From 3c78cd4d15a32731eaf38ce01a6bf15331a8e9a9 Mon Sep 17 00:00:00 2001 From: David Nguyen Date: Sat, 3 Oct 2026 09:22:06 +0700 Subject: [PATCH 01/30] refactor(monthly-labs): redesign archive and detail layouts --- components/monthly-labs/pages.tsx | 291 +++++++++++++++++++++++++----- 1 file changed, 241 insertions(+), 50 deletions(-) diff --git a/components/monthly-labs/pages.tsx b/components/monthly-labs/pages.tsx index 391ba024c..4adf2fc17 100644 --- a/components/monthly-labs/pages.tsx +++ b/components/monthly-labs/pages.tsx @@ -1,10 +1,22 @@ "use client" import Link from "next/link" import { useEffect, useState } from "react" -import { Check, Copy, ExternalLink, Gamepad2 } from "lucide-react" +import { + ArrowLeft, + ArrowRight, + CalendarDays, + Check, + Clock3, + Copy, + ExternalLink, + Gamepad2, + Info, + KeyRound, + Trophy, + UsersRound, +} from "lucide-react" import InternalPageShell from "@/components/site/internal-page-shell" import ContentCard from "@/components/site/content-card" -import ContentLink from "@/components/site/content-link" import SwagArtwork from "@/components/arcade/swag-artwork" import { useSiteCatalog } from "@/components/site/use-site-catalog" import { getWebsiteLocaleInfo } from "@/lib/website-i18n" @@ -12,6 +24,7 @@ import { monthlyGamesText } from "@/components/arcade/monthly-games-copy" import { labState, monthLabel, monthlyLabMonthPath, monthlyLabPath, type MonthlyLab } from "./model" type Props = { months?: { month: string; count: number }[]; month?: string; labs?: MonthlyLab[]; lab?: MonthlyLab } + export default function MonthlyLabsPage({ months, month, labs, lab }: Props) { const { catalog, locale } = useSiteCatalog() const m = catalog.messages @@ -19,61 +32,239 @@ export default function MonthlyLabsPage({ months, month, labs, lab }: Props) { const text = (key: Parameters[1], params?: Record) => monthlyGamesText(catalog, key, params) const [now, setNow] = useState(() => Date.now()) const [copyStatus, setCopyStatus] = useState<"idle" | "copied" | "failed">("idle") - useEffect(() => { const timer = setInterval(() => setNow(Date.now()), 60_000); return () => clearInterval(timer) }, []) + + useEffect(() => { + const timer = setInterval(() => setNow(Date.now()), 60_000) + return () => clearInterval(timer) + }, []) + const label = month ? monthLabel(month, intlLocale) : m.monthlyLabs const title = lab?.title ?? label const description = month ? m.labMonthDescription.replace("{month}", label) : m.labArchiveDescription + const detailState = lab ? labState(lab, now) : null + async function copyCode() { - try { await navigator.clipboard.writeText(lab?.accessCode ?? ""); setCopyStatus("copied") } - catch { setCopyStatus("failed") } + try { + await navigator.clipboard.writeText(lab?.accessCode ?? "") + setCopyStatus("copied") + } catch { + setCopyStatus("failed") + } } + const formatNumber = (value: number) => new Intl.NumberFormat(intlLocale).format(value) const deadline = lab?.deadline && Number.isFinite(Date.parse(lab.deadline)) - ? new Intl.DateTimeFormat(intlLocale, { dateStyle: "long", timeStyle: "short", timeZone: lab.deadlineTimeZone ?? "UTC" }).format(new Date(lab.deadline)) + ? new Intl.DateTimeFormat(intlLocale, { + dateStyle: "long", + timeStyle: "short", + timeZone: lab.deadlineTimeZone ?? "UTC", + }).format(new Date(lab.deadline)) : text("deadlineUnavailable") - const breadcrumbs = - return
- -
- {months &&
{months.map(item => -

{monthLabel(item.month, intlLocale)}

-

{m.labCount.replace("{count}", formatNumber(item.count))}

- {m.monthlyLabs} -
)}
} - {labs && <>

{m.labCount.replace("{count}", formatNumber(labs.length))}

{labs.map(item => - {item.imageUrl ? : )}
} - {lab && <> - -
{lab.imageUrl ? :
-
- {m[labState(lab, now)]} -
-
{text("accessCode")}
{lab.accessCode ? <>{lab.accessCode}{copyStatus === "failed" ? m.labCopyFailed : ""} : m.notAvailable}
-
{text("deadline")}
{lab.deadline && Number.isFinite(Date.parse(lab.deadline)) ? : deadline}
- {lab.points !== null &&
{m.arcadePoints}
{text(lab.points === 1 ? "arcadePoint" : "arcadePoints", { count: formatNumber(lab.points) })}
} - {lab.spotsRemaining !== null &&
{m.labHistoricalSlots}
{formatNumber(lab.spotsRemaining)}
} -
-
- {lab.joinUrl && labState(lab, now) !== "labArchived" && {text("openGame")}} - {lab.sourceUrl && {m.labSource}} - {lab.joinUrl && labState(lab, now) === "labArchived" && {m.labDetails}} -
+ + const breadcrumbs = ( + + ) + + return ( +
+ +
+ {months && ( +
+ {months.map((item) => ( + + + + + {monthLabel(item.month, intlLocale)} + {m.labCount.replace("{count}", formatNumber(item.count))} + +
- -

{m.labSourceDescription}

{lab.description ?? m.labMissingDescription}

- } - {(month || lab) &&

{m.labHistoricalNotice}

} -
-
-
+ )} + + {labs && ( + <> +
+

{m.labCount.replace("{count}", formatNumber(labs.length))}

+ +
+ +
+ {labs.map((item) => { + const state = labState(item, now) + return ( + + + {item.imageUrl ? ( + + ) : ( + + + + + {m[state]} + {item.points !== null && ( + + + )} + + + {item.title} + + + {m.labDetails} + + + + ) + })} +
+ + )} + + {lab && detailState && ( + <> + +
+ {lab.imageUrl ? ( + + ) : ( +
+ +
+
+ {m[detailState]} + {lab.points !== null && ( + + + )} +
+ +
+
+
+
+ {lab.accessCode ? ( + <> + {lab.accessCode} + + {copyStatus === "failed" ? m.labCopyFailed : ""} + + ) : m.notAvailable} +
+
+ +
+
+
+ {lab.deadline && Number.isFinite(Date.parse(lab.deadline)) + ? + : deadline} +
+
+ + {lab.points !== null && ( +
+
+
{text(lab.points === 1 ? "arcadePoint" : "arcadePoints", { count: formatNumber(lab.points) })}
+
+ )} + + {lab.spotsRemaining !== null && ( +
+
+
{formatNumber(lab.spotsRemaining)}
+
+ )} +
+ +
+ {lab.joinUrl && detailState !== "labArchived" && ( + + {text("openGame")} + + )} + {lab.sourceUrl && ( + + {m.labSource} + + )} + {lab.joinUrl && detailState === "labArchived" && ( + + {m.labDetails} + + )} +
+
+
+ + +
+
+

{lab.description ?? m.labMissingDescription}

+
+ + )} + + {(month || lab) && ( + + )} +
+ +
+ ) } From 47f8172d6e95dfc06ae722ff5f7144ba2ed15860 Mon Sep 17 00:00:00 2001 From: David Nguyen Date: Sat, 3 Oct 2026 09:22:50 +0700 Subject: [PATCH 02/30] style(monthly-labs): unify responsive archive and detail cards --- app/styles/site-ui.css | 414 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 404 insertions(+), 10 deletions(-) diff --git a/app/styles/site-ui.css b/app/styles/site-ui.css index 80440d581..ef3684436 100644 --- a/app/styles/site-ui.css +++ b/app/styles/site-ui.css @@ -205,14 +205,408 @@ html[lang="en"] .hero-heading h1 { font-weight: 400; } .site-content-page .content-primary-action { color: var(--ui-on-accent); } .content-primary-action:hover { filter: brightness(.94); } -.monthly-lab-breadcrumbs { display: flex; flex-wrap: wrap; align-items: center; gap: .5rem; margin-bottom: 2rem; font-size: .875rem; color: var(--ui-muted); overflow-wrap: anywhere; } +.monthly-lab-breadcrumbs { + display: flex; + flex-wrap: wrap; + align-items: center; + gap: .5rem; + margin-bottom: 2rem; + font-size: .875rem; + color: var(--ui-muted); + overflow-wrap: anywhere; +} +.monthly-lab-breadcrumbs a { color: var(--ui-muted); transition: color .15s ease; } +.monthly-lab-breadcrumbs a:hover { color: var(--ui-accent); } .monthly-lab-breadcrumbs [aria-current] { color: var(--ui-text); } -.monthly-lab-art { display: flex; align-items: center; justify-content: center; border-radius: var(--ui-radius); background: var(--ui-soft); padding: 1rem; } -.monthly-lab-status { display: inline-flex; align-self: flex-start; margin-top: 1rem; padding: .25rem .65rem; border-radius: var(--ui-radius-sm); font-size: .75rem; font-weight: 600; background: var(--ui-soft); color: var(--ui-muted); } -.monthly-lab-status.labActive { background: var(--ui-accent-soft); color: var(--ui-accent); } -.monthly-lab-facts { display: grid; gap: 1rem; margin-top: 1.25rem; } -.monthly-lab-facts dt { font-size: .75rem; color: var(--ui-muted); margin-bottom: .3rem; } -.monthly-lab-facts dd { display: flex; flex-wrap: wrap; align-items: center; gap: .75rem; font-size: .875rem; overflow-wrap: anywhere; } -.monthly-lab-facts code { user-select: all; overflow-wrap: anywhere; } -.monthly-lab-facts [role=status]:empty { display: none; } -.monthly-lab-source { display: inline-flex; align-items: center; gap: .5rem; min-height: 44px; color: var(--ui-accent); font-size: .875rem; } + +.monthly-labs-content { + display: grid; + gap: 1.5rem; +} + +.monthly-lab-archive-grid, +.monthly-lab-game-grid { + display: grid; + gap: 1rem; +} +.monthly-lab-archive-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); } +.monthly-lab-game-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } + +.monthly-lab-month-card { + display: grid; + grid-template-columns: auto minmax(0, 1fr) auto; + align-items: center; + gap: 1rem; + min-height: 104px; + padding: 1.15rem; + border: 1px solid var(--ui-border); + border-radius: var(--ui-radius-lg); + background: var(--ui-surface); + color: var(--ui-text); + box-shadow: var(--ui-shadow); + transition: transform .18s ease, border-color .18s ease, box-shadow .18s ease; +} +.monthly-lab-month-card:hover { + transform: translateY(-2px); + border-color: var(--ui-accent-border); + box-shadow: 0 12px 28px hsl(224 50% 3% / .16); +} +.monthly-lab-month-icon { + display: grid; + width: 46px; + height: 46px; + place-items: center; + border-radius: 14px; + background: var(--ui-accent-soft); + color: var(--ui-accent); +} +.monthly-lab-month-icon svg { width: 22px; height: 22px; } +.monthly-lab-month-copy { + display: grid; + min-width: 0; + gap: .3rem; +} +.monthly-lab-month-copy strong { + color: var(--ui-text); + font-size: 1rem; + line-height: 1.35; +} +.monthly-lab-month-copy span { + color: var(--ui-muted); + font-size: .82rem; + line-height: 1.4; +} +.monthly-lab-card-arrow { + width: 18px; + height: 18px; + color: var(--ui-muted); + transition: transform .18s ease, color .18s ease; +} +.monthly-lab-month-card:hover .monthly-lab-card-arrow, +.monthly-lab-game-card:hover .monthly-lab-card-arrow { + transform: translateX(3px); + color: var(--ui-accent); +} + +.monthly-lab-list-toolbar { + display: flex; + align-items: center; + justify-content: space-between; + gap: 1rem; + min-height: 44px; +} +.monthly-lab-list-toolbar p { + margin: 0; + color: var(--ui-muted); + font-size: .875rem; +} +.monthly-lab-back-link { + display: inline-flex; + min-height: 40px; + align-items: center; + gap: .45rem; + padding: .5rem .8rem; + border: 1px solid var(--ui-border); + border-radius: var(--ui-radius-sm); + background: var(--ui-surface); + color: var(--ui-text); + font-size: .82rem; + font-weight: 700; + transition: border-color .15s ease, color .15s ease, background .15s ease; +} +.monthly-lab-back-link:hover { + border-color: var(--ui-accent-border); + background: var(--ui-accent-soft); + color: var(--ui-accent); +} +.monthly-lab-back-link svg { width: 16px; height: 16px; } + +.monthly-lab-game-card { + display: flex; + min-width: 0; + min-height: 100%; + flex-direction: column; + overflow: hidden; + border: 1px solid var(--ui-border); + border-radius: var(--ui-radius-lg); + background: var(--ui-surface); + color: var(--ui-text); + box-shadow: var(--ui-shadow); + transition: transform .18s ease, border-color .18s ease, box-shadow .18s ease; +} +.monthly-lab-game-card:hover { + transform: translateY(-2px); + border-color: var(--ui-accent-border); + box-shadow: 0 14px 32px hsl(224 50% 3% / .16); +} +.monthly-lab-game-art { + display: flex; + aspect-ratio: 16 / 7; + align-items: center; + justify-content: center; + overflow: hidden; + border-bottom: 1px solid var(--ui-border); + background: + radial-gradient(circle at 20% 20%, var(--ui-hero-glow), transparent 44%), + var(--ui-soft); + padding: 1.25rem; +} +.monthly-lab-game-art img { + transition: transform .2s ease; +} +.monthly-lab-game-card:hover .monthly-lab-game-art img { + transform: scale(1.025); +} +.monthly-lab-placeholder-icon { + width: 64px; + height: 64px; + color: var(--ui-muted); +} +.monthly-lab-game-body { + display: flex; + min-width: 0; + flex: 1; + flex-direction: column; + padding: 1.15rem 1.2rem 1.2rem; +} +.monthly-lab-card-meta { + display: flex; + flex-wrap: wrap; + align-items: center; + gap: .5rem; +} +.monthly-lab-status, +.monthly-lab-points-badge { + display: inline-flex; + min-height: 28px; + align-items: center; + gap: .35rem; + padding: .3rem .65rem; + border-radius: 999px; + font-size: .72rem; + font-weight: 800; + line-height: 1; +} +.monthly-lab-status { + background: var(--ui-soft); + color: var(--ui-muted); +} +.monthly-lab-status.labActive { + background: var(--ui-accent-soft); + color: var(--ui-accent); +} +.monthly-lab-status.labUnknown { + color: var(--ui-warning); +} +.monthly-lab-points-badge { + background: var(--ui-info-soft); + color: var(--ui-info); +} +.monthly-lab-points-badge svg { width: 13px; height: 13px; } +.monthly-lab-game-title { + margin-top: .9rem; + color: var(--ui-text); + font-size: 1.05rem; + line-height: 1.45; + overflow-wrap: anywhere; +} +.monthly-lab-game-footer { + display: flex; + align-items: center; + justify-content: space-between; + gap: .75rem; + margin-top: auto; + padding-top: 1.15rem; + color: var(--ui-accent); + font-size: .82rem; + font-weight: 800; +} +.monthly-lab-game-footer svg { + width: 17px; + height: 17px; + transition: transform .18s ease; +} +.monthly-lab-game-card:hover .monthly-lab-game-footer svg { + transform: translateX(3px); +} + +.monthly-lab-detail-card { + display: grid; + grid-template-columns: minmax(0, .9fr) minmax(0, 1.1fr); + overflow: hidden; + padding: 0 !important; +} +.monthly-lab-detail-media { + display: flex; + min-height: 360px; + align-items: center; + justify-content: center; + border-right: 1px solid var(--ui-border); + background: + radial-gradient(circle at 30% 25%, var(--ui-hero-glow), transparent 42%), + var(--ui-soft); + padding: 2rem; +} +.monthly-lab-detail-placeholder { + width: 120px; + height: 120px; + color: var(--ui-muted); +} +.monthly-lab-detail-body { + display: flex; + min-width: 0; + flex-direction: column; + justify-content: center; + padding: 1.75rem; +} +.monthly-lab-fact-grid { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: .8rem; + margin-top: 1.35rem; +} +.monthly-lab-fact { + min-width: 0; + padding: .9rem; + border: 1px solid var(--ui-border); + border-radius: var(--ui-radius); + background: var(--ui-soft); +} +.monthly-lab-fact dt { + display: flex; + align-items: center; + gap: .45rem; + margin-bottom: .45rem; + color: var(--ui-muted); + font-size: .72rem; + font-weight: 700; +} +.monthly-lab-fact dt svg { + width: 14px; + height: 14px; + color: var(--ui-accent); +} +.monthly-lab-fact dd { + display: flex; + flex-wrap: wrap; + align-items: center; + gap: .6rem; + margin: 0; + color: var(--ui-text); + font-size: .86rem; + font-weight: 700; + line-height: 1.5; + overflow-wrap: anywhere; +} +.monthly-lab-fact code { + max-width: 100%; + user-select: all; + overflow-wrap: anywhere; +} +.monthly-lab-fact [role=status]:empty { display: none; } +.monthly-lab-detail-actions { + display: flex; + flex-wrap: wrap; + gap: .75rem; + margin-top: 1.25rem; +} +.monthly-lab-source { + display: inline-flex; + min-height: 44px; + align-items: center; + justify-content: center; + gap: .5rem; + padding: .625rem .95rem; + border: 1px solid var(--ui-border); + border-radius: var(--ui-radius-sm); + background: var(--ui-surface); + color: var(--ui-accent); + font-size: .875rem; + font-weight: 700; + transition: border-color .15s ease, background .15s ease; +} +.monthly-lab-source:hover { + border-color: var(--ui-accent-border); + background: var(--ui-accent-soft); +} +.monthly-lab-description-card { + padding: 1.4rem; +} +.monthly-lab-section-heading { + display: flex; + align-items: center; + gap: .7rem; +} +.monthly-lab-section-heading svg { + width: 20px; + height: 20px; + color: var(--ui-accent); +} +.monthly-lab-section-heading h2 { + margin: 0; + color: var(--ui-text); + font-size: 1.05rem; +} +.monthly-lab-description-card > p { + margin: .9rem 0 0; + color: var(--ui-muted); + font-size: .92rem; + line-height: 1.75; +} +.monthly-lab-history-note { + display: flex; + align-items: flex-start; + gap: .7rem; + padding: .9rem 1rem; + border: 1px solid var(--ui-border); + border-radius: var(--ui-radius); + background: var(--ui-soft); + color: var(--ui-muted); + font-size: .82rem; + line-height: 1.6; +} +.monthly-lab-history-note svg { + width: 17px; + height: 17px; + flex: 0 0 auto; + margin-top: .15rem; + color: var(--ui-info); +} +.monthly-lab-history-note p { margin: 0; } + +@media (max-width: 900px) { + .monthly-lab-archive-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } + .monthly-lab-detail-card { grid-template-columns: 1fr; } + .monthly-lab-detail-media { + min-height: 280px; + border-right: 0; + border-bottom: 1px solid var(--ui-border); + } +} + +@media (max-width: 640px) { + .monthly-lab-archive-grid, + .monthly-lab-game-grid, + .monthly-lab-fact-grid { + grid-template-columns: 1fr; + } + .monthly-lab-month-card { min-height: 92px; } + .monthly-lab-list-toolbar { + align-items: flex-start; + flex-direction: column; + } + .monthly-lab-game-art { aspect-ratio: 16 / 8; } + .monthly-lab-detail-media { + min-height: 220px; + padding: 1.25rem; + } + .monthly-lab-detail-body { padding: 1.2rem; } + .monthly-lab-detail-actions { + display: grid; + grid-template-columns: 1fr; + } + .monthly-lab-detail-actions :is(.content-primary-action,.monthly-lab-source) { + width: 100%; + } +} + From a6d1c0d45d755e2373658a1b4b737826993eedc2 Mon Sep 17 00:00:00 2001 From: David Nguyen Date: Sat, 3 Oct 2026 09:23:05 +0700 Subject: [PATCH 03/30] test(monthly-labs): cover redesigned responsive hierarchy --- tests/monthly-labs.test.mjs | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/tests/monthly-labs.test.mjs b/tests/monthly-labs.test.mjs index 8c73cc3ff..3329094f1 100644 --- a/tests/monthly-labs.test.mjs +++ b/tests/monthly-labs.test.mjs @@ -85,3 +85,27 @@ test("all monthly page copy has translated catalogs with matching placeholders a assert.match(source, /labState\(lab, now\) !== "labArchived"/) assert.match(readRepoFile("app/sitemap.ts"), /monthlyLabPath/) }) + +test("monthly labs use one responsive UI hierarchy across archive, month and detail pages", () => { + const source = readRepoFile("components/monthly-labs/pages.tsx") + const styles = readRepoFile("app/styles/site-ui.css") + + for (const className of [ + "monthly-lab-archive-grid", + "monthly-lab-month-card", + "monthly-lab-game-grid", + "monthly-lab-game-card", + "monthly-lab-detail-card", + "monthly-lab-fact-grid", + "monthly-lab-history-note", + ]) { + assert.ok(source.includes(className), `monthly lab JSX missing ${className}`) + assert.ok(styles.includes(`.${className}`), `monthly lab CSS missing ${className}`) + } + + assert.match(source, /href="\/monthly-labs\/" className="monthly-lab-back-link"/) + assert.match(source, /className="monthly-lab-detail-actions"/) + assert.match(styles, /@media \(max-width: 640px\)[\s\S]*monthly-lab-game-grid/) + assert.doesNotMatch(source, /className="grid gap-4 md:grid-cols-2"/) +}) + From 515c8485576cbce3d995269fab4758986e0b14c6 Mon Sep 17 00:00:00 2001 From: David Nguyen Date: Sat, 3 Oct 2026 13:40:20 +0700 Subject: [PATCH 04/30] fix(monthly-labs): use supported lucide icons --- components/monthly-labs/pages.tsx | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/components/monthly-labs/pages.tsx b/components/monthly-labs/pages.tsx index 4adf2fc17..5af8df304 100644 --- a/components/monthly-labs/pages.tsx +++ b/components/monthly-labs/pages.tsx @@ -4,16 +4,15 @@ import { useEffect, useState } from "react" import { ArrowLeft, ArrowRight, - CalendarDays, + Calendar, Check, - Clock3, + Clock, Copy, ExternalLink, Gamepad2, Info, - KeyRound, Trophy, - UsersRound, + Users, } from "lucide-react" import InternalPageShell from "@/components/site/internal-page-shell" import ContentCard from "@/components/site/content-card" @@ -96,7 +95,7 @@ export default function MonthlyLabsPage({ months, month, labs, lab }: Props) { {months.map((item) => ( - {monthLabel(item.month, intlLocale)} @@ -180,7 +179,7 @@ export default function MonthlyLabsPage({ months, month, labs, lab }: Props) {
-
+
{lab.accessCode ? ( <> @@ -201,7 +200,7 @@ export default function MonthlyLabsPage({ months, month, labs, lab }: Props) {
-
+
{lab.deadline && Number.isFinite(Date.parse(lab.deadline)) ? @@ -218,7 +217,7 @@ export default function MonthlyLabsPage({ months, month, labs, lab }: Props) { {lab.spotsRemaining !== null && (
-
+
{formatNumber(lab.spotsRemaining)}
)} From 3a1f6dc6517583c3b912db640473fa1ab31f04c1 Mon Sep 17 00:00:00 2001 From: David Nguyen Date: Sat, 3 Oct 2026 13:40:22 +0700 Subject: [PATCH 05/30] test(monthly-labs): follow derived detail state From f37cd7ed90acf9f3b28440c4313d5f0598f947e4 Mon Sep 17 00:00:00 2001 From: David Nguyen Date: Sat, 3 Oct 2026 13:40:43 +0700 Subject: [PATCH 06/30] test(monthly-labs): assert derived archived state --- tests/monthly-labs.test.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/monthly-labs.test.mjs b/tests/monthly-labs.test.mjs index 3329094f1..89ce9afbd 100644 --- a/tests/monthly-labs.test.mjs +++ b/tests/monthly-labs.test.mjs @@ -82,7 +82,7 @@ test("all monthly page copy has translated catalogs with matching placeholders a assert.deepEqual(leaks, []) assert.match(source, /useSiteCatalog/) assert.match(source, /lab\.deadlineTimeZone \?\? "UTC"/) - assert.match(source, /labState\(lab, now\) !== "labArchived"/) + assert.match(source, /detailState !== "labArchived"/) assert.match(readRepoFile("app/sitemap.ts"), /monthlyLabPath/) }) From 147ab18db7e0b3864520580ab535a75fc7c64c9b Mon Sep 17 00:00:00 2001 From: David Nguyen Date: Sat, 3 Oct 2026 13:42:38 +0700 Subject: [PATCH 07/30] test(monthly-labs): update browser selectors for redesigned cards --- scripts/verify-monthly-labs.mjs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts/verify-monthly-labs.mjs b/scripts/verify-monthly-labs.mjs index 472f575c6..d367c7593 100644 --- a/scripts/verify-monthly-labs.mjs +++ b/scripts/verify-monthly-labs.mjs @@ -30,10 +30,10 @@ try { assert.equal(await page.locator("h1").count(), 1) if (path === "/monthly-labs/") { await page.locator("h1").filter({ hasText: catalog.messages.monthlyLabs }).waitFor() - assert.equal(await page.locator(".content-card").count(), Object.keys(snapshot.months).length) + assert.equal(await page.locator(".monthly-lab-month-card").count(), Object.keys(snapshot.months).length) } else if (path.endsWith("/09/")) { - assert.equal(await page.locator(".content-card").count(), september.length) - await page.locator(".content-primary-action").filter({ hasText: catalog.messages.labDetails }).first().waitFor() + assert.equal(await page.locator(".monthly-lab-game-card").count(), september.length) + await page.locator(".monthly-lab-game-card").filter({ hasText: catalog.messages.labDetails }).first().waitFor() } else { assert.equal(await page.locator("h1").innerText(), example.title) await page.locator(".monthly-lab-status").filter({ hasText: catalog.messages.labArchived }).waitFor() From 4c1c387315ed7b1d1c601b70e12af8cbd62bc011 Mon Sep 17 00:00:00 2001 From: David Nguyen Date: Sat, 3 Oct 2026 13:49:45 +0700 Subject: [PATCH 08/30] fix(monthly-games): align detail and open game actions --- components/arcade/monthly-games-panel.tsx | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/components/arcade/monthly-games-panel.tsx b/components/arcade/monthly-games-panel.tsx index 7cf4599ad..7fe920ff3 100644 --- a/components/arcade/monthly-games-panel.tsx +++ b/components/arcade/monthly-games-panel.tsx @@ -553,11 +553,19 @@ export default function MonthlyGamesPanel({ badges, hasProfile }: MonthlyGamesPa
- {detailPath && {catalog.messages.labDetails}} - {game.joinUrl && ( - - {text("openGame")} - + {(detailPath || game.joinUrl) && ( +
+ {detailPath && ( + + {catalog.messages.labDetails} + + )} + {game.joinUrl && ( + + {text("openGame")} + + )} +
)} From 8f0c894756001ff2cb0a5afbb83dda70532358d3 Mon Sep 17 00:00:00 2001 From: David Nguyen Date: Sat, 3 Oct 2026 13:49:48 +0700 Subject: [PATCH 09/30] style(monthly-games): keep card actions on one row --- app/styles/monthly-games.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/styles/monthly-games.css b/app/styles/monthly-games.css index c1b0d8c2a..c6bb46757 100644 --- a/app/styles/monthly-games.css +++ b/app/styles/monthly-games.css @@ -1 +1 @@ -.monthly-games-host{margin-top:14px;scroll-margin-top:92px}.monthly-games-panel{position:relative;margin:0 0 14px;padding:20px;border:1px solid rgba(124,58,237,.24);border-radius:14px;background:linear-gradient(145deg,rgba(12,20,39,.96),rgba(17,28,50,.94));box-shadow:0 16px 40px rgba(2,8,23,.18)}.monthly-games-panel.is-loading{display:flex;align-items:center;justify-content:center;gap:10px;min-height:92px;color:var(--arcade-muted);font-size:.78rem}.monthly-games-panel.is-loading svg{width:18px;height:18px;color:#a78bfa}.monthly-games-heading{display:flex;align-items:flex-start;justify-content:space-between;gap:18px;margin-bottom:17px}.monthly-games-heading>div:first-child{min-width:0}.monthly-games-kicker{display:flex;align-items:center;gap:7px;margin-bottom:7px;color:#a78bfa;font-size:.68rem;font-weight:800;letter-spacing:.08em;text-transform:uppercase}.monthly-games-kicker svg{width:15px;height:15px}.monthly-games-heading h2{margin:0 0 6px;color:#f8fbff;font-size:1.28rem;line-height:1.25;text-transform:capitalize}.monthly-games-heading p{max-width:700px;margin:0;color:var(--arcade-muted);font-size:.74rem;line-height:1.55}.monthly-progress-summary{display:grid;grid-template-columns:auto auto;align-items:baseline;gap:2px 7px;flex:0 0 150px;padding:10px 12px;border:1px solid rgba(167,139,250,.2);border-radius:10px;background:rgba(9,15,31,.58)}.monthly-progress-summary.is-pending{grid-template-columns:auto 1fr;flex-basis:210px}.monthly-progress-summary strong{color:#fff;font-size:1.3rem}.monthly-progress-summary span{color:var(--arcade-muted);font-size:.68rem}.monthly-progress-summary i{grid-column:1/-1;height:6px;overflow:hidden;border-radius:999px;background:#18233b}.monthly-progress-summary i b{display:block;height:100%;border-radius:inherit;background:linear-gradient(90deg,#7c3aed,#22c55e);transition:width .3s ease}.monthly-games-grid{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:14px;align-items:stretch}.monthly-game-card{display:flex;min-width:0;min-height:100%;overflow:hidden;flex-direction:column;border:1px solid rgba(148,163,184,.14);border-radius:14px;background:linear-gradient(180deg,rgba(10,19,36,.94),rgba(6,14,28,.98));box-shadow:inset 0 1px 0 rgba(255,255,255,.02);transition:transform .18s ease,border-color .18s ease,box-shadow .18s ease}.monthly-game-card:hover{transform:translateY(-2px);border-color:rgba(167,139,250,.38);box-shadow:0 14px 30px rgba(2,8,23,.24)}.monthly-game-card.is-complete{border-color:rgba(34,197,94,.32);box-shadow:inset 0 1px 0 rgba(34,197,94,.05)}.monthly-game-art{position:relative;display:flex;height:178px;align-items:center;justify-content:center;overflow:hidden;padding:14px;border-bottom:1px solid rgba(148,163,184,.08);background:radial-gradient(circle at 50% 44%,rgba(66,85,120,.22),rgba(8,15,30,.98) 70%);color:#a78bfa}.monthly-game-art>img{display:block;width:auto!important;height:auto!important;max-width:72%!important;max-height:150px!important;object-fit:contain!important;object-position:center!important;background:transparent!important;filter:drop-shadow(0 8px 16px rgba(0,0,0,.18))}.monthly-game-art>svg{width:34px;height:34px}.monthly-game-art-backdrop{position:absolute!important;inset:-34px!important;width:calc(100% + 68px)!important;height:calc(100% + 68px)!important;max-width:none!important;max-height:none!important;object-fit:cover!important;opacity:.23;filter:blur(28px) saturate(1.3)!important;transform:scale(1.08);pointer-events:none}.monthly-game-art::after{content:"";position:absolute;inset:0;background:linear-gradient(180deg,rgba(6,14,28,.2),rgba(6,14,28,.55));pointer-events:none}.monthly-game-art-image{position:relative;z-index:1;max-width:76%!important;max-height:154px!important;filter:drop-shadow(0 10px 20px rgba(0,0,0,.34))!important}.monthly-game-status{position:absolute;z-index:2;top:10px;right:10px;display:flex;align-items:center;gap:5px;padding:5px 8px;border:1px solid rgba(251,191,36,.32);border-radius:999px;background:rgba(8,15,30,.9);color:#fbbf24;font-size:.61rem;font-weight:800;backdrop-filter:blur(8px)}.monthly-game-status svg{width:12px;height:12px}.monthly-game-status.is-complete{border-color:rgba(34,197,94,.42);color:#4ade80}.monthly-game-status.is-pending{border-color:rgba(96,165,250,.34);color:#93c5fd}.monthly-game-body{display:flex;min-height:0;flex:1;flex-direction:column;padding:15px 15px 14px}.monthly-game-body h3{margin:0 0 7px;color:#f4f8ff;font-size:.91rem;line-height:1.38}.monthly-game-description{display:-webkit-box;overflow:hidden;min-height:3.25em;margin:0 0 13px;color:#93a4bd;font-size:.69rem;line-height:1.55;-webkit-box-orient:vertical;-webkit-line-clamp:3}.monthly-game-meta{display:grid;gap:9px;margin-top:auto}.monthly-access-row{display:flex;align-items:stretch;gap:7px}.monthly-access-value{display:flex;min-width:0;flex:1;align-items:center;gap:8px;padding:7px 9px;border:1px solid rgba(167,139,250,.2);border-radius:8px;background:rgba(124,58,237,.09)}.monthly-access-value>span{flex:0 0 auto;color:#a78bfa;font-size:.57rem;font-weight:800;letter-spacing:.05em;text-transform:uppercase}.monthly-access-value code{min-width:0;overflow:hidden;color:#f5f3ff;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:.7rem;font-weight:800;text-overflow:ellipsis;white-space:nowrap}.monthly-copy-button{display:inline-flex;flex:0 0 auto;align-items:center;justify-content:center;gap:5px;min-width:68px;padding:0 10px;border:1px solid rgba(167,139,250,.28);border-radius:8px;background:rgba(124,58,237,.14);color:#c4b5fd;font-size:.61rem;font-weight:800;cursor:pointer;transition:border-color .16s ease,background .16s ease,color .16s ease}.monthly-copy-button:hover{border-color:rgba(196,181,253,.55);background:rgba(124,58,237,.22);color:#ede9fe}.monthly-copy-button svg{width:13px;height:13px}.monthly-copy-button.is-copied{border-color:rgba(34,197,94,.42);background:rgba(34,197,94,.1);color:#86efac}.monthly-game-facts{display:flex;flex-wrap:wrap;gap:7px 14px;padding:0 1px}.monthly-game-facts>span{display:flex;align-items:center;gap:5px;color:#9fb0c6;font-size:.65rem}.monthly-game-facts svg{width:13px;height:13px;color:#8b5cf6}.monthly-game-points{color:#fbbf24!important;font-weight:800}.monthly-game-points svg{color:#f59e0b!important}.monthly-game-deadline{display:grid!important;grid-template-columns:auto auto minmax(0,1fr);align-items:center;gap:6px!important;padding:8px 9px;border:1px solid rgba(251,191,36,.26);border-radius:8px;background:linear-gradient(90deg,rgba(120,53,15,.18),rgba(69,26,3,.08));color:#fde68a!important}.monthly-game-deadline svg{width:13px;height:13px;color:#f59e0b!important}.monthly-game-deadline strong{font-size:.59rem;letter-spacing:.05em;text-transform:uppercase}.monthly-game-deadline time{justify-self:end;overflow:hidden;color:#fef3c7;font-size:.64rem;font-weight:750;text-align:right;text-overflow:ellipsis;white-space:nowrap}.monthly-game-link{display:flex;align-items:center;justify-content:center;gap:6px;margin-top:11px;padding:8px 10px;border:1px solid rgba(124,58,237,.28);border-radius:8px;background:rgba(124,58,237,.11);color:#c4b5fd;font-size:.68rem;font-weight:800;text-decoration:none}.monthly-game-link:hover{border-color:rgba(139,92,246,.52);background:rgba(124,58,237,.18);color:#ede9fe}.monthly-game-link svg{width:13px;height:13px}.light .monthly-games-panel{border-color:rgba(109,40,217,.18);background:linear-gradient(145deg,rgba(255,255,255,.98),rgba(248,250,252,.98));box-shadow:0 14px 34px rgba(15,23,42,.08)}.light .monthly-games-heading h2,.light .monthly-game-body h3,.light .monthly-progress-summary strong{color:#172033}.light .monthly-progress-summary{border-color:rgba(109,40,217,.16);background:#f8fafc}.light .monthly-progress-summary i{background:#e2e8f0}.light .monthly-game-card{border-color:#e2e8f0;background:linear-gradient(180deg,#fff,#fbfdff)}.light .monthly-game-card.is-complete{border-color:rgba(22,163,74,.28)}.light .monthly-game-art{border-bottom-color:#e5e7eb;background:radial-gradient(circle at 50% 42%,#f8fafc,#eef2f7)}.light .monthly-game-art::after{background:linear-gradient(180deg,rgba(255,255,255,.15),rgba(255,255,255,.62))}.light .monthly-game-art-backdrop{opacity:.2}.light .monthly-game-description,.light .monthly-game-facts>span{color:#64748b}.light .monthly-game-points{color:#b45309!important}.light .monthly-game-points svg{color:#d97706!important}.light .monthly-access-value{border-color:#ddd6fe;background:#faf5ff}.light .monthly-access-value>span{color:#7c3aed}.light .monthly-access-value code{color:#4c1d95}.light .monthly-copy-button{border-color:#ddd6fe;background:#f5f3ff;color:#6d28d9}.light .monthly-copy-button:hover{background:#ede9fe}.light .monthly-game-deadline{border-color:#fde68a;background:linear-gradient(90deg,#fffbeb,#fffdf5);color:#92400e!important}.light .monthly-game-deadline time{color:#78350f}.light .monthly-game-link{border-color:rgba(109,40,217,.2);background:#f5f3ff;color:#6d28d9}.light .monthly-game-link:hover{background:#ede9fe;color:#5b21b6}@media(max-width:1050px){.monthly-games-grid{grid-template-columns:repeat(2,minmax(0,1fr))}}@media(max-width:680px){.monthly-games-host{margin-top:10px;scroll-margin-top:76px}.monthly-games-panel{padding:13px;border-radius:12px}.monthly-games-heading{display:grid;gap:12px;margin-bottom:14px}.monthly-games-heading h2{font-size:1.12rem}.monthly-games-heading p{font-size:.7rem;line-height:1.5}.monthly-progress-summary{width:100%;box-sizing:border-box;padding:9px 10px}.monthly-progress-summary.is-pending{grid-template-columns:auto 1fr;flex-basis:auto}.monthly-games-grid{grid-template-columns:1fr;gap:12px}.monthly-game-card{border-radius:12px}.monthly-game-art{height:154px;padding:10px}.monthly-game-art-backdrop{inset:-24px!important;width:calc(100% + 48px)!important;height:calc(100% + 48px)!important;opacity:.2;filter:blur(22px) saturate(1.2)!important;transform:scale(1.05)}.monthly-game-art-image{max-width:66%!important;max-height:128px!important;filter:drop-shadow(0 8px 16px rgba(0,0,0,.3))!important}.monthly-game-status{top:8px;right:8px;max-width:58%;padding:4px 7px;font-size:.57rem;line-height:1.2;text-align:right}.monthly-game-status svg{width:11px;height:11px}.monthly-game-body{padding:13px}.monthly-game-body h3{font-size:.88rem;line-height:1.35}.monthly-game-description{min-height:0;margin-bottom:11px;font-size:.67rem;-webkit-line-clamp:2}.monthly-game-meta{gap:8px}.monthly-access-row{display:grid;grid-template-columns:minmax(0,1fr) 44px;gap:6px}.monthly-access-value{padding:8px 9px}.monthly-access-value>span{display:none}.monthly-access-value code{font-size:.69rem}.monthly-copy-button{min-width:44px;min-height:40px;padding:0;border-radius:8px}.monthly-copy-button span{display:none}.monthly-game-facts{display:grid;grid-template-columns:1fr 1fr;gap:7px 10px}.monthly-game-facts>span{font-size:.63rem}.monthly-game-points{justify-content:flex-start}.monthly-game-deadline{grid-template-columns:auto minmax(0,1fr);gap:4px 6px!important;padding:8px}.monthly-game-deadline strong{font-size:.57rem}.monthly-game-deadline time{grid-column:1/-1;justify-self:start;padding-left:19px;font-size:.62rem;text-align:left;white-space:normal}.monthly-game-link{min-height:40px;margin-top:10px;padding:9px 10px;font-size:.68rem}.light .monthly-game-art-backdrop{opacity:.16}}@media(max-width:380px){.monthly-games-panel{padding:10px}.monthly-game-art{height:144px}.monthly-game-art-image{max-width:62%!important;max-height:118px!important}.monthly-game-status{max-width:64%;font-size:.54rem}.monthly-game-facts{grid-template-columns:1fr}.monthly-access-row{grid-template-columns:minmax(0,1fr) 42px}.monthly-copy-button{min-width:42px}.monthly-game-deadline time{padding-left:18px}}.monthly-progress-summary.is-coming-soon{grid-template-columns:1fr;align-content:center;flex-basis:190px}.monthly-progress-summary.is-coming-soon strong{font-size:.86rem;color:#c4b5fd}.monthly-progress-summary.is-coming-soon span{font-size:.65rem;line-height:1.35}.monthly-games-coming-soon{display:flex;align-items:center;gap:16px;min-height:150px;padding:22px;border:1px dashed rgba(167,139,250,.28);border-radius:14px;background:linear-gradient(135deg,rgba(124,58,237,.08),rgba(15,23,42,.16));color:#dbe4f3}.monthly-coming-soon-icon{display:grid;place-items:center;flex:0 0 54px;width:54px;height:54px;border:1px solid rgba(167,139,250,.3);border-radius:16px;background:rgba(124,58,237,.12);color:#c4b5fd}.monthly-coming-soon-icon svg{width:25px;height:25px}.monthly-coming-soon-label{display:inline-block;margin-bottom:5px;color:#a78bfa;font-size:.62rem;font-weight:800;letter-spacing:.07em;text-transform:uppercase}.monthly-games-coming-soon h3{margin:0 0 6px;color:#f8fbff;font-size:1rem;line-height:1.35}.monthly-games-coming-soon p{max-width:700px;margin:0;color:var(--arcade-muted);font-size:.72rem;line-height:1.55}.light .monthly-progress-summary.is-coming-soon strong{color:#6d28d9}.light .monthly-games-coming-soon{border-color:rgba(109,40,217,.2);background:linear-gradient(135deg,#faf5ff,#f8fafc);color:#334155}.light .monthly-coming-soon-icon{border-color:#ddd6fe;background:#f5f3ff;color:#7c3aed}.light .monthly-games-coming-soon h3{color:#172033}.light .monthly-games-coming-soon p{color:#64748b}@media(max-width:680px){.monthly-progress-summary.is-coming-soon{flex-basis:auto}.monthly-games-coming-soon{align-items:flex-start;gap:12px;min-height:0;padding:16px}.monthly-coming-soon-icon{flex-basis:44px;width:44px;height:44px;border-radius:13px}.monthly-coming-soon-icon svg{width:21px;height:21px}.monthly-games-coming-soon h3{font-size:.91rem}.monthly-games-coming-soon p{font-size:.68rem}} +.monthly-games-host{margin-top:14px;scroll-margin-top:92px}.monthly-games-panel{position:relative;margin:0 0 14px;padding:20px;border:1px solid rgba(124,58,237,.24);border-radius:14px;background:linear-gradient(145deg,rgba(12,20,39,.96),rgba(17,28,50,.94));box-shadow:0 16px 40px rgba(2,8,23,.18)}.monthly-games-panel.is-loading{display:flex;align-items:center;justify-content:center;gap:10px;min-height:92px;color:var(--arcade-muted);font-size:.78rem}.monthly-games-panel.is-loading svg{width:18px;height:18px;color:#a78bfa}.monthly-games-heading{display:flex;align-items:flex-start;justify-content:space-between;gap:18px;margin-bottom:17px}.monthly-games-heading>div:first-child{min-width:0}.monthly-games-kicker{display:flex;align-items:center;gap:7px;margin-bottom:7px;color:#a78bfa;font-size:.68rem;font-weight:800;letter-spacing:.08em;text-transform:uppercase}.monthly-games-kicker svg{width:15px;height:15px}.monthly-games-heading h2{margin:0 0 6px;color:#f8fbff;font-size:1.28rem;line-height:1.25;text-transform:capitalize}.monthly-games-heading p{max-width:700px;margin:0;color:var(--arcade-muted);font-size:.74rem;line-height:1.55}.monthly-progress-summary{display:grid;grid-template-columns:auto auto;align-items:baseline;gap:2px 7px;flex:0 0 150px;padding:10px 12px;border:1px solid rgba(167,139,250,.2);border-radius:10px;background:rgba(9,15,31,.58)}.monthly-progress-summary.is-pending{grid-template-columns:auto 1fr;flex-basis:210px}.monthly-progress-summary strong{color:#fff;font-size:1.3rem}.monthly-progress-summary span{color:var(--arcade-muted);font-size:.68rem}.monthly-progress-summary i{grid-column:1/-1;height:6px;overflow:hidden;border-radius:999px;background:#18233b}.monthly-progress-summary i b{display:block;height:100%;border-radius:inherit;background:linear-gradient(90deg,#7c3aed,#22c55e);transition:width .3s ease}.monthly-games-grid{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:14px;align-items:stretch}.monthly-game-card{display:flex;min-width:0;min-height:100%;overflow:hidden;flex-direction:column;border:1px solid rgba(148,163,184,.14);border-radius:14px;background:linear-gradient(180deg,rgba(10,19,36,.94),rgba(6,14,28,.98));box-shadow:inset 0 1px 0 rgba(255,255,255,.02);transition:transform .18s ease,border-color .18s ease,box-shadow .18s ease}.monthly-game-card:hover{transform:translateY(-2px);border-color:rgba(167,139,250,.38);box-shadow:0 14px 30px rgba(2,8,23,.24)}.monthly-game-card.is-complete{border-color:rgba(34,197,94,.32);box-shadow:inset 0 1px 0 rgba(34,197,94,.05)}.monthly-game-art{position:relative;display:flex;height:178px;align-items:center;justify-content:center;overflow:hidden;padding:14px;border-bottom:1px solid rgba(148,163,184,.08);background:radial-gradient(circle at 50% 44%,rgba(66,85,120,.22),rgba(8,15,30,.98) 70%);color:#a78bfa}.monthly-game-art>img{display:block;width:auto!important;height:auto!important;max-width:72%!important;max-height:150px!important;object-fit:contain!important;object-position:center!important;background:transparent!important;filter:drop-shadow(0 8px 16px rgba(0,0,0,.18))}.monthly-game-art>svg{width:34px;height:34px}.monthly-game-art-backdrop{position:absolute!important;inset:-34px!important;width:calc(100% + 68px)!important;height:calc(100% + 68px)!important;max-width:none!important;max-height:none!important;object-fit:cover!important;opacity:.23;filter:blur(28px) saturate(1.3)!important;transform:scale(1.08);pointer-events:none}.monthly-game-art::after{content:"";position:absolute;inset:0;background:linear-gradient(180deg,rgba(6,14,28,.2),rgba(6,14,28,.55));pointer-events:none}.monthly-game-art-image{position:relative;z-index:1;max-width:76%!important;max-height:154px!important;filter:drop-shadow(0 10px 20px rgba(0,0,0,.34))!important}.monthly-game-status{position:absolute;z-index:2;top:10px;right:10px;display:flex;align-items:center;gap:5px;padding:5px 8px;border:1px solid rgba(251,191,36,.32);border-radius:999px;background:rgba(8,15,30,.9);color:#fbbf24;font-size:.61rem;font-weight:800;backdrop-filter:blur(8px)}.monthly-game-status svg{width:12px;height:12px}.monthly-game-status.is-complete{border-color:rgba(34,197,94,.42);color:#4ade80}.monthly-game-status.is-pending{border-color:rgba(96,165,250,.34);color:#93c5fd}.monthly-game-body{display:flex;min-height:0;flex:1;flex-direction:column;padding:15px 15px 14px}.monthly-game-body h3{margin:0 0 7px;color:#f4f8ff;font-size:.91rem;line-height:1.38}.monthly-game-description{display:-webkit-box;overflow:hidden;min-height:3.25em;margin:0 0 13px;color:#93a4bd;font-size:.69rem;line-height:1.55;-webkit-box-orient:vertical;-webkit-line-clamp:3}.monthly-game-meta{display:grid;gap:9px;margin-top:auto}.monthly-access-row{display:flex;align-items:stretch;gap:7px}.monthly-access-value{display:flex;min-width:0;flex:1;align-items:center;gap:8px;padding:7px 9px;border:1px solid rgba(167,139,250,.2);border-radius:8px;background:rgba(124,58,237,.09)}.monthly-access-value>span{flex:0 0 auto;color:#a78bfa;font-size:.57rem;font-weight:800;letter-spacing:.05em;text-transform:uppercase}.monthly-access-value code{min-width:0;overflow:hidden;color:#f5f3ff;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:.7rem;font-weight:800;text-overflow:ellipsis;white-space:nowrap}.monthly-copy-button{display:inline-flex;flex:0 0 auto;align-items:center;justify-content:center;gap:5px;min-width:68px;padding:0 10px;border:1px solid rgba(167,139,250,.28);border-radius:8px;background:rgba(124,58,237,.14);color:#c4b5fd;font-size:.61rem;font-weight:800;cursor:pointer;transition:border-color .16s ease,background .16s ease,color .16s ease}.monthly-copy-button:hover{border-color:rgba(196,181,253,.55);background:rgba(124,58,237,.22);color:#ede9fe}.monthly-copy-button svg{width:13px;height:13px}.monthly-copy-button.is-copied{border-color:rgba(34,197,94,.42);background:rgba(34,197,94,.1);color:#86efac}.monthly-game-facts{display:flex;flex-wrap:wrap;gap:7px 14px;padding:0 1px}.monthly-game-facts>span{display:flex;align-items:center;gap:5px;color:#9fb0c6;font-size:.65rem}.monthly-game-facts svg{width:13px;height:13px;color:#8b5cf6}.monthly-game-points{color:#fbbf24!important;font-weight:800}.monthly-game-points svg{color:#f59e0b!important}.monthly-game-deadline{display:grid!important;grid-template-columns:auto auto minmax(0,1fr);align-items:center;gap:6px!important;padding:8px 9px;border:1px solid rgba(251,191,36,.26);border-radius:8px;background:linear-gradient(90deg,rgba(120,53,15,.18),rgba(69,26,3,.08));color:#fde68a!important}.monthly-game-deadline svg{width:13px;height:13px;color:#f59e0b!important}.monthly-game-deadline strong{font-size:.59rem;letter-spacing:.05em;text-transform:uppercase}.monthly-game-deadline time{justify-self:end;overflow:hidden;color:#fef3c7;font-size:.64rem;font-weight:750;text-align:right;text-overflow:ellipsis;white-space:nowrap}.monthly-game-actions{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:8px;margin-top:11px}.monthly-game-actions>.monthly-lab-source,.monthly-game-actions>.monthly-game-link{width:100%;min-width:0;min-height:40px;margin-top:0;box-sizing:border-box;white-space:nowrap}.monthly-game-link{display:flex;align-items:center;justify-content:center;gap:6px;margin-top:11px;padding:8px 10px;border:1px solid rgba(124,58,237,.28);border-radius:8px;background:rgba(124,58,237,.11);color:#c4b5fd;font-size:.68rem;font-weight:800;text-decoration:none}.monthly-game-link:hover{border-color:rgba(139,92,246,.52);background:rgba(124,58,237,.18);color:#ede9fe}.monthly-game-link svg{width:13px;height:13px}.light .monthly-games-panel{border-color:rgba(109,40,217,.18);background:linear-gradient(145deg,rgba(255,255,255,.98),rgba(248,250,252,.98));box-shadow:0 14px 34px rgba(15,23,42,.08)}.light .monthly-games-heading h2,.light .monthly-game-body h3,.light .monthly-progress-summary strong{color:#172033}.light .monthly-progress-summary{border-color:rgba(109,40,217,.16);background:#f8fafc}.light .monthly-progress-summary i{background:#e2e8f0}.light .monthly-game-card{border-color:#e2e8f0;background:linear-gradient(180deg,#fff,#fbfdff)}.light .monthly-game-card.is-complete{border-color:rgba(22,163,74,.28)}.light .monthly-game-art{border-bottom-color:#e5e7eb;background:radial-gradient(circle at 50% 42%,#f8fafc,#eef2f7)}.light .monthly-game-art::after{background:linear-gradient(180deg,rgba(255,255,255,.15),rgba(255,255,255,.62))}.light .monthly-game-art-backdrop{opacity:.2}.light .monthly-game-description,.light .monthly-game-facts>span{color:#64748b}.light .monthly-game-points{color:#b45309!important}.light .monthly-game-points svg{color:#d97706!important}.light .monthly-access-value{border-color:#ddd6fe;background:#faf5ff}.light .monthly-access-value>span{color:#7c3aed}.light .monthly-access-value code{color:#4c1d95}.light .monthly-copy-button{border-color:#ddd6fe;background:#f5f3ff;color:#6d28d9}.light .monthly-copy-button:hover{background:#ede9fe}.light .monthly-game-deadline{border-color:#fde68a;background:linear-gradient(90deg,#fffbeb,#fffdf5);color:#92400e!important}.light .monthly-game-deadline time{color:#78350f}.light .monthly-game-link{border-color:rgba(109,40,217,.2);background:#f5f3ff;color:#6d28d9}.light .monthly-game-link:hover{background:#ede9fe;color:#5b21b6}@media(max-width:1050px){.monthly-games-grid{grid-template-columns:repeat(2,minmax(0,1fr))}}@media(max-width:680px){.monthly-games-host{margin-top:10px;scroll-margin-top:76px}.monthly-games-panel{padding:13px;border-radius:12px}.monthly-games-heading{display:grid;gap:12px;margin-bottom:14px}.monthly-games-heading h2{font-size:1.12rem}.monthly-games-heading p{font-size:.7rem;line-height:1.5}.monthly-progress-summary{width:100%;box-sizing:border-box;padding:9px 10px}.monthly-progress-summary.is-pending{grid-template-columns:auto 1fr;flex-basis:auto}.monthly-games-grid{grid-template-columns:1fr;gap:12px}.monthly-game-card{border-radius:12px}.monthly-game-art{height:154px;padding:10px}.monthly-game-art-backdrop{inset:-24px!important;width:calc(100% + 48px)!important;height:calc(100% + 48px)!important;opacity:.2;filter:blur(22px) saturate(1.2)!important;transform:scale(1.05)}.monthly-game-art-image{max-width:66%!important;max-height:128px!important;filter:drop-shadow(0 8px 16px rgba(0,0,0,.3))!important}.monthly-game-status{top:8px;right:8px;max-width:58%;padding:4px 7px;font-size:.57rem;line-height:1.2;text-align:right}.monthly-game-status svg{width:11px;height:11px}.monthly-game-body{padding:13px}.monthly-game-body h3{font-size:.88rem;line-height:1.35}.monthly-game-description{min-height:0;margin-bottom:11px;font-size:.67rem;-webkit-line-clamp:2}.monthly-game-meta{gap:8px}.monthly-access-row{display:grid;grid-template-columns:minmax(0,1fr) 44px;gap:6px}.monthly-access-value{padding:8px 9px}.monthly-access-value>span{display:none}.monthly-access-value code{font-size:.69rem}.monthly-copy-button{min-width:44px;min-height:40px;padding:0;border-radius:8px}.monthly-copy-button span{display:none}.monthly-game-facts{display:grid;grid-template-columns:1fr 1fr;gap:7px 10px}.monthly-game-facts>span{font-size:.63rem}.monthly-game-points{justify-content:flex-start}.monthly-game-deadline{grid-template-columns:auto minmax(0,1fr);gap:4px 6px!important;padding:8px}.monthly-game-deadline strong{font-size:.57rem}.monthly-game-deadline time{grid-column:1/-1;justify-self:start;padding-left:19px;font-size:.62rem;text-align:left;white-space:normal}.monthly-game-link{min-height:40px;margin-top:10px;padding:9px 10px;font-size:.68rem}.light .monthly-game-art-backdrop{opacity:.16}}@media(max-width:380px){.monthly-games-panel{padding:10px}.monthly-game-art{height:144px}.monthly-game-art-image{max-width:62%!important;max-height:118px!important}.monthly-game-status{max-width:64%;font-size:.54rem}.monthly-game-facts{grid-template-columns:1fr}.monthly-access-row{grid-template-columns:minmax(0,1fr) 42px}.monthly-copy-button{min-width:42px}.monthly-game-deadline time{padding-left:18px}}.monthly-progress-summary.is-coming-soon{grid-template-columns:1fr;align-content:center;flex-basis:190px}.monthly-progress-summary.is-coming-soon strong{font-size:.86rem;color:#c4b5fd}.monthly-progress-summary.is-coming-soon span{font-size:.65rem;line-height:1.35}.monthly-games-coming-soon{display:flex;align-items:center;gap:16px;min-height:150px;padding:22px;border:1px dashed rgba(167,139,250,.28);border-radius:14px;background:linear-gradient(135deg,rgba(124,58,237,.08),rgba(15,23,42,.16));color:#dbe4f3}.monthly-coming-soon-icon{display:grid;place-items:center;flex:0 0 54px;width:54px;height:54px;border:1px solid rgba(167,139,250,.3);border-radius:16px;background:rgba(124,58,237,.12);color:#c4b5fd}.monthly-coming-soon-icon svg{width:25px;height:25px}.monthly-coming-soon-label{display:inline-block;margin-bottom:5px;color:#a78bfa;font-size:.62rem;font-weight:800;letter-spacing:.07em;text-transform:uppercase}.monthly-games-coming-soon h3{margin:0 0 6px;color:#f8fbff;font-size:1rem;line-height:1.35}.monthly-games-coming-soon p{max-width:700px;margin:0;color:var(--arcade-muted);font-size:.72rem;line-height:1.55}.light .monthly-progress-summary.is-coming-soon strong{color:#6d28d9}.light .monthly-games-coming-soon{border-color:rgba(109,40,217,.2);background:linear-gradient(135deg,#faf5ff,#f8fafc);color:#334155}.light .monthly-coming-soon-icon{border-color:#ddd6fe;background:#f5f3ff;color:#7c3aed}.light .monthly-games-coming-soon h3{color:#172033}.light .monthly-games-coming-soon p{color:#64748b}@media(max-width:680px){.monthly-progress-summary.is-coming-soon{flex-basis:auto}.monthly-games-coming-soon{align-items:flex-start;gap:12px;min-height:0;padding:16px}.monthly-coming-soon-icon{flex-basis:44px;width:44px;height:44px;border-radius:13px}.monthly-coming-soon-icon svg{width:21px;height:21px}.monthly-games-coming-soon h3{font-size:.91rem}.monthly-games-coming-soon p{font-size:.68rem}} From 409772442c8bfe8ce2654ace186c707fe186db68 Mon Sep 17 00:00:00 2001 From: David Nguyen Date: Sat, 3 Oct 2026 14:10:15 +0700 Subject: [PATCH 10/30] style(monthly-games): unify action button typography --- app/styles/monthly-games.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/styles/monthly-games.css b/app/styles/monthly-games.css index c6bb46757..159dd94c2 100644 --- a/app/styles/monthly-games.css +++ b/app/styles/monthly-games.css @@ -1 +1 @@ -.monthly-games-host{margin-top:14px;scroll-margin-top:92px}.monthly-games-panel{position:relative;margin:0 0 14px;padding:20px;border:1px solid rgba(124,58,237,.24);border-radius:14px;background:linear-gradient(145deg,rgba(12,20,39,.96),rgba(17,28,50,.94));box-shadow:0 16px 40px rgba(2,8,23,.18)}.monthly-games-panel.is-loading{display:flex;align-items:center;justify-content:center;gap:10px;min-height:92px;color:var(--arcade-muted);font-size:.78rem}.monthly-games-panel.is-loading svg{width:18px;height:18px;color:#a78bfa}.monthly-games-heading{display:flex;align-items:flex-start;justify-content:space-between;gap:18px;margin-bottom:17px}.monthly-games-heading>div:first-child{min-width:0}.monthly-games-kicker{display:flex;align-items:center;gap:7px;margin-bottom:7px;color:#a78bfa;font-size:.68rem;font-weight:800;letter-spacing:.08em;text-transform:uppercase}.monthly-games-kicker svg{width:15px;height:15px}.monthly-games-heading h2{margin:0 0 6px;color:#f8fbff;font-size:1.28rem;line-height:1.25;text-transform:capitalize}.monthly-games-heading p{max-width:700px;margin:0;color:var(--arcade-muted);font-size:.74rem;line-height:1.55}.monthly-progress-summary{display:grid;grid-template-columns:auto auto;align-items:baseline;gap:2px 7px;flex:0 0 150px;padding:10px 12px;border:1px solid rgba(167,139,250,.2);border-radius:10px;background:rgba(9,15,31,.58)}.monthly-progress-summary.is-pending{grid-template-columns:auto 1fr;flex-basis:210px}.monthly-progress-summary strong{color:#fff;font-size:1.3rem}.monthly-progress-summary span{color:var(--arcade-muted);font-size:.68rem}.monthly-progress-summary i{grid-column:1/-1;height:6px;overflow:hidden;border-radius:999px;background:#18233b}.monthly-progress-summary i b{display:block;height:100%;border-radius:inherit;background:linear-gradient(90deg,#7c3aed,#22c55e);transition:width .3s ease}.monthly-games-grid{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:14px;align-items:stretch}.monthly-game-card{display:flex;min-width:0;min-height:100%;overflow:hidden;flex-direction:column;border:1px solid rgba(148,163,184,.14);border-radius:14px;background:linear-gradient(180deg,rgba(10,19,36,.94),rgba(6,14,28,.98));box-shadow:inset 0 1px 0 rgba(255,255,255,.02);transition:transform .18s ease,border-color .18s ease,box-shadow .18s ease}.monthly-game-card:hover{transform:translateY(-2px);border-color:rgba(167,139,250,.38);box-shadow:0 14px 30px rgba(2,8,23,.24)}.monthly-game-card.is-complete{border-color:rgba(34,197,94,.32);box-shadow:inset 0 1px 0 rgba(34,197,94,.05)}.monthly-game-art{position:relative;display:flex;height:178px;align-items:center;justify-content:center;overflow:hidden;padding:14px;border-bottom:1px solid rgba(148,163,184,.08);background:radial-gradient(circle at 50% 44%,rgba(66,85,120,.22),rgba(8,15,30,.98) 70%);color:#a78bfa}.monthly-game-art>img{display:block;width:auto!important;height:auto!important;max-width:72%!important;max-height:150px!important;object-fit:contain!important;object-position:center!important;background:transparent!important;filter:drop-shadow(0 8px 16px rgba(0,0,0,.18))}.monthly-game-art>svg{width:34px;height:34px}.monthly-game-art-backdrop{position:absolute!important;inset:-34px!important;width:calc(100% + 68px)!important;height:calc(100% + 68px)!important;max-width:none!important;max-height:none!important;object-fit:cover!important;opacity:.23;filter:blur(28px) saturate(1.3)!important;transform:scale(1.08);pointer-events:none}.monthly-game-art::after{content:"";position:absolute;inset:0;background:linear-gradient(180deg,rgba(6,14,28,.2),rgba(6,14,28,.55));pointer-events:none}.monthly-game-art-image{position:relative;z-index:1;max-width:76%!important;max-height:154px!important;filter:drop-shadow(0 10px 20px rgba(0,0,0,.34))!important}.monthly-game-status{position:absolute;z-index:2;top:10px;right:10px;display:flex;align-items:center;gap:5px;padding:5px 8px;border:1px solid rgba(251,191,36,.32);border-radius:999px;background:rgba(8,15,30,.9);color:#fbbf24;font-size:.61rem;font-weight:800;backdrop-filter:blur(8px)}.monthly-game-status svg{width:12px;height:12px}.monthly-game-status.is-complete{border-color:rgba(34,197,94,.42);color:#4ade80}.monthly-game-status.is-pending{border-color:rgba(96,165,250,.34);color:#93c5fd}.monthly-game-body{display:flex;min-height:0;flex:1;flex-direction:column;padding:15px 15px 14px}.monthly-game-body h3{margin:0 0 7px;color:#f4f8ff;font-size:.91rem;line-height:1.38}.monthly-game-description{display:-webkit-box;overflow:hidden;min-height:3.25em;margin:0 0 13px;color:#93a4bd;font-size:.69rem;line-height:1.55;-webkit-box-orient:vertical;-webkit-line-clamp:3}.monthly-game-meta{display:grid;gap:9px;margin-top:auto}.monthly-access-row{display:flex;align-items:stretch;gap:7px}.monthly-access-value{display:flex;min-width:0;flex:1;align-items:center;gap:8px;padding:7px 9px;border:1px solid rgba(167,139,250,.2);border-radius:8px;background:rgba(124,58,237,.09)}.monthly-access-value>span{flex:0 0 auto;color:#a78bfa;font-size:.57rem;font-weight:800;letter-spacing:.05em;text-transform:uppercase}.monthly-access-value code{min-width:0;overflow:hidden;color:#f5f3ff;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:.7rem;font-weight:800;text-overflow:ellipsis;white-space:nowrap}.monthly-copy-button{display:inline-flex;flex:0 0 auto;align-items:center;justify-content:center;gap:5px;min-width:68px;padding:0 10px;border:1px solid rgba(167,139,250,.28);border-radius:8px;background:rgba(124,58,237,.14);color:#c4b5fd;font-size:.61rem;font-weight:800;cursor:pointer;transition:border-color .16s ease,background .16s ease,color .16s ease}.monthly-copy-button:hover{border-color:rgba(196,181,253,.55);background:rgba(124,58,237,.22);color:#ede9fe}.monthly-copy-button svg{width:13px;height:13px}.monthly-copy-button.is-copied{border-color:rgba(34,197,94,.42);background:rgba(34,197,94,.1);color:#86efac}.monthly-game-facts{display:flex;flex-wrap:wrap;gap:7px 14px;padding:0 1px}.monthly-game-facts>span{display:flex;align-items:center;gap:5px;color:#9fb0c6;font-size:.65rem}.monthly-game-facts svg{width:13px;height:13px;color:#8b5cf6}.monthly-game-points{color:#fbbf24!important;font-weight:800}.monthly-game-points svg{color:#f59e0b!important}.monthly-game-deadline{display:grid!important;grid-template-columns:auto auto minmax(0,1fr);align-items:center;gap:6px!important;padding:8px 9px;border:1px solid rgba(251,191,36,.26);border-radius:8px;background:linear-gradient(90deg,rgba(120,53,15,.18),rgba(69,26,3,.08));color:#fde68a!important}.monthly-game-deadline svg{width:13px;height:13px;color:#f59e0b!important}.monthly-game-deadline strong{font-size:.59rem;letter-spacing:.05em;text-transform:uppercase}.monthly-game-deadline time{justify-self:end;overflow:hidden;color:#fef3c7;font-size:.64rem;font-weight:750;text-align:right;text-overflow:ellipsis;white-space:nowrap}.monthly-game-actions{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:8px;margin-top:11px}.monthly-game-actions>.monthly-lab-source,.monthly-game-actions>.monthly-game-link{width:100%;min-width:0;min-height:40px;margin-top:0;box-sizing:border-box;white-space:nowrap}.monthly-game-link{display:flex;align-items:center;justify-content:center;gap:6px;margin-top:11px;padding:8px 10px;border:1px solid rgba(124,58,237,.28);border-radius:8px;background:rgba(124,58,237,.11);color:#c4b5fd;font-size:.68rem;font-weight:800;text-decoration:none}.monthly-game-link:hover{border-color:rgba(139,92,246,.52);background:rgba(124,58,237,.18);color:#ede9fe}.monthly-game-link svg{width:13px;height:13px}.light .monthly-games-panel{border-color:rgba(109,40,217,.18);background:linear-gradient(145deg,rgba(255,255,255,.98),rgba(248,250,252,.98));box-shadow:0 14px 34px rgba(15,23,42,.08)}.light .monthly-games-heading h2,.light .monthly-game-body h3,.light .monthly-progress-summary strong{color:#172033}.light .monthly-progress-summary{border-color:rgba(109,40,217,.16);background:#f8fafc}.light .monthly-progress-summary i{background:#e2e8f0}.light .monthly-game-card{border-color:#e2e8f0;background:linear-gradient(180deg,#fff,#fbfdff)}.light .monthly-game-card.is-complete{border-color:rgba(22,163,74,.28)}.light .monthly-game-art{border-bottom-color:#e5e7eb;background:radial-gradient(circle at 50% 42%,#f8fafc,#eef2f7)}.light .monthly-game-art::after{background:linear-gradient(180deg,rgba(255,255,255,.15),rgba(255,255,255,.62))}.light .monthly-game-art-backdrop{opacity:.2}.light .monthly-game-description,.light .monthly-game-facts>span{color:#64748b}.light .monthly-game-points{color:#b45309!important}.light .monthly-game-points svg{color:#d97706!important}.light .monthly-access-value{border-color:#ddd6fe;background:#faf5ff}.light .monthly-access-value>span{color:#7c3aed}.light .monthly-access-value code{color:#4c1d95}.light .monthly-copy-button{border-color:#ddd6fe;background:#f5f3ff;color:#6d28d9}.light .monthly-copy-button:hover{background:#ede9fe}.light .monthly-game-deadline{border-color:#fde68a;background:linear-gradient(90deg,#fffbeb,#fffdf5);color:#92400e!important}.light .monthly-game-deadline time{color:#78350f}.light .monthly-game-link{border-color:rgba(109,40,217,.2);background:#f5f3ff;color:#6d28d9}.light .monthly-game-link:hover{background:#ede9fe;color:#5b21b6}@media(max-width:1050px){.monthly-games-grid{grid-template-columns:repeat(2,minmax(0,1fr))}}@media(max-width:680px){.monthly-games-host{margin-top:10px;scroll-margin-top:76px}.monthly-games-panel{padding:13px;border-radius:12px}.monthly-games-heading{display:grid;gap:12px;margin-bottom:14px}.monthly-games-heading h2{font-size:1.12rem}.monthly-games-heading p{font-size:.7rem;line-height:1.5}.monthly-progress-summary{width:100%;box-sizing:border-box;padding:9px 10px}.monthly-progress-summary.is-pending{grid-template-columns:auto 1fr;flex-basis:auto}.monthly-games-grid{grid-template-columns:1fr;gap:12px}.monthly-game-card{border-radius:12px}.monthly-game-art{height:154px;padding:10px}.monthly-game-art-backdrop{inset:-24px!important;width:calc(100% + 48px)!important;height:calc(100% + 48px)!important;opacity:.2;filter:blur(22px) saturate(1.2)!important;transform:scale(1.05)}.monthly-game-art-image{max-width:66%!important;max-height:128px!important;filter:drop-shadow(0 8px 16px rgba(0,0,0,.3))!important}.monthly-game-status{top:8px;right:8px;max-width:58%;padding:4px 7px;font-size:.57rem;line-height:1.2;text-align:right}.monthly-game-status svg{width:11px;height:11px}.monthly-game-body{padding:13px}.monthly-game-body h3{font-size:.88rem;line-height:1.35}.monthly-game-description{min-height:0;margin-bottom:11px;font-size:.67rem;-webkit-line-clamp:2}.monthly-game-meta{gap:8px}.monthly-access-row{display:grid;grid-template-columns:minmax(0,1fr) 44px;gap:6px}.monthly-access-value{padding:8px 9px}.monthly-access-value>span{display:none}.monthly-access-value code{font-size:.69rem}.monthly-copy-button{min-width:44px;min-height:40px;padding:0;border-radius:8px}.monthly-copy-button span{display:none}.monthly-game-facts{display:grid;grid-template-columns:1fr 1fr;gap:7px 10px}.monthly-game-facts>span{font-size:.63rem}.monthly-game-points{justify-content:flex-start}.monthly-game-deadline{grid-template-columns:auto minmax(0,1fr);gap:4px 6px!important;padding:8px}.monthly-game-deadline strong{font-size:.57rem}.monthly-game-deadline time{grid-column:1/-1;justify-self:start;padding-left:19px;font-size:.62rem;text-align:left;white-space:normal}.monthly-game-link{min-height:40px;margin-top:10px;padding:9px 10px;font-size:.68rem}.light .monthly-game-art-backdrop{opacity:.16}}@media(max-width:380px){.monthly-games-panel{padding:10px}.monthly-game-art{height:144px}.monthly-game-art-image{max-width:62%!important;max-height:118px!important}.monthly-game-status{max-width:64%;font-size:.54rem}.monthly-game-facts{grid-template-columns:1fr}.monthly-access-row{grid-template-columns:minmax(0,1fr) 42px}.monthly-copy-button{min-width:42px}.monthly-game-deadline time{padding-left:18px}}.monthly-progress-summary.is-coming-soon{grid-template-columns:1fr;align-content:center;flex-basis:190px}.monthly-progress-summary.is-coming-soon strong{font-size:.86rem;color:#c4b5fd}.monthly-progress-summary.is-coming-soon span{font-size:.65rem;line-height:1.35}.monthly-games-coming-soon{display:flex;align-items:center;gap:16px;min-height:150px;padding:22px;border:1px dashed rgba(167,139,250,.28);border-radius:14px;background:linear-gradient(135deg,rgba(124,58,237,.08),rgba(15,23,42,.16));color:#dbe4f3}.monthly-coming-soon-icon{display:grid;place-items:center;flex:0 0 54px;width:54px;height:54px;border:1px solid rgba(167,139,250,.3);border-radius:16px;background:rgba(124,58,237,.12);color:#c4b5fd}.monthly-coming-soon-icon svg{width:25px;height:25px}.monthly-coming-soon-label{display:inline-block;margin-bottom:5px;color:#a78bfa;font-size:.62rem;font-weight:800;letter-spacing:.07em;text-transform:uppercase}.monthly-games-coming-soon h3{margin:0 0 6px;color:#f8fbff;font-size:1rem;line-height:1.35}.monthly-games-coming-soon p{max-width:700px;margin:0;color:var(--arcade-muted);font-size:.72rem;line-height:1.55}.light .monthly-progress-summary.is-coming-soon strong{color:#6d28d9}.light .monthly-games-coming-soon{border-color:rgba(109,40,217,.2);background:linear-gradient(135deg,#faf5ff,#f8fafc);color:#334155}.light .monthly-coming-soon-icon{border-color:#ddd6fe;background:#f5f3ff;color:#7c3aed}.light .monthly-games-coming-soon h3{color:#172033}.light .monthly-games-coming-soon p{color:#64748b}@media(max-width:680px){.monthly-progress-summary.is-coming-soon{flex-basis:auto}.monthly-games-coming-soon{align-items:flex-start;gap:12px;min-height:0;padding:16px}.monthly-coming-soon-icon{flex-basis:44px;width:44px;height:44px;border-radius:13px}.monthly-coming-soon-icon svg{width:21px;height:21px}.monthly-games-coming-soon h3{font-size:.91rem}.monthly-games-coming-soon p{font-size:.68rem}} +.monthly-games-host{margin-top:14px;scroll-margin-top:92px}.monthly-games-panel{position:relative;margin:0 0 14px;padding:20px;border:1px solid rgba(124,58,237,.24);border-radius:14px;background:linear-gradient(145deg,rgba(12,20,39,.96),rgba(17,28,50,.94));box-shadow:0 16px 40px rgba(2,8,23,.18)}.monthly-games-panel.is-loading{display:flex;align-items:center;justify-content:center;gap:10px;min-height:92px;color:var(--arcade-muted);font-size:.78rem}.monthly-games-panel.is-loading svg{width:18px;height:18px;color:#a78bfa}.monthly-games-heading{display:flex;align-items:flex-start;justify-content:space-between;gap:18px;margin-bottom:17px}.monthly-games-heading>div:first-child{min-width:0}.monthly-games-kicker{display:flex;align-items:center;gap:7px;margin-bottom:7px;color:#a78bfa;font-size:.68rem;font-weight:800;letter-spacing:.08em;text-transform:uppercase}.monthly-games-kicker svg{width:15px;height:15px}.monthly-games-heading h2{margin:0 0 6px;color:#f8fbff;font-size:1.28rem;line-height:1.25;text-transform:capitalize}.monthly-games-heading p{max-width:700px;margin:0;color:var(--arcade-muted);font-size:.74rem;line-height:1.55}.monthly-progress-summary{display:grid;grid-template-columns:auto auto;align-items:baseline;gap:2px 7px;flex:0 0 150px;padding:10px 12px;border:1px solid rgba(167,139,250,.2);border-radius:10px;background:rgba(9,15,31,.58)}.monthly-progress-summary.is-pending{grid-template-columns:auto 1fr;flex-basis:210px}.monthly-progress-summary strong{color:#fff;font-size:1.3rem}.monthly-progress-summary span{color:var(--arcade-muted);font-size:.68rem}.monthly-progress-summary i{grid-column:1/-1;height:6px;overflow:hidden;border-radius:999px;background:#18233b}.monthly-progress-summary i b{display:block;height:100%;border-radius:inherit;background:linear-gradient(90deg,#7c3aed,#22c55e);transition:width .3s ease}.monthly-games-grid{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:14px;align-items:stretch}.monthly-game-card{display:flex;min-width:0;min-height:100%;overflow:hidden;flex-direction:column;border:1px solid rgba(148,163,184,.14);border-radius:14px;background:linear-gradient(180deg,rgba(10,19,36,.94),rgba(6,14,28,.98));box-shadow:inset 0 1px 0 rgba(255,255,255,.02);transition:transform .18s ease,border-color .18s ease,box-shadow .18s ease}.monthly-game-card:hover{transform:translateY(-2px);border-color:rgba(167,139,250,.38);box-shadow:0 14px 30px rgba(2,8,23,.24)}.monthly-game-card.is-complete{border-color:rgba(34,197,94,.32);box-shadow:inset 0 1px 0 rgba(34,197,94,.05)}.monthly-game-art{position:relative;display:flex;height:178px;align-items:center;justify-content:center;overflow:hidden;padding:14px;border-bottom:1px solid rgba(148,163,184,.08);background:radial-gradient(circle at 50% 44%,rgba(66,85,120,.22),rgba(8,15,30,.98) 70%);color:#a78bfa}.monthly-game-art>img{display:block;width:auto!important;height:auto!important;max-width:72%!important;max-height:150px!important;object-fit:contain!important;object-position:center!important;background:transparent!important;filter:drop-shadow(0 8px 16px rgba(0,0,0,.18))}.monthly-game-art>svg{width:34px;height:34px}.monthly-game-art-backdrop{position:absolute!important;inset:-34px!important;width:calc(100% + 68px)!important;height:calc(100% + 68px)!important;max-width:none!important;max-height:none!important;object-fit:cover!important;opacity:.23;filter:blur(28px) saturate(1.3)!important;transform:scale(1.08);pointer-events:none}.monthly-game-art::after{content:"";position:absolute;inset:0;background:linear-gradient(180deg,rgba(6,14,28,.2),rgba(6,14,28,.55));pointer-events:none}.monthly-game-art-image{position:relative;z-index:1;max-width:76%!important;max-height:154px!important;filter:drop-shadow(0 10px 20px rgba(0,0,0,.34))!important}.monthly-game-status{position:absolute;z-index:2;top:10px;right:10px;display:flex;align-items:center;gap:5px;padding:5px 8px;border:1px solid rgba(251,191,36,.32);border-radius:999px;background:rgba(8,15,30,.9);color:#fbbf24;font-size:.61rem;font-weight:800;backdrop-filter:blur(8px)}.monthly-game-status svg{width:12px;height:12px}.monthly-game-status.is-complete{border-color:rgba(34,197,94,.42);color:#4ade80}.monthly-game-status.is-pending{border-color:rgba(96,165,250,.34);color:#93c5fd}.monthly-game-body{display:flex;min-height:0;flex:1;flex-direction:column;padding:15px 15px 14px}.monthly-game-body h3{margin:0 0 7px;color:#f4f8ff;font-size:.91rem;line-height:1.38}.monthly-game-description{display:-webkit-box;overflow:hidden;min-height:3.25em;margin:0 0 13px;color:#93a4bd;font-size:.69rem;line-height:1.55;-webkit-box-orient:vertical;-webkit-line-clamp:3}.monthly-game-meta{display:grid;gap:9px;margin-top:auto}.monthly-access-row{display:flex;align-items:stretch;gap:7px}.monthly-access-value{display:flex;min-width:0;flex:1;align-items:center;gap:8px;padding:7px 9px;border:1px solid rgba(167,139,250,.2);border-radius:8px;background:rgba(124,58,237,.09)}.monthly-access-value>span{flex:0 0 auto;color:#a78bfa;font-size:.57rem;font-weight:800;letter-spacing:.05em;text-transform:uppercase}.monthly-access-value code{min-width:0;overflow:hidden;color:#f5f3ff;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:.7rem;font-weight:800;text-overflow:ellipsis;white-space:nowrap}.monthly-copy-button{display:inline-flex;flex:0 0 auto;align-items:center;justify-content:center;gap:5px;min-width:68px;padding:0 10px;border:1px solid rgba(167,139,250,.28);border-radius:8px;background:rgba(124,58,237,.14);color:#c4b5fd;font-size:.61rem;font-weight:800;cursor:pointer;transition:border-color .16s ease,background .16s ease,color .16s ease}.monthly-copy-button:hover{border-color:rgba(196,181,253,.55);background:rgba(124,58,237,.22);color:#ede9fe}.monthly-copy-button svg{width:13px;height:13px}.monthly-copy-button.is-copied{border-color:rgba(34,197,94,.42);background:rgba(34,197,94,.1);color:#86efac}.monthly-game-facts{display:flex;flex-wrap:wrap;gap:7px 14px;padding:0 1px}.monthly-game-facts>span{display:flex;align-items:center;gap:5px;color:#9fb0c6;font-size:.65rem}.monthly-game-facts svg{width:13px;height:13px;color:#8b5cf6}.monthly-game-points{color:#fbbf24!important;font-weight:800}.monthly-game-points svg{color:#f59e0b!important}.monthly-game-deadline{display:grid!important;grid-template-columns:auto auto minmax(0,1fr);align-items:center;gap:6px!important;padding:8px 9px;border:1px solid rgba(251,191,36,.26);border-radius:8px;background:linear-gradient(90deg,rgba(120,53,15,.18),rgba(69,26,3,.08));color:#fde68a!important}.monthly-game-deadline svg{width:13px;height:13px;color:#f59e0b!important}.monthly-game-deadline strong{font-size:.59rem;letter-spacing:.05em;text-transform:uppercase}.monthly-game-deadline time{justify-self:end;overflow:hidden;color:#fef3c7;font-size:.64rem;font-weight:750;text-align:right;text-overflow:ellipsis;white-space:nowrap}.monthly-game-actions{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:8px;margin-top:11px}.monthly-game-actions>.monthly-lab-source,.monthly-game-actions>.monthly-game-link{display:flex;width:100%;min-width:0;min-height:40px;align-items:center;justify-content:center;gap:6px;margin-top:0;box-sizing:border-box;padding:8px 10px;border-radius:8px;font-family:inherit;font-size:.68rem;font-weight:800;line-height:1.2;text-decoration:none;white-space:nowrap}.monthly-game-link{display:flex;align-items:center;justify-content:center;gap:6px;margin-top:11px;padding:8px 10px;border:1px solid rgba(124,58,237,.28);border-radius:8px;background:rgba(124,58,237,.11);color:#c4b5fd;font-size:.68rem;font-weight:800;text-decoration:none}.monthly-game-link:hover{border-color:rgba(139,92,246,.52);background:rgba(124,58,237,.18);color:#ede9fe}.monthly-game-link svg{width:13px;height:13px}.light .monthly-games-panel{border-color:rgba(109,40,217,.18);background:linear-gradient(145deg,rgba(255,255,255,.98),rgba(248,250,252,.98));box-shadow:0 14px 34px rgba(15,23,42,.08)}.light .monthly-games-heading h2,.light .monthly-game-body h3,.light .monthly-progress-summary strong{color:#172033}.light .monthly-progress-summary{border-color:rgba(109,40,217,.16);background:#f8fafc}.light .monthly-progress-summary i{background:#e2e8f0}.light .monthly-game-card{border-color:#e2e8f0;background:linear-gradient(180deg,#fff,#fbfdff)}.light .monthly-game-card.is-complete{border-color:rgba(22,163,74,.28)}.light .monthly-game-art{border-bottom-color:#e5e7eb;background:radial-gradient(circle at 50% 42%,#f8fafc,#eef2f7)}.light .monthly-game-art::after{background:linear-gradient(180deg,rgba(255,255,255,.15),rgba(255,255,255,.62))}.light .monthly-game-art-backdrop{opacity:.2}.light .monthly-game-description,.light .monthly-game-facts>span{color:#64748b}.light .monthly-game-points{color:#b45309!important}.light .monthly-game-points svg{color:#d97706!important}.light .monthly-access-value{border-color:#ddd6fe;background:#faf5ff}.light .monthly-access-value>span{color:#7c3aed}.light .monthly-access-value code{color:#4c1d95}.light .monthly-copy-button{border-color:#ddd6fe;background:#f5f3ff;color:#6d28d9}.light .monthly-copy-button:hover{background:#ede9fe}.light .monthly-game-deadline{border-color:#fde68a;background:linear-gradient(90deg,#fffbeb,#fffdf5);color:#92400e!important}.light .monthly-game-deadline time{color:#78350f}.light .monthly-game-link{border-color:rgba(109,40,217,.2);background:#f5f3ff;color:#6d28d9}.light .monthly-game-link:hover{background:#ede9fe;color:#5b21b6}@media(max-width:1050px){.monthly-games-grid{grid-template-columns:repeat(2,minmax(0,1fr))}}@media(max-width:680px){.monthly-games-host{margin-top:10px;scroll-margin-top:76px}.monthly-games-panel{padding:13px;border-radius:12px}.monthly-games-heading{display:grid;gap:12px;margin-bottom:14px}.monthly-games-heading h2{font-size:1.12rem}.monthly-games-heading p{font-size:.7rem;line-height:1.5}.monthly-progress-summary{width:100%;box-sizing:border-box;padding:9px 10px}.monthly-progress-summary.is-pending{grid-template-columns:auto 1fr;flex-basis:auto}.monthly-games-grid{grid-template-columns:1fr;gap:12px}.monthly-game-card{border-radius:12px}.monthly-game-art{height:154px;padding:10px}.monthly-game-art-backdrop{inset:-24px!important;width:calc(100% + 48px)!important;height:calc(100% + 48px)!important;opacity:.2;filter:blur(22px) saturate(1.2)!important;transform:scale(1.05)}.monthly-game-art-image{max-width:66%!important;max-height:128px!important;filter:drop-shadow(0 8px 16px rgba(0,0,0,.3))!important}.monthly-game-status{top:8px;right:8px;max-width:58%;padding:4px 7px;font-size:.57rem;line-height:1.2;text-align:right}.monthly-game-status svg{width:11px;height:11px}.monthly-game-body{padding:13px}.monthly-game-body h3{font-size:.88rem;line-height:1.35}.monthly-game-description{min-height:0;margin-bottom:11px;font-size:.67rem;-webkit-line-clamp:2}.monthly-game-meta{gap:8px}.monthly-access-row{display:grid;grid-template-columns:minmax(0,1fr) 44px;gap:6px}.monthly-access-value{padding:8px 9px}.monthly-access-value>span{display:none}.monthly-access-value code{font-size:.69rem}.monthly-copy-button{min-width:44px;min-height:40px;padding:0;border-radius:8px}.monthly-copy-button span{display:none}.monthly-game-facts{display:grid;grid-template-columns:1fr 1fr;gap:7px 10px}.monthly-game-facts>span{font-size:.63rem}.monthly-game-points{justify-content:flex-start}.monthly-game-deadline{grid-template-columns:auto minmax(0,1fr);gap:4px 6px!important;padding:8px}.monthly-game-deadline strong{font-size:.57rem}.monthly-game-deadline time{grid-column:1/-1;justify-self:start;padding-left:19px;font-size:.62rem;text-align:left;white-space:normal}.monthly-game-link{min-height:40px;margin-top:10px;padding:9px 10px;font-size:.68rem}.light .monthly-game-art-backdrop{opacity:.16}}@media(max-width:380px){.monthly-games-panel{padding:10px}.monthly-game-art{height:144px}.monthly-game-art-image{max-width:62%!important;max-height:118px!important}.monthly-game-status{max-width:64%;font-size:.54rem}.monthly-game-facts{grid-template-columns:1fr}.monthly-access-row{grid-template-columns:minmax(0,1fr) 42px}.monthly-copy-button{min-width:42px}.monthly-game-deadline time{padding-left:18px}}.monthly-progress-summary.is-coming-soon{grid-template-columns:1fr;align-content:center;flex-basis:190px}.monthly-progress-summary.is-coming-soon strong{font-size:.86rem;color:#c4b5fd}.monthly-progress-summary.is-coming-soon span{font-size:.65rem;line-height:1.35}.monthly-games-coming-soon{display:flex;align-items:center;gap:16px;min-height:150px;padding:22px;border:1px dashed rgba(167,139,250,.28);border-radius:14px;background:linear-gradient(135deg,rgba(124,58,237,.08),rgba(15,23,42,.16));color:#dbe4f3}.monthly-coming-soon-icon{display:grid;place-items:center;flex:0 0 54px;width:54px;height:54px;border:1px solid rgba(167,139,250,.3);border-radius:16px;background:rgba(124,58,237,.12);color:#c4b5fd}.monthly-coming-soon-icon svg{width:25px;height:25px}.monthly-coming-soon-label{display:inline-block;margin-bottom:5px;color:#a78bfa;font-size:.62rem;font-weight:800;letter-spacing:.07em;text-transform:uppercase}.monthly-games-coming-soon h3{margin:0 0 6px;color:#f8fbff;font-size:1rem;line-height:1.35}.monthly-games-coming-soon p{max-width:700px;margin:0;color:var(--arcade-muted);font-size:.72rem;line-height:1.55}.light .monthly-progress-summary.is-coming-soon strong{color:#6d28d9}.light .monthly-games-coming-soon{border-color:rgba(109,40,217,.2);background:linear-gradient(135deg,#faf5ff,#f8fafc);color:#334155}.light .monthly-coming-soon-icon{border-color:#ddd6fe;background:#f5f3ff;color:#7c3aed}.light .monthly-games-coming-soon h3{color:#172033}.light .monthly-games-coming-soon p{color:#64748b}@media(max-width:680px){.monthly-progress-summary.is-coming-soon{flex-basis:auto}.monthly-games-coming-soon{align-items:flex-start;gap:12px;min-height:0;padding:16px}.monthly-coming-soon-icon{flex-basis:44px;width:44px;height:44px;border-radius:13px}.monthly-coming-soon-icon svg{width:21px;height:21px}.monthly-games-coming-soon h3{font-size:.91rem}.monthly-games-coming-soon p{font-size:.68rem}} From 8e23b76e5521c501a1def32d5a2a0cddc08fcefa Mon Sep 17 00:00:00 2001 From: David Nguyen Date: Sat, 3 Oct 2026 14:24:55 +0700 Subject: [PATCH 11/30] refactor(monthly-labs): simplify mobile detail hierarchy --- components/monthly-labs/pages.tsx | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/components/monthly-labs/pages.tsx b/components/monthly-labs/pages.tsx index 5af8df304..0411bb981 100644 --- a/components/monthly-labs/pages.tsx +++ b/components/monthly-labs/pages.tsx @@ -167,18 +167,12 @@ export default function MonthlyLabsPage({ months, month, labs, lab }: Props) {
-
+
{m[detailState]} - {lab.points !== null && ( - - - )}
-
+
{lab.accessCode ? ( @@ -199,7 +193,7 @@ export default function MonthlyLabsPage({ months, month, labs, lab }: Props) {
-
+
{lab.deadline && Number.isFinite(Date.parse(lab.deadline)) @@ -209,14 +203,14 @@ export default function MonthlyLabsPage({ months, month, labs, lab }: Props) {
{lab.points !== null && ( -
+
{text(lab.points === 1 ? "arcadePoint" : "arcadePoints", { count: formatNumber(lab.points) })}
)} {lab.spotsRemaining !== null && ( -
+
{formatNumber(lab.spotsRemaining)}
From a42a7b53d533f4f0777e0577eb97f224200cbf78 Mon Sep 17 00:00:00 2001 From: David Nguyen Date: Sat, 3 Oct 2026 14:24:59 +0700 Subject: [PATCH 12/30] style(monthly-labs): compact mobile detail layout --- app/styles/site-ui.css | 68 ++++++++++++++++++++++++++++++++++++++---- 1 file changed, 62 insertions(+), 6 deletions(-) diff --git a/app/styles/site-ui.css b/app/styles/site-ui.css index ef3684436..30cec31c4 100644 --- a/app/styles/site-ui.css +++ b/app/styles/site-ui.css @@ -586,8 +586,7 @@ html[lang="en"] .hero-heading h1 { font-weight: 400; } @media (max-width: 640px) { .monthly-lab-archive-grid, - .monthly-lab-game-grid, - .monthly-lab-fact-grid { + .monthly-lab-game-grid { grid-template-columns: 1fr; } .monthly-lab-month-card { min-height: 92px; } @@ -597,16 +596,73 @@ html[lang="en"] .hero-heading h1 { font-weight: 400; } } .monthly-lab-game-art { aspect-ratio: 16 / 8; } .monthly-lab-detail-media { - min-height: 220px; - padding: 1.25rem; + height: 176px; + min-height: 0; + padding: .75rem 1rem; + } + .monthly-lab-detail-media :is(img,svg) { + max-height: 150px; + } + .monthly-lab-detail-body { + justify-content: flex-start; + padding: 1rem; + } + .monthly-lab-detail-meta { + margin-bottom: .15rem; + } + .monthly-lab-fact-grid { + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: .65rem; + margin-top: .9rem; + } + .monthly-lab-fact { + padding: .75rem; + border-radius: var(--ui-radius-sm); + } + .monthly-lab-fact-access, + .monthly-lab-fact-deadline { + grid-column: 1 / -1; + } + .monthly-lab-fact-points:last-child, + .monthly-lab-fact-places:nth-child(3):last-child { + grid-column: 1 / -1; + } + .monthly-lab-fact dt { + margin-bottom: .35rem; + font-size: .68rem; + } + .monthly-lab-fact dd { + gap: .45rem; + font-size: .79rem; + line-height: 1.4; + } + .monthly-lab-fact-access dd { + flex-wrap: nowrap; + } + .monthly-lab-fact-access code { + min-width: 0; + flex: 1; + font-size: .8rem; + } + .monthly-lab-fact-access .monthly-copy-button { + min-width: 42px; + min-height: 38px; + padding: 0 .7rem; } - .monthly-lab-detail-body { padding: 1.2rem; } .monthly-lab-detail-actions { display: grid; - grid-template-columns: 1fr; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: .65rem; + margin-top: .9rem; } .monthly-lab-detail-actions :is(.content-primary-action,.monthly-lab-source) { width: 100%; + min-width: 0; + min-height: 42px; + padding: .6rem .7rem; + font-size: .78rem; + line-height: 1.2; + text-align: center; } } From c47753e9f0de131e522085e8cc9e67444ac7fef2 Mon Sep 17 00:00:00 2001 From: David Nguyen Date: Mon, 5 Oct 2026 11:51:42 +0700 Subject: [PATCH 13/30] sync: inherit app/layout.tsx from #87 --- app/layout.tsx | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/app/layout.tsx b/app/layout.tsx index 085311c09..49b056c5e 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -4,6 +4,7 @@ import localFont from "next/font/local" import type { Metadata, Viewport } from "next" import WebsiteLanguage from "@/components/i18n/website-language" import CookieConsent from "@/components/privacy/cookie-consent" +import BackToTop from "@/components/back-to-top" import { ThemeProvider } from "@/components/theme-provider" import ThemeToggle from "@/components/theme-toggle" import { WEBSITE_SITE_URL } from "@/lib/website-i18n" @@ -29,10 +30,7 @@ import "./styles/facilitator-participation.css" import "./styles/facilitator-launcher-visibility.css" import "./styles/cookie-consent.css" import "./styles/home-refactor.css" -import "./styles/shared-profile.css" -import "./styles/profile-share.css" -import "./styles/bonus-milestone.css" -import "./styles/site-ui.css" +import "./styles/back-to-top.css" const arcadeRequestDedupeBootstrap = readFileSync( path.join(process.cwd(), "scripts", "arcade-request-dedupe.js"), @@ -240,6 +238,7 @@ export default function RootLayout({ children }: Readonly<{ children: React.Reac {children} + Date: Mon, 5 Oct 2026 11:51:46 +0700 Subject: [PATCH 14/30] sync: inherit app/redesign-calculator.tsx from #87 --- app/redesign-calculator.tsx | 53 ++++++++++++++++++++++++++++++------- 1 file changed, 44 insertions(+), 9 deletions(-) diff --git a/app/redesign-calculator.tsx b/app/redesign-calculator.tsx index 0c72b1052..417b2b052 100644 --- a/app/redesign-calculator.tsx +++ b/app/redesign-calculator.tsx @@ -7,7 +7,7 @@ import DashboardTiers from "@/components/arcade/dashboard-tiers" import GuestDashboard from "@/components/arcade/guest-dashboard" import PreviewModeToolbar from "@/components/arcade/preview-mode-toolbar" import { readStoredDashboard, type DashboardViewMode } from "@/components/arcade/dashboard-state" -import { useTierSlotHistory } from "@/components/arcade/tier-slot-history" +import { SlotChangeBadge, TierSlotHistoryPanel, useTierSlotHistory } from "@/components/arcade/tier-slot-history" import { getWebsiteLocale, getWebsiteLocaleFromPathname, @@ -908,14 +908,49 @@ export default function RedesignCalculator({ )} - +
From 95ca2260ca98a854d1228cc132fd358941206378 Mon Sep 17 00:00:00 2001 From: David Nguyen Date: Mon, 5 Oct 2026 11:51:49 +0700 Subject: [PATCH 15/30] sync: inherit app/styles/back-to-top.css from #87 --- app/styles/back-to-top.css | 55 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 app/styles/back-to-top.css diff --git a/app/styles/back-to-top.css b/app/styles/back-to-top.css new file mode 100644 index 000000000..bbb87c6b2 --- /dev/null +++ b/app/styles/back-to-top.css @@ -0,0 +1,55 @@ +.back-to-top { + position: fixed; + right: 22px; + bottom: 22px; + z-index: 92; + display: grid; + place-items: center; + width: 42px; + height: 42px; + padding: 0; + border: 1px solid rgba(139, 92, 246, 0.45); + border-radius: 50%; + background: rgba(91, 33, 182, 0.94); + color: #fff; + box-shadow: 0 10px 30px rgba(0, 0, 0, 0.28); + cursor: pointer; + backdrop-filter: blur(12px); + transition: transform 160ms ease, background 160ms ease, box-shadow 160ms ease; +} + +.back-to-top:hover { + transform: translateY(-2px); + background: rgba(109, 40, 217, 0.98); + box-shadow: 0 14px 34px rgba(0, 0, 0, 0.34); +} + +.back-to-top:focus-visible { + outline: 2px solid #67e8f9; + outline-offset: 3px; +} + +.back-to-top svg { + width: 18px; + height: 18px; +} + +html.light .back-to-top { + border-color: rgba(109, 40, 217, 0.3); + background: rgba(109, 40, 217, 0.92); +} + +@media (max-width: 640px) { + .back-to-top { + right: 14px; + bottom: calc(82px + env(safe-area-inset-bottom)); + width: 40px; + height: 40px; + } +} + +@media (prefers-reduced-motion: reduce) { + .back-to-top { + transition: none; + } +} From a34756610bc1d4791e405474e421c26ffe562bc2 Mon Sep 17 00:00:00 2001 From: David Nguyen Date: Mon, 5 Oct 2026 11:51:53 +0700 Subject: [PATCH 16/30] sync: inherit app/styles/home-refactor.css from #87 --- app/styles/home-refactor.css | 694 +++++++++++++++++++++++++---------- 1 file changed, 508 insertions(+), 186 deletions(-) diff --git a/app/styles/home-refactor.css b/app/styles/home-refactor.css index 62039e653..6b514795b 100644 --- a/app/styles/home-refactor.css +++ b/app/styles/home-refactor.css @@ -1,51 +1,180 @@ /* Home dashboard visual refactor (v2). - * Scoped to .arcade-dashboard-v2 so widgets and internal pages retain their own themes. + * The established profile dashboard keeps the legacy visual system. + * V2 surface/layout overrides are intentionally scoped to guest mode; preview controls + * remain shared so PR-only Guest/Profile switching stays usable. */ -.arcade-dashboard-v2 { - --v2-shell: var(--ui-width); - --v2-gutter: var(--ui-gutter); - --v2-radius-lg: var(--ui-radius-lg); - --v2-radius-md: var(--ui-radius); - --v2-radius-sm: var(--ui-radius-sm); - --v2-surface: var(--ui-surface); - --v2-surface-strong: var(--ui-surface); - --v2-surface-soft: var(--ui-soft); - --v2-border: var(--ui-border); - --v2-border-strong: var(--ui-accent-border); - --v2-shadow: var(--ui-shadow); - --v2-shadow-soft: var(--ui-shadow); -} - -.arcade-dashboard-v2 .program-countdown-host, -.arcade-dashboard-v2 .extension-strip, -.arcade-dashboard-v2 .dashboard-shell, -.arcade-dashboard-v2 .dashboard-empty-state, -.arcade-dashboard-v2 .arcade-footer { +.arcade-dashboard-v2[data-dashboard-view="guest"] { + --v2-shell: 1200px; + --v2-gutter: 24px; + --v2-radius-lg: 20px; + --v2-radius-md: 16px; + --v2-radius-sm: 12px; + --v2-surface: rgba(9, 17, 36, 0.78); + --v2-surface-strong: rgba(10, 19, 40, 0.94); + --v2-surface-soft: rgba(15, 27, 52, 0.62); + --v2-border: rgba(148, 163, 184, 0.14); + --v2-border-strong: rgba(139, 92, 246, 0.28); + --v2-shadow: 0 12px 32px rgba(0, 0, 0, 0.16); + --v2-shadow-soft: 0 4px 16px rgba(0, 0, 0, 0.10); + background: + radial-gradient(circle at 82% 5%, rgba(99, 102, 241, 0.16), transparent 31rem), + radial-gradient(circle at 12% 24%, rgba(14, 165, 233, 0.11), transparent 29rem), + linear-gradient(180deg, #050918 0%, #071126 48%, #050918 100%); +} + +html.light .arcade-dashboard-v2[data-dashboard-view="guest"] { + --v2-surface: rgba(255, 255, 255, 0.82); + --v2-surface-strong: rgba(255, 255, 255, 0.96); + --v2-surface-soft: rgba(248, 250, 252, 0.82); + --v2-border: rgba(100, 116, 139, 0.15); + --v2-border-strong: rgba(79, 70, 229, 0.20); + --v2-shadow: 0 22px 58px rgba(51, 65, 85, 0.11); + --v2-shadow-soft: 0 10px 30px rgba(51, 65, 85, 0.08); + background: + radial-gradient(circle at 82% 5%, rgba(99, 102, 241, 0.11), transparent 31rem), + radial-gradient(circle at 12% 24%, rgba(14, 165, 233, 0.08), transparent 29rem), + linear-gradient(180deg, #f8faff 0%, #eef3ff 48%, #f8faff 100%); +} + +.arcade-dashboard-v2[data-dashboard-view="guest"] .arcade-header, +.arcade-dashboard-v2[data-dashboard-view="guest"] .arcade-hero, +.arcade-dashboard-v2[data-dashboard-view="guest"] .program-countdown-host, +.arcade-dashboard-v2[data-dashboard-view="guest"] .extension-strip, +.arcade-dashboard-v2[data-dashboard-view="guest"] .dashboard-shell, +.arcade-dashboard-v2[data-dashboard-view="guest"] .dashboard-empty-state, +.arcade-dashboard-v2[data-dashboard-view="guest"] .arcade-footer { width: min(var(--v2-shell), calc(100% - (var(--v2-gutter) * 2))); } -.arcade-dashboard-v2 .arcade-nav { +/* Header: compact floating workspace bar instead of a full-width divider. */ +.arcade-dashboard-v2[data-dashboard-view="guest"] .arcade-header { + top: 10px; + min-height: 64px; + margin-top: 10px; + padding: 0 12px 0 14px; + border: 1px solid var(--v2-border); + border-radius: var(--v2-radius-md); + background: var(--v2-surface-strong); + box-shadow: var(--v2-shadow-soft); + backdrop-filter: blur(20px) saturate(130%); +} + +.arcade-dashboard-v2[data-dashboard-view="guest"] .arcade-brand-mark { + width: 36px; + height: 36px; + border-radius: 11px; +} + +.arcade-dashboard-v2[data-dashboard-view="guest"] .arcade-brand-copy { + font-size: 0.68rem; +} + +.arcade-dashboard-v2[data-dashboard-view="guest"] .arcade-brand em { + padding: 4px 6px; + border-radius: 6px; +} + +.arcade-dashboard-v2[data-dashboard-view="guest"] .arcade-nav { gap: 2px; } -.arcade-dashboard-v2 .arcade-nav a { +.arcade-dashboard-v2[data-dashboard-view="guest"] .arcade-nav a { padding: 20px 11px 18px; border-radius: 9px; font-size: 0.8rem; } -.arcade-dashboard-v2 .arcade-nav a::after { +.arcade-dashboard-v2[data-dashboard-view="guest"] .arcade-nav a::after { left: 11px; right: 11px; } -.arcade-dashboard-v2 .header-store-link { +.arcade-dashboard-v2[data-dashboard-view="guest"] .arcade-header-actions { + gap: 7px; +} + +.arcade-dashboard-v2[data-dashboard-view="guest"] .header-store-link { min-height: 38px; border-radius: 10px; } -.arcade-dashboard-v2 .dashboard-view-switch { +/* Hero: clearer hierarchy and a stronger analyzer focal point. */ +.arcade-dashboard-v2[data-dashboard-view="guest"] .arcade-hero { + grid-template-columns: minmax(0, 1.08fr) minmax(360px, 0.92fr); + gap: clamp(32px, 5vw, 72px); + align-items: center; + padding: clamp(54px, 7vw, 86px) 0 38px; +} + +.arcade-dashboard-v2[data-dashboard-view="guest"] .hero-heading { + max-width: 690px; +} + +.arcade-dashboard-v2[data-dashboard-view="guest"] .pixel-kicker { + width: fit-content; + padding: 7px 10px; + border: 1px solid rgba(34, 211, 238, 0.17); + border-radius: 999px; + background: rgba(8, 145, 178, 0.06); +} + +html.light .arcade-dashboard-v2[data-dashboard-view="guest"] .pixel-kicker { + border-color: rgba(2, 132, 199, 0.15); + background: rgba(224, 242, 254, 0.7); +} + +.arcade-dashboard-v2[data-dashboard-view="guest"] .hero-heading h1 { + margin: 22px 0 18px; + max-width: 760px; + font-size: clamp(2.25rem, 4.4vw, 4.35rem); + line-height: 1.13; + letter-spacing: -0.055em; + text-wrap: balance; +} + +.arcade-dashboard-v2[data-dashboard-view="guest"] .hero-description { + max-width: 610px; + font-size: 1rem; + line-height: 1.72; +} + +.arcade-dashboard-v2[data-dashboard-view="guest"] .trust-pills { + gap: 8px; + margin-top: 22px; +} + +.arcade-dashboard-v2[data-dashboard-view="guest"] .trust-pills span { + min-height: 34px; + padding: 7px 11px; + border-radius: 999px; +} + +.arcade-dashboard-v2[data-dashboard-view="guest"] .profile-analyzer-card { + position: relative; + padding: 28px; + overflow: hidden; + border-color: var(--v2-border-strong); + border-radius: var(--v2-radius-lg); + background: var(--v2-surface-strong); + box-shadow: var(--v2-shadow); +} + +.arcade-dashboard-v2[data-dashboard-view="guest"] .profile-analyzer-card::before { + content: ""; + position: absolute; + inset: 0 0 auto; + height: 2px; + background: linear-gradient(90deg, transparent, var(--arcade-purple), var(--arcade-cyan), transparent); + opacity: 0.85; +} + +.arcade-dashboard-v2[data-dashboard-view="guest"] .analyzer-title { + margin-bottom: 18px; + font-size: 0.72rem; +} + +.arcade-dashboard-v2[data-dashboard-view="guest"] .dashboard-view-switch { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 6px; @@ -56,7 +185,7 @@ background: var(--v2-surface-soft); } -.arcade-dashboard-v2 .dashboard-view-switch button { +.arcade-dashboard-v2[data-dashboard-view="guest"] .dashboard-view-switch button { display: inline-flex; align-items: center; justify-content: center; @@ -74,37 +203,59 @@ transition: background 160ms ease, border-color 160ms ease, color 160ms ease, transform 160ms ease; } -.arcade-dashboard-v2 .dashboard-view-switch button svg { +.arcade-dashboard-v2[data-dashboard-view="guest"] .dashboard-view-switch button svg { width: 16px; height: 16px; } -.arcade-dashboard-v2 .dashboard-view-switch button:hover { +.arcade-dashboard-v2[data-dashboard-view="guest"] .dashboard-view-switch button:hover { color: var(--arcade-text); } -.arcade-dashboard-v2 .dashboard-view-switch button.is-active { +.arcade-dashboard-v2[data-dashboard-view="guest"] .dashboard-view-switch button.is-active { border-color: var(--v2-border-strong); background: var(--v2-surface-strong); color: var(--arcade-text); box-shadow: 0 6px 18px rgba(0, 0, 0, 0.12); } -.arcade-dashboard-v2 .dashboard-view-switch button:focus-visible { +.arcade-dashboard-v2[data-dashboard-view="guest"] .dashboard-view-switch button:focus-visible { outline: 2px solid var(--arcade-cyan); outline-offset: 2px; } +.arcade-dashboard-v2[data-dashboard-view="guest"] .profile-analyzer-card form { + grid-template-columns: minmax(0, 1fr); + gap: 10px; +} + +.arcade-dashboard-v2[data-dashboard-view="guest"] .profile-input { + min-height: 58px; + padding: 0 15px; + border-radius: var(--v2-radius-sm); + background: var(--v2-surface-soft); +} + +.arcade-dashboard-v2[data-dashboard-view="guest"] .analyze-button { + min-height: 50px; + border-radius: var(--v2-radius-sm); + box-shadow: 0 12px 28px rgba(124, 58, 237, 0.22); +} + +.arcade-dashboard-v2[data-dashboard-view="guest"] .analyzer-help-row { + margin-top: 14px; +} + /* Countdown uses the same desktop shell and card language as the rest of the workspace. */ -.arcade-dashboard-v2 .program-countdown-host { +.arcade-dashboard-v2[data-dashboard-view="guest"] .program-countdown-host { margin-bottom: 22px; } -.arcade-dashboard-v2 .program-countdown-grid { +.arcade-dashboard-v2[data-dashboard-view="guest"] .program-countdown-grid { gap: 16px; } -.arcade-dashboard-v2 .program-countdown-card { +.arcade-dashboard-v2[data-dashboard-view="guest"] .program-countdown-card { border-color: var(--v2-border); border-radius: var(--v2-radius-md); background: @@ -113,19 +264,19 @@ box-shadow: var(--v2-shadow-soft); } -html.light .arcade-dashboard-v2 .program-countdown-card { +html.light .arcade-dashboard-v2[data-dashboard-view="guest"] .program-countdown-card { background: radial-gradient(circle at 10% 10%, var(--countdown-accent-soft), transparent 52%), var(--v2-surface); } -.arcade-dashboard-v2 .program-countdown-part { +.arcade-dashboard-v2[data-dashboard-view="guest"] .program-countdown-part { border-color: var(--v2-border); background: var(--v2-surface-soft); } /* Secondary browser CTA becomes part of the same visual system. */ -.arcade-dashboard-v2 .extension-strip { +.arcade-dashboard-v2[data-dashboard-view="guest"] .extension-strip { min-height: 70px; margin-top: 4px; margin-bottom: 22px; @@ -136,71 +287,71 @@ html.light .arcade-dashboard-v2 .program-countdown-card { box-shadow: var(--v2-shadow-soft); } -.arcade-dashboard-v2 .extension-store-mark { +.arcade-dashboard-v2[data-dashboard-view="guest"] .extension-store-mark { width: 42px; height: 42px; border-radius: 12px; } -.arcade-dashboard-v2 .extension-store-actions a { +.arcade-dashboard-v2[data-dashboard-view="guest"] .extension-store-actions a { min-height: 42px; border-radius: 10px; } /* Results: consistent rhythm, less empty height and stronger grouping. */ -.arcade-dashboard-v2 .dashboard-summary-grid, -.arcade-dashboard-v2 .dashboard-content-grid, -.arcade-dashboard-v2 .dashboard-bottom-grid { +.arcade-dashboard-v2[data-dashboard-view="guest"] .dashboard-summary-grid, +.arcade-dashboard-v2[data-dashboard-view="guest"] .dashboard-content-grid, +.arcade-dashboard-v2[data-dashboard-view="guest"] .dashboard-bottom-grid { gap: 16px; margin-bottom: 16px; } -.arcade-dashboard-v2 .dashboard-summary-grid { +.arcade-dashboard-v2[data-dashboard-view="guest"] .dashboard-summary-grid { grid-template-columns: minmax(0, 1.05fr) minmax(0, 1.05fr) minmax(0, 0.9fr); } -.arcade-dashboard-v2 .dashboard-content-grid { +.arcade-dashboard-v2[data-dashboard-view="guest"] .dashboard-content-grid { grid-template-columns: minmax(0, 1fr) 360px; } -.arcade-dashboard-v2 .dashboard-panel, -.arcade-dashboard-v2 .dashboard-empty-state { +.arcade-dashboard-v2[data-dashboard-view="guest"] .dashboard-panel, +.arcade-dashboard-v2[data-dashboard-view="guest"] .dashboard-empty-state { border-color: var(--v2-border); border-radius: var(--v2-radius-md); background: var(--v2-surface); box-shadow: var(--v2-shadow-soft); } -.arcade-dashboard-v2 .dashboard-panel { +.arcade-dashboard-v2[data-dashboard-view="guest"] .dashboard-panel { padding: 22px; } -.arcade-dashboard-v2 .dashboard-summary-grid > .dashboard-panel { +.arcade-dashboard-v2[data-dashboard-view="guest"] .dashboard-summary-grid > .dashboard-panel { min-height: 0; } -.arcade-dashboard-v2 .panel-title { +.arcade-dashboard-v2[data-dashboard-view="guest"] .panel-title { font-size: 0.71rem; letter-spacing: 0.055em; } -.arcade-dashboard-v2 .profile-overview { +.arcade-dashboard-v2[data-dashboard-view="guest"] .profile-overview { min-height: 0; margin: 20px 0; } -.arcade-dashboard-v2 .profile-photo { +.arcade-dashboard-v2[data-dashboard-view="guest"] .profile-photo { width: 76px; height: 76px; } -.arcade-dashboard-v2 .profile-stat-grid { +.arcade-dashboard-v2[data-dashboard-view="guest"] .profile-stat-grid { gap: 8px; margin-top: auto; padding-top: 16px; } -.arcade-dashboard-v2 .profile-stat-grid > div { +.arcade-dashboard-v2[data-dashboard-view="guest"] .profile-stat-grid > div { min-height: 82px; padding: 10px 8px; border: 1px solid var(--v2-border); @@ -208,62 +359,62 @@ html.light .arcade-dashboard-v2 .program-countdown-card { background: var(--v2-surface-soft); } -.arcade-dashboard-v2 .profile-stat-grid > div + div { +.arcade-dashboard-v2[data-dashboard-view="guest"] .profile-stat-grid > div + div { border-left: 1px solid var(--v2-border); } -.arcade-dashboard-v2 .points-total { +.arcade-dashboard-v2[data-dashboard-view="guest"] .points-total { margin: 20px 0 16px; } -.arcade-dashboard-v2 .points-total strong { +.arcade-dashboard-v2[data-dashboard-view="guest"] .points-total strong { font-size: 2.45rem; letter-spacing: -0.04em; } -.arcade-dashboard-v2 .point-composition-bar { +.arcade-dashboard-v2[data-dashboard-view="guest"] .point-composition-bar { height: 14px; margin-bottom: 14px; border-radius: 999px; } -.arcade-dashboard-v2 .point-breakdown-row { +.arcade-dashboard-v2[data-dashboard-view="guest"] .point-breakdown-row { min-height: 44px; padding: 9px 0; } -.arcade-dashboard-v2 .tier-status-main { +.arcade-dashboard-v2[data-dashboard-view="guest"] .tier-status-main { margin: 20px 0 16px; } -.arcade-dashboard-v2 .tier-availability-grid > div { +.arcade-dashboard-v2[data-dashboard-view="guest"] .tier-availability-grid > div { min-height: 82px; border-color: var(--v2-border); border-radius: var(--v2-radius-sm); background: var(--v2-surface-soft); } -.arcade-dashboard-v2 .badge-heading-row { +.arcade-dashboard-v2[data-dashboard-view="guest"] .badge-heading-row { align-items: flex-start; gap: 14px; } -.arcade-dashboard-v2 .badge-filters { +.arcade-dashboard-v2[data-dashboard-view="guest"] .badge-filters { gap: 6px; } -.arcade-dashboard-v2 .badge-filters button { +.arcade-dashboard-v2[data-dashboard-view="guest"] .badge-filters button { min-height: 34px; padding-inline: 12px; border-radius: 999px; } -.arcade-dashboard-v2 .earned-badge-grid { +.arcade-dashboard-v2[data-dashboard-view="guest"] .earned-badge-grid { grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 10px; } -.arcade-dashboard-v2 .earned-badge { +.arcade-dashboard-v2[data-dashboard-view="guest"] .earned-badge { min-height: 0; padding: 12px; border-color: var(--v2-border); @@ -273,21 +424,21 @@ html.light .arcade-dashboard-v2 .program-countdown-card { transition: transform 160ms ease, border-color 160ms ease, box-shadow 160ms ease; } -.arcade-dashboard-v2 .earned-badge:hover { +.arcade-dashboard-v2[data-dashboard-view="guest"] .earned-badge:hover { transform: translateY(-2px); border-color: var(--v2-border-strong); box-shadow: var(--v2-shadow-soft); } -.arcade-dashboard-v2 .earned-badge-art { +.arcade-dashboard-v2[data-dashboard-view="guest"] .earned-badge-art { border-radius: 11px; } -.arcade-dashboard-v2 .tier-list { +.arcade-dashboard-v2[data-dashboard-view="guest"] .tier-list { gap: 7px; } -.arcade-dashboard-v2 .tier-list-row { +.arcade-dashboard-v2[data-dashboard-view="guest"] .tier-list-row { min-height: 64px; padding: 10px 11px; border: 1px solid var(--v2-border); @@ -295,20 +446,20 @@ html.light .arcade-dashboard-v2 .program-countdown-card { background: var(--v2-surface-soft); } -.arcade-dashboard-v2 .tier-list-row.is-current { +.arcade-dashboard-v2[data-dashboard-view="guest"] .tier-list-row.is-current { border-color: var(--v2-border-strong); box-shadow: inset 3px 0 0 var(--arcade-purple); } -.arcade-dashboard-v2 .dashboard-bottom-grid { +.arcade-dashboard-v2[data-dashboard-view="guest"] .dashboard-bottom-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); } -.arcade-dashboard-v2 .compact-panel { +.arcade-dashboard-v2[data-dashboard-view="guest"] .compact-panel { min-height: 0; } -.arcade-dashboard-v2 .goal-progress { +.arcade-dashboard-v2[data-dashboard-view="guest"] .goal-progress { height: 9px; border-radius: 999px; } @@ -367,7 +518,7 @@ html.light .arcade-dashboard-v2 .preview-mode-toolbar { } /* Guest dashboard: compact, visual and intentionally separate from profile data. */ -.arcade-dashboard-v2 .dashboard-empty-state.guest-dashboard { +.arcade-dashboard-v2[data-dashboard-view="guest"] .dashboard-empty-state.guest-dashboard { display: grid; grid-template-columns: 1fr; gap: 20px; @@ -378,7 +529,7 @@ html.light .arcade-dashboard-v2 .preview-mode-toolbar { box-shadow: none; } -.arcade-dashboard-v2 .guest-dashboard-hero { +.arcade-dashboard-v2[data-dashboard-view="guest"] .guest-dashboard-hero { position: relative; isolation: isolate; display: grid; @@ -399,7 +550,7 @@ html.light .arcade-dashboard-v2 .preview-mode-toolbar { 0 18px 44px rgba(0, 0, 0, 0.16); } -.arcade-dashboard-v2 .guest-dashboard-hero::before { +.arcade-dashboard-v2[data-dashboard-view="guest"] .guest-dashboard-hero::before { content: ""; position: absolute; inset: 0; @@ -412,7 +563,7 @@ html.light .arcade-dashboard-v2 .preview-mode-toolbar { mask-image: linear-gradient(90deg, transparent, black 18%, black 82%, transparent); } -.arcade-dashboard-v2 .guest-dashboard-orb { +.arcade-dashboard-v2[data-dashboard-view="guest"] .guest-dashboard-orb { position: relative; display: grid; place-items: center; @@ -429,25 +580,25 @@ html.light .arcade-dashboard-v2 .preview-mode-toolbar { 0 0 34px rgba(255, 193, 7, 0.16); } -.arcade-dashboard-v2 .guest-dashboard-orb svg { +.arcade-dashboard-v2[data-dashboard-view="guest"] .guest-dashboard-orb svg { width: 38px; height: 38px; } -.arcade-dashboard-v2 .guest-dashboard-copy { +.arcade-dashboard-v2[data-dashboard-view="guest"] .guest-dashboard-copy { min-width: 0; display: grid; gap: 10px; } -.arcade-dashboard-v2 .guest-dashboard-copy strong { +.arcade-dashboard-v2[data-dashboard-view="guest"] .guest-dashboard-copy strong { color: var(--arcade-text); font-size: clamp(1.35rem, 2.3vw, 2rem); line-height: 1.15; letter-spacing: -0.035em; } -.arcade-dashboard-v2 .guest-dashboard-copy p { +.arcade-dashboard-v2[data-dashboard-view="guest"] .guest-dashboard-copy p { max-width: 620px; margin: 0; color: var(--arcade-muted); @@ -455,7 +606,7 @@ html.light .arcade-dashboard-v2 .preview-mode-toolbar { line-height: 1.65; } -.arcade-dashboard-v2 .guest-dashboard-decoration { +.arcade-dashboard-v2[data-dashboard-view="guest"] .guest-dashboard-decoration { align-self: stretch; display: grid; place-items: center; @@ -464,13 +615,13 @@ html.light .arcade-dashboard-v2 .preview-mode-toolbar { transform: rotate(-12deg) translateX(18px); } -.arcade-dashboard-v2 .guest-dashboard-decoration svg { +.arcade-dashboard-v2[data-dashboard-view="guest"] .guest-dashboard-decoration svg { width: 110px; height: 110px; stroke-width: 1.35; } -html.light .arcade-dashboard-v2 .guest-dashboard-hero { +html.light .arcade-dashboard-v2[data-dashboard-view="guest"] .guest-dashboard-hero { border-color: rgba(99, 102, 241, 0.24); background: radial-gradient(circle at 18% 50%, rgba(251, 191, 36, 0.13), transparent 25%), @@ -478,20 +629,20 @@ html.light .arcade-dashboard-v2 .guest-dashboard-hero { linear-gradient(135deg, rgba(255, 255, 255, 0.98), rgba(246, 248, 255, 0.96)); } -.arcade-dashboard-v2 .guest-tier-heading { +.arcade-dashboard-v2[data-dashboard-view="guest"] .guest-tier-heading { display: flex; align-items: center; justify-content: space-between; margin-top: 2px; } -.arcade-dashboard-v2 .guest-tier-heading > div { +.arcade-dashboard-v2[data-dashboard-view="guest"] .guest-tier-heading > div { display: flex; align-items: center; gap: 11px; } -.arcade-dashboard-v2 .guest-tier-heading-icon { +.arcade-dashboard-v2[data-dashboard-view="guest"] .guest-tier-heading-icon { display: grid; place-items: center; width: 36px; @@ -502,17 +653,17 @@ html.light .arcade-dashboard-v2 .guest-dashboard-hero { color: #c054ff; } -.arcade-dashboard-v2 .guest-tier-heading-icon svg { +.arcade-dashboard-v2[data-dashboard-view="guest"] .guest-tier-heading-icon svg { width: 18px; height: 18px; } -.arcade-dashboard-v2 .guest-tier-heading > div > div { +.arcade-dashboard-v2[data-dashboard-view="guest"] .guest-tier-heading > div > div { display: grid; gap: 2px; } -.arcade-dashboard-v2 .guest-tier-heading strong { +.arcade-dashboard-v2[data-dashboard-view="guest"] .guest-tier-heading strong { color: var(--arcade-text); font-size: 0.88rem; font-weight: 850; @@ -520,18 +671,18 @@ html.light .arcade-dashboard-v2 .guest-dashboard-hero { text-transform: uppercase; } -.arcade-dashboard-v2 .guest-tier-heading span { +.arcade-dashboard-v2[data-dashboard-view="guest"] .guest-tier-heading span { color: var(--arcade-muted); font-size: 0.68rem; } -.arcade-dashboard-v2 .empty-tier-grid { +.arcade-dashboard-v2[data-dashboard-view="guest"] .empty-tier-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px; } -.arcade-dashboard-v2 .guest-tier-card { +.arcade-dashboard-v2[data-dashboard-view="guest"] .guest-tier-card { --tier-accent: #8b5cf6; --tier-soft: rgba(139, 92, 246, 0.12); --tier-border: rgba(139, 92, 246, 0.30); @@ -549,45 +700,45 @@ html.light .arcade-dashboard-v2 .guest-dashboard-hero { box-shadow: none; } -.arcade-dashboard-v2 .shared-tier-panel .empty-tier-grid { +.arcade-dashboard-v2[data-dashboard-view="guest"] .shared-tier-panel .empty-tier-grid { grid-template-columns: minmax(0, 1fr); } -.arcade-dashboard-v2 .guest-tier-card.is-current { +.arcade-dashboard-v2[data-dashboard-view="guest"] .guest-tier-card.is-current { border-color: var(--tier-accent); background: linear-gradient(90deg, var(--tier-soft), transparent), var(--v2-surface-soft); box-shadow: inset 3px 0 0 var(--tier-accent); } -.arcade-dashboard-v2 .guest-tier-card.is-current .guest-tier-icon { +.arcade-dashboard-v2[data-dashboard-view="guest"] .guest-tier-card.is-current .guest-tier-icon { border-color: var(--tier-accent); } -.arcade-dashboard-v2 .guest-tier-card.tier-120 { +.arcade-dashboard-v2[data-dashboard-view="guest"] .guest-tier-card.tier-120 { --tier-accent: #ffd43b; --tier-soft: rgba(255, 212, 59, 0.09); --tier-border: rgba(255, 212, 59, 0.28); } -.arcade-dashboard-v2 .guest-tier-card.tier-95 { +.arcade-dashboard-v2[data-dashboard-view="guest"] .guest-tier-card.tier-95 { --tier-accent: #c084fc; --tier-soft: rgba(192, 132, 252, 0.10); --tier-border: rgba(192, 132, 252, 0.30); } -.arcade-dashboard-v2 .guest-tier-card.tier-75 { +.arcade-dashboard-v2[data-dashboard-view="guest"] .guest-tier-card.tier-75 { --tier-accent: #3b82f6; --tier-soft: rgba(59, 130, 246, 0.10); --tier-border: rgba(59, 130, 246, 0.30); } -.arcade-dashboard-v2 .guest-tier-card.tier-50 { +.arcade-dashboard-v2[data-dashboard-view="guest"] .guest-tier-card.tier-50 { --tier-accent: #34d399; --tier-soft: rgba(52, 211, 153, 0.10); --tier-border: rgba(52, 211, 153, 0.28); } -.arcade-dashboard-v2 .guest-tier-icon { +.arcade-dashboard-v2[data-dashboard-view="guest"] .guest-tier-icon { display: grid; place-items: center; width: 52px; @@ -599,24 +750,24 @@ html.light .arcade-dashboard-v2 .guest-dashboard-hero { box-shadow: 0 0 24px var(--tier-soft); } -.arcade-dashboard-v2 .guest-tier-icon svg { +.arcade-dashboard-v2[data-dashboard-view="guest"] .guest-tier-icon svg { width: 25px; height: 25px; } -.arcade-dashboard-v2 .guest-tier-main { +.arcade-dashboard-v2[data-dashboard-view="guest"] .guest-tier-main { min-width: 0; display: grid; gap: 11px; } -.arcade-dashboard-v2 .guest-tier-copy { +.arcade-dashboard-v2[data-dashboard-view="guest"] .guest-tier-copy { min-width: 0; display: grid; gap: 3px; } -.arcade-dashboard-v2 .guest-tier-copy strong { +.arcade-dashboard-v2[data-dashboard-view="guest"] .guest-tier-copy strong { overflow: hidden; color: var(--arcade-text); font-size: 0.86rem; @@ -627,19 +778,19 @@ html.light .arcade-dashboard-v2 .guest-dashboard-hero { white-space: nowrap; } -.arcade-dashboard-v2 .guest-tier-copy span { +.arcade-dashboard-v2[data-dashboard-view="guest"] .guest-tier-copy span { color: var(--arcade-muted); font-size: 0.7rem; } -.arcade-dashboard-v2 .guest-tier-progress { +.arcade-dashboard-v2[data-dashboard-view="guest"] .guest-tier-progress { height: 7px; overflow: hidden; border-radius: 999px; background: rgba(100, 116, 139, 0.17); } -.arcade-dashboard-v2 .guest-tier-progress > span { +.arcade-dashboard-v2[data-dashboard-view="guest"] .guest-tier-progress > span { display: block; height: 100%; border-radius: inherit; @@ -647,7 +798,7 @@ html.light .arcade-dashboard-v2 .guest-dashboard-hero { box-shadow: 0 0 14px var(--tier-soft); } -.arcade-dashboard-v2 .guest-tier-availability { +.arcade-dashboard-v2[data-dashboard-view="guest"] .guest-tier-availability { min-width: 112px; display: grid; justify-items: end; @@ -657,30 +808,98 @@ html.light .arcade-dashboard-v2 .guest-dashboard-hero { text-align: right; } -.arcade-dashboard-v2 .guest-tier-availability > strong { +.arcade-dashboard-v2[data-dashboard-view="guest"] .guest-tier-availability > strong { color: var(--arcade-text); font-size: 0.9rem; font-weight: 850; font-variant-numeric: tabular-nums; } -.arcade-dashboard-v2 .guest-tier-availability > strong span { +.arcade-dashboard-v2[data-dashboard-view="guest"] .guest-tier-availability > strong span { color: var(--arcade-muted); font-weight: 600; } -.arcade-dashboard-v2 .guest-tier-availability small { +.arcade-dashboard-v2[data-dashboard-view="guest"] .guest-tier-availability small { color: var(--arcade-muted); font-size: 0.62rem; } -.arcade-dashboard-v2 .guest-tier-availability .tier-slot-delta { +.arcade-dashboard-v2[data-dashboard-view="guest"] .guest-tier-availability .tier-slot-delta { margin-top: 1px; font-size: 0.58rem; } + +/* Shared tier renderer is used by both Guest and Profile modes. + * Keep these component styles global to v2 without reapplying the full profile redesign. */ +.arcade-dashboard-v2 .shared-tier-panel { + container-type: inline-size; +} +.arcade-dashboard-v2 .shared-tier-panel .empty-tier-grid { + display: grid; + grid-template-columns: minmax(0, 1fr); + gap: 7px; +} +.arcade-dashboard-v2 .shared-tier-panel .guest-tier-card { + --tier-accent: #8b5cf6; + --tier-soft: rgba(139, 92, 246, 0.12); + --tier-border: rgba(139, 92, 246, 0.30); + display: grid; + grid-template-columns: 38px minmax(0, 1fr) auto; + align-items: center; + gap: 9px; + min-height: 64px; + padding: 9px 10px; + overflow: hidden; + border: 1px solid var(--arcade-border-soft); + border-radius: 10px; + background: rgba(13, 25, 49, 0.72); +} +.arcade-dashboard-v2 .shared-tier-panel .guest-tier-card.tier-120 { --tier-accent:#ffd43b; --tier-soft:rgba(255,212,59,.09); --tier-border:rgba(255,212,59,.28); } +.arcade-dashboard-v2 .shared-tier-panel .guest-tier-card.tier-95 { --tier-accent:#c084fc; --tier-soft:rgba(192,132,252,.10); --tier-border:rgba(192,132,252,.30); } +.arcade-dashboard-v2 .shared-tier-panel .guest-tier-card.tier-75 { --tier-accent:#3b82f6; --tier-soft:rgba(59,130,246,.10); --tier-border:rgba(59,130,246,.30); } +.arcade-dashboard-v2 .shared-tier-panel .guest-tier-card.tier-50 { --tier-accent:#34d399; --tier-soft:rgba(52,211,153,.10); --tier-border:rgba(52,211,153,.28); } +.arcade-dashboard-v2 .shared-tier-panel .guest-tier-card.is-current { + border-color: var(--tier-accent); + box-shadow: inset 3px 0 0 var(--tier-accent); +} +.arcade-dashboard-v2 .shared-tier-panel .guest-tier-icon { + display:grid; place-items:center; width:36px; height:36px; + border:1px solid var(--tier-border); border-radius:9px; + background:var(--tier-soft); color:var(--tier-accent); +} +.arcade-dashboard-v2 .shared-tier-panel .guest-tier-icon svg { width:17px; height:17px; } +.arcade-dashboard-v2 .shared-tier-panel .guest-tier-main { min-width:0; display:grid; gap:5px; } +.arcade-dashboard-v2 .shared-tier-panel .guest-tier-copy { min-width:0; display:grid; gap:1px; } +.arcade-dashboard-v2 .shared-tier-panel .guest-tier-copy strong { + overflow:hidden; color:var(--arcade-text); font-size:.72rem; font-weight:850; + text-overflow:ellipsis; text-transform:uppercase; white-space:nowrap; +} +.arcade-dashboard-v2 .shared-tier-panel .guest-tier-copy span { color:var(--arcade-muted); font-size:.61rem; } +.arcade-dashboard-v2 .shared-tier-panel .guest-tier-progress { + height:5px; overflow:hidden; border-radius:999px; background:rgba(100,116,139,.17); +} +.arcade-dashboard-v2 .shared-tier-panel .guest-tier-progress > span { + display:block; height:100%; border-radius:inherit; background:var(--tier-accent); +} +.arcade-dashboard-v2 .shared-tier-panel .guest-tier-availability { + min-width:72px; display:grid; justify-items:end; gap:2px; padding-left:7px; + border-left:1px solid var(--arcade-border-soft); text-align:right; +} +.arcade-dashboard-v2 .shared-tier-panel .guest-tier-availability > strong { + color:var(--arcade-text); font-size:.72rem; font-weight:850; font-variant-numeric:tabular-nums; +} +.arcade-dashboard-v2 .shared-tier-panel .guest-tier-availability > strong span { color:var(--arcade-muted); font-weight:600; } +.arcade-dashboard-v2 .shared-tier-panel .guest-tier-availability small { color:var(--arcade-muted); font-size:.55rem; } +.arcade-dashboard-v2 .shared-tier-panel .guest-tier-heading { display:flex; align-items:center; justify-content:space-between; gap:8px; margin:0; } +.arcade-dashboard-v2 .shared-tier-panel .guest-tier-heading > div { display:flex; align-items:center; gap:8px; min-width:0; } +.arcade-dashboard-v2 .shared-tier-panel .guest-tier-heading-icon { display:grid; place-items:center; width:28px; height:28px; flex:0 0 auto; } +.arcade-dashboard-v2 .shared-tier-panel .guest-tier-heading strong { font-size:.78rem; } +.arcade-dashboard-v2 .shared-tier-panel .guest-tier-heading span { font-size:.6rem; white-space:nowrap; } + /* Footer aligns with the workspace surfaces without competing with content. */ -.arcade-dashboard-v2 .arcade-footer { +.arcade-dashboard-v2[data-dashboard-view="guest"] .arcade-footer { margin-top: 34px; padding-top: 24px; border-top-color: var(--v2-border); @@ -688,34 +907,47 @@ html.light .arcade-dashboard-v2 .guest-dashboard-hero { /* Tablet */ @media (max-width: 1080px) { - .arcade-dashboard-v2 { + .arcade-dashboard-v2[data-dashboard-view="guest"] { --v2-gutter: 18px; } - .arcade-dashboard-v2 .dashboard-summary-grid { + .arcade-dashboard-v2[data-dashboard-view="guest"] .arcade-hero { + grid-template-columns: minmax(0, 0.9fr) minmax(340px, 1.1fr); + gap: 28px; + } + + .arcade-dashboard-v2[data-dashboard-view="guest"] .dashboard-summary-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } - .arcade-dashboard-v2 .tier-status-panel { + .arcade-dashboard-v2[data-dashboard-view="guest"] .tier-status-panel { grid-column: 1 / -1; } - .arcade-dashboard-v2 .dashboard-content-grid { + .arcade-dashboard-v2[data-dashboard-view="guest"] .dashboard-content-grid { grid-template-columns: minmax(0, 1fr) 300px; } - .arcade-dashboard-v2 .earned-badge-grid { + .arcade-dashboard-v2[data-dashboard-view="guest"] .earned-badge-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); } } /* Mobile / small tablet */ @media (max-width: 820px) { - .arcade-dashboard-v2 { + .arcade-dashboard-v2[data-dashboard-view="guest"] { --v2-gutter: 12px; } - .arcade-dashboard-v2 .arcade-nav { + .arcade-dashboard-v2[data-dashboard-view="guest"] .arcade-header { + top: 7px; + min-height: 58px; + margin-top: 7px; + padding-inline: 10px; + border-radius: 14px; + } + + .arcade-dashboard-v2[data-dashboard-view="guest"] .arcade-nav { top: 64px; left: 0; right: 0; @@ -724,30 +956,44 @@ html.light .arcade-dashboard-v2 .guest-dashboard-hero { background: var(--v2-surface-strong); } - .arcade-dashboard-v2 .arcade-nav a { + .arcade-dashboard-v2[data-dashboard-view="guest"] .arcade-nav a { padding: 12px; } - .arcade-dashboard-v2 .dashboard-content-grid { + .arcade-dashboard-v2[data-dashboard-view="guest"] .arcade-hero { grid-template-columns: 1fr; + gap: 26px; + padding: 42px 0 28px; } - .arcade-dashboard-v2 .tier-list-panel { + .arcade-dashboard-v2[data-dashboard-view="guest"] .hero-heading { + max-width: 650px; + } + + .arcade-dashboard-v2[data-dashboard-view="guest"] .profile-analyzer-card { + padding: 22px; + } + + .arcade-dashboard-v2[data-dashboard-view="guest"] .dashboard-content-grid { + grid-template-columns: 1fr; + } + + .arcade-dashboard-v2[data-dashboard-view="guest"] .tier-list-panel { order: 0; } - .arcade-dashboard-v2 .dashboard-bottom-grid { + .arcade-dashboard-v2[data-dashboard-view="guest"] .dashboard-bottom-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } - .arcade-dashboard-v2 .next-goal-panel { + .arcade-dashboard-v2[data-dashboard-view="guest"] .next-goal-panel { grid-column: 1 / -1; } } /* Phones */ @media (max-width: 600px) { - .arcade-dashboard-v2 { + .arcade-dashboard-v2[data-dashboard-view="guest"] { --v2-gutter: 8px; padding-bottom: calc(70px + env(safe-area-inset-bottom)); } @@ -756,19 +1002,66 @@ html.light .arcade-dashboard-v2 .guest-dashboard-hero { padding-bottom: calc(70px + env(safe-area-inset-bottom)); } + .arcade-dashboard-v2[data-dashboard-view="guest"] .arcade-header { + width: calc(100% - 16px); + } + + .arcade-dashboard-v2[data-dashboard-view="guest"] .arcade-brand-mark { + width: 32px; + height: 32px; + } + + .arcade-dashboard-v2[data-dashboard-view="guest"] .arcade-brand-copy { + font-size: 0.58rem; + } + + .arcade-dashboard-v2[data-dashboard-view="guest"] .arcade-hero { + padding-top: 34px; + } + + .arcade-dashboard-v2[data-dashboard-view="guest"] .pixel-kicker { + padding: 6px 8px; + font-size: 0.62rem; + } + + .arcade-dashboard-v2[data-dashboard-view="guest"] .hero-heading h1 { + margin-top: 18px; + font-size: clamp(1.8rem, 9.2vw, 2.6rem); + line-height: 1.25; + } + + .arcade-dashboard-v2[data-dashboard-view="guest"] .hero-description { + font-size: 0.88rem; + } + + .arcade-dashboard-v2[data-dashboard-view="guest"] .trust-pills { + display: flex; + flex-direction: column; + align-items: flex-start; + } + + .arcade-dashboard-v2[data-dashboard-view="guest"] .trust-pills span { + width: fit-content; + } + + .arcade-dashboard-v2[data-dashboard-view="guest"] .profile-analyzer-card { + padding: 16px; + border-radius: 16px; + } + .arcade-dashboard-v2 .preview-mode-toolbar { width: calc(100% - 32px); max-width: 360px; } - .arcade-dashboard-v2 .dashboard-empty-state.guest-dashboard { + .arcade-dashboard-v2[data-dashboard-view="guest"] .dashboard-empty-state.guest-dashboard { gap: 16px; padding: 0; margin-bottom: 16px; border-radius: 18px; } - .arcade-dashboard-v2 .guest-dashboard-hero { + .arcade-dashboard-v2[data-dashboard-view="guest"] .guest-dashboard-hero { grid-template-columns: 64px minmax(0, 1fr); gap: 14px; min-height: 0; @@ -776,7 +1069,7 @@ html.light .arcade-dashboard-v2 .guest-dashboard-hero { border-radius: 16px; } - .arcade-dashboard-v2 .guest-dashboard-orb { + .arcade-dashboard-v2[data-dashboard-view="guest"] .guest-dashboard-orb { width: 58px; height: 58px; box-shadow: @@ -784,48 +1077,48 @@ html.light .arcade-dashboard-v2 .guest-dashboard-hero { 0 0 24px rgba(255, 193, 7, 0.12); } - .arcade-dashboard-v2 .guest-dashboard-orb svg { + .arcade-dashboard-v2[data-dashboard-view="guest"] .guest-dashboard-orb svg { width: 26px; height: 26px; } - .arcade-dashboard-v2 .guest-dashboard-copy { + .arcade-dashboard-v2[data-dashboard-view="guest"] .guest-dashboard-copy { gap: 7px; } - .arcade-dashboard-v2 .guest-dashboard-copy strong { + .arcade-dashboard-v2[data-dashboard-view="guest"] .guest-dashboard-copy strong { font-size: 1.05rem; line-height: 1.25; } - .arcade-dashboard-v2 .guest-dashboard-copy p { + .arcade-dashboard-v2[data-dashboard-view="guest"] .guest-dashboard-copy p { font-size: 0.76rem; line-height: 1.5; } - .arcade-dashboard-v2 .guest-dashboard-decoration { + .arcade-dashboard-v2[data-dashboard-view="guest"] .guest-dashboard-decoration { display: none; } - .arcade-dashboard-v2 .guest-tier-heading { + .arcade-dashboard-v2[data-dashboard-view="guest"] .guest-tier-heading { margin-top: 0; } - .arcade-dashboard-v2 .guest-tier-heading-icon { + .arcade-dashboard-v2[data-dashboard-view="guest"] .guest-tier-heading-icon { width: 32px; height: 32px; } - .arcade-dashboard-v2 .guest-tier-heading strong { + .arcade-dashboard-v2[data-dashboard-view="guest"] .guest-tier-heading strong { font-size: 0.8rem; } - .arcade-dashboard-v2 .empty-tier-grid { + .arcade-dashboard-v2[data-dashboard-view="guest"] .empty-tier-grid { grid-template-columns: 1fr; gap: 8px; } - .arcade-dashboard-v2 .guest-tier-card { + .arcade-dashboard-v2[data-dashboard-view="guest"] .guest-tier-card { grid-template-columns: 46px minmax(0, 1fr) auto; gap: 11px; min-height: 88px; @@ -833,123 +1126,152 @@ html.light .arcade-dashboard-v2 .guest-dashboard-hero { border-radius: 14px; } - .arcade-dashboard-v2 .guest-tier-icon { + .arcade-dashboard-v2[data-dashboard-view="guest"] .guest-tier-icon { width: 44px; height: 44px; border-radius: 11px; } - .arcade-dashboard-v2 .guest-tier-icon svg { + .arcade-dashboard-v2[data-dashboard-view="guest"] .guest-tier-icon svg { width: 21px; height: 21px; } - .arcade-dashboard-v2 .guest-tier-main { + .arcade-dashboard-v2[data-dashboard-view="guest"] .guest-tier-main { gap: 8px; } - .arcade-dashboard-v2 .guest-tier-copy strong { + .arcade-dashboard-v2[data-dashboard-view="guest"] .guest-tier-copy strong { font-size: 0.85rem; } - .arcade-dashboard-v2 .guest-tier-copy span { + .arcade-dashboard-v2[data-dashboard-view="guest"] .guest-tier-copy span { font-size: 0.70rem; } - .arcade-dashboard-v2 .guest-tier-progress { + .arcade-dashboard-v2[data-dashboard-view="guest"] .guest-tier-progress { height: 6px; } - .arcade-dashboard-v2 .guest-tier-availability { + .arcade-dashboard-v2[data-dashboard-view="guest"] .guest-tier-availability { min-width: 94px; gap: 2px; padding-left: 10px; } - .arcade-dashboard-v2 .guest-tier-availability > strong { + .arcade-dashboard-v2[data-dashboard-view="guest"] .guest-tier-availability > strong { font-size: 0.85rem; } - .arcade-dashboard-v2 .guest-tier-availability small { + .arcade-dashboard-v2[data-dashboard-view="guest"] .guest-tier-availability small { max-width: 88px; font-size: 0.66rem; } - .arcade-dashboard-v2 .extension-strip { + .arcade-dashboard-v2[data-dashboard-view="guest"] .profile-input { + min-height: 52px; + } + + .arcade-dashboard-v2[data-dashboard-view="guest"] .analyze-button { + min-height: 48px; + } + + .arcade-dashboard-v2[data-dashboard-view="guest"] .extension-strip { grid-template-columns: auto 1fr; padding: 12px; border-radius: 14px; } - .arcade-dashboard-v2 .extension-store-actions { + .arcade-dashboard-v2[data-dashboard-view="guest"] .extension-store-actions { grid-column: 1 / -1; } - .arcade-dashboard-v2 .dashboard-summary-grid, -.arcade-dashboard-v2 .dashboard-bottom-grid { + .arcade-dashboard-v2[data-dashboard-view="guest"] .dashboard-summary-grid, + .arcade-dashboard-v2[data-dashboard-view="guest"] .dashboard-bottom-grid { grid-template-columns: 1fr; } - .arcade-dashboard-v2 .tier-status-panel, -.arcade-dashboard-v2 .next-goal-panel { + .arcade-dashboard-v2[data-dashboard-view="guest"] .tier-status-panel, + .arcade-dashboard-v2[data-dashboard-view="guest"] .next-goal-panel { grid-column: auto; } - .arcade-dashboard-v2 .dashboard-panel { + .arcade-dashboard-v2[data-dashboard-view="guest"] .dashboard-panel { padding: 16px; border-radius: 14px; } - .arcade-dashboard-v2 .profile-overview { + .arcade-dashboard-v2[data-dashboard-view="guest"] .profile-overview { gap: 12px; margin: 16px 0; } - .arcade-dashboard-v2 .profile-photo { + .arcade-dashboard-v2[data-dashboard-view="guest"] .profile-photo { width: 62px; height: 62px; } - .arcade-dashboard-v2 .profile-stat-grid { + .arcade-dashboard-v2[data-dashboard-view="guest"] .profile-stat-grid { gap: 6px; } - .arcade-dashboard-v2 .profile-stat-grid > div { + .arcade-dashboard-v2[data-dashboard-view="guest"] .profile-stat-grid > div { min-height: 72px; padding: 8px 5px; } - .arcade-dashboard-v2 .profile-stat-icon { + .arcade-dashboard-v2[data-dashboard-view="guest"] .profile-stat-icon { width: 26px; height: 26px; } - .arcade-dashboard-v2 .points-total strong { + .arcade-dashboard-v2[data-dashboard-view="guest"] .points-total strong { font-size: 2.05rem; } - .arcade-dashboard-v2 .point-breakdown-row { + .arcade-dashboard-v2[data-dashboard-view="guest"] .point-breakdown-row { grid-template-columns: minmax(0, 1fr) auto 45px; gap: 7px; } - .arcade-dashboard-v2 .earned-badge-grid { + .arcade-dashboard-v2[data-dashboard-view="guest"] .earned-badge-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } + + .arcade-dashboard-v2[data-dashboard-view="guest"] .mobile-bottom-nav { + left: 0; + right: 0; + bottom: 0; + min-height: 54px; + padding: 5px 8px calc(5px + env(safe-area-inset-bottom)); + border: 0; + border-top: 1px solid var(--v2-border); + border-radius: 0; + background: var(--v2-surface-strong); + box-shadow: var(--v2-shadow); + } + + .arcade-dashboard-v2[data-dashboard-view="guest"] .mobile-bottom-nav a { + border-radius: 10px; + } + + .arcade-dashboard-v2[data-dashboard-view="guest"] .mobile-bottom-nav a:active { + background: rgba(139, 92, 246, 0.11); + } } @media (max-width: 380px) { - .arcade-dashboard-v2 .earned-badge-grid { + .arcade-dashboard-v2[data-dashboard-view="guest"] .earned-badge-grid { grid-template-columns: 1fr; } - .arcade-dashboard-v2 .header-store-link { + .arcade-dashboard-v2[data-dashboard-view="guest"] .header-store-link { display: none; } } /* The same cards also fit the narrower desktop profile sidebar. */ -.arcade-dashboard-v2 .shared-tier-panel { +.arcade-dashboard-v2[data-dashboard-view="guest"] .shared-tier-panel { container-type: inline-size; display: flex; flex-direction: column; @@ -959,47 +1281,47 @@ html.light .arcade-dashboard-v2 .guest-dashboard-hero { } @container (max-width: 420px) { - .arcade-dashboard-v2 .guest-tier-card { + .arcade-dashboard-v2[data-dashboard-view="guest"] .guest-tier-card { grid-template-columns: 44px minmax(0, 1fr) auto; gap: 8px; min-height: 88px; padding: 11px 12px; } - .arcade-dashboard-v2 .guest-tier-icon { + .arcade-dashboard-v2[data-dashboard-view="guest"] .guest-tier-icon { width: 44px; height: 44px; } - .arcade-dashboard-v2 .guest-tier-copy strong { + .arcade-dashboard-v2[data-dashboard-view="guest"] .guest-tier-copy strong { font-size: 0.85rem; } - .arcade-dashboard-v2 .guest-tier-copy span { + .arcade-dashboard-v2[data-dashboard-view="guest"] .guest-tier-copy span { font-size: 0.70rem; } - .arcade-dashboard-v2 .guest-tier-availability { + .arcade-dashboard-v2[data-dashboard-view="guest"] .guest-tier-availability { min-width: 94px; padding-left: 8px; } - .arcade-dashboard-v2 .guest-tier-availability > strong { + .arcade-dashboard-v2[data-dashboard-view="guest"] .guest-tier-availability > strong { font-size: 0.85rem; } - .arcade-dashboard-v2 .guest-tier-availability small { + .arcade-dashboard-v2[data-dashboard-view="guest"] .guest-tier-availability small { max-width: 88px; font-size: 0.66rem; } } /* Consistent section rhythm, without extra boxes around supporting copy. */ -.arcade-dashboard-v2 .shared-tier-panel .guest-tier-heading { +.arcade-dashboard-v2[data-dashboard-view="guest"] .shared-tier-panel .guest-tier-heading { margin: 0; } -.arcade-dashboard-v2 .shared-tier-panel .tier-note { +.arcade-dashboard-v2[data-dashboard-view="guest"] .shared-tier-panel .tier-note { margin: 0; padding: 0; border: 0; @@ -1010,12 +1332,12 @@ html.light .arcade-dashboard-v2 .guest-dashboard-hero { line-height: 1.65; } -.arcade-dashboard-v2 .shared-tier-panel .tier-trends-trigger { +.arcade-dashboard-v2[data-dashboard-view="guest"] .shared-tier-panel .tier-trends-trigger { margin: 0; box-shadow: none; } -.arcade-dashboard-v2 .shared-tier-panel .swag-drops-preview { +.arcade-dashboard-v2[data-dashboard-view="guest"] .shared-tier-panel .swag-drops-preview { margin-top: 4px; } @@ -1026,15 +1348,15 @@ html.light .arcade-dashboard-v2 .guest-dashboard-hero { } /* New tier surfaces use the Font Awesome adapter, not only SVG icons. */ -.arcade-dashboard-v2 .guest-tier-icon .fa-icon { +.arcade-dashboard-v2[data-dashboard-view="guest"] .guest-tier-icon .fa-icon { font-size: 20px; } -.arcade-dashboard-v2 .guest-tier-heading-icon .fa-icon { +.arcade-dashboard-v2[data-dashboard-view="guest"] .guest-tier-heading-icon .fa-icon { font-size: 17px; } -.arcade-dashboard-v2 .guest-dashboard-orb .fa-icon { +.arcade-dashboard-v2[data-dashboard-view="guest"] .guest-dashboard-orb .fa-icon { font-size: 28px; } @@ -1043,13 +1365,13 @@ html.light .arcade-dashboard-v2 .guest-dashboard-hero { } /* Tier accents remain readable on the light surfaces. */ -html.light .arcade-dashboard-v2 .guest-tier-card.tier-120 { --tier-accent: #a16207; } -html.light .arcade-dashboard-v2 .guest-tier-card.tier-95 { --tier-accent: #9333ea; } -html.light .arcade-dashboard-v2 .guest-tier-card.tier-75 { --tier-accent: #2563eb; } -html.light .arcade-dashboard-v2 .guest-tier-card.tier-50 { --tier-accent: #059669; } +html.light .arcade-dashboard-v2[data-dashboard-view="guest"] .guest-tier-card.tier-120 { --tier-accent: #a16207; } +html.light .arcade-dashboard-v2[data-dashboard-view="guest"] .guest-tier-card.tier-95 { --tier-accent: #9333ea; } +html.light .arcade-dashboard-v2[data-dashboard-view="guest"] .guest-tier-card.tier-75 { --tier-accent: #2563eb; } +html.light .arcade-dashboard-v2[data-dashboard-view="guest"] .guest-tier-card.tier-50 { --tier-accent: #059669; } /* Keep the attained rank readable in the compact profile summary. */ -.arcade-dashboard-v2 .profile-stat-grid > div:last-child strong { +.arcade-dashboard-v2[data-dashboard-view="guest"] .profile-stat-grid > div:last-child strong { overflow: visible; white-space: normal; text-overflow: clip; From c12e6187da27e03c3f6bfc8d6650e4260133d339 Mon Sep 17 00:00:00 2001 From: David Nguyen Date: Mon, 5 Oct 2026 11:51:56 +0700 Subject: [PATCH 17/30] sync: inherit components/back-to-top.tsx from #87 --- components/back-to-top.tsx | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 components/back-to-top.tsx diff --git a/components/back-to-top.tsx b/components/back-to-top.tsx new file mode 100644 index 000000000..67cb7020a --- /dev/null +++ b/components/back-to-top.tsx @@ -0,0 +1,31 @@ +"use client" + +import { ArrowUp } from "lucide-react" +import { useEffect, useState } from "react" + +const SHOW_AFTER = 560 + +export default function BackToTop() { + const [visible, setVisible] = useState(false) + + useEffect(() => { + const update = () => setVisible(window.scrollY > SHOW_AFTER) + update() + window.addEventListener("scroll", update, { passive: true }) + return () => window.removeEventListener("scroll", update) + }, []) + + if (!visible) return null + + return ( + + ) +} From 45c76f9f9fa1022ad5101f163753b9a385ccaf76 Mon Sep 17 00:00:00 2001 From: David Nguyen Date: Mon, 5 Oct 2026 11:51:59 +0700 Subject: [PATCH 18/30] sync: inherit components/site/site-footer.tsx from #87 --- components/site/site-footer.tsx | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/components/site/site-footer.tsx b/components/site/site-footer.tsx index cbbecb78c..c0ba80152 100644 --- a/components/site/site-footer.tsx +++ b/components/site/site-footer.tsx @@ -1,12 +1,18 @@ "use client" import Link from "next/link" +import { Chrome, Gamepad2, Globe2 } from "lucide-react" +import { CHROME_EXTENSION_URL, FIREFOX_EXTENSION_URL } from "@/components/arcade/model" import { useSiteMessages } from "./use-site-messages" export default function SiteFooter() { const messages = useSiteMessages() return (
+
+ + ARCADEPOINTS +

{messages.unofficial}

+
) } From 5369f5eaba57981897eb3fb2af449b7cc626ce41 Mon Sep 17 00:00:00 2001 From: David Nguyen Date: Mon, 5 Oct 2026 11:52:24 +0700 Subject: [PATCH 19/30] sync: inherit tests/home-layout.test.mjs from #87 --- tests/home-layout.test.mjs | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/tests/home-layout.test.mjs b/tests/home-layout.test.mjs index 7b6af8a9e..6587aba42 100644 --- a/tests/home-layout.test.mjs +++ b/tests/home-layout.test.mjs @@ -51,7 +51,7 @@ test("Monthly Labs has deterministic React-owned anchors in both dashboard state assert.match(guestDashboard, / emptyMonthly) - assert.match(readRepoFile("components/site/site-header.tsx"), /href="\/monthly-labs\/"/) + assert.match(readRepoFile("components/site/site-header.tsx"), /href="\\/monthly-labs\\/"/) assert.match(guide, /data-home-order="about"/) }) @@ -105,7 +105,14 @@ test("home dashboard v2 stays scoped and its visual overrides load last", () => assert.match(calculator, /arcade-dashboard-page arcade-dashboard-v2/) assert.match(refactorStyles, /\.arcade-dashboard-v2/) - assert.match(refactorStyles, /\.arcade-dashboard-v2 \.program-countdown-host/) + assert.match( + refactorStyles, + /\.arcade-dashboard-v2\[data-dashboard-view="guest"\] \.program-countdown-host/, + ) + assert.doesNotMatch( + refactorStyles, + /\.arcade-dashboard-v2 \.program-countdown-host/, + ) assert.match(refactorStyles, /\.guest-dashboard-hero/) assert.match(refactorStyles, /\.guest-tier-card\.tier-120/) assert.match(refactorStyles, /\.guest-tier-progress/) @@ -185,12 +192,13 @@ test("PR-preview guest and profile debug modes stay profile-safe and fully local }) -test("guest and profile share one tier renderer; only profile passes the attained tier", () => { - assert.equal((calculator.match(/ { + assert.equal((calculator.match(/ Date: Mon, 5 Oct 2026 11:52:27 +0700 Subject: [PATCH 20/30] sync: reconcile #87 footer with #89 monthly labs UI --- app/styles/site-ui.css | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/app/styles/site-ui.css b/app/styles/site-ui.css index 30cec31c4..7e918a2a5 100644 --- a/app/styles/site-ui.css +++ b/app/styles/site-ui.css @@ -32,11 +32,21 @@ html:is(.light, .dark) .arcade-dashboard-page.arcade-dashboard-page { .website-language-menu.website-language-menu { border-color: var(--ui-border); border-radius: var(--ui-radius); background: var(--ui-surface); color: var(--ui-text); box-shadow: var(--ui-shadow); max-height: min(480px, calc(100dvh - 110px)); overflow-y: auto; } .website-language-option.website-language-option { color: var(--ui-text); border-radius: var(--ui-radius-sm); } .website-language-option:is(:hover,.is-selected) { background: var(--ui-accent-soft); } -.arcade-dashboard-page .site-footer.site-footer { display: flex; flex-direction: column; align-items: flex-start; gap: 16px; width: min(var(--ui-width), calc(100% - var(--ui-gutter) * 2)); margin: 32px auto 0; padding: 24px 0 32px; border-top: 1px solid var(--ui-border); background: transparent; } -.site-footer p { max-width: 68ch; margin: 0; font-size: .85rem; color: var(--ui-muted); } -.site-footer .footer-route-links { display: flex; flex-wrap: wrap; gap: 12px 24px; } -.site-footer .footer-route-links a { color: var(--ui-muted); font-size: .85rem; } -.site-footer .footer-route-links a:hover { color: var(--ui-accent); } +.arcade-dashboard-page .site-footer.site-footer { + width: min(1280px, calc(100% - 40px)); + margin: 32px auto 0; + background: transparent; +} +.site-footer .footer-brand { flex: 0 0 auto; } +.site-footer p { margin: 0; color: var(--arcade-muted); } +.site-footer .footer-route-links { display: flex; flex-wrap: wrap; gap: 12px 18px; } +.site-footer .footer-route-links a, +.site-footer .footer-store-links a { color: var(--arcade-muted); text-decoration: none; } +.site-footer .footer-route-links a:hover, +.site-footer .footer-store-links a:hover { color: var(--arcade-text); } +.site-footer .footer-store-links { display: flex; align-items: center; gap: 12px; } +.site-footer .footer-store-links a { display: inline-flex; align-items: center; gap: 5px; } +.site-footer .footer-store-links svg { width: 14px; height: 14px; } /* Readable content and Swag grids share the same container and hierarchy. */ .site-content-page .internal-page-hero { border: 0; background: transparent; } .site-content-page .internal-page-glow { display: none; } From e7d914ec8a5a8bb5ae8a0f91e9f8e2cb82ef1d43 Mon Sep 17 00:00:00 2001 From: David Nguyen Date: Mon, 5 Oct 2026 11:55:25 +0700 Subject: [PATCH 21/30] sync: inherit latest app/layout.tsx from #87 --- app/layout.tsx | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/layout.tsx b/app/layout.tsx index 49b056c5e..5e831cc04 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -30,6 +30,10 @@ import "./styles/facilitator-participation.css" import "./styles/facilitator-launcher-visibility.css" import "./styles/cookie-consent.css" import "./styles/home-refactor.css" +import "./styles/shared-profile.css" +import "./styles/profile-share.css" +import "./styles/bonus-milestone.css" +import "./styles/site-ui.css" import "./styles/back-to-top.css" const arcadeRequestDedupeBootstrap = readFileSync( From dbabf2cfca0a6b24d502026dc0a75d353d7c8bce Mon Sep 17 00:00:00 2001 From: David Nguyen Date: Mon, 5 Oct 2026 11:55:28 +0700 Subject: [PATCH 22/30] sync: inherit latest components/site/site-footer.tsx from #87 --- components/site/site-footer.tsx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/components/site/site-footer.tsx b/components/site/site-footer.tsx index c0ba80152..f983898fb 100644 --- a/components/site/site-footer.tsx +++ b/components/site/site-footer.tsx @@ -2,9 +2,13 @@ import Link from "next/link" import { Chrome, Gamepad2, Globe2 } from "lucide-react" -import { CHROME_EXTENSION_URL, FIREFOX_EXTENSION_URL } from "@/components/arcade/model" import { useSiteMessages } from "./use-site-messages" +const CHROME_EXTENSION_URL = + "https://chromewebstore.google.com/detail/google-cloud-skills-boost/lmbhjioadhcoebhgapaidogodllonbgg" +const FIREFOX_EXTENSION_URL = + "https://addons.mozilla.org/addon/cloud-skills-boost-helper" + export default function SiteFooter() { const messages = useSiteMessages() return ( From c8f98791cce5d86f61d9f5ae224c087568d7ac86 Mon Sep 17 00:00:00 2001 From: David Nguyen Date: Mon, 5 Oct 2026 11:55:32 +0700 Subject: [PATCH 23/30] sync: inherit latest tests/home-layout.test.mjs from #87 From 4af3fdb4c8972fc054645b1ef79e567645eb4ae7 Mon Sep 17 00:00:00 2001 From: David Nguyen Date: Mon, 5 Oct 2026 11:56:50 +0700 Subject: [PATCH 24/30] sync: inherit latest dashboard test --- tests/home-layout.test.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/home-layout.test.mjs b/tests/home-layout.test.mjs index 6587aba42..e18cc8048 100644 --- a/tests/home-layout.test.mjs +++ b/tests/home-layout.test.mjs @@ -51,7 +51,7 @@ test("Monthly Labs has deterministic React-owned anchors in both dashboard state assert.match(guestDashboard, / emptyMonthly) - assert.match(readRepoFile("components/site/site-header.tsx"), /href="\\/monthly-labs\\/"/) + assert.ok(readRepoFile("components/site/site-header.tsx").includes('href="/monthly-labs/"')) assert.match(guide, /data-home-order="about"/) }) From 0ceab9cc093a8478a1205fae1e595ca98bae2e64 Mon Sep 17 00:00:00 2001 From: David Nguyen Date: Mon, 5 Oct 2026 19:23:10 +0700 Subject: [PATCH 25/30] fix: sync facilitator season state handling --- .../arcade/facilitator-participation.ts | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/components/arcade/facilitator-participation.ts b/components/arcade/facilitator-participation.ts index ee284354d..2be2feed9 100644 --- a/components/arcade/facilitator-participation.ts +++ b/components/arcade/facilitator-participation.ts @@ -1,6 +1,19 @@ export const FACILITATOR_PARTICIPATION_EVENT = "arcade-facilitator-participation-change" export const FACILITATOR_PANEL_OPEN_EVENT = "arcade-facilitator-panel-open" +export const FACILITATOR_PROGRAM_STATE_EVENT = + "arcade-facilitator-program-state-change" + +export type FacilitatorProgramState = + | "unknown" + | "active" + | "ended" + | "unconfigured" + | "disabled" + +export type FacilitatorProgramStateDetail = { + state: FacilitatorProgramState +} const PARTICIPATION_STORAGE_PREFIX = "arcade-facilitator-participation-v1" @@ -61,3 +74,35 @@ export function writeFacilitatorParticipation( ), ) } + +const FACILITATOR_PROGRAM_STATES = new Set([ + "unknown", + "active", + "ended", + "unconfigured", + "disabled", +]) + +export function readFacilitatorProgramState(): FacilitatorProgramState { + if (typeof document === "undefined") return "unknown" + + const state = document.documentElement.dataset.facilitatorProgramState + return FACILITATOR_PROGRAM_STATES.has(state as FacilitatorProgramState) + ? (state as FacilitatorProgramState) + : "unknown" +} + +export function writeFacilitatorProgramState( + state: FacilitatorProgramState, +): void { + if (typeof document === "undefined" || typeof window === "undefined") return + if (document.documentElement.dataset.facilitatorProgramState === state) return + + document.documentElement.dataset.facilitatorProgramState = state + window.dispatchEvent( + new CustomEvent( + FACILITATOR_PROGRAM_STATE_EVENT, + { detail: { state } }, + ), + ) +} From 0ef34726916fc1da5249fff48a6400cddebcdbce Mon Sep 17 00:00:00 2001 From: David Nguyen Date: Mon, 5 Oct 2026 19:23:13 +0700 Subject: [PATCH 26/30] fix: sync facilitator season state handling --- components/arcade/program-countdown.tsx | 35 ++++++++++++++++++++----- 1 file changed, 29 insertions(+), 6 deletions(-) diff --git a/components/arcade/program-countdown.tsx b/components/arcade/program-countdown.tsx index 738f2ff88..6f1964026 100644 --- a/components/arcade/program-countdown.tsx +++ b/components/arcade/program-countdown.tsx @@ -9,10 +9,14 @@ import { type DeadlineSource, type ResolvedDeadline, } from "./countdown-deadline" +import { + FACILITATOR_PANEL_OPEN_EVENT, + writeFacilitatorProgramState, + type FacilitatorProgramState, +} from "./facilitator-participation" const HOST_SELECTOR = ".program-countdown-host" const HERO_SELECTOR = ".arcade-hero" -const FACILITATOR_LAUNCHER_SELECTOR = ".facilitator-launcher" const DEFAULT_TIME_ZONE_OFFSET = "+05:30" const FIREBASE_CDN_VERSION = "12.18.0" const DEFAULT_FETCH_INTERVAL_MS = 900_000 @@ -339,10 +343,18 @@ function formatPart(value: number): string { return String(value).padStart(2, "0") } +function facilitatorProgramState( + config: ProgramCountdownConfig | undefined, + nowMs: number, +): FacilitatorProgramState { + if (!config) return "unknown" + if (!config.enabled) return "disabled" + if (!config.deadline) return "unconfigured" + return countdownParts(config.deadline, nowMs).ended ? "ended" : "active" +} + function openFacilitatorDetails() { - document - .querySelector(FACILITATOR_LAUNCHER_SELECTOR) - ?.click() + window.dispatchEvent(new Event(FACILITATOR_PANEL_OPEN_EVENT)) } function ProgramCard({ @@ -389,8 +401,12 @@ function ProgramCard({ ) : remaining.ended ? (
- Unavailable - Program tracker + {config.id === "facilitator" ? "Season ended" : "Unavailable"} + + {config.id === "facilitator" + ? "Facilitator Program 2026 has ended" + : "Program tracker"} +
{config.id === "facilitator" ? (