Files
tp/main_dc/yalarba/easySite/app/pages/news/index.vue
T
valitovgaziz 2941b14b38 flatten easySite directory: remove extra easySite/easySite nesting
- Moved contents of main_dc/yalarba/easySite/easySite/ up to easySite/
- Updated docker-compose.yml build context path
- Deleted empty nested easySite/ directory
2026-06-12 11:16:15 +05:00

1142 lines
29 KiB
Vue
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<div class="page-wrapper">
<!-- Хедер -->
<Header />
<!-- Основной контент -->
<main class="news-page">
<!-- Hero секция -->
<section class="news-hero">
<div class="container">
<div class="hero-content">
<h1 class="hero-title">Новости</h1>
<p class="hero-subtitle">
Будьте в курсе последних событий, обновлений и важных объявлений компании
</p>
<div class="news-stats">
<div class="stat-item">
<span class="stat-number">{{ totalNews }}</span>
<span class="stat-label">новостей</span>
</div>
<div class="stat-item">
<span class="stat-number">{{ currentYear }}</span>
<span class="stat-label">год</span>
</div>
</div>
</div>
</div>
</section>
<!-- Фильтры и поиск -->
<section class="news-filters">
<div class="container">
<div class="filters-content">
<div class="search-box">
<input
v-model="searchQuery"
type="text"
class="search-input"
placeholder="Поиск по новостям..."
@input="handleSearch"
>
<span class="search-icon">🔍</span>
</div>
<div class="filter-controls">
<div class="filter-group">
<label class="filter-label">Категория:</label>
<select v-model="selectedCategory" @change="filterNews" class="filter-select">
<option value="all">Все категории</option>
<option value="company">Компания</option>
<option value="updates">Обновления</option>
<option value="events">События</option>
<option value="partnership">Партнерство</option>
<option value="technology">Технологии</option>
</select>
</div>
<div class="filter-group">
<label class="filter-label">Год:</label>
<select v-model="selectedYear" @change="filterNews" class="filter-select">
<option value="all">Все годы</option>
<option v-for="year in availableYears" :key="year" :value="year">
{{ year }}
</option>
</select>
</div>
<div class="view-toggle">
<button
@click="viewMode = 'grid'"
:class="['view-button', { 'active': viewMode === 'grid' }]"
aria-label="Сетка"
>
</button>
<button
@click="viewMode = 'list'"
:class="['view-button', { 'active': viewMode === 'list' }]"
aria-label="Список"
>
</button>
</div>
</div>
</div>
</div>
</section>
<!-- Основной контент новостей -->
<section class="news-content">
<div class="container">
<!-- Заголовок с результатами -->
<div class="results-header">
<h2 class="results-title">
{{ filteredNews.length }} {{ getNewsCountText(filteredNews.length) }}
<span v-if="selectedCategory !== 'all'" class="filter-indicator">
в категории "{{ getCategoryName(selectedCategory) }}"
</span>
<span v-if="selectedYear !== 'all'" class="filter-indicator">
за {{ selectedYear }} год
</span>
</h2>
</div>
<!-- Список новостей -->
<div v-if="filteredNews.length > 0" :class="['news-grid', { 'list-view': viewMode === 'list' }]">
<article
v-for="newsItem in paginatedNews"
:key="newsItem.id"
:class="['news-card', { 'featured': newsItem.featured }]"
>
<div class="news-image">
<div class="image-placeholder">
{{ getCategoryIcon(newsItem.category) }}
</div>
<div class="news-badges">
<span class="news-category">{{ getCategoryName(newsItem.category) }}</span>
<span v-if="newsItem.featured" class="news-featured">Важно</span>
</div>
</div>
<div class="news-body">
<div class="news-meta">
<span class="news-date">{{ formatDate(newsItem.date) }}</span>
<span class="news-read-time"> {{ newsItem.readTime }}</span>
</div>
<h3 class="news-title">
<NuxtLink :to="`/news/${newsItem.id}`" class="news-link">
{{ newsItem.title }}
</NuxtLink>
</h3>
<p class="news-excerpt">{{ newsItem.excerpt }}</p>
<div class="news-footer">
<div class="news-tags">
<span
v-for="tag in (newsItem.tags || []).slice(0, 2)"
:key="tag"
class="news-tag"
>
#{{ tag }}
</span>
<span v-if="(newsItem.tags || []).length > 2" class="news-tag-more">
+{{ (newsItem.tags || []).length - 2 }}
</span>
</div>
<NuxtLink :to="`/news/${newsItem.id}`" class="read-more">
Читать далее
</NuxtLink>
</div>
</div>
</article>
</div>
<!-- Пагинация -->
<div v-if="totalPages > 1 && filteredNews.length > 0" class="pagination">
<button
@click="prevPage"
:disabled="currentPage === 1"
class="pagination-button prev"
>
Назад
</button>
<div class="pagination-pages">
<button
v-for="page in visiblePages"
:key="page"
@click="goToPage(page)"
:class="['pagination-page', { 'active': page === currentPage }]"
:disabled="page === '...'"
>
{{ page }}
</button>
</div>
<button
@click="nextPage"
:disabled="currentPage === totalPages"
class="pagination-button next"
>
Вперед
</button>
</div>
<!-- Сообщение, если новостей нет -->
<div v-if="filteredNews.length === 0" class="no-news">
<div class="no-news-icon">📰</div>
<h3 class="no-news-title">Новости не найдены</h3>
<p class="no-news-description">
Попробуйте изменить параметры поиска или выбрать другую категорию
</p>
<button @click="resetFilters" class="btn btn-primary">
Сбросить фильтры
</button>
</div>
</div>
</section>
<!-- Подписка на новости -->
<section class="newsletter-section">
<div class="container">
<div class="newsletter-card">
<div class="newsletter-content">
<h2 class="newsletter-title">Будьте в курсе новостей</h2>
<p class="newsletter-description">
Подпишитесь на рассылку и получайте самые важные новости первыми
</p>
<form @submit.prevent="subscribeNewsletter" class="newsletter-form">
<div class="form-group">
<input
v-model="email"
type="email"
class="newsletter-input"
placeholder="your@email.com"
required
>
<button type="submit" class="btn btn-primary newsletter-button">
Подписаться
</button>
</div>
<p class="newsletter-note">
Подписываясь, вы соглашаетесь с обработкой персональных данных
</p>
</form>
</div>
<div class="newsletter-icon">📧</div>
</div>
</div>
</section>
</main>
<!-- Футер -->
<Footer />
</div>
</template>
<script setup lang="ts">
// Импорты в начале файла
const Header = defineAsyncComponent(() => import('~/components/layout/Header.vue'))
const Footer = defineAsyncComponent(() => import('~/components/layout/Footer.vue'))
definePageMeta({
title: 'Новости - ООО "ИКЦ ЯЛ АРБА"'
})
// Состояние фильтров и поиска
const searchQuery = ref('')
const selectedCategory = ref('all')
const selectedYear = ref('all')
const viewMode = ref<'grid' | 'list'>('grid')
const email = ref('')
// Пагинация
const currentPage = ref(1)
const itemsPerPage = 6
// Моковые данные новостей с гарантированной структурой
const news = ref([
{
id: 1,
title: 'Запуск новой платформы для создания сайтов',
excerpt: 'Представляем революционную платформу, которая позволяет создавать профессиональные сайты за считанные минуты без навыков программирования.',
content: 'Полный текст новости...',
category: 'updates',
date: '2025-12-15',
readTime: '3 мин',
tags: ['платформа', 'запуск', 'технологии'],
featured: true
},
{
id: 2,
title: 'Новое партнерство с ведущими IT-компаниями',
excerpt: 'Мы объявляем о стратегическом партнерстве с крупными игроками рынка для расширения возможностей нашей платформы.',
content: 'Полный текст новости...',
category: 'partnership',
date: '2023-12-10',
readTime: '4 мин',
tags: ['партнерство', 'развитие', 'сотрудничество'],
featured: false
},
{
id: 3,
title: 'Обновление интерфейса пользователя',
excerpt: 'Значительно улучшен пользовательский интерфейс платформы для большего удобства и эффективности работы.',
content: 'Полный текст новости...',
category: 'updates',
date: '2023-12-05',
readTime: '2 мин',
tags: ['интерфейс', 'обновление', 'UX'],
featured: false
},
{
id: 4,
title: 'Проведение вебинара по цифровой трансформации',
excerpt: 'Приглашаем на бесплатный вебинар, посвященный современным тенденциям цифровой трансформации бизнеса.',
content: 'Полный текст новости...',
category: 'events',
date: '2023-11-28',
readTime: '1 мин',
tags: ['вебинар', 'образование', 'бизнес'],
featured: true
},
{
id: 5,
title: 'Расширение команды разработки',
excerpt: 'Наша команда пополнилась новыми талантливыми разработчиками для ускорения развития платформы.',
content: 'Полный текст новости...',
category: 'company',
date: '2023-11-20',
readTime: '2 мин',
tags: ['команда', 'разработка', 'кадры'],
featured: false
},
{
id: 6,
title: 'Внедрение искусственного интеллекта',
excerpt: 'Интегрировали AI-технологии для автоматизации процессов и предоставления умных рекомендаций пользователям.',
content: 'Полный текст новости...',
category: 'technology',
date: '2023-11-15',
readTime: '5 мин',
tags: ['AI', 'технологии', 'инновации'],
featured: false
}
])
// Вычисляемые свойства
const totalNews = computed(() => news.value?.length || 0)
const currentYear = computed(() => new Date().getFullYear())
const availableYears = computed(() => {
if (!news.value) return []
const years = new Set(news.value.map(item => new Date(item.date).getFullYear()))
return Array.from(years).sort((a, b) => b - a)
})
const filteredNews = computed(() => {
if (!news.value) return []
let filtered = [...news.value]
// Фильтрация по поисковому запросу
if (searchQuery.value) {
const query = searchQuery.value.toLowerCase()
filtered = filtered.filter(item =>
item.title?.toLowerCase().includes(query) ||
item.excerpt?.toLowerCase().includes(query) ||
item.tags?.some(tag => tag.toLowerCase().includes(query))
)
}
// Фильтрация по категории
if (selectedCategory.value !== 'all') {
filtered = filtered.filter(item => item.category === selectedCategory.value)
}
// Фильтрация по году
if (selectedYear.value !== 'all') {
filtered = filtered.filter(item =>
new Date(item.date).getFullYear().toString() === selectedYear.value
)
}
// Сортировка по дате (новые сначала)
return filtered.sort((a, b) => new Date(b.date).getTime() - new Date(a.date).getTime())
})
const totalPages = computed(() => Math.ceil((filteredNews.value?.length || 0) / itemsPerPage))
const paginatedNews = computed(() => {
const start = (currentPage.value - 1) * itemsPerPage
const end = start + itemsPerPage
return filteredNews.value?.slice(start, end) || []
})
const visiblePages = computed(() => {
const total = totalPages.value
const current = currentPage.value
const pages = []
if (total <= 7) {
for (let i = 1; i <= total; i++) pages.push(i)
} else {
if (current <= 4) {
for (let i = 1; i <= 5; i++) pages.push(i)
pages.push('...')
pages.push(total)
} else if (current >= total - 3) {
pages.push(1)
pages.push('...')
for (let i = total - 4; i <= total; i++) pages.push(i)
} else {
pages.push(1)
pages.push('...')
for (let i = current - 1; i <= current + 1; i++) pages.push(i)
pages.push('...')
pages.push(total)
}
}
return pages
})
// Методы
const handleSearch = () => {
currentPage.value = 1
}
const filterNews = () => {
currentPage.value = 1
}
const resetFilters = () => {
searchQuery.value = ''
selectedCategory.value = 'all'
selectedYear.value = 'all'
currentPage.value = 1
}
const prevPage = () => {
if (currentPage.value > 1) currentPage.value--
}
const nextPage = () => {
if (currentPage.value < totalPages.value) currentPage.value++
}
const goToPage = (page: number | string) => {
if (typeof page === 'number') {
currentPage.value = page
}
}
const formatDate = (dateString: string) => {
try {
const date = new Date(dateString)
return date.toLocaleDateString('ru-RU', {
day: 'numeric',
month: 'long',
year: 'numeric'
})
} catch {
return dateString
}
}
const getCategoryName = (category: string) => {
const categories: { [key: string]: string } = {
company: 'Компания',
updates: 'Обновления',
events: 'События',
partnership: 'Партнерство',
technology: 'Технологии'
}
return categories[category] || category
}
const getCategoryIcon = (category: string) => {
const icons: { [key: string]: string } = {
company: '🏢',
updates: '🔄',
events: '📅',
partnership: '🤝',
technology: '💻'
}
return icons[category] || '📰'
}
const getNewsCountText = (count: number) => {
if (count % 10 === 1 && count % 100 !== 11) return 'новость'
if (count % 10 >= 2 && count % 10 <= 4 && (count % 100 < 10 || count % 100 >= 20)) return 'новости'
return 'новостей'
}
const subscribeNewsletter = async () => {
if (!email.value) return
try {
// Временно убираем Telegram для устранения ошибок
console.log('Подписка на рассылку:', email.value)
alert('✅ Вы успешно подписались на рассылку!')
email.value = ''
} catch (error) {
console.error('Ошибка подписки:', error)
alert('❌ Произошла ошибка при подписке. Попробуйте позже.')
}
}
// Обработка ошибок
onMounted(() => {
console.log('News page mounted')
})
onErrorCaptured((error) => {
console.error('Error captured in news page:', error)
return false
})
</script>
<style scoped>
.page-wrapper {
min-height: 100vh;
display: flex;
flex-direction: column;
}
.news-page {
flex: 1;
}
/* Hero секция */
.news-hero {
background: var(--hero-gradient);
color: white;
position: relative;
overflow: hidden;
padding: 4rem 0;
text-align: center;
}
.news-hero::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: var(--hero-pattern);
opacity: 0.1;
}
.hero-content {
position: relative;
z-index: 10;
max-width: 800px;
margin: 0 auto;
}
.hero-title {
font-family: var(--font-heading);
font-size: var(--text-4xl);
font-weight: var(--font-bold);
line-height: var(--leading-tight);
margin-bottom: var(--space-lg);
color: white;
}
.hero-subtitle {
font-family: var(--font-accent);
font-size: var(--text-xl);
font-weight: var(--font-light);
line-height: var(--leading-relaxed);
opacity: 0.9;
color: white;
margin-bottom: var(--space-2xl);
}
.news-stats {
display: flex;
justify-content: center;
gap: var(--space-2xl);
flex-wrap: wrap;
}
.stat-item {
display: flex;
flex-direction: column;
align-items: center;
}
.stat-number {
font-size: var(--text-3xl);
font-weight: var(--font-bold);
color: white;
}
.stat-label {
font-size: var(--text-sm);
color: rgba(255, 255, 255, 0.8);
text-transform: uppercase;
letter-spacing: var(--tracking-wide);
}
/* Фильтры */
.news-filters {
background: var(--bg-secondary);
padding: var(--space-xl) 0;
border-bottom: 1px solid var(--border-light);
}
.filters-content {
display: flex;
justify-content: space-between;
align-items: center;
gap: var(--space-xl);
flex-wrap: wrap;
}
.search-box {
position: relative;
flex: 1;
max-width: 400px;
}
.search-input {
width: 100%;
padding: var(--space-sm) var(--space-xl) var(--space-sm) var(--space-md);
border: 1px solid var(--border-medium);
border-radius: var(--radius-lg);
background: var(--bg-primary);
color: var(--text-primary);
font-size: var(--text-base);
transition: all 0.3s ease;
}
.search-input:focus {
outline: none;
border-color: var(--primary-500);
box-shadow: 0 0 0 3px rgb(14 165 233 / 0.1);
}
.search-icon {
position: absolute;
right: var(--space-md);
top: 50%;
transform: translateY(-50%);
color: var(--text-tertiary);
}
.filter-controls {
display: flex;
align-items: center;
gap: var(--space-lg);
flex-wrap: wrap;
}
.filter-group {
display: flex;
align-items: center;
gap: var(--space-sm);
}
.filter-label {
font-size: var(--text-sm);
color: var(--text-secondary);
font-weight: var(--font-medium);
white-space: nowrap;
}
.filter-select {
padding: var(--space-xs) var(--space-sm);
border: 1px solid var(--border-medium);
border-radius: var(--radius-md);
background: var(--bg-primary);
color: var(--text-primary);
font-size: var(--text-sm);
cursor: pointer;
}
.view-toggle {
display: flex;
border: 1px solid var(--border-medium);
border-radius: var(--radius-md);
overflow: hidden;
}
.view-button {
padding: var(--space-xs) var(--space-sm);
background: var(--bg-primary);
border: none;
cursor: pointer;
transition: all 0.2s ease;
font-size: var(--text-lg);
}
.view-button:hover {
background: var(--bg-secondary);
}
.view-button.active {
background: var(--primary-500);
color: white;
}
/* Основной контент */
.news-content {
padding: var(--space-2xl) 0;
}
.results-header {
margin-bottom: var(--space-2xl);
}
.results-title {
font-family: var(--font-heading);
font-size: var(--text-2xl);
font-weight: var(--font-semibold);
color: var(--text-primary);
}
.filter-indicator {
color: var(--text-secondary);
font-weight: var(--font-normal);
}
/* Сетка новостей */
.news-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(350px, 1fr));
gap: var(--space-xl);
margin-bottom: var(--space-2xl);
}
.news-grid.list-view {
grid-template-columns: 1fr;
}
.news-grid.list-view .news-card {
display: grid;
grid-template-columns: 200px 1fr;
gap: var(--space-lg);
}
.news-card {
background: var(--bg-primary);
border-radius: var(--radius-xl);
overflow: hidden;
border: 1px solid var(--border-light);
transition: all 0.3s ease;
position: relative;
}
.news-card:hover {
transform: translateY(-4px);
box-shadow: var(--shadow-lg);
}
.news-card.featured {
border-color: var(--primary-500);
box-shadow: 0 0 0 1px var(--primary-500);
}
.news-image {
position: relative;
height: 200px;
overflow: hidden;
background: var(--bg-secondary);
display: flex;
align-items: center;
justify-content: center;
}
.image-placeholder {
font-size: 3rem;
opacity: 0.5;
}
.news-badges {
position: absolute;
top: var(--space-md);
left: var(--space-md);
display: flex;
gap: var(--space-xs);
}
.news-category {
background: var(--primary-500);
color: white;
padding: 0.25rem 0.5rem;
border-radius: var(--radius-sm);
font-size: var(--text-xs);
font-weight: var(--font-semibold);
text-transform: uppercase;
letter-spacing: var(--tracking-wide);
}
.news-featured {
background: var(--error-500);
color: white;
padding: 0.25rem 0.5rem;
border-radius: var(--radius-sm);
font-size: var(--text-xs);
font-weight: var(--font-semibold);
}
.news-body {
padding: var(--space-lg);
display: flex;
flex-direction: column;
flex: 1;
}
.news-meta {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: var(--space-sm);
font-size: var(--text-sm);
color: var(--text-tertiary);
}
.news-date {
font-weight: var(--font-medium);
}
.news-read-time {
display: flex;
align-items: center;
gap: var(--space-xs);
}
.news-title {
margin-bottom: var(--space-md);
}
.news-link {
font-family: var(--font-heading);
font-size: var(--text-xl);
font-weight: var(--font-semibold);
color: var(--text-primary);
text-decoration: none;
line-height: var(--leading-tight);
transition: color 0.2s ease;
}
.news-link:hover {
color: var(--primary-600);
text-decoration: underline;
}
.news-excerpt {
color: var(--text-secondary);
line-height: var(--leading-relaxed);
margin-bottom: var(--space-lg);
flex: 1;
}
.news-footer {
display: flex;
justify-content: space-between;
align-items: center;
margin-top: auto;
}
.news-tags {
display: flex;
gap: var(--space-xs);
flex-wrap: wrap;
}
.news-tag {
background: var(--bg-secondary);
color: var(--text-secondary);
padding: 0.25rem 0.5rem;
border-radius: var(--radius-sm);
font-size: var(--text-xs);
font-weight: var(--font-medium);
}
.news-tag-more {
color: var(--text-tertiary);
font-size: var(--text-xs);
font-style: italic;
}
.read-more {
color: var(--primary-600);
text-decoration: none;
font-weight: var(--font-semibold);
font-size: var(--text-sm);
transition: color 0.2s ease;
}
.read-more:hover {
color: var(--primary-700);
text-decoration: underline;
}
/* Пагинация */
.pagination {
display: flex;
justify-content: center;
align-items: center;
gap: var(--space-md);
margin-top: var(--space-2xl);
}
.pagination-button {
padding: var(--space-sm) var(--space-md);
border: 1px solid var(--border-medium);
border-radius: var(--radius-md);
background: var(--bg-primary);
color: var(--text-primary);
cursor: pointer;
transition: all 0.2s ease;
font-weight: var(--font-medium);
}
.pagination-button:hover:not(:disabled) {
background: var(--bg-secondary);
border-color: var(--primary-500);
}
.pagination-button:disabled {
opacity: 0.5;
cursor: not-allowed;
}
.pagination-pages {
display: flex;
gap: var(--space-xs);
}
.pagination-page {
padding: var(--space-sm) var(--space-md);
border: 1px solid var(--border-medium);
border-radius: var(--radius-md);
background: var(--bg-primary);
color: var(--text-primary);
cursor: pointer;
transition: all 0.2s ease;
min-width: 2.5rem;
text-align: center;
}
.pagination-page.active {
background: var(--primary-500);
color: white;
border-color: var(--primary-500);
}
.pagination-page:hover:not(.active):not(:disabled) {
background: var(--bg-secondary);
border-color: var(--primary-500);
}
.pagination-page:disabled {
cursor: default;
}
/* Сообщение об отсутствии новостей */
.no-news {
text-align: center;
padding: var(--space-2xl);
color: var(--text-secondary);
}
.no-news-icon {
font-size: 4rem;
margin-bottom: var(--space-lg);
opacity: 0.5;
}
.no-news-title {
font-family: var(--font-heading);
font-size: var(--text-2xl);
font-weight: var(--font-semibold);
color: var(--text-primary);
margin-bottom: var(--space-md);
}
.no-news-description {
margin-bottom: var(--space-xl);
max-width: 400px;
margin-left: auto;
margin-right: auto;
}
/* Подписка на новости */
.newsletter-section {
background: var(--bg-secondary);
padding: var(--space-2xl) 0;
border-top: 1px solid var(--border-light);
}
.newsletter-card {
background: var(--bg-primary);
border-radius: var(--radius-xl);
padding: var(--space-2xl);
display: grid;
grid-template-columns: 1fr auto;
gap: var(--space-2xl);
align-items: center;
border: 1px solid var(--border-light);
max-width: 800px;
margin: 0 auto;
}
.newsletter-content {
flex: 1;
}
.newsletter-title {
font-family: var(--font-heading);
font-size: var(--text-2xl);
font-weight: var(--font-semibold);
color: var(--text-primary);
margin-bottom: var(--space-sm);
}
.newsletter-description {
color: var(--text-secondary);
margin-bottom: var(--space-lg);
line-height: var(--leading-relaxed);
}
.newsletter-form {
max-width: 400px;
}
.newsletter-form .form-group {
display: flex;
gap: var(--space-sm);
margin-bottom: var(--space-sm);
}
.newsletter-input {
flex: 1;
padding: var(--space-sm) var(--space-md);
border: 1px solid var(--border-medium);
border-radius: var(--radius-md);
background: var(--bg-primary);
color: var(--text-primary);
font-size: var(--text-base);
}
.newsletter-input:focus {
outline: none;
border-color: var(--primary-500);
}
.newsletter-button {
white-space: nowrap;
}
.newsletter-note {
font-size: var(--text-xs);
color: var(--text-tertiary);
margin: 0;
}
.newsletter-icon {
font-size: 4rem;
opacity: 0.7;
}
/* Адаптивность */
@media (max-width: 1024px) {
.filters-content {
flex-direction: column;
align-items: stretch;
}
.search-box {
max-width: none;
}
.filter-controls {
justify-content: space-between;
}
.newsletter-card {
grid-template-columns: 1fr;
text-align: center;
}
}
@media (max-width: 768px) {
.news-hero {
padding: 3rem 0;
}
.hero-title {
font-size: var(--text-3xl);
}
.news-grid {
grid-template-columns: 1fr;
}
.news-grid.list-view .news-card {
grid-template-columns: 1fr;
}
.filter-controls {
flex-direction: column;
align-items: stretch;
gap: var(--space-md);
}
.filter-group {
justify-content: space-between;
}
.pagination {
flex-wrap: wrap;
gap: var(--space-sm);
}
.newsletter-form .form-group {
flex-direction: column;
}
}
@media (max-width: 480px) {
.news-hero {
padding: 2rem 0;
}
.hero-title {
font-size: var(--text-2xl);
}
.news-stats {
gap: var(--space-lg);
}
.news-card {
margin: 0 var(--space-sm);
}
.news-body {
padding: var(--space-md);
}
.news-footer {
flex-direction: column;
gap: var(--space-md);
align-items: flex-start;
}
.newsletter-card {
padding: var(--space-xl);
margin: 0 var(--space-sm);
}
}
</style>