---
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: shipped
created: 2026-06-04
shipped: 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

**Shipped 2026-06-04.** All 8 sub-convoys merged to main in five PRs:

| Sub-convoy | PR | Squash commit | Notes |
| --- | --- | --- | --- |
| #1 hearth-bg-corner-embers + dark default | [#102](https://github.com/varutasu/tcg-vault/pull/102) | `dd5ddce` | Boosted gradient ~50%; dark base shifted #1a0f0a → #0d0e1a |
| #2 sidebar pill + wordmark | [#103](https://github.com/varutasu/tcg-vault/pull/103) | `ceb041b` | Bundled with #5 |
| #5 Daily Ember widget | [#103](https://github.com/varutasu/tcg-vault/pull/103) | `ceb041b` | Mocked hook; backend in follow-up |
| #4 StatCard primitive | [#104](https://github.com/varutasu/tcg-vault/pull/104) | `3d11ef1` | 4 accent gradients; dashboard 4-up wired |
| #3 TopSearchBar + Cmd+K + sweep | [#105](https://github.com/varutasu/tcg-vault/pull/105) | `e6e7780` | 6 page-header-glass call sites swept |
| #6 card-grid outer-glow | [#106](https://github.com/varutasu/tcg-vault/pull/106) | `ea21b01` | Single-axis shadow per perf budget |
| #7 dashboard rebuild | [#107](https://github.com/varutasu/tcg-vault/pull/107) | `7acee45` | Bundled with #8 |
| #8 right-rail Card Spotlight | [#107](https://github.com/varutasu/tcg-vault/pull/107) | `7acee45` | Sketch tier; demo data + inline SVG charts |

**Follow-up convoys queued** (out of scope for this epic, tracked
here so the next agent doesn't re-discover them):

1. `daily-ember-backend` — wire `useDailyEmber()` to a real
   `/api/user/daily-ember` endpoint that derives the current/max
   from `collection_activity` + `scan_activity` day-bucketed sums.
2. `rarity-aggregation` — populate the StatCard "Rare Cards" tile
   with a real count (currently 0 + "Coming soon"). Requires
   `user_cards.rarity` column to be populated by the import jobs.
3. `wishlist-feature` — populate the StatCard "Wishlist Items" tile.
   Requires a new `wishlist` table + API.
4. `user-activity-feed` — replace `<DashboardRecentActivity>`'s demo
   rows with rows from a new `/api/user/activity` endpoint
   (aggregates across collection_activity + scan_activity +
   trade_activity).
5. `market-data` — replace `<DashboardCardSpotlight>`'s static demo
   with real market-value + price-history + watchlist APIs. Decide
   whether to add a charting library at that time (current sketch
   uses inline SVG per umbrella § 2 "No new dependency").
6. `featured-collection-filters` — wire the "All Sets" filter
   dropdown + grid/list toggle in `<DashboardFeaturedCollection>`.
7. `command-palette-federated-search` — extend
   `<CommandPaletteModal>` from input-then-submit to live federated
   results across cards / decks / lists / users. The current
   "Quick actions" pattern is the minimum viable.
8. `page-header-glass-css-cleanup` — `.page-header-glass` utility
   in `styles/globals.css` has zero call sites after sub-convoy #3
   (umbrella § 7.3 contract). Remove the rule itself in a small
   housekeeping convoy.
9. `topbar-user-menu-dropdown` — the user-menu chip in
   `<TopSearchBar>` currently routes to `/settings` on click. A
   follow-up can add a real dropdown (Settings, Profile, Theme
   Toggle, Sign Out) if the operator wants the full pattern.
