# Assets and processing — AFTERHOURS

All five pieces of media were supplied for this concept. Nothing here was
sourced from stock libraries or from another restaurant, and none of it was
replaced with diagrams or placeholders. The originals are kept in
`assets/afterhours/` at the repository root, which Next does not serve.

| Original | Used as | Delivered |
|---|---|---|
| `grill-still.png` (1672×941) | Hero poster and the fallback when video cannot play | `img/grill-poster-{480,960,1600}.webp` |
| `grill-source.mp4` (1280×720, 24fps, 5.04s) | Hero background film | `media/grill.webm`, `media/grill.mp4` |
| `table-spread.png` (1672×941) | The pull-back reveal after the introduction | `img/table-{640,1100,1672}.webp` |
| `dining-room.png` (1672×941) | The room | `img/room-{640,1100,1672}.webp` |
| `old-fashioned.png` (1672×941) | At the bar | `img/bar-{640,1100,1672}.webp` |

Images: cropped to the widths above, re-encoded as WebP at q82 with sharp YUV.
The hero poster is preloaded with `imagesrcset`; everything below the first
screen is lazy.

## The film, and its loop

The supplied clip is a slow dolly: the steak drifts right and grows across the
five seconds, so the last frame does not resemble the first and a plain loop
cuts hard. Measured, frame 121 against frame 1 came out at RMSE 11807, where
consecutive frames in the same clip sit between 2018 and 3328.

It was rebuilt as a real loop rather than left to jump, and without reversing
fire or smoke. The last 16 frames are dissolved into the first 16, so the clip
is 105 frames and its final frame is the one immediately before the frame the
loop restarts on. Re-measured after the rebuild, the join is **RMSE 2838**,
inside the clip's own frame-to-frame range, so it is not a visible cut. The
dissolve itself blends two points of the camera move and reads as depth of
field rather than a double exposure.

The audio track was unused and has been removed: `grill.webm` and `grill.mp4`
carry video only.

## Bitrate

The first delivery ran the loop out at 2.23 Mbps H.264 and 1.47 Mbps VP9,
against a supplied clip carrying 4.41 Mbps. Roughly half the file was being
thrown away, and on firelit smoke — the hardest thing either codec has to
do — that is where it shows.

Rebuilt from the supplied clip in one pass: the loop is assembled on lossless
PNG and encoded once, instead of being encoded, read back and encoded again.
Measured against the source frames over output frames 20-95, which are plain
source frames with no dissolve in them:

| | bitrate | size | mean PSNR vs source |
|---|---|---|---|
| first delivery | 2.23 Mbps | 1.2 MB | 39.39 dB |
| crf 19 | 3.58 Mbps | 2.0 MB | 42.59 dB |
| **shipped, crf 14** | **6.13 Mbps** | **3.3 MB** | **45.33 dB** |

x264 `-crf 14 -preset veryslow -tune film`, VP9 `-crf 18 -cpu-used 0`. The knee
on the size/quality curve is 19, and that is what would ship if weight
mattered here. It does not, so this runs above the bitrate the source itself
carries and the encoder stops being a term in the result at all — the worst
frame measures 45.01 dB, against 38.05 in the first delivery.

Resolution is not fixable here. The clip is 1280x720 and the hero is
full-bleed, so on a 1440px retina viewport it is drawn into 3628 device pixels
— a 2.83x upscale, of which 1.26x is the deliberate `scale()` that lifts the
steak clear of the wordmark. Only a higher-resolution source would move that.

## Playback

Muted, `playsinline`, looping, with the still as the poster so something is on
screen before the film arrives. Playback pauses when the hero scrolls out of
view and when the tab is hidden. There is a labelled pause and play control,
and `prefers-reduced-motion` stops it playing at all and leaves the poster up.

The supplied still is close to the opening framing but not identical to frame 1
(RMSE 4690), so the flame changes shape slightly as the film takes over. That
is normal for a poster and was left alone.
