# OC-Sport site — mirror & gap analysis
Captured 2026-08-30 from https://oc-sport.com

## What the mirror contains
| Path | Contents |
|---|---|
| `html/` | 14 SSR documents (7 pages × en/ar) |
| `assets/` | 44 JS chunks, 4 CSS, 3 woff2 fonts |
| `images/` | 14 public webp + favicon |
| `images/schedules/` | 34 session cards (1080×1080 PNG, ~40 MB) |
| `api/` | OpenAPI 3.0.3 spec (99 endpoints), branches, schedules |
| `analysis/messages.{en,ar}.json` | **604 strings × 2 locales, 35 namespaces** |

## Their stack
Next.js (App Router, RSC) + Mantine + Tailwind, MongoDB behind a REST API,
S3/MinIO media via presigned URLs, Cloudflare. Font: **Cairo**. Locales en/ar,
`x-default` = en.

Brand ramp (from inlined Mantine theme):
- brand 0–9: `#f0f4ff … #4c94ff (6) … #0c1236 (9)`
- accent 0–9: `#fffdf0 … #ffe743 (6) … #c9a000 (9)`

Animations: `fadeIn slideUp scaleIn float glow shimmer bounceSubtle pageEnter pulse bounce`

## Their real page set (7)
`/` · `/about-us` · `/contact-us` · `/el3b` · `/login` · `/verify` · `/terms-and-conditions`
Nav also lists 5 sport pages (football, basketball, handball, gymnastics,
women's football) + `/branch/{slug}` + Ramadan events page.

## Homepage blocks
Hero (5-colour jersey, front/back) · AboutUs · Chairman (profile + achievements list)
· Video (YouTube + quote) · Branches (searchable, data-driven) · Partners (logo strip)
· CTR (final CTA) · ContactUs (form + 3 info cards)
> Render order not visually confirmed — headless browser unavailable in this container.

## The two things that make their site "not shallow"
1. **`BranchPage`** (116 strings): multi-player enrolment wizard — guardian choice,
   registration type, add/remove N players, per-player kit size/number/name-on-shirt,
   school, medical notes, parents' jobs, compound-resident question, addons —
   with a **live discount breakdown** (multi-player, compound-resident, club-member,
   addon, subscription) and OTP phone auth → order → payment.
2. **Per-record media**: every session carries an uploaded 1080×1080 card;
   every branch, partner, activity carries images.

## Our builder — what exists
- 19 `SectionKey` cases, 6 hero layout variants
- 61 columns on `website_settings` (colour, type, density, radius, card style,
  navbar ×8, footer ×4, announcement bar, popup, floating elements)
- 13 `MediaCollection` types incl. `ActivityPhoto`, `Gallery`
- `WebsiteDataService` binds activities/programs/branches/stats/testimonials/faqs/news/partners/gallery
- `Event` module already has a **dynamic form builder**: `events.form_fields` JSONB,
  `event_registrations.form_data` JSONB, 15 `FormFieldType` cases incl. FileUpload/Terms/Phone
- `app/Livewire/Public/EventRegistrationForm.php` — public unauthenticated form precedent

## Our builder — the blocking gaps
| # | Gap | Evidence |
|---|---|---|
| 1 | **No pages.** One landing page only. | No `website_pages` table anywhere; routes `/`, `/website/news`, `/website/events` hardcoded |
| 2 | **One section per type, forever.** | `unique(['academy_id','section_key'])` |
| 3 | **Cannot add or delete sections.** | `SectionManager` exposes only `toggleSection()` + `reorder()`; service `firstOrCreate`s one row per key |
| 4 | **No repeatable sub-items in a section.** | Fixed `title/subtitle/content` (+`_en`) columns; only `settings` JSONB, unedited by any UI |
| 5 | **No navigation model.** | Navbar links derived, not authored; no dropdown/mega-menu data |
| 6 | **No public enrolment funnel.** | No public OTP, no public pricing preview, no order/payment surface |
| 7 | **No per-record media on sessions/schedules.** | `MediaCollection` has no session/schedule collection |

## Structural mismatch to watch
Their bilingual shape is nested `{en, ar}` objects. Ours is paired columns
(`title` / `title_en`). Note our column named `title` holds **Arabic** (ar is default)
and `title_en` holds English — inverted vs their `en`-primary JSON. Any importer
must map deliberately, not positionally.
