diff --git a/frontend/src/components/home/Hero.astro b/frontend/src/components/home/Hero.astro index d7a2119..51728ce 100644 --- a/frontend/src/components/home/Hero.astro +++ b/frontend/src/components/home/Hero.astro @@ -45,7 +45,7 @@ const lawyerImageUrl = "/images/home/avtourist-surgut.avif"; Юрист
20+ - ЛЕТ ОПЫТА В ХМАО + ЛЕТ ОПЫТА В СУДАХ
diff --git a/frontend/src/components/home/Reviews.astro b/frontend/src/components/home/Reviews.astro index 14fc746..7c87cee 100644 --- a/frontend/src/components/home/Reviews.astro +++ b/frontend/src/components/home/Reviews.astro @@ -75,6 +75,15 @@ const displayReviews = [...latestReviews, ...latestReviews, ...latestReviews]; display: flex; } + /* Центрирование карточки на мобильном */ + #sliderContainer { + scroll-snap-type: x mandatory; + } + + #sliderTrack { + scroll-snap-align: center; + } + /* Убедимся, что карточка растягивается на всю ширину слайда */ .card-item :global(.review-card) { width: 100%; diff --git a/frontend/src/components/home/Steps.astro b/frontend/src/components/home/Steps.astro index a65c948..f48c133 100644 --- a/frontend/src/components/home/Steps.astro +++ b/frontend/src/components/home/Steps.astro @@ -93,19 +93,19 @@ const { -
+
-
98%
+
0%
успешных дел
-
500+
+
0+
довольных клиентов
-
15 лет
+
0 лет
опыта
@@ -644,6 +644,32 @@ const { threshold: 0.15 }; + // Анимация счётчиков + function animateCounter(el: HTMLElement) { + const target = parseInt(el.dataset.target || '0'); + const suffix = el.dataset.suffix || ''; + const duration = 1500; + const startTime = performance.now(); + + // Easing функция (ease-out cubic) + const easeOutCubic = (t: number) => 1 - Math.pow(1 - t, 3); + + function update(currentTime: number) { + const elapsed = currentTime - startTime; + const progress = Math.min(elapsed / duration, 1); + const easedProgress = easeOutCubic(progress); + const currentValue = Math.round(easedProgress * target); + + el.textContent = `${currentValue}${suffix}`; + + if (progress < 1) { + requestAnimationFrame(update); + } + } + + requestAnimationFrame(update); + } + const observer = new IntersectionObserver((entries) => { entries.forEach(entry => { if (entry.isIntersecting) { @@ -652,6 +678,17 @@ const { setTimeout(() => { el.classList.add('is-visible'); + + // Если это stats-bar, запускаем анимацию счётчиков + if (el.id === 'stats-bar') { + const counters = el.querySelectorAll('.stat-number'); + counters.forEach((counter, index) => { + // Небольшая задержка между счётчиками для каскадного эффекта + setTimeout(() => { + animateCounter(counter as HTMLElement); + }, index * 150); + }); + } }, delay); observer.unobserve(el); diff --git a/frontend/src/components/layout/footer/Footer.astro b/frontend/src/components/layout/footer/Footer.astro index 937be30..a299cbd 100644 --- a/frontend/src/components/layout/footer/Footer.astro +++ b/frontend/src/components/layout/footer/Footer.astro @@ -1,4 +1,5 @@ --- +import { COMPANY } from '@constants'; const sectionsLinks = [ { label: 'Услуги', href: '/services' }, @@ -37,31 +38,31 @@ const currentYear = new Date().getFullYear().toString();

- - + + + + \ No newline at end of file diff --git a/frontend/src/components/services/AdvantagesList.astro b/frontend/src/components/services/AdvantagesList.astro new file mode 100644 index 0000000..22476fb --- /dev/null +++ b/frontend/src/components/services/AdvantagesList.astro @@ -0,0 +1,158 @@ +--- +const advantages = [ + { number: "01", title: "Специализация на автоспорах", desc: "Мы занимаемся исключительно делами по лишению прав. Это даёт глубокое знание практики судов Сургута." }, + { number: "02", title: "98% успешных дел", desc: "Наша статистика говорит сама за себя. Большинство дел заканчиваются в пользу клиента." }, + { number: "03", title: "Бесплатная консультация", desc: "Первичная консультация бесплатно. Честно скажем о перспективах дела до начала работы." }, + { number: "04", title: "Работа без предоплаты", desc: "Оплата только после положительного результата. Мы уверены в своих силах." }, + { number: "05", title: "Полное сопровождение", desc: "Берём на себя весь процесс — от анализа документов до получения прав в ГИБДД." }, + { number: "06", title: "Связь 24/7", desc: "На связи в любое время. Оперативно отвечаем на вопросы и информируем о ходе дела." } +]; +--- + +
+
+
+ +

Преимущества работы с нами

+

Доверьте своё дело профессионалам с многолетним опытом

+
+
+ {advantages.map((adv, index) => ( +
+
{adv.number}
+

{adv.title}

+

{adv.desc}

+
+ ))} +
+
+
+ + \ No newline at end of file diff --git a/frontend/src/components/services/ArticlesList.astro b/frontend/src/components/services/ArticlesList.astro new file mode 100644 index 0000000..c153516 --- /dev/null +++ b/frontend/src/components/services/ArticlesList.astro @@ -0,0 +1,174 @@ +--- +const articles = [ + { icon: "🍺", title: "Ст. 12.8 — Управление в состоянии опьянения", desc: "Оспариваем результаты освидетельствования, проверяем соблюдение процедуры медосвидетельствования.", chance: "Высокий шанс" }, + { icon: "🚫", title: "Ст. 12.26 — Отказ от медосвидетельствования", desc: "Анализируем законность требований о прохождении освидетельствования, ищем нарушения процедуры.", chance: "Высокий шанс" }, + { icon: "🚦", title: "Ст. 12.12 — Проезд на красный свет", desc: "Проверяем работу камер, анализируем материалы дела, ищем свидетелей.", chance: "Средний шанс" }, + { icon: "🔄", title: "Ст. 12.15 — Выезд на встречную полосу", desc: "Анализируем дорожную разметку, знаки, показания инспекторов.", chance: "Высокий шанс" }, + { icon: "🚗", title: "Ст. 12.27 — Оставление места ДТП", desc: "Доказываем отсутствие умысла или необходимость покинуть место по уважительной причине.", chance: "Средний шанс" }, + { icon: "⚠️", title: "Другие статьи", desc: "Работаем с любыми статьями КоАП — каждая ситуация индивидуальна и требует анализа.", chance: "Зависит от дела" } +]; +--- + +
+
+
+ +

По каким статьям КоАП возвращаем права

+

Оспариваем лишение по всем основным статьям КоАП РФ

+
+
+ {articles.map((article, index) => ( +
+
{article.icon}
+

{article.title}

+

{article.desc}

+ {article.chance} +
+ ))} +
+
+
+ + \ No newline at end of file diff --git a/frontend/src/components/services/FaqList.astro b/frontend/src/components/services/FaqList.astro new file mode 100644 index 0000000..59148b7 --- /dev/null +++ b/frontend/src/components/services/FaqList.astro @@ -0,0 +1,168 @@ +--- +const faqs = [ + { question: "Можно ли реально вернуть права после лишения?", answer: "Да, в 98% случаев нам удаётся вернуть права. Мы анализируем материалы дела и честно говорим о перспективах до начала работы." }, + { question: "Сколько времени занимает процесс?", answer: "В среднем процесс занимает от 2 недель до 3 месяцев в зависимости от сложности дела и загруженности судов." }, + { question: "Что если суд уже вынес решение?", answer: "Мы можем обжаловать решение в апелляционном суде в течение 10 дней с момента вынесения. Даже если срок прошёл, есть другие механизмы." }, + { question: "Какие гарантии результата?", answer: "Мы работаем без предоплаты — оплата только после положительного результата. Если не вернём права — вернём деньги." }, + { question: "Нужно ли моё присутствие в суде?", answer: "Нет, мы представляем ваши интересы по доверенности. Ваше присутствие не требуется — мы ведём дело полностью." }, + { question: "Что нужно для начала работы?", answer: "Достаточно позвонить нам или оставить заявку. На бесплатной консультации мы всё обсудим и начнём работу." } +]; +--- + +
+
+
+ +

Частые вопросы

+

Ответы на популярные вопросы о возврате прав

+
+
+ {faqs.map((faq, index) => ( +
+ + {faq.question} + + + + +
+

+
+
+ ))} +
+
+
+ + diff --git a/frontend/src/components/services/PricingPlans.astro b/frontend/src/components/services/PricingPlans.astro new file mode 100644 index 0000000..3346b67 --- /dev/null +++ b/frontend/src/components/services/PricingPlans.astro @@ -0,0 +1,247 @@ +--- +const plans = [ + { badge: "Базовый", title: "Консультация + анализ дела", price: "Бесплатно", features: ["Анализ материалов дела", "Оценка перспектив", "Рекомендации по действиям", "Консультация по статьям"], btnText: "Получить консультацию", btnClass: "" }, + { badge: "Популярный", title: "Полное сопровождение", price: "от 15 000 ₽", features: ["Всё из базового пакета", "Подготовка жалоб", "Представительство в суде", "Сбор доказательств", "Получение прав"], btnText: "Начать работу", btnClass: "gold", popular: true }, + { badge: "Премиум", title: "Сложные дела", price: "от 30 000 ₽", features: ["Всё из полного пакета", "Экспертиза", "Апелляция", "Работа с ГИБДД", "Приоритетная поддержка"], btnText: "Обсудить дело", btnClass: "" } +]; +--- + +
+
+
+ +

Сколько стоит возврат прав

+

Прозрачные цены без скрытых платежей

+
+
+
+
+ + \ No newline at end of file diff --git a/frontend/src/components/services/ServiceCategories.astro b/frontend/src/components/services/ServiceCategories.astro index 154b341..75d0c50 100644 --- a/frontend/src/components/services/ServiceCategories.astro +++ b/frontend/src/components/services/ServiceCategories.astro @@ -27,9 +27,9 @@ const { category: "Водительские права", services: [ { - title: "Возврат прав после лишения", + title: "Возврат прав при лишении", description: "Оспариваем лишение водительских прав в суде. Работаем с любыми статьями КоАП.", - price: "от 15 000 ₽", + price: "от 40 000 ₽", icon: "🚗", href: "/services/license-return", features: ["Анализ протокола", "Представительство в суде", "Обжалование решения", "Сопровождение до результата"] @@ -37,7 +37,7 @@ const { { title: "Оспаривание лишения", description: "Находим процессуальные нарушения и добиваемся отмены постановлений.", - price: "от 20 000 ₽", + price: "от 30 000 ₽", icon: "⚖️", href: "/services/appeal", features: ["Аудит материалов дела", "Поиск нарушений процедуры", "Подготовка жалобы", "Защита в апелляционном суде"] diff --git a/frontend/src/components/services/ServiceIncludes.astro b/frontend/src/components/services/ServiceIncludes.astro new file mode 100644 index 0000000..69b4f81 --- /dev/null +++ b/frontend/src/components/services/ServiceIncludes.astro @@ -0,0 +1,164 @@ +--- +const includes = [ + { icon: "📄", title: "Анализ протокола и документов", desc: "Изучаем все материалы дела, ищем процессуальные нарушения" }, + { icon: "💬", title: "Консультация 24/7", desc: "На связи в любое время, отвечаем на все вопросы" }, + { icon: "📝", title: "Подготовка жалоб и ходатайств", desc: "Составляем все необходимые процессуальные документы" }, + { icon: "👨‍⚖️", title: "Представительство в суде", desc: "Полное ведение дела без вашего участия" }, + { icon: "🔍", title: "Сбор доказательств", desc: "Находим свидетелей, запрашиваем видеозаписи" }, + { icon: "✅", title: "Гарантия результата", desc: "Если не вернули права — вернём деньги" } +]; +--- + +
+
+
+ +

Что входит в услугу

+

Полный спектр работ по возврату ваших прав

+
+
+ {includes.map((item, index) => ( +
+
+ {item.icon} +
+

{item.title}

+

{item.desc}

+
+ ))} +
+
+
+ + \ No newline at end of file diff --git a/frontend/src/components/services/WorkSteps.astro b/frontend/src/components/services/WorkSteps.astro new file mode 100644 index 0000000..9a345aa --- /dev/null +++ b/frontend/src/components/services/WorkSteps.astro @@ -0,0 +1,189 @@ +--- +const steps = [ + { number: "01", title: "Бесплатная консультация", desc: "Анализируем вашу ситуацию, оцениваем перспективы дела. Рассказываем о шансах и стоимости." }, + { number: "02", title: "Анализ материалов дела", desc: "Изучаем протоколы, постановления, видеозаписи. Ищем процессуальные нарушения." }, + { number: "03", title: "Подготовка стратегии", desc: "Разрабатываем линию защиты, собираем доказательства, готовим документы для суда." }, + { number: "04", title: "Защита в суде", desc: "Представляем ваши интересы в суде первой и апелляционной инстанции." }, + { number: "05", title: "Получение прав", desc: "Помогаем с процедурой возврата прав после успешного решения суда." } +]; +--- + +
+
+
+ +

Как мы возвращаем ваши права

+

Прозрачный процесс от первой консультации до получения прав

+
+
+ {steps.map((step, index) => ( +
+
{step.number}
+
+

{step.title}

+

{step.desc}

+
+
+ ))} +
+
+
+ + \ No newline at end of file diff --git a/frontend/src/constants.ts b/frontend/src/constants.ts index 11678b7..ca83532 100644 --- a/frontend/src/constants.ts +++ b/frontend/src/constants.ts @@ -10,8 +10,9 @@ export const COMPANY = { name: 'Автоюрист 086', fullName: 'Центр защиты прав водителей "Автоюрист 086"', phone: '+7 (922) 253-83-75', + phoneClean: '+79222538375', email: 'redibedi2019@gmail.com', - address: 'г. Сургут, пр. Комсомольский, д. 19', + address: 'г. Сургут, ул. 50 лет ВЛКСМ д.1', workHours: 'Пн-Пт: 9:00 - 18:00', } as const; diff --git a/frontend/src/pages/privacy.astro b/frontend/src/pages/privacy.astro index 520de65..1282ba8 100644 --- a/frontend/src/pages/privacy.astro +++ b/frontend/src/pages/privacy.astro @@ -1,6 +1,6 @@ --- import Layout from '@layouts/Layout.astro'; -import { SITE_URL } from '@constants'; +import { SITE_URL, COMPANY } from '@constants'; ---
- г. Сургут, ул. Мира, 15 + {COMPANY.address}
- +7 (3462) 00-00-00 + {COMPANY.phone}
diff --git a/frontend/src/pages/services.astro b/frontend/src/pages/services/index.astro similarity index 81% rename from frontend/src/pages/services.astro rename to frontend/src/pages/services/index.astro index 5cb09d2..e567398 100644 --- a/frontend/src/pages/services.astro +++ b/frontend/src/pages/services/index.astro @@ -18,9 +18,9 @@ import ServiceCategories from "@components/services/ServiceCategories.astro"; > + + +
+ + + + + + + + +
+
+ + + + diff --git a/frontend/src/scripts/animations.ts b/frontend/src/scripts/animations.ts new file mode 100644 index 0000000..306fd37 --- /dev/null +++ b/frontend/src/scripts/animations.ts @@ -0,0 +1,28 @@ +export function setupScrollAnimations(): void { + if (typeof window === 'undefined') return; + + const observerOptions = { + root: null, + rootMargin: '0px 0px -50px 0px', + threshold: 0.1 + }; + + const observer = new IntersectionObserver((entries) => { + entries.forEach(entry => { + if (entry.isIntersecting) { + const el = entry.target as HTMLElement; + const delay = parseInt(el.dataset.delay || '0'); + + setTimeout(() => { + el.classList.add('is-visible'); + }, delay); + + observer.unobserve(el); + } + }); + }, observerOptions); + + document.querySelectorAll('.animate-on-scroll').forEach((el) => { + observer.observe(el); + }); +} \ No newline at end of file diff --git a/frontend/src/scripts/tabs.ts b/frontend/src/scripts/tabs.ts new file mode 100644 index 0000000..7e736c6 --- /dev/null +++ b/frontend/src/scripts/tabs.ts @@ -0,0 +1,74 @@ +export function setupTabs(): void { + if (typeof window === 'undefined') return; + + const tabButtons = document.querySelectorAll('.tab-btn'); + const tabPanels = document.querySelectorAll('.tab-panel'); + + tabButtons.forEach(btn => { + btn.addEventListener('click', () => { + const tabId = btn.getAttribute('data-tab'); + + // Remove active from all buttons + tabButtons.forEach(b => { + b.classList.remove('active'); + b.setAttribute('aria-selected', 'false'); + }); + + // Remove active from all panels + tabPanels.forEach(panel => { + panel.classList.remove('active'); + }); + + // Activate current tab + btn.classList.add('active'); + btn.setAttribute('aria-selected', 'true'); + + const targetPanel = document.getElementById(`tab-${tabId}`); + if (targetPanel) { + targetPanel.classList.add('active'); + + // Animate counters if stats tab + if (tabId === 'stats') { + animateCounters(targetPanel); + } + } + }); + }); +} + +export function animateCounters(panel: HTMLElement): void { + const counters = panel.querySelectorAll('[data-count]'); + + counters.forEach(counter => { + const target = parseInt(counter.getAttribute('data-count') || '0'); + const suffix = counter.textContent?.replace(/[0-9]/g, '') || ''; + const duration = 1500; + const startTime = performance.now(); + + const easeOutCubic = (t: number) => 1 - Math.pow(1 - t, 3); + + const update = (currentTime: number) => { + const elapsed = currentTime - startTime; + const progress = Math.min(elapsed / duration, 1); + const easedProgress = easeOutCubic(progress); + const currentValue = Math.round(easedProgress * target); + + counter.textContent = `${currentValue}${suffix}`; + + if (progress < 1) { + requestAnimationFrame(update); + } + }; + + requestAnimationFrame(update); + }); + + // Animate stat bars + const statBars = panel.querySelectorAll('[data-width]'); + statBars.forEach(bar => { + const width = bar.getAttribute('data-width'); + setTimeout(() => { + (bar as HTMLElement).style.width = `${width}%`; + }, 200); + }); +} \ No newline at end of file diff --git a/frontend/tsconfig.json b/frontend/tsconfig.json index d96b1a5..5a40763 100644 --- a/frontend/tsconfig.json +++ b/frontend/tsconfig.json @@ -9,7 +9,7 @@ "@components/*": ["src/components/*"], "@data/*": ["src/data/*"], "@constants": ["src/constants.ts"], - "@constants/*": ["src/constants/*"], + "@scripts/*": ["src/scripts/*"], "@layouts/*": ["src/layouts/*"], "@assets/*": ["src/assets/*"], "@pages/*": ["src/pages/*"]