Canning Linen Co.

Generative Assets · Industry Demos · how it was made

How the Canning Linen Co. demo was built

Canning Linen Co. is a fictional Welshpool commercial laundry — linen hire and wash-and-return for Perth hotels, restaurants, aged care homes, clinics and gyms. The page exists for one reader: the operations manager with a linen problem who wants the week priced honestly, to the whole dollar, before they ring. Its single job is a weekly-service enquiry with the business, the volume, the changes, the delivery days and the kitchen linen already attached.

The Linen Run

The signature is a drawn SVG loading dock in elevation: a roller door with steam drifting across the bay, cage trolleys stacked with folded linen beside a one-metre cage mark, a van backed on with its beacon breathing, a week strip of seven day ticks along the dock edge, and the week's van runs queued in the yard lane. Every control redraws the dock honestly and reprices the week from the pinned anchors.

Pinned anchors (GST inclusive; every figure is per week)

BusinessUnitStepperHireWash onlyKitchen linenFolded height
Hotel & motelroom5–80, step 5$9$5yes30 mm
Restaurant & functioncover20–300, step 10$2$1yes4 mm
Aged care & disability accommodationbed5–60, step 5$11$6yes30 mm
Physio & medical clinictreatment room2–20, step 1$12$7no20 mm
Gym & studiopack of 25 towels1–20, step 1$16$9no100 mm

Rates are dollars per unit, per change. Chef whites and aprons are a flat +$40 a week. Delivery is 2 days included, 3 days +$25, 6 days +$60. Changes run 1–6 a week.

The minimum, and which lines sit inside it. The $150 weekly minimum applies to the linen subtotal — units × changes × rate plus kitchen linen. The delivery surcharge sits outside it and is added afterwards. So the week is max(linen subtotal, 150) + delivery surcharge. The rate multiplies the actual volume and changes, never a billed minimum; kitchen linen is flat and inside; delivery is flat and outside. The exact boundary is reachable on the pinned grids: hotel & motel, wash only, 30 rooms, one change is 30 × 1 × $5 = $150 exactly, and it prints the "exactly at the $150 weekly minimum" note rather than the shortfall template. There is no empty state — a business chip, a mode and a delivery chip are always selected, and every stepper has a floor. Every rate is a whole dollar and every stepper grid is whole units, so no total ever needs rounding.

Changes can never outrun delivery days

Raising changes above the selected delivery days forces delivery up to the smallest option that covers it (3 changes → 3 days; 4–6 changes → 6 days) and remembers the previous choice. Every delivery option with fewer days than the current changes is disabled with the reason on the page — "6 changes a week needs 6 delivery days". Lowering changes restores the remembered option only when it becomes valid again; on a partial unwind — the constraint relaxes but the remembered value is still too small — the forced value is held, never stepped down through intermediates. The memory is cleared by any explicit delivery-chip change while the force is active. Kitchen linen behaves the same way in the other direction: the toggle is disabled with its reason on the clinic and gym chips, its value is remembered, and it is restored the moment a hospitality business is chipped again.

Worked examples, measured from the running page

ScenarioArithmeticWeekMinimum noteCages
Rest state — hotel, 25 rooms, hire, 3 changes, 3-day, whites off25 × 3 × 9 = 675, + 25 delivery$700absent1
Cheapest output — restaurant, 20 covers, wash only, 1 change, 2-day20 × 1 × 1 = 20 → minimum 150, + 0$150"…your linen comes to $20"1
Exact boundary — hotel, 30 rooms, wash only, 1 change, 2-day30 × 1 × 5 = 150, + 0$150"exactly at the $150 weekly minimum"1
Hotel, 25 rooms, wash only, 1 change, 2-day125 → minimum 150, + 0$150"…your linen comes to $125"1
Restaurant, 120 covers, hire, 6 changes, 6-day, whites on1,440 + 40 = 1,480, + 60$1,540absent2
Aged care, 40 beds, hire, 3 changes, 3-day, whites on1,320 + 40 = 1,360, + 25$1,385absent2
Clinic, 6 treatment rooms, hire, 2 changes, 2-day144 → minimum 150, + 0$150"…your linen comes to $144"; whites disabled1
Gym, 8 towel packs, hire, 6 changes, 6-day768, + 60$828absent2
Hotel, 80 rooms, hire, 6 changes, 6-day, whites on4,320 + 40 = 4,360, + 60$4,420absent6
Hotel, 25 rooms, wash only, 3 changes, 3-day375, + 25$400absent1
Force — hotel 25, hire, 1 change, 2-day → changes 6225 → forced to 6-day: 1,350 + 60$225 → $1,410absent; 2-day and 3-day disabled1 → 6
Partial unwind — changes 6 → 3 (remembered 2-day still invalid)holds 6-day: 675 + 60$735absent; 3-day re-enabled3
Restore — changes 3 → 2 (remembered 2-day valid again)450 + 0$450absent; memory cleared2
Memory invalidation — forced to 6-day, lower to 3, chip 3-day explicitly, changes → 1stays on 3-day: 225 + 25$250absent1
Rapid fire — gym → clinic → restaurant → hotel in one evaluate, volumes at each floorhotel floor 5 × 3 × 9 = 135 → minimum 150, + 25$175"…your linen comes to $135"1

Every figure above, the grid floor ($150 a week, which is the Linen Run's cheapest output and the services grid's stated floor), the three minimum states (below / exactly / above), the force and both unwind behaviours, and 40 random control vectors were checked against an oracle reimplemented from the pinned anchors in the verification harness — not from the page's own code.

Palette — argued from a folding table under high-bay light

Folded-linen white for the ground, press-steel for the ink and the primary CTA, a real laundry-bluing blue for every mark and rule, and unbleached linen sand that lives only in the drawn stacks, the strapping and the kraft docket. Ratios below were computed with WCAG arithmetic from the shipped tokens during Pass 1 — role, hex and ratio from the same script row.

Folded linen#F4F4F1 page ground; cards #FFFFFF, soft cards #FAFAF8
Press steel#232B31 body ink · 13.04:1 on the ground, 14.37:1 on white, 13.75:1 on soft cards, 11.49:1 on kraft. Primary CTA fill, white labels 14.37:1 (hover #171D22, 17.00:1)
Laundry blue#1E5FA8 day ticks, cage frames, rules, chip outlines · secondary CTA fill, white labels 6.45:1 (hover #184E8A, 8.41:1). As text on the ground 5.85:1 — in the 4–6 band, so small blue copy uses the pre-minted ink below
Blue ink#17518F links, the docket's service line, prices in the grid · 7.30:1 on the ground, 8.05:1 on white, 6.43:1 on kraft, 6.75:1 on the blue tint
Linen sand#D8C7A5 stack edges, strapping, the docket cord · 1.51:1 on the ground — fill and lines only, never text
Sand ink#7A6435 the minimum note, lock reasons, the docket's rules · 5.15:1 on the ground, 5.67:1 on white, 4.54:1 on kraft
Sub ink#5A656D hints, eyebrows, unlit day letters, "not selected" ledger lines · 5.42:1 on the ground, 5.97:1 on white, 5.71:1 on soft cards, 4.77:1 on kraft, 5.01:1 on the blue tint
Kraft#EFE5D0 the docket and its stub · steel ink on it 11.49:1, blue ink 6.43:1, sand ink 4.54:1
Map water#DCE7EF the Indian Ocean and the Swan · steel labels on it 11.44:1 with a water-coloured halo

Every spec figure measured exactly on the shipped tokens — steel 13.04 / 14.37, white-on-blue 6.45 and 8.41 on hover, blue-on-ground 5.85, blue ink 7.30, sand 1.51 (fill only), sand ink 5.15 — so no token moved. The sub ink, the kraft and the map water were minted by the builder and measured before use; the footer's light ink #D6DCE0 reads 10.38:1 on steel and its quiet variant #AEB8BF 7.12:1. Map labels are body ink with a ground-coloured halo (paint-order: stroke) wherever they cross a fill. Inside the open door — a dark bay — the remedy runs the other way: the one-metre cage mark, the bay reading and the ruler are drawn in white on the door's #2E3840 so they hold their contrast where the steel ink could not.

Type

$700 a week · Hotel & motel · 25 rooms · 3 changes
Familjen Grotesk 700 — squared and slightly condensed, it folds like linen: headings, the display line over the dock, the docket's figure, stepper values.
Sheets, towels, table linen and chef whites go out on the truck and come back folded, counted and on the day you said.
Karla 400–700 — the workhorse: 17 px body (16 px minimum in form fields on phones), labels, ledger, FAQ. Eyebrows in 12 px caps at .17 em tracking.

The display moment is the rest-state line set over the dock — "$700 A WEEK · HOTEL & MOTEL · 25 ROOMS · 3 CHANGES" — rebuilt from state on every change, so the figure is always the live total and the rest-state figure is the measured $700. Its tokens are joined with non-breaking spaces so a wrap never strands a dot or a unit at a line end.

Techniques

Conversion decisions

Local SEO we'd implement for the real business

The live page carries only WebPage, WebSite and BreadcrumbList structured data naming it a demonstration — a fictional laundry must not mark itself up as a real one, and the reviews on the page are demonstration copy that is deliberately not marked up at all. For the real client we would ship the block below. Schema.org has an exact organisation type for this trade — DryCleaningOrLaundry, a LocalBusiness subtype — so there is no need to reach for a generic ProfessionalService. Real reviews would be pulled from the Google Business Profile feed and marked up only with the reviewer's consent.

<script type="application/ld+json">
{
  "@context": "https://schema.org",
  "@type": "DryCleaningOrLaundry",
  "name": "Canning Linen Co.",
  "description": "Commercial laundry and linen hire in Welshpool — sheets, towels, table linen and chef whites for Perth hotels, restaurants, aged care homes, clinics and gyms.",
  "url": "https://www.example-canninglinen.com.au/",
  "telephone": "+61 8 9000 0373",
  "image": "https://www.example-canninglinen.com.au/dock.jpg",
  "priceRange": "$$",
  "address": {
    "@type": "PostalAddress",
    "addressLocality": "Welshpool",
    "addressRegion": "WA",
    "postalCode": "6106",
    "addressCountry": "AU"
  },
  "geo": { "@type": "GeoCoordinates", "latitude": -31.99, "longitude": 115.94 },
  "areaServed": ["Perth", "Northbridge", "East Perth", "Subiaco", "Osborne Park", "Cannington", "Fremantle", "Joondalup"],
  "openingHoursSpecification": [
    { "@type": "OpeningHoursSpecification", "dayOfWeek": ["Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"], "opens": "04:00", "closes": "18:00" }
  ],
  "knowsAbout": ["commercial laundry", "linen hire", "hotel linen", "table linen", "aged care laundry", "thermal disinfection", "chef whites", "gym towel service"],
  "hasOfferCatalog": {
    "@type": "OfferCatalog",
    "name": "Weekly linen services",
    "itemListElement": [
      { "@type": "Offer", "itemOffered": { "@type": "Service", "name": "Hotel and motel linen hire" }, "priceSpecification": { "@type": "UnitPriceSpecification", "price": "9", "priceCurrency": "AUD", "unitText": "per room, per change" } },
      { "@type": "Offer", "itemOffered": { "@type": "Service", "name": "Restaurant and function table linen" }, "priceSpecification": { "@type": "UnitPriceSpecification", "price": "2", "priceCurrency": "AUD", "unitText": "per cover, per change" } },
      { "@type": "Offer", "itemOffered": { "@type": "Service", "name": "Aged care and disability accommodation linen" }, "priceSpecification": { "@type": "UnitPriceSpecification", "price": "11", "priceCurrency": "AUD", "unitText": "per bed, per change" } },
      { "@type": "Offer", "itemOffered": { "@type": "Service", "name": "Gym and studio towel packs" }, "priceSpecification": { "@type": "UnitPriceSpecification", "price": "16", "priceCurrency": "AUD", "unitText": "per pack of 25, per change" } }
    ]
  },
  "aggregateRating": { "@type": "AggregateRating", "ratingValue": "4.8", "reviewCount": "57" },
  "review": [
    {
      "@type": "Review",
      "author": { "@type": "Person", "name": "Real customer, with consent" },
      "datePublished": "2026-06-14",
      "reviewRating": { "@type": "Rating", "ratingValue": "5" },
      "reviewBody": "Pulled from the live Google Business Profile feed — never hand-written."
    }
  ]
}
</script>

Alongside the schema: a Google Business Profile with the Welshpool plant pinned and the service area drawn, "commercial laundry Perth" / "linen hire Perth" / "hotel linen service Perth" / "aged care laundry Perth" as the page's core phrases with the suburbs above as secondary, NAP consistency across the hospitality and aged-care supplier directories, and a reviews feed that updates without anyone touching the page.

What changes for the real client

Iteration log

Two builder calls worth flagging. (1) Cages are the builder's mechanism, not the spec's: the concept pinned a proportional stack between a 40-unit floor and a 300-unit ceiling with a constant one-metre mark; over a 360:1 range those three cannot all hold in one column, so the 300-unit ceiling was read as one cage trolley's 2.5 m fill line and the overflow drawn as further cages. The proportional model, the floor, the ceiling and the constant mark are all shipped as pinned; the cage count is the honest way the big states stay truthful, and its drawn cap of six is the range's own ceiling. (2) Volume resets to the chip's floor on a business swap rather than being remembered per business — the five grids share no values, so a remembered 25 rooms has no meaning on a 20–300 covers grid. This matches the rapid-fire example pinned in the concept, and it is stated on this page so it is not read as a lost value.