const FORM_ENDPOINT = '/api/leads/medical-centers-no-physio'; const header = document.getElementById('header'); const updateHeader = () => header.classList.toggle('scrolled', window.scrollY > 18); updateHeader(); window.addEventListener('scroll', updateHeader, {passive:true}); const revealObserver = 'IntersectionObserver' in window ? new IntersectionObserver((entries, observer) => { entries.forEach(entry => { if (entry.isIntersecting) { entry.target.classList.add('is-visible'); observer.unobserve(entry.target); } }); }, {threshold:.12, rootMargin:'0px 0px -42px'}) : null; document.querySelectorAll('[data-reveal]').forEach((el, i) => { el.style.transitionDelay = `${Math.min((i % 5) * 55, 220)}ms`; if (revealObserver) revealObserver.observe(el); else el.classList.add('is-visible'); }); const counterObserver = 'IntersectionObserver' in window ? new IntersectionObserver((entries, observer) => { entries.forEach(entry => { if (!entry.isIntersecting) return; const el = entry.target; const target = parseFloat(el.dataset.counter || '0'); const suffix = el.dataset.suffix || ''; const prefix = el.dataset.prefix || ''; const decimals = String(target).includes('.') ? 1 : 0; const start = performance.now(); const duration = 950; const tick = now => { const p = Math.min(1, (now - start) / duration); const eased = 1 - Math.pow(1 - p, 3); const v = target * eased; el.textContent = prefix + (target === 2.41 ? v.toFixed(2) : v.toFixed(decimals)).replace('.', ',') + suffix; if (p < 1) requestAnimationFrame(tick); }; requestAnimationFrame(tick); observer.unobserve(el); }); }, {threshold:.6}) : null; document.querySelectorAll('[data-counter]').forEach(el => counterObserver ? counterObserver.observe(el) : null); // Save UTM parameters into hidden fields. const params = new URLSearchParams(location.search); ['utm_source','utm_medium','utm_campaign','utm_content','utm_term'].forEach(key => { const input = document.querySelector(`[name="${key}"]`); let stored = ''; try { stored = sessionStorage.getItem(key) || ''; } catch (e) { stored = ''; } if (input) input.value = params.get(key) || stored; if (params.get(key)) { try { sessionStorage.setItem(key, params.get(key)); } catch (e) {} } }); const phone = document.getElementById('phone'); phone.addEventListener('input', () => { let d = phone.value.replace(/\D/g,'').slice(0,11); if (!d) return; if (d[0] === '8') d = '7' + d.slice(1); if (d[0] !== '7') d = '7' + d; let value = '+7'; if (d.length > 1) value += ' ' + d.slice(1,4); if (d.length >= 5) value += ' ' + d.slice(4,7); if (d.length >= 8) value += '-' + d.slice(7,9); if (d.length >= 10) value += '-' + d.slice(9,11); phone.value = value; }); function setStatus(message, type) { const status = document.getElementById('formStatus'); status.textContent = message; status.className = `form-status show ${type}`; } async function submitLead(event) { event.preventDefault(); const form = event.currentTarget; const button = document.getElementById('submitBtn'); if (form.website.value) return false; if (!form.checkValidity()) { form.reportValidity(); setStatus('Проверьте обязательные поля формы.', 'error'); return false; } const phoneDigits = form.phone.value.replace(/\D/g,''); if (phoneDigits.length < 11) { setStatus('Укажите полный номер телефона.', 'error'); form.phone.focus(); return false; } const payload = Object.fromEntries(new FormData(form).entries()); payload.created_at = new Date().toISOString(); payload.page_url = location.href; payload.page_title = document.title; button.disabled = true; button.textContent = 'Отправляем…'; try { if (location.protocol === 'file:') throw new Error('preview'); const response = await fetch(FORM_ENDPOINT, { method:'POST', headers:{'Content-Type':'application/json'}, body:JSON.stringify(payload) }); if (!response.ok) throw new Error(`HTTP ${response.status}`); setStatus('Спасибо. Заявка отправлена — специалист свяжется с вами для предварительного аудита.', 'success'); form.reset(); } catch (error) { console.warn('Lead endpoint is not connected in preview:', error); if (location.protocol === 'file:' || location.hostname === 'localhost') { setStatus('Демонстрационный режим: форма заполнена корректно. Для публикации подключите FORM_ENDPOINT к CRM или webhook.', 'success'); } else { setStatus('Не удалось отправить заявку. Попробуйте ещё раз или свяжитесь с компанией по телефону.', 'error'); } } finally { button.disabled = false; button.innerHTML = `Получить предварительный аудит `; } return false; } window.submitLead = submitLead; /* ---- next bundled script ---- */ (function(){ const root=document.querySelector('[data-course-calculator]'); if(!root)return; const countInput=root.querySelector('#courseCalcCount'), checkInput=root.querySelector('#courseCalcCheck'), investmentInput=root.querySelector('#courseCalcInvestment'); const monthOutput=root.querySelector('[data-course-month]'), yearOutput=root.querySelector('[data-course-year]'), paybackOutput=root.querySelector('[data-course-payback]'), roiOutput=root.querySelector('[data-course-roi]'); const money=new Intl.NumberFormat('ru-RU',{maximumFractionDigits:0}); const decimal=new Intl.NumberFormat('ru-RU',{minimumFractionDigits:1,maximumFractionDigits:1}); function safeNumber(input){const value=Number(String(input.value||'').replace(/\s/g,'').replace(',','.'));return Number.isFinite(value)&&value>0?value:0} function updateCourseRevenue(){ const courses=safeNumber(countInput), averageCheck=safeNumber(checkInput), investment=safeNumber(investmentInput); const month=courses&&averageCheck?Math.round(courses*averageCheck):0; const year=month*12; monthOutput.textContent=month?money.format(month)+' ₽':'—'; yearOutput.textContent=year?money.format(year)+' ₽':'—'; if(month&&investment){const payback=investment/month; paybackOutput.textContent=decimal.format(payback)+' мес.'; const roi=((year-investment)/investment)*100; roiOutput.textContent=Number.isFinite(roi)?money.format(Math.round(roi))+'%':'—'} else {paybackOutput.textContent='—';roiOutput.textContent='—'} } [countInput,checkInput,investmentInput].forEach(el=>el&&el.addEventListener('input',updateCourseRevenue)); updateCourseRevenue(); })(); /* === EXO callback modal === */ (function(){ const modal=document.getElementById('exoCallbackModal'); const form=document.getElementById('exoCallbackForm'); if(!modal||!form) return; const triggers=[...document.querySelectorAll('.btn--call,.header-call')]; const closeEls=modal.querySelectorAll('[data-callback-close]'); const nameInput=document.getElementById('exoCallbackName'); const phoneInput=document.getElementById('exoCallbackPhone'); const submit=document.getElementById('exoCallbackSubmit'); const status=document.getElementById('exoCallbackStatus'); let lastFocused=null; function setStatus(message,type){ status.textContent=message; status.className='exo-callback__status is-show '+(type==='success'?'is-success':'is-error'); } function clearStatus(){ status.textContent=''; status.className='exo-callback__status'; } function openModal(event){ if(event) event.preventDefault(); lastFocused=document.activeElement; clearStatus(); modal.classList.add('is-open'); modal.setAttribute('aria-hidden','false'); document.body.classList.add('exo-callback-open'); setTimeout(()=>nameInput && nameInput.focus(),60); } function closeModal(){ modal.classList.remove('is-open'); modal.setAttribute('aria-hidden','true'); document.body.classList.remove('exo-callback-open'); if(lastFocused && typeof lastFocused.focus==='function') lastFocused.focus(); } triggers.forEach(el=>el.addEventListener('click',openModal)); closeEls.forEach(el=>el.addEventListener('click',closeModal)); document.addEventListener('keydown',e=>{ if(e.key==='Escape' && modal.classList.contains('is-open')) closeModal(); }); function formatPhone(){ let d=phoneInput.value.replace(/\D/g,'').slice(0,11); if(!d){phoneInput.value='';return} if(d[0]==='8') d='7'+d.slice(1); if(d[0]!=='7') d='7'+d; let value='+7'; if(d.length>1) value+=' '+d.slice(1,4); if(d.length>=5) value+=' '+d.slice(4,7); if(d.length>=8) value+='-'+d.slice(7,9); if(d.length>=10) value+='-'+d.slice(9,11); phoneInput.value=value; } phoneInput.addEventListener('input',formatPhone); form.addEventListener('submit',async function(event){ event.preventDefault(); clearStatus(); if(!form.checkValidity()){ form.reportValidity(); return; } const phoneDigits=phoneInput.value.replace(/\D/g,''); if(phoneDigits.length<11){ setStatus('Укажите полный номер телефона.','error'); phoneInput.focus(); return; } const payload=Object.fromEntries(new FormData(form).entries()); delete payload.consent; payload.lead_type='callback'; payload.source='header-call-popup'; payload.page_url=location.href; payload.page_title=document.title; payload.created_at=new Date().toISOString(); const original=submit.textContent; submit.disabled=true; submit.textContent='Отправляем…'; try{ if(location.protocol==='file:') throw new Error('preview'); const endpoint=form.dataset.endpoint; const response=await fetch(endpoint,{ method:'POST', headers:{'Content-Type':'application/json','Accept':'application/json'}, body:JSON.stringify(payload), credentials:'same-origin' }); if(!response.ok) throw new Error('HTTP '+response.status); setStatus('Спасибо. Заявка отправлена — специалист свяжется с вами.','success'); form.reset(); }catch(error){ console.warn('Callback form:',error); if(location.protocol==='file:' || location.hostname==='localhost'){ setStatus('Демонстрационный режим: форма работает. После загрузки на сервер заявка будет отправляться в подключённый обработчик.','success'); }else{ setStatus('Не удалось отправить заявку. Попробуйте ещё раз или позвоните нам по номеру +7 927 789-60-71.','error'); } }finally{ submit.disabled=false; submit.textContent=original; } }); })();