/* ============================================================================
   PREMIUM WEB DELIVERY — DESIGN SYSTEM
   Готовая дизайн-система одним файлом. Чистый CSS, без сборщика, без зависимостей.
   Проверено под Flask/Jinja: <link rel="stylesheet" href="{{ url_for('static',
   filename='design-system.css') }}"> в base.html ПЕРВЫМ, свой app.css — ВТОРЫМ.

   ПРИНЦИПЫ (нарушение = провал на адверсариальном ревью):
   1. РАЗМЕР ПРЕВЬЮ = РАЗМЕРУ РЕШЕНИЯ. «Узнать объект» ~200px, «сравнить два» ~500px,
      «разметить/принять по лицу» ~800px. Никаких 190px там, где решают по лицу.
      Готовые сетки: .media-grid--recognize / --compare / --annotate (см. блок 15).
   2. Критические данные не прячем в миниатюры. Оригинал и результат сравниваем
      в равном размере (.compare-pair), а не плашкой 34x54.
   3. Каждый компонент имеет ВСЕ состояния: hover / active / focus-visible /
      disabled / loading / empty / error. Состояние без стиля = дыра в системе.
   4. Ничего «модного»: запрещены glassmorphism (backdrop-filter на панелях),
      неоновые градиенты, glow-тени, цветные свечения. Премиальность даёт
      плотность, точность отступов, системность состояний и скорость, не блеск.
   5. Всё через токены. Хардкод цвета/отступа в шаблоне — ошибка ревью.

   СТРУКТУРА ФАЙЛА:
     01 Токены: цвет (light)          09 Card
     02 Токены: цвет (dark)           10 Badge / Chip
     03 Токены: размеры, тень, z, время  11 Table
     04 Reset и база                  12 Modal / Toast / Tooltip / Popover
     05 Типографика                   13 Tabs / Stepper / Progress
     06 Layout, плотность, утилиты    14 Skeleton / Spinner / Empty / Kbd
     07 Button                        15 Media: сетки, aspect-ratio, тайлы
     08 Формы                         16 Accessibility, motion, print
   ========================================================================= */


/* ============================================================================
   01. ТОКЕНЫ — ЦВЕТ (светлая тема, база)
   Все цвета определяются ЗДЕСЬ на голом :root. Тёмная тема ниже только
   переопределяет те же имена. Никогда не задавай цвет только внутри @media —
   иначе в одной из тем он будет пустым.
   ========================================================================= */
/* Metal Virtus Group, V1 «Кузня» (T025): panel light/dark, terminal dark — see app/static/brand/tokens.json. */
/* brand-tokens:panel-light begin — generated by tools/apply_brand_tokens.py from app/static/brand/tokens.json; do not edit by hand */
.panel {
  --surface-0: #F2F1EE;
  --surface-1: #FFFFFF;
  --surface-2: #F8F7F5;
  --surface-3: #FFFFFF;
  --surface-4: #E7E5E0;
  --surface-inverse: #1E2125;
  --text-1: #16181B;
  --text-2: #41464D;
  --text-3: #555A62;
  --text-4: #62676F;
  --text-inverse: #F5F4F1;
  --border: #D2D0CA;
  --border-subtle: #E4E2DD;
  --border-strong: #83888F;
  --link: #A23A0B;
  --link-hover: #7F2D08;
  --focus-ring: #B9400C;
  --accent: #B9400C;
  --accent-hover: #9D360A;
  --accent-active: #822C08;
  --accent-on: #FFFFFF;
  --accent-bg: #FCEADF;
  --accent-fg: #97350A;
  --accent-border: #E2A986;
  --fill-hover: rgba(22,24,27,0.05);
  --fill-active: rgba(22,24,27,0.09);
  --fill-selected: rgba(185,64,12,0.10);
  --fill-track: #E1DFD9;
  --scrim: rgba(14,16,18,0.55);
  --skeleton-base: #E7E5E0;
  --skeleton-sheen: #F4F3F0;
  --success: #1D7442;
  --success-hover: #186238;
  --success-active: #13502D;
  --success-on: #FFFFFF;
  --success-bg: #E5F3EA;
  --success-fg: #17613A;
  --success-border: #A9D6BA;
  --warning: #8C6800;
  --warning-hover: #765700;
  --warning-active: #614800;
  --warning-on: #FFFFFF;
  --warning-bg: #FBF2CF;
  --warning-fg: #6B5000;
  --warning-border: #E2C766;
  --danger: #B01E3C;
  --danger-hover: #961933;
  --danger-active: #7C142A;
  --danger-on: #FFFFFF;
  --danger-bg: #FBE7EB;
  --danger-fg: #991A35;
  --danger-border: #EDB3C0;
  --info: #2B678A;
  --info-hover: #245774;
  --info-active: #1D475F;
  --info-on: #FFFFFF;
  --info-bg: #E5EFF5;
  --info-fg: #215675;
  --info-border: #ADCADC;
  --chart-work: #B9400C;
  --chart-net: #17613A;
  --chart-pause: #6B5000;
  --camera-fill: #FFFFFF;
  --camera-fill-soft: #F3F2EF;
  --camera-ink: #16181B;
  --camera-muted: #474C53;
  --camera-success: #17633A;
  --camera-border: #737A83;
  --terminal-exit-bg: #9E1F37;
  --terminal-exit-on: #FFFFFF;
  --crm-shadow: 0 16px 48px rgba(22, 24, 27, 0.16);
}
/* brand-tokens:panel-light end */
/* brand-tokens:panel-dark begin — generated by tools/apply_brand_tokens.py from app/static/brand/tokens.json; do not edit by hand */
[data-theme="dark"] .panel {
  --surface-0: #141619;
  --surface-1: #1C1F23;
  --surface-2: #23272C;
  --surface-3: #2A2F35;
  --surface-4: #101214;
  --surface-inverse: #ECEAE6;
  --text-1: #F1EFEB;
  --text-2: #BEC2C8;
  --text-3: #A9AEB5;
  --text-4: #959BA3;
  --text-inverse: #16181B;
  --border: #363B42;
  --border-subtle: #2A2E34;
  --border-strong: #6E757E;
  --link: #FF9C5E;
  --link-hover: #FFB889;
  --focus-ring: #FF8A3D;
  --accent: #FF7A2E;
  --accent-hover: #FF9150;
  --accent-active: #EC6A1F;
  --accent-on: #170A03;
  --accent-bg: #3A2215;
  --accent-fg: #FFA46D;
  --accent-border: #7D401D;
  --fill-hover: rgba(255,255,255,0.06);
  --fill-active: rgba(255,255,255,0.10);
  --fill-selected: rgba(255,122,46,0.16);
  --fill-track: #2D3137;
  --scrim: rgba(0,0,0,0.66);
  --skeleton-base: #23272C;
  --skeleton-sheen: #2D3238;
  --success: #45B673;
  --success-hover: #59C384;
  --success-active: #38995F;
  --success-on: #04150B;
  --success-bg: #12271B;
  --success-fg: #80D4A3;
  --success-border: #265331;
  --warning: #EAC43C;
  --warning-hover: #F2D058;
  --warning-active: #D0AA25;
  --warning-on: #1A1400;
  --warning-bg: #29230F;
  --warning-fg: #F0D26E;
  --warning-border: #5B4B17;
  --danger: #F2637E;
  --danger-hover: #F57B92;
  --danger-active: #D64F68;
  --danger-on: #1A0509;
  --danger-bg: #2E1419;
  --danger-fg: #F7909F;
  --danger-border: #622633;
  --info: #5CA8D7;
  --info-hover: #75B7E0;
  --info-active: #4791BE;
  --info-on: #04121B;
  --info-bg: #0F2230;
  --info-fg: #92C8E7;
  --info-border: #23495F;
  --chart-work: #FF7A2E;
  --chart-net: #80D4A3;
  --chart-pause: #F0D26E;
  --camera-fill: #FFFFFF;
  --camera-fill-soft: #F3F2EF;
  --camera-ink: #16181B;
  --camera-muted: #474C53;
  --camera-success: #17633A;
  --camera-border: #737A83;
  --terminal-exit-bg: #9E1F37;
  --terminal-exit-on: #FFFFFF;
  --crm-shadow: 0 16px 48px rgba(0, 0, 0, 0.42);
}
/* brand-tokens:panel-dark end */
/* brand-tokens:root-light begin — generated by tools/apply_brand_tokens.py from app/static/brand/tokens.json; do not edit by hand */
:root {
  color-scheme: light;
  --brand-primary: #23272C;
  --brand-accent: #E8560F;
  --brand-ink: #16181B;
  --brand-paper: #F2F1EE;
  --surface-0: #F2F1EE;
  --surface-1: #FFFFFF;
  --surface-2: #F8F7F5;
  --surface-3: #FFFFFF;
  --surface-4: #E7E5E0;
  --surface-inverse: #1E2125;
  --text-1: #16181B;
  --text-2: #41464D;
  --text-3: #555A62;
  --text-4: #62676F;
  --text-inverse: #F5F4F1;
  --border: #D2D0CA;
  --border-subtle: #E4E2DD;
  --border-strong: #83888F;
  --link: #A23A0B;
  --link-hover: #7F2D08;
  --focus-ring: #B9400C;
  --accent: #B9400C;
  --accent-hover: #9D360A;
  --accent-active: #822C08;
  --accent-on: #FFFFFF;
  --accent-bg: #FCEADF;
  --accent-fg: #97350A;
  --accent-border: #E2A986;
  --fill-hover: rgba(22,24,27,0.05);
  --fill-active: rgba(22,24,27,0.09);
  --fill-selected: rgba(185,64,12,0.10);
  --fill-track: #E1DFD9;
  --scrim: rgba(14,16,18,0.55);
  --skeleton-base: #E7E5E0;
  --skeleton-sheen: #F4F3F0;
  --success: #1D7442;
  --success-hover: #186238;
  --success-active: #13502D;
  --success-on: #FFFFFF;
  --success-bg: #E5F3EA;
  --success-fg: #17613A;
  --success-border: #A9D6BA;
  --warning: #8C6800;
  --warning-hover: #765700;
  --warning-active: #614800;
  --warning-on: #FFFFFF;
  --warning-bg: #FBF2CF;
  --warning-fg: #6B5000;
  --warning-border: #E2C766;
  --danger: #B01E3C;
  --danger-hover: #961933;
  --danger-active: #7C142A;
  --danger-on: #FFFFFF;
  --danger-bg: #FBE7EB;
  --danger-fg: #991A35;
  --danger-border: #EDB3C0;
  --info: #2B678A;
  --info-hover: #245774;
  --info-active: #1D475F;
  --info-on: #FFFFFF;
  --info-bg: #E5EFF5;
  --info-fg: #215675;
  --info-border: #ADCADC;
  --chart-work: #B9400C;
  --chart-net: #17613A;
  --chart-pause: #6B5000;
  --camera-fill: #FFFFFF;
  --camera-fill-soft: #F3F2EF;
  --camera-ink: #16181B;
  --camera-muted: #474C53;
  --camera-success: #17633A;
  --camera-border: #737A83;
  --terminal-exit-bg: #9E1F37;
  --terminal-exit-on: #FFFFFF;
  --shadow-1: 0 1px 2px rgba(22, 24, 27, 0.07), 0 1px 1px rgba(22, 24, 27, 0.04);
  --shadow-2: 0 2px 4px -1px rgba(22, 24, 27, 0.08), 0 6px 14px -4px rgba(22, 24, 27, 0.09);
  --shadow-3: 0 4px 10px -3px rgba(22, 24, 27, 0.11), 0 16px 32px -10px rgba(22, 24, 27, 0.15);
  --shadow-4: 0 8px 20px -6px rgba(22, 24, 27, 0.15), 0 32px 64px -20px rgba(22, 24, 27, 0.23);
}
/* brand-tokens:root-light end */


/* ============================================================================
   02. ТОКЕНЫ — ЦВЕТ (тёмная тема)
   Два блока обязательны: системная тема (когда переключателя не трогали)
   и явный [data-theme="dark"] (когда пользователь выбрал руками).
   ========================================================================= */
/* brand-tokens:root-dark-system begin — generated by tools/apply_brand_tokens.py from app/static/brand/tokens.json; do not edit by hand */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    color-scheme: dark;
    --surface-0: #0F1113;
    --surface-1: #1A1D21;
    --surface-2: #22262B;
    --surface-3: #292E34;
    --surface-4: #0A0B0D;
    --surface-inverse: #ECEAE6;
    --text-1: #F5F3EF;
    --text-2: #C6CACF;
    --text-3: #AEB3BA;
    --text-4: #9AA0A8;
    --text-inverse: #16181B;
    --border: #3A3F46;
    --border-subtle: #2A2E34;
    --border-strong: #787F88;
    --link: #FF9C5E;
    --link-hover: #FFB889;
    --focus-ring: #FF8A3D;
    --accent: #FF7A2E;
    --accent-hover: #FF9150;
    --accent-active: #EC6A1F;
    --accent-on: #170A03;
    --accent-bg: #3A2215;
    --accent-fg: #FFA46D;
    --accent-border: #7D401D;
    --fill-hover: rgba(255,255,255,0.06);
    --fill-active: rgba(255,255,255,0.10);
    --fill-selected: rgba(255,122,46,0.16);
    --fill-track: #2A2E34;
    --scrim: rgba(0,0,0,0.66);
    --skeleton-base: #22262B;
    --skeleton-sheen: #2C3137;
    --success: #45B673;
    --success-hover: #59C384;
    --success-active: #38995F;
    --success-on: #04150B;
    --success-bg: #12271B;
    --success-fg: #80D4A3;
    --success-border: #265331;
    --warning: #EAC43C;
    --warning-hover: #F2D058;
    --warning-active: #D0AA25;
    --warning-on: #1A1400;
    --warning-bg: #29230F;
    --warning-fg: #F0D26E;
    --warning-border: #5B4B17;
    --danger: #F2637E;
    --danger-hover: #F57B92;
    --danger-active: #D64F68;
    --danger-on: #1A0509;
    --danger-bg: #2E1419;
    --danger-fg: #F7909F;
    --danger-border: #622633;
    --info: #5CA8D7;
    --info-hover: #75B7E0;
    --info-active: #4791BE;
    --info-on: #04121B;
    --info-bg: #0F2230;
    --info-fg: #92C8E7;
    --info-border: #23495F;
    --chart-work: #FF7A2E;
    --chart-net: #80D4A3;
    --chart-pause: #F0D26E;
    --camera-fill: #FFFFFF;
    --camera-fill-soft: #F3F2EF;
    --camera-ink: #16181B;
    --camera-muted: #474C53;
    --camera-success: #17633A;
    --camera-border: #737A83;
    --terminal-exit-bg: #1A1D21;
    --terminal-exit-on: #F5F3EF;
    --shadow-1: 0 1px 2px rgba(0, 0, 0, 0.5), 0 1px 1px rgba(0, 0, 0, 0.35);
    --shadow-2: 0 2px 4px -1px rgba(0, 0, 0, 0.55), 0 6px 14px -4px rgba(0, 0, 0, 0.5);
    --shadow-3: 0 4px 10px -3px rgba(0, 0, 0, 0.6), 0 16px 32px -10px rgba(0, 0, 0, 0.6);
    --shadow-4: 0 8px 20px -6px rgba(0, 0, 0, 0.65), 0 32px 64px -20px rgba(0, 0, 0, 0.7);
  }
}
/* brand-tokens:root-dark-system end */

/* brand-tokens:root-dark begin — generated by tools/apply_brand_tokens.py from app/static/brand/tokens.json; do not edit by hand */
:root[data-theme="dark"] {
  color-scheme: dark;
  --surface-0: #0F1113;
  --surface-1: #1A1D21;
  --surface-2: #22262B;
  --surface-3: #292E34;
  --surface-4: #0A0B0D;
  --surface-inverse: #ECEAE6;
  --text-1: #F5F3EF;
  --text-2: #C6CACF;
  --text-3: #AEB3BA;
  --text-4: #9AA0A8;
  --text-inverse: #16181B;
  --border: #3A3F46;
  --border-subtle: #2A2E34;
  --border-strong: #787F88;
  --link: #FF9C5E;
  --link-hover: #FFB889;
  --focus-ring: #FF8A3D;
  --accent: #FF7A2E;
  --accent-hover: #FF9150;
  --accent-active: #EC6A1F;
  --accent-on: #170A03;
  --accent-bg: #3A2215;
  --accent-fg: #FFA46D;
  --accent-border: #7D401D;
  --fill-hover: rgba(255,255,255,0.06);
  --fill-active: rgba(255,255,255,0.10);
  --fill-selected: rgba(255,122,46,0.16);
  --fill-track: #2A2E34;
  --scrim: rgba(0,0,0,0.66);
  --skeleton-base: #22262B;
  --skeleton-sheen: #2C3137;
  --success: #45B673;
  --success-hover: #59C384;
  --success-active: #38995F;
  --success-on: #04150B;
  --success-bg: #12271B;
  --success-fg: #80D4A3;
  --success-border: #265331;
  --warning: #EAC43C;
  --warning-hover: #F2D058;
  --warning-active: #D0AA25;
  --warning-on: #1A1400;
  --warning-bg: #29230F;
  --warning-fg: #F0D26E;
  --warning-border: #5B4B17;
  --danger: #F2637E;
  --danger-hover: #F57B92;
  --danger-active: #D64F68;
  --danger-on: #1A0509;
  --danger-bg: #2E1419;
  --danger-fg: #F7909F;
  --danger-border: #622633;
  --info: #5CA8D7;
  --info-hover: #75B7E0;
  --info-active: #4791BE;
  --info-on: #04121B;
  --info-bg: #0F2230;
  --info-fg: #92C8E7;
  --info-border: #23495F;
  --chart-work: #FF7A2E;
  --chart-net: #80D4A3;
  --chart-pause: #F0D26E;
  --camera-fill: #FFFFFF;
  --camera-fill-soft: #F3F2EF;
  --camera-ink: #16181B;
  --camera-muted: #474C53;
  --camera-success: #17633A;
  --camera-border: #737A83;
  --terminal-exit-bg: #1A1D21;
  --terminal-exit-on: #F5F3EF;
  --shadow-1: 0 1px 2px rgba(0, 0, 0, 0.5), 0 1px 1px rgba(0, 0, 0, 0.35);
  --shadow-2: 0 2px 4px -1px rgba(0, 0, 0, 0.55), 0 6px 14px -4px rgba(0, 0, 0, 0.5);
  --shadow-3: 0 4px 10px -3px rgba(0, 0, 0, 0.6), 0 16px 32px -10px rgba(0, 0, 0, 0.6);
  --shadow-4: 0 8px 20px -6px rgba(0, 0, 0, 0.65), 0 32px 64px -20px rgba(0, 0, 0, 0.7);
}
/* brand-tokens:root-dark end */


/* ============================================================================
   03. ТОКЕНЫ — РАЗМЕРЫ, ТИПОГРАФИКА, ТЕНИ, Z-INDEX, ВРЕМЯ
   ========================================================================= */
:root {
  /* --- Шрифты бренда MVG (T025): Onest — текст, Oswald — заголовки и крупные числа,
     JetBrains Mono — коды и номера. Локальные WOFF2 в static/fonts (fonts.css) --- */
  --font-sans: "Onest", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
               "Helvetica Neue", Arial, "Noto Sans", sans-serif;
  --font-display: "Oswald", "Arial Narrow", "Segoe UI", sans-serif;
  --font-mono: "JetBrains Mono", ui-monospace, SFMono-Regular, "SF Mono", Menlo,
               Consolas, "Liberation Mono", monospace;

  /* --- Шкала кеглей: 12 / 13 / 14 / 16 / 20 / 24 / 32.
     Каждому размеру — своя высота строки и трекинг. Крупный текст
     всегда с отрицательным трекингом, мелкий — с положительным. --- */
  --fs-12: 0.75rem;    --lh-12: 1.3333;  --tr-12: 0.01em;    /* мета, подписи, kbd */
  --fs-13: 0.8125rem;  --lh-13: 1.3846;  --tr-13: 0.005em;   /* плотные таблицы, чипы */
  --fs-14: 0.875rem;   --lh-14: 1.4286;  --tr-14: 0em;       /* базовый UI-текст */
  --fs-16: 1rem;       --lh-16: 1.5;     --tr-16: -0.005em;  /* body, длинный текст */
  --fs-20: 1.25rem;    --lh-20: 1.4;     --tr-20: -0.01em;   /* заголовок карточки */
  --fs-24: 1.5rem;     --lh-24: 1.3333;  --tr-24: -0.015em;  /* заголовок раздела */
  --fs-32: 2rem;       --lh-32: 1.25;    --tr-32: -0.02em;   /* заголовок страницы */

  --fw-regular: 400;
  --fw-medium: 500;
  --fw-semibold: 600;
  --fw-bold: 700;

  /* --- Спейсинг: строгая 4px-сетка. Значений вне шкалы в проекте быть не должно --- */
  --sp-0: 0;
  --sp-05: 2px;
  --sp-1: 4px;
  --sp-2: 8px;
  --sp-3: 12px;
  --sp-4: 16px;
  --sp-5: 20px;
  --sp-6: 24px;
  --sp-8: 32px;
  --sp-10: 40px;
  --sp-12: 48px;
  --sp-16: 64px;
  --sp-20: 80px;

  /* --- Радиусы --- */
  --r-xs: 3px;
  --r-sm: 5px;
  --r-md: 8px;    /* кнопки, инпуты, чипы */
  --r-lg: 12px;   /* карточки, модалки, тайлы медиа */
  --r-xl: 16px;
  --r-full: 999px;

  /* --- Обводка фокуса. Единая для всей системы, меняется только здесь --- */
  --focus-w: 2px;
  --focus-offset: 2px;

  /* --- Высоты контролов (переопределяются плотностью, блок 06) --- */
  --control-h-sm: 28px;
  --control-h-md: 36px;
  --control-h-lg: 44px;
  --row-h: 44px;              /* строка таблицы/списка */
  --target-min: 44px;         /* минимальная площадь касания, WCAG 2.5.8 */

  /* --- Ширины --- */
  --container-max: 1440px;
  --container-narrow: 760px;  /* формы и текст: длиннее — глаз теряет строку */
  --sidebar-w: 264px;

  /* --- Z-index: шкала, а не случайные 9999 --- */
  --z-base: 0;
  --z-raised: 10;
  --z-sticky: 100;
  --z-dropdown: 200;
  --z-overlay: 300;
  --z-modal: 400;
  --z-popover: 500;
  --z-toast: 600;
  --z-tooltip: 700;

  /* --- Длительности. Всё, что дольше 320ms в UI-контроле, ощущается тормозом --- */
  --dur-1: 90ms;    /* мгновенная реакция: hover, нажатие */
  --dur-2: 140ms;   /* смена состояния контрола */
  --dur-3: 220ms;   /* появление панели, поповера */
  --dur-4: 320ms;   /* модалка, крупная панель */
  --ease-out: cubic-bezier(0.2, 0.8, 0.3, 1);
  --ease-in-out: cubic-bezier(0.4, 0, 0.2, 1);
  --ease-in: cubic-bezier(0.4, 0, 1, 1);

  /* --- Breakpoints. В медиазапросах переменные не работают — держим тут как
     единственный источник правды и пишем те же числа руками:
       sm 480px | md 768px | lg 1024px | xl 1280px | 2xl 1600px --- */
  --bp-sm: 480px;
  --bp-md: 768px;
  --bp-lg: 1024px;
  --bp-xl: 1280px;
  --bp-2xl: 1600px;
}


/* ============================================================================
   04. RESET И БАЗА
   Минимальный современный reset: без обнуления там, где это вредит доступности.
   ========================================================================= */
*, *::before, *::after { box-sizing: border-box; }

* { margin: 0; }

html {
  -webkit-text-size-adjust: 100%;
  tab-size: 2;
}

body {
  min-block-size: 100vh;
  background: var(--surface-0);
  color: var(--text-1);
  font-family: var(--font-sans);
  font-size: var(--fs-14);
  line-height: var(--lh-14);
  letter-spacing: var(--tr-14);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

img, svg, video, canvas, picture {
  display: block;
  max-inline-size: 100%;
  block-size: auto;
}

input, button, textarea, select {
  font: inherit;
  color: inherit;
  letter-spacing: inherit;
}

button { background: none; border: none; padding: 0; }

a {
  color: var(--link);
  text-decoration-color: color-mix(in srgb, var(--link) 40%, transparent);
  text-underline-offset: 2px;
}
a:hover { color: var(--link-hover); text-decoration-color: currentColor; }

p, h1, h2, h3, h4, h5, h6 { overflow-wrap: break-word; }

ul[class], ol[class] { list-style: none; padding: 0; }

hr {
  border: none;
  border-block-start: 1px solid var(--border-subtle);
  margin-block: var(--sp-4);
}

[hidden] { display: none !important; }

:disabled, [aria-disabled="true"] { cursor: not-allowed; }

::selection { background: var(--accent-bg); color: var(--text-1); }

/* Скроллбары: тонкие, нейтральные, видны в обеих темах */
* { scrollbar-width: thin; scrollbar-color: var(--border-strong) transparent; }
*::-webkit-scrollbar { inline-size: 10px; block-size: 10px; }
*::-webkit-scrollbar-thumb {
  background: var(--border-strong);
  border: 3px solid transparent;
  background-clip: content-box;
  border-radius: var(--r-full);
}
*::-webkit-scrollbar-track { background: transparent; }

/* ЕДИНОЕ КОЛЬЦО ФОКУСА. Клавиатурный фокус виден ВСЕГДА и везде.
   Никогда не писать outline:none без замены — это провал ревью. */
:focus-visible {
  outline: var(--focus-w) solid var(--focus-ring);
  outline-offset: var(--focus-offset);
  border-radius: var(--r-sm);
}
:focus:not(:focus-visible) { outline: none; }


/* ============================================================================
   05. ТИПОГРАФИКА
   Заголовки — классы, не теги: уровень тега выбирается по структуре документа,
   размер — по визуальной роли. Так семантика не ломается ради вида.
   ========================================================================= */
.t-page   { font-size: var(--fs-32); line-height: var(--lh-32); letter-spacing: var(--tr-32); font-weight: var(--fw-semibold); color: var(--text-1); }
.t-section{ font-size: var(--fs-24); line-height: var(--lh-24); letter-spacing: var(--tr-24); font-weight: var(--fw-semibold); color: var(--text-1); }
.t-title  { font-size: var(--fs-20); line-height: var(--lh-20); letter-spacing: var(--tr-20); font-weight: var(--fw-semibold); color: var(--text-1); }
.t-body   { font-size: var(--fs-16); line-height: var(--lh-16); letter-spacing: var(--tr-16); color: var(--text-1); }
.t-ui     { font-size: var(--fs-14); line-height: var(--lh-14); letter-spacing: var(--tr-14); color: var(--text-1); }
.t-dense  { font-size: var(--fs-13); line-height: var(--lh-13); letter-spacing: var(--tr-13); color: var(--text-1); }
.t-meta   { font-size: var(--fs-12); line-height: var(--lh-12); letter-spacing: var(--tr-12); color: var(--text-3); }

/* Надзаголовок-рубрика: только для коротких меток разделов */
.t-overline {
  font-size: var(--fs-12);
  line-height: var(--lh-12);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  font-weight: var(--fw-semibold);
  color: var(--text-3);
}

.text-1 { color: var(--text-1); }
.text-2 { color: var(--text-2); }
.text-3 { color: var(--text-3); }
.text-4 { color: var(--text-4); }
.text-accent  { color: var(--accent-fg); }
.text-success { color: var(--success-fg); }
.text-danger  { color: var(--danger-fg); }
.text-warning { color: var(--warning-fg); }
.text-info    { color: var(--info-fg); }

.fw-regular  { font-weight: var(--fw-regular); }
.fw-medium   { font-weight: var(--fw-medium); }
.fw-semibold { font-weight: var(--fw-semibold); }
.fw-bold     { font-weight: var(--fw-bold); }

/* Цифры в таблицах и метриках — только табличные, иначе колонки «пляшут» */
.num { font-variant-numeric: tabular-nums; font-feature-settings: "tnum" 1; }
.mono { font-family: var(--font-mono); font-size: 0.9em; }

/* Обрезки. line-clamp обязателен для карточек с текстом переменной длины */
.truncate { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-inline-size: 0; }
.clamp-2, .clamp-3 { display: -webkit-box; -webkit-box-orient: vertical; overflow: hidden; }
.clamp-2 { -webkit-line-clamp: 2; }
.clamp-3 { -webkit-line-clamp: 3; }

.measure { max-inline-size: 68ch; }   /* комфортная длина строки для чтения */


/* ============================================================================
   06. LAYOUT, ПЛОТНОСТЬ, УТИЛИТЫ
   ========================================================================= */
.container {
  inline-size: 100%;
  max-inline-size: var(--container-max);
  margin-inline: auto;
  padding-inline: var(--sp-6);
}
.container--narrow { max-inline-size: var(--container-narrow); }
.container--full { max-inline-size: none; }

/* Вертикальный поток с одинаковым шагом. --gap переопределяется на месте. */
.stack { display: flex; flex-direction: column; gap: var(--stack-gap, var(--sp-4)); }
.stack--2 { --stack-gap: var(--sp-2); }
.stack--3 { --stack-gap: var(--sp-3); }
.stack--6 { --stack-gap: var(--sp-6); }
.stack--8 { --stack-gap: var(--sp-8); }

/* Горизонтальная группа с переносом: тулбары, ряды кнопок, списки чипов */
.cluster {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--cluster-gap, var(--sp-2));
}
.cluster--3 { --cluster-gap: var(--sp-3); }
.cluster--4 { --cluster-gap: var(--sp-4); }

/* Ряд без переноса: строка с иконкой и текстом */
.row { display: flex; align-items: center; gap: var(--sp-2); min-inline-size: 0; }
.row--between { justify-content: space-between; }
.row--end { justify-content: flex-end; }
.row--start { align-items: flex-start; }
.grow { flex: 1 1 auto; min-inline-size: 0; }
.shrink-0 { flex: 0 0 auto; }
.spacer { flex: 1 1 auto; }

/* Панель приложения: боковое меню + рабочая область */
.app-shell { display: grid; grid-template-columns: var(--sidebar-w) minmax(0, 1fr); min-block-size: 100vh; }
@media (max-width: 1024px) { .app-shell { grid-template-columns: minmax(0, 1fr); } }

/* Липкая шапка рабочей области (фильтры, действия над списком) */
.toolbar {
  position: sticky;
  inset-block-start: 0;
  z-index: var(--z-sticky);
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  flex-wrap: wrap;
  padding: var(--sp-3) var(--sp-4);
  background: var(--surface-2);
  border-block-end: 1px solid var(--border-subtle);
}

/* ПЛОТНОСТЬ. Ставится на любой контейнер: data-density="compact|comfortable".
   compact — режим оператора: много объектов, решения серийные.
   comfortable — режим разбора: объектов мало, каждое решение весит. */
[data-density="compact"] {
  --control-h-sm: 24px;
  --control-h-md: 30px;
  --control-h-lg: 38px;
  --row-h: 34px;
  --pad-x: var(--sp-2);
  --pad-y: var(--sp-1);
  --stack-gap: var(--sp-2);
  --cluster-gap: var(--sp-2);
  font-size: var(--fs-13);
  line-height: var(--lh-13);
}
[data-density="comfortable"] {
  --control-h-sm: 30px;
  --control-h-md: 38px;
  --control-h-lg: 46px;
  --row-h: 52px;
  --pad-x: var(--sp-4);
  --pad-y: var(--sp-3);
  --stack-gap: var(--sp-5);
  --cluster-gap: var(--sp-3);
}

/* Отступы и выравнивание — короткий набор, не «tailwind в CSS» */
.mt-0{margin-block-start:0}.mt-1{margin-block-start:var(--sp-1)}.mt-2{margin-block-start:var(--sp-2)}
.mt-3{margin-block-start:var(--sp-3)}.mt-4{margin-block-start:var(--sp-4)}.mt-6{margin-block-start:var(--sp-6)}
.mb-1{margin-block-end:var(--sp-1)}.mb-2{margin-block-end:var(--sp-2)}.mb-3{margin-block-end:var(--sp-3)}
.mb-4{margin-block-end:var(--sp-4)}.mb-6{margin-block-end:var(--sp-6)}
.p-2{padding:var(--sp-2)}.p-3{padding:var(--sp-3)}.p-4{padding:var(--sp-4)}.p-6{padding:var(--sp-6)}
.text-left{text-align:left}.text-center{text-align:center}.text-right{text-align:right}
.w-full{inline-size:100%}
.divider { block-size: 1px; background: var(--border-subtle); inline-size: 100%; }
.divider--v { inline-size: 1px; block-size: auto; align-self: stretch; background: var(--border-subtle); }
.scroll-y { overflow-y: auto; overscroll-behavior: contain; }


/* ============================================================================
   07. BUTTON — 4 варианта × 3 размера, все состояния
   primary   — единственное главное действие на экране
   secondary — обычные действия (контурная)
   ghost     — третичные и иконочные, внутри плотных списков
   danger    — необратимое: удалить, отклонить, остановить
   Состояния: :hover :active :focus-visible :disabled .is-loading [aria-pressed]
   ========================================================================= */
.btn {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--sp-2);
  block-size: var(--control-h-md);
  padding-inline: var(--sp-4);
  border: 1px solid transparent;
  border-radius: var(--r-md);
  font-size: var(--fs-14);
  line-height: 1;
  font-weight: var(--fw-medium);
  letter-spacing: var(--tr-14);
  white-space: nowrap;
  cursor: pointer;
  user-select: none;
  text-decoration: none;
  transition:
    background-color var(--dur-1) var(--ease-out),
    border-color var(--dur-1) var(--ease-out),
    color var(--dur-1) var(--ease-out),
    box-shadow var(--dur-1) var(--ease-out),
    transform var(--dur-1) var(--ease-out);
}
.btn:active { transform: translateY(0.5px); }
.btn:focus-visible { outline: var(--focus-w) solid var(--focus-ring); outline-offset: var(--focus-offset); }

/* Иконка внутри кнопки: фиксированный бокс, чтобы текст не «прыгал» */
.btn svg, .btn .icon { inline-size: 16px; block-size: 16px; flex: 0 0 auto; }

/* --- primary --- */
.btn--primary { background: var(--accent); color: var(--accent-on); border-color: var(--accent); box-shadow: var(--shadow-1); }
.btn--primary:hover:not(:disabled):not(.is-loading) { background: var(--accent-hover); border-color: var(--accent-hover); }
.btn--primary:active:not(:disabled) { background: var(--accent-active); border-color: var(--accent-active); box-shadow: none; }

/* --- secondary --- */
.btn--secondary { background: var(--surface-1); color: var(--text-1); border-color: var(--border); box-shadow: var(--shadow-1); }
.btn--secondary:hover:not(:disabled):not(.is-loading) { background: var(--surface-2); border-color: var(--border-strong); }
.btn--secondary:active:not(:disabled) { background: var(--fill-active); box-shadow: none; }

/* --- ghost --- */
.btn--ghost { background: transparent; color: var(--text-2); border-color: transparent; }
.btn--ghost:hover:not(:disabled):not(.is-loading) { background: var(--fill-hover); color: var(--text-1); }
.btn--ghost:active:not(:disabled) { background: var(--fill-active); }

/* --- danger --- */
.btn--danger { background: var(--danger); color: var(--danger-on); border-color: var(--danger); box-shadow: var(--shadow-1); }
.btn--danger:hover:not(:disabled):not(.is-loading) { background: var(--danger-hover); border-color: var(--danger-hover); }
.btn--danger:active:not(:disabled) { background: var(--danger-active); border-color: var(--danger-active); box-shadow: none; }
/* Мягкий вариант опасного действия — в списках, где кнопок много */
.btn--danger-quiet { background: transparent; color: var(--danger-fg); border-color: transparent; }
.btn--danger-quiet:hover:not(:disabled) { background: var(--danger-bg); }

/* --- Размеры. sm/md/lg = вес действия, не «чтобы влезло» --- */
.btn--sm { block-size: var(--control-h-sm); padding-inline: var(--sp-3); font-size: var(--fs-13); border-radius: var(--r-sm); gap: var(--sp-1); }
.btn--md { block-size: var(--control-h-md); }
.btn--lg { block-size: var(--control-h-lg); padding-inline: var(--sp-6); font-size: var(--fs-16); border-radius: var(--r-md); }
.btn--lg svg, .btn--lg .icon { inline-size: 18px; block-size: 18px; }

/* Квадратная иконочная кнопка */
.btn--icon { padding-inline: 0; inline-size: var(--control-h-md); }
.btn--icon.btn--sm { inline-size: var(--control-h-sm); }
.btn--icon.btn--lg { inline-size: var(--control-h-lg); }

/* Кнопка на всю ширину — для форм и мобильных футеров */
.btn--block { inline-size: 100%; }

/* --- disabled: гасим цвет и тень, НЕ прячем элемент --- */
.btn:disabled, .btn[aria-disabled="true"] {
  background: var(--surface-2);
  color: var(--text-4);
  border-color: var(--border-subtle);
  box-shadow: none;
  cursor: not-allowed;
  transform: none;
}
.btn--ghost:disabled { background: transparent; }

/* --- loading: ширина кнопки НЕ меняется, спиннер поверх, клики блокируются.
   Разметка: <button class="btn btn--primary is-loading" aria-busy="true">
               <span class="btn__label">Запустить</span></button> --- */
.btn.is-loading { pointer-events: none; color: transparent; }
.btn.is-loading .btn__label, .btn.is-loading svg, .btn.is-loading .icon { visibility: hidden; }
.btn.is-loading::after {
  content: "";
  position: absolute;
  inline-size: 16px;
  block-size: 16px;
  border: 2px solid currentColor;
  border-block-start-color: transparent;
  border-radius: var(--r-full);
  color: var(--accent-on);
  animation: ds-spin 700ms linear infinite;
}
.btn--secondary.is-loading::after, .btn--ghost.is-loading::after { color: var(--text-2); }

/* Кнопка-переключатель (фильтр, режим) — состояние читается без цвета текста */
.btn[aria-pressed="true"] {
  background: var(--fill-selected);
  color: var(--accent-fg);
  border-color: var(--accent-border);
}

/* Группа кнопок (сегментированный переключатель режимов) */
.btn-group { display: inline-flex; }
.btn-group .btn { border-radius: 0; margin-inline-start: -1px; }
.btn-group .btn:first-child { border-start-start-radius: var(--r-md); border-end-start-radius: var(--r-md); margin-inline-start: 0; }
.btn-group .btn:last-child { border-start-end-radius: var(--r-md); border-end-end-radius: var(--r-md); }
.btn-group .btn:focus-visible, .btn-group .btn[aria-pressed="true"] { z-index: 1; }

/* Гарантия площади касания 44px для мелких контролов: невидимая зона вокруг */
.btn--sm, .btn--icon.btn--sm, .hit-target { position: relative; }
.btn--sm::before, .btn--icon.btn--sm::before, .hit-target::before {
  content: "";
  position: absolute;
  inset: 50% 50%;
  inline-size: max(100%, var(--target-min));
  block-size: max(100%, var(--target-min));
  transform: translate(-50%, -50%);
}


/* ============================================================================
   08. ФОРМЫ — input / textarea / select / checkbox / radio / switch
   Поле = .field (обёртка) + .label + контрол + .field__hint | .field__error.
   Подсказка всегда есть, ошибка появляется рядом с полем, а не всплывашкой.
   ========================================================================= */
.field { display: flex; flex-direction: column; gap: var(--sp-1); min-inline-size: 0; }

.label {
  font-size: var(--fs-13);
  line-height: var(--lh-13);
  font-weight: var(--fw-medium);
  color: var(--text-2);
  display: inline-flex;
  align-items: center;
  gap: var(--sp-1);
}
.label--required::after { content: "*"; color: var(--danger-fg); }
.label__optional { color: var(--text-4); font-weight: var(--fw-regular); }

.input, .textarea, .select {
  inline-size: 100%;
  min-block-size: var(--control-h-md);
  padding: 0 var(--sp-3);
  background: var(--surface-1);
  color: var(--text-1);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  font-size: var(--fs-14);
  line-height: var(--lh-14);
  transition: border-color var(--dur-1) var(--ease-out), box-shadow var(--dur-1) var(--ease-out), background-color var(--dur-1) var(--ease-out);
}
.textarea { padding: var(--sp-2) var(--sp-3); min-block-size: 88px; resize: vertical; line-height: var(--lh-16); }

.input::placeholder, .textarea::placeholder { color: var(--text-4); }
.input:hover:not(:disabled), .textarea:hover:not(:disabled), .select:hover:not(:disabled) { border-color: var(--border-strong); }
.input:focus, .textarea:focus, .select:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-bg);
}
.input:focus-visible, .textarea:focus-visible, .select:focus-visible { outline: none; }

.input:disabled, .textarea:disabled, .select:disabled {
  background: var(--surface-4);
  color: var(--text-4);
  border-color: var(--border-subtle);
  cursor: not-allowed;
}
.input[readonly], .textarea[readonly] { background: var(--surface-2); color: var(--text-2); }

/* Ошибка: красная граница + текст. Только граница — недостаточно (цветовая слепота) */
.input[aria-invalid="true"], .textarea[aria-invalid="true"], .select[aria-invalid="true"],
.is-invalid .input, .is-invalid .textarea, .is-invalid .select {
  border-color: var(--danger);
  background: var(--danger-bg);
}
.input[aria-invalid="true"]:focus, .is-invalid .input:focus { box-shadow: 0 0 0 3px var(--danger-bg); }

.field__hint { font-size: var(--fs-12); line-height: var(--lh-12); color: var(--text-3); }
.field__error {
  display: flex;
  align-items: flex-start;
  gap: var(--sp-1);
  font-size: var(--fs-12);
  line-height: var(--lh-12);
  color: var(--danger-fg);
  font-weight: var(--fw-medium);
}
.field__error::before { content: "!"; flex: 0 0 auto; inline-size: 14px; block-size: 14px; border-radius: var(--r-full); background: var(--danger); color: var(--danger-on); font-size: 10px; line-height: 14px; text-align: center; font-weight: var(--fw-bold); }

/* Размеры полей — те же три ступени, что у кнопок */
.input--sm, .select--sm { min-block-size: var(--control-h-sm); font-size: var(--fs-13); padding-inline: var(--sp-2); }
.input--lg, .select--lg { min-block-size: var(--control-h-lg); font-size: var(--fs-16); }

/* Select: свой значок, чтобы не отличался от инпута между браузерами */
.select {
  appearance: none;
  padding-inline-end: var(--sp-8);
  background-image: linear-gradient(45deg, transparent 50%, currentColor 50%), linear-gradient(135deg, currentColor 50%, transparent 50%);
  background-position: calc(100% - 17px) calc(50% + 1px), calc(100% - 12px) calc(50% + 1px);
  background-size: 5px 5px, 5px 5px;
  background-repeat: no-repeat;
  cursor: pointer;
}

/* Поле с иконкой/кнопкой внутри (поиск, очистка) */
.input-group { position: relative; display: flex; align-items: center; }
.input-group .input { padding-inline-start: var(--sp-8); }
.input-group__icon {
  position: absolute;
  inset-inline-start: var(--sp-3);
  inline-size: 16px; block-size: 16px;
  color: var(--text-3);
  pointer-events: none;
}
.input-group__action { position: absolute; inset-inline-end: var(--sp-1); }

/* --- Checkbox и Radio: свой бокс, потому что нативный не даёт состояний --- */
.choice {
  position: relative;
  display: inline-flex;
  align-items: flex-start;
  gap: var(--sp-2);
  min-block-size: var(--control-h-md);
  padding-block: var(--sp-1);
  cursor: pointer;
  font-size: var(--fs-14);
  color: var(--text-1);
}
.choice input[type="checkbox"], .choice input[type="radio"] {
  appearance: none;
  flex: 0 0 auto;
  inline-size: 18px;
  block-size: 18px;
  margin: 1px 0 0;
  background: var(--surface-1);
  border: 1px solid var(--border-strong);
  border-radius: var(--r-xs);
  cursor: pointer;
  display: grid;
  place-content: center;
  transition: background-color var(--dur-1) var(--ease-out), border-color var(--dur-1) var(--ease-out);
}
.choice input[type="radio"] { border-radius: var(--r-full); }
.choice:hover input:not(:disabled) { border-color: var(--accent); }

.choice input[type="checkbox"]::before {
  content: "";
  inline-size: 10px; block-size: 6px;
  border: 2px solid var(--accent-on);
  border-block-start: none; border-inline-end: none;
  transform: rotate(-45deg) translate(1px, -1px) scale(0.6);
  opacity: 0;
  transition: opacity var(--dur-1) var(--ease-out), transform var(--dur-1) var(--ease-out);
}
.choice input[type="checkbox"]:checked { background: var(--accent); border-color: var(--accent); }
.choice input[type="checkbox"]:checked::before { opacity: 1; transform: rotate(-45deg) translate(1px, -1px) scale(1); }
.choice input[type="checkbox"]:indeterminate { background: var(--accent); border-color: var(--accent); }
.choice input[type="checkbox"]:indeterminate::before {
  content: ""; opacity: 1; inline-size: 9px; block-size: 2px;
  background: var(--accent-on); border: none; transform: none;
}
.choice input[type="radio"]::before {
  content: ""; inline-size: 8px; block-size: 8px; border-radius: var(--r-full);
  background: var(--accent-on); transform: scale(0); transition: transform var(--dur-1) var(--ease-out);
}
.choice input[type="radio"]:checked { background: var(--accent); border-color: var(--accent); }
.choice input[type="radio"]:checked::before { transform: scale(1); }

.choice input:focus-visible { outline: var(--focus-w) solid var(--focus-ring); outline-offset: var(--focus-offset); }
.choice input:disabled { background: var(--surface-4); border-color: var(--border-subtle); cursor: not-allowed; }
.choice:has(input:disabled) { color: var(--text-4); cursor: not-allowed; }
.choice__hint { display: block; font-size: var(--fs-12); color: var(--text-3); margin-block-start: var(--sp-05); }

/* --- Switch: включает режим НЕМЕДЛЕННО (без «Сохранить»). Если нужен submit —
   это checkbox, а не switch. Подпись обязана называть последствие. --- */
.switch { position: relative; display: inline-flex; align-items: center; gap: var(--sp-2); cursor: pointer; min-block-size: var(--control-h-md); }
.switch input {
  appearance: none;
  inline-size: 36px; block-size: 20px;
  background: var(--fill-track);
  border: 1px solid var(--border);
  border-radius: var(--r-full);
  cursor: pointer;
  transition: background-color var(--dur-2) var(--ease-out), border-color var(--dur-2) var(--ease-out);
}
.switch input::before {
  content: "";
  position: absolute;
  inset-block-start: 50%;
  inset-inline-start: 3px;
  inline-size: 14px; block-size: 14px;
  background: var(--surface-1);
  border-radius: var(--r-full);
  box-shadow: var(--shadow-1);
  transform: translateY(-50%);
  transition: transform var(--dur-2) var(--ease-out);
}
.switch input:checked { background: var(--accent); border-color: var(--accent); }
.switch input:checked::before { transform: translate(16px, -50%); }
.switch input:focus-visible { outline: var(--focus-w) solid var(--focus-ring); outline-offset: var(--focus-offset); }
.switch input:disabled { opacity: 0.5; cursor: not-allowed; }

/* Пояснение выбора: техническое слово + последствие для человека.
   «Качественно» → «Высокое качество · медленнее, ~4 мин». Второе — .option__meta */
.option-list { display: flex; flex-direction: column; gap: var(--sp-1); }
.option {
  display: flex; align-items: flex-start; gap: var(--sp-3);
  padding: var(--sp-3);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  background: var(--surface-1);
  cursor: pointer;
  transition: border-color var(--dur-1) var(--ease-out), background-color var(--dur-1) var(--ease-out);
}
.option:hover { border-color: var(--border-strong); background: var(--surface-2); }
.option:has(input:checked) { border-color: var(--accent); background: var(--accent-bg); }
.option__title { font-weight: var(--fw-medium); color: var(--text-1); }
.option__meta { display: block; font-size: var(--fs-12); color: var(--text-3); margin-block-start: var(--sp-05); }

/* Футер формы: действия всегда справа, отмена — слева от подтверждения */
.form-actions {
  display: flex; align-items: center; justify-content: flex-end; gap: var(--sp-2);
  padding-block-start: var(--sp-4);
  border-block-start: 1px solid var(--border-subtle);
}


/* ============================================================================
   09. CARD — контейнер объекта. Три части: header / body / footer.
   .card--interactive только если карточка целиком кликабельна (иначе hover врёт).
   ========================================================================= */
.card {
  display: flex;
  flex-direction: column;
  background: var(--surface-1);
  border: 1px solid var(--border-subtle);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-1);
  overflow: clip;
}
.card__header {
  display: flex; align-items: center; gap: var(--sp-3);
  padding: var(--sp-4);
  border-block-end: 1px solid var(--border-subtle);
  background: var(--surface-1);
}
.card__title { font-size: var(--fs-16); font-weight: var(--fw-semibold); line-height: var(--lh-16); color: var(--text-1); }
.card__subtitle { font-size: var(--fs-12); color: var(--text-3); margin-block-start: var(--sp-05); }
.card__body { padding: var(--sp-4); flex: 1 1 auto; min-inline-size: 0; }
.card__body--flush { padding: 0; }               /* когда внутри таблица или медиа */
.card__footer {
  display: flex; align-items: center; gap: var(--sp-2);
  padding: var(--sp-3) var(--sp-4);
  border-block-start: 1px solid var(--border-subtle);
  background: var(--surface-2);
}
.card--interactive { cursor: pointer; transition: box-shadow var(--dur-2) var(--ease-out), border-color var(--dur-2) var(--ease-out), transform var(--dur-2) var(--ease-out); }
.card--interactive:hover { box-shadow: var(--shadow-2); border-color: var(--border); }
.card--interactive:active { transform: translateY(1px); box-shadow: var(--shadow-1); }
.card--interactive:focus-visible { outline: var(--focus-w) solid var(--focus-ring); outline-offset: var(--focus-offset); }
.card.is-selected { border-color: var(--accent); box-shadow: 0 0 0 1px var(--accent), var(--shadow-2); }
.card.is-disabled { opacity: 0.55; pointer-events: none; }


/* ============================================================================
   10. BADGE / CHIP
   badge — статус объекта (только чтение). chip — фильтр или выбор (кликается).
   Разные роли — разный вид: путать их нельзя.
   ========================================================================= */
.badge {
  display: inline-flex; align-items: center; gap: var(--sp-1);
  padding: 2px var(--sp-2);
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
  background: var(--surface-2);
  color: var(--text-2);
  font-size: var(--fs-12);
  line-height: var(--lh-12);
  font-weight: var(--fw-medium);
  white-space: nowrap;
}
.badge--accent  { background: var(--accent-bg);  border-color: var(--accent-border);  color: var(--accent-fg); }
.badge--success { background: var(--success-bg); border-color: var(--success-border); color: var(--success-fg); }
.badge--danger  { background: var(--danger-bg);  border-color: var(--danger-border);  color: var(--danger-fg); }
.badge--warning { background: var(--warning-bg); border-color: var(--warning-border); color: var(--warning-fg); }
.badge--info    { background: var(--info-bg);    border-color: var(--info-border);    color: var(--info-fg); }
.badge--solid   { background: var(--accent); border-color: var(--accent); color: var(--accent-on); }
/* Точка-статус перед текстом — читается даже при цветовой слепоте вместе со словом */
.badge__dot { inline-size: 6px; block-size: 6px; border-radius: var(--r-full); background: currentColor; flex: 0 0 auto; }
/* Счётчик на иконке */
.badge--count { min-inline-size: 18px; block-size: 18px; padding-inline: 5px; justify-content: center; border-radius: var(--r-full); font-variant-numeric: tabular-nums; }

.chip {
  position: relative;
  display: inline-flex; align-items: center; gap: var(--sp-1);
  block-size: 28px;
  padding-inline: var(--sp-3);
  border: 1px solid var(--border);
  border-radius: var(--r-full);
  background: var(--surface-1);
  color: var(--text-2);
  font-size: var(--fs-13);
  cursor: pointer;
  white-space: nowrap;
  transition: background-color var(--dur-1) var(--ease-out), border-color var(--dur-1) var(--ease-out), color var(--dur-1) var(--ease-out);
}
.chip:hover:not(:disabled) { background: var(--surface-2); border-color: var(--border-strong); color: var(--text-1); }
.chip:active:not(:disabled) { background: var(--fill-active); }
.chip:focus-visible { outline: var(--focus-w) solid var(--focus-ring); outline-offset: var(--focus-offset); }
.chip[aria-pressed="true"], .chip.is-selected {
  background: var(--accent-bg); border-color: var(--accent-border); color: var(--accent-fg); font-weight: var(--fw-medium);
}
.chip:disabled { background: var(--surface-2); color: var(--text-4); border-color: var(--border-subtle); cursor: not-allowed; }
.chip__remove {
  inline-size: 16px; block-size: 16px;
  display: grid; place-content: center;
  border-radius: var(--r-full);
  color: var(--text-3);
  cursor: pointer;
  position: relative;
}
.chip__remove:hover { background: var(--fill-active); color: var(--text-1); }
.chip__count { color: var(--text-3); font-variant-numeric: tabular-nums; }


/* ============================================================================
   11. TABLE — липкая шапка, подсветка строки, выделение, числовые колонки
   Обёртка .table-wrap обязательна: она даёт горизонтальный скролл и
   ограничивает высоту, иначе sticky-шапке не за что цепляться.
   ========================================================================= */
.table-wrap {
  inline-size: 100%;
  max-block-size: var(--table-max-h, 70vh);
  overflow: auto;
  overscroll-behavior: contain;
  border: 1px solid var(--border-subtle);
  border-radius: var(--r-lg);
  background: var(--surface-1);
}
.table { inline-size: 100%; border-collapse: separate; border-spacing: 0; font-size: var(--fs-14); }

.table thead th {
  position: sticky;
  inset-block-start: 0;
  z-index: var(--z-raised);
  background: var(--surface-2);
  color: var(--text-3);
  font-size: var(--fs-12);
  font-weight: var(--fw-semibold);
  letter-spacing: 0.03em;
  text-transform: uppercase;
  text-align: start;
  padding: var(--sp-2) var(--sp-3);
  border-block-end: 1px solid var(--border);
  white-space: nowrap;
}
.table tbody td {
  padding: 0 var(--sp-3);
  block-size: var(--row-h);
  border-block-end: 1px solid var(--border-subtle);
  color: var(--text-1);
  vertical-align: middle;
}
.table tbody tr:last-child td { border-block-end: none; }
.table tbody tr { transition: background-color var(--dur-1) var(--ease-out); }
.table tbody tr:hover { background: var(--fill-hover); }
.table tbody tr.is-selected { background: var(--fill-selected); }
.table tbody tr.is-selected td { border-block-end-color: var(--accent-border); }
.table tbody tr[aria-disabled="true"] { color: var(--text-4); }
.table tbody tr:focus-visible { outline: var(--focus-w) solid var(--focus-ring); outline-offset: -2px; }

/* Числовые колонки — вправо и табличными цифрами, иначе сравнивать невозможно */
.table .num, .table th.num { text-align: end; font-variant-numeric: tabular-nums; }
/* Липкая первая колонка для широких таблиц */
.table--sticky-first tbody td:first-child,
.table--sticky-first thead th:first-child {
  position: sticky; inset-inline-start: 0; z-index: 1; background: var(--surface-1);
  box-shadow: 1px 0 0 var(--border-subtle);
}
.table--sticky-first thead th:first-child { z-index: calc(var(--z-raised) + 1); background: var(--surface-2); }
/* Плотный режим таблицы — для операторских списков */
.table--compact tbody td { block-size: 32px; font-size: var(--fs-13); padding-inline: var(--sp-2); }
.table--compact thead th { padding-block: var(--sp-1); }
/* Заголовок с сортировкой */
.table th[aria-sort] { cursor: pointer; user-select: none; }
.table th[aria-sort]:hover { color: var(--text-1); background: var(--fill-hover); }
.table th[aria-sort="ascending"]::after { content: " ↑"; color: var(--accent-fg); }
.table th[aria-sort="descending"]::after { content: " ↓"; color: var(--accent-fg); }
/* Колонка действий: показываем на hover строки, но НЕ скрываем от клавиатуры */
.table .row-actions { opacity: 0; transition: opacity var(--dur-1) var(--ease-out); }
.table tr:hover .row-actions,
.table tr:focus-within .row-actions { opacity: 1; }


/* ============================================================================
   12. MODAL / TOAST / TOOLTIP / POPOVER
   Модалка = <dialog class="modal">. Высота ограничена, скроллится ТЕЛО,
   шапка и футер с действиями всегда видны — иначе кнопка «Сохранить» уезжает.
   ========================================================================= */
.modal {
  inline-size: min(var(--modal-w, 560px), calc(100vw - var(--sp-8)));
  max-block-size: min(85vh, 900px);
  padding: 0;
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  background: var(--surface-3);
  color: var(--text-1);
  box-shadow: var(--shadow-4);
  overflow: hidden;
  display: flex;
  flex-direction: column;
}
.modal::backdrop { background: var(--scrim); }
.modal[open] { animation: ds-modal-in var(--dur-3) var(--ease-out); }
.modal--wide { --modal-w: 880px; }
.modal--review { --modal-w: 1200px; max-block-size: 92vh; }  /* экраны сравнения медиа */

.modal__header {
  display: flex; align-items: flex-start; gap: var(--sp-3);
  padding: var(--sp-4) var(--sp-5);
  border-block-end: 1px solid var(--border-subtle);
  flex: 0 0 auto;
}
.modal__title { font-size: var(--fs-20); font-weight: var(--fw-semibold); line-height: var(--lh-20); }
.modal__desc { font-size: var(--fs-13); color: var(--text-3); margin-block-start: var(--sp-1); }
.modal__body { padding: var(--sp-5); overflow-y: auto; overscroll-behavior: contain; flex: 1 1 auto; }
.modal__footer {
  display: flex; align-items: center; justify-content: flex-end; gap: var(--sp-2);
  padding: var(--sp-3) var(--sp-5);
  border-block-start: 1px solid var(--border-subtle);
  background: var(--surface-2);
  flex: 0 0 auto;
}
.modal__close { margin-inline-start: auto; }
@media (max-width: 640px) {
  .modal { inline-size: 100vw; max-inline-size: 100vw; max-block-size: 100dvh; block-size: 100dvh; border-radius: 0; border: none; }
}

/* --- Toast: результат фонового действия + ОТМЕНА. Без undo система хрупкая --- */
.toast-region {
  position: fixed;
  inset-block-end: var(--sp-5);
  inset-inline-end: var(--sp-5);
  z-index: var(--z-toast);
  display: flex; flex-direction: column; gap: var(--sp-2);
  inline-size: min(400px, calc(100vw - var(--sp-8)));
  pointer-events: none;
}
.toast {
  pointer-events: auto;
  display: flex; align-items: flex-start; gap: var(--sp-3);
  padding: var(--sp-3) var(--sp-4);
  background: var(--surface-3);
  border: 1px solid var(--border);
  border-inline-start: 3px solid var(--text-3);
  border-radius: var(--r-md);
  box-shadow: var(--shadow-3);
  animation: ds-toast-in var(--dur-3) var(--ease-out);
}
.toast--success { border-inline-start-color: var(--success); }
.toast--danger  { border-inline-start-color: var(--danger); }
.toast--warning { border-inline-start-color: var(--warning); }
.toast--info    { border-inline-start-color: var(--info); }
.toast__title { font-weight: var(--fw-medium); font-size: var(--fs-14); }
.toast__text { font-size: var(--fs-13); color: var(--text-2); margin-block-start: var(--sp-05); }
.toast__actions { margin-inline-start: auto; display: flex; gap: var(--sp-1); flex: 0 0 auto; }
.toast.is-leaving { animation: ds-toast-out var(--dur-2) var(--ease-in) forwards; }

/* --- Tooltip: только пояснение, никогда не единственный носитель смысла.
   Разметка: <button class="btn" data-tooltip="Пересобрать превью">…</button> --- */
[data-tooltip] { position: relative; }
[data-tooltip]::after {
  content: attr(data-tooltip);
  position: absolute;
  inset-block-end: calc(100% + 6px);
  inset-inline-start: 50%;
  transform: translateX(-50%);
  z-index: var(--z-tooltip);
  padding: var(--sp-1) var(--sp-2);
  background: var(--surface-inverse);
  color: var(--text-inverse);
  border-radius: var(--r-sm);
  font-size: var(--fs-12);
  line-height: var(--lh-12);
  white-space: nowrap;
  opacity: 0;
  pointer-events: none;
  transition: opacity var(--dur-2) var(--ease-out) 400ms;
  box-shadow: var(--shadow-2);
}
[data-tooltip]:hover::after, [data-tooltip]:focus-visible::after { opacity: 1; }
[data-tooltip][data-tooltip-side="bottom"]::after { inset-block-end: auto; inset-block-start: calc(100% + 6px); }

/* --- Popover / выпадающая панель. Позиционируется JS или .popover-anchor --- */
.popover-anchor { position: relative; }
.popover {
  position: absolute;
  z-index: var(--z-popover);
  min-inline-size: 220px;
  max-inline-size: 360px;
  padding: var(--sp-2);
  background: var(--surface-3);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  box-shadow: var(--shadow-3);
  animation: ds-pop-in var(--dur-2) var(--ease-out);
}
.popover[hidden] { display: none; }
.menu { display: flex; flex-direction: column; gap: 1px; }
.menu__item {
  display: flex; align-items: center; gap: var(--sp-2);
  min-block-size: 32px;
  padding: 0 var(--sp-2);
  border-radius: var(--r-sm);
  color: var(--text-1);
  font-size: var(--fs-14);
  text-align: start;
  cursor: pointer;
  inline-size: 100%;
}
.menu__item:hover { background: var(--fill-hover); }
.menu__item[aria-selected="true"] { background: var(--fill-selected); color: var(--accent-fg); }
.menu__item:disabled, .menu__item[aria-disabled="true"] { color: var(--text-4); cursor: not-allowed; background: none; }
.menu__item--danger { color: var(--danger-fg); }
.menu__item--danger:hover { background: var(--danger-bg); }
.menu__sep { block-size: 1px; background: var(--border-subtle); margin-block: var(--sp-1); }
.menu__shortcut { margin-inline-start: auto; color: var(--text-3); font-size: var(--fs-12); }


/* ============================================================================
   13. TABS / STEPPER / PROGRESS
   ========================================================================= */
.tabs {
  display: flex; align-items: center; gap: var(--sp-1);
  border-block-end: 1px solid var(--border-subtle);
  overflow-x: auto;
  scrollbar-width: none;
}
.tabs::-webkit-scrollbar { display: none; }
.tab {
  position: relative;
  display: inline-flex; align-items: center; gap: var(--sp-2);
  block-size: var(--control-h-lg);
  padding-inline: var(--sp-3);
  color: var(--text-2);
  font-size: var(--fs-14);
  font-weight: var(--fw-medium);
  white-space: nowrap;
  cursor: pointer;
  border-block-end: 2px solid transparent;
  margin-block-end: -1px;
  transition: color var(--dur-1) var(--ease-out), border-color var(--dur-1) var(--ease-out), background-color var(--dur-1) var(--ease-out);
}
.tab:hover { color: var(--text-1); background: var(--fill-hover); }
.tab[aria-selected="true"] { color: var(--accent-fg); border-block-end-color: var(--accent); }
.tab:disabled, .tab[aria-disabled="true"] { color: var(--text-4); cursor: not-allowed; background: none; }
.tab:focus-visible { outline: var(--focus-w) solid var(--focus-ring); outline-offset: -2px; }
.tab__count { color: var(--text-3); font-variant-numeric: tabular-nums; font-weight: var(--fw-regular); }
.tabpanel:focus-visible { outline: var(--focus-w) solid var(--focus-ring); outline-offset: var(--focus-offset); }

/* --- Stepper: где я в многошаговом процессе. Состояния: done / current / todo --- */
.stepper { display: flex; align-items: flex-start; gap: var(--sp-2); }
.step { display: flex; align-items: center; gap: var(--sp-2); flex: 1 1 0; min-inline-size: 0; }
.step__marker {
  flex: 0 0 auto;
  inline-size: 24px; block-size: 24px;
  display: grid; place-content: center;
  border-radius: var(--r-full);
  border: 1px solid var(--border);
  background: var(--surface-1);
  color: var(--text-3);
  font-size: var(--fs-12);
  font-weight: var(--fw-semibold);
  font-variant-numeric: tabular-nums;
}
.step__label { font-size: var(--fs-13); color: var(--text-3); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.step__line { block-size: 1px; background: var(--border); flex: 1 1 auto; }
.step.is-done .step__marker { background: var(--success); border-color: var(--success); color: var(--success-on); }
.step.is-done .step__marker::before { content: "✓"; }
.step.is-done .step__label { color: var(--text-2); }
.step.is-done .step__line { background: var(--success-border); }
.step.is-current .step__marker { border-color: var(--accent); color: var(--accent-fg); background: var(--accent-bg); box-shadow: 0 0 0 3px var(--accent-bg); }
.step.is-current .step__label { color: var(--text-1); font-weight: var(--fw-medium); }
.step.is-error .step__marker { background: var(--danger); border-color: var(--danger); color: var(--danger-on); }
.step.is-error .step__label { color: var(--danger-fg); }

/* --- Progress. Определённый прогресс всегда лучше неопределённого:
   если знаешь долю — показывай долю и остаток времени, не «крутилку» --- */
.progress {
  inline-size: 100%;
  block-size: 6px;
  background: var(--fill-track);
  border-radius: var(--r-full);
  overflow: hidden;
}
.progress__bar {
  block-size: 100%;
  inline-size: var(--value, 0%);
  background: var(--accent);
  border-radius: inherit;
  transition: inline-size var(--dur-4) var(--ease-out);
}
.progress--success .progress__bar { background: var(--success); }
.progress--danger .progress__bar { background: var(--danger); }
.progress--lg { block-size: 10px; }
.progress--indeterminate .progress__bar {
  inline-size: 35%;
  animation: ds-progress-slide 1.2s var(--ease-in-out) infinite;
}
.progress-label { display: flex; justify-content: space-between; gap: var(--sp-2); font-size: var(--fs-12); color: var(--text-3); margin-block-end: var(--sp-1); }


/* ============================================================================
   14. SKELETON / SPINNER / EMPTY / KBD
   Скелет обязан повторять ФОРМУ будущего контента, включая пропорции медиа,
   иначе после загрузки страница «прыгает» (CLS) и выглядит дёшево.
   ========================================================================= */
.skeleton {
  background: linear-gradient(90deg, var(--skeleton-base) 25%, var(--skeleton-sheen) 37%, var(--skeleton-base) 63%);
  background-size: 400% 100%;
  animation: ds-skeleton 1.4s var(--ease-in-out) infinite;
  border-radius: var(--r-sm);
}
.skeleton--text { block-size: 12px; margin-block: 4px; }
.skeleton--text.w-60 { inline-size: 60%; }
.skeleton--text.w-40 { inline-size: 40%; }
.skeleton--title { block-size: 18px; inline-size: 45%; border-radius: var(--r-sm); }
.skeleton--avatar { inline-size: 32px; block-size: 32px; border-radius: var(--r-full); }
.skeleton--btn { block-size: var(--control-h-md); inline-size: 96px; border-radius: var(--r-md); }
/* Медиа-скелет: пропорция задаётся классом aspect-ratio (блок 15), не пикселями */
.skeleton--media { inline-size: 100%; block-size: 100%; border-radius: var(--r-lg); }
.skeleton--9x16 { aspect-ratio: 9 / 16; inline-size: 100%; border-radius: var(--r-lg); }
.skeleton--16x9 { aspect-ratio: 16 / 9; inline-size: 100%; border-radius: var(--r-lg); }
.skeleton--1x1  { aspect-ratio: 1 / 1;  inline-size: 100%; border-radius: var(--r-lg); }

.spinner {
  display: inline-block;
  inline-size: 16px; block-size: 16px;
  border: 2px solid var(--border);
  border-block-start-color: var(--accent);
  border-radius: var(--r-full);
  animation: ds-spin 700ms linear infinite;
}
.spinner--sm { inline-size: 12px; block-size: 12px; border-width: 1.5px; }
.spinner--lg { inline-size: 24px; block-size: 24px; border-width: 3px; }
/* Блокирующая загрузка области: контент виден, но заблокирован */
.is-busy { position: relative; pointer-events: none; }
.is-busy::after {
  content: "";
  position: absolute; inset: 0;
  background: var(--surface-1);
  opacity: 0.6;
  border-radius: inherit;
}

/* --- Empty state: три обязательные части — что произошло, почему, что сделать.
   Пустой экран без кнопки действия = тупик для пользователя. --- */
.empty {
  display: flex; flex-direction: column; align-items: center; text-align: center;
  gap: var(--sp-3);
  padding: var(--sp-12) var(--sp-6);
  color: var(--text-2);
}
.empty__icon {
  inline-size: 40px; block-size: 40px;
  display: grid; place-content: center;
  border-radius: var(--r-full);
  background: var(--surface-4);
  color: var(--text-3);
}
.empty__title { font-size: var(--fs-16); font-weight: var(--fw-semibold); color: var(--text-1); }
.empty__text { font-size: var(--fs-13); color: var(--text-3); max-inline-size: 46ch; }
.empty--error .empty__icon { background: var(--danger-bg); color: var(--danger-fg); }
.empty--compact { padding: var(--sp-6) var(--sp-4); }

/* --- Полоса ошибки уровня страницы/секции: текст + повтор действия --- */
.alert {
  display: flex; align-items: flex-start; gap: var(--sp-3);
  padding: var(--sp-3) var(--sp-4);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  background: var(--surface-2);
  font-size: var(--fs-13);
  color: var(--text-2);
}
.alert--danger  { background: var(--danger-bg);  border-color: var(--danger-border);  color: var(--danger-fg); }
.alert--warning { background: var(--warning-bg); border-color: var(--warning-border); color: var(--warning-fg); }
.alert--success { background: var(--success-bg); border-color: var(--success-border); color: var(--success-fg); }
.alert--info    { background: var(--info-bg);    border-color: var(--info-border);    color: var(--info-fg); }
.alert__title { font-weight: var(--fw-semibold); display: block; margin-block-end: var(--sp-05); }
.alert__actions { margin-inline-start: auto; flex: 0 0 auto; }

/* --- Kbd: горячие клавиши подписываются ВЕЗДЕ, где действие имеет клавишу --- */
.kbd {
  display: inline-flex; align-items: center; justify-content: center;
  min-inline-size: 20px;
  block-size: 20px;
  padding-inline: 5px;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-block-end-width: 2px;
  border-radius: var(--r-xs);
  color: var(--text-2);
  font-family: var(--font-mono);
  font-size: 11px;
  line-height: 1;
}
.kbd-hint { display: inline-flex; align-items: center; gap: var(--sp-1); color: var(--text-3); font-size: var(--fs-12); }
/* Легенда клавиш внизу рабочего экрана — обязательна там, где есть быстрые клавиши */
.kbd-legend {
  display: flex; flex-wrap: wrap; gap: var(--sp-4);
  padding: var(--sp-2) var(--sp-4);
  background: var(--surface-2);
  border-block-start: 1px solid var(--border-subtle);
  font-size: var(--fs-12);
  color: var(--text-3);
}


/* ============================================================================
   15. МЕДИА: СЕТКИ ПО РАЗМЕРУ РЕШЕНИЯ, ПРОПОРЦИИ, ТАЙЛЫ, СРАВНЕНИЕ
   ГЛАВНЫЙ БЛОК СИСТЕМЫ. Размер превью выбирается по задаче, а не по месту.
   ========================================================================= */

/* Пропорции. Всегда фиксируем ratio: иначе layout прыгает при загрузке. */
.ar { position: relative; inline-size: 100%; background: var(--surface-4); border-radius: var(--r-lg); overflow: hidden; }
.ar > img, .ar > video, .ar > canvas { inline-size: 100%; block-size: 100%; object-fit: cover; display: block; }
.ar--9x16 { aspect-ratio: 9 / 16; }   /* рилс, сторис — основной формат */
.ar--16x9 { aspect-ratio: 16 / 9; }   /* горизонтальное видео, превью экрана */
.ar--1x1  { aspect-ratio: 1 / 1; }    /* аватар, квадратный кроп */
.ar--3x4  { aspect-ratio: 3 / 4; }    /* портретное фото */
.ar--contain > img, .ar--contain > video { object-fit: contain; background: var(--surface-4); }

/* Сетка медиа. Плотность задаётся переменной --tile = МИНИМАЛЬНАЯ ширина плитки.
   Тайлы тянутся до равной ширины, ряды остаются ровными. */
.media-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(var(--tile, 260px), 1fr));
  gap: var(--tile-gap, var(--sp-4));
  align-items: start;
}

/* ПРАВИЛО РАЗМЕРА (нарушать нельзя):
   --recognize  ~200px — «узнать объект»: листаю каталог, ищу знакомое.
   --scan       ~300px — «оценить состав кадра»: вижу сцену целиком.
   --compare    ~500px — «сравнить два варианта»: выбираю лучший из похожих.
   --annotate   ~800px — «разметить / принять по лицу»: решение по деталям.
   Экран приёмки генерации = --compare минимум; приёмка по лицу = --annotate. */
.media-grid--recognize { --tile: 200px; --tile-gap: var(--sp-3); }
.media-grid--scan      { --tile: 300px; }
.media-grid--compare   { --tile: 500px; --tile-gap: var(--sp-5); }
.media-grid--annotate  { --tile: 800px; --tile-gap: var(--sp-6); }
/* Ручной регулятор плотности (слайдер в тулбаре пишет --tile инлайном) */
.media-grid[style*="--tile"] { grid-template-columns: repeat(auto-fill, minmax(var(--tile), 1fr)); }
@media (max-width: 768px) {
  .media-grid { --tile: min(var(--tile, 260px), 100%); --tile-gap: var(--sp-3); }
}

/* Тайл медиа: превью + подпись + место под статус и действия */
.media-tile {
  position: relative;
  display: flex; flex-direction: column; gap: var(--sp-2);
  background: var(--surface-1);
  border: 1px solid var(--border-subtle);
  border-radius: var(--r-lg);
  padding: var(--sp-2);
  cursor: pointer;
  transition: border-color var(--dur-1) var(--ease-out), box-shadow var(--dur-1) var(--ease-out);
}
.media-tile:hover { border-color: var(--border-strong); box-shadow: var(--shadow-2); }
.media-tile:focus-visible { outline: var(--focus-w) solid var(--focus-ring); outline-offset: var(--focus-offset); }
.media-tile.is-selected { border-color: var(--accent); box-shadow: 0 0 0 1px var(--accent); background: var(--accent-bg); }
.media-tile.is-rejected { opacity: 0.55; }
.media-tile.is-rejected .ar { filter: grayscale(1); }
.media-tile__media { position: relative; }
/* Статус кладём НА превью только как дублирующий сигнал; текст статуса — снизу */
.media-tile__flag {
  position: absolute;
  inset-block-start: var(--sp-2);
  inset-inline-start: var(--sp-2);
  z-index: 1;
}
/* Панель действий тайла: видна при hover и ВСЕГДА при фокусе внутри */
.media-tile__actions {
  position: absolute;
  inset-block-end: var(--sp-2);
  inset-inline: var(--sp-2);
  display: flex; gap: var(--sp-1); justify-content: flex-end;
  opacity: 0;
  transition: opacity var(--dur-1) var(--ease-out);
}
.media-tile:hover .media-tile__actions,
.media-tile:focus-within .media-tile__actions,
.media-tile.is-selected .media-tile__actions { opacity: 1; }
.media-tile__caption { display: flex; align-items: center; gap: var(--sp-2); min-inline-size: 0; padding-inline: var(--sp-1); }
.media-tile__name { font-size: var(--fs-13); color: var(--text-1); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.media-tile__meta { font-size: var(--fs-12); color: var(--text-3); font-variant-numeric: tabular-nums; margin-inline-start: auto; flex: 0 0 auto; }

/* СРАВНЕНИЕ «оригинал ↔ результат». Обе стороны РАВНОГО размера.
   Показывать источник миниатюрой рядом с крупным результатом — запрещено. */
.compare-pair {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--sp-4);
  align-items: start;
}
@media (max-width: 900px) { .compare-pair { grid-template-columns: minmax(0, 1fr); } }
.compare-pair__side { display: flex; flex-direction: column; gap: var(--sp-2); min-inline-size: 0; }
.compare-pair__label {
  display: flex; align-items: center; gap: var(--sp-2);
  font-size: var(--fs-12);
  font-weight: var(--fw-semibold);
  letter-spacing: 0.03em;
  text-transform: uppercase;
  color: var(--text-3);
}
.compare-pair--stack-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }

/* Экран приёмки: крупный кадр слева, панель решения справа, липкая. */
.review-layout {
  display: grid;
  grid-template-columns: minmax(0, 1fr) var(--review-panel-w, 380px);
  gap: var(--sp-6);
  align-items: start;
}
.review-layout__panel { position: sticky; inset-block-start: var(--sp-4); display: flex; flex-direction: column; gap: var(--sp-4); }
@media (max-width: 1100px) {
  .review-layout { grid-template-columns: minmax(0, 1fr); }
  .review-layout__panel { position: static; }
}
/* Крупный кадр решения: не мельче 560px по высоте, иначе лицо не разобрать */
.review-stage {
  display: grid; place-items: center;
  min-block-size: 560px;
  background: var(--surface-4);
  border: 1px solid var(--border-subtle);
  border-radius: var(--r-lg);
  overflow: hidden;
}
.review-stage > img, .review-stage > video { max-block-size: 80vh; inline-size: auto; object-fit: contain; }

/* Полоса кадров/вариантов под сценой — это НАВИГАЦИЯ, а не место решения */
.filmstrip { display: flex; gap: var(--sp-2); overflow-x: auto; padding-block: var(--sp-2); scroll-snap-type: x proximity; }
.filmstrip__item {
  flex: 0 0 auto;
  inline-size: 96px;
  border: 2px solid transparent;
  border-radius: var(--r-md);
  overflow: hidden;
  cursor: pointer;
  scroll-snap-align: start;
  background: var(--surface-4);
}
.filmstrip__item[aria-current="true"] { border-color: var(--accent); }
.filmstrip__item:focus-visible { outline: var(--focus-w) solid var(--focus-ring); outline-offset: var(--focus-offset); }

/* Панель массовых операций: появляется, когда выбран хотя бы один объект.
   Обязательна там, где объектов больше десятка. */
.bulk-bar {
  position: sticky;
  inset-block-end: 0;
  z-index: var(--z-sticky);
  display: flex; align-items: center; gap: var(--sp-3);
  padding: var(--sp-3) var(--sp-4);
  background: var(--surface-3);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-3);
}
.bulk-bar__count { font-weight: var(--fw-semibold); font-variant-numeric: tabular-nums; }
.bulk-bar__actions { margin-inline-start: auto; display: flex; gap: var(--sp-2); }


/* ============================================================================
   16. ACCESSIBILITY, АНИМАЦИИ, ПЕЧАТЬ
   ========================================================================= */

/* Текст только для скринридера (подписи иконочных кнопок, статусы) */
.sr-only {
  position: absolute !important;
  inline-size: 1px; block-size: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}
/* Ссылка «к содержимому» — первый элемент body на каждой странице */
.skip-link {
  position: absolute;
  inset-block-start: -100px;
  inset-inline-start: var(--sp-4);
  z-index: var(--z-tooltip);
  padding: var(--sp-2) var(--sp-4);
  background: var(--accent);
  color: var(--accent-on);
  border-radius: var(--r-md);
  transition: inset-block-start var(--dur-2) var(--ease-out);
}
.skip-link:focus { inset-block-start: var(--sp-4); }

/* Живой регион для статусов фоновых операций (aria-live="polite") */
.live-region { position: relative; }

/* Гарантия площади касания: применять к любому мелкому кликабельному элементу */
.tap-safe { position: relative; min-inline-size: var(--target-min); min-block-size: var(--target-min); display: inline-flex; align-items: center; justify-content: center; }

/* Ключевые кадры. Все анимации системы — только здесь. */
@keyframes ds-spin { to { transform: rotate(360deg); } }
@keyframes ds-skeleton { 0% { background-position: 100% 50%; } 100% { background-position: 0 50%; } }
@keyframes ds-modal-in { from { opacity: 0; transform: translateY(8px) scale(0.99); } to { opacity: 1; transform: none; } }
@keyframes ds-pop-in { from { opacity: 0; transform: translateY(-4px); } to { opacity: 1; transform: none; } }
@keyframes ds-toast-in { from { opacity: 0; transform: translateX(16px); } to { opacity: 1; transform: none; } }
@keyframes ds-toast-out { to { opacity: 0; transform: translateX(16px); } }
@keyframes ds-progress-slide { 0% { margin-inline-start: -35%; } 100% { margin-inline-start: 100%; } }

/* Уважение к настройке «меньше движения»: гасим всё, кроме индикаторов
   процесса — они несут смысл, но крутятся медленнее. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
  .spinner, .btn.is-loading::after {
    animation: ds-spin 1.6s linear infinite !important;
    animation-duration: 1.6s !important;
    animation-iteration-count: infinite !important;
  }
  .skeleton { animation: none !important; background: var(--skeleton-base); }
  .progress--indeterminate .progress__bar { inline-size: 100%; animation: none !important; opacity: 0.5; }
}

/* Белая поверхность терминала служит подсветкой лица во время захвата. */
/* brand-tokens:camera begin — generated by tools/apply_brand_tokens.py from app/static/brand/tokens.json; do not edit by hand */
:root {
  --camera-fill: #FFFFFF;
  --camera-fill-soft: #F3F2EF;
  --camera-ink: #16181B;
  --camera-muted: #474C53;
  --camera-success: #17633A;
  --camera-border: #737A83;
}
/* brand-tokens:camera end */

/* Режим высокой контрастности Windows: возвращаем системные цвета границам */
@media (forced-colors: active) {
  .btn, .input, .textarea, .select, .card, .chip, .badge, .modal, .popover { border: 1px solid ButtonBorder; }
  .btn:focus-visible, :focus-visible { outline: 2px solid Highlight; }
  .media-tile.is-selected, .table tr.is-selected { outline: 2px solid Highlight; }
}

/* Печать: убираем интерфейс, оставляем данные */
@media print {
  .toolbar, .bulk-bar, .toast-region, .btn, .tabs, .filmstrip, .skip-link { display: none !important; }
  body { background: #fff; color: #000; }
  .card, .table-wrap { box-shadow: none; border-color: #ccc; }
  .table thead th { position: static; }
  a[href]::after { content: " (" attr(href) ")"; font-size: 10px; color: #555; }
}
