/* Дизайн-система хакку, собрана в один файл: deploy/build.js */
/* ── tokens/core.css ───────────────────────────────── */
/* ============================================================
   Hakku 2.0 — Core tokens (layer 1 of 4: core)
   ----------------------------------------------------------------
   One core for the whole Hakku ecosystem: hakku.ai, business.hakku.ai,
   LMS + client forks (cbr.hakku.ai), OS, Cowork, communications.
   Paper-first: white canvas, black ink, ONE hairline grey, three
   saturated accents used sparingly, two signature gradients for key
   moments. Holographic glass renders are the signature graphic.

   Layers (import order in tokens.css):
     core.css          ← raw palette, fonts, scale — this file
     themes.css        ← [data-theme="light"|"dark"], [data-canvas="ink"]
     mode-comm.css     ← [data-mode="comm"]    landings / decks / КП
     mode-product.css  ← [data-mode="product"] LMS / OS / Cowork UI

   Defaults (no attributes) = light theme, comm-like canon scale —
   backwards-compatible with colors_and_type.css consumers.
   ============================================================ */

/* ---- Fonts (ALL LOCAL — rule: no CDN, no external requests) --- */
/* Tektur — display / headings / numerals. Brand rule: display stays
   Regular 400; heavier cuts exist for UI accents only. */
@font-face { font-family: "Tektur"; font-weight: 400; src: url("/severstal/design_system/fonts/Tektur-Regular.ttf")  format("truetype"); font-display: swap; }
@font-face { font-family: "Tektur"; font-weight: 500; src: url("/severstal/design_system/fonts/Tektur-Medium.ttf")   format("truetype"); font-display: swap; }
@font-face { font-family: "Tektur"; font-weight: 600; src: url("/severstal/design_system/fonts/Tektur-SemiBold.ttf") format("truetype"); font-display: swap; }
@font-face { font-family: "Tektur"; font-weight: 700; src: url("/severstal/design_system/fonts/Tektur-Bold.ttf")     format("truetype"); font-display: swap; }
@font-face { font-family: "Tektur"; font-weight: 800; src: url("/severstal/design_system/fonts/Tektur-ExtraBold.ttf")format("truetype"); font-display: swap; }
@font-face { font-family: "Tektur"; font-weight: 900; src: url("/severstal/design_system/fonts/Tektur-Black.ttf")    format("truetype"); font-display: swap; }

/* Tektur Narrow / Tight — condensed display for dense numerals,
   deck stats and tight lockups (inherited from the hakku.ai system). */
@font-face { font-family: "Tektur Narrow"; font-weight: 400; src: url("/severstal/design_system/fonts/TekturNarrow-Regular.ttf")  format("truetype"); font-display: swap; }
@font-face { font-family: "Tektur Narrow"; font-weight: 500; src: url("/severstal/design_system/fonts/TekturNarrow-Medium.ttf")   format("truetype"); font-display: swap; }
@font-face { font-family: "Tektur Narrow"; font-weight: 600; src: url("/severstal/design_system/fonts/TekturNarrow-SemiBold.ttf") format("truetype"); font-display: swap; }
@font-face { font-family: "Tektur Narrow"; font-weight: 700; src: url("/severstal/design_system/fonts/TekturNarrow-Bold.ttf")     format("truetype"); font-display: swap; }
@font-face { font-family: "Tektur Tight";  font-weight: 400; src: url("/severstal/design_system/fonts/TekturTight-Regular.ttf")   format("truetype"); font-display: swap; }
@font-face { font-family: "Tektur Tight";  font-weight: 500; src: url("/severstal/design_system/fonts/TekturTight-Medium.ttf")    format("truetype"); font-display: swap; }
@font-face { font-family: "Tektur Tight";  font-weight: 600; src: url("/severstal/design_system/fonts/TekturTight-SemiBold.ttf")  format("truetype"); font-display: swap; }
@font-face { font-family: "Tektur Tight";  font-weight: 700; src: url("/severstal/design_system/fonts/TekturTight-Bold.ttf")      format("truetype"); font-display: swap; }

/* Manrope — body / UI (с 2.1.0, выбор команды 2026-07-20). Variable 200–800, кириллица нативная. */
@font-face { font-family: "Manrope"; font-weight: 200 800; src: url("/severstal/design_system/fonts/Manrope-Variable.ttf") format("truetype-variations"); font-display: swap; }

/* Inter — legacy body / UI (fallback, поверхности до миграции). Variable file covers 100–900. */
@font-face { font-family: "Inter"; font-weight: 100 900; src: url("/severstal/design_system/fonts/InterVariable.ttf") format("truetype-variations"); font-display: swap; }

:root {
  /* ---- Core neutrals -------------------------------------- */
  --paper:        #ffffff;   /* canvas — the default ground */
  --ash:          #f4f4f5;   /* soft ashen — alternating section ground */
  --ink:          #000000;   /* absolute black — text + ink surfaces */
  --hairline:     #818181;   /* THE ONLY GREY. Borders/dividers only. */

  /* Ink tints (text hierarchy on paper — by opacity, never new hues) */
  --ink-90:       rgba(0,0,0,.90);
  --ink-72:       rgba(0,0,0,.72);   /* body copy on paper */
  --ink-55:       rgba(0,0,0,.55);   /* secondary / captions */
  --ink-40:       rgba(0,0,0,.40);   /* faint / disabled */
  --ink-12:       rgba(0,0,0,.12);   /* soft border on paper */
  --ink-08:       rgba(0,0,0,.08);   /* divider / hairline-soft */
  --ink-04:       rgba(0,0,0,.04);   /* tonal fill / chip ground */

  /* Paper tints (text hierarchy on ink surfaces) */
  --paper-90:     rgba(255,255,255,.90);
  --paper-72:     rgba(255,255,255,.72);
  --paper-55:     rgba(255,255,255,.55);
  --paper-40:     rgba(255,255,255,.40);
  --paper-16:     rgba(255,255,255,.16);
  --paper-10:     rgba(255,255,255,.10);
  --paper-04:     rgba(255,255,255,.04);

  /* ---- Brand accents (one trio for the whole ecosystem) ---- */
  --blue:         #2A3EF4;   /* ultramarine — primary accent / links */
  --magenta:      #D51F75;   /* raspberry  — emphasis / highlights */
  --orange:       #FD7202;   /* vivid orange — early-bird / urgency */

  /* ---- Signature gradients (key moments only) ------------- */
  --grad-cool:    linear-gradient(90deg, #2A3EF4 0%, #D51F75 100%); /* @kind color */ /* blue → magenta */
  --grad-warm:    linear-gradient(90deg, #D51F75 0%, #FD7202 100%); /* @kind color */ /* magenta → orange */
  --grad-full:    linear-gradient(90deg, #2A3EF4 0%, #D51F75 55%, #FD7202 100%); /* @kind color */

  /* ---- Radii (4 / 8 / 16 scale + pill) -------------------- */
  --r-xs:   4px;     /* chips, tags, inline marks */
  --r-sm:   8px;     /* inputs, small controls */
  --r-md:   16px;    /* default card */
  --r-lg:   24px;    /* large card / pricing tier */
  --r-pill: 999px;   /* buttons, badges, toggles */

  /* ---- Spacing (8pt base) --------------------------------- */
  --s-1: 4px;  --s-2: 8px;  --s-3: 12px; --s-4: 16px; --s-5: 24px;
  --s-6: 32px; --s-7: 48px; --s-8: 64px; --s-9: 96px; --s-10: 128px;

  /* Layout */
  --container:     1240px;  /* landing / product shell */
  --container-doc: 1040px;  /* reading width — proposals, one-pagers; narrower than landing */
  --gutter:        56px;

  /* ---- Motion (inherited from the hakku.ai system) --------- */
  --ease:         cubic-bezier(.16,1,.3,1);      /* entrances / large moves */
  --ease-brand:   cubic-bezier(.2,.8,.2,1);      /* hovers / small moves */
  --dur:          .6s;                            /* entrance duration */
  --dur-fast:     .18s;                           /* hover / press */
  --dur-med:      250ms;                          /* content reveal (slide/expand) */

  /* ---- Shadows (rare — brand prefers flat + hairline) ----- */
  --shadow-sm:    0 1px 2px rgba(0,0,0,.06);
  --shadow-md:    0 12px 32px rgba(0,0,0,.10);
  --glow-blue:    0 0 72px rgba(42,62,244,.20);
  --glow-magenta: 0 0 72px rgba(213,31,117,.20);

  /* ---- Type families -------------------------------------- */
  --font-display: "Tektur", system-ui, sans-serif;        /* headings + numerals */
  --font-display-narrow: "Tektur Narrow", "Tektur", system-ui, sans-serif;
  --font-display-tight:  "Tektur Tight",  "Tektur", system-ui, sans-serif;
  --font-body:    "Manrope", "Inter", system-ui, sans-serif; /* body + UI; comm и product сходятся на Manrope (решение 2026-09) */
  --font-mono:    ui-monospace, "JetBrains Mono", Menlo, monospace;

  /* ---- Type scale (comm-like canon defaults) --------------- */
  --t-hero:    clamp(3rem, 6.8vw, 5.6rem);   /* @kind font */ /* 48 → 90  hero */
  --t-h1:      clamp(2.4rem, 5vw, 4rem);     /* @kind font */ /* 38 → 64  section title */
  --t-h2:      clamp(2rem, 3.5vw, 3rem);     /* @kind font */ /* 32 → 48 */
  --t-h3:      1.75rem;                       /* 28  card title */
  --t-h4:      1.375rem;                      /* 22  small card title */
  --t-h5:      1.25rem;                       /* 20 */
  --t-figure:  clamp(1.375rem, 3.2vw, 2.125rem); /* @kind font */ /* 22 → 34 — large standalone figure (total, tariff, stat); a role like --t-body-lg, not a heading level */
  --t-body-lg: 1.1875rem;                     /* 19  hero subcopy / lede */
  --t-body:    1rem;                          /* 16  body */
  --t-small:   0.875rem;                      /* 14  UI / secondary */
  --t-caption: 0.75rem;                       /* 12  eyebrow / meta */

  /* ---- Semantic (light values; themes.css switches them) --- */
  --surface:        var(--paper);   /* page ground */
  --surface-soft:   var(--ash);     /* alternating section ground */
  --surface-card:   var(--paper);   /* card fill */
  --surface-raised: #fafafa;        /* raised panel / sticky bars */
  --surface-ink:    var(--ink);     /* inverted card / CTA */
  --fg:             var(--ink);
  --fg-muted:       var(--ink-72);
  --fg-faint:       var(--ink-55);
  --border:         var(--ink-12);  /* default soft border */
  --border-strong:  var(--ink);     /* "popular" / selected outline */
  --accent:         var(--blue);
  --accent-on:      #ffffff;        /* text on accent fills */
}

/* ── tokens/themes.css ───────────────────────────────── */
/* ============================================================
   Hakku 2.0 — Themes (layer 2 of 4: theme)
   ----------------------------------------------------------------
   Light is the default (values live in core.css :root).
   Dark is a FIRST-CLASS theme for PRODUCT surfaces (LMS, OS, Cowork)
   and stage decks. Communication surfaces — landings, КП, one-pagers —
   are light-only (decision 2026-09).

   [data-canvas="ink"] stays for SECTION-level inversion inside the
   light theme (hero strips, CTA bands, brand-guide blocks).

   Rule: components must consume SEMANTIC tokens (--surface, --fg,
   --border…), never raw --ink/--paper directly — that is what makes
   both themes and ink-canvas sections work.
   ============================================================ */

/* ---- Explicit light (same as :root defaults — for toggles) -- */
[data-theme="light"] {
  --surface:        var(--paper);
  --surface-soft:   var(--ash);
  --surface-card:   var(--paper);
  --surface-raised: #fafafa;
  --surface-ink:    var(--ink);
  --fg:             var(--ink);
  --fg-muted:       var(--ink-72);
  --fg-faint:       var(--ink-55);
  --border:         var(--ink-12);
  --border-strong:  var(--ink);
}

/* ---- Dark theme (first-class) ------------------------------ */
[data-theme="dark"] {
  --surface:        var(--ink);    /* absolute black canvas */
  --surface-soft:   #0a0a0a;       /* alternating section ground */
  --surface-card:   #0a0a0a;       /* card fill */
  --surface-raised: #111111;       /* raised panel / sticky bars */
  --surface-ink:    var(--paper);  /* "inverted" card flips to paper */
  --fg:             var(--paper);
  --fg-muted:       var(--paper-72);
  --fg-faint:       var(--paper-55);
  --border:         var(--paper-16);
  --border-strong:  var(--paper);

  /* product-surface extras (tables, rows) */
  --row-even:       #111111;
  --row-odd:        #0a0a0a;

  background: var(--surface);
  color: var(--fg);
}

/* Light-theme product extras (defined here so both themes carry them) */
:root, [data-theme="light"] {
  --row-even:       #f8fafc;
  --row-odd:        #ffffff;
}

/* ---- Ink canvas — section-level inversion (canon) ----------- */
[data-canvas="ink"] {
  --surface:        var(--ink);
  --surface-soft:   #0a0a0a;
  --surface-card:   #0a0a0a;
  --surface-raised: #111111;
  --surface-ink:    var(--paper);
  --fg:             var(--paper);
  --fg-muted:       var(--paper-72);
  --fg-faint:       var(--paper-55);
  --border:         var(--paper-16);
  --border-strong:  var(--paper);
  background: var(--ink);
  color: var(--paper);
}

/* Paper canvas — re-invert a region inside dark theme / ink canvas */
[data-canvas="paper"] {
  --surface:        var(--paper);
  --surface-soft:   var(--ash);
  --surface-card:   var(--paper);
  --surface-raised: #fafafa;
  --surface-ink:    var(--ink);
  --fg:             var(--ink);
  --fg-muted:       var(--ink-72);
  --fg-faint:       var(--ink-55);
  --border:         var(--ink-12);
  --border-strong:  var(--ink);
  background: var(--paper);
  color: var(--ink);
}

/* ── tokens/mode-comm.css ───────────────────────────────── */
/* ============================================================
   Hakku 2.0 — Communication mode (layer 3 of 4: mode)
   ----------------------------------------------------------------
   [data-mode="comm"] — landings, presentations, КП, one-pagers,
   digital offers. Big editorial typography, generous air, hero
   graphics (holographic glass renders), marquees.

   The core :root defaults ARE comm-scaled (canon), so this layer
   mostly documents and pins the comm-specific knobs.
   ============================================================ */

[data-mode="comm"] {
  /* Тело comm — Manrope. Решение 2026-09: пин Inter снят, Inter остаётся в фолбэке. */
  --font-body: "Manrope", "Inter", system-ui, sans-serif;

  --focus-ring: 0 0 0 3px rgba(42,62,244,.25);  /* accessible focus, same as product mode */

  /* 140px давало «почти весь экран между блоками»: .section читает токен
     как одно значение и сверху, и снизу. Сокращено до 36px, единое
     на всех ширинах. */
  --section-pad:    36px;
  --section-gap:    var(--s-8);

  /* Hero graphic sizing — holo render sits large, rotated, cropped */
  --hero-graphic:   520px;    /* nominal size of the holo render */

  /* Deck (presentation) geometry — 16:9 master */
  --slide-w:        1280px;
  --slide-h:        720px;
  --slide-pad:      72px;

  /* Comm type re-pins (same as core defaults; explicit for clarity) */
  --t-hero:  clamp(3rem, 6.8vw, 5.6rem);  /* @kind font */
  --t-h1:    clamp(2.4rem, 5vw, 4rem);    /* @kind font */
}

/* ── tokens/mode-product.css ───────────────────────────────── */
/* ============================================================
   Hakku 2.0 — Product mode (layer 3 of 4: mode)
   ----------------------------------------------------------------
   [data-mode="product"] — LMS (business.hakku.ai, client forks like
   cbr.hakku.ai), OS, Cowork and any application UI. Denser type,
   real control states, tables, forms, navigation.

   Same paper/ink DNA: hierarchy by size and weight, hairline borders
   instead of shadows, pills for actions. No new hues.
   ============================================================ */

[data-mode="product"] {
  /* ---- Compressed type scale (app density) ----------------- */
  --t-hero:    2.5rem;      /* @kind font */ /* 40 — page hero (rare) */
  --t-h1:      2rem;        /* @kind font */ /* 32 — page title */
  --t-h2:      1.5rem;      /* 24 — section title */
  --t-h3:      1.25rem;     /* 20 — card title */
  --t-h4:      1.0625rem;   /* 17 — small card / row title */
  --t-h5:      1rem;        /* 16 */
  --t-body-lg: 1.0625rem;   /* 17 — lede */
  --t-body:    0.9375rem;   /* 15 — app body */
  --t-small:   0.8125rem;   /* 13 — UI secondary */
  --t-caption: 0.6875rem;   /* 11 — meta / eyebrow */

  /* ---- Controls -------------------------------------------- */
  --ctl-h:      40px;       /* default control height (buttons, inputs) */
  --ctl-h-sm:   32px;
  --ctl-h-lg:   48px;
  --ctl-pad-x:  var(--s-4);
  --ctl-radius: var(--r-sm);     /* inputs/selects: 8px */
  --btn-radius: var(--r-pill);   /* buttons stay pills */
  --focus-ring: 0 0 0 3px rgba(42,62,244,.25);  /* accessible focus */

  /* ---- Tables / lists --------------------------------------- */
  --row-h:      48px;
  --row-h-sm:   36px;
  --table-head-fg: var(--fg-faint);   /* uppercase caption head */

  /* ---- App layout ------------------------------------------- */
  --sidebar-w:   264px;
  --topbar-h:    64px;
  --content-max: 1080px;    /* reading/content column inside app */
  --pane-pad:    var(--s-6);

  /* App ground: soft ash canvas, paper cards (light theme) */
  --surface:     var(--surface-soft);

  /* Tighter body rhythm */
  line-height: 1.5;
}

/* Dark-theme product: keep true black canvas, cards raise */
[data-theme="dark"] [data-mode="product"],
[data-mode="product"][data-theme="dark"] {
  --surface: var(--ink);
}

/* ── tokens/mobile.css ───────────────────────────────── */
/* ============================================================
   Hakku 2.0 — Mobile / adaptive tokens (layer 5 of 5: mobile)
   ----------------------------------------------------------------
   Одна шкала брейкпоинтов на всю экосистему:
     480 (--bp-sm) · 768 (--bp-md) · 1024 (--bp-lg) · 1280 (--bp-xl)
   Своих брейкпоинтов страницы не заводят: пять в LMS (1240/1024/900/
   820/560) и семь в лендингах (860…460) сводятся сюда.

   Спека и приёмка — карточки группы «Mobile» в preview/:
   mobile-foundations · controls · navigation · table · overlays · appshell.

   ВАЖНО: custom properties в @media не работают, поэтому значения
   брейкпоинтов ниже продублированы литералами. Токены --bp-* нужны
   JS-потребителям (matchMedia) и документации — при изменении шкалы
   правится и то, и другое.

   Правило слоя: десктоп ≥1280 не меняется ни на пиксель. Всё, что
   здесь есть, живёт внутри @media или в новых токенах.
   ============================================================ */

:root {
  /* ---- Шкала брейкпоинтов (для JS и документации) ---------- */
  --bp-sm: 480px;    /* телефон */
  --bp-md: 768px;    /* планшет-портрет / крупный телефон */
  --bp-lg: 1024px;   /* сайдбар возвращается */
  --bp-xl: 1280px;   /* десктоп */

  /* ---- Safe area (0 на десктопе и без поддержки) ----------- */
  --safe-t: env(safe-area-inset-top, 0px);
  --safe-r: env(safe-area-inset-right, 0px);
  --safe-b: env(safe-area-inset-bottom, 0px);
  --safe-l: env(safe-area-inset-left, 0px);

  /* ---- Мобильная оболочка продукта (V3: табы + drawer) ----- */
  --tabbar-h:           56px;   /* нижний таб-бар, + safe-area снизу */
  --drawer-w:           322px;  /* off-canvas дерево: 390 − 68 */
  --composer-h:         64px;   /* панель ввода (сессия Cowork) */
  --topbar-h-collapsed: 48px;   /* топбар в прокрученном состоянии */

  /* ---- Голографика: на десктопе ограничений нет ------------ */
  --holo-max:     none;   /* max-width рендера */
  --holo-opacity: 1;
}

/* ============================================================
   Тач-размеры
   ----------------------------------------------------------------
   Не только по ширине: телефон в ландшафте — 844 CSS-px, шире
   --bp-md. Поэтому второе условие — грубый указатель на нешироком
   экране. Десктопный мыш-указатель (pointer: fine) не задет.
   ============================================================ */
@media (max-width: 480px), (pointer: coarse) and (max-width: 1023px) {
  [data-mode="product"] {
    --ctl-h:      44px;   /* 40 → 44 — норма iOS HIG */
    --ctl-h-sm:   36px;   /* 32 → 36 — минимум для вторичных целей */
    --ctl-h-lg:   52px;   /* 48 → 52 — формы входа, крупные CTA */
    --row-h:      56px;   /* 48 → 56 — строка списка это основная цель */
    --row-h-sm:   44px;   /* 36 → 44 — шапка таблицы */
  }
}

/* ============================================================
   ≤ 768 — планшет-портрет и крупный телефон
   ============================================================ */
@media (max-width: 768px) {
  :root { --gutter: 24px; }            /* было в components/base.css */

  [data-mode="comm"] {
    --topbar-h:     56px;              /* nav лендинга: 72 → 56 */
    --hero-graphic: 340px;             /* 520 не влезает в колонку */
  }
}

/* ============================================================
   ≤ 480 — телефон (базовая ширина проектирования 390)
   ============================================================ */
@media (max-width: 480px) {
  :root {
    --gutter:       20px;
    --holo-max:     150px;   /* вместо хардкода !important в LMS */
    --holo-opacity: .5;
  }

  /* ---- Продукт: шкала на шаг ниже + плотность оболочки ----- */
  [data-mode="product"] {
    --t-hero:     1.875rem;   /* @kind font */ /* 40 → 30 */
    --t-h1:       1.5rem;     /* @kind font */ /* 32 → 24 */
    --t-h2:       1.25rem;    /* 24 → 20 */
    --t-h3:       1.0625rem;  /* 20 → 17 */
    /* --t-body 15 не трогаем: читается. Поля ввода — отдельное
       правило ≥16px в components/mobile.css (иначе iOS зумит). */
    --topbar-h:   56px;       /* 64 → 56, при скролле 48 */
    --pane-pad:   var(--s-4); /* 24 → 16 */
    --content-max: 100%;      /* колонка чтения не ограничивается */
  }

  /* ---- Комм: редакционная шкала тоже вниз ------------------ */
  [data-mode="comm"] {
    --t-hero:       1.875rem;   /* @kind font */ /* 30 — clamp-минимум 48 не влезает */
    --t-h1:         1.625rem;   /* @kind font */ /* 26 */
    --t-h2:         1.375rem;   /* 22 */
    --t-h3:         1.25rem;    /* 20 */
    --t-body-lg:    1rem;       /* 19 → 16 */
    --hero-graphic: 280px;
  }
}

/* ── components/base.css ───────────────────────────────── */
/* ============================================================
   Hakku 2.0 — Base element styles
   Canon base layer (inherited from бИИзнес colors_and_type.css),
   made theme-aware via semantic tokens. Requires tokens/tokens.css.
   ============================================================ */
html, body {
  margin: 0;
  background: var(--surface);
  color: var(--fg);
  font-family: var(--font-body);
  font-size: var(--t-body);
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}
* { box-sizing: border-box; }
a { color: inherit; text-decoration: none; }

/* Headings — display face, tight, Regular weight (brand rule) */
h1, .h1, h2, .h2, h3, .h3, h4, .h4, h5, .h5 {
  font-family: var(--font-display);
  font-weight: 400;
  line-height: 1.05;
  letter-spacing: -0.02em;
  margin: 0;
  color: var(--fg);
}
.hero { font-size: var(--t-hero); line-height: 0.98; }
h1, .h1 { font-size: var(--t-h1); }
h2, .h2 { font-size: var(--t-h2); }
h3, .h3 { font-size: var(--t-h3); letter-spacing: -0.01em; }
h4, .h4 { font-size: var(--t-h4); letter-spacing: -0.01em; }
h5, .h5 { font-size: var(--t-h5); letter-spacing: -0.01em; }

p, .body { font-size: var(--t-body); line-height: 1.6; color: var(--fg-muted); }

/* Выделение внутри текста. Абзац набирается приглушённым --fg-muted,
   поэтому выделенное слово возвращает полный контраст и вес: без правила
   браузер даёт свой жирный поверх приглушённого цвета, и выделение
   читается тусклее окружающего текста, а не ярче. */
strong, b { color: var(--fg); font-weight: 600; }
.lede    { font-size: var(--t-body-lg); line-height: 1.55; color: var(--fg-muted); }
.small   { font-size: var(--t-small); }
.caption { font-size: var(--t-caption); color: var(--fg-faint); letter-spacing: 0.02em; }
.mono    { font-family: var(--font-mono); }

/* Eyebrow — the recurring section label. UPPERCASE, tracked, faint. */
.eyebrow {
  font-family: var(--font-body);
  font-size: var(--t-caption);
  font-weight: 500;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--fg-faint);
}

/* Numerals / stat figures — display face, very tight */
.numeral {
  font-family: var(--font-display);
  font-weight: 400;
  letter-spacing: -0.02em;
  line-height: 1;
  font-feature-settings: "tnum" 1;
}

/* Gradient text — reserved for a single "wow" figure per view */
.grad-text {
  background: var(--grad-cool);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}

/* Layout helper */
.container { max-width: var(--container); margin: 0 auto; padding: 0 var(--gutter); }
/* Мобильные шаги --gutter (24 на ≤768, 20 на ≤480) живут в
   tokens/mobile.css — гуттер это токен, а не свойство компонента. */

/* ── components/components.css ───────────────────────────────── */
/* ============================================================
   Hakku 2.0 — Components
   Canon component classes (buttons, cards, badges, lattice, nav)
   + product-mode controls (inputs, tables, sidebar). Theme-aware:
   everything consumes semantic tokens only.
   Requires tokens/tokens.css + base.css.
   ============================================================ */

/* ============ BUTTONS — always pills ======================== */
.btn {
  display: inline-flex; align-items: center; justify-content: center;
  gap: var(--s-2);
  height: var(--ctl-h, 48px);
  padding: 0 var(--s-5);
  border-radius: var(--r-pill);
  border: 1px solid transparent;
  font-family: var(--font-body);
  font-size: var(--t-small); font-weight: 500;
  cursor: pointer; white-space: nowrap;
  transition: filter var(--dur-fast) var(--ease-brand),
              background var(--dur-fast) var(--ease-brand),
              border-color var(--dur-fast) var(--ease-brand);
  -webkit-user-select: none; user-select: none;
}
.btn:focus-visible { outline: none; box-shadow: var(--focus-ring, 0 0 0 3px rgba(42,62,244,.25)); }

/* Primary — solid ink on paper (flips to paper-on-ink in dark/ink) */
.btn-primary { background: var(--fg); color: var(--surface); }

/* Ghost — transparent + hairline */
.btn-ghost { background: transparent; color: var(--fg); border-color: var(--border); }

/* Accent — the rare loud CTA (one per view) */
.btn-accent { background: var(--accent); color: var(--accent-on); }

/* Ховер — только там, где есть указатель: на тач-устройстве
   состояние залипает после тапа, и пользователь видит «активной»
   кнопку, с которой уже ушёл. Правило слоя, не исключение. */
@media (hover: hover) {
  .btn-primary:hover { filter: brightness(1.35); }
  [data-theme="dark"] .btn-primary:hover,
  [data-canvas="ink"] .btn-primary:hover { filter: brightness(0.85); }
  .btn-ghost:hover { border-color: var(--border-strong); }
  .btn-accent:hover { filter: brightness(1.1); }
}

.btn-sm { height: var(--ctl-h-sm, 36px); padding: 0 var(--s-4); font-size: var(--t-caption); }
.btn-lg { height: var(--ctl-h-lg, 56px); padding: 0 var(--s-6); font-size: var(--t-body); }
.btn[disabled], .btn.is-disabled { opacity: .4; pointer-events: none; }

/* The brand's only recurring "icon" is the literal arrow glyph → */
.btn .arrow { font-family: var(--font-body); }

/* Links gain a hairline underline on hover */
.link { color: var(--fg); border-bottom: 1px solid transparent; transition: border-color var(--dur-fast) var(--ease-brand); }
@media (hover: hover) { .link:hover { border-color: var(--fg-faint); } }
.link-accent { color: var(--accent); }

/* ============ BADGES / CHIPS ================================ */
.badge {
  display: inline-flex; align-items: center; gap: var(--s-2);
  padding: 6px 14px;
  border-radius: var(--r-pill);
  border: 1px solid var(--border);
  font-size: var(--t-caption); font-weight: 500;
  color: var(--fg-muted);
  background: transparent;
}
.badge-fill   { background: var(--ink-04); border-color: transparent; }
.badge-ink    { background: var(--fg); color: var(--surface); border-color: transparent; }
.badge-accent { background: var(--accent); color: var(--accent-on); border-color: transparent; }
/* Status dot — the single 6px circle marker */
.badge .dot { width: 6px; height: 6px; border-radius: 50%; background: var(--accent); }
.badge .dot-orange { background: var(--orange); }
.badge .dot-magenta { background: var(--magenta); }

/* ============ CARDS ========================================= */
/* Hairline borders do the work, not shadows. */
.card {
  background: var(--surface-card);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  padding: var(--s-6);
}
.card-md { border-radius: var(--r-md); padding: var(--s-5); }
/* Selected / "popular" card inverts */
.card-ink {
  background: var(--surface-ink);
  color: var(--surface);
  border: 1px solid var(--border-strong);
}
.card-ink h1, .card-ink h2, .card-ink h3, .card-ink h4, .card-ink h5,
.card-ink p { color: inherit; }
/* Soft card — tonal fill, no border (product lists) */
.card-soft { background: var(--surface-soft); border-color: transparent; }

/* ---- Кликабельная карточка -------------------------------------
   Карточка-ссылка показывает кликабельность ховером, а не постоянным
   украшением: пять стрелок в ряд спорят с содержимым карточек. На
   тач-раскрывать нечем — там стрелка видна всегда (как в мобильном
   меню, components/mobile.css). Нижний паддинг увеличен на зону
   стрелки (отступ + диаметр + зазор до текста), иначе многострочный
   абзац упирается в кружок. */
.card-click {
  position: relative; display: block; text-decoration: none; color: inherit;
  padding-bottom: calc(var(--s-4) + 32px + var(--s-3));
  transition: transform var(--dur-fast, 150ms) var(--ease-brand, ease), border-color var(--dur-fast, 150ms) var(--ease-brand, ease);
}
.card-click-arrow {
  position: absolute; right: var(--s-4); bottom: var(--s-4);
  width: 32px; height: 32px; border-radius: 50%;
  display: grid; place-items: center;
  background: var(--accent); color: var(--paper);
  opacity: 0; transform: translateY(4px);
  transition: opacity var(--dur-fast, 150ms) var(--ease-brand, ease), transform var(--dur-fast, 150ms) var(--ease-brand, ease);
}
/* Специфичность (0,0,2,1) — обязана перебить .icon, svg.lucide
   (0,0,1,1) в любом порядке правил, а не только при текущем. */
.card-click .card-click-arrow svg { color: var(--paper); }
@media (hover: hover) {
  .card-click:hover { transform: translateY(-2px); border-color: var(--border-strong); }
  .card-click:hover .card-click-arrow { opacity: 1; transform: translateY(0); }
}
.card-click:focus-visible { outline: none; box-shadow: var(--focus-ring, 0 0 0 3px rgba(42,62,244,.25)); }
.card-click:focus-visible .card-click-arrow { opacity: 1; transform: translateY(0); }
/* Тач без hover: раскрывать нечем — стрелка стоит постоянно, без
   подъёма карточки и без анимации появления. */
@media (hover: none) {
  .card-click-arrow { opacity: 1; transform: none; transition: none; }
}

/* ---- Светлый остров на чернильном полотне ----------------------
   Просто покрасить фон нельзя: base.css красит p в --fg-muted,
   который на ink-канве равен paper-72 — текст исчезнет на белом.
   Карточка переопределяет семантические токены у себя, а не цвета
   у каждого потомка. */
.card-paper {
  background: var(--paper);
  --fg: var(--ink);
  --fg-muted: var(--ink-72);
  --fg-faint: var(--ink-55);
  --border: var(--ink-12);
  color: var(--fg);
}

/* ---- Веса ступеней ---------------------------------------------
   Последовательность различается весом карточки, а не цветом:
   перебор палитры внутри одного ряда читается как радуга. Перебивают
   фон .card только порядком в файле (специфичность та же) — блок
   должен оставаться после .card. */
.card-weight-flat { background: transparent; }
.card-weight-mid  { background: var(--surface-raised); }

/* ============ LATTICE — 1px hairline grid =================== */
/* Signature editorial table look: cells separated by 1px gap over
   a hairline ground. */
.lattice {
  display: grid;
  gap: 1px;
  background: var(--border);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  overflow: hidden;
}
.lattice > * { background: var(--surface-card); padding: var(--s-6); }
.lattice-3 { grid-template-columns: repeat(3, 1fr); }
.lattice-2 { grid-template-columns: repeat(2, 1fr); }
.lattice-4 { grid-template-columns: repeat(4, 1fr); }
@media (max-width: 768px) { .lattice-2, .lattice-3, .lattice-4 { grid-template-columns: 1fr; } }

/* ============ NAV (sticky, blurred) ========================= */
.nav {
  position: sticky; top: 0; z-index: 50;
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--s-5);
  height: var(--topbar-h, 72px);
  padding: 0 var(--s-6);
  background: color-mix(in srgb, var(--surface) 92%, transparent);
  -webkit-backdrop-filter: blur(20px); backdrop-filter: blur(20px);
  border-bottom: 1px solid var(--ink-08);
}
[data-theme="dark"] .nav, [data-canvas="ink"] .nav { border-bottom-color: var(--paper-10); }

/* ---- Glass-вариант шапки ---------------------------------------
   Материал: размытие подложки плюс насыщение — без saturate() эффект
   читается как мутное пятно, а не стекло. Три ребёнка раскладываются
   гридом, а не space-between: при space-between центр зависит от
   ширины боковых блоков и не совпадает с центром страницы.
   border-bottom здесь и в [data-theme="dark"] .nav (выше) имеют
   одинаковую специфичность (0,2,0) — побеждает более позднее по
   файлу правило, поэтому блок должен оставаться после .nav. */
.nav.nav-glass {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  -webkit-backdrop-filter: blur(16px) saturate(180%);
  backdrop-filter: blur(16px) saturate(180%);
  background: color-mix(in srgb, var(--surface) 72%, transparent);
  border-bottom: 1px solid var(--border);
}
.nav.nav-glass > *:first-child  { justify-self: start; }
.nav.nav-glass > *:nth-child(2) { justify-self: center; }
/* Правый блок закреплён в третьей колонке явно. На ≤1279 mobile.css
   прячет .nav-links (второго ребёнка), display:none выкидывает его
   из грида — без явной колонки правый блок автоматически встаёт
   в среднюю (auto) колонку и налезает на логотип. На десктопе
   с тремя детьми он и так в третьей колонке — раскладка не меняется. */
.nav.nav-glass > *:last-child   { justify-self: end; grid-column: 3; }

/* ============ SECTION scaffolding (comm) ==================== */
.section { padding: var(--section-pad, 140px) 0; border-top: 1px solid var(--ink-08); }
[data-theme="dark"] .section, [data-canvas="ink"] .section { border-top-color: var(--paper-10); }

/* ============ TOGGLE — pill switcher ======================== */
.toggle {
  display: inline-flex; padding: 4px;
  border: 1px solid var(--border); border-radius: var(--r-pill);
  background: var(--surface-card);
}
.toggle button {
  border: 0; background: transparent; color: var(--fg-muted);
  padding: 8px 18px; border-radius: var(--r-pill);
  font: 500 var(--t-small)/1 var(--font-body); cursor: pointer;
  transition: background var(--dur-fast) var(--ease-brand), color var(--dur-fast) var(--ease-brand);
}
.toggle button.is-active { background: var(--fg); color: var(--surface); }

/* ============ ICONS (Lucide subset, product UI) ============= */
/* Brand rule: 1.5px stroke, ink-coloured, sparse. Icons never
   compete with holographic graphics. */
.icon, svg.lucide { width: 20px; height: 20px; stroke-width: 1.5; color: var(--fg-muted); flex: none; }
.icon-sm { width: 16px; height: 16px; }
.icon-lg { width: 24px; height: 24px; }
/* Icons as <img> can't inherit currentColor (SVG stroke stays black) —
   dark theme / ink canvas auto-inverts them. Prefer inline SVG when the
   icon must be tinted precisely. */
[data-theme="dark"] img.icon, [data-canvas="ink"] img.icon { filter: invert(1); opacity: .72; }
/* …except on inverted fills (active nav pill is paper in dark) */
[data-theme="dark"] .nav-item.is-active img.icon,
[data-canvas="ink"] .nav-item.is-active img.icon { filter: none; opacity: 1; }

/* ============ PRODUCT MODE — forms ========================== */
.input, .select, .textarea {
  width: 100%;
  height: var(--ctl-h, 40px);
  padding: 0 var(--ctl-pad-x, 14px);
  border: 1px solid var(--border);
  border-radius: var(--ctl-radius, 8px);
  background: var(--surface-card);
  color: var(--fg);
  font: 400 var(--t-body)/1.4 var(--font-body);
  transition: border-color var(--dur-fast) var(--ease-brand), box-shadow var(--dur-fast) var(--ease-brand);
}
.textarea { height: auto; min-height: 96px; padding: var(--s-3) var(--ctl-pad-x, 14px); resize: vertical; }
.input::placeholder, .textarea::placeholder { color: var(--fg-faint); }
@media (hover: hover) { .input:hover, .select:hover, .textarea:hover { border-color: var(--hairline); } }
.input:focus, .select:focus, .textarea:focus {
  outline: none; border-color: var(--accent);
  box-shadow: var(--focus-ring, 0 0 0 3px rgba(42,62,244,.25));
}
.input.is-invalid { border-color: var(--magenta); }
.field { display: grid; gap: var(--s-2); }
.field > label { font: 500 var(--t-small)/1.2 var(--font-body); color: var(--fg); }
.field > .hint { font-size: var(--t-caption); color: var(--fg-faint); }
.field > .error { font-size: var(--t-caption); color: var(--magenta); }

/* Checkbox / radio — ink, square/circle, no accent fill until checked */
.checkbox, .radio { accent-color: var(--fg); width: 16px; height: 16px; }

/* Switch */
.switch { position: relative; width: 40px; height: 24px; border-radius: var(--r-pill);
  background: var(--ink-12); transition: background var(--dur-fast) var(--ease-brand);
  border: 0; cursor: pointer; }
.switch::after { content: ""; position: absolute; top: 3px; left: 3px; width: 18px; height: 18px;
  border-radius: 50%; background: var(--paper); box-shadow: var(--shadow-sm);
  transition: transform var(--dur-fast) var(--ease-brand); }
.switch.is-on { background: var(--fg); }
.switch.is-on::after { transform: translateX(16px); }

/* ============ PRODUCT MODE — tables ========================= */
.table { width: 100%; border-collapse: collapse; font-size: var(--t-body); }
.table th {
  text-align: left; height: var(--row-h-sm, 36px); padding: 0 var(--s-4);
  font: 500 var(--t-caption)/1 var(--font-body);
  letter-spacing: .08em; text-transform: uppercase;
  color: var(--table-head-fg, var(--fg-faint));
  border-bottom: 1px solid var(--border);
}
.table td { height: var(--row-h, 48px); padding: 0 var(--s-4);
  border-bottom: 1px solid var(--ink-08); color: var(--fg-muted); }
[data-theme="dark"] .table td { border-bottom-color: var(--paper-10); }
@media (hover: hover) {
  .table tr:hover td { background: var(--ink-04); }
  [data-theme="dark"] .table tr:hover td { background: var(--paper-04); }
}
.table td:first-child { color: var(--fg); font-weight: 500; }
.table-striped tbody tr:nth-child(even) td { background: var(--row-even); }

/* ============ PRODUCT MODE — app shell ====================== */
/* Мобильная раскладка (табы + drawer на ≤767) — mobile.css */
.app-shell { display: grid; grid-template-columns: var(--sidebar-w, 264px) 1fr; min-height: 100vh; min-height: 100dvh; }
.sidebar {
  background: var(--surface-card);
  border-right: 1px solid var(--border);
  padding: var(--s-5);
  display: flex; flex-direction: column; gap: var(--s-1);
}
.sidebar .nav-item {
  display: flex; align-items: center; gap: var(--s-3);
  padding: 10px 14px; border-radius: var(--r-sm);
  font: 500 var(--t-small)/1.2 var(--font-body); color: var(--fg-muted);
  cursor: pointer; transition: background var(--dur-fast) var(--ease-brand), color var(--dur-fast) var(--ease-brand);
}
@media (hover: hover) {
  .sidebar .nav-item:hover { background: var(--ink-04); color: var(--fg); }
  [data-theme="dark"] .sidebar .nav-item:hover { background: var(--paper-04); }
}
.sidebar .nav-item.is-active { background: var(--fg); color: var(--surface); }
.sidebar .nav-item.is-active .icon { color: inherit; }

/* Progress (courses) — hairline track, ink fill, gradient for 100% */
.progress { height: 4px; border-radius: var(--r-pill); background: var(--ink-08); overflow: hidden; }
[data-theme="dark"] .progress { background: var(--paper-10); }
.progress > i { display: block; height: 100%; background: var(--fg); border-radius: var(--r-pill); }
.progress.is-done > i { background: var(--grad-cool); }

/* Tabs — underline style (product) */
.tabs { display: flex; gap: var(--s-5); border-bottom: 1px solid var(--border); }
.tabs button {
  border: 0; background: none; cursor: pointer;
  padding: var(--s-3) 0; margin-bottom: -1px;
  font: 500 var(--t-small)/1.2 var(--font-body); color: var(--fg-faint);
  border-bottom: 2px solid transparent;
  transition: color var(--dur-fast) var(--ease-brand), border-color var(--dur-fast) var(--ease-brand);
}
@media (hover: hover) { .tabs button:hover { color: var(--fg); } }
.tabs button.is-active { color: var(--fg); border-bottom-color: var(--fg); }

/* ============ MOTION helpers ================================ */
.fade-up { opacity: 0; transform: translateY(16px);
  animation: hk-fade-up var(--dur) var(--ease-brand) forwards; }
@keyframes hk-fade-up { to { opacity: 1; transform: none; } }
/* Ambient rotation for holographic renders (90–110s, linear) */
.holo-spin { animation: hk-spin 100s linear infinite; }
@keyframes hk-spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) {
  .fade-up { animation: none; opacity: 1; transform: none; }
  .holo-spin { animation: none; }
  /* Появление стрелки несёт смысл (кликабельно) — остаётся;
     сдвиг карточки и сдвиг стрелки — украшение, зануляются. */
  .card-click:hover { transform: none; }
  .card-click-arrow { transform: none; }
}

/* ── components/mobile.css ───────────────────────────────── */
/* ============================================================
   Hakku 2.0 — Mobile components
   ----------------------------------------------------------------
   Мобильный слой компонентов: оболочка продукта V3 (нижние табы +
   drawer), бургер-навигация комм-режима, таблицы, оверлеи.
   Требует tokens/tokens.css (включая tokens/mobile.css) + base.css
   + components.css. Подключается последним — часть правил намеренно
   перекрывает десктопные внутри @media.

   Спека и приёмка: preview/mobile-*.html (группа «Mobile»),
   живой прогон — preview/mobile-live-product.html и
   preview/mobile-live-comm.html на 390 и 320.

   Инвариант: десктоп ≥1280 не меняется. Всё либо внутри @media,
   либо новые классы, которых не было.

   Состояния переключает потребитель (JS), классы фиксированы:
     .app-shell.is-drawer-open   — drawer открыт
     .nav-menu.is-open           — меню лендинга открыто
     .nav.is-scrolled            — топбар свёрнут при скролле
     .sheet.is-open / .modal.is-open / .scrim.is-open
     body.is-locked              — замок прокрутки под оверлеем
     .burger[aria-expanded]      — иконка бургера ↔ крестик
     [data-surface="input"]      — экран объявляет себя вводовым
   ============================================================ */

/* ============================================================
   1. Платформенные гарантии
   ============================================================ */

/* Высота — dvh: адресная строка съедает низ, и прижатая панель
   становится недосягаемой. vh остаётся фоллбэком. */
.app-shell { min-height: 100vh; min-height: 100dvh; }

/* Замок прокрутки: страница под оверлеем/меню/drawer не едет. */
body.is-locked { overflow: hidden; overscroll-behavior: none; }

@media (max-width: 768px) {
  /* Ничто не выезжает за край: медиа по ширине контейнера, длинные
     русские слова в display-заголовках переносятся, а не режутся. */
  img, video, canvas, svg:not(.icon):not(.lucide) { max-width: 100%; }
  .hero, h1, .h1, h2, .h2, h3, .h3 { overflow-wrap: break-word; }

  /* Причина №1 горизонтального скролла: грид- и флекс-элемент по
     умолчанию не сжимается ниже своего min-content и раздувает
     колонку — таблица, длинное слово или ряд кнопок утаскивает всю
     страницу вбок. Явные min-width компонентов это не сбрасывает
     (специфичность класса выше). */
  *, *::before, *::after { min-width: 0; }

  /* Поля ввода ≥16px — иначе iOS Safari зумит страницу на фокусе
     и уводит вёрстку вбок. Это поведение браузера, а не читаемость. */
  .input, .select, .textarea { font-size: max(16px, var(--t-body)); }
}

/* ============================================================
   2. Иконка-кнопка: зона 44 без сдвига оптического ритма
   ============================================================ */
.btn-icon {
  display: inline-grid; place-items: center; flex: none;
  width: var(--ctl-h, 40px); height: var(--ctl-h, 40px);
  padding: 0; border: 1px solid transparent; border-radius: var(--r-sm);
  background: transparent; color: var(--fg-muted);
  cursor: pointer;
  transition: background var(--dur-fast) var(--ease-brand), color var(--dur-fast) var(--ease-brand);
}
.btn-icon:focus-visible { outline: none; box-shadow: var(--focus-ring, 0 0 0 3px rgba(42,62,244,.25)); }
.btn-icon .icon { color: inherit; }
.btn-icon-round { border-radius: 50%; }
.btn-icon-ink   { background: var(--fg); color: var(--surface); }
/* Иконка-картинка не наследует currentColor: на залитой кнопке её
   надо инвертировать — тем же приёмом, что активный пункт сайдбара. */
.btn-icon-ink img.icon { filter: invert(1); }
[data-theme="dark"] .btn-icon-ink img.icon,
[data-canvas="ink"] .btn-icon-ink img.icon { filter: none; opacity: 1; }
/* Кнопка у самого края панели: зона 44 съедает паддинг панели, а
   рисунок остаётся на оптической линии. Ровно --s-2, не больше:
   -10px при паддинге 8 даёт 2px горизонтального скролла. */
.btn-icon-flush:first-child { margin-left: calc(-1 * var(--s-2)); }
.btn-icon-flush:last-child  { margin-right: calc(-1 * var(--s-2)); }

@media (hover: hover) {
  .btn-icon:hover { background: var(--ink-04); color: var(--fg); }
  [data-theme="dark"] .btn-icon:hover, [data-canvas="ink"] .btn-icon:hover { background: var(--paper-04); }
  .btn-icon-ink:hover { background: var(--fg); filter: brightness(1.35); }
}

/* ============================================================
   3. Кнопки на телефоне: первичное действие во всю ширину
   ============================================================ */
.btn-block { width: 100%; }
.btn-row { display: flex; gap: var(--s-2); }
.btn-row > .btn { flex: 1 1 0; min-width: 0; }

@media (max-width: 480px) {
  /* Панель действий: первичная кнопка всегда полноширинная */
  .btn-stack { display: grid; gap: var(--s-2); }
  .btn-stack > .btn { width: 100%; }
}

/* Тач-цели у контролов, которые на десктопе меньше 44 */
@media (max-width: 480px), (pointer: coarse) and (max-width: 1023px) {
  .toggle button { min-height: var(--ctl-h-sm, 36px); }
  .tabs button { min-height: var(--ctl-h, 44px); }
  .checkbox, .radio { width: 20px; height: 20px; }
  /* Переключатель рисуется 40×24 — зона добирается невидимым полем */
  .switch::before { content: ""; position: absolute; inset: -10px; }
  .sidebar .nav-item { min-height: var(--ctl-h, 44px); font-size: var(--t-body); }
}

/* ============================================================
   4. Оболочка продукта — V3: 4 нижних таба + drawer
   ----------------------------------------------------------------
   ≤767   табы + drawer, композер вытесняет табы на экранах ввода
   768…1023  табы и бургер уходят, сайдбар 264 закреплён
   ≥1024  десктопная раскладка без изменений

   Комм-режим держит свой порог бургера отдельно (до 1279) — см. §5,
   этот брейкпоинт не трогать.

   Разметка:
     <div class="app-shell has-tabbar">
       <aside class="sidebar">…полное дерево…</aside>
       <div class="scrim" data-drawer-close></div>
       <main>
         <div class="nav"><button class="burger btn-icon" aria-expanded="false">…</button>…</div>
         …
       </main>
       <nav class="tabbar"><a class="tab is-active">…</a>…×4</nav>
     </div>
   ============================================================ */

/* Бургер и таб-бар не существуют на десктопе */
.burger { display: none; }
.tabbar { display: none; }

/* Подпись группы разделов в сайдбаре (в drawer подписывает «в табах») */
.sidebar .nav-group {
  font: 500 var(--t-caption)/1 var(--font-body);
  letter-spacing: .12em; text-transform: uppercase; color: var(--fg-faint);
  padding: var(--s-3) var(--s-3) var(--s-1);
}
/* Кнопка закрытия существует только у drawer */
@media (min-width: 768px) { .sidebar [data-drawer-close] { display: none; } }

/* Подложка — общая для drawer, меню и оверлеев */
.scrim {
  position: fixed; inset: 0; z-index: 65;
  background: rgba(0,0,0,.5);
  opacity: 0; visibility: hidden;
  transition: opacity var(--dur-fast) var(--ease-brand), visibility 0s linear var(--dur-fast);
}
.scrim.is-open { opacity: 1; visibility: visible; transition-delay: 0s; }

@media (max-width: 767px) {
  .app-shell { grid-template-columns: 1fr; }

  /* ---- Сайдбар становится off-canvas drawer ---------------- */
  .app-shell > .sidebar {
    position: fixed; top: 0; bottom: 0; left: 0; z-index: 70;
    width: min(var(--drawer-w), calc(100% - 56px));
    padding: calc(var(--s-4) + var(--safe-t)) var(--s-3) calc(var(--s-4) + var(--safe-b));
    overflow-y: auto; overscroll-behavior: contain;
    border-right: 1px solid var(--border);
    transform: translateX(-101%); visibility: hidden;
    transition: transform var(--dur-fast) var(--ease-brand), visibility 0s linear var(--dur-fast);
  }
  .app-shell.is-drawer-open > .sidebar { transform: none; visibility: visible; transition-delay: 0s; }
  .app-shell.is-drawer-open > .scrim  { opacity: 1; visibility: visible; transition-delay: 0s; }

  /* Разделы, уже вынесенные в табы, приглушены под подписью
     «в табах» — иначе два входа выглядят одинаково значимыми.
     Текущее место не приглушаем: иначе непонятно, где стоишь. */
  .sidebar .nav-item.is-in-tabs:not(.is-active) { opacity: .5; }

  .burger { display: inline-grid; }

  /* ---- Топбар: 56, при скролле 48 с тенью ------------------ */
  .app-shell .nav { padding: 0 var(--s-2) 0 var(--s-4); gap: var(--s-2); }
  .nav.is-scrolled { height: var(--topbar-h-collapsed); box-shadow: 0 1px 12px rgba(0,0,0,.08); }
  [data-theme="dark"] .nav.is-scrolled { box-shadow: 0 1px 12px rgba(0,0,0,.6); }
  .nav-title { flex: 1 1 auto; min-width: 0;
    font-family: var(--font-display); font-weight: 400; font-size: var(--t-h4);
    letter-spacing: -0.01em; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

  /* ---- Нижний таб-бар ------------------------------------- */
  .tabbar {
    position: fixed; left: 0; right: 0; bottom: 0; z-index: 60;
    display: flex;
    padding-bottom: var(--safe-b);
    background: var(--surface-card);
    border-top: 1px solid var(--border);
  }
  .tabbar .tab {
    flex: 1 1 0; min-width: 0;
    height: var(--tabbar-h);
    display: flex; flex-direction: column; align-items: center; justify-content: center;
    gap: 4px; padding: 0 2px;
    font: 500 var(--t-caption)/1 var(--font-body);
    color: var(--fg-faint); text-align: center;
    cursor: pointer;
  }
  .tabbar .tab .icon { color: inherit; }
  .tabbar .tab.is-active { color: var(--fg); font-weight: 600; }
  .tabbar .tab:focus-visible { outline: none; box-shadow: var(--focus-ring, 0 0 0 3px rgba(42,62,244,.25)); }

  /* Контент не заезжает под таб-бар */
  .app-shell:has(> .tabbar) > main,
  .app-shell.has-tabbar > main { padding-bottom: calc(var(--tabbar-h) + var(--safe-b)); }

  /* ---- Экран ввода объявляет себя сам --------------------- */
  /* Оболочка не угадывает по маршруту: иначе каждый новый экран
     Cowork требует правки правила в оболочке. */
  [data-surface="input"] .tabbar { display: none; }
  [data-surface="input"] .app-shell:has(> .tabbar) > main,
  [data-surface="input"] .app-shell.has-tabbar > main { padding-bottom: 0; }
  [data-surface="input"] .nav { height: var(--topbar-h-collapsed); }
}

/* Композер — панель ввода, прижатая к низу контентной колонки */
.composer {
  position: sticky; bottom: 0; z-index: 40;
  display: flex; align-items: center; gap: var(--s-2);
  min-height: var(--composer-h);
  padding: var(--s-2) var(--s-3) calc(var(--s-2) + var(--safe-b));
  background: var(--surface-card);
  border-top: 1px solid var(--border);
}
.composer > .input, .composer > .textarea { border-radius: var(--r-pill); }
.composer > .textarea { min-height: var(--ctl-h, 44px); padding: 10px var(--s-4); }

/* ============================================================
   5. Навигация комм-режима: бургер + полноэкранное меню
   ============================================================ */
.nav-menu { display: none; }
.burger .icon-x { display: none; }
.burger[aria-expanded="true"] .icon-menu { display: none; }
.burger[aria-expanded="true"] .icon-x    { display: block; }

@media (max-width: 1279px) {
  /* 768…1279: восемь пунктов навигации лендинга не помещаются инлайн
     (переполнение ловилось уже на 1024) — бургер занимает место инлайн-
     меню на всём диапазоне ≤1279 (= --bp-xl - 1, tokens/mobile.css),
     не только на телефоне: инлайн-навигация гарантированно помещается
     только с 1280px. Таб-бар и drawer продукта (§4) — другая ось
     (форм-фактор телефона, диапазон 768…1023), их брейкпоинт не трогаем. */
  [data-mode="comm"] .burger { display: inline-grid; }
  [data-mode="comm"] .nav { padding: 0 var(--s-2) 0 var(--gutter); gap: var(--s-3); }
  /* Пункты меню на 390 не помещаются даже в две строки — их место
     занимает одно целевое действие, всё остальное в меню */
  .nav > .nav-links { display: none; }   /* в .nav-glass правый блок держит
     явная grid-column: 3 (components.css), иначе он съезжает в центр */

  .nav-menu {
    position: fixed; inset: 0; z-index: 45;   /* ниже sticky .nav (50) */
    display: flex; flex-direction: column;
    padding: calc(var(--topbar-h, 56px) + var(--safe-t) + var(--s-2)) var(--gutter) calc(var(--s-3) + var(--safe-b));
    background: var(--surface);
    overflow-y: auto; overscroll-behavior: contain;
    opacity: 0; visibility: hidden; transform: translateY(-8px);
    transition: opacity var(--dur-fast) var(--ease-brand),
                transform var(--dur-fast) var(--ease-brand),
                visibility 0s linear var(--dur-fast);
  }
  .nav-menu.is-open { opacity: 1; visibility: visible; transform: none; transition-delay: 0s; }
  .nav-menu a:not(.btn), .nav-menu .menu-item {
    display: flex; align-items: center; justify-content: space-between; gap: var(--s-4);
    min-height: 56px;
    border-bottom: 1px solid var(--ink-08);
    font-family: var(--font-display); font-weight: 400; font-size: 1.375rem;
    letter-spacing: -0.02em; color: var(--fg);
  }
  [data-theme="dark"] .nav-menu a:not(.btn), [data-theme="dark"] .nav-menu .menu-item { border-bottom-color: var(--paper-10); }
  .nav-menu a:not(.btn) > .arrow, .nav-menu .menu-item > .arrow { font-size: var(--t-body); color: var(--fg-faint); }
  /* CTA прижат к низу над safe-area — до него дотягивается палец */
  .nav-menu .menu-foot { margin-top: auto; padding-top: var(--s-5); }
  .nav-menu .menu-foot > .btn { width: 100%; height: var(--ctl-h-lg, 52px); font-size: var(--t-body); }
}

/* ============================================================
   6. Таблицы: скролл с липкой колонкой ИЛИ развёртка в карточку
   ----------------------------------------------------------------
   Обе раскладки — один и тот же <table>. Переключение классом на
   обёртке/таблице, не двумя кусками HTML: иначе один из них рано
   или поздно отстанет от другого.

   До четырёх колонок — .table-cards. От пяти и когда важно
   сравнение — .table-wrap.table-sticky.
   Для .table-cards каждому <td> нужен data-label="Заголовок колонки".
   ============================================================ */
.table-wrap { overflow-x: auto; overscroll-behavior-x: contain; -webkit-overflow-scrolling: touch; }

@media (max-width: 767px) {
  .table-wrap > .table { min-width: max-content; }
  .table-wrap > .table th, .table-wrap > .table td { white-space: nowrap; }

  /* А · первая колонка липкая, цифры уезжают */
  .table-sticky th:first-child, .table-sticky td:first-child {
    position: sticky; left: 0; z-index: 1;
    background: var(--surface-card);
    box-shadow: 1px 0 0 var(--border);
  }
  .table-sticky.table-striped tbody tr:nth-child(even) td:first-child { background: var(--row-even); }

  /* Б · строка развёрнута в карточку */
  .table-cards { display: block; }
  .table-cards thead { display: none; }
  .table-cards tbody, .table-cards tr, .table-cards td { display: block; }
  .table-cards tr {
    padding: var(--s-3) var(--s-4);
    border-bottom: 1px solid var(--border);
  }
  .table-cards td {
    height: auto; min-height: 28px; padding: 0; border: 0;
    display: flex; align-items: baseline; justify-content: space-between; gap: var(--s-4);
    white-space: normal;
  }
  .table-cards td::before {
    content: attr(data-label);
    flex: none;
    font: 500 var(--t-caption)/1.4 var(--font-body);
    letter-spacing: .04em; color: var(--fg-faint);
  }
  .table-cards td:first-child {
    display: block; margin-bottom: var(--s-2);
    font-family: var(--font-display); font-weight: 400; font-size: var(--t-h4);
    letter-spacing: -0.01em; color: var(--fg);
  }
  .table-cards td:first-child::before { content: none; }
  .table-cards tr:hover td { background: none; }

  /* Подчёркивающие табы не сжимаются, а прокручиваются */
  .tabs { overflow-x: auto; scrollbar-width: none; gap: var(--s-4); }
  .tabs::-webkit-scrollbar { display: none; }
  .tabs button { flex: none; }
}

/* ============================================================
   7. Оверлеи: bottom sheet · полноэкранная модалка · тост
   ----------------------------------------------------------------
   Действия над объектом → sheet. Ввод и длинный контент → модалка.
   Подтверждение результата → тост. Необратимое действие → модалка
   с явным «Удалить»: свайп закрывает sheet случайно.
   ============================================================ */
.sheet {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 70;
  max-height: 90dvh; overflow-y: auto; overscroll-behavior: contain;
  padding: var(--s-2) var(--s-4) calc(var(--s-4) + var(--safe-b));
  background: var(--surface-card);
  border-top: 1px solid var(--border);
  border-radius: var(--r-lg) var(--r-lg) 0 0;
  transform: translateY(101%); visibility: hidden;
  transition: transform var(--dur-fast) var(--ease-brand), visibility 0s linear var(--dur-fast);
}
.sheet.is-open { transform: none; visibility: visible; transition-delay: 0s; }
.sheet-grab { display: block; width: 40px; height: 4px; margin: 0 auto var(--s-3);
  border-radius: 2px; background: var(--ink-12); }
[data-theme="dark"] .sheet-grab { background: var(--paper-16); }
.sheet-head, .modal-head {
  display: flex; align-items: center; justify-content: space-between; gap: var(--s-3);
}
.sheet-title, .modal-title {
  font-family: var(--font-display); font-weight: 400; font-size: var(--t-h4);
  letter-spacing: -0.01em; color: var(--fg);
}
/* Строка действия внутри sheet — вся строка это цель */
.sheet-actions { display: grid; gap: var(--s-2); margin-top: var(--s-3); }
.sheet-actions > * {
  display: flex; align-items: center; gap: var(--s-3);
  min-height: var(--row-h, 56px); padding: 10px var(--s-4);
  border: 1px solid var(--border); border-radius: var(--ctl-radius, 8px);
  font-size: var(--t-body); color: var(--fg); cursor: pointer;
}

.modal {
  position: fixed; inset: 0; z-index: 70;
  display: flex; flex-direction: column;
  background: var(--surface-card);
  opacity: 0; visibility: hidden;
  transition: opacity var(--dur-fast) var(--ease-brand), visibility 0s linear var(--dur-fast);
}
.modal.is-open { opacity: 1; visibility: visible; transition-delay: 0s; }
.modal-head {
  flex: none; min-height: 48px;
  padding: var(--safe-t) var(--s-2) 0 var(--s-4);
  border-bottom: 1px solid var(--border);
}
.modal-body { flex: 1 1 auto; overflow-y: auto; padding: var(--s-4); }
.modal-foot {
  flex: none; display: grid; gap: var(--s-2);
  padding: var(--s-3) var(--s-4) calc(var(--s-3) + var(--safe-b));
  border-top: 1px solid var(--border);
}
/* От планшета модалка становится обычным центрированным диалогом */
@media (min-width: 768px) {
  .modal {
    inset: auto; top: 50%; left: 50%;
    width: min(560px, calc(100% - 2 * var(--s-6)));
    max-height: min(720px, 90dvh);
    transform: translate(-50%, -50%);
    border: 1px solid var(--border); border-radius: var(--r-lg);
    overflow: hidden;
  }
  .modal-head { padding-top: 0; }
}

/* Тост — снизу над safe-area: сверху его перекрывает шторка iOS.
   Живёт 4 секунды, действие отмены внутри. Ошибки формы тостом не
   показываем — только под полем. */
.toast {
  position: fixed; z-index: 80;
  left: var(--s-4); right: var(--s-4);
  bottom: calc(var(--s-3) + var(--safe-b));
  display: flex; align-items: center; gap: var(--s-3);
  padding: var(--s-3) var(--s-4);
  background: var(--surface-ink); color: var(--surface);
  border-radius: var(--r-md);
  font-size: var(--t-small);
  opacity: 0; visibility: hidden; transform: translateY(8px);
  transition: opacity var(--dur-fast) var(--ease-brand),
              transform var(--dur-fast) var(--ease-brand),
              visibility 0s linear var(--dur-fast);
}
.toast.is-open { opacity: 1; visibility: visible; transform: none; transition-delay: 0s; }
.toast .icon { color: inherit; }
.toast-action { margin-left: auto; font-weight: 600; cursor: pointer; }
/* Над таб-баром, когда оболочка мобильная */
@media (max-width: 767px) {
  .toast-above-tabbar { bottom: calc(var(--tabbar-h) + var(--safe-b) + var(--s-2)); }
}
@media (min-width: 768px) {
  .toast { left: auto; right: var(--s-6); width: min(420px, calc(100% - 2 * var(--s-6))); }
}

/* ============================================================
   8. Комм-схлопывание: рельс со snap · голографика
   ============================================================ */
@media (max-width: 767px) {
  /* Логотипы, тарифы, кейсы, тренеры — рельс вместо решётки */
  .lattice-rail {
    display: flex; overflow-x: auto;
    scroll-snap-type: x mandatory; scrollbar-width: none;
    border-radius: var(--r-md);
  }
  .lattice-rail::-webkit-scrollbar { display: none; }
  .lattice-rail > * { flex: 0 0 78%; scroll-snap-align: start; }
  /* Рельс выходит в край экрана, но первый элемент стоит на линии
     гуттера: без scroll-padding snap-точка съедает падинг и рельс
     открывается уже прокрученным. */
  .rail {
    display: flex; gap: var(--s-3); overflow-x: auto;
    scroll-snap-type: x mandatory; scrollbar-width: none;
    margin-inline: calc(-1 * var(--gutter)); padding-inline: var(--gutter);
    scroll-padding-inline: var(--gutter);
  }
  .rail::-webkit-scrollbar { display: none; }
  .rail > * { flex: 0 0 auto; scroll-snap-align: start; }
}

/* Голографический рендер: гасится токенами, не !important по месту */
.holo, .holo-spin { max-width: var(--holo-max); height: auto; opacity: var(--holo-opacity); }

/* ============================================================
   9. Motion
   ============================================================ */
@media (prefers-reduced-motion: reduce) {
  .scrim, .sheet, .modal, .toast, .nav-menu, .app-shell > .sidebar { transition: none; }
}
