/* 2026-08-31 — Створено. Спільні токени хабу Inovis: колір, шрифт, ритм, форма,
   рух. Один файл на всі модулі; лежить у hub-common і їде в кожен сервіс
   класпасом (Spring Boot віддає static/ із залежних jar-ів), тому це НЕ CDN —
   сервіс піднімається в закритому контурі без жодного зовнішнього звернення.

   Навіщо. До цього кожен модуль мав власний :root, і вони вже розійшлися:
   hub-auth стояв на системному шрифті з радіусами 6/10/14, аналітика й
   накладні — на Manrope з 8/12/16, нейтралі різнилися в кожному з девʼяти
   значень, а --pos/--warn у hub-auth не було взагалі. Перехід із лаунчера в
   модуль читався як перехід між різними продуктами.

   Джерело правди для бренд-кольору — сайт-візитка inovis.com.ua:
   --brand #1B2A4A, --accent #1FC8C2. Решта — робоча проєкція цих двох.

   Що сюди НЕ входить:
   * --accent2 #7aa2ff з візитки. Другий акцент — маркетинговий прийом; на
     робочому екрані він конкурує з бірюзою й ламає правило «одна дія — один
     колір».
   * Компоненти. Тут тільки токени й база; .panel/.sheet/.tile лишаються в
     стилях модуля, бо в кожного своя механіка.
   * inventory-mo. Це свідомий польовий підбренд (завжди темний, ціль 44px,
     системний шрифт — підвал, рукавиця, слабка мережа), він тримає власні
     токени й цей файл не підключає.

   Порядок підключення: <link hub/tokens.css> ПЕРЕД <link style.css>. */

/* ─── Шрифти ────────────────────────────────────────────────────────
   Manrope несе текст і числа, IBM Plex Mono — надрядки, шапку таблиці й
   лічильники. Обидва з кирилицею. cyrillic-ext узято не заради екзотики,
   а через гривню (U+20B4). Ліцензії — hub/fonts/README.md. */
@font-face {
  font-family:"Manrope"; font-style:normal; font-weight:400 800; font-display:swap;
  src:url(fonts/manrope-cyrillic-ext.woff2) format("woff2");
  unicode-range:U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}
@font-face {
  font-family:"Manrope"; font-style:normal; font-weight:400 800; font-display:swap;
  src:url(fonts/manrope-cyrillic.woff2) format("woff2");
  unicode-range:U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
@font-face {
  font-family:"Manrope"; font-style:normal; font-weight:400 800; font-display:swap;
  src:url(fonts/manrope-latin-ext.woff2) format("woff2");
  unicode-range:U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304,
                U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB,
                U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family:"Manrope"; font-style:normal; font-weight:400 800; font-display:swap;
  src:url(fonts/manrope-latin.woff2) format("woff2");
  unicode-range:U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC,
                U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
                U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family:"Plex Mono"; font-style:normal; font-weight:400; font-display:swap;
  src:url(fonts/plexmono-400-cyrillic.woff2) format("woff2");
  unicode-range:U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
@font-face {
  font-family:"Plex Mono"; font-style:normal; font-weight:400; font-display:swap;
  src:url(fonts/plexmono-400-latin.woff2) format("woff2");
  unicode-range:U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC,
                U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
                U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family:"Plex Mono"; font-style:normal; font-weight:600; font-display:swap;
  src:url(fonts/plexmono-600-cyrillic.woff2) format("woff2");
  unicode-range:U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
@font-face {
  font-family:"Plex Mono"; font-style:normal; font-weight:600; font-display:swap;
  src:url(fonts/plexmono-600-latin.woff2) format("woff2");
  unicode-range:U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC,
                U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
                U+2212, U+2215, U+FEFF, U+FFFD;
}

:root {
  /* ─ Бренд ─ Навій — структура (рейка, шапка, сцена входу). Бірюза — дія.
     Коли навієм пофарбована ще й кнопка, око перестає відрізняти «тут тло»
     від «сюди тиснути». */
  --navy:#1B2A4A; --navy-deep:#142038; --navy-lift:#243758;
  --teal:#1FC8C2; --teal-ink:#0C7470; --teal-soft:#E3F7F6;
  /* #1FC8C2 на білому дає контраст 1.9 — текстом бути не може. Для тексту й
     значків --teal-ink, для заливки --teal з --on-teal зверху.
     31.08.2026 — --teal-ink затемнено з #0E8A86: воно не дотягувало навіть на
     білому (4.20), а на власному --teal-soft давало 3.78, тобто найчастіший
     випадок — пігулка з бірюзовим написом — і був найгіршим. Проміжне #0D7C78
     давало на soft 4.53 — прохід на 0.03, тобто будь-яке наступне освітлення
     плитки знову ламало б найчастішу пігулку в продукті. Зупинились на #0C7470:
     5.04 на soft і 5.60 на білому. Освітлювати натомість сам soft не вийшло —
     він і так лише 1.11 проти білої поверхні, і пігулка просто зникла б. */
  --on-teal:#06181C; --teal-hover:#28D6D0;

  /* ─ Нейтралі ─ */
  --ground:#F1F4F9; --surface:#FFFFFF; --sunken:#F5F7FA;
  --line:#E4E8F0; --line-strong:#CFD6E2;
  --ink:#141C29; --ink-2:#4A5567; --ink-3:#657083;
  /* 31.08.2026 — --ink-3 затемнено з #7A8598. Це не «майже текст», а текст:
     ним підписані лічильники на плитках і другий рядок у списках, і 3.38 на
     --ground читалося сірим шумом. Тепер 4.54. Ієрархія ціла: ink 15.9,
     ink-2 6.84, ink-3 4.54 — три виразні сходинки, а не дві з половиною. */

  /* ─ Семантика ─ Окрема вісь; акцентом не рахується.
     Пари тут дві, і плутати їх не можна. --pos/--warn/--neg — це заливка,
     рамка, крапка, стрілка: колір, який видно плямою. Текстом вони бути не
     можуть: на своєму ж світлому тлі дають 2.7–3.5 замість 4.5, а на білому
     ще менше. Для тексту й значків — --*-ink, рівно як --teal-ink при --teal.
     Перевірено на найгіршому з реальних тл (власний soft): warn-ink 5.13,
     neg-ink 5.68, pos-ink 4.70. */
  --pos:#2E9E6B; --warn:#C8891A; --neg:#D2544A;
  --pos-soft:#E6F4EC; --warn-soft:#FBF1DE; --neg-soft:#FBE9E7;
  --pos-ink:#1B7A4F; --warn-ink:#8A5D0A; --neg-ink:#A8322A;
  /* 31.08.2026 — Окрема заливка для небезпечної кнопки. --neg білим написом
     давав 4.11 у світлій темі і 2.88 у темній: обидві менші за 4.5, а напис на
     кнопці — звичайний текст. Ані білий, ані майже чорний на самому --neg не
     дотягують у світлій (4.11 / 4.46), тому міняється саме заливка: у світлій
     беремо темніший --neg-ink (білий на ньому — 6.66), у темній лишаємо світлий
     --neg із темним чорнилом (6.37). Пляма кнопки при цьому теж не губиться:
     6.66 і 5.87 проти поверхні. --neg лишається як був — це колір рамки й
     крапки, не кнопки. */
  --neg-fill:#A8322A; --on-neg:#FFFFFF;

  /* ─ Шрифт ─ Запасні стеки лишаються: поки woff2 їде (або не доїхав),
     сторінка має бути читабельною, а не порожньою. */
  --font-ui: "Manrope", system-ui, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  --font-mono: "Plex Mono", ui-monospace, "Cascadia Mono", "Segoe UI Mono", Consolas, monospace;

  /* ─ Кегль ─ Сім сходинок замість 57 випадкових чисел. 13/13.5/14/14.5 на око
     не розрізняються, отже ієрархії не створюють — вона тільки розмивається.
     Наявні розміри переїжджають сюди в міру дотику, не однією заміною. */
  --fs-1:10px;    /* службова мітка, надрядок */
  --fs-2:11.5px;  /* дрібне пояснення, лічильник */
  --fs-3:13px;    /* основний текст, рядок списку */
  --fs-4:14.5px;  /* кнопка, поле вводу */
  --fs-5:17px;    /* заголовок картки */
  --fs-6:21px;    /* заголовок екрана */
  --fs-7:26px;    /* число підсумку */

  /* ─ Ритм ─ */
  --s-1:4px; --s-2:8px; --s-3:12px; --s-4:16px; --s-5:24px;

  /* ─ Форма ─ */
  --r-s:8px; --r-m:12px; --r-l:16px;

  /* ─ Висота ─ Дві, не більше: shadow — панель, shadow-lift — те, що спливло.
     Тон синій під колір підкладки: чорна тінь на холодному світлому тлі
     читається брудом. */
  --shadow:0 1px 2px rgba(20,32,56,.05), 0 12px 28px -16px rgba(20,32,56,.22);
  --shadow-lift:0 2px 4px rgba(20,32,56,.06), 0 20px 40px -20px rgba(20,32,56,.30);
  --shadow-s:0 1px 2px rgba(20,32,56,.12);

  /* ─ Рух ─ Одна крива: швидкий старт, мʼяке гальмування — стан видно одразу,
     а не наприкінці переходу. ease-in заборонений: він притримує саме той
     момент, на який людина дивиться. --t-fast для дрібного (колір, рамка),
     --t-base для того, що зʼявляється або їде. */
  --ease:cubic-bezier(.22,.75,.28,1);
  --t-fast:120ms; --t-base:180ms;

  /* Рідні контроли (select, date) малюються системою. Без color-scheme вони
     лишаються білими на темній сторінці. */
  color-scheme:light;
}

/* ─── Темна тема ────────────────────────────────────────────────────
   01.09.2026 — Три стани замість одного. До цього тема бралася виключно з
   налаштування системи, і людина не мала жодного важеля: телефон у темній
   темі — склад темний, а таблиця виручки теж темна, навіть якщо дивитись на
   неї при денному світлі. Тепер:

     :root                                    — світла (за замовчуванням);
     @media (prefers-color-scheme: dark)
       :root:not([data-theme="light"])        — система каже «темно», і людина
                                                не сказала протилежного;
     :root[data-theme="dark"]                 — людина натиснула «темна».

   Порядок і :not() тут несучі: без них явний вибір «світла» програвав би
   налаштуванню системи, і перемикач працював би лише в один бік.

   ⚠ ДВА БЛОКИ ТЕМНИХ ТОКЕНІВ НИЖЧЕ МАЮТЬ БУТИ ОДНАКОВІ. CSS не вміє
   застосувати одне правило і всередині @media, і поза ним, тому це свідоме
   дублювання. Правиш один — правиш обидва; розбіжність вилізе тільки в
   когось одного з двох станів і шукатиметься довго. */

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    color-scheme:dark;
    --ground:#0C1220; --surface:#151D2B; --sunken:#111826;
    --line:#26303F; --line-strong:#38445A;
    --ink:#E8EEF6; --ink-2:#AAB6C7; --ink-3:#7E8B9E;
    --teal-soft:#123A3C; --teal-ink:#3FD8D2;
    /* Темна тема — не інверсія: зелений і червоний зі світлої тут глухнуть,
       тому світлішають, а не перевертаються. */
    --pos:#4CC38A; --warn:#E0A94A; --neg:#E8776D;
    --pos-soft:#122C22; --warn-soft:#31280F; --neg-soft:#301A18;
    /* На темному тлі темнити текст нікуди: світлий семантичний колір уже дає
       6.7–7.5. Токени лишаються різними іменами з однаковим значенням — щоб
       розмітка не знала, яка зараз тема. */
    --pos-ink:#4CC38A; --warn-ink:#E0A94A; --neg-ink:#E8776D;
    --neg-fill:#E8776D; --on-neg:#2A0A07;
    --shadow:0 1px 2px rgba(0,0,0,.4), 0 12px 28px -16px rgba(0,0,0,.7);
    --shadow-lift:0 2px 4px rgba(0,0,0,.45), 0 20px 40px -20px rgba(0,0,0,.75);
    --shadow-s:0 1px 2px rgba(0,0,0,.45);
  }
}

/* ⚠ Копія блоку вище. Тримати синхронно. */
:root[data-theme="dark"] {
  color-scheme:dark;
  --ground:#0C1220; --surface:#151D2B; --sunken:#111826;
  --line:#26303F; --line-strong:#38445A;
  --ink:#E8EEF6; --ink-2:#AAB6C7; --ink-3:#7E8B9E;
  --teal-soft:#123A3C; --teal-ink:#3FD8D2;
  --pos:#4CC38A; --warn:#E0A94A; --neg:#E8776D;
  --pos-soft:#122C22; --warn-soft:#31280F; --neg-soft:#301A18;
  --pos-ink:#4CC38A; --warn-ink:#E0A94A; --neg-ink:#E8776D;
  --neg-fill:#E8776D; --on-neg:#2A0A07;
  --shadow:0 1px 2px rgba(0,0,0,.4), 0 12px 28px -16px rgba(0,0,0,.7);
  --shadow-lift:0 2px 4px rgba(0,0,0,.45), 0 20px 40px -20px rgba(0,0,0,.75);
  --shadow-s:0 1px 2px rgba(0,0,0,.45);
}

/* Явна світла. Потрібна саме заради color-scheme: без неї рідні контроли
   (select, календар, смуга гортання) лишаються темними на світлій сторінці,
   коли система стоїть у темній темі. Токени вже світлі з :root. */
:root[data-theme="light"] { color-scheme:light; }

/* ─── База ──────────────────────────────────────────────────────── */
* { box-sizing:border-box; }
html, body { height:100%; }
body {
  margin:0; background:var(--ground); color:var(--ink);
  font-family:var(--font-ui); font-size:14px; line-height:1.5;
  -webkit-font-smoothing:antialiased;
  /* Manrope має пропорційні цифри за замовчуванням — одиниця вужча за нуль на
     третину. Без цього рядка колонки сум стрибають при кожному сортуванні. */
  font-variant-numeric:tabular-nums;
}
button, input, select, textarea { font:inherit; color:inherit; }

/* Клавіатура має бачити, де вона стоїть. Правило одне на весь хаб, щоб не
   доводилось згадувати про нього в кожному новому компоненті. */
:focus-visible { outline:2px solid var(--teal); outline-offset:2px; }

/* Дотик не має лишати сірий прямокутник підсвітки поверх власного стану
   натискання. Самі :hover модулі загортають у @media (hover: hover). */
@media (hover: none) {
  * { -webkit-tap-highlight-color:transparent; }
}

/* Системна смуга гортання Windows широка, зі стрілками й білим жолобом — на
   темній темі вона світиться смугою посеред інтерфейсу. Ширина не нульова
   навмисно: у довгій таблиці смуга — єдина ознака, що нижче ще щось є. */
* { scrollbar-width:thin; scrollbar-color:var(--line-strong) transparent; }
*::-webkit-scrollbar { width:10px; height:10px; }
*::-webkit-scrollbar-track { background:transparent; }
*::-webkit-scrollbar-thumb {
  background:var(--line-strong); border-radius:99px;
  border:3px solid transparent; background-clip:padding-box;
}
*::-webkit-scrollbar-corner { background:transparent; }
@media (hover: hover) {
  *::-webkit-scrollbar-thumb:hover { background:var(--ink-3); background-clip:padding-box; }
}

/* ─── Кнопки ────────────────────────────────────────────────────────
   Один словник на весь хаб. До цього їх було чотири: .btn-primary (teal) в
   аналітиці, button.primary в накладних, .btn-primary (navy!) у hub-auth і
   .btn-main в інвентаризації — тобто один клас означав протилежні кольори в
   сусідніх сервісах. Модулі лишають свої історичні імена як псевдоніми, але
   вигляд береться звідси. */
.btn, .btn-primary, .btn-ghost, .btn-danger {
  display:inline-flex; align-items:center; justify-content:center; gap:var(--s-2);
  font-size:var(--fs-4); font-weight:600; line-height:1.2;
  padding:11px var(--s-4); cursor:pointer;
  border:1px solid transparent; border-radius:var(--r-s);
  transition:background var(--t-fast) var(--ease),
             border-color var(--t-fast) var(--ease),
             color var(--t-fast) var(--ease),
             transform var(--t-fast) var(--ease);
}
/* Головна дія — завжди бірюзова. */
.btn-primary {
  background:var(--teal); color:var(--on-teal);
  box-shadow:inset 0 1px 0 rgba(255,255,255,.28), 0 6px 16px -8px rgba(14,138,134,.35);
}
.btn-ghost { background:transparent; color:var(--ink-2); border-color:var(--line-strong); }
.btn-danger { background:var(--neg-fill); color:var(--on-neg); }

/* Палець має отримати фізичну відповідь одразу, ще до відповіді сервера. */
.btn:active, .btn-primary:active, .btn-ghost:active, .btn-danger:active { transform:scale(.98); }
.btn[disabled], .btn-primary[disabled], .btn-ghost[disabled], .btn-danger[disabled] {
  opacity:.55; cursor:default; transform:none;
}
/* 01.09.2026 — Палець має отримати 44 px незалежно від того, який шрифт у кнопки:
   спільна кнопка з відступом 11 px давала 39 px, а де модуль зменшував текст
   (адмінка — 13 px) — ще менше. Росте тільки мінімум висоти й тільки для дотику:
   мишка бачить кнопки такими, як були, а горизонтальні відступи не міняються,
   тож жоден рядок кнопок не переноситься. */
@media (pointer: coarse) {
  .btn, .btn-primary, .btn-ghost, .btn-danger { min-height:44px; }
}
@media (hover: hover) {
  .btn-primary:hover:not([disabled]) { background:var(--teal-hover); }
  .btn-ghost:hover:not([disabled]) { background:var(--sunken); color:var(--ink); }
  .btn-danger:hover:not([disabled]) { filter:brightness(1.06); }
}

/* Прохання про спокій прибирає ПЕРЕМІЩЕННЯ, а не рух узагалі. Звична заглушка
   «*, transition-duration:.01ms !important» гасить заразом і ті переходи, що
   нічого не возять: підсвітку поля у фокусі, зміну кольору кнопки, появу
   підказки. Людина з вестибулярним розладом просила не їздити екраном, а
   отримувала інтерфейс, який клацає станами без натяку на звʼязок. Тому
   лишається все, що міняє колір і прозорість, і зникає все, що їде.
   Правило написане через * без !important навмисно: модуль має змогу
   лишити конкретну анімацію, якщо вона несе зміст (індикатор очікування). */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior:auto; }
  *, *::before, *::after {
    transition-property:opacity, color, background-color, border-color, box-shadow, fill, stroke;
    animation-duration:.01ms; animation-iteration-count:1;
  }
}
