diff --git a/public/css/main.css b/public/css/main.css index 3f9002a..9849ee4 100644 --- a/public/css/main.css +++ b/public/css/main.css @@ -1349,6 +1349,27 @@ a.fab--secondary:visited { padding: calc(2 * clamp(2.5rem, 6vw, 4.5rem)) calc(2 * max(var(--space), env(safe-area-inset-right, 0px))) calc(2 * clamp(2rem, 5vw, 3.5rem)) calc(2 * max(var(--space), env(safe-area-inset-left, 0px))); } +/* Hero проявляется только после готовности фона-шэйдера (html.mesh-bg-ready), + чтобы текст не вскакивал раньше, чем подгрузится живой фон. */ +.hero { + opacity: 0; + transform: translateY(14px); + transition: opacity 0.8s ease 0.2s, transform 0.8s ease 0.2s; +} + +html.mesh-bg-ready .hero { + opacity: 1; + transform: none; +} + +@media (prefers-reduced-motion: reduce) { + .hero { + opacity: 1; + transform: none; + transition: none; + } +} + .hero__grid { max-width: var(--max); margin: 0 auto; diff --git a/public/js/modules/ui/page-mesh-shader.js b/public/js/modules/ui/page-mesh-shader.js index 2d4f8ea..1f1b88a 100644 --- a/public/js/modules/ui/page-mesh-shader.js +++ b/public/js/modules/ui/page-mesh-shader.js @@ -42,8 +42,8 @@ function colorVec4Array(colors) { export function initPageMeshShader() { const host = document.querySelector(".page-mesh-bg"); - if (!host) return; - if (prefersReducedMotion()) return; // статичный CSS-fallback остаётся виден + if (!host) return markMeshReady(); // нет хоста — hero всё равно проявляем + if (prefersReducedMotion()) return markMeshReady(); // статичный CSS-fallback, hero сразу виден const sizing = () => ({ u_fit: ShaderFitOptions[defaultObjectSizing.fit], @@ -86,10 +86,12 @@ export function initPageMeshShader() { try { mount.intersectionObserver?.disconnect(); } catch {} // Первый кадр уже отрисован в конструкторе — отмечаем готовность, - // чтобы CSS плавно проявил canvas (fade-in), без рывка. + // чтобы CSS плавно проявил canvas (fade-in) и hero. host.classList.add("is-ready"); + markMeshReady(); } catch (e) { console.warn("Mesh shader mount failed, fallback to static bg", e); + markMeshReady(); return; } @@ -110,3 +112,9 @@ export function initPageMeshShader() { try { mount.dispose(); } catch {} }, { once: true }); } + +// Сигнал готовности фона: проявляем hero (см. .hero в main.css). +// Ставится во всех ветках инициализации, чтобы hero никогда не висел скрытым. +function markMeshReady() { + document.documentElement.classList.add("mesh-bg-ready"); +}