"Build your football reputation, one match at a time."
BallKnowledge is a premium World Cup 2026 prediction and debate platform. Fans lock in match predictions, submit bold hot takes, and earn collectible FIFA-style Verdict Cards — all tracked against a persistent Football IQ reputation score.
🔗 Live: ballknowledge.live
Locker Room → World Cup Hub → Match Room → VAR Tribunal → Verdict Card → My Card → Share
- Locker Room (
/profile) — Manage manager profile settings in a widescreen 3-column cockpit (Identity, FUT Card Spotlight, Telemetry & Operations) with secure credentials authentication. - World Cup Hub (
/world-cup-hub) — Browse all 104 fixtures across Groups A–L with live standings - Match Room (
/match/[id]) — Before kickoff: lock score prediction, MOTM, goalscorer, hot takes, and Best XI squad. Once the match starts, a shared database live banter chat opens. - VAR Tribunal — After a match completes, the AI grades your hot takes and checks your score prediction against actual results
- Verdict Card — A custom FIFA-style collectible card is generated based on your performance score
- My Card (
/football-iq) — View your Football IQ card and collected card album - Share — Viral share links for individual cards (
/card/[id]) and full profile decks (/u/[username])
| Layer | Technology |
|---|---|
| Framework | Next.js 16.2.9 (App Router, Turbopack) |
| Language | TypeScript, React 19 |
| Database | PostgreSQL via Prisma Client v6 |
| State | localStorage-first with secure credentials login & database-backed live chat |
| AI | OpenRouter → Groq → Nvidia NIM → Deterministic fallback |
| Styling | Tailwind CSS v4, Dark Glassmorphism, custom CSS |
| Animation | Framer Motion, GSAP + ScrollTrigger, Lenis |
| Icons | Lucide React |
| Fonts | Space Grotesk (display), Outfit (body), Oswald (cards) |
| Deployment | Cloudflare Pages (Serverless) |
| Status | Condition | Predictions |
|---|---|---|
UPCOMING |
Before kickoff | ✅ Editable |
LIVE |
Kickoff → +2 hrs | 🔒 Locked |
COMPLETED |
Kickoff +2 hrs | 🔒 Locked & Graded |
Points are earned across Match Outcome, Scoreline Accuracy, Man of the Match, and First Goalscorer. Max capped at 100.
- Match Outcome (35 Points): Correct Draw (35 pts), Correct Winner (30 pts), Wrong Outcome (15 pts)
- Scoreline Accuracy (30 Points): Home/Away goals (each exact: 15 pts, off by 1: 10 pts, off by 2: 5 pts, off by 3+: 0 pts)
- Man of the Match (20 Points): Correct MOTM name match (20 pts), Partial name match/same team (12 pts), Wrong (4 pts)
- First Goalscorer (15 Points): Correct first goalscorer (15 pts), Scored later in the match (10 pts), Wrong/no contribution (3 pts)
- Formula: $\text{Round}(\text{Average Match Performance Rating of Selected XI} \times 10)$
- Player ratings are based on official FIFA match-specific performance scores (ranging from 4.5 to 9.9) for completed/resolved matches, rather than static card OVRs.
- Formula: Average of graded take base scores multiplied by confidence level.
- Base Scores:
CORRECT(100 pts),PARTIALLY_CORRECT(75 pts),INCORRECT(50 pts) - Confidence Multiplier: 1 (0.8x), 2 (0.9x), 3 (1.0x), 4 (1.1x), 5 (1.2x)
-
- Free users: 3 takes graded per match; Premium/Admin users: up to 5.
-
Formula:
$50 + \text{messages sent} + \text{positive reactions (upvotes)}$ , capped at 100.
| OVR Rating | Rarity |
|---|---|
| 90–99 | 🟡 LEGENDARY |
| 75–89 | 🟣 EPIC |
| 60–74 | 🔵 RARE |
| 1–59 | ⚪ COMMON |
- Node.js ≥ 18
- PostgreSQL database (local or hosted — Supabase, Railway, Neon)
- At least one AI key (OpenRouter recommended; Groq as fallback)
npm installCopy .env.example and fill in your values:
cp .env.example .env# Required
DATABASE_URL="postgresql://<user>:<password>@<host>:<port>/<db>?schema=public"
# AI grading — at least one required
OPENROUTER_API_KEY="sk-or-..." # Primary (AI grading + image gen)
GROQ_API_KEY="gsk_..." # Fallback
NVIDIA_API_KEY="nvapi-..." # Second fallback
# Required for production (OG image, sitemap)
NEXT_PUBLIC_SITE_URL="https://ballknowledge.live"
# Optional: Google SSO Auth
NEXT_PUBLIC_GOOGLE_CLIENT_ID="your_google_client_id.apps.googleusercontent.com"
# Optional: Discord OAuth2 Authentication
NEXT_PUBLIC_DISCORD_CLIENT_ID="your_discord_client_id"
DISCORD_CLIENT_SECRET="your_discord_client_secret"
# Optional: Facebook OAuth2 Authentication
NEXT_PUBLIC_FACEBOOK_APP_ID="your_facebook_app_id"
FACEBOOK_CLIENT_SECRET="your_facebook_client_secret"# Push schema to DB (creates all tables)
npx prisma db push
# Or run migrations
npx prisma migrate dev
# Generate Prisma client
npx prisma generatenpm run dev
# → http://localhost:3000npm run build
npm run startsrc/
├── app/
│ ├── page.tsx # Landing page (light-themed intentionally for marketing)
│ ├── layout.tsx # Root layout + metadata (OG, Twitter)
│ ├── globals.css # Design system, keyframes, glassmorphism
│ ├── error.tsx # Global error boundary
│ ├── not-found.tsx # Branded 404 page
│ ├── sitemap.ts # Auto-generates /sitemap.xml
│ ├── world-cup-hub/ # Tournament schedule + standings (dark theme)
│ ├── match/[id]/ # Match prediction cockpit (dark theme)
│ ├── football-iq/ # My Card + collected cards album (dark theme)
│ ├── profile/ # Profile settings + avatar (dark theme)
│ ├── card/[id]/ # Viral card share page (dark theme)
│ ├── u/[username]/ # Public profile share page
│ ├── leaderboard/ # Global manager leaderboard (dark theme)
│ └── api/
│ ├── matches/ # GET all World Cup fixtures
│ ├── teams/ # GET all World Cup teams
│ ├── resolve-match/ # POST: AI-grade and save predictions
│ ├── profile/[username]/ # GET public profile
│ ├── card/[id]/ # GET shared card data
│ ├── leaderboard/ # GET ranked leaderboard entries
│ ├── auth/ # POST: Secure manager sign-in/sign-up
│ ├── chat/[matchId]/ # GET/POST: Database-backed room banter chat
│ └── stats/ # GET community counter stats
├── components/
│ ├── TacticalPitch.tsx # Interactive 4-3-3 squad builder grid
│ ├── PredictionModal.tsx # Predictions + hot takes form
│ ├── MatchLiveChat.tsx # Database-backed banter chat (live matches)
│ ├── SportsCenterCard.tsx # FIFA-style card renderer (canvas + SVG)
│ ├── FlagImage.tsx # flagcdn.com flag images w/ emoji fallback
│ ├── Navbar.tsx # Solid top header (bg-[#0B0F19], no transparency)
│ ├── Footer.tsx # Footer
│ ├── Providers.tsx # React Query + client providers
│ ├── PageTransition.tsx # Framer Motion page transition wrapper
│ └── SmoothScroll.tsx # Lenis smooth scroll init
├── lib/
│ ├── matchUtils.ts # Shared: parseLocalDate, getDeterministicMatchResult, getFlagEmoji
│ ├── countries.ts # Country name → ISO code map (+ flagcdn URL helper)
│ ├── worldcupData.ts # Server-side match/team fetcher (local JSON, 5-min TTL cache)
│ ├── worldcup2026/
│ │ ├── football.matches.json # 104 World Cup 2026 fixtures (authoritative data source)
│ │ └── football.teams.json # 32 World Cup teams
│ ├── profileSync.ts # Client-side localStorage ↔ DB sync helpers
│ ├── roster.ts # 32-team player roster data
│ ├── landingData.ts # Static data for landing page tickers
│ ├── db.ts # Prisma singleton client
│ └── tribunalDB.ts # Static tribunal/verdict data types
prisma/
│ └── schema.prisma # DB models: FootballIQProfile, MatchPrediction, HotTake, ChatMessage, MatchCard
public/
│ ├── images/
│ │ ├── og-preview.png # 1200×630 social share image
│ │ ├── ball_knowledge_logo.png # Logo used in Navbar
│ │ └── *.webp # Background images (stadium, VIP box, etc.)
│ └── robots.txt
All critical user state (predictions, profile, chat) is saved to localStorage first. DB sync is opportunistic — if the database is unreachable, the app remains fully functional. This hybrid pattern means:
- ✅ Works without a DB connection (zero crash)
- ✅ Fast reads (no DB roundtrip for common actions)
- ✅ DB syncs when the resolution endpoint is called
Match status (UPCOMING/LIVE/COMPLETED) is computed from new Date() vs. the fixture's local_date. All pages use the shared parseLocalDate() utility from src/lib/matchUtils.ts.
OpenRouter (llama-3.3-70b-instruct)
→ Groq (llama-3.3-70b-specdec)
→ Nvidia NIM (meta/llama-3.1-70b-instruct)
→ Deterministic local tribunal fallback
All routes serve: X-Frame-Options: SAMEORIGIN, X-Content-Type-Options: nosniff, Strict-Transport-Security, Referrer-Policy: strict-origin-when-cross-origin, Permissions-Policy.
| Role | Hot Takes | Privileges |
|---|---|---|
FREE |
3 max | Standard predictions |
PREMIUM |
5 max | Roast styling, user tagging |
ADMIN |
5 max | Bypass kickoff lock (dev/testing) |
Roles are stored in localStorage and synced to PostgreSQL on upgrade. Change tier at /pricing.
- Push to GitHub
- Connect repo on dash.cloudflare.com
- Add environment variables in Cloudflare Pages dashboard settings:
DATABASE_URLOPENROUTER_API_KEY(recommended primary)GROQ_API_KEYNVIDIA_API_KEY(optional)NEXT_PUBLIC_SITE_URL(e.g.https://ballknowledge.live)
- Deploy. Cloudflare will automatically build and deploy your app.
See README-raspi.md for the full PM2 + Nginx reverse proxy setup guide.
MIT — see LICENSE.