Media inventory
Every screenshot, video and diagram the documentation calls for, ordered by priority rather than by filename.
59 placeholders: 58 screenshots, 1 video.
Each one appears inline in the docs as a greppable comment:
<!-- MEDIA: screenshot | id: example-placeholder-id | description -->To find one in the source:
grep -rn "id: install-snippet-copy" docs/To find everything still outstanding:
grep -rn "MEDIA:" docs/Capture conventions
Apply to everything unless a row says otherwise.
| Viewport | 1440 × 900 for full-page app screenshots. Crop tightly for single cards, dialogs and DevTools panels. |
| Theme | Light, unless the row says dark. Two rows deliberately want dark, to check contrast. |
| Data | Realistic and non-round. 12,483 not 10,000. Vary statuses, dates and magnitudes so a table tells a story. Never Test 1 / Test 2, never Lorem ipsum. |
| Names | example.com for the tracked domain. Real-looking referrers (news.ycombinator.com, reddit.com) are fine. No invented person names in testimonial style. |
| Site key | A plausible fake: ts_a1b2c3d4e5f6. Never a real one. |
| API key | Always visibly fake. Never a real ts_live_ value, even revoked. |
| Chrome | Hide browser bookmarks bars and unrelated extensions. Nothing personal in a tab title. |
| Redaction | No real email addresses, no real customer domains, no real invoice figures. |
| Format | PNG at 2× device pixel ratio. Video as MP4, H.264, 1440 × 900, no audio. |
Priority 1 — blocking getting-started and install
These are the pages a new customer reads in their first five minutes. Nothing else matters until these exist.
1a. The first-run flow
| # | id | Type | Appears in | Must show | Capture notes |
| 1 | install-snippet-copy | screenshot | getting-started/install-the-script.md | The install page with the snippet rendered, real site key visible, copy button | Platform tab strip above it, "Plain HTML" selected. 1440px, light. The single most reused image — get this one right first. |
| 2 | first-event-connect | video | getting-started/see-your-first-visit.md | Install page in "Waiting", snippet pasted into a page in a second tab, page loaded, cut back, page flips to "Connected" with a timestamp | ~20s screen capture, no audio, 1440 × 900, light. The one video in the set. |
| 3 | install-waiting-state | screenshot | getting-started/see-your-first-visit.md | Install page in "Waiting for your first event", with the snippet above it and the polling indicator | Light. Pairs with #4 — same page, same scroll position, same crop. |
| 4 | install-connected-state | screenshot | getting-started/see-your-first-visit.md | The same page after the first event: "Connected", the first-event time, the dashboard link | Light. Must be framed identically to #3 so the two read as before/after. |
| 5 | gs-add-site-form | screenshot | getting-started/add-your-first-site.md | "Add a site" form filled in: domain example.com, a name, timezone select open | 1440px, light. |
| 6 | verify-network-tab | screenshot | getting-started/see-your-first-visit.md | DevTools Network: t.js (200) and POST /api/events (202) with body {"ok":true} | Dark DevTools. Crop to the two rows and the status column. |
| 7 | dashboard-full-populated | screenshot | getting-started/what-to-look-at-first.md | The whole dashboard scrolled to top: four tiles, traffic chart with the AI crawler series visible, top of the geo/tech row | ~1 month of realistic traffic. 12,483 views / 3,912 visitors or similar. 1440px, light. |
| 8 | dashboard-drilldown-filter | screenshot | getting-started/what-to-look-at-first.md | Dashboard after clicking a referrer row: Filters button with a count badge of 1, popover open listing the filter | Light. |
| 9 | gs-site-quota-refused | screenshot | getting-started/add-your-first-site.md | "Add a site" refused on Starter with one site already: the site-limit message and the upgrade link | Light. Needs a Starter account at its limit. |
1b. The install-page snippet tabs
Six shots of the same screen with a different platform tab selected. Capture them in one sitting — same account, same site, same key, same crop — so they read as one set.
| # | id | Type | Appears in | Must show |
| 10 | install-snippet-plain-html | screenshot | install/plain-html.md | "Plain HTML" tab selected, snippet with real site key, copy button |
| 11 | install-snippet-nextjs | screenshot | install/nextjs.md | "Next.js" tab, the App Router variant |
| 12 | install-snippet-wordpress | screenshot | install/wordpress.md | "WordPress" tab |
| 13 | install-snippet-webflow | screenshot | install/webflow.md | "Webflow" tab |
| 14 | install-snippet-framer | screenshot | install/framer.md | "Framer" tab |
| 15 | install-snippet-gtm | screenshot | install/google-tag-manager.md | "Google Tag Manager" tab — the DOM-construction variant, visibly different from the plain snippet |
| 16 | install-snippet-ai-builders | screenshot | install/ai-builders.md | "AI builders" tab — the paste-into-chat prompt with the real key substituted |
All: 1440px, light.
1c. Verification
| # | id | Type | Appears in | Must show | Capture notes |
| 17 | install-connected-with-timestamp | screenshot | install/verifying-it-works.md | Install page in Connected state with the first-event time and the dashboard link | May be the same capture as #4 if the framing suits both. |
| 18 | verify-network-202 | screenshot | install/verifying-it-works.md | Network tab filtered to /api/events: status 202, Response panel showing {"ok":true} | Crop tight to the request row plus the response body. |
Priority 2 — third-party platform UIs
Screens inside someone else's product. Each needs a real account on that platform, which makes them the slowest to produce — start acquiring accounts early even though they rank second.
Keep each platform's own theme; do not force light on a product whose UI is dark.
| # | id | Type | Appears in | Must show | Capture notes |
| 19 | webflow-custom-code-head | screenshot | install/webflow.md | Webflow Site settings → Custom code → Head code, snippet pasted, Save changes button visible | Webflow's dark UI. Site settings, not page settings — the page's whole warning turns on that. |
| 20 | wordpress-theme-file-editor | screenshot | install/wordpress.md | WordPress Theme File Editor, header.php open, snippet directly above </head>, highlighted | WP admin light. |
| 21 | gtm-custom-html-tag | screenshot | install/google-tag-manager.md | GTM tag editor: Custom HTML tag with the snippet, trigger "All Pages" | GTM light. The trigger must be legible — it is the point of the shot. |
| 22 | framer-custom-code-head | screenshot | install/framer.md | Framer Site settings → Custom code, snippet in "Start of <head> tag" | Framer's dark UI. |
| 23 | squarespace-code-injection | screenshot | install/head-field-platforms.md | Squarespace Settings → Advanced → Code Injection, snippet in Header | Squarespace light. Needs a Business plan for the field to exist. |
| 24 | wix-custom-code-settings | screenshot | install/head-field-platforms.md | Wix Tracking & Analytics custom code dialog: snippet pasted, "All pages", "Head" | Wix light. Needs a Premium plan. Both selectors must be legible. |
| 25 | ghost-code-injection | screenshot | install/head-field-platforms.md | Ghost Settings → Code injection, snippet in Site header | Ghost admin. |
| 26 | webflow-custom-attribute-goal | screenshot | install/webflow.md | Webflow Designer element settings: custom attribute data-ts-goal = signup on a button | Webflow dark. |
Priority 3 — the dashboard
One populated test site can produce almost all of these in a single session. Seed it with about a month of varied traffic — several countries, several referrers, a mix of browsers, and a decent spread of crawler families — then capture in one pass so the numbers agree across every shot.
| # | id | Type | Appears in | Must show | Capture notes |
| 27 | dashboard-four-tiles | screenshot | dashboard/metrics.md | The four summary tiles: Views, Unique visitors, AI crawler views, Verified crawlers | e.g. 12,483 / 3,912 / 1,247 / 62%. Crop to the tile row. Note: verification is not running yet, so a live capture shows 0%. Either capture against seeded data with verified rows, or hold this until IP verification ships. |
| 28 | dashboard-breakdown-cards | screenshot | dashboard/breakdowns.md | Geo and tech cards side by side, internal tabs visible, populated | 1440px, light. |
| 29 | dashboard-filters-popover | screenshot | dashboard/breakdowns.md | Filters popover open with two active filters (Country: Germany, Browser: Firefox), remove buttons, "Clear all" | Light. |
| 30 | dashboard-range-picker-open | screenshot | dashboard/date-ranges.md | Range picker open: full preset list on the left, two-month calendar on the right | 1440px, light. |
| 31 | dashboard-channels-card | screenshot | dashboard/channels.md | Traffic sources → Channels: a spread across Direct, Organic Search, Referral, Social, AI, Campaign | Light. The AI channel row must be present and visible — it is the differentiated one. |
| 32 | dashboard-world-map | screenshot | dashboard/world-map.md | World map populated across several continents, hover tooltip on one country showing name and count | Light, desktop. |
| 33 | dashboard-world-map-dark | screenshot | dashboard/world-map.md | The same map in dark theme | Dark. Deliberate — checks the country fill scale reads against a dark ground. |
| 34 | dashboard-live-now | screenshot | dashboard/live-view.md | "Now" preset selected: minute buckets over 30 minutes, "updated Xs ago" beside the refresh button | Light. Needs live traffic during capture. |
| 35 | dashboard-ai-human-split | screenshot | dashboard/metrics.md | The AI-vs-human split stacked bar, several crawler families plus a human segment | Light, cropped to the card. |
| 36 | site-settings-exclusions | screenshot | dashboard/excluding-your-own-visits.md | Exclusions panel: excluded paths with a few glob patterns, excluded IPs with one address stored | Light. Use a private-range IP; nothing real. |
Priority 4 — the AI panel
The product's differentiator. Same seeded site as Priority 3, with a realistic crawler mix.
| # | id | Type | Appears in | Must show | Capture notes |
| 37 | ai-panel-full | screenshot | ai-visibility/what-this-panel-shows.md | The AI card, Crawlers tab, spread across operators | e.g. OpenAI 612 / Anthropic 341 / Google 188 / Perplexity 74. 1440px, light. |
| 38 | ai-pages-read-tab | screenshot | ai-visibility/ai-traffic-by-page.md | AI card, "Pages read" tab: ranked paths with crawler view counts | Use a docs or blog site so the paths are recognisable and the distribution is uneven. Light. |
| 39 | chart-humans-vs-crawlers | screenshot | ai-visibility/humans-vs-ai.md | The main traffic chart with both series and the full legend | Pick a range where the crawler series has visible shape, not a flat line. Light. |
| 40 | ai-human-split-bar | screenshot | ai-visibility/humans-vs-ai.md | The split stacked bar with named crawler families labelled | Light, cropped. May be the same capture as #35 if the labels are legible in both. |
Priority 5 — troubleshooting and error states
Every one of these is a failure state, so each has to be produced deliberately against a test site. Several cannot be captured by chance.
| # | id | Type | Appears in | Must show | Capture notes |
| 41 | troubleshoot-network-check | screenshot | troubleshooting/no-events.md | Network tab on a working install: t.js 200 and /api/events 202 | The reference "this is what right looks like". Crop tight. |
| 42 | csp-console-error | screenshot | script/content-security-policy.md | Console showing the CSP violation for connect-src blocking /api/events | Dark DevTools, cropped to the message. Set up a test page with script-src allowed and connect-src not — that exact asymmetry is the point. |
| 43 | adblock-blocked-request | screenshot | troubleshooting/ad-blockers.md | Network tab with t.js blocked/failed and an empty Console | Both panels visible. The pairing is the diagnostic — a blocker prints nothing where a CSP prints a message. |
| 44 | duplicate-events-network | screenshot | troubleshooting/duplicate-events.md | Two t.js requests and two /api/events POSTs from one page load | Deliberately double-install a test page. |
| 45 | custom-event-console-error | screenshot | script/custom-events.md | Console error naming a rejected goal — e.g. Signup refused for the capital letter | Dark DevTools, cropped. Fire a deliberately invalid event. |
| 46 | proxy-broken-ip-symptom | screenshot | script/proxying.md | The dashboard under a misconfigured proxy: unique visitors at 1–2 against thousands of views, world map showing one country | Annotate with a callout arrow. A "what wrong looks like" figure — build a proxy that drops the IP against a test site to produce it. |
| 47 | identity-mode-toggle | screenshot | script/cookieless-mode.md | Site settings identity control: all three modes, first-party cookie selected as default, accuracy note rendered beside it | Light. The accuracy note must be legible — the rule is that the honest limit sits next to the choice, and this image is the evidence. |
| 48 | site-delete-confirmation | screenshot | privacy/deleting-your-data.md | Delete-site dialog: type-the-domain field and the warning about what is removed | Light. Use a throwaway site. |
Priority 6 — features not yet built
Do not attempt these until the feature ships. Each is listed against the page that will need it, so nothing is forgotten at launch.
Sharing
| # | id | Type | Appears in | Must show |
| 49 | sharing-create-link | screenshot | sharing/creating-a-share-link.md | The Sharing panel: created link with copy button, section toggles, range cap, password field, expiry |
| 50 | shared-dashboard-view | screenshot | sharing/what-a-viewer-sees.md | A share link opened logged out: read-only dashboard, no sidebar, no settings, no account menu |
#50 is the important one — the page's whole promise is "here is exactly what your viewer sees", so it must be captured in a genuinely signed-out browser rather than approximated.
Team
| # | id | Type | Appears in | Must show |
| 51 | team-invite-dialog | screenshot | team/inviting-someone.md | Invite dialog with an email entered and the role selector open showing all four roles |
| 52 | team-seat-limit-refused | screenshot | team/seat-limits.md | Invite refused on Starter, with the seat allowance and the upgrade link |
API
| # | id | Type | Appears in | Must show | Capture notes |
| 53 | api-key-created-once | screenshot | api/getting-an-api-key.md | Key creation result: the full plaintext key, copy button, prominent shown-once warning | A clearly fake key. Never a real ts_live_, even a revoked one. |
Billing
| # | id | Type | Appears in | Must show | Capture notes |
| 54 | pricing-slider-two-tiers | screenshot | billing/plans-compared.md | Pricing page: slider at 200k, both tier cards at that step, monthly/yearly toggle | The pricing page may exist before the billing screens do — check, this may be capturable now. |
| 55 | billing-usage-meter | screenshot | billing/what-counts-as-an-event.md | Usage meter: events used against the limit, ~70% consumed, period end date | |
| 56 | over-quota-banner | screenshot | billing/at-your-limit.md | Dashboard with the over-quota banner: usage against limit, add-on and upgrade options | Realistic overage, e.g. 214,802 of 200,000. Needs an account deliberately pushed over. |
| 57 | billing-payment-method | screenshot | billing/invoices.md | Payment method section: saved card brand and last four, update button | A Stripe test card. Never real card data. |
| 58 | goals-card | screenshot | script/custom-events.md | The Goals card: three or four goal names with visitor counts, at least one showing a differing completions line beneath it | The differing row is the point — it is what shows the two numbers are not the same measurement. Light theme, cropped to the card. |
| 59 | goals-filter-applied | screenshot | script/custom-events.md | The dashboard with a goal filter active: filter chip at the top, country card narrowed to the visitors who triggered that goal | Shows the visitor-scoped filter — the country card must still show pageview-derived rows, not just the goal rows. Light theme, top half. |
Batching plan
Roughly how to sequence a capture session.
| Batch | What | Needs |
| A | 1–9, 17–18 | One fresh account, one new site, one test page to paste into. Do the flow once, capturing as you go. Yields the whole first-run story including the video. |
| B | 10–16 | The same account, cycling the install page's platform tabs. Fast — one screen, seven states. |
| C | 19–26 | Accounts on Webflow, WordPress, GTM, Framer, Squarespace (Business), Wix (Premium), Ghost. The long pole. Start acquiring these while doing Batch A. |
| D | 27–40 | One test site seeded with ~1 month of varied human and crawler traffic. Capture the whole dashboard and AI panel in one pass so numbers agree between shots. |
| E | 41–48 | Deliberate failures: a CSP page, an ad blocker, a double install, an invalid event, a proxy that drops the IP. Each is a small setup. |
| F | 49–57 | After the corresponding features ship. |
Blockers worth knowing before you start
#27 dashboard-four-tiles and #37/#39 (the AI panel) — IP verification is not
running yet, so a live capture shows "Verified crawlers" as 0%. Either seed
verified rows, or accept the 0% and re-capture when verification ships. Do not
retouch the number.
#54–57 (billing) — the billing screens are not built. The pricing page may be; check before deferring #54.
#49–53 (sharing, team, API) — those screens do not exist yet.
#46 proxy-broken-ip-symptom — this one has to be manufactured. Stand up a
proxy that deliberately drops the client IP, point a test site at it, generate
traffic from a few different addresses, and capture the collapse. It is the most
effort of any single image here and it is also the one that stops a customer
destroying their own visitor counts.