# Public Website Builder — Academy Advertising Sites

## Vision

Every academy that signs up gets a **stunning, data-driven public website** — zero technical knowledge required. The system's home screen is NOT a login page. It's a professional advertising website that pulls real data from the management system and combines it with custom CMS content.

**URL Flow:**
- `academy-slug.caprover.al-arcade.com` → Public website (visitors)
- `academy-slug.caprover.al-arcade.com/manage` → Login → Dashboard (staff)

---

## Architecture Overview

```
┌─────────────────────────────────────────────────────┐
│                  PUBLIC WEBSITE                       │
│  (Blade views, no auth, cached, SEO-optimized)      │
├─────────────────────────────────────────────────────┤
│  Data Sources:                                       │
│  ├── CMS Content (website_sections table)           │
│  ├── Media Library (media table + local/S3 storage) │
│  ├── Live Data (programs, branches, activities)     │
│  └── Theme Settings (website_settings table)        │
├─────────────────────────────────────────────────────┤
│              MANAGEMENT CMS PANEL                     │
│  (Livewire components inside dashboard)             │
│  ├── Website Editor (sections, text, photos)        │
│  ├── Media Manager (upload + crop + preview)        │
│  ├── Theme Picker (colors, fonts, layout)           │
│  └── Preview Mode (live preview before publish)     │
└─────────────────────────────────────────────────────┘
```

---

## Design Decisions

| Decision | Choice | Reason |
|---|---|---|
| Template Style | **Bold & Athletic** | Dark, diagonal, dynamic — matches sports energy |
| CMS Editor | **Hybrid: Forms + Live Preview** | Reliable form editing with real-time preview iframe |
| Live Data | **Moderate** | Programs + branches live; prices/schedules CMS-managed |
| Routing | **Same app, separate route group** | `/site/{slug}` with full-page cache middleware |
| Image Processing | Intervention Image v3 | Resize, crop, WebP conversion |
| Caching | Full-page cache (Laravel Response Cache) | Sub-50ms page loads |
| Animations | Alpine.js + Intersection Observer | Count-up, fade-in, slide-up on scroll |
| Map | Leaflet.js (OpenStreetMap) | Free, no API key |
| SEO | Spatie Laravel SEO + JSON-LD | LocalBusiness schema per academy |

---

## Media Library System

### Upload Specifications

| Upload Target | Dimensions | Aspect Ratio | Max Size | Collection Key | Usage |
|---|---|---|---|---|---|
| Academy Logo | 400×400px | 1:1 | 500KB | `academy_logo` | Header, footer, favicon |
| Academy Cover | 1920×600px | 16:5 | 2MB | `academy_cover` | Hero section |
| Activity Photo | 800×600px | 4:3 | 1MB | `activity_photo` | Activity cards |
| Branch Photo | 1200×800px | 3:2 | 1.5MB | `branch_photo` | Branch section |
| Gallery Photos | 1200×800px | 3:2 | 1.5MB | `gallery` | Gallery grid |
| Team/Coach Photo | 600×600px | 1:1 | 500KB | `team_photo` | Team section circles |
| Testimonial Avatar | 200×200px | 1:1 | 200KB | `testimonial_avatar` | Testimonials |
| Partner/Sponsor Logo | 300×120px | 5:2 | 300KB | `partner_logo` | Partners bar |

### Upload UX Requirements

1. **Pre-upload**: Show exact dimensions as a dotted frame with measurements overlaid
2. **During upload**: Progress bar with percentage
3. **Post-upload**: Crop tool with LOCKED aspect ratio + zoom slider
4. **Quality indicator**:
   - Green: Resolution meets or exceeds target
   - Yellow: 75-99% of target (acceptable)
   - Red: Below 75% (too small, warn user)
5. **Context preview**: "See how this looks on your website" button opens section preview
6. **Accepted formats**: JPEG, PNG, WebP — auto-convert to WebP for serving
7. **Dimension guide text**: Always visible below uploader: "Required: 1920×600px (16:5) • Max 2MB • JPG/PNG/WebP"

### Upload Component Wireframe

```
┌─────────────────────────────────────────────────────────────────┐
│  Upload Academy Cover Photo                                      │
├─────────────────────────────────────────────────────────────────┤
│                                                                   │
│  ┌───────────────────────────────────────────────┐              │
│  │                                               │              │
│  │         ┌─────────────────────┐               │              │
│  │         │   1920 × 600 px     │               │              │
│  │         │   ━━━━━━━━━━━━━━━   │               │              │
│  │         │   16:5 aspect ratio │               │              │
│  │         │                     │               │              │
│  │         │   Click or drag     │               │              │
│  │         │   to upload         │               │              │
│  │         └─────────────────────┘               │              │
│  │                                               │              │
│  └───────────────────────────────────────────────┘              │
│                                                                   │
│  Required: 1920×600px (16:5) • Max 2MB • JPG/PNG/WebP           │
│  Tip: Use a high-contrast image — text overlay will be added     │
│                                                                   │
│  ┌──────────────────────────────────────────────┐               │
│  │  After upload: CROP TOOL appears here         │               │
│  │  • Locked aspect ratio                        │               │
│  │  • Zoom slider                                │               │
│  │  • Quality indicator (Green: Excellent)       │               │
│  │  • "Preview on website" button                │               │
│  └──────────────────────────────────────────────┘               │
│                                                                   │
│         [Cancel]                    [Save & Apply]               │
└─────────────────────────────────────────────────────────────────┘
```

---

## CMS Sections

### Available Sections (16 total)

| Section Key | Name (AR) | Content Type | Data Source |
|---|---|---|---|
| `hero` | البانر الرئيسي | Cover image + headline + CTA | CMS only |
| `about` | من نحن | Rich text + optional image | CMS only |
| `activities` | الأنشطة | Cards grid | **Live DB** + photos |
| `programs` | البرامج | Cards/list | **Live DB** + CMS descriptions |
| `branches` | الفروع | Map + cards | **Live DB** + photos |
| `schedule` | المواعيد | Timetable/calendar | CMS-managed |
| `trainers` | المدربون | Team grid | CMS-managed |
| `gallery` | معرض الصور | Masonry/grid | CMS (uploaded photos) |
| `testimonials` | آراء العملاء | Carousel | CMS table |
| `pricing` | الأسعار | Pricing cards | CMS-managed |
| `partners` | شركاؤنا | Logo bar | CMS table |
| `contact` | تواصل معنا | Form + map + info | CMS + branch data |
| `faq` | الأسئلة الشائعة | Accordion | CMS table |
| `cta` | سجل الآن | Call-to-action banner | CMS only |
| `stats` | إنجازاتنا | Counter animations | **Live DB** (computed counts) |
| `news` | الأخبار | Blog/posts grid | CMS table |

### Live Data vs CMS Data

| What Visitors See | Source | Cache Duration |
|---|---|---|
| Academy name, logo | `organizations` table | 1 hour |
| Activity names + photos | `activities` + `media` | 1 hour |
| Program names, age ranges | `training_programs` | 1 hour |
| Program descriptions for website | `website_sections` CMS | Invalidated on save |
| Branch names, addresses, phones | `branches` | 1 hour |
| Branch photos, descriptions | `media` + CMS | Invalidated on save |
| Schedules | CMS-managed (not live) | Invalidated on save |
| Prices | CMS-managed (not live) | Invalidated on save |
| Trainer profiles | CMS-managed | Invalidated on save |
| Testimonials, FAQ, News | CMS tables | Invalidated on save |
| Stats (counts) | Computed + cached | 6 hours |

---

## CMS Editor — Hybrid Forms + Live Preview

### Editor Layout Wireframe

```
┌──────────────────────────────────────────────────────────────────────────┐
│  Website Editor                                 [Preview] [Publish]       │
├────────────────────────────┬─────────────────────────────────────────────┤
│  SECTIONS                  │                                              │
│  ─────────────────         │   ┌─────────────────────────────────────┐   │
│  ☰ البانر الرئيسي    ✓    │   │                                     │   │
│  ☰ من نحن           ✓    │   │       LIVE PREVIEW IFRAME            │   │
│  ☰ الأنشطة          ✓    │   │                                     │   │
│  ☰ البرامج          ✓    │   │   Updates in real-time as you        │   │
│  ☰ الفروع           ✓    │   │   modify form fields on the left     │   │
│  ☰ المدربون         ✗    │   │                                     │   │
│  ☰ معرض الصور       ✓    │   │   Highlighted section indicator      │   │
│  ☰ آراء العملاء      ✓    │   │   shows which section you're         │   │
│  ☰ الأسعار          ✗    │   │   currently editing                   │   │
│  ☰ شركاؤنا          ✓    │   │                                     │   │
│  ☰ تواصل معنا       ✓    │   │                                     │   │
│  ☰ الأسئلة الشائعة  ✗    │   │                                     │   │
│                            │   └─────────────────────────────────────┘   │
│  [+ Add Section]           │                                              │
├────────────────────────────┤   Desktop    Tablet    Mobile               │
│  EDITING: البانر الرئيسي   │                                              │
│  ─────────────────         │                                              │
│  Title (AR): [أكاديمية..]  │                                              │
│  Title (EN): [Academy...]  │                                              │
│  Subtitle:   [انضم إلين.] │                                              │
│  CTA Text:   [سجل الآن]   │                                              │
│  CTA Link:   [/register]  │                                              │
│  Cover Photo: [Change]     │                                              │
│  Overlay: [Dark v] [70%]  │                                              │
│                            │                                              │
│  [Save Section]            │                                              │
└────────────────────────────┴─────────────────────────────────────────────┘
```

### Editor Behaviors

- **Draggable sections**: Reorder via drag handle (☰)
- **Toggle enable/disable**: Click ✓/✗ to show/hide section on public site
- **Section-specific forms**: Each section has its own form fields (see below)
- **Live preview**: iframe loads `/manage/website/preview` route (authenticated, no cache)
- **Debounced updates**: Preview refreshes 500ms after last keystroke
- **Device toggle**: Switch preview between desktop (100%), tablet (768px), mobile (375px)
- **Publish flow**: Explicit button — site stays in draft until published

### Section Form Fields

#### Hero (البانر الرئيسي)
- Title AR (required)
- Title EN (optional)
- Subtitle AR
- Subtitle EN
- CTA button text AR
- CTA button text EN
- CTA link URL
- Cover photo upload (1920×600)
- Overlay color (dark/light/accent)
- Overlay opacity (0-100% slider)

#### About (من نحن)
- Heading AR / EN
- Body text AR (rich text — bold, italic, lists)
- Body text EN
- Side image upload (800×600)
- Image position (start/end)

#### Activities (الأنشطة)
- Section heading AR / EN
- Max items to display (number)
- Display style: grid / carousel
- Per activity (from live data): can override photo, can add description
- Show/hide specific activities toggle

#### Programs (البرامج)
- Section heading AR / EN
- Display style: cards / list / alternating
- Per program (from live data): featured description AR/EN, featured image
- Show/hide specific programs toggle
- Show age range: yes/no
- Show "spots available": yes/no

#### Branches (الفروع)
- Section heading AR / EN
- Show map: yes/no
- Map center coordinates (auto from branches)
- Per branch (from live data): description AR/EN, photo, display order
- Show phone number: yes/no
- Show working hours: yes/no

#### Trainers (المدربون)
- Section heading AR / EN
- Display style: grid / carousel
- Items: manual list of trainers (name, role, bio, photo)
- Columns per row: 3 / 4

#### Gallery (معرض الصور)
- Section heading AR / EN
- Layout: masonry / grid / carousel
- Columns: 2 / 3 / 4
- Photos: bulk upload with sort order
- Lightbox: yes/no

#### Testimonials (آراء العملاء)
- Section heading AR / EN
- Display: carousel / grid
- Auto-play speed (seconds)
- Items from `website_testimonials` table

#### Pricing (الأسعار)
- Section heading AR / EN
- Display style: cards / table
- Items: manual pricing tiers (name, price, features list, highlighted: yes/no)
- CTA text per tier

#### Stats (إنجازاتنا)
- Section heading AR / EN
- Stats to show (checkboxes): participants, trainers, branches, years, programs, graduates
- Custom stats: label + number (up to 6)
- Animation: count-up / fade-in / none

#### Contact (تواصل معنا)
- Section heading AR / EN
- Show form: yes/no
- Form fields: name, phone, email, message (all required)
- Show map: yes/no
- Show branch addresses: yes/no
- Show social links: yes/no

#### FAQ (الأسئلة الشائعة)
- Section heading AR / EN
- Items from `website_faqs` table (question/answer pairs)
- Display: accordion (single open) / accordion (multi open)

#### Partners (شركاؤنا)
- Section heading AR / EN
- Display: logo bar (scrolling) / grid
- Items from `website_partners` table

#### CTA (سجل الآن)
- Heading AR / EN
- Subheading AR / EN
- Button text AR / EN
- Button link
- Background: solid color / gradient / image
- Background image upload (1920×400)

#### News (الأخبار)
- Section heading AR / EN
- Max items to display
- Show date: yes/no
- Show excerpt: yes/no
- Items from `website_news` table

---

## Website Settings (Theme Configuration)

```
website_settings (per academy):
├── Colors
│   ├── primary_color (hex) — default #1a1a2e
│   ├── secondary_color (hex) — default #16213e
│   ├── accent_color (hex) — default #e94560
│   └── text_color (hex) — default #ffffff
├── Typography
│   ├── heading_font — from curated Arabic-safe list:
│   │   Cairo, Tajawal, IBM Plex Sans Arabic, Noto Sans Arabic,
│   │   Almarai, Readex Pro, Rubik, El Messiri
│   └── body_font — same list
├── Layout
│   ├── sections_order (JSON array of section keys)
│   ├── sections_enabled (JSON object: key → boolean)
│   └── navbar_style ('solid' | 'transparent' | 'floating')
├── Content
│   ├── site_title (AR)
│   ├── site_title_en
│   ├── site_description (AR, for meta tag)
│   ├── site_description_en
│   ├── social_links (JSONB):
│   │   facebook, instagram, twitter, youtube, tiktok, linkedin
│   └── whatsapp_number (with country code)
└── Advanced
    ├── custom_css (optional, textarea)
    ├── google_analytics_id
    └── facebook_pixel_id
```

---

## Bold & Athletic Template — Visual Design Spec

### Design Language

- **Background**: Dark (#1a1a2e to #16213e gradient)
- **Accent**: Vibrant pop color (default #e94560, customizable)
- **Typography**: Bold, uppercase headings with letter-spacing
- **Geometry**: Diagonal clip-paths, skewed section dividers
- **Cards**: Dark surface with accent border on hover, image zoom effect
- **Buttons**: Solid accent with glow/scale on hover
- **Spacing**: Generous whitespace between sections (py-20 to py-32)

### Section-by-Section Design

#### Hero
- Full viewport height (100vh desktop, 60vh mobile)
- Cover image with `clip-path: polygon(0 0, 100% 0, 100% 85%, 0 100%)` diagonal bottom
- Dark gradient overlay (70% opacity default)
- Heading: 4xl-6xl, Cairo bold, text-shadow
- Animated accent underline (width animates in on load)
- CTA button: accent bg, rounded-lg, hover:scale-105 + box-shadow glow
- Floating stats bar at bottom: semi-transparent dark bg, 3-4 stats with counters

#### Activities
- Dark section background
- Subtle diagonal line pattern (SVG bg, 5% opacity)
- Cards: 3-column grid (lg), 2-column (md), 1-column (sm)
- Card: image with `object-cover`, overlay gradient from bottom
- Hover: image scale(1.05), accent border-bottom appears
- Activity name: absolute bottom, white text on gradient

#### Programs
- Alternating left/right layout per program
- Large image one side, content the other
- Content: program name (heading), age range badge, description, "learn more" link
- Diagonal accent stripe between items (thin, decorative)
- Mobile: stacked, image on top

#### Branches
- Map section: full-width Leaflet map with custom markers (accent color)
- Below map: branch cards in a row
- Card: branch photo, name, address, phone, working hours
- Hover: card lifts (shadow increase) + accent top-border

#### Stats
- Full-width dark band with CSS diagonal accent lines (pseudo-elements)
- 4 stats in a row, large numbers (text-5xl)
- Numbers animate (count up) when scrolled into view (Intersection Observer)
- Label below each number in muted text
- Icon above each number in accent color

#### Trainers
- Circular photos with 3px accent color border
- Name below in white, role/specialization in muted text
- Hover: image slight scale + border thickens
- Grid: 4-column (lg), 3 (md), 2 (sm)

#### Testimonials
- Carousel with large quote marks (accent color, faded)
- Testimonial text centered, italicized
- Avatar (circle) + name + role below
- Navigation: dots or arrows in accent color
- Auto-play with pause on hover

#### Gallery
- Masonry grid layout (CSS columns or JS masonry)
- Hover: image darkens + "view" icon appears
- Click: lightbox with navigation
- Lazy loading for performance

#### Contact
- Split: form on one side, info + map on the other
- Form: dark input fields with accent focus ring
- Validation: inline, Arabic error messages
- Submit: accent button with loading state
- Success: animated checkmark + "سنتواصل معك قريبا"

#### Footer
- Dark background (darkest shade)
- Academy logo + brief description
- Quick links (sections)
- Social media icons (accent hover color)
- WhatsApp floating button (fixed, bottom-end corner)
- Copyright line

---

## Database Schema

### Migration 1: `media`

```php
Schema::create('media', function (Blueprint $table) {
    $table->id();
    $table->foreignId('academy_id')->constrained('organizations');
    $table->uuid('uuid')->unique();
    $table->string('collection', 50); // academy_logo, academy_cover, activity_photo, etc.
    $table->string('original_filename');
    $table->string('disk_path');
    $table->string('mime_type', 50);
    $table->unsignedInteger('width');
    $table->unsignedInteger('height');
    $table->unsignedInteger('file_size'); // bytes
    $table->string('alt_text')->nullable();
    $table->string('alt_text_ar')->nullable();
    $table->nullableMorphs('mediable'); // polymorphic: activity, branch, person, etc.
    $table->unsignedSmallInteger('sort_order')->default(0);
    $table->timestamps();
    $table->softDeletes();

    $table->index(['academy_id', 'collection']);
});
```

### Migration 2: `website_settings`

```php
Schema::create('website_settings', function (Blueprint $table) {
    $table->id();
    $table->foreignId('academy_id')->constrained('organizations')->unique();
    $table->string('template', 30)->default('bold_athletic');

    // Colors
    $table->string('primary_color', 7)->default('#1a1a2e');
    $table->string('secondary_color', 7)->default('#16213e');
    $table->string('accent_color', 7)->default('#e94560');
    $table->string('text_color', 7)->default('#ffffff');

    // Typography
    $table->string('heading_font', 50)->default('Cairo');
    $table->string('body_font', 50)->default('Cairo');

    // Layout
    $table->string('navbar_style', 20)->default('transparent');

    // Content
    $table->string('site_title')->nullable();
    $table->string('site_title_en')->nullable();
    $table->text('site_description')->nullable();
    $table->text('site_description_en')->nullable();
    $table->jsonb('social_links')->default('{}');
    $table->string('whatsapp_number', 20)->nullable();

    // Advanced
    $table->text('custom_css')->nullable();
    $table->string('google_analytics_id', 30)->nullable();
    $table->string('facebook_pixel_id', 30)->nullable();

    // Publish state
    $table->boolean('is_published')->default(false);
    $table->timestamp('published_at')->nullable();

    $table->timestamps();
});

DB::statement("ALTER TABLE website_settings ADD CONSTRAINT website_settings_template_check
    CHECK (template IN ('bold_athletic', 'clean_professional', 'vibrant_playful'))");

DB::statement("ALTER TABLE website_settings ADD CONSTRAINT website_settings_navbar_style_check
    CHECK (navbar_style IN ('solid', 'transparent', 'floating'))");
```

### Migration 3: `website_sections`

```php
Schema::create('website_sections', function (Blueprint $table) {
    $table->id();
    $table->foreignId('academy_id')->constrained('organizations');
    $table->string('section_key', 50);
    $table->string('title')->nullable();
    $table->string('title_en')->nullable();
    $table->string('subtitle')->nullable();
    $table->string('subtitle_en')->nullable();
    $table->text('content')->nullable(); // rich text body (AR)
    $table->text('content_en')->nullable();
    $table->jsonb('settings')->default('{}'); // section-specific config
    $table->unsignedSmallInteger('sort_order')->default(0);
    $table->boolean('is_enabled')->default(true);
    $table->timestamps();

    $table->unique(['academy_id', 'section_key']);
    $table->index(['academy_id', 'sort_order']);
});

DB::statement("ALTER TABLE website_sections ADD CONSTRAINT website_sections_section_key_check
    CHECK (section_key IN ('hero', 'about', 'activities', 'programs', 'branches',
    'schedule', 'trainers', 'gallery', 'testimonials', 'pricing', 'partners',
    'contact', 'faq', 'cta', 'stats', 'news'))");
```

### Migration 4: `website_testimonials`

```php
Schema::create('website_testimonials', function (Blueprint $table) {
    $table->id();
    $table->foreignId('academy_id')->constrained('organizations');
    $table->uuid('uuid')->unique();
    $table->string('name');
    $table->string('name_en')->nullable();
    $table->string('role')->nullable(); // e.g., "ولي أمر" or "لاعب"
    $table->string('role_en')->nullable();
    $table->text('content'); // AR testimonial text
    $table->text('content_en')->nullable();
    $table->unsignedTinyInteger('rating')->default(5); // 1-5
    $table->boolean('is_featured')->default(false);
    $table->unsignedSmallInteger('sort_order')->default(0);
    $table->timestamps();
    $table->softDeletes();

    $table->index(['academy_id', 'is_featured']);
});
```

### Migration 5: `website_faqs`

```php
Schema::create('website_faqs', function (Blueprint $table) {
    $table->id();
    $table->foreignId('academy_id')->constrained('organizations');
    $table->uuid('uuid')->unique();
    $table->string('question'); // AR
    $table->string('question_en')->nullable();
    $table->text('answer'); // AR
    $table->text('answer_en')->nullable();
    $table->unsignedSmallInteger('sort_order')->default(0);
    $table->boolean('is_published')->default(true);
    $table->timestamps();
    $table->softDeletes();

    $table->index(['academy_id', 'is_published']);
});
```

### Migration 6: `website_news`

```php
Schema::create('website_news', function (Blueprint $table) {
    $table->id();
    $table->foreignId('academy_id')->constrained('organizations');
    $table->uuid('uuid')->unique();
    $table->string('title'); // AR
    $table->string('title_en')->nullable();
    $table->string('slug');
    $table->text('body'); // AR, rich text
    $table->text('body_en')->nullable();
    $table->string('excerpt')->nullable(); // AR
    $table->string('excerpt_en')->nullable();
    $table->timestamp('published_at')->nullable();
    $table->boolean('is_featured')->default(false);
    $table->unsignedSmallInteger('sort_order')->default(0);
    $table->foreignId('created_by')->constrained('users');
    $table->timestamps();
    $table->softDeletes();

    $table->unique(['academy_id', 'slug']);
    $table->index(['academy_id', 'published_at']);
});
```

### Migration 7: `website_partners`

```php
Schema::create('website_partners', function (Blueprint $table) {
    $table->id();
    $table->foreignId('academy_id')->constrained('organizations');
    $table->uuid('uuid')->unique();
    $table->string('name'); // AR
    $table->string('name_en')->nullable();
    $table->string('website_url')->nullable();
    $table->unsignedSmallInteger('sort_order')->default(0);
    $table->boolean('is_active')->default(true);
    $table->timestamps();
    $table->softDeletes();

    $table->index(['academy_id', 'is_active']);
});
```

### Migration 8: `contact_submissions`

```php
Schema::create('contact_submissions', function (Blueprint $table) {
    $table->id();
    $table->foreignId('academy_id')->constrained('organizations');
    $table->uuid('uuid')->unique();
    $table->string('name');
    $table->string('phone', 20);
    $table->string('email')->nullable();
    $table->text('message');
    $table->string('status', 20)->default('new'); // new, read, replied, archived
    $table->text('admin_notes')->nullable();
    $table->foreignId('replied_by')->nullable()->constrained('users');
    $table->timestamp('read_at')->nullable();
    $table->timestamp('replied_at')->nullable();
    $table->timestamps();

    $table->index(['academy_id', 'status']);
    $table->index(['academy_id', 'created_at']);
});

DB::statement("ALTER TABLE contact_submissions ADD CONSTRAINT contact_submissions_status_check
    CHECK (status IN ('new', 'read', 'replied', 'archived'))");
```

---

## File Structure

```
app/Domain/Website/
├── Models/
│   ├── WebsiteSetting.php
│   ├── WebsiteSection.php
│   ├── WebsiteTestimonial.php
│   ├── WebsiteFaq.php
│   ├── WebsiteNews.php
│   ├── WebsitePartner.php
│   └── ContactSubmission.php
├── Services/
│   ├── WebsiteSettingService.php
│   ├── WebsiteSectionService.php
│   ├── WebsiteDataService.php       (aggregates live data for public site)
│   └── WebsiteCacheService.php      (tag-based cache invalidation)
├── Enums/
│   ├── SectionKey.php
│   ├── NavbarStyle.php
│   ├── TemplateStyle.php
│   └── ContactSubmissionStatus.php
└── Events/
    ├── WebsitePublished.php
    └── ContactSubmitted.php

app/Domain/Shared/
├── Models/Media.php
└── Services/MediaService.php

app/Http/Controllers/
├── PublicWebsiteController.php      (renders public site — single controller)
└── ContactFormController.php        (handles contact form submission)

app/Http/Middleware/
├── ResolveAcademyFromSlug.php       (sets current_academy from URL slug)
└── CachePublicPage.php              (full-page cache with tag invalidation)

app/Livewire/Website/
├── ThemeEditor.php                  (colors, fonts, navbar style)
├── SectionManager.php               (list, reorder, enable/disable sections)
├── SectionEditor.php                (form fields for active section)
├── MediaUploader.php                (reusable — crop, resize, quality check)
├── MediaGalleryManager.php          (bulk upload + sort for gallery section)
├── TestimonialManager.php           (CRUD for testimonials)
├── FaqManager.php                   (CRUD for FAQs)
├── NewsManager.php                  (CRUD for news posts)
├── PartnerManager.php               (CRUD for partners)
├── ContactSubmissionList.php        (view + manage contact form submissions)
└── WebsitePreview.php               (preview iframe controller)

resources/views/
├── website/
│   ├── layout.blade.php             (public site master — no auth, dark theme)
│   ├── index.blade.php              (loops through enabled sections)
│   ├── navbar.blade.php             (transparent/solid/floating)
│   ├── footer.blade.php             (logo, links, social, copyright)
│   └── sections/
│       ├── hero.blade.php
│       ├── about.blade.php
│       ├── activities.blade.php
│       ├── programs.blade.php
│       ├── branches.blade.php
│       ├── schedule.blade.php
│       ├── trainers.blade.php
│       ├── gallery.blade.php
│       ├── testimonials.blade.php
│       ├── pricing.blade.php
│       ├── partners.blade.php
│       ├── contact.blade.php
│       ├── faq.blade.php
│       ├── cta.blade.php
│       ├── stats.blade.php
│       └── news.blade.php
└── livewire/website/
    ├── theme-editor.blade.php
    ├── section-manager.blade.php
    ├── section-editor.blade.php
    ├── media-uploader.blade.php
    ├── testimonial-manager.blade.php
    ├── faq-manager.blade.php
    ├── news-manager.blade.php
    ├── partner-manager.blade.php
    └── contact-submission-list.blade.php
```

---

## Build Order — 12 Vertical Slices

### Slice 1: Media Library Foundation
- Migration: `media` table
- Model: `Media` with `BelongsToAcademy`, `HasUuid`, `SoftDeletes`
- Service: `MediaService`
  - `upload(UploadedFile, collection, mediable?)` — validate, resize, convert WebP, store
  - `crop(Media, x, y, width, height)` — re-crop existing image
  - `delete(Media)` — soft delete + optionally remove from disk
  - `getCollectionSpec(collection)` — returns dimensions, aspect ratio, max size
- Livewire: `MediaUploader` component
  - Props: `collection`, `mediableType`, `mediableId`, `multiple`
  - Shows dimension guide, handles upload, crop tool, quality indicator
  - Emits: `media-uploaded` with media ID
- Storage config: `disks.media` in `filesystems.php`
- **Test in browser**: upload an image, see crop tool, verify WebP output

### Slice 2: Website Settings & Theme
- Migration: `website_settings`
- Model: `WebsiteSetting` with `BelongsToAcademy`
- Service: `WebsiteSettingService`
  - `getOrCreate(academy)` — returns settings, creates defaults if none
  - `update(academy, data)` — validates + saves + invalidates cache
  - `publish(academy)` — sets is_published, fires WebsitePublished event
- Livewire: `ThemeEditor`
  - Color pickers (accent, primary, secondary)
  - Font dropdowns (curated Arabic-safe list)
  - Navbar style selector
  - Social links inputs
  - Live preview updates via Alpine
- **Test in browser**: change colors, see preview update

### Slice 3: Website Sections Framework
- Migration: `website_sections`
- Model: `WebsiteSection` with `BelongsToAcademy`
- Service: `WebsiteSectionService`
  - `seedDefaults(academy)` — creates all 16 sections with default sort order
  - `reorder(academy, orderedKeys[])` — bulk update sort_order
  - `toggle(section, enabled)` — enable/disable
  - `updateContent(section, data)` — save title, subtitle, content, settings
- Livewire: `SectionManager` (draggable list with enable/disable toggles)
- Livewire: `SectionEditor` (dynamic form based on section_key)
- Seeder: auto-seeds sections when new academy is created
- **Test in browser**: reorder sections, toggle on/off, edit content

### Slice 4: Public Site Layout + Hero Section
- Route: `GET /site/{slug}` → `PublicWebsiteController@show`
- Middleware: `ResolveAcademyFromSlug`
- Layout: `website/layout.blade.php` — dark theme, no auth, loads fonts + Alpine
- Navbar: transparent with scroll-to-solid transition
- Hero section Blade view with diagonal clip-path, overlay, CTA
- CSS: custom properties from `website_settings` (injected as inline `<style>`)
- **Test in browser**: visit `/site/test-academy`, see styled hero

### Slice 5: Activities + Programs Sections (Live Data)
- `WebsiteDataService::getActivities(academy)` — cached query
- `WebsiteDataService::getPrograms(academy)` — cached query
- Activities Blade: grid cards with hover zoom + overlay
- Programs Blade: alternating image/content layout
- CMS forms: per-section settings (display style, max items, show/hide toggles)
- Photo linking: `MediaUploader` in activity/program forms
- **Test in browser**: see live activities from DB rendered in bold template

### Slice 6: Branches Section (Live Data + Map)
- `WebsiteDataService::getBranches(academy)` — cached query
- Branches Blade: Leaflet map + branch cards below
- Map: custom markers in accent color, popup with branch info
- Branch photos via `media` polymorphic relation
- CMS form: per-branch descriptions, show/hide phone, working hours
- **Test in browser**: see map with markers, branch cards

### Slice 7: Testimonials, FAQ, Partners (CMS Tables)
- Migrations: `website_testimonials`, `website_faqs`, `website_partners`
- Models with `BelongsToAcademy`, `HasUuid`, `SoftDeletes`
- Livewire CRUD components for each (create, edit, delete, reorder)
- Public Blade views:
  - Testimonials: carousel with large quotes, avatar, rating stars
  - FAQ: accordion with smooth expand/collapse (Alpine `x-collapse`)
  - Partners: scrolling logo bar
- **Test in browser**: add testimonials from CMS, see on public site

### Slice 8: Gallery + News (CMS Content)
- Migration: `website_news`
- Gallery: bulk `MediaUploader` for collection='gallery' + drag-to-sort
- Gallery Blade: masonry grid, lightbox on click (Alpine)
- News: CRUD component with title, body (simple textarea), excerpt, publish date
- News Blade: cards grid with featured image, date, excerpt
- **Test in browser**: upload gallery photos, create news post, verify public views

### Slice 9: Stats + Contact Sections
- Stats: `WebsiteDataService::getStats(academy)` — counts participants, trainers, branches, programs
- Stats Blade: animated counter (Alpine + Intersection Observer)
- Migration: `contact_submissions`
- Model: `ContactSubmission`
- Contact Blade: form with validation + map + social links
- `ContactFormController`: validates, stores, dispatches `ContactSubmitted` event
- Livewire: `ContactSubmissionList` — view/manage submissions in dashboard
- **Test in browser**: submit contact form, see in dashboard

### Slice 10: Remaining Sections (About, Schedule, Trainers, Pricing, CTA)
- About Blade: rich text + image (content from `website_sections.content`)
- Schedule Blade: simple timetable from CMS settings JSON
- Trainers Blade: team grid with circular photos
- Pricing Blade: cards with feature lists, highlighted plan
- CTA Blade: full-width banner with gradient/image bg, large heading + button
- Each gets its section-specific form in `SectionEditor`
- **Test in browser**: enable each section, fill content, verify

### Slice 11: Live Preview System
- Route: `GET /manage/website/preview` (auth required, no cache)
- Renders same template as public but always fresh (no cache)
- `WebsitePreview` Livewire component: manages iframe
- Section highlight: when editing section X, preview scrolls to it + adds outline
- Device toggle: wraps iframe in width constraints (100%, 768px, 375px)
- Debounced refresh: `wire:model.live.debounce.500ms` triggers preview reload
- **Test in browser**: edit fields, see changes in preview iframe

### Slice 12: Publishing + Cache + SEO
- Publish button: sets `is_published = true` + `published_at`
- Cache middleware: full-page cache on public routes, tagged per academy
- `WebsiteCacheService::invalidate(academy)` — called on any CMS save
- SEO: `<title>`, `<meta description>`, Open Graph tags, JSON-LD LocalBusiness
- Sitemap: auto-generated for published academies
- WhatsApp floating button: fixed position, links to academy's WhatsApp
- Final route wiring: root `/` redirects to `/site/{default-slug}` (not login)
- **Test in browser**: publish, verify cache headers, check SEO meta tags

---

## Routing Configuration

```php
// routes/web.php

// Public website (no auth, cached)
Route::middleware(['resolve.academy.slug', 'cache.public.page'])
    ->prefix('site/{slug}')
    ->group(function () {
        Route::get('/', [PublicWebsiteController::class, 'show'])->name('website.show');
        Route::get('/news/{newsSlug}', [PublicWebsiteController::class, 'newsArticle'])->name('website.news.show');
        Route::post('/contact', [ContactFormController::class, 'submit'])->name('website.contact.submit');
    });

// CMS management (inside existing auth dashboard routes)
Route::middleware(['auth', 'permission:website.manage'])
    ->prefix('manage/website')
    ->group(function () {
        Route::get('/', WebsiteSectionManager::class)->name('manage.website');
        Route::get('/theme', WebsiteThemeEditor::class)->name('manage.website.theme');
        Route::get('/preview', [PublicWebsiteController::class, 'preview'])->name('manage.website.preview');
        Route::get('/testimonials', WebsiteTestimonialManager::class)->name('manage.website.testimonials');
        Route::get('/faqs', WebsiteFaqManager::class)->name('manage.website.faqs');
        Route::get('/news', WebsiteNewsManager::class)->name('manage.website.news');
        Route::get('/partners', WebsitePartnerManager::class)->name('manage.website.partners');
        Route::get('/gallery', WebsiteMediaGalleryManager::class)->name('manage.website.gallery');
        Route::get('/contacts', WebsiteContactSubmissionList::class)->name('manage.website.contacts');
    });

// Root redirect (homepage is the public site, not login)
Route::get('/', function () {
    // Resolve academy from domain/config and redirect to public site
    return redirect()->route('website.show', ['slug' => config('app.default_academy_slug')]);
});
```

---

## Permissions

| Permission Key | Description | Roles |
|---|---|---|
| `website.manage` | Access website CMS panel | academy_owner, academy_admin |
| `website.publish` | Publish/unpublish the website | academy_owner, academy_admin |
| `website.theme` | Change colors, fonts, template | academy_owner |
| `website.contacts` | View contact form submissions | academy_owner, academy_admin, receptionist |

---

## Cache Strategy

| What | TTL | Invalidation |
|---|---|---|
| Full public page | 1 hour | On any CMS save for that academy |
| Live data (programs, branches, activities) | 1 hour | On model update (observer) |
| Stats (counts) | 6 hours | Scheduled job recalculates |
| Media URLs | 24 hours | On media delete/replace |
| Preview page | Never cached | Always fresh for editors |

**Implementation:**
- Cache tags: `['website', 'academy:{id}']`
- `WebsiteCacheService::invalidate($academyId)` — flushes all tagged cache
- Called from: section save, theme save, media upload, publish toggle
- Model observers on `Activity`, `TrainingProgram`, `Branch` — invalidate website cache on change

---

## SEO Implementation

### Meta Tags (in `website/layout.blade.php`)
```html
<title>{{ $settings->site_title }} — {{ $academy->name_ar }}</title>
<meta name="description" content="{{ $settings->site_description }}">
<meta property="og:title" content="{{ $settings->site_title }}">
<meta property="og:description" content="{{ $settings->site_description }}">
<meta property="og:image" content="{{ $academy->cover_photo_url }}">
<meta property="og:type" content="website">
<link rel="canonical" href="{{ route('website.show', $academy->slug) }}">
```

### JSON-LD Structured Data
```json
{
  "@context": "https://schema.org",
  "@type": "SportsActivityLocation",
  "name": "{{ academy name }}",
  "description": "{{ site description }}",
  "address": {
    "@type": "PostalAddress",
    "addressLocality": "{{ branch city }}",
    "addressCountry": "EG"
  },
  "telephone": "{{ branch phone }}",
  "image": "{{ cover photo }}",
  "sameAs": ["{{ social links }}"]
}
```

---

## Performance Budget

| Metric | Target | How |
|---|---|---|
| First Contentful Paint | < 1.5s | Full-page cache, optimized images |
| Largest Contentful Paint | < 2.5s | WebP images, lazy loading below fold |
| Total page size | < 2MB | WebP compression, font subsetting |
| Lighthouse score | > 90 | Semantic HTML, accessibility, meta tags |
| Time to Interactive | < 3s | Minimal JS (Alpine only), no heavy frameworks |

---

## Dependencies to Install

```bash
composer require intervention/image:^3.0      # Image processing
composer require spatie/laravel-responsecache  # Full-page caching
# OR custom cache middleware (simpler)

npm install cropperjs                          # Client-side crop tool
npm install leaflet                            # Maps (if not using CDN)
```

---

## Default Section Order (seeded for new academies)

1. hero
2. about
3. activities
4. programs
5. branches
6. stats
7. trainers
8. gallery
9. testimonials
10. pricing
11. partners
12. faq
13. news
14. contact
15. cta
16. schedule (disabled by default)

---

## Future Enhancements (NOT in initial build)

- Multiple template styles (Clean & Professional, Vibrant & Playful)
- Custom domain support (academy.com → their public site)
- Blog with full CMS (categories, tags, rich editor)
- Online registration form (pre-enrollment from public site)
- Event calendar integration
- Video sections (YouTube/Vimeo embed)
- Multilingual URL slugs
- A/B testing for CTA variations
- Analytics dashboard (page views, popular sections, form conversion rate)
