---
title: Polish the film game page's play surface, controls and series readouts
type: task
status: completed
description: >-
  Operator's next iteration on /film/game/:esbid: pill-style play type and event badges, sturdier
  explosive/TD display, ordinal downs, a subtle series grouping, drive EPA and yardage in drive
  heads, a button-styled controls disclosure, and one voice for the keys section. Deployed base:
  a70d1fc14.
base_uri: user:task/league/polish-film-game-play-surface-and-controls.md
created_at: '2026-11-05T00:17:29.416Z'
entity_id: 5de4219d-e0c0-42a6-92a1-634b0efa6dcf
finished_at: '2026-11-05T04:27:52.597Z'
observations:
  - ████████████████████████████████████████████████
  - ████████████████████████████████████████████████
  - ████████████████████████████████████████████████
  - ████████████████████████████████████████████████
  - ████████████████████████████████████████████████
  - ████████████████████████████████████████████████
  - ████████████████████████████████████████████████
owner_identity_uri: █████████████████████████
priority: Medium
public_read: true
started_at: '2026-11-05T00:32:19.820Z'
tags:
  - user:tag/league-xo-football.md
updated_at: '2026-11-05T04:27:52.597Z'
visibility_analyzed_at: '2026-11-05T23:49:49.106Z'
---

The operator's iteration on `/film/game/:esbid` (the film-game page) play list and stage, worked through four review rounds on 2026-11-04 and deployed after each; the last deploy is `1683780e4` (2026-11-04 23:12 EST). The surface now: drives are framed blocks with a team-coloured top edge and a paper head (team, outcome pill, start time, an SVG drive chart, a labelled figure grid of start, plays, yards, first downs, time and EPA); each series of downs closes on a strip naming how it ended; rows carry a neutral type pill, an ordinal down, a signed EPA figure and a result pill (solid for TD, INT or FUMBLE, soft for SACK or EXPLOSIVE, gain in the pill), and wash green or red by EPA; the controls disclosure is a Controls button with the step key chips; the keys section has one label. The rules are recorded in STYLE.md § The film play list.

## Operator's requests (handed off 2026-11-04 EST)

- The play type should get a background colour, pill-like. Give the same pill treatment to the other play events (the score / turnover / sack / explosive kind badges), not just a coloured word.
- Reconsider how explosive plays and touchdowns are displayed — currently one small kind word each; make the display carry more weight while staying in the prose voice. Questions to settle in one pass: what should a total-touchdown drive's plays look like; does an explosive on every play of a drive read as noise?
- Down labels must read ordinal: `1st, 2nd, 3rd, 4th & 7`, not `1 & 7`. `film_down_label` in [`app/views/components/film-play-browser/film-play-browse.mjs`] currently interpolates the bare number.
- The series must be visually separable, subtly — a soft grouping read per drive so the eye can follow one series of downs end to end. No lone left border (STYLE.md): state in words and fills.
- Drive heads / group meta must show drive EPA and yardage gained, etc. Check which columns exist (`nfl_drives`, `nfl_plays`, plays view) — EPA is a live gap; yardage and play counts already render.
- The controls disclosure should read as a real button with a text label, not a bare band word. Carries the previous/next key chips.
- The keys section has multiple labels — the collapsed "Keys" toggle and its expanded "Keys" / "Edit keys" heads duplicate; tighten to one voice.

## Constraints that bind every item

- No lone left border on any row (STYLE.md ruling 2026-11-04). State lives in words; a selected row is a shared fill.
- `$film_selected` stays distinct from all NFL team colours; keep the violet. Do not reuse a team colour for any state.
- New colours go in [`app/styles/palette.styl`] via `role()`, documented in STYLE.md, and pass the 4.5:1 contrast floor on the ground.
- Result flags are read in [`libs-server/film/film-play-detail.mjs`] directly from `nfl_plays` (`play_pass_yards`, `play_rush_yards`, `play_is_sack`, `play_is_interception`, `play_is_fumble_lost`, `play_is_*_touchdown`); explosives via [`libs-shared/explosive-play-yards.mjs`] (`PASS ≥ 20`, `RUSH ≥ 10`).
- A league deploy ships all of `origin/master` and must run from a throwaway worktree (`DEPLOY_ACK_RANGE=<n> yarn deploy:all`) — an unrelated stray untracked `scripts/_tmp_q2.mjs` fails the in-tree gate (2); never delete that file (not yours).

## Plan

- [x] Style the play type and the play-event kind badges as pills with background fills (keep the word in the pill; keep the `$film_*` colour voice).
- [x] Settle and implement a stronger display for touchdowns and explosive plays; write the ruling into STYLE.md's "film play list" section.
- [x] Ordinalise down labels (`1st`–`4th`) in `film_down_label`.
- [x] Add a subtle series grouping to the play list so a drive's down series reads end to end.
- [x] Surface drive EPA and yardage gained in the drive/group heads; find the columns first.
- [x] Make the controls disclosure a button with a text label.
- [x] Collapse the keys section's duplicate labels to one.
- [x] Spec each pure function change (test/app.film-browse.spec.mjs pattern) and run `bash scripts/test-isolated.sh test/app.film-browse.spec.mjs test/app.film-keys.spec.mjs` plus prettier/eslint/`yarn check:color-literals`.

## Review rounds (2026-11-04, all deployed)

- [x] Round two: nearly square pills; no sideways scroll; series made legible; row background by EPA; drive head that does not cut off; per-play signed EPA.
- [x] Round three: drive as a framed block with a structured head; series result on its own strip; where a drive starts and ends visible.
- [x] Round four: a drive chart in each drive head (field position, per-snap gain or loss, first downs, touchdown end zone, selected play).

## Key locations

- `app/views/components/film-play-browser/film-play-list.js` + `.styl` — rows, pills, series.
- `app/views/components/film-play-stage/film-play-stage.js` + `.styl` — controls disclosure and keys section.
- `app/views/components/film-play-browser/film-play-browse.mjs` — `film_down_label`, drive grouping, play kinds.
- `libs-server/film/film-play-detail.mjs` — play context result flags (there is redis on the context path? check `/plays-views` before assuming a fresh deploy reads through).
- `libs-server/film/film-browse.mjs` — drive EPA, first downs and snaps; `app/views/components/film-play-browser/film-drive-chart.js` + `.styl` — the drive chart.
- `app/styles/palette.styl`, `STYLE.md`, `test/app.film-browse.spec.mjs`, `test/app.film-keys.spec.mjs`.
- Deploy runbook: `docs/guides/ship.md`.
