/* ============================================================================
   DEOS — ЕДИНЫЙ ФАЙЛ ОФОРМЛЕНИЯ

   Дизайн-код: Atlassian Design System (Apache-2.0) во всём, кроме меток —
   метки взяты у Apple (таблетка, 13/600, без рамки). Полное описание и
   правила — в отдельном документе «DEOS · дизайн-код».

   Правило одно: ни один экран не пишет свои размеры, цвета и скругления.
   Нужен новый вид элемента — он появляется ЗДЕСЬ и получает имя.
   Любое число ниже — это токен из Atlassian, а не подобранное на глаз.
   ========================================================================= */

/* ============================================================ ШРИФТЫ
   ДВА начертания на всю программу, оба переменные, оба лежат рядом —
   в сеть за ними программа не ходит и без сети работает так же.

   Manrope — заголовки. Геометрический гротеск, широкие овалы, спокойный
   характер; в крупном кегле держит форму лучше интерфейсного шрифта.
   Inter — весь остальной текст И ВСЕ ЦИФРЫ. Рисовался под экран: крупный
   рост строчных, различимые «I l 1», ровный ноль без точки и без косой
   черты.

   ТРЕТЬЕГО НАЧЕРТАНИЯ НЕТ И БЫТЬ НЕ ДОЛЖНО. Раньше было — вернее, было
   его имя: в правиле стояла цепочка системных названий SF Mono, Cascadia
   Mono, Roboto Mono, Consolas, а файла не лежало никакого. Что из этой
   цепочки подставится, решал не дизайн-код, а то, что случайно оказалось
   установлено в системе: на macOS выходил SF Mono, на Windows — Cascadia
   Mono с точкой внутри нуля, на Linux — что придётся. Одна и та же
   программа показывала цифры по-разному у разных людей.

   Теперь цифры набираются Inter, и обязательно ТАБЛИЧНЫМИ: у Inter есть
   набор цифр одинаковой ширины (tabular-nums), поэтому бегущее время не
   дёргается, а числа в столбце стоят разряд под разрядом. Manrope для
   этого не годится — цифры одинаковой ширины у него не нарисованы, «1»
   уже «0», и секунды в таймере прыгали бы влево-вправо.

   Нужен другой вид надписи — берётся другой вес или кегль, а не другой
   шрифт.                                                               */
@font-face {
  font-family: "Manrope"; font-style: normal; font-weight: 400 800;
  font-display: swap; src: url("/fonts/manrope-cyrillic-wght-normal.woff2") format("woff2");
  unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
@font-face {
  font-family: "Manrope"; font-style: normal; font-weight: 400 800;
  font-display: swap; src: url("/fonts/manrope-latin-wght-normal.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+2000-206F, U+2192, U+2212;
}
@font-face {
  font-family: "Inter"; font-style: normal; font-weight: 100 900;
  font-display: swap; src: url("/fonts/inter-cyrillic-wght-normal.woff2") format("woff2");
  unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
@font-face {
  font-family: "Inter"; font-style: normal; font-weight: 100 900;
  font-display: swap; src: url("/fonts/inter-latin-wght-normal.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+2000-206F, U+2192, U+2212;
}

/* ============================================================== ТОКЕНЫ */
:root {
  /* ── ФОРМА. ОДНО ЧИСЛО НА ВСЮ ПРОГРАММУ.
     Скругление контрола — не значение из списка, а ДОЛЯ ЕГО СОБСТВЕННОЙ
     ВЫСОТЫ. Доля одна, --round, и она задаёт характер формы всей
     программы разом: сделать её мягче или строже — это поправить здесь
     одну цифру, а не сорок правил по файлу.

     Почему именно доля высоты, а не проценты. В CSS проценты в скруглении
     считаются по КАЖДОЙ стороне отдельно: 36% на кнопке 200×32 дают угол
     72 по горизонтали и 11.5 по вертикали — вместо круглого угла выходит
     вытянутый эллипс, и чем шире кнопка, тем он безобразнее. Поэтому доля
     берётся от высоты и превращается в обычные пиксели.

     Контейнеры — карточка, окно, меню — из этого правила выпадают, и это
     не недосмотр. У них нет собственной высоты: карточка бывает и в сто
     пикселей, и во весь экран. Доля от высоты дала бы у высокой карточки
     угол в полсотни пикселей. Поэтому у контейнеров всего два значения на
     всю программу, и они посчитаны от роста контролов, а не от себя.

     РЕШЕНИЕ ПРИНЯТО И ОНО ОДНО: 0.375 — скруглённый прямоугольник.
     Таблеток в программе нет: кнопка, поле, вкладка, пункт сегмента,
     значок-кнопка — все скруглены по одной и той же доле своей высоты,
     и все выглядят одинаково скруглёнными, хотя цифры у них разные
     (кнопка 32 → 12, поле 40 → 15, компактная кнопка 24 → 9).

     Смешения двух форм в одном ряду нет нигде. Захочется мягче или
     строже — это одна цифра здесь, и вся программа меняется разом,
     включая всё, что появится в ней потом. */
  --round: .375;

  --r-xs:   calc(16px * var(--round));           /* флажок, точка выбора  ≈ 6 */
  --r-s:    calc(var(--h-btn-sm) * var(--round));/* метка, ячейка календаря ≈ 9 */
  --r:      calc(var(--h-btn) * var(--round));   /* кнопка, поле, вкладка ≈ 12 */
  --r-l:    calc(44px * var(--round));           /* карточка, меню        ≈ 16 */
  --r-xl:   calc(60px * var(--round));           /* окно, панель          ≈ 22 */
  --r-full: 9999px;

  /* КУРСОР ПЕРЕТАСКИВАНИЯ — единственный, который программа рисует сама.
     Причина одна: там, где что-то тащат, браузер показывает свою ладонь.
     Рисует её операционная система, в каждой свою, настроить нельзя, и
     рядом с нашим оформлением она выглядит случайной. Обычную стрелку и
     все прочие курсоры программа НЕ трогает.

     Рука нарисована здесь, а не взята готовой: курсор уезжает заказчику
     в поставке, и чужая картинка с неизвестной лицензией там не нужна.
     Раскрытая ладонь — «можно взять», сжатый кулак — «держим».

     Белая обводка нужна, чтобы рука не пропадала на тёмном фоне: одна и
     та же годится и для светлой темы, и для тёмной. Горячая точка —
     середина ладони. Хвост `, grab` / `, grabbing` — запасной вариант,
     если своё изображение не подхватится. */
  --cur-grab: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32"><path d="M12.2 27.5 C9.6 25.4 7.4 22.6 6.4 20.2 C5.8 18.8 6.3 17.6 7.5 17.2 C8.5 16.9 9.4 17.3 10.1 18.4 L11.2 20.1 L11.2 8.6 C11.2 7.3 12.0 6.5 13.1 6.5 C14.2 6.5 15.0 7.3 15.0 8.6 L15.0 14.6 L15.0 6.2 C15.0 4.9 15.8 4.1 16.9 4.1 C18.0 4.1 18.8 4.9 18.8 6.2 L18.8 14.6 L18.8 6.8 C18.8 5.5 19.6 4.7 20.7 4.7 C21.8 4.7 22.6 5.5 22.6 6.8 L22.6 14.9 L22.6 9.4 C22.6 8.2 23.4 7.4 24.4 7.4 C25.5 7.4 26.3 8.2 26.3 9.4 L26.3 19.4 C26.3 24.0 23.6 27.5 20.0 27.5 Z" fill="%231C1B18" stroke="%23ffffff" stroke-width="2.6" stroke-linejoin="round"/><path d="M12.2 27.5 C9.6 25.4 7.4 22.6 6.4 20.2 C5.8 18.8 6.3 17.6 7.5 17.2 C8.5 16.9 9.4 17.3 10.1 18.4 L11.2 20.1 L11.2 8.6 C11.2 7.3 12.0 6.5 13.1 6.5 C14.2 6.5 15.0 7.3 15.0 8.6 L15.0 14.6 L15.0 6.2 C15.0 4.9 15.8 4.1 16.9 4.1 C18.0 4.1 18.8 4.9 18.8 6.2 L18.8 14.6 L18.8 6.8 C18.8 5.5 19.6 4.7 20.7 4.7 C21.8 4.7 22.6 5.5 22.6 6.8 L22.6 14.9 L22.6 9.4 C22.6 8.2 23.4 7.4 24.4 7.4 C25.5 7.4 26.3 8.2 26.3 9.4 L26.3 19.4 C26.3 24.0 23.6 27.5 20.0 27.5 Z" fill="%231C1B18"/></svg>') 16 16, grab;
  --cur-hold: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32"><path d="M11.2 14.2 A1.9 1.9 0 0 1 15.0 14.2 A1.9 1.9 0 0 1 18.8 14.2 A1.9 1.9 0 0 1 22.6 14.2 A1.9 1.9 0 0 1 26.4 14.2 L26.4 19.6 C26.4 24.1 23.8 27.5 20.2 27.5 L12.4 27.5 C9.9 25.6 7.5 22.9 6.7 20.6 C6.1 19.1 6.7 17.9 7.9 17.6 C8.9 17.3 9.7 17.9 10.4 19.0 L11.2 20.2 Z" fill="%231C1B18" stroke="%23ffffff" stroke-width="2.6" stroke-linejoin="round"/><path d="M11.2 14.2 A1.9 1.9 0 0 1 15.0 14.2 A1.9 1.9 0 0 1 18.8 14.2 A1.9 1.9 0 0 1 22.6 14.2 A1.9 1.9 0 0 1 26.4 14.2 L26.4 19.6 C26.4 24.1 23.8 27.5 20.2 27.5 L12.4 27.5 C9.9 25.6 7.5 22.9 6.7 20.6 C6.1 19.1 6.7 17.9 7.9 17.6 C8.9 17.3 9.7 17.9 10.4 19.0 L11.2 20.2 Z" fill="%231C1B18"/></svg>') 16 16, grabbing;

  /* ── ВЛОЖЕННОЕ СКРУГЛЕНИЕ. Правило одно и оно арифметическое:
     скругление вложенной коробки = скругление внешней МИНУС зазор между
     ними. Дуги тогда идут параллельно (концентрично) и угол выглядит
     цельным. Если взять готовую ступень «на глаз» — например 8 внутри 12
     при зазоре 2, — вложенное скругление оказывается больше положенного,
     и бегунок сегмента отходит от угла подложки заметной щелью.

     Поэтому число здесь не подбирается: контейнер объявляет свой зазор
     в --pad и сам же им отступает, а вложенное считает скругление через
     calc(). Меняется отступ — скругление едет за ним само. */
  --pad: 0px;

  /* ── ШАГ. Atlassian space: 2 4 6 8 12 16 20 24 32 40 48 64 80.
     Между этими значениями отступов в программе нет. */
  --sp-025:  2px;
  --sp-050:  4px;
  --sp-075:  6px;
  --sp-100:  8px;
  --sp-150: 12px;
  --sp-200: 16px;
  --sp-250: 20px;
  --sp-300: 24px;
  --sp-400: 32px;
  --sp-500: 40px;
  --sp-600: 48px;
  --gap:    16px;          /* привычное имя для --sp-200 */

  /* ── РАМКИ. Три ширины и больше никаких. */
  --bw:      1px;
  --bw-sel:  2px;
  --bw-foc:  2px;

  /* ── РОСТ КОНТРОЛОВ. Кнопка 32, компактная 24, поле 40, компактное 32. */
  --h-btn:      32px;
  --h-btn-sm:   24px;
  --h-field:    40px;
  --h-field-sm: 32px;
  --h-row:      32px;      /* строка таблицы */
  --h-row-head: 28px;
  --h-menu:     32px;      /* пункт всплывающего меню */

  /* ── ПОВЕРХНОСТИ. Нейтраль Atlassian, но тёплая: программа про мебель,
     ледяной серый в ней выглядит казённо. Шкала та же, оттенок сдвинут. */
  --bg:        #F7F6F3;    /* surface.sunken */
  --card:      #FFFFFF;    /* surface */
  --field:     #FFFFFF;    /* background.input */
  --field2:    #F2F1ED;    /* surface.hovered */
  --line:      #DEDCD5;    /* border */
  --line2:     #B9B6AC;    /* border.input — заметная рамка поля */

  /* Прозрачная нейтраль: ложится на любой фон, включая тёмную тему. */
  --a-100: rgba(23, 23, 23, .03);
  --a-200: rgba(5, 21, 36, .06);
  --a-300: rgba(11, 18, 14, .14);
  --a-400: rgba(8, 15, 33, .29);

  /* ── ТЕКСТ. Четыре ступени: основной, приглушённый, самый тихий, мёртвый. */
  --ink:       #292A2E;
  --ink2:      #57585E;
  --ink3:      #74767E;
  --ink-dis:   rgba(8, 15, 33, .29);

  /* ── ФИРМЕННЫЙ ЦВЕТ. Перекрывается настройкой оформления целиком:
     --acc-grad появляется только у градиентных наборов. */
  /* ВАЖНО ПРО ПАРЫ «ЗАЛИВКА — ТУШЬ». Их две, и путать их нельзя:
       --acc        + --acc-fg   — сам выбранный цвет; тушь считается по
                                   его яркости и может быть тёмной;
       --acc-solid  + #FFFFFF    — тот же цвет, УВЕДЁННЫЙ ВНИЗ ровно
                                   настолько, чтобы белая надпись держала
                                   4.5:1. Тушь на нём белая по определению.

     Поставить --acc-fg на заливку --acc-solid — значит взять тушь,
     посчитанную для светлого цвета, и положить её на тёмный. Со светлыми
     акцентами (мята, персик, фисташка) выходила тёмная надпись на тёмной
     заливке: кнопка читалась как чёрный слепой брусок. */
  --acc:        #66A88F;   /* сам выбранный цвет: рамки, ползунок, флажок */
  --acc-solid:  #3F7A62;   /* он же, затемнённый до читаемости белого текста */
  --acc-weak:   #EFF6F3;
  --acc-weak-2: #D8E9E2;
  --acc-ink:   #3B614F;
  --acc-fg:    #FFFFFF;

  /* ── СОСТОЯНИЯ.
     --danger-fg — тушь по сплошному красному. В светлой теме красный
     тёмный и надпись на нём белая; в тёмной теме красный, наоборот,
     светлый (иначе он не читается на тёмной поверхности), и белая
     надпись на нём пропадает — там тушь тёмная. Своего значения у этой
     переменной в коде экранов быть не должно: только имя. */
  --danger:        #C9372C;
  --danger-fg:     #FFFFFF;
  --danger-weak:   #FFEDEB;
  --danger-weak-2: #FFD5D2;
  --danger-ink:    #AE2E24;
  --warn:          #FBC828;
  --warn-weak:     #FFF7D6;
  --warn-weak-2:   #FFEFB8;
  --warn-ink:      #7F5F01;
  --good:          #4C6B1F;
  --good-weak:     #EFFFD6;
  --good-weak-2:   #DCF5B4;
  --good-ink:      #37471F;

  /* ── ТИПОГРАФИКА. Atlassian: body 14/20, small 12/16, large 16/24.
     Заголовки весом 653 — это не опечатка, это вес переменного шрифта. */
  --fs-s:   12px;  --lh-s:  16px;
  --fs:     14px;  --lh:    20px;
  --fs-l:   16px;  --lh-l:  24px;
  /* Жирное — только там, где оно несёт смысл: заголовок, шапка таблицы,
     итоговое число. Подписи полей и метки идут средним весом: сплошной
     полужирный превращает экран в кашу, в которой ничего не выделено. */
  --fw-reg:  400;
  --fw-med:  500;
  --fw-semi: 600;
  --fw-bold: 700;

  --font:  "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Arial, sans-serif;
  --font-h: "Manrope", "Inter", -apple-system, "Segoe UI", Roboto, Arial, sans-serif;
  /* Цифровой слот. Тот же Inter — отдельное имя нужно, чтобы в правилах
     было видно: здесь цифры, и они обязаны быть табличными. */
  --num: var(--font);

  /* ── ТЕНИ. Ровно три, из elevation.shadow Atlassian. */
  --sh-raised:  0 1px 1px rgba(30, 31, 33, .25), 0 0 1px rgba(30, 31, 33, .31);
  --sh-overlay: 0 8px 12px rgba(30, 31, 33, .15), 0 0 1px rgba(30, 31, 33, .31);
  --sh-overflow: 0 0 8px rgba(30, 31, 33, .16), 0 0 1px rgba(30, 31, 33, .12);
  --shadow:    var(--sh-raised);      /* старые имена, чтобы не ломать экраны */
  --shadow-lg: var(--sh-overlay);

  /* ── ФОКУС. Одно кольцо на всю программу. */
  --focus: #4688EC;

  /* размеры оболочки */
  --topbar-h: 56px;
  --rail-w:   238px;

  /* слои — единственная шкала, произвольных чисел в коде быть не должно */
  --z-sticky:  20;
  --z-rail:    30;
  --z-popover: 220;
  --z-overlay: 300;
  --z-dialog:  400;
  --z-toast:   500;
}

/* Только корень. Если это правило совпадёт ещё с каким-нибудь узлом внутри
   страницы, оно назначит фирменные переменные заново — и всё, что внутри
   этого узла, увидит запасную мяту вместо выбранного человеком цвета.
   Ровно так и было, пока отметка темы ставилась ещё и на body. */
:root[data-theme="dark"] {

  --bg:     #17181A;
  --card:   #1E1F21;
  --field:  #1E1F21;
  --field2: #26282B;
  --line:   #35373B;
  --line2:  #5A5D64;

  --a-100: rgba(255, 255, 255, .03);
  --a-200: rgba(255, 255, 255, .07);
  --a-300: rgba(255, 255, 255, .14);
  --a-400: rgba(255, 255, 255, .30);

  --ink:     #DEE0E3;
  --ink2:    #A9ABB2;
  --ink3:    #8C8F97;
  --ink-dis: rgba(255, 255, 255, .28);

  --acc-weak:   #23302B;
  --acc-weak-2: #2E4239;
  --acc-ink:    #9FD3BC;

  --danger:        #F87168;
  --danger-fg:     #1D2125;
  --danger-weak:   #42221F;
  --danger-weak-2: #4E2622;
  --danger-ink:    #FD9891;
  --warn-weak:     #392D06;
  --warn-weak-2:   #423409;
  --warn-ink:      #F5CD47;
  --good:          #94C748;
  --good-weak:     #28311B;
  --good-weak-2:   #2F3B21;
  --good-ink:      #B3DF72;

  --focus: #669DF1;

  --sh-raised:  0 1px 1px rgba(3, 4, 4, .5), 0 0 1px rgba(3, 4, 4, .5);
  --sh-overlay: 0 8px 12px rgba(3, 4, 4, .36), 0 0 1px rgba(3, 4, 4, .5);
  --sh-overflow: 0 0 8px rgba(3, 4, 4, .36), 0 0 1px rgba(3, 4, 4, .5);
}

/* Лист коммерческого предложения живёт по своим правилам: он всегда светлый
   и фирменный, даже когда программа в тёмной теме. */
.sheet {
  --sheet-bg:     #FFFFFF;
  --sheet-ink:    #1C1B18;
  --sheet-ink-2:  #6B675E;
  --sheet-line:   #E4E2DC;
  --sheet-accent: #1E5B44;
  --sheet-accent-weak: #E7F0EB;
  --sheet-radius: 12px;
  --sheet-border-width: 1px;
  --sheet-border-style: solid;
}

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

/* ================================================================ БАЗА */
*, *::before, *::after { box-sizing: border-box; }

html, body { margin: 0; height: 100%; }

body {
  font-family: var(--font);
  font-size: 14px;
  line-height: 1.45;
  color: var(--ink);
  background: var(--bg);
  -webkit-font-smoothing: antialiased;
  overflow: hidden;
}

h1, h2, h3, h4 {
  margin: 0;
  font-family: var(--font-h);
  font-weight: var(--fw-bold);
  letter-spacing: -.01em;
  line-height: 1.22;
}
h1 { font-size: 21px; }
h2 { font-size: 17px; }
h3 { font-size: 14.5px; }
p  { margin: 0 0 10px; }

a { color: var(--acc-ink); text-decoration: none; }
a:hover { text-decoration: underline; }

/* Цвет наследуется наравне со шрифтом. Без этого кнопка без своего
   цвета берёт системный buttontext — чёрный, и в тёмной теме текст
   такой кнопки становится нечитаемым. */
button { font: inherit; color: inherit; cursor: pointer; }
input, select, textarea { font: inherit; color: inherit; }

.num    { font-family: var(--num); font-variant-numeric: tabular-nums; }
.muted  { color: var(--ink2); }
.faint  { color: var(--ink3); }
.small  { font-size: 12.5px; }
.nowrap { white-space: nowrap; }
.grow   { flex: 1 1 auto; min-width: 0; }
.spacer { flex: 1 1 auto; }
.hidden { display: none !important; }

.row { display: flex; align-items: center; gap: 10px; }
.row.wrap { flex-wrap: wrap; }
.col { display: flex; flex-direction: column; gap: 10px; }

::-webkit-scrollbar { width: 10px; height: 10px; }
::-webkit-scrollbar-thumb { background: var(--acc-weak); border-radius: var(--r-s); }
::-webkit-scrollbar-thumb:hover { background: var(--acc); }
::-webkit-scrollbar-track { background: transparent; }

:focus-visible { outline: 2px solid var(--acc); outline-offset: 2px; border-radius: var(--r-s); }

/* ═══════════════════════════════════════════════════════════════════════
   ЦЕНТРИРОВАНИЕ — ОДНО ПРАВИЛО НА ВСЮ ПРОГРАММУ, ВЫШЕ ВСЕХ ОСТАЛЬНЫХ.

   Каждая «коробка» — кнопка, значок-кнопка, плюс-минус, флажок, вкладка,
   сегмент, метка — центрирует содержимое по обеим осям. Ниже по файлу это
   НЕ переопределяется. Появился новый вид коробки — впиши его сюда, и он
   отцентрован; подбирать padding под глаз не нужно.

   Строки «контрол + подпись» центрируются только по вертикали: по
   горизонтали текст обязан идти от начала, иначе подписи прыгают.
   ═══════════════════════════════════════════════════════════════════ */
.btn, .icon-btn, .stepper-btn, .checkbox-box, .tab, .seg > button,
.chip, .radbtn, .swatch, .avatar {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  text-align: center;
}
.switch, .checkbox, .switch-row, .stepper, .range-row, .field-row-inline {
  align-items: center;
}
.btn > *, .icon-btn > *, .stepper-btn > *, .checkbox-box > *, .chip > * { flex: none; }
.num, .stepper-value, .range-value, td.right, th.right { font-variant-numeric: tabular-nums; }

/* ======================================================== ЕДИНЫЙ СЛОЙ ПОЛЕЙ
   Atlassian: высота 40 (компактное 32), рамка 1px border.input, скругление 6,
   текст 14/20, отступ по горизонтали 12. В фокусе рамка красится и добавляется
   внутренняя того же цвета — визуально получается 2px, а размер поля не
   меняется и соседи не дёргаются.

   Ползунки, флажки, переключатели и выбор цвета сюда НЕ входят: у них своя
   разметка. Именно из-за отсутствия этого исключения ползунки растягивались
   на всю ширину и уезжали за свои подписи. */
input:not([type=checkbox]):not([type=radio]):not([type=file]):not([type=range]):not([type=color]),
textarea,
select,
.input,
.textarea,
.select {
  font: inherit;
  font-size: var(--fs);
  line-height: var(--lh);
  color: var(--ink);
  background: var(--field);
  border: var(--bw) solid var(--line2);
  border-radius: var(--r);
  height: var(--h-field);
  padding: 0 var(--sp-150);
  width: 100%;
  transition: border-color .15s, background .15s, box-shadow .15s;
}
input.small, .input.small, select.small, .select.small { height: var(--h-field-sm); }

textarea, .textarea {
  height: auto; min-height: 76px;
  padding: var(--sp-100) var(--sp-150);
  resize: vertical;
}

select, .select {
  appearance: none;
  -webkit-appearance: none;
  background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="12" height="8"><path d="M1 1.5 6 6.5 11 1.5" stroke="%23747680" stroke-width="1.6" fill="none" stroke-linecap="round"/></svg>');
  background-repeat: no-repeat;
  background-position: right var(--sp-150) center;
  padding-right: var(--sp-400);
  cursor: pointer;
}

input:not([type=checkbox]):not([type=radio]):not([type=range]):focus,
textarea:focus, select:focus, .input:focus, .textarea:focus {
  outline: none;
  border-color: var(--acc);
  box-shadow: inset 0 0 0 var(--bw) var(--acc);
}

input::placeholder, textarea::placeholder { color: var(--ink3); opacity: 1; }
input:disabled, textarea:disabled, select:disabled,
.input:disabled, .textarea:disabled {
  background: var(--a-200);
  border-color: var(--a-200);
  color: var(--ink-dis);
  cursor: not-allowed;
}

.input.invalid, .textarea.invalid, input.invalid, textarea.invalid {
  border-color: var(--danger);
  box-shadow: inset 0 0 0 var(--bw) var(--danger);
}
.input.num { text-align: right; font-family: var(--num); font-variant-numeric: tabular-nums; }

/* Стрелки у числовых полей убираем: для чисел есть степпер. */
input[type=number]::-webkit-outer-spin-button,
input[type=number]::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
input[type=number] { -moz-appearance: textfield; appearance: textfield; }

/* Подпись 12/16 весом 700, отступ 4. Подсказка и ошибка — тоже 12/16. */
.field-row   { display: flex; flex-direction: column; gap: var(--sp-050); }
.field-label { font-size: var(--fs-s); line-height: var(--lh-s);
               font-weight: var(--fw-med); color: var(--ink2); }
.field-hint  { font-size: var(--fs-s); line-height: var(--lh-s); color: var(--ink3); }
.field-error { font-size: var(--fs-s); line-height: var(--lh-s); color: var(--danger-ink); }
.field-grid  { display: grid; gap: var(--gap);
               grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); }

/* ============================================================== КНОПКИ
   Atlassian: высота 32, отступ по горизонтали 12, текст 14/500, скругление 6.
   Обычная кнопка прозрачная с тонкой рамкой; главная — заливка фирменным
   цветом (или градиентом, если он выбран в оформлении). */
.btn {
  gap: var(--sp-100);
  height: var(--h-btn);
  padding: 0 var(--sp-150);
  border: var(--bw) solid var(--a-300);
  border-radius: var(--r);
  background: transparent;
  color: var(--ink2);
  font-weight: var(--fw-med);
  font-size: var(--fs);
  line-height: var(--lh);
  white-space: nowrap;
  transition: background .15s, border-color .15s, color .15s;
}
.btn:hover  { background: var(--a-200); color: var(--ink); }
.btn:active { background: var(--a-300); }
.btn:focus-visible { outline: var(--bw-foc) solid var(--focus); outline-offset: 2px; }
.btn:disabled {
  background: var(--a-200); border-color: transparent;
  color: var(--ink-dis); cursor: not-allowed;
}

/* Заливка берёт НЕ выбранный цвет, а его затемнённый вариант: светлые
   акценты (персик, мята, фисташка) с белым текстом читались плохо, а с
   тёмным — выглядели как выключенная кнопка. Теперь цвет уводится вниз
   ровно настолько, чтобы белая надпись держала контраст 4.5:1, и текст
   на заливке всегда белый. Ползунки, флажки и рамки продолжают брать
   сам выбранный цвет — там контраст с текстом не нужен. */
/* ГРАДИЕНТ СЧИТАЕТСЯ ОТ ВНЕШНЕГО КРАЯ, а не от внутреннего.
   По умолчанию фоновая картинка раскладывается по внутренней коробке
   (без рамки), а закрашивается по внешней (с рамкой) — и в полоске
   шириной с рамку градиент повторяется своим ПРОТИВОПОЛОЖНЫМ концом.
   На кнопке с прозрачной рамкой это давало ободок чужого цвета по всему
   краю, особенно заметный на скруглениях. Отсюда «артефакты по краям».
   Раскладка по внешней коробке и запрет повтора убирают его совсем. */
.btn.primary {
  border-color: transparent;
  background: var(--acc-grad, var(--acc-solid));
  background-origin: border-box;
  background-repeat: no-repeat;
  color: #fff;
}
.btn.primary:hover  { filter: brightness(.92); background: var(--acc-grad, var(--acc-solid));
                      background-origin: border-box; background-repeat: no-repeat; color: #fff; }
.btn.primary:active { filter: brightness(.88); }
.btn.primary:disabled { background: var(--a-200); color: var(--ink-dis); filter: none; }

.btn.danger { border-color: transparent; background: var(--danger); color: var(--danger-fg); }
.btn.danger:hover { background: var(--danger-ink); filter: none; }
.btn.danger.subtle { background: transparent; color: var(--danger-ink); border-color: var(--a-300); }

.btn.ghost { border-color: transparent; background: transparent; color: var(--ink2); }
.btn.ghost:hover { background: var(--a-200); color: var(--ink); }

/* Скругление у маленькой кнопки СВОЁ. Пока она брала общее (12 при
   высоте 32), на высоте 24 те же 12 давали ровно половину высоты — то
   есть пилюлю. Отсюда и брались «пиллсы, которые до сих пор везде»:
   «Изменить», «Коды доступа», «Выйти», «Вернуть исходный макет». Доля
   формы у обеих кнопок теперь одна и та же, разная только высота. */
.btn.small { height: var(--h-btn-sm); padding: 0 var(--sp-150); border-radius: var(--r-s); }
.btn.block { width: 100%; }
.btn.selected { color: var(--acc-ink); border-color: var(--acc-solid);
  background: linear-gradient(180deg, var(--acc-weak) 0%, var(--acc-weak-2) 100%); }

.icon-btn {
  width: var(--h-btn); height: var(--h-btn);
  font-size: 16px; line-height: 1;
  color: var(--ink2);
  background: transparent;
  border: none;
  border-radius: var(--r);
  transition: background .15s, color .15s;
}
.icon-btn:hover { background: var(--a-200); color: var(--ink); }
.icon-btn:focus-visible { outline: var(--bw-foc) solid var(--focus); outline-offset: 2px; }
.icon-btn.danger:hover { background: var(--danger-weak); color: var(--danger-ink); }
/* Маленькая — там, где рядом мелкий текст: строка таблицы, уведомление.
   Скругление своё, по своей высоте: общее дало бы пилюлю. */
.icon-btn.small {
  width: var(--h-btn-sm); height: var(--h-btn-sm);
  font-size: 14px;
  border-radius: var(--r-s);
}

/* Кнопка-карточка: выбор одного из нескольких. */
.radbtn {
  flex-direction: column; gap: var(--sp-050);
  border: var(--bw) solid var(--a-300);
  border-radius: var(--r);
  background: transparent;
  padding: var(--sp-100) var(--sp-150);
  font-size: var(--fs-s); line-height: var(--lh-s);
  color: var(--ink2);
  transition: background .15s, border-color .15s, color .15s;
}
.radbtn:hover { background: var(--a-200); color: var(--ink); }
.radbtn.on   { border-color: var(--acc); color: var(--acc-ink);
  background: linear-gradient(180deg, var(--acc-weak) 0%, var(--acc-weak-2) 100%); }

/* ======================================================== ПЕРЕКЛЮЧАТЕЛЬ
   Atlassian toggle: дорожка 32×16, ручка 12, отступ 2. Выключенный — тёмная
   нейтраль, а не серая: у Atlassian выключенное состояние читается как
   «сознательно выключено», а не «неактивно».
   Дорожка НИКОГДА не растягивается: flex:none и жёсткий размер. */
.switch {
  display: inline-flex;
  gap: var(--sp-100);
  cursor: pointer;
  user-select: none;
}
.switch input { position: absolute; opacity: 0; width: 0; height: 0; pointer-events: none; }

/* Рост переключателя — 22 при ширине 42. Прежние 32×16 читались как
   значок, а не как то, что нажимают: на экране с десятком строк подряд
   попасть в полоску высотой шестнадцать точек можно, но целиться в неё
   приходится, а переключатель — самое частое действие в настройках. */
.switch-track {
  position: relative;
  flex: none;
  width: 42px;
  height: 22px;
  background: var(--ink);
  border-radius: var(--r-full);
  transition: background .2s;
}
.switch-track::after {
  content: "";
  position: absolute; top: 3px; left: 3px;
  width: 16px; height: 16px;
  background: #fff;
  border-radius: 50%;
  transition: transform .2s;
}
.switch input:checked + .switch-track { background: var(--acc); }
.switch input:checked + .switch-track::after { transform: translateX(20px); }
.switch input:disabled + .switch-track { background: var(--a-200); }
.switch input:disabled ~ span { color: var(--ink-dis); }
.switch input:focus-visible + .switch-track { outline: var(--bw-foc) solid var(--focus); outline-offset: 2px; }

.switch > span:not(.switch-track) { font-size: var(--fs); line-height: var(--lh); color: var(--ink); }

/* Строка настройки: подпись слева, переключатель справа, и он не едет. */
.switch-row {
  display: flex;
  gap: var(--sp-200);
  padding: var(--sp-150) 0;
}
.switch-row + .switch-row { border-top: var(--bw) solid var(--line); }
.switch-row .switch-row-text { flex: 1 1 auto; min-width: 0; }
.switch-row .switch-row-text b { display: block; font-weight: var(--fw-med); font-size: var(--fs); }
.switch-row .switch-row-text span { color: var(--ink3); font-size: var(--fs-s); line-height: var(--lh-s); }
.switch-row .switch { flex: none; }

/* ═══════════════════════════════════════════════ ПОЛЕ ПОИСКА
   Родной крестик браузера убран. Он синий, одного размера в любой теме,
   и оформлению не подчиняется: ни цвет, ни форма ему не задаются. Вместо
   него — обычная кнопка со значком из общего реестра, она знает и тему,
   и наведение, и скругление.

   Крестик появляется только у заполненного поля: пустое поле с кнопкой
   очистки обещает работу, которой нет. */
input[type="search"]::-webkit-search-cancel-button,
input[type="search"]::-webkit-search-decoration { -webkit-appearance: none; appearance: none; }

.field-search { position: relative; display: block; width: 100%; }
.field-search .input { width: 100%; padding-right: calc(var(--h-btn-sm) + var(--sp-150)); }
.field-clear {
  position: absolute; top: 50%; right: var(--sp-075);
  transform: translateY(-50%);
  display: none; align-items: center; justify-content: center;
  width: var(--h-btn-sm); height: var(--h-btn-sm);
  padding: 0;
  color: var(--ink3);
  background: var(--a-200);
  border: none; border-radius: var(--r-full);
  cursor: pointer;
  transition: background .12s, color .12s;
}
.field-search.filled .field-clear { display: inline-flex; }
.field-clear:hover { background: var(--a-300); color: var(--ink); }
.field-clear:focus-visible { outline: var(--bw-foc) solid var(--focus); outline-offset: 2px; }

/* Кнопка внутри сообщения: «Обновить страницу», «Вернуть». Сообщение с
   такой кнопкой само не гаснет — оно зовёт что-то сделать. */
.toast-action { margin-top: var(--sp-100); align-self: flex-start; }

/* ═══════════════════════════════════════════════════ ЗНАЧКИ
   Все рисунки приходят из общего реестра (kit/icons.js) одним узлом
   .ic. Размер задаётся на нём, цвет наследуется от кнопки — поэтому
   один и тот же значок годится и на светлом, и на тёмном, и не требует
   ни одной подгонки по месту. */
.ic {
  display: inline-flex; align-items: center; justify-content: center;
  flex: none;
}
.ic svg { width: 100%; height: 100%; display: block; }

/* ═══════════════════════════════════════════════ ФЛАЖОК И ТОЧКА ВЫБОРА
   Atlassian: флажок 14×14, скругление 2, рамка 1; точка 15 с точкой 6.
   Галочка и точка центрируются правилом наверху файла, а не отступами. */
.checkbox { display: inline-flex; gap: var(--sp-100); cursor: pointer; user-select: none;
            font-size: var(--fs); line-height: var(--lh); }
.checkbox input { position: absolute; opacity: 0; width: 0; height: 0; pointer-events: none; }
/* Рост флажка — 18, а не 14. По той же причине, что и у переключателя:
   в таблице, где флажок стоит в каждой строке, четырнадцать точек — это
   попадание с прицеливанием.

   Галочка внутри — РИСУНОК ИЗ ОБЩЕГО РЕЕСТРА (kit/icons.js), а не черта,
   собранная из рамки псевдоэлемента. Черту приходилось двигать
   отступами под глаз, и она съезжала: у неё нет середины, которую можно
   было бы совместить с серединой коробки. Рисунок центрируется общим
   правилом наверху файла, как и всё остальное внутри коробок. */
.checkbox-box {
  flex: none;
  width: 18px; height: 18px;
  border: var(--bw) solid var(--line2);
  border-radius: var(--r-xs);
  background: var(--field);
  transition: background .12s, border-color .12s;
}
.checkbox-box .ic { color: #fff; opacity: 0; transition: opacity .12s; }
.checkbox input:checked + .checkbox-box .ic { opacity: 1; }
.checkbox-box svg { width: 100%; height: 100%; display: block; }
.checkbox input:checked + .checkbox-box { background: var(--acc); border-color: var(--acc); }
.checkbox input:disabled + .checkbox-box { background: var(--a-200); border-color: var(--a-200); }
.checkbox input:focus-visible + .checkbox-box { outline: var(--bw-foc) solid var(--focus); outline-offset: 2px; }

.checkbox.radio .checkbox-box { width: 18px; height: 18px; border-radius: 50%; }
.checkbox.radio .checkbox-box .ic { display: none; }
.checkbox.radio .checkbox-box::after {
  content: ""; width: 7px; height: 7px; border-radius: 50%; background: #fff;
  transform: scale(0); transition: transform .12s;
}
.checkbox.radio input:checked + .checkbox-box::after { transform: scale(1); }

/* ============================================================== ПОЛЗУНОК
   Atlassian range: обёртка 40, трек 6 со скруглением до конца, ручка 16
   без тени. Своя разметка, а не наследство полей ввода — ширину задаёт
   вмещающий блок, а не правило width:100% из слоя полей. */
.range {
  -webkit-appearance: none;
  appearance: none;
  width: 100%;
  height: 24px;
  background: transparent;
  padding: 0;
  border: none;
  margin: 0;
}
.range::-webkit-slider-runnable-track {
  height: 6px;
  border-radius: var(--r-full);
  background: linear-gradient(var(--acc), var(--acc)) 0/var(--range-fill, 0%) 100% no-repeat,
              var(--a-300);
}
.range::-moz-range-track { height: 6px; border-radius: var(--r-full); background: var(--a-300); }
.range::-moz-range-progress { height: 6px; border-radius: var(--r-full); background: var(--acc); }
.range::-webkit-slider-thumb {
  -webkit-appearance: none; appearance: none;
  width: 16px; height: 16px;
  margin-top: -5px;
  border-radius: 50%;
  background: var(--acc);
  border: none;
  cursor: pointer;
}
.range::-moz-range-thumb {
  width: 16px; height: 16px;
  border-radius: 50%;
  background: var(--acc);
  border: none;
  cursor: pointer;
}
.range:focus-visible::-webkit-slider-thumb { outline: var(--bw-foc) solid var(--focus); outline-offset: 2px; }
.range:disabled { opacity: .4; }

/* Ползунок с подписью и числом — типовая строка настройки. */
.range-row { display: grid; grid-template-columns: 1fr auto; gap: var(--sp-050) var(--sp-150); }
.range-row .range-value { font-family: var(--num); font-variant-numeric: tabular-nums; font-size: var(--fs-s); color: var(--ink2);
                          min-width: 52px; text-align: right; }
.range-row .range { grid-column: 1 / -1; }

/* =============================================================== СТЕППЕР
   Плюс и минус — обычные кнопки 32 со скруглением 6, а не овалы. Число
   фиксированной ширины моноширинным: «4» и «11» не должны дёргать соседей.
   Подпись слева — отдельная колонка, иначе строки разъезжаются. */
.stepper { display: inline-flex; gap: var(--sp-050); }
.stepper .stepper-btn {
  width: var(--h-btn); height: var(--h-btn);
  flex: none;
  border-radius: var(--r);
  border: var(--bw) solid var(--a-300);
  background: transparent;
  color: var(--ink2);
  font-size: 15px;
  line-height: 1;
  padding: 0;
  transition: background .15s, color .15s;
}
.stepper .stepper-btn:hover { background: var(--a-200); color: var(--ink); }
.stepper .stepper-btn:focus-visible { outline: var(--bw-foc) solid var(--focus); outline-offset: 2px; }
.stepper .stepper-btn:disabled { background: var(--a-200); border-color: transparent;
                                 color: var(--ink-dis); cursor: not-allowed; }
.stepper .stepper-value {
  width: 44px;
  text-align: center;
  font-family: var(--num); font-variant-numeric: tabular-nums;
  font-size: var(--fs);
  font-weight: var(--fw-semi);
}
.stepper input.stepper-value {
  width: 62px; height: var(--h-btn);
  padding: 0 var(--sp-075);
  border-radius: var(--r);
}
/* Строка «подпись + степпер»: подпись всегда одной ширины. */
.stepper-row { display: grid; grid-template-columns: 116px auto; gap: var(--sp-150); align-items: center; }
.stepper-row > .stepper-row-label { font-size: var(--fs); color: var(--ink2); }

/* ═══════════════════════════════════════════════════════════ СЕГМЕНТЫ
   Подложка и бегунок — тот самый случай вложенного скругления. Зазор
   объявлен один раз в --pad, отступ и скругление бегунка считаются от
   него: подобранного числа здесь нет и быть не может.

   Сегментов в программе два имени, .seg и .segment, — это одно и то же
   и правила у них общие.

   ШИРИНА. Сегмент занимает ровно столько, сколько нужно его кнопкам, и
   ни пикселем больше. Без этого он растягивался во всю карточку: карточка
   выкладывает содержимое колонкой, а колонка по умолчанию тянет ребёнка
   на всю ширину — и подложка уезжала далеко вправо от последней кнопки.

   БЕГУНОК — отдельный узел .seg-thumb, он лежит ПОД кнопками и переезжает
   между ними. Подкладывает и двигает его /kit/segment.js, одинаково во
   всей программе; экраны по-прежнему только вешают класс на кнопку.
   Выбранная кнопка сама по себе фона не имеет — фон у неё едет. */
.seg, .segment {
  --pad: var(--sp-025);
  position: relative;
  display: inline-flex;
  width: fit-content; max-width: 100%;
  padding: var(--pad);
  background: var(--a-200);
  border: none;
  border-radius: var(--r);
}
.seg > button, .segment button {
  position: relative; z-index: 1;
  height: 28px; padding: 0 var(--sp-150);
  font-size: var(--fs); font-weight: var(--fw-med); line-height: 1;
  color: var(--ink2); background: none; border: none;
  border-radius: calc(var(--r) - var(--pad));
  transition: color .15s;
  white-space: nowrap;
}
.seg > button:hover, .segment button:hover { color: var(--ink); }
/* Выбранный пункт — фирменная надпись поверх приехавшей плашки, а НЕ
   сплошная заливка фирменным цветом. Сплошная заливка здесь неуместна
   дважды: сегмент — это выбор, а не главное действие, и рядом с
   настоящей главной кнопкой два сплошных пятна спорят друг с другом. */
.seg > button.on, .segment button.active {
  color: var(--acc-ink);
  font-weight: var(--fw-semi);
}

/* Плашка бегунка. Стоит по левому краю подложки и переезжает сдвигом:
   двигать сдвигом дешевле, чем менять left, — браузер делает это на
   видеокарте и не пересчитывает раскладку на каждом кадре. */
.seg-thumb {
  position: absolute; left: 0; top: var(--pad); bottom: var(--pad);
  z-index: 0;
  width: 0;
  border-radius: calc(var(--r) - var(--pad));
  background: var(--card);
  box-shadow: var(--sh-raised);
  opacity: 0;
  pointer-events: none;
  transition: transform .24s cubic-bezier(.2, .8, .3, 1),
              width .24s cubic-bezier(.2, .8, .3, 1),
              opacity .15s;
}
.seg-thumb.shown { opacity: 1; }
/* Первое появление и подгонка по ширине окна — без переезда. */
.seg-thumb.no-anim { transition: none; }

/* В тёмной теме плашку поднимает не тень, а тонкая светлая обводка.
   Тень со смещением вниз рисует под плашкой тёмную полоску; на светлом
   фоне она читается как объём, на тёмном сливается с подложкой и
   выглядит зазором. Плашка при этом кажется сдвинутой вверх, хотя
   стоит ровно: сверху до края подложки столько же, сколько снизу. */
:root[data-theme="dark"] .seg-thumb {
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, .08);
}

/* =========================================================== РАЗДЕЛ НАСТРОЕК */
.setsec {
  border: 1px solid var(--line);
  border-radius: var(--r);
  background: var(--field2);
  padding: 14px 16px;
  margin-bottom: var(--gap);
}
.setsec > .t {
  font-size: 11px; font-weight: var(--fw-semi); letter-spacing: .06em; text-transform: uppercase;
  color: var(--ink3);
  margin-bottom: 10px;
}
.setsec > .hint { font-size: 11.5px; color: var(--ink3); margin: -4px 0 10px; line-height: 1.5; }

/* ═══════════════════════════════════════════════════════════════════════
   ОБРЕЗКА ПО СКРУГЛЕНИЮ — ОДНО ПРАВИЛО НА ВСЮ ПРОГРАММУ.

   Коробка со скруглением обязана обрезать содержимое по своей форме.
   Иначе прямоугольные потомки с собственной заливкой — шапка таблицы,
   первая строка, выделенный пункт — рисуются поверх скруглённого угла,
   и из-под него торчат белые уголки.

   Обрезка сделана через overflow: clip, а не hidden. hidden превращает
   коробку в область прокрутки: прилипающая шапка внутри перестаёт
   прилипать, а колесо мыши начинает прокручивать саму карточку. clip
   обрезает и только.

   Всё, что обязано выходить за пределы коробки, — выпадающие списки,
   палитра цвета, всплывающие подсказки — ставится по окну, а не внутри
   потока. Так это и сделано: см. .combo-drop и .cp-pop.
   ═══════════════════════════════════════════════════════════════════ */
.card, .grid-table, .kard, .toast, .dialog, .panel,
.segment, .seg, .wg-quick, .eco-fold, .code-copy, .code-bar, .wg-bar,
.dash-quota-t, .swatch, .chip, .tab, .btn, .icon-btn, .stepper-btn,
.checkbox-box, .avatar, .cp-pop, .combo-drop, .selm {
  overflow: clip;
}
/* Прокручиваемым областям обрезка не нужна — у них своя. */
.grid-rows, .scr, .screen, .rail, .panel-body { overflow: auto; }

/* ============================================================= КАРТОЧКИ
   Atlassian: скругление 8, тень raised. Рамки у карточки нет — её роль
   играет тень: два слоя, второй в 1px даёт чёткий край на любом фоне. */
.card {
  background: var(--card);
  border: none;
  border-radius: var(--r-l);
  box-shadow: var(--sh-raised);
}
.card-pad  { padding: var(--gap); }
.card-head {
  display: flex; align-items: center; gap: var(--sp-100);
  padding: var(--sp-150) var(--gap);
  border-bottom: var(--bw) solid var(--line);
}
.card-title { font-family: var(--font-h); font-weight: var(--fw-bold);
              font-size: var(--fs-l); line-height: var(--lh-l); }

/* ================================================================= МЕТКИ
   ЕДИНСТВЕННОЕ ИСКЛЮЧЕНИЕ ИЗ ДИЗАЙН-КОДА: метки взяты у Apple, а не
   у Atlassian. Apple: таблетка (скругление по высоте), 13/600, высота 22,
   отступ 9, БЕЗ рамки — цвет несёт заливка. Atlassian-овский вариант
   (прямоугольник 4, 11/700 заглавными) выглядит как ярлык на складе;
   таблетка мягче и лучше сочетается с округлыми аватарами.

   Правило для новых меток: заливка — слабый цвет состояния, текст — его же
   тёмный вариант, рамки нет никогда. */
.chip {
  gap: var(--sp-075);
  height: 22px;
  padding: 0 9px;
  font-size: 13px;
  font-weight: 600;
  line-height: 1;
  /* Метка тоже не таблетка. Форма в программе одна, и исключений из неё
     нет: рядом с кнопкой в одном ряду таблетка читается как ещё одна
     кнопка, только почему-то другой формы. Скругление считается от роста
     метки той же долей, что у всего остального. */
  border-radius: calc(22px * var(--round));
  background: var(--a-200);
  border: none;
  color: var(--ink2);
}
.chip.accent { color: var(--acc-ink);
  background: linear-gradient(180deg, var(--acc-weak) 0%, var(--acc-weak-2) 100%); }
.chip.danger { color: var(--danger-ink);
  background: linear-gradient(180deg, var(--danger-weak) 0%, var(--danger-weak-2) 100%); }
.chip.warn   { color: var(--warn-ink);
  background: linear-gradient(180deg, var(--warn-weak) 0%, var(--warn-weak-2) 100%); }
.chip.good   { color: var(--good-ink);
  background: linear-gradient(180deg, var(--good-weak) 0%, var(--good-weak-2) 100%); }
/* Плотная метка для таблиц, где 22 много. */
.chip.small  { height: 18px; padding: 0 7px; font-size: 11.5px; }

/* ============================================================== ТАБЛИЦЫ
   Atlassian: шапка 12/700 БЕЗ заглавных, линия под шапкой 2, строка 32,
   отступы 4/8. Заглавные буквы в шапке убраны сознательно — на длинных
   названиях материалов они читаются хуже и занимают больше места. */
table.list { width: 100%; border-collapse: collapse; }
table.list th {
  height: var(--h-row-head);
  padding: var(--sp-050) var(--sp-100);
  font-size: var(--fs-s); line-height: var(--lh-s);
  font-weight: var(--fw-semi); text-align: left;
  color: var(--ink2);
  border-bottom: var(--bw-sel) solid var(--a-300);
  background: var(--card);
}
/* Прилипающая шапка — только там, где таблица одна и прокручивается сама.
   По умолчанию её нет: на экране с несколькими таблицами подряд шапка
   каждой из них вставала на нулевую отметку и повисала поверх строк
   предыдущей — выглядело как сквозной пропуск строк. Включается классом
   на таблице, а не достаётся всем даром. */
table.list.sticky-head th {
  position: sticky; top: 0;
  z-index: var(--z-sticky);
  box-shadow: var(--sh-overflow);
}

/* Длинная таблица прокручивается САМА, в своей коробке.
   Пока прокручивался весь экран, шапка липла к его внутреннему краю — а
   выше края остаётся полоска отступа, и сквозь неё проезжали строки:
   выглядело так, будто оглавление наехало на список. Со своей коробкой
   шапке некуда съезжать, и полоски над ней не существует. */
.list-scroll { max-height: min(64vh, 720px); overflow: auto; }
table.list td {
  height: var(--h-row);
  padding: var(--sp-050) var(--sp-100);
  font-size: var(--fs); line-height: var(--lh);
  vertical-align: middle;
}
table.list tbody tr { border-bottom: var(--bw) solid var(--line); }
table.list tbody tr:last-child { border-bottom: 0; }
table.list tbody tr:hover { background: var(--a-200); }
table.list tbody tr.selected {
  background: linear-gradient(180deg, var(--acc-weak) 0%, var(--acc-weak-2) 100%); }
table.list td.right, table.list th.right { text-align: right; }
table.list tr.muted td { color: var(--ink3); }

/* Таблица-сетка: шапка и строки берут одно описание столбцов — разъехаться
   не могут. */
.grid-table { border: var(--bw) solid var(--line); border-radius: var(--r-l); overflow: hidden; }
.grid-head, .grid-row { display: grid; gap: var(--sp-100); align-items: center;
                        padding: var(--sp-075) var(--sp-150); }
.grid-head {
  font-size: var(--fs-s); line-height: var(--lh-s); font-weight: var(--fw-semi);
  color: var(--ink2);
  background: var(--a-100);
  border-bottom: var(--bw-sel) solid var(--a-300);
}
.grid-head .right, .grid-row .right { text-align: right; }
.grid-row { min-height: var(--h-row); }
.grid-row + .grid-row { border-top: var(--bw) solid var(--line); }
.grid-row:hover { background: var(--a-200); }
.grid-rows { max-height: min(56vh, 620px); overflow: auto; }

/* Действия над строкой списка: копия и удаление. Стоят вместе и всегда
   в одном порядке — сначала безобидное, потом опасное. */
.row-acts { display: inline-flex; align-items: center; gap: var(--sp-050); }

.empty-state { padding: var(--sp-500) var(--sp-300); text-align: center; color: var(--ink3); }

/* =============================================================== ВКЛАДКИ
   Atlassian: высота 32, отступ 8, скругление 6, подчёркивание 2 с отступом
   8 с каждой стороны — линия короче самой вкладки, это её примета. */
.tabs { display: flex; flex-wrap: wrap; gap: var(--sp-050); }
.tab {
  position: relative;
  height: var(--h-btn);
  padding: 0 var(--sp-150);
  font-weight: var(--fw-med); font-size: var(--fs); line-height: var(--lh);
  color: var(--ink2);
  background: none; border: none;
  border-radius: var(--r);
  transition: background .15s, color .15s;
}
.tab:hover { background: var(--a-200); color: var(--ink); }
.tab:focus-visible { outline: var(--bw-foc) solid var(--focus); outline-offset: 2px; }
/* Выбранная вкладка — заливка градиентом слабого фирменного цвета, а не
   полоска снизу. Полоска приклеивала вкладки к линии и на длинных
   названиях читалась как подчёркнутая ссылка. */
.tab.active {
  color: var(--acc-ink);
  background: linear-gradient(180deg, var(--acc-weak) 0%, var(--acc-weak-2) 100%);
}
.tab.active:hover { background: linear-gradient(180deg, var(--acc-weak-2) 0%, var(--acc-weak) 100%); }


/* Второе имя сегмента описано выше, вместе с первым: два описания одного
   и того же элемента расходились — у .segment бегунок был со скруглением
   8, у .seg — тоже 8, но подложка у обоих 12 при зазоре 2. */

/* ================================================= ВЫПАДАЮЩИЙ СПИСОК */
.combo { position: relative; width: 100%; }
.combo-button {
  display: flex; align-items: center; gap: 8px;
  width: 100%;
  height: var(--h-field);
  padding: 0 var(--sp-150);
  font-size: var(--fs);
  text-align: left;
  color: var(--ink);
  background: var(--field);
  border: var(--bw) solid var(--line2);
  border-radius: var(--r);
  transition: border-color .15s, background .15s;
}
.combo-button:hover { background: var(--field2); }
.combo-button:disabled { opacity: .5; cursor: not-allowed; }
.combo.open .combo-button { border-color: var(--acc); background: var(--card); }
.combo.empty .combo-label { color: var(--ink3); }
.combo.invalid .combo-button { border-color: var(--danger); background: var(--danger-weak); }

.combo-label { flex: 1 1 auto; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.combo-caret { color: var(--ink3); font-size: 10px; }

/* Список стоит по окну: любой предок, обрезающий содержимое по своим
   скруглённым углам, обрезал бы и его. Положение считает combo.js. */
.combo-drop {
  position: fixed; z-index: var(--z-popover);
  display: none;
  padding: var(--sp-100);
  background: var(--card);
  border: none;
  border-radius: var(--r-l);
  box-shadow: var(--sh-overlay);
}

.combo.open .combo-drop { display: block; }

.combo-search {
  width: 100%; padding: 7px 10px; margin-bottom: 6px;
  font-size: 13px;
  background: var(--field2);
  border: var(--bw) solid var(--line2);
  border-radius: var(--r-s);
}
.combo-options { max-height: 264px; overflow: auto; }
.combo-option { display: flex; align-items: center; gap: 8px; padding: 8px 10px;
                border-radius: var(--r-s); cursor: pointer; }
.combo-option-title { flex: 1 1 auto; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.combo-option-note  { color: var(--ink3); font-size: 11.5px; }
.combo-option:hover, .combo-option.active { background: var(--field2); }
.combo-option.selected { color: var(--acc-ink); font-weight: 600;
  background: linear-gradient(180deg, var(--acc-weak) 0%, var(--acc-weak-2) 100%); }
.combo-empty { padding: 14px 10px; text-align: center; color: var(--ink3); }

/* ================================================================ ЦВЕТ */
/* Кружок выбора цвета — как в основе: круглый, без рамки, выбранный
   обведён двойным кольцом (фон карточки + чернила). Градиент ложится
   в background целиком, поэтому кружок показывает ровно то, что получит
   кнопка. */
.swatch {
  width: 28px; height: 28px; flex: none;
  border-radius: 50%;
  border: none;
  padding: 0;
  appearance: none; -webkit-appearance: none;
  background-clip: border-box;
  cursor: pointer;
  transition: box-shadow .15s, transform .1s;
}
.swatch:hover { transform: scale(1.08); }
.swatch.on { box-shadow: 0 0 0 2.5px var(--card), 0 0 0 5px var(--ink); }
.swatch:focus-visible { outline: var(--bw-foc) solid var(--focus); outline-offset: 3px; }

/* Выбор цвета. Раньше разметка создавала «.cp-pop» и «.cp», а оформление
   было написано на «.colorpop» — палитра открывалась совсем без стилей:
   без положения, без фона, без размеров, то есть визуально не работала
   вовсе. Классы сведены к тем, что реально создаются. */
.cp-pop {
  position: fixed; z-index: var(--z-popover);
  width: 248px;
  background: var(--card);
  border: none;
  border-radius: var(--r-l);
  padding: var(--sp-150);
  box-shadow: var(--sh-overlay);
}
.cp { display: flex; flex-direction: column; gap: var(--sp-150); }
.cp .cp-sv {
  position: relative; height: 140px;
  border-radius: var(--r-s);
  cursor: crosshair;
  touch-action: none;
}
.cp .cp-sv:focus-visible { outline: var(--bw-foc) solid var(--focus); outline-offset: 2px; }
.cp .cp-dot {
  position: absolute; width: 16px; height: 16px;
  border: 3px solid #fff; border-radius: 50%;
  transform: translate(-50%, -50%);
  box-shadow: 0 0 0 1px rgba(0, 0, 0, .35), 0 1px 4px rgba(0, 0, 0, .45);
  pointer-events: none;
}
.cp .cp-hue {
  -webkit-appearance: none; appearance: none;
  width: 100%; height: 14px; margin: 0; padding: 0; border: none;
  border-radius: var(--r-full);
  background: linear-gradient(90deg, #f00, #ff0, #0f0, #0ff, #00f, #f0f, #f00);
  cursor: pointer;
}
.cp .cp-hue::-webkit-slider-runnable-track { background: none; height: 14px; border: none; }
.cp .cp-hue::-webkit-slider-thumb {
  -webkit-appearance: none; appearance: none;
  width: 18px; height: 18px; margin-top: -2px;
  border-radius: 50%;
  border: 3px solid #fff; background: transparent;
  box-shadow: 0 0 0 1px rgba(0, 0, 0, .35);
  cursor: pointer;
}
.cp .cp-hue::-moz-range-track { background: none; height: 14px; border: none; }
.cp .cp-hue::-moz-range-thumb {
  width: 18px; height: 18px; border-radius: 50%;
  border: 3px solid #fff; background: transparent;
  box-shadow: 0 0 0 1px rgba(0, 0, 0, .35);
}
.cp .cp-row  { display: flex; gap: var(--sp-100); align-items: center; }
.cp .cp-prev {
  width: var(--h-field); height: var(--h-field); flex: none;
  border-radius: var(--r); border: var(--bw) solid var(--a-300);
}
.cp .cp-hex  { flex: 1 1 auto; font-family: var(--num); font-variant-numeric: tabular-nums; text-transform: uppercase;
               text-align: center; }

/* ============================================================ СООБЩЕНИЯ */
#toasts {
  position: fixed; z-index: var(--z-toast);
  right: 20px; bottom: 20px;
  display: flex; flex-direction: column; gap: 10px;
  max-width: min(420px, calc(100vw - 40px));
}
/* Вид сообщения несёт заливка, а не полоска слева: цвет уходит от левого
   края вглубь и растворяется в карточке. Полоска в 2 пикселя выглядела
   как обрезок и на широком сообщении терялась вовсе. */
.toast {
  position: relative;
  border: none;
  border-radius: var(--r-l);
  box-shadow: var(--sh-overlay);
  padding: var(--sp-200);
  padding-right: var(--sp-500);
  min-width: 300px;
  background: linear-gradient(100deg,
              var(--acc-weak-2) 0%, var(--acc-weak) 30%, var(--card) 74%);
  opacity: 0; transform: translateY(12px);
  transition: opacity .22s, transform .22s;
}
.toast.shown { opacity: 1; transform: none; }
.toast.good   { background: linear-gradient(100deg,
                var(--good-weak-2) 0%, var(--good-weak) 30%, var(--card) 74%); }
.toast.warn   { background: linear-gradient(100deg,
                var(--warn-weak-2) 0%, var(--warn-weak) 30%, var(--card) 74%); }
.toast.danger { background: linear-gradient(100deg,
                var(--danger-weak-2) 0%, var(--danger-weak) 30%, var(--card) 74%); }
.toast-title { font-family: var(--font-h); font-weight: var(--fw-bold);
               font-size: var(--fs); line-height: var(--lh); color: var(--ink); }
.toast.danger .toast-title { color: var(--danger-ink); }
.toast.warn .toast-title   { color: var(--warn-ink); }
.toast-text  { font-size: var(--fs-s); line-height: var(--lh-s); color: var(--ink2); margin-top: var(--sp-025); }
/* Крестик в уведомлении — тот же крестик, что в окне, панели и строке
   таблицы: класс `.icon-btn.small`. Своей формы у него нет и быть не
   должно. Раньше он был нарисован отдельно — другой размер, другая
   посадка, розовый кружок при наведении, — и на фоне остальных крестиков
   программы выглядел чужим. */
.toast-close {
  position: absolute;
  top: var(--sp-100);
  right: var(--sp-100);
}

/* ============================================================= ОКНА */
.scrim {
  position: fixed; inset: 0;
  display: grid; place-items: center;
  padding: 20px;
  background: rgba(24, 22, 18, .44);
  backdrop-filter: blur(3px);
  opacity: 0; transition: opacity .18s;
}
.scrim.shown { opacity: 1; }
.dialog-scrim { z-index: var(--z-dialog); }
.panel-scrim  { z-index: var(--z-overlay); }

.dialog {
  width: min(440px, 94vw);
  padding: 24px;
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: var(--r-l);
  box-shadow: var(--shadow-lg);
  transform: scale(.94);
  transition: transform .16s;
}
.scrim.shown .dialog { transform: none; }
.dialog-title { font-family: var(--font-h); font-weight: var(--fw-bold); font-size: 17px; }
.dialog.danger .dialog-title { color: var(--danger); }
.dialog-body { margin-top: 8px; color: var(--ink2); font-size: 13.5px; line-height: 1.5; }
.dialog-body .field-row { color: var(--ink); }
.dialog-actions { display: flex; justify-content: flex-end; gap: 10px; margin-top: 20px; }

.panel {
  display: flex; flex-direction: column;
  max-height: calc(100vh - 40px);
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: var(--r-l);
  box-shadow: var(--shadow-lg);
  overflow: hidden;
  transform: scale(.97);
  transition: transform .16s;
}
.scrim.shown .panel { transform: none; }
.panel-head { display: flex; align-items: center; gap: 10px;
              padding: 15px 24px; border-bottom: 1px solid var(--line); }
.panel-title { flex: 1 1 auto; font-family: var(--font-h); font-size: 17px; font-weight: var(--fw-bold); }
.panel-body { flex: 1 1 auto; overflow: auto; padding: 20px 24px 24px; }

/* ========================================================== ОБОЛОЧКА */
#root { height: 100%; }

/* ═════════════════════════════════════════════════════ ЭКРАН ВХОДА
   Единственный экран, который человек видит до того, как программа стала
   его рабочим местом, — и единственный, где уместно оформление ради
   впечатления. Отсюда во весь экран мэш-градиент: те же цвета, что в
   знаке DEOS, но разбавленные почти до пастели. Разбавление обязательно:
   насыщенный перелив во весь экран пестрит, спорит с формой входа и
   мешает читать поля.

   Мэш собран из пяти мягких пятен по углам и в середине. Каждое гаснет
   в СВОЙ ЖЕ цвет с нулевой непрозрачностью, а не в «transparent»:
   «transparent» — это прозрачный ЧЁРНЫЙ, и переход к нему даёт по краям
   пятна грязную серую кайму. */
.login {
  position: relative;
  display: grid; place-items: center; min-height: 100%; padding: var(--sp-300);
  background: var(--bg);
  overflow: hidden;
}
.login::before {
  content: "";
  position: absolute; inset: -10%;
  z-index: 0;
  background:
    radial-gradient(46% 52% at 14% 8%, rgba(244, 204, 205, .85) 0%, rgba(244, 204, 205, 0) 68%),
    radial-gradient(50% 46% at 86% 4%, rgba(247, 233, 196, .80) 0%, rgba(247, 233, 196, 0) 68%),
    radial-gradient(52% 54% at 6% 92%, rgba(181, 224, 223, .80) 0%, rgba(181, 224, 223, 0) 68%),
    radial-gradient(48% 50% at 94% 88%, rgba(158, 214, 223, .78) 0%, rgba(158, 214, 223, 0) 68%),
    radial-gradient(70% 64% at 50% 58%, rgba(219, 221, 209, .55) 0%, rgba(219, 221, 209, 0) 68%);
  pointer-events: none;
}
[data-theme="dark"] .login::before {
  background:
    radial-gradient(46% 52% at 14% 8%, rgba(76, 49, 51, .85) 0%, rgba(76, 49, 51, 0) 68%),
    radial-gradient(50% 46% at 86% 4%, rgba(78, 69, 45, .80) 0%, rgba(78, 69, 45, 0) 68%),
    radial-gradient(52% 54% at 6% 92%, rgba(32, 63, 64, .80) 0%, rgba(32, 63, 64, 0) 68%),
    radial-gradient(48% 50% at 94% 88%, rgba(17, 56, 63, .78) 0%, rgba(17, 56, 63, 0) 68%),
    radial-gradient(70% 64% at 50% 58%, rgba(58, 61, 54, .55) 0%, rgba(58, 61, 54, 0) 68%);
}

.login-card {
  position: relative; z-index: 1;
  width: min(420px, 100%);
  padding: var(--sp-400) var(--sp-400) var(--sp-300);
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: var(--r-xl);
  box-shadow: 0 24px 60px rgba(28, 27, 24, .14), var(--sh-overlay);
}
/* Знак на входе — та же разметка, что в шапке, только заметно крупнее:
   здесь он главное, что есть на экране, а не пометка в углу. Высотой
   задаётся весь размер: ширину рисунок берёт из своих пропорций. */
.login-brand { height: 52px; color: var(--ink); margin-bottom: var(--sp-100); }
.login-brand svg { display: block; height: 100%; width: auto; }
.login-brand + .login-form { margin-top: var(--sp-300); }
.login-sub   { color: var(--ink2); margin: 0 0 var(--sp-300); font-size: var(--fs-l); }
.login-form  { display: flex; flex-direction: column; gap: 14px; }
.login-error { padding: 9px 12px; color: var(--danger); background: var(--danger-weak);
               border-radius: var(--r-s); font-size: 13px; }

/* Кнопка входа — единственная кнопка на экране и единственное место, где
   перелив уместен целиком: экран входа для впечатления и сделан. Внутри
   программы такого нет и не будет — там кнопок в ряду по три, и перелив
   на каждой превратил бы работу в ярмарку.

   ПЕРЕЛИВ СОБРАН КАК МЭШ, а не как одна линия. Линейный градиент идёт
   строго в одну сторону, и переход между цветами читается полосами.
   Здесь вместо него четыре мягких пятна (радиальные градиенты) поверх
   ровной подложки: пятна перекрываются краями, и граница между цветами
   размывается — переход виден как свет, а не как шов.

   Порядок слоёв важен: пятна лежат сверху, ровный цвет — последним. Он
   закрывает углы, куда пятна не достают, и без него на скруглениях
   проступал бы фон страницы.

   НАДПИСЬ обычными буквами, крупная и тонкая. Прописные с разрядкой
   выглядели как вывеска: слово короткое, а растянуто во всю кнопку.

   СВЕЧЕНИЕ идёт со всех сторон и видно в любой теме. Раньше это была
   тень со смещением вниз, и на светлом фоне она читалась не как свечение,
   а как подложенная под кнопку серая плашка. Теперь это два кольца без
   смещения: ближнее плотное, дальнее размытое. */
.login-form .btn.primary {
  height: 56px;
  border-radius: calc(56px * var(--round));
  border: none;
  font-family: var(--font-h);
  font-size: 19px;
  font-weight: 300;
  letter-spacing: .01em;
  text-transform: none;
  color: #fff;
  background-image:
    radial-gradient(78% 150% at 6% 20%, #A8555A 0%, rgba(168, 85, 90, 0) 62%),
    radial-gradient(72% 150% at 34% 90%, #B08C42 0%, rgba(176, 140, 66, 0) 60%),
    radial-gradient(80% 160% at 68% 10%, #2F8480 0%, rgba(47, 132, 128, 0) 64%),
    radial-gradient(90% 170% at 100% 80%, #00636F 0%, rgba(0, 99, 111, 0) 70%),
    linear-gradient(100deg, #8E4B4D 0%, #A98A44 32%, #2A7C7A 64%, #00636F 100%);
  background-size: 100% 100%;
  background-origin: border-box;
  background-repeat: no-repeat;
  box-shadow: 0 0 0 1px rgba(0, 99, 111, .18),
              0 0 28px 2px rgba(0, 99, 111, .30),
              0 0 60px 6px rgba(140, 80, 90, .18);
  transition: filter .15s, box-shadow .2s, transform .12s;
}
.login-form .btn.primary:hover {
  filter: brightness(1.1);
  box-shadow: 0 0 0 1px rgba(0, 99, 111, .24),
              0 0 34px 4px rgba(0, 99, 111, .38),
              0 0 76px 10px rgba(140, 80, 90, .24);
}
.login-form .btn.primary:active { transform: translateY(1px); filter: brightness(.96); }
.login-form .btn.primary:disabled { filter: grayscale(.55) brightness(.9); box-shadow: none; }
/* ═══════════════════════════════════ РАЗДЕЛИТЕЛЬ ВНУТРИ КАРТОЧКИ
   Пунктир с подписью посередине. Нужен там, где две части одного дела
   стоят в одной карточке: сплошная черта разрезала бы карточку надвое,
   а пунктир показывает, что это по-прежнему одно целое. */
.io-split {
  display: flex; align-items: center; gap: var(--sp-150);
  margin: var(--sp-200) 0 var(--sp-100);
  color: var(--ink3);
  font-size: var(--fs-s);
  text-transform: uppercase;
  letter-spacing: .06em;
}
.io-split::before, .io-split::after {
  content: "";
  flex: 1 1 auto;
  border-top: 1px dashed var(--line);
}

/* ══════════════════════════════════════ МАССОВОЕ ИЗМЕНЕНИЕ
   Полоса над таблицей: сколько отмечено и что с этим можно сделать.
   Появляется вместе с первой отметкой и уходит вместе с последней —
   постоянная полоса занимала бы место ради кнопок, которые без
   выделения ничего не делают.

   Липкая: в категории на двести позиций отметить строки в начале и в
   конце — обычное дело, и кнопки не должны уезжать за край экрана. */
.bulk-bar {
  position: sticky;
  top: 0;
  z-index: 3;
  display: flex; flex-wrap: wrap; align-items: center;
  gap: var(--sp-100);
  margin-bottom: var(--sp-150);
  padding: var(--sp-100) var(--sp-150);
  background: var(--acc-weak);
  border: var(--bw) solid var(--acc-solid);
  border-radius: var(--r);
}
.bulk-bar b { color: var(--acc-ink); white-space: nowrap; }
.bulk-bar .spacer { flex: 1 1 auto; }

/* ══════════════════════════════════════ СТРОКА, К КОТОРОЙ ПОДВЕЛИ
   Переход из поиска открывает раздел целиком, и найденная строка в нём —
   одна из двухсот. Подсветка показывает, какая именно.

   Пульсирует, а не горит ровно: ровная заливка сливается с отмеченными
   строками и с наведением, а медленное биение глаз ловит сам, даже когда
   смотрит в другую часть экрана. Гаснет от наведения курсора — то есть
   ровно тогда, когда строку нашли. */
@keyframes row-spotlight {
  0%, 100% { background: var(--acc-weak); box-shadow: inset 3px 0 0 var(--acc-solid); }
  50%      { background: var(--acc-weak-2); box-shadow: inset 3px 0 0 var(--acc); }
}
.grid-row.spotlight {
  animation: row-spotlight 1.8s ease-in-out infinite;
  scroll-margin: 120px;
}
@media (prefers-reduced-motion: reduce) {
  .grid-row.spotlight { animation: none; background: var(--acc-weak);
                        box-shadow: inset 3px 0 0 var(--acc-solid); }
}

/* Отмеченная строка подсвечена: без подсветки при прокрутке непонятно,
   что именно попадёт под изменение. */
.grid-row.picked { background: var(--acc-weak); }

/* Флажок «выделить всё» стоит в шапке таблицы и не должен её раздувать. */
.grid-head .head-pick { padding: 0; }

/* Включённый значок в полосе действий строки: особый коэффициент задан. */
.icon-btn.on { color: var(--acc-ink); background: var(--acc-weak); }

/* ═════════════════════════════════════════ ПЕРЕКЛЮЧАТЕЛЬ ТЕМЫ
   Внизу по центру экрана входа. Внутри программы его нет: там тема
   живёт на экране «Оформление», вместе с цветом и остальным видом.

   Приглушён нарочно: это не действие экрана входа, а выручка для того,
   кому сейчас режет глаза. Под курсором проявляется полностью. */
.theme-switch {
  position: fixed;
  left: 50%;
  bottom: var(--sp-300);
  transform: translateX(-50%);
  z-index: 60;
  display: inline-flex; align-items: center; justify-content: center;
  gap: var(--sp-100);
  height: 34px;
  padding: 0 var(--sp-200);
  color: var(--ink2);
  background: var(--a-200);
  border: var(--bw) solid transparent;
  border-radius: var(--r-full);
  font-size: var(--fs-s);
  cursor: pointer;
  opacity: .42;
  backdrop-filter: blur(6px);
  transition: opacity .18s, background .18s, color .18s;
}
.theme-switch-icon { display: inline-flex; }
.theme-switch svg { width: 16px; height: 16px; display: block; }
.theme-switch:hover { opacity: 1; background: var(--a-300); color: var(--ink); }
.theme-switch:active { transform: translateX(-50%) scale(.97); }
.theme-switch:focus-visible {
  opacity: 1;
  outline: var(--bw-foc) solid var(--focus); outline-offset: 2px;
}
@media print { .theme-switch { display: none; } }

.login-note  { margin-top: 16px; padding-top: 14px; border-top: 1px solid var(--line);
               font-size: 12px; color: var(--ink3); }

.app {
  display: grid;
  grid-template-rows: var(--topbar-h) 1fr;
  grid-template-columns: var(--rail-w) 1fr;
  grid-template-areas: "brand topbar" "rail screen";
  height: 100%;
}

/* ═════════════════════════════════════════════════ УГОЛ СО ЗНАКАМИ
   Место делится ровно пополам: 1fr — черта — 1fr. Не «сколько знаку
   надо», а именно пополам, иначе широкий знак одной компании съедал бы
   место у знака другой, и одна и та же программа выглядела бы у разных
   заказчиков по-разному.

   Внутри своей половины знак растягивается до упора и уменьшается по
   СВОИМ пропорциям: знаки бывают вытянутые в строку и почти квадратные,
   ужать их к одной высоте нельзя. Отсюда contain у картинки и meet у
   разметки — оба вписывают рисунок целиком и ставят его по центру.

   Черта идёт во всю рабочую высоту угла, знаки чуть ниже её: они тянутся
   к черте, но не упираются в неё. */
/* min-height и min-width в ноль — не украшение. Ячейка сетки по
   умолчанию не сжимается меньше своего содержимого (min-height: auto), и
   квадратный знак компании раздвигал угол во всю свою высоту: знак DEOS
   уезжал вбок, а шапка налезала на боковое меню. С нулём ячейка держит
   ровно ту высоту, которую ей отвела сетка, а знак вписывается в неё. */
.brand-cell {
  grid-area: brand;
  display: grid;
  grid-template-columns: 1fr;
  align-items: stretch;
  min-height: 0; min-width: 0;
  padding: var(--sp-100) var(--sp-150);
  overflow: hidden;
  background: var(--card);
  border-right: 1px solid var(--line);
  border-bottom: 2px solid var(--acc-weak);
}
.brand-cell:has(.brand-line) { grid-template-columns: 1fr 1px 1fr; }
.brand-half {
  display: flex; align-items: center; justify-content: center;
  min-width: 0; min-height: 0; height: 100%;
  overflow: hidden;
  padding: var(--sp-050) var(--sp-100);
}
.brand-line { align-self: stretch; width: 1px; background: var(--line2); opacity: .55; }

.topbar {
  grid-area: topbar;
  display: flex; align-items: center; gap: 10px;
  padding: 0 18px;
  background: var(--card);
  border-bottom: 2px solid var(--acc-weak);
}
.topbar-title { font-family: var(--font-h); font-weight: var(--fw-semi); font-size: 15px; }
.topbar-who { text-align: right; line-height: 1.25; }
.topbar-who b { display: block; font-weight: var(--fw-semi); font-size: 13px; }
.topbar-who span { color: var(--ink3); font-size: 11.5px; }

.banner { display: inline-flex; align-items: center; gap: 8px;
          padding: 5px 13px; font-size: 12px; font-weight: var(--fw-med);
          border-radius: calc(26px * var(--round)); }
.banner.acting    { background: var(--acc-weak); color: var(--acc-ink); }
.banner.read-only { background: var(--warn-weak); color: var(--warn); }

.rail {
  grid-area: rail;
  display: flex; flex-direction: column; gap: 2px;
  padding: var(--sp-100) var(--sp-150) var(--sp-300);
  overflow: auto;
  background: var(--card);
  border-right: var(--bw) solid var(--line);
}
/* Заголовок группы отделяется от предыдущей чертой и воздухом, а не одним
   лишь мелким шрифтом: раньше группы читались как сплошной список, в
   котором подписи терялись среди пунктов. */
.rail-group {
  position: relative;
  margin-top: var(--sp-250);
  padding: var(--sp-200) var(--sp-150) var(--sp-075);
  font-size: 10px; font-weight: var(--fw-semi); letter-spacing: .09em;
  text-transform: uppercase;
  color: var(--ink3);
}
.rail-group::before {
  content: ""; position: absolute; left: var(--sp-150); right: var(--sp-150); top: 0;
  height: var(--bw); background: var(--line);
}
.rail-group:first-child { margin-top: 0; padding-top: var(--sp-050); }
.rail-group:first-child::before { display: none; }
.rail-item {
  display: flex; align-items: center; gap: 10px;
  padding: 9px 13px;
  font-weight: 600; font-size: 13.5px;
  text-align: left;
  color: var(--ink2);
  background: none; border: none;
  border-radius: var(--r);
  transition: background .15s, color .15s;
}
.rail-item:hover { background: var(--field2); color: var(--ink); }
.rail-item.active { color: var(--acc-ink);
  background: linear-gradient(180deg, var(--acc-weak) 0%, var(--acc-weak-2) 100%); }
.rail-spacer { flex: 1 1 auto; min-height: 12px; }

/* Метка наступившего срока. Висит на разделе «Доска», пока просроченное
   не убрано: сообщение показывается один раз, и без метки о пропущенном
   напоминании узнать было бы неоткуда. Мигает — иначе теряется среди
   названий разделов. */
/* ЦИФРА В КРУЖКЕ.
   Выключка по обеим осям — общим правилом наверху файла, но одного его
   мало: оно центрирует СТРОКУ, а не саму цифру. Строка выше цифры на
   высоту подстрочных элементов («у», «р»), которых в числе нет вовсе, и
   из-за этого цифра садится выше середины кружка.

   Разница посчитана, а не подобрана: у начертания подъём 10, спуск 3,
   краска цифры — 8 вверх от базовой линии и 0 вниз. При росте строки 11
   середина краски оказывается на полточки выше середины строки. Отступ
   сверху в одну точку при заданной высоте сдвигает содержимое ровно на
   половину — то есть на эти самые полточки.

   Правило вынесено на общий класс: любая цифра в кружке (метка на
   разделе, счётчик) берёт его и стоит ровно. */
.rail-badge, .num-round {
  padding-top: 1px;
}

.rail-badge {
  flex: none;
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 20px; height: 20px; padding: 1px var(--sp-075) 0;
  border-radius: var(--r-full);
  background: var(--danger); color: var(--danger-fg);
  font-family: var(--num); font-variant-numeric: tabular-nums; font-size: 11px; font-weight: var(--fw-semi);
  line-height: 1;
  animation: badgepulse 1.4s ease-in-out infinite;
}
@keyframes badgepulse {
  0%, 100% { box-shadow: 0 0 0 0 rgba(201, 55, 44, .55); }
  55%      { box-shadow: 0 0 0 6px rgba(201, 55, 44, 0); }
}

.screen { grid-area: screen; overflow: auto; padding: 22px 24px 40px; }
.screen-head { display: flex; align-items: flex-start; gap: var(--gap); margin-bottom: 20px; }
.screen-head h1 { font-size: 21px; }
.screen-head .screen-sub { color: var(--ink2); margin-top: 3px; font-size: 13px; }

.screen-loading, .screen-error { display: grid; place-items: center; min-height: 200px; color: var(--ink3); }
.screen-error { color: var(--danger); text-align: center; }

@media (max-width: 900px) {
  .app { grid-template-columns: 1fr; grid-template-areas: "topbar" "screen"; }
  .brand-cell, .rail { display: none; }
}

/* ══════════════════════════════════════════════════════ ГРАФИКИ
   Рисуются разметкой SVG (kit/chart.js). Цвета — только переменные
   оформления: график сам подчиняется теме и выбранному цвету, и ни одна
   картинка не знает, светлая сейчас тема или тёмная.

   Шесть оттенков для долей — не радуга, а один цвет разной плотности с
   двумя соседями. Пёстрая раскладка на печати превращается в кашу, а
   объяснить, почему «ручной ввод» именно фиолетовый, всё равно нечем. */
/* Высота картинки задана здесь, а не выводится из ширины. Иначе широкая
   карточка растягивала картинку по высоте вместе с шириной, и полкарточки
   уходило в пустоту, а соседние карточки в ряду переставали совпадать. */
.chart { display: block; width: 100%; height: var(--chart-h, 132px); overflow: visible; }
.chart-line, .chart-dot { vector-effect: non-scaling-stroke; }

.chart-bar { fill: var(--acc-weak-2); transition: fill .12s; }
.chart-bar:hover { fill: var(--acc); }
.chart-bar.on { fill: var(--acc); }
.chart-bar.zero { fill: var(--a-300); }

.chart-line { fill: none; stroke: var(--acc-solid); stroke-width: 2;
              stroke-linejoin: round; stroke-linecap: round;
              vector-effect: non-scaling-stroke; }
.chart-area { fill: var(--acc-weak); stroke: none; }
.chart-dot { fill: var(--acc-solid); }

.chart-ring { stroke: var(--a-200); }
.chart-arc { transition: opacity .12s; }
.chart-arc:hover { opacity: .82; }
.chart-center {
  font-family: var(--font-h); font-size: 22px; font-weight: var(--fw-bold);
  fill: var(--ink);
}

/* Оттенки долей. Первый — выбранный цвет программы, дальше он же слабее
   и два соседних тона. */
.tone-0 { stroke: var(--acc-solid); color: var(--acc-solid); }
.tone-1 { stroke: var(--acc); color: var(--acc); }
.tone-2 { stroke: var(--acc-weak-2); color: var(--acc-weak-2); }
.tone-3 { stroke: var(--warn); color: var(--warn); }
.tone-4 { stroke: var(--ink3); color: var(--ink3); }
.tone-5 { stroke: var(--a-300); color: var(--a-300); }

/* Подписи под графиком описаны в dash.css одной записью .chart-ticks —
   общей для столбиков и для линии. Прежних двух видов (.chart-axis по
   краям и .chart-days сеткой) больше нет: от них два графика в соседних
   карточках выходили с разным отступом до подписей и разным кеглем. */

.dash-donut { display: flex; align-items: center; gap: var(--gap); flex-wrap: wrap; }
.dash-donut .chart-donut { width: 132px; flex: none; }

.chart-legend { list-style: none; margin: 0; padding: 0; flex: 1 1 160px; min-width: 0; }
.chart-legend li {
  display: flex; align-items: center; gap: var(--sp-100);
  padding: var(--sp-050) 0;
  font-size: var(--fs-s);
}
.chart-legend i {
  width: 10px; height: 10px; flex: none; border-radius: 3px;
  background: currentColor;
}

/* Полосы в строку: имя, полоса, число. Читается как список, а не как
   картинка, — поэтому годится там, где у каждой доли есть имя. */
.chart-rows { display: grid; gap: var(--sp-100); }
.chart-row {
  display: grid; grid-template-columns: minmax(0, 96px) 1fr auto;
  align-items: center; gap: var(--sp-100);
  font-size: var(--fs-s);
}
.chart-row-t { color: var(--ink2); overflow: hidden; text-overflow: ellipsis;
               white-space: nowrap; }
.chart-row-bar {
  height: 8px; border-radius: var(--r-full);
  background: var(--a-200);
  overflow: hidden;
}
.chart-row-bar i {
  display: block; height: 100%;
  background: var(--acc-solid);
  border-radius: var(--r-full);
}
.chart-row-n { color: var(--ink2); }

/* ================================================================ СВОДКА
   Экран администратора. Сверху плитки главных чисел, дальше сетка
   карточек, которая сама решает, сколько колонок влезло.

   ГЛАВНОЕ ЧИСЛО И СРАВНЕНИЕ. Под каждым числом стоит приписка: насколько
   это больше или меньше прошлого такого же периода. Она нарочно мельче и
   приглушённее — главное читается сразу, сравнение вторым взглядом. Одним
   кеглем эти две вещи спорили бы за внимание. */
.dash-top {
  display: grid; gap: var(--sp-150);
  grid-template-columns: repeat(auto-fit, minmax(210px, 1fr));
  margin-bottom: var(--sp-200);
}
.dash-tile {
  display: flex; flex-direction: column; gap: 2px;
  min-height: 92px;
  padding: var(--sp-150) var(--sp-200);
  background: var(--card);
  border: var(--bw) solid var(--line);
  border-radius: var(--r-l);
  box-shadow: var(--sh-raised);
}
.dash-tile-l {
  font-size: 11px; font-weight: var(--fw-semi); letter-spacing: .06em;
  text-transform: uppercase; color: var(--ink3);
}
.dash-tile-v {
  font-family: var(--font-h); font-size: 26px; font-weight: var(--fw-bold);
  line-height: 1.15; color: var(--ink);
}
.dash-tile-d {
  display: flex; align-items: center; gap: 4px;
  min-height: 16px;
  font-size: 11.5px; line-height: 1.3; color: var(--ink3);
}
.dash-tile-d.up { color: var(--good-ink); }
.dash-tile-d.down { color: var(--danger-ink); }
.dash-tile-arrow { display: inline-flex; }

.dash-grid {
  display: grid; gap: var(--gap);
  grid-template-columns: repeat(auto-fit, minmax(340px, 1fr));
  align-items: start;
  margin-top: 6px;
}
.dash-card { min-width: 0; }
/* Карточки со списком из четырёх колонок занимают две клетки сетки:
   в одной их содержимое обрезалось до многоточий. */
@media (min-width: 1180px) { .dash-card.wide { grid-column: span 2; } }
/* Ячейки сводки не должны распирать карточку: длинное имя обрезается
   многоточием, а колонка с суммой остаётся на месте. */
.dash-card .grid-head > *, .dash-card .grid-row > * {
  min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.dash-card .grid-table { border-radius: var(--r); }
.dash-card .card-pad { padding: 14px 16px 16px; }
.dash-note { font-size: 11.5px; }

/* Столбики по дням месяца. Высота задаётся заливкой, а не самим столбцом,
   чтобы подпись с числом всегда оставалась внизу на одной линии. */
.dash-bars {
  display: flex; align-items: flex-end; gap: 3px;
  height: 132px;
}
.dash-bar {
  flex: 1 1 0; min-width: 0;
  display: flex; flex-direction: column; justify-content: flex-end; align-items: center;
  height: 100%;
}
.dash-bar-fill {
  width: 100%;
  min-height: 2px;
  border-radius: var(--r-s) var(--r-s) 2px 2px;
  background: var(--acc-weak);
  transition: background .15s;
}
.dash-bar:hover .dash-bar-fill { background: var(--acc); }
.dash-bar.now .dash-bar-fill { background: var(--acc); }
.dash-bar-d {
  margin-top: 5px;
  font-size: 9px; line-height: 1;
  color: var(--ink3);
  font-variant-numeric: tabular-nums;
}
.dash-bar.now .dash-bar-d { color: var(--acc-ink); font-weight: var(--fw-semi); }

/* ЖУРНАЛ ИЗМЕНЕНИЙ.
   Строка стала выше: в ней три разных вещи — что сделано, с чем, и кем
   когда. Втиснутые в одну линию, они читались как сплошной текст. Слева
   действие с подробностью, справа время и человек. */
.dash-feed { list-style: none; margin: 0; padding: 0; }
.dash-feed-i {
  display: flex; align-items: flex-start; justify-content: space-between;
  gap: var(--sp-200);
  padding: var(--sp-150) 0;
  border-bottom: var(--bw) solid var(--line);
  font-size: var(--fs);
}
.dash-feed-i:last-child { border-bottom: 0; }
.dash-feed-main { min-width: 0; }
.dash-feed-a { display: block; font-weight: var(--fw-med); color: var(--ink); }
.dash-feed-d { margin-top: 2px; color: var(--ink2); font-size: var(--fs-s); }
.dash-feed-side {
  flex: none; text-align: right;
  display: flex; flex-direction: column; gap: 2px;
}
.dash-feed-t { color: var(--ink3); font-variant-numeric: tabular-nums; font-size: var(--fs-s); }
.dash-feed-w { color: var(--ink3); font-size: var(--fs-s); }

/* Сворачивание журнала. Он длинный и нужен не каждый день, а места
   занимал столько же, сколько все числа сводки вместе. */
.dash-fold { padding: 0; }
.dash-fold-head {
  display: flex; align-items: center; gap: var(--sp-150);
  width: 100%;
  padding: var(--sp-150) var(--gap);
  background: none; border: none; cursor: pointer;
  text-align: left;
}
.dash-fold-head:hover { background: var(--a-100); }
.dash-fold-x { display: inline-flex; color: var(--ink3); transition: transform .18s; }
.dash-fold.open .dash-fold-x { transform: rotate(180deg); }
.dash-fold-body { display: none; padding: 0 var(--gap) var(--gap); }
.dash-fold.open .dash-fold-body { display: block; }

/* Список фактов о компании. */
.dash-facts { display: grid; grid-template-columns: 1fr auto; gap: 0; margin: 0; }
.dash-facts dt, .dash-facts dd {
  margin: 0; padding: 7px 0;
  border-bottom: 1px solid var(--line);
  font-size: 12.5px;
}
.dash-facts dt { color: var(--ink2); }
.dash-facts dd { text-align: right; font-weight: var(--fw-med); font-variant-numeric: tabular-nums; }
.dash-facts dt:nth-last-of-type(1), .dash-facts dd:nth-last-of-type(1) { border-bottom: 0; }

.dash-quota { margin-top: 12px; }
.dash-quota-l {
  display: flex; justify-content: space-between; gap: 10px;
  font-size: 11.5px; color: var(--ink2); margin-bottom: 5px;
}
.dash-quota-t {
  height: 7px; border-radius: var(--r-full);
  background: var(--field2);
  border: 1px solid var(--line);
  overflow: hidden;
}
.dash-quota-f { height: 100%; background: var(--acc); border-radius: var(--r-full); }
.dash-quota.over .dash-quota-f { background: var(--danger); }
.dash-quota.over .dash-quota-l { color: var(--danger); }
/* Место кончается — предупреждение, а не тревога: программа справится
   сама, вытеснив картинки, и красный тут был бы неправдой. */
.dash-quota.tight .dash-quota-f { background: var(--warn); }
.dash-quota.tight .dash-quota-l { color: var(--warn-ink); }
.dash-quota-note {
  margin-top: var(--sp-075);
  font-size: var(--fs-s); line-height: 1.45;
  color: var(--ink2);
}
.dash-quota.over .dash-quota-note { color: var(--danger-ink); }

/* Ряд кружков выбора цвета. */
.swatches { display: flex; flex-wrap: wrap; gap: var(--sp-100); align-items: center; }

/* ============================================================= ЭКОНОМИКА
   Разбор цены на примере. Классы этого блока потерялись при объединении
   стилей в один файл — цепочка превращалась в сплошную простыню текста,
   потому что оформления у неё не осталось вовсе. */
.eco-fold {
  border: var(--bw) solid var(--line);
  border-radius: var(--r-l);
  background: var(--card);
  overflow: hidden;
  margin-top: var(--sp-300);
}
.eco-toggle {
  display: flex; align-items: center; gap: var(--sp-150);
  width: 100%;
  padding: var(--sp-150) var(--sp-200);
  background: transparent; border: none; text-align: left;
  color: var(--ink);
}
.eco-toggle:hover { background: var(--a-100); }
.eco-toggle-t { font-family: var(--font-h); font-weight: var(--fw-bold); font-size: var(--fs-l); }
.eco-toggle-n { flex: 1 1 auto; color: var(--ink3); font-size: var(--fs-s); }
.eco-toggle-x { display: inline-flex; color: var(--ink3); transition: transform .2s; }
.eco-fold.open .eco-toggle-x { transform: rotate(180deg); }
.eco-fold.open .eco-toggle { border-bottom: var(--bw) solid var(--line); }

.eco-body { display: none; padding: var(--sp-200); }
.eco-fold.open .eco-body { display: block; }

.eco-controls {
  display: grid; gap: var(--sp-150);
  grid-template-columns: repeat(auto-fit, minmax(200px, 240px));
  margin-bottom: var(--sp-200);
}
.eco-cost { max-width: 180px; }

/* Поля экономики — по размеру числа, а не во всю ширину экрана.
   Под коэффициент 1,85 и процент 4 отводить треть экрана незачем: поле
   шириной с абзац обещает, что туда впишут абзац.

   Колонки РАВНЫЕ и заданы числом, а не подбором по месту: при auto-fit
   на широком экране между двумя полями появлялась пустота в пол-экрана,
   а пояснения под ними, разной длины, разводили низ блока на разную
   высоту — и следующий за ними разбор вставал криво.

   Пояснения выровнены по общей строке сетки: подписи в первой, поля во
   второй, пояснения в третьей. Тогда поля стоят на одной линии, сколько
   бы строк ни занял текст под соседним. */
.eco-grid {
  grid-template-columns: repeat(auto-fit, minmax(240px, 260px));
  gap: var(--sp-150) var(--sp-400);
  align-items: start;
  margin-bottom: var(--sp-300);
}
/* Каждая настройка — своя карточка с рамкой. Без рамок два числа с
   подписями и пояснениями читались одним пятном: где кончается наценка и
   начинается рекламация, приходилось разбирать по смыслу текста, а не
   видеть глазом. Рамка тонкая и без заливки — это разделитель, а не
   выделение: настройки равнозначны, и выделять одну из них незачем. */
.eco-grid > .field-row, .eco-card {
  display: grid;
  grid-template-rows: auto auto 1fr;
  gap: var(--sp-050);
  height: 100%;
  padding: var(--sp-150) var(--sp-200) var(--sp-200);
  border: var(--bw) solid var(--line);
  border-radius: var(--r-l);
}

/* Переключатель рядом с ними — такая же карточка, только содержимое в
   строку: он тоже настройка, и выпадать из ряда ему незачем. */
.eco-card.row-card {
  display: flex; align-items: center;
  padding: var(--sp-150) var(--sp-200);
}
.eco-grid .input { max-width: 160px; }

.eco-example + .eco-example { margin-top: var(--sp-300); }
.eco-title {
  font-family: var(--font-h);
  font-size: var(--fs); font-weight: var(--fw-bold);
  margin-bottom: var(--sp-100);
}

/* Строка цепочки: название, сумма справа моноширинным, пояснение под ними
   во всю ширину. Три колонки в одну строку разъезжались на длинных
   пояснениях, поэтому пояснение вынесено на свою строку сетки. */
.eco-step {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: 2px var(--sp-150);
  align-items: baseline;
  padding: var(--sp-100) var(--sp-150);
  border-bottom: var(--bw) solid var(--line);
  font-size: var(--fs);
}
.eco-step:first-of-type { border-top: var(--bw) solid var(--line); }
.eco-step-t { color: var(--ink2); }
.eco-step-sum {
  font-family: var(--num); font-variant-numeric: tabular-nums;
  color: var(--ink); white-space: nowrap;
}
.eco-step-note {
  grid-column: 1 / -1;
  color: var(--ink3); font-size: var(--fs-s); line-height: var(--lh-s);
}
.eco-step-note:empty { display: none; }
.eco-step.total {
  background: var(--acc-weak);
  border-bottom: none;
  border-radius: 0 0 var(--r-s) var(--r-s);
}
.eco-step.total .eco-step-t { color: var(--acc-ink); font-weight: var(--fw-semi); }
.eco-step.total .eco-step-sum { color: var(--acc-ink); font-weight: var(--fw-semi); }
.eco-converted { color: var(--ink3); font-size: var(--fs-s); }

/* ========================================================= КОДЫ ДОСТУПА
   Экран на два кода и ОДИН отсчёт. Отсчёт один потому, что коды меняются
   в одну и ту же секунду: два одинаковых счётчика рядом показывали одно
   число дважды и заставляли сверять, не разошлись ли они.

   Код набран моноширинным в крупном кегле и разряжен: его диктуют
   голосом, и «0» с «O» перепутать нельзя. Вся плашка с кодом — кнопка
   копирования: во время разговора в неё надо попадать не глядя. */

/* --- общий отсчёт: кольцо и подпись --- */
.code-clock {
  display: flex; align-items: center; gap: var(--sp-300);
  margin-bottom: var(--gap);
}
.code-ring { position: relative; flex: none; width: 108px; height: 108px; }
.code-ring svg { display: block; transform: rotate(-90deg); }
.code-ring-track { fill: none; stroke: var(--a-200); stroke-width: 9; }
.code-ring-live {
  fill: none; stroke: var(--acc); stroke-width: 9; stroke-linecap: round;
  transition: stroke-dashoffset .95s linear, stroke .3s;
}
.code-clock.soon .code-ring-live { stroke: var(--warn); }
.code-clock.stale .code-ring-live { stroke: var(--a-300); transition: none; }
.code-ring-num {
  position: absolute; inset: 0;
  display: grid; place-items: center;
  font-family: var(--num); font-variant-numeric: tabular-nums;
  font-size: 34px; font-weight: var(--fw-semi); color: var(--ink);
}
.code-clock.soon .code-ring-num { color: var(--warn-ink); }
.code-clock-text { min-width: 0; }
.code-clock-t {
  font-family: var(--font-h); font-weight: var(--fw-bold);
  font-size: 22px; line-height: 1.2;
}
.code-clock-s { color: var(--ink2); font-size: var(--fs); margin-top: var(--sp-050); }

/* --- сами коды --- */
.code-cards {
  display: grid; gap: var(--gap);
  grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
}
.code-card { padding: var(--sp-200); display: flex; flex-direction: column; gap: var(--sp-150); }
.code-head { display: flex; align-items: center; gap: var(--sp-150); }
.code-title {
  flex: 1 1 auto;
  font-family: var(--font-h); font-weight: var(--fw-bold);
  font-size: var(--fs-l); line-height: var(--lh-l);
}

.code-copy {
  display: flex; align-items: center; justify-content: center; gap: var(--sp-150);
  width: 100%; padding: var(--sp-250) var(--sp-150);
  border: none; border-radius: var(--r);
  background: var(--acc-weak);
  color: var(--acc-ink);
  transition: background .15s;
}
.code-copy:hover:not(:disabled) { background: var(--acc-weak-2); }
.code-copy:disabled { background: var(--a-200); color: var(--ink-dis); cursor: not-allowed; }
.code-copy:focus-visible { outline: var(--bw-foc) solid var(--focus); outline-offset: 2px; }
.code-value {
  font-family: var(--num); font-variant-numeric: tabular-nums;
  font-size: 34px; line-height: 1;
  font-weight: var(--fw-semi); letter-spacing: .16em;
}
.code-copy-x { display: inline-flex; opacity: .55; }
.code-copy:hover:not(:disabled) .code-copy-x { opacity: 1; }
.code-cards.stale .code-copy { opacity: .55; }

.code-hint { font-size: var(--fs-s); line-height: 1.55; color: var(--ink2); }
.code-hint p { margin: 0 0 var(--sp-075); }
.code-hint .code-tail { color: var(--ink3); margin: 0; }

.code-note {
  margin-top: var(--gap); padding: var(--sp-150) var(--sp-200);
  border-radius: var(--r-l);
  background: var(--a-100);
  font-size: var(--fs-s); line-height: 1.55; color: var(--ink3);
}
.code-note p { margin: 0 0 var(--sp-050); }

/* =============================================================== ПРОСЧЁТЫ */
/* Полоса отбора: у неё есть собственный отступ снизу — раньше сегмент
   прилипал вплотную к таблице. */
.quotes-bar { display: flex; flex-wrap: wrap; gap: var(--sp-100);
              margin-bottom: var(--sp-200); }

/* Итоги. Таблица без шапки: подписи слева, числа в одной колонке. */
table.list.totals td { border: none; height: 34px; }
table.list.totals tbody tr { border-bottom: var(--bw) solid var(--line); }
table.list.totals tbody tr:last-child { border-bottom: none; }
table.list.totals tbody tr:hover { background: none; }
table.list.totals td:first-child { color: var(--ink2); }
table.list tbody tr.total {
  background: linear-gradient(180deg, var(--acc-weak) 0%, var(--acc-weak-2) 100%);
}
table.list tbody tr.total td { font-weight: var(--fw-semi); color: var(--acc-ink); }
table.list tbody tr.total:hover { background: linear-gradient(180deg, var(--acc-weak) 0%, var(--acc-weak-2) 100%); }

.quotes-base { display: flex; flex-direction: column; gap: 2px; }
.quotes-base-l { font-size: var(--fs-s); color: var(--ink2); }
.quotes-base-v {
  font-family: var(--font-h); font-size: 24px; font-weight: var(--fw-bold);
  font-variant-numeric: tabular-nums;
}
.quotes-base-n { font-size: var(--fs-s); color: var(--ink3); }

/* Лист предложения внутри панели: поля и прокрутка вокруг него. */
.kp-sheet-host {
  display: flex; justify-content: center;
  padding: var(--sp-200) 0;
  background: var(--a-100);
  border-radius: var(--r-l);
}

/* ====================================================== ЗНАК ПЛАТФОРМЫ
   Знак DEOS и знак компании стоят рядом через тонкую черту: DEOS
   развернул компании инфраструктуру, компания в ней работает. Это
   соседство, а не подмена одного другим. */
/* Знак занимает свою половину целиком и вписывается в неё по своим
   пропорциям. Ни высоты, ни ширины в пикселях здесь нет: половина знает
   свой размер, знак берёт его весь. */
.brand-logo {
  display: block;
  width: 100%; height: 100%;
  max-width: 100%; max-height: 100%;
  min-width: 0; min-height: 0;
  object-fit: contain; object-position: center;
}

/* Собственный знак стоит разметкой, а не картинкой, и потому красится
   цветом текста: в тёмной теме он перекрашивается сам и никакой плашки
   под собой не требует. Вписывается так же: preserveAspectRatio по
   умолчанию — meet, то есть тот же contain. */
.brand-own {
  display: flex;
  width: 100%; height: 100%; min-width: 0;
  color: var(--ink);
}
.brand-own svg { width: 100%; height: 100%; }

/* Плашки под знаком компании нет. Она была придумана ради тёмных знаков
   на тёмной шапке, но получилось хуже задачи: белый прямоугольник вокруг
   чужого знака выглядит как ошибка вёрстки, и виден он у всех, включая
   тех, чей знак и без него читается. Знак ставится как есть; скругление
   углов остаётся — оно ничего не подрисовывает. */
[data-theme="dark"] .brand-half.file { border-radius: var(--r-s); }

/* ДВА ЗНАКА У КОМПАНИИ: обычный и для тёмной темы. Показывает нужный
   оформление, а не код: тему меняют из двух мест, и переключение
   картинки руками рано или поздно разошлось бы с одним из них.

   Второго знака может не быть вовсе — тогда обычный виден всегда. */
.brand-logo.for-dark { display: none; }
:root[data-theme="dark"] .brand-half.file .brand-logo.for-light:not(:only-child) {
  display: none;
}
:root[data-theme="dark"] .brand-logo.for-dark { display: block; }
[data-theme="dark"] .brand-line { background: var(--line2); }

[data-theme="dark"] .brand-preview-img {
  border-radius: var(--r-s);
  padding: 8px 12px;
}

/* Место под логотип — размером с логотип, а не во всю ширину экрана.
   Туда кладут квадрат или неширокий прямоугольник; растянутая на весь
   экран подложка вокруг маленькой картинки только сбивает с толку —
   человек думает, что от него ждут изображение такой ширины. */
.brand-preview {
  display: flex; align-items: center; justify-content: center;
  width: 260px; height: 100px; flex: none;
  padding: var(--sp-150);
  background: var(--a-100);
  border-radius: var(--r-l);
}
.brand-preview-img { max-height: 100%; max-width: 100%; object-fit: contain; }
/* Плитки знаков: одна или две рядом. Две — когда для тёмной темы задан
   свой знак. Рядом, а не одна под другой: их сравнивают глазом. */
.logo-slots { display: flex; flex-wrap: wrap; gap: var(--sp-300); }
.logo-slot { display: flex; flex-direction: column; gap: var(--sp-100); }

/* Плитка и кнопки к ней — в один ряд: кнопки относятся к плитке, а не
   к карточке целиком, и стоять должны рядом с ней. */
.brand-place {
  display: flex; align-items: center; gap: var(--sp-200); flex-wrap: wrap;
}
.brand-preview-empty { color: var(--ink3); font-size: var(--fs-s); }
/* Встроенный знак в предпросмотре — он одноцветный и красится темой. */
.brand-preview-own { height: 40px; color: var(--ink); }
.brand-preview-own svg { display: block; height: 100%; width: auto; }

/* Пока что-то тащат, курсор «схвачен» держится над ВСЕЙ страницей.
   Иначе он менялся бы обратно, стоит указателю сойти с самого блока, —
   а сходит он сразу: блок едет следом за указателем и из-под него уходит.
   `!important` здесь по делу: перебить надо все курсоры, какие встретятся
   по дороге, включая текстовый над полями ввода. */
body.dragging-now,
body.dragging-now * { cursor: var(--cur-hold) !important; }

/* Поле с глазом: пароль стоит в поле скрытым, значок справа открывает
   его. Значок лежит ВНУТРИ поля, а не рядом кнопкой: так это устроено
   везде, и человек ищет его именно там. */
.input-with-eye { position: relative; display: block; }
.input-with-eye .input { width: 100%; padding-right: calc(var(--h-btn) + var(--sp-050)); }
.input-eye {
  position: absolute;
  top: 50%; right: var(--sp-050);
  transform: translateY(-50%);
  display: inline-flex; align-items: center; justify-content: center;
  width: var(--h-btn-sm); height: var(--h-btn-sm);
  color: var(--ink3);
  background: none; border: none;
  border-radius: var(--r-s);
}
.input-eye:hover { background: var(--a-200); color: var(--ink); }
.input-eye:focus-visible { outline: var(--bw-foc) solid var(--focus); outline-offset: 1px; }
.input-eye svg { display: block; }

/* ============================================== ОКНО ЛИЦЕНЗИИ
   Слева условия лицензии, справа то, что компании продано. Два столбца,
   а не один длинный: список продаваемого длиннее условий, и в одну
   колонку окно становится лентой, по которой мастер ездит при каждой
   продаже. */
.lic-work {
  display: grid;
  gap: var(--sp-200);
  grid-template-columns: minmax(300px, 400px) minmax(320px, 1fr);
  align-items: start;
}
.lic-work > .col { display: flex; flex-direction: column; gap: var(--sp-200); }
@media (max-width: 900px) { .lic-work { grid-template-columns: 1fr; } }

/* ================================================= ДАННЫЕ КОМПАНИИ */
.profile-grid {
  display: grid; gap: var(--sp-150);
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
}
.profile-grid .field-row.wide { grid-column: 1 / -1; }

/* ================================================ ВИЗИТКА ДИЗАЙНЕРА
   Поля слева, живой лист справа: правку видно там же, где её делают.
   Предпросмотр рисует код самого предложения, поэтому своих правил для
   карточки здесь нет — только место, куда лист становится. */
.vc-work {
  display: grid;
  gap: var(--sp-300);
  grid-template-columns: minmax(280px, 1fr) minmax(280px, 1fr);
  align-items: start;
  margin-top: var(--sp-200);
}
@media (max-width: 980px) { .vc-work { grid-template-columns: 1fr; } }

/* Поля визитки короткие: телефон и почта — не абзац текста. */
.vc-grid { grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); }

/* Лист в предпросмотре уменьшен: он тут не для чтения, а для того, чтобы
   увидеть, как визитка сядет. Поля листа при этом настоящие. */
.vc-sheet {
  padding: var(--sp-150);
  background: var(--a-100);
  border-radius: var(--r-l);
  overflow: clip;
}
.vc-sheet .sheet { padding: 8mm; font-size: 13px; }
.vc-seg { flex-wrap: wrap; }
.vc-seg button { font-size: 12px; padding: 0 var(--sp-100); }
.vc-off { color: var(--ink3); font-size: var(--fs-s); }

/* ═══════════════════════════════════════════ УВЕДОМЛЕНИЯ ПЛАТФОРМЫ
   Одни и те же правила на обе стороны: у мастера — список отправленного,
   у компании — список пришедшего. Вид один намеренно: это одна и та же
   вещь, и выглядеть она должна одинаково с обеих сторон.

   Сообщение свёрнуто по умолчанию. Развёрнутыми все сразу они превращают
   окно в ленту, по которой надо прокручивать, чтобы найти вчерашнее. */
.notice-row { padding: 0; overflow: hidden; }
.notice-head {
  display: flex; align-items: center; gap: var(--sp-150);
  width: 100%;
  padding: var(--sp-150) var(--gap);
  background: none; border: none; cursor: pointer;
  text-align: left;
}
.notice-head:hover { background: var(--a-100); }
.notice-x { display: inline-flex; color: var(--ink3); transition: transform .18s; }
.notice-row.open .notice-x { transform: rotate(180deg); }
.notice-body {
  display: none;
  padding: 0 var(--gap) var(--gap);
}
.notice-row.open .notice-body { display: flex; flex-direction: column; gap: var(--sp-150); }

/* Текст сообщения — как его написали: переносы строк сохраняются.
   Абзацы в объявлении несут смысл, и склеивать их в кирпич нельзя. */
.notice-text { white-space: pre-wrap; color: var(--ink); line-height: 1.55; }

.notice-shot img {
  display: block;
  max-width: 100%; max-height: 420px;
  border: 1px solid var(--line);
  border-radius: var(--r);
}

/* Непрочитанное. Точка, а не заливка всей карточки: заливка спорила бы
   с меткой вида, которая уже несёт цвет. */
.notice-dot {
  width: 8px; height: 8px; flex: 0 0 auto;
  border-radius: 50%;
  background: var(--acc);
}
.notice-row.fresh .notice-head b { font-weight: var(--fw-semi); }

/* Вид сообщения. Цвет — из тех же наборов, что у остальных меток. */
.chip.kind-update { color: var(--acc-ink);
  background: linear-gradient(180deg, var(--acc-weak) 0%, var(--acc-weak-2) 100%); }
.chip.kind-work   { color: var(--warn-ink);
  background: linear-gradient(180deg, var(--warn-weak) 0%, var(--warn-weak-2) 100%); }
.chip.kind-offer  { color: var(--good-ink);
  background: linear-gradient(180deg, var(--good-weak) 0%, var(--good-weak-2) 100%); }

/* Выбор компаний при отправке. Список в своей рамке и со своей прокруткой:
   компаний бывает много, и вытягивать ими карточку на три экрана нельзя.

   СТОЛБИКОМ, А НЕ В НЕСКОЛЬКО КОЛОНОК. Раскладка по колонкам ставила
   компании рядом друг с другом, и список читался поперёк: глаз шёл слева
   направо и попадал не на следующую компанию, а на соседнюю. Выбор из
   списка ищут сверху вниз — и порядок в нём должен быть один. */
.notice-companies {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--sp-050);
  max-height: 320px; overflow: auto;
  padding: var(--sp-100);
  border: 1px solid var(--line);
  border-radius: var(--r);
}
/* Каждая компания — отдельная карточка: строку с именем, кодом и меткой
   лицензии иначе не отделить от соседней. */
.notice-company {
  display: flex; align-items: center; gap: var(--sp-100);
  min-height: var(--h-row);
  padding: var(--sp-050) var(--sp-100);
  background: var(--a-100);
  border: var(--bw) solid var(--line);
  border-radius: var(--r);
}
.notice-company:hover { background: var(--a-200); }
.notice-company .checkbox { flex: 1 1 auto; min-width: 0; }

/* Счётчик на кнопке в шапке. Центрирование по обеим осям — как у всех
   таких кружков в программе; цифра стоит ровно, а не «где легла». */
.topbar-badge {
  flex: none;
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 18px; height: 18px; padding: 1px var(--sp-050) 0;
  border-radius: var(--r-full);
  /* Заливка — --acc-solid, тушь белая. Пара обязательная: на самом
     --acc со светлым акцентом белая цифра пропадает. */
  background: var(--acc-solid); color: #FFFFFF;
  font-family: var(--num); font-variant-numeric: tabular-nums;
  font-size: 11px; font-weight: var(--fw-semi); line-height: 1;
}

/* ═══════════════════════════════════════════════ ВЕБ-РЕДАКТОР (АЛЬФА)
   Отметка «альфа» стоит прямо в разметке экрана, а не в примечании к
   релизу: тот, кто им пользуется, обязан видеть, что вещь ранняя. */
.alpha { gap: var(--sp-150); }
.alpha-warn {
  display: flex; align-items: flex-start; gap: var(--sp-150);
  padding: var(--sp-150) var(--gap);
  border: 1px dashed var(--warn);
  border-radius: var(--r);
  background: var(--warn-weak);
  color: var(--warn-ink);
}
.alpha-work {
  display: grid;
  grid-template-columns: minmax(0, 1.4fr) minmax(280px, 1fr);
  gap: var(--gap);
  align-items: start;
}
.alpha-cat { gap: var(--sp-075); }
.alpha-stage {
  padding: var(--sp-150);
  border: 1px solid var(--line);
  border-radius: var(--r);
  background: var(--a-100);
}
.alpha-svg { width: 100%; height: auto; max-height: 460px; display: block; }
/* Стена — подложка. Модуль читается заливкой, выбранный — обводкой
   акцентом: цветом заливки выбранный спорил бы с меткой «не собирается». */
.alpha-wall { fill: var(--card); stroke: var(--line); stroke-width: 6; }
.alpha-mod { fill: var(--a-200); stroke: var(--ink3); stroke-width: 6; cursor: pointer; }
.alpha-mod:hover { fill: var(--a-300, var(--a-200)); }
.alpha-mod.on { stroke: var(--acc); stroke-width: 14; }
.alpha-mod.bad { stroke: var(--danger); stroke-width: 14; }
.alpha-face { fill: var(--card); stroke: var(--ink3); stroke-width: 4; pointer-events: none; }
.alpha-slab { fill: var(--acc-weak); stroke: var(--acc); stroke-width: 4; }
.alpha-over { margin-top: var(--sp-100); color: var(--warn-ink); font-size: var(--fs-s); }
.alpha-props { display: flex; flex-direction: column; gap: var(--sp-150); }

@media (max-width: 1100px) {
  .alpha-work { grid-template-columns: 1fr; }
}
/* В узкой колонке свойств поля становятся в две колонки: десять полей
   в одну вытягивают панель длиннее самой сцены. */
.alpha-props .calc-grid { grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); }

/* ═══════════════════════════════════════════════════ ЭКРАН ПРОСЧЁТА
   Раскладка взята из работающей версии: выбор системы карточками,
   данные заказа — выезжающей панелью, состав — тремя колонками, внизу
   полоса действий. Причина каждой из трёх вещей записана рядом.

   ВЫБОР СИСТЕМЫ. Системы независимы: у каждой свой заказ и свой состав.
   Карточка говорит, чем система отличается, и помечает начатый просчёт —
   иначе о начатой работе узнают, только зайдя внутрь. */
.syscards {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
  gap: var(--gap);
  max-width: 1040px;
}
.syscard {
  display: flex; flex-direction: column; gap: var(--sp-100);
  padding: var(--sp-250) var(--sp-200);
  text-align: left;
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: var(--r);
  cursor: pointer;
  transition: border-color .15s, background .15s;
}
.syscard:hover { border-color: var(--acc); background: var(--acc-weak); }
.syscard-top { display: flex; align-items: center; gap: var(--sp-100); }
.syscard b { font-size: 15px; color: var(--ink); }
.syscard-note { font-size: var(--fs-s); color: var(--ink2); line-height: 1.5; }
.syscard-on {
  align-self: flex-start;
  padding: 2px var(--sp-100);
  border-radius: var(--r-s);
  background: var(--acc-weak); color: var(--acc-ink);
  font-size: 11.5px; font-weight: var(--fw-semi);
}

.calc-head { align-items: flex-start; gap: var(--sp-150); }

/* ДАННЫЕ ЗАКАЗА — полоса-кнопка, открывающая панель. Пунктир говорит,
   что это не карточка с содержимым, а вход в него. */
.odbtn {
  display: flex; align-items: center; gap: var(--sp-150);
  width: 100%;
  padding: var(--sp-150) var(--sp-200);
  border: 1px dashed var(--line2, var(--line));
  border-radius: var(--r);
  background: none;
  color: var(--ink2);
  text-align: left;
  cursor: pointer;
  transition: border-color .15s, background .15s, color .15s;
}
.odbtn:hover { border-color: var(--acc); background: var(--acc-weak); color: var(--acc-ink); }
.odbtn-t { font-weight: var(--fw-semi); color: var(--ink); }
.odbtn-s { flex: 1 1 auto; font-size: var(--fs-s); color: var(--ink3); }
.odbtn-a { font-size: var(--fs-s); font-weight: var(--fw-semi); color: var(--acc-ink); }
/* Не хватает как раз того, что лежит внутри, — полоса подсвечивается. */
.odbtn.wanted { border-color: var(--warn); color: var(--warn-ink); }
.odbtn.wanted .odbtn-a { color: var(--warn-ink); }

/* ИМПОРТ ИЗ ПРО100 — сворачиваемая карточка: после разбора она не нужна,
   а место занимает. Складывается сама, как только разбор прошёл. */
.import-card { padding: 0; overflow: hidden; }
.import-head {
  display: flex; align-items: center; gap: var(--sp-150);
  width: 100%; padding: var(--sp-150) var(--gap);
  background: none; border: none; cursor: pointer; text-align: left;
}
.import-head:hover { background: var(--a-100); }
.import-t { font-weight: var(--fw-semi); }
.import-state { flex: 1 1 auto; font-size: var(--fs-s); color: var(--ink3); }
.import-chev { display: inline-flex; color: var(--ink3); transition: transform .18s; }
.import-card.collapsed .import-chev { transform: rotate(-90deg); }
.import-body {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 180px;
  gap: var(--gap);
  padding: 0 var(--gap) var(--gap);
}
.import-card.collapsed .import-body { display: none; }
.import-area { font-family: var(--num); font-size: 12.5px; resize: vertical; }
.import-side { gap: var(--sp-075); }
.import-body .calc-errors-box { grid-column: 1 / -1; }

/* СОСТАВ — три колонки, как в работающей версии: материалы, фурнитура,
   дополнительные элементы рядом. Списком во всю ширину два десятка
   позиций занимают три экрана прокрутки при пустом месте справа. */
.cols3 {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--gap);
  align-items: start;
}
@media (max-width: 1280px) { .cols3 { grid-template-columns: 1fr 1fr; } }
@media (max-width: 900px)  { .cols3 { grid-template-columns: 1fr; } }

.work-head { display: flex; align-items: baseline; gap: var(--sp-100); }
.work-head h2 { margin: 0; }
.work-count { margin-left: auto; font-size: var(--fs-s); color: var(--ink3); }
.work-list { gap: var(--sp-075); }
.work-empty { padding: var(--sp-150) 0; color: var(--ink3); font-size: var(--fs-s); }

/* Позиция — плотный блок в две строки: что и сколько сверху, выбор вида
   и пересчёт в закупку снизу. Строкой таблицы это не сделать: выбор вида
   шире всех остальных колонок вместе взятых. */
.work-item {
  padding: var(--sp-100) var(--sp-150);
  border: 1px solid var(--line);
  border-radius: var(--r-s);
  background: var(--a-100);
}
.work-top, .work-fhead {
  display: flex; align-items: center; gap: var(--sp-075);
  margin-bottom: var(--sp-075);
}
.work-tech { font-weight: var(--fw-med); overflow: hidden; text-overflow: ellipsis;
  white-space: nowrap; }
.work-num { margin-left: auto; font-family: var(--num);
  font-variant-numeric: tabular-nums; font-size: 12px; color: var(--ink2); }
.work-cat { flex: 1 1 auto; min-width: 0; }
.work-cat .combo { width: 100%; }
.combo { max-width: 100%; }
.combo .combo-button { width: 100%; }
.work-qty { display: flex; align-items: center; gap: var(--sp-050); flex: none; }
.work-qty .input { width: 84px; text-align: right; }
.work-unit { font-size: 11.5px; color: var(--ink3); white-space: nowrap; }
.work-drop { flex: none; }

/* Выбор вида и «в закупке». Плашка справа — главное, что даёт экран:
   14,2 м² это абстракция, а «4 Листа» — то, что закупят. */
.work-pick { display: grid; grid-template-columns: minmax(0, 1fr) 92px;
  gap: var(--sp-075); align-items: center; }
.work-pick .combo { width: 100%; }
.work-buy {
  padding: 5px var(--sp-100);
  border-radius: var(--r-s);
  background: var(--acc-weak); color: var(--acc-ink);
  font-family: var(--num); font-size: 11.5px; font-weight: var(--fw-semi);
  text-align: center; white-space: nowrap;
}
.work-buy.wait { background: var(--a-200); color: var(--ink3); }

/* Счётчик распределения фурнитуры: пока не сошлось — предупреждение,
   сошлось — спокойный цвет. Расчёт с несведённой фурнитурой не пойдёт. */
.work-counter {
  flex: none;
  padding: 3px var(--sp-100);
  border-radius: var(--r-s);
  background: var(--warn-weak); color: var(--warn-ink);
  font-family: var(--num); font-size: 11.5px; font-weight: var(--fw-semi);
}
.work-counter.ok { background: var(--good-weak); color: var(--good-ink); }

.work-rows { gap: var(--sp-050); }
/* Строка вида фурнитуры переносится: выбор вида шире, чем количество,
   кнопка «Все» и крестик вместе взятые, и в одну строку в колонке они
   не помещаются — список схлопывается в ноль. Так же было в прежней
   версии: список во всю ширину, под ним количество и кнопки. */
.work-frow { display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-050); }
.work-frow .combo { flex: 1 1 100%; min-width: 0; }
.work-frow .input { flex: 0 0 74px; width: 74px; text-align: right; }
.work-frow .work-drop { margin-left: auto; }
.work-all {
  flex: none;
  padding: 5px var(--sp-100);
  border: 1px solid var(--acc);
  border-radius: var(--r-s);
  background: var(--acc-weak); color: var(--acc-ink);
  font-size: 11.5px; font-weight: var(--fw-semi);
  cursor: pointer;
}
.work-all:hover { background: var(--acc-solid); color: #fff; border-color: transparent; }

.work-add {
  align-self: flex-start;
  padding: var(--sp-050) 0;
  background: none; border: none;
  color: var(--acc-ink); font-size: var(--fs-s); font-weight: var(--fw-semi);
  cursor: pointer;
}
.work-add:hover { text-decoration: underline; }

/* ПОЛОСА ДЕЙСТВИЙ. Стоит внизу экрана в общем потоке и не всплывает
   над содержимым: пока она была липкой, она закрывала раскрытые списки
   в нижних карточках. Итога до расчёта на ней нет — правило заказчика. */
.actrow {
  position: static; z-index: 0;
  display: flex; align-items: center; gap: var(--sp-150);
  margin-top: var(--sp-250);
  padding: var(--sp-150) var(--gap);
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: var(--r);
  box-shadow: var(--sh-raised);
}
.actrow-err { font-size: var(--fs-s); color: var(--warn-ink); max-width: 46%; }
.actrow-total {
  font-family: var(--num); font-variant-numeric: tabular-nums;
  font-size: 17px; font-weight: var(--fw-semi);
}
.actrow-run { min-width: 150px; height: 36px; }
.actrow-run.done { opacity: .75; }
.kp-btns { display: none; gap: var(--sp-100); }
.kp-btns.on { display: flex; }

/* Панель заказа: разделы отбиты подписью, поля идут одно под другим —
   панель узкая, и две колонки в ней не помещаются. */
.order-panel { gap: var(--sp-150); }
.order-sec {
  margin-top: var(--sp-100);
  font-size: 11px; font-weight: var(--fw-semi); letter-spacing: .06em;
  text-transform: uppercase; color: var(--acc-ink);
}
.order-sec:first-child { margin-top: 0; }
.order-shot img {
  display: block; max-width: 100%; max-height: 160px;
  border: 1px solid var(--line); border-radius: var(--r-s);
}
.field-req { color: var(--danger); margin-left: 3px; }
.calc-unit button { min-width: 34px; }
.calc-errors-box { gap: var(--sp-050); }
.calc-error-line {
  padding: var(--sp-075) var(--sp-100);
  border-left: 3px solid var(--warn);
  background: var(--warn-weak);
  color: var(--warn-ink);
  font-size: var(--fs-s);
}
ul.calc-errors { margin: 0; padding-left: 18px; display: flex;
  flex-direction: column; gap: var(--sp-050); }

/* ═══════════════════════════ ПРОСЧЁТ: перенесённая разметка
   Классы — из работающей версии, значения — из дизайн-кода. Так экран
   остаётся тем же, каким его знают, но перестаёт жить по своим цветам
   и своим скруглениям. */

/* Выпадающий список работающей версии. Свой, не из библиотеки: у него
   поиск внутри списка и повадки, к которым привыкли. */
.combo-legacy { position: relative; }
.combo-legacy > button {
  display: flex; align-items: center; justify-content: space-between;
  width: 100%; min-height: var(--h-ctl, 32px);
  padding: var(--sp-075) var(--sp-150);
  background: var(--field, var(--a-100)); color: var(--ink);
  border: 1px solid var(--line); border-radius: var(--r-s);
  font: inherit; text-align: left; cursor: pointer;
}
.combo-legacy > button .lbl {
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.combo-legacy > button .caret {
  display: inline-flex; flex: none; margin-left: var(--sp-075); color: var(--ink3);
}
.combo-legacy > button .caret svg { width: 16px; height: 16px; }
.combo-legacy.open > button .caret { transform: rotate(180deg); }
.combo-legacy > button:hover { border-color: var(--acc); }
.combo-legacy > button.empty { color: var(--ink3); }
.combo-legacy > button.invalid { border-color: var(--danger); }
.combo-legacy .pop {
  display: none; position: absolute; left: 0; right: 0; top: calc(100% + 4px);
  z-index: 30; padding: var(--sp-100);
  background: var(--card); border: 1px solid var(--line);
  border-radius: var(--r); box-shadow: var(--sh-raised);
}
.combo-legacy.open .pop { display: block; }
.combo-legacy .pop input {
  width: 100%; margin-bottom: var(--sp-075);
  padding: var(--sp-075) var(--sp-100);
  background: var(--field, var(--a-100)); color: var(--ink);
  border: 1px solid var(--line); border-radius: var(--r-s); font: inherit;
}
.combo-legacy .list { max-height: 240px; overflow: auto; }
.combo-legacy .opt {
  padding: var(--sp-100) var(--sp-150);
  border-radius: var(--r-s); cursor: pointer;
}
.combo-legacy .opt:hover, .combo-legacy .opt.hl { background: var(--a-200); }
.combo-legacy .opt.sel { color: var(--acc-ink); font-weight: var(--fw-semi); }
.combo-legacy .none { padding: var(--sp-100); color: var(--ink3); font-size: var(--fs-s); }

/* Карточка материала: что и сколько сверху, выбор вида и пересчёт снизу. */
.mat {
  padding: var(--sp-150) var(--sp-150) var(--sp-150);
  border: 1px solid var(--line); border-radius: var(--r-s);
  background: var(--a-100);
}
.mat .top {
  display: flex; justify-content: space-between; align-items: baseline;
  gap: var(--sp-150); margin-bottom: var(--sp-100);
}
.mat .tech { font-weight: var(--fw-med); }
.mat .qty {
  font-family: var(--num); font-variant-numeric: tabular-nums;
  font-size: 12px; color: var(--ink2);
}
.mat .pick {
  display: grid; grid-template-columns: minmax(0, 1fr) 92px;
  gap: var(--sp-075); align-items: center;
}
/* «В закупке» — главное, что даёт экран: 14,2 м² это абстракция,
   а «4 листа» — то, что закупят. */
.conv {
  padding: 5px var(--sp-100); border-radius: var(--r-s);
  background: var(--acc-weak); color: var(--acc-ink);
  font-family: var(--num); font-size: 11.5px; font-weight: var(--fw-semi);
  text-align: center; white-space: nowrap;
}
.conv.wait { background: var(--a-200); color: var(--ink3); }

/* Категория фурнитуры: сколько всего и сколько уже распределено. */
.fcat {
  padding: var(--sp-150);
  border: 1px solid var(--line); border-radius: var(--r-s);
  background: var(--a-100);
}
.fcat .head {
  display: flex; align-items: baseline; gap: var(--sp-100);
  margin-bottom: var(--sp-100);
}
.fcat .head .n { font-weight: var(--fw-med); }
.fcat .head .okv {
  font-family: var(--num); font-size: 12px; color: var(--ink2);
}
.counter {
  margin-left: auto; padding: 3px var(--sp-100); border-radius: var(--r-s);
  background: var(--warn-weak); color: var(--warn-ink);
  font-family: var(--num); font-size: 11.5px; font-weight: var(--fw-semi);
}
.counter.ok { background: var(--good-weak); color: var(--good-ink); }

/* Строки: вид на всю ширину, под ним количество и кнопки. Выбор вида
   шире всего остального вместе взятого — в одну строку он схлопывается. */
.frow, .frow2, .mrow, .xrow {
  display: flex; flex-wrap: wrap; align-items: center;
  gap: var(--sp-050); margin-bottom: var(--sp-075);
}
.frow .cb, .xrow .cb { flex: 1 1 100%; min-width: 0; }
.mrow .cbCat, .frow2 .cbCat { flex: 1 1 auto; min-width: 0; }
.mrow .cbItem, .frow2 .cbItem { flex: 1 1 100%; min-width: 0; }
/* Поле количества. Внутри поля стоит кнопка (весь остаток, единицы), и
   ширину задаёт обёртка .field-affix, а не само поле. */
.xrow input { flex: 0 0 84px; width: 84px; text-align: right; }
.mrow .conv { margin-left: auto; }
.frow .xdel, .frow2 .xdel, .mrow .xdel, .xrow .xdel { margin-left: auto; }

/* Кнопка внутри поля количества оформлена в css/calc-order.css — здесь
   её правил нет намеренно: две копии одного правила спорили друг с
   другом, и побеждала та, что стояла в этом файле. */

.qall, .tgl {
  flex: none; padding: 5px var(--sp-100);
  border: 1px solid var(--acc); border-radius: var(--r-s);
  background: var(--acc-weak); color: var(--acc-ink);
  font-size: 11.5px; font-weight: var(--fw-semi); cursor: pointer;
}
.qall:hover, .tgl:hover { background: var(--acc-solid); color: #fff; border-color: transparent; }
/* Убрать строку. Площадь нажатия во всю высоту поля рядом, рамки в
   покое нет: это действие второго ряда, и рамка тянула на себя взгляд
   наравне с выбором вида. */
.xdel {
  flex: none; width: 32px; height: 32px;
  display: inline-flex; align-items: center; justify-content: center;
  border: 1px solid transparent; border-radius: var(--r-s);
  background: none; color: var(--ink3); cursor: pointer; line-height: 1;
  transition: color .12s, background .12s, border-color .12s;
}
.xdel:hover { border-color: var(--danger); color: var(--danger); background: var(--danger-weak); }
.xdel:focus-visible { outline: 2px solid var(--acc); outline-offset: 1px; }
.xdel svg { width: 14px; height: 14px; }
.import-chev svg { width: 16px; height: 16px; }
.sysdrop-btn i svg { width: 14px; height: 14px; }
.addrow {
  align-self: flex-start; margin-top: var(--sp-050);
  padding: var(--sp-075) var(--sp-150);
  background: none; border: 1px dashed var(--line2); border-radius: var(--r-s);
  cursor: pointer;
  color: var(--acc-ink); font-size: var(--fs-s); font-weight: var(--fw-semi);
}
.addrow:hover { border-color: var(--acc); background: var(--acc-weak); }
.fcat .addrow { margin-top: var(--sp-075); }

/* Уголок растягивания у полей многострочного ввода один на всю
   программу: тянуть можно только по высоте — ширину задаёт колонка,
   и растянутое по ширине поле вылезало из карточки. */
textarea, textarea.input { resize: vertical; min-height: 84px; }
textarea.no-resize { resize: none; }

/* Поля данных заказа. */
.f { margin-bottom: var(--sp-150); }
.f label { display: block; margin-bottom: 4px; font-size: 12px; color: var(--ink2); }
.f label .req { color: var(--danger); }
.f .input, .f textarea { width: 100%; }
.f .row .input { flex: 1 1 0; min-width: 0; }
.input.invalid { border-color: var(--danger); }

.actrow .ar-calc.done { opacity: .75; }
/* Карточки состава не обрезают содержимое: выпадающий список выходит за
   их край, а карточка по умолчанию режет всё лишнее ради липкой шапки
   таблиц. Здесь таблиц нет, а список должен раскрываться поверх. */
.cols3 > .card, .cols3 .card { overflow: visible; }
.cols3 > .card { position: relative; z-index: 0; }
.combo-legacy.open { position: relative; z-index: 40; }

/* ═════════════════════════════ ДАННЫЕ ЗАКАЗА: ОСТРОВКИ
   Полей больше двух десятков, и одним столбцом они читались как
   сплошная лента. Разбиты по смыслу: заказ, доставка, деньги, шапка. */
.order-island {
  display: flex; flex-direction: column; gap: var(--sp-150);
  padding: var(--sp-200);
  border: 1px solid var(--line2); border-radius: var(--r);
  background: var(--field2);
}
.order-island + .order-island { margin-top: var(--sp-200); }
.order-island .order-sec {
  display: flex; align-items: center; gap: var(--sp-100);
  margin: 0; padding: 0 0 var(--sp-100); border: 0;
  border-bottom: 1px solid var(--line);
  font-size: 11px; font-weight: var(--fw-bold); letter-spacing: .06em;
  text-transform: uppercase; color: var(--ink3);
}
.order-island .f { margin-bottom: 0; }
.order-island .f label { margin-bottom: 6px; font-size: 12.5px; color: var(--ink2); }
.order-island .field-hint { margin-top: 6px; }
.order-panel { padding: var(--sp-025) 0 var(--sp-150); }
.order-two { display: grid; grid-template-columns: 1fr 1fr; gap: var(--sp-150); }
.order-dash { color: var(--ink3); flex: none; }
.row.nowrap { display: flex; flex-wrap: nowrap; align-items: center; gap: var(--sp-075); }
.row.nowrap .grow { flex: 1 1 auto; min-width: 0; }
@media (max-width: 620px) { .order-two { grid-template-columns: 1fr; } }

/* Значок внутри поля — дата и всё, что открывается выбором. */
.field-icon { position: relative; }
.field-icon > .input { width: 100%; padding-right: 40px; }
.field-icon-btn {
  position: absolute; top: 1px; right: 1px; bottom: 1px; width: 36px;
  display: inline-flex; align-items: center; justify-content: center;
  border: 0; background: none; color: var(--ink3); cursor: pointer;
  border-radius: 0 var(--r-s) var(--r-s) 0;
}
.field-icon-btn:hover { color: var(--acc-ink); background: var(--acc-weak); }
.field-icon-btn svg { width: 17px; height: 17px; }

/* Выбор числа. Неделя начинается с понедельника — так её и читают. */
.daypick {
  position: absolute; top: calc(100% + 6px); right: 0; z-index: 60;
  width: 268px; padding: var(--sp-150);
  background: var(--card); border: 1px solid var(--line);
  border-radius: var(--r); box-shadow: var(--sh-raised);
}
.daypick-head {
  display: flex; align-items: center; justify-content: space-between;
  margin-bottom: var(--sp-100); font-size: var(--fs-s);
}
.daypick-head button {
  width: 26px; height: 26px; border: 1px solid var(--line);
  border-radius: var(--r-s); background: none; color: var(--ink2); cursor: pointer;
}
.daypick-head button:hover { border-color: var(--acc); color: var(--acc-ink); }
.daypick-week, .daypick-grid {
  display: grid; grid-template-columns: repeat(7, 1fr); gap: 2px;
}
.daypick-week span {
  text-align: center; font-size: 10.5px; color: var(--ink3);
  padding-bottom: var(--sp-050);
}
.daypick-grid button {
  height: 30px; border: 1px solid transparent; border-radius: var(--r-s);
  background: none; color: var(--ink); cursor: pointer;
  font-family: var(--num); font-size: 12.5px;
}
.daypick-grid button:hover { background: var(--a-200); }
.daypick-grid button.now { border-color: var(--line2); }
.daypick-grid button.on { background: var(--acc-solid); color: #fff; }
.daypick-foot {
  display: flex; gap: var(--sp-075); margin-top: var(--sp-100);
  padding-top: var(--sp-100); border-top: 1px solid var(--line);
}
.daypick-foot button {
  flex: 1; padding: var(--sp-075); border: 1px solid var(--line);
  border-radius: var(--r-s); background: none; color: var(--ink2);
  font-size: var(--fs-s); cursor: pointer;
}
.daypick-foot button:hover { border-color: var(--acc); color: var(--acc-ink); }

/* Доля или сумма. Оба способа видны сразу, выбранный подсвечен. */
.seg-switch {
  display: inline-flex; flex: none; padding: 2px;
  border: 1px solid var(--line); border-radius: var(--r-s);
  background: var(--a-100);
}
.seg-switch button {
  display: inline-flex; align-items: center; justify-content: center;
  width: 34px; height: 28px; border: 0; border-radius: calc(var(--r-s) - 2px);
  background: none; color: var(--ink3); cursor: pointer;
  transition: background .12s, color .12s;
}
.seg-switch button svg { width: 15px; height: 15px; }
.seg-switch button:hover { color: var(--ink); }
.seg-switch button.on { background: var(--acc-solid); color: #fff; }

/* Картинка предложения: место, куда её кладут, видно до того, как
   положили, — и перетаскиванием, и выбором файла. */
.drop-pic {
  display: block; min-height: 108px;
  border: 1px dashed var(--line2); border-radius: var(--r);
  background: var(--a-100); overflow: hidden;
}
.drop-pic.over { border-color: var(--acc); background: var(--acc-weak); }
.drop-pic.full { border-style: solid; padding: 0; }
.drop-pic img { display: block; width: 100%; max-height: 200px; object-fit: contain; }
.drop-pic-pick {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 3px; width: 100%; min-height: 108px; padding: var(--sp-150);
  border: 0; background: none; cursor: pointer; color: var(--ink2);
}
.drop-pic-pick:hover { color: var(--acc-ink); }
.drop-pic-ic { display: inline-flex; color: var(--ink3); }
.drop-pic-ic svg { width: 22px; height: 22px; }
.drop-pic-t { font-size: var(--fs-s); font-weight: var(--fw-semi); }
.drop-pic-n { font-size: 11px; color: var(--ink3); }

/* Код подтверждения. Набирается как пин-код: по центру, крупно,
   с разрядкой — так видно, сколько цифр уже введено. */
.input.pin {
  width: 100%; text-align: center;
  font-family: var(--num); font-size: 26px; font-weight: var(--fw-semi);
  letter-spacing: .34em; text-indent: .34em;
  padding: var(--sp-150) var(--sp-100);
}
