Behavioral reference for CTI Clubhouse ยท v2026.06.09
| When | What it shows |
|---|---|
| Before trip | Large number (days remaining) + "days until tee off" + "Sept 29 โ Oct 4, 2026 ยท Michigan" |
| Sep 29 | "Welcome to the Couch Tour Invitational! Safe Travels" |
| Sep 30 | "Day 1: Practice Round" |
| Oct 1 | "Day 2: Tournament Begins!" |
| Oct 2 | "Day 3: On To Arcadia Bluffs" |
| Oct 3 | "Day 4: The Final Day" |
| Oct 4 + | "Thanks for Playing. See You Next Year!" (persists forever after trip) |
Dates are hardcoded. The getTodayKey() function returns the current date key โ outside trip dates it returns '9-30' as a fallback for tee times display.
Shows today's rounds from TODAY_SCHEDULE keyed by date. Displays course name, Practice/Tournament badge, and both tee times (Pairing 1 / Pairing 2). Hidden when no schedule exists for today. On post-trip dates the entire widget is replaced by the Trip Summary.
Fetches live data from Open-Meteo API using hardcoded coordinates for each trip day's course location. Shows current temp (ยฐF), weather description, wind speed, humidity, and daily high/low. Falls back to "Live on trip" placeholder if fetch fails or app is in preview mode. Hidden entirely after Oct 4.
Only visible during the trip (hidden after Oct 4). Shows three categories:
Shows placeholder "MVPs will appear after the first round is complete" until at least one of today's rounds is fully scored. If two rounds happen on the same day (Oct 3), it accumulates across both.
Real-time group chat backed by Supabase messages table. Any player can send messages. Messages persist for the life of the app. The input is disabled until a player is selected. Duplicate messages are prevented via optimistic rendering โ an optimistic message is pushed immediately on send, then replaced by the real Supabase record when the INSERT event arrives via WebSocket.
The tee times, weather, and MVP widgets are all hidden. The countdown area is replaced by a Trip Summary showing final team points per round and the tournament winner.
5 pills: FD ยท Loop ยท South ยท Dozen ยท Bluffs. Active pill highlighted navy. Tap to switch rounds. Selection persists in localStorage.
Two buttons: Pairing 1 / Pairing 2. Each pairing is 2v2 better ball match play. Pairings per round:
7-column grid: Hole ยท Par ยท TBD ยท TBD ยท PTS ยท TBD ยท TBD. Column headers will show player names once teams are assigned. Navy = Team 1, Green = Team 2.
For 12-hole rounds (The Dozen), the scorecard splits into "Front Six" and "Back Six" instead of front/back nine. Subtotals show at the halfway point and at the end.
Tap any score cell โ modal opens with โ / + controls for score and drink count. Tap outside or save to confirm. Scores save to Supabase immediately and broadcast to all connected devices via WebSocket.
Score color coding:
Calculated on net scores (gross minus handicap strokes on that hole).
Stroke dots appear below the score button on holes where a player receives a handicap stroke.
Current state: SUPPRESS_SHARED_STROKE_DOTS = true โ all dots suppressed until course handicaps are entered. When you populate course handicaps, flip this flag to false.
Important: Course handicaps in the COURSES array must already be pre-calculated at 90% of USGA course handicap. Do NOT enter raw handicap index โ enter the final adjusted value. The 90% fallback formula has been removed.
Each hole: better ball net score from each side is compared.
Subtotal rows show running team point totals at Out and final Total.
Long-press (or hold) a score button to add a drink for that player on that hole. Drinks also have a manual tracker in the Stats tab for drinks consumed outside of scoring.
Toggle between the two tabs at the top of the leaderboard page.
Shows cumulative team points across all 5 tournament rounds and both pairings. Each team card shows total points and a per-round breakdown row.
Point accumulation rules:
Head-to-head individual match play standings. Each player is matched against their opposite-number pairing partner across rounds. Shows individual W/L/T record.
Note: Best/Worst Round explicitly excludes The Dozen (12 holes) to avoid skewing gross totals.
Shows the full week schedule from the SCHEDULE array. Each day lists course name, tee times (both groups), and a Practice/Tournament badge. This is a static display โ it does not change based on date.
Current tee times:
A floating banner above the nav bar showing the current round name + "Live Match", with one row per pairing showing match status (e.g. "Warthogs lead 3โ1 ยท Thru 7") and holes played.
The banner floats across all tabs โ it persists when you navigate between Home, Score, Leaderboard, etc.
| Condition | Banner State |
|---|---|
| 10 min before tee time (on correct date, no scores yet) | Shows "All Square" for both pairings โ pre-round mode |
| Scores being entered, round incomplete | Shows live match scores, updates in real time |
| Round complete, same day, before 9pm | Shows final result โ persists until 9pm |
| 9pmโ4am | Hidden |
| 4am, same day as round | Resumes showing (Priority 2/4) |
| Day after round's scheduled date | Never shows for that round again |
| Next day, 10 min before next round | Pre-round "All Square" appears for new round |
Once a round's scheduled date has passed, it is permanently excluded from all banner priorities.
Team names are pulled from TEAM_NAMES. Plural team names use "lead"/"win" (grammatically correct for group nouns).
Shows all 8 players with email, phone, handicap index, and tee group. Handicap shows "TBD" in navy until populated. Team badge shows "TBD" until teams are assigned. Tap email/phone to open mail/dialer. All info is hardcoded in PLAYER_INFO โ edit the array to update.
Blueprint-style cards for all 7 courses (2 practice, 5 tournament). Each card shows Location, Par, and Designer in a spec-sheet format. Below that, a 2-column player grid shows each player's course handicap. Player names are navy; handicap values show "โ" until populated in COURSES[].players.
Arrival/departure info for all 8 players. Ben and Nate show "Driving." All other players have editable fields: Date, Time, Airport, Flight # for both arrival and departure. Data stored in Supabase flights table. Permissions: each player can edit their own row; Owen (p7) can edit all rows. Tap a cell to edit inline.
Quotes stored in Supabase quotes table. Any player can add a quote with attribution and context. Quotes persist year to year. Commissioner can delete quotes.
Three sections: Trip History cards (2022โ2026), All-Time Standings table, and Handicap History table. All data is hardcoded in TRIP_HISTORY, ARCHIVE_STANDINGS, and ARCHIVE_HDCP arrays. Must be manually updated after each trip. 2026 trip shows as TBD with tbd:true flag โ excluded from standings until updated.
Standings sort order: trips (most first) โ wins โ ties โ losses (fewest first).
Contains: Add to Home Screen instructions (expandable, with screenshots), Troubleshooting / cache clearing instructions, About section (version, credits), and Commissioner Docs link (visible to Owen only).
Deep link: cticlubhouse.com/#appinfo opens App Info directly and auto-expands the iPhone install instructions.
The app subscribes to all changes on scores, drinks, manual_drinks, and messages tables via WebSocket. Any score entered by any player on any device updates everyone's scorecard within ~1 second. The connection status dot in the app header shows green (connected) or red (disconnected).
All scores are also saved to localStorage on the device. On app load, local data renders instantly while Supabase data loads in the background. Supabase always wins on conflict โ it overwrites local with server truth on every load.
The app uses a service worker (sw.js) that caches the HTML, icons, and manifest. Cache version is currently cti-2026-v7. When a new version is deployed:
To force a cache bust on deploy: bump the version string in sw.js from cti-2026-v7 to cti-2026-v8, etc.
GitHub repo: ochamberlin7/cti-clubhouse โ Netlify auto-deploy on commit (~30 sec). Alternatively, drag-and-drop a zip to Netlify deploys page (costs zero build credits). When dragging, always include all 4 files: index.html, sw.js, manifest.json, apple-touch-icon-2.png.
Enter the total welcome dinner bill. The losing team splits it evenly โ 4 ways.