PRJ-02Turnkey sauna building

Your Saunas: the thing being sold does not exist yet.

A custom CRM for a turnkey sauna builder. The room is designed, priced, framed, and installed by the same system, because the price depends on a shape that only exists as a drawing when the client is deciding.

PRJ-02-AScope of work

What the business does.

Turnkey sauna building: design a room to fit the space a client has, quote it, take a deposit, order the materials, build it, and install it with a crew.

The sale happens over a drawing. Everything downstream, the material list, the framing, the price, the deposit, follows from the shape agreed in that conversation.

The problem shape.

No product you can buy has a sauna in it. There is no field for a bench, no rule that a door onto a hot bench has to open outward, no concept of a wall elevation at a consistent scale.

Before the system, the drawing, the material list and the price were three separate artefacts made by three separate acts of attention. Three artefacts describing one room will eventually disagree, and the disagreement surfaces in front of the client.

PRJ-02-BDetail callouts

What was built.

Most of this is a CRM. The first four items are not: they are a drawing editor for one trade, about ten thousand lines of it, and the planner running on the CRM page is that code on demo data. Each item names the file that proves it.

A

A drawing editor for saunas, not a shape tool with prices on it

Drag a corner and the neighbouring walls move with it, because a diagonal wall prices a room nobody can frame. The area, the material take-off and the price follow the drag rather than being recalculated afterwards by hand. Underneath it are benches with their own anchors and thickness, doors and windows that know which stretches of wall they may start at, vents, the heater and the power feed, each drawn where the trade puts it. It is the largest single thing in the system.

Proof

components/sauna-planner.tsx, lib/sauna-plan.ts

B

The rules in it are his, and they are not settings

A vent sits no closer than five inches to the end of the glass or to the corner, because a hole cut into the last inches of a stud bay has nothing left to fix it to. A power feed is never lower than four inches off the floor, comes with three feet of cable at that point, and is drawn as a one-inch dot that stays on top of whatever it overlaps. A bench is between four and seven inches thick. None of these is a preference a user can switch off, and no product you can rent has a field for any of them.

Proof

lib/sauna-plan.ts · VENT_CLEARANCE_IN, POWER_FEED, BENCH_THICKNESS_IN

C

Heater sizes are read, never guessed

The contractor pastes the supplier's own specification into the product card, and the planner reads the kilowatts, the width, the depth and the room volume out of that text to rank which heaters fit the room being drawn. Every field is optional: a heater whose rating cannot be read is shown as unknown rather than sized by inference, and a safety rail filed under Heater reads as nothing at all, which is the correct answer.

Proof

lib/heater-spec.ts

D

Wall elevations at one scale, and a set the crew can build from

Each wall drawn as its own elevation, all at one scale on purpose, so two walls can be compared by eye without reading the numbers. Doors default to swinging outward, because a door opening inward onto a hot bench is a safety problem rather than a preference, and rough openings add their own margin so nobody works it out on site. The plan and the elevations print as a PDF, which is the artefact that actually leaves the office.

Proof

components/wall-elevations.tsx, components/sauna-plan-pdf.tsx, components/sauna-elevations-pdf.tsx

E

The caller card arrives before the phone rings

An inbound call lands and a card is already in Telegram: who it is, the number in a tappable form, and the last project they discussed. By the time the phone is answered the context is there. A call cannot move a project on its own.

Proof

app/api/twilio/voice/route.ts

F

Deposits by card and by bank transfer

Built on Stripe Connect, so the money lands in the contractor's own account rather than passing through ours. ACH is offered alongside card because on a large deposit the card fee is the difference between a few dollars and a few hundred. Fees are computed in whole cents and refunds return them proportionally.

Proof

lib/deposit-fee.ts, lib/stripe.ts

G

E-signature built in-house

The signature page is composited into the real PDF with both names, the date, and an audit trail. Branding comes from the company record, so the document looks like the contractor's rather than like a software vendor's.

Proof

supabase/migrations/028_esignature.sql, app/sign/

H

The rest of the working day

Stock that comes off the shelf when a client approves, once, even if the webhook fires twice. Crew payouts under one rule instead of three that disagreed. Reports that agree with each other because revenue is defined in exactly one module.

Proof

lib/inventory.ts, lib/installer-pay.ts, lib/revenue.ts

I

3152 tests and 189 migrations

The test suite runs 3152 tests across 249 files, and every schema change since the first day is a numbered migration. 30 of the tests are guard tests that read the source rather than the output, and each one had to fail against the code that shipped the bug before it was allowed in.

Proof

supabase/migrations/ (189 files), lib/__tests__/

J

Deliberately no AI

Both AI features this system inherited were removed. Neither could actually run, and one of them reported work it had not done. Saying so is more useful to the next client than a feature list that quietly implies otherwise.

Proof

See /services/ai-agents for where the AI actually is

PRJ-02-CReference

Where this lives.

Custom build under Orium Studios. Its own database, its own migration runner, its own deployment.

What this sheet leaves out, on purpose. Not one figure of the client's appears on it: not what the build cost, not what he charges, not what his estimates come to, not what the business turns over. Those are his to disclose and nobody else's. There is no owner's name and no third party's name either. There is also a second product in this client's world, an online store, which was built here but whose selling is run by someone else entirely; this sheet describes the CRM and claims nothing about how that store trades. And this system contains no AI at all, deliberately: its telephony raises the caller's record before the phone rings, and a person takes it from there. Nothing on this sheet is an AI claim about this client.

System
Your Saunas
Repository
sauna-crm
Trade
Turnkey sauna building

Files named on this sheet

  • sauna-crm · components/sauna-planner.tsx, the planner
  • sauna-crm · lib/deposit-fee.ts, Stripe Connect deposit fees
  • sauna-crm · lib/revenue.ts, one definition of revenue
  • sauna-crm · lib/stripe.ts, byte-identical in all three systems

Three separate deployments, three separate databases, and no shared data of any kind between clients. What they do share is the code that was proven in one of them first: lib/stripe.ts is byte-for-byte identical in all three repositories.

Read the build log: what was found, and the commit that fixed itBack to the drawing index

SHT-10Issued for construction

Start with the audit.

A short video walkthrough of where your lead flow leaks and what it would take to close it. Free, and there is no call afterwards unless you want one.

  • Where the lead flow leaks, walked the way a buyer walks it
  • The one fix worth doing first, and what it is costing to leave
  • What to automate first, and what is not worth automating yet
Get my free audit →

30 seconds to submit · delivered same day · no card, no call