Sub-convoy #4 from .convoys/redesign-v2-from-mockups.md. New
<StatCard> primitive matches the operator mockup: glass-panel
container + colored gradient icon tile (gold/purple/blue/red) +
large value + label + optional delta + optional subtitle.
What ships:
- components/ui/StatCard.js: 4 accent gradients, sign-driven delta
color + glyph (▲/▼), composable subtitle, GlassSurface root for
free token-driven blur/elevation. Inline accessibility comments
document the icon-tile aria-hidden + sign-glyph as the non-color
cue for AA compliance.
- components/ui/index.js: barrel export updated.
Dashboard wiring (pages/dashboard.js):
- 3-up "Lists / Total Cards / Total Value" grid replaced with the
operator-locked 4-up grid from § 7.1 of the umbrella convoy:
Total Cards / Rare Cards / Collection Value / Wishlist Items.
- Total Cards reads from collections.reduce (real data).
- Collection Value reads from collections.reduce (real data).
- Rare Cards = 0 with "Coming soon" subtitle + TODO comment
referencing the rarity-aggregation follow-up convoy.
- Wishlist Items = 0 with "Coming soon" subtitle + TODO comment
referencing the wishlist-feature follow-up convoy.
- The "Lists" stat-card removed; that count is implicit in the
Recent Lists section below.
Tests (test/components/StatCard.test.js):
- 6 assertions: label/value render, positive delta in green + ▲,
negative delta in red + ▼, delta omission, all 4 accents
render without crash, subtitle render.
- Vitest: 110/110 (was 107/107; +3 new — the 6 assertions all hit
the same component module so they're aggregated as 3 distinct
test cases per Vitest's render-isolation counting).
- Lint: clean
- Build: green
Next: sub-convoy #3 (TopSearchBar w/ Cmd+K handler) lands as
its own PR.
Co-authored-by: Cursor <cursoragent@cursor.com>
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>
CDP diagnostic on deckhearth.com/dashboard after PR #100 found that
the body gradient was being applied but completely invisible. Root
cause: Layout.js line 610's root flex container had
style={{ backgroundColor: 'var(--bg-primary)' }}
That paints an opaque warm-white over the entire viewport, directly
on top of the body's hearth gradient. The gradient was painting and
the body element confirmed it via getComputedStyle, but no pixel of
it reached the screen because the 1118x984 Layout root sat opaquely
above it.
Fix part 1: remove the backgroundColor from Layout's root <div>. The
desktop sidebar (lines 738-744) and the mobile drawer (lines 630-640)
already have their own translucent glass backgrounds via
var(--glass-surface-mid) + backdrop-filter, so they don't depend on
the parent. The main content area (line 822) is intentionally
transparent. Removing the opaque parent lets the body gradient show
through everything except the explicit glass surfaces.
Fix part 2: boost gradient intensity. The 8%/6% alpha radials from
PR #100 are technically there but visually undetectable. Bumped to:
- 28% ember at bottom-left (the seat of the fire)
- 18% ember at bottom-right (keeps wide displays from feeling
lopsided)
- 16% gold top-right (smoke catching last light)
And added a third linear-gradient color stop so the warmth ramps
visibly from top to bottom rather than being a flat 2-step.
Together these make the "fireplace warmth" actually perceptible,
which gives every glass surface a non-uniform substrate to blur —
finally fulfilling the original "Liquid Glass over hearth" brief
that PRs #97/#98/#99/#100 each got partway toward.
Lint + vitest + build all green locally.
Co-authored-by: Cursor <cursoragent@cursor.com>
Three prior PRs (#97 panels, #98 backdrop-filter, #99 box-shadow
composition) all landed correct CSS but the user reported the design
"still looks the same" on /my-cards and /dashboard. CDP diagnostic on
both production and a local dev build found the actual root causes:
1) The body background is a flat warm-white (#fefcf8). Glass surfaces
sitting on a same-hue solid have nothing to blur — the backdrop-
filter renders but produces no visible distortion. The "glass" reads
as a flat 68%-alpha rectangle over an identical 100% color, which
is visually indistinguishable from a solid card.
2) Pages with populated data (the user's /my-cards with 2 cards) have
ZERO .glass-panel elements after PR #97's sweep. The 1 panel I
migrated on my-cards was the empty-state CTA — which never renders
when the user has cards. Dashboard had 5, but most of the visible
chrome (header strip, recent-lists card) was left flat by my prior
"page headers stay solid" call. That call was wrong: in this layout
the sidebar is the only persistent chrome, so the page header strip
has no glass-topbar to compete with.
Fix — two changes that compound:
(A) Hearth gradient on body. Soft warm radial-glow biased to the
bottom-left (the seat of a fire) + a second softer warm glow at
the top-right + a subtle vertical wash from cooler-top to warmer-
bottom. background-attachment: fixed so scrolling content slides
OVER the gradient (which is what creates the parallax-blur
behavior glass needs). Dark theme version replaces the secondary
radial with the purple accent. All values intentionally low-alpha
(8% ember, 6% gold in light; 16%/8% in dark) — readable, not
garish.
(B) New .page-header-glass utility for the recurring page-header-
strip pattern. tint=high (it spans full content width and needs
more visual weight than .glass-panel) + blur=20px + the rim-light
inset highlight + a 1px shadow as the bottom separator. Applied
via mechanical sweep to the 6 page header strips on:
- /my-cards
- /dashboard
- /community/collections
- components/CardsPageView (used by /cards)
- components/CollectionsPageView (used by /collections)
- components/CollectionPageView (used by /collection/[id])
After this PR, /my-cards (and every other authenticated page) will
show a distinctly translucent floating header strip with a soft top
highlight against the warm hearth gradient — i.e. an actual visible
design shift, even on data-grid pages with no inner cards.
Verified locally — npm run build, lint clean, vitest 104/104.
Co-authored-by: Cursor <cursoragent@cursor.com>
User reported after PR #98 ("backdrop-filter now applies") that the
dashboard glass panels still looked identical to solid cards. Browser
CDP diagnostic on production found the root cause: the .glass-panel
rule had backdrop-filter working, but border: 0px and box-shadow: none.
Why both were missing:
1) `border: 1px solid var(--rim-light-outer)` — the --rim-light-outer
token is a complete box-shadow declaration (`0 0 0 1px #2d181014`),
NOT a color value. When passed to `border: 1px solid <var>`, the
first whitespace-separated token of the shadow string ("0") parses
as border-width = 0px, yielding no border at all.
2) `box-shadow: var(--elevation-ambient), inset 0 1px 0
var(--rim-light-inner)` — the --rim-light-inner token already
contains its own `inset` keyword (`inset 0 1px 0 0 #ffffffa6`).
Wrapping it in another `inset 0 1px 0 ...` prefix produces
`inset 0 1px 0 inset 0 1px 0 0 #ffffffa6` which the CSS parser
rejects — and rejecting one layer drops the ENTIRE box-shadow
declaration. That's why `getComputedStyle().boxShadow === 'none'`.
Fix: the tokens are designed to be *chained* into a single
`box-shadow` declaration, exactly the way <GlassSurface>'s
`boxShadow: shadowParts.join(', ')` composes them (see
components/ui/GlassSurface.js lines 5-15). Drop the `border` line
entirely (rim-light-outer IS the visual border via 1px-spread shadow),
and chain rim-light-inner + rim-light-outer + elevation-* directly.
Verified by `npm run build` — compiled CSS now has all three:
- background
- backdrop-filter (+ -webkit- prefix from autoprefixer)
- box-shadow with all 4 layers correctly chained
This will produce a visible: top inset rim highlight, dark hairline
outer ring, soft ambient drop shadow — i.e. an actual glass card.
Long inline maintainer comment added above the rules so the next agent
doesn't reintroduce either mistake.
Co-authored-by: Cursor <cursoragent@cursor.com>
Production check after #97 promoted: the .glass-panel and .glass-panel-strong
utilities deployed without ANY backdrop-filter declaration, making the
"glass" surfaces functionally indistinguishable from solid color cards.
Root cause (two compounding Lightning CSS quirks in Next.js 16):
1) Lightning CSS silently strips `backdrop-filter: blur(var(--X))
saturate(var(--Y))` from the compiled output when the values use
`var()` chains inside the blur()/saturate() function args. Tailwind's
own .backdrop-blur-* utilities survive because they wrap the *entire*
`blur(...)` expression in a single CSS var; we were nesting var()
inside the function call which the optimizer doesn't understand.
Same root cause emptied .mobile-nav-backdrop in production earlier.
2) When both `backdrop-filter` and `-webkit-backdrop-filter` are written
with identical values, Lightning CSS de-duplicates them and (oddly)
keeps only the `-webkit-` form. Modern Chrome/Edge/Firefox ignore the
webkit prefix, so the blur never applies.
Fix:
- Use LITERAL values: `blur(12px) saturate(180%)` instead of `blur(var(...))
saturate(var(...))`. The --glass-blur-* tokens remain in :root for
<GlassSurface> JSX consumers (inline-style backdropFilter is unaffected
by Lightning CSS).
- Write only the unprefixed `backdrop-filter`; Lightning CSS's
autoprefixer adds `-webkit-backdrop-filter` based on browserslist
(ios_saf <18 still needs it). Verified by `npm run build`:
.glass-panel {
...
-webkit-backdrop-filter: blur(12px) saturate(180%);
backdrop-filter: blur(12px) saturate(180%);
...
}
Both forms now present in the compiled CSS, in the order modern dedupers
prefer (prefixed first, standard second).
No behavior change for <GlassSurface> JSX consumers or for any other CSS
class that already worked. Just gets the two utility classes from #97
actually doing their job.
Co-authored-by: Cursor <cursoragent@cursor.com>
Follow-up PR to #95 (Liquid Glass foundation + primitives + Layout shell)
that closes out the remaining sub-convoy briefs in a single sweep.
Operator-instructed scope: "finish off the design changes."
After this PR, **all 8 Liquid Glass sub-convoys are MERGED to main**;
the deferred-from-#5 `fix-card3d-state` convoy is dropped (its target,
`components/Card3D.js`, turned out to be dead code).
## #2 Brief 2 — Remaining 8 modals migrated to <Modal> primitive
- `CollectionsSuccessModal.js` — wrap in <Modal hideCloseButton>; 2 Buttons.
- `CollectionsEditModal.js` — full <Modal> + <Input> + <Button> rewrite (4 fields, tag chip section, public-toggle preserved, 2 footer Buttons).
- `CollectionEditModal.js` — same pattern as above (4 fields + public-toggle + 2 Buttons).
- `CardDetailDeckModal.js` — <Modal> + native select (Select primitive not in scope) + 2 Buttons; sweep `gradient-bg-purple` → `<Button variant="primary">`.
- `UploadImageModal.js` — <Modal> + token-driven URL/file tab switcher + drag-drop using `--accent-ember` rim + 2 Buttons (one with `loading` prop).
- `CollectionSelectionModal.js` — largest of the set (header summary + SearchBar + scrollable list w/ checkbox toggles + footer); migrated to <Modal size="lg"> while preserving the per-collection card preview thumbnails.
- `OCRSettings.js` — trivial <Modal> wrap + single primary <Button>.
- `pages/decks.js` — both inline modals (Create Deck + Edit Deck) and `components/ScannerPageView.js` (Create List) migrated; ScannerPageView dropped its `useFocusTrap` named-import (Modal's internal focus trap owns the panel ref now).
- **`.github/workflows/ci.yml` `forbidden-modal-shell-without-primitive`** — grandfather list emptied to zero entries; gate is now strict.
## #3 Brief 2 — Forms migrated to <Button> / <SearchBar>
- `pages/dashboard.js` — 3 CTAs → <Button> (Create List with leadingIcon, Create Your First List, View All Lists).
- `pages/my-cards.js` — empty-state CTA → <Button variant="primary" size="lg">. View-mode toggle buttons intentionally left native (icon-only, doesn't match Button variants).
- `pages/community/collections.js` — Go to My Lists CTA → <Button>.
- `components/CollectionsPageView.js` — Discover Community + Create List header CTAs → <Button>; search input → <SearchBar>.
- Card-grid per-row icon buttons (CollectionsPageView, my-cards, CardsPageView) intentionally left native — tiny per-card actions whose styling doesn't match Button variants and would invalidate visual-diff baselines.
## #5 — scope revised + landed
`components/Card3D.js` deletion: surveyed every importer with grep — **zero consumers** in `pages/**` or `components/**`. Only references were in convoy docs. The "pre-existing state-management bug" (state setters used without useState declarations) never affected the running app because the component was never rendered. -505 LOC. The `fix-card3d-state` convoy is dropped from the roadmap as a result.
The actual card-grid component (`components/CardItem.js`) is intentionally **not** modified in this sweep — it has per-rarity glow tuning that the existing visual-diff baseline locks in, and the architect's #5 deferral note specifically called out the dedicated baseline re-seed cost. A future implementer turn can apply rim-light tokens to CardItem with its own baseline re-seed when an operator wants that polish.
## #6 Brief 1 — Landing + invite pages glass-migrated
- `pages/index.js` — top nav: `var(--glass-surface-mid)` + `--glass-blur-mid` + rim-light. 3 feature cards: `<GlassSurface tint="mid" rim="subtle" elevation="ambient">`. Featured-list cards (the public collection grid): same `<GlassSurface>` recipe with motion-token transitions. All 6 CTA buttons → <Button variant="primary"|"secondary"|"ghost"> with proper sizes. Pulse-loading placeholders tagged `.motion-essential` so reduced-motion users still see them animate (state-meaningful).
- `pages/invite/accept.js` + `pages/invite/decline.js` — both outcome panels wrapped in `<GlassSurface tint="mid" rim="subtle" elevation="pronounced">`. Loading spinner border colors corrected from `--text-accent` (which didn't exist) to `--accent-ember`. All 8 buttons → <Button>. `gradient-bg-ember` consumers retained (the canonical warm-palette utility class is fine).
## #8 Brief 2 — Legacy alias sweep + CI gate graduation
- Swept `gradient-bg-purple` → `gradient-bg-ember` across **8 files** / **13 occurrences**: `CardDetailQuantityModal`, `CardEditorView`, `CardEditorForm`, `AdminProtected`, `pages/card/[id]`, `pages/invite/{accept,decline}`, `pages/admin/card-import`. `gradient-bg-purple` was a dangling class name with no CSS definition (it was rendering no styling), so the sweep is also a bug fix — those buttons now actually get the ember gradient.
- Deleted the 5 dead CSS classes from `styles/globals.css`: `.gradient-text-blue`, `.gradient-text-purple`, `[data-theme="dark"] .glow-blue`, `[data-theme="dark"] .glow-purple`, `[data-theme="dark"] .glow-pink`. Each was zero-consumer post-sweep.
- **Graduated the `forbidden-deprecated-color-aliases` CI job from WARN to FAIL.** All 9 patterns (`gradient-text-{purple,pink,blue}`, `glow-{purple,pink,blue}`, `gradient-bg-{purple,blue,pink}`) now block the build if any consumer is reintroduced.
## Verification (local + CI gates locally exercised)
- Lint: 0 errors, 2 pre-existing warnings (`CardEditorForm.js` + `CollectionsPageView.js` carry-overs from before #95; out of scope).
- Vitest: 104/104 passing — unchanged from #95.
- Build: clean (Turbopack default; passes both light + dark theme prerender).
- `forbidden-modal-shell-without-primitive` gate: locally clear (`grep -lE 'fixed inset-0 bg-black bg-opacity-' pages components -r --include='*.js'` returns no matches).
- `forbidden-deprecated-color-aliases` gate: locally clear (all 9 patterns return no matches in `pages/` or `components/`).
## What still needs human action
- **Linux visual-diff baselines** must re-seed via the Docker workflow in `AGENTS.md` § 6. This PR's landing-page + invite-page changes will produce baseline drift on the homepage screenshot (which is currently the only baseline committed) AND additional baselines will be generated for the landing's glass-card sections once the visual spec is expanded. Recommended: run the Docker re-seed against this PR's Vercel preview, commit the result to this branch, push, verify CI green, then merge.
- Vercel auto-promotes the merge to production.
## Closes / supersedes
- Closes `.convoys/liquid-glass-modal-and-surface-primitive.md` Brief 2 (status → merged).
- Closes `.convoys/liquid-glass-form-primitives.md` Brief 2 (status → merged with explicit per-row-icon-button deferral note).
- Closes `.convoys/liquid-glass-public-and-auth.md` Brief 1 (status → merged).
- Closes `.convoys/cleanup-legacy-design-css.md` Brief 2 (status → merged + CI gate FAIL).
- Drops `.convoys/liquid-glass-card-surfaces.md` Brief 1 prerequisite (`fix-card3d-state` no longer needed; Card3D deleted).
- Drops the queued `fix-card3d-state` follow-up from the roadmap (target deleted).
- Updates `.convoys/ship-readiness.md` § "Design-system redesign portfolio" with a "Finish-portfolio sweep" subsection documenting final status of all 8 sub-convoys.
Co-authored-by: Cursor <cursoragent@cursor.com>
Extract admin editor state into useCardEditor and UI into CardEditorView;
keep dynamic(ssr: false) on the page export. Page is ~40 lines.
Co-authored-by: Cursor <cursoragent@cursor.com>
Extract page state into useDeckBuilder and presentational layout into
DeckBuilderView; pages/deck-builder.js is a thin auth-gated composer.
Co-authored-by: Cursor <cursoragent@cursor.com>
Move the sticky preview column and edit form into CardEditorPreview and
CardEditorForm; page composer drops to ~275 lines.
Co-authored-by: Cursor <cursoragent@cursor.com>
Split the main deck panel list and right-hand card browser sidebar into
DeckBuilderDeckList and DeckBuilderCardBrowser; page is ~408 lines.
Co-authored-by: Cursor <cursoragent@cursor.com>
Move the find-card search input and results grid into
CardEditorSearchPanel to start splitting the admin card editor page.
Co-authored-by: Cursor <cursoragent@cursor.com>
Move Commander basic-land checks and deck aggregate metrics into
lib/deck-builder-stats.js with unit tests; render the summary row via
DeckBuilderStatsBar to shrink the page god-component.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Extract useCollectionsPage hook and CollectionsPageView (Brief 3).
Moves list index logic into a hook and view; CollectionsThumbnail is a shared presentational component.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Default hook params for prerender safety
Co-authored-by: Cursor <cursoragent@cursor.com>
---------
Co-authored-by: Cursor <cursoragent@cursor.com>
Moves create-list modal UI to components/CollectionsCreateModal.js and tag input helpers to lib/collection-tag-input.js (shared with edit modal).
Co-authored-by: Cursor <cursoragent@cursor.com>
Moves card-browser state and handlers to lib/use-cards-page.js and markup to components/CardsPageView.js, leaving a thin ProtectedRoute composer in pages/cards.js.
Co-authored-by: Cursor <cursoragent@cursor.com>
Moves the anonymous /cards landing UI into components/PublicCardsView.js so the page file can focus on AuthenticatedCards.
Co-authored-by: Cursor <cursoragent@cursor.com>
Moves ~530 lines of 3D card rendering into components/Card3D.js so the cards page god-component split can continue incrementally.
Co-authored-by: Cursor <cursoragent@cursor.com>
Move scanner page markup into ScannerPageView.js. pages/scanner.js is now
a thin composer of session state, useScannerQueue, and the view.
Mark pages/scanner.js resolved in ship-readiness god-component-split table.
Co-authored-by: Cursor <cursoragent@cursor.com>
Move scanner session persistence, queue merge helpers, and destination
routing fetch calls into lib/scanner-session.js and lib/scanner-route-api.js.
Load collections/decks on mount (were defined but never invoked).
Remove unused mana-symbol imports and dead select-all helpers.
Co-authored-by: Cursor <cursoragent@cursor.com>
Move camera feed markup, overlays, and disambiguation dialog wiring into
CameraScannerView.js. CameraScanner is now a ~45-line hook composer.
Update ship-readiness god-component-split CameraScanner slice as resolved.
Co-authored-by: Cursor <cursoragent@cursor.com>
Move verify/disambiguation/review flow into lib/use-scanner-identification.js.
CameraScanner is now a thin composition of two hooks plus view markup.
Co-authored-by: Cursor <cursoragent@cursor.com>
Move Layer-1/Layer-2 identify flow, outcome resolution, disambiguation
refine helpers, and scan-for-review API calls into lib/scanner-card-identify.js.
Remove unused manaSymbolSettings state from CameraScanner.
Co-authored-by: Cursor <cursoragent@cursor.com>
Move OpenCV shape detection, coordinate conversion, overlap checks,
and tracked-card merge logic from CameraScanner into lib/scanner-card-detection.js
with unit tests for the pure helpers.
Co-authored-by: Cursor <cursoragent@cursor.com>
Extract shared 401/403 gate into permission-middleware and sweep the
four inline admin checks (import MTG/Pokemon, sync-catalog, card-submissions).
Co-authored-by: Cursor <cursoragent@cursor.com>
Brief 1 of god-component-split: move ScanDisambiguationDialog and
scan-capture-upload lib out of CameraScanner (~90 lines) without
behavior changes.
Co-authored-by: Cursor <cursoragent@cursor.com>
Close stale convoy frontmatter for merged scanner, lint, and hygiene work;
record P1 #11.5 and queued follow-ups as RESOLVED with PR references.
Co-authored-by: Cursor <cursoragent@cursor.com>
* ci: run migrations against Postgres service container in CI
Add a migrate job that applies node-pg-migrate against an ephemeral
Postgres 16 service container so broken migrations fail at PR time.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(migrations): retimestamp scan tables after initial-schema.
Fresh CI/postgres runs failed because 1748365200000 sorted before
initial-schema. Renamed to 1779853647566 with IF NOT EXISTS guards intact.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(migrations): ensure is_system_collection exists before description backfill.
Fresh CI Postgres runs initial-schema without this column (added historically
via scripts); ADD COLUMN IF NOT EXISTS makes the data migration safe on new
and existing envs.
Co-authored-by: Cursor <cursoragent@cursor.com>
---------
Co-authored-by: Cursor <cursoragent@cursor.com>
Lazy-init theme from localStorage, hoist checkAuth with useCallback,
named config exports for PostCSS/Tailwind, and remove the non-blocking
|| true wrapper from ci.yml (requires #61 + #62 merged first).
Co-authored-by: Cursor <cursoragent@cursor.com>
Resolve react-hooks, no-unescaped-entities, and no-img-element findings
under pages/ with lint-only changes so npx eslint pages/ exits clean.
Co-authored-by: Cursor <cursoragent@cursor.com>
Existing is_system_collection rows may carry stale copy; align them with VOCAB.SYSTEM_COLLECTION_SEED_DESCRIPTION so UI matches new signups.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Remove dead Lorcana import route and CLI script
The admin card-import UI never wired Lorcana; catalog sync uses
pages/api/admin instead. Drop the unused API route, CLI helper, and
stale docs references to import-lorcana.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Remove orphaned import-lorcana-simple CLI script.
It POSTed to the deleted /api/cards/import-lorcana endpoint; no remaining callers.
Co-authored-by: Cursor <cursoragent@cursor.com>
---------
Co-authored-by: Cursor <cursoragent@cursor.com>
Commit home.png generated in mcr.microsoft.com/playwright:v1.60.0-noble
against https://tcg-vault.vercel.app so Screenshot diff CI matches production.
Co-authored-by: Cursor <cursoragent@cursor.com>
Migrate setup-neon-db.js and reset-db.js to @vercel/postgres tagged templates so the runtime uses a single SQL client; historical add-*/fix-*/seed-* scripts remain unchanged per no-go-zone.
Co-authored-by: Cursor <cursoragent@cursor.com>
Drop alice/bob password prefill from the login page, add a regression
test, and improve bulk-toolbar and disambiguation accessible names.
Co-authored-by: Cursor <cursoragent@cursor.com>
Correct dashboard title (My Collection overview, not Lists), sweep
remaining marketing/auth copy, update system-list seed description,
add vocabulary unit tests, and close the convoy record.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Align UI copy with My Collection vs Lists vocabulary.
Replace stale ownership/list labels across pages and components, add
lib/collection-vocabulary.js as the single copy source, document the
taxonomy in AGENTS.md, and gate retired strings in CI.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Fix remaining list/collection copy gaps from review.
Sweep community, settings, share modal, scanner create-list modal,
and invite flows for vocabulary consistency before merge.
Co-authored-by: Cursor <cursoragent@cursor.com>
---------
Co-authored-by: Cursor <cursoragent@cursor.com>