// ============================================================ // sections-b.jsx — Live Feed, Leaderboards, Badges // ============================================================ // ---- LIVE ACTIVITY FEED ---- function kindMeta(kind) { return { run: { tag: "RUN", c: "var(--teal)" }, badge: { tag: "BADGE", c: "oklch(0.78 0.13 92)" }, streak: { tag: "STRK", c: "oklch(0.72 0.13 150)" }, team: { tag: "TEAM", c: "oklch(0.72 0.12 280)" }, }[kind] || { tag: "LOG", c: "var(--teal)" }; } function agoLabel(t) { if (t <= 0) return "today"; if (t < 60) return t + " min ago"; if (t < 1440) return Math.floor(t / 60) + " hr ago"; const d = Math.floor(t / 1440); return d === 1 ? "yesterday" : d + " days ago"; } function Feed() { const [items, setItems] = useState(() => FEED_DATA.map(it => ({ ...it }))); const [count, setCount] = useState(15 * 60); // Countdown to the next sync. useEffect(() => { const tick = setInterval(() => setCount(c => (c <= 1 ? 15 * 60 : c - 1)), 1000); return () => clearInterval(tick); }, []); // When the countdown wraps, pull fresh real data from the server. useEffect(() => { if (count !== 15 * 60) return; let cancelled = false; fetchClubData().then(d => { if (cancelled || !d || !d.feed) return; const prevIds = new Set(items.map(i => i.id)); setItems(d.feed.map((it, i) => ({ ...it, fresh: i === 0 && !prevIds.has(it.id) }))); }).catch(() => {}); return () => { cancelled = true; }; }, [count]); const mm = String(Math.floor(count / 60)).padStart(2, "0"); const ss = String(count % 60).padStart(2, "0"); return (
// 03 — Live activity

Every run shows up here.

Pulled straight from Strava and refreshed automatically. Lace up and you're on the board within the quarter-hour.

NEXT SYNC
{mm}:{ss}
refresh interval · 15:00
{items.length === 0 ? (

// no activities synced yet — connect Strava and your runs land here

) : ( {items.map((it) => { const km = kindMeta(it.kind); return (
{km.tag}
{it.who} {it.action} {it.meta}
{agoLabel(it.t)}
); })}
)}
); } // ---- LEADERBOARDS ---- function Leaderboard({ onSelect }) { const keys = Object.keys(LEADERBOARDS); const [tab, setTab] = useState("distance"); const cfg = LEADERBOARDS[tab]; const ranked = rankBy(tab); const max = ranked.length ? cfg.get(ranked[0]) || 1 : 1; const podium = ranked.length >= 3; return (
// 01 — Monthly leaderboards ‹ Prev {MONTHNAV.label} {MONTHNAV.next ? Next › : Next ›}

Climb the board.

{keys.map(k => ( ))}
{ranked.length === 0 ? (

// no runners on the board yet — be the first to connect Strava

) : ( {podium && (
{[1, 0, 2].map((idx) => { const m = ranked[idx]; const place = idx + 1; return ( ); })}
)}
{(podium ? ranked.slice(3) : ranked).map((m, i) => { const rank = podium ? i + 4 : i + 1; const pct = (cfg.get(m) / max) * 100; const top = rank <= 3; return ( ); })}
)} {ranked.length > 0 &&

↳ select any runner to open their profile

}
); } // ---- BADGES ---- function Badges({ onSelect }) { return (
// 03 — Achievements

Cyber-themed badges,
earned in the field.

Unlocked through real activity. Every emblem maps a security discipline onto a running habit.

{BADGES.map((b, i) => { const holders = MEMBERS.filter(m => m.badges.includes(b.id)); return (
{b.code}
{b.name}
{b.desc}
{b.holders} earned {holders.slice(0, 4).map(m => ( { e.stopPropagation(); onSelect(m); }} title={m.name}>{m.name[0]} ))}
); })}
); } // ---- WEDNESDAY ATTENDANCE ---- function Attendance() { const labels = ATTENDANCE.labels || []; const rows = [...(ATTENDANCE.rows || [])].sort( (a, b) => b.attended - a.attended || a.name.localeCompare(b.name) ); return (
// 02 — Wednesday attendance

Who showed up on Wednesdays.

Every Wednesday is a club run — log one that day and you're marked present.

{rows.length === 0 ? (

// no runners on the board yet

) : (
Runner
{labels.map((d, i) => (
W{i + 1} {d}
))}
Total
{rows.map((r) => { const full = r.total > 0 && r.attended === r.total; return (
{r.name}
{r.wednesdays.map((p, i) => (
{p ? "✓" : ""}
))}
{r.attended}/{r.total}
); })}
)}
); } Object.assign(window, { Feed, Leaderboard, Badges, Attendance });