Product design · ownership · shipping

A trustworthy mirror for an analog campground board

Green Ridge State Forest campsites are first-come, first-served — managed at a physical signup sheet during limited hours at headquarters. Availability is invisible to anyone not standing in front of that board. The design problem: make the clipboard legible and shareable, honestly — without pretending the app is something it isn’t.

Solo build, full ownership: product design, design system, frontend, API, vision pipeline, admin tooling, ops, and marketing — shipped to web and native WebView users.
Site detail in Greenridge: notes, calendar, and context for one campsite.
Greenridge map with campsite pins and search.
Greenridge sign-in screen.
Overview

At a glance

Role
Product design, UX writing, design-system ownership, full-stack implementation, deployment & ops docs.
Timeline
Rapid foundation in early 2026; iterative pivots toward clipboard truth, calmer field UX, and operational hardening.
Stack
React · Vite PWA · Node/Express · AWS Cognito · Anthropic Claude · Lightsail · Caddy · Capacitor · Mailgun.
Status
Live at /greenridge/; marketing at site root; optional Reddit ingest & cron jobs documented for ops.
Problem discovery

Ground truth lives on a physical sheet

Green Ridge doesn’t expose a reservation API — the Forest Service manages a first-come, first-served signup sheet at headquarters, staffed during limited hours. The operational ground truth lives on that physical board. Campers want planning context (what kind of site is this?) and situational context (what did the board say recently?). A polished “availability” UI that pretends to be authoritative would erode trust faster than no app at all.

Uncertainty is not an edge case in the product. Uncertainty is the product condition. — from the in-repo design process write-up

The design problem became: help people browse and compare sites honestly, surface freshness of clipboard-derived state, and make contribution at HQ feel fast and cooperative—not extractive.

The official first-come camping roster on a clipboard at Green Ridge State Forest headquarters.
The operational artifact at HQ: the only live source for campsite availability. Greenridge mirrors this clipboard — it doesn’t replace it.
Design process

Full ownership, no handoffs

The build wasn’t sequential phases — design, then engineering, then ops. It was concurrent decisions made by the same person. Three choices shaped everything.

The artifact defined the architecture

The physical signup sheet at HQ didn’t just supply data — it supplied the mental model campers already use on-site. That constraint drove the information architecture more than any wireframe: the grid they reason about at headquarters had to be the grid they see in the app. Any abstraction that lost that fidelity would also lose trust.

Honesty as a design primitive

Every availability state — fresh, stale, warn, archived — had to be designed twice: once for browsing (map and sites list) and once for contributing (the clipboard flow). A camper arriving at HQ needs to feel like their photo will help others, without making promises the system can’t keep. That’s an editorial and interaction problem, not just a data problem. Uncertainty is the product condition, so freshness language became a first-class design element everywhere.

Coherence without handoffs

With one person making all calls, coherence doesn’t come from review cycles — it comes from shared primitives. The semantic design token system and functional design kit weren’t luxuries; they were the mechanism that kept the story consistent across map, admin, marketing, and native shells while surviving pivots without visual regressions. The kit imports real production components — it can’t drift from the app because it is the app.

Field & flow

Clipboard capture

Photography is the refresh mechanism: a camper at HQ captures what’s on the board so everyone else gets a more current picture—without claiming an official reservation.

Two entry points: the in-app update-from-clipboard flow for signed-in users, and the standalone photo-clipboard landing for social/share campaigns—same API host, consistent outcome semantics.

Field constraints: copy favors “photo” over “scan,” mobile-first layouts, and server-side checks that reinforce trust—e.g. validating image bytes and dimensions, optional EXIF capture date, and an HQ proximity / GPS gate when metadata supports it (skippable paths exist when GPS isn’t present). Those aren’t bureaucratic hurdles; they align contribution with actually being at the board.

Outcomes users see: uploads can become live availability when parsing is confident and dates align; land in pending review when the model or validator is unsure (staff can correct before publish); reject when the image isn’t usable; or join the historic archive when the sheet isn’t current-cycle—so copy and UI never blur those lanes.

Greenridge map screen on a phone.
Map
Greenridge sites list screen.
Sites
Greenridge campsite detail screen.
Site detail
Greenridge clipboard photo capture flow on a phone.
Clipboard flow

The operational artifact in the field: the product mirrors this object—not a reservation database. Tap the card to open the full clipboard photo flow.

Vision & backend

Clipboard pipeline and vision model

The vision layer doesn’t “guess reservations.” It extracts structured rows from a photo; the server validates JSON, applies privacy rules, and routes uncertain work to humans. Exact model IDs live in server config and can change; the architecture below is stable.

1 · Upload Multipart image POST /api/sheet; Cognito-linked identity where required.
2 · Hygiene Magic-byte check, strip/re-encode for archive, dimension limits, optional vision preprocess & downscale before the model call.
3 · HQ gate EXIF GPS vs headquarters radius when present; skip when metadata doesn’t allow a fair check.
4 · Vision parse Multimodal call returns structured JSON (rows, sites, dates)—retries with backoff on bad output.
5 · Validate Server schema validation and business rules; low confidence → pending review, not silent publish.
6 · Store Redacted public archive image; optional audit copy for admin; duplicate detection via content hash.
  • Human-in-the-loop: uncertain parses surface in admin review—trust is a workflow, not only a model score.
  • Duplicates & throttles: identical processed bytes rate-limit repeat full parses; protects cost and discourages spam.
  • Implementation pointers: api/src/server.ts (sheet upload route), api/src/lib/claude.ts (parseSheetImage), plus the in-repo clipboard pipeline work plan (stages, env, and ops).
The pipeline doesn't stop at Store. Admin-validated snapshots feed back into the model as a curated training corpus—guided re-parse seeds the model with corrected rows to catch missed sites, and a fine-tuning export packages each validated image + correction pair for OpenAI fine-tuning. See ML feedback loop below.
Accuracy loop

ML feedback loop

Every admin-validated snapshot is a training signal. Corrections made in the review UI feed directly into re-parse quality and the fine-tuning corpus—no separate annotation workflow.

1
Admin verify Row-by-row editor: approve, correct dates, split confident vs. uncertain. Archived photo with zoom/pan for cross-reference.
2
Guided re-parse Validated rows injected as human-verified anchors. Model re-runs, keeps guided rows authoritative, surfaces missed sites.
3
Dataset curation Snapshots marked includeInExports build the corpus. Same production preprocessing applied—model trains on what it sees at inference time.
4
Fine-tune & deploy JSONL export → OpenAI fine-tune job → model ID set via OPENAI_SHEET_MODEL. No code change to deploy.
Each correction improves the next parse. Dynamic few-shot also runs in parallel—top-N validated snapshots injected as live examples into every prompt, no training run required. (api/src/lib/sheetParseFewShotDynamic.ts)
Surfaces

The product, four ways

Design kit · Foundations · Color System
Phases Day · 2:00 PM Sunrise · 5:30 AM Sunset · 7:15 PM Night · 10:30 PM
Screenshot of the Greenridge interactive design kit Color System page: left sidebar lists Foundations, Atoms, Molecules, and Organisms; main area shows Palette — All Phases columns for Day, Sunrise, Sunset, and Night with semantic tokens (--color-bg, --color-text, etc.), and Contrast Pairs sample cards.
Production-linked kit: the same semantic tokens power the live app; phases remap CSS variables on :root without forking components.

UX & design kit

Time-of-day phases remap the same semantic CSS variables on :root—components don’t fork, tokens do. The interactive kit imports real components and global CSS so the explorer can’t drift from production.

Engineering & ops

Sheet uploads, vision parse, snapshot lifecycle, and admin review are described step-by-step under Clipboard pipeline and vision model. Operationally: Cognito-protected API, Caddy → Node on Lightsail, systemd + cron, deploy scripts in deploy/.

  • deploy/ README — deploy & server operations
  • api/src/server.ts — API entry, routes & middleware

Marketing

Standalone HTML landing at site root with OG/Twitter cards, hero narrative, and TestFlight interest capture via the same host API. A share-friendly clipboard landing lowers friction for “photograph the sheet” campaigns.

Mobile distribution

Capacitor shells load the production URL so a web deploy reaches TestFlight / Play internal users immediately—native polish without forking the product surface.

  • App Store listing and release checklist
  • Capacitor shell vs PWA and native plugin notes
Takeaways

Five lessons that generalize

  • State where authority lives. Booking stays on the sheet; the app describes last known state and freshness.

  • In uncertain domains, provenance beats polish. Stale/warn/fresh semantics align map, list, and upload outcomes.

  • Domain feedback loops beat generic dashboards. Parsed rows, favorites, and historic stays answer “is this working?”

  • Cleanup is design. Renaming flows, trimming chrome, and fixing mobile keyboards changed trust as much as new features.

  • AI as collaborator and runtime. Assistants accelerated shipping; vision parsing makes the analog board legible—within guardrails.

Bookmarks

Work map

Everything worth bookmarking in one place.

See it in the wild

Open the live PWA, the marketing story at site root, or the quick clipboard capture flow — same product, different front doors.