Commit 85cf6677 authored by Mahmoud Aglan's avatar Mahmoud Aglan

Redesign academy website to light theme with skeleton empty states

- Default background: white, text: dark gray, accent: blue
- Hero/navbar/footer stay dark (branded primary color)
- All sections show skeleton placeholder cards when no data exists
- Removed all hard-coded oklch dark values from section views
- Cards, testimonials, FAQ items now use white bg + subtle borders
- Empty states show shimmer animation + "لم تُضف ... بعد" message
Co-Authored-By: 's avatarClaude Opus 4.6 <noreply@anthropic.com>
parent 9ea528af
@import 'tailwindcss';
/* ═══════════════════════════════════════════════════════════════
BOLD & ATHLETIC — Public Academy Website Template
Dark, diagonal, dynamic. Sports energy.
CLEAN & PROFESSIONAL — Public Academy Website Template
Light, minimal, structured. Ready out of the box.
═══════════════════════════════════════════════════════════════ */
@theme {
--color-website-primary: var(--site-primary, #1a1a2e);
--color-website-secondary: var(--site-secondary, #16213e);
--color-website-accent: var(--site-accent, #e94560);
--color-website-text: var(--site-text, #ffffff);
--color-website-muted: oklch(0.75 0.01 260);
--color-website-surface: oklch(0.15 0.02 260);
--color-website-surface-raised: oklch(0.18 0.02 260);
--color-website-border: oklch(0.25 0.02 260);
--color-website-primary: var(--site-primary, #1e3a5f);
--color-website-secondary: var(--site-secondary, #2d5a87);
--color-website-accent: var(--site-accent, #2563eb);
--color-website-text: var(--site-text, #1f2937);
--color-website-muted: #6b7280;
--color-website-surface: #ffffff;
--color-website-surface-raised: #f9fafb;
--color-website-border: #e5e7eb;
}
@layer base {
.website-body {
background: linear-gradient(135deg, var(--site-primary, #1a1a2e) 0%, var(--site-secondary, #16213e) 100%);
color: var(--site-text, #ffffff);
background: var(--site-bg, #ffffff);
color: var(--site-text, #1f2937);
font-family: var(--site-body-font, 'Cairo'), 'Noto Sans Arabic', system-ui, sans-serif;
direction: rtl;
min-height: 100vh;
......@@ -41,28 +41,27 @@
inset-inline: 0;
z-index: 100;
transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
backdrop-filter: blur(0px);
background: var(--site-primary, #1e3a5f);
}
.site-nav--scrolled {
background: oklch(0.12 0.02 260 / 0.95);
backdrop-filter: blur(12px);
box-shadow: 0 4px 30px oklch(0 0 0 / 0.3);
background: var(--site-primary, #1e3a5f);
box-shadow: 0 2px 20px rgba(0,0,0,0.1);
}
.site-nav--solid {
background: oklch(0.12 0.02 260 / 0.98);
backdrop-filter: blur(12px);
background: var(--site-primary, #1e3a5f);
}
/* ─── Hero ─── */
.hero-section {
position: relative;
min-height: 100vh;
min-height: 85vh;
display: flex;
align-items: center;
justify-content: center;
overflow: hidden;
background: linear-gradient(135deg, var(--site-primary, #1e3a5f) 0%, var(--site-secondary, #2d5a87) 100%);
}
.hero-section::after {
......@@ -71,13 +70,13 @@
bottom: 0;
left: 0;
right: 0;
height: 120px;
background: linear-gradient(to top, var(--site-primary, #1a1a2e), transparent);
height: 80px;
background: linear-gradient(to top, var(--site-bg, #ffffff), transparent);
z-index: 2;
}
.hero-clip {
clip-path: polygon(0 0, 100% 0, 100% 88%, 0 100%);
clip-path: none;
}
.hero-overlay {
......@@ -85,8 +84,8 @@
inset: 0;
background: linear-gradient(
135deg,
oklch(0.1 0.02 260 / var(--overlay-opacity, 0.7)) 0%,
oklch(0.05 0.03 260 / calc(var(--overlay-opacity, 0.7) + 0.1)) 100%
rgba(0,0,0, var(--overlay-opacity, 0.4)) 0%,
rgba(0,0,0, calc(var(--overlay-opacity, 0.4) + 0.1)) 100%
);
z-index: 1;
}
......@@ -99,186 +98,166 @@
}
.hero-title {
font-size: clamp(2.5rem, 6vw, 5rem);
font-size: clamp(2.5rem, 6vw, 4.5rem);
font-weight: 800;
line-height: 1.1;
text-shadow: 0 4px 20px oklch(0 0 0 / 0.5);
letter-spacing: -0.02em;
line-height: 1.15;
color: #ffffff;
letter-spacing: -0.01em;
}
.hero-accent-line {
width: 0;
height: 4px;
margin: 1.5rem auto;
background: var(--site-accent, #e94560);
background: var(--site-accent, #2563eb);
border-radius: 2px;
animation: accent-expand 1s 0.5s cubic-bezier(0.4, 0, 0.2, 1) forwards;
}
@keyframes accent-expand {
to { width: 120px; }
to { width: 80px; }
}
/* ─── Section Shared ─── */
.site-section {
position: relative;
padding: 6rem 0;
padding: 5rem 0;
overflow: hidden;
background: var(--site-bg, #ffffff);
}
.site-section--alt {
background: #f8fafc;
}
.site-section--dark {
background: oklch(0.1 0.015 260);
background: var(--site-primary, #1e3a5f);
color: #ffffff;
}
.site-section--pattern {
background-image: repeating-linear-gradient(
-45deg,
transparent,
transparent 40px,
oklch(1 0 0 / 0.02) 40px,
oklch(1 0 0 / 0.02) 41px
);
background-image: none;
}
.section-title {
font-size: clamp(1.75rem, 4vw, 3rem);
font-size: clamp(1.75rem, 3.5vw, 2.5rem);
font-weight: 700;
color: var(--site-text, #1f2937);
margin-bottom: 0.5rem;
position: relative;
display: inline-block;
}
.site-section--dark .section-title {
color: #ffffff;
}
.section-title::after {
content: '';
position: absolute;
bottom: -8px;
inset-inline-start: 0;
width: 60px;
width: 50px;
height: 3px;
background: var(--site-accent, #e94560);
background: var(--site-accent, #2563eb);
border-radius: 2px;
}
/* ─── Cards (Activity / Program / Branch) ─── */
/* ─── Cards ─── */
.site-card {
position: relative;
border-radius: 1rem;
overflow: hidden;
background: oklch(0.15 0.02 260);
border: 1px solid oklch(0.25 0.02 260);
transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
background: #ffffff;
border: 1px solid #e5e7eb;
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
box-shadow: 0 1px 3px rgba(0,0,0,0.05);
}
.site-card:hover {
transform: translateY(-4px);
border-color: var(--site-accent, #e94560);
box-shadow: 0 20px 40px oklch(0 0 0 / 0.3),
0 0 0 1px var(--site-accent, #e94560);
transform: translateY(-2px);
border-color: var(--site-accent, #2563eb);
box-shadow: 0 10px 30px rgba(0,0,0,0.08);
}
.site-card__image {
width: 100%;
aspect-ratio: 4/3;
object-fit: cover;
transition: transform 0.6s cubic-bezier(0.4, 0, 0.2, 1);
transition: transform 0.5s cubic-bezier(0.4, 0, 0.2, 1);
}
.site-card:hover .site-card__image {
transform: scale(1.08);
transform: scale(1.03);
}
.site-card__overlay {
position: absolute;
inset: 0;
background: linear-gradient(to top, oklch(0.08 0.02 260 / 0.9) 0%, transparent 60%);
opacity: 0;
transition: opacity 0.4s;
}
.site-card:hover .site-card__overlay {
opacity: 1;
background: linear-gradient(to top, rgba(0,0,0,0.7) 0%, transparent 60%);
opacity: 0.8;
transition: opacity 0.3s;
}
/* ─── Stats Counter ─── */
/* ─── Stats ─── */
.stats-band {
position: relative;
background: oklch(0.08 0.02 260);
background: var(--site-primary, #1e3a5f);
overflow: hidden;
}
.stats-band::before,
.stats-band::after {
content: '';
position: absolute;
width: 200px;
height: 200%;
background: var(--site-accent, #e94560);
opacity: 0.05;
transform: rotate(-15deg);
}
.stats-band::before { left: 10%; top: -50%; }
.stats-band::after { right: 15%; top: -50%; }
.stat-number {
font-size: clamp(2.5rem, 5vw, 4rem);
font-size: clamp(2.5rem, 5vw, 3.5rem);
font-weight: 800;
color: var(--site-accent, #e94560);
color: var(--site-accent, #2563eb);
line-height: 1;
}
/* ─── Testimonials Carousel ─── */
/* ─── Testimonials ─── */
.testimonial-card {
background: oklch(0.15 0.02 260);
border-radius: 1.5rem;
padding: 2.5rem;
border: 1px solid oklch(0.25 0.02 260);
background: #ffffff;
border-radius: 1rem;
padding: 2rem;
border: 1px solid #e5e7eb;
position: relative;
box-shadow: 0 1px 3px rgba(0,0,0,0.05);
}
.testimonial-card::before {
content: '\201C';
position: absolute;
top: 1rem;
inset-inline-start: 1.5rem;
font-size: 5rem;
top: 0.75rem;
inset-inline-start: 1.25rem;
font-size: 4rem;
line-height: 1;
color: var(--site-accent, #e94560);
opacity: 0.3;
color: var(--site-accent, #2563eb);
opacity: 0.15;
font-family: serif;
}
/* ─── FAQ Accordion ─── */
/* ─── FAQ ─── */
.faq-item {
border: 1px solid oklch(0.25 0.02 260);
border: 1px solid #e5e7eb;
border-radius: 0.75rem;
overflow: hidden;
transition: border-color 0.3s;
background: #ffffff;
}
.faq-item:hover,
.faq-item[data-open="true"] {
border-color: var(--site-accent, #e94560);
border-color: var(--site-accent, #2563eb);
}
/* ─── CTA Section ─── */
.cta-section {
position: relative;
background: linear-gradient(135deg, var(--site-accent, #e94560), oklch(0.55 0.2 350));
background: linear-gradient(135deg, var(--site-primary, #1e3a5f), var(--site-secondary, #2d5a87));
overflow: hidden;
}
.cta-section::before {
content: '';
position: absolute;
inset: 0;
background: repeating-linear-gradient(
-45deg,
transparent,
transparent 30px,
oklch(1 0 0 / 0.05) 30px,
oklch(1 0 0 / 0.05) 31px
);
content: none;
}
/* ─── Buttons ─── */
......@@ -296,31 +275,32 @@
}
.site-btn--primary {
background: var(--site-accent, #e94560);
background: var(--site-accent, #2563eb);
color: white;
box-shadow: 0 4px 15px oklch(0.6 0.2 15 / 0.3);
box-shadow: 0 2px 8px rgba(37, 99, 235, 0.25);
}
.site-btn--primary:hover {
transform: scale(1.05);
box-shadow: 0 8px 25px oklch(0.6 0.2 15 / 0.5);
transform: translateY(-1px);
box-shadow: 0 4px 15px rgba(37, 99, 235, 0.35);
}
.site-btn--outline {
background: transparent;
color: white;
border: 2px solid oklch(1 0 0 / 0.3);
border: 2px solid rgba(255,255,255,0.4);
}
.site-btn--outline:hover {
border-color: var(--site-accent, #e94560);
color: var(--site-accent, #e94560);
border-color: white;
background: rgba(255,255,255,0.1);
}
/* ─── Footer ─── */
.site-footer {
background: oklch(0.06 0.015 260);
border-top: 1px solid oklch(0.2 0.02 260);
background: var(--site-primary, #1e3a5f);
color: #ffffff;
border-top: none;
}
/* ─── WhatsApp Float ─── */
......@@ -336,7 +316,7 @@
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 4px 15px oklch(0.5 0.2 150 / 0.4);
box-shadow: 0 4px 12px rgba(37, 211, 102, 0.3);
transition: transform 0.3s;
animation: whatsapp-pulse 2s infinite;
}
......@@ -346,15 +326,15 @@
}
@keyframes whatsapp-pulse {
0%, 100% { box-shadow: 0 4px 15px oklch(0.5 0.2 150 / 0.4); }
50% { box-shadow: 0 4px 25px oklch(0.5 0.2 150 / 0.6), 0 0 0 8px oklch(0.5 0.2 150 / 0.1); }
0%, 100% { box-shadow: 0 4px 12px rgba(37, 211, 102, 0.3); }
50% { box-shadow: 0 4px 20px rgba(37, 211, 102, 0.5), 0 0 0 6px rgba(37, 211, 102, 0.08); }
}
/* ─── Animations ─── */
.reveal {
opacity: 0;
transform: translateY(30px);
transition: all 0.8s cubic-bezier(0.4, 0, 0.2, 1);
transform: translateY(20px);
transition: all 0.7s cubic-bezier(0.4, 0, 0.2, 1);
}
.reveal--visible {
......@@ -371,7 +351,7 @@
.map-container {
border-radius: 1rem;
overflow: hidden;
border: 1px solid oklch(0.25 0.02 260);
border: 1px solid #e5e7eb;
height: 350px;
}
......@@ -388,6 +368,7 @@
overflow: hidden;
aspect-ratio: 3/2;
cursor: pointer;
border: 1px solid #e5e7eb;
}
.gallery-item img {
......@@ -398,19 +379,7 @@
}
.gallery-item:hover img {
transform: scale(1.1);
}
.gallery-item::after {
content: '';
position: absolute;
inset: 0;
background: oklch(0.1 0 0 / 0);
transition: background 0.3s;
}
.gallery-item:hover::after {
background: oklch(0.1 0 0 / 0.4);
transform: scale(1.05);
}
/* ─── Partners Logo Bar ─── */
......@@ -429,33 +398,74 @@
.site-input {
width: 100%;
padding: 0.875rem 1rem;
background: oklch(0.12 0.02 260);
border: 1px solid oklch(0.25 0.02 260);
background: #ffffff;
border: 1px solid #d1d5db;
border-radius: 0.75rem;
color: white;
color: #1f2937;
font-size: 1rem;
transition: border-color 0.3s;
}
.site-input:focus {
outline: none;
border-color: var(--site-accent, #e94560);
box-shadow: 0 0 0 3px oklch(0.6 0.2 15 / 0.1);
border-color: var(--site-accent, #2563eb);
box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.1);
}
.site-input::placeholder {
color: oklch(0.55 0.01 260);
color: #9ca3af;
}
.site-section--dark .site-input {
background: rgba(255,255,255,0.1);
border-color: rgba(255,255,255,0.2);
color: #ffffff;
}
.site-section--dark .site-input::placeholder {
color: rgba(255,255,255,0.5);
}
/* ─── Empty/Skeleton States ─── */
.skeleton-card {
background: #f3f4f6;
border: 1px solid #e5e7eb;
border-radius: 1rem;
overflow: hidden;
}
.skeleton-block {
background: linear-gradient(90deg, #f3f4f6 25%, #e5e7eb 50%, #f3f4f6 75%);
background-size: 200% 100%;
animation: skeleton-shimmer 1.5s infinite;
border-radius: 0.5rem;
}
@keyframes skeleton-shimmer {
0% { background-position: 200% 0; }
100% { background-position: -200% 0; }
}
.empty-state {
text-align: center;
padding: 3rem 1rem;
color: #9ca3af;
}
.empty-state svg {
margin: 0 auto 1rem;
opacity: 0.4;
}
}
/* ─── Responsive ─── */
@media (max-width: 768px) {
.hero-section {
min-height: 70vh;
min-height: 65vh;
}
.site-section {
padding: 3.5rem 0;
padding: 3rem 0;
}
.gallery-grid {
......
......@@ -17,8 +17,7 @@ class="h-12 w-12 rounded-lg object-contain">
</p>
@endif
{{-- Social Links --}}
@if(is_array($settings->social_links) && count(array_filter($settings->social_links)))
@if(is_array($settings->social_links ?? null) && count(array_filter($settings->social_links)))
<div class="flex items-center gap-3 mt-6">
@foreach(['facebook', 'instagram', 'twitter', 'youtube', 'tiktok'] as $platform)
@if(!empty($settings->social_links[$platform]))
......
......@@ -26,10 +26,11 @@
{{-- Dynamic CSS Variables from Academy Branding --}}
<style>
:root {
--site-primary: {{ $settings->primary_color ?? '#1a1a2e' }};
--site-secondary: {{ $settings->secondary_color ?? '#16213e' }};
--site-accent: {{ $settings->accent_color ?? '#e94560' }};
--site-text: {{ $settings->text_color ?? '#ffffff' }};
--site-primary: {{ $settings->primary_color ?? '#1e3a5f' }};
--site-secondary: {{ $settings->secondary_color ?? '#2d5a87' }};
--site-accent: {{ $settings->accent_color ?? '#2563eb' }};
--site-text: {{ $settings->text_color ?? '#1f2937' }};
--site-bg: {{ $settings->background_color ?? '#ffffff' }};
--site-heading-font: '{{ $settings->heading_font ?? 'Cairo' }}', 'Noto Sans Arabic', sans-serif;
--site-body-font: '{{ $settings->body_font ?? 'Cairo' }}', 'Noto Sans Arabic', sans-serif;
}
......
......@@ -10,23 +10,26 @@
<div class="site-section">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div class="grid grid-cols-1 lg:grid-cols-2 gap-12 items-center">
{{-- Text Content --}}
<div class="reveal">
<h2 class="section-title">{{ $section->title ?? 'من نحن' }}</h2>
@if($section->content)
<div class="mt-8 text-white/70 leading-relaxed text-lg space-y-4">
<div class="mt-8 text-gray-600 leading-relaxed text-lg space-y-4">
{!! nl2br(e($section->content)) !!}
</div>
@else
<div class="mt-8 text-gray-400 leading-relaxed text-lg">
<p>أضف وصفاً عن أكاديميتك من إعدادات الموقع</p>
</div>
@endif
@if($section->getSetting('show_stats', true))
@if($section->getSetting('show_stats', true) && !empty($data['stats'] ?? []))
<div class="mt-8 flex items-center gap-8">
@foreach(($data['stats'] ?? []) as $key => $value)
@if($loop->index < 3 && $value > 0)
<div class="text-center">
<span class="block text-2xl font-bold text-[var(--site-accent)]">{{ $value }}+</span>
<span class="text-sm text-white/50">
<span class="text-sm text-gray-500">
{{ match($key) { 'participants' => 'طالب', 'trainers' => 'مدرب', 'branches' => 'فرع', 'programs' => 'برنامج', default => $key } }}
</span>
</div>
......@@ -36,19 +39,17 @@
@endif
</div>
{{-- Image --}}
<div class="reveal reveal--delay-2">
@if($sectionImage)
<div class="relative">
<img src="{{ $sectionImage->url }}"
alt="{{ $section->title }}"
class="rounded-2xl w-full object-cover shadow-2xl">
{{-- Decorative accent corner --}}
<div class="absolute -bottom-4 -start-4 w-24 h-24 border-b-4 border-s-4 border-[var(--site-accent)] rounded-bl-2xl"></div>
class="rounded-2xl w-full object-cover shadow-lg">
<div class="absolute -bottom-3 -start-3 w-20 h-20 border-b-4 border-s-4 border-[var(--site-accent)] rounded-bl-2xl"></div>
</div>
@else
<div class="relative rounded-2xl overflow-hidden aspect-[4/3] bg-gradient-to-br from-[var(--site-accent)]/20 to-transparent border border-white/10 flex items-center justify-center">
<svg class="w-20 h-20 text-white/20" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<div class="relative rounded-2xl overflow-hidden aspect-[4/3] bg-gray-50 border border-gray-200 flex items-center justify-center">
<svg class="w-16 h-16 text-gray-300" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1" d="M19 21V5a2 2 0 00-2-2H7a2 2 0 00-2 2v16m14 0h2m-2 0h-5m-9 0H3m2 0h5M9 7h1m-1 4h1m4-4h1m-1 4h1m-5 10v-5a1 1 0 011-1h2a1 1 0 011 1v5m-4 0h4"/>
</svg>
</div>
......
......@@ -3,62 +3,71 @@
$maxItems = $section->getSetting('max_items', 6);
@endphp
@if($activities->count())
<div class="site-section site-section--dark site-section--pattern">
<div class="site-section site-section--alt">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div class="text-center mb-12 reveal">
<h2 class="section-title mx-auto">{{ $section->title ?? 'الأنشطة' }}</h2>
@if($section->subtitle)
<p class="mt-4 text-white/60 text-lg">{{ $section->subtitle }}</p>
<p class="mt-4 text-gray-500 text-lg">{{ $section->subtitle }}</p>
@endif
</div>
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6">
@foreach($activities->take($maxItems) as $activity)
@php
$activityImage = \App\Domain\Website\Models\Media::withoutGlobalScope('academy')
->where('academy_id', $academy->id)
->where('collection', 'activity_photo')
->where('mediable_type', 'activity')
->where('mediable_id', $activity->id)
->first();
@endphp
<div class="site-card reveal reveal--delay-{{ ($loop->index % 4) + 1 }}">
<div class="relative overflow-hidden aspect-[4/3]">
@if($activityImage)
<img src="{{ $activityImage->url }}"
alt="{{ $activity->name_ar }}"
class="site-card__image">
@else
<div class="w-full h-full bg-gradient-to-br from-[var(--site-accent)]/30 to-[var(--site-secondary)] flex items-center justify-center">
@if($activity->icon)
<span class="text-5xl">{{ $activity->icon }}</span>
@else
<svg class="w-16 h-16 text-white/30" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M13 10V3L4 14h7v7l9-11h-7z"/>
</svg>
@if($activities->count())
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6">
@foreach($activities->take($maxItems) as $activity)
@php
$activityImage = \App\Domain\Website\Models\Media::withoutGlobalScope('academy')
->where('academy_id', $academy->id)
->where('collection', 'activity_photo')
->where('mediable_type', 'activity')
->where('mediable_id', $activity->id)
->first();
@endphp
<div class="site-card reveal reveal--delay-{{ ($loop->index % 4) + 1 }}">
<div class="relative overflow-hidden aspect-[4/3]">
@if($activityImage)
<img src="{{ $activityImage->url }}"
alt="{{ $activity->name_ar }}"
class="site-card__image">
@else
<div class="w-full h-full bg-gradient-to-br from-[var(--site-accent)]/10 to-gray-100 flex items-center justify-center">
@if($activity->icon ?? null)
<span class="text-5xl">{{ $activity->icon }}</span>
@else
<svg class="w-14 h-14 text-gray-300" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M13 10V3L4 14h7v7l9-11h-7z"/>
</svg>
@endif
</div>
@endif
<div class="site-card__overlay"></div>
<div class="absolute bottom-0 inset-x-0 p-4 bg-gradient-to-t from-black/70 to-transparent">
<h3 class="text-white font-bold text-lg">{{ $activity->name_ar }}</h3>
@if($activity->description_ar ?? null)
<p class="text-white/80 text-sm mt-1 line-clamp-2">{{ $activity->description_ar }}</p>
@endif
</div>
@endif
<div class="site-card__overlay"></div>
{{-- Activity Name Overlay --}}
<div class="absolute bottom-0 inset-x-0 p-4 bg-gradient-to-t from-black/80 to-transparent">
<h3 class="text-white font-bold text-lg">{{ $activity->name_ar }}</h3>
@if($activity->description_ar)
<p class="text-white/70 text-sm mt-1 line-clamp-2">{{ $activity->description_ar }}</p>
@endif
</div>
<div class="h-1 w-full" style="background: {{ $activity->color ?? 'var(--site-accent)' }}"></div>
</div>
@if($activity->color)
<div class="h-1 w-full" style="background: {{ $activity->color }}"></div>
@else
<div class="h-1 w-full bg-[var(--site-accent)]"></div>
@endif
</div>
@endforeach
</div>
@endforeach
</div>
@else
{{-- Empty skeleton state --}}
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6">
@for($i = 0; $i < 3; $i++)
<div class="skeleton-card">
<div class="aspect-[4/3] skeleton-block"></div>
<div class="p-4 space-y-2">
<div class="skeleton-block h-5 w-2/3"></div>
<div class="skeleton-block h-3 w-full"></div>
</div>
</div>
@endfor
</div>
<div class="empty-state mt-6">
<p class="text-sm">لم تُضف أنشطة بعد</p>
</div>
@endif
</div>
</div>
@endif
......@@ -2,37 +2,49 @@
$faqs = $data['faqs'] ?? collect();
@endphp
@if($faqs->count())
<div class="site-section">
<div class="site-section site-section--alt">
<div class="max-w-3xl mx-auto px-4 sm:px-6 lg:px-8">
<div class="text-center mb-12 reveal">
<h2 class="section-title mx-auto">{{ $section->title ?? 'الأسئلة الشائعة' }}</h2>
@if($section->subtitle)
<p class="mt-4 text-white/60 text-lg">{{ $section->subtitle }}</p>
<p class="mt-4 text-gray-500 text-lg">{{ $section->subtitle }}</p>
@endif
</div>
<div class="space-y-3" x-data="{ open: null }">
@foreach($faqs as $faq)
<div class="faq-item reveal reveal--delay-{{ min($loop->index + 1, 4) }}"
:data-open="open === {{ $faq->id }} ? 'true' : 'false'">
<button @click="open = open === {{ $faq->id }} ? null : {{ $faq->id }}"
class="w-full flex items-center justify-between p-5 text-start">
<span class="text-white font-medium pe-4">{{ $faq->question }}</span>
<svg class="w-5 h-5 text-[var(--site-accent)] shrink-0 transition-transform duration-300"
:class="open === {{ $faq->id }} ? 'rotate-180' : ''"
fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"/>
</svg>
</button>
<div x-show="open === {{ $faq->id }}"
x-collapse
class="px-5 pb-5">
<p class="text-white/60 leading-relaxed">{{ $faq->answer }}</p>
@if($faqs->count())
<div class="space-y-3" x-data="{ open: null }">
@foreach($faqs as $faq)
<div class="faq-item reveal reveal--delay-{{ min($loop->index + 1, 4) }}"
:data-open="open === {{ $faq->id }} ? 'true' : 'false'">
<button @click="open = open === {{ $faq->id }} ? null : {{ $faq->id }}"
class="w-full flex items-center justify-between p-5 text-start">
<span class="text-gray-900 font-medium pe-4">{{ $faq->question }}</span>
<svg class="w-5 h-5 text-[var(--site-accent)] shrink-0 transition-transform duration-300"
:class="open === {{ $faq->id }} ? 'rotate-180' : ''"
fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"/>
</svg>
</button>
<div x-show="open === {{ $faq->id }}"
x-collapse
class="px-5 pb-5">
<p class="text-gray-600 leading-relaxed">{{ $faq->answer }}</p>
</div>
</div>
</div>
@endforeach
</div>
@endforeach
</div>
@else
<div class="space-y-3">
@for($i = 0; $i < 3; $i++)
<div class="skeleton-card p-5 flex items-center justify-between">
<div class="skeleton-block h-5 w-2/3"></div>
<div class="skeleton-block h-5 w-5 !rounded-full"></div>
</div>
@endfor
</div>
<div class="empty-state mt-4">
<p class="text-sm">لم تُضف أسئلة شائعة بعد</p>
</div>
@endif
</div>
</div>
@endif
......@@ -2,30 +2,40 @@
$gallery = $data['gallery'] ?? collect();
@endphp
@if($gallery->count())
<div class="site-section site-section--dark" x-data="{ lightbox: null }">
<div class="site-section" x-data="{ lightbox: null }">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div class="text-center mb-12 reveal">
<h2 class="section-title mx-auto">{{ $section->title ?? 'معرض الصور' }}</h2>
@if($section->subtitle)
<p class="mt-4 text-white/60 text-lg">{{ $section->subtitle }}</p>
<p class="mt-4 text-gray-500 text-lg">{{ $section->subtitle }}</p>
@endif
</div>
<div class="gallery-grid reveal">
@foreach($gallery as $image)
<div class="gallery-item" @click="lightbox = '{{ $image->url }}'">
<img src="{{ $image->url }}"
alt="{{ $image->alt_text_ar ?? 'صورة من المعرض' }}"
loading="lazy">
<div class="absolute inset-0 flex items-center justify-center opacity-0 hover:opacity-100 transition-opacity z-10">
<svg class="w-10 h-10 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0zM10 7v3m0 0v3m0-3h3m-3 0H7"/>
</svg>
@if($gallery->count())
<div class="gallery-grid reveal">
@foreach($gallery as $image)
<div class="gallery-item" @click="lightbox = '{{ $image->url }}'">
<img src="{{ $image->url }}"
alt="{{ $image->alt_text_ar ?? 'صورة من المعرض' }}"
loading="lazy">
<div class="absolute inset-0 flex items-center justify-center opacity-0 hover:opacity-100 transition-opacity z-10 bg-black/30">
<svg class="w-10 h-10 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0zM10 7v3m0 0v3m0-3h3m-3 0H7"/>
</svg>
</div>
</div>
</div>
@endforeach
</div>
@endforeach
</div>
@else
<div class="gallery-grid">
@for($i = 0; $i < 6; $i++)
<div class="aspect-[3/2] skeleton-block rounded-lg"></div>
@endfor
</div>
<div class="empty-state mt-4">
<p class="text-sm">لم تُضف صور بعد</p>
</div>
@endif
</div>
{{-- Lightbox --}}
......@@ -43,4 +53,3 @@ class="fixed inset-0 z-[200] bg-black/95 flex items-center justify-center p-4 cu
</button>
</div>
</div>
@endif
......@@ -2,48 +2,64 @@
$news = $data['news'] ?? collect();
@endphp
@if($news->count())
<div class="site-section site-section--dark">
<div class="site-section site-section--alt">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div class="text-center mb-12 reveal">
<h2 class="section-title mx-auto">{{ $section->title ?? 'آخر الأخبار' }}</h2>
@if($section->subtitle)
<p class="mt-4 text-white/60 text-lg">{{ $section->subtitle }}</p>
<p class="mt-4 text-gray-500 text-lg">{{ $section->subtitle }}</p>
@endif
</div>
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
@foreach($news as $article)
<article class="site-card reveal reveal--delay-{{ ($loop->index % 3) + 1 }}">
@if($article->image)
<div class="relative overflow-hidden aspect-[16/9]">
<img src="{{ $article->image->url }}"
alt="{{ $article->title }}"
class="site-card__image"
loading="lazy">
</div>
@endif
<div class="p-5">
@if($article->published_at)
<time class="text-xs text-white/40">
{{ $article->published_at->translatedFormat('j F Y') }}
</time>
@if($news->count())
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
@foreach($news as $article)
<article class="site-card reveal reveal--delay-{{ ($loop->index % 3) + 1 }}">
@if($article->image)
<div class="relative overflow-hidden aspect-[16/9]">
<img src="{{ $article->image->url }}"
alt="{{ $article->title }}"
class="site-card__image"
loading="lazy">
</div>
@endif
<h3 class="text-lg font-bold text-white mt-2 line-clamp-2">
{{ $article->title }}
</h3>
<div class="p-5">
@if($article->published_at)
<time class="text-xs text-gray-400">
{{ $article->published_at->translatedFormat('j F Y') }}
</time>
@endif
@if($article->excerpt)
<p class="text-white/50 text-sm mt-2 line-clamp-3">
{{ $article->excerpt }}
</p>
@endif
<h3 class="text-lg font-bold text-gray-900 mt-2 line-clamp-2">
{{ $article->title }}
</h3>
@if($article->excerpt)
<p class="text-gray-500 text-sm mt-2 line-clamp-3">
{{ $article->excerpt }}
</p>
@endif
</div>
</article>
@endforeach
</div>
@else
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
@for($i = 0; $i < 3; $i++)
<div class="skeleton-card">
<div class="aspect-[16/9] skeleton-block"></div>
<div class="p-5 space-y-3">
<div class="skeleton-block h-3 w-20"></div>
<div class="skeleton-block h-5 w-3/4"></div>
<div class="skeleton-block h-3 w-full"></div>
</div>
</div>
</article>
@endforeach
</div>
@endfor
</div>
<div class="empty-state mt-4">
<p class="text-sm">لم تُضف أخبار بعد</p>
</div>
@endif
</div>
</div>
@endif
......@@ -3,78 +3,95 @@
$maxItems = $section->getSetting('max_items', 6);
@endphp
@if($programs->count())
<div class="site-section">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div class="text-center mb-12 reveal">
<h2 class="section-title mx-auto">{{ $section->title ?? 'البرامج التدريبية' }}</h2>
@if($section->subtitle)
<p class="mt-4 text-white/60 text-lg">{{ $section->subtitle }}</p>
<p class="mt-4 text-gray-500 text-lg">{{ $section->subtitle }}</p>
@endif
</div>
<div class="space-y-8">
@foreach($programs->take($maxItems) as $program)
@php
$programImage = \App\Domain\Website\Models\Media::withoutGlobalScope('academy')
->where('academy_id', $academy->id)
->where('collection', 'section_image')
->where('mediable_type', 'training_program')
->where('mediable_id', $program->id)
->first();
$isReversed = $loop->index % 2 !== 0;
@endphp
<div class="grid grid-cols-1 lg:grid-cols-2 gap-8 items-center reveal {{ $isReversed ? '' : '' }}">
{{-- Image --}}
<div class="{{ $isReversed ? 'lg:order-2' : '' }}">
@if($programImage)
<img src="{{ $programImage->url }}"
alt="{{ $program->name_ar }}"
class="rounded-xl w-full aspect-[16/10] object-cover border border-white/10">
@else
<div class="rounded-xl w-full aspect-[16/10] bg-gradient-to-br from-[var(--site-accent)]/10 to-[var(--site-secondary)] border border-white/10 flex items-center justify-center">
<svg class="w-16 h-16 text-white/20" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M12 6.253v13m0-13C10.832 5.477 9.246 5 7.5 5S4.168 5.477 3 6.253v13C4.168 18.477 5.754 18 7.5 18s3.332.477 4.5 1.253m0-13C13.168 5.477 14.754 5 16.5 5c1.747 0 3.332.477 4.5 1.253v13C19.832 18.477 18.247 18 16.5 18c-1.746 0-3.332.477-4.5 1.253"/>
</svg>
</div>
@endif
</div>
{{-- Content --}}
<div class="{{ $isReversed ? 'lg:order-1' : '' }}">
<h3 class="text-2xl font-bold text-white">{{ $program->name_ar }}</h3>
<div class="flex items-center gap-3 mt-3">
@if(($program->age_from ?? null) && ($program->age_to ?? null))
<span class="inline-flex items-center gap-1 px-3 py-1 rounded-full text-xs font-medium bg-[var(--site-accent)]/10 text-[var(--site-accent)] border border-[var(--site-accent)]/20">
{{ $program->age_from }} - {{ $program->age_to }} سنة
</span>
@endif
@if($program->duration_months ?? null)
<span class="inline-flex items-center gap-1 px-3 py-1 rounded-full text-xs font-medium bg-white/5 text-white/70 border border-white/10">
{{ $program->duration_months }} شهر
</span>
@if($programs->count())
<div class="space-y-8">
@foreach($programs->take($maxItems) as $program)
@php
$programImage = \App\Domain\Website\Models\Media::withoutGlobalScope('academy')
->where('academy_id', $academy->id)
->where('collection', 'section_image')
->where('mediable_type', 'training_program')
->where('mediable_id', $program->id)
->first();
$isReversed = $loop->index % 2 !== 0;
@endphp
<div class="grid grid-cols-1 lg:grid-cols-2 gap-8 items-center reveal">
<div class="{{ $isReversed ? 'lg:order-2' : '' }}">
@if($programImage)
<img src="{{ $programImage->url }}"
alt="{{ $program->name_ar }}"
class="rounded-xl w-full aspect-[16/10] object-cover border border-gray-200">
@else
<div class="rounded-xl w-full aspect-[16/10] bg-gradient-to-br from-[var(--site-accent)]/5 to-gray-50 border border-gray-200 flex items-center justify-center">
<svg class="w-14 h-14 text-gray-300" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M12 6.253v13m0-13C10.832 5.477 9.246 5 7.5 5S4.168 5.477 3 6.253v13C4.168 18.477 5.754 18 7.5 18s3.332.477 4.5 1.253m0-13C13.168 5.477 14.754 5 16.5 5c1.747 0 3.332.477 4.5 1.253v13C19.832 18.477 18.247 18 16.5 18c-1.746 0-3.332.477-4.5 1.253"/>
</svg>
</div>
@endif
</div>
@if($program->description_ar ?? null)
<p class="mt-4 text-white/60 leading-relaxed">
{{ Str::limit($program->description_ar, 200) }}
</p>
@endif
<div class="{{ $isReversed ? 'lg:order-1' : '' }}">
<h3 class="text-2xl font-bold text-gray-900">{{ $program->name_ar }}</h3>
<div class="flex items-center gap-3 mt-3">
@if(($program->age_from ?? null) && ($program->age_to ?? null))
<span class="inline-flex items-center gap-1 px-3 py-1 rounded-full text-xs font-medium bg-[var(--site-accent)]/10 text-[var(--site-accent)] border border-[var(--site-accent)]/20">
{{ $program->age_from }} - {{ $program->age_to }} سنة
</span>
@endif
@if($program->duration_months ?? null)
<span class="inline-flex items-center gap-1 px-3 py-1 rounded-full text-xs font-medium bg-gray-100 text-gray-600 border border-gray-200">
{{ $program->duration_months }} شهر
</span>
@endif
</div>
@if($program->description_ar ?? null)
<p class="mt-4 text-gray-500 leading-relaxed">
{{ Str::limit($program->description_ar, 200) }}
</p>
@endif
<a href="#section-contact" class="inline-flex items-center gap-2 mt-6 text-[var(--site-accent)] font-medium hover:gap-3 transition-all">
سجّل الآن
<svg class="w-4 h-4 rotate-180" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 8l4 4m0 0l-4 4m4-4H3"/></svg>
</a>
<a href="#section-contact" class="inline-flex items-center gap-2 mt-6 text-[var(--site-accent)] font-medium hover:gap-3 transition-all">
سجّل الآن
<svg class="w-4 h-4 rotate-180" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 8l4 4m0 0l-4 4m4-4H3"/></svg>
</a>
</div>
</div>
</div>
@if(!$loop->last)
<div class="h-px w-full bg-gradient-to-l from-transparent via-white/10 to-transparent"></div>
@endif
@endforeach
</div>
@if(!$loop->last)
<div class="h-px w-full bg-gray-100"></div>
@endif
@endforeach
</div>
@else
{{-- Empty skeleton state --}}
<div class="space-y-8">
@for($i = 0; $i < 2; $i++)
<div class="grid grid-cols-1 lg:grid-cols-2 gap-8 items-center">
<div class="{{ $i % 2 !== 0 ? 'lg:order-2' : '' }}">
<div class="rounded-xl w-full aspect-[16/10] skeleton-block"></div>
</div>
<div class="{{ $i % 2 !== 0 ? 'lg:order-1' : '' }} space-y-3">
<div class="skeleton-block h-7 w-1/2"></div>
<div class="skeleton-block h-4 w-full"></div>
<div class="skeleton-block h-4 w-3/4"></div>
</div>
</div>
@endfor
</div>
<div class="empty-state mt-6">
<p class="text-sm">لم تُضف برامج تدريبية بعد</p>
</div>
@endif
</div>
</div>
@endif
......@@ -28,7 +28,7 @@
</svg>
</div>
<span class="stat-number" data-counter="{{ $stats[$key] }}" data-suffix="+">0</span>
<span class="block mt-2 text-white/50 text-sm">{{ $meta['label'] }}</span>
<span class="block mt-2 text-white/60 text-sm">{{ $meta['label'] }}</span>
</div>
@endif
@endforeach
......
......@@ -3,12 +3,12 @@
@endphp
@if($testimonials->count())
<div class="site-section" x-data="{ current: 0, total: {{ $testimonials->count() }} }" x-init="setInterval(() => current = (current + 1) % total, 5000)">
<div class="site-section site-section--alt" x-data="{ current: 0, total: {{ $testimonials->count() }} }" x-init="setInterval(() => current = (current + 1) % total, 5000)">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div class="text-center mb-12 reveal">
<h2 class="section-title mx-auto">{{ $section->title ?? 'آراء عملائنا' }}</h2>
@if($section->subtitle)
<p class="mt-4 text-white/60 text-lg">{{ $section->subtitle }}</p>
<p class="mt-4 text-gray-500 text-lg">{{ $section->subtitle }}</p>
@endif
</div>
......@@ -22,7 +22,7 @@
x-transition:leave="transition ease-in duration-300"
x-transition:leave-start="opacity-100"
x-transition:leave-end="opacity-0">
<p class="text-white/80 text-lg leading-relaxed italic relative z-10">
<p class="text-gray-600 text-lg leading-relaxed italic relative z-10">
{{ $testimonial->content }}
</p>
......@@ -32,21 +32,20 @@
alt="{{ $testimonial->name }}"
class="w-12 h-12 rounded-full object-cover border-2 border-[var(--site-accent)]">
@else
<div class="w-12 h-12 rounded-full bg-[var(--site-accent)]/20 flex items-center justify-center">
<div class="w-12 h-12 rounded-full bg-[var(--site-accent)]/10 flex items-center justify-center">
<span class="text-[var(--site-accent)] font-bold">{{ mb_substr($testimonial->name, 0, 1) }}</span>
</div>
@endif
<div>
<span class="block text-white font-medium">{{ $testimonial->name }}</span>
<span class="block text-gray-900 font-medium">{{ $testimonial->name }}</span>
@if($testimonial->role)
<span class="block text-white/40 text-sm">{{ $testimonial->role }}</span>
<span class="block text-gray-400 text-sm">{{ $testimonial->role }}</span>
@endif
</div>
{{-- Stars --}}
<div class="ms-auto flex gap-0.5">
@for($i = 1; $i <= 5; $i++)
<svg class="w-4 h-4 {{ $i <= $testimonial->rating ? 'text-amber-400' : 'text-white/20' }}" fill="currentColor" viewBox="0 0 20 20">
<svg class="w-4 h-4 {{ $i <= $testimonial->rating ? 'text-amber-400' : 'text-gray-200' }}" fill="currentColor" viewBox="0 0 20 20">
<path d="M9.049 2.927c.3-.921 1.603-.921 1.902 0l1.07 3.292a1 1 0 00.95.69h3.462c.969 0 1.371 1.24.588 1.81l-2.8 2.034a1 1 0 00-.364 1.118l1.07 3.292c.3.921-.755 1.688-1.54 1.118l-2.8-2.034a1 1 0 00-1.175 0l-2.8 2.034c-.784.57-1.838-.197-1.539-1.118l1.07-3.292a1 1 0 00-.364-1.118L2.98 8.72c-.783-.57-.38-1.81.588-1.81h3.461a1 1 0 00.951-.69l1.07-3.292z"/>
</svg>
@endfor
......@@ -55,12 +54,11 @@ class="w-12 h-12 rounded-full object-cover border-2 border-[var(--site-accent)]"
</div>
@endforeach
{{-- Dots Navigation --}}
<div class="flex items-center justify-center gap-2 mt-8">
@foreach($testimonials as $testimonial)
<button @click="current = {{ $loop->index }}"
class="w-2.5 h-2.5 rounded-full transition-all duration-300"
:class="current === {{ $loop->index }} ? 'bg-[var(--site-accent)] w-6' : 'bg-white/20 hover:bg-white/40'"
:class="current === {{ $loop->index }} ? 'bg-[var(--site-accent)] w-6' : 'bg-gray-300 hover:bg-gray-400'"
aria-label="شهادة {{ $loop->iteration }}">
</button>
@endforeach
......@@ -68,4 +66,28 @@ class="w-2.5 h-2.5 rounded-full transition-all duration-300"
</div>
</div>
</div>
@else
<div class="site-section site-section--alt">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div class="text-center mb-12">
<h2 class="section-title mx-auto">{{ $section->title ?? 'آراء عملائنا' }}</h2>
</div>
<div class="max-w-3xl mx-auto">
<div class="skeleton-card p-8 space-y-4">
<div class="skeleton-block h-4 w-full"></div>
<div class="skeleton-block h-4 w-3/4"></div>
<div class="flex items-center gap-3 mt-6">
<div class="skeleton-block w-12 h-12 !rounded-full"></div>
<div class="space-y-2">
<div class="skeleton-block h-4 w-24"></div>
<div class="skeleton-block h-3 w-16"></div>
</div>
</div>
</div>
</div>
<div class="empty-state mt-4">
<p class="text-sm">لم تُضف آراء عملاء بعد</p>
</div>
</div>
</div>
@endif
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment