import { useState } from "react";
import PageShell from "../components/PageShell.tsx"
import eventsData from "../events.js";
const LOGO_FILES = import.meta.glob("../assets/event-logos/*.svg", {
eager: true,
import: "default",
});
const LOGOS = Object.fromEntries(
Object.entries(LOGO_FILES).map(([path, src]) => [path.split("/").pop(), src])
);
const logoSrc = file => (file && LOGOS[file]) || null;
// Used when an event doesn't name its own org_logo.
const DEFAULT_ORG_LOGO = null;
/* An that removes itself if the file 404s. */
function Logo({ file, alt = "", className }) {
const [failed, setFailed] = useState(false);
const src = logoSrc(file);
if (!src || failed) return null;
return (
setFailed(true)}
/>
);
}
const TEAL = "#138ba0"; // page heading + last-resort card color
/* ── Grid view ────────────────────────────────────────────────
The grid fits as many columns as the screen allows, with no
breakpoints: each card is at least CARD_MIN wide, and the
columns share whatever space is left over.
CARD_MIN narrowest a card may get before dropping a column
MAX_COLS ceiling on columns, so cards don't get absurd on
very wide monitors. GRID_MAX is derived from it.
───────────────────────────────────────────────────────────── */
const CARD_MIN = "32rem";
const MAX_COLS = 4;
const GRID_MAX = `calc(${MAX_COLS} * 38rem)`;
const GRID_TEMPLATE = `repeat(auto-fill, minmax(min(${CARD_MIN}, 100%), 1fr))`;
/* Collapsed past-events strip: how much shows, and the downward
fade applied while it's collapsed. */
const PAST_PEEK = "10rem";
const PAST_FADE = "linear-gradient(to bottom, black 0%, black 45%, transparent 100%)";
/* ── Carousel sizing ──────────────────────────────────────────
CARD the card itself (your original max-w-3xl)
GAP space between cards — raise to push neighbors out
FADE_DIST how far past the card's edge neighbors fade to nothing
───────────────────────────────────────────────────────────── */
const CARD = "min(48rem, 90vw)";
const GAP = "5rem";
const SLIDE = `calc(${CARD} + ${GAP})`;
const HALF_SLIDE = `calc(${CARD} / 2)`;
const FADE_DIST = "18rem";
const EDGE_FADE = `linear-gradient(to right,
transparent calc(50% - (${HALF_SLIDE} + ${FADE_DIST})),
black calc(50% - ${HALF_SLIDE}),
black calc(50% + ${HALF_SLIDE}),
transparent calc(50% + (${HALF_SLIDE} + ${FADE_DIST})))`;
/* ── Grid-card layout ─────────────────────────────────────────
Container queries, not media queries: each card reacts to its
OWN width, which is what we need now that the column count is
dynamic and the viewport says nothing about how wide a card is.
Wide card NGU logo + details on the left, event logo on the
right, descriptions full-width underneath.
Narrow card logos on their own row, then details, then
descriptions — three stacked rows.
───────────────────────────────────────────────────────────── */
const CARD_STYLES = `
.ev-card { container-type: inline-size; }
.ev-grid {
display: grid;
gap: 0.25rem 1.5rem;
grid-template-columns: 2fr 1fr;
grid-template-areas:
"ngu image"
"info image"
"desc desc";
}
@container (max-width: 30rem) {
.ev-grid {
grid-template-columns: 1fr;
grid-template-areas:
"ngu"
"info"
"desc";
}
.ev-grid .ev-image {
justify-self: start;
width: auto;
max-height: 7rem;
margin-bottom: 0.5rem;
}
}
.ev-ngu { grid-area: ngu; }
.ev-info { grid-area: info; }
.ev-image {
grid-area: image;
justify-self: stretch;
align-self: center;
max-height: 11rem;
}
.ev-desc { grid-area: desc; margin-top: 1rem; }
`;
const InstagramIcon = ({ id = "ig-gradient" }) => (
);
/* ═══════════════════════════════════════════════════════════════
EVENT CARD — one component, two sizes.
compact=false → the full card used in the carousel
compact=true → the grid card: details left, text right, and a
footer pinned to the bottom so every card in a
row is the same height with aligned buttons.
═══════════════════════════════════════════════════════════════ */
export function Card({
ev,
defaultColor = TEAL,
accent = TEAL,
compact = false,
interactive = true,
}) {
const past = ev.status === "past";
const color = ev.color || defaultColor;
const orgLogo = ev.org_logo || DEFAULT_ORG_LOGO;
const eventLogo = ev.image;
const igHandle = ev.instagram || null;
const igUrl = igHandle
? `https://instagram.com/${igHandle.replace(/^@/, "")}`
: null;
const descriptions = (
<>
{ev.desc_a && (
{ev.desc_a}
)} {ev.desc_b && ({ev.desc_b}
)} > ); return ("{ev.theme}"
)} {ev.date &&{ev.date}
} {ev.location &&{ev.location}
}"{ev.theme}"
)} {ev.date &&{ev.date}
} {ev.location &&{ev.location}
}This event has concluded — thank you to everyone who joined us!
) : ({igHandle ? "Registration has not opened yet, follow our instagram for more details." : "Registration has not opened yet — check back soon for more details."}
{igHandle && (· Events coming soon, stay connected for announcements ·
) : view === "grid" ? ( /* ── GRID VIEW — upcoming first, past events collapsed below ── */