# Design pass 9: the market strip on Now and the Market page skeleton

Built on the pass 8 files (`design-pass8-2026-09-27/d2-tracks/`). New: `market.html`. `now.html` gains the `Market` nav link (line 163) and one contiguous strip block (lines 164 to 193). `board.html` and `situation.html` gain the `Market` nav link, one line each. Everything else is byte-equal to pass 8 (asserted by diff in (c)). Pass 7 stays the frozen Now; this is the proposal beside it.

Contract: `contract/CHANGES-v3.2.md` exists and has no §9 entry, so the v3.1 shape stands: `market: {asOf, lines[8], weather[3]}`, a line `{id, title, text, value: Field, source, cadence, attribution}`. The data is the `market` object of `contract/fixtures/now.json`; no number was authored.

## (a) What changed, and every departure from T3 §4

**Changed.**
- `now.html`: nav `Now · Board · Market` with `Market` a link carrying `?t=` when rewound (line 163, same onclick pattern as the Board link). One block after the header bar: a `<style>`, the band markup and a small script that fills it from the reading and follows the moment by watching the moment label (`#mT`). Rows, Mini Clock, rail, For you, order rule, end line and footer untouched.
- `board.html`, `situation.html`: `<span>Market</span>` became the link. One line each.
- `market.html`: new page (spec in (b)).
- `scripts/verify.cjs`: new, the Market checks. Pass 8's Board checks moved unchanged to `scripts/verify-board.cjs` (re-run in (c): every Board check passes; the one frozen-Now diff assertion fails by design). `scripts/shots.cjs`: the pass 7 and pass 8 suite plus the four new shots at the end.

**Departures from T3 §4, with the reason.**
1. **Forms dropped.** T3 suggests "one big number + three small band numbers + vs usual arrow", "ranked bar or seven gauges", "ranked list with change arrows", "sparkline", "strip of four small numbers with trend arrows", "colour only on extremes". The brief makes this a skeleton with the frozen grammar: title, sentence, value caption. No arrows, no colour for direction, no charts. The forms are the next Market pass.
2. **Views.** T3 has T, W, M per line. The page is today only and says so in the rule line; W and M wait for 30 days of our own tape (T3 §4 itself: M honest from about 2026-10-10).
3. **Type tags.** T3 marks Breadth and Launchpad pace **D** and Band heat **L / D**; the brief tags lines 1, 2, 3 and 5 `leading, weak`. I followed the brief: T3's own prose under the table names runners count, breadth, band turnover and launchpad graduation pace as the only items "leading only in the weak sense that heat persists for hours to days". Chain heat `describes` (T3 D). Dollars in or out (T3 C), Risk appetite (D) and Leverage (D, extremes mildly contrarian) are `background`, as T3 says to label 7 to 9. The About drawer explains the three words.
4. **Leverage source.** The reading's attribution is `Source: exchange public endpoints` and its basis `provider:exchanges`. The rights pack Market table says Leverage is `Data: Coinalyze` linked, or the line is dropped, and **not** "exchange public endpoints" (Binance terms, RIGHTS §2.13); contract §9 says the rights pack governs over fixture placeholders. The page prints `Data: Coinalyze` linked to `https://coinalyze.net/` and the basis `Coinalyze`. The sentence is the reading's, byte for byte.
5. **Dollars in or out attribution.** The reading says `Source: on-chain totalSupply reads`; the page prints `Source: on-chain supply reads`, the rights pack's caption. `totalSupply` is a contract function name, an internal string on a reader's page.
6. **Other attributions.** Printed per the rights pack: `Source: Caverio tape · Powered by CoinGecko` (Band heat; the reading says `data by CoinGecko`), `Source: GeckoTerminal and Caverio tape · Powered by CoinGecko` (Chain heat), `Powered by CoinGecko` alone (Risk appetite; the reading says `Data by CoinGecko`). Our own captions do not link.
7. **Strip mark placement.** The rights pack says the narrow weather strip prints one line under it: `Powered by CoinGecko`. To keep the band one line tall on desktop (brief), the mark sits at the right end of the band after the clock, same screen, same caption size, linked. On the phone it is on the band's first line.
8. **Strip height on the phone.** The brief asks for at most two lines at 390. The three phrases are 146 characters; at a readable 12.5 px they need three lines, plus the `Market · clock · mark` line: the band is 94 px (four lines). I did not truncate or scroll the phrases (the verify requires them byte for byte and a reader would lose the third phrase). A short phone form is asked for in (e).
9. **Value formatting.** `21000` prints `21K` and `1200000000` prints `$1.2B`, the same forms the reading's own sentences use. Rpc basis prints `chain reads`.

## (b) The strip and Market specification

**Strip on Now.** One band between the header bar and the `Now` head, full width (surface `#10151A`, 1 px `ink .08` border, radius 10). Desktop: one line, 36 px. Left `Market` (600, ink): the page link, stretched over the whole band, so a click or tap anywhere opens `market.html`, or `market.html?t=HH:MMZ` when rewound. Then the three `weather` phrases joined by ` · `, 13 px ink, tabular figures. Right, C03 caption (12 px, ink .60): `Market · 07:15Z` then `Powered by CoinGecko` linked to `https://www.coingecko.com/`, `rel="noopener"`, new tab, shown when any phrase comes from a CoinGecko-marked line (Band heat, Chain heat). Phone (820 and under): `Market` + clock and mark on line one, phrases wrap below; band at least 44 px. Hover and focus add nothing but the focus ring (on the band) and a 1 px border lift on hover. No direction colour, arrows or icons.

**Market page.** Nav `Now · Board · Market`, Market bold, Now and Board carrying `?t=` when rewound. Head `Market` + `8 lines · Live 17:27Z` / `8 lines · Market at HH:MMZ` (`aria-live`). The question: `The weather of the pond you trade in, at HH:MMZ. It describes; it does not forecast.` Clock block exactly Now's: Play, `Market time` over `Live · 17:27Z` / `Market at HH:MMZ` + `Historical`, `Back to live`, `Copy link` (`Copied · HH:MMZ` rewound), the rail with Now's x mapping (`PAD` 6, 16:27Z to 17:27Z, same grid column) and Now's landmarks (surfaced, state change, safety) at or before the moment; drag, arrows, PageUp/PageDown, Home/End; `?t=` in the URL. Under the clock, one caption: `Market · 07:15Z, the last reading at or before HH:MMZ`.

**Card, desktop 1280**, grid `232 | 1fr | 340`, surface `#161C22`:
1. Title, 20/600, the largest text on the card (column 1).
2. Type tag under it, C03 caption: `leading, weak` (Runners today, Band heat, Breadth, Launchpad pace), `describes` (Chain heat), `background` (Dollars in or out, Risk appetite, Leverage).
3. Sentence = `text`, 15 px ink, tabular figures (column 2).
4. Right column, C03 caption: `<value> · <basis> · <cadence>` (value in ink .74, 500): `44 · tape · 15 min`, `1.4x · tape · 1 h`, `31% · tape · 15 min`, `-19% · GeckoTerminal · 1 h`, `21K · chain reads · 1 h`, `$1.2B · chain reads · 5 min`, `58.1% · CoinGecko · 1 h`, `0.01% · Coinalyze · 1 h`. Basis: `tape`, `chain reads` (rpc), the provider's name for `provider:*`. `~` only when the Field carries an estimate basis (none here).
5. The attribution under it (rules below). Then, where the Field carries `switchNote`, `provider field until the switch date` (Chain heat, Risk appetite, Leverage).

**Card, phone 390**: title (18/600), sentence (14 px), one caption paragraph: `value · basis · cadence · attribution [· provider field until the switch date] · type`. Vendor links get a 32 px or taller hit box.

**Order and rules.** Contract order, fixed: `runners_today, band_heat, breadth, chain_heat, launchpad_pace, dollars_in_out, risk_appetite, leverage`. Under the list: `Lines are in a fixed order. None forecasts. Today only; the week and month views start when our tape is 30 days old.` Then About this data (what the lines are, the three type words, the marks and the switch line, and `Example values, not from our tape` naming all eight lines and the 07:15Z reading). Footer: `Caverio · Market, 14 Sep 2026, held at 17:27Z` and the rights pack footer line.

**Historical rule (`asOf <= t`).** At moment t the strip and every card show the latest reading with `asOf` at or before t; nothing after t, no interpolation. The reading is 07:15Z, so live, 16:57Z and 16:42Z all show it, captioned `Market · 07:15Z`. When no reading is at or before t: the strip prints `Market not read yet` on the hatch (Now's `not on our tape yet` hatch, ink .10 diagonal) with no clock and no mark; the page hides the cards and prints the hatched block `Market not read yet` / `Nothing on our tape for the market as of HH:MMZ.`, head `Market not read yet · Market at HH:MMZ`, clock caption `No market reading at or before HH:MMZ`. (Verified by moving the reading to 17:00Z in the test.)

**Attribution rule.** The printed attribution is the reading's `attribution`, except where the rights pack governs: a vendor named gets its exact mark, linked to its home page, `rel="noopener"`, new tab, no parameters (`Powered by CoinGecko` → `https://www.coingecko.com/` for CoinGecko and GeckoTerminal; `Data: Coinalyze` → `https://coinalyze.net/`). Marks are 12 px, the caption size, never under 10 px, on the same screen as the value. Our own captions (`Source: Caverio tape`, `Source: Caverio launchpad reads`, `Source: on-chain supply reads`) do not link.

**Not on Market, ever.** Token rows, a recommendation, a composite index, a forecast word (`will`, `expect`, `likely`), green or red or arrows for a change, any number not in the reading, a vendor logo, a week or month view before our tape is 30 days old.

**Accessibility.** Focus-visible rings (ice .55, 2 px) on nav, the band, the vendor marks, Play, buttons, rail, About. `aria-live` on the moment and the head count. Reduced motion: playback steps one minute at a time. Phone hit boxes 32 px or more, band 44 px or more. AA: minimum contrast 5.87 on Market (the value caption on the card surface), 6.06 on the strip (the clock caption).

## (c) Verification

`NODE_PATH=/home/botbox/node_modules node scripts/verify.cjs` (full output `scripts/verify-market-output.txt`):

```
PASS now.html vs pass 8: hunks 163c163,194 (line 163 changed, one contiguous block added after it, nothing removed)
PASS now.html: lines removed or changed from pass 8: 1 (the nav line)
PASS board.html vs pass 8: 230c230 (one line)
PASS situation.html vs pass 8: 298c298 (one line)
PASS 1280 now live strip phrases byte for byte: "under $1M is the most active band today · Solana leads DEX volume; Robinhood Chain -19% on the week · runners today 44, near the 7-day average"
PASS 1280 now live strip clock "Market · 07:15Z" (moment Live · 17:27Z)
PASS 1280 now live strip mark "Powered by CoinGecko" https://www.coingecko.com/ rel noopener 12px (caption 12px)
PASS 1280 now live strip sits between the header bar and the page head, band 36px, phrases 1 line(s)
PASS 1280 now live band click opens market.html
PASS 1280 market live eight cards in contract order: runners_today, band_heat, breadth, chain_heat, launchpad_pace, dollars_in_out, risk_appetite, leverage
PASS 1280 market live titles and text byte for byte with the reading
PASS 1280 market live title is the largest text on every card (20px)
PASS 1280 market live value captions: 44 · tape · 15 min | 1.4x · tape · 1 h | 31% · tape · 15 min | -19% · GeckoTerminal · 1 h | 21K · chain reads · 1 h | $1.2B · chain reads · 5 min | 58.1% · CoinGecko · 1 h | 0.01% · Coinalyze · 1 h
PASS 1280 market live marks: runners_today "Source: Caverio tape"; band_heat "Source: Caverio tape · Powered by CoinGecko" https://www.coingecko.com/ 12px; breadth "Source: Caverio tape"; chain_heat "Source: GeckoTerminal and Caverio tape · Powered by CoinGecko" https://www.coingecko.com/ 12px; launchpad_pace "Source: Caverio launchpad reads"; dollars_in_out "Source: on-chain supply reads"; risk_appetite "Powered by CoinGecko" https://www.coingecko.com/ 12px; leverage "Data: Coinalyze" https://coinalyze.net/ 12px
PASS 1280 market live type tags: runners_today=leading, weak, band_heat=leading, weak, breadth=leading, weak, chain_heat=describes, launchpad_pace=leading, weak, dollars_in_out=background, risk_appetite=background, leverage=background
PASS 1280 market live switch line on chain_heat, risk_appetite, leverage
PASS 1280 market live question "The weather of the pond you trade in, at 17:27Z. It describes; it does not forecast.", moment "Live · 17:27Z" aria-live polite
PASS 1280 market live reading clock "Market · 07:15Z, the last reading at or before 17:27Z"
PASS 1280 market live rule line
PASS 1280 market live nav A:Now now.html | A:Board board.html | B:Market
PASS 1280 live rail stripX(17:27Z) market 1027 = now 1027
PASS 1280 now.html live Market link market.html, nav *Now · Board · Market
PASS 1280 board.html live Market link market.html, nav Now · *Board · Market
PASS 1280 situation.html live Market link market.html, nav Now · *Situation · Board · Market
PASS 1280 now.html live banned (alpha, smart money, signal, score, rank, PnL, will, expect, likely): zero; em dashes 0
PASS 1280 board.html live banned (alpha, smart money, signal, score, rank, PnL, will, expect, likely): zero; em dashes 0
PASS 1280 situation.html live banned (alpha, smart money, signal, score, rank, PnL, will, expect, likely): zero; em dashes 0
PASS 1280 market.html live banned (alpha, smart money, signal, score, rank, PnL, will, expect, likely): zero; em dashes 0
PASS 1280 now 16:57Z strip phrases byte for byte: "under $1M is the most active band today · Solana leads DEX volume; Robinhood Chain -19% on the week · runners today 44, near the 7-day average"
PASS 1280 now 16:57Z strip clock "Market · 07:15Z" (moment Now at 16:57Z)
PASS 1280 now 16:57Z strip mark "Powered by CoinGecko" https://www.coingecko.com/ rel noopener 12px (caption 12px)
PASS 1280 now 16:57Z strip sits between the header bar and the page head, band 36px, phrases 1 line(s)
PASS 1280 now 16:57Z band click opens market.html?t=16:57Z
PASS 1280 market 16:57Z eight cards in contract order: runners_today, band_heat, breadth, chain_heat, launchpad_pace, dollars_in_out, risk_appetite, leverage
PASS 1280 market 16:57Z titles and text byte for byte with the reading
PASS 1280 market 16:57Z title is the largest text on every card (20px)
PASS 1280 market 16:57Z value captions: 44 · tape · 15 min | 1.4x · tape · 1 h | 31% · tape · 15 min | -19% · GeckoTerminal · 1 h | 21K · chain reads · 1 h | $1.2B · chain reads · 5 min | 58.1% · CoinGecko · 1 h | 0.01% · Coinalyze · 1 h
PASS 1280 market 16:57Z marks: runners_today "Source: Caverio tape"; band_heat "Source: Caverio tape · Powered by CoinGecko" https://www.coingecko.com/ 12px; breadth "Source: Caverio tape"; chain_heat "Source: GeckoTerminal and Caverio tape · Powered by CoinGecko" https://www.coingecko.com/ 12px; launchpad_pace "Source: Caverio launchpad reads"; dollars_in_out "Source: on-chain supply reads"; risk_appetite "Powered by CoinGecko" https://www.coingecko.com/ 12px; leverage "Data: Coinalyze" https://coinalyze.net/ 12px
PASS 1280 market 16:57Z type tags: runners_today=leading, weak, band_heat=leading, weak, breadth=leading, weak, chain_heat=describes, launchpad_pace=leading, weak, dollars_in_out=background, risk_appetite=background, leverage=background
PASS 1280 market 16:57Z switch line on chain_heat, risk_appetite, leverage
PASS 1280 market 16:57Z question "The weather of the pond you trade in, at 16:57Z. It describes; it does not forecast.", moment "Market at 16:57Z" aria-live polite
PASS 1280 market 16:57Z reading clock "Market · 07:15Z, the last reading at or before 16:57Z"
PASS 1280 market 16:57Z rule line
PASS 1280 market 16:57Z nav A:Now now.html?t=16:57Z | A:Board board.html?t=16:57Z | B:Market
PASS 1280 16:57Z rail stripX(16:57Z) market 880 = now 880
PASS 1280 now.html 16:57Z Market link market.html?t=16:57Z, nav *Now · Board · Market
PASS 1280 board.html 16:57Z Market link market.html?t=16:57Z, nav Now · *Board · Market
PASS 1280 situation.html 16:57Z Market link market.html?t=16:57Z, nav Now · *Situation · Board · Market
PASS 1280 now.html 16:57Z banned (alpha, smart money, signal, score, rank, PnL, will, expect, likely): zero; em dashes 0
PASS 1280 board.html 16:57Z banned (alpha, smart money, signal, score, rank, PnL, will, expect, likely): zero; em dashes 0
PASS 1280 situation.html 16:57Z banned (alpha, smart money, signal, score, rank, PnL, will, expect, likely): zero; em dashes 0
PASS 1280 market.html 16:57Z banned (alpha, smart money, signal, score, rank, PnL, will, expect, likely): zero; em dashes 0
PASS 1280 now 16:42Z strip phrases byte for byte: "under $1M is the most active band today · Solana leads DEX volume; Robinhood Chain -19% on the week · runners today 44, near the 7-day average"
PASS 1280 now 16:42Z strip clock "Market · 07:15Z" (moment Now at 16:42Z)
PASS 1280 now 16:42Z strip mark "Powered by CoinGecko" https://www.coingecko.com/ rel noopener 12px (caption 12px)
PASS 1280 now 16:42Z strip sits between the header bar and the page head, band 36px, phrases 1 line(s)
PASS 1280 now 16:42Z band click opens market.html?t=16:42Z
PASS 1280 market 16:42Z eight cards in contract order: runners_today, band_heat, breadth, chain_heat, launchpad_pace, dollars_in_out, risk_appetite, leverage
PASS 1280 market 16:42Z titles and text byte for byte with the reading
PASS 1280 market 16:42Z title is the largest text on every card (20px)
PASS 1280 market 16:42Z value captions: 44 · tape · 15 min | 1.4x · tape · 1 h | 31% · tape · 15 min | -19% · GeckoTerminal · 1 h | 21K · chain reads · 1 h | $1.2B · chain reads · 5 min | 58.1% · CoinGecko · 1 h | 0.01% · Coinalyze · 1 h
PASS 1280 market 16:42Z marks: runners_today "Source: Caverio tape"; band_heat "Source: Caverio tape · Powered by CoinGecko" https://www.coingecko.com/ 12px; breadth "Source: Caverio tape"; chain_heat "Source: GeckoTerminal and Caverio tape · Powered by CoinGecko" https://www.coingecko.com/ 12px; launchpad_pace "Source: Caverio launchpad reads"; dollars_in_out "Source: on-chain supply reads"; risk_appetite "Powered by CoinGecko" https://www.coingecko.com/ 12px; leverage "Data: Coinalyze" https://coinalyze.net/ 12px
PASS 1280 market 16:42Z type tags: runners_today=leading, weak, band_heat=leading, weak, breadth=leading, weak, chain_heat=describes, launchpad_pace=leading, weak, dollars_in_out=background, risk_appetite=background, leverage=background
PASS 1280 market 16:42Z switch line on chain_heat, risk_appetite, leverage
PASS 1280 market 16:42Z question "The weather of the pond you trade in, at 16:42Z. It describes; it does not forecast.", moment "Market at 16:42Z" aria-live polite
PASS 1280 market 16:42Z reading clock "Market · 07:15Z, the last reading at or before 16:42Z"
PASS 1280 market 16:42Z rule line
PASS 1280 market 16:42Z nav A:Now now.html?t=16:42Z | A:Board board.html?t=16:42Z | B:Market
PASS 1280 16:42Z rail stripX(16:42Z) market 806.5 = now 806.5
PASS 1280 now.html 16:42Z Market link market.html?t=16:42Z, nav *Now · Board · Market
PASS 1280 board.html 16:42Z Market link market.html?t=16:42Z, nav Now · *Board · Market
PASS 1280 situation.html 16:42Z Market link market.html?t=16:42Z, nav Now · *Situation · Board · Market
PASS 1280 now.html 16:42Z banned (alpha, smart money, signal, score, rank, PnL, will, expect, likely): zero; em dashes 0
PASS 1280 board.html 16:42Z banned (alpha, smart money, signal, score, rank, PnL, will, expect, likely): zero; em dashes 0
PASS 1280 situation.html 16:42Z banned (alpha, smart money, signal, score, rank, PnL, will, expect, likely): zero; em dashes 0
PASS 1280 market.html 16:42Z banned (alpha, smart money, signal, score, rank, PnL, will, expect, likely): zero; em dashes 0
PASS 390 now live strip phrases byte for byte: "under $1M is the most active band today · Solana leads DEX volume; Robinhood Chain -19% on the week · runners today 44, near the 7-day average"
PASS 390 now live strip clock "Market · 07:15Z" (moment Live · 17:27Z)
PASS 390 now live strip mark "Powered by CoinGecko" https://www.coingecko.com/ rel noopener 12px (caption 12px)
PASS 390 now live strip sits between the header bar and the page head, band 94px, phrases 3 line(s)
PASS 390 now live band height 94 >= 44, scrollWidth 390
PASS 390 now live band click opens market.html
PASS 390 market live eight cards in contract order: runners_today, band_heat, breadth, chain_heat, launchpad_pace, dollars_in_out, risk_appetite, leverage
PASS 390 market live titles and text byte for byte with the reading
PASS 390 market live title is the largest text on every card (18px)
PASS 390 market live value captions: 44 · tape · 15 min | 1.4x · tape · 1 h | 31% · tape · 15 min | -19% · GeckoTerminal · 1 h | 21K · chain reads · 1 h | $1.2B · chain reads · 5 min | 58.1% · CoinGecko · 1 h | 0.01% · Coinalyze · 1 h
PASS 390 market live marks: runners_today "Source: Caverio tape"; band_heat "Source: Caverio tape · Powered by CoinGecko" https://www.coingecko.com/ 12px; breadth "Source: Caverio tape"; chain_heat "Source: GeckoTerminal and Caverio tape · Powered by CoinGecko" https://www.coingecko.com/ 12px; launchpad_pace "Source: Caverio launchpad reads"; dollars_in_out "Source: on-chain supply reads"; risk_appetite "Powered by CoinGecko" https://www.coingecko.com/ 12px; leverage "Data: Coinalyze" https://coinalyze.net/ 12px
PASS 390 market live type tags: runners_today=leading, weak, band_heat=leading, weak, breadth=leading, weak, chain_heat=describes, launchpad_pace=leading, weak, dollars_in_out=background, risk_appetite=background, leverage=background
PASS 390 market live switch line on chain_heat, risk_appetite, leverage
PASS 390 market live question "The weather of the pond you trade in, at 17:27Z. It describes; it does not forecast.", moment "Live · 17:27Z" aria-live polite
PASS 390 market live reading clock "Market · 07:15Z, the last reading at or before 17:27Z"
PASS 390 market live rule line
PASS 390 market live nav A:Now now.html | A:Board board.html | B:Market
PASS 390 market live scrollWidth 390
PASS 390 live rail stripX(17:27Z) market 349 = now 349
PASS 390 now.html live Market link market.html, nav *Now · Board · Market
PASS 390 now.html live scrollWidth 390
PASS 390 board.html live Market link market.html, nav Now · *Board · Market
PASS 390 board.html live scrollWidth 390
PASS 390 situation.html live Market link market.html, nav Now · *Situation · Board · Market
PASS 390 situation.html live scrollWidth 390
PASS 390 now.html live banned (alpha, smart money, signal, score, rank, PnL, will, expect, likely): zero; em dashes 0
PASS 390 board.html live banned (alpha, smart money, signal, score, rank, PnL, will, expect, likely): zero; em dashes 0
PASS 390 situation.html live banned (alpha, smart money, signal, score, rank, PnL, will, expect, likely): zero; em dashes 0
PASS 390 market.html live banned (alpha, smart money, signal, score, rank, PnL, will, expect, likely): zero; em dashes 0
PASS 390 now 16:57Z strip phrases byte for byte: "under $1M is the most active band today · Solana leads DEX volume; Robinhood Chain -19% on the week · runners today 44, near the 7-day average"
PASS 390 now 16:57Z strip clock "Market · 07:15Z" (moment Now at 16:57Z)
PASS 390 now 16:57Z strip mark "Powered by CoinGecko" https://www.coingecko.com/ rel noopener 12px (caption 12px)
PASS 390 now 16:57Z strip sits between the header bar and the page head, band 94px, phrases 3 line(s)
PASS 390 now 16:57Z band height 94 >= 44, scrollWidth 390
PASS 390 now 16:57Z band click opens market.html?t=16:57Z
PASS 390 market 16:57Z eight cards in contract order: runners_today, band_heat, breadth, chain_heat, launchpad_pace, dollars_in_out, risk_appetite, leverage
PASS 390 market 16:57Z titles and text byte for byte with the reading
PASS 390 market 16:57Z title is the largest text on every card (18px)
PASS 390 market 16:57Z value captions: 44 · tape · 15 min | 1.4x · tape · 1 h | 31% · tape · 15 min | -19% · GeckoTerminal · 1 h | 21K · chain reads · 1 h | $1.2B · chain reads · 5 min | 58.1% · CoinGecko · 1 h | 0.01% · Coinalyze · 1 h
PASS 390 market 16:57Z marks: runners_today "Source: Caverio tape"; band_heat "Source: Caverio tape · Powered by CoinGecko" https://www.coingecko.com/ 12px; breadth "Source: Caverio tape"; chain_heat "Source: GeckoTerminal and Caverio tape · Powered by CoinGecko" https://www.coingecko.com/ 12px; launchpad_pace "Source: Caverio launchpad reads"; dollars_in_out "Source: on-chain supply reads"; risk_appetite "Powered by CoinGecko" https://www.coingecko.com/ 12px; leverage "Data: Coinalyze" https://coinalyze.net/ 12px
PASS 390 market 16:57Z type tags: runners_today=leading, weak, band_heat=leading, weak, breadth=leading, weak, chain_heat=describes, launchpad_pace=leading, weak, dollars_in_out=background, risk_appetite=background, leverage=background
PASS 390 market 16:57Z switch line on chain_heat, risk_appetite, leverage
PASS 390 market 16:57Z question "The weather of the pond you trade in, at 16:57Z. It describes; it does not forecast.", moment "Market at 16:57Z" aria-live polite
PASS 390 market 16:57Z reading clock "Market · 07:15Z, the last reading at or before 16:57Z"
PASS 390 market 16:57Z rule line
PASS 390 market 16:57Z nav A:Now now.html?t=16:57Z | A:Board board.html?t=16:57Z | B:Market
PASS 390 market 16:57Z scrollWidth 390
PASS 390 16:57Z rail stripX(16:57Z) market 195 = now 195
PASS 390 now.html 16:57Z Market link market.html?t=16:57Z, nav *Now · Board · Market
PASS 390 now.html 16:57Z scrollWidth 390
PASS 390 board.html 16:57Z Market link market.html?t=16:57Z, nav Now · *Board · Market
PASS 390 board.html 16:57Z scrollWidth 390
PASS 390 situation.html 16:57Z Market link market.html?t=16:57Z, nav Now · *Situation · Board · Market
PASS 390 situation.html 16:57Z scrollWidth 390
PASS 390 now.html 16:57Z banned (alpha, smart money, signal, score, rank, PnL, will, expect, likely): zero; em dashes 0
PASS 390 board.html 16:57Z banned (alpha, smart money, signal, score, rank, PnL, will, expect, likely): zero; em dashes 0
PASS 390 situation.html 16:57Z banned (alpha, smart money, signal, score, rank, PnL, will, expect, likely): zero; em dashes 0
PASS 390 market.html 16:57Z banned (alpha, smart money, signal, score, rank, PnL, will, expect, likely): zero; em dashes 0
PASS 390 now 16:42Z strip phrases byte for byte: "under $1M is the most active band today · Solana leads DEX volume; Robinhood Chain -19% on the week · runners today 44, near the 7-day average"
PASS 390 now 16:42Z strip clock "Market · 07:15Z" (moment Now at 16:42Z)
PASS 390 now 16:42Z strip mark "Powered by CoinGecko" https://www.coingecko.com/ rel noopener 12px (caption 12px)
PASS 390 now 16:42Z strip sits between the header bar and the page head, band 94px, phrases 3 line(s)
PASS 390 now 16:42Z band height 94 >= 44, scrollWidth 390
PASS 390 now 16:42Z band click opens market.html?t=16:42Z
PASS 390 market 16:42Z eight cards in contract order: runners_today, band_heat, breadth, chain_heat, launchpad_pace, dollars_in_out, risk_appetite, leverage
PASS 390 market 16:42Z titles and text byte for byte with the reading
PASS 390 market 16:42Z title is the largest text on every card (18px)
PASS 390 market 16:42Z value captions: 44 · tape · 15 min | 1.4x · tape · 1 h | 31% · tape · 15 min | -19% · GeckoTerminal · 1 h | 21K · chain reads · 1 h | $1.2B · chain reads · 5 min | 58.1% · CoinGecko · 1 h | 0.01% · Coinalyze · 1 h
PASS 390 market 16:42Z marks: runners_today "Source: Caverio tape"; band_heat "Source: Caverio tape · Powered by CoinGecko" https://www.coingecko.com/ 12px; breadth "Source: Caverio tape"; chain_heat "Source: GeckoTerminal and Caverio tape · Powered by CoinGecko" https://www.coingecko.com/ 12px; launchpad_pace "Source: Caverio launchpad reads"; dollars_in_out "Source: on-chain supply reads"; risk_appetite "Powered by CoinGecko" https://www.coingecko.com/ 12px; leverage "Data: Coinalyze" https://coinalyze.net/ 12px
PASS 390 market 16:42Z type tags: runners_today=leading, weak, band_heat=leading, weak, breadth=leading, weak, chain_heat=describes, launchpad_pace=leading, weak, dollars_in_out=background, risk_appetite=background, leverage=background
PASS 390 market 16:42Z switch line on chain_heat, risk_appetite, leverage
PASS 390 market 16:42Z question "The weather of the pond you trade in, at 16:42Z. It describes; it does not forecast.", moment "Market at 16:42Z" aria-live polite
PASS 390 market 16:42Z reading clock "Market · 07:15Z, the last reading at or before 16:42Z"
PASS 390 market 16:42Z rule line
PASS 390 market 16:42Z nav A:Now now.html?t=16:42Z | A:Board board.html?t=16:42Z | B:Market
PASS 390 market 16:42Z scrollWidth 390
PASS 390 16:42Z rail stripX(16:42Z) market 118 = now 118
PASS 390 now.html 16:42Z Market link market.html?t=16:42Z, nav *Now · Board · Market
PASS 390 now.html 16:42Z scrollWidth 390
PASS 390 board.html 16:42Z Market link market.html?t=16:42Z, nav Now · *Board · Market
PASS 390 board.html 16:42Z scrollWidth 390
PASS 390 situation.html 16:42Z Market link market.html?t=16:42Z, nav Now · *Situation · Board · Market
PASS 390 situation.html 16:42Z scrollWidth 390
PASS 390 now.html 16:42Z banned (alpha, smart money, signal, score, rank, PnL, will, expect, likely): zero; em dashes 0
PASS 390 board.html 16:42Z banned (alpha, smart money, signal, score, rank, PnL, will, expect, likely): zero; em dashes 0
PASS 390 situation.html 16:42Z banned (alpha, smart money, signal, score, rank, PnL, will, expect, likely): zero; em dashes 0
PASS 390 market.html 16:42Z banned (alpha, smart money, signal, score, rank, PnL, will, expect, likely): zero; em dashes 0
PASS reading moved to 17:00Z: strip at 16:57Z "Market not read yet" (hatched true), at 17:10Z "under $1M is the most active b..." "Market · 17:00Z"
PASS reading moved to 17:00Z: market at 16:57Z cards 0, "Market not read yet | Nothing on our tape for the market as of 16:57Z.", head "Market not read yet · Market at 16:57Z", hatch repeating-linear-gradient; at 17:00Z cards 8, "Market · 17:00Z, the last reading at or before 17:00Z"
PASS rail keys 30 x Left: Market at 16:57Z, copy market.html?t=16:57Z "Copied · 16:57Z", Back to live shown true
PASS URL after rewind market.html?t=16:57Z
PASS Back to live: market.html
PASS drag to x of 16:42Z: Market at 16:42Z
PASS AA market.html body *: min 5.87 (va cap "44 · tape · 15 min")
PASS AA now.html #mkt, #mkt *: min 6.06 (SPAN "Market · 07:15Z")
PASS Tab order from the top of Now: :auto 1px, :auto 1px, mktA:solid 2px, mktM:solid 2px
PASS 390 market hit boxes >= 32 px: play 35, btn 35, rail 47, A@band_heat 37, A@chain_heat 37, A@risk_appetite 37, A@leverage 37, SUMMARY 53
PASS 390 now band hit box 94 px
PASS reduced motion: play steps a minute at a time, after 0.7 s at 16:32Z
PASS U+2014 absent in now.html
PASS U+2014 absent in board.html
PASS U+2014 absent in situation.html
PASS U+2014 absent in market.html
PASS U+2014 absent in scripts/verify.cjs
PASS U+2014 absent in scripts/shots.cjs
PASS market.html markup: no pass N, worker or internal names
hex colours in market.html: #0A0D10 #10151A #161C22 #1C232B #E6EAED #72C3E6 #C8742E #D6336C #E9E3A4
ALL PASS
```

`scripts/shots.cjs` (the pass 7 Now suite + pass 8 shots + the four new shots; full output `scripts/verify-output.txt`). Against pass 8's `verify-output.txt` the only differences are the play timing (1.3 s to 1.4 s), the popover y on the phone (100 px lower: the strip sits above the list), the Tab order on Now (the strip link and the CoinGecko mark come after the nav; the band ring is drawn on the link's `:after`, checked in verify.cjs), and the four new shot lines. The `situation rendered banned: contract` line is pass 8's own (the Safety section's "contract, deployer"), unchanged.

```
now-desktop.jpg scrollWidth 1280 ok | overflowing: none | 189 KB q 80 | fonts Geist
now-desktop-1657.jpg scrollWidth 1280 ok | overflowing: none | 127 KB q 80 | fonts Geist
now-desktop-hover.jpg scrollWidth 1280 ok | overflowing: none | 192 KB q 80 | fonts Geist
now-phone.jpg scrollWidth 390 ok | overflowing: none | 159 KB q 80 | fonts Geist
now-phone-1657.jpg scrollWidth 390 ok | overflowing: none | 106 KB q 80 | fonts Geist
now-phone-why.jpg scrollWidth 390 ok | overflowing: none | 157 KB q 80 | fonts Geist
situation-desktop.jpg scrollWidth 1280 ok | overflowing: none | 205 KB q 80 | fonts Geist/Geist Mono
board-desktop.jpg scrollWidth 1280 ok | overflowing: none | 146 KB q 80 | fonts Geist
board-desktop-1657.jpg scrollWidth 1280 ok | overflowing: none | 109 KB q 80 | fonts Geist
board-phone.jpg scrollWidth 390 ok | overflowing: none | 128 KB q 80 | fonts Geist
board-phone-why.jpg scrollWidth 390 ok | overflowing: none | 130 KB q 80 | fonts Geist
  shot now-strip-desktop.jpg: 189 KB (q 80), scrollWidth 1280, fonts Geist
  shot market-desktop.jpg: 131 KB (q 80), scrollWidth 1280, fonts Geist
  shot market-desktop-1657.jpg: 134 KB (q 80), scrollWidth 1280, fonts Geist
  shot market-phone.jpg: 102 KB (q 80), scrollWidth 390, fonts Geist
```

`scripts/verify-board.cjs` (pass 8's Board checks, re-run on these files): 78 PASS, 1 FAIL, the same 79 checks as pass 8 (full output `scripts/verify-board-output.txt`). The one FAIL is pass 8's own assertion "now.html differs from pass 7 by line 163 only": pass 9 adds the strip block to Now on purpose, so that frozen-Now assertion no longer holds and is replaced by the pass 9 diff check against pass 8 in verify.cjs. Every Board check passes.

## (d) Hashes and diffs against pass 8

```
1e99a3bfccd4f7efab3248f71db3ee07a9b26588d32061a6968b8e69b6df2aee  now.html
8abc2e092066433b4e061d105f820f5882139a0106ba42df0ab49dcb3a9c0c18  board.html
70071c5f28a89ce9c01d4248b74aaefc866dc14a01184bfd98709e6d52a44af8  situation.html
fe75b052054a1b4b4d24d81e2da071a48678b8ab25ef463f108abb87df472d53  market.html
```

```
$ diff pass8/now.html pass9/now.html
163c163,194
< <header class="bar"><div class="wm"><i></i>CAVERIO</div><nav class="nav"><b>Now</b><a href="board.html" onclick="var t=new URLSearchParams(location.search).get('t');this.href='board.html'+(t?'?t='+t:'')">Board</a><span>Market</span></nav><div class="search">Search a token or paste an address</div></header>
---
> <header class="bar"><div class="wm"><i></i>CAVERIO</div><nav class="nav"><b>Now</b><a href="board.html" onclick="var t=new URLSearchParams(location.search).get('t');this.href='board.html'+(t?'?t='+t:'')">Board</a><a href="market.html" onclick="var t=new URLSearchParams(location.search).get('t');this.href='market.html'+(t?'?t='+t:'')">Market</a></nav><div class="search">Search a token or paste an address</div></header>
> <!-- the market strip: three weather phrases and the market clock, filled from the market reading; tap opens the Market page at the same moment -->
> <style>
> .mkt{position:relative;display:flex;align-items:baseline;gap:14px;min-height:36px;padding:8px 14px;margin:2px 0 4px;background:var(--s1);border:1px solid var(--hl);border-radius:10px;font-size:13px;line-height:1.4}
> .mkt:hover{border-color:var(--hl2)}
> .mkt .mk{font-weight:600;color:var(--ink);flex:0 0 auto}
> .mkt .mk:after{content:"";position:absolute;inset:0;border-radius:10px}
> .mkt .mk:focus-visible{outline:none}.mkt .mk:focus-visible:after{outline:2px solid rgba(114,195,230,.55);outline-offset:2px}
> .mkt .mw{flex:1 1 auto;min-width:0;color:var(--ink)}
> .mkt .mc{flex:0 0 auto;display:flex;gap:6px;align-items:baseline;white-space:nowrap}
> .mkt .mc a{position:relative;z-index:2;color:var(--u);text-decoration:underline;text-decoration-color:rgba(230,234,237,.3);text-underline-offset:2px;padding:4px 0;margin:-4px 0}
> .mkt .mc a:hover{color:var(--n)}
> .mkt .mc a:focus-visible{outline:2px solid rgba(114,195,230,.55);outline-offset:2px;border-radius:3px}
> .mkt.nr .mw{color:var(--n);background:repeating-linear-gradient(45deg,rgba(230,234,237,.10) 0 1.2px,transparent 1.2px 5px);border-radius:4px;padding:0 6px}
> @media (max-width:820px){.mkt{flex-wrap:wrap;gap:2px 10px;min-height:44px;padding:10px 12px;margin:0 0 6px;font-size:12.5px}.mkt .mw{order:3;flex-basis:100%}.mkt .mc{margin-left:auto}}
> </style>
> <div class="mkt" id="mkt"><a class="mk" id="mktA" href="market.html">Market</a><span class="mw" id="mktW"></span><span class="mc cap"><span id="mktC"></span><a id="mktM" href="https://www.coingecko.com/" target="_blank" rel="noopener" hidden>Powered by CoinGecko</a></span></div>
> <script>
> (function(){
> // the market reading on the page's day (market.asOf, lines[].attribution, weather[3]); the phrase-to-line map is ours, the reading does not carry it
> var MKT={asOf:'07:15Z',weather:['under $1M is the most active band today','Solana leads DEX volume; Robinhood Chain -19% on the week','runners today 44, near the 7-day average'],weatherLines:['band_heat','chain_heat','runners_today'],coingecko:['band_heat','chain_heat','risk_appetite']};
> function mins(c){var p=String(c).replace(/z$/i,'').split(':');return (+p[0])*60+(+p[1]);}
> function draw(){var mt=document.getElementById('mT'),s=mt?mt.textContent:'',m=(s.match(/(\d\d:\d\d)Z/)||[])[1],live=/^Live/.test(s),read=!m||mins(MKT.asOf)<=mins(m);
>  var a=document.getElementById('mktA');a.href='market.html'+(m&&!live?'?t='+m+'Z':'');
>  document.getElementById('mkt').classList.toggle('nr',!read);
>  document.getElementById('mktW').textContent=read?MKT.weather.join(' · '):'Market not read yet';
>  document.getElementById('mktC').textContent=read?'Market · '+MKT.asOf:'';
>  document.getElementById('mktM').hidden=!(read&&MKT.weatherLines.some(function(l){return MKT.coingecko.indexOf(l)>=0;}));}
> window.caverioMarket={reading:MKT,draw:draw};
> document.addEventListener('DOMContentLoaded',function(){draw();var mt=document.getElementById('mT');if(mt&&window.MutationObserver)new MutationObserver(draw).observe(mt,{childList:true,characterData:true,subtree:true});});
> })();
> </script>
$ diff pass8/board.html pass9/board.html
230c230
< <header class="bar"><div class="wm"><i></i>CAVERIO</div><nav class="nav"><a href="now.html" id="navNow">Now</a><b>Board</b><span>Market</span></nav><div class="search">Search a token or paste an address</div></header>
---
> <header class="bar"><div class="wm"><i></i>CAVERIO</div><nav class="nav"><a href="now.html" id="navNow">Now</a><b>Board</b><a href="market.html" onclick="var t=new URLSearchParams(location.search).get('t');this.href='market.html'+(t?'?t='+t:'')">Market</a></nav><div class="search">Search a token or paste an address</div></header>
$ diff pass8/situation.html pass9/situation.html
298c298
<  <nav class="nav"><a href="now.html" id="navNow">Now</a><b>Situation</b><a href="board.html" onclick="var t=new URLSearchParams(location.search).get('t');this.href='board.html'+(t?'?t='+t:'')">Board</a><span>Market</span></nav>
---
>  <nav class="nav"><a href="now.html" id="navNow">Now</a><b>Situation</b><a href="board.html" onclick="var t=new URLSearchParams(location.search).get('t');this.href='board.html'+(t?'?t='+t:'')">Board</a><a href="market.html" onclick="var t=new URLSearchParams(location.search).get('t');this.href='market.html'+(t?'?t='+t:'')">Market</a></nav>
```

## (e) Wants from the read model (for v3.3)

- `market.asOf` on the same day as `now.asOf`: the reading is dated 2026-09-27T07:15Z and Now is 2026-09-14T17:27:30Z; the page compares clock times on Now's day. A dated compare would print `Market not read yet` at every moment.
- `market.readings[]` (or `lines[].asOf`): one `asOf` per document means one reading for the whole hour; the historical rule needs the reading history, and lines with 5 min and 1 h cadences have different clocks.
- `weather[]` as `{text, lineIds[]}`: the strip's CoinGecko mark needs to know which lines a phrase comes from; the page hardcodes Band heat and Chain heat.
- `weather[].short` (40 characters or fewer): the three phrases need four lines on a 390 px phone; the brief wants two.
- `lines[].attribution` per the rights pack: Leverage `Data: Coinalyze` (not `exchange public endpoints`), Dollars in or out `on-chain supply reads` (not `totalSupply`), `Powered by CoinGecko` for the vendor mark; plus a `mark: {label, href}` field so the page does not keep its own table.
- `lines[].kind` in {`leading_weak`, `describes`, `background`}: the type tag is T3's judgement, today hardcoded on the page.
- `value.display`: the formatted value (`21K`, `$1.2B`, `1.4x`); `unit` alone leaves the format to the page.
- `value.basis: provider:exchanges` → `provider:coinalyze` for Leverage, if that is the source.
- `lines[].sevenDay` / `thirtyDay` when the tape is 30 days old, for the week and month views.

Status: DONE
