fix: robust coaster matching + dark carnival color scheme

Matching fixes:
- normalize() now strips all non-word/non-space chars via [^\w\s] instead of
  a hand-rolled list, catching !, curly apostrophe (U+2019), and any future edge cases
- Add isCoaster() helper with prefix matching (min 5 chars) to handle subtitle
  mismatches in either direction (e.g. "Apocalypse" vs "Apocalypse the Ride",
  "The New Revolution - Classic" vs "New Revolution")
- Fix top-level rides loop which still used coasterNames.has(normalize()) instead
  of isCoaster() — this was the recurring bug causing top-level rides to miss

UI:
- Dark neutral base (#111) replacing cold navy and muddy purple
- Neon accent palette: hot pink, electric green, vivid yellow, cyan
- Park page max-width 960→1280px, calendar cells 72→96px tall
- Scrollbar accent matches theme

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-04 15:22:59 -04:00
parent bad366d5ea
commit e9da6f3120
5 changed files with 84 additions and 58 deletions

View File

@@ -126,7 +126,7 @@ export function ParkMonthCalendar({ parkId, year, month, monthData, today }: Par
if (!cell.day || !cell.iso) {
return (
<div key={ci} style={{
minHeight: 72,
minHeight: 96,
background: ci === 0 || ci === 6 ? "var(--color-weekend-header)" : "transparent",
borderRight: ci < 6 ? "1px solid var(--color-border-subtle)" : "none",
}} />
@@ -147,18 +147,18 @@ export function ParkMonthCalendar({ parkId, year, month, monthData, today }: Par
return (
<div key={ci} style={{
minHeight: 72,
padding: "8px 10px",
minHeight: 96,
padding: "10px 12px",
background: bg,
borderRight: ci < 6 ? "1px solid var(--color-border-subtle)" : "none",
borderLeft: isToday ? "2px solid var(--color-today-border)" : "none",
display: "flex",
flexDirection: "column",
gap: 4,
gap: 5,
}}>
{/* Date number */}
<span style={{
fontSize: "0.85rem",
fontSize: "0.95rem",
fontWeight: isToday ? 700 : isWeekend ? 600 : 400,
color: isToday
? "var(--color-today-text)"
@@ -172,18 +172,18 @@ export function ParkMonthCalendar({ parkId, year, month, monthData, today }: Par
{/* Status */}
{!dayData ? (
<span style={{ fontSize: "0.65rem", color: "var(--color-text-dim)" }}></span>
<span style={{ fontSize: "0.75rem", color: "var(--color-text-dim)" }}></span>
) : isPH && isOpen ? (
<div style={{
background: "var(--color-ph-bg)",
border: "1px solid var(--color-ph-border)",
borderRadius: 4,
padding: "2px 5px",
borderRadius: 5,
padding: "3px 6px",
}}>
<div style={{ fontSize: "0.55rem", fontWeight: 700, color: "var(--color-ph-label)", textTransform: "uppercase", letterSpacing: "0.05em" }}>
<div style={{ fontSize: "0.6rem", fontWeight: 700, color: "var(--color-ph-label)", textTransform: "uppercase", letterSpacing: "0.05em" }}>
Passholder
</div>
<div style={{ fontSize: "0.6rem", color: "var(--color-ph-hours)", marginTop: 1 }}>
<div style={{ fontSize: "0.65rem", color: "var(--color-ph-hours)", marginTop: 2 }}>
{dayData.hoursLabel}
</div>
</div>
@@ -191,15 +191,15 @@ export function ParkMonthCalendar({ parkId, year, month, monthData, today }: Par
<div style={{
background: "var(--color-open-bg)",
border: "1px solid var(--color-open-border)",
borderRadius: 4,
padding: "2px 5px",
borderRadius: 5,
padding: "3px 6px",
}}>
<div style={{ fontSize: "0.6rem", color: "var(--color-open-hours)" }}>
<div style={{ fontSize: "0.65rem", color: "var(--color-open-hours)" }}>
{dayData.hoursLabel}
</div>
</div>
) : (
<span style={{ fontSize: "0.9rem", color: "var(--color-text-dim)", lineHeight: 1 }}>·</span>
<span style={{ fontSize: "1rem", color: "var(--color-text-dim)", lineHeight: 1 }}>·</span>
)}
</div>
);