# asc_palette — the §5 palette → display seam
ASCENSION's §5 authors the part in sRGB hex and says what every phrase's sky
should **read as on screen**. This module is the arithmetic that makes that
true, in one place, so no aspect re-derives it and no aspect guesses.
It draws nothing and decides nothing about the picture. It owns three facts.
## 1. The space the sky's ramp is evaluated in
§5 gives a phrase two ends — a cold zenith and a warm horizon — and rule 1
forbids a third hue between them. Both cannot hold, and the arithmetic says
which one bends. The straight display-space line from P5's `#2E4560` to its
`#FFC98A` is
```
r(u) = 46 + 209u g(u) = 69 + 132u b(u) = 96 + 42u
```
and at `u = 0.30` those are 108.7 / 108.6 / 108.6 — a neutral at value 109,
which is the `#808080` §5 names as proof the grade has failed. So the ramp
**must** leave the chord. Leaving it on one side of the hue circle gives
magenta, which rule 1 excludes by name; leaving it on the other gives a
teal-green bridge. Those are the only two options: they are the two sides of a
line.
The crossover is therefore authored, deliberately, on the cold-to-teal side,
with its hue rotated along the **descending** arc so the magenta family is
geometrically unreachable rather than swept out afterwards, and its chroma set
to the most sRGB can hold at its lightness (`gamutFit`) — being as far from the
neutral axis as the gamut allows is exactly the anti-neutral position.
Two things keep the bridge from becoming a hue of its own:
- **It is confined.** `HORIZON_TIGHT` holds the warm end near the horizon and
`ZEN_RATE` brings the phrase's own zenith back within a few degrees above it,
so the crossing occupies a narrow band rather than a third of the sky.
- **It is dark.** `MID_L` keeps its lightness near the zenith's, not the
horizon's, so it reads as the shadowed part of a sky.
And it is **azimuthal**, per §6's contre-jour: opposite the sun there is no burn,
so both the warm end and the bridge fade to a darkened cold sky and that whole
side of the frame stays one hue family. The azimuthal fade travels *through* the
bridge for the same reason the vertical one does — a straight line from a cold
sky to a warm one passes next to the neutral axis.
## 2. The display ceiling, derived
§6 permits exactly one element per bar above 1.0 linear and clamps everything
else below §2's bloom threshold of 1.4. **The sky is never that element.** But
§5's bright phrases ask the sky for `#FF9A5A` and `#FFC98A`, whose brightest
channel is display 1.0, and the scene-linear value that lands on display 1.0 is
7.24 — five times the threshold.
`ceilFor(sceneExposure)` is the display value that lands on `SKY_SCENE_CAP`
(1.35, under the threshold by construction) at this frame's exposure. It is not a
constant to sweep: raise `expEV` and the ceiling rises with it. At `expEV = +1.2`
the ceiling is 0.985 and §5's horizon hexes land within 4/255. At `expEV = 0` the
same ceiling is 0.916 and they cannot be reached at all — which is where a washed
horizon comes from.
## 3. The inverse of the chain
The engine runs, in this order:
```
motion_blur 30 · bloom 40 (threshold on max(r,g,b), pre-exposure)
colorGrade 52 c*bri, then mix(luma, c, sat), then contrast (pinned 1.0)
tonemap 60 ACES(c * BASE_EXPOSURE * 2^expEV)
vignette 70 display-space multiply
demo_fade 200 letterbox + the part's fadeIn/fadeOut
asc_endcard_point 210
```
- `invAces` is the exact inverse of the engine's Narkowicz operator.
- `unSaturate` is the exact inverse of the colorGrade pass's saturation term.
- Multiplicative exposure and brightness are cancelled by the dome's own
`exposure` property, which carries `1 / aux.sceneExposure`.
The dome's fragment shader applies all of that per pixel, so a §5 hex lands on
itself whatever the grade and `expEV` do — including at the dark end, where
feeding the plain linearised hex loses the palette outright (`#060A12` arrives at
0.0018 and leaves the tonemap at 0.0004, a ninth of one 8-bit step).
**Anything LIT rather than emissive takes `hexL`, not `hexD`.** The seam is for
surfaces whose §5 value is what the frame should carry; a lit surface's value is
decided by the render.
## The gates, and the measurement window for every number
`verify(zenD, horD, skyCeil, vign, sunGlow, mid)` replays the dome's own blend law
and reports three detectors. `asc_clock.checkPalette()` runs it against the
triples the dome **actually receives** at each timestamp — the §5 rows already
folded toward the void by `skyMix`, the derived ceiling, and the dome's glow term.
| detector | definition | budget |
|---|---|---|
| `magentaPct` | green below both other channels, value ≥ 50 | **0.00 %** |
| `relSatPct` | (max−min)/max < 0.25, value ≥ 40 | ≤ 2 % |
| `absSatPct` | (max−min) ≤ 20, value ≥ 40 | ≤ 2 % |
The magenta gate is exactly zero because that family is exactly avoidable. The
low-saturation gates are not zero and cannot be: some sample on any continuous
ramp between §5's two ends sits near the neutral axis, and the only questions are
how few and how dark.
**The function's numbers are not the frame's numbers, and the frame's are the ones
that count.** Near the horizon a camera looking level compresses many screen rows
into a small range of elevation, so the transition band costs several times the
area a uniform sample over elevation suggests. Measured on the shipped frames
(active frame only, letterbox excluded), the two detectors the round-1 critics
used read:
| | round 1 | now |
|---|---|---|
| mid-tone neutral (value 96–159, sat < 0.10) — **the brief's own test** | 9.2–26.4 % | **0.00 % at all 16 timestamps** |
| magenta family | 3.4–8.5 % | **0.00 % at all 16 timestamps** |
| dark pixels not carrying blue (§5 rule 2) | 44–70 % | **0.00–0.56 %** |
| any low-saturation pixel, value ≥ 60 | 26.4 % at H4 | 0.00 % at 10 of 16; 1.3–4.6 % at 4; 10.5 % at t=05; 25.0 % at t=11 |
The last row is the transition band itself, at value 60–99 — dark olive-teal
rows a few pixels deep, not the mid-tone `#808080` the brief bans and not in the
banned `#C0`–`#E0` band. t = 05 and t = 11 are its worst two frames because
`skyMix` is folding the whole gradient toward a cold void there, which compresses
every channel difference at once.