Tap day on strip = instant switch. Swipe day strip = scroll. Swipe content left/right = page transition (Apple Calendar). Day strip auto-scrolls to selected day.
"April 2026 ▼" — tap title + chevron expands month grid. Day strip replaced by full month calendar. Tap a day = collapse back to day strip with selected day. Same component across all calendar screens (Coach, Athlete, Booking, Invite).
Long press event → event lifts, enters drag mode. Drag within same day only. Coach's own slot occupied → blocked. Unavailable zone → blocked. Participant conflicts → warning dialog "2 of 7 have conflicts. Move anyway?" Coach decides. Drop releases event at new time.
p=personal (green), g=group (cyan), e=external (gray). At-a-glance overview.
Hatched diagonal pattern = outside coach's available hours OR blocked by external calendar.
Imported from Google/Apple Calendar. Full-width, opacity 0.7, anonymized (title only).
All non-Planned states share the same visual grammar: colored 10% bg tint + 1px perimeter + 3px left stripe + inline pill next to title. Pill appears inline (not absolute-positioned) — reads as part of the title. Colors map to semantic roles, not to coach/athlete roles.
Group events use only 4 of these states: Planned, Review, Missed, Finished. Request and Awaiting don't apply — group is a template, athletes join without approval.
Cancelled events disappear from calendar entirely (not a visible state). All visible statuses retained for 2 months, then archived.
GET {role}/training-events/?startDate&endDateGET coach/training-events/allowed-hours/PATCH {role}/training-events/{id}/change-status/
24-hour vertical timeline, 15-min grid snap.
Generated on-demand via Appsflyer OneLink when coach taps Invite — not pre-generated for all future events. Short URL (321.fit/e/xK3aB) carries event_id in attribution params.
Deep link resolves by event_id, so reschedule doesn't break it. If event cancelled before athlete opens → app shows "Event unavailable" and redirects to coach profile.
Backend caches short URL per event_id (1 Appsflyer call per event lifetime). Client caches per session.
"Ticket card" layout — compact summary + note + participants.
Push notification after event end time: "Training ended. Tap to review and complete." Deep links to this screen.
Does NOT block completion. Stays as cash_pending record. Coach can see unpaid athletes in training history.
Post-training screen. Coach reviews payments and completes training.