/*
 * Cyber background — комбинированный фон без литеральной "матрицы".
 *
 * Слои (снизу вверх):
 *   1. Body::before — точечная сетка 24×24 + ambient blobs, дрейф 75s.
 *   2. Body::after — две диагональные aurora-полосы (lime↔electric ↘ и
 *      magenta ↗) дрейфуют 45s, поверх — краевые vignette-свечения
 *      (лайм слева, magenta справа), brightness breathe 12s.
 *   3. .cb-scanline (3 штуки через cyber-bg.js):
 *        - горизонтальная лайм sweep ↓ 14s
 *        - горизонтальная electric sweep ↑ 18s
 *        - вертикальная magenta sweep → 22s
 *   4. .cb-particle (6-8 штук) — редкие точки пульсируют 3-6s
 *      в случайных местах. Lime / electric / magenta / amber.
 *
 * Активируется когда на body нет класса .no-cb-bg.
 * Всё CSS — на pseudo-элементах и нескольких fixed div, не пересекается с
 * legacy layout. Уважает prefers-reduced-motion.
 */

/* === Aurora palette — кастомизируемые цвета фона ===
   4 RGB-триплета БЕЗ alpha, чтобы каждый слой задавал свою прозрачность через
   rgba(var(--cb-aurora-N), .XX). Дефолт = исторический «green aurora».
   Переопределяется ИНЛАЙН на <html> до первого кадра (без зелёной вспышки)
   скриптом из localStorage['cb_palette'] — см. секцию «Внешний вид» в
   settings.html. Эти переменные НЕ трогают --cb-acid/--cb-electric и пр. из
   cyber.css (акценты UI: кнопки, чипы) — меняется только фон-меш и его частицы. */
:root {
  --cb-aurora-1: 163, 230, 53;   /* lime — главный */
  --cb-aurora-2: 56, 189, 248;   /* electric blue */
  --cb-aurora-3: 236, 72, 153;   /* magenta */
  --cb-aurora-4: 251, 191, 36;   /* amber */
}

/* === Нейтрализация legacy bg-килеров ===
   На некоторых страницах (calendar.html) загружаются три legacy css:
     1. magatron-video-bg.css — `body { background: #000 !important }`,
        `html::before, body::before { position: fixed; inset: 0; background: #000 }`.
     2. magatron-cyberpunk-fx.css — свой `body::after` с приглушёнными
        синими градиентами, `body { background-color: #000 !important }`.
     3. magatron-bg-fix.css — `body::before, body::after { display: none !important }`
        + делает body/html/.cal-wrap/etc. transparent. Это и было причиной
        белого фона: !important выключал мои body::before/::after.
   Project-страницы этих файлов НЕ грузят — там сразу всё играет.

   Здесь — принудительные противо-нейтрализации. */
html {
  /* ВЕСЬ меш живёт на background САМОГО html. Почему именно здесь, а не на
     fixed-псевдоэлементах: фон корневого элемента пропагируется на canvas
     вьюпорта и ГАРАНТИРОВАННО красит весь физический экран, включая обе safe-area
     зоны (статус-бар сверху, home-indicator снизу) и overscroll. А position:fixed
     псевдоэлементы на iOS standalone PWA обрезаются по layout-вьюпорту и в зону
     home-indicator НЕ дотягиваются (эмпирически доказано цветовой пробой: html=
     зелёный красил полосу, fixed-меш=малиновый — нет). Раньше меш был на псевдо,
     а html нёс только тусклые блобы → зона индикатора была темнее экрана = «чёрная
     полоса». Теперь html — ЕДИНСТВЕННЫЙ источник фона, шва быть не может.
     Порядок слоёв (сверху вниз): аврора → сетка → блобы → базовый цвет. */
  background-image:
    /* ЗАЛИВКА ЗОНЫ HOME-INDICATOR (самый верхний слой). КОРЕНЬ «чёрной полосы».
       Доказано цветовой пробой: зону home-indicator (нижние ~34px физ. экрана)
       красит ТОЛЬКО фон html — fixed-слои (#app-shell inset:0, #llm-bar,
       .pchat-panel) обрезаются по layout-вьюпорту и туда НЕ дотягивают. Поэтому
       непрозрачный нижний бар заканчивается выше зоны, а под ним торчал
       html-меш (#0d0c14) = тёмная полоса/шов. Решение: html сам красит
       нижнюю кромку сплошной полосой высотой safe-area тем же цветом, что и бар
       над ней. Цвет задаёт КАЖДАЯ страница через --cb-indicator-fill (бар-цвет
       на calendar/wrapper; transparent по умолчанию → на меш-страницах
       (projects) кромка остаётся мешем, шва нет). background-position:bottom +
       size 100%×(safe-area+14) → полоса прибита к физическому низу canvas. */
    linear-gradient(var(--cb-indicator-fill, transparent), var(--cb-indicator-fill, transparent)),
    /* НИЖНЕЕ СВЕЧЕНИЕ — равномерная по ширине подсветка нижней кромки. Зачем:
       правый-нижний угол меша объективно самый тёмный (там лишь один бледный
       розовый блоб 0.17, диагональная аврора туда не достаёт), а зону индикатора
       красит ТОЛЬКО html → угол читался чёрным. Вертикальный градиент одинаков по
       горизонтали → ровно поднимает ВСЮ нижнюю кромку и оба угла, убирая L/R-
       асимметрию. Гаснет вверх за ~170px. */
    linear-gradient(to top, rgba(var(--cb-aurora-2), 0.12) 0, rgba(var(--cb-aurora-1), 0.07) 70px, transparent 170px),
    /* аврора: краевые vignette-свечения (статичны, остаются тут).
       Две ДИАГОНАЛЬНЫЕ полосы переехали на body::after (transform-слой, GPU-
       композит) — здесь оставлены ПРОЗРАЧНЫМИ, чтобы не дублироваться и сохранить
       выравнивание 11-слойных массивов background-size/position/repeat. */
    linear-gradient(110deg, transparent, transparent),
    linear-gradient(250deg, transparent, transparent),
    radial-gradient(ellipse 30% 85% at 0% 50%, rgba(var(--cb-aurora-1), 0.18), transparent 70%),
    radial-gradient(ellipse 30% 85% at 100% 50%, rgba(var(--cb-aurora-3), 0.16), transparent 70%),
    /* точечная сетка 24×24 (цвет точек — из настроек подложки) */
    radial-gradient(circle at 1px 1px, var(--cb-backdrop-dot, rgba(255, 255, 255, 0.035)) 1px, transparent 1px),
    /* ambient blobs */
    radial-gradient(ellipse 90% 60% at 50% -5%, rgba(var(--cb-aurora-1), 0.20), transparent 70%),
    radial-gradient(ellipse 80% 55% at 100% 100%, rgba(var(--cb-aurora-3), 0.17), transparent 70%),
    radial-gradient(ellipse 80% 55% at 0% 90%, rgba(var(--cb-aurora-2), 0.18), transparent 70%),
    radial-gradient(ellipse 40% 30% at 70% 40%, rgba(var(--cb-aurora-4), 0.08), transparent 70%) !important;
  background-size:
    100% calc(env(safe-area-inset-bottom, 0px) + 14px),
    100% 100%,
    220% 220%, 220% 220%, 100% 100%, 100% 100%,
    24px 24px,
    100% 100%, 100% 100%, 100% 100%, 100% 100% !important;
  background-repeat:
    no-repeat,
    no-repeat,
    no-repeat, no-repeat, no-repeat, no-repeat,
    repeat,
    no-repeat, no-repeat, no-repeat, no-repeat !important;
  background-position:
    0 100%,
    0 0,
    0% 100%, 100% 0%, 0 0, 0 0,
    0 0,
    0% 0%, 0% 0%, 0% 0%, 0% 0% !important;
  /* База приподнята с #08080c до #0d0c14 — пустоты меша тёмно-индиговые, не
     «чёрная дыра». Переменную --cb-bg (карточки и пр.) не трогаем. */
  background-color: var(--cb-backdrop-base, #0d0c14) !important;
  /* PERF: html-меш СТАТИЧЕН (рисуется один раз, кешируется как слой). Раньше тут
     анимировался background-position (cb-root-drift) — это перекрашивало весь
     полноэкранный меш каждый кадр (профиль: ~8с Painting) и лагало артефакты.
     Дрейф аврор теперь на body::after через transform (GPU-композит, без paint). */
  /* Без overscroll-rubber-band — края при «оттянутом» скролле iOS показывали
     светлый сафари-фон. */
  overscroll-behavior: none;
  /* iOS standalone PWA: ЕДИНИЦА = 100vh, НЕ 100dvh. 100dvh на cold-start даёт
     неверную высоту (короче экрана) и «чинится» только физическим поворотом —
     ровно то, что наблюдал юзер (поворот убирал чёрную полосу внизу). 100vh в
     standalone (без тулбара) = полная высота экрана с первого кадра, фон-меш
     html красит зону home-indicator. Программный пересчёт env() — в cyber-bg.js. */
  min-height: 100vh;
}

/* Дрейф всего меша на html-холсте. 9 слоёв в том же порядке, что и background-image:
   [аврора110, аврора250, радL, радR, сетка, блоб-лайм, блоб-pink, блоб-blue, блоб-amber].
   Сетка (5-й) и радиальные авроры (3,4) стоят на месте — у них позиция константна. */
/* Дрейфует ТОЛЬКО аврора (слои 3,4 — размер 220%, есть куда двигаться). Заливка
   индикатора (1), нижнее свечение (2), сетка, радиальные авроры и блобы
   (размер 100% → position-% = no-op) стоят на месте. 11 значений = 11 слоёв. */
@keyframes cb-root-drift {
  0% {
    background-position:
      0 100%, 0 0, 0% 100%, 100% 0%, 0 0, 0 0, 0 0,
      0% 0%, 0% 0%, 0% 0%, 0% 0%;
  }
  50% {
    background-position:
      0 100%, 0 0, 60% 40%, 40% 70%, 0 0, 0 0, 0 0,
      0% 0%, 0% 0%, 0% 0%, 0% 0%;
  }
  100% {
    background-position:
      0 100%, 0 0, 100% 10%, 10% 90%, 0 0, 0 0, 0 0,
      0% 0%, 0% 0%, 0% 0%, 0% 0%;
  }
}

/* iOS PWA: safe-area. ВАЖНО — НЕ ставим padding-bottom на body.
   Раньше body имел padding-bottom: env(safe-area-inset-bottom), и это
   укорачивало body снизу на высоту home-indicator. Т.к. фон (body::before/
   ::after, fixed inset:0) и нижние фиксированные бары (#llm-bar, .pchat-panel)
   привязываются к body, их «низ» оказывался на safe-area-top, а под ними
   торчала чёрная body-полоса высотой home-indicator — на ВСЕХ страницах с
   нижним баром (календарь, wrapper-чат и т.д.). Верх при этом был «прибит»
   корректно, потому что padding-top смещает только контент, а не низ фона.
   Решение: body тянем до физического низа (без bottom-padding), а отступ
   под home-indicator даёт сам нижний бар своим padding-bottom. */
body, body.mg-app {
  padding-top: env(safe-area-inset-top, 0px) !important;
  padding-bottom: 0 !important;
  padding-left: env(safe-area-inset-left, 0px) !important;
  padding-right: env(safe-area-inset-right, 0px) !important;
}
html::before { display: none !important; content: none !important; }
body {
  /* body ПРОЗРАЧЕН: меш живёт на html-холсте (покрывает весь экран вкл. safe-area).
     Если у body непрозрачный фон, он закрывает html-холст, и в зоне индикатора,
     куда fixed-псевдоэлементы не дотягивают, торчит сплошной body-цвет = «чёрная
     полоса». Прозрачный body пускает холст насквозь — полоса исчезает. */
  background: transparent !important;
  background-color: transparent !important;
  isolation: isolate;
  /* 100vh, не 100dvh — см. коммент у html выше (cold-start iOS standalone PWA). */
  min-height: 100vh;
  overscroll-behavior: none;
}

/* LLM-bar: фиксируем bottom через dvh-варианты, помогает iOS Safari/PWA не
   "поднимать" bar при collapse URL-bar или при keyboard-up. */
#llm-bar {
  position: fixed !important;
  bottom: 0 !important;
  left: 0 !important;
  right: 0 !important;
  /* JS-fallback via visualViewport (если есть) можно навесить через bottom:
     calc(100dvh - 100%); но bottom:0 + transform:translateZ(0) надёжнее. */
  transform: translateZ(0);
}

/* body::before/::after ОТКЛЮЧЕНЫ. Весь меш переехал на background html (см.
   выше): только корневой фон достаёт до зоны home-indicator, fixed-псевдо — нет.
   Держать их параллельно нельзя — на основном экране меш удвоился бы (html+псевдо),
   и зона индикатора (только html) снова стала бы темнее = «полоса» вернулась бы.
   Блоки body::before/::after ниже оставлены как inert-история, но не рендерятся. */
/* body::before остаётся выключенным (его dotted-mesh живёт на background html).
   body::after ПЕРЕИСПОЛЬЗОВАН ниже как лёгкий transform-композитный слой дрейфа
   аврор — поэтому его здесь НЕ глушим. */
body::before {
  display: none !important;
  content: none !important;
}

/* === Слой 1: dotted grid + ambient mesh === */
body::before {
  content: "";
  position: fixed;
  /* iOS PWA: fixed bottom:0/inset:0 останавливается ровно на innerHeight и НЕ
     закрашивает зону home-indicator (она ниже innerHeight) — там торчит чёрная
     полоса ~34px под нижним баром. А на скролл-страницах visual viewport ещё и
     уезжает на vv.offsetTop (~68px), и низ фона не достаёт до физического низа.
     Решение — overscan: тянем слой top:-120px..bottom:-120px, т.е. с запасом за
     ВСЕ края. Лишнее уходит за экран (для фона безопасно), но зона индикатора и
     любой visual-viewport-offset гарантированно закрашены мешем, не чёрным.
     Верх не трогаем (top:0) — он и так прибит корректно. */
  top: 0;
  left: 0;
  right: 0;
  bottom: -120px;
  pointer-events: none;
  z-index: -3;
  background:
    /* Слой A: dotted grid (цвет точек — из настроек подложки) */
    radial-gradient(circle at 1px 1px, var(--cb-backdrop-dot, rgba(255, 255, 255, 0.035)) 1px, transparent 1px) 0 0 / 24px 24px,
    /* Слой B: ambient blobs — насыщенные, чтобы пустой bg сам выглядел
       как проекты со сквозной игрой цвета даже при минимуме карточек */
    radial-gradient(ellipse 90% 60% at 50% -5%, rgba(var(--cb-aurora-1), 0.20), transparent 70%),
    radial-gradient(ellipse 80% 55% at 100% 100%, rgba(var(--cb-aurora-3), 0.17), transparent 70%),
    radial-gradient(ellipse 80% 55% at 0% 90%, rgba(var(--cb-aurora-2), 0.18), transparent 70%),
    /* Дополнительный центральный orb — янтарный, для тёплого ядра */
    radial-gradient(ellipse 40% 30% at 70% 40%, rgba(var(--cb-aurora-4), 0.08), transparent 70%);
  background-color: var(--cb-backdrop-base, #08080c);
  animation: cb-mesh-drift 75s ease-in-out infinite alternate;
}

@keyframes cb-mesh-drift {
  0%   { background-position: 0 0, 0% 0%, 0% 0%, 0% 0%, 0% 0%; }
  50%  { background-position: 0 0, 8% 12%, -10% -14%, 12% 6%, -8% 10%; }
  100% { background-position: 0 0, -5% -7%, 6% 10%, -7% -5%, 9% -8%; }
}

/* === Слой 2: Aurora — ДРЕЙФ ЧЕРЕЗ TRANSFORM (GPU-композит, без перекраски) ===
   Раньше дрейф жил на background-position (cb-aurora-drift/cb-root-drift) + filter
   brightness (cb-edges-breathe) — оба свойства триггерят paint, и браузер
   перерисовывал полноэкранный слой каждый кадр (профиль: ~8с Painting, лаг
   артефактов). Теперь две диагональные аврора-полосы живут на этом ОТДЕЛЬНОМ
   оверсайз-слое и двигаются ТОЛЬКО transform'ом → собственный композитный слой,
   нулевой paint. Краевые vignette-свечения остались статичными на html-меше. */
/* Слой аврор СТАТИЧЕН. Любая анимация фона (background-position ИЛИ transform)
   под стеклянными backdrop-filter-панелями оболочки заставляет браузер
   пересчитывать блюр каждый кадр → лаг и срыв отрисовки (чёрные плитки, гост-
   эффект при переходах). Поэтому фон неподвижен: рисуется один раз и кешируется,
   нулевая покадровая нагрузка. Никаких will-change/animation здесь. */
body::after {
  content: "";
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  bottom: -120px;
  pointer-events: none;
  z-index: -2;
  background:
    linear-gradient(110deg,
      transparent 20%,
      rgba(var(--cb-aurora-1), 0.16) 42%,
      rgba(var(--cb-aurora-2), 0.20) 52%,
      transparent 78%),
    linear-gradient(250deg,
      transparent 25%,
      rgba(var(--cb-aurora-3), 0.16) 50%,
      transparent 78%);
  background-size: 130% 130%, 130% 130%;
  background-position: 0% 100%, 100% 0%;
  background-repeat: no-repeat;
}

/* === Слой 3: Scanlines (3 направления) === */
.cb-scanline {
  position: fixed;
  pointer-events: none;
  z-index: -1;
  opacity: 0;
  will-change: transform, opacity;
}

/* Горизонтальная лайм sweep ↓ */
.cb-scanline--h-down {
  left: 0; right: 0; top: 0;
  height: 1px;
  background: linear-gradient(90deg,
    transparent 0%,
    rgba(var(--cb-aurora-2), 0.15) 22%,
    rgba(var(--cb-aurora-1), 0.30) 50%,
    rgba(var(--cb-aurora-3), 0.15) 78%,
    transparent 100%);
  box-shadow: 0 0 8px rgba(var(--cb-aurora-1), 0.28);
  animation: cb-scanline-h-down 14s linear infinite;
}

/* Горизонтальная electric sweep ↑ */
.cb-scanline--h-up {
  left: 0; right: 0; top: 0;
  height: 1px;
  background: linear-gradient(90deg,
    transparent 0%,
    rgba(var(--cb-aurora-3), 0.10) 28%,
    rgba(var(--cb-aurora-2), 0.32) 50%,
    rgba(var(--cb-aurora-1), 0.12) 72%,
    transparent 100%);
  box-shadow: 0 0 6px rgba(var(--cb-aurora-2), 0.25);
  animation: cb-scanline-h-up 18s linear infinite;
  animation-delay: -6s;
}

/* Вертикальная magenta sweep → */
.cb-scanline--v-right {
  top: 0; bottom: 0; left: 0;
  width: 1px;
  background: linear-gradient(180deg,
    transparent 0%,
    rgba(var(--cb-aurora-2), 0.10) 22%,
    rgba(var(--cb-aurora-3), 0.28) 50%,
    rgba(var(--cb-aurora-1), 0.12) 78%,
    transparent 100%);
  box-shadow: 0 0 6px rgba(var(--cb-aurora-3), 0.25);
  animation: cb-scanline-v-right 22s linear infinite;
  animation-delay: -10s;
}

@keyframes cb-scanline-h-down {
  0%   { transform: translateY(-4vh); opacity: 0; }
  5%   { opacity: 0.55; }
  95%  { opacity: 0.55; }
  100% { transform: translateY(104vh); opacity: 0; }
}

@keyframes cb-scanline-h-up {
  0%   { transform: translateY(108vh); opacity: 0; }
  5%   { opacity: 0.45; }
  95%  { opacity: 0.45; }
  100% { transform: translateY(-4vh); opacity: 0; }
}

@keyframes cb-scanline-v-right {
  0%   { transform: translateX(-4vw); opacity: 0; }
  5%   { opacity: 0.40; }
  95%  { opacity: 0.40; }
  100% { transform: translateX(104vw); opacity: 0; }
}

/* === Слой 4: Particles — редкие пульсирующие точки === */
.cb-particle {
  position: fixed;
  width: 4px;
  height: 4px;
  border-radius: 50%;
  pointer-events: none;
  z-index: -1;
  opacity: 0;
  animation-name: cb-particle-pulse;
  animation-iteration-count: infinite;
  animation-timing-function: ease-in-out;
  will-change: opacity, transform;
}

.cb-particle--acid     { background: rgba(var(--cb-aurora-1), 0.75); box-shadow: 0 0 10px rgba(var(--cb-aurora-1), 0.80); }
.cb-particle--electric { background: rgba(var(--cb-aurora-2), 0.75); box-shadow: 0 0 10px rgba(var(--cb-aurora-2), 0.80); }
.cb-particle--magenta  { background: rgba(var(--cb-aurora-3), 0.75); box-shadow: 0 0 10px rgba(var(--cb-aurora-3), 0.80); }
.cb-particle--amber    { background: rgba(var(--cb-aurora-4), 0.65); box-shadow: 0 0 10px rgba(var(--cb-aurora-4), 0.70); }

@keyframes cb-particle-pulse {
  0%, 100% { opacity: 0;    transform: scale(0.5); }
  50%      { opacity: 0.65; transform: scale(1); }
}

/* === Отключение для special pages === */
body.no-cb-bg::before,
body.no-cb-bg::after { display: none; }
body.no-cb-bg .cb-scanline,
body.no-cb-bg .cb-particle { display: none; }

/* === Уважение к prefers-reduced-motion === */
@media (prefers-reduced-motion: reduce) {
  body::before,
  body::after { animation: none; }
  .cb-scanline,
  .cb-particle { display: none; }
}
