Новые изменения
This commit is contained in:
parent
32bcc76021
commit
c39b0c0821
9 changed files with 512 additions and 362 deletions
209
frontend/src/components/base/CTA.astro
Normal file
209
frontend/src/components/base/CTA.astro
Normal file
|
|
@ -0,0 +1,209 @@
|
|||
---
|
||||
import Button from '@components/base/Button.astro';
|
||||
|
||||
interface CTAProps {
|
||||
icon?: 'chat' | 'consult' | 'help' | 'phone';
|
||||
title: string;
|
||||
description: string;
|
||||
btnText?: string;
|
||||
btnHref?: string;
|
||||
variant?: 'default' | 'consultation';
|
||||
}
|
||||
|
||||
const {
|
||||
icon = 'chat',
|
||||
title,
|
||||
description,
|
||||
btnText,
|
||||
btnHref = "#contact",
|
||||
variant = 'default'
|
||||
} = Astro.props as CTAProps;
|
||||
|
||||
const iconPaths: Record<string, string> = {
|
||||
chat: '<path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/>',
|
||||
consult: '<path d="M22 16.92v3a2 2 0 0 1-2.18 2 19.79 19.79 0 0 1-8.63-3.07 19.5 19.5 0 0 1-6-6 19.79 19.79 0 0 1-3.07-8.67A2 2 0 0 1 4.11 2h3a2 2 0 0 1 2 1.72 12.84 12.84 0 0 0 .7 2.81 2 2 0 0 1-.45 2.11L8.09 9.91a16 16 0 0 0 6 6l1.27-1.27a2 2 0 0 1 2.11-.45 12.84 12.84 0 0 0 2.81.7A2 2 0 0 1 22 16.92z"/>',
|
||||
help: '<path d="M12 22c5.523 0 10-4.477 10-10S17.523 2 12 2 2 6.477 2 12s4.477 10 10 10z"/><path d="M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3"/><path d="M12 17h.01"/>',
|
||||
phone: '<circle cx="12" cy="12" r="10"/><path d="M12 6v6l4 2"/>'
|
||||
};
|
||||
---
|
||||
|
||||
<section class="cta-section">
|
||||
<div class="cta-section__overlay"></div>
|
||||
<div class="site-container">
|
||||
<div class="cta-section__inner animate-on-scroll" data-animation="fade-up">
|
||||
<div class="cta-section__icon">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" set:html={iconPaths[icon] || iconPaths.chat}></svg>
|
||||
</div>
|
||||
<h3 class="cta-section__title">{title}</h3>
|
||||
<p class="cta-section__desc">{description}</p>
|
||||
{btnText && (
|
||||
<Button variant="gold" size="lg" href={btnHref} class="cta-section__btn">
|
||||
{btnText}
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<style>
|
||||
.cta-section {
|
||||
padding: 0;
|
||||
position: relative;
|
||||
overflow: visible;
|
||||
margin-bottom: 3rem;
|
||||
}
|
||||
|
||||
.site-container {
|
||||
max-width: 1400px;
|
||||
margin: 0 auto;
|
||||
padding: 0 20px;
|
||||
}
|
||||
|
||||
.cta-section__inner {
|
||||
padding: 3.5rem 2.5rem;
|
||||
background: linear-gradient(135deg, #0a2540 0%, #1e3050 100%);
|
||||
border-radius: 20px;
|
||||
box-shadow: 0 20px 50px rgba(0, 0, 0, 0.15);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.cta-section__overlay {
|
||||
position: absolute;
|
||||
top: -50%;
|
||||
right: -20%;
|
||||
width: 60%;
|
||||
height: 150%;
|
||||
background: radial-gradient(circle, rgba(234, 194, 110, 0.15) 0%, transparent 70%);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.cta-section__icon {
|
||||
width: 64px;
|
||||
height: 64px;
|
||||
margin: 0 auto 1.5rem;
|
||||
background: rgba(234, 194, 110, 0.15);
|
||||
border-radius: 16px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: #eac26e;
|
||||
}
|
||||
|
||||
.cta-section__icon svg {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
}
|
||||
|
||||
.cta-section__title {
|
||||
font-size: clamp(1.5rem, 3vw, 1.75rem);
|
||||
font-weight: 700;
|
||||
color: #ffffff;
|
||||
margin: 0 0 0.75rem 0;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.cta-section__desc {
|
||||
font-size: 1.05rem;
|
||||
color: rgba(255, 255, 255, 0.75);
|
||||
margin: 0 0 2rem 0;
|
||||
max-width: 600px;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.cta-section__btn {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
/* Анимации */
|
||||
.animate-on-scroll {
|
||||
opacity: 0;
|
||||
will-change: opacity, transform;
|
||||
}
|
||||
|
||||
[data-animation="fade-up"] {
|
||||
transform: translateY(40px);
|
||||
transition: opacity 0.8s cubic-bezier(0.4, 0, 0.2, 1),
|
||||
transform 0.8s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
}
|
||||
|
||||
.animate-on-scroll.is-visible {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.cta-section {
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.cta-section__inner {
|
||||
padding: 2.5rem 1.5rem;
|
||||
}
|
||||
|
||||
.cta-section__title {
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
.cta-section__desc {
|
||||
font-size: 0.95rem;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
.cta-section {
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.cta-section__inner {
|
||||
padding: 2rem 1.25rem;
|
||||
}
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.animate-on-scroll {
|
||||
opacity: 1;
|
||||
transform: none;
|
||||
transition: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
const setupAnimations = () => {
|
||||
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);
|
||||
});
|
||||
};
|
||||
|
||||
setupAnimations();
|
||||
document.addEventListener('astro:after-swap', setupAnimations);
|
||||
</script>
|
||||
178
frontend/src/components/base/Pagination.astro
Normal file
178
frontend/src/components/base/Pagination.astro
Normal file
|
|
@ -0,0 +1,178 @@
|
|||
---
|
||||
export interface PaginationProps {
|
||||
currentPage: number;
|
||||
totalPages: number;
|
||||
baseUrl: string;
|
||||
showPrevNext?: boolean;
|
||||
}
|
||||
|
||||
const {
|
||||
currentPage,
|
||||
totalPages,
|
||||
baseUrl,
|
||||
showPrevNext = true
|
||||
} = Astro.props as PaginationProps;
|
||||
|
||||
const getPageUrl = (page: number) => {
|
||||
if (page === 1) return baseUrl;
|
||||
return `${baseUrl}/page/${page}`;
|
||||
};
|
||||
|
||||
const getPages = () => {
|
||||
const pages: (number | string)[] = [];
|
||||
for (let i = 1; i <= totalPages; i++) {
|
||||
if (i === 1 || i === totalPages || (i >= currentPage - 1 && i <= currentPage + 1)) {
|
||||
pages.push(i);
|
||||
} else if (pages[pages.length - 1] !== '...') {
|
||||
pages.push('...');
|
||||
}
|
||||
}
|
||||
return pages;
|
||||
};
|
||||
---
|
||||
|
||||
<nav class="pagination">
|
||||
<div class="pagination__wrapper">
|
||||
{showPrevNext && (
|
||||
<!-- Кнопка Назад -->
|
||||
<a
|
||||
href={currentPage > 1 ? getPageUrl(currentPage - 1) : '#'}
|
||||
class={`pagination__btn pagination__btn--prev ${currentPage === 1 ? 'disabled' : ''}`}
|
||||
aria-disabled={currentPage === 1}
|
||||
>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="m15 18-6-6 6-6"/>
|
||||
</svg>
|
||||
</a>
|
||||
)}
|
||||
|
||||
<!-- Номера страниц -->
|
||||
<div class="pagination__pages">
|
||||
{getPages().map((page) => (
|
||||
page === '...' ? (
|
||||
<span class="pagination__ellipsis">…</span>
|
||||
) : (
|
||||
<a
|
||||
href={getPageUrl(page as number)}
|
||||
class={`pagination__page ${page === currentPage ? 'active' : ''}`}
|
||||
aria-current={page === currentPage ? 'page' : undefined}
|
||||
>
|
||||
{page}
|
||||
</a>
|
||||
)
|
||||
))}
|
||||
</div>
|
||||
|
||||
{showPrevNext && (
|
||||
<!-- Кнопка Вперед -->
|
||||
<a
|
||||
href={currentPage < totalPages ? getPageUrl(currentPage + 1) : '#'}
|
||||
class={`pagination__btn pagination__btn--next ${currentPage === totalPages ? 'disabled' : ''}`}
|
||||
aria-disabled={currentPage === totalPages}
|
||||
>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="m9 18 6-6-6-6"/>
|
||||
</svg>
|
||||
</a>
|
||||
)}
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<style>
|
||||
.pagination {
|
||||
padding: 3rem 0;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.pagination__wrapper {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.pagination__btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 2.75rem;
|
||||
height: 2.75rem;
|
||||
border-radius: 0.75rem;
|
||||
border: 2px solid #e2e8f0;
|
||||
background: #ffffff;
|
||||
color: #64748b;
|
||||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.pagination__btn:not(.disabled):hover {
|
||||
border-color: #d4af37;
|
||||
color: #d4af37;
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
.pagination__btn.disabled {
|
||||
opacity: 0.4;
|
||||
cursor: not-allowed;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.pagination__btn svg {
|
||||
width: 1.25rem;
|
||||
height: 1.25rem;
|
||||
}
|
||||
|
||||
.pagination__pages {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.375rem;
|
||||
}
|
||||
|
||||
.pagination__page {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-width: 2.75rem;
|
||||
height: 2.75rem;
|
||||
border-radius: 0.75rem;
|
||||
border: 2px solid transparent;
|
||||
background: #ffffff;
|
||||
color: #64748b;
|
||||
font-size: 0.9rem;
|
||||
font-weight: 600;
|
||||
text-decoration: none;
|
||||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
}
|
||||
|
||||
.pagination__page:hover {
|
||||
border-color: #d4af37;
|
||||
color: #d4af37;
|
||||
}
|
||||
|
||||
.pagination__page.active {
|
||||
background: linear-gradient(135deg, #d4af37 0%, #eac26e 100%);
|
||||
border-color: #d4af37;
|
||||
color: #1e293b;
|
||||
box-shadow: 0 4px 12px rgba(212, 175, 55, 0.3);
|
||||
}
|
||||
|
||||
.pagination__ellipsis {
|
||||
color: #94a3b8;
|
||||
padding: 0 0.25rem;
|
||||
font-size: 1.25rem;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
@media (max-width: 640px) {
|
||||
.pagination__pages {
|
||||
gap: 0.25rem;
|
||||
}
|
||||
|
||||
.pagination__page,
|
||||
.pagination__btn {
|
||||
width: 2.25rem;
|
||||
height: 2.25rem;
|
||||
min-width: 2.25rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
---
|
||||
import Button from '@components/base/Button.astro';
|
||||
import Pagination from '@components/base/Pagination.astro';
|
||||
import CTA from '@components/base/CTA.astro';
|
||||
|
||||
interface Case {
|
||||
id: number;
|
||||
|
|
@ -96,8 +97,25 @@ const {
|
|||
sum: "280 000 ₽",
|
||||
href: "/cases/casco-damage-claim"
|
||||
}
|
||||
] as Case[]
|
||||
} = Astro.props;
|
||||
] as Case[],
|
||||
currentPage = 1,
|
||||
casesPerPage = 4,
|
||||
baseUrl = "/cases"
|
||||
} = Astro.props as {
|
||||
sectionSubtitle?: string;
|
||||
sectionTitle?: string;
|
||||
sectionDescription?: string;
|
||||
filterLabel?: string;
|
||||
cases?: Case[];
|
||||
currentPage?: number;
|
||||
casesPerPage?: number;
|
||||
baseUrl?: string;
|
||||
};
|
||||
|
||||
const totalPages = Math.ceil(cases.length / casesPerPage);
|
||||
const startIndex = (currentPage - 1) * casesPerPage;
|
||||
const endIndex = startIndex + casesPerPage;
|
||||
const paginatedCases = cases.slice(startIndex, endIndex);
|
||||
|
||||
// Получаем уникальные категории
|
||||
const categories = ["Все", ...Array.from(new Set(cases.map((c: Case) => c.category)))];
|
||||
|
|
@ -130,7 +148,7 @@ const categories = ["Все", ...Array.from(new Set(cases.map((c: Case) => c.cat
|
|||
|
||||
<!-- Список кейсов -->
|
||||
<div class="cases-list">
|
||||
{cases.map((caseItem: Case, index: number) => (
|
||||
{paginatedCases.map((caseItem: Case, index: number) => (
|
||||
<article class="case-card animate-on-scroll" data-animation="slide-up" data-delay={index * 100 + 300} data-category={caseItem.category}>
|
||||
<div class="case-card__inner">
|
||||
<!-- Верхняя часть -->
|
||||
|
|
@ -204,19 +222,22 @@ const categories = ["Все", ...Array.from(new Set(cases.map((c: Case) => c.cat
|
|||
))}
|
||||
</div>
|
||||
|
||||
<!-- Пагинация -->
|
||||
{totalPages > 1 && (
|
||||
<Pagination
|
||||
currentPage={currentPage}
|
||||
totalPages={totalPages}
|
||||
baseUrl={baseUrl}
|
||||
/>
|
||||
)}
|
||||
|
||||
<!-- CTA -->
|
||||
<div class="cases-cta animate-on-scroll" data-animation="fade-up" data-delay="800">
|
||||
<div class="cases-cta__icon">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/>
|
||||
</svg>
|
||||
</div>
|
||||
<h3 class="cases-cta__title">Нужна помощь с похожей ситуацией?</h3>
|
||||
<p class="cases-cta__desc">Расскажите о вашей проблеме — мы найдём решение. Первая консультация бесплатно.</p>
|
||||
<Button variant="gold" size="lg" href="#contact">
|
||||
Получить консультацию
|
||||
</Button>
|
||||
</div>
|
||||
<CTA
|
||||
icon="help"
|
||||
title="Нужна помощь с похожей ситуацией?"
|
||||
description="Расскажите о вашей проблеме — мы найдём решение. Первая консультация бесплатно."
|
||||
btnText="Получить консультацию"
|
||||
/>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
|
|
@ -347,7 +368,7 @@ const categories = ["Все", ...Array.from(new Set(cases.map((c: Case) => c.cat
|
|||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 2.5rem;
|
||||
margin-bottom: 5rem;
|
||||
margin-bottom: 3rem;
|
||||
}
|
||||
|
||||
/* --- Карточка кейса --- */
|
||||
|
|
@ -592,72 +613,6 @@ const categories = ["Все", ...Array.from(new Set(cases.map((c: Case) => c.cat
|
|||
color: var(--color-accent);
|
||||
}
|
||||
|
||||
/* --- CTA --- */
|
||||
.cases-cta {
|
||||
text-align: center;
|
||||
padding: 3.5rem 2.5rem;
|
||||
background: linear-gradient(135deg, var(--color-primary) 0%, var(--color-primary-light) 100%);
|
||||
border-radius: 20px;
|
||||
box-shadow: var(--shadow-xl);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.cases-cta::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: -50%;
|
||||
right: -20%;
|
||||
width: 60%;
|
||||
height: 150%;
|
||||
background: radial-gradient(circle, var(--color-accent-glow) 0%, transparent 70%);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.cases-cta__icon {
|
||||
width: 64px;
|
||||
height: 64px;
|
||||
margin: 0 auto 1.5rem;
|
||||
background: rgba(234, 194, 110, 0.15);
|
||||
border-radius: 16px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: var(--color-accent);
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.cases-cta__icon svg {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
}
|
||||
|
||||
.cases-cta__title {
|
||||
font-size: 1.75rem;
|
||||
font-weight: 700;
|
||||
color: #ffffff;
|
||||
margin: 0 0 0.75rem 0;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.cases-cta__desc {
|
||||
font-size: 1.05rem;
|
||||
color: rgba(255, 255, 255, 0.75);
|
||||
margin: 0 0 2rem 0;
|
||||
max-width: 600px;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.cases-cta .btn {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
/* --- Анимации --- */
|
||||
.animate-on-scroll {
|
||||
opacity: 0;
|
||||
|
|
@ -736,18 +691,6 @@ const categories = ["Все", ...Array.from(new Set(cases.map((c: Case) => c.cat
|
|||
align-items: flex-start;
|
||||
gap: 0.75rem;
|
||||
}
|
||||
|
||||
.cases-cta {
|
||||
padding: 2.5rem 1.5rem;
|
||||
}
|
||||
|
||||
.cases-cta__title {
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
.cases-cta__desc {
|
||||
font-size: 0.95rem;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
|
|
@ -781,10 +724,6 @@ const categories = ["Все", ...Array.from(new Set(cases.map((c: Case) => c.cat
|
|||
flex-direction: column;
|
||||
gap: 0.75rem;
|
||||
}
|
||||
|
||||
.cases-cta {
|
||||
padding: 2rem 1.25rem;
|
||||
}
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
---
|
||||
import Button from '@components/base/Button.astro';
|
||||
import Pagination from '@components/base/Pagination.astro';
|
||||
|
||||
interface ServiceCategory {
|
||||
id: string;
|
||||
|
|
@ -133,8 +134,19 @@ const {
|
|||
}
|
||||
]
|
||||
}
|
||||
] as ServiceCategory[]
|
||||
} = Astro.props;
|
||||
] as ServiceCategory[],
|
||||
currentPage = 1,
|
||||
servicesPerPage = 6,
|
||||
baseUrl = "/services"
|
||||
} = Astro.props as {
|
||||
sectionSubtitle?: string;
|
||||
sectionTitle?: string;
|
||||
sectionDescription?: string;
|
||||
categories?: ServiceCategory[];
|
||||
currentPage?: number;
|
||||
servicesPerPage?: number;
|
||||
baseUrl?: string;
|
||||
};
|
||||
---
|
||||
|
||||
<section class="service-categories" id="services-list">
|
||||
|
|
@ -212,15 +224,6 @@ const {
|
|||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<!-- CTA внизу -->
|
||||
<div class="section-cta animate-on-scroll" data-animation="fade-up" data-delay="800">
|
||||
<p class="cta-text-main">Не нашли нужную услугу?</p>
|
||||
<p class="cta-text-sub">Позвоните — разберём вашу ситуацию бесплатно</p>
|
||||
<Button variant="gold" size="lg" href="#contact">
|
||||
Связаться с нами
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
|
|
@ -629,51 +632,6 @@ const {
|
|||
color: var(--color-accent);
|
||||
}
|
||||
|
||||
/* --- Нижний CTA --- */
|
||||
.section-cta {
|
||||
margin-top: 5rem;
|
||||
text-align: center;
|
||||
padding: 3rem 2rem;
|
||||
background: linear-gradient(135deg, var(--color-primary) 0%, var(--color-primary-light) 100%);
|
||||
border-radius: 20px;
|
||||
box-shadow: var(--shadow-xl);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.section-cta::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: -50%;
|
||||
right: -20%;
|
||||
width: 60%;
|
||||
height: 150%;
|
||||
background: radial-gradient(circle, var(--color-accent-glow) 0%, transparent 70%);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.cta-text-main {
|
||||
font-size: 1.5rem;
|
||||
font-weight: 700;
|
||||
color: #ffffff;
|
||||
margin: 0 0 0.5rem 0;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.cta-text-sub {
|
||||
font-size: 1rem;
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
margin: 0 0 1.5rem 0;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.section-cta .btn {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
/* --- Адаптивность --- */
|
||||
@media (max-width: 1200px) {
|
||||
.services-grid {
|
||||
|
|
@ -755,19 +713,6 @@ const {
|
|||
justify-content: flex-end;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.section-cta {
|
||||
padding: 2rem 1.5rem;
|
||||
margin-top: 3rem;
|
||||
}
|
||||
|
||||
.cta-text-main {
|
||||
font-size: 1.25rem;
|
||||
}
|
||||
|
||||
.cta-text-sub {
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
|
|
@ -810,19 +755,6 @@ const {
|
|||
.service-title {
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
|
||||
.section-cta {
|
||||
padding: 1.5rem 1rem;
|
||||
border-radius: 16px;
|
||||
}
|
||||
|
||||
.cta-text-main {
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
|
||||
.cta-text-sub {
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
}
|
||||
|
||||
/* @media (prefers-reduced-motion: reduce) */
|
||||
|
|
|
|||
|
|
@ -5,6 +5,8 @@ import PageHero from '@components/base/PageHero.astro';
|
|||
import BlogCategories from '@components/blog/BlogCategories.astro';
|
||||
import BlogCard from '@components/blog/BlogCard.astro';
|
||||
import BlogPagination from '@components/blog/BlogPagination.astro';
|
||||
import Pagination from '@components/base/Pagination.astro';
|
||||
import CTA from '@components/base/CTA.astro';
|
||||
import SearchModal from '@components/base/SearchModal.astro';
|
||||
import { blogPosts, categories } from '@data/blogData';
|
||||
|
||||
|
|
@ -64,24 +66,21 @@ const paginatedPosts = blogPosts.slice(startIndex, endIndex);
|
|||
</div>
|
||||
|
||||
<!-- Пагинация -->
|
||||
<BlogPagination currentPage={currentPage} totalPages={totalPages} />
|
||||
<Pagination
|
||||
currentPage={currentPage}
|
||||
totalPages={totalPages}
|
||||
baseUrl="/blog"
|
||||
/>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- CTA-блок -->
|
||||
<section class="blog-cta">
|
||||
<div class="site-container">
|
||||
<div class="cta-content">
|
||||
<h2 class="cta-title">Нужна консультация юриста?</h2>
|
||||
<p class="cta-text">
|
||||
Не нашли ответ на свой вопрос? Запишитесь на бесплатную консультацию — мы поможем разобраться в вашей ситуации
|
||||
</p>
|
||||
<button class="cta-button" id="consultation-btn" data-modal-target="consultation-modal">
|
||||
Записаться на консультацию
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<CTA
|
||||
icon="consult"
|
||||
title="Нужна консультация юриста?"
|
||||
description="Не нашли ответ на свой вопрос? Запишитесь на бесплатную консультацию — мы поможем разобраться в вашей ситуации"
|
||||
btnText="Записаться на консультацию"
|
||||
/>
|
||||
|
||||
<SearchModal />
|
||||
</Layout>
|
||||
|
|
@ -102,66 +101,6 @@ const paginatedPosts = blogPosts.slice(startIndex, endIndex);
|
|||
transition: opacity 0.3s ease, transform 0.3s ease;
|
||||
}
|
||||
|
||||
/* ===== CTA ===== */
|
||||
.blog-cta {
|
||||
padding: 5rem 0;
|
||||
background: linear-gradient(135deg, #0a2540 0%, #1e3a5f 100%);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.blog-cta::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
bottom: -30%;
|
||||
left: -10%;
|
||||
width: 500px;
|
||||
height: 500px;
|
||||
background: radial-gradient(circle, rgba(212, 175, 55, 0.1) 0%, transparent 70%);
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.cta-content {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
text-align: center;
|
||||
max-width: 600px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.cta-title {
|
||||
font-size: clamp(1.75rem, 4vw, 2.5rem);
|
||||
font-weight: 800;
|
||||
color: #ffffff;
|
||||
margin: 0 0 1rem;
|
||||
letter-spacing: -0.02em;
|
||||
}
|
||||
|
||||
.cta-text {
|
||||
color: rgba(255, 255, 255, 0.75);
|
||||
font-size: 1.1rem;
|
||||
line-height: 1.6;
|
||||
margin: 0 0 2rem;
|
||||
}
|
||||
|
||||
.cta-button {
|
||||
background: linear-gradient(135deg, #d4af37 0%, #eac26e 100%);
|
||||
color: #1e293b;
|
||||
border: none;
|
||||
padding: 1rem 2.5rem;
|
||||
border-radius: 0.75rem;
|
||||
font-size: 1.1rem;
|
||||
font-weight: 700;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
box-shadow: 0 4px 15px rgba(212, 175, 55, 0.3);
|
||||
}
|
||||
|
||||
.cta-button:hover {
|
||||
transform: translateY(-3px);
|
||||
box-shadow: 0 8px 25px rgba(212, 175, 55, 0.4);
|
||||
}
|
||||
|
||||
/* ===== RESPONSIVE ===== */
|
||||
@media (max-width: 1024px) {
|
||||
.blog-grid {
|
||||
|
|
|
|||
|
|
@ -5,6 +5,8 @@ import PageHero from '@components/base/PageHero.astro';
|
|||
import BlogCategories from '@components/blog/BlogCategories.astro';
|
||||
import BlogCard from '@components/blog/BlogCard.astro';
|
||||
import BlogPagination from '@components/blog/BlogPagination.astro';
|
||||
import Pagination from '@components/base/Pagination.astro';
|
||||
import CTA from '@components/base/CTA.astro';
|
||||
import SearchModal from '@components/base/SearchModal.astro';
|
||||
import { blogPosts, categories } from '@data/blogData';
|
||||
|
||||
|
|
@ -65,24 +67,21 @@ const paginatedPosts = blogPosts.slice(startIndex, endIndex);
|
|||
</div>
|
||||
|
||||
<!-- Пагинация -->
|
||||
<BlogPagination currentPage={currentPage} totalPages={totalPages} />
|
||||
<Pagination
|
||||
currentPage={currentPage}
|
||||
totalPages={totalPages}
|
||||
baseUrl="/blog"
|
||||
/>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- CTA-блок -->
|
||||
<section class="blog-cta">
|
||||
<div class="site-container">
|
||||
<div class="cta-content">
|
||||
<h2 class="cta-title">Нужна консультация юриста?</h2>
|
||||
<p class="cta-text">
|
||||
Не нашли ответ на свой вопрос? Запишитесь на бесплатную консультацию — мы поможем разобраться в вашей ситуации
|
||||
</p>
|
||||
<button class="cta-button" id="consultation-btn" data-modal-target="consultation-modal">
|
||||
Записаться на консультацию
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<CTA
|
||||
icon="consult"
|
||||
title="Нужна консультация юриста?"
|
||||
description="Не нашли ответ на свой вопрос? Запишитесь на бесплатную консультацию — мы поможем разобраться в вашей ситуации"
|
||||
btnText="Записаться на консультацию"
|
||||
/>
|
||||
|
||||
<SearchModal />
|
||||
</Layout>
|
||||
|
|
@ -103,66 +102,6 @@ const paginatedPosts = blogPosts.slice(startIndex, endIndex);
|
|||
transition: opacity 0.3s ease, transform 0.3s ease;
|
||||
}
|
||||
|
||||
/* ===== CTA ===== */
|
||||
.blog-cta {
|
||||
padding: 5rem 0;
|
||||
background: linear-gradient(135deg, #0a2540 0%, #1e3a5f 100%);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.blog-cta::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
bottom: -30%;
|
||||
left: -10%;
|
||||
width: 500px;
|
||||
height: 500px;
|
||||
background: radial-gradient(circle, rgba(212, 175, 55, 0.1) 0%, transparent 70%);
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.cta-content {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
text-align: center;
|
||||
max-width: 600px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.cta-title {
|
||||
font-size: clamp(1.75rem, 4vw, 2.5rem);
|
||||
font-weight: 800;
|
||||
color: #ffffff;
|
||||
margin: 0 0 1rem;
|
||||
letter-spacing: -0.02em;
|
||||
}
|
||||
|
||||
.cta-text {
|
||||
color: rgba(255, 255, 255, 0.75);
|
||||
font-size: 1.1rem;
|
||||
line-height: 1.6;
|
||||
margin: 0 0 2rem;
|
||||
}
|
||||
|
||||
.cta-button {
|
||||
background: linear-gradient(135deg, #d4af37 0%, #eac26e 100%);
|
||||
color: #1e293b;
|
||||
border: none;
|
||||
padding: 1rem 2.5rem;
|
||||
border-radius: 0.75rem;
|
||||
font-size: 1.1rem;
|
||||
font-weight: 700;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
box-shadow: 0 4px 15px rgba(212, 175, 55, 0.3);
|
||||
}
|
||||
|
||||
.cta-button:hover {
|
||||
transform: translateY(-3px);
|
||||
box-shadow: 0 8px 25px rgba(212, 175, 55, 0.4);
|
||||
}
|
||||
|
||||
/* ===== RESPONSIVE ===== */
|
||||
@media (max-width: 1024px) {
|
||||
.blog-grid {
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
import Layout from '@layouts/Layout.astro';
|
||||
import { SITE_URL, COMPANY } from '@constants';
|
||||
import PageHero from '@components/base/PageHero.astro';
|
||||
import CTA from '@components/base/CTA.astro';
|
||||
|
||||
// Логика авторизации (пока статичная переменная)
|
||||
const isAuthorized = false; // Измените на true, чтобы увидеть форму
|
||||
|
|
@ -174,21 +175,12 @@ const isAuthorized = false; // Измените на true, чтобы увиде
|
|||
</section>
|
||||
|
||||
<!-- CTA-блок -->
|
||||
<section class="contacts-cta">
|
||||
<div class="site-container">
|
||||
<div class="cta-content">
|
||||
<h2 class="cta-title animate-on-scroll" data-animation="fade-up">
|
||||
Нужна срочная помощь?
|
||||
</h2>
|
||||
<p class="cta-text animate-on-scroll" data-animation="fade-up" data-delay="100">
|
||||
Запишитесь на бесплатную консультацию прямо сейчас — мы перезвоним в течение 15 минут
|
||||
</p>
|
||||
<button class="cta-button animate-on-scroll" data-animation="fade-up" data-delay="200" id="consultation-btn" data-modal-target="consultation-modal">
|
||||
Записаться на консультацию
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<CTA
|
||||
icon="phone"
|
||||
title="Нужна срочная помощь?"
|
||||
description="Запишитесь на бесплатную консультацию прямо сейчас — мы перезвоним в течение 15 минут"
|
||||
btnText="Записаться на консультацию"
|
||||
/>
|
||||
</Layout>
|
||||
|
||||
<style>
|
||||
|
|
@ -357,14 +349,6 @@ const isAuthorized = false; // Измените на true, чтобы увиде
|
|||
.form-privacy { font-size: 0.8rem; color: #94a3b8; text-align: center; }
|
||||
.privacy-link { color: #d4af37; text-decoration: underline; }
|
||||
|
||||
/* ===== CTA & MAP ===== */
|
||||
.contacts-cta { padding: 5rem 0; background: linear-gradient(135deg, #0a2540 0%, #1e3a5f 100%); position: relative; overflow: hidden; }
|
||||
.cta-content { position: relative; z-index: 1; text-align: center; max-width: 600px; margin: 0 auto; }
|
||||
.cta-title { font-size: clamp(1.75rem, 4vw, 2.5rem); font-weight: 800; color: #ffffff; margin: 0 0 1rem; }
|
||||
.cta-text { color: rgba(255, 255, 255, 0.75); font-size: 1.1rem; margin: 0 0 2rem; }
|
||||
.cta-button { background: linear-gradient(135deg, #d4af37 0%, #eac26e 100%); color: #1e293b; border: none; padding: 1rem 2.5rem; border-radius: 0.75rem; font-size: 1.1rem; font-weight: 700; cursor: pointer; transition: all 0.3s ease; }
|
||||
.cta-button:hover { transform: translateY(-3px); box-shadow: 0 8px 25px rgba(212, 175, 55, 0.4); }
|
||||
|
||||
/* ===== ANIMATIONS ===== */
|
||||
.animate-on-scroll { opacity: 0; will-change: opacity, transform; }
|
||||
[data-animation="fade-up"] { transform: translateY(30px); transition: opacity 0.7s ease, transform 0.7s ease; }
|
||||
|
|
|
|||
|
|
@ -2,9 +2,19 @@
|
|||
import Layout from '@layouts/Layout.astro';
|
||||
import { SITE_URL } from '@constants';
|
||||
import PageHero from '@components/base/PageHero.astro';
|
||||
import CTA from '@components/base/CTA.astro';
|
||||
import Pagination from '@components/base/Pagination.astro';
|
||||
import ReviewCard from '@components/reviews/ReviewCard.astro';
|
||||
import VotingSummary from '@components/reviews/VotingSummary.astro';
|
||||
import { reviewsData, votingSummary } from '@data/reviewsData';
|
||||
|
||||
const REVIEWS_PER_PAGE = 6;
|
||||
const currentPage = 1;
|
||||
const totalPages = Math.ceil(reviewsData.length / REVIEWS_PER_PAGE);
|
||||
|
||||
const startIndex = 0;
|
||||
const endIndex = REVIEWS_PER_PAGE;
|
||||
const paginatedReviews = reviewsData.slice(startIndex, endIndex);
|
||||
---
|
||||
|
||||
<Layout
|
||||
|
|
@ -44,7 +54,7 @@ import { reviewsData, votingSummary } from '@data/reviewsData';
|
|||
|
||||
<!-- Сетка отзывов -->
|
||||
<div class="reviews-grid">
|
||||
{reviewsData.map((review) => (
|
||||
{paginatedReviews.map((review) => (
|
||||
<ReviewCard
|
||||
name={review.name}
|
||||
car={review.car}
|
||||
|
|
@ -59,16 +69,23 @@ import { reviewsData, votingSummary } from '@data/reviewsData';
|
|||
))}
|
||||
</div>
|
||||
|
||||
<!-- Пагинация -->
|
||||
{totalPages > 1 && (
|
||||
<Pagination
|
||||
currentPage={currentPage}
|
||||
totalPages={totalPages}
|
||||
baseUrl="/reviews"
|
||||
/>
|
||||
)}
|
||||
|
||||
<!-- CTA блок -->
|
||||
<div class="cta-section animate-on-scroll" data-animation="fade-up" data-delay="300">
|
||||
<h2 class="cta-title">Хотите оставить отзыв?</h2>
|
||||
<p class="cta-text">
|
||||
Поделитесь своим опытом работы с нами. Ваш отзыв поможет другим водителям принять правильное решение.
|
||||
</p>
|
||||
<a href="/contacts" class="cta-button">
|
||||
Оставить отзыв
|
||||
</a>
|
||||
</div>
|
||||
<CTA
|
||||
icon="chat"
|
||||
title="Хотите оставить отзыв?"
|
||||
description="Поделитесь своим опытом работы с нами. Ваш отзыв поможет другим водителям принять правильное решение."
|
||||
btnText="Оставить отзыв"
|
||||
btnHref="/contacts"
|
||||
/>
|
||||
</div>
|
||||
</section>
|
||||
</Layout>
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@
|
|||
import Layout from '@layouts/Layout.astro';
|
||||
import { SITE_URL } from '@constants';
|
||||
import PageHero from "@components/base/PageHero.astro";
|
||||
import CTA from "@components/base/CTA.astro";
|
||||
import Pagination from "@components/base/Pagination.astro";
|
||||
import ServiceCategories from "@components/services/ServiceCategories.astro";
|
||||
---
|
||||
|
||||
|
|
@ -30,4 +32,15 @@ import ServiceCategories from "@components/services/ServiceCategories.astro";
|
|||
bgImage="/images/home/bg_hero.avif"
|
||||
/>
|
||||
<ServiceCategories />
|
||||
<Pagination
|
||||
currentPage={1}
|
||||
totalPages={2}
|
||||
baseUrl="/services"
|
||||
/>
|
||||
<CTA
|
||||
icon="consult"
|
||||
title="Нужна юридическая помощь?"
|
||||
description="Не нашли нужную услугу? Позвоните — разберём вашу ситуацию бесплатно. Первая консультация в подарок."
|
||||
btnText="Связаться с нами"
|
||||
/>
|
||||
</Layout>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue