# Examples

Every complete plan in the repository's
[`examples/`](https://github.com/chanmeng666/archlang/tree/main/examples) directory,
**live and editable** — edit the source on the left and the SVG on the right recompiles
instantly (client-side, deterministic, no server). Hit **Open in Playground** to keep
going with the full editor.

They are ordered as a tour rather than an inventory: a plan you can read in one screen
first, then houses, then the large public buildings, then the drawings that exist to
prove one language feature. Every figure quoted below comes from
[`archlang describe --json`](/analysis) on the file itself — and every warning from
`archlang lint` / `archlang validate` on the same file — so the prose and the drawing cannot
disagree.

Two things worth knowing before you start. A plan written as
[`level` blocks](/reference#levels-a-multi-storey-building-v1-21) compiles to **one
drawing per storey**, and a live widget shows **page 1** — the lowest storey, which is
what `compile().svg` returns; the whole set is in `compile().pages`, and
`archlang describe --level 2 --json` reads a specific floor. And the playground's **theme
selector restyles any plan** — blueprint, dark, mono, presentation — without touching a
line of its source, because a theme changes inks and hatches, never geometry.

## The whole language on one sheet

### Hillside Villa

The showpiece: a two-storey villa with an attached garage, drawn on one A2 sheet at
1:50. The widget below shows **page 1 — the ground floor**, **11 rooms, 196.54 m²**
(`archlang describe --json`); the upper floor adds nine more rooms over 140.76 m²
(`archlang describe --level 2 --json`).

It is the plan to read when you want to see how the surfaces above fit together rather
than in isolation: `site { street north }` names the facades a plot orientation lint
rule can reason about; a `room … polygon` chamfers a reading nook off the study and an
L-shaped master suite wraps its own ensuite; an `arc` wall edge bows a bay window off the
living room; all six door kinds ship somewhere a builder would actually put one — hinged,
sliding, pocket, bifold, barn and the `garage` door on the wing; a `stair` carries the
same id on both `level` blocks, so it is one shaft, not two symbols that happen to line
up; a `void` opens a double-height gap over the living room; `roof overhang` draws the
eaves on the upper storey; and a `component` authored once — `ensuite() { … }` — is
`place`d three times: twice on the bedroom band, the second with `mirror x` so the pair
face opposite ways, and once more inside the master suite.

`archlang lint` raises seven warnings on it, all on the upper floor. Two are left in on
purpose, as the honest cost of a real site: two bedrooms whose windows don't face the
equator side. (The master en-suite, reached only through its bedroom, is not one: the
other two en-suites also open off the landing.) The other five squeeze to
640 mm, each at a real 700 mm door, and all five are walks from the top of the stair, the
floor's only way in: each bed stands across its own bedroom's door, so bedrooms 2 and 3 are
walked into through their en-suites, and every en-suite is entered by a 700 mm door, which
the warning names. The Master Suite's route to its own en-suite passes through that
en-suite's one door, already named, so it is not reported twice; the walk to the Master
Suite itself is 700 mm at its narrowest, which meets the minimum. Every room is reachable.

![hillside-villa.arch, compiled](/examples/hillside-villa.svg)

```arch
# The SHOWPIECE flagship — a two-storey hillside villa with an attached garage, the plan a
# first-time visitor to the language should see. Everything else in examples/ is one
# thing done well; this one is the whole language on one sheet. Surfaces it exercises:
# `site` (five derived compass names) · `paper`+`scale`+`axes`+`schedule`+`legend` (a real
# sheet with a title block) · `zone` (living/service/sleeping wings, rolled up in the
# schedule subtotals) · `room polygon` (a chamfered nook off the study, and the L-shaped
# master suite) · `arc` wall edge (a bowed bay off the living room) · all SIX door kinds —
# the five that have to be named (sliding/pocket/bifold/barn/garage), plus the `hinged`
# default on the rest, each where a builder would actually put it. Count them off the
# `door` lines and you get five, because `hinged` is the kind you never write: ask
# `arch describe --json` instead, which reports the kind on every door · `window
# on <wall> at <pos>` plus a `for` loop (a street-facing ribbon of study windows) ·
# `level 1`/`level 2` · `stair` (same id both levels, one shaft) · `void` (a double-height
# gap over the living room) · `roof overhang` (the eaves, drawn on the upper storey) ·
# `component` + `place … mirror x` (a mirrored pair of ensuite bathrooms) · ~30 furniture
# families across kitchen/living/bedroom/study/garage/terrace · `dims auto all` plus one
# manual dim.
#
# The plot fronts the street on the NORTH, in the northern hemisphere — so `back` and
# `equator_side` are the SAME side (south), which is why the living room, the arc bay and
# every bedroom on the garden band take the south facade, and the garage/service band
# stacks along the street. `site` draws nothing; it only names directions.
plan "Hillside Villa" {
  units mm
  grid 100
  paper A2 landscape
  scale 1:50
  north up
  dims auto all
  schedule rooms
  legend

  site { street north hemisphere north }

  # A mirrored PAIR of ensuite bathrooms, authored once. `place ensuite() as <name> at
  # (x,y) [mirror x]` instantiates it as an addressable box (its own wall + room + three
  # wet fixtures); the parent cuts each suite's own door by a dotted reference into the
  # instance's wall (`en2.wall`, `en3.wall`) — the same composition contract as
  # `examples/museum-wings.arch`. No door is drawn INSIDE the component on purpose: which
  # bedroom it opens into is the parent's decision, exactly like the wing's hall door.
  component ensuite() {
    wall id=wall partition thickness 100 { (0,0) (1800,0) (1800,2600) (0,2600) close }
    room id=room at (0,0) size 1800x2600 label "Ensuite" uses bath
    # `inset 150`, not `flush`, on the two NORTH fixtures: every placement backs that edge
    # onto a THICKER host wall (`shell` 250 mm for en2/en3, `w_l2_band` 200 mm for en_m)
    # running along the same line as this component's own 100 mm wall — `flush` reads the
    # nearer face and collides with the far one. The shower goes top-RIGHT: every
    # instance's bedroom door lands on the LEFT edge (`at 7500` below), and a shower there
    # would leave it no swing room.
    furniture shower in room anchor top-right   inset 150 size 900x900
    furniture basin  in room anchor top-left    inset 150 size 600x450
    furniture wc     in room anchor bottom-left flush     size 400x700
  }

  # =====================================================================================
  # LEVEL 1 — entry level: garage, study, living/dining/kitchen, terrace
  # =====================================================================================
  level 1 "Ground floor" {
    # ---- shell ------------------------------------------------------------------------
    # One closed ring for the main volume, with a bowed bay let into the south (garden)
    # wall behind the living room — a curved arc EDGE, not a separate `room circle`,
    # walked from (4400,10200) to (1600,10200) with `cw` so it bulges OUTWARD.
    wall id=shell exterior thickness 250 {
      (0,0) (13800,0)
      (13800,10200)
      (4400,10200)
      arc (1600,10200) radius 2000 cw
      (0,10200)
      close
    }

    # The garage/utility wing: three sides only (its fourth side is the main shell's own
    # west face, so nothing is drawn twice).
    wall id=w_wing_n exterior  thickness 250 { (-5500,0) (0,0) }
    wall id=w_wing_w exterior  thickness 250 { (-5500,0) (-5500,10200) }
    wall id=w_wing_s exterior  thickness 250 { (-5500,10200) (0,10200) }
    wall id=w_util   partition thickness 150 { (-5500,6200) (0,6200) }

    # Front-band partitions (street side).
    wall id=w_office_e   partition thickness 100 { (4000,0) (4000,4200) }
    wall id=w_powder_w   partition thickness 100 { (8000,0) (8000,2200) }
    wall id=w_powder_e   partition thickness 100 { (10000,0) (10000,2200) }
    wall id=w_frontsvc_s partition thickness 100 { (8000,2200) (13800,2200) }
    wall id=w_entry_svc  partition thickness 100 { (8000,2200) (8000,4200) }

    # Front/back band spine, and the garden-band partitions.
    wall id=w_l1_band_w partition thickness 200 { (0,4200) (8000,4200) }
    wall id=w_l1_band_e partition thickness 200 { (8000,4200) (13800,4200) }
    wall id=w_liv_din   partition thickness 150 { (4800,4200) (4800,10200) }
    wall id=w_din_kit   partition thickness 100 { (8000,4200) (8000,8200) }
    wall id=w_din_terr  partition thickness 150 { (8000,8200) (8000,10200) }
    wall id=w_kit_terr  exterior  thickness 200 { (8000,8200) (13800,8200) }

    # ---- rooms, grouped into wings by `zone` -------------------------------------------
    zone service "Service wing" {
      room id=r_garage  at (-5500,0)    size 5500x6200 label "Garage"  uses garage
      room id=r_utility at (-5500,6200) size 5500x4000 label "Utility" uses utility
      room id=r_powder  at (8000,0)     size 2000x2200 label "Powder"  uses wc
      room id=r_pantry  at (10000,0)    size 3800x2200 label "Pantry"  uses storage
    }

    zone living "Living wing" {
      # The study: a rectangle with its SE corner chamfered into a reading nook, an
      # ANGLED room via `room polygon` — an exact shoelace area, label at the centroid.
      room id=r_office polygon
        (0,0) (4000,0) (4000,3400) (3200,4200) (0,4200)
        label "Study" uses office
      room id=r_entry   at (4000,0)    size 4000x4200 label "Entry"          uses entry hall
      room id=r_living  at (0,4200)    size 4800x6000 label "Living"         uses living
      room id=r_dining  at (4800,4200) size 3200x6000 label "Dining"         uses dining
      room id=r_kitchen at (8000,4200) size 5800x4000 label "Kitchen"        uses kitchen
      room id=r_terrace at (8000,8200) size 5800x2000 label "Terrace"
      # Behind Powder/Pantry, reached from Entry via `o_entry_svc` — gives their doors a
      # real room on the other side (a door onto bare, room-less floor never registers in
      # the access graph, even though it looks walkable on the sheet).
      room id=r_service at (8000,2200) size 5800x2000 label "Service Hall" uses hall circulation
    }

    # ---- doors: one kind per situation -------------------------------------------------
    door id=d_front         at (6000,0) width 1200 wall shell hinge left swing into r_entry
    door id=d_office        at (4000,2000) width 900 wall w_office_e hinge left swing into r_office
    door id=d_garage        garage at (-2750,0) width 3000 wall w_wing_n
    door id=d_garage_int    at (0,2000) width 900 wall shell hinge right swing into r_office
    door id=d_garage_util   bifold on w_util at 2750 width 1500 swing in
    door id=d_utility_ext   at (-2750,10200) width 900 wall w_wing_s hinge right swing out
    door id=d_powder        pocket on w_frontsvc_s at 1000 width 800 slide right
    door id=d_pantry        barn on w_frontsvc_s at 3900 width 900 swing in
    door id=d_kit_corr      on w_l1_band_e at 2900 width 1200 hinge right swing into r_kitchen
    door id=d_din_kit       on w_din_kit at 2000 width 1000 swing into r_kitchen
    door id=d_din_terrace   at (8000,9200) width 900 wall w_din_terr hinge left swing into r_terrace
    door id=d_kit_terrace   sliding on w_kit_terr at 3000 width 2400 slide left
    door id=d_living_garden at (800,10200) width 1000 wall shell hinge left swing out

    opening id=o_liv_din on w_liv_din at 2000 width 3000
    opening id=o_entry_svc on w_entry_svc at 1000 width 1800
    opening id=o_entry_dining on w_l1_band_w at 5500 width 2400

    # ---- windows: the ribbon of study windows is a `for` loop --------------------------
    for i in 0..3 {
      window on shell at (500 + i * 1100) width 700
    }
    window on shell at 11800 width 900                # pantry
    window on shell at 19000 width 1200                # kitchen (east wall)
    window on shell at 21000 width 1200                # kitchen (east wall)
    window on shell at 23000 width 1400                # terrace (east wall)
    window on shell at 17000 width 900                 # corridor (east wall)
    window at (6000,10200) width 1800 wall shell        # dining, south wall

    # ---- furniture ----------------------------------------------------------------------
    furniture desk         at (600,2700)  size 1600x700 in r_office
    furniture office_chair at (2300,2900) size 600x600  in r_office
    furniture bookshelf    at (200,3700)  size 1600x300 in r_office

    furniture rug          at (600,5000)  size 3000x2400 in r_living
    furniture sofa_l        at (700,5600)  size 2400x1500 in r_living
    furniture coffee_table  at (1300,7200) size 1000x500  in r_living
    furniture armchair      at (3200,5750) size 900x850  rotate 270 in r_living
    furniture plant         at (300,4400)  size 600x600  in r_living
    furniture piano          at (3000,8700) size 1600x1200 rotate 270 in r_living
    furniture tv_unit against wall w_liv_din offset 3000 in r_living

    furniture dining_table at (5600,6800) size 2000x1000 in r_dining

    furniture counter        against wall shell segment 1 offset 4900 size 1000x600 in r_kitchen
    furniture kitchen_sink   against wall shell segment 1 offset 5800 in r_kitchen
    furniture stove          against wall shell segment 1 offset 6500 in r_kitchen
    furniture dishwasher     against wall shell segment 1 offset 7100 in r_kitchen
    furniture fridge         against wall shell segment 1 offset 7700 in r_kitchen
    furniture upper_cabinet  against wall w_l1_band_e offset 4500 in r_kitchen
    furniture island         at (9200,5400) size 1800x900 in r_kitchen
    furniture barstool       at (8900,6500) size 400x400  in r_kitchen
    furniture barstool       at (9500,6500) size 400x400  in r_kitchen
    furniture barstool       at (10100,6500) size 400x400 in r_kitchen

    furniture washer against wall w_wing_w offset 6800 in r_utility
    furniture dryer  against wall w_wing_w offset 7400 in r_utility

    furniture wc    in r_powder anchor bottom-left flush size 400x700
    furniture basin in r_powder anchor top-right   flush size 600x450

    furniture car at (-4800,700) size 1800x4600 in r_garage
    furniture car at (-2300,700) size 1800x4600 in r_garage

    furniture sun_lounger at (9000,8700)  size 1900x700 in r_terrace
    furniture sun_lounger at (11200,8700) size 1900x700 in r_terrace

    dim (8000,10500)->(13800,10500) offset 600 text "Terrace 5.8 x 2.0 m"

    stair id=stair at (4900,2700) size 2200x1000 dir up
  }

  # =====================================================================================
  # LEVEL 2 — sleeping level: two secondary suites, the master suite, and a gallery
  # over the double-height living room
  # =====================================================================================
  level 2 "Upper floor" {
    # ---- shell --------------------------------------------------------------------------
    # No garage below this footprint and no bay above it: a plain closed rectangle, so its
    # `roof overhang` below is a simple mitred offset with nothing curved to refuse.
    wall id=shell exterior thickness 250 { (0,0) (13800,0) (13800,10200) (0,10200) close }

    # ---- partitions -----------------------------------------------------------------------
    wall id=w_bed2_s  partition thickness 100 { (0,2600) (4000,2600) }
    wall id=w_linen_s partition thickness 100 { (5800,2600) (8000,2600) }
    wall id=w_bed3_s  partition thickness 100 { (9800,2600) (13800,2600) }
    wall id=w_l2_band partition thickness 200 { (0,4200) (13800,4200) }
    wall id=w_gallery_e partition thickness 200 { (4800,4200) (4800,10200) }

    zone sleeping "Sleeping wing" {
      room id=r_bed2   at (0,0)      size 4000x2600 label "Bedroom 2" uses bedroom
      room id=r_linen  at (5800,0)   size 2200x2600 label "Linen"     uses storage
      room id=r_bed3   at (9800,0)   size 4000x2600 label "Bedroom 3" uses bedroom
      place ensuite() as en2 at (4000,0)
      place ensuite() as en3 at (9800,0) mirror x

      # The master suite: an L-SHAPED `room polygon` wrapping its own ensuite, which sits
      # in the notch (the third, unmirrored placement of the same component) rather than
      # being carved from a plain rectangle.
      room id=r_master polygon
        (4800,4200) (12000,4200) (12000,6800) (13800,6800) (13800,10200) (4800,10200)
        label "Master Suite" at (7000,7500) uses bedroom
      place ensuite() as en_m at (12000,4200)
    }

    room id=r_landing at (0,2600) size 13800x1600 label "Landing" uses hall circulation

    # The gallery: it contains the void over the living room below, so it keeps its own
    # full floor area even though a 3.0 x 4.2 m hole obstructs the middle of it.
    room id=r_gallery at (0,4200) size 4800x6000 label "Gallery" uses hall circulation

    # ---- doors ----------------------------------------------------------------------------
    door id=d_bed2  on w_bed2_s  at 2000  width 900 swing in
    door id=d_bed3  on w_bed3_s  at 2000  width 900 swing in
    door id=d_linen on w_linen_s at 1100  width 700 swing out
    # Pocket, not hinged: each ensuite is 1800 mm wide with a bed, wardrobe and this
    # doorway all competing for the same wall — a swinging leaf has nowhere to clear.
    door id=d_en2   pocket on en2.wall  at 7500  width 700 slide right
    door id=d_en3   pocket on en3.wall  at 7500  width 700 slide right
    door id=d_enm   on en_m.wall at 7500  width 700 swing out
    door id=d_master_corr on w_l2_band at 10500 width 1000 swing into r_master
    opening id=o_gallery on w_l2_band at 1500 width 1800

    # en2 and en3 also open directly onto the landing (their own SOUTH wall, local
    # run-length 5300), so each reads as a shared/guest bath as well as an ensuite. `en_m`
    # deliberately keeps its ONE door, into the master bedroom only: it sits in the
    # building's far corner with no wall on circulation, and a master ensuite reached only
    # through its own bedroom is the conventional case, not a defect — and `arch lint` does
    # not flag it (`W_BATH_VIA_BEDROOM`), because en2 and en3 are bathrooms on this storey
    # reached off the landing without passing through a bedroom. `arch lint` raises eight
    # warnings in all: `W_ROOM_NOT_EQUATOR_FACING` on bedrooms 2 and 3, whose only windows face
    # north; and six `W_PATH_TOO_NARROW` on this floor, each a real 700 mm door (640 mm
    # clear). Five are walks from the top of the stair: each bed's clearance covers its
    # bedroom's own door, so bedrooms 2 and 3 are walked into through their ensuites, and
    # each ensuite's way in is one of these 700 mm doors. The sixth is the Master Suite's
    # route to its ensuite, through `d_enm`; the walk to the Master Suite itself is 700 mm at
    # its narrowest, which meets the minimum, so the room is flagged for that route instead.
    door id=d_en2_corr on en2.wall at 5300 width 700 swing out
    door id=d_en3_corr on en3.wall at 5300 width 700 swing out

    # ---- windows ----------------------------------------------------------------------------
    window on shell at 1200  width 1400   # bedroom 2, north wall
    window on shell at 11600 width 1400   # bedroom 3, north wall
    window at (6800,10200)  width 2400 wall shell   # master suite, south wall
    window at (0,7000)      width 1400 wall shell   # gallery, west wall

    # ---- furniture --------------------------------------------------------------------------
    furniture bed       at (1250,150)  size 1500x2000 in r_bed2
    furniture nightstand at (700,150)  size 450x400   in r_bed2
    furniture wardrobe   at (2900,300) size 550x1800  in r_bed2

    furniture bed       at (11050,150) size 1500x2000 in r_bed3
    furniture nightstand at (12600,150) size 450x400  in r_bed3
    furniture wardrobe   at (13100,300) size 450x1800  in r_bed3

    furniture rug        at (6300,7400) size 3400x2400 in r_master
    furniture double_bed at (7100,7600) size 1800x2000 in r_master
    furniture nightstand at (6500,7600) size 450x400   in r_master
    furniture nightstand at (9050,7600) size 450x400   in r_master
    furniture wardrobe   at (5000,4400) size 1800x600  in r_master
    furniture armchair   at (5100,9000) size 900x850   in r_master
    # A reading nook in the otherwise-bare east corner of the L, south of the ensuite.
    furniture desk         at (10200,7100) size 1200x700 in r_master
    furniture office_chair at (10500,7900) size 600x600  in r_master

    # The gallery railing looks down into the double-height living room below — a `void`
    # does not reduce this room's own area (`describe --json` reports it under `voids[]`).
    void id=v_gallery at (900,5100) size 3000x4200

    # The eaves: derived from THIS storey's own closed shell, mitred outward — it draws
    # and nothing else, but it does grow the page.
    roof overhang 700 wall shell

    stair id=stair at (4900,2700) size 2200x1000 dir down
  }

  axes {
    x at 0, 4000, 8000, 10000, 13800
    y at 0, 2600, 4200, 10200
  }

  title {
    project "Hillside Villa"
    drawn_by "ArchLang"
    date "2026-08-27"
  }
}
```

### Garden House

The other half of the language, and the one every plan above leaves out: **everything
outside the wall line**. A two-storey family house on a 22 × 22 m suburban lot, A2 at
1:100 — **6 rooms and 136.5 m² of floor**, beside **220.7 m² of ground**
(`archlang describe --json`'s `totals.outdoor_area_m2`) and a **484 m² lot**
(`site.lot_area_m2`). Those are three different numbers and the release exists to keep
them apart: a terrace is not floor area, so a ground surface appears in no `rooms[]`, no
`schedule rooms` row and no access graph.

Fifteen [`outdoor`](/reference#outdoor-ground-outside-the-building-v1-31) surfaces cover
all nine kinds — the driveway that runs to the garage door, paving to the front door and
again between the patio and the deck, a patio behind the living room and a deck off the
kitchen, a `water` pool, lawns front and back, gravel for the bin and shed yard, planting
on two front beds and three boundaries, and — on the upper storey — a `balcony` off the
main bedroom whose railing is derived on the three edges with no wall behind them. Each
draws a **scale-aware** material hatch, so the pattern is the same size on the sheet here
as it would be at 1:50. Two
[`fence`](/reference#fence-a-boundary-line-on-the-ground-v1-31) runs post the lot: a
`panel` enclosure round the pool — which is the drawing saying what the model does not,
since ground obstructs nothing, water included — and a `picket` street frontage that
stops either side of the path and the drive.

The garage is the other track meeting the first. `room … uses garage` classifies it and
`W_GARAGE_TOO_NARROW` measures it (5.5 m across, against the 2.7 m one parked `car`
needs), and its door is [`door garage`](/reference#door-kinds-v1-25) — the sixth kind,
which takes no clause at all and puts a **dashed** overhead projection inside the garage.
Which side that is is derived rather than written: the resolver asks which face has
*floor*, and the driveway on the other face is ground, not floor. Fourteen of the
twenty-one outdoor fixture families furnish the garden, all at their catalogued
footprints and none of them labelled.

`archlang lint` raises exactly one warning, left in and named in the source: an aspect
heuristic — `W_ROOM_NOT_EQUATOR_FACING` on the one habitable room with no south window,
Bedroom 2 on level 2. On a two-band plan one bedroom has to take the street aspect, and
the two rooms people sleep in longest get the garden, so this is the case where the
drafter overrules the rule. `archlang validate` is clean; `archlang validate --strict` reports
that one.

![garden-house.arch, compiled](/examples/garden-house.svg)

```arch
# The OUTDOOR flagship — a two-storey family house on a 22 x 22 m suburban lot, drawn as a
# SITE plan: the building is half of it, and the other half is ground.
#
# Everything else in examples/ draws what is inside the wall line. This one is the plan a
# house is actually issued as, which is the whole point of the v1.31 surfaces: `site …
# boundary` puts the lot line on the sheet, nine `outdoor` ground materials say what every
# square metre outside the walls IS, `fence` posts the pool enclosure and the street
# frontage, and 14 of the 21 outdoor fixture families furnish the garden the way the
# kitchen fixtures furnish a kitchen.
#
# READ THESE THREE FACTS BEFORE READING THE DRAWING. A ground surface is NOT a room: it is
# absent from `describe --json`'s `rooms[]`, from `totals.floor_area_m2` and from the drawn
# `schedule rooms` table, and its area lands in `totals.outdoor_area_m2` instead. It
# obstructs NOTHING — the pool included — so circulation walks straight across the water;
# that is a stated v1 simplification, and the `fence` round the pool is the drawing saying
# what the model does not. And it GROWS THE PAGE: the lot line is 22 m from the street, so
# without `paper` every line weight in the house would be scaled to the garden. Hence the
# sheet.
#
# THE GARAGE IS THE OTHER HALF OF THE RELEASE. `room … uses garage` is a use kind, so the
# schedule reads "Garage" and `W_GARAGE_TOO_NARROW` measures it: 5.5 m across, against the
# 2.7 m one parked `car` needs. Its door is `door garage`, the sixth kind — a sectional
# leaf that takes NO clause at all, because it parks OVERHEAD rather than swinging or
# sliding, and the dashed rectangle inside the garage is that overhead projection. Which
# side it projects to is DERIVED, not written: the resolver probes one wall thickness off
# each face and takes the one with floor. The driveway outside is `outdoor driveway`, and
# it is emphatically not floor, so the answer is the garage.
#
# ORIENTATION. The plot fronts the street on the NORTH, in the northern hemisphere, so
# `equator_side` is south — the garden. That is why the living room, the kitchen/dining
# and two of the three bedrooms take the south facade and the service band stacks along
# the street.
#
# ONE DELIBERATE WARNING, left in on purpose:
#
#   W_ROOM_NOT_EQUATOR_FACING x1 — Bedroom 2 (level 2) is the one habitable room with no
#   south window. On a two-band plan one bedroom has to take the street aspect, and the
#   two rooms people sleep in longest get the garden. The rule is an advisory drafting
#   heuristic, not a daylight measurement, and this is the case where the drafter
#   overrules it. `arch validate` is clean; `arch validate --strict` reports this one.
plan "Garden House" {
  units mm
  grid 100
  paper A2 landscape
  scale 1:100
  north up
  dims auto overall
  schedule rooms
  legend

  # The lot line. `street`/`hemisphere` draw nothing and only name directions; `boundary`
  # is the one part of `site` that puts ink on the page (a dash-dot property line on
  # C-PROP) and adds `site.lot_area_m2` — 484 m² here, by exact shoelace.
  site {
    street north
    hemisphere north
    boundary (0,0) (22000,0) (22000,22000) (0,22000)
  }

  # Shell centrelines. The building is an L: a 11.5 x 9 m house with a 5.5 x 6 m garage
  # on its street-side flank.
  let T = 300         # exterior thickness

  level 1 "Ground floor" {
    wall id=shell exterior thickness T {
      (2500,4500) (19500,4500) (19500,10500) (14000,10500) (14000,13500) (2500,13500) close
    }
    wall id=w_h1  partition thickness 100 { (6800,4500) (6800,13500) }
    wall id=w_h2  partition thickness 100 { (9200,4500) (9200,13500) }
    wall id=w_w1  partition thickness 100 { (2500,9000) (6800,9000) }
    wall id=w_e1  partition thickness 100 { (9200,8000) (14000,8000) }
    wall id=w_gar partition thickness 200 { (14000,4500) (14000,10500) }

    room id=r_hall    at (6800,4500)  size 2400x9000  label "Hall"           uses hall entry circulation
    room id=r_study   at (2500,4500)  size 4300x4500  label "Study"          uses office
    room id=r_living  at (2500,9000)  size 4300x4500  label "Living"         uses living
    room id=r_util    at (9200,4500)  size 4800x3500  label "Utility"        uses utility wc
    room id=r_kitchen at (9200,8000)  size 4800x5500  label "Kitchen/Dining" uses kitchen dining
    room id=r_garage  at (14000,4500) size 5500x6000  label "Garage"         uses garage

    # The front door, on the street facade, into the spine.
    door id=d_front on shell at 5500 width 1000 hinge left swing into r_hall

    # THE GARAGE DOOR. No `hinge`, no `slide`, no `swing`, no `open` — every one of them is
    # E_DOOR_KIND_CLAUSE, and each refusal is its own argument: the leaf travels UP, so
    # there is no jamb to hinge on, no wall to slide along and no intermediate position to
    # draw. The dashed rectangle it puts inside the garage is the panel parked overhead,
    # and dashed is this language's one convention for anything above the cut plane.
    door id=d_garage garage on shell at 14250 width 3000

    door id=d_study  on w_h1 at 2000 width 900 swing into r_study
    door id=d_living on w_h1 at 6500 width 900 swing into r_living
    # A pocket leaf: the utility takes no corridor, and the panel slides north into 1.3 m
    # of blank spine wall (it needs 850 — its own 800 plus a jamb allowance).
    door id=d_util pocket on w_h2 at 1800 width 800 slide left
    # Kitchen to garage — the mud-room door every attached garage has.
    door id=d_gar on w_gar at 1800 width 900 swing into r_util
    # Living and kitchen are one space across the spine.
    opening id=o_kitchen on w_h2 at 6800 width 1600

    # The two garden doors, each the leaf a builder would put there. A `bifold` opens the
    # living room onto the patio; a `sliding` pair opens the kitchen onto the deck. Neither
    # sweeps anything — `doorSwing()` is null for every kind but `hinged` — so nothing on
    # the terrace has to be kept clear of a leaf.
    door id=d_patio bifold  on shell at 41500 width 2000
    door id=d_deck  sliding on shell at 34200 width 1800 slide right

    window id=win_study   on shell at 49700 width 1600
    window id=win_util    on shell at 9100  width 900
    window id=win_living  on shell at 39400 width 1000
    window id=win_kitchen on shell at 32400 width 1200

    stair id=st at (6900,5200) size 900x3200 dir up

    # ---- Inside ----
    furniture desk      in r_study anchor top-left    flush size 1600x700
    furniture bookshelf in r_study anchor bottom-left flush size 1800x350
    furniture chair     at (3300,6000) size 500x500 rotate 180 in r_study

    furniture sofa    against wall shell segment 5 offset 2100 size 2200x900 in r_living
    furniture rug     at (3200,10400) size 2200x1600 in r_living
    furniture tv_unit at (5500,9200)  size 1200x400  in r_living

    furniture washer  against wall shell segment 0 offset 7300 in r_util
    furniture dryer   against wall shell segment 0 offset 8100 in r_util
    furniture basin   against wall shell segment 0 offset 9900 in r_util
    furniture wc      against wall shell segment 0 offset 10900 in r_util

    furniture kitchen_sink against wall w_e1 offset 600  in r_kitchen
    furniture stove        against wall w_e1 offset 1600 in r_kitchen
    furniture fridge       against wall w_e1 offset 2500 in r_kitchen
    furniture dining_table at (10200,10800) size 1600x900 in r_kitchen

    furniture car at (15400,5900) size 1900x4300 in r_garage
    furniture bicycle at (18300,6200) size 600x1700 in r_garage

    # ---- The ground ----
    #
    # Nine surfaces, six materials. None is a room, none obstructs anything, and each one
    # draws a scale-aware hatch over a tint: the pattern is the same size ON THE SHEET at
    # 1:100 as it would be at 1:50, because every dimension in it steps off the drawing's
    # reference dimension rather than off millimetres of building.
    outdoor id=g_drive  driveway at (14500,0)     size 5000x4500  label "Driveway"
    outdoor id=g_path   paving   at (7300,0)      size 1400x4500
    outdoor id=g_frontl lawn     at (600,600)     size 6300x3400  label "Front lawn"
    outdoor id=g_frontb planting at (8900,600)    size 5400x3400
    outdoor id=g_fronte planting at (19700,600)   size 1700x3400

    outdoor id=g_patio  patio    at (2600,13700)  size 4300x3300  label "Patio"
    outdoor id=g_link   paving   at (6900,13700)  size 2300x3300
    outdoor id=g_deck   deck     at (9200,13700)  size 4800x2900  label "Deck"
    outdoor id=g_pool   water    at (15200,13900) size 4000x3000  label "Pool"
    outdoor id=g_lawn   lawn     at (5800,17400)  size 13600x3600 label "Lawn"
    outdoor id=g_yard   gravel   at (2600,17400)  size 3000x3600
    outdoor id=g_bed_s  planting at (2600,21100)  size 16800x600
    outdoor id=g_bed_w  planting at (600,13700)   size 1700x8000
    outdoor id=g_bed_e  planting at (19800,13700) size 1900x8000

    # The pool enclosure — a `panel` fence, which is what a pool barrier is. A fence is NOT
    # a thin wall: no thickness, no poche, it hosts no opening and it joins no access
    # graph, so `describe --json` reports it under `fences[]` and never under `walls[]`. A
    # gate is deferred by name rather than faked by letting a door host onto it.
    fence id=f_pool panel { (14800,13500) (19600,13500) (19600,17300) (14800,17300) close }
    # The street frontage, in the other style. It stops either side of the path and the
    # drive, which is how a picket fence meets an entrance.
    fence id=f_street picket { (400,400) (7100,400) }

    # ---- The garden ----
    #
    # Fourteen of the twenty-one outdoor families. Nothing here is `requiresWall`: outdoors
    # the wall is the exception, and a fixture standing in the middle of a lawn is the
    # normal arrangement rather than a floating one.
    furniture outdoor_table at (2700,14400) size 1600x900
    furniture umbrella      at (4900,14400) size 900x900
    furniture bbq           at (6000,13900) size 700x600

    furniture sun_lounger at (9600,14100) size 700x1900
    furniture sun_lounger at (10600,14100) size 700x1900

    furniture shed        at (2900,19100) size 2400x1700
    furniture bin         at (2800,17600) size 600x600
    furniture bin         at (3600,17600) size 600x600
    furniture clothesline at (2800,18400) size 2500x400
    furniture ev_charger  at (14000,2000) size 400x300
    furniture mailbox     at (6600,700)   size 400x300

    furniture trampoline at (9000,17800) size 3000x3000
    furniture tree       at (6100,18000)  size 2400x2400
    furniture tree       at (17400,18100) size 2600x2600
    furniture conifer    at (20000,15000) size 1500x1500
    furniture hedge      at (2600,21200)  size 16800x500
    furniture shrub      at (900,15000)   size 900x900
    furniture shrub      at (900,17200)   size 900x900
    furniture shrub      at (900,19400)   size 900x900
    furniture shrub      at (20100,17600) size 900x900
    furniture shrub      at (20100,19600) size 900x900
    furniture shrub      at (20100,1300)  size 900x900
    furniture shrub      at (9400,1300)   size 900x900
    furniture shrub      at (12600,1300)  size 900x900
  }

  level 2 "First floor" {
    wall id=shell exterior thickness T {
      (2500,4500) (14000,4500) (14000,13500) (2500,13500) close
    }
    wall id=w_h1 partition thickness 100 { (6800,4500) (6800,13500) }
    wall id=w_h2 partition thickness 100 { (9200,4500) (9200,13500) }
    wall id=w_w1 partition thickness 100 { (2500,9000) (6800,9000) }
    wall id=w_e1 partition thickness 100 { (9200,8000) (14000,8000) }

    # "Bedroom 1" IS the main bedroom — the one with the balcony. It is named for its
    # number rather than its rank so the three read as a set, and so the label fits inside
    # its own floor: the renderer has no font metrics, only the closed-form estimate in
    # `text-metrics.ts`, and "Main bedroom" is wider than a 4.8 m room at 1:100.
    room id=r_landing at (6800,4500) size 2400x9000 label "Landing"   uses circulation hall
    room id=r_bed2    at (2500,4500) size 4300x4500 label "Bedroom 2" uses bedroom
    room id=r_bed3    at (2500,9000) size 4300x4500 label "Bedroom 3" uses bedroom
    room id=r_bath    at (9200,4500) size 4800x3500 label "Bathroom"  uses bath
    room id=r_bed1    at (9200,8000) size 4800x5500 label "Bedroom 1" uses bedroom

    # No exterior door on this storey — see the header note. The stair below carries the
    # same id, so the shaft is what joins this landing to the ground floor.
    door id=d_bed2 on w_h1 at 2000 width 800 swing into r_bed2
    door id=d_bed3 on w_h1 at 6500 width 800 swing into r_bed3
    door id=d_bath on w_h2 at 1800 width 800 swing into r_bath
    door id=d_bed1 on w_h2 at 6800 width 800 swing into r_bed1

    # THE BALCONY, and the door that keeps it legal. An `outdoor balcony` rails every edge
    # with no wall one thickness behind it — here the three free sides, derived at each
    # edge's own midpoint — and `W_BALCONY_NO_DOOR` fires on a balcony with no opening
    # within a wall thickness of it. This sliding leaf is that opening. It also sits over
    # the ground floor's `deck`, which is what a balcony usually is.
    door id=d_balcony sliding on shell at 23500 width 1800 slide right

    window id=win_bed2  on shell at 1700  width 1600
    window id=win_bath  on shell at 8500  width 900
    window id=win_bed3  on shell at 29900 width 1600
    window id=win_bed1  on shell at 21500 width 1200

    outdoor id=g_balcony balcony at (9300,13700) size 4200x1800 label "Balcony"

    stair id=st at (6900,5200) size 900x3200 dir down

    # Every bed goes `against wall`, not `anchor … flush`: `against` derives the quarter
    # turn FROM the wall, so the headboard ends up at the wall. An anchored bed keeps
    # rotation 0 whatever edge it is pushed to, which is `W_FIXTURE_BACK_TO_ROOM`.
    furniture bed      against wall shell segment 1 offset 6500 size 1600x2100 in r_bed1
    furniture wardrobe in r_bed1 anchor top flush size 1600x600
    furniture nightstand at (12600,9500) size 450x400 in r_bed1

    furniture bed      against wall shell segment 3 offset 6700 size 1400x2000 in r_bed2
    furniture wardrobe in r_bed2 anchor top flush size 1400x600

    furniture bed      against wall shell segment 3 offset 2300 size 1400x2000 in r_bed3
    furniture desk     in r_bed3 anchor top flush size 1400x600

    furniture bathtub in r_bath anchor top-right    flush size 1700x700
    furniture basin   in r_bath anchor bottom-left  flush size 600x450
    furniture wc      in r_bath anchor bottom-right flush size 400x700

    # The eaves, on the top storey only — this level's `shell` is the plan's one closed
    # exterior ring on THIS page, so no `wall` clause is needed. The garage is single
    # storey and carries no eaves line of its own.
    roof overhang 500
  }

  title {
    project "Garden House"
    drawn_by "ArchLang"
    date "2026-08-28"
  }
}
```

## Start here

### One room

The smallest complete ArchLang plan, and the one the [guide](/guide) opens with: a
200 mm shell, one room that says what it is for, a door and a window, and a single
overall dimension chain. **1 room, 20 m²**, fourteen lines including its comment.

Neither opening carries a coordinate. `door … on shell at 60%` walks 60 % of the way
around the shell wall's own run and puts the door there, so the door cannot be reported
off-wall and cannot drift when the room resizes. Change `5000x4000` to something else and
the wall, both openings and the dimension text all follow.

![one-room.arch, compiled](/examples/one-room.svg)

```arch
# The smallest complete ArchLang plan: one room, one door, one window, one dimension chain.
# Every other example is this file with more of it — a shell you can walk into, a room that
# knows what it is for, and openings pinned to the wall by position rather than coordinate.
plan "One Room" {
  units mm
  grid 50
  north up
  dims auto overall
  wall id=shell exterior thickness 200 { (0,0) (5000,0) (5000,4000) (0,4000) close }
  room id=r_main at (0,0) size 5000x4000 label "Room" uses living
  door   id=d_in on shell at 60% width 900  swing in   # 60% of the loop's run: the south wall
  window id=w_n  on shell at 10% width 1500            # 10%: the north wall
  title { project "One Room" drawn_by "ArchLang" date "2026-08-16" }
}
```

### Laneway House

The signature example: a one-bedroom laneway cottage of **3 rooms and 48.75 m²**
(Living / Kitchen 27.3, Bedroom 13.2, Bath 8.25) with **4 doors and 4 windows**, on a
7.5 × 6.5 m footprint. It is the plan the home page's hero types out.

Nothing in it is positioned by hand. Every opening is pinned to a run distance along a
named wall (`on w_lane at 2100`), every fixture resolves against a room or the wall behind
it (`in r_live centered`, `against wall w_west offset 400`), and the bath/bedroom pair is
laid out by a [`strip`](/reference#strip-v1-13) that assigns each room its depth and
derives the rest. `site { street north }`
([site and orientation](/reference#site-and-orientation)) names the two facades the plan
turns on — the front door faces the lane, the glazing faces the garden — and it draws
nothing at all; it only gives those directions names an intent or a lint rule can assert
against. Two of the four doors are not hinged: a **sliding** garden door, and a
**pocket** door to the bath whose `slide right` aims the panel down the solid length of
the hall partition rather than at the 500 mm that would trip `W_POCKET_RUN`.

![laneway-house.arch, compiled](/examples/laneway-house.svg)

```arch
# A one-bedroom laneway cottage — 49 m² that reads as one program.
#
# Nothing here is positioned by hand. Every opening is pinned to a run distance along a
# named wall (`on <wall> at <pos>`), every fixture resolves against a room or a wall
# (`in <room> anchor …`, `against wall …`), and the bath/bedroom pair is laid out by
# `strip`. `site { street north }` names the two facades the plan turns on: the door
# faces the lane, the glass faces the garden.
plan "Laneway House" {
  units mm
  grid 50            # partitions are 100 thick, so a `flush` fixture lands on a …50
  north up
  dims auto all
  site { street north }

  wall id=w_lane   exterior  thickness 200 { (0,0) (7500,0) }
  wall id=w_east   exterior  thickness 200 { (7500,0) (7500,6500) }
  wall id=w_garden exterior  thickness 200 { (0,6500) (7500,6500) }
  wall id=w_west   exterior  thickness 200 { (0,0) (0,6500) }
  wall id=w_hall   partition thickness 100 { (4200,0) (4200,6500) }
  wall id=w_bath   partition thickness 100 { (4200,2500) (7500,2500) }

  room id=r_live at (0,0) size 4200x6500 label "Living / Kitchen" uses living kitchen
  strip down at (4200,0) gap 0 width 3300 {
    room id=r_bath size 2500 label "Bath"    uses bath
    room id=r_bed  size 4000 label "Bedroom" uses bedroom
  }

  door id=d_front  on w_lane at 2100 width 900 hinge near start swing into r_live
  door id=d_garden sliding on w_garden at 2700 width 1800 slide left
  door id=d_bed    on w_hall at 5600 width 800 hinge left swing into r_bed
  # `w_hall` is walked lane→garden, so `slide right` sends the panel down the solid wall
  # below the jamb; `slide left` would aim it at 500 mm and trip `W_POCKET_RUN`.
  door id=d_bath   pocket on w_hall at 900 width 800 slide right

  window on w_west   at 2400 width 1200
  window on w_garden at 700  width 1000
  window on w_garden at 5850 width 1600
  window on w_east   at 1500 width 600

  furniture fridge       against wall w_west offset 400  in r_live
  furniture stove        against wall w_west offset 1200 in r_live
  furniture kitchen_sink against wall w_west offset 2000 in r_live
  # The rug is an UNDERLAY, drawn before the pieces that stand on it so the sofa and
  # table sit visibly on top rather than hiding it — anchored to the room, like
  # everything else here, never a coordinate.
  furniture rug   in r_live anchor bottom-left inset 400 size 2800x3400
  furniture table  in r_live centered size 1300x900 label "Table"
  furniture sofa   in r_live anchor bottom-left flush inset 250 size 900x2000  label "Sofa"
  furniture bed    in r_bed  anchor bottom-right flush inset 250 size 1500x2000 label "Bed"
  furniture robe   in r_bed  anchor top-left     flush inset 200 size 1800x600  label "Wardrobe"
  furniture shower in r_bath anchor top-right    flush size 900x900
  furniture wc     in r_bath anchor bottom-left  flush size 400x700
  furniture basin  in r_bath anchor bottom-right flush size 600x450

  title { project "Laneway House" drawn_by "ArchLang" date "2026-08-16" }
}
```

### Garden Loft

**3 rooms, 35 m²**, at `scale 1:50` — deliberately the smallest plan still worth
*describing*. It is the plan the home page reads its own room schedule from, which is the
constraint that shaped it: three rooms, so the derived table fits in a band beside the
drawing, but a real fit-out behind each one — a kitchen run with a wall behind it, a bath
with three fixtures, a door into every room.

There is no `paper` block here, so `scale 1:50` is *annotation*: the title block says 1:50
and the SVG scales to whatever box it is dropped into. That is what a web page wants; a
building issued to a builder wants the [sheet](/reference#paper-and-scale-the-sheet)
instead, which several examples below declare.

![garden-loft.arch, compiled](/examples/garden-loft.svg)

```arch
# A 35 m² garden loft — the plan the docs home page reads its own facts from.
#
# Deliberately the smallest plan worth DESCRIBING: three rooms, so `describe()`'s room
# table fits in a band beside the drawing, but a real one — a kitchen run with a wall
# behind it, a bath with three fixtures, a door into every room. `scale 1:50` is
# annotation here (there is no `paper` block), which is what a web page wants: the
# caption says 1:50 and the SVG scales to whatever box it is dropped into. Nothing but
# the walls carries a coordinate — openings are pinned to a run along a named wall and
# fixtures resolve against their room or the wall behind them.
plan "Garden Loft" {
  units mm
  grid 50
  scale 1:50
  north up
  dims auto all

  wall id=w_north exterior  thickness 200 { (0,0) (7000,0) }
  wall id=w_east  exterior  thickness 200 { (7000,0) (7000,5000) }
  wall id=w_south exterior  thickness 200 { (0,5000) (7000,5000) }
  wall id=w_west  exterior  thickness 200 { (0,0) (0,5000) }
  wall id=w_spine partition thickness 100 { (4200,0) (4200,5000) }
  wall id=w_bath  partition thickness 100 { (4200,3200) (7000,3200) }

  room id=r_live at (0,0)       size 4200x5000 label "Living / Kitchen" uses living kitchen
  room id=r_bed  at (4200,0)    size 2800x3200 label "Bedroom"          uses bedroom
  room id=r_bath at (4200,3200) size 2800x1800 label "Bath"             uses bath

  door id=d_main on w_south at 3200 width 900 hinge near start swing into r_live
  door id=d_bed  on w_spine at 1600 width 800 hinge left  swing into r_bed
  door id=d_bath on w_spine at 4100 width 800 hinge right swing into r_bath

  window on w_west  at 3600 width 1400
  window on w_north at 2400 width 1800
  window on w_north at 5600 width 1200
  window on w_south at 5000 width 600

  furniture fridge       against wall w_west offset 400  in r_live
  furniture stove        against wall w_west offset 1100 in r_live
  furniture kitchen_sink against wall w_west offset 1900 in r_live
  furniture table  in r_live centered size 1300x900 label "Table"
  furniture sofa   in r_live anchor bottom-left flush inset 300 size 1900x850 label "Sofa"
  furniture bed    in r_bed  anchor bottom-right flush inset 200 size 1500x2000 label "Bed"
  furniture robe   in r_bed  anchor top-left      flush inset 150 size 1200x600  label "Wardrobe"
  furniture shower in r_bath anchor top-right    flush size 900x900
  furniture wc     in r_bath anchor bottom       flush size 400x700
  furniture basin  in r_bath anchor bottom-right flush size 600x450

  title { project "Garden Loft" drawn_by "ArchLang" date "2026-08-16" }
}
```

### Tiny House

A 7.2 × 3.0 m micro-home — **2 rooms, 19.6 m², 3 doors, 5 windows** — and the example for
the three language forms that are about *fit* rather than shape.

The wet room is 1.55 m wide, so a hinged leaf swinging in fouls the WC and swinging out
sweeps the only walkway in the house; it gets a **barn** door instead, which sweeps
nothing at all (`doorSwing()` returns `null` for every non-hinged
[kind](/reference#door-kinds-v1-25), so `W_SWING_OBSTRUCTED` cannot even apply) and parks
on the wall it came from. The wardrobe recess — 1.9 × 0.65 m, far under the 4 m² of floor
`lint` expects of a room, so it is deliberately *not* declared as one — closes with a
**bifold**: two leaves, each half of the 1600 opening, folding out of the way instead of
swinging 800 mm into a 1.85 m galley. That 1.85 m is not typed anywhere: `dim clear`
pulls each endpoint onto the inner *face* of the wall the measurement runs into, so the
number on the drawing is the built clear width, derived. `open 0.6` on the barn door is
drawing-only — nothing measured reads it — and is written because a leaf drawn shut is
indistinguishable from a hinged one on paper.

![tiny-house.arch, compiled](/examples/tiny-house.svg)

```arch
# A 7.2 x 3.0 m micro-home on one level — the DOOR-KINDS-AND-CLEARANCE example.
#
# A tiny house is the building where every millimetre is argued over, so it is the right
# plan to show the three language forms that are about *fit* rather than shape:
#
#   door barn   … open 0.6   a leaf that hangs on the FACE of the wall instead of in it
#   door bifold … width 1600 two leaves that fold into their own opening
#   dim clear   (a)->(b)     a dimension pulled in to the two INNER wall faces
#
# Why a barn door and not a hinged one? The wet room is 1.55 m wide. A hinged leaf
# swinging in fouls the WC; swinging out it sweeps the only walkway in the house. A barn
# leaf sweeps nothing at all — `doorSwing()` returns null for every non-hinged kind, so
# `W_SWING_OBSTRUCTED` cannot even apply to it — and parks on the wall it came from.
# `slide right` sends it toward the wall's far end (the same left/right convention
# `hinge` uses, read along the wall's own traversal direction), which here is the blank
# south-east corner of the living room rather than back over the loft ladder.
#
# `open 0.6` is DRAWING-ONLY. Nothing measured reads it: it changes how far along the
# wall the panel is drawn and nothing else. It is written here because a barn door drawn
# shut is indistinguishable from a hinged one on paper, and the point of the drawing is
# to show where the leaf goes when it is open.
#
# The wardrobe is a built-in recess, not a room — at 1.9 x 0.65 m it is far under the
# 4 m2 floor `lint` expects of a room, and calling it one would be a fiction. It is
# simply two partitions and a `bifold` door: two leaves, each half the 1600 opening,
# folding out of the way instead of swinging 800 mm into a galley that is 1.85 m wide.
#
# That 1.85 m is what `dim clear` reports. An ordinary `dim` measures between the two
# points you wrote; `clear` pulls each endpoint in onto the inner FACE of the wall the
# measurement runs into — here the galley face of the wardrobe front and the inner face
# of the north shell — so the number is the built clear width, derived, never typed.
# Take 600 mm of worktop out of it and 1250 mm of walkway is left, which is why the
# fridge door and the wardrobe leaves can both be open at once.
#
# ONE DELIBERATE WARNING, left in on purpose:
#
#   W_PATH_TOO_NARROW x1 — the walk to the Wet room. `arch describe --json` reports it
#   under `circulation.blocked` with a MEASURED `widestWayInMm` of 400 against the
#   700 mm minimum. The 800 mm barn door is not the pinch; the room is: 1700 x 2800 mm
#   holding a shower, a WC and a basin, which is what a wet room in a 7.2 x 3.0 m house
#   costs. The warning appeared in v1.33.0, when the rule stopped going silent on a room
#   it could not reach — before that this plan reported nothing at all, which was worse
#   than reporting 400 mm. It stands rather than being nudged away: moving a piece to
#   silence a measurement is how a plan stops being evidence. `arch validate` is clean;
#   `arch validate --strict` reports this one.
plan "Tiny House" {
  units mm
  # `grid 50`, not 100: every partition here is 100 thick, so a fixture placed `flush`
  # against one lands on a half-thickness (...50) coordinate, and a 100 grid would snap
  # it back INTO the wall.
  grid 50
  north up

  # ---- shell + the two pieces of joinery that act as structure ---------------
  # 7200 x 3000 over the outer faces; 6800 x 2600 inside. The shell is one closed
  # outline so its corners mitre, and openings on it are written `at (x,y) wall shell`
  # — an exact centreline point, which can no more be off-wall than `on … at <pos>` can.
  wall id=shell exterior thickness 200 { (0,0) (7000,0) (7000,2800) (0,2800) close }

  # The wet room's only partition. Named, because the barn door is hung on it by run
  # distance from its first point, (5300,0).
  wall id=w_wet partition thickness 100 { (5300,0) (5300,2800) }

  # The wardrobe: a front and a return. Everything they enclose is still living-room
  # floor as far as the plan is concerned — a recess, not a space.
  wall id=w_ward   partition thickness 100 { (0,2000) (2000,2000) }
  wall id=w_ward_e partition thickness 100 { (2000,2000) (2000,2800) }

  # ---- rooms ----------------------------------------------------------------
  # One room does living, sleeping and cooking, which is what `uses living kitchen`
  # says: it is a kitchen for the fixture rules and a living room for everything else.
  room id=r_main at (0,0)    size 5300x2800 label "Living / Sleep" uses living kitchen
  room id=r_wet  at (5300,0) size 1700x2800 label "Wet room"       uses bath

  # ---- doors ----------------------------------------------------------------
  # The front door is the only hinged leaf in the house, and the only one with a swing
  # arc to keep clear: 900 mm of quarter-disc between the wardrobe return and the bench.
  door id=d_front at (2700,2800) width 900 wall shell hinge left swing in

  # The wet room. `swing` on a barn door does not mean a sweep — there is none — it
  # names the FACE the leaf hangs on, and `in` is the wall's +normal side: the living
  # room, where there is 1600 mm of blank wall for the panel to park on.
  # `open 0.6` draws the panel 60% of the way along that travel.
  door id=d_wet barn on w_wet at 1500 width 800 swing in slide right open 0.6

  # The wardrobe. A bifold's two leaves fold toward the `swing` face — out into the
  # galley, because there is nothing behind them to fold into.
  door id=d_ward bifold on w_ward at 1000 width 1600 swing out

  # ---- windows --------------------------------------------------------------
  window id=n_galley at (700,0)     width 900  wall shell   # over the sink
  window id=n_bed    at (3700,0)    width 1200 wall shell   # over the bed
  window id=w_gable  at (0,1400)    width 900  wall shell
  window id=s_bench  at (4300,2800) width 1200 wall shell
  window id=e_wet    at (7000,1500) width 600  wall shell

  # ---- galley: three catalogued fixtures, no coordinates --------------------
  # `offset` is the distance of the piece's CENTRE along shell segment 0 — the north
  # run, walked from (0,0) — and the footprints come from the catalogue, so no width,
  # depth or wall face is written down twice.
  furniture kitchen_sink against wall shell segment 0 offset 500  in r_main
  furniture stove        against wall shell segment 0 offset 1200 in r_main
  furniture fridge       against wall shell segment 0 offset 1800 in r_main

  # ---- wet room -------------------------------------------------------------
  # Anchored to the room and `flush`, so each piece measures from the backing wall's
  # inner face rather than from a half-thickness someone worked out by hand.
  furniture shower in r_wet anchor top-left    flush size 900x900
  furniture wc     in r_wet anchor bottom-left flush size 400x700

  # The basin is the one fixture placed by coordinate, and therefore the one that has to
  # state its own quarter-turn. `rotate 0` means "back to the north", so `rotate 90` puts
  # this basin's back on its EAST edge — against the shell, in the south-east corner the
  # shower does not take. Note the declared `size` is always the plan-axis footprint:
  # the symbol turns inside it, the rectangle does not.
  furniture basin at (6450,2100) size 450x600 rotate 90 in r_wet

  # ---- living / sleeping ----------------------------------------------------
  furniture bed      at (2600,100)  size 2000x1400 label "Bed"      in r_main
  furniture bench    at (3400,2100) size 1500x600  label "Bench"    in r_main
  furniture wardrobe at (100,2350)  size 1800x350  label "Wardrobe" rotate 180 in r_main
  # The loft ladder's hatch, flush to the wet-room partition. 600 wide because a label
  # is drawn INSIDE its own footprint: "Ladder" does not fit a 350 mm box, and the
  # renderer has no metrics to shrink it with — it would simply run out over the wall.
  furniture ladder   at (4650,100)  size 600x700   label "Ladder"   in r_main

  # ---- dimensions -----------------------------------------------------------
  # Hand chains rather than `dims auto`: on a plan this small the automatic GB/T set
  # would out-draw the building. Endpoint ORDER picks the side — the line is offset
  # along the LEFT normal of from->to — so each of these is written in the direction
  # that throws it out into the margin. The clear dim carries no `text`: the whole point
  # is that 1850 is measured off the two wall faces, not typed in by the author.
  dim clear (200,0)->(200,2000) offset 600
  dim faces (0,2800)->(7000,2800) offset 500
  dim faces (7000,2800)->(7000,0) offset 500

  # ---- the eaves --------------------------------------------------------------
  # `shell` is the plan's one closed exterior ring, so no `wall` clause is needed to
  # disambiguate it. A micro-home wears its roof close: 400 mm, not the 600 a suburban
  # bungalow shades its glazing with.
  roof overhang 400

  title {
    project "Tiny House"
    drawn_by "ArchLang"
    date "2026-08-16"
  }
}
```

### Studio (1BR)

The long-standing flagship: **4 rooms, 42 m², 3 doors, 3 windows**, `scale 1:50`. A tour
of rooms tagged with **`uses`**, a cased [`opening`](/reference#opening-v1-3) linking the
living space to the hall, real [fixture symbols](/furniture) (sink, counter, stove,
fridge, shower, basin, WC), and doors whose swings stay clear of the furniture. It is
**lint-clean** under the default profile and import-free — a single file with no `import`
and no World seam — which is why the test suite uses it as the plan that must always
compile from nothing. Run `archlang describe` / `archlang lint` on it to see the
[analysis](/analysis) layer in action.

![studio.arch, compiled](/examples/studio.svg)

```arch
# A compact studio apartment — the canonical ArchLang example.
#
# Architecturally sound (passes `arch lint`): every room opens off a central hall,
# so the bath is never reached through the bedroom; the bath is fully enclosed and
# fitted with real fixtures; and no door leaf sweeps onto furniture. Self-contained
# (no imports) so it compiles from a single file.
plan "Studio 1BR" {
  units mm
  grid 50
  scale 1:50
  north up

  # Exterior shell + partitions. The x=4000 divider runs the FULL height so the bath
  # is walled off from the living space; the right column splits into bedroom / hall / bath.
  wall exterior  thickness 200 { (0,0) (7000,0) (7000,6000) (0,6000) close }
  wall partition thickness 100 { (4000,0) (4000,6000) }
  wall partition thickness 100 { (4000,3000) (7000,3000) }
  wall partition thickness 100 { (4000,4400) (7000,4400) }

  room id=r_living at (0,0)       size 4000x6000 label "Living / Kitchen" uses living kitchen
  room id=r_bed    at (4000,0)    size 3000x3000 label "Bedroom"           uses bedroom
  room id=r_hall   at (4000,3000) size 3000x1400 label "Hall"             uses hall
  room id=r_bath   at (4000,4400) size 3000x1600 label "Bath"             uses bath

  # Entrance into the living space; the hall links it to the bedroom and the bath.
  # Living ↔ hall is a cased opening (circulation needs no door leaf); the bedroom
  # and bath each get a real door off the hall.
  door    id=d_main   at (3000,6000) width 1000 wall exterior  hinge left  swing in
  opening id=o_living at (4000,3700) width 900  wall partition
  door    id=d_bed    at (6400,3000) width 800  wall partition hinge right swing out
  door    id=d_bath   at (4600,4400) width 800  wall partition hinge left  swing out

  window at (0,2000)    width 1500 wall exterior
  window at (7000,1500) width 1200 wall exterior
  window at (7000,5200) width 700  wall exterior

  # Kitchen run along the north wall: sink · counter · stove · fridge (drawn as symbols).
  furniture kitchen_sink at (300,250)  size 800x600
  furniture counter      at (1200,250) size 600x600
  furniture stove        at (1950,250) size 600x600
  furniture fridge       at (2700,250) size 600x650

  # Living + bedroom furniture, kept clear of the door swings.
  furniture sofa at (350,4300) size 2000x900  label "Sofa"
  furniture bed  at (4300,300) size 1500x2000 label "Bed"

  # Bathroom fixtures, kept clear of the door's entry path (the door swings out into
  # the hall): shower in the far corner, basin against the partition, WC on the south
  # wall — the left third of the room stays open so you can actually step inside.
  # The WC is anchored to the room's south edge, so its rotation (180 — cistern to the
  # south wall) is DERIVED; `flush` measures from that wall's inner face rather than the
  # room rectangle's centerline edge, so the 200mm shell's half-thickness is never named.
  furniture shower at (6000,5000) size 900x900   # against the E + S walls (corner)
  furniture basin  at (5200,4450) size 600x450   # back to the hall partition
  furniture wc     in r_bath anchor bottom flush size 400x700

  # Dimension strings: room widths above, overall extents around. The reference
  # (witness) points sit on the building's OUTER faces (x=-100/7100, y=-100/6100 for
  # the 200mm shell) so the extension lines start at the wall face and read outward,
  # never poking back into the building — while the spans still measure centerline to
  # centerline (4000 · 3000 · 7000 · 6000).
  dim (4000,-100)->(0,-100)    offset 250 text "4000"
  dim (7000,-100)->(4000,-100) offset 250 text "3000"
  dim (0,6100)->(7000,6100)    offset 500 text "7000"
  dim (7100,6000)->(7100,0)    offset 500 text "6000"

  title {
    project "Studio Apartment"
    drawn_by "ArchCanvas"
    date "2026-06-27"
  }
}
```

## Homes

### Two-bedroom flat

**5 rooms, 80 m², 5 doors, 5 windows** on a 10 × 8 m footprint at `scale 1:100` — a
larger plan with a central corridor and several openings, placed absolutely at a real
apartment's scale, with `north` reoriented.

![two-bed.arch, compiled](/examples/two-bed.svg)

```arch
# A two-bedroom apartment with a central corridor.
plan "Two-Bedroom Flat" {
  units mm
  grid 100
  scale 1:100
  north right

  wall id=w_shell   exterior  thickness 250 { (0,0) (10000,0) (10000,8000) (0,8000) close }
  wall id=w_kb      partition thickness 100 { (4000,0) (4000,5000) }
  wall id=w_bh      partition thickness 100 { (4000,5000) (10000,5000) }
  wall id=w_kb2     partition thickness 100 { (0,5000) (4000,5000) }
  wall id=w_bath_w  partition thickness 100 { (4000,5000) (4000,8000) }
  wall id=w_hall    partition thickness 100 { (7000,5000) (7000,8000) }

  room id=r_kitchen at (0,0)        size 4000x5000 label "Kitchen / Living"
  room id=r_bed1    at (4000,0)     size 6000x5000 label "Master Bedroom"
  room id=r_bed2    at (0,5000)     size 4000x3000 label "Bedroom 2"
  room id=r_bath    at (4000,5000)  size 3000x3000 label "Bathroom"
  room id=r_hall    at (7000,5000)  size 3000x3000 label "Hall"

  door id=d_front at (8500,8000) width 1000 wall w_shell hinge left swing into r_hall
  door at (4000,2500)  width 900  wall partition hinge left swing in
  door id=d_bed2 on w_kb2 at 2000 width 900 hinge right swing into r_bed2
  door at (5500,5000)  width 800  wall partition hinge left swing in
  door at (7000,6500)  width 900  wall partition hinge right swing in

  window at (2000,0)     width 2000 wall exterior
  window at (8000,0)     width 2400 wall exterior
  window at (0,3000)     width 1500 wall exterior
  window at (10000,3000) width 1500 wall exterior
  window id=w_bed2 at (2000,8000) width 1400 wall w_shell

  # The master bed's head is already on the north wall (rotate 0). Bedroom 2's shares no
  # wall with the corridor, so its head goes to the south wall instead.
  furniture bed at (5000,300) size 1800x2100 label "Bed"
  furniture bed at (300,5800) size 1400x2000 label "Bed" rotate 180
  furniture wardrobe in r_bed1 anchor top-right flush size 1200x600
  furniture wardrobe against wall w_bath_w offset 1500 in r_bed2

  # Kitchen run along the west wall, clear of the window at (0,3000); the living half of
  # the room takes a sofa and a small table.
  furniture fridge       against wall w_shell segment 3 offset 7500 in r_kitchen
  furniture stove        against wall w_shell segment 3 offset 6800 in r_kitchen
  furniture kitchen_sink against wall w_shell segment 3 offset 6100 in r_kitchen
  furniture sofa  at (2850,600) size 850x600 label "Sofa"  in r_kitchen
  furniture table at (200,3200) size 700x500 label "Table" in r_kitchen

  furniture shower against wall w_bath_w offset 2000 in r_bath
  furniture wc     against wall w_shell segment 2 offset 4500 in r_bath
  furniture basin  against wall w_shell segment 2 offset 3400 in r_bath

  # The eaves: `w_shell` is the plan's one closed exterior ring, so no `wall` clause is
  # needed to disambiguate it.
  roof overhang 500

  # Overall extents, outside face to outside face: `faces` pushes each endpoint out
  # onto the wall it runs into, so the printed span is the true 10300 × 8300 the
  # building measures — the 10000 × 8000 room grid plus half the shell at each end,
  # and note the shell is 300 not 250 here because `grid 100` snaps the thickness.
  # Exactly the arithmetic a hand-written `text` gets wrong (it used to say 10000).
  dim faces (0,8000)->(10000,8000) offset 700
  dim faces (0,0)->(0,8000) offset 700

  title {
    project "Two-Bedroom Flat"
    drawn_by "ArchCanvas"
    date "2026-06-25"
  }
}
```

### Attached (placement sugar)

A one-bedroom flat — **2 rooms, 28 m²** — with **no hand-computed coordinates** for its
openings or furniture. It exercises the placement sugar together: a
[`strip`](/reference#strip-v1-13) laying rooms out end to end, doors and windows attached
to a wall by position (`on <wall> at <pos>`, so they can never be reported "off wall"), a
door that opens toward a named room (`swing into`), and furniture anchored inside a room
(`in <room> anchor … inset …`).

![attached.arch, compiled](/examples/attached.svg)

```arch
# A one-bedroom flat authored with the v1.13 placement sugar — no hand-computed
# coordinates for openings or furniture. It exercises, together:
#   • `strip` — a row of rooms laid out end to end
#   • openings attached to a wall by position (`door|window … on <wall> at <pos>`)
#   • a door that opens toward a named room (`swing into`) hinged at a wall end
#   • furniture placed relative to a room (`in <room> anchor …`)
# Compiles clean and passes `arch lint`.
plan "Attached 1BR" {
  units mm
  grid 100
  north up

  # Living + bedroom laid left-to-right by a strip, sharing a 4 m depth.
  strip right at (0,0) gap 0 height 4000 {
    room id=r_living size 4000 label "Living"  uses living
    room id=r_bed    size 3000 label "Bedroom" uses bedroom
  }

  # Exterior shell as four straight walls (each a clean start→end to attach onto)
  # plus the partition between the two rooms.
  wall id=w_north exterior  thickness 200 { (0,0) (7000,0) }
  wall id=w_south exterior  thickness 200 { (0,4000) (7000,4000) }
  wall id=w_west  exterior  thickness 200 { (0,0) (0,4000) }
  wall id=w_east  exterior  thickness 200 { (7000,0) (7000,4000) }
  wall id=w_part  partition thickness 100 { (4000,0) (4000,4000) }

  # Entrance 2000 mm along the south wall, hinged at that wall's start end and
  # opening into the living room. The bedroom door on the partition opens inward.
  door id=d_main on w_south at 2000 width 1000 hinge near start swing into r_living
  door id=d_bed  on w_part  at 2000 width 900  swing into r_bed

  # A window centred on each room's exterior wall.
  window on w_west at 50% width 1400
  window on w_east at 50% width 1200

  # Furniture placed by anchor inside each room (never off a coordinate).
  furniture sofa in r_living anchor top-left inset 300 size 2000x900  label "Sofa"
  furniture bed  in r_bed    anchor top-right inset 300 size 1500x2000 label "Bed"
}
```

### Furnished Flat (the drawn symbol catalogue)

**7 rooms, 90.72 m², 6 doors, 6 windows** on an 8.4 × 10.8 m footprint — and **39 pieces
across 38 of the 83 catalogued furniture families**, which is what it is here for. Every
other example draws a fixture or two in passing; this one exercises all five *indoor*
symbol domains at once: a fitted kitchen run with an island and a dashed
[overhead cabinet](/furniture#how-a-symbol-is-drawn), a plumbed bathroom, two furnished
bedrooms, a lounge and dining zone, and a utility room with a washer beside a dryer (the
same box, told apart by the chords across its drum).

Two things to read in the source rather than the drawing. **Not one piece carries a
`label`** — a drawn symbol ignores it, so the only text here comes from the rooms. And
**most carry no `size`** either: `against wall <id> … in <room>` takes the
[catalogued footprint](/furniture#the-symbol-catalogue) and derives the rotation from the
wall, so the whole kitchen, bathroom and both beds are written without a hand-computed
number. `offset` is where a piece's **centre** lands along the wall run, which is why a
fitted run reads as a sequence of centres rather than corners. `archlang validate` is clean;
`archlang validate --strict` reports exactly one warning, named in the source so it is not
mistaken for rot — `W_PATH_TOO_NARROW` on the walk into the kitchen, which the nav grid
measures at 400 mm through the 1200 mm opening, and which is left standing rather than
nudged away.

![furnished-flat.arch, compiled](/examples/furnished-flat.svg)

```arch
# The FURNITURE flagship — a two-bedroom flat, furnished with the whole symbol catalogue.
#
# Every other example draws a fixture or two in passing. This one is here for the drawn
# symbols themselves: thirty-eight of the eighty-three catalogued kinds, across all five
# indoor domains (bath, kitchen/utility, bedroom, living/dining, office/misc). It is a
# two-bedroom flat rather than a showroom, so the ceiling on that number is the FLOOR
# AREA, not the catalogue: every piece here is somewhere a real one would go, and the
# linter is what says so -- no piece floats off its wall, backs onto the room, blocks a
# door, eats a clearance or collides with anything. `arch validate --strict` reports
# exactly ONE warning, named here so it is not mistaken for rot: `W_PATH_TOO_NARROW` on
# the walk into the kitchen, which the nav grid measures at 400 mm through the 1200 mm
# opening. It appeared when that rule started reporting a MEASURED way in instead of a
# fabricated 0 mm, and it is left standing rather than nudged away -- moving a piece to
# silence a measurement is how a plan stops being evidence.
#
# Four things to read off the source rather than the picture:
#
#   1. NO LABELS. A drawn symbol ignores `label` — the word reaches `describe()` and
#      nothing else — so the only text in this drawing comes from the rooms.
#   2. MOST PIECES CARRY NO SIZE. `against wall <id> … in <room>` takes the catalogued
#      footprint (a fridge is 600 along the wall by 650 deep) and derives the rotation
#      from the wall, so a fitted kitchen, a plumbed bathroom and a bed with its
#      headboard on the right wall are written without a single hand-computed number.
#      `offset` is where the piece's CENTRE lands along the wall run, so a kitchen run
#      is written as a sequence of centres, not as corners.
#      Only the free-standing pieces — the L-sofa, armchair, coffee table, dining table,
#      island, desk, office chair, plant, rug, piano and lounger — declare a `size`,
#      because with no wall there is nothing to tell `along` from `depth`.
#   3. THE RUG IS AN UNDERLAY. It is walked on rather than round, so the pieces standing
#      on it raise no overlap warning and the circulation flood-fill crosses it. Rugs,
#      alone among the symbols, are drawn unfilled — nothing they are written after can
#      be hidden by them.
#   4. THREE PIECES ARE OVERHEAD, and the drawing says so with a dashed outline: the
#      range hood sits over the stove at the same offset, the mirror over the basin at
#      the same offset, and the wall cabinet over its length of the east wall. A thing
#      above the cut plane and a thing below it cannot collide, so those pairs raise no
#      overlap warning — but unlike the rug they are still obstacles to a walk, because a
#      body is taller than a wall unit's underside. That is the whole difference between
#      the two flags, and it is why this plan can draw a kitchen that is actually fitted.
plan "Furnished Flat" {
  units mm
  grid 50
  paper A3 portrait
  scale 1:50
  north up
  dims auto overall
  schedule rooms

  wall id=w_north  exterior  thickness 200 { (0,0) (8400,0) }
  wall id=w_east   exterior  thickness 200 { (8400,0) (8400,10800) }
  wall id=w_south  exterior  thickness 200 { (0,10800) (8400,10800) }
  wall id=w_west   exterior  thickness 200 { (0,0) (0,10800) }

  wall id=w_kit    partition thickness 100 { (5000,0) (5000,5600) }
  wall id=w_util   partition thickness 100 { (5000,3800) (8400,3800) }
  wall id=w_hall_n partition thickness 100 { (0,5600) (8400,5600) }
  wall id=w_hall_s partition thickness 100 { (0,6800) (8400,6800) }
  wall id=w_bed    partition thickness 100 { (3600,6800) (3600,10800) }
  wall id=w_bath   partition thickness 100 { (6400,6800) (6400,10800) }

  room id=r_live    at (0,0)       size 5000x5600 label "Living / Dining" uses living
  room id=r_kitchen at (5000,0)    size 3400x3800 label "Kitchen"         uses kitchen
  room id=r_util    at (5000,3800) size 3400x1800 label "Utility"
  room id=r_hall    at (0,5600)    size 8400x1200 label "Hall"            uses hall
  room id=r_bed1    at (0,6800)    size 3600x4000 label "Bedroom 1"       uses bedroom
  room id=r_bed2    at (3600,6800) size 2800x4000 label "Bedroom 2"       uses bedroom
  room id=r_bath    at (6400,6800) size 2000x4000 label "Bath"            uses bath

  door id=d_front on w_east   at 6200 width 900 hinge near start swing into r_hall
  door id=d_live  on w_hall_n at 900  width 900 hinge near start swing into r_live
  door id=d_util  on w_hall_n at 5700 width 800 hinge near start swing into r_util
  door id=d_bed1  on w_hall_s at 700  width 800 hinge near start swing into r_bed1
  door id=d_bed2  on w_hall_s at 5400 width 800 hinge near start swing into r_bed2
  door id=d_bath  on w_hall_s at 7000 width 800 hinge near start swing into r_bath
  opening         on w_kit    at 1600 width 1200

  window on w_north at 3300 width 1400
  window on w_north at 6650 width 1200
  window on w_west  at 1800 width 1400
  window on w_west  at 4300 width 1200
  window on w_south at 2600 width 1400
  window on w_south at 4400 width 1400

  # --- Living / Dining --------------------------------------------------------
  # The rug is an UNDERLAY: the coffee table and the L-sofa stand on it, so that overlap
  # is the arrangement rather than a collision, and the flood-fill walks straight over it.
  # It is also the only symbol here drawn with no fill, which is why it cannot hide the
  # sofa it is written after.
  furniture rug          at (200,600)   size 2700x2400 in r_live
  furniture tv_unit      against wall w_north offset 1000 in r_live
  furniture sofa_l       at (300,1300)  size 2400x1500 rotate 180 in r_live
  furniture coffee_table at (900,700)   size 1000x500 in r_live
  furniture armchair     at (3300,1100) size 900x850 rotate 90 in r_live
  furniture sun_lounger  at (1950,150)  size 1400x700 rotate 90 in r_live
  furniture plant        at (4300,300)  size 600x600 in r_live
  # A piano carries NO catalogued footprint on purpose, so it cannot be written
  # `against wall` — that form would derive a rotation from the wall and face the
  # keyboard into it. Placed by hand, like it is on a real drawing.
  furniture piano        at (250,3200)  size 1400x1400 in r_live
  furniture dining_table at (2700,3300) size 2100x2000 in r_live
  furniture radiator     against wall w_west offset 4300 in r_live

  # --- Kitchen: a fitted run of centres along the north wall -------------------
  furniture counter       against wall w_north offset 5650 size 1200x600 in r_kitchen
  furniture kitchen_sink  against wall w_north offset 6650 in r_kitchen
  furniture stove         against wall w_north offset 7350 in r_kitchen
  furniture range_hood    against wall w_north offset 7350 in r_kitchen
  furniture dishwasher    against wall w_north offset 7950 in r_kitchen
  furniture fridge        against wall w_east  offset 1000 in r_kitchen
  furniture upper_cabinet against wall w_east  offset 1800 in r_kitchen
  furniture microwave     against wall w_east  offset 2600 in r_kitchen
  furniture island        at (5800,2000) size 1600x900 in r_kitchen

  # --- Utility ----------------------------------------------------------------
  furniture washer       against wall w_east offset 4400 in r_util
  furniture dryer        against wall w_east offset 5000 in r_util
  furniture laundry_sink against wall w_util offset 1300 in r_util
  furniture water_heater against wall w_util offset 500  in r_util

  # --- Bedroom 1 --------------------------------------------------------------
  furniture double_bed against wall w_west   offset 9100  in r_bed1
  furniture nightstand against wall w_west   offset 7925  in r_bed1
  furniture nightstand against wall w_west   offset 10275 in r_bed1
  furniture wardrobe   against wall w_hall_s offset 2600  in r_bed1
  furniture vanity     against wall w_bed     offset 1700  in r_bed1
  furniture dresser    against wall w_bed     offset 3250  in r_bed1

  # --- Bedroom 2 --------------------------------------------------------------
  furniture bed          against wall w_bath offset 2950  in r_bed2
  furniture desk         against wall w_bed  offset 1000 size 1200x700 in r_bed2
  furniture office_chair at (4450,8200) size 600x600 in r_bed2
  furniture bookshelf    against wall w_bed  offset 3350 in r_bed2

  # --- Bath -------------------------------------------------------------------
  furniture shower  against wall w_bath  offset 1350 in r_bath
  furniture basin   against wall w_east  offset 7800 in r_bath
  furniture mirror  against wall w_east  offset 7800 in r_bath
  furniture wc      against wall w_east  offset 9200 in r_bath
  furniture bathtub against wall w_south offset 7450 in r_bath
  furniture bidet   against wall w_east  offset 8500 in r_bath

  title { project "Furnished Flat" drawn_by "ArchLang" date "2026-08-26" }
}
```

### Issued Sheet (drawing number, notes, door and window tags)

A three-room flat — **3 rooms, 54 m², 3 doors, 3 windows**, 9.2 × 6.2 m to the outer
faces — drawn the way a sheet leaves the office, on A3 at `scale 1:50`. The title block
carries a drawing **number** and **revision** (`title { … number "A-101" revision "B" }`),
the general notes sit in a numbered **NOTES** table (`notes "…"`), and
`schedule openings` puts a tag beside every door (`D01`…) and window (`W01`…) keyed to a
**DOOR & WINDOW SCHEDULE**: the room(s) each opening serves, its width, its kind and, for
a hinged leaf, its hand, read from the side it opens into (the EN/DIN convention). All of it is derived from the
plan and all of it draws only — `archlang describe` and `archlang lint` report exactly what
they would without those lines. A window's tag sits inside the room it serves; a door's
sits beside the doorway past the hinge jamb, clear of the swing and out of the passage.

![sheet-notes.arch, compiled](/examples/sheet-notes.svg)

```arch
# An issued sheet — the SHEET-ANNOTATION example.
#
# A drawing that leaves the office carries more than the plan: a drawing number and a
# revision in the title block, the general notes every contractor reads first, and a tag
# on every door and window keyed to a schedule. All three are opt-in sheet statements, and
# all three are presentation only — `describe()` and `lint()` learn nothing from them:
#
#   title { … number "A-101" revision "B" }   two more title-block rows
#   notes "…"                                 the NOTES table, numbered in source order
#   schedule openings                         D01…/W01… tags + the DOOR & WINDOW SCHEDULE
#
# The schedule is DERIVED, never typed: the rooms each opening serves (the same answer as
# `describe --json`'s `doors[].between` / `windows[].room`), its width, its kind and, for a
# hinged leaf, its HAND — read off the drawn leaf from the side it opens into, so it does
# not depend on which way the wall was written. A tag sits beside its opening: a window's
# inside the room, a door's past the hinge jamb, clear of the swing and out of the doorway.
plan "Sheet Notes Flat" {
  units mm
  grid 50
  paper A3 landscape
  scale 1:50
  north up
  dims auto all
  schedule rooms
  schedule openings
  notes "All dimensions in millimetres. Do not scale from this drawing."
  notes "Check every dimension on site before work starts."
  notes "Room areas are taken to wall centrelines."
  notes "Door hand is read from the side the door opens into."

  title { project "Sheet Notes Flat" drawn_by "ArchLang" date "2026-10-13" number "A-101" revision "B" }

  # ---- shell -------------------------------------------------------------------
  wall id=shell exterior  thickness 200 { (0,0) (9000,0) (9000,6000) (0,6000) close }
  wall id=w_mid partition thickness 100 { (5000,0) (5000,6000) }
  wall id=w_bed partition thickness 100 { (5000,3000) (9000,3000) }

  # ---- rooms -------------------------------------------------------------------
  room id=r_living at (0,0)    size 5000x6000 label "Living Room" uses living
  room id=r_bed    at (5000,0) size 4000x3000 label "Bedroom" uses bedroom
  room id=r_study  at (5000,3000) size 4000x3000 label "Study" uses office

  # ---- doors -------------------------------------------------------------------
  # The front door opens IN, so its tag cannot sit outside (the dimension chains live
  # there): it goes on the swing side, behind the hinge, where the leaf never reaches.
  door id=d_front on shell at 2500 width 1000 hinge left swing in
  door id=d_bed on w_mid at 1500 width 900 hinge right swing out
  door id=d_study sliding on w_mid at 4500 width 900

  # ---- windows -----------------------------------------------------------------
  window on shell at 26000 width 1800
  window on shell at 7000 width 1400
  window on shell at 13500 width 1400
}
```

### Suburban Bungalow (orientation, and doors that are not hinged)

A single-storey house — **8 rooms, 102 m², 6 doors, 7 windows** at `scale 1:100` — on a
plot that fronts the street on the **south**, in the southern hemisphere, and the whole
layout follows from that one declared fact. `site { street south hemisphere south }`
([site and orientation](/reference#site-and-orientation)) draws nothing; it names five
directions on `archlang describe --json --select site` — `street` (S), `back` (N),
`equator_side` (N), `sunrise_side` (E), `sunset_side` (W). Here `back` and `equator_side`
are the *same* side, which is why the house turns away from the road: the living room and
both bedrooms take the north facade, and the service band — entry, hall, bath, laundry,
kitchen — is stacked along the street. Delete Bedroom 2's north window and `archlang lint`
says so, through the one advisory rule that reads a site,
`W_ROOM_NOT_EQUATOR_FACING`. These are **drafting heuristics for an aspect, not a daylight
measurement**: there is no sun model, no latitude and no date anywhere in ArchLang.

The other half is the [door kinds](/reference#door-kinds-v1-25). Three of the six doors
are not hinged, each where a builder would actually put one: a **sliding** garden door
onto the deck, a **pocket** door to the bath so no leaf lands in a 1500 mm corridor, and a
**bifold** to the laundry. `slide right` on the pocket is load-bearing, not cosmetic — it
aims the panel at the 2200 mm of solid wall between its jamb and the laundry door, where
`slide left` would give it 500 mm against the 850 mm it needs and raise `W_POCKET_RUN`. A
non-hinged leaf sweeps nothing, so only the two bedroom doors carry a swing arc, and
`describe().doors[].kind` appears on exactly the three that are not the default.

![bungalow.arch, compiled](/examples/bungalow.svg)

```arch
# A single-storey suburban bungalow — the ORIENTATION-AND-OPENINGS example (v1.25).
#
# The plot fronts the street on the SOUTH and the garden on the NORTH, in the southern
# hemisphere. That one fact decides the whole layout, so the plan states it:
#
#   site { street south hemisphere south }
#
# `site` draws nothing. It names five directions on `arch describe --json --select site`:
# `street` (S), `back` (N — the garden), `equator_side` (N, because south of the equator
# the equator lies northward), `sunrise_side` (E) and `sunset_side` (W). Here `back` and
# `equator_side` are the SAME side, which is exactly why the house turns away from the
# road: the living room and both bedrooms take the north facade, and the service band —
# entry, hall, bath, laundry, kitchen — is stacked along the street. One advisory lint
# rule reads it, `W_ROOM_NOT_EQUATOR_FACING`; leave a bedroom with nothing but its east
# window and it will say so.
#
# These are DRAFTING HEURISTICS FOR AN ASPECT, not a daylight measurement. ArchLang has
# no sun model, no latitude and no date, so `equator_side` says which way a window looks
# and nothing whatever about how much light comes through it.
#
# The other half of the plan is the door KINDS. A leaf is not always hinged, and the
# three non-default kinds here are each where a builder would actually put one:
#
#   sliding  the garden door — the full-height slider onto the deck
#   pocket   the bath, off a 1500 mm corridor: no leaf in the hall, none in the bath
#   bifold   the laundry, folding into its own opening rather than over the machines
#
# A non-hinged leaf sweeps nothing, so `doorSwing()` returns null for all three and
# `W_SWING_OBSTRUCTED` cannot apply to them — only the two bedroom doors, which take the
# default `hinged` by writing no kind at all, carry a swing arc.
plan "Bungalow" {
  units mm
  # `grid 50` because every partition here is 100 thick and the plan is set out in
  # half-metres. It used to be a WORKAROUND as well: a fixture placed `flush` against a
  # 100 partition lands on a half-thickness (…50) coordinate, and the resolver used to
  # grid-snap that DERIVED coordinate like an authored one, so `grid 100` rounded it back
  # INTO the wall and raised `W_FURNITURE_WALL_COLLISION` on a correct plan. A derived
  # coordinate is no longer snapped, so the grid is free again — these numbers are
  # unchanged, and so is every byte this file draws.
  grid 50
  paper A3 landscape
  scale 1:100
  north up
  dims auto all
  schedule rooms

  site { street south hemisphere south }

  # ---- shell + structure ----------------------------------------------------
  # `w_spine` runs the full width and divides the garden band from everything behind it;
  # `w_service` does the same for the street band. Both are named because the doors below
  # are placed `on` them by RUN DISTANCE from the wall's first point, and because
  # `W_POCKET_RUN` measures the panel's cavity along one named segment.
  wall id=shell     exterior  thickness 200 { (0,0) (12000,0) (12000,8500) (0,8500) close }
  wall id=w_bed1    partition thickness 100 { (6000,0) (6000,4800) }
  wall id=w_bed2    partition thickness 100 { (9000,0) (9000,4800) }
  wall id=w_spine   partition thickness 100 { (0,4800) (12000,4800) }
  wall id=w_kitchen partition thickness 100 { (3000,4800) (3000,8500) }
  wall id=w_service partition thickness 100 { (3000,6300) (12000,6300) }
  wall id=w_bath    partition thickness 100 { (5800,6300) (5800,8500) }
  wall id=w_laundry partition thickness 100 { (8000,6300) (8000,8500) }

  # ---- rooms ----------------------------------------------------------------
  # Garden band (north): the rooms people sit and sleep in, every one of them on the
  # equator-facing facade. Street band (south): the rooms that do not need it.
  room id=r_living  at (0,0)       size 6000x4800 label "Living / Dining" uses living dining
  room id=r_bed1    at (6000,0)    size 3000x4800 label "Bedroom 1"       uses bedroom
  room id=r_bed2    at (9000,0)    size 3000x4800 label "Bedroom 2"       uses bedroom
  room id=r_kitchen at (0,4800)    size 3000x3700 label "Kitchen"         uses kitchen
  room id=r_hall    at (3000,4800) size 9000x1500 label "Hall"            uses hall circulation
  room id=r_bath    at (3000,6300) size 2800x2200 label "Bath"            uses bath
  room id=r_laundry at (5800,6300) size 2200x2200 label "Laundry"         uses utility
  room id=r_entry   at (8000,6300) size 4000x2200 label "Entry"           uses entry

  # ---- doors: one kind per situation ----------------------------------------
  # The front door is on the street facade, and it is the only exterior hinged leaf.
  door id=d_front at (10000,8500) width 1000 wall shell hinge left swing in

  # The garden door. A slider is what a deck opening actually is, and it is why the
  # living room can open this far to the north without a leaf eating the floor.
  # `slide left` runs the panel back toward the wall's start — onto the solid pier
  # between this opening and the window, not across the glass.
  door id=d_garden sliding at (4800,0) width 2000 wall shell slide left

  # Bedrooms keep the default: `hinged` is what you get by writing no kind at all, and
  # these two are the only doors in the plan with a swing arc to obstruct.
  door id=d_bed1 on w_spine at 7500  width 800 hinge left  swing in
  door id=d_bed2 on w_spine at 10500 width 800 hinge right swing in

  # The bath. A hinged leaf here has nowhere good to go: outward it blocks a 1500 mm
  # corridor, inward it fouls the basin. A pocket door answers both, and `slide` is not
  # cosmetic here — `w_service` is walked west to east, so `slide right` drives the panel
  # into the 2200 mm of solid wall between this jamb and the laundry door. `slide left`
  # would aim it at the corner instead, where the run is 500 mm against the 850 mm an
  # 800 mm panel needs, and `W_POCKET_RUN` would report it 350 mm short.
  door id=d_bath pocket on w_service at 900 width 800 slide right

  # The laundry, one room further along the same wall. A bifold folds into its own
  # opening, so it swings neither into the corridor nor over the machines.
  door id=d_laundry bifold on w_service at 3900 width 800

  # Cased openings: circulation needs no leaf.
  opening id=o_kitchen at (1500,4800)  width 2400 wall w_spine
  opening id=o_living  at (4500,4800)  width 1400 wall w_spine
  opening id=o_pantry  at (3000,5550)  width 900  wall w_kitchen
  opening id=o_entry   at (10000,6300) width 1200 wall w_service

  # ---- windows --------------------------------------------------------------
  # North first: every habitable room gets the equator-facing aspect, which is what keeps
  # `W_ROOM_NOT_EQUATOR_FACING` quiet. Then the ends, then the street side.
  window id=n_living at (900,0)      width 1400 wall shell
  window id=n_bed1   at (7500,0)     width 1600 wall shell
  window id=n_bed2   at (10500,0)    width 1600 wall shell
  window id=e_bed2   at (12000,2400) width 1200 wall shell   # sunrise side as well as north
  window id=e_hall   at (12000,5550) width 900  wall shell   # light to the corridor's end
  window id=w_sink   at (0,6600)     width 1200 wall shell   # sunset side, over the sink
  window id=s_bath   at (3600,8500)  width 600  wall shell

  # ---- fixtures -------------------------------------------------------------
  # No fixture here names an absolute coordinate against a wall, and none writes a
  # `rotate`. The kitchen run rides the shell's west segment, so `offset` is a distance
  # ALONG that segment (measured from its first point, (0,8500)) and both the position
  # and the quarter-turn are derived from the wall; the size comes from the catalogue.
  # The sanitary fixtures are anchored to their room instead, where `flush` measures
  # from the backing wall's inner FACE, so no half-thickness is written down by hand.
  furniture kitchen_sink against wall shell segment 3 offset 1900 in r_kitchen  # under the window
  furniture stove        against wall shell segment 3 offset 1100 in r_kitchen
  furniture fridge       against wall shell segment 3 offset 500  in r_kitchen

  furniture basin  in r_bath anchor left   flush size 450x600
  furniture wc     in r_bath anchor bottom flush size 400x700
  furniture shower in r_bath anchor right  flush size 900x900

  furniture counter in r_laundry anchor bottom flush size 1400x600

  furniture sofa  at (600,2600)  size 2200x900  label "Sofa"
  furniture table at (3600,1800) size 1400x900  label "Table"
  furniture bed   at (6200,200)  size 1500x2000 label "Bed"
  furniture bed   at (9200,200)  size 1500x2000 label "Bed"

  # ---- the eaves ------------------------------------------------------------
  # A bungalow is mostly roof, and on a hot plot it is the eave that shades the north
  # glazing rather than any glass. One line draws it: the outline is DERIVED by pushing
  # `shell`'s ring out by half its thickness plus 600, mitred at each corner, so it moves
  # if the walls do and there is no second set of coordinates to keep in step. `shell` is
  # the only closed `exterior` wall here, so it needs no naming — write
  # `roof overhang 600 wall shell` if a second one is ever added, or the plan gets
  # `E_ROOF_AMBIGUOUS` rather than a guess.
  #
  # It draws and nothing more: no `describe()` key, no lint rule, no schedule row. What
  # it does change is the PAGE — the drawing extent now includes the eaves, so the sheet
  # grows to hold them.
  roof overhang 600

  title {
    project "Suburban Bungalow"
    drawn_by "ArchCanvas"
    date "2026-08-13"
  }
}
```

### Courtyard House

A 16 × 11.5 m single-storey house whose rooms ring an open middle — **11 rooms, 163 m²,
10 doors, 15 windows** at `scale 1:100`.

The first thing to understand about the courtyard is that **it is not a room**. Nothing is
declared inside it: it is the hole the building wraps around, open to the sky, and calling
it a `room` would put 21 m² of weather into the floor schedule. It is enclosed by a single
three-segment partition, and everything that reads the plan — the schedule,
`describe().totals`, the circulation flood-fill — simply sees no floor there. The courtyard
is also the shape that breaks the naive way of answering *which way does this window look?*
The window on the courtyard's west wall has the gallery's floor to its west and open air to
its east, so it looks **east**, not west; ArchLang answers by probing one wall thickness off
each face of the window's own host segment and taking the side no room occupies, which is
exact here and on a sloped facade alike. Read it back with
`archlang describe --json --select windows`.

The wings are [`zone`s](/reference#zones-wings-and-departments-v1-22) — living 67.75 m²
over 4 rooms, private 65.5 m² over 4, service 29.75 m² over 3. A zone has **zero
geometry**: delete the wrappers and the drawing is byte-identical. Membership is declared,
never inferred from where a room happens to sit, which is why the schedule can carry a
subtotal per wing that no amount of looking at the drawing could have derived. The gallery
is one [polygon room](/reference#polygonal-rooms-v1-23) — a U wrapping three sides of the
courtyard, so the schedule lists one corridor rather than three — and because a U's
centroid falls outside it, its name is pinned by hand in the south arm.

![courtyard-house.arch, compiled](/examples/courtyard-house.svg)

```arch
# A 16.3 x 11.8 m single-storey house whose rooms ring an open courtyard.
#
# The courtyard is the whole point of the plan, and the first thing to understand about
# it is that IT IS NOT A ROOM. Nothing is declared at (5000,3500)-(11000,7000): it is
# the hole the building is wrapped around, open to the sky, and calling it a `room` with
# a `uses` would put 21 m2 of weather into the floor schedule. What encloses it is a
# single three-segment partition, `w_court`, and everything that reads the plan — the
# schedule, `describe().totals`, the circulation flood-fill — simply sees no floor there.
#
# THE FACING RULE. A courtyard is the shape that breaks the naive way of answering
# "which way does this window look?". Take `w_court_w`, the window on the courtyard's
# WEST wall. That wall is west of the courtyard, so a rule that compared the window with
# the middle of the building would call it west-facing. It is not: the gallery's floor is
# on the west side of that wall and the open air is on the east, so the window looks
# EAST. ArchLang answers it by probing one wall thickness off each face of the window's
# own host segment and taking the side no room occupies, which is exact on a courtyard
# and on a sloped facade alike. Read it back with:
#
#   arch describe examples/courtyard-house.arch --json --select windows
#
# and `w_court_w` reports `"facing": "east"`, `w_court_e` reports `"facing": "west"` —
# each looking ACROSS the courtyard, which is what someone standing at them sees.
#
# ZONES. `zone living { … }`, `zone private { … }` and `zone service { … }` group the
# room statements into wings. A zone has ZERO geometry: every statement inside resolves
# exactly as if the wrapper were deleted, same coordinates, same ids, byte-identical
# drawing. Membership is DECLARED, never inferred from where a room happens to sit —
# which is why the schedule below can carry a subtotal per wing that no amount of
# looking at the drawing could have derived. `arch describe --json --select zones` rolls
# the areas up; `--zone private` reads one wing on its own.
#
# The gallery is one POLYGON room, not three rectangles: a U wrapping the courtyard on
# west, south and east. Written as three rooms it would need three openings between
# pieces of the same corridor, and the schedule would list a corridor three times. Its
# centroid falls in the courtyard — a U is the shape whose average point is outside it —
# so the label is placed by hand in the south arm.
#
# Also here: `left-of` and `above`, the two relational directions no other example uses;
# `sliding` doors where a room opens onto the courtyard (a leaf that swings 900 mm into
# a 1500 mm gallery is a leaf in the way); and one `pocket` door for the same reason.
plan "Courtyard House" {
  units mm
  # `grid 50`: partitions are 100 thick, so a `flush` fixture lands on a ...50 face.
  grid 50
  paper A3 landscape
  scale 1:100
  north up
  dims auto all
  schedule rooms
  legend

  # The plot fronts the street on the WEST. `site` draws nothing; it names five
  # directions on `describe --json --select site` — street (W), back (E), equator_side
  # (S, north of the equator), sunrise_side (E), sunset_side (W). They are a DRAFTING
  # HEURISTIC for an aspect: ArchLang has no sun model, no latitude and no date.
  site { street west hemisphere north }

  # ---- shell, bands and the courtyard wall ----------------------------------
  wall id=shell exterior thickness 300 { (0,0) (16000,0) (16000,11500) (0,11500) close }

  # Two full-width partitions do most of the work: one behind the north band of rooms,
  # one in front of the south band of bedrooms. Each is a single wall because the line
  # is continuous even though what it separates changes along it.
  wall id=w_band_n partition thickness 200 { (0,3500) (16000,3500) }
  wall id=w_band_s partition thickness 200 { (0,8500) (16000,8500) }

  # The courtyard itself: one wall, three segments, mitred at both corners.
  wall id=w_court partition thickness 200 { (5000,3500) (5000,7000) (11000,7000) (11000,3500) }

  # 150, not 100: a pocket door needs a partition thick enough to swallow its panel.
  wall id=w_gal_w  partition thickness 150 { (3500,3500) (3500,8500) }
  wall id=w_gal_e  partition thickness 100 { (12500,3500) (12500,8500) }
  wall id=w_entry  partition thickness 100 { (3500,0) (3500,3500) }
  wall id=w_dining partition thickness 100 { (9000,0) (9000,3500) }
  wall id=w_kitch  partition thickness 100 { (12500,0) (12500,3500) }
  wall id=w_bath   partition thickness 100 { (12500,6000) (16000,6000) }
  wall id=w_bed1   partition thickness 100 { (5500,8500) (5500,11500) }
  wall id=w_bed2   partition thickness 100 { (11000,8500) (11000,11500) }

  # ---- rooms, grouped into wings by `zone` ----------------------------------
  zone living "Living wing" {
    room id=r_living at (3500,0) size 5500x3500 label "Living"  uses living
    # `left-of` places the entry against the living room's west face and `align top`
    # lines their north edges up — no second coordinate is written, and moving the
    # living room moves the entry with it.
    room id=r_entry  left-of r_living align top size 3500x3500 label "Entry" uses entry hall
    room id=r_dining at (9000,0) size 3500x3500 label "Dining" uses dining

    # The gallery: a U around the courtyard. Its centroid is in the open air, so the
    # label point is given explicitly (otherwise `W_ROOM_LABEL_OUTSIDE`).
    room id=r_gallery polygon
      (3500,3500) (5000,3500) (5000,7000) (11000,7000) (11000,3500)
      (12500,3500) (12500,8500) (3500,8500)
      label "Gallery" at (8000,7750) uses circulation hall
  }

  zone private "Private wing" {
    room id=r_bed1 at (0,8500)     size 5500x3000 label "Bedroom 1" uses bedroom
    room id=r_bed2 at (5500,8500)  size 5500x3000 label "Bedroom 2" uses bedroom
    room id=r_bed3 at (11000,8500) size 5000x3000 label "Bedroom 3" uses bedroom
    # `above` is the other new direction: the study sits directly on top of bedroom 1,
    # its own depth up from that room's north edge, west edges aligned.
    room id=r_study above r_bed1 align left size 3500x5000 label "Study" uses office
  }

  zone service "Service wing" {
    room id=r_kitchen at (12500,0)    size 3500x3500 label "Kitchen"   uses kitchen
    room id=r_bath    at (12500,3500) size 3500x2500 label "Bathroom"  uses bath
    room id=r_util    at (12500,6000) size 3500x2500 label "Utility"   uses utility
  }

  # ---- doors ----------------------------------------------------------------
  # `swing in` and not `swing into r_entry`: a relationally-placed room is deliberately
  # NOT a `swing into` target, because its rectangle is DERIVED — the compiler will not
  # resolve an authored clause against a position it computed for you. On an exterior
  # wall the explicit word is the plain one anyway: `in` is the shell's inward face.
  door id=d_front at (0,1750) width 1000 wall shell hinge left swing in

  # Onto the courtyard. A slider is what a full-height garden opening actually is, and
  # a hinged leaf here would sweep either the gallery or the courtyard threshold.
  door id=d_court_l sliding on w_band_n at 6000 width 1800 slide left
  door id=d_court_g sliding on w_court  at 6500 width 2000 slide right

  # The study, off a gallery only 1350 mm wide between faces. A pocket keeps both sides
  # clear; `slide right` drives the panel south into 2100 mm of blank wall (it needs
  # 850 — its own 800 plus a jamb), where `slide left` would aim it at the corner.
  door id=d_study pocket on w_gal_w at 2500 width 800 slide right

  door id=d_bath  on w_gal_e  at 750   width 800 swing into r_bath
  door id=d_util  on w_gal_e  at 4000  width 800 swing into r_util
  door id=d_kitch on w_kitch  at 1750  width 900 swing into r_kitchen
  door id=d_bed1  on w_band_s at 4500  width 900 swing into r_bed1
  door id=d_bed2  on w_band_s at 8000  width 900 swing into r_bed2
  door id=d_bed3  on w_band_s at 11750 width 900 swing into r_bed3

  # Cased openings: the public rooms read into one another with no leaf at all.
  opening id=o_entry  at (3500,1750)  width 1500 wall w_entry
  opening id=o_dining at (9000,1750)  width 1600 wall w_dining
  opening id=o_gal_w  at (4250,3500)  width 1200 wall w_band_n
  opening id=o_gal_e  at (11750,3500) width 1200 wall w_band_n

  # ---- windows: outer facades first -----------------------------------------
  window id=n_living  at (6000,0)      width 2000 wall shell
  window id=n_dining  at (10750,0)     width 1600 wall shell
  window id=n_kitchen at (14250,0)     width 1400 wall shell
  window id=e_kitchen at (16000,1750)  width 900  wall shell
  window id=e_bath    at (16000,4750)  width 600  wall shell
  window id=e_util    at (16000,7250)  width 600  wall shell
  window id=s_bed1    at (2750,11500)  width 1600 wall shell
  window id=s_bed2    at (8250,11500)  width 1600 wall shell
  window id=s_bed3    at (13500,11500) width 1600 wall shell
  window id=w_study   at (0,6000)      width 1400 wall shell
  window id=w_entry_l at (0,2950)      width 800  wall shell

  # ---- ...then the courtyard walls ------------------------------------------
  # These three are the facing demonstration. `w_court_w` is hosted on the courtyard's
  # WEST wall and faces EAST; `w_court_e` is on its east wall and faces WEST; the dining
  # room's and the living room's courtyard windows are on the band wall and face SOUTH,
  # which is the same equator side the bedrooms take from the outer facade.
  window id=w_court_l at (8000,3500)  width 1200 wall w_band_n
  window id=w_court_w at (5000,5250)  width 1200 wall w_court
  window id=w_court_e at (11000,5250) width 1200 wall w_court
  window id=w_court_d at (10000,3500) width 1600 wall w_band_n

  # ---- furniture ------------------------------------------------------------
  furniture sofa  in r_living anchor top flush size 2400x900 label "Sofa"
  furniture table in r_living centered size 1600x900 label "Table"

  furniture table in r_dining centered size 1600x900 label "Dining table"

  # The kitchen run rides the shell's north segment; `offset` is the distance of each
  # piece's CENTRE along it from (0,0), and the footprints come from the catalogue.
  furniture kitchen_sink against wall shell segment 0 offset 13100 in r_kitchen
  furniture stove        against wall shell segment 0 offset 13800 in r_kitchen
  furniture counter      against wall shell segment 0 offset 14400 in r_kitchen
  furniture fridge       against wall shell segment 0 offset 15000 in r_kitchen

  furniture bathtub in r_bath anchor top-right    flush size 1700x700
  furniture wc      in r_bath anchor bottom-left  flush size 400x700
  furniture basin   in r_bath anchor bottom-right flush size 600x450

  furniture counter in r_util anchor top flush size 1800x600
  # 900 x 600 of plumbed appliance bay, flush to the east wall. It is drawn 900 wide
  # because a label is drawn INSIDE its own footprint and the renderer has no metrics
  # to shrink one with: at 1:100 the word "Washer" is 825 mm long on the plan.
  # `rotate 90` puts the machine's BACK — where its supply and waste run — on the east
  # wall it stands against. `washer` is catalogued as a wall-requiring fixture as of
  # v1.28, so leaving it at rotate 0 reads as an appliance plumbed into open floor
  # (W_FIXTURE_BACK_TO_ROOM). The footprint is square-ish and the piece is a plain
  # rectangle, so nothing in the drawing moves.
  furniture washer  at (14950,7300) size 900x600 label "Washer" rotate 90

  # The study is placed relationally, so its pieces take absolute points rather than
  # anchors — the room's own rectangle is derived, and hanging furniture off a derived
  # edge would hide which of the two the author actually meant.
  furniture desk  at (300,3700) size 1600x700 label "Desk"
  furniture bookcase at (1500,8000) size 1800x400 label "Bookcase" rotate 180
  furniture bench    at (150,3000)  size 1200x400 label "Bench"

  furniture bed      in r_bed1 anchor bottom-left  flush size 1500x2000 label "Double"
  furniture wardrobe at (4000,11000) size 1400x350 label "Wardrobe" rotate 180
  furniture bed      in r_bed2 anchor bottom-right flush size 1500x2000 label "Double"
  furniture bed      in r_bed3 anchor bottom-right flush size 1500x2000 label "Double"

  # The one piece with no `in <room>` at all — because there is no room to name. A
  # planter standing in the courtyard is furniture in the open air, and every rule that
  # would ask which room it belongs to simply does not apply to it.
  furniture planter at (7250,4750) size 1500x1500 label "Planter"

  # ---- the eaves --------------------------------------------------------------
  # `shell` is the plan's one closed exterior wall — a plain rectangle. The courtyard
  # is cut by an INTERIOR partition, `w_court`, so it never enters this offset at all;
  # the concave gallery that wraps it is a room polygon, not a wall, and `roof` only
  # ever reads the wall ring.
  roof overhang 600

  title {
    project "Courtyard House"
    drawn_by "ArchLang"
    date "2026-08-16"
  }
}
```

### Townhouse (three storeys)

A 5.5 × 11 m terrace house written as three
[`level` blocks](/reference#levels-a-multi-storey-building-v1-21), `paper A3 portrait` at
`scale 1:50`. Each storey is **4 rooms and 55.64 m²** and becomes its own page — ground
(Hall, Living, Cloakroom, Kitchen/Dining), first (Landing, Bedroom 1, Bathroom, Bedroom 2),
second (Landing, Study, Shower room, Main bedroom). `archlang compile` writes
`townhouse.L1.svg`, `townhouse.L2.svg` and `townhouse.L3.svg`; the widget below shows page
1. Everything outside the blocks — `units`, `grid`, `paper`, `scale`, `dims auto` — is
stated once, because one building is issued on one sheet at one scale.

**The stair is one object written three times.** Ids are unique *within* a level, so the
same `id=st` on all three storeys is not a clash — it is the declaration that these are one
shaft, and it is what makes the upper floors legal: they have no front door of their own,
and `lint` still reads the first-floor landing as reached from outside because
[`describe().vertical`](/reference#vertical-circulation-stair-elevator-escalator-v1-21)
connects it to the storey below. Change one storey's id and you get `W_STAIR_UNMATCHED`
rather than a silently disconnected floor. `dir` is not decoration either: the flight runs
along its footprint's long axis and `dir up` is entered at that axis's larger-coordinate
end, which is also what the navigation grid reads — a stair obstructs circulation
everywhere except its entry edge, and that is why the ground-floor flight stops 1.15 m
short of the back wall. One flight-length further south, the only route from the front door
to the kitchen measured 300 mm on the nav grid against a 700 minimum, and `lint` refused the
plan with `W_PATH_TOO_NARROW`.

![townhouse.arch, compiled](/examples/townhouse.svg)

```arch
# A 5.5 x 11 m three-storey terrace house — one drawing per storey, from `level` blocks.
#
# A terrace house is the building type that only makes sense in section, and `level` is
# how a plan says so. Each block is an ordinary plan body and becomes its own page:
# `arch compile` writes townhouse.L1.svg, townhouse.L2.svg, townhouse.L3.svg, and
# `describe --json` grows a `levels[]`. Everything outside the blocks — `units`, `grid`,
# `paper`, `scale`, `dims auto` — is stated once and applies to every storey, because one
# building is issued on one sheet at one scale.
#
# THE STAIR IS ONE OBJECT WRITTEN THREE TIMES. Ids are unique WITHIN a level, so the same
# `id=st` on all three storeys is not a clash — it is the declaration that these are one
# shaft. That single fact is what makes the upper floors legal: they have no front door
# of their own, and `lint` still reads the first-floor landing as reached from outside,
# because `describe().vertical` connects it to the storey below. Change one storey's id
# and you get `W_STAIR_UNMATCHED` rather than a silently disconnected floor.
#
# `dir` is declared per storey and is not decoration: the flight runs along its footprint's
# LONG axis, and `dir up` is entered at that axis's larger-coordinate end. Here the long
# axis is y, so the ground-floor flight is entered from the back of the hall and climbs
# toward the street; the same footprint on the top floor carries `dir down`, entered at
# the opposite end. That is also what the nav grid reads — a stair obstructs circulation
# everywhere except its entry edge, which is why the flight stops 1.15 m short of the back
# wall. That strip is the only route from the front door to the kitchen, and one flight
# length further south it measured 350 mm of built depth — 300 on the nav grid, against a
# 700 minimum, so `lint` refused the plan with `W_PATH_TOO_NARROW`. Moving the flight is
# the fix, not widening a door: nothing on the route is a named element to widen.
#
# WHY THE PARTITIONS DON'T MOVE. The spine wall at x=3200, the wet-room wall at y=4600 and
# the cross wall at y=7000 stand on all three storeys in the same place, so the WC, the
# bathroom and the shower room sit one above the other on a single stack. That is not
# copy-paste: in a terrace the spine carries the floors and the drain fixes the wet rooms,
# so what a storey is free to change is only what its rooms are called and what is in them.
#
# Two door kinds earn their place rather than illustrate themselves. The ground-floor WC
# opens off a hall 1.75 m wide with a stair in it, so its leaf is a `pocket`: it takes no
# corridor and none of the 7.7 m2 cloakroom, and `slide left` drives the panel north into
# 2.65 m of blank spine wall (it needs 850 — its own 800 plus a jamb). The garden door is
# a `sliding` leaf, which is what a patio door actually is. A hinged one swinging in would
# cross the dining table; swung out it would sweep the paving — and the dimension chain a
# builder reads the back elevation from. No non-hinged leaf sweeps anything: `doorSwing()`
# returns null for every kind but `hinged`, so `W_SWING_OBSTRUCTED` cannot even apply to
# it. A `sliding` door is drawn as a BYPASS pair — two leaves on two tracks inside the
# reveal, so it needs no blank wall to park on at all — and `slide` only says which of the
# two is the fixed one: `right` is measured along the host wall's traversal, and the shell's
# south run is walked east to west, so the western leaf is fixed and the eastern one travels.
plan "Townhouse" {
  units mm
  # `grid 50`: the spine partition is 200 and the cross walls 100, so every `flush`
  # fixture lands on a ...50 or ...00 face — coordinates this grid can express exactly.
  grid 50
  paper A3 portrait
  scale 1:50
  north up
  dims auto all

  # Outer faces 5500 x 11000; 4900 clear between the party walls. A terrace is blind on
  # both flanks, so every window in the house is on the front or the back — which is the
  # single fact that shapes the plan: the deep middle of each floor takes the stair and the
  # wet room, and the two ends take the rooms people sit in.
  let W = 5200        # shell centerlines
  let D = 10700
  let T = 300         # exterior thickness
  let SPINE = 3200    # the loadbearing spine, front to mid-plan
  let WET   = 4600    # the wet-room wall — the same line on all three storeys
  let BACK  = 7000    # the cross wall in front of the back room

  level 1 "Ground floor" {
    wall id=shell   exterior  thickness T   { (0,0) (W,0) (W,D) (0,D) close }
    wall id=spine   partition thickness 200 { (SPINE,0) (SPINE,BACK) }
    wall id=w_wet   partition thickness 100 { (0,WET) (SPINE,WET) }
    wall id=w_back  partition thickness 100 { (0,BACK) (W,BACK) }

    room id=r_hall    at (SPINE,0)  size 2000x7000 label "Hall"           uses hall entry circulation
    room id=r_living  at (0,0)      size 3200x4600 label "Living"         uses living
    room id=r_wc      at (0,WET)    size 3200x2400 label "Cloakroom"      uses wc
    room id=r_kitchen at (0,BACK)   size 5200x3700 label "Kitchen/Dining" uses kitchen dining

    door id=d_front  at (4200,0) width 1000 wall shell hinge left swing into r_hall
    door id=d_living on spine at 2300 width 900 swing into r_living
    # The pocket. `slide left` runs the panel back toward the spine's first point,
    # (3200,0) — north, into solid wall — not south toward the corner 800 mm away.
    door id=d_wc pocket on spine at 5800 width 800 slide left
    # The patio slider: two bypass leaves in one 1.2 m reveal, nothing swept, nothing
    # parked on the wall. `slide right` is west here, so the western leaf is the fixed one.
    door id=d_back sliding at (4200,D) width 1200 wall shell slide right

    opening id=o_kitchen at (4200,BACK) width 1200 wall w_back

    window id=n_living  at (1600,0) width 1600 wall shell
    window id=s_kitchen at (1400,D)  width 1600 wall shell

    furniture media in r_living anchor top-left flush size 1200x400 label "Media"
    furniture sofa  in r_living anchor bottom    flush size 2000x850 label "Sofa"

    furniture wc    in r_wc anchor bottom-left  flush size 400x700
    furniture basin in r_wc anchor bottom-right flush size 600x450

    # The kitchen run rides the shell's WEST segment (index 3, walked from (0,10700)
    # northward), so `offset` is a distance along that segment and both the position and
    # the quarter-turn come from the wall; the footprints come from the catalogue.
    furniture kitchen_sink against wall shell segment 3 offset 1200 in r_kitchen
    furniture stove        against wall shell segment 3 offset 2100 in r_kitchen
    furniture fridge       against wall shell segment 3 offset 2900 in r_kitchen
    furniture table in r_kitchen centered size 1400x900 label "Table"

    stair id=st at (4150,2600) size 900x3200 dir up
  }

  level 2 "First floor" {
    wall id=shell  exterior  thickness T   { (0,0) (W,0) (W,D) (0,D) close }
    wall id=spine  partition thickness 200 { (SPINE,0) (SPINE,BACK) }
    wall id=w_wet  partition thickness 100 { (0,WET) (SPINE,WET) }
    wall id=w_back partition thickness 100 { (0,BACK) (W,BACK) }

    room id=r_landing at (SPINE,0)  size 2000x7000 label "Landing"   uses circulation hall
    room id=r_bed1    at (0,0)      size 3200x4600 label "Bedroom 1" uses bedroom
    room id=r_bath    at (0,WET)    size 3200x2400 label "Bathroom"  uses bath
    room id=r_bed2    at (0,BACK)   size 5200x3700 label "Bedroom 2" uses bedroom

    # No exterior door on this floor. The stair below carries the same id, so the access
    # graph reaches this landing through the shaft.
    door id=d_bed1 on spine  at 1800 width 800 swing into r_bed1
    door id=d_bath on spine  at 5200 width 800 swing into r_bath
    door id=d_bed2 on w_back at 4200 width 800 swing into r_bed2

    window id=n_bed1    at (1600,0) width 1600 wall shell
    window id=n_landing at (4200,0) width 1200 wall shell
    window id=s_bed2    at (2600,D)  width 1800 wall shell

    furniture bed      in r_bed1 anchor bottom-left  flush size 1400x2000 label "Double"
    furniture wardrobe in r_bed1 anchor bottom-right flush size 1200x600  label "Wardrobe"

    # The three pieces go round the three walls the door is not on, so the leaf opens onto
    # bare floor: bath under the back wall, WC beside it on the spine, basin by the window.
    furniture bathtub in r_bath anchor bottom-left  flush size 1700x700
    furniture wc      in r_bath anchor bottom-right flush size 400x700
    furniture basin   in r_bath anchor top-left     flush size 600x450

    furniture bed      in r_bed2 anchor bottom-left flush size 1500x2000 label "Double"
    furniture wardrobe in r_bed2 anchor top-left    flush size 1400x600  label "Wardrobe"
    furniture chest    at (4550,9200) size 500x1000 label "Chest" in r_bed2

    stair id=st at (4150,2600) size 900x3200 dir up
  }

  level 3 "Second floor" {
    wall id=shell  exterior  thickness T   { (0,0) (W,0) (W,D) (0,D) close }
    wall id=spine  partition thickness 200 { (SPINE,0) (SPINE,BACK) }
    wall id=w_wet  partition thickness 100 { (0,WET) (SPINE,WET) }
    wall id=w_back partition thickness 100 { (0,BACK) (W,BACK) }

    room id=r_landing at (SPINE,0)  size 2000x7000 label "Landing"      uses circulation hall
    room id=r_study   at (0,0)      size 3200x4600 label "Study"        uses office
    room id=r_bath    at (0,WET)    size 3200x2400 label "Shower room"  uses bath
    room id=r_master  at (0,BACK)   size 5200x3700 label "Main bedroom" uses bedroom

    door id=d_study  on spine  at 1800 width 800 swing into r_study
    door id=d_bath   on spine  at 5200 width 800 swing into r_bath
    door id=d_master on w_back at 4200 width 900 swing into r_master

    window id=n_study   at (1600,0) width 1600 wall shell
    window id=n_landing at (4200,0) width 1200 wall shell
    window id=s_master  at (2600,D)  width 1800 wall shell

    furniture desk     in r_study anchor top-left    flush size 1600x700 label "Desk"
    furniture bookcase in r_study anchor bottom-left flush size 1800x400 label "Bookcase"

    # The same three walls as the bathroom below, and a shower where the bath is — the
    # drain, the vent and the door all stay where the storey under them put them.
    furniture shower in r_bath anchor bottom-left  flush size 900x900
    furniture wc     in r_bath anchor bottom-right flush size 400x700
    furniture basin  in r_bath anchor top-left     flush size 600x450

    furniture bed      in r_master anchor bottom-left flush size 1600x2100 label "Double"
    furniture wardrobe in r_master anchor top-left    flush size 1800x600  label "Wardrobe"
    furniture chest    at (4550,8500) size 500x1000   label "Chest" in r_master

    stair id=st at (4150,2600) size 900x3200 dir down

    # The eaves, on the top storey only — `roof` is per-level like everything else
    # here, and this level's `shell` is the plan's one closed exterior ring on THIS
    # page, so no `wall` clause is needed.
    roof overhang 400
  }

  title {
    project "Three-storey Terrace"
    drawn_by "ArchLang"
    date "2026-08-16"
  }
}
```

### Two-storey house

A compact house written as two
[`level` blocks](/reference#levels-a-multi-storey-building-v1-21) — **3 rooms and 56 m² on
the ground floor**, on `paper A3 landscape`. No `scale` is written: the sheet auto-fits,
and the title block reports the 1:50 it chose. It compiles to two sheets
(`two-storey.L1.svg`, `two-storey.L2.svg`), each with its own dimension chains and a title
block stamped `LEVEL 1 — Ground floor` / `LEVEL 2 — First floor`. The settings and the
`let`s sit *outside* the levels and apply to both: one building, one sheet spec, one scale.
It is the smaller sibling of the townhouse above — the two-page version of the same idea,
without the stack of wet rooms.

![two-storey.arch, compiled](/examples/two-storey.svg)

```arch
# A compact two-storey house — one drawing per storey, from `level` blocks.
#
# Settings and the plan-global `let`s live OUTSIDE the levels and apply to every one of
# them: one building is issued on one sheet at one scale. Each level's body is an ordinary
# plan body, and ids are unique WITHIN a level — so the `stair` below exists on both floors
# at the same coordinates, and that shared id is what declares one shaft: `describe --json`
# reports it under `vertical.connections`, and it is why the first floor needs no front
# door of its own to lint clean.
#
# THE HEIGHTS DRAW NOTHING, AND THAT IS THE POINT. A plan is a horizontal cut, so the
# `height` below moves no byte of either drawing — `examples/two-storey.svg` is the same
# file it was before these clauses existed. What the datum feeds is `describe --json
# --select heights` and `arch compile --view axon`. It is written in three tiers, narrowing:
# the plan's `height 2700` is every storey's floor-to-floor, the upper `level … height 3000`
# overrides it for that storey alone, and a `sill`/`head` on a window bounds that one piece
# of glazing. `heights.elevation` ACCUMULATES the storeys below rather than multiplying, so
# the first floor sits at 2700 — the ground floor's height — not at 2 x 3000. Most windows
# here write nothing and take the 900/2100 defaults; the two that do write are the two a
# drawing cannot tell you about.
plan "Two-storey house" {
  units mm
  grid 50
  height 2700         # floor-to-floor for every storey that does not override it
  paper A3 landscape
  north up
  dims auto all

  let W = 8000        # outer width (wall centerlines)
  let D = 7000        # outer depth
  let T = 200         # exterior wall thickness
  let HALL = 2200     # the stair hall / landing runs the full depth on the left

  level 1 "Ground floor" {
    wall id=shell exterior thickness T { (0,0) (W,0) (W,D) (0,D) close }
    wall id=hall_w partition thickness 100 { (HALL,0) (HALL,D) }
    wall id=kitchen_w partition thickness 100 { (HALL,3600) (W,3600) }

    room id=hall at (0,0) size 2200x7000 label "Hall" uses hall
    room id=living at (2200,0) size 5800x3600 label "Living" uses living
    room id=kitchen at (2200,3600) size 5800x3400 label "Kitchen" uses kitchen

    door id=front on shell at 21900 width 1000 swing into hall
    door id=d_living on hall_w at 1800 width 900 swing into living
    door id=d_kitchen on hall_w at 5300 width 900 swing into kitchen

    # A tall window onto the garden: 300 off the floor and up to 2400, inside this
    # storey's 2700. The kitchen window beside it writes neither and takes 900/2100.
    window id=w_living on shell at 5000 width 1800 sill 300 head 2400
    window id=w_kitchen on shell at 13300 width 1200

    furniture sofa in living anchor top flush inset 300 size 2000x850 label "Sofa"
    furniture kitchen_sink against wall kitchen_w offset 1200 in kitchen
    stair id=stair at (100,1500) size 900x2600 dir up
  }

  # A taller first floor than the ground: `height` in the level HEADER overrides the plan's
  # 2700 for this storey only (a `height` in the body would be E_LEVEL_MIX).
  level 2 "First floor" height 3000 {
    wall id=shell exterior thickness T { (0,0) (W,0) (W,D) (0,D) close }
    wall id=hall_w partition thickness 100 { (HALL,0) (HALL,D) }
    wall id=bath_w partition thickness 100 { (0,4600) (HALL,4600) }
    wall id=bed_w partition thickness 100 { (HALL,3600) (W,3600) }

    room id=landing at (0,0) size 2200x4600 label "Landing" uses circulation
    room id=bath at (0,4600) size 2200x2400 label "Bath" uses bath
    room id=bed1 at (2200,0) size 5800x3600 label "Bedroom 1" uses bedroom
    room id=bed2 at (2200,3600) size 5800x3400 label "Bedroom 2" uses bedroom

    # No exterior door up here: the `stair` below carries the same id as the ground
    # floor's, so the access graph reads the landing as reached from outside via the
    # storey below. (Before vertical circulation was modelled this floor needed a
    # made-up balcony door to lint clean.)
    door id=d_bed1 on hall_w at 1800 width 900 swing into bed1
    door id=d_bed2 on hall_w at 4100 width 900 swing into bed2
    door id=d_bath on bath_w at 1100 width 800 swing into bath

    window id=w_bed1 on shell at 5000 width 1500
    window id=w_bed2 on shell at 13300 width 1200
    # A bathroom window set high for privacy: `sill` alone, so the head still defaults to
    # 2100 — an authored bound and a defaulted one on the same opening.
    window id=w_bath on shell at 24200 width 600 sill 1500

    furniture bed in bed1 anchor top-right flush inset 300 size 1500x2000 label "Double"
    furniture bed in bed2 anchor bottom flush inset 300 size 1000x2000 label "Single"
    furniture wc in bath anchor bottom-left flush size 400x700
    furniture basin in bath anchor bottom-right flush size 600x450
    stair id=stair at (100,1500) size 900x2600 dir down

    # The landing looks down into the hall below over a balustrade: there is no floor
    # here, so the plan says so. A `void` is drawn (dashed rectangle + both diagonals)
    # and it OBSTRUCTS circulation — you cannot walk across a hole — with the walkable
    # halo suppressed on every edge, because you can stand at the railing. It does NOT
    # come off the landing's area: that number is the room's floor area and it is what
    # the schedule and `describe --json` both report, so subtracting the well here would
    # leave the drawing and the table disagreeing. `describe --json` lists it under
    # `voids[]` with the room it falls in, which is where a net figure comes from.
    void id=gallery at (100,200) size 900x1100

    # The eaves, on the top storey only — this level's `shell` is the plan's one
    # closed exterior ring on THIS page, so no `wall` clause is needed.
    roof overhang 400
  }

  title {
    project "Two-storey house"
    drawn_by "ArchLang"
    date "2026-07-26"
  }
}
```

## Public buildings

### Museum (a real sheet, at a real scale)

A ~100 × 60 m single-level museum — **14 rooms, 6000 m², 7 doors, 9 windows** — and the
large-building flagship. Everything above is a dwelling, where sizing every annotation as
a fraction of the drawing happens to look right; at 100 m it does not, and room labels come
out metres tall. So this plan declares a **sheet**:
[`paper A1 landscape`](/reference#paper-and-scale-the-sheet) +
[`scale 1:200`](/reference#paper-and-scale-the-sheet), which makes every annotation a fixed
size **on that sheet** — 3.5 mm room labels and 0.5 mm wall lines whatever the building
measures. It also drives `dims auto all` (the GB/T three-chain exterior dimensioning), a
column grid written with `for`, and fixture rows placed by `against wall <id> offset …` so
no coordinate is hand-computed.

![museum.arch, compiled](/examples/museum.svg)

```arch
# A ~100 x 60 m single-level museum — the LARGE-BUILDING flagship.
#
# What this example is for: everything else in examples/ is a dwelling, where the old
# "every size is a fraction of the drawing" rule happens to look right. At 100 m it does
# not — room labels come out metres tall. So this plan declares a SHEET:
#
#   paper A1 landscape   the drawing is issued on a real 841 x 594 mm sheet
#   scale 1:200          which makes every annotation a fixed size ON THAT SHEET
#                        (3.5 mm room labels, 0.5 mm wall lines, 15 mm margins)
#
# A1 at 1:200 is the conventional GB/T presentation for a public building of this size:
# the 100 x 60 m footprint lands at 500 x 300 mm of ink with room for the three
# dimension chains and the title block. Drop the `scale` line and the sheet auto-fits
# the finest of 1:50 / 1:100 / 1:200 / 1:500 that fits — here, the same 1:200.
plan "City Museum" {
  units mm
  grid 100
  paper A1 landscape
  scale 1:200
  north up
  dims auto all

  # ---- shell + structure ----------------------------------------------------
  wall id=shell exterior thickness 300 { (0,0) (100000,0) (100000,60000) (0,60000) close }

  # The spine: one 6 m corridor the full width of the building, with everything
  # opening off it. Its two faces are the only walls the galleries need.
  wall id=spine_n partition thickness 200 { (0,26000) (100000,26000) }
  wall id=spine_s partition thickness 200 { (0,32000) (100000,32000) }

  # North row dividers (galleries, then the service wing at the east end).
  wall partition thickness 200 { (26000,0) (26000,26000) }
  wall partition thickness 200 { (52000,0) (52000,26000) }
  wall partition thickness 200 { (78000,0) (78000,26000) }
  wall partition thickness 200 { (78000,14000) (100000,14000) }

  # South row dividers (galleries, entrance hall, the WC stack and the cafe).
  wall partition thickness 200 { (30000,32000) (30000,60000) }
  wall partition thickness 200 { (48000,32000) (48000,60000) }
  wall partition thickness 200 { (70000,32000) (70000,60000) }
  wall id=wc_west partition thickness 200 { (76000,32000) (76000,60000) }
  wall partition thickness 200 { (86000,32000) (86000,60000) }
  wall id=wcw_s partition thickness 200 { (76000,42000) (86000,42000) }
  wall id=wcm_s partition thickness 200 { (76000,52000) (86000,52000) }

  # ---- north row ------------------------------------------------------------
  room id=g1 at (0,0) size 26000x26000 label "Gallery 1"
  room id=g2 at (26000,0) size 26000x26000 label "Gallery 2"
  room id=g3 at (52000,0) size 26000x26000 label "Gallery 3"
  room id=store at (78000,0) size 22000x14000 label "Store" uses storage
  room id=office at (78000,14000) size 22000x12000 label "Offices" uses office

  # ---- the spine ------------------------------------------------------------
  room id=spine at (0,26000) size 100000x6000 label "Concourse" uses circulation hall

  # ---- south row ------------------------------------------------------------
  room id=g4 at (0,32000) size 30000x28000 label "Gallery 4"
  room id=hall at (30000,32000) size 18000x28000 label "Entrance Hall" uses entry hall
  room id=g5 at (48000,32000) size 22000x28000 label "Gallery 5"
  room id=link at (70000,32000) size 6000x28000 label "Lobby" uses circulation
  room id=wcw at (76000,32000) size 10000x10000 label "WC Women" uses wc
  room id=wcm at (76000,42000) size 10000x10000 label "WC Men" uses wc
  room id=plant at (76000,52000) size 10000x8000 label "Plant" uses utility
  room id=cafe at (86000,32000) size 14000x28000 label "Cafe" uses dining

  # ---- openings: the public route -------------------------------------------
  # Main entrance (south facade) into the hall, then a wide threshold to the spine.
  door at (39000,60000) width 2000 swing out
  opening at (39000,32000) width 4000

  # Galleries read off the spine through cased openings, not doors.
  opening at (13000,26000) width 3000
  opening at (39000,26000) width 3000
  opening at (65000,26000) width 3000
  opening at (15000,32000) width 3000
  opening at (59000,32000) width 3000
  opening at (73000,32000) width 3000
  opening at (93000,32000) width 4000

  # Service wing: offices off the spine, the store through the offices, and its own
  # exterior service door on the north facade.
  door at (89000,26000) width 1200
  door at (89000,14000) width 1200
  door at (89000,0) width 1200 swing out

  # The WC stack + plant room open off the lobby.
  door at (76000,37000) width 1200
  door at (76000,47000) width 1200
  door at (76000,56000) width 1200

  # ---- glazing --------------------------------------------------------------
  window at (13000,0) width 4000
  window at (39000,0) width 4000
  window at (65000,0) width 4000
  window at (0,13000) width 3000
  window at (0,46000) width 3000
  window at (100000,20000) width 3000
  window at (100000,45000) width 4000
  window at (15000,60000) width 4000
  window at (59000,60000) width 4000

  # ---- structure: gallery column bays ---------------------------------------
  # Four columns per north gallery, on the bay third-points (grid-snapped).
  let BAY = 8700
  for i in 0..3 {
    let x0 = i * 26000
    column at (x0 + BAY, 8700) size 600x600
    column at (x0 + 2 * BAY, 8700) size 600x600
    column at (x0 + BAY, 17400) size 600x600
    column at (x0 + 2 * BAY, 17400) size 600x600
  }
  # The two deep south galleries carry their own bays.
  for i in 0..2 {
    column at (10000 + i * 10000, 41000) size 600x600
    column at (10000 + i * 10000, 51000) size 600x600
    column at (54000 + i * 12000, 41000) size 600x600
    column at (54000 + i * 12000, 51000) size 600x600
  }

  # ---- sanitary fixtures ----------------------------------------------------
  # `against wall <id>` places each piece BY CONSTRUCTION: the offset runs along that
  # wall, the depth comes off its face, the rotation is derived from which side it lands
  # on (`in <room>` picks the side), and a catalogued fixture needs no `size`. So a row
  # of WCs is three offsets — no coordinates, no half-thickness arithmetic, and no
  # fixture can end up floating or facing its own wall.
  furniture wc against wall wcw_s offset 1500 in wcw
  furniture wc against wall wcw_s offset 5000 in wcw
  furniture wc against wall wcw_s offset 8500 in wcw
  furniture basin against wall spine_s offset 79000 in wcw
  furniture basin against wall spine_s offset 83000 in wcw

  furniture wc against wall wcm_s offset 1500 in wcm
  furniture wc against wall wcm_s offset 5000 in wcm
  furniture wc against wall wcm_s offset 8500 in wcm
  furniture basin against wall wcw_s offset 3000 in wcm
  furniture basin against wall wcw_s offset 7000 in wcm

  # ---- cafe fit-out ---------------------------------------------------------
  furniture counter against wall spine_s offset 90000 size 6000x800 label "Servery" in cafe
  furniture table at (89000,45000) size 1600x1600 label "Table" in cafe
  furniture table at (93000,45000) size 1600x1600 label "Table" in cafe

  # ---- reception ------------------------------------------------------------
  furniture desk  at (33000,38000) size 1800x800 label "Reception desk" in hall
  furniture chair at (35200,39200) size 500x500 in hall
  furniture chair at (33000,40500) size 500x500 in hall
  furniture plant at (44000,55000) size 700x700 in hall

  # ---- gallery seating & planting --------------------------------------------
  # One bench and one plant per gallery — sparse-elegant, not empty — kept clear of
  # the bay columns above.
  furniture bench at (19000,20000) size 1800x500 in g1
  furniture plant at (23000,2000)  size 700x700  in g1
  furniture bench at (45000,20000) size 1800x500 in g2
  furniture plant at (49000,2000)  size 700x700  in g2
  furniture bench at (71000,20000) size 1800x500 in g3
  furniture plant at (75000,2000)  size 700x700  in g3
  furniture bench at (24000,50000) size 1800x500 in g4
  furniture plant at (26000,35000) size 700x700  in g4
  furniture bench at (60000,55000) size 1800x500 in g5
  furniture plant at (49000,35000) size 700x700  in g5

  title { project "City Museum" drawn_by "ArchLang" date "2026-07-26" }
}
```

### Harbour Aquarium (curves)

A 60 × 40 m public aquarium — **8 rooms, 2028.83 m², 3 doors, 6 windows**, A2 at 1:200 —
and the [curved-geometry](/reference#curved-walls-arc-edges-v1-24) flagship. Everything
above is rectilinear; an aquarium is the building type that is not. Its centrepiece is a
cylindrical tank you walk around, written as a
[circular room](/reference#circular-rooms-v1-24) (`room circle at (cx,cy) radius R`, an
exact 201.06 m² = πR²) inside a drum wall made of two `arc` edges, and its public frontage
turns the south-east corner as a quarter circle of R12000 instead of mitring it.

Two things to read off the drawing. The curved faces are **true arcs** — SVG `A` commands,
native DXF `ARC` entities — so the rotunda never looks faceted however far you zoom in. And
because a linear dimension chain cannot describe an arc,
[`dims auto` dimensions the round things](/reference#dimensioning-a-curve-v1-24) the way
GB/T 50104 does: an `R` leader per distinct arc and a `φ` call-out across the circular room,
while the three exterior chains stay on the straight facades. Both rotunda doors sit **on**
the curve — `on rotunda at 25%` walks the wall by run length, so an arc contributes its arc
length rather than its chord — and their leaves swing off the tangent at the doorway.

![aquarium.arch, compiled](/examples/aquarium.svg)

```arch
# A ~60 x 40 m public aquarium — the CURVED-GEOMETRY flagship (v1.24).
#
# Everything else in examples/ is rectilinear, because until v1.24 the language was. An
# aquarium is the building type that is NOT: its centrepiece is a cylindrical tank you
# walk around, and its public frontage bows. This plan exercises both curve forms and
# nothing else that is new:
#
#   arc (x,y) radius R [cw|ccw] [major]   a CURVED WALL EDGE, from the previous vertex to
#                                         this one along a circle of radius R. Default is
#                                         the minor arc turning `ccw` as drawn.
#   room circle at (cx,cy) radius R       a CIRCULAR FLOOR. Its area is measured exactly
#                                         (piR^2), never from the tessellation the grid
#                                         and adjacency layers use.
#
# Two things to read off the drawing. (1) The curved faces are TRUE arcs — SVG `A`
# commands, DXF `ARC` entities — so the rotunda never looks faceted at any zoom.
# (2) `dims auto` cannot chain a curve, so it dimensions the round things the way GB/T
# 50104 does: an `R` leader per distinct arc and a `phi` call-out across the circular
# room, while the linear chains stay on the straight facades where they belong.
#
# Issued on A2 at 1:200, the conventional GB/T presentation for a public building this
# size, with the positioning axes on the straight structural grid only.
plan "Harbour Aquarium" {
  units mm
  grid 100
  paper A2 landscape
  scale 1:200
  north up
  dims auto all
  schedule rooms

  # ---- shell: three straight facades and one curved one ---------------------
  #
  # The south-east corner is turned as a quarter circle of R12000 rather than mitred —
  # the curved public frontage facing the harbour. `cw` puts the centre on the RIGHT of
  # travel (the wall is walked (60000,28000) -> (48000,40000), heading south-west), which
  # is the inside of the building, so the facade bows OUTWARD. Swap it to `ccw` and the
  # same two endpoints give the concave corner instead.
  wall id=shell exterior thickness 300 {
    (0,0)
    (60000,0)
    (60000,28000)
    arc (48000,40000) radius 12000 cw
    (0,40000)
    close
  }

  # ---- the cylindrical tank: one full circle from two semicircular arcs -----
  #
  # There is no `close`-an-arc form on purpose: a closed curve is written as the two
  # halves it is, which keeps every edge's radius visible in the source. Both are the
  # default `ccw`, so the wall is walked east -> south -> west -> north -> east, and the
  # `hinge`/`swing` of a door on it reads against THAT direction of travel (the tangent
  # at the doorway), exactly as it would on a straight run.
  #
  # The circle is tangent to the concourse at its south point (30000,22000) and to the
  # plant room at its north point (30000,6000), so a door at either lands on a real
  # room boundary and connects two spaces.
  wall id=rotunda partition thickness 200 {
    (38000,14000)
    arc (22000,14000) radius 8000
    arc (38000,14000) radius 8000
  }

  # ---- straight structure ---------------------------------------------------
  wall id=concourse_n partition thickness 200 { (0,22000) (21000,22000) }
  wall id=concourse_n2 partition thickness 200 { (39000,22000) (60000,22000) }
  wall id=concourse_s partition thickness 200 { (0,30000) (60000,30000) }
  wall partition thickness 200 { (21000,0) (21000,22000) }
  wall partition thickness 200 { (39000,0) (39000,22000) }
  wall partition thickness 200 { (21000,6000) (39000,6000) }
  wall id=foyer_w partition thickness 200 { (20000,30000) (20000,40000) }
  wall id=foyer_e partition thickness 200 { (40000,30000) (40000,40000) }

  # ---- rooms ----------------------------------------------------------------
  # `dims auto` puts the phi call-out ON the diameter, through the centre — which is where
  # GB/T wants it and where the room name would otherwise be. Moving the NAME is the
  # author's job (the compiler never relocates a dimension to make room for a label), so
  # this one is pinned above the centre line with `label … at`.
  room id=rotunda_r circle at (30000,14000) radius 8000 label "Ocean Rotunda" at (30000,10500) uses hall
  room id=reef      at (0,6000)      size 21000x16000 label "Reef Gallery"    uses living
  room id=kelp      at (39000,6000)  size 21000x16000 label "Kelp Forest"     uses living
  room id=plant     at (21000,0)     size 18000x6000  label "Filtration"      uses utility
  # The two rooms against the bowed south-east corner follow the curve, because a
  # rectangle there would claim floor OUTSIDE the building: the cafe's would count
  # 200 m² for about 169. A `room polygon` cannot take an `arc` edge, so its curved side
  # is a run of vertices ON the arc, every 7.5°, snapped inward to the 100 mm grid.
  # Inscribed in the curve, the area can only err a fraction under (168.0 m²), never
  # over. The concourse only clips the arc's first 2 m, so one vertex does it.
  room id=concourse polygon (0,22000) (60000,22000) (60000,28000) (59800,30000) (0,30000)
    label "Concourse" uses circulation
  room id=foyer     at (20000,30000) size 20000x10000 label "Entrance Foyer"  uses entry
  room id=shop      at (0,30000)     size 20000x10000 label "Shop"            uses office
  room id=cafe polygon (40000,30000) (59800,30000) (59400,31600) (58800,33000) (58100,34400)
    (57100,35700) (56100,36800) (54800,37800) (53500,38600) (52100,39200) (50500,39700)
    (49000,39900) (48000,40000) (40000,40000)
    label "Cafe" uses dining

  # ---- openings -------------------------------------------------------------
  #
  # Both rotunda doors sit ON the curve. `on rotunda at <pct>` walks the wall by RUN
  # length — an arc contributes its arc length R*theta, not its chord — so 25% is a
  # quarter of the way round from the wall's start at the EAST point. The default `ccw`
  # runs east -> north -> west -> south, so 25% is the north point (the staff door into
  # Filtration) and 75% the south one (the public door off the Concourse).
  door id=d_rot_n on rotunda at 25% width 1200
  door id=d_rot_s on rotunda at 75% width 1800

  # The main entrance is on the CURVED facade, at 15% of the shell's run measured from
  # its start — which lands on the straight south wall; the pair of harbour-side windows
  # below sit on the arc itself, their panes along the tangent and their jambs radial.
  door id=d_main at (30000,40000) width 2400 wall shell swing in
  window id=w_arc1 on shell at 47% width 2000
  window id=w_arc2 on shell at 50% width 2000
  # No facade chain can tick an opening on a curve, so `lint` reports each one
  # (W_OPENING_NOT_DIMENSIONED) until it is dimensioned by hand. These are drawn ON the
  # openings, jamb to jamb (offset 0), and state the width along the arc as written.
  dim (59100,32400)->(59700,30500) offset 0 text "2000"
  dim (55800,37200)->(57200,35800) offset 0 text "2000"
  window at (10000,0) width 3000 wall shell
  window at (50000,0) width 3000 wall shell
  window at (0,14000) width 3000 wall shell
  window at (60000,14000) width 3000 wall shell

  opening id=o_shop at (20000,35000) width 2400 wall foyer_w
  opening id=o_cafe at (40000,35000) width 2400 wall foyer_e
  opening id=o_foyer at (30000,30000) width 4000 wall concourse_s
  opening id=o_reef at (10500,22000) width 3000 wall concourse_n
  opening id=o_kelp at (49500,22000) width 3000 wall concourse_n2

  # ---- fit-out ----------------------------------------------------------------
  # Benches ring the rotunda at the four quadrants clear of the cardinal doors
  # (25%/75% north/south on the drum). A ticket desk sits in the foyer, and the
  # cafe gets a few tables, kept west of the curved south-east facade where its floor
  # narrows.
  furniture bench at (34450,9550)  size 1200x450 in rotunda_r
  furniture bench at (25550,9550)  size 1200x450 in rotunda_r
  furniture bench at (25550,18450) size 1200x450 in rotunda_r
  furniture bench at (34450,18450) size 1200x450 in rotunda_r

  furniture desk   at (22000,33000) size 1800x800 label "Ticket desk" in foyer
  furniture chair  at (24200,33200) size 500x500  in foyer
  furniture plant  at (37000,32000) size 700x700  in foyer

  furniture table at (43000,32000) size 1400x1400 label "Table" in cafe
  furniture table at (47000,32000) size 1400x1400 label "Table" in cafe
  furniture table at (43000,35500) size 1400x1400 label "Table" in cafe
  furniture plant at (41000,38500) size 600x600   in cafe

  # ---- positioning axes: the STRAIGHT structural grid only ------------------
  # A curve has no datum line, so it carries none; its radius is its dimension.
  axes {
    x at 0
    x at 21000
    x at 39000
    x at 60000
    y at 0
    y at 22000
    y at 30000
    y at 40000
  }
}
```

### Reading Room Library (sheet tables)

A 50 × 32 m public library around a circular reading room — **14 rooms, 1545.06 m², 8 doors,
16 windows**, `paper A2 landscape` at `scale 1:200`, split into three
[zones](/reference#zones-wings-and-departments-v1-22): public (9 rooms, 948.06 m²), stacks
(2 rooms, 374.5 m²) and staff (3 rooms, 222.5 m²).

The reading room is a drum inscribed in the 16 × 16 m central bay — a
[`room circle`](/reference#circular-rooms-v1-24) of exactly 201.06 m² inside a wall of
`arc` edges — so it is tangent to all four bay walls and every tangent point is a
threshold; the four corner spandrels it leaves belong to no room and carry no schedule row.
The circle is written as **eight** arcs rather than the two semicircles the shape needs,
deliberately: the analysis grid behind `describe --json`'s `circulation` rasterises a wall
between its *vertices*, so two semicircles lay a phantom chord straight across the room and
report the walk to it at five times its true length. Eight keeps every chord inside the
face it stands for, and the drawing is identical either way — every face is a true arc, at
any zoom.

The sheet is the other half. `schedule rooms` and `legend` fill the margin and are **fully
derived**, nothing to configure: the legend lists one row per wall hatch material actually
used and one per placed fixture category that has a plan symbol, and the schedule's rows and
total are the same numbers `archlang describe --json` reports. At the foot of the file,
`dim radius <wallId> segment <n>` and `dim diameter <roomId>` take both geometry and text
from the element they name, so R8000 and the centre are never retyped.

![library.arch, compiled](/examples/library.svg)

```arch
# A ~50 x 32 m public library around a circular reading room — the SHEET-AND-TABLES example.
#
# 1. CURVED GEOMETRY. The reading room is a drum: a `room circle` for the floor and a
#    `wall` of `arc` edges for the enclosure, the pair `examples/aquarium.arch` introduces.
#    The drum is INSCRIBED in the 16 x 16 m central bay, so it is tangent to all four bay
#    walls and every tangent point is a threshold; the four corner spandrels it leaves
#    (~14 m2 each) belong to no room and carry no schedule row. The circle is written as
#    EIGHT arcs rather than the two semicircles the shape needs, deliberately: the analysis
#    grid behind `describe --json`'s `circulation` rasterises a wall between its VERTICES,
#    so two semicircles lay a phantom chord straight across the room and report the walk to
#    it at 5x its true length. Eight keeps every chord inside the face it stands for, and
#    the DRAWING is identical either way — every face is a true arc, at any zoom.
#
# 2. THE SHEET. `paper A2 landscape` + `scale 1:200` fixes every annotation at a size ON
#    THE SHEET instead of a fraction of a 50 m building, and `schedule rooms` + `legend`
#    fill the margin. Both tables are fully derived, nothing to configure: the legend lists
#    one row per wall hatch MATERIAL used (`concrete` for the shell, the default poche for
#    the partitions) and one per placed fixture CATEGORY that has a plan symbol — wc, basin,
#    kitchen_sink, counter, fridge, stove. Use neither and the table comes out empty.
#
# 3. THE EXPLICIT CURVE CALL-OUTS at the foot of the file. `dim radius <wallId> segment <n>`
#    and `dim diameter <roomId>` take BOTH geometry and text from the element they name, so
#    R8000 and the centre are never retyped. `dims auto` already synthesizes one R per
#    DISTINCT arc (deduplicated by centre+radius, so this eight-arc circle still gets one)
#    and one phi per circular room, in every mode but `walls` — so these two statements are
#    what the sheet would still carry with `dims auto` switched off, and they cost nothing
#    while it is on. Both are zero-offset by contract, which is exactly why neither can trip
#    `W_DIM_INSIDE` or `W_DIM_OVERLAP`: a zero-offset dim sits ON what it measures.
#
# Departments are `zone`s — declared, never inferred from position, and carrying zero
# geometric semantics: the SVG is byte-identical without them. All they add is `zones[]` on
# `describe --json` and the subtotal grouping in the room schedule.
plan "Reading Room Library" {
  units mm
  # `grid 50`, not 100: the partitions are 200 thick and the shell 300, so a fixture
  # placed `flush` against the shell lands on a ...50 coordinate (32000 - 150). A grid of
  # 100 could not express that face and would snap the fixture back into the wall.
  grid 50
  paper A2 landscape
  scale 1:200
  north up
  dims auto all
  schedule rooms
  legend

  # ---- structure: shell in `material concrete`, partitions in the default poche ------
  wall id=shell exterior thickness 300 material concrete { (0,0) (50000,0) (50000,32000) (0,32000) close }

  # The two spine walls: west wing | central bay | east wing.
  wall id=w_west partition thickness 200 { (17000,0) (17000,32000) }
  wall id=w_east partition thickness 200 { (33000,0) (33000,32000) }

  # The bay's cross walls, and the drum inscribed between all four.
  wall id=w_ref_s  partition thickness 200 { (17000,8000) (33000,8000) }
  wall id=w_hall_n partition thickness 200 { (17000,24000) (33000,24000) }

  # Every vertex below is an exact grid-50 point at R8000 from (25000,16000) — 4800/6400 is
  # a 3-4-5 triple scaled — so the eight arcs close a true circle. Walked from the EAST
  # point the default `ccw` runs east -> north -> west -> south, which puts the thresholds
  # at 25% (north), 50% (west) and 75% (south) of the wall's RUN; an arc contributes its
  # arc length R*theta to that run, never its chord.
  wall id=w_drum partition thickness 200 {
    (33000,16000)
    arc (29800,9600)  radius 8000
    arc (25000,8000)  radius 8000
    arc (20200,9600)  radius 8000
    arc (17000,16000) radius 8000
    arc (20200,22400) radius 8000
    arc (25000,24000) radius 8000
    arc (29800,22400) radius 8000
    arc (33000,16000) radius 8000
  }

  # Both wings carry a 3 m aisle on the drum's centre line, so each meets it at a tangent.
  wall id=w_waisle_n partition thickness 200 { (0,14500) (17000,14500) }
  wall id=w_waisle_s partition thickness 200 { (0,17500) (17000,17500) }

  wall id=w_cafe_e   partition thickness 200 { (44000,0) (44000,14500) }
  wall id=w_eaisle_n partition thickness 200 { (33000,14500) (50000,14500) }
  wall id=w_eaisle_s partition thickness 200 { (33000,17500) (50000,17500) }
  wall id=w_staff_s  partition thickness 200 { (33000,24000) (50000,24000) }
  wall id=w_lobby_s  partition thickness 200 { (33000,27000) (50000,27000) }
  wall id=w_wcw_e    partition thickness 200 { (39000,27000) (39000,32000) }
  wall id=w_wcm_e    partition thickness 200 { (45000,27000) (45000,32000) }

  # ---- axes: the STRAIGHT structural grid only; a curve's radius is its dimension ----
  axes {
    x at 0, 17000, 33000, 44000, 50000
    y at 0, 14500, 17500, 24000, 27000, 32000
  }

  # ---- rooms, grouped by department -----------------------------------------
  zone public "Public" {
    room id=r_hall     at (17000,24000) size 16000x8000  label "Entrance Hall"      uses entry hall
    room id=r_reading  circle at (25000,16000) radius 8000 label "Reading Room" at (25000,17200) uses hall
    room id=r_children at (0,17500)     size 17000x14500 label "Children's Library" uses living
    room id=r_waisle   at (0,14500)     size 17000x3000  label "West Aisle"         uses circulation
    room id=r_cafe     at (33000,0)     size 11000x14500 label "Cafe"               uses dining
    room id=r_eaisle   at (33000,14500) size 17000x3000  label "East Aisle"         uses circulation
    room id=r_lobby    at (33000,24000) size 17000x3000  label "WC Lobby"           uses circulation
    room id=r_wc_w     at (33000,27000) size 6000x5000   label "WC Women"           uses wc
    room id=r_wc_m     at (39000,27000) size 6000x5000   label "WC Men"             uses wc
  }

  zone stacks "Stacks" {
    room id=r_stacks at (0,0)     size 17000x14500 label "Open Stacks" at (8500,12400) uses storage
    room id=r_ref    at (17000,0) size 16000x8000  label "Reference Stacks" uses storage
  }

  zone staff "Staff" {
    room id=r_staff   at (33000,17500) size 17000x6500  label "Staff Workroom" uses office
    room id=r_kitchen at (44000,0)     size 6000x14500  label "Cafe Kitchen"   uses kitchen
    room id=r_plant   at (45000,27000) size 5000x5000   label "Plant Room"     uses utility
  }

  # ---- thresholds -----------------------------------------------------------
  # The drum is tangent to each bay wall at one point, so a threshold there has to be cut
  # through BOTH faces — the arc and the straight wall it touches — or the poche of the
  # second one still stands in the doorway. Each pair below is one physical opening.
  opening id=o_drum_s  on w_drum   at 75%   width 3000
  opening id=o_hall_n  on w_hall_n at 50%   width 3000
  opening id=o_drum_n  on w_drum   at 25%   width 3000
  opening id=o_ref_s   on w_ref_s  at 50%   width 3000
  opening id=o_drum_w  on w_drum   at 50%   width 2400
  opening id=o_west_16 on w_west   at 16000 width 2400

  # The rest of the public route.
  door id=d_main at (25000,32000) width 2400 wall shell swing into r_hall
  opening id=o_hall_children on w_west at 28000 width 3000
  opening id=o_hall_lobby    on w_east at 25500 width 3000
  opening id=o_ref_stacks    on w_west at 4000  width 2400
  opening id=o_stacks_aisle  on w_waisle_n at 50% width 3000
  opening id=o_children_aisle on w_waisle_s at 50% width 3000
  opening id=o_cafe_aisle    on w_eaisle_n at 5500 width 3000
  opening id=o_staff_aisle   on w_eaisle_s at 8500 width 2400

  # Staff and service doors. The cafe kitchen keeps a pocket door off the aisle so no leaf
  # swings into a 3 m corridor, and its own service door onto the east yard.
  door id=d_kitchen pocket on w_eaisle_n at 14000 width 1000 slide right
  door id=d_cafe_kitchen on w_cafe_e at 50% width 1000 swing into r_kitchen
  door id=d_service at (50000,13000) width 1200 wall shell swing out
  door id=d_staff_lobby on w_staff_s at 2500 width 1000 swing into r_lobby
  door id=d_wc_w  on w_lobby_s at 1500  width 1000 swing into r_wc_w
  door id=d_wc_m  on w_lobby_s at 7500  width 1000 swing into r_wc_m
  door id=d_plant on w_lobby_s at 13000 width 900  swing into r_plant

  # ---- glazing --------------------------------------------------------------
  window at (6000,0)      width 4000 wall shell
  window at (12000,0)     width 4000 wall shell
  window at (21000,0)     width 3000 wall shell
  window at (29000,0)     width 3000 wall shell
  window at (38500,0)     width 4000 wall shell
  window at (0,6000)      width 3000 wall shell
  window at (0,16000)     width 2000 wall shell
  window at (0,24000)     width 4000 wall shell
  window at (50000,10500) width 1600 wall shell
  window at (50000,20500) width 3000 wall shell
  window at (6000,32000)  width 4000 wall shell
  window at (12000,32000) width 4000 wall shell
  window at (20000,32000) width 3000 wall shell
  window at (30000,32000) width 3000 wall shell
  window at (36000,32000) width 1200 wall shell
  window at (42000,32000) width 1200 wall shell

  # ---- vertical circulation, side by side in the hall -----------------------
  # A single-storey plan still draws both symbols; `W_STAIR_UNMATCHED` is a multi-storey
  # question and never fires here.
  stair id=st_main at (28500,26500) size 1400x4500 dir up
  elevator id=lift_main at (30500,28000) size 2200x2200

  # ---- fit-out --------------------------------------------------------------
  # Reading tables ride the drum's chord lines, leaving the centre band clear for the phi
  # call-out and the west threshold.
  furniture table at (21500,9800)  size 7000x1400 label "Reading table"
  furniture table at (20500,12200) size 9000x1400 label "Reading table"
  furniture table at (20500,18400) size 9000x1400 label "Reading table"
  furniture table at (21500,20800) size 7000x1400 label "Reading table"

  for i in 0..5 {
    furniture shelf at (2000, 1500 + i * 2200) size 13000x600 label "Stacks"
  }
  for i in 0..4 {
    furniture shelf at (18500 + i * 3200, 1200) size 600x5000 label "Reference"
  }
  for i in 0..3 {
    furniture table at (2500 + i * 4500, 19500) size 2000x2000 label "Table"
    furniture table at (2500 + i * 4500, 23500) size 2000x2000 label "Table"
    furniture shelf at (2000 + i * 4500, 28500) size 3000x500 label "Picture books"
  }
  for i in 0..5 {
    furniture desk at (34500 + i * 3000, 18500) size 1600x800 label "Desk"
    furniture desk at (34500 + i * 3000, 22000) size 1600x800 label "Desk"
  }
  for i in 0..3 {
    furniture table at (34500 + i * 3000, 2500) size 1600x1600 label "Table"
    furniture table at (34500 + i * 3000, 6500) size 1600x1600 label "Table"
  }

  furniture sofa    at (2000,27500)  size 2400x900 label "Story bench"
  furniture sofa    at (18000,30000) size 2400x900 label "Seating"
  furniture sofa    at (21000,30000) size 2400x900 label "Seating"
  furniture counter against wall w_hall_n offset 4000 size 4000x700 label "Enquiries" in r_hall
  furniture plant   at (18500,29000) size 700x700 in r_hall
  furniture counter against wall w_cafe_e offset 3000 size 4000x700 label "Servery" in r_cafe

  # Catalogued fixtures need no `size`: the footprint, the depth off the wall face and the
  # quarter-turn all come from the wall and the catalogue.
  furniture table at (45000,2500) size 3600x900 label "Prep"
  furniture table at (45000,4600) size 3600x900 label "Prep"
  furniture kitchen_sink against wall shell segment 1 offset 2000 in r_kitchen
  furniture counter      against wall shell segment 1 offset 3600 in r_kitchen
  furniture fridge       against wall shell segment 1 offset 5200 in r_kitchen
  furniture stove        against wall shell segment 1 offset 6800 in r_kitchen

  furniture wc    against wall shell segment 2 offset 11500 in r_wc_w
  furniture wc    against wall shell segment 2 offset 13000 in r_wc_w
  furniture wc    against wall shell segment 2 offset 14500 in r_wc_w
  furniture basin against wall w_east offset 28000 in r_wc_w
  furniture basin against wall w_east offset 29500 in r_wc_w

  furniture wc    against wall shell segment 2 offset 5500 in r_wc_m
  furniture wc    against wall shell segment 2 offset 7000 in r_wc_m
  furniture wc    against wall shell segment 2 offset 8500 in r_wc_m
  furniture basin against wall w_wcw_e offset 1000 in r_wc_m
  furniture basin against wall w_wcw_e offset 2500 in r_wc_m

  # ---- the explicit curve call-outs (see the head of the file) --------------
  dim radius w_drum segment 0
  dim diameter r_reading

  title {
    project "Reading Room Library"
    drawn_by "ArchLang"
    date "2026-08-16"
  }
}
```

### Transit Hall

A 90 × 40 m metro concourse — **12 rooms, 3600 m², 7 doors, 19 windows**, A2 at 1:200 — and
the big-public-building example. One fact organises the whole plan: a transit hall is two
buildings that share a roof. The unpaid side is street, and anyone may stand in it; the paid
side is platform, and the only way across is the gate line. So the drawing is a single
300 mm partition running the full 90 m width, with nine `opening`s punched in it — eight
800 mm gates from a `for` loop, and one 1200 mm wide aisle written beside them. Nothing
else joins the two halves.

That split is [declared](/reference#zones-wings-and-departments-v1-22), never inferred from
position: `paid` is 1260 m² over 1 room, `unpaid` 1948 m² over 5, and back-of-house 80 m²
over 2, so "how much unpaid floor is there" is a read rather than a sum — while the SVG is
byte-identical without the zone wrappers. The four retail units are one
[`strip`](/reference#strip-v1-13), not four hand-placed rooms: each unit's offset is the
running sum of the ones before it, so changing a shop's depth moves every unit below it and
nothing else. Escalators and a lift are
[vertical shafts](/reference#vertical-circulation-stair-elevator-escalator-v1-21) drawn with
their chevrons and UP/DN arrows; on a single-storey plan they still obstruct circulation,
and `W_STAIR_UNMATCHED` is a multi-storey question that never fires here.

![transit-hall.arch, compiled](/examples/transit-hall.svg)

```arch
# A 90 x 40 m metro concourse split by its gate line — the BIG-PUBLIC-BUILDING example.
#
# One fact organises the whole plan: a transit hall is two buildings that share a roof. The
# UNPAID side is street, and anyone may stand in it — shops, WCs, ticket machines. The PAID
# side is platform, and the only way across is the gate line. So the drawing is a single
# 300 mm partition, `w_gate`, running the full 90 m width, with nine `opening`s punched in
# it by a `for` loop: eight 800 mm gates and one 1200 mm wide aisle. Nothing else joins the
# two halves — the retail strip and the service wing both stop dead at that wall.
#
# Three things this example is the showcase for:
#
#   `zone`      the paid / unpaid / service split is DECLARED, never inferred from
#               position. It draws nothing (the SVG is byte-identical without it), but the
#               room schedule groups by it with a subtotal per zone, and `describe --json`
#               reports `zones[]` — so "how much unpaid floor is there" is a read, not a sum.
#   `strip`     the four retail units are one block, not four hand-placed rooms. Each unit's
#               offset is the running sum of the ones before it, so changing a shop's depth
#               moves every unit below it and nothing else.
#   escalators  `escalator … dir up|down` and `elevator` are one shaft each, drawn with the
#               chevrons and the UP/DN arrow. On a single-storey plan they still draw and
#               still obstruct circulation; `W_STAIR_UNMATCHED` is a multi-storey question
#               and never fires here. Both narrow ends of an escalator are entries.
#
# The columns are on the 12 m structural grid the `axes` bubbles name, laid by `for` — one
# statement per row, not eighteen coordinates.
plan "Transit Hall" {
  units mm
  # `grid 50`: partitions are 200 thick and the shell 400, so a fixture placed against a
  # face lands on a ...50 coordinate a 100 grid could not express.
  grid 50
  paper A2 landscape
  scale 1:200
  north up
  dims auto all
  schedule rooms

  let BAY = 12000     # structural bay, and the axis grid
  let GATE = 14000    # y of the gate line

  # ---- shell and the gate line ----------------------------------------------
  wall id=shell exterior thickness 400 material concrete { (0,0) (90000,0) (90000,40000) (0,40000) close }
  wall id=w_gate partition thickness 300 { (0,GATE) (90000,GATE) }

  # ---- unpaid side: retail strip (west) and the service wing (east) ---------
  wall id=w_retail_e partition thickness 200 { (12000,GATE) (12000,40000) }
  wall id=w_r1 partition thickness 200 { (0,20500) (12000,20500) }
  wall id=w_r2 partition thickness 200 { (0,27000) (12000,27000) }
  wall id=w_r3 partition thickness 200 { (0,33500) (12000,33500) }

  wall id=w_wing_w partition thickness 200 { (76000,GATE) (76000,40000) }
  wall id=w_wing_e partition thickness 200 { (80000,GATE) (80000,40000) }
  wall id=w_wcw_s  partition thickness 200 { (80000,21000) (90000,21000) }
  wall id=w_wcm_s  partition thickness 200 { (80000,28000) (90000,28000) }
  wall id=w_wca_s  partition thickness 200 { (80000,32000) (90000,32000) }
  wall id=w_staff_s partition thickness 200 { (80000,36000) (90000,36000) }

  axes {
    x at 0, 12000, 24000, 36000, 48000, 60000, 72000, 90000
    y at 0, 14000, 27000, 40000
  }

  # ---- rooms ----------------------------------------------------------------
  # Four retail units laid end to end down the west edge: only each unit's DEPTH is
  # written, so changing one shop moves every unit below it and nothing else. `strip` is a
  # PLAN-LEVEL block — it cannot nest inside a `zone` (E_STRIP_NEST) — so the kiosks are the
  # one group the schedule lists on its own, after the three zoned departments.
  strip down at (0,GATE) gap 0 width 12000 {
    room id=r_shop1 size 6500 label "Kiosk 1" uses office
    room id=r_shop2 size 6500 label "Kiosk 2" uses office
    room id=r_shop3 size 6500 label "Kiosk 3" uses office
    room id=r_shop4 size 6500 label "Kiosk 4" uses office
  }

  zone paid "Paid" {
    room id=r_paid at (0,0) size 90000x14000 label "Paid Concourse" uses hall circulation
  }

  zone unpaid "Unpaid" {
    room id=r_unpaid at (12000,GATE) size 64000x26000 label "Unpaid Concourse" uses hall circulation

    room id=r_lobby at (76000,GATE) size 4000x26000 label "Lobby"         uses circulation
    room id=r_wc_w  at (80000,GATE) size 10000x7000 label "WC Women"      uses wc
    room id=r_wc_m  at (80000,21000) size 10000x7000 label "WC Men"       uses wc
    room id=r_wc_a  at (80000,28000) size 10000x4000 label "Accessible WC" uses wc
  }

  zone service "Back of house" {
    room id=r_staff   at (80000,32000) size 10000x4000 label "Staff Room"   uses office
    room id=r_control at (80000,36000) size 10000x4000 label "Control Room" uses office
  }

  # ---- the gate line --------------------------------------------------------
  # `on <wall> at <pos>` takes a LITERAL position, so a generated run of gates uses the
  # absolute form instead: the point is on the gate wall's centreline by construction
  # (y = GATE), and `wall w_gate` names the host. Eight standard gates, then the wide aisle.
  for i in 0..8 {
    opening at (30000 + i * 2400, GATE) width 800 wall w_gate
  }
  opening id=o_gate_wide on w_gate at 49200 width 1200

  # ---- the rest of the route ------------------------------------------------
  door id=d_main at (45000,40000) width 4000 wall shell swing into r_unpaid

  opening id=o_shop1 on w_retail_e at 3250  width 2400
  opening id=o_shop2 on w_retail_e at 9750  width 2400
  opening id=o_shop3 on w_retail_e at 16250 width 2400
  opening id=o_shop4 on w_retail_e at 22750 width 2400

  opening id=o_lobby_n on w_wing_w at 4000  width 3000
  opening id=o_lobby_s on w_wing_w at 22000 width 2400

  door id=d_wc_w on w_wing_e at 3500  width 1000 swing into r_wc_w
  door id=d_wc_m on w_wing_e at 10500 width 1000 swing into r_wc_m
  door id=d_wc_a on w_wing_e at 16000 width 1100 swing into r_wc_a

  # Back of house takes pocket doors: a 4 m lobby is a queue, and neither leaf may sweep
  # into it. `slide` runs along the wall's traversal (north to south here), so each panel
  # drives into the solid run between the two doors rather than at a corner.
  door id=d_staff   pocket on w_wing_e at 20000 width 900 slide right
  door id=d_control pocket on w_wing_e at 24000 width 900 slide left
  door id=d_staff_entry at (90000,38500) width 1200 wall shell swing out

  # ---- glazing --------------------------------------------------------------
  # Curtain-wall bays on the three street facades, written as three runs rather than
  # seventeen statements. Every centre is on the shell's centreline by construction.
  for i in 0..4 {
    window at (15000 + i * 20000, 0) width 6000 wall shell      # paid-side north facade
    window at (0, 17000 + i * 6500) width 3000 wall shell       # one per kiosk, west facade
  }
  for i in 0..3 {
    window at (18000 + i * 9000, 40000) width 6000 wall shell   # south facade, west of the entrance
    window at (54000 + i * 9000, 40000) width 6000 wall shell   # south facade, east of the entrance
  }
  window at (0,7000) width 3000 wall shell
  window at (90000,7000) width 3000 wall shell
  window at (90000,17500) width 1200 wall shell
  window at (90000,24500) width 1200 wall shell
  window at (90000,34000) width 1500 wall shell

  # ---- vertical circulation down to the platforms ---------------------------
  escalator id=esc_down at (41000,3000) size 1400x9000 dir down
  escalator id=esc_up   at (44000,3000) size 1400x9000 dir up
  elevator  id=lift_plat at (50500,3000) size 2400x2400

  # ---- structure: one statement per column row ------------------------------
  for i in 0..6 {
    column at (BAY + i * BAY, 7000) size 800x800
  }
  for i in 0..5 {
    column at (2 * BAY + i * BAY, 27000) size 800x800
  }

  # ---- sanitary fixtures ----------------------------------------------------
  # `against wall <id> offset <mm>` puts the piece's CENTRE at that distance along the
  # wall; the depth off the face, the quarter-turn and (for a catalogued fixture) the
  # footprint are all derived, so no coordinate and no half-thickness is written here.
  for i in 0..4 {
    furniture wc against wall w_wcw_s offset 1500 + i * 1500 in r_wc_w
    furniture wc against wall w_wcm_s offset 1500 + i * 1500 in r_wc_m
  }
  furniture basin against wall w_gate  offset 87000 in r_wc_w
  furniture basin against wall w_gate  offset 88500 in r_wc_w
  furniture basin against wall w_wcw_s offset 7000 in r_wc_m
  furniture basin against wall w_wcw_s offset 8500 in r_wc_m
  furniture wc    against wall w_wca_s offset 1500 in r_wc_a
  furniture basin against wall w_wca_s offset 3500 in r_wc_a

  # ---- concourse fit-out ----------------------------------------------------
  # Ticket machines back onto the gate wall on the unpaid side — `offset` is the distance
  # along the wall to the piece's CENTRE, and `size` on an `against` piece reads
  # along x depth, so the 6 m bank spans 17-23 m and stands 900 off the face.
  furniture counter against wall w_gate offset 20000 size 6000x900 label "Ticket machines" in r_unpaid
  furniture counter against wall w_gate offset 60000 size 6000x900 label "Ticket machines" in r_unpaid
  # The assistance desk sits just east of the wide gate, clear of every gate approach.
  furniture desk    at (52000,16500) size 4000x1200 label "Assistance"
  # Benches stop short of the escalator/lift block (x >= 41000 on the paid side).
  for i in 0..4 {
    furniture bench at (30000 + i * 6000, 34000) size 2400x700 label "Bench"
    furniture bench at (18000 + i * 6000, 5000)  size 2400x700 label "Bench"
  }
  furniture desk at (82000,34000) size 3000x900 label "Desk" in r_staff
  furniture desk at (82000,38000) size 3000x900 label "Desk" in r_control

  # ---- light touch: the four kiosks and the WC lobby were otherwise bare -----
  furniture shelf at (1000,16400) size 900x300 label "Shelving" in r_shop1
  furniture plant at (10200,16700) size 600x600 in r_shop1
  furniture shelf at (1000,22900) size 900x300 label "Shelving" in r_shop2
  furniture plant at (10200,23200) size 600x600 in r_shop2
  furniture shelf at (1000,29400) size 900x300 label "Shelving" in r_shop3
  furniture plant at (10200,29700) size 600x600 in r_shop3
  furniture shelf at (1000,35900) size 900x300 label "Shelving" in r_shop4
  furniture plant at (10200,36200) size 600x600 in r_shop4

  furniture bench at (77800,24500) size 450x1200 in r_lobby
  furniture plant at (78800,20500) size 500x500  in r_lobby

  title {
    project "Metro Interchange — concourse level"
    drawn_by "ArchLang"
    date "2026-08-16"
  }
}
```

### Clinic Wing

An outpatient clinic — **13 rooms, 294.12 m², 11 doors, 13 windows** at `scale 1:100` —
whose six consulting rooms are **one [component](/reference#components) placed six times**.
Every consulting room in a clinic is the same room; drawn the usual way that is twenty-four
statements to keep in step by hand. Here it is written once and
[instantiated](/reference#placing-instances-place-v1-22): each `place consult(…) as c1 …` is
an addressable instance of exactly 16.2 m².

Three things follow. **Namespacing** — ids inside the component become
`<instance>.<id>`, so the six rooms are `c1.main` … `c6.main` and the plan can address
exactly one of them: `furniture … in c2.main` puts an ECG trolley in the second room and
nowhere else, and `archlang describe --room c2.main` reads it back. **Mirroring** — the north row
is placed as drawn and the south row with `mirror y`, a real reflection about the instance's
own origin, so the corridor wall lands on the corridor for both rows and all six doors face
the same way; because a reflection moves the origin to the far edge, a mirrored 4500-deep
unit is placed at the far y. **One side wall** — each instance carries its left wall and its
corridor wall only, an L rather than a box, so at 3600 centres no two partitions ever
coincide. The rest of the wing is ordinary: a 2400 corridor with a `dim clear` proving
2300 mm of it is actually clear floor, pocket doors on the consulting rooms so no leaf ever
sweeps into a corridor patients queue in, and a sliding door wide enough for a trolley into
treatment. The plan is clean under `archlang lint --profile accessibility-advisory` as well as
the default ruleset.

![clinic.arch, compiled](/examples/clinic.svg)

```arch
# An outpatient clinic wing whose six consulting rooms are ONE component placed six times.
#
# Every consulting room in a clinic is the same room. Drawn the usual way that is six
# copies of four walls, a door, a desk and a basin — twenty-four statements that must be
# kept in step by hand, and a plan where widening the rooms means editing every one. So
# this plan writes the room ONCE, as a component, and instantiates it:
#
#   component consult(depth, no) { … }        # authored in LOCAL coords from (0,0)
#   place consult(4500, 1) as c1 at (9000,0)  # an addressable INSTANCE
#
# Three things follow from that, and they are what this example is the showcase for:
#
#   NAMESPACING   ids inside the component become `<instance>.<id>`, so the six rooms are
#                 `c1.main` … `c6.main` and the plan can address exactly one of them —
#                 `furniture … in c2.main` below puts an ECG trolley in the second room
#                 and nowhere else. `arch describe --room c2.main` reads it back.
#   MIRRORING     the north row is placed as drawn; the south row is placed with
#                 `mirror y`, a REAL reflection about the instance's own origin. So the
#                 corridor wall — local `y = depth` — lands on the corridor for both rows
#                 and all six doors face the same way. Because a reflection moves the
#                 origin to the far edge, a mirrored 4500-deep unit is placed at the FAR
#                 y (11400), not at 6900.
#   ONE SIDE WALL each instance carries its LEFT wall and its CORRIDOR wall only — an L,
#                 not a box. Laid at 3600 centres, each unit's left wall closes the one
#                 before it, so no two partitions ever coincide; the plan supplies the
#                 single wall that closes the last unit of each row.
#
# The rest of the wing is written the ordinary way: a 2400 corridor down the middle with
# a `dim clear` proving 2300 of it is actually clear floor, the public rooms (entrance,
# reception, waiting) at the west end and the clinical support rooms (treatment, staff,
# accessible WC) at the east, declared as two `zone`s. Door kinds carry the same argument
# a builder would: the consulting rooms take `pocket` doors so no leaf ever sweeps into a
# corridor patients queue in, and the treatment room a `sliding` one wide enough for a
# trolley. This plan is clean under `arch lint --profile accessibility-advisory` as well
# as the default ruleset.
plan "Clinic Wing" {
  units mm
  # `grid 50`: every partition here is 100 thick, so a fixture backed onto one lands on a
  # half-thickness (…50) coordinate that a 100 grid could not express without snapping it
  # back into the wall.
  grid 50
  paper A3 landscape
  scale 1:100
  north up
  dims auto all
  # A `schedule` AND a `legend`. The schedule groups rooms by the innermost zone the
  # author DECLARED — `public` and `clinical` — and a `place`d instance's implicit zone
  # (`clinical.c1` …) is transparent to that grouping, so the six consult rooms sit under
  # "Clinical" instead of printing six one-row groups with six subtotals. (They did print
  # six, before v1.26.2; this file shipped with a legend and no schedule because of it.)
  # `describe --zone clinical.c4` still addresses one instance — a `place` is a namespace,
  # not a table heading.
  schedule rooms
  legend

  # ---- the consulting room, written once -------------------------------------
  # `depth` is the room's depth; `no` only feeds the label. The corridor is at local
  # `y = depth`, which is the edge `mirror y` turns over.
  component consult(depth, no) {
    wall id=w_side partition thickness 100 { (0,0) (0,depth) }
    wall id=w_corr partition thickness 100 { (0,depth) (3600,depth) }

    room id=main at (0,0) size 3600 x depth label "Consult {no}" uses office

    # A pocket door: the panel slides into the 1300 mm of wall past its jamb (the run
    # `W_POCKET_RUN` measures), so nothing swings into the corridor. 1000 wide, not the
    # residential 900: at 900 every one of these routes reads 840 mm of clear width and
    # `arch lint --profile accessibility-advisory` says so, room by room.
    door id=d pocket on w_corr at 50% width 1000 slide right

    furniture id=f_desk  desk  in main anchor top   inset 900 size 1600x800  label "Desk"
    furniture id=f_couch couch in main anchor right inset 200 size 700x1900  label "Couch"
    furniture id=f_basin basin against wall w_side offset depth - 800 in main
  }

  # ---- shell and the walls the plan itself owns -------------------------------
  wall id=shell   exterior  thickness 200 { (0,0) (25800,0) (25800,11400) (0,11400) close }
  wall id=w_wb    partition thickness 100 { (0,6900) (9000,6900) }
  wall id=w_wb_d  partition thickness 100 { (4500,6900) (4500,11400) }
  wall id=w_corr_w partition thickness 100 { (9000,4500) (9000,6900) }
  wall id=w_mid_n partition thickness 100 { (19800,0) (19800,4500) }
  wall id=w_mid_s partition thickness 100 { (19800,6900) (19800,11400) }
  wall id=w_tr_s  partition thickness 100 { (19800,4500) (25800,4500) }
  wall id=w_ea_n  partition thickness 100 { (19800,6900) (25800,6900) }
  wall id=w_ea_d  partition thickness 100 { (22800,6900) (22800,11400) }

  # ---- the two departments ----------------------------------------------------
  zone public "Public" {
    room id=r_wait    at (0,0)        size 9000x6900 label "Waiting"   uses hall
    room id=r_recept  at (0,6900)     size 4500x4500 label "Reception" uses office
    room id=r_entry   at (4500,6900)  size 4500x4500 label "Entrance"  uses entry
  }

  zone clinical "Clinical" {
    room id=r_corr  at (9000,4500)   size 16800x2400 label "Corridor"      uses circulation
    room id=r_treat at (19800,0)     size 6000x4500  label "Treatment"     uses office
    room id=r_staff at (19800,6900)  size 3000x4500  label "Staff Room"    uses office
    room id=r_wc    at (22800,6900)  size 3000x4500  label "Accessible WC" uses wc

    # Three north, three south. The south row is `mirror y`, so its doors face the
    # corridor; each instance also becomes a nested zone (`clinical.c4`), which is what
    # lets `arch describe --json --select zones` report one consulting room on its own.
    place consult(4500, 1) as c1 at (9000,0)
    place consult(4500, 2) as c2 at (12600,0)
    place consult(4500, 3) as c3 at (16200,0)
    place consult(4500, 4) as c4 at (9000,11400)  mirror y
    place consult(4500, 5) as c5 at (12600,11400) mirror y
    place consult(4500, 6) as c6 at (16200,11400) mirror y
  }

  # ---- the route through the building ------------------------------------------
  # An automatic slider at the front door, then a cased opening into the waiting hall and
  # a full-width opening into the corridor — no leaf anywhere on the patient's route.
  door    id=d_main   sliding at (6750,11400) width 1800 wall shell slide right
  opening id=o_entry  on w_wb at 6750 width 2400
  opening id=o_recept on w_wb at 900  width 1200
  door    id=d_recept on w_wb_d  at 2250 width 900
  opening id=o_corr   on w_corr_w at 50% width 1800

  # The treatment room takes a 1200 slider a trolley fits through; the accessible WC's
  # leaf swings OUT into the corridor, which is why the corridor is dimensioned below.
  door id=d_treat sliding on w_tr_s at 3000 width 1200 slide left
  # `hinge near end`: the leaf swings toward the party wall, not back over the sink.
  door id=d_staff on w_ea_n at 1500 width 1000 hinge near end
  door id=d_wc    on w_ea_n at 4500 width 1000 swing into r_corr

  # ---- glazing -------------------------------------------------------------------
  # One window per consulting room on each long facade, on the room's centre by
  # construction: the units are on 3600 centres from x = 9000.
  for i in 0..3 {
    window at (10800 + i * 3600, 0)     width 1200 wall shell
    window at (10800 + i * 3600, 11400) width 1200 wall shell
  }
  window at (4500,0)      width 3000 wall shell
  window at (0,3000)      width 2400 wall shell
  window at (22800,0)     width 1800 wall shell
  window at (25800,2250)  width 1200 wall shell
  window at (2250,11400)  width 1500 wall shell
  window at (21300,11400) width 1200 wall shell
  window at (24300,11400) width 900  wall shell

  # ---- fit-out ---------------------------------------------------------------------
  for i in 0..3 {
    furniture seating at (1200, 1200 + i * 1800) size 2400x600 label "Seating"
    furniture seating at (5400, 1200 + i * 1800) size 2400x600 label "Seating"
  }
  furniture id=f_recept counter against wall w_wb offset 3100 size 2400x600 in r_recept

  furniture id=f_tr_counter counter against wall w_tr_s offset 1000 size 1800x600 in r_treat
  furniture id=f_tr_basin   basin   against wall w_tr_s offset 5200 in r_treat
  furniture id=f_tr_couch   couch   at (21000,1000) size 2000x800 label "Exam couch"

  furniture id=f_st_sink kitchen_sink against wall w_mid_s offset 1200 in r_staff
  furniture id=f_st_table table at (20400,9600) size 1600x900 label "Table"

  furniture id=f_wc_pan   wc    against wall w_ea_d offset 3500 in r_wc
  furniture id=f_wc_basin basin against wall w_ea_d offset 1200 in r_wc

  # The dotted id is what makes ONE instance addressable: this trolley belongs to the
  # second consulting room and to no other.
  furniture id=f_ecg trolley in c2.main anchor bottom-right inset 300 size 700x500 label "ECG"

  # The Entrance is otherwise a bare pass-through between the slider and the waiting hall.
  furniture id=f_entry_plant plant at (8000,7200) size 600x600 in r_entry

  # ---- the dimension that matters --------------------------------------------------
  # `dim clear` pulls both endpoints in to the inner FACES, so this reads the corridor's
  # clear width (2400 centre-to-centre less two half-partitions = 2300), not its nominal
  # one. `offset 0` keeps the line on the measured axis: a clear width is measured where
  # it is, so there is no outward side to push it to.
  dim clear (11000,4500)->(11000,6900) offset 0 text "2300 clear"

  title {
    project "Outpatient clinic wing"
    drawn_by "ArchLang"
    date "2026-08-16"
  }
}
```

### Practice Office

A small professional-services office — **9 rooms, 225 m², 9 doors, 10 windows**, 18.2 × 12.7 m
to the outer faces, issued on `paper A2 landscape` at `scale 1:100`. An open workspace, a
manager's room and two meeting rooms hang off a 1800 mm spine corridor that is a real room
(`uses circulation`), so every door lands on a space the reachability and path-width lint can
measure; reception, a break room, an accessible WC and a comms room line the
service side. This is the workplace counterpart of the dwellings above — no bedrooms, no
kitchen triangle — and it carries the sheet statements a contractor reads first:
`schedule rooms` totals the floor by zone (129.15 m² of workspace, 95.85 m² of support),
`schedule openings` tags every door and window and keys them to the
**DOOR & WINDOW SCHEDULE**, and three `notes` fill the numbered **NOTES** table. The
accessible WC takes a 1000 mm door that swings out into the corridor, a second exit closes the east end of the spine so no room is a dead end, and the plan is clean under
`archlang lint --profile accessibility-advisory` as well as the default ruleset.

![practice-office.arch, compiled](/examples/practice-office.svg)

```arch
# A small professional-services office: one open workspace, a manager's room, two meeting
# rooms, a break room, reception, a comms room and an accessible WC, all on a
# 1800 mm spine corridor.
#
# It is the WORKPLACE counterpart of the dwellings in this folder — no bedrooms, no kitchen
# triangle — and it shows the sheet statements a contractor reads first:
#
#   schedule rooms       NO. · NAME · AREA, grouped by the two zones below
#   schedule openings    D01… / W01… tags and the DOOR & WINDOW SCHEDULE, derived from the
#                        walls, never typed
#   notes "…"            the general notes, numbered in source order
#
# The spine is a real room (`uses circulation`) so every door lands on a space the
# reachability and path-width lint can measure. The WC door is 1000 wide and the room is
# deep enough to turn a wheelchair — the accessible-WC rule in
# `--profile accessibility-advisory`.
plan "Practice Office" {
  units mm
  grid 50
  paper A2 landscape
  scale 1:100
  north up
  dims auto all
  schedule rooms
  schedule openings
  notes "All dimensions in millimetres. Do not scale from this drawing."
  notes "Room areas are as scheduled; confirm on site before ordering finishes."
  notes "Fire-rated door to the comms room to be confirmed by the engineer."

  title { project "Practice Office" drawn_by "ArchLang" date "2026-10-14" number "A-201" revision "A" }

  # ---- shell and partitions ---------------------------------------------------
  wall id=shell exterior thickness 200 { (0,0) (18000,0) (18000,12500) (0,12500) close }
  # The two corridor walls run the full width; the cross walls stop on them.
  wall id=w_cn partition thickness 100 { (0,6000) (18000,6000) }
  wall id=w_cs partition thickness 100 { (0,7800) (18000,7800) }
  wall id=w_n1 partition thickness 100 { (10500,0) (10500,6000) }
  wall id=w_n2 partition thickness 100 { (14000,0) (14000,6000) }
  wall id=w_s1 partition thickness 100 { (4500,7800) (4500,12500) }
  wall id=w_s2 partition thickness 100 { (9300,7800) (9300,12500) }
  wall id=w_s3 partition thickness 100 { (11800,7800) (11800,12500) }
  wall id=w_s4 partition thickness 100 { (13500,7800) (13500,12500) }

  # ---- rooms ------------------------------------------------------------------
  zone work "Workspace" {
    room id=r_team    at (0,0)        size 10500x6000 label "Team Workspace" uses office
    room id=r_manager at (10500,0)    size 3500x6000  label "Manager" uses office
    room id=r_meet1   at (14000,0)    size 4000x6000  label "Meeting 1" uses office
    room id=r_meet2   at (13500,7800) size 4500x4700  label "Meeting 2" uses office
  }
  zone support "Support" {
    room id=r_spine   at (0,6000)     size 18000x1800 label "Corridor" uses circulation
    room id=r_recep   at (0,7800)     size 4500x4700  label "Reception" uses entry
    room id=r_break   at (4500,7800)  size 4800x4700  label "Break Room" uses kitchen dining
    room id=r_wc      at (9300,7800)  size 2500x4700  label "Accessible WC" uses wc
    room id=r_comms   at (11800,7800) size 1700x4700  label "Comms" uses utility
  }

  # ---- doors ------------------------------------------------------------------
  door id=d_front at (2250,12500) width 1200 wall shell hinge left swing in
  opening id=o_recep on w_cs at 2250 width 1500
  door id=d_open on w_cn at 6000 width 1200 swing into r_team
  door id=d_mgr on w_cn at 12250 width 1000 swing into r_manager
  door id=d_m1 on w_cn at 16000 width 1000 swing into r_meet1
  door id=d_break on w_cs at 7000 width 1000 swing into r_break
  door id=d_wc on w_cs at 10550 width 1000 swing into r_spine
  door id=d_comms on w_cs at 12650 width 1000 swing into r_comms
  door id=d_m2 on w_cs at 15750 width 1000 swing into r_meet2
  # A second way out: an outward-opening door closes the east end of the spine, so no room is a dead end.
  door id=d_east at (18000,6900) width 1000 wall shell swing out

  # ---- windows ----------------------------------------------------------------
  window at (2500,0) width 2000 wall shell
  window at (6500,0) width 2000 wall shell
  window at (9000,0) width 2000 wall shell
  window at (12250,0) width 1400 wall shell
  window at (16000,0) width 2000 wall shell
  window at (18000,3000) width 2000 wall shell
  window at (18000,9500) width 2000 wall shell
  window at (7000,12500) width 1800 wall shell
  window at (16900,12500) width 1800 wall shell
  window at (0,3000) width 2000 wall shell

  # ---- fit-out ----------------------------------------------------------------
  for i in 0..3 {
    furniture desk at (600 + i * 3200, 800) size 1600x800 label "Desk {i + 1}"
    furniture desk at (600 + i * 3200, 3600) size 1600x800 label "Desk {i + 4}"
  }
  furniture meeting_table in r_meet1 centered size 2600x1200 label "Meeting table"
  furniture meeting_table in r_meet2 centered size 2600x1200 label "Meeting table"
  furniture desk in r_manager anchor top inset 600 size 1600x800 label "Desk"
  furniture reception_desk in r_recep anchor top inset 800 size 2200x800 label "Reception"
  furniture kitchen_sink against wall w_s2 offset 1200 in r_break
  furniture counter against wall w_s2 offset 2400 in r_break
  furniture fridge against wall w_s2 offset 3600 in r_break
  furniture table in r_break centered size 1800x900 label "Table"
  furniture wc against wall shell segment 2 offset 7450 in r_wc
  furniture basin against wall w_s3 offset 2200 in r_wc
}
```

### Corner Cafe

A neighbourhood cafe — **6 rooms, 150 m², 7 doors, 8 windows** at `scale 1:100` on
`paper A3 landscape`, 15.2 × 10.2 m to the outer faces. A cafe is two buildings in one plan:
a front of house the public walks and a back of house they never see. Two
[zones](/reference#zones-wings-and-departments-v1-22), `front` (101.1 m²) and `back` (48.9 m²),
split the schedule's subtotals at no cost in geometry; the public enters the 90 m² dining
room by a 1200 mm leaf, staff reach the kitchen only through the servery, a cased opening in the servery wall lets the dining room order across the counter, the
customer WC opens off its own lobby rather than the dining room, and a goods door on
the east wall opens onto the kitchen alone. The nine two-tops are a pair of nested `for`
loops over the catalogue `table` and `chair`, and the kitchen line (sink, stove, fridge) is
`against wall` so each piece derives its own footprint. `legend` lists the fixture categories
actually placed. Clean under both lint profiles.

![corner-cafe.arch, compiled](/examples/corner-cafe.svg)

```arch
# A 15 x 10 m neighbourhood cafe: a dining room for eighteen, a servery, a kitchen, a store and
# one customer WC.
#
# A cafe is two buildings in one plan — a front of house the public walks, and a back of
# house they never see — and the plan makes that split legible three ways:
#
#   zones      `front` and `back` group the schedule's subtotals, and cost no geometry
#   doors      the public enters the dining room by a 1200 leaf; staff reach the kitchen
#              only THROUGH the servery, and the goods door on the east wall opens onto the
#              kitchen alone; the dining room orders through a cased opening in the servery
#              wall, and the customer WC opens off its own lobby, never the dining room
#   fit-out    every seat is a catalogue `table` + `chair` pair laid by `for`, the servery
#              counter is backed onto the wall it serves, and the kitchen line (sink,
#              stove, fridge) is `against wall` so each piece derives its own footprint
#
# `legend` lists the hatch and the fixture categories actually placed; `schedule rooms`
# totals the floor. Clean under both lint profiles.
plan "Corner Cafe" {
  units mm
  grid 50
  paper A3 landscape
  scale 1:100
  north up
  dims auto all
  schedule rooms
  legend

  # ---- shell and partitions ---------------------------------------------------
  wall id=shell exterior thickness 200 { (0,0) (15000,0) (15000,10000) (0,10000) close }
  # The spine between front and back of house, with the partitions that hang off it.
  wall id=w_e partition thickness 100 { (9000,0) (9000,10000) }
  wall id=w_k partition thickness 100 { (9000,4000) (15000,4000) }
  wall id=w_s partition thickness 100 { (9000,7000) (15000,7000) }
  wall id=w_wc partition thickness 100 { (12700,7000) (12700,10000) }
  # The WC does not open off the dining room: a small lobby sits between them.
  wall id=w_lb partition thickness 100 { (11000,7000) (11000,10000) }

  # ---- rooms ------------------------------------------------------------------
  zone front "Front of house" {
    room id=r_dining at (0,0)       size 9000x10000 label "Dining" uses dining
    room id=r_lobby  at (9000,7000) size 2000x3000  label "Lobby" uses circulation
    room id=r_wc     at (11000,7000) size 1700x3000 label "WC" uses wc
  }
  zone back "Back of house" {
    room id=r_kitchen at (9000,0)     size 6000x4000 label "Kitchen" uses kitchen
    room id=r_servery at (9000,4000)  size 6000x3000 label "Servery" uses kitchen
    room id=r_store   at (12700,7000) size 2300x3000 label "Store" uses storage
  }

  # ---- doors ------------------------------------------------------------------
  door id=d_front at (4500,10000) width 1200 wall shell hinge left swing in
  door id=d_pass on w_e at 6300 width 1000 hinge near end swing into r_servery
  door id=d_kitchen on w_k at 1500 width 1000 swing into r_kitchen
  door id=d_store on w_s at 4500 width 1000 swing into r_store
  door id=d_lobby on w_e at 9200 width 1000 swing into r_lobby
  door id=d_wc on w_lb at 900 width 1000 swing into r_lobby
  opening id=o_serve on w_e at 4850 width 1200
  door id=d_goods at (15000,2000) width 1000 wall shell swing out

  # ---- windows ----------------------------------------------------------------
  window at (2000,0) width 2000 wall shell
  window at (6500,0) width 2000 wall shell
  window at (0,2500) width 2400 wall shell
  window at (0,7500) width 2400 wall shell
  window at (1800,10000) width 2000 wall shell
  window at (7200,10000) width 1800 wall shell
  window at (10500,0) width 1400 wall shell
  window at (15000,3300) width 900 wall shell

  # ---- fit-out ----------------------------------------------------------------
  # A grid of two-tops: a 800 x 800 table with a chair at each of two sides.
  for i in 0..3 {
    for j in 0..3 {
      furniture table at (1800 + i * 2400, 1400 + j * 2200) size 800x800 label "T{i * 3 + j + 1}"
      furniture chair at (1950 + i * 2400, 700 + j * 2200) size 450x450
      furniture chair at (1950 + i * 2400, 2250 + j * 2200) size 450x450
    }
  }
  furniture bench in r_dining anchor left inset 100 size 500x2400 label "Bench"
  furniture plant in r_dining anchor top-right inset 300 size 500x500

  furniture bar_counter against wall w_e offset 4850 size 1200x600 in r_servery
  furniture upper_cabinet against wall w_k offset 4500 size 1400x350 in r_servery
  furniture kitchen_sink against wall shell segment 0 offset 11000 in r_kitchen
  furniture stove against wall shell segment 0 offset 12600 in r_kitchen
  furniture fridge against wall shell segment 0 offset 14000 in r_kitchen
  furniture wc against wall w_s offset 2900 in r_wc
  furniture basin against wall w_wc offset 1500 in r_wc
}
```

### Classroom Block

A single-storey school block — **10 rooms, 315 m², 10 doors, 7 windows** at `scale 1:100` on
`paper A2 landscape`, 22.75 × 14.25 m to the outer faces. Three 60 m² classrooms open off a
2000 mm corridor; the lobby, a staff prep room, a store, pupil WCs, an accessible WC and a
plant room line the service side. The three teaching rooms are the same room, so their
furniture is written once as a loop over the classroom width (`let X = i * CW`): a teacher's
desk, twelve tables in three columns and a bookshelf run, with each door at the east end of
the corridor wall so the leaf swings away from the tables. A second exit closes the east
end of the 22.5 m corridor, so no classroom is a dead end. The zones split the floor into
180 m² of `teaching` and 135 m² of `service`, `schedule openings` tags every door and window,
and the plan is written to pass `archlang lint --profile accessibility-advisory` as well as
the default ruleset.

![classroom-block.arch, compiled](/examples/classroom-block.svg)

```arch
# A single-storey classroom block: three 60 m2 classrooms on a 2 m corridor, with the
# entrance lobby, a staff prep room, a store, pupil WCs, an accessible WC and a plant room
# along the service side.
#
# Teaching rooms are the same room three times, so the furniture is written ONCE as a loop
# over the room's west edge — `let X = i * CW` is the only thing that differs between
# classrooms — and the plan keeps the three rooms in step by construction. Each room has:
#
#   a teacher's desk on the north wall, facing a class of twelve tables in three columns,
#   a bookshelf run along the corridor wall, and a door at the EAST end of that wall,
#   so the leaf swings away from the tables and the entry path stays clear of them.
#
# Schools are exactly where the `accessibility-advisory` profile is not optional, so the
# plan is written to pass it: 1000 mm door leaves, a 2000 mm corridor, the door end of every
# classroom left clear of tables, and an accessible WC with its door on the corridor. It is
# also lint-clean under the default ruleset.
plan "Classroom Block" {
  units mm
  grid 50
  paper A2 landscape
  scale 1:100
  north up
  dims auto all
  schedule rooms
  schedule openings
  notes "All dimensions in millimetres. Do not scale from this drawing."
  notes "Classroom doors are 1000 mm leaves; a second exit closes the east end of the corridor."
  notes "Pupil WC cubicle partitions are 1500 mm deep."

  title { project "Classroom Block" drawn_by "ArchLang" date "2026-10-14" number "A-301" revision "A" }

  let CW = 7500   # classroom width

  # ---- shell and partitions ---------------------------------------------------
  wall id=shell exterior thickness 250 { (0,0) (22500,0) (22500,14000) (0,14000) close }
  wall id=w_cn partition thickness 150 { (0,8000) (22500,8000) }
  wall id=w_cs partition thickness 150 { (0,10000) (22500,10000) }
  wall id=w_c1 partition thickness 150 { (7500,0) (7500,8000) }
  wall id=w_c2 partition thickness 150 { (15000,0) (15000,8000) }
  wall id=w_s1 partition thickness 150 { (5000,10000) (5000,14000) }
  wall id=w_s2 partition thickness 150 { (9500,10000) (9500,14000) }
  wall id=w_s3 partition thickness 150 { (12500,10000) (12500,14000) }
  wall id=w_s4 partition thickness 150 { (17000,10000) (17000,14000) }
  wall id=w_s5 partition thickness 150 { (19500,10000) (19500,14000) }
  # Cubicle partitions between the three pupil pans (pans at x 13000, 14500, 16000).
  wall id=w_cub1 partition thickness 150 { (13750,12500) (13750,14000) }
  wall id=w_cub2 partition thickness 150 { (15250,12500) (15250,14000) }

  # ---- rooms ------------------------------------------------------------------
  zone teaching "Teaching" {
    room id=r_c1 at (0,0)     size 7500x8000 label "Classroom 1" uses office
    room id=r_c2 at (7500,0)  size 7500x8000 label "Classroom 2" uses office
    room id=r_c3 at (15000,0) size 7500x8000 label "Classroom 3" uses office
  }
  zone service "Service" {
    room id=r_corr  at (0,8000)      size 22500x2000 label "Corridor" uses circulation
    room id=r_lobby at (0,10000)     size 5000x4000  label "Entrance Lobby" uses entry
    room id=r_prep  at (5000,10000)  size 4500x4000  label "Staff Prep" uses office
    room id=r_store at (9500,10000)  size 3000x4000  label "Store" uses storage
    room id=r_wcp   at (12500,10000) size 4500x4000  label "Pupil WCs" uses wc
    room id=r_wca   at (17000,10000) size 2500x4000  label "Accessible WC" uses wc
    room id=r_plant at (19500,10000) size 3000x4000  label "Plant Room" uses utility
  }

  # ---- doors ------------------------------------------------------------------
  door id=d_front at (2500,14000) width 1400 wall shell hinge left swing in
  opening id=o_lobby on w_cs at 2500 width 1500
  door id=d_c1 on w_cn at 6700 width 1000 swing into r_c1
  door id=d_c2 on w_cn at 14200 width 1000 swing into r_c2
  door id=d_c3 on w_cn at 21700 width 1000 swing into r_c3
  door id=d_prep on w_cs at 7250 width 1000 swing into r_prep
  door id=d_store on w_cs at 11000 width 1000 swing into r_store
  door id=d_wcp on w_cs at 14750 width 1000 swing into r_wcp
  door id=d_wca on w_cs at 18250 width 1000 swing into r_corr
  door id=d_plant on w_cs at 21000 width 1000 swing into r_plant
  # The corridor is 22.5 m long: a second exit closes its east end, so no classroom is a dead end.
  door id=d_east at (22500,9000) width 1000 wall shell swing out

  # ---- windows: one wide glazed bay per classroom, north-lit ------------------
  window at (3750,0) width 4500 wall shell
  window at (11250,0) width 4500 wall shell
  window at (18750,0) width 4500 wall shell
  window at (0,4000) width 2000 wall shell
  window at (22500,4000) width 2000 wall shell
  window at (7250,14000) width 1800 wall shell
  window at (22500,13000) width 1000 wall shell

  # ---- fit-out ----------------------------------------------------------------
  for i in 0..3 {
    let X = i * CW
    furniture desk at (X + 3000, 500) size 1600x800 label "Teacher"
    furniture bookshelf at (X + 1500, 7450) size 3000x400 label "Books" rotate 180
    for c in 0..3 {
      for r in 0..4 {
        furniture table at (X + 1200 + c * 1800, 2100 + r * 1300) size 1200x600
      }
    }
  }
  furniture desk in r_prep anchor bottom-right inset 400 size 1600x800 label "Desk"
  furniture shelf in r_store anchor bottom inset 300 size 2000x500 label "Shelving"
  furniture wc against wall shell segment 2 offset 9500 in r_wcp
  furniture wc against wall shell segment 2 offset 8000 in r_wcp
  furniture wc against wall shell segment 2 offset 6500 in r_wcp
  furniture wc against wall shell segment 2 offset 4250 in r_wca
  furniture basin against wall w_s4 offset 3000 in r_wcp
  furniture basin against wall w_s5 offset 2000 in r_wca
}
```

## Geometry & experiments

### Gallery L (rooms that are not rectangles)

Two [polygonal rooms](/reference#polygonal-rooms-v1-23) — **2 rooms, 153 m²** on
`paper A3 landscape`, which auto-fits to 1:100: an **L**-shaped gallery wrapping a
**trapezoid** lobby, so the building has an angled south-west facade.
`room polygon (x,y) …` replaces `at` + `size` with the room's
own ring, and everything downstream follows the ring rather than a bounding box — the
gallery reports its exact **132 m²** (its box would claim 168), the two rooms read as
adjacent across the boundary they actually share, and the label sits at the polygon's
centroid (the lobby pins its own with `label "…" at (x,y)`). The entrance door is hosted on
the *angled* wall the ordinary way, and `dims auto all` measures the chain off the rooms'
vertex coordinates.

![gallery-l.arch, compiled](/examples/gallery-l.svg)

```arch
# Polygonal rooms (v1.23): a floor whose spaces are not rectangles.
#
# `room polygon (x,y) …` gives a room an explicit, implicitly-closed ring instead of
# `at` + `size`. Two shapes a rectangle cannot express are drawn here:
#
#   · gallery — an L, wrapping the entrance hall on two sides. Its area (132 m²) is the
#     exact shoelace area of the ring, not its 12 × 14 m bounding box.
#   · lobby   — a trapezoid, so the building has an angled south-west facade. The joint
#     where that facade meets the south wall is 40°, which is what the wall outline's
#     mitre cap is there for.
#
# Everything else is ordinary: the walls, the doors and `dims auto` neither know nor
# care that a room is polygonal.
plan "Gallery L" {
  units mm
  grid 100
  paper A3 landscape
  north up
  theme presentation
  dims auto all
  title { project "Polygon rooms" drawn_by "ArchLang" }

  wall exterior thickness 300 {
    (0,0) (12000,0) (12000,9000) (6000,14000) (0,14000) close
  }
  wall id=spine partition thickness 150 { (6000,8000) (6000,14000) }
  wall id=north_face partition thickness 150 { (6000,8000) (12000,8000) }

  room id=gallery polygon (0,0) (12000,0) (12000,8000) (6000,8000) (6000,14000) (0,14000)
    label "Gallery"
  room id=lobby polygon (6000,8000) (12000,8000) (12000,9000) (6000,14000)
    label "Lobby" at (8200,10200) uses entry

  # The entrance sits on the ANGLED south-west facade, so it opens into the trapezoid
  # lobby — a door on a wall that is not axis-aligned, hosted the ordinary way.
  door id=entry on exterior at 51% width 1500 swing in
  # The facade chains measure along x and y, so none can tick a door on the angled face;
  # `lint` reports it (W_OPENING_NOT_DIMENSIONED) until it is dimensioned by hand. This
  # one is drawn ON the opening, jamb to jamb (offset 0).
  dim (8400,12000)->(9600,11000) offset 0 text "1500"
  door id=inner on spine at 50% width 1200 hinge left swing in
  window id=north_light on exterior at 20% width 3000

  # ---- fit-out ----------------------------------------------------------------
  # Polygon rooms refuse the anchor forms (E_PLACE_POLY — a nine-point anchor
  # needs a rectangle), so every piece here is an absolute `at`, kept clear of the
  # L's missing quadrant (x 6000-12000, y 8000-14000 is the lobby's, not the
  # gallery's) and of the two doors' swing quarter-discs.
  furniture bench at (2000,2000)   size 1800x500 in gallery
  furniture bench at (8000,2000)   size 1800x500 in gallery
  furniture plant at (10500,6500)  size 700x700  in gallery
  furniture plant at (1000,12800)  size 700x700  in gallery
  furniture desk  at (2000,11000)  size 1800x800 label "Reception desk" in gallery
  furniture chair at (4200,11500)  size 500x500  in gallery
}
```

### Hexagon Pavilion

Six trapezoidal galleries ringing a circular rotunda — **7 rooms, 122.79 m²** on a
15 × 12 m hexagonal footprint. Every other example is built from rectangles, because that is
what a floor plan usually is; this one is not rectangular anywhere, and it exists to show
that [polygon rooms](/reference#polygonal-rooms-v1-23),
[circular rooms](/reference#circular-rooms-v1-24) and
[`arc` wall edges](/reference#curved-walls-arc-edges-v1-24) compose.

**The hexagon is not regular, and that is the point.** A regular hexagon of circumradius R
carries four of its vertices at an irrational offset no grid can express, so its sloped
facades never measure a round anything — set one out at R = 7500 on a 50 mm grid and each
nominal 7500 edge comes back 7504. A drawing that calls that 7500 is lying, and one that
calls it 7504 looks like a mistake. So this shell is built on a 3-4-5 triangle instead:
every sloped facade steps 4500 across and 6000 down, which is exactly 7500, and every vertex
lands on the grid. The drum in the middle is one wall of two semicircular arcs on a
centreline of radius 3000 — the inner hexagon's inradius, so it is tangent to all six
galleries' inner edges, which is what lets the rotunda be `room circle … radius 3000`
(exactly 28.27 m² = πR²) and counts each gallery as adjacent to it. The six cased openings
are positioned `on drum at <pct>`, which walks the wall by **arc length**, so 8.333 % is 30°
— square onto the middle of a gallery. Its palette comes from a single colour with
`theme from "#2f6f4e"`.

![hexagon-pavilion.arch, compiled](/examples/hexagon-pavilion.svg)

```arch
# A hexagonal garden pavilion: six trapezoidal galleries ringing a circular rotunda.
#
# Every other example in examples/ is built from rectangles, because that is what a
# floor plan usually is. This one is not rectangular ANYWHERE, and it exists to show
# that the two non-rectilinear room forms compose with a curved wall:
#
#   room … polygon (x,y) …            an explicit ring — exact shoelace area, label at
#                                     the centroid, no bounding box anywhere in the answer
#   room … circle at (cx,cy) radius   a round floor, area measured as exactly piR^2
#   arc (x,y) radius R                a curved WALL EDGE, drawn as a true SVG/DXF arc
#
# THE HEXAGON IS NOT REGULAR, AND THAT IS THE POINT. A regular hexagon of circumradius R
# carries four of its vertices at y = +/- R*sqrt(3)/2 — an irrational number no grid can
# express — so its sloped facades never measure a round anything: set one out at R = 7500
# on a 50 mm grid and each nominal 7500 edge comes back 7504. A drawing that calls that
# 7500 is lying, and one that calls it 7504 looks like a mistake. So this shell is built
# on a 3-4-5 triangle instead. Every sloped facade steps 4500 across and 6000 down, which
# is exactly 7500, and every vertex lands on the grid:
#
#     (16000,7000) (11500,1000) (5500,1000) (1000,7000) (5500,13000) (11500,13000)
#
# Four facades of 7500, two of 6000, 15000 x 12000 overall — and every number on the sheet
# is a whole millimetre a builder can set out with a tape.
#
# A second hexagon at exactly half scale sits inside it. The six galleries are the
# trapezoids BETWEEN the two: four points each, two outer vertices and the two inner ones
# behind them — 7500 or 6000 on the facade, half that on the inside, 3000 deep. Six radial
# spokes run along the joins, and a masonry drum fills the middle.
#
# The drum is the part worth reading twice. It is written as ONE wall of two semicircular
# `arc` edges (there is no close-an-arc form: a closed curve is the two halves it is), on
# a centreline of radius 3000 and 1200 thick. 3000 is not a free choice: it is the inner
# hexagon's INRADIUS, so the drum's centreline is the circle inscribed in that hexagon,
# touching all six of the galleries' inner edges. That is what lets the rotunda be
# `room circle … radius 3000` — the same centreline, which is the ordinary ArchLang
# convention that a room's boundary is its wall's centreline, and the reason each gallery
# counts as ADJACENT to the rotunda rather than 300 mm short of it. The outer face lands
# at 3600, past the inner hexagon's corners at 3354, so the drum swallows the galleries'
# inner edge instead of leaving a crescent of unwalled floor behind it, and
# `dim diameter rotunda` calls out a round phi 6000. The six cased openings are positioned
# `on drum at <pct>`, which walks the wall by ARC LENGTH — so 8.333% is 30 degrees, square
# onto the middle of a gallery.
#
# Three things a hexagon teaches about ANNOTATION.
#
#   1. `dims auto overall` is the only auto mode worth having here: it gives the building
#      one bounding chain, one R leader per distinct arc and one phi per circular room.
#      The room and wall chains have no axis to lie along on a 53-degree facade, so the
#      two north facades are dimensioned BY HAND. Endpoint ORDER is what puts each line
#      outside, because the offset runs along the LEFT normal of from->to — write either
#      pair backwards and the line is drawn through the building (`W_DIM_INSIDE`, with a
#      machine-applicable swap). And the OFFSET has to be bigger than it looks: the rule
#      measures the drawn line's midpoint against the building's room-extents BOX, so on
#      a splayed facade clearing the wall is not enough — 1800 is still inside the
#      rectangle, 3000 is out in the margin where a dimension belongs.
#   2. Neither hand dim carries a `text` override, and neither needs one: the facade
#      really is 7500, so that is what the drawing says.
#   3. A polygon room labels itself at its CENTROID, and here the centroid is the worst
#      place available — a 3000-deep trapezoid whose middle is where the drum bulges past
#      it — while the phi call-out is drawn ON the diameter, exactly where the rotunda's
#      own name would go. So every room pins its name by hand with `label "…" at (x,y)`,
#      out on the radius that bisects its own wedge, midway between the drum face and the
#      facade. That is the one place in this file where the author knows something the
#      centroid does not.
#
# The palette is derived from a single colour with `theme from "#2f6f4e"` — one hex value
# becomes the whole set of wall, poche, floor and annotation inks.
plan "Hexagon Pavilion" {
  units mm
  grid 50
  scale 1:50
  north up
  dims auto overall

  theme from "#2f6f4e"

  # ---- shell: the outer hexagon ---------------------------------------------
  # Walked from the east vertex clockwise on the page, so the six facades run
  # 0-7500 north-east, 7500-13500 north, 13500-21000 north-west, 21000-28500 south-west,
  # 28500-34500 south, 34500-42000 south-east. The openings below are positioned in those
  # millimetres rather than percentages, because a hexagon of two edge lengths has no
  # single percentage that means "the middle of a facade".
  wall id=shell exterior thickness 300 {
    (16000,7000)
    (11500,1000)
    (5500,1000)
    (1000,7000)
    (5500,13000)
    (11500,13000)
    close
  }

  # ---- the drum: one full circle, written as its two halves -----------------
  wall id=drum partition thickness 1200 {
    (11500,7000)
    arc (5500,7000) radius 3000
    arc (11500,7000) radius 3000
  }

  # ---- the six spokes -------------------------------------------------------
  # Each runs from an inner hexagon vertex out to the outer vertex beyond it, so the
  # galleries' side edges and these walls are the same six lines.
  wall id=spoke_e  partition thickness 200 { (12250,7000) (16000,7000) }
  wall id=spoke_ne partition thickness 200 { (10000,4000) (11500,1000) }
  wall id=spoke_nw partition thickness 200 { (7000,4000)  (5500,1000) }
  wall id=spoke_w  partition thickness 200 { (4750,7000)  (1000,7000) }
  wall id=spoke_sw partition thickness 200 { (7000,10000) (5500,13000) }
  wall id=spoke_se partition thickness 200 { (10000,10000) (11500,13000) }

  # ---- rooms ----------------------------------------------------------------
  room id=rotunda circle at (8500,7000) radius 3000 label "Rotunda" at (8500,8300) uses hall

  room id=g_ne polygon (16000,7000) (11500,1000) (10000,4000) (12250,7000)
    label "Reading" at (12250,4200) uses living
  room id=g_n  polygon (11500,1000) (5500,1000) (7000,4000) (10000,4000)
    label "Studio" at (8500,2300) uses office
  room id=g_nw polygon (5500,1000) (1000,7000) (4750,7000) (7000,4000)
    label "Tea Room" at (4750,4200) uses dining
  room id=g_sw polygon (1000,7000) (5500,13000) (7000,10000) (4750,7000)
    label "Store" at (4750,9800) uses storage
  room id=g_s  polygon (5500,13000) (11500,13000) (10000,10000) (7000,10000)
    label "Entrance" at (7600,11700) uses entry
  room id=g_se polygon (11500,13000) (16000,7000) (12250,7000) (10000,10000)
    label "Gallery" at (12250,9800) uses living

  # ---- portals: six cased openings on the curve -----------------------------
  opening id=p_ne on drum at 8.333%  width 1400
  opening id=p_n  on drum at 25%     width 1400
  opening id=p_nw on drum at 41.667% width 1400
  opening id=p_sw on drum at 58.333% width 1400
  opening id=p_s  on drum at 75%     width 1400
  opening id=p_se on drum at 91.667% width 1400

  # ---- entrance and glazing -------------------------------------------------
  # The entrance slides. A leaf on hinges would sweep a 1.4 m quarter-disc across the
  # middle of the one room that has to stay clear; a `sliding` leaf sweeps nothing at all,
  # so no swing rule applies to it and the entrance keeps its own floor.
  door id=d_main sliding on shell at 31500 width 1400 slide left

  window id=win_ne on shell at 3750  width 2400
  window id=win_n  on shell at 10500 width 2400
  window id=win_nw on shell at 17250 width 2400
  window id=win_sw on shell at 24750 width 2400
  window id=win_se on shell at 38250 width 2400

  # ---- dimensions -----------------------------------------------------------
  dim diameter rotunda
  dim (16000,7000)->(11500,1000) offset 3000
  dim (5500,1000)->(1000,7000)   offset 3000

  # ---- loose furniture ------------------------------------------------------
  # Polygon rooms refuse the anchor forms (E_PLACE_POLY — a nine-point anchor needs a
  # rectangle), so every free-standing piece here is an absolute `at`. The two that back
  # onto something use `against wall`, which stays legal on a polygon room because it
  # measures from the WALL and not from the room.
  #
  # An axis-aligned piece in a wedge gallery has to be set out against a SLOPED wall, and
  # its far corner is what reaches that wall first — not its edge. Both benches below sat
  # 100-130 mm inside a wall solid until `W_FURNITURE_WALL_COLLISION` learned to measure a
  # wall that is not axis-aligned; nothing had ever checked. Keep them west of where they
  # were: f_bench clears the sloped south-east facade, f_seat clears spoke_se.
  furniture id=f_desk  counter against wall shell segment 1 offset 3000 size 3000x500 label "Work bench" in g_n
  furniture id=f_shelf counter against wall spoke_w offset 1400 size 1200x600 label "Shelves" in g_sw
  furniture id=f_read  table at (13150,5550)  size 900x900  label "Table" in g_ne
  furniture id=f_tea   table at (3850,5550)   size 900x900  label "Tea"   in g_nw
  furniture id=f_bench bench at (12950,8450)  size 1400x450 label "Bench" in g_se
  furniture id=f_seat  bench at (9450,12000)  size 1400x450 label "Bench" in g_s

  title {
    project "Hexagon Pavilion"
    drawn_by "ArchLang"
    date "2026-08-16"
  }
}
```

### Terrace Row

Four terrace dwellings generated from **one component, placed and mirrored** — **16 rooms,
199.8 m², 16 doors, 18 windows** across 22.2 × 9.6 m. A terrace is the same plan four times,
so it is written once: `component unit(w, d, gable)` in its own coordinates from (0,0), then
four [`place`](/reference#placing-instances-place-v1-22) statements, alternating `mirror x`.

`mirror x` reflects about the *instance's* own origin, so a mirrored unit is placed at its
far x edge. Alternating it is what makes a terrace a terrace: u1 and u2 meet local-x=w to
local-x=w, so their bathrooms and kitchen runs back onto one shared party wall, and u2 and u3
meet local-x=0 to local-x=0. The consequence is that the row's two free gables are both the
local x=0 wall — which is why `gable` is 1 for the end units and 0 for the middle two, and why
the `if` inside the component puts a side window on that wall and nowhere else. The offsets
are arithmetic rather than typing (`widths` is an array and the placements are a running sum
of it), the 600 mm stagger is one coordinate, and three of the four room names are pinned with
`label "…" at (x,y)` written in the **component's** coordinates — a pinned label is a point
like any other, so `place` carries it through the frame and the mirrored units get the
mirrored position for free. `theme blueprint` is a named theme base: a whole cyanotype palette
from one word.

![terrace-row.arch, compiled](/examples/terrace-row.svg)

```arch
# A row of four terrace dwellings generated from ONE component, placed and mirrored.
#
# A terrace is the same plan four times, so it should be written once. `component` +
# `place` is how ArchLang says that, and this file is the smallest complete example of
# the pattern:
#
#   component unit(w, d, gable) { … }        one dwelling, authored from (0,0) in its
#                                            OWN coordinates, parameterised by width
#   place unit(W1, DEPTH, 1) as u1 at (…)    an addressable INSTANCE — every id inside
#                                            becomes `u1.<id>`, auto-ids restart per
#                                            instance, so the four are order-independent
#   … as u2 at (…) mirror x                  a real reflection: door swings, hinges and
#                                            the whole fit-out come out mirror-image
#
# Three things about the row are worth reading closely.
#
# MIRRORING PAIRS THE UNITS. `mirror x` reflects about the INSTANCE's own origin, so a
# mirrored unit is placed at its FAR x edge — u2 is 5400 wide and placed at 10800, which
# puts it on 5400…10800. Alternating the mirror is what makes a terrace a terrace: u1 and
# u2 meet local-x=w to local-x=w, so their bathrooms and kitchen runs back onto one shared
# party wall, and u2 and u3 meet local-x=0 to local-x=0. The consequence to notice is that
# the row's two FREE gables are both the local x=0 wall — which is why `gable` is 1 for u1
# and u4 and 0 for the two in the middle, and why the `if` inside the component puts a side
# window on that wall and nowhere else.
#
# THE OFFSETS ARE ARITHMETIC, NOT TYPING. `widths` is an array and X2/X3/X4 are a running
# sum of it. `place` cannot go inside a `for` (the `as <name>` is a plain identifier, not
# an interpolation), so the four lines are unrolled — but change a width and every unit
# after it moves on its own.
#
# THE STAGGER IS ONE COORDINATE. Alternate units sit 600 further from the street, which on
# this sheet means u2 and u4 are placed at y = 0 and u1 and u3 at y = SETBACK. The party
# walls still coincide over everything but that 600, so each junction draws the short
# exterior return a sawtooth terrace actually has.
#
# WHY THE PLAN IS THIS PLAN. A row of four is drawn on ONE sheet, so the unit that survives
# is the one whose rooms are still legible at a quarter of the width — which is a drawing
# constraint, not an architectural one, and it is worth being honest that it drove the
# layout. Four rooms, not five: living and kitchen are one front room with the galley run
# on the party wall, the way a 5.4 m frontage is actually built. Names of one word. Nothing
# narrower than 1.8 m. The test is not "does it compile" but "does every room still say its
# own name inside its own walls" — so `arch describe --json` reporting four rooms per unit
# and the PNG showing sixteen unobstructed labels are the same check, run two ways. Three
# of the four names are therefore PINNED with `label "…" at (x,y)`, written in the
# COMPONENT's own coordinates: a pinned label is a point like any other, so `place` carries
# it through the frame, and the two mirrored units get the mirrored position for free. Get
# the name right in one unit and it is right in four.
#
# `theme blueprint` is a NAMED theme base — a whole cyanotype palette from one word, with
# no `theme { … }` block to maintain.
plan "Terrace Row" {
  units mm
  # 50, not 100: the partitions are 100 thick and the fit-out is anchored `flush` to
  # their inner faces, which lands on half-thickness coordinates.
  grid 50
  north up
  dims auto overall

  theme blueprint

  let DEPTH   = 9000            # every unit is the same depth; only the width varies
  let SETBACK = 600             # the sawtooth: alternate units step back from the street
  let widths  = [5400, 5400, 6000, 5400]

  let W1 = widths[0]
  let W2 = widths[1]
  let W3 = widths[2]
  let W4 = widths[3]

  # Running sum of the widths — the left edge of each unit in plan coordinates.
  let X1 = 0
  let X2 = X1 + W1
  let X3 = X2 + W2
  let X4 = X3 + W3

  # ---- one dwelling ---------------------------------------------------------
  component unit(w, d, gable) {
    let BACK  = 4000            # the sleeping band, at the back of the plot
    let HALLD = 1800            # the cross hall between the two bands
    let BATHW = 2200
    let BEDW  = w - BATHW
    let FRONT = d - BACK - HALLD

    wall id=shell    exterior  thickness 250 { (0,0) (w,0) (w,d) (0,d) close }
    wall id=w_bath   partition thickness 100 { (BEDW,0) (BEDW,BACK) }
    wall id=w_hall_n partition thickness 100 { (0,BACK) (w,BACK) }
    wall id=w_hall_s partition thickness 100 { (0,BACK + HALLD) (w,BACK + HALLD) }

    room id=bed    at (0,0)            size BEDW x BACK  label "Bed"    at (1400,800)        uses bedroom
    room id=bath   at (BEDW,0)         size BATHW x BACK label "Bath"   at (BEDW + 1100,2100) uses bath utility
    room id=hall   at (0,BACK)         size w x HALLD    label "Hall"   uses hall circulation
    room id=living at (0,BACK + HALLD) size w x FRONT    label "Living" at (w / 2,6900)     uses living dining kitchen

    # Front door off the street into the front room; the garden door is a slider, so no
    # leaf sweeps the bedroom floor.
    door id=front at (1700,d) width 950 wall shell swing into living
    door id=rear sliding at (1200,0) width 1400 wall shell slide left

    door    id=d_bed  on w_hall_n at 2300 width 800 swing into bed
    # `on <wall> at <pos>` takes a literal position, so the one opening whose distance
    # depends on the unit WIDTH is written as a point on the same wall's centreline.
    door    id=d_bath at (w - 1400,BACK) width 800 wall w_hall_n swing into bath
    opening id=o_hall on w_hall_s at 900 width 1000

    window id=win_liv  at (600,d)        width 900  wall shell
    window id=win_liv2 at (w - 900,d)    width 1000 wall shell
    window id=win_bed  at (BEDW - 450,0) width 800  wall shell
    window id=win_bath at (w - 500,0)    width 500  wall shell

    # Only the two units with a free gable get a side window — the other two have a
    # party wall there, and a window in a party wall is not a window.
    if gable == 1 {
      window id=win_gable at (0,2100) width 1200 wall shell
    }

    # Fit-out by anchor: every piece is measured from its room's backing wall FACE
    # (`flush`), so nothing here writes a coordinate or a half-thickness.
    furniture id=f_bed  bed      in bed anchor bottom-left flush inset 100 size 1400x1900 label "Bed"
    furniture id=f_ward wardrobe in bed anchor right        flush size 600x1200

    furniture id=f_shower shower in bath anchor top-left     flush size 900x900
    furniture id=f_basin  basin  in bath anchor top-right    flush size 600x450
    furniture id=f_wc     wc     in bath anchor bottom-right flush size 400x700

    furniture id=f_sofa  sofa  in living anchor bottom-left flush size 800x1800 label "Sofa"

    # The galley run rides the party wall: `against` derives position, depth and the
    # quarter-turn from the wall, and each catalogued fixture brings its own footprint.
    furniture id=f_sink   kitchen_sink against wall shell segment 1 offset 6300 in living
    furniture id=f_stove  stove        against wall shell segment 1 offset 7200 in living
    furniture id=f_fridge fridge       against wall shell segment 1 offset 8100 in living
  }

  # ---- the row --------------------------------------------------------------
  # A mirrored instance is placed at its FAR x edge, so u2 covers X2…X2+W2 and u4 covers
  # X4…X4+W4. u1 and u4 carry the gable flag; u2 and u4 take the sawtooth's front row.
  place unit(W1, DEPTH, 1) as u1 at (X1, SETBACK)
  place unit(W2, DEPTH, 0) as u2 at (X2 + W2, 0) mirror x
  place unit(W3, DEPTH, 0) as u3 at (X3, SETBACK)
  place unit(W4, DEPTH, 1) as u4 at (X4 + W4, 0) mirror x

  title {
    project "Four-unit terrace"
    drawn_by "ArchLang"
    date "2026-08-16"
  }
}
```

## Scripting & composition

### Relational (right-of / below)

**4 rooms, 60 m²** at `scale 1:100`, positioned relative to one another with `right-of` /
`below`, `align` and `gap` — resolved to absolute coordinates by deterministic arithmetic in
topological order, not an optimizer (see [relational placement](/relational)).

![relational.arch, compiled](/examples/relational.svg)

```arch
# Relational placement (v1.0): rooms positioned relative to one another with
# `right-of` / `left-of` / `below` / `above` (+ `align` and `gap`), resolved to
# absolute coordinates by pure arithmetic in dependency order. Only `living` is
# anchored with `at`; every other room derives its position from a neighbour, so
# changing the anchor or a size reflows the whole plan deterministically.
#
# FOUR DELIBERATE WARNINGS, and all four are one fact about the language:
#
#   W_ROOM_DISCONNECTED x3 (Kitchen, Bedroom, Bath) + W_ROOM_NOT_ENCLOSED x1 (Bath)
#
# A relationally-laid plan cannot carry partitions that reflow with it. Rooms resolve to
# coordinates, but nothing can read a resolved room boundary back OUT: a wall point is an
# expression, expressions see `let` constants and the nine built-ins, and there is no
# `kitchen.x` — writing one is `E_UNKNOWN_REF`, not a coordinate. So the four interior
# boundaries here (living|kitchen, living|bedroom, kitchen|bath, bedroom|bath) carry no
# wall, no wall is there to host a door, and three of the four rooms have no way in.
#
# Both obvious repairs were measured, and both cost more than they buy:
#
#   * A cased `opening` on a wall-free boundary clears W_ROOM_DISCONNECTED from
#     `arch lint` and raises W_OPENING_OFF_WALL from `arch validate` — `lint` is the
#     soundness layer alone, `validate` is parse + resolve + lint. That is one warning
#     traded for another, and the second one is the true one.
#   * Absolute partitions at x=5000 and y=4000 would compile clean and would make this
#     file a plan whose ROOMS reflow and whose WALLS silently do not — the trap the file
#     exists to warn about. Hoisting every dimension into `let` constants and writing the
#     walls as arithmetic over them fixes the *size* case only: change `bath right-of bed`
#     to `bath below kitchen` and the walls stay exactly where they were. It also rebuilds
#     by hand the coordinate model `right-of` exists to remove.
#
# So these four stand. `arch validate` is clean; `arch validate --strict` reports them.
# Everything NOT caused by that limitation was repaired rather than documented: the
# kitchen and the bath have their serviced fixtures, and the bedroom has a window.
plan "Relational 1BR" {
  units mm
  grid 50
  scale 1:100
  north up

  # Exterior shell (walls are always absolute — relational placement is rooms-only).
  wall exterior thickness 200 { (0,0) (8000,0) (8000,7500) (0,7500) close }

  room id=living  at (0,0)                         size 5000x4000 label "Living"
  room id=kitchen right-of living align top  gap 0 size 3000x4000 label "Kitchen"
  room id=bed     below living    align left gap 0 size 5000x3500 label "Bedroom"
  room id=bath    right-of bed    align top  gap 0 size 3000x3500 label "Bath"

  door   at (1000,0) width 1000 wall exterior hinge left swing in
  window at (6500,0) width 1800 wall exterior
  window at (3200,7500) width 1600 wall exterior

  # Head against the south wall — the only edge of this room the bed's 1500 mm head can
  # back onto, since the boundary it shares with the living room carries no wall.
  furniture bed at (300,5400) size 1500x2000 label "Bed" rotate 180

  # A handful of freestanding pieces in the two upper rooms — this file's only wall is
  # the exterior shell (relational placement is rooms-only, so there are no partitions
  # to write `against wall` on), so everything below is a plain `at`.
  furniture sofa         at (200,2900) size 1800x800 label "Sofa" in living
  furniture coffee_table at (200,2000) size 900x500               in living
  furniture dining_table at (5300,300)  size 2400x1200 label "Table" in kitchen
  furniture chair        at (5600,1700) size 450x450               in kitchen
  furniture chair        at (6800,1700) size 450x450               in kitchen

  # The serviced pieces. Every one of these is `requiresWall` — a sink, a stove, a WC and
  # a basin are plumbed or vented, so each backs onto the only wall this plan has, the
  # exterior shell. That is also why the kitchen run and the bathroom run both sit on the
  # EAST facade: the boundaries these two rooms share with their neighbours carry no wall
  # to back onto (see the note in the header).
  furniture sink   at (7300,2400) size 600x800 rotate 90 in kitchen
  furniture stove  at (7300,3300) size 600x600 rotate 90 in kitchen

  furniture wc     at (7200,4300) size 700x400 rotate 90 in bath
  furniture basin  at (7450,5000) size 450x600 rotate 90 in bath
  furniture shower at (5200,6500) size 900x900            in bath

  # Overall width, outside face to outside face (`faces` projects each endpoint onto
  # the wall it runs into, so it prints the true 8200 rather than the 8000 room grid).
  dim faces (0,7500)->(8000,7500) offset 600

  title {
    project "Relational Layout Demo"
    drawn_by "ArchCanvas"
    date "2026-06-26"
  }
}
```

### Parametric (let + for)

**3 rooms, 60 m², 3 doors, 3 windows** — a row of repeated units generated from `let`
bindings, a value-function, an array, a scoped `set`, a `for` loop and string interpolation.
It is the demonstration that [scripting](/reference#control-flow) expands **at compile time**
into a fixed, deterministic drawing: there is no runtime, no clock and no randomness, so the
row you see is exactly what the loop produced.

![parametric.arch, compiled](/examples/parametric.svg)

```arch
# Parametric plan (v0.8 scripting): a row of studio units generated with a
# `for` loop over a range, a value-function, an array indexed per unit, a scoped
# `set` rule, an `if`, and string-interpolated labels. Everything is derived
# from the constants — change COUNT and the whole row regenerates.
plan "Parametric — Studio Row" {
  units mm
  grid 50
  scale 1:100
  north up

  # Plan-level constants (visible everywhere below — plan scope is global).
  let WALL  = 200
  let W     = 4000          # unit width
  let H     = 5000          # unit depth
  let DOOR  = 900
  let WIN   = 1600
  let COUNT = 3             # number of units

  # A value-function (pure closure) — area in square metres.
  let aream2(w, h) = w * h / 1000000

  # Per-unit names, indexed by the loop variable.
  let names = ["Studio A", "Studio B", "Studio C"]

  # Entrance doors swing outward throughout this plan (scoped default).
  set door(swing: out)

  for i in 0..COUNT {
    let x = i * W
    wall exterior thickness WALL { (x, 0) (x + W, 0) (x + W, H) (x, H) close }
    room at (x, 0) size W x H label "{names[i]}"
    furniture bed   at (x + 300, 300)      size 1500x2000 label "Bed"
    furniture kitch at (x + W - 1900, 300) size 1600x600  label "Kitchen"
    door   at (x + W / 2, H) width DOOR wall exterior hinge left
    window at (x + W / 2, 0) width WIN  wall exterior

    # The end unit carries a per-unit area dimension (computed by the function).
    # Referenced to the outer face (y = H + WALL/2) so the extension lines start at
    # the wall and read downward, away from the building.
    if i == COUNT - 1 {
      dim (x, H + WALL / 2)->(x + W, H + WALL / 2) offset 600 text "{aream2(W, H)} m² each"
    }
  }

  # Overall run, dimensioned above the building: right-to-left so the offset lands
  # ABOVE the row (outside), and referenced to the outer top face (y = -WALL/2).
  dim (W * COUNT, 0 - WALL / 2)->(0, 0 - WALL / 2) offset 1300 text "{COUNT} units"
}
```

### Accessible metadata

**2 rooms, 24 m²** — the smallest plan that carries
[`accTitle` and `accDescr`](/reference#accessible-metadata-acctitle-accdescr), which supply
the SVG `<title>`/`<desc>` emitted by `archlang compile --accessible`. They are **metadata
only**: the default output is byte-identical without the flag. Omit them and the description
is derived from [`describe()`](/analysis)'s caption instead.

![accessible.arch, compiled](/examples/accessible.svg)

```arch
# Accessible metadata demo — `accTitle` / `accDescr`.
#
# These two plan-level keywords supply the accessible SVG `<title>` and `<desc>`
# used by `arch compile --accessible` (and `compile(src, { accessible: true })`).
# They OVERRIDE the defaults: `accTitle` replaces the plan name in `<title>`, and
# `accDescr` replaces the auto-derived describe() caption in `<desc>`. Default
# (non-accessible) output is unaffected — this is metadata only.
plan "Flat 2B" {
  units mm
  grid 50
  north up
  accTitle "Two-room flat — accessible floor plan"
  accDescr "A small flat: a living room with the entrance, and a bedroom off it, joined by a single interior door."

  wall exterior  thickness 200 { (0,0) (6000,0) (6000,4000) (0,4000) close }
  wall partition thickness 100 { (3500,0) (3500,4000) }

  room id=r_living at (0,0)    size 3500x4000 label "Living" uses living
  room id=r_bed    at (3500,0) size 2500x4000 label "Bedroom" uses bedroom

  door    id=d_main at (1500,0)    width 1000 wall exterior  hinge left swing in
  opening id=o_int  at (3500,2000) width 900  wall partition

  window at (5000,4000) width 1200 wall exterior

  # ---- fit-out ----------------------------------------------------------------
  # Kept clear of the entrance door's swing quarter-disc (radius 1000, hinge left
  # at x=1000) and of the interior opening.
  furniture sofa         at (300,3100)  size 1600x700 in r_living
  furniture coffee_table at (400,2300)  size 600x400  in r_living
  furniture plant        at (2900,3200) size 500x500  in r_living

  # The bed sits clear of the interior opening (x 3550-4300 stays a 750 mm-wide
  # entry corridor into the room), so the accessible route through it keeps its
  # 700 mm minimum.
  furniture bed        at (4300,100)  size 1500x2000 in r_bed
  furniture nightstand at (4300,2150) size 450x400   in r_bed
  furniture table      at (4300,3100) size 900x700   label "Table" in r_bed
}
```

## Style

### Themed (blueprint + brick)

**2 rooms, 24 m²** with a custom `theme { … }` block and a brick wall **material** (hatch) —
the same geometry, restyled. Nothing about a theme touches geometry, which is why the
playground's theme selector can restyle any plan on this page. See
[theming](/reference#theming) and [materials](/reference#wall).

![themed.arch, compiled](/examples/themed.svg)

```arch
# Theming + materials: a two-room plan with a brick exterior, a partition,
# and a dark "blueprint" theme. Try CompileOptions.theme to override at runtime.
#
# ONE DELIBERATE WARNING, left in on purpose:
#
#   W_NO_ENTRANCE x1 — this plan has exactly one door and it is on the PARTITION, so
#   the two rooms open onto each other and neither opens onto the outside. That is not
#   an oversight: the file is a two-room fragment whose subject is the `theme` block
#   and the `material brick` clause, and it is deliberately the corpus's one example of
#   a building with no way in, so the rule has a shipped positive case to fire on
#   (`test/lint.test.ts` pins `themed.arch` as the SOLE `W_NO_ENTRANCE` example across
#   all thirty examples — cutting a front door here would silence that pin as well as
#   the warning). `arch validate` is clean; `arch validate --strict` reports this one.
plan "Themed Duplex" {
  units mm
  grid 50
  scale 1:100
  north up

  theme {
    background: "#1e2127"
    wall: "#e8e8e8"
    wallFill: "#3a3f4b"
    wallHatch: "#5a6172"
    room: "#272b33"
    roomLabel: "#f0f0f0"
    areaLabel: "#9aa0aa"
    furniture: "#2f343d"
    dim: "#6cb6ff"
    annotation: "#cfd3da"
    font: "Georgia, serif"
    lineWeight: 1.3
  }

  let W = 6000
  let H = 4000

  wall exterior thickness 250 material brick { (0,0) (W,0) (W,H) (0,H) close }
  wall partition thickness 100 { (3000,0) (3000,H) }

  room id=a at (0,0)    size 3000x4000 label "Living"
  room id=b at (3000,0) size 3000x4000 label "Bedroom"

  furniture bed at (3300,300) size 1500x2000 label "Bed"
  furniture nightstand at (4850,700) size 300x350 in b
  furniture desk        at (300,300)  size 1200x600 in a
  furniture office_chair at (900,1000) size 450x450 in a

  door   at (3000,2000) width 900  wall partition hinge left swing in
  window at (1500,0)    width 1600 wall exterior
  window at (4500,0)    width 1200 wall exterior

  # `faces` measures outside-to-outside: 6000 of room grid + half the 250 shell at
  # each end = the 6250 the building actually is.
  dim faces (0,H)->(W,H) offset 600
}
```

### Materials (the whole hatch palette)

A 16 × 8 m maker-space — **4 rooms, 128 m², 5 doors, 6 windows**, `paper A3 landscape` at
`scale 1:50` — whose walls carry the entire wall-material palette, one per side. Poché is not
decoration: on a real drawing the hatch inside a wall **is** the specification, and ArchLang
spells that as one optional clause on `wall`:
`material poche|concrete|brick|insulation|tile|none [scale <n>] [angle <deg>]`.

All six appear here, each on the element a builder would actually detail that way and each on
a wall long enough to read: 300 mm **concrete** on the two long facades (the thickest thing in
the building, because that is what carries the roof), 230 mm **brick** on the east gable with
`scale 1.5 angle 90` so the courses run along the wall instead of across it, a 200 mm
**insulation** leaf drawn as its own wall so the leaf rather than the assembly carries the
hatch, **tile** on the two WC partitions, **none** on the glazed office screen (deliberately
empty rather than defaulted), and the default poché on the two service partitions. The sheet
is what makes it readable, and it is the second thing this file is for: at 1:50 a 100 mm
partition draws 2 mm wide and a 300 mm facade 6 mm; halve that to 1:100 and they become 1 mm
and 3 mm — the walls still look right and the hatch inside them is gone. A material sampler
has to be issued at a scale where the poché survives, so the scale is written down rather
than left to the auto-fit. The `legend` beside it is derived: one swatch per material
actually used and one per fixture category actually placed, growing a row on its own if you
add a seventh.

![materials.arch, compiled](/examples/materials.svg)

```arch
# A 16 x 8 m maker-space whose walls are the whole wall-material palette, one per side.
#
# Poche is not decoration. On a real drawing the hatch inside a wall IS the specification:
# it says concrete here, brickwork there, an insulated leaf in that facade, tiling round
# the wet room, and a frameless glazed screen where there is no wall at all. ArchLang
# spells that as one optional clause on `wall`:
#
#   material poche|concrete|brick|insulation|tile|none [scale <n>] [angle <deg>]
#
# All six appear below, each on the element a builder would actually detail that way, and
# each on a wall long enough to read — a hatch you cannot see is a specification you have
# not written:
#
#   concrete   300   the two long facades — the structural shell, and the thickest thing
#                    in the building because that is what carries the roof
#   brick      230   the east gable, `scale 1.5 angle 90`: one brick-and-a-bit on plan,
#                    with the courses turned to run ALONG the wall instead of across it
#   insulation 200   the west facade's insulated leaf, drawn as its own wall so that the
#                    leaf — not the assembly it belongs to — is what carries the hatch
#   tile       100   the two WC partitions, the only walls in the building that get tiled
#   none       100   the office screen: glazed, so it stops sound and not sight, and its
#                    poche is deliberately EMPTY rather than defaulted
#   (omitted)  150   the two service partitions, which take the default poche hatch
#
# THE SHEET IS WHAT MAKES THIS READABLE, and it is the second thing this file is for.
# `paper A3 landscape` + `scale 1:50` is an operative pair: every annotation becomes a
# fixed number of millimetres ON THE PAPER (3.5 mm labels, 0.5 mm lines) times the scale
# denominator, and — the point here — a 100 mm partition is drawn 2 mm wide and a 300 mm
# facade 6 mm. Halve that to 1:100 — the auto-fit's answer on a smaller sheet — and they
# become 1 mm and 3 mm: the walls still look right, and the hatch inside them is gone.
# A material sampler is a drawing that has to be issued at a scale where the poche
# survives, so the scale is written down rather than left to the auto-fit.
#
# `legend` is what makes the sampler legible from the sheet alone: one swatch per material
# actually used and one per fixture category actually placed, all derived — there is
# nothing to configure and nothing to keep in step by hand. Add a wall in a seventh
# material and the table grows a row on its own. `schedule rooms` beside it is derived the
# same way: its NO./NAME/AREA rows and its total are the rooms below, and they are the
# same numbers `arch describe --json` reports under `schedule[]`.
#
# ONE DELIBERATE WARNING, left in on purpose:
#
#   W_SCALE_OVERFLOW x1 — and it is about the MARGIN, not the page. The sheet this file
#   emits is exactly A3 landscape (420 x 297 mm, viewBox 21000 x 14850 at 1:50) and
#   nothing is clipped; what the fit rule reports is its budget, 16300 x 8300 mm of
#   building against 17675 x 7305 mm of drawing area once the margins, the dimension
#   bands, the title block and the two margin tables are taken out. It appeared in
#   v1.27.0, when `usablePlanMm` started reserving the band its own `schedule` and
#   `legend` occupy — a true positive that three releases of drawings had been quietly
#   spending.
#
#   The diagnostic's remedy is a coarser scale, and taking it would defeat the file. At
#   1:100 six wall materials no longer read side by side, and this header's own sentence
#   is that a hatch you cannot see is a specification you have not written. So the scale
#   stands and the sampler is issued with its legend tight to the margin. `arch validate`
#   is clean; `arch validate --strict` reports this one.
plan "Workshop" {
  units mm
  # 10, not 50: a 230 brick wall has a 115 half-thickness, and the WC fixtures are placed
  # `flush` against wall FACES — so the grid has to be able to express a half-thickness of
  # every wall a fixture backs onto, or the snap pushes the piece into the wall it is
  # supposed to sit against.
  grid 10
  paper A3 landscape
  scale 1:50
  north up
  dims auto all
  schedule rooms
  legend

  style wall { stroke: "#1f2a24" }
  style room { fill: "#f6f4ef" }

  # ---- shell: four walls, four specifications ------------------------------
  wall id=w_north exterior thickness 300 material concrete { (0,0) (16000,0) }
  wall id=w_south exterior thickness 300 material concrete { (0,8000) (16000,8000) }
  wall id=w_east  exterior thickness 230 material brick scale 1.5 angle 90 { (16000,0) (16000,8000) }
  wall id=w_west  exterior thickness 200 material insulation { (0,0) (0,8000) }

  # ---- the service strip ----------------------------------------------------
  # Five partitions, three specifications: a glazed screen to the office, ordinary
  # blockwork to the store, tiling to the WC.
  wall id=p_screen partition thickness 100 material none { (12000,0)    (12000,3000) }
  wall id=p_spine  partition thickness 150               { (12000,3000)  (12000,5800) }
  wall id=p_wc_w   partition thickness 100 material tile { (12000,5800) (12000,8000) }
  wall id=p_off_s  partition thickness 150               { (12000,3000)  (16000,3000) }
  wall id=p_wc_n   partition thickness 100 material tile { (12000,5800) (16000,5800) }

  # ---- rooms ----------------------------------------------------------------
  room id=r_shop   at (0,0)        size 12000x8000 label "Workshop" uses utility
  room id=r_office at (12000,0)    size 4000x3000  label "Office"   uses office
  room id=r_store  at (12000,3000) size 4000x2800  label "Store"    uses storage
  room id=r_wc     at (12000,5800) size 4000x2200  label "WC"       uses wc

  # ---- openings -------------------------------------------------------------
  # The shop door slides: a 2.4 m leaf on hinges would sweep a quarter of the aisle, and a
  # sliding leaf sweeps nothing at all, so no swing rule applies to it.
  door id=d_shop sliding on w_south at 3000 width 2400 slide left
  door id=d_ped    on w_south at 6600 width 1000 swing into r_shop
  door id=d_office on p_screen at 1500 width 900 swing into r_office
  door id=d_store  on p_spine  at 1400 width 900 swing into r_store
  door id=d_wc     on p_wc_w   at 1100 width 800 swing into r_wc

  window id=win_n1 on w_north at 2000 width 2400
  window id=win_n2 on w_north at 6000 width 2400
  window id=win_n3 on w_north at 10000 width 2400
  window id=win_w  on w_west  at 4000 width 1800
  window id=win_o  on w_east  at 1500 width 1600
  window id=win_wc on w_east  at 6900 width 600

  # ---- fit-out --------------------------------------------------------------
  # Every catalogued fixture backs onto a wall by construction: the offset runs ALONG the
  # named wall, the depth comes off its face, and the quarter-turn is derived from the
  # side `in <room>` picks. Nothing here writes a coordinate or a rotation.
  furniture id=f_sink   sink    against wall w_north offset 5000 in r_shop
  furniture id=f_bench1 counter against wall w_west  offset 2000 size 3000x700 label "Bench" in r_shop
  furniture id=f_bench2 counter against wall w_west  offset 6200 size 2500x700 label "Bench" in r_shop
  furniture id=f_bench3 counter against wall w_north offset 9500 size 3000x700 label "Bench" in r_shop
  furniture id=f_bench4 counter against wall w_south offset 9500 size 3000x700 label "Bench" in r_shop
  furniture id=f_rack1  racking at (1600,5600) size 2600x600 label "Racking"       in r_shop
  furniture id=f_table  table at (3400,3300) size 2600x1300 label "Assembly table" in r_shop
  furniture id=f_saw    table at (7600,5400) size 2000x1000 label "Bench saw"      in r_shop
  furniture id=f_desk   desk  at (13800,700) size 1600x800  label "Desk"           in r_office
  furniture id=f_ochair office_chair at (14600,1700) size 500x500 in r_office
  furniture id=f_rug    rug   at (13000,300) size 2900x2400 in r_office
  furniture id=f_racks  racking at (12300,3300) size 3400x600 label "Racking"      in r_store

  furniture id=f_wc    wc    in r_wc anchor bottom-right flush size 400x700
  furniture id=f_basin basin in r_wc anchor top-right    flush size 600x450

  title {
    project "Maker-space workshop"
    drawn_by "ArchLang"
    date "2026-08-16"
  }
}
```
