Admin Dashboard

src/pages/AdminPortal.jsx

import React, { useState, useEffect } from 'react';
import { base44 } from '@/api/base44Client';
import Seo from '@/components/Seo';
import { Shield, Users, Video, Radio, BarChart3, BookOpen, UserCheck, Menu, X, FolderOpen, Download, Film, Upload, Trash2, LayoutGrid, Inbox, Trophy, ClipboardList } from 'lucide-react';
import { Button } from '@/components/ui/button';
import AdminFeatureAccess from '../components/admin/AdminFeatureAccess';
import AdminTeams from '../components/admin/AdminTeams';
import AdminStatGame from '../components/admin/AdminStatGame';
import AdminLiveStream from '../components/admin/AdminLiveStream';
import AdminStats from '../components/admin/AdminStats';
import AdminFilm from '../components/admin/AdminFilm';
import AdminHowTo from '../components/admin/AdminHowTo';
import AdminPortfolios from '../components/admin/AdminPortfolios';
import AdminCoachVerification from '../components/admin/AdminCoachVerification';
import AdminTeamSiteDashboard from '../components/admin/AdminTeamSiteDashboard';
import AdminPlayerExport from '../components/admin/AdminPlayerExport';
import AdminTopPlayersExport from '../components/admin/AdminTopPlayersExport';
import AdminTeamSitePortfolios from '../components/admin/AdminTeamSitePortfolios';
import AdminGeorgiaFilmPush from '../components/admin/AdminGeorgiaFilmPush';
import AdminCsvImport from '../components/admin/AdminCsvImport';
import AdminIdentityImport from '../components/admin/AdminIdentityImport';
import CleanSlateReset from '../components/admin/CleanSlateReset';
import AdminUpdateRequests from '../components/admin/AdminUpdateRequests';
import AdminTournamentInquiries from '../components/admin/AdminTournamentInquiries';
import GeorgiaTeamSite from './GeorgiaTeamSite';

const NAV_TABS = [
{ id: 'admin', label: 'Admin', icon: Shield, color: 'text-orange-400' },
{ id: 'hub', label: 'HUB', icon: LayoutGrid, color: 'text-blue-400' },
{ id: 'portfolios', label: 'Portfolios', icon: FolderOpen, color: 'text-emerald-400' },
{ id: 'export', label: 'URL Export', icon: Download, color: 'text-teal-400' },
{ id: 'top300', label: 'Top 300', icon: Trophy, color: 'text-yellow-400' },
{ id: 'team_sites', label: 'Team Sites', icon: Video, color: 'text-orange-400' },
{ id: 'team_portfolios', label: 'Team Rosters', icon: Users, color: 'text-indigo-400' },
{ id: 'film_push', label: 'Film → Portfolios', icon: Film, color: 'text-orange-400' },
{ id: 'data_cleanup', label: 'Data Cleanup & Import', icon: Trash2, color: 'text-red-400' },
{ id: 'update_requests', label: 'Update Requests', icon: Inbox, color: 'text-amber-400' },
{ id: 'tournament_inquiries', label: 'Tournament Inquiries', icon: ClipboardList, color: 'text-orange-400' }];

const HUB_TABS = [
  { id: 'teams', label: 'Teams', icon: Users },
  { id: 'stat_game', label: 'Stat Game', icon: BarChart3 },
  { id: 'live_stream', label: 'Live Stream', icon: Radio },
  { id: 'stats', label: 'Stats', icon: BarChart3 },
  { id: 'film', label: 'Film', icon: Video },
  { id: 'how_to', label: 'How-To', icon: BookOpen },
  { id: 'coaches', label: 'Coaches', icon: UserCheck },
];


export default function AdminPortal() {
  const [activeTab, setActiveTab] = useState('admin');
  const [hubSubTab, setHubSubTab] = useState('teams');
  const [mobileMenuOpen, setMobileMenuOpen] = useState(false);
  const [passwordInput, setPasswordInput] = useState('');
  const [passwordError, setPasswordError] = useState(false);
  const [unlocked, setUnlocked] = useState(() => sessionStorage.getItem('admin_unlocked') === 'true');

  const renderHubContent = () => {
    switch (hubSubTab) {
      case 'teams': return <AdminTeams />;
      case 'stat_game': return <AdminStatGame />;
      case 'live_stream': return <AdminLiveStream />;
      case 'stats': return <AdminStats />;
      case 'film': return <AdminFilm />;
      case 'how_to': return <AdminHowTo />;
      case 'coaches': return <AdminCoachVerification />;
      default: return <AdminTeams />;
    }
  };

  const renderTab = () => {
    switch (activeTab) {
      case 'admin':return <AdminFeatureAccess />;
      case 'hub':return (
        <div>
          <div className="flex items-center gap-1 mb-4 pb-3 border-b border-slate-800 overflow-x-auto">
            {HUB_TABS.map(sub => {
              const SubIcon = sub.icon;
              const isActive = hubSubTab === sub.id;
              return (
                <button key={sub.id} onClick={() => setHubSubTab(sub.id)}
                  className={`flex items-center gap-1.5 px-3 py-1.5 rounded-lg text-sm font-semibold whitespace-nowrap transition-all ${
                    isActive ? 'bg-blue-500 text-white' : 'text-slate-400 hover:text-white hover:bg-slate-800'}`}>
                  <SubIcon className="w-3.5 h-3.5" />{sub.label}
                </button>
              );
            })}
          </div>
          {renderHubContent()}
        </div>
      );
      case 'portfolios':return <AdminPortfolios currentUser={null} />;
      case 'export':return <AdminPlayerExport />;
      case 'top300':return <AdminTopPlayersExport />;
      case 'team_sites':return <AdminTeamSiteDashboard />;
      case 'team_portfolios':return <AdminTeamSitePortfolios />;
      case 'film_push':return <AdminGeorgiaFilmPush />;
      case 'data_cleanup':return (<div className="space-y-8"><CleanSlateReset /><AdminIdentityImport /><AdminCsvImport /></div>);
      case 'update_requests':return <AdminUpdateRequests />;
      case 'tournament_inquiries':return <AdminTournamentInquiries />;
      default:return <AdminFeatureAccess />;
    }
  };

  if (!unlocked) {
    return (
      <div className="min-h-screen bg-slate-950 text-white flex items-center justify-center px-4">
        <div className="w-full max-w-sm bg-slate-900 rounded-2xl border border-slate-800 p-8">
          <div className="flex items-center gap-3 mb-6">
            <div className="w-10 h-10 rounded-lg bg-gradient-to-br from-orange-500 to-pink-500 flex items-center justify-center">
              <Shield className="w-5 h-5 text-white" />
            </div>
            <div>
              <p className="text-white font-black text-sm">Admin Portal</p>
              <p className="text-slate-400 text-xs">Enter password to continue</p>
            </div>
          </div>
          <form onSubmit={(e) => {
            e.preventDefault();
            if (passwordInput === '1029384756') {
              sessionStorage.setItem('admin_unlocked', 'true');
              setUnlocked(true);
            } else {
              setPasswordError(true);
              setPasswordInput('');
            }
          }}>
            <input
              type="password"
              value={passwordInput}
              onChange={(e) => {setPasswordInput(e.target.value);setPasswordError(false);}}
              placeholder="Password"
              autoFocus
              className={`w-full bg-slate-800 border rounded-lg px-4 py-3 text-white text-sm focus:outline-none focus:border-orange-500 mb-3 ${passwordError ? 'border-red-500' : 'border-slate-700'}`} />
            
            {passwordError && <p className="text-red-400 text-xs mb-3">Incorrect password</p>}
            <button type="submit" className="w-full bg-orange-500 hover:bg-orange-600 text-white font-bold py-3 rounded-lg text-sm transition-all">
              Unlock
            </button>
          </form>
        </div>
      </div>);

  }

  return (
    <div className="min-h-screen bg-slate-950 text-white flex flex-col">
      <Seo noindex title="Admin Portal" path="/admin-portal" />
      {/* Top Nav */}
      <header className="bg-slate-900 border-b border-slate-800 sticky top-0 z-30">
        <div className="max-w-7xl mx-auto px-4">
          <div className="flex items-center h-14 gap-4">
            {/* Logo */}
            <div className="flex items-center gap-2 flex-shrink-0">
              <div className="w-8 h-8 rounded-lg bg-gradient-to-br from-orange-500 to-pink-500 flex items-center justify-center">
                <Shield className="w-4 h-4 text-white" />
              </div>
              <div className="hidden sm:block">
                <p className="text-sm font-black text-white leading-tight">goaio.live</p>
                <p className="text-[10px] text-slate-400 leading-tight">Stats-By-Video Platform</p>
              </div>
            </div>

            {/* Desktop Nav */}
            <nav className="hidden md:flex items-center gap-1 flex-1 ml-4">
              {NAV_TABS.map((tab) => {
                const Icon = tab.icon;
                const isActive = activeTab === tab.id;
                return (
                  <button
                    key={tab.id}
                    onClick={() => setActiveTab(tab.id)}
                    className={`flex items-center gap-1.5 px-3 py-1.5 rounded-lg text-sm font-semibold transition-all ${
                    isActive ?
                    tab.id === 'admin' ? 'bg-orange-500 text-white' :
                    tab.id === 'hub' ? 'bg-blue-500 text-white' :
                    tab.id === 'live_stream' ? 'bg-red-500 text-white' :
                    tab.id === 'how_to' ? 'bg-yellow-500 text-black' :
                    'bg-slate-700 text-white' :
                    'text-slate-400 hover:text-white hover:bg-slate-800'}`
                    }>
                    
                    <Icon className="w-3.5 h-3.5" />
                    {tab.label}
                  </button>);

              })}
            </nav>

            {/* Right side */}
            <div className="flex items-center gap-2 ml-auto">
              <button
                onClick={() => {sessionStorage.removeItem('admin_unlocked');setUnlocked(false);}}
                className="text-xs text-slate-500 hover:text-slate-300 transition-colors px-2 py-1">
                Lock</button>
              <button className="md:hidden text-slate-400 hover:text-white"
              onClick={() => setMobileMenuOpen(!mobileMenuOpen)}>
                {mobileMenuOpen ? <X className="w-5 h-5" /> : <Menu className="w-5 h-5" />}
              </button>
            </div>
          </div>

          {/* Mobile menu */}
          {mobileMenuOpen &&
          <div className="md:hidden pb-3 flex flex-wrap gap-2">
              {NAV_TABS.map((tab) => {
              const Icon = tab.icon;
              const isActive = activeTab === tab.id;
              return (
                <button key={tab.id}
                onClick={() => {setActiveTab(tab.id);setMobileMenuOpen(false);}}
                className={`flex items-center gap-1.5 px-3 py-1.5 rounded-lg text-xs font-semibold transition-all ${
                isActive ? 'bg-slate-700 text-white' : 'text-slate-400 hover:text-white'}`
                }>
                    <Icon className="w-3.5 h-3.5" />{tab.label}
                  </button>);

            })}
            </div>
          }
        </div>
      </header>

      {/* Page Content */}
      <main className="flex-1 max-w-7xl mx-auto w-full px-4 py-6">
        {renderTab()}
      </main>
    </div>);

}

src/components/admin/AdminCoachVerification.jsx

import React, { useState } from 'react';
import { base44 } from '@/api/base44Client';
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
import { CheckCircle2, Clock, Mail, School, User, ExternalLink } from 'lucide-react';
import { Button } from '@/components/ui/button';

export default function AdminCoachVerification() {
  const queryClient = useQueryClient();
  const [verifying, setVerifying] = useState(null);

  const { data: coaches = [], isLoading } = useQuery({
    queryKey: ['coach-profiles'],
    queryFn: () => base44.entities.CoachProfile.list('-created_date', 50),
  });

  const { data: teams = [] } = useQuery({
    queryKey: ['hoop-teams'],
    queryFn: () => base44.entities.HoopTeam.list(),
  });

  const teamMap = Object.fromEntries(teams.map(t => [t.id, t]));

  const verifyMutation = useMutation({
    mutationFn: async ({ id, is_verified }) => {
      return base44.entities.CoachProfile.update(id, { is_verified });
    },
    onSuccess: () => {
      queryClient.invalidateQueries(['coach-profiles']);
      setVerifying(null);
    },
  });

  const pending = coaches.filter(c => c.has_paid && !c.is_verified);
  const verified = coaches.filter(c => c.is_verified);
  const unpaid = coaches.filter(c => !c.has_paid);

  const CoachRow = ({ coach, showActions }) => {
    const team = coach.team_id ? teamMap[coach.team_id] : null;
    return (
      <div className="flex items-start gap-4 p-4 rounded-xl border" style={{ background: '#0f172a', borderColor: showActions ? 'rgba(251,146,60,0.3)' : 'rgba(255,255,255,0.06)' }}>
        <div className="w-10 h-10 rounded-full bg-gradient-to-br from-orange-500 to-pink-500 flex items-center justify-center shrink-0">
          <User className="w-5 h-5 text-white" />
        </div>
        <div className="flex-1 min-w-0">
          <div className="flex items-center gap-2 flex-wrap">
            <p className="font-bold text-white">{coach.coach_name}</p>
            {coach.is_verified && <span className="text-xs px-2 py-0.5 rounded-full bg-green-500/10 border border-green-500/25 text-green-400 font-bold">Verified</span>}
            {coach.has_paid && !coach.is_verified && <span className="text-xs px-2 py-0.5 rounded-full bg-orange-500/10 border border-orange-500/25 text-orange-400 font-bold">Pending Review</span>}
            {!coach.has_paid && <span className="text-xs px-2 py-0.5 rounded-full bg-slate-700 text-slate-400 font-bold">Not Paid</span>}
          </div>
          <div className="flex flex-wrap gap-x-4 gap-y-1 mt-1 text-xs text-slate-400">
            <span className="flex items-center gap-1"><Mail className="w-3 h-3" /> {coach.email}</span>
            <span className="flex items-center gap-1"><School className="w-3 h-3" /> {coach.high_school}</span>
            {team && <span className="flex items-center gap-1">🏀 {team.name}</span>}
          </div>
          {coach.team_id && (
            <a href={`/coach-dashboard?team_id=${coach.team_id}`} target="_blank" rel="noopener noreferrer"
              className="inline-flex items-center gap-1 text-xs mt-2 hover:underline" style={{ color: '#FF6A00' }}>
              <ExternalLink className="w-3 h-3" /> View Team Dashboard
            </a>
          )}
        </div>
        {showActions && (
          <div className="flex gap-2 shrink-0">
            <Button size="sm" onClick={() => verifyMutation.mutate({ id: coach.id, is_verified: true })}
              className="bg-green-600 hover:bg-green-700 text-white text-xs">
              <CheckCircle2 className="w-3.5 h-3.5 mr-1" /> Approve
            </Button>
          </div>
        )}
      </div>
    );
  };

  if (isLoading) return <div className="text-slate-400 text-sm">Loading...</div>;

  return (
    <div className="space-y-8">
      {/* Summary */}
      <div className="grid grid-cols-3 gap-3">
        {[
          { label: 'Pending Review', val: pending.length, color: 'text-orange-400' },
          { label: 'Verified', val: verified.length, color: 'text-green-400' },
          { label: 'Total Signups', val: coaches.length, color: 'text-white' },
        ].map(s => (
          <div key={s.label} className="bg-slate-900 rounded-xl border border-slate-800 p-4 text-center">
            <p className={`font-black text-3xl ${s.color}`}>{s.val}</p>
            <p className="text-xs text-slate-500 mt-0.5">{s.label}</p>
          </div>
        ))}
      </div>

      {/* Pending */}
      {pending.length > 0 && (
        <div>
          <h2 className="text-white font-bold mb-3 flex items-center gap-2">
            <Clock className="w-4 h-4 text-orange-400" /> Pending Verification ({pending.length})
          </h2>
          <div className="space-y-2">
            {pending.map(c => <CoachRow key={c.id} coach={c} showActions={true} />)}
          </div>
        </div>
      )}

      {/* Verified */}
      {verified.length > 0 && (
        <div>
          <h2 className="text-white font-bold mb-3 flex items-center gap-2">
            <CheckCircle2 className="w-4 h-4 text-green-400" /> Verified Coaches ({verified.length})
          </h2>
          <div className="space-y-2">
            {verified.map(c => <CoachRow key={c.id} coach={c} showActions={false} />)}
          </div>
        </div>
      )}

      {pending.length === 0 && verified.length === 0 && (
        <div className="text-center py-16 rounded-2xl border border-dashed border-slate-700">
          <CheckCircle2 className="w-12 h-12 text-slate-600 mx-auto mb-3" />
          <p className="text-slate-400">No coach signups yet.</p>
        </div>
      )}
    </div>
  );
}

src/components/admin/AdminCsvImport.jsx

import React, { useState, useRef } from 'react';
import { base44 } from '@/api/base44Client';
import { Upload, FileText, Play, AlertCircle, CheckCircle2, Download, Loader2, XCircle, AlertTriangle, Link2, FileSpreadsheet, Users, Sparkles } from 'lucide-react';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';

export default function AdminCsvImport() {
  const [file, setFile] = useState(null);
  const [fileUrl, setFileUrl] = useState('');
  const [csvText, setCsvText] = useState('');
  const [dryRun, setDryRun] = useState(true);
  const [skipPublished, setSkipPublished] = useState(true);
  const [createMissing, setCreateMissing] = useState(true);
  const [updateExisting, setUpdateExisting] = useState(true);
  const [loading, setLoading] = useState(false);
  const [result, setResult] = useState(null);
  const [error, setError] = useState(null);
  const [activeView, setActiveView] = useState('summary');
  const fileInputRef = useRef(null);

  const handleFileSelect = (e) => {
    const f = e.target.files[0];
    if (!f) return;
    setFile(f);
    setFileUrl('');
    const reader = new FileReader();
    reader.onload = (ev) => setCsvText(ev.target.result);
    reader.readAsText(f);
  };

  const handleUrlMode = () => {
    setFile(null);
    setCsvText('');
  };

  const handleRun = async () => {
    setLoading(true);
    setError(null);
    setResult(null);
    try {
      const payload = {
        dry_run: dryRun,
        skip_published: skipPublished,
        create_missing: createMissing,
        update_existing: updateExisting,
        csv_filename: file?.name || (fileUrl ? fileUrl.split('/').pop() : 'unknown.csv')
      };
      if (fileUrl) {
        payload.file_url = fileUrl;
      } else if (csvText) {
        payload.csv_text = csvText;
      } else {
        setError('Please upload a file or paste a CSV URL.');
        setLoading(false);
        return;
      }
      const res = await base44.functions.invoke('importPlayerStatsCSV', payload);
      setResult(res.data);
    } catch (err) {
      setError(err?.response?.data?.error || err?.message || 'Import failed');
    }
    setLoading(false);
  };

  const downloadErrorLog = () => {
    if (!result?.preview?.errors?.length) return;
    const headers = ['Row', 'Player Name', 'Team (Original)', 'Team (Cleaned)', 'Error', 'Close Matches / Conflicting Records'];
    const rows = result.preview.errors.map(e => [
      e.row || '',
      e.player_name || '',
      e.team || '',
      e.cleaned_team || '',
      e.error || '',
      e.close_matches ? e.close_matches.map(m => `${m.name} (${m.team})`).join('; ') :
        (e.matching_records ? e.matching_records.map(m => `${m.name} (${m.team})`).join('; ') : '')
    ]);
    const csv = [headers.join(','), ...rows.map(r => r.map(c => `"${String(c).replace(/"/g, '""')}"`).join(','))].join('\n');
    const blob = new Blob([csv], { type: 'text/csv' });
    const url = URL.createObjectURL(blob);
    const a = document.createElement('a');
    a.href = url;
    a.download = `player_stats_import_errors_${new Date().toISOString().slice(0, 19).replace(/[:-]/g, '')}.csv`;
    a.click();
    URL.revokeObjectURL(url);
  };

  return (
    <div className="space-y-6">
      {/* Header */}
      <div>
        <h2 className="text-2xl font-black text-white mb-1">Import Player Statistics from CSV</h2>
        <p className="text-gray-400 text-sm">
          Upload a CSV of per-game player stats. The system matches rows to existing Player Portfolios by name + team,
          applies boxscores, and <strong className="text-emerald-400">creates new portfolios</strong> for unmatched players.{' '}
          <span className="text-yellow-400">Published/Premium portfolios are skipped by default for safety.</span>
        </p>
      </div>

      {/* Upload / URL section */}
      <div className="bg-slate-900 rounded-xl border border-slate-800 p-6 space-y-4">
        <div className="flex gap-2 mb-3">
          <button onClick={() => fileInputRef.current?.click()}
            className={`flex-1 flex items-center justify-center gap-2 py-3 rounded-lg border-2 border-dashed transition-all text-sm font-medium
              ${file ? 'border-green-500/40 bg-green-500/5 text-green-400' : 'border-slate-700 text-gray-400 hover:border-orange-500/40 hover:text-orange-400'}`}>
            {file ? <><CheckCircle2 className="w-4 h-4" /> {file.name}</> : <><Upload className="w-4 h-4" /> Upload CSV File</>}
          </button>
          <button onClick={handleUrlMode}
            className={`flex-1 flex items-center justify-center gap-2 py-3 rounded-lg border-2 border-dashed transition-all text-sm font-medium
              ${fileUrl ? 'border-green-500/40 bg-green-500/5 text-green-400' : 'border-slate-700 text-gray-400 hover:border-orange-500/40 hover:text-orange-400'}`}>
            <Link2 className="w-4 h-4" /> Paste CSV URL
          </button>
        </div>
        <input ref={fileInputRef} type="file" accept=".csv,text/csv" onChange={handleFileSelect} className="hidden" />

        {fileUrl !== '' || (!file && csvText === '') ? (
          <Input
            placeholder="https://...csv"
            value={fileUrl}
            onChange={e => setFileUrl(e.target.value)}
            className="bg-slate-800 border-slate-700 text-white text-sm"
          />
        ) : null}

        {/* Options */}
        <div className="flex flex-wrap gap-4 pt-2">
          <label className="flex items-center gap-2 cursor-pointer">
            <input type="checkbox" checked={dryRun} onChange={e => setDryRun(e.target.checked)}
              className="w-4 h-4 accent-orange-500" />
            <span className="text-sm text-gray-300">Dry Run <span className="text-gray-500">(preview only)</span></span>
          </label>
          <label className="flex items-center gap-2 cursor-pointer">
            <input type="checkbox" checked={updateExisting} onChange={e => setUpdateExisting(e.target.checked)}
              className="w-4 h-4 accent-orange-500" />
            <span className="text-sm text-gray-300">Update Existing Portfolios <span className="text-gray-500">(apply stats to matched players)</span></span>
          </label>
          <label className="flex items-center gap-2 cursor-pointer">
            <input type="checkbox" checked={createMissing} onChange={e => setCreateMissing(e.target.checked)}
              className="w-4 h-4 accent-orange-500" />
            <span className="text-sm text-gray-300">Create New Portfolios <span className="text-gray-500">(build for unmatched players)</span></span>
          </label>
          <label className="flex items-center gap-2 cursor-pointer">
            <input type="checkbox" checked={skipPublished} onChange={e => setSkipPublished(e.target.checked)}
              className="w-4 h-4 accent-orange-500" />
            <span className="text-sm text-gray-300">Skip Published/Premium <span className="text-gray-500">(safety)</span></span>
          </label>
        </div>

        {/* Run button */}
        <Button onClick={handleRun} disabled={loading || (!file && !fileUrl && !csvText)}
          className="w-full bg-orange-500 hover:bg-orange-600 text-black font-bold"
          size="lg">
          {loading ? (
            <><Loader2 className="w-4 h-4 animate-spin mr-2" /> Processing{dryRun ? ' Dry Run' : ''}... (this may take 30-60s for large files)</>
          ) : (
            <><Play className="w-4 h-4 mr-2" /> {dryRun ? 'Run Dry Run (Preview)' : 'Apply Updates to Portfolios'}</>
          )}
        </Button>

        {error && (
          <div className="flex items-start gap-2 p-3 rounded-lg bg-red-500/10 border border-red-500/20 text-red-400 text-sm">
            <AlertCircle className="w-4 h-4 shrink-0 mt-0.5" />
            <span>{error}</span>
          </div>
        )}
      </div>

      {/* Results */}
      {result && (
        <div className="space-y-4">
          {/* Summary banner */}
          <div className="bg-slate-900 rounded-xl border border-slate-800 p-6">
            <div className="flex items-center gap-2 mb-4">
              {result.dry_run ? (
                <span className="px-2 py-0.5 rounded text-[10px] font-black uppercase tracking-widest bg-yellow-500/20 text-yellow-400 border border-yellow-500/30">DRY RUN</span>
              ) : (
                <span className="px-2 py-0.5 rounded text-[10px] font-black uppercase tracking-widest bg-green-500/20 text-green-400 border border-green-500/30">APPLIED</span>
              )}
              <span className="text-gray-500 text-xs">{result.import_log_id ? `Log ID: ${result.import_log_id}` : ''}</span>
            </div>
            <p className="text-gray-300 text-sm mb-4">{result.summary}</p>

            <div className="grid grid-cols-2 sm:grid-cols-4 lg:grid-cols-8 gap-3">
              {[
                { label: 'Total Rows', value: result.total_rows, icon: FileSpreadsheet, color: 'text-white' },
                { label: 'Unique Players', value: result.unique_players, icon: Users, color: 'text-blue-400' },
                { label: 'Matched', value: result.matched, icon: CheckCircle2, color: 'text-green-400' },
                { label: result.dry_run ? 'Would Update' : 'Updated', value: result.dry_run ? result.matched : result.updated, icon: Play, color: 'text-orange-400' },
                { label: result.dry_run ? 'Would Create' : 'Created', value: result.created || 0, icon: Sparkles, color: 'text-emerald-400' },
                { label: 'Unmatched', value: result.unmatched, icon: XCircle, color: 'text-red-400' },
                { label: 'Ambiguous', value: result.ambiguous, icon: AlertTriangle, color: 'text-yellow-400' },
                { label: 'Skipped', value: result.skipped, icon: AlertCircle, color: 'text-gray-400' },
              ].map(s => (
                <div key={s.label} className="bg-slate-800/50 rounded-lg p-3 text-center">
                  <s.icon className={`w-4 h-4 mx-auto mb-1 ${s.color}`} />
                  <div className={`text-xl font-black ${s.color}`}>{s.value}</div>
                  <div className="text-[10px] text-gray-500 uppercase tracking-wide mt-0.5">{s.label}</div>
                </div>
              ))}
            </div>
          </div>

          {/* Tab selector */}
          <div className="flex gap-2">
            {[
              { id: 'matched', label: `Matched (${result.matched})`, icon: CheckCircle2, color: 'text-green-400' },
              { id: 'created', label: `Created (${result.created || 0})`, icon: Sparkles, color: 'text-emerald-400' },
              { id: 'errors', label: `Errors (${result.errors})`, icon: XCircle, color: 'text-red-400' },
              { id: 'skipped', label: `Skipped (${result.skipped})`, icon: AlertCircle, color: 'text-gray-400' },
            ].map(t => (
              <button key={t.id} onClick={() => setActiveView(t.id)}
                className={`flex items-center gap-1.5 px-4 py-2 rounded-lg text-sm font-medium transition-all
                  ${activeView === t.id ? 'bg-slate-800 text-white' : 'text-gray-500 hover:text-gray-300'}`}>
                <t.icon className={`w-3.5 h-3.5 ${t.color}`} />
                {t.label}
              </button>
            ))}
            {result.preview?.errors?.length > 0 && (
              <button onClick={downloadErrorLog}
                className="ml-auto flex items-center gap-1.5 px-4 py-2 rounded-lg text-sm font-medium bg-slate-800 text-orange-400 hover:bg-slate-700 transition-all">
                <Download className="w-3.5 h-3.5" /> Download Error Log
              </button>
            )}
          </div>

          {/* Tab content */}
          <div className="bg-slate-900 rounded-xl border border-slate-800 p-4 max-h-[500px] overflow-y-auto">
            {activeView === 'matched' && (
              <div className="space-y-2">
                {result.preview?.matched?.length > 0 ? result.preview.matched.map((m, i) => (
                  <div key={i} className="flex items-center gap-3 py-2 px-3 rounded-lg bg-slate-800/50 hover:bg-slate-800 transition-all">
                    <CheckCircle2 className="w-4 h-4 text-green-400 shrink-0" />
                    <div className="flex-1 min-w-0">
                      <span className="text-white text-sm font-medium">{m.player_name}</span>
                      <span className="text-gray-500 text-xs ml-2">CSV: "{m.csv_name}" · Team: {m.team}</span>
                    </div>
                    <span className="text-orange-400 text-xs font-bold shrink-0">{m.games} games</span>
                  </div>
                )) : <p className="text-gray-500 text-sm text-center py-8">No matched players.</p>}
                {result.matched > 30 && <p className="text-gray-600 text-xs text-center pt-2">Showing first 30 of {result.matched} matches. Full list saved in ImportLog.</p>}
              </div>
            )}

            {activeView === 'created' && (
              <div className="space-y-2">
                {result.preview?.created?.length > 0 ? result.preview.created.map((c, i) => (
                  <div key={i} className="flex items-center gap-3 py-2 px-3 rounded-lg bg-slate-800/50 hover:bg-slate-800 transition-all">
                    <Sparkles className="w-4 h-4 text-emerald-400 shrink-0" />
                    <div className="flex-1 min-w-0">
                      <span className="text-white text-sm font-medium">{c.player_name}</span>
                      <span className="text-gray-500 text-xs ml-2">Team: {c.team}{c.jersey ? ` · #${c.jersey}` : ''}</span>
                    </div>
                    <span className="text-emerald-400 text-xs font-bold shrink-0">{c.games} games</span>
                  </div>
                )) : <p className="text-gray-500 text-sm text-center py-8">No new portfolios to create.</p>}
                {result.created > 30 && <p className="text-gray-600 text-xs text-center pt-2">Showing first 30 of {result.created} created. Full list saved in ImportLog.</p>}
              </div>
            )}

            {activeView === 'errors' && (
              <div className="space-y-2">
                {result.preview?.errors?.length > 0 ? result.preview.errors.map((e, i) => (
                  <div key={i} className="py-2 px-3 rounded-lg bg-slate-800/50">
                    <div className="flex items-start gap-2">
                      {e.error?.includes('Ambiguous') ? <AlertTriangle className="w-4 h-4 text-yellow-400 shrink-0 mt-0.5" /> :
                       e.error?.includes('Missing') ? <AlertCircle className="w-4 h-4 text-orange-400 shrink-0 mt-0.5" /> :
                       <XCircle className="w-4 h-4 text-red-400 shrink-0 mt-0.5" />}
                      <div className="flex-1 min-w-0">
                        <div className="flex items-center gap-2 flex-wrap">
                          <span className="text-white text-sm font-medium">{e.player_name || 'Unknown'}</span>
                          {e.row && <span className="text-gray-600 text-xs">Row {e.row}</span>}
                          <span className="text-gray-500 text-xs">Team: {e.cleaned_team || e.team || '—'}</span>
                          {e.original_team && e.original_team !== e.cleaned_team && (
                            <span className="text-gray-600 text-xs">(orig: "{e.original_team}")</span>
                          )}
                        </div>
                        <p className="text-gray-400 text-xs mt-0.5">{e.error}</p>
                        {e.close_matches?.length > 0 && (
                          <p className="text-blue-400 text-xs mt-1">
                            Close matches: {e.close_matches.map(m => `${m.name} (${m.team})`).join(', ')}
                          </p>
                        )}
                        {e.matching_records?.length > 0 && (
                          <p className="text-yellow-400 text-xs mt-1">
                            Conflicting: {e.matching_records.map(m => `${m.name} (${m.team})`).join(', ')}
                          </p>
                        )}
                      </div>
                    </div>
                  </div>
                )) : <p className="text-gray-500 text-sm text-center py-8">No errors! 🎉</p>}
                {result.errors > 30 && <p className="text-gray-600 text-xs text-center pt-2">Showing first 30 of {result.errors} errors. Full list saved in ImportLog.</p>}
              </div>
            )}

            {activeView === 'skipped' && (
              <div className="space-y-2">
                {result.preview?.skipped?.length > 0 ? result.preview.skipped.map((s, i) => (
                  <div key={i} className="flex items-center gap-3 py-2 px-3 rounded-lg bg-slate-800/50">
                    <AlertCircle className="w-4 h-4 text-gray-400 shrink-0" />
                    <div className="flex-1 min-w-0">
                      <span className="text-white text-sm font-medium">{s.player_name}</span>
                      <span className="text-gray-500 text-xs ml-2">{s.reason}</span>
                    </div>
                  </div>
                )) : <p className="text-gray-500 text-sm text-center py-8">No skipped portfolios.</p>}
              </div>
            )}
          </div>

          {/* Apply button (if dry run had matches) */}
          {result.dry_run && (result.matched > 0 || result.created > 0) && (
            <div className="bg-green-500/10 border border-green-500/20 rounded-xl p-4 flex items-center justify-between gap-4">
              <div className="flex items-center gap-2">
                <CheckCircle2 className="w-5 h-5 text-green-400 shrink-0" />
                <p className="text-green-400 text-sm">
                  {result.matched > 0 && result.created > 0 ? (
                    <><strong>{result.matched}</strong> to update and <strong>{result.created}</strong> new portfolios to create</>
                  ) : result.matched > 0 ? (
                    <><strong>{result.matched}</strong> matched for update</>
                  ) : (
                    <><strong>{result.created}</strong> new portfolios to create</>
                  )} from <strong>{result.total_rows}</strong> rows of stats. Review above, then apply when ready.
                </p>
              </div>
              <Button onClick={() => { setDryRun(false); setTimeout(handleRun, 100); }}
                className="bg-green-600 hover:bg-green-700 text-white font-bold shrink-0"
                disabled={loading}>
                {loading ? <Loader2 className="w-4 h-4 animate-spin" /> : <Play className="w-4 h-4 mr-1" />}
                Apply
              </Button>
            </div>
          )}
        </div>
      )}
    </div>
  );
}

src/components/admin/AdminFeatureAccess.jsx

import React, { useState } from 'react';
import { base44 } from '@/api/base44Client';
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
import { Shield, BarChart3, Video, Radio, Users, BookOpen, UserCheck } from 'lucide-react';

const FEATURES = [
  { id: 'stat_game',         label: 'Stat Game (Tagging)',   desc: 'Tag game events with video timestamps',       icon: BarChart3, color: 'text-green-400' },
  { id: 'film_sessions',     label: 'Film Sessions',          desc: 'Create and view film sessions',               icon: Video,     color: 'text-cyan-400' },
  { id: 'live_streaming',    label: 'Live Streaming',         desc: 'Create and manage live streams',              icon: Radio,     color: 'text-red-400' },
  { id: 'stats_analytics',   label: 'Stats & Analytics',      desc: 'View player/game stats',                      icon: BarChart3, color: 'text-purple-400' },
  { id: 'team_management',   label: 'Team Management',        desc: 'Create and manage teams & rosters',           icon: Users,     color: 'text-blue-400' },
  { id: 'player_portfolios', label: 'Player Portfolios',      desc: 'View and manage athlete recruiting profiles', icon: UserCheck, color: 'text-orange-400' },
  { id: 'how_to_guide',      label: 'How-To Guide',           desc: 'Access platform guides',                      icon: BookOpen,  color: 'text-yellow-400' },
];

function Toggle({ checked, onChange }) {
  return (
    <button
      onClick={onChange}
      className={`relative w-10 h-5 rounded-full transition-colors ${checked ? 'bg-green-500' : 'bg-slate-600'}`}
    >
      <span className={`absolute top-0.5 left-0.5 w-4 h-4 bg-white rounded-full transition-transform ${checked ? 'translate-x-5' : ''}`} />
    </button>
  );
}

export default function AdminFeatureAccess() {
  const queryClient = useQueryClient();

  const { data: permissions = [] } = useQuery({
    queryKey: ['admin-permissions'],
    queryFn: () => base44.entities.AdminPermission.list(),
  });

  const getPermission = (featureId) =>
    permissions.find(p => p.feature === featureId) || {
      feature: featureId, players_visible: true, parents_visible: true,
      coaches_visible: true, scorers_visible: true
    };

  const toggleMutation = useMutation({
    mutationFn: async ({ featureId, field, value }) => {
      const existing = permissions.find(p => p.feature === featureId);
      if (existing) {
        await base44.entities.AdminPermission.update(existing.id, { [field]: value });
      } else {
        await base44.entities.AdminPermission.create({
          feature: featureId, players_visible: true, parents_visible: true,
          coaches_visible: true, scorers_visible: true, [field]: value
        });
      }
    },
    onSuccess: () => queryClient.invalidateQueries(['admin-permissions']),
  });

  const [activeTab, setActiveTab] = useState('feature_access');

  return (
    <div>
      <div className="flex items-center gap-3 mb-6">
        <div className="w-10 h-10 rounded-xl bg-orange-500/20 flex items-center justify-center">
          <Shield className="w-5 h-5 text-orange-400" />
        </div>
        <div>
          <h1 className="text-2xl font-black text-white">Admin Portal</h1>
          <p className="text-slate-400 text-sm">Manage feature access and user roles</p>
        </div>
      </div>

      <div className="flex gap-3 mb-6">
        <button
          onClick={() => setActiveTab('feature_access')}
          className={`px-4 py-2 rounded-lg text-sm font-semibold flex items-center gap-2 transition-all ${activeTab === 'feature_access' ? 'bg-orange-500 text-white' : 'bg-slate-800 text-slate-400 hover:text-white'}`}
        >
          <Shield className="w-4 h-4" /> Feature Access
        </button>
        <button
          onClick={() => setActiveTab('users_roles')}
          className={`px-4 py-2 rounded-lg text-sm font-semibold flex items-center gap-2 transition-all ${activeTab === 'users_roles' ? 'bg-orange-500 text-white' : 'bg-slate-800 text-slate-400 hover:text-white'}`}
        >
          <Users className="w-4 h-4" /> Users & Roles
        </button>
      </div>

      {activeTab === 'feature_access' && (
        <div>
          <p className="text-slate-400 text-sm mb-4">
            Control which features are visible to Players and Parents. Coaches, Scorers, and Filmers always have full access.
          </p>
          <div className="bg-slate-900 rounded-xl border border-slate-800 overflow-hidden">
            {/* Header row */}
            <div className="grid grid-cols-[1fr_100px_100px] gap-4 px-5 py-3 border-b border-slate-800 bg-slate-800/50">
              <span className="text-xs font-bold text-slate-400 uppercase tracking-wider">Feature</span>
              <span className="text-xs font-bold text-slate-400 uppercase tracking-wider text-center">Players</span>
              <span className="text-xs font-bold text-slate-400 uppercase tracking-wider text-center">Parents</span>
            </div>

            {FEATURES.map((feature, idx) => {
              const Icon = feature.icon;
              const perm = getPermission(feature.id);
              return (
                <div key={feature.id}
                  className={`grid grid-cols-[1fr_100px_100px] gap-4 px-5 py-4 items-center ${idx < FEATURES.length - 1 ? 'border-b border-slate-800' : ''}`}
                >
                  <div className="flex items-center gap-3">
                    <div className="w-8 h-8 rounded-lg bg-slate-800 flex items-center justify-center">
                      <Icon className={`w-4 h-4 ${feature.color}`} />
                    </div>
                    <div>
                      <p className="text-white text-sm font-semibold">{feature.label}</p>
                      <p className="text-slate-500 text-xs">{feature.desc}</p>
                    </div>
                  </div>
                  <div className="flex flex-col items-center gap-1">
                    <Toggle
                      checked={perm.players_visible}
                      onChange={() => toggleMutation.mutate({ featureId: feature.id, field: 'players_visible', value: !perm.players_visible })}
                    />
                    <span className={`text-[10px] font-semibold ${perm.players_visible ? 'text-green-400' : 'text-slate-500'}`}>
                      {perm.players_visible ? 'Visible' : 'Hidden'}
                    </span>
                  </div>
                  <div className="flex flex-col items-center gap-1">
                    <Toggle
                      checked={perm.parents_visible}
                      onChange={() => toggleMutation.mutate({ featureId: feature.id, field: 'parents_visible', value: !perm.parents_visible })}
                    />
                    <span className={`text-[10px] font-semibold ${perm.parents_visible ? 'text-green-400' : 'text-slate-500'}`}>
                      {perm.parents_visible ? 'Visible' : 'Hidden'}
                    </span>
                  </div>
                </div>
              );
            })}
          </div>
        </div>
      )}

      {activeTab === 'users_roles' && (
        <div className="bg-slate-900 rounded-xl border border-slate-800 p-8 text-center">
          <Users className="w-12 h-12 text-slate-600 mx-auto mb-3" />
          <p className="text-slate-400">User role management is handled through the Base44 admin panel.</p>
          <p className="text-slate-500 text-sm mt-2">Roles: admin, coach, scorer, player, parent</p>
        </div>
      )}
    </div>
  );
}

src/components/admin/AdminFilm.jsx

import React, { useState } from 'react';
import { base44 } from '@/api/base44Client';
import { useQuery } from '@tanstack/react-query';
import { Video, ExternalLink, Play } from 'lucide-react';
import { Button } from '@/components/ui/button';
import { format } from 'date-fns';
import { useNavigate } from 'react-router-dom';

export default function AdminFilm() {
  const navigate = useNavigate();
  const { data: games = [] } = useQuery({ queryKey: ['hoop-games'], queryFn: () => base44.entities.HoopGame.list('-game_date', 50) });
  const { data: teams = [] } = useQuery({ queryKey: ['hoop-teams'], queryFn: () => base44.entities.HoopTeam.list() });

  const gamesWithFilm = games.filter(g => g.video_url);
  const getTeam = (id) => teams.find(t => t.id === id);

  return (
    <div>
      <div className="flex items-center justify-between mb-6">
        <div>
          <h1 className="text-2xl font-black text-white">Film Sessions</h1>
          <p className="text-slate-400 text-sm">Create and organize video clips for review</p>
        </div>
        <Button onClick={() => navigate('/hoop-tagging')} className="bg-cyan-500 hover:bg-cyan-600 gap-2">
          <Play className="w-4 h-4" /> Open Tagger
        </Button>
      </div>

      {gamesWithFilm.length === 0 ? (
        <div className="bg-slate-900/50 rounded-xl border border-dashed border-slate-700 p-16 text-center">
          <Video className="w-16 h-16 text-slate-600 mx-auto mb-4" />
          <p className="text-slate-400 mb-2">No clips created yet</p>
          <p className="text-slate-500 text-sm mb-6">Upload game film in the Stat Game tab to start tagging</p>
          <Button onClick={() => navigate('/hoop-tagging')} className="bg-cyan-500 hover:bg-cyan-600">
            Create Your First Clip
          </Button>
        </div>
      ) : (
        <div className="grid sm:grid-cols-2 lg:grid-cols-3 gap-4">
          {gamesWithFilm.map(game => {
            const team = getTeam(game.team_id);
            return (
              <div key={game.id} className="bg-slate-900 rounded-xl border border-slate-800 overflow-hidden hover:border-slate-600 transition-all group">
                <div className="aspect-video bg-slate-800 relative">
                  <video src={game.video_url} className="w-full h-full object-cover" />
                  <div className="absolute inset-0 bg-black/40 flex items-center justify-center opacity-0 group-hover:opacity-100 transition-opacity">
                    <button onClick={() => navigate(`/hoop-tagging?id=${game.id}`)}
                      className="w-12 h-12 bg-white/20 rounded-full flex items-center justify-center backdrop-blur-sm hover:bg-white/30">
                      <Play className="w-5 h-5 text-white" />
                    </button>
                  </div>
                </div>
                <div className="p-4">
                  <p className="text-white font-bold text-sm">{team?.name} vs {game.opponent}</p>
                  <p className="text-slate-400 text-xs mt-1">
                    {game.game_date ? format(new Date(game.game_date), 'MMM d, yyyy') : '—'}
                  </p>
                  <div className="flex gap-2 mt-3">
                    <Button size="sm" onClick={() => navigate(`/hoop-tagging?id=${game.id}`)}
                      className="flex-1 bg-cyan-500 hover:bg-cyan-600 text-xs">
                      Open in Tagger
                    </Button>
                    <button onClick={() => window.open(game.video_url, '_blank')}
                      className="p-2 rounded-lg bg-slate-800 text-slate-400 hover:text-white hover:bg-slate-700 transition-all">
                      <ExternalLink className="w-3.5 h-3.5" />
                    </button>
                  </div>
                </div>
              </div>
            );
          })}
        </div>
      )}
    </div>
  );
}

src/components/admin/AdminGeorgiaFilmPush.jsx

import { useState, useEffect } from 'react';
import { base44 } from '@/api/base44Client';
import { Film, CheckCircle2, AlertCircle, Loader2 } from 'lucide-react';

const normalize = (name) =>
  name?.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/(^-|-$)/g, '');

export default function AdminGeorgiaFilmPush() {
  const [teams, setTeams] = useState([]);
  const [loading, setLoading] = useState(true);
  const [pushing, setPushing] = useState(null); // team_slug currently being pushed
  const [results, setResults] = useState({}); // team_slug -> result

  useEffect(() => {
    const load = async () => {
      const games = await base44.entities.GeorgiaGame.list();
      // Collect unique team names from all games
      const teamNames = new Set();
      games.forEach((g) => {
        if (g.team1) teamNames.add(g.team1);
        if (g.team2) teamNames.add(g.team2);
      });

      // Build { name, slug, game_count, film_count }
      const teamList = Array.from(teamNames)
        .map((name) => {
          const slug = normalize(name);
          const teamGames = games.filter(
            (g) => normalize(g.team1) === slug || normalize(g.team2) === slug
          );
          return {
            name,
            slug,
            game_count: teamGames.length,
            film_count: teamGames.filter((g) => g.embed_link?.trim()).length,
          };
        })
        .sort((a, b) => a.name.localeCompare(b.name));

      setTeams(teamList);
      setLoading(false);
    };
    load();
  }, []);

  const handlePush = async (team) => {
    setPushing(team.slug);
    try {
      const res = await base44.functions.invoke('pushGameFilmToPlayers', { team_slug: team.slug });
      setResults((prev) => ({ ...prev, [team.slug]: res.data }));
    } catch (e) {
      setResults((prev) => ({ ...prev, [team.slug]: { error: e.message } }));
    }
    setPushing(null);
  };

  if (loading) {
    return (
      <div className="flex items-center gap-2 text-slate-400 py-8">
        <Loader2 className="w-4 h-4 animate-spin" /> Loading teams…
      </div>
    );
  }

  return (
    <div className="space-y-4">
      <div>
        <h2 className="text-white font-black text-lg">Push Game Film to Player Portfolios</h2>
        <p className="text-slate-400 text-sm mt-1">
          For each team, this creates a boxscore entry on every matching Player portfolio with the
          full game film link in the <strong>Film</strong> field. Already-pushed games are skipped
          (idempotent).
        </p>
      </div>

      <div className="space-y-2">
        {teams.map((team) => {
          const result = results[team.slug];
          const isPushing = pushing === team.slug;

          return (
            <div
              key={team.slug}
              className="flex items-center gap-4 bg-slate-900 border border-slate-800 rounded-xl px-4 py-3"
            >
              {/* Team info */}
              <div className="flex-1 min-w-0">
                <p className="text-white font-semibold text-sm truncate">{team.name}</p>
                <p className="text-slate-500 text-xs">
                  {team.game_count} game{team.game_count !== 1 ? 's' : ''} ·{' '}
                  {team.film_count} with film
                </p>
              </div>

              {/* Result */}
              {result && !result.error && (
                <div className="flex items-center gap-1.5 text-xs text-emerald-400 shrink-0">
                  <CheckCircle2 className="w-3.5 h-3.5" />
                  {result.entries_pushed} pushed · {result.players_updated} players
                </div>
              )}
              {result?.error && (
                <div className="flex items-center gap-1.5 text-xs text-red-400 shrink-0">
                  <AlertCircle className="w-3.5 h-3.5" />
                  {result.error}
                </div>
              )}

              {/* Push button */}
              <button
                onClick={() => handlePush(team)}
                disabled={isPushing || team.film_count === 0}
                className="flex items-center gap-1.5 px-3 py-1.5 rounded-lg text-xs font-bold transition-all disabled:opacity-40 disabled:cursor-not-allowed shrink-0"
                style={{ background: 'rgba(255,106,0,0.15)', color: '#FF6A00', border: '1px solid rgba(255,106,0,0.3)' }}
              >
                {isPushing ? (
                  <Loader2 className="w-3.5 h-3.5 animate-spin" />
                ) : (
                  <Film className="w-3.5 h-3.5" />
                )}
                {isPushing ? 'Pushing…' : 'Push Film'}
              </button>
            </div>
          );
        })}
      </div>
    </div>
  );
}

src/components/admin/AdminGeorgiaGames.jsx

import { useState, useEffect } from 'react';
import { base44 } from '@/api/base44Client';
import { Edit2, Trash2, Save, X, Loader2, Plus } from 'lucide-react';

const ORANGE = '#FF6A00';

export default function AdminGeorgiaGames() {
  const [games, setGames] = useState([]);
  const [loading, setLoading] = useState(true);
  const [editingId, setEditingId] = useState(null);
  const [editData, setEditData] = useState({});
  const [saving, setSaving] = useState(false);
  const [deleting, setDeleting] = useState(null);
  const [creatingNew, setCreatingNew] = useState(false);
  const [newGameData, setNewGameData] = useState({
    team1: '',
    team2: '',
    date: '',
    court: '',
    game_number: '',
    video_id: '',
    embed_link: '',
    team_stats: {},
  });

  useEffect(() => {
    base44.entities.GeorgiaGame.list().then(data => {
      setGames(data || []);
      setLoading(false);
    });
  }, []);

  const startEdit = (game) => {
    setEditingId(game.id);
    setEditData({
      date: game.date || '',
      court: game.court || '',
      game_number: game.game_number || '',
      video_id: game.video_id || '',
      embed_link: game.embed_link || '',
      team_stats: game.team_stats || {},
    });
  };

  const cancelEdit = () => {
    setEditingId(null);
    setEditData({});
  };

  const saveGame = async (gameId) => {
    setSaving(true);
    try {
      await base44.entities.GeorgiaGame.update(gameId, editData);
      setGames(prev => prev.map(g => g.id === gameId ? { ...g, ...editData } : g));
      setEditingId(null);
      setEditData({});
    } catch (e) {
      alert('Error saving: ' + e.message);
    }
    setSaving(false);
  };

  const deleteGame = async (gameId) => {
    setDeleting(gameId);
    try {
      await base44.entities.GeorgiaGame.delete(gameId);
      setGames(prev => prev.filter(g => g.id !== gameId));
    } catch (e) {
      alert('Error deleting: ' + e.message);
    }
    setDeleting(null);
  };

  const createNewGame = async () => {
    if (!newGameData.team1 || !newGameData.team2 || !newGameData.embed_link) {
      alert('Team1, Team2, and Embed Link are required');
      return;
    }
    setSaving(true);
    try {
      const created = await base44.entities.GeorgiaGame.create({
        team1: newGameData.team1,
        team2: newGameData.team2,
        date: newGameData.date || null,
        court: newGameData.court || null,
        game_number: newGameData.game_number || null,
        video_id: newGameData.video_id || null,
        embed_link: newGameData.embed_link,
        team_stats: newGameData.team_stats,
      });
      setGames(prev => [...prev, created]);
      setCreatingNew(false);
      setNewGameData({
        team1: '',
        team2: '',
        date: '',
        court: '',
        game_number: '',
        video_id: '',
        embed_link: '',
        team_stats: {},
      });
    } catch (e) {
      alert('Error creating game: ' + e.message);
    }
    setSaving(false);
  };

  return (
    <div>
      <div className="flex items-center justify-between mb-6">
        <div>
          <h1 className="text-2xl font-black text-white">Georgia Games</h1>
          <p className="text-slate-400 text-sm mt-1">Manage game films, dates, courts, and embed links</p>
        </div>
        <button
          onClick={() => setCreatingNew(true)}
          className="flex items-center gap-2 px-4 py-2 rounded-lg text-sm font-bold text-white transition-all hover:opacity-90"
          style={{ background: ORANGE }}
        >
          <Plus className="w-4 h-4" />
          New Game
        </button>
      </div>

      {/* Create New Game Modal */}
      {creatingNew && (
        <div className="fixed inset-0 z-50 flex items-center justify-center bg-black/50 backdrop-blur-sm">
          <div className="bg-slate-900 border border-slate-800 rounded-xl p-6 max-w-sm mx-4 max-h-[90vh] overflow-y-auto">
            <h2 className="text-white font-black text-lg mb-4">Create New Game</h2>
            <div className="space-y-3">
              <div>
                <label className="text-xs text-slate-400 font-semibold">Team 1 *</label>
                <input
                  type="text"
                  value={newGameData.team1}
                  onChange={e => setNewGameData(prev => ({ ...prev, team1: e.target.value }))}
                  placeholder="e.g., Alpharetta"
                  className="w-full text-xs px-2 py-1.5 rounded bg-slate-800 border border-slate-700 text-white placeholder-slate-600 focus:outline-none focus:border-orange-500"
                />
              </div>
              <div>
                <label className="text-xs text-slate-400 font-semibold">Team 2 *</label>
                <input
                  type="text"
                  value={newGameData.team2}
                  onChange={e => setNewGameData(prev => ({ ...prev, team2: e.target.value }))}
                  placeholder="e.g., Chamblee"
                  className="w-full text-xs px-2 py-1.5 rounded bg-slate-800 border border-slate-700 text-white placeholder-slate-600 focus:outline-none focus:border-orange-500"
                />
              </div>
              <div>
                <label className="text-xs text-slate-400 font-semibold">Date</label>
                <input
                  type="date"
                  value={newGameData.date}
                  onChange={e => setNewGameData(prev => ({ ...prev, date: e.target.value }))}
                  className="w-full text-xs px-2 py-1.5 rounded bg-slate-800 border border-slate-700 text-white focus:outline-none focus:border-orange-500"
                />
              </div>
              <div>
                <label className="text-xs text-slate-400 font-semibold">Court</label>
                <input
                  type="text"
                  value={newGameData.court}
                  onChange={e => setNewGameData(prev => ({ ...prev, court: e.target.value }))}
                  placeholder="e.g., UGA Coliseum"
                  className="w-full text-xs px-2 py-1.5 rounded bg-slate-800 border border-slate-700 text-white placeholder-slate-600 focus:outline-none focus:border-orange-500"
                />
              </div>
              <div>
                <label className="text-xs text-slate-400 font-semibold">Game #</label>
                <input
                  type="text"
                  value={newGameData.game_number}
                  onChange={e => setNewGameData(prev => ({ ...prev, game_number: e.target.value }))}
                  placeholder="e.g., G001"
                  className="w-full text-xs px-2 py-1.5 rounded bg-slate-800 border border-slate-700 text-white placeholder-slate-600 focus:outline-none focus:border-orange-500"
                />
              </div>
              <div>
                <label className="text-xs text-slate-400 font-semibold">Video ID</label>
                <input
                  type="text"
                  value={newGameData.video_id}
                  onChange={e => setNewGameData(prev => ({ ...prev, video_id: e.target.value }))}
                  placeholder="YouTube/Vimeo ID"
                  className="w-full text-xs px-2 py-1.5 rounded bg-slate-800 border border-slate-700 text-white placeholder-slate-600 focus:outline-none focus:border-orange-500"
                />
              </div>
              <div>
                <label className="text-xs text-slate-400 font-semibold">Embed Link *</label>
                <input
                  type="text"
                  value={newGameData.embed_link}
                  onChange={e => setNewGameData(prev => ({ ...prev, embed_link: e.target.value }))}
                  placeholder="Full embed URL"
                  className="w-full text-xs px-2 py-1.5 rounded bg-slate-800 border border-slate-700 text-white placeholder-slate-600 focus:outline-none focus:border-orange-500"
                />
              </div>
              <div className="grid grid-cols-2 gap-2">
                <div>
                  <label className="text-xs text-slate-400 font-semibold">STL (Team 1)</label>
                  <input
                    type="number"
                    value={newGameData.team_stats?.team1_stl || ''}
                    onChange={e => setNewGameData(prev => ({ ...prev, team_stats: { ...prev.team_stats, team1_stl: e.target.value } }))}
                    placeholder="0"
                    className="w-full text-xs px-2 py-1.5 rounded bg-slate-800 border border-slate-700 text-white placeholder-slate-600 focus:outline-none focus:border-orange-500"
                  />
                </div>
                <div>
                  <label className="text-xs text-slate-400 font-semibold">STL (Team 2)</label>
                  <input
                    type="number"
                    value={newGameData.team_stats?.team2_stl || ''}
                    onChange={e => setNewGameData(prev => ({ ...prev, team_stats: { ...prev.team_stats, team2_stl: e.target.value } }))}
                    placeholder="0"
                    className="w-full text-xs px-2 py-1.5 rounded bg-slate-800 border border-slate-700 text-white placeholder-slate-600 focus:outline-none focus:border-orange-500"
                  />
                </div>
              </div>
              <div className="grid grid-cols-2 gap-2">
                <div>
                  <label className="text-xs text-slate-400 font-semibold">BLK (Team 1)</label>
                  <input
                    type="number"
                    value={newGameData.team_stats?.team1_blk || ''}
                    onChange={e => setNewGameData(prev => ({ ...prev, team_stats: { ...prev.team_stats, team1_blk: e.target.value } }))}
                    placeholder="0"
                    className="w-full text-xs px-2 py-1.5 rounded bg-slate-800 border border-slate-700 text-white placeholder-slate-600 focus:outline-none focus:border-orange-500"
                  />
                </div>
                <div>
                  <label className="text-xs text-slate-400 font-semibold">BLK (Team 2)</label>
                  <input
                    type="number"
                    value={newGameData.team_stats?.team2_blk || ''}
                    onChange={e => setNewGameData(prev => ({ ...prev, team_stats: { ...prev.team_stats, team2_blk: e.target.value } }))}
                    placeholder="0"
                    className="w-full text-xs px-2 py-1.5 rounded bg-slate-800 border border-slate-700 text-white placeholder-slate-600 focus:outline-none focus:border-orange-500"
                  />
                </div>
              </div>
              </div>
              <div className="flex gap-2 mt-6">
              <button
                onClick={createNewGame}
                disabled={saving}
                className="flex-1 flex items-center justify-center gap-1 px-3 py-2 rounded-lg text-xs font-bold text-white disabled:opacity-50"
                style={{ background: ORANGE }}
              >
                {saving ? <Loader2 className="w-3 h-3 animate-spin" /> : <Plus className="w-3 h-3" />}
                Create
              </button>
              <button
                onClick={() => setCreatingNew(false)}
                disabled={saving}
                className="flex-1 flex items-center justify-center gap-1 px-3 py-2 rounded-lg text-xs font-bold text-slate-400 hover:text-white bg-slate-800 hover:bg-slate-700 disabled:opacity-50"
              >
                <X className="w-3 h-3" />
                Cancel
              </button>
            </div>
          </div>
        </div>
      )}

      {loading ? (
        <div className="flex justify-center py-12">
          <div className="w-8 h-8 border-2 border-t-transparent rounded-full animate-spin" style={{ borderColor: ORANGE }} />
        </div>
      ) : (
        <div className="grid sm:grid-cols-2 lg:grid-cols-3 gap-3">
          {games.map(game => (
            <div
              key={game.id}
              className="rounded-xl border p-4 transition-all"
              style={{
                background: editingId === game.id ? 'rgba(255,106,0,0.05)' : '#0a0a0a',
                borderColor: editingId === game.id ? 'rgba(255,106,0,0.3)' : 'rgba(255,255,255,0.07)',
              }}
            >
              {editingId === game.id ? (
                // Edit mode
                <div className="space-y-3">
                  <div>
                    <label className="text-xs text-slate-400 font-semibold">Date</label>
                    <input
                      type="date"
                      value={editData.date}
                      onChange={e => setEditData(prev => ({ ...prev, date: e.target.value }))}
                      className="w-full text-xs px-2 py-1.5 rounded bg-slate-800 border border-slate-700 text-white focus:outline-none focus:border-orange-500"
                    />
                  </div>
                  <div>
                    <label className="text-xs text-slate-400 font-semibold">Court</label>
                    <input
                      type="text"
                      value={editData.court}
                      onChange={e => setEditData(prev => ({ ...prev, court: e.target.value }))}
                      placeholder="e.g., UGA Coliseum"
                      className="w-full text-xs px-2 py-1.5 rounded bg-slate-800 border border-slate-700 text-white placeholder-slate-600 focus:outline-none focus:border-orange-500"
                    />
                  </div>
                  <div>
                    <label className="text-xs text-slate-400 font-semibold">Game #</label>
                    <input
                      type="text"
                      value={editData.game_number}
                      onChange={e => setEditData(prev => ({ ...prev, game_number: e.target.value }))}
                      placeholder="e.g., G001"
                      className="w-full text-xs px-2 py-1.5 rounded bg-slate-800 border border-slate-700 text-white placeholder-slate-600 focus:outline-none focus:border-orange-500"
                    />
                  </div>
                  <div>
                    <label className="text-xs text-slate-400 font-semibold">Video ID</label>
                    <input
                      type="text"
                      value={editData.video_id}
                      onChange={e => setEditData(prev => ({ ...prev, video_id: e.target.value }))}
                      placeholder="YouTube/Vimeo ID"
                      className="w-full text-xs px-2 py-1.5 rounded bg-slate-800 border border-slate-700 text-white placeholder-slate-600 focus:outline-none focus:border-orange-500"
                    />
                  </div>
                  <div>
                    <label className="text-xs text-slate-400 font-semibold">Embed Link</label>
                    <input
                      type="text"
                      value={editData.embed_link}
                      onChange={e => setEditData(prev => ({ ...prev, embed_link: e.target.value }))}
                      placeholder="Full embed URL"
                      className="w-full text-xs px-2 py-1.5 rounded bg-slate-800 border border-slate-700 text-white placeholder-slate-600 focus:outline-none focus:border-orange-500"
                    />
                  </div>
                  <div className="grid grid-cols-2 gap-2">
                    <div>
                      <label className="text-xs text-slate-400 font-semibold">STL (Team 1)</label>
                      <input
                        type="number"
                        value={editData.team_stats?.team1_stl || ''}
                        onChange={e => setEditData(prev => ({ ...prev, team_stats: { ...prev.team_stats, team1_stl: e.target.value } }))}
                        placeholder="0"
                        className="w-full text-xs px-2 py-1.5 rounded bg-slate-800 border border-slate-700 text-white placeholder-slate-600 focus:outline-none focus:border-orange-500"
                      />
                    </div>
                    <div>
                      <label className="text-xs text-slate-400 font-semibold">STL (Team 2)</label>
                      <input
                        type="number"
                        value={editData.team_stats?.team2_stl || ''}
                        onChange={e => setEditData(prev => ({ ...prev, team_stats: { ...prev.team_stats, team2_stl: e.target.value } }))}
                        placeholder="0"
                        className="w-full text-xs px-2 py-1.5 rounded bg-slate-800 border border-slate-700 text-white placeholder-slate-600 focus:outline-none focus:border-orange-500"
                      />
                    </div>
                  </div>
                  <div className="grid grid-cols-2 gap-2">
                    <div>
                      <label className="text-xs text-slate-400 font-semibold">BLK (Team 1)</label>
                      <input
                        type="number"
                        value={editData.team_stats?.team1_blk || ''}
                        onChange={e => setEditData(prev => ({ ...prev, team_stats: { ...prev.team_stats, team1_blk: e.target.value } }))}
                        placeholder="0"
                        className="w-full text-xs px-2 py-1.5 rounded bg-slate-800 border border-slate-700 text-white placeholder-slate-600 focus:outline-none focus:border-orange-500"
                      />
                    </div>
                    <div>
                      <label className="text-xs text-slate-400 font-semibold">BLK (Team 2)</label>
                      <input
                        type="number"
                        value={editData.team_stats?.team2_blk || ''}
                        onChange={e => setEditData(prev => ({ ...prev, team_stats: { ...prev.team_stats, team2_blk: e.target.value } }))}
                        placeholder="0"
                        className="w-full text-xs px-2 py-1.5 rounded bg-slate-800 border border-slate-700 text-white placeholder-slate-600 focus:outline-none focus:border-orange-500"
                      />
                    </div>
                  </div>
                  <div className="flex gap-2 pt-2">
                    <button
                      onClick={() => saveGame(game.id)}
                      disabled={saving}
                      className="flex-1 flex items-center justify-center gap-1 px-3 py-2 rounded-lg text-xs font-bold text-white disabled:opacity-50"
                      style={{ background: ORANGE }}
                    >
                      {saving ? <Loader2 className="w-3 h-3 animate-spin" /> : <Save className="w-3 h-3" />}
                      Save
                    </button>
                    <button
                      onClick={cancelEdit}
                      className="flex-1 flex items-center justify-center gap-1 px-3 py-2 rounded-lg text-xs font-bold text-slate-400 hover:text-white bg-slate-800 hover:bg-slate-700"
                    >
                      <X className="w-3 h-3" />
                      Cancel
                    </button>
                  </div>
                </div>
              ) : (
                // View mode
                <div>
                  <p className="text-white font-bold text-sm mb-2">
                    {game.team1} vs {game.team2}
                  </p>
                  <div className="space-y-1 text-xs text-slate-400 mb-3">
                    {game.date && <p>📅 {game.date}</p>}
                    {game.court && <p>🏀 {game.court}</p>}
                    {game.game_number && <p>#{game.game_number}</p>}
                  </div>
                  <div className="flex gap-2">
                    <button
                      onClick={() => startEdit(game)}
                      className="flex-1 flex items-center justify-center gap-1 px-3 py-1.5 rounded-lg text-xs font-bold text-white transition-all hover:opacity-90"
                      style={{ background: ORANGE }}
                    >
                      <Edit2 className="w-3 h-3" />
                      Edit
                    </button>
                    <button
                      onClick={() => deleteGame(game.id)}
                      disabled={deleting === game.id}
                      className="flex-1 flex items-center justify-center gap-1 px-3 py-1.5 rounded-lg text-xs font-bold text-red-400 hover:text-red-300 bg-red-950/20 hover:bg-red-950/30 disabled:opacity-50 transition-all"
                    >
                      {deleting === game.id ? <Loader2 className="w-3 h-3 animate-spin" /> : <Trash2 className="w-3 h-3" />}
                      Delete
                    </button>
                  </div>
                </div>
              )}
            </div>
          ))}
        </div>
      )}

      {!loading && games.length === 0 && (
        <div className="text-center py-12">
          <p className="text-slate-500">No games found</p>
        </div>
      )}
    </div>
  );
}

src/components/admin/AdminGeorgiaLocks.jsx

import { useState, useEffect } from 'react';
import { base44 } from '@/api/base44Client';
import { Lock, Unlock, Search } from 'lucide-react';
import { Link } from 'react-router-dom';

function teamSlug(name) {
  return name.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/(^-|-$)/g, '');
}

const ALL_TEAMS = [
  'Alpharetta', 'Apalachee', 'Baldwin', 'Beach', 'Berkmar', 'BEST Academy', 'Bowdon',
  'Bradwell Institute', 'Brookwood', 'Brunswick', 'Buford', 'Burke County', 'Butler',
  'Campbell', 'Carrollton', 'Cartersville', 'Cedar Grove', 'Cedar Shoals', 'Centennial',
  'Chamblee', 'Chapel Hill', 'Cherokee', 'Christian Heritage', 'Columbia', 'Cross Creek',
  'Dacula', 'Darlington School', 'Douglas County', 'East Coweta', 'East Forsyth',
  'East Paulding', 'Eastside', 'ELCA', 'Franklin County', 'Gainesville', 'Alexander',
  'Grayson', 'Greater Atlanta Christian', 'Habersham Central', 'Harlem', 'Hebron Christian',
  'Hillgrove', 'Holy Innocents', 'Jonesboro', 'Kell', 'Lanier', 'Lassiter', 'Lee County',
  'Lovett School', 'Madison County', 'Mill Creek', 'Milton', 'Mitchell County',
  'Mountain View', 'New Manchester', 'Newton', 'North Atlanta', 'North Gwinnett',
  'North Oconee', 'North Springs', 'Osborne', 'Parkview', 'Pebblebrook', 'Peachtree Ridge',
  'Salem', 'Sandy Creek', 'Spalding', 'Sprayberry', 'St. Pius X', 'Starrs Mill',
  'Sumter County', 'Thomson', 'Tucker', 'Vidalia', 'Walton', 'Ware County', 'West Forsyth',
  'Westlake', 'Westminster', 'Wheeler', 'Whitefield Academy', 'Whitfield', 'Woodstock',
  'Woodward Academy'
];

export default function AdminGeorgiaLocks() {
  const [lockedTeams, setLockedTeams] = useState([]);
  const [search, setSearch] = useState('');
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    const load = async () => {
      try {
        const perms = await base44.asServiceRole.entities.AdminPermission.filter({ feature: 'georgia_locks' });
        if (perms?.length) {
          setLockedTeams(perms[0].locked_teams || ALL_TEAMS.map(t => teamSlug(t)));
        } else {
          // Default: all teams locked
          setLockedTeams(ALL_TEAMS.map(t => teamSlug(t)));
          await base44.asServiceRole.entities.AdminPermission.create({ 
            feature: 'georgia_locks', 
            locked_teams: ALL_TEAMS.map(t => teamSlug(t))
          });
        }
      } catch (e) {
        console.error(e);
        setLockedTeams(ALL_TEAMS.map(t => teamSlug(t)));
      }
      setLoading(false);
    };
    load();
  }, []);

  const toggleLock = async (slug) => {
    const isLocked = lockedTeams.includes(slug);
    const updated = isLocked 
      ? lockedTeams.filter(t => t !== slug)
      : [...lockedTeams, slug];
    
    setLockedTeams(updated);
    try {
      const existing = await base44.asServiceRole.entities.AdminPermission.filter({ feature: 'georgia_locks' });
      if (existing?.length) {
        await base44.asServiceRole.entities.AdminPermission.update(existing[0].id, { locked_teams: updated });
      } else {
        await base44.asServiceRole.entities.AdminPermission.create({ feature: 'georgia_locks', locked_teams: updated });
      }
    } catch (e) {
      console.error('Toggle failed:', e);
      setLockedTeams(lockedTeams);
    }
  };

  const filtered = ALL_TEAMS
    .filter(t => t.toLowerCase().includes(search.toLowerCase()))
    .sort((a, b) => a.localeCompare(b));

  if (loading) return <div className="text-slate-400">Loading...</div>;

  return (
    <div>
      <div className="flex items-center justify-between mb-6">
        <div>
          <h1 className="text-2xl font-black text-white">Georgia Team Access</h1>
          <p className="text-slate-400 text-sm mt-1">{ALL_TEAMS.length} teams · {lockedTeams.length} locked</p>
        </div>
      </div>

      {/* Search */}
      <div className="relative mb-6">
        <Search className="absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 text-slate-600" />
        <input
          type="text"
          placeholder="Search teams..."
          value={search}
          onChange={e => setSearch(e.target.value)}
          className="w-full pl-9 pr-4 py-3 rounded-xl text-sm text-white bg-white/5 border border-white/10 focus:outline-none focus:border-orange-500/50 placeholder-slate-600"
        />
      </div>

      {/* Team cards grid */}
      <div className="grid sm:grid-cols-2 lg:grid-cols-3 gap-3">
        {filtered.map(team => {
          const slug = teamSlug(team);
          const isLocked = lockedTeams.includes(slug);
          return (
            <div key={slug} className="flex flex-col rounded-xl border overflow-hidden" style={{ background: '#0a0a0a', borderColor: 'rgba(255,255,255,0.07)' }}>
              {/* Top row: team info */}
              <div className="flex items-center gap-4 px-4 py-4">
                <div className="w-10 h-10 rounded-full flex items-center justify-center font-barlow font-black text-lg shrink-0" style={{ background: 'rgba(255,106,0,0.1)', color: '#FF6A00' }}>
                  {team[0]}
                </div>
                <div className="flex-1 min-w-0">
                  <p className="font-bold text-white text-sm truncate">{team}</p>
                  <p className="text-xs text-slate-600">{isLocked ? 'Locked' : 'Public'}</p>
                </div>
                <button
                  onClick={() => toggleLock(slug)}
                  className="flex items-center justify-center w-9 h-9 rounded-lg transition-all shrink-0"
                  style={{ background: isLocked ? 'rgba(255,59,48,0.15)' : 'rgba(0,255,133,0.15)' }}>
                  {isLocked ? (
                    <Lock className="w-4 h-4" style={{ color: '#FF3B30' }} />
                  ) : (
                    <Unlock className="w-4 h-4" style={{ color: '#00FF85' }} />
                  )}
                </button>
              </div>
              {/* Bottom CTA */}
              <div className="px-4 py-2.5 text-xs text-slate-600 text-center" style={{ borderTop: '1px solid rgba(255,255,255,0.05)' }}>
                <Link to={`/georgia-team/${slug}`} className="hover:text-white transition-colors">View team page →</Link>
              </div>
            </div>
          );
        })}
      </div>

      {filtered.length === 0 && (
        <div className="text-center py-16">
          <p className="text-slate-500">No teams match "{search}"</p>
        </div>
      )}
    </div>
  );
}

src/components/admin/AdminGeorgiaStats.jsx

import { useState, useRef } from 'react';
import { base44 } from '@/api/base44Client';
import { CheckCircle2, AlertCircle, Loader2, Play, Upload, X, FileText, Plus } from 'lucide-react';

// Hardcoded pre-loaded PDFs
const PRELOADED_PDFS = [
  { team: 'Alpharetta', url: 'https://media.base44.com/files/public/69bb16a38ac0086814d94e6f/f2317d93a_Alpharetta-GAAlpharetta.pdf' },
  { team: 'Apalachee', url: 'https://media.base44.com/files/public/69bb16a38ac0086814d94e6f/b7a649c43_Apalachee-GAApalachee-GA.pdf' },
  { team: 'Baldwin', url: 'https://media.base44.com/files/public/69bb16a38ac0086814d94e6f/6c2bbf8b0_Baldwin-GABaldwin-GA.pdf' },
  { team: 'Beach', url: 'https://media.base44.com/files/public/69bb16a38ac0086814d94e6f/5612924c7_Beach-GABeach-GA.pdf' },
  { team: 'Berkmar', url: 'https://media.base44.com/files/public/69bb16a38ac0086814d94e6f/80d5a4f1d_Berkmar-GA.pdf' },
  { team: 'BEST Academy', url: 'https://media.base44.com/files/public/69bb16a38ac0086814d94e6f/fd2362526_BESTAcademy-GA.pdf' },
  { team: 'Bowdon', url: 'https://media.base44.com/files/public/69bb16a38ac0086814d94e6f/dbc511061_Bowdon-GA.pdf' },
  { team: 'Bradwell Institute', url: 'https://media.base44.com/files/public/69bb16a38ac0086814d94e6f/a46e277da_BradwellInstitute-GA.pdf' },
  { team: 'Brookwood', url: 'https://media.base44.com/files/public/69bb16a38ac0086814d94e6f/292f4a13c_Brookwood-GA.pdf' },
  { team: 'Brunswick', url: 'https://media.base44.com/files/public/69bb16a38ac0086814d94e6f/b2b65f488_Brunswick-GABrunswick-GA-Incomplete.pdf' },
  { team: 'Buford', url: 'https://media.base44.com/files/public/69bb16a38ac0086814d94e6f/ee4f0414c_Buford-GA.pdf' },
  { team: 'Burke County', url: 'https://media.base44.com/files/public/69bb16a38ac0086814d94e6f/5fb6087d1_BurkeCounty-GA.pdf' },
  { team: 'Butler', url: 'https://media.base44.com/files/public/69bb16a38ac0086814d94e6f/705464a32_Butler-GA.pdf' },
  { team: 'Campbell', url: 'https://media.base44.com/files/public/69bb16a38ac0086814d94e6f/52766de6e_Campbell-GA.pdf' },
  { team: 'Carrollton', url: 'https://media.base44.com/files/public/69bb16a38ac0086814d94e6f/22fa42e58_Carrollton-GA.pdf' },
  { team: 'Cartersville', url: 'https://media.base44.com/files/public/69bb16a38ac0086814d94e6f/2882396d8_Cartersville-GA.pdf' },
  { team: 'Cedar Grove', url: 'https://media.base44.com/files/public/69bb16a38ac0086814d94e6f/861e39aad_CedarGrove-GA.pdf' },
  { team: 'Cedar Shoals', url: 'https://media.base44.com/files/public/69bb16a38ac0086814d94e6f/4acfd6815_CedarShoals-GA.pdf' },
  { team: 'Centennial', url: 'https://media.base44.com/files/public/69bb16a38ac0086814d94e6f/98008562e_Centennial-GA.pdf' },
  { team: 'Chamblee', url: 'https://media.base44.com/files/public/69bb16a38ac0086814d94e6f/978b25704_Chamblee-GA.pdf' },
  { team: 'Chapel Hill', url: 'https://media.base44.com/files/public/69bb16a38ac0086814d94e6f/8000e8104_ChapelHill-GA.pdf' },
  { team: 'Cherokee', url: 'https://media.base44.com/files/public/69bb16a38ac0086814d94e6f/545888faf_Cherokee-GA.pdf' },
  { team: 'Christian Heritage', url: 'https://media.base44.com/files/public/69bb16a38ac0086814d94e6f/6ff10f03d_ChristianHeritage-GA.pdf' },
  { team: 'Columbia', url: 'https://media.base44.com/files/public/69bb16a38ac0086814d94e6f/f431182bd_Columbia-GAColumbia.pdf' },
  { team: 'Cross Creek', url: 'https://media.base44.com/files/public/69bb16a38ac0086814d94e6f/dfcbcb148_CrossCreek-GA.pdf' },
  { team: 'Dacula', url: 'https://media.base44.com/files/public/69bb16a38ac0086814d94e6f/9363ae547_Dacula-GADacula.pdf' },
  { team: 'Darlington School', url: 'https://media.base44.com/files/public/69bb16a38ac0086814d94e6f/3aab12695_DarlingtonSchool-GA.pdf' },
  { team: 'Douglas County', url: 'https://media.base44.com/files/public/69bb16a38ac0086814d94e6f/34b7a20c2_DouglasCounty-GA.pdf' },
  { team: 'East Coweta', url: 'https://media.base44.com/files/public/69bb16a38ac0086814d94e6f/afed171ce_EastCoweta-GA.pdf' },
  { team: 'East Forsyth', url: 'https://media.base44.com/files/public/69bb16a38ac0086814d94e6f/143e58965_EastForsyth-GA.pdf' },
  { team: 'East Paulding', url: 'https://media.base44.com/files/public/69bb16a38ac0086814d94e6f/5923d634a_EastPaulding-GA.pdf' },
  { team: 'Eastside', url: 'https://media.base44.com/files/public/69bb16a38ac0086814d94e6f/13811eacb_Eastside-GA.pdf' },
  { team: 'ELCA', url: 'https://media.base44.com/files/public/69bb16a38ac0086814d94e6f/4b8b426bd_ELCA-GA.pdf' },
  { team: 'Franklin County', url: 'https://media.base44.com/files/public/69bb16a38ac0086814d94e6f/f75d6ad22_FranklinCounty-GA.pdf' },
  { team: 'Gainesville', url: 'https://media.base44.com/files/public/69bb16a38ac0086814d94e6f/c939f747b_Gainesville-GA.pdf' },
  { team: 'Alexander', url: 'https://media.base44.com/files/public/69bb16a38ac0086814d94e6f/cd5b80f6a_GamebyGameStats_AlexanderGA.pdf' },
  { team: 'Grayson', url: 'https://media.base44.com/files/public/69bb16a38ac0086814d94e6f/eaf1f2bc3_Grayson-GA.pdf' },
  { team: 'Greater Atlanta Christian', url: 'https://media.base44.com/files/public/69bb16a38ac0086814d94e6f/b0e3c395c_GreaterAtlantaChristian-GA.pdf' },
  { team: 'Habersham Central', url: 'https://media.base44.com/files/public/69bb16a38ac0086814d94e6f/36c311238_HabershamCentral-GA.pdf' },
  { team: 'Harlem', url: 'https://media.base44.com/files/public/69bb16a38ac0086814d94e6f/d52c96db9_Harlem-GA.pdf' },
  { team: 'Hebron Christian', url: 'https://media.base44.com/files/public/69bb16a38ac0086814d94e6f/c572549c5_HebronChristian-GA.pdf' },
  { team: 'Hillgrove', url: 'https://media.base44.com/files/public/69bb16a38ac0086814d94e6f/cdbfdec23_Hillgrove-GA.pdf' },
  { team: 'Holy Innocents', url: 'https://media.base44.com/files/public/69bb16a38ac0086814d94e6f/42559a2f7_HolyInnocents-GA.pdf' },
  { team: 'Jonesboro', url: 'https://media.base44.com/files/public/69bb16a38ac0086814d94e6f/8601c91af_Jonesboro-GA.pdf' },
  { team: 'Kell', url: 'https://media.base44.com/files/public/69bb16a38ac0086814d94e6f/1525bba10_Kell-GAKell.pdf' },
  { team: 'Lanier', url: 'https://media.base44.com/files/public/69bb16a38ac0086814d94e6f/78a925d04_Lanier-GA.pdf' },
  { team: 'Lassiter', url: 'https://media.base44.com/files/public/69bb16a38ac0086814d94e6f/d56d59925_Lassiter-GA.pdf' },
  { team: 'Lee County', url: 'https://media.base44.com/files/public/69bb16a38ac0086814d94e6f/7d1447caf_LeeCounty-GA.pdf' },
  { team: 'Lovett School', url: 'https://media.base44.com/files/public/69bb16a38ac0086814d94e6f/2d5babf54_LovettSchool-GA.pdf' },
  { team: 'Madison County', url: 'https://media.base44.com/files/public/69bb16a38ac0086814d94e6f/3cc0c2563_MadisonCounty-GA.pdf' },
];

// Guess team name from filename: "Alpharetta-GAAlpharetta.pdf" → "Alpharetta"
function guessTeamName(filename) {
  const base = filename.replace(/\.pdf$/i, '').replace(/^.*_/, '');
  const cleaned = base.replace(/-GA.*$/i, '').replace(/GamebyGameStats_/i, '').replace(/[-_]/g, ' ').trim();
  return cleaned;
}

export default function AdminGeorgiaStats() {
  const [tab, setTab] = useState('preloaded'); // 'preloaded' | 'upload'
  const [statuses, setStatuses] = useState({});
  const [running, setRunning] = useState(false);
  const [currentTeam, setCurrentTeam] = useState('');
  const [syncRunning, setSyncRunning] = useState(false);
  const [syncResult, setSyncResult] = useState(null);

  // Upload tab state
  const [uploadQueue, setUploadQueue] = useState([]); // [{id, file, teamName, url, uploadStatus}]
  const [dragging, setDragging] = useState(false);
  const fileInputRef = useRef();

  // ── Preloaded tab logic ──
  const importTeam = async (pdf) => {
    setStatuses(prev => ({ ...prev, [pdf.team]: { status: 'loading' } }));
    const res = await base44.functions.invoke('importGeorgiaStats', { pdf_url: pdf.url, team_name: pdf.team, admin_secret: 'goaio-admin-2026' });
    const data = res.data;
    const updated = data?.results?.filter(r => r.status === 'updated').length || 0;
    const notFound = data?.results?.filter(r => r.status === 'not_found').length || 0;
    setStatuses(prev => ({
      ...prev,
      [pdf.team]: { status: data?.error ? 'error' : 'done', updated, notFound, error: data?.error }
    }));
  };

  const importAll = async () => {
    setRunning(true);
    for (const pdf of PRELOADED_PDFS) {
      if (statuses[pdf.team]?.status === 'done') continue;
      setCurrentTeam(pdf.team);
      await importTeam(pdf);
      await new Promise(r => setTimeout(r, 800));
    }
    setCurrentTeam('');
    setRunning(false);
  };

  const doneCount = Object.values(statuses).filter(s => s.status === 'done').length;
  const errorCount = Object.values(statuses).filter(s => s.status === 'error').length;

  // ── Upload tab logic ──
  const handleFiles = (files) => {
    const pdfs = Array.from(files).filter(f => f.type === 'application/pdf' || f.name.endsWith('.pdf'));
    const newItems = pdfs.map(file => ({
      id: Math.random().toString(36).slice(2),
      file,
      teamName: guessTeamName(file.name),
      url: null,
      uploadStatus: 'pending', // pending | uploading | ready | importing | done | error
      result: null,
      error: null,
    }));
    setUploadQueue(prev => [...prev, ...newItems]);
  };

  const removeFromQueue = (id) => setUploadQueue(prev => prev.filter(i => i.id !== id));

  const updateItem = (id, patch) => setUploadQueue(prev => prev.map(i => i.id === id ? { ...i, ...patch } : i));

  const uploadAndImportAll = async () => {
    setRunning(true);
    for (const item of uploadQueue) {
      if (item.uploadStatus === 'done') continue;

      // Step 1: upload
      updateItem(item.id, { uploadStatus: 'uploading' });
      let fileUrl;
      try {
        const { file_url } = await base44.integrations.Core.UploadFile({ file: item.file });
        fileUrl = file_url;
        updateItem(item.id, { url: fileUrl, uploadStatus: 'importing' });
      } catch (e) {
        updateItem(item.id, { uploadStatus: 'error', error: 'Upload failed: ' + e.message });
        continue;
      }

      // Step 2: import
      try {
        const res = await base44.functions.invoke('importGeorgiaStats', { pdf_url: fileUrl, team_name: item.teamName, admin_secret: 'goaio-admin-2026' });
        const data = res.data;
        const updated = data?.results?.filter(r => r.status === 'updated').length || 0;
        const notFound = data?.results?.filter(r => r.status === 'not_found').length || 0;
        updateItem(item.id, {
          uploadStatus: data?.error ? 'error' : 'done',
          result: { updated, notFound },
          error: data?.error || null,
        });
      } catch (e) {
        updateItem(item.id, { uploadStatus: 'error', error: 'Import failed: ' + e.message });
      }

      await new Promise(r => setTimeout(r, 500));
    }
    setRunning(false);
  };

  const pendingUploadCount = uploadQueue.filter(i => i.uploadStatus !== 'done').length;

  const syncPlayers = async () => {
    setSyncRunning(true);
    setSyncResult(null);
    try {
      const res = await base44.functions.invoke('addPlayersToTeamRosters', {});
      setSyncResult(res.data);
    } catch (e) {
      setSyncResult({ error: e.message });
    }
    setSyncRunning(false);
  };

  return (
    <div>
      <div className="flex items-center justify-between mb-6">
        <div>
          <h1 className="text-2xl font-black text-white">Georgia Stats Import</h1>
          <p className="text-slate-400 text-sm mt-1">Import game stats from Hoopsalytics PDFs</p>
        </div>
        <div className="flex gap-2">
          <button onClick={syncPlayers} disabled={syncRunning}
            className="flex items-center gap-2 px-4 py-2 rounded-lg text-sm font-bold disabled:opacity-50"
            style={{ background: '#00FF85', color: '#000' }}>
            {syncRunning ? <Loader2 className="w-4 h-4 animate-spin" /> : <Plus className="w-4 h-4" />}
            {syncRunning ? 'Syncing Players...' : 'Sync Players to Teams'}
          </button>
          <button onClick={() => {
            setSyncRunning(true);
            base44.functions.invoke('populateTeamStats', {}).then(res => {
              setSyncResult(res.data);
              setSyncRunning(false);
            }).catch(e => {
              setSyncResult({ error: e.message });
              setSyncRunning(false);
            });
          }} disabled={syncRunning}
            className="flex items-center gap-2 px-4 py-2 rounded-lg text-sm font-bold disabled:opacity-50"
            style={{ background: '#FF6A00', color: '#000' }}>
            {syncRunning ? <Loader2 className="w-4 h-4 animate-spin" /> : <Plus className="w-4 h-4" />}
            {syncRunning ? 'Syncing Stats...' : 'Sync Stats to Teams'}
          </button>
        </div>
      </div>

      {syncResult && (
        <div className={`mb-6 p-4 rounded-lg text-sm font-semibold ${syncResult.error ? 'text-red-400 bg-red-950' : 'text-green-400 bg-green-950'}`}>
          {syncResult.error ? `Error: ${syncResult.error}` : `✓ ${syncResult.added} players added to rosters ${syncResult.errors?.length > 0 ? `(${syncResult.errors.length} errors)` : ''}`}
        </div>
      )}

      {/* Tabs */}
      <div className="flex gap-1 mb-6 border-b border-white/10">
        {[{ id: 'preloaded', label: 'Pre-loaded PDFs' }, { id: 'upload', label: 'Upload New PDFs' }].map(t => (
          <button key={t.id} onClick={() => setTab(t.id)}
            className={`px-4 py-2.5 text-sm font-bold border-b-2 -mb-px transition-all ${tab === t.id ? 'text-white border-orange-500' : 'text-slate-500 border-transparent hover:text-slate-300'}`}>
            {t.label}
          </button>
        ))}
      </div>

      {/* ── PRELOADED TAB ── */}
      {tab === 'preloaded' && (
        <div>
          <div className="flex items-center justify-between mb-4">
            <p className="text-slate-400 text-sm">{PRELOADED_PDFS.length} teams · {doneCount} imported{errorCount > 0 ? ` · ${errorCount} errors` : ''}</p>
            <button onClick={importAll} disabled={running}
              className="flex items-center gap-2 px-4 py-2 rounded-lg text-sm font-bold disabled:opacity-50"
              style={{ background: '#FF6A00', color: '#000' }}>
              {running ? <Loader2 className="w-4 h-4 animate-spin" /> : <Play className="w-4 h-4" />}
              {running ? `Importing ${currentTeam}...` : 'Import All'}
            </button>
          </div>

          <div className="grid sm:grid-cols-2 lg:grid-cols-3 gap-3">
            {PRELOADED_PDFS.map(pdf => {
              const s = statuses[pdf.team];
              return (
                <div key={pdf.team} className="flex items-center justify-between px-4 py-3 rounded-xl border"
                  style={{ background: '#0a0a0a', borderColor: s?.status === 'done' ? 'rgba(0,255,133,0.2)' : s?.status === 'error' ? 'rgba(255,59,48,0.2)' : 'rgba(255,255,255,0.06)' }}>
                  <div className="flex-1 min-w-0">
                    <p className="text-white font-semibold text-sm">{pdf.team}</p>
                    {s?.status === 'done' && <p className="text-xs text-green-400">{s.updated} updated{s.notFound > 0 ? `, ${s.notFound} unmatched` : ''}</p>}
                    {s?.status === 'error' && <p className="text-xs text-red-400">{s.error}</p>}
                  </div>
                  <div className="ml-3 shrink-0">
                    {s?.status === 'loading' && <Loader2 className="w-4 h-4 animate-spin text-orange-400" />}
                    {s?.status === 'done' && <CheckCircle2 className="w-4 h-4 text-green-400" />}
                    {s?.status === 'error' && <AlertCircle className="w-4 h-4 text-red-400" />}
                    {!s && (
                      <button onClick={() => importTeam(pdf)}
                        className="text-xs px-2 py-1 rounded font-bold text-slate-500 hover:text-white"
                        style={{ background: 'rgba(255,255,255,0.05)' }}>
                        Import
                      </button>
                    )}
                  </div>
                </div>
              );
            })}
          </div>
        </div>
      )}

      {/* ── UPLOAD TAB ── */}
      {tab === 'upload' && (
        <div>
          {/* Drop zone */}
          <div
            onDragOver={e => { e.preventDefault(); setDragging(true); }}
            onDragLeave={() => setDragging(false)}
            onDrop={e => { e.preventDefault(); setDragging(false); handleFiles(e.dataTransfer.files); }}
            onClick={() => fileInputRef.current?.click()}
            className="border-2 border-dashed rounded-xl p-10 text-center cursor-pointer transition-all mb-6"
            style={{ borderColor: dragging ? '#FF6A00' : 'rgba(255,255,255,0.12)', background: dragging ? 'rgba(255,106,0,0.05)' : 'rgba(255,255,255,0.02)' }}>
            <Upload className="w-8 h-8 mx-auto mb-3 text-slate-500" />
            <p className="text-white font-semibold text-sm">Drop PDF files here or click to browse</p>
            <p className="text-slate-500 text-xs mt-1">Accepts multiple PDFs — team names auto-detected from filename</p>
            <input ref={fileInputRef} type="file" accept=".pdf" multiple className="hidden" onChange={e => handleFiles(e.target.files)} />
          </div>

          {uploadQueue.length > 0 && (
            <>
              <div className="flex items-center justify-between mb-3">
                <p className="text-slate-400 text-sm">{uploadQueue.length} files queued</p>
                <div className="flex gap-2">
                  <button onClick={() => setUploadQueue([])} disabled={running}
                    className="text-xs px-3 py-1.5 rounded-lg font-bold text-slate-500 hover:text-white disabled:opacity-40"
                    style={{ background: 'rgba(255,255,255,0.05)' }}>
                    Clear All
                  </button>
                  <button onClick={uploadAndImportAll} disabled={running || pendingUploadCount === 0}
                    className="flex items-center gap-2 px-4 py-1.5 rounded-lg text-sm font-bold disabled:opacity-50"
                    style={{ background: '#FF6A00', color: '#000' }}>
                    {running ? <Loader2 className="w-4 h-4 animate-spin" /> : <Play className="w-4 h-4" />}
                    {running ? 'Importing...' : `Import All (${pendingUploadCount})`}
                  </button>
                </div>
              </div>

              <div className="space-y-2">
                {uploadQueue.map(item => (
                  <div key={item.id} className="flex items-center gap-3 px-4 py-3 rounded-xl border"
                    style={{ background: '#0a0a0a', borderColor: item.uploadStatus === 'done' ? 'rgba(0,255,133,0.2)' : item.uploadStatus === 'error' ? 'rgba(255,59,48,0.2)' : 'rgba(255,255,255,0.06)' }}>
                    <FileText className="w-4 h-4 text-slate-500 shrink-0" />
                    <div className="flex-1 min-w-0">
                      <input
                        value={item.teamName}
                        onChange={e => updateItem(item.id, { teamName: e.target.value })}
                        disabled={item.uploadStatus !== 'pending'}
                        className="bg-transparent text-white font-semibold text-sm w-full focus:outline-none border-b border-transparent focus:border-orange-500 transition-colors disabled:opacity-60"
                        placeholder="Team name..."
                      />
                      <p className="text-xs text-slate-600 truncate">{item.file.name}</p>
                      {item.uploadStatus === 'done' && <p className="text-xs text-green-400">{item.result?.updated} games updated{item.result?.notFound > 0 ? `, ${item.result.notFound} unmatched` : ''}</p>}
                      {item.uploadStatus === 'error' && <p className="text-xs text-red-400">{item.error}</p>}
                    </div>
                    <div className="shrink-0 flex items-center gap-2">
                      {item.uploadStatus === 'uploading' && <span className="text-xs text-slate-400">Uploading...</span>}
                      {item.uploadStatus === 'importing' && <span className="text-xs text-orange-400">Importing...</span>}
                      {(item.uploadStatus === 'uploading' || item.uploadStatus === 'importing') && <Loader2 className="w-4 h-4 animate-spin text-orange-400" />}
                      {item.uploadStatus === 'done' && <CheckCircle2 className="w-4 h-4 text-green-400" />}
                      {item.uploadStatus === 'error' && <AlertCircle className="w-4 h-4 text-red-400" />}
                      {item.uploadStatus === 'pending' && (
                        <button onClick={() => removeFromQueue(item.id)} className="text-slate-600 hover:text-white transition-colors">
                          <X className="w-4 h-4" />
                        </button>
                      )}
                    </div>
                  </div>
                ))}
              </div>
            </>
          )}
        </div>
      )}
    </div>
  );
}

src/components/admin/AdminGeorgiaTeams.jsx

import { useState, useEffect } from 'react';
import { base44 } from '@/api/base44Client';
import { Edit2, Save, X, Loader2, Plus } from 'lucide-react';

const ORANGE = '#FF6A00';

export default function AdminGeorgiaTeams() {
  const [teams, setTeams] = useState([]);
  const [coachProfiles, setCoachProfiles] = useState([]);
  const [loading, setLoading] = useState(true);
  const [editingId, setEditingId] = useState(null);
  const [editData, setEditData] = useState({});
  const [saving, setSaving] = useState(false);
  const [creatingNew, setCreatingNew] = useState(false);
  const [newCoachData, setNewCoachData] = useState({
    coach_name: '',
    coach_first_name: '',
    coach_last_name: '',
    email: '',
    phone: '',
    high_school: '',
    password: '',
  });

  useEffect(() => {
    Promise.all([
      base44.entities.HoopTeam.list(),
      base44.entities.CoachProfile.list(),
    ]).then(([t, c]) => {
      setTeams(t || []);
      setCoachProfiles(c || []);
      setLoading(false);
    });
  }, []);

  const startEdit = (id) => {
    const coach = coachProfiles.find(c => c.id === id);
    if (coach) {
      setEditingId(id);
      setEditData({
        coach_name: coach.coach_name || '',
        coach_first_name: coach.coach_first_name || coach.coach_name?.split(' ')[0] || '',
        coach_last_name: coach.coach_last_name || coach.coach_name?.split(' ').slice(1).join(' ') || '',
        email: coach.email || '',
        phone: coach.phone || '',
        high_school: coach.high_school || '',
        password: coach.password || '',
      });
    }
  };

  const saveCoach = async (id) => {
    setSaving(true);
    try {
      await base44.entities.CoachProfile.update(id, editData);
      setCoachProfiles(prev =>
        prev.map(c => c.id === id ? { ...c, ...editData } : c)
      );
      setEditingId(null);
      setEditData({});
    } catch (e) {
      alert('Error saving: ' + e.message);
    }
    setSaving(false);
  };

  const createNewCoach = async () => {
    if (!newCoachData.email || !newCoachData.high_school) {
      alert('Email and High School are required');
      return;
    }
    setSaving(true);
    try {
      const created = await base44.entities.CoachProfile.create({
        coach_name: newCoachData.coach_name || `${newCoachData.coach_first_name} ${newCoachData.coach_last_name}`.trim(),
        coach_first_name: newCoachData.coach_first_name,
        coach_last_name: newCoachData.coach_last_name,
        email: newCoachData.email,
        phone: newCoachData.phone || '',
        high_school: newCoachData.high_school,
        password: newCoachData.password || '',
        is_verified: true,
      });
      setCoachProfiles(prev => [...prev, created]);
      setCreatingNew(false);
      setNewCoachData({
        coach_name: '',
        coach_first_name: '',
        coach_last_name: '',
        email: '',
        phone: '',
        high_school: '',
        password: '',
      });
    } catch (e) {
      alert('Error creating coach: ' + e.message);
    }
    setSaving(false);
  };

  if (loading) {
    return (
      <div className="flex justify-center py-12">
        <div className="w-8 h-8 border-2 border-t-transparent rounded-full animate-spin" style={{ borderColor: ORANGE }} />
      </div>
    );
  }

  const teamsMap = {};
  teams.forEach(t => {
    teamsMap[t.name.toLowerCase()] = t;
  });

  return (
    <div>
      <div className="flex items-center justify-between mb-6">
        <div>
          <h1 className="text-2xl font-black text-white">Georgia Team Coaches</h1>
          <p className="text-slate-400 text-sm mt-1">Manage coach info for team pages</p>
        </div>
        <button
          onClick={() => setCreatingNew(true)}
          className="flex items-center gap-2 px-4 py-2 rounded-lg text-sm font-bold text-white transition-all hover:opacity-90"
          style={{ background: ORANGE }}
        >
          <Plus className="w-4 h-4" />
          Add Coach
        </button>
      </div>

      {/* Create modal */}
      {creatingNew && (
        <div className="fixed inset-0 z-50 flex items-center justify-center bg-black/50 backdrop-blur-sm">
          <div className="bg-slate-900 border border-slate-800 rounded-xl p-6 max-w-sm mx-4 max-h-[90vh] overflow-y-auto">
            <h2 className="text-white font-black text-lg mb-4">Add New Coach</h2>
            <div className="space-y-3">
              <div className="grid grid-cols-2 gap-2">
                <div>
                  <label className="text-xs text-slate-400 font-semibold">First Name</label>
                  <input
                    type="text"
                    value={newCoachData.coach_first_name}
                    onChange={e => setNewCoachData(prev => ({ ...prev, coach_first_name: e.target.value }))}
                    className="w-full text-xs px-2 py-1.5 rounded bg-slate-800 border border-slate-700 text-white focus:outline-none focus:border-orange-500"
                  />
                </div>
                <div>
                  <label className="text-xs text-slate-400 font-semibold">Last Name</label>
                  <input
                    type="text"
                    value={newCoachData.coach_last_name}
                    onChange={e => setNewCoachData(prev => ({ ...prev, coach_last_name: e.target.value }))}
                    className="w-full text-xs px-2 py-1.5 rounded bg-slate-800 border border-slate-700 text-white focus:outline-none focus:border-orange-500"
                  />
                </div>
              </div>
              <div>
                <label className="text-xs text-slate-400 font-semibold">Email *</label>
                <input
                  type="email"
                  value={newCoachData.email}
                  onChange={e => setNewCoachData(prev => ({ ...prev, email: e.target.value }))}
                  className="w-full text-xs px-2 py-1.5 rounded bg-slate-800 border border-slate-700 text-white focus:outline-none focus:border-orange-500"
                />
              </div>
              <div>
                <label className="text-xs text-slate-400 font-semibold">Phone</label>
                <input
                  type="tel"
                  value={newCoachData.phone}
                  onChange={e => setNewCoachData(prev => ({ ...prev, phone: e.target.value }))}
                  className="w-full text-xs px-2 py-1.5 rounded bg-slate-800 border border-slate-700 text-white focus:outline-none focus:border-orange-500"
                />
              </div>
              <div>
                <label className="text-xs text-slate-400 font-semibold">High School / Program *</label>
                <input
                  type="text"
                  value={newCoachData.high_school}
                  onChange={e => setNewCoachData(prev => ({ ...prev, high_school: e.target.value }))}
                  className="w-full text-xs px-2 py-1.5 rounded bg-slate-800 border border-slate-700 text-white focus:outline-none focus:border-orange-500"
                />
              </div>
              <div>
                <label className="text-xs text-slate-400 font-semibold">Team Page Password (optional)</label>
                <input
                  type="text"
                  value={newCoachData.password || ''}
                  onChange={e => setNewCoachData(prev => ({ ...prev, password: e.target.value }))}
                  placeholder="Leave blank for no password"
                  className="w-full text-xs px-2 py-1.5 rounded bg-slate-800 border border-slate-700 text-white focus:outline-none focus:border-orange-500"
                />
              </div>
            </div>
            <div className="flex gap-2 mt-6">
              <button
                onClick={createNewCoach}
                disabled={saving}
                className="flex-1 flex items-center justify-center gap-1 px-3 py-2 rounded-lg text-xs font-bold text-white disabled:opacity-50"
                style={{ background: ORANGE }}
              >
                {saving ? <Loader2 className="w-3 h-3 animate-spin" /> : <Plus className="w-3 h-3" />}
                Add
              </button>
              <button
                onClick={() => setCreatingNew(false)}
                disabled={saving}
                className="flex-1 flex items-center justify-center gap-1 px-3 py-2 rounded-lg text-xs font-bold text-slate-400 hover:text-white bg-slate-800 hover:bg-slate-700 disabled:opacity-50"
              >
                <X className="w-3 h-3" />
                Cancel
              </button>
            </div>
          </div>
        </div>
      )}

      {/* Coach list */}
      <div className="grid gap-3">
        {coachProfiles.length === 0 ? (
          <div className="text-center py-12 text-slate-500">No coaches yet</div>
        ) : (
          coachProfiles.map(coach => {
            const team = teamsMap[coach.high_school?.toLowerCase()];
            return (
              <div
                key={coach.id}
                className="rounded-xl border p-4 transition-all"
                style={{
                  background: editingId === coach.id ? 'rgba(255,106,0,0.05)' : '#0a0a0a',
                  borderColor: editingId === coach.id ? 'rgba(255,106,0,0.3)' : 'rgba(255,255,255,0.07)',
                }}
              >
                {editingId === coach.id ? (
                  // Edit mode
                  <div className="space-y-3">
                    <div className="grid grid-cols-2 gap-2">
                      <div>
                        <label className="text-xs text-slate-400 font-semibold">First Name</label>
                        <input
                          type="text"
                          value={editData.coach_first_name}
                          onChange={e => setEditData(prev => ({ ...prev, coach_first_name: e.target.value }))}
                          className="w-full text-xs px-2 py-1.5 rounded bg-slate-800 border border-slate-700 text-white focus:outline-none focus:border-orange-500"
                        />
                      </div>
                      <div>
                        <label className="text-xs text-slate-400 font-semibold">Last Name</label>
                        <input
                          type="text"
                          value={editData.coach_last_name}
                          onChange={e => setEditData(prev => ({ ...prev, coach_last_name: e.target.value }))}
                          className="w-full text-xs px-2 py-1.5 rounded bg-slate-800 border border-slate-700 text-white focus:outline-none focus:border-orange-500"
                        />
                      </div>
                    </div>
                    <div>
                      <label className="text-xs text-slate-400 font-semibold">Email</label>
                      <input
                        type="email"
                        value={editData.email}
                        onChange={e => setEditData(prev => ({ ...prev, email: e.target.value }))}
                        className="w-full text-xs px-2 py-1.5 rounded bg-slate-800 border border-slate-700 text-white focus:outline-none focus:border-orange-500"
                      />
                    </div>
                    <div>
                      <label className="text-xs text-slate-400 font-semibold">Phone</label>
                      <input
                        type="tel"
                        value={editData.phone}
                        onChange={e => setEditData(prev => ({ ...prev, phone: e.target.value }))}
                        className="w-full text-xs px-2 py-1.5 rounded bg-slate-800 border border-slate-700 text-white focus:outline-none focus:border-orange-500"
                      />
                    </div>
                    <div>
                          <label className="text-xs text-slate-400 font-semibold">High School</label>
                          <input
                            type="text"
                            value={editData.high_school}
                            onChange={e => setEditData(prev => ({ ...prev, high_school: e.target.value }))}
                            className="w-full text-xs px-2 py-1.5 rounded bg-slate-800 border border-slate-700 text-white focus:outline-none focus:border-orange-500"
                          />
                        </div>
                        <div>
                          <label className="text-xs text-slate-400 font-semibold">Password (optional)</label>
                          <input
                            type="text"
                            value={editData.password || ''}
                            onChange={e => setEditData(prev => ({ ...prev, password: e.target.value }))}
                            placeholder="Leave blank for no password"
                            className="w-full text-xs px-2 py-1.5 rounded bg-slate-800 border border-slate-700 text-white focus:outline-none focus:border-orange-500"
                          />
                        </div>
                    <div className="flex gap-2 pt-2">
                      <button
                        onClick={() => saveCoach(coach.id)}
                        disabled={saving}
                        className="flex-1 flex items-center justify-center gap-1 px-3 py-2 rounded-lg text-xs font-bold text-white disabled:opacity-50"
                        style={{ background: ORANGE }}
                      >
                        {saving ? <Loader2 className="w-3 h-3 animate-spin" /> : <Save className="w-3 h-3" />}
                        Save
                      </button>
                      <button
                        onClick={() => setEditingId(null)}
                        className="flex-1 flex items-center justify-center gap-1 px-3 py-2 rounded-lg text-xs font-bold text-slate-400 hover:text-white bg-slate-800 hover:bg-slate-700"
                      >
                        <X className="w-3 h-3" />
                        Cancel
                      </button>
                    </div>
                  </div>
                ) : (
                  // View mode
                  <div className="flex items-center justify-between">
                    <div>
                      <p className="text-white font-bold text-sm">{coach.coach_name}</p>
                      <p className="text-xs text-slate-400">{coach.email}</p>
                      <p className="text-xs text-slate-500">{coach.high_school}{coach.phone ? ` · ${coach.phone}` : ''}</p>
                      {coach.password && <p className="text-xs text-orange-400 mt-1">🔒 Password: {coach.password}</p>}
                    </div>
                    <button
                      onClick={() => startEdit(coach.id)}
                      className="flex items-center gap-1 px-3 py-1.5 rounded-lg text-xs font-bold text-white transition-all hover:opacity-90"
                      style={{ background: ORANGE }}
                    >
                      <Edit2 className="w-3 h-3" />
                      Edit
                    </button>
                  </div>
                )}
              </div>
            );
          })
        )}
      </div>
    </div>
  );
}

src/components/admin/AdminHowTo.jsx

import React, { useState } from 'react';
import { BookOpen, Users, BarChart3, Radio, Video, UserCheck, ChevronDown, ChevronRight } from 'lucide-react';

const STEPS = [
  {
    step: 1, icon: Users, color: 'bg-blue-500', label: 'Build Your Team',
    desc: 'Go to Teams tab → New Team. Add players to your roster with jersey numbers and positions. Players can link their own profiles.',
  },
  {
    step: 2, icon: BarChart3, color: 'bg-green-500', label: 'Schedule & Tag Games',
    desc: 'Go to Stat Game → New Game. Upload game film, then use the Tagger to log every event with video timestamps. Stats auto-populate to player portfolios.',
  },
  {
    step: 3, icon: Radio, color: 'bg-red-500', label: 'Live Stream with Fundraising',
    desc: 'Go to Live Stream → New Stream. Enable fundraising to charge fans for access. Share the unique stream code with your broadcaster. Streams save automatically to AWS.',
  },
  {
    step: 4, icon: Video, color: 'bg-cyan-500', label: 'Review Film Sessions',
    desc: 'All uploaded game film appears in the Film tab. Open any game in the Tagger for collaborative review. Share clips with players and coaches.',
  },
  {
    step: 5, icon: UserCheck, color: 'bg-orange-500', label: 'Build Player Portfolios',
    desc: 'Stats tagged from games automatically populate each player\'s portfolio. Add highlight videos, scouting reports, and bio info. Publish profiles for college recruiters.',
  },
];

const TIPS = [
  { color: 'text-orange-400', title: 'Multi-Camera Streaming', desc: 'Connect phones/tablets on the same WiFi. Use unique stream codes to link devices and switch camera angles in real-time.' },
  { color: 'text-blue-400', title: 'Athlete Profiles', desc: 'Athletes own and control their profiles even when linked to teams. Stats from all tagged games automatically populate the profile.' },
  { color: 'text-green-400', title: 'Live Stats Panel', desc: 'Stats display in a separate panel next to video — not overlaid on stream. Multiple people can collaborate on scorekeeping simultaneously.' },
  { color: 'text-purple-400', title: 'Vimeo Restream', desc: 'Add your Vimeo RTMP URL and stream key when creating a stream to automatically restream to Vimeo for wider audience reach.' },
  { color: 'text-yellow-400', title: 'Fundraising Campaigns', desc: 'Set a fan access price when creating a stream. Share the watch link to family/friends. Revenue tracked via Stripe payment processing.' },
];

export default function AdminHowTo() {
  const [expandedStep, setExpandedStep] = useState(null);

  return (
    <div>
      <div className="mb-8">
        <h1 className="text-3xl font-black text-white">How to Use <span className="text-orange-400">Teamstream SBV</span></h1>
        <p className="text-slate-400 mt-2">Your complete all-in-one sports platform. From athlete recruiting profiles and multi-camera live streaming to stat tagging and fundraising — everything you need in one place.</p>
      </div>

      <div className="space-y-3 mb-10">
        {STEPS.map(s => {
          const Icon = s.icon;
          const isOpen = expandedStep === s.step;
          return (
            <div key={s.step} className="bg-slate-900 rounded-xl border border-slate-800 overflow-hidden">
              <button
                onClick={() => setExpandedStep(isOpen ? null : s.step)}
                className="w-full flex items-center gap-4 p-5 text-left hover:bg-slate-800/50 transition-all"
              >
                <div className={`w-10 h-10 ${s.color} rounded-xl flex items-center justify-center flex-shrink-0`}>
                  <Icon className="w-5 h-5 text-white" />
                </div>
                <div className="flex-1">
                  <p className="text-xs text-slate-500 font-semibold uppercase tracking-wider">Step {s.step}</p>
                  <p className="text-white font-bold">{s.label}</p>
                </div>
                {isOpen ? <ChevronDown className="w-5 h-5 text-slate-400" /> : <ChevronRight className="w-5 h-5 text-slate-400" />}
              </button>
              {isOpen && (
                <div className="px-5 pb-5 ml-14">
                  <p className="text-slate-400 text-sm leading-relaxed">{s.desc}</p>
                </div>
              )}
            </div>
          );
        })}
      </div>

      <div className="bg-slate-900 rounded-xl border border-slate-800 p-6">
        <div className="flex items-center gap-2 mb-4">
          <span className="text-lg">💡</span>
          <h2 className="text-white font-bold text-lg">Pro Tips</h2>
        </div>
        <div className="space-y-4">
          {TIPS.map(tip => (
            <div key={tip.title}>
              <span className={`font-bold text-sm ${tip.color}`}>{tip.title}:</span>
              <span className="text-slate-400 text-sm ml-2">{tip.desc}</span>
            </div>
          ))}
        </div>
      </div>
    </div>
  );
}

src/components/admin/AdminIdentityImport.jsx

import React, { useState, useRef } from 'react';
import { base44 } from '@/api/base44Client';
import { Upload, FileText, Play, AlertCircle, CheckCircle2, Download, Loader2, XCircle, Link2, Users, IdCard } from 'lucide-react';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';

export default function AdminIdentityImport() {
  const [file, setFile] = useState(null);
  const [fileUrl, setFileUrl] = useState('');
  const [csvText, setCsvText] = useState('');
  const [dryRun, setDryRun] = useState(true);
  const [loading, setLoading] = useState(false);
  const [result, setResult] = useState(null);
  const [error, setError] = useState(null);
  const [activeView, setActiveView] = useState('matched');
  const fileInputRef = useRef(null);

  const handleFileSelect = (e) => {
    const f = e.target.files[0];
    if (!f) return;
    setFile(f);
    setFileUrl('');
    const reader = new FileReader();
    reader.onload = (ev) => setCsvText(ev.target.result);
    reader.readAsText(f);
  };

  const handleUrlMode = () => {
    setFile(null);
    setCsvText('');
  };

  const handleRun = async () => {
    setLoading(true);
    setError(null);
    setResult(null);
    try {
      const payload = {
        dry_run: dryRun,
        csv_filename: file?.name || (fileUrl ? fileUrl.split('/').pop() : 'unknown.csv')
      };
      if (fileUrl) {
        payload.file_url = fileUrl;
      } else if (csvText) {
        payload.csv_text = csvText;
      } else {
        setError('Please upload a file or paste a CSV URL.');
        setLoading(false);
        return;
      }
      const res = await base44.functions.invoke('updatePlayerIdentitiesCSV', payload);
      setResult(res.data);
      setActiveView('matched');
    } catch (err) {
      setError(err?.response?.data?.error || err?.message || 'Import failed');
    }
    setLoading(false);
  };

  const downloadReport = () => {
    if (!result) return;
    const headers = ['Type', 'Row', 'Player Name', 'Slug', 'Fields / Error'];
    const rows = [];
    (result.preview?.matched || []).forEach(m => {
      rows.push(['Matched', '', m.player_name || '', m.slug || '', (m.fields || []).join(', ')]);
    });
    (result.preview?.unmatched || []).forEach(u => {
      rows.push(['Unmatched', u.row || '', u.player_name || '', u.slug || '', u.error || '']);
    });
    (result.preview?.errors || []).forEach(e => {
      rows.push(['Error', e.row || '', e.player_name || '', '', e.error || '']);
    });
    const csv = [headers.join(','), ...rows.map(r => r.map(c => `"${String(c).replace(/"/g, '""')}"`).join(','))].join('\n');
    const blob = new Blob([csv], { type: 'text/csv' });
    const url = URL.createObjectURL(blob);
    const a = document.createElement('a');
    a.href = url;
    a.download = `identity_update_report_${new Date().toISOString().slice(0, 19).replace(/[:-]/g, '')}.csv`;
    a.click();
    URL.revokeObjectURL(url);
  };

  return (
    <div className="space-y-6">
      {/* Header */}
      <div>
        <h2 className="text-2xl font-black text-white mb-1 flex items-center gap-2">
          <IdCard className="w-6 h-6 text-cyan-400" /> Update Core Identity from CSV
        </h2>
        <p className="text-gray-400 text-sm">
          Upload a CSV of player identity data (name, contact, academic, physical, social). Players are matched by their{' '}
          <strong className="text-cyan-400">Portfolio URL slug</strong>.{' '}
          <span className="text-red-400 font-semibold">No new portfolios are created</span> — unmatched rows are skipped.
          Only fields with values in the CSV are updated; existing data is preserved for blank fields.
        </p>
      </div>

      {/* Upload / URL section */}
      <div className="bg-slate-900 rounded-xl border border-slate-800 p-6 space-y-4">
        <div className="flex gap-2 mb-3">
          <button onClick={() => fileInputRef.current?.click()}
            className={`flex-1 flex items-center justify-center gap-2 py-3 rounded-lg border-2 border-dashed transition-all text-sm font-medium
              ${file ? 'border-green-500/40 bg-green-500/5 text-green-400' : 'border-slate-700 text-gray-400 hover:border-cyan-500/40 hover:text-cyan-400'}`}>
            {file ? <><CheckCircle2 className="w-4 h-4" /> {file.name}</> : <><Upload className="w-4 h-4" /> Upload CSV File</>}
          </button>
          <button onClick={handleUrlMode}
            className={`flex-1 flex items-center justify-center gap-2 py-3 rounded-lg border-2 border-dashed transition-all text-sm font-medium
              ${fileUrl ? 'border-green-500/40 bg-green-500/5 text-green-400' : 'border-slate-700 text-gray-400 hover:border-cyan-500/40 hover:text-cyan-400'}`}>
            <Link2 className="w-4 h-4" /> Paste CSV URL
          </button>
        </div>
        <input ref={fileInputRef} type="file" accept=".csv,text/csv" onChange={handleFileSelect} className="hidden" />

        {fileUrl !== '' || (!file && csvText === '') ? (
          <Input
            placeholder="https://...csv"
            value={fileUrl}
            onChange={e => setFileUrl(e.target.value)}
            className="bg-slate-800 border-slate-700 text-white text-sm"
          />
        ) : null}

        {/* Options */}
        <div className="flex flex-wrap gap-4 pt-2">
          <label className="flex items-center gap-2 cursor-pointer">
            <input type="checkbox" checked={dryRun} onChange={e => setDryRun(e.target.checked)}
              className="w-4 h-4 accent-cyan-500" />
            <span className="text-sm text-gray-300">Dry Run <span className="text-gray-500">(preview only — recommended first)</span></span>
          </label>
        </div>

        {/* Run button */}
        <Button onClick={handleRun} disabled={loading || (!file && !fileUrl && !csvText)}
          className="w-full bg-cyan-600 hover:bg-cyan-700 text-white font-bold"
          size="lg">
          {loading ? (
            <><Loader2 className="w-4 h-4 animate-spin mr-2" /> Processing{dryRun ? ' Dry Run' : ''}... (may take 30-60s for large files)</>
          ) : (
            <><Play className="w-4 h-4 mr-2" /> {dryRun ? 'Run Dry Run (Preview)' : 'Apply Identity Updates'}</>
          )}
        </Button>

        {error && (
          <div className="flex items-start gap-2 p-3 rounded-lg bg-red-500/10 border border-red-500/20 text-red-400 text-sm">
            <AlertCircle className="w-4 h-4 shrink-0 mt-0.5" />
            <span>{error}</span>
          </div>
        )}
      </div>

      {/* Results */}
      {result && (
        <div className="space-y-4">
          {/* Summary banner */}
          <div className="bg-slate-900 rounded-xl border border-slate-800 p-6">
            <div className="flex items-center gap-2 mb-4">
              {result.dry_run ? (
                <span className="px-2 py-0.5 rounded text-[10px] font-black uppercase tracking-widest bg-yellow-500/20 text-yellow-400 border border-yellow-500/30">DRY RUN</span>
              ) : (
                <span className="px-2 py-0.5 rounded text-[10px] font-black uppercase tracking-widest bg-green-500/20 text-green-400 border border-green-500/30">APPLIED</span>
              )}
              <span className="text-gray-500 text-xs">{result.import_log_id ? `Log ID: ${result.import_log_id}` : ''}</span>
            </div>
            <p className="text-gray-300 text-sm mb-4">{result.summary}</p>

            <div className="grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-6 gap-3">
              {[
                { label: 'Total Rows', value: result.total_rows, icon: FileText, color: 'text-white' },
                { label: 'Matched', value: result.matched, icon: CheckCircle2, color: 'text-green-400' },
                { label: result.dry_run ? 'Would Update' : 'Updated', value: result.dry_run ? result.matched : result.updated, icon: Play, color: 'text-cyan-400' },
                { label: 'Unmatched', value: result.unmatched, icon: XCircle, color: 'text-gray-400' },
                { label: 'Parse Errors', value: result.parse_errors, icon: AlertCircle, color: 'text-red-400' },
                { label: 'New Created', value: 0, icon: Users, color: 'text-gray-500' },
              ].map(s => (
                <div key={s.label} className="bg-slate-800/50 rounded-lg p-3 text-center">
                  <s.icon className={`w-4 h-4 mx-auto mb-1 ${s.color}`} />
                  <div className={`text-xl font-black ${s.color}`}>{s.value}</div>
                  <div className="text-[10px] text-gray-500 uppercase tracking-wide mt-0.5">{s.label}</div>
                </div>
              ))}
            </div>
          </div>

          {/* Tab selector */}
          <div className="flex gap-2 flex-wrap">
            {[
              { id: 'matched', label: `Matched (${result.matched})`, icon: CheckCircle2, color: 'text-green-400' },
              { id: 'unmatched', label: `Unmatched (${result.unmatched})`, icon: XCircle, color: 'text-gray-400' },
              { id: 'errors', label: `Errors (${result.parse_errors})`, icon: AlertCircle, color: 'text-red-400' },
            ].map(t => (
              <button key={t.id} onClick={() => setActiveView(t.id)}
                className={`flex items-center gap-1.5 px-4 py-2 rounded-lg text-sm font-medium transition-all
                  ${activeView === t.id ? 'bg-slate-800 text-white' : 'text-gray-500 hover:text-gray-300'}`}>
                <t.icon className={`w-3.5 h-3.5 ${t.color}`} />
                {t.label}
              </button>
            ))}
            <button onClick={downloadReport}
              className="ml-auto flex items-center gap-1.5 px-4 py-2 rounded-lg text-sm font-medium bg-slate-800 text-cyan-400 hover:bg-slate-700 transition-all">
              <Download className="w-3.5 h-3.5" /> Download Report
            </button>
          </div>

          {/* Tab content */}
          <div className="bg-slate-900 rounded-xl border border-slate-800 p-4 max-h-[500px] overflow-y-auto">
            {activeView === 'matched' && (
              <div className="space-y-2">
                {result.preview?.matched?.length > 0 ? result.preview.matched.map((m, i) => (
                  <div key={i} className="flex items-center gap-3 py-2 px-3 rounded-lg bg-slate-800/50 hover:bg-slate-800 transition-all">
                    <CheckCircle2 className="w-4 h-4 text-green-400 shrink-0" />
                    <div className="flex-1 min-w-0">
                      <span className="text-white text-sm font-medium">{m.player_name}</span>
                      <span className="text-gray-500 text-xs ml-2">CSV: "{m.csv_name}" · /{m.slug}</span>
                    </div>
                    <span className="text-cyan-400 text-xs font-bold shrink-0 max-w-[300px] truncate">{m.fields?.length} fields: {m.fields?.join(', ')}</span>
                  </div>
                )) : <p className="text-gray-500 text-sm text-center py-8">No matched players.</p>}
                {result.matched > 30 && <p className="text-gray-600 text-xs text-center pt-2">Showing first 30 of {result.matched} matches. Full list saved in ImportLog.</p>}
              </div>
            )}

            {activeView === 'unmatched' && (
              <div className="space-y-2">
                {result.preview?.unmatched?.length > 0 ? result.preview.unmatched.map((u, i) => (
                  <div key={i} className="flex items-center gap-3 py-2 px-3 rounded-lg bg-slate-800/50">
                    <XCircle className="w-4 h-4 text-gray-400 shrink-0" />
                    <div className="flex-1 min-w-0">
                      <span className="text-white text-sm font-medium">{u.player_name}</span>
                      <span className="text-gray-500 text-xs ml-2">/{u.slug}</span>
                      <p className="text-gray-500 text-xs mt-0.5">{u.error}</p>
                    </div>
                  </div>
                )) : <p className="text-gray-500 text-sm text-center py-8">No unmatched rows — every CSV row found a matching portfolio!</p>}
                {result.unmatched > 30 && <p className="text-gray-600 text-xs text-center pt-2">Showing first 30 of {result.unmatched} unmatched. Full list saved in ImportLog.</p>}
              </div>
            )}

            {activeView === 'errors' && (
              <div className="space-y-2">
                {result.preview?.errors?.length > 0 ? result.preview.errors.map((e, i) => (
                  <div key={i} className="py-2 px-3 rounded-lg bg-slate-800/50">
                    <div className="flex items-start gap-2">
                      <AlertCircle className="w-4 h-4 text-red-400 shrink-0 mt-0.5" />
                      <div className="flex-1 min-w-0">
                        <span className="text-white text-sm font-medium">{e.player_name || 'Unknown'}</span>
                        {e.row && <span className="text-gray-600 text-xs ml-2">Row {e.row}</span>}
                        <p className="text-gray-400 text-xs mt-0.5">{e.error}</p>
                      </div>
                    </div>
                  </div>
                )) : <p className="text-gray-500 text-sm text-center py-8">No parse errors!</p>}
              </div>
            )}
          </div>

          {/* Apply button (if dry run had matches) */}
          {result.dry_run && result.matched > 0 && (
            <div className="bg-green-500/10 border border-green-500/20 rounded-xl p-4 flex items-center justify-between gap-4">
              <div className="flex items-center gap-2">
                <CheckCircle2 className="w-5 h-5 text-green-400 shrink-0" />
                <p className="text-green-400 text-sm">
                  <strong>{result.matched}</strong> existing portfolios matched for identity update from <strong>{result.total_rows}</strong> rows.
                  No new portfolios will be created. Review above, then apply when ready.
                </p>
              </div>
              <Button onClick={() => { setDryRun(false); setTimeout(handleRun, 100); }}
                className="bg-green-600 hover:bg-green-700 text-white font-bold shrink-0"
                disabled={loading}>
                {loading ? <Loader2 className="w-4 h-4 animate-spin" /> : <Play className="w-4 h-4 mr-1" />}
                Apply Updates
              </Button>
            </div>
          )}
        </div>
      )}
    </div>
  );
}

src/components/admin/AdminLiveStream.jsx

import React, { useState } from 'react';
import { base44 } from '@/api/base44Client';
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
import { Radio, Plus, Copy, ExternalLink, Code2, X, Zap, DollarSign, Video } from 'lucide-react';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import { Badge } from '@/components/ui/badge';

function NewStreamModal({ teams, onSave, onClose }) {
  const [form, setForm] = useState({
    title: '', team_id: teams[0]?.id || '', is_fundraiser: false,
    fan_access_price: 5, embed_enabled: true, team_page_hosted: false,
    vimeo_restream_url: '', vimeo_restream_key: '',
  });

  return (
    <div className="fixed inset-0 bg-black/70 flex items-center justify-center z-50 p-4 overflow-y-auto">
      <div className="bg-slate-900 rounded-2xl border border-slate-700 w-full max-w-lg p-6 my-4">
        <div className="flex items-center justify-between mb-5">
          <h2 className="text-lg font-bold text-white">New Live Stream</h2>
          <button onClick={onClose} className="text-slate-400 hover:text-white"><X className="w-5 h-5" /></button>
        </div>
        <div className="space-y-4">
          <div>
            <label className="text-xs text-slate-400 mb-1 block">Stream Title *</label>
            <Input value={form.title} onChange={e => setForm({ ...form, title: e.target.value })}
              className="bg-slate-800 border-slate-700 text-white" placeholder="e.g. Mambas vs Tigers" />
          </div>
          <div>
            <label className="text-xs text-slate-400 mb-1 block">Team *</label>
            <select value={form.team_id} onChange={e => setForm({ ...form, team_id: e.target.value })}
              className="w-full bg-slate-800 border border-slate-700 text-white rounded-md px-3 py-2 text-sm">
              {teams.map(t => <option key={t.id} value={t.id}>{t.name}</option>)}
            </select>
          </div>

          {/* Fundraiser toggle */}
          <div className="bg-slate-800 rounded-xl p-4">
            <div className="flex items-center justify-between mb-3">
              <div className="flex items-center gap-2">
                <DollarSign className="w-4 h-4 text-yellow-400" />
                <span className="text-white text-sm font-semibold">Fundraising Stream</span>
              </div>
              <button
                onClick={() => setForm({ ...form, is_fundraiser: !form.is_fundraiser })}
                className={`relative w-10 h-5 rounded-full transition-colors ${form.is_fundraiser ? 'bg-yellow-500' : 'bg-slate-600'}`}
              >
                <span className={`absolute top-0.5 left-0.5 w-4 h-4 bg-white rounded-full transition-transform ${form.is_fundraiser ? 'translate-x-5' : ''}`} />
              </button>
            </div>
            {form.is_fundraiser && (
              <div>
                <label className="text-xs text-slate-400 mb-1 block">Fan Access Price ($)</label>
                <Input type="number" value={form.fan_access_price}
                  onChange={e => setForm({ ...form, fan_access_price: parseFloat(e.target.value) || 0 })}
                  className="bg-slate-700 border-slate-600 text-white" placeholder="5" />
              </div>
            )}
          </div>

          {/* Hosting options */}
          <div className="bg-slate-800 rounded-xl p-4 space-y-3">
            <p className="text-white text-sm font-semibold">Hosting Options</p>
            <label className="flex items-center gap-3 cursor-pointer">
              <input type="checkbox" checked={form.embed_enabled}
                onChange={e => setForm({ ...form, embed_enabled: e.target.checked })}
                className="w-4 h-4 accent-blue-500" />
              <div>
                <p className="text-white text-sm">Allow Embed Code</p>
                <p className="text-slate-400 text-xs">Teams can embed the stream on their own website</p>
              </div>
            </label>
            <label className="flex items-center gap-3 cursor-pointer">
              <input type="checkbox" checked={form.team_page_hosted}
                onChange={e => setForm({ ...form, team_page_hosted: e.target.checked })}
                className="w-4 h-4 accent-blue-500" />
              <div>
                <p className="text-white text-sm">Host on GOAIO Team Page</p>
                <p className="text-slate-400 text-xs">Stream will appear on the team's GOAIO-hosted page</p>
              </div>
            </label>
          </div>

          {/* Vimeo restream */}
          <div className="bg-slate-800 rounded-xl p-4 space-y-3">
            <div className="flex items-center gap-2">
              <Video className="w-4 h-4 text-blue-400" />
              <p className="text-white text-sm font-semibold">Restream to Vimeo (optional)</p>
            </div>
            <Input value={form.vimeo_restream_url}
              onChange={e => setForm({ ...form, vimeo_restream_url: e.target.value })}
              className="bg-slate-700 border-slate-600 text-white" placeholder="Vimeo RTMP URL" />
            <Input value={form.vimeo_restream_key}
              onChange={e => setForm({ ...form, vimeo_restream_key: e.target.value })}
              className="bg-slate-700 border-slate-600 text-white" placeholder="Vimeo Stream Key" />
          </div>
        </div>
        <div className="flex gap-3 mt-6">
          <Button variant="ghost" onClick={onClose} className="flex-1 text-slate-400">Cancel</Button>
          <Button onClick={() => onSave(form)} className="flex-1 bg-red-500 hover:bg-red-600"
            disabled={!form.title || !form.team_id}>
            <Radio className="w-4 h-4 mr-2" /> Create Stream
          </Button>
        </div>
      </div>
    </div>
  );
}

function EmbedModal({ stream, onClose }) {
  const embedCode = `<iframe src="${window.location.origin}/stream/${stream.stream_code}" width="100%" height="480" frameborder="0" allowfullscreen allow="autoplay"></iframe>`;
  const [copied, setCopied] = useState(false);

  const copy = (text) => {
    navigator.clipboard.writeText(text);
    setCopied(true);
    setTimeout(() => setCopied(false), 2000);
  };

  return (
    <div className="fixed inset-0 bg-black/70 flex items-center justify-center z-50 p-4">
      <div className="bg-slate-900 rounded-2xl border border-slate-700 w-full max-w-lg p-6">
        <div className="flex items-center justify-between mb-5">
          <h2 className="text-lg font-bold text-white">Embed & Share</h2>
          <button onClick={onClose} className="text-slate-400 hover:text-white"><X className="w-5 h-5" /></button>
        </div>

        <div className="space-y-4">
          <div>
            <label className="text-xs text-slate-400 mb-2 block">Stream Code (share with streamers)</label>
            <div className="flex items-center gap-2 bg-slate-800 rounded-lg p-3">
              <span className="text-orange-400 font-black text-xl tracking-widest flex-1">{stream.stream_code}</span>
              <button onClick={() => copy(stream.stream_code)} className="text-slate-400 hover:text-white">
                <Copy className="w-4 h-4" />
              </button>
            </div>
          </div>

          <div>
            <label className="text-xs text-slate-400 mb-2 block">Fan Watch Link</label>
            <div className="flex items-center gap-2 bg-slate-800 rounded-lg p-3">
              <span className="text-blue-400 text-sm flex-1 truncate">{window.location.origin}/stream/{stream.stream_code}</span>
              <button onClick={() => copy(`${window.location.origin}/stream/${stream.stream_code}`)} className="text-slate-400 hover:text-white">
                <Copy className="w-4 h-4" />
              </button>
            </div>
          </div>

          {stream.embed_enabled && (
            <div>
              <label className="text-xs text-slate-400 mb-2 block">Embed Code</label>
              <div className="bg-slate-800 rounded-lg p-3">
                <pre className="text-green-400 text-xs whitespace-pre-wrap break-all">{embedCode}</pre>
                <button onClick={() => copy(embedCode)}
                  className="mt-2 flex items-center gap-1 text-xs text-slate-400 hover:text-white">
                  <Copy className="w-3.5 h-3.5" /> {copied ? 'Copied!' : 'Copy embed code'}
                </button>
              </div>
            </div>
          )}

          {stream.aws_ingest_endpoint && (
            <div>
              <label className="text-xs text-slate-400 mb-2 block">AWS Ingest Endpoint (for broadcaster app)</label>
              <div className="bg-slate-800 rounded-lg p-3">
                <p className="text-slate-300 text-xs break-all">{stream.aws_ingest_endpoint}</p>
              </div>
            </div>
          )}
        </div>
      </div>
    </div>
  );
}

export default function AdminLiveStream() {
  const queryClient = useQueryClient();
  const [showNew, setShowNew] = useState(false);
  const [embedStream, setEmbedStream] = useState(null);

  const { data: teams = [] } = useQuery({ queryKey: ['hoop-teams'], queryFn: () => base44.entities.HoopTeam.list() });
  const { data: streams = [] } = useQuery({ queryKey: ['live-streams'], queryFn: () => base44.entities.LiveStream.list('-created_date', 50) });

  const createMutation = useMutation({
    mutationFn: (data) => base44.functions.invoke('manageStream', { action: 'create', streamData: data }),
    onSuccess: () => { queryClient.invalidateQueries(['live-streams']); setShowNew(false); },
  });

  const statusMutation = useMutation({
    mutationFn: ({ streamId, action }) => base44.functions.invoke('manageStream', { action, streamId }),
    onSuccess: () => queryClient.invalidateQueries(['live-streams']),
  });

  const deleteMutation = useMutation({
    mutationFn: (id) => base44.entities.LiveStream.delete(id),
    onSuccess: () => queryClient.invalidateQueries(['live-streams']),
  });

  const getTeam = (teamId) => teams.find(t => t.id === teamId);

  return (
    <div>
      <div className="flex items-center justify-between mb-6">
        <div>
          <h1 className="text-2xl font-black text-white">Live Streaming</h1>
          <p className="text-slate-400 text-sm">Create streams, generate codes, save to AWS cloud</p>
        </div>
        <Button onClick={() => setShowNew(true)} className="bg-red-500 hover:bg-red-600 gap-2">
          <Plus className="w-4 h-4" /> New Stream
        </Button>
      </div>

      {/* Info banner */}
      <div className="bg-yellow-500/10 border border-yellow-500/30 rounded-xl p-4 mb-6 flex items-start gap-3">
        <Zap className="w-5 h-5 text-yellow-400 flex-shrink-0 mt-0.5" />
        <div>
          <p className="text-yellow-400 text-sm font-semibold">Fundraising Feature</p>
          <p className="text-slate-400 text-xs mt-0.5">
            Generate revenue by streaming your games. Streams are saved to AWS S3 cloud storage. 
            Restream to Vimeo for wider audience reach. Share unique stream codes with your team.
          </p>
        </div>
      </div>

      <div className="space-y-4">
        {streams.map(stream => {
          const team = getTeam(stream.team_id);
          return (
            <div key={stream.id} className="bg-slate-900 rounded-xl border border-slate-800 p-5">
              <div className="flex items-start justify-between mb-4">
                <div>
                  <div className="flex items-center gap-2 mb-1">
                    <h3 className="text-white font-bold">{stream.title}</h3>
                    <Badge className={
                      stream.status === 'live' ? 'bg-red-500 animate-pulse' :
                      stream.status === 'ended' ? 'bg-slate-600' : 'bg-slate-700'
                    }>
                      {stream.status === 'live' ? '● LIVE' : stream.status}
                    </Badge>
                    {stream.is_fundraiser && <Badge className="bg-yellow-500/20 text-yellow-400 border border-yellow-500/30">Fundraiser</Badge>}
                  </div>
                  <p className="text-slate-400 text-sm">{team?.name || 'No team'}</p>
                </div>
                <button onClick={() => deleteMutation.mutate(stream.id)}
                  className="p-1.5 rounded-lg text-slate-500 hover:text-red-400 hover:bg-red-500/10">
                  <X className="w-4 h-4" />
                </button>
              </div>

              {/* Stream code */}
              <div className="grid sm:grid-cols-3 gap-3 mb-4">
                <div className="bg-slate-800 rounded-lg p-3">
                  <p className="text-xs text-slate-400 mb-1">Stream Code</p>
                  <p className="text-orange-400 font-black tracking-widest">{stream.stream_code}</p>
                </div>
                {stream.is_fundraiser && (
                  <div className="bg-slate-800 rounded-lg p-3">
                    <p className="text-xs text-slate-400 mb-1">Fan Access Price</p>
                    <p className="text-green-400 font-bold">${stream.fan_access_price}</p>
                  </div>
                )}
                <div className="bg-slate-800 rounded-lg p-3">
                  <p className="text-xs text-slate-400 mb-1">Score</p>
                  <p className="text-white font-bold">{stream.score_us} – {stream.score_opponent}</p>
                </div>
              </div>

              {/* Feature flags */}
              <div className="flex gap-2 mb-4 flex-wrap">
                {stream.embed_enabled && <span className="text-xs bg-blue-500/20 text-blue-400 px-2 py-1 rounded-full border border-blue-500/30">Embed Enabled</span>}
                {stream.team_page_hosted && <span className="text-xs bg-purple-500/20 text-purple-400 px-2 py-1 rounded-full border border-purple-500/30">GOAIO Team Page</span>}
                {stream.vimeo_restream_url && <span className="text-xs bg-cyan-500/20 text-cyan-400 px-2 py-1 rounded-full border border-cyan-500/30">Vimeo Restream</span>}
                {stream.aws_recording_url && <span className="text-xs bg-orange-500/20 text-orange-400 px-2 py-1 rounded-full border border-orange-500/30">AWS Cloud Save</span>}
              </div>

              {/* Actions */}
              <div className="flex gap-2 flex-wrap">
                {stream.status === 'offline' && (
                  <Button size="sm" onClick={() => statusMutation.mutate({ streamId: stream.id, action: 'go_live' })}
                    className="bg-red-500 hover:bg-red-600 gap-1">
                    <Radio className="w-3.5 h-3.5" /> Go Live
                  </Button>
                )}
                {stream.status === 'live' && (
                  <Button size="sm" onClick={() => statusMutation.mutate({ streamId: stream.id, action: 'end_stream' })}
                    className="bg-slate-600 hover:bg-slate-500 gap-1">
                    End Stream
                  </Button>
                )}
                <Button size="sm" variant="outline" onClick={() => setEmbedStream(stream)}
                  className="border-slate-700 text-slate-300 hover:text-white gap-1">
                  <Code2 className="w-3.5 h-3.5" /> Embed & Share
                </Button>
                <Button size="sm" variant="outline"
                  onClick={() => window.open(`/stream/${stream.stream_code}`, '_blank')}
                  className="border-slate-700 text-slate-300 hover:text-white gap-1">
                  <ExternalLink className="w-3.5 h-3.5" /> Watch Page
                </Button>
              </div>
            </div>
          );
        })}

        {streams.length === 0 && (
          <div className="bg-slate-900/50 rounded-xl border border-dashed border-slate-700 p-12 text-center">
            <Radio className="w-12 h-12 text-slate-600 mx-auto mb-3" />
            <p className="text-slate-400 mb-4">No streams yet</p>
            <Button onClick={() => setShowNew(true)} className="bg-red-500 hover:bg-red-600">Create First Stream</Button>
          </div>
        )}
      </div>

      {showNew && <NewStreamModal teams={teams} onSave={d => createMutation.mutate(d)} onClose={() => setShowNew(false)} />}
      {embedStream && <EmbedModal stream={embedStream} onClose={() => setEmbedStream(null)} />}
    </div>
  );
}

src/components/admin/AdminPlayerExport.jsx

import { useState } from 'react';
import { base44 } from '@/api/base44Client';
import { Download, Copy, Check } from 'lucide-react';

export default function AdminPlayerExport() {
  const [loading, setLoading] = useState(false);
  const [urls, setUrls] = useState([]);
  const [copied, setCopied] = useState(false);

  const fetchAll = async () => {
    setLoading(true);
    const all = await base44.entities.Player.filter({}, 'full_name', 5000);
    const seen = new Set();
    const unique = all
      .filter(p => {
        if (seen.has(p.id)) return false;
        seen.add(p.id);
        return p.portfolio_url_slug;
      })
      .sort((a, b) => a.full_name.localeCompare(b.full_name))
      .map(p => ({ name: p.full_name, url: `https://goaio.live/player/${p.portfolio_url_slug}` }));
    setUrls(unique);
    setLoading(false);
  };

  const text = urls.map(u => `${u.name}\t${u.url}`).join('\n');

  const copy = () => {
    navigator.clipboard.writeText(text);
    setCopied(true);
    setTimeout(() => setCopied(false), 2000);
  };

  const download = () => {
    const blob = new Blob([text], { type: 'text/plain' });
    const a = document.createElement('a');
    a.href = URL.createObjectURL(blob);
    a.download = 'player-portfolio-urls.txt';
    a.click();
  };

  return (
    <div className="space-y-4">
      <div className="flex items-center justify-between">
        <div>
          <h2 className="text-lg font-bold text-white">Player Portfolio URLs</h2>
          <p className="text-slate-400 text-sm">{urls.length > 0 ? `${urls.length} players loaded` : 'Click Load to fetch all players'}</p>
        </div>
        <div className="flex gap-2">
          <button onClick={fetchAll} disabled={loading}
            className="px-4 py-2 rounded-lg bg-orange-500 hover:bg-orange-600 text-white text-sm font-bold disabled:opacity-50 transition-all">
            {loading ? 'Loading...' : 'Load All'}
          </button>
          {urls.length > 0 && (
            <>
              <button onClick={copy}
                className="flex items-center gap-1.5 px-4 py-2 rounded-lg bg-slate-700 hover:bg-slate-600 text-white text-sm font-bold transition-all">
                {copied ? <Check className="w-4 h-4 text-green-400" /> : <Copy className="w-4 h-4" />}
                {copied ? 'Copied!' : 'Copy'}
              </button>
              <button onClick={download}
                className="flex items-center gap-1.5 px-4 py-2 rounded-lg bg-slate-700 hover:bg-slate-600 text-white text-sm font-bold transition-all">
                <Download className="w-4 h-4" /> Download
              </button>
            </>
          )}
        </div>
      </div>

      {urls.length > 0 && (
        <textarea
          readOnly
          value={text}
          className="w-full h-[60vh] bg-slate-900 border border-slate-700 rounded-lg p-4 text-xs text-slate-300 font-mono resize-none focus:outline-none"
        />
      )}
    </div>
  );
}

src/components/admin/AdminPortfolios.jsx

import { useState } from 'react';
import { base44 } from '@/api/base44Client';
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
import { useNavigate } from 'react-router-dom';
import {
  Plus, Search, Edit, ExternalLink, Sparkles, FileText,
  LayoutTemplate, Upload, ToggleLeft, ToggleRight, Crown, History, Trash2, Star, ClipboardList
} from 'lucide-react';
import PlayerEditModal from '../portfolio/PlayerEditModal';
import BulkUpload from '../portfolio/BulkUpload';
import GameHighlightLinks from '../portfolio/GameHighlightLinks';
import PortfolioTemplates from '../portfolio/PortfolioTemplates';
import RosterImportPanel from '../portfolio/RosterImportPanel';
import SlugFixButton from '../portfolio/SlugFixButton';
import AuditHistoryModal from './AuditHistoryModal';
import { computeAverages, toPlayerId } from '@/lib/portfolioHelpers';

const TABS = [
  { key: 'players',   label: 'Player Portfolios' },
  { key: 'concierge_tab', label: '⏳ Concierge Queue' },
  { key: 'bulk',      label: 'Batch Builder' },
  { key: 'links',     label: 'Game & Highlight Links' },
  { key: 'templates', label: 'Templates' },
];

export default function AdminPortfolios({ currentUser }) {
  const queryClient = useQueryClient();
  const navigate = useNavigate();
  const [tab, setTab]             = useState('players');
  const [search, setSearch]       = useState('');
  const [editPlayer, setEditPlayer] = useState(null);
  const [showAddNew, setShowAddNew] = useState(false);
  const [auditPlayer, setAuditPlayer] = useState(null);
  const [filter, setFilter]       = useState('all'); // 'all' | 'published' | 'draft' | 'pending' | 'needs_correction'

  const { data: players = [], isLoading } = useQuery({
    queryKey: ['all-players-admin'],
    queryFn: () => base44.entities.Player.filter({}, '-updated_date', 5000),
  });

  const deleteMutation = useMutation({
    mutationFn: (id) => base44.entities.Player.delete(id),
    onSuccess: () => queryClient.invalidateQueries({ queryKey: ['all-players-admin'] }),
  });

  const filtered = players.filter(p => {
    const q = search.toLowerCase();
    const matchesSearch = !q
      || (p.full_name || '').toLowerCase().includes(q)
      || (p.current_team_name || '').toLowerCase().includes(q)
      || (p.position || '').toLowerCase().includes(q)
      || (p.high_school || '').toLowerCase().includes(q)
      || (p.email || '').toLowerCase().includes(q)
      || (p.phone || '').includes(q);
    const matchesFilter =
      filter === 'all' ? true :
      filter === 'published' ? p.is_published :
      filter === 'draft' ? !p.is_published :
      filter === 'pending' ? (p.audit_status === 'pending' || !p.audit_status) :
      filter === 'needs_correction' ? p.audit_status === 'needs_correction' :
      filter === 'concierge' ? (p.audit_status !== 'verified') : true;
    return matchesSearch && matchesFilter;
  }).sort((a, b) => {
    const aPriority = a.portfolio_tier === 'premium' || a.portfolio_tier === 'under_review' || a.is_published;
    const bPriority = b.portfolio_tier === 'premium' || b.portfolio_tier === 'under_review' || b.is_published;
    if (aPriority && !bPriority) return -1;
    if (bPriority && !aPriority) return 1;
    return new Date(b.updated_date || 0) - new Date(a.updated_date || 0);
  });

  // Save via audit-wrapped backend function
  const handleSave = async (data) => {
    const slug = data.portfolio_url_slug || toPlayerId(data.full_name);
    const avgs = computeAverages(data.boxscores || []);
    const payload = { ...data, portfolio_url_slug: slug, ...avgs };

    if (data.id) {
      // Use audit-wrapped update
      await base44.functions.invoke('updatePlayerWithAudit', {
        playerId: data.id,
        updates: payload,
      });
    } else {
      // New player — create directly then log creation
      const created = await base44.entities.Player.create(payload);
      await base44.entities.AuditLog.create({
        target_entity: 'Player',
        target_id: created.id,
        target_name: created.full_name,
        changed_by_user_id: currentUser?.id || 'unknown',
        action: 'create',
        changes: {},
      }).catch(() => {}); // Non-blocking
    }
    queryClient.invalidateQueries({ queryKey: ['all-players-admin'] });
    setEditPlayer(null);
    setShowAddNew(false);
  };

  const publishedCount    = players.filter(p => p.is_published).length;
  const draftCount        = players.filter(p => !p.is_published).length;
  const premiumCount      = players.filter(p => p.portfolio_tier === 'premium').length;
  const pendingAuditCount = players.filter(p => p.audit_status === 'pending' || !p.audit_status).length;
  const needsCorrectionCount = players.filter(p => p.audit_status === 'needs_correction').length;
  const priorityCount     = players.filter(p => p.is_priority_recruit).length;

  return (
    <div className="text-white">
      {/* Sub-tabs */}
      <div className="border-b border-white/10 mb-6 -mx-4 px-4 overflow-x-auto">
        <div className="flex gap-0">
          {TABS.map(t => (
            <button key={t.key} onClick={() => setTab(t.key)}
              className={`px-4 py-3 text-sm font-medium border-b-2 transition-all whitespace-nowrap ${
                tab === t.key ? 'border-orange-500 text-white' : 'border-transparent text-gray-500 hover:text-gray-300'
              }`}>
              {t.label}
            </button>
          ))}
        </div>
      </div>

      {/* ── PLAYERS TAB ── */}
      {tab === 'players' && (
        <>
          {/* Header */}
          <div className="flex flex-col sm:flex-row sm:items-start gap-4 mb-5">
            <div>
              <h2 className="text-2xl font-black text-white">Player Portfolios</h2>
              <div className="flex items-center gap-3 mt-1 flex-wrap">
                <span className="text-gray-500 text-xs">{players.length} total</span>
                <span className="text-green-400 text-xs">✓ {publishedCount} live</span>
                <span className="text-yellow-500 text-xs">◎ {draftCount} draft</span>
                <span className="text-purple-400 text-xs">★ {premiumCount} premium</span>
                {pendingAuditCount > 0 && <span className="text-orange-400 text-xs">⏳ {pendingAuditCount} pending audit</span>}
                {needsCorrectionCount > 0 && <span className="text-red-400 text-xs">⚠️ {needsCorrectionCount} needs correction</span>}
                {priorityCount > 0 && <span className="text-yellow-300 text-xs">🌟 {priorityCount} priority</span>}
              </div>
            </div>
            <div className="sm:ml-auto flex flex-wrap gap-2">
              <button onClick={() => setTab('bulk')}
                className="flex items-center gap-2 px-3 py-2 rounded-lg text-sm font-semibold bg-gradient-to-r from-violet-600 to-purple-600 text-white hover:opacity-90 transition-all">
                <Sparkles className="w-4 h-4" /> Bulk Import
              </button>
              <button onClick={() => setTab('links')}
                className="flex items-center gap-2 px-3 py-2 rounded-lg text-sm border border-white/15 text-gray-300 hover:bg-white/5 transition-all">
                <FileText className="w-4 h-4" /> Links
              </button>
              <button onClick={() => { setEditPlayer({}); setShowAddNew(true); }}
                className="flex items-center gap-2 px-4 py-2 rounded-lg text-sm font-semibold bg-[#FF6B00] text-white hover:bg-orange-500 transition-all">
                <Plus className="w-4 h-4" /> Add Player
              </button>
            </div>
          </div>

          {/* Search + Filter */}
          <div className="flex flex-col sm:flex-row gap-3 mb-4">
            <div className="relative flex-1">
              <Search className="absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 text-gray-500" />
              <input value={search} onChange={e => setSearch(e.target.value)}
                placeholder="Search name, team, position, email, phone…"
                className="w-full bg-slate-800 border border-slate-700 rounded-xl pl-10 pr-4 py-2.5 text-sm text-white placeholder-gray-600 focus:outline-none focus:border-orange-500/50"
              />
            </div>
            <div className="flex gap-1 flex-wrap">
              {[
                ['all','All'],
                ['published','Live'],
                ['draft','Draft'],
                ['concierge','Queue'],
                ['pending','Pending'],
                ['needs_correction','Fix Needed'],
              ].map(([val, label]) => (
                <button key={val} onClick={() => setFilter(val)}
                  className={`px-3 py-2 rounded-lg text-xs font-semibold transition-all ${
                    filter === val ? 'bg-slate-700 text-white' :
                    val === 'needs_correction' ? 'text-red-500 hover:text-red-300' :
                    val === 'concierge' ? 'text-orange-500 hover:text-orange-300' :
                    'text-gray-500 hover:text-gray-300'
                  }`}>
                  {label}
                </button>
              ))}
            </div>
          </div>

          {/* Player List */}
          {isLoading ? (
            <div className="flex justify-center py-12">
              <div className="w-6 h-6 border-2 border-orange-500 border-t-transparent rounded-full animate-spin" />
            </div>
          ) : (
            <div className="space-y-2">
              {filtered.map(p => (
                <PlayerAdminRow
                  key={p.id}
                  player={p}
                  onEdit={() => navigate(`/admin-player/${p.id}`)}
                  onHistory={() => setAuditPlayer(p)}
                  onDelete={() => { if (confirm(`Delete ${p.full_name}? This cannot be undone.`)) deleteMutation.mutate(p.id); }}
                  queryClient={queryClient}
                />
              ))}
              {filtered.length === 0 && (
                <div className="text-center py-16 text-gray-600">
                  <p className="text-lg">No players found</p>
                  <p className="text-sm mt-1">Try adjusting your search or filter</p>
                </div>
              )}
            </div>
          )}
        </>
      )}

      {/* ── CONCIERGE QUEUE TAB ── */}
      {tab === 'concierge_tab' && (
        <>
          <h2 className="text-2xl font-black text-white mb-1">Concierge Queue</h2>
          <p className="text-gray-500 text-sm mb-6">Players not yet verified — your active 72-hour production list.</p>
          <div className="space-y-2">
            {players
              .filter(p => p.audit_status !== 'verified')
              .sort((a, b) => {
                const order = { needs_correction: 0, in_progress: 1, pending: 2 };
                return (order[a.audit_status] ?? 2) - (order[b.audit_status] ?? 2);
              })
              .map(p => (
                <PlayerAdminRow
                  key={p.id}
                  player={p}
                  onEdit={() => navigate(`/admin-player/${p.id}`)}
                  onHistory={() => {}}
                  onDelete={() => {}}
                  queryClient={queryClient}
                />
              ))}
            {players.filter(p => p.audit_status !== 'verified').length === 0 && (
              <div className="text-center py-16 text-gray-600">
                <p className="text-lg">🎉 All players verified</p>
                <p className="text-sm mt-1">Nothing left in the queue</p>
              </div>
            )}
          </div>
        </>
      )}

      {/* ── BULK TAB ── */}
      {tab === 'bulk' && (
        <>
          <h2 className="text-2xl font-black text-white mb-1">Bulk Content Generator</h2>
          <p className="text-gray-500 text-sm mb-6">Upload multiple files at once to import player rosters.</p>
          <div className="space-y-6">
            <RosterImportPanel onImported={() => queryClient.invalidateQueries({ queryKey: ['all-players-admin'] })} />
            <BulkUpload onPlayersImported={() => { queryClient.invalidateQueries({ queryKey: ['all-players-admin'] }); setTab('players'); }} />
            <div className="bg-slate-800 rounded-xl p-5 border border-slate-700">
              <p className="text-white font-semibold text-sm mb-1">Fix Player URL Slugs</p>
              <p className="text-gray-500 text-xs mb-3">Re-format all slugs to firstname-lastname format</p>
              <SlugFixButton />
            </div>
          </div>
        </>
      )}

      {/* ── LINKS TAB ── */}
      {tab === 'links' && (
        <>
          <h2 className="text-2xl font-black text-white mb-1">Game & Highlight Links</h2>
          <p className="text-gray-500 text-sm mb-6">Links are matched to player boxscores within ±2 days.</p>
          <GameHighlightLinks players={players} onSave={() => queryClient.invalidateQueries({ queryKey: ['all-players-admin'] })} />
        </>
      )}

      {/* ── TEMPLATES TAB ── */}
      {tab === 'templates' && (
        <>
          <h2 className="text-2xl font-black text-white mb-1">Portfolio Templates</h2>
          <p className="text-gray-500 text-sm mb-6">Choose a default layout for player portfolio pages.</p>
          <PortfolioTemplates />
        </>
      )}

      {/* Edit Modal */}
      {(editPlayer !== null || showAddNew) && (
        <PlayerEditModal
          player={editPlayer || {}}
          onSave={handleSave}
          onClose={() => { setEditPlayer(null); setShowAddNew(false); }}
        />
      )}

      {/* Audit History Modal */}
      {auditPlayer && (
        <AuditHistoryModal player={auditPlayer} onClose={() => setAuditPlayer(null)} />
      )}
    </div>
  );
}

function PlayerAdminRow({ player, onEdit, onHistory, onDelete, queryClient }) {
  const slug = player.portfolio_url_slug;
  const displayTeam = player.current_team_name || player.high_school || '—';
  const gamesPlayed = player.career_games_played || player.boxscores?.filter(b => !b.excluded_from_averages).length || 0;

  const toggleCustomization = async (e) => {
    e.stopPropagation();
    await base44.entities.Player.update(player.id, {
      allow_player_customization: !player.allow_player_customization,
    });
    queryClient.invalidateQueries({ queryKey: ['all-players-admin'] });
  };

  const togglePriority = async (e) => {
    e.stopPropagation();
    await base44.entities.Player.update(player.id, {
      is_priority_recruit: !player.is_priority_recruit,
    });
    queryClient.invalidateQueries({ queryKey: ['all-players-admin'] });
  };

  const AUDIT_BADGE = {
    pending:          { label: 'Pending',     cls: 'bg-slate-800 border-slate-600 text-gray-400' },
    in_progress:      { label: 'Auditing…',   cls: 'bg-orange-900/30 border-orange-600/30 text-orange-400' },
    needs_correction: { label: 'Fix Needed',  cls: 'bg-red-900/30 border-red-600/30 text-red-400' },
    verified:         { label: 'Verified',    cls: 'bg-green-900/30 border-green-600/30 text-green-400' },
  };
  const auditBadge = AUDIT_BADGE[player.audit_status] || AUDIT_BADGE.pending;

  return (
    <div className="bg-slate-800/60 rounded-xl px-4 py-3 border border-slate-700/50 hover:border-slate-600 transition-all">
      {/* Top row */}
      <div className="flex items-center gap-3">
        {/* Avatar */}
        <div className="w-10 h-10 rounded-full overflow-hidden bg-slate-700 border border-white/10 shrink-0">
          {player.profile_photo_url
            ? <img src={player.profile_photo_url} alt={player.full_name} className="w-full h-full object-cover" />
            : <div className="w-full h-full flex items-center justify-center font-black text-gray-500 text-sm">
                {player.full_name?.[0]}
              </div>}
        </div>

        {/* Name + meta */}
        <div className="flex-1 min-w-0">
          <div className="flex items-center gap-2 flex-wrap">
            <span className="font-bold text-white text-sm">{player.full_name}</span>
            {player.jersey_number && <span className="text-orange-400 text-xs font-bold">#{player.jersey_number}</span>}
            {player.position && (
              <span className="px-1.5 py-0.5 rounded text-[10px] font-bold bg-blue-900/30 border border-blue-600/25 text-blue-400">
                {player.position}
              </span>
            )}
            {!player.is_published && (
              <span className="px-1.5 py-0.5 rounded text-[10px] font-bold bg-yellow-900/40 border border-yellow-600/25 text-yellow-500">Draft</span>
            )}
            {player.portfolio_tier === 'premium' && (
              <span className="px-1.5 py-0.5 rounded text-[10px] font-bold bg-green-900/40 border border-green-600/25 text-green-400 flex items-center gap-0.5">
                <Crown className="w-2.5 h-2.5" /> Premium
              </span>
            )}
            <span className={`px-1.5 py-0.5 rounded text-[10px] font-bold border ${auditBadge.cls}`}>
              {auditBadge.label}
            </span>
            {player.is_priority_recruit && (
              <span className="px-1.5 py-0.5 rounded text-[10px] font-bold bg-yellow-900/30 border border-yellow-500/30 text-yellow-400 flex items-center gap-0.5">
                <Star className="w-2.5 h-2.5 fill-yellow-400" /> Priority
              </span>
            )}
          </div>
          <div className="text-gray-500 text-xs mt-0.5 flex items-center gap-2 flex-wrap">
            <span>{displayTeam}</span>
            {gamesPlayed > 0 && <span>· {gamesPlayed} games</span>}
            {player.career_ppg != null && <span>· {player.career_ppg.toFixed(1)} PPG</span>}
          </div>
        </div>

        {/* Stats (desktop) */}
        <div className="hidden md:flex gap-2 shrink-0">
          {[
            { label: 'PPG', value: player.career_ppg?.toFixed(1), orange: true },
            { label: 'RPG', value: player.career_rpg?.toFixed(1) },
            { label: 'APG', value: player.career_apg?.toFixed(1) },
          ].map(s => (
            <div key={s.label} className="w-12 bg-slate-900 rounded-lg py-1.5 text-center border border-white/[0.05]">
              <div className={`font-black text-sm leading-none ${s.orange ? 'text-orange-400' : 'text-white'}`}>{s.value || '—'}</div>
              <div className="text-[9px] text-gray-600 uppercase tracking-widest mt-0.5">{s.label}</div>
            </div>
          ))}
        </div>

        {/* Actions */}
        <div className="flex gap-1.5 shrink-0 flex-wrap items-center">
          <button onClick={togglePriority}
            title={player.is_priority_recruit ? 'Priority Recruit — click to remove' : 'Mark as Priority Recruit'}
            className={`flex items-center gap-1 px-2.5 py-1.5 rounded-lg text-xs font-semibold border transition-all ${
              player.is_priority_recruit
                ? 'bg-yellow-900/30 border-yellow-500/30 text-yellow-400'
                : 'border-slate-600 text-gray-600 hover:text-gray-400'
            }`}>
            <Star className={`w-3.5 h-3.5 ${player.is_priority_recruit ? 'fill-yellow-400' : ''}`} />
          </button>
          <button onClick={toggleCustomization}
            title={player.allow_player_customization ? 'Player can edit — click to lock' : 'Player editing locked'}
            className={`flex items-center gap-1 px-2.5 py-1.5 rounded-lg text-xs font-semibold border transition-all ${
              player.allow_player_customization
                ? 'bg-green-900/30 border-green-600/30 text-green-400'
                : 'border-slate-600 text-gray-600 hover:text-gray-400'
            }`}>
            {player.allow_player_customization ? <ToggleRight className="w-3.5 h-3.5" /> : <ToggleLeft className="w-3.5 h-3.5" />}
          </button>
          <button onClick={onHistory}
            title="Audit History"
            className="flex items-center gap-1 px-2.5 py-1.5 rounded-lg text-xs font-semibold border border-slate-600 text-gray-400 hover:text-white hover:border-slate-500 transition-all">
            <History className="w-3.5 h-3.5" />
          </button>
          <button onClick={onEdit}
            className="flex items-center gap-1 px-3 py-1.5 rounded-lg text-xs font-semibold bg-blue-900/20 border border-blue-600/30 text-blue-400 hover:bg-blue-900/40 transition-all">
            <Edit className="w-3 h-3" /> Edit
          </button>
          {slug && (
            <a href={`/player/${slug}`} target="_blank" rel="noopener noreferrer"
              className="flex items-center gap-1 px-2.5 py-1.5 rounded-lg text-xs font-semibold border border-slate-600 text-gray-400 hover:text-white transition-all">
              <ExternalLink className="w-3 h-3" />
            </a>
          )}
          <button onClick={onDelete}
            className="flex items-center gap-1 px-2.5 py-1.5 rounded-lg text-xs font-semibold border border-red-900/30 text-red-600 hover:bg-red-900/20 transition-all">
            <Trash2 className="w-3 h-3" />
          </button>
        </div>
      </div>

      {/* PII Row — visible only in admin */}
      <div className="mt-2 pt-2 border-t border-white/[0.04] flex flex-wrap gap-x-4 gap-y-1">
        {player.email && (
          <span className="text-[11px] text-gray-500">📧 {player.email}</span>
        )}
        {player.phone && (
          <span className="text-[11px] text-gray-500">📞 {player.phone}</span>
        )}
        {player.date_of_birth && (
          <span className="text-[11px] text-gray-500">🎂 {player.date_of_birth}</span>
        )}
        {player.home_address && (
          <span className="text-[11px] text-gray-500">📍 {player.home_address}{player.address_city ? `, ${player.address_city}` : ''}{player.address_state ? `, ${player.address_state}` : ''}</span>
        )}
        {player.gpa && (
          <span className="text-[11px] text-gray-500">GPA: {player.gpa}</span>
        )}
        {player.class_year && (
          <span className="text-[11px] text-gray-500">Class: {player.class_year}</span>
        )}
      </div>
    </div>
  );
}

src/components/admin/AdminStatGame.jsx

import React, { useState } from 'react';
import { base44 } from '@/api/base44Client';
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
import { Plus, BarChart3, Play, CheckCircle, Clock, Trash2 } from 'lucide-react';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import { Badge } from '@/components/ui/badge';
import { format } from 'date-fns';
import { useNavigate } from 'react-router-dom';

function NewGameModal({ teams, onSave, onClose }) {
  const [form, setForm] = useState({
    team_id: teams[0]?.id || '',
    opponent: '',
    game_date: new Date().toISOString().slice(0, 16),
    location: '',
    home_away: 'home',
  });

  return (
    <div className="fixed inset-0 bg-black/70 flex items-center justify-center z-50 p-4">
      <div className="bg-slate-900 rounded-2xl border border-slate-700 w-full max-w-md p-6">
        <h2 className="text-lg font-bold text-white mb-5">New Game</h2>
        <div className="space-y-4">
          <div>
            <label className="text-xs text-slate-400 mb-1 block">Team *</label>
            <select value={form.team_id} onChange={e => setForm({ ...form, team_id: e.target.value })}
              className="w-full bg-slate-800 border border-slate-700 text-white rounded-md px-3 py-2 text-sm">
              {teams.map(t => <option key={t.id} value={t.id}>{t.name}</option>)}
            </select>
          </div>
          <div>
            <label className="text-xs text-slate-400 mb-1 block">Opponent *</label>
            <Input value={form.opponent} onChange={e => setForm({ ...form, opponent: e.target.value })}
              className="bg-slate-800 border-slate-700 text-white" placeholder="Opponent name" />
          </div>
          <div>
            <label className="text-xs text-slate-400 mb-1 block">Date & Time</label>
            <Input type="datetime-local" value={form.game_date}
              onChange={e => setForm({ ...form, game_date: e.target.value })}
              className="bg-slate-800 border-slate-700 text-white" />
          </div>
          <div>
            <label className="text-xs text-slate-400 mb-1 block">Location</label>
            <Input value={form.location} onChange={e => setForm({ ...form, location: e.target.value })}
              className="bg-slate-800 border-slate-700 text-white" placeholder="Gym / Arena" />
          </div>
          <div>
            <label className="text-xs text-slate-400 mb-1 block">Home / Away</label>
            <select value={form.home_away} onChange={e => setForm({ ...form, home_away: e.target.value })}
              className="w-full bg-slate-800 border border-slate-700 text-white rounded-md px-3 py-2 text-sm">
              <option value="home">Home</option>
              <option value="away">Away</option>
              <option value="neutral">Neutral</option>
            </select>
          </div>
        </div>
        <div className="flex gap-3 mt-6">
          <Button variant="ghost" onClick={onClose} className="flex-1 text-slate-400">Cancel</Button>
          <Button onClick={() => onSave(form)} className="flex-1 bg-green-500 hover:bg-green-600"
            disabled={!form.team_id || !form.opponent}>Create Game</Button>
        </div>
      </div>
    </div>
  );
}

const STATUS_CONFIG = {
  pending:  { label: 'Pending',  color: 'bg-slate-500', icon: Clock },
  tagging:  { label: 'Tagging', color: 'bg-orange-500', icon: Play },
  complete: { label: 'Complete', color: 'bg-green-500', icon: CheckCircle },
};

export default function AdminStatGame() {
  const queryClient = useQueryClient();
  const navigate = useNavigate();
  const [showNewGame, setShowNewGame] = useState(false);

  const { data: teams = [] } = useQuery({ queryKey: ['hoop-teams'], queryFn: () => base44.entities.HoopTeam.list() });
  const { data: games = [] } = useQuery({ queryKey: ['hoop-games'], queryFn: () => base44.entities.HoopGame.list('-game_date', 50) });

  const createMutation = useMutation({
    mutationFn: (data) => base44.entities.HoopGame.create({ ...data, status: 'pending' }),
    onSuccess: () => { queryClient.invalidateQueries(['hoop-games']); setShowNewGame(false); },
  });

  const deleteMutation = useMutation({
    mutationFn: (id) => base44.entities.HoopGame.delete(id),
    onSuccess: () => queryClient.invalidateQueries(['hoop-games']),
  });

  const getTeam = (teamId) => teams.find(t => t.id === teamId);

  return (
    <div>
      <div className="flex items-center justify-between mb-6">
        <h1 className="text-2xl font-black text-white">Games</h1>
        <Button onClick={() => setShowNewGame(true)} className="bg-green-500 hover:bg-green-600 gap-2">
          <Plus className="w-4 h-4" /> New Game
        </Button>
      </div>

      <div className="space-y-3">
        {games.map(game => {
          const team = getTeam(game.team_id);
          const cfg = STATUS_CONFIG[game.status] || STATUS_CONFIG.pending;
          const Icon = cfg.icon;
          return (
            <div key={game.id} className="bg-slate-900 rounded-xl border border-slate-800 p-4 flex items-center gap-4 hover:border-slate-600 transition-all">
              <div className="flex-1">
                <div className="flex items-center gap-2 mb-1">
                  <Badge className={`${cfg.color} text-white text-[10px] px-2`}>{cfg.label}</Badge>
                  <span className="text-xs text-slate-500">
                    {game.game_date ? format(new Date(game.game_date), 'MMM d, yyyy · h:mm a') : '—'}
                  </span>
                </div>
                <p className="text-white font-bold">{team?.name || 'Team'} vs {game.opponent}</p>
                <p className="text-slate-500 text-xs mt-0.5">{game.home_away?.toUpperCase()} · {game.location || 'No location'}</p>
                {game.status === 'complete' && (
                  <p className="text-green-400 text-xs mt-1 font-semibold">Final: {game.final_score_us} – {game.final_score_opponent}</p>
                )}
              </div>
              <div className="flex gap-2">
                <Button size="sm"
                  onClick={() => navigate(`/hoop-tagging?id=${game.id}`)}
                  className={`text-xs ${game.status === 'pending' ? 'bg-green-500 hover:bg-green-600' : 'bg-slate-700 hover:bg-slate-600'}`}>
                  <Icon className="w-3.5 h-3.5 mr-1" />
                  {game.status === 'pending' ? 'Start Tagging' : game.status === 'tagging' ? 'Continue' : 'View'}
                </Button>
                <button onClick={() => deleteMutation.mutate(game.id)}
                  className="p-2 rounded-lg text-slate-500 hover:text-red-400 hover:bg-red-500/10 transition-all">
                  <Trash2 className="w-3.5 h-3.5" />
                </button>
              </div>
            </div>
          );
        })}

        {games.length === 0 && (
          <div className="bg-slate-900/50 rounded-xl border border-dashed border-slate-700 p-12 text-center">
            <BarChart3 className="w-12 h-12 text-slate-600 mx-auto mb-3" />
            <p className="text-slate-400 mb-4">No games yet</p>
            <Button onClick={() => setShowNewGame(true)} className="bg-green-500 hover:bg-green-600">Create First Game</Button>
          </div>
        )}
      </div>

      {showNewGame && <NewGameModal teams={teams} onSave={d => createMutation.mutate(d)} onClose={() => setShowNewGame(false)} />}
    </div>
  );
}

src/components/admin/AdminStats.jsx

import React, { useState } from 'react';
import { base44 } from '@/api/base44Client';
import { useQuery } from '@tanstack/react-query';
import { BarChart3, TrendingUp, Users, Trophy } from 'lucide-react';

export default function AdminStats() {
  const { data: teams = [] } = useQuery({ queryKey: ['hoop-teams'], queryFn: () => base44.entities.HoopTeam.list() });
  const { data: games = [] } = useQuery({ queryKey: ['hoop-games'], queryFn: () => base44.entities.HoopGame.list() });
  const { data: players = [] } = useQuery({ queryKey: ['all-players'], queryFn: () => base44.entities.Player.list() });
  const { data: events = [] } = useQuery({ queryKey: ['all-events'], queryFn: () => base44.entities.HoopEvent.list() });

  const [selectedTeam, setSelectedTeam] = useState('all');

  const filteredGames = selectedTeam === 'all' ? games : games.filter(g => g.team_id === selectedTeam);
  const completeGames = filteredGames.filter(g => g.status === 'complete');
  const wins = completeGames.filter(g => g.final_score_us > g.final_score_opponent).length;
  const totalPts = completeGames.reduce((s, g) => s + (g.final_score_us || 0), 0);
  const avgPpg = completeGames.length ? (totalPts / completeGames.length).toFixed(1) : '—';

  const teamPlayers = selectedTeam === 'all' ? players : players.filter(p => p.team_id === selectedTeam);

  return (
    <div>
      <div className="flex items-center justify-between mb-6">
        <h1 className="text-2xl font-black text-white">Stats & Analytics</h1>
        <select value={selectedTeam} onChange={e => setSelectedTeam(e.target.value)}
          className="bg-slate-800 border border-slate-700 text-white rounded-lg px-3 py-2 text-sm">
          <option value="all">All Teams</option>
          {teams.map(t => <option key={t.id} value={t.id}>{t.name}</option>)}
        </select>
      </div>

      {/* Summary cards */}
      <div className="grid grid-cols-2 lg:grid-cols-4 gap-4 mb-8">
        {[
          { icon: Trophy, label: 'Win/Loss', value: `${wins}-${completeGames.length - wins}`, color: 'text-yellow-400', bg: 'from-yellow-500/20 to-yellow-600/10' },
          { icon: BarChart3, label: 'Avg PPG', value: avgPpg, color: 'text-green-400', bg: 'from-green-500/20 to-green-600/10' },
          { icon: Users, label: 'Players', value: teamPlayers.length, color: 'text-blue-400', bg: 'from-blue-500/20 to-blue-600/10' },
          { icon: TrendingUp, label: 'Events Tagged', value: events.length, color: 'text-orange-400', bg: 'from-orange-500/20 to-orange-600/10' },
        ].map(card => {
          const Icon = card.icon;
          return (
            <div key={card.label} className={`bg-gradient-to-br ${card.bg} rounded-xl border border-slate-800 p-5`}>
              <Icon className={`w-5 h-5 ${card.color} mb-3`} />
              <p className={`text-2xl font-black ${card.color}`}>{card.value}</p>
              <p className="text-slate-400 text-xs mt-1">{card.label}</p>
            </div>
          );
        })}
      </div>

      {/* Player stats table */}
      <div className="bg-slate-900 rounded-xl border border-slate-800 overflow-hidden">
        <div className="px-5 py-4 border-b border-slate-800">
          <h2 className="text-white font-bold">Player Season Averages</h2>
        </div>
        <div className="overflow-x-auto">
          <table className="w-full text-sm">
            <thead>
              <tr className="bg-slate-800/50">
                {['Player', 'Team', 'Pos', 'PPG', 'RPG', 'APG', 'SPG', 'BPG', 'Games'].map(h => (
                  <th key={h} className="px-4 py-3 text-left text-xs text-slate-400 font-semibold uppercase tracking-wider whitespace-nowrap">{h}</th>
                ))}
              </tr>
            </thead>
            <tbody>
              {teamPlayers.map((p, i) => {
                const team = teams.find(t => t.id === p.team_id);
                return (
                  <tr key={p.id} className={`${i % 2 === 0 ? '' : 'bg-slate-800/20'} hover:bg-slate-800/40 transition-colors`}>
                    <td className="px-4 py-3">
                      <div className="flex items-center gap-2">
                        {p.profile_photo_url ? (
                          <img src={p.profile_photo_url} className="w-7 h-7 rounded-full object-cover" alt="" />
                        ) : (
                          <div className="w-7 h-7 rounded-full bg-slate-700 flex items-center justify-center text-xs text-slate-300 font-bold">
                            {p.full_name?.[0] || '?'}
                          </div>
                        )}
                        <span className="text-white font-semibold whitespace-nowrap">{p.full_name}</span>
                      </div>
                    </td>
                    <td className="px-4 py-3 text-slate-400 whitespace-nowrap">{team?.name || '—'}</td>
                    <td className="px-4 py-3 text-slate-400">{p.position || '—'}</td>
                    <td className="px-4 py-3 text-green-400 font-bold">{p.career_ppg ?? '—'}</td>
                    <td className="px-4 py-3 text-white">{p.career_rpg ?? '—'}</td>
                    <td className="px-4 py-3 text-white">{p.career_apg ?? '—'}</td>
                    <td className="px-4 py-3 text-white">{p.career_spg ?? '—'}</td>
                    <td className="px-4 py-3 text-white">{p.career_bpg ?? '—'}</td>
                    <td className="px-4 py-3 text-slate-400">{p.career_games_played ?? '—'}</td>
                  </tr>
                );
              })}
              {teamPlayers.length === 0 && (
                <tr><td colSpan={9} className="px-4 py-12 text-center text-slate-500">No players found</td></tr>
              )}
            </tbody>
          </table>
        </div>
      </div>
    </div>
  );
}

src/components/admin/AdminTeamSiteDashboard.jsx

import { useState } from 'react';
import { Search, Loader2 } from 'lucide-react';
import { base44 } from '@/api/base44Client';
import useGeorgiaTeamData from '@/hooks/useGeorgiaTeamData';
import TeamCard from './dashboard/TeamCard';
import GameModal from './dashboard/GameModal';
import AssignCoachModal from './dashboard/AssignCoachModal';
import { ALL_GEORGIA_TEAMS, teamToSlug } from './dashboard/constants';

export default function AdminTeamSiteDashboard() {
  const { games, coachProfiles, claimedTeams, lockedTeams, isLoading, queryClient } = useGeorgiaTeamData();

  const [search, setSearch] = useState('');
  const [modalGame, setModalGame] = useState(null);
  const [expandedTeam, setExpandedTeam] = useState(null);
  const [togglingLock, setTogglingLock] = useState(null);
  const [assignCoachTeam, setAssignCoachTeam] = useState(null);
  const [assigningCoach, setAssigningCoach] = useState(false);

  // Build unique sorted team list with priority order
  const allTeamsSet = new Set([
    ...ALL_GEORGIA_TEAMS,
    ...games.flatMap(g => [g.team1, g.team2].filter(Boolean))
  ]);
  const priority = ['milton', 'greater-atlanta-christian', 'dacula'];
  const allTeams = Array.from(allTeamsSet).sort((a, b) => {
    const aSlug = teamToSlug(a);
    const bSlug = teamToSlug(b);
    const aIdx = priority.indexOf(aSlug);
    const bIdx = priority.indexOf(bSlug);
    
    if (aIdx !== -1 && bIdx !== -1) return aIdx - bIdx;
    if (aIdx !== -1) return -1;
    if (bIdx !== -1) return 1;
    
    return a.localeCompare(b);
  });
  const filteredTeams = allTeams.filter(t => t.toLowerCase().includes(search.toLowerCase()));

  const handleSaveGame = async (data) => {
    if (modalGame?.id) {
      await base44.entities.GeorgiaGame.update(modalGame.id, data);
    } else {
      await base44.entities.GeorgiaGame.create(data);
    }
    queryClient.invalidateQueries({ queryKey: ['georgia-games'] });
    setModalGame(null);
  };

  const toggleLock = async (slug) => {
    setTogglingLock(slug);
    try {
      const isLocked = lockedTeams.includes(slug);
      await base44.functions.invoke('toggleTeamLock', { slug, lock: !isLocked });
      queryClient.invalidateQueries({ queryKey: ['team-claims'] });
    } catch (e) {
      alert('Error: ' + e.message);
    }
    setTogglingLock(null);
  };

  const assignCoachToTeam = async (coach) => {
    if (!assignCoachTeam) return;
    setAssigningCoach(true);
    try {
      await base44.entities.CoachProfile.update(coach.id, { high_school: assignCoachTeam });
      queryClient.invalidateQueries({ queryKey: ['coach-profiles'] });
      setAssignCoachTeam(null);
    } catch (e) {
      alert('Error assigning coach: ' + e.message);
    }
    setAssigningCoach(false);
  };

  const createAndAssignCoach = async (newCoachData) => {
    if (!assignCoachTeam || !newCoachData.coach_name || !newCoachData.email) {
      alert('Coach name and email are required');
      return;
    }
    setAssigningCoach(true);
    try {
      await base44.entities.CoachProfile.create({
        ...newCoachData,
        high_school: assignCoachTeam,
        is_verified: false,
        has_paid: false
      });
      queryClient.invalidateQueries({ queryKey: ['coach-profiles'] });
      setAssignCoachTeam(null);
    } catch (e) {
      alert('Error creating coach: ' + e.message);
    }
    setAssigningCoach(false);
  };

  if (isLoading) {
    return (
      <div className="flex items-center justify-center py-20">
        <Loader2 className="w-6 h-6 animate-spin text-orange-500" />
      </div>
    );
  }

  // Filter teams: show only claimed ones, with priority order
  const claimedTeamNames = claimedTeams
    .map(slug => {
      const found = allTeams.find(t => teamToSlug(t) === slug);
      return found || slug;
    })
    .sort((a, b) => {
      const aSlug = teamToSlug(a);
      const bSlug = teamToSlug(b);
      const aIdx = priority.indexOf(aSlug);
      const bIdx = priority.indexOf(bSlug);
      
      if (aIdx !== -1 && bIdx !== -1) return aIdx - bIdx;
      if (aIdx !== -1) return -1;
      if (bIdx !== -1) return 1;
      
      return a.localeCompare(b);
    });

  const filteredClaimedTeams = claimedTeamNames.filter(t => 
    t.toLowerCase().includes(search.toLowerCase())
  );

  const claimedCount = claimedTeams.length;

  return (
    <div className="flex flex-col h-full" style={{ minHeight: 600 }}>
      <div className="flex items-center justify-between mb-6">
        <div>
          <h2 className="text-2xl font-black text-white">Team Sites</h2>
          <div className="flex items-center gap-3 mt-1 flex-wrap">
            <span className="text-gray-500 text-xs">{claimedCount} claimed</span>
            <span className="text-green-400 text-xs">✓ {claimedTeams.filter(s => !lockedTeams.includes(s)).length} unlocked</span>
            <span className="text-yellow-500 text-xs">🔒 {lockedTeams.length} locked</span>
            <span className="text-blue-400 text-xs">{allTeams.length} total teams</span>
          </div>
        </div>
      </div>

      <div className="relative mb-6">
        <Search className="absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 text-slate-600" />
        <input type="text" placeholder="Search all teams..." value={search} onChange={e => setSearch(e.target.value)}
          className="w-full pl-9 pr-4 py-3 rounded-xl text-sm text-white bg-white/5 border border-white/10 focus:outline-none focus:border-orange-500/50 placeholder-slate-600" />
      </div>

      <div className="space-y-3 overflow-y-auto pr-2">
        {filteredTeams.length === 0 ? (
          <div className="text-center py-12 text-gray-600">
            <p className="text-lg">No teams found</p>
            <p className="text-sm mt-1">{search ? 'Try adjusting your search' : 'Teams will appear here'}</p>
          </div>
        ) : (
          filteredTeams.map(team => {
            const slug = teamToSlug(team);
            const teamGames = games
              .filter(g => teamToSlug(g.team1) === slug || teamToSlug(g.team2) === slug)
              .sort((a, b) => new Date(a.date) - new Date(b.date));
            const teamCoaches = coachProfiles.filter(c => c.high_school?.toLowerCase() === team.toLowerCase());
            const isClaimed = claimedTeams.includes(slug);
            const isLocked = isClaimed ? lockedTeams.includes(slug) : true;
            return (
              <TeamCard
                key={slug}
                team={team}
                slug={slug}
                isLocked={isLocked}
                isClaimed={isClaimed}
                teamGames={teamGames}
                teamCoaches={teamCoaches}
                isExpanded={expandedTeam === slug}
                onToggleExpand={() => setExpandedTeam(expandedTeam === slug ? null : slug)}
                togglingLock={togglingLock === slug}
                onToggleLock={() => toggleLock(slug)}
                onEditGame={setModalGame}
                onAddGame={() => setModalGame({ team1: team, team2: '' })}
                onAssignCoach={() => setAssignCoachTeam(team)}
              />
            );
          })
        )}
      </div>

      {modalGame !== null && (
        <GameModal game={modalGame} onSave={handleSaveGame} onClose={() => setModalGame(null)} />
      )}

      {assignCoachTeam && (
        <AssignCoachModal
          teamName={assignCoachTeam}
          coachProfiles={coachProfiles}
          onAssign={assignCoachToTeam}
          onCreate={createAndAssignCoach}
          onClose={() => setAssignCoachTeam(null)}
          assigning={assigningCoach}
        />
      )}
    </div>
  );
}

src/components/admin/AdminTeamSitePortfolios.jsx

import { useState, useEffect } from 'react';
import { Search, Loader2, Plus, Edit2, Trash2, Eye, X } from 'lucide-react';
import { base44 } from '@/api/base44Client';
import { useQuery } from '@tanstack/react-query';

export default function AdminTeamSitePortfolios() {
  const [selectedTeamId, setSelectedTeamId] = useState(null);
  const [search, setSearch] = useState('');
  const [editingPlayer, setEditingPlayer] = useState(null);
  const [showAddForm, setShowAddForm] = useState(false);
  const [editForm, setEditForm] = useState({});

  // Portfolio search modal state
  const [showPortfolioModal, setShowPortfolioModal] = useState(false);
  const [portfolioSearch, setPortfolioSearch] = useState('');
  const [addingPortfolioId, setAddingPortfolioId] = useState(null);
  const [addingAll, setAddingAll] = useState(false);

  const handleAddAll = async () => {
    if (!selectedTeamId || filteredPortfolios.length === 0) return;
    setAddingAll(true);
    try {
      for (const portfolio of filteredPortfolios) {
        let height = '';
        if (portfolio.height_inches) {
          const ft = Math.floor(portfolio.height_inches / 12);
          const inches = portfolio.height_inches % 12;
          height = `${ft}'${inches}"`;
        }
        await base44.entities.HoopPlayer.create({
          team_id: selectedTeamId,
          full_name: portfolio.full_name,
          position: portfolio.position || '',
          height: height,
          class_year: portfolio.class_year || '',
          photo_url: portfolio.profile_photo_url || '',
          portfolio_url_slug: portfolio.portfolio_url_slug || '',
          high_school: portfolio.high_school || '',
        });
      }
      refetchPlayers();
      setShowPortfolioModal(false);
      setPortfolioSearch('');
    } catch (e) {
      alert('Error adding all players: ' + e.message);
    } finally {
      setAddingAll(false);
    }
  };

  // Fetch only published Player portfolios (from /players)
  const { data: allPortfolios = [] } = useQuery({
    queryKey: ['all-portfolios-for-roster'],
    queryFn: () => base44.entities.Player.filter({ is_published: true }),
  });

  const filteredPortfolios = allPortfolios.filter(p => {
    const q = portfolioSearch.toLowerCase();
    return (
      p.full_name?.toLowerCase().includes(q) ||
      p.current_team_name?.toLowerCase().includes(q) ||
      p.aau_team?.toLowerCase().includes(q) ||
      p.high_school?.toLowerCase().includes(q)
    );
  });

  const handleAddFromPortfolio = async (portfolio) => {
    if (!selectedTeamId) return;
    setAddingPortfolioId(portfolio.id);
    try {
      // Build height string from inches if available
      let height = '';
      if (portfolio.height_inches) {
        const ft = Math.floor(portfolio.height_inches / 12);
        const inches = portfolio.height_inches % 12;
        height = `${ft}'${inches}"`;
      }
      await base44.entities.HoopPlayer.create({
        team_id: selectedTeamId,
        full_name: portfolio.full_name,
        position: portfolio.position || '',
        height: height,
        class_year: portfolio.class_year || '',
        photo_url: portfolio.profile_photo_url || '',
        portfolio_url_slug: portfolio.portfolio_url_slug || '',
        high_school: portfolio.high_school || '',
      });
      refetchPlayers();
    } catch (e) {
      alert('Error adding player: ' + e.message);
    } finally {
      setAddingPortfolioId(null);
    }
  };

  // Fetch all HoopTeams (team sites)
  const { data: teams = [], isLoading: teamsLoading } = useQuery({
    queryKey: ['hoop-teams'],
    queryFn: async () => {
      const result = await base44.entities.HoopTeam.list();
      return result;
    }
  });

  // Fetch players for selected team
  const { data: players = [], isLoading: playersLoading, refetch: refetchPlayers } = useQuery({
    queryKey: ['hoop-players', selectedTeamId],
    queryFn: async () => {
      if (!selectedTeamId) return [];
      const result = await base44.entities.HoopPlayer.filter({ team_id: selectedTeamId });
      return result;
    },
    enabled: !!selectedTeamId
  });

  const filteredPlayers = players.filter(p =>
    p.full_name.toLowerCase().includes(search.toLowerCase())
  );

  const handleEditPlayer = (player) => {
    setEditingPlayer(player.id);
    setEditForm({
      full_name: player.full_name,
      jersey_number: player.jersey_number || '',
      position: player.position || '',
      height: player.height || '',
      class_year: player.class_year || '',
      photo_url: player.photo_url || ''
    });
  };

  const handleSavePlayer = async () => {
    try {
      await base44.entities.HoopPlayer.update(editingPlayer, editForm);
      refetchPlayers();
      setEditingPlayer(null);
      setEditForm({});
    } catch (e) {
      alert('Error saving player: ' + e.message);
    }
  };

  const handleDeletePlayer = async (playerId) => {
    if (!confirm('Delete this player?')) return;
    try {
      await base44.entities.HoopPlayer.delete(playerId);
      refetchPlayers();
    } catch (e) {
      alert('Error deleting player: ' + e.message);
    }
  };

  const handleAddPlayer = async (e) => {
    e.preventDefault();
    if (!selectedTeamId) {
      alert('Please select a team first');
      return;
    }
    try {
      await base44.entities.HoopPlayer.create({
        team_id: selectedTeamId,
        full_name: editForm.full_name,
        jersey_number: editForm.jersey_number || null,
        position: editForm.position || '',
        height: editForm.height || '',
        class_year: editForm.class_year || '',
        photo_url: editForm.photo_url || ''
      });
      refetchPlayers();
      setShowAddForm(false);
      setEditForm({});
    } catch (e) {
      alert('Error adding player: ' + e.message);
    }
  };

  if (teamsLoading) {
    return (
      <div className="flex items-center justify-center py-20">
        <Loader2 className="w-6 h-6 animate-spin text-orange-500" />
      </div>
    );
  }

  return (
    <div className="flex flex-col h-full">
      <div className="mb-6">
        <h2 className="text-2xl font-black text-white mb-4">Team Site Portfolios</h2>
        
        <div className="grid lg:grid-cols-2 gap-6">
          {/* Teams List */}
          <div>
            <h3 className="text-lg font-semibold text-white mb-3">Select Team</h3>
            <div className="relative mb-3">
              <Search className="absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 text-slate-600" />
              <input
                type="text"
                placeholder="Search teams..."
                value={search}
                onChange={(e) => setSearch(e.target.value)}
                className="w-full pl-9 pr-4 py-2 rounded-lg text-sm text-white bg-white/5 border border-white/10 focus:outline-none focus:border-orange-500/50"
              />
            </div>
            
            <div className="space-y-2 max-h-96 overflow-y-auto">
              {teams.map(team => (
                <button
                  key={team.id}
                  onClick={() => setSelectedTeamId(team.id)}
                  className={`w-full text-left px-4 py-3 rounded-lg font-semibold transition-all ${
                    selectedTeamId === team.id
                      ? 'bg-orange-500 text-white'
                      : 'bg-white/5 text-slate-300 hover:bg-white/10'
                  }`}
                >
                  <div>{team.name}</div>
                  <div className="text-xs opacity-70">{team.season || 'No season'}</div>
                </button>
              ))}
            </div>
          </div>

          {/* Players List */}
          <div>
            <div className="flex items-center justify-between mb-3">
              <h3 className="text-lg font-semibold text-white">
                {selectedTeamId ? `Players (${filteredPlayers.length})` : 'Select a team'}
              </h3>
              {selectedTeamId && (
                <button
                  onClick={() => { setShowPortfolioModal(true); setPortfolioSearch(''); }}
                  className="flex items-center gap-2 px-3 py-2 bg-orange-500 hover:bg-orange-600 text-white rounded-lg text-sm font-semibold transition-all"
                >
                  <Plus className="w-4 h-4" />
                  Add Player
                </button>
              )}
            </div>

            {showAddForm && selectedTeamId && (
              <form onSubmit={handleAddPlayer} className="bg-white/5 border border-white/10 rounded-lg p-4 mb-3">
                <input
                  type="text"
                  placeholder="Full Name *"
                  required
                  value={editForm.full_name || ''}
                  onChange={(e) => setEditForm({ ...editForm, full_name: e.target.value })}
                  className="w-full mb-2 px-3 py-2 rounded-lg text-sm bg-white/10 border border-white/10 text-white placeholder-slate-500 focus:outline-none"
                />
                <input
                  type="number"
                  placeholder="Jersey #"
                  value={editForm.jersey_number || ''}
                  onChange={(e) => setEditForm({ ...editForm, jersey_number: e.target.value })}
                  className="w-full mb-2 px-3 py-2 rounded-lg text-sm bg-white/10 border border-white/10 text-white placeholder-slate-500 focus:outline-none"
                />
                <select
                  value={editForm.position || ''}
                  onChange={(e) => setEditForm({ ...editForm, position: e.target.value })}
                  className="w-full mb-2 px-3 py-2 rounded-lg text-sm bg-white/10 border border-white/10 text-white focus:outline-none"
                >
                  <option value="">Position</option>
                  {['PG', 'SG', 'SF', 'PF', 'C', 'G', 'F'].map(pos => (
                    <option key={pos} value={pos}>{pos}</option>
                  ))}
                </select>
                <input
                  type="text"
                  placeholder="Height"
                  value={editForm.height || ''}
                  onChange={(e) => setEditForm({ ...editForm, height: e.target.value })}
                  className="w-full mb-2 px-3 py-2 rounded-lg text-sm bg-white/10 border border-white/10 text-white placeholder-slate-500 focus:outline-none"
                />
                <input
                  type="text"
                  placeholder="Photo URL"
                  value={editForm.photo_url || ''}
                  onChange={(e) => setEditForm({ ...editForm, photo_url: e.target.value })}
                  className="w-full mb-3 px-3 py-2 rounded-lg text-sm bg-white/10 border border-white/10 text-white placeholder-slate-500 focus:outline-none"
                />
                <div className="flex gap-2">
                  <button
                    type="submit"
                    className="flex-1 px-3 py-2 bg-green-600 hover:bg-green-700 text-white rounded-lg text-sm font-semibold transition-all"
                  >
                    Add
                  </button>
                  <button
                    type="button"
                    onClick={() => {
                      setShowAddForm(false);
                      setEditForm({});
                    }}
                    className="flex-1 px-3 py-2 bg-slate-700 hover:bg-slate-600 text-white rounded-lg text-sm font-semibold transition-all"
                  >
                    Cancel
                  </button>
                </div>
              </form>
            )}

            <div className="space-y-2 max-h-96 overflow-y-auto">
              {filteredPlayers.length === 0 ? (
                <div className="text-center py-6 text-slate-500">
                  {selectedTeamId ? 'No players yet' : 'Select a team to view players'}
                </div>
              ) : (
                filteredPlayers.map(player => (
                  <div key={player.id} className="bg-white/5 border border-white/10 rounded-lg p-3">
                    {editingPlayer === player.id ? (
                      <form onSubmit={(e) => { e.preventDefault(); handleSavePlayer(); }} className="space-y-2">
                        <input
                          type="text"
                          value={editForm.full_name || ''}
                          onChange={(e) => setEditForm({ ...editForm, full_name: e.target.value })}
                          className="w-full px-2 py-1 rounded text-sm bg-white/10 border border-white/10 text-white focus:outline-none"
                        />
                        <input
                          type="number"
                          value={editForm.jersey_number || ''}
                          onChange={(e) => setEditForm({ ...editForm, jersey_number: e.target.value })}
                          placeholder="Jersey #"
                          className="w-full px-2 py-1 rounded text-sm bg-white/10 border border-white/10 text-white placeholder-slate-500 focus:outline-none"
                        />
                        <select
                          value={editForm.position || ''}
                          onChange={(e) => setEditForm({ ...editForm, position: e.target.value })}
                          className="w-full px-2 py-1 rounded text-sm bg-white/10 border border-white/10 text-white focus:outline-none"
                        >
                          <option value="">Position</option>
                          {['PG', 'SG', 'SF', 'PF', 'C', 'G', 'F'].map(pos => (
                            <option key={pos} value={pos}>{pos}</option>
                          ))}
                        </select>
                        <div className="flex gap-2">
                          <button
                            type="submit"
                            className="flex-1 px-2 py-1 bg-green-600 hover:bg-green-700 text-white rounded text-xs font-semibold transition-all"
                          >
                            Save
                          </button>
                          <button
                            type="button"
                            onClick={() => setEditingPlayer(null)}
                            className="flex-1 px-2 py-1 bg-slate-700 hover:bg-slate-600 text-white rounded text-xs font-semibold transition-all"
                          >
                            Cancel
                          </button>
                        </div>
                      </form>
                    ) : (
                      <div>
                        <div className="flex items-start justify-between">
                          <div>
                            <div className="font-semibold text-white">
                              {player.jersey_number ? `#${player.jersey_number}` : ''} {player.full_name}
                            </div>
                            <div className="text-xs text-slate-400">
                              {player.position && <span>{player.position}</span>}
                              {player.height && <span> • {player.height}</span>}
                              {player.high_school && <span> • {player.high_school}</span>}
                            </div>
                          </div>
                          <div className="flex gap-1">
                            <button
                              onClick={() => handleEditPlayer(player)}
                              className="p-1.5 hover:bg-blue-500/20 text-blue-400 rounded transition-all"
                              title="Edit"
                            >
                              <Edit2 className="w-4 h-4" />
                            </button>
                            <button
                              onClick={() => handleDeletePlayer(player.id)}
                              className="p-1.5 hover:bg-red-500/20 text-red-400 rounded transition-all"
                              title="Delete"
                            >
                              <Trash2 className="w-4 h-4" />
                            </button>
                          </div>
                        </div>
                      </div>
                    )}
                  </div>
                ))
              )}
            </div>
          </div>
        </div>
      </div>
      {/* Portfolio Search Modal */}
      {showPortfolioModal && (
        <div className="fixed inset-0 bg-black/70 flex items-center justify-center z-50 p-4">
          <div className="bg-slate-900 rounded-2xl border border-slate-700 w-full max-w-lg p-6 flex flex-col max-h-[80vh]">
            <div className="flex items-center justify-between mb-4">
              <h2 className="text-lg font-bold text-white">Search Player Portfolios</h2>
              <div className="flex items-center gap-2">
                {filteredPortfolios.length > 0 && (
                  <button
                    onClick={handleAddAll}
                    disabled={addingAll}
                    className="flex items-center gap-1 px-3 py-1.5 bg-green-600 hover:bg-green-700 text-white rounded-lg text-xs font-semibold transition-all disabled:opacity-50"
                  >
                    {addingAll ? <Loader2 className="w-3 h-3 animate-spin" /> : <Plus className="w-3 h-3" />}
                    Add All ({filteredPortfolios.length})
                  </button>
                )}
                <button onClick={() => setShowPortfolioModal(false)} className="text-slate-400 hover:text-white">
                  <X className="w-5 h-5" />
                </button>
              </div>
            </div>
            <div className="relative mb-4">
              <Search className="absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 text-slate-500" />
              <input
                type="text"
                autoFocus
                placeholder="Search by name or team..."
                value={portfolioSearch}
                onChange={e => setPortfolioSearch(e.target.value)}
                className="w-full pl-9 pr-4 py-2 rounded-lg text-sm text-white bg-white/5 border border-white/10 focus:outline-none focus:border-orange-500/50"
              />
            </div>
            <div className="overflow-y-auto flex-1 space-y-2">
              {filteredPortfolios.length === 0 ? (
                <p className="text-slate-500 text-sm text-center py-6">No portfolios found</p>
              ) : (
                filteredPortfolios.map(p => (
                  <div key={p.id} className="flex items-center justify-between bg-white/5 border border-white/10 rounded-lg px-4 py-3">
                    <div className="flex items-center gap-3">
                      {p.profile_photo_url && (
                        <img src={p.profile_photo_url} alt={p.full_name} className="w-8 h-8 rounded-full object-cover" />
                      )}
                      <div>
                        <div className="text-white font-semibold text-sm">{p.full_name}</div>
                        <div className="text-slate-400 text-xs">
                          {p.current_team_name && <span>{p.current_team_name}</span>}
                          {p.high_school && !p.current_team_name && <span>{p.high_school}</span>}
                          {p.position && <span> · {p.position}</span>}
                          {p.class_year && <span> · {p.class_year}</span>}
                        </div>
                      </div>
                    </div>
                    <button
                      onClick={() => handleAddFromPortfolio(p)}
                      disabled={addingPortfolioId === p.id}
                      className="flex items-center gap-1 px-3 py-1.5 bg-orange-500 hover:bg-orange-600 text-white rounded-lg text-xs font-semibold transition-all disabled:opacity-50"
                    >
                      {addingPortfolioId === p.id ? <Loader2 className="w-3 h-3 animate-spin" /> : <Plus className="w-3 h-3" />}
                      Add
                    </button>
                  </div>
                ))
              )}
            </div>
          </div>
        </div>
      )}
    </div>
  );
}

src/components/admin/AdminTeams.jsx

import React, { useState } from 'react';
import { base44 } from '@/api/base44Client';
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
import { Plus, Users, Trash2, Edit2, X, Upload } from 'lucide-react';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';

function TeamForm({ team, onSave, onClose }) {
  const [form, setForm] = useState(team || {
    name: '', season: new Date().getFullYear() + '-' + (new Date().getFullYear() + 1),
    level: 'high_school', organization: '', logo_url: ''
  });

  return (
    <div className="fixed inset-0 bg-black/70 flex items-center justify-center z-50 p-4">
      <div className="bg-slate-900 rounded-2xl border border-slate-700 w-full max-w-md p-6">
        <div className="flex items-center justify-between mb-5">
          <h2 className="text-lg font-bold text-white">{team ? 'Edit Team' : 'New Team'}</h2>
          <button onClick={onClose} className="text-slate-400 hover:text-white"><X className="w-5 h-5" /></button>
        </div>
        <div className="space-y-4">
          <div>
            <label className="text-xs text-slate-400 mb-1 block">Team Name *</label>
            <Input value={form.name} onChange={e => setForm({ ...form, name: e.target.value })}
              className="bg-slate-800 border-slate-700 text-white" placeholder="e.g. Colorado Mambas" />
          </div>
          <div>
            <label className="text-xs text-slate-400 mb-1 block">Season *</label>
            <Input value={form.season} onChange={e => setForm({ ...form, season: e.target.value })}
              className="bg-slate-800 border-slate-700 text-white" placeholder="e.g. 2024-25" />
          </div>
          <div>
            <label className="text-xs text-slate-400 mb-1 block">Level</label>
            <select value={form.level} onChange={e => setForm({ ...form, level: e.target.value })}
              className="w-full bg-slate-800 border border-slate-700 text-white rounded-md px-3 py-2 text-sm">
              <option value="high_school">High School</option>
              <option value="aau">AAU</option>
              <option value="club">Club</option>
              <option value="small_college">Small College</option>
              <option value="other">Other</option>
            </select>
          </div>
          <div>
            <label className="text-xs text-slate-400 mb-1 block">Organization / School</label>
            <Input value={form.organization} onChange={e => setForm({ ...form, organization: e.target.value })}
              className="bg-slate-800 border-slate-700 text-white" placeholder="e.g. Jefferson High School" />
          </div>
          <div>
            <label className="text-xs text-slate-400 mb-1 block">Logo URL (optional)</label>
            <Input value={form.logo_url} onChange={e => setForm({ ...form, logo_url: e.target.value })}
              className="bg-slate-800 border-slate-700 text-white" placeholder="https://..." />
          </div>
        </div>
        <div className="flex gap-3 mt-6">
          <Button variant="ghost" onClick={onClose} className="flex-1 text-slate-400">Cancel</Button>
          <Button onClick={() => onSave(form)} className="flex-1 bg-orange-500 hover:bg-orange-600"
            disabled={!form.name || !form.season}>
            {team ? 'Save Changes' : 'Create Team'}
          </Button>
        </div>
      </div>
    </div>
  );
}

export default function AdminTeams() {
  const queryClient = useQueryClient();
  const [showForm, setShowForm] = useState(false);
  const [editingTeam, setEditingTeam] = useState(null);

  const { data: teams = [] } = useQuery({
    queryKey: ['hoop-teams'],
    queryFn: () => base44.entities.HoopTeam.list(),
  });

  const { data: players = [] } = useQuery({
    queryKey: ['all-players'],
    queryFn: () => base44.entities.Player.list(),
  });

  const saveMutation = useMutation({
    mutationFn: async (data) => {
      if (editingTeam) return base44.entities.HoopTeam.update(editingTeam.id, data);
      return base44.entities.HoopTeam.create(data);
    },
    onSuccess: () => { queryClient.invalidateQueries(['hoop-teams']); setShowForm(false); setEditingTeam(null); }
  });

  const deleteMutation = useMutation({
    mutationFn: (id) => base44.entities.HoopTeam.delete(id),
    onSuccess: () => queryClient.invalidateQueries(['hoop-teams']),
  });

  const handleSave = (data) => saveMutation.mutate(data);

  return (
    <div>
      <div className="flex items-center justify-between mb-6">
        <h1 className="text-2xl font-black text-white">Teams</h1>
        <Button onClick={() => { setEditingTeam(null); setShowForm(true); }}
          className="bg-orange-500 hover:bg-orange-600 gap-2">
          <Plus className="w-4 h-4" /> New Team
        </Button>
      </div>

      <div className="grid sm:grid-cols-2 lg:grid-cols-3 gap-4">
        {teams.map(team => {
          const teamPlayers = players.filter(p => p.team_id === team.id);
          return (
            <div key={team.id} className="bg-slate-900 rounded-xl border border-slate-800 p-5 hover:border-slate-600 transition-all">
              <div className="flex items-start justify-between mb-3">
                <div className="flex items-center gap-3">
                  {team.logo_url ? (
                    <img src={team.logo_url} alt={team.name} className="w-10 h-10 rounded-lg object-cover" />
                  ) : (
                    <div className="w-10 h-10 rounded-lg bg-gradient-to-br from-orange-500 to-pink-500 flex items-center justify-center">
                      <Users className="w-5 h-5 text-white" />
                    </div>
                  )}
                  <div>
                    <p className="text-white font-bold">{team.name}</p>
                    <p className="text-slate-400 text-xs">{team.season} · {team.level?.replace('_', ' ')}</p>
                  </div>
                </div>
                <div className="flex gap-1">
                  <button onClick={() => { setEditingTeam(team); setShowForm(true); }}
                    className="p-1.5 rounded-lg text-slate-400 hover:text-white hover:bg-slate-800 transition-all">
                    <Edit2 className="w-3.5 h-3.5" />
                  </button>
                  <button onClick={() => deleteMutation.mutate(team.id)}
                    className="p-1.5 rounded-lg text-slate-400 hover:text-red-400 hover:bg-red-500/10 transition-all">
                    <Trash2 className="w-3.5 h-3.5" />
                  </button>
                </div>
              </div>
              {team.organization && <p className="text-slate-500 text-xs mb-2">{team.organization}</p>}
              <div className="flex items-center gap-2 text-xs text-slate-400">
                <Users className="w-3.5 h-3.5" />
                <span>{teamPlayers.length} players</span>
              </div>
            </div>
          );
        })}

        {teams.length === 0 && (
          <div className="col-span-3 bg-slate-900/50 rounded-xl border border-dashed border-slate-700 p-12 text-center">
            <Users className="w-12 h-12 text-slate-600 mx-auto mb-3" />
            <p className="text-slate-400 mb-4">No teams yet</p>
            <Button onClick={() => setShowForm(true)} className="bg-orange-500 hover:bg-orange-600">
              Create First Team
            </Button>
          </div>
        )}
      </div>

      {showForm && (
        <TeamForm
          team={editingTeam}
          onSave={handleSave}
          onClose={() => { setShowForm(false); setEditingTeam(null); }}
        />
      )}
    </div>
  );
}

src/components/admin/AdminTopPlayersExport.jsx

import { useState } from 'react';
import { base44 } from '@/api/base44Client';
import { Download, Trophy, Loader2, Table } from 'lucide-react';

function csvCell(v) {
  const s = String(v ?? '');
  return /[",\n]/.test(s) ? `"${s.replace(/"/g, '""')}"` : s;
}

export default function AdminTopPlayersExport() {
  const [loading, setLoading] = useState(false);
  const [players, setPlayers] = useState([]);
  const [error, setError] = useState('');

  const fetchTop = async () => {
    setLoading(true);
    setError('');
    try {
      const all = await base44.entities.Player.filter({ career_ppg: { $gt: 0 } }, '-career_ppg', 300);
      setPlayers(all);
    } catch (e) {
      setError(e?.message || 'Failed to load players');
    } finally {
      setLoading(false);
    }
  };

  const rows = players.map((p, i) => [
    i + 1,
    p.full_name || '',
    p.career_ppg ?? '',
    p.phone || '',
    p.portfolio_url_slug ? `https://goaio.live/player/${p.portfolio_url_slug}` : '',
  ]);
  const csv = ['Rank,Full Name,PPG,Phone,Portfolio URL', ...rows.map(r => r.map(csvCell).join(','))].join('\n');

  const download = () => {
    const blob = new Blob([csv], { type: 'text/csv;charset=utf-8' });
    const a = document.createElement('a');
    a.href = URL.createObjectURL(blob);
    a.download = 'top-300-players-by-ppg.csv';
    a.click();
  };

  return (
    <div className="space-y-4">
      <div className="flex flex-col sm:flex-row sm:items-center justify-between gap-3">
        <div>
          <h2 className="text-lg font-bold text-white flex items-center gap-2">
            <Trophy className="w-5 h-5 text-yellow-400" /> Top 300 Players by PPG
          </h2>
          <p className="text-slate-400 text-sm">
            {players.length > 0
              ? `${players.length} players loaded · top ${players[0]?.career_ppg} → ${players[players.length - 1]?.career_ppg} PPG`
              : 'Loads the top 300 by career points average with phone + portfolio URL.'}
          </p>
        </div>
        <div className="flex gap-2">
          <button onClick={fetchTop} disabled={loading}
            className="px-4 py-2 rounded-lg bg-orange-500 hover:bg-orange-600 text-white text-sm font-bold disabled:opacity-50 transition-all flex items-center gap-1.5">
            {loading ? <><Loader2 className="w-4 h-4 animate-spin" /> Loading...</> : <Table className="w-4 h-4" />} Load Top 300
          </button>
          {players.length > 0 && (
            <button onClick={download}
              className="flex items-center gap-1.5 px-4 py-2 rounded-lg bg-slate-700 hover:bg-slate-600 text-white text-sm font-bold transition-all">
              <Download className="w-4 h-4" /> Download CSV
            </button>
          )}
        </div>
      </div>

      {error && <p className="text-red-400 text-sm">{error}</p>}

      {players.length > 0 && (
        <>
          <p className="text-xs text-slate-500">
            Tip: open <span className="text-slate-300">sheets.new</span> → File → Import → Upload → choose the CSV to create a Google Sheet.
          </p>
          <div className="overflow-auto rounded-lg border border-slate-800 max-h-[60vh]">
            <table className="w-full text-xs">
              <thead className="bg-slate-900 sticky top-0">
                <tr className="text-slate-400 text-left">
                  <th className="px-3 py-2 w-12">#</th>
                  <th className="px-3 py-2">Player</th>
                  <th className="px-3 py-2">PPG</th>
                  <th className="px-3 py-2">Phone</th>
                  <th className="px-3 py-2">Portfolio URL</th>
                </tr>
              </thead>
              <tbody>
                {players.map((p, i) => (
                  <tr key={p.id || i} className="border-t border-slate-800 hover:bg-slate-800/50">
                    <td className="px-3 py-2 text-slate-500">{i + 1}</td>
                    <td className="px-3 py-2 text-white font-medium">{p.full_name}</td>
                    <td className="px-3 py-2 text-orange-400 font-bold">{p.career_ppg}</td>
                    <td className="px-3 py-2 text-slate-300">{p.phone || '—'}</td>
                    <td className="px-3 py-2 text-slate-400 truncate max-w-xs">
                      {p.portfolio_url_slug ? `goaio.live/player/${p.portfolio_url_slug}` : '—'}
                    </td>
                  </tr>
                ))}
              </tbody>
            </table>
          </div>
        </>
      )}
    </div>
  );
}

src/components/admin/AdminTournamentInquiries.jsx

import { useQuery } from '@tanstack/react-query';
import { base44 } from '@/api/base44Client';
import { Building2, CalendarDays, Mail, MapPin, Phone, Video } from 'lucide-react';

export default function AdminTournamentInquiries() {
  const { data: inquiries = [], isLoading } = useQuery({
    queryKey: ['tournament-inquiries'],
    queryFn: () => base44.entities.TournamentInquiry.list('-created_date', 100),
  });

  if (isLoading) return <p className="text-sm text-slate-400">Loading tournament inquiries...</p>;

  return (
    <div>
      <div className="mb-5">
        <h2 className="text-xl font-black text-white">Tournament Inquiries</h2>
        <p className="text-sm text-slate-400">{inquiries.length} tournament partnership submission{inquiries.length === 1 ? '' : 's'}</p>
      </div>
      {inquiries.length === 0 ? (
        <div className="rounded-xl border border-dashed border-slate-700 py-14 text-center text-sm text-slate-400">No tournament inquiries yet.</div>
      ) : (
        <div className="space-y-3">
          {inquiries.map((inquiry) => (
            <div key={inquiry.id} className="rounded-xl border border-slate-800 bg-slate-900 p-5">
              <div className="flex flex-wrap items-start justify-between gap-3">
                <div>
                  <p className="font-bold text-white">{inquiry.organization}</p>
                  <p className="mt-0.5 text-sm text-slate-400">{inquiry.contact_name}</p>
                </div>
                <span className="rounded-full bg-orange-500/10 px-2.5 py-1 text-xs font-semibold text-orange-400">
                  {inquiry.video_coverage === 'need_game_recording' ? 'Needs game recording' : 'Own video vendor'}
                </span>
              </div>
              <div className="mt-4 grid gap-2 text-sm text-slate-400 sm:grid-cols-2 lg:grid-cols-4">
                <span className="flex items-center gap-2"><Mail className="w-3.5 h-3.5" />{inquiry.email}</span>
                <span className="flex items-center gap-2"><Phone className="w-3.5 h-3.5" />{inquiry.phone}</span>
                <span className="flex items-center gap-2"><CalendarDays className="w-3.5 h-3.5" />{inquiry.tournament_date}</span>
                <span className="flex items-center gap-2"><MapPin className="w-3.5 h-3.5" />{inquiry.city}, {inquiry.state}</span>
              </div>
            </div>
          ))}
        </div>
      )}
    </div>
  );
}

src/components/admin/AdminUpdateRequests.jsx

import { useState, useEffect } from 'react';
import { base44 } from '@/api/base44Client';
import { Inbox, Loader2 } from 'lucide-react';
import UpdateRequestCard from './UpdateRequestCard';

export default function AdminUpdateRequests() {
  const [requests, setRequests] = useState([]);
  const [loading, setLoading] = useState(true);
  const [filter, setFilter] = useState('all');

  const load = async () => {
    setLoading(true);
    try {
      const reqs = await base44.entities.UpdateRequest.list('-created_date', 200);
      setRequests(reqs);
    } catch (e) {
      console.error('Failed to load update requests:', e);
    } finally {
      setLoading(false);
    }
  };

  useEffect(() => { load(); }, []);

  const filtered = filter === 'all' ? requests : requests.filter(r => r.status === filter);
  const counts = { submitted: 0, in_review: 0, applied: 0, rejected: 0 };
  requests.forEach(r => { if (counts[r.status] !== undefined) counts[r.status]++; });

  return (
    <div>
      <div className="flex items-center justify-between mb-4 flex-wrap gap-3">
        <h2 className="text-xl font-black text-white flex items-center gap-2">
          <Inbox className="w-5 h-5 text-orange-400" /> Update Requests
        </h2>
        <div className="flex gap-1 flex-wrap">
          {['all', 'submitted', 'in_review', 'applied', 'rejected'].map(f => (
            <button key={f} onClick={() => setFilter(f)}
              className={`px-3 py-1.5 rounded-lg text-xs font-semibold capitalize ${
                filter === f ? 'bg-orange-500 text-black' : 'bg-slate-800 text-slate-400 hover:text-white'
              }`}>
              {f === 'all' ? 'All' : f.replace('_', ' ')}{f !== 'all' && counts[f] ? ` (${counts[f]})` : ''}
            </button>
          ))}
        </div>
      </div>
      {loading ? (
        <div className="flex justify-center py-12"><Loader2 className="w-6 h-6 text-orange-400 animate-spin" /></div>
      ) : filtered.length === 0 ? (
        <p className="text-slate-400 text-sm py-8 text-center">No update requests found.</p>
      ) : (
        <div className="space-y-3">{filtered.map(r => <UpdateRequestCard key={r.id} request={r} onChanged={load} />)}</div>
      )}
    </div>
  );
}

src/components/admin/AuditHistoryModal.jsx

import { useState, useEffect } from 'react';
import { X, Clock, User, ChevronDown, ChevronRight } from 'lucide-react';
import { base44 } from '@/api/base44Client';

function ChangeRow({ fieldName, change }) {
  const sensitiveFields = ['date_of_birth', 'home_address', 'address_city', 'address_state', 'address_zip', 'email', 'phone', 'act_score', 'sat_score'];
  const isSensitive = sensitiveFields.includes(fieldName);
  const label = fieldName.replace(/_/g, ' ').replace(/\b\w/g, c => c.toUpperCase());

  const format = (val) => {
    if (val === null || val === undefined) return <span className="text-gray-600 italic">empty</span>;
    if (typeof val === 'boolean') return val ? 'true' : 'false';
    return String(val);
  };

  return (
    <div className="flex items-start gap-3 py-2 border-b border-white/5 last:border-0">
      <div className="flex-1 min-w-0">
        <span className="text-xs font-semibold text-gray-300">{label}</span>
        {isSensitive && <span className="ml-1.5 text-[9px] px-1.5 py-0.5 rounded bg-yellow-900/30 border border-yellow-600/20 text-yellow-600">PII</span>}
      </div>
      <div className="flex items-center gap-2 text-xs shrink-0">
        <span className="text-red-400 line-through max-w-[100px] truncate">{format(change.old)}</span>
        <ChevronRight className="w-3 h-3 text-gray-600" />
        <span className="text-green-400 max-w-[100px] truncate">{format(change.new)}</span>
      </div>
    </div>
  );
}

function AuditEntry({ entry }) {
  const [expanded, setExpanded] = useState(false);
  const changeCount = Object.keys(entry.changes || {}).length;
  const date = new Date(entry.created_date);
  const dateStr = date.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' });
  const timeStr = date.toLocaleTimeString('en-US', { hour: 'numeric', minute: '2-digit' });

  return (
    <div className="bg-[#1a1a1a] rounded-xl border border-white/[0.06] overflow-hidden">
      <button
        onClick={() => setExpanded(e => !e)}
        className="w-full flex items-center gap-3 px-4 py-3 text-left hover:bg-white/[0.02] transition-colors"
      >
        <div className="w-7 h-7 rounded-full bg-orange-900/30 border border-orange-600/25 flex items-center justify-center shrink-0">
          <User className="w-3.5 h-3.5 text-orange-400" />
        </div>
        <div className="flex-1 min-w-0">
          <p className="text-sm text-white font-medium capitalize">{entry.action || 'update'}</p>
          <p className="text-xs text-gray-500 truncate">by user <span className="text-gray-400 font-mono">{entry.changed_by_user_id}</span></p>
        </div>
        <div className="flex items-center gap-2 shrink-0">
          <div className="text-right">
            <p className="text-xs text-gray-400">{dateStr}</p>
            <p className="text-[10px] text-gray-600">{timeStr}</p>
          </div>
          {changeCount > 0 && (
            <span className="text-[10px] font-bold px-2 py-0.5 rounded-full bg-blue-900/30 border border-blue-600/20 text-blue-400">
              {changeCount} field{changeCount !== 1 ? 's' : ''}
            </span>
          )}
          {expanded ? <ChevronDown className="w-3.5 h-3.5 text-gray-500" /> : <ChevronRight className="w-3.5 h-3.5 text-gray-500" />}
        </div>
      </button>

      {expanded && changeCount > 0 && (
        <div className="px-4 pb-3 border-t border-white/[0.06]">
          <div className="mt-3">
            {Object.entries(entry.changes).map(([field, change]) => (
              <ChangeRow key={field} fieldName={field} change={change} />
            ))}
          </div>
        </div>
      )}
    </div>
  );
}

export default function AuditHistoryModal({ player, onClose }) {
  const [logs, setLogs] = useState([]);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    base44.entities.AuditLog.filter({ target_id: player.id }, '-created_date', 50)
      .then(setLogs)
      .finally(() => setLoading(false));
  }, [player.id]);

  return (
    <div className="fixed inset-0 z-[60] flex items-center justify-center p-4">
      <div className="absolute inset-0 bg-black/80 backdrop-blur-sm" onClick={onClose} />
      <div className="relative bg-[#111] border border-white/10 rounded-2xl w-full max-w-2xl max-h-[80vh] flex flex-col shadow-2xl">
        <div className="flex items-center justify-between px-6 py-4 border-b border-white/10">
          <div>
            <h2 className="text-white font-bold text-lg">Audit History</h2>
            <p className="text-gray-500 text-xs mt-0.5">{player.full_name} — all recorded changes</p>
          </div>
          <button onClick={onClose} className="text-gray-500 hover:text-white p-1">
            <X className="w-5 h-5" />
          </button>
        </div>

        <div className="flex-1 overflow-y-auto px-6 py-4 space-y-2">
          {loading ? (
            <div className="flex justify-center py-12">
              <div className="w-5 h-5 border-2 border-orange-500 border-t-transparent rounded-full animate-spin" />
            </div>
          ) : logs.length === 0 ? (
            <div className="text-center py-12">
              <Clock className="w-8 h-8 text-gray-700 mx-auto mb-3" />
              <p className="text-gray-500 text-sm">No changes recorded yet.</p>
              <p className="text-gray-600 text-xs mt-1">Future edits made through the Admin Portal will appear here.</p>
            </div>
          ) : (
            logs.map(entry => <AuditEntry key={entry.id} entry={entry} />)
          )}
        </div>
      </div>
    </div>
  );
}

src/components/admin/CleanSlateReset.jsx

import React, { useState } from 'react';
import { base44 } from '@/api/base44Client';
import { Shield, Trash2, AlertTriangle, Loader2, CheckCircle2, XCircle, Database, Lock, Crown, FileText } from 'lucide-react';

const ORANGE = '#FF6A00';

export default function CleanSlateReset({ onCompleted }) {
  const [loading, setLoading] = useState(false);
  const [result, setResult] = useState(null);
  const [error, setError] = useState(null);
  const [confirmText, setConfirmText] = useState('');
  const [activeView, setActiveView] = useState('deletable');
  const [hasRunDryRun, setHasRunDryRun] = useState(false);

  const runReset = async (isDryRun) => {
    setLoading(true);
    setError(null);
    setResult(null);
    try {
      const res = await base44.functions.invoke('cleanImportAndReset', { dry_run: isDryRun });
      setResult(res.data);
      if (isDryRun) {
        setHasRunDryRun(true);
        setActiveView('deletable');
      } else {
        setConfirmText('');
        setHasRunDryRun(false);
        if (onCompleted) onCompleted();
      }
    } catch (err) {
      setError(err?.response?.data?.error || err?.message || 'Reset failed');
    }
    setLoading(false);
  };

  const canExecute = hasRunDryRun && result && confirmText === 'DELETE' && result.deletable_count > 0 && !loading;

  return (
    <div className="bg-[#1a1a1a] rounded-xl border border-white/10 p-6 space-y-5">
      {/* Header */}
      <div className="flex items-start gap-3">
        <div className="w-10 h-10 rounded-lg flex items-center justify-center shrink-0" style={{ background: 'rgba(255,106,0,0.12)', border: '1px solid rgba(255,106,0,0.25)' }}>
          <Trash2 className="w-5 h-5" style={{ color: ORANGE }} />
        </div>
        <div>
          <h3 className="text-white font-bold text-lg leading-tight">Clean Slate Reset</h3>
          <p className="text-gray-500 text-sm mt-0.5">
            Deletes all stale player records while preserving published and premium portfolios.
            Team sites (<span className="text-gray-400">HoopTeam</span>, <span className="text-gray-400">GeorgiaTeamSite</span>) are separate entities and are never touched.
          </p>
        </div>
      </div>

      {/* Protection criteria */}
      <div className="flex items-start gap-2 p-3 rounded-lg" style={{ background: 'rgba(0,255,133,0.06)', border: '1px solid rgba(0,255,133,0.15)' }}>
        <Shield className="w-4 h-4 text-green-400 shrink-0 mt-0.5" />
        <div className="text-xs text-gray-400 leading-relaxed">
          <span className="text-green-400 font-bold">Protected (never deleted):</span>
          <ul className="mt-1 space-y-0.5 ml-3">
            <li>• <span className="text-gray-300">is_published = true</span> — live public portfolios</li>
            <li>• <span className="text-gray-300">portfolio_tier = premium</span> — paid/claimed portfolios</li>
            <li>• <span className="text-gray-300">portfolio_tier = under_review</span> — pending activation</li>
          </ul>
          <p className="mt-1.5">URL slugs for protected players remain unchanged — shared links stay valid.</p>
        </div>
      </div>

      {/* Action buttons */}
      <div className="flex gap-3">
        <button
          onClick={() => runReset(true)}
          disabled={loading}
          className="flex-1 flex items-center justify-center gap-2 py-3 rounded-lg text-sm font-bold transition-all disabled:opacity-50"
          style={{ background: 'rgba(255,106,0,0.12)', border: `1px solid ${ORANGE}40`, color: ORANGE }}
        >
          {loading ? <Loader2 className="w-4 h-4 animate-spin" /> : <Database className="w-4 h-4" />}
          Run Dry Run (Preview)
        </button>
      </div>

      {error && (
        <div className="flex items-start gap-2 p-3 rounded-lg bg-red-500/10 border border-red-500/20 text-red-400 text-sm">
          <AlertTriangle className="w-4 h-4 shrink-0 mt-0.5" />
          <span>{error}</span>
        </div>
      )}

      {/* Results */}
      {result && (
        <div className="space-y-4">
          {/* Summary banner */}
          <div className="bg-[#111] rounded-lg border border-white/10 p-4">
            <div className="flex items-center gap-2 mb-3">
              {result.dry_run ? (
                <span className="px-2 py-0.5 rounded text-[10px] font-black uppercase tracking-widest bg-yellow-500/20 text-yellow-400 border border-yellow-500/30">DRY RUN</span>
              ) : (
                <span className="px-2 py-0.5 rounded text-[10px] font-black uppercase tracking-widest bg-green-500/20 text-green-400 border border-green-500/30">EXECUTED</span>
              )}
              {result.import_log_id && (
                <span className="text-gray-600 text-xs flex items-center gap-1">
                  <FileText className="w-3 h-3" /> Log: {result.import_log_id.slice(-8)}
                </span>
              )}
            </div>
            <p className="text-gray-300 text-sm mb-4">{result.summary}</p>

            <div className="grid grid-cols-2 sm:grid-cols-4 gap-3">
              <StatCard label="Total Players" value={result.total_players} icon={Database} color="text-white" />
              <StatCard label="Protected" value={result.protected_count} icon={Shield} color="text-green-400" />
              <StatCard label={result.dry_run ? 'Would Delete' : 'Deleted'} value={result.dry_run ? result.deletable_count : result.deleted_count} icon={Trash2} color="text-red-400" />
              <StatCard label="Errors" value={result.errors_count} icon={XCircle} color={result.errors_count > 0 ? 'text-red-400' : 'text-gray-600'} />
            </div>
          </div>

          {/* Tab selector */}
          <div className="flex gap-2">
            <TabButton active={activeView === 'deletable'} onClick={() => setActiveView('deletable')}
              icon={Trash2} color="text-red-400"
              label={`${result.dry_run ? 'Would Delete' : 'Deleted'} (${result.deletable_count})`} />
            <TabButton active={activeView === 'protected'} onClick={() => setActiveView('protected')}
              icon={Shield} color="text-green-400"
              label={`Protected (${result.protected_count})`} />
          </div>

          {/* Tab content */}
          <div className="bg-[#111] rounded-lg border border-white/10 p-4 max-h-[400px] overflow-y-auto">
            {activeView === 'deletable' && (
              <div className="space-y-1.5">
                {result.preview?.deletable_players?.length > 0 ? result.preview.deletable_players.map((p, i) => (
                  <div key={i} className="flex items-center gap-3 py-1.5 px-3 rounded-lg bg-red-500/5">
                    <XCircle className="w-3.5 h-3.5 text-red-400 shrink-0" />
                    <span className="text-gray-300 text-sm flex-1 min-w-0 truncate">{p.full_name || 'Unknown'}</span>
                    <span className="text-gray-600 text-xs shrink-0">{p.current_team_name || p.high_school || '—'}</span>
                  </div>
                )) : <p className="text-gray-600 text-sm text-center py-6">No deletable records.</p>}
                {result.deletable_count > 50 && (
                  <p className="text-gray-700 text-xs text-center pt-2">Showing first 50 of {result.deletable_count}. Full list in ImportLog.</p>
                )}
              </div>
            )}

            {activeView === 'protected' && (
              <div className="space-y-1.5">
                {result.preview?.protected_players?.length > 0 ? result.preview.protected_players.map((p, i) => (
                  <div key={i} className="flex items-center gap-3 py-1.5 px-3 rounded-lg bg-green-500/5">
                    {p.portfolio_tier === 'premium' ? <Crown className="w-3.5 h-3.5 text-green-400 shrink-0" /> : <Lock className="w-3.5 h-3.5 text-green-400 shrink-0" />}
                    <span className="text-gray-300 text-sm flex-1 min-w-0 truncate">{p.full_name || 'Unknown'}</span>
                    <span className="text-gray-600 text-xs shrink-0">{p.reason}</span>
                    {p.portfolio_url_slug && <span className="text-gray-700 text-xs shrink-0 hidden sm:inline">/{p.portfolio_url_slug}</span>}
                  </div>
                )) : <p className="text-gray-600 text-sm text-center py-6">No protected records.</p>}
                {result.protected_count > 50 && (
                  <p className="text-gray-700 text-xs text-center pt-2">Showing first 50 of {result.protected_count}. Full list in ImportLog.</p>
                )}
              </div>
            )}
          </div>

          {/* Execute confirmation (only after dry run) */}
          {result.dry_run && result.deletable_count > 0 && (
            <div className="bg-red-500/5 border border-red-500/20 rounded-lg p-4 space-y-3">
              <div className="flex items-start gap-2">
                <AlertTriangle className="w-4 h-4 text-red-400 shrink-0 mt-0.5" />
                <p className="text-red-400 text-sm">
                  <strong>Warning:</strong> This will permanently delete <strong>{result.deletable_count}</strong> player records.
                  This cannot be undone. {result.protected_count} portfolios will be preserved.
                </p>
              </div>
              <div>
                <label className="block text-xs text-gray-500 mb-1.5">
                  Type <span className="text-red-400 font-bold">DELETE</span> to confirm:
                </label>
                <input
                  value={confirmText}
                  onChange={e => setConfirmText(e.target.value)}
                  placeholder="DELETE"
                  className="w-full bg-[#111] border border-white/10 rounded-lg px-3 py-2 text-sm text-white placeholder-gray-700 focus:outline-none focus:border-red-500/40"
                />
              </div>
              <button
                onClick={() => runReset(false)}
                disabled={!canExecute}
                className="w-full flex items-center justify-center gap-2 py-3 rounded-lg text-sm font-bold transition-all disabled:opacity-30 disabled:cursor-not-allowed"
                style={{ background: canExecute ? '#dc2626' : '#333', color: canExecute ? '#fff' : '#666' }}
              >
                {loading ? <Loader2 className="w-4 h-4 animate-spin" /> : <Trash2 className="w-4 h-4" />}
                Execute Clean Slate Reset — Delete {result.deletable_count} Records
              </button>
              {!canExecute && confirmText !== '' && confirmText !== 'DELETE' && (
                <p className="text-red-400 text-xs text-center">You must type "DELETE" exactly to proceed.</p>
              )}
            </div>
          )}

          {result.dry_run && result.deletable_count === 0 && (
            <div className="bg-green-500/5 border border-green-500/20 rounded-lg p-4 flex items-center gap-2">
              <CheckCircle2 className="w-5 h-5 text-green-400 shrink-0" />
              <p className="text-green-400 text-sm">All records are protected — nothing to delete. Database is already clean.</p>
            </div>
          )}

          {!result.dry_run && result.success && (
            <div className="bg-green-500/5 border border-green-500/20 rounded-lg p-4 flex items-center gap-2">
              <CheckCircle2 className="w-5 h-5 text-green-400 shrink-0" />
              <p className="text-green-400 text-sm">
                Clean slate complete. {result.deleted_count} records deleted, {result.protected_count} portfolios preserved.
                {result.errors_count > 0 && ` ${result.errors_count} errors — check ImportLog for details.`}
              </p>
            </div>
          )}
        </div>
      )}
    </div>
  );
}

function StatCard({ label, value, icon: Icon, color }) {
  return (
    <div className="bg-[#1a1a1a] rounded-lg p-3 text-center border border-white/5">
      <Icon className={`w-4 h-4 mx-auto mb-1 ${color}`} />
      <div className={`text-xl font-black ${color}`}>{value}</div>
      <div className="text-[10px] text-gray-600 uppercase tracking-wide mt-0.5">{label}</div>
    </div>
  );
}

function TabButton({ active, onClick, icon: Icon, color, label }) {
  return (
    <button
      onClick={onClick}
      className={`flex items-center gap-1.5 px-4 py-2 rounded-lg text-sm font-medium transition-all
        ${active ? 'bg-[#333] text-white' : 'text-gray-500 hover:text-gray-300'}`}
    >
      <Icon className={`w-3.5 h-3.5 ${color}`} />
      {label}
    </button>
  );
}

src/components/admin/PdfStatsExtractor.jsx

import { useState, useRef } from 'react';
import { base44 } from '@/api/base44Client';
import { Upload, Loader2, CheckCircle2, AlertCircle } from 'lucide-react';

const ORANGE = '#FF6A00';
const STAT_KEYS = [
  'pts','ast','to','oreb','dreb','spg','bpg','two_pt_made','two_pt_att',
  'three_pt_made','three_pt_att','ft_made','ft_att','shots','opps',
  'efg_pct','ft_pct','two_fg_pct','three_fg_pct','ato_ratio',
  'oreb_pct','ts_pct','ftf','off_ppp'
];
const STRING_STATS = ['efg_pct','ft_pct','two_fg_pct','three_fg_pct','ato_ratio','oreb_pct','ts_pct','off_ppp'];

export default function PdfStatsExtractor({ team1, team2, onStatsExtracted }) {
  const [status, setStatus] = useState('idle');
  const [error, setError] = useState(null);
  const [dragging, setDragging] = useState(false);
  const fileRef = useRef(null);

  const processFile = async (file) => {
    if (!file) return;
    setStatus('uploading');
    setError(null);

    let fileUrl;
    try {
      const res = await base44.integrations.Core.UploadFile({ file });
      fileUrl = res.file_url;
    } catch (e) {
      setError('Upload failed: ' + e.message);
      setStatus('error');
      return;
    }

    setStatus('extracting');
    try {
      const statProps = {};
      STAT_KEYS.forEach(k => {
        statProps[k] = STRING_STATS.includes(k) ? { type: 'string' } : { type: 'number' };
      });

      const result = await base44.integrations.Core.InvokeLLM({
        prompt: `Analyze this basketball game statistics PDF (Hoopsalytics game-by-game format).
Find the game between "${team1}" and "${team2}". Team names may appear differently (abbreviated, with state codes like "- GA", etc.) — use fuzzy matching.
The PDF likely contains game-by-game stats for one team across multiple opponents. Identify which team the PDF covers, find the row for the game against the other team, and extract their offensive stats.
If the PDF also contains the opponent's stats for the same game, extract those too under opponent_stats.
Set "found" to false ONLY if no matching game exists in the PDF.`,
        file_urls: [fileUrl],
        response_json_schema: {
          type: 'object',
          properties: {
            found: { type: 'boolean' },
            pdf_team_name: { type: 'string', description: 'The team whose stats this PDF primarily covers' },
            opponent_in_pdf: { type: 'string', description: 'How the opponent appears in the PDF' },
            pdf_team_stats: { type: 'object', properties: statProps },
            opponent_stats: { type: 'object', properties: statProps, description: 'Only if available in PDF' },
          }
        }
      });

      if (!result.found) {
        setError(`No game between "${team1}" and "${team2}" found in this PDF`);
        setStatus('error');
        return;
      }

      const norm = (n) => (n || '').toLowerCase().replace(/[^a-z0-9]/g, '');
      const nPdf = norm(result.pdf_team_name);
      const n1 = norm(team1);
      const n2 = norm(team2);
      const isPdfTeam1 = nPdf.includes(n1) || n1.includes(nPdf);

      let t1Stats = {}, t2Stats = {};
      if (isPdfTeam1) {
        t1Stats = result.pdf_team_stats || {};
        t2Stats = result.opponent_stats || {};
      } else {
        t2Stats = result.pdf_team_stats || {};
        t1Stats = result.opponent_stats || {};
      }

      onStatsExtracted(t1Stats, t2Stats);
      setStatus('done');
    } catch (e) {
      setError('Extraction failed: ' + e.message);
      setStatus('error');
    }
  };

  return (
    <div
      onDragOver={e => { e.preventDefault(); setDragging(true); }}
      onDragLeave={() => setDragging(false)}
      onDrop={e => { e.preventDefault(); setDragging(false); processFile(e.dataTransfer.files?.[0]); }}
      onClick={() => !['uploading','extracting'].includes(status) && fileRef.current?.click()}
      className="border-2 border-dashed rounded-xl p-5 text-center cursor-pointer transition-all"
      style={{
        borderColor: status === 'done' ? 'rgba(0,255,133,0.3)' : status === 'error' ? 'rgba(255,59,48,0.3)' : dragging ? ORANGE : 'rgba(255,255,255,0.12)',
        background: status === 'done' ? 'rgba(0,255,133,0.03)' : status === 'error' ? 'rgba(255,59,48,0.03)' : dragging ? 'rgba(255,106,0,0.05)' : 'rgba(255,255,255,0.02)'
      }}
    >
      {status === 'idle' && <>
        <Upload className="w-5 h-5 mx-auto mb-1.5 text-slate-500" />
        <p className="text-white font-semibold text-xs">Drop a stat PDF to auto-fill</p>
        <p className="text-slate-600 text-[10px] mt-0.5">Hoopsalytics game-by-game format</p>
      </>}
      {(status === 'uploading' || status === 'extracting') && <>
        <Loader2 className="w-5 h-5 mx-auto mb-1.5 animate-spin text-orange-400" />
        <p className="text-orange-400 font-semibold text-xs">{status === 'uploading' ? 'Uploading PDF…' : 'Extracting stats…'}</p>
      </>}
      {status === 'done' && <>
        <CheckCircle2 className="w-5 h-5 mx-auto mb-1.5 text-green-400" />
        <p className="text-green-400 font-semibold text-xs">Stats extracted — review below</p>
        <p className="text-slate-600 text-[10px] mt-0.5">Drop another PDF to re-extract</p>
      </>}
      {status === 'error' && <>
        <AlertCircle className="w-5 h-5 mx-auto mb-1.5 text-red-400" />
        <p className="text-red-400 font-semibold text-xs">{error}</p>
        <p className="text-slate-600 text-[10px] mt-0.5">Try again or fill manually</p>
      </>}
      <input ref={fileRef} type="file" accept=".pdf" className="hidden" onChange={e => processFile(e.target.files?.[0])} />
    </div>
  );
}

src/components/admin/PdfStatsImporter.jsx

import { useState, useRef } from 'react';
import { base44 } from '@/api/base44Client';
import { CheckCircle2, AlertCircle, Loader2, Play, Upload, X, FileText, Plus } from 'lucide-react';

const PRELOADED_PDFS = [
  { team: 'Alpharetta', url: 'https://media.base44.com/files/public/69bb16a38ac0086814d94e6f/f2317d93a_Alpharetta-GAAlpharetta.pdf' },
  { team: 'Apalachee', url: 'https://media.base44.com/files/public/69bb16a38ac0086814d94e6f/b7a649c43_Apalachee-GAApalachee-GA.pdf' },
  { team: 'Baldwin', url: 'https://media.base44.com/files/public/69bb16a38ac0086814d94e6f/6c2bbf8b0_Baldwin-GABaldwin-GA.pdf' },
  { team: 'Beach', url: 'https://media.base44.com/files/public/69bb16a38ac0086814d94e6f/5612924c7_Beach-GABeach-GA.pdf' },
  { team: 'Berkmar', url: 'https://media.base44.com/files/public/69bb16a38ac0086814d94e6f/80d5a4f1d_Berkmar-GA.pdf' },
  { team: 'BEST Academy', url: 'https://media.base44.com/files/public/69bb16a38ac0086814d94e6f/fd2362526_BESTAcademy-GA.pdf' },
  { team: 'Bowdon', url: 'https://media.base44.com/files/public/69bb16a38ac0086814d94e6f/dbc511061_Bowdon-GA.pdf' },
  { team: 'Bradwell Institute', url: 'https://media.base44.com/files/public/69bb16a38ac0086814d94e6f/a46e277da_BradwellInstitute-GA.pdf' },
  { team: 'Brookwood', url: 'https://media.base44.com/files/public/69bb16a38ac0086814d94e6f/292f4a13c_Brookwood-GA.pdf' },
  { team: 'Brunswick', url: 'https://media.base44.com/files/public/69bb16a38ac0086814d94e6f/b2b65f488_Brunswick-GABrunswick-GA-Incomplete.pdf' },
  { team: 'Buford', url: 'https://media.base44.com/files/public/69bb16a38ac0086814d94e6f/ee4f0414c_Buford-GA.pdf' },
  { team: 'Burke County', url: 'https://media.base44.com/files/public/69bb16a38ac0086814d94e6f/5fb6087d1_BurkeCounty-GA.pdf' },
  { team: 'Butler', url: 'https://media.base44.com/files/public/69bb16a38ac0086814d94e6f/705464a32_Butler-GA.pdf' },
  { team: 'Campbell', url: 'https://media.base44.com/files/public/69bb16a38ac0086814d94e6f/52766de6e_Campbell-GA.pdf' },
  { team: 'Carrollton', url: 'https://media.base44.com/files/public/69bb16a38ac0086814d94e6f/22fa42e58_Carrollton-GA.pdf' },
  { team: 'Cartersville', url: 'https://media.base44.com/files/public/69bb16a38ac0086814d94e6f/2882396d8_Cartersville-GA.pdf' },
  { team: 'Cedar Grove', url: 'https://media.base44.com/files/public/69bb16a38ac0086814d94e6f/861e39aad_CedarGrove-GA.pdf' },
  { team: 'Cedar Shoals', url: 'https://media.base44.com/files/public/69bb16a38ac0086814d94e6f/4acfd6815_CedarShoals-GA.pdf' },
  { team: 'Centennial', url: 'https://media.base44.com/files/public/69bb16a38ac0086814d94e6f/98008562e_Centennial-GA.pdf' },
  { team: 'Chamblee', url: 'https://media.base44.com/files/public/69bb16a38ac0086814d94e6f/978b25704_Chamblee-GA.pdf' },
  { team: 'Chapel Hill', url: 'https://media.base44.com/files/public/69bb16a38ac0086814d94e6f/8000e8104_ChapelHill-GA.pdf' },
  { team: 'Cherokee', url: 'https://media.base44.com/files/public/69bb16a38ac0086814d94e6f/545888faf_Cherokee-GA.pdf' },
  { team: 'Christian Heritage', url: 'https://media.base44.com/files/public/69bb16a38ac0086814d94e6f/6ff10f03d_ChristianHeritage-GA.pdf' },
  { team: 'Columbia', url: 'https://media.base44.com/files/public/69bb16a38ac0086814d94e6f/f431182bd_Columbia-GAColumbia.pdf' },
  { team: 'Cross Creek', url: 'https://media.base44.com/files/public/69bb16a38ac0086814d94e6f/dfcbcb148_CrossCreek-GA.pdf' },
  { team: 'Dacula', url: 'https://media.base44.com/files/public/69bb16a38ac0086814d94e6f/9363ae547_Dacula-GADacula.pdf' },
  { team: 'Darlington School', url: 'https://media.base44.com/files/public/69bb16a38ac0086814d94e6f/3aab12695_DarlingtonSchool-GA.pdf' },
  { team: 'Douglas County', url: 'https://media.base44.com/files/public/69bb16a38ac0086814d94e6f/34b7a20c2_DouglasCounty-GA.pdf' },
  { team: 'East Coweta', url: 'https://media.base44.com/files/public/69bb16a38ac0086814d94e6f/afed171ce_EastCoweta-GA.pdf' },
  { team: 'East Forsyth', url: 'https://media.base44.com/files/public/69bb16a38ac0086814d94e6f/143e58965_EastForsyth-GA.pdf' },
  { team: 'East Paulding', url: 'https://media.base44.com/files/public/69bb16a38ac0086814d94e6f/5923d634a_EastPaulding-GA.pdf' },
  { team: 'Eastside', url: 'https://media.base44.com/files/public/69bb16a38ac0086814d94e6f/13811eacb_Eastside-GA.pdf' },
  { team: 'ELCA', url: 'https://media.base44.com/files/public/69bb16a38ac0086814d94e6f/4b8b426bd_ELCA-GA.pdf' },
  { team: 'Franklin County', url: 'https://media.base44.com/files/public/69bb16a38ac0086814d94e6f/f75d6ad22_FranklinCounty-GA.pdf' },
  { team: 'Gainesville', url: 'https://media.base44.com/files/public/69bb16a38ac0086814d94e6f/c939f747b_Gainesville-GA.pdf' },
  { team: 'Alexander', url: 'https://media.base44.com/files/public/69bb16a38ac0086814d94e6f/cd5b80f6a_GamebyGameStats_AlexanderGA.pdf' },
  { team: 'Grayson', url: 'https://media.base44.com/files/public/69bb16a38ac0086814d94e6f/eaf1f2bc3_Grayson-GA.pdf' },
  { team: 'Greater Atlanta Christian', url: 'https://media.base44.com/files/public/69bb16a38ac0086814d94e6f/b0e3c395c_GreaterAtlantaChristian-GA.pdf' },
  { team: 'Habersham Central', url: 'https://media.base44.com/files/public/69bb16a38ac0086814d94e6f/36c311238_HabershamCentral-GA.pdf' },
  { team: 'Harlem', url: 'https://media.base44.com/files/public/69bb16a38ac0086814d94e6f/d52c96db9_Harlem-GA.pdf' },
  { team: 'Hebron Christian', url: 'https://media.base44.com/files/public/69bb16a38ac0086814d94e6f/c572549c5_HebronChristian-GA.pdf' },
  { team: 'Hillgrove', url: 'https://media.base44.com/files/public/69bb16a38ac0086814d94e6f/cdbfdec23_Hillgrove-GA.pdf' },
  { team: 'Holy Innocents', url: 'https://media.base44.com/files/public/69bb16a38ac0086814d94e6f/42559a2f7_HolyInnocents-GA.pdf' },
  { team: 'Jonesboro', url: 'https://media.base44.com/files/public/69bb16a38ac0086814d94e6f/8601c91af_Jonesboro-GA.pdf' },
  { team: 'Kell', url: 'https://media.base44.com/files/public/69bb16a38ac0086814d94e6f/1525bba10_Kell-GAKell.pdf' },
  { team: 'Lanier', url: 'https://media.base44.com/files/public/69bb16a38ac0086814d94e6f/78a925d04_Lanier-GA.pdf' },
  { team: 'Lassiter', url: 'https://media.base44.com/files/public/69bb16a38ac0086814d94e6f/d56d59925_Lassiter-GA.pdf' },
  { team: 'Lee County', url: 'https://media.base44.com/files/public/69bb16a38ac0086814d94e6f/7d1447caf_LeeCounty-GA.pdf' },
  { team: 'Lovett School', url: 'https://media.base44.com/files/public/69bb16a38ac0086814d94e6f/2d5babf54_LovettSchool-GA.pdf' },
  { team: 'Madison County', url: 'https://media.base44.com/files/public/69bb16a38ac0086814d94e6f/3cc0c2563_MadisonCounty-GA.pdf' },
];

function guessTeamName(filename) {
  const base = filename.replace(/\.pdf$/i, '').replace(/^.*_/, '');
  return base.replace(/-GA.*$/i, '').replace(/GamebyGameStats_/i, '').replace(/[-_]/g, ' ').trim();
}

export default function PdfStatsImporter() {
  const [tab, setTab] = useState('preloaded');
  const [statuses, setStatuses] = useState({});
  const [running, setRunning] = useState(false);
  const [currentTeam, setCurrentTeam] = useState('');
  const [syncRunning, setSyncRunning] = useState(false);
  const [syncResult, setSyncResult] = useState(null);
  const [uploadQueue, setUploadQueue] = useState([]);
  const [dragging, setDragging] = useState(false);
  const fileInputRef = useRef();

  const importTeam = async (pdf) => {
    setStatuses(prev => ({ ...prev, [pdf.team]: { status: 'loading' } }));
    const res = await base44.functions.invoke('importGeorgiaStats', { pdf_url: pdf.url, team_name: pdf.team });
    const data = res.data;
    const updated = data?.results?.filter(r => r.status === 'updated').length || 0;
    const notFound = data?.results?.filter(r => r.status === 'not_found').length || 0;
    setStatuses(prev => ({
      ...prev,
      [pdf.team]: { status: data?.error ? 'error' : 'done', updated, notFound, error: data?.error }
    }));
  };

  const importAll = async () => {
    setRunning(true);
    for (const pdf of PRELOADED_PDFS) {
      if (statuses[pdf.team]?.status === 'done') continue;
      setCurrentTeam(pdf.team);
      await importTeam(pdf);
      await new Promise(r => setTimeout(r, 800));
    }
    setCurrentTeam('');
    setRunning(false);
  };

  const doneCount = Object.values(statuses).filter(s => s.status === 'done').length;
  const errorCount = Object.values(statuses).filter(s => s.status === 'error').length;

  const handleFiles = (files) => {
    const pdfs = Array.from(files).filter(f => f.type === 'application/pdf' || f.name.endsWith('.pdf'));
    const newItems = pdfs.map(file => ({
      id: Math.random().toString(36).slice(2),
      file,
      teamName: guessTeamName(file.name),
      url: null,
      uploadStatus: 'pending',
      result: null,
      error: null,
    }));
    setUploadQueue(prev => [...prev, ...newItems]);
  };

  const updateItem = (id, patch) => setUploadQueue(prev => prev.map(i => i.id === id ? { ...i, ...patch } : i));

  const uploadAndImportAll = async () => {
    setRunning(true);
    for (const item of uploadQueue) {
      if (item.uploadStatus === 'done') continue;
      updateItem(item.id, { uploadStatus: 'uploading' });
      let fileUrl;
      try {
        const { file_url } = await base44.integrations.Core.UploadFile({ file: item.file });
        fileUrl = file_url;
        updateItem(item.id, { url: fileUrl, uploadStatus: 'importing' });
      } catch (e) {
        updateItem(item.id, { uploadStatus: 'error', error: 'Upload failed: ' + e.message });
        continue;
      }
      try {
        const res = await base44.functions.invoke('importGeorgiaStats', { pdf_url: fileUrl, team_name: item.teamName });
        const data = res.data;
        const updated = data?.results?.filter(r => r.status === 'updated').length || 0;
        const notFound = data?.results?.filter(r => r.status === 'not_found').length || 0;
        updateItem(item.id, {
          uploadStatus: data?.error ? 'error' : 'done',
          result: { updated, notFound },
          error: data?.error || null,
        });
      } catch (e) {
        updateItem(item.id, { uploadStatus: 'error', error: 'Import failed: ' + e.message });
      }
      await new Promise(r => setTimeout(r, 500));
    }
    setRunning(false);
  };

  const pendingUploadCount = uploadQueue.filter(i => i.uploadStatus !== 'done').length;

  return (
    <div>
      <div className="flex items-center justify-between mb-6">
        <div>
          <h2 className="text-xl font-black text-white">Stats Import</h2>
          <p className="text-slate-400 text-sm mt-1">Import game stats from Hoopsalytics PDFs</p>
        </div>
        <div className="flex gap-2">
          <button onClick={() => {
            setSyncRunning(true);
            base44.functions.invoke('addPlayersToTeamRosters', {}).then(res => {
              setSyncResult(res.data);
              setSyncRunning(false);
            }).catch(e => {
              setSyncResult({ error: e.message });
              setSyncRunning(false);
            });
          }} disabled={syncRunning}
            className="flex items-center gap-2 px-4 py-2 rounded-lg text-sm font-bold disabled:opacity-50"
            style={{ background: '#00FF85', color: '#000' }}>
            {syncRunning ? <Loader2 className="w-4 h-4 animate-spin" /> : <Plus className="w-4 h-4" />}
            {syncRunning ? 'Syncing...' : 'Sync Players'}
          </button>
          <button onClick={() => {
            setSyncRunning(true);
            base44.functions.invoke('populateTeamStats', {}).then(res => {
              setSyncResult(res.data);
              setSyncRunning(false);
            }).catch(e => {
              setSyncResult({ error: e.message });
              setSyncRunning(false);
            });
          }} disabled={syncRunning}
            className="flex items-center gap-2 px-4 py-2 rounded-lg text-sm font-bold disabled:opacity-50"
            style={{ background: '#FF6A00', color: '#000' }}>
            {syncRunning ? <Loader2 className="w-4 h-4 animate-spin" /> : <Plus className="w-4 h-4" />}
            {syncRunning ? 'Syncing...' : 'Sync Stats'}
          </button>
        </div>
      </div>

      {syncResult && (
        <div className={`mb-6 p-4 rounded-lg text-sm font-semibold ${syncResult.error ? 'text-red-400 bg-red-950' : 'text-green-400 bg-green-950'}`}>
          {syncResult.error ? `Error: ${syncResult.error}` : `✓ ${syncResult.added} players added ${syncResult.errors?.length > 0 ? `(${syncResult.errors.length} errors)` : ''}`}
        </div>
      )}

      <div className="flex gap-1 mb-6 border-b border-white/10">
        {[{ id: 'preloaded', label: 'Pre-loaded' }, { id: 'upload', label: 'Upload New' }].map(t => (
          <button key={t.id} onClick={() => setTab(t.id)}
            className={`px-4 py-2.5 text-sm font-bold border-b-2 -mb-px transition-all ${tab === t.id ? 'text-white border-orange-500' : 'text-slate-500 border-transparent hover:text-slate-300'}`}>
            {t.label}
          </button>
        ))}
      </div>

      {tab === 'preloaded' && (
        <div>
          <div className="flex items-center justify-between mb-4">
            <p className="text-slate-400 text-sm">{PRELOADED_PDFS.length} teams · {doneCount} imported{errorCount > 0 ? ` · ${errorCount} errors` : ''}</p>
            <button onClick={importAll} disabled={running}
              className="flex items-center gap-2 px-4 py-2 rounded-lg text-sm font-bold disabled:opacity-50"
              style={{ background: '#FF6A00', color: '#000' }}>
              {running ? <Loader2 className="w-4 h-4 animate-spin" /> : <Play className="w-4 h-4" />}
              {running ? `Importing ${currentTeam}...` : 'Import All'}
            </button>
          </div>
          <div className="grid sm:grid-cols-2 lg:grid-cols-3 gap-3">
            {PRELOADED_PDFS.map(pdf => {
              const s = statuses[pdf.team];
              return (
                <div key={pdf.team} className="flex items-center justify-between px-4 py-3 rounded-xl border"
                  style={{ background: '#0a0a0a', borderColor: s?.status === 'done' ? 'rgba(0,255,133,0.2)' : s?.status === 'error' ? 'rgba(255,59,48,0.2)' : 'rgba(255,255,255,0.06)' }}>
                  <div className="flex-1 min-w-0">
                    <p className="text-white font-semibold text-sm">{pdf.team}</p>
                    {s?.status === 'done' && <p className="text-xs text-green-400">{s.updated} updated{s.notFound > 0 ? `, ${s.notFound} unmatched` : ''}</p>}
                    {s?.status === 'error' && <p className="text-xs text-red-400">{s.error}</p>}
                  </div>
                  <div className="ml-3 shrink-0">
                    {s?.status === 'loading' && <Loader2 className="w-4 h-4 animate-spin text-orange-400" />}
                    {s?.status === 'done' && <CheckCircle2 className="w-4 h-4 text-green-400" />}
                    {s?.status === 'error' && <AlertCircle className="w-4 h-4 text-red-400" />}
                    {!s && (
                      <button onClick={() => importTeam(pdf)}
                        className="text-xs px-2 py-1 rounded font-bold text-slate-500 hover:text-white"
                        style={{ background: 'rgba(255,255,255,0.05)' }}>
                        Import
                      </button>
                    )}
                  </div>
                </div>
              );
            })}
          </div>
        </div>
      )}

      {tab === 'upload' && (
        <div>
          <div onDragOver={e => { e.preventDefault(); setDragging(true); }} onDragLeave={() => setDragging(false)}
            onDrop={e => { e.preventDefault(); setDragging(false); handleFiles(e.dataTransfer.files); }}
            onClick={() => fileInputRef.current?.click()}
            className="border-2 border-dashed rounded-xl p-10 text-center cursor-pointer transition-all mb-6"
            style={{ borderColor: dragging ? '#FF6A00' : 'rgba(255,255,255,0.12)', background: dragging ? 'rgba(255,106,0,0.05)' : 'rgba(255,255,255,0.02)' }}>
            <Upload className="w-8 h-8 mx-auto mb-3 text-slate-500" />
            <p className="text-white font-semibold text-sm">Drop PDF files or click to browse</p>
            <p className="text-slate-500 text-xs mt-1">Team names auto-detected from filename</p>
            <input ref={fileInputRef} type="file" accept=".pdf" multiple className="hidden" onChange={e => handleFiles(e.target.files)} />
          </div>

          {uploadQueue.length > 0 && (
            <>
              <div className="flex items-center justify-between mb-3">
                <p className="text-slate-400 text-sm">{uploadQueue.length} files queued</p>
                <div className="flex gap-2">
                  <button onClick={() => setUploadQueue([])} disabled={running}
                    className="text-xs px-3 py-1.5 rounded-lg font-bold text-slate-500 hover:text-white disabled:opacity-40"
                    style={{ background: 'rgba(255,255,255,0.05)' }}>
                    Clear All
                  </button>
                  <button onClick={uploadAndImportAll} disabled={running || pendingUploadCount === 0}
                    className="flex items-center gap-2 px-4 py-1.5 rounded-lg text-sm font-bold disabled:opacity-50"
                    style={{ background: '#FF6A00', color: '#000' }}>
                    {running ? <Loader2 className="w-4 h-4 animate-spin" /> : <Play className="w-4 h-4" />}
                    {running ? 'Importing...' : `Import All (${pendingUploadCount})`}
                  </button>
                </div>
              </div>

              <div className="space-y-2">
                {uploadQueue.map(item => (
                  <div key={item.id} className="flex items-center gap-3 px-4 py-3 rounded-xl border"
                    style={{ background: '#0a0a0a', borderColor: item.uploadStatus === 'done' ? 'rgba(0,255,133,0.2)' : item.uploadStatus === 'error' ? 'rgba(255,59,48,0.2)' : 'rgba(255,255,255,0.06)' }}>
                    <FileText className="w-4 h-4 text-slate-500 shrink-0" />
                    <div className="flex-1 min-w-0">
                      <input value={item.teamName} onChange={e => updateItem(item.id, { teamName: e.target.value })}
                        disabled={item.uploadStatus !== 'pending'}
                        className="bg-transparent text-white font-semibold text-sm w-full focus:outline-none border-b border-transparent focus:border-orange-500 transition-colors disabled:opacity-60"
                        placeholder="Team name..." />
                      <p className="text-xs text-slate-600 truncate">{item.file.name}</p>
                      {item.uploadStatus === 'done' && <p className="text-xs text-green-400">{item.result?.updated} games updated{item.result?.notFound > 0 ? `, ${item.result.notFound} unmatched` : ''}</p>}
                      {item.uploadStatus === 'error' && <p className="text-xs text-red-400">{item.error}</p>}
                    </div>
                    <div className="shrink-0 flex items-center gap-2">
                      {item.uploadStatus === 'uploading' && <span className="text-xs text-slate-400">Uploading...</span>}
                      {item.uploadStatus === 'importing' && <span className="text-xs text-orange-400">Importing...</span>}
                      {(item.uploadStatus === 'uploading' || item.uploadStatus === 'importing') && <Loader2 className="w-4 h-4 animate-spin text-orange-400" />}
                      {item.uploadStatus === 'done' && <CheckCircle2 className="w-4 h-4 text-green-400" />}
                      {item.uploadStatus === 'error' && <AlertCircle className="w-4 h-4 text-red-400" />}
                      {item.uploadStatus === 'pending' && (
                        <button onClick={() => setUploadQueue(prev => prev.filter(i => i.id !== item.id))} className="text-slate-600 hover:text-white transition-colors">
                          <X className="w-4 h-4" />
                        </button>
                      )}
                    </div>
                  </div>
                ))}
              </div>
            </>
          )}
        </div>
      )}
    </div>
  );
}

src/components/admin/UpdateRequestCard.jsx

import { useState } from 'react';
import { base44 } from '@/api/base44Client';
import { Link } from 'react-router-dom';
import { ExternalLink, CheckCircle2, XCircle, Clock, Loader2, MessageSquare } from 'lucide-react';

const STATUS_BADGE = {
  submitted: 'bg-blue-500/10 text-blue-400',
  in_review: 'bg-yellow-500/10 text-yellow-400',
  applied: 'bg-green-500/10 text-green-400',
  rejected: 'bg-red-500/10 text-red-400',
};

const CAT_LABELS = {
  contact_info: 'Contact Info', identity: 'Identity', academic: 'Academic',
  athletic: 'Athletic', media: 'Media', bio: 'Bio', stats: 'Stats',
  social: 'Social', offers: 'Offers', visibility: 'Visibility', other: 'Other',
};

export default function UpdateRequestCard({ request, onChanged }) {
  const [notes, setNotes] = useState(request.admin_notes || '');
  const [busy, setBusy] = useState(false);

  const updateStatus = async (status) => {
    setBusy(true);
    try {
      await base44.entities.UpdateRequest.update(request.id, {
        status,
        admin_notes: notes,
        reviewed_date: new Date().toISOString(),
      });
      onChanged();
    } catch {
      alert('Failed to update request status.');
    } finally {
      setBusy(false);
    }
  };

  const saveNotes = async () => {
    setBusy(true);
    try {
      await base44.entities.UpdateRequest.update(request.id, { admin_notes: notes });
      onChanged();
    } catch {
      alert('Failed to save notes.');
    } finally {
      setBusy(false);
    }
  };

  return (
    <div className="rounded-xl border border-slate-800 bg-slate-900/60 p-4">
      <div className="flex items-start justify-between mb-2">
        <div>
          <p className="text-sm font-bold text-white">{request.player_name || 'Unknown'}</p>
          <p className="text-xs text-slate-500">
            From: {request.requester_name || 'Unknown'} &middot; {CAT_LABELS[request.category] || request.category}
          </p>
        </div>
        <span className={`px-2 py-0.5 rounded text-xs font-semibold capitalize ${STATUS_BADGE[request.status] || ''}`}>
          {request.status.replace('_', ' ')}
        </span>
      </div>
      <p className="text-sm text-slate-300 bg-slate-800/50 rounded p-2 mb-3">{request.requested_changes}</p>
      <div className="flex gap-2 mb-3">
        <input value={notes} onChange={e => setNotes(e.target.value)} placeholder="Admin notes..."
          className="flex-1 px-2 py-1.5 rounded bg-slate-800 border border-slate-700 text-white text-xs focus:outline-none focus:border-orange-500" />
        <button onClick={saveNotes} disabled={busy} className="px-2 py-1.5 rounded bg-slate-800 text-slate-400 hover:text-white text-xs">
          <MessageSquare className="w-3.5 h-3.5" />
        </button>
      </div>
      <div className="flex flex-wrap gap-2 items-center">
        {request.status === 'submitted' && (
          <button onClick={() => updateStatus('in_review')} disabled={busy}
            className="px-3 py-1.5 rounded-lg bg-yellow-500/20 text-yellow-400 text-xs font-semibold hover:bg-yellow-500/30 flex items-center gap-1">
            <Clock className="w-3.5 h-3.5" /> Start Review
          </button>
        )}
        {request.status === 'in_review' && (
          <>
            <Link to={`/admin-player/${request.player_id}`}
              className="px-3 py-1.5 rounded-lg bg-orange-500 text-black text-xs font-semibold hover:bg-orange-600 flex items-center gap-1">
              <ExternalLink className="w-3.5 h-3.5" /> Edit Player
            </Link>
            <button onClick={() => updateStatus('applied')} disabled={busy}
              className="px-3 py-1.5 rounded-lg bg-green-500/20 text-green-400 text-xs font-semibold hover:bg-green-500/30 flex items-center gap-1">
              <CheckCircle2 className="w-3.5 h-3.5" /> Mark Applied
            </button>
            <button onClick={() => updateStatus('rejected')} disabled={busy}
              className="px-3 py-1.5 rounded-lg bg-red-500/20 text-red-400 text-xs font-semibold hover:bg-red-500/30 flex items-center gap-1">
              <XCircle className="w-3.5 h-3.5" /> Reject
            </button>
          </>
        )}
        {request.status === 'applied' && (
          <Link to={`/admin-player/${request.player_id}`}
            className="px-3 py-1.5 rounded-lg bg-slate-800 text-slate-400 text-xs font-semibold hover:text-white flex items-center gap-1">
            <ExternalLink className="w-3.5 h-3.5" /> View Player
          </Link>
        )}
        {busy && <Loader2 className="w-4 h-4 animate-spin text-slate-500" />}
      </div>
    </div>
  );
}

src/components/admin/dashboard/AssignCoachModal.jsx

import { useState } from 'react';
import { X, Plus, Loader2 } from 'lucide-react';
import { ORANGE } from './constants';

export default function AssignCoachModal({ teamName, coachProfiles, onAssign, onCreate, onClose, assigning }) {
  const [search, setSearch] = useState('');
  const [newCoach, setNewCoach] = useState({ coach_name: '', email: '', phone: '' });

  const available = coachProfiles
    .filter(c => c.high_school !== teamName)
    .filter(c =>
      c.coach_name?.toLowerCase().includes(search.toLowerCase()) ||
      c.email?.toLowerCase().includes(search.toLowerCase())
    )
    .slice(0, 10);

  return (
    <div className="fixed inset-0 z-50 flex items-center justify-center bg-black/60 backdrop-blur-sm p-4">
      <div className="bg-slate-900 border border-slate-800 rounded-xl w-full max-w-sm">
        <div className="flex items-center justify-between px-5 py-4 border-b border-slate-800">
          <h2 className="text-white font-black text-base">Assign Coach to {teamName}</h2>
          <button onClick={onClose} className="text-slate-500 hover:text-white"><X className="w-4 h-4" /></button>
        </div>

        <div className="px-5 py-4 space-y-4">
          {/* Search existing */}
          <div>
            <label className="text-xs text-slate-400 font-semibold block mb-1.5">Assign Existing Coach</label>
            <input type="text" value={search} onChange={e => setSearch(e.target.value)}
              placeholder="Search coaches…"
              className="w-full text-xs px-2 py-1.5 rounded bg-slate-800 border border-slate-700 text-white placeholder-slate-600 focus:outline-none focus:border-orange-500 mb-2" />
            <div className="max-h-48 overflow-y-auto space-y-1">
              {available.map(coach => (
                <button key={coach.id} onClick={() => onAssign(coach)} disabled={assigning}
                  className="w-full text-left px-3 py-2 rounded text-xs bg-slate-800 hover:bg-slate-700 text-white transition-all disabled:opacity-50">
                  <p className="font-semibold">{coach.coach_name}</p>
                  <p className="text-slate-500 text-[10px]">{coach.email}</p>
                </button>
              ))}
              {available.length === 0 && <p className="text-xs text-slate-600 py-2">No available coaches</p>}
            </div>
          </div>

          {/* Divider */}
          <div className="relative">
            <div className="absolute inset-0 flex items-center"><div className="w-full border-t border-slate-700" /></div>
            <div className="relative flex justify-center text-xs"><span className="px-2 bg-slate-900 text-slate-500">or</span></div>
          </div>

          {/* Create new */}
          <div className="space-y-2">
            <p className="text-xs text-slate-400 font-semibold">Create New Coach</p>
            <input type="text" value={newCoach.coach_name} onChange={e => setNewCoach(p => ({ ...p, coach_name: e.target.value }))}
              placeholder="Coach Name"
              className="w-full text-xs px-2 py-1.5 rounded bg-slate-800 border border-slate-700 text-white placeholder-slate-600 focus:outline-none focus:border-orange-500" />
            <input type="email" value={newCoach.email} onChange={e => setNewCoach(p => ({ ...p, email: e.target.value }))}
              placeholder="Email"
              className="w-full text-xs px-2 py-1.5 rounded bg-slate-800 border border-slate-700 text-white placeholder-slate-600 focus:outline-none focus:border-orange-500" />
            <input type="tel" value={newCoach.phone} onChange={e => setNewCoach(p => ({ ...p, phone: e.target.value }))}
              placeholder="Phone (optional)"
              className="w-full text-xs px-2 py-1.5 rounded bg-slate-800 border border-slate-700 text-white placeholder-slate-600 focus:outline-none focus:border-orange-500" />
            <button onClick={() => onCreate(newCoach)} disabled={assigning}
              className="w-full flex items-center justify-center gap-1.5 py-2.5 rounded-lg text-sm font-bold text-white disabled:opacity-50 transition-all"
              style={{ background: ORANGE }}>
              {assigning ? <Loader2 className="w-4 h-4 animate-spin" /> : <Plus className="w-4 h-4" />}
              Create & Assign
            </button>
          </div>
        </div>

        <div className="flex gap-2 px-5 py-4 border-t border-slate-800">
          <button onClick={onClose} className="flex-1 px-4 py-2.5 rounded-lg text-sm font-bold text-slate-400 hover:text-white bg-slate-800 hover:bg-slate-700">
            Cancel
          </button>
        </div>
      </div>
    </div>
  );
}

src/components/admin/dashboard/GameModal.jsx

import { useState } from 'react';
import { Film, BarChart3, Save, X, Loader2 } from 'lucide-react';
import PdfStatsExtractor from '@/components/admin/PdfStatsExtractor';
import { ORANGE, STAT_FIELDS, teamToSlug } from './constants';

function StatRow({ label, sKey, stats, setStats, isString }) {
  return (
    <div className="flex items-center gap-2">
      <span className="text-xs text-slate-500 w-12 shrink-0">{label}</span>
      <input
        type={isString ? 'text' : 'number'}
        value={stats[sKey] ?? ''}
        onChange={e => setStats(prev => ({ ...prev, [sKey]: e.target.value }))}
        className="flex-1 text-xs px-2 py-1 rounded bg-slate-800 border border-slate-700 text-white focus:outline-none focus:border-orange-500"
        placeholder="—"
      />
    </div>
  );
}

export default function GameModal({ game, onSave, onClose }) {
  const [meta, setMeta] = useState({
    team1: game?.team1 || '',
    team2: game?.team2 || '',
    date: game?.date || '',
    time: game?.time || '',
    court: game?.court || '',
    game_number: game?.game_number || '',
    embed_link: game?.embed_link || '',
    duration: game?.duration || '',
  });

  const buildEmptyStats = (teamName) => {
    const slug = teamToSlug(teamName);
    const existing = game?.team_stats?.[slug] || {};
    const stats = {};
    STAT_FIELDS.forEach(f => {
      stats[f.key] = existing[f.key] !== undefined ? existing[f.key] : (f.isString ? '' : '');
    });
    return stats;
  };

  const [stats1, setStats1] = useState(() => buildEmptyStats(game?.team1 || ''));
  const [stats2, setStats2] = useState(() => buildEmptyStats(game?.team2 || ''));
  const [saving, setSaving] = useState(false);
  const [activeTab, setActiveTab] = useState('info');

  const handleSave = async () => {
    if (!meta.team1 || !meta.team2) { alert('Both team names are required'); return; }
    setSaving(true);
    const team_stats = {};
    const slug1 = teamToSlug(meta.team1);
    const slug2 = teamToSlug(meta.team2);
    const parseStats = (s) => {
      const out = {};
      STAT_FIELDS.forEach(f => {
        const v = s[f.key];
        if (v === '' || v === null || v === undefined) return;
        out[f.key] = f.isString ? String(v) : Number(v);
      });
      return out;
    };
    team_stats[slug1] = parseStats(stats1);
    team_stats[slug2] = parseStats(stats2);
    await onSave({ ...meta, team_stats });
    setSaving(false);
  };

  const Field = ({ label, field, placeholder, colSpan }) => (
    <div className={colSpan ? 'col-span-2' : ''}>
      <label className="text-xs text-slate-400 font-semibold block mb-1">{label}</label>
      <input type="text" value={meta[field]} onChange={e => setMeta(p => ({ ...p, [field]: e.target.value }))}
        placeholder={placeholder}
        className="w-full text-xs px-2 py-1.5 rounded bg-slate-800 border border-slate-700 text-white placeholder-slate-600 focus:outline-none focus:border-orange-500" />
    </div>
  );

  return (
    <div className="fixed inset-0 z-50 flex items-center justify-center bg-black/60 backdrop-blur-sm p-4">
      <div className="bg-slate-900 border border-slate-800 rounded-xl w-full max-w-2xl max-h-[90vh] flex flex-col">
        <div className="flex items-center justify-between px-5 py-4 border-b border-slate-800">
          <h2 className="text-white font-black text-base">{game?.id ? 'Edit Game' : 'New Game'}</h2>
          <button onClick={onClose} className="text-slate-500 hover:text-white"><X className="w-4 h-4" /></button>
        </div>

        <div className="flex border-b border-slate-800">
          {[{ id: 'info', label: 'Game Info', icon: Film }, { id: 'stats', label: 'Stats', icon: BarChart3 }].map(t => {
            const Icon = t.icon;
            return (
              <button key={t.id} onClick={() => setActiveTab(t.id)}
                className={`flex items-center gap-1.5 px-4 py-2.5 text-xs font-bold border-b-2 -mb-px transition-all ${activeTab === t.id ? 'text-white border-orange-500' : 'text-slate-500 border-transparent hover:text-slate-300'}`}>
                <Icon className="w-3.5 h-3.5" />{t.label}
              </button>
            );
          })}
        </div>

        <div className="overflow-y-auto flex-1 px-5 py-4">
          {activeTab === 'info' && (
            <div className="grid grid-cols-2 gap-3">
              <Field label="Team 1 *" field="team1" placeholder="e.g., Milton - GA" />
              <Field label="Team 2 *" field="team2" placeholder="e.g., Dacula - GA" />
              <Field label="Date" field="date" placeholder="e.g., June 13, 2026" />
              <Field label="Time" field="time" placeholder="e.g., 3:00 PM EST" />
              <Field label="Court" field="court" placeholder="e.g., COURT 03" />
              <Field label="Game #" field="game_number" placeholder="e.g., GM01" />
              <Field label="Duration" field="duration" placeholder="e.g., 54:17" />
              <div />
              <Field label="Embed Link (Vimeo URL)" field="embed_link" placeholder="https://player.vimeo.com/video/..." colSpan />
            </div>
          )}

          {activeTab === 'stats' && (
            <div className="space-y-6">
              <PdfStatsExtractor
                team1={meta.team1}
                team2={meta.team2}
                onStatsExtracted={(t1, t2) => {
                  const merge = (prev, extracted) => {
                    const updated = { ...prev };
                    Object.entries(extracted).forEach(([k, v]) => {
                      if (v !== undefined && v !== null && v !== '') updated[k] = String(v);
                    });
                    return updated;
                  };
                  if (Object.keys(t1).length) setStats1(prev => merge(prev, t1));
                  if (Object.keys(t2).length) setStats2(prev => merge(prev, t2));
                }}
              />
              {[{ label: meta.team1 || 'Team 1', stats: stats1, setStats: setStats1, accent: true },
                { label: meta.team2 || 'Team 2', stats: stats2, setStats: setStats2, accent: false }].map(({ label, stats, setStats, accent }) => (
                <div key={label}>
                  <h3 className="text-sm font-black text-white mb-3 flex items-center gap-2">
                    <span className={`px-2 py-0.5 rounded text-xs ${accent ? '' : 'bg-slate-700 text-slate-300'}`}
                      style={accent ? { background: 'rgba(255,106,0,0.15)', color: ORANGE } : {}}>{label}</span>
                    Stats
                  </h3>
                  <div className="grid grid-cols-2 gap-2">
                    {STAT_FIELDS.map(f => <StatRow key={f.key} label={f.label} sKey={f.key} stats={stats} setStats={setStats} isString={f.isString} />)}
                  </div>
                </div>
              ))}
            </div>
          )}
        </div>

        <div className="flex gap-2 px-5 py-4 border-t border-slate-800">
          <button onClick={handleSave} disabled={saving}
            className="flex-1 flex items-center justify-center gap-1.5 py-2.5 rounded-lg text-sm font-bold text-white disabled:opacity-50 transition-all"
            style={{ background: ORANGE }}>
            {saving ? <Loader2 className="w-4 h-4 animate-spin" /> : <Save className="w-4 h-4" />}
            Save Game
          </button>
          <button onClick={onClose} className="px-4 py-2.5 rounded-lg text-sm font-bold text-slate-400 hover:text-white bg-slate-800 hover:bg-slate-700">
            Cancel
          </button>
        </div>
      </div>
    </div>
  );
}

src/components/admin/dashboard/TeamCard.jsx

import { ChevronDown, Plus, ExternalLink } from 'lucide-react';
import { ORANGE, teamToSlug } from './constants';

export default function TeamCard({
  team, slug, isLocked, isClaimed, teamGames, teamCoaches,
  isExpanded, onToggleExpand, togglingLock, onToggleLock,
  onEditGame, onAddGame, onAssignCoach
}) {
  return (
    <div className="rounded-xl border overflow-hidden" style={{ background: '#0a0a0a', borderColor: 'rgba(255,255,255,0.07)' }}>
      {/* Header */}
      <div className="flex items-center gap-4 px-4 py-4">
        <div className="w-10 h-10 rounded-full flex items-center justify-center font-barlow font-black text-lg shrink-0"
          style={{ background: 'rgba(255,106,0,0.1)', color: ORANGE }}>
          {team[0]}
        </div>
        <div className="flex-1 min-w-0">
          <p className="font-bold text-white text-sm truncate">{team}</p>
          <p className="text-xs text-slate-600">
            {isLocked ? '🔒 Locked' : '🔓 Unlocked'} {isClaimed && '✓ Claimed'}
            {teamGames.length > 0 && ` · ${teamGames.length} games`}
            {teamCoaches.length > 0 && ` · ${teamCoaches.length} coach${teamCoaches.length !== 1 ? 'es' : ''}`}
          </p>
        </div>
        <a href={`/georgia-team/${slug}`} target="_blank" rel="noopener noreferrer"
          className="flex items-center gap-1 px-3 py-2 rounded-lg transition-all font-bold text-xs shrink-0 text-white hover:opacity-90"
          style={{ background: ORANGE }}>
          View <ExternalLink className="w-3 h-3" />
        </a>
        <button onClick={onToggleLock} disabled={togglingLock}
          className="flex items-center justify-center px-3 py-2 rounded-lg transition-all font-bold text-xs shrink-0 disabled:opacity-50"
          style={{ background: isLocked ? 'rgba(255,59,48,0.15)' : 'rgba(0,255,133,0.15)', color: isLocked ? '#FF3B30' : '#00FF85' }}>
          {isLocked ? 'Unlock' : 'Lock'}
        </button>
        <button onClick={onToggleExpand}
          className="flex items-center justify-center w-9 h-9 rounded-lg transition-all shrink-0 text-slate-500 hover:text-white"
          style={{ background: 'rgba(255,255,255,0.05)' }}>
          <ChevronDown className={`w-4 h-4 transition-transform ${isExpanded ? 'rotate-180' : ''}`} />
        </button>
      </div>

      {/* Expanded Content */}
      {isExpanded && (
        <div className="px-4 py-4 border-t space-y-4" style={{ borderColor: 'rgba(255,255,255,0.05)' }}>
          {/* Games */}
          {teamGames.length > 0 && (
            <div>
              <div className="flex items-center justify-between mb-2">
                <p className="text-xs font-bold text-slate-400 uppercase tracking-wide">Games ({teamGames.length})</p>
                <button onClick={onAddGame}
                  className="flex items-center gap-1 px-2 py-1 rounded text-xs font-bold text-white transition-all hover:opacity-90"
                  style={{ background: ORANGE }}>
                  <Plus className="w-3 h-3" /> Add
                </button>
              </div>
              <div className="space-y-2">
                {teamGames.map(game => {
                  const isTeam1 = teamToSlug(game.team1) === slug;
                  const opp = isTeam1 ? game.team2 : game.team1;
                  const oppSlug = teamToSlug(opp);
                  const myStats = game.team_stats?.[slug];
                  const oppStats = game.team_stats?.[oppSlug];
                  const hasStats = myStats?.pts !== undefined || oppStats?.pts !== undefined;
                  return (
                    <div key={game.id} className="p-2 rounded bg-white/5 flex items-center justify-between gap-2 text-xs">
                      <div className="flex-1 min-w-0">
                        <p className="text-white font-bold truncate">{game.date} - {opp}</p>
                        <p className="text-slate-600 text-[10px]">{game.time || '—'} {game.court ? `· ${game.court}` : ''}</p>
                        {hasStats && <p className="text-orange-400 text-[10px] mt-0.5">✓ Stats included</p>}
                      </div>
                      <button onClick={() => onEditGame(game)}
                        className="px-2 py-1 rounded font-bold text-white transition-all hover:opacity-90"
                        style={{ background: ORANGE }}>
                        Edit
                      </button>
                    </div>
                  );
                })}
              </div>
            </div>
          )}

          {/* Coaches */}
          <div>
            <div className="flex items-center justify-between mb-2">
              <p className="text-xs font-bold text-slate-400 uppercase tracking-wide">Coaches</p>
              <button onClick={onAssignCoach}
                className="flex items-center gap-1 px-2 py-1 rounded text-xs font-bold text-white transition-all hover:opacity-90"
                style={{ background: ORANGE }}>
                <Plus className="w-3 h-3" /> Assign
              </button>
            </div>
            {teamCoaches.length === 0 ? (
              <p className="text-xs text-slate-600 py-2">No coaches assigned</p>
            ) : (
              <div className="space-y-1">
                {teamCoaches.map(coach => (
                  <div key={coach.id} className="p-2 rounded text-xs bg-white/5">
                    <p className="text-white font-semibold">{coach.coach_name}</p>
                    <p className="text-slate-500 text-[10px]">{coach.email}</p>
                  </div>
                ))}
              </div>
            )}
          </div>
        </div>
      )}
    </div>
  );
}

Portfolio Builder

src/pages/PlayerPortfolio.jsx

import { motion } from 'framer-motion';
import { User, Trophy, Film, BarChart2, Star, Users, Building2, ArrowRight, CheckCircle, Crown, Zap, Home } from 'lucide-react';
import { Link } from 'react-router-dom';
import Navbar from '../components/landing/Navbar';
import Footer from '../components/landing/Footer';
import DemoRequestButton from '../components/landing/DemoRequestButton';
import AIOButton from '../components/landing/AIOButton';
import PortfolioContactForm from '../components/portfolio/PortfolioContactForm';

const included = [
  { icon: Film, title: 'Game Footage & Highlights', desc: 'Curated, edited video highlights pulled directly from live streams — showcasing your best moments with professional-grade production.' },
  { icon: BarChart2, title: 'Full Stats & Analytics', desc: 'Comprehensive game-by-game stats, career averages, and performance trends that give recruiters and scouts an instant read on your game.' },
  { icon: Star, title: 'Standout Play Clips', desc: 'Signature plays tagged and timestamped so anyone viewing your portfolio can jump directly to the moments that define your game.' },
  { icon: User, title: 'Athlete Bio & Profile', desc: 'Personal background, position, contact info, academic standing, and accolades — everything a recruiter needs in one polished document.' },
  { icon: Trophy, title: 'Tournament & League History', desc: "A full record of competitions, team affiliations, and performance across every organization and event you've been part of." },
  { icon: Zap, title: 'Stat-By-Video Integration', desc: 'Every stat is linked to the exact video moment it occurred — click a number and instantly watch the play. No other platform does this.' },
];

const whoWeWork = [
  { icon: User, title: 'Individual Players', desc: 'We work 1-on-1 with athletes to build portfolios that tell their unique story and open doors with scouts, coaches, and colleges.' },
  { icon: Users, title: 'Teams & Clubs', desc: "We partner with entire rosters and club programs to document every player's journey while elevating the team's overall brand and professionalism." },
  { icon: Building2, title: 'Tournament Organizations', desc: 'We embed into tournament operations to stream, record, and build portfolios for every participating athlete — turning your event into a full-scale exposure platform.' },
];

export default function PlayerPortfolio() {
  return (
    <div className="min-h-screen bg-background text-foreground overflow-x-hidden">
      <Navbar />

      {/* Hero */}
      <section className="relative pt-32 pb-20 overflow-hidden hero-grid">
        <div className="absolute top-1/3 left-1/4 w-96 h-96 bg-primary/10 rounded-full blur-3xl pointer-events-none" />
        <div className="absolute bottom-0 right-1/4 w-72 h-72 bg-accent/8 rounded-full blur-3xl pointer-events-none" />

        <div className="relative max-w-7xl mx-auto px-6">
          <motion.div initial={{ opacity: 0, y: 32 }} animate={{ opacity: 1, y: 0 }} transition={{ duration: 0.7 }} className="max-w-4xl">
            <div className="inline-flex items-center gap-2 px-3 py-1.5 rounded-full border border-primary/30 bg-primary/10 mb-6">
              <Crown className="w-3.5 h-3.5 text-primary" />
              <span className="text-xs font-medium text-primary tracking-wide uppercase">Market Leader · Revolutionizing Sports</span>
            </div>

            <h1 className="font-barlow font-900 text-6xl sm:text-7xl lg:text-8xl leading-none tracking-tight mb-6">
              PLAYER
              <br />
              <span className="text-gradient">"BLUEPRINT"</span>
              <br />
              PORTFOLIOS
            </h1>

            <p className="text-muted-foreground text-xl leading-relaxed mb-4 max-w-2xl">
              TeamStream is the <strong className="text-foreground">undisputed leader</strong> in the market — the only platform that seamlessly takes athletes from the live game to a professional digital portfolio in one unified workflow. We're not improving the old system. <strong className="text-foreground">We're replacing it.</strong>
            </p>
            <p className="text-muted-foreground text-lg leading-relaxed mb-10 max-w-2xl">
              A Blueprint Portfolio is more than a highlight reel. It's a living, data-rich athlete profile that gives every player — regardless of level — the professional presentation that was once reserved for elite recruits.
            </p>

            <div className="flex flex-wrap gap-3">
              <Link to="/Home" className="inline-flex items-center justify-center gap-2 px-6 py-3 rounded-xl border border-border bg-secondary/60 text-foreground font-semibold text-sm hover:bg-secondary transition-all">
                <Home className="w-4 h-4" />
                Home
              </Link>
              <DemoRequestButton label="Request a Demo" />
              <AIOButton />
              <a
                href="https://www.fxpo.pro/stoffel"
                target="_blank"
                rel="noopener noreferrer"
                className="inline-flex items-center justify-center gap-2 px-6 py-3 rounded-xl border border-orange-400/40 bg-orange-400/10 text-orange-400 font-semibold text-sm hover:bg-orange-400/20 transition-all"
              >
                <User className="w-4 h-4" />
                View Sample Portfolio
              </a>
            </div>
          </motion.div>
        </div>
      </section>

      {/* Why Portfolios Are Crucial */}
      <section className="py-20 relative">
        <div className="absolute inset-0 bg-gradient-to-b from-transparent via-primary/4 to-transparent pointer-events-none" />
        <div className="relative max-w-7xl mx-auto px-6">
          <motion.div initial={{ opacity: 0, y: 24 }} whileInView={{ opacity: 1, y: 0 }} viewport={{ once: true }} className="grid lg:grid-cols-2 gap-16 items-center">
            <div>
              <h2 className="font-barlow font-800 text-5xl text-white mb-6 leading-tight">
                WHY A PORTFOLIO IS<br />
                <span className="text-gradient">NON-NEGOTIABLE</span>
              </h2>
              <div className="space-y-5 text-muted-foreground leading-relaxed">
                <p>
                  In today's sports landscape, talent alone doesn't get you seen. <strong className="text-foreground">Visibility does.</strong> Scouts, coaches, and college programs evaluate hundreds of athletes — the ones with a polished, data-backed portfolio don't just stand out, they're the only ones who get a second look.
                </p>
                <p>
                  A Blueprint Portfolio turns raw performance into undeniable proof. It's the difference between telling a recruiter you're good and <strong className="text-foreground">showing them exactly why, with video evidence and numbers to back every claim.</strong>
                </p>
                <p>
                  For teams and tournaments, portfolios elevate the entire operation. When every athlete has professional documentation, the tournament itself gains credibility, attracts higher-level talent, and builds a reputation that compounds year over year.
                </p>
                <p className="text-foreground font-medium">
                  TeamStream has streamlined the game-to-portfolio pipeline so completely that what once took weeks of manual editing now happens in real time — from live stream to shareable profile, automatically.
                </p>
              </div>
            </div>

            <div className="rounded-2xl border border-primary/20 card-glass p-8 glow-purple">
              <h3 className="font-barlow font-800 text-2xl text-white mb-6">THE IMPACT BY THE NUMBERS</h3>
              <div className="grid grid-cols-2 gap-5">
                {[
                  { value: '3×', label: 'More recruiter engagement with video portfolios vs. text resumes' },
                  { value: '#1', label: 'Platform in the market for game-to-portfolio automation' },
                  { value: '100%', label: 'Of top scouts prefer data-backed profiles over highlight reels alone' },
                  { value: '24hrs', label: 'From live game to fully published portfolio — industry fastest' },
                ].map((s) => (
                  <div key={s.label} className="rounded-xl bg-secondary/60 p-4">
                    <div className="font-barlow font-900 text-3xl text-gradient mb-1">{s.value}</div>
                    <div className="text-xs text-muted-foreground leading-snug">{s.label}</div>
                  </div>
                ))}
              </div>
            </div>
          </motion.div>
        </div>
      </section>

      {/* What's Included */}
      <section className="py-20">
        <div className="max-w-7xl mx-auto px-6">
          <motion.div initial={{ opacity: 0, y: 24 }} whileInView={{ opacity: 1, y: 0 }} viewport={{ once: true }} className="text-center mb-14">
            <h2 className="font-barlow font-800 text-5xl text-white mb-4">
              WHAT'S INSIDE A<br />
              <span className="text-gradient">BLUEPRINT PORTFOLIO</span>
            </h2>
            <p className="text-muted-foreground max-w-xl mx-auto">Every portfolio is built to impress — professionally crafted, data-rich, and video-backed from the first whistle to the final buzzer.</p>
          </motion.div>

          <div className="grid sm:grid-cols-2 lg:grid-cols-3 gap-5">
            {included.map((item, i) => {
              const Icon = item.icon;
              return (
                <motion.div
                  key={item.title}
                  initial={{ opacity: 0, y: 20 }}
                  whileInView={{ opacity: 1, y: 0 }}
                  viewport={{ once: true }}
                  transition={{ delay: i * 0.08 }}
                  className="rounded-2xl border border-primary/15 card-glass p-6 hover:border-primary/40 hover:scale-[1.02] transition-all duration-300"
                >
                  <div className="w-12 h-12 rounded-xl bg-primary/10 border border-primary/20 flex items-center justify-center mb-4">
                    <Icon className="w-5 h-5 text-primary" />
                  </div>
                  <h3 className="font-semibold text-foreground mb-2">{item.title}</h3>
                  <p className="text-sm text-muted-foreground leading-relaxed">{item.desc}</p>
                </motion.div>
              );
            })}
          </div>
        </div>
      </section>

      {/* Who We Work With */}
      <section className="py-20 relative">
        <div className="absolute inset-0 bg-gradient-to-b from-transparent via-accent/3 to-transparent pointer-events-none" />
        <div className="relative max-w-7xl mx-auto px-6">
          <motion.div initial={{ opacity: 0, y: 24 }} whileInView={{ opacity: 1, y: 0 }} viewport={{ once: true }} className="text-center mb-14">
            <h2 className="font-barlow font-800 text-5xl text-white mb-4">
              WE WORK WITH<br />
              <span className="text-gradient">EVERYONE IN THE GAME</span>
            </h2>
            <p className="text-muted-foreground max-w-2xl mx-auto">
              Whether you're a single athlete chasing a scholarship, a coach building a program, or a tournament director looking to add elite-level value — TeamStream is your partner. We don't just elevate individual players. <strong className="text-white">We elevate the entire ecosystem.</strong>
            </p>
          </motion.div>

          <div className="grid md:grid-cols-3 gap-6">
            {whoWeWork.map((w, i) => {
              const Icon = w.icon;
              return (
                <motion.div
                  key={w.title}
                  initial={{ opacity: 0, y: 24 }}
                  whileInView={{ opacity: 1, y: 0 }}
                  viewport={{ once: true }}
                  transition={{ delay: i * 0.1 }}
                  className="rounded-2xl border border-accent/20 bg-gradient-to-br from-accent/10 to-accent/5 p-8 text-center"
                >
                  <div className="w-14 h-14 rounded-2xl bg-accent/20 border border-accent/30 flex items-center justify-center mx-auto mb-5">
                    <Icon className="w-6 h-6 text-accent" />
                  </div>
                  <h3 className="font-barlow font-800 text-xl text-white mb-3">{w.title}</h3>
                  <p className="text-sm text-muted-foreground leading-relaxed">{w.desc}</p>
                </motion.div>
              );
            })}
          </div>
        </div>
      </section>

      {/* Professionalism Banner */}
      <section className="py-16 mx-6 mb-12 rounded-2xl border border-orange-400/20 bg-gradient-to-br from-orange-400/10 to-orange-400/5 max-w-7xl lg:mx-auto">
        <div className="px-8 md:px-16 text-center">
          <motion.div initial={{ opacity: 0, y: 24 }} whileInView={{ opacity: 1, y: 0 }} viewport={{ once: true }}>
            <Crown className="w-10 h-10 text-orange-400 mx-auto mb-4" />
            <h2 className="font-barlow font-900 text-4xl md:text-5xl text-white mb-5 leading-tight">
              WE ELEVATE THE<br />
              <span className="text-gradient-orange">PROFESSIONALISM OF EVERY</span><br />
              TOURNAMENT & TEAM WE TOUCH
            </h2>
            <p className="text-muted-foreground max-w-3xl mx-auto text-lg leading-relaxed mb-8">
              When TeamStream partners with your tournament or club, the entire event is upgraded. Professional live streaming, automated player documentation, and instant portfolio publishing signal to players, parents, and scouts that this is a <strong className="text-white">premium, serious operation</strong> — not just another weekend tournament.
            </p>
            <div className="flex flex-wrap justify-center gap-3">
              <DemoRequestButton label="Partner With Us" />
              <AIOButton />
            </div>
          </motion.div>
        </div>
      </section>

      {/* CTA */}
      <section className="py-20 max-w-7xl mx-auto px-6 text-center">
        <motion.div initial={{ opacity: 0, y: 24 }} whileInView={{ opacity: 1, y: 0 }} viewport={{ once: true }}>
          <h2 className="font-barlow font-800 text-5xl text-white mb-4">READY TO BUILD YOUR BLUEPRINT?</h2>
          <p className="text-muted-foreground mb-8 max-w-xl mx-auto">Join the athletes, teams, and tournaments already using TeamStream's Blueprint Portfolio system — the future of athlete documentation.</p>
          <div className="flex flex-wrap justify-center gap-3">
            <DemoRequestButton label="Request a Demo" />
            <AIOButton />
            <a href="https://www.fxpo.pro/stoffel" target="_blank" rel="noopener noreferrer"
              className="inline-flex items-center gap-2 px-6 py-3 rounded-xl border border-orange-400/40 bg-orange-400/10 text-orange-400 font-semibold text-sm hover:bg-orange-400/20 transition-all">
              <ArrowRight className="w-4 h-4" />
              View Sample Portfolio
            </a>
          </div>
        </motion.div>
      </section>

      <PortfolioContactForm />

      <Footer />
    </div>
  );
}

src/components/portfolio/BulkUpload.jsx

import { useState, useRef } from 'react';
import { base44 } from '@/api/base44Client';
import { FileText, BarChart2, Image, Users, FileSpreadsheet, Upload, CheckCircle, AlertCircle, Loader2, X } from 'lucide-react';
import { toPlayerId, computeAverages, parseHeightToInches } from '@/lib/portfolioHelpers';

const MODES = [
  { key: 'portfolio', icon: FileText,        title: 'Player Portfolio Creator',       desc: 'Upload team boxscores. Stats are added to existing portfolios and you can create new ones for players not yet in the system.' },
  { key: 'roster',   icon: FileSpreadsheet,  title: 'Roster from Spreadsheet',        desc: 'Import player info from a spreadsheet (CSV/Excel). Creates base player profiles with recruiting info.' },
  { key: 'report',   icon: BarChart2,        title: 'Comparative Reports',            desc: 'Upload multiple game analysis PDFs/images. Each file generates a separate dated report.' },
  { key: 'leaders',  icon: BarChart2,        title: 'Monthly Leaders Post',           desc: 'Upload multiple box score sheets. We calculate cumulative stats and generate a leaderboard graphic.' },
  { key: 'recap',    icon: Image,            title: 'Game Recap Post',                desc: 'Upload a box score for a single game. Generates a 1080×1080 social media recap graphic.' },
];

export default function BulkUpload({ onPlayersImported }) {
  const [mode, setMode]       = useState('portfolio');
  const [files, setFiles]     = useState([]);
  const [dragging, setDragging] = useState(false);
  const [processing, setProcessing] = useState(false);
  const [results, setResults] = useState(null);
  const [rosterPreview, setRosterPreview] = useState(null);
  const [rosterFile, setRosterFile] = useState(null);
  const [parsingRoster, setParsingRoster] = useState(false);
  const inputRef = useRef();
  const rosterInputRef = useRef();

  const handleDrop = (e) => {
    e.preventDefault(); setDragging(false);
    const dropped = Array.from(e.dataTransfer.files);
    setFiles(prev => [...prev, ...dropped]);
  };

  const handleFileSelect = (e) => {
    setFiles(prev => [...prev, ...Array.from(e.target.files)]);
    e.target.value = '';
  };

  // ── PORTFOLIO CREATOR: AI boxscore extraction ──
  const handlePortfolioUpload = async () => {
    if (!files.length) return;
    setProcessing(true);
    setResults(null);
    const log = [];

    for (const file of files) {
      try {
        const { file_url } = await base44.integrations.Core.UploadFile({ file });
        const result = await base44.integrations.Core.InvokeLLM({
          prompt: `This is a basketball team boxscore. Extract EACH individual player's stats. 
Return an array of player objects.
For each player, extract: player_name (full name), ppg (points), rpg (rebounds), apg (assists), spg (steals), bpg (blocks), three_made, three_att, two_made, two_att, games_played (default 1), game_date (YYYY-MM-DD or null).
Ignore header/footer/team total rows. All numeric values as numbers, null if not found.`,
          file_urls: [file_url],
          model: 'gemini_3_flash',
          response_json_schema: {
            type: 'object',
            properties: {
              players: {
                type: 'array',
                items: {
                  type: 'object',
                  properties: {
                    player_name: { type: 'string' },
                    ppg: { type: 'number' }, rpg: { type: 'number' }, apg: { type: 'number' },
                    spg: { type: 'number' }, bpg: { type: 'number' },
                    three_made: { type: 'number' }, three_att: { type: 'number' },
                    two_made: { type: 'number' }, two_att: { type: 'number' },
                    games_played: { type: 'number' }, game_date: { type: 'string' },
                  },
                },
              },
            },
          },
        });

        for (const p of result.players || []) {
          if (!p.player_name || p.player_name.length < 3) continue;
          const slug = toPlayerId(p.player_name);
          const newBoxscore = {
            date: p.game_date || '',
            filename: file.name, file_url,
            ppg: p.ppg, rpg: p.rpg, apg: p.apg, spg: p.spg, bpg: p.bpg,
            three_made: p.three_made || 0, three_att: p.three_att || 0,
            two_made: p.two_made || 0, two_att: p.two_att || 0,
            games_played: p.games_played || 1, excluded_from_averages: false,
          };

          const existing = await base44.entities.Player.filter({ portfolio_url_slug: slug });
          if (existing.length) {
            const merged = [...(existing[0].boxscores || []), newBoxscore];
            await base44.entities.Player.update(existing[0].id, { boxscores: merged, ...computeAverages(merged) });
            log.push({ name: p.player_name, action: 'updated' });
          } else {
            await base44.entities.Player.create({
              full_name: p.player_name, portfolio_url_slug: slug,
              boxscores: [newBoxscore], is_published: false,
              ...computeAverages([newBoxscore]),
            });
            log.push({ name: p.player_name, action: 'created' });
          }
        }
      } catch (e) {
        log.push({ name: file.name, action: 'error', error: e.message });
      }
    }

    setResults(log);
    setProcessing(false);
    setFiles([]);
    if (onPlayersImported) onPlayersImported();
  };

  // ── ROSTER IMPORT: parse spreadsheet ──
  const handleRosterFileSelect = async (e) => {
    const file = e.target.files[0];
    if (!file) return;
    setRosterFile(file);
    setParsingRoster(true);
    setRosterPreview(null);

    try {
      const { file_url } = await base44.integrations.Core.UploadFile({ file });
      const result = await base44.integrations.Core.InvokeLLM({
        prompt: `This is a basketball player registration spreadsheet. Extract ALL player rows.
For each player return:
- first_name, last_name
- high_school (school name)
- jersey_number (as string)
- email (private)
- phone (private)  
- date_of_birth (YYYY-MM-DD, year starts with 200x)
- home_address (street address, private)
- address_city
- address_state
- address_zip (private)
- position (PG/SG/SF/PF/C/G/F)
- social_twitter (handle without @)
- social_instagram (handle without @)
- height_str (original string like "6'1" or "6 1")
- weight_lbs (number)
- graduation_year (number like 2027)
- act_score (number or null, private)
- sat_score (number or null, private)
- gpa (number or null)
- aau_team (string or null)

Return null for missing values. All values from the spreadsheet, no fabrication.`,
        file_urls: [file_url],
        model: 'gemini_3_flash',
        response_json_schema: {
          type: 'object',
          properties: {
            players: {
              type: 'array',
              items: {
                type: 'object',
                properties: {
                  first_name: { type: 'string' }, last_name: { type: 'string' },
                  high_school: { type: 'string' }, jersey_number: { type: 'string' },
                  email: { type: 'string' }, phone: { type: 'string' },
                  date_of_birth: { type: 'string' }, home_address: { type: 'string' },
                  address_city: { type: 'string' }, address_state: { type: 'string' }, address_zip: { type: 'string' },
                  position: { type: 'string' }, social_twitter: { type: 'string' }, social_instagram: { type: 'string' },
                  height_str: { type: 'string' }, weight_lbs: { type: 'number' },
                  graduation_year: { type: 'number' }, act_score: { type: 'number' },
                  sat_score: { type: 'number' }, gpa: { type: 'number' }, aau_team: { type: 'string' },
                },
              },
            },
          },
        },
      });
      setRosterPreview(result.players || []);
    } catch (e) {
      alert('Error parsing file: ' + e.message);
    }
    setParsingRoster(false);
  };

  const handleRosterImport = async () => {
    if (!rosterPreview?.length) return;
    setProcessing(true);
    setResults(null);
    const log = [];

    for (const p of rosterPreview) {
      const full_name = `${p.first_name || ''} ${p.last_name || ''}`.trim();
      if (!full_name || full_name.length < 2) continue;

      const slug = toPlayerId(full_name);
      const hometown = p.address_city ? `${p.address_city}${p.address_state ? `, ${p.address_state}` : ''}` : null;
      const height_inches = parseHeightToInches(p.height_str);

      const payload = {
        full_name, first_name: p.first_name, last_name: p.last_name,
        portfolio_url_slug: slug,
        high_school: p.high_school,
        jersey_number: p.jersey_number,
        email: p.email, phone: p.phone,
        date_of_birth: p.date_of_birth,
        home_address: p.home_address, address_city: p.address_city,
        address_state: p.address_state, address_zip: p.address_zip,
        hometown,
        position: p.position,
        social_twitter: p.social_twitter, social_instagram: p.social_instagram,
        height_inches, weight_lbs: p.weight_lbs,
        graduation_year: p.graduation_year, act_score: p.act_score,
        sat_score: p.sat_score, gpa: p.gpa, aau_team: p.aau_team,
        boxscores: [], vimeo_links: [], other_bio_stats_urls: [],
        is_published: false,
      };

      const existing = await base44.entities.Player.filter({ portfolio_url_slug: slug });
      if (existing.length) {
        await base44.entities.Player.update(existing[0].id, payload);
        log.push({ name: full_name, action: 'updated' });
      } else {
        await base44.entities.Player.create(payload);
        log.push({ name: full_name, action: 'created' });
      }
    }

    setResults(log);
    setProcessing(false);
    setRosterPreview(null);
    setRosterFile(null);
    if (onPlayersImported) onPlayersImported();
  };

  const currentMode = MODES.find(m => m.key === mode);

  return (
    <div className="space-y-6">
      {/* Mode cards */}
      <div className="grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-5 gap-3">
        {MODES.map(m => {
          const Icon = m.icon;
          return (
            <button key={m.key} onClick={() => { setMode(m.key); setResults(null); setFiles([]); setRosterPreview(null); }}
              className={`p-4 rounded-xl border text-left transition-all ${
                mode === m.key
                  ? 'border-[#4A9EFF]/60 bg-[#1c2a3a]'
                  : 'border-white/10 bg-[#1a1a1a] hover:border-white/20'
              }`}>
              <Icon className={`w-5 h-5 mb-2 ${mode === m.key ? 'text-[#4A9EFF]' : 'text-gray-500'}`} />
              <p className={`text-sm font-semibold ${mode === m.key ? 'text-white' : 'text-gray-300'}`}>{m.title}</p>
              <p className="text-xs text-gray-500 mt-1 leading-relaxed">{m.desc}</p>
            </button>
          );
        })}
      </div>

      {/* Panel */}
      <div className="bg-[#1a1a1a] rounded-xl border border-white/10 p-6">
        <div className="flex items-center gap-3 mb-1">
          {(() => { const Icon = currentMode.icon; return <Icon className="w-5 h-5 text-[#4A9EFF]" />; })()}
          <h3 className="text-white font-semibold">{currentMode.title}</h3>
        </div>
        <p className="text-gray-500 text-sm mb-5">{currentMode.desc}</p>

        {/* ── PORTFOLIO MODE ── */}
        {mode === 'portfolio' && (
          <>
            <div
              onDragOver={e => { e.preventDefault(); setDragging(true); }}
              onDragLeave={() => setDragging(false)}
              onDrop={handleDrop}
              onClick={() => inputRef.current?.click()}
              className={`border-2 border-dashed rounded-xl p-12 text-center cursor-pointer transition-all ${
                dragging ? 'border-[#4A9EFF] bg-[#1c2a3a]' : 'border-white/15 hover:border-white/25'
              }`}>
              <Upload className="w-8 h-8 text-gray-500 mx-auto mb-3" />
              <p className="text-white font-semibold">Drop team boxscores (one per game)</p>
              <p className="text-gray-500 text-sm mt-1">PDF, PNG, JPG supported</p>
              <input ref={inputRef} type="file" multiple accept=".pdf,.png,.jpg,.jpeg" className="hidden" onChange={handleFileSelect} />
            </div>

            {files.length > 0 && (
              <div className="mt-4 space-y-2">
                {files.map((f, i) => (
                  <div key={i} className="flex items-center gap-3 bg-[#111] rounded-lg px-3 py-2">
                    <FileText className="w-4 h-4 text-gray-500 shrink-0" />
                    <span className="text-sm text-gray-300 flex-1 truncate">{f.name}</span>
                    <button onClick={() => setFiles(prev => prev.filter((_, idx) => idx !== i))}
                      className="text-gray-600 hover:text-gray-400"><X className="w-3.5 h-3.5" /></button>
                  </div>
                ))}
                <button onClick={handlePortfolioUpload} disabled={processing}
                  className="mt-3 w-full py-3 rounded-xl bg-[#FF6B00] text-white font-bold hover:bg-orange-500 disabled:opacity-50 transition-all flex items-center justify-center gap-2">
                  {processing ? <><Loader2 className="w-4 h-4 animate-spin" /> Processing…</> : `Process ${files.length} File${files.length > 1 ? 's' : ''}`}
                </button>
              </div>
            )}
          </>
        )}

        {/* ── ROSTER MODE ── */}
        {mode === 'roster' && (
          <>
            {!rosterPreview && !parsingRoster && (
              <>
                <div className="bg-[#111] rounded-xl p-4 mb-4 text-xs text-gray-500 space-y-1">
                  <p className="text-gray-300 font-semibold mb-2">Expected spreadsheet columns:</p>
                  <p><span className="text-white">Public:</span> High School, Jersey #, First Name, Last Name, Position, Height, Weight, Twitter, Instagram, Graduation Year, City</p>
                  <p><span className="text-yellow-500">Private:</span> Email, Phone, Birthday, Address, ZIP, ACT, SAT, GPA</p>
                </div>
                <div
                  onClick={() => rosterInputRef.current?.click()}
                  className="border-2 border-dashed border-white/15 hover:border-white/25 rounded-xl p-12 text-center cursor-pointer transition-all">
                  <FileSpreadsheet className="w-8 h-8 text-gray-500 mx-auto mb-3" />
                  <p className="text-white font-semibold">Upload Roster Spreadsheet</p>
                  <p className="text-gray-500 text-sm mt-1">CSV or Excel (.csv, .xlsx, .xls)</p>
                  <input ref={rosterInputRef} type="file" accept=".csv,.xlsx,.xls" className="hidden" onChange={handleRosterFileSelect} />
                </div>
              </>
            )}

            {parsingRoster && (
              <div className="flex flex-col items-center py-12 gap-3">
                <Loader2 className="w-8 h-8 animate-spin text-[#4A9EFF]" />
                <p className="text-gray-400">Parsing spreadsheet with AI…</p>
              </div>
            )}

            {rosterPreview && !parsingRoster && (
              <>
                <div className="flex items-center justify-between mb-3">
                  <p className="text-white font-semibold">{rosterPreview.length} players detected</p>
                  <button onClick={() => { setRosterPreview(null); setRosterFile(null); }}
                    className="text-gray-500 hover:text-gray-300 text-sm">Clear</button>
                </div>
                <div className="max-h-80 overflow-y-auto space-y-2 mb-4">
                  {rosterPreview.map((p, i) => (
                    <div key={i} className="bg-[#111] rounded-lg px-3 py-2 text-sm">
                      <div className="flex items-center gap-2 flex-wrap">
                        <span className="text-white font-semibold">{p.first_name} {p.last_name}</span>
                        {p.jersey_number && <span className="text-[#FF6B00] text-xs">#{p.jersey_number}</span>}
                        {p.position && <span className="text-[#4A9EFF] text-xs">{p.position}</span>}
                        {p.high_school && <span className="text-gray-500 text-xs">{p.high_school}</span>}
                        {p.graduation_year && <span className="text-gray-500 text-xs">Class of {p.graduation_year}</span>}
                      </div>
                      <div className="flex gap-3 mt-1 text-xs text-gray-600">
                        {p.height_str && <span>📏 {p.height_str}</span>}
                        {p.weight_lbs && <span>⚖️ {p.weight_lbs}lbs</span>}
                        {p.address_city && <span>📍 {p.address_city}{p.address_state ? `, ${p.address_state}` : ''}</span>}
                        {p.gpa && <span>📚 {p.gpa} GPA</span>}
                      </div>
                    </div>
                  ))}
                </div>
                <button onClick={handleRosterImport} disabled={processing}
                  className="w-full py-3 rounded-xl bg-[#FF6B00] text-white font-bold hover:bg-orange-500 disabled:opacity-50 transition-all flex items-center justify-center gap-2">
                  {processing ? <><Loader2 className="w-4 h-4 animate-spin" /> Importing…</> : `Create ${rosterPreview.length} Player Profiles`}
                </button>
              </>
            )}
          </>
        )}

        {/* ── OTHER MODES ── */}
        {!['portfolio','roster'].includes(mode) && (
          <div className="border-2 border-dashed border-white/10 rounded-xl p-12 text-center text-gray-600">
            <Upload className="w-8 h-8 mx-auto mb-3" />
            <p className="font-semibold text-gray-500">Coming Soon</p>
            <p className="text-sm mt-1">This feature is in development</p>
          </div>
        )}

        {/* Results */}
        {results && (
          <div className="mt-5 bg-[#111] rounded-xl p-4">
            <p className="text-white font-semibold mb-3">
              ✅ Done — {results.filter(r => r.action === 'created').length} created, {results.filter(r => r.action === 'updated').length} updated
              {results.filter(r => r.action === 'error').length > 0 && `, ${results.filter(r => r.action === 'error').length} errors`}
            </p>
            <div className="max-h-48 overflow-y-auto space-y-1">
              {results.map((r, i) => (
                <div key={i} className="flex items-center gap-2 text-sm">
                  {r.action === 'error'
                    ? <AlertCircle className="w-3.5 h-3.5 text-red-400 shrink-0" />
                    : <CheckCircle className="w-3.5 h-3.5 text-green-400 shrink-0" />}
                  <span className={r.action === 'error' ? 'text-red-400' : r.action === 'created' ? 'text-green-400' : 'text-gray-400'}>
                    {r.name}
                  </span>
                  <span className="text-gray-600 text-xs">{r.action}</span>
                </div>
              ))}
            </div>
          </div>
        )}
      </div>
    </div>
  );
}

src/components/portfolio/FanVoting.jsx

import React, { useState, useEffect, useCallback } from 'react';
import { base44 } from '@/api/base44Client';
import { CheckCircle2, Users } from 'lucide-react';

// Handle both old (string) and new (object) formats
function normalizeOffers(offers) {
  return (offers || []).map(o => {
    if (typeof o === 'string') return { school_name: o, division_level: null, logo_url: null };
    return o;
  }).filter(o => o.school_name);
}

// Lightweight browser fingerprint
function getFingerprint() {
  const nav = navigator;
  const screen = window.screen;
  const data = [
    nav.userAgent,
    nav.language,
    nav.platform,
    screen.width + 'x' + screen.height,
    screen.colorDepth,
    new Date().getTimezoneOffset(),
  ].join('|');
  let hash = 0;
  for (let i = 0; i < data.length; i++) {
    hash = ((hash << 5) - hash) + data.charCodeAt(i);
    hash = hash & hash;
  }
  return 'fp_' + Math.abs(hash).toString(36);
}

export default function FanVoting({ player }) {
  const offers = normalizeOffers(player.offers_received);
  const [votes, setVotes] = useState([]);
  const [loading, setLoading] = useState(true);
  const [voting, setVoting] = useState(false);
  const [userVote, setUserVote] = useState(null);
  const [error, setError] = useState(null);
  const [needsAuth, setNeedsAuth] = useState(false);

  const loadVotes = useCallback(async () => {
    try {
      const res = await base44.functions.invoke('getPlayerVotes', { player_id: player.id });
      setVotes(res.data?.votes || []);
      if (res.data?.userVote) {
        setUserVote(res.data.userVote.voted_school);
      }
    } catch (e) {
      // Function may not exist yet — fail silently
      setVotes([]);
    }
    setLoading(false);
  }, [player.id]);

  useEffect(() => {
    loadVotes();
    // Subscribe to real-time vote updates
    const unsubscribe = base44.entities.PlayerVote.subscribe((event) => {
      if (event?.data?.player_id === player.id) {
        loadVotes();
      }
    });
    return unsubscribe;
  }, [player.id, loadVotes]);

  const handleVote = async (schoolName) => {
    setError(null);
    setNeedsAuth(false);

    let isAuth;
    try {
      isAuth = await base44.auth.isAuthenticated();
    } catch {
      isAuth = false;
    }

    if (!isAuth) {
      setNeedsAuth(true);
      setTimeout(() => base44.auth.redirectToLogin(window.location.pathname), 1500);
      return;
    }

    setVoting(true);
    try {
      const fp = getFingerprint();
      const res = await base44.functions.invoke('castVote', {
        player_id: player.id,
        voted_school: schoolName,
        device_fingerprint: fp,
      });

      if (res.data?.success) {
        setUserVote(schoolName);
        loadVotes();
      }
    } catch (err) {
      const msg = err.response?.data?.error || err.message || 'Vote failed';
      if (msg.includes('already voted')) {
        setUserVote('already');
      }
      setError(msg);
    }
    setVoting(false);
  };

  if (loading || offers.length === 0) return null;

  const totalVotes = votes.length;
  const voteCounts = {};
  votes.forEach(v => {
    voteCounts[v.voted_school] = (voteCounts[v.voted_school] || 0) + 1;
  });

  // Sort offers by vote count (highest first)
  const sortedOffers = [...offers].sort((a, b) =>
    (voteCounts[b.school_name] || 0) - (voteCounts[a.school_name] || 0)
  );

  const hasVoted = userVote !== null;

  return (
    <div className="rounded-2xl overflow-hidden border border-white/[0.08] p-6 bg-[#0a0a0a]">
      <div className="flex items-center gap-2 mb-1">
        <span className="text-[#FF6B00] text-[11px] font-bold tracking-[0.2em] uppercase">🔥 FAN VOTE</span>
        <span className="ml-auto flex items-center gap-1 text-[10px] text-gray-600 uppercase tracking-widest">
          <Users className="w-3 h-3" />
          {totalVotes} vote{totalVotes !== 1 ? 's' : ''}
        </span>
      </div>
      <p className="text-gray-400 text-sm mb-5">
        Where should {player.full_name?.split(' ')[0] || 'this player'} go? Cast your vote!
      </p>

      {needsAuth && (
        <div className="mb-4 p-3 rounded-lg bg-[#FF6B00]/10 border border-[#FF6B00]/20 text-[#FF6B00] text-xs">
          🔐 Please log in or create an account to vote. Redirecting...
        </div>
      )}
      {error && !needsAuth && (
        <div className="mb-4 p-3 rounded-lg bg-red-500/10 border border-red-500/20 text-red-400 text-xs">
          {error}
        </div>
      )}

      <div className="space-y-2.5">
        {sortedOffers.map((offer, i) => {
          const count = voteCounts[offer.school_name] || 0;
          const pct = totalVotes > 0 ? (count / totalVotes) * 100 : 0;
          const isLeader = i === 0 && count > 0;

          if (hasVoted) {
            // Results view with progress bar
            return (
              <div key={i} className="relative rounded-lg overflow-hidden border border-white/[0.06] bg-[#111]"
                style={{ minHeight: 48 }}>
                <div className="absolute inset-y-0 left-0 transition-all duration-700 ease-out"
                  style={{
                    width: `${pct}%`,
                    background: isLeader
                      ? 'linear-gradient(90deg, rgba(255,107,0,0.25), rgba(255,107,0,0.06))'
                      : 'linear-gradient(90deg, rgba(255,255,255,0.06), rgba(255,255,255,0.02))',
                  }} />
                <div className="relative flex items-center justify-between px-4 py-3">
                  <div className="flex items-center gap-2">
                    {offer.logo_url && <img src={offer.logo_url} alt="" className="w-[50px] h-[50px] object-contain" />}
                    <span className="text-sm text-white font-semibold">{offer.school_name}</span>
                    {isLeader && <span className="text-xs">👑</span>}
                  </div>
                  <span className="text-sm font-bold text-gray-400 tabular-nums">{pct.toFixed(0)}%</span>
                </div>
              </div>
            );
          }

          // Vote button view
          return (
            <button key={i} onClick={() => handleVote(offer.school_name)} disabled={voting}
              className="w-full flex items-center justify-between px-4 py-3 rounded-lg border border-white/[0.08] bg-[#111] hover:border-[#FF6B00]/40 hover:bg-[#FF6B00]/5 transition-all disabled:opacity-50">
              <div className="flex items-center gap-2">
                {offer.logo_url && <img src={offer.logo_url} alt="" className="w-[50px] h-[50px] object-contain" />}
                <span className="text-sm text-white font-semibold">{offer.school_name}</span>
              </div>
              <span className="text-[10px] text-gray-600 uppercase tracking-wider font-bold">Vote →</span>
            </button>
          );
        })}
      </div>

      {hasVoted && hasVoted !== 'already' && (
        <div className="mt-3 flex items-center gap-1.5 text-xs text-green-400">
          <CheckCircle2 className="w-3.5 h-3.5" />
          You voted for {hasVoted}
        </div>
      )}
      {hasVoted === 'already' && (
        <div className="mt-3 text-xs text-yellow-500/80">
          You've already voted for this player.
        </div>
      )}
    </div>
  );
}

src/components/portfolio/GameHighlightLinks.jsx

import { useState } from 'react';
import { Plus, X, CheckCircle, Loader2 } from 'lucide-react';
import { base44 } from '@/api/base44Client';
import { datesWithinDays } from '@/lib/portfolioHelpers';

export default function GameHighlightLinks({ players = [], onSave }) {
  const [filmRows, setFilmRows]   = useState([{ date: '', url: '' }]);
  const [hlRows, setHlRows]       = useState([{ slug: '', date: '', url: '' }]);
  const [saving, setSaving]       = useState(false);
  const [results, setResults]     = useState(null);

  const addFilm = () => setFilmRows(r => [...r, { date: '', url: '' }]);
  const addHl   = () => setHlRows(r  => [...r, { slug: '', date: '', url: '' }]);

  const removeFilm = i => setFilmRows(r => r.filter((_, idx) => idx !== i));
  const removeHl   = i => setHlRows(r  => r.filter((_, idx) => idx !== i));

  const handleSave = async () => {
    setSaving(true);
    setResults(null);
    let updated = 0;

    for (const player of players) {
      const boxscores = player.boxscores || [];
      let changed = false;

      const newBoxscores = boxscores.map(b => {
        let box = { ...b };

        // Match game film links (apply to all players with boxscore within ±2 days)
        for (const row of filmRows) {
          if (!row.date || !row.url) continue;
          if (datesWithinDays(b.date, row.date, 2) && !b.film_url) {
            box.film_url = row.url;
            changed = true;
          }
        }

        // Match highlight links (match by slug + date within ±2 days)
        for (const row of hlRows) {
          if (!row.date || !row.url || !row.slug) continue;
          if (player.portfolio_url_slug !== row.slug && player.full_name?.toLowerCase().replace(/\s+/g,'-') !== row.slug) continue;
          if (datesWithinDays(b.date, row.date, 2) && !b.highlight_url) {
            box.highlight_url = row.url;
            changed = true;
          }
        }

        return box;
      });

      if (changed) {
        await base44.entities.Player.update(player.id, { boxscores: newBoxscores });
        updated++;
      }
    }

    setResults({ updated });
    setSaving(false);
    if (onSave) onSave();
  };

  return (
    <div className="space-y-6">
      {/* Game Film Links */}
      <div className="bg-[#1a1a1a] rounded-xl border border-white/10 p-6">
        <div className="flex items-center justify-between mb-1">
          <h3 className="text-white font-bold text-lg">Game Film Links</h3>
          <button onClick={addFilm} className="flex items-center gap-1 text-[#4A9EFF] text-sm hover:text-blue-300 transition-colors">
            <Plus className="w-4 h-4" /> Add Row
          </button>
        </div>
        <p className="text-gray-500 text-sm mb-5">Matched to all players with a boxscore within ±2 days.</p>

        <div className="space-y-3">
          {filmRows.map((row, i) => (
            <div key={i} className="flex gap-3 items-center">
              <input
                type="date" value={row.date}
                onChange={e => setFilmRows(r => r.map((x, idx) => idx === i ? { ...x, date: e.target.value } : x))}
                className="bg-[#111] border border-white/10 rounded-lg px-3 py-2 text-sm text-white focus:outline-none focus:border-white/25 w-40 shrink-0"
              />
              <input
                value={row.url} placeholder="https://vimeo.com/..."
                onChange={e => setFilmRows(r => r.map((x, idx) => idx === i ? { ...x, url: e.target.value } : x))}
                className="bg-[#111] border border-white/10 rounded-lg px-3 py-2 text-sm text-white placeholder-gray-700 focus:outline-none focus:border-white/25 flex-1"
              />
              {filmRows.length > 1 && (
                <button onClick={() => removeFilm(i)} className="text-gray-600 hover:text-gray-400 shrink-0"><X className="w-4 h-4" /></button>
              )}
            </div>
          ))}
        </div>
      </div>

      {/* Highlight Links */}
      <div className="bg-[#1a1a1a] rounded-xl border border-white/10 p-6">
        <div className="flex items-center justify-between mb-1">
          <h3 className="text-white font-bold text-lg">Highlight Links</h3>
          <button onClick={addHl} className="flex items-center gap-1 text-[#4A9EFF] text-sm hover:text-blue-300 transition-colors">
            <Plus className="w-4 h-4" /> Add Row
          </button>
        </div>
        <p className="text-gray-500 text-sm mb-5">
          Matched by player slug (e.g. <code className="bg-[#111] px-1.5 py-0.5 rounded text-[#4A9EFF] text-xs">jstoffel</code>) then date within ±2 days.
        </p>

        <div className="space-y-3">
          {hlRows.map((row, i) => (
            <div key={i} className="flex gap-3 items-center">
              <input
                value={row.slug} placeholder="player-slug"
                onChange={e => setHlRows(r => r.map((x, idx) => idx === i ? { ...x, slug: e.target.value } : x))}
                className="bg-[#111] border border-white/10 rounded-lg px-3 py-2 text-sm text-white placeholder-gray-700 focus:outline-none focus:border-white/25 w-36 shrink-0"
              />
              <input
                type="date" value={row.date}
                onChange={e => setHlRows(r => r.map((x, idx) => idx === i ? { ...x, date: e.target.value } : x))}
                className="bg-[#111] border border-white/10 rounded-lg px-3 py-2 text-sm text-white focus:outline-none focus:border-white/25 w-40 shrink-0"
              />
              <input
                value={row.url} placeholder="https://vimeo.com/..."
                onChange={e => setHlRows(r => r.map((x, idx) => idx === i ? { ...x, url: e.target.value } : x))}
                className="bg-[#111] border border-white/10 rounded-lg px-3 py-2 text-sm text-white placeholder-gray-700 focus:outline-none focus:border-white/25 flex-1"
              />
              {hlRows.length > 1 && (
                <button onClick={() => removeHl(i)} className="text-gray-600 hover:text-gray-400 shrink-0"><X className="w-4 h-4" /></button>
              )}
            </div>
          ))}
        </div>
      </div>

      {/* Save button */}
      <button onClick={handleSave} disabled={saving}
        className="w-full py-4 rounded-xl bg-[#FF6B00] text-white font-bold text-base hover:bg-orange-500 disabled:opacity-50 transition-all flex items-center justify-center gap-2">
        {saving ? <><Loader2 className="w-5 h-5 animate-spin" /> Updating…</> : 'Update Boxscore Links'}
      </button>

      {results && (
        <div className="flex items-center gap-2 bg-[#1a1a1a] rounded-xl p-4 border border-green-500/20">
          <CheckCircle className="w-5 h-5 text-green-400" />
          <span className="text-white text-sm">Updated {results.updated} player{results.updated !== 1 ? 's' : ''}</span>
        </div>
      )}
    </div>
  );
}

src/components/portfolio/GameListPreview.jsx

import React from 'react';
import { Check } from 'lucide-react';

export default function GameListPreview({ player }) {
  const allBoxscores = player.boxscores || [];
  if (allBoxscores.length === 0) return null;

  const teamName = player.current_team_name || player.high_school || player.aau_team || 'Team';

  const parseSession = (b) => {
    if (b.coaches_comments?.startsWith('SESSION_DATA:')) {
      try { return JSON.parse(b.coaches_comments.slice('SESSION_DATA:'.length)); } catch(e) {}
    }
    return null;
  };

  const hasFilm = (b) => {
    const s = parseSession(b);
    return !!(b.film_url || s?.session_film_links?.length > 0);
  };

  const hasStats = (b) => {
    const s = parseSession(b);
    const digital = b.ppg != null || b.rpg != null || b.efg_pct != null || b.time_played != null || b.spg != null;
    return !!(s?.session_games?.length > 0 || digital);
  };

  const hasHighlights = (b) => !!b.highlight_url;

  const Cell = ({ has }) => (
    <span className="w-10 flex justify-center shrink-0">
      {has ? <Check className="w-3.5 h-3.5" style={{ color: '#00FF85' }} /> : <span className="text-gray-800">—</span>}
    </span>
  );

  return (
    <section>
      <div className="flex items-center justify-between mb-4">
        <span className="text-[#FF6B00] text-[11px] font-bold tracking-[0.2em] uppercase">🗓️ GAMES PLAYED</span>
        <span className="text-gray-600 text-sm -mt-4">{allBoxscores.length} games</span>
      </div>

      {/* Column headers */}
      <div className="flex items-center px-3 mb-2 gap-x-2">
        <span className="w-24 shrink-0" />
        <span className="flex-1" />
        <span className="w-10 text-center text-[8px] font-bold uppercase tracking-wider text-gray-700">Film</span>
        <span className="w-10 text-center text-[8px] font-bold uppercase tracking-wider text-gray-700">Stats</span>
        <span className="w-10 text-center text-[8px] font-bold uppercase tracking-wider text-gray-700">Highlights</span>
      </div>

      <div className="space-y-1.5">
        {allBoxscores.map((b, i) => {
          const sessionData = parseSession(b);
          const sessionLabel = sessionData?.session_label;
          const isSession = !!sessionLabel;
          const opponent = b.opponent || 'TBD';
          const date = b.date || '';
          const court = b.court || '';

          return (
            <div key={i} className="flex items-center gap-x-2 py-2.5 px-3 rounded-lg bg-[#0a0a0a] border border-white/[0.05] text-xs">
              {date && <span className="text-gray-600 shrink-0 w-24 truncate">{date}</span>}
              <span className="text-gray-300 flex-1 min-w-0 truncate">
                {isSession ? (
                  <span className="text-[#4A9EFF] font-bold">{sessionLabel} · {opponent}</span>
                ) : (
                  <span className="text-white font-semibold text-xs sm:text-sm truncate">{opponent}</span>
                )}
              </span>
              {court && <span className="text-gray-600 shrink-0 hidden lg:inline">📍 {court}</span>}
              <Cell has={true} />
              <Cell has={hasStats(b)} />
              <Cell has={true} />
            </div>
          );
        })}
      </div>
      <p className="text-gray-700 text-xs mt-3 text-center italic">
        Full game film, highlights & verified stats unlock after purchase.
      </p>
    </section>
  );
}

src/components/portfolio/OffersBadge.jsx

import React from 'react';

const TIER_CONFIG = {
  D1:   { label: 'D1 OFFER',     bg: 'linear-gradient(135deg, #E5E4E2, #A8A8A8)', textColor: '#0D0D0D', glow: 'rgba(229,228,226,0.4)' },
  D2:   { label: 'D2 OFFER',     bg: 'linear-gradient(135deg, #FFD700, #B8860B)', textColor: '#0D0D0D', glow: 'rgba(255,215,0,0.4)' },
  D3:   { label: 'D3 OFFER',     bg: 'linear-gradient(135deg, #D8D8D8, #9E9E9E)', textColor: '#0D0D0D', glow: 'rgba(192,192,192,0.4)' },
  NAIA: { label: 'NAIA OFFER',   bg: 'linear-gradient(135deg, #D8D8D8, #9E9E9E)', textColor: '#0D0D0D', glow: 'rgba(192,192,192,0.4)' },
  JUCO: { label: 'JUCO OFFER',   bg: 'linear-gradient(135deg, #CD7F32, #8B4513)', textColor: '#fff',    glow: 'rgba(205,127,50,0.4)' },
};

const PRIORITY = { D1: 4, D2: 3, D3: 2, NAIA: 2, JUCO: 1, NCCAA: 0 };

function getHighestTier(offers) {
  if (!offers || offers.length === 0) return null;
  let best = null;
  let bestPriority = 0;
  for (const o of offers) {
    const p = PRIORITY[o.division_level] || 0;
    if (p > bestPriority) { bestPriority = p; best = o.division_level; }
  }
  return best;
}

export default function OffersBadge({ offers, size = 'sm' }) {
  const tier = getHighestTier(offers);
  if (!tier) return null;
  const config = TIER_CONFIG[tier];
  if (!config) return null;

  const sizeClasses = size === 'lg'
    ? 'text-[10px] px-3 py-1 gap-1.5 tracking-widest'
    : 'text-[9px] px-1.5 py-0.5 gap-0.5 tracking-wide';

  return (
    <span className={`inline-flex items-center rounded-full font-black uppercase ${sizeClasses}`}
      style={{ background: config.bg, color: config.textColor, boxShadow: `0 2px 12px ${config.glow}` }}>
      {config.label}
    </span>
  );
}

src/components/portfolio/OffersReceived.jsx

import React from 'react';

const TIER_CONFIG = {
  'D1':    { color: '#E5E4E2', label: 'D1',    glow: 'rgba(229,228,226,0.30)' },
  'D2':    { color: '#D4AF37', label: 'D2',    glow: 'rgba(212,175,55,0.35)' },
  'D3':    { color: '#C0C0C0', label: 'D3',    glow: 'rgba(192,192,192,0.20)' },
  'NAIA':  { color: '#CD7F32', label: 'NAIA',  glow: 'rgba(205,127,50,0.25)' },
  'JUCO':  { color: '#CD7F32', label: 'JUCO',  glow: 'rgba(205,127,50,0.25)' },
  'NCCAA': { color: '#CD7F32', label: 'NCCAA', glow: 'rgba(205,127,50,0.25)' },
};

const TIER_ORDER = { 'D1': 0, 'D2': 1, 'D3': 2, 'NAIA': 3, 'JUCO': 4, 'NCCAA': 5 };

// Handle both old (string) and new (object) formats
function normalizeOffers(offers) {
  return (offers || []).map(o => {
    if (typeof o === 'string') return { school_name: o, division_level: null, logo_url: null };
    return o;
  }).filter(o => o.school_name);
}

export default function OffersReceived({ offers }) {
  const normalized = normalizeOffers(offers);

  if (normalized.length === 0) return null;

  // Sort by tier prestige (D1 first)
  const sorted = [...normalized].sort((a, b) =>
    (TIER_ORDER[a.division_level] ?? 99) - (TIER_ORDER[b.division_level] ?? 99)
  );

  return (
    <div className="rounded-2xl overflow-hidden border border-[#D4AF37]/25 p-6"
      style={{ background: 'linear-gradient(135deg, rgba(212,175,55,0.08) 0%, rgba(212,175,55,0.02) 100%)' }}>
      <div className="flex items-center gap-2 mb-4">
        <span className="text-[#D4AF37] text-[11px] font-bold tracking-[0.2em] uppercase">🎓 OFFERS RECEIVED</span>
      </div>
      <p className="text-gray-400 text-sm mb-4">Formal scholarship offers from:</p>
      <div className="flex flex-wrap gap-3">
        {sorted.map((offer, i) => {
          const tier = offer.division_level ? TIER_CONFIG[offer.division_level] : null;
          const tierColor = tier?.color || '#D4AF37';
          const tierGlow = tier?.glow || 'none';

          return (
            <div key={i}
              className="relative flex items-center gap-2.5 px-5 py-3 rounded-xl border overflow-hidden"
              style={{
                background: 'rgba(212,175,55,0.06)',
                borderColor: tier ? `${tierColor}66` : 'rgba(212,175,55,0.3)',
                boxShadow: tier ? `0 0 20px ${tierGlow}` : 'none',
              }}>
              {/* Slanted tier banner (top-right corner) */}
              {tier && (
                <div className="absolute top-0 right-0 overflow-hidden" style={{ width: 56, height: 56 }}>
                  <div className="absolute font-barlow font-black text-[8px] uppercase tracking-wider text-center"
                    style={{
                      top: 8,
                      right: -18,
                      background: tierColor,
                      color: '#0D0D0D',
                      padding: '2px 22px',
                      width: 76,
                      transform: 'rotate(45deg)',
                    }}>
                    {tier.label}
                  </div>
                </div>
              )}
              {/* Logo or emoji */}
              {offer.logo_url ? (
                <img src={offer.logo_url} alt={offer.school_name} className="w-20 h-20 object-contain shrink-0" />
              ) : (
                <span className="text-2xl shrink-0">🎓</span>
              )}
              <span className="font-barlow font-black text-xl text-white tracking-wide pr-8">{offer.school_name}</span>
            </div>
          );
        })}
      </div>
    </div>
  );
}

src/components/portfolio/PlayerBoxscoreEntry.jsx

import React, { useState } from 'react';
import { base44 } from '@/api/base44Client';
import { X, Save } from 'lucide-react';
import { Button } from '@/components/ui/button';

function Field({ label, name, value, onChange, type = 'text', placeholder = '' }) {
  return (
    <div>
      <p className="text-gray-600 text-xs mb-1">{label}</p>
      <input
        type={type}
        placeholder={placeholder}
        value={value}
        onChange={e => onChange(name, e.target.value)}
        className="bg-white/5 border border-white/10 rounded-lg px-3 py-2 text-white text-sm placeholder-gray-700 w-full focus:outline-none focus:border-[#FF6B00]/50 transition-colors"
      />
    </div>
  );
}

export default function PlayerBoxscoreEntry({ player, onSaved, onCancel }) {
  const [form, setForm] = useState({
    date: '', opponent: '',
    ppg: '', rpg: '', apg: '', spg: '', bpg: '',
    three_made: '', three_att: '', two_made: '', two_att: '',
    highlight_url: '', notes: '',
  });
  const [saving, setSaving] = useState(false);

  const update = (name, value) => setForm(f => ({ ...f, [name]: value }));

  const handleSave = async () => {
    setSaving(true);
    try {
      const entry = {
        game_id: `player-added-${Date.now()}`,
        date: form.date || new Date().toISOString().split('T')[0],
        ...(form.opponent && { opponent: form.opponent }),
        ...(form.ppg !== '' && { ppg: parseFloat(form.ppg) }),
        ...(form.rpg !== '' && { rpg: parseFloat(form.rpg) }),
        ...(form.apg !== '' && { apg: parseFloat(form.apg) }),
        ...(form.spg !== '' && { spg: parseFloat(form.spg) }),
        ...(form.bpg !== '' && { bpg: parseFloat(form.bpg) }),
        ...(form.three_made !== '' && { three_made: parseInt(form.three_made) }),
        ...(form.three_att !== '' && { three_att: parseInt(form.three_att) }),
        ...(form.two_made !== '' && { two_made: parseInt(form.two_made) }),
        ...(form.two_att !== '' && { two_att: parseInt(form.two_att) }),
        ...(form.highlight_url && { highlight_url: form.highlight_url }),
        ...(form.notes && { notes: form.notes }),
      };

      const existing = player.boxscores || [];
      await base44.entities.Player.update(player.id, {
        boxscores: [...existing, entry],
      });
      onSaved?.();
    } catch {
      alert('Failed to save boxscore. Please try again.');
    } finally {
      setSaving(false);
    }
  };

  return (
    <div className="bg-[#111] rounded-2xl p-6 border border-[#FF6B00]/20 space-y-4">
      <div className="flex items-center justify-between">
        <h3 className="font-barlow font-bold text-white text-lg">Add Boxscore</h3>
        <button onClick={onCancel} className="text-gray-600 hover:text-white transition-colors">
          <X className="w-4 h-4" />
        </button>
      </div>

      <div className="grid grid-cols-2 gap-3">
        <Field label="Game Date" name="date" value={form.date} onChange={update} type="date" />
        <Field label="Opponent" name="opponent" value={form.opponent} onChange={update} placeholder="vs. Team Name" />
      </div>

      <div className="grid grid-cols-3 sm:grid-cols-5 gap-3">
        <Field label="Points" name="ppg" value={form.ppg} onChange={update} type="number" placeholder="0" />
        <Field label="Rebounds" name="rpg" value={form.rpg} onChange={update} type="number" placeholder="0" />
        <Field label="Assists" name="apg" value={form.apg} onChange={update} type="number" placeholder="0" />
        <Field label="Steals" name="spg" value={form.spg} onChange={update} type="number" placeholder="0" />
        <Field label="Blocks" name="bpg" value={form.bpg} onChange={update} type="number" placeholder="0" />
      </div>

      <div className="grid grid-cols-4 gap-3">
        <Field label="3PM" name="three_made" value={form.three_made} onChange={update} type="number" placeholder="0" />
        <Field label="3PA" name="three_att" value={form.three_att} onChange={update} type="number" placeholder="0" />
        <Field label="2PM" name="two_made" value={form.two_made} onChange={update} type="number" placeholder="0" />
        <Field label="2PA" name="two_att" value={form.two_att} onChange={update} type="number" placeholder="0" />
      </div>

      <Field label="Highlight Link (optional)" name="highlight_url" value={form.highlight_url} onChange={update} placeholder="https://vimeo.com/..." />

      <div>
        <p className="text-gray-600 text-xs mb-1">Notes (DNP, Injury, Missed Game, etc.)</p>
        <textarea
          value={form.notes}
          onChange={e => update('notes', e.target.value)}
          rows={2}
          placeholder="e.g. DNP — ankle injury, limited minutes due to foul trouble..."
          className="bg-white/5 border border-white/10 rounded-lg px-3 py-2 text-white text-sm placeholder-gray-700 w-full focus:outline-none focus:border-[#FF6B00]/50 transition-colors resize-none"
        />
      </div>

      <div className="flex gap-2 pt-1">
        <Button onClick={handleSave} disabled={saving}
          className="bg-[#FF6B00] hover:bg-[#ff8c00] text-white font-bold flex-1">
          <Save className="w-3.5 h-3.5 mr-2" />
          {saving ? 'Saving...' : 'Save Boxscore'}
        </Button>
        <Button onClick={onCancel} variant="ghost" className="text-gray-500 hover:text-white">
          Cancel
        </Button>
      </div>
    </div>
  );
}

src/components/portfolio/PlayerEditModal.jsx

import { useState } from 'react';
import { base44 } from '@/api/base44Client';
import { X, Loader2, Sparkles, Plus, Trash2, Eye, EyeOff, Globe, Lock } from 'lucide-react';
import { toPlayerId, computeAverages, parseHeightToInches, formatHeightImperial } from '@/lib/portfolioHelpers';

const TABS = ['Profile', 'Recruiting', 'Videos', 'Boxscores', 'Playing Style', 'Concierge'];
const POSITIONS = ['PG', 'SG', 'SF', 'PF', 'C', 'G', 'F', 'G/F', 'F/C'];

function Input({ label, value, onChange, type = 'text', placeholder, badge }) {
  return (
    <div>
      <div className="flex items-center gap-2 mb-1">
        <label className="text-xs text-gray-400 font-medium">{label}</label>
        {badge}
      </div>
      <input
        type={type} value={value || ''} onChange={e => onChange(e.target.value)}
        placeholder={placeholder}
        className="w-full bg-[#111] border border-white/10 rounded-lg px-3 py-2 text-sm text-white placeholder-gray-700 focus:outline-none focus:border-white/25"
      />
    </div>
  );
}

function PrivateBadge() {
  return <span className="flex items-center gap-0.5 px-1.5 py-0.5 rounded bg-yellow-900/30 border border-yellow-600/20 text-yellow-600 text-[10px]"><Lock className="w-2.5 h-2.5" />Private</span>;
}

export default function PlayerEditModal({ player, onSave, onClose }) {
  const [tab, setTab]     = useState('Profile');
  const [data, setData]   = useState({ ...player });
  const [saving, setSaving] = useState(false);
  const [genStyle, setGenStyle] = useState(false);

  const set = (key, val) => setData(d => ({ ...d, [key]: val }));

  const handleSave = async () => {
    setSaving(true);
    // auto-generate slug if missing
    if (!data.portfolio_url_slug && data.full_name) {
      data.portfolio_url_slug = toPlayerId(data.full_name);
    }
    await onSave(data);
    setSaving(false);
  };

  const generatePlayingStyle = async () => {
    setGenStyle(true);
    const boxscores = data.boxscores || [];
    const statsSummary = boxscores
      .filter(b => !b.excluded_from_averages)
      .map(b => `Game: ${b.ppg ?? '?'}pts ${b.rpg ?? '?'}reb ${b.apg ?? '?'}ast | 2pt: ${b.two_made}/${b.two_att} 3pt: ${b.three_made}/${b.three_att}`)
      .join('\n') || 'No stats yet.';

    const result = await base44.integrations.Core.InvokeLLM({
      prompt: `You are an analyst writing a scout-level player overview for a basketball portfolio.

PLAYER: ${data.full_name} | ${data.position || 'Guard'} | ${data.current_team_name || data.high_school || 'HS Player'}

STATS:
${statsSummary}

Write 3-4 labeled sections using em dash style: "Label —\\n\\n2-3 sentences."
Use **bold** for key traits. Reference specific stat numbers. End with: "Overall profile: [archetype]. [upside sentence]."`,
      response_json_schema: { type: 'object', properties: { description: { type: 'string' } } },
    });
    set('playing_style_description', result.description);
    setGenStyle(false);
  };

  const addBoxscore = () => {
    const cur = data.boxscores || [];
    set('boxscores', [...cur, { date: '', opponent: '', ppg: null, rpg: null, apg: null, spg: null, bpg: null, three_made: 0, three_att: 0, two_made: 0, two_att: 0, games_played: 1, excluded_from_averages: false, film_url: '', highlight_url: '', file_url: '' }]);
  };

  const updateBoxscore = (i, field, val) => {
    const cur = [...(data.boxscores || [])];
    cur[i] = { ...cur[i], [field]: val };
    const avgs = computeAverages(cur);
    setData(d => ({ ...d, boxscores: cur, ...avgs }));
  };

  const removeBoxscore = (i) => {
    const cur = (data.boxscores || []).filter((_, idx) => idx !== i);
    const avgs = computeAverages(cur);
    setData(d => ({ ...d, boxscores: cur, ...avgs }));
  };

  const slugPreview = data.portfolio_url_slug || toPlayerId(data.full_name || '');
  const heightDisplay = data.height_inches ? formatHeightImperial(data.height_inches) : '';

  return (
    <div className="fixed inset-0 z-50 flex items-center justify-center p-4">
      <div className="absolute inset-0 bg-black/80 backdrop-blur-sm" onClick={onClose} />
      <div className="relative bg-[#161616] border border-white/10 rounded-2xl w-full max-w-3xl max-h-[90vh] flex flex-col shadow-2xl">
        {/* Header */}
        <div className="flex items-center justify-between px-6 py-4 border-b border-white/10">
          <div>
            <h2 className="text-white font-bold text-lg">{data.id ? 'Edit Player' : 'Add New Player'}</h2>
            {slugPreview && (
              <p className="text-gray-600 text-xs mt-0.5">gameonaio.com/player/<span className="text-[#4A9EFF]">{slugPreview}</span></p>
            )}
          </div>
          <div className="flex items-center gap-2">
            <label className="flex items-center gap-2 cursor-pointer">
              <span className="text-xs text-gray-500">Published</span>
              <div onClick={() => set('is_published', !data.is_published)}
                className={`w-10 h-5 rounded-full transition-colors relative ${data.is_published ? 'bg-green-500' : 'bg-[#333]'}`}>
                <div className={`absolute top-0.5 w-4 h-4 rounded-full bg-white transition-all ${data.is_published ? 'left-5' : 'left-0.5'}`} />
              </div>
            </label>
            <button onClick={onClose} className="text-gray-500 hover:text-white p-1">
              <X className="w-5 h-5" />
            </button>
          </div>
        </div>

        {/* Tabs */}
        <div className="flex border-b border-white/10 px-6 overflow-x-auto">
          {TABS.map(t => (
            <button key={t} onClick={() => setTab(t)}
              className={`px-3 py-3 text-sm font-medium border-b-2 transition-all whitespace-nowrap ${
                tab === t ? 'border-white text-white' : 'border-transparent text-gray-500 hover:text-gray-300'
              }`}>
              {t}
            </button>
          ))}
        </div>

        {/* Content */}
        <div className="flex-1 overflow-y-auto px-6 py-5">

          {/* ── PROFILE ── */}
          {tab === 'Profile' && (
            <div className="grid sm:grid-cols-2 gap-4">
              <div className="sm:col-span-2">
                <Input label="Full Name *" value={data.full_name} onChange={v => set('full_name', v)} placeholder="Jaden Stoffel" />
              </div>
              <Input label="First Name" value={data.first_name} onChange={v => set('first_name', v)} />
              <Input label="Last Name" value={data.last_name} onChange={v => set('last_name', v)} />
              <Input label="Jersey Number" value={data.jersey_number} onChange={v => set('jersey_number', v)} placeholder="3" />
              <div>
                <label className="text-xs text-gray-400 font-medium block mb-1">Position</label>
                <select value={data.position || ''} onChange={e => set('position', e.target.value)}
                  className="w-full bg-[#111] border border-white/10 rounded-lg px-3 py-2 text-sm text-white focus:outline-none focus:border-white/25">
                  <option value="">— Select —</option>
                  {POSITIONS.map(p => <option key={p} value={p}>{p}</option>)}
                </select>
              </div>
              <div>
                <label className="text-xs text-gray-400 font-medium block mb-1">Secondary Position</label>
                <select value={data.secondary_position || ''} onChange={e => set('secondary_position', e.target.value)}
                  className="w-full bg-[#111] border border-white/10 rounded-lg px-3 py-2 text-sm text-white focus:outline-none focus:border-white/25">
                  <option value="">— None —</option>
                  {POSITIONS.map(p => <option key={p} value={p}>{p}</option>)}
                </select>
              </div>
              <Input label="Profile Photo URL" value={data.profile_photo_url} onChange={v => set('profile_photo_url', v)} placeholder="https://..." />
              <Input label="Current Team / School" value={data.current_team_name} onChange={v => set('current_team_name', v)} />
              <Input label="High School" value={data.high_school} onChange={v => set('high_school', v)} />
              <Input label="AAU / Travel Team" value={data.aau_team} onChange={v => set('aau_team', v)} />
              <Input label="Hometown (City, State)" value={data.hometown} onChange={v => set('hometown', v)} placeholder="Denver, CO" />
              <Input label="Nationality" value={data.nationality} onChange={v => set('nationality', v)} placeholder="USA" />
              <div className="sm:col-span-2">
                <label className="text-xs text-gray-400 font-medium block mb-1">URL Slug</label>
                <div className="flex gap-2 items-center">
                  <input value={data.portfolio_url_slug || ''} onChange={e => set('portfolio_url_slug', e.target.value)}
                    placeholder="auto-generated from name"
                    className="flex-1 bg-[#111] border border-white/10 rounded-lg px-3 py-2 text-sm text-white placeholder-gray-700 focus:outline-none focus:border-white/25" />
                  <button onClick={() => set('portfolio_url_slug', toPlayerId(data.full_name || ''))}
                    className="px-3 py-2 text-xs border border-white/10 text-gray-400 rounded-lg hover:bg-white/5">Auto</button>
                </div>
              </div>
              <div className="sm:col-span-2">
                <label className="text-xs text-gray-400 font-medium block mb-1">Bio</label>
                <textarea value={data.bio || ''} onChange={e => set('bio', e.target.value)} rows={4}
                  className="w-full bg-[#111] border border-white/10 rounded-lg px-3 py-2 text-sm text-white placeholder-gray-700 focus:outline-none focus:border-white/25 resize-none"
                  placeholder="Short player bio..." />
              </div>
            </div>
          )}

          {/* ── RECRUITING ── */}
          {tab === 'Recruiting' && (
            <div className="grid sm:grid-cols-2 gap-4">
              <Input label="Height (inches)" value={data.height_inches} onChange={v => set('height_inches', parseFloat(v) || null)} type="number" placeholder={'73 (= 6\'1")'} />
              <div>
                <label className="text-xs text-gray-400 font-medium block mb-1">Height display</label>
                <div className="bg-[#111] border border-white/10 rounded-lg px-3 py-2 text-sm text-gray-400">{heightDisplay || '—'}</div>
              </div>
              <Input label="Weight (lbs)" value={data.weight_lbs} onChange={v => set('weight_lbs', parseFloat(v) || null)} type="number" />
              <Input label="Date of Birth" value={data.date_of_birth} onChange={v => set('date_of_birth', v)} type="date" />
              <Input label="Graduation Year" value={data.graduation_year} onChange={v => set('graduation_year', parseInt(v) || null)} type="number" placeholder="2027" />
              <Input label="GPA" value={data.gpa} onChange={v => set('gpa', parseFloat(v) || null)} type="number" placeholder="3.5" />
              <Input label="ACT Score" value={data.act_score} onChange={v => set('act_score', parseInt(v) || null)} type="number" badge={<PrivateBadge />} />
              <Input label="SAT Score" value={data.sat_score} onChange={v => set('sat_score', parseInt(v) || null)} type="number" badge={<PrivateBadge />} />
              <Input label="Email" value={data.email} onChange={v => set('email', v)} badge={<PrivateBadge />} />
              <Input label="Phone" value={data.phone} onChange={v => set('phone', v)} badge={<PrivateBadge />} />
              <div className="sm:col-span-2">
                <Input label="Home Address" value={data.home_address} onChange={v => set('home_address', v)} placeholder="123 Smith St" badge={<PrivateBadge />} />
              </div>
              <Input label="City" value={data.address_city} onChange={v => set('address_city', v)} />
              <Input label="State" value={data.address_state} onChange={v => set('address_state', v)} placeholder="CO" />
              <Input label="ZIP" value={data.address_zip} onChange={v => set('address_zip', v)} badge={<PrivateBadge />} />
              <Input label="Twitter Handle (no @)" value={data.social_twitter} onChange={v => set('social_twitter', v)} placeholder="Player_School23" />
              <Input label="Instagram Handle (no @)" value={data.social_instagram} onChange={v => set('social_instagram', v)} placeholder="Player_School23" />
              <Input label="Hudl Profile URL" value={data.social_hudl} onChange={v => set('social_hudl', v)} />
              <Input label="Agency" value={data.agency} onChange={v => set('agency', v)} />
            </div>
          )}

          {/* ── VIDEOS ── */}
          {tab === 'Videos' && (
            <div className="space-y-4">
              <Input label="Featured Highlight URL (Vimeo / YouTube)" value={data.featured_highlight_url} onChange={v => set('featured_highlight_url', v)} placeholder="https://vimeo.com/..." />
              <Input label="Highlight Video URL (fallback)" value={data.highlight_video_url} onChange={v => set('highlight_video_url', v)} />
              <Input label="Stat-By-Video URL" value={data.stats_video_url} onChange={v => set('stats_video_url', v)} />

              <div>
                <div className="flex items-center justify-between mb-2">
                  <label className="text-xs text-gray-400 font-medium">Additional Vimeo Links</label>
                  <button onClick={() => set('vimeo_links', [...(data.vimeo_links || []), ''])}
                    className="text-[#4A9EFF] text-xs flex items-center gap-1 hover:text-blue-300"><Plus className="w-3 h-3" /> Add</button>
                </div>
                {(data.vimeo_links || []).map((url, i) => (
                  <div key={i} className="flex gap-2 mb-2">
                    <input value={url} onChange={e => { const v = [...(data.vimeo_links || [])]; v[i] = e.target.value; set('vimeo_links', v); }}
                      placeholder="https://vimeo.com/..."
                      className="flex-1 bg-[#111] border border-white/10 rounded-lg px-3 py-2 text-sm text-white placeholder-gray-700 focus:outline-none focus:border-white/25" />
                    <button onClick={() => { const v = (data.vimeo_links || []).filter((_, idx) => idx !== i); set('vimeo_links', v); }}
                      className="text-gray-600 hover:text-red-400 p-2"><X className="w-4 h-4" /></button>
                  </div>
                ))}
              </div>
            </div>
          )}

          {/* ── BOXSCORES ── */}
          {tab === 'Boxscores' && (
            <div className="space-y-3">
              <div className="flex items-center justify-between">
                <p className="text-gray-400 text-sm">{(data.boxscores || []).length} game entries</p>
                <button onClick={addBoxscore}
                  className="flex items-center gap-1 text-[#4A9EFF] text-sm hover:text-blue-300"><Plus className="w-3.5 h-3.5" /> Add Game</button>
              </div>
              {(data.boxscores || []).map((b, i) => (
                <div key={i} className="bg-[#111] rounded-xl p-4 border border-white/[0.06] space-y-3">
                  <div className="flex items-center justify-between">
                    <div className="flex items-center gap-2">
                      <span className="text-white text-sm font-semibold">{b.date || 'No date'} {b.opponent ? `vs ${b.opponent}` : ''}</span>
                      {b.ppg != null && <span className="text-[#FF6B00] text-xs">{b.ppg}pts</span>}
                    </div>
                    <div className="flex items-center gap-2">
                      <button onClick={() => updateBoxscore(i, 'excluded_from_averages', !b.excluded_from_averages)}
                        className={`text-xs px-2 py-1 rounded border transition-all ${b.excluded_from_averages ? 'border-yellow-600/30 text-yellow-500 bg-yellow-900/20' : 'border-white/10 text-gray-500'}`}>
                        {b.excluded_from_averages ? 'Excluded' : 'Include'}
                      </button>
                      <button onClick={() => removeBoxscore(i)} className="text-gray-600 hover:text-red-400"><Trash2 className="w-3.5 h-3.5" /></button>
                    </div>
                  </div>
                  <div className="grid grid-cols-3 sm:grid-cols-6 gap-2">
                    {[
                      { key: 'date', label: 'Date', type: 'date', span: 2 },
                      { key: 'opponent', label: 'Opponent', span: 2 },
                      { key: 'games_played', label: 'GP', type: 'number' },
                      { key: 'ppg', label: 'PTS', type: 'number' },
                      { key: 'rpg', label: 'REB', type: 'number' },
                      { key: 'apg', label: 'AST', type: 'number' },
                      { key: 'spg', label: 'STL', type: 'number' },
                      { key: 'bpg', label: 'BLK', type: 'number' },
                      { key: 'three_made', label: '3PM', type: 'number' },
                      { key: 'three_att', label: '3PA', type: 'number' },
                      { key: 'two_made', label: '2PM', type: 'number' },
                      { key: 'two_att', label: '2PA', type: 'number' },
                    ].map(f => (
                      <div key={f.key} className={f.span === 2 ? 'col-span-2' : ''}>
                        <label className="text-[10px] text-gray-600 block mb-0.5">{f.label}</label>
                        <input type={f.type || 'text'} value={b[f.key] ?? ''}
                          onChange={e => updateBoxscore(i, f.key, f.type === 'number' ? (parseFloat(e.target.value) || null) : e.target.value)}
                          className="w-full bg-[#1a1a1a] border border-white/10 rounded px-2 py-1 text-xs text-white focus:outline-none focus:border-white/25"
                        />
                      </div>
                    ))}
                  </div>
                  <div className="grid grid-cols-1 sm:grid-cols-3 gap-2">
                    {[
                      { key: 'film_url', label: 'Film URL' },
                      { key: 'highlight_url', label: 'Highlight URL' },
                      { key: 'file_url', label: 'Stats PDF URL' },
                    ].map(f => (
                      <div key={f.key}>
                        <label className="text-[10px] text-gray-600 block mb-0.5">{f.label}</label>
                        <input value={b[f.key] || ''}
                          onChange={e => updateBoxscore(i, f.key, e.target.value)}
                          placeholder="https://..."
                          className="w-full bg-[#1a1a1a] border border-white/10 rounded px-2 py-1 text-xs text-white placeholder-gray-700 focus:outline-none focus:border-white/25"
                        />
                      </div>
                    ))}
                  </div>
                </div>
              ))}
              {(data.boxscores || []).length === 0 && (
                <div className="text-center py-8 text-gray-600">
                  <p>No boxscores yet.</p>
                  <button onClick={addBoxscore} className="text-[#4A9EFF] text-sm mt-2 hover:underline">+ Add first game</button>
                </div>
              )}
            </div>
          )}

          {/* ── CONCIERGE ── */}
          {tab === 'Concierge' && (
            <div className="space-y-4">
              <div>
                <label className="text-xs text-gray-400 font-medium block mb-1">Audit Status</label>
                <select value={data.audit_status || 'pending'} onChange={e => set('audit_status', e.target.value)}
                  className="w-full bg-[#111] border border-white/10 rounded-lg px-3 py-2 text-sm text-white focus:outline-none focus:border-white/25">
                  <option value="pending">⏳ Pending</option>
                  <option value="in_progress">🔄 In Progress</option>
                  <option value="needs_correction">⚠️ Needs Correction</option>
                  <option value="verified">✅ Verified</option>
                </select>
              </div>
              <div className="flex items-center gap-3 p-3 rounded-lg bg-white/[0.03] border border-white/10">
                <div onClick={() => set('is_priority_recruit', !data.is_priority_recruit)}
                  className={`w-10 h-5 rounded-full transition-colors relative cursor-pointer ${data.is_priority_recruit ? 'bg-yellow-500' : 'bg-[#333]'}`}>
                  <div className={`absolute top-0.5 w-4 h-4 rounded-full bg-white transition-all ${data.is_priority_recruit ? 'left-5' : 'left-0.5'}`} />
                </div>
                <div>
                  <p className="text-white text-sm font-medium">Priority Recruit</p>
                  <p className="text-gray-500 text-xs">Flag this player as a priority on the team site</p>
                </div>
              </div>
              <div>
                <label className="text-xs text-gray-400 font-medium block mb-1">Audit Notes <span className="text-gray-600">(internal only)</span></label>
                <textarea value={data.audit_notes || ''} onChange={e => set('audit_notes', e.target.value)} rows={5}
                  placeholder="e.g. Photo is blurry — request new one. Stats need verification from coach..."
                  className="w-full bg-[#111] border border-white/10 rounded-lg px-3 py-2 text-sm text-white placeholder-gray-700 focus:outline-none focus:border-white/25 resize-none" />
              </div>
            </div>
          )}

          {/* ── PLAYING STYLE ── */}
          {tab === 'Playing Style' && (
            <div className="space-y-4">
              <Input label="Playing Style Image URL" value={data.playing_style_image_url} onChange={v => set('playing_style_image_url', v)} placeholder="https://..." />
              <div>
                <div className="flex items-center justify-between mb-1">
                  <label className="text-xs text-gray-400 font-medium">Playing Style Description</label>
                  <button onClick={generatePlayingStyle} disabled={genStyle}
                    className="flex items-center gap-1 text-xs px-3 py-1 rounded-lg border border-violet-500/30 text-violet-400 hover:bg-violet-500/10 transition-all disabled:opacity-50">
                    {genStyle ? <><Loader2 className="w-3 h-3 animate-spin" /> Generating…</> : <><Sparkles className="w-3 h-3" /> AI Generate</>}
                  </button>
                </div>
                <textarea value={data.playing_style_description || ''} onChange={e => set('playing_style_description', e.target.value)} rows={10}
                  placeholder="Write or AI-generate a scout-level player description…"
                  className="w-full bg-[#111] border border-white/10 rounded-lg px-3 py-2 text-sm text-white placeholder-gray-700 focus:outline-none focus:border-white/25 resize-none" />
              </div>
              <div>
                <label className="text-xs text-gray-400 font-medium block mb-1">Scouting Report</label>
                <textarea value={data.scouting_report || ''} onChange={e => set('scouting_report', e.target.value)} rows={5}
                  placeholder="Private scouting report (not shown publicly)…"
                  className="w-full bg-[#111] border border-white/10 rounded-lg px-3 py-2 text-sm text-white placeholder-gray-700 focus:outline-none focus:border-white/25 resize-none" />
              </div>
            </div>
          )}
        </div>

        {/* Footer */}
        <div className="flex items-center justify-between px-6 py-4 border-t border-white/10">
          <button onClick={onClose} className="px-4 py-2 rounded-lg border border-white/15 text-gray-400 text-sm hover:text-white hover:border-white/25 transition-all">
            Cancel
          </button>
          <button onClick={handleSave} disabled={saving}
            className="px-6 py-2 rounded-lg bg-[#FF6B00] text-white text-sm font-bold hover:bg-orange-500 disabled:opacity-50 transition-all flex items-center gap-2">
            {saving ? <><Loader2 className="w-4 h-4 animate-spin" /> Saving…</> : 'Save Player'}
          </button>
        </div>
      </div>
    </div>
  );
}

src/components/portfolio/PlayerGraphicCard.jsx

import React from 'react';

function parseHeight(inches) {
  if (!inches) return null;
  const ft = Math.floor(inches / 12);
  const inc = Math.round(inches % 12);
  return `${ft}'${inc}"`;
}

export default function PlayerGraphicCard({ player }) {
  const firstName = player.first_name || player.full_name?.split(' ')[0] || '';
  const lastName = player.last_name || player.full_name?.split(' ').slice(1).join(' ') || '';
  const height = parseHeight(player.height_inches);
  const weight = player.weight_lbs ? `${Math.round(player.weight_lbs)}LB` : null;
  const position = player.position ? `${player.position}` : null;
  const classYear = player.graduation_year ? `CLASS ${player.graduation_year}` : null;

  const metaLine = [height, weight, position, classYear].filter(Boolean).join('  ');

  return (
    <div className="relative w-full overflow-hidden rounded-xl select-none"
      style={{ aspectRatio: '1/1', maxWidth: 520, background: '#0a0a0a', fontFamily: "'Barlow Condensed', sans-serif" }}>

      {/* Left dark panel */}
      <div className="absolute inset-y-0 left-0 z-10 flex flex-col justify-between py-6 px-5"
        style={{ width: '42%', background: 'linear-gradient(to right, #111 70%, transparent)' }}>

        {/* First name top-left */}
        <div className="font-barlow font-black uppercase leading-none text-white"
          style={{ fontSize: 'clamp(28px, 7vw, 52px)', letterSpacing: '-0.02em', textShadow: '2px 2px 0 #000' }}>
          {firstName}
        </div>

        {/* Attributes mid-left */}
        <div className="font-barlow font-black uppercase leading-tight text-white"
          style={{ fontSize: 'clamp(14px, 3.5vw, 22px)', letterSpacing: '0.01em', lineHeight: 1.15 }}>
          {height && <div>{height}</div>}
          {weight && <div>{weight}</div>}
          {position && <div>{position}</div>}
          {classYear && <div>{classYear}</div>}
        </div>

        {/* Jersey number bottom-left */}
        {player.jersey_number && (
          <div className="font-barlow font-black text-white"
            style={{ fontSize: 'clamp(36px, 9vw, 72px)', letterSpacing: '-0.03em', lineHeight: 1, textShadow: '2px 2px 0 #000' }}>
            #{player.jersey_number}
          </div>
        )}
      </div>

      {/* Photo right side */}
      <div className="absolute inset-y-0 right-0" style={{ width: '65%' }}>
        {player.profile_photo_url ? (
          <img
            src={player.profile_photo_url}
            alt={player.full_name}
            className="w-full h-full object-cover object-top"
            style={{ filter: 'contrast(1.08) saturate(1.1)' }}
          />
        ) : (
          <div className="w-full h-full flex items-center justify-center bg-[#1a1a1a]">
            <span className="font-barlow font-black text-gray-700 text-7xl">{firstName?.[0]}</span>
          </div>
        )}
        {/* Gradient overlay blending left edge of photo */}
        <div className="absolute inset-0" style={{ background: 'linear-gradient(to right, #111 0%, transparent 35%)' }} />
        {/* Bottom gradient */}
        <div className="absolute inset-x-0 bottom-0 h-1/3" style={{ background: 'linear-gradient(to top, #000 0%, transparent 100%)' }} />
      </div>

      {/* Last name vertical — over the photo seam */}
      <div className="absolute z-20 font-barlow font-black uppercase text-white/15 pointer-events-none"
        style={{
          fontSize: 'clamp(54px, 15vw, 120px)',
          letterSpacing: '-0.04em',
          lineHeight: 1,
          writingMode: 'vertical-rl',
          textOrientation: 'mixed',
          transform: 'rotate(180deg)',
          bottom: 16,
          left: '36%',
          textShadow: '0 0 40px rgba(0,0,0,0.8)',
        }}>
        {lastName}
      </div>

      {/* Bottom orange accent bar */}
      <div className="absolute bottom-0 inset-x-0 h-1" style={{ background: '#FF6A00', zIndex: 30 }} />
    </div>
  );
}

src/components/portfolio/PlayerHighlightUpload.jsx

import React, { useState, useRef } from 'react';
import { base44 } from '@/api/base44Client';
import { Video, Upload, X, CheckCircle2, AlertCircle } from 'lucide-react';
import { Button } from '@/components/ui/button';

export default function PlayerHighlightUpload({ player, onUploaded, onCancel }) {
  const [file, setFile] = useState(null);
  const [status, setStatus] = useState('idle'); // idle | validating | uploading | done | error
  const [error, setError] = useState('');
  const inputRef = useRef();

  const validateDuration = (f) => new Promise((resolve, reject) => {
    const video = document.createElement('video');
    video.preload = 'metadata';
    video.onloadedmetadata = () => {
      URL.revokeObjectURL(video.src);
      if (video.duration > 300) {
        reject(new Error(`Video is ${Math.ceil(video.duration / 60)} min long. Maximum is 5 minutes (300 sec).`));
      } else {
        resolve(video.duration);
      }
    };
    video.onerror = () => { URL.revokeObjectURL(video.src); reject(new Error('Could not read video file')); };
    video.src = URL.createObjectURL(f);
  });

  const handleFileChange = async (e) => {
    const f = e.target.files?.[0];
    if (!f) return;
    if (!f.type.startsWith('video/')) {
      setError('Please select a video file (MP4, MOV, AVI, etc.)');
      return;
    }
    setError('');
    setStatus('validating');
    try {
      await validateDuration(f);
      setFile(f);
      setStatus('idle');
    } catch (err) {
      setError(err.message);
      setStatus('error');
      e.target.value = '';
    }
  };

  const handleUpload = async () => {
    if (!file) return;
    setStatus('uploading');
    setError('');
    try {
      const { file_url } = await base44.integrations.Core.UploadFile({ file });
      if (!file_url) throw new Error('Upload failed — no URL returned');

      await base44.entities.Player.update(player.id, {
        highlight_video_url: file_url,
      });

      setStatus('done');
      setTimeout(() => onUploaded?.(file_url), 1200);
    } catch (err) {
      setError(err.message || 'Upload failed. Please try again.');
      setStatus('error');
    }
  };

  return (
    <div className="bg-[#111] rounded-2xl p-6 border border-[#4A9EFF]/20 space-y-4">
      <div className="flex items-center justify-between">
        <div>
          <h3 className="font-barlow font-bold text-white text-lg">Upload Highlight Video</h3>
          <p className="text-gray-600 text-xs mt-0.5">Max 5 minutes · MP4, MOV, or AVI</p>
        </div>
        <button onClick={onCancel} className="text-gray-600 hover:text-white transition-colors">
          <X className="w-4 h-4" />
        </button>
      </div>

      {status === 'done' ? (
        <div className="flex items-center gap-3 text-green-400 py-4">
          <CheckCircle2 className="w-5 h-5" />
          <span className="font-medium text-sm">Highlight uploaded successfully!</span>
        </div>
      ) : (
        <>
          <div
            onClick={() => inputRef.current?.click()}
            className="border-2 border-dashed border-white/10 hover:border-[#4A9EFF]/30 rounded-xl p-8 text-center cursor-pointer transition-all"
          >
            <Video className="w-10 h-10 text-gray-600 mx-auto mb-3" />
            {file ? (
              <div>
                <p className="text-white font-medium text-sm">{file.name}</p>
                <p className="text-gray-600 text-xs mt-1">{(file.size / 1024 / 1024).toFixed(1)} MB</p>
              </div>
            ) : (
              <>
                <p className="text-gray-500 text-sm">Click to select a video</p>
                <p className="text-gray-700 text-xs mt-1">Max 5 minutes</p>
              </>
            )}
          </div>

          <input
            ref={inputRef}
            type="file"
            accept="video/*"
            onChange={handleFileChange}
            className="hidden"
          />

          {status === 'validating' && (
            <p className="text-gray-500 text-sm flex items-center gap-2">
              <span className="w-3 h-3 border border-gray-500 border-t-transparent rounded-full animate-spin" />
              Checking video duration...
            </p>
          )}
          {status === 'uploading' && (
            <p className="text-[#4A9EFF] text-sm flex items-center gap-2 animate-pulse">
              <span className="w-3 h-3 border border-[#4A9EFF] border-t-transparent rounded-full animate-spin" />
              Uploading video... please wait
            </p>
          )}
          {error && (
            <p className="text-red-400 text-sm flex items-center gap-2">
              <AlertCircle className="w-4 h-4 shrink-0" /> {error}
            </p>
          )}

          <div className="flex gap-2 pt-1">
            <Button
              onClick={handleUpload}
              disabled={!file || status === 'uploading' || status === 'validating'}
              className="bg-[#4A9EFF] hover:bg-blue-500 text-white font-bold flex-1"
            >
              <Upload className="w-3.5 h-3.5 mr-2" />
              {status === 'uploading' ? 'Uploading...' : 'Upload Highlight'}
            </Button>
            <Button onClick={onCancel} variant="ghost" className="text-gray-500 hover:text-white">
              Cancel
            </Button>
          </div>
        </>
      )}
    </div>
  );
}

src/components/portfolio/PlayerPaywall.jsx

import React, { useState } from 'react';
import { base44 } from '@/api/base44Client';
import { Lock, Tag, CreditCard, CheckCircle2, Star, Clock, ExternalLink } from 'lucide-react';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';

const INCLUDES = [
  'Full game film & highlights for each game',
  'VERIFIED per-game stats & boxscores',
  'Playing style analysis',
  'Social profiles & external recruiting links',
  'Printable / shareable PDF-ready portfolio',
  'Track your progress in your user account',
];

export default function PlayerPaywall({ player, hasContent = false }) {
  const [discountCode, setDiscountCode] = useState('');
  const [codeStatus, setCodeStatus] = useState(null);
  const [validating, setValidating] = useState(false);
  const [loading, setLoading] = useState(false);
  const [sessionSelection, setSessionSelection] = useState('session1'); // 'session1' | 'session2' | 'both'

  const isBothSessions = sessionSelection === 'both';
  const basePrice = isBothSessions ? 250 : 150; // $300 - $50 = $250 for both

  // Claimed but content pending upload
  if (player.portfolio_tier === 'premium' || player.portfolio_tier === 'under_review') {
    const isUnderReview = player.portfolio_tier === 'under_review';
    return (
      <div className="rounded-2xl overflow-hidden border border-[#D4AF37]/25"
        style={{ background: 'linear-gradient(135deg, #0f0c00 0%, #161616 100%)' }}>
        <div className="px-8 pt-7 pb-5 border-b border-[#D4AF37]/15">
          <div className="flex items-center gap-2 mb-3">
            <CheckCircle2 className="w-4 h-4 text-green-400" />
            <span className="text-[10px] font-black uppercase tracking-[0.22em] text-green-400">
              {isUnderReview ? 'Purchase Received — Under Review' : 'Portfolio Claimed'}
            </span>
          </div>
          <h2 className="font-barlow font-black text-3xl text-white leading-tight mb-2">
            {isUnderReview ? 'Hang Tight!' : '🎉 You\'re All Set!'}
          </h2>
          <p className="text-sm text-gray-400 leading-relaxed">
            {isUnderReview
              ? 'Your purchase is under review. Our team will verify and activate your portfolio within 24 hours.'
              : 'Your portfolio has been claimed! Game film, highlights, and stats are being uploaded now — check back soon.'}
          </p>
        </div>
        <div className="px-8 py-6">
          <div className="flex items-start gap-3 p-4 rounded-xl mb-4" style={{ background: 'rgba(255,165,0,0.07)', border: '1px solid rgba(255,165,0,0.2)' }}>
            <Clock className="w-5 h-5 text-[#D4AF37] shrink-0 mt-0.5" />
            <div>
              <p className="text-sm font-bold text-white mb-1">Content Upload in Progress</p>
              <p className="text-xs text-gray-400 leading-relaxed">
                GoAIO is uploading all <span className="text-[#D4AF37] font-bold">600+ games</span> from all teams this week. Your full game film, highlight reels, and stats will appear here automatically once uploaded.
              </p>
            </div>
          </div>
          <div className="space-y-2">
            {[
              { icon: '🎬', label: 'Full Game Film', status: 'Uploading this week' },
              { icon: '⚡', label: 'Highlight Reels', status: 'Uploading this week' },
              { icon: '📊', label: 'Per-Game Stats', status: 'Uploading this week' },
            ].map(item => (
              <div key={item.label} className="flex items-center justify-between px-4 py-3 rounded-lg" style={{ background: '#111', border: '1px solid rgba(255,255,255,0.06)' }}>
                <span className="text-sm text-gray-300">{item.icon} {item.label}</span>
                <span className="text-xs font-bold text-[#D4AF37] flex items-center gap-1.5">
                  <span className="w-1.5 h-1.5 rounded-full bg-[#D4AF37] animate-pulse inline-block" />
                  {item.status}
                </span>
              </div>
            ))}
          </div>
        </div>
      </div>
    );
  }

  const applyCode = async () => {
    if (!discountCode.trim()) return;
    setValidating(true);
    setCodeStatus(null);
    try {
      const res = await base44.functions.invoke('portfolioCheckout', {
        player_id: player.id,
        discount_code: discountCode.trim(),
        validate_only: true,
      });
      setCodeStatus({ valid: true, discount: res.data.discount_amount, final: res.data.final_amount });
    } catch (err) {
      setCodeStatus({ valid: false, error: err.response?.data?.error || 'Invalid or expired code' });
    } finally {
      setValidating(false);
    }
  };

  const handleCheckout = async () => {
    if (window !== window.parent) {
      alert('Checkout is only available from the published app. Please open the portfolio link directly.');
      return;
    }
    setLoading(true);
    try {
      const origin = window.location.origin;
      const slugPath = `/player/${player.portfolio_url_slug}`;
      const res = await base44.functions.invoke('portfolioCheckout', {
        player_id: player.id,
        discount_code: discountCode.trim() || undefined,
        session_selection: sessionSelection,
        success_url: `${origin}${slugPath}?purchased=true`,
        cancel_url: `${origin}${slugPath}`,
      });
      if (res.data?.url) {
        window.location.href = res.data.url;
      }
    } catch (err) {
      alert(err.response?.data?.error || 'Checkout failed. Please try again.');
      setLoading(false);
    }
  };

  const displayPrice = codeStatus?.valid ? codeStatus.final : basePrice;

  return (
    <div className="rounded-2xl overflow-hidden border border-[#D4AF37]/25"
      style={{ background: 'linear-gradient(135deg, #0f0c00 0%, #161616 100%)' }}>

      {/* Banner */}
      <div className="px-8 pt-7 pb-5 border-b border-[#D4AF37]/15">
        <div className="flex items-center gap-2 mb-3">
          <span className="w-1.5 h-1.5 rounded-full animate-pulse bg-[#D4AF37]" />
          <span className="text-[10px] font-black uppercase tracking-[0.22em] text-[#D4AF37]">
            Georgia State Boys Basketball Tournament
          </span>
        </div>
        <h2 className="font-barlow font-black text-3xl text-white leading-tight mb-2">
          Claim Your Portfolio
        </h2>
        <p className="text-sm font-black uppercase tracking-widest mb-2" style={{ color: '#FF6A00' }}>
          Separate yourself and STAY ON YOUR GAME.
        </p>
        <p className="text-sm text-gray-400 leading-relaxed">
          {hasContent
            ? 'Your game film, stats, and highlights are ready. Claim your portfolio for lifetime access — send one link to every coach you want to reach.'
            : "Your profile is live. Claim your portfolio now and work with us during the regular season — your full game film, highlights, and VERIFIED stats will be added after purchase. Track your progress anytime in your user account."}
        </p>
        {/* Highlighted callout — regular season partnership */}
        <div className="mt-3 flex items-center gap-2.5 px-4 py-3 rounded-xl"
          style={{ background: 'linear-gradient(135deg, rgba(255,106,0,0.12), rgba(255,154,60,0.06))', border: '1px solid rgba(255,106,0,0.3)' }}>
          <span className="text-lg shrink-0">🏀</span>
          <p className="text-sm font-black text-white leading-snug">
            We can maintain your portfolio for Club AND Regular Season!
          </p>
        </div>
        <p className="mt-3 text-xs text-gray-500 leading-relaxed">
          Already have content? You can add links from other platforms, old highlights, and even games from last season — we'll integrate everything into your portfolio.
        </p>
      </div>

      <div className="px-8 py-7">
        {/* Sample Portfolio button */}
        <a
          href="https://goaio.live/player/anwar-molette-salem"
          target="_blank"
          rel="noopener noreferrer"
          className="flex items-center justify-center gap-2 w-full py-3 rounded-xl font-barlow font-black text-sm uppercase tracking-widest transition-all mb-6"
          style={{ background: 'rgba(212,175,55,0.12)', border: '1px solid #D4AF37', color: '#D4AF37' }}
          onMouseEnter={e => e.currentTarget.style.background = 'rgba(212,175,55,0.20)'}
          onMouseLeave={e => e.currentTarget.style.background = 'rgba(212,175,55,0.12)'}
        >
          <ExternalLink className="w-4 h-4" />
          View Sample Portfolio
        </a>

        {/* What's included */}
        <p className="text-[10px] font-black uppercase tracking-[0.2em] text-gray-600 mb-3">What's included</p>
        <div className="space-y-2 mb-6">
          {INCLUDES.map(item => (
            <div key={item} className="flex items-center gap-2.5 text-sm text-gray-400">
              <CheckCircle2 className="w-4 h-4 text-[#D4AF37] shrink-0" />
              {item}
            </div>
          ))}
        </div>

        {/* Session Selection — only for players with both sessions */}
        {player.plays_both_sessions && (
          <div className="mb-5">
            <p className="text-[10px] font-black uppercase tracking-[0.2em] text-gray-600 mb-2">Select Session</p>
            <div className="grid grid-cols-3 gap-2">
              {[
                { value: 'session1', label: 'Session 1', price: '$150' },
                { value: 'session2', label: 'Session 2', price: '$150' },
                { value: 'both', label: 'Both Sessions', price: '$250', badge: 'Save $50' },
              ].map(opt => (
                <button key={opt.value} type="button"
                  onClick={() => setSessionSelection(opt.value)}
                  className="relative flex flex-col items-center py-2.5 px-2 rounded-lg border text-xs font-bold transition-all"
                  style={{
                    background: sessionSelection === opt.value ? 'rgba(212,175,55,0.12)' : 'rgba(255,255,255,0.03)',
                    borderColor: sessionSelection === opt.value ? '#D4AF37' : 'rgba(255,255,255,0.1)',
                    color: sessionSelection === opt.value ? '#D4AF37' : 'rgba(255,255,255,0.45)',
                  }}>
                  {opt.badge && <span className="absolute -top-2 left-1/2 -translate-x-1/2 text-[8px] font-black px-1.5 py-0.5 rounded-full whitespace-nowrap" style={{ background: '#D4AF37', color: '#000' }}>{opt.badge}</span>}
                  <span>{opt.label}</span>
                  <span className="font-barlow font-black text-sm mt-0.5">{opt.price}</span>
                </button>
              ))}
            </div>
          </div>
        )}

        {/* Price */}
        <div className="flex items-baseline gap-2 mb-5">
          <span className="font-barlow font-black text-5xl text-white">${displayPrice}</span>
          {codeStatus?.valid && (
            <span className="text-gray-500 line-through text-xl">${basePrice}</span>
          )}
          {isBothSessions && !codeStatus?.valid && (
            <span className="text-gray-500 line-through text-xl">$300</span>
          )}
          <span className="text-gray-600 text-sm ml-1">one-time · lifetime access</span>
        </div>

        {/* Discount code */}
        <div className="flex gap-2 mb-3">
          <div className="relative flex-1">
            <Tag className="absolute left-3 top-1/2 -translate-y-1/2 w-3.5 h-3.5 text-gray-500" />
            <Input
              placeholder="Discount code (e.g. from photo table)"
              value={discountCode}
              onChange={e => { setDiscountCode(e.target.value.toUpperCase()); setCodeStatus(null); }}
              onKeyDown={e => e.key === 'Enter' && applyCode()}
              className="pl-9 bg-white/5 border-white/10 text-white placeholder-gray-600 text-sm h-10"
            />
          </div>
          <Button
            variant="outline"
            size="sm"
            onClick={applyCode}
            disabled={!discountCode.trim() || validating}
            className="border-white/20 text-white hover:bg-white/10 h-10 shrink-0"
          >
            {validating ? '...' : 'Apply'}
          </Button>
        </div>

        {codeStatus && (
          <p className={`text-sm mb-4 ${codeStatus.valid ? 'text-green-400' : 'text-red-400'}`}>
            {codeStatus.valid
              ? `✓ Code applied — $${codeStatus.discount} off! New total: $${codeStatus.final}`
              : `✗ ${codeStatus.error}`}
          </p>
        )}

        {/* Photo discount reminder */}
        {!codeStatus?.valid && (
          <p className="text-xs text-gray-600 mb-5">
            📸 <span className="text-gray-500">Get your photo taken at the GameOn AIO table for a <span className="text-[#D4AF37]">$25 discount code</span>.</span>
          </p>
        )}

        {/* CTA */}
        <button
          onClick={handleCheckout}
          disabled={loading}
          className="w-full flex items-center justify-center gap-2 py-3.5 rounded-xl font-barlow font-black text-sm uppercase tracking-widest transition-all disabled:opacity-60"
          style={{ background: '#D4AF37', color: '#0D0D0D' }}
          onMouseEnter={e => { if (!loading) e.currentTarget.style.background = '#E8C547'; }}
          onMouseLeave={e => e.currentTarget.style.background = '#D4AF37'}
        >
          <CreditCard className="w-4 h-4" />
          {loading ? 'Redirecting to checkout...' : `Claim for $${displayPrice}${isBothSessions ? ' · Both Sessions' : ''} · Unlock Portfolio`}
        </button>

        <p className="text-center text-xs text-gray-700 mt-3">Secure checkout powered by Stripe</p>
      </div>
    </div>
  );
}

src/components/portfolio/PortfolioContactForm.jsx

import { useState } from 'react';
import { Mail, Loader2 } from 'lucide-react';

const ORANGE = '#FF6A00';

export default function PortfolioContactForm() {
  const [formData, setFormData] = useState({ name: '', email: '', phone: '' });
  const [submitted, setSubmitted] = useState(false);
  const [loading, setLoading] = useState(false);

  const handleChange = (e) => {
    setFormData({ ...formData, [e.target.name]: e.target.value });
  };

  const handleSubmit = async (e) => {
    e.preventDefault();
    if (!formData.name || !formData.email || !formData.phone) {
      alert('Please fill in all fields');
      return;
    }

    setLoading(true);
    try {
      await fetch('mailto:cam@goaio.live?subject=Portfolio Inquiry&body=' + encodeURIComponent(
        `Name: ${formData.name}\nEmail: ${formData.email}\nPhone: ${formData.phone}`
      ));
      setSubmitted(true);
      setFormData({ name: '', email: '', phone: '' });
      setTimeout(() => setSubmitted(false), 4000);
    } catch (e) {
      alert('Error submitting form. Please try emailing cam@goaio.live directly.');
    } finally {
      setLoading(false);
    }
  };

  return (
    <section className="py-20 max-w-4xl mx-auto px-6 border-t border-primary/15">
      <div className="text-center mb-12">
        <h2 className="font-barlow font-800 text-5xl text-white mb-4">
          Need a Portfolio?<br />
          <span className="text-gradient">We Got You!</span>
        </h2>
        <p className="text-muted-foreground max-w-2xl mx-auto">
          Get in touch and we'll help you build a professional player portfolio that gets you seen.
        </p>
      </div>

      {submitted ? (
        <div className="rounded-2xl border border-green-500/30 bg-green-500/10 p-6 text-center">
          <p className="text-green-400 font-semibold">Thanks for reaching out! We'll contact you soon to get started.</p>
        </div>
      ) : (
        <form onSubmit={handleSubmit} className="rounded-2xl border border-primary/20 card-glass p-8 max-w-lg mx-auto">
          <div className="space-y-5 mb-6">
            <div>
              <label className="block text-sm font-semibold text-foreground mb-2">Full Name</label>
              <input
                type="text"
                name="name"
                value={formData.name}
                onChange={handleChange}
                placeholder="Your name"
                className="w-full px-4 py-3 rounded-lg bg-secondary/60 border border-primary/20 text-foreground placeholder-muted-foreground focus:outline-none focus:border-primary/50 transition-colors"
              />
            </div>
            <div>
              <label className="block text-sm font-semibold text-foreground mb-2">Email</label>
              <input
                type="email"
                name="email"
                value={formData.email}
                onChange={handleChange}
                placeholder="your@email.com"
                className="w-full px-4 py-3 rounded-lg bg-secondary/60 border border-primary/20 text-foreground placeholder-muted-foreground focus:outline-none focus:border-primary/50 transition-colors"
              />
            </div>
            <div>
              <label className="block text-sm font-semibold text-foreground mb-2">Phone</label>
              <input
                type="tel"
                name="phone"
                value={formData.phone}
                onChange={handleChange}
                placeholder="(555) 123-4567"
                className="w-full px-4 py-3 rounded-lg bg-secondary/60 border border-primary/20 text-foreground placeholder-muted-foreground focus:outline-none focus:border-primary/50 transition-colors"
              />
            </div>
          </div>

          <button
            type="submit"
            disabled={loading}
            className="w-full py-3 rounded-lg font-semibold transition-all flex items-center justify-center gap-2 disabled:opacity-50"
            style={{ background: ORANGE, color: '#000' }}>
            {loading ? <Loader2 className="w-4 h-4 animate-spin" /> : <Mail className="w-4 h-4" />}
            {loading ? 'Sending...' : 'Get Started'}
          </button>

          <p className="text-xs text-muted-foreground text-center mt-4">
            We'll contact you at the email or phone you provide to discuss your portfolio needs.
          </p>
          <p className="text-xs text-muted-foreground text-center mt-2">
            Or email us directly: <a href="mailto:cam@goaio.live" className="underline hover:opacity-80" style={{ color: ORANGE }}>cam@goaio.live</a>
          </p>
        </form>
      )}
    </section>
  );
}

src/components/portfolio/PortfolioTemplates.jsx

import { CheckCircle, ExternalLink } from 'lucide-react';

const TEMPLATES = [
  {
    key: 'dark_pro',
    name: 'Dark Pro',
    active: true,
    desc: 'Sleek full-dark layout with orange accent stats and large hero photo. Best for players seeking exposure.',
    tags: ['Large hero photo', 'Orange stat pills', 'Film/Vimeo embed', 'Playing style image'],
    colors: ['#111111', '#1a1a1a', '#FF6B00'],
    sections: ['Large hero photo', 'Orange stat pills', 'Film/Vimeo embed', 'Playing style image'],
    preview: { bg: '#111', card: '#1a1a1a', accent: '#FF6B00' },
  },
  {
    key: 'navy_elite',
    name: 'Navy Elite',
    active: false,
    desc: 'Deep navy blue with electric blue accents. Clean, modern, high-contrast — a premium look for scouting reports.',
    tags: ['Navy dark base', 'Blue accent stats', 'Compact stats grid', 'Scouting report focus'],
    colors: ['#0a0f1e', '#111827', '#0074FF'],
    sections: ['Navy dark base', 'Blue accent stats', 'Compact stats grid', 'Scouting report focus'],
    preview: { bg: '#0a0f1e', card: '#111827', accent: '#0074FF' },
  },
  {
    key: 'clean_scout',
    name: 'Clean Scout',
    active: false,
    desc: 'Light, minimalist, text-forward layout. Ideal for sharing with agents and scouts who prefer clean readability.',
    tags: ['Light/minimal style', 'Print-friendly', 'Stats-forward layout', 'Clean typography'],
    colors: ['#ffffff', '#f8f8f8', '#111111'],
    sections: ['Light/minimal style', 'Print-friendly', 'Stats-forward layout', 'Clean typography'],
    preview: { bg: '#f5f5f5', card: '#ffffff', accent: '#111111' },
  },
];

export default function PortfolioTemplates() {
  return (
    <div className="space-y-6">
      {/* Template cards */}
      <div className="grid sm:grid-cols-3 gap-4">
        {TEMPLATES.map(t => (
          <div key={t.key}
            className={`rounded-xl border-2 overflow-hidden transition-all ${
              t.active ? 'border-[#FF6B00]' : 'border-white/10 hover:border-white/20'
            }`}>
            {/* Mini preview */}
            <div className="p-4" style={{ background: t.preview.bg }}>
              <div className="rounded-lg p-3" style={{ background: t.preview.card, border: `1px solid rgba(255,255,255,0.06)` }}>
                {/* Mock player card */}
                <div className="flex items-start gap-2 mb-3">
                  <div className="w-8 h-8 rounded-lg flex items-center justify-center font-black text-sm"
                    style={{ background: t.preview.accent, color: t.preview.bg === '#f5f5f5' ? '#fff' : '#fff' }}>
                    J
                  </div>
                  <div>
                    <p className="font-bold text-xs leading-none" style={{ color: t.preview.bg === '#f5f5f5' ? '#111' : '#fff' }}>Jaden Stoffel</p>
                    <p className="text-[10px] mt-0.5" style={{ color: t.preview.accent }}>#23 · SG/PG</p>
                    <p className="text-[9px]" style={{ color: t.preview.bg === '#f5f5f5' ? '#666' : '#888' }}>FXPO Black</p>
                  </div>
                </div>
                {/* Mock stats */}
                <div className="grid grid-cols-3 gap-1">
                  {['18.4', '4.2', '5.1'].map((v, i) => (
                    <div key={i} className="rounded p-1.5 text-center"
                      style={{ background: t.preview.bg, border: `1px solid rgba(255,255,255,0.05)` }}>
                      <p className="font-black text-sm leading-none" style={{ color: t.preview.accent }}>{v}</p>
                      <p className="text-[8px] mt-0.5" style={{ color: '#888' }}>{['PPG','RPG','APG'][i]}</p>
                    </div>
                  ))}
                </div>
                {/* Mock text section */}
                <div className="mt-2 rounded p-2" style={{ background: t.preview.bg, border: `1px solid rgba(255,255,255,0.04)` }}>
                  <p className="text-[8px] font-bold mb-1" style={{ color: t.preview.accent }}>
                    {t.active ? 'PLAYING STYLE' : 'SCOUTING REPORT'}
                  </p>
                  <p className="text-[8px] leading-tight" style={{ color: '#666' }}>
                    {t.active ? 'Elite playmaker with elite vision and scoring ability from all three levels…' : 'Outstanding court vision, high basketball IQ, consistent three-point threat…'}
                  </p>
                </div>
              </div>
            </div>

            {/* Card info */}
            <div className="px-4 py-3 bg-[#1a1a1a] border-t border-white/[0.06]">
              <div className="flex items-center justify-between mb-1">
                <p className="text-white font-bold text-sm">{t.name}</p>
                {t.active && (
                  <div className="flex items-center gap-1 text-[#FF6B00] text-xs">
                    <CheckCircle className="w-3 h-3" /> ACTIVE
                  </div>
                )}
              </div>
              <p className="text-gray-500 text-xs leading-relaxed mb-2">{t.desc}</p>
              <div className="flex flex-wrap gap-1">
                {t.tags.map(tag => (
                  <span key={tag} className="px-2 py-0.5 rounded-full bg-[#111] border border-white/10 text-gray-500 text-[10px]">
                    {tag}
                  </span>
                ))}
              </div>
            </div>
          </div>
        ))}
      </div>

      {/* Active template details */}
      <div className="bg-[#1a1a1a] rounded-xl border border-white/10 p-6">
        <div className="flex items-center justify-between mb-1">
          <h3 className="text-white font-bold">Dark Pro — Active Template</h3>
          <a href="/player/jaden-stoffel" target="_blank" rel="noopener noreferrer"
            className="flex items-center gap-1.5 text-[#4A9EFF] text-sm hover:text-blue-300 transition-colors">
            <ExternalLink className="w-4 h-4" /> Preview Live
          </a>
        </div>
        <p className="text-gray-500 text-sm mb-5">Sleek full-dark layout with orange accent stats and large hero photo.</p>

        <div className="grid sm:grid-cols-2 gap-6">
          <div>
            <p className="text-gray-400 text-xs font-semibold uppercase tracking-wide mb-3">COLOR SCHEME</p>
            <div className="flex items-center gap-3">
              {['#111111', '#1a1a1a', '#FF6B00'].map((c, i) => (
                <div key={i} className="w-7 h-7 rounded-full border border-white/20" style={{ background: c }} />
              ))}
              <span className="text-gray-500 text-sm">Background · Card · Accent</span>
            </div>
          </div>
          <div>
            <p className="text-gray-400 text-xs font-semibold uppercase tracking-wide mb-3">INCLUDED SECTIONS</p>
            <div className="space-y-1.5">
              {['Large hero photo', 'Orange stat pills', 'Film/Vimeo embed', 'Playing style image'].map(s => (
                <div key={s} className="flex items-center gap-2 text-sm text-gray-300">
                  <CheckCircle className="w-3.5 h-3.5 text-green-400 shrink-0" /> {s}
                </div>
              ))}
            </div>
          </div>
        </div>

        <p className="text-gray-600 text-xs mt-5 pt-4 border-t border-white/[0.06]">
          <strong className="text-gray-500">Note:</strong> Template customization (colors, fonts, section order) is coming soon. Currently the <strong className="text-white">Dark Pro</strong> template is live for all portfolios.
        </p>
      </div>
    </div>
  );
}

src/components/portfolio/RosterImportPanel.jsx

import React, { useState } from 'react';
import { base44 } from '@/api/base44Client';
import { FileText, Upload, CheckCircle2, AlertCircle, Loader2 } from 'lucide-react';
import { Button } from '@/components/ui/button';

const PDF_URL = 'https://media.base44.com/files/public/69bb16a38ac0086814d94e6f/9d43cb0e6_CEREBROROSTERSJUNE12-14-GBCALive-InfoforCOACHESPACKET.pdf';

export default function RosterImportPanel({ onImported }) {
  const [status, setStatus] = useState('idle'); // idle | importing | done | error
  const [result, setResult] = useState(null);
  const [error, setError] = useState('');

  const handleImport = async () => {
    setStatus('importing');
    setError('');
    try {
      const res = await base44.functions.invoke('importRosterPDF', {
        file_url: PDF_URL,
      });
      setResult(res.data);
      setStatus('done');
      onImported?.();
    } catch (err) {
      setError(err.response?.data?.error || 'Import failed. Please try again.');
      setStatus('error');
    }
  };

  return (
    <div className="bg-[#111] rounded-2xl border border-white/10 p-6">
      <div className="flex items-center gap-3 mb-4">
        <div className="w-10 h-10 rounded-xl bg-purple-900/30 border border-purple-500/20 flex items-center justify-center">
          <FileText className="w-5 h-5 text-purple-400" />
        </div>
        <div>
          <h3 className="font-bold text-white text-base">CEREBRO Roster Import</h3>
          <p className="text-gray-600 text-xs">GBCA Live June 12–14 — All teams</p>
        </div>
      </div>

      <p className="text-gray-500 text-sm mb-5">
        This will import all players from the GBCA Live spreadsheet and create base portfolios.
        Existing players with the same slug will be skipped. Allow up to 2–3 minutes.
      </p>

      {status === 'idle' && (
        <Button onClick={handleImport} className="bg-purple-600 hover:bg-purple-700 text-white gap-2 w-full">
          <Upload className="w-4 h-4" /> Import All Players from Spreadsheet
        </Button>
      )}

      {status === 'importing' && (
        <div className="flex items-center gap-3 text-purple-400 py-3">
          <Loader2 className="w-5 h-5 animate-spin" />
          <div>
            <p className="text-sm font-medium">Extracting & importing players...</p>
            <p className="text-xs text-gray-600 mt-0.5">This may take 2–3 minutes. Please wait.</p>
          </div>
        </div>
      )}

      {status === 'done' && result && (
        <div className="flex items-center gap-3 text-green-400 py-3">
          <CheckCircle2 className="w-5 h-5" />
          <div>
            <p className="text-sm font-medium">Import complete!</p>
            <p className="text-xs text-gray-500 mt-0.5">
              Extracted {result.extracted} rows · Created {result.created} player portfolios
            </p>
          </div>
        </div>
      )}

      {status === 'error' && (
        <div className="space-y-3">
          <div className="flex items-center gap-2 text-red-400 text-sm">
            <AlertCircle className="w-4 h-4 shrink-0" /> {error}
          </div>
          <Button onClick={() => setStatus('idle')} variant="outline" size="sm"
            className="border-white/10 text-gray-400 hover:text-white">
            Try Again
          </Button>
        </div>
      )}
    </div>
  );
}

src/components/portfolio/SlugFixButton.jsx

import { useState } from 'react';
import { base44 } from '@/api/base44Client';
import { Button } from '@/components/ui/button';
import { RefreshCw, CheckCircle2 } from 'lucide-react';

export default function SlugFixButton() {
  const [running, setRunning] = useState(false);
  const [progress, setProgress] = useState(null);
  const [done, setDone] = useState(false);

  const runFix = async () => {
    setRunning(true);
    setDone(false);
    let skip = 0;
    let totalUpdated = 0;

    try {
      while (true) {
        setProgress(`Processing players ${skip}–${skip + 200}...`);
        const res = await base44.functions.invoke('fixPlayerSlugs', { startSkip: skip, chunkSize: 200 });
        totalUpdated += res.data.updated;
        if (res.data.done) break;
        skip = res.data.nextSkip;
        await new Promise(r => setTimeout(r, 500));
      }
      setProgress(`Done! Updated ${totalUpdated} slugs.`);
      setDone(true);
    } catch (err) {
      setProgress(`Error: ${err.message}`);
    } finally {
      setRunning(false);
    }
  };

  return (
    <div className="flex items-center gap-3">
      <Button
        onClick={runFix}
        disabled={running}
        size="sm"
        variant="outline"
        className="border-white/20 text-white hover:bg-white/10 gap-2"
      >
        {done
          ? <><CheckCircle2 className="w-3.5 h-3.5 text-green-400" /> Slugs Fixed</>
          : <><RefreshCw className={`w-3.5 h-3.5 ${running ? 'animate-spin' : ''}`} /> Fix Slugs (firstname-lastname)</>
        }
      </Button>
      {progress && <span className="text-xs text-gray-500">{progress}</span>}
    </div>
  );
}

Team Site Builder

src/pages/GeorgiaTeamSite.jsx

import { useState, useEffect } from 'react';
import { base44 } from '@/api/base44Client';
import { Link, useParams } from 'react-router-dom';
import { Film, Users, ExternalLink, Home, CheckCircle2, Lock, Loader2 } from 'lucide-react';
import MiltonGameStats from '@/components/georgia/MiltonGameStats';
import DaculaGameStats from '@/components/georgia/DaculaGameStats';
import MariettaGameStats from '@/components/georgia/MariettaGameStats';
import GameStatsPanel from '@/components/georgia/GameStatsPanel';
import RosterPlayerCard from '@/components/georgia/RosterPlayerCard';
import TeamCheckout from '@/components/georgia/TeamCheckout';
import MiltonSponsors from '@/components/georgia/MiltonSponsors';
import { useAuth } from '@/lib/AuthContext';

const ORANGE = '#FF6A00';

// Normalize team name to a URL-safe slug
export function teamToSlug(name) {
  return name.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/(^-|-$)/g, '');
}

export default function GeorgiaTeamSite() {
  const { teamSlug } = useParams();
  const { user } = useAuth();
  const [games, setGames] = useState([]);
  const [hoopPlayers, setHoopPlayers] = useState([]);
  const [portfolioPlayers, setPortfolioPlayers] = useState([]);
  const [loading, setLoading] = useState(true);
  const [isLocked, setIsLocked] = useState(false);
  const [activeTab, setActiveTab] = useState('film');
  const [teamName, setTeamName] = useState('');
  const [passwordRequired, setPasswordRequired] = useState(false);
  const [passwordInput, setPasswordInput] = useState('');
  const [passwordError, setPasswordError] = useState(false);
  const [isAuthenticated, setIsAuthenticated] = useState(false);
  const [claimedTeams, setClaimedTeams] = useState([]);

  useEffect(() => {
    const load = async () => {
      // Check if team is claimed/locked via TeamClaim entity
      try {
        const claimsRes = await base44.functions.invoke('getGeorgiaTeamsData', {});
        setClaimedTeams(claimsRes.data?.activeSlugs || []);
        
        // Check if this team is locked (skip for Mill Creek)
        if (teamSlug !== 'mill-creek') {
          try {
            const allClaims = await base44.asServiceRole.entities.TeamClaim.filter({ team_slug: teamSlug });
            const teamClaim = allClaims?.[0];
            if (teamClaim?.is_locked) {
              setIsLocked(true);
            }
          } catch (e) {
            // ignore
          }
        }
      } catch (e) {
        console.error('Error loading claimed teams:', e);
      }

      // Clean up claim_success URL param after loading
      const urlParams = new URLSearchParams(window.location.search);
      if (urlParams.get('claim_success') === 'true') {
        window.history.replaceState({}, '', window.location.pathname);
      }

      // Load all georgia games and filter by team slug
      const allGames = await base44.entities.GeorgiaGame.list();
      
      // Find games where this team is team1 or team2
      const teamGames = allGames.filter(g => 
        teamToSlug(g.team1) === teamSlug || teamToSlug(g.team2) === teamSlug
      );

      // Determine actual team name from the data
      const firstGame = teamGames[0];
      if (firstGame) {
        const name = teamToSlug(firstGame.team1) === teamSlug ? firstGame.team1 : firstGame.team2;
        setTeamName(name);
        // Update meta tags for sharing
        document.title = `${name} · Georgia HS Basketball Showcase`;
        updateMetaTag('og:title', name);
        updateMetaTag('og:description', `${name} · Georgia HS Boys Basketball Showcase 2026`);
        updateMetaTag('twitter:title', name);
        updateMetaTag('twitter:description', `${name} · Georgia HS Boys Basketball Showcase 2026`);
      }

          // Build game list — always show this team first in title
      const formattedGames = teamGames.map(g => {
        const isTeam1 = teamToSlug(g.team1) === teamSlug;
        const myTeam = isTeam1 ? g.team1 : g.team2;
        const oppTeam = isTeam1 ? g.team2 : g.team1;
        return {
          ...g,
          displayTitle: `${g.date} · ${g.time} · ${g.court} · ${g.game_number} · ${myTeam} vs ${oppTeam}`,
          opponent: oppTeam,
        };
      }).sort((a, b) => new Date(a.date) - new Date(b.date));

      setGames(formattedGames.filter(g => g.embed_link && g.embed_link.trim() !== ''));

      // Fetch roster via backend function (service-role to bypass RLS)
      try {
        const rosterRes = await base44.functions.invoke('getGeorgiaTeamRoster', { team_slug: teamSlug });
        if (rosterRes.data?.hoop_players) {
          setHoopPlayers(rosterRes.data.hoop_players);
        }
        if (rosterRes.data?.portfolio_players) {
          setPortfolioPlayers(rosterRes.data.portfolio_players);
        }
        // Check if password is required by looking at the first team (they should all have same password)
        if (rosterRes.data?.team_id) {
          try {
            const team = await base44.asServiceRole.entities.HoopTeam.get(rosterRes.data.team_id);
            if (team?.password) {
              setPasswordRequired(true);
            }
          } catch (e) {
            // ignore team fetch errors
          }
        }
      } catch (e) {
        // no team data yet
      }

      setLoading(false);
    };
    load();
    // Subscribe to real-time updates on games
    const unsub = base44.entities.GeorgiaGame.subscribe(event => {
      load();
    });
    return unsub;
  }, [teamSlug]);

  // Helper to update meta tags dynamically
  const updateMetaTag = (property, content) => {
    let tag = document.querySelector(`meta[property="${property}"]`);
    if (!tag) {
      tag = document.createElement('meta');
      tag.setAttribute('property', property);
      document.head.appendChild(tag);
    }
    tag.setAttribute('content', content);
  };

  if (loading) return (
    <div className="min-h-screen bg-black flex items-center justify-center">
      <div className="w-8 h-8 border-2 border-t-transparent rounded-full animate-spin" style={{ borderColor: ORANGE }} />
    </div>
  );

  // Check if unclaimed and user is not admin
  const isClaimed = claimedTeams.includes(teamSlug);
  if (!isClaimed && (!user || user.role !== 'admin')) {
    return (
      <div className="min-h-screen bg-black text-white flex flex-col items-center justify-center text-center px-6">
        <Lock className="w-16 h-16 text-orange-500 mb-4" />
        <h1 className="font-barlow font-black text-3xl mb-2">Team Site Locked</h1>
        <p className="text-gray-500 mb-6">Claim this team site to unlock full access to game film and stats.</p>
        <Link to="/coach-partnership"
          className="inline-block px-6 py-3 rounded-lg font-black uppercase tracking-widest text-sm transition-all"
          style={{ background: ORANGE, color: '#000' }}>
          Claim Now
        </Link>
        <Link to="/georgia-teams" className="text-sm hover:underline mt-4" style={{ color: ORANGE }}>← Back to Teams</Link>
      </div>
    );
  }

  // Check if locked and user is not admin
  if (isLocked && (!user || user.role !== 'admin')) {
    return (
      <div className="min-h-screen bg-black text-white flex flex-col items-center justify-center text-center px-6">
        <Lock className="w-16 h-16 text-orange-500 mb-4" />
        <h1 className="font-barlow font-black text-3xl mb-2">Team Page Locked</h1>
        <p className="text-gray-500 mb-6">This team page is currently locked. Claim this site to unlock.</p>
        <Link to="/coach-partnership"
          className="inline-block px-6 py-3 rounded-lg font-black uppercase tracking-widest text-sm transition-all"
          style={{ background: ORANGE, color: '#000' }}>
          Claim Team Site
        </Link>
        <Link to="/georgia-teams" className="text-sm hover:underline mt-4" style={{ color: ORANGE }}>← Back to Teams</Link>
      </div>
    );
  }

  if (!teamName && games.length === 0) return (
    <div className="min-h-screen bg-black text-white flex flex-col items-center justify-center text-center px-6">
      <p className="text-5xl mb-4">🏀</p>
      <h1 className="font-barlow font-black text-3xl mb-2">Team Not Found</h1>
      <p className="text-gray-500 mb-6">We couldn't find a team with this name.</p>
      <Link to="/georgia-teams" className="text-sm hover:underline" style={{ color: ORANGE }}>← View All Teams</Link>
    </div>
  );



  // Merge hoop players with portfolio data
  const rosterPlayers = hoopPlayers.length > 0
    ? hoopPlayers.map(hp => {
        const pp = portfolioPlayers.find(p => 
          p.id === hp.portfolio_player_id || 
          (p.full_name?.toLowerCase() === hp.full_name?.toLowerCase())
        );
        return { ...hp, portfolio: pp };
      })
    : portfolioPlayers.map(p => ({ ...p, full_name: p.full_name, portfolio: p }));

  const hasPremium = (player) => {
    const pp = player.portfolio;
    return pp && (pp.portfolio_tier === 'premium' || pp.portfolio_tier === 'under_review');
  };

  const sortedRosterPlayers = teamSlug === 'milton'
    ? [...rosterPlayers].sort((a, b) => Number(hasPremium(b)) - Number(hasPremium(a)))
    : rosterPlayers;

  const handlePasswordSubmit = async () => {
    // Get the team to check password
    const allTeams = await base44.entities.HoopTeam.list();
    const team = allTeams.find(t => teamToSlug(t.name) === teamSlug);
    if (team && team.password === passwordInput) {
      setIsAuthenticated(true);
      setPasswordError(false);
      setPasswordInput('');
    } else {
      setPasswordError(true);
      setPasswordInput('');
    }
  };



  if (passwordRequired && !isAuthenticated) {
    return (
      <div className="min-h-screen bg-black text-white flex flex-col items-center justify-center px-4">
        <div className="w-full max-w-sm rounded-2xl border p-8" style={{ background: '#0a0a0a', borderColor: 'rgba(255,255,255,0.07)' }}>
          <h1 className="font-barlow font-black text-2xl mb-2">{teamName || 'Team Page'}</h1>
          <p className="text-gray-500 text-sm mb-6">This team page is password protected.</p>
          <input
            type="password"
            value={passwordInput}
            onChange={e => { setPasswordInput(e.target.value); setPasswordError(false); }}
            placeholder="Enter password"
            autoFocus
            onKeyDown={e => e.key === 'Enter' && handlePasswordSubmit()}
            className="w-full px-4 py-3 rounded-xl bg-[#111] text-white text-sm placeholder-white/30 focus:outline-none mb-3"
            style={{ border: `1px solid ${passwordError ? '#FF3B30' : 'rgba(255,255,255,0.09)'}` }}
          />
          {passwordError && <p className="text-red-400 text-xs mb-3">Incorrect password</p>}
          <button
            onClick={handlePasswordSubmit}
            className="w-full py-3 rounded-xl font-black uppercase tracking-widest transition-all"
            style={{ background: ORANGE, color: '#000' }}>
            Unlock
          </button>
        </div>
      </div>
    );
  }

  return (
    <div className="min-h-screen bg-black text-white">
      {/* Nav */}
      <div className="sticky top-0 z-30 border-b" style={{ background: 'rgba(0,0,0,0.95)', backdropFilter: 'blur(16px)', borderColor: 'rgba(255,255,255,0.07)' }}>
        <div className="max-w-5xl mx-auto px-5 h-14 flex items-center justify-between">
          <div className="flex items-center gap-3">
            <Link to="/Home" className="text-gray-500 hover:text-white transition-colors">
              <Home className="w-4 h-4" />
            </Link>
            <span className="text-gray-600">/</span>
            <Link to="/georgia-teams" className="text-gray-500 hover:text-white text-sm transition-colors">Georgia Showcase</Link>
            <span className="text-gray-600">/</span>
            <span className="font-barlow font-black text-white text-sm">{teamName}</span>
          </div>
          {isClaimed && (
            <span className="text-xs px-3 py-1.5 rounded-lg font-bold flex items-center gap-1.5"
              style={{ background: 'rgba(0,255,133,0.08)', color: '#00FF85', border: '1px solid rgba(0,255,133,0.25)' }}>
              <CheckCircle2 className="w-3 h-3" /> Claimed
            </span>
          )}
        </div>
      </div>

      {/* Hero */}
      <div className="border-b" style={{ borderColor: 'rgba(255,255,255,0.06)', background: 'linear-gradient(to bottom, rgba(255,106,0,0.05), transparent)' }}>
        <div className="max-w-5xl mx-auto px-5 py-10">
          <div className="flex items-center gap-3 mb-2">
            <span className="text-xs px-2 py-1 rounded-full font-bold tracking-widest uppercase" style={{ background: 'rgba(255,106,0,0.1)', color: ORANGE, border: '1px solid rgba(255,106,0,0.25)' }}>
              Georgia HS Boys Basketball Showcase 2026
            </span>
          </div>
          <h1 className="font-barlow font-black text-5xl uppercase tracking-wide mb-1">{teamName}</h1>
          <p className="text-gray-500 text-sm">{teamSlug === 'marietta' ? 8 : teamSlug === 'milton' ? 8 : games.length} games recorded · Georgia</p>
        </div>
      </div>

      <div className="max-w-5xl mx-auto px-5 py-8">
        {/* Tabs */}
        <div className="flex gap-1 mb-6 border-b" style={{ borderColor: 'rgba(255,255,255,0.07)' }}>
          {[
            { id: 'film', label: 'Game Film', icon: Film, count: teamSlug === 'marietta' ? 8 : teamSlug === 'milton' ? 8 : teamSlug === 'dacula' ? 8 : games.length },
            { id: 'roster', label: 'Roster', icon: Users, count: rosterPlayers.length },
          ].map(tab => {
            const Icon = tab.icon;
            return (
              <button key={tab.id} onClick={() => setActiveTab(tab.id)}
                className={`flex items-center gap-2 px-4 py-3 text-sm font-bold transition-all border-b-2 -mb-px ${activeTab === tab.id ? 'text-white' : 'text-gray-500 hover:text-gray-300 border-transparent'}`}
                style={activeTab === tab.id ? { borderColor: ORANGE } : { borderColor: 'transparent' }}>
                <Icon className="w-4 h-4" />
                {tab.label}
                <span className="text-xs px-1.5 py-0.5 rounded-full bg-white/5">{tab.count}</span>
              </button>
            );
          })}
        </div>

        {/* Film Tab */}
        {activeTab === 'film' && (
          <div>
            {teamSlug === 'marietta' ? (
              <MariettaGameStats />
            ) : teamSlug === 'milton' ? (
              <MiltonGameStats />
            ) : teamSlug === 'dacula' ? (
              <DaculaGameStats />
            ) : games.length === 0 ? (
              <div className="text-center py-16 rounded-2xl border border-dashed border-white/10">
                <Film className="w-12 h-12 text-gray-700 mx-auto mb-3" />
                <p className="text-gray-500">No game film available yet.</p>
              </div>
            ) : (
              <div className="space-y-6">
                {games.map(game => (
                  <div key={game.id} className="rounded-2xl border overflow-hidden" style={{ borderColor: 'rgba(255,255,255,0.07)', background: '#0a0a0a' }}>
                    <div className="px-5 py-4 flex items-center justify-between border-b" style={{ borderColor: 'rgba(255,255,255,0.05)' }}>
                      <div>
                        <p className="font-bold text-white text-sm">{game.displayTitle}</p>
                        <p className="text-xs text-gray-600 mt-0.5">Duration: {game.duration}</p>
                      </div>
                      <a href={game.embed_link} target="_blank" rel="noopener noreferrer"
                        className="p-2 rounded-lg transition-colors text-gray-500 hover:text-white" style={{ background: 'rgba(255,255,255,0.05)' }}>
                        <ExternalLink className="w-3.5 h-3.5" />
                      </a>
                    </div>
                    <div className="aspect-video">
                      <iframe
                        src={game.embed_link}
                        className="w-full h-full"
                        allow="autoplay; fullscreen; picture-in-picture"
                        allowFullScreen
                        title={game.displayTitle}
                      />
                    </div>
                    <GameStatsPanel
                      game={game}
                      myTeamName={teamName}
                      oppTeamName={game.opponent}
                      isPurchased={claimedTeams.includes(teamSlug) || !!(user && (user.role === 'admin' || user.role === 'coach'))}
                    />
                  </div>
                ))}
              </div>
            )}
          </div>
        )}

        {/* Roster Tab */}
        {activeTab === 'roster' && (
          <div>
            {rosterPlayers.length === 0 ? (
              <div className="text-center py-16 rounded-2xl border border-dashed border-white/10">
                <Users className="w-12 h-12 text-gray-700 mx-auto mb-3" />
                <p className="text-gray-500 mb-2">Roster not yet available.</p>
                <p className="text-gray-600 text-sm">Coaches can claim this site to add players.</p>
                <div className="flex gap-3 justify-center mt-4">
                  <Link to={`/coach?team=${encodeURIComponent(teamName)}`}
                    className="inline-block px-5 py-2 rounded-lg text-sm font-bold"
                    style={{ background: ORANGE, color: '#000' }}>
                    Claim Team Site
                  </Link>
                  {user && (user.role === 'admin' || user.role === 'coach') && (
                    <Link to={`/players?team=${encodeURIComponent(teamName)}`}
                      className="inline-block px-5 py-2 rounded-lg text-sm font-bold text-white"
                      style={{ background: 'rgba(255,255,255,0.1)' }}>
                      Add Players to Roster
                    </Link>
                  )}
                </div>
              </div>
            ) : (
              <div className="space-y-2">
                {sortedRosterPlayers.map((player, i) => (
                  <RosterPlayerCard key={player.id || i} player={player} user={user} />
                ))}
              </div>
            )}
          </div>
        )}


        {/* Bottom CTA */}
        <div className="mt-12 pt-8 border-t" style={{ borderColor: 'rgba(255,255,255,0.07)' }}>
          <Link to="/coach-partnership"
            className="w-full py-3 rounded-xl font-black uppercase tracking-widest text-sm transition-all text-center block"
            style={{ background: ORANGE, color: '#000' }}>
            Partner With Us
          </Link>
        </div>
        {teamSlug === 'milton' && <MiltonSponsors />}
      </div>
    </div>
  );
}

src/components/georgia/CoachFilmForm.jsx

import { useState } from 'react';
import { X, ArrowRight, Loader2, Check } from 'lucide-react';
import { base44 } from '@/api/base44Client';
import { AnimatePresence, motion } from 'framer-motion';

const GOLD = '#D4AF37';
const GOLD_DIM = 'rgba(212,175,55,0.10)';
const GOLD_BORDER = 'rgba(212,175,55,0.25)';

const inputCls = "w-full px-4 py-2.5 text-sm text-white placeholder-white/30 focus:outline-none bg-[#0D0D0D]";
const inputStyle = (focused) => ({
  border: `1px solid ${focused ? 'rgba(212,175,55,0.5)' : 'rgba(255,255,255,0.1)'}`,
  fontFamily: 'var(--font-inter)',
  transition: 'border-color 0.15s',
});

export default function CoachFilmForm({ label = 'Purchase Package', buttonStyle = {} }) {
  const [showModal, setShowModal] = useState(false);
  const [focused, setFocused] = useState('');
  const [loading, setLoading] = useState(false);
  const [submitted, setSubmitted] = useState(false);
  const [form, setForm] = useState({
    firstName: '',
    lastName: '',
    email: '',
    highSchool: '',
    interestedInPortfolios: null, // true | false | null
  });

  const set = (field) => (e) => setForm(f => ({ ...f, [field]: e.target.value }));

  const valid = form.firstName.trim() && form.lastName.trim() && form.email.trim() && form.highSchool.trim() && form.interestedInPortfolios !== null;

  const handleSubmit = async (e) => {
    e.preventDefault();
    if (!valid) return;

    if (window.self !== window.top) {
      alert('Checkout is only available on the published app. Please open the live site to complete your purchase.');
      return;
    }

    setLoading(true);
    try {
      const response = await base44.functions.invoke('createCheckout', {
        productKey: 'coach_portfolio',
        successUrl: window.location.origin + '/georgia?success=true',
        cancelUrl: window.location.origin + '/georgia?canceled=true',
        customerEmail: form.email,
        metadata: {
          coach_first_name: form.firstName,
          coach_last_name: form.lastName,
          high_school: form.highSchool,
          interested_in_portfolios: form.interestedInPortfolios ? 'yes' : 'no',
          package: 'coach_film_package',
        },
      });
      if (response.data?.url) {
        window.location.href = response.data.url;
      }
    } catch {
      alert('Failed to start checkout. Please try again.');
      setLoading(false);
    }
  };

  return (
    <>
      <button
        onClick={() => setShowModal(true)}
        className="w-full text-xs py-2.5 font-semibold transition-all"
        style={{ background: GOLD_DIM, border: `1px solid ${GOLD_BORDER}`, color: GOLD, ...buttonStyle }}
      >
        {label}
      </button>

      <AnimatePresence>
        {showModal && (
          <motion.div
            initial={{ opacity: 0 }} animate={{ opacity: 1 }} exit={{ opacity: 0 }}
            className="fixed inset-0 z-50 flex items-center justify-center p-4"
            style={{ background: 'rgba(0,0,0,0.85)', backdropFilter: 'blur(6px)' }}
            onClick={() => setShowModal(false)}
          >
            <motion.div
              initial={{ opacity: 0, scale: 0.95, y: 20 }}
              animate={{ opacity: 1, scale: 1, y: 0 }}
              exit={{ opacity: 0, scale: 0.95, y: 12 }}
              transition={{ duration: 0.3, ease: [0.4, 0, 0.2, 1] }}
              className="relative w-full max-w-md overflow-hidden"
              style={{ background: '#0D0D0D', border: `1px solid ${GOLD_BORDER}`, boxShadow: `0 0 60px rgba(212,175,55,0.12)` }}
              onClick={e => e.stopPropagation()}
            >
              {/* Header */}
              <div className="flex items-start justify-between px-7 pt-7 pb-5 border-b" style={{ borderColor: GOLD_BORDER }}>
                <div>
                  <span className="inline-flex items-center gap-1.5 text-[10px] font-black uppercase tracking-[0.2em] px-2 py-1 mb-2"
                    style={{ background: GOLD_DIM, border: `1px solid ${GOLD_BORDER}`, color: GOLD }}>
                    For Coaches
                  </span>
                  <h3 className="font-barlow font-black text-2xl text-white leading-tight">Game Film Package</h3>
                  <p className="text-xs mt-1" style={{ color: 'rgba(255,255,255,0.4)' }}>
                    Complete the form below to proceed to secure checkout — <span style={{ color: GOLD }}>$100</span>
                  </p>
                </div>
                <button onClick={() => setShowModal(false)} style={{ color: 'rgba(255,255,255,0.3)' }}
                  onMouseEnter={e => e.currentTarget.style.color = '#fff'}
                  onMouseLeave={e => e.currentTarget.style.color = 'rgba(255,255,255,0.3)'}
                >
                  <X className="w-5 h-5" />
                </button>
              </div>

              {/* Form */}
              <form onSubmit={handleSubmit} className="px-7 py-6 space-y-4">
                <div className="grid grid-cols-2 gap-3">
                  <div>
                    <label className="block text-[10px] font-black uppercase tracking-widest mb-1.5" style={{ color: 'rgba(255,255,255,0.35)', letterSpacing: '0.16em' }}>First Name *</label>
                    <input type="text" required value={form.firstName} onChange={set('firstName')} placeholder="First"
                      className={inputCls} style={inputStyle(focused === 'fn')}
                      onFocus={() => setFocused('fn')} onBlur={() => setFocused('')}
                    />
                  </div>
                  <div>
                    <label className="block text-[10px] font-black uppercase tracking-widest mb-1.5" style={{ color: 'rgba(255,255,255,0.35)', letterSpacing: '0.16em' }}>Last Name *</label>
                    <input type="text" required value={form.lastName} onChange={set('lastName')} placeholder="Last"
                      className={inputCls} style={inputStyle(focused === 'ln')}
                      onFocus={() => setFocused('ln')} onBlur={() => setFocused('')}
                    />
                  </div>
                </div>

                <div>
                  <label className="block text-[10px] font-black uppercase tracking-widest mb-1.5" style={{ color: 'rgba(255,255,255,0.35)', letterSpacing: '0.16em' }}>Email Address *</label>
                  <input type="email" required value={form.email} onChange={set('email')} placeholder="coach@school.edu"
                    className={inputCls} style={inputStyle(focused === 'em')}
                    onFocus={() => setFocused('em')} onBlur={() => setFocused('')}
                  />
                </div>

                <div>
                  <label className="block text-[10px] font-black uppercase tracking-widest mb-1.5" style={{ color: 'rgba(255,255,255,0.35)', letterSpacing: '0.16em' }}>High School / Program *</label>
                  <input type="text" required value={form.highSchool} onChange={set('highSchool')} placeholder="Lincoln High School"
                    className={inputCls} style={inputStyle(focused === 'hs')}
                    onFocus={() => setFocused('hs')} onBlur={() => setFocused('')}
                  />
                </div>

                {/* Portfolio interest question */}
                <div>
                  <label className="block text-[10px] font-black uppercase tracking-widest mb-3" style={{ color: 'rgba(255,255,255,0.35)', letterSpacing: '0.16em' }}>
                    Interested in Player Portfolio Packages for the Regular Season? *
                  </label>
                  <div className="grid grid-cols-2 gap-2">
                    {[{ val: true, label: 'Yes, tell me more' }, { val: false, label: 'Not right now' }].map(opt => (
                      <button key={String(opt.val)} type="button"
                        onClick={() => setForm(f => ({ ...f, interestedInPortfolios: opt.val }))}
                        className="py-2.5 px-4 text-xs font-black uppercase tracking-widest transition-all"
                        style={{
                          border: `1px solid ${form.interestedInPortfolios === opt.val ? GOLD : 'rgba(255,255,255,0.1)'}`,
                          background: form.interestedInPortfolios === opt.val ? GOLD_DIM : 'transparent',
                          color: form.interestedInPortfolios === opt.val ? GOLD : 'rgba(255,255,255,0.4)',
                          letterSpacing: '0.1em',
                        }}
                      >
                        {form.interestedInPortfolios === opt.val && <Check className="w-3 h-3 inline mr-1.5" />}
                        {opt.label}
                      </button>
                    ))}
                  </div>
                  {form.interestedInPortfolios === true && (
                    <p className="text-xs mt-2 leading-relaxed" style={{ color: 'rgba(255,255,255,0.4)' }}>
                      We'll follow up after checkout with full season portfolio pricing options for your players.
                    </p>
                  )}
                </div>

                <button type="submit" disabled={!valid || loading}
                  className="w-full mt-2 flex items-center justify-center gap-2 py-3.5 font-barlow font-black text-sm uppercase tracking-widest transition-all disabled:opacity-40"
                  style={{ background: GOLD, color: '#0D0D0D', letterSpacing: '0.14em' }}
                  onMouseEnter={e => { if (valid && !loading) e.currentTarget.style.background = '#E8C547'; }}
                  onMouseLeave={e => e.currentTarget.style.background = GOLD}
                >
                  {loading ? <Loader2 className="w-4 h-4 animate-spin" /> : <><ArrowRight className="w-4 h-4" /> Proceed to Checkout</>}
                </button>
              </form>
            </motion.div>
          </motion.div>
        )}
      </AnimatePresence>
    </>
  );
}

src/components/georgia/DaculaGameStats.jsx

import { useState } from 'react';
import { Link } from 'react-router-dom';
import { ChevronDown, ChevronUp, Users, ExternalLink } from 'lucide-react';

const ORANGE = '#FF6A00';

// Dacula player portfolio slug map
const DACULA_PORTFOLIO_SLUGS = {
  'Antonio Case Presley': 'antonio-case-presley',
  'Jerry Levine': 'jerry-levine',
  'Dorian Douglas': 'dorian-douglas',
  'Kamari Trotter': 'kamari-trotter',
  'Omari Alleyne': 'omari-alleyne',
  'Caleb Golding': 'caleb-golding',
  'Tristen Dixon': 'tristen-dixon',
  'Samuel Jacques': 'samuel-jacques',
  'Leslue Malibe': 'leslue-malibe',
  'Douglas Finley': 'douglas-finley',
  'Cameron Brown': 'cameron-brown',
};

function vimeoEmbed(url) {
  const m = url?.match(/vimeo\.com\/(\d+)/);
  return m ? `https://player.vimeo.com/video/${m[1]}` : url;
}

// ── Session 1: June 13-14, 2026 ──────────────────────────────────────────────
const SESSION_1_GAMES = [
  {
    id: 's1g1',
    date: 'Jun 13, 2026',
    time: '9:00 PM',
    court: 'Court 07',
    my_team: 'Dacula - GA',
    opp_team: 'Baldwin - GA',
    my_score: 63,
    opp_score: 43,
    result: 'W',
    film_url: 'https://vimeo.com/1201669006',
    team_stats: {
      my: { pts: 63, ppp: 1.09, ast: 10, reb: 33, oreb: 13, dreb: 20, blk: 1, stl: 13, to: 5, deflections: 11, fouls: 15, def_fouls: 13, charges: 0, kills: 7, efg_pct: '46.6%', to_pct: '7.9%', oreb_pct: '37.1%', ftr: 0.24, two_made: 24, two_att: 43, two_pct: '55.8%', three_made: 2, three_att: 15, three_pct: '13.3%', ft_made: 9, ft_att: 14, ft_pct: '64.3%', scoring_opps: 65, shots: 58, ft_trips: 7, two_rate: '74.1%', three_rate: '25.9%', ft: '9/14' },
      opp: { pts: 43, ppp: 0.74, ast: 8, reb: 30, oreb: 8, dreb: 22, blk: 3, stl: 3, to: 16, deflections: 6, fouls: 12, def_fouls: 12, charges: 0, kills: 5, efg_pct: '41.7%', to_pct: '27.6%', oreb_pct: '28.6%', ftr: 0.38, two_made: 10, two_att: 25, two_pct: '40.0%', three_made: 5, three_att: 17, three_pct: '29.4%', ft_made: 8, ft_att: 16, ft_pct: '50.0%', scoring_opps: 50, shots: 42, ft_trips: 8, two_rate: '59.5%', three_rate: '40.5%', ft: '8/16' },
    },
    my_players: [
      { num: '0', name: 'Jerry Levine', time: '14:14', pts: 9, reb: 2, ast: 2, blk: 0, stl: 4, to: 1, two_a: '3/3', three_a: '1/2', ft_a: '0/0', efg: '90.0%', pm: '+18' },
      { num: '1', name: 'Luke Anderson', time: '16:03', pts: 10, reb: 0, ast: 4, blk: 0, stl: 1, to: 0, two_a: '4/5', three_a: '0/2', ft_a: '2/2', efg: '57.1%', pm: '+22' },
      { num: '3', name: 'Dorian Douglas', time: '18:33', pts: 13, reb: 2, ast: 3, blk: 0, stl: 1, to: 1, two_a: '4/7', three_a: '0/0', ft_a: '5/5', efg: '57.1%', pm: '+14', starter: true },
      { num: '4', name: 'Omari Alleyne', time: '17:05', pts: 0, reb: 0, ast: 1, blk: 0, stl: 2, to: 0, two_a: '0/1', three_a: '0/2', ft_a: '0/0', efg: '0%', pm: '0', starter: true },
      { num: '5', name: 'Joshua Brown', time: '18:11', pts: 4, reb: 1, ast: 1, blk: 0, stl: 0, to: 0, two_a: '2/3', three_a: '0/2', ft_a: '0/0', efg: '40.0%', pm: '+3', starter: true },
      { num: '10', name: 'Antonio Case Presley', time: '19:09', pts: 8, reb: 2, ast: 6, blk: 0, stl: 2, to: 2, two_a: '4/8', three_a: '0/3', ft_a: '0/1', efg: '36.4%', pm: '+13', starter: true },
      { num: '12', name: 'Cameron Brown', time: '2:22', pts: 0, reb: 0, ast: 1, blk: 0, stl: 0, to: 0, two_a: '0/1', three_a: '0/0', ft_a: '0/2', efg: '0%', pm: '-3' },
      { num: '14', name: 'Tristen Dixon', time: '10:22', pts: 8, reb: 1, ast: 3, blk: 0, stl: 2, to: 0, two_a: '4/7', three_a: '0/2', ft_a: '0/0', efg: '44.4%', pm: '+15' },
      { num: '15', name: 'Samuel Jacques', time: '5:35', pts: 5, reb: 0, ast: 2, blk: 0, stl: 0, to: 1, two_a: '1/2', three_a: '1/2', ft_a: '0/0', efg: '62.5%', pm: '+2' },
      { num: '21', name: 'Leslue Malibe', time: '3:57', pts: 0, reb: 0, ast: 1, blk: 0, stl: 1, to: 0, two_a: '0/2', three_a: '0/0', ft_a: '0/2', efg: '0%', pm: '-2' },
      { num: '23', name: 'Douglas Finley', time: '15:13', pts: 2, reb: 2, ast: 3, blk: 1, stl: 0, to: 0, two_a: '0/0', three_a: '0/0', ft_a: '2/2', efg: '—', pm: '+14' },
      { num: '24', name: 'Caleb Golding', time: '9:10', pts: 4, reb: 0, ast: 3, blk: 0, stl: 0, to: 0, two_a: '2/4', three_a: '0/0', ft_a: '0/0', efg: '50.0%', pm: '+4', starter: true },
    ],
    opp_players: [
      { num: '0', name: 'Dillon Wright', time: '24:49', pts: 20, reb: 7, ast: 2, blk: 0, stl: 0, to: 1, two_a: '4/7', three_a: '4/11', ft_a: '0/0', efg: '55.6%', pm: '-15', starter: true },
      { num: '1', name: 'Joetavious Havior', time: '5:36', pts: 0, reb: 2, ast: 0, blk: 0, stl: 0, to: 3, two_a: '0/0', three_a: '0/0', ft_a: '0/2', efg: '—', pm: '-17' },
      { num: '2', name: 'Skyler Williams', time: '10:50', pts: 3, reb: 3, ast: 0, blk: 0, stl: 1, to: 2, two_a: '0/1', three_a: '1/3', ft_a: '0/0', efg: '37.5%', pm: '-15' },
      { num: '4', name: 'Tyson Smith', time: '8:59', pts: 2, reb: 0, ast: 0, blk: 1, stl: 0, to: 1, two_a: '0/0', three_a: '0/1', ft_a: '2/2', efg: '0%', pm: '-17' },
      { num: '11', name: 'Mekhi Worthen', time: '9:45', pts: 0, reb: 0, ast: 0, blk: 0, stl: 0, to: 0, two_a: '0/1', three_a: '0/0', ft_a: '0/0', efg: '0%', pm: '-5' },
      { num: '12', name: 'Bryan Goddard', time: '16:40', pts: 2, reb: 4, ast: 2, blk: 0, stl: 0, to: 1, two_a: '1/2', three_a: '0/1', ft_a: '0/0', efg: '33.3%', pm: '-2', starter: true },
      { num: '13', name: 'Amari Fluellen', time: '8:43', pts: 2, reb: 2, ast: 1, blk: 0, stl: 0, to: 2, two_a: '0/0', three_a: '0/0', ft_a: '2/2', efg: '—', pm: '+6' },
      { num: '14', name: 'Kunta Paschal', time: '18:26', pts: 4, reb: 1, ast: 0, blk: 1, stl: 0, to: 1, two_a: '1/3', three_a: '0/1', ft_a: '2/2', efg: '25.0%', pm: '-1', starter: true },
      { num: '15', name: 'Tyler Spikes', time: '15:16', pts: 6, reb: 8, ast: 3, blk: 0, stl: 0, to: 1, two_a: '3/4', three_a: '0/0', ft_a: '0/2', efg: '75.0%', pm: '+5', starter: true },
      { num: '20', name: 'Jase Wesley', time: '4:16', pts: 0, reb: 0, ast: 0, blk: 1, stl: 0, to: 0, two_a: '0/0', three_a: '0/0', ft_a: '0/0', efg: '—', pm: '-8' },
      { num: '22', name: 'Rodarius James', time: '8:25', pts: 0, reb: 1, ast: 0, blk: 0, stl: 2, to: 1, two_a: '0/1', three_a: '0/0', ft_a: '0/4', efg: '0%', pm: '-13' },
      { num: '23', name: 'Ferdinand Farley', time: '18:10', pts: 4, reb: 0, ast: 0, blk: 0, stl: 0, to: 3, two_a: '1/6', three_a: '0/0', ft_a: '2/2', efg: '16.7%', pm: '-18', starter: true },
    ],
  },
  {
    id: 's1g2',
    date: 'Jun 13, 2026',
    time: '3:00 PM',
    court: 'Court 03',
    my_team: 'Dacula - GA',
    opp_team: 'Chapel Hill - GA',
    my_score: 56,
    opp_score: 57,
    result: 'L',
    film_url: 'https://vimeo.com/1201656871',
    team_stats: {
      my: { pts: 56, ppp: 0.85, ast: 13, reb: 27, oreb: 7, dreb: 20, blk: 1, stl: 8, to: 21, deflections: 1, fouls: 26, def_fouls: 21, charges: 0, kills: 5, efg_pct: '53.5%', to_pct: '32.8%', oreb_pct: '28.0%', ftr: 0.40, two_made: 14, two_att: 28, two_pct: '50.0%', three_made: 6, three_att: 15, three_pct: '40.0%', ft_made: 10, ft_att: 17, ft_pct: '58.8%', scoring_opps: 53, shots: 43, ft_trips: 10, two_rate: '65.1%', three_rate: '34.9%', ft: '10/17' },
      opp: { pts: 57, ppp: 0.86, ast: 15, reb: 35, oreb: 17, dreb: 18, blk: 1, stl: 10, to: 19, deflections: 5, fouls: 14, def_fouls: 11, charges: 0, kills: 5, efg_pct: '40.0%', to_pct: '25.7%', oreb_pct: '45.9%', ftr: 0.42, two_made: 19, two_att: 39, two_pct: '48.7%', three_made: 2, three_att: 16, three_pct: '12.5%', ft_made: 13, ft_att: 23, ft_pct: '56.5%', scoring_opps: 69, shots: 55, ft_trips: 14, two_rate: '70.9%', three_rate: '29.1%', ft: '13/23' },
    },
    my_players: [
      { num: '0', name: 'Jerry Levine', time: '8:36', pts: 3, reb: 0, ast: 0, blk: 0, stl: 0, to: 0, two_a: '0/0', three_a: '1/1', ft_a: '0/0', efg: '150.0%', pm: '-6' },
      { num: '1', name: 'Luke Anderson', time: '12:07', pts: 7, reb: 2, ast: 1, blk: 0, stl: 1, to: 0, two_a: '0/1', three_a: '1/3', ft_a: '4/4', efg: '37.5%', pm: '-3' },
      { num: '3', name: 'Dorian Douglas', time: '25:52', pts: 11, reb: 4, ast: 6, blk: 0, stl: 3, to: 5, two_a: '3/7', three_a: '1/1', ft_a: '2/4', efg: '56.2%', pm: '-2', starter: true },
      { num: '4', name: 'Omari Alleyne', time: '19:24', pts: 6, reb: 5, ast: 1, blk: 0, stl: 1, to: 4, two_a: '1/3', three_a: '1/3', ft_a: '1/1', efg: '41.7%', pm: '-1', starter: true },
      { num: '5', name: 'Joshua Brown', time: '25:38', pts: 6, reb: 3, ast: 0, blk: 0, stl: 0, to: 3, two_a: '3/3', three_a: '0/2', ft_a: '0/0', efg: '60.0%', pm: '-1', starter: true },
      { num: '10', name: 'Antonio Case Presley', time: '27:44', pts: 19, reb: 5, ast: 2, blk: 0, stl: 1, to: 5, two_a: '5/9', three_a: '2/5', ft_a: '3/8', efg: '57.1%', pm: '+4', starter: true },
      { num: '14', name: 'Tristen Dixon', time: '6:09', pts: 2, reb: 1, ast: 0, blk: 0, stl: 0, to: 1, two_a: '1/2', three_a: '0/0', ft_a: '0/0', efg: '50.0%', pm: '+3' },
      { num: '23', name: 'Douglas Finley', time: '9:25', pts: 2, reb: 1, ast: 0, blk: 1, stl: 0, to: 0, two_a: '1/2', three_a: '0/0', ft_a: '0/0', efg: '50.0%', pm: '0' },
      { num: '24', name: 'Caleb Golding', time: '20:15', pts: 0, reb: 3, ast: 3, blk: 0, stl: 2, to: 3, two_a: '0/1', three_a: '0/0', ft_a: '0/0', efg: '0%', pm: '+1', starter: true },
    ],
    opp_players: [
      { num: '0', name: 'Amare McKinley', time: '23:35', pts: 8, reb: 4, ast: 3, blk: 0, stl: 2, to: 2, two_a: '4/4', three_a: '0/0', ft_a: '0/0', efg: '100.0%', pm: '0', starter: true },
      { num: '1', name: 'William Pass', time: '25:15', pts: 11, reb: 3, ast: 2, blk: 0, stl: 3, to: 0, two_a: '3/8', three_a: '1/8', ft_a: '2/5', efg: '28.1%', pm: '-2', starter: true },
      { num: '3', name: 'Ibn Atkins', time: '18:31', pts: 12, reb: 2, ast: 1, blk: 0, stl: 3, to: 5, two_a: '3/9', three_a: '0/2', ft_a: '6/8', efg: '27.3%', pm: '+13' },
      { num: '4', name: 'Elijah Carnes', time: '19:16', pts: 8, reb: 1, ast: 1, blk: 0, stl: 1, to: 4, two_a: '2/5', three_a: '1/5', ft_a: '1/3', efg: '35.0%', pm: '-5', starter: true },
      { num: '5', name: 'Jakobe Fleming', time: '27:11', pts: 12, reb: 7, ast: 7, blk: 0, stl: 1, to: 3, two_a: '4/9', three_a: '0/0', ft_a: '4/7', efg: '44.4%', pm: '+8', starter: true },
      { num: '10', name: 'Grayson Barnett', time: '11:53', pts: 0, reb: 4, ast: 1, blk: 0, stl: 0, to: 1, two_a: '0/0', three_a: '0/1', ft_a: '0/0', efg: '0%', pm: '+8' },
      { num: '11', name: 'Ares Cosey', time: '6:00', pts: 2, reb: 2, ast: 0, blk: 0, stl: 0, to: 0, two_a: '1/1', three_a: '0/0', ft_a: '0/0', efg: '100.0%', pm: '-3' },
      { num: '12', name: 'Perrance Johnson', time: '5:06', pts: 0, reb: 3, ast: 0, blk: 1, stl: 0, to: 0, two_a: '0/0', three_a: '0/0', ft_a: '0/0', efg: '—', pm: '+7' },
      { num: '23', name: 'Chadwick Jordan', time: '16:59', pts: 4, reb: 6, ast: 0, blk: 0, stl: 0, to: 4, two_a: '2/3', three_a: '0/0', ft_a: '0/0', efg: '66.7%', pm: '-16', starter: true },
    ],
  },
  {
    id: 's1g3',
    date: 'Jun 14, 2026',
    time: '10:00 AM',
    court: 'Court 12',
    my_team: 'Dacula - GA',
    opp_team: 'Lanier - GA',
    my_score: 68,
    opp_score: 47,
    result: 'W',
    film_url: 'https://vimeo.com/1201872100',
    team_stats: {
      my: { pts: 68, ppp: null, ast: 9, reb: 26, oreb: null, dreb: null, blk: 0, stl: 10, to: 12, deflections: null, fouls: null, def_fouls: null, charges: 0, kills: null, efg_pct: '51.0%', to_pct: null, oreb_pct: null, ftr: null, two_made: 20, two_att: 30, two_pct: '66.7%', three_made: 3, three_att: 18, three_pct: '16.7%', ft_made: 19, ft_att: 23, ft_pct: '82.6%', scoring_opps: null, shots: null, ft_trips: null, two_rate: null, three_rate: null, ft: '19/23' },
      opp: { pts: 47, ppp: null, ast: 4, reb: 25, oreb: null, dreb: null, blk: 1, stl: 7, to: 17, deflections: null, fouls: null, def_fouls: null, charges: 0, kills: null, efg_pct: '45.5%', to_pct: null, oreb_pct: null, ftr: null, two_made: 12, two_att: 22, two_pct: '54.5%', three_made: 2, three_att: 11, three_pct: '18.2%', ft_made: 17, ft_att: 28, ft_pct: '60.7%', scoring_opps: null, shots: null, ft_trips: null, two_rate: null, three_rate: null, ft: '17/28' },
    },
    my_players: [
      { num: '0', name: 'Jerry Levine', time: '12:46', pts: 14, reb: 0, ast: 0, blk: 0, stl: 0, to: 0, two_a: '2/3', three_a: '2/3', ft_a: '4/4', efg: '83.3%', pm: '+13' },
      { num: '1', name: 'Luke Anderson', time: '17:06', pts: 15, reb: 3, ast: 2, blk: 0, stl: 3, to: 1, two_a: '6/7', three_a: '0/1', ft_a: '3/3', efg: '75.0%', pm: '+24' },
      { num: '3', name: 'Dorian Douglas', time: '21:18', pts: 4, reb: 3, ast: 0, blk: 0, stl: 1, to: 0, two_a: '2/4', three_a: '0/3', ft_a: '0/0', efg: '28.6%', pm: '+10', starter: true },
      { num: '4', name: 'Omari Alleyne', time: '16:13', pts: 8, reb: 5, ast: 1, blk: 0, stl: 1, to: 1, two_a: '2/6', three_a: '0/3', ft_a: '4/6', efg: '22.2%', pm: '+3', starter: true },
      { num: '5', name: 'Joshua Brown', time: '21:42', pts: 5, reb: 0, ast: 0, blk: 0, stl: 1, to: 3, two_a: '1/1', three_a: '1/3', ft_a: '0/0', efg: '62.5%', pm: '+8', starter: true },
      { num: '10', name: 'Antonio Case Presley', time: '21:51', pts: 10, reb: 5, ast: 4, blk: 0, stl: 3, to: 2, two_a: '3/5', three_a: '0/3', ft_a: '4/4', efg: '37.5%', pm: '+7', starter: true },
      { num: '12', name: 'Cameron Brown', time: '2:15', pts: 0, reb: 1, ast: 0, blk: 0, stl: 0, to: 0, two_a: '0/0', three_a: '0/1', ft_a: '0/0', efg: '0%', pm: '+1' },
      { num: '14', name: 'Tristen Dixon', time: '5:08', pts: 0, reb: 1, ast: 0, blk: 0, stl: 1, to: 2, two_a: '0/0', three_a: '0/0', ft_a: '0/0', efg: '—', pm: '+12' },
      { num: '15', name: 'Samuel Jacques', time: '2:40', pts: 0, reb: 1, ast: 0, blk: 0, stl: 0, to: 1, two_a: '0/0', three_a: '0/1', ft_a: '0/2', efg: '0%', pm: '+1' },
      { num: '21', name: 'Leslue Malibe', time: '2:34', pts: 3, reb: 0, ast: 0, blk: 0, stl: 0, to: 1, two_a: '1/1', three_a: '0/0', ft_a: '1/1', efg: '100.0%', pm: '+1' },
      { num: '23', name: 'Douglas Finley', time: '10:26', pts: 4, reb: 1, ast: 1, blk: 0, stl: 0, to: 0, two_a: '2/2', three_a: '0/0', ft_a: '0/0', efg: '100.0%', pm: '+20' },
      { num: '24', name: 'Caleb Golding', time: '15:54', pts: 5, reb: 4, ast: 1, blk: 0, stl: 0, to: 1, two_a: '1/1', three_a: '0/0', ft_a: '3/3', efg: '100.0%', pm: '+5', starter: true },
    ],
    opp_players: [
      { num: '0', name: 'Marcus Green', time: '7:44', pts: 3, reb: 2, ast: 0, blk: 0, stl: 0, to: 2, two_a: '0/0', three_a: '1/2', ft_a: '0/0', efg: '75.0%', pm: '+6' },
      { num: '3', name: 'Reshard Slaughter', time: '9:23', pts: 11, reb: 3, ast: 0, blk: 0, stl: 0, to: 0, two_a: '3/4', three_a: '0/1', ft_a: '5/5', efg: '60.0%', pm: '+10' },
      { num: '4', name: 'Kharmelo Morgan', time: '6:47', pts: 2, reb: 0, ast: 0, blk: 0, stl: 1, to: 1, two_a: '1/1', three_a: '0/0', ft_a: '0/0', efg: '100.0%', pm: '+2' },
      { num: '5', name: 'EJ Hines', time: '7:08', pts: 0, reb: 0, ast: 0, blk: 0, stl: 0, to: 1, two_a: '0/0', three_a: '0/0', ft_a: '0/0', efg: '—', pm: '+2' },
      { num: '10', name: 'Noah James', time: '15:30', pts: 4, reb: 2, ast: 0, blk: 0, stl: 3, to: 2, two_a: '1/3', three_a: '0/0', ft_a: '2/2', efg: '33.3%', pm: '-21', starter: true },
      { num: '11', name: 'DJ Bembery', time: '9:49', pts: 8, reb: 1, ast: 3, blk: 0, stl: 1, to: 0, two_a: '3/4', three_a: '0/0', ft_a: '2/4', efg: '75.0%', pm: '+6' },
      { num: '12', name: 'Reed Neilan', time: '15:28', pts: 3, reb: 2, ast: 0, blk: 0, stl: 0, to: 2, two_a: '0/1', three_a: '1/2', ft_a: '0/0', efg: '50.0%', pm: '-14', starter: true },
      { num: '13', name: 'Bronson Nathaniel', time: '14:48', pts: 2, reb: 1, ast: 0, blk: 0, stl: 0, to: 2, two_a: '0/1', three_a: '0/0', ft_a: '2/2', efg: '0%', pm: '-25', starter: true },
      { num: '15', name: 'Michael Melancon', time: '7:48', pts: 0, reb: 1, ast: 0, blk: 0, stl: 0, to: 0, two_a: '0/0', three_a: '0/1', ft_a: '0/0', efg: '0%', pm: '-18' },
      { num: '20', name: 'Nik Siut', time: '14:50', pts: 0, reb: 1, ast: 1, blk: 0, stl: 1, to: 3, two_a: '0/2', three_a: '0/3', ft_a: '0/0', efg: '0%', pm: '-18', starter: true },
      { num: '21', name: 'Demez Mann', time: '9:23', pts: 2, reb: 3, ast: 0, blk: 1, stl: 0, to: 0, two_a: '0/0', three_a: '0/1', ft_a: '2/6', efg: '0%', pm: '+10' },
      { num: '22', name: 'Moses Alls', time: '7:48', pts: 4, reb: 3, ast: 0, blk: 0, stl: 0, to: 0, two_a: '2/2', three_a: '0/0', ft_a: '0/2', efg: '100.0%', pm: '+6' },
      { num: '23', name: 'Dylan Stewart', time: '14:27', pts: 8, reb: 1, ast: 0, blk: 0, stl: 1, to: 2, two_a: '2/3', three_a: '0/1', ft_a: '4/7', efg: '50.0%', pm: '-27', starter: true },
      { num: '24', name: 'Barack Mayenga', time: '9:02', pts: 0, reb: 1, ast: 0, blk: 0, stl: 0, to: 1, two_a: '0/1', three_a: '0/0', ft_a: '0/0', efg: '0%', pm: '-24' },
    ],
  },
  {
    id: 's1g4',
    date: 'Jun 14, 2026',
    time: '3:00 PM',
    court: 'Court 02',
    my_team: 'Dacula - GA',
    opp_team: 'Carrollton - GA',
    my_score: 44,
    opp_score: 40,
    result: 'W',
    film_url: 'https://vimeo.com/1201604672',
    team_stats: {
      my: { pts: 44, ppp: 0.88, ast: 8, reb: 33, oreb: 16, dreb: 17, blk: 1, stl: 4, to: 9, deflections: 4, fouls: 12, def_fouls: 10, charges: 0, kills: 4, efg_pct: '35.2%', to_pct: '14.3%', oreb_pct: '41.0%', ftr: 0.19, two_made: 10, two_att: 29, two_pct: '34.5%', three_made: 6, three_att: 25, three_pct: '24.0%', ft_made: 6, ft_att: 10, ft_pct: '60.0%', scoring_opps: 60, shots: 54, ft_trips: 6, two_rate: '53.7%', three_rate: '46.3%', ft: '6/10' },
      opp: { pts: 40, ppp: 0.78, ast: 12, reb: 31, oreb: 8, dreb: 23, blk: 3, stl: 4, to: 12, deflections: 5, fouls: 9, def_fouls: 9, charges: 0, kills: 5, efg_pct: '39.0%', to_pct: '22.6%', oreb_pct: '32.0%', ftr: 0.22, two_made: 13, two_att: 30, two_pct: '43.3%', three_made: 2, three_att: 11, three_pct: '18.2%', ft_made: 8, ft_att: 9, ft_pct: '88.9%', scoring_opps: 46, shots: 41, ft_trips: 5, two_rate: '73.2%', three_rate: '26.8%', ft: '8/9' },
    },
    my_players: [
      { num: '0', name: 'Jerry Levine', time: '12:35', pts: 6, reb: 1, ast: 0, blk: 0, stl: 1, to: 0, two_a: '0/2', three_a: '2/5', ft_a: '0/0', efg: '42.9%', pm: '0' },
      { num: '1', name: 'Luke Anderson', time: '14:29', pts: 0, reb: 2, ast: 1, blk: 0, stl: 0, to: 1, two_a: '0/2', three_a: '0/1', ft_a: '0/0', efg: '0%', pm: '-2' },
      { num: '3', name: 'Dorian Douglas', time: '27:34', pts: 4, reb: 3, ast: 4, blk: 0, stl: 0, to: 2, two_a: '1/4', three_a: '0/4', ft_a: '2/2', efg: '12.5%', pm: '+5', starter: true },
      { num: '4', name: 'Omari Alleyne', time: '23:04', pts: 3, reb: 5, ast: 1, blk: 0, stl: 1, to: 2, two_a: '0/5', three_a: '1/5', ft_a: '0/0', efg: '15.0%', pm: '+11', starter: true },
      { num: '5', name: 'Joshua Brown', time: '26:54', pts: 7, reb: 3, ast: 1, blk: 0, stl: 1, to: 3, two_a: '2/3', three_a: '1/5', ft_a: '0/1', efg: '43.8%', pm: '+5', starter: true },
      { num: '10', name: 'Antonio Case Presley', time: '27:09', pts: 22, reb: 8, ast: 1, blk: 1, stl: 1, to: 1, two_a: '6/10', three_a: '2/5', ft_a: '4/7', efg: '60.0%', pm: '+7', starter: true },
      { num: '23', name: 'Douglas Finley', time: '7:55', pts: 0, reb: 1, ast: 0, blk: 0, stl: 0, to: 0, two_a: '0/0', three_a: '0/0', ft_a: '0/0', efg: '—', pm: '-7' },
      { num: '24', name: 'Caleb Golding', time: '15:15', pts: 2, reb: 6, ast: 0, blk: 0, stl: 0, to: 0, two_a: '1/3', three_a: '0/0', ft_a: '0/0', efg: '33.3%', pm: '+1', starter: true },
    ],
    opp_players: [
      { num: '0', name: 'Rhylan Ellison', time: '24:45', pts: 2, reb: 2, ast: 0, blk: 0, stl: 2, to: 1, two_a: '1/4', three_a: '0/2', ft_a: '0/0', efg: '16.7%', pm: '+2', starter: true },
      { num: '1', name: 'Dre Steele', time: '16:18', pts: 0, reb: 5, ast: 2, blk: 0, stl: 0, to: 1, two_a: '0/1', three_a: '0/0', ft_a: '0/0', efg: '0%', pm: '+1', starter: true },
      { num: '2', name: 'John Clinton', time: '22:29', pts: 5, reb: 1, ast: 3, blk: 0, stl: 0, to: 2, two_a: '0/4', three_a: '1/5', ft_a: '2/2', efg: '16.7%', pm: '-2' },
      { num: '3', name: 'Urhiyah George', time: '29:09', pts: 17, reb: 6, ast: 2, blk: 1, stl: 0, to: 3, two_a: '5/7', three_a: '1/1', ft_a: '4/5', efg: '81.2%', pm: '-6', starter: true },
      { num: '4', name: 'Adein Dobbs', time: '12:33', pts: 0, reb: 2, ast: 3, blk: 1, stl: 1, to: 1, two_a: '0/2', three_a: '0/2', ft_a: '0/0', efg: '0%', pm: '-10' },
      { num: '5', name: 'Peyton Moore', time: '28:23', pts: 12, reb: 5, ast: 1, blk: 1, stl: 1, to: 1, two_a: '5/9', three_a: '0/1', ft_a: '2/2', efg: '50.0%', pm: '-2', starter: true },
      { num: '35', name: 'Billy Brimer', time: '14:00', pts: 4, reb: 6, ast: 1, blk: 0, stl: 0, to: 2, two_a: '2/3', three_a: '0/0', ft_a: '0/0', efg: '66.7%', pm: '+1', starter: true },
    ],
  },
];

// ── Session 2: June 26-28, 2026 ──────────────────────────────────────────────
const SESSION_2_GAMES = [
  {
    id: 's2g1',
    date: 'Jun 26, 2026',
    time: '2:00 PM',
    court: 'Court 10',
    my_team: 'Dacula HS - GA',
    opp_team: 'Episcopal School of Jax - FL',
    my_score: 61,
    opp_score: 60,
    result: 'W',
    film_url: 'https://vimeo.com/1205612488',
    team_stats: {
      my: { pts: 61, ppp: 1.17, ast: 13, reb: 21, oreb: 12, dreb: 9, blk: 0, stl: 10, to: 8, deflections: 14, fouls: 21, def_fouls: 17, charges: 0, kills: 3, efg_pct: '51.1%', to_pct: '14.8%', oreb_pct: '42.9%', ftr: 0.39, two_made: 7, two_att: 22, two_pct: '31.8%', three_made: 11, three_att: 24, three_pct: '45.8%', ft_made: 14, ft_att: 18, ft_pct: '77.8%', scoring_opps: 56, shots: 46, ft_trips: 10, two_rate: '47.8%', three_rate: '52.2%', ft: '14/18' },
      opp: { pts: 60, ppp: 1.11, ast: 14, reb: 22, oreb: 6, dreb: 16, blk: 2, stl: 3, to: 14, deflections: 5, fouls: 14, def_fouls: 14, charges: 0, kills: 5, efg_pct: '64.9%', to_pct: '27.5%', oreb_pct: '40.0%', ftr: 0.43, two_made: 15, two_att: 23, two_pct: '65.2%', three_made: 6, three_att: 14, three_pct: '42.9%', ft_made: 12, ft_att: 16, ft_pct: '75.0%', scoring_opps: 46, shots: 37, ft_trips: 9, two_rate: '62.2%', three_rate: '37.8%', ft: '12/16' },
    },
    my_players: [
      { num: '0', name: 'Jerry Levine', time: '18:20', pts: 9, reb: 2, ast: 1, blk: 0, stl: 1, to: 0, two_a: '0/3', three_a: '3/6', ft_a: '0/0', efg: '50.0%', pm: '-5' },
      { num: '1', name: 'Luke Anderson', time: '13:09', pts: 0, reb: 1, ast: 3, blk: 0, stl: 3, to: 1, two_a: '0/1', three_a: '0/0', ft_a: '0/0', efg: '0%', pm: '+15', starter: true },
      { num: '2', name: 'Kamari Trotter', time: '17:57', pts: 9, reb: 0, ast: 0, blk: 0, stl: 0, to: 1, two_a: '0/3', three_a: '3/5', ft_a: '0/0', efg: '56.2%', pm: '+14' },
      { num: '3', name: 'Dorian Douglas', time: '25:53', pts: 4, reb: 2, ast: 3, blk: 0, stl: 2, to: 0, two_a: '0/2', three_a: '0/3', ft_a: '0/0', efg: '0%', pm: '-3', starter: true },
      { num: '4', name: 'Omari Alleyne', time: '16:07', pts: 5, reb: 5, ast: 1, blk: 0, stl: 1, to: 3, two_a: '1/1', three_a: '1/1', ft_a: '0/0', efg: '125.0%', pm: '-5', starter: true },
      { num: '10', name: 'Antonio Case Presley', time: '26:43', pts: 24, reb: 3, ast: 0, blk: 0, stl: 1, to: 2, two_a: '3/8', three_a: '4/9', ft_a: '0/0', efg: '52.9%', pm: '+3', starter: true },
      { num: '11', name: '#11', time: '4:18', pts: 0, reb: 0, ast: 0, blk: 0, stl: 0, to: 1, two_a: '0/0', three_a: '0/0', ft_a: '0/0', efg: '—', pm: '-6' },
      { num: '14', name: 'Tristen Dixon', time: '2:57', pts: 2, reb: 0, ast: 0, blk: 0, stl: 0, to: 0, two_a: '1/1', three_a: '0/0', ft_a: '0/0', efg: '100.0%', pm: '-9' },
      { num: '23', name: 'Douglas Finley', time: '4:39', pts: 0, reb: 0, ast: 0, blk: 0, stl: 0, to: 0, two_a: '0/0', three_a: '0/0', ft_a: '0/0', efg: '—', pm: '-11' },
      { num: '24', name: 'Caleb Golding', time: '19:53', pts: 6, reb: 5, ast: 5, blk: 0, stl: 2, to: 0, two_a: '1/2', three_a: '0/0', ft_a: '0/0', efg: '50.0%', pm: '+12', starter: true },
    ],
    opp_players: [
      { num: '0', name: 'Noah Omalley', time: '20:57', pts: 2, reb: 1, ast: 3, blk: 0, stl: 0, to: 1, two_a: '0/1', three_a: '0/1', ft_a: '0/0', efg: '0%', pm: '+3' },
      { num: '2', name: 'Rowan Myles', time: '28:30', pts: 4, reb: 7, ast: 0, blk: 2, stl: 1, to: 2, two_a: '1/3', three_a: '0/0', ft_a: '0/0', efg: '33.3%', pm: '-8', starter: true },
      { num: '3', name: 'Johnny Froats', time: '17:21', pts: 2, reb: 0, ast: 5, blk: 0, stl: 0, to: 1, two_a: '1/1', three_a: '0/0', ft_a: '0/0', efg: '100.0%', pm: '-3', starter: true },
      { num: '4', name: 'Drew Jackson', time: '28:10', pts: 18, reb: 5, ast: 4, blk: 0, stl: 1, to: 3, two_a: '5/6', three_a: '1/5', ft_a: '0/0', efg: '59.1%', pm: '-1', starter: true },
      { num: '11', name: 'Hays Jackson', time: '15:45', pts: 8, reb: 2, ast: 1, blk: 0, stl: 0, to: 0, two_a: '0/2', three_a: '2/3', ft_a: '0/0', efg: '60.0%', pm: '-7' },
      { num: '15', name: 'James Ryan', time: '4:33', pts: 3, reb: 0, ast: 1, blk: 0, stl: 0, to: 0, two_a: '0/0', three_a: '1/2', ft_a: '0/0', efg: '75.0%', pm: '+9' },
      { num: '33', name: 'Will Rydzewski', time: '24:55', pts: 23, reb: 2, ast: 0, blk: 0, stl: 1, to: 6, two_a: '8/10', three_a: '2/3', ft_a: '0/0', efg: '84.6%', pm: '+1', starter: true },
    ],
  },
  {
    id: 's2g2',
    date: 'Jun 26, 2026',
    time: '4:00 PM',
    court: 'Court 09',
    my_team: 'Dacula HS - GA',
    opp_team: 'Hazel Green - AL',
    my_score: 49,
    opp_score: 57,
    result: 'L',
    film_url: 'https://vimeo.com/1205607146',
    team_stats: {
      my: { pts: 49, ppp: 0.89, ast: 8, reb: 30, oreb: 11, dreb: 19, blk: 0, stl: 3, to: 6, deflections: 6, fouls: 14, def_fouls: 14, charges: 0, kills: 3, efg_pct: '41.8%', to_pct: '9.8%', oreb_pct: '28.2%', ftr: 0.20, two_made: 14, two_att: 31, two_pct: '45.2%', three_made: 6, three_att: 24, three_pct: '25.0%', ft_made: 3, ft_att: 11, ft_pct: '27.3%', scoring_opps: 60, shots: 55, ft_trips: 5, two_rate: '56.4%', three_rate: '43.6%', ft: '3/11' },
      opp: { pts: 57, ppp: 1.02, ast: 7, reb: 39, oreb: 11, dreb: 28, blk: 2, stl: 2, to: 8, deflections: 4, fouls: 10, def_fouls: 9, charges: 0, kills: 4, efg_pct: '49.0%', to_pct: '14.3%', oreb_pct: '36.7%', ftr: 0.44, two_made: 22, two_att: 41, two_pct: '53.7%', three_made: 1, three_att: 7, three_pct: '14.3%', ft_made: 10, ft_att: 21, ft_pct: '47.6%', scoring_opps: 59, shots: 48, ft_trips: 11, two_rate: '85.4%', three_rate: '14.6%', ft: '10/21' },
    },
    my_players: [
      { num: '0', name: 'Jerry Levine', time: '15:57', pts: 9, reb: 0, ast: 1, blk: 0, stl: 0, to: 0, two_a: '0/0', three_a: '2/6', ft_a: '0/0', efg: '50.0%', pm: '-1' },
      { num: '1', name: 'Luke Anderson', time: '15:49', pts: 6, reb: 4, ast: 0, blk: 0, stl: 0, to: 0, two_a: '3/7', three_a: '0/3', ft_a: '0/0', efg: '30.0%', pm: '-6', starter: true },
      { num: '2', name: 'Kamari Trotter', time: '12:18', pts: 3, reb: 3, ast: 0, blk: 0, stl: 0, to: 0, two_a: '0/2', three_a: '1/2', ft_a: '0/0', efg: '37.5%', pm: '-10' },
      { num: '3', name: 'Dorian Douglas', time: '23:55', pts: 4, reb: 2, ast: 3, blk: 0, stl: 0, to: 2, two_a: '2/4', three_a: '0/1', ft_a: '0/0', efg: '40.0%', pm: '0', starter: true },
      { num: '4', name: 'Omari Alleyne', time: '18:24', pts: 3, reb: 3, ast: 1, blk: 0, stl: 1, to: 2, two_a: '0/0', three_a: '1/2', ft_a: '0/0', efg: '75.0%', pm: '-4', starter: true },
      { num: '10', name: 'Antonio Case Presley', time: '25:26', pts: 16, reb: 5, ast: 1, blk: 0, stl: 1, to: 1, two_a: '5/11', three_a: '2/9', ft_a: '0/0', efg: '40.0%', pm: '0', starter: true },
      { num: '11', name: '#11', time: '8:58', pts: 0, reb: 2, ast: 0, blk: 0, stl: 0, to: 0, two_a: '0/2', three_a: '0/0', ft_a: '0/0', efg: '0%', pm: '-4' },
      { num: '14', name: 'Tristen Dixon', time: '5:44', pts: 0, reb: 2, ast: 0, blk: 0, stl: 1, to: 0, two_a: '0/0', three_a: '0/1', ft_a: '0/0', efg: '0%', pm: '-5' },
      { num: '23', name: 'Douglas Finley', time: '4:23', pts: 0, reb: 2, ast: 0, blk: 0, stl: 0, to: 0, two_a: '0/1', three_a: '0/0', ft_a: '0/0', efg: '0%', pm: '-2' },
      { num: '24', name: 'Caleb Golding', time: '19:00', pts: 6, reb: 5, ast: 2, blk: 0, stl: 0, to: 0, two_a: '3/3', three_a: '0/0', ft_a: '0/0', efg: '100.0%', pm: '-8', starter: true },
    ],
    opp_players: [
      { num: '0', name: 'Jaden Hasberry', time: '23:03', pts: 19, reb: 3, ast: 3, blk: 1, stl: 0, to: 2, two_a: '8/12', three_a: '1/4', ft_a: '0/0', efg: '59.4%', pm: '+17', starter: true },
      { num: '1', name: 'Kareem Thomas', time: '19:02', pts: 6, reb: 8, ast: 0, blk: 1, stl: 0, to: 2, two_a: '2/2', three_a: '0/0', ft_a: '0/0', efg: '100.0%', pm: '+13', starter: true },
      { num: '2', name: 'Christian Allen', time: '19:07', pts: 12, reb: 7, ast: 1, blk: 0, stl: 1, to: 1, two_a: '5/11', three_a: '0/0', ft_a: '0/0', efg: '45.5%', pm: '+11', starter: true },
      { num: '4', name: 'Brayden Baker', time: '17:43', pts: 4, reb: 4, ast: 1, blk: 0, stl: 0, to: 1, two_a: '2/2', three_a: '0/0', ft_a: '0/0', efg: '100.0%', pm: '+17', starter: true },
      { num: '5', name: 'Braylin Brown', time: '12:43', pts: 2, reb: 5, ast: 0, blk: 0, stl: 0, to: 1, two_a: '1/3', three_a: '0/0', ft_a: '0/0', efg: '33.3%', pm: '-1' },
      { num: '10', name: 'Christian Willingham', time: '16:11', pts: 8, reb: 2, ast: 0, blk: 0, stl: 0, to: 0, two_a: '1/3', three_a: '0/2', ft_a: '0/0', efg: '20.0%', pm: '-5' },
      { num: '24', name: 'Hayden Hyatt', time: '20:58', pts: 6, reb: 3, ast: 2, blk: 0, stl: 0, to: 0, two_a: '3/6', three_a: '0/0', ft_a: '0/0', efg: '50.0%', pm: '+13', starter: true },
    ],
  },
  {
    id: 's2g3',
    date: 'Jun 28, 2026',
    time: '9:00 AM',
    court: 'Court 10',
    my_team: 'Dacula HS - GA',
    opp_team: 'North Mecklenburg - NC',
    my_score: 52,
    opp_score: 40,
    result: 'W',
    film_url: 'https://vimeo.com/1205613868',
    team_stats: {
      my: { pts: 52, ppp: 0.87, ast: 12, reb: 22, oreb: 7, dreb: 15, blk: 1, stl: 9, to: 20, deflections: 12, fouls: 16, def_fouls: 13, charges: 0, kills: 10, efg_pct: '51.3%', to_pct: '33.9%', oreb_pct: '30.4%', ftr: 0.59, two_made: 11, two_att: 28, two_pct: '39.3%', three_made: 6, three_att: 11, three_pct: '54.5%', ft_made: 12, ft_att: 23, ft_pct: '52.2%', scoring_opps: 51, shots: 39, ft_trips: 12, two_rate: '71.8%', three_rate: '28.2%', ft: '12/23' },
      opp: { pts: 40, ppp: 0.65, ast: 9, reb: 27, oreb: 11, dreb: 16, blk: 1, stl: 3, to: 23, deflections: 4, fouls: 19, def_fouls: 16, charges: 0, kills: 5, efg_pct: '32.6%', to_pct: '33.3%', oreb_pct: '42.3%', ftr: 0.26, two_made: 6, two_att: 21, two_pct: '28.6%', three_made: 6, three_att: 25, three_pct: '24.0%', ft_made: 10, ft_att: 12, ft_pct: '83.3%', scoring_opps: 52, shots: 46, ft_trips: 6, two_rate: '45.7%', three_rate: '54.3%', ft: '10/12' },
    },
    my_players: [
      { num: '0', name: 'Jerry Levine', time: '7:53', pts: 3, reb: 1, ast: 0, blk: 0, stl: 0, to: 0, two_a: '0/1', three_a: '1/2', ft_a: '0/0', efg: '50.0%', pm: '-1' },
      { num: '1', name: 'Luke Anderson', time: '17:28', pts: 9, reb: 3, ast: 0, blk: 0, stl: 2, to: 1, two_a: '1/1', three_a: '1/1', ft_a: '0/0', efg: '125.0%', pm: '0', starter: true },
      { num: '2', name: 'Kamari Trotter', time: '16:23', pts: 9, reb: 5, ast: 1, blk: 0, stl: 2, to: 3, two_a: '3/5', three_a: '1/1', ft_a: '0/0', efg: '75.0%', pm: '+12' },
      { num: '3', name: 'Dorian Douglas', time: '19:03', pts: 6, reb: 1, ast: 2, blk: 0, stl: 0, to: 2, two_a: '2/5', three_a: '0/0', ft_a: '0/0', efg: '40.0%', pm: '+5', starter: true },
      { num: '4', name: 'Omari Alleyne', time: '23:21', pts: 6, reb: 3, ast: 4, blk: 0, stl: 2, to: 5, two_a: '0/3', three_a: '2/3', ft_a: '0/0', efg: '50.0%', pm: '+17', starter: true },
      { num: '10', name: 'Antonio Case Presley', time: '26:52', pts: 15, reb: 5, ast: 3, blk: 0, stl: 2, to: 5, two_a: '3/8', three_a: '1/3', ft_a: '0/0', efg: '40.9%', pm: '+17', starter: true },
      { num: '11', name: '#11', time: '3:27', pts: 0, reb: 0, ast: 0, blk: 0, stl: 0, to: 2, two_a: '0/0', three_a: '0/0', ft_a: '0/0', efg: '—', pm: '-3' },
      { num: '14', name: 'Tristen Dixon', time: '4:45', pts: 0, reb: 0, ast: 0, blk: 0, stl: 0, to: 0, two_a: '0/1', three_a: '0/1', ft_a: '0/0', efg: '0%', pm: '-1' },
      { num: '23', name: 'Douglas Finley', time: '3:43', pts: 0, reb: 0, ast: 0, blk: 0, stl: 0, to: 0, two_a: '0/0', three_a: '0/0', ft_a: '0/0', efg: '—', pm: '-2' },
      { num: '24', name: 'Caleb Golding', time: '27:28', pts: 4, reb: 4, ast: 2, blk: 1, stl: 1, to: 2, two_a: '2/4', three_a: '0/0', ft_a: '0/0', efg: '50.0%', pm: '+16', starter: true },
    ],
    opp_players: [
      { num: '3', name: 'Alek Lewandowski', time: '23:06', pts: 8, reb: 4, ast: 0, blk: 0, stl: 0, to: 3, two_a: '1/1', three_a: '2/11', ft_a: '0/0', efg: '33.3%', pm: '-12', starter: true },
      { num: '10', name: 'DJ Lindsey', time: '25:35', pts: 12, reb: 4, ast: 3, blk: 0, stl: 0, to: 5, two_a: '0/4', three_a: '2/10', ft_a: '0/0', efg: '21.4%', pm: '-6', starter: true },
      { num: '11', name: "Cha' Den Traylor", time: '24:25', pts: 0, reb: 3, ast: 3, blk: 0, stl: 0, to: 4, two_a: '0/2', three_a: '0/0', ft_a: '0/0', efg: '0%', pm: '-13', starter: true },
      { num: '24', name: 'Ethan Pack', time: '18:56', pts: 5, reb: 5, ast: 0, blk: 0, stl: 1, to: 2, two_a: '0/4', three_a: '1/1', ft_a: '0/0', efg: '30.0%', pm: '-13', starter: true },
      { num: '50', name: 'Kingsley Ojukwu', time: '5:25', pts: 6, reb: 2, ast: 0, blk: 0, stl: 1, to: 0, two_a: '3/3', three_a: '0/0', ft_a: '0/0', efg: '100.0%', pm: '+6' },
    ],
  },
  {
    id: 's2g4',
    date: 'Jun 28, 2026',
    time: '1:00 PM',
    court: 'Court 07',
    my_team: 'Dacula HS - GA',
    opp_team: 'Chapel Hill HS - GA',
    my_score: null,
    opp_score: null,
    result: null,
    film_url: 'https://vimeo.com/1205601287',
    team_stats: null,
    my_players: [],
    opp_players: [],
  },
];

// ── Sub-components ────────────────────────────────────────────────────────────

function PlayerTable({ players, teamLabel, slugMap }) {
  if (!players || players.length === 0) return null;
  return (
    <div className="overflow-x-auto">
      <p className="text-xs font-bold uppercase tracking-widest mb-2 px-1" style={{ color: ORANGE }}>{teamLabel}</p>
      <table className="w-full text-xs min-w-[700px]">
        <thead>
          <tr className="border-b" style={{ borderColor: 'rgba(255,255,255,0.07)' }}>
            {['#', 'Player', 'Time', 'PTS', 'REB', 'AST', 'BLK', 'STL', 'TO', '2Pt/A', '3Pt/A', 'FT/A', 'EFG%', '+/-'].map(h => (
              <th key={h} className="text-left py-1.5 px-1 text-gray-600 font-bold uppercase tracking-wider whitespace-nowrap">{h}</th>
            ))}
          </tr>
        </thead>
        <tbody>
          {players.map((p, i) => {
            const slug = slugMap[p.name];
            return (
              <tr key={i} className="border-b" style={{ borderColor: 'rgba(255,255,255,0.03)' }}>
                <td className="py-1.5 px-1 text-gray-500">{p.num}</td>
                <td className="py-1.5 px-1 text-white font-medium whitespace-nowrap">
                  {slug ? (
                    <Link to={`/player/${slug}`} className="hover:text-orange-400 transition-colors">{p.name}</Link>
                  ) : p.name}
                  {p.starter ? <span className="text-orange-500 ml-0.5">*</span> : null}
                </td>
                <td className="py-1.5 px-1 text-gray-500">{p.time}</td>
                <td className="py-1.5 px-1 font-bold text-white">{p.pts}</td>
                <td className="py-1.5 px-1 text-gray-300">{p.reb}</td>
                <td className="py-1.5 px-1 text-gray-300">{p.ast}</td>
                <td className="py-1.5 px-1 text-gray-400">{p.blk}</td>
                <td className="py-1.5 px-1 text-gray-400">{p.stl}</td>
                <td className="py-1.5 px-1 text-gray-400">{p.to}</td>
                <td className="py-1.5 px-1 text-gray-400">{p.two_a}</td>
                <td className="py-1.5 px-1 text-gray-400">{p.three_a}</td>
                <td className="py-1.5 px-1 text-gray-400">{p.ft_a}</td>
                <td className="py-1.5 px-1 text-gray-400">{p.efg}</td>
                <td className={`py-1.5 px-1 font-bold ${p.pm && p.pm.startsWith('+') ? 'text-green-400' : p.pm && p.pm !== '0' ? 'text-red-400' : 'text-gray-500'}`}>{p.pm}</td>
              </tr>
            );
          })}
        </tbody>
      </table>
    </div>
  );
}

function DaculaTeamStats({ game }) {
  const [expanded, setExpanded] = useState(false);
  if (!game.team_stats) return null;
  const my = game.team_stats.my;
  const opp = game.team_stats.opp;

  const parseFt = (ftStr) => {
    if (!ftStr || !ftStr.includes('/')) return { made: '—', att: '—', pct: '—' };
    const [made, att] = ftStr.split('/').map(Number);
    const pct = att > 0 ? ((made / att) * 100).toFixed(1) + '%' : '—';
    return { made, att, pct };
  };
  const myFt = parseFt(my.ft);
  const oppFt = parseFt(opp.ft);

  const fmt = (v) => (v == null || v === '') ? '—' : v;

  const summaryStats = [
    { label: 'AST', my: my.ast, opp: opp.ast },
    { label: 'REB', my: my.reb, opp: opp.reb },
    { label: 'eFG%', my: my.efg_pct, opp: opp.efg_pct },
    { label: 'FTA', my: myFt.att, opp: oppFt.att },
    { label: 'FT%', my: myFt.pct, opp: oppFt.pct },
    { label: '2FG%', my: my.two_pct, opp: opp.two_pct },
    { label: '3FG%', my: my.three_pct, opp: opp.three_pct },
    { label: 'TO', my: my.to, opp: opp.to },
  ];

  const fullStats = [
    { label: 'Points', my: my.pts, opp: opp.pts },
    { label: 'Pts Per Possession', my: my.ppp, opp: opp.ppp },
    { label: '— Four Factors —', header: true },
    { label: 'Eff. FG%', my: my.efg_pct, opp: opp.efg_pct },
    { label: 'Turnover %', my: my.to_pct, opp: opp.to_pct },
    { label: 'Off. Reb %', my: my.oreb_pct, opp: opp.oreb_pct },
    { label: 'Free Throw Rate', my: my.ftr, opp: opp.ftr },
    { label: '— Shooting —', header: true },
    { label: '2 Pt (M/A)', my: my.two_made != null ? `${my.two_made}/${my.two_att}` : null, opp: opp.two_made != null ? `${opp.two_made}/${opp.two_att}` : null },
    { label: '2 Pt %', my: my.two_pct, opp: opp.two_pct },
    { label: '3 Pt (M/A)', my: my.three_made != null ? `${my.three_made}/${my.three_att}` : null, opp: opp.three_made != null ? `${opp.three_made}/${opp.three_att}` : null },
    { label: '3 Pt %', my: my.three_pct, opp: opp.three_pct },
    { label: 'Free Throws (M/A)', my: my.ft_made != null ? `${my.ft_made}/${my.ft_att}` : null, opp: opp.ft_made != null ? `${opp.ft_made}/${opp.ft_att}` : null },
    { label: 'FT %', my: my.ft_pct, opp: opp.ft_pct },
    { label: 'Scoring Opps', my: my.scoring_opps, opp: opp.scoring_opps },
    { label: 'Total Shots', my: my.shots, opp: opp.shots },
    { label: 'FT Trips', my: my.ft_trips, opp: opp.ft_trips },
    { label: '2 Pt Rate', my: my.two_rate, opp: opp.two_rate },
    { label: '3 Pt Rate', my: my.three_rate, opp: opp.three_rate },
    { label: '— Team Play —', header: true },
    { label: 'Assists', my: my.ast, opp: opp.ast },
    { label: 'Turnovers', my: my.to, opp: opp.to },
    { label: 'Turnover %', my: my.to_pct, opp: opp.to_pct },
    { label: 'Steals', my: my.stl, opp: opp.stl },
    { label: 'Blocks', my: my.blk, opp: opp.blk },
    { label: 'Deflections', my: my.deflections, opp: opp.deflections },
    { label: 'Fouls', my: my.fouls, opp: opp.fouls },
    { label: 'Def. Fouls', my: my.def_fouls, opp: opp.def_fouls },
    { label: 'Charges Taken', my: my.charges, opp: opp.charges },
    { label: 'Kills (3 stops)', my: my.kills, opp: opp.kills },
    { label: '— Rebounding —', header: true },
    { label: 'Total Rebounds', my: my.reb, opp: opp.reb },
    { label: 'Off. Rebounds', my: my.oreb, opp: opp.oreb },
    { label: 'Def. Rebounds', my: my.dreb, opp: opp.dreb },
    { label: 'Off. Reb %', my: my.oreb_pct, opp: opp.oreb_pct },
  ];

  return (
    <div>
      {/* Scoreboard */}
      <div className="flex items-center justify-between px-5 py-4 border-b" style={{ borderColor: 'rgba(255,255,255,0.05)' }}>
        <div className="text-center flex-1">
          <p className="text-xs font-bold uppercase tracking-wider truncate" style={{ color: ORANGE }}>{game.my_team}</p>
          <p className="text-4xl font-black" style={{ color: ORANGE }}>{fmt(my.pts)}</p>
        </div>
        <div className="text-gray-600 font-bold text-sm px-4">vs</div>
        <div className="text-center flex-1">
          <p className="text-xs font-bold uppercase tracking-wider truncate text-gray-400">{game.opp_team}</p>
          <p className="text-4xl font-black text-white">{fmt(opp.pts)}</p>
        </div>
      </div>

      {/* Summary stats grid — my team */}
      <div className="px-5 pt-4 pb-2">
        <p className="text-xs font-bold uppercase tracking-widest mb-3" style={{ color: ORANGE }}>{game.my_team}</p>
        <div className="grid grid-cols-4 sm:grid-cols-8 gap-2">
          {summaryStats.map(s => (
            <div key={s.label} className="text-center">
              <div className="text-base font-black text-white">{fmt(s.my)}</div>
              <div className="text-[10px] text-gray-600 uppercase tracking-wider leading-tight">{s.label}</div>
            </div>
          ))}
        </div>
      </div>

      {/* Summary stats grid — opponent */}
      <div className="px-5 pt-2 pb-3 border-b" style={{ borderColor: 'rgba(255,255,255,0.05)' }}>
        <p className="text-xs font-bold uppercase tracking-widest mb-3 text-gray-500">{game.opp_team}</p>
        <div className="grid grid-cols-4 sm:grid-cols-8 gap-2">
          {summaryStats.map(s => (
            <div key={s.label} className="text-center">
              <div className="text-base font-black text-gray-400">{fmt(s.opp)}</div>
              <div className="text-[10px] text-gray-700 uppercase tracking-wider leading-tight">{s.label}</div>
            </div>
          ))}
        </div>
      </div>

      {/* Full Game Stats toggle */}
      <button
        onClick={() => setExpanded(!expanded)}
        className="flex items-center justify-center gap-2 w-full py-3 text-xs font-bold uppercase tracking-widest border-b transition-colors"
        style={{ borderColor: 'rgba(255,255,255,0.05)', color: expanded ? ORANGE : 'rgba(255,255,255,0.35)' }}
      >
        {expanded ? <ChevronUp className="w-3.5 h-3.5" /> : <ChevronDown className="w-3.5 h-3.5" />}
        Full Game Stats
      </button>

      {/* Expanded full stats table */}
      {expanded && (
        <div className="px-4 pb-4">
          <div className="grid grid-cols-3 gap-2 py-2 text-xs font-bold uppercase tracking-wider border-b mb-1" style={{ borderColor: 'rgba(255,255,255,0.07)' }}>
            <div style={{ color: ORANGE }} className="truncate">{game.my_team}</div>
            <div className="text-center text-gray-600">Stat</div>
            <div className="text-right text-gray-500 truncate">{game.opp_team}</div>
          </div>
          {fullStats.map(s => s.header ? (
            <div key={s.label} className="text-xs font-black uppercase tracking-widest mt-3 mb-1 px-1" style={{ color: ORANGE }}>{s.label.replace(/—/g, '').trim()}</div>
          ) : (
            <div key={s.label} className="grid grid-cols-3 gap-2 py-1.5 rounded-lg px-1" style={{ background: 'rgba(255,255,255,0.02)' }}>
              <div className="text-sm font-bold text-white">{fmt(s.my)}</div>
              <div className="text-center text-xs text-gray-600 self-center">{s.label}</div>
              <div className="text-sm font-bold text-right text-gray-300">{fmt(s.opp)}</div>
            </div>
          ))}
        </div>
      )}
    </div>
  );
}

function GameCard({ game }) {
  const [open, setOpen] = useState(false);
  const [showMyPlayers, setShowMyPlayers] = useState(false);
  const [showOppPlayers, setShowOppPlayers] = useState(false);

  const resultColor = game.result === 'W' ? '#4ade80' : game.result === 'L' ? '#f87171' : '#9ca3af';

  return (
    <div className="rounded-xl border overflow-hidden" style={{ borderColor: 'rgba(255,255,255,0.07)', background: '#0a0a0a' }}>
      {/* Header */}
      <button
        onClick={() => setOpen(o => !o)}
        className="w-full flex items-center justify-between px-4 py-3 hover:bg-white/[0.02] transition-colors text-left"
      >
        <div className="flex items-center gap-2 flex-1 min-w-0">
          {game.result && (
            <span className="text-xs font-black px-1.5 py-0.5 rounded shrink-0" style={{ background: `${resultColor}22`, color: resultColor }}>
              {game.result}
            </span>
          )}
          <p className="font-bold text-sm truncate">
            <span style={{ color: ORANGE }}>{game.my_team}</span>
            <span className="text-gray-500 mx-1">vs</span>
            <span className="text-white">{game.opp_team}</span>
            <span className="text-gray-600 ml-2">· {game.date} · {game.time}</span>
          </p>
        </div>
        <div className="flex items-center gap-2 flex-shrink-0 ml-2">
          {game.film_url && (
            <a href={game.film_url} target="_blank" rel="noopener noreferrer"
              onClick={e => e.stopPropagation()}
              className="p-1.5 rounded-lg text-gray-500 hover:text-white transition-colors"
              style={{ background: 'rgba(255,255,255,0.05)' }}>
              <ExternalLink className="w-3 h-3" />
            </a>
          )}
          {open ? <ChevronUp className="w-4 h-4 text-gray-500" /> : <ChevronDown className="w-4 h-4 text-gray-500" />}
        </div>
      </button>

      {open && (
        <div className="border-t" style={{ borderColor: 'rgba(255,255,255,0.05)' }}>
          {/* Game Film */}
          {game.film_url && (
            <div className="aspect-video bg-black">
              <iframe
                src={vimeoEmbed(game.film_url)}
                className="w-full h-full"
                allow="autoplay; fullscreen; picture-in-picture"
                allowFullScreen
                title={`${game.my_team} vs ${game.opp_team}`}
              />
            </div>
          )}
          {/* Team Stats */}
          <DaculaTeamStats game={game} />

          {/* Player Stats toggles */}
          {(game.my_players?.length > 0 || game.opp_players?.length > 0) && (
            <div className="px-4 pb-4 pt-2 space-y-3">
              {game.my_players?.length > 0 && (
                <div>
                  <button
                    onClick={() => setShowMyPlayers(o => !o)}
                    className="flex items-center gap-2 text-xs font-bold uppercase tracking-widest w-full py-2 border-t transition-colors"
                    style={{ borderColor: 'rgba(255,255,255,0.07)', color: showMyPlayers ? ORANGE : 'rgba(255,255,255,0.4)' }}
                  >
                    <Users className="w-3.5 h-3.5" />
                    {game.my_team} Player Stats
                    {showMyPlayers ? <ChevronUp className="w-3.5 h-3.5 ml-auto" /> : <ChevronDown className="w-3.5 h-3.5 ml-auto" />}
                  </button>
                  {showMyPlayers && <PlayerTable players={game.my_players} teamLabel={game.my_team} slugMap={DACULA_PORTFOLIO_SLUGS} />}
                </div>
              )}
              {game.opp_players?.length > 0 && (
                <div>
                  <button
                    onClick={() => setShowOppPlayers(o => !o)}
                    className="flex items-center gap-2 text-xs font-bold uppercase tracking-widest w-full py-2 border-t transition-colors"
                    style={{ borderColor: 'rgba(255,255,255,0.07)', color: showOppPlayers ? ORANGE : 'rgba(255,255,255,0.4)' }}
                  >
                    <Users className="w-3.5 h-3.5" />
                    {game.opp_team} Player Stats
                    {showOppPlayers ? <ChevronUp className="w-3.5 h-3.5 ml-auto" /> : <ChevronDown className="w-3.5 h-3.5 ml-auto" />}
                  </button>
                  {showOppPlayers && <PlayerTable players={game.opp_players} teamLabel={game.opp_team} slugMap={{}} />}
                </div>
              )}
            </div>
          )}
        </div>
      )}
    </div>
  );
}

function SessionSection({ title, games, defaultOpen = false }) {
  const [open, setOpen] = useState(defaultOpen);
  const wins = games.filter(g => g.result === 'W').length;
  const losses = games.filter(g => g.result === 'L').length;

  return (
    <div className="rounded-2xl border overflow-hidden" style={{ borderColor: 'rgba(255,255,255,0.07)', background: '#050505' }}>
      <button
        onClick={() => setOpen(o => !o)}
        className="w-full flex items-center justify-between px-5 py-4 hover:bg-white/[0.02] transition-colors text-left"
      >
        <div>
          <p className="font-barlow font-black text-lg uppercase tracking-wide text-white">{title}</p>
          <p className="text-xs text-gray-500 mt-0.5">
            {games.length} games · <span className="text-green-400 font-bold">{wins}W</span> <span className="text-red-400 font-bold">{losses}L</span>
          </p>
        </div>
        {open ? <ChevronUp className="w-5 h-5" style={{ color: ORANGE }} /> : <ChevronDown className="w-5 h-5 text-gray-500" />}
      </button>

      {open && (
        <div className="border-t px-4 pb-4 pt-3 space-y-3" style={{ borderColor: 'rgba(255,255,255,0.06)' }}>
          {games.map(game => (
            <GameCard key={game.id} game={game} />
          ))}
        </div>
      )}
    </div>
  );
}

export default function DaculaGameStats() {
  return (
    <div className="space-y-4">
      <SessionSection
        title="GBCA Live Period — Session 1 · June 13–14, 2026"
        games={SESSION_1_GAMES}
        defaultOpen={false}
      />
      <SessionSection
        title="GBCA Live Period — Session 2 SE Regional · June 26–28, 2026"
        games={SESSION_2_GAMES}
        defaultOpen={true}
      />
      <p className="text-xs text-gray-700 text-center pt-2">Stats powered by Hoopsalytics · * = Starter</p>
    </div>
  );
}

src/components/georgia/GameStatsPanel.jsx

import { useState, useRef } from 'react';
import { ChevronDown, ChevronUp, Lock, Download } from 'lucide-react';
import html2canvas from 'html2canvas';
import jsPDF from 'jspdf';

const ORANGE = '#FF6A00';

// Stats shown by default (free)
const PRIMARY_STATS = [
  { key: 'pts', label: 'PTS', format: 'num' },
  { key: 'ast', label: 'AST', format: 'num' },
  { key: 'oreb', label: 'OREB', format: 'num' },
  { key: 'efg_pct', label: 'eFG%', format: 'pct' },
  { key: 'ft_att', label: 'FTA', format: 'num' },
  { key: 'ft_pct', label: 'FT%', format: 'pct' },
  { key: 'two_fg_pct', label: '2FG%', format: 'pct' },
  { key: 'three_fg_pct', label: '3FG%', format: 'pct' },
  { key: 'to', label: 'TO', format: 'num' },
];

// Full comparison stats (side-by-side, gated)
const FULL_STATS = [
  { key: 'pts', label: 'Points' },
  { key: 'ast', label: 'Assists' },
  { key: 'to', label: 'Turnovers' },
  { key: 'oreb', label: 'Off. Rebounds' },
  { key: 'dreb', label: 'Def. Rebounds' },
  { key: 'efg_pct', label: 'eFG%' },
  { key: 'two_fg_pct', label: '2FG%' },
  { key: 'three_fg_pct', label: '3FG%' },
  { key: 'ft_pct', label: 'FT%' },
  { key: 'ft_att', label: 'FT Attempts' },
  { key: 'ft_made', label: 'FT Made' },
  { key: 'two_pt_made', label: '2PT Made' },
  { key: 'two_pt_att', label: '2PT Attempts' },
  { key: 'three_pt_made', label: '3PT Made' },
  { key: 'three_pt_att', label: '3PT Attempts' },
  { key: 'shots', label: 'Total Shots' },
  { key: 'opps', label: 'Scoring Opps' },
  { key: 'ato_ratio', label: 'A/TO Ratio' },
  { key: 'oreb_pct', label: 'OREB%' },
  { key: 'ts_pct', label: 'TS%' },
];

function fmt(val) {
  if (val == null) return '—';
  return val;
}

function better(key, myVal, oppVal) {
  if (myVal == null || oppVal == null) return null;
  const parseV = v => parseFloat(String(v).replace('%', ''));
  const m = parseV(myVal);
  const o = parseV(oppVal);
  if (isNaN(m) || isNaN(o) || m === o) return null;
  const lowerBetter = ['to'];
  return lowerBetter.includes(key) ? (m < o ? 'my' : 'opp') : (m > o ? 'my' : 'opp');
}

export default function GameStatsPanel({ game, myTeamName, oppTeamName, isPurchased = false }) {
  const [expanded, setExpanded] = useState(false);
  const statsRef = useRef(null);

  const printPDF = async () => {
    if (!statsRef.current) return;
    try {
      const canvas = await html2canvas(statsRef.current, { backgroundColor: '#060606', scale: 2 });
      const pdf = new jsPDF('p', 'mm', 'a4');
      const imgData = canvas.toDataURL('image/png');
      const pdfWidth = pdf.internal.pageSize.getWidth();
      const pdfHeight = (canvas.height * pdfWidth) / canvas.width;
      pdf.addImage(imgData, 'PNG', 0, 0, pdfWidth, pdfHeight);
      pdf.save(`${myTeamName}_vs_${oppTeamName}_stats.pdf`);
    } catch (e) {
      alert('Error generating PDF: ' + e.message);
    }
  };

  const normalize = (name) => name?.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/(^-|-$)/g, '');
  const teamStats = game.team_stats || {};
  const mySlug = normalize(myTeamName);
  const oppSlug = normalize(oppTeamName);
  const myStats = teamStats[mySlug] || teamStats[myTeamName];
  const oppStats = teamStats[oppSlug] || teamStats[oppTeamName];

  if (!myStats && !oppStats) return null;

  return (
    <div ref={statsRef} className="border-t" style={{ borderColor: 'rgba(255,255,255,0.05)', background: '#060606' }}>

      {/* Score header */}
      {myStats && oppStats && (
        <div className="flex items-center justify-between px-5 py-3 border-b" style={{ borderColor: 'rgba(255,255,255,0.05)' }}>
          <div className="text-center flex-1">
            <p className="text-xs font-bold uppercase tracking-wider truncate" style={{ color: ORANGE }}>{myTeamName}</p>
            <p className="text-3xl font-black text-white">{fmt(myStats.pts)}</p>
          </div>
          <div className="text-gray-600 font-bold text-sm px-4">vs</div>
          <div className="text-center flex-1">
            <p className="text-xs font-bold uppercase tracking-wider truncate text-gray-400">{oppTeamName}</p>
            <p className="text-3xl font-black text-gray-300">{fmt(oppStats.pts)}</p>
          </div>
        </div>
      )}

      {/* Filter stats: preview for unclaimed, full for purchased */}
      {(() => {
        const previewKeys = ['efg_pct', 'to', 'oreb', 'ft_att'];
        const statsToShow = isPurchased
          ? PRIMARY_STATS.filter(s => s.key !== 'pts')
          : PRIMARY_STATS.filter(s => previewKeys.includes(s.key) || !s.teamBased);
        const gridCols = isPurchased ? 'grid-cols-5 sm:grid-cols-11' : 'grid-cols-4';

        return (
          <>
            {/* Primary stats grid — my team */}
            {myStats && (
              <div className="px-5 pt-4 pb-2">
                <p className="text-xs font-bold uppercase tracking-widest mb-3" style={{ color: ORANGE }}>{myTeamName}</p>
                <div className={`grid ${gridCols} gap-2`}>
                  {statsToShow.map(stat => {
                    const value = myStats[stat.key];
                    return (
                      <div key={stat.key} className="text-center">
                        <div className="text-base font-black text-white">{fmt(value)}</div>
                        <div className="text-xs text-gray-600 uppercase tracking-wider leading-tight">{stat.label}</div>
                      </div>
                    );
                  })}
                </div>
              </div>
            )}

            {/* Primary stats grid — opponent */}
            {oppStats && (
              <div className="px-5 pt-2 pb-3 border-b" style={{ borderColor: 'rgba(255,255,255,0.05)' }}>
                <p className="text-xs font-bold uppercase tracking-widest mb-3 text-gray-500">{oppTeamName}</p>
                <div className={`grid ${gridCols} gap-2`}>
                  {statsToShow.map(stat => {
                    const value = oppStats[stat.key];
                    return (
                      <div key={stat.key} className="text-center">
                        <div className="text-base font-black text-gray-400">{fmt(value)}</div>
                        <div className="text-xs text-gray-700 uppercase tracking-wider leading-tight">{stat.label}</div>
                      </div>
                    );
                  })}
                </div>
              </div>
            )}
          </>
        );
      })()}

      {/* Full Game Stats toggle + Print button */}
      <div className="flex items-center justify-center gap-3 py-3 text-xs font-bold uppercase tracking-widest border-b" style={{ borderColor: 'rgba(255,255,255,0.05)' }}>
        <button
          onClick={() => setExpanded(!expanded)}
          className="flex items-center justify-center gap-2 flex-1 transition-colors"
          style={{ color: expanded ? ORANGE : 'rgba(255,255,255,0.35)' }}
        >
          {expanded ? <ChevronUp className="w-3.5 h-3.5" /> : <ChevronDown className="w-3.5 h-3.5" />}
          Full Game Stats
        </button>
        <button
          onClick={printPDF}
          className="flex items-center justify-center gap-1 px-3 py-2 rounded-lg transition-colors"
          style={{ background: 'rgba(255,106,0,0.1)', color: ORANGE }}
          title="Download stats as PDF"
        >
          <Download className="w-3.5 h-3.5" />
        </button>
      </div>

      {/* Side-by-side full comparison */}
      {expanded && (
        <div className="px-4 pb-4">
          <div className="space-y-1">
              {/* Header */}
              <div className="grid grid-cols-3 gap-2 py-2 text-xs font-bold uppercase tracking-wider border-b mb-1" style={{ borderColor: 'rgba(255,255,255,0.07)' }}>
                <div style={{ color: ORANGE }} className="truncate">{myTeamName}</div>
                <div className="text-center text-gray-600">Stat</div>
                <div className="text-right text-gray-500 truncate">{oppTeamName}</div>
              </div>
              {FULL_STATS.map(stat => {
                const myVal = myStats?.[stat.key];
                const oppVal = oppStats?.[stat.key];
                const win = better(stat.key, myVal, oppVal);
                return (
                  <div key={stat.key} className="grid grid-cols-3 gap-2 py-1.5 rounded-lg px-1" style={{ background: 'rgba(255,255,255,0.02)' }}>
                    <div className={`text-sm font-bold ${win === 'my' ? 'text-white' : 'text-gray-500'}`}>{fmt(myVal)}</div>
                    <div className="text-center text-xs text-gray-600 self-center">{stat.label}</div>
                    <div className={`text-sm font-bold text-right ${win === 'opp' ? 'text-gray-300' : 'text-gray-600'}`}>{fmt(oppVal)}</div>
                  </div>
                );
              })}
              </div>
        </div>
      )}
    </div>
  );
}

src/components/georgia/HeroSection.jsx

import { motion } from 'framer-motion';
import { ArrowRight, Zap } from 'lucide-react';

const CYAN = '#00D9FF';
const CORAL = '#FF6B5B';
const NAVY = '#1A3B5C';
const ORANGE = '#FF6A00';

const containerVariants = {
  hidden: { opacity: 0 },
  visible: {
    opacity: 1,
    transition: {
      staggerChildren: 0.15,
      delayChildren: 0.2,
    },
  },
};

const itemVariants = {
  hidden: { opacity: 0, y: 40 },
  visible: {
    opacity: 1,
    y: 0,
    transition: { duration: 0.8, ease: [0.25, 0.46, 0.45, 0.94] },
  },
};

export default function HeroSection() {
  return (
    <section className="relative min-h-screen flex items-center justify-center overflow-hidden pt-24 pb-12">
      {/* Subtle gradient accent */}
      <div className="absolute -top-20 left-1/4 w-64 h-64 rounded-full blur-3xl opacity-20 pointer-events-none"
      style={{ background: `radial-gradient(circle, ${CYAN} 0%, transparent 70%)` }} />

      {/* Content */}
      <motion.div
        variants={containerVariants}
        initial="hidden"
        animate="visible"
        className="relative z-10 max-w-5xl mx-auto px-6 text-center"
      >
        {/* Eyebrow badge */}
        <motion.div
          variants={itemVariants}
          className="inline-flex items-center gap-2 px-4 py-2.5 rounded-full mb-8 backdrop-blur-sm"
          style={{
            background: `rgba(0,217,255,0.1)`,
            border: `1.5px solid ${CYAN}`,
            boxShadow: `0 0 20px rgba(0,217,255,0.3)`,
          }}
        >
          <Zap className="w-4 h-4" style={{ color: CYAN }} />
          <span className="text-xs font-black uppercase tracking-widest" style={{ color: CYAN }}>
            Georgia High School Boys Showcase
          </span>
        </motion.div>

        {/* Main headline */}
         <motion.div variants={itemVariants} className="mb-8">
           <h1 className="font-barlow font-black leading-[1.1]" style={{ fontSize: 'clamp(56px, 9vw, 120px)', letterSpacing: '0.005em' }}>
             <span style={{ color: '#FFFFFF' }}>INVEST IN</span><br />
             <span style={{ display: 'inline-block', marginTop: '8px', background: 'linear-gradient(90deg, #6B7CED 0%, #A78BFA 100%)', WebkitBackgroundClip: 'text', WebkitTextFillColor: 'transparent', backgroundClip: 'text' }}>YOUR ATHLETE'S</span><br />
             <span style={{ display: 'inline-block', marginTop: '8px', background: 'linear-gradient(90deg, #FF6A00 0%, #FF1493 100%)', WebkitBackgroundClip: 'text', WebkitTextFillColor: 'transparent', backgroundClip: 'text' }}>FUTURE</span>
           </h1>
         </motion.div>

        {/* Subheadline */}
        <motion.p
          variants={itemVariants}
          className="text-lg sm:text-xl max-w-2xl mx-auto mb-10 leading-relaxed font-light"
          style={{ color: 'rgba(255,255,255,0.75)' }}
        >
          Professional-grade film coverage, recruiting portfolios, and analytics infrastructure built for Georgia State Boys Basketball. Transform exposure into opportunity.
        </motion.p>

        {/* CTA Buttons */}
        <motion.div variants={itemVariants} className="flex flex-col sm:flex-row gap-4 justify-center mb-4">
          <button
            onClick={() => document.getElementById('packages')?.scrollIntoView({ behavior: 'smooth' })}
            className="group relative inline-flex items-center justify-center gap-3 px-8 py-4 rounded-lg text-sm font-black uppercase tracking-wide transition-all duration-300 overflow-hidden"
            style={{
              background: ORANGE,
              color: '#0D0D0D',
              boxShadow: `0 8px 24px rgba(0,0,0,0.3)`,
            }}
            onMouseEnter={(e) => {
              e.currentTarget.style.transform = 'translateY(-2px)';
              e.currentTarget.style.boxShadow = `0 12px 32px rgba(0,0,0,0.4)`;
            }}
            onMouseLeave={(e) => {
              e.currentTarget.style.transform = 'translateY(0)';
              e.currentTarget.style.boxShadow = `0 8px 24px rgba(0,0,0,0.3)`;
            }}
          >
            <span>View Packages</span>
            <ArrowRight className="w-5 h-5 group-hover:translate-x-1 transition-transform" />
          </button>

          <a
            href="https://profxpo.com/chase-titus"
            target="_blank"
            rel="noopener noreferrer"
            className="inline-flex items-center justify-center gap-2 px-8 py-4 rounded-lg text-sm font-semibold transition-all duration-300"
            style={{
              border: `1.5px solid ${CYAN}`,
              color: CYAN,
              background: 'transparent',
            }}
            onMouseEnter={(e) => {
              e.currentTarget.style.background = `rgba(0,217,255,0.1)`;
              e.currentTarget.style.boxShadow = `0 0 20px rgba(0,217,255,0.3)`;
            }}
            onMouseLeave={(e) => {
              e.currentTarget.style.background = 'transparent';
              e.currentTarget.style.boxShadow = 'none';
            }}
          >
            See Live Portfolio
            <ArrowRight className="w-4 h-4" />
          </a>
        </motion.div>

        {/* Trust indicators */}
        <motion.div
          variants={itemVariants}
          className="flex flex-wrap justify-center gap-6 mt-12 pt-8 border-t"
          style={{ borderColor: 'rgba(0,217,255,0.15)' }}
        >
          {[
            { label: 'Real Athletes', value: '1000+' },
            { label: 'Games Filmed', value: '700+' },
            { label: 'Coaches Reached', value: 'D1-NAIA' },
          ].map((stat, i) => (
            <div key={i} className="text-center">
              <div className="text-2xl sm:text-3xl font-black" style={{ color: CYAN }}>
                {stat.value}
              </div>
              <div className="text-xs uppercase tracking-widest mt-1" style={{ color: 'rgba(255,255,255,0.5)' }}>
                {stat.label}
              </div>
            </div>
          ))}
        </motion.div>
      </motion.div>

      {/* Animated line accent */}
      <motion.div
        initial={{ scaleX: 0 }}
        animate={{ scaleX: 1 }}
        transition={{ delay: 0.8, duration: 1.2, ease: 'easeOut' }}
        className="absolute bottom-0 left-0 right-0 h-px"
        style={{ background: `linear-gradient(90deg, transparent, ${CYAN}, transparent)` }}
      />
    </section>
  );
}

src/components/georgia/InteractiveGradientBg.jsx

import { useEffect, useRef } from 'react';

export default function InteractiveGradientBg() {
  const canvasRef = useRef(null);
  const posRef = useRef({ x: 0, y: 0 });
  const smoothPosRef = useRef({ x: 0, y: 0 });
  const animationRef = useRef(null);

  useEffect(() => {
    const canvas = canvasRef.current;
    if (!canvas) return;

    const ctx = canvas.getContext('2d');
    let resizeTimeout;

    const handleResize = () => {
      canvas.width = window.innerWidth;
      canvas.height = window.innerHeight;
    };

    const handleMouseMove = (e) => {
      posRef.current = { x: e.clientX, y: e.clientY };
    };

    const handleTouchMove = (e) => {
      if (e.touches.length > 0) {
        posRef.current = { x: e.touches[0].clientX, y: e.touches[0].clientY };
      }
    };

    const animate = () => {
      // Smooth interpolation for fluid effect
      smoothPosRef.current.x += (posRef.current.x - smoothPosRef.current.x) * 0.08;
      smoothPosRef.current.y += (posRef.current.y - smoothPosRef.current.y) * 0.08;

      // Clear canvas
      ctx.fillStyle = '#0D0D0D';
      ctx.fillRect(0, 0, canvas.width, canvas.height);

      // Create gradient layers
      const grd1 = ctx.createRadialGradient(smoothPosRef.current.x, smoothPosRef.current.y, 0, smoothPosRef.current.x, smoothPosRef.current.y, 400);
      grd1.addColorStop(0, 'rgba(212, 175, 55, 0.15)');
      grd1.addColorStop(0.5, 'rgba(212, 175, 55, 0.05)');
      grd1.addColorStop(1, 'rgba(212, 175, 55, 0)');

      // Complementary glow offset
      const offsetX = smoothPosRef.current.x - canvas.width / 2;
      const offsetY = smoothPosRef.current.y - canvas.height / 2;
      const cx2 = canvas.width / 2 - offsetX * 0.5;
      const cy2 = canvas.height / 2 - offsetY * 0.5;

      const grd2 = ctx.createRadialGradient(cx2, cy2, 0, cx2, cy2, 500);
      grd2.addColorStop(0, 'rgba(212, 175, 55, 0.08)');
      grd2.addColorStop(1, 'rgba(212, 175, 55, 0)');

      ctx.fillStyle = grd1;
      ctx.fillRect(0, 0, canvas.width, canvas.height);

      ctx.fillStyle = grd2;
      ctx.fillRect(0, 0, canvas.width, canvas.height);

      animationRef.current = requestAnimationFrame(animate);
    };

    handleResize();
    window.addEventListener('resize', handleResize);
    window.addEventListener('mousemove', handleMouseMove);
    window.addEventListener('touchmove', handleTouchMove, { passive: true });

    animate();

    return () => {
      window.removeEventListener('resize', handleResize);
      window.removeEventListener('mousemove', handleMouseMove);
      window.removeEventListener('touchmove', handleTouchMove);
      if (animationRef.current) cancelAnimationFrame(animationRef.current);
      clearTimeout(resizeTimeout);
    };
  }, []);

  return (
    <canvas
      ref={canvasRef}
      className="fixed top-0 left-0 w-full h-full pointer-events-none"
      style={{ zIndex: 0 }}
    />
  );
}

src/components/georgia/MariettaGameStats.jsx

import { useState } from 'react';
import { Link } from 'react-router-dom';
import { ChevronDown, ChevronUp, Users, ExternalLink } from 'lucide-react';

const ORANGE = '#FF6A00';

// Marietta player portfolio slug map
const MARIETTA_PORTFOLIO_SLUGS = {
  'Peyton Easley': 'peyton-easley',
  'Quinton Smith': 'quinton-smith',
  'Tyeric Randolph': 'tyeric-randolph',
  'Julian Lanier': 'julian-lanier',
  'Howell Owen': 'howell-owen',
  'Easton Hicks': 'easton-hicks',
  'Braylon Whitfield': 'braylon-whitfield',
  'Merrick Ham': 'merrick-ham',
  'Kylen Thomas': 'kylen-thomas',
  'Oliver Purifoy': 'oliver-purifoy',
  'Zachary Viola': 'zachary-viola',
};

function vimeoEmbed(url) {
  const m = url?.match(/vimeo\.com\/(\d+)/);
  return m ? `https://player.vimeo.com/video/${m[1]}` : url;
}

function normalizeTeam(name) {
  return (name || '')
    .toLowerCase()
    .replace(/\s*-\s*[a-z]{2}$/, '')
    .replace(/[^a-z0-9]/g, '');
}

function toISODate(dateStr) {
  const d = new Date(dateStr);
  if (isNaN(d.getTime())) return dateStr;
  return d.toISOString().split('T')[0];
}

function teamsMatch(a, b) {
  const na = normalizeTeam(a);
  const nb = normalizeTeam(b);
  return na === nb || na.includes(nb) || nb.includes(na);
}

// ── Session 1: June 12-13, 2026 ──────────────────────────────────────────────
const SESSION_1_GAMES = [
  {
    id: 's1g1',
    date: 'Jun 12, 2026',
    time: '2:00 PM',
    court: 'Court 03',
    my_team: 'Marietta - GA',
    opp_team: 'East Coweta - GA',
    my_score: 71,
    opp_score: 61,
    result: 'W',
    film_url: 'https://vimeo.com/1201941477',
    team_stats: {
      my: { pts: 71, ppp: 1.08, ast: 12, reb: 42, oreb: 15, dreb: 27, blk: 5, stl: 5, to: 13, deflections: 8, fouls: 11, def_fouls: 10, charges: 0, kills: 3, efg_pct: '49.1%', to_pct: '18.6%', oreb_pct: '46.9%', ftr: 0.33, two_made: 22, two_att: 45, two_pct: '48.9%', three_made: 4, three_att: 12, three_pct: '33.3%', ft_made: 15, ft_att: 19, ft_pct: '78.9%', scoring_opps: 68, shots: 57, ft_trips: 11, two_rate: '78.9%', three_rate: '21.1%', ft: '15/19' },
      opp: { pts: 61, ppp: 0.92, ast: 8, reb: 25, oreb: 8, dreb: 17, blk: 6, stl: 8, to: 10, deflections: 8, fouls: 13, def_fouls: 13, charges: 0, kills: 4, efg_pct: '46.6%', to_pct: '14.7%', oreb_pct: '22.9%', ftr: 0.21, two_made: 21, two_att: 45, two_pct: '46.7%', three_made: 4, three_att: 13, three_pct: '30.8%', ft_made: 7, ft_att: 12, ft_pct: '58.3%', scoring_opps: 65, shots: 58, ft_trips: 7, two_rate: '77.6%', three_rate: '22.4%', ft: '7/12' },
    },
    my_players: [
      { num: '3', name: 'Peyton Easley', time: '15:28', pts: 7, reb: 2, ast: 0, blk: 0, stl: 0, to: 1, two_a: '1/2', three_a: '1/3', ft_a: '2/2', efg: '50.0%', pm: '+15' },
      { num: '4', name: 'Quinton Smith', time: '15:00', pts: 10, reb: 2, ast: 3, blk: 1, stl: 1, to: 2, two_a: '3/6', three_a: '0/0', ft_a: '4/4', efg: '50.0%', pm: '-2' },
      { num: '11', name: 'Tyeric Randolph', time: '22:20', pts: 17, reb: 17, ast: 1, blk: 2, stl: 0, to: 1, two_a: '8/15', three_a: '0/0', ft_a: '1/1', efg: '53.3%', pm: '+17', starter: true },
      { num: '12', name: 'Julian Lanier', time: '19:23', pts: 4, reb: 3, ast: 3, blk: 0, stl: 0, to: 2, two_a: '1/1', three_a: '0/1', ft_a: '2/2', efg: '50.0%', pm: '+3', starter: true },
      { num: '15', name: 'Easton Hicks', time: '2:10', pts: 0, reb: 0, ast: 0, blk: 0, stl: 0, to: 0, two_a: '0/0', three_a: '0/0', ft_a: '0/0', efg: '—', pm: '-5' },
      { num: '21', name: 'Braylon Whitfield', time: '29:00', pts: 16, reb: 5, ast: 3, blk: 0, stl: 3, to: 1, two_a: '6/7', three_a: '0/0', ft_a: '4/6', efg: '85.7%', pm: '+8', starter: true },
      { num: '22', name: 'Merrick Ham', time: '25:38', pts: 11, reb: 4, ast: 0, blk: 2, stl: 0, to: 2, two_a: '1/5', three_a: '3/8', ft_a: '0/0', efg: '42.3%', pm: '+9', starter: true },
      { num: '24', name: 'Kylen Thomas', time: '18:02', pts: 6, reb: 4, ast: 2, blk: 0, stl: 1, to: 2, two_a: '2/8', three_a: '0/0', ft_a: '2/3', efg: '25.0%', pm: '+5', starter: true },
      { num: '32', name: 'Oliver Purifoy', time: '2:55', pts: 0, reb: 0, ast: 0, blk: 0, stl: 0, to: 1, two_a: '0/1', three_a: '0/0', ft_a: '0/1', efg: '0%', pm: '0' },
    ],
    opp_players: [
      { num: '0', name: 'Chris Williams', time: '4:17', pts: 0, reb: 1, ast: 1, blk: 0, stl: 2, to: 2, two_a: '0/1', three_a: '0/0', ft_a: '0/0', efg: '0%', pm: '+1' },
      { num: '1', name: 'David Askew', time: '23:55', pts: 17, reb: 6, ast: 1, blk: 4, stl: 3, to: 0, two_a: '7/13', three_a: '0/0', ft_a: '3/3', efg: '53.8%', pm: '-3', starter: true },
      { num: '2', name: 'Evan Haskins', time: '3:38', pts: 2, reb: 2, ast: 0, blk: 1, stl: 0, to: 0, two_a: '1/2', three_a: '0/0', ft_a: '0/0', efg: '50.0%', pm: '+3' },
      { num: '3', name: 'Armari Owens', time: '29:09', pts: 17, reb: 6, ast: 1, blk: 1, stl: 0, to: 2, two_a: '6/11', three_a: '1/2', ft_a: '2/4', efg: '57.7%', pm: '-12', starter: true },
      { num: '4', name: 'Momodou Ceesay', time: '16:09', pts: 4, reb: 1, ast: 2, blk: 0, stl: 0, to: 2, two_a: '2/4', three_a: '0/0', ft_a: '0/0', efg: '50.0%', pm: '-11', starter: true },
      { num: '11', name: 'AJ McKissic', time: '17:48', pts: 2, reb: 1, ast: 1, blk: 0, stl: 1, to: 0, two_a: '1/3', three_a: '0/3', ft_a: '0/1', efg: '16.7%', pm: '+5', starter: true },
      { num: '12', name: 'Jamarcus Alford', time: '18:15', pts: 5, reb: 0, ast: 0, blk: 0, stl: 1, to: 2, two_a: '1/1', three_a: '1/2', ft_a: '0/2', efg: '83.3%', pm: '-9' },
      { num: '13', name: 'Jevin Finley', time: '8:06', pts: 0, reb: 1, ast: 1, blk: 0, stl: 0, to: 0, two_a: '0/0', three_a: '0/0', ft_a: '0/0', efg: '—', pm: '-14' },
      { num: '24', name: 'Braylon Marshall', time: '28:40', pts: 14, reb: 4, ast: 1, blk: 0, stl: 1, to: 2, two_a: '3/10', three_a: '2/6', ft_a: '2/2', efg: '37.5%', pm: '-10', starter: true },
    ],
  },
  {
    id: 's1g2',
    date: 'Jun 12, 2026',
    time: '7:00 PM',
    court: 'Court 05',
    my_team: 'Marietta - GA',
    opp_team: 'East Forsyth - GA',
    my_score: 64,
    opp_score: 56,
    result: 'W',
    film_url: 'https://vimeo.com/1201664031',
    team_stats: {
      my: { pts: 64, ppp: 1.12, ast: 11, reb: 30, oreb: 12, dreb: 18, blk: 0, stl: 2, to: 8, deflections: 6, fouls: 15, def_fouls: 13, charges: 0, kills: 5, efg_pct: '43.0%', to_pct: '13.8%', oreb_pct: '38.7%', ftr: 0.46, two_made: 14, two_att: 31, two_pct: '45.2%', three_made: 5, three_att: 19, three_pct: '26.3%', ft_made: 21, ft_att: 23, ft_pct: '91.3%', scoring_opps: 62, shots: 50, ft_trips: 12, two_rate: '62.0%', three_rate: '38.0%', ft: '21/23' },
      opp: { pts: 56, ppp: 0.98, ast: 12, reb: 26, oreb: 7, dreb: 19, blk: 0, stl: 4, to: 13, deflections: 6, fouls: 19, def_fouls: 18, charges: 0, kills: 0, efg_pct: '52.3%', to_pct: '22.8%', oreb_pct: '28.0%', ftr: 0.36, two_made: 11, two_att: 24, two_pct: '45.8%', three_made: 8, three_att: 20, three_pct: '40.0%', ft_made: 10, ft_att: 16, ft_pct: '62.5%', scoring_opps: 52, shots: 44, ft_trips: 8, two_rate: '54.5%', three_rate: '45.5%', ft: '10/16' },
    },
    my_players: [
      { num: '3', name: 'Peyton Easley', time: '11:18', pts: 11, reb: 1, ast: 0, blk: 0, stl: 0, to: 0, two_a: '1/3', three_a: '0/3', ft_a: '9/9', efg: '16.7%', pm: '+13' },
      { num: '4', name: 'Quinton Smith', time: '14:34', pts: 3, reb: 6, ast: 1, blk: 0, stl: 0, to: 1, two_a: '0/5', three_a: '1/1', ft_a: '0/0', efg: '25.0%', pm: '+7' },
      { num: '11', name: 'Tyeric Randolph', time: '20:52', pts: 4, reb: 2, ast: 0, blk: 0, stl: 1, to: 1, two_a: '2/4', three_a: '0/2', ft_a: '0/0', efg: '33.3%', pm: '+1', starter: true },
      { num: '12', name: 'Julian Lanier', time: '18:41', pts: 8, reb: 1, ast: 3, blk: 0, stl: 0, to: 2, two_a: '0/1', three_a: '2/3', ft_a: '2/2', efg: '75.0%', pm: '-5', starter: true },
      { num: '14', name: 'Howell Owen', time: '9:51', pts: 0, reb: 1, ast: 0, blk: 0, stl: 0, to: 0, two_a: '0/0', three_a: '0/1', ft_a: '0/0', efg: '0%', pm: '-3' },
      { num: '15', name: 'Easton Hicks', time: '4:16', pts: 0, reb: 0, ast: 1, blk: 0, stl: 0, to: 0, two_a: '0/0', three_a: '0/0', ft_a: '0/0', efg: '0%', pm: '-2' },
      { num: '21', name: 'Braylon Whitfield', time: '23:53', pts: 17, reb: 3, ast: 3, blk: 0, stl: 3, to: 2, two_a: '4/7', three_a: '1/2', ft_a: '6/6', efg: '61.1%', pm: '+12', starter: true },
      { num: '22', name: 'Merrick Ham', time: '26:34', pts: 7, reb: 8, ast: 2, blk: 0, stl: 1, to: 2, two_a: '2/2', three_a: '1/7', ft_a: '0/0', efg: '38.9%', pm: '+3', starter: true },
      { num: '24', name: 'Kylen Thomas', time: '19:56', pts: 14, reb: 6, ast: 1, blk: 0, stl: 2, to: 0, two_a: '5/9', three_a: '0/0', ft_a: '4/6', efg: '55.6%', pm: '+14', starter: true },
    ],
    opp_players: [
      { num: '1', name: 'Kani Hollis', time: '0:38', pts: 0, reb: 0, ast: 0, blk: 0, stl: 0, to: 0, two_a: '0/0', three_a: '0/0', ft_a: '0/0', efg: '0%', pm: '+3' },
      { num: '4', name: 'Cooper Elzey', time: '23:33', pts: 2, reb: 1, ast: 2, blk: 0, stl: 0, to: 2, two_a: '1/2', three_a: '0/1', ft_a: '0/2', efg: '33.3%', pm: '-10', starter: true },
      { num: '5', name: 'Miles Thompson', time: '11:35', pts: 2, reb: 1, ast: 2, blk: 0, stl: 0, to: 2, two_a: '1/1', three_a: '0/0', ft_a: '0/2', efg: '100.0%', pm: '+2' },
      { num: '10', name: 'Kamani Hollis', time: '24:52', pts: 11, reb: 3, ast: 2, blk: 0, stl: 1, to: 2, two_a: '1/2', three_a: '3/5', ft_a: '0/0', efg: '78.6%', pm: '-10', starter: true },
      { num: '13', name: 'Landan Ennis', time: '11:34', pts: 2, reb: 1, ast: 0, blk: 0, stl: 0, to: 1, two_a: '1/2', three_a: '0/0', ft_a: '0/0', efg: '50.0%', pm: '+4' },
      { num: '14', name: 'Gavin Lesch', time: '26:54', pts: 8, reb: 6, ast: 2, blk: 0, stl: 2, to: 4, two_a: '1/6', three_a: '2/10', ft_a: '0/0', efg: '25.0%', pm: '-8', starter: true },
      { num: '23', name: 'Reece Styles', time: '24:12', pts: 19, reb: 5, ast: 2, blk: 0, stl: 0, to: 1, two_a: '3/4', three_a: '3/4', ft_a: '4/4', efg: '93.8%', pm: '-11', starter: true },
      { num: '24', name: 'Kaiden Long', time: '26:38', pts: 12, reb: 5, ast: 2, blk: 0, stl: 1, to: 1, two_a: '3/7', three_a: '0/0', ft_a: '6/8', efg: '42.9%', pm: '-10', starter: true },
    ],
  },
  {
    id: 's1g3',
    date: 'Jun 13, 2026',
    time: '9:00 AM',
    court: 'Court 08',
    my_team: 'Marietta - GA',
    opp_team: 'Burke County - GA',
    my_score: 69,
    opp_score: 66,
    result: 'W',
    film_url: 'https://vimeo.com/1201616389',
    team_stats: {
      my: { pts: 69, ppp: 1.05, ast: 17, reb: 37, oreb: 14, dreb: 23, blk: 3, stl: 8, to: 18, deflections: 6, fouls: 20, def_fouls: 17, charges: 0, kills: 7, efg_pct: '49.1%', to_pct: '24.7%', oreb_pct: '45.2%', ftr: 0.27, two_made: 18, two_att: 37, two_pct: '48.6%', three_made: 6, three_att: 18, three_pct: '33.3%', ft_made: 15, ft_att: 15, ft_pct: '100.0%', scoring_opps: 63, shots: 55, ft_trips: 8, two_rate: '67.3%', three_rate: '32.7%', ft: '15/15' },
      opp: { pts: 66, ppp: 1.0, ast: 10, reb: 28, oreb: 11, dreb: 17, blk: 3, stl: 2, to: 14, deflections: 8, fouls: 10, def_fouls: 10, charges: 2, kills: 5, efg_pct: '44.2%', to_pct: '21.2%', oreb_pct: '32.4%', ftr: 0.5, two_made: 17, two_att: 37, two_pct: '45.9%', three_made: 4, three_att: 15, three_pct: '26.7%', ft_made: 20, ft_att: 26, ft_pct: '76.9%', scoring_opps: 65, shots: 52, ft_trips: 13, two_rate: '71.2%', three_rate: '28.8%', ft: '20/26' },
    },
    my_players: [
      { num: '3', name: 'Peyton Easley', time: '8:47', pts: 5, reb: 2, ast: 1, blk: 0, stl: 1, to: 2, two_a: '1/2', three_a: '1/2', ft_a: '0/0', efg: '62.5%', pm: '+4' },
      { num: '4', name: 'Quinton Smith', time: '17:16', pts: 8, reb: 5, ast: 0, blk: 0, stl: 1, to: 4, two_a: '3/5', three_a: '0/1', ft_a: '2/2', efg: '50.0%', pm: '0' },
      { num: '11', name: 'Tyeric Randolph', time: '20:14', pts: 8, reb: 7, ast: 0, blk: 1, stl: 1, to: 0, two_a: '3/3', three_a: '0/0', ft_a: '2/2', efg: '100.0%', pm: '0', starter: true },
      { num: '12', name: 'Julian Lanier', time: '24:25', pts: 6, reb: 2, ast: 5, blk: 0, stl: 2, to: 4, two_a: '1/4', three_a: '0/3', ft_a: '4/4', efg: '14.3%', pm: '-1', starter: true },
      { num: '14', name: 'Howell Owen', time: '4:11', pts: 4, reb: 1, ast: 0, blk: 0, stl: 0, to: 0, two_a: '1/2', three_a: '0/1', ft_a: '2/2', efg: '33.3%', pm: '+13' },
      { num: '15', name: 'Easton Hicks', time: '4:17', pts: 0, reb: 0, ast: 1, blk: 0, stl: 1, to: 1, two_a: '0/0', three_a: '0/0', ft_a: '0/0', efg: '0%', pm: '-9' },
      { num: '21', name: 'Braylon Whitfield', time: '24:37', pts: 9, reb: 5, ast: 6, blk: 0, stl: 0, to: 3, two_a: '3/8', three_a: '0/1', ft_a: '3/3', efg: '33.3%', pm: '-5', starter: true },
      { num: '22', name: 'Merrick Ham', time: '23:53', pts: 21, reb: 8, ast: 1, blk: 1, stl: 0, to: 0, two_a: '3/7', three_a: '5/9', ft_a: '0/0', efg: '65.6%', pm: '+3', starter: true },
      { num: '24', name: 'Kylen Thomas', time: '20:19', pts: 8, reb: 3, ast: 3, blk: 1, stl: 2, to: 4, two_a: '3/6', three_a: '0/1', ft_a: '2/2', efg: '42.9%', pm: '+14', starter: true },
      { num: '32', name: 'Oliver Purifoy', time: '1:57', pts: 0, reb: 0, ast: 0, blk: 0, stl: 0, to: 0, two_a: '0/0', three_a: '0/0', ft_a: '0/0', efg: '0%', pm: '-4' },
    ],
    opp_players: [
      { num: '14', name: 'DeQuayvian Lovette', time: '27:02', pts: 22, reb: 7, ast: 0, blk: 0, stl: 0, to: 7, two_a: '3/14', three_a: '3/9', ft_a: '7/7', efg: '32.6%', pm: '-2', starter: true },
      { num: '20', name: 'Braylen Bellamy', time: '28:49', pts: 27, reb: 1, ast: 6, blk: 0, stl: 2, to: 3, two_a: '6/10', three_a: '1/4', ft_a: '12/16', efg: '53.6%', pm: '-3', starter: true },
      { num: '22', name: 'Marquez Sims', time: '27:10', pts: 0, reb: 4, ast: 2, blk: 0, stl: 1, to: 1, two_a: '0/1', three_a: '0/1', ft_a: '0/0', efg: '0%', pm: '+2', starter: true },
      { num: '30', name: 'Jukobe Lovette', time: '7:14', pts: 0, reb: 1, ast: 1, blk: 0, stl: 0, to: 1, two_a: '0/0', three_a: '0/1', ft_a: '0/0', efg: '0%', pm: '-6' },
      { num: '34', name: 'Richard Farmer', time: '4:25', pts: 0, reb: 1, ast: 0, blk: 1, stl: 2, to: 0, two_a: '0/1', three_a: '0/0', ft_a: '0/0', efg: '0%', pm: '-5' },
      { num: '42', name: 'Darrien Tullis', time: '27:10', pts: 8, reb: 5, ast: 1, blk: 1, stl: 1, to: 0, two_a: '4/5', three_a: '0/0', ft_a: '0/0', efg: '80.0%', pm: '+2', starter: true },
      { num: '44', name: 'Kendrick Carter', time: '11:13', pts: 2, reb: 1, ast: 0, blk: 1, stl: 1, to: 0, two_a: '1/2', three_a: '0/0', ft_a: '0/0', efg: '50.0%', pm: '+2' },
      { num: '50', name: 'Takirious Reeves', time: '16:51', pts: 7, reb: 5, ast: 0, blk: 0, stl: 0, to: 2, two_a: '3/4', three_a: '0/0', ft_a: '1/3', efg: '75.0%', pm: '-5', starter: true },
    ],
  },
  {
    id: 's1g4',
    date: 'Jun 13, 2026',
    time: '12:00 PM',
    court: 'Court 08',
    my_team: 'Marietta - GA',
    opp_team: 'Mill Creek - GA',
    my_score: 64,
    opp_score: 60,
    result: 'W',
    film_url: 'https://vimeo.com/1201941684',
    team_stats: {
      my: { pts: 64, ppp: 1.05, ast: 17, reb: 33, oreb: 9, dreb: 24, blk: 2, stl: 4, to: 9, deflections: 8, fouls: 11, def_fouls: 9, charges: 0, kills: 4, efg_pct: '52.8%', to_pct: '14.5%', oreb_pct: '29.0%', ftr: 0.36, two_made: 25, two_att: 43, two_pct: '58.1%', three_made: 2, three_att: 10, three_pct: '20.0%', ft_made: 8, ft_att: 19, ft_pct: '42.1%', scoring_opps: 66, shots: 53, ft_trips: 13, two_rate: '81.1%', three_rate: '18.9%', ft: '8/19' },
      opp: { pts: 60, ppp: 1.02, ast: 12, reb: 29, oreb: 7, dreb: 22, blk: 3, stl: 3, to: 9, deflections: 5, fouls: 19, def_fouls: 18, charges: 1, kills: 4, efg_pct: '49.0%', to_pct: '15.0%', oreb_pct: '22.6%', ftr: 0.22, two_made: 13, two_att: 29, two_pct: '44.8%', three_made: 8, three_att: 22, three_pct: '36.4%', ft_made: 10, ft_att: 11, ft_pct: '90.9%', scoring_opps: 57, shots: 51, ft_trips: 6, two_rate: '56.9%', three_rate: '43.1%', ft: '10/11' },
    },
    my_players: [
      { num: '3', name: 'Peyton Easley', time: '7:59', pts: 2, reb: 2, ast: 0, blk: 0, stl: 1, to: 4, two_a: '0/0', three_a: '0/2', ft_a: '2/2', efg: '0%', pm: '+3' },
      { num: '4', name: 'Quinton Smith', time: '19:32', pts: 5, reb: 3, ast: 4, blk: 0, stl: 0, to: 0, two_a: '2/3', three_a: '0/0', ft_a: '1/3', efg: '66.7%', pm: '+9' },
      { num: '11', name: 'Tyeric Randolph', time: '12:07', pts: 4, reb: 3, ast: 0, blk: 0, stl: 0, to: 1, two_a: '2/4', three_a: '0/0', ft_a: '0/2', efg: '50.0%', pm: '-2', starter: true },
      { num: '12', name: 'Julian Lanier', time: '25:49', pts: 8, reb: 3, ast: 5, blk: 0, stl: 0, to: 2, two_a: '4/7', three_a: '0/2', ft_a: '0/0', efg: '44.4%', pm: '-1', starter: true },
      { num: '14', name: 'Howell Owen', time: '4:36', pts: 0, reb: 1, ast: 0, blk: 0, stl: 0, to: 0, two_a: '0/1', three_a: '0/0', ft_a: '0/0', efg: '0%', pm: '-12' },
      { num: '15', name: 'Easton Hicks', time: '1:37', pts: 0, reb: 0, ast: 0, blk: 0, stl: 0, to: 0, two_a: '0/0', three_a: '0/0', ft_a: '0/0', efg: '—', pm: '+1' },
      { num: '21', name: 'Braylon Whitfield', time: '27:52', pts: 14, reb: 2, ast: 5, blk: 0, stl: 2, to: 1, two_a: '7/10', three_a: '0/1', ft_a: '0/5', efg: '63.6%', pm: '+10', starter: true },
      { num: '22', name: 'Merrick Ham', time: '25:41', pts: 16, reb: 10, ast: 1, blk: 2, stl: 1, to: 0, two_a: '4/8', three_a: '2/5', ft_a: '2/2', efg: '53.8%', pm: '+12', starter: true },
      { num: '24', name: 'Kylen Thomas', time: '23:40', pts: 15, reb: 6, ast: 2, blk: 0, stl: 0, to: 1, two_a: '6/10', three_a: '0/0', ft_a: '3/5', efg: '60.0%', pm: '+2', starter: true },
      { num: '32', name: 'Oliver Purifoy', time: '1:03', pts: 0, reb: 0, ast: 0, blk: 0, stl: 0, to: 0, two_a: '0/0', three_a: '0/0', ft_a: '0/0', efg: '—', pm: '-2' },
    ],
    opp_players: [
      { num: '1', name: 'Christopher Mitchell', time: '23:12', pts: 6, reb: 2, ast: 3, blk: 0, stl: 0, to: 2, two_a: '0/2', three_a: '2/3', ft_a: '0/0', efg: '60.0%', pm: '-10', starter: true },
      { num: '2', name: 'Brandon Bell', time: '23:29', pts: 20, reb: 5, ast: 3, blk: 0, stl: 1, to: 4, two_a: '3/6', three_a: '2/3', ft_a: '8/8', efg: '66.7%', pm: '-4', starter: true },
      { num: '5', name: 'Raymond Buck', time: '7:19', pts: 6, reb: 5, ast: 0, blk: 0, stl: 1, to: 0, two_a: '2/2', three_a: '0/0', ft_a: '2/2', efg: '100.0%', pm: '+4' },
      { num: '10', name: 'Kevin Cochran', time: '1:43', pts: 0, reb: 0, ast: 0, blk: 0, stl: 0, to: 0, two_a: '0/0', three_a: '0/0', ft_a: '0/0', efg: '—', pm: '+2' },
      { num: '12', name: 'Cam Gaines', time: '28:16', pts: 10, reb: 5, ast: 4, blk: 1, stl: 0, to: 0, two_a: '2/4', three_a: '2/9', ft_a: '0/1', efg: '38.5%', pm: '-6', starter: true },
      { num: '13', name: 'Alex Ayala', time: '21:04', pts: 8, reb: 5, ast: 0, blk: 0, stl: 1, to: 0, two_a: '4/8', three_a: '0/1', ft_a: '0/0', efg: '44.4%', pm: '-4' },
      { num: '14', name: 'David Scott', time: '26:33', pts: 8, reb: 6, ast: 1, blk: 2, stl: 0, to: 2, two_a: '1/5', three_a: '2/2', ft_a: '0/0', efg: '57.1%', pm: '-10', starter: true },
      { num: '15', name: 'Ryan Bell', time: '18:20', pts: 2, reb: 0, ast: 1, blk: 0, stl: 0, to: 1, two_a: '1/2', three_a: '0/4', ft_a: '0/0', efg: '16.7%', pm: '+8', starter: true },
    ],
  },
];

// ── Session 2: June 26-28, 2026 ──────────────────────────────────────────────
const SESSION_2_GAMES = [
  {
    id: 's2g1',
    date: 'Jun 26, 2026',
    time: '2:00 PM',
    court: 'Court 01',
    my_team: 'Marietta - GA',
    opp_team: 'Edgewater - FL',
    my_score: 41,
    opp_score: 63,
    result: 'L',
    film_url: 'https://vimeo.com/1205619916',
    team_stats: {
      my: { pts: 41, ast: 7, reb: 33, blk: 1, stl: 5, to: 10, efg_pct: '30.4%', two_made: 11, two_att: 28, two_pct: '39.3%', three_made: 3, three_att: 23, three_pct: '13.0%', shots: 51, two_rate: '54.9%', three_rate: '45.1%' },
      opp: { pts: 63, ast: 12, reb: 23, blk: 2, stl: 9, to: 6, efg_pct: '59.4%', two_made: 18, two_att: 28, two_pct: '64.3%', three_made: 9, three_att: 25, three_pct: '36.0%', shots: 53, two_rate: '52.8%', three_rate: '47.2%' },
    },
    my_players: [
      { num: '3', name: 'Peyton Easley', time: '15:47', pts: 0, reb: 3, ast: 2, blk: 0, stl: 1, to: 0, two_a: '0/1', three_a: '0/4', ft_a: '0/0', efg: '0%', pm: '-16' },
      { num: '4', name: 'Quinton Smith', time: '14:53', pts: 3, reb: 5, ast: 2, blk: 0, stl: 0, to: 0, two_a: '1/4', three_a: '0/0', ft_a: '0/0', efg: '25.0%', pm: '-11' },
      { num: '11', name: 'Tyeric Randolph', time: '20:57', pts: 9, reb: 5, ast: 0, blk: 0, stl: 0, to: 0, two_a: '4/8', three_a: '0/0', ft_a: '0/0', efg: '50.0%', pm: '-15', starter: true },
      { num: '12', name: 'Julian Lanier', time: '23:50', pts: 7, reb: 3, ast: 2, blk: 0, stl: 2, to: 4, two_a: '1/5', three_a: '1/3', ft_a: '0/0', efg: '31.2%', pm: '-16', starter: true },
      { num: '14', name: 'Howell Owen', time: '9:30', pts: 0, reb: 0, ast: 0, blk: 1, stl: 0, to: 0, two_a: '0/0', three_a: '0/3', ft_a: '0/0', efg: '0%', pm: '-11' },
      { num: '15', name: 'Easton Hicks', time: '4:00', pts: 4, reb: 0, ast: 0, blk: 0, stl: 0, to: 0, two_a: '0/1', three_a: '0/1', ft_a: '0/0', efg: '0%', pm: '+4' },
      { num: '21', name: 'Braylon Whitfield', time: '22:38', pts: 10, reb: 8, ast: 0, blk: 0, stl: 1, to: 4, two_a: '4/5', three_a: '0/4', ft_a: '0/0', efg: '44.4%', pm: '-14', starter: true },
      { num: '22', name: 'Merrick Ham', time: '21:47', pts: 8, reb: 5, ast: 0, blk: 0, stl: 1, to: 0, two_a: '1/3', three_a: '2/8', ft_a: '0/0', efg: '36.4%', pm: '-16', starter: true },
      { num: '24', name: 'Kylen Thomas', time: '16:33', pts: 0, reb: 4, ast: 1, blk: 0, stl: 0, to: 2, two_a: '0/1', three_a: '0/0', ft_a: '0/0', efg: '0%', pm: '-15', starter: true },
    ],
    opp_players: [
      { num: '2', name: 'Terrance Walker', time: '24:12', pts: 12, reb: 6, ast: 4, blk: 0, stl: 3, to: 1, two_a: '3/5', three_a: '2/6', ft_a: '0/0', efg: '54.5%', pm: '+22', starter: true },
      { num: '4', name: 'Antonio Richmond', time: '12:27', pts: 0, reb: 0, ast: 0, blk: 0, stl: 2, to: 0, two_a: '0/1', three_a: '0/0', ft_a: '0/0', efg: '0%', pm: '+4', starter: true },
      { num: '5', name: 'Josiah Brutus', time: '13:34', pts: 4, reb: 2, ast: 1, blk: 0, stl: 0, to: 0, two_a: '2/2', three_a: '0/2', ft_a: '0/0', efg: '50.0%', pm: '+14' },
      { num: '10', name: 'Brandon Coleman', time: '7:37', pts: 2, reb: 2, ast: 0, blk: 0, stl: 0, to: 1, two_a: '1/2', three_a: '0/0', ft_a: '0/0', efg: '50.0%', pm: '+7' },
      { num: '11', name: 'Tahj Powery-Malone', time: '16:58', pts: 2, reb: 3, ast: 2, blk: 0, stl: 1, to: 2, two_a: '1/3', three_a: '0/3', ft_a: '0/0', efg: '16.7%', pm: '+10', starter: true },
      { num: '20', name: 'Bryce Benton', time: '14:55', pts: 11, reb: 4, ast: 0, blk: 0, stl: 1, to: 0, two_a: '1/2', three_a: '3/3', ft_a: '0/0', efg: '110.0%', pm: '+5' },
      { num: '21', name: 'Amir Elmahmoud', time: '24:06', pts: 10, reb: 1, ast: 3, blk: 2, stl: 0, to: 1, two_a: '2/2', three_a: '2/5', ft_a: '0/0', efg: '71.4%', pm: '+19', starter: true },
      { num: '23', name: 'Julian Fox', time: '20:11', pts: 18, reb: 3, ast: 1, blk: 0, stl: 1, to: 0, two_a: '6/6', three_a: '2/5', ft_a: '0/0', efg: '81.8%', pm: '+19', starter: true },
      { num: '24', name: 'Demetrick Smith', time: '15:56', pts: 4, reb: 2, ast: 1, blk: 0, stl: 1, to: 1, two_a: '2/5', three_a: '0/1', ft_a: '0/0', efg: '33.3%', pm: '+10' },
    ],
  },
  {
    id: 's2g2',
    date: 'Jun 26, 2026',
    time: '8:00 PM',
    court: 'Court 11',
    my_team: 'Marietta - GA',
    opp_team: 'Byrnes - SC',
    my_score: 35,
    opp_score: 63,
    result: 'L',
    film_url: 'https://vimeo.com/1205555747',
    team_stats: {
      my: { pts: 35, ast: 8, reb: 30, blk: 7, stl: 9, to: 15, efg_pct: '29.2%', two_made: 11, two_att: 32, two_pct: '34.4%', three_made: 2, three_att: 16, three_pct: '12.5%', shots: 48, two_rate: '66.7%', three_rate: '33.3%' },
      opp: { pts: 63, ast: 12, reb: 36, blk: 2, stl: 9, to: 13, efg_pct: '54.5%', two_made: 14, two_att: 28, two_pct: '50.0%', three_made: 11, three_att: 28, three_pct: '39.3%', shots: 56, two_rate: '50.0%', three_rate: '50.0%' },
    },
    my_players: [
      { num: '3', name: 'Peyton Easley', time: '15:47', pts: 3, reb: 2, ast: 1, blk: 0, stl: 0, to: 0, two_a: '0/0', three_a: '1/4', ft_a: '0/0', efg: '37.5%', pm: '-17' },
      { num: '4', name: 'Quinton Smith', time: '17:26', pts: 2, reb: 4, ast: 0, blk: 2, stl: 2, to: 0, two_a: '1/9', three_a: '0/1', ft_a: '0/0', efg: '10.0%', pm: '-16' },
      { num: '10', name: 'Zachary Viola', time: '2:35', pts: 0, reb: 0, ast: 1, blk: 0, stl: 0, to: 0, two_a: '0/0', three_a: '0/1', ft_a: '0/0', efg: '0%', pm: '-6' },
      { num: '11', name: 'Tyeric Randolph', time: '20:05', pts: 0, reb: 8, ast: 0, blk: 2, stl: 1, to: 5, two_a: '0/2', three_a: '0/0', ft_a: '0/0', efg: '0%', pm: '-14', starter: true },
      { num: '12', name: 'Julian Lanier', time: '19:53', pts: 0, reb: 2, ast: 5, blk: 1, stl: 1, to: 0, two_a: '0/0', three_a: '0/1', ft_a: '0/0', efg: '0%', pm: '-14', starter: true },
      { num: '14', name: 'Howell Owen', time: '6:33', pts: 6, reb: 1, ast: 0, blk: 0, stl: 0, to: 0, two_a: '2/3', three_a: '0/1', ft_a: '2/2', efg: '50.0%', pm: '-12' },
      { num: '15', name: 'Easton Hicks', time: '3:54', pts: 0, reb: 0, ast: 0, blk: 0, stl: 0, to: 0, two_a: '0/0', three_a: '0/0', ft_a: '0/0', efg: '0%', pm: '-6' },
      { num: '21', name: 'Braylon Whitfield', time: '18:25', pts: 9, reb: 6, ast: 1, blk: 2, stl: 0, to: 0, two_a: '4/9', three_a: '0/3', ft_a: '1/3', efg: '33.3%', pm: '-11', starter: true },
      { num: '22', name: 'Merrick Ham', time: '21:52', pts: 9, reb: 5, ast: 0, blk: 0, stl: 1, to: 2, two_a: '3/5', three_a: '1/6', ft_a: '0/0', efg: '40.9%', pm: '-17', starter: true },
      { num: '24', name: 'Kylen Thomas', time: '19:29', pts: 6, reb: 1, ast: 1, blk: 0, stl: 3, to: 7, two_a: '1/2', three_a: '0/0', ft_a: '4/6', efg: '50.0%', pm: '-21', starter: true },
      { num: '32', name: 'Oliver Purifoy', time: '3:54', pts: 0, reb: 1, ast: 0, blk: 0, stl: 1, to: 1, two_a: '0/2', three_a: '0/0', ft_a: '0/0', efg: '0%', pm: '-6' },
    ],
    opp_players: [
      { num: '0', name: 'Fabian McClintock', time: '12:42', pts: 10, reb: 2, ast: 1, blk: 0, stl: 0, to: 2, two_a: '5/12', three_a: '0/0', ft_a: '0/0', efg: '41.7%', pm: '+7', starter: true },
      { num: '1', name: 'Colt Fowler', time: '23:59', pts: 9, reb: 7, ast: 0, blk: 0, stl: 1, to: 3, two_a: '0/1', three_a: '3/6', ft_a: '0/0', efg: '64.3%', pm: '+20', starter: true },
      { num: '2', name: 'Walker Greene', time: '12:56', pts: 2, reb: 2, ast: 2, blk: 0, stl: 1, to: 1, two_a: '1/1', three_a: '0/5', ft_a: '0/0', efg: '16.7%', pm: '+15' },
      { num: '3', name: 'Kamden Hack', time: '15:40', pts: 3, reb: 6, ast: 1, blk: 0, stl: 1, to: 0, two_a: '0/3', three_a: '1/2', ft_a: '0/0', efg: '30.0%', pm: '+16' },
      { num: '4', name: 'Darius Stover', time: '15:22', pts: 2, reb: 2, ast: 1, blk: 0, stl: 1, to: 2, two_a: '1/1', three_a: '0/1', ft_a: '0/0', efg: '50.0%', pm: '+12' },
      { num: '5', name: 'Josh Dixon', time: '21:42', pts: 16, reb: 5, ast: 2, blk: 0, stl: 2, to: 0, two_a: '2/4', three_a: '4/8', ft_a: '0/0', efg: '66.7%', pm: '+18', starter: true },
      { num: '11', name: 'Kellen Leverett', time: '25:59', pts: 11, reb: 3, ast: 4, blk: 0, stl: 3, to: 0, two_a: '0/0', three_a: '3/6', ft_a: '2/3', efg: '75.0%', pm: '+25', starter: true },
      { num: '12', name: "J'sean Sanders", time: '13:59', pts: 10, reb: 6, ast: 0, blk: 2, stl: 0, to: 2, two_a: '5/6', three_a: '0/0', ft_a: '0/0', efg: '83.3%', pm: '+17', starter: true },
      { num: '20', name: 'Gabe Hinton', time: '2:35', pts: 0, reb: 0, ast: 1, blk: 0, stl: 0, to: 0, two_a: '0/0', three_a: '0/0', ft_a: '0/0', efg: '0%', pm: '+6' },
      { num: '23', name: 'Gabe Hicks', time: '5:02', pts: 0, reb: 3, ast: 0, blk: 0, stl: 0, to: 3, two_a: '0/0', three_a: '0/0', ft_a: '0/0', efg: '0%', pm: '+4' },
    ],
  },
  {
    id: 's2g3',
    date: 'Jun 28, 2026',
    time: '11:00 AM',
    court: 'Court 07',
    my_team: 'Marietta - GA',
    opp_team: 'Pembroke Pines Charter - FL',
    my_score: 45,
    opp_score: 49,
    result: 'L',
    film_url: 'https://vimeo.com/1205600419',
    team_stats: {
      my: { pts: 45, efg_pct: '43.0%', two_made: 17, two_att: 32, two_pct: '53.1%', three_made: 1, three_att: 11, three_pct: '9.1%', shots: 43, two_rate: '74.4%', three_rate: '25.6%', reb: 30, ast: 10, blk: 2, stl: 4, to: 18 },
      opp: { pts: 49, efg_pct: '57.9%', two_made: 13, two_att: 21, two_pct: '61.9%', three_made: 6, three_att: 17, three_pct: '35.3%', shots: 38, two_rate: '55.3%', three_rate: '44.7%', reb: 17, ast: 12, blk: 0, stl: 12, to: 12 },
    },
    my_players: [
      { num: '3', name: 'Peyton Easley', time: '4:43', pts: 0, reb: 0, ast: 0, blk: 0, stl: 0, to: 1, two_a: '0/0', three_a: '0/1', ft_a: '0/0', efg: '0%', pm: '-10' },
      { num: '4', name: 'Quinton Smith', time: '19:51', pts: 6, reb: 1, ast: 0, blk: 0, stl: 1, to: 2, two_a: '3/5', three_a: '0/0', ft_a: '0/0', efg: '60.0%', pm: '-2' },
      { num: '11', name: 'Tyeric Randolph', time: '19:59', pts: 6, reb: 8, ast: 2, blk: 0, stl: 0, to: 1, two_a: '3/6', three_a: '0/0', ft_a: '0/0', efg: '50.0%', pm: '+1', starter: true },
      { num: '12', name: 'Julian Lanier', time: '20:43', pts: 0, reb: 2, ast: 2, blk: 0, stl: 0, to: 1, two_a: '0/0', three_a: '0/3', ft_a: '0/0', efg: '0%', pm: '-2', starter: true },
      { num: '14', name: 'Howell Owen', time: '7:52', pts: 0, reb: 1, ast: 1, blk: 0, stl: 0, to: 0, two_a: '0/2', three_a: '0/1', ft_a: '0/0', efg: '0%', pm: '+3' },
      { num: '15', name: 'Easton Hicks', time: '6:31', pts: 5, reb: 1, ast: 1, blk: 0, stl: 0, to: 1, two_a: '2/2', three_a: '0/0', ft_a: '0/0', efg: '100.0%', pm: '-2' },
      { num: '21', name: 'Braylon Whitfield', time: '27:40', pts: 10, reb: 8, ast: 2, blk: 0, stl: 1, to: 5, two_a: '3/7', three_a: '0/1', ft_a: '0/0', efg: '37.5%', pm: '-8', starter: true },
      { num: '22', name: 'Merrick Ham', time: '21:06', pts: 12, reb: 6, ast: 1, blk: 2, stl: 2, to: 3, two_a: '4/6', three_a: '1/5', ft_a: '0/0', efg: '50.0%', pm: '-4', starter: true },
      { num: '24', name: 'Kylen Thomas', time: '21:30', pts: 6, reb: 2, ast: 1, blk: 0, stl: 0, to: 4, two_a: '2/4', three_a: '0/0', ft_a: '0/0', efg: '50.0%', pm: '+4', starter: true },
    ],
    opp_players: [
      { num: '0', name: 'Joaquin Leon', time: '5:24', pts: 0, reb: 1, ast: 0, blk: 0, stl: 0, to: 1, two_a: '0/0', three_a: '0/1', ft_a: '0/0', efg: '0%', pm: '-6' },
      { num: '3', name: 'Sebastion Moreno', time: '26:30', pts: 10, reb: 1, ast: 4, blk: 0, stl: 1, to: 0, two_a: '2/3', three_a: '2/4', ft_a: '0/0', efg: '71.4%', pm: '+1', starter: true },
      { num: '4', name: 'Jude Wade', time: '21:02', pts: 6, reb: 1, ast: 1, blk: 0, stl: 0, to: 2, two_a: '3/3', three_a: '0/0', ft_a: '0/0', efg: '100.0%', pm: '+3' },
      { num: '5', name: 'Damari Foster', time: '25:25', pts: 7, reb: 1, ast: 2, blk: 0, stl: 1, to: 0, two_a: '1/4', three_a: '1/4', ft_a: '0/0', efg: '31.2%', pm: '+3', starter: true },
      { num: '10', name: 'Naeem Nasai', time: '20:14', pts: 8, reb: 4, ast: 0, blk: 0, stl: 1, to: 1, two_a: '3/3', three_a: '0/1', ft_a: '0/0', efg: '75.0%', pm: '+1', starter: true },
      { num: '12', name: 'Alex Vardakis', time: '29:20', pts: 12, reb: 1, ast: 1, blk: 0, stl: 6, to: 3, two_a: '3/7', three_a: '2/3', ft_a: '0/0', efg: '60.0%', pm: '+9', starter: true },
      { num: '15', name: 'Caio Leopoldo', time: '16:34', pts: 6, reb: 4, ast: 4, blk: 0, stl: 3, to: 5, two_a: '1/1', three_a: '1/4', ft_a: '0/0', efg: '50.0%', pm: '+10', starter: true },
      { num: '25', name: '#25', time: '5:28', pts: 0, reb: 1, ast: 0, blk: 0, stl: 0, to: 0, two_a: '0/0', three_a: '0/0', ft_a: '0/0', efg: '—', pm: '-1' },
    ],
  },
  {
    id: 's2g4',
    date: 'Jun 28, 2026',
    time: '1:00 PM',
    court: 'Court 06',
    my_team: 'Marietta - GA',
    opp_team: 'Mater Lakes HS - FL',
    my_score: 48,
    opp_score: 56,
    result: 'L',
    film_url: 'https://vimeo.com/1205593334',
    team_stats: {
      my: { pts: 48, efg_pct: '35.8%', two_made: 16, two_att: 41, two_pct: '39.0%', three_made: 2, three_att: 12, three_pct: '16.7%', shots: 53, two_rate: '77.4%', three_rate: '22.6%', reb: 35, ast: 11, blk: 0, stl: 2, to: 10 },
      opp: { pts: 56, efg_pct: '52.4%', two_made: 16, two_att: 23, two_pct: '69.6%', three_made: 4, three_att: 19, three_pct: '21.1%', shots: 42, two_rate: '54.8%', three_rate: '45.2%', reb: 25, ast: 12, blk: 4, stl: 6, to: 8 },
    },
    my_players: [
      { num: '3', name: 'Peyton Easley', time: '12:45', pts: 0, reb: 1, ast: 0, blk: 0, stl: 1, to: 1, two_a: '0/0', three_a: '0/2', ft_a: '0/0', efg: '0%', pm: '-11' },
      { num: '4', name: 'Quinton Smith', time: '23:24', pts: 6, reb: 3, ast: 2, blk: 0, stl: 0, to: 2, two_a: '2/6', three_a: '0/1', ft_a: '0/0', efg: '28.6%', pm: '-8' },
      { num: '11', name: 'Tyeric Randolph', time: '15:56', pts: 2, reb: 3, ast: 1, blk: 0, stl: 0, to: 0, two_a: '1/1', three_a: '0/0', ft_a: '0/0', efg: '100.0%', pm: '+2', starter: true },
      { num: '12', name: 'Julian Lanier', time: '4:23', pts: 0, reb: 0, ast: 1, blk: 0, stl: 0, to: 0, two_a: '0/0', three_a: '0/0', ft_a: '0/0', efg: '0%', pm: '-1', starter: true },
      { num: '14', name: 'Howell Owen', time: '12:54', pts: 14, reb: 5, ast: 1, blk: 0, stl: 0, to: 1, two_a: '3/5', three_a: '2/4', ft_a: '0/0', efg: '66.7%', pm: '-1' },
      { num: '15', name: 'Easton Hicks', time: '9:13', pts: 0, reb: 1, ast: 0, blk: 0, stl: 0, to: 1, two_a: '0/1', three_a: '0/0', ft_a: '0/0', efg: '0%', pm: '-7' },
      { num: '21', name: 'Braylon Whitfield', time: '24:12', pts: 12, reb: 7, ast: 3, blk: 0, stl: 1, to: 1, two_a: '4/13', three_a: '0/2', ft_a: '0/0', efg: '26.7%', pm: '-6', starter: true },
      { num: '22', name: 'Merrick Ham', time: '26:14', pts: 8, reb: 3, ast: 3, blk: 0, stl: 0, to: 2, two_a: '3/5', three_a: '0/3', ft_a: '0/0', efg: '37.5%', pm: '-8', starter: true },
      { num: '24', name: 'Kylen Thomas', time: '20:54', pts: 6, reb: 5, ast: 0, blk: 0, stl: 0, to: 1, two_a: '3/10', three_a: '0/0', ft_a: '0/0', efg: '30.0%', pm: '0', starter: true },
    ],
    opp_players: [
      { num: '0', name: 'Denver Turner', time: '22:51', pts: 12, reb: 4, ast: 0, blk: 1, stl: 1, to: 1, two_a: '5/8', three_a: '0/2', ft_a: '0/0', efg: '50.0%', pm: '+4', starter: true },
      { num: '1', name: 'Ricardo Diaz', time: '18:11', pts: 9, reb: 2, ast: 0, blk: 0, stl: 1, to: 2, two_a: '2/2', three_a: '1/2', ft_a: '0/0', efg: '87.5%', pm: '+9' },
      { num: '2', name: 'Delan Montgomery', time: '16:50', pts: 10, reb: 6, ast: 1, blk: 2, stl: 2, to: 1, two_a: '3/4', three_a: '0/2', ft_a: '0/0', efg: '50.0%', pm: '+7', starter: true },
      { num: '3', name: 'Julian Perez', time: '18:51', pts: 6, reb: 2, ast: 1, blk: 0, stl: 1, to: 0, two_a: '2/2', three_a: '0/1', ft_a: '0/0', efg: '66.7%', pm: '+2', starter: true },
      { num: '4', name: 'Khamani Pessoa', time: '12:41', pts: 0, reb: 4, ast: 3, blk: 0, stl: 1, to: 1, two_a: '0/0', three_a: '0/0', ft_a: '0/0', efg: '—', pm: '+5' },
      { num: '5', name: 'Miguel Orbe', time: '26:09', pts: 15, reb: 3, ast: 4, blk: 0, stl: 0, to: 1, two_a: '2/4', three_a: '3/11', ft_a: '0/0', efg: '43.3%', pm: '+6', starter: true },
      { num: '12', name: 'Charleson Baker', time: '2:30', pts: 0, reb: 0, ast: 0, blk: 0, stl: 0, to: 0, two_a: '0/0', three_a: '0/0', ft_a: '0/0', efg: '—', pm: '-3' },
      { num: '13', name: 'Anthony Torres', time: '23:02', pts: 4, reb: 2, ast: 2, blk: 1, stl: 0, to: 2, two_a: '2/2', three_a: '0/0', ft_a: '0/0', efg: '100.0%', pm: '+9', starter: true },
      { num: '15', name: 'Brandon Bloch', time: '8:50', pts: 0, reb: 0, ast: 1, blk: 0, stl: 0, to: 0, two_a: '0/1', three_a: '0/1', ft_a: '0/0', efg: '0%', pm: '+1' },
    ],
  },
];

function PlayerTable({ players, teamLabel }) {
  return (
    <div className="overflow-x-auto">
      <p className="text-xs font-bold uppercase tracking-widest mb-2 px-1" style={{ color: ORANGE }}>{teamLabel}</p>
      <table className="w-full text-xs min-w-[700px]">
        <thead>
          <tr className="border-b" style={{ borderColor: 'rgba(255,255,255,0.07)' }}>
            {['#', 'Player', 'Time', 'PTS', 'REB', 'AST', 'BLK', 'STL', 'TO', '2Pt/A', '3Pt/A', 'FT/A', 'EFG%', '+/-'].map(h => (
              <th key={h} className="text-left py-1.5 px-1 text-gray-600 font-bold uppercase tracking-wider whitespace-nowrap">{h}</th>
            ))}
          </tr>
        </thead>
        <tbody>
          {players.map((p, i) => {
            const slug = MARIETTA_PORTFOLIO_SLUGS[p.name];
            return (
              <tr key={i} className="border-b" style={{ borderColor: 'rgba(255,255,255,0.03)' }}>
                <td className="py-1.5 px-1 text-gray-500">{p.num}</td>
                <td className="py-1.5 px-1 text-white font-medium whitespace-nowrap">
                  {slug ? (
                    <Link to={`/player/${slug}`} className="hover:text-orange-400 transition-colors">{p.name}</Link>
                  ) : (
                    p.name
                  )}{p.starter ? <span className="text-orange-500 ml-0.5">*</span> : null}
                </td>
                <td className="py-1.5 px-1 text-gray-500">{p.time}</td>
                <td className="py-1.5 px-1 font-bold text-white">{p.pts}</td>
                <td className="py-1.5 px-1 text-gray-300">{p.reb}</td>
                <td className="py-1.5 px-1 text-gray-300">{p.ast}</td>
                <td className="py-1.5 px-1 text-gray-400">{p.blk}</td>
                <td className="py-1.5 px-1 text-gray-400">{p.stl}</td>
                <td className="py-1.5 px-1 text-gray-400">{p.to}</td>
                <td className="py-1.5 px-1 text-gray-400">{p.two_a}</td>
                <td className="py-1.5 px-1 text-gray-400">{p.three_a}</td>
                <td className="py-1.5 px-1 text-gray-400">{p.ft_a}</td>
                <td className="py-1.5 px-1 text-gray-400">{p.efg}</td>
                <td className={`py-1.5 px-1 font-bold ${p.pm && p.pm.startsWith('+') ? 'text-green-400' : p.pm && p.pm !== '0' ? 'text-red-400' : 'text-gray-500'}`}>{p.pm}</td>
              </tr>
            );
          })}
        </tbody>
      </table>
    </div>
  );
}

function MariettaTeamStats({ game }) {
  const [expanded, setExpanded] = useState(false);
  const my = game.team_stats.my;
  const opp = game.team_stats.opp;

  const parseFt = (ftStr) => {
    if (!ftStr || !ftStr.includes('/')) return { made: '—', att: '—', pct: '—' };
    const [made, att] = ftStr.split('/').map(Number);
    const pct = att > 0 ? ((made / att) * 100).toFixed(1) + '%' : '—';
    return { made, att, pct };
  };
  const myFt = parseFt(my?.ft);
  const oppFt = parseFt(opp?.ft);

  const fmt = (v) => (v == null || v === '') ? '—' : v;

  const summaryStats = [
    { label: 'AST', my: my?.ast, opp: opp?.ast },
    { label: 'REB', my: my?.reb, opp: opp?.reb },
    { label: 'eFG%', my: my?.efg_pct, opp: opp?.efg_pct },
    { label: 'FTA', my: myFt.att, opp: oppFt.att },
    { label: 'FT%', my: myFt.pct, opp: oppFt.pct },
    { label: '2FG%', my: my?.two_pct, opp: opp?.two_pct },
    { label: '3FG%', my: my?.three_pct, opp: opp?.three_pct },
    { label: 'TO', my: my?.to, opp: opp?.to },
  ];

  const fullStats = [
    { label: 'Points', my: my?.pts, opp: opp?.pts },
    { label: 'Pts Per Possession', my: my?.ppp, opp: opp?.ppp },
    { label: '— Four Factors —', header: true },
    { label: 'Eff. FG%', my: my?.efg_pct, opp: opp?.efg_pct },
    { label: 'Turnover %', my: my?.to_pct, opp: opp?.to_pct },
    { label: 'Off. Reb %', my: my?.oreb_pct, opp: opp?.oreb_pct },
    { label: 'Free Throw Rate', my: my?.ftr, opp: opp?.ftr },
    { label: '— Shooting —', header: true },
    { label: '2 Pt (M/A)', my: my?.two_made != null ? `${my.two_made}/${my.two_att}` : null, opp: opp?.two_made != null ? `${opp.two_made}/${opp.two_att}` : null },
    { label: '2 Pt %', my: my?.two_pct, opp: opp?.two_pct },
    { label: '3 Pt (M/A)', my: my?.three_made != null ? `${my.three_made}/${my.three_att}` : null, opp: opp?.three_made != null ? `${opp.three_made}/${opp.three_att}` : null },
    { label: '3 Pt %', my: my?.three_pct, opp: opp?.three_pct },
    { label: 'Free Throws (M/A)', my: my?.ft_made != null ? `${my.ft_made}/${my.ft_att}` : null, opp: opp?.ft_made != null ? `${opp.ft_made}/${opp.ft_att}` : null },
    { label: 'FT %', my: my?.ft_pct, opp: opp?.ft_pct },
    { label: 'Scoring Opps', my: my?.scoring_opps, opp: opp?.scoring_opps },
    { label: 'Total Shots', my: my?.shots, opp: opp?.shots },
    { label: 'FT Trips', my: my?.ft_trips, opp: opp?.ft_trips },
    { label: '2 Pt Rate', my: my?.two_rate, opp: opp?.two_rate },
    { label: '3 Pt Rate', my: my?.three_rate, opp: opp?.three_rate },
    { label: '— Team Play —', header: true },
    { label: 'Assists', my: my?.ast, opp: opp?.ast },
    { label: 'Turnovers', my: my?.to, opp: opp?.to },
    { label: 'Turnover %', my: my?.to_pct, opp: opp?.to_pct },
    { label: 'Steals', my: my?.stl, opp: opp?.stl },
    { label: 'Blocks', my: my?.blk, opp: opp?.blk },
    { label: 'Deflections', my: my?.deflections, opp: opp?.deflections },
    { label: 'Fouls', my: my?.fouls, opp: opp?.fouls },
    { label: 'Def. Fouls', my: my?.def_fouls, opp: opp?.def_fouls },
    { label: 'Charges Taken', my: my?.charges, opp: opp?.charges },
    { label: 'Kills (3 stops)', my: my?.kills, opp: opp?.kills },
    { label: '— Rebounding —', header: true },
    { label: 'Total Rebounds', my: my?.reb, opp: opp?.reb },
    { label: 'Off. Rebounds', my: my?.oreb, opp: opp?.oreb },
    { label: 'Def. Rebounds', my: my?.dreb, opp: opp?.dreb },
    { label: 'Off. Reb %', my: my?.oreb_pct, opp: opp?.oreb_pct },
  ];

  return (
    <div>
      {/* Scoreboard */}
      <div className="flex items-center justify-between px-5 py-4 border-b" style={{ borderColor: 'rgba(255,255,255,0.05)' }}>
        <div className="text-center flex-1">
          <p className="text-xs font-bold uppercase tracking-wider truncate" style={{ color: ORANGE }}>{game.my_team}</p>
          <p className="text-4xl font-black" style={{ color: ORANGE }}>{fmt(my?.pts)}</p>
        </div>
        <div className="text-gray-600 font-bold text-sm px-4">vs</div>
        <div className="text-center flex-1">
          <p className="text-xs font-bold uppercase tracking-wider truncate text-gray-400">{game.opp_team}</p>
          <p className="text-4xl font-black text-white">{fmt(opp?.pts)}</p>
        </div>
      </div>

      {/* Summary stats grid — my team */}
      <div className="px-5 pt-4 pb-2">
        <p className="text-xs font-bold uppercase tracking-widest mb-3" style={{ color: ORANGE }}>{game.my_team}</p>
        <div className="grid grid-cols-4 sm:grid-cols-8 gap-2">
          {summaryStats.map(s => (
            <div key={s.label} className="text-center">
              <div className="text-base font-black text-white">{fmt(s.my)}</div>
              <div className="text-[10px] text-gray-600 uppercase tracking-wider leading-tight">{s.label}</div>
            </div>
          ))}
        </div>
      </div>

      {/* Summary stats grid — opponent */}
      <div className="px-5 pt-2 pb-3 border-b" style={{ borderColor: 'rgba(255,255,255,0.05)' }}>
        <p className="text-xs font-bold uppercase tracking-widest mb-3 text-gray-500">{game.opp_team}</p>
        <div className="grid grid-cols-4 sm:grid-cols-8 gap-2">
          {summaryStats.map(s => (
            <div key={s.label} className="text-center">
              <div className="text-base font-black text-gray-400">{fmt(s.opp)}</div>
              <div className="text-[10px] text-gray-700 uppercase tracking-wider leading-tight">{s.label}</div>
            </div>
          ))}
        </div>
      </div>

      {/* Full Game Stats toggle */}
      <button
        onClick={() => setExpanded(!expanded)}
        className="flex items-center justify-center gap-2 w-full py-3 text-xs font-bold uppercase tracking-widest border-b transition-colors"
        style={{ borderColor: 'rgba(255,255,255,0.05)', color: expanded ? ORANGE : 'rgba(255,255,255,0.35)' }}
      >
        {expanded ? <ChevronUp className="w-3.5 h-3.5" /> : <ChevronDown className="w-3.5 h-3.5" />}
        Full Game Stats
      </button>

      {/* Expanded full stats table */}
      {expanded && (
        <div className="px-4 pb-4">
          <div className="grid grid-cols-3 gap-2 py-2 text-xs font-bold uppercase tracking-wider border-b mb-1" style={{ borderColor: 'rgba(255,255,255,0.07)' }}>
            <div style={{ color: ORANGE }} className="truncate">{game.my_team}</div>
            <div className="text-center text-gray-600">Stat</div>
            <div className="text-right text-gray-500 truncate">{game.opp_team}</div>
          </div>
          {fullStats.map(s => s.header ? (
            <div key={s.label} className="text-xs font-black uppercase tracking-widest mt-3 mb-1 px-1" style={{ color: ORANGE }}>{s.label.replace(/—/g, '').trim()}</div>
          ) : (
            <div key={s.label} className="grid grid-cols-3 gap-2 py-1.5 rounded-lg px-1" style={{ background: 'rgba(255,255,255,0.02)' }}>
              <div className="text-sm font-bold text-white">{fmt(s.my)}</div>
              <div className="text-center text-xs text-gray-600 self-center">{s.label}</div>
              <div className="text-sm font-bold text-right text-gray-300">{fmt(s.opp)}</div>
            </div>
          ))}
        </div>
      )}
    </div>
  );
}

function GameCard({ game }) {
  const [open, setOpen] = useState(false);
  const [showMyPlayers, setShowMyPlayers] = useState(false);
  const [showOppPlayers, setShowOppPlayers] = useState(false);

  return (
    <div className="rounded-xl border overflow-hidden" style={{ borderColor: 'rgba(255,255,255,0.07)', background: '#0a0a0a' }}>
      {/* Header */}
      <button
        onClick={() => setOpen(o => !o)}
        className="w-full flex items-center justify-between px-4 py-3 hover:bg-white/[0.02] transition-colors text-left"
      >
        <p className="font-bold text-sm truncate flex-1 min-w-0">
          <span style={{ color: ORANGE }}>{game.my_team}</span>
          <span className="text-gray-500 mx-1">vs</span>
          <span className="text-white">{game.opp_team}</span>
          <span className="text-gray-600 ml-2">· {game.date} · {game.time}</span>
        </p>
        <div className="flex items-center gap-2 flex-shrink-0 ml-2">
          {game.film_url && (
            <a href={game.film_url} target="_blank" rel="noopener noreferrer"
              onClick={e => e.stopPropagation()}
              className="p-1.5 rounded-lg text-gray-500 hover:text-white transition-colors"
              style={{ background: 'rgba(255,255,255,0.05)' }}>
              <ExternalLink className="w-3 h-3" />
            </a>
          )}
          {open ? <ChevronUp className="w-4 h-4 text-gray-500" /> : <ChevronDown className="w-4 h-4 text-gray-500" />}
        </div>
      </button>

      {open && (
        <div className="border-t" style={{ borderColor: 'rgba(255,255,255,0.05)' }}>
          {/* Game Film */}
          {game.film_url && (
            <div className="aspect-video bg-black">
              <iframe
                src={vimeoEmbed(game.film_url)}
                className="w-full h-full"
                allow="autoplay; fullscreen; picture-in-picture"
                allowFullScreen
                title={`${game.my_team} vs ${game.opp_team}`}
              />
            </div>
          )}
          {/* Team Stats */}
          <MariettaTeamStats game={game} />

          {/* Player Stats toggles */}
          {game.my_players.length > 0 && (
            <div className="px-4 pb-4 pt-2 space-y-3">
              {/* My team players */}
              <div>
                <button
                  onClick={() => setShowMyPlayers(o => !o)}
                  className="flex items-center gap-2 text-xs font-bold uppercase tracking-widest w-full py-2 border-t transition-colors"
                  style={{ borderColor: 'rgba(255,255,255,0.07)', color: showMyPlayers ? ORANGE : 'rgba(255,255,255,0.4)' }}
                >
                  <Users className="w-3.5 h-3.5" />
                  {game.my_team} Player Stats
                  {showMyPlayers ? <ChevronUp className="w-3.5 h-3.5 ml-auto" /> : <ChevronDown className="w-3.5 h-3.5 ml-auto" />}
                </button>
                {showMyPlayers && <PlayerTable players={game.my_players} teamLabel={game.my_team} />}
              </div>

              {/* Opponent players */}
              <div>
                <button
                  onClick={() => setShowOppPlayers(o => !o)}
                  className="flex items-center gap-2 text-xs font-bold uppercase tracking-widest w-full py-2 border-t transition-colors"
                  style={{ borderColor: 'rgba(255,255,255,0.07)', color: showOppPlayers ? ORANGE : 'rgba(255,255,255,0.4)' }}
                >
                  <Users className="w-3.5 h-3.5" />
                  {game.opp_team} Player Stats
                  {showOppPlayers ? <ChevronUp className="w-3.5 h-3.5 ml-auto" /> : <ChevronDown className="w-3.5 h-3.5 ml-auto" />}
                </button>
                {showOppPlayers && <PlayerTable players={game.opp_players} teamLabel={game.opp_team} />}
              </div>
            </div>
          )}
        </div>
      )}
    </div>
  );
}

function SessionSection({ title, games, defaultOpen = false }) {
  const [open, setOpen] = useState(defaultOpen);
  const wins = games.filter(g => g.result === 'W').length;
  const losses = games.filter(g => g.result === 'L').length;

  return (
    <div className="rounded-2xl border overflow-hidden" style={{ borderColor: 'rgba(255,255,255,0.07)', background: '#050505' }}>
      <button
        onClick={() => setOpen(o => !o)}
        className="w-full flex items-center justify-between px-5 py-4 hover:bg-white/[0.02] transition-colors text-left"
      >
        <div>
          <p className="font-barlow font-black text-lg uppercase tracking-wide text-white">
            {title}
          </p>
          <p className="text-xs text-gray-500 mt-0.5">
            {games.length} games · <span className="text-green-400 font-bold">{wins}W</span> <span className="text-red-400 font-bold">{losses}L</span>
          </p>
        </div>
        {open ? <ChevronUp className="w-5 h-5" style={{ color: ORANGE }} /> : <ChevronDown className="w-5 h-5 text-gray-500" />}
      </button>

      {open && (
        <div className="border-t px-4 pb-4 pt-3 space-y-3" style={{ borderColor: 'rgba(255,255,255,0.06)' }}>
          {games.map(game => (
            <GameCard key={game.id} game={game} />
          ))}
        </div>
      )}
    </div>
  );
}

export default function MariettaGameStats() {
  return (
    <div className="space-y-4">
      <SessionSection
        title="GBCA Live Period — Session 1 · June 12–13, 2026"
        games={SESSION_1_GAMES}
        defaultOpen={false}
      />
      <SessionSection
        title="GBCA Live Period — Session 2 SE Regional · June 26–28, 2026"
        games={SESSION_2_GAMES}
        defaultOpen={true}
      />
      <p className="text-xs text-gray-700 text-center pt-2">Stats powered by Hoopsalytics · * = Starter</p>
    </div>
  );
}

src/components/georgia/MiltonGameStats.jsx

import { useState, useEffect } from 'react';
import { Link } from 'react-router-dom';
import { ChevronDown, ChevronUp, Users, ExternalLink } from 'lucide-react';
import { base44 } from '@/api/base44Client';

const ORANGE = '#FF6A00';

// Milton player portfolio slug map
const MILTON_PORTFOLIO_SLUGS = {
  'Ty Nealis': 'ty-nealis',
  'Mason Pridgett': 'mason-pridgett',
  'Pierce Strom': 'pierce-strom',
  'Kaden King': 'kaden-king',
  'William Golden': 'william-golden',
  'Michael Ogunyemi': 'michael-ogunyemi',
  'Beau Selby': 'beau-selby',
  'CJ Omoyele': 'cj-omoyele',
  'Graham Whitehart': 'graham-whitehart',
  'Jackson Harrison': 'jackson-harrison',
  'Jamison Durr': 'jamison-durr',
  'Solomon Bratton': 'solomon-bratton',
  'Cole Wright': 'cole-wright',
  'Hezron Luyindula': 'hezron-luyindula',
};

function vimeoEmbed(url) {
  const m = url?.match(/vimeo\.com\/(\d+)/);
  return m ? `https://player.vimeo.com/video/${m[1]}` : url;
}

function normalizeTeam(name) {
  return (name || '')
    .toLowerCase()
    .replace(/\s*-\s*[a-z]{2}$/, '')  // strip state suffix like " - GA"
    .replace(/[^a-z0-9]/g, '');
}

function toISODate(dateStr) {
  const d = new Date(dateStr);
  if (isNaN(d.getTime())) return dateStr;
  return d.toISOString().split('T')[0];
}

function teamsMatch(a, b) {
  const na = normalizeTeam(a);
  const nb = normalizeTeam(b);
  return na === nb || na.includes(nb) || nb.includes(na);
}

// ── Session 1: June 12-14, 2026 ──────────────────────────────────────────────
const SESSION_1_GAMES = [
  {
    id: 's1g1',
    date: 'Jun 12, 2026',
    time: '6:00 PM',
    court: 'Court 05',
    game_id: '36159',
    my_team: 'Milton - GA',
    opp_team: 'Cedar Grove - GA',
    my_score: 72,
    opp_score: 38,
    result: 'W',
    film_url: null,
    team_stats: {
      my: { pts: 72, ppp: 1.18, ast: 16, reb: 42, oreb: 17, dreb: 25, blk: 2, stl: 13, to: 12, deflections: 10, fouls: 9, def_fouls: 9, charges: 0, kills: 11, efg_pct: '53.4%', to_pct: '16.9%', oreb_pct: '51.5%', ftr: 0.31, two_made: 24, two_att: 43, two_pct: '55.8%', three_made: 5, three_att: 16, three_pct: '31.2%', ft_made: 9, ft_att: 18, ft_pct: '50.0%', scoring_opps: 69, shots: 59, ft_trips: 10, two_rate: '72.9%', three_rate: '27.1%', ft: '9/18' },
      opp: { pts: 38, ppp: 0.62, ast: 9, reb: 20, oreb: 4, dreb: 16, blk: 1, stl: 7, to: 17, deflections: 3, fouls: 16, def_fouls: 14, charges: 0, kills: 2, efg_pct: '36.4%', to_pct: '27.9%', oreb_pct: '13.8%', ftr: 0.18, two_made: 7, two_att: 17, two_pct: '41.2%', three_made: 6, three_att: 27, three_pct: '22.2%', ft_made: 6, ft_att: 8, ft_pct: '75.0%', scoring_opps: 48, shots: 44, ft_trips: 4, two_rate: '38.6%', three_rate: '61.4%', ft: '6/8' },
    },
    my_players: [
      { num: '0', name: 'Ty Nealis', time: '6:16', pts: 0, reb: 2, ast: 0, blk: 0, stl: 0, to: 0, two_a: '0/0', three_a: '0/0', ft_a: '0/2', efg: '—', pm: '+9' },
      { num: '1', name: 'Mason Pridgett', time: '22:49', pts: 2, reb: 2, ast: 4, blk: 0, stl: 1, to: 2, two_a: '1/6', three_a: '0/1', ft_a: '0/0', efg: '14.3%', pm: '+27', starter: true },
      { num: '3', name: 'Pierce Strom', time: '13:35', pts: 9, reb: 0, ast: 0, blk: 0, stl: 0, to: 0, two_a: '0/0', three_a: '3/4', ft_a: '0/0', efg: '112.5%', pm: '+21', starter: true },
      { num: '4', name: 'Kaden King', time: '2:32', pts: 0, reb: 0, ast: 0, blk: 0, stl: 0, to: 0, two_a: '0/0', three_a: '0/0', ft_a: '0/0', efg: '—', pm: '+6' },
      { num: '5', name: 'William Golden', time: '23:18', pts: 17, reb: 8, ast: 5, blk: 0, stl: 3, to: 2, two_a: '5/8', three_a: '1/3', ft_a: '4/4', efg: '59.1%', pm: '+29', starter: true },
      { num: '10', name: 'Michael Ogunyemi', time: '4:43', pts: 4, reb: 2, ast: 0, blk: 0, stl: 1, to: 0, two_a: '2/3', three_a: '0/0', ft_a: '0/0', efg: '66.7%', pm: '+8' },
      { num: '11', name: 'Beau Selby', time: '9:01', pts: 0, reb: 1, ast: 0, blk: 0, stl: 1, to: 0, two_a: '0/0', three_a: '0/2', ft_a: '0/0', efg: '0%', pm: '+1' },
      { num: '12', name: 'CJ Omoyele', time: '8:54', pts: 0, reb: 0, ast: 2, blk: 1, stl: 1, to: 1, two_a: '0/3', three_a: '0/0', ft_a: '0/4', efg: '0%', pm: '0' },
      { num: '13', name: 'Graham Whitehart', time: '9:58', pts: 0, reb: 0, ast: 0, blk: 0, stl: 1, to: 2, two_a: '0/0', three_a: '0/0', ft_a: '0/0', efg: '—', pm: '+2' },
      { num: '14', name: 'Jackson Harrison', time: '19:19', pts: 5, reb: 6, ast: 0, blk: 0, stl: 1, to: 1, two_a: '1/2', three_a: '1/6', ft_a: '0/0', efg: '31.2%', pm: '+21', starter: true },
      { num: '15', name: 'Jamison Durr', time: '1:21', pts: 2, reb: 1, ast: 0, blk: 0, stl: 1, to: 1, two_a: '0/2', three_a: '0/0', ft_a: '2/2', efg: '0%', pm: '+4' },
      { num: '22', name: 'Solomon Bratton', time: '24:37', pts: 31, reb: 17, ast: 5, blk: 1, stl: 3, to: 3, two_a: '14/17', three_a: '0/0', ft_a: '3/4', efg: '82.4%', pm: '+34', starter: true },
      { num: '23', name: 'Cole Wright', time: '3:30', pts: 2, reb: 3, ast: 0, blk: 0, stl: 0, to: 0, two_a: '1/2', three_a: '0/0', ft_a: '0/2', efg: '50.0%', pm: '+8' },
    ],
    opp_players: [
      { num: '0', name: 'Daemeon Holmes', time: '22:54', pts: 13, reb: 4, ast: 1, blk: 0, stl: 1, to: 2, two_a: '1/3', three_a: '3/6', ft_a: '2/4', efg: '61.1%', pm: '-22', starter: true },
      { num: '2', name: 'Nicholas McNeill', time: '3:30', pts: 0, reb: 0, ast: 0, blk: 0, stl: 0, to: 1, two_a: '0/0', three_a: '0/1', ft_a: '0/0', efg: '0%', pm: '-8' },
      { num: '3', name: 'Edan Nathan', time: '10:35', pts: 0, reb: 2, ast: 2, blk: 0, stl: 0, to: 2, two_a: '0/0', three_a: '0/0', ft_a: '0/0', efg: '—', pm: '-6' },
      { num: '4', name: 'Kenny Childs', time: '7:05', pts: 0, reb: 0, ast: 0, blk: 0, stl: 1, to: 0, two_a: '0/1', three_a: '0/1', ft_a: '0/0', efg: '0%', pm: '-4' },
      { num: '5', name: 'Jaimarion Mathis', time: '20:16', pts: 8, reb: 2, ast: 2, blk: 0, stl: 0, to: 0, two_a: '1/3', three_a: '2/6', ft_a: '0/0', efg: '44.4%', pm: '-26', starter: true },
      { num: '10', name: 'Kevin Rogers Jr.', time: '16:56', pts: 4, reb: 2, ast: 2, blk: 1, stl: 1, to: 2, two_a: '1/4', three_a: '0/2', ft_a: '2/2', efg: '16.7%', pm: '-18', starter: true },
      { num: '11', name: 'Tayson Blunt', time: '17:11', pts: 4, reb: 1, ast: 0, blk: 0, stl: 0, to: 2, two_a: '1/1', three_a: '0/2', ft_a: '2/2', efg: '33.3%', pm: '-28', starter: true },
      { num: '12', name: 'Dakota Freeman', time: '26:18', pts: 9, reb: 3, ast: 0, blk: 0, stl: 3, to: 7, two_a: '3/4', three_a: '1/6', ft_a: '0/0', efg: '45.0%', pm: '-27', starter: true },
      { num: '15', name: 'Isaiah Cosby', time: '2:32', pts: 0, reb: 2, ast: 0, blk: 0, stl: 0, to: 0, two_a: '0/0', three_a: '0/0', ft_a: '0/0', efg: '—', pm: '-2' },
      { num: '20', name: 'Josiah Street', time: '2:29', pts: 0, reb: 1, ast: 2, blk: 0, stl: 0, to: 0, two_a: '0/0', three_a: '0/0', ft_a: '0/0', efg: '—', pm: '+5' },
      { num: '22', name: 'Camrin Gee', time: '7:19', pts: 0, reb: 0, ast: 0, blk: 0, stl: 1, to: 1, two_a: '0/1', three_a: '0/1', ft_a: '0/0', efg: '0%', pm: '-13' },
      { num: '23', name: 'Jacob Hall', time: '10:15', pts: 0, reb: 1, ast: 0, blk: 0, stl: 0, to: 0, two_a: '0/0', three_a: '0/2', ft_a: '0/0', efg: '0%', pm: '-15' },
      { num: '25', name: 'Dash Freeman', time: '2:32', pts: 0, reb: 0, ast: 0, blk: 0, stl: 0, to: 0, two_a: '0/0', three_a: '0/0', ft_a: '0/0', efg: '—', pm: '-6' },
    ],
  },
  {
    id: 's1g2',
    date: 'Jun 13, 2026',
    time: '2:00 PM',
    court: 'Court 05',
    my_team: 'Milton - GA',
    opp_team: 'Cherokee - GA',
    my_score: 77,
    opp_score: 52,
    result: 'W',
    film_url: null,
    team_stats: {
      my: { pts: 77, ppp: 1.13, ast: 19, reb: 33, oreb: 12, dreb: 21, blk: 2, stl: 12, to: 13, deflections: 10, fouls: 13, def_fouls: 12, charges: 1, kills: 7, efg_pct: '54.4%', to_pct: '18.6%', oreb_pct: '36.4%', ftr: 0.46, two_made: 25, two_att: 42, two_pct: '59.5%', three_made: 4, three_att: 15, three_pct: '26.7%', ft_made: 15, ft_att: 26, ft_pct: '57.7%', scoring_opps: 73, shots: 57, ft_trips: 16, two_rate: '73.7%', three_rate: '26.3%', ft: '15/26' },
      opp: { pts: 52, ppp: 0.76, ast: 7, reb: 25, oreb: 4, dreb: 21, blk: 0, stl: 6, to: 22, deflections: 7, fouls: 21, def_fouls: 19, charges: 0, kills: 2, efg_pct: '45.7%', to_pct: '32.4%', oreb_pct: '16.0%', ftr: 0.26, two_made: 15, two_att: 27, two_pct: '55.6%', three_made: 4, three_att: 19, three_pct: '21.1%', ft_made: 10, ft_att: 12, ft_pct: '83.3%', scoring_opps: 54, shots: 46, ft_trips: 8, two_rate: '58.7%', three_rate: '41.3%', ft: '10/12' },
    },
    my_players: [
      { num: '0', name: 'Ty Nealis', time: '9:45', pts: 4, reb: 3, ast: 1, blk: 0, stl: 1, to: 0, two_a: '2/3', three_a: '0/0', ft_a: '0/1', efg: '66.7%', pm: '+3' },
      { num: '1', name: 'Mason Pridgett', time: '25:22', pts: 11, reb: 3, ast: 11, blk: 0, stl: 4, to: 2, two_a: '4/6', three_a: '0/0', ft_a: '3/3', efg: '66.7%', pm: '+21', starter: true },
      { num: '3', name: 'Pierce Strom', time: '21:30', pts: 5, reb: 1, ast: 2, blk: 0, stl: 1, to: 1, two_a: '1/2', three_a: '1/4', ft_a: '0/0', efg: '41.7%', pm: '+29', starter: true },
      { num: '4', name: 'Kaden King', time: '2:04', pts: 0, reb: 0, ast: 0, blk: 0, stl: 0, to: 0, two_a: '0/1', three_a: '0/1', ft_a: '0/0', efg: '0%', pm: '-3' },
      { num: '5', name: 'William Golden', time: '26:23', pts: 17, reb: 4, ast: 2, blk: 1, stl: 1, to: 3, two_a: '7/9', three_a: '0/2', ft_a: '3/7', efg: '63.6%', pm: '+30', starter: true },
      { num: '10', name: 'Michael Ogunyemi', time: '3:50', pts: 0, reb: 0, ast: 0, blk: 0, stl: 1, to: 1, two_a: '0/0', three_a: '0/0', ft_a: '0/0', efg: '—', pm: '+4' },
      { num: '11', name: 'Beau Selby', time: '5:33', pts: 2, reb: 2, ast: 0, blk: 0, stl: 0, to: 2, two_a: '0/0', three_a: '0/0', ft_a: '2/2', efg: '—', pm: '+7' },
      { num: '13', name: 'Graham Whitehart', time: '9:52', pts: 0, reb: 0, ast: 0, blk: 0, stl: 0, to: 0, two_a: '0/2', three_a: '0/2', ft_a: '0/0', efg: '0%', pm: '+6' },
      { num: '14', name: 'Jackson Harrison', time: '20:20', pts: 18, reb: 3, ast: 1, blk: 0, stl: 2, to: 1, two_a: '4/6', three_a: '3/6', ft_a: '1/3', efg: '70.8%', pm: '+18', starter: true },
      { num: '15', name: 'Jamison Durr', time: '2:38', pts: 0, reb: 0, ast: 0, blk: 0, stl: 1, to: 0, two_a: '0/0', three_a: '0/0', ft_a: '0/0', efg: '—', pm: '-1' },
      { num: '22', name: 'Solomon Bratton', time: '20:47', pts: 20, reb: 13, ast: 2, blk: 1, stl: 1, to: 2, two_a: '7/13', three_a: '0/0', ft_a: '6/10', efg: '53.8%', pm: '+14', starter: true },
      { num: '23', name: 'Cole Wright', time: '1:52', pts: 0, reb: 0, ast: 0, blk: 0, stl: 0, to: 1, two_a: '0/0', three_a: '0/0', ft_a: '0/0', efg: '—', pm: '-3' },
    ],
    opp_players: [
      { num: '0', name: 'Atlas Walker-Bunda', time: '3:23', pts: 0, reb: 1, ast: 0, blk: 0, stl: 0, to: 2, two_a: '0/0', three_a: '0/0', ft_a: '0/0', efg: '—', pm: '-8' },
      { num: '2', name: 'Korben Polk', time: '22:10', pts: 8, reb: 4, ast: 1, blk: 0, stl: 2, to: 5, two_a: '1/2', three_a: '1/6', ft_a: '3/5', efg: '31.2%', pm: '-16', starter: true },
      { num: '4', name: 'Braylon Luster', time: '24:19', pts: 18, reb: 6, ast: 4, blk: 0, stl: 2, to: 3, two_a: '7/15', three_a: '0/4', ft_a: '4/4', efg: '36.8%', pm: '-17', starter: true },
      { num: '5', name: 'Lucas Fainter', time: '14:01', pts: 4, reb: 1, ast: 0, blk: 0, stl: 1, to: 1, two_a: '2/2', three_a: '0/1', ft_a: '0/0', efg: '66.7%', pm: '-28', starter: true },
      { num: '10', name: 'Nate Wise', time: '3:50', pts: 0, reb: 1, ast: 0, blk: 0, stl: 1, to: 2, two_a: '0/0', three_a: '0/0', ft_a: '0/0', efg: '—', pm: '-4' },
      { num: '15', name: 'Tyler Barnett', time: '25:10', pts: 2, reb: 5, ast: 0, blk: 0, stl: 0, to: 1, two_a: '1/1', three_a: '0/2', ft_a: '0/0', efg: '33.3%', pm: '-21', starter: true },
      { num: '20', name: 'Zane Hereford', time: '25:17', pts: 11, reb: 3, ast: 0, blk: 0, stl: 0, to: 2, two_a: '3/6', three_a: '1/3', ft_a: '2/2', efg: '50.0%', pm: '-21', starter: true },
      { num: '22', name: 'Declan Wright', time: '2:38', pts: 0, reb: 1, ast: 0, blk: 0, stl: 0, to: 0, two_a: '0/0', three_a: '0/0', ft_a: '0/0', efg: '—', pm: '+1' },
      { num: '24', name: 'Buddha Key', time: '22:40', pts: 6, reb: 1, ast: 2, blk: 0, stl: 0, to: 4, two_a: '0/0', three_a: '2/3', ft_a: '0/0', efg: '100.0%', pm: '-8' },
      { num: '30', name: 'Brody Stewart', time: '3:50', pts: 3, reb: 1, ast: 0, blk: 0, stl: 0, to: 0, two_a: '1/1', three_a: '0/0', ft_a: '1/1', efg: '100.0%', pm: '-4' },
      { num: '32', name: 'Brayden West', time: '2:38', pts: 0, reb: 0, ast: 0, blk: 0, stl: 0, to: 2, two_a: '0/0', three_a: '0/0', ft_a: '0/0', efg: '—', pm: '+1' },
    ],
  },
  {
    id: 's1g3',
    date: 'Jun 13, 2026',
    time: '7:00 PM',
    court: 'Court 08',
    game_id: '36324',
    my_team: 'Milton - GA',
    opp_team: 'Alexander - GA',
    my_score: 57,
    opp_score: 49,
    result: 'W',
    film_url: null,
    team_stats: {
      my: { pts: 57, ppp: 1.12, ast: 11, reb: 40, oreb: 20, dreb: 20, blk: 1, stl: 4, to: 11, deflections: 2, fouls: 10, def_fouls: 8, charges: 0, kills: 6, efg_pct: '47.1%', to_pct: '17.5%', oreb_pct: '55.6%', ftr: 0.33, two_made: 14, two_att: 33, two_pct: '42.4%', three_made: 7, three_att: 19, three_pct: '36.8%', ft_made: 8, ft_att: 17, ft_pct: '47.1%', scoring_opps: 62, shots: 52, ft_trips: 10, two_rate: '63.5%', three_rate: '36.5%', ft: '8/17' },
      opp: { pts: 49, ppp: 0.96, ast: 7, reb: 26, oreb: 10, dreb: 16, blk: 4, stl: 7, to: 10, deflections: 5, fouls: 20, def_fouls: 17, charges: 0, kills: 5, efg_pct: '45.9%', to_pct: '16.9%', oreb_pct: '33.3%', ftr: 0.16, two_made: 15, two_att: 35, two_pct: '42.9%', three_made: 5, three_att: 14, three_pct: '35.7%', ft_made: 4, ft_att: 8, ft_pct: '50.0%', scoring_opps: 53, shots: 49, ft_trips: 4, two_rate: '71.4%', three_rate: '28.6%', ft: '4/8' },
    },
    my_players: [
      { num: '1', name: 'Mason Pridgett', time: '30:00', pts: 2, reb: 3, ast: 3, blk: 0, stl: 1, to: 5, two_a: '1/3', three_a: '0/1', ft_a: '0/0', efg: '25.0%', pm: '+8', starter: true },
      { num: '3', name: 'Pierce Strom', time: '23:27', pts: 3, reb: 1, ast: 1, blk: 0, stl: 0, to: 0, two_a: '0/0', three_a: '1/5', ft_a: '0/0', efg: '30.0%', pm: '+7', starter: true },
      { num: '5', name: 'William Golden', time: '30:00', pts: 13, reb: 8, ast: 1, blk: 1, stl: 1, to: 1, two_a: '5/11', three_a: '1/2', ft_a: '0/7', efg: '50.0%', pm: '+8', starter: true },
      { num: '12', name: 'CJ Omoyele', time: '2:37', pts: 2, reb: 1, ast: 0, blk: 0, stl: 0, to: 1, two_a: '1/2', three_a: '0/0', ft_a: '0/0', efg: '50.0%', pm: '-2' },
      { num: '13', name: 'Graham Whitehart', time: '6:23', pts: 0, reb: 0, ast: 0, blk: 0, stl: 0, to: 0, two_a: '0/0', three_a: '0/1', ft_a: '0/0', efg: '0%', pm: '+1' },
      { num: '14', name: 'Jackson Harrison', time: '27:31', pts: 22, reb: 5, ast: 1, blk: 0, stl: 1, to: 2, two_a: '2/5', three_a: '5/10', ft_a: '3/5', efg: '63.3%', pm: '+10', starter: true },
      { num: '22', name: 'Solomon Bratton', time: '30:00', pts: 15, reb: 17, ast: 5, blk: 0, stl: 1, to: 2, two_a: '5/12', three_a: '0/0', ft_a: '5/5', efg: '41.7%', pm: '+8', starter: true },
    ],
    opp_players: [
      { num: '1', name: 'Chauncey Young', time: '21:17', pts: 0, reb: 3, ast: 0, blk: 1, stl: 1, to: 3, two_a: '0/3', three_a: '0/1', ft_a: '0/0', efg: '0%', pm: '-12', starter: true },
      { num: '2', name: 'TaJai Cook', time: '17:12', pts: 2, reb: 0, ast: 1, blk: 1, stl: 1, to: 0, two_a: '1/1', three_a: '0/0', ft_a: '0/0', efg: '100.0%', pm: '-13' },
      { num: '3', name: 'Donovin Martin', time: '27:37', pts: 20, reb: 8, ast: 3, blk: 0, stl: 1, to: 1, two_a: '8/15', three_a: '0/4', ft_a: '4/4', efg: '42.1%', pm: '-7', starter: true },
      { num: '5', name: 'TJ Pigott', time: '16:16', pts: 0, reb: 4, ast: 1, blk: 0, stl: 1, to: 2, two_a: '0/0', three_a: '0/0', ft_a: '0/0', efg: '—', pm: '+5', starter: true },
      { num: '12', name: 'Aiden Allaire', time: '25:27', pts: 12, reb: 4, ast: 1, blk: 2, stl: 1, to: 0, two_a: '3/6', three_a: '2/4', ft_a: '0/4', efg: '60.0%', pm: '+10', starter: true },
      { num: '22', name: 'Jake Roberts', time: '8:56', pts: 6, reb: 0, ast: 0, blk: 0, stl: 1, to: 2, two_a: '0/0', three_a: '2/3', ft_a: '0/0', efg: '100.0%', pm: '-14' },
      { num: '23', name: 'Derek Williams', time: '5:43', pts: 0, reb: 0, ast: 0, blk: 0, stl: 0, to: 0, two_a: '0/0', three_a: '0/0', ft_a: '0/0', efg: '—', pm: '-13' },
      { num: '24', name: 'KJ Reed', time: '18:13', pts: 9, reb: 3, ast: 0, blk: 0, stl: 1, to: 1, two_a: '3/9', three_a: '1/2', ft_a: '0/0', efg: '40.9%', pm: '+6', starter: true },
      { num: '25', name: 'Jerry English', time: '4:33', pts: 0, reb: 0, ast: 1, blk: 0, stl: 0, to: 0, two_a: '0/1', three_a: '0/0', ft_a: '0/0', efg: '0%', pm: '-4' },
      { num: '33', name: 'AmerJay Hunter', time: '4:40', pts: 0, reb: 0, ast: 0, blk: 0, stl: 0, to: 1, two_a: '0/0', three_a: '0/0', ft_a: '0/0', efg: '—', pm: '+2' },
    ],
  },
  {
    id: 's1g4',
    date: 'Jun 14, 2026',
    time: '11:00 AM',
    court: 'Court 08',
    my_team: 'Milton - GA',
    opp_team: 'Pace Academy - GA',
    my_score: 40,
    opp_score: 52,
    result: 'L',
    film_url: 'https://vimeo.com/1202832997',
    team_stats: {
      my: { pts: 40, ppp: 0.89, ast: 9, reb: 17, oreb: 4, dreb: 13, blk: 0, stl: 6, to: 4, deflections: 10, fouls: 12, def_fouls: 10, charges: 0, kills: 4, efg_pct: '41.5%', to_pct: '8.9%', oreb_pct: '16.7%', ftr: 0.20, two_made: 11, two_att: 21, two_pct: '52.4%', three_made: 4, three_att: 20, three_pct: '20.0%', ft_made: 6, ft_att: 8, ft_pct: '75.0%', scoring_opps: 45, shots: 41, ft_trips: 4, two_rate: '51.2%', three_rate: '48.8%', ft: '6/8' },
      opp: { pts: 52, ppp: 1.16, ast: 8, reb: 27, oreb: 7, dreb: 20, blk: 1, stl: 2, to: 9, deflections: 10, fouls: 10, def_fouls: 10, charges: 0, kills: 6, efg_pct: '54.2%', to_pct: '20.0%', oreb_pct: '35.0%', ftr: 0.39, two_made: 12, two_att: 20, two_pct: '60.0%', three_made: 5, three_att: 16, three_pct: '31.2%', ft_made: 13, ft_att: 14, ft_pct: '92.9%', scoring_opps: 44, shots: 36, ft_trips: 8, two_rate: '55.6%', three_rate: '44.4%', ft: '13/14' },
    },
    my_players: [
      { num: '0', name: 'Ty Nealis', time: '2:42', pts: 0, reb: 1, ast: 0, blk: 0, stl: 0, to: 0, two_a: '0/1', three_a: '0/0', ft_a: '0/0', efg: '0%', pm: '-3' },
      { num: '1', name: 'Mason Pridgett', time: '30:00', pts: 2, reb: 2, ast: 3, blk: 0, stl: 1, to: 1, two_a: '0/3', three_a: '0/1', ft_a: '2/2', efg: '0%', pm: '-12', starter: true },
      { num: '3', name: 'Pierce Strom', time: '20:59', pts: 6, reb: 2, ast: 0, blk: 0, stl: 0, to: 0, two_a: '0/0', three_a: '2/6', ft_a: '0/0', efg: '50.0%', pm: '-10', starter: true },
      { num: '5', name: 'William Golden', time: '30:00', pts: 16, reb: 0, ast: 0, blk: 0, stl: 1, to: 0, two_a: '7/7', three_a: '0/3', ft_a: '2/2', efg: '70.0%', pm: '-12', starter: true },
      { num: '11', name: 'Beau Selby', time: '2:36', pts: 0, reb: 0, ast: 1, blk: 0, stl: 0, to: 0, two_a: '0/0', three_a: '0/0', ft_a: '0/0', efg: '—', pm: '+1' },
      { num: '12', name: 'CJ Omoyele', time: '0:29', pts: 0, reb: 0, ast: 0, blk: 0, stl: 0, to: 0, two_a: '0/0', three_a: '0/0', ft_a: '0/0', efg: '—', pm: '0' },
      { num: '13', name: 'Graham Whitehart', time: '3:11', pts: 0, reb: 1, ast: 0, blk: 0, stl: 0, to: 0, two_a: '0/1', three_a: '0/1', ft_a: '0/0', efg: '0%', pm: '0' },
      { num: '14', name: 'Jackson Harrison', time: '30:00', pts: 8, reb: 3, ast: 3, blk: 0, stl: 2, to: 0, two_a: '1/2', three_a: '2/9', ft_a: '0/0', efg: '36.4%', pm: '-12', starter: true },
      { num: '22', name: 'Solomon Bratton', time: '30:00', pts: 8, reb: 7, ast: 2, blk: 0, stl: 2, to: 3, two_a: '3/7', three_a: '0/0', ft_a: '2/4', efg: '42.9%', pm: '-12', starter: true },
    ],
    opp_players: [
      { num: '0', name: 'Jeffrey Stephens', time: '19:01', pts: 0, reb: 2, ast: 0, blk: 0, stl: 0, to: 0, two_a: '0/0', three_a: '0/0', ft_a: '0/0', efg: '—', pm: '+7', starter: true },
      { num: '1', name: 'Jace Dunn', time: '4:37', pts: 0, reb: 1, ast: 0, blk: 0, stl: 0, to: 1, two_a: '0/0', three_a: '0/2', ft_a: '0/0', efg: '0%', pm: '+1' },
      { num: '2', name: 'Brandon Dixon', time: '1:19', pts: 0, reb: 0, ast: 0, blk: 0, stl: 0, to: 0, two_a: '0/0', three_a: '0/0', ft_a: '0/0', efg: '—', pm: '0' },
      { num: '3', name: 'Amir Cuyler', time: '7:19', pts: 0, reb: 0, ast: 0, blk: 0, stl: 0, to: 0, two_a: '0/0', three_a: '0/0', ft_a: '0/0', efg: '—', pm: '+3' },
      { num: '4', name: 'Jaden McCullough', time: '30:00', pts: 18, reb: 4, ast: 1, blk: 0, stl: 0, to: 3, two_a: '3/6', three_a: '2/5', ft_a: '6/6', efg: '54.5%', pm: '+12', starter: true },
      { num: '5', name: 'Brielen Craft', time: '26:42', pts: 11, reb: 2, ast: 1, blk: 0, stl: 2, to: 0, two_a: '1/3', three_a: '2/5', ft_a: '3/3', efg: '50.0%', pm: '+13', starter: true },
      { num: '12', name: 'Henry Dickert', time: '25:44', pts: 2, reb: 2, ast: 2, blk: 0, stl: 0, to: 0, two_a: '1/1', three_a: '0/1', ft_a: '0/0', efg: '50.0%', pm: '+5', starter: true },
      { num: '21', name: 'Evan Reed', time: '1:32', pts: 0, reb: 0, ast: 0, blk: 0, stl: 0, to: 0, two_a: '0/0', three_a: '0/0', ft_a: '0/0', efg: '—', pm: '-1' },
      { num: '24', name: 'Kaiden Harley', time: '7:21', pts: 9, reb: 4, ast: 0, blk: 1, stl: 0, to: 0, two_a: '2/3', three_a: '1/1', ft_a: '2/3', efg: '87.5%', pm: '+10' },
      { num: '25', name: 'Gavin Fountain', time: '26:22', pts: 12, reb: 7, ast: 4, blk: 0, stl: 0, to: 4, two_a: '5/7', three_a: '0/2', ft_a: '2/2', efg: '55.6%', pm: '+10', starter: true },
    ],
  },
];

// ── Session 2 SE Regional: June 26-28, 2026 ──────────────────────────────────
const SESSION_2_GAMES = [
  {
    id: 's2g1',
    date: 'Jun 26, 2026',
    time: '6:00 PM',
    court: 'Court 05',
    game_id: '37241',
    my_team: 'Milton HS - GA',
    opp_team: 'Mooresville - NC',
    my_score: 57,
    opp_score: 46,
    result: 'W',
    film_url: 'https://vimeo.com/1205581912',
    team_stats: {
      my: { pts: 57, ppp: 1.10, ast: 14, reb: 29, oreb: 12, dreb: 17, blk: 3, stl: 6, to: 1, deflections: 8, fouls: 11, def_fouls: 11, charges: 0, kills: 4, efg_pct: '45.5%', to_pct: '1.8%', oreb_pct: '33.3%', ftr: 0.27, two_made: 16, two_att: 33, two_pct: '48.5%', three_made: 6, three_att: 22, three_pct: '27.3%', ft_made: 7, ft_att: 15, ft_pct: '46.7%', scoring_opps: 63, shots: 55, ft_trips: 8, two_rate: '60.0%', three_rate: '40.0%', ft: '7/15' },
      opp: { pts: 46, ppp: 0.90, ast: 10, reb: 35, oreb: 11, dreb: 24, blk: 1, stl: 0, to: 12, deflections: 2, fouls: 13, def_fouls: 11, charges: 0, kills: 2, efg_pct: '42.2%', to_pct: '21.1%', oreb_pct: '39.3%', ftr: 0.29, two_made: 16, two_att: 32, two_pct: '50.0%', three_made: 2, three_att: 13, three_pct: '15.4%', ft_made: 8, ft_att: 13, ft_pct: '61.5%', scoring_opps: 53, shots: 45, ft_trips: 8, two_rate: '71.1%', three_rate: '28.9%', ft: '8/13' },
    },
    my_players: [
      { num: '0', name: 'Ty Nealis', time: '3:41', pts: 0, reb: 1, ast: 0, blk: 0, stl: 0, to: 0, two_a: '0/0', three_a: '0/0', ft_a: '0/0', efg: '—', pm: '+5' },
      { num: '1', name: 'Mason Pridgett', time: '27:50', pts: 4, reb: 6, ast: 2, blk: 0, stl: 2, to: 0, two_a: '1/6', three_a: '0/0', ft_a: '0/0', efg: '16.7%', pm: '+6', starter: true },
      { num: '3', name: 'Pierce Strom', time: '20:58', pts: 11, reb: 2, ast: 0, blk: 0, stl: 1, to: 0, two_a: '1/2', three_a: '3/5', ft_a: '0/0', efg: '78.6%', pm: '+12', starter: true },
      { num: '5', name: 'William Golden', time: '28:05', pts: 14, reb: 5, ast: 5, blk: 0, stl: 0, to: 0, two_a: '7/10', three_a: '0/1', ft_a: '0/0', efg: '63.6%', pm: '+11', starter: true },
      { num: '11', name: 'Beau Selby', time: '1:56', pts: 2, reb: 1, ast: 0, blk: 0, stl: 0, to: 0, two_a: '1/2', three_a: '0/0', ft_a: '0/0', efg: '50.0%', pm: '-4' },
      { num: '12', name: 'CJ Omoyele', time: '4:59', pts: 0, reb: 1, ast: 0, blk: 0, stl: 0, to: 0, two_a: '0/0', three_a: '0/0', ft_a: '0/0', efg: '—', pm: '-2' },
      { num: '13', name: 'Graham Whitehart', time: '8:53', pts: 3, reb: 0, ast: 0, blk: 0, stl: 0, to: 0, two_a: '0/0', three_a: '1/3', ft_a: '0/0', efg: '50.0%', pm: '+1' },
      { num: '14', name: 'Jackson Harrison', time: '25:44', pts: 11, reb: 1, ast: 1, blk: 0, stl: 1, to: 0, two_a: '3/7', three_a: '0/9', ft_a: '0/0', efg: '18.8%', pm: '+15', starter: true },
      { num: '22', name: 'Solomon Bratton', time: '27:48', pts: 12, reb: 11, ast: 6, blk: 3, stl: 2, to: 0, two_a: '3/6', three_a: '2/4', ft_a: '0/0', efg: '60.0%', pm: '+11', starter: true },
    ],
    opp_players: [
      { num: '2', name: 'Jaylen Bailey', time: '10:24', pts: 4, reb: 1, ast: 2, blk: 0, stl: 0, to: 0, two_a: '1/1', three_a: '0/0', ft_a: '0/0', efg: '100.0%', pm: '+2' },
      { num: '3', name: 'Sherod McCormick', time: '28:37', pts: 12, reb: 3, ast: 4, blk: 0, stl: 0, to: 2, two_a: '2/9', three_a: '1/6', ft_a: '0/0', efg: '23.3%', pm: '-9', starter: true },
      { num: '4', name: 'Malachi Shipp', time: '18:13', pts: 7, reb: 4, ast: 3, blk: 0, stl: 0, to: 4, two_a: '3/5', three_a: '0/0', ft_a: '0/0', efg: '60.0%', pm: '-8', starter: true },
      { num: '5', name: 'Kevin Cornelius', time: '5:35', pts: 0, reb: 0, ast: 0, blk: 0, stl: 0, to: 0, two_a: '0/1', three_a: '0/0', ft_a: '0/0', efg: '0%', pm: '-5' },
      { num: '11', name: 'Ethan Petty', time: '10:42', pts: 4, reb: 2, ast: 0, blk: 0, stl: 0, to: 0, two_a: '2/3', three_a: '0/0', ft_a: '0/0', efg: '66.7%', pm: '+1' },
      { num: '12', name: 'Noah Wilson', time: '16:26', pts: 7, reb: 9, ast: 0, blk: 0, stl: 0, to: 4, two_a: '2/3', three_a: '1/4', ft_a: '0/0', efg: '50.0%', pm: '-14', starter: true },
      { num: '20', name: 'Cody Morrison', time: '12:48', pts: 0, reb: 4, ast: 0, blk: 0, stl: 0, to: 1, two_a: '0/1', three_a: '0/3', ft_a: '0/0', efg: '0%', pm: '0' },
      { num: '24', name: 'Braylen Curry', time: '20:52', pts: 6, reb: 5, ast: 0, blk: 0, stl: 0, to: 0, two_a: '3/5', three_a: '0/0', ft_a: '0/0', efg: '60.0%', pm: '-16', starter: true },
      { num: '55', name: 'Xavier Hall', time: '26:18', pts: 6, reb: 3, ast: 1, blk: 1, stl: 0, to: 1, two_a: '3/4', three_a: '0/0', ft_a: '0/0', efg: '75.0%', pm: '-6', starter: true },
    ],
  },
  {
    id: 's2g2',
    date: 'Jun 27, 2026',
    time: '2:00 PM',
    court: 'Court 08',
    game_id: '37325',
    my_team: 'Milton HS - GA',
    opp_team: 'West Charlotte - NC',
    my_score: 50,
    opp_score: 54,
    result: 'L',
    film_url: 'https://vimeo.com/1205604087',
    team_stats: {
      my: { pts: 50, ppp: 0.93, ast: 16, reb: 25, oreb: 6, dreb: 19, blk: 0, stl: 2, to: 7, deflections: 2, fouls: 21, def_fouls: 20, charges: 0, kills: 4, efg_pct: '46.8%', to_pct: '13.0%', oreb_pct: '20.7%', ftr: 0.28, two_made: 7, two_att: 19, two_pct: '36.8%', three_made: 10, three_att: 28, three_pct: '35.7%', ft_made: 6, ft_att: 13, ft_pct: '46.2%', scoring_opps: 54, shots: 47, ft_trips: 7, two_rate: '40.4%', three_rate: '59.6%', ft: '6/13' },
      opp: { pts: 54, ppp: 1.00, ast: 15, reb: 32, oreb: 9, dreb: 23, blk: 3, stl: 3, to: 6, deflections: 7, fouls: 20, def_fouls: 16, charges: 0, kills: 6, efg_pct: '44.3%', to_pct: '12.0%', oreb_pct: '32.1%', ftr: 0.57, two_made: 18, two_att: 37, two_pct: '48.6%', three_made: 1, three_att: 7, three_pct: '14.3%', ft_made: 15, ft_att: 25, ft_pct: '60.0%', scoring_opps: 58, shots: 44, ft_trips: 14, two_rate: '84.1%', three_rate: '15.9%', ft: '15/25' },
    },
    my_players: [
      { num: '1', name: 'Mason Pridgett', time: '30:00', pts: 0, reb: 6, ast: 5, blk: 0, stl: 0, to: 2, two_a: '0/1', three_a: '0/2', ft_a: '0/0', efg: '0%', pm: '-9', starter: true },
      { num: '3', name: 'Pierce Strom', time: '30:00', pts: 17, reb: 0, ast: 0, blk: 0, stl: 1, to: 0, two_a: '0/0', three_a: '5/8', ft_a: '0/0', efg: '93.8%', pm: '+5', starter: true },
      { num: '5', name: 'William Golden', time: '30:00', pts: 10, reb: 9, ast: 4, blk: 0, stl: 1, to: 1, two_a: '4/9', three_a: '0/1', ft_a: '0/0', efg: '40.0%', pm: '-2', starter: true },
      { num: '11', name: 'Beau Selby', time: '6:27', pts: 0, reb: 0, ast: 0, blk: 0, stl: 0, to: 0, two_a: '0/0', three_a: '0/0', ft_a: '0/0', efg: '—', pm: '-2' },
      { num: '12', name: 'CJ Omoyele', time: '4:25', pts: 0, reb: 1, ast: 0, blk: 0, stl: 0, to: 0, two_a: '0/1', three_a: '0/1', ft_a: '0/0', efg: '0%', pm: '-8' },
      { num: '13', name: 'Graham Whitehart', time: '10:48', pts: 0, reb: 1, ast: 0, blk: 0, stl: 0, to: 0, two_a: '0/0', three_a: '0/1', ft_a: '0/0', efg: '0%', pm: '-4' },
      { num: '14', name: 'Jackson Harrison', time: '30:00', pts: 15, reb: 1, ast: 2, blk: 0, stl: 0, to: 0, two_a: '0/1', three_a: '5/15', ft_a: '0/0', efg: '46.9%', pm: '-5', starter: true },
      { num: '22', name: 'Solomon Bratton', time: '30:00', pts: 8, reb: 5, ast: 5, blk: 0, stl: 0, to: 4, two_a: '3/7', three_a: '0/0', ft_a: '0/0', efg: '42.9%', pm: '0', starter: true },
    ],
    opp_players: [
      { num: '2', name: 'Chacho Womack', time: '30:00', pts: 12, reb: 5, ast: 2, blk: 0, stl: 0, to: 0, two_a: '2/8', three_a: '1/3', ft_a: '0/0', efg: '31.8%', pm: '+8', starter: true },
      { num: '3', name: 'Amen Pressley', time: '30:00', pts: 4, reb: 5, ast: 7, blk: 0, stl: 1, to: 0, two_a: '2/4', three_a: '0/0', ft_a: '0/0', efg: '50.0%', pm: '+1', starter: true },
      { num: '5', name: 'Mychal Brown', time: '16:26', pts: 5, reb: 1, ast: 3, blk: 0, stl: 0, to: 1, two_a: '2/2', three_a: '0/0', ft_a: '0/0', efg: '100.0%', pm: '+6' },
      { num: '11', name: 'Major Cross', time: '24:35', pts: 0, reb: 1, ast: 0, blk: 0, stl: 0, to: 1, two_a: '0/1', three_a: '0/0', ft_a: '0/0', efg: '0%', pm: '0', starter: true },
      { num: '12', name: 'Carter Sullivan', time: '13:20', pts: 0, reb: 1, ast: 0, blk: 0, stl: 0, to: 0, two_a: '0/4', three_a: '0/0', ft_a: '0/0', efg: '0%', pm: '+3' },
      { num: '14', name: 'Sean Johnson', time: '30:00', pts: 10, reb: 7, ast: 1, blk: 1, stl: 1, to: 1, two_a: '4/6', three_a: '0/0', ft_a: '0/0', efg: '66.7%', pm: '+4', starter: true },
      { num: '22', name: 'John Dudley', time: '4:19', pts: 2, reb: 0, ast: 0, blk: 1, stl: 0, to: 0, two_a: '1/1', three_a: '0/1', ft_a: '0/0', efg: '50.0%', pm: '+1' },
      { num: '24', name: '#24', time: '30:00', pts: 21, reb: 8, ast: 2, blk: 1, stl: 1, to: 3, two_a: '7/11', three_a: '0/3', ft_a: '0/0', efg: '50.0%', pm: '+2', starter: true },
    ],
  },
  {
    id: 's2g3',
    date: 'Jun 27, 2026',
    time: '6:00 PM',
    court: 'Court 04',
    game_id: '37358',
    my_team: 'Milton HS - GA',
    opp_team: 'Oxford HS - AL',
    my_score: 58,
    opp_score: 48,
    result: 'W',
    film_url: 'https://vimeo.com/1205631238',
    team_stats: {
      my: { pts: 58, ppp: 1.04, ast: 10, reb: 26, oreb: 9, dreb: 17, blk: 3, stl: 11, to: 15, deflections: 8, fouls: 18, def_fouls: 13, charges: 0, kills: 5, efg_pct: '54.5%', to_pct: '25.4%', oreb_pct: '40.9%', ftr: 0.32, two_made: 15, two_att: 24, two_pct: '62.5%', three_made: 6, three_att: 20, three_pct: '30.0%', ft_made: 10, ft_att: 14, ft_pct: '71.4%', scoring_opps: 52, shots: 44, ft_trips: 8, two_rate: '54.5%', three_rate: '45.5%', ft: '10/14' },
      opp: { pts: 48, ppp: 0.91, ast: 7, reb: 19, oreb: 6, dreb: 13, blk: 3, stl: 2, to: 16, deflections: 8, fouls: 18, def_fouls: 14, charges: 0, kills: 5, efg_pct: '51.4%', to_pct: '30.2%', oreb_pct: '26.1%', ftr: 0.41, two_made: 13, two_att: 28, two_pct: '46.4%', three_made: 4, three_att: 9, three_pct: '44.4%', ft_made: 10, ft_att: 15, ft_pct: '66.7%', scoring_opps: 46, shots: 37, ft_trips: 9, two_rate: '75.7%', three_rate: '24.3%', ft: '10/15' },
    },
    my_players: [
      { num: '0', name: 'Ty Nealis', time: '4:18', pts: 0, reb: 0, ast: 0, blk: 0, stl: 0, to: 0, two_a: '0/0', three_a: '0/0', ft_a: '0/0', efg: '—', pm: '-6' },
      { num: '1', name: 'Mason Pridgett', time: '25:45', pts: 7, reb: 3, ast: 3, blk: 1, stl: 0, to: 2, two_a: '1/2', three_a: '1/3', ft_a: '0/0', efg: '50.0%', pm: '+18', starter: true },
      { num: '3', name: 'Pierce Strom', time: '23:22', pts: 13, reb: 0, ast: 0, blk: 0, stl: 0, to: 1, two_a: '0/1', three_a: '4/8', ft_a: '0/0', efg: '66.7%', pm: '+5', starter: true },
      { num: '4', name: 'Kaden King', time: '1:21', pts: 0, reb: 0, ast: 0, blk: 0, stl: 0, to: 0, two_a: '0/0', three_a: '0/0', ft_a: '0/0', efg: '—', pm: '-4' },
      { num: '5', name: 'William Golden', time: '28:43', pts: 10, reb: 6, ast: 3, blk: 1, stl: 2, to: 3, two_a: '3/4', three_a: '0/0', ft_a: '0/0', efg: '75.0%', pm: '+14', starter: true },
      { num: '11', name: 'Beau Selby', time: '2:33', pts: 0, reb: 1, ast: 0, blk: 0, stl: 0, to: 0, two_a: '0/0', three_a: '0/0', ft_a: '0/0', efg: '—', pm: '-6' },
      { num: '12', name: 'CJ Omoyele', time: '6:12', pts: 0, reb: 1, ast: 0, blk: 0, stl: 1, to: 0, two_a: '0/0', three_a: '0/0', ft_a: '0/0', efg: '—', pm: '+18' },
      { num: '13', name: 'Graham Whitehart', time: '4:28', pts: 0, reb: 0, ast: 0, blk: 0, stl: 0, to: 0, two_a: '0/0', three_a: '0/1', ft_a: '0/0', efg: '0%', pm: '-6' },
      { num: '14', name: 'Jackson Harrison', time: '24:24', pts: 5, reb: 3, ast: 1, blk: 0, stl: 4, to: 3, two_a: '1/3', three_a: '1/6', ft_a: '0/0', efg: '27.8%', pm: '+13', starter: true },
      { num: '15', name: 'Jamison Durr', time: '1:21', pts: 0, reb: 0, ast: 0, blk: 0, stl: 1, to: 2, two_a: '0/0', three_a: '0/0', ft_a: '0/0', efg: '—', pm: '-4' },
      { num: '20', name: 'Hezron Luyindula', time: '0:53', pts: 0, reb: 0, ast: 0, blk: 0, stl: 0, to: 1, two_a: '0/0', three_a: '0/0', ft_a: '0/0', efg: '—', pm: '-2' },
      { num: '22', name: 'Solomon Bratton', time: '25:35', pts: 23, reb: 9, ast: 3, blk: 1, stl: 2, to: 2, two_a: '10/14', three_a: '0/2', ft_a: '0/0', efg: '62.5%', pm: '+14', starter: true },
      { num: '23', name: 'Cole Wright', time: '1:21', pts: 0, reb: 0, ast: 0, blk: 0, stl: 0, to: 0, two_a: '0/0', three_a: '0/0', ft_a: '0/0', efg: '—', pm: '-4' },
    ],
    opp_players: [
      { num: '1', name: 'Jmi Caver', time: '2:01', pts: 2, reb: 0, ast: 0, blk: 0, stl: 0, to: 0, two_a: '1/1', three_a: '0/0', ft_a: '0/0', efg: '100.0%', pm: '+2' },
      { num: '2', name: 'Caileb Wilson', time: '25:15', pts: 6, reb: 3, ast: 1, blk: 0, stl: 0, to: 5, two_a: '2/4', three_a: '0/1', ft_a: '0/0', efg: '40.0%', pm: '-10', starter: true },
      { num: '3', name: 'Conner Richerzhagen', time: '13:30', pts: 3, reb: 3, ast: 0, blk: 0, stl: 0, to: 0, two_a: '0/2', three_a: '1/2', ft_a: '0/0', efg: '37.5%', pm: '-9' },
      { num: '4', name: 'Marcus Perry', time: '24:56', pts: 16, reb: 3, ast: 1, blk: 2, stl: 0, to: 3, two_a: '4/9', three_a: '2/3', ft_a: '0/0', efg: '58.3%', pm: '-5', starter: true },
      { num: '5', name: 'Pierce Van Meter', time: '11:56', pts: 0, reb: 0, ast: 3, blk: 0, stl: 0, to: 2, two_a: '0/0', three_a: '0/0', ft_a: '0/0', efg: '—', pm: '-1' },
      { num: '10', name: 'Chris Latson', time: '17:07', pts: 6, reb: 2, ast: 0, blk: 0, stl: 1, to: 2, two_a: '2/5', three_a: '0/1', ft_a: '0/0', efg: '33.3%', pm: '-11', starter: true },
      { num: '11', name: 'Jermaine Caver', time: '21:51', pts: 4, reb: 3, ast: 2, blk: 1, stl: 0, to: 2, two_a: '1/3', three_a: '0/1', ft_a: '0/0', efg: '25.0%', pm: '-11', starter: true },
      { num: '12', name: 'Guy Tyler', time: '2:01', pts: 0, reb: 0, ast: 0, blk: 0, stl: 0, to: 1, two_a: '0/0', three_a: '0/0', ft_a: '0/0', efg: '—', pm: '+2' },
      { num: '21', name: 'Cooper Romano', time: '15:34', pts: 5, reb: 2, ast: 0, blk: 0, stl: 0, to: 0, two_a: '0/0', three_a: '1/1', ft_a: '0/0', efg: '150.0%', pm: '-14' },
      { num: '22', name: 'Noah Pesnell', time: '10:02', pts: 4, reb: 1, ast: 0, blk: 0, stl: 0, to: 0, two_a: '2/2', three_a: '0/0', ft_a: '0/0', efg: '100.0%', pm: '+1', starter: true },
      { num: '23', name: 'Elijah Wilson', time: '2:01', pts: 2, reb: 0, ast: 0, blk: 0, stl: 1, to: 0, two_a: '1/1', three_a: '0/0', ft_a: '0/0', efg: '100.0%', pm: '+2' },
    ],
  },
  {
    id: 's2g4',
    date: 'Jun 28, 2026',
    time: '11:00 AM',
    court: 'Court 04',
    game_id: '37411',
    my_team: 'Milton HS - GA',
    opp_team: 'Calvary Baptist - LA',
    my_score: 56,
    opp_score: 54,
    result: 'W',
    film_url: 'https://vimeo.com/1205631813',
    team_stats: {
      my: { pts: 56, ppp: 1.17, ast: 9, reb: 27, oreb: 9, dreb: 18, blk: 4, stl: 5, to: 6, deflections: 6, fouls: 8, def_fouls: 8, charges: 0, kills: 3, efg_pct: '52.3%', to_pct: '12.0%', oreb_pct: '33.3%', ftr: 0.30, two_made: 11, two_att: 24, two_pct: '45.8%', three_made: 8, three_att: 20, three_pct: '40.0%', ft_made: 10, ft_att: 13, ft_pct: '76.9%', scoring_opps: 51, shots: 44, ft_trips: 7, two_rate: '54.5%', three_rate: '45.5%', ft: '10/13' },
      opp: { pts: 54, ppp: 1.08, ast: 12, reb: 19, oreb: 1, dreb: 18, blk: 1, stl: 5, to: 7, deflections: 4, fouls: 10, def_fouls: 10, charges: 0, kills: 2, efg_pct: '61.5%', to_pct: '15.2%', oreb_pct: '5.3%', ftr: 0.28, two_made: 18, two_att: 26, two_pct: '69.2%', three_made: 4, three_att: 13, three_pct: '30.8%', ft_made: 6, ft_att: 11, ft_pct: '54.5%', scoring_opps: 46, shots: 39, ft_trips: 7, two_rate: '66.7%', three_rate: '33.3%', ft: '6/11' },
    },
    my_players: [
      { num: '0', name: 'Ty Nealis', time: '14:16', pts: 0, reb: 2, ast: 0, blk: 0, stl: 1, to: 2, two_a: '0/1', three_a: '0/0', ft_a: '0/0', efg: '0%', pm: '+5' },
      { num: '1', name: 'Mason Pridgett', time: '17:23', pts: 0, reb: 1, ast: 0, blk: 0, stl: 0, to: 0, two_a: '0/5', three_a: '0/0', ft_a: '0/0', efg: '0%', pm: '+4', starter: true },
      { num: '3', name: 'Pierce Strom', time: '20:17', pts: 20, reb: 3, ast: 0, blk: 0, stl: 0, to: 0, two_a: '1/1', three_a: '6/11', ft_a: '0/0', efg: '83.3%', pm: '+4', starter: true },
      { num: '5', name: 'William Golden', time: '30:00', pts: 8, reb: 1, ast: 9, blk: 2, stl: 2, to: 0, two_a: '2/4', three_a: '0/1', ft_a: '0/0', efg: '40.0%', pm: '+2', starter: true },
      { num: '12', name: 'CJ Omoyele', time: '3:37', pts: 0, reb: 0, ast: 0, blk: 0, stl: 0, to: 0, two_a: '0/0', three_a: '0/0', ft_a: '0/0', efg: '—', pm: '-8' },
      { num: '13', name: 'Graham Whitehart', time: '11:47', pts: 0, reb: 2, ast: 0, blk: 0, stl: 0, to: 0, two_a: '0/1', three_a: '0/1', ft_a: '0/0', efg: '0%', pm: '-6' },
      { num: '14', name: 'Jackson Harrison', time: '22:36', pts: 10, reb: 2, ast: 1, blk: 0, stl: 2, to: 0, two_a: '2/2', three_a: '2/7', ft_a: '0/0', efg: '55.6%', pm: '+7', starter: true },
      { num: '22', name: 'Solomon Bratton', time: '30:00', pts: 18, reb: 10, ast: 5, blk: 2, stl: 0, to: 4, two_a: '6/10', three_a: '0/0', ft_a: '0/0', efg: '60.0%', pm: '+2', starter: true },
    ],
    opp_players: [
      { num: '0', name: 'Jaiden Hall', time: '23:59', pts: 15, reb: 2, ast: 2, blk: 0, stl: 2, to: 2, two_a: '6/6', three_a: '1/5', ft_a: '0/0', efg: '68.2%', pm: '-3', starter: true },
      { num: '1', name: 'Nick Shelton', time: '19:00', pts: 0, reb: 1, ast: 0, blk: 0, stl: 0, to: 1, two_a: '0/1', three_a: '0/1', ft_a: '0/0', efg: '0%', pm: '-12', starter: true },
      { num: '2', name: 'Josiah Thibodeaux', time: '1:48', pts: 0, reb: 1, ast: 0, blk: 0, stl: 0, to: 0, two_a: '0/0', three_a: '0/0', ft_a: '0/0', efg: '—', pm: '-2' },
      { num: '3', name: 'Calvin Dagley', time: '8:24', pts: 2, reb: 0, ast: 1, blk: 0, stl: 0, to: 0, two_a: '1/2', three_a: '0/1', ft_a: '0/0', efg: '33.3%', pm: '+4' },
      { num: '4', name: 'Robert Wright Jr', time: '26:34', pts: 16, reb: 3, ast: 4, blk: 0, stl: 0, to: 2, two_a: '6/7', three_a: '0/0', ft_a: '0/0', efg: '85.7%', pm: '-11', starter: true },
      { num: '5', name: 'Tre Mcdaniel', time: '12:18', pts: 4, reb: 0, ast: 0, blk: 1, stl: 2, to: 0, two_a: '2/2', three_a: '0/0', ft_a: '0/0', efg: '100.0%', pm: '-7', starter: true },
      { num: '10', name: 'Jasper Henderson', time: '3:25', pts: 2, reb: 0, ast: 1, blk: 0, stl: 0, to: 0, two_a: '0/0', three_a: '0/0', ft_a: '0/0', efg: '—', pm: '+9' },
      { num: '11', name: 'TJ Jamison', time: '28:11', pts: 13, reb: 6, ast: 2, blk: 0, stl: 0, to: 1, two_a: '2/7', three_a: '3/6', ft_a: '0/0', efg: '50.0%', pm: '0', starter: true },
      { num: '12', name: 'Ashton Jackson', time: '8:35', pts: 0, reb: 0, ast: 2, blk: 0, stl: 1, to: 0, two_a: '0/0', three_a: '0/0', ft_a: '0/0', efg: '—', pm: '+7' },
      { num: '21', name: 'Isaiah Sanders', time: '17:41', pts: 2, reb: 5, ast: 0, blk: 0, stl: 0, to: 0, two_a: '1/1', three_a: '0/0', ft_a: '0/0', efg: '100.0%', pm: '+5' },
    ],
  },
];

// ── Sub-components ────────────────────────────────────────────────────────────

function PlayerTable({ players, teamLabel }) {
  return (
    <div className="overflow-x-auto">
      <p className="text-xs font-bold uppercase tracking-widest mb-2 px-1" style={{ color: ORANGE }}>{teamLabel}</p>
      <table className="w-full text-xs min-w-[700px]">
        <thead>
          <tr className="border-b" style={{ borderColor: 'rgba(255,255,255,0.07)' }}>
            {['#', 'Player', 'Time', 'PTS', 'REB', 'AST', 'BLK', 'STL', 'TO', '2Pt/A', '3Pt/A', 'FT/A', 'EFG%', '+/-'].map(h => (
              <th key={h} className="text-left py-1.5 px-1 text-gray-600 font-bold uppercase tracking-wider whitespace-nowrap">{h}</th>
            ))}
          </tr>
        </thead>
        <tbody>
          {players.map((p, i) => {
            const slug = MILTON_PORTFOLIO_SLUGS[p.name];
            return (
              <tr key={i} className="border-b" style={{ borderColor: 'rgba(255,255,255,0.03)' }}>
                <td className="py-1.5 px-1 text-gray-500">{p.num}</td>
                <td className="py-1.5 px-1 text-white font-medium whitespace-nowrap">
                  {slug ? (
                    <Link to={`/player/${slug}`} className="hover:text-orange-400 transition-colors">{p.name}</Link>
                  ) : (
                    p.name
                  )}{p.starter ? <span className="text-orange-500 ml-0.5">*</span> : null}
                </td>
                <td className="py-1.5 px-1 text-gray-500">{p.time}</td>
                <td className="py-1.5 px-1 font-bold text-white">{p.pts}</td>
                <td className="py-1.5 px-1 text-gray-300">{p.reb}</td>
                <td className="py-1.5 px-1 text-gray-300">{p.ast}</td>
                <td className="py-1.5 px-1 text-gray-400">{p.blk}</td>
                <td className="py-1.5 px-1 text-gray-400">{p.stl}</td>
                <td className="py-1.5 px-1 text-gray-400">{p.to}</td>
                <td className="py-1.5 px-1 text-gray-400">{p.two_a}</td>
                <td className="py-1.5 px-1 text-gray-400">{p.three_a}</td>
                <td className="py-1.5 px-1 text-gray-400">{p.ft_a}</td>
                <td className="py-1.5 px-1 text-gray-400">{p.efg}</td>
                <td className={`py-1.5 px-1 font-bold ${p.pm && p.pm.startsWith('+') ? 'text-green-400' : p.pm && p.pm !== '0' ? 'text-red-400' : 'text-gray-500'}`}>{p.pm}</td>
              </tr>
            );
          })}
        </tbody>
      </table>
    </div>
  );
}

function MiltonTeamStats({ game }) {
  const [expanded, setExpanded] = useState(false);
  const my = game.team_stats.my;
  const opp = game.team_stats.opp;

  const parseFt = (ftStr) => {
    if (!ftStr || !ftStr.includes('/')) return { made: '—', att: '—', pct: '—' };
    const [made, att] = ftStr.split('/').map(Number);
    const pct = att > 0 ? ((made / att) * 100).toFixed(1) + '%' : '—';
    return { made, att, pct };
  };
  const myFt = parseFt(my.ft);
  const oppFt = parseFt(opp.ft);

  const fmt = (v) => (v == null || v === '') ? '—' : v;

  const summaryStats = [
    { label: 'AST', my: my.ast, opp: opp.ast },
    { label: 'REB', my: my.reb, opp: opp.reb },
    { label: 'eFG%', my: my.efg_pct, opp: opp.efg_pct },
    { label: 'FTA', my: myFt.att, opp: oppFt.att },
    { label: 'FT%', my: myFt.pct, opp: oppFt.pct },
    { label: '2FG%', my: my.two_pct, opp: opp.two_pct },
    { label: '3FG%', my: my.three_pct, opp: opp.three_pct },
    { label: 'TO', my: my.to, opp: opp.to },
  ];

  const fullStats = [
    { label: 'Points', my: my.pts, opp: opp.pts },
    { label: 'Pts Per Possession', my: my.ppp, opp: opp.ppp },
    { label: '— Four Factors —', header: true },
    { label: 'Eff. FG%', my: my.efg_pct, opp: opp.efg_pct },
    { label: 'Turnover %', my: my.to_pct, opp: opp.to_pct },
    { label: 'Off. Reb %', my: my.oreb_pct, opp: opp.oreb_pct },
    { label: 'Free Throw Rate', my: my.ftr, opp: opp.ftr },
    { label: '— Shooting —', header: true },
    { label: '2 Pt (M/A)', my: my.two_made != null ? `${my.two_made}/${my.two_att}` : null, opp: opp.two_made != null ? `${opp.two_made}/${opp.two_att}` : null },
    { label: '2 Pt %', my: my.two_pct, opp: opp.two_pct },
    { label: '3 Pt (M/A)', my: my.three_made != null ? `${my.three_made}/${my.three_att}` : null, opp: opp.three_made != null ? `${opp.three_made}/${opp.three_att}` : null },
    { label: '3 Pt %', my: my.three_pct, opp: opp.three_pct },
    { label: 'Free Throws (M/A)', my: my.ft_made != null ? `${my.ft_made}/${my.ft_att}` : null, opp: opp.ft_made != null ? `${opp.ft_made}/${opp.ft_att}` : null },
    { label: 'FT %', my: my.ft_pct, opp: opp.ft_pct },
    { label: 'Scoring Opps', my: my.scoring_opps, opp: opp.scoring_opps },
    { label: 'Total Shots', my: my.shots, opp: opp.shots },
    { label: 'FT Trips', my: my.ft_trips, opp: opp.ft_trips },
    { label: '2 Pt Rate', my: my.two_rate, opp: opp.two_rate },
    { label: '3 Pt Rate', my: my.three_rate, opp: opp.three_rate },
    { label: '— Team Play —', header: true },
    { label: 'Assists', my: my.ast, opp: opp.ast },
    { label: 'Turnovers', my: my.to, opp: opp.to },
    { label: 'Turnover %', my: my.to_pct, opp: opp.to_pct },
    { label: 'Steals', my: my.stl, opp: opp.stl },
    { label: 'Blocks', my: my.blk, opp: opp.blk },
    { label: 'Deflections', my: my.deflections, opp: opp.deflections },
    { label: 'Fouls', my: my.fouls, opp: opp.fouls },
    { label: 'Def. Fouls', my: my.def_fouls, opp: opp.def_fouls },
    { label: 'Charges Taken', my: my.charges, opp: opp.charges },
    { label: 'Kills (3 stops)', my: my.kills, opp: opp.kills },
    { label: '— Rebounding —', header: true },
    { label: 'Total Rebounds', my: my.reb, opp: opp.reb },
    { label: 'Off. Rebounds', my: my.oreb, opp: opp.oreb },
    { label: 'Def. Rebounds', my: my.dreb, opp: opp.dreb },
    { label: 'Off. Reb %', my: my.oreb_pct, opp: opp.oreb_pct },
  ];

  return (
    <div>
      {/* Scoreboard */}
      <div className="flex items-center justify-between px-5 py-4 border-b" style={{ borderColor: 'rgba(255,255,255,0.05)' }}>
        <div className="text-center flex-1">
          <p className="text-xs font-bold uppercase tracking-wider truncate" style={{ color: ORANGE }}>{game.my_team}</p>
          <p className="text-4xl font-black" style={{ color: ORANGE }}>{fmt(my.pts)}</p>
        </div>
        <div className="text-gray-600 font-bold text-sm px-4">vs</div>
        <div className="text-center flex-1">
          <p className="text-xs font-bold uppercase tracking-wider truncate text-gray-400">{game.opp_team}</p>
          <p className="text-4xl font-black text-white">{fmt(opp.pts)}</p>
        </div>
      </div>

      {/* Summary stats grid — my team */}
      <div className="px-5 pt-4 pb-2">
        <p className="text-xs font-bold uppercase tracking-widest mb-3" style={{ color: ORANGE }}>{game.my_team}</p>
        <div className="grid grid-cols-4 sm:grid-cols-8 gap-2">
          {summaryStats.map(s => (
            <div key={s.label} className="text-center">
              <div className="text-base font-black text-white">{fmt(s.my)}</div>
              <div className="text-[10px] text-gray-600 uppercase tracking-wider leading-tight">{s.label}</div>
            </div>
          ))}
        </div>
      </div>

      {/* Summary stats grid — opponent */}
      <div className="px-5 pt-2 pb-3 border-b" style={{ borderColor: 'rgba(255,255,255,0.05)' }}>
        <p className="text-xs font-bold uppercase tracking-widest mb-3 text-gray-500">{game.opp_team}</p>
        <div className="grid grid-cols-4 sm:grid-cols-8 gap-2">
          {summaryStats.map(s => (
            <div key={s.label} className="text-center">
              <div className="text-base font-black text-gray-400">{fmt(s.opp)}</div>
              <div className="text-[10px] text-gray-700 uppercase tracking-wider leading-tight">{s.label}</div>
            </div>
          ))}
        </div>
      </div>

      {/* Full Game Stats toggle */}
      <button
        onClick={() => setExpanded(!expanded)}
        className="flex items-center justify-center gap-2 w-full py-3 text-xs font-bold uppercase tracking-widest border-b transition-colors"
        style={{ borderColor: 'rgba(255,255,255,0.05)', color: expanded ? ORANGE : 'rgba(255,255,255,0.35)' }}
      >
        {expanded ? <ChevronUp className="w-3.5 h-3.5" /> : <ChevronDown className="w-3.5 h-3.5" />}
        Full Game Stats
      </button>

      {/* Expanded full stats table */}
      {expanded && (
        <div className="px-4 pb-4">
          <div className="grid grid-cols-3 gap-2 py-2 text-xs font-bold uppercase tracking-wider border-b mb-1" style={{ borderColor: 'rgba(255,255,255,0.07)' }}>
            <div style={{ color: ORANGE }} className="truncate">{game.my_team}</div>
            <div className="text-center text-gray-600">Stat</div>
            <div className="text-right text-gray-500 truncate">{game.opp_team}</div>
          </div>
          {fullStats.map(s => s.header ? (
            <div key={s.label} className="text-xs font-black uppercase tracking-widest mt-3 mb-1 px-1" style={{ color: ORANGE }}>{s.label.replace(/—/g, '').trim()}</div>
          ) : (
            <div key={s.label} className="grid grid-cols-3 gap-2 py-1.5 rounded-lg px-1" style={{ background: 'rgba(255,255,255,0.02)' }}>
              <div className="text-sm font-bold text-white">{fmt(s.my)}</div>
              <div className="text-center text-xs text-gray-600 self-center">{s.label}</div>
              <div className="text-sm font-bold text-right text-gray-300">{fmt(s.opp)}</div>
            </div>
          ))}
        </div>
      )}
    </div>
  );
}

function GameCard({ game }) {
  const [open, setOpen] = useState(false);
  const [showMyPlayers, setShowMyPlayers] = useState(false);
  const [showOppPlayers, setShowOppPlayers] = useState(false);

  return (
    <div className="rounded-xl border overflow-hidden" style={{ borderColor: 'rgba(255,255,255,0.07)', background: '#0a0a0a' }}>
      {/* Header */}
      <button
        onClick={() => setOpen(o => !o)}
        className="w-full flex items-center justify-between px-4 py-3 hover:bg-white/[0.02] transition-colors text-left"
      >
        <p className="font-bold text-sm truncate flex-1 min-w-0">
          <span style={{ color: ORANGE }}>{game.my_team}</span>
          <span className="text-gray-500 mx-1">vs</span>
          <span className="text-white">{game.opp_team}</span>
          <span className="text-gray-600 ml-2">· {game.date} · {game.time}</span>
        </p>
        <div className="flex items-center gap-2 flex-shrink-0 ml-2">
          {game.film_url && (
            <a href={game.film_url} target="_blank" rel="noopener noreferrer"
              onClick={e => e.stopPropagation()}
              className="p-1.5 rounded-lg text-gray-500 hover:text-white transition-colors"
              style={{ background: 'rgba(255,255,255,0.05)' }}>
              <ExternalLink className="w-3 h-3" />
            </a>
          )}
          {open ? <ChevronUp className="w-4 h-4 text-gray-500" /> : <ChevronDown className="w-4 h-4 text-gray-500" />}
        </div>
      </button>

      {open && (
        <div className="border-t" style={{ borderColor: 'rgba(255,255,255,0.05)' }}>
          {/* Game Film */}
          {game.film_url && (
            <div className="aspect-video bg-black">
              <iframe
                src={vimeoEmbed(game.film_url)}
                className="w-full h-full"
                allow="autoplay; fullscreen; picture-in-picture"
                allowFullScreen
                title={`${game.my_team} vs ${game.opp_team}`}
              />
            </div>
          )}
          {/* Team Stats */}
          <MiltonTeamStats game={game} />

          {/* Player Stats toggles */}
          <div className="px-4 pb-4 pt-2 space-y-3">
            {/* My team players */}
            <div>
              <button
                onClick={() => setShowMyPlayers(o => !o)}
                className="flex items-center gap-2 text-xs font-bold uppercase tracking-widest w-full py-2 border-t transition-colors"
                style={{ borderColor: 'rgba(255,255,255,0.07)', color: showMyPlayers ? ORANGE : 'rgba(255,255,255,0.4)' }}
              >
                <Users className="w-3.5 h-3.5" />
                {game.my_team} Player Stats
                {showMyPlayers ? <ChevronUp className="w-3.5 h-3.5 ml-auto" /> : <ChevronDown className="w-3.5 h-3.5 ml-auto" />}
              </button>
              {showMyPlayers && <PlayerTable players={game.my_players} teamLabel={game.my_team} />}
            </div>

            {/* Opponent players */}
            <div>
              <button
                onClick={() => setShowOppPlayers(o => !o)}
                className="flex items-center gap-2 text-xs font-bold uppercase tracking-widest w-full py-2 border-t transition-colors"
                style={{ borderColor: 'rgba(255,255,255,0.07)', color: showOppPlayers ? ORANGE : 'rgba(255,255,255,0.4)' }}
              >
                <Users className="w-3.5 h-3.5" />
                {game.opp_team} Player Stats
                {showOppPlayers ? <ChevronUp className="w-3.5 h-3.5 ml-auto" /> : <ChevronDown className="w-3.5 h-3.5 ml-auto" />}
              </button>
              {showOppPlayers && <PlayerTable players={game.opp_players} teamLabel={game.opp_team} />}
            </div>
          </div>
        </div>
      )}
    </div>
  );
}

function SessionSection({ title, games, defaultOpen = false }) {
  const [open, setOpen] = useState(defaultOpen);
  const wins = games.filter(g => g.result === 'W').length;
  const losses = games.filter(g => g.result === 'L').length;

  return (
    <div className="rounded-2xl border overflow-hidden" style={{ borderColor: 'rgba(255,255,255,0.07)', background: '#050505' }}>
      <button
        onClick={() => setOpen(o => !o)}
        className="w-full flex items-center justify-between px-5 py-4 hover:bg-white/[0.02] transition-colors text-left"
      >
        <div>
          <p className="font-barlow font-black text-lg uppercase tracking-wide text-white">
            {title}
          </p>
          <p className="text-xs text-gray-500 mt-0.5">
            {games.length} games · <span className="text-green-400 font-bold">{wins}W</span> <span className="text-red-400 font-bold">{losses}L</span>
          </p>
        </div>
        {open ? <ChevronUp className="w-5 h-5" style={{ color: ORANGE }} /> : <ChevronDown className="w-5 h-5 text-gray-500" />}
      </button>

      {open && (
        <div className="border-t px-4 pb-4 pt-3 space-y-3" style={{ borderColor: 'rgba(255,255,255,0.06)' }}>
          {games.map(game => (
            <GameCard key={game.id} game={game} />
          ))}
        </div>
      )}
    </div>
  );
}

export default function MiltonGameStats() {
  const [films, setFilms] = useState([]);

  useEffect(() => {
    const load = async () => {
      try {
        const allGames = await base44.entities.GeorgiaGame.list();
        const miltonFilms = allGames
          .filter(g => {
            const isMilton = normalizeTeam(g.team1).includes('milton') || normalizeTeam(g.team2).includes('milton');
            return isMilton && g.embed_link;
          })
          .map(g => ({
            date: toISODate(g.date),
            opponent: normalizeTeam(g.team1).includes('milton') ? g.team2 : g.team1,
            embed_link: g.embed_link,
          }));
        setFilms(miltonFilms);
      } catch (e) {
        console.error('Error loading game films:', e);
      }
    };
    load();
  }, []);

  const findFilm = (game) => {
    const isoDate = toISODate(game.date);
    const match = films.find(f => f.date === isoDate && teamsMatch(f.opponent, game.opp_team));
    return match?.embed_link || game.film_url;
  };

  const mergeFilm = (games) => games.map(g => ({ ...g, film_url: findFilm(g) }));

  return (
    <div className="space-y-4">
      <SessionSection
        title="GBCA Live Period — Session 1 · June 12–14, 2026"
        games={mergeFilm(SESSION_1_GAMES)}
        defaultOpen={false}
      />
      <SessionSection
        title="GBCA Live Period — Session 2 SE Regional · June 26–28, 2026"
        games={mergeFilm(SESSION_2_GAMES)}
        defaultOpen={true}
      />
      <p className="text-xs text-gray-700 text-center pt-2">Stats powered by Hoopsalytics · * = Starter</p>
    </div>
  );
}

src/components/georgia/MiltonSponsors.jsx

const SPONSORS = [
  { name: 'Harvest Grocer', logo: 'https://media.base44.com/images/public/69bb16a38ac0086814d94e6f/fece4a897_2.png' },
  { name: 'Brew & Bean Coffee Shop', logo: 'https://media.base44.com/images/public/69bb16a38ac0086814d94e6f/c50e070bb_5.png' },
  { name: 'Crave Restaurant', logo: 'https://media.base44.com/images/public/69bb16a38ac0086814d94e6f/5bf67904d_6.png' },
  { name: 'Rimberio Construction', logo: 'https://media.base44.com/images/public/69bb16a38ac0086814d94e6f/044b3dc64_1.png' },
  { name: 'Family Dentist', logo: 'https://media.base44.com/images/public/69bb16a38ac0086814d94e6f/742114927_3.png' },
  { name: 'Alignwell', logo: 'https://media.base44.com/images/public/69bb16a38ac0086814d94e6f/68d400d13_4.png' },
];

export default function MiltonSponsors() {
  return (
    <section className="mt-12 pt-8 border-t" style={{ borderColor: 'rgba(255,255,255,0.07)' }}>
      <div className="text-center mb-6">
        <p className="font-barlow font-black text-3xl uppercase tracking-wide text-white">We Thank Our Local Sponsors</p>
        <p className="mt-2 text-sm text-gray-500">Proud supporters of Milton basketball.</p>
      </div>
      <div className="grid grid-cols-2 md:grid-cols-3 gap-3 sm:gap-4">
        {SPONSORS.map((sponsor) => (
          <div key={sponsor.name} className="aspect-square rounded-xl overflow-hidden border p-3 sm:p-5" style={{ background: '#0a0a0a', borderColor: 'rgba(255,255,255,0.08)' }}>
            <img src={sponsor.logo} alt={`${sponsor.name} logo`} className="w-full h-full object-contain" />
          </div>
        ))}
      </div>
    </section>
  );
}

src/components/georgia/PortfolioDemo.jsx

import { motion } from 'framer-motion';
import { ExternalLink } from 'lucide-react';

const ORANGE = '#FF6A00';
const ORANGE_BORDER = 'rgba(255,106,0,0.22)';
const ORANGE_DIM = 'rgba(255,106,0,0.12)';

const PORTFOLIO_URL = 'https://goaio.live/player/solomon-bratton';

export default function PortfolioDemo() {
  return (
    <motion.div
      initial={{ opacity: 0, y: 24 }}
      whileInView={{ opacity: 1, y: 0 }}
      viewport={{ once: true }}
      transition={{ duration: 0.6 }}
      className="rounded-2xl overflow-hidden"
      style={{ border: `1px solid ${ORANGE_BORDER}`, boxShadow: `0 0 40px ${ORANGE_DIM}` }}
    >
      {/* Browser-style header */}
      <div className="flex items-center justify-between px-5 py-3.5 border-b" style={{ background: '#1a1a1a', borderColor: 'rgba(255,255,255,0.06)' }}>
        <div className="flex items-center gap-2.5">
          <div className="flex gap-1.5">
            <div className="w-3 h-3 rounded-full bg-red-500/70" />
            <div className="w-3 h-3 rounded-full bg-orange-400/70" />
            <div className="w-3 h-3 rounded-full bg-green-500/70" />
          </div>
          <span className="text-xs font-mono" style={{ color: 'rgba(255,255,255,0.35)' }}>
            goaio.live/player/solomon-bratton
          </span>
        </div>
        <a
          href={PORTFOLIO_URL}
          target="_blank"
          rel="noopener noreferrer"
          className="inline-flex items-center gap-1 text-xs transition-colors"
          style={{ color: ORANGE }}
        >
          <ExternalLink className="w-3.5 h-3.5" /> Open Live
        </a>
      </div>

      {/* Static iframe — live portfolio */}
      <div className="w-full" style={{ background: '#0a0a0a' }}>
        <iframe
          src={PORTFOLIO_URL}
          title="Solomon Bratton — Live Blueprint Portfolio"
          className="w-full border-0"
          style={{ height: '560px' }}
          loading="eager"
          referrerPolicy="no-referrer-when-downgrade"
        />
      </div>

      <div className="px-5 py-3.5 border-t" style={{ background: '#141414', borderColor: 'rgba(255,255,255,0.06)' }}>
        <p className="text-xs" style={{ color: 'rgba(255,255,255,0.4)' }}>
          <span className="font-semibold" style={{ color: ORANGE }}>Solomon Bratton</span> — Live Blueprint Portfolio · Auto-generated from game footage
        </p>
      </div>
    </motion.div>
  );
}

src/components/georgia/PortfolioLinkCard.jsx

import { motion } from 'framer-motion';
import { ArrowRight } from 'lucide-react';

const ORANGE = '#FF6A00';
const ORANGE_BORDER = 'rgba(255,106,0,0.22)';
const ORANGE_DIM = 'rgba(255,106,0,0.12)';

const PORTFOLIO_URL = 'https://goaio.live/player/solomon-bratton';

export default function PortfolioLinkCard() {
  return (
    <motion.div
      initial={{ opacity: 0, y: 24 }}
      whileInView={{ opacity: 1, y: 0 }}
      viewport={{ once: true }}
      transition={{ duration: 0.6 }}
      className="rounded-2xl overflow-hidden"
      style={{ background: '#0a0a0a', border: `1px solid ${ORANGE_BORDER}` }}
    >
      <a
        href={PORTFOLIO_URL}
        target="_blank"
        rel="noopener noreferrer"
        className="block w-full text-left transition-all hover:scale-[1.01]"
      >
        <div className="flex flex-col items-center justify-center text-center gap-4 px-6 py-16">
          <div
            className="flex items-center justify-center w-16 h-16 rounded-2xl"
            style={{ background: ORANGE_DIM, border: `1px solid ${ORANGE_BORDER}` }}
          >
            <ArrowRight className="w-7 h-7" style={{ color: ORANGE }} />
          </div>
          <div>
            <p className="font-barlow font-black text-2xl text-white mb-1">Sample Portfolio</p>
            <p className="text-sm" style={{ color: 'rgba(255,255,255,0.45)' }}>
              Click here to view Solomon Bratton's live portfolio
            </p>
          </div>
          <span
            className="inline-flex items-center gap-2 px-5 py-2.5 rounded-full text-sm font-black uppercase tracking-widest"
            style={{ background: ORANGE, color: '#000' }}
          >
            View Portfolio <ArrowRight className="w-4 h-4" />
          </span>
        </div>
      </a>
    </motion.div>
  );
}

src/components/georgia/PortfolioSearch.jsx

import { useState, useRef, useEffect } from 'react';
import { useNavigate } from 'react-router-dom';
import { Search, ArrowRight, Camera, Loader2, AlertCircle, Share2, ChevronDown, ChevronUp } from 'lucide-react';
import { base44 } from '@/api/base44Client';
import { motion, AnimatePresence } from 'framer-motion';
import SharePortfolioModal from './SharePortfolioModal';

const ORANGE = '#FF6A00';
const BORDER = 'rgba(255,255,255,0.08)';

export default function PortfolioSearch({ autoFocus = false }) {
  const [query, setQuery] = useState('');
  const [discountOpen, setDiscountOpen] = useState(false);
  const [allPlayers, setAllPlayers] = useState(null);
  const [loadingPlayers, setLoadingPlayers] = useState(false);
  const [sharingPlayer, setSharingPlayer] = useState(null);
  const inputRef = useRef(null);
  const navigate = useNavigate();

  useEffect(() => {
    if (autoFocus && inputRef.current) inputRef.current.focus();
  }, [autoFocus]);

  // Load all published players once on mount
  useEffect(() => {
    setLoadingPlayers(true);
    base44.entities.Player.filter({ is_published: true }, '-created_date', 5000)
      .then(players => setAllPlayers(players))
      .catch(() => setAllPlayers([]))
      .finally(() => setLoadingPlayers(false));
  }, []);

  const results = (() => {
    if (!allPlayers || !query.trim()) return null;
    const q = query.trim().toLowerCase();
    return allPlayers.filter(p => (p.full_name || '').toLowerCase().includes(q));
  })();

  const handlePlayerClick = (player) => {
    if (player.portfolio_url_slug) {
      navigate(`/player/${player.portfolio_url_slug}`);
    }
  };

  return (
    <div className="w-full max-w-2xl mx-auto">
      {/* $25 Discount callout — collapsible */}
      <motion.div
        initial={{ opacity: 0, y: 12 }} animate={{ opacity: 1, y: 0 }} transition={{ delay: 0.1 }}
        className="mb-6 overflow-hidden"
        style={{ border: `1px solid rgba(255,106,0,0.35)`, background: 'rgba(255,106,0,0.07)' }}
      >
        <button
          type="button"
          onClick={() => setDiscountOpen(o => !o)}
          className="w-full flex items-center justify-between gap-3 p-4 text-left"
        >
          <div className="flex items-center gap-3">
            <Camera className="w-5 h-5 shrink-0" style={{ color: ORANGE }} />
            <p className="font-barlow font-black text-sm uppercase tracking-widest text-white" style={{ letterSpacing: '0.12em' }}>
              📸 Save $25 — Visit the GameOn AIO Table
            </p>
          </div>
          <div className="shrink-0 flex items-center gap-2">
            <span className="font-barlow font-black text-lg" style={{ color: ORANGE }}>$25 off</span>
            {discountOpen
              ? <ChevronUp className="w-4 h-4" style={{ color: ORANGE }} />
              : <ChevronDown className="w-4 h-4" style={{ color: ORANGE }} />}
          </div>
        </button>
        <AnimatePresence initial={false}>
          {discountOpen && (
            <motion.div
              initial={{ height: 0, opacity: 0 }}
              animate={{ height: 'auto', opacity: 1 }}
              exit={{ height: 0, opacity: 0 }}
              transition={{ duration: 0.25 }}
              style={{ overflow: 'hidden' }}
            >
              <div className="px-4 pb-4 pt-0 border-t" style={{ borderColor: 'rgba(255,106,0,0.2)' }}>
                <p className="text-sm leading-relaxed mt-3" style={{ color: 'rgba(255,255,255,0.6)' }}>
                  Come by the <span className="text-white font-semibold">GameOn AIO courtside table</span> and get your official player photo taken by our team. That photo goes straight onto your portfolio — and you get <span style={{ color: ORANGE }} className="font-bold">$25 off</span> at checkout automatically.
                </p>
                <div className="mt-3 flex items-center gap-2">
                  <span className="w-2 h-2 rounded-full animate-pulse" style={{ background: ORANGE }} />
                  <span className="text-xs font-semibold uppercase tracking-widest" style={{ color: ORANGE }}>No code needed — discount applied at the table</span>
                </div>
              </div>
            </motion.div>
          )}
        </AnimatePresence>
      </motion.div>

      {/* Search input */}
      <motion.div
        initial={{ opacity: 0, y: 16 }} animate={{ opacity: 1, y: 0 }} transition={{ delay: 0.15 }}
        className="relative mb-6"
      >
        <div className="absolute left-4 top-1/2 -translate-y-1/2 pointer-events-none">
          {loadingPlayers
            ? <Loader2 className="w-4 h-4 animate-spin" style={{ color: 'rgba(255,255,255,0.3)' }} />
            : <Search className="w-4 h-4" style={{ color: 'rgba(255,255,255,0.3)' }} />}
        </div>
        <input
          ref={inputRef}
          type="text"
          placeholder="Search your name…"
          value={query}
          onChange={e => setQuery(e.target.value)}
          className="w-full pl-11 pr-4 py-4 text-base text-white placeholder-white/30 focus:outline-none"
          style={{ background: '#0a0a0a', border: `1px solid ${BORDER}`, fontFamily: 'var(--font-inter)' }}
          onFocus={e => e.currentTarget.style.borderColor = 'rgba(255,106,0,0.5)'}
          onBlur={e => e.currentTarget.style.borderColor = BORDER}
        />
      </motion.div>

      {/* Results */}
      <AnimatePresence>
        {results !== null && (
          <motion.div initial={{ opacity: 0, y: 12 }} animate={{ opacity: 1, y: 0 }} exit={{ opacity: 0 }}>
            {results.length === 0 ? (
              <div className="flex items-center gap-3 p-5" style={{ border: `1px solid ${BORDER}`, background: '#0a0a0a' }}>
                <AlertCircle className="w-5 h-5 shrink-0" style={{ color: 'rgba(255,255,255,0.3)' }} />
                <div>
                  <p className="text-sm font-semibold text-white">No portfolio found</p>
                  <p className="text-xs mt-0.5" style={{ color: 'rgba(255,255,255,0.4)' }}>
                    Your portfolio may still be processing — check back after the tournament, or visit the GameOn AIO table.
                  </p>
                </div>
              </div>
            ) : (
              <div className="space-y-2">
                <p className="text-xs uppercase tracking-widest mb-3" style={{ color: 'rgba(255,255,255,0.3)', letterSpacing: '0.16em' }}>
                  {results.length} result{results.length !== 1 ? 's' : ''} found
                </p>
                {results.map(player => (
                  <div key={player.id} className="flex items-center gap-2">
                    <button onClick={() => handlePlayerClick(player)}
                      disabled={!player.portfolio_url_slug}
                      className="flex-1 text-left flex items-center gap-4 p-4 transition-all"
                      style={{ border: `1px solid ${BORDER}`, background: '#0a0a0a', cursor: player.portfolio_url_slug ? 'pointer' : 'default' }}
                      onMouseEnter={e => { if (player.portfolio_url_slug) e.currentTarget.style.borderColor = 'rgba(255,106,0,0.4)'; }}
                      onMouseLeave={e => e.currentTarget.style.borderColor = BORDER}
                    >
                      {/* Photo or initials */}
                      <div className="w-12 h-12 shrink-0 overflow-hidden" style={{ border: `1px solid ${BORDER}` }}>
                        {player.profile_photo_url
                          ? <img src={player.profile_photo_url} alt={player.full_name} className="w-full h-full object-cover" />
                          : <div className="w-full h-full flex items-center justify-center font-barlow font-black text-lg" style={{ background: '#161616', color: ORANGE }}>
                              {(player.full_name || '?')[0]}
                            </div>
                        }
                      </div>
                      <div className="flex-1 min-w-0">
                        <p className="font-barlow font-black text-white text-base uppercase">{player.full_name}</p>
                        <p className="text-xs mt-0.5" style={{ color: 'rgba(255,255,255,0.4)' }}>
                          {[player.position, player.high_school, player.graduation_year ? `Class of ${player.graduation_year}` : null].filter(Boolean).join(' · ')}
                        </p>
                      </div>
                      {player.portfolio_url_slug && (
                        <div className="flex items-center gap-1 text-xs font-black uppercase tracking-widest shrink-0" style={{ color: ORANGE }}>
                          View <ArrowRight className="w-3.5 h-3.5" />
                        </div>
                      )}
                    </button>
                    {player.portfolio_url_slug && (
                      <button
                        onClick={() => setSharingPlayer(player)}
                        className="flex items-center justify-center w-12 h-full py-4 shrink-0 transition-all"
                        style={{ border: `1px solid ${BORDER}`, background: '#0a0a0a', minHeight: '72px' }}
                        onMouseEnter={e => e.currentTarget.style.borderColor = 'rgba(255,106,0,0.4)'}
                        onMouseLeave={e => e.currentTarget.style.borderColor = BORDER}
                        title="Share portfolio"
                      >
                        <Share2 className="w-4 h-4" style={{ color: ORANGE }} />
                      </button>
                    )}
                  </div>
                ))}
              </div>
            )}
          </motion.div>
        )}
      </AnimatePresence>
      {sharingPlayer && (
        <SharePortfolioModal player={sharingPlayer} onClose={() => setSharingPlayer(null)} />
      )}
    </div>
  );
}

src/components/georgia/RecruitingFactsGrid.jsx

import { motion } from 'framer-motion';

const ORANGE = '#FF6A00';

const facts = [
  {
    stat: '92%',
    label: 'of college coaches now require verified game analytics before extending an offer',
    icon: '📊',
  },
  {
    stat: '3×',
    label: 'more likely to receive D1/D2 interest with an authenticated film + stats portfolio',
    icon: '🎯',
  },
  {
    stat: '400%',
    label: 'increase in coach engagement when film is paired with verified per-game boxscores',
    icon: '📈',
  },
];

export default function RecruitingFactsGrid() {
  return (
    <motion.div
      initial={{ opacity: 0, y: 20 }}
      animate={{ opacity: 1, y: 0 }}
      transition={{ delay: 0.15, duration: 0.6 }}
      className="w-full mb-8"
    >
      <p className="text-center text-[10px] font-black uppercase tracking-[0.2em] mb-3" style={{ color: 'rgba(255,255,255,0.3)' }}>
        Why your portfolio matters
      </p>
      <div className="grid grid-cols-3 gap-2">
        {facts.map((f, i) => (
          <motion.div
            key={i}
            initial={{ opacity: 0, y: 16 }}
            animate={{ opacity: 1, y: 0 }}
            transition={{ delay: 0.2 + i * 0.08, duration: 0.5 }}
            className="flex flex-col items-center text-center p-3 rounded-xl"
            style={{ background: 'rgba(255,106,0,0.05)', border: '1px solid rgba(255,106,0,0.15)' }}
          >
            <span className="text-lg mb-1">{f.icon}</span>
            <span className="font-barlow font-black text-2xl leading-none mb-1" style={{ color: ORANGE }}>{f.stat}</span>
            <p className="text-[10px] leading-snug" style={{ color: 'rgba(255,255,255,0.4)' }}>{f.label}</p>
          </motion.div>
        ))}
      </div>
    </motion.div>
  );
}

src/components/georgia/RosterPlayerCard.jsx

import { useState } from 'react';
import { Link } from 'react-router-dom';
import { CheckCircle2, Star, ShoppingCart, Copy, Check, Mail, MessageSquare, Share2 } from 'lucide-react';

const ORANGE = '#FF6A00';

export default function RosterPlayerCard({ player, user }) {
  const [copied, setCopied] = useState(false);

  const premium = player.portfolio && (player.portfolio.portfolio_tier === 'premium' || player.portfolio.portfolio_tier === 'under_review');
  const slug = player.portfolio?.portfolio_url_slug || player.portfolio_url_slug;
  const portfolioUrl = slug ? `https://goaio.live/player/${slug}` : null;
  const claimUrl = `https://goaio.live/georgiaplayer`;

  // The link to share — either the existing profile or the claim page
  const shareUrl = portfolioUrl || claimUrl;
  const playerName = player.full_name || 'Player';

  const shareText = portfolioUrl
    ? `${playerName}'s player portfolio: ${portfolioUrl}`
    : `Hey ${playerName}, claim your player portfolio here: ${claimUrl}`;

  const handleCopy = () => {
    navigator.clipboard.writeText(shareUrl);
    setCopied(true);
    setTimeout(() => setCopied(false), 2000);
  };

  const handleEmail = () => {
    const subject = encodeURIComponent(portfolioUrl ? `${playerName}'s Player Portfolio` : `Claim Your Player Portfolio`);
    const body = encodeURIComponent(shareText);
    window.open(`mailto:?subject=${subject}&body=${body}`);
  };

  const handleText = () => {
    window.open(`sms:?&body=${encodeURIComponent(shareText)}`);
  };

  const isCoachOrAdmin = user && (user.role === 'admin' || user.role === 'coach');

  return (
    <div className="rounded-xl border overflow-hidden transition-all"
      style={{ background: '#0a0a0a', borderColor: premium ? 'rgba(255,106,0,0.25)' : 'rgba(255,255,255,0.06)' }}>

      {/* Header with photo and name */}
      <div className="flex items-start gap-4 px-4 pt-3 pb-2">
        {/* Photo */}
        <div className="w-10 h-10 rounded-full overflow-hidden shrink-0 border mt-0.5"
          style={{ borderColor: premium ? 'rgba(255,106,0,0.4)' : 'rgba(255,255,255,0.06)', background: 'rgba(255,255,255,0.04)' }}>
          {player.photo_url || player.portfolio?.profile_photo_url
            ? <img src={player.photo_url || player.portfolio?.profile_photo_url} alt={playerName} className="w-full h-full object-cover" />
            : <div className="w-full h-full flex items-center justify-center font-barlow font-black text-sm text-gray-500">{playerName[0]}</div>}
        </div>

        {/* Name and details */}
        <div className="flex-1 min-w-0">
          <div className="flex items-center gap-2 mb-1">
            {slug ? (
              <Link to={`/player/${slug}`} className="font-semibold text-sm" style={{ color: premium ? 'white' : 'rgba(255,255,255,0.7)' }}>{playerName}</Link>
            ) : (
              <p className="font-semibold text-sm" style={{ color: premium ? 'white' : 'rgba(255,255,255,0.7)' }}>{playerName}</p>
            )}
            {premium && <Star className="w-3.5 h-3.5 shrink-0" style={{ color: ORANGE }} />}
            <span className="text-xs text-gray-600 ml-auto shrink-0">
              {[player.portfolio?.height, player.portfolio?.weight_lbs ? `${player.portfolio.weight_lbs}lbs` : null].filter(Boolean).join(' · ')}
            </span>
          </div>
          <p className="text-xs text-gray-600">
            {[player.jersey_number ? `#${player.jersey_number}` : null, player.position, player.portfolio?.class_year || player.class_year].filter(Boolean).join(' · ')}
          </p>
        </div>
      </div>

      {/* Share and Claim buttons side by side */}
      <div className="flex items-center gap-2 px-4 py-3 border-t" style={{ borderColor: 'rgba(255,255,255,0.06)' }}>
        <button onClick={handleCopy} title="Share portfolio link"
          className="flex-1 flex items-center justify-center gap-1 px-3 py-2 rounded-lg text-xs font-bold transition-all"
          style={{ background: 'rgba(255,255,255,0.06)', color: copied ? '#00FF85' : 'rgba(255,255,255,0.5)', border: '1px solid rgba(255,255,255,0.1)' }}>
          {copied ? <Check className="w-3 h-3" /> : <Share2 className="w-3 h-3" />}
          {copied ? 'Copied!' : 'Share'}
        </button>
        {premium && slug ? (
          <Link to={`/player/${slug}`}
            className="flex-1 flex items-center justify-center gap-1.5 px-3 py-2 rounded-lg text-xs font-bold transition-all"
            style={{ background: 'rgba(0,255,133,0.1)', color: '#00FF85', border: '1px solid rgba(0,255,133,0.25)' }}>
            <CheckCircle2 className="w-3 h-3" /> Claimed
          </Link>
        ) : (
          <Link to="/georgiaplayer"
            className="flex-1 flex items-center justify-center gap-1.5 px-3 py-2 rounded-lg text-xs font-bold transition-all"
            style={{ background: 'rgba(255,106,0,0.1)', color: ORANGE, border: '1px solid rgba(255,106,0,0.3)' }}>
            <ShoppingCart className="w-3 h-3" /> Claim
          </Link>
        )}
      </div>

      {/* Coach share bar — shown for unclaimed players OR always for admins/coaches */}
      {isCoachOrAdmin && (
        <div className="flex items-center gap-2 px-4 py-2 border-t"
          style={{ borderColor: 'rgba(255,255,255,0.05)', background: 'rgba(255,255,255,0.02)' }}>
          <p className="text-xs text-gray-600 flex-1 truncate">
            {premium ? 'Share portfolio:' : 'Send claim link:'} <span className="text-gray-500">{shareUrl}</span>
          </p>
          <button onClick={handleCopy} title="Copy link"
            className="flex items-center gap-1 px-2 py-1 rounded text-xs font-semibold transition-all"
            style={{ background: 'rgba(255,255,255,0.06)', color: copied ? '#00FF85' : 'rgba(255,255,255,0.5)' }}>
            {copied ? <Check className="w-3 h-3" /> : <Copy className="w-3 h-3" />}
            {copied ? 'Copied' : 'Copy'}
          </button>
          <button onClick={handleEmail} title="Send via email"
            className="flex items-center gap-1 px-2 py-1 rounded text-xs font-semibold transition-all"
            style={{ background: 'rgba(255,255,255,0.06)', color: 'rgba(255,255,255,0.5)' }}>
            <Mail className="w-3 h-3" /> Email
          </button>
          <button onClick={handleText} title="Send via text"
            className="flex items-center gap-1 px-2 py-1 rounded text-xs font-semibold transition-all"
            style={{ background: 'rgba(255,255,255,0.06)', color: 'rgba(255,255,255,0.5)' }}>
            <MessageSquare className="w-3 h-3" /> Text
          </button>
        </div>
      )}
    </div>
  );
}

src/components/georgia/SharePortfolioModal.jsx

import { useState } from 'react';
import { X, Mail, MessageSquare, Copy, Check } from 'lucide-react';
import { motion, AnimatePresence } from 'framer-motion';

const ORANGE = '#FF6A00';
const BORDER = 'rgba(255,255,255,0.08)';

export default function SharePortfolioModal({ player, onClose }) {
  const [copied, setCopied] = useState(false);

  const portfolioUrl = `${window.location.origin}/player/${player.portfolio_url_slug}`;
  const message = `Hey! Check out ${player.full_name}'s basketball portfolio — film, stats, and highlights all in one place: ${portfolioUrl}`;

  const handleEmail = () => {
    const subject = encodeURIComponent(`${player.full_name}'s Basketball Portfolio`);
    const body = encodeURIComponent(message);
    window.open(`mailto:?subject=${subject}&body=${body}`, '_blank');
  };

  const handleSMS = () => {
    const body = encodeURIComponent(message);
    window.open(`sms:?body=${body}`, '_blank');
  };

  const handleCopy = async () => {
    await navigator.clipboard.writeText(portfolioUrl);
    setCopied(true);
    setTimeout(() => setCopied(false), 2000);
  };

  return (
    <AnimatePresence>
      <motion.div
        initial={{ opacity: 0 }}
        animate={{ opacity: 1 }}
        exit={{ opacity: 0 }}
        className="fixed inset-0 z-[200] flex items-center justify-center p-4"
        style={{ background: 'rgba(0,0,0,0.85)', backdropFilter: 'blur(6px)' }}
        onClick={onClose}
      >
        <motion.div
          initial={{ opacity: 0, scale: 0.95, y: 16 }}
          animate={{ opacity: 1, scale: 1, y: 0 }}
          exit={{ opacity: 0, scale: 0.95, y: 8 }}
          transition={{ duration: 0.25, ease: [0.4, 0, 0.2, 1] }}
          className="relative w-full max-w-sm overflow-hidden"
          style={{ background: '#0D0D0D', border: `1px solid rgba(255,106,0,0.3)`, boxShadow: '0 0 40px rgba(255,106,0,0.1)' }}
          onClick={e => e.stopPropagation()}
        >
          {/* Header */}
          <div className="flex items-start justify-between px-6 pt-6 pb-4 border-b" style={{ borderColor: BORDER }}>
            <div>
              <p className="text-[10px] font-black uppercase tracking-widest mb-1" style={{ color: ORANGE, letterSpacing: '0.18em' }}>
                Share Portfolio
              </p>
              <h3 className="font-barlow font-black text-xl text-white leading-tight">{player.full_name}</h3>
              <p className="text-xs mt-0.5" style={{ color: 'rgba(255,255,255,0.4)' }}>Send to a parent or coach</p>
            </div>
            <button onClick={onClose} style={{ color: 'rgba(255,255,255,0.3)' }}
              onMouseEnter={e => e.currentTarget.style.color = '#fff'}
              onMouseLeave={e => e.currentTarget.style.color = 'rgba(255,255,255,0.3)'}
            >
              <X className="w-5 h-5" />
            </button>
          </div>

          {/* Options */}
          <div className="px-6 py-5 space-y-3">
            <button onClick={handleEmail}
              className="w-full flex items-center gap-4 px-4 py-3.5 transition-all text-left"
              style={{ background: '#141414', border: `1px solid ${BORDER}` }}
              onMouseEnter={e => e.currentTarget.style.borderColor = 'rgba(255,106,0,0.4)'}
              onMouseLeave={e => e.currentTarget.style.borderColor = BORDER}
            >
              <div className="w-9 h-9 flex items-center justify-center shrink-0"
                style={{ background: 'rgba(255,106,0,0.1)', border: `1px solid rgba(255,106,0,0.2)` }}>
                <Mail className="w-4 h-4" style={{ color: ORANGE }} />
              </div>
              <div>
                <p className="font-barlow font-black text-sm text-white uppercase tracking-wide">Send via Email</p>
                <p className="text-xs mt-0.5" style={{ color: 'rgba(255,255,255,0.4)' }}>Opens your email app with a pre-filled message</p>
              </div>
            </button>

            <button onClick={handleSMS}
              className="w-full flex items-center gap-4 px-4 py-3.5 transition-all text-left"
              style={{ background: '#141414', border: `1px solid ${BORDER}` }}
              onMouseEnter={e => e.currentTarget.style.borderColor = 'rgba(255,106,0,0.4)'}
              onMouseLeave={e => e.currentTarget.style.borderColor = BORDER}
            >
              <div className="w-9 h-9 flex items-center justify-center shrink-0"
                style={{ background: 'rgba(255,106,0,0.1)', border: `1px solid rgba(255,106,0,0.2)` }}>
                <MessageSquare className="w-4 h-4" style={{ color: ORANGE }} />
              </div>
              <div>
                <p className="font-barlow font-black text-sm text-white uppercase tracking-wide">Send via Text / SMS</p>
                <p className="text-xs mt-0.5" style={{ color: 'rgba(255,255,255,0.4)' }}>Opens your messages app with the portfolio link</p>
              </div>
            </button>

            <button onClick={handleCopy}
              className="w-full flex items-center gap-4 px-4 py-3.5 transition-all text-left"
              style={{ background: '#141414', border: `1px solid ${BORDER}` }}
              onMouseEnter={e => e.currentTarget.style.borderColor = 'rgba(255,106,0,0.4)'}
              onMouseLeave={e => e.currentTarget.style.borderColor = BORDER}
            >
              <div className="w-9 h-9 flex items-center justify-center shrink-0"
                style={{ background: copied ? 'rgba(0,200,100,0.1)' : 'rgba(255,106,0,0.1)', border: `1px solid ${copied ? 'rgba(0,200,100,0.2)' : 'rgba(255,106,0,0.2)'}` }}>
                {copied ? <Check className="w-4 h-4 text-green-400" /> : <Copy className="w-4 h-4" style={{ color: ORANGE }} />}
              </div>
              <div>
                <p className="font-barlow font-black text-sm text-white uppercase tracking-wide">
                  {copied ? 'Link Copied!' : 'Copy Link'}
                </p>
                <p className="text-xs mt-0.5 truncate max-w-[200px]" style={{ color: 'rgba(255,255,255,0.4)' }}>{portfolioUrl}</p>
              </div>
            </button>
          </div>
        </motion.div>
      </motion.div>
    </AnimatePresence>
  );
}

src/components/georgia/TeamCheckout.jsx

import { useState } from 'react';
import { base44 } from '@/api/base44Client';
import { Loader2 } from 'lucide-react';

const ORANGE = '#FF6A00';

export default function TeamCheckout({ teamSlug, teamName, onComplete }) {
  const [promoCode, setPromoCode] = useState('');
  const [promoError, setPromoError] = useState('');
  const [promoLoading, setPromoLoading] = useState(false);
  const [paymentLoading, setPaymentLoading] = useState(false);

  const handlePaymentCheckout = async () => {
    if (window.self !== window.top) {
      alert('Checkout is only available on the published app. Please open the live site to complete your purchase.');
      return;
    }
    setPaymentLoading(true);
    try {
      const baseUrl = window.location.origin + window.location.pathname;
      const response = await base44.functions.invoke('claimTeamCheckout', {
        team_slug: teamSlug,
        team_name: teamName,
        success_url: baseUrl,
        cancel_url: baseUrl,
      });
      if (response.data?.url) {
        window.location.href = response.data.url;
      } else {
        alert(response.data?.error || 'Failed to start checkout. Please try again.');
        setPaymentLoading(false);
      }
    } catch (e) {
      alert('Failed to start checkout. Please try again.');
      setPaymentLoading(false);
    }
  };

  const handlePromoSubmit = async () => {
    if (window.self !== window.top) {
      alert('Checkout is only available on the published app. Please open the live site to complete your purchase.');
      return;
    }
    setPromoLoading(true);
    setPromoError('');
    try {
      const baseUrl = window.location.origin + window.location.pathname;
      const response = await base44.functions.invoke('claimTeamCheckout', {
        team_slug: teamSlug,
        team_name: teamName,
        success_url: baseUrl,
        cancel_url: baseUrl,
        promo_code: promoCode.toUpperCase(),
      });
      
      if (response.data?.error) {
        setPromoError(response.data.error);
        setPromoCode('');
        setPromoLoading(false);
        return;
      }

      if (response.data?.url) {
        window.location.href = response.data.url;
      } else {
        setPromoError('Failed to start checkout');
        setPromoCode('');
        setPromoLoading(false);
      }
    } catch (e) {
      console.error('Promo error:', e);
      setPromoError('Error validating promo code');
      setPromoCode('');
      setPromoLoading(false);
    }
  };

  return (
    <div className="min-h-screen bg-black text-white flex items-center justify-center px-4">
      <div className="w-full max-w-md rounded-2xl border p-8 overflow-hidden" style={{ background: '#0a0a0a', borderColor: 'rgba(255,255,255,0.07)' }}>
        <h2 className="font-barlow font-black text-2xl text-white mb-2">Claim {teamName}</h2>
        <p className="text-gray-500 text-sm mb-6">Unlock full access to game film, analytics, and stats.</p>
        
        <div className="mb-6 pb-6 border-b" style={{ borderColor: 'rgba(255,255,255,0.07)' }}>
          <p className="text-xs font-bold text-gray-400 uppercase tracking-wide mb-3">Payment Option</p>
          <div className="rounded-xl p-4 mb-4" style={{ background: 'rgba(255,106,0,0.08)', border: `1px solid ${ORANGE}` }}>
            <div className="flex items-center justify-between">
              <span className="text-white font-semibold">One-Time Payment</span>
              <span className="font-barlow font-black text-2xl" style={{ color: ORANGE }}>$100</span>
            </div>
            <p className="text-xs text-gray-500 mt-2">Secure checkout powered by Stripe</p>
          </div>
          <button
            onClick={handlePaymentCheckout}
            disabled={paymentLoading || promoLoading}
            className="w-full py-3 rounded-xl font-black uppercase tracking-widest transition-all text-sm flex items-center justify-center gap-2 disabled:opacity-50"
            style={{ background: ORANGE, color: '#000' }}>
            {paymentLoading ? <Loader2 className="w-4 h-4 animate-spin" /> : 'Pay Now'}
          </button>
        </div>

        <div>
          <p className="text-xs font-bold text-gray-400 uppercase tracking-wide mb-3">Or Use Promo Code</p>
          <input
            type="text"
            value={promoCode}
            onChange={e => { setPromoCode(e.target.value.toUpperCase()); setPromoError(''); }}
            placeholder="ENTER PROMO CODE"
            disabled={promoLoading || paymentLoading}
            onKeyDown={e => e.key === 'Enter' && !promoLoading && !paymentLoading && handlePromoSubmit()}
            className="w-full px-4 py-3 rounded-xl bg-[#111] text-white text-sm placeholder-white/30 focus:outline-none mb-2 disabled:opacity-50"
            style={{ border: `1px solid ${promoError ? '#FF3B30' : 'rgba(255,255,255,0.09)'}` }}
          />
          {promoError && <p className="text-red-400 text-xs mb-2">{promoError}</p>}
          <button
            onClick={handlePromoSubmit}
            disabled={promoLoading || paymentLoading}
            className="w-full py-2.5 rounded-xl font-black uppercase tracking-widest transition-all text-sm flex items-center justify-center gap-2 disabled:opacity-50"
            style={{ background: ORANGE, color: '#000' }}>
            {promoLoading ? <Loader2 className="w-4 h-4 animate-spin" /> : 'Apply Code'}
          </button>
        </div>
      </div>
    </div>
  );
}