diff --git a/docs/plans/marketing-website.md b/docs/plans/marketing-website.md new file mode 100644 index 0000000..9222b69 --- /dev/null +++ b/docs/plans/marketing-website.md @@ -0,0 +1,160 @@ + + + +# PatchDeck Marketing Website — Implementation Plan + +**Status:** awaiting approval +**Branch:** `jeremymcs/website` +**Date:** 2026-08-30 + +## Decisions (confirmed with owner) + +| Decision | Choice | +| --- | --- | +| Location / deploy | New top-level `site/` directory, its own Vercel project | +| Stack | Static HTML + Tailwind CLI (no framework, no runtime JS deps) | +| Positioning | Pure free OSS — install CTA + GitHub stars, no pricing, no email capture | +| Scope | One long landing page; everything else deep-links to existing docs | + +Existing GitHub Pages docs (`docs/`, `.github/workflows/pages.yml`) are **left untouched**. +The new site links out to them. + +## 1. Directory layout + +```text +site/ + package.json # standalone; only devDep is tailwindcss + package-lock.json + tailwind.config.cjs # brand tokens + vercel.json # cache headers + clean URLs + build.mjs # tailwind CLI + copy index.html/public -> dist + index.html # the entire landing page (source of truth) + src/ + input.css # @tailwind directives + component layer + public/ + assets/*.webp # pre-optimized screenshots (committed) + favicon.svg + og.png + robots.txt + sitemap.xml + dist/ # build output (gitignored) -> Vercel output dir + README.md # how to run/deploy the site +``` + +Root `.gitignore` gains `site/dist/`. + +## 2. Build + +- `site/package.json` is standalone so Vercel's **Root Directory = `site`** works + cleanly and the marketing site never pulls the app's ~120 dependencies. +- `npm run build` = `node build.mjs`, which: + 1. runs `tailwindcss -c tailwind.config.cjs -i src/input.css -o dist/styles.css --minify` + 2. copies `index.html` -> `dist/index.html` + 3. copies `public/**` -> `dist/` +- `npm run dev` = same with `--watch`. +- No PostCSS config needed; Tailwind CLI handles it. + +**Vercel project settings** (documented in `site/README.md`, applied via dashboard): +Root Directory `site` · Framework Preset `Other` · Build `npm run build` · Output `dist`. + +## 3. Image optimization (done once, committed — not at build time) + +Screenshots in `docs/assets/` total ~4 MB. Keeping the build reproducible on +Vercel's Linux builders means pre-optimizing locally with `sips` and committing +the results, rather than shelling out to macOS tools during CI. + +| Source | Target | +| --- | --- | +| `PatchDeck-Dashboard.png` (3840w, 244 KB) | 1920w webp | +| `PatchDeck-PRs.png` (997 KB) | 1920w webp | +| `PatchDeck-Issues.png` (886 KB) | 1920w webp | +| `PatchDeck-Releases.png` (348 KB) | 1920w webp | +| `PatchDeck-Settings.png` (315 KB) | 1920w webp | +| `PatchDeck-Logs.png` (1.2 MB) | 1920w webp | + +`hero-background.png` (1.5 MB) is **not** copied — it is a plain dark-blue radial +gradient, reproduced in CSS at zero bytes. + +Every `` gets explicit `width`/`height` (no layout shift), `loading="lazy"` ++ `decoding="async"` below the fold, and a real descriptive `alt`. + +## 4. Visual direction + +Reconciles `design-system/patchdeck/MASTER.md` with how the product actually looks. + +- **Surface:** near-black `#08090C`, panels `#111318`, hairline borders + `rgba(148,163,184,.12)` — matches the real dashboard, not a generic slate theme. +- **Primary CTA:** green `#22C55E` (per MASTER.md "code dark + run green"). +- **State accents:** blue `#60A5FA` (processing), amber `#F59E0B` (in progress), + red `#EF4444` (failed), green `#22C55E` (resolved). These are the product's own + status colors and give the page a distinctive, honest palette. +- **Type:** Fira Code for headings/micro-labels, Fira Sans for body (per MASTER.md, + via Google Fonts with full system fallback stacks). +- **Signature detail:** uppercase wide-tracked micro-labels and tabular numerals, + lifted straight from the dashboard chrome. + +> Deviation to flag: MASTER.md specifies green as the sole accent. The product UI is +> blue/amber/red. Plan keeps green for CTAs only and uses the product's real state +> colors elsewhere, so the site looks like the app it is selling. + +## 5. Page sections + +1. **Nav** — sticky, hairline bottom border. Logo, Features / How it works / Docs / + GitHub, `npm i -g` CTA. Mobile: details/summary disclosure, no JS framework. +2. **Hero** — headline, subhead, copy-to-clipboard install command, secondary + "View on GitHub". Badge row: MIT · Node 22+ · v1.6.0 · local-first. + Dashboard screenshot in a window frame over a CSS radial glow. +3. **The problem** — the stale-PR loop, three beats. Uses `sdlc-bottleneck.png`. +4. **How it works** — Watch → Sync → Triage → Dispatch, plus the real feedback + lifecycle (`pending → queued → in_progress → resolved`, branching to + `rejected` / `flagged` / `failed`) drawn as inline SVG, theme-aware. +5. **Feature sections** — alternating layout, each with a real screenshot: + Pull requests · Issues · Releases · CI & deployment healing · Logs · Settings. +6. **Local-first & safe by default** — the genuine differentiator, straight from + the README: state lives in `~/.patchdeck/state.sqlite`, git work happens in + app-owned worktrees, auto releases / CI healing / deployment healing all off by + default, drain mode, hourly agent-run ceiling, token redaction in logs, + remote access opt-in. +7. **Interfaces** — 4 cards: web dashboard, MCP server, local REST API, desktop app. +8. **Install / quickstart** — prerequisites (Node 22+, Git, GitHub auth, `claude` + or `codex`), install command, first-run cautious-defaults checklist. +9. **FAQ** — native `
`: what it costs, which agents, where data lives, + does it need repo write access, can I run it remotely, does it work on Windows. +10. **Footer** — MIT, © 2026, fork credit to `yungookim/oh-my-pr`, doc/repo links. + +## 6. Quality bar + +- Semantic landmarks, one `

`, skip-to-content link. +- Visible `:focus-visible` rings on every interactive element. +- `prefers-reduced-motion` honored; all motion is CSS-only. +- Responsive from 320px; no horizontal body scroll (wide blocks scroll internally). +- Meta: description, canonical, OG + Twitter card, `theme-color`, + JSON-LD `SoftwareApplication`, `robots.txt`, `sitemap.xml`. +- Total JS: one small inline script for copy-to-clipboard and the mobile nav. + No frameworks, no analytics, no third-party requests except Google Fonts. + +## 7. Copy accuracy rules + +Every claim traces to the repo. No invented metrics, no fake testimonials, no +made-up user counts, no "10x faster". Version and feature claims come from +`README.md` and `package.json` (v1.6.0). The README's own caution — *"PatchDeck can +spend paid agent usage when automation is enabled"* — is surfaced on the page +rather than hidden, because it builds trust with the exact audience being targeted. + +## 8. Build order + +1. Scaffold `site/` + standalone `package.json`, install `tailwindcss`. +2. Optimize + commit screenshots into `site/public/assets/`. +3. `tailwind.config.cjs` + `src/input.css` brand tokens. +4. Write `index.html` section by section. +5. `build.mjs`, `vercel.json`, `robots.txt`, `sitemap.xml`, favicon, OG image. +6. Build, then verify the rendered page in a browser at desktop + mobile widths. +7. `site/README.md` with Vercel setup steps. +8. Commit on `jeremymcs/website`. + +## 9. Out of scope + +- Creating/linking the Vercel project or buying a domain (owner action; can be + driven via the Vercel connector on request). +- Restyling the existing `docs/` site. +- Blog, changelog page, or any additional marketing pages. diff --git a/site/README.md b/site/README.md new file mode 100644 index 0000000..572f365 --- /dev/null +++ b/site/README.md @@ -0,0 +1,104 @@ + + + +# PatchDeck marketing site + +The public landing page for PatchDeck. A single static HTML page compiled with the +Tailwind CLI — no framework, no runtime dependencies, no build-time network calls. + +This is **separate** from the documentation site in `../docs/`, which is still +deployed to GitHub Pages by `.github/workflows/pages.yml`. The landing page links +out to those docs. + +## Layout + +```text +site/ + index.html the entire page — the source of truth for all copy + src/input.css base styles + component layer + tailwind.config.cjs brand tokens (colors, fonts) + build.mjs Tailwind CLI + copy index.html and public/ into dist/ + public/ static assets copied verbatim to the site root + assets/ pre-optimized screenshots (webp) and the OG image + vercel.json cache headers and security headers + dist/ build output — gitignored +``` + +## Local development + +```bash +cd site +npm install +npm run dev # rebuilds dist/ on change +``` + +Then serve `dist/` with any static server, e.g.: + +```bash +cd dist && python3 -m http.server 4321 +``` + +A one-shot production build: + +```bash +npm run build +``` + +## Deploying to Vercel + +The site is its own Vercel project, pointed at this subdirectory. Create the +project from the `jeremymcs/patchdeck` repository and set: + +| Setting | Value | +| --- | --- | +| Root Directory | `site` | +| Framework Preset | `Other` | +| Build Command | `npm run build` | +| Output Directory | `dist` | +| Install Command | `npm install` | + +`vercel.json` handles caching (immutable for `/assets/*`) and the security headers. + +### One thing to change after the domain is set + +The canonical URL currently points at `https://patchdeck.vercel.app/`. Once the +real domain is attached, update it in three places: + +- `index.html` — ``, the `og:url` / `og:image` / + `twitter:image` meta tags, and the `url` field in the JSON-LD block +- `public/robots.txt` — the `Sitemap:` line +- `public/sitemap.xml` — the `` element + +## Screenshots + +Screenshots are pre-optimized and committed rather than converted during the +build, so the build stays reproducible on Vercel's Linux builders (the conversion +tooling here is macOS/Homebrew-local). + +Sources live in `../docs/assets/`. To regenerate after new screenshots land, +with `webp` installed (`brew install webp`): + +```bash +sips --resampleWidth 1920 ../docs/assets/PatchDeck-Dashboard.png --out /tmp/d.png +cwebp -q 80 -m 6 -sharp_yuv /tmp/d.png -o public/assets/patchdeck-dashboard.webp +``` + +The OG image is a 1200x630 top crop of the dashboard screenshot: + +```bash +ffmpeg -i /tmp/d.png -vf "crop=1920:1006:0:0,scale=1200:630:flags=lanczos" \ + public/assets/og.png +``` + +Every `` in `index.html` carries explicit `width`/`height` (1920x1080) to +prevent layout shift. Keep that in sync if the aspect ratio ever changes. + +## Copy rules + +Claims on the page trace back to the repository — `README.md`, `package.json`, +and the docs in `docs/public/`. No invented metrics, no testimonials, no user +counts. When PatchDeck's behaviour or defaults change, update the page: + +- the version badge in the hero and the `softwareVersion` in the JSON-LD +- the safety section, which mirrors the defaults documented in the root `README.md` +- the issue label lists in the Issues feature section diff --git a/site/build.mjs b/site/build.mjs new file mode 100644 index 0000000..cd4147d --- /dev/null +++ b/site/build.mjs @@ -0,0 +1,60 @@ +// PatchDeck Website — static build (Tailwind CLI + asset copy) +// Copyright (c) 2026 Jeremy McSpadden + +import fs from "node:fs"; +import path from "node:path"; +import { spawn, spawnSync } from "node:child_process"; + +const ROOT = import.meta.dirname; +const DIST = path.join(ROOT, "dist"); +const PUBLIC = path.join(ROOT, "public"); +const watch = process.argv.includes("--watch"); + +const tailwindBin = path.join( + ROOT, + "node_modules/.bin", + process.platform === "win32" ? "tailwindcss.cmd" : "tailwindcss", +); + +/** Recursively copy a directory into dist, preserving structure. */ +function copyDir(from, to) { + if (!fs.existsSync(from)) return; + fs.mkdirSync(to, { recursive: true }); + for (const entry of fs.readdirSync(from, { withFileTypes: true })) { + const src = path.join(from, entry.name); + const dest = path.join(to, entry.name); + if (entry.isDirectory()) copyDir(src, dest); + else fs.copyFileSync(src, dest); + } +} + +function copyStatic() { + fs.mkdirSync(DIST, { recursive: true }); + fs.copyFileSync(path.join(ROOT, "index.html"), path.join(DIST, "index.html")); + copyDir(PUBLIC, DIST); +} + +const cssArgs = [ + "-c", + path.join(ROOT, "tailwind.config.cjs"), + "-i", + path.join(ROOT, "src/input.css"), + "-o", + path.join(DIST, "styles.css"), +]; + +fs.rmSync(DIST, { recursive: true, force: true }); +copyStatic(); + +if (watch) { + fs.watch(path.join(ROOT, "index.html"), () => copyStatic()); + fs.watch(PUBLIC, { recursive: true }, () => copyStatic()); + spawn(tailwindBin, [...cssArgs, "--watch"], { stdio: "inherit" }); + console.log("watching — dist/ is live"); +} else { + const result = spawnSync(tailwindBin, [...cssArgs, "--minify"], { stdio: "inherit" }); + if (result.status !== 0) process.exit(result.status ?? 1); + + const size = (file) => (fs.statSync(path.join(DIST, file)).size / 1024).toFixed(1); + console.log(`\nbuilt dist/ — index.html ${size("index.html")} KB, styles.css ${size("styles.css")} KB`); +} diff --git a/site/index.html b/site/index.html new file mode 100644 index 0000000..343e589 --- /dev/null +++ b/site/index.html @@ -0,0 +1,804 @@ + + + + + + +PatchDeck — Keep your pull requests moving + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Skip to content + + +
+ + + +
+ +
+ + +
+ + + + +
+
+
+ + Local-first · MIT · v1.6.0 +
+ +

+ Stop babysitting your pull requests. +

+ +

+ PatchDeck watches the repositories you choose, pulls review comments and failing + checks into one dashboard, then runs your own coding + agent in an isolated worktree to fix what needs fixing. +

+ + +
+
+ + npm install -g @jeremymcs/patchdeck + +
+

then run patchdeck → dashboard on localhost:5001

+
+ + + +

+ Node 22+  ·  Git  ·  Claude Code or Codex +

+
+ + +
+ +
+
+ + + + localhost:5001 +
+ The PatchDeck dashboard: counters for watched repos, open PRs, open issues, processing and failed jobs, a list of watched repositories, and a live activity feed of pull requests being monitored. +
+
+
+
+ + +
+
+

The problem

+

Writing the code was never the slow part

+

+ A pull request goes up in minutes. Then it sits — waiting on a review comment + nobody actioned, a check that failed for a boring reason, a thread left unresolved. + The work isn't hard. It's just scattered across tabs, and it's easy to forget. +

+
+ +
+
+
+ +
+

Feedback goes stale

+

+ Review comments pile up across PRs. Some need real thought. Most need a + two-line change that nobody has gotten around to. +

+
+
+
+ +
+

Checks fail quietly

+

+ A lint rule, a flaky job, a snapshot that needed updating. You find out when + you next open the tab — which might be tomorrow. +

+
+
+
+ +
+

Context lives in tabs

+

+ Figuring out what still needs action means opening every PR, one at a time, + and reconstructing where each one got stuck. +

+
+
+
+ +
+ + +
+
+

How it works

+

Watch. Sync. Triage. Dispatch.

+

+ PatchDeck runs on your machine, polls the repos you point it at, and turns every + piece of open feedback into a tracked item with a visible state. +

+
+ +
    +
  1. + Step 01 +

    Watch

    +

    + Add a repository or paste a single PR URL. New repos default to + My PRs only so you don't + accidentally monitor the whole team on day one. +

    +
  2. +
  3. + Step 02 +

    Sync

    +

    + Review comments, review threads, PR status, mergeability, CI state and + activity logs land in one local database. +

    +
  4. +
  5. + Step 03 +

    Triage

    +

    + Each item is sorted into work to accept, feedback to reject as a no-op, or + something flagged for a human to look at. +

    +
  6. +
  7. + Step 04 +

    Dispatch

    +

    + Accepted work runs in a clean git worktree. Verified fixes get committed and + pushed, threads get replies, conversations get resolved. +

    +
  8. +
+ + +
+
+

Every feedback item has a visible state

+
+
+ + + + + + + + + + + pending + + queued + + in_progress + + resolved + + + + + + + + + + + + + + + + rejected + + flagged + + failed + + + no action needed + needs a human decision + retried with backoff + + + +
+
+
+ +
+ + +
+
+

What's inside

+

One deck for everything around the code

+
+ +
+ + +
+
+

Pull requests

+

See every open thread in one place

+

+ PatchDeck stores each PR's comments, review threads, failing checks, mergeability, + docs-assessment state and local activity log. When work is accepted it prepares a + clean worktree, runs your agent with the PR context, and handles the follow-up — + pushing commits, replying on GitHub, resolving threads, polling CI. +

+
    +
  • Pause automation on a single PR without losing its state
  • +
  • Trusted reviewers and ignored bots, configurable per install
  • +
  • Direct PR URLs stay tracked regardless of repo scope
  • +
+
+
+
Pull requests
+ The PatchDeck pull requests page listing tracked PRs with their review threads, check status and per-PR actions. +
+
+ + +
+
+

Issues

+

Turn a labelled issue into an open PR

+

+ Hit Work issue and PatchDeck creates an + isolated worktree, works from the issue context, pushes a branch and opens a linked + pull request. Automatic issue work is opt-in per repository and gated by labels. +

+
+
+

Ready labels

+

ready-for-agent
ready-to-work
agent-ready
ready

+
+
+

Blocking labels

+

blocked · question
needs-maintainer-review
needs-author-feedback
needs-discussion

+
+
+
+
+
Issues
+ The PatchDeck issues page showing issue bodies, labels, work state, failed attempts and linked pull requests. +
+
+ + +
+
+

Releases

+

Version bump, notes, published

+

+ PatchDeck can evaluate merged PRs for release-worthiness, propose a version bump, + write the release notes and create the GitHub release. The Releases page shows both + PatchDeck runs and releases created outside the app. +

+

+ Automatic release creation is off by default. + Leave it off and use the manual Release button instead. +

+
+
+
Releases
+ The PatchDeck releases pipeline showing release runs, proposed version bumps and published GitHub releases. +
+
+ + +
+
+

CI & deployment healing

+

Bounded repairs for boring failures

+

+ When enabled, PatchDeck classifies failing checks, opens a healing session, and only + attempts repairs for failures it considers fixable in the PR branch. Retry limits and + concurrency are yours to set. Post-merge, it can watch supported deployments and open + a follow-up fix PR if one fails. +

+

+ Deployment platform detection currently covers Vercel and Railway markers, and the + matching CLI must be installed and authenticated on the same machine. +

+
+
+
+
+
+ + build / lint +
+ failed +
+
+
+ + healing session +
+ attempt 1/3 +
+
+
+ + build / lint +
+ passing +
+
+

+ Illustration of a healing session. CI healing is disabled until you turn it on. +

+
+
+ + +
+
+

Logs

+

Watch exactly what it did

+

+ Structured logs stream to stdout and to ~/.patchdeck/log/server.log. + The Logs page adds filtering, search and live tailing. GitHub tokens are redacted + before anything is written. +

+

+ Because every agent run is recorded, you can turn automation on one switch at a time + and check the trail before trusting the next one. +

+
+
+
Logs
+ The PatchDeck logs page with filtering, search and live tailing of structured server log records. +
+
+ +
+
+ +
+ + +
+
+
+

Safety

+

Local-first, and cautious on purpose

+

+ PatchDeck drives a coding agent against real branches, so it is built to be + boring about it. State lives on the machine running it, git work happens in + app-owned worktrees, and the automatic paths ship switched off. +

+

+ PatchDeck can spend paid agent usage when automation is enabled. Start manually, + watch the logs, and only turn on the automatic paths you actually trust. +

+ +
+
Settings
+ The PatchDeck settings page with toggles for automation, agent selection, retry limits and GitHub credentials. +
+
+ +
+
+

Your data stays put

+

Everything lives in ~/.patchdeck/state.sqlite. No PatchDeck server, no telemetry, no account.

+
+
+

Isolated worktrees

+

Agents never touch your working copy. Each run gets a clean git worktree under the PatchDeck home directory.

+
+
+

Off by default

+

Automatic releases, CI healing and deployment healing are all disabled until you explicitly enable them.

+
+
+

Spend ceiling

+

Cap agent runs per rolling hour across every path. At the ceiling, work waits instead of failing, then resumes on its own.

+
+
+

Drain mode

+

Pause all new agent work in one switch without deleting a single piece of tracked state.

+
+
+

Bounded retries

+

Failures retry with backoff up to your limit. Anything needing a person is parked under Needs attention.

+
+
+
+
+ +
+ + +
+
+

Interfaces

+

Four ways in, one local state

+
+ +
+
+

Web dashboard

+ patchdeck +

The primary UI for PRs, issues, releases, logs and settings.

+
+
+

MCP server

+ patchdeck mcp +

Tool access from any MCP-compatible host, against the same local state.

+
+
+

Local REST API

+ starts with the server +

Programmatic access for your own scripts and dashboards.

+
+
+

Desktop app

+ npm run tauri:build +

A native shell that keeps PatchDeck in the macOS menu bar with live counts.

+
+
+ +

+ You can also just ask it things — + natural-language questions + about tracked PR state work from the dashboard, the API and MCP. +

+
+ +
+ + +
+
+

Install

+

Running in about a minute

+
+ +
+
+

Terminal

+
+
+

1 · Install

+
+ + npm install -g @jeremymcs/patchdeck + +
+
+
+

2 · Start

+
+ + patchdeck + +
+

Opens http://localhost:5001 in your browser.

+
+
+

Prerequisites

+
    +
  • Node.js 22+ and Git
  • +
  • GitHub auth via gh auth login, GITHUB_TOKEN, or a token saved in Settings
  • +
  • A local coding CLI: claude or codex, installed and authenticated
  • +
+
+
+
+ +
+

A cautious first run

+
    +
  1. 1

    Add one repository, or paste a single PR URL.

  2. +
  3. 2

    Confirm GitHub auth in Settings if no token was picked up automatically.

  4. +
  5. 3

    Keep repo discovery on My PRs only until you want team-wide monitoring.

  6. +
  7. 4

    Turn off Auto PRs and Auto Issues before adding busy repos.

  8. +
  9. 5

    Work manually until the behaviour looks right, then re-enable the automatic paths you want.

  10. +
+ +
+
+
+ +
+ + +
+
+
+

FAQ

+

Reasonable questions

+
+ +
+
+ + Does PatchDeck cost anything? + + +

PatchDeck itself is free and MIT-licensed. It drives your coding agent, though — so when automation is enabled it can spend whatever your Claude Code or Codex plan charges. That's why every automatic path ships off, and why there's an hourly ceiling on agent runs.

+
+ +
+ + Which coding agents are supported? + + +

Claude Code (claude) and OpenAI Codex (codex). Install and authenticate one on the same machine; you can also configure a fallback agent for when the primary is unavailable.

+
+ +
+ + Where does my data go? + + +

Nowhere. State is a SQLite file at ~/.patchdeck/state.sqlite (override with PATCHDECK_HOME). There is no PatchDeck backend and no account to create. Traffic goes to GitHub and to your agent's provider, and nowhere else.

+
+ +
+ + Will it push to my branches without asking? + + +

Only on paths you've enabled. When work is accepted and automation is on, PatchDeck commits and pushes verified fixes to the PR branch and can reply to review threads. Turn off Auto PRs and Auto Issues to keep everything manual, use drain mode to pause new work, or pause a single PR without untracking it.

+
+ +
+ + Can I reach the dashboard from another machine? + + +

Yes, and it's opt-in. Set PATCHDECK_WEB_USERNAME and PATCHDECK_WEB_PASSWORD (or save credentials in Settings) before starting. Local browser and local API access don't need a login. Put TLS in front of it before exposing it outside a trusted network.

+
+ +
+ + Do I have to let it run automatically? + + +

No. Plenty of people run PatchDeck purely as a dashboard — one place to see which PRs have unresolved threads or failing checks — and press the buttons themselves. The automation is there when you want it.

+
+
+
+
+ + +
+ +
+

Give your PRs somewhere to land

+

Free, MIT-licensed, and it runs entirely on your machine.

+
+
+ + npm install -g @jeremymcs/patchdeck + +
+
+ +
+
+ +
+ + + + + + + diff --git a/site/package-lock.json b/site/package-lock.json new file mode 100644 index 0000000..cc101d5 --- /dev/null +++ b/site/package-lock.json @@ -0,0 +1,1016 @@ +{ + "name": "patchdeck-site", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "patchdeck-site", + "version": "1.0.0", + "license": "MIT", + "devDependencies": { + "tailwindcss": "^3.4.17" + } + }, + "node_modules/@alloc/quick-lru": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", + "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.6.0.tgz", + "integrity": "sha512-T7jf+5zgsZHwNJ4lvQ7/aezbyk0nNX+zJVWpmHA7VYsEx7a7qr5Rg5IbtJFqkgze5Y2sruq1RUY8Q837Od7iFw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", + "dev": true, + "license": "MIT" + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/arg": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", + "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", + "dev": true, + "license": "MIT" + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/camelcase-css": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", + "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chokidar/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/commander": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "dev": true, + "license": "MIT", + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/didyoumean": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", + "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/dlv": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", + "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", + "dev": true, + "license": "MIT" + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fastq": { + "version": "1.20.3", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.3.tgz", + "integrity": "sha512-XKv5nnLs6nLF71NgiKJLIZFLkPyIEuOselLG7ujZnGrRfQK8HpvY+WqKhAJUAdLomwVHErVS4LfxFlPq0/FTAw==", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/hasown": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz", + "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==", + "dev": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "license": "MIT", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-core-module": { + "version": "2.16.2", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.2.tgz", + "integrity": "sha512-evOr8xfXKxE6qSR0hSXL2r3sd7ALj8+7jQEUvPYcm5sgZFdJ+AYzT6yNmJenvIYQBgIGwfwz08sL8zoL7yq2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "hasown": "^2.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/jiti": { + "version": "1.21.7", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.7.tgz", + "integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==", + "dev": true, + "license": "MIT", + "bin": { + "jiti": "bin/jiti.js" + } + }, + "node_modules/lilconfig": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", + "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antonk52" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true, + "license": "MIT" + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mz": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", + "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "any-promise": "^1.0.0", + "object-assign": "^4.0.1", + "thenify-all": "^1.0.0" + } + }, + "node_modules/nanoid": { + "version": "3.3.18", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.18.tgz", + "integrity": "sha512-DTg4MJbGMWkfi6VZFdNt2/caMbQy4Ou+Op/hJQvGEWcnVfoA1QA+xzRKAzw9jD6+GVOOeYr/mIcuDSdug6F6+w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-hash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", + "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true, + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pirates": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz", + "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/postcss": { + "version": "8.5.26", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.26.tgz", + "integrity": "sha512-u82N74LFzG8ca+dD8puPnplTXoGH4fTPpVGuIbt36G3qvNlkvfD0lEAZSxaly3KX8TS/L1A1gsCEmvKmBcVbkQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.17", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postcss-import": { + "version": "15.1.0", + "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz", + "integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==", + "dev": true, + "license": "MIT", + "dependencies": { + "postcss-value-parser": "^4.0.0", + "read-cache": "^1.0.0", + "resolve": "^1.1.7" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "postcss": "^8.0.0" + } + }, + "node_modules/postcss-js": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.1.0.tgz", + "integrity": "sha512-oIAOTqgIo7q2EOwbhb8UalYePMvYoIeRY2YKntdpFQXNosSu3vLrniGgmH9OKs/qAkfoj5oB3le/7mINW1LCfw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "camelcase-css": "^2.0.1" + }, + "engines": { + "node": "^12 || ^14 || >= 16" + }, + "peerDependencies": { + "postcss": "^8.4.21" + } + }, + "node_modules/postcss-load-config": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-6.0.1.tgz", + "integrity": "sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "lilconfig": "^3.1.1" + }, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "jiti": ">=1.21.0", + "postcss": ">=8.0.9", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + }, + "postcss": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/postcss-nested": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.2.0.tgz", + "integrity": "sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "postcss-selector-parser": "^6.1.1" + }, + "engines": { + "node": ">=12.0" + }, + "peerDependencies": { + "postcss": "^8.2.14" + } + }, + "node_modules/postcss-selector-parser": { + "version": "6.1.4", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.4.tgz", + "integrity": "sha512-bIoJLOmjCO1S9XdY/DcnR5hJxvrDir1PbGChrzXG3vw0/FOliy/fA3dmdhQ441kah4gKv+TwckGzex6wNS5cnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/read-cache": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.2.tgz", + "integrity": "sha512-/peqiBB/n07gQGLsWaHho3WfvUyRscw0gYTsEFMhrIe/nWLkYaf5SbKYjGYqtRV3aPwykJgF2VEMo1ac4bnsGA==", + "dev": true, + "license": "MIT" + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/resolve": { + "version": "1.22.12", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.12.tgz", + "integrity": "sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "is-core-module": "^2.16.1", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sucrase": { + "version": "3.35.1", + "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.1.tgz", + "integrity": "sha512-DhuTmvZWux4H1UOnWMB3sk0sbaCVOoQZjv8u1rDoTV0HTdGem9hkAZtl4JZy8P2z4Bg0nT+YMeOFyVr4zcG5Tw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.2", + "commander": "^4.0.0", + "lines-and-columns": "^1.1.6", + "mz": "^2.7.0", + "pirates": "^4.0.1", + "tinyglobby": "^0.2.11", + "ts-interface-checker": "^0.1.9" + }, + "bin": { + "sucrase": "bin/sucrase", + "sucrase-node": "bin/sucrase-node" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/tailwindcss": { + "version": "3.4.19", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.19.tgz", + "integrity": "sha512-3ofp+LL8E+pK/JuPLPggVAIaEuhvIz4qNcf3nA1Xn2o/7fb7s/TYpHhwGDv1ZU3PkBluUVaF8PyCHcm48cKLWQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@alloc/quick-lru": "^5.2.0", + "arg": "^5.0.2", + "chokidar": "^3.6.0", + "didyoumean": "^1.2.2", + "dlv": "^1.1.3", + "fast-glob": "^3.3.2", + "glob-parent": "^6.0.2", + "is-glob": "^4.0.3", + "jiti": "^1.21.7", + "lilconfig": "^3.1.3", + "micromatch": "^4.0.8", + "normalize-path": "^3.0.0", + "object-hash": "^3.0.0", + "picocolors": "^1.1.1", + "postcss": "^8.4.47", + "postcss-import": "^15.1.0", + "postcss-js": "^4.0.1", + "postcss-load-config": "^4.0.2 || ^5.0 || ^6.0", + "postcss-nested": "^6.2.0", + "postcss-selector-parser": "^6.1.2", + "resolve": "^1.22.8", + "sucrase": "^3.35.0" + }, + "bin": { + "tailwind": "lib/cli.js", + "tailwindcss": "lib/cli.js" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/thenify": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", + "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "any-promise": "^1.0.0" + } + }, + "node_modules/thenify-all": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", + "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "thenify": ">= 3.1.0 < 4" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", + "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tinyglobby/node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/tinyglobby/node_modules/picomatch": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.7.tgz", + "integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/ts-interface-checker": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", + "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true, + "license": "MIT" + } + } +} diff --git a/site/package.json b/site/package.json new file mode 100644 index 0000000..2c84b0d --- /dev/null +++ b/site/package.json @@ -0,0 +1,17 @@ +{ + "name": "patchdeck-site", + "version": "1.0.0", + "private": true, + "description": "Marketing website for PatchDeck", + "license": "MIT", + "type": "module", + "scripts": { + "build": "node build.mjs", + "dev": "node build.mjs --watch", + "serve": "node build.mjs && node --run serve:only", + "serve:only": "npx --yes serve dist -l 4321" + }, + "devDependencies": { + "tailwindcss": "^3.4.17" + } +} diff --git a/site/public/assets/og.png b/site/public/assets/og.png new file mode 100644 index 0000000..3e6c4da Binary files /dev/null and b/site/public/assets/og.png differ diff --git a/site/public/assets/patchdeck-dashboard.webp b/site/public/assets/patchdeck-dashboard.webp new file mode 100644 index 0000000..2770a94 Binary files /dev/null and b/site/public/assets/patchdeck-dashboard.webp differ diff --git a/site/public/assets/patchdeck-issues.webp b/site/public/assets/patchdeck-issues.webp new file mode 100644 index 0000000..ac20018 Binary files /dev/null and b/site/public/assets/patchdeck-issues.webp differ diff --git a/site/public/assets/patchdeck-logs.webp b/site/public/assets/patchdeck-logs.webp new file mode 100644 index 0000000..14542a4 Binary files /dev/null and b/site/public/assets/patchdeck-logs.webp differ diff --git a/site/public/assets/patchdeck-prs.webp b/site/public/assets/patchdeck-prs.webp new file mode 100644 index 0000000..594f95d Binary files /dev/null and b/site/public/assets/patchdeck-prs.webp differ diff --git a/site/public/assets/patchdeck-releases.webp b/site/public/assets/patchdeck-releases.webp new file mode 100644 index 0000000..f41bbcc Binary files /dev/null and b/site/public/assets/patchdeck-releases.webp differ diff --git a/site/public/assets/patchdeck-settings.webp b/site/public/assets/patchdeck-settings.webp new file mode 100644 index 0000000..8cc678f Binary files /dev/null and b/site/public/assets/patchdeck-settings.webp differ diff --git a/site/public/favicon.svg b/site/public/favicon.svg new file mode 100644 index 0000000..bdd4a93 --- /dev/null +++ b/site/public/favicon.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/site/public/robots.txt b/site/public/robots.txt new file mode 100644 index 0000000..cbd6bd9 --- /dev/null +++ b/site/public/robots.txt @@ -0,0 +1,4 @@ +User-agent: * +Allow: / + +Sitemap: https://patchdeck.vercel.app/sitemap.xml diff --git a/site/public/sitemap.xml b/site/public/sitemap.xml new file mode 100644 index 0000000..0a5630c --- /dev/null +++ b/site/public/sitemap.xml @@ -0,0 +1,8 @@ + + + + https://patchdeck.vercel.app/ + weekly + 1.0 + + diff --git a/site/src/input.css b/site/src/input.css new file mode 100644 index 0000000..23f003e --- /dev/null +++ b/site/src/input.css @@ -0,0 +1,162 @@ +/* PatchDeck Website — base styles and component layer */ +/* Copyright (c) 2026 Jeremy McSpadden */ + +@tailwind base; +@tailwind components; +@tailwind utilities; + +@layer base { + :root { + color-scheme: dark; + --hairline: rgba(148, 163, 184, 0.12); + --hairline-strong: rgba(148, 163, 184, 0.22); + } + + html { + scroll-behavior: smooth; + scroll-padding-top: 5rem; + -webkit-text-size-adjust: 100%; + } + + body { + margin: 0; + background-color: #08090c; + color: #94a3b8; + font-family: theme("fontFamily.sans"); + -webkit-font-smoothing: antialiased; + text-rendering: optimizeLegibility; + overflow-x: hidden; + } + + h1, + h2, + h3, + h4 { + color: #f8fafc; + font-family: theme("fontFamily.mono"); + font-weight: 600; + letter-spacing: -0.02em; + } + + :focus-visible { + outline: 2px solid #4ade80; + outline-offset: 3px; + border-radius: 2px; + } + + /* Inline code in prose wraps as one unit, so multi-word commands such as + `gh auth login` never break across lines mid-phrase. */ + :is(p, li, summary) code { + white-space: nowrap; + } + + ::selection { + background: rgba(34, 197, 94, 0.28); + color: #f0fdf4; + } + + ::-webkit-scrollbar { + width: 9px; + height: 9px; + } + + ::-webkit-scrollbar-track { + background: #08090c; + } + + ::-webkit-scrollbar-thumb { + background: #272c3a; + border-radius: 999px; + } + + ::-webkit-scrollbar-thumb:hover { + background: #3d4459; + } + + @media (prefers-reduced-motion: reduce) { + html { + scroll-behavior: auto; + } + + *, + *::before, + *::after { + animation-duration: 0.01ms !important; + animation-iteration-count: 1 !important; + transition-duration: 0.01ms !important; + } + } +} + +@layer components { + /* The product's signature uppercase, wide-tracked micro-label. */ + .eyebrow { + @apply font-mono text-[11px] font-medium uppercase tracking-[0.18em] text-slate-500; + } + + .shell { + @apply mx-auto w-full max-w-shell px-5 sm:px-8; + } + + .hairline { + border-color: var(--hairline); + } + + .panel { + @apply rounded-xl border bg-ink-900; + border-color: var(--hairline); + } + + .btn { + @apply inline-flex items-center justify-center gap-2 rounded-lg px-5 py-3 font-mono text-sm font-semibold transition-all duration-200; + } + + .btn-primary { + @apply btn bg-run-500 text-ink-950 hover:bg-run-400; + box-shadow: 0 0 0 1px rgba(34, 197, 94, 0.35), 0 8px 24px -10px rgba(34, 197, 94, 0.7); + } + + .btn-primary:hover { + transform: translateY(-1px); + } + + .btn-secondary { + @apply btn border bg-ink-850 text-slate-200 hover:bg-ink-800 hover:text-white; + border-color: var(--hairline-strong); + } + + /* Screenshot chrome — mimics the app window it is showing. */ + .frame { + @apply overflow-hidden rounded-xl border bg-ink-900; + border-color: var(--hairline-strong); + box-shadow: + 0 1px 0 rgba(255, 255, 255, 0.04) inset, + 0 30px 70px -30px rgba(0, 0, 0, 0.9); + } + + .frame-bar { + @apply flex items-center gap-1.5 border-b px-3.5 py-2.5; + border-color: var(--hairline); + background: rgba(20, 23, 32, 0.6); + } + + .frame-dot { + @apply h-2.5 w-2.5 rounded-full; + } + + .tabnum { + font-variant-numeric: tabular-nums; + } + + /* Section divider that fades at both ends. */ + .rule { + height: 1px; + background: linear-gradient( + 90deg, + transparent, + rgba(148, 163, 184, 0.18) 20%, + rgba(148, 163, 184, 0.18) 80%, + transparent + ); + } +} diff --git a/site/tailwind.config.cjs b/site/tailwind.config.cjs new file mode 100644 index 0000000..434c7ef --- /dev/null +++ b/site/tailwind.config.cjs @@ -0,0 +1,74 @@ +// PatchDeck Website — Tailwind theme tokens +// Copyright (c) 2026 Jeremy McSpadden + +/** @type {import('tailwindcss').Config} */ +module.exports = { + content: ["./index.html"], + theme: { + extend: { + colors: { + // Product surfaces — matches the real dashboard chrome. + ink: { + 950: "#08090C", + 900: "#0B0D11", + 850: "#0F1116", + 800: "#141720", + 700: "#1C202B", + 600: "#272C3A", + }, + // Primary CTA — design-system "run green". + run: { + 400: "#4ADE80", + 500: "#22C55E", + 600: "#16A34A", + }, + // Live status accents, lifted from the app's own state colors. + state: { + processing: "#60A5FA", + progress: "#F59E0B", + failed: "#EF4444", + resolved: "#22C55E", + }, + }, + fontFamily: { + mono: [ + "Fira Code", + "ui-monospace", + "SFMono-Regular", + "Menlo", + "Monaco", + "Consolas", + "Liberation Mono", + "monospace", + ], + sans: [ + "Fira Sans", + "ui-sans-serif", + "system-ui", + "-apple-system", + "BlinkMacSystemFont", + "Segoe UI", + "sans-serif", + ], + }, + maxWidth: { + shell: "76rem", + }, + keyframes: { + "pulse-dot": { + "0%, 100%": { opacity: "1" }, + "50%": { opacity: "0.35" }, + }, + "rise": { + from: { opacity: "0", transform: "translateY(12px)" }, + to: { opacity: "1", transform: "translateY(0)" }, + }, + }, + animation: { + "pulse-dot": "pulse-dot 2s ease-in-out infinite", + rise: "rise 0.5s ease-out both", + }, + }, + }, + plugins: [], +}; diff --git a/site/vercel.json b/site/vercel.json new file mode 100644 index 0000000..bd5060c --- /dev/null +++ b/site/vercel.json @@ -0,0 +1,28 @@ +{ + "$schema": "https://openapi.vercel.sh/vercel.json", + "cleanUrls": true, + "trailingSlash": false, + "headers": [ + { + "source": "/assets/(.*)", + "headers": [ + { "key": "Cache-Control", "value": "public, max-age=31536000, immutable" } + ] + }, + { + "source": "/styles.css", + "headers": [ + { "key": "Cache-Control", "value": "public, max-age=3600, must-revalidate" } + ] + }, + { + "source": "/(.*)", + "headers": [ + { "key": "X-Content-Type-Options", "value": "nosniff" }, + { "key": "Referrer-Policy", "value": "strict-origin-when-cross-origin" }, + { "key": "X-Frame-Options", "value": "DENY" }, + { "key": "Permissions-Policy", "value": "camera=(), microphone=(), geolocation=(), interest-cohort=()" } + ] + } + ] +}