Новые компоенÑСодаÐзданÑа форма ÐаÐутентификациÐи

This commit is contained in:
Web-serfer 2026-04-01 21:28:29 +05:00
parent 689f2b31c8
commit 18c91e69a5
11 changed files with 968 additions and 271 deletions

View file

@ -1,6 +1,5 @@
# Используем официальный образ Bun через зеркало
FROM dockerhub.timeweb.cloud/oven/bun:alpine AS build
WORKDIR /app
# Копируем файлы зависимостей

View file

@ -20,7 +20,7 @@ const lawyerImageUrl = "/images/home/heroImg.jpg";
<!-- Оверлей для синего оттенка фона -->
<div class="hero-overlay"></div>
<div class="hero-container">
<div class="site-container hero-grid">
<!-- Левая колонка: Текст -->
<div class="hero-content">
@ -97,18 +97,15 @@ const lawyerImageUrl = "/images/home/heroImg.jpg";
z-index: 1;
}
/* Контейнер для выравнивания */
.hero-container {
/* Сетка для контента Hero */
.hero-grid {
position: relative;
z-index: 2; /* Поверх оверлея */
width: 100%;
max-width: var(--site-max-width);
margin: 0 auto;
padding: 2rem;
display: flex;
justify-content: space-between;
align-items: center;
gap: 4rem;
padding: 2rem;
}
/* --- Левая часть (Текст) --- */
@ -274,7 +271,7 @@ const lawyerImageUrl = "/images/home/heroImg.jpg";
/* --- Адаптивность --- */
@media (max-width: 1024px) {
.hero-container {
.hero-grid {
flex-direction: column;
text-align: center; /* Центрируем контент на планшетах */
padding: 4rem 2rem;

View file

@ -1,355 +1,488 @@
---
interface ServiceCard {
id: string;
type: 'wide-light' | 'wide-dark' | 'standard';
title: string;
desc: string;
icon: string;
linkText?: string;
btnText?: string;
href?: string;
}
const sectionSubtitle = "Специализация";
const sectionTitle = "Комплексная защита на дороге";
const services = {
card1: {
const {
sectionSubtitle = "НАШИ УСЛУГИ",
sectionTitle = "Помощь автовладельцам в Сургуте",
services = [
{
id: 'card1',
type: 'wide-light',
title: "Возврат водительских прав",
desc: "Оспаривание протоколов за отказ от мед. освидетельствования, выезд на встречную полосу и другие нарушения. Анализируем ошибки инспекторов.",
linkText: "Узнать подробнее"
desc: "Вернём права после лишения в кратчайшие сроки. Работаем по всей территории РФ.",
icon: "🚗",
href: "#services"
},
card2: {
title: "Споры по ОСАГО/КАСКО",
desc: "Добиваемся полных выплат от страховых. Взыскание недоплаченных сумм, УТС и штрафов."
{
id: 'card2',
type: 'standard',
title: "ОСАГО/КАСКО",
desc: "Споры со страховыми компаниями. Взыскание полного ущерба.",
icon: "🛡️",
href: "#services"
},
card3: {
{
id: 'card3',
type: 'standard',
title: "Разбор ДТП",
desc: "Установление истинного виновника аварии. Независимая экспертиза и трасология."
desc: "Полное сопровождение при разборе ДТП. Оспаривание вины.",
icon: "🚨",
href: "#services"
},
card4: {
{
id: 'card4',
type: 'wide-dark',
title: "Представительство в суде",
desc: "Берем на себя всю бумажную волокиту и посещение заседаний. Защищаем ваши интересы во всех судебных инстанциях Сургута и округа.",
btnText: "Записаться на встречу"
desc: "Защита ваших интересов в судах всех инстанций.",
icon: "⚖️",
href: "#services"
}
};
] as ServiceCard[]
} = Astro.props;
---
<section class="services-section">
<div class="container">
<div class="site-container">
<!-- Заголовок секции -->
<div class="section-header">
<span class="subtitle">{sectionSubtitle}</span>
<h2 class="title">{sectionTitle}</h2>
</div>
<!-- Сетка карточек (Bento Grid) -->
<!-- Bento Grid -->
<div class="bento-grid">
{services.map((service: ServiceCard) => (
<a
href={service.href || '/services'}
class={`card
${service.type === 'wide-light' ? 'card-wide card-featured' : ''}
${service.type === 'wide-dark' ? 'card-wide card-dark' : ''}
${service.type === 'standard' ? 'card-standard' : ''}
`}
>
{service.type === 'wide-light' && <div class="card-glow"></div>}
{service.type === 'standard' && <div class="card-glow"></div>}
{service.type === 'wide-dark' && <div class="card-glow-dark"></div>}
<!-- Карточка 1: Широкая светлая -->
<div class="card large-card light-bg">
<!-- Фоновый водяной знак (Молоток) -->
<div class="bg-watermark">
<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M14 13L10 9M14 13L15.5 11.5C16.3284 10.6716 16.3284 9.32843 15.5 8.5C14.6716 7.67157 13.3284 7.67157 12.5 8.5L11 10M14 13L16 15C16.8284 15.8284 18.1716 15.8284 19 15C19.8284 14.1716 19.8284 12.8284 19 12L17 10M10 9L8 11C7.17157 11.8284 5.82843 11.8284 5 11C4.17157 10.1716 4.17157 8.82843 5 8L7 6M10 9L8.5 7.5C7.67157 6.67157 7.67157 5.32843 8.5 4.5C9.32843 3.67157 10.6716 3.67157 11.5 4.5L13 6M4 20L9 15" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M4 20H8V22H2V16H4V20Z" fill="currentColor"/>
</svg>
</div>
<div class="icon dark-icon">
<!-- Иконка: Перечеркнутая камера -->
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="1" y1="1" x2="23" y2="23"></line><path d="M21 15V7a2 2 0 0 0-2-2H8.5"></path><path d="M3 7v8a2 2 0 0 0 2 2h14"></path><circle cx="12" cy="13" r="3"></circle></svg>
</div>
<div class="card-content">
<h3 class="card-title">{services.card1.title}</h3>
<p class="card-desc">{services.card1.desc}</p>
<div class={`card-icon
${service.type === 'wide-dark' ? 'icon-white' : ''}
${service.type === 'standard' || service.type === 'wide-light' ? 'icon-gold' : ''}
`}>
<span class="icon-emoji">{service.icon}</span>
</div>
<a href="#services" class="text-link">
{services.card1.linkText}
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M5 12h14M12 5l7 7-7 7"/></svg>
<h3 class="card-title">{service.title}</h3>
<p class="card-desc">{service.desc}</p>
<span class="card-arrow">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M5 12h14M12 5l7 7-7 7"/>
</svg>
</span>
</div>
{service.type === 'wide-light' && <div class="card-accent"></div>}
{service.type === 'wide-dark' && <div class="card-shine"></div>}
</a>
</div>
<!-- Карточка 2: Маленькая белая -->
<div class="card small-card white-bg">
<div class="icon gold-icon">
<!-- Иконка: Документ/Страховка -->
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M19 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11l5 5v11a2 2 0 0 1-2 2z"></path><circle cx="12" cy="13" r="2"></circle></svg>
</div>
<div class="card-content">
<h3 class="card-title">{services.card2.title}</h3>
<p class="card-desc">{services.card2.desc}</p>
</div>
</div>
<!-- Карточка 3: Маленькая белая -->
<div class="card small-card white-bg">
<div class="icon gold-icon">
<!-- Иконка: Автомобиль с восклицательным знаком -->
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M14 16H9m10 0h3v-3.15a1 1 0 0 0-.84-.99L16 11l-2.7-3.6a2 2 0 0 0-1.6-.8H9.3a2 2 0 0 0-1.6.8L5 11l-5.16.86a1 1 0 0 0-.84.99V16h3"></path><circle cx="6.5" cy="16.5" r="2.5"></circle><circle cx="16.5" cy="16.5" r="2.5"></circle><path d="M12 2v4m0 4h.01"></path></svg>
</div>
<div class="card-content">
<h3 class="card-title">{services.card3.title}</h3>
<p class="card-desc">{services.card3.desc}</p>
</div>
</div>
<!-- Карточка 4: Широкая темная -->
<div class="card large-card dark-bg">
<!-- Фоновый водяной знак (Весы) -->
<div class="bg-watermark watermark-bottom">
<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
<path d="M12 3v18M3 10l4-4 4 4M21 10l-4-4-4 4M5 10v4a2 2 0 002 2h0a2 2 0 002-2v-4M15 10v4a2 2 0 002 2h0a2 2 0 002-2v-4"/>
<path d="M10 21h4M9 6h6"/>
</svg>
</div>
<div class="card-content card-content-bottom">
<h3 class="card-title">{services.card4.title}</h3>
<p class="card-desc">{services.card4.desc}</p>
<a href="#contact" class="btn-outline">{services.card4.btnText}</a>
</div>
</div>
))}
</div>
</div>
</section>
<style>
/* Общие стили секции */
:root {
--color-primary: #1e3050;
--color-accent: #d4af37;
--color-accent-light: #e8c547;
--color-dark: #0b1426;
--color-light: #f8fafc;
--color-gray: #64748b;
--gradient-gold: linear-gradient(135deg, #d4af37 0%, #f4d03f 50%, #d4af37 100%);
--gradient-dark: linear-gradient(135deg, #0b1426 0%, #1e3050 100%);
--shadow-sm: 0 1px 2px 0 rgb(0 0 0 / 0.05);
--shadow-md: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1);
--shadow-xl: 0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1);
--mouse-x: 50%;
--mouse-y: 50%;
}
.services-section {
padding: 6rem 2rem;
background-color: #ffffff;
font-family: system-ui, -apple-system, sans-serif;
padding: 6rem 1.5rem;
background: linear-gradient(180deg, #ffffff 0%, #f8fafc 100%);
font-family: 'Inter', system-ui, -apple-system, sans-serif;
}
.container {
max-width: 1400px;
margin: 0 auto;
}
/* Заголовок секции */
/* Заголовок */
.section-header {
margin-bottom: 3rem;
margin-bottom: 4rem;
text-align: center;
}
.subtitle {
display: block;
color: #d1b06b; /* Золотой цвет */
font-size: 0.8rem;
display: inline-block;
color: var(--color-accent);
font-size: 0.75rem;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 2px;
margin-bottom: 0.8rem;
letter-spacing: 3px;
margin-bottom: 1rem;
padding: 0.5rem 1rem;
background: rgba(212, 175, 55, 0.1);
border-radius: 999px;
}
.title {
color: #1e3050; /* Темно-синий */
font-size: clamp(2rem, 3vw, 2.8rem);
color: var(--color-primary);
font-size: clamp(2rem, 4vw, 3rem);
font-weight: 800;
margin: 0;
letter-spacing: -0.5px;
line-height: 1.2;
letter-spacing: -0.02em;
}
/* Настройка сетки Bento */
/* Bento Grid */
.bento-grid {
display: grid;
/* Делим на 3 равные колонки */
grid-template-columns: repeat(3, 1fr);
/* 2 строки авто-высоты */
grid-template-rows: auto auto;
gap: 1.5rem;
}
/* Общие стили карточек */
/* Card as Link */
.card {
position: relative;
border-radius: 12px;
border-radius: 24px;
padding: 2.5rem;
overflow: hidden;
transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
isolation: isolate;
text-decoration: none;
cursor: pointer;
display: block;
}
.card::before {
content: '';
position: absolute;
inset: 0;
border-radius: inherit;
padding: 1px;
background: linear-gradient(135deg, rgba(255,255,255,0.4) 0%, rgba(255,255,255,0.1) 100%);
mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
mask-composite: exclude;
-webkit-mask-composite: xor;
pointer-events: none;
}
/* Glow Effect */
.card-glow, .card-glow-dark {
position: absolute;
inset: -1px;
border-radius: inherit;
opacity: 0;
transition: opacity 0.4s ease;
background: radial-gradient(600px circle at var(--mouse-x, 50%) var(--mouse-y, 50%),
rgba(212, 175, 55, 0.15), transparent 40%);
z-index: 0;
pointer-events: none;
}
.card-glow-dark {
background: radial-gradient(600px circle at var(--mouse-x, 50%) var(--mouse-y, 50%),
rgba(212, 175, 55, 0.2), transparent 40%);
}
.card:hover .card-glow,
.card:hover .card-glow-dark {
opacity: 1;
}
/* Card Sizes */
.card-wide {
grid-column: span 2;
min-height: 320px;
}
.card-standard {
grid-column: span 1;
min-height: 280px;
background: rgba(255, 255, 255, 0.8);
backdrop-filter: blur(12px);
border: 1px solid rgba(226, 232, 240, 0.8);
box-shadow: var(--shadow-md), inset 0 1px 0 rgba(255,255,255,0.8);
}
/* Featured Card */
.card-featured {
background: linear-gradient(135deg, #f1f5f9 0%, #e2e8f0 100%);
border: 1px solid rgba(226, 232, 240, 0.6);
box-shadow: var(--shadow-xl);
}
.card-featured .card-accent {
position: absolute;
top: 0;
right: 0;
width: 300px;
height: 300px;
background: radial-gradient(circle, rgba(212, 175, 55, 0.1) 0%, transparent 70%);
pointer-events: none;
}
/* Dark Card */
.card-dark {
background: var(--gradient-dark);
border: 1px solid rgba(255, 255, 255, 0.1);
box-shadow: var(--shadow-xl), 0 0 60px rgba(11, 20, 38, 0.3);
}
.card-dark .card-title {
color: #ffffff;
}
.card-dark .card-desc {
color: rgba(255, 255, 255, 0.7);
}
.card-shine {
position: absolute;
top: -50%;
left: -50%;
width: 200%;
height: 200%;
background: linear-gradient(45deg, transparent 30%, rgba(255, 255, 255, 0.03) 50%, transparent 70%);
transform: rotate(45deg);
transition: transform 0.6s;
pointer-events: none;
}
.card-dark:hover .card-shine {
transform: rotate(45deg) translate(20%, 20%);
}
/* Card Content */
.card-content {
position: relative;
z-index: 2;
height: 100%;
display: flex;
flex-direction: column;
justify-content: space-between;
}
/* Icons - Emoji Style */
.card-icon {
width: 64px;
height: 64px;
display: flex;
align-items: center;
justify-content: center;
border-radius: 20px;
margin-bottom: 1.5rem;
background: rgba(30, 48, 80, 0.08);
transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
box-shadow: 0 4px 15px rgba(30, 48, 80, 0.1);
flex-shrink: 0;
}
.icon-emoji {
font-size: 2rem;
line-height: 1;
filter: drop-shadow(0 2px 4px rgba(0, 0, 0, 0.1));
transition: transform 0.3s ease;
}
.card:hover {
transform: translateY(-3px);
.card:hover .icon-emoji {
transform: scale(1.1);
}
/* Размеры карточек в сетке */
.large-card {
grid-column: span 2; /* Занимает 2 колонки из 3 */
min-height: 380px;
.card:hover .card-icon {
transform: translateY(-4px);
box-shadow: 0 8px 25px rgba(30, 48, 80, 0.15);
}
.small-card {
grid-column: span 1; /* Занимает 1 колонку из 3 */
min-height: 300px;
.icon-gold {
background: linear-gradient(135deg, rgba(212, 175, 55, 0.15) 0%, rgba(244, 208, 63, 0.1) 100%);
box-shadow: 0 4px 20px rgba(212, 175, 55, 0.2);
}
/* Цветовые темы карточек */
.light-bg {
background-color: #f6f8fb; /* Светло-серый/голубоватый */
box-shadow: inset 0 0 0 1px rgba(0,0,0,0.03);
.card:hover .icon-gold {
box-shadow: 0 8px 30px rgba(212, 175, 55, 0.3);
background: linear-gradient(135deg, rgba(212, 175, 55, 0.2) 0%, rgba(244, 208, 63, 0.15) 100%);
}
.white-bg {
background-color: #ffffff;
box-shadow: 0 10px 40px rgba(0, 0, 0, 0.05), 0 1px 3px rgba(0,0,0,0.02);
.icon-white {
background: rgba(255, 255, 255, 0.1);
border: 1px solid rgba(255, 255, 255, 0.2);
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.2);
}
.dark-bg {
background-color: #0b2341; /* Глубокий синий */
color: #ffffff;
.card:hover .icon-white {
background: rgba(255, 255, 255, 0.15);
box-shadow: 0 8px 30px rgba(0, 0, 0, 0.3);
}
/* Иконки в левом верхнем углу */
.icon {
width: 24px;
height: 24px;
margin-bottom: 2rem;
.icon-white .icon-emoji {
filter: drop-shadow(0 2px 4px rgba(0, 0, 0, 0.2));
}
.dark-icon { color: #1e3050; }
.gold-icon { color: #d1b06b; }
/* Типографика внутри карточек */
.card-content {
flex-grow: 1;
z-index: 2; /* Поверх водяных знаков */
}
/* Заголовки карточек сделаны шрифтом с засечками для солидности */
/* Typography */
.card-title {
font-family: Georgia, 'Times New Roman', Times, serif;
font-size: 1.4rem;
font-size: 1.5rem;
font-weight: 700;
margin: 0 0 1rem 0;
color: var(--color-primary);
margin: 0 0 0.75rem 0;
line-height: 1.3;
letter-spacing: -0.01em;
transition: color 0.3s ease;
}
.light-bg .card-title, .white-bg .card-title {
color: #1e3050;
.card:hover .card-title {
color: var(--color-accent);
}
.card-dark:hover .card-title {
color: var(--color-accent-light);
}
/* Описание карточек */
.card-desc {
font-family: Georgia, 'Times New Roman', Times, serif;
font-size: 0.95rem;
line-height: 1.6;
font-size: 1rem;
line-height: 1.7;
color: var(--color-gray);
margin: 0;
flex-grow: 1;
}
.light-bg .card-desc, .white-bg .card-desc {
color: #6c7a8c;
}
.dark-bg .card-desc {
color: #8c9bb0;
}
/* Фоновые водяные знаки (Огромные полупрозрачные иконки) */
.bg-watermark {
position: absolute;
z-index: 1;
}
/* Для первой карточки (Молоток по центру) */
.light-bg .bg-watermark {
top: 50%;
left: 50%;
transform: translate(-50%, -50%) rotate(-15deg);
width: 300px;
height: 300px;
color: rgba(210, 217, 228, 0.5); /* Очень бледный серо-голубой */
}
/* Для темной карточки (Весы в правом нижнем углу) */
.dark-bg .bg-watermark {
bottom: -20px;
right: 10px;
width: 200px;
height: 200px;
color: rgba(255, 255, 255, 0.03); /* Очень прозрачный белый */
}
/* Элементы действий (Ссылки и Кнопки) */
.text-link {
/* Arrow Indicator */
.card-arrow {
display: inline-flex;
align-items: center;
gap: 0.5rem;
color: #1e3050;
text-decoration: none;
font-weight: 700;
font-size: 0.95rem;
margin-top: 2rem;
z-index: 2;
transition: color 0.2s ease;
}
.text-link:hover {
color: #d1b06b;
}
/* Кнопка в темной карточке */
.card-content-bottom {
display: flex;
flex-direction: column;
justify-content: center;
height: 100%;
width: 44px;
height: 44px;
border-radius: 50%;
background: rgba(30, 48, 80, 0.08);
color: var(--color-primary);
margin-top: 1.5rem;
margin-left: auto;
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
opacity: 0.6;
}
.btn-outline {
align-self: flex-start;
margin-top: 2rem;
padding: 0.8rem 1.8rem;
color: #ffffff;
text-decoration: none;
font-weight: 600;
font-size: 0.9rem;
border: 1px solid rgba(255, 255, 255, 0.2);
border-radius: 4px;
background: rgba(255, 255, 255, 0.05);
transition: all 0.3s ease;
z-index: 2;
.card:hover .card-arrow {
background: var(--color-accent);
color: white;
opacity: 1;
transform: translateX(4px);
}
.btn-outline:hover {
background: #ffffff;
color: #0b2341;
/* Hover Effects */
.card:hover {
transform: translateY(-4px);
}
/* --- Адаптивность --- */
.card-featured:hover {
box-shadow: var(--shadow-xl), 0 20px 40px rgba(30, 48, 80, 0.15);
}
/* Планшеты (перестраиваем в 2 колонки) */
.card-standard:hover {
box-shadow: var(--shadow-xl), 0 20px 40px rgba(0, 0, 0, 0.1);
border-color: rgba(212, 175, 55, 0.3);
}
.card-dark:hover {
box-shadow: var(--shadow-xl), 0 0 80px rgba(11, 20, 38, 0.5);
border-color: rgba(212, 175, 55, 0.3);
}
/* Responsive */
@media (max-width: 1024px) {
.bento-grid {
grid-template-columns: repeat(2, 1fr);
}
.large-card {
grid-column: span 2; /* На планшете широкие карточки занимают всю ширину */
.card-wide {
grid-column: span 2;
}
.small-card {
grid-column: span 1; /* Маленькие встают по две в ряд */
.card-standard {
grid-column: span 1;
}
}
/* Мобильные телефоны (все в 1 колонку) */
@media (max-width: 768px) {
.services-section {
padding: 4rem 1.5rem;
padding: 4rem 1rem;
}
.bento-grid {
grid-template-columns: 1fr;
}
.large-card, .small-card {
.card-wide,
.card-standard {
grid-column: span 1;
min-height: auto;
}
.card {
padding: 2rem 1.5rem;
padding: 2rem;
}
.light-bg .bg-watermark {
width: 200px;
height: 200px;
.card-title {
font-size: 1.25rem;
}
.card-icon {
width: 56px;
height: 56px;
}
.icon-emoji {
font-size: 1.75rem;
}
}
/* Animation */
@keyframes fadeInUp {
from {
opacity: 0;
transform: translateY(30px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.card {
animation: fadeInUp 0.6s ease-out forwards;
opacity: 0;
}
.card:nth-child(1) { animation-delay: 0.1s; }
.card:nth-child(2) { animation-delay: 0.2s; }
.card:nth-child(3) { animation-delay: 0.3s; }
.card:nth-child(4) { animation-delay: 0.4s; }
</style>
<script>
// Glow effect on mouse move
document.querySelectorAll('.card').forEach((card) => {
(card as HTMLElement).addEventListener('mousemove', (e: MouseEvent) => {
const rect = (card as HTMLElement).getBoundingClientRect();
const x = e.clientX - rect.left;
const y = e.clientY - rect.top;
(card as HTMLElement).style.setProperty('--mouse-x', `${x}px`);
(card as HTMLElement).style.setProperty('--mouse-y', `${y}px`);
});
});
</script>

View file

@ -32,7 +32,7 @@ const features = [
---
<section class="why-us-section">
<div class="container">
<div class="site-container">
<!-- Левая колонка: Изображение -->
<div class="image-column">
@ -77,9 +77,7 @@ const features = [
font-family: system-ui, -apple-system, sans-serif;
}
.container {
max-width: 1300px;
margin: 0 auto;
.site-container {
display: grid;
grid-template-columns: 1fr 1fr; /* Две равные колонки */
gap: 6rem; /* Большой отступ между колонками */
@ -208,7 +206,7 @@ const features = [
/* --- Адаптивность --- */
@media (max-width: 1024px) {
.container {
.site-container {
gap: 3rem; /* Уменьшаем отступ между колонками на планшетах */
}
@ -223,7 +221,7 @@ const features = [
padding: 4rem 1.5rem;
}
.container {
.site-container {
grid-template-columns: 1fr; /* Одна колонка на мобильных */
gap: 4rem;
}

View file

@ -14,7 +14,7 @@ const legalLinks = [
---
<footer class="footer">
<div class="footer-container">
<div class="site-container footer-container">
<!-- Верхняя часть футера (Колонки) -->
<div class="footer-top">
@ -90,13 +90,7 @@ const legalLinks = [
color: #8c9bb0; /* Приглушенный серо-голубой цвет текста */
/* Используем шрифт с засечками (serif), как на макете */
font-family: Georgia, 'Times New Roman', Times, serif;
padding: 4rem 2rem 1.5rem 2rem;
}
.footer-container {
width: 100%;
max-width: var(--site-max-width);
margin: 0 auto;
padding: 4rem 0 1.5rem 0;
}
/* Сетка для колонок */

View file

@ -6,7 +6,7 @@ import MobileMenu from './MobileMenu.astro';
---
<header class="header-wrapper">
<div class="header-container">
<div class="site-container header-container">
<!-- Логотип -->
<Logo />
@ -78,22 +78,24 @@ import MobileMenu from './MobileMenu.astro';
<style>
/* Общая обертка хедера (фон и скругления как на скриншоте) */
.header-wrapper {
position: fixed;
top: 0;
left: 0;
right: 0;
background-color: #d1d9e4; /* Серо-голубой фон */
border-top-left-radius: 8px; /* Скругления из макета */
border-top-right-radius: 8px;
padding: 1.2rem 2rem;
padding: 1.2rem 0;
font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
z-index: 1000;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
}
/* Контейнер для выравнивания контента */
.header-container {
width: 100%;
max-width: var(--site-max-width);
margin: 0 auto;
display: flex;
justify-content: space-between;
align-items: center;
padding: 0 2rem;
}
/* Блок с телефоном и кнопкой */

View file

@ -7,7 +7,7 @@ export interface Props {
}
const {
href = '/login',
href = '/auth/sign-in',
variant = 'outline',
size = 'md',
class: className = '',

View file

@ -29,7 +29,7 @@ const { title, description, canonicalLink } = Astro.props;
</head>
<body>
<Header />
<main>
<main class="main-content">
<slot />
</main>
<Footer />
@ -37,6 +37,13 @@ const { title, description, canonicalLink } = Astro.props;
</body>
</html>
<style>
/* Отступ сверху для фиксированного хедера */
.main-content {
padding-top: 80px; /* Высота хедера + отступ */
}
</style>
<script>
// Клиентский скрипт для открытия модального окна
document.addEventListener('DOMContentLoaded', () => {

View file

@ -0,0 +1,299 @@
---
import Layout from '@layouts/Layout.astro';
const pageTitle = "Регистрация";
const pageDescription = "Создайте аккаунт для доступа к личному кабинету";
---
<Layout title={pageTitle} description={pageDescription}>
<div class="auth-page">
<div class="auth-container">
<div class="auth-card">
<div class="auth-header">
<h1>Регистрация</h1>
<p>Создайте аккаунт для доступа ко всем функциям</p>
</div>
<form class="auth-form" id="sign-up-form">
<div class="form-group">
<label for="name">ФИО</label>
<input
type="text"
id="name"
name="name"
placeholder="Иванов Иван Иванович"
required
autocomplete="name"
/>
</div>
<div class="form-group">
<label for="email">Email</label>
<input
type="email"
id="email"
name="email"
placeholder="example@mail.ru"
required
autocomplete="email"
/>
</div>
<div class="form-group">
<label for="phone">Телефон</label>
<input
type="tel"
id="phone"
name="phone"
placeholder="+7 (999) 000-00-00"
required
autocomplete="tel"
/>
</div>
<div class="form-group">
<label for="password">Пароль</label>
<input
type="password"
id="password"
name="password"
placeholder="••••••••"
required
autocomplete="new-password"
minlength="6"
/>
</div>
<div class="form-group">
<label for="confirmPassword">Подтвердите пароль</label>
<input
type="password"
id="confirmPassword"
name="confirmPassword"
placeholder="••••••••"
required
autocomplete="new-password"
minlength="6"
/>
</div>
<div class="form-options">
<label class="checkbox-label">
<input type="checkbox" name="agreement" required />
<span>Согласен с <a href="#">условиями обработки данных</a></span>
</label>
</div>
<button type="submit" class="btn-submit">
Зарегистрироваться
</button>
</form>
<div class="auth-footer">
<p>Уже есть аккаунт? <a href="/auth/sign-in">Войти</a></p>
</div>
</div>
</div>
</div>
</Layout>
<style>
/* Основная страница */
.auth-page {
min-height: calc(100vh - 160px);
background: linear-gradient(135deg, #f5f7fa 0%, #e8ecf1 100%);
display: flex;
align-items: center;
justify-content: center;
padding: 2rem;
}
.auth-container {
width: 100%;
max-width: 440px;
}
/* Карточка авторизации */
.auth-card {
background: #ffffff;
border-radius: 16px;
padding: 2rem 1.75rem;
box-shadow: 0 10px 40px rgba(0, 0, 0, 0.1);
}
/* Заголовок */
.auth-header {
text-align: center;
margin-bottom: 1.5rem;
}
.auth-header h1 {
color: #1e3050;
font-size: 1.5rem;
font-weight: 800;
margin: 0 0 0.5rem 0;
letter-spacing: -0.02em;
}
.auth-header p {
color: #64748b;
font-size: 0.9rem;
margin: 0;
}
/* Форма */
.auth-form {
display: flex;
flex-direction: column;
gap: 1rem;
}
.form-group {
display: flex;
flex-direction: column;
gap: 0.4rem;
}
.form-group label {
color: #1e3050;
font-size: 0.85rem;
font-weight: 600;
}
.form-group input {
padding: 0.7rem 0.9rem;
border: 1px solid #e2e8f0;
border-radius: 8px;
font-size: 0.95rem;
transition: all 0.2s ease;
background: #f8fafc;
}
.form-group input:focus {
outline: none;
border-color: #d4af37;
box-shadow: 0 0 0 3px rgba(212, 175, 55, 0.1);
background: #ffffff;
}
.form-group input::placeholder {
color: #94a3b8;
}
/* Опции формы */
.form-options {
font-size: 0.875rem;
}
.checkbox-label {
display: flex;
align-items: flex-start;
gap: 0.5rem;
color: #64748b;
cursor: pointer;
}
.checkbox-label input[type="checkbox"] {
width: 16px;
height: 16px;
accent-color: #d4af37;
cursor: pointer;
margin-top: 2px;
}
.checkbox-label a {
color: #d4af37;
text-decoration: none;
font-weight: 500;
}
.checkbox-label a:hover {
color: #b8941f;
}
/* Кнопка отправки */
.btn-submit {
background: linear-gradient(135deg, #eac26e 0%, #ce9f40 100%);
color: #ffffff;
border: none;
border-radius: 8px;
padding: 1rem;
font-size: 1rem;
font-weight: 700;
cursor: pointer;
transition: all 0.3s ease;
margin-top: 0.5rem;
}
.btn-submit:hover {
transform: translateY(-2px);
box-shadow: 0 8px 20px rgba(206, 159, 64, 0.4);
}
.btn-submit:active {
transform: translateY(0);
}
/* Подвал формы */
.auth-footer {
text-align: center;
margin-top: 1.25rem;
padding-top: 1.25rem;
border-top: 1px solid #e2e8f0;
}
.auth-footer p {
color: #64748b;
font-size: 0.9rem;
margin: 0;
}
.auth-footer a {
color: #d4af37;
text-decoration: none;
font-weight: 600;
transition: color 0.2s ease;
}
.auth-footer a:hover {
color: #b8941f;
}
/* Адаптивность */
@media (max-width: 480px) {
.auth-card {
padding: 2rem 1.5rem;
}
.auth-header h1 {
font-size: 1.5rem;
}
}
</style>
<script>
// Обработка отправки формы
document.getElementById('sign-up-form')?.addEventListener('submit', async (e) => {
e.preventDefault();
const form = e.target as HTMLFormElement;
const formData = new FormData(form);
const name = formData.get('name') as string;
const email = formData.get('email') as string;
const phone = formData.get('phone') as string;
const password = formData.get('password') as string;
const confirmPassword = formData.get('confirmPassword') as string;
// Проверка совпадения паролей
if (password !== confirmPassword) {
alert('Пароли не совпадают');
return;
}
// Здесь будет логика отправки на сервер
console.log('Регистрация:', { name, email, phone, password });
// Пример: await fetch('/api/auth/signup', { method: 'POST', body: JSON.stringify({ name, email, phone, password }) });
});
</script>

View file

@ -0,0 +1,261 @@
---
import Layout from '@layouts/Layout.astro';
const pageTitle = "Вход в аккаунт";
const pageDescription = "Войдите в свой аккаунт для доступа к личному кабинету";
---
<Layout title={pageTitle} description={pageDescription}>
<div class="auth-page">
<div class="auth-container">
<div class="auth-card">
<div class="auth-header">
<h1>Вход в аккаунт</h1>
<p>Введите свои данные для входа</p>
</div>
<form class="auth-form" id="sign-in-form">
<div class="form-group">
<label for="email">Email</label>
<input
type="email"
id="email"
name="email"
placeholder="example@mail.ru"
required
autocomplete="email"
/>
</div>
<div class="form-group">
<label for="password">Пароль</label>
<input
type="password"
id="password"
name="password"
placeholder="••••••••"
required
autocomplete="current-password"
/>
</div>
<div class="form-options">
<label class="checkbox-label">
<input type="checkbox" name="remember" />
<span>Запомнить меня</span>
</label>
<a href="#" class="forgot-link">Забыли пароль?</a>
</div>
<button type="submit" class="btn-submit">
Войти
</button>
</form>
<div class="auth-footer">
<p>Нет аккаунта? <a href="/auth/sig-up">Зарегистрироваться</a></p>
</div>
</div>
</div>
</div>
</Layout>
<style>
/* Основная страница */
.auth-page {
min-height: calc(100vh - 160px);
background: linear-gradient(135deg, #f5f7fa 0%, #e8ecf1 100%);
display: flex;
align-items: center;
justify-content: center;
padding: 2rem;
}
.auth-container {
width: 100%;
max-width: 440px;
}
/* Карточка авторизации */
.auth-card {
background: #ffffff;
border-radius: 16px;
padding: 2rem 1.75rem;
box-shadow: 0 10px 40px rgba(0, 0, 0, 0.1);
}
/* Заголовок */
.auth-header {
text-align: center;
margin-bottom: 1.5rem;
}
.auth-header h1 {
color: #1e3050;
font-size: 1.5rem;
font-weight: 800;
margin: 0 0 0.5rem 0;
letter-spacing: -0.02em;
}
.auth-header p {
color: #64748b;
font-size: 0.9rem;
margin: 0;
}
/* Форма */
.auth-form {
display: flex;
flex-direction: column;
gap: 1rem;
}
.form-group {
display: flex;
flex-direction: column;
gap: 0.4rem;
}
.form-group label {
color: #1e3050;
font-size: 0.85rem;
font-weight: 600;
}
.form-group input {
padding: 0.7rem 0.9rem;
border: 1px solid #e2e8f0;
border-radius: 8px;
font-size: 0.95rem;
transition: all 0.2s ease;
background: #f8fafc;
}
.form-group input:focus {
outline: none;
border-color: #d4af37;
box-shadow: 0 0 0 3px rgba(212, 175, 55, 0.1);
background: #ffffff;
}
.form-group input::placeholder {
color: #94a3b8;
}
/* Опции формы */
.form-options {
display: flex;
justify-content: space-between;
align-items: center;
font-size: 0.875rem;
}
.checkbox-label {
display: flex;
align-items: center;
gap: 0.5rem;
color: #64748b;
cursor: pointer;
}
.checkbox-label input[type="checkbox"] {
width: 16px;
height: 16px;
accent-color: #d4af37;
cursor: pointer;
}
.forgot-link {
color: #d4af37;
text-decoration: none;
font-weight: 500;
transition: color 0.2s ease;
}
.forgot-link:hover {
color: #b8941f;
}
/* Кнопка отправки */
.btn-submit {
background: linear-gradient(135deg, #eac26e 0%, #ce9f40 100%);
color: #ffffff;
border: none;
border-radius: 8px;
padding: 1rem;
font-size: 1rem;
font-weight: 700;
cursor: pointer;
transition: all 0.3s ease;
margin-top: 0.5rem;
}
.btn-submit:hover {
transform: translateY(-2px);
box-shadow: 0 8px 20px rgba(206, 159, 64, 0.4);
}
.btn-submit:active {
transform: translateY(0);
}
/* Подвал формы */
.auth-footer {
text-align: center;
margin-top: 1.25rem;
padding-top: 1.25rem;
border-top: 1px solid #e2e8f0;
}
.auth-footer p {
color: #64748b;
font-size: 0.9rem;
margin: 0;
}
.auth-footer a {
color: #d4af37;
text-decoration: none;
font-weight: 600;
transition: color 0.2s ease;
}
.auth-footer a:hover {
color: #b8941f;
}
/* Адаптивность */
@media (max-width: 480px) {
.auth-card {
padding: 2rem 1.5rem;
}
.auth-header h1 {
font-size: 1.5rem;
}
.form-options {
flex-direction: column;
align-items: flex-start;
gap: 0.75rem;
}
}
</style>
<script>
// Обработка отправки формы
document.getElementById('sign-in-form')?.addEventListener('submit', async (e) => {
e.preventDefault();
const form = e.target as HTMLFormElement;
const formData = new FormData(form);
const email = formData.get('email') as string;
const password = formData.get('password') as string;
// Здесь будет логика отправки на сервер
console.log('Вход:', { email, password });
// Пример: await fetch('/api/auth/signin', { method: 'POST', body: JSON.stringify({ email, password }) });
});
</script>

View file

@ -26,3 +26,10 @@
/* Ширина контейнера сайта */
--site-max-width: 1400px;
}
/* Единый контейнер для всех компонентов */
.site-container {
max-width: var(--site-max-width);
margin: 0 auto;
padding: 0 2rem;
}