# Leaderboard Findings

This is the most important surface for the redesign.

Primary visual reference:

- `screenshots/08_iphone_live_leaderboard_overlay.png`
- Viewport: `430 x 932`
- Source: live site, logged-in/authenticated session
- State: top scores loaded with real data

## Current Implementation

UI file:

- `src/components/LeaderboardDialog.tsx`

Client API:

- `src/leaderboard.ts`

Server/API:

- `netlify/functions/leaderboard.ts`
- `netlify/functions/leaderboard-session.ts`
- `netlify/functions/me.ts`
- `netlify/functions/_shared/leaderboard.ts`
- `netlify/functions/_shared/leaderboardDb.ts`

Database:

- `leaderboard_players`
- `score_submissions`
- `leaderboard_sessions`

The current leaderboard is a modal overlay. It receives `open` and `onClose`, renders a fixed overlay with `role="dialog"`, closes on backdrop click, and stops propagation on the card.

On open, it fetches:

- public leaderboard entries through `fetchLeaderboard()`
- current player display name through `fetchCurrentPlayer()` when signed in

Current UI states:

- Loading
- Error
- Empty
- Data rows
- Signed-out login callout
- Account dialog nested from the leaderboard

Current row display:

- Rank
- Display name
- `Round {entry.round + 1}`
- Short elapsed time
- Submitted month/day
- Score

Important implementation detail:

- Public entries do not expose player id.
- Current-player row highlighting is display-name based, not id-based.
- This is okay for a simple UI but not reliable if two users share a display name.

## Data Model

Public leaderboard entry:

```ts
interface LeaderboardEntry {
  name: string;
  score: number;
  round: number;
  elapsedMs: number;
  submittedAt: string;
}
```

Score submission payload:

```ts
interface LeaderboardScorePayload {
  score: number;
  round: number;
  puzzleId: string;
  puzzleBankVersion?: string;
  boardWidth?: number;
  boardHeight?: number;
  swapCount: number;
  swapLimit: number;
  bonusMovesEarned: number;
  elapsedMs: number;
  breakdown: {
    words: number;
    board: number;
    movesLeft: number;
    time: number;
  };
}
```

Database score fields include total score, round, puzzle id, board size, swaps, swap limit, bonus moves earned, elapsed ms, word/board/move/time point breakdown, total delta, validation status, submitted timestamp, and hashed IP/user agent.

Server ranking behavior:

- Returns best accepted score per player.
- Orders by score descending, elapsed time ascending, submitted time ascending.
- Caps returned entries at 25.
- Supports ranges: all, day, week, month.

Client UI currently calls `fetchLeaderboard()` with no range, so the visible leaderboard is all-time only.

## Modal Behavior

The current modal is visually polished but still reads as a card:

- Background board remains visible and blurred.
- Card has a warm paper gradient, top green/yellow stripe, heavy lower shadow.
- Header is oversized relative to content.
- Rows scroll inside the card.
- On mobile, this feels close to full-screen but still has card margins and modal chrome.

Live mobile finding:

- At 430 x 932, the leaderboard card is readable and attractive.
- It still competes with blurred gameplay behind it.
- The rank/score rows work well, but the surface lacks a sense of "destination" or prestige.
- The page could support a fuller leaderboard identity without breaking the app.

## Full-Screen Options

Option A: Keep modal, polish responsive card

- Lowest risk.
- Keep existing state and component contract.
- Expand mobile card to nearly full viewport, improve scrolling, add tabs/range filters, improve row hierarchy.
- Best if the redesign is "Faithful Polish".

Option B: Full-screen dialog/view state

- Recommended for mobile-first redesign.
- Still controlled by `open/onClose`, no router required.
- On mobile, the overlay becomes a full-screen leaderboard surface with persistent header, range filters, player context, and scrollable rows.
- On desktop, it can remain a centered card or become a wider board-like panel.
- Fits the app because leaderboard is opened from multiple game states.

Option C: Route-backed leaderboard page

- Strongest sense of destination.
- Would allow `/leaderboard` links, sharing, and a real browser history entry.
- Medium-to-high implementation cost because the app currently has no React router.
- Needs careful state handling so opening leaderboard does not disrupt an active game.

Option D: Inline leaderboard section below/around board

- Not recommended for the main game loop.
- The board already uses the vertical mobile viewport tightly.
- Inline leaderboard would compete with the active puzzle and bonus bough.

## Recommended Direction

For Claude Design, ask for a mobile-first full-screen leaderboard concept while keeping the final direction open.

Implementation target should probably be Option B:

- Full-screen dialog/view on mobile.
- Card or wide panel on desktop.
- Preserve current fetch/account logic.
- Add range controls using existing API support.
- Add a richer "your best / signed-in identity" module only if it can be supported by current public/current-player data.

## What A Full-Screen Leaderboard Would Touch

Easy:

- `src/styles.css` leaderboard overlay/card/list rules.
- Add range state in `LeaderboardDialog.tsx`.
- Call `fetchLeaderboard(range)`.
- Add segmented range controls: all, day, week, month.
- Tune mobile dimensions and scroll behavior.
- Improve loading/error/empty states.

Medium:

- Extract `LeaderboardContent` from modal chrome so full-screen and card modes share logic.
- Add player context row or "you" row.
- Distinguish open source: HUD, victory, loss, direct route.
- Add better focus management and keyboard escape handling.
- Add tests for range fetch and responsive rendering.

Risky:

- Adding pagination or more than 25 entries. Server caps and SQL need updates.
- Adding player-id based current-row highlighting. Public entry shape and privacy decisions need changes.
- Reworking score submission timing. Current hook uses one-use sessions and dedupes by score key.
- Adding route-backed navigation without disrupting in-progress game state.

## Keeping Swapple Identity

Preserve:

- Warm paper surface.
- Serif leaderboard title and large serif scores.
- Green/yellow semantic palette.
- Thin rules and modest radii.
- Rank chips with tile-like tactility.
- Apple as subtle accent, not a cartoon takeover.

Explore:

- More editorial scoreboard hierarchy.
- A "daily orchard board" or "top crate" motif.
- Row details that feel like puzzle provenance: round, time, date, perhaps board shape if exposed later.
- Victory-to-leaderboard transition that carries the final score forward.
- A premium indie puzzle feel: richer typography, calmer spacing, better mobile edge-to-edge composition.
