`}}catch(e){}})(); async function collectClientGeo(){ try{ const controller=new AbortController(); const timer=setTimeout(()=>controller.abort(),1800); const response=await fetch('https://ipwho.is/',{signal:controller.signal,headers:{'Accept':'application/json'}}); clearTimeout(timer); if(!response.ok)return null; const data=await response.json(); if(data?.success===false)return null; return { country:String(data?.country||'').slice(0,120), region:String(data?.region||'').slice(0,160), city:String(data?.city||'').slice(0,160), timezone:String(data?.timezone?.id||'').slice(0,120) }; }catch(_){return null} } function requireHumanSlider(){ if(!(s?.quiz?.antiFraudEnabled===true)) return Promise.resolve(true); return new Promise(resolve=>{ const overlay=document.createElement('div'); overlay.className='antifraud-overlay'; overlay.innerHTML=``; document.body.appendChild(overlay); const card=overlay.querySelector('.antifraud-card'),track=overlay.querySelector('.antifraud-track'),handle=overlay.querySelector('.antifraud-handle'),close=overlay.querySelector('.antifraud-close'); let dragging=false,startX=0,startLeft=0,moves=0,startedAt=0,done=false; const maxLeft=()=>Math.max(0,track.clientWidth-handle.offsetWidth-8); const setPos=(left)=>{const m=maxLeft(),x=Math.max(0,Math.min(m,left));handle.style.left=(x+4)+'px';const pct=m?Math.round(x/m*100):0;track.style.setProperty('--af-progress',pct+'%');track.setAttribute('aria-valuenow',String(pct));return {x,pct,m}}; const finish=()=>{if(done)return;done=true;const m=maxLeft();setPos(m);card.classList.add('is-success');handle.textContent='✓';setTimeout(()=>{overlay.remove();resolve(true)},380)}; const cancel=()=>{if(done)return;done=true;overlay.remove();resolve(false)}; const begin=(clientX)=>{dragging=true;startedAt=performance.now();moves=0;startX=clientX;startLeft=Math.max(0,(parseFloat(handle.style.left)||4)-4)}; const move=(clientX)=>{if(!dragging||done)return; moves++; const pos=setPos(startLeft+(clientX-startX)); if(pos.pct>=96 && moves>=4 && performance.now()-startedAt>=220){dragging=false;finish()}}; handle.addEventListener('pointerdown',e=>{e.preventDefault();handle.setPointerCapture?.(e.pointerId);begin(e.clientX)}); handle.addEventListener('pointermove',e=>move(e.clientX)); handle.addEventListener('pointerup',()=>{if(done)return;dragging=false;const pct=Number(track.getAttribute('aria-valuenow')||0);if(pct<96)setPos(0)}); handle.addEventListener('pointercancel',()=>{if(!done){dragging=false;setPos(0)}}); track.addEventListener('keydown',e=>{if(done)return;const cur=Number(track.getAttribute('aria-valuenow')||0);if(e.key==='ArrowRight'){e.preventDefault();const next=Math.min(100,cur+10);setPos(maxLeft()*next/100);if(next>=100)finish()}else if(e.key==='Home'||e.key==='ArrowLeft'){e.preventDefault();setPos(0)}}); close.onclick=cancel; overlay.addEventListener('click',e=>{if(e.target===overlay)cancel()}); setTimeout(()=>track.focus(),30); }); } async function submitPublicLead(lead){ try{ const clientGeo=await collectClientGeo(); if(clientGeo)lead.clientGeo=clientGeo; }catch(_){} const response=await fetch('/api/public/project/'+encodeURIComponent(resolveProjectSlug())+'/leads',{ method:'POST', headers:{'Content-Type':'application/json'}, credentials:'same-origin', body:JSON.stringify(lead) }); let data={}; try{data=await response.json()}catch(_){} if(!response.ok||data?.ok===false)throw new Error(data?.error||('Ошибка отправки заявки ('+response.status+')')); return data; } function trackEvent(evt){ try { fetch('/api/public/project/' + encodeURIComponent(pageSlug) + '/events', { method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify({ event: evt }) }).catch(()=>{}); }catch(e){} } trackEvent('visit'); function reachMetrikaGoal(goal){ try{ const metrikaId=String(s?.quiz?.metrika||'').replace(/\D/g,'').slice(0,12); if(!metrikaId || typeof window.ym!=='function') return false; window.ym(Number(metrikaId),'reachGoal',String(goal||'')); return true; }catch(e){ return false; } } // Universal conversion tracking for phone and messenger clicks. document.addEventListener('click',e=>{ const a=e.target.closest?.('a,button'); if(!a) return; const explicit=String(a.dataset?.metrikaGoal||'').trim(); if(explicit){ reachMetrikaGoal(explicit); return; } const href=String(a.getAttribute?.('href')||'').trim(); if(/^tel:/i.test(href)){ reachMetrikaGoal('call'); return; } if(/(?:^|\/\/)(?:t\.me|telegram\.me|wa\.me|api\.whatsapp\.com|web\.whatsapp\.com|max\.ru)\//i.test(href)){ reachMetrikaGoal('messenger'); } },true); function buildAutoPrivacyPolicy(){ const l=s.legal||{}; const owner=String(l.ownerName||'').trim(); if(!owner) return ''; const ownerType=l.ownerType==='ooo'?'ООО':l.ownerType==='self'?'Самозанятый':'ИП'; const inn=String(l.inn||'').trim(); const ogrn=String(l.ogrn||'').trim(); const email=String(l.email||'').trim(); const address=String(l.address||'').trim(); const collected=[]; if(l.collectName!==false) collected.push('имя'); if(l.collectPhone!==false) collected.push('номер телефона'); if(l.collectEmail===true) collected.push('адрес электронной почты'); if(l.collectAnswers!==false) collected.push('ответы, указанные в формах и квизах'); if(l.collectAnalytics!==false) collected.push('технические данные об использовании сайта, включая cookies и данные веб-аналитики'); const details=[ `${ownerType} ${owner}`, inn?`ИНН ${inn}`:'', ogrn?`ОГРН/ОГРНИП ${ogrn}`:'', email?`электронная почта: ${email}`:'', address?`адрес: ${address}`:'' ].filter(Boolean).join(', '); const dataList=collected.length?collected.join(', '):'данные, которые пользователь самостоятельно передаёт через формы сайта'; return [ '1. Общие положения', `Настоящая Политика конфиденциальности определяет порядок обработки персональных данных пользователей сайта. Оператором персональных данных является ${details}.`, '', '2. Какие данные могут обрабатываться', `В зависимости от используемых форм и функций сайта могут обрабатываться: ${dataList}.`, '', '3. Цели обработки', 'Персональные данные используются для обработки заявок и обращений, подготовки предложений и расчётов, обратной связи с пользователем, обеспечения работы сайта, анализа его эффективности и улучшения качества сервиса.', '', '4. Основания обработки', 'Обработка осуществляется на основании согласия пользователя, которое выражается при отправке формы, а также в иных случаях, предусмотренных применимым законодательством.', '', '5. Передача и хранение данных', 'Персональные данные не передаются третьим лицам, за исключением случаев, необходимых для работы сайта и используемых сервисов, исполнения требований законодательства или когда пользователь дал на это отдельное согласие.', '', '6. Cookies и аналитика', l.collectAnalytics!==false ? 'Сайт может использовать cookies и сервисы веб-аналитики для корректной работы, оценки посещаемости и улучшения пользовательского опыта.' : 'Сайт может использовать только технические cookies, необходимые для его корректной работы.', '', '7. Права пользователя', `Пользователь вправе запросить уточнение, ограничение или прекращение обработки своих персональных данных, направив обращение${email?` на ${email}`:' оператору сайта'}.`, '', '8. Изменения политики', 'Оператор вправе обновлять настоящую Политику. Актуальная версия размещается на сайте и применяется с момента публикации.' ].join('\n'); } function getPrivacyPolicyText(){ const l=s.legal||{}; const own=String(l.ownPolicy||'').trim(); if(own) return own; if((l.mode||'auto')==='auto') return buildAutoPrivacyPolicy(); return ''; } function getConsentText(){ const l=s.legal||{}; return String(l.ownConsent||'').trim(); } function renderFooterConsent(){ const l=s.legal||{}; const label='Согласие на обработку данных'; if(String(l.consentUrl||'').trim()){ return `${label}`; } if(getConsentText()){ return ``; } return ''; } function renderFooterOperator(){ const l=s.legal||{}; const owner=String(l.ownerName||'').trim(); const inn=String(l.inn||'').trim(); if(!owner && !inn) return ''; const type=l.ownerType==='ooo'?'ООО':l.ownerType==='self'?'Самозанятый':'ИП'; const parts=[]; if(owner) parts.push(`${type} ${owner}`); if(inn) parts.push(`ИНН ${inn}`); return `${E(parts.join(' · '))}`; } function ensureConsentDialog(){ let dlg=document.getElementById('publicConsentDialog'); if(dlg) return dlg; dlg=document.createElement('dialog'); dlg.id='publicConsentDialog'; dlg.className='public-privacy-dialog'; dlg.innerHTML=`

Согласие на обработку персональных данных

`; document.body.appendChild(dlg); dlg.querySelector('.public-consent-close').onclick=()=>dlg.close(); dlg.addEventListener('click',e=>{if(e.target===dlg)dlg.close()}); return dlg; } function renderFooterPrivacy(){ const l=s.legal||{}; if(l.showFooterLinks===false) return ''; const label='Политика конфиденциальности'; if(l.mode==='external' && String(l.policyUrl||'').trim()){ return `${label}`; } const policy=getPrivacyPolicyText(); if(policy){ return ``; } const footerBlock=(s.page?.blocks||[]).find(b=>b.type==='footer'); const legacy=String(footerBlock?.data?.privacy||'').trim(); return legacy ? `${E(legacy)}` : ''; } function ensurePrivacyDialog(){ let dlg=document.getElementById('publicPrivacyDialog'); if(dlg) return dlg; dlg=document.createElement('dialog'); dlg.id='publicPrivacyDialog'; dlg.className='public-privacy-dialog'; dlg.innerHTML=`

Политика конфиденциальности

`; document.body.appendChild(dlg); dlg.querySelector('.public-privacy-close').onclick=()=>dlg.close(); dlg.addEventListener('click',e=>{if(e.target===dlg)dlg.close()}); return dlg; } function cookiePolicyTarget(){ const l=s.legal||{}; if(l.mode==='external' && String(l.policyUrl||'').trim()){ return {type:'url', value:String(l.policyUrl).trim()}; } if(getPrivacyPolicyText()){ return {type:'dialog', value:''}; } return null; } function renderCookieBanner(){ const l=s.legal||{}; if(l.showCookieBanner===false) return ''; const target=cookiePolicyTarget(); const policyLink=target ? (target.type==='url' ? `Подробнее` : ``) : ''; return ``; } function initCookieBanner(){ const banner=document.getElementById('cookieBanner'); if(!banner) return; const key='unicorn_cookie_notice_v1'; try{ if(localStorage.getItem(key)==='accepted'){ banner.remove(); return; } }catch(_){} requestAnimationFrame(()=>banner.classList.add('is-visible')); banner.querySelector('.cookie-accept')?.addEventListener('click',()=>{ try{localStorage.setItem(key,'accepted')}catch(_){} banner.classList.remove('is-visible'); setTimeout(()=>banner.remove(),220); }); banner.querySelector('.cookie-policy-open')?.addEventListener('click',()=>{ const policy=getPrivacyPolicyText(); if(!policy)return; const dlg=ensurePrivacyDialog(); const body=dlg.querySelector('.public-privacy-text'); body.innerHTML=''; policy.split(/\n{2,}/).forEach(chunk=>{ const p=document.createElement('p'); p.textContent=chunk.trim(); body.appendChild(p); }); if(!dlg.open)dlg.showModal(); }); } const R={ header:d=>{ const brand=(d.brand||'').trim()||(s.quiz?.title||'').trim()||'Компания'; const tagline=(d.tagline||'').trim(), phone=(d.phone||'').trim(), msg=(d.messenger||'').trim()||'Связаться', loc=(d.location||'').trim(); const mobileLogo=d.mobileLogo||d.logo||'', mlz=Math.max(1,Number(d.mobileLogoZoom)||1.35), mlx=Math.min(100,Math.max(0,Number(d.mobileLogoX)||50)), mly=Math.min(100,Math.max(0,Number(d.mobileLogoY)||50)); const lz=Math.min(2,Math.max(.5,Number(d.logoCropZoom)||1)), lx=Math.min(100,Math.max(0,Number(d.logoCropX)||50)), ly=Math.min(100,Math.max(0,Number(d.logoCropY)||50)); const logoFormat=d.logoDesktopFormat||'wide', logoCropped=d.logoDesktopCropped===true; const squareTaglineMode=d.squareTaglineMode||'right'; const desktopManual=d.logoManualCropEnabled===true; const mcx=Number(d.logoManualCropX)||0,mcy=Number(d.logoManualCropY)||0,mcw=Math.max(1,Number(d.logoManualCropW)||100),mch=Math.max(1,Number(d.logoManualCropH)||100),mcr=Math.max(.2,Number(d.logoManualCropRatio)||1); const mobileManual=d.mobileLogoManualCropEnabled===true; const mobileManualFormat=mobileManual&&d.mobileLogoManualCropFormat==='wide'?'wide':'square'; const mmx=Number(d.mobileLogoManualCropX)||0,mmy=Number(d.mobileLogoManualCropY)||0,mmw=Math.max(1,Number(d.mobileLogoManualCropW)||100),mmh=Math.max(1,Number(d.mobileLogoManualCropH)||100); const squareLogo=desktopManual?mcr<=1.35:logoFormat==='square'; const action=d.messengerAction||'anchor'; let attrs=''; if(action==='anchor') attrs=`data-anchor="${E(d.messengerAnchor||'quiz')}"`; else if(action==='telegram'){ const rawTelegram=String(d.messengerUrl||'').trim(); const telegramUrl=/^https?:\/\//i.test(rawTelegram)?rawTelegram:(rawTelegram?`https://t.me/${rawTelegram.replace(/^@/,'').replace(/^t\.me\//i,'')}`:''); attrs=`data-url="${E(telegramUrl)}" data-external="1" data-metrika-goal="messenger"`; } else if(action==='url') attrs=`data-url="${E(d.messengerUrl||'')}" data-external="1"`; else if(action==='phone') attrs=`data-phone="${E(phone)}" data-metrika-goal="call"`; return ``; }, hero:d=>{ const title=(d.title||'').trim()||(s.quiz?.title||'').trim()||'Качественные решения для вашего бизнеса', subtitle=(d.subtitle||'').trim(), kicker=(d.kicker||'').trim(), btn=(d.button||'').trim(); const trust=(d.trust||'').trim(), heroImg=(d.image||'').trim()||'https://images.unsplash.com/photo-1497366216548-37526070297c?auto=format&fit=crop&w=1600&q=85'; const heroVideo=(d.video||'').trim(), heroIsVideo=d.mediaType==='video'&&heroVideo; const promoMode=d.imagePromoMode==='image'?'image':'text'; const promoKicker=(d.imagePromoKicker||'').trim(), promoText=(d.imagePromoText||'').trim(); const showPromo=d.imagePromoEnabled!==false&&(promoMode==='text'||String(d.imagePromoImage||'').trim()); const hx=Math.min(100,Math.max(0,Number(d.imageCropX)||50)),hy=Math.min(100,Math.max(0,Number(d.imageCropY)||50)),hz=Math.min(2,Math.max(.5,Number(d.imageCropZoom)||1)); const custom=d.customTypography===true, tDesk=Math.min(96,Math.max(32,Number(d.titleSizeDesktop)||62)), tMob=Math.min(60,Math.max(26,Number(d.titleSizeMobile)||38)), sDesk=Math.min(30,Math.max(13,Number(d.subtitleSizeDesktop)||16)), sMob=Math.min(24,Math.max(13,Number(d.subtitleSizeMobile)||15)); const promoSize=['small','medium','large'].includes(d.imagePromoSize)?d.imagePromoSize:'medium', promoBg=d.imagePromoBackground!==false, promoFit=d.imagePromoFit==='cover'?'cover':'contain', promoScale=Math.min(160,Math.max(60,Number(d.imagePromoScale)||100))/100, pix=Math.min(100,Math.max(0,Number(d.imagePromoImageCropX??50))), piy=Math.min(100,Math.max(0,Number(d.imagePromoImageCropY??50))), piz=Math.min(2,Math.max(.5,Number(d.imagePromoImageCropZoom??1))); return `
${kicker?`✦ ${E(kicker)}`:''}

${E(title)}

${subtitle?`

${E(subtitle)}

`:''}
${trust?`
${iconOf(d.trustIcon||'check')?`${E(iconOf(d.trustIcon||'check'))}`:''}${E(trust)}
`:''}
${heroIsVideo?``:``}${!showPromo?'':(promoMode==='image'&&d.imagePromoImage?`
`:`
${E(promoKicker||'🎁 Только сейчас')}${E(promoText||'Бесплатный предварительный расчёт проекта')}
`)}
`; }, promobar:d=>{if(!d)return '';const promoParts=i=>{const raw=String(d['item'+i]||'').trim(),parts=raw.split(/\n+|(?<=\.)\s*/).map(x=>x.trim()).filter(Boolean);return {title:String(d['itemTitle'+i]||'').trim()||(parts[0]||raw),text:String(d['itemText'+i]||'').trim()||(parts.length>1?parts.slice(1).join(' '):'')}};const hasAny=String(d.offer||'').trim()||[1,2,3].some(i=>{const p=promoParts(i);return p.title||p.text});if(!hasAny)return '';const iconStyle=d.iconStyle==='color'?'color':'simple';const card=i=>{const p=promoParts(i);if(!p.title&&!p.text)return '';if(d.variant!=='iconCards')return `${E(iconOf(d['icon'+i]||'check'))}${E(p.title||d['item'+i]||'')}`;const extended=!!p.text;return `${promoIconHtml(d['icon'+i]||'check',iconStyle)}${E(p.title)}${extended?`${E(p.text)}`:''}`};return `
${['benefitsOnly','iconCards'].includes(d.variant)?'':`
🎁 ${E(d.offer||'')}
`}
${card(1)}${card(2)}${card(3)}
`}, about:d=>{const paras=String(d.text||'').split(/\n\s*\n/).map(x=>x.trim()).filter(Boolean),ax=Math.min(100,Math.max(0,Number(d.imageCropX)||50)),ay=Math.min(100,Math.max(0,Number(d.imageCropY)||50)),az=Math.min(2,Math.max(.5,Number(d.imageCropZoom)||1)),max=Math.min(100,Math.max(0,Number(d.mobileImageCropX??50))),may=Math.min(100,Math.max(0,Number(d.mobileImageCropY??35))),maz=Math.min(2,Math.max(.5,Number(d.mobileImageCropZoom??1))),fit=d.imageFit==='contain'?'contain':'cover';return `
${d.mediaType==='video'?`
${videoCard({video:d.video||'',image:d.image||''})}
`:`
`}
${E(d.eyebrow)}

${E(d.title)}

${paras.map(p=>`

${E(p).replace(/\n/g,'
')}

`).join('')}
${(d.stats||[]).filter(x=>String(x.value||'').trim()&&String(x.label||'').trim()).length?`
${(d.stats||[]).filter(x=>String(x.value||'').trim()&&String(x.label||'').trim()).map(x=>`
${E(x.value)}${E(x.label)}
`).join('')}
`:''}
`}, quiz:d=>`
${E(d.eyebrow)}

${E(d.title)}

${E(d.subtitle)}

`, benefits:d=>`
Почему мы

${E(d.title)}

${(d.items||[]).map((x,i)=>`
${String(i+1).padStart(2,'0')}

${E(x.title)}

${E(x.text)}

`).join('')}
`, works:d=>{const items=d.items||[],layout=d.layout||'three',legacySlider=['slider','slider3','slider4'].includes(layout),navSlider=['two','three','vertical','wide'].includes(layout)&&d.navigation==='slider',isSlider=legacySlider||navSlider,moreLayout=layout==='more',showTitles=d.showTitles!==false,bg=d.cardBackground===true?'work-bg':'',fit=['contain','width','height'].includes(d.imageFit)?d.imageFit:'cover',sliderCount=layout==='slider4'?4:layout==='two'||layout==='wide'?2:layout==='vertical'?4:3,rowSettings=d.rowSettings||{};const gridCols=moreLayout?2:(layout==='two'?2:layout==='vertical'?4:layout==='wide'?2:3),maxCollapsedRows=2,collapsedVisible=items.length<=gridCols?items.length:Math.max(gridCols,Math.floor(Math.min(items.length,gridCols*maxCollapsedRows)/gridCols)*gridCols);const imgHtml=x=>{const cx=Math.min(100,Math.max(0,Number(x.imageCropX??x.cropX??50))),cy=Math.min(100,Math.max(0,Number(x.imageCropY??x.cropY??50))),cz=Math.min(2,Math.max(.5,Number(x.imageCropZoom??x.cropZoom??1)));return ``};const compareHtml=x=>{const before=x.beforeImage||'',after=x.afterImage||'';if(!before||!after)return `
Добавьте фото «До» и «После»
`;return `
${E((x.title||'Работа')+' — до')}
${E((x.title||'Работа')+' — после')}
ДоПосле
`};const card=(x,extra='',active='')=>`
${x.type==='video'?videoCard(x):x.type==='compare'?compareHtml(x):imgHtml(x)}${showTitles&&x.title?`${E(x.title)}`:''}
`;const usedRows=[...new Set(items.map(x=>Math.max(1,Number(x.row)||1)))].sort((a,b)=>a-b),multiRows=usedRows.length>1||Object.keys(rowSettings).length>0;const ratioClass=r=>{const q=(rowSettings[String(r)]||{}).ratio||'default';return q==='16x9'?'works-row-ratio-16x9':q==='4x3'?'works-row-ratio-4x3':q==='3x4'?'works-row-ratio-3x4':q==='1x1'?'works-row-ratio-1x1':q==='2x1'?'works-row-ratio-2x1':'works-row-ratio-default'};const renderRows=()=>usedRows.map((row,ri)=>{const rowItems=items.filter(x=>(Math.max(1,Number(x.row)||1))===row),cfg=rowSettings[String(row)]||{},mode=cfg.mode||(row===1&&isSlider?'slider':'grid'),cols=[1,2,3,4].includes(Number(cfg.columns))?Number(cfg.columns):(row===1?sliderCount:2),rc=ratioClass(row),mobileClass=cfg.mobileMode==='stack'?'works-row-mobile-stack':'works-row-mobile-swipe';if(mode==='slider')return `
${rowItems.map((x,i)=>card(x,'works-slide',i
`;return `
${rowItems.map(x=>card(x)).join('')}
`}).join('');return `
${d.eyebrow?`
${E(d.eyebrow)}
`:''}

${E(d.title)}

${d.subtitle?`

${E(d.subtitle)}

`:''}
${multiRows?renderRows():(isSlider?`
${items.map((x,i)=>card(x,'works-slide',i
`:`
${items.map((x,i)=>card(x,i>=collapsedVisible?'work-extra':'')).join('')}
${items.length>collapsedVisible?`
`:''}`)}
`}, textImage:d=>`
${E(d.kicker)}

${E(d.title)}

${E(d.text)}

`, steps:d=>`
Как работаем

${E(d.title)}

${(d.items||[]).map((x,i)=>`
${String(i+1).padStart(2,'0')}

${E(x.title)}

${E(x.text)}

`).join('')}
`, reviews:d=>{const shots=(d.items||[]).filter(x=>(x.type==='video'?x.video:x.image)),cols=[3,4,5].includes(Number(d.cardsPerRow))?Number(d.cardsPerRow):3;const cards=shots.map(x=>x.type==='video'?`
${videoCard(x)}${x.title?`${E(x.title)}`:''}
`:``).join('');return `
Отзывы

${E(d.title||'Отзывы клиентов')}

${E(d.subtitle||'')}

${d.mode==='embed'?`
${d.embed||''}
`:(d.layout==='slider'?`
${cards}
`:`
${cards}
`)}
`}, customCode:d=>`
${d.title?`

${E(d.title)}

`:''}
${d.code||''}
`, final:d=>`

${E(d.title)}

${E(d.subtitle)}

${E(d.phone||'')}
`, footer:d=>``}; let bs=[...(s.page.blocks||[])]; // Defensive normalization for public page: Footer is always last, // and Final CTA is always immediately before Footer/Promo-footer. const take=t=>bs.find(b=>b.type===t); const header=take('header'), hero=take('hero'), quiz=take('quiz'), promoBlock=take('promo'), finalBlock=take('final'), footer=take('footer'); const content=bs.filter(b=>!['header','hero','quiz','promo','final','footer'].includes(b.type)); let ordered=[]; if(header) ordered.push(header); if(hero) ordered.push(hero); if(promoBlock && s.page.promoPlacement!=='beforeFooter') ordered.push(promoBlock); if(quiz) ordered.push(quiz); ordered.push(...content); if(finalBlock) ordered.push(finalBlock); if(promoBlock && s.page.promoPlacement==='beforeFooter') ordered.push(promoBlock); if(footer) ordered.push(footer); const renderBlock=b=>{ if(b.type==='promo') return R.promobar?R.promobar(b.data||{}):''; return R[b.type]?R[b.type](b.data||{}):''; }; app.innerHTML=ordered.filter(b=>b.enabled!==false).map(renderBlock).join('') + renderCookieBanner(); initCookieBanner(); /* v159 review slider arrows */ document.querySelectorAll('.reviews-layout-slider .reviews-slider-wrap').forEach(wrap=>{ const rail=wrap.querySelector('.reviews-grid'); const prev=wrap.querySelector('.reviews-prev'); const next=wrap.querySelector('.reviews-next'); if(!rail)return; const step=()=>{ const card=rail.querySelector('.review-shot'); if(!card)return Math.max(280,rail.clientWidth); const gap=parseFloat(getComputedStyle(rail).gap||'14')||14; return card.getBoundingClientRect().width+gap; }; prev?.addEventListener('click',()=>rail.scrollBy({left:-step(),behavior:'smooth'})); next?.addEventListener('click',()=>rail.scrollBy({left:step(),behavior:'smooth'})); }); document.querySelectorAll('.footer-privacy-open').forEach(btn=>{ btn.addEventListener('click',()=>{ const policy=getPrivacyPolicyText(); if(!policy)return; const dlg=ensurePrivacyDialog(); const body=dlg.querySelector('.public-privacy-text'); body.innerHTML=''; policy.split(/\n{2,}/).forEach(chunk=>{ const p=document.createElement('p'); p.textContent=chunk.trim(); body.appendChild(p); }); if(!dlg.open)dlg.showModal(); }); }); document.querySelectorAll('.footer-consent-open').forEach(btn=>{ btn.addEventListener('click',()=>{ const text=getConsentText(); if(!text)return; const dlg=ensureConsentDialog(); const body=dlg.querySelector('.public-privacy-text'); body.innerHTML=''; text.split(/\n{2,}/).forEach(chunk=>{ const p=document.createElement('p'); p.textContent=chunk.trim(); body.appendChild(p); }); if(!dlg.open)dlg.showModal(); }); }); document.querySelectorAll('.brand:not(.brand-square-logo) .brand-logo-desktop').forEach(img=>{const apply=()=>{if(img.naturalWidth&&img.naturalHeight){const r=img.naturalWidth/img.naturalHeight;if(r>.82&&r<1.22)img.closest('.brand')?.classList.add('brand-square-auto')}};if(img.complete)apply();else img.addEventListener('load',apply,{once:true})}); // Execute trusted widget scripts only on the published page. In admin preview they stay inert. if(!new URLSearchParams(location.search).has('preview')){ document.querySelectorAll('.custom-code-content script, .reviews-embed script').forEach(oldScript=>{ const fresh=document.createElement('script'); [...oldScript.attributes].forEach(a=>fresh.setAttribute(a.name,a.value)); fresh.textContent=oldScript.textContent||''; oldScript.replaceWith(fresh); }); } function quizUploadPreviewHtml(items){ return (Array.isArray(items)?items:[]).map((url,i)=>`
Загруженное фото
`).join(''); } async function uploadEmbeddedQuizImages(files,maxCount){ const selected=[...files].slice(0,Math.max(1,maxCount||5)); if(!selected.length)return []; for(const file of selected){ const mime=String(file.type||'').toLowerCase(); const name=String(file.name||'').toLowerCase(); const supported=/^image\/(jpeg|jpg|png|webp|gif)$/i.test(mime)||/\.(jpe?g|png|webp|gif)$/i.test(name); if(!supported)throw new Error('Поддерживаются JPG, PNG, WebP и GIF'); if(file.size>5*1024*1024)throw new Error('Одна фотография должна быть не больше 5 МБ'); } const fd=new FormData(); selected.forEach(file=>fd.append('images',file,file.name||'photo')); let r; try{ r=await fetch('/api/public/project/'+encodeURIComponent(pageSlug)+'/quiz-images',{method:'POST',body:fd,credentials:'same-origin'}); }catch(_){ throw new Error('Не удалось связаться с сервером. Проверьте интернет и попробуйте ещё раз.'); } const raw=await r.text(); let d={};try{d=raw?JSON.parse(raw):{}}catch(_){} if(!r.ok)throw new Error(d.error||('Не удалось загрузить фотографию ('+r.status+')')); const urls=Array.isArray(d.urls)?d.urls:[]; if(!urls.length)throw new Error('Сервер не вернул загруженную фотографию'); return urls.map(url=>new URL(url,location.origin).href); } function rq(){let h=document.getElementById('qbody');if(!h)return;let qs=s.quiz.questions||[];if(qi${E(a||'')}`; }else if(q.type==='number'){ f=``; }else if(isUpload){ const uploaded=Array.isArray(a)?a:[]; const maxUpload=[1,2,3,5,10].includes(Number(q.uploadMax))?Number(q.uploadMax):5; f=`
Добавьте фотографииДо ${maxUpload} ${maxUpload===1?'фотографии':'фотографий'} · JPG, PNG, WebP или GIF · до 5 МБ
${quizUploadPreviewHtml(uploaded)}
${uploaded.length?`Загружено: ${uploaded.length} из ${maxUpload}`:''}
`; }else{ f=`${isMulti?`
✓Можно выбрать несколько вариантов${Array.isArray(a)&&a.length?`Выбрано: ${a.length}`:'Выберите подходящие ответы'}
`:''}
${(q.options||[]).map((o,oi)=>{const img=(q.optionImages||[])[oi]||'';const selected=isMulti?Array.isArray(a)&&a.includes(o):a===o;return ``}).join('')}
`; } h.innerHTML=`
Шаг ${qi+1} из ${qs.length}

${E(q.title)}

${String(q.description||'').trim()?`

${E(q.description)}

`:''}${q.image?`
`:''}${f}
`; const nextBtn=h.querySelector('.next'); const hasRequiredAnswer=()=>{if(!q.required)return true;const v=qa[qi];if(['multi','upload'].includes(q.type))return Array.isArray(v)&&v.length>0;if(q.type==='text')return String(h.querySelector('#txt')?.value??v??'').trim().length>0;if(q.type==='number')return String(h.querySelector('#num')?.value??v??'').trim().length>0;return String(v??'').trim().length>0}; const syncNext=()=>{if(nextBtn)nextBtn.disabled=!hasRequiredAnswer()}; if(!isUpload&&q.type!=='text'&&q.type!=='number')h.querySelectorAll('.answer').forEach(b=>b.onclick=()=>{ const value=b.dataset.a; if(isMulti){ const current=Array.isArray(qa[qi])?[...qa[qi]]:[]; const pos=current.indexOf(value); if(pos>=0)current.splice(pos,1);else current.push(value); qa[qi]=current; b.classList.toggle('selected',current.includes(value)); const count=h.querySelector('#multiChoiceCount'); if(count)count.textContent=current.length?`Выбрано: ${current.length}`:'Выберите подходящие ответы'; syncNext(); return; } qa[qi]=value; h.querySelectorAll('.answer').forEach(x=>x.classList.toggle('selected',x===b)); syncNext(); if(isGift)return; const isSingle=q.type==='single'||!q.type||q.type==='radio'; if(isSingle)setTimeout(()=>{qi++;rq()},220); }); if(isUpload){ const input=h.querySelector('#quizUploadFiles'),replaceInput=h.querySelector('#quizReplaceFile'),drop=h.querySelector('#quizUploadDrop'),previews=h.querySelector('#quizUploadPreviews'),status=h.querySelector('#quizUploadStatus'); const maxUpload=[1,2,3,5,10].includes(Number(q.uploadMax))?Number(q.uploadMax):5; let replaceIndex=-1; const redraw=()=>{const arr=Array.isArray(qa[qi])?qa[qi]:[];previews.innerHTML=quizUploadPreviewHtml(arr);drop.classList.toggle('has-files',arr.length>0);status.textContent=arr.length?`Загружено: ${arr.length} из ${maxUpload}`:'';status.classList.remove('error');previews.querySelectorAll('[data-upload-remove]').forEach(btn=>btn.onclick=()=>{const current=[...(qa[qi]||[])];current.splice(Number(btn.dataset.uploadRemove),1);qa[qi]=current;redraw();syncNext()});previews.querySelectorAll('[data-upload-replace]').forEach(btn=>btn.onclick=()=>{replaceIndex=Number(btn.dataset.uploadReplace);replaceInput.click()});syncNext()}; const process=async files=>{const current=Array.isArray(qa[qi])?[...qa[qi]]:[];const room=maxUpload-current.length;if(room<=0){status.textContent=`Можно загрузить не больше ${maxUpload}`;status.classList.add('error');return}status.textContent='Загружаем…';status.classList.remove('error');try{const urls=await uploadEmbeddedQuizImages([...files].slice(0,room),room);qa[qi]=current.concat(urls);redraw()}catch(err){status.textContent=err.message||'Ошибка загрузки';status.classList.add('error')}}; input.onchange=e=>{process(e.target.files);e.target.value=''}; replaceInput.onchange=async e=>{const file=e.target.files?.[0];if(!file||replaceIndex<0)return;e.target.value='';status.textContent='Заменяем…';status.classList.remove('error');try{const urls=await uploadEmbeddedQuizImages([file],1);const current=[...(qa[qi]||[])];if(urls[0]&¤t[replaceIndex])current[replaceIndex]=urls[0];qa[qi]=current;redraw()}catch(err){status.textContent=err.message||'Ошибка загрузки';status.classList.add('error')}finally{replaceIndex=-1}}; drop.ondragover=e=>{e.preventDefault();drop.classList.add('dragover')};drop.ondragleave=()=>drop.classList.remove('dragover');drop.ondrop=e=>{e.preventDefault();drop.classList.remove('dragover');process(e.dataTransfer.files)}; redraw(); } if(q.type==='text')h.querySelector('#txt')?.addEventListener('input',syncNext); if(q.type==='number')h.querySelector('#num')?.addEventListener('input',syncNext); syncNext(); h.querySelector('.prev').onclick=()=>{if(qi){qi--;rq()}}; h.querySelector('.next').onclick=()=>{ if(q.type==='text')qa[qi]=h.querySelector('#txt')?.value||''; if(q.type==='number')qa[qi]=h.querySelector('#num')?.value||''; const emptyArray=['multi','upload'].includes(q.type)&&(!Array.isArray(qa[qi])||qa[qi].length===0); if(q.required&&(!qa[qi]||emptyArray)){ const el=h.querySelector('.answers')||h.querySelector('.quiz-upload-drop')||h.querySelector('#txt')||h.querySelector('#num'); if(el){el.classList.add('validation-error');setTimeout(()=>el.classList.remove('validation-error'),1400)} return; } qi++;rq(); } }else{document.getElementById('prog').style.width='100%';{ if(!window.__contactsTracked){window.__contactsTracked=true;trackEvent('contacts');} const methods=[]; if(s.quiz.contactMethodPhone!==false)methods.push(['phone','Позвонить']); if(s.quiz.contactMethodTelegram!==false)methods.push(['telegram','Telegram']); if(s.quiz.contactMethodWhatsapp!==false)methods.push(['whatsapp','WhatsApp']); if(s.quiz.contactMethodMax!==false)methods.push(['max','MAX']); if(s.quiz.contactMethodVk===true)methods.push(['vk','ВКонтакте']); if(!methods.length)methods.push(['phone','Позвонить']); const consentOn=s.legal?.showConsent!==false; const legalText=s.legal?.formText||'Нажимая кнопку, вы соглашаетесь на обработку персональных данных.'; const contactTitle=String(s.quiz?.contactTitle||'Как с вами связаться?').trim()||'Как с вами связаться?'; const contactHint=String(s.quiz?.contactHint||'').trim(); const summaryItems=[]; qs.forEach((question,index)=>{const value=qa[index];if(question.type==='upload'&&Array.isArray(value)&&value.length)summaryItems.push(`📷 ${value.length} фото загружено`);if(question.type==='gift'&&value)summaryItems.push(`🎁 ${E(value)}`)}); h.innerHTML=`
Последний шаг

${E(contactTitle)}

${contactHint?`

${E(contactHint)}

`:''}${summaryItems.length?`
${summaryItems.join('')}
`:''}
${methods.map((m,i)=>``).join('')}
${consentOn?``:''}
`; const consentEl=document.getElementById('consent');if(consentEl)consentEl.checked=false; let selectedMethod=methods[0][0]; const nameEl=document.getElementById('nm'),phEl=document.getElementById('ph'),hint=document.getElementById('methodHint'); nameEl.addEventListener('input',()=>{const v=sanitizePersonName(nameEl.value);if(nameEl.value!==v)nameEl.value=v;}); phEl.addEventListener('input',()=>{if(!['telegram','vk'].includes(selectedMethod))phEl.value=formatRuPhone(phEl.value);}); const syncMethod=()=>{ const isTelegram=selectedMethod==='telegram', isVk=selectedMethod==='vk', isTextContact=isTelegram||isVk; phEl.inputMode=isTextContact?'text':'tel'; phEl.placeholder=isTelegram?'@username или телефон':isVk?'vk.com/username или username':'+7 (999) 123-45-67'; if(!isTextContact&&phEl.value)phEl.value=formatRuPhone(phEl.value); hint.textContent=isTelegram?'Укажите Telegram username или номер телефона.':isVk?'Укажите ник или полную ссылку на профиль ВКонтакте.':selectedMethod==='whatsapp'?'Укажите российский номер, к которому привязан WhatsApp.':selectedMethod==='max'?'Укажите российский номер телефона для MAX.':'Укажите российский номер телефона.'; }; h.querySelectorAll('.contact-method').forEach(btn=>btn.onclick=()=>{ selectedMethod=btn.dataset.method; h.querySelectorAll('.contact-method').forEach(x=>x.classList.toggle('selected',x===btn)); syncMethod(); }); syncMethod(); h.querySelector('.prev').onclick=()=>{qi=Math.max(0,qs.length-1);rq()}; document.getElementById('send').onclick=async ()=>{ const contact=phEl.value.trim(); const sendBtn=document.getElementById('send'); const isTextContact=['telegram','vk'].includes(selectedMethod); const vkOk=selectedMethod!=='vk'||/^(?:https?:\/\/)?(?:www\.)?vk\.(?:com|ru)\/[A-Za-z0-9_.-]+\/?(?:[?#].*)?$|^@?[A-Za-z0-9_.-]{2,64}$/.test(contact); if(!contact || (!isTextContact&&!validRuPhone(contact)) || !vkOk){ phEl.style.borderColor='#ef4444'; hint.textContent=selectedMethod==='telegram'?'Укажите Telegram username или номер телефона.':selectedMethod==='vk'?'Укажите ник ВКонтакте или ссылку вида vk.com/username.':'Введите номер полностью: +7 (999) 123-45-67'; phEl.focus(); return; } phEl.style.borderColor=''; if(consentOn&&!document.getElementById('consent').checked){ const consentLabel=document.querySelector('.public-consent'); if(consentLabel){ consentLabel.classList.add('validation-error'); setTimeout(()=>consentLabel.classList.remove('validation-error'),1600); } return; } if(!(await requireHumanSlider())) return; sendBtn.disabled=true; sendBtn.textContent='Отправка...'; const slug=pageSlug; markQuizStarted();const lead={date:new Date().toISOString(),name:document.getElementById('nm').value.trim(),phone:['telegram','vk'].includes(selectedMethod)?'':contact,contact,contactMethod:selectedMethod,answers:qa,visitorId:__visitorId,company_website:document.getElementById('company_website')?.value||'',antiFraud:collectAntiFraudMeta(),...collectLeadAttribution()}; try { await submitPublicLead(lead); } catch(e) { console.error('Server lead error:',e); sendBtn.disabled=false; sendBtn.textContent=s.quiz.contactButton||'Отправить'; hint.textContent='Не удалось отправить заявку. Попробуйте ещё раз.'; hint.style.color='#ef4444'; return; } try{await notifyLead(lead)}catch(e){console.warn('Webhook notification error:',e)} s.leads=s.leads||[]; s.leads.unshift(lead); try { localStorage.setItem(KEY,JSON.stringify(s)); } catch(e){} window.location.href='/thanks'; } }}}rq();document.querySelectorAll('.jump').forEach(b=>b.onclick=()=>document.querySelector('.quiz-section')?.scrollIntoView({behavior:'smooth'})); document.querySelectorAll('.header-cta').forEach(b=>b.onclick=()=>{const a=b.dataset.anchor,u=b.dataset.url,p=b.dataset.phone,external=b.dataset.external==='1';if(u){window.open(u,external?'_blank':'_self','noopener')}else if(p){location.href='tel:'+p.replace(/[^+\d]/g,'')}else if(a){document.getElementById(a)?.scrollIntoView({behavior:'smooth'})}}); function initWorkComparisons(){ const clampCompare=(n,min,max)=>Math.min(max,Math.max(min,n)); document.querySelectorAll('[data-compare]').forEach(compare=>{ if(compare.dataset.compareReady==='1')return; compare.dataset.compareReady='1'; const handle=compare.querySelector('.compare-handle'); if(!handle)return; const setFromClientX=x=>{const r=compare.getBoundingClientRect();if(!r.width)return;const pct=clampCompare((x-r.left)/r.width*100,0,100);compare.style.setProperty('--compare',pct+'%');handle.setAttribute('aria-valuenow',String(Math.round(pct)))}; let dragging=false; compare.addEventListener('pointerdown',e=>{dragging=true;try{compare.setPointerCapture(e.pointerId)}catch(_){}setFromClientX(e.clientX);e.preventDefault()}); compare.addEventListener('pointermove',e=>{if(!dragging)return;setFromClientX(e.clientX);e.preventDefault()}); const stop=e=>{if(!dragging)return;dragging=false;try{compare.releasePointerCapture(e.pointerId)}catch(_){}}; compare.addEventListener('pointerup',stop);compare.addEventListener('pointercancel',stop); handle.addEventListener('keydown',e=>{let v=Number(handle.getAttribute('aria-valuenow')||50);if(e.key==='ArrowLeft')v-=2;else if(e.key==='ArrowRight')v+=2;else return;e.preventDefault();v=clampCompare(v,0,100);compare.style.setProperty('--compare',v+'%');handle.setAttribute('aria-valuenow',String(v))}); }); } initWorkComparisons(); document.querySelectorAll('.works-more').forEach(btn=>{ const section=btn.closest('.works'); const extras=[...section.querySelectorAll('.work-extra')]; btn.onclick=()=>{ const hasHidden=extras.some(el=>!el.classList.contains('is-revealed')); if(hasHidden){ extras.forEach(el=>el.classList.add('is-revealed')); section.classList.add('works-expanded'); btn.innerHTML='Скрыть работы ↑'; }else{ extras.forEach(el=>el.classList.remove('is-revealed')); section.classList.remove('works-expanded'); btn.innerHTML='Ещё работы ↓'; section.scrollIntoView({behavior:'smooth',block:'start'}); } }; }); // Click-to-open gallery images without loading a second heavy original. const workLightbox=document.createElement('dialog'); workLightbox.className='work-lightbox'; workLightbox.innerHTML=''; document.body.appendChild(workLightbox); document.querySelectorAll('.work-image-open').forEach(btn=>btn.addEventListener('click',()=>{ const src=btn.dataset.workImage;if(!src)return; workLightbox.querySelector('img').src=src;workLightbox.showModal(); })); workLightbox.querySelector('.work-lightbox-close').onclick=()=>workLightbox.close(); workLightbox.addEventListener('click',e=>{if(e.target===workLightbox)workLightbox.close()}); workLightbox.addEventListener('close',()=>{const img=workLightbox.querySelector('img');if(img)img.removeAttribute('src')}); document.querySelectorAll('.review-image-open').forEach(btn=>btn.addEventListener('click',()=>{const src=btn.dataset.reviewImage;if(!src)return;workLightbox.querySelector('img').src=src;workLightbox.showModal()})); document.querySelectorAll('.works-slider-wrap').forEach(wrap=>{ const slider=wrap.querySelector('.works-slider'); const slides=[...wrap.querySelectorAll('.works-slide')]; if(!slider||!slides.length)return; let index=0; let suppressClick=false; const desktopCount=Number(wrap.dataset.count||3); const visibleCount=()=>{ if(window.innerWidth<=560) return 1; if(window.innerWidth<=900) return Math.min(2,slides.length); return Math.min(desktopCount,slides.length); }; const show=i=>{ const total=slides.length; const count=visibleCount(); if(!total)return; index=((i%total)+total)%total; // Carousel window wraps around, so arrows/drag still work even when // total === visibleCount (all cards stay visible but their order cycles). const visible=new Set(); for(let k=0;k{ slide.classList.toggle('is-active',visible.has(n)); const rel=(n-index+total)%total; slide.style.order=visible.has(n)?String(rel):String(total+rel); }); const hide=total<=1; wrap.querySelectorAll('.works-slider-arrow').forEach(a=>a.style.visibility=hide?'hidden':'visible'); }; const goPrev=()=>show(index-1); const goNext=()=>show(index+1); wrap.querySelector('.works-prev')?.addEventListener('click',e=>{ e.preventDefault(); e.stopPropagation(); goPrev(); }); wrap.querySelector('.works-next')?.addEventListener('click',e=>{ e.preventDefault(); e.stopPropagation(); goNext(); }); // Touch swipe: change only the carousel index. Do not translate/scale the page. let touchStartX=0,touchStartY=0,touchLastX=0,touchHorizontal=false; slider.addEventListener('touchstart',e=>{ if(!e.touches||e.touches.length!==1)return; touchStartX=touchLastX=e.touches[0].clientX; touchStartY=e.touches[0].clientY; touchHorizontal=false; },{passive:true}); slider.addEventListener('touchmove',e=>{ if(!e.touches||e.touches.length!==1)return; touchLastX=e.touches[0].clientX; const dx=touchLastX-touchStartX; const dy=e.touches[0].clientY-touchStartY; if(!touchHorizontal && Math.abs(dx)>8 && Math.abs(dx)>Math.abs(dy)*1.15){ touchHorizontal=true; } if(touchHorizontal)e.preventDefault(); },{passive:false}); slider.addEventListener('touchend',()=>{ if(!touchHorizontal)return; const dx=touchLastX-touchStartX; if(Math.abs(dx)>=34){ suppressClick=true; dx<0?goNext():goPrev(); setTimeout(()=>{suppressClick=false},180); } touchHorizontal=false; },{passive:true}); // Desktop mouse drag. let drag=false,dragStartX=0,dragStartY=0,dragLastX=0,dragHorizontal=false; slider.addEventListener('pointerdown',e=>{ if(e.pointerType==='touch')return; // touch is handled above if(e.button!==0)return; drag=true; dragStartX=dragLastX=e.clientX; dragStartY=e.clientY; dragHorizontal=false; // Do not preventDefault here: a normal click on a work must still open the lightbox. }); slider.addEventListener('pointermove',e=>{ if(!drag||e.pointerType==='touch')return; dragLastX=e.clientX; const dx=dragLastX-dragStartX; const dy=e.clientY-dragStartY; if(!dragHorizontal && Math.abs(dx)>7 && Math.abs(dx)>Math.abs(dy)*1.1){ dragHorizontal=true; slider.classList.add('is-dragging'); try{slider.setPointerCapture(e.pointerId)}catch(_){} } if(dragHorizontal)e.preventDefault(); }); const finishPointer=e=>{ if(!drag)return; const dx=dragLastX-dragStartX; if(dragHorizontal&&Math.abs(dx)>=34){ suppressClick=true; dx<0?goNext():goPrev(); setTimeout(()=>{suppressClick=false},180); } drag=false; dragHorizontal=false; slider.classList.remove('is-dragging'); try{slider.releasePointerCapture(e.pointerId)}catch(_){} }; slider.addEventListener('pointerup',finishPointer); slider.addEventListener('pointercancel',finishPointer); slider.addEventListener('click',e=>{ if(!suppressClick)return; e.preventDefault(); e.stopPropagation(); e.stopImmediatePropagation?.(); },true); window.addEventListener('resize',()=>show(index)); show(0); }); function ensureVideoModal(){ let modal=document.getElementById('siteVideoModal'); if(modal)return modal; modal=document.createElement('div');modal.id='siteVideoModal';modal.className='site-video-modal';modal.innerHTML='
'; document.body.appendChild(modal); modal.addEventListener('click',e=>{if(e.target===modal||e.target.closest('.site-video-modal-close')){modal.classList.remove('is-open');modal.querySelector('iframe').src='';document.body.style.overflow=''}}); return modal; } document.addEventListener('click',e=>{ const btn=e.target.closest('.video-modal-open');if(!btn)return; const modal=ensureVideoModal();modal.querySelector('iframe').src=btn.dataset.videoUrl||'';modal.classList.add('is-open');document.body.style.overflow='hidden'; }); \n\n