Product & engine spec · V1

The smallest thing that prices a job properly.

Eight features, one calculation engine, mobile first. Every number in the examples below is computed live by the same engine.ts that runs the app.

05

MVP specification

#FeatureV1 scopeExplicitly NOT in V1
1User accountEmail magic link plus Google sign-in. One user per business.Teams, roles, SSO
2Business profileName, ABN, phone, email, suburb, GST toggle, default rates (employee, owner, subbie), supplies, travel/km, target margin, T&Cs, monthly overhead list.Logo on PDF (Pro, V1.1)
3Quote calculator6-step wizard: job type, property, labour, other costs, overhead, target. Live price bar on every step. Rule-based time suggestion.Photo/AI room recognition, Maps travel
4Profit / marginMargin %, markup %, or profit $ targets; before or after overhead basis; 3 overhead allocation methods; red/amber/green with configurable thresholds; rounding options.Actual vs quoted hours
5GST10%, on/off from the profile. Ex-GST and GST shown separately; customer price includes GST.Tax invoices
6Customer message4 tones; editable; leak-check for internal terms; copy plus SMS, WhatsApp, email deep links; optional AI polish.Sending from our servers, PDF
7Save quoteDraft, Sent, Accepted, Declined, Completed. Duplicate and edit.Customer e-acceptance
8Quote historyFilterable list; basic monthly KPIs on Home.Full report suite (Pro)

Platform: responsive web app built on Hono + Cloudflare Pages. The engine is a pure TypeScript module shared by the client bundle and the server routes, so a quote is always reproducible. Storage: Cloudflare D1 (SQLite) keeps the business profile, overhead lines and saved quotes. Calculations run entirely on the device, so the app keeps working offline.

06

User journey

First run (target: first quote in under 4 minutes)

Sees TikTok / FB post→Landing: free calculator→Sign in (email)→3-question setup: GST? your hourly cost? your time's worth?→First quote→Sees red/amber/green→Copies message

On-site quote (core loop)

Walk-through with client→Job type→Rooms and condition→Accept or adjust hours→Costs prefilled→Result + round up→Message → SMS→Marked Sent

Follow-up and habit

Update status (Accepted)→Duplicate for a similar job→Monthly: Reports: which job types earn→July: “Wages rose 4.75%, review your rates” nudge
07

Wireframes

Home
+ Quote a job
Quotes 12
Win 58%
Rev $3.2k
Profit $640
Recent ▮ Sarah $325 Sent
Recent ▮ Tom $640 Won
Home · Quotes · [+] · Reports · Settings
One big action. KPIs are about this month only.
Wizard: Labour (3/6)
Suggested: 5 h labour [Use]
Employees [− 2 +]
Owner on job (○)
Hours on site [− 2.5 +]
2 × 2.5 = 5 labour h
Employee $/h [$38]
$322.15 · ● on target · [Next]
Live price bar: you never lose sight of the number.
Result
SUGGESTED QUOTE
$322.15 inc GST
BE $250 · 30% · $13/h
exact
320
325★
330
Breakdown: labour / direct / OH / GST / profit
Why this price? (AI)
Market reference (indicative)
Write customer message
Rounding re-calculates margin. “Best” = lowest round-up that meets target.
Message
[Prof][Friendly][SMS][Formal]
Hi Sarah, thanks for… our price for your fortnightly clean is $325 including GST per visit…
● No internal costs shown
SMS
WA
Mail
FB
Copy & mark as sent
No labour, cost or margin in the customer's text.
11

Mobile UI design

Principles

  • One-thumb use: primary action pinned bottom-right; tap targets 44px or larger (steppers are 40×40 inside 52px rows).
  • Steppers over keyboards: most inputs are counts, so no keyboard pops up on site.
  • The live price is always visible (bottom bar).
  • Colour means one thing only: red/amber/green is reserved for margin status.
  • Numbers use tabular mono so columns line up.

System

  • Type: Bricolage Grotesque (display) · Geist (UI) · Geist Mono (numbers).
  • Palette: warm paper #F5F3EE, ink #15171C, brand blue oklch(.52 .18 258); status red/amber/green in oklch.
  • Radius 12–22px; hairline borders instead of shadows (clearer in bright sunlight).

Open the working app →

08

Database / data structure

-- Cloudflare D1 (SQLite). All money stored in cents or REAL, AUD. All prices stored ex-GST + gst separately.
businesses     (id, name, abn, phone, email, suburb, state, gst_registered,
                employee_rate, owner_rate, sub_rate, supplies_per_job, travel_per_km,
                target_margin, billable_hours_pm, jobs_pm, terms, updated_at)
overhead_items (id, business_id, label, monthly_amount, sort)
settings       (id PK, red_below_pct, green_at_pct, round_to, overhead_method, basis)
quotes         (id, customer, suburb, status, job_type, created_at,
                inputs         TEXT,   -- JSON snapshot: property, labour, costs, overhead, target
                engine_version TEXT,   -- e.g. "1.0.0" — results reproducible forever
                chosen_price_inc REAL, message_tone, message_text,
                parent_quote_id TEXT)  -- duplicates

Storing the input snapshot plus engine version means a quote always shows the numbers it was sent with, even after defaults or the engine change. The client also caches state in localStorage so the calculator keeps working offline and syncs when reconnected.

09

Calculation engine specification

Definitions (strict)

TermFormula
Labour hourscleaners on job × hours on site
1. Direct labouremployees × h × employee rate + owner × h × owner rate
Subcontractor costsubbies × h × sub rate + fixed subcontracted items
2. Other direct costssupplies + travel + parking + equipment + laundry + waste + consumables + other
Direct job cost (Cd)direct labour + subcontractor cost + other direct
3. Overhead (O)per hour: (monthly OH ÷ billable labour h) × labour h · per job: monthly OH ÷ jobs per month (or override) · % of revenue: p × P
Revenue ex GST (P)see pricing below
4. GSTround₂(P × 10%) if registered, else 0
6. Customer priceP + GST
Gross profit / marginP − Cd · (P − Cd) ÷ P
5. Profit after overheadP − Cd − O · margin: ÷ P · markup: ÷ (Cd + O)
Break-even (inc GST)(Cd + O_fixed) ÷ (1 − p) × 1.1
Per labour hourP ÷ labour h · profit ÷ labour h

Solving for the price

Let C = Cd + O_fixed when the target is after overhead (the default), or C = Cd when it's on direct costs only (classic gross margin). Let p be the %-of-revenue overhead (0 if another method is used).

Gross margin M :  P = C ÷ (1 − M − p)          // NOT C × (1 + M)
Markup K       :  P = C × (1 + K) ÷ (1 − p(1 + K))
Profit $T      :  P = (C + T) ÷ (1 − p)
Guard          :  if denominator ≤ 0 → error "target + overhead % must be < 100%"
Rounding       :  P rounded to cents first, then GST = round₂(P × 0.10)

Rounding options

For step s (default $5): offer floor, ceil and ceil + s of the inc-GST price. Each is re-evaluated: P' = price ÷ 1.1, then margin, profit and status are recalculated. The recommended option is the lowest round-up, never the cheapest. Options below the calculated price are marked as trimming margin.

Status

Red if profit after overhead is below 0 or the margin (on the chosen basis) is under the red threshold (default 15%). Amber if it's under the green threshold (default 30%). Otherwise green. Both thresholds are configurable.

Time suggestion (not a price input)

Transparent rule-of-thumb: minutes per room type × job-type multiplier (bond 2.1×, deep 1.6×, builders 2.6×…) × condition (0.85–1.75) × frequency (regular only) × storeys × pets + access. Commercial and builders use floor-area rates. The user must accept it; it never changes hours on its own.

10

Example calculations engine self-test: pass

Computed live below by the same engine.ts that runs the app.

A · The brief’s test case: 30% gross margin on direct cost, before overhead

✓ PASS: expected $292.86 + $29.29 = $322.15

Labour hours2 × 2.5 h = 5 h
1 · Direct labour$190.00
Subcontractors$0.00
2 · Other direct costs$15.00
Direct job cost$205.00
3 · Allocated overhead$0.00
Revenue ex GST$292.86
4 · GST$29.29
6 · Customer price$322.15
Gross profit · margin$87.86 · 30.0%
5 · Profit after OH · margin · markup$87.86 · 30.0% · 42.9%
Break-even (inc GST)$225.50
Revenue / profit per labour h$58.57 / $17.57
StatusGREEN
Rounding options$320 → 29.5% · $325 → 30.6% ★ · $330 → 31.7%

B · Same job, target after overhead (per labour hour, $1,450/mo ÷ 320 h)

Labour hours2 × 2.5 h = 5 h
1 · Direct labour$190.00
Subcontractors$0.00
2 · Other direct costs$15.00
Direct job cost$205.00
3 · Allocated overhead$22.66
Revenue ex GST$325.22
4 · GST$32.52
6 · Customer price$357.74
Gross profit · margin$120.22 · 37.0%
5 · Profit after OH · margin · markup$97.56 · 30.0% · 42.9%
Break-even (inc GST)$250.43
Revenue / profit per labour h$65.04 / $19.51
StatusAMBER
Rounding options$355 → 29.5% · $360 → 30.4% ★ · $365 → 31.4%

C · Owner-operator doing the job solo. Owner time valued at $45/h

Labour hours1 × 3 h = 3 h
1 · Direct labour$135.00 (owner $135.00)
Subcontractors$0.00
2 · Other direct costs$20.00
Direct job cost$155.00
3 · Allocated overhead$13.59
Revenue ex GST$240.85
4 · GST$24.09
6 · Customer price$264.94
Gross profit · margin$85.85 · 35.6%
5 · Profit after OH · margin · markup$72.26 · 30.0% · 42.9%
Break-even (inc GST)$185.45
Revenue / profit per labour h$80.28 / $24.09
StatusGREEN
Rounding options$260 → 28.7% · $265 → 30.0% ★ · $270 → 31.3%

D · The markup trap: 30% markup vs 30% margin

Labour hours2 × 2.5 h = 5 h
1 · Direct labour$190.00
Subcontractors$0.00
2 · Other direct costs$15.00
Direct job cost$205.00
3 · Allocated overhead$0.00
Revenue ex GST$266.50
4 · GST$26.65
6 · Customer price$293.15
Gross profit · margin$61.50 · 23.1%
5 · Profit after OH · margin · markup$61.50 · 23.1% · 30.0%
Break-even (inc GST)$225.50
Revenue / profit per labour h$53.30 / $12.30
StatusAMBER
Rounding options$290 → 22.2% · $295 → 23.6% ★ · $300 → 24.8%
Cost $205.00 × 1.30 = $266.50 ex GST, a gross margin of only 23.1%. A true 30% margin needs $205.00 ÷ 0.70 = $292.86. That's a difference of $26.36 per job; on a fortnightly client that's $685 a year.

E · Bond clean with % of revenue overhead (12%) and $80 profit target

Labour hours2 × 5 h = 10 h
1 · Direct labour$380.00
Subcontractors$150.00
2 · Other direct costs$35.00
Direct job cost$565.00
3 · Allocated overhead$87.95
Revenue ex GST$732.95
4 · GST$73.30
6 · Customer price$806.25
Gross profit · margin$167.95 · 22.9%
5 · Profit after OH · margin · markup$80.00 · 10.9% · 12.3%
Break-even (inc GST)$706.26
Revenue / profit per labour h$73.30 / $8.00
StatusRED
Rounding options$805 → 10.8% · $810 → 11.3% ★ · $815 → 11.7%

F · Not GST registered

Labour hours1 × 3 h = 3 h
1 · Direct labour$114.00
Subcontractors$0.00
2 · Other direct costs$10.00
Direct job cost$124.00
3 · Allocated overhead$16.11
Revenue ex GST$200.16
4 · GSTn/a
6 · Customer price$200.16
Gross profit · margin$76.16 · 38.0%
5 · Profit after OH · margin · markup$60.05 · 30.0% · 42.9%
Break-even (inc GST)$140.11
Revenue / profit per labour h$66.72 / $20.02
StatusGREEN
Rounding options$200 → 29.9% · $205 → 31.7% ★ · $210 → 33.3%
—

AI assistant rules

  • Explanation first, AI second. The “Why this price?” paragraph is generated deterministically from engine output, so it can never be wrong. The AI only answers follow-up questions.
  • The AI prompt contains only the calculated figures (no customer name), and is told to use only those, never invent market or competitor prices or wage rates, and never give tax, legal or accounting advice.
  • “Polish message” may rephrase only. The result is rejected if the price string disappears; the leak-check still runs.
  • Every AI output is labelled and user-editable. Nothing is sent automatically. The API key never reaches the client — all model calls go through the server route.
—

Market price comparison

Stored with a confidence level, source and retrieval date, at Australia, state or city level. Displayed as a bar with a “You” marker. It never changes the calculated price. Current data status:

Job typeRegionRangeConfidenceSource
Bond / end-of-lease
per clean
Sydney (NSW)1br $369, 2br $419, 3br $517, 4br $677verifiedFantastic Cleaners Sydney published “from” prices, Sept 2026
Bond / end-of-lease
per clean
Melbourne (VIC)1br $305, 2br $345, 3br $440, 4br $585partialhipages article quoting Fantastic Cleaners Melbourne (2023 prices — likely higher now)
Bond / end-of-lease
per clean
Australia (indicative)1br $280–420, 2br $340–520, 3br $420–680, 4br $560–900draftWorking range for validation — to be confirmed with 30+ local price lists
Residential regular
per cleaner-hour
Australia (indicative)$45–75draftWorking range for validation — confirm via operator interviews & published hourly rates
Deep / spring clean
per clean (3-bed)
Australia (indicative)$350–650draftWorking range for validation
Airbnb / short-stay turnover
per turnover (1–2 bed, ex linen)
Australia (indicative)$90–180draftWorking range for validation
Commercial office
per cleaner-hour
Australia (indicative)$40–70draftWorking range for validation — contract pricing varies widely
Post-construction / builders clean
per m² (indicative)
Australia (indicative)$5–12draftWorking range for validation

Before launch: collect 30+ published price lists per capital city (bond, regular hourly, Airbnb turnover, commercial hourly, builders per m²) and record each source.

—

Job types and multipliers

Job typeIDTime multiplier
Residential regularregular1.0×
One-off cleanoneoff1.3×
Deep / spring cleandeep1.6×
Bond / end-of-leasebond2.1×
Airbnb / short stayairbnb0.9×
Commercial officecommercial1.0×
NDIS cleaningndis1.1×
Post-construction / new-buildconstruction2.6×
Customcustom1.0×