
Who
I live in the Blue Ridge ops lane. Locals need a directory that feels like a map, not a lead-gen trap. Owners need a page they can claim without a sales call. Operators need dashboards that do not invent click-through rates.
BestWNC is for people hunting restaurants, coffee, contractors, wellness, and shops from Asheville to Boone, and for the owners of those places who will list free, claim if we already seeded them, and upgrade only when reach matters.
If you build MarTech, local SEO products, or Stripe-backed owner tools, this post is field notes.
What
I shipped a local business directory for Western North Carolina.
Stack on the box: Next.js 16.2.6, React 19, Convex, Clerk, Stripe, PostHog, Sentry, Resend, Tailwind v4, Bun, Phosphor icons, Vercel. Repo is private under HurleyUS/bestwnc.com. Site is public at bestwnc.com.

Listings start free. Paid plans live in code in lib/billing.ts, not as a fragile Stripe Dashboard catalog:
- Unlimited: $10/mo or $80/yr. photos, video, posts, events, widgets, social links, contact, messaging
- Featured: $50/mo or $480/yr. Unlimited plus priority / pinned placement
- Max: $100/mo or $960/yr. Featured plus ad credits and the top tooling tier
Manual add-ons stay manual until fulfilled: Online Presence Analysis $8, Vetted Badge $80, Listing Sync $480. Dynamic checkout builds the price at session time.
Seed data is 101 real WNC businesses. Asheville-heavy, plus Waynesville, Brevard, Hendersonville, Black Mountain, and the rest of the corridor. Claim if we already have you. Add if we do not.
The part that matters this week: analytics honesty. Owner analytics used to look busier than the measurement layer could defend. On September 8 I changed the API and UI so recorded cumulative views, review count, and average rating stay; period views, clicks, conversion rates, and weekly series return null with an explicit availability object. README says the same thing out loud. Cumulative views include repeat, owner, and bot traffic. they are not unique visitors. null is the product.
Same two days: ownership claims and billing hardened, unsupported sales claims stripped from marketing surfaces, Stripe webhooks allowed through session middleware when signed, and honeypot fields on every public contact / newsletter form because spam was flooding Advertising and General Inquiry. Fill the hidden field and you get silent success, no email, no lead row.

Where
It runs on Vercel against Convex. Auth is Clerk. Money is Stripe. Mail is Resend. Errors go to Sentry. Product events go to PostHog.
Surfaces that matter:
- Public discovery: explore, categories, cities, top-rated, trending, search, business pages
- Owner funnel:
/owners,/claim,/add-business, dashboard edit / photos / reviews / analytics / upgrade - Pricing:
/pricingwith the plan cards that matchPLAN_DEFINITIONS
Audience sits in Western North Carolina and with builders who ship local directories instead of another generic "AI growth" wrapper.

When
2026-01-08. Init. Next.js + Convex + Tailwind. Directory MVP the same day.
January–February. Categories, seed scripts, Mountain Modern redesign, mobile nav, instant listing (no review queue), Sentry, claim + email verification, PostHog, Schema.org, PWA, design variant lab, Phosphor icons, Stripe checkout/portal/webhook, 101 real WNC businesses, photo upload via Convex storage, owner dashboard, admin moderation queue.
March. Launch-week security and reliability. Env validation that fails fast. Stripe monetization sprint. Dynamic pricing documented. Revenue-ready checklist. Owner plans, billing roles, social tables and signed-in pages.
April–May. Listing boosts on the owner dashboard. Real PostHog wiring passes. Blacksmith CI gates. Production deploy prep. Robots set to index, follow.
2026-09-07. PR #109. secure ownership claims and billing; remove unsupported sales claims; keep purchased manual services pending until fulfillment.
2026-09-08. PR #110. report only recorded business analytics (null where unmeasured). PR #111. honeypot fields on public contact forms. HEAD ae65364. Two hundred sixty-one commits on main.
That is the clock from empty repo to an honest owner dashboard.

Why
Directories lie by default. Fake weekly charts. Fake conversion. Fake "impressions" that never existed as rows.
I refused that for BestWNC. If the counter is cumulative and polluted by owners and crawlers, say so. If period CTR is not instrumented, return null, not zero dressed as insight.
I also refused a soft claim path and a spam inbox. Claims require verified identity. Billing routes have security tests. Inquiry forms fail closed for bots without giving them a bounce they can learn from.
The product is still a directory: find a place, claim a place, pay for reach when you want it. Shipping the revenue path and deleting the metrics theater in the same week is the operator move.
If you own a shop between Asheville and Boone, what would you fix first on your BestWNC page: photos, hours, or the claim so nobody else can edit it?
