/* global React, Icon, Button, Eyebrow, Chip, Dashed, MOCK, AvatarBy, useProfileByHandle, useFollowCounts, useIsFollowing, useFollowers, useFollowing, followUser, unfollowUser, uploadAvatar, updateProfile, formatHandle, useUpcomingEvents, useCompletedMatchDetail */
// Profile (self + public) with member-gated stats

function ProfileScreen({ go, tier, viewingHandle, profile: signedInProfile }) {
  const isSelf = !viewingHandle || viewingHandle === MOCK.USER.handle;

  // For other users, fetch their real profile from Supabase by handle
  // (tolerates @-prefix). Falls back to the legacy mock lookup so
  // links from MOCK-driven screens (e.g. Social leaderboard) still work.
  const [realTarget, targetLoading] = useProfileByHandle(isSelf ? null : viewingHandle);
  const mockTarget = isSelf ? null : (MOCK.FRIENDS.find(f => f.handle === viewingHandle) || null);

  const user = isSelf
    ? MOCK.USER
    : (realTarget
        ? {
            id:           realTarget.id,
            handle:       realTarget.handle,
            name:         [realTarget.first_name, realTarget.last_name].filter(Boolean).join(' ') || realTarget.handle,
            avatar:       realTarget.avatar_url || null,
            avatar_url:   realTarget.avatar_url || null,
            sbx:          Number(realTarget.sbx) || 4.0,
            color:        'var(--moss)',
          }
        : (mockTarget || MOCK.FRIENDS[0]));

  const userInitial = (user.name || user.handle || '?').replace(/^@/, '').charAt(0).toUpperCase();
  const targetId    = (realTarget && realTarget.id) || (isSelf ? MOCK.USER.id : null);

  // Real follow state + counts
  const viewerId = signedInProfile && signedInProfile.id;
  const [counts] = useFollowCounts(targetId);
  const isFollowing = useIsFollowing(viewerId, targetId);
  const [followBusy, setFollowBusy] = React.useState(false);
  const [editing, setEditing] = React.useState(false);
  const [followListOpen, setFollowListOpen] = React.useState(null); // null | 'followers' | 'following'
  const [guestPassesOpen, setGuestPassesOpen] = React.useState(false);
  const [matchHistoryOpen, setMatchHistoryOpen] = React.useState(false);

  async function toggleFollow() {
    if (!viewerId || !targetId || viewerId === targetId || followBusy) return;
    setFollowBusy(true);
    try {
      if (isFollowing) await unfollowUser({ viewerId, targetId });
      else             await followUser({ viewerId, targetId });
    } catch (_) { /* swallow — RLS errors etc. */ }
    setFollowBusy(false);
  }

  const [youAreMember] = [tier === 'league' || tier === 'plus' || tier === 'stats'];

  if (!isSelf && targetLoading && !mockTarget) {
    return (
      <div style={{ background: 'var(--canvas)', minHeight: '100%', display: 'flex', alignItems: 'center', justifyContent: 'center', color: 'var(--forest)', opacity: 0.5 }}>
        Loading…
      </div>
    );
  }

  return (
    <div style={{ background: 'var(--canvas)', minHeight: '100%', paddingBottom: 120 }}>
      {/* Cover */}
      <div style={{
        height: 200,
        background: `linear-gradient(135deg, var(--forest-dark) 0%, var(--forest) 55%, var(--moss) 100%)`,
        position: 'relative', overflow: 'hidden',
      }}>
        <div className="grain" style={{ position: 'absolute', inset: 0, pointerEvents: 'none' }}/>
        <img src="assets/mascot-full-cream.svg" alt="" style={{
          position: 'absolute', right: -24, top: 24, width: 220, opacity: 0.16, transform: 'rotate(-6deg)',
        }}/>
        {!isSelf && (
          <button onClick={() => go({ screen: 'social' })} style={{
            position: 'absolute', top: 58, left: 16,
            width: 40, height: 40, borderRadius: 999,
            background: 'rgba(14,28,19,0.55)', backdropFilter: 'blur(12px)',
            WebkitBackdropFilter: 'blur(12px)',
            color: 'var(--cream)',
            display: 'flex', alignItems: 'center', justifyContent: 'center',
            border: '1px solid rgba(234,226,206,0.22)',
          }}>
            <Icon.ArrowLeft size={16}/>
          </button>
        )}
      </div>

      <div style={{ padding: '0 20px', marginTop: -50, position: 'relative' }}>
        <div style={{ display: 'flex', alignItems: 'flex-end', gap: 14 }}>
          <AvatarCircle
            url={user.avatar || user.avatar_url}
            initial={userInitial}
            isSelf={isSelf}
            userId={viewerId}
          />
          <div style={{ flex: 1, paddingBottom: 8 }}>
            {isSelf && MOCK.USER.foundingMember && (
              <div style={{
                display: 'inline-block',
                padding: '4px 10px', borderRadius: 999,
                background: 'var(--forest)', color: 'var(--cream)',
                fontSize: 9, fontWeight: 800, letterSpacing: '0.16em', textTransform: 'uppercase',
                boxShadow: 'var(--shadow-sm)', marginBottom: 6,
              }}>⛳ Founding</div>
            )}
          </div>
          {!isSelf && viewerId && targetId && viewerId !== targetId && (
            <Button
              variant={isFollowing ? 'outline' : 'forest'}
              size="sm"
              onClick={toggleFollow}
              disabled={followBusy || isFollowing === null}
              style={{ marginBottom: 8 }}
            >
              {isFollowing === null ? '…' : (isFollowing ? 'Following' : 'Follow')}
            </Button>
          )}
          {isSelf && (
            <Button
              variant="outline"
              size="sm"
              onClick={() => setEditing(true)}
              style={{ marginBottom: 8 }}
            >
              Edit profile
            </Button>
          )}
        </div>

        <div style={{ marginTop: 10 }}>
          <div style={{ fontFamily: 'var(--font-display)', fontSize: 28, lineHeight: 1, color: 'var(--forest)' }}>
            {user.name}
          </div>
          <div style={{ fontSize: 13, opacity: 0.6, marginTop: 4, fontWeight: 600 }}>{formatHandle(user.handle)}</div>
          {isSelf && (
            <div className="caption-serif" style={{ fontSize: 15, marginTop: 8, color: 'var(--ink)', opacity: 0.85 }}>
              "{user.bio}"
            </div>
          )}
          {/* Follow stats — works for self + others */}
          <div style={{ display: 'flex', gap: 18, marginTop: 12, alignItems: 'baseline' }}>
            <FollowStat label="Followers" value={counts.followers} onClick={() => setFollowListOpen('followers')}/>
            <FollowStat label="Following" value={counts.following} onClick={() => setFollowListOpen('following')}/>
          </div>
          <div style={{ display: 'flex', gap: 14, marginTop: 12, flexWrap: 'wrap' }}>
            <Mini label="Home" value={isSelf ? user.homeCourse : 'Melreese'}/>
            <Mini label="Joined" value={isSelf ? user.joined : 'Jan 2026'}/>
            <Mini label="Events attended" value={isSelf ? user.eventsPlayed : '—'}/>
          </div>
        </div>
      </div>

      {/* Public stats shown for self. For other users — gated by whether THEY'RE a member. */}
      <div style={{ padding: '22px 16px 0' }}>
        {isSelf ? (
          <SelfStatsBlock user={user} go={go} tier={tier}/>
        ) : (
          <PublicStatsBlock user={user} viewerIsMember={youAreMember} go={go}/>
        )}
      </div>

      {/* Match history */}
      <div style={{ padding: '20px 16px 0' }}>
        <div style={{ fontSize: 11, fontFamily: 'var(--font-mono)', color: 'var(--forest)', opacity: 0.55, letterSpacing: '0.12em', textTransform: 'uppercase', marginBottom: 10 }}>Match history</div>
        <div className="card" style={{ overflow: 'hidden' }}>
          {MOCK.HISTORY.length === 0 ? (
            <div style={{ padding: '24px 14px', textAlign: 'center', opacity: 0.4 }}>
              <div style={{ fontSize: 14 }}>No matches yet.</div>
            </div>
          ) : (
            <>
              {MOCK.HISTORY.slice(0, 4).map((r, i) => {
                const isW = r.result === 'W', isL = r.result === 'L';
                const badgeStyle = isW
                  ? { background: 'var(--forest)', color: 'var(--cream)', border: 'none' }
                  : isL
                  ? { background: 'var(--cream)', color: 'var(--forest)', border: 'none' }
                  : { background: 'var(--paper)', color: 'var(--forest)', border: '1px solid rgba(28,73,42,0.25)' };
                const marginColor = isW ? 'var(--forest)' : isL ? 'var(--forest)' : '#8A6A4A';
                const marginOpacity = isL ? 0.55 : 1;
                return (
                  <div key={r.id} style={{
                    display: 'flex', alignItems: 'center', gap: 12,
                    padding: '13px 14px',
                    borderBottom: '1px solid rgba(14,28,19,0.05)',
                  }}>
                    <div style={{ fontFamily: 'var(--font-display)', fontSize: 13, color: 'var(--forest)', width: 30, opacity: 0.7 }}>{r.week}</div>
                    <div style={{
                      width: 24, height: 24, borderRadius: 6,
                      display: 'flex', alignItems: 'center', justifyContent: 'center',
                      fontFamily: 'var(--font-display)', fontSize: 13,
                      ...badgeStyle,
                    }}>{r.result}</div>
                    <div style={{ flex: 1, fontSize: 12 }}>vs {r.opp}</div>
                    <div style={{ fontFamily: 'var(--font-display)', fontSize: 15, color: marginColor, opacity: marginOpacity }}>{r.margin}</div>
                  </div>
                );
              })}
              {MOCK.HISTORY.length > 4 && (
                <button onClick={() => setMatchHistoryOpen(true)} style={{
                  width: '100%', padding: '13px 14px',
                  background: 'transparent', border: 'none',
                  color: 'var(--forest)', fontSize: 12, fontWeight: 700,
                  cursor: 'pointer', textAlign: 'center',
                  display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 6,
                }}>
                  View all {MOCK.HISTORY.length} matches <Icon.ArrowRight size={12}/>
                </button>
              )}
            </>
          )}
        </div>
      </div>

      {isSelf && (
        <div style={{ padding: '22px 16px 0' }}>
          <div className="card" style={{ padding: 4 }}>
            <MenuRow label="Manage my membership" onClick={() => go({ screen: 'membership' })}/>
            <MenuRow label="Guest Passes" onClick={() => setGuestPassesOpen(true)}/>
            <MenuRow label="Sign out" last/>
          </div>
        </div>
      )}

      {editing && isSelf && (
        <EditProfileSheet
          profile={signedInProfile}
          onClose={() => setEditing(false)}
        />
      )}

      {followListOpen && (
        <FollowListSheet
          userId={targetId}
          mode={followListOpen}
          viewerId={viewerId}
          go={go}
          onClose={() => setFollowListOpen(null)}
        />
      )}

      {guestPassesOpen && (
        <GuestPassesSheet
          tier={tier}
          go={go}
          onClose={() => setGuestPassesOpen(false)}
        />
      )}

      {matchHistoryOpen && (
        <MatchHistorySheet
          history={MOCK.HISTORY}
          onClose={() => setMatchHistoryOpen(false)}
        />
      )}
    </div>
  );
}

function Mini({ label, value }) {
  return (
    <div>
      <div style={{ fontSize: 9, fontFamily: 'var(--font-mono)', letterSpacing: '0.12em', textTransform: 'uppercase', opacity: 0.5 }}>{label}</div>
      <div style={{ fontSize: 13, fontWeight: 700, color: 'var(--forest)', marginTop: 3 }}>{value}</div>
    </div>
  );
}

function SelfStatsBlock({ user, go, tier }) {
  const isStatsEnabled = tier !== 'walkup';
  if (!isStatsEnabled) {
    return (
      <div style={{
        background: 'var(--paper)', borderRadius: 18, padding: 18,
        border: '1px dashed rgba(14,28,19,0.15)', textAlign: 'center',
      }}>
        <Icon.Lock size={20} color="var(--forest)"/>
        <div style={{ fontFamily: 'var(--font-display)', fontSize: 18, marginTop: 8, color: 'var(--forest)' }}>Rating locked</div>
        <div style={{ fontSize: 12, opacity: 0.7, marginTop: 4, marginBottom: 14 }}>
          Walk-up matches don't count toward your Sandbox Rating. Unlock with Stats or League.
        </div>
        <Button variant="forest" size="sm" onClick={() => go({ screen: 'membership' })}>See plans</Button>
      </div>
    );
  }
  const trend = user.sbxTrend;
  return (
    <div style={{
      background: `linear-gradient(135deg, var(--forest-dark) 0%, var(--forest) 55%, var(--moss) 100%)`,
      color: 'var(--cream)',
      borderRadius: 'var(--radius-card-lg)', padding: 20, position: 'relative', overflow: 'hidden',
      boxShadow: 'var(--shadow-md)',
    }}>
      <div className="grain" style={{ position: 'absolute', inset: 0, pointerEvents: 'none' }}/>
      <div style={{ position: 'absolute', right: 16, top: 16, fontFamily: 'var(--font-mono)', fontSize: 10, opacity: 0.6, letterSpacing: '0.24em', fontWeight: 700 }}>SBX · v1</div>
      <div style={{ fontSize: 10, fontFamily: 'var(--font-mono)', opacity: 0.65, letterSpacing: '0.14em', textTransform: 'uppercase', position: 'relative' }}>Sandbox Rating™</div>
      <div style={{ display: 'flex', alignItems: 'flex-end', justifyContent: 'space-between', marginTop: 10, position: 'relative' }}>
        <div>
          <div style={{ display: 'flex', alignItems: 'baseline', gap: 8 }}>
            <span style={{ fontFamily: 'var(--font-display)', fontSize: 56, lineHeight: 0.85, letterSpacing: '-0.03em' }}>{user.sbx.toFixed(3)}</span>
            <span style={{ fontSize: 12, color: user.sbxDelta >= 0 ? '#B8E0A4' : 'var(--clay)', fontWeight: 700 }}>
              {user.sbxDelta >= 0 ? '+' : ''}{user.sbxDelta.toFixed(3)}
            </span>
          </div>
          <div style={{ fontSize: 11, opacity: 0.65, marginTop: 4 }}>Top {100 - user.sbxPercentile}% · #{user.sbxGlobalRank.toLocaleString()} global</div>
        </div>
        {/* mini sparkline */}
        <svg viewBox="0 0 80 36" style={{ width: 90, height: 40 }} preserveAspectRatio="none">
          {(() => {
            const lo = Math.min(...trend), hi = Math.max(...trend);
            const pts = trend.map((v, i) => [(i / (trend.length - 1)) * 78 + 1, 34 - ((v - lo) / (hi - lo || 1)) * 30]);
            const d = pts.map((p, i) => (i ? 'L' : 'M') + p[0].toFixed(1) + ',' + p[1].toFixed(1)).join(' ');
            return (
              <>
                <path d={d} fill="none" stroke="var(--clay)" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round"/>
                <circle cx={pts[pts.length - 1][0]} cy={pts[pts.length - 1][1]} r="2.5" fill="var(--clay)"/>
              </>
            );
          })()}
        </svg>
      </div>
      <div style={{ height: 1, background: 'rgba(234,226,206,0.14)', margin: '16px 0 12px' }}/>
      <div style={{ display: 'flex', gap: 14, fontSize: 11, fontFamily: 'var(--font-mono)', opacity: 0.7, letterSpacing: '0.06em' }}>
        <span><strong>{user.matchesW}–{user.matchesL}–{user.matchesH}</strong> RECORD</span>
        <span>·</span>
        <span>REL. {Math.round(user.sbxReliability * 100)}%</span>
      </div>
      <Button variant="outlineCream" size="sm" full style={{ marginTop: 12 }} onClick={() => go({ screen: 'stats' })}>
        Full dashboard <Icon.ArrowRight size={14}/>
      </Button>
    </div>
  );
}

function PublicStatsBlock({ user, viewerIsMember, go }) {
  // If viewer is member, show full. If not, show gated teaser.
  if (viewerIsMember) {
    const rel = user.rel ?? 0.8;
    const reliable = rel >= 0.5;
    return (
      <div style={{
        background: `linear-gradient(135deg, var(--forest-dark) 0%, var(--forest) 55%, var(--moss) 100%)`,
        color: 'var(--cream)',
        borderRadius: 'var(--radius-card-lg)', padding: 20, position: 'relative', overflow: 'hidden',
        boxShadow: 'var(--shadow-md)',
      }}>
        <div className="grain" style={{ position: 'absolute', inset: 0, pointerEvents: 'none' }}/>
        <div style={{ position: 'absolute', right: 16, top: 16, fontFamily: 'var(--font-mono)', fontSize: 10, opacity: 0.6, letterSpacing: '0.24em', fontWeight: 700 }}>SBX · v1</div>
        <div style={{ fontSize: 10, fontFamily: 'var(--font-mono)', opacity: 0.65, letterSpacing: '0.14em', textTransform: 'uppercase', position: 'relative' }}>Sandbox Rating™</div>
        <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginTop: 6 }}>
          <div style={{
            fontFamily: 'var(--font-display)', fontSize: 52, lineHeight: 0.85,
            letterSpacing: '-0.03em',
            borderBottom: reliable ? 'none' : '3px dotted rgba(234,226,206,0.5)',
          }}>{user.sbx?.toFixed(3) ?? '—'}</div>
          <div style={{ display: 'flex', gap: 10 }}>
            <Mini2 label="Record" value="9–5–1"/>
            <Mini2 label="Events attended" value={user.eventsPlayed || 0}/>
          </div>
        </div>
        <div style={{ fontSize: 11, opacity: 0.65, marginTop: 6 }}>
          {reliable ? 'Rated · reliability ' : 'Provisional · reliability '}{Math.round(rel * 100)}%
        </div>
      </div>
    );
  }
  return (
    <div style={{
      background: 'var(--paper)', border: '1px dashed rgba(14,28,19,0.18)',
      borderRadius: 18, padding: 18, textAlign: 'center',
    }}>
      <Icon.Lock size={20} color="var(--forest)"/>
      <div style={{ fontFamily: 'var(--font-display)', fontSize: 18, marginTop: 8, color: 'var(--forest)' }}>Ratings are member-only</div>
      <div style={{ fontSize: 12, opacity: 0.7, marginTop: 4, marginBottom: 14 }}>
        Members see each other's Sandbox Rating, trend and full head-to-head.
      </div>
      <Button variant="forest" size="sm" onClick={() => go({ screen: 'membership' })}>Become a member</Button>
    </div>
  );
}

function Mini2({ label, value }) {
  return (
    <div style={{ textAlign: 'right' }}>
      <div style={{ fontSize: 9, fontFamily: 'var(--font-mono)', letterSpacing: '0.12em', textTransform: 'uppercase', opacity: 0.6 }}>{label}</div>
      <div style={{ fontFamily: 'var(--font-display)', fontSize: 20, marginTop: 3 }}>{value}</div>
    </div>
  );
}

function MenuRow({ label, onClick, last }) {
  return (
    <button onClick={onClick} style={{
      display: 'flex', alignItems: 'center', width: '100%',
      padding: '15px 14px', borderBottom: last ? 'none' : '1px solid rgba(14,28,19,0.05)',
      fontSize: 13, fontWeight: 600, color: 'var(--ink)', textAlign: 'left',
    }}>
      <span style={{ flex: 1 }}>{label}</span>
      <Icon.Chevron dir="right" size={12} color="var(--forest)"/>
    </button>
  );
}

// ─── Avatar circle with optional upload UI for self ───────────────────
function AvatarCircle({ url, initial, isSelf, userId }) {
  const fileRef = React.useRef(null);
  const [busy, setBusy]       = React.useState(false);
  const [err, setErr]         = React.useState('');
  const [pending, setPending] = React.useState(null); // file to crop

  function onPick(e) {
    const file = e.target.files && e.target.files[0];
    e.target.value = ''; // allow reselecting same file later
    if (!file || !userId) return;
    if (file.size > 20 * 1024 * 1024) {
      setErr('Image must be under 20 MB.');
      return;
    }
    setErr('');
    setPending(file);
  }

  async function onCropConfirm(croppedFile) {
    setBusy(true);
    try {
      await uploadAvatar({ userId, file: croppedFile });
      setPending(null);
    } catch (e) {
      setErr(e.message || 'Upload failed.');
    }
    setBusy(false);
  }

  const circle = (
    <div style={{ position: 'relative', width: 96, height: 96 }}>
      {/* Image clipped to circle */}
      <div style={{
        width: 96, height: 96, borderRadius: 999,
        background: '#5A7B4A',
        border: '4px solid var(--cream)',
        display: 'flex', alignItems: 'center', justifyContent: 'center',
        color: 'var(--cream)', fontFamily: 'var(--font-display)', fontSize: 38,
        overflow: 'hidden',
        boxShadow: 'var(--shadow-sm)',
        boxSizing: 'border-box',
      }}>
        {url ? (
          <img src={url} alt="" style={{ width: '100%', height: '100%', objectFit: 'cover' }}/>
        ) : (
          initial
        )}
      </div>
      {/* Badge sits OVER the picture, just inside the bottom-right edge */}
      {isSelf && (
        <div style={{
          position: 'absolute', right: 4, bottom: 4,
          width: 30, height: 30, borderRadius: 999,
          background: 'var(--forest)', color: 'var(--cream)',
          display: 'flex', alignItems: 'center', justifyContent: 'center',
          border: '2px solid var(--cream)',
          fontSize: 16, fontWeight: 800, lineHeight: 1,
          boxShadow: 'var(--shadow-sm)',
          pointerEvents: 'none',
        }}>
          {busy ? '…' : '+'}
        </div>
      )}
    </div>
  );

  if (!isSelf) {
    const [zoomed, setZoomed] = React.useState(false);
    return (
      <>
        <button type="button" onClick={() => setZoomed(true)} style={{ padding: 0, background: 'transparent', border: 'none', cursor: 'pointer' }}>
          {circle}
        </button>
        {zoomed && (
          <div
            onClick={() => setZoomed(false)}
            style={{
              position: 'fixed', inset: 0, zIndex: 9999,
              background: 'rgba(0,0,0,0.85)',
              display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center',
              gap: 16,
            }}
          >
            <div style={{
              width: 220, height: 220, borderRadius: 999,
              background: '#5A7B4A',
              border: '4px solid rgba(255,255,255,0.25)',
              overflow: 'hidden',
              display: 'flex', alignItems: 'center', justifyContent: 'center',
              color: 'var(--cream)', fontFamily: 'var(--font-display)', fontSize: 80,
            }}>
              {url ? <img src={url} alt="" style={{ width: '100%', height: '100%', objectFit: 'cover' }}/> : initial}
            </div>
            <div style={{ fontSize: 11, fontFamily: 'var(--font-mono)', color: 'rgba(255,255,255,0.4)', letterSpacing: '0.12em', textTransform: 'uppercase' }}>TAP TO CLOSE</div>
          </div>
        )}
      </>
    );
  }

  return (
    <>
      <button
        type="button"
        onClick={() => fileRef.current && fileRef.current.click()}
        disabled={busy}
        aria-label="Change profile photo"
        style={{ padding: 0, background: 'transparent', border: 'none', cursor: busy ? 'wait' : 'pointer' }}
      >
        {circle}
      </button>
      <input
        ref={fileRef}
        type="file"
        accept="image/*"
        onChange={onPick}
        style={{ display: 'none' }}
      />
      {err && (
        <div style={{ fontSize: 11, color: 'var(--loss)', marginTop: 4 }}>{err}</div>
      )}
      {pending && (
        <AvatarCropper
          file={pending}
          busy={busy}
          onCancel={() => { if (!busy) setPending(null); }}
          onConfirm={onCropConfirm}
        />
      )}
    </>
  );
}

// ─── Avatar cropper (modal) ──────────────────────────────────────────
// Lets the user pan + zoom the picked image inside a circular crop
// window, then exports a 512×512 jpg of exactly what's in the circle.
function AvatarCropper({ file, busy, onCancel, onConfirm }) {
  const FRAME = 280;             // circle window size on screen
  const OUT   = 512;             // exported image size

  const [src,   setSrc]    = React.useState(null);
  const [imgW,  setImgW]   = React.useState(0);
  const [imgH,  setImgH]   = React.useState(0);
  const [zoom,  setZoom]   = React.useState(1);   // multiplier on top of "cover" base
  const [pos,   setPos]    = React.useState({ x: 0, y: 0 }); // image-center offset from frame-center, screen px
  const [drag,  setDrag]   = React.useState(null);
  const [err,   setErr]    = React.useState('');
  const imgRef = React.useRef(null);

  // Read the file into a data URL once
  React.useEffect(() => {
    const reader = new FileReader();
    reader.onload = (e) => setSrc(e.target.result);
    reader.onerror = () => setErr('Could not read image.');
    reader.readAsDataURL(file);
  }, [file]);

  function onImgLoad(e) {
    setImgW(e.target.naturalWidth);
    setImgH(e.target.naturalHeight);
    setZoom(1);
    setPos({ x: 0, y: 0 });
  }

  // Cover-the-frame base scale × user zoom
  const baseScale       = (imgW && imgH) ? Math.max(FRAME / imgW, FRAME / imgH) : 1;
  const effectiveScale  = baseScale * zoom;
  const renderedW       = imgW * effectiveScale;
  const renderedH       = imgH * effectiveScale;

  // Drag-to-pan (mouse + touch) with global listeners while dragging
  function pointOf(e) {
    const t = e.touches && e.touches[0];
    return { x: (t || e).clientX, y: (t || e).clientY };
  }
  function startDrag(e) {
    const p = pointOf(e);
    setDrag({ x: p.x - pos.x, y: p.y - pos.y });
  }
  React.useEffect(() => {
    if (!drag) return;
    const move = (e) => {
      e.preventDefault();
      const p = pointOf(e);
      setPos({ x: p.x - drag.x, y: p.y - drag.y });
    };
    const end = () => setDrag(null);
    window.addEventListener('mousemove', move);
    window.addEventListener('mouseup', end);
    window.addEventListener('touchmove', move, { passive: false });
    window.addEventListener('touchend', end);
    return () => {
      window.removeEventListener('mousemove', move);
      window.removeEventListener('mouseup', end);
      window.removeEventListener('touchmove', move);
      window.removeEventListener('touchend', end);
    };
  }, [drag]);

  async function confirm() {
    if (!imgRef.current || !imgW || !imgH) return;
    setErr('');
    try {
      const canvas = document.createElement('canvas');
      canvas.width = OUT; canvas.height = OUT;
      const ctx = canvas.getContext('2d');

      // Source rect (in original image pixels) that ends up in the frame.
      // Image center on screen = (FRAME/2 + pos.x, FRAME/2 + pos.y)
      // Image top-left on screen = (FRAME/2 + pos.x - renderedW/2, ...)
      // Source x at frame's screen-x=0 = (renderedW/2 - FRAME/2 - pos.x) / effectiveScale
      const sx = (renderedW / 2 - FRAME / 2 - pos.x) / effectiveScale;
      const sy = (renderedH / 2 - FRAME / 2 - pos.y) / effectiveScale;
      const sw = FRAME / effectiveScale;
      const sh = FRAME / effectiveScale;

      ctx.drawImage(imgRef.current, sx, sy, sw, sh, 0, 0, OUT, OUT);

      const blob = await new Promise(res => canvas.toBlob(res, 'image/jpeg', 0.92));
      if (!blob) throw new Error('Could not encode image.');
      const baseName = (file.name || 'avatar').replace(/\.[^.]+$/, '') || 'avatar';
      const cropped = new File([blob], baseName + '.jpg', { type: 'image/jpeg' });
      await onConfirm(cropped);
    } catch (e) {
      setErr(e.message || 'Crop failed.');
    }
  }

  return (
    <div
      style={{
        position: 'fixed', inset: 0,
        background: 'rgba(14,28,19,0.88)', backdropFilter: 'blur(8px)',
        WebkitBackdropFilter: 'blur(8px)',
        display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center',
        zIndex: 1000, padding: 20,
      }}
      onClick={(e) => { if (e.target === e.currentTarget && !busy) onCancel(); }}
    >
      <div style={{ color: 'var(--cream)', fontFamily: 'var(--font-display)', fontSize: 24, marginBottom: 4 }}>
        Crop your photo
      </div>
      <div style={{ color: 'rgba(234,226,206,0.55)', fontSize: 11, fontFamily: 'var(--font-mono)', letterSpacing: '0.08em', textTransform: 'uppercase', marginBottom: 18 }}>
        Drag to reposition · slider to zoom
      </div>

      <div
        style={{
          width: FRAME, height: FRAME, position: 'relative',
          overflow: 'hidden', borderRadius: 999,
          background: '#000',
          boxShadow: '0 0 0 4px var(--cream), 0 24px 60px rgba(0,0,0,0.5)',
          touchAction: 'none', cursor: drag ? 'grabbing' : 'grab',
          userSelect: 'none',
        }}
        onMouseDown={startDrag}
        onTouchStart={startDrag}
      >
        {src && (
          <img
            ref={imgRef}
            src={src}
            onLoad={onImgLoad}
            draggable={false}
            alt=""
            style={{
              position: 'absolute',
              left: '50%', top: '50%',
              width: renderedW || 'auto',
              height: renderedH || 'auto',
              maxWidth: 'none', maxHeight: 'none',
              transform: `translate(${-renderedW / 2 + pos.x}px, ${-renderedH / 2 + pos.y}px)`,
              userSelect: 'none', pointerEvents: 'none',
            }}
          />
        )}
      </div>

      <div style={{ width: FRAME, marginTop: 18, display: 'flex', alignItems: 'center', gap: 12 }}>
        <span style={{ color: 'var(--cream)', fontSize: 14, opacity: 0.6, fontWeight: 800 }}>−</span>
        <input
          type="range" min="1" max="4" step="0.02"
          value={zoom}
          onChange={(e) => setZoom(Number(e.target.value))}
          style={{ flex: 1, accentColor: 'var(--cream)' }}
        />
        <span style={{ color: 'var(--cream)', fontSize: 14, opacity: 0.6, fontWeight: 800 }}>+</span>
      </div>

      {err && (
        <div style={{ color: '#E7B8A7', fontSize: 12, marginTop: 10 }}>{err}</div>
      )}

      <div style={{ display: 'flex', gap: 12, marginTop: 22 }}>
        <Button variant="outlineCream" onClick={onCancel} disabled={busy}>Cancel</Button>
        <Button variant="primary" onClick={confirm} disabled={busy || !src}>
          {busy ? 'Uploading…' : 'Save photo'}
        </Button>
      </div>
    </div>
  );
}

// ─── Follower / Following stat (compact display) ──────────────────────
function FollowStat({ label, value, onClick }) {
  return (
    <button onClick={onClick} style={{ padding: 0, background: 'transparent', border: 'none', cursor: 'pointer', textAlign: 'left' }}>
      <div style={{ fontFamily: 'var(--font-display)', fontSize: 20, color: 'var(--forest)', lineHeight: 1, letterSpacing: '-0.01em' }}>
        {value}
      </div>
      <div style={{ fontSize: 9, fontFamily: 'var(--font-mono)', color: 'var(--forest)', opacity: 0.55, letterSpacing: '0.12em', textTransform: 'uppercase', marginTop: 4 }}>
        {label}
      </div>
    </button>
  );
}

// ─── Guest Passes Sheet ──────────────────────────────────────────────
function GuestPassesSheet({ tier, go, onClose }) {
  const isPlus = tier === 'plus';
  const isLeague = tier === 'league';
  const totalPasses = isLeague ? 2 : 0; // plus = unlimited, tracked differently

  const now = new Date();
  const currentMonth = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}`;
  const nextFirst = new Date(now.getFullYear(), now.getMonth() + 1, 1);
  const resetStr = nextFirst.toLocaleDateString('en-US', { month: 'long', day: 'numeric' });

  // Invites: [{ id, contact, eventId, eventName, status: 'pending'|'used', sentAt, month }]
  const [invites, setInvites] = React.useState(() => {
    try {
      const stored = JSON.parse(localStorage.getItem('spp_guest_invites') || '[]');
      return stored.filter(inv => inv.month === currentMonth);
    } catch { return []; }
  });

  const [sendStep, setSendStep] = React.useState(null); // null | 'event' | 'contact'
  const [selectedEvent, setSelectedEvent] = React.useState(null);
  const [contactInput, setContactInput] = React.useState('');
  const [upcoming] = useUpcomingEvents(6);

  function saveInvites(newInvites) {
    try {
      const all = JSON.parse(localStorage.getItem('spp_guest_invites') || '[]');
      const otherMonths = all.filter(inv => inv.month !== currentMonth);
      localStorage.setItem('spp_guest_invites', JSON.stringify([...otherMonths, ...newInvites]));
    } catch {}
    setInvites(newInvites);
  }

  function sendInvite() {
    if (!selectedEvent || !contactInput.trim()) return;
    const newInvite = {
      id: Date.now().toString(),
      contact: contactInput.trim(),
      eventId: selectedEvent.id,
      eventName: `${selectedEvent.courseShort || selectedEvent.title} · ${selectedEvent.date}`,
      status: 'pending',
      sentAt: now.toISOString().slice(0, 10),
      month: currentMonth,
    };
    saveInvites([...invites, newInvite]);
    setContactInput('');
    setSelectedEvent(null);
    setSendStep(null);
  }

  function cancelInvite(id) {
    saveInvites(invites.filter(inv => inv.id !== id));
  }

  const canSendMore = isPlus || (isLeague && invites.length < totalPasses);

  return (
    <div style={{ position: 'fixed', inset: 0, zIndex: 200, display: 'flex', flexDirection: 'column', justifyContent: 'flex-end' }}>
      <div onClick={onClose} style={{ position: 'absolute', inset: 0, background: 'rgba(0,0,0,0.55)' }}/>
      <div style={{ position: 'relative', background: 'var(--forest-dark)', borderRadius: '24px 24px 0 0', maxHeight: '85vh', display: 'flex', flexDirection: 'column', overflow: 'hidden' }}>
        <div className="grain" style={{ position: 'absolute', inset: 0, pointerEvents: 'none' }}/>
        <div style={{ width: 40, height: 4, borderRadius: 999, background: 'rgba(234,226,206,0.2)', margin: '14px auto 0', position: 'relative' }}/>
        <div style={{ padding: '16px 22px 14px', display: 'flex', alignItems: 'center', justifyContent: 'space-between', position: 'relative', borderBottom: '1px solid rgba(234,226,206,0.1)' }}>
          <div>
            <div style={{ fontFamily: 'var(--font-display)', fontSize: 26, color: 'var(--cream)', lineHeight: 1, letterSpacing: '-0.01em' }}>Guest Passes</div>
            <div style={{ fontSize: 10, fontFamily: 'var(--font-mono)', color: 'var(--cream)', opacity: 0.5, marginTop: 4, letterSpacing: '0.1em' }}>
              {isPlus ? 'UNLIMITED · 1 PER EVENT' : isLeague ? `${invites.length} OF ${totalPasses} USED · RESETS ${resetStr.toUpperCase()}` : ''}
            </div>
          </div>
          <img src="assets/mascot-full-cream.svg" alt="" style={{ width: 64, opacity: 0.25, marginRight: -8 }}/>
        </div>

        <div style={{ overflowY: 'auto', flex: 1, padding: '20px 16px 32px', position: 'relative' }}>
          {!isPlus && !isLeague ? (
            <div style={{ textAlign: 'center', padding: '32px 0', color: 'var(--cream)', opacity: 0.55 }}>
              <div style={{ fontFamily: 'var(--font-display)', fontSize: 20, marginBottom: 8 }}>No guest passes on your plan.</div>
              <div style={{ fontSize: 13 }}>Upgrade to League or Plus to bring a friend.</div>
              <button onClick={() => { onClose(); go({ screen: 'membership' }); }} style={{
                marginTop: 18, padding: '10px 20px', borderRadius: 999,
                background: 'rgba(234,226,206,0.15)', border: '1px solid rgba(234,226,206,0.3)',
                color: 'var(--cream)', fontSize: 13, fontWeight: 700, cursor: 'pointer',
              }}>View plans</button>
            </div>
          ) : (
            <>
              {/* Send new pass button */}
              {canSendMore && sendStep === null && (
                <button onClick={() => setSendStep('event')} style={{
                  width: '100%', padding: '18px 20px', borderRadius: 18,
                  background: 'rgba(234,226,206,0.12)', border: '1.5px solid rgba(234,226,206,0.28)',
                  color: 'var(--cream)', cursor: 'pointer', marginBottom: 14,
                  display: 'flex', alignItems: 'center', gap: 14,
                }}>
                  <div style={{ width: 44, height: 44, borderRadius: 999, background: 'rgba(234,226,206,0.15)', display: 'flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0, fontSize: 24, lineHeight: 1 }}>+</div>
                  <div style={{ textAlign: 'left' }}>
                    <div style={{ fontFamily: 'var(--font-display)', fontSize: 18, lineHeight: 1, marginBottom: 4 }}>
                      {isPlus ? 'Send a guest pass' : `Send a guest pass · ${totalPasses - invites.length} left`}
                    </div>
                    <div style={{ fontSize: 10, fontFamily: 'var(--font-mono)', opacity: 0.5, letterSpacing: '0.08em' }}>
                      INVITE VIA EMAIL OR PHONE
                    </div>
                  </div>
                </button>
              )}

              {/* Existing invites */}
              {invites.length > 0 && (
                <div style={{ display: 'flex', flexDirection: 'column', gap: 12, marginBottom: 14 }}>
                  {invites.map(invite => (
                    <div key={invite.id} style={{
                      borderRadius: 18, overflow: 'hidden',
                      border: invite.status === 'used' ? '1px solid rgba(234,226,206,0.1)' : '1px solid rgba(234,226,206,0.28)',
                      opacity: invite.status === 'used' ? 0.5 : 1,
                    }}>
                      <div style={{
                        display: 'flex', alignItems: 'stretch',
                        background: invite.status === 'used' ? 'rgba(234,226,206,0.06)' : 'rgba(234,226,206,0.12)',
                      }}>
                        <div style={{ flex: 1, padding: '16px 0 16px 18px' }}>
                          <div style={{ fontSize: 8, fontFamily: 'var(--font-mono)', color: 'var(--cream)', opacity: 0.5, letterSpacing: '0.2em' }}>SANDBOX PITCH + PUTT</div>
                          <div style={{ fontFamily: 'var(--font-display)', fontSize: 20, color: 'var(--cream)', lineHeight: 0.95, marginTop: 6, letterSpacing: '-0.01em' }}>
                            {invite.status === 'used' ? 'Used' : 'Pending invite'}
                          </div>
                          <div style={{ fontSize: 11, fontFamily: 'var(--font-mono)', color: 'var(--cream)', opacity: 0.65, marginTop: 6, letterSpacing: '0.04em' }}>
                            {invite.contact}
                          </div>
                          <div style={{ fontSize: 10, fontFamily: 'var(--font-mono)', color: 'var(--cream)', opacity: 0.4, marginTop: 3, letterSpacing: '0.04em' }}>
                            {invite.eventName}
                          </div>
                          {invite.status === 'pending' && (
                            <button onClick={() => cancelInvite(invite.id)} style={{
                              marginTop: 8, padding: '4px 10px', borderRadius: 999,
                              background: 'rgba(234,226,206,0.1)', border: '1px solid rgba(234,226,206,0.2)',
                              color: 'var(--cream)', fontSize: 9, fontWeight: 700, letterSpacing: '0.08em',
                              cursor: 'pointer', textTransform: 'uppercase',
                            }}>Cancel invite</button>
                          )}
                        </div>
                        <div style={{ width: 0, borderLeft: '1.5px dashed rgba(234,226,206,0.2)', margin: '14px 0' }}/>
                        <div style={{ width: 80, display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
                          <img src="assets/mascot-full-cream.svg" alt="" style={{ width: 48, opacity: invite.status === 'used' ? 0.15 : 0.55 }}/>
                        </div>
                      </div>
                    </div>
                  ))}
                </div>
              )}

              {invites.length === 0 && (
                <div style={{ textAlign: 'center', color: 'var(--cream)', opacity: 0.3, fontSize: 11, fontFamily: 'var(--font-mono)', letterSpacing: '0.06em', marginTop: 4 }}>
                  {isPlus ? `UNLIMITED · 1 PER EVENT · RESETS ${resetStr.toUpperCase()}` : `${totalPasses} PASSES THIS MONTH · RESETS ${resetStr.toUpperCase()}`}
                </div>
              )}

              {!canSendMore && invites.length > 0 && (
                <div style={{ textAlign: 'center', color: 'var(--cream)', opacity: 0.35, fontSize: 11, fontFamily: 'var(--font-mono)', letterSpacing: '0.06em', marginTop: 8 }}>
                  ALL PASSES USED · RESETS {resetStr.toUpperCase()}
                </div>
              )}
            </>
          )}
        </div>
      </div>

      {/* Event picker sub-sheet */}
      {sendStep === 'event' && (
        <div style={{ position: 'fixed', inset: 0, zIndex: 300, display: 'flex', flexDirection: 'column', justifyContent: 'flex-end' }}>
          <div onClick={() => setSendStep(null)} style={{ position: 'absolute', inset: 0, background: 'rgba(0,0,0,0.4)' }}/>
          <div style={{ position: 'relative', background: 'var(--paper)', borderRadius: '24px 24px 0 0', maxHeight: '65vh', display: 'flex', flexDirection: 'column', overflow: 'hidden' }}>
            <div style={{ width: 40, height: 4, borderRadius: 999, background: 'rgba(14,28,19,0.15)', margin: '14px auto 0' }}/>
            <div style={{ padding: '14px 20px 12px', borderBottom: '1px solid rgba(14,28,19,0.07)' }}>
              <div style={{ fontFamily: 'var(--font-display)', fontSize: 20, color: 'var(--forest)' }}>Pick an event</div>
              <div style={{ fontSize: 11, color: 'var(--ink)', opacity: 0.55, marginTop: 3 }}>Your guest will get an invite for this event.</div>
            </div>
            <div style={{ overflowY: 'auto', flex: 1 }}>
              {upcoming.length === 0 ? (
                <div style={{ padding: 32, textAlign: 'center', opacity: 0.4, fontSize: 13 }}>No upcoming events.</div>
              ) : upcoming.map((ev, i) => {
                const alreadySent = isPlus && invites.some(inv => inv.eventId === ev.id);
                return (
                  <button key={ev.id} onClick={() => { if (!alreadySent) { setSelectedEvent(ev); setSendStep('contact'); } }} style={{
                    width: '100%', textAlign: 'left', padding: '14px 20px',
                    borderBottom: i < upcoming.length - 1 ? '1px solid rgba(14,28,19,0.05)' : 'none',
                    background: 'transparent', border: 'none', cursor: alreadySent ? 'not-allowed' : 'pointer',
                    opacity: alreadySent ? 0.4 : 1,
                  }}>
                    <div style={{ fontSize: 14, fontWeight: 700, color: 'var(--forest)' }}>{ev.courseShort || ev.title}</div>
                    <div style={{ fontSize: 11, opacity: 0.55, fontFamily: 'var(--font-mono)', marginTop: 3, letterSpacing: '0.03em' }}>
                      {ev.date} · {ev.time}{alreadySent ? ' · Pass already sent' : ''}
                    </div>
                  </button>
                );
              })}
            </div>
          </div>
        </div>
      )}

      {/* Contact input sub-sheet */}
      {sendStep === 'contact' && selectedEvent && (
        <div style={{ position: 'fixed', inset: 0, zIndex: 300, display: 'flex', flexDirection: 'column', justifyContent: 'flex-end' }}>
          <div onClick={() => setSendStep('event')} style={{ position: 'absolute', inset: 0, background: 'rgba(0,0,0,0.4)' }}/>
          <div style={{ position: 'relative', background: 'var(--paper)', borderRadius: '24px 24px 0 0', padding: '0 20px 36px', overflow: 'hidden' }}>
            <div style={{ width: 40, height: 4, borderRadius: 999, background: 'rgba(14,28,19,0.15)', margin: '14px auto 16px' }}/>
            <div style={{ fontFamily: 'var(--font-display)', fontSize: 22, color: 'var(--forest)', marginBottom: 6 }}>Who's your guest?</div>
            <div style={{ fontSize: 12, color: 'var(--ink)', opacity: 0.55, marginBottom: 18, lineHeight: 1.5 }}>
              Enter their email or phone. They'll receive an invite for {selectedEvent.courseShort || selectedEvent.title} on {selectedEvent.date}. The pass shows as pending until they sign up using the same contact.
            </div>
            <input
              type="text"
              placeholder="email@example.com or (305) 555-0100"
              value={contactInput}
              onChange={e => setContactInput(e.target.value)}
              autoFocus
              style={{
                width: '100%', boxSizing: 'border-box',
                padding: '14px 16px', borderRadius: 14,
                border: '1.5px solid rgba(14,28,19,0.15)', background: 'var(--canvas)',
                fontSize: 15, color: 'var(--ink)', fontFamily: 'inherit',
                outline: 'none', marginBottom: 12,
              }}
            />
            <Button variant="forest" full onClick={sendInvite} disabled={!contactInput.trim()}>
              Send invite <Icon.ArrowRight size={14}/>
            </Button>
            <button onClick={() => setSendStep('event')} style={{
              width: '100%', marginTop: 8, padding: '12px 0',
              background: 'transparent', border: 'none',
              color: 'var(--forest)', fontSize: 13, fontWeight: 600, cursor: 'pointer', opacity: 0.6,
            }}>Back</button>
          </div>
        </div>
      )}
    </div>
  );
}

// ─── Match History Sheet ─────────────────────────────────────────────
function MatchHistorySheet({ history, onClose }) {
  const [selectedMatch, setSelectedMatch] = React.useState(null);

  return (
    <div style={{ position: 'fixed', inset: 0, zIndex: 200, display: 'flex', flexDirection: 'column', justifyContent: 'flex-end' }}>
      <div onClick={onClose} style={{ position: 'absolute', inset: 0, background: 'rgba(0,0,0,0.45)' }}/>
      <div style={{ position: 'relative', background: 'var(--paper)', borderRadius: '24px 24px 0 0', maxHeight: '85vh', display: 'flex', flexDirection: 'column', overflow: 'hidden' }}>
        <div style={{ width: 40, height: 4, borderRadius: 999, background: 'rgba(14,28,19,0.15)', margin: '14px auto 0' }}/>
        <div style={{ padding: '16px 20px 12px', display: 'flex', alignItems: 'center', justifyContent: 'space-between', borderBottom: '1px solid rgba(14,28,19,0.07)' }}>
          <div style={{ fontFamily: 'var(--font-display)', fontSize: 22, color: 'var(--forest)', lineHeight: 1 }}>Match history</div>
          <button onClick={onClose} style={{ padding: '6px 10px', borderRadius: 999, background: 'rgba(14,28,19,0.07)', border: 'none', fontSize: 12, fontWeight: 700, color: 'var(--forest)', cursor: 'pointer' }}>Done</button>
        </div>
        <div style={{ overflowY: 'auto', flex: 1, padding: '16px' }}>
          {history.length === 0 ? (
            <div style={{ padding: 40, textAlign: 'center', opacity: 0.4 }}>
              <div style={{ fontFamily: 'var(--font-display)', fontSize: 18, color: 'var(--forest)', marginBottom: 6 }}>No matches yet.</div>
            </div>
          ) : (
            <div className="card" style={{ overflow: 'hidden' }}>
              {history.map((r, i) => {
                const isW = r.result === 'W', isL = r.result === 'L';
                const badgeStyle = isW
                  ? { background: 'var(--forest)', color: 'var(--cream)', border: 'none' }
                  : isL
                  ? { background: 'var(--cream)', color: 'var(--forest)', border: 'none' }
                  : { background: 'var(--paper)', color: 'var(--forest)', border: '1px solid rgba(28,73,42,0.25)' };
                const marginColor = isW ? 'var(--forest)' : isL ? 'var(--forest)' : '#8A6A4A';
                const marginOpacity = isL ? 0.55 : 1;
                return (
                  <button key={r.id} onClick={() => setSelectedMatch(r)} style={{
                    display: 'flex', alignItems: 'center', gap: 12, width: '100%', textAlign: 'left',
                    padding: '13px 14px', border: 'none', cursor: 'pointer', background: 'transparent',
                    borderBottom: i < history.length - 1 ? '1px solid rgba(14,28,19,0.05)' : 'none',
                  }}>
                    <div style={{ fontFamily: 'var(--font-display)', fontSize: 13, color: 'var(--forest)', width: 30, opacity: 0.7 }}>{r.week}</div>
                    <div style={{
                      width: 24, height: 24, borderRadius: 6, flexShrink: 0,
                      display: 'flex', alignItems: 'center', justifyContent: 'center',
                      fontFamily: 'var(--font-display)', fontSize: 13,
                      ...badgeStyle,
                    }}>{r.result}</div>
                    <div style={{ flex: 1, fontSize: 12, color: 'var(--ink)' }}>vs {r.opp}</div>
                    <div style={{ fontFamily: 'var(--font-display)', fontSize: 15, color: marginColor, opacity: marginOpacity }}>{r.margin}</div>
                    <Icon.Chevron dir="right" size={10} color="var(--forest)"/>
                  </button>
                );
              })}
            </div>
          )}
        </div>
      </div>

      {selectedMatch && (
        <MatchScorecardSheet
          match={selectedMatch}
          onClose={() => setSelectedMatch(null)}
        />
      )}
    </div>
  );
}

// ─── Match Scorecard Sheet ────────────────────────────────────────────
function MatchScorecardSheet({ match, onClose }) {
  const [detail, loading] = useCompletedMatchDetail(match.id);

  const isW = match.result === 'W';
  const isL = match.result === 'L';
  const userId = MOCK.USER && MOCK.USER.id;

  // Determine user's side from detail (A or B) to translate hole results
  const youSide = detail ? (detail.player_a === userId ? 'A' : 'B') : null;

  // Map hole result (A/B/H) to user's perspective (W/L/H)
  function holeResult(h) {
    if (!h.result || h.result === 'H') return 'H';
    if (!youSide) return h.result;
    return h.result === youSide ? 'W' : 'L';
  }

  return (
    <div style={{ position: 'fixed', inset: 0, zIndex: 300, display: 'flex', flexDirection: 'column', justifyContent: 'flex-end' }}>
      <div onClick={onClose} style={{ position: 'absolute', inset: 0, background: 'rgba(0,0,0,0.6)' }}/>
      <div style={{ position: 'relative', background: 'var(--paper)', borderRadius: '24px 24px 0 0', maxHeight: '88vh', display: 'flex', flexDirection: 'column', overflow: 'hidden' }}>
        <div style={{ width: 40, height: 4, borderRadius: 999, background: 'rgba(14,28,19,0.15)', margin: '14px auto 0' }}/>
        <div style={{ padding: '14px 20px 12px', display: 'flex', alignItems: 'center', justifyContent: 'space-between', borderBottom: '1px solid rgba(14,28,19,0.07)' }}>
          <div style={{ fontFamily: 'var(--font-display)', fontSize: 20, color: 'var(--forest)' }}>Scorecard</div>
          <button onClick={onClose} style={{ padding: '6px 10px', borderRadius: 999, background: 'rgba(14,28,19,0.07)', border: 'none', fontSize: 12, fontWeight: 700, color: 'var(--forest)', cursor: 'pointer' }}>Close</button>
        </div>

        <div style={{ overflowY: 'auto', flex: 1, padding: '20px 16px 32px' }}>
          {/* Result hero */}
          <div style={{
            background: isW
              ? `linear-gradient(135deg, var(--forest-dark) 0%, var(--forest) 55%, var(--moss) 100%)`
              : isL ? 'rgba(14,28,19,0.05)' : 'var(--paper)',
            color: isW ? 'var(--cream)' : 'var(--forest)',
            borderRadius: 20, padding: '24px 20px',
            textAlign: 'center', marginBottom: 20,
            border: !isW ? '1px solid rgba(14,28,19,0.1)' : 'none',
            boxShadow: isW ? 'var(--shadow-md)' : 'none',
            position: 'relative', overflow: 'hidden',
          }}>
            {isW && <div className="grain" style={{ position: 'absolute', inset: 0, pointerEvents: 'none' }}/>}
            <div style={{ fontSize: 9, fontFamily: 'var(--font-mono)', opacity: isW ? 0.7 : 0.5, letterSpacing: '0.16em', textTransform: 'uppercase', position: 'relative' }}>
              {match.week} · Match play
            </div>
            <div style={{ fontFamily: 'var(--font-display)', fontSize: 72, lineHeight: 0.82, marginTop: 10, letterSpacing: '-0.03em', position: 'relative' }}>
              {isW ? 'WIN' : isL ? 'LOSS' : 'TIED'}
            </div>
            <div style={{ fontFamily: 'var(--font-display)', fontSize: 30, marginTop: 10, letterSpacing: '-0.01em', opacity: 0.85, position: 'relative' }}>
              {match.margin}
            </div>
            <div style={{ fontSize: 13, fontFamily: 'var(--font-mono)', marginTop: 8, opacity: isW ? 0.7 : 0.5, letterSpacing: '0.04em', position: 'relative' }}>
              vs {match.opp}
            </div>
          </div>

          {/* Hole grid (if detail loaded) */}
          {loading ? (
            <div style={{ textAlign: 'center', padding: '16px 0', opacity: 0.4, fontSize: 13 }}>Loading details…</div>
          ) : detail && detail.holes.length > 0 ? (
            <div>
              <div style={{ fontSize: 10, fontFamily: 'var(--font-mono)', color: 'var(--forest)', opacity: 0.55, letterSpacing: '0.12em', textTransform: 'uppercase', marginBottom: 10 }}>
                Hole by hole
              </div>
              <div style={{ display: 'flex', gap: 5, flexWrap: 'wrap' }}>
                {detail.holes.map(h => {
                  const r = holeResult(h);
                  let bg, fg, border;
                  if (r === 'W')     { bg = 'var(--forest)'; fg = 'var(--cream)'; border = 'none'; }
                  else if (r === 'L') { bg = 'var(--cream)'; fg = 'var(--forest)'; border = 'none'; }
                  else if (r === 'H') { bg = 'var(--paper)'; fg = 'var(--forest)'; border = '1px solid rgba(28,73,42,0.25)'; }
                  else               { bg = 'transparent'; fg = 'rgba(14,28,19,0.3)'; border = '1px solid rgba(14,28,19,0.08)'; }
                  return (
                    <div key={h.hole_number} style={{
                      width: 44, height: 52, borderRadius: 10,
                      background: bg, color: fg, border,
                      display: 'flex', flexDirection: 'column',
                      alignItems: 'center', justifyContent: 'center', gap: 2,
                    }}>
                      <span style={{ fontSize: 9, fontFamily: 'var(--font-mono)', opacity: 0.65 }}>{h.hole_number}</span>
                      <span style={{ fontFamily: 'var(--font-display)', fontSize: 16, lineHeight: 1 }}>{r || '·'}</span>
                    </div>
                  );
                })}
              </div>
              {/* Summary counts */}
              {(() => {
                const won = detail.holes.filter(h => holeResult(h) === 'W').length;
                const halved = detail.holes.filter(h => holeResult(h) === 'H').length;
                const lost = detail.holes.filter(h => holeResult(h) === 'L').length;
                return (
                  <div style={{ display: 'flex', gap: 16, fontSize: 11, fontFamily: 'var(--font-mono)', color: 'var(--forest)', opacity: 0.75, marginTop: 14, letterSpacing: '0.06em' }}>
                    <span><strong style={{ fontSize: 14, fontFamily: 'var(--font-display)' }}>{won}</strong> WON</span>
                    <span><strong style={{ fontSize: 14, fontFamily: 'var(--font-display)' }}>{halved}</strong> HALVED</span>
                    <span><strong style={{ fontSize: 14, fontFamily: 'var(--font-display)' }}>{lost}</strong> LOST</span>
                  </div>
                );
              })()}
            </div>
          ) : detail ? (
            <div style={{ textAlign: 'center', opacity: 0.35, fontSize: 12, fontFamily: 'var(--font-mono)', letterSpacing: '0.06em' }}>
              HOLE DETAIL NOT AVAILABLE FOR THIS MATCH
            </div>
          ) : null}
        </div>
      </div>
    </div>
  );
}

function FollowListSheet({ userId, mode, viewerId, go, onClose }) {
  const [followerList, followerLoading] = useFollowers(mode === 'followers' ? userId : null);
  const [followingList, followingLoading] = useFollowing(mode === 'following' ? userId : null);
  const list = mode === 'followers' ? followerList : followingList;
  const loading = mode === 'followers' ? followerLoading : followingLoading;

  return (
    <div style={{ position: 'fixed', inset: 0, zIndex: 200, display: 'flex', flexDirection: 'column', justifyContent: 'flex-end' }}>
      <div onClick={onClose} style={{ position: 'absolute', inset: 0, background: 'rgba(0,0,0,0.45)' }}/>
      <div style={{ position: 'relative', background: 'var(--paper)', borderRadius: '24px 24px 0 0', maxHeight: '75vh', display: 'flex', flexDirection: 'column', overflow: 'hidden' }}>
        {/* Handle */}
        <div style={{ width: 40, height: 4, borderRadius: 999, background: 'rgba(14,28,19,0.15)', margin: '14px auto 0' }}/>
        <div style={{ padding: '16px 20px 12px', display: 'flex', alignItems: 'center', justifyContent: 'space-between', borderBottom: '1px solid rgba(14,28,19,0.07)' }}>
          <div style={{ fontFamily: 'var(--font-display)', fontSize: 22, color: 'var(--forest)', lineHeight: 1 }}>
            {mode === 'followers' ? 'Followers' : 'Following'}
          </div>
          <button onClick={onClose} style={{ padding: '6px 10px', borderRadius: 999, background: 'rgba(14,28,19,0.07)', border: 'none', fontSize: 12, fontWeight: 700, color: 'var(--forest)', cursor: 'pointer' }}>Done</button>
        </div>
        <div style={{ overflowY: 'auto', flex: 1 }}>
          {loading ? (
            <div style={{ padding: 32, textAlign: 'center', opacity: 0.4, fontSize: 13 }}>Loading…</div>
          ) : list.length === 0 ? (
            <div style={{ padding: 40, textAlign: 'center' }}>
              <div style={{ fontFamily: 'var(--font-display)', fontSize: 18, color: 'var(--forest)', marginBottom: 6 }}>
                {mode === 'followers' ? 'No followers yet.' : 'Not following anyone yet.'}
              </div>
            </div>
          ) : (
            list.map((p, i) => {
              const name = [p.first_name, p.last_name].filter(Boolean).join(' ') || p.handle || '—';
              return (
                <button key={p.id} onClick={() => { onClose(); go({ screen: 'profile', viewingHandle: p.handle }); }} style={{
                  width: '100%', textAlign: 'left', padding: '12px 20px',
                  borderBottom: i < list.length - 1 ? '1px solid rgba(14,28,19,0.05)' : 'none',
                  background: 'transparent', border: 'none', cursor: 'pointer',
                  display: 'flex', alignItems: 'center', gap: 12,
                }}>
                  <AvatarBy url={p.avatar_url} name={name} size={40}/>
                  <div style={{ flex: 1 }}>
                    <div style={{ fontSize: 14, fontWeight: 700, color: 'var(--forest)' }}>{name}</div>
                    <div style={{ fontSize: 11, opacity: 0.55, fontFamily: 'var(--font-mono)', marginTop: 2 }}>
                      {p.handle ? `@${p.handle.replace(/^@/, '')}` : ''}
                      {p.sbx ? ` · SBX ${Number(p.sbx).toFixed(3)}` : ''}
                    </div>
                  </div>
                </button>
              );
            })
          )}
        </div>
      </div>
    </div>
  );
}

// ─── Edit Profile sheet ──────────────────────────────────────────────
function EditProfileSheet({ profile, onClose }) {
  const [firstName, setFirstName] = React.useState(profile.first_name || '');
  const [lastName,  setLastName]  = React.useState(profile.last_name  || '');
  const [handle,    setHandle]    = React.useState((profile.handle || '').replace(/^@/, ''));
  const [bio,       setBio]       = React.useState(profile.bio || '');
  const [home,      setHome]      = React.useState(profile.home_course || '');
  const [focused,   setFocused]   = React.useState(null);
  const [busy,      setBusy]      = React.useState(false);
  const [err,       setErr]       = React.useState('');

  React.useEffect(() => {
    const prev = document.body.style.overflow;
    document.body.style.overflow = 'hidden';
    return () => { document.body.style.overflow = prev; };
  }, []);

  async function save() {
    setErr(''); setBusy(true);
    try {
      await updateProfile({
        userId: profile.id,
        first_name: firstName,
        last_name: lastName,
        handle, bio,
        home_course: home,
      });
      onClose();
    } catch (e) {
      setErr(e.message || 'Could not save.');
    }
    setBusy(false);
  }

  // ── Modern field styling: filled subtle background, no border by default,
  // ── thin forest border on focus.
  const fieldShell = (id) => ({
    background: focused === id ? '#FFFFFF' : '#F4EFE2',
    borderRadius: 14,
    padding: '12px 16px',
    border: focused === id ? '1.5px solid #1C492A' : '1.5px solid transparent',
    transition: 'border-color .15s, background .15s',
  });
  const inputBase = {
    display: 'block',
    width: '100%',
    boxSizing: 'border-box',
    margin: 0, padding: 0,
    background: 'transparent',
    border: 'none',
    fontSize: 16, fontWeight: 500,
    color: '#0E1C13',
    fontFamily: 'inherit',
    outline: 'none',
    WebkitAppearance: 'none',
    appearance: 'none',
    lineHeight: 1.3,
  };
  const inputLabel = {
    display: 'block',
    fontSize: 11,
    fontWeight: 500,
    color: 'rgba(14,28,19,0.55)',
    marginBottom: 4,
    letterSpacing: '0.01em',
  };

  return (
    <div
      onClick={(e) => { if (e.target === e.currentTarget && !busy) onClose(); }}
      style={{
        position: 'fixed', inset: 0,
        background: 'rgba(14,28,19,0.55)',
        backdropFilter: 'blur(8px)', WebkitBackdropFilter: 'blur(8px)',
        display: 'flex', alignItems: 'flex-end', justifyContent: 'center',
        zIndex: 1000,
      }}
    >
      <div style={{
        width: '100%', maxWidth: 440,
        background: '#FFFFFF',
        borderTopLeftRadius: 28, borderTopRightRadius: 28,
        boxShadow: '0 -24px 72px rgba(0,0,0,0.35)',
        maxHeight: '94vh',
        display: 'flex', flexDirection: 'column',
        overflow: 'hidden',
      }}>
        {/* Drag handle */}
        <div style={{
          width: 40, height: 4, borderRadius: 999,
          background: 'rgba(14,28,19,0.16)',
          margin: '10px auto 0',
        }}/>

        {/* Header */}
        <div style={{
          display: 'flex', alignItems: 'center', justifyContent: 'space-between',
          padding: '16px 22px 18px',
        }}>
          <button onClick={busy ? undefined : onClose} style={{
            background: 'transparent', border: 'none', color: '#1C492A',
            fontSize: 15, fontWeight: 500,
            opacity: busy ? 0.4 : 0.75, cursor: busy ? 'wait' : 'pointer',
            padding: 0,
          }}>Cancel</button>
          <div style={{ fontSize: 16, fontWeight: 700, color: '#0E1C13' }}>
            Edit profile
          </div>
          <button
            onClick={busy ? undefined : save}
            disabled={busy}
            style={{
              background: 'transparent', border: 'none',
              color: '#1C492A', fontSize: 15, fontWeight: 700,
              opacity: busy ? 0.4 : 1, cursor: busy ? 'wait' : 'pointer',
              padding: 0,
            }}>{busy ? 'Saving…' : 'Save'}</button>
        </div>

        {/* Body */}
        <div style={{
          padding: '4px 22px 24px',
          overflowY: 'auto', flex: 1,
        }}>
          <div style={{ display: 'flex', gap: 10, marginBottom: 12 }}>
            <div style={{ ...fieldShell('first'), flex: 1, minWidth: 0 }}>
              <label style={inputLabel}>First name</label>
              <input
                type="text"
                value={firstName}
                onChange={(e) => setFirstName(e.target.value)}
                onFocus={() => setFocused('first')}
                onBlur={() => setFocused(null)}
                disabled={busy}
                autoFocus
                style={inputBase}
              />
            </div>
            <div style={{ ...fieldShell('last'), flex: 1, minWidth: 0 }}>
              <label style={inputLabel}>Last name</label>
              <input
                type="text"
                value={lastName}
                onChange={(e) => setLastName(e.target.value)}
                onFocus={() => setFocused('last')}
                onBlur={() => setFocused(null)}
                disabled={busy}
                style={inputBase}
              />
            </div>
          </div>

          <div style={{ ...fieldShell('handle'), marginBottom: 4 }}>
            <label style={inputLabel}>Username</label>
            <div style={{ display: 'flex', alignItems: 'center', gap: 2 }}>
              <span style={{ fontSize: 16, fontWeight: 500, color: 'rgba(14,28,19,0.4)', lineHeight: 1.3 }}>@</span>
              <input
                type="text"
                value={handle}
                onChange={(e) => setHandle(e.target.value.replace(/^@/, '').toLowerCase())}
                onFocus={() => setFocused('handle')}
                onBlur={() => setFocused(null)}
                disabled={busy}
                placeholder="yourhandle"
                style={inputBase}
              />
            </div>
          </div>
          <div style={{ fontSize: 12, color: 'rgba(14,28,19,0.5)', padding: '6px 4px 16px' }}>
            Letters, numbers, _ and . — 2 to 24 characters.
          </div>

          <div style={{ ...fieldShell('home'), marginBottom: 12 }}>
            <label style={inputLabel}>Home course</label>
            <input
              type="text"
              value={home}
              onChange={(e) => setHome(e.target.value)}
              onFocus={() => setFocused('home')}
              onBlur={() => setFocused(null)}
              disabled={busy}
              placeholder="e.g. Melreese"
              style={inputBase}
            />
          </div>

          <div style={{ ...fieldShell('bio'), marginBottom: 4 }}>
            <div style={{ display: 'flex', alignItems: 'baseline', justifyContent: 'space-between' }}>
              <label style={inputLabel}>Bio</label>
              <span style={{
                fontSize: 11,
                color: bio.length >= 200 ? '#C44536' : 'rgba(14,28,19,0.4)',
                fontVariantNumeric: 'tabular-nums',
              }}>{bio.length}/200</span>
            </div>
            <textarea
              value={bio}
              onChange={(e) => setBio(e.target.value)}
              onFocus={() => setFocused('bio')}
              onBlur={() => setFocused(null)}
              disabled={busy}
              maxLength={200}
              placeholder="A line about your game…"
              rows={3}
              style={{
                ...inputBase,
                minHeight: 64,
                resize: 'none',
                lineHeight: 1.4,
              }}
            />
          </div>

          {err && (
            <div style={{
              background: 'rgba(196,69,54,0.08)',
              color: '#9C2E22',
              borderRadius: 12,
              padding: '12px 14px',
              fontSize: 13, fontWeight: 500,
              marginTop: 16,
            }}>{err}</div>
          )}
        </div>
      </div>
    </div>
  );
}

Object.assign(window, { ProfileScreen });
