The website the owner asked for wasn't the product the studio needed.
I found this studio looking for a tattoo. The website was a Canva blog page with a note from the owner saying he was buried in admin. I cold-DMed him with a working demo, used a 5-minute reframe call to move the engagement off "build a website" and onto fix what's actually leaking revenue. Five months of solo build later: deposit-gated booking, LLM design assist, revenue-weighted waitlist, and 11% revenue growth on a $1.3M base.
I cold-DMed the studio with a working demo before there was a conversation.
"I know it looks bad. I'm booked solid and buried in admin. Haven't had time to fix it."
Canva-style blog page with a few photos, paraphrased note from the owner above the fold. The studio is well-known locally, two-year wait on the artist tier most clients want, but the front door looked like a placeholder from 2019.
A live demo site. Friday evening. Cold inbound. No retainer in place.
Built and deployed a working studio-site demo over a Friday and pinged the owner with the link. He responded the same evening, was impressed, but didn't bite. That hesitation was the signal that the website wasn't the real pain. The real engagement got opened by a 5-minute reframe call the following week.
Three of four artists named designs as the biggest pain. The data sided with the fourth.
I stopped the owner on the next call: five minutes, I want to understand the pain points before either of us decides anything. Asked the magic-wand question. He said scheduling and admin work. The website was the artifact, not the wound.
Monday I went on-site. Walked each of the four artists individually through their full client lifecycle: discovery, consult, design, session, billing. Asked where time went and what hurt. Three artists named designs as their biggest time sink. One named last-minute cancellations leaving an empty chair. The owner's mental model matched the majority: designs are the bottleneck, find a way to speed them up.
I drilled into "designs" as a category. The pain wasn't drawing. It was translating vague client briefs into a visual. Clients showed up with a Pinterest screenshot, a sentence, or "something with a bird, idk." Artists spent unpaid consult hours converting those into renderable concepts before any tattoo time was billable.
The senior call: artist pain is not the same as revenue pain
Artist time on designs is unpaid time the studio absorbs. Empty chair time from a no-show is directly billable revenue walking out the door. The artists' stated #1 was the right problem for them; the studio's #1 was a different problem. I sequenced scheduling and deposits into v1 and pushed LLM design assist to v2. Sold it to the owner with the same data: a wall-projected unit-economics view of where time was actually leaking and what each leak was worth.
Three findings reshaped the v1 scope
- The website was a symptom of admin overload. The owner had self-diagnosed in plain text on the home page; the artifact had drifted because no one had time to fix it. Building a better website without fixing what was eating the time would have produced a prettier site and the same problem.
- No-shows were the largest direct revenue leak. Baseline 26% no-show rate on a chair-time business at $1.3M billings made this the biggest single dollar item on the discovery board.
- The 4 artists are not interchangeable. Each has a style, a clientele, and a lead time. Anything I built had to model artist-level constraints (skill set, color palette, session length norms), not collapse them into a generic studio calendar.
Four primitives, one stack, two layers of guardrails.
v1 hypothesis, stated cleanly: if I shipped a deposit-gated self-serve booking layer, an LLM design assist, and a backfill waitlist on a shared stack, then clients would self-design and book through the website instead of consuming artist consult time, and any no-show slot would refill from a queue instead of going dark. We'd know it worked when no-show rate fell, slot-fill rate climbed, weekly admin hours dropped, and revenue lifted as the same chair-time absorbed more billable work.
One north star: weekly billable chair-hours. That metric collapses the no-show problem and the slot-fill problem into the same number. Guardrails: artist tool-friction, client drop-off at the deposit gate, design-output quality complaints, and recurring-client confusion about the new flow.
System architecture · v1 build
Four product primitives sharing one data layer and one identity model. Guardrails sit at design-time (system prompt) and runtime (input/output logging, payment idempotency).
The "why one stack" call paid off once v2 started. Adding the Llama 3.1 pre-gate, the revenue-weighted waitlist cron, and dynamic slot-length on the booking flow were three changes touching one data model, not three integrations. The architecture decision was the prerequisite for the iteration speed the next section describes.
Four primitives, live in production, capturing data from day one.
v1 was a hard launch to all 4 artists at once. No pilot, no soft-launch cohort, no holdout. The owner posted the new system on Instagram and turned off the legacy booking path the same week. That decision was not best practice; it was shipped that way because the studio is small and a parallel-system rollout was operationally messier than the cutover. I instrumented Mixpanel events on every primitive so the v2 corrections could be data-driven, not vibes-driven.
Size-tiered booking with dynamic slot lengths.
Mechanism
Client picks an artist, picks a tattoo size (small / medium / large / xlarge), picks a slot. Slot length is dynamic, not fixed: small is 30 min + 10 min buffer, medium is 90 min + 30 min buffer, large is 4 hours + 90 min buffer. Studio's actual session-length norms baked into the slot generator instead of inheriting a generic 1-hour grid.
Why size-keyed instead of artist-estimated
Asking the artist to confirm every booking before it's accepted reintroduces the bottleneck the system is trying to remove. Asking the client to size their own tattoo with visual references is fast, accurate enough, and self-correcting at deposit time (a small-tattoo deposit on a clearly large piece is rejected at consult). I kept the artist out of the booking path on purpose.
The recurring vs. new split
Self-serve adoption broke cleanly along the new-client / recurring split. 70% of new clients booked through the website against 43% of recurring clients. Recurring-client volume is only ~6% of total bookings, so the absolute miss is small, but the directional read is real: regulars prefer to text their artist.
Non-refundable deposit at booking. v1 was a flat $25.
Mechanism
Booking is not confirmed until a non-refundable deposit clears Stripe. The deposit applies to the final price at the session. v1 used a flat $25 across all sizes; the assumption was that any skin-in-the-game would shift no-show behavior.
Why deposit-gating over the alternatives
I considered SMS reminders alone (no money), card-on-file with an authorization at booking, a 24-hour confirmation call, double-booking high-risk slots, and blacklisting repeat no-shows. Deposit-gating was the only mechanism that put real friction at the abandonment moment. Reminders nudge; deposits commit. Card-on-file without capture lets clients dispute. Double-booking trades one harm for another. Blacklisting is reactive and small-sample-fragile in a studio of this scale.
v1 result honestly stated
No-show rate moved from 26% to 22.4%, a 14% relative drop. Real, but smaller than I'd modeled. The flat $25 was too low to commit clients booking medium-and-up sessions where the eventual ticket is $300 to $1,500. v2 fixed this; v1 was the data that justified the fix.
Brief-to-image generation, system-prompt scoped to studio constraints.
Mechanism
Client describes the tattoo in plain English. If the brief is vague ("a flower", "something cool"), the system asks follow-up questions instead of generating. Once the brief is concrete enough, Nano Banana renders an image. Output ships with a "subject to artist approval" disclaimer so the artist remains the design authority and the model is positioned as a brief-translator, not a replacement.
The 6-revision system prompt with the owner
I iterated the system prompt with the owner across six revisions over two weeks. Constraints he insisted on, that I would not have added on my own: no political figures or ideological imagery, no novelty / meme requests, no photorealism the artists can't reproduce by hand, color palette restricted to inks the studio actually stocks, body-part awareness so a forearm piece doesn't get rendered at chest scale. The vague-brief follow-up loop was his idea too; it mirrors how artists handle real consults.
Winning artist adoption with unit economics
The artists pushed back early. Concern was design quality and being shown half-baked AI work the client expected them to honor. I didn't pitch them one-on-one. I projected the math on the wall: ~$0.40 model cost per consult against 10 to 15 minutes recovered per client, equivalent to roughly one additional client every two days of capacity. The owner mandated the trial. All four adopted post-mandate. Honest about the mechanism: this was top-down, not consensus-built.
FIFO queue against no-show slots. v1 ran on first-in-first-out.
Mechanism
When a slot frees up (no-show, cancellation, overrun), the next person on the waitlist gets a notification with a 30-minute claim window. Click-to-claim, deposit captured at claim time. v1 ordered the queue strictly by signup time, on the assumption that fairness was the right anchor.
The data v1 produced
Used the studio's pre-existing client questionnaire (asks about prior tattoos, scope, references) as a feature in the waitlist signup. Within four weeks I could read the cohort cleanly: ~80% of waitlist signups had zero prior tattoos, and ~93% of those were small-ticket sessions. FIFO was filling slots, but the slots that opened up were medium-and-up windows, and the queue was front-loaded with small-ticket clients who didn't even know what they wanted yet.
Slot fit, not just slot fill
The fairness anchor was the wrong frame. The studio doesn't sell waitlist position, it sells artist time. If a 4-hour large-piece slot opens up at 11am and the FIFO front of queue is a small-piece walk-in, the studio fills the chair at a fraction of the slot's revenue potential. v1 fixed slot-fill rate; it didn't fix slot-fit. v2 reframed the queue around expected revenue per slot.
v1 launched. Then the data taught me what to build next.
Three v2 corrections shipped between weeks 6 and 14 of the engagement, each grounded in v1 data. Listing them in the order the data surfaced them, not the order I shipped them. I did not get all three sequencing decisions right; the BNPL + SMS one is in the redo section for a reason.
Flat $25 → size-tiered → tiered + Klarna BNPL + Twilio SMS.
v1: flat $25
Hypothesis: any non-refundable deposit moves the needle. Result: no-show rate 26% → 22.4%, a 14% relative drop. Real but underwhelming. The flat fee was too small relative to the eventual ticket size to commit clients booking anything above small.
v1.5: size-tiered ($25 / $50 / $100 / $250)
Mapped deposit to size selection: $25 small, $50 medium, $100 large, $250 xlarge. Visual size references next to each tier so a client knows what "medium" means. Hypothesis: scale the friction with the ticket. Result: bookings dropped 22% week-over-week. The $50 medium-tier was the choke point. ~60% of client volume goes into medium pieces; doubling their deposit at booking caused real abandonment at the gate.
v2: tiered + Klarna BNPL on medium-and-up + Twilio SMS reminders
Two changes shipped together (the redo section addresses why that was a mistake). Klarna BNPL handles the deposit on medium and above so the up-front friction returns to small-tier levels while the commitment stays. SMS reminder fires 24h before the session. End state: no-show rate at 7.8%, a 70% relative drop from baseline. Bookings recovered to pre-tiered levels. Both interventions hit at once so per-feature attribution is not clean, which is itself a finding.
FIFO → 2-hour priority windows scored by expected revenue per slot.
The signal v1 produced
FIFO waitlist filled chairs but mismatched them to the slots that opened. ~80% of waitlist signups were zero-prior-tattoo clients; ~93% of those wanted small pieces. The studio kept losing yield on medium-and-up slots that freed up unexpectedly because a small-piece walk-in was at the front of the queue.
v2 algorithm
Waitlist signup now requires a size selection (the same tier vocabulary as booking). When a slot frees, a Supabase cron job runs at 2-hour cadence and scores every active waitlist entry against the freed slot by expected revenue, not arrival time. The 2-hour batching window was a fairness compromise: pure revenue-max would penalize earlier signups who happened to be smaller. Within a window, the highest-yield match wins.
Smart routing of overrun time
If a 4-hour large session finishes in 2.5 hours, the freed 1.5-hour tail is a medium-shaped window. The same scoring routes a medium-sized waitlist entry into that window if one matches. This was not in v1 at all; it became viable once size was a first-class field on every booking.
The named weakness
Waitlist post-session satisfaction came in at 40% in the post-tattoo survey. The complaint was almost always the same: no ETA, no estimated wait, no signal of where they were in line. v2 fixed slot-fit; it did not fix queue transparency. That sits in the redo section.
Llama 3.1 intent classifier in front of Nano Banana, via Groq.
The abuse pattern v1 logged
Runtime input/output logs surfaced an obvious pattern within two weeks of launch: a non-trivial share of design-assist requests were novelty (political figures, meme tattoos, "draw me a pig in a suit"). None of those would ever get tattooed. Each one still hit Nano Banana at full per-call cost.
v2 architecture
Inserted a Llama 3.1 intent classifier on Groq's free tier in front of Nano Banana. Every design request passes through the classifier first; if the intent is plausibly tattooable (real subject, in-scope content, body-part appropriate), Nano Banana fires. If the intent is novelty, ideological, or off-scope, the system returns a generic redirect prompt asking the user for something realistic. Free Groq tokens make the gate cost-free at runtime.
Tradeoff stated honestly
+2.5 seconds of latency added to every legitimate request because of the extra hop. −66% on average LLM cost across the design-assist surface. The latency tax was acceptable because design assist is async by feel; users expect a beat before an image renders. If this were synchronous chat I would not have shipped it.
Measured against Mixpanel + Supabase dashboards I stood up at engagement start.
11% revenue growth measured over 4 months post-launch on a ~$1.3M annual base. Same chair time, more billable work landing in it. Headline driver: no-show rate dropping from 26% to 7.8% combined with smarter waitlist backfill recovering 58% of no-show revenue.
26% baseline → 7.8% post-v2 deposits + BNPL + SMS, a 70% relative reduction. Three iterations got us here: flat $25 (−14% rel), tiered (no further drop, lost bookings), tiered + Klarna BNPL on medium+ + Twilio SMS reminders 24h prior.
70% of new client bookings routed through self-serve, against 43% of recurring clients. Recurring volume is only ~6% of total bookings, so the absolute miss is small; the directional read is that regulars prefer to text their artist directly. Not a problem to solve, a behavior to respect.
Self-reported weekly admin time across owner + 4 artists dropped from ~12h to ~5h. Time-logger fall-off after week 3 is real and named; the post-launch number is built from the weeks the logs were filled. Treated as directional, not precise.
Post-session feedback · what the survey said
Embedded a 1-minute, 5-tap survey at session checkout in exchange for a $5 discount on the next visit. Response rate: 90%, against 15% on the v1 attempt that texted clients after the fact with no incentive. The survey ran for four weeks and was retired once volume stabilized. Three measured surfaces:
Surface satisfaction · post-session survey
Three things I'd do differently.
I'd ship Klarna BNPL and Twilio SMS in sequence, not together.
The two changes targeted different problems (booking abandonment at the deposit gate vs. no-show after booking) and both shipped in the same week. The combined effect was unambiguously positive, but I lost the ability to attribute the drop in no-show rate cleanly between the two levers. Concretely: I'd ship SMS reminders first because it's the lower-risk change, hold for two weeks of data, then ship Klarna. The cost of the lost attribution wasn't large in this engagement, but the discipline matters when a portfolio of these decisions compounds.
The weekly time-logger died after week 3. I should have built a lighter measurement primitive.
I asked the owner and artists to log weekly admin time on a structured form. Compliance dropped sharply after week 3 once the novelty wore off and the logging itself became part of the admin overhead the project was supposed to reduce. The fix is not "remind them harder." The fix is to derive the admin-hours number from system data (booking confirmations sent, manual reschedules, deposit reconciliations) and only ask the humans for the residual that can't be measured. I'd do that on the next engagement and treat self-report as the validation layer, not the source of truth.
The diagnosis was the product. The build was downstream.
The owner had been on a Canva site for two years and had no working model of where his revenue was actually leaking. The engagement existed because no one had quantified the leak. If I were starting over, I'd sell the diagnosis explicitly as the first deliverable (a one-pager naming the no-show cost, the deposit gap, and the consult-time leak in dollars) and only then propose the build. It would have shortened the trust-building phase, anchored every later product decision against a shared baseline, and made the v1 → v2 iteration story easier to defend internally because the dollar value of each correction was already on the wall.
If this is the kind of PM thinking you're hiring for.
Available for new product roles. Same playbook as above: discovery before spec, sequence by impact, ship the bet that moves the largest number first, own the bets that don't land.