PR #12987: the prospects list's first paint

PR #12987 at 3e5e51d478. Local dev server against the stage table (read-only), staff session with one company picked, default 60-day window. Table cells are blurred in every capture (stage mirrors carry real names); the company rail is cropped out.

This PR moves when the rows appear. It does not reduce total database work: the tile counts still need the whole-window walk, now on the page's first refresh instead of the first paint. Reducing that load is the counts-source PR (#12994).

Three captures, each asserted in the page text: a large company's first paint (rows filled, tiles "—"), the same page after its counts land, and a small company counted from its own rows.

The three captures

1. Large company (13,217 leads in the window), first paint

Prospects page: tiles show dashes, table rows are filled

Asserted in the DOM at the moment the first real row appeared (2.3 s after navigation): 50 real rows on screen; Inquiries tile reads "—"; Tours tile reads "—". The tiles never show a count of the 100-row page.

2. Same page, after the counts poll lands

Prospects page: tiles show 13217 inquiries and 5 tours; the toolbar says showing the 100 most recently active of 13,217

Asserted at 13.1 s after navigation: Inquiries reads 13217, Tours reads 5, and the rows are unchanged (50). The toolbar says the table holds the 100 most recently active of 13,217.

3. Small company (85 leads in the window): one page is the whole window

Prospects page: tiles show 85 inquiries, 41 tours, 20 applications, 10 signed at first paint

Asserted at the first real row (2.7 s): the cursor is null, so the page counts its own rows: Inquiries 85, Tours 41, count label "85 prospects". There is no dash state here.

Not shown

Before the counts land (capture 1), the toolbar's "100 prospects" is the size of the page, not of the window. Part 2 (paging the rest on screen) marks it as a lower bound. Dev-mode timings on a shared machine; they are not production numbers.

PropFlow Docs