Files
NHL-Scoreboard/app/static/styles.css
T
josh 64b2e4b5e1
CI / Lint (push) Successful in 7s
CI / Test (push) Successful in 10s
CI / Build & Push (push) Successful in 19s
refactor: auto-prompt for notification permission, drop OT alerts button
Browsers already gate Notification.requestPermission behind a native
prompt, so a dedicated button was redundant UI clutter. Prompting on
load (only when permission state is "default") keeps the flow and
clears space in the banner for future notification types.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-19 13:12:01 -04:00

1131 lines
22 KiB
CSS
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
:root {
--bg: #111;
--card: #1c1c1c;
--card-border: #2a2a2a;
--badge-bg: #2a2a2a;
--text: #f0f0f0;
--text-muted: #666;
--green-bg: #14532d;
--green-text: #86efac;
--green-accent: #22c55e;
--red: #ef4444;
--gap: 1rem;
--radius: 12px;
--card-w: 290px;
/* Cup theme palette — only referenced when body.playoff-mode is set */
--cup-gold-1: #d4af37;
--cup-gold-2: #f5d76e;
--cup-gold-dim: #8a6d1a;
--cup-silver-1: #c0c8d0;
--cup-silver-2: #e8ecef;
--cup-silver-dim: #6b7580;
--ice-1: #0a1628;
--ice-2: #162844;
--ice-accent: #4fc3f7;
--gold-gradient: linear-gradient(90deg, var(--cup-gold-dim), var(--cup-gold-2) 50%, var(--cup-gold-1));
--banner-bg: linear-gradient(135deg, #0a1628 0%, #162844 55%, #1a1a2e 100%);
}
*, *::before, *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
background: var(--bg);
color: var(--text);
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
min-height: 100vh;
}
/* ── Header ─────────────────────────────────────── */
header {
padding: 1rem 1.25rem 0.25rem;
text-align: center;
}
.header-title {
font-size: 2rem;
font-weight: 700;
color: var(--text);
}
/* ── Layout ─────────────────────────────────────── */
main {
padding: 0.75rem 1.25rem 2rem;
max-width: 1800px;
margin-left: auto;
margin-right: auto;
}
.section {
margin-bottom: 2rem;
}
.section.hidden {
display: none;
}
.section-heading {
font-size: 0.7rem;
font-weight: 700;
text-align: center;
text-transform: uppercase;
letter-spacing: 0.12em;
color: var(--text-muted);
margin-bottom: 0.875rem;
margin-top: 0.25rem;
}
.games-grid {
display: flex;
flex-wrap: wrap;
gap: var(--gap);
justify-content: center;
}
/* ── Game Card ──────────────────────────────────── */
.game-box {
background: var(--card);
border: 1px solid var(--card-border);
border-radius: var(--radius);
padding: 1rem 1rem 0.875rem;
width: var(--card-w);
flex-shrink: 0;
border-top-width: 3px;
}
.game-box-live {
border-top-color: var(--green-accent);
}
.game-box-intermission {
border-top-color: #f59e0b;
}
/* ── Card Header (badges + live dot) ───────────── */
.card-header {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 0.5rem;
min-height: 1.25rem;
}
.badges {
display: flex;
gap: 0.3rem;
align-items: center;
flex-wrap: wrap;
}
.badge {
font-size: 0.65rem;
font-weight: 700;
padding: 0.2rem 0.45rem;
border-radius: 4px;
text-transform: uppercase;
letter-spacing: 0.05em;
background: var(--badge-bg);
color: var(--text);
white-space: nowrap;
}
.badge-live {
background: var(--green-bg);
color: var(--green-text);
}
.badge-muted {
color: var(--text-muted);
}
.live-dot {
width: 7px;
height: 7px;
background: var(--red);
border-radius: 50%;
flex-shrink: 0;
animation: pulse 1.8s ease-in-out infinite;
}
@keyframes pulse {
0%, 100% { opacity: 1; }
50% { opacity: 0.4; }
}
/* ── Team Rows ──────────────────────────────────── */
.team-row {
display: flex;
align-items: center;
gap: 0.625rem;
padding: 0.55rem 0;
}
.team-row + .team-row {
border-top: 1px solid var(--card-border);
}
.team-logo {
width: 40px;
height: 40px;
object-fit: contain;
flex-shrink: 0;
}
.team-meta {
flex: 1;
min-width: 0;
display: flex;
flex-direction: column;
gap: 0.1rem;
}
.team-name {
font-size: 0.825rem;
font-weight: 600;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.team-sog {
font-size: 0.68rem;
color: var(--text-muted);
}
.team-pp {
font-size: 0.68rem;
color: var(--red);
font-weight: 600;
}
.team-score {
font-size: 1.6rem;
font-weight: 700;
margin-left: auto;
flex-shrink: 0;
min-width: 1.75rem;
text-align: right;
letter-spacing: -0.02em;
}
.team-record {
font-size: 0.72rem;
color: var(--text-muted);
margin-left: auto;
flex-shrink: 0;
white-space: nowrap;
}
/* ── Power Play Badge (inline in card header) ─── */
.badge-pp {
background: rgba(239, 68, 68, 0.15);
color: var(--red);
border: 1px solid rgba(239, 68, 68, 0.35);
font-variant-numeric: tabular-nums;
}
/* ── Hype Meter ─────────────────────────────────── */
.hype-meter {
margin-top: 0.75rem;
}
.hype-label {
display: block;
font-size: 0.6rem;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.1em;
color: var(--text-muted);
margin-bottom: 0.3rem;
}
.gauge-track {
height: 4px;
background: var(--badge-bg);
border-radius: 99px;
overflow: hidden;
}
.gauge {
height: 100%;
border-radius: 99px;
width: 0;
transition: width 0.5s ease;
}
/* ── Desktop ────────────────────────────────────── */
@media (min-width: 900px) {
:root {
--card-w: 340px;
--gap: 1.25rem;
}
main {
padding: 1rem 2rem 2.5rem;
}
.header-title {
font-size: 2.4rem;
}
.section-heading {
font-size: 0.85rem;
}
.game-box {
padding: 1.125rem 1.125rem 1rem;
}
.team-logo {
width: 48px;
height: 48px;
}
.badge {
font-size: 0.75rem;
}
.team-name {
font-size: 0.95rem;
}
.team-score {
font-size: 1.9rem;
}
.hype-label {
font-size: 0.7rem;
}
}
@media (min-width: 1400px) {
:root {
--card-w: 400px;
--gap: 1.5rem;
}
main {
padding: 1.25rem 2.5rem 3rem;
}
.header-title {
font-size: 2.8rem;
}
.section-heading {
font-size: 0.95rem;
}
.game-box {
padding: 1.25rem 1.25rem 1.125rem;
}
.team-logo {
width: 56px;
height: 56px;
}
.badge {
font-size: 0.82rem;
}
.team-name {
font-size: 1.05rem;
}
.team-score {
font-size: 2.2rem;
}
.hype-label {
font-size: 0.76rem;
}
}
/* ── Mobile ─────────────────────────────────────── */
@media (max-width: 640px) {
:root {
--card-w: 100%;
}
.games-grid {
flex-direction: column;
}
}
/* ── Playoff Banner ─────────────────────────────── */
.playoff-banner {
display: grid;
grid-template-columns: 1fr auto 1fr;
align-items: center;
gap: 1rem;
padding: 0.875rem 1.25rem;
background: var(--banner-bg);
border-bottom: 2px solid transparent;
border-image: var(--gold-gradient) 1;
position: relative;
}
.playoff-banner.hidden {
display: none;
}
.banner-main {
grid-column: 2;
display: flex;
align-items: center;
gap: 1rem;
min-width: 0;
}
.banner-actions {
grid-column: 3;
justify-self: end;
display: flex;
align-items: center;
gap: 0.5rem;
}
.banner-trophy {
width: 36px;
height: 44px;
flex-shrink: 0;
filter: drop-shadow(0 0 6px rgba(212, 175, 55, 0.25));
}
.banner-text {
min-width: 0;
text-align: center;
}
.banner-title {
font-size: 0.95rem;
font-weight: 800;
letter-spacing: 0.12em;
background: var(--gold-gradient);
-webkit-background-clip: text;
background-clip: text;
color: transparent;
line-height: 1.2;
}
.banner-year {
color: var(--cup-silver-2);
-webkit-text-fill-color: var(--cup-silver-2);
font-weight: 700;
margin-left: 0.35rem;
}
.banner-meta {
margin-top: 0.2rem;
display: flex;
flex-wrap: wrap;
justify-content: center;
gap: 0.4rem 0.7rem;
font-size: 0.75rem;
color: var(--cup-silver-1);
letter-spacing: 0.03em;
}
.banner-meta > span.hidden { display: none; }
.banner-meta > span:not(.hidden) + span:not(.hidden)::before {
content: "\00b7";
color: var(--cup-gold-dim);
margin-right: 0.7rem;
}
.meta-elim, .meta-game7 {
color: var(--cup-gold-2);
font-weight: 700;
}
@media (min-width: 900px) {
.playoff-banner {
padding: 1.125rem 2rem;
gap: 1.25rem;
}
.banner-trophy { width: 44px; height: 54px; }
.banner-title { font-size: 1.15rem; }
.banner-meta { font-size: 0.85rem; }
}
@media (max-width: 640px) {
.playoff-banner {
grid-template-columns: 1fr;
justify-items: center;
padding: 0.75rem 1rem;
}
.banner-main { grid-column: 1; }
.banner-actions { grid-column: 1; justify-self: center; }
}
/* ── Playoff Game Cards ─────────────────────────── */
.playoff-mode .section-heading-gold {
background: var(--gold-gradient);
-webkit-background-clip: text;
background-clip: text;
color: transparent;
-webkit-text-fill-color: transparent;
letter-spacing: 0.18em;
font-weight: 800;
}
.game-box-playoff {
border-top-width: 3px;
border-image: var(--gold-gradient) 1;
border-image-slice: 1;
}
/* Gold top stripe wins over the green-accent live stripe on playoff games */
.game-box-playoff.game-box-live,
.game-box-playoff.game-box-intermission {
border-image: var(--gold-gradient) 1;
}
.game-box-pinned {
box-shadow: 0 0 0 1px rgba(212, 175, 55, 0.35),
0 6px 20px rgba(212, 175, 55, 0.1);
}
.game-box-sudden-death {
border-top-width: 3px;
animation: pulse-gold 2.2s ease-in-out infinite;
}
@keyframes pulse-gold {
0%, 100% {
box-shadow: 0 0 0 1px rgba(212, 175, 55, 0.35),
0 0 10px rgba(212, 175, 55, 0.25);
}
50% {
box-shadow: 0 0 0 1px rgba(245, 215, 110, 0.75),
0 0 22px rgba(245, 215, 110, 0.45);
}
}
/* Playoff context row above the period badges */
.playoff-context {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 0.35rem 0.6rem;
margin-bottom: 0.5rem;
padding-bottom: 0.5rem;
border-bottom: 1px solid var(--card-border);
}
.series-summary {
font-size: 0.72rem;
color: var(--cup-silver-1);
font-weight: 600;
letter-spacing: 0.02em;
}
/* Playoff stake badges */
.badge-round {
background: rgba(212, 175, 55, 0.1);
color: var(--cup-gold-2);
border: 1px solid rgba(212, 175, 55, 0.35);
}
.badge-conf {
background: rgba(79, 195, 247, 0.12);
color: var(--ice-accent);
border-color: rgba(79, 195, 247, 0.4);
}
.badge-cup {
background: linear-gradient(90deg, rgba(212, 175, 55, 0.25), rgba(245, 215, 110, 0.25));
color: #1a1200;
border: 1px solid var(--cup-gold-1);
font-weight: 800;
}
.badge-game7 {
background: linear-gradient(90deg, var(--cup-gold-dim), var(--cup-gold-1));
color: #1a1200;
border: 1px solid var(--cup-gold-2);
font-weight: 800;
letter-spacing: 0.1em;
}
.badge-clincher {
background: rgba(239, 68, 68, 0.15);
color: #fca5a5;
border: 1px solid rgba(239, 68, 68, 0.45);
}
.badge-pivotal {
background: rgba(79, 195, 247, 0.12);
color: var(--ice-accent);
border: 1px solid rgba(79, 195, 247, 0.4);
}
.badge-sudden-death {
background: linear-gradient(90deg, var(--cup-gold-dim), var(--cup-gold-1));
color: #1a1200;
border: 1px solid var(--cup-gold-2);
font-weight: 800;
letter-spacing: 0.08em;
}
.series-blurb {
margin-top: 0.65rem;
padding-top: 0.55rem;
border-top: 1px solid var(--card-border);
font-size: 0.72rem;
color: var(--cup-silver-1);
letter-spacing: 0.01em;
font-style: italic;
text-align: center;
}
/* In playoff mode, retint the hype meter to silver → gold */
.playoff-mode .hype-label {
color: var(--cup-silver-dim);
}
/* Override the red live dot with gold for playoff-mode bodies */
.playoff-mode .game-box-playoff .live-dot {
background: var(--cup-gold-1);
box-shadow: 0 0 6px rgba(245, 215, 110, 0.7);
}
@media (min-width: 900px) {
.series-summary { font-size: 0.82rem; }
.series-blurb { font-size: 0.82rem; }
}
@media (min-width: 1400px) {
.series-summary { font-size: 0.9rem; }
.series-blurb { font-size: 0.9rem; }
}
.series-header {
display: flex;
align-items: center;
justify-content: space-between;
gap: 1rem;
padding: 1rem 1.25rem 0.25rem;
}
.series-header .header-title {
font-size: 1.3rem;
}
/* Clickable playoff card wrapper */
.series-link {
display: contents;
color: inherit;
text-decoration: none;
}
.series-link .game-box {
cursor: pointer;
transition: transform 0.12s ease, border-color 0.12s ease;
}
.series-link:hover .game-box {
border-color: var(--cup-gold-1);
transform: translateY(-1px);
}
.series-link:focus-visible .game-box {
outline: 2px solid var(--cup-gold-2);
outline-offset: 2px;
}
/* ── Series detail page (/series/<id>) ──────────── */
.header-link {
text-decoration: none;
color: var(--cup-silver-2);
}
.series-main {
padding: 1rem 1.25rem 3rem;
max-width: 1000px;
margin: 0 auto;
display: flex;
flex-direction: column;
gap: 1.5rem;
}
.series-hero {
background: var(--banner-bg);
border: 1px solid var(--cup-gold-dim);
border-radius: var(--radius);
padding: 1.25rem;
display: flex;
flex-direction: column;
gap: 1rem;
align-items: center;
text-align: center;
}
.series-hero-eyebrow {
display: flex;
gap: 0.4rem;
flex-wrap: wrap;
justify-content: center;
}
.series-teams {
display: grid;
grid-template-columns: 1fr auto 1fr;
align-items: center;
gap: 1rem;
width: 100%;
}
.series-team {
display: flex;
flex-direction: column;
align-items: center;
gap: 0.35rem;
padding: 0.5rem;
border-radius: 10px;
border: 1px solid transparent;
}
.series-team-leader {
border-color: var(--cup-gold-dim);
background: rgba(212, 175, 55, 0.06);
}
.series-team-logo {
width: 72px;
height: 72px;
object-fit: contain;
}
.series-team-name {
font-weight: 600;
font-size: 1.05rem;
color: var(--cup-silver-2);
}
.series-team-meta {
font-size: 0.78rem;
color: var(--cup-silver-dim);
letter-spacing: 0.04em;
text-transform: uppercase;
}
.series-team-wins {
font-size: 2.5rem;
font-weight: 700;
color: var(--cup-gold-2);
line-height: 1;
margin-top: 0.25rem;
}
.series-versus {
display: flex;
flex-direction: column;
gap: 0.2rem;
align-items: center;
}
.series-versus-label {
font-size: 0.7rem;
letter-spacing: 0.15em;
color: var(--cup-silver-dim);
}
.series-versus-score {
font-size: 1.4rem;
font-weight: 600;
color: var(--cup-gold-2);
}
.series-versus-best {
font-size: 0.75rem;
color: var(--cup-silver-dim);
}
.series-headline {
font-size: 1rem;
color: var(--cup-silver-1);
max-width: 46ch;
}
.series-next-card {
background: var(--card);
border: 1px solid var(--cup-gold-dim);
border-radius: 10px;
padding: 0.9rem 1rem;
display: flex;
flex-direction: column;
gap: 0.4rem;
align-items: center;
margin-top: 0.6rem;
}
.series-next-matchup {
display: flex;
gap: 0.6rem;
align-items: center;
font-size: 1.2rem;
font-weight: 600;
}
.series-next-team {
color: var(--cup-silver-2);
}
.series-next-at {
color: var(--cup-silver-dim);
font-size: 0.9rem;
}
.series-next-meta {
font-size: 0.85rem;
color: var(--cup-silver-1);
text-align: center;
}
.series-games {
list-style: none;
display: flex;
flex-direction: column;
gap: 0.5rem;
margin-top: 0.6rem;
}
.series-game {
display: grid;
grid-template-columns: 90px 1fr auto;
gap: 0.75rem;
align-items: center;
padding: 0.7rem 0.9rem;
background: var(--card);
border: 1px solid var(--card-border);
border-radius: 10px;
}
.series-game-live,
.series-game-completed {
border-color: var(--cup-gold-dim);
}
.series-game-col-number {
font-weight: 600;
color: var(--cup-silver-1);
letter-spacing: 0.03em;
font-size: 0.85rem;
}
.series-game-col-matchup {
display: flex;
flex-direction: column;
gap: 0.2rem;
}
.series-game-team {
display: flex;
justify-content: space-between;
gap: 0.6rem;
font-size: 0.95rem;
}
.series-game-abbrev {
font-weight: 600;
color: var(--cup-silver-2);
}
.series-game-score {
color: var(--cup-silver-1);
font-variant-numeric: tabular-nums;
min-width: 1.6em;
text-align: right;
}
.series-game-winner {
color: var(--cup-gold-2);
font-weight: 700;
}
.series-game-state {
font-size: 0.8rem;
color: var(--cup-silver-dim);
text-align: right;
}
@media (max-width: 600px) {
.series-teams {
grid-template-columns: 1fr;
}
.series-versus {
order: 3;
}
.series-team-logo {
width: 56px;
height: 56px;
}
.series-game {
grid-template-columns: 70px 1fr;
}
.series-game-col-state {
grid-column: 1 / -1;
text-align: left;
}
.series-game-state {
text-align: left;
}
}
/* ── Bracket page (/bracket) ─────────────────────── */
.bracket-main {
padding: 1rem 1.25rem 3rem;
max-width: 1600px;
margin: 0 auto;
}
.bracket-hero {
text-align: center;
margin-bottom: 1.5rem;
}
.bracket-title {
font-size: 1.6rem;
font-weight: 700;
color: var(--cup-silver-2);
letter-spacing: 0.05em;
background: var(--gold-gradient);
-webkit-background-clip: text;
background-clip: text;
-webkit-text-fill-color: transparent;
}
.bracket-subtitle {
color: var(--cup-silver-dim);
font-size: 0.9rem;
margin-top: 0.25rem;
}
/* Desktop grid layout */
.bracket-grid {
display: grid;
grid-template-columns: repeat(7, minmax(0, 1fr));
gap: 0.75rem;
align-items: stretch;
}
.bracket-col {
display: flex;
flex-direction: column;
justify-content: space-around;
gap: 0.6rem;
}
.bracket-col-heading {
font-size: 0.72rem;
letter-spacing: 0.1em;
text-transform: uppercase;
color: var(--cup-silver-dim);
text-align: center;
margin-bottom: 0.25rem;
}
.bracket-cup-heading {
color: var(--cup-gold-2);
}
.bracket-col-cup {
justify-content: center;
}
.bracket-matchup {
display: flex;
flex-direction: column;
gap: 2px;
background: var(--card);
border: 1px solid var(--card-border);
border-radius: 8px;
padding: 0.45rem 0.55rem;
text-decoration: none;
color: inherit;
transition: border-color 0.12s ease, transform 0.12s ease;
}
.bracket-matchup:hover {
border-color: var(--cup-gold-1);
transform: translateY(-1px);
}
.bracket-matchup:focus-visible {
outline: 2px solid var(--cup-gold-2);
outline-offset: 2px;
}
.bracket-matchup-active {
border-color: var(--cup-gold-dim);
}
.bracket-matchup-complete {
opacity: 0.75;
}
.bracket-matchup-empty {
background: transparent;
border-style: dashed;
}
.bracket-col-cup .bracket-matchup {
border-color: var(--cup-gold-dim);
background: linear-gradient(135deg, #162844 0%, #1a1a2e 100%);
padding: 0.7rem 0.6rem;
}
.bracket-col-cup .bracket-matchup:hover {
border-color: var(--cup-gold-2);
}
.bracket-team {
display: grid;
grid-template-columns: 22px 1fr auto auto;
align-items: center;
gap: 0.4rem;
padding: 0.2rem 0;
font-size: 0.85rem;
}
.bracket-team-logo {
width: 22px;
height: 22px;
object-fit: contain;
}
.bracket-team-abbrev {
font-weight: 600;
color: var(--cup-silver-2);
}
.bracket-team-seed {
font-size: 0.7rem;
color: var(--cup-silver-dim);
letter-spacing: 0.03em;
}
.bracket-team-wins {
font-variant-numeric: tabular-nums;
min-width: 1.2em;
text-align: right;
color: var(--cup-silver-1);
font-weight: 600;
}
.bracket-team-winner {
color: var(--cup-gold-2);
}
.bracket-team-winner .bracket-team-abbrev,
.bracket-team-winner .bracket-team-wins {
color: var(--cup-gold-2);
}
.bracket-team-placeholder {
color: var(--cup-silver-dim);
text-align: center;
padding: 0.3rem 0;
font-size: 0.8rem;
grid-template-columns: 1fr;
}
/* Mobile accordion — hidden on desktop */
.bracket-accordion {
display: none;
flex-direction: column;
gap: 0.75rem;
margin-top: 1rem;
}
.bracket-round {
background: var(--card);
border: 1px solid var(--card-border);
border-radius: 10px;
overflow: hidden;
}
.bracket-round-summary {
padding: 0.8rem 1rem;
cursor: pointer;
font-weight: 600;
color: var(--cup-gold-2);
letter-spacing: 0.03em;
list-style: none;
display: flex;
justify-content: space-between;
align-items: center;
}
.bracket-round-summary::after {
content: "+";
color: var(--cup-silver-dim);
font-size: 1.2rem;
}
.bracket-round[open] .bracket-round-summary::after {
content: "";
}
.bracket-round-body {
padding: 0 0.8rem 0.8rem;
display: flex;
flex-direction: column;
gap: 0.8rem;
}
.bracket-round-half-heading {
font-size: 0.7rem;
letter-spacing: 0.1em;
text-transform: uppercase;
color: var(--cup-silver-dim);
margin-bottom: 0.3rem;
}
.bracket-round-half {
display: flex;
flex-direction: column;
gap: 0.4rem;
}
/* Switch layout at narrow widths */
@media (max-width: 900px) {
.bracket-grid { display: none; }
.bracket-accordion { display: flex; }
}
/* Banner bracket link (both pages) */
.banner-bracket-link {
background: transparent;
border: 1px solid var(--cup-gold-dim);
color: var(--cup-gold-2);
padding: 0.35rem 0.7rem;
border-radius: 999px;
font-size: 0.78rem;
letter-spacing: 0.05em;
text-decoration: none;
transition: border-color 0.12s ease, color 0.12s ease;
white-space: nowrap;
}
.banner-bracket-link:hover {
border-color: var(--cup-gold-1);
color: var(--cup-gold-2);
}