diff --git a/flow-state/EPIC-4.md b/flow-state/EPIC-4.md
index 5c9481f..a2e2d82 100644
--- a/flow-state/EPIC-4.md
+++ b/flow-state/EPIC-4.md
@@ -394,3 +394,191 @@ do it once the numbers were understood, and they now are.
written — direction clears the arcless reference by 0.26, which is hard to explain without
a story — but it remains the only thing that would attribute the floor rise (0.1697 →
0.2130) to this work rather than to everything else that changed alongside it.
+
+---
+
+## 14. Composition — the frame has a floor and a ceiling
+
+Not part of the original plan. It comes out of the same place §12 did: the layer stack was
+being asked to carry the video, and half of what it was stacking was nothing.
+
+**The floor.** Two thirds of the library is `composable` — sparse by design, elements ON
+something. `canBackground` let almost all of it anchor a section anyway, so a section's
+bottom layer was regularly a scene painting 2% of the frame with black behind it. Measured
+across six videos, sampling the middle of every section: **9 of 40 frames were under 20%
+painted, the darkest at 0.3%**. That is a minute and a half of a few bright things on black,
+and no gate could see it, because every gate on the stack was a limit rather than a floor.
+
+Every section now stands on a GROUND: a canvas painting at least half the frame, cast per
+section kind so a section's shot cuts change the shot and not the world. When the shot is
+itself a full canvas it *is* the ground — two canvases stacked is two pictures fighting. The
+same measurement now reads **mean 92% painted, darkest 63%, nothing under 30%**, and phase 12
+gates it in the image, not in the spec.
+
+**The ceiling.** Layering had a rate but no budget, so how full a frame got was a die roll
+that knew nothing about the song. A stack now has a coverage budget — director appetite
+(`crowd`, 1.05 brutalist to 1.7 corrupt), times the section's energy and density, times where
+the story is — capped at **200%**, two frames' worth of material. The ground and the shot are
+paid for first; the budget governs what may be stacked on top, and the odds of an overlay
+fall off as the headroom does rather than only at the wall.
+
+**What it cost, and what it bought.** Same instrument, same six seeds:
+
+```
+ baseline with grounds
+separation 0.10 0.44
+direction 0.06 0.21 (arcless reference 0.02 → 0.08)
+library 80% 97% of what the library can express
+scale 42% 56%
+motion 68% 96%
+texture 74% 90%
+```
+
+The ceiling *fell* (0.2334 → 0.2031) while observed rose, so part of that separation gain is
+the reference arm coming down: videos that share a ground vocabulary are less unalike even
+when they share no shots. The honest reading is that the floor rose 0.009, direction tripled,
+and the ceiling moved toward the floor — the numbers are up, and not all of the rise is
+signal.
+
+**A scene that reads the previous frame cannot be a ground.** `prev()` returns the whole
+composited frame, *including whatever is layered on top of this scene*, so a datamosh under a
+shot is not grounding it, it is eating it. Measured the moment one became a bed: the render
+stopped reproducing from a seek, and two WebGL contexts diverged by 91/255 against a ceiling
+of 4 — phase 4 caught both. `readsHistory` is derived from the shader source rather than
+declared, and costs the ground pool six of twenty scenes, five of them glitch.
+
+`scenes/coverage.js` is the measured coverage of every scene, pasted back out of the phase 12
+gate, which re-measures and fails on drift over a tenth of a frame. A new scene has no entry
+and therefore cannot ground anything until someone renders it.
+
+### 14.1 The measurements moved into the repo
+
+Everything in §14 depends on a number — how much of the frame a scene paints — and the
+first version of it was a table pasted into a source file by hand. That is the same mistake
+`surface` already was: a measured fact written down by a person, correct on the day and
+wrong after the next shader edit.
+
+**`src/scenes/metadata.json`** is now generated, tracked in git, and holds every measured
+fact about every visualizer: coverage as a shot, coverage as a bed, the variety score, the
+per-block scores, and the mean structural profile. Nothing in it is typed by a human.
+`surface` is derived from it (canvas at 50%, composable below) and declaring `surface:` in a
+scene file is now a lint error.
+
+It is refreshed from **gallery.html → refresh metadata**, which re-renders the library and
+writes the file through a dev-only endpoint in `vite.config.js`. The file carries a
+fingerprint of everything that can move a number in it — the scenes, the shader contract,
+the identities and palettes they are handed, the descriptor definitions — and phase 12 fails
+when that stops matching. A stale metadata file does not produce a stale report, it produces
+wrong videos, so it has to be detectable.
+
+**Measuring at the wrong bias measures nothing.** Coverage is mostly a function of a scene's
+parameters, and the first measurement took one number per scene at the busiest section of
+six songs. Metaballs measures 69% there and painted **6%** as an intro's ground. Three
+things came out of chasing that, in order of how much each was worth:
+
+* A bed is drawn with a **moderate hand**. `extremity` pushes parameters toward the ends of
+ their ranges, and half of what decides coverage is a parameter with no `bias` key at all —
+ Metaballs' `threshold` — so extremity is the only thing moving it and one end is an empty
+ frame. Grounds sample at `extremity * 0.2`. The extremes belong to the shot.
+* Energy and density are **floors** for a ground, not reductions. Calming the bed by
+ lowering energy is how to empty it: scenes bias their fill against `energy` as often as
+ against `density`.
+* Coverage is measured **twice**, once as a shot and once as a bed, at the same bias and
+ temperament the generator will use. `GROUND_BIAS` and `groundTemperamentFrom` live in
+ `scenes/surface.js` and are imported by both the generator and the measuring pass, so the
+ two cannot drift apart.
+
+Rendered result across six videos, sampling the middle of every section: **mean 87% painted,
+darkest 30%**, against 51%/0.3% before any of this.
+
+**Eleven scenes can ground a section** — five organic, five geometric, one structural. There
+is no `minimal`, `flow` or `glitch` canvas in the library that paints half the frame without
+reading `prev()`, so every quiet section in every video stands on one of five organic beds.
+That is the largest remaining hole and it is library work, not generator work: the fix is
+writing dense, self-contained canvases in the three thin families.
+
+### 14.2 The red line is gone
+
+`MIN_VARIETY` — 0.1, drawn across the gallery, "below this a scene is the same picture
+wherever it appears" — was right when a section was one scene and is wrong now. A section is
+a ground, a shot on it and sometimes a pass over that, so what a viewer sees is a
+combination, and a scene that is reliably itself is a good ingredient in one. Nineteen
+scenes were failing a bar for being consistent.
+
+What replaced it is a level up, and it is the reason the profiles are in the metadata: the
+generator weights every layering choice by **structural distance** — how unalike two scenes
+measured, on the same descriptor the variety harness compares videos with. Family labels and
+the render disagree often enough to matter; two `geometric` scenes can be 0.31 apart and a
+`flow` and an `organic` scene 0.04, and stacking the second pair is one picture at double
+density. Measured over 436 stacked pairs: **mean distance 0.268, zero near-twins**.
+
+### 14.3 The other end of the frame
+
+The floor had a ceiling missing. Measured across twelve videos, sampling the middle of every
+section: **a median 24% of every frame was clipped to pure white, and whole sections
+rendered at 100%.** The videos were washed out, and no gate could see it because every guard
+on the frame asked whether there was enough in it.
+
+Three causes, all introduced or exposed by §14, in the order they were found:
+
+**The shot was screened over its ground.** Screen is a lightening operator — correct for a
+few bright elements over a bed, and over a filled canvas it drives everything toward white.
+Replaced with a **lumakey** blend (`passes.js`): the shot's own brightness is its alpha, so
+it replaces the ground where it paints and leaves it where it does not, keeping its own
+colour instead of adding it to the bed's. Median clipping 24% → 11%.
+
+**The feedback loop was an accumulator.** `cur + hist * decay * amount` settles a still
+image at `1/(1 - decay*amount)` times its drawn brightness — **2.3x** at the settings the
+generator hands out. Survivable when a frame was a few bright things on black; fatal the
+moment every section stood on a filled ground. Turning feedback off took a blown frame from
+100% to 34% mean luminance, which is the whole diagnosis in one number. It now divides by
+the same gain, at 0.6 rather than 1.0: full normalisation took the lift out along with the
+blowout — the median frame fell from 95% painted to 71% and fourteen sections dropped
+through the black-frame floor.
+
+**Nothing rolled off.** The grade clipped. A **highlight shoulder** now compresses everything
+above 0.75 toward but never to 1. Clipped white is not brightness, it is missing information:
+every difference inside it has been deleted.
+
+Rendered, after all three — 74 sections, twelve videos:
+
+```
+ before after
+painted 51% mean 88% mean, darkest 43% (was darkest 0.3%)
+clipped white 24% median 0% median, worst 21% (was worst 100%)
+luminance 77% median 42% median, worst 80%
+```
+
+The gate is now two-ended: `composition · a rendered section is neither black nor blown out`.
+
+**A ground has to survive the song's identity.** Chasing the dark end turned up the same
+class of error as §14.1: a ground measured at 61% painted **2%** in one particular video,
+because that song's ink treatment is `hollow` — outlines, no fill. Two changes came out of
+it. The world is now drawn solid and the song's hand is kept for the subject
+(`groundPersonalityFrom`), and eligibility takes the **worst** identity into account as well
+as the mean: a ground fills the frame on average and never vanishes. Holding the worst case
+to the full 50% would leave *four* castable beds in the library, which is a worse video than
+an occasionally dim intro.
+
+**Seven scenes can ground a section** — five geometric, two organic. Every quiet section of
+every video stands on one of two beds. Restated from §14.1 because it got worse, not better:
+this is the library's largest hole.
+
+### 14.4 Blazing on purpose
+
+Screening the shot over its ground and letting the two brightnesses sum is a real effect — a
+drop that goes to paper for eight bars reads as the song peaking. The mistake in §14.3 was
+not the effect, it was that the effect was the **default**: every section did it, so a median
+quarter of every frame in every video was clipped, and nothing about that said "peak".
+
+It is a decision now. Each director declares a `blaze` appetite — brutalist 0.05 (mass does
+not glow), corrupt 0.5 (overload is the subject) — and a section still has to earn one: loud,
+and late in the story. Quiet kinds never blaze, because a breakdown that goes white is not a
+choice, it is a bug with a rationale. The default composite is the lumakey, which keeps the
+shot's own colour.
+
+The ceiling gate matches the intent rather than banning brightness. Per section, only a hard
+cap — past about half the frame at full white there is no picture left to read. Across the
+population, **no more than a fifth of sections may be hot at all**, which is the number that
+actually distinguishes a director choosing to peak from a pipeline with no headroom.
+Measured now: **1 of 74 sections blazing**, worst 30% clipped.
diff --git a/flow-state/HOWTO-variety.md b/flow-state/HOWTO-variety.md
index 26605e8..f12586b 100644
--- a/flow-state/HOWTO-variety.md
+++ b/flow-state/HOWTO-variety.md
@@ -28,16 +28,23 @@ checks.html?scene=X the per-scene gate, including `consumes:` lines
```
The gallery score is the mean structural distance between a scene's own six
-frames, on the same descriptor the variety harness uses. **Minimum bar: 0.1**
-(`MIN_VARIETY` in `checks/gallery.js`), drawn as a red line. Below it a scene is
-the same picture wherever it appears, and because it is cast into many songs
-that sameness leaks between them — the viewer recognises the shot rather than
-the song.
+frames, on the same descriptor the variety harness uses. High means the song
+changes this scene a lot; low means it looks like itself wherever it is cast.
-Most of the library does not clear it. That is deliberate: a bar set where the
-work already is measures nothing.
+**There is no bar.** There used to be one — 0.1, drawn across the gallery as a
+red line — and it was removed when composition arrived. A section is no longer
+one scene: it is a ground, a shot standing on it, and sometimes a pass over
+that, so what a viewer sees is a COMBINATION. A scene that is reliably itself is
+a perfectly good ingredient in one, and the old line failed it for being
+consistent.
-Some reference points, measured:
+What replaced it is a level up. The interesting quantity is how unalike the
+scenes in one stack are, and the generator now weights its choices by exactly
+that — see `structuralDistance` in `scenes/surface.js`. Your scene does not have
+to be varied on its own. It has to be unlike the things it will be stacked with,
+and that is measured for you.
+
+Some reference points, measured across six songs:
```
0.324 Droste Feedback varies a lot
@@ -47,6 +54,29 @@ Some reference points, measured:
0.047 Moiré Grid after a subject; 0.031 before
```
+### The measured metadata
+
+`src/scenes/metadata.json` is generated, tracked in git, and holds every
+measured fact about every scene: coverage, variety, per-block scores and the
+structural profile the distance above is computed from. Nothing in it is
+hand-written, and two decisions read it:
+
+* **`surface`** — canvas at 50% coverage or more, composable below. It used to
+ be declared per scene and it drifted; nine scenes claimed `canvas` while
+ painting under a third of the frame. Declaring `surface:` in a scene file is
+ now an error.
+* **which scenes may be a GROUND** — a canvas that does not call `prev()`, fills the frame
+ when sampled as a bed, and does not vanish under any of the six identities. Coverage is
+ measured twice, once as a shot and once as a bed, because a scene paints wildly different
+ amounts at different parameters: Metaballs is 69% as a drop's shot and 6% as an intro's
+ ground.
+
+Refresh it from **gallery.html → refresh metadata** after changing a shader or a
+metric. The file carries a fingerprint of the scenes, the shader contract, the
+identities and palettes, and the descriptor definitions; when that stops
+matching, phase 12 fails and tells you to re-measure. The write goes through a
+dev-only endpoint in `vite.config.js`, so it only works under `npm run dev`.
+
---
## What the descriptor can and cannot see
diff --git a/flow-state/gallery.html b/flow-state/gallery.html
index aefb33d..0912bc8 100644
--- a/flow-state/gallery.html
+++ b/flow-state/gallery.html
@@ -126,6 +126,7 @@
+