# ARCHITECTURE.md — Technical Architecture

> Defines patterns and requirements. Exact schema, migrations, and API routes are Claude Code's to work out during implementation — this document is the contract those decisions must satisfy.

## Repos

1. **Backend** (this repo) — Laravel. Serves the public website, the JSON API consumed by Flutter, and the custom admin panel.
2. **Mobile** — Flutter. Single codebase, Android + iOS.

Product docs (PROJECT.md, this file, PHASES.md) live in the backend repo as source of truth. Mobile repo's CLAUDE.md links back rather than duplicating.

## Stack

- **Backend:** Laravel 13 (PHP 8.3+)
- **Database:** MySQL or PostgreSQL — confirm what's available on the hosting account; either is fine for this workload
- **Cache / leaderboards / queues:** Redis (to be activated on the hosting account)
- **Real-time:** Laravel Reverb + Echo, for live club-battle score updates
- **Admin panel:** custom-built — Laravel controllers/Blade with Livewire + Alpine for the reactive pieces (forms, tables, drag-reorder). Not Filament. Budget real implementation time for this; there's no free CRUD scaffolding.
- **Auth:** Sanctum (API tokens, serves website + Flutter) + Socialite (Google/Facebook/Apple login)
- **Push notifications:** Firebase Cloud Messaging, once the mobile app exists. Before that, email is the re-engagement channel for the website.
- **Image generation:** Intervention Image (or equivalent), for the certificate/OG-image generator (see below)
- **Payments:** Keepz, BOG installment
- **Mobile:** Flutter, single codebase

## Hosting: cPanel/WHM (self-managed server)

This runs on infrastructure you already administer, not a rented VPS or restrictive third-party shared host — that matters, because it means root/WHM-level setup is available where a typical shared-hosting tenant wouldn't have it.

- **The Laravel app itself** deploys as a standard cPanel account: document root pointed at `public/`, PHP version pinned via MultiPHP/CloudLinux `ea-php` (need 8.3+ for Laravel 13 — confirm `ea-php83` or newer is installed), Composer and Artisan run over SSH.
- **Cron:** Laravel's scheduler needs one cPanel cron entry running `schedule:run` every minute, same as any Laravel deploy.
- **Redis:** install/activate at the server level (not per-cPanel-account) — needed for leaderboards, cache, and the queue driver.
- **Queue workers:** cron alone isn't reliable for a persistent worker. Use Supervisor (or systemd directly, since you have root) to keep `queue:work` running and auto-restart on failure — this is the one piece that needs deliberate setup rather than "just works" on shared hosting.
- **Reverb:** needs a long-running process on its own port, not a request/response PHP script. Run it as a systemd service, and add a LiteSpeed reverse-proxy rule to expose it through the normal domain/port rather than opening a raw port to the internet. Treat this as its own small infra task, not an afterthought — see PHASES.md.
- **Storage:** certificates/OG images and any user uploads go on local disk under the cPanel account (or S3-compatible storage later if you want CDN offload) — not in the database.

## Configuration: admin-editable settings, not env

- A `settings` table (or structured config table) holds anything a non-developer might reasonably need to change: site name/branding text, star economy values (cost per star, rewards per challenge type), feature toggles, weekly quiz scheduling, sponsor slot config, leaderboard reset timing.
- Wrap it in a cached settings service/facade — reads should hit Redis/cache, not the DB, on every request.
- `.env` stays limited to true bootstrap/infra values: DB credentials, `APP_KEY`, mail credentials, Redis connection, and — importantly — **payment gateway secret keys stay in env, not the admin-editable settings table**, even though they're not "DB info." Anything admin-editable is one panel compromise away from being exposed; payment secrets don't belong there.

## Visual question generation

Procedural generation, not hand-drawn images — well-studied for the matrix/rotation family (the same technique used to generate matrix-reasoning benchmarks in AI research). Solves two problems at once: unlimited unique items from a small number of templates, and natural resistance to memorization/sharing, since every attempt renders a fresh instance.

**Grid/attribute family** — rotation matching, pattern-matrix completion, odd-one-out, sequence-what's-next:
- **Data model:** a compact rule description, not a fixed image — grid size, which attributes vary (shape, size, color, count, rotation, position), and which relation governs each attribute across the grid (constant, progression, arithmetic combination of two cells producing a third, etc.).
- **Distractors:** take the correct answer and break exactly one rule, so options are plausible rather than obviously wrong.

**Paper-folding family** — single/double-fold, hole-punch style. Added after competitive research showed this is common even on simple consumer sites, and confirmed it's 2D reflection geometry, not true 3D — a correction from the original architecture pass, which had grouped it with the hard cases:
- **Data model:** a shape (square/rectangle), one or two fold lines (vertical/horizontal for v1), and a punched hole position. Correct answer = reflect the hole position across each fold line, in reverse fold order.
- **Distractors:** reflect across the wrong axis, or produce the wrong hole count — same break-one-rule principle as the grid family.
- Keep to one or two axis-aligned folds for v1 — that's the range that stays simple reflection math. More folds or non-axis-aligned folds start approaching the harder end of the spectrum below.

**Rendering (both families):** client-side from the rule description — SVG on web, a custom-drawn widget in Flutter. Don't generate and store per-user image files for the question itself.

**v1 scope:** 5–7 coded templates across both families (rotation matching, pattern-matrix completion, odd-one-out, single/double-fold hole-punch; sequence-what's-next follows in Phase 2). A generic visual template *builder* in the admin is explicitly a later phase — the admin's job early on is toggling templates on/off and tuning difficulty/frequency, not designing new templates through a UI.

**Explicitly out of scope early:** full 3D object-rotation matching and cube-net-folds-into-which-cube — genuinely different (true 3D topology/rendering) problems, not variations on the above. These mostly show up on corporate aptitude-test-prep sites with licensed content, not quick consumer IQ sites.

## Certificate / share-image generation

One shared generator serves both the downloadable "certificate" and the Open Graph image used when a result is shared — same template (name/username, score, city rank, date, small "for fun, not an official credential" disclaimer), two delivery contexts. Don't build these as two separate systems.

## SEO

- Public pages are server-rendered Blade, not a client-side-only SPA — required for crawlability.
- Per-page meta tags (title/description), Open Graph + Twitter card tags (feeding off the certificate/share-image generator above), sitemap.xml, structured data where it applies (e.g., Quiz/Article schema).
- City/occupation/age-group leaderboard pages are real, indexable content pages — treat them as landing pages, not just authenticated app screens.

## Required patterns (recap, unchanged from earlier planning)

- **Leaderboards:** Redis sorted sets, scoped (national / city / club / weekly-reset), not `ORDER BY score LIMIT` against the primary DB.
- **Stars/points:** append-only ledger table; balance is derived by summing, never stored as a mutable integer.
- **Score/quiz integrity:** all timing and answers validated server-side. Randomized question banks per attempt. Explicit rule for which attempt counts toward public rank.
- **Localization:** every UI string and every piece of quiz content goes through the translation layer. English-only content during development is fine; hardcoded strings are not.
- **Theming:** light/dark, light default, registered-user preference persisted server-side and synced; guests get local/device-level default only.
- **Privacy/age:** registration gated to 16+ for v1. Real name/photo/public-leaderboard fields gated separately to 18+.

## Deferred to implementation time (Claude Code decides)

- Exact database schema and migrations
- Exact API route/endpoint design (document as OpenAPI once stabilized)
- Exact translation storage mechanism
- Testing framework specifics (Pest is the modern Laravel default; not mandated)
- Exact verified-mode proctoring mechanism — start with time/tab-lock only, evaluate webcam later
