feat: initial commit — World Cup stats app with pnpm, Traefik, Docker

Full-stack World Cup web app (1930–2026):
- Next.js 16 + TailwindCSS 4 + GraphQL Yoga + Apollo Client 4 + Drizzle + PostgreSQL 16
- 23 tournaments synced from openfootball/worldcup.json (matches, goals, teams, stadiums, squads, standings)
- Pages: home (live), groups, stats, history, search, /tournaments/[year], /teams/[slug], /players/[name]
- Live match detection via isLive() + Apollo 60 s poll
- pnpm with node-linker=hoisted for Docker compatibility
- docker-compose.yml with Traefik labels (HTTPS redirect, TLS, security middleware)
- docker-compose.dev.yml for local dev (DB only, port 5432 exposed)
- Dockerfile: multi-stage pnpm build, standalone Next.js output, sync script bundled
- .env.example with all required variables documented
- Comprehensive README with local dev, deployment, schema, and GraphQL API reference

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-06-14 15:36:44 +02:00
commit 58b4114159
46 changed files with 9040 additions and 0 deletions
+57
View File
@@ -0,0 +1,57 @@
@import "tailwindcss";
@import "flag-icons/css/flag-icons.min.css";
@theme inline {
--color-bg: #040d08;
--color-card: #0a1810;
--color-hero: #0d2416;
--color-green: #22c55e;
--color-green-light: #4ade80;
--color-green-sec: #6abf7a;
--color-green-muted: #2a5c35;
--color-green-dark: #1a3a22;
--color-text: #dff5e8;
--color-border: rgba(34,197,94,0.15);
--font-display: "Bebas Neue", cursive;
--font-body: "Space Grotesk", system-ui, sans-serif;
}
* { box-sizing: border-box; margin: 0; padding: 0; }
html { scroll-behavior: smooth; }
body {
background: #040d08;
color: #dff5e8;
font-family: "Space Grotesk", system-ui, sans-serif;
min-height: 100vh;
overflow-x: hidden;
}
::-webkit-scrollbar { width: 5px; }
::-webkit-scrollbar-track { background: #020a04; }
::-webkit-scrollbar-thumb { background: rgba(34,197,94,0.25); border-radius: 4px; }
@keyframes livePulse {
0%, 100% { opacity: 1; transform: scale(1); }
50% { opacity: 0.2; transform: scale(0.6); }
}
@keyframes fadeIn {
from { opacity: 0; transform: translateY(8px); }
to { opacity: 1; transform: translateY(0); }
}
.animate-live { animation: livePulse 2s ease-in-out infinite; }
.animate-fade-in { animation: fadeIn 0.3s ease-out; }
.pitch-grid {
background-image: repeating-linear-gradient(
0deg,
transparent,
transparent 44px,
rgba(34,197,94,0.018) 44px,
rgba(34,197,94,0.018) 88px
);
}