56a5a4ea50
modified: begushiybashkir/bbvue/src/views/Reviews.vue modified: begushiybashkir/bbvue/src/views/Training.vue add Members page for begushiybashkir site
1634 lines
39 KiB
Vue
1634 lines
39 KiB
Vue
<template>
|
||
<div class="reviews-page">
|
||
<!-- Герой-секция -->
|
||
<section class="hero-section">
|
||
<div class="container">
|
||
<div class="hero-content">
|
||
<h1 class="hero-title">⭐ Отзывы участников</h1>
|
||
<p class="hero-subtitle">Реальные истории и достижения наших бегунов</p>
|
||
<div class="hero-stats">
|
||
<div class="stat">
|
||
<div class="stat-number">{{ averageRating }}</div>
|
||
<div class="stat-label">Средний рейтинг</div>
|
||
</div>
|
||
<div class="stat">
|
||
<div class="stat-number">{{ totalReviews }}</div>
|
||
<div class="stat-label">Отзывов</div>
|
||
</div>
|
||
<div class="stat">
|
||
<div class="stat-number">{{ successStories }}</div>
|
||
<div class="stat-label">Историй успеха</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- Основной контент -->
|
||
<section class="reviews-section">
|
||
<div class="container">
|
||
<div class="reviews-header">
|
||
<h2 class="section-title">Что говорят наши участники</h2>
|
||
<div class="reviews-controls">
|
||
<div class="sort-controls">
|
||
<label for="sort">Сортировка:</label>
|
||
<select id="sort" v-model="sortBy" @change="sortReviews">
|
||
<option value="newest">Сначала новые</option>
|
||
<option value="oldest">Сначала старые</option>
|
||
<option value="highest">Высокий рейтинг</option>
|
||
<option value="lowest">Низкий рейтинг</option>
|
||
</select>
|
||
</div>
|
||
<div class="filter-controls">
|
||
<button
|
||
v-for="filter in ratingFilters"
|
||
:key="filter.value"
|
||
:class="['filter-btn', { 'active': activeRatingFilter === filter.value }]"
|
||
@click="filterByRating(filter.value)"
|
||
>
|
||
{{ filter.label }}
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Сетка отзывов -->
|
||
<div class="reviews-grid">
|
||
<div
|
||
v-for="review in paginatedReviews"
|
||
:key="review.id"
|
||
class="review-card"
|
||
:class="getReviewCardClass(review.rating)"
|
||
>
|
||
<div class="review-header">
|
||
<div class="reviewer-info">
|
||
<div class="reviewer-avatar">
|
||
{{ getInitials(review.author) }}
|
||
</div>
|
||
<div class="reviewer-details">
|
||
<h4 class="review-author">{{ review.author }}</h4>
|
||
<p class="reviewer-achievement" v-if="review.achievement">
|
||
🏆 {{ review.achievement }}
|
||
</p>
|
||
<p class="review-membership">
|
||
Участник {{ review.membership }}
|
||
</p>
|
||
</div>
|
||
</div>
|
||
<div class="review-rating">
|
||
<div class="stars">
|
||
<span
|
||
v-for="star in 5"
|
||
:key="star"
|
||
:class="['star', { 'filled': star <= review.rating }]"
|
||
>
|
||
★
|
||
</span>
|
||
</div>
|
||
<span class="rating-value">{{ review.rating }}/5</span>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="review-content">
|
||
<p class="review-text">{{ review.text }}</p>
|
||
|
||
<div class="review-meta">
|
||
<div class="meta-items">
|
||
<span class="meta-item" v-if="review.distance">
|
||
🏃 {{ review.distance }}
|
||
</span>
|
||
<span class="meta-item" v-if="review.improvement">
|
||
📈 {{ review.improvement }}
|
||
</span>
|
||
<span class="meta-item" v-if="review.trainings">
|
||
💪 {{ review.trainings }} тренировок
|
||
</span>
|
||
</div>
|
||
<span class="review-date">{{ formatDate(review.date) }}</span>
|
||
</div>
|
||
|
||
<div class="review-actions" v-if="review.verified">
|
||
<span class="verified-badge">✅ Проверенный отзыв</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Пагинация -->
|
||
<div class="pagination" v-if="totalPages > 1">
|
||
<button
|
||
class="pagination-btn"
|
||
:disabled="currentPage === 1"
|
||
@click="changePage(currentPage - 1)"
|
||
>
|
||
‹
|
||
</button>
|
||
|
||
<button
|
||
v-for="page in visiblePages"
|
||
:key="page"
|
||
:class="['pagination-btn', { 'active': currentPage === page }]"
|
||
@click="changePage(page)"
|
||
>
|
||
{{ page }}
|
||
</button>
|
||
|
||
<button
|
||
class="pagination-btn"
|
||
:disabled="currentPage === totalPages"
|
||
@click="changePage(currentPage + 1)"
|
||
>
|
||
›
|
||
</button>
|
||
</div>
|
||
|
||
<!-- Статистика отзывов -->
|
||
<div class="reviews-stats">
|
||
<h3>📊 Распределение оценок</h3>
|
||
<div class="rating-bars">
|
||
<div
|
||
v-for="rating in 5"
|
||
:key="rating"
|
||
class="rating-bar"
|
||
>
|
||
<div class="rating-label">
|
||
<span class="stars-small">
|
||
<span
|
||
v-for="star in 5"
|
||
:key="star"
|
||
:class="['star-small', { 'filled': star <= (6 - rating) }]"
|
||
>
|
||
★
|
||
</span>
|
||
</span>
|
||
</div>
|
||
<div class="bar-container">
|
||
<div
|
||
class="bar-fill"
|
||
:style="{ width: getRatingPercentage(6 - rating) + '%' }"
|
||
></div>
|
||
</div>
|
||
<div class="rating-count">
|
||
{{ getRatingCount(6 - rating) }}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- Форма отзыва -->
|
||
<section class="add-review-section" id="add-review">
|
||
<div class="container">
|
||
<div class="add-review-content">
|
||
<div class="form-header">
|
||
<h2 class="section-title">✍️ Поделитесь своим опытом</h2>
|
||
<p>Расскажите о своих достижениях и впечатлениях от тренировок</p>
|
||
</div>
|
||
|
||
<form class="review-form" @submit.prevent="submitReview" v-if="!isAuthenticated">
|
||
<div class="auth-required">
|
||
<div class="auth-message">
|
||
<h3>🔐 Войдите, чтобы оставить отзыв</h3>
|
||
<p>Только участники клуба могут оставлять отзывы</p>
|
||
</div>
|
||
<div class="auth-actions">
|
||
<router-link to="/login" class="btn btn-primary">
|
||
🔑 Войти в аккаунт
|
||
</router-link>
|
||
<router-link to="/register" class="btn btn-secondary">
|
||
🏃 Стать участником
|
||
</router-link>
|
||
</div>
|
||
</div>
|
||
</form>
|
||
|
||
<form class="review-form" @submit.prevent="submitReview" v-else>
|
||
<div class="form-group">
|
||
<label>Ваша оценка *</label>
|
||
<div class="rating-input">
|
||
<button
|
||
v-for="star in 5"
|
||
:key="star"
|
||
type="button"
|
||
:class="['star-btn', { 'active': newReview.rating >= star }]"
|
||
@click="newReview.rating = star"
|
||
>
|
||
★
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="form-row">
|
||
<div class="form-group">
|
||
<label for="achievement">Ваше достижение</label>
|
||
<input
|
||
id="achievement"
|
||
v-model="newReview.achievement"
|
||
type="text"
|
||
placeholder="Например: пробежал первый марафон"
|
||
>
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="distance">Любимая дистанция</label>
|
||
<select id="distance" v-model="newReview.distance">
|
||
<option value="">Выберите дистанцию</option>
|
||
<option value="5 км">5 км</option>
|
||
<option value="10 км">10 км</option>
|
||
<option value="21.1 км">Полумарафон (21.1 км)</option>
|
||
<option value="42.2 км">Марафон (42.2 км)</option>
|
||
<option value="Трейл">Трейл</option>
|
||
</select>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="form-group">
|
||
<label for="review-text">Ваш отзыв *</label>
|
||
<textarea
|
||
id="review-text"
|
||
v-model="newReview.text"
|
||
rows="5"
|
||
placeholder="Расскажите о вашем опыте в клубе, достижениях, атмосфере..."
|
||
required
|
||
></textarea>
|
||
<div class="char-counter">
|
||
{{ newReview.text.length }}/500 символов
|
||
</div>
|
||
</div>
|
||
|
||
<div class="form-actions">
|
||
<button
|
||
type="submit"
|
||
class="btn btn-primary"
|
||
:disabled="!isFormValid || submitting"
|
||
>
|
||
<span v-if="submitting">📤 Отправка...</span>
|
||
<span v-else>📝 Опубликовать отзыв</span>
|
||
</button>
|
||
<button
|
||
type="button"
|
||
class="btn btn-outline"
|
||
@click="resetForm"
|
||
>
|
||
🗑️ Очистить
|
||
</button>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- Призыв к действию -->
|
||
<section class="cta-section">
|
||
<div class="container">
|
||
<div class="cta-content">
|
||
<h2>Готовы присоединиться к нашему сообществу?</h2>
|
||
<p>Станьте следующей историей успеха в нашем клубе!</p>
|
||
|
||
<div class="success-stories-preview">
|
||
<div class="story-card">
|
||
<div class="story-avatar">А</div>
|
||
<div class="story-content">
|
||
<h4>Анна</h4>
|
||
<p>"С нуля до марафона за 1 год! Спасибо тренеру и команде!"</p>
|
||
<span class="story-achievement">🏅 Марафон 4:20:15</span>
|
||
</div>
|
||
</div>
|
||
<div class="story-card">
|
||
<div class="story-avatar">Д</div>
|
||
<div class="story-content">
|
||
<h4>Данил</h4>
|
||
<p>"Нашел друзей и улучшил результат на 10км на 15 минут"</p>
|
||
<span class="story-achievement">⚡ 10км за 42:30</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="cta-features">
|
||
<div class="cta-feature">
|
||
<div class="feature-icon">👨🏫</div>
|
||
<div class="feature-text">
|
||
<strong>Профессиональный тренер</strong>
|
||
<span>Мастер спорта с индивидуальным подходом</span>
|
||
</div>
|
||
</div>
|
||
<div class="cta-feature">
|
||
<div class="feature-icon">👥</div>
|
||
<div class="feature-text">
|
||
<strong>Дружное сообщество</strong>
|
||
<span>Поддержка и мотивация единомышленников</span>
|
||
</div>
|
||
</div>
|
||
<div class="cta-feature">
|
||
<div class="feature-icon">📈</div>
|
||
<div class="feature-text">
|
||
<strong>Личный прогресс</strong>
|
||
<span>Регулярное улучшение результатов</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="cta-actions">
|
||
<router-link to="/training" class="btn btn-primary btn-large">
|
||
🏃 Записаться на тренировку
|
||
</router-link>
|
||
<router-link to="/register" class="btn btn-secondary">
|
||
👥 Вступить в клуб
|
||
</router-link>
|
||
</div>
|
||
|
||
<div class="cta-contacts">
|
||
<p>Есть вопросы? Свяжитесь с нами:</p>
|
||
<div class="contact-links">
|
||
<a href="https://t.me/begushiybashkir" class="contact-link">📱 Telegram</a>
|
||
<a href="tel:+79273093095" class="contact-link">📞 +7 (927) 30-93-095</a>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
</div>
|
||
</template>
|
||
|
||
<script>
|
||
export default {
|
||
// eslint-disable-next-line vue/multi-word-component-names
|
||
name: 'Reviews',
|
||
data() {
|
||
return {
|
||
isAuthenticated: false, // В реальном приложении получать из store/auth
|
||
sortBy: 'newest',
|
||
activeRatingFilter: 'all',
|
||
currentPage: 1,
|
||
reviewsPerPage: 6,
|
||
submitting: false,
|
||
ratingFilters: [
|
||
{ value: 'all', label: 'Все оценки' },
|
||
{ value: '5', label: '⭐ 5 звезд' },
|
||
{ value: '4', label: '⭐ 4+ звезды' },
|
||
{ value: '3', label: '⭐ 3+ звезды' }
|
||
],
|
||
newReview: {
|
||
rating: 0,
|
||
text: '',
|
||
achievement: '',
|
||
distance: ''
|
||
},
|
||
reviews: [
|
||
{
|
||
id: 1,
|
||
author: 'Анна',
|
||
rating: 5,
|
||
text: 'Пришла в клуб с нулевым опытом бега. За год прошла путь от 0 до марафона! Тренер Загир - профессионал своего дела, всегда поддерживает и дает ценные советы. Атмосфера в клубе невероятная!',
|
||
date: '2025-01-15',
|
||
achievement: 'Первый марафон за 4:20:15',
|
||
distance: '42.2 км',
|
||
improvement: '+8 мин на 10км',
|
||
trainings: 45,
|
||
membership: '1 год',
|
||
verified: true
|
||
},
|
||
{
|
||
id: 2,
|
||
author: 'Михаил',
|
||
rating: 5,
|
||
text: 'Искал профессиональный подход к тренировкам и нашел его здесь. Индивидуальные программы, работа над техникой, регулярные соревнования. За 6 месяцев улучшил результат на полумарафоне на 25 минут!',
|
||
date: '2025-01-12',
|
||
achievement: 'Полумарафон 1:35:20',
|
||
distance: '21.1 км',
|
||
improvement: '+25 мин на 21км',
|
||
trainings: 32,
|
||
membership: '6 месяцев',
|
||
verified: true
|
||
},
|
||
{
|
||
id: 3,
|
||
author: 'Елена',
|
||
rating: 5,
|
||
text: 'Очень нравятся групповые тренировки - всегда есть с кем побегать и пообщаться. Тренер внимательно следит за техникой, что помогло избежать травм. За 3 месяца похудела на 8 кг и полюбила бег!',
|
||
date: '2025-01-10',
|
||
achievement: 'Похудение на 8 кг',
|
||
distance: '10 км',
|
||
improvement: '+12 мин на 5км',
|
||
trainings: 24,
|
||
membership: '3 месяца',
|
||
verified: true
|
||
},
|
||
{
|
||
id: 4,
|
||
author: 'Сергей',
|
||
rating: 5,
|
||
text: 'Как опытный бегун могу сказать - тренер Загир один из лучших в Уфе. Его методики действительно работают. Благодаря клубу пробежал ультрамарафон и нашел единомышленников.',
|
||
date: '2025-01-08',
|
||
achievement: 'Ультрамарафон 80 км',
|
||
distance: 'Трейл',
|
||
improvement: 'Новый уровень',
|
||
trainings: 68,
|
||
membership: '2 года',
|
||
verified: true
|
||
},
|
||
{
|
||
id: 5,
|
||
author: 'Ғаяз',
|
||
rating: 4,
|
||
text: 'Хороший клуб с сильным тренером. Немного не хватает вечерних тренировок в будни. В остальном - отличная атмосфера и профессиональный подход.',
|
||
date: '2025-01-05',
|
||
achievement: 'Марафон 3:34:33',
|
||
distance: '42.2 км',
|
||
improvement: '+15 мин на марафоне',
|
||
trainings: 52,
|
||
membership: '1.5 года',
|
||
verified: true
|
||
},
|
||
{
|
||
id: 6,
|
||
author: 'Данил',
|
||
rating: 5,
|
||
text: 'Лучшее решение - присоединиться к этому клубу! Нашел друзей, улучшил результаты, участвую в забегах. Отдельное спасибо за подготовку к трейлам - незабываемые впечатления!',
|
||
date: '2025-01-03',
|
||
achievement: 'Трейл 120 км',
|
||
distance: 'Трейл',
|
||
improvement: '+18 мин на 10км',
|
||
trainings: 41,
|
||
membership: '10 месяцев',
|
||
verified: true
|
||
},
|
||
{
|
||
id: 7,
|
||
author: 'Ильвира',
|
||
rating: 5,
|
||
text: 'Как маме двоих детей было сложно найти время для спорта. В клубе подобрали удобный график, поддержали морально. Теперь бегаю регулярно и чувствую себя прекрасно!',
|
||
date: '2024-12-28',
|
||
achievement: 'Первый полумарафон',
|
||
distance: '21.1 км',
|
||
improvement: 'С нуля до 21км',
|
||
trainings: 18,
|
||
membership: '5 месяцев',
|
||
verified: true
|
||
},
|
||
{
|
||
id: 8,
|
||
author: 'Булат',
|
||
rating: 4,
|
||
text: 'Нравится системный подход к тренировкам. Есть небольшие замечания по организации некоторых мероприятий, но в целом - хороший клуб с перспективой развития.',
|
||
date: '2024-12-25',
|
||
achievement: 'Полумарафон 1:45:48',
|
||
distance: '21.1 км',
|
||
improvement: '+10 мин на 10км',
|
||
trainings: 29,
|
||
membership: '8 месяцев',
|
||
verified: true
|
||
}
|
||
]
|
||
}
|
||
},
|
||
computed: {
|
||
totalReviews() {
|
||
return this.reviews.length
|
||
},
|
||
averageRating() {
|
||
if (this.reviews.length === 0) return 0
|
||
const sum = this.reviews.reduce((acc, review) => acc + review.rating, 0)
|
||
return (sum / this.reviews.length).toFixed(1)
|
||
},
|
||
successStories() {
|
||
return this.reviews.filter(review => review.achievement && review.rating >= 4).length
|
||
},
|
||
filteredReviews() {
|
||
let filtered = this.reviews
|
||
|
||
// Фильтрация по рейтингу
|
||
if (this.activeRatingFilter !== 'all') {
|
||
const minRating = parseInt(this.activeRatingFilter)
|
||
filtered = filtered.filter(review => review.rating >= minRating)
|
||
}
|
||
|
||
// Сортировка
|
||
switch (this.sortBy) {
|
||
case 'newest':
|
||
filtered.sort((a, b) => new Date(b.date) - new Date(a.date))
|
||
break
|
||
case 'oldest':
|
||
filtered.sort((a, b) => new Date(a.date) - new Date(b.date))
|
||
break
|
||
case 'highest':
|
||
filtered.sort((a, b) => b.rating - a.rating || new Date(b.date) - new Date(a.date))
|
||
break
|
||
case 'lowest':
|
||
filtered.sort((a, b) => a.rating - b.rating || new Date(b.date) - new Date(a.date))
|
||
break
|
||
}
|
||
|
||
return filtered
|
||
},
|
||
paginatedReviews() {
|
||
const startIndex = (this.currentPage - 1) * this.reviewsPerPage
|
||
return this.filteredReviews.slice(startIndex, startIndex + this.reviewsPerPage)
|
||
},
|
||
totalPages() {
|
||
return Math.ceil(this.filteredReviews.length / this.reviewsPerPage)
|
||
},
|
||
visiblePages() {
|
||
const pages = []
|
||
const startPage = Math.max(1, this.currentPage - 2)
|
||
const endPage = Math.min(this.totalPages, startPage + 4)
|
||
|
||
for (let i = startPage; i <= endPage; i++) {
|
||
pages.push(i)
|
||
}
|
||
return pages
|
||
},
|
||
isFormValid() {
|
||
return this.newReview.rating > 0 &&
|
||
this.newReview.text.length >= 10 &&
|
||
this.newReview.text.length <= 500
|
||
}
|
||
},
|
||
methods: {
|
||
getInitials(name) {
|
||
return name.split(' ').map(n => n[0]).join('').toUpperCase()
|
||
},
|
||
getReviewCardClass(rating) {
|
||
return `rating-${rating}`
|
||
},
|
||
formatDate(dateString) {
|
||
const date = new Date(dateString)
|
||
return date.toLocaleDateString('ru-RU', {
|
||
day: 'numeric',
|
||
month: 'long',
|
||
year: 'numeric'
|
||
})
|
||
},
|
||
sortReviews() {
|
||
this.currentPage = 1
|
||
},
|
||
filterByRating(rating) {
|
||
this.activeRatingFilter = rating
|
||
this.currentPage = 1
|
||
},
|
||
changePage(page) {
|
||
this.currentPage = page
|
||
window.scrollTo({ top: 0, behavior: 'smooth' })
|
||
},
|
||
getRatingCount(rating) {
|
||
return this.reviews.filter(review => review.rating === rating).length
|
||
},
|
||
getRatingPercentage(rating) {
|
||
const count = this.getRatingCount(rating)
|
||
return (count / this.totalReviews) * 100
|
||
},
|
||
async submitReview() {
|
||
if (!this.isFormValid) return
|
||
|
||
this.submitting = true
|
||
|
||
try {
|
||
// Имитация запроса к API
|
||
await new Promise(resolve => setTimeout(resolve, 1500))
|
||
|
||
const newReview = {
|
||
id: this.reviews.length + 1,
|
||
author: 'Вы', // В реальном приложении брать из профиля
|
||
rating: this.newReview.rating,
|
||
text: this.newReview.text,
|
||
date: new Date().toISOString().split('T')[0],
|
||
achievement: this.newReview.achievement,
|
||
distance: this.newReview.distance,
|
||
improvement: '',
|
||
trainings: 0,
|
||
membership: 'Новый участник',
|
||
verified: false
|
||
}
|
||
|
||
this.reviews.unshift(newReview)
|
||
this.resetForm()
|
||
|
||
// Показать уведомление об успехе
|
||
alert('Спасибо за ваш отзыв! После проверки модератором он будет опубликован.')
|
||
|
||
} catch (error) {
|
||
console.error('Ошибка при отправке отзыва:', error)
|
||
alert('Произошла ошибка при отправке отзыва. Попробуйте еще раз.')
|
||
} finally {
|
||
this.submitting = false
|
||
}
|
||
},
|
||
resetForm() {
|
||
this.newReview = {
|
||
rating: 0,
|
||
text: '',
|
||
achievement: '',
|
||
distance: ''
|
||
}
|
||
},
|
||
login() {
|
||
// В реальном приложении перенаправлять на страницу логина
|
||
this.isAuthenticated = true
|
||
}
|
||
},
|
||
mounted() {
|
||
// В реальном приложении проверять авторизацию
|
||
// this.checkAuth()
|
||
}
|
||
}
|
||
</script>
|
||
|
||
<style scoped>
|
||
.reviews-page {
|
||
min-height: 100vh;
|
||
background: linear-gradient(135deg, #f8fff8 0%, #f0f8f0 100%);
|
||
}
|
||
|
||
/* Герой-секция */
|
||
.hero-section {
|
||
background: linear-gradient(135deg, #2e8b57 0%, #26734a 100%);
|
||
color: white;
|
||
padding: 80px 0 60px;
|
||
text-align: center;
|
||
}
|
||
|
||
.hero-title {
|
||
font-size: 3rem;
|
||
margin-bottom: 1rem;
|
||
font-weight: 800;
|
||
}
|
||
|
||
.hero-subtitle {
|
||
font-size: 1.3rem;
|
||
opacity: 0.9;
|
||
margin-bottom: 2rem;
|
||
}
|
||
|
||
.hero-stats {
|
||
display: flex;
|
||
justify-content: center;
|
||
gap: 3rem;
|
||
flex-wrap: wrap;
|
||
}
|
||
|
||
.stat {
|
||
text-align: center;
|
||
}
|
||
|
||
.stat-number {
|
||
font-size: 2.5rem;
|
||
font-weight: 800;
|
||
color: #ffd700;
|
||
margin-bottom: 0.5rem;
|
||
}
|
||
|
||
.stat-label {
|
||
font-size: 0.9rem;
|
||
opacity: 0.8;
|
||
}
|
||
|
||
/* Основные стили */
|
||
.container {
|
||
max-width: 1200px;
|
||
margin: 0 auto;
|
||
padding: 0 20px;
|
||
}
|
||
|
||
.section-title {
|
||
font-size: 2.5rem;
|
||
color: #2e8b57;
|
||
margin-bottom: 1rem;
|
||
font-weight: 700;
|
||
}
|
||
|
||
/* Заголовок отзывов */
|
||
.reviews-header {
|
||
margin-bottom: 3rem;
|
||
}
|
||
|
||
.reviews-controls {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
flex-wrap: wrap;
|
||
gap: 2rem;
|
||
}
|
||
|
||
.sort-controls {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 1rem;
|
||
}
|
||
|
||
.sort-controls label {
|
||
font-weight: 600;
|
||
color: #333;
|
||
}
|
||
|
||
.sort-controls select {
|
||
padding: 8px 12px;
|
||
border: 2px solid #e9ecef;
|
||
border-radius: 8px;
|
||
background: white;
|
||
cursor: pointer;
|
||
}
|
||
|
||
.filter-controls {
|
||
display: flex;
|
||
gap: 0.5rem;
|
||
flex-wrap: wrap;
|
||
}
|
||
|
||
.filter-btn {
|
||
padding: 8px 16px;
|
||
border: 2px solid #e9ecef;
|
||
background: white;
|
||
border-radius: 20px;
|
||
cursor: pointer;
|
||
transition: all 0.3s ease;
|
||
font-size: 0.9rem;
|
||
}
|
||
|
||
.filter-btn:hover {
|
||
border-color: #2e8b57;
|
||
}
|
||
|
||
.filter-btn.active {
|
||
background: #2e8b57;
|
||
color: white;
|
||
border-color: #2e8b57;
|
||
}
|
||
|
||
/* Сетка отзывов */
|
||
.reviews-section {
|
||
padding: 4rem 0;
|
||
}
|
||
|
||
.reviews-grid {
|
||
display: grid;
|
||
grid-template-columns: repeat(auto-fit, minmax(400px, 1fr));
|
||
gap: 2rem;
|
||
margin-bottom: 3rem;
|
||
}
|
||
|
||
.review-card {
|
||
background: white;
|
||
border-radius: 15px;
|
||
padding: 2rem;
|
||
box-shadow: 0 5px 20px rgba(0, 0, 0, 0.08);
|
||
transition: all 0.3s ease;
|
||
border: 2px solid transparent;
|
||
}
|
||
|
||
.review-card:hover {
|
||
transform: translateY(-5px);
|
||
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.15);
|
||
}
|
||
|
||
/* Стили по рейтингу */
|
||
.review-card.rating-5 {
|
||
border-color: #ffd700;
|
||
background: linear-gradient(135deg, #fff9e6 0%, #fff3cc 100%);
|
||
}
|
||
|
||
.review-card.rating-4 {
|
||
border-color: #a8e6cf;
|
||
background: linear-gradient(135deg, #f0f8f0 0%, #e8f5e9 100%);
|
||
}
|
||
|
||
.review-card.rating-3 {
|
||
border-color: #ffd8b2;
|
||
background: linear-gradient(135deg, #fff3e0 0%, #ffe0b2 100%);
|
||
}
|
||
|
||
.review-header {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: flex-start;
|
||
margin-bottom: 1.5rem;
|
||
}
|
||
|
||
.reviewer-info {
|
||
display: flex;
|
||
gap: 1rem;
|
||
align-items: flex-start;
|
||
}
|
||
|
||
.reviewer-avatar {
|
||
width: 50px;
|
||
height: 50px;
|
||
background: #2e8b57;
|
||
color: white;
|
||
border-radius: 50%;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
font-weight: bold;
|
||
font-size: 1.2rem;
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
.review-author {
|
||
color: #2e8b57;
|
||
margin: 0 0 0.3rem 0;
|
||
font-size: 1.2rem;
|
||
}
|
||
|
||
.reviewer-achievement {
|
||
color: #e74c3c;
|
||
font-weight: 600;
|
||
font-size: 0.9rem;
|
||
margin: 0 0 0.3rem 0;
|
||
}
|
||
|
||
.review-membership {
|
||
color: #666;
|
||
font-size: 0.8rem;
|
||
margin: 0;
|
||
}
|
||
|
||
.review-rating {
|
||
text-align: right;
|
||
}
|
||
|
||
.stars {
|
||
display: flex;
|
||
gap: 2px;
|
||
margin-bottom: 0.5rem;
|
||
justify-content: flex-end;
|
||
}
|
||
|
||
.star {
|
||
color: #ddd;
|
||
font-size: 1.2rem;
|
||
}
|
||
|
||
.star.filled {
|
||
color: #ffd700;
|
||
}
|
||
|
||
.rating-value {
|
||
font-weight: bold;
|
||
color: #333;
|
||
font-size: 1.1rem;
|
||
}
|
||
|
||
.review-content {
|
||
space-y: 1rem;
|
||
}
|
||
|
||
.review-text {
|
||
line-height: 1.6;
|
||
color: #333;
|
||
margin-bottom: 1.5rem;
|
||
}
|
||
|
||
.review-meta {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
margin-bottom: 1rem;
|
||
}
|
||
|
||
.meta-items {
|
||
display: flex;
|
||
gap: 1rem;
|
||
flex-wrap: wrap;
|
||
}
|
||
|
||
.meta-item {
|
||
background: rgba(46, 139, 87, 0.1);
|
||
color: #2e8b57;
|
||
padding: 0.3rem 0.8rem;
|
||
border-radius: 15px;
|
||
font-size: 0.8rem;
|
||
font-weight: 600;
|
||
}
|
||
|
||
.review-date {
|
||
color: #666;
|
||
font-size: 0.9rem;
|
||
}
|
||
|
||
.review-actions {
|
||
border-top: 1px solid #e9ecef;
|
||
padding-top: 1rem;
|
||
}
|
||
|
||
.verified-badge {
|
||
color: #27ae60;
|
||
font-size: 0.8rem;
|
||
font-weight: 600;
|
||
}
|
||
|
||
/* Пагинация */
|
||
.pagination {
|
||
display: flex;
|
||
justify-content: center;
|
||
gap: 0.5rem;
|
||
margin: 3rem 0;
|
||
}
|
||
|
||
.pagination-btn {
|
||
padding: 10px 15px;
|
||
border: 2px solid #e9ecef;
|
||
background: white;
|
||
border-radius: 8px;
|
||
cursor: pointer;
|
||
transition: all 0.3s ease;
|
||
font-weight: 600;
|
||
}
|
||
|
||
.pagination-btn:hover:not(:disabled) {
|
||
border-color: #2e8b57;
|
||
background: #f8fff8;
|
||
}
|
||
|
||
.pagination-btn.active {
|
||
background: #2e8b57;
|
||
color: white;
|
||
border-color: #2e8b57;
|
||
}
|
||
|
||
.pagination-btn:disabled {
|
||
opacity: 0.5;
|
||
cursor: not-allowed;
|
||
}
|
||
|
||
/* Статистика оценок */
|
||
.reviews-stats {
|
||
background: white;
|
||
padding: 2rem;
|
||
border-radius: 15px;
|
||
box-shadow: 0 5px 20px rgba(0, 0, 0, 0.08);
|
||
margin-bottom: 3rem;
|
||
}
|
||
|
||
.reviews-stats h3 {
|
||
color: #2e8b57;
|
||
margin-bottom: 1.5rem;
|
||
text-align: center;
|
||
}
|
||
|
||
.rating-bars {
|
||
max-width: 400px;
|
||
margin: 0 auto;
|
||
}
|
||
|
||
.rating-bar {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 1rem;
|
||
margin-bottom: 0.8rem;
|
||
}
|
||
|
||
.rating-label {
|
||
width: 80px;
|
||
}
|
||
|
||
.stars-small {
|
||
display: flex;
|
||
gap: 1px;
|
||
}
|
||
|
||
.star-small {
|
||
color: #ddd;
|
||
font-size: 0.8rem;
|
||
}
|
||
|
||
.star-small.filled {
|
||
color: #ffd700;
|
||
}
|
||
|
||
.bar-container {
|
||
flex: 1;
|
||
height: 8px;
|
||
background: #e9ecef;
|
||
border-radius: 4px;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.bar-fill {
|
||
height: 100%;
|
||
background: #2e8b57;
|
||
border-radius: 4px;
|
||
transition: width 0.5s ease;
|
||
}
|
||
|
||
.rating-count {
|
||
width: 30px;
|
||
text-align: right;
|
||
font-weight: 600;
|
||
color: #333;
|
||
}
|
||
|
||
/* Форма отзыва */
|
||
.add-review-section {
|
||
background: white;
|
||
padding: 4rem 0;
|
||
border-top: 1px solid #e9ecef;
|
||
}
|
||
|
||
.add-review-content {
|
||
max-width: 600px;
|
||
margin: 0 auto;
|
||
}
|
||
|
||
.form-header {
|
||
text-align: center;
|
||
margin-bottom: 3rem;
|
||
}
|
||
|
||
.form-header p {
|
||
color: #666;
|
||
font-size: 1.1rem;
|
||
}
|
||
|
||
.review-form {
|
||
background: #f8fff8;
|
||
padding: 2rem;
|
||
border-radius: 15px;
|
||
border: 2px solid #e9ecef;
|
||
}
|
||
|
||
.auth-required {
|
||
text-align: center;
|
||
padding: 2rem;
|
||
}
|
||
|
||
.auth-message {
|
||
margin-bottom: 2rem;
|
||
}
|
||
|
||
.auth-message h3 {
|
||
color: #2e8b57;
|
||
margin-bottom: 1rem;
|
||
}
|
||
|
||
.auth-actions {
|
||
display: flex;
|
||
gap: 1rem;
|
||
justify-content: center;
|
||
flex-wrap: wrap;
|
||
}
|
||
|
||
.form-group {
|
||
margin-bottom: 1.5rem;
|
||
}
|
||
|
||
.form-group label {
|
||
display: block;
|
||
margin-bottom: 0.5rem;
|
||
font-weight: 600;
|
||
color: #333;
|
||
}
|
||
|
||
.rating-input {
|
||
display: flex;
|
||
gap: 0.5rem;
|
||
}
|
||
|
||
.star-btn {
|
||
background: none;
|
||
border: none;
|
||
font-size: 2rem;
|
||
color: #ddd;
|
||
cursor: pointer;
|
||
transition: all 0.2s ease;
|
||
padding: 0.2rem;
|
||
}
|
||
|
||
.star-btn.active {
|
||
color: #ffd700;
|
||
transform: scale(1.1);
|
||
}
|
||
|
||
.star-btn:hover {
|
||
transform: scale(1.2);
|
||
}
|
||
|
||
.form-row {
|
||
display: grid;
|
||
grid-template-columns: 1fr 1fr;
|
||
gap: 1rem;
|
||
}
|
||
|
||
.form-group input,
|
||
.form-group select,
|
||
.form-group textarea {
|
||
width: 100%;
|
||
padding: 12px 15px;
|
||
border: 2px solid #e9ecef;
|
||
border-radius: 8px;
|
||
font-size: 1rem;
|
||
transition: border-color 0.3s ease;
|
||
font-family: inherit;
|
||
}
|
||
|
||
.form-group input:focus,
|
||
.form-group select:focus,
|
||
.form-group textarea:focus {
|
||
outline: none;
|
||
border-color: #2e8b57;
|
||
}
|
||
|
||
.char-counter {
|
||
text-align: right;
|
||
font-size: 0.8rem;
|
||
color: #666;
|
||
margin-top: 0.5rem;
|
||
}
|
||
|
||
.form-actions {
|
||
display: flex;
|
||
gap: 1rem;
|
||
justify-content: center;
|
||
flex-wrap: wrap;
|
||
}
|
||
|
||
/* CTA секция */
|
||
.cta-section {
|
||
background: linear-gradient(135deg, #2e8b57 0%, #26734a 100%);
|
||
color: white;
|
||
padding: 80px 0;
|
||
text-align: center;
|
||
}
|
||
|
||
.cta-content h2 {
|
||
font-size: 2.5rem;
|
||
margin-bottom: 1rem;
|
||
}
|
||
|
||
.cta-content p {
|
||
font-size: 1.2rem;
|
||
opacity: 0.9;
|
||
margin-bottom: 3rem;
|
||
}
|
||
|
||
.success-stories-preview {
|
||
display: grid;
|
||
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
||
gap: 1.5rem;
|
||
margin-bottom: 3rem;
|
||
max-width: 600px;
|
||
margin-left: auto;
|
||
margin-right: auto;
|
||
}
|
||
|
||
.story-card {
|
||
background: rgba(255, 255, 255, 0.1);
|
||
padding: 1.5rem;
|
||
border-radius: 10px;
|
||
backdrop-filter: blur(10px);
|
||
display: flex;
|
||
gap: 1rem;
|
||
align-items: flex-start;
|
||
text-align: left;
|
||
}
|
||
|
||
.story-avatar {
|
||
width: 40px;
|
||
height: 40px;
|
||
background: #ffd700;
|
||
color: #333;
|
||
border-radius: 50%;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
font-weight: bold;
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
.story-content h4 {
|
||
margin: 0 0 0.5rem 0;
|
||
color: white;
|
||
}
|
||
|
||
.story-content p {
|
||
font-size: 0.9rem;
|
||
opacity: 0.9;
|
||
margin: 0 0 0.5rem 0;
|
||
line-height: 1.4;
|
||
}
|
||
|
||
.story-achievement {
|
||
color: #ffd700;
|
||
font-size: 0.8rem;
|
||
font-weight: 600;
|
||
}
|
||
|
||
.cta-features {
|
||
display: grid;
|
||
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
||
gap: 2rem;
|
||
margin-bottom: 3rem;
|
||
max-width: 800px;
|
||
margin-left: auto;
|
||
margin-right: auto;
|
||
}
|
||
|
||
.cta-feature {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 1rem;
|
||
text-align: left;
|
||
}
|
||
|
||
.feature-icon {
|
||
font-size: 2.5rem;
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
.feature-text {
|
||
display: flex;
|
||
flex-direction: column;
|
||
}
|
||
|
||
.feature-text strong {
|
||
font-size: 1.1rem;
|
||
margin-bottom: 0.3rem;
|
||
}
|
||
|
||
.feature-text span {
|
||
opacity: 0.8;
|
||
font-size: 0.9rem;
|
||
}
|
||
|
||
.cta-actions {
|
||
display: flex;
|
||
gap: 1rem;
|
||
justify-content: center;
|
||
flex-wrap: wrap;
|
||
margin-bottom: 2rem;
|
||
}
|
||
|
||
.cta-contacts p {
|
||
margin-bottom: 1rem;
|
||
opacity: 0.8;
|
||
}
|
||
|
||
.contact-links {
|
||
display: flex;
|
||
gap: 1.5rem;
|
||
justify-content: center;
|
||
flex-wrap: wrap;
|
||
}
|
||
|
||
.contact-link {
|
||
color: #ffd700;
|
||
text-decoration: none;
|
||
font-weight: 600;
|
||
transition: opacity 0.3s ease;
|
||
}
|
||
|
||
.contact-link:hover {
|
||
opacity: 0.8;
|
||
}
|
||
|
||
/* Кнопки */
|
||
.btn {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
padding: 12px 24px;
|
||
border-radius: 50px;
|
||
text-decoration: none;
|
||
font-weight: 600;
|
||
transition: all 0.3s ease;
|
||
border: 2px solid transparent;
|
||
gap: 0.5rem;
|
||
font-size: 1rem;
|
||
cursor: pointer;
|
||
}
|
||
|
||
.btn-primary {
|
||
background: #ffd700;
|
||
color: #333;
|
||
}
|
||
|
||
.btn-primary:hover {
|
||
background: #e6c200;
|
||
transform: translateY(-2px);
|
||
}
|
||
|
||
.btn-secondary {
|
||
background: transparent;
|
||
color: white;
|
||
border-color: white;
|
||
}
|
||
|
||
.btn-secondary:hover {
|
||
background: white;
|
||
color: #2e8b57;
|
||
transform: translateY(-2px);
|
||
}
|
||
|
||
.btn-outline {
|
||
background: transparent;
|
||
color: #2e8b57;
|
||
border-color: #2e8b57;
|
||
}
|
||
|
||
.btn-outline:hover {
|
||
background: #2e8b57;
|
||
color: white;
|
||
transform: translateY(-2px);
|
||
}
|
||
|
||
.btn-large {
|
||
padding: 15px 30px;
|
||
font-size: 1.1rem;
|
||
}
|
||
|
||
.btn:disabled {
|
||
opacity: 0.6;
|
||
cursor: not-allowed;
|
||
transform: none !important;
|
||
}
|
||
|
||
/* Адаптивность */
|
||
@media (max-width: 768px) {
|
||
.hero-title {
|
||
font-size: 2.2rem;
|
||
}
|
||
|
||
.hero-stats {
|
||
gap: 2rem;
|
||
}
|
||
|
||
.stat-number {
|
||
font-size: 2rem;
|
||
}
|
||
|
||
.reviews-controls {
|
||
flex-direction: column;
|
||
align-items: stretch;
|
||
}
|
||
|
||
.sort-controls {
|
||
justify-content: space-between;
|
||
}
|
||
|
||
.filter-controls {
|
||
justify-content: center;
|
||
}
|
||
|
||
.reviews-grid {
|
||
grid-template-columns: 1fr;
|
||
}
|
||
|
||
.form-row {
|
||
grid-template-columns: 1fr;
|
||
}
|
||
|
||
.review-header {
|
||
flex-direction: column;
|
||
align-items: flex-start;
|
||
gap: 1rem;
|
||
}
|
||
|
||
.review-rating {
|
||
align-self: flex-end;
|
||
}
|
||
|
||
.success-stories-preview {
|
||
grid-template-columns: 1fr;
|
||
}
|
||
|
||
.cta-features {
|
||
grid-template-columns: 1fr;
|
||
}
|
||
|
||
.cta-actions {
|
||
flex-direction: column;
|
||
align-items: center;
|
||
}
|
||
|
||
.btn {
|
||
width: 100%;
|
||
max-width: 300px;
|
||
}
|
||
|
||
.auth-actions {
|
||
flex-direction: column;
|
||
}
|
||
}
|
||
|
||
@media (max-width: 480px) {
|
||
.hero-section {
|
||
padding: 60px 0 40px;
|
||
}
|
||
|
||
.hero-title {
|
||
font-size: 1.8rem;
|
||
}
|
||
|
||
.section-title {
|
||
font-size: 2rem;
|
||
}
|
||
|
||
.container {
|
||
padding: 0 15px;
|
||
}
|
||
|
||
.review-card {
|
||
padding: 1.5rem;
|
||
}
|
||
|
||
.reviewer-info {
|
||
flex-direction: column;
|
||
text-align: center;
|
||
}
|
||
|
||
.reviewer-avatar {
|
||
align-self: center;
|
||
}
|
||
|
||
.review-meta {
|
||
flex-direction: column;
|
||
gap: 1rem;
|
||
align-items: flex-start;
|
||
}
|
||
|
||
.meta-items {
|
||
flex-direction: column;
|
||
gap: 0.5rem;
|
||
}
|
||
}
|
||
|
||
/* Анимации */
|
||
@keyframes fadeInUp {
|
||
from {
|
||
opacity: 0;
|
||
transform: translateY(30px);
|
||
}
|
||
to {
|
||
opacity: 1;
|
||
transform: translateY(0);
|
||
}
|
||
}
|
||
|
||
.review-card {
|
||
animation: fadeInUp 0.6s ease;
|
||
}
|
||
|
||
@keyframes pulse {
|
||
0% {
|
||
transform: scale(1);
|
||
}
|
||
50% {
|
||
transform: scale(1.05);
|
||
}
|
||
100% {
|
||
transform: scale(1);
|
||
}
|
||
}
|
||
|
||
.star-btn:hover {
|
||
animation: pulse 0.3s ease;
|
||
}
|
||
|
||
/* Задержки для анимаций карточек */
|
||
.review-card:nth-child(odd) {
|
||
animation-delay: 0.1s;
|
||
}
|
||
|
||
.review-card:nth-child(even) {
|
||
animation-delay: 0.2s;
|
||
}
|
||
|
||
/* Состояния загрузки */
|
||
.loading {
|
||
opacity: 0.6;
|
||
pointer-events: none;
|
||
}
|
||
|
||
/* Улучшения доступности */
|
||
@media (prefers-reduced-motion: reduce) {
|
||
* {
|
||
animation-duration: 0.01ms !important;
|
||
animation-iteration-count: 1 !important;
|
||
transition-duration: 0.01ms !important;
|
||
}
|
||
}
|
||
|
||
/* Фокус-стили */
|
||
button:focus-visible,
|
||
a:focus-visible,
|
||
select:focus-visible,
|
||
input:focus-visible,
|
||
textarea:focus-visible {
|
||
outline: 2px solid #2e8b57;
|
||
outline-offset: 2px;
|
||
}
|
||
|
||
/* Темная тема */
|
||
@media (prefers-color-scheme: dark) {
|
||
.reviews-page {
|
||
background: linear-gradient(135deg, #1a1a1a 0%, #2d2d2d 100%);
|
||
}
|
||
|
||
.review-card {
|
||
background: #2d2d2d;
|
||
color: #ffffff;
|
||
}
|
||
|
||
.review-text {
|
||
color: #cccccc;
|
||
}
|
||
|
||
.form-group input,
|
||
.form-group select,
|
||
.form-group textarea {
|
||
background: #3d3d3d;
|
||
border-color: #555;
|
||
color: white;
|
||
}
|
||
|
||
.review-form {
|
||
background: #2d2d2d;
|
||
border-color: #555;
|
||
}
|
||
}
|
||
|
||
/* Кастомный скроллбар */
|
||
.reviews-section::-webkit-scrollbar {
|
||
width: 8px;
|
||
}
|
||
|
||
.reviews-section::-webkit-scrollbar-track {
|
||
background: #f1f1f1;
|
||
}
|
||
|
||
.reviews-section::-webkit-scrollbar-thumb {
|
||
background: #2e8b57;
|
||
border-radius: 4px;
|
||
}
|
||
|
||
.reviews-section::-webkit-scrollbar-thumb:hover {
|
||
background: #26734a;
|
||
}
|
||
|
||
/* Утилитарные классы */
|
||
.text-center {
|
||
text-align: center;
|
||
}
|
||
|
||
.mb-2 {
|
||
margin-bottom: 2rem;
|
||
}
|
||
|
||
.mt-2 {
|
||
margin-top: 2rem;
|
||
}
|
||
|
||
/* Состояния валидации формы */
|
||
.form-group.error input,
|
||
.form-group.error textarea,
|
||
.form-group.error select {
|
||
border-color: #e74c3c;
|
||
}
|
||
|
||
.error-message {
|
||
color: #e74c3c;
|
||
font-size: 0.8rem;
|
||
margin-top: 0.5rem;
|
||
}
|
||
|
||
/* Уведомления */
|
||
.notification {
|
||
position: fixed;
|
||
top: 20px;
|
||
right: 20px;
|
||
background: #27ae60;
|
||
color: white;
|
||
padding: 1rem 1.5rem;
|
||
border-radius: 8px;
|
||
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.2);
|
||
z-index: 1000;
|
||
animation: slideInRight 0.3s ease;
|
||
}
|
||
|
||
@keyframes slideInRight {
|
||
from {
|
||
transform: translateX(100%);
|
||
opacity: 0;
|
||
}
|
||
to {
|
||
transform: translateX(0);
|
||
opacity: 1;
|
||
}
|
||
}
|
||
|
||
/* Адаптация для печати */
|
||
@media print {
|
||
.hero-section,
|
||
.add-review-section,
|
||
.cta-section,
|
||
.review-actions,
|
||
.pagination {
|
||
display: none;
|
||
}
|
||
|
||
.review-card {
|
||
break-inside: avoid;
|
||
box-shadow: none;
|
||
border: 1px solid #ddd;
|
||
}
|
||
}
|
||
</style> |