How the Hartley & Marsh Legal demo was made
A fictional suburban law firm in Mount Lawley, built as a conversion-focused landing page for one audience: households who want a will, a probate grant, or a first family-law conference — priced before they walk in. The page's single job is a booked initial appointment.
Counsel, not theatre
Most small-firm websites hide the price and hope the phone rings. This one argues the opposite: the process on one page, the price in the open, and a straight answer when you don't need a lawyer at all (JP certification is free at the library; Legal Aid WA on 1300 650 579 when money is tight). No scales of justice, no lion crests, no outcome promises anywhere — process and price only, which is both the honest register for legal marketing and the one that converts anxious first-time clients.
Parchment chambers daylight
Argued from the office, not a mood board: deed paper, the leather desk inlay, the brass door plaque, the bound law reports.
The spec's ink/line rule applied twice over: brass never sets body text on parchment (eyebrows and labels use the derived #7A5A20 brass-ink at 5.6:1), and on the counsel-green explorer panel the fee figures use a lightened #C9A45C brass-bright (5.3:1) while body copy runs in parchment tints.
Bookish authority, costs-agreement figures
- Libre Caslon Text — display and headings. Bookish authority with warmth; the italic carries the pull-quote ("The process on one page. The price in the open.").
- Source Sans 3 — body and UI. Clean, quietly professional, ≥16px on mobile.
- IBM Plex Mono — every fee figure, the eyebrows, phone numbers and the file-note stamp, set like lines from a costs agreement. The mono voice is the honesty device: whenever the page states money, it switches to the typeface of an invoice.
The Fixed-Fee Matter Explorer
Five matter chips (wills & estate planning, probate & estates, family-law first steps, employment advice, small-business agreements) drive a procedural SVG process timeline and a set of fee cards. The timeline is one dashed brass stroke over a muted track: selecting a matter re-draws it stage by stage via stroke-dashoffset, nodes lighting on a 180ms stagger; four HTML stage buttons (not SVG controls — cleaner keyboard access) jump the draw to any stage and swap the plain-language file note beneath. Per the W15 learning, the progress stroke uses butt caps and stays at opacity 0 until drawn length exceeds 1px, so no stray cap-dot ever renders at stage 1. When a matter reaches stage 4 of 4, a small brass wax-seal — "FIXED FEE · IN WRITING" — stamps onto the lead fee card, echoing the sealed deed in the hero illustration. The whole scene rests mid-demonstration: wills selected, timeline drawn to stage 2, the $440/$660/$850 cards showing, so the static thumbnail reads instantly. Every interaction updates window.__lawyer (matter, stage, displayed fees, seal, form status).
Conversion tie-in: choosing a matter (chip, "Book this matter", or any service-card link) pre-fills the appointment form's matter select — the demo interaction becomes a warmer lead. Deliberately, the form is not pre-filled at rest, so a fresh visitor's empty submit exercises all four required fields.
The fee model and its shape
Every figure on the page comes from one table, plausible for the 2026 Perth suburban market, and the explorer, services grid and FAQ all read from it:
| Matter | Fixed fee | Note |
|---|---|---|
| Simple will | $440 | single person |
| Couples' mirror wills | $660 | deliberately 1.5× the single fee, not 2× — one conference, two matching documents |
| Estate package (will + EPA + EPG) | $850 / $1,400 | single / couple — the couple rate again sub-2×, same shape |
| Probate (uncontested) | from $3,300 | + court filing fees — the Supreme Court sets filing fees; shown as disbursements, never marked up |
| Family-law first conference | $390 | the page's price floor — explorer's cheapest output equals the services grid's floor |
| Employment advice | from $420 | fixed quote before advice work |
| Business agreements | from $990 | fixed quote before drafting |
| Beyond fixed scopes | $420/hr | 6-minute units, itemised on every invoice |
Shape notes for QA: the couple pricing is deliberately sub-double (a real firm's shape — shared conference time), "from" prefixes mark scopes that legitimately vary, and court filing fees are hedged as an external authority's number rather than asserted — the Supreme Court of WA sets and revises them. No outcome language exists anywhere on the page; the only promises are process and price.
Why the page is shaped this way
- Above the fold at 375px: name + "fixed-fee … priced in the open" value line, Book CTA, and the Legal Practice Board registration chip — the three things an anxious first-time legal client checks before scrolling.
- Two CTAs, one intent: "Book an initial appointment" (primary) and "See fixed fees" (for the majority who need to trust the pricing before they'll book). The explorer is the persuasion engine; the form is the destination.
- Form fields: name, phone, suburb, matter select, optional note — the minimum a receptionist needs to book a conflict-checked first appointment. The note field is explicitly labelled "nothing here is legal advice yet". Confirmation renders locally and says so ("Demonstration form — nothing is sent"), phrased in the wave's confirmation lexicon.
- Mobile sticky bar: Call + Book, both ≥44px — a separating parent researching at midnight books; a probate executor calls.
- Honesty as conversion: the "When you don't need us" card (free JP certification, Legal Aid WA referral) costs two enquiries and earns fifty — it is the page's trust engine, not a loss.
Local SEO we'd implement for the real business
This demo ships only a WebPage schema naming itself a demonstration — fictional LocalBusiness/Review structured data in a real index is dishonest markup. A real firm would ship this (plus real reviews with consent, suburb landing content, and a Google Business Profile wired to the same NAP):
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "LegalService",
"name": "Hartley & Marsh Legal",
"telephone": "+61 8 XXXX XXXX",
"address": {
"@type": "PostalAddress",
"streetAddress": "Suite X, XXX Beaufort Street",
"addressLocality": "Mount Lawley",
"addressRegion": "WA", "postalCode": "6050", "addressCountry": "AU"
},
"areaServed": ["Mount Lawley","Highgate","North Perth","Inglewood",
"Menora","Bedford","Maylands","Bayswater","Perth"],
"openingHoursSpecification": [{
"@type": "OpeningHoursSpecification",
"dayOfWeek": ["Monday","Tuesday","Wednesday","Thursday","Friday"],
"opens": "08:30", "closes": "17:00"
}],
"priceRange": "$390–$3,300+",
"review": [{
"@type": "Review",
"author": {"@type": "Person", "name": "Real client, with consent"},
"reviewRating": {"@type": "Rating", "ratingValue": "5"},
"reviewBody": "Sourced from the firm's live review feed."
}]
}
</script>
What would change for a live firm
- Practice management: the form posts into LEAP, Smokeball or Actionstep intake with conflict-check fields; Settify for structured family-law intake; FamilyProperty for property matters.
- Searches & disbursements: InfoTrack integration so disbursements land itemised, matching the page's at-cost promise.
- Document automation: Josef or Checkbox driving the wills package from the intake data; Xero for the 6-minute-unit invoicing the copy promises.
- Content: real practitioner profiles and registration numbers, consented client reviews, and the firm's actual fee schedule replacing this demo's table.
Under the parchment
- Single self-contained file; Google Fonts the only external resource. No raster images: the chambers still-life (brass plaque, law reports, sealed deed, fountain pen), the Beaufort Street service-area map, and the timeline are all hand-built SVG; the plaque glint is a one-off Web Animations API sweep, motion-gated.
- Entrance choreography via IntersectionObserver on
.revealelements, gated ondocument.fonts.readywith a 1.2s fallback; without JS nothing is ever hidden (the hiding class is only applied underhtml.js). - Reduced motion: reveals render settled, the timeline draws instantly, fee-card swaps are immediate, the glint never runs.
- SVG map mirrored by a plain-text suburb list; the timeline mirrored by HTML stage buttons and a text file-note — no information lives only in a picture.
Three passes, driven headlessly
Every pass drove the page in headless Chrome (puppeteer) and asserted output — form submits on fresh loads, the explorer against an independently reimplemented fee oracle, screenshots reviewed by eye at 1280 and 375.
Pass 1 — Craft
- Found: at 375×667 the hero's trust chips sat below the fold (block bottom 761px). Tightened the mobile hero (padding 64→40px, h1 to 30px, margin trims) — CTA bottom now 448px, first trust chip 574px, both comfortably above the fold.
- Found (harness, not site): the page's
scroll-behavior:smoothdefeated the stepped scroll driver — smooth animation lagged the programmatic steps, so sections below ~5,300px never entered the viewport and their reveals stayed unfired in full-page screenshots. Driver switched to instant scrolling; real user scrolling was never affected. - Verified: rest state exactly per spec (wills, stage 2 of 4, $440/$660/$850 + $1,400 showing; dash draw at 213.3 of 640 units); zero console errors; one h1; screenshots reviewed end to end at both widths.
Pass 2 — Depth
- Added: the stage-4 wax-seal stamp ("FIXED FEE · IN WRITING") on the lead fee card — the second-read reward for driving a matter to completion, timed 650ms after the draw lands; asserted absent at rest, present and fully opaque after driving.
- Added: 70ms staggered entrance on fee-card swaps, replacing a flat fade.
- Verified against the oracle: displayed fees for all five matters matched an independently reimplemented spec table (family $390; probate from $3,300; employment from $420; business from $990; wills $440/$660/$850 + $1,400 couple); cheapest explorer output across all matters ($390) equals the services grid's floor; chips, stage buttons and FAQ also driven by keyboard.
Pass 3 — Hardening
- W15 dot guard asserted: at stage 1 the progress stroke computes to opacity 0 (butt caps, hidden until drawn length > 1px) — no stray cap-dot.
- 375px: scrollWidth 375; wordmark (20–188px) and header tel link (229–355px) fully in viewport; sticky bar fixed with a ≥44px tel target in the bottom 140px, tapped without error.
- Forms: empty submit on a fresh load flags all four required fields (name, phone, suburb, matter) with
aria-invalid+ visible errors; filled submit renders the local confirmation ("request received … we'd be in touch within one business day") echoing name and suburb. - Reduced motion: settled timeline visible, instant matter swap still renders oracle-correct fees, zero console errors. Honesty sweeps: every (08) 9xxx number on the page uses the reserved 9000 exchange; no LocalBusiness/Review/AggregateRating JSON-LD on the index.
- Chanel rule: removed the Guildford Rd label from the service-area map (the road line stays) — one fewer competing mark around the Maylands dot.