A Bilingual Website, Then the App That Runs the Salon
A full page-for-page bilingual build, and 95% of appointments now arriving through online booking instead of a phone call
The Challenge
Fancy Pet Salon needed a comprehensive online presence to compete in Lynchburg, Virginia's dog grooming market. They wanted a bilingual website (English and Spanish) to serve the growing Hispanic community, with full local SEO targeting dozens of service and breed combinations. The existing web presence was minimal, just a Facebook page.
The Solution
I built an Astro SSR website covering every grooming service, dog breed, and location combination. Each page has unique content, Schema.org structured data, and proper hreflang tags for the Spanish mirror. The site is built on Astro with a custom CSS framework, no WordPress, no page builder bloat. I also built an AI chatbot (Buddy) powered by Groq/Llama 3.3 70B that answers customer questions in both English and Spanish, with guardrails to prevent off-topic responses. The site is server-rendered behind Cloudflare with a hero poster image as the LCP element for fast mobile load.
Beyond the Website: Systems That Keep Working
A fast website is the foundation, not the finish line. To keep Fancy Pet Salon visible in local search and easy to book, we layered two automated systems on top of it, the same playbook we run for J.M. Field and other local clients.
Automated Google Business Profile Posting
An automated poster publishes a fresh Update to her Google Business Profile every week, with no manual effort from the owner. A profile that stays active signals to Google that the business is current, which supports Map Pack visibility for high-intent searches like "dog groomers near me." It is the same posting engine that keeps our other local clients' profiles fresh week after week.
Online Appointment Booking
We stood up a self-hosted Cal.com booking system so pet owners can book a grooming appointment online instead of playing phone tag. Availability lives in one place, and the booking link drops straight into the website and the Business Profile so customers can act the moment they find her.
Then the Book Itself: Rebuilding the Salon on an App
Getting the bookings in was only half of it. Fancy Pet Salon is a one-groomer shop. The person who takes the booking is the same person with both hands on a dog for most of the working day, so anything that asks her to stop, pick up a phone, or go find a pen competes directly with the work she actually gets paid for.
So we built the book. Three surfaces sitting on one back end: a browser dashboard at admin.fancypetsalon.com, which came first, an Android app she keeps on her phone, and an iOS build now going out through TestFlight. The Android and iOS apps are not two codebases. The screens, the data layer and the business rules are one Kotlin Multiplatform module compiled for both, so a fix to the booking logic lands on her phone and on an iPhone from the same change. The app is sideloaded rather than published, and it updates itself, checking a manifest, noticing a newer build, and offering it. Underneath both is a single SQLite database on the server holding clients, dogs, appointments, takings and expenses, with the Cal.com bookings syncing straight in. Nothing here is a SaaS subscription with the salon's data locked inside someone else's product. It is her book, on her server, in a file that can be copied.
What the Book Holds
Read out of the production database on 2026-08-17. These are counts, not estimates.
118
Client records in the app
138
Dogs on file
172
Appointments in the book
131
Completed with a payment recorded
25
Still upcoming
$85.87
Average ticket
12 of 172, 7%
No-shows
55
Days of takings recorded
93 of 118
Clients with an email address
5
Enrolled devices
2026-03-22 to 2026-10-01
Book spans
Online Booking Now Carries the Book
The number worth caring about is not how many appointments exist. It is how many of them appeared without the groomer doing anything.
| Month | Appointments | Booked online |
|---|---|---|
| 2026-03 | 31 | 0 |
| 2026-04 | 9 | 0 |
| 2026-05 (salon closed) | 1 | 0 |
| 2026-06 (salon closed) | 0 | 0 |
| 2026-07 | 74 | 70 |
| 2026-08 | 43 | 41 |
| 2026-09 (forward bookings) | 11 | 11 |
| 2026-10 (forward bookings) | 3 | 3 |
In July, 70 of 74 appointments came in through online booking. In August it is 41 of 43, and every appointment on the books for September and October arrived the same way. Counting from June, when online booking went live, it is 125 of 131. Those are grooms that landed in her day without a phone ringing mid-clip.
One honest note about that table. The early months are not weak trade. March and April were partial data entry while the system was new, and some of those rows arrived as a bulk import rather than as bookings. May and June are near-empty because the salon was shut: the owner was out of the country. So the only fully representative operating months are July and August, and we are not going to draw a growth curve through a closure.
The Shop Was Shut and the Book Still Filled
The owner splits her year between Lynchburg and Colombia. In May and June the salon was closed and she was out of the country. Nobody was answering a phone, and there was nobody in the shop to answer it.
During June the site took 33 bookings anyway. Thirty-one of them were for July, the month she came back. Her first weeks home were already full before the plane landed, and the calendar had closed itself for the dates she was away, so nobody could book a groom that was never going to happen.
It is happening again as this is written. She flies back on 21 September, and there are already 14 appointments on the books for that trip, every one of them booked online. That is the part worth stealing for any appointment business: the booking does not depend on somebody being free to pick up.
A Client Portal, So the Salon Is Not the Only One Holding the Record
Clients sign in at portal.fancypetsalon.com and see their own dogs: breed, size, notes, visit history. They keep their own contact details current, which means a new phone number or a change of medication reaches the groomer before the appointment instead of being discovered at the table. It runs on the same database as the salon dashboard and the app, so there is one record of a client, not three.
A signup from the portal is matched against the existing book automatically. When the match is uncertain it goes to a review queue rather than silently creating a second copy of somebody who has been coming for two years.
We Deliberately Gave Up National Traffic
Fancy Pet Salon is one groomer in Lynchburg. She cannot serve somebody in Phoenix searching "dog nail clipping", and for a while the site was ranking for exactly that kind of term: thousands of impressions a month, at position 33, converting nobody. We moved the site off national phrasing and onto its own trade area.
The result reads badly if you only look at impressions, and correctly if you look at customers:
| Search Console, 28 days | Before | After |
|---|---|---|
| Clicks from local searches | 44 | 44 |
| Local click-through rate | 2.19% | 3.36% |
| National impressions | 9,609 | 2,240 |
| National queries ranked for | 1,713 | 458 |
Local clicks were identical on both sides of the change. Local click-through improved. What went away was 7,369 monthly impressions that had been producing almost nothing: national click-through was 0.27%, against 3.36% locally after the shift.
Why the average position looks worse. It moved from 24.2 to 36.2, and that is the honest consequence of this choice. The old national pages did not disappear, they sank: "dog nail clipping" went from position 33 to position 70 and still logs impressions nobody clicks. Those dead entries drag the average down. It is a metric we accepted getting worse on purpose, because the alternative was keeping traffic that never became a customer.
What Four Months of Real Use Actually Taught Us
Five features in this app work correctly and have never been used once in production:
| Feature | Used in production |
|---|---|
| Dog photos | 7 of 138 dogs |
| Grooming notes on a dog | 8 of 138 dogs |
| No-shows marked | 12, the feature is now in use |
| Rabies expiry dates | 0 of 138 dogs |
| Signed consent forms | 0 |
The split is clean enough to be a design rule. The transactional features have total adoption. The reference-data features have none. Anything attached to a moment that was going to happen anyway, an appointment arriving or money changing hands, gets used constantly. Anything that asks a busy person to stop and enter data about a dog for some later benefit sat untouched for four months.
The wrong response to that is more reminders and more empty fields with red asterisks on them. The right response is to move the capture to a moment that already exists. Ask for the rabies date when a dog turns up for its first appointment, not on a profile screen nobody opens. That is what we build next, and it is only visible because the app has been in real hands long enough to show it.
Still Shipping
Builds 37 through 41 went out across 2026-07-29 and 2026-07-30. Rabies dates now surface on the appointment instead of only on the dog's page. A warning flags when the client arriving today missed their last appointment. The price a dog was charged last time is offered back at payment. Tips are recorded separately from the groom price. And 54 drawn breed portraits fill in automatically from the breed field, so a client list without photographs is not a wall of initials.
Two of those, the rabies prompt and the no-show warning, are correct and currently inert, because no rabies date has been entered and no no-show has been marked. That is the same finding as the section above, showing up in the release notes.
The Part Nobody Planned
A book built for one salon turned out to be a book most small grooming shops need. That became PupBooker, the same codebase cut into a separate product through Android build flavours, local-first on the phone with Google Drive backup. It is in closed testing on Google Play right now, free during testing and capped at 15 clients. It is not publicly available, it is not priced, and nobody outside the test group is using it. It is here only as the honest end of the arc: build something specific enough to be genuinely useful to one person, and it tends to be useful to the next hundred.
Results
Same Local Clicks, 77% Less Noise
We moved the site off national terms and onto its own trade area. Local clicks were 44 before the shift and 44 after, while national impressions fell 77%. Local click-through went from 2.19% to 3.36%. Verified in Search Console
95% of Appointments Booked Online
125 of 131 since online booking went live in June, counted in the production database on 2026-08-17. In July it was 70 of 74, in August 41 of 43
$85.87 Average Ticket, 7% No-Shows
Across 131 paid appointments. Page one for "dog groomers Lynchburg VA" and "dog grooming Lynchburg VA", the searches that actually convert
One Book, Four Surfaces
Web dashboard, self-updating Android app, iOS build in TestFlight, and a client portal, all on one SQLite database running 118 client records and 138 dogs
216 Pages
Full coverage of services, breeds, and locations in both languages
Bilingual
Complete EN/ES mirror with hreflang tags and localized SEO
AI Chatbot
Custom Buddy chatbot handling customer inquiries 24/7 in both languages
Automated GBP Posts
Weekly Google Business Profile Updates published automatically to keep the Maps listing active for local search
Want Similar Results?
Let's discuss your project. Free consultation, no strings attached.
Get in Touch