/* ── ПЕРВАЯ строка файла: @import обязан стоять до любых правил ──────────
   этап 6 «Живой улей» (§1.8). Один импорт даёт self-hosted Manrope и
   IBM Plex Mono ВСЕМ восьми поверхностям, которые грузят этот файл
   (hive, gate, docs, launch, platilink-launch, onwave-deploy/-spend/-servers).
   До этого --onwave-font-sans год объявлял "Manrope", и ни одна из
   lodge-страниц её не грузила — отсюда и ощущение «разных продуктов».
   Файла может не быть (woff2 не налиты через api/fetch_fonts.py) — тогда
   @import тихо не разрешится, и всё идёт на системном запасе, который
   стоит вторым в --onwave-font-sans/-mono. Ни одна страница не ломается. */
@import url("/fonts/onwave-fonts.css");

/*
 * onwave · дизайн-токены v1 «onwave warm» · WAVE-UI-UNIFY-20260813
 *
 * Использование: <link rel="stylesheet" href="/onwave-tokens.css">
 * Основные токены — HSL для гибкости с shadcn-ui на React-стороне,
 * плюс plain CSS-переменные для статических onwave-страниц.
 */

:root {
  /* ── onwave warm палитра ── */
  --onwave-bg: #fbfaf8;
  --onwave-surface: #f4f0e6;
  --onwave-surface-2: #ece5d3;
  --onwave-border: #d9d3c1;
  --onwave-border-strong: #b8ac8f;
  --onwave-ink: #2f3d1e;
  --onwave-ink-soft: #4a5c2f;
  --onwave-muted: #7c7561;

  --onwave-primary: #2f3d1e;
  --onwave-primary-fg: #fbfaf8;
  --onwave-accent: #6b4d1a;
  --onwave-accent-soft: #a37a3a;
  --onwave-gold: #c5a145;

  --onwave-ok: #4f7a2e;
  --onwave-warn: #a86b21;
  --onwave-fail: #a53a3a;
  --onwave-info: #3b6e8a;

  /* Типографика */
  --onwave-font-sans: "Manrope", ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  --onwave-font-mono: "IBM Plex Mono", ui-monospace, "SFMono-Regular", Menlo, Consolas, monospace;

  /* Радиусы и тени */
  --onwave-radius: 4px;
  --onwave-radius-lg: 8px;
  --onwave-shadow-1: 0 1px 2px rgba(47, 61, 30, 0.06);
  --onwave-shadow-2: 0 4px 12px rgba(47, 61, 30, 0.08);

  /* Ритм */
  --onwave-pad-x: clamp(16px, 3vw, 32px);
  --onwave-max-w: 1280px;

  /* Legacy aliases (мигрирующая совместимость — не использовать в новом коде) */
  --lodge-bg: var(--onwave-bg); --lodge-surface: var(--onwave-surface);
  --lodge-surface-2: var(--onwave-surface-2); --lodge-border: var(--onwave-border);
  --lodge-border-strong: var(--onwave-border-strong); --lodge-ink: var(--onwave-ink);
  --lodge-ink-soft: var(--onwave-ink-soft); --lodge-muted: var(--onwave-muted);
  --lodge-primary: var(--onwave-primary); --lodge-primary-fg: var(--onwave-primary-fg);
  --lodge-accent: var(--onwave-accent); --lodge-accent-soft: var(--onwave-accent-soft);
  --lodge-gold: var(--onwave-gold);
  --lodge-ok: var(--onwave-ok); --lodge-warn: var(--onwave-warn);
  --lodge-fail: var(--onwave-fail); --lodge-info: var(--onwave-info);
  --lodge-font-sans: var(--onwave-font-sans); --lodge-font-mono: var(--onwave-font-mono);
  --lodge-radius: var(--onwave-radius); --lodge-radius-lg: var(--onwave-radius-lg);
  --lodge-shadow-1: var(--onwave-shadow-1); --lodge-shadow-2: var(--onwave-shadow-2);
  --lodge-pad-x: var(--onwave-pad-x); --lodge-max-w: var(--onwave-max-w);
}

/* Ручной тёмный режим (только по toggle, НЕ по OS-теме) */
:root[data-onwave-theme="dark"], :root[data-lodge-theme="dark"] {
  --onwave-bg: #14161a;
  --onwave-surface: #1c1f24;
  --onwave-surface-2: #24282f;
  --onwave-border: #2d323a;
  --onwave-border-strong: #464b56;
  --onwave-ink: #eae6d8;
  --onwave-ink-soft: #c9c3af;
  --onwave-muted: #8a866f;
  --onwave-primary: #a5c47a;
  --onwave-primary-fg: #14161a;
  --onwave-accent: #c5a145;
  --onwave-accent-soft: #d4b56a;
  --onwave-shadow-1: 0 1px 2px rgba(0, 0, 0, 0.4);
  --onwave-shadow-2: 0 4px 14px rgba(0, 0, 0, 0.55);
}

/* ── Общая шапка-навигация onwave (v2 · WAVE-HEADER-UNIFY-20260215) ─── */
.onwave-nav, .lodge-nav {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px var(--onwave-pad-x);
  background: var(--onwave-surface);
  border-bottom: 1px solid var(--onwave-nav-line);
  font-family: var(--onwave-font-sans);
  font-size: var(--fs-sm); line-height: var(--lh-sm);
  color: var(--onwave-muted);
  position: sticky;
  top: 0;
  z-index: 40;
  backdrop-filter: saturate(180%) blur(6px);
  box-shadow: var(--onwave-nav-shadow);
  min-height: 44px;
  box-sizing: border-box;
}
.onwave-nav .onwave-nav-brand, .lodge-nav .lodge-nav-brand {
  color: var(--onwave-ink);
  font-weight: 700;
  letter-spacing: 0.02em;
  margin-right: 6px;
  font-family: var(--onwave-font-sans);
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: var(--fs-md);
}
.onwave-nav .onwave-nav-brand::before, .lodge-nav .lodge-nav-brand::before {
  content: "~";
  color: var(--onwave-accent);
  font-family: var(--onwave-font-mono);
  font-size: var(--fs-lg);   /* знак бренда, не текст — но кегль всё равно со шкалы */
  font-weight: 600;
  transform: translateY(-1px);
}

.onwave-nav .onwave-nav-items {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 2px;
}

/* Unified menu-item styling (tab-button and link both look identical) */
.onwave-nav .onwave-nav-item,
.onwave-nav a,
.lodge-nav a {
  color: var(--onwave-muted);
  text-decoration: none;
  padding: 6px 10px;
  border-radius: var(--r-s);
  transition: color 0.15s, background-color 0.15s, border-color 0.15s;
  font: inherit;
  background: transparent;
  border: 1px solid transparent;
  cursor: pointer;
  line-height: 1.25;
  white-space: nowrap;
}
.onwave-nav .onwave-nav-item:hover,
.onwave-nav a:hover,
.lodge-nav a:hover {
  color: var(--onwave-ink);
  background: var(--onwave-surface-2);
}
.onwave-nav .onwave-nav-item[aria-current="page"],
.onwave-nav a[aria-current="page"],
.lodge-nav a[aria-current="page"] {
  color: var(--onwave-ink);
  background: var(--onwave-bg);
  border-color: var(--onwave-border);
}

.onwave-nav .onwave-nav-spacer, .lodge-nav .lodge-nav-spacer { flex: 1; }

.onwave-nav .onwave-nav-actions {
  display: flex;
  align-items: center;
  gap: 6px;
}

.onwave-nav .onwave-nav-action,
.onwave-nav .onwave-nav-theme,
.lodge-nav .lodge-nav-theme {
  border: 1px solid var(--onwave-border);
  background: transparent;
  color: var(--onwave-muted);
  cursor: pointer;
  padding: 4px 10px;
  border-radius: var(--r-s);
  font: inherit;
  font-size: var(--fs-sm);
  line-height: 1.25;
  transition: color 0.15s, background-color 0.15s, border-color 0.15s;
}
.onwave-nav .onwave-nav-action:hover,
.onwave-nav .onwave-nav-theme:hover,
.lodge-nav .lodge-nav-theme:hover {
  color: var(--onwave-ink);
  background: var(--onwave-surface-2);
  border-color: var(--onwave-border-strong);
}
.onwave-nav .onwave-nav-logout {
  color: var(--onwave-accent);
}

/* Financial beacon dot (Wave HEADER-UNIFY-20260215) — single source of truth
 * for daily spend status across ALL pages. State broadcast via .green /
 * .yellow / .red / .gray class (canonical, animatable) with data-color
 * attribute mirroring for backwards compatibility. */
.onwave-nav .onwave-nav-beacon-wrap {
  display: inline-flex;
  align-items: center;
  gap: 6px;
}
.onwave-nav .onwave-nav-beacon {
  font-size: var(--fs-lg);   /* хост иконы-точки: её размер — .9em от этого кегля */
  line-height: 1;
  padding: 0 2px;
  user-select: none;
  transition: color 0.3s, filter 0.3s, transform 0.15s;
  color: #9ca09a;
}
.onwave-nav .onwave-nav-beacon-wrap:hover .onwave-nav-beacon {
  transform: scale(1.15);
}
.onwave-nav .onwave-nav-beacon.green,
.onwave-nav .onwave-nav-beacon[data-color="green"] {
  color: #4ea365; filter: drop-shadow(0 0 4px rgba(78,163,101,0.4));
  animation: onwave-beacon-breathe 3.4s ease-in-out infinite;
}
.onwave-nav .onwave-nav-beacon.yellow,
.onwave-nav .onwave-nav-beacon[data-color="yellow"] {
  color: #d1a041; filter: drop-shadow(0 0 4px rgba(209,160,65,0.5));
  animation: onwave-beacon-breathe 3.4s ease-in-out infinite;
}
.onwave-nav .onwave-nav-beacon.red,
.onwave-nav .onwave-nav-beacon[data-color="red"] {
  color: #c8524b; filter: drop-shadow(0 0 4px rgba(200,82,75,0.55));
  animation: onwave-beacon-red-pulse 1.6s ease-in-out infinite;
}
.onwave-nav .onwave-nav-beacon.gray,
.onwave-nav .onwave-nav-beacon[data-color="gray"] {
  color: #b5b8b5; opacity: 0.55;
}
@keyframes onwave-beacon-red-pulse {
  0%, 100% { opacity: 1; }
  50%      { opacity: 0.55; }
}
/* Wave DAVID-HIVE-MIGRATE-20260818-BREATHE :: обычное дежурное состояние
 * (не тревога — та уже своя, быстрая и резкая) получает медленное, едва
 * заметное дыхание — сигнал "данные живые, а не застывший снимок", а не
 * украшение. Только зелёный/жёлтый: серому (загрузка) и красному
 * (тревога) — не подмешиваем, у них свой смысл. */
@keyframes onwave-beacon-breathe {
  0%, 100% { opacity: 1;   transform: scale(1); }
  50%      { opacity: .78; transform: scale(1.06); }
}

/* Micro-caption "$X/$Y" — visible ONLY on wide viewports (>1200px).
 * Hidden on tablet/mobile to keep header lean; tooltip on the dot still gives
 * full data. */
.onwave-nav .onwave-nav-beacon-caption {
  display: none;
  font-size: var(--fs-xs);
  font-family: var(--onwave-font-mono);   /* «$X/$Y» — данные, моно законно */
  color: var(--onwave-muted);
  letter-spacing: 0.01em;
  transition: color 0.3s;
  white-space: nowrap;
  animation: onwave-caption-breathe 3.4s ease-in-out infinite;
}
@keyframes onwave-caption-breathe {
  0%, 100% { opacity: .8; }
  50%      { opacity: 1; }
}
@media (min-width: 1200px) {
  .onwave-nav .onwave-nav-beacon-caption {
    display: inline;
  }
}
.onwave-nav .onwave-nav-beacon-wrap:hover .onwave-nav-beacon-caption {
  color: var(--onwave-ink);
}

/* Suppress page-local duplicate headers/nav when shared header mounts.
 * Setting body[data-onwave-nav-mounted] is done by onwave-nav.js itself so
 * that if the script fails to load, local fallback stays visible. */
body[data-onwave-nav-mounted] > header:not(.onwave-nav):not(.lodge-nav) {
  display: none !important;
}
body[data-onwave-nav-mounted] .wrap > header:not(.onwave-nav):not(.lodge-nav),
body[data-onwave-nav-mounted] .bar,
body[data-onwave-nav-mounted] header.topbar {
  display: none !important;
}
body[data-onwave-nav-mounted] nav.tabs[data-testid="hive-main-nav"] {
  display: none !important;
}

/* shadcn HSL for React kanban side */
:root {
  --background: 40 30% 98%;
  --foreground: 78 30% 16%;
  --card: 42 25% 94%;
  --card-foreground: 78 30% 16%;
  --popover: 42 25% 94%;
  --popover-foreground: 78 30% 16%;
  --primary: 78 32% 22%;
  --primary-foreground: 40 30% 98%;
  --secondary: 42 25% 88%;
  --secondary-foreground: 78 30% 16%;
  --muted: 42 20% 90%;
  --muted-foreground: 42 10% 45%;
  --accent: 40 30% 90%;
  --accent-foreground: 78 30% 16%;
  --destructive: 0 55% 47%;
  --destructive-foreground: 40 30% 98%;
  --border: 42 20% 82%;
  --input: 42 20% 82%;
  --ring: 78 32% 22%;
  --radius: 0.375rem;
}


/* ═══ Wave PHASE-5-BATCH5-MOBILE-20260814 · MOBILE polish ═══
 * Единые мобайл-правки для всех poverkhностей onwave (<=640px).
 * Master directive: последний спринт, мобайл-полировка.
 *
 * Ключевые исправления:
 * 1. .onwave-nav-items: убираем flex-wrap → делаем горизонтальный
 *    overflow-x:auto с momentum scroll (стек не помещается на 390px).
 * 2. Кнопки в actions: min-height 44px для тактильности.
 * 3. Caption $X/$Y виден на мобиле (не только >1200px) — компактный.
 * 4. Padding контейнера сжат.
 * 5. Skip legacy top-bar duplicates → hidden.
 */
@media (max-width: 640px) {
  .onwave-nav, .lodge-nav {
    gap: 6px;
    padding: 8px 12px;
    flex-wrap: nowrap;
    overflow: hidden;
  }
  .onwave-nav .onwave-nav-brand, .lodge-nav .lodge-nav-brand {
    font-size: var(--fs-sm);
    margin-right: 4px;
    flex-shrink: 0;
  }
  .onwave-nav .onwave-nav-items {
    flex-wrap: nowrap;
    overflow-x: auto;
    overflow-y: hidden;
    scrollbar-width: none;  /* Firefox */
    -ms-overflow-style: none;  /* IE */
    -webkit-overflow-scrolling: touch;
    min-width: 0;
    flex: 1 1 0;
    padding-bottom: 2px;
    mask-image: linear-gradient(to right, black 92%, transparent 100%);
  }
  .onwave-nav .onwave-nav-items::-webkit-scrollbar { display: none; }
  .onwave-nav .onwave-nav-item,
  .onwave-nav a,
  .lodge-nav a {
    flex-shrink: 0;
    padding: 8px 10px;
    min-height: 32px;
    display: inline-flex;
    align-items: center;
    font-size: var(--fs-sm);
  }
  .onwave-nav .onwave-nav-actions {
    gap: 4px;
    flex-shrink: 0;
  }
  .onwave-nav .onwave-nav-action,
  .onwave-nav .onwave-nav-theme,
  .lodge-nav .lodge-nav-theme {
    padding: 6px 8px;
    min-height: 32px;
    font-size: var(--fs-xs);
  }
  .onwave-nav .onwave-nav-spacer { flex: 0 0 4px; }

  /* Beacon caption виден на мобиле в компактной форме */
  .onwave-nav .onwave-nav-beacon-caption {
    display: inline;
    font-size: var(--fs-xs);
  }

  /* Общий main padding — уменьшаем */
  body main { padding-left: 12px !important; padding-right: 12px !important; }

  /* Все таблицы в onwave-контуре: горизонтальный скролл вместо разрывания */
  main table {
    display: block;
    overflow-x: auto;
    white-space: nowrap;
    -webkit-overflow-scrolling: touch;
    max-width: 100%;
  }
  main table thead, main table tbody, main table tr { width: 100%; }

  /* Виджеты в .summary grid — 1 колонка */
  main .summary, main .widgets { grid-template-columns: 1fr !important; }

  /* Скрыть узкую тень между ячейками на мобиле — экономит место */
  main table th, main table td { padding: 8px 10px !important; }

  /* Кнопки/inputs — тактильные target */
  main button, main input, main select, main textarea {
    min-height: 40px;
  }

  /* h1 — сжимаем на ступень шкалы: титул страницы → титул блока */
  main h1 { font-size: var(--fs-lg) !important; line-height: var(--lh-lg); }
  main h1 small { display: block; margin-left: 0; margin-top: 4px; font-size: var(--fs-xs); }
}

/* Ultra-narrow (<=380px, iPhone SE) — доп сжатие */
@media (max-width: 380px) {
  .onwave-nav .onwave-nav-brand { font-size: var(--fs-sm); }
  .onwave-nav .onwave-nav-item, .onwave-nav a, .lodge-nav a {
    padding: 6px 8px; font-size: var(--fs-xs);
  }
  .onwave-nav .onwave-nav-beacon-caption { display: none; }
}


/* ═══════════════════════════════════════════════════════════════════════
   Этап 0 дизайн-скачка «Живой улей» · 19.08.2026
   Общий язык цвета и движения. Значения — копия из hive.html (единственный
   источник правды), чтобы lodge-страницы говорили теми же ролями.
   Новые токены пока никто не носит: добавление не может ничего сдвинуть.
   --lodge-ok/warn/fail СОЗНАТЕЛЬНО не переклеены — см. этап 5.
   ═══════════════════════════════════════════════════════════════════════ */
/* ═══════════════════════════════════════════════════════════════════════
   Этап 0 · вторая половина: порт роль-токенов в /opt/roy-lite/site/onwave-tokens.css
   Спека: docs_DESIGN_SPEC_20260819.md §3.e («Сначала — общий язык») и §3.c.

   ЭТО НЕ РАБОЧИЙ ФАЙЛ И ОН НИКУДА НЕ ПОДКЛЮЧЁН.
   Это заготовка для человека: агент, делавший этап 0, писать в
   /opt/roy-lite/site/ не имеет права (граница задачи — только /opt/roy-lite/api),
   поэтому половина этапа, живущая в site/, оформлена патчем, а не правкой.

   Куда приложить: дописать в конец /opt/roy-lite/site/onwave-tokens.css,
   ПЕРЕД любыми правилами, которые эти токены используют.

   Почему это безопасно для hive.html (проверка «без визуального сдвига»):
   hive.html подключает onwave-tokens.css строкой 15 (<link>), а свой
   собственный :root/.dark объявляет строкой 33 (<style>) — то есть ПОЗЖЕ
   и при равной специфичности перебивает. Значения ниже скопированы из
   hive.html один-в-один, поэтому даже при совпадении каскада ничего не
   меняется. Выигрывают lodge-страницы: у них этих ролей раньше не было.

   Значения — копипаст из hive.html:73-102 (светлая) и hive.html:134-146
   (тёмная), сверено на момент коммита. Единственный источник правды —
   по-прежнему hive.html; здесь копия, потому что общий язык обязан быть
   доступен страницам, которые hive.html не грузят.
   Контрасты пересчитывает tests/test_palette_roles.py — не на глаз.
   ═══════════════════════════════════════════════════════════════════════ */

/* Двойной селектор кроет обе схемы тем разом: класс .dark на <html> в
   hive.html и атрибут data-onwave-theme на lodge-страницах. Унификация
   самого механизма темы — это этап 6, не этап 0. */

:root,
[data-onwave-theme="light"] {
  /* ── СТАТУС: состояние. Набор фиксирован и закрыт: четыре роли. ───── */
  --status-good:        120 60% 40%;
  --status-warning:      40.8 96% 54%;
  --status-serious:      16.9 79% 64%;
  --status-critical:      0 61.3% 52.4%;
  /* ink — тот же тон, доведённый до текстового контраста к фону темы */
  --status-good-ink:      120 60% 26%;
  --status-warning-ink:    40.8 88% 30%;
  --status-serious-ink:    16.9 65% 41%;
  --status-critical-ink:    0 62% 48%;

  /* ── КАТЕГОРИЯ: принадлежность (проект / эпик / слой). ─────────────── */
  --cat-1: 212 68% 50%;   /* синий      */
  --cat-2:  18 81% 56%;   /* оранжевый  */
  --cat-3: 162 73% 40%;   /* бирюзовый  */
  --cat-4: 258 49% 44%;   /* фиолетовый */
  --cat-5: 338 68% 70%;   /* розовый    */
  --cat-6: 120 100% 26%;  /* зелёный    */
  --cat-7: 300 45% 42%;   /* пурпурный  */
  --cat-8: 218 22% 45%;   /* сланцевый  */

  /* ── Токены движения (§3.c) ────────────────────────────────────────── */
  --breathe: 2.4s;
  --settle: 300ms;
  --afterglow: 1200ms;
  --flip: 220ms;
}

/* Статусные ЗАЛИВКИ (--status-good/-warning/-serious/-critical) в тёмной
   теме не переопределяются: роль одна на обе темы, меняется только ink. */
.dark,
[data-onwave-theme="dark"] {
  --status-good-ink:      120 52% 62%;
  --status-warning-ink:    40.8 90% 60%;
  --status-serious-ink:    16.9 79% 66%;
  --status-critical-ink:    0 70% 66%;

  --cat-1: 212 77% 64%;
  --cat-2:  18 85% 64%;
  --cat-3: 162 58% 49%;
  --cat-4: 258 68% 72%;
  --cat-5: 338 74% 78%;
  --cat-6: 120 47% 47%;
  --cat-7: 300 50% 70%;
  --cat-8: 218 29% 67%;
}

/* ═══ Модернизация «Тихая бумага» · M0/M2: ритм, шкала, поверхности ═══
   Спека: docs_MODERNIZATION_SPEC_20260819.md §3. Токены без цвета — общие
   для обоих миров разом: hsl-триплеты hive.html и hex-токены lodge-страниц
   читают одни и те же значения, поэтому «13px» на конвейере и «13px» на
   доске больше не могут разъехаться.

   Диагноз, который это лечит (§1.1): 225 объявлений font-size в одном
   hive.html, размазанных между .62 и .95rem. Это не шкала, а континуум
   микрокеглей: иерархии по размеру нет вообще, всю работу тащат вес и
   капс. Ниже — шесть размеров, каждый с ПАРНОЙ высотой строки (высота
   строки в px, а не множителем: множитель на .75rem и на 1.5rem даёт
   разный ритм, а вертикальная сетка обязана быть одна).

   M2 носит только тип-шкалу. Пространство, радиусы и тени объявлены
   заранее (§3 отдаётся одним куском) — их наденут M3 и M4; объявление
   переменной, которую никто не читает, ничего не двигает. */
:root {
  /* ── Пространство · сетка 4pt. Значений вне шкалы не существует. ── */
  --sp-1: 4px;   /* внутри строки: иконка↔слово, чип↔чип            */
  --sp-2: 8px;   /* между карточками, между чипами и заголовком      */
  --sp-3: 12px;  /* паддинг карточки задачи, гэп колонок             */
  --sp-4: 16px;  /* паддинг виджета/плитки, гэп плиток               */
  --sp-5: 20px;  /* паддинг крупной карточки, шапка модала           */
  --sp-6: 24px;  /* между блоками внутри экрана                      */
  --sp-7: 32px;  /* между секциями экрана                            */
  --sp-8: 40px;  /* верх страницы → заголовок                        */
  --sp-9: 48px;  /* низ страницы                                     */

  /* ── Тип-шкала · шесть размеров, каждый с парной высотой строки.
     Ни одного font-size вне шкалы. 225 деклараций сводятся к шести. ── */
  --fs-xs: .75rem;     --lh-xs: 16px;  /* 12 · кикеры, подписи данных, мета  */
  --fs-sm: .8125rem;   --lh-sm: 18px;  /* 13 · чипы, кнопки, вторичный текст */
  --fs-md: .875rem;    --lh-md: 20px;  /* 14 · рабочий текст, титул карточки */
  --fs-lg: 1rem;       --lh-lg: 22px;  /* 16 · заголовок блока/секции        */
  --fs-xl: 1.25rem;    --lh-xl: 26px;  /* 20 · заголовок страницы/модала     */
  --fs-num: 1.5625rem; --lh-num: 30px; /* 25 · герой-числа плиток            */

  /* ── Радиусы: три + пилюля. Calc-производные запрещены. ── */
  --r-s: 6px;    /* чипы, теги, мини-элементы   */
  --r-m: 10px;   /* кнопки, инпуты, карточки задач */
  --r-l: 14px;   /* виджеты, плитки, колонки, модал */
  --r-pill: 999px;

  /* ── Разделители и высота ── */
  --hairline-a: .65;                       /* светлая: линия в полтона     */
  --shadow-pop: 0 1px 2px hsl(30 15% 12% / .05), 0 2px 6px hsl(30 15% 12% / .05);
  --shadow-overlay: 0 16px 40px hsl(30 15% 12% / .16), 0 3px 10px hsl(30 15% 12% / .08);

  /* M3 · §3.1, строка «Карточка»: в СВЕТЛОЙ теме у карточки разделителя-
     линии нет — её отделяет тон. В ТЁМНОЙ тон не работает (surface #1c1f24
     на фоне #14161a — разница в четыре процента светлоты), и там карточка
     обязана носить hairline. Одним токеном, а не парой правил на каждую
     поверхность: §7 запрещает переписывать инлайн-стили в классы, а инлайн
     не умеет «а в тёмной теме иначе». */
  --onwave-card-line: transparent;
  --lodge-card-line: var(--onwave-card-line);

  /* M3 · «один разделитель на стык»: шапка отделялась от полосы контента
     СРАЗУ линией и тенью. В светлой теме стык держит тень (тон + подъём
     читаются), поэтому линия гасится; в тёмной тень не видна на #14161a —
     там работает hairline, а тень снимается. Та же пара, что у карточки
     выше, только для одного стыка «шапка ↔ страница». */
  --onwave-nav-line: transparent;
  --onwave-nav-shadow: var(--onwave-shadow-1);
}
.dark, [data-onwave-theme="dark"] {
  --hairline-a: 1;                         /* тёмная: полутон утонет — линия в полную силу */
  --shadow-pop: 0 1px 2px hsl(0 0% 0% / .4);
  --shadow-overlay: 0 16px 48px hsl(0 0% 0% / .55), 0 3px 10px hsl(0 0% 0% / .4);
  --onwave-card-line: var(--onwave-border);
  --onwave-nav-line: var(--onwave-border);
  --onwave-nav-shadow: none;
}

/* ── Пять голосов (§3.2) ──────────────────────────────────────────────
   Голоса не заводят своих классов: §7 запрещает переписывать инлайн-стили
   в классы ради чистоты, поэтому каждый голос выдаётся по месту, а здесь —
   правила, по которым он выдаётся, чтобы следующая правка не изобретала
   седьмой кегль и семнадцатую разрядку.

   ТИТУЛ СТРАНИЦЫ  Manrope 650 · --fs-xl · ls −.01em
   ТИТУЛ БЛОКА     Manrope 600 · --fs-lg · sentence case
   КИКЕР           Manrope 600 · --fs-xs · ls .05em · uppercase · muted
                   — ЕДИНСТВЕННЫЙ законный капс во всей системе. До этого
                   шага капс стоял 29 раз в 16 вариантах разрядки
                   (.02em….12em): на кнопках, табах, лейблах форм, th
                   таблиц. Русские прописные шире латинских, поэтому
                   разрядка выше .05em превращает «В РАБОТЕ» в трафарет —
                   .05em это жёсткий потолок, а не ориентир.
   РАБОЧИЙ ТЕКСТ   Manrope 400–500 · --fs-md
   ДАННЫЕ          IBM Plex Mono 500 · tabular-nums — и ТОЛЬКО данные:
                   числа, деньги, id, хэши, время, слаги. Моно не бывает
                   на элементе управления и не бывает на лейбле.

   Единственный класс здесь — для тех страниц, где своего .mono нет
   (onwave-spend.html): ставить руками font-family в четырёх местах —
   ровно тот путь, которым набирается следующая сотня деклараций. */
.ow-data {
  font-family: var(--onwave-font-mono);
  font-variant-numeric: tabular-nums;
}

/* ── Иконки · спека модернизации §4 (M1) ──────────────────────────────
   Спрайт инжектит onwave-nav.js первым узлом <body>; здесь — только
   геометрия. Размер задаётся кеглем строки (1em-масштаб), краска —
   currentColor, поэтому иконка автоматически в тоне текста, статуса или
   роли, рядом с которой стоит, и в обеих темах разом.

   #ow-icons прячем правилом, а не только атрибутом hidden: hidden — это
   HTML-атрибут, а корень спрайта живёт в SVG-пространстве имён, где
   UA-правило [hidden] не срабатывает. Без этой строки спрайт занял бы
   150px белого сверху каждой страницы. */
#ow-icons { position: absolute; width: 0; height: 0; overflow: hidden; }

.ic   { width: 1.1em; height: 1.1em; flex: none; vertical-align: -.2em; }
.ic-s { width: .9em;  height: .9em;  vertical-align: -.1em; }
svg.ic { display: inline-block; }

/* ── Канонический живой аффорданс + послесвечение (§3.c) ──────────────
   Те же правила, что уже стоят в hive.html (строки ~748-790), вынесены
   сюда, чтобы lodge-страницы этапа 5 («ИИ-штат», индикатор связи) не
   заводили свою копию дышащей точки. Кейфрейм назван как в hive.html,
   переобъявление одноимённого @keyframes безвредно — значения те же. */
.live-dot {
  width: 7px; height: 7px; border-radius: 50%;
  background: hsl(var(--status-good-ink)); display: inline-block; flex: none;
  animation: eventlog-breathe var(--breathe) ease-in-out infinite;
}
.live-dot.stale {
  animation: none; opacity: .35;
  background: hsl(var(--muted-foreground));
}
@keyframes eventlog-breathe {
  0%, 100% { opacity: .45; transform: scale(.85); }
  50%      { opacity: 1;   transform: scale(1.15); }
}

.just-changed { animation: afterglow var(--afterglow) ease-out 1; border-radius: 4px; }
@keyframes afterglow {
  from { background: hsl(var(--accent)); }
  to   { background: transparent; }
}

@media (prefers-reduced-motion: reduce) {
  .live-dot, [class*="pulse"], .just-changed { animation: none !important; }
  * { transition-duration: 0.01ms !important; }
}


/* ── Деньги в шапке · этап 4 «Живой улей» ─────────────────────────────
   Две полосы разной природы. Статусная краска разрешена ТОЛЬКО полосе
   реальных долларов: подписка в долларах кончиться не может, красить её
   состоянием — врать. Красный остаётся за фактом остановки улья. */
.onwave-nav .ow-money {
  display: inline-flex; gap: .7rem; align-items: center;
  text-decoration: none; font-size: var(--fs-xs);
  font-family: var(--onwave-font-mono, ui-monospace, monospace);  /* деньги = данные */
}
.onwave-nav .ow-lane { display: inline-flex; align-items: center; gap: .3rem; }
.onwave-nav .ow-lane b { font-variant-numeric: tabular-nums; }
.onwave-nav .ow-tag { opacity: .75; }
.onwave-nav .ow-api { color: hsl(var(--status-good-ink)); }
.onwave-nav .ow-money[data-state="warning"]   .ow-api,
.onwave-nav .ow-money[data-state="cap_stale"] .ow-api { color: hsl(var(--status-warning-ink)); }
.onwave-nav .ow-money[data-state="serious"]   .ow-api { color: hsl(var(--status-serious-ink)); }
.onwave-nav .ow-money[data-state="critical"]  .ow-api { color: hsl(var(--status-critical-ink)); }
.onwave-nav .ow-money[data-state="critical"]  .ow-ic {
  animation: onwave-beacon-red-pulse 1.6s ease-in-out infinite;
}
.onwave-nav .ow-sub { color: var(--onwave-muted, hsl(var(--muted-foreground))); }
/* Слово состояния полосы денег — кикер, единственная законная должность
   капса (§3.2). Кегль поднят с .62rem до 12px: ниже 12px muted-краска не
   держит AA, а «CAP_STALE» на 9.9px читалось как шум. */
.onwave-nav .ow-state {
  font-family: var(--onwave-font-sans);
  font-size: var(--fs-xs); letter-spacing: .05em; text-transform: uppercase;
}
@media (max-width: 1200px) {
  .onwave-nav .ow-sub, .onwave-nav .ow-state { display: none; }
}
@keyframes onwave-beacon-red-pulse {
  0%, 100% { opacity: 1; } 50% { opacity: .35; }
}
