diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte
index 8287bd5..c68051d 100644
--- a/src/routes/+page.svelte
+++ b/src/routes/+page.svelte
@@ -26,7 +26,6 @@
eventEndpoint = new EventSource('/api/registeredEvents');
eventEndpoint.onmessage = (e) => {
eventTable = JSON.parse(e.data);
- console.log(eventTable);
};
});
@@ -172,6 +171,10 @@
+
{#each bracket.items as player}
@@ -209,337 +212,354 @@
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/routes/api/registeredEvents/+server.ts b/src/routes/api/registeredEvents/+server.ts
index d28a93e..82a5912 100644
--- a/src/routes/api/registeredEvents/+server.ts
+++ b/src/routes/api/registeredEvents/+server.ts
@@ -9,7 +9,6 @@ export async function GET() {
let eventList = async () => {
// Get eventList with structure from database
let newEventList = await getRegisteredEventsWithPlayers();
- console.log(newEventList);
// send to client
enqueue(newEventList);
};
diff --git a/src/routes/event/[eventId]/+page.svelte b/src/routes/event/[eventId]/+page.svelte
index 30ea3bd..a08500d 100644
--- a/src/routes/event/[eventId]/+page.svelte
+++ b/src/routes/event/[eventId]/+page.svelte
@@ -3,6 +3,30 @@
import type { PageProps } from './$types';
let { params }: PageProps = $props();
+ function ordinal(n: number) {
+ const s = ['th', 'st', 'nd', 'rd'];
+ const v = n % 100;
+ return n + (s[(v - 20) % 10] ?? s[v] ?? s[0]);
+ }
+
+ function marquee(node: HTMLElement) {
+ function measure() {
+ const inner = node.querySelector
('.marquee-inner');
+ if (!inner) return;
+ const overflow = inner.scrollWidth - node.clientWidth;
+ if (overflow > 2) {
+ node.style.setProperty('--scroll-dist', `-${overflow + 6}px`);
+ inner.classList.add('scrolling');
+ } else {
+ inner.classList.remove('scrolling');
+ }
+ }
+ measure();
+ const ro = new ResizeObserver(measure);
+ ro.observe(node);
+ return { destroy: () => ro.disconnect() };
+ }
+
let eventId = params.eventId;
let eventEndpoint: EventSource;
@@ -40,8 +64,45 @@
{console.log(event)}
{event.name} - {event.division}
- {#each event.registeredPlayers as division}
-
{division.name}
+ div
+ {#each event.registeredPlayers as bracket, bi}
+ {#if bi > 0}
+
+ {/if}
+
+
+ {#each bracket.items as player}
+
+
+
+
+
+
+ {player.firstName}
+ {player.lastName}
+
+
+ {#if player.placement !== 0}
+
{ordinal(player.placement)}
+ {:else}
+
+ {/if}
+
+ {/each}
+
{/each}
diff --git a/src/routes/layout.css b/src/routes/layout.css
index 7c97174..6334050 100644
--- a/src/routes/layout.css
+++ b/src/routes/layout.css
@@ -1,5 +1,338 @@
@import url('https://cdn.jsdelivr.net/npm/@catppuccin/palette/css/catppuccin.css');
+@import url('https://fonts.googleapis.com/css2?family=Black+Ops+One&display=swap');
@import 'tailwindcss';
@import '@catppuccin/tailwindcss/mocha.css';
@plugin '@tailwindcss/forms';
@plugin '@tailwindcss/typography';
+
+:global(html),
+:global(body) {
+ min-height: 100vh;
+}
+
+:global(body > div), /* SvelteKit's root wrapper */
+:global(#svelte) {
+ min-height: 100vh;
+ display: flex;
+ flex-direction: column;
+}
+
+.page {
+ display: flex;
+ flex-direction: column;
+ max-height: 150vh;
+ max-width: 1000px;
+ margin: 0 auto;
+ width: 100%;
+}
+
+.goldman {
+ font-family: 'Black Ops One', system-ui;
+ font-weight: 400;
+}
+
+/* ── Leaderboard ── */
+.leaderboard {
+ display: flex;
+ flex-direction: column;
+ gap: 10px;
+ padding: 14px 14px 6px;
+}
+
+.score-box {
+ position: relative;
+ overflow: hidden;
+ border-radius: 14px;
+ border: 2px solid var(--c);
+ color: var(--c);
+ background: color-mix(in srgb, var(--c) 10%, transparent);
+ /* Grid stacking: ghost + fg share the same cell */
+ display: grid;
+ grid-template-columns: 1fr;
+ grid-template-rows: 1fr;
+ text-decoration: none;
+ transition: filter 0.15s ease;
+}
+.score-box:hover {
+ filter: brightness(1.15);
+}
+.score-box > * {
+ grid-column: 1;
+ grid-row: 1;
+}
+
+.winner {
+ aspect-ratio: 2 / 1;
+ border-width: 3px;
+ min-height: 80px;
+}
+.runner {
+ aspect-ratio: 4 / 1;
+ min-height: 56px;
+}
+
+/* Ghost SVG: fill the cell edge-to-edge, text flush to bottom */
+.score-ghost {
+ width: 100%;
+ height: 100%;
+ opacity: 0.18;
+ fill: currentColor;
+}
+.ghost-svg {
+ width: 100%;
+ height: 100%;
+ display: block;
+}
+
+/* Foreground */
+.score-fg {
+ position: relative;
+ z-index: 1;
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ padding: 0 14px;
+ gap: 8px;
+}
+.score-meta {
+ display: flex;
+ flex-direction: column;
+ min-width: 0;
+ flex: 1;
+}
+.score-rank {
+ font-size: 10px;
+ letter-spacing: 1.5px;
+ text-transform: uppercase;
+ opacity: 0.5;
+ line-height: 1;
+ margin-bottom: 3px;
+}
+.score-name {
+ font-size: clamp(11px, 3vw, 20px);
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+}
+.score-pts {
+ font-size: clamp(20px, 5.5vw, 40px);
+ letter-spacing: 2px;
+ flex-shrink: 0;
+}
+.winner .score-name {
+ font-size: clamp(16px, 5vw, 32px);
+}
+.winner .score-pts {
+ font-size: clamp(26px, 7.5vw, 52px);
+}
+
+/*
+ * Runners-up grid:
+ * - 1 column on small screens (<480px)
+ * - 2 columns on medium (480–699px)
+ * - 4 columns on large (≥700px), max 5
+ * --runner-count drives the actual column count on large screens
+ * so fewer than 5 teams still fill the whole row.
+ */
+.runners-grid {
+ display: grid;
+ gap: 10px;
+ grid-template-columns: 1fr;
+}
+@media (min-width: 480px) {
+ .runners-grid {
+ grid-template-columns: repeat(2, 1fr);
+ }
+
+ .winner {
+ aspect-ratio: 2 / 1;
+ }
+
+ .runner {
+ aspect-ratio: 2 / 1;
+ }
+}
+@media (min-width: 700px) {
+ .runners-grid {
+ /* clamp actual count to 4 on large, but use --runner-count
+ so e.g. 2 teams still fill 2 equal columns not 2 of 4 */
+ grid-template-columns: repeat(min(var(--runner-count), 4), 1fr);
+ }
+ .winner {
+ aspect-ratio: 3 / 1;
+ }
+ .runner {
+ aspect-ratio: 3 / 2;
+ }
+}
+
+/* ── Section label ── */
+.section-label {
+ font-size: 10px;
+ letter-spacing: 2.5px;
+ text-transform: uppercase;
+ opacity: 0.4;
+ padding: 12px 16px 4px;
+ margin: 0;
+}
+
+/* ── Events scrollable container ── */
+.events-scroll {
+ flex: 1;
+ overflow-y: auto;
+ /* max-height: 900px; */
+ min-height: 0;
+ padding: 0 14px 24px;
+ /* Thin custom scrollbar */
+ scrollbar-width: thin;
+ scrollbar-color: color-mix(in srgb, currentColor 30%, transparent) transparent;
+}
+.events {
+ display: flex;
+ flex-direction: column;
+ gap: 10px;
+}
+
+/* ── Event card ── */
+.event-card {
+ border-radius: 12px;
+ border: 1px solid color-mix(in srgb, currentColor 18%, transparent);
+ overflow: hidden;
+ transition: box-shadow 0.3s ease;
+}
+/* Focus highlight pulse — added/removed by $effect */
+.event-card.highlight-pulse {
+ animation: card-pulse 1.2s ease-out forwards;
+}
+@keyframes card-pulse {
+ 0% {
+ box-shadow: 0 0 0 3px currentColor;
+ }
+ 100% {
+ box-shadow: 0 0 0 0px currentColor;
+ }
+}
+
+.event-header {
+ display: flex;
+ align-items: baseline;
+ gap: 10px;
+ padding: 9px 13px 7px;
+ border-bottom: 1px solid color-mix(in srgb, currentColor 10%, transparent);
+ flex-wrap: wrap;
+}
+.event-name {
+ font-size: 15px;
+ text-decoration: none;
+ color: inherit;
+}
+.event-name:hover {
+ text-decoration: underline;
+}
+.event-division {
+ font-size: 10px;
+ letter-spacing: 1.2px;
+ text-transform: uppercase;
+ opacity: 0.4;
+}
+
+/* ── Brackets ── */
+.brackets {
+ display: flex;
+ flex-direction: column;
+}
+.bracket-sep {
+ height: 1px;
+ background: color-mix(in srgb, currentColor 10%, transparent);
+ margin: 0 10px;
+}
+.bracket-row {
+ display: flex;
+ gap: 6px;
+ padding: 7px 9px;
+ flex-wrap: wrap;
+}
+
+/* ── Player boxes ── */
+.player-box {
+ flex: 1 1 0; /* equal widths, no min-content bias */
+ max-width: 160px;
+ position: relative;
+ overflow: hidden;
+ border-radius: 8px;
+ border: 1.5px solid var(--c);
+ color: var(--c);
+ background: color-mix(in srgb, var(--c) 10%, transparent);
+ padding: 5px 7px 5px;
+ display: flex;
+ flex-direction: column;
+ min-height: 52px;
+}
+
+/* 1 col on small screens, 4 across on large */
+@media (max-width: 479px) {
+ .bracket-row {
+ flex-direction: column;
+ }
+ .player-box {
+ max-width: 100%;
+ }
+}
+@media (min-width: 700px) {
+ .player-box {
+ max-width: calc(25% - 6px);
+ } /* 4 per row */
+}
+
+.player-ghost {
+ position: absolute;
+ inset: 0;
+ opacity: 0.15;
+ fill: currentColor;
+ pointer-events: none;
+}
+.ghost-svg {
+ width: 100%;
+ height: 100%;
+ display: block;
+}
+
+.player-name-wrap {
+ position: relative;
+ z-index: 1;
+ overflow: hidden;
+ white-space: nowrap;
+}
+.marquee-inner {
+ display: inline-block;
+ font-size: 11px;
+ font-weight: 600;
+ white-space: nowrap;
+}
+.marquee-inner.scrolling {
+ animation: marquee-scroll 7s ease-in-out infinite;
+}
+@keyframes marquee-scroll {
+ 0%,
+ 20% {
+ transform: translateX(0);
+ }
+ 70%,
+ 90% {
+ transform: translateX(var(--scroll-dist, 0px));
+ }
+ 100% {
+ transform: translateX(0);
+ }
+}
+
+.player-placement {
+ position: relative;
+ z-index: 1;
+ font-size: 17px;
+ line-height: 1.1;
+ margin-top: 2px;
+}
+.player-placement-gap {
+ height: 20px;
+}