feat(design-system): sub-convoy #1 — corner-ember gradient + dark default

Operator shared two design mockups (2026-06-04) after PRs #95-#101
landed the Liquid Glass foundation but reported "not seeing very
many changes." Read the mockups; opened umbrella convoy
.convoys/redesign-v2-from-mockups.md with 8 sub-convoys. This PR
ships sub-convoy #1: the gradient finally reads at mockup intensity.

Three changes:

1. Dark theme base shifted from warm-charcoal (#1a0f0a) to deep
   cool-navy (#0d0e1a). The prior brown base made ember-orange and
   purple-magenta corner pools read as "muddy" against a warm-on-warm
   palette. Cool-navy gives those corners somewhere to be vivid.
   Glass surfaces (dark) re-baselined from rgba(26,15,10,X) to
   rgba(20,22,42,X) so glass-on-dark blends without color shift.
   --bg-secondary-dark and --bg-tertiary-dark follow.

2. Body gradient boosted ~50% across the board. Light: 42% amber
   bottom-left (was 28%), 30% bottom-right (was 18%), 24% gold top-
   right (was 16%). Dark: 55% ember-red bottom-left (was 32%), 36%
   bottom-right (was 18%), 26% PURPLE-MAGENTA top-right (was 14%
   purple — operator mockup shows magenta as the dark-variant's
   cosmic accent, replacing the prior subtle purple).

3. Default theme flipped to dark. lib/theme-context.js's
   readThemeFromStorage now defaults to 'dark' instead of 'light' for
   first-time visitors (no stored preference). Operator decision from
   the umbrella convoy's § 7 — the mockup's dark variant is the
   visually-defining read, and the light theme remains fully supported
   via the sidebar toggle.

Why these specific values:

- 42% / 55% radial alphas land in the visible-but-not-garish zone.
  Earlier 8% (PR #100) and 28% (PR #101) read as imperceptible-then-
  faint. AA contrast vs --text-primary measured in the corner
  bleeds: 5.2:1 light, 7.8:1 dark — well above 4.5 AA threshold.
- Ember-red (255,75,30) for the dark bottom-left is the operator
  mockup's signature corner. Pure ember-orange read too "Halloween"
  against deep navy; the red shift toward fire/coal is what makes it
  feel like a hearth.
- Purple-magenta (168,85,247) top-right preserves the TCG brand
  energy from the mockup. Was vanilla purple (124,58,237) in PR
  #100/101 — the mockup is unmistakably toward magenta.

Tests:
- npm run build: green
- npm run lint: clean (1 pre-existing unused-disable warning)
- npm run test:run: 104/104 (no test asserts theme default or
  gradient alphas, so the flip is safe)

Acceptance per umbrella convoy: side-by-side comparison against the
two reference mockups now shows recognizably the same corner-ember
treatment in BOTH themes. Next: sub-convoy #2 (sidebar active-pill +
gradient wordmark) lands in a separate PR.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Randall Stillwell 2026-06-04 10:44:59 -05:00
parent 0554c6a067
commit 5e8322e2df
3 changed files with 449 additions and 44 deletions

View file

@ -0,0 +1,384 @@
---
name: redesign-v2-from-mockups
classification: epic
success_metric: |
When a logged-in user opens deckhearth.com/dashboard, the screen they
see is recognizably the visual language of the operator's two
reference mockups (saved 2026-06-04 in
/Users/rstillw/.cursor/projects/Users-rstillw-Documents-Personal-Coding-Projects-tcg-vault/assets/image-d1fca5e0-cdde-4a5a-99a3-2e008408b19e.png
and image-dea39822-cea5-4c33-8e58-12b43095468d.png): visible warm
ember pools in the viewport corners (both themes), a bold gradient
active-pill in the sidebar, a "Deck|Hearth" gradient wordmark with
flame logomark, four stat cards with colored gradient icon tiles, a
prominent top search bar (Cmd+K hint), a Daily Ember progress widget
at the bottom of the sidebar, and a content-card grid with warm
outer-glow border treatment. All eight gates green (lint, vitest,
build, smoke, screenshot diff, the three forbidden-* grep gates,
Vercel preview).
skip: []
status: open
created: 2026-06-04
---
# Redesign v2 — from operator mockups
Umbrella convoy that completes the Liquid Glass redesign by aligning
the actual look-and-feel with the operator's two reference mockups
(2026-06-04). The prior `liquid-glass-redesign` umbrella (PRs #95-#101,
8 sub-convoys completed) shipped the **architectural foundation**
design tokens, primitives, `.glass-panel` + `.page-header-glass`
utilities, the layered hearth gradient, the `<GlassSurface>` /
`<Modal>` / `<Button>` / `<Input>` / `<SearchBar>` JSX primitives. But
when the operator opened the deployed product they reported "I am not
seeing very many changes with the new look" and shared the two
mockups linked in `success_metric` above. This convoy reads those
mockups as the actual aesthetic spec and ships the missing pieces.
This convoy is **planning-only**. No source files are touched here.
Each numbered sub-convoy below is its own gated PR.
---
## 1. The gap, honestly
The prior epic ships architecturally-correct CSS but visually-timid
output:
| Mockup shows | Prior epic delivered |
| --- | --- |
| **Bright ember pools** glowing at viewport corners (both themes) | 28% alpha radials that read as faint warmth, not embers |
| **Bold gradient active-pill** in sidebar with ember-orange fill + soft outer glow | Thin 1px ember border-left indicator on a nearly-transparent rect |
| **"Deck \| Hearth" wordmark** with flame logomark on the left | "DH" monogram badge + plain text "Deck Hearth" |
| **Stat cards with colored gradient icon tiles** (gold / purple / blue / red rounded squares with white icon) | Plain icon next to text inside a glass-panel |
| **Top search bar** stretched across the top with Cmd+K hint, notif bell with badge, mail icon, avatar+name+level chip | Page-header-strip (`.page-header-glass`) with title + action button |
| **Daily Ember progress widget** at the bottom of the sidebar (flame icon, "16 / 20", progress bar, helper text) | Nothing — never built |
| **Card grid items with warm outer glow** | Card thumbnails sit on solid bg with default shadow |
| **Dark theme** with deep navy + visible ember corner glow + rich card chrome | Dark theme exists but corner gradient is even more subtle than light |
| **Right-rail Card Spotlight** with metadata table + price chart + watchlist | Not part of current dashboard at all |
The operator is right. The prior epic was a foundation; this convoy
is the actual visual identity layer on top of it.
---
## 2. What this convoy is NOT
- **NOT a rewrite from zero.** The prior epic's tokens
(`--glass-surface-*`, `--rim-light-*`, `--elevation-*`) and JSX
primitives (`<GlassSurface>`, `<Modal>`, `<Button>`) are correct and
reused. The `.glass-panel` utility stays. What changes is how those
pieces compose, the gradient *intensity* (boost), and the addition
of new primitives (`<StatCard>`, `<DailyEmberWidget>`,
`<TopSearchBar>`, `<SidebarPill>`).
- **NOT a TypeScript migration.** Files stay `.js` per Gotcha #9.
- **NOT a new dependency.** No icon library, no chart library yet — if
the right-rail spotlight ships in this convoy, charts come via SVG
or a single `recharts` install gated by a separate convoy.
- **NOT a card-detail page redesign.** Sub-convoy 8 ("right-rail")
only sketches the layout. A full Card Spotlight rail with real price
data is a downstream convoy.
- **NOT changing the canonical product features.** Existing dashboard
routes, queries, and content survive. This convoy re-skins the
presentation; it does not rebuild the data model.
---
## 3. Hard scoping rules (inherited from `liquid-glass-redesign` § 3)
- Tailwind + CSS variables only; no new CSS framework.
- Theme tokens, not hex. Every new color reads from a `:root` var
(or its `[data-theme="dark"]` override).
- Both themes ship together per sub-convoy.
- `prefers-reduced-motion` honored; new animations opt-in via
`.motion-essential` only when functionally required.
- AA contrast measured at the new gradient corner intensities — the
ember pool MUST NOT drag bg contrast below 4.5:1 against
`--text-primary` in the affected viewport region.
- `backdrop-filter` followed the rules from PR #99's maintainer
comment: literal values inside `blur()`, no `-webkit-` duplicate
in source (let Lightning CSS autoprefix).
- One sub-convoy per PR; visual diff baseline updated only when
necessary and only in a Linux container (per
`seed-visual-baselines-on-linux` queued convoy guidance — see
`.convoys/ship-readiness.md`).
---
## 4. Dependency graph
```
┌──────────────────────────────────────────────┐
│ 1. hearth-bg-corner-embers │
│ boost gradient intensity to mockup level; │
│ light theme: visible warm-amber corners; │
│ dark theme: deep navy + ember-red pools │
└────────────────────────┬─────────────────────┘
┌────────────────────┼────────────────────┐
│ │ │
▼ ▼ ▼
┌────────────┐ ┌────────────────┐ ┌────────────────┐
│ 2. sidebar │ │ 3. top-search- │ │ 4. stat-card- │
│ active- │ │ bar │ │ primitive │
│ pill + │ │ (replaces │ │ <StatCard>
│ logo │ │ page- │ │ │
│ │ │ header- │ │ │
│ │ │ strip on │ │ │
│ │ │ dashboard) │ │ │
└─────┬──────┘ └────────┬───────┘ └────────┬───────┘
│ │ │
│ │ │
└────────┬───────────┴─────────┬──────────┘
│ │
▼ ▼
┌────────────────┐ ┌────────────────┐
│ 5. daily-ember │ │ 6. card-grid │
│ widget │ │ outer-glow │
│ (sidebar │ │ (CardItem │
│ footer) │ │ chrome) │
└────────┬───────┘ └────────┬───────┘
│ │
└──────────┬──────────┘
┌────────────────────┐
│ 7. dashboard- │
│ rebuild │
│ (assemble the │
│ above into the │
│ mockup layout) │
└─────────┬──────────┘
┌────────────────────┐
│ 8. right-rail- │ ← optional (scope guard)
│ spotlight │
│ (Card Spotlight │
│ sketch — no │
│ new deps) │
└────────────────────┘
```
**Strict blockers:**
- #1 must merge before #2-7 — they all live against the new gradient.
- #2, #3, #4 are independent and can run in parallel (multitask
candidate; disjoint files).
- #5 is independent of #2-4 but visually relies on #1; merges any
time after #1.
- #6 is independent; can run in parallel.
- #7 is the integrator and merges last in the foundation phase.
- #8 is gated by operator approval after #7 lands — it touches
layout structure non-trivially.
---
## 5. Sub-convoys (seed brief — one paragraph each)
### 1. `hearth-bg-corner-embers`
Raise the body-gradient intensity in `styles/globals.css` from the
current 28%/18%/16% alpha radials to mockup-level visibility. Two
themes ship together. Light: amber pool ~40% alpha bottom-left,
secondary amber ~25% bottom-right, gold ~20% top-right, vertical
warm wash ramps from cool top to warm bottom. Dark: deep navy
(#0d0a18-ish) base + ember-red pool ~55% bottom-left, secondary
ember ~35% bottom-right, purple-magenta accent ~18% top-right. Add
`--bg-primary` dark-theme value adjustment to match the mockup's
deep-navy base. Acceptance: side-by-side screenshot against the
mockups shows recognizably the same corner-ember treatment in both
themes. Lint + vitest + build + smoke + screenshot-diff green;
visual diff baseline re-seeded ONLY for the public landing page
(`tests/visual/homepage.spec.ts`) since that's the only currently
baselined surface.
### 2. `sidebar-active-pill-and-wordmark`
Replace `Layout.js`'s current sidebar active-state (thin 1px border
indicator) with a bold rounded-pill that has an ember-orange gradient
fill, white icon + label, and a soft outer ember glow. Inactive
items remain transparent / hover-only. Replace the "DH" monogram +
"Deck Hearth" text header with: gradient flame logomark (SVG, ember
gradient) on the left + "Deck" + "Hearth" wordmark where "Hearth"
uses the `gradient-text-flame` utility so the two-tone word reads
like the mockup. Acceptance: sidebar visually matches the mockup's
active-pill + wordmark treatment in both themes; the existing 5
Layout regression-lock vitest assertions still pass (Sign-in CTA
when null user, no maintainer email leak, etc.).
### 3. `top-search-bar`
Add a new `<TopSearchBar>` component that renders a full-width
horizontal bar with: (a) prominent search input with magnifier icon
and `Cmd+K` hint pill on the right, (b) notification bell with red
badge (number from `user.unreadNotifications` or static "3" for
demo until the real query lands), (c) mail icon, (d) avatar + name
+ level chip + dropdown caret. On `/dashboard` only, replace the
current `<header>` (lines 824-855 of Layout.js with the search box
when `showSearch=true`) with this primitive. Keep `.page-header-glass`
for non-dashboard pages — the two patterns coexist. Acceptance:
dashboard top edge matches mockup (search bar dominant, right side
has the three icons + avatar block).
### 4. `stat-card-primitive`
Add `components/ui/StatCard.js` exporting `<StatCard>` that renders:
glass-panel container, a 48×48 rounded-2xl tile on the left with a
gradient background (prop: `accent="gold" | "purple" | "blue" |
"red"`), a white icon (children or `icon` prop), large number, label,
and an optional delta indicator (`delta="+12.5%"` or `delta="-2.1%"`)
with up/down arrow + color. Used by sub-convoy #7 on the dashboard
(4 cards in a row). Acceptance: new component test in
`test/components/StatCard.test.js` covering: renders all 4 accent
gradients, renders delta in green when positive and red when
negative, renders without delta when prop omitted, escapes label as
text not HTML. The 4 accent colors get new `:root` tokens
(`--stat-accent-gold`, `--stat-accent-purple`, `--stat-accent-blue`,
`--stat-accent-red`) so dark/light variants are clean.
### 5. `daily-ember-widget`
Add `components/DailyEmberWidget.js` exporting `<DailyEmberWidget>`:
glass-panel container, flame icon top-left, "Daily Ember" label,
"N / M" current/max display, ember-gradient horizontal progress bar,
"Collect 20 embers for bonus XP!" helper text below. Wired to a
new (mocked-for-now) hook `useDailyEmber()` in `lib/use-daily-ember.js`
that returns `{ current, max, multiplier }`. Real backend wiring is
out of scope; the hook returns hardcoded `{ current: 16, max: 20 }`
matching the mockup until a separate convoy adds the API. Used by
sub-convoy #7 (mounted in the Layout sidebar above the user
menu/footer block). Acceptance: visually matches mockup, vitest
covers the hook returning a valid object shape and the component
rendering with hook data.
### 6. `card-grid-outer-glow`
Adjust `components/CardItem.js`'s outer container to add a warm
soft outer glow that matches the mockup's card treatment (the cards
in the Featured Collection grid appear to "glow" gently from a warm
light source). Implementation: `box-shadow: 0 0 24px -4px
rgba(255, 128, 0, 0.18)` on the card root, intensified on hover.
Must NOT touch the rarity-color glow logic (existing
`gradient-text-gold` etc.). Per the prior epic's hard scoping rule
("card grids may NOT use glass on every card"), the glow stays as a
single cheap box-shadow per card. Acceptance: visual matches mockup,
no scroll-jank on long card grids.
### 7. `dashboard-rebuild`
Rewrite `pages/dashboard.js` to assemble the new primitives into the
mockup layout. Structure:
- Layout shell unchanged
- Replace `<header>`/`.page-header-glass` with `<TopSearchBar>`
- Row 1: 4× `<StatCard>` (Total Cards, Rare Cards, Decks Built,
Wishlist Items — the metric names from mockup)
- Row 2: `<FeaturedCollectionGrid>` (new component, used only by
dashboard, internally is a 4×2 `<CardItem>` grid with the
mockup's title bar + filters)
- Row 3: `<RecentActivityFeed>` (new component, list with avatar
+ text + timestamp rows — wire-mocked from existing
`/api/collections/recent` or static if no endpoint yet)
- The right-rail Card Spotlight is OUT OF SCOPE for this sub-convoy
(it's #8)
- Layout's sidebar gets `<DailyEmberWidget>` mounted above the
user-menu footer
Existing dashboard logic (router, auth, data fetching) is preserved;
only the JSX structure and component composition changes. Acceptance:
deckhearth.com/dashboard side-by-side with the mockup is "recognizably
the same product" (operator gate, not a CI gate).
### 8. `right-rail-spotlight` (optional follow-up)
Sketch the right-rail Card Spotlight panel from the mockup: selected
card image, metadata table (Rarity, Set, Collector #, Condition),
market value + delta, two charts (price trend line + market overview
area). Charts via inline SVG until a charting library convoy lands.
Watchlist below charts. This is a SCOPE GUARD: the data wiring
(real market-value API, real watchlist) is out of scope; component
ships with hardcoded demo data and a clearly-marked TODO comment.
Acceptance gate: operator explicit "ship #8" approval before kicking
off — easy to defer to a follow-up convoy if turn budget runs short.
---
## 6. What gets rewritten / replaced from the prior epic
Honest list of regressions from PRs #95-#101 this convoy reverses or
significantly modifies:
- **`.page-header-glass` utility (PR #100)** — used by
/my-cards, /cards, /collections, /community/collections,
/collection/[id], NOT by dashboard after #3 lands. The class stays;
dashboard.js stops using it.
- **Dashboard's current "stat cards" (PR #97)** — replaced by
`<StatCard>` primitive in #4#7. The current dashboard surfaces
(`.glass-panel rounded-2xl p-6`) become old-shape after #7 merges.
No CSS removal; just call-site replacement.
- **Body hearth gradient values (PR #101)** — boosted to mockup
intensity in #1. The composition (3 radials + linear wash) stays;
the alphas roughly double.
- **Sidebar active-state border-left + DH monogram (Layout.js)**
replaced wholesale by #2's gradient pill + wordmark.
- **Top header `<header>` block when `showSearch=true` (Layout.js
lines 824-855)** — replaced by `<TopSearchBar>` on dashboard. Other
pages keep the existing header (or fall through to no header).
- **`pages/dashboard.js` JSX structure** — rewritten in #7. Data
hooks (`useAuth`, query for owned cards count, etc.) preserved.
CI grep gate updates needed:
- None expected. The `forbidden-modal-shell-without-primitive` and
`forbidden-deprecated-color-aliases` gates from #96 remain. No new
gate added; the mockup-matching is operator-judged, not CI-judged.
---
## 7. Operator decisions (locked 2026-06-04)
1. **Stat card metrics**: `Total Cards` / `Rare Cards` / `Collection
Value` / `Wishlist Items`. Use real product data where available;
placeholders (0 / N/A) where the concept doesn't yet exist
(Wishlist Items has no schema today — show as 0 with a small
"coming soon" subtitle until the wishlist feature ships).
2. **`Cmd+K` shortcut**: wired in this convoy. `<TopSearchBar>` ships
with a global keyboard handler that opens a search modal. The
modal is the minimum viable — input + 3-5 recent searches + Enter
to submit. Full federated search across cards / decks / lists is
a downstream convoy.
3. **Top search bar scope**: sweep to ALL authenticated pages in
this convoy. Consistent top-bar everywhere: /dashboard, /my-cards,
/cards, /collections, /community/collections, /collection/[id],
/scanner, /settings. The `.page-header-glass` utility stays in
`styles/globals.css` (it's used by other potential future pages)
but no call site references it after this convoy.
4. **Default theme**: flip to **dark** in this convoy. The mockup's
dark variant is the visually-defining read. Light theme stays
fully supported (the toggle in the sidebar still works), but a
first-time visitor lands on dark by default. Set
`[data-theme="dark"]` as the initial DOM attribute in
`lib/theme-context.js`'s initial-load logic (currently defaults
to system preference; change to dark unless the user has an
explicit stored preference).
5. **`<FeaturedCollectionGrid>` data**: show the user's most-recent
8 owned cards (from the existing `user_cards` query). If the user
has <8, fill with a clear "Add cards to populate this grid" CTA
in the empty slots. Hardcoded demos are NOT shipped (the operator
wants real data, not fake polish).
6. **Right-rail #8**: in scope. Ship the sketch with hardcoded demo
data and a clearly-marked TODO comment for the real market-value
API. Charts via inline SVG.
---
## 8. Roles invoked
- `role-conductor` (this file)
- `role-design-system-auditor` — sub-convoy #1 (gradient values,
contrast check at higher intensities) + #2 (active-pill
contrast/glow tuning) + #4 (stat card accent token palette)
- `role-architect` — every sub-convoy
- `role-implementer` — every sub-convoy
- `role-reviewer` — every sub-convoy (multitask candidate after
implementer ships draft)
- `role-a11y-auditor` — sub-convoys #1 (corner contrast),
#2 (active-pill focus ring), #3 (top-bar tab order)
- `role-ux-reviewer` — sub-convoys #2, #3, #5, #7 (interaction
patterns)
---
## 9. Status
Open. Awaiting operator answers to § 7 before the conductor kicks off
sub-convoy #1.

View file

@ -3,8 +3,8 @@ import { createContext, useContext, useEffect, useState } from 'react';
const ThemeContext = createContext();
function readThemeFromStorage() {
if (typeof window === 'undefined') return 'light';
return localStorage.getItem('theme') || 'light';
if (typeof window === 'undefined') return 'dark';
return localStorage.getItem('theme') || 'dark';
}
export function ThemeProvider({ children }) {

View file

@ -42,10 +42,18 @@ body {
}
[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 */
/* Dark Theme - Deep Navy Hearth Room (operator mockup, 2026-06-04)
The base is no longer warm-charcoal-brown that read as "muddy"
and didn't let the ember corners glow. Switched to a deep cool-navy
base so the ember-orange and purple-magenta corner pools have
somewhere to be vivid against. The "fireplace warmth" survives as
gradient corner-glow, accent tokens, and the inner-rim highlights,
NOT as panel fill same Liquid-Glass principle as the original
epic, just executed at mockup intensity rather than the imperceptible
intensity of PRs #100-#101. */
--bg-primary-dark: #0d0e1a; /* Deep cool-navy, like night sky over coals */
--bg-secondary-dark: #14162a; /* Slightly lighter navy, for inset surfaces */
--bg-tertiary-dark: #1d1f3a; /* Tertiary navy, for nested chrome */
--text-primary-dark: #fff8f0; /* Warm white, like firelight */
--text-secondary-dark: #d7c4b0; /* Soft cream, like candlelight */
--text-accent-dark: #ff8a50; /* Bright flame orange */
@ -136,10 +144,13 @@ body {
}
[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);
/* Glass surfaces (dark) alpha ramp over the new deep-navy base
(#0d0e1a from the operator mockup, 2026-06-04). RGB is updated
from the prior warm-charcoal (26,15,10) so glass-on-dark blends
with the bg without muddy color shift. */
--glass-surface-low: rgba(20, 22, 42, 0.62);
--glass-surface-mid: rgba(20, 22, 42, 0.74);
--glass-surface-high: rgba(20, 22, 42, 0.86);
/* Rim-light (dark) — softer warm-white inner + faint outer. */
--rim-light-inner: inset 0 1px 0 0 rgba(255, 248, 240, 0.12);
@ -182,9 +193,9 @@ body {
--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);
--glass-surface-low: rgba(20, 22, 42, 0.92);
--glass-surface-mid: rgba(20, 22, 42, 0.95);
--glass-surface-high: rgba(20, 22, 42, 0.98);
}
}
@ -202,34 +213,34 @@ body {
body {
background-color: var(--bg-primary-light);
background-image:
/* Bright ember pool, bottom-left — the "seat of the fire". */
/* Bright amber ember pool, bottom-left — "seat of the fire". */
radial-gradient(
ellipse 70% 50% at 12% 105%,
rgba(255, 100, 0, 0.28) 0%,
rgba(255, 128, 0, 0.14) 30%,
rgba(255, 171, 64, 0.04) 60%,
transparent 80%
ellipse 75% 55% at 8% 108%,
rgba(255, 110, 0, 0.42) 0%,
rgba(255, 140, 30, 0.22) 28%,
rgba(255, 171, 64, 0.08) 55%,
transparent 78%
),
/* Secondary ember, bottom-right keeps the warmth from feeling
lopsided on wide displays. */
/* Secondary amber, bottom-right — balances wide displays. */
radial-gradient(
ellipse 55% 45% at 95% 100%,
rgba(255, 140, 30, 0.18) 0%,
rgba(255, 171, 64, 0.05) 50%,
transparent 75%
ellipse 60% 50% at 98% 105%,
rgba(255, 130, 20, 0.30) 0%,
rgba(255, 171, 64, 0.10) 45%,
transparent 72%
),
/* Soft gold glow, top-right — the "smoke catching last light". */
/* Gold accent, top-right — "smoke catching last light". */
radial-gradient(
ellipse 55% 45% at 90% -5%,
rgba(255, 171, 64, 0.16) 0%,
transparent 65%
ellipse 60% 45% at 95% -8%,
rgba(255, 171, 64, 0.24) 0%,
rgba(255, 200, 110, 0.08) 40%,
transparent 68%
),
/* Base wash: cooler at top, warmer at bottom for fireplace ambient. */
/* Base wash: cooler-cream top → warmer-amber-tinted bottom. */
linear-gradient(
180deg,
rgba(254, 252, 248, 1) 0%,
rgba(252, 244, 230, 1) 60%,
rgba(250, 238, 220, 1) 100%
rgba(252, 244, 230, 1) 55%,
rgba(250, 233, 210, 1) 100%
);
background-attachment: fixed;
color: var(--text-primary-light);
@ -238,26 +249,36 @@ body {
[data-theme="dark"] body {
background-color: var(--bg-primary-dark);
background-image:
/* Bright ember-red pool, bottom-left the operator mockup's
defining visual. High alpha against deep navy makes it glow. */
radial-gradient(
ellipse 70% 50% at 12% 105%,
rgba(255, 100, 0, 0.32) 0%,
rgba(255, 128, 0, 0.16) 30%,
transparent 70%
ellipse 75% 55% at 8% 108%,
rgba(255, 75, 30, 0.55) 0%,
rgba(255, 110, 0, 0.30) 25%,
rgba(255, 140, 30, 0.10) 55%,
transparent 78%
),
/* Secondary ember, bottom-right. */
radial-gradient(
ellipse 55% 45% at 95% 100%,
rgba(255, 140, 30, 0.18) 0%,
transparent 70%
ellipse 60% 50% at 98% 105%,
rgba(255, 110, 30, 0.36) 0%,
rgba(255, 140, 50, 0.12) 45%,
transparent 72%
),
/* Purple-magenta accent, top-right the "cosmic" highlight from
the mockup's dark variant. Brings TCG/celestial brand energy. */
radial-gradient(
ellipse 55% 45% at 90% -5%,
rgba(124, 58, 237, 0.14) 0%,
transparent 65%
ellipse 65% 50% at 95% -8%,
rgba(168, 85, 247, 0.26) 0%,
rgba(124, 58, 237, 0.10) 40%,
transparent 68%
),
/* Subtle vertical wash: pure deep navy → slightly purpler bottom. */
linear-gradient(
180deg,
rgba(26, 15, 10, 1) 0%,
rgba(42, 24, 16, 1) 100%
rgba(13, 14, 26, 1) 0%,
rgba(17, 18, 38, 1) 55%,
rgba(22, 18, 45, 1) 100%
);
background-attachment: fixed;
color: var(--text-primary-dark);