The surface
Every market with a provider id has exactly one canonical surface:

/m/[marketId] — the tape on air, the price blocks (the SIGNAL controls), the arc, the event’s other markets.
https://denpa.ai/m/[marketId] e.g. https://denpa.ai/m/559652The surface composes, in one screen:
- market data — question, prices, volume, resolution state (server-rendered from the provider)
- bound streams — live HLS context attached by operators
- signal — the price blocks are the SIGNAL buttons; viewers act on the market without leaving
- crowd — MARKET vs CROWD vs YOU, bots excluded
- related markets — the rest of the event, volume-ranked
- on-air state — what the program clock is airing and what is up next
- receipts — who broadcast on it, when, to how many
- A surface MUST render a live state, an empty state and an error state.
- A surface MUST show a price only when one exists. Unknown is rendered as
--, never as a placeholder value. - Bindings MUST be HLS. Playback prefers hls.js before native HLS.
- Filing a signal without identity prompts sign-in; it never opens the provider in a new tab.
Interface
Section titled “Interface”interface Surface { market(): MarketContext // question · prices · volume · endDate · resolved streams(): Binding[] // { handle, url (HLS), label, updatedAt } bind(url, label?) // identity required; handle resolved server-side unbind() // the caller's own binding only signals(): Signal[] // canonical: one per operator per market signal(side, confidence?, take?) // writes Signal + Snapshot atomically crowd(): { market, crowd, you } related(): MarketRef[] onAir(): Segment | null receipts(): Receipt[]}The home surface, denpa.ai, is a heatmap of top and resolving-24h markets plus pinned live operator tiles. Everything routes to market pages.