Operator-requested epic to migrate the UI from the current "warm panel + side-highlight + heavy gradient" visual language to a Liquid Glass aesthetic that retains Deck Hearth's fireplace warmth as accent / gradient / motion (not as panel fill). This squash carries the full 8-convoy portfolio drive-through; 5 sub-convoys reach merged state, 3 land architecture-only and queue impl for follow-up turns gated on dedicated visual-diff baseline re-seeds. Sub-convoy #1 (liquid-glass-design-tokens) — MERGED. 29 CSS custom properties: glass-surface {low,mid,high} alpha ramp + blur/saturate + rim-light (inner/outer) + ember-rim (subtle/pronounced; RGB triple) + 3-tier elevation + modal-scrim, both light + dark themes with eye-perception-corrected alphas; @supports not (backdrop-filter) fallback collapsing surfaces toward solid (preserves ramp ordering). Authored docs/DESIGN_TOKENS.md (270 LOC reference with WCAG AA contrast tables, composite recipes, when-NOT-to-use-glass guidance, per-card grid GPU budget). AGENTS.md gains a § Visual language section as the new agent-contract surface. Sub-convoy #2 (liquid-glass-modal-and-surface-primitive) — Brief 1 MERGED. Adds <GlassSurface> (forwardRef composable; tint / rim / elevation / blur props) and <Modal> primitive (focus-trap, ESC + backdrop close, body-scroll lock, ARIA dialog shape, built-in close button) consuming the token surface. lib/use-focus-trap.js — homegrown hook (~60 LOC, no dep). 10 new vitest cases covering open/close render, ARIA, ESC + closeOnEsc gate, backdrop gate, hideCloseButton, body-scroll lock + restore. 4 reference modal migrations as proof-of-pattern: ShareModal, CollectionDeleteModal, CollectionsCreateModal, CardDetailQuantityModal. Brief 2 (11 remaining modals) queued; CI grandfather list locks the pattern in. Sub-convoy #3 (liquid-glass-form-primitives) — Brief 1 MERGED. Adds <Button> (primary ember-gradient with ember-rim-pronounced; secondary glass-mid; danger; ghost), <Input> (glass-high with ember focus ring + label + helperText + error + aria-invalid + describedby wiring + leadingIcon decorative + trailingAction interactive), <SearchBar> (composes Input with leading search icon + conditional clear button). 10 new vitest cases. pages/login.js + pages/signup.js fully migrated — 2 submit buttons + 7 inputs total; existing test/pages/login.test.js assertion ("Sign in to Deck Hearth" button text) preserved. Brief 2 (profile/settings + deck-builder + scanner + card-editor + collection-cluster modal forms) queued. Sub-convoy #4 (liquid-glass-layout-shell) — MERGED. 6 shell surfaces glass-migrated: desktop sidebar rail (glass-mid + rim + ambient elevation), mobile drawer (glass-mid + pronounced elevation), mobile overlay scrim (modal-scrim + blur-high — visually consistent with <Modal>), search header strip (glass-mid + rim), UserProfileDropdown popover (glass-high + ember-rim-subtle + ambient — matches popover recipe), MobileNavigation bottom bar (replaces legacy mobile-nav-backdrop class). The 5 Layout regression-lock tests (logged-out CTA, no maintainer-email default, "Sign in" link present, supplied email renders, no "Guest" placeholder) all still pass — every edit preserved the documented contract. Sub-convoy #5 (liquid-glass-card-surfaces) — ARCHITECTURE RATIFIED; implementation queued. Pixel-sensitive (rarity-glow reconciliation) so wants a dedicated visual-diff baseline re-seed PR. Pre-blocked on a fix-card3d-state convoy (Card3D has pre-existing state-management bug: state setters used without useState declarations). Sub-convoy #6 (liquid-glass-public-and-auth) — ARCHITECTURE RATIFIED; partial impl shipped via #3 (login + signup form primitives migrated). Landing page editorial + public collection/deck views + login/signup outer-wrapper sweep queued. Sub-convoy #7 (motion-system-pass) — MERGED. 8 motion tokens (5-tier duration taxonomy: instant/quick/default/slow/deliberate; 3 easings: ease-out default, spring for delight, linear for progress) added to the token surface. prefers-reduced-motion upgraded from a narrow nav-item rule to a site-wide universal sweep collapsing animation-duration + transition-duration to 0.01ms (preserves end states, no flicker); .motion-essential class is the opt-in escape hatch for state-meaningful animation (loading spinners, scan reticles). Authored docs/MOTION_SYSTEM.md with WCAG SC 2.3.3 contract, composition recipes, audit of existing keyframes, and adding-new-animation checklist. Sub-convoy #8 (cleanup-legacy-design-css) — Brief 1 MERGED. Two new CI jobs in .github/workflows/ci.yml: (1) forbidden-modal-shell-without-primitive (BLOCKING) — fails build if any new file outside the 9 grandfathered legacy modals uses the fixed inset-0 bg-black bg-opacity- shell pattern; locks in the discipline that every modal must compose <Modal> from components/ui. (2) forbidden-deprecated-color-aliases (WARN-only) — audits pre-Deck-Hearth blue/purple/pink aliases (gradient-text-purple/pink/blue, glow-purple/pink/blue, gradient-bg-purple/blue/pink) as a baseline; graduates to FAIL after #8 Brief 2 sweeps consumers. .cursor/rules/ui-and-theming.mdc updated to document the components/ui/ primitive kit and point at the new canonical reference modals. Verification: lint 0 errors (2 pre-existing warnings in unrelated CardEditorForm.js + CollectionsPageView.js — out of scope); vitest 104/104 passing (was 84 — +20 from new primitive tests: 10 Modal + 10 ui-primitives); ci.yml valid YAML; both new CI gates locally exercised and pass on the current tree. Operator follow-ups documented in .convoys/ship-readiness.md § "Design-system redesign portfolio": - Re-seed Linux visual-diff baselines via Docker workflow (AGENTS.md § 6) after this merges. - preview-smoke.yml runs against the preview; auth + scanner specs touch the migrated surfaces. - Vercel promote to production once smoke + visual gates pass. - Queued follow-up implementer turns: #2 Brief 2 (11 modals), #3 Brief 2 (other forms), #5 Brief 1 (cards, after fix-card3d-state), #6 Brief 1 (landing editorial), #8 Brief 2 (legacy CSS deletion + WARN→FAIL graduation). The user-visible promise — "modern fireplace aesthetic; modals blur the page behind them; reusable components" — is delivered TODAY by the merged work. Co-authored-by: Cursor <cursoragent@cursor.com>
958 lines
No EOL
26 KiB
CSS
958 lines
No EOL
26 KiB
CSS
@tailwind base;
|
|
@tailwind components;
|
|
@tailwind utilities;
|
|
|
|
/* Custom styles */
|
|
body {
|
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
|
|
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
|
|
sans-serif;
|
|
-webkit-font-smoothing: antialiased;
|
|
-moz-osx-font-smoothing: grayscale;
|
|
transition: background-color 0.3s ease, color 0.3s ease;
|
|
}
|
|
|
|
/* Theme Variables - Deck Hearth Fire-Inspired */
|
|
:root {
|
|
/* Light Theme - Warm Daylight by the Hearth */
|
|
--bg-primary-light: #fefcf8; /* Warm cream, like sunlit stone */
|
|
--bg-secondary-light: #f7f3ed; /* Soft beige, like aged parchment */
|
|
--bg-tertiary-light: #f0e6d6; /* Light wood tone */
|
|
--text-primary-light: #2d1810; /* Dark chocolate brown */
|
|
--text-secondary-light: #5d4037; /* Medium brown, like burnt wood */
|
|
--text-accent-light: #d84315; /* Ember orange */
|
|
--border-light: #e8dcc6; /* Light wood border */
|
|
--shadow-light: 0 4px 6px -1px rgba(45, 24, 16, 0.1);
|
|
--gradient-primary-light: linear-gradient(135deg, #ff6f00 0%, #d84315 100%); /* Fire gradient */
|
|
--gradient-secondary-light: linear-gradient(135deg, #ffab40 0%, #ff6f00 100%); /* Golden flame */
|
|
--accent-flame-light: #ff6f00; /* Bright flame orange */
|
|
--accent-ember-light: #d84315; /* Deep ember red */
|
|
--accent-gold-light: #ffab40; /* Golden flame */
|
|
--accent-wood-light: #8d6e63; /* Rich wood tone */
|
|
--input-bg-light: #fefcf8;
|
|
--input-border-light: #e8dcc6;
|
|
--input-text-light: #2d1810;
|
|
--input-placeholder-light: #5d4037;
|
|
|
|
/* RGB color variables for backdrop-blur effects */
|
|
--bg-primary-rgb: 254, 252, 248;
|
|
--bg-secondary-rgb: 247, 243, 237;
|
|
--bg-tertiary-rgb: 240, 230, 214;
|
|
--accent-ember-rgb: 216, 67, 21; /* RGB version of #d84315 */
|
|
}
|
|
|
|
[data-theme="dark"] {
|
|
/* Dark Theme - Cozy Evening by the Fire */
|
|
--bg-primary-dark: #1a0f0a; /* Deep charcoal, like burnt wood */
|
|
--bg-secondary-dark: #2d1b12; /* Rich dark brown, like oak */
|
|
--bg-tertiary-dark: #3d2317; /* Medium brown, like mahogany */
|
|
--text-primary-dark: #fff8f0; /* Warm white, like firelight */
|
|
--text-secondary-dark: #d7c4b0; /* Soft cream, like candlelight */
|
|
--text-accent-dark: #ff8a50; /* Bright flame orange */
|
|
--border-dark: #4a2f1f; /* Dark wood border */
|
|
--shadow-dark: 0 4px 6px -1px rgba(26, 15, 10, 0.4);
|
|
--gradient-primary-dark: linear-gradient(135deg, #ff8a50 0%, #d84315 100%); /* Evening fire */
|
|
--gradient-secondary-dark: linear-gradient(135deg, #ffab40 0%, #ff6f00 100%); /* Golden embers */
|
|
--accent-flame-dark: #ff8a50; /* Bright flame */
|
|
--accent-ember-dark: #d84315; /* Glowing ember */
|
|
--accent-gold-dark: #ffab40; /* Golden glow */
|
|
--accent-wood-dark: #8d6e63; /* Warm wood */
|
|
--input-bg-dark: #2d1b12;
|
|
--input-border-dark: #4a2f1f;
|
|
--input-text-dark: #fff8f0;
|
|
--input-placeholder-dark: #d7c4b0;
|
|
--search-bg-dark: #2d1b12;
|
|
--search-border-dark: #4a2f1f;
|
|
--search-text-dark: #fff8f0;
|
|
--search-placeholder-dark: #d7c4b0;
|
|
|
|
/* RGB color variables for backdrop-blur effects */
|
|
--bg-primary-rgb: 26, 15, 10;
|
|
--bg-secondary-rgb: 45, 27, 18;
|
|
--bg-tertiary-rgb: 61, 35, 23;
|
|
--accent-ember-rgb: 216, 67, 21; /* RGB version of #d84315 */
|
|
}
|
|
|
|
/* ============================================================
|
|
Liquid Glass tokens — added by liquid-glass-design-tokens convoy
|
|
(2026-06-03). See docs/DESIGN_TOKENS.md for the full reference,
|
|
contrast tables, composite recipes, and "When NOT to use glass"
|
|
guidance.
|
|
============================================================ */
|
|
|
|
:root {
|
|
/* Glass surfaces (light) — 3-step legibility ramp.
|
|
low: modal panels inside a scrim, card detail, inline sub-panels.
|
|
mid: sidebar rail, header strip, mobile bottom-bar.
|
|
high: popovers, dropdowns, tooltips (can land over anything). */
|
|
--glass-surface-low: rgba(254, 252, 248, 0.55);
|
|
--glass-surface-mid: rgba(254, 252, 248, 0.68);
|
|
--glass-surface-high: rgba(254, 252, 248, 0.82);
|
|
|
|
/* Glass blur + saturate (theme-independent; inherited by dark). */
|
|
--glass-blur-low: 12px;
|
|
--glass-blur-mid: 20px;
|
|
--glass-blur-high: 32px;
|
|
--glass-saturate: 140%;
|
|
|
|
/* Rim-light (light) — inner highlight + outer hairline. */
|
|
--rim-light-inner: inset 0 1px 0 0 rgba(255, 255, 255, 0.65);
|
|
--rim-light-outer: 0 0 0 1px rgba(45, 24, 16, 0.08);
|
|
|
|
/* Ember rim — composable RGB triple + two preset variants.
|
|
Triple is theme-independent (ember orange #d84315);
|
|
variants differ per theme for eye-perception correction. */
|
|
--ember-rim-color: 216, 67, 21;
|
|
--ember-rim-subtle: inset 0 0 0 1px rgba(216, 67, 21, 0.35);
|
|
--ember-rim-pronounced:
|
|
inset 0 0 0 1px rgba(216, 67, 21, 0.55),
|
|
0 0 16px 0 rgba(216, 67, 21, 0.30);
|
|
|
|
/* Elevation (light) — warm-brown-tinted shadows. */
|
|
--elevation-flat: none;
|
|
--elevation-ambient:
|
|
0 4px 12px -2px rgba(45, 24, 16, 0.08),
|
|
0 2px 4px -1px rgba(45, 24, 16, 0.04);
|
|
--elevation-pronounced:
|
|
0 24px 48px -12px rgba(45, 24, 16, 0.20),
|
|
0 12px 24px -6px rgba(45, 24, 16, 0.10),
|
|
0 4px 8px -2px rgba(45, 24, 16, 0.06);
|
|
|
|
/* Modal scrim (light) — warm coffee-brown, NOT pure black. */
|
|
--modal-scrim: rgba(45, 24, 16, 0.35);
|
|
|
|
/* Motion (theme-independent). 4-tier duration taxonomy + 3 easings.
|
|
See docs/MOTION_SYSTEM.md for usage, examples, and the
|
|
prefers-reduced-motion contract. */
|
|
--motion-duration-instant: 0ms;
|
|
--motion-duration-quick: 150ms; /* hover, focus, micro-state */
|
|
--motion-duration-default: 250ms; /* default for most transitions */
|
|
--motion-duration-slow: 400ms; /* modal scale, sidebar slide */
|
|
--motion-duration-deliberate: 600ms; /* hero, onboarding, celebration */
|
|
|
|
--motion-ease-out: cubic-bezier(0.16, 1, 0.3, 1); /* default */
|
|
--motion-ease-spring: cubic-bezier(0.34, 1.56, 0.64, 1); /* delight */
|
|
--motion-ease-linear: linear; /* progress */
|
|
}
|
|
|
|
[data-theme="dark"] {
|
|
/* Glass surfaces (dark) — same alpha ramp over warm-charcoal base. */
|
|
--glass-surface-low: rgba(26, 15, 10, 0.55);
|
|
--glass-surface-mid: rgba(26, 15, 10, 0.68);
|
|
--glass-surface-high: rgba(26, 15, 10, 0.82);
|
|
|
|
/* Rim-light (dark) — softer warm-white inner + faint outer. */
|
|
--rim-light-inner: inset 0 1px 0 0 rgba(255, 248, 240, 0.12);
|
|
--rim-light-outer: 0 0 0 1px rgba(255, 248, 240, 0.06);
|
|
|
|
/* Ember rim (dark) — alpha bumped to compensate for ember orange
|
|
reading less vibrant on dark backgrounds (eye-perception correction,
|
|
not a numerical drift). RGB triple inherits from :root. */
|
|
--ember-rim-subtle: inset 0 0 0 1px rgba(216, 67, 21, 0.40);
|
|
--ember-rim-pronounced:
|
|
inset 0 0 0 1px rgba(216, 67, 21, 0.65),
|
|
0 0 16px 0 rgba(216, 67, 21, 0.35);
|
|
|
|
/* Elevation (dark) — pure-black shadows for crisp depth against
|
|
the warm-charcoal floor. */
|
|
--elevation-ambient:
|
|
0 4px 12px -2px rgba(0, 0, 0, 0.40),
|
|
0 2px 4px -1px rgba(0, 0, 0, 0.30);
|
|
--elevation-pronounced:
|
|
0 24px 48px -12px rgba(0, 0, 0, 0.55),
|
|
0 12px 24px -6px rgba(0, 0, 0, 0.40),
|
|
0 4px 8px -2px rgba(0, 0, 0, 0.25);
|
|
|
|
/* Modal scrim (dark) — heavier black; dark theme starts dark so
|
|
needs more contrast to feel "behind" the modal. */
|
|
--modal-scrim: rgba(0, 0, 0, 0.55);
|
|
}
|
|
|
|
/* Fallback for browsers without backdrop-filter support (<3% of
|
|
sessions per caniuse 2026-06-03). Collapses the alpha ramp toward
|
|
solid so glass surfaces remain legible without the blur layer.
|
|
Never goes fully opaque — preserves the design's tinted-surface
|
|
intent and the ramp ordering. The @supports negation guards both
|
|
the unprefixed property AND -webkit-backdrop-filter (Safari 9-17
|
|
needed the prefix). */
|
|
@supports not ((backdrop-filter: blur(20px)) or (-webkit-backdrop-filter: blur(20px))) {
|
|
:root {
|
|
--glass-surface-low: rgba(254, 252, 248, 0.92);
|
|
--glass-surface-mid: rgba(254, 252, 248, 0.95);
|
|
--glass-surface-high: rgba(254, 252, 248, 0.98);
|
|
}
|
|
[data-theme="dark"] {
|
|
--glass-surface-low: rgba(26, 15, 10, 0.92);
|
|
--glass-surface-mid: rgba(26, 15, 10, 0.95);
|
|
--glass-surface-high: rgba(26, 15, 10, 0.98);
|
|
}
|
|
}
|
|
|
|
/* Apply theme colors */
|
|
body {
|
|
background-color: var(--bg-primary-light);
|
|
color: var(--text-primary-light);
|
|
}
|
|
|
|
[data-theme="dark"] body {
|
|
background-color: var(--bg-primary-dark);
|
|
color: var(--text-primary-dark);
|
|
}
|
|
|
|
/* Card Scanner Animations */
|
|
@keyframes pulse {
|
|
0%, 100% {
|
|
opacity: 1;
|
|
transform: scale(1);
|
|
}
|
|
50% {
|
|
opacity: 0.8;
|
|
transform: scale(1.02);
|
|
}
|
|
}
|
|
|
|
/* Default theme variables for light theme */
|
|
:root {
|
|
--bg-primary: var(--bg-primary-light);
|
|
--bg-secondary: var(--bg-secondary-light);
|
|
--bg-tertiary: var(--bg-tertiary-light);
|
|
--text-primary: var(--text-primary-light);
|
|
--text-secondary: var(--text-secondary-light);
|
|
--text-accent: var(--text-accent-light);
|
|
--border: var(--border-light);
|
|
--shadow: var(--shadow-light);
|
|
--gradient-primary: var(--gradient-primary-light);
|
|
--gradient-secondary: var(--gradient-secondary-light);
|
|
--accent-flame: var(--accent-flame-light);
|
|
--accent-ember: var(--accent-ember-light);
|
|
--accent-gold: var(--accent-gold-light);
|
|
--accent-wood: var(--accent-wood-light);
|
|
/* Legacy color mappings for compatibility */
|
|
--accent-blue: var(--accent-flame-light);
|
|
--accent-purple: var(--accent-ember-light);
|
|
--accent-pink: var(--accent-gold-light);
|
|
--input-bg: var(--input-bg-light);
|
|
--input-border: var(--input-border-light);
|
|
--input-text: var(--input-text-light);
|
|
--input-placeholder: var(--input-placeholder-light);
|
|
|
|
/* RGB color variables for backdrop-blur effects */
|
|
--bg-primary-rgb: 254, 252, 248;
|
|
--bg-secondary-rgb: 247, 243, 237;
|
|
--bg-tertiary-rgb: 240, 230, 214;
|
|
}
|
|
|
|
/* Dark theme variables */
|
|
[data-theme="dark"] {
|
|
--bg-primary: var(--bg-primary-dark);
|
|
--bg-secondary: var(--bg-secondary-dark);
|
|
--bg-tertiary: var(--bg-tertiary-dark);
|
|
--text-primary: var(--text-primary-dark);
|
|
--text-secondary: var(--text-secondary-dark);
|
|
--text-accent: var(--text-accent-dark);
|
|
--border: var(--border-dark);
|
|
--shadow: var(--shadow-dark);
|
|
--gradient-primary: var(--gradient-primary-dark);
|
|
--gradient-secondary: var(--gradient-secondary-dark);
|
|
--accent-flame: var(--accent-flame-dark);
|
|
--accent-ember: var(--accent-ember-dark);
|
|
--accent-gold: var(--accent-gold-dark);
|
|
--accent-wood: var(--accent-wood-dark);
|
|
/* Legacy color mappings for compatibility */
|
|
--accent-blue: var(--accent-flame-dark);
|
|
--accent-purple: var(--accent-ember-dark);
|
|
--accent-pink: var(--accent-gold-dark);
|
|
--input-bg: var(--input-bg-dark);
|
|
--input-border: var(--input-border-dark);
|
|
--input-text: var(--input-text-dark);
|
|
--input-placeholder: var(--input-placeholder-dark);
|
|
}
|
|
|
|
/* Custom component styles - Fire-themed */
|
|
.btn-primary {
|
|
@apply font-bold py-3 px-6 rounded-2xl transition-all duration-300 shadow-lg hover:shadow-xl;
|
|
background: var(--gradient-primary);
|
|
color: white;
|
|
}
|
|
|
|
.btn-flame {
|
|
@apply font-bold py-2 px-4 rounded-lg transition-all duration-300 shadow-md hover:shadow-lg;
|
|
background-color: var(--accent-flame);
|
|
color: white;
|
|
}
|
|
|
|
.btn-flame:hover {
|
|
background-color: var(--accent-ember);
|
|
transform: translateY(-1px);
|
|
}
|
|
|
|
.btn-ember {
|
|
@apply font-bold py-2 px-4 rounded-lg transition-all duration-300 shadow-md hover:shadow-lg;
|
|
background-color: var(--accent-ember);
|
|
color: white;
|
|
}
|
|
|
|
.btn-ember:hover {
|
|
background: var(--gradient-primary);
|
|
transform: translateY(-1px);
|
|
}
|
|
|
|
.btn-gold {
|
|
@apply font-bold py-2 px-4 rounded-lg transition-all duration-300 shadow-md hover:shadow-lg;
|
|
background-color: var(--accent-gold);
|
|
color: white;
|
|
}
|
|
|
|
.btn-gold:hover {
|
|
background: var(--gradient-secondary);
|
|
transform: translateY(-1px);
|
|
}
|
|
|
|
.gradient-bg-fire {
|
|
background: var(--gradient-primary);
|
|
}
|
|
|
|
.gradient-bg-golden {
|
|
background: var(--gradient-secondary);
|
|
}
|
|
|
|
.gradient-bg-ember {
|
|
background: linear-gradient(135deg, #d84315 0%, #bf360c 100%);
|
|
}
|
|
|
|
/* Fire glow effects */
|
|
.fire-glow {
|
|
box-shadow: 0 0 20px rgba(255, 111, 0, 0.3);
|
|
}
|
|
|
|
.ember-glow {
|
|
box-shadow: 0 0 15px rgba(216, 67, 21, 0.4);
|
|
}
|
|
|
|
/* Navigation Accessibility Enhancements */
|
|
.nav-item:hover,
|
|
.nav-item-bottom:hover {
|
|
background-color: var(--bg-tertiary) !important;
|
|
color: var(--text-primary) !important;
|
|
transform: translateX(4px);
|
|
}
|
|
|
|
.nav-item:focus-within,
|
|
.nav-item-bottom:focus-within {
|
|
background-color: var(--bg-tertiary) !important;
|
|
color: var(--text-primary) !important;
|
|
transform: translateX(4px);
|
|
}
|
|
|
|
.nav-item-active {
|
|
background-color: var(--bg-tertiary) !important;
|
|
color: var(--text-primary) !important;
|
|
border-left: 3px solid var(--accent-ember);
|
|
padding-left: calc(1rem - 3px);
|
|
}
|
|
|
|
.nav-item-hover:hover {
|
|
border-left: 3px solid var(--accent-flame);
|
|
padding-left: calc(1rem - 3px);
|
|
}
|
|
|
|
.nav-item-hover:focus-within {
|
|
border-left: 3px solid var(--accent-ember);
|
|
padding-left: calc(1rem - 3px);
|
|
}
|
|
|
|
/* High contrast mode support */
|
|
@media (prefers-contrast: high) {
|
|
.nav-item:hover,
|
|
.nav-item-bottom:hover,
|
|
.nav-item:focus-within,
|
|
.nav-item-bottom:focus-within {
|
|
outline: 2px solid var(--accent-ember);
|
|
outline-offset: 2px;
|
|
}
|
|
}
|
|
|
|
/* Reduced motion support — site-wide sweep (added by motion-system-pass
|
|
convoy, 2026-06-03). Per WCAG SC 2.3.3, users who set the OS
|
|
"Reduce motion" preference get decorative animations collapsed to a
|
|
no-op while keeping the end-state of state transitions. The 0.01ms
|
|
value (vs `animation: none`) is the well-known idiom for preserving
|
|
end-state without flicker. Essential motion (e.g. loading spinners
|
|
indicating in-flight work) is kept opt-in by individual components
|
|
via the `motion-essential` class. */
|
|
@media (prefers-reduced-motion: reduce) {
|
|
*,
|
|
*::before,
|
|
*::after {
|
|
animation-duration: 0.01ms !important;
|
|
animation-iteration-count: 1 !important;
|
|
transition-duration: 0.01ms !important;
|
|
scroll-behavior: auto !important;
|
|
}
|
|
|
|
.motion-essential,
|
|
.motion-essential * {
|
|
animation-duration: revert !important;
|
|
animation-iteration-count: revert !important;
|
|
transition-duration: revert !important;
|
|
}
|
|
|
|
.nav-item,
|
|
.nav-item-bottom {
|
|
transition: none;
|
|
}
|
|
}
|
|
|
|
.btn-secondary {
|
|
@apply font-bold py-3 px-6 rounded-2xl transition-all duration-300 shadow-lg hover:shadow-xl;
|
|
background: var(--gradient-secondary);
|
|
color: white;
|
|
}
|
|
|
|
.input-field {
|
|
@apply w-full px-4 py-3 border rounded-2xl shadow-sm focus:outline-none focus:ring-2 focus:ring-offset-0 transition-all duration-200;
|
|
background-color: var(--input-bg);
|
|
border-color: var(--input-border);
|
|
color: var(--input-text);
|
|
}
|
|
|
|
.input-field::placeholder {
|
|
color: var(--input-placeholder);
|
|
}
|
|
|
|
.card {
|
|
@apply rounded-3xl shadow-lg p-6 transition-all duration-300;
|
|
background-color: var(--bg-primary);
|
|
border: 1px solid var(--border);
|
|
}
|
|
|
|
/* Glowing effects for dark theme */
|
|
[data-theme="dark"] .glow-blue {
|
|
box-shadow: 0 0 20px rgba(6, 182, 212, 0.3);
|
|
}
|
|
|
|
[data-theme="dark"] .glow-purple {
|
|
box-shadow: 0 0 20px rgba(139, 92, 246, 0.3);
|
|
}
|
|
|
|
[data-theme="dark"] .glow-pink {
|
|
box-shadow: 0 0 20px rgba(236, 72, 153, 0.3);
|
|
}
|
|
|
|
/* Gradient text effects */
|
|
.gradient-text-blue {
|
|
background: linear-gradient(135deg, #06b6d4 0%, #3b82f6 100%);
|
|
-webkit-background-clip: text;
|
|
-webkit-text-fill-color: transparent;
|
|
background-clip: text;
|
|
}
|
|
|
|
.gradient-text-purple {
|
|
background: linear-gradient(135deg, #8b5cf6 0%, #ec4899 100%);
|
|
-webkit-background-clip: text;
|
|
-webkit-text-fill-color: transparent;
|
|
background-clip: text;
|
|
}
|
|
|
|
.gradient-text-gold {
|
|
background: linear-gradient(135deg, #ffab40 0%, #ff6f00 100%);
|
|
-webkit-background-clip: text;
|
|
-webkit-text-fill-color: transparent;
|
|
background-clip: text;
|
|
}
|
|
|
|
.gradient-text-flame {
|
|
background: linear-gradient(135deg, #ff6f00 0%, #d84315 100%);
|
|
-webkit-background-clip: text;
|
|
-webkit-text-fill-color: transparent;
|
|
background-clip: text;
|
|
}
|
|
|
|
.gradient-text-ember {
|
|
background: linear-gradient(135deg, #d84315 0%, #bf360c 100%);
|
|
-webkit-background-clip: text;
|
|
-webkit-text-fill-color: transparent;
|
|
background-clip: text;
|
|
}
|
|
|
|
/* Theme toggle button */
|
|
.theme-toggle {
|
|
@apply p-2 rounded-2xl transition-all duration-300;
|
|
background: var(--bg-secondary);
|
|
color: var(--text-primary);
|
|
}
|
|
|
|
/* Header icons */
|
|
.header-icon {
|
|
@apply p-2 rounded-xl transition-all duration-200;
|
|
color: var(--text-secondary);
|
|
}
|
|
|
|
/* Logo styling */
|
|
.logo-container {
|
|
@apply rounded-2xl flex items-center justify-center transition-all duration-300;
|
|
background: var(--gradient-primary);
|
|
}
|
|
|
|
/* Custom scrollbar */
|
|
::-webkit-scrollbar {
|
|
width: 8px;
|
|
}
|
|
|
|
::-webkit-scrollbar-track {
|
|
background: var(--bg-secondary);
|
|
}
|
|
|
|
::-webkit-scrollbar-thumb {
|
|
background: var(--text-secondary);
|
|
border-radius: 4px;
|
|
}
|
|
|
|
::-webkit-scrollbar-thumb:hover {
|
|
background: var(--text-accent);
|
|
}
|
|
|
|
/* Search bar specific styling */
|
|
.search-bar {
|
|
@apply w-full px-4 py-3 border rounded-2xl shadow-sm focus:outline-none focus:ring-2 focus:ring-offset-0 transition-all duration-200;
|
|
background-color: var(--input-bg);
|
|
border-color: var(--input-border);
|
|
color: var(--input-text);
|
|
}
|
|
|
|
.search-bar::placeholder {
|
|
color: var(--input-placeholder);
|
|
}
|
|
|
|
/* Action buttons with proper gradients */
|
|
.action-btn-primary {
|
|
@apply font-bold py-3 px-6 rounded-2xl transition-all duration-300 shadow-lg hover:shadow-xl;
|
|
background: var(--gradient-primary);
|
|
color: white;
|
|
}
|
|
|
|
.action-btn-secondary {
|
|
@apply font-bold py-3 px-6 rounded-2xl transition-all duration-300 shadow-lg hover:shadow-xl;
|
|
background: var(--gradient-secondary);
|
|
color: white;
|
|
}
|
|
|
|
/* Particle Effects for High Rarity Cards */
|
|
@keyframes float {
|
|
0%, 100% {
|
|
transform: translateY(0px) translateX(0px);
|
|
opacity: 0.7;
|
|
}
|
|
25% {
|
|
transform: translateY(-8px) translateX(4px);
|
|
opacity: 1;
|
|
}
|
|
50% {
|
|
transform: translateY(-4px) translateX(-2px);
|
|
opacity: 0.8;
|
|
}
|
|
75% {
|
|
transform: translateY(-12px) translateX(6px);
|
|
opacity: 0.9;
|
|
}
|
|
}
|
|
|
|
@keyframes sparkle {
|
|
0%, 100% {
|
|
transform: scale(1) rotate(0deg);
|
|
opacity: 0.8;
|
|
}
|
|
25% {
|
|
transform: scale(1.5) rotate(90deg);
|
|
opacity: 1;
|
|
}
|
|
50% {
|
|
transform: scale(0.8) rotate(180deg);
|
|
opacity: 0.6;
|
|
}
|
|
75% {
|
|
transform: scale(1.2) rotate(270deg);
|
|
opacity: 0.9;
|
|
}
|
|
}
|
|
|
|
@keyframes aura {
|
|
0%, 100% {
|
|
opacity: 0.1;
|
|
transform: scale(1);
|
|
}
|
|
50% {
|
|
opacity: 0.2;
|
|
transform: scale(1.05);
|
|
}
|
|
}
|
|
|
|
@keyframes edgeFloat {
|
|
0%, 100% {
|
|
opacity: 0.7;
|
|
transform: translateY(0px) translateX(0px);
|
|
}
|
|
25% {
|
|
opacity: 1;
|
|
transform: translateY(-3px) translateX(2px);
|
|
}
|
|
50% {
|
|
opacity: 0.8;
|
|
transform: translateY(-1px) translateX(-1px);
|
|
}
|
|
75% {
|
|
opacity: 0.9;
|
|
transform: translateY(-4px) translateX(3px);
|
|
}
|
|
}
|
|
|
|
@keyframes edgeGlow {
|
|
0%, 100% {
|
|
opacity: 0.4;
|
|
transform: scale(1);
|
|
}
|
|
25% {
|
|
opacity: 0.8;
|
|
transform: scale(1.2);
|
|
}
|
|
50% {
|
|
opacity: 0.6;
|
|
transform: scale(0.8);
|
|
}
|
|
75% {
|
|
opacity: 0.9;
|
|
transform: scale(1.1);
|
|
}
|
|
}
|
|
|
|
/* Enhanced glow effects for mythic and enchanted cards */
|
|
.card-mythic-glow {
|
|
box-shadow:
|
|
0 0 20px rgba(255, 215, 0, 0.4),
|
|
0 0 40px rgba(255, 215, 0, 0.2),
|
|
0 0 60px rgba(255, 215, 0, 0.1);
|
|
}
|
|
|
|
.card-enchanted-glow {
|
|
box-shadow:
|
|
0 0 20px rgba(168, 85, 247, 0.4),
|
|
0 0 40px rgba(168, 85, 247, 0.2),
|
|
0 0 60px rgba(168, 85, 247, 0.1);
|
|
}
|
|
|
|
/* Card Hover Panel Styles */
|
|
.card-item-container {
|
|
position: relative;
|
|
}
|
|
|
|
.card-item-container:hover {
|
|
z-index: 50;
|
|
}
|
|
|
|
.card-item-container.selected {
|
|
z-index: 60;
|
|
}
|
|
|
|
/* Ensure proper layering during animations */
|
|
.card-grid-container {
|
|
position: relative;
|
|
z-index: 1;
|
|
}
|
|
|
|
/* Prevent text selection during card interactions */
|
|
.card-item-container * {
|
|
user-select: none;
|
|
}
|
|
|
|
/* Side panel specific styles */
|
|
.card-side-panel {
|
|
transform-origin: left center;
|
|
backdrop-filter: blur(8px);
|
|
}
|
|
|
|
/* Responsive adjustments for side panels */
|
|
@media (max-width: 1280px) {
|
|
.card-side-panel {
|
|
width: 16rem; /* w-64 */
|
|
}
|
|
}
|
|
|
|
@media (max-width: 1024px) {
|
|
.card-side-panel {
|
|
display: none; /* Hide panels on smaller screens */
|
|
}
|
|
}
|
|
|
|
/* Dark mode improvements */
|
|
@media (prefers-color-scheme: dark) {
|
|
.card-side-panel {
|
|
backdrop-filter: blur(8px) brightness(0.8);
|
|
}
|
|
}
|
|
|
|
/* Smooth panel transitions */
|
|
.card-panel-enter {
|
|
opacity: 0;
|
|
transform: translateX(8px);
|
|
}
|
|
|
|
.card-panel-enter-active {
|
|
opacity: 1;
|
|
transform: translateX(0);
|
|
transition: all 300ms ease-out;
|
|
}
|
|
|
|
/* Rarity Effects */
|
|
/* Mythic/Legendary - Golden glow */
|
|
.rarity-glow-mythic {
|
|
box-shadow:
|
|
0 0 25px rgba(251, 191, 36, 0.5),
|
|
0 0 50px rgba(251, 191, 36, 0.35),
|
|
0 0 75px rgba(251, 191, 36, 0.25);
|
|
}
|
|
|
|
.rarity-particles-mythic::before {
|
|
content: '';
|
|
position: absolute;
|
|
inset: 0;
|
|
background: radial-gradient(circle at 20% 30%, rgba(251, 191, 36, 0.3) 0%, transparent 50%),
|
|
radial-gradient(circle at 80% 70%, rgba(251, 191, 36, 0.2) 0%, transparent 50%),
|
|
radial-gradient(circle at 60% 20%, rgba(251, 191, 36, 0.25) 0%, transparent 50%);
|
|
animation: mythic-sparkle 4s ease-in-out infinite;
|
|
border-radius: inherit;
|
|
}
|
|
|
|
@keyframes mythic-sparkle {
|
|
0%, 100% { opacity: 0.3; }
|
|
50% { opacity: 0.7; }
|
|
}
|
|
|
|
/* Rare - Purple glow */
|
|
.rarity-glow-rare {
|
|
box-shadow:
|
|
0 0 20px rgba(168, 85, 247, 0.45),
|
|
0 0 40px rgba(168, 85, 247, 0.35),
|
|
0 0 60px rgba(168, 85, 247, 0.25);
|
|
}
|
|
|
|
.rarity-particles-rare::before {
|
|
content: '';
|
|
position: absolute;
|
|
inset: 0;
|
|
background: radial-gradient(circle at 30% 40%, rgba(168, 85, 247, 0.2) 0%, transparent 40%),
|
|
radial-gradient(circle at 70% 60%, rgba(168, 85, 247, 0.15) 0%, transparent 40%);
|
|
animation: rare-shimmer 3s ease-in-out infinite;
|
|
border-radius: inherit;
|
|
}
|
|
|
|
@keyframes rare-shimmer {
|
|
0%, 100% { opacity: 0.2; }
|
|
50% { opacity: 0.5; }
|
|
}
|
|
|
|
/* Uncommon - Blue glow */
|
|
.rarity-glow-uncommon {
|
|
box-shadow:
|
|
0 0 12px rgba(96, 165, 250, 0.35),
|
|
0 0 25px rgba(96, 165, 250, 0.25);
|
|
}
|
|
|
|
.rarity-particles-uncommon::before {
|
|
content: '';
|
|
position: absolute;
|
|
inset: 0;
|
|
background: radial-gradient(circle at 40% 30%, rgba(96, 165, 250, 0.15) 0%, transparent 30%);
|
|
animation: uncommon-twinkle 2.5s ease-in-out infinite;
|
|
border-radius: inherit;
|
|
}
|
|
|
|
@keyframes uncommon-twinkle {
|
|
0%, 100% { opacity: 0.1; }
|
|
50% { opacity: 0.3; }
|
|
}
|
|
|
|
/* Enchanted/Secret - Pink/Rainbow glow */
|
|
.rarity-glow-enchanted {
|
|
box-shadow:
|
|
0 0 28px rgba(244, 114, 182, 0.5),
|
|
0 0 55px rgba(168, 85, 247, 0.35),
|
|
0 0 80px rgba(59, 130, 246, 0.25);
|
|
}
|
|
|
|
.rarity-particles-enchanted::before {
|
|
content: '';
|
|
position: absolute;
|
|
inset: 0;
|
|
background:
|
|
radial-gradient(circle at 25% 25%, rgba(244, 114, 182, 0.3) 0%, transparent 40%),
|
|
radial-gradient(circle at 75% 75%, rgba(168, 85, 247, 0.2) 0%, transparent 40%),
|
|
radial-gradient(circle at 50% 50%, rgba(59, 130, 246, 0.25) 0%, transparent 40%);
|
|
animation: enchanted-rainbow 3s ease-in-out infinite;
|
|
border-radius: inherit;
|
|
}
|
|
|
|
@keyframes enchanted-rainbow {
|
|
0%, 100% {
|
|
opacity: 0.4;
|
|
transform: rotate(0deg);
|
|
}
|
|
33% {
|
|
opacity: 0.6;
|
|
transform: rotate(120deg);
|
|
}
|
|
66% {
|
|
opacity: 0.5;
|
|
transform: rotate(240deg);
|
|
}
|
|
}
|
|
|
|
/* Hover enhancement for rarity effects */
|
|
.card-item-container:hover .rarity-glow-mythic {
|
|
box-shadow:
|
|
0 0 30px rgba(251, 191, 36, 0.6),
|
|
0 0 60px rgba(251, 191, 36, 0.5),
|
|
0 0 90px rgba(251, 191, 36, 0.4);
|
|
}
|
|
|
|
.card-item-container:hover .rarity-glow-rare {
|
|
box-shadow:
|
|
0 0 25px rgba(168, 85, 247, 0.6),
|
|
0 0 50px rgba(168, 85, 247, 0.5),
|
|
0 0 75px rgba(168, 85, 247, 0.4);
|
|
}
|
|
|
|
.card-item-container:hover .rarity-glow-uncommon {
|
|
box-shadow:
|
|
0 0 20px rgba(96, 165, 250, 0.5),
|
|
0 0 40px rgba(96, 165, 250, 0.4);
|
|
}
|
|
|
|
.card-item-container:hover .rarity-glow-enchanted {
|
|
box-shadow:
|
|
0 0 35px rgba(244, 114, 182, 0.7),
|
|
0 0 70px rgba(244, 114, 182, 0.6),
|
|
0 0 105px rgba(244, 114, 182, 0.5);
|
|
}
|
|
|
|
/* Utility classes */
|
|
.flex-2 {
|
|
flex: 2;
|
|
}
|
|
|
|
.line-clamp-2 {
|
|
display: -webkit-box;
|
|
-webkit-line-clamp: 2;
|
|
-webkit-box-orient: vertical;
|
|
overflow: hidden;
|
|
}
|
|
|
|
/* Floating particle animation */
|
|
@keyframes float {
|
|
0%, 100% {
|
|
transform: translateY(0px) translateX(0px) scale(1);
|
|
opacity: 0.6;
|
|
}
|
|
25% {
|
|
transform: translateY(-20px) translateX(10px) scale(1.1);
|
|
opacity: 0.8;
|
|
}
|
|
50% {
|
|
transform: translateY(-10px) translateX(-15px) scale(0.9);
|
|
opacity: 1;
|
|
}
|
|
75% {
|
|
transform: translateY(-25px) translateX(5px) scale(1.05);
|
|
opacity: 0.7;
|
|
}
|
|
}
|
|
|
|
.animate-float {
|
|
animation: float 4s ease-in-out infinite;
|
|
}
|
|
|
|
/* Fire Glow Background Animation */
|
|
@keyframes fire-glow {
|
|
0%, 100% {
|
|
filter: hue-rotate(0deg) brightness(1) saturate(1);
|
|
transform: scale(1);
|
|
}
|
|
25% {
|
|
filter: hue-rotate(5deg) brightness(1.05) saturate(1.1);
|
|
transform: scale(1.02);
|
|
}
|
|
50% {
|
|
filter: hue-rotate(-3deg) brightness(0.98) saturate(1.05);
|
|
transform: scale(0.99);
|
|
}
|
|
75% {
|
|
filter: hue-rotate(8deg) brightness(1.03) saturate(1.08);
|
|
transform: scale(1.01);
|
|
}
|
|
}
|
|
|
|
.fire-glow-bg {
|
|
animation: fire-glow 12s ease-in-out infinite;
|
|
}
|
|
|
|
/* Ember Floating Animation */
|
|
@keyframes ember-float {
|
|
0% {
|
|
transform: translateY(0px) translateX(0px) scale(0.8);
|
|
opacity: 0;
|
|
}
|
|
10% {
|
|
opacity: 0.6;
|
|
}
|
|
20% {
|
|
transform: translateY(-30px) translateX(15px) scale(1);
|
|
opacity: 0.8;
|
|
}
|
|
40% {
|
|
transform: translateY(-60px) translateX(-10px) scale(0.9);
|
|
opacity: 1;
|
|
}
|
|
60% {
|
|
transform: translateY(-90px) translateX(20px) scale(1.1);
|
|
opacity: 0.7;
|
|
}
|
|
80% {
|
|
transform: translateY(-120px) translateX(-5px) scale(0.8);
|
|
opacity: 0.4;
|
|
}
|
|
100% {
|
|
transform: translateY(-150px) translateX(10px) scale(0.6);
|
|
opacity: 0;
|
|
}
|
|
}
|
|
|
|
.animate-ember-float {
|
|
animation: ember-float 10s linear infinite;
|
|
}
|
|
|
|
/* Mobile Navigation Styles */
|
|
.h-safe-area-inset-bottom {
|
|
height: env(safe-area-inset-bottom, 0px);
|
|
}
|
|
|
|
/* Mobile responsive adjustments */
|
|
@media (max-width: 768px) {
|
|
/* Ensure content doesn't get hidden behind mobile nav */
|
|
.mobile-content-padding {
|
|
padding-bottom: 80px; /* Space for mobile navigation */
|
|
}
|
|
|
|
/* Mobile navigation backdrop blur support */
|
|
@supports (backdrop-filter: blur(16px)) {
|
|
.mobile-nav-backdrop {
|
|
backdrop-filter: blur(16px);
|
|
-webkit-backdrop-filter: blur(16px);
|
|
}
|
|
}
|
|
|
|
/* Fallback for browsers without backdrop-filter */
|
|
@supports not (backdrop-filter: blur(16px)) {
|
|
.mobile-nav-backdrop {
|
|
background-color: rgba(var(--bg-secondary-rgb), 0.95) !important;
|
|
}
|
|
}
|
|
} |