# Ground look and levels

## overview.md
```yaml
id: ground-look-and-levels
title: Ground look and levels
area: client
summary: The ground of Lor gets its look (unshaded, painted = seen; sand a step down; water and lava moving; fewer, better tiles from a fresh Claude Design brief) and its third dimension — three walkable levels joined by ramp hexagrams, same-flavour mountain stacking to a double peak, and a boulder — all inside the one grid, one mesh and one movement rule the ground-types epic built.
repo: land-of-lor (game + director + tools/world-editor + docs/design), and this workspace (epic, deliverables)
depends_on: ["ground-types"]
```

## What this is

A workshop between Kris and a laptop session on 2026-08-18, run against two north-star pictures
(a cartoony painted coastline; a blockier one with a raised grass cliff over a beach), turned
into a build. The record — twelve decisions with their why, the levels rule stated precisely,
provisional hexagrams for the engine build, the consequences said out loud — is
`land-of-lor/docs/design/2026-08-18-ground-look-and-levels-workshop.md`. **Read it first; this overview does not restate it.**

The one-paragraph version: the ground shader goes unshaded so what Claude Design paints is what
the player sees; sand sinks an eighth of a cell like water sinks a quarter; every hexagram gains
a *level* (−1/0/+1) and you cross levels only over a *ramp* hexagram whose slope is derived from
its neighbours (one edge rule, in `movement.gd` for server and client prediction, in the flow
field for creatures, and in one shared `shell/ground_rules.mjs` for the loader's validation and
the editor's loose ends); the mesh becomes a height field on the lattice it already has (cliff
faces where water banks are today, shaded by the shader as an earth wall with a lip); mountains
stack only among the same flavour, to a double peak, the Volcano crowning any range, and Earth
over Mountain is a rounded-square boulder; water and lava move in the shader behind a toggle
with a frame-time readout; and the next tile brief asks for ~30 files (a hexagram points at its
tile, several may share one, a hexagram with no meaning shows its flat base colour) from a fresh
Claude Design project (became https://claude.ai/design/p/373a1833-0687-48d5-9ee0-70784f613ff3 — Kris reuses this same session for future tile tweaks).

## Scope and non-goals

In: everything above, tests, shots, the editor's palette and loose ends for levels and ramps,
the Badlands repainted for the new mountain rules, the brief, and shipping v3 tiles when they
come back (APK bump, droplet if the director changes — it shouldn't; nothing on the wire moves).

Out: which hexagrams *finally* carry which meaning beyond the provisional four — that is task
005 with Kris, and it comes with a one-off migration of retired flavours in existing paint;
passability by nature (water creatures on water) — later, as before; any decor layer (Kris:
rocks and plants will be items and complex objects, not decoration); the tile art itself, which
Claude Design makes.

## Order

001 the look → 002 levels and ramps → 003 mountains → 004 animation → 005 meanings pass + brief
(Kris) → 006 v3 tiles in and shipped. 001–004 are engine work a session does alone and shows in
shots; 005 needs Kris; 006 waits on Claude Design.

## status.md
```yaml
updated: 2026-08-20
parked: false
tasks: {"backlog":0,"needs-input":0,"ready":1,"doing":0,"review":1,"done":5}
open_questions: []
tag: needs-review
next: 006 waits on Kris's formal sign-off (the v3 tiles are live). 007 — a flank texture for mountains — was TRIED on 2026-08-20 and rolled back: not better enough for its cost. It stays ready, with the experiment written up in its own file; the root cause is that the ground mesh carries no UVs.
```

# Task summary

Six tasks, created 2026-08-18 from the ground look & levels workshop with Kris
(`land-of-lor/docs/design/2026-08-18-ground-look-and-levels-workshop.md`); 001–004 were built
and shipped the same day by the workshop session (director at `e34e8ba` on the droplet, `/play`
at the same, APK **v15**), 005's meanings were mostly decided in the workshop and its brief is
written; 006's tiles landed 2026-08-19 and are shipped, in review.

- **001 — the look** — `done`. Unshaded ground, sand a step down, tile pointers + flat
  fallback, blob shadows.
- **002 — levels and ramps** — `done`. Three levels, derived ramps, one shared rule module,
  the height-field mesh, the editor's meanings and loose ends; 247 + 202 tests.
- **003 — mountains** — `done`. Same-flavour stacking (MAX_STACK 50), the boulder, the volcano
  crowning; the Badlands re-sculpted.
- **004 — animation** — `done`. Water/lava drift, shimmer, foam, lava glow; the perf readout;
  laptop numbers taken, the phone's is Kris's.
- **005 — meanings pass + brief** — `done`. Kris decided the meanings live; the v3 brief is
  in `docs/design/briefs/`, for a fresh Claude Design project.
- **007 — a flank texture for mountains** — `ready`, and now once-attempted. The faces of
  the new landform mountains wear their flavour's floor tile; this asks Claude Design (the
  same session as the v3 tiles) for art drawn for a vertical surface, and wires it in at no
  GPU cost. **The art came back 2026-08-20, was built end-to-end and tried on Kris's phone,
  and was rolled back** — not bad, but not better enough to earn even its small cost. Full
  experiment log at the foot of the task file. Back to the drawing board, not abandoned.
- **006 — v3 tiles in and shipped** — `review`. The 30 v3 files in the game, Kris approved the renders in-session; /play + APK v16; deliverable 15.

## Open questions

None.

## Next

Mountains became landform on 2026-08-19 — a range is one continuous massif in the floor mesh,
a blocky group one rounded cube, stacking now needs the exact same hexagram, and the mesh got
cheaper doing it (the Badlands: 64,800 -> 18,900 triangles). Shipped to /play and APK v18 as
deliverable 16, approved by Kris on the phone in-session; 007 is the follow-up it named.

The 2026-08-20 flank experiment (007) is the one thing since: built, run on the phone,
judged and rolled back the same session, with nothing pushed and the workspace plan
untouched until this note. What it found is that the ground mesh has no UVs — every ground
texture, this one included, is projected from world position, which is exact on flat ground
and on a sheer wall but not across a curved mountain face. Trying other textures under the
current projection is the cheap next move; giving the massif real UVs is the thorough one.

001–005: Signed off by Kris 2026-08-18. 006 built and shipped 2026-08-19 — the v3 tiles from Claude Design session https://claude.ai/design/p/373a1833-0687-48d5-9ee0-70784f613ff3 (Kris keeps that session for future tile tweaks). In review; sign-off closes the epic.

## tasks/ (7)

### ground-look-and-levels/001 — The look — unshaded ground, sink depths, tile pointers with a flat fallback
```yaml
id: ground-look-and-levels/001
title: The look — unshaded ground, sink depths, tile pointers with a flat fallback
epic: ground-look-and-levels
state: done
priority: 1
blocked_by: []
estimate: S
created: 2026-08-18
updated: 2026-08-18
claimed_by: null
claimed_at: null
delivers: []
review_artifact: land-of-lor/docs/handoff/ground-look-and-levels.md
```

## What to do

Decisions 1, 2 and 10 of the workshop note (`land-of-lor/docs/design/2026-08-18-ground-look-and-levels-workshop.md`).

- `game/world/client/ground.gdshader`: `render_mode unshaded`; the shading the ground needs is
  drawn here — darken by slope (the normal is available), a darker rim where a surface steps
  down (bank of water/lava/sand; later the cliff faces of 002), so a step reads as a drawn edge.
- `game/shell/elements.js` (`sync-elements.sh` after): the ground *type* carries a `sink`
  (cells: water and lava 0.25, sand 0.125, the rest 0) in place of `kind: sunken`; keep
  `kind` for `mountain` only if something still needs it — prefer one field with one meaning.
  Each hexagram's `groundVariant` gains an optional `tile` (a `<lower>/<upper>` file id;
  several hexagrams may point at one file); no `tile` → the flat `colour`. Delete the
  `pattern` fields and the procedural pattern painter in `ground_atlas.gd` (`draw_tile`) — dead
  the moment tiles are pointers; the atlas is: PNG if pointed at and present, else a flat fill.
- `ground_mesh.gd` / `world_units.gd`: `SUNKEN_DEPTH` becomes the per-type sink; the standing
  height on sand is the sand's own surface (no submerging — `ENTITY_SINK` is for water/lava).
- Shots: `_shots.gd` gains `LOR_SHOTS_ONLY=ground` (only the ground shots) — the lab in the
  workshop session needed it; note in `game/README.md` that Godot re-imports changed PNGs only
  from the editor or `godot --headless --path world --import`.

## Definition of done

`test_runner.gd` green (mesh rules updated for sink), the ground shots on the Badlands and the
Village show sand a step down with a drawn rim and no lighting lift, `sync-elements.sh` run,
director tests green (the TS table reader follows the field change). Committed and pushed.

## Where the work lands

`land-of-lor/game/world/client/`, `game/shell/elements.js`, `game/director/src/groundTypes.ts`.

## What was done (2026-08-18, laptop workshop session)

Built and shipped the same day (APK v15, `/play` at `e34e8ba`). The ground shader is
`unshaded` — painted = seen — and shades banks, ramps and cliff faces itself; sand sinks an
eighth (`sink` per type in `elements.js`, `kind: flat|liquid|mountain`, `plain` per type); tiles
are `tile` pointers (28 named; no tile = flat colour, deliberately) and the pattern painter is
gone; bodies carry a blob shadow and the sun's shadow pass is off (an unshaded ground can't
receive shadow maps — the build's call, in the handoff); `LOR_SHOTS_ONLY=ground`, and the
re-import gotcha is in `game/README.md`. Handoff: `land-of-lor/docs/handoff/ground-look-and-levels.md`.

### ground-look-and-levels/002 — Three levels and ramp hexagrams — the edge rule, the height-field mesh, the mirrors and the editor
```yaml
id: ground-look-and-levels/002
title: Three levels and ramp hexagrams — the edge rule, the height-field mesh, the mirrors and the editor
epic: ground-look-and-levels
state: done
priority: 1
blocked_by: []
estimate: M
created: 2026-08-18
updated: 2026-08-18
claimed_by: null
claimed_at: null
delivers: []
review_artifact: land-of-lor/docs/handoff/ground-look-and-levels.md
```

## What to do

Decisions 3–6 of the workshop note (`land-of-lor/docs/design/2026-08-18-ground-look-and-levels-workshop.md`), with its provisional hexagrams (kw 27 high, kw 41
low, kw 42 and 61 ramps) until the meanings pass (005) replaces them.

- Data: `level` (−1/0/+1, default 0) and `ramp: true` on hexagrams in `elements.js`;
  `ground_types.gd` / `groundTypes.ts` read them.
- `grid.gd`: at `apply_ground`/`set_ground` precompute per cell its level and, for ramps, the
  derived axis and direction (recomputed for the neighbours of a changed cell too); expose
  `edge_level(x, y, dir)` and `is_ramp(x, y)`. Hot loops stay one array read.
- `movement.gd`: the edge rule — the step A→B needs B walkable and the two surfaces meeting at
  the shared edge; diagonals need all four cells at one level and none a ramp. Client
  prediction gets it for free (it calls the same function). `flow_field.gd`: the same rule in
  its neighbour loop. New `Result.LEVEL_BLOCKED` with its reason string.
- `ground_mesh.gd`: the height field — `level × BLOCK − sink`, ramps a plane between their two
  end levels — under the "highest touching cell" vertex rule; `standing_height` = the surface at
  the cell centre (a ramp's centre is halfway); mountains and portals sit on their cell's
  surface. `world_units.gd`: `LEVEL_HEIGHT` (a full cell to start; Kris tunes it over time).
- `ground.gdshader`: cliff faces (steep, tall steps) shaded as an earth wall with a lip line
  along the top edge; a bank (a sink) keeps its lighter rim.
- One shared `game/shell/ground_rules.mjs` (ES module, browser and node): `canStep`,
  `reachableFrom` with the corner rule *and* the level rule; `director/src/groundContent.ts`
  and `tools/world-editor/area-editor.js` use it (the editor is already served from `shell/`
  by `server.mjs`). The editor: palette shows level/ramp on a hexagram's chip text, Random mix
  and the director's sprinkle presets never pick a levelled or ramp hexagram, loose ends flag a
  ramp with no level change beside it and a ramp with two candidate axes; the loader refuses
  the same things it always did, now with the level rule in its reachability.
- `_shots.gd`'s built-in showcase gains a plateau with a ramp and a pit with a ramp; take the
  chase-camera shot with a body at the foot of a cliff on the camera side — the occlusion
  check that decides whether `LEVEL_HEIGHT` stays a full cell.
- Tests: `test_runner.gd` (rule cases: same level, ramp ends, ramp flanks, diagonals, dead
  ramp, derived axis; mesh heights on a plateau and a ramp) and `director` tests
  (`groundContent` reachability with a cliff and a ramp).

## Definition of done

All of the above green; the showcase shots in `docs/handoff/ground-look-and-levels/`; a
`docs/handoff/ground-look-and-levels.md` note with what was decided in the build (block height
after the occlusion check, ramp edge cases). No protocol or schema change. Committed and pushed.

## Where the work lands

`land-of-lor/game/world/`, `game/shell/`, `game/director/src/`, `tools/world-editor/`, `docs/handoff/`.

## What was done (2026-08-18, laptop workshop session)

Built and shipped the same day. Kris's assignments (not the provisional four): the Deep Water
family is the deep level (−1, wadeable), Wind over Lake the ramp down; Mountain over Stone the
high level, Wind over Stone the ramp up; the Grass family reserved for variety. One shared
`game/shell/ground_rules.mjs` (levels, derived ramps, the whole step rule) for the director's
loader validation and the area editor; `grid.gd` precomputes levels/ramp directions;
`movement.gd` (`LEVEL_BLOCKED`) and `flow_field.gd` ask it; the mesh is a height field on the
same lattice; the shader keeps a plateau's blend to its level and paints faces; the editor says
every chip's meaning, mixes only variety, flags a level change with no ramp and odd ramps; the
showcase has a plateau + ramp, a pit + ramp, and the occlusion shot (`LEVEL_HEIGHT` stays a
full cell: legs hidden, torso and head visible). test_runner 247/247, director 202/202.
Handoff: `land-of-lor/docs/handoff/ground-look-and-levels.md`.

### ground-look-and-levels/003 — Mountains — same-flavour stacking to a double peak, the Volcano crowning, and the kw 15 boulder
```yaml
id: ground-look-and-levels/003
title: Mountains — same-flavour stacking to a double peak, the Volcano crowning, and the kw 15 boulder
epic: ground-look-and-levels
state: done
priority: 2
blocked_by: []
estimate: S
created: 2026-08-18
updated: 2026-08-18
claimed_by: null
claimed_at: null
delivers: []
review_artifact: land-of-lor/docs/handoff/ground-look-and-levels.md
```

## What to do

Decisions 7 and 8 of the workshop note (`land-of-lor/docs/design/2026-08-18-ground-look-and-levels-workshop.md`).

- `ground_mesh.gd`: `is_peak` becomes `stack_level(x, y)` — the Chebyshev distance to the
  nearest cell that is not the same hexagram (or out of bounds), capped at `MAX_STACK = 2`;
  the Volcano (Fire over Mountain) matches any Mountain flavour. `mountain_instances()` returns
  `stack` (0..2) and `shape` (`cone` | `boulder`); Earth over Mountain (kw 15) is the boulder.
- `area_view.gd`: two MultiMeshes — cones at three heights (`MOUNTAIN_HEIGHT`, peak, double
  peak in `world_units.gd`), boulders a low-poly rounded square block generated in code (a
  superellipsoid, one function) at three sizes; the volcano cap on top of whatever height its
  cone reached. Both lit, both standing on the cell's surface (002's levels).
- Content: repaint the Badlands range in `game/content/areas/the-badlands.ground.json` — a
  one-flavour cone range with the volcano at its centre, and a separate boulder field of kw 15
  — with the area editor or `paint-ground`, then `load-world` on the next deploy.
- Tests in `test_runner.gd` for `stack_level` (edge, ring, double ring, mixed flavours, the
  volcano exception, the cap).

## Definition of done

Shots of the repainted Badlands range and a boulder field in `docs/handoff/ground-look-and-levels/`;
tests green; committed and pushed.

## Where the work lands

`land-of-lor/game/world/client/`, `game/content/areas/`, `docs/handoff/`.

## What was done (2026-08-18, laptop workshop session)

Built and shipped the same day, folded into the levels work. Same-flavour stacking as one
multi-source flood, `MAX_STACK = 50` (Kris: "why the hell not"), the Volcano matching any
Mountain flavour so it crowns a range; what stands on a cell is data (`shape`: cone / volcano /
boulder — a code-built rounded block — or nothing for the five undefined flavours, still
impassable); one MultiMesh per shape on the cell's surface. The Badlands re-sculpted as a
deterministic preset (Kris: "nothing there is sacred"): two stone plateaus with three ramps up,
the lake as the deep level with two ramps down in notches, a 5×5 boulder square mountain, a
one-flavour cone range crowned by its volcano — validated, every ramp rising, every area
reachable; `load-world` run on the droplet. Handoff: `land-of-lor/docs/handoff/ground-look-and-levels.md`.

### ground-look-and-levels/004 — Water and lava move — shader drift, shimmer, glow, shore foam, behind a toggle with a frame-time readout
```yaml
id: ground-look-and-levels/004
title: Water and lava move — shader drift, shimmer, glow, shore foam, behind a toggle with a frame-time readout
epic: ground-look-and-levels
state: done
priority: 2
blocked_by: []
estimate: S
created: 2026-08-18
updated: 2026-08-18
claimed_by: null
claimed_at: null
delivers: []
review_artifact: land-of-lor/docs/handoff/ground-look-and-levels.md
```

## What to do

Decision 9 of the workshop note (`land-of-lor/docs/design/2026-08-18-ground-look-and-levels-workshop.md`).

- `ground.gdshader`: for cells flagged as sinking liquids (water, lava — the flag byte in the
  data texture already says sunken; sand is not a liquid) drift the tile sample position with
  `TIME`; a second, slower offset layer blended in for shimmer; lava a slow brightness pulse;
  animated foam (value noise, white) on the bank band at the waterline, read off the height. A
  `ground_animation` uniform (0/1) turns all of it off.
- A frame-time readout in the HUD's dev/debug corner (or the existing dev overlay), so the
  number is readable on a phone; `game/README.md` says how to read it. Take the laptop and web
  numbers with the animation on and off on the 60×60 Badlands at maximum zoom-out; the phone
  number is Kris's — say so in the handoff.
- A short GIF (a few frames from the shot harness) in `docs/handoff/ground-look-and-levels/`.

## Definition of done

Animation visible in the GIF, toggle works, readout works, numbers in the handoff note.
Committed and pushed.

## Where the work lands

`land-of-lor/game/world/client/`, `docs/handoff/`.

## What was done (2026-08-18, laptop workshop session)

Built and shipped the same day. Drift + shimmer for liquid cells (two reads instead of one, a
coherent per-cell branch), a slow glow pulse on lava, foam along the waterline (water only,
never lava or a cliff face, sparse moving crests), all from `TIME`; behind the `animate`
uniform, remembered across Area rebuilds; the HUD's bottom-left perf readout (frame ms · fps ·
water moving/still) flips it on a tap. `LOR_SHOTS_ANIM=N` for the GIF
(`docs/handoff/ground-look-and-levels/water-lava-foam.gif`), `LOR_SHOTS_PERF=1` for the number:
laptop iGPU, whole 60×60 Badlands at 844×390, viewport GPU time 0.96 ms still, ~1.2 ms moving.
**The phone number is Kris's** — read the readout, tap, read again. Handoff: `land-of-lor/docs/handoff/ground-look-and-levels.md`.

### ground-look-and-levels/007 — A flank texture for mountains — the faces are wearing floor tiles
```yaml
id: ground-look-and-levels/007
title: A flank texture for mountains — the faces are wearing floor tiles
epic: ground-look-and-levels
state: ready
priority: 2
blocked_by: []
estimate: M
created: 2026-08-19
updated: 2026-08-20
claimed_by: null
claimed_at: null
delivers: []
review_artifact: null
```

## Why

Mountains became landform on 2026-08-19 (deliverable 16): a contiguous group of one flavour is
now one continuous massif in the floor mesh, with steep faces and a sheer foot. Those faces
currently wear **the flavour's own ground tile** — the same texture the flat floor of that cell
uses. It is honest and it reads as stone, but it is a top-down floor texture standing upright,
and on the steeper part of a face it smears.

The one texture in the game drawn *for* a vertical surface is `client/ground/special/cliff-face.png`,
and it is deliberately not used here: it is authored as exactly one level tall and seamless
left-to-right, so on a mountain flank three or four cells high it bands horizontally. The
shader suppresses it on mountain cells for that reason (`ground.gdshader`, the `mountain_w`
term).

## What to do

- **The brief is written** — `land-of-lor/docs/design/briefs/MOUNTAIN_FLANK_BRIEF.md` (2026-08-19),
  ready for Kris to paste into the session below. What follows is what it asks for, and stands
  as the spec if it needs rewriting: a **mountain flank** texture (or a small set — one per
  mountain flavour that has a shape today: the plain cone, the volcano, the blocky kind).
  **Kris reuses one Claude Design session for tile work** — https://claude.ai/design/p/373a1833-0687-48d5-9ee0-70784f613ff3
  — so this goes there rather than into a fresh project (`docs/design/README.md`).
  The brief goes in `land-of-lor/docs/design/briefs/`, and reads the v3 tile brief first so the
  palette and the "painted = seen, no motifs, one colour per family" rules carry over.
- The brief must state what the shader can actually use: the face is sampled by world position,
  the art must tile seamlessly in both directions (unlike the cliff face, which is one level
  tall by construction), and it must read at a phone's scale — a flank is only ~150 px on
  screen.
- Render candidates with the **area preview kit** (`docs/design/tiles/preview/`) before judging
  them: a single tile tells you nothing about how a whole massif looks. Add a mountain view to
  the kit if the existing two grids don't show one.
- Then wire it: a `flank_tex` uniform beside `face_tex` in `ground.gdshader`, chosen per cell
  by flavour, blended in by the existing slope term rather than a new branch — the mountain
  weight (`mountain_w`) and `cliff` factor are already computed. **No new texture reads on
  fragments that are not mountain**, and no measurable cost on the ground's GPU time (the
  perf readout and `LOR_SHOTS_PERF=1` are the check; the ground is currently 0.69–0.89 ms
  animated on the 60x60 Badlands).
- Re-take the Badlands shots and note the before/after in the handoff.

## Definition of done

The flank art is in the repo, the shader uses it on mountain faces only, `LOR_SHOTS_PERF=1`
shows no regression against the numbers above, the Godot suite is green, and the brief has
moved to `docs/design/historic/` per `docs/design/README.md`.

## Where the work lands

`land-of-lor/docs/design/briefs/`, `land-of-lor/game/world/client/ground.gdshader`,
`client/ground/`, `docs/handoff/ground-look-and-levels.md`.

---

## Experiment log — 2026-08-20 (tried on the phone, then rolled back)

The art came back from Claude Design and was wired in end-to-end as a **local experiment**,
run on Kris's phone, and then **rolled back**. Nothing shipped: no push, no APK upload, no
`/play` change. The three PNGs are still only in the handback package
(`~/Downloads/mountain-back-from-cd/ground/flank/`), not in the repo.

**Verdict (Kris, on the phone): not bad, but not better *enough*, and it came with a
performance cost.** Back to the drawing board.

### What was built

- `flank/{mountain,earth,fire}.png` into `client/ground/`, built by `ground_atlas.gd` into
  one 8x1 strip indexed by flavour (a flavour with no flank art fell back to its own
  mountain floor tile, so it was additive).
- One extra texture read in `ground.gdshader`, inside a branch only mountain fragments
  enter — flat ground, banks, ramps and cliff faces were untouched. The bank's 22%
  darkening stepped aside where the flank took over, so a face was cut once (~25%), not
  twice (~41%).
- Two settings of the slope gate were tried and judged against renders and on the phone.

### What was learned

**The slope gate has no good setting, and that is the finding.** A landform massif is a
shallow dome about a cell tall, so almost none of its surface is truly sheer:

- **Steep gate** (on past ~64 deg): the flank hid in a sliver at the foot. Kris could not
  see that the mountains had changed at all.
- **Gentle gate** (on past ~26 deg): the flank clad the whole cone and the mountains
  clearly changed — but a flat texture projected across a curved surface produced visible
  wavy undulations, and the bedding closed into contour rings over the crown.

Cost was small but real: 0.53-0.62 ms on the 60x60 Badlands (laptop iGPU) against a
0.69-0.89 ms baseline, i.e. inside the noise, plus one texture read per mountain fragment.
Small is not free, and the rule Kris applied is the right one: a cost has to buy a
visible win.

### Root cause, and the recommendation

**The mesh carries no UVs.** `ground_mesh.gd` emits position only; every texture in the
ground — floor tiles, the cliff face, and this flank — is projected from *world position*
in the shader. On flat ground and on a sheer wall that projection is exact. On a curved
mountain face it is not: the texture is projected through the curve rather than laid along
it, which is exactly what the undulations are.

So the fix is **in the mesh, not in the landform's shape**. Kris's instinct here was right
and the first analysis written in-session (that the mountain needed real flat faces to clad)
was the weaker answer — worth noting because it would have sent the next session at the
wrong problem. Unwrapping the massif's surface and giving those vertices real UVs lets a
single texture be laid along the surface, stretching with it, no projection error and no
gate to tune: the whole flank/floor question stops being a slope threshold and becomes
"where does the UV shell start". That is a bigger change than this task assumed, and it
touches the mesh builder that `002` made pure and tested.

**Still worth exploring, and NOT closed off by the above:** different textures for
different results. Only these three were tried, all in the same painted-strata family. A
flank with less directional bedding, or one authored to tolerate curvature, may well read
fine even under the current world-position projection — that is a cheap thing to test and
should be tried before committing to UV work.

### Where to pick this up

Nothing to undo — the repo is at `9f67b74` and the phone was rebuilt back to clean v18.
The brief stays in `land-of-lor/docs/design/briefs/` (it has NOT moved to `historic/`,
because it has not been satisfied). The Claude Design session is unchanged and still holds
the flank work if the art is wanted again.

### ground-look-and-levels/005 — The meanings pass with Kris, tile pointers, and the new brief for a fresh Claude Design project
```yaml
id: ground-look-and-levels/005
title: The meanings pass with Kris, tile pointers, and the new brief for a fresh Claude Design project
epic: ground-look-and-levels
state: done
priority: 3
blocked_by: []
estimate: M
created: 2026-08-18
updated: 2026-08-18
claimed_by: null
claimed_at: null
delivers: []
review_artifact: land-of-lor/docs/handoff/ground-look-and-levels.md
```

## What to do

Decisions 10 and 12 of the workshop note (`land-of-lor/docs/design/2026-08-18-ground-look-and-levels-workshop.md`), and the note's "provisional assignments"
section — this task replaces the provisional four with Kris's real choices.

- With Kris: which hexagrams carry a meaning (high, low, ramp up/down, boulder, dirt, ice,
  volcano rock, cliff face …), which one or two flavours per type are the *variety* the
  sprinkle presets and Random mix may use, and which share a tile. Write the outcome into
  `elements.js` (`level`, `ramp`, `tile`) and `sync-elements.sh`.
- The migration: existing paint (every home, the Village, the Badlands) contains flavours that
  now carry a meaning; a one-off, idempotent director migration replaces retired flavours with
  their type's plain hexagram (`[migrate] repainted N cells of retired flavours`), rehearsed on
  a backup like the Area rename was (`Dev/CLAUDE.md`, 2026-08-16). Deployment record in
  `Dev/CLAUDE.md`.
- The brief: `docs/design/briefs/GROUND_TILES_BRIEF_v3.md` (the 64-tile brief moves to
  `historic/`), self-contained, for a **fresh** Claude Design project: the ~30 files with their
  meaning, the palette from the north-star pictures (attached), unshaded — painted = seen, no
  lighting lift, snow not white; texture density like v1, palette like v2, no single motifs;
  flavours as one field with subtle accents; the preview kit attached with its prompt; the
  contact sheet in the atlas layout. Kris takes it to Claude Design.

## Definition of done

`elements.js` says the meanings; migration written, tested against a copy, deployed with the
record; brief committed and handed to Kris. Committed and pushed.

## Where the work lands

`land-of-lor/game/shell/elements.js`, `game/director/src/`, `docs/design/briefs/`, `Dev/CLAUDE.md`.

## What was done (2026-08-18, laptop workshop session)

Kris made most of the meanings pass in the workshop itself (recorded in
`land-of-lor/docs/design/2026-08-18-ground-look-and-levels-workshop.md`): grass = variety only,
so **no migration is needed** (homes sprinkle grass alone); Lake = the deep level with Wind over
Lake down; Mountain over Stone high with Wind over Stone up; cone / volcano / boulder and five
undefined mountain flavours; undefined = flat colour. `elements.js` says it all. The brief is
written: `land-of-lor/docs/design/briefs/GROUND_TILES_BRIEF_v3.md` — 27 files for a **fresh**
Claude Design project, with the two north-star pictures beside it and the preview kit's prompt
updated. **Kris reviews the brief and takes it to Claude Design.** Still his to name: whether a
raised sand/dirt flavour is wanted too ("high (sand) ground"), the five undefined mountain
flavours, and which flavours share tiles when v3 lands.

### ground-look-and-levels/006 — v3 tiles in — kit renders, in-game shots, judged, shipped
```yaml
id: ground-look-and-levels/006
title: v3 tiles in — kit renders, in-game shots, judged, shipped
epic: ground-look-and-levels
state: review
priority: 3
blocked_by: ["ground-look-and-levels/005"]
estimate: M
created: 2026-08-18
updated: 2026-08-19
claimed_by: null
claimed_at: null
delivers: [15]
review_artifact: deliverables/015-the-v3-tiles-are-in.md
```

## What to do

When Claude Design's v3 files come back: drop them into `game/world/client/ground/`, render
them with the preview kit (`docs/design/tiles/preview/`, whole areas + game-scale crops → 
`docs/design/tiles/v3-*`), take the ground shots (Badlands, Village), and put both in front of
Kris before anything ships. Swatches into `elements.js` (`groundVariant.colour` from the art),
`sync-elements.sh`, APK bump, web build, deploy record — the same shipping steps as
`ground-types/008` minus the director (nothing on the wire changed in this epic).

## Definition of done

Kris says yes to the renders and shots; live on `/play` and as an APK; deliverable written.
Committed and pushed.

## Where the work lands

`land-of-lor/game/`, `docs/design/tiles/`, this workspace (`deliverables/`).

## Built 2026-08-19 (laptop session, Kris present)

Claude Design's 30 files (29 tiles + the cliff face) landed from
https://claude.ai/design/p/373a1833-0687-48d5-9ee0-70784f613ff3 — **Kris keeps this session
for future tile tweaks; follow-up tile work goes back to it, not to a fresh project.** Kris
looked at the in-game renders and said yes before anything was committed. Shipped: tiles +
swatches + the Ramp down's first tile (61 → lake/wind) in land-of-lor `6e1fdce`, design
record (`tiles/v3-*`, kit maps regenerated, brief → historic) in `b6a31b1`; web build on
`/play`, APK v16 at `/download/land-of-lor-v16.apk`. Deliverable 15 has the shots. No
director deploy — nothing on the wire changed, as this task's brief said.

## questions/ (0)

## deliverables (3)

- #16 — Mountains are landform — a range is one mountain now, not a field of cones (2026-08-19) — /deliverables/16
- #15 — The v3 tiles are in — Claude Design's set on the ground of Lor (2026-08-19) — /deliverables/15
- #14 — Levels, ramps and mountains — the ground of Lor before the v3 tiles (2026-08-19) — /deliverables/14