Кодим
Меню в зеро блоке, которое исчезает и появляется при прокрутке вверх

1. При прокрутки вниз меню из зеро блока виднеется 200px, потом исчезает

2. При прокрутке вверх через 100px появляется и остается, но если пользователь опять начал скролить вниз, то исчезает через 200px

3. Замените ваш номер блока меню в зеро блоке (вначале кода)

4. Высота меню - блока 40Px

<style>
#rec1248699606 {
position: fixed !important;
top: 0; left: 0; right: 0;
width: 100%;
z-index: 9999;
transform: translateY(0);
transition: transform 0.35s ease;
will-change: transform;
}

/* Отступ для контента */
body.tilda-sticky-padding { padding-top: 40px !important; }

html, body { margin:0; }
</style>

<script>
document.addEventListener('DOMContentLoaded', function () {
const menu = document.querySelector('#rec1248699606');
if (!menu) return;

const HIDE_AFTER_DOWN_PX = 200;
const SHOW_AFTER_UP_PX = 100;
const NEAR_TOP_PX = 10;

let lastY = window.pageYOffset || 0;
let dir = 'up';
let acc = 0;
let hidden = false;
let ticking = false;

function applyPadding(visible, y) {
// padding добавляем только если не у самого верха
if (visible && y > NEAR_TOP_PX) {
document.body.classList.add('tilda-sticky-padding');
} else {
document.body.classList.remove('tilda-sticky-padding');
}
}

function showMenu(y) {
if (hidden) {
menu.style.transform = 'translateY(0)';
hidden = false;
}
applyPadding(true, y);
}

function hideMenu() {
if (!hidden) {
menu.style.transform = 'translateY(-100%)';
hidden = true;
}
applyPadding(false, window.pageYOffset || 0);
}

function onScroll() {
const y = window.pageYOffset || 0;
const delta = y - lastY;

if (y <= NEAR_TOP_PX) {
acc = 0; dir = 'up';
showMenu(y); // у самого верха меню видно
lastY = y; return;
}

if (delta > 0) { // вниз
if (dir !== 'down') { dir = 'down'; acc = 0; }
acc += delta;
if (acc >= HIDE_AFTER_DOWN_PX) hideMenu();
} else if (delta < 0) { // вверх
if (dir !== 'up') { dir = 'up'; acc = 0; }
acc += Math.abs(delta);
if (acc >= SHOW_AFTER_UP_PX) showMenu(y);
}

lastY = y;
}

window.addEventListener('scroll', function () {
if (!ticking) {
requestAnimationFrame(function () { onScroll(); ticking = false; });
ticking = true;
}
}, { passive: true });

// Стартовое состояние
showMenu(window.pageYOffset || 0);
});
</script>

Меняем id блоков
Код для автоскейла Tilda
<style>
/* Обёртка и бокс только для этого блока */
#rec1741810701 .as-shell{ position:relative; width:100%; }
#rec1741810701 .as-box{
position:absolute; left:50%; top:0;
transform-origin:0 0; will-change:transform,height;
}
/* медиа не тянут ширину — скейлим целиком */
#rec1741810701 .as-box img,
#rec1741810701 .as-box video,
#rec1741810701 .as-box canvas{
max-width:none !important;
height:auto;
}
</style>

<script>
(function(){
var recId = 'rec1741810701';

// базы как договорились
var BASE_DESKTOP = 1200;
var BASE_TABLET = 960;
var BASE_MOB_480 = 480;
var BASE_MOB_320 = 320;

var inited = false;
var baseH = null;
var currentBaseW = BASE_DESKTOP;

function pickBaseW(vw){
if(vw <= 360) return BASE_MOB_320;
if(vw <= 480) return BASE_MOB_480;
if(vw <= 992) return BASE_TABLET;
return BASE_DESKTOP;
}

function wrapAndMeasure(rec){
if(inited) return;
inited = true;

var shell = document.createElement('div');
shell.className = 'as-shell';

var box = document.createElement('div');
box.className = 'as-box';
shell.appendChild(box);

while(rec.firstChild){
box.appendChild(rec.firstChild);
}
rec.appendChild(shell);
rec.style.overflow = 'hidden';

currentBaseW = pickBaseW(getViewportW());
box.style.width = currentBaseW + 'px';

baseH = measureBaseH(box);
applyScale(rec, shell, box);

var debounced = debounce(function(){
applyScale(rec, shell, box);
}, 80);

window.addEventListener('resize', debounced, {passive:true});
window.addEventListener('orientationchange', debounced, {passive:true});

if('ResizeObserver' in window){
var ro = new ResizeObserver(function(){
applyScale(rec, shell, box);
});
ro.observe(rec);
ro.observe(box);
}

document.addEventListener('tilda:lazyload', debounced, {passive:true});
window.addEventListener('load', debounced, {passive:true});

if(document.fonts && document.fonts.ready){
document.fonts.ready.then(debounced).catch(function(){});
}

setTimeout(debounced, 150);
setTimeout(debounced, 500);
}

function measureBaseH(box){
var h = box.getBoundingClientRect().height || box.offsetHeight || 0;
return h || 600;
}

function getViewportW(){
return Math.min(
window.innerWidth || 0,
document.documentElement.clientWidth || 0
) || BASE_DESKTOP;
}

function applyScale(rec, shell, box){
var vw = getViewportW();
var nextBase = pickBaseW(vw);

if(nextBase !== currentBaseW){
currentBaseW = nextBase;
box.style.width = currentBaseW + 'px';
baseH = measureBaseH(box);
}

var rect = rec.getBoundingClientRect();
var cs = window.getComputedStyle(rec);
var padL = parseFloat(cs.paddingLeft) || 0;
var padR = parseFloat(cs.paddingRight) || 0;

var recW = (rect.width || rec.clientWidth || currentBaseW) - (padL + padR);
var availW = Math.max(1, Math.min(recW, vw));
var scale = availW / currentBaseW;

box.style.height = baseH + 'px';
box.style.transform =
'translateX(' + (-(currentBaseW * scale / 2)) + 'px) scale(' + scale + ')';

shell.style.height = (baseH * scale) + 'px';
}

function debounce(fn, ms){
var t;
return function(){
clearTimeout(t);
t = setTimeout(fn, ms);
};
}

function init(){
var rec = document.getElementById(recId);
if(!rec || rec.dataset.asInit === '1') return;
rec.dataset.asInit = '1';
wrapAndMeasure(rec);
}

if(document.readyState === 'loading'){
document.addEventListener('DOMContentLoaded', init, {once:true});
} else {
init();
}

document.addEventListener('tilda:changedata', init, {passive:true});
})();
</script>

Меняем id блоков
Скролл в сторону
<style>
/* Мобилка — нативный горизонтальный скролл с инерцией */
#rec1484817681 .sbx-viewport{
cursor: grab;
touch-action: auto; /* вертикальные жесты — странице */
}
#rec1484817681 .sbx-viewport:active{ cursor: grabbing; }

/* Артборд скроллится по X нативно */
#rec1484817681 .t396__artboard.sbx-viewport{
overflow-x: auto;
overflow-y: hidden;
-webkit-overflow-scrolling: touch; /* iOS инерция */
scroll-behavior: smooth; /* для стрелок/программного скролла */
}

/* Прячем полосы прокрутки */
#rec1484817681 .t396__artboard{
scrollbar-width: none;
-ms-overflow-style: none;
}
#rec1484817681 .t396__artboard::-webkit-scrollbar{ display: none; }

/* Невидимый растяжитель ширины (не перекрывает клики) */
#rec1484817681 .sbx-spacer{
position: relative;
display: block;
width: 0;
height: 1px;
pointer-events: none;
}

/* «Пальчик» для стрелок */
.left, .right { cursor: pointer; }

/* Во время desktop-drag — хваталка и без выделения текста */
#rec1484817681 .sbx-viewport.is-dragging{
cursor: grabbing;
user-select: none;
}
</style>

<script>
(function(){
var blockId = '#rec1484817681';

var initInt = setInterval(function(){
var artboard = document.querySelector(blockId + ' .t396__artboard');
if(!artboard) return;
clearInterval(initInt);

/* 1) Помечаем артборд как вьюпорт (ничего не оборачиваем) */
if (!artboard.classList.contains('sbx-viewport')) {
artboard.classList.add('sbx-viewport');
}

/* 2) Spacer для ширины (автоскейл Тильды не трогаем) */
var spacer = artboard.querySelector('.sbx-spacer');
if (!spacer){
spacer = document.createElement('div');
spacer.className = 'sbx-spacer';
artboard.appendChild(spacer);
}

/* 3) Замер самой правой точки контента */
function measureScrollWidth(){
var elems = artboard.querySelectorAll('.t396__elem, .t396__group');
var p = artboard.getBoundingClientRect();
var maxRight = 0;

elems.forEach(function(el){
var r = el.getBoundingClientRect();
var left = r.left - p.left;
var right = left + r.width;
if (right > maxRight) maxRight = right;
});

var pad = 40;
var width = Math.ceil(Math.max(maxRight + pad, artboard.clientWidth));
spacer.style.width = width + 'px';
}

/* 4) Ререндер Zero */
function tildaRerender(){
var root = document.querySelector(blockId);
if (!root) return;
if (typeof t396_allgroups__renderView === 'function') t396_allgroups__renderView(root);
if (typeof t396_allelems__renderView === 'function') t396_allelems__renderView(root);
var id = blockId.replace('#rec','');
if (typeof t396_doResize === 'function') t396_doResize(id);
artboard.classList.remove('t396__artboard-fixed-no-bg');
}

/* 5) Хелперы */
function getCurrentX(){ return artboard.scrollLeft || 0; }
function getMaxScroll(){ return Math.max(0, artboard.scrollWidth - artboard.clientWidth); }
function clamp(v,min,max){ return Math.min(Math.max(v,min),max); }

/* 6) Стрелки (.left/.right) — делегирование */
function smoothScrollToX(targetX){
targetX = clamp(targetX, 0, getMaxScroll());
if ('scrollBehavior' in document.documentElement.style){
artboard.scrollTo({ left: targetX, top: 0, behavior: 'smooth' });
return;
}
var start = getCurrentX();
var dist = targetX - start;
var startTime = null, duration = 350;
function step(ts){
if (!startTime) startTime = ts;
var t = (ts - startTime) / duration; if (t > 1) t = 1;
var ease = t < .5 ? 2*t*t : -1 + (4 - 2*t)*t;
artboard.scrollLeft = start + dist*ease;
if (t < 1) requestAnimationFrame(step);
}
requestAnimationFrame(step);
}

document.addEventListener('click', function(e){
var leftBtn = e.target.closest && e.target.closest('.left');
var rightBtn = e.target.closest && e.target.closest('.right');
if (!leftBtn && !rightBtn) return;
e.preventDefault();
var shift = clamp(Math.floor(artboard.clientWidth * 0.8), 240, 800);
if (leftBtn) smoothScrollToX(getCurrentX() - shift);
if (rightBtn) smoothScrollToX(getCurrentX() + shift);
}, true);

/* 7) Desktop drag: ровный + мягкая инерция.
+ ФИКС КЛИКА: если НЕ тащили — вручную открываем ссылку под курсором */
function isTouchEnv(){
return (('maxTouchPoints' in navigator && navigator.maxTouchPoints > 0) ||
(window.matchMedia && matchMedia('(pointer:coarse)').matches));
}
(function enableDesktopDrag(){
if (isTouchEnv()) return; // только десктоп

var dragging = false, moved = false;
var startX = 0, startLeft = 0;
var lastX = 0, lastT = 0, velocity = 0; // px/ms
var rafId = null;
var downTarget = null; // элемент под курсором на pointerdown

/* Тюн “докатки” */
var DRAG_THRESH = 12; // высокий порог — не трогаем обычные клики
var FRICTION = 0.96; // мягкая длинная докатка
var MIN_V = 0.010;
var MAX_STEP = 60;
var savedSmooth = '';

function stopMomentum(){
if (rafId){ cancelAnimationFrame(rafId); rafId = null; }
velocity = 0;
}

function momentum(){
stopMomentum();
var prev = performance.now();
function frame(now){
var dt = Math.min(now - prev, MAX_STEP); prev = now;
artboard.scrollLeft = clamp(artboard.scrollLeft - velocity * dt, 0, getMaxScroll());
velocity *= Math.pow(FRICTION, dt / 16.67);
if (Math.abs(velocity) < MIN_V) { stopMomentum(); return; }
if (artboard.scrollLeft <= 0 || artboard.scrollLeft >= getMaxScroll()) velocity *= 0.5;
rafId = requestAnimationFrame(frame);
}
rafId = requestAnimationFrame(frame);
}

artboard.addEventListener('pointerdown', function(e){
if (e.pointerType !== 'mouse' || e.button !== 0) return; // только ЛКМ мыши
dragging = true; moved = false;
downTarget = e.target;

stopMomentum();
savedSmooth = artboard.style.scrollBehavior;
artboard.style.scrollBehavior = 'auto';

startX = e.clientX; startLeft = artboard.scrollLeft;
lastX = e.clientX; lastT = performance.now(); velocity = 0;

artboard.classList.add('is-dragging');
artboard.setPointerCapture(e.pointerId);
/* важно: НЕ делаем preventDefault — клики не ломаем */
});

artboard.addEventListener('pointermove', function(e){
if (!dragging) return;
var dx = e.clientX - startX;
if (Math.abs(dx) >= DRAG_THRESH) moved = true;

artboard.scrollLeft = clamp(startLeft - dx, 0, getMaxScroll());

var now = performance.now(), dt = now - lastT;
if (dt > 0){
var vx = (e.clientX - lastX) / dt; // px/ms
velocity = velocity * 0.85 + vx * 0.15;
}
lastX = e.clientX; lastT = now;

if (moved) e.preventDefault(); // во время реального drag — без выделения текста
});

function finish(e){
if (!dragging) return;
dragging = false;

artboard.releasePointerCapture(e.pointerId);
artboard.classList.remove('is-dragging');
artboard.style.scrollBehavior = savedSmooth || 'smooth';

if (moved && Math.abs(velocity) > MIN_V) momentum(); else stopMomentum();

/* --- КЛЮЧЕВОЙ ФИКС КЛИКА ---
Если НЕ было drag (moved == false), вручную открываем ссылку под курсором.
Это пробивает ситуации, когда где-то вверх по дереву кто-то глушит click. */
if (!moved) {
var link = downTarget && downTarget.closest && downTarget.closest('a[href]');
if (link) {
// уважаем target и модификаторы
var href = link.getAttribute('href');
var target = link.getAttribute('target');
var openBlank = target === '_blank' || e.ctrlKey || e.metaKey || e.shiftKey || e.altKey;
if (openBlank) {
window.open(href, '_blank');
} else {
window.location.href = href;
}
}
}

downTarget = null;
}

artboard.addEventListener('pointerup', finish);
artboard.addEventListener('pointercancel', finish);

/* Любое новое действие — останавливает инерцию */
['wheel','keydown','mousedown'].forEach(function(ev){
artboard.addEventListener(ev, stopMomentum, { passive: true });
});
})();

/* 8) Инициализация + реинит */
measureScrollWidth();
tildaRerender();
setTimeout(function(){
measureScrollWidth();
tildaRerender();
}, 400);

var rez;
window.addEventListener('resize', function(){
clearTimeout(rez);
rez = setTimeout(function(){
measureScrollWidth();
tildaRerender();
}, 150);
});

/* Подправка line-height (если доступно) */
setTimeout(function(){
if (typeof t396_elem_fixLineHeight === 'function'){
artboard.querySelectorAll('.tn-elem[data-elem-type="text"]').forEach(function(el){
t396_elem_fixLineHeight(el);
});
}
}, 1200);
}, 50);

setTimeout(function(){ clearInterval(initInt); }, 8000);
})();
</script>

Версия сайта для слабовидящих
<!-- ===== ПАНЕЛЬ ДЛЯ СЛАБОВИДЯЩИХ • АКТУАЛЬНЫЙ КОД ===== -->

<style>
.vi-toolbar-wrap{
position:fixed;
top:0;
left:0;
right:0;
z-index:9999;
}
.vi-toolbar{
max-width:1160px;
margin:0 auto;
padding:10px 16px;
border:1px solid #000;
font-family:Arial,system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif;
font-size:16px;
display:flex;
flex-wrap:wrap;
gap:24px;
align-items:center;
justify-content:space-between;
background:#ffffff;
}
.vi-toolbar__group{
display:flex;
align-items:center;
gap:12px;
flex-wrap:wrap;
}
.vi-toolbar__label{
font-weight:700;
text-transform:uppercase;
letter-spacing:0.04em;
font-size:14px;
}
.vi-toolbar__value{
font-weight:700;
margin-left:4px;
}
.vi-font-btn{
min-width:44px;
height:32px;
padding:0 10px;
border-radius:4px;
border:1px solid #000;
background:#fff;
cursor:pointer;
font-size:14px;
}
.vi-font-btn:hover{background:#f3f3f3;}

.vi-color-btn{
width:32px;
height:32px;
border-radius:4px;
border:1px solid #000;
cursor:pointer;
display:inline-flex;
align-items:center;
justify-content:center;
font-size:14px;
font-weight:700;
}
.vi-color-btn span{
border-bottom:1px solid currentColor;
}
.vi-color-btn--active{
outline:3px solid #ff9800;
outline-offset:2px;
}

.vi-color-default{background:#ffffff;color:#000000 !important;}
.vi-color-bw {background:#000000;color:#ffffff !important;}
.vi-color-beige {background:#f5edc7;color:#000000 !important;}

.vi-normal-btn{
padding:6px 14px;
border-radius:4px;
border:1px solid #000;
background:#ffffff;
text-decoration:none !important;
font-size:14px;
cursor:pointer;
white-space:nowrap;
color:#000000 !important;
display:inline-flex;
align-items:center;
gap:6px;
}
.vi-normal-btn:hover{background:#f3f3f3;}

.vi-eye-wrap{
position:relative;
width:16px;
height:16px;
display:inline-block;
}
.vi-eye{
position:absolute;
inset:0;
width:16px;
height:16px;
}
.vi-eye-dark{display:block;}
.vi-eye-light{display:none;}

@media(max-width:767px){
.vi-toolbar{
flex-direction:column;
align-items:flex-start;
gap:12px;
}
}

body.vi-has-toolbar{padding-top:80px;}

html.vi-on{}

/* ========= ЦВЕТОВЫЕ СХЕМЫ ДЛЯ СТРАНИЦЫ И ПАНЕЛИ ========= */

/* Базовая белая тема */
html.vi-on.vi-theme-default .vi-toolbar{
background:#ffffff;
color:#000000;
border-color:#000000;
}

/* ЧЁРНАЯ ТЕМА */

html.vi-on.vi-theme-bw body{
background:#000000 !important;
color:#ffffff !important;
}

html.vi-on.vi-theme-bw .t-rec,
html.vi-on.vi-theme-bw .t-section{
background-color:#000000 !important;
}

html.vi-on.vi-theme-bw .t396__artboard,
html.vi-on.vi-theme-bw .t396__carrier,
html.vi-on.vi-theme-bw .t396__filter{
background-color:#000000 !important;
}

html.vi-on.vi-theme-bw .vi-toolbar{
background:#000000;
color:#ffffff;
border-color:#ffffff;
}

html.vi-on.vi-theme-bw p,
html.vi-on.vi-theme-bw li,
html.vi-on.vi-theme-bw span,
html.vi-on.vi-theme-bw h1,
html.vi-on.vi-theme-bw h2,
html.vi-on.vi-theme-bw h3,
html.vi-on.vi-theme-bw h4,
html.vi-on.vi-theme-bw h5,
html.vi-on.vi-theme-bw h6,
html.vi-on.vi-theme-bw .t-text,
html.vi-on.vi-theme-bw .t-descr,
html.vi-on.vi-theme-bw .t-title,
html.vi-on.vi-theme-bw .t-name,
html.vi-on.vi-theme-bw .t-uptitle,
html.vi-on.vi-theme-bw .tn-atom:not([data-elem-type="button"]){
color:#ffffff !important;
}

/* ссылки, кроме кнопки "обычная версия" */
html.vi-on.vi-theme-bw a:not(.vi-normal-btn){
color:#ffffff !important;
text-decoration:underline !important;
}

/* БЕЖЕВАЯ ТЕМА */

html.vi-on.vi-theme-beige body{
background:#f5edc7 !important;
color:#000000 !important;
}

html.vi-on.vi-theme-beige .t-rec,
html.vi-on.vi-theme-beige .t-section{
background-color:#f5edc7 !important;
}

html.vi-on.vi-theme-beige .t396__artboard,
html.vi-on.vi-theme-beige .t396__carrier,
html.vi-on.vi-theme-beige .t396__filter{
background-color:#f5edc7 !important;
}

html.vi-on.vi-theme-beige .vi-toolbar{
background:#f5edc7;
color:#000000;
border-color:#000000;
}

html.vi-on.vi-theme-beige p,
html.vi-on.vi-theme-beige li,
html.vi-on.vi-theme-beige span,
html.vi-on.vi-theme-beige h1,
html.vi-on.vi-theme-beige h2,
html.vi-on.vi-theme-beige h3,
html.vi-on.vi-theme-beige h4,
html.vi-on.vi-theme-beige h5,
html.vi-on.vi-theme-beige h6,
html.vi-on.vi-theme-beige .t-text,
html.vi-on.vi-theme-beige .t-descr,
html.vi-on.vi-theme-beige .t-title,
html.vi-on.vi-theme-beige .t-name,
html.vi-on.vi-theme-beige .t-uptitle,
html.vi-on.vi-theme-beige .tn-atom:not([data-elem-type="button"]){
color:#000000 !important;
}

html.vi-on.vi-theme-beige a:not(.vi-normal-btn){
color:#000000 !important;
text-decoration:underline !important;
}

/* ===== КНОПКИ С КЛАССОМ js-vi-btn (контейнер формы) ===== */
/* ВАЖНО: красим только настоящую кнопку внутри контейнера,
чтобы не красить всю форму целиком */

/* Zero-блок кнопка / обычные кнопки */
html.vi-on.vi-theme-bw .js-vi-btn .tn-atom[data-elem-type="button"],
html.vi-on.vi-theme-bw .js-vi-btn .t-submit,
html.vi-on.vi-theme-bw .js-vi-btn .t-btn{
background-color:#ffffff !important;
color:#000000 !important;
border-color:#ffffff !important;
box-shadow:none !important;
border-radius:9999px !important;
}
html.vi-on.vi-theme-bw .js-vi-btn .tn-atom[data-elem-type="button"] a,
html.vi-on.vi-theme-bw .js-vi-btn .t-submit a,
html.vi-on.vi-theme-bw .js-vi-btn .t-btn a{
color:#000000 !important;
text-decoration:none !important;
}

html.vi-on.vi-theme-beige .js-vi-btn .tn-atom[data-elem-type="button"],
html.vi-on.vi-theme-beige .js-vi-btn .t-submit,
html.vi-on.vi-theme-beige .js-vi-btn .t-btn{
background-color:#000000 !important;
color:#ffffff !important;
border-color:#000000 !important;
box-shadow:none !important;
border-radius:9999px !important;
}
html.vi-on.vi-theme-beige .js-vi-btn .tn-atom[data-elem-type="button"] a,
html.vi-on.vi-theme-beige .js-vi-btn .t-submit a,
html.vi-on.vi-theme-beige .js-vi-btn .t-btn a{
color:#ffffff !important;
text-decoration:none !important;
}

/* === ШЕЙПЫ / КАРТОЧКИ С ОБВОДКОЙ В ZERO (border) === */

html.vi-on.vi-theme-bw .tn-atom[data-elem-type="shape"],
html.vi-on.vi-theme-bw .tn-atom[data-elem-type="shape"] *{
border-color:#ffffff !important;
}

/* === ПОПРАВКИ ПАНЕЛИ ДЛЯ ЦВЕТА ТЕКСТА === */

/* Буква "Ц" на кнопках панели всегда контрастная */
html.vi-on .vi-color-default span{color:#000000 !important;}
html.vi-on .vi-color-bw span {color:#ffffff !important;}
html.vi-on .vi-color-beige span {color:#000000 !important;}

/* "Обычная версия сайта" — контрастная и без подчёркивания */
html.vi-on.vi-theme-bw .vi-normal-btn{
background:#ffffff !important;
color:#000000 !important;
border-color:#ffffff !important;
text-decoration:none !important;
}
html.vi-on.vi-theme-beige .vi-normal-btn{
background:#000000 !important;
color:#ffffff !important;
border-color:#000000 !important;
text-decoration:none !important;
}

/* Глазики: в бежевой теме показываем белый svg */
html.vi-on.vi-theme-beige .vi-eye-dark{display:none;}
html.vi-on.vi-theme-beige .vi-eye-light{display:block;}

/* убираем анимации/переходы на странице в версии для слабовидящих */
html.vi-on *{
animation:none !important;
transition:none !important;
}
</style>

<!-- ===== САМА ПАНЕЛЬ ===== -->
<div class="vi-toolbar-wrap">
<div class="vi-toolbar" aria-label="Настройки версии для слабовидящих">
<div class="vi-toolbar__group">
<span class="vi-toolbar__label">
РАЗМЕР ШРИФТА
<span class="vi-toolbar__value" id="vi-font-value">16 px</span>
</span>
<button type="button" class="vi-font-btn" data-vi-font="base">A</button>
<button type="button" class="vi-font-btn" data-vi-font="big">A+</button>
</div>

<div class="vi-toolbar__group">
<span class="vi-toolbar__label">ЦВЕТА САЙТА</span>
<button type="button"
class="vi-color-btn vi-color-default"
data-vi-theme="default"><span>Ц</span></button>
<button type="button"
class="vi-color-btn vi-color-bw"
data-vi-theme="bw"><span>Ц</span></button>
<button type="button"
class="vi-color-btn vi-color-beige"
data-vi-theme="beige"><span>Ц</span></button>
</div>

<div class="vi-toolbar__group">
<a href="/" class="vi-normal-btn">
<span class="vi-eye-wrap">
<!-- тёмный глазик (по умолчанию / в чёрной теме) -->
<img class="vi-eye vi-eye-dark"
src="https://static.tildacdn.com/tild6132-3832-4261-b034-333339626365/eye-slash.svg"
alt="">
<!-- белый глазик для бежевой темы -->
<img class="vi-eye vi-eye-light"
src="https://static.tildacdn.com/tild3635-3735-4035-b962-353836333738/eye-slash_1.svg"
alt="">
</span>
Обычная версия сайта
</a>
</div>
</div>
</div>

<script>
(function(){
const root = document.documentElement;
const body = document.body;
const STORAGE_KEY = 'vi_panel_settings_v_final3';

/* ДВА уровня размера шрифта: 0 — базовый, 1 — увеличенный */
const LEVELS = [1, 1.25];

const TEXT_SELECTORS =
'p, li, h1, h2, h3, h4, h5, h6,'+
'.t-text, .t-descr, .t-title, .t-name, .t-uptitle,'+
'.tn-atom';

let state = {
baseBodySize: 16,
levelIndex: 0, // 0 — базовый, 1 — увеличенный
theme: 'default' // default | bw | beige
};

function detectBaseBodySize(){
const cs = window.getComputedStyle(body);
const size = parseFloat(cs.fontSize);
return size || 16;
}

function loadSettings(){
try{
const saved = localStorage.getItem(STORAGE_KEY);
if(saved){
const p = JSON.parse(saved);
if(p && typeof p === 'object'){
if(p.baseBodySize) state.baseBodySize = p.baseBodySize;
if(typeof p.levelIndex === 'number') state.levelIndex = p.levelIndex;
if(p.theme) state.theme = p.theme;
}
}
}catch(e){}
}

function saveSettings(){
try{
localStorage.setItem(STORAGE_KEY, JSON.stringify(state));
}catch(e){}
}

function applyFontSize(){
const k = LEVELS[state.levelIndex] || 1;
const els = document.querySelectorAll(TEXT_SELECTORS);

els.forEach(function(el){
if(el.closest('.vi-toolbar')) return;

if(!el.dataset.viBaseSize){
const cs = window.getComputedStyle(el);
const base = parseFloat(cs.fontSize);
if(!base || isNaN(base)) return;
el.dataset.viBaseSize = String(base);
}
const base = parseFloat(el.dataset.viBaseSize);
if(!base || isNaN(base)) return;

const newSize = base * k;
el.style.fontSize = newSize + 'px';
});

const val = document.getElementById('vi-font-value');
if(val){
const current = state.baseBodySize * k;
val.textContent = Math.round(current) + ' px';
}
}

function applyTheme(){
root.classList.add('vi-on');
body.classList.add('vi-has-toolbar');

root.classList.remove('vi-theme-default','vi-theme-bw','vi-theme-beige');
root.classList.add('vi-theme-' + state.theme);

document.querySelectorAll('.vi-color-btn').forEach(function(btn){
const theme = btn.getAttribute('data-vi-theme');
btn.classList.toggle('vi-color-btn--active', theme === state.theme);
});
}

function applyAll(){
applyTheme();
applyFontSize();
}

/* Инициализация */
state.baseBodySize = detectBaseBodySize();
loadSettings();
if(state.levelIndex < 0 || state.levelIndex > LEVELS.length-1) state.levelIndex = 0;

applyAll();
saveSettings();

/* Кнопки размера */
document.querySelectorAll('.vi-font-btn').forEach(function(btn){
btn.addEventListener('click', function(){
const mode = this.getAttribute('data-vi-font');
state.levelIndex = (mode === 'big') ? 1 : 0; // только 2 состояния
applyFontSize();
saveSettings();
});
});

/* Кнопки цвета */
document.querySelectorAll('.vi-color-btn').forEach(function(btn){
btn.addEventListener('click', function(){
const theme = this.getAttribute('data-vi-theme') || 'default';
state.theme = theme;
applyTheme();
saveSettings();
});
});

})();
</script>

Фильтр товаров, вместо тильдавского
<!-- КАСТОМНЫЙ ФИЛЬТР ГРАЦИЯ, УПРАВЛЯЮЩИЙ ВСТРОЕННЫМ ФИЛЬТРОМ ТИЛЬДЫ -->

<style>
.grazia-filter {
font-family: "Raleway", system-ui, -apple-system, BlinkMacSystemFont, sans-serif;
color: #4A2130;
max-width: 280px;
position: relative;
z-index: 5;
}

/* Блок магазина чуть ниже по z-index (ID каталога!) */
#rec1571386751 {
position: relative;
z-index: 1;
}

/* СКРЫВАЕМ ШТАТНЫЙ ФИЛЬТР ТИЛЬДЫ ВНУТРИ #rec1571386751 */
#rec1571386751 .t-store__filter-col,
#rec1571386751 .t-store__filter,
#rec1571386751 .t-store__filter__wrapper,
#rec1571386751 .t-store__filter-wrapper {
max-height: 0 !important;
overflow: hidden !important;
opacity: 0 !important;
pointer-events: none !important;
margin: 0 !important;
padding: 0 !important;
border: 0 !important;
}

/* ==== ДЕСКТОПНЫЙ ВИД ==== */

.grazia-filter__group {
margin-bottom: 16px;
border-radius: 16px;
background: #ECC4CE;
overflow: hidden;
}

.grazia-filter__head {
width: 100%;
padding: 14px 20px;
border: none;
background: #ECC4CE;
display: flex;
align-items: center;
justify-content: space-between;
cursor: pointer;
}

.grazia-filter__title {
font-weight: 700;
font-size: 18px;
color: #4A2130;
}

.grazia-filter__icon {
position: relative;
width: 18px;
height: 18px;
flex: 0 0 18px;
display: flex;
align-items: center;
justify-content: center;
transition: transform 0.2s ease;
}

.grazia-filter__icon::before {
content: "";
width: 0;
height: 0;
border-left: 5px solid transparent;
border-right: 5px solid transparent;
border-top: 6px solid #4A2130;
}

.grazia-filter__group.is-open .grazia-filter__icon {
transform: rotate(180deg);
}

.grazia-filter__body {
padding: 10px 20px 14px;
border-top: 1px solid rgba(74, 33, 48, 0.08);
display: none;
}

.grazia-filter__group.is-open .grazia-filter__body {
display: block;
}

.filter-option {
display: flex;
align-items: center;
gap: 8px;
margin: 3px 0;
cursor: pointer;
}

.filter-option input {
position: absolute;
opacity: 0;
pointer-events: none;
}

.filter-option__box {
width: 16px;
height: 16px;
border-radius: 3px;
border: 1px solid #4A2130;
box-sizing: border-box;
position: relative;
flex: 0 0 16px;
}

.filter-option input:checked + .filter-option__box::after {
content: "";
position: absolute;
inset: 3px;
background: #4A2130;
}

.filter-option__label {
font-weight: 400;
font-size: 14px;
line-height: 1.4;
color: rgba(74, 33, 48, 0.8);
}

/* ===== МОБИЛЬНЫЙ ВИД В СТИЛЕ АККОРДЕОНОВ ===== */
@media screen and (max-width: 640px) {

.grazia-filter {
max-width: 100%;
width: 100%;
padding: 0 16px 20px;
box-sizing: border-box;
}

.grazia-filter__group {
background: transparent;
margin-bottom: 12px;
border-radius: 0;
overflow: visible;
}

.grazia-filter__head {
background: #ECC4CE;
color: #4A2130;
border-radius: 18px;
padding: 14px 18px;
box-shadow: 0 8px 20px rgba(0,0,0,0.08);
}

.grazia-filter__title {
color: #4A2130;
font-size: 15px;
}

.grazia-filter__icon::before {
border-top-color: #4A2130;
}

.grazia-filter__body {
margin-top: 8px;
padding: 12px 16px 14px;
border-radius: 16px;
background: #F8F5F0;
box-shadow: 0 6px 16px rgba(0,0,0,0.06);
display: none;
}

.grazia-filter__group.is-open .grazia-filter__body {
display: block;
}

.filter-option {
margin: 6px 0;
gap: 10px;
}

.filter-option__box {
width: 18px;
height: 18px;
border-radius: 4px;
}

.filter-option input:checked + .filter-option__box::after {
inset: 3px;
}

.filter-option__label {
font-size: 15px;
color: #4A2130;
}
}
</style>

<div class="grazia-filter">
<!-- Тип (на десктопе открыта по умолчанию) -->
<div class="grazia-filter__group is-open">
<button class="grazia-filter__head" type="button">
<span class="grazia-filter__title">Тип</span>
<span class="grazia-filter__icon"></span>
</button>
<div class="grazia-filter__body">
<label class="filter-option">
<input type="checkbox" class="js-filter" data-group="type" value="pi">
<span class="filter-option__box"></span>
<span class="filter-option__label">Пионы</span>
</label>
<label class="filter-option">
<input type="checkbox" class="js-filter" data-group="type" value="ro">
<span class="filter-option__box"></span>
<span class="filter-option__label">Розы</span>
</label>
<label class="filter-option">
<input type="checkbox" class="js-filter" data-group="type" value="bu">
<span class="filter-option__box"></span>
<span class="filter-option__label">Букеты</span>
</label>
<label class="filter-option">
<input type="checkbox" class="js-filter" data-group="type" value="mo">
<span class="filter-option__box"></span>
<span class="filter-option__label">Монобукеты</span>
</label>
<label class="filter-option">
<input type="checkbox" class="js-filter" data-group="type" value="ko">
<span class="filter-option__box"></span>
<span class="filter-option__label">Корзинки</span>
</label>
<label class="filter-option">
<input type="checkbox" class="js-filter" data-group="type" value="sk">
<span class="filter-option__box"></span>
<span class="filter-option__label">Шляпные коробки</span>
</label>
<label class="filter-option">
<input type="checkbox" class="js-filter" data-group="type" value="ne">
<span class="filter-option__box"></span>
<span class="filter-option__label">Невесте</span>
</label>
<label class="filter-option">
<input type="checkbox" class="js-filter" data-group="type" value="of">
<span class="filter-option__box"></span>
<span class="filter-option__label">Оформление</span>
</label>
<label class="filter-option">
<input type="checkbox" class="js-filter" data-group="type" value="po">
<span class="filter-option__box"></span>
<span class="filter-option__label">Подарки</span>
</label>
<label class="filter-option">
<input type="checkbox" class="js-filter" data-group="type" value="ng">
<span class="filter-option__box"></span>
<span class="filter-option__label">Новый год</span>
</label>
<label class="filter-option">
<input type="checkbox" class="js-filter" data-group="type" value="dr">
<span class="filter-option__box"></span>
<span class="filter-option__label">День рождения</span>
</label>
<label class="filter-option">
<input type="checkbox" class="js-filter" data-group="type" value="ra">
<span class="filter-option__box"></span>
<span class="filter-option__label">Ранункулюсы</span>
</label>
<label class="filter-option">
<input type="checkbox" class="js-filter" data-group="type" value="gc">
<span class="filter-option__box"></span>
<span class="filter-option__label">Горшечные цветы</span>
</label>
</div>
</div>

<!-- Повод -->
<div class="grazia-filter__group">
<button class="grazia-filter__head" type="button">
<span class="grazia-filter__title">Повод</span>
<span class="grazia-filter__icon"></span>
</button>
<div class="grazia-filter__body">
<label class="filter-option">
<input type="checkbox" class="js-filter" data-group="occasion" value="go">
<span class="filter-option__box"></span>
<span class="filter-option__label">Годовщина</span>
</label>
<label class="filter-option">
<input type="checkbox" class="js-filter" data-group="occasion" value="dr">
<span class="filter-option__box"></span>
<span class="filter-option__label">День рождения</span>
</label>
<label class="filter-option">
<input type="checkbox" class="js-filter" data-group="occasion" value="cd">
<span class="filter-option__box"></span>
<span class="filter-option__label">Цветы для дома</span>
</label>
<label class="filter-option">
<input type="checkbox" class="js-filter" data-group="occasion" value="np">
<span class="filter-option__box"></span>
<span class="filter-option__label">На праздник</span>
</label>
<label class="filter-option">
<input type="checkbox" class="js-filter" data-group="occasion" value="sv">
<span class="filter-option__box"></span>
<span class="filter-option__label">Свадьба</span>
</label>
<label class="filter-option">
<input type="checkbox" class="js-filter" data-group="occasion" value="sd">
<span class="filter-option__box"></span>
<span class="filter-option__label">Свидание</span>
</label>
<label class="filter-option">
<input type="checkbox" class="js-filter" data-group="occasion" value="dn">
<span class="filter-option__box"></span>
<span class="filter-option__label">Для настроения</span>
</label>
</div>
</div>

<!-- Цветок -->
<div class="grazia-filter__group">
<button class="grazia-filter__head" type="button">
<span class="grazia-filter__title">Цветок</span>
<span class="grazia-filter__icon"></span>
</button>
<div class="grazia-filter__body">
<label class="filter-option">
<input type="checkbox" class="js-filter" data-group="flower" value="al">
<span class="filter-option__box"></span>
<span class="filter-option__label">Альстромерия</span>
</label>
<label class="filter-option">
<input type="checkbox" class="js-filter" data-group="flower" value="ge">
<span class="filter-option__box"></span>
<span class="filter-option__label">Георгин</span>
</label>
<label class="filter-option">
<input type="checkbox" class="js-filter" data-group="flower" value="gi">
<span class="filter-option__box"></span>
<span class="filter-option__label">Гиацинт</span>
</label>
<label class="filter-option">
<input type="checkbox" class="js-filter" data-group="flower" value="gp">
<span class="filter-option__box"></span>
<span class="filter-option__label">Гипсофила</span>
</label>
<label class="filter-option">
<input type="checkbox" class="js-filter" data-group="flower" value="go">
<span class="filter-option__box"></span>
<span class="filter-option__label">Гортензия</span>
</label>
<label class="filter-option">
<input type="checkbox" class="js-filter" data-group="flower" value="de">
<span class="filter-option__box"></span>
<span class="filter-option__label">Дельфиниум</span>
</label>
<label class="filter-option">
<input type="checkbox" class="js-filter" data-group="flower" value="di">
<span class="filter-option__box"></span>
<span class="filter-option__label">Диантус</span>
</label>
<label class="filter-option">
<input type="checkbox" class="js-filter" data-group="flower" value="ir">
<span class="filter-option__box"></span>
<span class="filter-option__label">Ирис</span>
</label>
<label class="filter-option">
<input type="checkbox" class="js-filter" data-group="flower" value="mm">
<span class="filter-option__box"></span>
<span class="filter-option__label">Мимоза</span>
</label>
<label class="filter-option">
<input type="checkbox" class="js-filter" data-group="flower" value="or">
<span class="filter-option__box"></span>
<span class="filter-option__label">Орхидея</span>
</label>
<label class="filter-option">
<input type="checkbox" class="js-filter" data-group="flower" value="pi">
<span class="filter-option__box"></span>
<span class="filter-option__label">Пион</span>
</label>
<label class="filter-option">
<input type="checkbox" class="js-filter" data-group="flower" value="pr">
<span class="filter-option__box"></span>
<span class="filter-option__label">Пионовидная роза</span>
</label>
<label class="filter-option">
<input type="checkbox" class="js-filter" data-group="flower" value="ps">
<span class="filter-option__box"></span>
<span class="filter-option__label">Подсолнух</span>
</label>
<label class="filter-option">
<input type="checkbox" class="js-filter" data-group="flower" value="ra">
<span class="filter-option__box"></span>
<span class="filter-option__label">Ранункулюс</span>
</label>
<label class="filter-option">
<input type="checkbox" class="js-filter" data-group="flower" value="ro">
<span class="filter-option__box"></span>
<span class="filter-option__label">Роза</span>
</label>
<label class="filter-option">
<input type="checkbox" class="js-filter" data-group="flower" value="sr">
<span class="filter-option__box"></span>
<span class="filter-option__label">Сирень</span>
</label>
<label class="filter-option">
<input type="checkbox" class="js-filter" data-group="flower" value="tu">
<span class="filter-option__box"></span>
<span class="filter-option__label">Тюльпан</span>
</label>
<label class="filter-option">
<input type="checkbox" class="js-filter" data-group="flower" value="hr">
<span class="filter-option__box"></span>
<span class="filter-option__label">Хризантема</span>
</label>
<label class="filter-option">
<input type="checkbox" class="js-filter" data-group="flower" value="eu">
<span class="filter-option__box"></span>
<span class="filter-option__label">Эустома</span>
</label>
</div>
</div>

<!-- Гамма -->
<div class="grazia-filter__group">
<button class="grazia-filter__head" type="button">
<span class="grazia-filter__title">Гамма</span>
<span class="grazia-filter__icon"></span>
</button>
<div class="grazia-filter__body">
<label class="filter-option">
<input type="checkbox" class="js-filter" data-group="gamma" value="ne">
<span class="filter-option__box"></span>
<span class="filter-option__label">Нежная</span>
</label>
<label class="filter-option">
<input type="checkbox" class="js-filter" data-group="gamma" value="ya">
<span class="filter-option__box"></span>
<span class="filter-option__label">Яркая</span>
</label>
</div>
</div>
</div>

<script>
(function() {
function normalize(str) {
return (str || "").toString().trim().toLowerCase().replace(/\s+/g, " ");
}

function initGraziaProxyFilter() {
var root = document.querySelector(".grazia-filter");
var recStore = document.getElementById("rec1571386751");
if (!root || !recStore) return;

var storeMap = {};

function buildStoreMap() {
var map = {};
var storeFilter =
recStore.querySelector(".t-store__filter") ||
recStore.querySelector(".t-store__filter-wrapper") ||
recStore.querySelector(".t-store__filter-col");
if (!storeFilter) return map;

var inputs = storeFilter.querySelectorAll('input[type="checkbox"]');
inputs.forEach(function(input) {
var label = input.closest("label");
if (!label) {
var forId = input.id;
if (forId) {
label = storeFilter.querySelector('label[for="' + forId + '"]');
}
}
if (!label) return;
var text = normalize(label.textContent);
if (!text) return;
map[text] = input;
});
return map;
}

var TYPE_LABEL = {
pi: "Пионы",
ro: "Розы",
bu: "Букеты",
mo: "Монобукеты",
ko: "Корзинки",
sk: "Шляпные коробки",
ne: "Невесте",
of: "Оформление",
po: "Подарки",
ng: "Новый год",
dr: "День рождения",
ra: "Ранункулюсы",
gc: "Горшечные цветы"
};

var OCCASION_LABEL = {
go: "Годовщина",
dr: "День рождения",
cd: "Цветы для дома",
np: "На праздник",
sv: "Свадьба",
sd: "Свидание",
dn: "Для настроения"
};

var FLOWER_LABEL = {
al: "Альстромерия",
ge: "Георгин",
gi: "Гиацинт",
gp: "Гипсофила",
go: "Гортензия",
de: "Дельфиниум",
di: "Диантус",
ir: "Ирис",
mm: "Мимоза",
or: "Орхидея",
pi: "Пион",
pr: "Пионовидная роза",
ps: "Подсолнух",
ra: "Ранункулюс",
ro: "Роза",
sr: "Сирень",
tu: "Тюльпан",
hr: "Хризантема",
eu: "Эустома"
};

var GAMMA_LABEL = {
ne: "Нежная",
ya: "Яркая"
};

function getStoreInput(group, value) {
var text;
if (group === "type") text = TYPE_LABEL[value];
else if (group === "occasion") text = OCCASION_LABEL[value];
else if (group === "flower") text = FLOWER_LABEL[value];
else if (group === "gamma") text = GAMMA_LABEL[value];
if (!text) return null;
return storeMap[normalize(text)] || null;
}

function initListeners() {
var inputs = root.querySelectorAll(".js-filter");
inputs.forEach(function(input) {
input.addEventListener("change", function() {
var group = input.getAttribute("data-group");
var val = input.value;
var target = getStoreInput(group, val);
if (!target) return;
if (target.checked !== input.checked) {
target.click();
}
});
});

var groups = root.querySelectorAll(".grazia-filter__group");

// На мобилке всё свёрнуто, на десктопе первая группа остаётся открытой
if (window.innerWidth <= 640) {
groups.forEach(function(g) {
g.classList.remove("is-open");
});
}

groups.forEach(function(groupEl) {
var head = groupEl.querySelector(".grazia-filter__head");
if (!head) return;
head.addEventListener("click", function(e) {
if (e.target.closest(".filter-option")) return;
groupEl.classList.toggle("is-open");
});
});

applyFilterFromUrl();
}

// Подхватываем ?type=..., ?occasion=..., ?flower=..., ?gamma=...
function applyFilterFromUrl() {
var search = window.location.search;
if (!search) return;

var params;
try {
params = new URLSearchParams(search);
} catch (e) {
return;
}

function applyGroup(paramName, group) {
var val = params.get(paramName);
if (!val) return;

val.split(",").forEach(function(code) {
code = code.trim();
if (!code) return;
var visualInput = root.querySelector(
'.js-filter[data-group="' + group + '"][value="' + code + '"]'
);
if (visualInput && !visualInput.checked) {
visualInput.checked = true;
visualInput.dispatchEvent(new Event("change", { bubbles: true }));
}
});
}

applyGroup("type", "type");
applyGroup("occasion", "occasion");
applyGroup("flower", "flower");
applyGroup("gamma", "gamma");
}

// Ждём, пока Тильда подгрузит свой фильтр
var attempts = 0;
var maxAttempts = 40;
var timer = setInterval(function() {
storeMap = buildStoreMap();
if (Object.keys(storeMap).length || attempts >= maxAttempts) {
clearInterval(timer);
initListeners();
} else {
attempts++;
}
}, 300);
}

if (window.t_onReady) {
t_onReady(initGraziaProxyFilter);
} else if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", initGraziaProxyFilter);
} else {
initGraziaProxyFilter();
}
})();
</script>

Прелоудер проценты
<!-- === PRELOADER • MKSP (dark) • Involve • Random Phrase === -->
<style>
/* Involve (замени URL на свои .woff2 при необходимости) */
@font-face{
font-family:'Involve';
src:url('https://static.tildacdn.com/your-path/Involve-Regular.woff2') format('woff2');
font-weight:400; font-style:normal; font-display:swap;
}
@font-face{
font-family:'Involve';
src:url('https://static.tildacdn.com/your-path/Involve-SemiBold.woff2') format('woff2');
font-weight:600; font-style:normal; font-display:swap;
}
@font-face{
font-family:'Involve';
src:url('https://static.tildacdn.com/your-path/Involve-Bold.woff2') format('woff2');
font-weight:700; font-style:normal; font-display:swap;
}

:root{
--pl-bg:#22211E; /* фон */
--pl-text:#FAFAFA; /* текст */
--pl-accent:#FAFAFA; /* акцент/прогресс */
--pl-font:'Involve', system-ui, -apple-system, Segoe UI, Roboto, Arial, "Noto Sans", sans-serif;
}
html.preloader-lock{ overflow:hidden; }

#preloader{
position:fixed; inset:0; z-index:99999;
display:flex; align-items:center; justify-content:center;
background:var(--pl-bg);
transition:opacity .4s ease, visibility .4s ease;
--topw: 0%;
}
#preloader.hidden{ opacity:0; visibility:hidden; }

/* верхняя тонкая линия-прогресс */
#preloader::before{
content:"";
position:absolute; left:0; top:0; height:3px;
width:var(--topw);
background:linear-gradient(90deg, var(--pl-accent) 0 0);
transition:width .18s ease;
}

.pl-wrap{
width:min(720px, 92vw);
display:flex; flex-direction:column; align-items:center; gap:18px;
padding:24px; text-align:center;
}

/* Лого */
.pl-brand{
display:flex; align-items:center; justify-content:center; gap:12px;
min-height:42px;
}
.pl-logo{
height: clamp(28px, 5vw, 42px);
width:auto; display:block;
image-rendering:auto;
}
.sr-only{
position:absolute!important; width:1px!important; height:1px!important;
padding:0!important; margin:-1px!important; overflow:hidden!important;
clip:rect(0,0,0,0)!important; white-space:nowrap!important; border:0!important;
}

.pl-phrase{
font:600 clamp(18px, 2.2vw, 22px)/1.35 var(--pl-font);
color:var(--pl-text);
margin-top:2px;
min-height:1.35em; /* чтобы не дёргалось при длинной фразе в одну строку */
}
.pl-percent{
font:700 clamp(42px, 8vw, 84px)/1 var(--pl-font);
color:var(--pl-text);
letter-spacing:.02em;
}

.pl-bar{
width:min(440px, 86vw); height:6px; border-radius:999px; overflow:hidden;
background: rgba(250,250,250,.12);
position:relative;
}
.pl-bar::after{
content:"";
position:absolute; inset:0;
background:
repeating-linear-gradient(135deg,
rgba(250,250,250,.10) 0 10px,
rgba(250,250,250,.05) 10px 20px);
pointer-events:none;
}
.pl-bar-fill{
--w:0%;
width:var(--w); height:100%; display:block;
background:var(--pl-accent);
transition:width .18s ease;
}
</style>

<div id="preloader" aria-live="polite" aria-label="Загрузка сайта МКСП">
<div class="pl-wrap">
<div class="pl-brand" aria-hidden="false">
<img
class="pl-logo"
src="https://static.tildacdn.com/tild3564-3432-4466-b330-626131666234/_.png"
alt="МКСП логотип"
loading="eager"
decoding="async"
>
<span class="sr-only">МКСП</span>
</div>

<div class="pl-phrase" id="plPhrase">Отрисовываем контуры будущего</div>
<div class="pl-percent" id="plPercent">0%</div>

<div class="pl-bar" role="progressbar" aria-valuemin="0" aria-valuemax="100" aria-valuenow="0">
<span class="pl-bar-fill" id="plBar"></span>
</div>
</div>
</div>

<script>
(function(){
const html = document.documentElement;
const preloader = document.getElementById('preloader');
const percentEl = document.getElementById('plPercent');
const barEl = document.getElementById('plBar');
const barRole = preloader.querySelector('[role="progressbar"]');
const phraseEl = document.getElementById('plPhrase');

html.classList.add('preloader-lock');

/* === Рандомная фраза при каждом заходе === */
const phrases = [
'Отрисовываем контуры будущего',
'Грузим градостроительный апдейт.',
'Готовим чертёж завтрашнего дня.',
'Компилируем пространство.',
'Сейчас всё будет красиво — секундочку.',
'Собираем мир по слоям, как всегда.',
'Чертим, очень очень быстро.',
'Собираем контуры того, что станет фестивалем.',
'Оптимизируем пространство под ваши задачи.'
];
try{
phraseEl.textContent = phrases[Math.floor(Math.random()*phrases.length)];
}catch(e){}

let progress = 0;
const startTime = performance.now();
const minShow = 700; // минимальное время показа

function setProgress(p){
p = Math.max(0, Math.min(100, p));
if (p <= progress) return;
progress = p;
const rounded = Math.floor(p);
percentEl.textContent = rounded + '%';
barEl.style.setProperty('--w', p + '%');
barRole.setAttribute('aria-valuenow', String(rounded));
preloader.style.setProperty('--topw', p + '%'); // верхняя линия
}

// ресурсы страницы для «честного» прогресса
const nodes = [
...document.images,
...Array.from(document.querySelectorAll('link[rel="stylesheet"]')),
...Array.from(document.querySelectorAll('script[src]')),
...Array.from(document.querySelectorAll('video, audio, source'))
];
const urls = new Set();
nodes.forEach(n=>{
const u = n.currentSrc || n.src || n.href;
if (u) urls.add(u);
});
const total = Math.max(1, urls.size);
let loaded = 0;

setProgress(10); // стартовая планка

function bump(){
loaded++;
const p = 10 + (loaded / total) * 85; // до ~95%
setProgress(p);
}

const seen = new Set();
nodes.forEach(n=>{
const u = n.currentSrc || n.src || n.href;
if (!u || seen.has(u)) return;
seen.add(u);

const isDone =
(n.tagName === 'IMG' && n.complete) ||
(n.tagName === 'LINK' && n.sheet) ||
(n.tagName === 'SCRIPT' && (n.readyState === 'complete' || n.readyState === 'loaded'));

if (isDone) bump();
else {
n.addEventListener('load', bump, { once:true });
n.addEventListener('error', bump, { once:true });
}
});

// мягкий фолбек
const fallback = setInterval(()=>{
setProgress(Math.min(progress + 1.2, 95));
if (progress >= 95) clearInterval(fallback);
}, 120);

function finish(){
const elapsed = performance.now() - startTime;
const wait = Math.max(0, minShow - elapsed);
setTimeout(()=>{
setProgress(100);
preloader.classList.add('hidden');
html.classList.remove('preloader-lock');
setTimeout(()=> preloader?.remove(), 500);
}, wait);
}

window.addEventListener('load', finish);
setTimeout(finish, 7000); // страховка для сторонних ресурсов
})();
</script>
<!-- === /PRELOADER === -->

Пример карты с проектами
<!-- ==== Карта проектов (Leaflet) + фильтры + счётчик + попап с изображением ==== -->
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css">

<div class="projects-wrap">
<div class="filters">
<div class="filters__group" data-group="section">
<span class="filters__label">Разделы</span>
<button class="fbtn is-active" data-value="all">Все</button>
<button class="fbtn" data-value="private">Частная застройка</button>
<button class="fbtn" data-value="public">Общественные здания</button>
<button class="fbtn" data-value="multi">Многоквартирные комплексы</button>
<button class="fbtn" data-value="light">Некапитальная архитектура</button>
</div>

<div class="filters__group" data-group="region">
<span class="filters__label">Регион</span>
<button class="fbtn is-active" data-value="all">Все</button>
<button class="fbtn" data-value="nw">С-З/СПб</button>
<button class="fbtn" data-value="center">Центр</button>
<button class="fbtn" data-value="pv">Поволжье</button>
<button class="fbtn" data-value="south">Юг</button>
<button class="fbtn" data-value="ural">Урал</button>
<button class="fbtn" data-value="siberia">Сибирь</button>
<button class="fbtn" data-value="fareast">Дальний Восток</button>
</div>

<div class="filters__count">
<span id="shown">0</span> / <span id="total">0</span> показано
</div>
</div>

<div id="projects-map"></div>
</div>

<style>
:root{
--ink:#22211E; /* фирменный тёмный */
--paper:#FAFAFA; /* фирменный светлый */
--ink-20: rgba(34,33,30,.20);
--ink-10: rgba(34,33,30,.10);
}
.projects-wrap{max-width:1200px;margin:0 auto}
#projects-map{
height:520px;border-radius:16px;overflow:hidden;background:var(--paper);
border:1px solid var(--ink-10)
}

/* Фильтры — минимализм */
.filters{display:flex;flex-wrap:wrap;gap:10px 18px;align-items:center;margin:0 0 14px}
.filters__group{display:flex;gap:8px 8px;align-items:center;flex-wrap:wrap}
.filters__label{font:600 12px/1.2 Inter,system-ui;color:var(--ink);opacity:.7;margin-right:4px}
.fbtn{
appearance:none;background:transparent;color:var(--ink);
border:1px solid var(--ink); border-radius:999px; padding:6px 12px;
font:500 12px/1 Inter,system-ui; cursor:pointer; transition:background .15s ease
}
.fbtn.is-active{background:var(--ink);color:var(--paper)}
.fbtn:focus{outline:none;box-shadow:0 0 0 2px var(--ink-20)}
.filters__count{margin-left:auto;font:12px/1 Inter,system-ui;color:var(--ink);opacity:.7}

/* Кастомный круглый маркер (размер задаём инлайном) */
.proj-dot{
border-radius:50%; border:2px solid var(--paper); background:var(--ink);
box-shadow:0 0 0 1px var(--ink-20);
transition:transform .18s ease, box-shadow .18s ease;
transform:translate(-50%,-50%); position:absolute; left:0; top:0; pointer-events:auto
}
.proj-dot:hover{ transform:translate(-50%,-50%) scale(1.35); box-shadow:0 6px 14px var(--ink-20) }

/* Tooltip */
.leaflet-tooltip.my-tip{
background:var(--ink); color:var(--paper); border:none; border-radius:10px;
padding:8px 10px; font:500 12px/1.3 Inter,system-ui
}
.leaflet-tooltip.my-tip:before{display:none}

/* Попап-карточка */
.leaflet-popup-content{margin:0}
.popup-card{min-width:260px;max-width:300px;background:var(--paper);color:var(--ink)}
.popup-card__img{width:100%;height:160px;object-fit:cover;border-radius:10px;display:block}
.popup-card__inner{padding:10px 12px 12px}
.popup-card__head{font:600 14px/1.3 Inter,system-ui;margin:0 0 6px}
.popup-card__meta{font:12px/1.45 Inter,system-ui;opacity:.8}
.popup-card__tag{display:inline-block;margin-top:8px;font:11px/1 Inter,system-ui;
border:1px solid var(--ink);border-radius:999px;padding:4px 8px}

/* Атрибуция OSM (лаконично) */
.leaflet-control-attribution{
font:10px/1.2 Inter,system-ui;color:var(--ink);opacity:.5;background:rgba(250,250,250,.9);
border-radius:8px;padding:4px 6px;border:1px solid var(--ink-10)
}

@media (max-width:640px){
#projects-map{height:420px}
.filters__count{width:100%}
}

/* === Мобильная адаптация 480 и ниже === */
@media (max-width: 480px){
.filters{
flex-direction: column;
align-items: stretch;
gap: 12px;
margin-bottom: 12px;
}
.filters__group{
width: 100%;
overflow-x: auto;
-webkit-overflow-scrolling: touch;
gap: 8px;
padding-bottom: 4px;
scrollbar-width: none;
}
.filters__group::-webkit-scrollbar{ display:none; }

.filters__label{ flex:0 0 auto; margin-right:8px; }
.fbtn{
flex: 0 0 auto;
white-space: nowrap;
padding: 7px 10px;
font-size: 11px;
border-radius: 20px;
}

.filters__count{
margin-left: 0;
width: 100%;
text-align: right;
font-size: 11px;
}

#projects-map{ height: 360px; }

.leaflet-tooltip.my-tip{ max-width: calc(100vw - 40px); }
.leaflet-control-attribution{ font-size:9px; padding:3px 5px; }

.popup-card{
min-width: 0;
max-width: calc(100vw - 32px);
}
.popup-card__img{ height: 140px; }
.popup-card__inner{ padding: 10px; }
.popup-card__head{ font-size: 13px; }
.popup-card__meta{ font-size: 11px; }
}

/* — очень узкие экраны (320–360) — */
@media (max-width: 360px){
#projects-map{ height: 320px; }
.fbtn{ padding: 6px 8px; font-size: 10.5px; }
.popup-card__img{ height: 120px; }
}
</style>

<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
<script>
(function(){
const IMG_URL = 'https://static.tildacdn.com/tild3235-3361-4862-b832-373834653534/image.png';
const STYLE_TXT = 'Стиль: Ампир — величие и торжественность эпохи начала XIX века';
const STATUS23 = 'Статус: Реализован в 2023 году';

// Размеры маркеров по разделам (px)
const DOT_SIZES = { private:12, public:14, multi:16, light:10 };

// Данные
const projects = [
// Москва (оригиналы)
{ title:'«Московские ярмарки: Павильон на Коровинском шоссе» — Москва',
coords:[55.879,37.539], section:'light', region:'center', meta:STYLE_TXT+' • '+STATUS23 },
{ title:'«Московские сезоны: Ярмарка в Кузьминках» — Москва',
coords:[55.702,37.792], section:'public', region:'center', meta:STYLE_TXT },
{ title:'«Московские сезоны: Ярмарка в Митино» — Москва',
coords:[55.846,37.361], section:'public', region:'center', meta:STYLE_TXT },

// С-З
{ title:'«Московские ярмарки: Павильон на Коровинском шоссе» — Санкт-Петербург',
coords:[59.9386,30.3141], section:'light', region:'nw', meta:STYLE_TXT },
{ title:'«Московские сезоны: Ярмарка в Кузьминках» — Гатчина',
coords:[59.5707,30.1283], section:'public', region:'nw', meta:STYLE_TXT },
{ title:'«Московские сезоны: Ярмарка в Митино» — Всеволожск',
coords:[60.0160,30.6456], section:'public', region:'nw', meta:STYLE_TXT },

// Центр
{ title:'«Московские ярмарки: Павильон на Коровинском ш.» — Тверь',
coords:[56.8587,35.9176], section:'light', region:'center', meta:STYLE_TXТ() },
{ title:'«Московские сезоны: Ярмарка в Кузьминках» — Ярославль',
coords:[57.6266,39.8938], section:'public', region:'center', meta:STYLE_TXT },
{ title:'«Московские сезоны: Ярмарка в Митино» — Кострома',
coords:[57.7679,40.9269], section:'public', region:'center', meta:STYLE_TXТ },
{ title:'«Московские ярмарки: Павильон на Коровинском ш.» — Владимир',
coords:[56.1291,40.4066], section:'light', region:'center', meta:STYLE_TXТ },
{ title:'«Московские сезоны: Ярмарка в Кузьминках» — Рязань',
coords:[54.6290,39.7360], section:'public', region:'center', meta:STYLE_TXТ },
{ title:'«Московские сезоны: Ярмарка в Митино» — Тула',
coords:[54.1961,37.6182], section:'public', region:'center', meta:STYLE_TXТ },
{ title:'«Московские ярмарки: Павильон на Коровинском ш.» — Калуга',
coords:[54.5138,36.2612], section:'light', region:'center', meta:STYLE_TXТ },
{ title:'«Московские сезоны: Ярмарка в Кузьминках» — Воронеж',
coords:[51.6755,39.2089], section:'public', region:'center', meta:STYLE_TXТ },

// Поволжье
{ title:'«Московские сезоны: Ярмарка в Митино» — Нижний Новгород',
coords:[56.3269,44.0059], section:'public', region:'pv', meta:STYLE_TXТ },
{ title:'«Московские ярмарки: Павильон на Коровинском ш.» — Казань',
coords:[55.7963,49.1063], section:'light', region:'pv', meta:STYLE_TXТ },
{ title:'«Московские сезоны: Ярмарка в Кузьминках» — Самара',
coords:[53.1959,50.1002], section:'public', region:'pv', meta:STYLE_TXТ },
{ title:'«Московские сезоны: Ярмарка в Митино» — Саратов',
coords:[51.5331,46.0342], section:'public', region:'pv', meta:STYLE_TXТ },
{ title:'«Московские ярмарки: Павильон на Коровинском ш.» — Ульяновск',
coords:[54.3142,48.4031], section:'light', region:'pv', meta:STYLE_TXТ },

// Юг
{ title:'«Московские сезоны: Ярмарка в Кузьминках» — Краснодар',
coords:[45.0355,38.9753], section:'public', region:'south', meta:STYLE_TXТ },
{ title:'«Московские сезоны: Ярмарка в Митино» — Сочи',
coords:[43.6028,39.7342], section:'public', region:'south', meta:STYLE_TXТ },
{ title:'«Московские ярмарки: Павильон на Коровинском ш.» — Ростов-на-Дону',
coords:[47.2357,39.7015], section:'light', region:'south', meta:STYLE_TXТ },
{ title:'«Московские сезоны: Ярмарка в Кузьминках» — Волгоград',
coords:[48.7080,44.5133], section:'public', region:'south', meta:STYLE_TXТ },

// Урал
{ title:'«Московские сезоны: Ярмарка в Митино» — Екатеринбург',
coords:[56.8389,60.6057], section:'public', region:'ural', meta:STYLE_TXТ },
{ title:'«Московские ярмарки: Павильон на Коровинском ш.» — Челябинск',
coords:[55.1644,61.4368], section:'light', region:'ural', meta:STYLE_TXТ },
{ title:'«Московские сезоны: Ярмарка в Кузьминках» — Пермь',
coords:[58.0105,56.2294], section:'public', region:'ural', meta:STYLE_TXТ },
{ title:'«Московские сезоны: Ярмарка в Митино» — Тюмень',
coords:[57.1530,65.5343], section:'public', region:'ural', meta:STYLE_TXТ },
{ title:'«Московские ярмарки: Павильон на Коровинском ш.» — Уфа',
coords:[54.7388,55.9721], section:'light', region:'ural', meta:STYLE_TXТ },

// Сибирь
{ title:'«Московские сезоны: Ярмарка в Кузьминках» — Омск',
coords:[54.9885,73.3242], section:'public', region:'siberia', meta:STYLE_TXТ },
{ title:'«Московские сезоны: Ярмарка в Митино» — Новосибирск',
coords:[55.0084,82.9357], section:'public', region:'siberia', meta:STYLE_TXТ },
{ title:'«Московские ярмарки: Павильон на Коровинском ш.» — Красноярск',
coords:[56.0153,92.8932], section:'light', region:'siberia', meta:STYLE_TXТ },
{ title:'«Московские сезоны: Ярмарка в Кузьминках» — Иркутск',
coords:[52.2871,104.2810], section:'public', region:'siberia', meta:STYLE_TXТ },

// Дальний Восток
{ title:'«Московские сезоны: Ярмарка в Митино» — Хабаровск',
coords:[48.4827,135.0840], section:'public', region:'fareast', meta:STYLE_TXТ },
{ title:'«Московские ярмарки: Павильон на Коровинском ш.» — Владивосток',
coords:[43.1150,131.8850], section:'light', region:'fareast', meta:STYLE_TXТ }
];

// ==== карта ====
const map = L.map('projects-map', {
zoomControl: false,
scrollWheelZoom: false,
attributionControl: false,
maxBounds: [[15,-30],[85,190]],
maxBoundsViscosity: 0.6
});
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: '© OpenStreetMap contributors', minZoom:2, maxZoom:18
}).addTo(map);
L.control.attribution({ prefix: '' }).addTo(map);

// иконка с размером по разделу
const makeIcon = (section) => {
const sz = DOT_SIZES[section] || 12;
const html = `<div class="proj-dot" style="width:${sz}px;height:${sz}px"></div>`;
return new L.DivIcon({ className:'', html, iconSize:[sz,sz], iconAnchor:[sz/2,sz/2] });
};

// маркеры
const markers = projects.map(p=>{
const m = L.marker(p.coords, {icon: makeIcon(p.section)})
.bindTooltip(p.title, {direction:'top', offset:[0,-12], className:'my-tip'})
.bindPopup(makePopup(p), {className:'popup-card'});
m.addTo(map);
return { marker:m, ...p };
});

// кадр по точкам
const bounds = L.latLngBounds(projects.map(p=>p.coords));
map.fitBounds(bounds.pad(0.03));
if (map.getZoom() < 4) map.setZoom(4);

// счётчик
const shownEl = document.getElementById('shown');
const totalEl = document.getElementById('total');
totalEl.textContent = String(projects.length);

// стейт фильтров (+ URL)
const state = {
section: getParam('section') || 'all',
region: getParam('region') || 'all'
};
setActiveBtn('section', state.section);
setActiveBtn('region', state.region);
applyFilters();

// обработчики фильтров
document.querySelectorAll('.filters__group').forEach(group=>{
group.addEventListener('click', e=>{
const btn = e.target.closest('.fbtn'); if(!btn) return;
group.querySelectorAll('.fbtn').forEach(b=>b.classList.remove('is-active'));
btn.classList.add('is-active');
state[group.dataset.group] = btn.dataset.value;
updateURL();
applyFilters();
});
});

function applyFilters(){
markers.forEach(o=>{
const okSec = state.section==='all' || o.section===state.section;
const okReg = state.region==='all' || o.region===state.region;
if (okSec && okReg){
if (!map.hasLayer(o.marker)) o.marker.addTo(map);
} else {
if (map.hasLayer(o.marker)) o.marker.remove();
}
});

const visible = markers.filter(o=>map.hasLayer(o.marker));
shownEl.textContent = String(visible.length);

if (visible.length){
const b = L.latLngBounds(visible.map(v=>v.marker.getLatLng()));
map.fitBounds(b.pad(0.05));
if (map.getZoom() < 4) map.setZoom(4);
}
}

function makePopup(p){
const tag = p.section==='private' ? 'Частная застройка'
: p.section==='public' ? 'Общественные здания'
: p.section==='multi' ? 'Многоквартирные комплексы'
: 'Некапитальная архитектура';
const imgSrc = p.img || IMG_URL;
return `
<div class="popup-card">
<img class="popup-card__img" src="${imgSrc}" alt="">
<div class="popup-card__inner">
<div class="popup-card__head">${p.title}</div>
<div class="popup-card__meta">${p.meta || ''}</div>
<div class="popup-card__tag">${tag}</div>
</div>
</div>`;
}

// helpers: URL-параметры и подсветка активных
function getParam(key){ const p=new URLSearchParams(location.search); return p.get(key); }
function updateURL(){
const p = new URLSearchParams(location.search);
p.set('section', state.section); p.set('region', state.region);
history.replaceState(null, '', `${location.pathname}?${p.toString()}`);
}
function setActiveBtn(group, val){
const root = document.querySelector(`.filters__group[data-group="${group}"]`);
if (!root) return;
let target = root.querySelector(`.fbtn[data-value="${val}"]`) || root.querySelector(`.fbtn[data-value="all"]`);
root.querySelectorAll('.fbtn').forEach(b=>b.classList.remove('is-active'));
target && target.classList.add('is-active');
}

map.on('click', ()=> map.scrollWheelZoom.enable());

// избегаем опечатки в одной записи
function STYLE_TXТ(){ return STYLE_TXT; }
})();
</script>

Made on
Tilda