/* Generated for KoloPlus 1.1.7.330. Keep source order. */

/* START tokens.css */
:root {
  /* ── v1.1.6.251 (REV45 CANON-000): АЛІАСИ КАНОНУ — ГЛОБАЛЬНО ──────────────────────────────
     Токени --kp-* із kolo_plus_new_theme_preview_CANON_REV6.html були оголошені ЛИШЕ у
     widgets/main/header/header.css. Тобто будь-який стиль, що на них спирався, працював
     тільки там, де завантажено хедер — а на сторінці без нього мовчки брав порожнє значення.

     Це не теорія: саме так я й наступив на схожу міну у власному коді (див. нижче про
     --n-bg-elev). Тому канонні імена живуть тут, поруч із рештою токенів, і є аліасами на
     чинні --n-*: жодне значення не змінюється, змінюється лише ДОСЯЖНІСТЬ. */
  --kp-color-surface:        var(--n-bg-2);
  --kp-color-surface-raised: var(--n-bg-3);
  --kp-color-surface-muted:  var(--n-bg-soft);
  --kp-color-border:         var(--n-line);
  --kp-color-text:           var(--n-text);
  --kp-color-text-muted:     var(--n-text-mute);
  --kp-shadow-sm:            var(--n-sh-sm);

/* 1.1.7.027 — рішення власника: системні шрифти, як у каноні R8.91 (DesignTokens: font-body / font-display /
   font-mono). Inter / Chakra Petch / JetBrains Mono ніколи не вантажились (Google Fonts заборонені CSP).
   🔴 `--n-font-mono` (і похідні `--mono`, `--arena-mono`) — НЕ шрифт коду: «кібер»-тема ставила його на ПІДПИСИ,
   заголовки карток і числа (≈540 правил). У каноні моно лише для коду й технічних ідентифікаторів, і для цього є
   окремий `--kp-font-mono` нижче. Тож старі імена тепер показують на системний шрифт інтерфейсу, а справжній
   моно лишився за `--kp-font-mono`. */
--n-font:         system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;
--n-font-display: system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;
--n-font-mono:    var(--n-font);
--n-fs:        15px;
--n-lh:        1.55;
--n-lh-tight:  1.4;
--n-lh-snug:   1.45;
--n-w-reg:     400;
--n-w-med:     500;
--n-w-sem:     600;
--n-w-bold:    700;
--n-w-black:   800;
--n-fs-xs:  var(--n-fs);
--n-fs-sm:  var(--n-fs);
--n-fs-md:  var(--n-fs);
--n-fs-lg:  var(--n-fs);
--n-fs-xl:  var(--n-fs);
--n-fs-2xl: var(--n-fs);
--n-fs-3xl: var(--n-fs);
--n-fs-4xl: var(--n-fs);
--n-s-1: 4px;
--n-s-2: 8px;
--n-s-3: 12px;
--n-s-4: 16px;
--n-s-5: 24px;
--n-s-6: 32px;
--n-s-7: 48px;
--n-s-8: 64px;
--n-s-9: 96px;
--n-r-xs:   8px;
--n-r-sm:   10px;
--n-r-md:   14px;
--n-r-lg:   20px;
--n-r-xl:   26px;
--n-r-pill: 999px;
--n-r:      20px;
--kp-header-h:   68px;
--n-header-h:    60px;
--n-content-w:   720px;
--n-wide-w:     1080px;
--n-container-xl: 1200px;
--n-dur-fast:    120ms;
--n-dur-base:    180ms;
--n-dur-slow:    280ms;
--n-ease:        cubic-bezier(0.16, 1, 0.3, 1);
--n-ease-default: var(--n-ease);
--n-ease-spring: cubic-bezier(0.34, 1.56, 0.64, 1);
--n-z-base:     1;
--n-z-sticky:   100;
--n-z-header:   500;
--n-z-modal:   1000;
--n-z-popover: 1500;
--n-z-toast:   2000;
/* v1.1.5.163 (звіт audit_css_unification_plan_1_1_5_157, Етап 1): відсутні
   semantic tokens, потрібні для завершення CSS-токенізації. Theme-invariant
   (медальні/маскові/брендові кольори не мають залежати від теми). */
--n-rank-silver: #c0c0c0;
/* v1.1.5.178 (FULL_CSS_ANALYSIS_v178, HB-192): --n-bg-3 використовувався
   у 12 місцях (profile.css ×5, arena/inspect, security.php ×3 БЕЗ fallback —
   2FA recovery-коди без фону!, onboarding.php, friends_suggested ×2), але
   ніколи не існував. Семантика всіх uses = "м'яка втоплена поверхня" =
   --n-bg-soft. Alias theme-invariant — авто-адаптується через underlying токен. */
--n-bg-3: var(--n-bg-soft);
--n-rank-bronze: #cd7f32;
--n-mask-solid:  #000;
/* 🔴 v1.1.6.363: цей токен оголошений ДО поділу на теми, тому був однаковий
   для обох — і в цьому була помилка. Тло попередження у двох темах
   ПРОТИЛЕЖНЕ за яскравістю:
       світла --n-warn #8a5a06 (темний)  → білий 5.92 ✓ · чорний 3.19 🔴
       темна  --n-warn #f0b95c (світлий) → білий 1.78 🔴 · чорний 10.61 ✓
   Отже одним кольором не обійтись. Значення нижче — типове (світла тема),
   а для темної воно перевизначене у своєму блоці. */
--n-text-on-warn:   #ffffff;
--n-text-on-danger: var(--kp-color-on-danger);
--n-text-on-ok:     var(--kp-color-on-success);
--n-brand-facebook: #4267b2;
--n-brand-twitter:  #1da1f2;
--n-brand-telegram: #2ea6da;
/* v1.1.5.180 (HB-192-lite): compatibility aliases for historical token names
   that were only used through var(--x, fallback). Defining them removes
   fallback-degraded declarations while preserving current visual semantics. */
--kp-footer-h: 80px;
--kp-bottom-nav-h: 64px;
--kp-mobile-footer-height: var(--kp-bottom-nav-h);
--n-surface-2: var(--n-bg-2);
--n-rose: var(--n-danger);
--n-emerald: var(--n-ok);
--n-red: var(--n-danger);
--n-on-accent: var(--n-text-on-accent);
--text-on-accent: var(--n-text-on-accent);
--n-border: var(--n-line);
--n-bg-1: var(--n-bg-2);
--n-legend: var(--n-rarity-legendary);
--n-bg-active: var(--n-bg-hover);
--n-fg-2: var(--n-text-soft);
--n-soft: var(--n-bg-soft);
--n-card: var(--n-bg-2);
--n-surface-soft: var(--n-bg-soft);
--n-warning: var(--n-warn);
--r-sm: var(--n-r-sm);
--kp-surface: var(--n-bg-2);
--kp-text: var(--n-text);
--kp-border: var(--n-line);
--kp-text-mute: var(--n-text-mute);
--kp-bg: var(--n-bg);
--kp-accent: var(--n-accent);
--n-font-sans: var(--n-font);
--n-header-height: var(--kp-header-h);
--n-err: var(--n-danger);
--accent-border: var(--n-accent-border);
--n-accent-emerald-glow: color-mix(in oklab, var(--n-accent-emerald) 50%, transparent);
  /* v1.1.5.182: 23 SOFT-UI токени для порту auth/account дизайну */
  --n-accent-deep: color-mix(in srgb,var(--n-accent) 58%,#000);
  --n-camo-1: #0e0f16;
  --n-camo-2: #12131c;
  --n-camo-3: #16171f;
  --n-camo-4: #0c0d14;
  --n-danger-deep: #8f1715;
  --n-danger-hover: #f04a41;
  --n-danger-ring: rgba(225,58,50,.28);
  --n-font-body: 'Inter',system-ui,-apple-system,'Segoe UI',sans-serif;
  --n-gold-deep: #7c5820;
  --n-grid-line: rgba(163,153,116,.055);
  --n-grid-rgb: 255,255,255;
  --n-hi: 255,255,255;
  --n-info-deep: #2a5a67;
  --n-ink: 0,0,0;
  --n-ink-deep: 5,7,6;
  --n-ink-panel: 12,14,12;
  --n-line-rgb: 255,255,255;
  --n-metal: #0e0f16;
  --n-metal-2: #14151d;
  --n-text-strong: #ffffff;   /* канонна гама, світліше за color-text */
  --n-violet: #a78bfa;
  --n-violet-soft: rgba(128,91,168,.13);
}
/* 1.1.7.046 (знахідка власника 02.10.2026 «випадаючі списки в темній темі білі з текстом»): список, що випадає зі
   <select>, малює сам браузер, і без цього рядка він завжди світлий — на білому тлі опинявся світлий текст теми,
   тобто пункти були майже невидимі. `color-scheme` каже браузеру, в якій схемі малювати СВОЇ елементи: список,
   смуги прокрутки, календар, поле вибору файлу. Нижче ще й явні кольори пунктів — на випадок браузера, що
   `color-scheme` не слухає. */
:root,
:root[data-theme="light"] {
  color-scheme: light;
  /* v1.1.6.226 — канон REV6, акаунт. Точних --n--відповідників немає:
     --n-line це межа поверхні, а control-border у каноні помітно контрастніший.
     Живуть тут, а не в shell CSS: account_zone_contract_check (A-017) і
     account_redesign_v584_contract_check забороняють сирі hex поза токенами. */
  --kp-space-1: var(--n-s-1);
  --kp-space-2: var(--n-s-2);
  --kp-space-3: var(--n-s-3);
  --kp-space-4: 16px;
  --kp-space-5: 24px;
  --kp-space-6: 32px;

  --kp-border-width: 1px;
  --kp-radius-sm: 4px;   /* --n-r-sm = 10px — це РІЗНІ величини */
  --kp-radius-lg: 12px;  /* --n-r-md = 14px — теж не те саме */
  --kp-text-lg: 1.125rem;

  --kp-toggle-knob: #ffffff;   /* кулька перемикача біла в ОБОХ темах */
  --kp-radius-pill: 999px;
  --kp-weight-bold: 700;
  --kp-text-xs: 0.75rem;
  --kp-color-warning:    #8a5a06;
  --kp-color-warning-bg: #fdf3e0;
  --kp-color-danger:     #b3261e;
  --kp-color-control-border: #7d8b9f;
  --kp-color-surface-muted:  #eaeff7;

--n-bg:        #f3f6fb;   /* канон: color-page */
--n-bg-2:      #ffffff;   /* канон: color-surface (збіг) */
--n-bg-soft:   #eaeff7;   /* канон: color-surface-muted */
--n-bg-sunk:   #e6ecf6;   /* v1.1.6.295: було #e2e9f4 — на 0.07 не дотягувало до
                             AA з text-muted (4.43). Тепер 4.56. */
--n-bg-hover:  rgba(0, 0, 0, 0.05);
--n-text:      #11213a;   /* канон: color-text */
--n-text-soft: #5b6b84;   /* канон: color-text-muted */
/* v1.1.6.295 (REV46, CANON-STEP-12): приглушені рівні тексту переведено на
   канонну гаму — у .293 я про них забув, і вони лишились теплими сірими на
   холодних канонних поверхнях. Порахував: --n-text-mute #6b6660 на темній
   поверхні #121b2b давав 3.03:1 при порозі WCAG AA 4.5:1.
   Значення підібрані так, щоб найгірша пара з БУДЬ-ЯКОЮ канонною поверхнею
   лишалась ≥4.5 — перевіряється bin/rev46_contrast_check.mjs. */
--n-text-mute: #5b6b84;   /* канон text-muted; найгірша пара 4.56:1 */
--n-text-dim:  #5b6b84;   /* окремого тьмянішого рівня канон не має, а будь-який
                             світліший не дотягує до AA на світлих поверхнях */
--n-text-on-accent: #ffffff;   /* v1.1.6.895: на синьому бренду #106cee — білий (4.77:1) */
--n-line:        #dfe6f0;  /* канон: color-border */
--n-line-strong: color-mix(in srgb, #dfe6f0 55%, #11213a);  /* виведено з канонного border */
--n-line-soft:   color-mix(in srgb, #dfe6f0 55%, #ffffff);  /* виведено з канонного border */
--n-accent:        #106cee;   /* v1.1.6.895: канон accent (koloplus, бренд R3), світла */
--n-accent-hover:  #0d5ccc;   /* v1.1.6.895: темніше — білий 6.14:1 */
/* 🔴 v1.1.6.364: ОКРЕМИЙ токен для тексту на НАВЕДЕНОМУ акценті — і ось чому
   не можна обійтись наявним --n-text-on-accent:
       звичайний акцент світла #e45f3a → темний 4.60 ✓ · білий 3.50 🔴
       наведений        світла #ad5a3e → темний 3.31 🔴 · білий 4.87 ✓
       наведений        темна  #ff7a5c → темний 6.29 ✓ · білий 2.56 🔴
   У СВІТЛІЙ темі наведення ТЕМНІШЕ за звичайний стан, у ТЕМНІЙ — СВІТЛІШЕ.
   Тобто текст мусить різнитись не лише між темами, а й між СТАНАМИ. Правити
   --n-text-on-accent було б помилкою: він правильний для звичайного стану. */
--n-text-on-accent-hover: #ffffff;
/* v1.1.6.294 (REV46, CANON-STEP-11): похідні акценту тепер виводяться з
   var(--n-accent), а не з зашитого кольору.
   Це виправляє ДВІ речі. По-перше, мою власну незавершеність із .293: там
   --n-accent став канонним (#e45f3a / #ff7a55), а його відтінки лишились
   рахуватись від старого #c4694a / #ef6443 — тобто кнопка була одного
   помаранчевого, а її світіння й фокусне кільце іншого.
   По-друге, давнішу ваду: з десяти акцентів перемикався ЛИШЕ --n-accent, а
   м'яке тло, кільце й світіння лишались глиняними. Вибираєш синій акцент —
   кнопка синя, а підсвітка навколо неї помаранчева. Тепер усі 270 вживань
   похідних ідуть за активним акцентом, хай яким він буде. */
--n-accent-light:  color-mix(in oklab, var(--n-accent) 40%, white);
--n-accent-soft:   color-mix(in oklab, var(--n-accent) 14%, transparent);
--n-accent-ring:   color-mix(in oklab, var(--n-accent) 28%, transparent);
--n-accent-glow:   color-mix(in oklab, var(--n-accent) 22%, transparent);
--n-accent-border: color-mix(in oklab, var(--n-accent) 35%, transparent);
/* v1.1.6.295 (REV46, CANON-STEP-12): семантичні кольори вирівняно на канон і
   на поріг читабельності. Порахував білий текст на кожному:
     --n-ok #16a34a → 3.30:1 (канон success #187a4b → 5.35:1)
     --n-warn #c9881e → 2.99:1 (канон warning #8a5a06 → 6.51:1)
     --n-danger #dc2626 → 4.83:1 (проходить, лишаю)
   Тобто зелена кнопка «успіх» і бурштинова «увага» з білим написом були
   нижче WCAG AA — і не через канон, а віддавна. Канон дає темніші відтінки
   саме для цього. */
--n-ok:        #187a4b;   /* канон color-success */
--n-ok-soft:   color-mix(in oklab, #16a34a 12%, transparent);
--n-ok-light:  color-mix(in oklab, #16a34a 30%, white);
--n-warn:      #8a5a06;   /* канон color-warning */
--n-warn-soft: color-mix(in oklab, #c9881e 12%, transparent);
--n-danger:    #dc2626;
--n-danger-soft: color-mix(in oklab, #dc2626 12%, transparent);
--n-info:      #0e9ec2;
--n-info-soft: color-mix(in oklab, #0e9ec2 12%, transparent);
--n-hp:        #16a34a;
--n-hp-dim:    color-mix(in oklab, #16a34a 12%, transparent);
--n-gold:      #c9881e;
--n-gold-dim:  color-mix(in oklab, #c9881e 12%, transparent);
--n-lav:       #7c5cff;
--n-lav-dim:   color-mix(in oklab, #7c5cff 12%, transparent);
--n-cyan:      #0e9ec2;
--n-cyan-dim:  color-mix(in oklab, #0e9ec2 12%, transparent);
--n-pink:      #d6488f;
--n-pink-dim:  color-mix(in oklab, #d6488f 12%, transparent);
--n-xp:        #2563eb;
--n-cur-gold:    #c9881e;
--n-cur-crystal: #0e9ec2;
--n-cur-ducat:   #c4694a;
--n-cur-pos:     #16a34a;
--n-cur-neg:     #dc2626;
--n-sh-sm:   0 1px 3px rgba(42, 34, 28, 0.06);
--n-sh-md:   0 4px 12px rgba(42, 34, 28, 0.08);
--n-sh-lg:   0 12px 32px rgba(42, 34, 28, 0.10);
/* v1.1.6.292: четвертий щабель. Канон має overlay (0 24px 64px), а в --n- драбині
   його не було — через це --kp-shadow-overlay був єдиним, хто не вмів темну тему.
   Прозорість продовжує наявний ряд .06 → .08 → .10 → .14. */
--n-sh-overlay: 0 24px 64px rgba(42, 34, 28, 0.14);
--n-sh-glow: 0 0 0 1px var(--n-accent-soft), 0 8px 24px -8px var(--n-accent-glow);
/* v1.1.5.163: scrim/overlay tokens (modal backdrop, dimming layer) — світла
   тема темнішими, бо затемнює вже світлий фон. */
--n-scrim-soft:   rgba(0, 0, 0, 0.35);
--n-scrim:        rgb(9 14 26 / 0.55);   /* v1.1.6.293: канон color-scrim */
--n-scrim-strong: rgba(0, 0, 0, 0.72);
/* 🔴 v1.1.6.375: ТЕКСТ НА ЗАТЕМНЕННІ. Токен для затемнення був, а для тексту
   на ньому — ні, і через це в 18 місцях ставили --n-text-on-accent (#11213a,
   майже чорний). Він створений для АКЦЕНТНОГО тла й там працює (4.60:1), але
   на чорній накладці над фото дає 1.24-1.27:1 — напис зникає.
   Затемнення темне В ОБОХ темах (це його призначення — приглушити фото під
   написом), тож текст на ньому білий ЗАВЖДИ, без тематичності: ~20:1. */
--n-text-on-scrim: #ffffff;
--n-shadow-soft:     var(--n-sh-md);
--n-shadow-elevated: var(--n-sh-lg);
--n-focus-ring: 0 0 0 3px var(--n-accent-ring);
--n-bg-glow-top:    color-mix(in oklab, var(--n-accent) 8%, transparent);
--n-bg-glow-bottom: color-mix(in oklab, var(--n-accent) 5%, transparent);
--n-gradient-accent: linear-gradient(135deg, var(--n-accent), var(--n-gold));
--n-accent-koloplus: #106cee;   /* v1.1.6.895: канон — синій бренду R3 */
--n-accent-clay:    #e45f3a;   /* канон */
--n-accent-sage:    #64926d;   /* канон */
--n-accent-amber:   #d99614;   /* канон */
--n-accent-plum:    #9a4d8c;   /* канон */
--n-accent-blue:    #2369ff;   /* канон */
--n-accent-emerald: #18a66d;   /* канон */
--n-accent-violet:  #7653f6;   /* канон */
--n-accent-rose:    #e0568c;   /* канон */
--n-accent-cyan:    #149cb8;   /* канон */
--n-accent-coral:   #ef6d58;   /* канон */
--n-rarity-common:    #8a8378;
--n-rarity-uncommon:  #16a34a;
--n-rarity-rare:      #2563eb;
--n-rarity-epic:      #7c5cff;
--n-rarity-legendary: #c9881e;
--n-rarity-mythic:    #dc2626;
  /* v1.1.5.182: SOFT-UI токени (light-варіанти) */
  --n-danger-deep: #8c130e;
  --n-gold-deep: #6b4c0e;
  --n-grid-rgb: 0,0,0;
  --n-hi: 26,23,20;
  --n-info-deep: #1f4c59;
  --n-ink: 255,255,255;
  --n-ink-deep: 246,243,238;
  --n-ink-panel: 255,255,255;
  --n-line-rgb: 0,0,0;
  --n-metal: #ffffff;
  --n-text-strong: #0a1526;   /* канонна гама, темніше за color-text */
  --n-violet: #7c5cff;
}
[data-theme="dark"] {
  color-scheme: dark;
  --kp-toggle-knob: #ffffff;
  --kp-color-control-border: #5a739c;
  --kp-color-warning:    #f0b95c;
  --kp-color-warning-bg: #3a2c10;
  --kp-color-danger:     #f2857e;
  --kp-color-surface-muted:  #0f1726;

--n-bg:        #0c1220;   /* канон: color-page, темна */
--n-bg-2:      #121b2b;   /* канон: color-surface, темна */
--n-bg-soft:   #0f1726;   /* канон: color-surface-muted, темна */
--n-bg-sunk:   #0a1120;   /* глибше за surface-muted, у канонній гамі */
--n-bg-hover:  rgba(255, 255, 255, 0.07);
--n-text:      #f3f7ff;   /* канон: color-text, темна */
--n-text-soft: #9aa9c2;   /* канон: color-text-muted, темна */
/* v1.1.6.295: те саме для темної схеми — див. коментар у світлій. */
--n-text-mute: #9aa9c2;   /* канон text-muted, темна; найгірша пара 7.25:1 */
--n-text-dim:  #7d8ba3;   /* тьмяніший рівень, але ще ≥5:1 на всіх поверхнях */
--n-text-on-accent: #11213a;   /* v1.1.6.300, див. світлу схему */
--n-line:        #273650;  /* канон: color-border, темна */
--n-line-strong: color-mix(in srgb, #273650 60%, #f3f7ff);  /* виведено з канонного border, темна */
--n-line-soft:   color-mix(in srgb, #273650 60%, #0c1220);  /* виведено з канонного border, темна */
--n-accent:        #4d94ff;   /* v1.1.6.895: канон accent (koloplus), темна — темний текст 5.37:1 */
--n-accent-hover:  #6aa6ff;   /* v1.1.6.895: світліше — темний текст 6.53:1 */
--n-text-on-accent-hover: #11213a;   /* v1.1.6.364: тут наведення СВІТЛІШЕ — потрібен темний текст (6.29:1) */
/* v1.1.6.294: те саме для темної схеми — див. коментар у світлій. */
--n-accent-light:  color-mix(in oklab, var(--n-accent) 70%, white);
--n-accent-soft:   color-mix(in oklab, var(--n-accent) 14%, transparent);
--n-accent-ring:   color-mix(in oklab, var(--n-accent) 30%, transparent);
--n-accent-glow:   color-mix(in oklab, var(--n-accent) 38%, transparent);
--n-accent-border: color-mix(in oklab, var(--n-accent) 35%, transparent);
/* v1.1.6.295: темна схема — канонні success/warning. На темному тлі вони
   світліші, і текст на них має бути ТЕМНИЙ (--kp-color-on-success/-warning),
   що вже оголошено нижче. */
--n-ok:        #4ec38a;   /* канон color-success, темна */
--n-ok-soft:   color-mix(in oklab, #22c55e 12%, transparent);
--n-ok-light:  color-mix(in oklab, #22c55e 80%, white);
--n-warn:      #f0b95c;   /* канон color-warning, темна */
--n-text-on-warn: #111111;   /* v1.1.6.363: тло тут СВІТЛЕ — потрібен темний текст (10.61:1) */
--n-warn-soft: color-mix(in oklab, #f5c366 12%, transparent);
--n-danger:    #ef4444;
--n-danger-soft: color-mix(in oklab, #ef4444 12%, transparent);
--n-info:      #22d3ee;
--n-info-soft: color-mix(in oklab, #22d3ee 12%, transparent);
--n-hp:        #22c55e;
--n-hp-dim:    color-mix(in oklab, #22c55e 12%, transparent);
--n-gold:      #f5c366;
--n-gold-dim:  color-mix(in oklab, #f5c366 12%, transparent);
--n-lav:       #a78bfa;
--n-lav-dim:   color-mix(in oklab, #a78bfa 12%, transparent);
--n-cyan:      #22d3ee;
--n-cyan-dim:  color-mix(in oklab, #22d3ee 12%, transparent);
--n-pink:      #f472b6;
--n-pink-dim:  color-mix(in oklab, #f472b6 12%, transparent);
--n-xp:        #3b82f6;
--n-cur-gold:    #f5c366;
--n-cur-crystal: #22d3ee;
--n-cur-ducat:   #ef6443;
--n-cur-pos:     #22c55e;
--n-cur-neg:     #ef4444;
--n-sh-sm:   0 1px 3px rgba(0, 0, 0, 0.40);
--n-sh-md:   0 4px 16px rgba(0, 0, 0, 0.50);
--n-sh-lg:   0 16px 48px rgba(0, 0, 0, 0.65);
--n-sh-overlay: 0 24px 64px rgba(0, 0, 0, 0.75);   /* v1.1.6.292, див. світлу схему */
--n-sh-glow: 0 0 0 1px var(--n-accent-soft), 0 12px 32px -8px var(--n-accent-glow);
/* v1.1.5.163: scrim/overlay — темна тема трохи менш непрозора, бо фон уже темний. */
--n-scrim-soft:   rgba(0, 0, 0, 0.45);
--n-scrim:        rgb(3 6 12 / 0.62);   /* канон color-scrim, темна */
--n-scrim-strong: rgba(0, 0, 0, 0.82);
--n-shadow-soft:     var(--n-sh-md);
--n-shadow-elevated: var(--n-sh-lg);
--n-bg-glow-top:    color-mix(in oklab, var(--n-accent) 6%, transparent);
--n-bg-glow-bottom: color-mix(in oklab, var(--n-accent) 4%, transparent);
--n-gradient-accent: linear-gradient(135deg, var(--n-accent), var(--n-gold));
--n-accent-koloplus: #4d94ff;   /* v1.1.6.895: канон — синій бренду R3 */
--n-accent-clay:    #e45f3a;   /* канон */
--n-accent-sage:    #64926d;   /* канон */
--n-accent-amber:   #d99614;   /* канон */
--n-accent-plum:    #9a4d8c;   /* канон */
--n-accent-blue:    #2369ff;   /* канон */
--n-accent-emerald: #18a66d;   /* канон */
--n-accent-violet:  #7653f6;   /* канон */
--n-accent-rose:    #e0568c;   /* канон */
--n-accent-cyan:    #149cb8;   /* канон */
--n-accent-coral:   #ef6d58;   /* канон */
--n-rarity-common:    #a89a8a;
--n-rarity-uncommon:  #84b074;
--n-rarity-rare:      #7a9cc0;
--n-rarity-epic:      #c990b6;
--n-rarity-legendary: #f5c366;
--n-rarity-mythic:    #ef4444;
}
/* v1.1.6.293 (REV46, CANON-STEP-10): десять акцентів — канонні значення з
   прев'ю, окремо для світлої й темної схеми, і власний on-accent під кожен.
   Канон не має одного «тексту на акценті»: на глибоких (plum/blue/violet) він
   білий, на теплих — темний #11213a. Раніше в пакеті було глобальне #ffffff,
   тобто білий текст на світло-бурштиновій кнопці — контраст ~1.8:1. */
/* v1.1.6.895: темний текст для ЯСКРАВИХ НЕ АКЦЕНТНИХ заливок (здоров'я, бірюзовий «перевірено», легендарна
   рідкість). Раніше такі місця брали --n-text-on-accent, і це «працювало», доки акцент мав темний напис; для
   синього бренду він білий, тож тут потрібен власний токен. */
:root { --n-text-on-fill: #11213a; }
:root[data-kp-accent="koloplus"] { --n-accent: #106cee; --n-text-on-accent: #ffffff; --n-accent-hover: color-mix(in oklab, var(--n-accent) 85%, #000 15%); }
:root[data-kp-accent="clay"] { --n-accent: #e45f3a; --n-text-on-accent: #11213a; --n-accent-hover: color-mix(in oklab, var(--n-accent) 88%, #fff 12%); }
:root[data-kp-accent="coral"] { --n-accent: #ef6d58; --n-text-on-accent: #11213a; --n-accent-hover: color-mix(in oklab, var(--n-accent) 88%, #fff 12%); }
:root[data-kp-accent="sage"] { --n-accent: #64926d; --n-text-on-accent: #11213a; --n-accent-hover: color-mix(in oklab, var(--n-accent) 88%, #fff 12%); }
:root[data-kp-accent="amber"] { --n-accent: #d99614; --n-text-on-accent: #11213a; --n-accent-hover: color-mix(in oklab, var(--n-accent) 88%, #fff 12%); }
:root[data-kp-accent="plum"] { --n-accent: #9a4d8c; --n-text-on-accent: #ffffff; --n-accent-hover: color-mix(in oklab, var(--n-accent) 85%, #000 15%); }
:root[data-kp-accent="blue"] { --n-accent: #2369ff; --n-text-on-accent: #ffffff; --n-accent-hover: color-mix(in oklab, var(--n-accent) 85%, #000 15%); }
:root[data-kp-accent="emerald"] { --n-accent: #18a66d; --n-text-on-accent: #11213a; --n-accent-hover: color-mix(in oklab, var(--n-accent) 88%, #fff 12%); }
:root[data-kp-accent="violet"] { --n-accent: #7653f6; --n-text-on-accent: #ffffff; --n-accent-hover: color-mix(in oklab, var(--n-accent) 85%, #000 15%); }
:root[data-kp-accent="rose"] { --n-accent: #e0568c; --n-text-on-accent: #11213a; --n-accent-hover: color-mix(in oklab, var(--n-accent) 88%, #fff 12%); }
:root[data-kp-accent="cyan"] { --n-accent: #149cb8; --n-text-on-accent: #11213a; --n-accent-hover: color-mix(in oklab, var(--n-accent) 88%, #fff 12%); }
[data-theme="dark"][data-kp-accent="koloplus"] { --n-accent: #4d94ff; --n-text-on-accent: #11213a; }
[data-theme="dark"][data-kp-accent="clay"] { --n-accent: #ff7a55; --n-text-on-accent: #11213a; }
[data-theme="dark"][data-kp-accent="coral"] { --n-accent: #ff8b78; --n-text-on-accent: #11213a; }
[data-theme="dark"][data-kp-accent="sage"] { --n-accent: #86b88e; --n-text-on-accent: #11213a; }
[data-theme="dark"][data-kp-accent="amber"] { --n-accent: #f6bf4a; --n-text-on-accent: #11213a; }
[data-theme="dark"][data-kp-accent="plum"] { --n-accent: #c47ab5; --n-text-on-accent: #ffffff; }
[data-theme="dark"][data-kp-accent="blue"] { --n-accent: #6ea0ff; --n-text-on-accent: #ffffff; }
[data-theme="dark"][data-kp-accent="emerald"] { --n-accent: #45c98b; --n-text-on-accent: #11213a; }
[data-theme="dark"][data-kp-accent="violet"] { --n-accent: #a68af8; --n-text-on-accent: #ffffff; }
[data-theme="dark"][data-kp-accent="rose"] { --n-accent: #f279a8; --n-text-on-accent: #11213a; }
[data-theme="dark"][data-kp-accent="cyan"] { --n-accent: #38c8e4; --n-text-on-accent: #11213a; }
:root[data-kp-accent] {
/* v1.1.6.300 (REV46): ховер акценту тепер рухається ВІД кольору тексту, а не
   завжди в чорний.
   Було `color-mix(… var(--n-accent) 85%, #000 15%)` — тобто затемнення. Це
   правильно, доки напис на кнопці білий: темніше тло → більший контраст. Але
   канон дає на теплих акцентах ТЕМНИЙ напис (#11213a), і для нього затемнення
   працює навпаки: наведення робило кнопку менш читабельною (3.0-3.3:1 при
   порозі 4.5). Проба спіймала рівно дев'ять таких місць.
   Тепер напрямок задається разом із самим акцентом, у його блоці нижче: там,
   де напис темний, ховер СВІТЛІШАЄ; там, де білий (plum/blue/violet) —
   темнішає, як і було. Тут лишається безпечне типове значення. */
--n-accent-hover:  color-mix(in oklab, var(--n-accent) 88%, #fff 12%);
--n-accent-light:  color-mix(in oklab, var(--n-accent) 40%, white);
--n-accent-soft:   color-mix(in oklab, var(--n-accent) 14%, transparent);
--n-accent-ring:   color-mix(in oklab, var(--n-accent) 28%, transparent);
--n-accent-glow:   color-mix(in oklab, var(--n-accent) 22%, transparent);
--n-accent-border: color-mix(in oklab, var(--n-accent) 35%, transparent);
}
[data-theme="dark"]:root[data-kp-accent] {
--n-accent-hover: color-mix(in oklab, var(--n-accent) 78%, #000 22%);
--n-accent-soft:  color-mix(in oklab, var(--n-accent) 18%, transparent);
--n-accent-ring:  color-mix(in oklab, var(--n-accent) 32%, transparent);
--n-accent-glow:  color-mix(in oklab, var(--n-accent) 28%, transparent);
}
:root {
--bg:        var(--n-bg);
--panel:     var(--n-bg-2);
--panel-b:   var(--n-bg-sunk);
--cell:      var(--n-bg-soft);
--cell-h:    var(--n-bg-hover);
--border:    var(--n-line);
--border-h:  var(--n-line-strong);
--text:      var(--n-text);
--text-2:    var(--n-text-soft);
--text-3:    var(--n-text-dim);
--accent:     var(--n-accent);
--accent-g:   var(--n-accent-glow);
--accent-dim: var(--n-accent-soft);
--hp:        var(--n-hp);
--hp-dim:    var(--n-hp-dim);
--gold:      var(--n-gold);
--gold-dim:  var(--n-gold-dim);
--lav:       var(--n-lav);
--lav-dim:   var(--n-lav-dim);
--cyan:      var(--n-cyan);
--cyan-dim:  var(--n-cyan-dim);
--pink:      var(--n-pink);
--pink-dim:  var(--n-pink-dim);
--red:       var(--n-danger);
--red-dim:   var(--n-danger-soft);
--ok:        var(--n-ok);
--warn:      var(--n-warn);
--err:       var(--n-danger);
--blue:      var(--n-xp);
--blue-dim:  color-mix(in oklab, var(--n-xp) 12%, transparent);
--font:      var(--n-font);
--mono:      var(--n-font-mono);
--ease:      var(--n-ease);
--r:         var(--n-r);
--r-md:      var(--n-r-md);
}
@media (prefers-reduced-motion: reduce) {
:root {
--n-dur-fast: 0ms;
--n-dur-base: 0ms;
--n-dur-slow: 0ms;
}
}
@media (max-width: 900px) {
:root { --kp-header-h: 62px; }
}
@media (max-width: 640px) {
:root { --kp-header-h: 58px; }
}
/* 1.1.7.028: РОЗМІРИ ЗАГОЛОВКІВ СТОРІНОК — з канону R8.94, а не з кожної сторінки окремо.
   Обмір 190 сторінок показав 12 різних розмірів заголовка сторінки (15, 18, 23, 24 … 42 px) і чотири насичення
   (700/800/850/900); у каноні кожен h1 має 700, а звичайна «голова сторінки» — 22 px на широкому екрані й 20 px
   на телефоні. Сторінки-вітрини канону мають більший герой (ринок і крамниця 30, квести 32, акаунт 38,
   оголошення й спільноти 42) — ці розміри лишаються у своїх сторінок. */
:root { --kp-title-page: 22px; --kp-title-weight: 700; }
@media (max-width: 640px) {
:root { --kp-title-page: 20px; }
}
/* v1.1.5.184: dark-перекриття токенів, що «протікали» з ':root, :root[data-theme=light]'
   блоків через :root-частину (рівна специфічність, але dark-блок їх не перевизначав).
   Симптом: невидимі заголовки на темній темі (--n-text-strong=#0e0c0a на чорному).
   Значення = dark-палітра з чистого :root вище. */
[data-theme="dark"] {
  --n-danger-deep: #8f1715;
  --n-gold-deep: #7c5820;
  --n-grid-rgb: 255,255,255;
  --n-hi: 255,255,255;
  --n-info-deep: #2a5a67;
  --n-ink: 0,0,0;
  --n-ink-deep: 5,7,6;
  --n-ink-panel: 12,14,12;
  --n-line-rgb: 255,255,255;
  --n-metal: #0e0f16;
  --n-text-strong: #ffffff;   /* канонна гама, темна схема */
  --n-violet: #a78bfa;
}

/* ============================================================================
   v1.1.5.880 (UI-runway крок 1) — АЛІАСИ ВЖИВАНИХ, АЛЕ НЕ ОГОЛОШЕНИХ ТОКЕНІВ.

   Вимірювання перед правкою: у темі вживалось 146 токенів `--n-*`, з них 14 НЕ
   були оголошені ніде. Це не косметика: 16 вживань стояли БЕЗ фолбеку
   (`var(--n-surface)`), тобто у браузері давали порожнє значення — властивість
   просто ігнорувалась. Решта мали фолбек виду `var(--n-surface-2, #1a1f29)`, і
   це гірше за поламану верстку: зашитий колір проходив повз усі перевірки
   токен-політики, бо синтаксично це `var()`.

   Тут НЕ вигадано жодного нового кольору — кожен аліас указує на канонічний
   токен, що вже існує. Тому вони автоматично слухаються теми (light/dark) і
   акцентів: аліас успадковує те, на що показує.

   Канонічними лишаються праві частини; ці імена — сумісність із наявною
   розміткою, а не другий набір.
   ============================================================================ */
:root {
  --n-surface:      var(--n-surface-2);
  --n-surface-3:    var(--n-bg-3);
  --n-bg-elevated:  var(--n-bg-2);
  --n-panel:        rgb(var(--n-ink-panel));
  --n-cell:         var(--n-surface-2);
  --n-muted:        var(--n-text-mute);
  --n-text-muted:   var(--n-text-mute);
  --n-text-2:       var(--n-text-soft);
  --n-text-3:       var(--n-text-dim);
  --n-good:         var(--n-ok);
  --n-good-soft:    var(--n-ok-soft);
  --n-shadow:       var(--n-shadow-soft);
  --n-radius:       var(--n-r-md);
  --n-lh-normal:    var(--n-lh);

  /* Layout-сітка канонічного макета (KPLUS_CANONICAL_UI_TEMPLATE_v1). Значення взято
     з макета БЕЗ змін — щоб перший екран редизайну не починався з розбіжності. У темі
     вони ще не вживаються: це підготовка, а не мертвий код. */
  --n-container:    1320px;
  --n-content:      880px;
  --n-rail:         320px;
}

/* ============================================================================
   v1.1.6.284 (REV46, CANON-STEP-1) — ПОВНИЙ КОНТРАКТ ТОКЕНІВ КАНОНУ.

   Вимірювання ПЕРЕД правкою (css_canon_probe.mjs проти 1.1.6.283):
     • канон (kolo_plus_new_theme_preview_CANON_REV6.html) оголошує 81 токен
       `--kp-*` (плюс 3 `--kp-preview-*` — це риштування самого прев'ю, у
       продукті їм місця немає, тому сюди НЕ переносяться);
     • пакет оголошував 55, з них збігалися з каноном 16;
     • 44 канонні імені не існували в пакеті ВЗАГАЛІ;
     • 16 імен `--kp-*` вживались, не будучи оголошеними ніде.

   Блок 1 нижче — беззмінні (theme-invariant) величини канону. Споживачів у
   них поки НЕМАЄ, тож додавання не змінює жодного пікселя: змінюється лише
   те, що на них ВЖЕ МОЖНА спиратися, коли модуль переходить на канон.

   Про rem: `html` у base.css не перевизначає font-size, отже 1rem = 16px, і
   пари 16px↔1rem / 24px↔1.5rem — та сама обчислена величина, а не «схожа».
   ============================================================================ */
:root {
  /* — розкладка ————————————————————————————————————————————————— */
  --kp-layout-content-max: 760px;
  --kp-layout-wide-max:    1080px;
  --kp-layout-xl-max:      1200px;
  --kp-layout-full-max:    100%;
  --kp-layout-rail-min:    300px;

  /* — крок відступів (канон: 0…8 + жолоб) ——————————————————————— */
  --kp-space-0: 0;
  --kp-space-7: 3rem;
  --kp-space-8: 4rem;
  --kp-space-gutter: 1.5rem;

  /* — шрифти ————————————————————————————————————————————————————
     Канон називає ці три сімʼї, але пакет уже вантажить Inter / Chakra Petch /
     JetBrains Mono у head.php. Підставляти сюди `system-ui` означало б тихо
     розшрифтувати сайт, тому канонні ІМЕНА показують на чинні `--n-*`: контракт
     зʼявляється, гарнітура лишається тією, яку реально завантажено. */
  --kp-font-body:    var(--n-font);
  --kp-font-display: var(--n-font-display);
  --kp-font-mono:    ui-monospace, SFMono-Regular, Menlo, monospace; /* 1.1.7.027: справжній моно — код, коди, ідентифікатори */

  /* — типографічна шкала —————————————————————————————————————— */
  --kp-text-sm:  0.875rem;
  --kp-text-md:  1rem;
  --kp-text-xl:  1.5rem;
  --kp-text-2xl: 2rem;
  --kp-leading-tight:  1.2;
  --kp-leading-normal: 1.5;
  --kp-leading-loose:  1.75;
  --kp-weight-normal: 400;
  --kp-weight-medium: 500;

  /* — контроли —————————————————————————————————————————————————— */
  --kp-control-height-sm: 32px;
  --kp-control-height-lg: 48px;
  --kp-target-min: 44px;
  /* Каталог (DesignTokens, група «дотик») обіцяє tap-min — те саме число, що й target-min. */
  --kp-tap-min: 44px;

  /* — радіуси ———————————————————————————————————————————————————— */
  --kp-radius-xs: 2px;
  --kp-radius-xl: 20px;

  /* — тіні (канон дає рівно чотири) ————————————————————————————— */
  /* v1.1.6.292 (REV46, CANON-STEP-9): ОДНА МОВА ТІНЕЙ.
     Було так: --kp-shadow-sm показував на --n-sh-sm (а отже знав про темну тему),
     а md/lg/overlay стояли канонними літералами й лишались однаковими на обох
     темах. Тобто одна шкала поводилась двома різними способами — я сам це заніс
     у .284 і не помітив, доки не звів тіні докупи.
     Геометрія збігається сама собою: --n-sh-md це 0 4px 12px, канонний md — теж
     0 4px 12px; lg в обох 0 12px 32px. Різниця лише в прозорості й у тому, що
     --n- має окремі значення для темної теми (там тінь мусить бути важчою, інакше
     її не видно на темній поверхні).
     Тому типово всі чотири йдуть через --n- драбину, а канонні літерали живуть
     під перемикачем нижче — як палітра. */
  /* Каталог обіцяє явне «без тіні» — щоб не писали `none` руками там, де токен скасовує тінь. */
  --kp-shadow-none:    none;
  --kp-shadow-md:      var(--n-sh-md);
  --kp-shadow-lg:      var(--n-sh-lg);
  --kp-shadow-overlay: var(--n-sh-overlay);

  /* — кольори валют ————————————————————————————————————————————————
     v1.1.6.738 (CURRENCY-COLOR-CANON-01). Ці три значення жили ЛІТЕРАЛАМИ щонайменше
     в чотирьох модулях одночасно: quests, wallet, level, medals. Однакова семантика,
     однакове число, чотири незалежні копії — тобто чотири місця, де воно розійдеться.

     Значення взяті ДОСЛІВНО з чинних літералів, тому вигляд не змінюється жодного
     пікселя: це винесення спільного, а не перефарбування. Родина `--kp-*` канонічна;
     нових `--n-*` не додається.

     Тема не розділяється навмисно: у коді літерали не були прив'язані до схеми, і
     вигадувати темний варіант там, де його ніколи не було, означало б змінити дизайн
     під виглядом рефакторингу. */
  --kp-currency-gold:    #fbbf24;
  --kp-currency-crystal: #22d3ee;
  --kp-currency-xp:      #a855f7;

  /* — рух ———————————————————————————————————————————————————————— */
  --kp-motion-fast:   120ms;
  --kp-motion-normal: 200ms;
  --kp-motion-slow:   320ms;
  --kp-ease-standard: cubic-bezier(0.2, 0, 0, 1);
  --kp-ease-emphasis: cubic-bezier(0.3, 0, 0, 1);

  /* — z-шари ————————————————————————————————————————————————————
     Канон: base 0 → dropdown 1000 → sticky 1100 → modal 1300 → toast 1400.
     У пакеті modal=1400 і toast=1300 стоять НАВПАКИ (тост під модалкою), але
     споживачів у обох нуль, тому переставляю одразу — до появи першого. */
  /* v1.1.6.286: --kp-z-content жив у app-layout.css разом із п'ятьма мертвими
     --kp-z-*. П'ять видалено (нуль споживачів), цей має два — тож оголошений тут,
     де його бачать усі layout-и. Канонного відповідника не має: канон дає
     --kp-z-base:0, а тут потрібна саме 1 (над декоративними body::before/::after,
     які base.css кладе позаду). */
  --kp-z-content:  1;
  --kp-z-base:     0;
  --kp-z-dropdown: 1000;
  --kp-z-sticky:   1100;
  --kp-z-modal:    1300;
  --kp-z-toast:    1400;
}

/* ── Кольори канону, яких у пакеті не було ────────────────────────────────
   Канон розрізняє схеми через [data-kp-scheme]; жива тема — через
   [data-theme] на <html> (head.php). Переносяться ЗНАЧЕННЯ канону, а скоуп —
   той, який справді існує в рантаймі. Інакше блок був би мертвим. */
:root,
:root[data-theme="light"] {
  --kp-color-page:  #f3f6fb;
  --kp-color-scrim: rgb(9 14 26 / 0.55);
  --kp-color-info:       #1f5f8b;
  --kp-color-on-info:    #ffffff;
  --kp-color-on-success: #ffffff;
  --kp-color-on-warning: #ffffff;
  --kp-color-on-danger:  #ffffff;

  /* 🔴 ДЕФЕКТ, знайдений вимірюванням (не косметика).
     Було: `--kp-color-focus-ring: var(--n-focus-ring)`, а --n-focus-ring це
     `0 0 0 3px …` — рядок ТІНІ. Усі 5 споживачів пишуть
     `outline: var(--kp-focus-ring-width) solid var(--kp-color-focus-ring)`,
     тобто `outline: 2px solid 0 0 0 3px …` — невалідна декларація, яку браузер
     викидає цілком. Фокусної обводки немає в хедері (3 місця) і в
     account_shell (2 місця) — саме там, де вона потрібна для клавіатури.
     Токен з іменем `--kp-color-*` мусить тримати КОЛІР. */
  --kp-color-focus-ring: #1f5f8b;
}
[data-theme="dark"] {
  --kp-color-page:  #0c1220;
  --kp-color-scrim: rgb(3 6 12 / 0.62);
  --kp-color-info:       #6fb2e0;
  --kp-color-on-info:    #08202f;
  --kp-color-on-success: #08251a;
  --kp-color-on-warning: #2a1c05;
  --kp-color-on-danger:  #2b0b09;
  --kp-color-focus-ring: #9ccbf2;
}

/* ============================================================================
   v1.1.6.293 (REV46, CANON-STEP-10): ПЕРЕМИКАЧА БІЛЬШЕ НЕМАЄ — КАНОН ТИПОВИЙ.

   У .284 канонні кольори лежали під [data-kp-canon="1"], бо тоді їх мали лише
   ~180 вживань `--kp-*`, а решта 16 тисяч `var(--n-*)` лишалась теплою: увімкнути
   означало отримати сторінку з двома палітрами. Тепер ядро `--n-*` саме несе
   канонні значення, тож палітра одна — і прапорець став зайвим.

   Зворотний хід лишився: `KP_LEGACY_PALETTE=1` друкує <html data-kp-legacy="1">
   і повертає теплу палітру блоком нижче. Специфічність [data-kp-legacy="1"] вища
   за :root, тож він виграє незалежно від порядку.
   ============================================================================ */


/* ============================================================================
   v1.1.6.300 (REV46): --n-accent-ink — АКЦЕНТ, ЯКИМ МОЖНА ПИСАТИ.

   «М'який чип» — акцентний текст на власному 14-відсотковому тлі — це патерн
   самого канону: `.kp-badge--accent { background: var(--kp-accent-soft);
   color: var(--kp-accent) }`. Я його перевірив на всіх десяти акцентах:

     акцент   світла   темна        акцент   світла   темна
     clay      2.97     5.47        emerald   2.69     6.32
     coral     2.60     6.00        violet    4.01     5.02
     sage      3.08     5.91        rose      3.03     5.37
     amber     2.24     7.64        cyan      2.78     6.59
     blue      3.83     5.29        plum      4.54     4.59

   У СВІТЛІЙ схемі не проходить 9 акцентів із 10 (поріг WCAG AA 4.5:1). Це не
   наша похибка: прев'ю показує чипи лише на глиняному акценті, і саме він там
   на світлій картці — 2.97. У темній схемі патерн проходить скрізь.

   Тому тут ВІДХИЛЕННЯ ВІД ПРЕВ'Ю, і я його називаю прямо: для тексту на м'якому
   тлі береться темніший відтінок того самого акценту — рівно настільки, щоб
   дотягнути до 4.5:1, і ні на крок далі, щоб колір лишався впізнаваним.

   Частка підібрана окремо під кожен акцент, бо єдиної не існує: теплим досить
   ~65-75%, глибоким (plum/violet/blue) треба 87-100%, а бурштиновому — 61%, бо
   він найсвітліший. Домішок — колір тексту, тож відтінок темнішає в бік тексту
   сторінки, а не в сірий.

   У ТЕМНІЙ схемі чорнило дорівнює самому акценту: там патерн і так проходить,
   і затемнювати означало б псувати те, що працює.
   ============================================================================ */
:root {
  /* Типове значення — вже затемнене (74%, як для глиняного акценту, що є типовим).
     Спокуса поставити тут `var(--n-accent)` була, але це значення-пастка: якби
     атрибут акценту колись не надрукувався, сторінка тихо повернулась би до
     нечитабельного варіанту. Фолбек має бути безпечним, а не «як було». */
  --n-accent-ink: color-mix(in srgb, var(--n-accent) 74%, var(--n-text));
}
:root[data-kp-accent="koloplus"] { --n-accent-ink: color-mix(in srgb, var(--n-accent) 87%, var(--n-text)); }
:root[data-kp-accent="clay"] { --n-accent-ink: color-mix(in srgb, var(--n-accent) 74%, var(--n-text)); }
:root[data-kp-accent="coral"] { --n-accent-ink: color-mix(in srgb, var(--n-accent) 67%, var(--n-text)); }
:root[data-kp-accent="sage"] { --n-accent-ink: color-mix(in srgb, var(--n-accent) 74%, var(--n-text)); }
:root[data-kp-accent="amber"] { --n-accent-ink: color-mix(in srgb, var(--n-accent) 61%, var(--n-text)); }
:root[data-kp-accent="plum"] { --n-accent-ink: color-mix(in srgb, var(--n-accent) 100%, var(--n-text)); }
:root[data-kp-accent="blue"] { --n-accent-ink: color-mix(in srgb, var(--n-accent) 87%, var(--n-text)); }
:root[data-kp-accent="emerald"] { --n-accent-ink: color-mix(in srgb, var(--n-accent) 67%, var(--n-text)); }
:root[data-kp-accent="violet"] { --n-accent-ink: color-mix(in srgb, var(--n-accent) 90%, var(--n-text)); }
:root[data-kp-accent="rose"] { --n-accent-ink: color-mix(in srgb, var(--n-accent) 74%, var(--n-text)); }
:root[data-kp-accent="cyan"] { --n-accent-ink: color-mix(in srgb, var(--n-accent) 68%, var(--n-text)); }
[data-theme="dark"] { --n-accent-ink: var(--n-accent); }   /* див. коментар вище */

/* ══ КАНОННІ ТОКЕНИ, ЩО ЖИЛИ У ВІДЖЕТІ ШАПКИ ═══════════════════════════════
   v1.1.6.321 (REV46).

   🔴 Ці дев'ять токенів були оголошені ЛИШЕ у widgets/main/header/header.css —
   у блоці :root всередині віджета. На них тримається 209 вживань у 63 файлах,
   і найбільше з них --kp-radius-md: 160 разів, тобто радіус половини інтерфейсу.

   Небезпека не теоретична. Шапка рендериться УМОВНО:
       <?php if ($showHeader && $theme …): ?>
   Тобто на будь-якій сторінці без шапки її CSS не підключається — і разом із ним
   зникають радіус, фокусні кільця, висота контролів, м'який акцент. Уся сторінка
   лишається без них, і ніде про це не сказано.

   Те, що це не спливло раніше, — випадковість: сторінок без шапки в продукті
   зараз мало. Але саме така конструкція дала збій v1.1.6.312: мій прохід
   перейменування не знав, що канонні токени оголошуються ще десь, окрім цього
   файла, і зробив їх циклічними (див. .320).

   Контракт має жити в одному місці — тут. У header.css лишається тільки те, що
   справді про шапку: її висота.
   ═══════════════════════════════════════════════════════════════════════════ */
:root {
  --kp-color-success: var(--n-ok);
  --kp-on-accent: var(--n-on-accent);
  --kp-accent-soft: var(--n-accent-soft);
  --kp-radius-md: var(--n-r-xs);
  --kp-focus-ring-width: 2px;
  --kp-focus-ring-offset: 2px;
  --kp-control-height-md: 40px;
  --kp-layout-shell-max: 1380px;
  --kp-layout-sidebar: 240px;
  /* Висота шапки як КОНСТАНТА оформлення. Не плутати з --kp-header-h: та
     змінна — результат виміру, який header.js пише для розкладки сторінки.
     Читати вимір усередині самої шапки не можна: у v1.1.6.309→.323 це давало
     самопідсилення, і шапка росла до цілого екрана. Канон задає шапці статичну
     висоту, і саме так тут. */
  --kp-header-min-h: 64px;
}

/* ── Стара тепла палітра (до .293). Вмикається KP_LEGACY_PALETTE=1 ─────────── */
:root[data-kp-legacy="1"],
[data-kp-legacy="1"][data-theme="light"] {
  --n-bg: #f6f3ee; --n-bg-2: #ffffff; --n-bg-soft: rgba(0,0,0,.025);
  --n-bg-sunk: rgba(0,0,0,.04); --n-text: #1a1714; --n-text-soft: #4a4540;
  --n-text-strong: #0e0c0a; --n-line: rgba(0,0,0,.07);
  --n-line-strong: rgba(0,0,0,.13); --n-line-soft: rgba(0,0,0,.04);
  --n-accent: #c4694a; --n-text-on-accent: #ffffff; --n-scrim: rgba(0,0,0,.55);
}
[data-kp-legacy="1"][data-theme="dark"] {
  --n-bg: #0a0b10; --n-bg-2: rgba(14,15,22,.95); --n-bg-soft: rgba(255,255,255,.04);
  --n-bg-sunk: rgba(8,9,14,.98); --n-text: #ddd8d0; --n-text-soft: #908a82;
  --n-text-strong: #f4f1ea; --n-line: rgba(255,255,255,.06);
  --n-line-strong: rgba(255,255,255,.13); --n-line-soft: rgba(255,255,255,.04);
  --n-accent: #ef6443; --n-text-on-accent: #ffffff; --n-scrim: rgba(0,0,0,.65);
}

/* 1.1.7.046: явні кольори пунктів випадного списку — підстраховка до color-scheme вище. */
select option,
select optgroup { background-color: var(--kp-color-surface, #15192280); color: var(--kp-color-text, #e8ecf4); }
[data-theme="dark"] select option,
[data-theme="dark"] select optgroup { background-color: #161b26; color: #e8ecf4; }
:root[data-theme="light"] select option,
:root[data-theme="light"] select optgroup { background-color: #ffffff; color: #11213a; }

/* END tokens.css */

/* START base.css */
/* v1.1.6.315 (REV46): знято !important там, де він нічого не тримає.
   Перевірка: для кожної декларації шукався конкурент — інше правило з тією
   самою властивістю, чий селектор може влучити в ТОЙ САМИЙ елемент. Ключі
   селекторів розширені класами, які реально стоять поруч у розмітці, бо два
   правила можуть цілити в один елемент через різні його класи. Крім того,
   пропущено все, де ця властивість ставиться інлайном із PHP/JS того самого
   модуля: там !important справді потрібен, щоб перебити інлайн. */
/* v1.1.6.312 (REV46): шар --n-* переведено на канонні імена.
   Обіцяно в .304 і зроблено окремо, щоб diff лишався читабельним.
   Рівність тут навіть простіша за минулий раз: канонні токени ВИЗНАЧЕНІ через
   ці ж --n-* (`--kp-color-surface: var(--n-bg-2)`), тож збіг не випадковий, а
   за побудовою. Однак я все одно звірив кожну пару після повного розкриття
   ланцюжка var() і ОКРЕМО у світлій та темній схемі — сім із семи.
   Жодне обчислене значення не змінюється. --n-* лишаються оголошеними в
   tokens.css: канонні токени спираються на них, і це шар значень під шаром імен. */
/* v1.1.6.304 (REV46): короткі аліаси кольорів замінено канонними іменами.
   Перепис панелей показав, що розсип у тлі й рамці — не в ЗНАЧЕННЯХ, а в ІМЕНАХ:
   тло малювалось як --n-bg-2 (101), --panel (76), --arena-panel (29) — три назви
   одного кольору; рамка як --n-line (172), --border (157), --arena-border (67).
   Канонні імена при цьому вживались 23 рази — тобто мова канону була в меншості
   у власному проекті.
   Замінено лише те, що ДОВЕДЕНО однакове: кожна пара звірена після повного
   розкриття ланцюжка var() і ОКРЕМО у світлій та темній схемі. Жодне обчислене
   значення не змінюється — це перейменування, не редизайн. */
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
[hidden] { display: none !important; }
html {
-webkit-text-size-adjust: 100%;
-webkit-tap-highlight-color: transparent;
text-rendering: optimizeLegibility;
scroll-behavior: smooth;
}
body {
font-family: var(--n-font);
font-size: var(--n-fs);
line-height: var(--n-lh);
font-weight: var(--n-w-reg);
color: var(--kp-color-text);
background: var(--kp-color-page);
min-height: 100vh;
letter-spacing: -0.005em;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
overflow-x: hidden;
position: relative;
transition:
background-color var(--n-dur-slow) var(--n-ease),
color           var(--n-dur-slow) var(--n-ease);
}
body::before {
content: '';
position: fixed;
inset: 0;
z-index: 0;
pointer-events: none;
background:
radial-gradient(ellipse 70% 50% at 20% 30%, var(--n-bg-glow-top),    transparent 55%),
radial-gradient(ellipse 50% 60% at 80% 70%, var(--n-bg-glow-bottom), transparent 55%);
}
body::after {
content: '';
position: fixed;
inset: 0;
z-index: 0;
pointer-events: none;
background-image:
repeating-linear-gradient(0deg,
transparent, transparent 2px,
rgba(255, 255, 255, 0.005) 2px, rgba(255, 255, 255, 0.005) 4px),
linear-gradient(rgba(255, 255, 255, 0.012) 1px, transparent 1px),
linear-gradient(90deg, rgba(255, 255, 255, 0.012) 1px, transparent 1px);
background-size: auto, 60px 60px, 60px 60px;
}
[data-theme="light"] body::after {
background-image:
repeating-linear-gradient(0deg,
transparent, transparent 2px,
rgba(0, 0, 0, 0.012) 2px, rgba(0, 0, 0, 0.012) 4px),
linear-gradient(rgba(0, 0, 0, 0.022) 1px, transparent 1px),
linear-gradient(90deg, rgba(0, 0, 0, 0.022) 1px, transparent 1px);
}
body > * { position: relative; z-index: 1; }
h1, h2, h3, h4, h5, h6 {
font-family: inherit;
font-size: var(--n-fs);
font-weight: var(--n-w-bold);
line-height: var(--n-lh-tight);
letter-spacing: -0.01em;
color: var(--kp-color-text);
}
/* 1.1.7.028: h1 без власного класу — це заголовок сторінки, а не рядок тексту. Скидання вище давало йому розмір
   звичайного тексту (15 px): «Арена», «Робота», «Можливі друзі», «#хештег», «Статистика» мали назву сторінки
   дрібнішу за кнопки. Тепер — канонний розмір голови сторінки (22/20 px, токен --kp-title-page). */
h1 { font-size: var(--kp-title-page, 22px); }
a {
color: var(--kp-accent);
text-decoration: none;
transition: color var(--n-dur-fast) var(--n-ease);
}
a:hover { color: var(--n-accent-hover); }
a:focus-visible {
outline: none;
box-shadow: var(--n-focus-ring);
border-radius: var(--n-r-xs);
}
ul, ol { list-style: none; }
button, input, select, textarea {
font-family: inherit;
font-size: var(--n-fs);
font-weight: var(--n-w-reg);
color: inherit;
}
button { cursor: pointer; background: none; border: none; }
button:disabled { cursor: not-allowed; }
/*
 * 🔴 v1.1.6.66 (ARENA-UI-P1-32). Канон давав фокусну рамку посиланням, полям і спискам — і НЕ
 * давав кнопкам. Знайдено виміром в Арені: з 11 інтерактивних елементів бойового екрана 11 не
 * мали ЖОДНОГО правила `:focus-visible`, зокрема кнопки дій бою і `button.btn-ghost`.
 *
 * Виправлено там, де живе канон, а не в Арені. Полагодити лише Арену означало б, що той самий
 * `btn-ghost` поводиться на різних екранах по-різному — тобто саме та розбіжність, проти якої
 * і робився весь A4.2.
 *
 * `:focus-visible` спрацьовує лише на клавіатурному фокусі, тож для миші вигляд не змінюється.
 */
button:focus-visible,
input:focus-visible, select:focus-visible, textarea:focus-visible {
outline: none;
box-shadow: var(--n-focus-ring);
}
img, svg, video, canvas { display: block; max-width: 100%; }
img { height: auto; }
::selection { background: var(--n-accent-soft); color: var(--kp-color-text); }
* { scrollbar-width: thin; scrollbar-color: var(--n-line-strong) transparent; }
*::-webkit-scrollbar { width: 8px; height: 8px; }
*::-webkit-scrollbar-track { background: transparent; }
*::-webkit-scrollbar-thumb {
background: var(--n-line-strong);
border-radius: var(--n-r-pill);
transition: background var(--n-dur-fast) var(--n-ease);
}
*::-webkit-scrollbar-thumb:hover { background: var(--kp-accent); }
.kp-icon {
display: inline-block;
vertical-align: -0.15em;
flex-shrink: 0;
pointer-events: none;
color: currentColor;
}
.kp-icon svg {
display: block;
width: 100%;
height: 100%;
fill: none;
stroke: currentColor;
}
@media print {
body::before, body::after { display: none; }
body { background: white !important; color: black; }
}
.kp-item-icon {
display: inline-block;
vertical-align: middle;
object-fit: contain;
image-rendering: -webkit-optimize-contrast;
}
.kp-item-icon--emoji {
display: inline-flex;
align-items: center;
justify-content: center;
line-height: 1;
}
.kp-quick-heal__iconwrap {
display: inline-flex;
align-items: center;
justify-content: center;
width: 20px;
height: 20px;
line-height: 1;
}
.kp-quick-heal__iconwrap .kp-item-icon {
width: 20px;
height: 20px;
}
.kp-shop-card__ico img.kp-item-icon {
width: 96px;
height: 96px;
filter: drop-shadow(0 4px 12px color-mix(in srgb, var(--kp-accent) 30%, transparent));
}
@media (max-width: 480px) {
.kp-shop-card__ico img.kp-item-icon {
width: 72px;
height: 72px;
}
}

/* ─────────────────────────────────────────────────────────────────────────────
 * v1.1.5.999: БЛОКУВАННЯ ПРОКРУТКИ ПІД ВІДКРИТИМИ ПАНЕЛЯМИ.
 *
 * Гейт №111 знайшов два класи, які JS ставить на кореневі елементи, а CSS про них не знав:
 *   • `right-slider-open` → на `<html>` (права панель);
 *   • `kp-header-notifications-open` → на `<body>` (сповіщення в шапці).
 *
 * Самі панелі показуються іншими механізмами (`.is-open` у слайдера, атрибут `hidden` у
 * сповіщень) — тому візуально все працювало. А ці два класи мали робити те, чого ніхто не
 * робив: спиняти прокрутку сторінки під панеллю.
 *
 * На телефоні різниця помітна одразу: людина гортає список сповіщень, доходить до кінця — і
 * далі починає їхати сторінка ПІД панеллю. Панель лишається на місці, а фон під нею повзе.
 *
 * `overscroll-behavior` тут важливіший за `overflow`: він спиняє саме «протікання» жесту з
 * панелі на документ.
 * ───────────────────────────────────────────────────────────────────────────── */
html.right-slider-open,
body.kp-header-notifications-open {
  overflow: hidden;
  overscroll-behavior: contain;
}

/* ─────────────────────────────────────────────────────────────────────────────
   v1.1.6.737 (ARENA-G8-REDUCED-MOTION-01): ЗМЕНШЕНИЙ РУХ — ГАРАНТІЯ ПЛАТФОРМИ.

   `tokens.css` занулює `--n-dur-*` під `prefers-reduced-motion`. Це працює рівно доти,
   доки тривалість написана ТОКЕНОМ. Кожен екран, що пише `.15s` літералом, тихо
   випадає з гарантії — і жоден статичний гейт цього не бачить, бо в тексті стилю
   медіазапит начебто є.

   Арена намагалась закрити діру ПЕРЕЛІКОМ класів у `modules/arena/arena.css`. Перелік
   протримався рівно до наступної хвилі: R8.49 перевела контроли на канонічну `.kp-btn`,
   у переліку її не було, і приймальня в справжньому Chromium знайшла те, що перелік
   пропустив — `kp-duel__share-btn`, `kp-watch__back`, `kp-share__ribbon-cta` та інші
   рухались під «зменшити рух» (0.15s і 0.2s). Перелік класів — це не гарантія, а
   зобов'язання пам'ятати; наступна хвиля забуде його так само.

   Тому правило стоїть ТУТ, у платформному шарі, і діє на весь документ. Один рядок
   замість переліку, який мусить наздоганяти кожен новий клас.

   `!important` тут не борг оформлення, а спосіб перемогти сторінкові стилі, які йдуть у
   каскаді ПІЗНІШЕ. Це стандартний спосіб для reduced-motion і єдиний, що не залежить від
   порядку підключення файлів. Бюджет `!important` Арени він не чіпає — цей файл не
   входить у вимір `arena_canon_check`.

   МЕЖА: рух зупиняється ПОВНІСТЮ, а не сповільнюється. Для спінера це означає статичну
   іконку. Свідомо: платформа вже так поводиться з токенними тривалостями (0ms), і два
   різні трактування одного налаштування були б гіршими за одне суворе.
   ───────────────────────────────────────────────────────────────────────────── */
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation: none !important;
    transition: none !important;
    scroll-behavior: auto !important;
  }
}

/* ═══ v1.1.6.892 — Brand R2: знак бренду як зображення ═════════════════════════════════════
   Текстовий знак «K+» замінено кольоровою емблемою з /assets/brand/koloplus/. Модифікатор
   `kp-brandmark--img` знімає з рамки знака фон-акцент, тінь і типографіку, а зображення
   заповнює рамку. !important — свідомо: канон REV6 закріплює фон знака шапки саме через
   !important, і лише так його можна перекрити для знака-зображення. */
.kp-brandmark--img { background: transparent !important; box-shadow: none !important; padding: 0 !important; overflow: hidden; line-height: 0; }
.kp-brandmark--img > .kp-brand-img { display: block; width: 100%; height: 100%; object-fit: contain; }
/* 1.1.7.041: банер «сторінка застаріла» на звичайних сторінках (ThemeManager::prependCsrfExpiredNotice) — той самий
   вигляд, що й банер результату в адмінці; стоїть у базовому стилі, бо може з'явитись на будь-якій сторінці. */
.kp-result-banner[data-kp-csrf-expired] { margin: 0 0 14px; padding: 12px 16px; border-radius: 10px; border-left: 4px solid currentColor; font-size: .92rem; line-height: 1.45; color: var(--n-err, #dc2626); background: color-mix(in oklab, var(--n-err, #dc2626) 10%, transparent); }
/* 1.1.7.042: «Підказку приховано» — повернення на ту саму сторінку після «Більше не показувати цю підказку». */
.kp-result-banner[data-kp-help-hidden-notice] { margin: 0 0 14px; padding: 12px 16px; border-radius: 10px; border-left: 4px solid var(--n-ok); font-size: .92rem; line-height: 1.45; color: var(--kp-color-text); background: color-mix(in oklab, var(--n-ok) 10%, transparent); }
.kp-result-banner[data-kp-help-hidden-notice] a { color: inherit; text-decoration: underline; }


/* 1.1.7.289 — canonical engagement glyph family.
   Shared by posts, comments, media and any host that renders the canonical widgets.
   Hosts arrange widgets but do not redefine their icons or selected state. */
/* ═══ 1.1.7.281 — KoloPlus post action icon family and comments preview ═══ */
/* All 17 source vector glyphs live in /public/assets/ui/post-actions, a single
   round-cap 24px icon system. Masks inherit the current theme accent/text. */
.kp-post-glyph {
  width: 20px; height: 20px; flex: 0 0 20px;
  display: inline-block; vertical-align: middle;
  color: inherit; background-color: currentColor;
  -webkit-mask: var(--kp-post-mask) center / contain no-repeat;
  mask: var(--kp-post-mask) center / contain no-repeat;
  pointer-events: none;
}
.kp-post-glyph--reaction-default { --kp-post-mask: url("/assets/ui/post-actions/reaction-default.svg"); }
.kp-post-glyph--reaction-love { --kp-post-mask: url("/assets/ui/post-actions/reaction-love.svg"); }
.kp-post-glyph--reaction-fire { --kp-post-mask: url("/assets/ui/post-actions/reaction-fire.svg"); }
.kp-post-glyph--reaction-funny { --kp-post-mask: url("/assets/ui/post-actions/reaction-funny.svg"); }
.kp-post-glyph--reaction-wow { --kp-post-mask: url("/assets/ui/post-actions/reaction-wow.svg"); }
.kp-post-glyph--reaction-sad { --kp-post-mask: url("/assets/ui/post-actions/reaction-sad.svg"); }
.kp-post-glyph--reaction-angry { --kp-post-mask: url("/assets/ui/post-actions/reaction-angry.svg"); }
.kp-post-glyph--reaction-top { --kp-post-mask: url("/assets/ui/post-actions/reaction-top.svg"); }
.kp-post-glyph--vote-up { --kp-post-mask: url("/assets/ui/post-actions/vote-up.svg"); }
.kp-post-glyph--vote-down { --kp-post-mask: url("/assets/ui/post-actions/vote-down.svg"); }
.kp-post-glyph--comments { --kp-post-mask: url("/assets/ui/post-actions/comments.svg"); }
.kp-post-glyph--share { --kp-post-mask: url("/assets/ui/post-actions/share.svg"); }
.kp-post-glyph--views { --kp-post-mask: url("/assets/ui/post-actions/views.svg"); }
.kp-post-glyph--earnings { --kp-post-mask: url("/assets/ui/post-actions/earnings.svg"); }
.kp-post-glyph--pin { --kp-post-mask: url("/assets/ui/post-actions/pin.svg"); }
.kp-post-glyph--open { --kp-post-mask: url("/assets/ui/post-actions/open.svg"); }
.kp-post-glyph--send { --kp-post-mask: url("/assets/ui/post-actions/send.svg"); }

/* END base.css */

/* START css/cyber-components.css */
/* v1.1.6.315 (REV46): знято !important там, де він нічого не тримає.
   Перевірка: для кожної декларації шукався конкурент — інше правило з тією
   самою властивістю, чий селектор може влучити в ТОЙ САМИЙ елемент. Ключі
   селекторів розширені класами, які реально стоять поруч у розмітці, бо два
   правила можуть цілити в один елемент через різні його класи. Крім того,
   пропущено все, де ця властивість ставиться інлайном із PHP/JS того самого
   модуля: там !important справді потрібен, щоб перебити інлайн. */
/* v1.1.6.314 (REV46): три виміряні виправлення контрасту.
   1) акцентний текст на приглушеній поверхні → чорнило акценту: 3.03 → 4.60
   2) чорнило на тлі, сильнішому за канонні 14% → звичайний текст: воно
      відкалібровано під 14%, а на 20-45% дає 3.1-4.2; звичайний текст на
      такому тлі дає 9.3-12.7, бо тло вже темне саме по собі
   3) напис на небезпечному тлі → --n-text-on-danger: 3.3 → 4.83.
      Це те саме правило, що у .299 — просто тло тут писалось як var(--red),
      а не var(--n-danger), і прохід його не бачив. Значення однакові:
      --red оголошено як var(--n-danger). */
/* v1.1.6.313 (REV46): 4 правил .cat-tab* прибрано — фільтр тепер канонна
   пігулка з css/canon-primitives.css, де ці ж імена приєднані до .pill-tab.
   Тут лишаються лише модифікатори кольору (.cat-tab--gold тощо), які канон не
   описує: вони фарбують рамку під семантику розділу й не чіпають геометрію. */
/* v1.1.6.304 (REV46): короткі аліаси кольорів замінено канонними іменами.
   Перепис панелей показав, що розсип у тлі й рамці — не в ЗНАЧЕННЯХ, а в ІМЕНАХ:
   тло малювалось як --n-bg-2 (101), --panel (76), --arena-panel (29) — три назви
   одного кольору; рамка як --n-line (172), --border (157), --arena-border (67).
   Канонні імена при цьому вживались 23 рази — тобто мова канону була в меншості
   у власному проекті.
   Замінено лише те, що ДОВЕДЕНО однакове: кожна пара звірена після повного
   розкриття ланцюжка var() і ОКРЕМО у світлій та темній схемі. Жодне обчислене
   значення не змінюється — це перейменування, не редизайн. */
/* v1.1.6.300 (REV46): акцентний текст на власному м'якому тлі переведено на
   --n-accent-ink. Патерн узятий із канону, але я перевірив його на всіх десяти
   акцентах: у світлій схемі 9 із 10 не дотягують до WCAG AA (2.24-4.01 при
   порозі 4.5). Чорнило — той самий акцент, затемнений рівно до 4.5 і ні на крок
   далі; у темній схемі воно дорівнює акценту, бо там патерн і так проходить.
   Обґрунтування з числами — у tokens.css над оголошенням токена. */
/* v1.1.6.294 (REV46, CANON-STEP-11): після переходу палітри на канон у .293
   тут лишались кольори, зашиті повз токени: 2 × білий текст на
   акцентному тлі (тепер var(--n-text-on-accent) — канон дає ТЕМНИЙ текст на
   теплих акцентах, білий давав контраст ~1.8:1) і 0 × значення старої
   палітри, які після .293 відстали від решти інтерфейсу. */
/* v1.1.6.291 (REV46, CANON-STEP-8): значення, які ВЖЕ дорівнювали кроку
   канонної шкали, отримали її ім'я: радіус 1, pill 0,
   відступи 19, вага 5. Жодне число не змінено — це
   перейменування, не редизайн. Значення поза шкалою лишились літералами
   свідомо: прев'ю канону саме вживає 10/14/6px для падінгів компонентів,
   тож стягувати їх до шкали означало б відійти ВІД прев'ю, а не до нього. */
/* v1.1.6.290 (REV46, CANON-STEP-7): 28 значень font-size переведено на
   канонну шкалу (--kp-text-xs/sm/md/lg/xl/2xl). Правило: найближчий крок,
   і лише коли зміна ≤12%%. Все, що далі (герой-цифри 3-5rem, мікропідписи
   <0.6rem), лишилось літералом — це не текстова шкала. Перелік: _meta/. */
/* v1.1.6.288 (REV46, CANON-STEP-5): прибрано 0 правил цілком і мертві
   частини селектора ще в 1 (аліас-імена, яких немає в розмітці — напр.
   `.admin-sub, .adm-header__sub` → лишається лише живе). Перевірено проти 37,9 МБ
   php/js/html усього деплою; динамічно зібрані імена виключені окремо.
   Перелік: _meta/REV46_dead_selectors.json. */
.hero { padding: 36px 0 28px; text-align: center; }
.hero__eyebrow {
font-family: var(--mono);
font-size: var(--kp-text-xs);
font-weight: 800;
letter-spacing: .15em;
text-transform: uppercase;
color: var(--kp-accent);
margin-bottom: 10px;
display: flex;
align-items: center;
justify-content: center;
gap: var(--kp-space-2);
}
.hero__eyebrow::before, .hero__eyebrow::after {
content: '';
width: 28px;
height: 1px;
background: var(--kp-accent);
opacity: .4;
}
.hero__title {
font-family: var(--mono);
font-size: var(--kp-text-2xl);
font-weight: 800;
letter-spacing: -.01em;
line-height: 1.1;
margin-bottom: 10px;
color: var(--kp-color-text);
}
.hero__title span { color: var(--kp-accent); }
.hero__sub {
font-size: var(--kp-text-sm);
color: var(--kp-color-text-muted);
max-width: 480px;
margin: 0 auto;
line-height: 1.5;
}
@media (max-width: 540px) {
.hero { padding: var(--kp-space-2) 0 14px; }
.hero__eyebrow {
font-size: .6rem;
margin-bottom: 6px;
}
.hero__eyebrow::before, .hero__eyebrow::after { width: 16px; }
.hero__title {
font-size: var(--kp-text-xl);
line-height: 1.15;
margin-bottom: 6px;
}
.hero__sub {
font-size: var(--kp-text-xs);
line-height: 1.4;
}
}
.card {
background: var(--kp-color-surface);
border: 1px solid var(--kp-color-border);
border-radius: var(--r);
padding: 20px;
transition: all .25s var(--ease);
}
.card--accent { position: relative; overflow: hidden; }
.card--accent::before {
content: '';
position: absolute;
top: 0; left: 0; right: 0;
height: 2px;
background: linear-gradient(90deg, transparent, var(--kp-accent), transparent);
opacity: .5;
}
.card__head {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 14px;
gap: 10px;
}
.card__title {
font-family: var(--mono);
font-size: var(--kp-text-sm);
font-weight: 800;
letter-spacing: .04em;
display: flex;
align-items: center;
gap: var(--kp-space-2);
color: var(--kp-color-text);
}
.card__sub {
font-size: var(--kp-text-xs);
color: var(--kp-color-text-muted);
margin-top: 3px;
line-height: 1.4;
}
.card__body { margin-top: 6px; }
.section-head {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 14px;
gap: 10px;
}
.section-title {
font-family: var(--mono);
font-size: var(--kp-text-sm);
font-weight: 800;
letter-spacing: .05em;
display: flex;
align-items: center;
gap: var(--kp-space-2);
color: var(--kp-color-text);
}
.cat-tab__count {
font-family: var(--mono);
font-size: .62rem;
font-weight: 800;
padding: 1px 6px;
border-radius: 6px;
background: rgba(255, 255, 255, .08);
color: var(--text-3);
}
:root[data-theme="light"] .cat-tab__count { background: rgba(0, 0, 0, .06); }
.cat-tab.is-active .cat-tab__count {
background: color-mix(in srgb, var(--kp-accent) 22%, transparent);
color: var(--kp-color-text);
}
.avatar {
width: 48px;
height: 48px;
border-radius: 50%;
background: var(--kp-color-surface-muted);
border: 1px solid var(--kp-color-border);
display: grid;
place-items: center;
font-family: var(--mono);
font-weight: 800;
color: var(--n-accent-ink);
font-size: var(--kp-text-md);
flex-shrink: 0;
overflow: hidden;
}
.avatar img { width: 100%; height: 100%; object-fit: cover; }
.avatar--lg { width:  96px; height:  96px; font-size: var(--kp-text-2xl);   border: 2px solid var(--accent-dim); }
.badge {
padding: 3px 9px;
border-radius: 7px;
font-family: var(--mono);
font-size: .6rem;
font-weight: 800;
letter-spacing: .04em;
display: inline-flex;
align-items: center;
gap: var(--kp-space-1);
}

/* v1.1.6.301 (REV46): геометрію цих трьох кнопок прибрано — вона тепер приходить
   із css/canon-primitives.css, де ті самі імена приєднані до канонного .kp-btn.
   Доти тут жила ДРУГА глобальна кнопка, що розходилась із канонною у висоті,
   радіусі й падінгу, а .btn-ghost ще й брав рамку кольору роздільника (контраст
   1.16:1 — див. .298). Лишається тільки те, чого канон не описує: світіння
   первинної кнопки й семантичні варіанти нижче. */
.btn-primary { box-shadow: 0 4px 16px var(--accent-g); }

.btn-primary:hover {
filter: brightness(1.08);
transform: translateY(-1px);
box-shadow: 0 6px 22px var(--accent-g);
}
.btn-primary:active { transform: scale(.97); }

.btn-ghost:hover {
border-color: var(--border-h);
background: var(--kp-color-surface-muted);
color: var(--kp-color-text);
}

.btn-icon:hover {
border-color: var(--border-h);
background: var(--cell-h);
color: var(--kp-color-text);
}
.btn-icon.is-active {
border-color: color-mix(in srgb, var(--kp-accent) 35%, transparent);
background: var(--accent-dim);
color: var(--kp-accent);
}
.form-card {
background: var(--kp-color-surface);
border: 1px solid var(--kp-color-border);
border-radius: 22px;
padding: 28px;
margin-bottom: var(--kp-space-4);
position: relative;
overflow: hidden;
}
.form-card::before {
content: '';
position: absolute;
top: 0; left: 50%;
transform: translateX(-50%);
width: 160px;
height: 2px;
background: linear-gradient(90deg, transparent, var(--kp-accent), transparent);
opacity: .4;
}
.form-group { margin-bottom: 18px; }
.form-label {
display: block;
font-size: var(--kp-text-xs);
font-weight: var(--kp-weight-bold);
color: var(--kp-color-text-muted);
letter-spacing: .04em;
margin-bottom: 7px;
}
.form-label span { color: var(--kp-accent); margin-left: 3px; }
.form-input, .form-textarea, .form-select {
width: 100%;
padding: 11px 14px;
border-radius: 13px;
border: 1px solid var(--kp-color-border);
background: var(--kp-color-surface-muted);
color: var(--kp-color-text);
font-family: var(--font);
font-size: var(--kp-text-sm);
outline: none;
transition: all .2s var(--ease);
}
.form-input::placeholder, .form-textarea::placeholder { color: var(--text-3); }
.form-input:focus, .form-textarea:focus, .form-select:focus {
border-color: color-mix(in srgb, var(--kp-accent) 30%, transparent);
background: var(--cell-h);
box-shadow: 0 0 0 3px color-mix(in srgb, var(--kp-accent) 12%, transparent);
}
.form-textarea { resize: vertical; min-height: 110px; line-height: 1.55; }
@media (max-width: 540px) { }
.post-card {
background: var(--kp-color-surface);
border: 1px solid var(--kp-color-border);
border-radius: var(--r);
padding: 18px;
transition: all .25s var(--ease);
}
.post-card:hover { border-color: var(--border-h); }
.kp-link { color: var(--kp-accent); text-decoration: none; }
.kp-link:hover { text-decoration: underline; }
.btn-primary--danger {
background: var(--red);
box-shadow: 0 4px 16px var(--red-dim);
}
.btn-primary--danger:hover { background: var(--red); filter: brightness(1.08); }
.btn-primary--success {
background: var(--hp);
box-shadow: 0 4px 16px var(--hp-dim);
}
.btn-primary--warning {
background: var(--gold);
color: #1a1208;
box-shadow: 0 4px 16px var(--gold-dim);
}
.btn-glass {
padding: 9px var(--kp-space-4);
border-radius: 11px;
border: 1px solid var(--kp-color-border);
background: var(--kp-color-surface-muted);
color: var(--kp-color-text);
font-family: var(--font);
font-size: var(--kp-text-xs);
font-weight: 600;
cursor: pointer;
transition: all .2s var(--ease);
display: inline-flex;
align-items: center;
gap: 6px;
text-decoration: none;
backdrop-filter: blur(8px);
-webkit-backdrop-filter: blur(8px);
}
.btn-glass:hover {
border-color: var(--kp-accent);
background: var(--accent-dim);
color: var(--kp-accent);
}
.form-input--min180 { min-width: 180px; width: auto; }
.avatar--circle { border-radius: 50%; }
.badge--success { background: color-mix(in srgb, var(--hp)   85%, transparent); color: #fff; }
.badge--warning { background: color-mix(in srgb, var(--gold) 85%, transparent); color: #1a1208; }
.badge--warn    { background: color-mix(in srgb, var(--gold) 85%, transparent); color: #1a1208; }
.badge--danger  { background: color-mix(in srgb, var(--red)  85%, transparent); color: #fff; }
.badge--info    { background: color-mix(in srgb, var(--cyan) 85%, transparent); color: #0a1820; }
.badge--muted {
background: var(--cell-h);
color: var(--text-3);
border: 1px solid var(--kp-color-border);
}
.badge--soft {
background: var(--accent-dim);
color: var(--kp-accent);
border: 1px solid var(--n-accent-border, color-mix(in srgb, var(--kp-accent) 35%, transparent));
}
.cat-tab--accent {
background: var(--kp-accent);
color: var(--n-text-on-accent);
border-color: transparent;
}
.cat-tab--gold {
background: var(--gold-dim);
color: var(--gold);
border-color: color-mix(in srgb, var(--gold) 35%, transparent);
}
.cat-tab--success {
background: var(--hp-dim);
color: var(--hp);
border-color: color-mix(in srgb, var(--hp) 35%, transparent);
}
.cat-tab--danger {
background: var(--red-dim);
color: var(--red);
border-color: color-mix(in srgb, var(--red) 35%, transparent);
}
.cat-tab--soft {
background: var(--kp-color-surface-muted);
border-color: var(--kp-color-border);
} .btn-ghost--sm { padding: 6px var(--kp-space-3); font-size: var(--kp-text-xs); }
.kp-listings-hero {
position: relative;
background: var(--kp-color-surface);
border: 1px solid var(--kp-color-border);
border-radius: var(--r);
padding: var(--kp-space-5) var(--kp-space-5) 22px;
display: flex;
align-items: center;
justify-content: space-between;
gap: var(--kp-space-4);
flex-wrap: wrap;
}
.kp-listings-hero__corner {
position: absolute;
width: 14px;
height: 14px;
border-color: var(--kp-accent);
pointer-events: none;
}
.kp-listings-hero__corner--tl {
top: -1px; left: -1px;
border-top: 2px solid; border-left: 2px solid;
}
.kp-listings-hero__corner--tr {
top: -1px; right: -1px;
border-top: 2px solid; border-right: 2px solid;
}
.kp-listings-hero__corner--bl {
bottom: -1px; left: -1px;
border-bottom: 2px solid; border-left: 2px solid;
}
.kp-listings-hero__corner--br {
bottom: -1px; right: -1px;
border-bottom: 2px solid; border-right: 2px solid;
}
.kp-listings-hero__main {
flex: 1;
min-width: 280px;
}
.kp-listings-hero__eyebrow {
font-family: var(--mono);
font-size: var(--kp-text-xs);
font-weight: var(--kp-weight-bold);
text-transform: uppercase;
letter-spacing: .12em;
color: var(--kp-accent);
margin-bottom: 6px;
}
.kp-listings-hero__title {
font-family: var(--mono);
font-size: var(--kp-text-xl);
font-weight: 800;
color: var(--kp-color-text);
margin: 0 0 6px;
letter-spacing: .02em;
}
.kp-listings-hero__sub {
font-size: var(--kp-text-sm);
color: var(--kp-color-text-muted);
margin: 0;
line-height: 1.5;
max-width: 580px;
}
.kp-listings-hero__cta {
flex-shrink: 0;
display: flex;
flex-direction: column;
gap: 6px;
}
@media (max-width: 540px) {
.kp-listings-hero {
padding: 18px var(--kp-space-4) var(--kp-space-4);
flex-direction: column;
align-items: stretch;
}
.kp-listings-hero__title {
font-size: 1.3rem;
}
.kp-listings-hero__cta {
flex-direction: row;
flex-wrap: wrap;
}
.kp-listings-hero__cta > * {
flex: 1 1 calc(50% - 4px);
}
}
.kp-comm-view-card {
background: var(--kp-color-surface);
border: 1px solid var(--kp-color-border);
border-radius: var(--r-md, 12px);
padding: var(--kp-space-4);
}
.kp-comm-view-card__head {
display: flex;
align-items: center;
justify-content: space-between;
gap: var(--kp-space-2);
margin-bottom: var(--kp-space-3);
}
.kp-comm-view-card__title {
font-family: var(--mono);
font-size: var(--kp-text-sm);
font-weight: var(--kp-weight-bold);
color: var(--kp-color-text);
margin: 0;
letter-spacing: .02em;
}
.kp-comm-view-card__link {
font-family: var(--mono);
font-size: var(--kp-text-xs);
color: var(--text-3);
text-decoration: none;
white-space: nowrap;
}
.kp-comm-view-card__link:hover { color: var(--kp-accent); }
.kp-comm-view-card__text {
font-size: var(--kp-text-sm);
color: var(--kp-color-text-muted);
line-height: 1.55;
margin: 0;
}
.kp-comm-view-card__cta {
width: 100%;
margin-top: 10px;
font-family: var(--mono);
font-size: var(--kp-text-xs) !important;
}
.kp-comm-empty {
grid-column: 1 / -1;
text-align: center;
padding: 40px 20px;
color: var(--text-3);
background: var(--kp-color-surface);
border: 1px dashed var(--kp-color-border);
border-radius: var(--r);
}
.kp-comm-empty__icon {
font-size: 3rem;
margin-bottom: 10px;
opacity: .6;
}
.kp-comm-empty__title {
font-family: var(--mono);
font-size: var(--kp-text-md);
font-weight: var(--kp-weight-bold);
color: var(--kp-color-text-muted);
margin-bottom: 6px;
}
.kp-comm-empty__sub {
font-size: var(--kp-text-sm);
line-height: 1.5;
max-width: 400px;
margin: 0 auto;
}
.kp-comm-create-input {
width: 100%;
padding: var(--n-s-3, 12px) var(--n-s-4, 16px);
border-radius: var(--r-md, 12px);
border: 1px solid var(--kp-color-border);
background: var(--kp-color-surface-muted);
color: var(--kp-color-text);
font-family: var(--font);
font-size: var(--kp-text-md);
outline: none;
transition: all .2s var(--ease);
}
.kp-comm-create-input::placeholder {
color: var(--text-3);
}
.kp-comm-create-input:focus {
border-color: color-mix(in srgb, var(--kp-accent) 35%, transparent);
background: var(--cell-h);
box-shadow: 0 0 0 3px color-mix(in srgb, var(--kp-accent) 8%, transparent);
}
.kp-comm-create-input--area {
resize: vertical;
min-height: 100px;
font-family: var(--font);
line-height: 1.55;
}
/* END css/cyber-components.css */

/* START css/kp-form.css */
/* v1.1.6.304 (REV46): короткі аліаси кольорів замінено канонними іменами.
   Перепис панелей показав, що розсип у тлі й рамці — не в ЗНАЧЕННЯХ, а в ІМЕНАХ:
   тло малювалось як --n-bg-2 (101), --panel (76), --arena-panel (29) — три назви
   одного кольору; рамка як --n-line (172), --border (157), --arena-border (67).
   Канонні імена при цьому вживались 23 рази — тобто мова канону була в меншості
   у власному проекті.
   Замінено лише те, що ДОВЕДЕНО однакове: кожна пара звірена після повного
   розкриття ланцюжка var() і ОКРЕМО у світлій та темній схемі. Жодне обчислене
   значення не змінюється — це перейменування, не редизайн. */
/* v1.1.6.291 (REV46, CANON-STEP-8): значення, які ВЖЕ дорівнювали кроку
   канонної шкали, отримали її ім'я: радіус 2, pill 0,
   відступи 13, вага 3. Жодне число не змінено — це
   перейменування, не редизайн. Значення поза шкалою лишились літералами
   свідомо: прев'ю канону саме вживає 10/14/6px для падінгів компонентів,
   тож стягувати їх до шкали означало б відійти ВІД прев'ю, а не до нього. */
/* v1.1.6.290 (REV46, CANON-STEP-7): 11 значень font-size переведено на
   канонну шкалу (--kp-text-xs/sm/md/lg/xl/2xl). Правило: найближчий крок,
   і лише коли зміна ≤12%%. Все, що далі (герой-цифри 3-5rem, мікропідписи
   <0.6rem), лишилось літералом — це не текстова шкала. Перелік: _meta/. */
/* ============================================================================
 * kp-form.css — v1.1.5.455 (REV16 L452R16-P1-004)
 * СПІЛЬНІ класи форм створення/редагування. Раніше kp-comm-create-* жили ЛИШЕ
 * в modules/communities/communities.css, а listings-форми (які вантажать тільки
 * модуль 'listings') покладались на ПРИХОВАНУ залежність — у проді 19 класів
 * лишались без стилів. Тепер вони тут, глобально через style.css @import;
 * обидва модулі споживають один канон. НЕ дублювати назад у модулях.
 * ============================================================================ */

.kp-comm-create-form {
  display: flex;
  flex-direction: column;
  gap: 18px;
}
/* ─── SECTION ────────────────────────────────────────────────────────── */
.kp-comm-create-section {
  background: var(--kp-color-surface);
  border: 1px solid var(--kp-color-border);
  border-radius: var(--r);
  padding: 18px 20px;
}
.kp-comm-create-section__head {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-bottom: var(--kp-space-3);
}
.kp-comm-create-section__num {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 26px;
  height: 26px;
  padding: 0 6px;
  border-radius: var(--kp-radius-md);
  background: var(--accent-dim);
  color: var(--kp-accent);
  font-family: var(--mono);
  font-size: var(--kp-text-xs);
  font-weight: 800;
  letter-spacing: .04em;
  border: 1px solid color-mix(in srgb, var(--kp-accent) 30%, transparent);
}
.kp-comm-create-section__label {
  font-family: var(--mono);
  font-size: var(--kp-text-sm);
  font-weight: var(--kp-weight-bold);
  color: var(--kp-color-text);
  letter-spacing: .03em;
  text-transform: uppercase;
}
.kp-comm-create-section__req {
  color: var(--kp-accent);
  font-weight: 800;
  font-size: var(--kp-text-md);
  margin-left: -4px;
}
.kp-comm-create-hint {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--kp-space-2);
  margin-top: 6px;
  font-size: var(--kp-text-xs);
  color: var(--text-3);
}
.kp-comm-create-counter {
  font-family: var(--mono);
  font-size: var(--kp-text-xs);
  color: var(--text-3);
  white-space: nowrap;
}
.kp-comm-create-counter.is-warn { color: var(--gold); }
.kp-comm-create-counter.is-err  { color: var(--red, #ef4444); font-weight: var(--kp-weight-bold); }
/* ─── TYPE SELECTOR (radio cards) ────────────────────────────────────── */
.kp-comm-create-types {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: var(--kp-space-2);
}
.kp-comm-create-type {
  display: grid;
  grid-template-columns: 36px 1fr 24px;
  gap: 10px;
  align-items: center;
  padding: var(--kp-space-3);
  border-radius: var(--kp-radius-lg);
  background: var(--kp-color-surface-muted);
  border: 1px solid var(--kp-color-border);
  cursor: pointer;
  transition: all .15s var(--ease);
}
.kp-comm-create-type:hover {
  border-color: var(--border-h);
  background: var(--cell-h);
}
.kp-comm-create-type input[type="radio"] {
  position: absolute;
  opacity: 0;
  pointer-events: none;
}
.kp-comm-create-type__icon {
  width: 36px;
  height: 36px;
  border-radius: 10px;
  background: var(--kp-color-surface);
  border: 1px solid var(--kp-color-border);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: var(--kp-text-lg);
}
.kp-comm-create-type__name {
  font-family: var(--mono);
  font-size: var(--kp-text-sm);
  font-weight: var(--kp-weight-bold);
  color: var(--kp-color-text);
  margin-bottom: 2px;
}
.kp-comm-create-type__desc {
  font-size: var(--kp-text-xs);
  color: var(--text-3);
  line-height: 1.4;
}
.kp-comm-create-type__check {
  width: 22px;
  height: 22px;
  border-radius: 50%;
  background: transparent;
  border: 1.5px solid var(--border-h);
  color: transparent;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: var(--kp-text-xs);
  font-weight: 800;
  transition: all .15s var(--ease);
}
.kp-comm-create-type.is-checked {
  border-color: var(--kp-accent);
  background: color-mix(in srgb, var(--kp-accent) 8%, var(--kp-color-surface-muted));
  box-shadow: 0 0 0 1px color-mix(in srgb, var(--kp-accent) 30%, transparent),
              0 4px 14px color-mix(in srgb, var(--kp-accent) 12%, transparent);
}
.kp-comm-create-type.is-checked .kp-comm-create-type__icon {
  background: var(--accent-dim);
  border-color: color-mix(in srgb, var(--kp-accent) 35%, transparent);
}
.kp-comm-create-type.is-checked .kp-comm-create-type__check {
  background: var(--kp-accent);
  border-color: var(--kp-accent);
  color: var(--n-text-on-accent);
}
.kp-comm-create-type.is-checked .kp-comm-create-type__name {
  color: var(--kp-accent);
}
/* ─── ACTIONS FOOTER ─────────────────────────────────────────────────── */
.kp-comm-create-actions {
  display: flex;
  gap: 10px;
  align-items: center;
  justify-content: space-between;
  padding: var(--kp-space-1) var(--kp-space-1) 0;
}
.kp-comm-create-submit {
  font-family: var(--mono) !important;
  letter-spacing: .04em;
  font-size: var(--kp-text-sm) !important;
  padding: var(--kp-space-3) var(--kp-space-5)!important;
}
.kp-comm-create-submit:disabled {
  opacity: .6;
  cursor: not-allowed;
}
.kp-comm-create-note {
  text-align: center;
  font-size: var(--kp-text-xs);
  color: var(--text-3);
  font-style: italic;
  margin: var(--kp-space-2) 0 0;
  line-height: 1.5;
}
/* v1.1.5.460 (REV18 P1-007): ці правила БУЛИ всередині @media(max-width:540px) у communities.css,
   а v455-переніс витягнув їх БЕЗ обгортки → column-reverse/width:100% застосовувались на ДЕСКТОПІ.
   Повертаємо media-обгортку. */
@media (max-width: 540px) {
  .kp-comm-create-section {
    padding: 14px var(--kp-space-4);
  }
  .kp-comm-create-actions {
    flex-direction: column-reverse;
    gap: var(--kp-space-2);
  }
  .kp-comm-create-actions a,
  .kp-comm-create-actions button {
    width: 100%;
  }
}
.kp-comm-create-notice{font-size:var(--fs-sm);color:var(--kp-color-text-muted);background:color-mix(in srgb,var(--kp-accent) 6%,transparent);border:1px solid color-mix(in srgb,var(--kp-accent) 25%,transparent);border-radius:var(--r);padding:10px var(--kp-space-3);margin-bottom:var(--kp-space-3);line-height:1.5}

/* v1.1.5.455 (REV16 P1-004): __body був ВЖИТИЙ у розмітці, але НЕ визначений ніде —
   контейнер тексту типу (назва+опис) у радіо-картці. Мінімальний коректний блок. */
.kp-comm-create-type__body { display: flex; flex-direction: column; gap: 2px; min-width: 0; }

/* ── v1.1.5.463 (a11y): видимий focus-visible для всіх контролів форм ──────────
   Раніше kp-form.css не мав жодного focus-стилю → клавіатурна навігація формами
   була невидима (критична a11y-діра). :focus-visible не чіпає мишу. */
.kp-comm-create-input:focus-visible,
.kp-comm-create-form input:focus-visible,
.kp-comm-create-form select:focus-visible,
.kp-comm-create-form textarea:focus-visible,
.kp-comm-create-form button:focus-visible,
.kp-comm-create-form a:focus-visible {
  outline: 2px solid var(--accent, #4a90d9);
  outline-offset: 2px;
  border-radius: var(--r-sm, 8px);
}
/* радіо-картки категорій: фокус на прихованому radio → підсвітка обгортки */
.kp-comm-create-type:focus-within {
  outline: 2px solid var(--accent, #4a90d9);
  outline-offset: 2px;
}

/* END css/kp-form.css */

/* START css/nova-bridge.css */
/* v1.1.6.312 (REV46): шар --n-* переведено на канонні імена.
   Обіцяно в .304 і зроблено окремо, щоб diff лишався читабельним.
   Рівність тут навіть простіша за минулий раз: канонні токени ВИЗНАЧЕНІ через
   ці ж --n-* (`--kp-color-surface: var(--n-bg-2)`), тож збіг не випадковий, а
   за побудовою. Однак я все одно звірив кожну пару після повного розкриття
   ланцюжка var() і ОКРЕМО у світлій та темній схемі — сім із семи.
   Жодне обчислене значення не змінюється. --n-* лишаються оголошеними в
   tokens.css: канонні токени спираються на них, і це шар значень під шаром імен. */
/* v1.1.6.292 (REV46, CANON-STEP-9): нейтральні тіні зведено до канонних
   чотирьох (lg×1).
   Смуга за радіусом розмиття: <5 → sm(2), <20 → md(12), <46 → lg(32),
   решта → overlay(64) — межі геометрично посередині між кроками канону.
   Кольорові світіння, ring-обводки, inset і none НЕ чіпано: це не висота
   поверхні. Перелік: _meta/REV46_shadows.json. */
/* v1.1.6.291 (REV46, CANON-STEP-8): значення, які ВЖЕ дорівнювали кроку
   канонної шкали, отримали її ім'я: радіус 0, pill 1,
   відступи 10, вага 1. Жодне число не змінено — це
   перейменування, не редизайн. Значення поза шкалою лишились літералами
   свідомо: прев'ю канону саме вживає 10/14/6px для падінгів компонентів,
   тож стягувати їх до шкали означало б відійти ВІД прев'ю, а не до нього. */
/* v1.1.6.290 (REV46, CANON-STEP-7): 11 значень font-size переведено на
   канонну шкалу (--kp-text-xs/sm/md/lg/xl/2xl). Правило: найближчий крок,
   і лише коли зміна ≤12%%. Все, що далі (герой-цифри 3-5rem, мікропідписи
   <0.6rem), лишилось літералом — це не текстова шкала. Перелік: _meta/. */
/* v1.1.6.289 (REV46, CANON-STEP-6): дві правки, обидві безпечні для попередження
   вище (файл несучий — не видаляти класи, що вживаються).
   1) 4 селекторів мали `html[data-theme='light']` НАЩАДКОМ :is(...) — тобто не
      збігались ніколи. Порядок виправлено, правило нарешті працює.
   2) З 6 списків селекторів прибрано 7 імен-аліасів, яких немає в жодному
      php/js/html усього деплою: .primary-btn .button-primary .danger-btn
      .attack-btn .btn-success .success-btn .button-secondary. ЖОДНЕ правило не
      видалено — у кожному з цих списків поруч стоїть живий клас (.btn-primary,
      .kp-btn--danger тощо). Тому «роздягнути сторінку» ця правка не може: набір
      правил той самий, коротший лише перелік імен. */
/* 1.1.7.028: `.kp-media-picker` ПРИБРАНО з усіх скоупів цього файлу (було 171 правил). Вікно «Додати медіа»
   має власні канонні стилі у віджеті, а старий «nova»-вигляд акаунта (ВЕЛИКІ ЛІТЕРИ з розрядкою, рамки, темні поля)
   накривав його на КОЖНІЙ сторінці створення допису. Усередині сторінок акаунта вибір аватара й далі отримує цей
   вигляд — через предка `.kp-page--account`. */
/* nova-bridge.css · v1.1.5.182
 * SCOPED порт nova SOFT-UI примітивів (base.css + cyber-components.css) ЛИШЕ для
 * перенесених nova-сторінок: account (kp-page--account) і порталованих модалок
 * (kp-media-picker, kp-crop-modal). Auth-сторінки самодостатні (auth.css).
 * НЕ глобально: селектори префіксовані — решта сайту (default-стиль) не зачеплена.
 * Джерело правил: nova v317. Токен-аліаси під скоупом. */

:is(.kp-page--account,.kp-crop-modal){--kp-page-max:1440px;--muted-color:var(--n-text-soft);--accent-color:var(--kp-accent)}

:is(.kp-page--account,.kp-crop-modal) button,
:is(.kp-page--account,.kp-crop-modal) input,
:is(.kp-page--account,.kp-crop-modal) select,
:is(.kp-page--account,.kp-crop-modal) textarea{font:inherit}
:is(.kp-page--account,.kp-crop-modal) button{color:inherit}
:is(.kp-page--account,.kp-crop-modal) input[type=text],
:is(.kp-page--account,.kp-crop-modal) input[type=email],
:is(.kp-page--account,.kp-crop-modal) input[type=password],
:is(.kp-page--account,.kp-crop-modal) input[type=number],
:is(.kp-page--account,.kp-crop-modal) input[type=search],
:is(.kp-page--account,.kp-crop-modal) input[type=url],
:is(.kp-page--account,.kp-crop-modal) select,
:is(.kp-page--account,.kp-crop-modal) textarea{width:100%;min-height:44px;border:1px solid var(--n-line-strong);border-radius:var(--n-r-sm);background:linear-gradient(180deg,rgba(var(--n-hi),.03),transparent 55%),rgba(var(--n-ink),.28);color:var(--kp-color-text);padding:0 13px;font-family:var(--n-font-body);font-weight:800}
:is(.kp-page--account,.kp-crop-modal) textarea{min-height:100px;padding-top:var(--kp-space-3);resize:vertical}
:is(.kp-page--account,.kp-crop-modal) input::placeholder,
:is(.kp-page--account,.kp-crop-modal) textarea::placeholder{color:var(--n-text-dim)}
:is(.kp-page--account,.kp-crop-modal) input:focus,
:is(.kp-page--account,.kp-crop-modal) select:focus,
:is(.kp-page--account,.kp-crop-modal) textarea:focus{outline:none;border-color:var(--kp-accent);box-shadow:0 0 0 3px var(--n-accent-ring)}
:is(.kp-page--account,.kp-crop-modal) button,
:is(.kp-page--account,.kp-crop-modal) .btn,
:is(.kp-page--account,.kp-crop-modal) .button,
:is(.kp-page--account,.kp-crop-modal) .kp-btn,
:is(.kp-page--account,.kp-crop-modal) input[type=submit],
:is(.kp-page--account,.kp-crop-modal) input[type=button],
:is(.kp-page--account,.kp-crop-modal) .btn-primary{position:relative;display:inline-flex;align-items:center;justify-content:center;gap:10px;min-height:42px;padding:0 18px;border:1px solid var(--n-line-strong);border-radius:var(--n-r-sm);color:var(--kp-color-text);background:linear-gradient(180deg,rgba(var(--n-hi),.055),transparent 45%),rgba(var(--n-hi),.035);box-shadow:inset 0 1px 0 rgba(var(--n-hi),.05);font-family:var(--n-font-display);font-size:var(--kp-text-xs);font-weight:900;letter-spacing:.06em;text-transform:uppercase;cursor:pointer;transition:transform .12s ease,border-color .15s ease,background .15s ease,color .15s ease}
:is(.kp-page--account,.kp-crop-modal) button:hover,
:is(.kp-page--account,.kp-crop-modal) .btn:hover,
:is(.kp-page--account,.kp-crop-modal) .button:hover,
:is(.kp-page--account,.kp-crop-modal) .kp-btn:hover{color:var(--n-text-strong);border-color:var(--n-line-strong)}
:is(.kp-page--account,.kp-crop-modal) button:active,
:is(.kp-page--account,.kp-crop-modal) .btn:active,
:is(.kp-page--account,.kp-crop-modal) .button:active,
:is(.kp-page--account,.kp-crop-modal) .kp-btn:active{transform:translateY(1px)}
:is(.kp-page--account,.kp-crop-modal) .btn-primary,
:is(.kp-page--account,.kp-crop-modal) .kp-btn--brand,
:is(.kp-page--account,.kp-crop-modal) .kp-btn--primary{border:0;color:var(--n-text-on-accent);background:linear-gradient(135deg,var(--kp-accent),var(--n-accent-deep));box-shadow:0 10px 24px color-mix(in srgb,var(--kp-accent) 28%, transparent)}
:is(.kp-page--account,.kp-crop-modal) .btn-danger,
:is(.kp-page--account,.kp-crop-modal) .kp-btn--danger{color:#fff4f0;background:repeating-linear-gradient(-55deg,var(--n-danger) 0 12px,#8f1715 12px 24px);box-shadow:0 0 18px rgba(225,58,50,.18)}
:is(.kp-page--account,.kp-crop-modal) .kp-btn--success{color:#dff6c2;background:linear-gradient(135deg,var(--n-hp-dim),#15240f)}
:is(.kp-page--account,.kp-crop-modal) .kp-btn--ghost,
:is(.kp-page--account,.kp-crop-modal) .kp-btn--secondary,
:is(.kp-page--account,.kp-crop-modal) .btn-secondary{border:0;background:var(--kp-color-surface-muted);box-shadow:none}
html[data-theme='light'] :is(.kp-page--account,.kp-crop-modal) .btn-danger,
html[data-theme='light'] :is(.kp-page--account,.kp-crop-modal) .kp-btn--danger{box-shadow:none}
:is(.kp-page--account,.kp-crop-modal) input:-webkit-autofill,
:is(.kp-page--account,.kp-crop-modal) input:-webkit-autofill:hover,
:is(.kp-page--account,.kp-crop-modal) input:-webkit-autofill:focus,
:is(.kp-page--account,.kp-crop-modal) input:-webkit-autofill:active,
:is(.kp-page--account,.kp-crop-modal) textarea:-webkit-autofill,
:is(.kp-page--account,.kp-crop-modal) select:-webkit-autofill{-webkit-text-fill-color:var(--kp-color-text);
  -webkit-box-shadow:0 0 0 1000px rgba(var(--n-ink),.55) inset,0 0 0 3px var(--n-accent-soft);
  box-shadow:0 0 0 1000px rgba(var(--n-ink),.55) inset;
  border-color:var(--kp-accent);
  caret-color:var(--kp-color-text);
  transition:background-color 9999s ease 0s;}
:is(.kp-page--account,.kp-crop-modal) .kp-alert,
:is(.kp-page--account,.kp-crop-modal) .alert{display:grid;grid-template-columns:auto 1fr auto;align-items:start;gap:var(--kp-space-3);padding:13px 14px;border:1px solid var(--n-line-strong);border-radius:var(--n-r-sm);background:linear-gradient(90deg,var(--n-info-soft),rgba(var(--n-ink),.18));color:var(--n-text-soft)}
:is(.kp-page--account,.kp-crop-modal) .kp-alert--success,
:is(.kp-page--account,.kp-crop-modal) .alert-success{border-color:rgba(123,163,72,.38);background:linear-gradient(90deg,var(--n-ok-soft),rgba(var(--n-ink),.18))}
:is(.kp-page--account,.kp-crop-modal) .kp-alert--danger,
:is(.kp-page--account,.kp-crop-modal) .alert-danger{border-color:rgba(225,58,50,.38);background:linear-gradient(90deg,var(--n-danger-soft),rgba(var(--n-ink),.18))}
:is(.kp-page--account,.kp-crop-modal) .kp-alert--warning,
:is(.kp-page--account,.kp-crop-modal) .alert-warning{border-color:rgba(196,145,53,.38);background:linear-gradient(90deg,var(--n-warn-soft),rgba(var(--n-ink),.18))}
:is(.kp-page--account,.kp-crop-modal) .kp-alert__title{display:block;color:var(--n-text-strong);font-family:var(--n-font-display);font-size:var(--kp-text-xs);text-transform:uppercase;letter-spacing:.08em}
:is(.kp-page--account,.kp-crop-modal) .kp-input,
:is(.kp-page--account,.kp-crop-modal) .kp-select,
:is(.kp-page--account,.kp-crop-modal) .kp-textarea,
:is(.kp-page--account,.kp-crop-modal) .form-input,
:is(.kp-page--account,.kp-crop-modal) .form-select,
:is(.kp-page--account,.kp-crop-modal) .form-textarea,
:is(.kp-page--account,.kp-crop-modal) .input,
:is(.kp-page--account,.kp-crop-modal) .admin-input,
:is(.kp-page--account,.kp-crop-modal) .kp-account-input,
:is(.kp-page--account,.kp-crop-modal) .kp-account-select,
:is(.kp-page--account,.kp-crop-modal) .kp-account-textarea,
:is(.kp-page--account,.kp-crop-modal) .kp-totp-input{width:100%;min-height:var(--kp-input-min-h);padding:var(--kp-space-2) 2px;border:0;border-bottom:1.5px solid var(--n-line-strong);border-radius:0;background:transparent;color:var(--kp-color-text);font-family:var(--n-font-body);font-size:var(--kp-input-fs);font-weight:600;line-height:1.4;transition:border-color .18s ease,background .18s ease;appearance:none;-webkit-appearance:none}
:is(.kp-page--account,.kp-crop-modal) .kp-input:hover,
:is(.kp-page--account,.kp-crop-modal) .kp-select:hover,
:is(.kp-page--account,.kp-crop-modal) .kp-textarea:hover,
:is(.kp-page--account,.kp-crop-modal) .kp-account-input:hover,
:is(.kp-page--account,.kp-crop-modal) .kp-account-select:hover,
:is(.kp-page--account,.kp-crop-modal) .kp-account-textarea:hover{border-bottom-color:color-mix(in srgb,var(--n-text-soft) 60%, transparent)}
:is(.kp-page--account,.kp-crop-modal) .kp-input:focus,
:is(.kp-page--account,.kp-crop-modal) .kp-select:focus,
:is(.kp-page--account,.kp-crop-modal) .kp-textarea:focus,
:is(.kp-page--account,.kp-crop-modal) .form-input:focus,
:is(.kp-page--account,.kp-crop-modal) .form-select:focus,
:is(.kp-page--account,.kp-crop-modal) .form-textarea:focus,
:is(.kp-page--account,.kp-crop-modal) .input:focus,
:is(.kp-page--account,.kp-crop-modal) .admin-input:focus,
:is(.kp-page--account,.kp-crop-modal) .kp-account-input:focus,
:is(.kp-page--account,.kp-crop-modal) .kp-account-select:focus,
:is(.kp-page--account,.kp-crop-modal) .kp-account-textarea:focus,
:is(.kp-page--account,.kp-crop-modal) .kp-totp-input:focus{outline:none;border-bottom-color:var(--kp-accent);box-shadow:none}
:is(.kp-page--account,.kp-crop-modal) .kp-input:disabled,
:is(.kp-page--account,.kp-crop-modal) .kp-select:disabled,
:is(.kp-page--account,.kp-crop-modal) .kp-textarea:disabled,
:is(.kp-page--account,.kp-crop-modal) .kp-input[readonly],
:is(.kp-page--account,.kp-crop-modal) .kp-textarea[readonly]{opacity:.55;cursor:not-allowed}
:is(.kp-page--account,.kp-crop-modal) .kp-input.is-error,
:is(.kp-page--account,.kp-crop-modal) .kp-select.is-error,
:is(.kp-page--account,.kp-crop-modal) .kp-textarea.is-error,
:is(.kp-page--account,.kp-crop-modal) .kp-field.is-error .kp-input,
:is(.kp-page--account,.kp-crop-modal) .kp-field.is-error .kp-select,
:is(.kp-page--account,.kp-crop-modal) .kp-field.is-error .kp-textarea{border-bottom-color:var(--n-danger);box-shadow:none}
:is(.kp-page--account,.kp-crop-modal) .kp-input.is-ok,
:is(.kp-page--account,.kp-crop-modal) .kp-select.is-ok,
:is(.kp-page--account,.kp-crop-modal) .kp-textarea.is-ok,
:is(.kp-page--account,.kp-crop-modal) .kp-field.is-ok .kp-input{border-bottom-color:var(--n-ok);box-shadow:none}
:is(.kp-page--account,.kp-crop-modal) .kp-select,
:is(.kp-page--account,.kp-crop-modal) .form-select,
:is(.kp-page--account,.kp-crop-modal) .kp-account-select{padding-right:30px;background-image:linear-gradient(45deg,transparent 50%,var(--kp-color-text-muted) 50%),linear-gradient(135deg,var(--kp-color-text-muted) 50%,transparent 50%);background-position:calc(100% - 14px) 55%,calc(100% - 9px) 55%;background-size:5px 5px,5px 5px;background-repeat:no-repeat;cursor:pointer}
:is(.kp-page--account,.kp-crop-modal) .kp-input--sm,
:is(.kp-page--account,.kp-crop-modal) .kp-select--sm{--kp-input-min-h:36px;--kp-input-fs:.85rem}
:is(.kp-page--account,.kp-crop-modal) .kp-field,
:is(.kp-page--account,.kp-crop-modal) .kp-form-field{display:grid;gap:6px;min-width:0}
:is(.kp-page--account,.kp-crop-modal) .kp-field__label,
:is(.kp-page--account,.kp-crop-modal) .kp-label{color:var(--n-text-soft);font-family:var(--n-font-display);font-size:10px;letter-spacing:.12em;text-transform:uppercase}
:is(.kp-page--account,.kp-crop-modal) .kp-field__hint,
:is(.kp-page--account,.kp-crop-modal) .kp-hint{color:var(--kp-color-text-muted);font-size:var(--kp-text-xs);line-height:1.4}
:is(.kp-page--account,.kp-crop-modal) .kp-field.is-error .kp-field__hint,
:is(.kp-page--account,.kp-crop-modal) .kp-hint.is-error{color:var(--n-danger);font-weight:800}
:is(.kp-page--account,.kp-crop-modal) .kp-field.is-ok .kp-field__hint{color:var(--n-ok);font-weight:800}
:is(.kp-page--account,.kp-crop-modal) .kp-field-row{display:grid;gap:14px}
:is(.kp-page--account,.kp-crop-modal) .kp-actions{display:flex;gap:10px;flex-wrap:wrap;align-items:center}
:is(.kp-page--account,.kp-crop-modal) .kp-actions-center{justify-content:center}
:is(.kp-page--account,.kp-crop-modal) .kp-actions-between-12{justify-content:space-between;gap:var(--kp-space-3)}
:is(.kp-page--account,.kp-crop-modal) .kp-actions-10-mt14{gap:10px;margin-top:14px}
:is(.kp-page--account,.kp-crop-modal) .kp-check,
:is(.kp-page--account,.kp-crop-modal) .kp-radio,
:is(.kp-page--account,.kp-crop-modal) .kp-switch{display:inline-flex;align-items:center;gap:11px;cursor:pointer;user-select:none;color:var(--kp-color-text);font-weight:800;font-size:var(--kp-text-sm)}
:is(.kp-page--account,.kp-crop-modal) .kp-check input,
:is(.kp-page--account,.kp-crop-modal) .kp-radio input,
:is(.kp-page--account,.kp-crop-modal) .kp-switch input{position:absolute;width:1px;height:1px;opacity:0;margin:0;pointer-events:none}
:is(.kp-page--account,.kp-crop-modal) .kp-check__box,
:is(.kp-page--account,.kp-crop-modal) .kp-radio__box{width:22px;height:22px;flex:0 0 auto;display:grid;place-items:center;border:2px solid var(--n-line-strong);border-radius:7px;background:rgba(var(--n-ink),.28);color:transparent;font-size:var(--kp-text-xs);font-weight:900;transition:all .15s ease}
:is(.kp-page--account,.kp-crop-modal) .kp-check input:checked + .kp-check__box{background:linear-gradient(135deg,var(--kp-accent),var(--n-accent-deep));border-color:transparent;color:var(--n-text-on-accent)}
:is(.kp-page--account,.kp-crop-modal) .kp-check input:checked + .kp-check__box::after{content:"✓"}
:is(.kp-page--account,.kp-crop-modal) .kp-check input:focus-visible + .kp-check__box,
:is(.kp-page--account,.kp-crop-modal) .kp-radio input:focus-visible + .kp-radio__box{box-shadow:0 0 0 3px var(--n-accent-soft)}
:is(.kp-page--account,.kp-crop-modal) .kp-check input:disabled ~ *,
:is(.kp-page--account,.kp-crop-modal) .kp-radio input:disabled ~ *{opacity:.5}
:is(.kp-page--account,.kp-crop-modal) input[type=checkbox],
:is(.kp-page--account,.kp-crop-modal) input[type=radio]{accent-color:var(--kp-accent)}
:is(.kp-page--account,.kp-crop-modal) .kp-fw-600{font-weight:600}
:is(.kp-page--account,.kp-crop-modal) .kp-aw{display:grid;grid-template-rows:auto 1fr;gap:14px;min-width:0;padding:18px;border:0;border-radius:var(--n-r-lg);background:linear-gradient(180deg,color-mix(in srgb,var(--kp-accent) 4%,transparent),transparent 24%),color-mix(in srgb,var(--kp-color-surface) 94%,transparent);box-shadow:var(--kp-shadow-lg)}
:is(.kp-page--account,.kp-crop-modal) .kp-aw__head{display:grid;grid-template-columns:auto 1fr auto;align-items:center;gap:var(--kp-space-3)}
:is(.kp-page--account,.kp-crop-modal) .kp-aw__icon{width:40px;height:40px;display:grid;place-items:center;border-radius:14px;background:var(--n-accent-soft);font-size:var(--kp-text-lg)}
:is(.kp-page--account,.kp-crop-modal) .kp-aw__title{margin:0;color:var(--n-text-strong);font-family:var(--n-font-display);font-size:var(--kp-text-sm);font-weight:800;line-height:1.25}
:is(.kp-page--account,.kp-crop-modal) .kp-aw__sub{display:block;margin-top:2px;color:var(--kp-color-text-muted);font-size:var(--kp-text-xs);line-height:1.4;font-weight:600}
:is(.kp-page--account,.kp-crop-modal) .kp-aw__state{align-self:start;padding:var(--kp-space-1) 10px;border-radius:var(--kp-radius-pill);background:var(--n-warn-soft);color:var(--n-text-soft);font-family:var(--n-font-display);font-size:10px;letter-spacing:.08em;text-transform:uppercase;white-space:nowrap}
:is(.kp-page--account,.kp-crop-modal) .kp-aw__state--done{background:var(--n-ok-soft);color:var(--n-ok)}
:is(.kp-page--account,.kp-crop-modal) .kp-aw__form{display:grid;gap:var(--kp-space-3);align-content:start}
:is(.kp-page--account,.kp-crop-modal) .kp-aw__actions{display:flex;flex-wrap:wrap;align-items:center;gap:10px;margin-top:2px}
:is(.kp-page--account,.kp-crop-modal) .kp-aw__more{color:var(--kp-color-text-muted);font-size:var(--kp-text-xs);font-weight:var(--kp-weight-bold);text-decoration:none}
:is(.kp-page--account,.kp-crop-modal) .kp-aw__more:hover{color:var(--kp-accent)}
:is(.kp-page--account,.kp-crop-modal) .kp-aw-grid{display:grid;gap:var(--kp-space-4);grid-template-columns:repeat(auto-fit,minmax(290px,1fr))}

@media(max-width:560px){
:is(.kp-page--account,.kp-crop-modal) .kp-aw{padding:15px}
:is(.kp-page--account,.kp-crop-modal) .kp-aw-grid{grid-template-columns:1fr}
}

:is(.kp-page--account,.kp-crop-modal) .kp-fw-seg{display:grid;gap:10px;grid-template-columns:1fr 1fr}
:is(.kp-page--account,.kp-crop-modal) .kp-fw-seg--3{grid-template-columns:1fr 1fr 1fr}
:is(.kp-page--account,.kp-crop-modal) .kp-fw-seg--col{grid-template-columns:1fr}

@media(max-width:420px){
:is(.kp-page--account,.kp-crop-modal) .kp-fw-seg--3{grid-template-columns:1fr}
}

:is(.kp-page--account,.kp-crop-modal) .kp-fw-opt{position:relative;display:block;cursor:pointer;min-width:0}
:is(.kp-page--account,.kp-crop-modal) .kp-fw-opt input{position:absolute;opacity:0;pointer-events:none}
:is(.kp-page--account,.kp-crop-modal) .kp-fw-opt>span{display:flex;align-items:center;justify-content:center;gap:var(--kp-space-2);min-height:44px;padding:0 var(--kp-space-3);border:1.5px solid var(--n-line-strong);border-radius:var(--n-r-sm);background:transparent;color:var(--n-text-soft);font-family:var(--n-font-body);font-weight:800;font-size:var(--kp-text-sm);line-height:1.25;text-align:center;transition:border-color .15s ease,background .15s ease,color .15s ease}
:is(.kp-page--account,.kp-crop-modal) .kp-fw-opt input:checked+span{border-color:var(--kp-accent);background:var(--n-accent-soft);color:var(--n-text-strong)}
:is(.kp-page--account,.kp-crop-modal) .kp-fw-opt input:focus-visible+span{outline:none;box-shadow:0 0 0 3px var(--n-accent-ring)}
:is(.kp-page--account,.kp-crop-modal) .kp-fw-opt input:disabled+span{opacity:.5;cursor:not-allowed}
:is(.kp-page--account,.kp-crop-modal) .kp-fw-check{display:flex;align-items:flex-start;gap:10px;cursor:pointer;color:var(--n-text-soft);font-size:var(--kp-text-sm);line-height:1.45;font-weight:600}
:is(.kp-page--account,.kp-crop-modal) .kp-fw-check input{margin:2px 0 0;width:18px;height:18px;flex:0 0 auto;accent-color:var(--kp-accent);cursor:pointer}
:is(.kp-page--account,.kp-crop-modal) .kp-fw-check>span{min-width:0}
:is(.kp-page--account,.kp-crop-modal) .kp-fw-check strong{display:block;color:var(--n-text-strong);font-weight:800}
:is(.kp-page--account,.kp-crop-modal) .kp-aw--fw [hidden]{display:none!important}

/* END css/nova-bridge.css */

/* START css/app-shell.css */
/* v1.1.6.312 (REV46): шар --n-* переведено на канонні імена.
   Обіцяно в .304 і зроблено окремо, щоб diff лишався читабельним.
   Рівність тут навіть простіша за минулий раз: канонні токени ВИЗНАЧЕНІ через
   ці ж --n-* (`--kp-color-surface: var(--n-bg-2)`), тож збіг не випадковий, а
   за побудовою. Однак я все одно звірив кожну пару після повного розкриття
   ланцюжка var() і ОКРЕМО у світлій та темній схемі — сім із семи.
   Жодне обчислене значення не змінюється. --n-* лишаються оголошеними в
   tokens.css: канонні токени спираються на них, і це шар значень під шаром імен. */
/* v1.1.6.304 (REV46): короткі аліаси кольорів замінено канонними іменами.
   Перепис панелей показав, що розсип у тлі й рамці — не в ЗНАЧЕННЯХ, а в ІМЕНАХ:
   тло малювалось як --n-bg-2 (101), --panel (76), --arena-panel (29) — три назви
   одного кольору; рамка як --n-line (172), --border (157), --arena-border (67).
   Канонні імена при цьому вживались 23 рази — тобто мова канону була в меншості
   у власному проекті.
   Замінено лише те, що ДОВЕДЕНО однакове: кожна пара звірена після повного
   розкриття ланцюжка var() і ОКРЕМО у світлій та темній схемі. Жодне обчислене
   значення не змінюється — це перейменування, не редизайн. */
/* v1.1.6.303 (REV46): радіус панелей зведено до канонного --kp-radius-lg.
   Перепис: у пакеті 690 справжніх панелей (тло + рамка + радіус, не контроли)
   і 68 різних радіусів. Найпоширеніший — уже канонний (91), далі йдуть 10px
   (86), 14px (59) і --n-r-md, який теж 14px (58): три сусідні значення для
   одного й того самого — картки. Канон для неї однозначний:
   `.kp-card { border-radius: var(--kp-radius-lg) }`.
   Контроли (кнопки, чипи, аватари, поля) з проходу виключені: там канон
   справді вживає 10-11px, і зводити їх до 12 означало б відійти ВІД прев'ю.
   `--r` (20px) теж не чіпано — це інший рівень, а не сусіднє значення. */
/* v1.1.6.291 (REV46, CANON-STEP-8): значення, які ВЖЕ дорівнювали кроку
   канонної шкали, отримали її ім'я: радіус 2, pill 0,
   відступи 4, вага 0. Жодне число не змінено — це
   перейменування, не редизайн. Значення поза шкалою лишились літералами
   свідомо: прев'ю канону саме вживає 10/14/6px для падінгів компонентів,
   тож стягувати їх до шкали означало б відійти ВІД прев'ю, а не до нього. */
/* v1.1.6.290 (REV46, CANON-STEP-7): 7 значень font-size переведено на
   канонну шкалу (--kp-text-xs/sm/md/lg/xl/2xl). Правило: найближчий крок,
   і лише коли зміна ≤12%%. Все, що далі (герой-цифри 3-5rem, мікропідписи
   <0.6rem), лишилось літералом — це не текстова шкала. Перелік: _meta/. */
/* app-shell.css · v1.1.6.286 (REV46, CANON-STEP-3)
 * ============================================================================
 * ОБОЛОНКА СТОРІНКИ — те, що мусить бути на КОЖНІЙ сторінці.
 *
 * 🔴 ЧОМУ ЦЕЙ ФАЙЛ ВЗАГАЛІ З'ЯВИВСЯ.
 *
 * ThemeManager (рядки 1397–1407) підключає РІВНО ОДИН layout-CSS через `match`:
 *   messages → messages-layout.css, auth → auth-layout.css, … default → app-layout.css
 *
 * Тобто `app-layout.css` вантажиться на layout «app» — одному з дев'яти. А в ньому
 * лежали правила, писані як загальні. На восьми інших layout-ах їх просто немає:
 *
 *  1. `html { scroll-padding-top/bottom }` — фікс v1.1.5.917 («Tab-фокус і перехід
 *     за #якорем не ставлять ціль під липку шапку й нижню навігацію»). Працював на
 *     ОДНОМУ layout-і з дев'яти. На auth, admin, profile, account, communities,
 *     listings, arena, messages ціль і далі опинялась під шапкою.
 *
 *  2. `.kp-refshare` — кнопка «поділитися сторінкою». `body_chrome.php` підключає
 *     `ref_share.php` на всіх дев'яти layout-ах, а показується вона на шляхах
 *     `/posts/ /listings/ /communities/ /arena/battle/ /media/ /guides/`
 *     (ReferralLinkBuilder::SHAREABLE_PREFIXES). З них `/listings/`, `/communities/`
 *     і `/arena/battle/` мають ВЛАСНІ layout-и → `app-layout.css` там не вантажиться
 *     → FAB не фіксований, не круглий, а панель QR вивалюється в потік сторінки.
 *     Три з шести родин маршрутів, де кнопка взагалі існує.
 *
 *  3. `.container` і базова частина `.app-page-wrapper` — через їх відсутність кожен
 *     layout переоголошував `display:flex; flex-direction:column; width:100%;
 *     margin-inline:auto` у себе. Це була не надлишковість, а КОМПЕНСАЦІЯ: видалити
 *     їх «як дублі» (перша спокуса) означало б зламати вісім layout-ів. Правильний
 *     бік — підняти базу сюди, і тоді компенсація стає непотрібною.
 *
 * ПОРЯДОК ПІДКЛЮЧЕННЯ (перевірено в ThemeManager): `style.css` друкується РАНІШЕ,
 * ніж `renderCss()` віддає layout-CSS. Специфічність `.container` і `.container--X`
 * однакова (0,1,0), тож при рівності виграє пізніший — layout. Саме тому база тут
 * безпечна: кожен із восьми layout-ів перевизначає `max-width` і `padding` у себе,
 * і його значення лишається чинним.
 *
 * ЩО СВІДОМО НЕ ПЕРЕНЕСЕНО: `.app-page-wrapper { min-height: calc(100vh − шапка −
 * підвал) }`. Воно лишилось в `app-layout.css`. Перенести — означає на восьми
 * layout-ах підняти підвал до низу екрана на коротких сторінках; зараз він стоїть
 * одразу під контентом. Це видима зміна компонування, і вона потребує рішення
 * власника, а не мого. Деталі — у RELEASE_NOTES_1_1_6_286.md.
 * ============================================================================ */

/* v1.1.5.917 — ПРОКРУТКА НЕ СТАВИТЬ ЦІЛЬ ПІД ПАНЕЛІ.
   Браузер сам прокручує до елемента у трьох випадках, і в жодному з них він не знає
   про наші фіксовані панелі: Tab-фокус на полі нижче краю екрана, перехід за #якорем,
   scrollIntoView. Обидва значення читають ті самі змінні, що пише footer.js/header.js,
   плюс safe-area: на телефоні під баром є ще смуга жестів, і вона теж перекриває.
   v1.1.6.286: переїхало з app-layout.css — там воно діяло на 1 layout із 9. */
html {
  scroll-padding-top: calc(var(--kp-header-h, 68px) + var(--n-s-2, 8px));
  scroll-padding-bottom: calc(var(--kp-bottom-nav-h, 0px) + env(safe-area-inset-bottom, 0px) + var(--n-s-3, 16px));
}

/* Базова обгортка.
   v1.1.6.290 (REV46, CANON-STEP-7): `min-height` переїхало сюди з app-layout.css.
   Доти воно діяло на layout «app» — одному з дев'яти, і на решті восьми підвал
   стояв одразу під контентом, а не внизу екрана. Саме до цього тягнулись усі ті
   `flex: 1`, які кожен layout писав у себе і які нічого не робили (body не є
   flex-контейнером). Тепер оболонка поводиться однаково скрізь — це і є «однакові
   елементи по всьому проекту».
   Дві однакові декларації — не помилка: `dvh` перекриває `vh` там, де підтримується,
   і не ламає там, де ні (адресний рядок на мобільному змінює vh, dvh — ні). */
.app-page-wrapper {
  position: relative;
  z-index: var(--kp-z-content);
  display: flex;
  flex-direction: column;
  min-height: calc(100vh - var(--kp-header-h, 60px) - var(--kp-footer-h, 80px));
  min-height: calc(100dvh - var(--kp-header-h, 60px) - var(--kp-footer-h, 80px));
}

/* Базовий контейнер. Кожен layout перевизначає max-width/padding у себе — і, як
   пояснено вище, його значення виграє за порядком підключення. */
.container {
  flex: 1;
  width: 100%;
  max-width: var(--n-content-w, 720px);
  margin-inline: auto;
  padding: var(--n-s-5, 20px) var(--n-s-3, 16px);
}

/* v1.1.5.739 — «Поділитися цією сторінкою зі своїм кодом». Лише токени (HB-001);
   без світіння (HB-002). Біле тло під QR — вимога контрасту для сканера.
   v1.1.6.286: переїхало з app-layout.css — три родини маршрутів, де кнопка
   показується, мають власні layout-и й лишались без цих правил. */
.kp-refshare { position: fixed; right: 16px; bottom: 84px; z-index: 60; }
.kp-refshare__fab {
  width: 44px; height: 44px; border-radius: 50%; cursor: pointer;
  border: 1px solid var(--kp-color-border); background: var(--kp-color-surface-muted); color: var(--kp-color-text);
  font-size: var(--kp-text-lg); display: flex; align-items: center; justify-content: center;
}
.kp-refshare__fab:hover { border-color: var(--border-h); background: var(--cell-h); }
.kp-refshare__sr { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); }
.kp-refshare__panel {
  position: absolute; right: 0; bottom: 54px; width: 268px; padding: var(--kp-space-3);
  border-radius: var(--kp-radius-lg); border: 1px solid var(--kp-color-border);
  /* v1.1.6.288: було `var(--bg-2)` — токен, якого НЕ ІСНУЄ (ані в tokens.css, ані
     деінде, ані з рантайму). `var()` без фолбеку на неоголошене — це невалідна
     декларація: браузер викидає її цілком, і панель із QR лишалась БЕЗ ТЛА,
     напівпрозорою над текстом сторінки. Я сам переніс цей рядок у .286, не
     звіривши токени — тому ловлю тут. Правильне ім'я аліаса на ту саму поверхню
     оголошене в tokens.css: --panel (= var(--kp-color-surface)). */
  background: var(--kp-color-surface);
  /* UI-293-02: Even without a mobile dock, short landscape viewports need
     internal scrolling. Reserve the 84px FAB offset, 54px panel separation,
     sticky header height, and a 12px minimum top gap. */
  box-sizing: border-box;
  max-height: max(0px, calc(100dvh - var(--kp-header-h, 64px) - 84px - 54px - 12px));
  overflow-y: auto;
  overscroll-behavior: contain;
  -webkit-overflow-scrolling: touch;
}
.kp-refshare__head { display: flex; align-items: center; justify-content: space-between; gap: var(--kp-space-2); }
.kp-refshare__head strong { font-family: var(--mono); font-size: var(--kp-text-sm); color: var(--kp-color-text); }
.kp-refshare__close { background: none; border: 0; color: var(--kp-color-text-muted); font-size: var(--kp-text-lg); cursor: pointer; }
.kp-refshare__hint { font-size: var(--kp-text-xs); color: var(--kp-color-text-muted); margin: 6px 0 10px; }
.kp-refshare__qr {
  display: flex; justify-content: center; min-height: 180px; background: #fff;
  border-radius: var(--kp-radius-lg); padding: 6px; border: 1px solid var(--kp-color-border);
}
.kp-refshare__row { display: flex; gap: 6px; margin-top: 10px; }
.kp-refshare__url {
  flex: 1; min-width: 0; font-size: var(--kp-text-xs); padding: 6px var(--kp-space-2); border-radius: var(--kp-radius-md);
  border: 1px solid var(--kp-color-border); background: var(--kp-color-surface-muted); color: var(--kp-color-text);
}
.kp-refshare__copy {
  padding: 6px 10px; border-radius: var(--kp-radius-md); cursor: pointer;
  border: 1px solid var(--kp-color-border); background: var(--kp-color-surface-muted); color: var(--kp-color-text); font-size: var(--kp-text-xs);
}
.kp-refshare__dl { display: inline-block; margin-top: var(--kp-space-2); font-size: var(--kp-text-xs); color: var(--kp-color-text-muted); }
@media (max-width: 480px) { .kp-refshare { right: 12px; bottom: 76px; } }

/* 1.1.7.151 — нагадування про перерву для неповнолітніх (власник: «Нагадування про час»). `body_chrome.php` підключає
   `_partials/wellbeing.php` на всіх дев'яти layout-ах, тож правила тут, а не в layout-CSS (див. пункт 2 вгорі).
   Картка канонічна (`.kp-card`), над нижньою навігацією, по центру; сторінку НЕ закриває і не блокує. */
.kp-break { position: fixed; left: 50%; bottom: 96px; transform: translateX(-50%); z-index: 70; width: min(420px, calc(100vw - 32px)); }
.kp-break[hidden] { display: none; }
.kp-break__card { display: flex; flex-direction: column; gap: var(--kp-space-2); padding: var(--kp-space-4); box-shadow: 0 8px 28px rgba(15, 23, 42, .18); }
.kp-break__title { font-size: var(--kp-text-md); color: var(--kp-color-text); }
.kp-break__text { margin: 0; font-size: var(--kp-text-sm); color: var(--kp-color-text-muted); }
.kp-break__ok { align-self: flex-end; }
@media (max-width: 480px) { .kp-break { bottom: 88px; } }

/* ============================================================================
 * 1.1.7.027 — СІТКА AppShell (канон R8.91: .app-grid / .main-col / data-page-grid / data-width)
 * ============================================================================
 * Бічна навігація (widgets/main/side_nav) + основна колонка + права колонка (widgets/main/user_strip — картки «Ваш K+ статус» і «Гаманець»,
 * лише для сітки main_rail). Сітку й ширину вирішує реєстр застосунку (config/page_presentation.php),
 * оболонка ставить їх у data-атрибути (layouts/app/_partials/body_chrome.php).
 *
 *   ≥1181px   бічна 240px · основна · (права 300px для main_rail)
 *   681–1180  бічна 72px лише з іконками · основна; правої немає
 *   ≤680      одна колонка; навігація — нижня панель і висувне меню
 *
 * Ширину сторінки тепер задає сітка: контейнери layout-ів (.container--profile 1200 тощо) усередині
 * основної колонки ширину не обмежують — інакше було б дві правди про ширину одної сторінки.
 */
.kp-app-grid {
  width: min(var(--kp-layout-shell-max), calc(100% - 32px));
  margin: 16px auto;
  display: grid;
  grid-template-columns: var(--kp-layout-sidebar) minmax(0, 1fr);
  gap: 16px;
  align-items: start;
}
.kp-app-grid[data-page-grid="main_rail"] {
  grid-template-columns: var(--kp-layout-sidebar) minmax(0, 1fr) var(--kp-layout-rail-min);
}
.kp-app-grid[data-width="content"] { --kp-page-width: var(--kp-layout-content-max); }
.kp-app-grid[data-width="wide"]    { --kp-page-width: var(--kp-layout-wide-max); }
.kp-app-grid[data-width="xl"]      { --kp-page-width: var(--kp-layout-xl-max); }
.kp-app-grid[data-width="full"]    { --kp-page-width: var(--kp-layout-full-max); }

.kp-main-col {
  min-width: 0;
  width: 100%;
  max-width: var(--kp-page-width, var(--kp-layout-wide-max));
  justify-self: center;
}
.kp-app-grid[data-page-grid="main_rail"] .kp-main-col { max-width: none; }
.kp-app-grid[data-page-grid="sidebar_main"] .kp-main-col,
.kp-app-grid[data-page-grid="sidebar_main_rail"] .kp-main-col { max-width: var(--kp-layout-xl-max); }

/* Обгортка й контейнер layout-у всередині колонки: без власної ширини й відступів (їх дає сітка). */
.kp-main-col > .app-page-wrapper { min-height: 0; }
.kp-main-col > .app-page-wrapper > .container {
  max-width: none;
  padding: 0;
}
/* Сторінковий примітив `.kp-page` (149 шаблонів) тримав власні 720px (--n-content-w) і бічні відступи: у колонці
   AppShell профіль ставав 696px замість канонних xl (1200). Ширину тепер дає сітка (data-width з реєстру). */
.kp-main-col .kp-page {
  max-width: none;
  margin-inline: 0;
  padding: 0 0 var(--n-s-5, 20px);
}
/* 1.1.7.046: гість (без сітки AppShell) — та сама ширина сторінки з реєстру, що й колонка в увійденого, а не 720 px. */
body[data-kp-guest-width="content"] { --kp-guest-page-width: var(--kp-layout-content-max); }
body[data-kp-guest-width="wide"]    { --kp-guest-page-width: var(--kp-layout-wide-max); }
body[data-kp-guest-width="xl"]      { --kp-guest-page-width: var(--kp-layout-xl-max); }
body[data-kp-guest-width="full"]    { --kp-guest-page-width: var(--kp-layout-full-max); }
body[data-kp-guest-width] .kp-page { max-width: var(--kp-guest-page-width, var(--kp-layout-wide-max)); }
body[data-kp-guest-width] .app-page-wrapper > .container { max-width: calc(var(--kp-guest-page-width, var(--kp-layout-wide-max)) + 2 * var(--n-s-3, 12px)); }

/* Шапка над сіткою: колонка бренду = ширина бічної колонки. Кнопка меню лишається й на комп'ютері, але лише
   іконкою: бічна колонка канону — короткий перелік, а «Повна навігація» (висувне меню) — ПОВНИЙ індекс сторінок
   продукту (Біржа, Кузня, Ринг тощо, яких прев'ю не малює). Без кнопки ці сторінки з комп'ютера лишались би без
   дороги з меню. Свідоме відхилення від канону (там ☰ лише на телефоні) — сказано власнику. */
@media (min-width: 681px) {
  body[data-kp-app-grid="1"] .kp-global-header__toggle-text { display: none; }
  body[data-kp-app-grid="1"] .kp-global-header__inner {
    grid-template-columns: var(--kp-layout-sidebar) minmax(0, 1fr) auto;
  }
}

@media (min-width: 1181px) and (max-width: 1365px) {
  .kp-app-grid { width: calc(100% - 24px); gap: 12px; }
}
@media (min-width: 1600px) {
  .kp-app-grid { gap: 18px; }
}

/* Планшет: іконки ліворуч, правої колонки немає. */
@media (max-width: 1180px) {
  .kp-app-grid[data-page-grid="main_rail"] { grid-template-columns: var(--kp-layout-sidebar) minmax(0, 1fr); }
  .kp-app-grid .kp-shell-rail { display: none; }
}
@media (min-width: 681px) and (max-width: 1180px) {
  .kp-app-grid,
  .kp-app-grid[data-page-grid="main_rail"] {
    width: min(100% - 24px, var(--kp-layout-shell-max));
    margin: 12px auto;
    grid-template-columns: 72px minmax(0, 1fr);
    gap: 12px;
  }
  /* Канон: 72px під знак бренду. У нас у першій колонці ще кнопка «Повної навігації», тож колонка — за вмістом
     (знак + кнопка), інакше вміст вилазив би (гейт шапки в account-settings.spec.js). */
  body[data-kp-app-grid="1"] .kp-global-header__inner { grid-template-columns: max-content minmax(0, 1fr) auto; }
  body[data-kp-app-grid="1"] .kp-global-header__brand-lockup { display: none; }
}

/* Телефон і низький альбомний екран: одна колонка. */
@media (max-width: 680px), (max-height: 520px) and (orientation: landscape) and (max-width: 980px) {
  .kp-app-grid,
  .kp-app-grid[data-page-grid="main_rail"] {
    display: block;
    width: 100%;
    margin: 0;
  }
  .kp-main-col { max-width: none; }
  .kp-main-col > .app-page-wrapper > .container { padding: 12px; }
}
@media (max-width: 480px) {
  .kp-main-col > .app-page-wrapper > .container { padding: 10px; }
}
@media (max-width: 360px) {
  .kp-main-col > .app-page-wrapper > .container { padding: 8px; }
}

/* 1.1.7.046 (знімок власника 02.10.2026 «чому різниця в стилях постів?»): бічні відступи колонки дає ОДИН шар —
   `.container`. Сторінка всередині нього додавала ще свої 12 px з кожного боку (а гостьова — ще 16), тож та сама картка
   допису на головній була вужча, ніж у стрічці, і ряд дій переносився на два рядки. Стрічка це вже обходила власним
   правилом (`.kp-page--feed { padding: 0 }`) — тепер правило спільне для всіх сторінок. */
.app-page-wrapper > .container > .kp-page { padding-inline: 0; }

/* 1.1.7.046: базовий вигляд PNG-значка меню (kp_menu_icon). Ширину й висоту ставить сам тег, тут — лише щоб
   картинка не розтягувалась і стояла на рядку тексту (кнопка «Створити допис»). */
img.kp-menu-icon { object-fit: contain; vertical-align: -4px; }

/* 1.1.7.291 UI-291-02: auxiliary floating controls cannot sit under the
   measured mobile dock. Applies to guests and members, including low landscape.
   Keep the existing positions on desktop and pages without a dock. */
@media (max-width: 680px), (max-height: 520px) and (orientation: landscape) and (max-width: 980px) {
  body:has(.kp-mobile-bottom-nav) .kp-refshare,
  body:has(.kp-mobile-bottom-nav) .kp-break {
    bottom: calc(max(var(--kp-bottom-nav-h, 64px), 56px) + var(--n-s-2, 8px) + var(--kp-space-3, 12px) + env(safe-area-inset-bottom, 0px));
  }
  /* UI-293-02: a share panel with a 180px QR is ~365px tall. On short
     landscape screens this put its close button ABOVE the viewport. The
     panel must scroll internally, staying clear of both header and dock.
     54px is the panel's existing offset above its 44px floating trigger. */
  body:has(.kp-mobile-bottom-nav) .kp-refshare__panel {
    box-sizing: border-box;
    max-height: max(0px, calc(100dvh - var(--kp-header-h, 64px) - max(var(--kp-bottom-nav-h, 64px), 56px) - var(--n-s-2, 8px) - var(--kp-space-3, 12px) - 54px - 12px - env(safe-area-inset-bottom, 0px)));
    overflow-y: auto;
    overscroll-behavior: contain;
    -webkit-overflow-scrolling: touch;
  }
}

/* 1.1.7.291 UI-291-05: modal stacking in the complete page shell.
   The old body > * { z-index:1 } and .app-page-wrapper { z-index:1 }
   trapped fullscreen dialogs inside a low stacking context: even a dialog
   at z-index:1300 stayed BEHIND the footer sibling at z-index:1. On mobile
   dock pages let the content wrappers participate in the document stacking
   context, so dialogs can cover the 1195 dock and the 1200 header. */
@media (max-width: 680px), (max-height: 520px) and (orientation: landscape) and (max-width: 980px) {
  body:has(.kp-mobile-bottom-nav) > .app-page-wrapper,
  body:has(.kp-mobile-bottom-nav) > .kp-app-grid,
  body:has(.kp-mobile-bottom-nav) .app-page-wrapper {
    z-index: auto;
  }
}

/* END css/app-shell.css */

/* START css/canon-primitives.css */
/* v1.1.6.300 (REV46): акцентний текст на власному м'якому тлі переведено на
   --n-accent-ink. Патерн узятий із канону, але я перевірив його на всіх десяти
   акцентах: у світлій схемі 9 із 10 не дотягують до WCAG AA (2.24-4.01 при
   порозі 4.5). Чорнило — той самий акцент, затемнений рівно до 4.5 і ні на крок
   далі; у темній схемі воно дорівнює акценту, бо там патерн і так проходить.
   Обґрунтування з числами — у tokens.css над оголошенням токена. */
/* v1.1.6.291 (REV46, CANON-STEP-8): значення, які ВЖЕ дорівнювали кроку
   канонної шкали, отримали її ім'я: радіус 0, pill 0,
   відступи 3, вага 0. Жодне число не змінено — це
   перейменування, не редизайн. Значення поза шкалою лишились літералами
   свідомо: прев'ю канону саме вживає 10/14/6px для падінгів компонентів,
   тож стягувати їх до шкали означало б відійти ВІД прев'ю, а не до нього. */
/* canon-primitives.css · v1.1.6.285 (REV46, CANON-STEP-2)
 * ============================================================================
 * КАНОНІЧНІ ПРИМІТИВИ з kolo_plus_new_theme_preview_CANON_REV6.html.
 *
 * Канон описує рівно чотири примітиви — і це вся його абетка:
 *   .kp-card · .kp-btn (+6 модифікаторів) · .kp-badge (+4) · .kp-form-field
 *
 * ВИМІРЮВАННЯ ПЕРЕД ПРАВКОЮ (1.1.6.284):
 *   .kp-card       — 212 вживань у розмітці; глобальна база Є (css/style.css);
 *   .kp-btn        —  84 вживання у розмітці; глобальної бази НЕМА ЖОДНОЇ;
 *   .kp-badge      —   2 вживання; база лише у widgets/ui/badge (вантажиться,
 *                      тільки коли рендериться той віджет);
 *   .kp-form-field —   2 вживання; база лише у widgets/account/account_shell.
 *
 * 🔴 ДЕФЕКТ, який видно з цих чисел. `.kp-btn` — центральний примітив канону —
 * не має глобального правила взагалі. 22 CSS-правила, що його згадують, живуть
 * у 5 файлах і всі СКОУПОВАНІ (`:is(.kp-page--account,…)` у nova-bridge,
 * `.kp-level-up__actions …`, auth). Поза цими скоупами `<button class="kp-btn">`
 * дістає з base.css рівно `background:none;border:none` — тобто виглядає як
 * звичайний текст. Це 19 кнопок на 8 сторінках:
 *   communities/earn_schedule (6), social/friends_suggested (3),
 *   posts/edit (2), communities/rules_view (2), communities/broadcast (2),
 *   messages/broadcasts_inbox (2), posts/post (1), lib/quick_fields (1).
 *
 * ── ДВА ШАРИ І ЧОМУ САМЕ ТАК ────────────────────────────────────────────────
 *
 * ШАР 1 (типовий, БЕЗ прапорця) — примітиви, яких глобально не існує:
 * `.kp-btn` і `.kp-form-field`. Тут нема чого перефарбовувати: вони лише
 * вдягають те, що зараз голе. Специфічність навмисно мінімальна (0,1,0), тож
 * КОЖНЕ наявне скоуповане правило (nova-bridge, level, auth, account) виграє,
 * як вигравало досі — вигляд уже стилізованих кнопок не змінюється.
 *
 * ШАР 2 (з .293 — типово; був за прапорцем) — примітиви, що мали глобальний
 * вигляд: `.kp-card` і `.kp-badge`. Переписати їх «на місці» означало б
 * перефарбувати 212 карток одним комітом. Тому канонний вигляд вмикається тим
 * самим перемикачем, що й палітра (KP_CANON=1).
 *
 * Правила написані ВИКЛЮЧНО на токенах канону. Через це один файл обслуговує
 * обидва режими: геометрія канонна вже зараз, а кольори підставляє той набір
 * токенів, який активний. Прапорець міняє значення — не правила.
 *
 * ⚠️ КІНЦЕВА ТОЧКА: коли палітра стане типовою, префікс `html[data-kp-canon="1"]`
 * знімається одним sed, а прапорець зникає з head.php і .env. Поки він тут —
 * міграція не завершена, і проба це рахує (`canonPending`, `gatedPrimitives`).
 *
 * ── СВІДОМІ ВІДХИЛЕННЯ ВІД ПРЕВ'Ю (не «майже те саме», а перелік) ───────────
 * 1. `font-weight:720/750` → `var(--kp-weight-bold)` (700). Пакет вантажить
 *    Inter дискретними накресленнями 400/500/600/700 (head.php), 720 фізично
 *    немає — браузер і так намалює 700. Токен робить це видимим.
 * 2. `.kp-badge--success/--warning/--danger` у прев'ю мають зашиті
 *    `rgba(20,167,101,.13)`, `rgba(233,156,21,.14)`, `#ae6c00`,
 *    `rgba(233,66,89,.12)`. Перенести їх дослівно = додати рівно ті літерали,
 *    які ця міграція прибирає. Тут вони виведені через color-mix із
 *    відповідних токенів: відтінок той самий, значення трохи інше.
 * 3. `.kp-btn--primary/--soft` у прев'ю позначені `!important`. У пакеті —
 *    без нього: інакше глобальне правило переб'є nova-bridge і перефарбує
 *    кнопки акаунта, чого цей крок робити не має.
 * 4. `.kp-card` у каноні БЕЗ padding — відступ дає контекстний клас
 *    (`.post{padding:15px}`, `.state-card{padding:var(--kp-space-4)}`). У пакеті таких
 *    класів ще нема, а 212 карток покладаються на padding зі style.css. Тому
 *    в канон-шарі padding збережено (`--kp-space-5`); зняти — коли з'являться
 *    контекстні класи. Борг названий, а не прихований.
 * 5. `.kp-btn--sm` немає в каноні (там `--small`), але розмітка вживає його
 *    10 разів проти 1. Аліас нижче не дає цим кнопкам лишитись без розміру;
 *    зводити розмітку на `--small` — робота модульних кроків.
 * ============================================================================ */

/* ══ ШАР 1 · ТИПОВО ═════════════════════════════════════════════════════════ */


/* ══ ЛЕГАСІ-ІМЕНА КНОПОК — ТА САМА КНОПКА ═══════════════════════════════════
   v1.1.6.301 (REV46).

   У проекті виявилось 183 різні родини кнопок на 844 правила. Канон має ОДНУ.
   Найбільша з чужих — не модульна, а друга ГЛОБАЛЬНА система з
   css/cyber-components.css: `.btn-primary` (164 вживання в розмітці) і
   `.btn-ghost` (387). Разом 551 — це більше, ніж у самого `.kp-btn` (84).

   Тобто дві глобальні кнопки жили паралельно й розходились у геометрії:
       канон .kp-btn : min-height 40, радіус 8, padding 10/14, рамка контролю
       .btn-primary  : без min-height, радіус 11, padding 9/18, плюс світіння
       .btn-ghost    : без min-height, радіус 11, padding 9/16, колір тексту
                       приглушений, рамка --border (той самий роздільник, чий
                       контраст 1.16:1 — див. .298)

   Переписувати 551 місце в шаблонах не треба: достатньо, щоб легасі-імена
   означали ТУ САМУ кнопку. Тому вони просто додані до канонних селекторів.

   Модульні перекриття (68 правил для .btn-ghost, 38 для .btn-primary) мають
   вищу специфічність і діють як діяли — сторінки зі свідомим власним стилем не
   змінюються. Змінюється БАЗА: там, де кнопку ніхто не перевизначав, вона тепер
   канонна.
   ═══════════════════════════════════════════════════════════════════════════ */
.kp-btn,
.btn-primary,
.btn-ghost,
.btn-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--kp-space-2);
  min-height: var(--kp-control-height-md);
  padding: 10px 14px;              /* канон дає саме ці числа, не крок шкали */
  border: var(--kp-border-width) solid var(--kp-color-border);
  border-radius: var(--kp-radius-md);
  background: var(--kp-color-surface);
  color: var(--kp-color-text);
  font-weight: var(--kp-weight-bold);
  text-decoration: none;
  cursor: pointer;
  transition: transform var(--kp-motion-normal) var(--kp-ease-standard),
              box-shadow var(--kp-motion-normal) var(--kp-ease-standard),
              background var(--kp-motion-normal) var(--kp-ease-standard),
              border-color var(--kp-motion-normal) var(--kp-ease-standard);
  touch-action: manipulation;
}
.btn-primary:hover,
.btn-ghost:hover,
.btn-icon:hover,
.kp-btn:hover {
  transform: translateY(-1px);
  box-shadow: var(--kp-shadow-sm);
}
.btn-primary:focus-visible,
.btn-ghost:focus-visible,
.btn-icon:focus-visible,
.kp-btn:focus-visible {
  outline: var(--kp-focus-ring-width) solid var(--kp-color-focus-ring);
  outline-offset: var(--kp-focus-ring-offset);
}
.kp-btn:disabled,
.kp-btn[aria-disabled="true"] {
  opacity: .55;
  cursor: not-allowed;
  transform: none;
  box-shadow: none;
}
.kp-btn--primary,
.btn-primary {
  background: var(--kp-accent);
  border-color: transparent;
  color: var(--kp-on-accent);
}
/* Кнопка-ПОСИЛАННЯ (<a class="kp-btn kp-btn--primary">) при наведенні. У каноні колір тексту основної кнопки
   позначено !important і він перемагає `a:hover`; у пакеті !important свідомо знято (відхилення 3), і
   `a:hover { color: var(--n-accent-hover) }` із base.css (специфічність 0,1,1) перебивав `.kp-btn--primary`
   (0,1,0): текст ставав синім на синьому, контраст 1.26:1 замість 4.77:1 («Знайти людей» на порожній стрічці
   й кожне інше посилання-кнопка). Вузьке правило лише для наведення на посилання: кнопки акаунта не зачіпає. */
a.kp-btn--primary:hover,
a.btn-primary:hover { color: var(--kp-on-accent); }
.kp-btn--soft {
  background: var(--kp-accent-soft);
  border-color: color-mix(in srgb, var(--kp-accent) 22%, var(--kp-color-border));
  color: var(--kp-color-text);
}
/* v1.1.6.298: прозора кнопка позначає себе ЛИШЕ рамкою, тож рамка мусить бути
   видимою: --kp-color-border (роздільник) дає на тлі сторінки 1.16:1, канонний
   колір контролю — 3.2:1 при порозі WCAG 3:1 для не-текстових елементів. */
.kp-btn--ghost,
.btn-ghost { background: transparent; border-color: var(--kp-color-control-border); }
.kp-btn--danger { color: var(--kp-color-danger); }
.kp-btn--icon,
.btn-icon {
  width: var(--kp-control-height-md);
  height: var(--kp-control-height-md);
  padding: 0;
  border-radius: 11px;
}
.kp-btn--small,
.kp-btn--sm {                      /* див. відхилення 5 */
  min-height: var(--kp-control-height-sm);
  padding: 7px 10px;
  font-size: var(--kp-text-xs);
}

.kp-form-field { display: grid; gap: 7px; }
.kp-form-field label {
  font-size: var(--kp-text-xs);
  font-weight: var(--kp-weight-bold);
  color: var(--kp-color-text-muted);
}
.kp-form-field input,
.kp-form-field textarea,
.kp-form-field select {
  width: 100%;
  min-height: var(--kp-control-height-md);
  padding: 11px var(--kp-space-3);
  border: var(--kp-border-width) solid var(--kp-color-control-border);
  border-radius: var(--kp-radius-md);
  background: var(--kp-color-surface);
  color: var(--kp-color-text);
  outline: none;
}
.kp-form-field textarea { min-height: 110px; resize: vertical; }
.kp-form-field input:focus-visible,
.kp-form-field textarea:focus-visible,
.kp-form-field select:focus-visible {
  outline: var(--kp-focus-ring-width) solid var(--kp-color-focus-ring);
  outline-offset: var(--kp-focus-ring-offset);
}


/* ══ ТАБЛИЦЯ, ЩО НЕ РОЗПИРАЄ СТОРІНКУ ══════════════════════════════════════
   v1.1.6.328 (REV46).

   🔴 На скріншоті сторінки дукатів таблиця виїхала за екран: стовпці «Створено»
   і «Перемога» обрізані, число не дочитати. Це той самий клас, що ряд реакцій у
   .324 — вміст ширший за екран і тягне за собою всю сторінку.

   Перепис: у пакеті **211 таблиць**, і **135 із них** не мають навколо себе
   нічого, що прокручується. Тобто дефект не поодинокий, а типовий.

   Канон дає точну відповідь, і не абияку — окрему обгортку:
       .arena-table-wrap { overflow: auto; border: 1px solid; border-radius }
       .arena-table      { width: 100%; border-collapse: collapse; min-width: 720px }
   Тобто таблиця НЕ стискається до екрана (720px мінімум — щоб стовпці лишались
   читабельними), а прокручується всередині своєї рамки.

   ── ЧОМУ ГЛОБАЛЬНЕ ПРАВИЛО, А НЕ 135 ОБГОРТОК ──────────────────────────────
   Обгортати кожну таблицю в розмітці — 135 правок у шаблонах, кожна з ризиком
   зачепити щось поруч. Натомість на вузькому екрані сама таблиця стає блоком і
   прокручується: `display: block; overflow-x: auto`. Це усталений прийом, і він
   не чіпає ані розмітки, ані широких екранів.

   Ціна названа чесно: у режимі `display: block` таблиця перестає розтягувати
   стовпці на всю ширину — вони стискаються до вмісту. Для таблиці даних на
   телефоні це прийнятно, для макетної було б ні.

   ⚠️ Спершу я написав тут, що макетних таблиць у пакеті немає. Перевірив — і це
   виявилось неправдою: у modules/admin/tax_rates дві таблиці «ключ — значення»
   з фіксованою шириною першого стовпця, тобто саме макетні. П'ять інших
   «знахідок» були хибними: мій пошук зловив слово table всередині коментаря.
   Правило їх усе одно не ламає — при `display: block` рядки й комірки
   загортаються в анонімні табличні бокси, тож структура лишається; змінюється
   тільки зовнішній бокс. Але твердження «таких немає» було б брехнею в коді, і
   тому воно замінене на те, що є насправді.

   Там, де вже є канонна обгортка `.arena-table-wrap` чи схожа, правило
   нешкідливе: зовнішній контейнер прокручується першим.
   ═══════════════════════════════════════════════════════════════════════════ */
.kp-table-wrap,
.arena-table-wrap {
  overflow: auto;
  -webkit-overflow-scrolling: touch;
  border: var(--kp-border-width) solid var(--kp-color-border);
  border-radius: var(--kp-radius-lg);
}
.arena-table { width: 100%; border-collapse: collapse; min-width: 720px; }
/* 1.1.7.045 (знімок власника /arena/ducats: «тут стилі за каноном?»). Із канону сюди перейшли лише обгортка й ширина, а
   комірки лишились без правил: шапка — жирна по центру (типовий вигляд браузера), рядки без відступів і ліній. Так
   виглядали шість екранів арени: дукати, міста, країни, місто, країна, гід. Нижче — правила прев'ю R9.05 дослівно
   (одна редакція в прев'ю). `.arena-me` прев'ю не перенесено: у продукті такого рядка немає. */
.arena-table th, .arena-table td { padding: 12px 14px; text-align: left; border-bottom: var(--kp-border-width) solid var(--kp-color-border); }
.arena-table th { font-size: 11px; color: var(--kp-color-text-muted); background: var(--kp-color-surface-muted); position: sticky; top: 0; }
.arena-table tr:last-child td { border-bottom: 0; }

@media (max-width: 680px) {
  /* Таблиця сама стає прокруткою — сторінку більше не тягне. */
  table {
    display: block;
    max-width: 100%;
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
  }
}

/* ══ СМУГА ФІЛЬТРІВ — ПІГУЛКИ ══════════════════════════════════════════════
   v1.1.6.313 (REV46).

   🔴 Спершу треба виправити те, що я сам стверджував у .311 і .312: нібито канон
   моделює фільтри лише як підкреслені вкладки, а пігулки — вигадка продукту, і
   тому переходити ризиковано. Це було НЕВІРНО. Канон має ОБИДВА патерни:
     .tabs / .tab            — підкреслення, всередині картки (вкладки розділу)
     .pill-tabs / .pill-tab  — пігулки в смузі інструментів (фільтри), 50 вживань
   Продуктовий `.cat-tab` лягає на другий один-в-один, і жодного рішення «на око»
   тут не потрібно було — досить було уважніше прочитати прев'ю.

   Канон:
     .pill-tab        border 1px; background surface; color text-muted;
                      padding 8px 11px; border-radius 999px; font-weight 700
     .pill-tab.active background accent-soft; border-color transparent; color accent

   Що змінюється в продукті: радіус 12px → пігулка, падінг 8/16 → 8/11, тло
   surface-muted → surface, активний стан із власного --accent-dim на канонний
   accent-soft.

   ⚠️ ОДНЕ ВІДХИЛЕННЯ, і воно те саме, що в .300: активна мітка в каноні —
   `color: var(--kp-accent)` на тлі accent-soft. Я перевіряв цю пару на всіх
   десяти акцентах: у світлій схемі 9 із 10 не дотягують до WCAG AA. Тому тут
   --n-accent-ink — той самий акцент, затемнений рівно до порогу.

   Імена `.cat-tab`/`.cat-tabs` лишаються: вони стоять у 77 місцях у 18 шаблонах,
   і переписувати розмітку заради перейменування не треба — досить, щоб імена
   означали той самий елемент. Так само зроблено з кнопками в .301.
   ═══════════════════════════════════════════════════════════════════════════ */
.pill-tabs,
.cat-tabs { display: flex; gap: 6px; flex-wrap: wrap; }

.pill-tab,
.cat-tab {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: 8px 11px;
  border: var(--kp-border-width) solid var(--kp-color-control-border);
  border-radius: var(--kp-radius-pill);
  background: var(--kp-color-surface);
  color: var(--kp-color-text-muted);
  font-size: var(--kp-text-xs);
  font-weight: var(--kp-weight-bold);
  cursor: pointer;
  text-decoration: none;
  white-space: nowrap;
}
.pill-tab:hover,
.cat-tab:hover { background: var(--kp-color-surface-muted); color: var(--kp-color-text); }

/* v1.1.6.316: `.is-active` додано і для .pill-tab — продуктові скрипти позначають
   активний фільтр саме цим класом (сповіщення), а прев'ю вживає `.active`.
   Підтримуються обидва: перейменовувати клас у чужому скрипті заради однаковості
   назви — це ризик заради косметики. */
.pill-tab.active, .pill-tab.is-active, .pill-tab[aria-selected="true"],
.cat-tab.is-active, .cat-tab.active, .cat-tab[aria-selected="true"] {
  background: var(--n-accent-soft);
  border-color: transparent;
  color: var(--n-accent-ink);
}
/* 1.1.7.154: ПІДСУМОК НА ВКЛАДЦІ. Медіатека малювала його власним класом із
   власною родиною вкладок; вкладка з числом потрібна не лише їй, тож значок
   живе тут, біля самої вкладки. Тло — напівпрозоре від тла сторінки, тому
   однаково читається і на спокійній, і на активній вкладці. */
.pill-tab__count,
.cat-tab__count {
  padding: 1px 7px;
  border-radius: var(--kp-radius-pill);
  background: color-mix(in oklab, var(--kp-color-page) 50%, transparent);
  font-size: var(--kp-text-xs);
  font-weight: var(--kp-weight-bold);
  min-width: 22px;
  text-align: center;
}
.pill-tab.is-active .pill-tab__count,
.cat-tab.is-active .cat-tab__count,
.cat-tab[aria-selected="true"] .cat-tab__count {
  background: color-mix(in oklab, var(--n-accent-ink) 14%, transparent);
}

.pill-tab:focus-visible,
.cat-tab:focus-visible {
  outline: var(--kp-focus-ring-width) solid var(--kp-color-focus-ring);
  outline-offset: var(--kp-focus-ring-offset);
}

/* Смуга інструментів сторінки: фільтри ліворуч, дія праворуч. */
.page-toolbar {
  display: flex;
  justify-content: space-between;
  gap: var(--kp-space-3);
  align-items: center;
  margin: 0 0 14px;
  flex-wrap: wrap;
}

/* ══ СІТКА ПОКАЗНИКІВ ══════════════════════════════════════════════════════
   v1.1.6.337 (REV46).

   Канон описує ряд однакових комірок «підпис — число — доповнення»:
       .arena-stats  grid; repeat(4, minmax(0,1fr)); gap 10; margin-top 16
       .arena-stat   padding 12; 1px рамка; radius 12; тло — поверхня
       strong        block; 20px
       .tiny         12px; приглушений

   У прев'ю це саме арена, але патерн загальний: те саме потрібно профілю,
   спільнотам, статистиці. Тому він тут, а не у CSS арени — інакше друга ж
   сторінка почала б його переписувати, як це вже сталось із карткою й полем.

   ⚠️ `repeat(4, …)` у прев'ю жорсткий: чотири комірки в ряд, бо там рівно
   чотири показники. У продукті їх буває три (без престижу) і п'ять. Тому
   `auto-fit` із мінімумом — сітка сама вирішує, скільки влізе, і на телефоні
   не розчавлює числа до нечитабельного. Це відхилення від букви прев'ю заради
   його ж наміру: показники мають лишатись читабельними.
   ═══════════════════════════════════════════════════════════════════════════ */
.arena-stats {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
  gap: 10px;
  margin-top: 16px;
}
.arena-stat {
  padding: 12px;
  border: var(--kp-border-width) solid var(--kp-color-border);
  background: var(--kp-color-surface);
  border-radius: var(--kp-radius-lg);
  min-width: 0;
}
.arena-stat > strong {
  display: block;
  font-size: var(--kp-text-lg);
  line-height: var(--kp-leading-tight);
  margin: 2px 0;
}
.tiny {
  font-size: var(--kp-text-xs);
  color: color-mix(in srgb, var(--kp-color-text-muted) 72%, transparent);
}

/* ══ ГОЛОВА СТОРІНКИ ═══════════════════════════════════════════════════════
   v1.1.6.311 (REV46).

   У прев'ю це найповторюваніший складений патерн після картки: `kp-card page-head`
   трапляється **32 рази** — на головній, у спільнотах, оголошеннях, профілі,
   арені. Скрізь однаково: заголовок і рядок пояснення під ним, на канонній
   картці.

   Тому місце йому тут, у примітивах, а не в CSS першої ж сторінки, якій він
   знадобився. Це той самий висновок, що я двічі робив на модулі авторизації —
   тільки цього разу зроблений ДО того, як з'явиться друга сторінка з тим самим
   блоком, а не після.

   Вживається як `<div class="kp-card page-head">` — картка дає поверхню, рамку,
   радіус і тінь, page-head лише відступи й типографіку.
   ═══════════════════════════════════════════════════════════════════════════ */
.page-head { padding: 18px 20px; margin-bottom: 14px; }
.page-head > h1 {
  font-size: var(--kp-title-page);
  font-weight: var(--kp-title-weight);
  line-height: var(--kp-leading-tight);
  margin: 0 0 3px;
}
.page-head > p { margin: 0; color: var(--kp-color-text-muted); }
/* 1.1.7.027: голова з дією праворуч — канон `.page-head > .section-title > div(h1, p) + кнопка` (view-home та інші). */
.page-head > .section-title { margin-bottom: 0; align-items: flex-start; }
.page-head > .section-title h1 {
  font-size: var(--kp-title-page);
  font-weight: var(--kp-title-weight);
  line-height: var(--kp-leading-tight);
  margin: 0 0 3px;
}
.page-head > .section-title p { margin: 0; color: var(--kp-color-text-muted); }
.page-head > .section-title > .kp-btn { flex: 0 0 auto; }
/* Кнопка-посилання в голові: правило `.section-title a` (колір посилання, 12px) не стосується кнопок —
   інакше текст основної кнопки ставав синім на синьому. */
.section-title > a.kp-btn,
.section-title > .page-head__actions > a.kp-btn { font-size: var(--kp-text-sm); font-weight: 800; }
.section-title > a.kp-btn--primary,
.section-title > .page-head__actions > a.kp-btn--primary { color: var(--kp-on-accent, #fff); }
/* 1.1.7.028 (канон R8.94 view-home): дві дії в голові — головна й другорядна поруч, одним рядом; на телефоні —
   під заголовком, кожна на всю ширину (як `.page-toolbar > .kp-btn` у каноні), а не втиснуті обабіч назви. */
.section-title > .page-head__actions { display: flex; flex-wrap: wrap; gap: var(--kp-space-2); justify-content: flex-end; flex: 0 0 auto; }
@media (max-width: 480px) {
  .page-head { padding: 14px 15px; }
  /* 1.1.7.028: розмір заголовка на телефоні — з токена (канон: 20 px), а не власний 18 px цієї голови */
  .page-head > .section-title { flex-wrap: wrap; }
  .section-title > .page-head__actions { flex: 1 1 100%; justify-content: stretch; }
  .section-title > .page-head__actions > .kp-btn { flex: 1 1 auto; }
}

/* ══ ЗАГОЛОВКИ СТОРІНОК — ОДИН СТАНДАРТ (1.1.7.028) ═══════════════════════════════════════════════════════
   Власник: «однакові заголовки … до одних стандартів, щоб не було стрибків між сторінками». Обмір: 12 розмірів
   заголовка сторінки на 190 сторінках. Кожен клас нижче — ЗАГОЛОВОК СТОРІНКИ (не картки й не розділу): він
   отримує канонну голову сторінки (22/20 px, насичення 700). `!important` тут свідомий і єдиний у цьому блоці:
   модульні стилі вантажаться ПІСЛЯ примітивів і мали власні розміри для телефона (15, 18, 23, 24 px) — без
   нього стандарт знову розсипався б за першим медіазапитом модуля. Сторінки-вітрини з більшим героєм канону
   (ринок, крамниця, квести, акаунт, оголошення, спільноти, правові) сюди не входять — їм лишається лише
   єдине насичення 700 (блок нижче). */
.kp-page-head__title,
.community-context__title,
.kp-import-drafts__title,
.kp-comm-broadcast-title,
.kp-earn-sched__title,
.kp-medals__title,
.kp-listing-page__title,
.w-media-hero__title,
.w-housing-hero__title,
.kp-brew-title,
.w-set-page-head__title,
.kp-comm-members-head__title,
.fg-hero__title,
.craft2__head-h,
.page-head__title,
.kp-comm-create-page .kp-comm-hero__title,
.kp-listings-hero--compact .kp-listings-hero__title {
  font-size: var(--kp-title-page) !important;
  font-weight: var(--kp-title-weight) !important;
  line-height: var(--kp-leading-tight);
}
/* Герої, розмір яких канон задає окремо (VIEW_META R8.94, обмір 1440 / 390 px). */
.kp-vip-hero__title { font-size: 30px !important; }                                   /* vip: 30 / 30 */
.kp-lky-hero__title { font-size: 28px !important; }                                   /* lucky-day: 28 / 28 (був 18) */
.kp-manage__title { font-size: clamp(27px, 4vw, 42px) !important; }                   /* my-listings: 42 / 27 (був 18) */
.kp-shop-page .page-head__title,
.kp-market-page .kp-listings-hero__title { font-size: 30px !important; }              /* shop, market: 30 / 22 */
.w-profile-hero__name { font-size: 25px !important; }                                 /* profile: 25 / 20 */
.w-referrals-header__title { font-size: 26px !important; }                            /* referrals: 26 / 26 */
.kp-seller-storefront__head h1,
.kp-worklog__head-text h1 { font-size: 28px !important; }                             /* listings-seller 28 / 28, worklog 28 / 24 */
.kp-listing-view-hero__title { font-size: 34px !important; }                          /* listing-detail: 34 / 24 */
@media (max-width: 640px) {
  .kp-shop-page .page-head__title,
  .kp-market-page .kp-listings-hero__title { font-size: 22px !important; }
  .w-profile-hero__name { font-size: 20px !important; }
  .kp-worklog__head-text h1,
  .kp-listing-view-hero__title { font-size: 24px !important; }
}
/* Герої-вітрини: розмір свій (канон), насичення — одне для всіх заголовків сторінок. */
.kp-account-shell__head h1,
.kp-listings-hero__title,
.community-manage__title,
.kp-comm-hero__title,
.hero__title,
.kp-pub h1,
.kp-legal__title,
.legal-hero h1,
.w-profile-hero__name,
.fam-title,
.gft-title,
.kp-listing-view-hero__title,
.kp-lky-hero__title,
.kp-manage__title,
.kp-vip-hero__title,
.w-referrals-header__title,
.w-messages-compose-card__title,
.kp-msg-overview__head h1 {
  font-weight: var(--kp-title-weight) !important;
}

/* ══ ПОЛЕ ВВОДУ — ГЛОБАЛЬНА БАЗА ═══════════════════════════════════════════
   v1.1.6.302 (REV46).

   Перепис показав: `input`/`textarea`/`select` описані 109 разів у 34 файлах, і
   в цих описах **14 різних радіусів, 16 різних мінімальних висот, 12 різних
   падінгів**. Канон дає одне поле.

   Причина розсипу проста: глобальної бази для полів НЕ ІСНУВАЛО. `base.css` дає
   їм лише шрифт (`font-family: inherit`), а `css/kp-form.css`, попри загальну
   назву, описує рівно одну форму — створення спільноти. Тобто кожен модуль,
   якому знадобилось поле, малював його з нуля — або не малював, і тоді поле
   лишалось із типовим виглядом браузера: тонка сіра рамка, майже без радіуса,
   висота менша за поріг дотику.

   Тепер база є, і вона канонна: та сама геометрія, що в `.kp-form-field input`
   вище. Специфічність `input[type="…"]` — (0,1,1); усі 109 наявних описів мають
   таку саму або вищу і йдуть пізніше, тож свідомо оформлені поля не змінюються.
   Змінюються ті, які ніхто не оформлював.


   ⚠️ ЧОМУ `:where()`. Спершу я написав селектори прямо — `input[type="text"], …`.
   Це (0,1,1). Потім перевірив, а не повірив собі: серед 109 наявних описів
   знайшлось ПʼЯТЬ зі СЛАБШОЮ специфічністю — `.kp-input`, `.kp-comm-create-input`,
   `.kp-bcast-search-input` (0,1,0) і два голі `select`/`textarea` (0,0,1). Тобто
   моя «база» перекрила б поля, які хтось свідомо оформив — рівно та несподіванка,
   якої база робити не має.
   `:where()` обнуляє специфічність усього списку. Тепер це справді ПІДЛОГА: діє
   там, де поле не оформлював ніхто, і поступається будь-якому правилу.

   Типи перелічені поіменно навмисно: `checkbox`, `radio`, `file`, `range`,
   `color` мають власний вигляд, і накладати на них геометрію текстового поля —
   зіпсувати їх. `input:not([type])` теж тут: без атрибута браузер вважає поле
   текстовим.
   ═══════════════════════════════════════════════════════════════════════════ */
:where(
  input[type="text"], input[type="email"], input[type="password"], input[type="search"],
  input[type="number"], input[type="tel"], input[type="url"], input[type="date"],
  input[type="datetime-local"], input[type="month"], input[type="week"], input[type="time"],
  input:not([type]), textarea, select
) {
  min-height: var(--kp-control-height-md);
  padding: 11px 12px;
  border: var(--kp-border-width) solid var(--kp-color-control-border);
  border-radius: var(--kp-radius-md);
  background: var(--kp-color-surface);
  color: var(--kp-color-text);
}
:where(textarea) { min-height: 110px; resize: vertical; }
:where(input, textarea)::placeholder { color: var(--kp-color-text-muted); }

/* Канон: на дотиковому вводі ціль не менша за --kp-target-min. */
@media (pointer: coarse) {
  .kp-btn { min-height: var(--kp-target-min); }
  .kp-btn--icon { min-width: var(--kp-target-min); height: var(--kp-target-min); }
}
@media (prefers-contrast: more) {
  .kp-btn,
  .kp-form-field input,
  .kp-form-field textarea,
  .kp-form-field select { border-width: 2px; }
}

/* ══ ШАР 2 · v1.1.6.293: БІЛЬШЕ НЕ ЗА ПРАПОРЦЕМ ═════════════════════════════
   `html[…]` дає (0,2,1) — вище за скоуповані `.kp-page--X .kp-card` (0,2,0),
   тож канон виграє незалежно від порядку підключення. `!important` у чужих
   файлах він НЕ перебиває — такі місця мають виправлятись у модульних кроках,
   а не глушитись звідси. */

.kp-card {
  background: var(--kp-color-surface);
  border: var(--kp-border-width) solid var(--kp-color-border);
  border-radius: var(--kp-radius-lg);
  box-shadow: var(--kp-shadow-sm);
  padding: var(--kp-space-5);      /* див. відхилення 4 */
  min-width: 0;                    /* канон: картка не розпирає грід */
}
@media (max-width: 480px) {
  .kp-card { border-radius: 14px; }
}
@media (prefers-contrast: more) {
  .kp-card { border-width: 2px; }
}

.kp-badge {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 5px var(--kp-space-2);
  border: 0;
  border-radius: var(--kp-radius-pill);
  background: var(--kp-color-surface-muted);
  color: var(--kp-color-text-muted);
  font-size: var(--kp-text-xs);
  font-weight: var(--kp-weight-bold);
}
.kp-badge--accent {
  background: var(--kp-accent-soft);
  color: var(--n-accent-ink);
}
.kp-badge--success {
  background: color-mix(in srgb, var(--kp-color-success) 13%, transparent);
  color: var(--kp-color-success);
}
.kp-badge--warning {
  background: color-mix(in srgb, var(--kp-color-warning) 14%, transparent);
  color: var(--kp-color-warning);
}
.kp-badge--danger {
  background: color-mix(in srgb, var(--kp-color-danger) 12%, transparent);
  color: var(--kp-color-danger);
}

/* ══ ГОЛОВА БЛОКУ ══════════════════════════════════════════════════════════
   v1.1.6.340 (REV46, ARENA-13).

   Канон: заголовок ліворуч, посилання «показати все» праворуч.
       .section-title      flex; space-between; gap 12; margin-bottom 12
       .section-title h2/h3   16px, без власних відступів
       .section-title a    акцент; 700; 12px; без підкреслення

   ⚠️ ІМ'Я ВЖЕ ЗАЙНЯТЕ, і це треба було перевірити, а не припустити.
   `css/cyber-components.css` (рядок 158) оголошує `.section-title` з ІНШИМ
   сенсом: моноширинний підпис, БЕЗ space-between, без відступу знизу.
   Порахував входження класу в чистому вигляді по всій розмітці пакета
   (php/js/html, розбір атрибутів class, а не пошук підрядка): **0**.
   Усе, що знайшлось, — інші імена, які лише МІСТЯТЬ цей рядок
   (`w-media-edit-form__section-title`, `inv2__section-title`,
   `kp-brew-section-title`). Тобто старе правило мертве: воно не малює нічого
   й нікому, і канонне ні в кого нічого не відбирає.

   Через цей же підрядок `dead_selector_check` і не назвав його мертвим — проба
   зіставляє ім'я з розміткою як текст. Це її межа, не помилка; записую тут,
   щоб наступного разу таке ім'я перевіряли вручну.

   Каскад перевірений, а не сподіваний: у `config/theme.php` ланцюг
   підключення — tokens, base, cyber-components(3), kp-form, nova-bridge,
   app-shell, canon-primitives(7), style. За рівної специфічності (0,1,0)
   виграє нижчий за ланцюгом, тобто цей файл.
   ═══════════════════════════════════════════════════════════════════════════ */
.section-title {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--kp-space-3);
  margin-bottom: var(--kp-space-3);
  /* 1.1.7.027: `css/cyber-components.css` досі оголошує `.section-title` моноширинним підписом (font-family mono,
     letter-spacing, 800) — і це правило НЕ мертве: воно діє скрізь, де вжито канонний `.section-title` (арена, тепер
     стрічка). Канон такого шрифту не має — скидаємо до шрифту сторінки. */
  font-family: inherit;
  font-size: inherit;
  font-weight: inherit;
  letter-spacing: normal;
}
.section-title h2,
.section-title h3 {
  margin: 0;
  font-size: var(--kp-text-md);
}
/* 🔴 ВІДХИЛЕННЯ ВІД КАНОНУ, І ВОНО КОШТУВАЛО Б ЧИТАБЕЛЬНОСТІ.
   Прев'ю пише тут `color: var(--kp-accent)`. Порахував пари вручну — бо
   `contrast_check` цю саме НЕ бачить: у правила посилання немає власного тла,
   тож пара потрапляє в 1640 «не піддались розкриттю», а не в 47 провалених.
   Тобто зелена проба тут нічого не доводила б.

       акцент  #e45f3a на сторінці #f3f6fb → 3.23:1   (треба 4.5) ✗
       акцент  #e45f3a на поверхні #ffffff → 3.50:1              ✗
       чорнило #ad4f3a на сторінці #f3f6fb → 4.90:1              ✓

   Ба більше: до `.340` голова цього блоку малювалась `--arena-dim` (#5b6b84),
   тобто 5.41:1. Взяти букву прев'ю означало б ПОГІРШИТИ те, що вже проходило.

   Тому тут `--n-accent-ink` — той самий акцент, затемнений рівно до порогу, і
   те саме рішення, що вже ухвалене у `.300` для акцентного тексту. У темній
   схемі чорнило дорівнює акценту, тож там це буквально канон. */
.section-title a {
  color: var(--n-accent-ink);
  font-weight: var(--kp-weight-bold);
  text-decoration: none;
  font-size: var(--kp-text-xs);
}

/* ══ РЯДОК РЕЙТИНГУ ════════════════════════════════════════════════════════
   v1.1.6.340 (REV46, ARENA-13).

   Канон описує рейтинг трьома правилами й одним звуженням:
       .arena-leaderboard      grid; gap 8
       .arena-leader-row       grid; 52px | minmax(0,1fr) | 120px | 110px;
                               gap 12; align center; padding 12/14
       .arena-leader-row__rank 20px; вага 900; по центру
       --me                    рамка акценту + м'яке акцентне тло
       ≤680px                  42px | 1fr | 84px, останній стовпець ховається

   🔴 ПОРЯДОК У ЦЬОМУ ФАЙЛІ НЕ ВИПАДКОВИЙ — це той самий урок, що в `.318`.
   `.kp-card` (рядок 481) і `.arena-leader-row` мають ОДНАКОВУ специфічність
   (0,1,0). Картка задає `padding: 24px` і тло поверхні; рядок мусить задати
   `12/14`, а `--me` — акцентне тло. За рівної специфічності виграє той, хто
   НИЖЧЕ у файлі. Тому блок стоїть у кінці, а не поруч із рештою арени вище.
   Якби він стояв вище — `--me` не малював би нічого, падінг лишився б 24px,
   і жодна проба про це не сказала б: обидва правила синтаксично живі.

   Чому взагалі тут, а не в CSS арени: канон малює цим рядком ДВА місця —
   сторінку `/arena/leaderboard` і топ-5 на `/arena`. Другий споживач
   з'явиться наступним кроком. Саме на цьому вже двічі спіткнулись картка й
   поле, коли спільний патерн лишали в CSS першої-ліпшої сторінки.
   ═══════════════════════════════════════════════════════════════════════════ */
.arena-leaderboard {
  display: grid;
  gap: var(--kp-space-2);
}
.arena-leader-row {
  display: grid;
  grid-template-columns: 52px minmax(0, 1fr) 120px 110px;
  gap: var(--kp-space-3);
  align-items: center;
  padding: var(--kp-space-3) 14px;
}
.arena-leader-row__rank {
  /* 20px поза шкалою (lg=18, xl=24). Канонне число лишене літералом свідомо —
     стягувати його до кроку означало б відійти ВІД прев'ю. Те саме рішення,
     що в CANON-STEP-8. */
  font-size: 20px;
  font-weight: 900;
  text-align: center;
}
.arena-leader-row--me {
  border-color: var(--kp-accent);
  background: var(--kp-accent-soft);
}
/* 1.1.7.146: рядок рейтингу — це завжди `kp-card arena-leader-row`, а `css/style.css` підключається ПІСЛЯ цього файлу
   і своїм `.kp-card { background; border }` (та сама специфічність 0,1,0) перебивав `--me`: «ваш» рядок ніде не
   підсвічувався (знайшла браузерна проба 1.1.7.146 на рейтингу Престижу; те саме було на рейтингу рингу). Подвійний
   клас (0,2,0) не залежить від порядку файлів. */
.kp-card.arena-leader-row--me,
.kp-card.arena-leader-row--me:hover {
  border-color: var(--kp-accent);
  background: var(--kp-accent-soft);
}

/* Телефон: канонне звуження до трьох стовпців. Ховається саме ОСТАННІЙ —
   тому в розмітці останнім має стояти найменш важливе. */
@media (max-width: 680px) {
  .arena-leader-row { grid-template-columns: 42px minmax(0, 1fr) 84px; }
  .arena-leader-row > :last-child { display: none; }
}

/* ══ ГЕРОЙ АРЕНИ ═══════════════════════════════════════════════════════════
   v1.1.6.341 (REV46, ARENA-14).

   Канон відкриває хаб арени однією карткою:
       .arena-hero        padding 20; position relative; overflow hidden;
                          лінійний градієнт від поверхні до м'якого акценту
       .arena-hero:after   ⚔ праворуч угорі, 110px, opacity .08

   Узято буквально — окрім одного. Знак ⚔ у прев'ю стоїть як `content` на
   псевдоелементі, тобто це ТЕКСТ у потоці доступності. Скрін-рідер прочитає
   «схрещені мечі» посеред заголовка сторінки. Додано `aria-hidden`-еквівалент
   для псевдоелемента — `content: "⚔" / ""` — альтернативний текст порожній,
   і знак лишається суто декоративним. Це не відступ від вигляду: малюється те
   саме, змінюється тільки те, що чує читач екрана.

   Чому в примітивах, а не в CSS арени: герой — це `kp-card` з іншим тлом, і
   градієнт зібраний із канонних токенів. Тримати його поруч із карткою
   означає, що наступна сторінка з героєм не почне писати свій. ═══════════ */
.arena-hero {
  padding: 20px;
  position: relative;
  overflow: hidden;
  background: linear-gradient(
    135deg,
    var(--kp-color-surface),
    color-mix(in srgb, var(--kp-accent-soft) 75%, var(--kp-color-surface))
  );
}
.arena-hero::after {
  content: "⚔" / "";
  position: absolute;
  right: 22px;
  top: -15px;
  font-size: 110px;
  opacity: .08;
  pointer-events: none;
}
.arena-hero h1 { margin: 0; }
.arena-hero > .kp-badge { margin-bottom: 8px; }

/* Канон: приглушений рядок пояснення. Оголошений тут, бо трапляється в
   прев'ю на кожній другій сторінці, а в пакеті власного правила не має. */
.muted { color: var(--kp-color-text-muted); }

/* ══ РЯДОК, ЗАЗОР, КРУЖОК ══════════════════════════════════════════════════
   v1.1.6.342 (REV46, ARENA-15).

   Канон:
       .row          flex; align-items center
       .gap12        gap 12
       .mini-avatar  36×36; коло; grid place-items center; білий текст;
                     градієнт #31405a → #101827; вага 850; flex 0 0 auto

   🔴 ЦЕ ВИПРАВЛЕННЯ МОГО ВЛАСНОГО ТВЕРДЖЕННЯ З `.340`.
   Тоді я написав — і в коді, і в нотатках, — що додати `.row` не можна, бо
   «три елементи розмітки вже носять цей клас». Перерахував як слід: справжніх
   входжень **нуль**, і в `.row`, і в `.gap12`, і в `.mini-avatar`.

   Звідки взялась трійка: я шукав `class="…"` і розбивав на токени, НЕ вирізавши
   спершу PHP. Через це змінна `$row['status']` усередині атрибута дала токен
   `row`. Порахувався код, а не розмітка. Правильний порядок — спершу прибрати
   `<?php … ?>`, потім читати атрибути; саме так рахує перевірка тепер.

   Висновок, який дорожчий за самі три правила: підрахунок теж треба перевіряти
   негативним контролем. «Виміряно» без цього означає рівно стільки ж, скільки
   «здається».

   `.mini-avatar` тримає літеру, а не файл, — тому відома проблема з
   незавантаженими аватарами його не стосується. Кольори тут літеральні, як у
   прев'ю: це не тема, а власна палітра кружка, і токена під неї канон не дає.
   Білий текст на #101827…#31405a дає 12.6:1 у найсвітлішому кінці. ══════ */
.row { display: flex; align-items: center; }
.gap12 { gap: var(--kp-space-3); }
.mini-avatar {
  width: 36px;
  height: 36px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  color: #fff;
  background: linear-gradient(135deg, #31405a, #101827);
  font-weight: 850;
  position: relative;
  flex: 0 0 auto;
}

/* ══ ПАНЕЛЬ І КІКЕР СТАНУ ══════════════════════════════════════════════════
   v1.1.6.345 (REV46, ARENA-18).

   Канон:
       .arena-panel   padding 18            (картка зі щільнішим падінгом,
                                             ніж типові 24 — під таблиці й списки)
       .state-kicker  flex; gap 8; wrap; margin-bottom 14
       .spec-state    тло приглушеної поверхні, приглушений текст
                      (значок «стан специфікації» поруч із головним значком)

   `.arena-ledger` / `.arena-ledger-row` / `.arena-amount--*` із того ж екрана
   прев'ю СВІДОМО не додані: у пакеті немає жодної розмітки, що їх вживала б, і
   вони стали б мертвими селекторами тієї ж хвилини. Причина — нижче, у
   коментарі всередині modules/arena/ducats: канонний реєстр дукатів це
   ГАМАНЕЦЬ глядача, а сторінка продукту — публічний реєстр емісії по всій грі.
   Різні сторінки з однаковою назвою. Примітив прийде разом зі сторінкою, яка
   його справді вживатиме. ═════════════════════════════════════════════════ */
.arena-panel { padding: 18px; }

.state-kicker {
  display: flex;
  align-items: center;
  gap: var(--kp-space-2);
  flex-wrap: wrap;
  margin-bottom: 14px;
}

.kp-badge.spec-state {
  background: var(--kp-color-surface-muted);
  color: var(--kp-color-text-muted);
}

/* ══ РОЗКЛАДКА Й РЯДОК-ЗНАЧЕННЯ ═══════════════════════════════════════════
   v1.1.6.346 (REV46, ARENA-19).

   Чотири примітиви, які прев'ю вживає всюди, а пакет не мав жодного:
       .stack                  grid; gap 16          — 15 входжень у прев'ю
       .account-field          padding 13/0; лінія зверху; перший без лінії
                                                     — 25 входжень
       .arena-contract         рамка + приглушене тло; значок і текст поруч
                                                     — 9 входжень
       .arena-dashboard-grid   1.35fr | minmax(290px,.65fr); на телефоні 1fr
                                                     — 2 входження

   Зіткнень немає — перевірено підрахунком із ВИРІЗАНИМ PHP і з негативним
   контролем на самому лічильнику (kp-card дав 204, тобто лічильник рахує):
   stack 0, account-field 0, arena-contract 0, arena-dashboard-grid 0.
   CSS-правил із цими іменами теж немає в жодному файлі теми.

   Перевіряю це окремо, бо stack — ім'я такого ж штибу, як row, на якому я
   помилився у .340: коротке, загальне, і саме таке легко вже зайняте. Різниця
   в тому, що цього разу лічильник має контроль.

   .arena-contract тримає ПОЯСНЕННЯ ПРАВИЛА поруч із дією (у прев'ю — «гроші не
   живуть у UI» біля ставки). Це не сповіщення: воно не з'являється й не зникає,
   а стоїть завжди. Тому й окремий примітив, а не kp-alert. ══════════════ */
.stack {
  display: grid;
  gap: var(--kp-space-4);
}

.account-field {
  padding: 13px 0;
  border-top: var(--kp-border-width) solid var(--kp-color-border);
}
.account-field:first-of-type { border-top: 0; }

/* 🔴 ЦІ ДВА ПРАВИЛА Я ЗАБУВ У `.346`, І ЦЕ БУЛА НЕ ДРІБНИЦЯ.
   Тоді я переніс із прев'ю лише `padding` і `border-top` — а канон описує
   `.account-field` ЧОТИРМА правилами. Без цих двох підпис і значення
   лишались рядковими й стояли поруч, тоді як у прев'ю підпис — приглушений
   12px НАД значенням, а значення — блоком під ним. Тобто на `/arena/ring`
   ставка й діапазон малювались не так, як задумано, і жодна проба цього не
   сказала б: правило синтаксично живе, клас у розмітці є.

   Урок: переносячи примітив, брати ВСІ правила з його імені, а не перше-ліпше.
   Найлегше загубити саме дочірні — вони стоять окремим рядком нижче. */
.account-field > span,
.account-field > small {
  display: block;
  color: var(--kp-color-text-muted);
  font-size: var(--kp-text-xs);
}
.account-field > strong {
  display: block;
  margin-top: 2px;
}

.arena-contract {
  display: flex;
  gap: 10px;
  align-items: flex-start;
  padding: var(--kp-space-3) 14px;
  border: var(--kp-border-width) solid var(--kp-color-border);
  border-radius: var(--kp-radius-lg);
  background: var(--kp-color-surface-muted);
  color: var(--kp-color-text-muted);
  font-size: var(--kp-text-sm);
}
.arena-contract strong {
  display: block;
  color: var(--kp-color-text);
  margin-bottom: 2px;
}

.arena-dashboard-grid {
  display: grid;
  grid-template-columns: minmax(0, 1.35fr) minmax(290px, .65fr);
  gap: 14px;
  margin-top: 14px;
}
@media (max-width: 980px) {
  .arena-dashboard-grid { grid-template-columns: 1fr; }
}

/* ══ СІТКА ЗАЛИ СЛАВИ ══════════════════════════════════════════════════════
   v1.1.6.347 (REV46, ARENA-20).

       .arena-hall-grid   grid; три рівні колонки; gap 12
                          на вузькому — дві

   Канонний `.arena-champion` (велике коло 76px, ім'я по центру, рядок статистики)
   тут НЕ доданий: у прев'ю картка зали слави тримає ОДНОГО чемпіона сезону, а
   сторінка продукту — категорію з подіумом на трьох. Примітив без споживача
   став би мертвим селектором тієї ж хвилини — те саме рішення, що з
   arena-ledger у .345. ═══════════════════════════════════════════════════ */
.arena-hall-grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--kp-space-3);
}
@media (max-width: 980px) {
  .arena-hall-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 620px) {
  .arena-hall-grid { grid-template-columns: 1fr; }
}

/* ══ «БРОВА» НАД ЗАГОЛОВКОМ ════════════════════════════════════════════════
   1.1.7.027 (канон R8.91 `.comm13-eyebrow`, `.kp-card .eyebrow`): короткий надпис великими літерами кольором акценту
   над заголовком розділу — «ПРАВИЛА», «ПОДІЇ», «РИНОК СПІЛЬНОТИ». У темі було ~30 локальних копій (`*__eyebrow`)
   з різними розмірами; нові розділи беруть цей примітив, а не заводять ще одну. */
.kp-eyebrow {
  display: block;
  font-size: 11px;
  font-weight: 800;
  letter-spacing: .12em;
  line-height: 1.3;
  text-transform: uppercase;
  color: var(--kp-accent);
}

/* ══ МОНО — ЛИШЕ ДЛЯ КОДУ ═════════════════════════════════════════════════
   1.1.7.027: `--n-font-mono`/`--mono` стали шрифтом інтерфейсу (див. tokens.css), тож справжній моноширинний шрифт
   для коду задається явно — `--kp-font-mono`, як у каноні. :where() — нульова специфічність: будь-яке свідоме
   оформлення коду в модулі лишається сильнішим. */
:where(code, pre, kbd, samp) { font-family: var(--kp-font-mono); }

/* ══ ГЕРОЙ РОЗДІЛУ — канон `.lst17-hero` ══════════════════════════════════
   1.1.7.027 (канон R8.91 view-listings): `.kp-listings-hero` — спільна голова оголошень, створення/редагування
   оголошення, предметів, ринку, ресурсів, рейтингу квестів. У каноні це картка з м'яким градієнтом акценту, великим
   заголовком clamp(27–42px) і діями праворуч унизу; у «кібер»-темі була пласка картка з кутиками й моно-заголовком
   20px. Кутики з розмітки прибрано; тут — геометрія й типографіка канону. */
.kp-listings-hero {
  align-items: flex-end;
  gap: 24px;
  padding: 28px;
  border-radius: 24px;
  background: linear-gradient(135deg, color-mix(in srgb, var(--kp-accent) 13%, var(--kp-color-surface)), var(--kp-color-surface));
  margin-bottom: 14px;
}
.kp-listings-hero__eyebrow { font-size: 11px; font-weight: 800; letter-spacing: .12em; margin-bottom: 0; }
.kp-listings-hero__title {
  font-size: clamp(27px, 4vw, 42px);
  font-weight: 800;
  line-height: 1.1;
  letter-spacing: normal;
  margin: 5px 0 7px;
}
.kp-listings-hero__sub { max-width: 700px; font-size: inherit; }
.kp-listings-hero__cta { flex-direction: row; flex-wrap: wrap; gap: 8px; }
@media (max-width: 540px) {
  .kp-listings-hero { padding: 18px; align-items: stretch; }
  .kp-listings-hero__title { font-size: 27px; }
}

/* ══ ГОЛОВА СТОРІНКИ МОДУЛЯ — `.kp-page-head` ══════════════════════════════
   1.1.7.027 (канон R8.91 `kp-card page-head`, напр. view-post-create): шість шаблонів (створення допису, рівень,
   статистика, активність, аналітика, головна-дашборд) мають власну голову `.kp-page-head` / `__title` / `__subtitle`,
   і лише частина модулів її оформлює. Решта малювала заголовок дрібним текстом без поверхні. Тут — база канону через
   :where() (нульова специфічність): модульне оформлення лишається сильнішим. */
:where(.kp-page-head) {
  display: block;
  padding: 18px 20px;
  margin: 0 0 14px;
  border: 1px solid var(--kp-color-border);
  border-radius: 18px;
  background: var(--kp-color-surface);
  box-shadow: var(--kp-shadow-sm, none);
}
/* Заголовок — класом (0,1,0), а не :where(): базове `h1 {…}` теми інакше перемагало б нульову специфічність. */
.kp-page-head__title { margin: 0 0 3px; font-size: var(--kp-text-xl); line-height: var(--kp-leading-tight); }
.kp-page-head__subtitle { margin: 0; color: var(--kp-color-text-muted); }
@media (max-width: 480px) {
  :where(.kp-page-head) { padding: 14px 15px; }
  .kp-page-head__title { font-size: var(--kp-text-lg); }
}

/* 1.1.7.059: one reusable contract for contextual UI icons. The PNG is white/transparent;
   the user-selected accent paints the rounded square in CSS. */
.kp-accent-glyph {
  width: 26px; height: 26px; padding: 5px; box-sizing: border-box;
  display: inline-grid; place-items: center; flex: 0 0 26px;
  border-radius: 8px; background: var(--kp-accent); color: #fff;
  line-height: 1; vertical-align: middle;
}
.kp-accent-glyph img.kp-menu-icon {
  width: 16px; height: 16px; display: block; object-fit: contain; vertical-align: 0; filter: none;
}
.kp-accent-glyph--sm { width: 22px; height: 22px; padding: 4px; flex-basis: 22px; border-radius: 7px; }
.kp-accent-glyph--sm img.kp-menu-icon { width: 14px; height: 14px; }
.kp-accent-glyph--lg { width: 32px; height: 32px; padding: 6px; flex-basis: 32px; border-radius: 10px; }
.kp-accent-glyph--lg img.kp-menu-icon { width: 20px; height: 20px; }


/* 1.1.7.072 — icon tiles: keep the accent visible on white, but make it translucent and calm. */
.kp-accent-glyph {
  background: color-mix(in srgb, var(--kp-accent) 24%, transparent);
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--kp-accent) 18%, var(--kp-color-border));
}

/* END css/canon-primitives.css */

/* START css/style.css */
/* v1.1.6.312 (REV46): шар --n-* переведено на канонні імена.
   Обіцяно в .304 і зроблено окремо, щоб diff лишався читабельним.
   Рівність тут навіть простіша за минулий раз: канонні токени ВИЗНАЧЕНІ через
   ці ж --n-* (`--kp-color-surface: var(--n-bg-2)`), тож збіг не випадковий, а
   за побудовою. Однак я все одно звірив кожну пару після повного розкриття
   ланцюжка var() і ОКРЕМО у світлій та темній схемі — сім із семи.
   Жодне обчислене значення не змінюється. --n-* лишаються оголошеними в
   tokens.css: канонні токени спираються на них, і це шар значень під шаром імен. */
/* v1.1.6.304 (REV46): короткі аліаси кольорів замінено канонними іменами.
   Перепис панелей показав, що розсип у тлі й рамці — не в ЗНАЧЕННЯХ, а в ІМЕНАХ:
   тло малювалось як --n-bg-2 (101), --panel (76), --arena-panel (29) — три назви
   одного кольору; рамка як --n-line (172), --border (157), --arena-border (67).
   Канонні імена при цьому вживались 23 рази — тобто мова канону була в меншості
   у власному проекті.
   Замінено лише те, що ДОВЕДЕНО однакове: кожна пара звірена після повного
   розкриття ланцюжка var() і ОКРЕМО у світлій та темній схемі. Жодне обчислене
   значення не змінюється — це перейменування, не редизайн. */
/* v1.1.6.303 (REV46): радіус панелей зведено до канонного --kp-radius-lg.
   Перепис: у пакеті 690 справжніх панелей (тло + рамка + радіус, не контроли)
   і 68 різних радіусів. Найпоширеніший — уже канонний (91), далі йдуть 10px
   (86), 14px (59) і --n-r-md, який теж 14px (58): три сусідні значення для
   одного й того самого — картки. Канон для неї однозначний:
   `.kp-card { border-radius: var(--kp-radius-lg) }`.
   Контроли (кнопки, чипи, аватари, поля) з проходу виключені: там канон
   справді вживає 10-11px, і зводити їх до 12 означало б відійти ВІД прев'ю.
   `--r` (20px) теж не чіпано — це інший рівень, а не сусіднє значення. */
/* v1.1.6.291 (REV46, CANON-STEP-8): значення, які ВЖЕ дорівнювали кроку
   канонної шкали, отримали її ім'я: радіус 0, pill 0,
   відступи 1, вага 0. Жодне число не змінено — це
   перейменування, не редизайн. Значення поза шкалою лишились літералами
   свідомо: прев'ю канону саме вживає 10/14/6px для падінгів компонентів,
   тож стягувати їх до шкали означало б відійти ВІД прев'ю, а не до нього. */
/* v1.1.6.290 (REV46, CANON-STEP-7): 8 значень font-size переведено на
   канонну шкалу (--kp-text-xs/sm/md/lg/xl/2xl). Правило: найближчий крок,
   і лише коли зміна ≤12%%. Все, що далі (герой-цифри 3-5rem, мікропідписи
   <0.6rem), лишилось літералом — це не текстова шкала. Перелік: _meta/. */
/* v1.1.6.297 (REV46, CACHE-P0-01): @import-и звідси ПРИБРАНО.
 *
 * Вони не мали `?v=` — адреса `tokens.css` не змінювалась ніколи, тож браузер і
 * service worker віддавали з кешу старий файл після кожного релізу. Оновлювався
 * лише style.css, бо версія була в його адресі.
 *
 * Тепер увесь ланцюжок емітує ThemeManager окремими <link> із версією, у тому
 * самому порядку: tokens → base → cyber-components → kp-form → nova-bridge →
 * app-shell → canon-primitives → (цей файл). Каскад не змінився.
 *
 * ДОДАЄШ НОВИЙ ГЛОБАЛЬНИЙ CSS — у масив $cssChain у ThemeManager, не сюди.
 */
/* style.css · v1.1.3.359
 * Imports (global) + layout primitives + page-level chrome.
 * UI components → cyber-components.css. Widget styles → widgets/.
 *
 * v359 REFACTOR: layout CSS файли БІЛЬШЕ НЕ @import-яться сюди!
 * Кожен layout (`app-layout.css`, `messages-layout.css`, `auth-layout.css` тощо)
 * завантажується ТІЛЬКИ для свого route через config/layout.php → page_css.
 *
 * Це усуває cross-layout CSS conflicts (як v358 коли base.css `min-height:100vh`
 * блокував messages route). Тепер кожна сторінка отримує лише свій layout CSS,
 * чисто і без `!important` хаків.
 *
 * Якщо додаєш новий layout — НЕ додавай тут @import. Замість того:
 *   1. Створи `layouts/app/{name}-layout.css`
 *   2. У `config/layout.php` додай case до layout matcher (там вже є logic).
 */

/* v1.1.5.182: scoped SOFT-UI примітиви для перенесених сторінок (account,
   media_picker, crop_modal). Селектори префіксовані — глобального впливу нема.

   v1.1.5.664 — УВАГА, НЕ ВИДАЛЯТИ ЗА НАЗВОЮ. Тема Nova знята з проєкту повністю, і назва
   файлу спокушає прибрати його «як залишок». Це помилка: файл НЕСУЧИЙ. Перевірено
   виконанням — із 81 класу, які він визначає, 59 уживаються в живих шаблонах і скриптах
   (серед них .btn, .btn-primary, .alert, .admin-input). Видалення роздягне частину сторінок.
   Що тут справді застаріле — це ІМ'Я. Перейменування вимагає одночасної правки цього
   @import і не дає нічого, крім ясності, тому відкладене свідомо. */
/* v1.1.6.285 (REV46, CANON-STEP-2): канонічні примітиви (.kp-btn, .kp-form-field
   типово; з .293 і .kp-card з .kp-badge теж). ОСТАННІМ у ланцюжку
   свідомо: nova-bridge скоупований (0,2,0), і при рівній специфічності виграє
   той, хто пізніше. Тут це і потрібно — глобальна база .kp-btn має (0,1,0), тож
   nova-bridge усе одно сильніший; а канон-шар із html[...] (0,2,1) вищий за
   обидва. Порядок фіксує лише поведінку рівних. Деталі й перелік відхилень від
   прев'ю — у шапці самого файла. */
/* v1.1.6.286 (REV46, CANON-STEP-3): оболонка сторінки — правила, що жили в
   app-layout.css і тому діяли на 1 layout із 9 (див. шапку файла). */


/* ─── Layout primitives ─── */
.kp-page {
  max-width: var(--n-content-w);
  margin-inline: auto;
  padding: var(--n-s-5) var(--n-s-3);
  position: relative;
  z-index: 1;
}


/* ─── Containers ─── */
.kp-card {
  background: var(--kp-color-surface);
  border: 1px solid var(--kp-color-border);
  border-radius: var(--r);
  padding: var(--n-s-5);
  transition: all var(--n-dur-base) var(--n-ease);
}
.kp-card:hover { border-color: var(--border-h); }
.kp-card--accent {
  position: relative;
  overflow: hidden;
}
.kp-card--accent::before {
  content: '';
  position: absolute;
  top: 0; left: 0; right: 0;
  height: 2px;
  background: linear-gradient(90deg, transparent, var(--kp-accent), transparent);
  opacity: 0.5;
}
.kp-card__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: var(--n-s-3);
  gap: var(--n-s-3);
}
.kp-card__title {
  font-family: var(--mono);
  font-size: var(--kp-text-sm);
  font-weight: var(--n-w-black);
  letter-spacing: 0.04em;
  color: var(--kp-color-text);
  display: flex;
  align-items: center;
  gap: var(--n-s-2);
}
.kp-card__sub {
  font-size: var(--kp-text-xs);
  color: var(--kp-color-text-muted);
  margin-top: 3px;
  line-height: 1.4;
}
.kp-card__body { margin-top: var(--n-s-2); }
.kp-card__acts {
  display: flex;
  gap: var(--n-s-2);
  flex-wrap: wrap;
  margin-top: var(--n-s-3);
}

/* Empty state */
.kp-empty {
  text-align: center;
  padding: var(--n-s-6) var(--n-s-4);
  background: var(--kp-color-surface);
  border: 1px solid var(--kp-color-border);
  border-radius: var(--r);
  color: var(--kp-color-text-muted);
}
.kp-empty__title {
  display: block;
  color: var(--kp-color-text);
  font-family: var(--mono);
  font-size: var(--kp-text-md);
  font-weight: var(--n-w-black);
  margin-bottom: 6px;
}


/* ─── Page-level chrome ─── */

/* Page title (для сторінок без hero) */
.kp-page-title {
  font-family: var(--mono);
  font-size: var(--kp-text-xl);
  font-weight: var(--n-w-black);
  letter-spacing: -0.01em;
  color: var(--kp-color-text);
  margin-bottom: var(--n-s-4);
  line-height: var(--n-lh-tight);
}

/* Alerts (success / warn / err) */
.kp-alert {
  display: flex;
  align-items: flex-start;
  gap: var(--n-s-3);
  padding: var(--n-s-3) var(--n-s-4);
  border-radius: var(--r-md);
  border: 1px solid var(--kp-color-border);
  background: var(--kp-color-surface-muted);
  font-size: var(--kp-text-sm);
  line-height: 1.5;
  margin-bottom: var(--n-s-3);
}
.kp-alert::before {
  content: 'ℹ';
  font-family: var(--mono);
  font-weight: 800;
  font-size: var(--kp-text-md);
  flex-shrink: 0;
}
.kp-alert--ok, .kp-alert--success {
  color: var(--hp);
  background: var(--hp-dim);
  border-color: color-mix(in srgb, var(--hp) 25%, transparent);
}
.kp-alert--ok::before, .kp-alert--success::before { content: '✓'; }
.kp-alert--warn, .kp-alert--warning {
  color: var(--gold);
  background: var(--gold-dim);
  border-color: color-mix(in srgb, var(--gold) 25%, transparent);
}
.kp-alert--warn::before, .kp-alert--warning::before { content: '⚠'; }
.kp-alert--err, .kp-alert--error, .kp-alert--danger {
  color: var(--red);
  background: var(--red-dim);
  border-color: color-mix(in srgb, var(--red) 25%, transparent);
}
.kp-alert--err::before, .kp-alert--error::before, .kp-alert--danger::before { content: '⚠'; }
.kp-modal.is-open { display: flex; }
@keyframes kpModalIn {
  from { opacity: 0; }
  to   { opacity: 1; }
}
@keyframes kpModalDialogIn {
  from { opacity: 0; transform: scale(0.95) translateY(8px); }
  to   { opacity: 1; transform: scale(1) translateY(0); }
}

/* Divider */
.kp-divider {
  border: 0;
  border-top: 1px solid var(--kp-color-border);
  margin: var(--n-s-4) 0;
}

/* Progress bar */
.kp-progress {
  height: 6px;
  background: var(--kp-color-surface-muted);
  border-radius: var(--n-r-pill);
  overflow: hidden;
  position: relative;
}

/* Skeleton loader */
.kp-skeleton {
  background: linear-gradient(90deg, var(--kp-color-surface-muted), var(--cell-h), var(--kp-color-surface-muted));
  background-size: 200% 100%;
  border-radius: var(--n-r-md);
  animation: kpSkeleton 1.4s ease-in-out infinite;
  color: transparent;
  user-select: none;
}
@keyframes kpSkeleton {
  0%   { background-position: 200% 0; }
  100% { background-position: -200% 0; }
}

/* ─── v1.1.4.024 — Currency icons (kp_currency_icon helper) ───
 * Bitmap PNG icons (gold/crystal/ducat) replace emoji 🪙/💎/🎫 у UI.
 */
.kp-currency-icon {
  display: inline-block;
  vertical-align: middle;
  object-fit: contain;
  flex-shrink: 0;
}
/* Align з text baselines у running прозі */
span > .kp-currency-icon,
.kp-tx__amount .kp-currency-icon,
.kp-wlt-tx__cur .kp-currency-icon {
  vertical-align: -3px;
}

/* v1.1.4.025 — kp-money inline unit (amount + currency icon) */
.kp-money {
  display: inline-flex;
  align-items: center;
  gap: var(--kp-space-1);
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}
.kp-currency-icon--inline {
  vertical-align: middle;
}

/* ============================================================
   v1.1.5.242: global boxed <select> — canonical .kp-select (boxed variant).
   Розширює наявний .kp-select (bridge має underline-варіант під account-scope).
   Тут — глобальний boxed-вигляд: рамка+фон+radius+chevron. Canonical --n-* tokens only.
   Замість неканонічного .kp-select-native (видалено). Реєстр: один select API.
   ============================================================ */
.kp-select-box { position: relative; display: block; }
.kp-select-box > .kp-select,
select.kp-select--boxed {
  -webkit-appearance: none; -moz-appearance: none; appearance: none;
  width: 100%; padding: 11px 40px 11px 14px;
  border: 1px solid var(--n-line-strong); border-radius: var(--kp-radius-lg);
  background: var(--n-card); color: var(--kp-color-text);
  font-family: var(--n-font-body, inherit); font-size: var(--kp-text-sm); font-weight: 600; line-height: 1.2;
  cursor: pointer;
  transition: border-color .15s ease, background .15s ease, box-shadow .15s ease;
}
.kp-select-box > .kp-select:hover,
select.kp-select--boxed:hover { border-color: var(--kp-accent); }
.kp-select-box > .kp-select:focus,
select.kp-select--boxed:focus { outline: none; }
.kp-select-box > .kp-select:focus-visible,
select.kp-select--boxed:focus-visible {
  outline: none; border-color: var(--kp-accent);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--kp-accent) 22%, transparent);
}
.kp-select-box > .kp-select:disabled,
select.kp-select--boxed:disabled { opacity: .55; cursor: not-allowed; }
.kp-select-box__chevron {
  position: absolute; right: 14px; top: 50%; transform: translateY(-50%);
  pointer-events: none; color: var(--kp-accent); font-size: var(--kp-text-xs);
}


/* ═══════════════════════════════════════════════════════════════════════════
   1.1.7.161 — РОЗМІТКА ТЕКСТУ: ОДИН НАБІР ПРАВИЛ НА ВЕСЬ ПРОЄКТ
   ═══════════════════════════════════════════════════════════════════════════
   Ці правила жили в `widgets/posts/post_card/post_card.css` під класом
   `.post-text--markdown` — тобто вантажились лише там, де є картка допису.

   Коли розмітку ввімкнули ще й в оголошеннях, рекламі, описі спільноти й
   профілю, копіювати їх по файлах означало б завести п'ять наборів, які з
   часом розійдуться. Тому вони переїхали сюди, у глобальний `style.css`, під
   нейтральним класом `.kp-md`, а картка допису тепер уживає той самий клас.

   ⚠️ ОСОБЛИВО ВАЖЛИВИЙ РЯДОК ПРО СПИСКИ. `base.css` глушить маркери
   (`ul, ol { list-style: none }`) заради меню. Без правил нижче розмічений
   список показував би відступи без крапок — рівно та вада, яку лагодили у
   `.139` для дописів. Якби правила лишились у картці допису, на чотирьох нових
   поверхнях вона повернулась би мовчки.
   ═══════════════════════════════════════════════════════════════════════════ */
.kp-md > :first-child { margin-top: 0; }
.kp-md > :last-child { margin-bottom: 0; }
.kp-md p { margin: 0 0 var(--n-s-3); }
.kp-md .post-md-heading {
  margin: var(--n-s-4) 0 var(--n-s-2);
  color: var(--kp-color-text);
  line-height: 1.25;
  font-weight: var(--n-w-bold);
  letter-spacing: -0.015em;
}
.kp-md .post-md-heading--1 { font-size: clamp(1.2rem, 2.4vw, 1.45rem); }
.kp-md .post-md-heading--2 { font-size: clamp(1.1rem, 2.1vw, 1.28rem); }
.kp-md .post-md-heading--3,
.kp-md .post-md-heading--4 { font-size: 1.05rem; }
.kp-md ul,
.kp-md ol { margin: 0 0 var(--n-s-3); padding-left: 1.45rem; }
.kp-md li + li { margin-top: 0.28rem; }
/* 1.1.7.139 (tester report BUG-01): base.css resets `ul, ol { list-style: none }` for
   menus, so Markdown lists in a post showed indents without markers. Post bodies get
   their markers back here, and only here: "•" for bullets, "1." for numbers, a
   different bullet per nesting level. */
.kp-md ul { list-style: disc outside; }
.kp-md ol { list-style: decimal outside; padding-left: 1.75rem; }
.kp-md ul ul { list-style-type: circle; }
.kp-md ul ul ul { list-style-type: square; }
.kp-md li { display: list-item; }
.kp-md li::marker { color: var(--kp-color-text-muted); }
.kp-md li > ul,
.kp-md li > ol { margin: 0.28rem 0 0; }
.kp-md li > p { margin: 0 0 0.35rem; }
.kp-md li > p:last-child { margin-bottom: 0; }
.kp-md blockquote {
  margin: var(--n-s-3) 0;
  padding: var(--n-s-2) var(--n-s-3);
  border-inline-start: 3px solid color-mix(in srgb, var(--kp-accent) 55%, transparent);
  border-radius: 0 var(--n-r-sm) var(--n-r-sm) 0;
  background: color-mix(in srgb, var(--kp-accent) 7%, transparent);
  color: var(--kp-color-text-muted);
}
.kp-md blockquote p { margin: 0; }
.kp-md code {
  padding: 0.12em 0.34em;
  border-radius: 0.38em;
  background: var(--kp-color-surface-muted, rgba(127,127,127,.12));
  font: 0.92em/1.45 ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
}
.kp-md pre {
  margin: var(--n-s-3) 0;
  padding: var(--n-s-3);
  overflow-x: auto;
  border: 1px solid var(--kp-color-border);
  border-radius: var(--n-r-md);
  background: var(--kp-color-surface-muted, rgba(127,127,127,.10));
}
.kp-md pre code { padding: 0; background: transparent; white-space: pre; }
.kp-md del { opacity: .72; }
.kp-md hr {
  height: 1px;
  margin: var(--n-s-4) 0;
  border: 0;
  background: var(--kp-color-border);
}

/* .276: real Markdown images, safe on 320px phones and wide desktops. */
.kp-md {
  min-width: 0;
  overflow-wrap: anywhere;
  line-height: 1.6;
}
.kp-md .kp-md-image {
  display: block;
  width: auto;
  height: auto;
  max-width: 100%;
  max-height: min(70vh, 720px);
  object-fit: contain;
  margin: var(--n-s-3) 0;
  border-radius: var(--n-r-md);
}
@media (max-width: 600px) {
  .kp-md .post-md-heading { line-height: 1.35; margin-block: var(--n-s-3) var(--n-s-2); }
  .kp-md .post-md-heading--1 { font-size: 1.3rem; }
  .kp-md .post-md-heading--2 { font-size: 1.18rem; }
  .kp-md p { margin-bottom: var(--n-s-3); }
  .kp-md pre { max-width: 100%; }
}


/* ── 1.1.7.169 (звіт тестувальника ARENA-01, 10.10.2026): банер «ви на роботі» ──────────────────
   Один стан персонажа — один вигляд. Тренування показує його з v1.1.3.996 власною розміткою
   (`.kp-training-working-banner`); арена, ринг і підземелля не показували взагалі. Клас спільний,
   бо три копії одного банера в трьох модульних таблицях стилів розійшлися б при першій же правці.
   Токени й розміри зняті з банера тренування, щоб два повідомлення про одне й те саме не читались
   як різні речі. */
.kp-work-banner {
  display: flex;
  align-items: center;
  gap: var(--kp-space-3);
  padding: var(--kp-space-3) 14px;
  margin: 0 0 var(--kp-space-3);
  background: color-mix(in srgb, var(--warn, var(--kp-accent)) 10%, var(--kp-color-surface-muted));
  border: 1px solid color-mix(in srgb, var(--warn, var(--kp-accent)) 30%, var(--kp-color-border));
  border-left: 3px solid var(--warn, var(--kp-accent));
  border-radius: var(--kp-radius-lg);
}
.kp-work-banner__icon { font-size: var(--kp-text-xl); flex-shrink: 0; }
.kp-work-banner__body { display: flex; flex-direction: column; gap: 2px; flex: 1; min-width: 0; }
.kp-work-banner__body strong {
  font-family: var(--mono);
  font-size: var(--kp-text-sm);
  font-weight: 800;
  color: var(--kp-color-text);
  letter-spacing: .02em;
}
.kp-work-banner__body span { font-size: var(--kp-text-xs); color: var(--kp-color-text-muted); }
.kp-work-banner__cta {
  flex-shrink: 0;
  padding: 7px 14px;
  background: var(--kp-accent);
  color: var(--n-text-on-accent);
  border-radius: var(--kp-radius-md);
  font-family: var(--mono);
  font-size: var(--kp-text-xs);
  font-weight: 700;
  text-decoration: none;
  white-space: nowrap;
}
.kp-work-banner__cta:hover { filter: brightness(1.08); }
@media (max-width: 480px) {
  .kp-work-banner { flex-wrap: wrap; }
  .kp-work-banner__cta { width: 100%; text-align: center; }
}

/* END css/style.css */
