NBA Feed
Your teams' news and games, with scores hidden until you reveal them.
Race-day crew coordination for ultra racers. Your crew works one board at the aid station, and everyone back home watches the splits land live.
Sports & fitnessJavaScript · HTMLGitHub @SendOffHQOnlineOwner unconfirmed
Have you tried it? One tap is enough. No sign-in needed.
No updates detected yet. GitHub releases will appear here.
Owners of public repositories under a personal GitHub account can sign in to verify ownership and manage the introduction themselves. No administrator approval is needed.
Sign in, generate a verification file, and commit it to this repository’s default branch within 30 minutes. Files on other branches or forks do not count. Then check the file below to manage the project without administrator approval. Keep the file to retain your access.
If needed, you will go to sign-in. After signing in, return here and press your chosen verification button again.
Run /nomorevibe verify in your project folder to verify ownership and manage the introduction yourself.
Verify in one minuteAsk about other verification options
Loading the full README. The saved excerpt is shown below.
A static, multi-race dashboard hub for ultra crew. Each race gets a live
dashboard, charts, printable report, and a phone-friendly pit board.
No server, no backend: every race is just a folder of files in this
repo, edited live via the GitHub Contents API.Read the full README ↗A static, multi-race dashboard hub for ultra crew. Each race gets a live dashboard, charts, printable report, and a phone-friendly crew page. No server, no backend: every race is just a folder of files in this repo, edited live via the GitHub Contents API.
index.html: the marketing page at the site root. Static, no account,
and the only page that is about SendOff rather than about a race.app/index.html: the race hub. Lists every race from races/index.json.
It carries <base href="/"> because it was written at the site root and
every link in it is relative: the tag is what keeps the move to /app/
a move rather than a rewrite. Every other page stayed where it was, so
share links, invites and reset links sent before the move still work.setup.html: wizard. Asks for race name, runner(s), course type
(loops vs. point-to-point segments), aid stations, cutoffs, intake
targets, and a GPX file, then commits races/<slug>/config.json,
races/<slug>/data.json, optional races/<slug>/course.gpx, and
updates races/index.json.race.html?id=<slug>: live dashboard for one race, including a
course map (Leaflet + OpenStreetMap) when a course.gpx is present.pit.html?id=<slug>: sign in/out, intake editing, aid-station editing.charts.html?id=<slug>: leg times, pace, aid times, intake, cumulative.print-report.html?id=<slug>: printable summary.Two course shapes are supported from day one and share the same data
schema. Each "leg" has a startTime (left the previous aid/pit) and
endTime (arrived at the next).
course: { loopCount, loopDistanceMi, loopElevationGainFt? }.
A loop may optionally carry loopSegments: the aid-to-aid segments of
one lap (same shape as segments below). When present, the loop is
treated as that segment pattern repeated loopCount times, so each lap
is split into timed checkpoints; total legs = loopCount × loopSegments.length.course: { segments: [{ name, fromAid, toAid, distanceMi, elevationGainFt?, elevationLossFt?, arriveCutoffHours? }] }.In the wizard, the segments course type collects aid stations as
{ name, mileage, arriveCutoffHours? }, and saves them as segments[]
derived from consecutive aid pairs. Per-segment elevation gain/loss is
computed from the uploaded GPX. Aid stations can be edited later from
the crew page.
This repo is a GitHub Template. To get your own hub:
/ (root).https://<your-handle>.github.io/<your-repo>/. You'll land
on the empty hub.The hub supports two ways to authenticate writes:
To enable the login proxy, deploy worker/ to Cloudflare (see
worker/README.md) and set auth.proxyUrl in
hub.json at the repo root to the worker's URL. With proxyUrl unset
or null, the hub falls back to direct PAT mode, so the proxy can be
down without bricking race day, as long as an operator has a PAT.
Generate password hashes for the worker's USERS secret with
admin/hash.html (opens in any browser, runs
PBKDF2-SHA256 locally: passwords never leave the device).
When the login proxy is enabled, each race is created with a visibility and an ACL:
From the race page, the creator (and any editor) can:
v1 limitation: Private race files still live in this public repo.
The worker enforces ACLs on writes and on /get reads, but anyone with
the exact direct URL of a private race's config.json or data.json
could fetch it from GitHub Pages. The slug randomization makes this
impractical to guess, but it's obscurity, not encryption. The plan is
to move private race storage out of the public repo in a future
iteration.
/
index.html marketing landing page (site root)
app/index.html race hub (moved from / when the landing page took it)
setup.html new-race wizard
race.html per-race dashboard (?id=<slug>)
pit.html per-race crew page
charts.html per-race charts
print-report.html per-race printable
hub.json optional hub-wide config (login proxy URL)
DESIGN.md the design system: tokens, type, components, and the rules the tests hold
lib/
race-core.js shared helpers (compute, GitHub API, GPX, format, auth)
race-theme.css shared design tokens + base styles
signup.html invite-acceptance landing page (proxy mode only)
reset.html password-reset landing page (proxy mode only)
admin.html hub admin tools: issue password-reset links
admin/
hash.html local PBKDF2 password hasher for the proxy USERS list
worker/
src/worker.js Cloudflare Worker auth proxy (optional)
wrangler.toml worker config
README.md worker deploy instructions
races/
index.json hub manifest: { races: [...] }
<slug>/
config.json race definition (course, runners, cutoffs, targets)
data.json live state: { runners: [{ id, legs: [...] }] }
course.gpx optional course file
races/index.json{
"races": [
{
"slug": "sangre-de-cristo-100-2026",
"name": "Sangre de Cristo 100",
"location": "Westcliffe, CO",
"startTime": "2026-07-19T04:00:00-06:00",
"courseType": "segments",
"totalDistanceMi": 101.3,
"runnerNames": ["Jason"],
"cutoffHours": 36,
"createdBy": "[email protected]"
}
],
"lastUpdated": "2026-05-15T12:00:00Z"
}
races/<slug>/config.json (loops){
"name": "Bloodroot 100",
"location": "Pittsfield, VT",
"startTime": "2026-05-08T07:00:00-04:00",
"courseType": "loops",
"visibility": "public",
"createdBy": "[email protected]",
"editors": ["[email protected]"],
"viewers": [],
"cutoffs": { "totalHours": 38, "lastLegStartHours": 35 },
"course": {
"loopCount": 10,
"loopDistanceMi": 10,
"loopElevationGainFt": 1900
},
"runners": [
{ "id": "jasmine", "name": "Jasmine", "bib": "Jasmine · #1", "color": "#c44a18" }
],
"targets": { "caloriesPerHour": 250, "fluidOzPerHour": 20, "sodiumMgPerHour": 500 }
}
visibility, createdBy, editors, and viewers are only meaningful
when the login proxy is enabled. In direct PAT mode they're written but
ignored: any PAT-bearer can read or write any file.
races/<slug>/config.json (segments){
"name": "Sangre de Cristo 100",
"location": "Westcliffe, CO",
"startTime": "2026-09-26T05:00:00-06:00",
"courseType": "segments",
"cutoffs": { "totalHours": 36 },
"course": {
"segments": [
{
"name": "Start → Music Pass",
"fromAid": "Start", "toAid": "Music Pass",
"distanceMi": 7.2,
"elevationGainFt": 2400, "elevationLossFt": 100,
"arriveCutoffHours": 3.0
}
]
},
"runners": [ /* same shape */ ],
"targets": { /* same shape */ }
}
races/<slug>/data.json{
"lastUpdated": "2026-05-09T22:35:00Z",
"runners": [
{
"id": "jasmine",
"legs": [
{
"index": 1,
"startTime": "2026-05-08T07:00:00-04:00",
"endTime": "2026-05-08T13:30:57Z",
"calories": 800, "fluidOz": 30, "sodiumMg": 1356,
"gearChanges": "...", "meds": "...", "issues": "...", "notes": "..."
}
]
}
]
}
legs[].index is 1-based and references either the loop number (loops
mode) or the position in config.course.segments (segments mode).
Aid-station / pit time between legs is derived as
legs[n].startTime - legs[n-1].endTime.
This started as crew tooling for a single race (BloodRoot, a 100-mile loop event), then needed point-to-point support for the Sangre de Cristo 100. The hub generalizes both into one template anyone can fork and add their own races to.