🎯 Key Features: - Added QR-code generation for service requests in modal window - Integrated real-time verification via Telegram bot - Implemented animated success confirmation - Added status polling for request verification �� Technical Changes: - Fixed JavaScript syntax errors in modern-scripts.js - Enhanced services_modern.html with QR-code section and success animation - Added check_request_status API endpoint - Improved CSS with success checkmark animations 🎨 UX Improvements: - Centered QR-code display with proper styling - Real-time status checking every 3 seconds - Animated success confirmation only after Telegram verification - Auto-close modal after successful confirmation 📱 Workflow: 1. User fills service request form 2. QR-code generated and displayed 3. User scans QR/clicks Telegram link 4. System polls for verification status 5. Success animation shows after Telegram confirmation 6. Modal auto-closes with notification This completes the modern service request system with Telegram bot integration.
657 lines
29 KiB
HTML
657 lines
29 KiB
HTML
{% extends 'web/base_modern.html' %}
|
||
{% load static %}
|
||
{% block title %}Наши услуги - SmartSolTech{% endblock %}
|
||
|
||
{% block content %}
|
||
<!-- Hero Section -->
|
||
<section class="hero-modern">
|
||
<div class="container-modern">
|
||
<div class="row justify-content-center text-center">
|
||
<div class="col-lg-8">
|
||
<span class="badge bg-gradient text-white mb-3 px-3 py-2 rounded-pill">
|
||
⚡ Полный спектр услуг
|
||
</span>
|
||
<h1 class="display-4 fw-bold mb-4">
|
||
Наши <span class="text-gradient">IT-услуги</span>
|
||
</h1>
|
||
<p class="lead text-muted mb-5">
|
||
От концепции до запуска - мы предоставляем комплексные IT-решения для роста вашего бизнеса
|
||
</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- Services Grid -->
|
||
<section class="section-padding">
|
||
<div class="container-modern">
|
||
<!-- Service Categories Filter -->
|
||
<div class="text-center mb-5">
|
||
<div class="btn-group" role="group" aria-label="Категории услуг">
|
||
<button type="button" class="btn btn-outline-primary active" data-filter="all">
|
||
Все услуги
|
||
</button>
|
||
<button type="button" class="btn btn-outline-primary" data-filter="web">
|
||
Веб-разработка
|
||
</button>
|
||
<button type="button" class="btn btn-outline-primary" data-filter="mobile">
|
||
Мобильные приложения
|
||
</button>
|
||
<button type="button" class="btn btn-outline-primary" data-filter="design">
|
||
Дизайн
|
||
</button>
|
||
<button type="button" class="btn btn-outline-primary" data-filter="other">
|
||
Другое
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="row g-4" id="services-container">
|
||
{% for service in services %}
|
||
<div class="col-lg-4 col-md-6 service-item" data-category="{{ service.category.name|lower }}">
|
||
<div class="card-modern h-100">
|
||
<div class="position-relative overflow-hidden" style="height: 200px;">
|
||
{% if service.image %}
|
||
<img src="{{ service.image.url }}" class="w-100 h-100" style="object-fit: cover;" alt="{{ service.name }}">
|
||
{% else %}
|
||
<div class="w-100 h-100 bg-gradient d-flex align-items-center justify-content-center">
|
||
<i class="fas fa-cogs text-white" style="font-size: 3rem;"></i>
|
||
</div>
|
||
{% endif %}
|
||
<div class="position-absolute top-0 start-0 p-3">
|
||
<span class="badge bg-primary">{{ service.category.name }}</span>
|
||
</div>
|
||
</div>
|
||
<div class="card-body d-flex flex-column">
|
||
<h5 class="mb-3">{{ service.name }}</h5>
|
||
<p class="text-muted flex-grow-1">{{ service.description|truncatewords:15 }}</p>
|
||
|
||
<!-- Service Features -->
|
||
<div class="mb-3">
|
||
<small class="text-muted d-block mb-2">Что входит:</small>
|
||
<div class="d-flex flex-wrap gap-1">
|
||
<span class="badge bg-light text-dark">Консультация</span>
|
||
<span class="badge bg-light text-dark">Разработка</span>
|
||
<span class="badge bg-light text-dark">Тестирование</span>
|
||
<span class="badge bg-light text-dark">Поддержка</span>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Price Range -->
|
||
<div class="d-flex justify-content-between align-items-center mb-3">
|
||
<div>
|
||
<small class="text-muted">От</small>
|
||
<span class="h5 text-primary mb-0">₩ {{ service.price|default:"По запросу" }}</span>
|
||
</div>
|
||
<div class="text-end">
|
||
<small class="text-muted">Срок</small>
|
||
<div class="fw-semibold">2-4 недели</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="d-flex gap-2">
|
||
<a href="{% url 'service_detail' service.pk %}" class="btn btn-outline-primary flex-grow-1">
|
||
Подробнее
|
||
</a>
|
||
<button class="btn btn-primary-modern"
|
||
onclick="openServiceModal({{ service.pk }}, '{{ service.name }}')">
|
||
<i class="fas fa-paper-plane"></i>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
{% endfor %}
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- Why Choose Our Services -->
|
||
<section class="section-padding bg-light">
|
||
<div class="container-modern">
|
||
<div class="row justify-content-center text-center mb-5">
|
||
<div class="col-lg-8">
|
||
<h2 class="display-6 fw-bold mb-4">
|
||
Почему выбирают <span class="text-gradient">наши услуги</span>
|
||
</h2>
|
||
<p class="lead text-muted">
|
||
Мы обеспечиваем высокое качество и профессиональный подход в каждом проекте
|
||
</p>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="row g-4">
|
||
<div class="col-lg-3 col-md-6">
|
||
<div class="text-center">
|
||
<div class="service-icon mx-auto mb-3 bg-primary">
|
||
<i class="fas fa-clock text-white"></i>
|
||
</div>
|
||
<h5>Быстро</h5>
|
||
<p class="text-muted">Соблюдаем сроки и работаем оперативно</p>
|
||
</div>
|
||
</div>
|
||
<div class="col-lg-3 col-md-6">
|
||
<div class="text-center">
|
||
<div class="service-icon mx-auto mb-3 bg-success">
|
||
<i class="fas fa-shield-alt text-white"></i>
|
||
</div>
|
||
<h5>Надежно</h5>
|
||
<p class="text-muted">Гарантия качества и стабильность работы</p>
|
||
</div>
|
||
</div>
|
||
<div class="col-lg-3 col-md-6">
|
||
<div class="text-center">
|
||
<div class="service-icon mx-auto mb-3 bg-warning">
|
||
<i class="fas fa-headset text-white"></i>
|
||
</div>
|
||
<h5>Поддержка</h5>
|
||
<p class="text-muted">24/7 техническая поддержка проектов</p>
|
||
</div>
|
||
</div>
|
||
<div class="col-lg-3 col-md-6">
|
||
<div class="text-center">
|
||
<div class="service-icon mx-auto mb-3 bg-info">
|
||
<i class="fas fa-chart-line text-white"></i>
|
||
</div>
|
||
<h5>Результативно</h5>
|
||
<p class="text-muted">Фокус на бизнес-результате клиента</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- Process Section -->
|
||
<section class="section-padding">
|
||
<div class="container-modern">
|
||
<div class="row justify-content-center text-center mb-5">
|
||
<div class="col-lg-8">
|
||
<h2 class="display-6 fw-bold mb-4">
|
||
Как мы <span class="text-gradient">работаем</span>
|
||
</h2>
|
||
<p class="lead text-muted">
|
||
Простой и прозрачный процесс от идеи до готового решения
|
||
</p>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="row">
|
||
<div class="col-12">
|
||
<div class="position-relative">
|
||
<!-- Timeline -->
|
||
<div class="timeline">
|
||
<div class="timeline-item">
|
||
<div class="timeline-marker bg-primary">1</div>
|
||
<div class="timeline-content">
|
||
<h5>Анализ и планирование</h5>
|
||
<p class="text-muted">Изучаем ваши потребности и составляем техническое задание</p>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="timeline-item">
|
||
<div class="timeline-marker bg-secondary">2</div>
|
||
<div class="timeline-content">
|
||
<h5>Дизайн и прототипирование</h5>
|
||
<p class="text-muted">Создаем дизайн-макеты и интерактивные прототипы</p>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="timeline-item">
|
||
<div class="timeline-marker bg-success">3</div>
|
||
<div class="timeline-content">
|
||
<h5>Разработка и тестирование</h5>
|
||
<p class="text-muted">Программируем решение и тщательно тестируем</p>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="timeline-item">
|
||
<div class="timeline-marker bg-warning">4</div>
|
||
<div class="timeline-content">
|
||
<h5>Запуск и сопровождение</h5>
|
||
<p class="text-muted">Запускаем проект и обеспечиваем техническую поддержку</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- CTA Section -->
|
||
<section class="section-padding bg-gradient text-white">
|
||
<div class="container-modern text-center">
|
||
<div class="row justify-content-center">
|
||
<div class="col-lg-8">
|
||
<h2 class="display-6 fw-bold mb-4">
|
||
Начнем ваш проект сегодня
|
||
</h2>
|
||
<p class="lead mb-5 opacity-90">
|
||
Получите бесплатную консультацию и расчет стоимости проекта
|
||
</p>
|
||
<div class="d-flex flex-wrap gap-3 justify-content-center">
|
||
<button class="btn btn-light btn-lg text-primary" onclick="openServiceModal(0, 'Консультация')">
|
||
<i class="fas fa-comments me-2"></i>
|
||
Бесплатная консультация
|
||
</button>
|
||
<a href="#" class="btn btn-outline-light btn-lg">
|
||
<i class="fas fa-download me-2"></i>
|
||
Скачать прайс-лист
|
||
</a>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- Service Request Modal -->
|
||
<div class="modal fade" id="serviceModal" tabindex="-1" aria-labelledby="serviceModalLabel" aria-hidden="true">
|
||
<div class="modal-dialog modal-lg">
|
||
<div class="modal-content border-0 shadow-lg">
|
||
<div class="modal-header bg-gradient text-white border-0">
|
||
<h5 class="modal-title" id="serviceModalLabel">
|
||
<i class="fas fa-paper-plane me-2"></i>
|
||
Заказать услугу
|
||
</h5>
|
||
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal" aria-label="Close"></button>
|
||
</div>
|
||
<div class="modal-body p-4">
|
||
<form id="serviceRequestForm">
|
||
{% csrf_token %}
|
||
<input type="hidden" id="serviceId" name="service_id">
|
||
|
||
<div class="row g-3">
|
||
<div class="col-md-6">
|
||
<label for="firstName" class="form-label">Имя *</label>
|
||
<input type="text" class="form-control" id="firstName" name="first_name" required>
|
||
</div>
|
||
<div class="col-md-6">
|
||
<label for="lastName" class="form-label">Фамилия *</label>
|
||
<input type="text" class="form-control" id="lastName" name="last_name" required>
|
||
</div>
|
||
<div class="col-md-6">
|
||
<label for="email" class="form-label">Email *</label>
|
||
<input type="email" class="form-control" id="email" name="email" required>
|
||
</div>
|
||
<div class="col-md-6">
|
||
<label for="phone" class="form-label">Телефон</label>
|
||
<input type="tel" class="form-control" id="phone" name="phone">
|
||
</div>
|
||
<div class="col-12">
|
||
<label for="description" class="form-label">Описание проекта *</label>
|
||
<textarea class="form-control" id="description" name="description" rows="4"
|
||
placeholder="Опишите ваш проект, цели и требования..." required></textarea>
|
||
</div>
|
||
<div class="col-md-6">
|
||
<label for="budget" class="form-label">Примерный бюджет</label>
|
||
<select class="form-select" id="budget" name="budget">
|
||
<option value="">Не определен</option>
|
||
<option value="1000-5000">₩ 1,000,000 - 5,000,000</option>
|
||
<option value="5000-10000">₩ 5,000,000 - 10,000,000</option>
|
||
<option value="10000+">₩ 10,000,000+</option>
|
||
</select>
|
||
</div>
|
||
<div class="col-md-6">
|
||
<label for="timeline" class="form-label">Желаемые сроки</label>
|
||
<select class="form-select" id="timeline" name="timeline">
|
||
<option value="">Не определены</option>
|
||
<option value="urgent">Срочно (1-2 недели)</option>
|
||
<option value="normal">Обычно (1-2 месяца)</option>
|
||
<option value="flexible">Гибкие сроки</option>
|
||
</select>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- QR Code Section (Hidden by default) -->
|
||
<div class="mt-4" id="qrCodeSection" style="display: none;">
|
||
<div class="alert alert-info text-center">
|
||
<h6><i class="fas fa-qrcode me-2"></i>Завершите регистрацию через Telegram</h6>
|
||
<p class="mb-3">Отсканируйте QR-код или перейдите по ссылке для подтверждения заявки:</p>
|
||
<div class="d-flex justify-content-center mb-3">
|
||
<img id="qrCodeImage" src="" alt="QR Code" class="img-fluid border rounded" style="max-width: 200px; min-width: 200px; height: 200px; object-fit: contain; display: none;">
|
||
</div>
|
||
<div class="mb-3">
|
||
<a id="telegramLink" href="" target="_blank" class="btn btn-info">
|
||
<i class="fab fa-telegram-plane me-2"></i>Открыть в Telegram
|
||
</a>
|
||
</div>
|
||
<div class="d-flex align-items-center justify-content-center">
|
||
<div class="spinner-border spinner-border-sm text-primary me-2" role="status" aria-hidden="true"></div>
|
||
<small class="text-muted">Ожидаем подтверждения в Telegram...</small>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Success Animation Section (Hidden by default) -->
|
||
<div class="mt-4" id="successSection" style="display: none;">
|
||
<div class="text-center py-5">
|
||
<div class="success-checkmark">
|
||
<div class="check-icon">
|
||
<span class="icon-line line-tip"></span>
|
||
<span class="icon-line line-long"></span>
|
||
<div class="icon-circle"></div>
|
||
<div class="icon-fix"></div>
|
||
</div>
|
||
</div>
|
||
<h4 class="text-success mt-3 mb-2">Заявка подана успешно!</h4>
|
||
<p class="text-muted">Мы свяжемся с вами в ближайшее время</p>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="mt-4">
|
||
<div class="form-check">
|
||
<input class="form-check-input" type="checkbox" id="agreeTerms" required>
|
||
<label class="form-check-label small" for="agreeTerms">
|
||
Я соглашаюсь с <a href="#" class="text-primary">условиями обработки персональных данных</a>
|
||
</label>
|
||
</div>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
<div class="modal-footer border-0">
|
||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Отмена</button>
|
||
<button type="submit" form="serviceRequestForm" class="btn btn-primary-modern">
|
||
<i class="fas fa-paper-plane me-2"></i>
|
||
Отправить заявку
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
{% endblock %}
|
||
|
||
{% block extra_scripts %}
|
||
<script>
|
||
// Service filtering
|
||
document.addEventListener('DOMContentLoaded', function() {
|
||
const filterButtons = document.querySelectorAll('[data-filter]');
|
||
const serviceItems = document.querySelectorAll('.service-item');
|
||
|
||
filterButtons.forEach(button => {
|
||
button.addEventListener('click', function() {
|
||
const filter = this.getAttribute('data-filter');
|
||
|
||
// Update active button
|
||
filterButtons.forEach(btn => btn.classList.remove('active'));
|
||
this.classList.add('active');
|
||
|
||
// Filter services
|
||
serviceItems.forEach(item => {
|
||
if (filter === 'all' || item.getAttribute('data-category').includes(filter)) {
|
||
item.style.display = 'block';
|
||
setTimeout(() => {
|
||
item.style.opacity = '1';
|
||
item.style.transform = 'translateY(0)';
|
||
}, 50);
|
||
} else {
|
||
item.style.opacity = '0';
|
||
item.style.transform = 'translateY(20px)';
|
||
setTimeout(() => {
|
||
item.style.display = 'none';
|
||
}, 300);
|
||
}
|
||
});
|
||
});
|
||
});
|
||
});
|
||
|
||
// Service modal
|
||
function openServiceModal(serviceId, serviceName) {
|
||
document.getElementById('serviceId').value = serviceId;
|
||
document.getElementById('serviceModalLabel').innerHTML =
|
||
'<i class="fas fa-paper-plane me-2"></i>Заказать услугу: ' + serviceName;
|
||
|
||
const modal = new bootstrap.Modal(document.getElementById('serviceModal'));
|
||
modal.show();
|
||
}
|
||
|
||
// Form submission
|
||
document.getElementById('serviceRequestForm').addEventListener('submit', function(e) {
|
||
e.preventDefault();
|
||
|
||
const formData = new FormData(this);
|
||
const submitBtn = document.querySelector('button[type="submit"][form="serviceRequestForm"]');
|
||
const originalContent = submitBtn.innerHTML;
|
||
|
||
submitBtn.innerHTML = '<i class="fas fa-spinner fa-spin me-2"></i>Отправляем...';
|
||
submitBtn.disabled = true;
|
||
|
||
// Получаем CSRF токен
|
||
const csrfToken = document.querySelector('[name=csrfmiddlewaretoken]').value;
|
||
|
||
// Подготавливаем данные для отправки
|
||
const serviceId = document.getElementById('serviceId').value;
|
||
const requestData = {
|
||
client_name: formData.get('first_name') + ' ' + formData.get('last_name'),
|
||
client_email: formData.get('email'),
|
||
client_phone: formData.get('phone') || '',
|
||
description: formData.get('description'),
|
||
budget: formData.get('budget') || '',
|
||
timeline: formData.get('timeline') || ''
|
||
};
|
||
|
||
// Отправляем запрос на создание QR-кода
|
||
fetch(`/service/generate_qr_code/${serviceId}/`, {
|
||
method: 'POST',
|
||
headers: {
|
||
'Content-Type': 'application/json',
|
||
'X-CSRFToken': csrfToken
|
||
},
|
||
body: JSON.stringify(requestData)
|
||
})
|
||
.then(response => response.json())
|
||
.then(data => {
|
||
if (data.qr_code_url) {
|
||
// Показываем секцию с QR-кодом
|
||
const qrSection = document.getElementById('qrCodeSection');
|
||
const qrImage = document.getElementById('qrCodeImage');
|
||
const telegramLink = document.getElementById('telegramLink');
|
||
|
||
qrImage.src = data.qr_code_url;
|
||
qrImage.style.display = 'block';
|
||
telegramLink.href = data.registration_link;
|
||
qrSection.style.display = 'block';
|
||
|
||
// Обновляем кнопку
|
||
submitBtn.innerHTML = '<i class="fas fa-telegram-plane me-2"></i>Перейдите в Telegram для подтверждения';
|
||
submitBtn.disabled = true;
|
||
|
||
// Начинаем проверку статуса подтверждения
|
||
const serviceRequestId = data.service_request_id;
|
||
confirmationCheckInterval = setInterval(() => {
|
||
checkConfirmationStatus(serviceRequestId, confirmationCheckInterval);
|
||
}, 3000); // Проверяем каждые 3 секунды
|
||
|
||
} else {
|
||
throw new Error(data.error || 'Ошибка при создании заявки');
|
||
}
|
||
})
|
||
.catch(error => {
|
||
console.error('Error:', error);
|
||
submitBtn.innerHTML = originalContent;
|
||
submitBtn.disabled = false;
|
||
submitBtn.classList.remove('btn-success');
|
||
submitBtn.classList.add('btn-primary-modern');
|
||
|
||
showNotification('Произошла ошибка при создании заявки. Попробуйте еще раз.', 'error');
|
||
});
|
||
});
|
||
|
||
// Функция проверки статуса подтверждения
|
||
function checkConfirmationStatus(serviceRequestId, checkInterval) {
|
||
fetch(`/service/check_status/${serviceRequestId}/`, {
|
||
method: 'GET',
|
||
headers: {
|
||
'Content-Type': 'application/json',
|
||
}
|
||
})
|
||
.then(response => response.json())
|
||
.then(data => {
|
||
if (data.is_verified) {
|
||
// Останавливаем проверку
|
||
clearInterval(checkInterval);
|
||
|
||
// Скрываем QR-код
|
||
const qrSection = document.getElementById('qrCodeSection');
|
||
qrSection.style.display = 'none';
|
||
|
||
// Показываем анимацию успеха
|
||
const successSection = document.getElementById('successSection');
|
||
successSection.style.display = 'block';
|
||
|
||
// Запускаем анимацию галочки
|
||
setTimeout(() => {
|
||
const checkmark = successSection.querySelector('.success-checkmark');
|
||
checkmark.classList.add('animate');
|
||
}, 100);
|
||
|
||
// Закрываем модальное окно через 3 секунды
|
||
setTimeout(() => {
|
||
const modal = bootstrap.Modal.getInstance(document.getElementById('serviceModal'));
|
||
modal.hide();
|
||
showNotification('Заявка подтверждена! Спасибо за регистрацию в Telegram.', 'success');
|
||
}, 3000);
|
||
}
|
||
})
|
||
.catch(error => {
|
||
console.log('Ожидаем подтверждения...', error);
|
||
});
|
||
}
|
||
|
||
function showNotification(message, type) {
|
||
const notification = document.createElement('div');
|
||
notification.className = `alert alert-${type} position-fixed top-0 end-0 m-3`;
|
||
notification.style.zIndex = '9999';
|
||
notification.innerHTML = `
|
||
<i class="fas fa-${type === 'success' ? 'check-circle' : 'exclamation-triangle'} me-2"></i>
|
||
${message}
|
||
<button type="button" class="btn-close" onclick="this.parentElement.remove()"></button>
|
||
`;
|
||
|
||
document.body.appendChild(notification);
|
||
|
||
setTimeout(() => {
|
||
notification.remove();
|
||
}, 5000);
|
||
}
|
||
|
||
// Reset modal when it's hidden
|
||
let confirmationCheckInterval = null;
|
||
|
||
document.getElementById('serviceModal').addEventListener('hidden.bs.modal', function() {
|
||
// Останавливаем проверку подтверждения
|
||
if (confirmationCheckInterval) {
|
||
clearInterval(confirmationCheckInterval);
|
||
confirmationCheckInterval = null;
|
||
}
|
||
|
||
// Сброс формы
|
||
document.getElementById('serviceRequestForm').reset();
|
||
|
||
// Скрытие всех секций
|
||
document.getElementById('qrCodeSection').style.display = 'none';
|
||
document.getElementById('qrCodeImage').style.display = 'none';
|
||
document.getElementById('successSection').style.display = 'none';
|
||
|
||
// Убираем анимацию галочки
|
||
const checkmark = document.querySelector('.success-checkmark');
|
||
if (checkmark) {
|
||
checkmark.classList.remove('animate');
|
||
}
|
||
|
||
// Сброс кнопки отправки
|
||
const submitBtn = document.querySelector('button[type="submit"][form="serviceRequestForm"]');
|
||
submitBtn.innerHTML = '<i class="fas fa-paper-plane me-2"></i>Отправить заявку';
|
||
submitBtn.disabled = false;
|
||
submitBtn.classList.remove('btn-success');
|
||
submitBtn.classList.add('btn-primary-modern');
|
||
});
|
||
</script>
|
||
|
||
<style>
|
||
.timeline {
|
||
position: relative;
|
||
}
|
||
|
||
.timeline::before {
|
||
content: '';
|
||
position: absolute;
|
||
left: 30px;
|
||
top: 0;
|
||
bottom: 0;
|
||
width: 2px;
|
||
background: var(--border-color);
|
||
}
|
||
|
||
.timeline-item {
|
||
position: relative;
|
||
padding-left: 80px;
|
||
margin-bottom: 2rem;
|
||
}
|
||
|
||
.timeline-marker {
|
||
position: absolute;
|
||
left: 0;
|
||
top: 0;
|
||
width: 60px;
|
||
height: 60px;
|
||
border-radius: 50%;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
color: white;
|
||
font-weight: bold;
|
||
font-size: 1.25rem;
|
||
z-index: 1;
|
||
}
|
||
|
||
.timeline-content {
|
||
background: var(--bg-light);
|
||
padding: 1.5rem;
|
||
border-radius: 12px;
|
||
border: 1px solid var(--border-color);
|
||
}
|
||
|
||
.service-item {
|
||
transition: all 0.3s ease;
|
||
}
|
||
|
||
.btn-group .btn {
|
||
border-radius: 25px !important;
|
||
margin: 0 5px;
|
||
}
|
||
|
||
.btn-group .btn.active {
|
||
background: var(--gradient-primary);
|
||
border-color: transparent;
|
||
color: white;
|
||
}
|
||
|
||
@media (max-width: 768px) {
|
||
.timeline::before {
|
||
left: 15px;
|
||
}
|
||
|
||
.timeline-item {
|
||
padding-left: 50px;
|
||
}
|
||
|
||
.timeline-marker {
|
||
width: 40px;
|
||
height: 40px;
|
||
font-size: 1rem;
|
||
}
|
||
|
||
.btn-group {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
gap: 10px;
|
||
justify-content: center;
|
||
}
|
||
|
||
.btn-group .btn {
|
||
margin: 0;
|
||
}
|
||
}
|
||
</style>
|
||
{% endblock %} |