// Events index — two directions.
// Direction A · Calendar view (two-month grid + filter row + featured strip)
// Direction B · List view (grouped by month, big rows)
//
// Both follow the Events stream (Pine palette anchor, Hot Pink as the
// one-per-page pop). The page is Events-led, so the single pink CTA is
// "Buy tickets" on Plains Songbook — the season's main ticketed action.
const EI = {
paper: "#FBF7EE",
warmPaper: "#F5F0E2",
stone: "#E8E2D3",
navy: "#01234B",
ink: "#1F1B2E",
mute: "#6B6577",
pine: "#2F7A2E",
pineDeep: "#1A461A",
pineForest: "#0F2E10",
pineLifted: "#4FA13A",
meadow: "#78BE4D",
leaf: "#BFE0A5",
whisper: "#EBF3DF",
pink: "#EB4D86", // the Events pop
yellow: "#F3CF03",
council: "#45217A",
artists: "#01234B",
};
function EventsNav({ active = "events" }) {
const items = [
{ label: "What's on", stream: "events", path: "#/events", active: active === "events" },
{ label: "Artists", stream: "artists", path: "#/artists" },
{ label: "Programs", stream: "council", path: "#/about" },
{ label: "News", stream: "news", path: "#/news" },
{ label: "Visit", stream: "council", path: "#/about" },
];
return (
);
}
function EventsFooter() {
return (
);
}
function EventsHero({ subtitle, count, mostWantedCta }) {
return (
Events & Learning · {subtitle}
What's on this season.
{count} events through summer 2026 — drop-in days, classes, openings, and one
ticketed concert. Everything is free or pay-what-you-can unless marked.
{/* THE one Hot Pink pop CTA */}
{mostWantedCta} →
Add to calendar (.ics)
);
}
function FilterBar() {
const filterKinds = [
{ label: "All", count: 14, active: true },
{ label: "Drop-in", count: 6 },
{ label: "Class", count: 3 },
{ label: "Talk", count: 2 },
{ label: "Ticketed", count: 2 },
{ label: "Opening", count: 1 },
];
return (
Search
⌕Search by title or venue…
Showing May – Jul 2026
Change ▾
Kind
{filterKinds.map((f) => (
{f.label}
{f.count}
))}
);
}
// ════════════════════════════════════════════════════════════════
// Direction A — Calendar (two months side by side)
// ════════════════════════════════════════════════════════════════
function EventsIndexA() {
return (
{/* Toggle: Calendar / List */}
14 events
·
2 ticketed
·
1 series
{/* Two-month grid */}
{/* Legend */}
{Object.entries(KIND_STYLE).map(([k, s]) => (
{s.label}
))}
Featured · ticketed
{/* Highlighted upcoming strip — featured + next two */}
Coming up this season
Subscribe to the calendar
e.id === "songbook")} />
e.id === "spring-os")} />
e.id === "qw-party")} />
);
}
function ViewToggle({ active }) {
return (
{[
{ id: "calendar", label: "Calendar" },
{ id: "list", label: "List" },
].map((v) => (
{v.label}
))}
);
}
// Manually laid out — May 1 = Friday (col 5, week 1), Jun 1 = Monday (col 1)
// 0-indexed cols: Sun=0 Mon=1 Tue=2 Wed=3 Thu=4 Fri=5 Sat=6
const MAY_2026 = [
[null,null,null,null,null,1,2],
[3,4,5,6,7,8,9],
[10,11,12,13,14,15,16],
[17,18,19,20,21,22,23],
[24,25,26,27,28,29,30],
[31,null,null,null,null,null,null],
];
const JUN_2026 = [
[null,1,2,3,4,5,6],
[7,8,9,10,11,12,13],
[14,15,16,17,18,19,20],
[21,22,23,24,25,26,27],
[28,29,30,null,null,null,null],
[null,null,null,null,null,null,null],
];
function CalendarMonth({ name, days }) {
const [monthName, year] = name.split(" ");
const monthNum = ["January","February","March","April","May","June","July","August","September","October","November","December"].indexOf(monthName) + 1;
const monthIso = `${year}-${String(monthNum).padStart(2, "0")}`;
const headerLabels = ["S", "M", "T", "W", "T", "F", "S"];
return (
{/* Weekday header */}
{headerLabels.map((l, i) => (
{l}
))}
{/* Day cells */}
{days.flat().map((d, i) => (
))}
);
}
function CalendarCell({ day, iso }) {
const events = iso ? (EVENTS_BY_DATE[iso] || []) : [];
if (!day) {
return ;
}
return (
{day}
{events.slice(0, 2).map((e) => {
const s = KIND_STYLE[e.kind];
const featured = e.featured && e.kind === "ticketed";
return (
{e.title}
);
})}
{events.length > 2 && (
+{events.length - 2} more
)}
);
}
function FeaturedEventCard({ event }) {
if (!event) return null;
const d = shortDate(event.date);
return (
Featured · {KIND_STYLE[event.kind].label}
{d.day}
{d.mo} · {event.weekday}
{event.title}
{event.short}
When {event.time}
·
Where {event.venue}
Buy tickets →
);
}
function CompactEventCard({ event }) {
if (!event) return null;
const d = shortDate(event.date);
const s = KIND_STYLE[event.kind];
return (
{s.label}
{d.day}
{d.mo} · {event.weekday}
{event.title}
{event.short}
);
}
// ════════════════════════════════════════════════════════════════
// Direction B — List (grouped by month, big rows)
// ════════════════════════════════════════════════════════════════
function EventsIndexB() {
const byMonth = {};
EVENTS.forEach(e => {
const d = shortDate(e.date);
const key = `${d.monthFull} ${d.year}`;
(byMonth[key] = byMonth[key] || []).push(e);
});
const months = Object.keys(byMonth);
return (
14 events
·
Sorted by date
{months.map((m, mi) => (
{m.split(" ")[0]}
{m.split(" ")[1]}
{byMonth[m].length} events
{byMonth[m].map((e, i) => )}
))}
{/* Bottom pager — "load more" pattern */}
Showing all 14 events through Oct 2026
Look further ahead →
);
}
function EventListRow({ event }) {
const d = shortDate(event.date);
const s = KIND_STYLE[event.kind];
const isPinkCta = event.id === "songbook"; // the page's one pop
return (
{d.day}
{d.mo} · {event.weekday}
{s.label}
{event.featured && (
Featured
)}
{event.medium}
{event.title}
{event.short}
{event.time}
{event.venue}
{event.cost}
);
}
window.EventsIndexA = EventsIndexA;
window.EventsIndexB = EventsIndexB;