/* ============================================================
   Общие стили кабинета. Подключается во все экраны.
   ============================================================ */

/* Системная палитра Apple.
   Светлая — systemGroupedBackground + белые карточки, разделители через альфу
   (они так корректно ложатся на любой фон). Тёмная — по значениям Dark Mode. */
/* Системные цвета Apple разделены на два набора — это принципиально:
   --brand / --ok / --warn / --danger / --fact — для ЗАЛИВОК, индикаторов, крупных элементов.
   --*-text — accessible-варианты для МЕЛКОГО ТЕКСТА: чистый systemGreen на белом даёт
   всего 2.2:1, читать им подписи нельзя. Все *-text проверены на ≥4.5:1
   и на белой карточке, и на сером фоне группы. */
:root{
  --bg:#F2F2F7;                       /* systemGroupedBackground */
  --surface:#FFFFFF;                  /* secondarySystemGroupedBackground */
  --line:rgba(60,60,67,.29);          /* separator — системное значение */
  --ink:#000000;                      /* label */
  --ink2:rgba(60,60,67,.78);
  --ink2-solid:#3C3C43;              /* без прозрачности: на цветной подложке .78 проседает */          /* secondaryLabel, уплотнён до 5.6:1 */
  --hover:rgba(120,120,128,.08);
  --press:rgba(120,120,128,.16);
  --bubble:rgba(120,120,128,.12);
  --edge-shadow:rgba(0,0,0,.09);   /* край шапки при прокрутке */
  --glass:rgba(249,249,249,.80);
  --glass-solid:#F8F8F9;             /* непрозрачный аналог стекла */
  --table-head-solid:#F4F4F5;        /* шапка таблицы: --hover поверх карточки, но без прозрачности */

  --brand:#007AFF;                    /* systemBlue */
  --fact:#30B0C7;                     /* systemTeal */
  --ok:#34C759;                       /* systemGreen */
  --warn:#FF9500;                     /* systemOrange */
  --danger:#FF3B30;                   /* systemRed */

  --brand-text:#0057B8;               /* 5.4:1 даже на 11%-плашке */
  --brand-fill:#0064D2;               /* заливка под БЕЛЫЙ текст: 5.59:1 */
  --fact-text:#005C74;                /* 5.6:1 на плашке */
  --ok-text:#1A7030;                  /* 5.1:1 на плашке */
  --warn-text:#9E4700;                /* 5.2:1 на плашке */
  --danger-text:#BE0012;              /* 5.2:1 на плашке */
  --ok-fill:#1D7A33;                  /* заливка под БЕЛЫМ текстом: 5.41:1 */
  --warn-fill:#B25000;                /* то же для оранжевого: 5.20:1 */
  /* Нейтральная (графитовая) заливка ПОД БЕЛЫЙ текст.
     🔴 Для этого нельзя брать --ink2-solid: это цвет ТЕКСТА, и он меняет
     светлоту вместе с темой. В тёмной теме он становится светлым, и белая
     надпись на нём давала 1.79:1 — кнопка входа в админку читалась как пустая
     плашка. Здесь оба значения проверены с белым текстом (10.9:1 и 5.07:1)
     и с границей к карточке (11.2:1 и 3.36:1). */
  --neutral-fill:#3C3C43;

  /* Полосы прогресса. Дорожка --line задумана под границы и слишком светлая:
     системный зелёный на ней даёт 1.3:1 при норме 3:1 (WCAG 1.4.11).
     Поэтому у полос своя дорожка и свои заливки — в светлой теме плотные,
     в тёмной наоборот яркие, иначе на тёмной дорожке пропадут они. */
  --track:#C9C9CE;                   /* заливка к дорожке 3.15:1; границу компонента даёт .track-edge */                   /* было 1.26:1 — остаток хода не читался; замер новой: 1.65:1 */
  --bar-brand:#0064D2; --bar-ok:#237A38; --bar-warn:#B25000;
  --line-2:#8E3B00;                  /* вторая линия графика: 7.57:1 к карточке */
  /* Дорожка тумблера — отдельно от полос: полосе нужен контраст к светлой
     дорожке, тумблеру — к БЕЛОМУ бегунку внутри. Один цвет обе задачи не решает. */
  --switch-warn:#B25000; --switch-ok:#237A38;
  --bar-danger:#BE0012; --bar-fact:#005C74;

  /* Граница ОРГАНА УПРАВЛЕНИЯ. --line задуман под разделители и даёт 1.67:1;
     для полей и карточек выбора нужно 3:1 (WCAG 1.4.11). */
  --control-line:rgba(60,60,67,.62);

  /* Заливка аватара: нейтральная, различает имя, а не цвет */
  --avatar:#6E6E73;         /* белый текст на нём — 5.07:1 */

  --sidebar:260px;
}
@media (prefers-color-scheme: dark){
  :root{
    --bg:#000000;
    --edge-shadow:rgba(0,0,0,.55);   /* на чёрном тень читается только глубокой */
    --surface:#1C1C1E;
    --line:rgba(84,84,88,.65);
    --ink:#FFFFFF;
    --ink2:rgba(235,235,245,.60);     /* на тёмном уже 5.9:1 — уплотнять не нужно */
    --ink2-solid:rgba(235,235,245,.82);
    --hover:rgba(120,120,128,.24);
    --press:rgba(120,120,128,.36);
    --bubble:rgba(120,120,128,.24);
    --glass:rgba(28,28,30,.80);
    --glass-solid:#161618;
    --table-head-solid:#323236;

    --brand:#0A84FF; --fact:#40C8E0; --ok:#30D158; --warn:#FF9F0A; --danger:#FF453A;

    /* Текст на тёмном: системные цвета сами дают ≥6:1 даже на 11%-плашке,
       кроме синего — его пришлось осветлить (#0A84FF давал 4.13). */
    --brand-text:#409CFF;  --fact-text:#40C8E0;  --ok-text:#30D158;
    --warn-text:#FF9F0A;   --danger-text:#FF6B61;   /* на светлой карточке #FF453A давал 4.45 */

    /* Заливки ПОД БЕЛЫМ текстом — отдельные и всегда тёмные.
       Красить фон яркими *-text нельзя: белое на #30D158 даёт 2:1. */
    --brand-fill:#0060DF;  --ok-fill:#237A38;    --warn-fill:#B25000;
    /* Светлее, чем в светлой теме, — иначе плашка сливается с чёрным фоном.
       Белый текст 5.07:1, граница к карточке 3.36:1. */
    --neutral-fill:#6E6E73;

    --track:#3A3A3C;
    --bar-brand:#409CFF; --bar-ok:#30D158; --bar-warn:#FF9F0A;
    --line-2:#FFB870;
    --bar-danger:#FF453A; --bar-fact:#40C8E0;

    --control-line:rgba(140,140,148,.95);
    --switch-warn:#B25000; --switch-ok:#237A38;
    --avatar:#636366;         /* белый текст — 5.99:1 */
  }
}

html,body{
  background:var(--bg); color:var(--ink); height:100%; margin:0;
  /* Системный шрифт первым: на Apple подхватится SF, на Windows — Inter */
  font-family:-apple-system, BlinkMacSystemFont, 'SF Pro Text', 'SF Pro Display',
              'Inter', system-ui, 'Segoe UI', sans-serif;
}
body{-webkit-font-smoothing:antialiased; text-rendering:optimizeLegibility;}

/* --- Типографика: трекинг по таблице оптических размеров SF.
   Крупный кегль сжимаем сильнее, мелкий — почти не трогаем. --- */
.t-h1    {font-size:1.5rem;   line-height:1.2;  letter-spacing:-.023em; font-weight:600;}
.t-num   {font-size:1.625rem; line-height:1.1;  letter-spacing:-.024em; font-weight:600;
          font-variant-numeric:tabular-nums;}
.t-title {font-size:1.0625rem;line-height:1.3;  letter-spacing:-.024em; font-weight:600;}
.t-body  {font-size:.9375rem; line-height:1.55; letter-spacing:-.016em;}
.t-sec   {font-size:.8125rem; line-height:1.5;  letter-spacing:-.006em; color:var(--ink2);}
/* Трекинг зависит от кегля в обе стороны: крупный сжимаем, мелкий, наоборот,
   чуть разводим — на 12px слипшиеся буквы читаются хуже. */
.t-tiny  {font-size:.75rem;   line-height:1.45; letter-spacing:.005em;}
/* Заголовки групп у Apple с iOS 13 — обычный регистр, а не разряжённый капслок */
.t-label {font-size:.8125rem; line-height:1.35; letter-spacing:-.006em; font-weight:600;
          text-transform:none; color:var(--ink2);}
.tnum{font-variant-numeric:tabular-nums;}

.surface{background:var(--surface);}
.bd{border-color:var(--line);}
/* Граница ОРГАНА УПРАВЛЕНИЯ. --line задуман под разделители и даёт 1.67:1,
   а WCAG 1.4.11 требует 3:1 для всего, что нажимают. Правило общее, чтобы
   не гоняться за каждой кнопкой по файлам: их три десятка на 15 экранах. */
.bd-control,
button.bd, a.bd, label.bd,
[role="radio"].bd, [role="switch"].bd, [role="tab"].bd,
/* Обойма переключателя — тоже орган управления: её рамка давала 1.68:1,
   потому что перечисление ловило только сами кнопки внутри. */
[role="radiogroup"].bd, select.bd{
  border-color:var(--control-line);
}
/* Списки выбора не .pressable и не .field — псевдоэлемент-мишень до них
   не достаёт, а на сенсорном нужна та же планка 44px. */
@media (pointer: coarse){ select{min-height:44px;} }
.glass{background:var(--glass); backdrop-filter:blur(20px) saturate(180%);
       -webkit-backdrop-filter:blur(20px) saturate(180%);}

/* --- Отклик ПОКАЗЫВАЕМ на нажатии, действие СРАБАТЫВАЕТ на отпускании (click).
   Так жест можно отменить, уведя палец, и работает клавиатура. --- */
/* Вход в нажатие и выход из него — разной длительности. Одинаковая кривая в
   обе стороны ощущается щелчком тумблера: палец уже отпустил, а элемент ещё
   рывком возвращается. Уходим в нажатие почти мгновенно, возвращаемся мягко и
   критически задемпфированно — без отскока, как у системных кнопок. */
.pressable{transition:transform 240ms cubic-bezier(.22,1,.36,1),
                      background-color 240ms cubic-bezier(.22,1,.36,1);
           -webkit-tap-highlight-color:transparent;}
.pressable:hover{background:var(--hover);}
.pressable[data-pressed="true"]{background:var(--press); transform:scale(.985);
           transition-duration:70ms;}
/* Кольцо фокуса СПЛОШНЫМ цветом. Полупрозрачный (55%) давал 2.03:1 к фону —
   ниже порога 3:1 из WCAG 2.4.11: каретку было плохо видно на всех экранах. */
:focus-visible{outline:none;
  box-shadow:0 0 0 2px var(--surface), 0 0 0 4px var(--brand);}

/* Карточка-ссылка: hover меняет только границу — Apple не двигает элементы под курсором.
   Объявлено после .pressable, иначе её шорткат transition затрёт border-color. */
.card-hover{transition:transform 240ms cubic-bezier(.22,1,.36,1),
                       background-color 240ms cubic-bezier(.22,1,.36,1),
                       border-color 200ms cubic-bezier(.25,.1,.25,1);}
.card-hover:hover{border-color:color-mix(in srgb, var(--brand) 45%, transparent);}

/* Ступени крупных чисел со своим трекингом */
.t-num-lg{font-size:2rem;    line-height:1.05; letter-spacing:-.026em; font-weight:600;
          font-variant-numeric:tabular-nums;}
/* Ступень числа ужимается на узкой плитке: «10 593 700 ₽» вылезало за карточку */
/* На 1024–1200 «10 593 700 ₽» упиралось в границу плитки без отступа */
@media (min-width:1024px) and (max-width:1120px){ .t-num{font-size:1.25rem;} }
@media (min-width:1121px) and (max-width:1200px){ .t-num{font-size:1.375rem;} }
/* «10 593 700 ₽» — самое длинное число кабинета: на узких плитках ему нужна
   своя ступень, иначе оно упиралось в границу карточки без отступа. */
@media (max-width: 339px){ .t-num{font-size:.875rem;} }
@media (min-width:340px) and (max-width: 389px){ .t-num{font-size:1rem;} }
@media (min-width:390px) and (max-width: 640px){
  .t-num{font-size:1.25rem;}
  .t-num-lg{font-size:1.625rem;}
}
.t-num-sm{font-size:1.375rem;line-height:1.1;  letter-spacing:-.021em; font-weight:600;
          font-variant-numeric:tabular-nums;}
/* Ступени зеркальны .t-num: одна общая граница оставляла «меньший» кегль
   крупнее большего на 320 (14 против 17) и на 1024–1120 (20 против 22). */
@media (max-width: 339px){ .t-num-sm{font-size:.75rem;} }
@media (min-width:340px) and (max-width: 389px){ .t-num-sm{font-size:.875rem;} }
@media (min-width:390px) and (max-width: 640px){ .t-num-sm{font-size:1.0625rem;} }
@media (min-width:1024px) and (max-width:1120px){ .t-num-sm{font-size:1.0625rem;} }
@media (min-width:1121px) and (max-width:1200px){ .t-num-sm{font-size:1.1875rem;} }

/* --- Поля --- */
.field{
  /* Заливка вместо рамки: хайрлайн --line давал 1.70:1 и поля читались призраками.
     tertiarySystemFill — штатный приём Apple для отдельно стоящего поля. */
  background:var(--bubble); border:1px solid var(--control-line); border-radius:10px;
  padding:9px 12px; font-size:.875rem; color:var(--ink); width:100%; outline:none;
  /* Одна высота на input, select и number: разнобой в 2px рвал строку сетки */
  appearance:none; -webkit-appearance:none; height:38px;
  transition:border-color 140ms cubic-bezier(.25,.1,.25,1),
             box-shadow 140ms cubic-bezier(.25,.1,.25,1);
}
.field:focus, .field:focus-visible{
  border-color:var(--brand);
  box-shadow:0 0 0 2px var(--surface), 0 0 0 4px var(--brand);}
.field::placeholder{color:var(--ink2);}
/* Свой шеврон вместо системной стрелки Windows */
select.field{padding-right:32px; background-repeat:no-repeat;
  background-position:right 10px center; background-size:12px;
  background-image:url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%233C3C43' stroke-width='2.2' stroke-linecap='round'><path d='M6 9l6 6 6-6'/></svg>");}
@media (prefers-color-scheme: dark){
  select.field{background-image:url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23EBEBF5' stroke-width='2.2' stroke-linecap='round'><path d='M6 9l6 6 6-6'/></svg>");}
}
textarea.field{height:auto;}

/* --- Свой список выбора ---
   Нативный <select> остаётся в разметке и держит значение, но его собственный
   попап рисует ОС и стилю экрана не подчиняется. Прячем сам контрол, оставляя
   ему коробку: так он продолжает участвовать в форме, а видимое место занимает
   наша кнопка. Из обхода Tab он убран через tabindex=-1. */
.sel{position:relative;}
.sel-native{position:absolute; inset:0; width:100%; height:100%; margin:0;
            opacity:0; pointer-events:none;}
.sel-btn{display:flex; align-items:center; gap:8px; text-align:left;}
.sel-label{flex:1; min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;}

/* Дорожка ползунка — тот же --track, что у полос: на --line она давала 1.70:1.
   Пройденная доля закрашивается, иначе 5% и 50% отличаются только бегунком. */
/* Дорожка: сама заливка + кромка 1px сверху и снизу. Кромка нужна, потому что
   --track к белой карточке даёт 1.65:1 — границы органа управления не видно
   (WCAG 1.4.11). Рисуем её слоями фона, а не box-shadow: тот ложится по
   border-box и на сенсорном обводил всю зону нажатия капсулой 44px. */
input[type=range]{-webkit-appearance:none; height:4px; border-radius:2px;
                  background:var(--track); outline:none;
                  background-image:linear-gradient(var(--bar-brand), var(--bar-brand)),
                                   linear-gradient(var(--control-line), var(--control-line)),
                                   linear-gradient(var(--control-line), var(--control-line));
                  background-size:var(--range-pct, 0%) 100%, 100% 1px, 100% 1px;
                  background-position:0 0, 0 0, 0 100%;
                  background-repeat:no-repeat;}
input[type=range]::-webkit-slider-thumb{
  -webkit-appearance:none; width:18px; height:18px; border-radius:50%;
  background:var(--surface); border:2px solid var(--brand); cursor:pointer;
  box-shadow:0 1px 4px rgba(0,0,0,.18); transition:transform 100ms cubic-bezier(.25,.1,.25,1);}
input[type=range]:active::-webkit-slider-thumb{transform:scale(1.12);}

/* --- Флажок ---
   Свой, а не системный. Замерено: системный приходит 13×13 со своим радиусом,
   своей толщиной галки и цветом из настроек ОС — рядом с полями на 10px и
   собственным шевроном списка он читается как деталь из другой программы,
   а в тёмной теме продолжает жить по правилам системы, а не экрана.
   Галка появляется пружинисто и гаснет мгновенно: включение — событие,
   выключение — отмена. */
input[type="checkbox"]{
  appearance:none; -webkit-appearance:none;
  width:20px; height:20px; margin:0; flex:none;
  border:1.5px solid var(--control-line); border-radius:6px;
  background:var(--surface);
  display:inline-grid; place-content:center; cursor:pointer;
  transition:background-color 160ms cubic-bezier(.25,.1,.25,1),
             border-color 160ms cubic-bezier(.25,.1,.25,1);
}
input[type="checkbox"]::before{
  content:''; width:12px; height:12px; background:#fff;
  transform:scale(.3); opacity:0;
  transition:transform 180ms cubic-bezier(.22,1,.36,1), opacity 110ms linear;
  -webkit-mask:var(--check) center/contain no-repeat;
          mask:var(--check) center/contain no-repeat;
}
:root{
  --check:url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='3.4' stroke-linecap='round' stroke-linejoin='round'><path d='M20 6L9 17l-5-5'/></svg>");
}
input[type="checkbox"]:checked{background:var(--brand-fill); border-color:var(--brand-fill);}
input[type="checkbox"]:checked::before{transform:scale(1); opacity:1;}
input[type="checkbox"]:disabled{opacity:.45; cursor:default;}
@media (prefers-reduced-motion: reduce){
  input[type="checkbox"]::before{transition:opacity 120ms linear; transform:scale(1);}
}

/* Выключенный переключатель: --line даёт 1.7:1 и контрол сливается с карточкой.
   WCAG 1.4.11 требует 3:1 для границ элементов управления. */
.switch-off{background:#8E8E93 !important;}   /* systemGray: 3.3:1 к белой карточке */

.switch-knob{box-shadow:0 1px 2px rgba(0,0,0,.25);}
/* Дорожка тумблера обведена волосяной линией: системный жёлтый на жёлтой карточке
   иначе сливается с ней, и границы контрола не видно. */
.switch-track{box-shadow:inset 0 0 0 1px rgba(0,0,0,.14);}
/* Обводка дорожки объявлена ПОЗЖЕ :focus-visible при равной специфичности,
   поэтому кольцо фокуса надо вернуть здесь же — иначе тумблеры его теряют. */
.switch-track:focus-visible{box-shadow:inset 0 0 0 1px rgba(0,0,0,.14),
  0 0 0 2px var(--surface), 0 0 0 4px var(--brand);}
@media (prefers-color-scheme: dark){
  .switch-track{box-shadow:inset 0 0 0 1px rgba(255,255,255,.18);}
  .switch-track:focus-visible{box-shadow:inset 0 0 0 1px rgba(255,255,255,.18),
    0 0 0 2px var(--surface), 0 0 0 4px var(--brand);}
}

/* --- Скролл --- */
.scroll-thin::-webkit-scrollbar{width:8px; height:8px;}
.scroll-thin::-webkit-scrollbar-thumb{background:var(--line); border-radius:8px;}
.scroll-thin::-webkit-scrollbar-track{background:transparent;}
/* Мягкая маска вместо жёсткой линии там, где контент уходит под липкую шапку */
.scroll-mask{-webkit-mask-image:linear-gradient(to bottom, transparent 0, #000 14px);
                     mask-image:linear-gradient(to bottom, transparent 0, #000 14px);}

/* Край шапки. Постоянная черта под стеклом читается как шов и спорит с самим
   смыслом материала: контент должен исчезать ПОД шапкой, а не упираться в
   линейку. Поэтому граница не рисуется вовсе, а при прокрутке снизу проявляется
   мягкая тень — ровно там и тогда, когда под шапку заезжает содержимое. */
.appbar{position:relative;}
.appbar::after{content:''; position:absolute; left:0; right:0; top:100%; height:12px;
               pointer-events:none; opacity:0;
               transition:opacity 220ms cubic-bezier(.25,.1,.25,1);
               background:linear-gradient(to bottom, var(--edge-shadow), transparent);}
.appbar.edge::after{opacity:1;}
@media (prefers-reduced-motion: reduce){ .appbar::after{transition:none;} }

/* --- Таблицы --- */
tbody tr{transition:background-color 120ms cubic-bezier(.25,.1,.25,1);}
tbody tr:hover{background:var(--hover);}

/* --- Сайдбар --- */
#appSidebar{width:var(--sidebar); will-change:transform;}
#navPill{position:absolute; left:8px; right:8px; height:38px; border-radius:10px;
         background:color-mix(in srgb, var(--brand) 11%, transparent);
         border:1px solid color-mix(in srgb, var(--brand) 24%, transparent);
         pointer-events:none; will-change:transform;}
#appScrim{position:fixed; inset:0; background:rgba(0,0,0,.42); opacity:0;
          pointer-events:none; z-index:40; transition:opacity 220ms cubic-bezier(.25,.1,.25,1);}

@media (max-width: 900px){
  #appSidebar{position:fixed; top:0; bottom:0; left:0; z-index:50;
              transform:translateX(-100%); box-shadow:0 0 40px rgba(0,0,0,.18);}
  #appSidebar.open{transform:translateX(0);}
  #appScrim.open{opacity:1; pointer-events:auto;}
  .burger{display:grid !important;}
}

.burger{display:none;}

/* --- Анимации --- */
@keyframes riseIn{from{opacity:0; transform:translateY(12px)} to{opacity:1; transform:none}}
.rise{animation:riseIn 440ms cubic-bezier(.16,1,.3,1) both;}
@keyframes fadeIn{from{opacity:0} to{opacity:1}}
.fade{animation:fadeIn 260ms cubic-bezier(.25,.1,.25,1) both;}

/* --- Уважение к настройкам пользователя ---
   Reduce Motion у Apple означает «заменить движение на кросс-фейд»,
   а не «то же движение, но быстрее». Поэтому глушим transform/animation
   полностью, а переходы прозрачности оставляем — они помогают понимать. */
@media (prefers-reduced-motion: reduce){
  *{animation-duration:.01ms !important; animation-iteration-count:1 !important;
    transition-duration:.01ms !important;}
  .rise, .fade{animation:none;}
  .pressable[data-pressed="true"]{transform:none;}
  input[type=range]:active::-webkit-slider-thumb{transform:none;}
  /* проявление оставляем — оно не вестибулярное */
  .fade, [style*="opacity"]{transition:opacity 150ms linear !important;}
}
@media (prefers-reduced-transparency: reduce){
  .glass{background:var(--surface) !important; backdrop-filter:none !important;
         -webkit-backdrop-filter:none !important;}
}
@media (prefers-contrast: more){
  :root{
    --line:rgba(60,60,67,.55);
    --ink2:rgba(60,60,67,.92);
    --brand-text:#0040DD; --ok-text:#14652A; --warn-text:#8F4000;
    --danger-text:#B00010; --fact-text:#005569;
    /* Заливка теперь единственная граница поля — в этом режиме её надо усилить,
       иначе поля читаются ХУЖЕ, чем в обычном (замер давал 1.15:1). */
    --bubble:rgba(120,120,128,.22);
    --control-line:rgba(60,60,67,.75);   /* плотнее заливки, иначе рамка тонет */
  }
  .surface{border-color:var(--line);}
}

/* Тёмная тема с повышенным контрастом — отдельно. Блок выше объявлен ПОСЛЕ
   dark и перекрывал его светлыми значениями: текст падал с 5.95 до 1.49:1,
   то есть настройка доступности делала экран нечитаемым. */
@media (prefers-contrast: more) and (prefers-color-scheme: dark){
  :root{
    --line:rgba(120,120,128,.85);
    --ink2:rgba(235,235,245,.92);
    --bubble:rgba(120,120,128,.40);
    --control-line:rgba(170,170,178,1);
    --brand-text:#6AB4FF; --ok-text:#5CE07E; --warn-text:#FFB84D;
    --danger-text:#FF7A70; --fact-text:#66DCF0;
  }
}

/* Поле поиска в шапке: кольцо должно быть вокруг всей коробки, а не вокруг
   внутреннего input — иначе синий прямоугольник висит внутри серой рамки. */
.search-box{background:var(--bubble); border:1px solid var(--control-line);}
.search-box:focus-within{border-color:var(--brand);
  box-shadow:0 0 0 2px var(--surface), 0 0 0 4px var(--brand);}
.search-box input:focus-visible{box-shadow:none;}

/* Уехав вбок, пользователь переставал видеть, о какой строке речь */
/* Ниже 700px липкая колонка съедает почти всю ширину (замер: 303 из 340),
   и на данные места не остаётся — там она вреднее прокрутки. */
@media (min-width: 700px){
  /* Красим фоном ТОЙ поверхности, на которой лежит таблица. У экрана,
     где она лежит прямо на фоне страницы, --table-bg переопределяется локально. */
  .sticky-first th:first-child, .sticky-first td:first-child{
    position:sticky; left:0; z-index:1; background:var(--table-bg, var(--surface));}
  /* Непрозрачный аналог стекла: ряд шапки уже размывает фон, второй слой
     на липкой ячейке давал разницу в 5/255 и считал блюр дважды. */
  /* Шапке таблицы нужен непрозрачный аналог ЕЁ заливки (--hover), а не стекла:
     с --glass-solid под первой колонкой была видна тёмная плашка. */
  .sticky-first thead th:first-child{
    background:var(--table-head-solid);
    backdrop-filter:none; -webkit-backdrop-filter:none;}
}

/* Широкая таблица на телефоне: последний столбец несёт ответ (прибыль, решение
   системы), а лежит он в 440–520px за правым краем, и при прокрутке теряется,
   к какой строке относится значение. Ниже 700px таблица уступает место списку
   карточек — те же данные, но без горизонтальной прокрутки. */
.table-cards{display:none;}
@media (max-width:699px){
  .table-wide{display:none;}
  .table-cards{display:block;}
}
/* Таблице тарифов нужно больше: до 819px она ещё режет «Партнёр» (замер при
   700px — 62% колонки, «от 19 900 ₽» читается как «от 19 900»). iPad mini
   в портрете — ровно 744px, поэтому порог поднят именно для неё. */
@media (max-width:819px){
  #planCards{display:block;}
  #planCards + .table-wide{display:none;}
}
/* Второй диапазон: на 901px сайдбар входит в поток и отнимает 255px ширины,
   поэтому таблице снова тесно — обрезка просто переехала бы сюда.
   Замер: 900px → контейнер 846, 901px → 591 при нужных 720. */
@media (min-width:900px) and (max-width:1029px){
  #planCards{display:block;}
  #planCards + .table-wide{display:none;}
}

/* Таблица тарифов: первая колонка узкая (название возможности), поэтому
   липкой её можно держать на любой ширине — иначе на телефоне прокрутка
   к своей колонке срезала подписи строк. */
.sticky-plans th:first-child, .sticky-plans td:first-child{
  position:sticky; left:0; z-index:1; background:var(--surface); min-width:132px;}
/* Жёсткие 132px нужны только на узком экране. На 1440 места хватает, а
   фиксированная ширина всё равно ломала шесть подписей из девяти на две строки. */
@media (max-width:699px){
  .sticky-plans th:first-child, .sticky-plans td:first-child{width:132px;}
}
.sticky-plans thead th:first-child{background:var(--table-head-solid);}

/* Дорожка полосы к белой карточке — 1.65:1, то есть границы самого органа
   управления не видно (WCAG 1.4.11). Затемнять дорожку нельзя: это роняет
   контраст заливки к ней. Даём волосяную обводку внутрь. */
[role="progressbar"], .track-edge{box-shadow:inset 0 0 0 1px var(--control-line);}


/* При открытой шторке панель отмены прячем: она лежала поверх меню, срезала
   карточку тарифа в сайдбаре, а фокус до её кнопки не доходил. Окно
   отмены при этом продолжает идти — панель вернётся при закрытии меню. */
#appSidebar.open ~ #undo, body:has(#appSidebar.open) #undo{display:none !important;}

/* Счётчик в шапке «Объявлений» и бейдж роли в «Админке» уводили шапку
   во второй ряд на узком экране. Оба дублируются в теле страницы. */
@media (max-width:430px){ #total{display:none !important;} }
@media (max-width:430px){ header > .flex-1{display:none;} }
/* На 320px заголовок, «Импорт» и «Создать» вместе не помещаются:
   импорт остаётся доступен со «Студии» и из поиска. */
@media (max-width:359px){ .import-btn{display:none;} }
@media (max-width:379px){ .role-badge{display:none !important;} }

/* Ниже 440px «7 дней / 30 дней / 90 дней» и «Выгрузить» не помещаются в один
   ряд с заголовком: шапка занимала 104px — 12% экрана постоянно. */
.per-short{display:none;}
@media (max-width:559px){
  .per-full{display:none;}
  .per-short{display:inline-flex; align-items:center;}
}
/* На 320px сумма «Меню + заголовок + распорка + период» ровно равна ширине
   экрана и переносится. Сжимаем поля кнопок и убираем распорку. */
@media (max-width:359px){
  #period .per{padding-left:.375rem; padding-right:.375rem;}
  header > .flex-1{display:none;}
}

/* Пять шагов воронки в ряд требуют 824px: на 768–1171 последняя карточка
   уезжала за окно, и «Сделки» — то, ради чего блок назван «до сделки», —
   пропадали с экрана. */
.funnel-row{flex-direction:column;}
.funnel-chev{transform:rotate(90deg);}
@media (min-width:1180px){
  .funnel-row{flex-direction:row; flex-wrap:nowrap;}
  .funnel-chev{transform:none;}
}
/* Второй трек auto раздувался до max-content (617px), а главная колонка
   схлопывалась до 40px: ограничиваем её долей и держим минимум для главной. */
.funnel-roi{grid-template-columns:minmax(0,1fr);}
@media (min-width:1280px){
  .funnel-roi{grid-template-columns:minmax(320px,1.6fr) minmax(0,clamp(220px,26%,340px));}
  .roi-side{text-align:right;}
}

/* Поиск в меню нужен только там, где лупа не помещается в шапку */
.nav-search{display:none !important;}
/* Лупу прячем до 439px включительно: на 430px (iPhone Plus, Pro Max)
   она уводила шапку во второй ряд — 113px вместо 61px. */
@media (max-width:439px){ .search-narrow-hidden{display:none !important;} }
/* Полное «Тестовый режим» ниже 380px уводит шапку во второй ряд; имя контрола
   несёт aria-label, а название повторяется в баннере под шапкой. */
@media (max-width:389px){ .dry-full{display:none;} }
@media (min-width:390px){ .dry-short{display:none;} }
@media (max-width:439px){ .nav-search{display:flex !important;} }

/* Плавающая панель обязана читаться как слой над содержимым: без тени и с
   границей --line она была белым на белом (1.00:1) и выглядела продолжением списка. */
.shadow-lift{box-shadow:0 12px 40px rgba(0,0,0,.18);}
/* Список не должен уходить под панель: она резала нижний край кнопок
   последнего черновика на всех ширинах. */
body:has(#undo:not(.hidden)) #drafts{padding-bottom:76px;}

/* Окно отмены живёт в body (иначе маска прокрутки срезает его текст), поэтому
   левый край надо отвести от сайдбара вручную — иначе панель ляжет поверх меню. */
@media (min-width: 901px){
  #undo{left:calc(var(--sidebar) + 12px);}
}

/* Текст только для скринридера.
   ВАЖНО: он position:absolute, и без позиционированного предка считает
   координаты от документа — на экране ассистента три таких подписи
   растягивали страницу на 324px пустоты. Поэтому main всегда relative. */
main{position:relative;}
.sr-only{position:absolute; width:1px; height:1px; padding:0; margin:-1px;
         overflow:hidden; clip:rect(0 0 0 0); white-space:nowrap; border:0;}

/* Всплывающая панель: тень мягче, чем shadow-2xl — ближе к Apple */
#searchPanel{box-shadow:0 12px 40px rgba(0,0,0,.16), 0 1px 3px rgba(0,0,0,.06);}

/* Цель нажатия: на тач-устройствах строка меню не должна быть меньше 44px */
#appNav a{min-height:38px;}
@media (pointer: coarse){ #appNav a{min-height:44px;} }


/* Край прокрутки вместо жёсткой обрезки. На узком экране таблица уезжает вправо,
   и без этой подсказки не видно, что её вообще можно листать. */
.scroll-edge{position:absolute; top:0; right:0; bottom:0; width:40px; pointer-events:none;
             z-index:2; opacity:0; transition:opacity 220ms cubic-bezier(.25,.1,.25,1);
             background:linear-gradient(to left, rgba(0,0,0,.13), rgba(0,0,0,0));}
.scroll-edge-left{position:absolute; top:0; left:0; bottom:0; width:40px; pointer-events:none;
                  z-index:2; opacity:0; transition:opacity 220ms cubic-bezier(.25,.1,.25,1);
                  background:linear-gradient(to right, rgba(0,0,0,.13), rgba(0,0,0,0));}
@media (prefers-color-scheme: dark){
  .scroll-edge{background:linear-gradient(to left, rgba(0,0,0,.45), rgba(0,0,0,0));}
  .scroll-edge-left{background:linear-gradient(to right, rgba(0,0,0,.45), rgba(0,0,0,0));}
}
@media (prefers-reduced-motion: reduce){ .scroll-edge{transition:none;} }


/* ---------- Палец толще курсора ----------
   Apple держит планку 44×44pt. Расширяем именно ЗОНУ НАЖАТИЯ, а не сам элемент:
   псевдоэлемент не участвует в потоке, поэтому вёрстка на месте, а промахов нет.
   Только для сенсорного ввода — мышь и так попадает. */
@media (pointer: coarse){
  .pressable, [role="switch"], .row-check, #checkAll{position:relative;}
  .pressable::after, [role="switch"]::after{
    content:''; position:absolute; top:50%; left:50%; transform:translate(-50%,-50%);
    width:100%; height:100%; min-width:44px; min-height:44px;
  }
  /* Соседние кнопки 36px с зоной 44 стояли в 42px друг от друга: зоны
     перекрывались на 2px, и край одной перехватывал нажатие у соседа. */
  .tap-row{gap:0.5rem;}

  /* Внутри горизонтального скролла псевдоэлемент обрезается по вертикали —
     там зону расширяем настоящей высотой. */
  .overflow-x-auto .pressable{min-height:44px;}
  /* В переносимых рядах кнопки 31px стоят с зазором 8px: псевдоэлемент 44px
     вылезал на соседа, и промах по нижнему краю ВЫПОЛНЯЛ ЧУЖОЕ ДЕЙСТВИЕ.
     Здесь зона нужна настоящей высотой, а не наложением. */
  .flex-wrap > .pressable{min-height:44px;}
  /* Ссылка внутри обрезанной строки: truncate на самой ссылке давал
     overflow:hidden и срезал её зону до высоты строки — 23px вместо 44. */
  .link-tap{display:inline-flex; align-items:center; min-height:44px;}

  /* Поля и коробка поиска — не .pressable, псевдоэлемент до них не достаёт */
  .field{height:44px;}
  textarea.field{height:auto;}
  .search-box{min-height:44px; padding-block:0 !important;}   /* Tailwind с CDN грузится позже */
  .search-box input{align-self:stretch;}

  /* Флажки и ползунки — заменяемые элементы, псевдоэлемент к ним не крепится.
     Ползунку хватает прозрачного поля, флажку нужна мишень-обёртка. */
  input[type="checkbox"]{width:20px; height:20px;}
  .check-hit{display:grid; place-items:center; width:44px; height:44px; margin:-12px;}
  /* Без content-box отступ съедал всю высоту (border-box), и на сенсорном
     экране у ползунка пропадали и дорожка, и заливка пройденной доли.
     4px дорожки + 2×20px = ровно 44pt зоны нажатия. */
  input[type="range"]{box-sizing:content-box; padding-block:20px;
                     background-clip:content-box; background-origin:content-box;}
}


/* Заголовок, на который фокус переводит код (смена шага мастера), не должен
   рисовать рамку во всю ширину: пользователь туда не переходил табом.
   Фокус при этом реально стоит — скринридер шаг прочитает. */
h1[tabindex="-1"]:focus, h1[tabindex="-1"]:focus-visible,
h2[tabindex="-1"]:focus, h2[tabindex="-1"]:focus-visible{outline:none; box-shadow:none;}


/* «К содержимому» — первым в порядке обхода. Без него до первого элемента
   страницы двенадцать нажатий Tab через всё меню (WCAG 2.4.1, уровень A). */
.skip-link{position:fixed; top:8px; left:8px; z-index:200; transform:translateY(-160%);
           background:var(--surface); color:var(--ink); border:1px solid var(--control-line);
           border-radius:10px; padding:11px 14px; font-size:.875rem; font-weight:500;
           text-decoration:none; box-shadow:0 8px 24px rgba(0,0,0,.16);
           transition:transform 200ms cubic-bezier(.25,.1,.25,1);}
.skip-link:focus{transform:translateY(0);
  box-shadow:0 0 0 2px var(--surface), 0 0 0 4px var(--brand), 0 8px 24px rgba(0,0,0,.16);}
@media (prefers-reduced-motion: reduce){ .skip-link{transition:none;} }

/* Ссылка-название внутри строки таблицы. Замерено: 16–19px по высоте, тогда
   как WCAG 2.5.8 требует 24×24 даже для мыши. Растягивать саму строку нельзя —
   таблица станет разреженной; поднимаем высоту ЦЕЛИ, оставляя текст на месте.
   На сенсорном экране до 44px её доводит общее правило .link-tap ниже. */
.cell-link{display:inline-flex; align-items:center; min-height:24px;}
@media (pointer: coarse){ .cell-link{min-height:44px;} }

/* Колонка происхождения факта. На узком экране 150px — иначе значение сжимается;
   с 768px даём 190px, чтобы «спросили 3 раза — факта нет» шло одной строкой
   и правый край не выглядел лесенкой. */
.prov{width:150px;}
@media (min-width:768px){ .prov{width:190px;} }

/* Карточки «что требует вас сейчас». На планшете три карточки в двух колонках
   оставляли третью — и это как раз красная «Объявление отклонено» — висеть
   сиротой рядом с пустотой. Последняя карточка нечётного ряда занимает ряд
   целиком: дыры нет, а самое срочное становится шире, а не одиноким. */
.actions-grid{grid-template-columns:minmax(0,1fr);}
@media (min-width:640px){
  .actions-grid{grid-template-columns:repeat(2,minmax(0,1fr));}
  .actions-grid > :last-child:nth-child(odd){grid-column:1 / -1;}
}
@media (min-width:1024px){
  .actions-grid{grid-template-columns:repeat(3,minmax(0,1fr));}
  .actions-grid > :last-child:nth-child(odd){grid-column:auto;}
  /* Карточек бывает не три: предупреждение о кошельке появляется только когда
     деньги вот-вот кончатся. Последняя, оставшаяся одна в ряду (4-я, 7-я…),
     занимает ряд целиком — иначе рядом с ней зияет пустая треть. */
  .actions-grid > :last-child:nth-child(3n+1){grid-column:1 / -1;}
}

/* Каталог товаров. Это рабочий список продавца, а не витрина покупателя:
   решение принимается по названию и полноте паспорта, поэтому карточке хватает
   меньшей ширины, а фото — меньшей доли. Четвёртая колонка появляется только
   когда каждая карточка остаётся шире ~300px, иначе название ломается на три
   строки и плотность оборачивается против читаемости. */
.goods-grid{display:grid; grid-template-columns:repeat(auto-fill, minmax(260px, 1fr));}

/* Список — один товар в строку. Нужен, когда товаров десятки: глаз идёт по
   одной вертикали названий и сравнивает полноту паспорта, а не прыгает по сетке. */
.goods-list{display:grid; grid-template-columns:minmax(0,1fr);}

/* Колонка полноты паспорта в списке товаров. На 96px подпись и число
   «8 из 10» вставали в две строки и рвали ритм строки; с 768px, где подпись
   вообще показывается, даём запас. */
.passport-col{width:96px;}
@media (min-width:768px){ .passport-col{width:136px;} }

/* Название товара в плитке — ровно две строки: длинное обрезается, короткое
   резервирует место. Иначе цена, полоса паспорта и статус в соседних карточках
   встают на разной высоте, и ряд перестаёт читаться как ряд. */
.goods-title{display:-webkit-box; -webkit-line-clamp:2; -webkit-box-orient:vertical;
             overflow:hidden; min-height:2.7em;}
