Live product · Open PWA · Product case study · Interactive design kit
01 — The Problem
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.
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 clipboard at headquarters is a single physical artifact. It lives inside one building, during posted hours, and it can only be read by someone standing in front of it. There is no digital backup, no historical record, and no way to query it remotely. If you want to know what’s open, you drive there. That constraint shaped every product decision that followed.
02 — 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 live app.

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). Uncertainty is the product condition, so freshness language became a first-class design element everywhere.

Coherence without handoffs
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. Explore the interactive kit — it imports real production components and can’t drift from the app because it is the app.
03 — 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 — validating image bytes and dimensions, optional EXIF capture date, and an HQ proximity / GPS gate when metadata supports it.
Outcomes users see: uploads can become live availability when parsing is confident; land in pending review when the model or validator is unsure; reject when the image isn’t usable; or join the historic archive when the sheet isn’t current-cycle.
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.
04 — 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.
Multipart image
POST /api/sheet; Cognito-linked identity where required.
Validate bytes
Magic-byte check, strip/re-encode, dimension limits, optional preprocess before the model call.
Proximity check
EXIF GPS vs headquarters radius when present; skip when metadata doesn’t allow a fair check.
Structured JSON
Multimodal call returns rows, sites, dates — retries with backoff on bad output.
Schema + rules
Server schema validation; low confidence routes to pending review, not silent publish.
Redact + archive
Redacted public archive image; duplicate detection via content hash.
Human-in-the-loop: uncertain parses surface in admin review — trust is a workflow, not only a model score. Admin-validated snapshots feed back into the model as a curated training corpus.
05 — 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.
Row-by-row editor
Approve, correct dates, split confident vs. uncertain. Archived photo with zoom/pan.
Anchor + re-run
Validated rows injected as human-verified anchors; model surfaces missed sites.
Build the corpus
Snapshots marked for export build the corpus with production preprocessing.
JSONL → deploy
JSONL export → OpenAI fine-tune → model ID via OPENAI_SHEET_MODEL.
Dynamic few-shot also runs in parallel — top-N validated snapshots injected as live examples into every prompt, no training run required.
06 — Surfaces
The product, four ways.
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.
Longform
Product case study
Hero, pipeline diagrams, ML loop, kit explorer, and bookmarks — the full narrative on the product host (companion to this studio page).
UX & Design Kit
Tokens, not forks
Production-linked kit: semantic tokens power the live app; phases remap CSS without forking components.
Engineering & Ops
Cognito → Caddy → Node
Sheet uploads, vision parse, snapshot lifecycle, and admin review on a Cognito-protected API; Caddy → Node on Lightsail. Pipeline detail on the product case study.
Marketing
Same host, different door
Standalone HTML landing with OG cards, hero narrative, and TestFlight interest capture on the same host API.
Live product
PWA + Capacitor
Map, sites, guide, and clipboard capture — web deploy reaches TestFlight / Play internal users via Capacitor shells loading production.
07 — Takeaways
Five lessons that generalize.
State where authority lives
Booking stays on the sheet; the app describes last known state and freshness. The architecture refuses to overclaim what it actually knows.
In uncertain domains, provenance beats polish
Stale, warn, fresh, and archived semantics align map, list, and upload outcomes. Users can act on the data because they can see how old it is.
Domain feedback loops beat generic dashboards
Parsed rows, favorites, and historic stays answer “is this working?” — concrete domain signals replace abstract usage metrics.
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.
08 — Reflection
Uncertainty is the product condition.
Most product work assumes a stable source of truth and designs the interface around it. This one didn’t have that luxury. The truth lived on a piece of paper that only a few people could see at a time. Designing for that meant treating uncertainty as a feature of the system, not a bug — and building every layer, from UI copy to vision pipeline, to honor it.