feat: add football net background pattern and glass card styling

Diagonal ±45° goal-net texture on body background. All card surfaces
converted from opaque #0a1810 to glass-card (backdrop-blur + semi-transparent
rgba) or glass-card-hero (gradient rgba) so the net pattern shows through.
Covers all pages: home, groups, history, search, stats, teams, tournaments,
players, match cards, and 404.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-06-14 22:01:40 +02:00
parent 479c3d93e4
commit 767236739b
11 changed files with 60 additions and 49 deletions
+32 -1
View File
@@ -25,13 +25,44 @@
html { scroll-behavior: smooth; }
body {
background: #040d08;
background-color: #040d08;
/* Diagonal goal-net pattern */
background-image:
repeating-linear-gradient(
-45deg,
rgba(34,197,94,0.028) 0, rgba(34,197,94,0.028) 1px,
transparent 1px, transparent 28px
),
repeating-linear-gradient(
45deg,
rgba(34,197,94,0.028) 0, rgba(34,197,94,0.028) 1px,
transparent 1px, transparent 28px
);
color: #dff5e8;
font-family: "Space Grotesk", system-ui, sans-serif;
min-height: 100vh;
overflow-x: hidden;
}
/* Glass card — semi-transparent over the body net pattern */
.glass-card {
background: rgba(4, 18, 8, 0.78);
border: 1px solid rgba(34,197,94,0.15);
border-radius: 1rem;
overflow: hidden;
backdrop-filter: blur(10px);
-webkit-backdrop-filter: blur(10px);
}
.glass-card-hero {
background: linear-gradient(145deg, rgba(13,32,22,0.82), rgba(16,42,28,0.82));
border: 1px solid rgba(34,197,94,0.28);
border-radius: 1rem;
overflow: hidden;
backdrop-filter: blur(10px);
-webkit-backdrop-filter: blur(10px);
}
::-webkit-scrollbar { width: 5px; }
::-webkit-scrollbar-track { background: #020a04; }
::-webkit-scrollbar-thumb { background: rgba(34,197,94,0.25); border-radius: 4px; }
+1 -1
View File
@@ -57,7 +57,7 @@ export default function GroupsPage() {
})
const letter = groupName.replace('Group ', '')
return (
<div key={groupName} className="rounded-2xl overflow-hidden" style={{ background: '#0a1810', border: '1px solid rgba(34,197,94,0.15)' }}>
<div key={groupName} className="glass-card">
<div className="px-4 py-3 border-b" style={{
background: 'linear-gradient(90deg,rgba(34,197,94,0.12) 0%,rgba(34,197,94,0.04) 100%)',
borderColor: 'rgba(34,197,94,0.1)',
+1 -2
View File
@@ -54,8 +54,7 @@ export default function HistoryPage() {
const topScorer = t.topScorers?.[0]
return (
<Link key={t.year} href={`/tournaments/${t.year}`}>
<div className="rounded-2xl p-5 relative overflow-hidden cursor-pointer hover:border-[rgba(34,197,94,0.3)] transition-colors"
style={{ background: '#0a1810', border: '1px solid rgba(34,197,94,0.13)' }}>
<div className="glass-card p-5 relative cursor-pointer hover:border-[rgba(34,197,94,0.3)] transition-colors">
{/* Year watermark */}
<div className="absolute right-[-6px] bottom-[-18px] font-['Bebas_Neue'] text-[88px] leading-none pointer-events-none select-none"
style={{ color: 'rgba(34,197,94,0.04)' }}>
+1 -5
View File
@@ -7,11 +7,7 @@ export default function NotFound() {
return (
<div className="max-w-[1200px] mx-auto px-7 py-20 flex flex-col items-center text-center">
<div
className="pitch-grid rounded-2xl px-12 py-16 w-full max-w-lg"
style={{
background: 'linear-gradient(145deg,#0a1a0e 0%,#0d2416 100%)',
border: '1px solid rgba(34,197,94,0.2)',
}}
className="pitch-grid glass-card-hero rounded-2xl px-12 py-16 w-full max-w-lg"
>
<div className="font-['Bebas_Neue'] text-[120px] text-[#22c55e] leading-none">
404
+3 -4
View File
@@ -58,8 +58,7 @@ function UpcomingFixture({ match }: { match: UpcomingMatch }) {
const time = match.time?.split(' ')[0] ?? ''
return (
<Link href={`/tournaments/${match.year}#match-${match.id}`}>
<div className="rounded-[10px] p-3 px-4 flex items-center gap-2.5 hover:border-[rgba(34,197,94,0.2)] transition-colors cursor-pointer"
style={{ background: '#0a1810', border: '1px solid rgba(34,197,94,0.07)' }}>
<div className="glass-card rounded-[10px] p-3 px-4 flex items-center gap-2.5 hover:border-[rgba(34,197,94,0.2)] transition-colors cursor-pointer">
<TeamFlag name={match.team1.name} iso2={match.team1.iso2} size="sm" />
<div className="flex-1 text-[13px] text-[#6abf7a] font-medium truncate">
{match.team1.name} <span className="text-[#2a5c35]">vs</span> {match.team2.name}
@@ -102,7 +101,7 @@ export default function HomePage() {
<div>
{/* ── Hero ── */}
<div className="pitch-grid border-b" style={{
background: 'linear-gradient(145deg,#0a1a0e 0%,#0d2416 55%,#0a1a0e 100%)',
background: 'linear-gradient(145deg,rgba(10,26,14,0.9) 0%,rgba(13,36,22,0.9) 55%,rgba(10,26,14,0.9) 100%)',
borderColor: 'rgba(34,197,94,0.15)',
padding: '52px 0 44px',
}}>
@@ -182,7 +181,7 @@ export default function HomePage() {
{scorers.length > 0 && (
<div className="pt-8 pb-16">
<SectionHeader label="2026 Golden Boot Race" />
<div className="rounded-2xl overflow-hidden" style={{ background: '#0a1810', border: '1px solid rgba(34,197,94,0.16)' }}>
<div className="glass-card">
{scorers.map((s, i) => (
<Link key={s.playerName} href={`/players/${encodeURIComponent(s.playerName)}`}>
<div className="flex items-center gap-2 sm:gap-3 px-3 sm:px-4 py-3 border-b hover:bg-[rgba(34,197,94,0.03)] transition-colors cursor-pointer"
+3 -6
View File
@@ -65,10 +65,7 @@ export default function PlayerPage({ params }: { params: Promise<{ name: string
return (
<div className="max-w-[900px] mx-auto px-7 py-10 pb-16">
{/* Hero */}
<div className="pitch-grid rounded-2xl p-8 mb-8" style={{
background: 'linear-gradient(145deg,#0a1a0e,#0d2416)',
border: '1px solid rgba(34,197,94,0.2)',
}}>
<div className="pitch-grid glass-card-hero rounded-2xl p-8 mb-8">
<div className="flex items-center gap-6 flex-wrap">
{player.team && <TeamFlag name={player.team.name} iso2={player.team.iso2} size="xl" />}
<div>
@@ -94,7 +91,7 @@ export default function PlayerPage({ params }: { params: Promise<{ name: string
{ label: 'Penalties', value: player.penalties },
{ label: 'Tournaments', value: player.tournaments },
].map(item => (
<div key={item.label} className="rounded-xl p-4" style={{ background: '#0a1810', border: '1px solid rgba(34,197,94,0.12)' }}>
<div key={item.label} className="glass-card rounded-xl p-4">
<div className="text-[9px] text-[#2a5c35] tracking-[0.1em] uppercase mb-1.5">{item.label}</div>
<div className="font-['Bebas_Neue'] text-3xl text-[#22c55e]">{item.value}</div>
</div>
@@ -102,7 +99,7 @@ export default function PlayerPage({ params }: { params: Promise<{ name: string
</div>
{player.ownGoals > 0 && (
<div className="mb-6 rounded-xl p-3 px-4 text-sm text-[#2a5c35]" style={{ background: '#0a1810', border: '1px solid rgba(34,197,94,0.08)' }}>
<div className="mb-6 glass-card rounded-xl p-3 px-4 text-sm text-[#2a5c35]">
Includes {player.ownGoals} own goal{player.ownGoals !== 1 ? 's' : ''}
</div>
)}
+4 -8
View File
@@ -106,8 +106,7 @@ function SearchContent() {
<div className="grid grid-cols-[repeat(auto-fill,minmax(200px,1fr))] gap-2.5">
{results.teams.map((t: { name: string; iso2?: string | null; slug: string; stats?: { appearances: number; titles: number } | null }) => (
<Link key={t.name} href={`/teams/${t.slug}`}>
<div className="flex items-center gap-3 p-3 px-4 rounded-xl hover:border-[rgba(34,197,94,0.25)] transition-colors cursor-pointer"
style={{ background: '#0a1810', border: '1px solid rgba(34,197,94,0.12)' }}>
<div className="glass-card flex items-center gap-3 p-3 px-4 rounded-xl hover:border-[rgba(34,197,94,0.25)] transition-colors cursor-pointer">
<TeamFlag name={t.name} iso2={t.iso2} size="md" />
<div>
<div className="text-sm font-semibold text-[#dff5e8]">{t.name}</div>
@@ -129,8 +128,7 @@ function SearchContent() {
<div className="grid grid-cols-[repeat(auto-fill,minmax(220px,1fr))] gap-2.5">
{results.players.map((p: { playerName: string; goals: number; tournaments: number; team?: { name: string; iso2?: string | null } | null }) => (
<Link key={p.playerName} href={`/players/${encodeURIComponent(p.playerName)}`}>
<div className="flex items-center gap-3 p-3 px-4 rounded-xl hover:border-[rgba(34,197,94,0.25)] transition-colors cursor-pointer"
style={{ background: '#0a1810', border: '1px solid rgba(34,197,94,0.12)' }}>
<div className="glass-card flex items-center gap-3 p-3 px-4 rounded-xl hover:border-[rgba(34,197,94,0.25)] transition-colors cursor-pointer">
{p.team && <TeamFlag name={p.team.name} iso2={p.team.iso2} size="sm" />}
<div className="flex-1 min-w-0">
<div className="text-sm font-semibold text-[#dff5e8] truncate">{p.playerName}</div>
@@ -151,8 +149,7 @@ function SearchContent() {
<div className="grid grid-cols-[repeat(auto-fill,minmax(180px,1fr))] gap-2.5">
{results.tournaments.map((t: { year: number; host: string; winner?: string | null; totalGoals?: number | null; matchesCount?: number | null }) => (
<Link key={t.year} href={`/tournaments/${t.year}`}>
<div className="p-4 rounded-xl hover:border-[rgba(34,197,94,0.25)] transition-colors cursor-pointer"
style={{ background: '#0a1810', border: '1px solid rgba(34,197,94,0.12)' }}>
<div className="glass-card p-4 rounded-xl hover:border-[rgba(34,197,94,0.25)] transition-colors cursor-pointer">
<div className="font-['Bebas_Neue'] text-3xl text-[#22c55e]">{t.year}</div>
<div className="text-sm text-[#dff5e8]">{t.host}</div>
{t.winner && <div className="text-[10px] text-[#2a5c35] mt-1 flex items-center gap-1"><TrophyIcon className="w-3 h-3 flex-shrink-0" />{t.winner}</div>}
@@ -171,8 +168,7 @@ function SearchContent() {
<div className="flex flex-col gap-2">
{results.matches.map((m: SearchMatch) => (
<Link key={m.id} href={`/tournaments/${m.year}#match-${m.id}`}>
<div className="flex items-center gap-3 p-3 px-4 rounded-xl hover:border-[rgba(34,197,94,0.25)] transition-colors cursor-pointer"
style={{ background: '#0a1810', border: '1px solid rgba(34,197,94,0.12)' }}>
<div className="glass-card flex items-center gap-3 p-3 px-4 rounded-xl hover:border-[rgba(34,197,94,0.25)] transition-colors cursor-pointer">
<TeamFlag name={m.team1.name} iso2={m.team1.iso2} size="sm" />
<div className="flex-1 text-sm text-[#dff5e8]">{m.team1.name} vs {m.team2.name}</div>
{m.scoreFt && <span className="font-['Bebas_Neue'] text-lg text-[#22c55e]">{m.scoreFt[0]}{m.scoreFt[1]}</span>}
+3 -3
View File
@@ -51,7 +51,7 @@ function SectionTitle({ children, icon: Icon }: { children: React.ReactNode; ico
function Card({ children, className = '' }: { children: React.ReactNode; className?: string }) {
return (
<div className={`rounded-2xl overflow-hidden ${className}`} style={{ background: '#0a1810', border: '1px solid rgba(34,197,94,0.15)' }}>
<div className={`glass-card ${className}`}>
{children}
</div>
)
@@ -257,7 +257,7 @@ export default function StatsPage() {
<SectionTitle icon={SparklesIcon}>Hat-Tricks</SectionTitle>
<div className="grid grid-cols-[repeat(auto-fill,minmax(240px,1fr))] gap-3">
{hatTricks.map((h, i) => (
<div key={i} className="rounded-xl p-4" style={{ background: '#0a1810', border: '1px solid rgba(34,197,94,0.12)' }}>
<div key={i} className="glass-card rounded-xl p-4">
<div className="flex items-center gap-2 mb-2">
{h.team && <TeamFlag name={h.team.name} iso2={h.team.iso2} size="sm" />}
<div>
@@ -287,7 +287,7 @@ export default function StatsPage() {
{ label: 'Decided by PSO', value: `${etStats.wentToPenalties} (${etStats.penaltiesPct}%)` },
{ label: 'Decided in 90min', value: etStats.totalKnockoutMatches - etStats.wentToExtraTime },
].map(s => (
<div key={s.label} className="rounded-xl p-4" style={{ background: '#0a1810', border: '1px solid rgba(34,197,94,0.12)' }}>
<div key={s.label} className="glass-card rounded-xl p-4">
<div className="text-[9px] text-[#2a5c35] tracking-[0.1em] uppercase mb-2">{s.label}</div>
<div className="font-['Bebas_Neue'] text-2xl text-[#22c55e]">{s.value}</div>
</div>
+6 -10
View File
@@ -91,10 +91,7 @@ export default function TeamPage({ params }: { params: Promise<{ slug: string }>
return (
<div className="max-w-[1200px] mx-auto px-7 py-10 pb-16">
{/* Hero */}
<div className="pitch-grid rounded-2xl p-8 mb-8" style={{
background: 'linear-gradient(145deg,#0a1a0e,#0d2416)',
border: '1px solid rgba(34,197,94,0.2)',
}}>
<div className="pitch-grid glass-card-hero rounded-2xl p-8 mb-8">
<div className="flex items-center gap-6 flex-wrap">
<TeamFlag name={team.name} iso2={team.iso2} size="xl" />
<div>
@@ -127,13 +124,13 @@ export default function TeamPage({ params }: { params: Promise<{ slug: string }>
{ label: 'Win %', value: `${s.winPct}%` },
{ label: 'Goals For', value: s.goalsFor },
].map(item => (
<div key={item.label} className="rounded-xl p-4" style={{ background: '#0a1810', border: '1px solid rgba(34,197,94,0.12)' }}>
<div key={item.label} className="glass-card rounded-xl p-4">
<div className="text-[9px] text-[#2a5c35] tracking-[0.1em] uppercase mb-1.5">{item.label}</div>
<div className="font-['Bebas_Neue'] text-3xl text-[#22c55e]">{item.value}</div>
</div>
))}
</div>
<div className="rounded-xl overflow-hidden" style={{ background: '#0a1810', border: '1px solid rgba(34,197,94,0.12)' }}>
<div className="glass-card rounded-xl">
<div className="grid px-4 py-2.5 text-[9px] text-[#2a5c35] tracking-[0.1em] uppercase"
style={{ gridTemplateColumns: '1fr 44px 44px 44px 60px 60px 60px' }}>
<span>Team</span><span className="text-center">W</span><span className="text-center">D</span>
@@ -162,8 +159,7 @@ export default function TeamPage({ params }: { params: Promise<{ slug: string }>
<div className="flex flex-wrap gap-2">
{years.map(year => (
<Link key={year} href={`/tournaments/${year}`}
className="font-['Bebas_Neue'] text-lg px-3 py-1 rounded-lg transition-colors hover:text-[#22c55e] hover:border-[rgba(34,197,94,0.4)]"
style={{ background: '#0a1810', border: '1px solid rgba(34,197,94,0.15)', color: '#6abf7a' }}>
className="font-['Bebas_Neue'] text-lg px-3 py-1 rounded-lg transition-colors text-[#6abf7a] bg-[rgba(4,18,8,0.78)] border border-[rgba(34,197,94,0.15)] hover:text-[#22c55e] hover:border-[rgba(34,197,94,0.4)] backdrop-blur-sm">
{year}
</Link>
))}
@@ -184,7 +180,7 @@ export default function TeamPage({ params }: { params: Promise<{ slug: string }>
className="inline-block font-['Bebas_Neue'] text-[22px] text-[#22c55e] mb-2 hover:opacity-70 transition-opacity">
{year}
</Link>
<div className="rounded-xl overflow-hidden" style={{ background: '#0a1810', border: '1px solid rgba(34,197,94,0.12)' }}>
<div className="glass-card rounded-xl">
{yMatches.map((m, i) => {
const isHome = m.team1.name === team.name
const opponent = isHome ? m.team2 : m.team1
@@ -248,7 +244,7 @@ export default function TeamPage({ params }: { params: Promise<{ slug: string }>
{teamScorers.length > 0 && (
<div>
<h2 className="text-[11px] text-[#2a5c35] font-bold tracking-[0.14em] uppercase mb-4">Top Scorers</h2>
<div className="rounded-2xl overflow-hidden" style={{ background: '#0a1810', border: '1px solid rgba(34,197,94,0.15)' }}>
<div className="glass-card">
{teamScorers.map((sc: { playerName: string; goals: number; penalties: number; tournaments: number }, i: number) => (
<Link key={sc.playerName} href={`/players/${encodeURIComponent(sc.playerName)}`}>
<div className="flex items-center gap-2.5 px-3.5 py-2.5 border-b hover:bg-[rgba(34,197,94,0.03)] cursor-pointer"
+4 -7
View File
@@ -124,10 +124,7 @@ export default function TournamentPage({ params }: { params: Promise<{ year: str
return (
<div className="max-w-[1200px] mx-auto px-7 py-10 pb-16">
{/* Header */}
<div className="pitch-grid rounded-2xl p-8 mb-8" style={{
background: 'linear-gradient(145deg,#0a1a0e 0%,#0d2416 100%)',
border: '1px solid rgba(34,197,94,0.2)',
}}>
<div className="pitch-grid glass-card-hero rounded-2xl p-8 mb-8">
{liveMatches.length > 0 && <div className="mb-3"><LiveBadge label="Live Now" /></div>}
<div className="flex items-start justify-between flex-wrap gap-4">
<div>
@@ -185,7 +182,7 @@ export default function TournamentPage({ params }: { params: Promise<{ year: str
<div key={groupName} className="mb-8">
<h3 className="text-[13px] font-bold text-[#22c55e] tracking-wide uppercase mb-3">{groupName}</h3>
{/* Standings mini */}
<div className="rounded-xl overflow-hidden mb-3" style={{ background: '#0a1810', border: '1px solid rgba(34,197,94,0.1)' }}>
<div className="glass-card rounded-xl mb-3">
{sorted.map((s, i) => (
<Link key={s.team.id} href={`/teams/${s.team.slug}`}>
<div className="flex items-center gap-2 px-3 py-2 border-b hover:bg-[rgba(34,197,94,0.03)] cursor-pointer"
@@ -241,7 +238,7 @@ export default function TournamentPage({ params }: { params: Promise<{ year: str
<div>
<div className="sticky top-[76px]">
<h2 className="font-['Bebas_Neue'] text-xl text-[#22c55e] mb-4">TOP SCORERS</h2>
<div className="rounded-2xl overflow-hidden" style={{ background: '#0a1810', border: '1px solid rgba(34,197,94,0.15)' }}>
<div className="glass-card">
{t.topScorers?.map((s: { playerName: string; goals: number; penalties: number; team?: { name: string; iso2?: string | null; slug: string } | null }, i: number) => (
<Link key={s.playerName} href={`/players/${encodeURIComponent(s.playerName)}`}>
<div className="flex items-center gap-2.5 px-3.5 py-2.5 border-b hover:bg-[rgba(34,197,94,0.03)] cursor-pointer"
@@ -262,7 +259,7 @@ export default function TournamentPage({ params }: { params: Promise<{ year: str
</div>
{t.thirdPlace && (
<div className="mt-4 rounded-xl p-4" style={{ background: '#0a1810', border: '1px solid rgba(34,197,94,0.1)' }}>
<div className="glass-card mt-4 rounded-xl p-4">
<div className="text-[9px] text-[#2a5c35] tracking-[0.1em] uppercase mb-2">3rd Place</div>
<div className="flex items-center gap-2">
<TeamFlag name={t.thirdPlace} size="sm" />