diff --git a/README.md b/README.md index 4cd8117..70096e0 100644 --- a/README.md +++ b/README.md @@ -1,167 +1,99 @@ # Keep7 -Testing a 100-card Magic deck on a phone is a pain. +MTG deck testing tool for opening hands, early turns, and deck comparison. -Most tools feel cramped, slow, or built for desktop first. Keep7 fixes that. +Paste a decklist. Draw hands. Goldfish turns 1–5. Compare two decks side by side. Works on mobile. -Paste your list. Tap through opening hands. Goldfish turns. Compare two decks. Do it fast on mobile. +**[→ Try it at keep7.vercel.app](https://keep7.vercel.app)** -## 60-Second Recruiter Scan +--- -**What it is:** -Mobile-first MTG deck testing app for opening-hand consistency and early-turn simulation. +## What it does -**Problem solved:** -Commander deck testing on a phone is usually slow and clumsy. +Keep7 helps you answer real play questions before you sit down at a table: -**What I built:** -- Hand tester with keep/mull flow and draw reveal -- Turn 1-5 goldfish simulator -- Deck-vs-deck race mode with 20-hand trial verdicts -- Web deck loader (MTGJSON) with retries and fallback behavior -- Parser cleanup for noisy Moxfield export lines -- Full-screen tap-to-zoom card modal for mobile use +- **Is this opener keepable?** Draw 7-card hands, keep or mulligan, see your next 3 draws. +- **How does my curve play out?** Goldfish turns 1–5 — tap lands, cast spells, see available mana. +- **Which version of this deck starts better?** Run 20 opener trials for each list, get a plain verdict. -**Stack:** -Vanilla JS (ES modules), HTML, CSS, Scryfall API, MTGJSON API +## Modes -**Why this matters:** -Shows product thinking, practical front-end architecture, API resilience, and mobile UX execution in one project. +### Hand Test +- Parse any standard decklist format +- Draw a 7-card opener, keep or mulligan +- Reveal next 3 draws after you keep +- Track session stats: keep rate, land distribution, flood/screw risk -## What This App Does +### Goldfish +- Simulate turns 1–5 against an empty board +- Play lands from hand, tap for mana, cast spells +- Action log tracks what happened each turn -Keep7 is a client-side MTG deck testing app focused on Commander-sized lists. +### Deck Race +- Compare two decklists across 20 simulated openers each +- Stats: keep rate, avg lands, T3 land %, flood/screw risk +- Plain-English verdict you can act on -It helps you answer real play questions: +## Stack -- Is this opener keepable? -- How often do I hit land drops? -- Which version of this deck starts better? +- **Next.js 15** (App Router) + **React 19** +- **TypeScript** throughout +- **CSS Modules** — no CSS-in-JS, no utility framework +- **Scryfall API** — card data and images, with retry/fallback +- **MTGJSON** — random web deck loading +- Fully client-side — no custom backend, no auth, no database -## Why People Use Keep7 - -- **You can test in seconds, not minutes.** - Paste a decklist and start drawing hands right away. - -- **It works well on a phone.** - Big tap targets, quick flows, full-screen card zoom. - -- **You can compare deck versions side by side.** - Run 20 opener trials for each list and read a plain verdict. - -- **You can load random web decks for fun testing.** - Pull from MTGJSON by deck type, then tap "Try another." - -- **It keeps your choices.** - Deck text, web deck type, and card cache are saved in local storage. - -## Features - -### Test Opening Hands - -- Parse decklists from plain text -- Draw a 7-card opener -- Mulligan or keep -- Reveal the next 3 draws -- Track session stats (keep rate, land distribution, flood/screw signals) - -### Goldfish Turns 1-5 - -- Simulate early turns with your actual list -- Play lands from hand -- See available mana and castable cards - -### Compare Two Decks - -- Run 20 opener simulations for Deck A and Deck B -- See both opening hands -- Review core stats for each deck -- Get a short verdict line you can act on - -### Load Random Web Decks - -- Load random decks from MTGJSON -- Filter by deck type -- Fallback flow: selected type -> any type -> local sample deck -- One-tap "Try another" - -## Setup (Low Pressure) - -No account. No backend setup. No database. - -1. Clone this repo. -2. Start a local static server. -3. Open the app in your browser. - -### Option A: Python - -```bash -python -m http.server 5173 -``` - -### Option B: Node +## Setup ```bash -npx http-server -p 5173 +git clone https://github.com/fuzmaster/keep7.git +cd keep7 +npm install +npm run dev ``` -Then open: +Open [http://localhost:3000](http://localhost:3000). -`http://localhost:5173` +## Decklist format -## Decklist Input Format +One card per line: -Use one card per line: - -```text -1 Sol Ring -1 Arcane Signet -35 Plains +``` +4 Lightning Bolt +4 Goblin Guide +20 Mountain ``` -The parser handles common export noise, including set codes and collector numbers from Moxfield-style lines. - -## Tech and APIs - -- Vanilla HTML/CSS/JavaScript (ES modules) -- Scryfall API for card data and images -- MTGJSON API for random web deck loading -- Fully client-side (no custom backend) - -## Project Layout - -- `index.html` - app shell and mode panels -- `styles.css` - visual system and responsive layout -- `js/ui.js` - hand test flow and interactions -- `js/goldfishUi.js` - goldfish mode UI -- `js/raceUi.js` - deck race mode UI -- `js/remoteDeck.js` - MTGJSON deck loading logic -- `js/parser.js` - decklist parsing and cleanup -- `js/storage.js` - local cache and saved settings - -## Common Issues - -### App does not load modules +Handles Moxfield export format automatically (set codes, collector numbers, etc.). -Run from a local server, not `file://`. +## Importing decks -### Card images do not appear +- **Paste a decklist** — plain text, one card per line +- **Moxfield / Archidekt URLs** — import via the URL field +- **Random web deck** — loads from MTGJSON by deck type -Check internet access and Scryfall availability. +## Reliability -### Random web deck fails +- Scryfall requests are batched, rate-limited, and retried on failure +- If Scryfall is unavailable, the app falls back to placeholder cards — simulation still works +- Card data is cached in `localStorage` by deck hash (7-day TTL, up to 5 decks) +- `localStorage` failures are handled gracefully -MTGJSON may be down. Keep7 falls back to a local sample deck. +## Project layout -## Portfolio Note +``` +src/ + app/ Next.js App Router pages and layout + components/ UI components with colocated CSS modules + hooks/ State logic for each mode (useHandTest, useGoldfish, useRace) + lib/ Business logic: parser, engine, scryfall, storage, metrics + types/ TypeScript interfaces +``` -This project shows product thinking plus front-end execution: +## Privacy -- Mobile-first UX for a niche workflow -- Real-world parser cleanup for noisy deck exports -- Resilient API fallbacks -- Fast, no-framework JavaScript architecture +Keep7 runs entirely in your browser. Deck text and session data never leave your device. The only external requests are to Scryfall (card images/data) and MTGJSON (optional random deck loading). ## License -MIT License. See `LICENSE`. +MIT diff --git a/src/app/globals.css b/src/app/globals.css index 0e90811..a8b164a 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -1,33 +1,52 @@ /* ── Design Tokens ─────────────────────────────────────── */ :root { - --font-display: "Avenir Next", "Trebuchet MS", "Segoe UI", sans-serif; - --font-mono: "Consolas", "Courier New", monospace; - --bg-root: #121212; - --bg-surface: #181818; - --bg-card: #202020; - --bg-input: #fff8e7; - --bg-elevated: #242424; - - --text-primary: #ffffff; - --text-secondary: #d7d7d7; - --text-muted: #a7a7a7; - - --keep: #1db954; - --keep-dim: rgba(29, 185, 84, 0.16); - --mull: #ef6c4a; - --mull-dim: rgba(239, 108, 74, 0.16); - --gold: #ffd23f; - --gold-dim: rgba(255, 210, 63, 0.16); - --purple: #5dade2; + /* Backgrounds — deep navy, MTG Arena-coded */ + --bg-root: #07090f; + --bg-surface: #0c1020; + --bg-card: #131826; + --bg-input: #0a0e1c; + --bg-elevated: #161e30; + + /* Text */ + --text-primary: #eceef8; + --text-secondary: #8892b8; + --text-muted: #505878; + + /* Semantic: green = keep, red = mull — matches MTG color philosophy */ + --keep: #00c877; + --keep-dim: rgba(0, 200, 119, 0.12); + --mull: #e03c3c; + --mull-dim: rgba(224, 60, 60, 0.12); + + /* Gold — MTG card-frame amber, warmer than warning yellow */ + --gold: #c9a84c; + --gold-bright: #e2c468; + --gold-dim: rgba(201, 168, 76, 0.12); - --border-faint: #282828; - --border-subtle: #343434; - --border-mid: #4a4a4a; - --border-focus: #ffd23f; + /* MTG mana palette — used for card highlight states */ + --mana-white: #f0e6c0; + --mana-blue: #1e6eaa; + --mana-black: #3d2b5e; + --mana-red: #cc3b28; + --mana-green: #1a7a47; + + /* Borders */ + --border-faint: #181f38; + --border-subtle: #1f2842; + --border-mid: #293356; + --border-focus: #c9a84c; /* gold focus — replaces purple */ + + /* Glow effects — MTG Arena interactive signature */ + --glow-gold: 0 0 14px rgba(201, 168, 76, 0.22), 0 0 4px rgba(201, 168, 76, 0.12); + --glow-keep: 0 0 12px rgba(0, 200, 119, 0.25); + --glow-mull: 0 0 12px rgba(224, 60, 60, 0.25); --shadow-card: 0 10px 24px rgba(0, 0, 0, 0.28); --shadow-panel: 0 16px 40px rgba(0, 0, 0, 0.34); - --glow-keep: 0 0 0 1px rgba(29, 185, 84, 0.35), 0 0 18px rgba(29, 185, 84, 0.18); - --glow-gold: 0 0 0 1px rgba(255, 210, 63, 0.38), 0 0 18px rgba(255, 210, 63, 0.18); + --purple: #5dade2; + + /* Display font — Cinzel (Beleren-adjacent serif for logo/headings) */ + --font-display: 'Cinzel', serif; + --font-mono: "Consolas", "Courier New", monospace; } /* ── Reset ────────────────────────────────────────────── */ @@ -51,6 +70,8 @@ body { color: var(--text-primary); line-height: 1.5; min-height: 100dvh; + display: flex; + flex-direction: column; font-size: 16px; } @@ -68,6 +89,7 @@ button { } main { + flex: 1; padding-bottom: calc(5.8rem + env(safe-area-inset-bottom, 0px)); } diff --git a/src/app/layout.tsx b/src/app/layout.tsx index d8f4b8d..d6912d3 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -1,26 +1,52 @@ import type { Metadata } from 'next'; +import { Cinzel } from 'next/font/google'; +import { Footer } from '@/components/Footer'; import './globals.css'; +const cinzel = Cinzel({ + subsets: ['latin'], + weight: ['700'], + variable: '--font-display', + display: 'swap', +}); + export const metadata: Metadata = { - title: 'Keep7 — MTG Deck Consistency Lab', - description: 'Test opening hands, goldfish turns 1–5, and compare two decks side-by-side. Free, instant, no signup.', + title: 'Keep7 — MTG Deck Testing Tool', + description: 'Test Magic: The Gathering opening hands, goldfish turns 1–5, and compare two decks side by side. Free, instant, no account needed. Works on mobile.', + keywords: [ + 'MTG deck tester', 'Magic the Gathering', 'opening hand simulator', + 'goldfish MTG', 'deck consistency', 'Commander deck testing', + 'MTG hand test', 'mulligan helper', 'deck race', 'Scryfall', + ], openGraph: { - title: 'Keep7 — MTG Deck Consistency Lab', - description: 'Test MTG opening hands, goldfish, and compare decks.', + title: 'Keep7 — MTG Deck Testing Tool', + description: 'Test MTG opening hands, goldfish turns 1–5, and compare two decks side by side. Free and instant — no account needed.', url: 'https://keep7.vercel.app', siteName: 'Keep7', type: 'website', }, + twitter: { + card: 'summary', + title: 'Keep7 — MTG Deck Testing Tool', + description: 'Test MTG opening hands, goldfish, and compare decks. Free, instant, no signup.', + }, icons: { icon: '/favicon-card.svg', }, manifest: '/manifest.json', + robots: { + index: true, + follow: true, + }, }; export default function RootLayout({ children }: { children: React.ReactNode }) { return ( - - {children} + + + {children} +