Generative Assets · Industry demo build notes

Hawthorn Cut Butchery — how it was made

The concept: a fictional whole-carcase butcher in Mount Hawthorn selling fixed-price meat packs, built for the household that's done with grey supermarket trays. The page has one job — a pack order, pickup or delivery, with the contents and total already settled before anyone picks up the phone. The signature is a Pack Builder drawn on a butcher's cut-map of the steer: pick a pack, watch the sections light and the paper parcels stack.

Palette — argued from the counter

Butcher's paper, the block, the cool room and the sage of twine. All ratios below were measured with WCAG arithmetic from the shipped tokens, not assumed (the spec's figures were hypotheses; measurement confirmed all of them, no two-token remedy needed).

#F8F0E3 · butcher paperpage ground — the wrap itself
#2A211E · smokehousebody ink — measured 13.9:1 on the ground
#8C2F2B · oxbloodCTAs & links — white labels 8.2:1, on ground 7.3:1
#8FA98B · sagetwine & cut-map linework — lines and marks only
#4E6B4A · sage-inksage-family small copy — measured 5.3:1 on the ground

Type

Alfa Slab One for display — the slab a signwriter paints on a butcher's window, used for the wordmark, headings, prices and the pack-weight line ("8.7 KILOS, WRAPPED IN PAPER"). Source Sans 3 carries the body: legible at 16px+ on a phone in one hand, which is where trade traffic lives. The typographic bravery is the weight line — the biggest thing on the page after the h1 is a number of kilograms, because that's what a pack customer is actually buying.

Techniques

Honest numbers — the pricing contract

Every figure is pinned arithmetic from the pack anchors, verified in QA against an independently reimplemented oracle. The add-on arithmetic is the services-grid per-kilo pricing: extra mince +$18/kg, wings $24 for 2 kg = $12/kg, biltong $32 for 500 g = $64/kg — asserted, not coincidental.

ScenarioArithmeticTotal
Rest — Family pack, pickup185$185
Family + delivery185 ≥ 160 → free$185 · "free delivery applied"
Weeknight + delivery110 + 10$120 · "free from $160" note
Boundary — Weeknight + 1 kg mince + biltong + delivery110+18+32 = 160 → free$160 (exact threshold)
Nearest below — Weeknight + 2 kg mince + free-range + delivery158 + 10$168 + note
Freezer-filler + 4 kg mince + delivery422 → free$422
Cheapest output — Weeknight, pickup110$110 = grid floor

Conversion decisions

Local SEO we'd implement for the real business

This demo ships only WebPage/CreativeWork/BreadcrumbList schema, because fictional structured data in a real index is dishonest markup. For a real Hawthorn Cut we'd ship the block below. Schema.org has no butcher subtype — no ButcherShop exists — so the honest top-level type is GroceryStore, the nearest food-retail parent, sharpened with a precise description and keywords rather than a made-up type.

<script type="application/ld+json">
{
  "@context": "https://schema.org",
  "@type": "GroceryStore",
  "name": "Hawthorn Cut Butchery",
  "description": "Whole-carcase butcher shop. Beef broken down by hand on site; meat packs, house sausages and dry-aged steak.",
  "keywords": "butcher, meat packs, whole carcase, dry aged beef",
  "url": "https://hawthorncut.com.au",
  "telephone": "+61 8 XXXX XXXX",
  "priceRange": "$$",
  "address": {
    "@type": "PostalAddress",
    "addressLocality": "Mount Hawthorn",
    "addressRegion": "WA",
    "postalCode": "6016",
    "addressCountry": "AU"
  },
  "geo": { "@type": "GeoCoordinates", "latitude": -31.92, "longitude": 115.83 },
  "openingHoursSpecification": [
    { "@type": "OpeningHoursSpecification",
      "dayOfWeek": ["Tuesday","Wednesday","Thursday","Friday"],
      "opens": "08:00", "closes": "17:30" },
    { "@type": "OpeningHoursSpecification",
      "dayOfWeek": "Saturday", "opens": "07:00", "closes": "13:00" }
  ],
  "hasOfferCatalog": {
    "@type": "OfferCatalog",
    "name": "Meat packs",
    "itemListElement": [
      { "@type": "Offer", "name": "Weeknight pack", "price": "110", "priceCurrency": "AUD" },
      { "@type": "Offer", "name": "Family pack", "price": "185", "priceCurrency": "AUD" },
      { "@type": "Offer", "name": "Freezer-filler", "price": "350", "priceCurrency": "AUD" }
    ]
  },
  "aggregateRating": { "@type": "AggregateRating", "ratingValue": "4.9", "reviewCount": "180" },
  "review": [{
    "@type": "Review",
    "author": { "@type": "Person", "name": "(real customer)" },
    "reviewRating": { "@type": "Rating", "ratingValue": "5" },
    "reviewBody": "(pulled from the live Google Business Profile feed)"
  }]
}
</script>

Beyond markup: a Google Business Profile with weekly photo posts of the actual cabinet (butchers win local search on photos), suburb-level service pages only if delivery genuinely differs by suburb, and review acquisition wired to the counter — a QR on the wrap.

What would change for the real client

Iteration log

Pass 1 — Craft

Measured every contrast hypothesis with WCAG arithmetic: smokehouse ink 13.9:1, oxblood on ground 7.3:1, white on oxblood 8.2:1, sage-ink 5.3:1 — all clear, sage itself kept to lines and twine only. Removed a duplicated counter rect with a corrupted stroke attribute caught on code re-read. Tightened the ledger's dashed rules and gave the docket its own ink-dark card so the total reads like a handwritten chit. Rewrote three service-card blurbs that had drifted into filler ("quality cuts daily") toward counter language ("they rarely make it home intact").

Pass 2 — Depth

Added the chook standing by the counter with its "chooks from the next shed" tag — it re-labels to "free-range chooks, as asked" when the upgrade is on, so the toggle answers in the scene, not just the docket. Gave parcels a per-parcel ±2° rotation off a hash so the stack reads hand-stacked, and phased the entrance stagger against the snappy redraw (110 ms/parcel first draw, 34 ms after). The scene caption ("loin and rib stay on the counter") turns the two permanently-unlit sections from an oversight into the shop's actual argument.

Pass 3 — Hardening

The thumbnail probe measured the price ticket at y=804 against the 750px fold at 1200 wide — compacted the hero and builder rhythm and lifted the ticket in the scene; it now lands at ~706 with the steer. The native-resolution label-collision sweep caught four real defects no assert saw: the twine line striking through every one-line parcel label (fixed with drawn hand-label plates), the chook's tag hidden behind the bottom-left parcel in 4-wide stacks (the chook now stands under the counter lip), the vertical SHANK label buried behind top-row parcels (now horizontal, above the stack), and the van wordmark clipping its own cab. Masthead checked first at 375: wordmark right edge clears the compacted call button; the ticket's tight variant drops its subtitle below 600px and parcel labels step up to 15.5px. Re-ran the full suite after the fixes — 120/120: exact $160 boundary and $158 below-cell against the independent oracle, every control plus two rapid-fire rounds, zero console errors, reduced-motion static glint. Chanel rule: deleted a planned sawdust-speckle counter texture — the scene carries without it.