/* ══════════════════════════════════════════════════════════════════════
   ЧАСТЬ 1 — Типографика
   Unbounded (дисплей) слегка «плотнее» на больших кеглях — компенсируем
   трекингом. Manrope (текст) получает увеличенный line-height для
   удобного чтения длинных описаний. JetBrains Mono (цифры) — табличные
   цифры включены глобально, чтобы суммы в списках не «прыгали» по ширине.
   ══════════════════════════════════════════════════════════════════════ */
h1, h2, h3, .font-display {
  letter-spacing: -0.02em;
}
body, .font-sans {
  line-height: 1.55;
}
.font-mono {
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.01em;
}
/* Плейсхолдеры полей ввода — тем же читаемым текстовым шрифтом, но чуть
   светлее, чтобы не путать с введённым значением. */
input::placeholder, textarea::placeholder {
  font-family: 'Manrope', ui-sans-serif, sans-serif;
  opacity: 0.45;
}

/* ══════════════════════════════════════════════════════════════════════
   ЗАДАЧА 1 — Многослойный анимированный фон
   Три «пятна» radial-gradient поверх сплошного --bg-base (переменные
   заданы в page.ts по атрибуту html[data-theme]). Анимируется только
   transform на отдельных абсолютно спозиционированных span — без layout
   reflow и без blur() на весь экран, чтобы не сажать перформанс на
   слабых телефонах.
   ══════════════════════════════════════════════════════════════════════ */
.app-bg {
  position: fixed;
  inset: 0;
  z-index: 0;
  overflow: hidden;
  background: var(--bg-base, #020617);
  pointer-events: none;
}
.app-bg-blob {
  position: absolute;
  width: 62vmax;
  height: 62vmax;
  border-radius: 50%;
  will-change: transform;
  transform: translate3d(0, 0, 0);
}
.app-bg-blob-1 {
  top: -14%;
  left: -12%;
  background: radial-gradient(circle at 30% 30%, rgba(var(--bg-a1, 139, 92, 246), 0.42), rgba(var(--bg-a1, 139, 92, 246), 0) 70%);
  animation: bgDriftA 30s ease-in-out infinite alternate;
}
.app-bg-blob-2 {
  bottom: -18%;
  right: -12%;
  background: radial-gradient(circle at 70% 70%, rgba(var(--bg-a2, 34, 211, 238), 0.36), rgba(var(--bg-a2, 34, 211, 238), 0) 70%);
  animation: bgDriftB 36s ease-in-out infinite alternate;
}
.app-bg-blob-3 {
  top: 28%;
  right: 12%;
  width: 50vmax;
  height: 50vmax;
  background: radial-gradient(circle, rgba(var(--bg-a3, 232, 60, 205), 0.3), rgba(var(--bg-a3, 232, 60, 205), 0) 70%);
  animation: bgDriftC 40s ease-in-out infinite alternate;
}
@media (prefers-reduced-motion: reduce) {
  .app-bg-blob { animation: none !important; }
}

/* Тонкая зернистая текстура (grain) поверх аврора-пятен — премиальная,
   «плёночная» фактура без внешних изображений: чистый SVG-шум через
   feTurbulence, закодированный как data-URI. Статична (не анимируется),
   поэтому не влияет на perf/battery и не зависит от prefers-reduced-motion.
   mix-blend-mode:overlay мягко вплавляет её в цвет фона под ней. */
.app-bg::after {
  content: "";
  position: absolute;
  inset: 0;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='180' height='180'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
  opacity: 0.05;
  mix-blend-mode: overlay;
  pointer-events: none;
}
html:not(.dark) .app-bg::after {
  opacity: 0.035;
}

/* Контент должен рисоваться поверх фона — .app-content задаётся в app.js
   на обёртке main-контейнера (position: relative; z-index: 1). */
.app-content {
  position: relative;
  z-index: 1;
}
.app-content > header,
.app-content > main {
  position: relative;
  z-index: 1;
}

/* ══════════════════════════════════════════════════════════════════════
   ЗАДАЧА 2 — «Стеклянные» кнопки
   Базовый класс .glass-btn добавляется ДОПОЛНИТЕЛЬНО к существующим
   Tailwind-классам кнопки (не заменяет их — цвет текста/акцента
   по-прежнему берётся из Tailwind, .glass-btn отвечает только за
   стекло/блик/тактильность). Тёмный вариант — по умолчанию, светлый —
   через html:not(.dark).
   ══════════════════════════════════════════════════════════════════════ */
.glass-btn {
  --glow: rgba(139, 92, 246, 0.45);
  position: relative;
  overflow: hidden;
  isolation: isolate;
  -webkit-backdrop-filter: blur(16px);
  backdrop-filter: blur(16px);
  border-color: rgba(255, 255, 255, 0.14) !important;
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.22),
    inset 0 -1px 0 rgba(0, 0, 0, 0.18);
  transition: transform 0.16s cubic-bezier(0.34, 1.56, 0.64, 1),
              box-shadow 0.2s ease;
}
/* Сам стеклянный блик — отдельный слой, z-index:-1 внутри локального
   стекинг-контекста кнопки (isolation:isolate на .glass-btn делает его
   именно локальным, наружу не "протекает"). Он оказывается ЗА текстом/
   иконкой, но ПЕРЕД собственным фоном кнопки — будь то сплошной цвет
   (background-color) или градиент Tailwind (background-image) — поэтому
   не может ни стереть, ни "спорить" ни с одним из них: это гарантированно
   разные CSS-свойства/разные элементы, а не переопределение одного и
   того же background на одном и том же узле.  */
.glass-btn::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background-image: linear-gradient(180deg, rgba(255, 255, 255, 0.16), rgba(255, 255, 255, 0.02));
  pointer-events: none;
}
html:not(.dark) .glass-btn::before {
  background-image: linear-gradient(180deg, rgba(255, 255, 255, 0.5), rgba(255, 255, 255, 0.12));
}
html:not(.dark) .glass-btn {
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.85),
    inset 0 -1px 0 rgba(15, 23, 42, 0.1);
  border-color: rgba(15, 23, 42, 0.12) !important;
}
.glass-btn:hover,
.glass-btn:focus-visible {
  transform: translateY(-1px);
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.3),
    inset 0 -1px 0 rgba(0, 0, 0, 0.18),
    0 0 0 1.5px var(--glow),
    0 10px 24px -8px var(--glow);
}
.glass-btn:focus-visible {
  outline: 2px solid var(--glow);
  outline-offset: 2px;
}
.glass-btn:active {
  transform: scale(0.97) translateY(0);
}
/* Диагональный блик, «пробегающий» по кнопке при наведении — один раз,
   ~380мс, без повторов (см. .glass-btn:hover запуск анимации). */
.glass-btn::after {
  content: "";
  position: absolute;
  inset: -30% -70%;
  background: linear-gradient(115deg, transparent 42%, rgba(255, 255, 255, 0.35) 50%, transparent 58%);
  transform: translateX(-130%) skewX(-12deg);
  pointer-events: none;
}
.glass-btn:hover::after {
  animation: glareSweep 0.38s ease-out 1;
}
@media (prefers-reduced-motion: reduce) {
  .glass-btn:hover::after { animation: none !important; }
}

/* ══════════════════════════════════════════════════════════════════════
   ЗАДАЧА 3 — Цветовая айдентика вкладок
   Переменные задаются через [data-tab="..."] на корневой обёртке
   приложения (app.js) и наследуются вниз всем потомкам — поэтому их
   можно использовать где угодно в дереве без дублирования цветов.
   ══════════════════════════════════════════════════════════════════════ */
[data-tab="income"]   { --tab-from: 132, 204, 22;  --tab-to: 34, 211, 238; }   /* неоновый лайм → электрик-циан */
[data-tab="expense"]  { --tab-from: 236, 72, 153;  --tab-to: 249, 115, 22; }   /* неоновый розовый → закатный оранжевый */
[data-tab="debts"]    { --tab-from: 245, 158, 11;  --tab-to: 234, 179, 8; }    /* янтарный → золотой (без изменений) */
[data-tab="compare"]  { --tab-from: 34, 211, 238;  --tab-to: 139, 92, 246; }   /* циан → электрик-виолет */
[data-tab="settings"] { --tab-from: 71, 85, 105;   --tab-to: 167, 139, 250; }  /* сине-серый → мягкий виолет */

/* Лёгкая тонировка экрана цветом активной вкладки — дополнительный слой
   поверх .app-bg, НЕ поверх текста (z-index такой же, как у .app-bg,
   но идёт позже в DOM → рисуется поверх него). Затухает к центру, чтобы
   не мешать читаемости карточек с текстом. */
.tab-tint {
  position: fixed;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  background:
    radial-gradient(120% 55% at 50% -12%, rgba(var(--tab-from, 139, 92, 246), 0.16), transparent 60%),
    radial-gradient(120% 55% at 50% 112%, rgba(var(--tab-to, 217, 70, 239), 0.1), transparent 60%);
  transition: background 0.45s ease;
}

/* Переиспользуемая градиентная полоска-акцент для заголовков карточек —
   берёт цвет текущей вкладки автоматически, без хардкода per-tab классов. */
.tab-accent-bar {
  background: linear-gradient(90deg, rgb(var(--tab-from, 139, 92, 246)), rgb(var(--tab-to, 217, 70, 239)));
}

/* ══════════════════════════════════════════════════════════════════════
   ЭТАП 6 — Переход между вкладками
   Раньше смена вкладки была мгновенной (React просто размонтировал одно
   дерево и монтировал другое). Переключение вкладок — действие "десятки
   раз в день", поэтому анимация короткая (180мс, меньше порога в 300мс) и
   ненавязчивая: лёгкий fade + сдвиг на 6px, ease-out (акцент на начало
   движения, а не на затухание). Анимируются только transform/opacity —
   это GPU-свойства, не вызывающие пересчёт layout. Т.к. каждая вкладка при
   переключении реально размонтируется/монтируется заново (см. app.jsx —
   {activeTab === 'x' && (...)}), keyframe-анимация проигрывается заново
   при каждом монтировании без дополнительных трюков с key. ══════════════ */
@keyframes tabPanelEnter {
  from { opacity: 0; transform: translateY(6px); }
  to { opacity: 1; transform: translateY(0); }
}
.tab-panel-enter {
  animation: tabPanelEnter 180ms ease-out;
}
@media (prefers-reduced-motion: reduce) {
  /* Не убираем анимацию совсем — оставляем лёгкий fade (opacity), но
     убираем сдвиг, чтобы не провоцировать вестибулярный дискомфорт. */
  .tab-panel-enter {
    animation: none;
    opacity: 1;
  }
}

/* ══════════════════════════════════════════════════════════════════════
   ЭТАП 7 (v2) — "Бусина в пазу" в нижнем меню, настоящая рекреация эффекта
   из референс-видео "Meniscus" (компонент frontend/components/DockNav.jsx).

   Прежняя версия ЭТАП 7 была плоской заглушкой: прямоугольная "пилюля"
   ехала под прямой линией жёлоба на transform/opacity. Здесь жёлоб —
   ОДИН SVG-путь, который на каждом кадре анимации пересчитывается в JS
   под текущий X бусины (см. buildPath в DockNav.jsx) — поэтому кромка
   бара физически "облизывает" бусину и едет вместе с ней, а не просто
   визуально совпадает по позиции. Вся физика (пружина, асимметрия паза
   от скорости, интерполяция цвета, проявление подписи) — тоже там же,
   этот файл отвечает только за статичный внешний вид слоёв.

   Слои (все — прямые дети .dock-nav-wrap, порядок в DOM = порядок
   рисования):
   .dock-nav-glass       — HTML-подложка "мокрого стекла" плота: backdrop-
                            filter блюрит содержимое СТРАНИЦЫ позади бара
                            (не SVG!), clip-path: path(...) вырезает ей
                            форму паза — JS каждый кадр пишет туда тот же
                            'd', что и обводке ниже, так что форма всегда
                            совпадает один-в-один. Раньше вместо этого была
                            сплошная непрозрачная SVG-заливка — попросили
                            сделать полупрозрачной с блюром вместо плоского
                            чёрного фона.
   .dock-nav-svg         — SVG на всю ширину/высоту обёртки, viewBox в
                            пикселях 1:1 (подгоняется под реальную ширину
                            через ResizeObserver в JS). Только обводка —
                            заливки внутри SVG больше нет (см. выше).
   .dock-nav-plate-stroke— тонкая световая обводка кромки поверх стекла —
                            именно она даёт ощущение "поверхности жидкости"
                            (rim-light), как на референсе.
   .dock-nav-glow        — мягкое цветное пятно под бусиной, тот же цвет,
                            что и сама бусина в моменте (JS пишет --bf).
   .dock-nav-bead        — сама бусина: глянцевый радиальный градиент +
                            тень, X/Y и scale (сплющивание от скорости)
                            задаёт JS через transform на каждый кадр.
   .dock-nav-bead-icon   — иконка внутри бусины; JS плавно меняет opacity
                            в зависимости от близости к вкладке (проявляется
                            только рядом с "своей" вкладкой).
   .dock-nav-tabs        — ряд кнопок-вкладок (flex, равные колонки).
   .dock-nav-tab-btn     — сама кнопка. НАМЕРЕННО без своего фона/рамки/
                            тени (раньше был .glass-btn — снят: 5 таких
                            карточек впритык давали "рамку в рамке" и
                            залипающий цветной :focus-visible контур на
                            только что нажатой вкладке). Единственный фон —
                            стеклянная подложка дока под кнопками.
                            .dock-tab-covered (бусина сейчас над ней —
                            гасим статичную иконку под бусиной) и
                            .dock-tab-label-on (бусина почти доехала —
                            проявляем подпись) переключает JS через ref,
                            не через React state, чтобы не дёргать рендер
                            на каждый кадр.
   ══════════════════════════════════════════════════════════════════════ */
.dock-nav-wrap {
  position: relative;
  width: 100%;
  height: 92px;
  touch-action: pan-y;
}
.dock-nav-glass {
  position: absolute;
  inset: 0;
  background: rgba(2, 6, 23, 0.46);
  backdrop-filter: blur(18px) saturate(150%);
  -webkit-backdrop-filter: blur(18px) saturate(150%);
  will-change: clip-path;
}
html:not(.dark) .dock-nav-glass {
  background: rgba(255, 255, 255, 0.5);
}
.dock-nav-svg {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
}
.dock-nav-plate-stroke {
  fill: none;
  stroke: rgba(255, 255, 255, 0.16);
  stroke-width: 1.4;
  stroke-linejoin: round;
}
html:not(.dark) .dock-nav-plate-stroke {
  stroke: rgba(15, 23, 42, 0.14);
}
.dock-nav-glow {
  position: absolute;
  left: 0;
  top: 0;
  width: 168px;
  height: 104px;
  margin-left: -84px;
  margin-top: -34px;
  border-radius: 50%;
  background: radial-gradient(circle, rgba(var(--bf, 132, 204, 22), 0.42), transparent 70%);
  filter: blur(14px);
  pointer-events: none;
  will-change: transform;
}
.dock-nav-bead {
  position: absolute;
  left: 0;
  top: 0;
  width: 44px;
  height: 44px;
  margin-left: -22px;
  margin-top: -22px;
  border-radius: 9999px;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: grab;
  touch-action: none;
  will-change: transform;
  background: radial-gradient(120% 130% at 30% 22%, rgba(var(--bf, 132, 204, 22), 0.98), rgba(var(--bt, 34, 211, 238), 0.6) 75%);
  box-shadow:
    0 6px 18px -3px rgba(var(--bc, 132, 204, 22), 0.6),
    inset 0 1px 1px rgba(255, 255, 255, 0.4),
    inset 0 -3px 5px rgba(0, 0, 0, 0.22);
}
.dock-nav-bead:active {
  cursor: grabbing;
}
.dock-nav-bead-icon {
  display: flex;
  color: #0b0a14;
  will-change: opacity;
}
.dock-nav-tabs {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 6px;
  display: flex;
}
.dock-nav-tab-btn {
  position: relative;
  flex: 1 1 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 3px;
  padding: 7px 0 3px;
  /* Раньше здесь был класс .glass-btn (стеклянная карточка: фон, рамка,
     inset-тени, backdrop-blur, hover-блик, :focus-visible контур в цвет
     вкладки). Из-за него у каждой из 5 вкладок была своя видимая рамка,
     а впритык друг к другу они сливались в одну большую рамку вокруг
     всего бара — плюс у только что нажатой вкладки на мобильном Chrome
     залипал :focus-visible контур в её акцентном цвете. Сняли класс
     совсем: единственный фон, который должен быть виден — это сама
     SVG-плита дока под кнопками. bg/border/shadow обнулены явно (а не
     просто "унаследовано от Tailwind preflight"), чтобы это не зависело
     от порядка подключения стилей. */
  background: none;
  border: 0;
  box-shadow: none;
  color: rgba(148, 163, 184, 0.75);
}
html:not(.dark) .dock-nav-tab-btn {
  color: rgba(71, 85, 105, 0.75);
}
/* Клавиатурный фокус — узкое кольцо ТОЛЬКО вокруг самой иконки (не вся
   кнопка), в акцентном цвете вкладки (--acc). На тач/мышь почти никогда
   не срабатывает (:focus-visible), так что в обычном использовании
   ничего не видно вообще. */
.dock-nav-tab-btn:focus-visible {
  outline: none;
}
.dock-nav-tab-btn:focus-visible .dock-nav-tab-icon-wrap {
  outline: 2px solid var(--acc, #94a3b8);
  outline-offset: 4px;
  border-radius: 9999px;
}
.dock-nav-tab-icon {
  display: inline-flex;
  transition: opacity 110ms linear;
}
.dock-nav-tab-btn.dock-tab-covered .dock-nav-tab-icon {
  opacity: 0;
}
.dock-nav-tab-label {
  font-size: 9px;
  line-height: 1;
  font-weight: 700;
  color: rgb(var(--bf, 132, 204, 22));
  opacity: 0;
  transform: translateY(2px);
  transition: opacity 150ms ease, transform 150ms ease;
}
.dock-nav-tab-btn.dock-tab-label-on .dock-nav-tab-label {
  opacity: 1;
  transform: none;
}
.dock-nav-tab-icon-wrap {
  position: relative;
  display: inline-flex;
}
.dock-nav-badge {
  position: absolute;
  top: -1px;
  right: -3px;
  width: 7px;
  height: 7px;
  border-radius: 9999px;
  background: #ef4444;
}
@media (prefers-reduced-motion: reduce) {
  .dock-nav-tab-icon,
  .dock-nav-tab-label {
    transition: opacity 150ms ease;
  }
  .dock-nav-tab-label {
    transform: none;
  }
  .dock-nav-badge.animate-ping {
    animation: none;
  }
}