diff --git a/internal/design/ui/live-presets.md b/internal/design/ui/live-presets.md
new file mode 100644
index 000000000..a5aaa2a60
--- /dev/null
+++ b/internal/design/ui/live-presets.md
@@ -0,0 +1,222 @@
+---
+status: decided
+date: 2026-04-21
+---
+
+# Live Presets
+
+Dedicated `live-video` and `live-audio` presets for **live-only** playback, alongside the existing `video` and `audio` presets for on-demand-only playback.
+
+## Decision
+
+Ship live playback as **separate, live-only presets** — `live-video` and `live-audio`. Each pairs a live-capable feature set with a skin that renders live UI unconditionally. We do **not**:
+
+- Add live-capable state or UI to the existing `video` / `audio` presets.
+- Branch the live skins on `streamType` to cover VOD inside the same preset.
+
+Two presets, two jobs: base presets play VOD, live presets play live. A preset is a commitment to one mode, not a runtime toggle between both. If a live preset ever needs to cover VOD sources too, we can layer that in later — but we're not paying the complexity up front before we've felt the friction.
+
+## Context
+
+Live playback needs visibly different UI from VOD:
+
+- The duration display and standard scrubbing affordances disappear or change
+- A live indicator / "jump to live edge" button takes their place
+- The time slider, if shown at all, represents a DVR window, not `[0, duration]`
+- Live-only affordances are likely to grow (latency badge, "behind live" state, chat slot, etc.)
+
+`streamTypeFeature` exposes `streamType: 'on-demand' | 'live' | 'unknown'` and could drive this at runtime inside one shared skin. We're choosing not to.
+
+Authors almost always know at embed time whether their product plays live or VOD — an event page, a channel page, a DVR UI. "Live-aware" and "VOD-only" are two meaningfully different products, and the vast majority of `video` / `audio` integrations are VOD-only.
+
+This doc records two decisions, with separate rationales below:
+
+1. **Split** — live and VOD ship as separate presets.
+2. **Live-only** — the live preset's skin renders live UI unconditionally, with no in-skin VOD fallback.
+
+## Shape
+
+### Feature arrays
+
+Base presets stay as today. Live presets start close in capability and will diverge further as live-only needs emerge. `playbackRateFeature` is dropped — speed controls aren't meaningful for live playback, where the user is always pinned to (or chasing) the live edge.
+
+```ts
+// packages/core/src/dom/store/features/presets.ts
+export const videoFeatures: VideoFeatures = [
+ playbackFeature,
+ playbackRateFeature,
+ volumeFeature,
+ timeFeature,
+ sourceFeature,
+ bufferFeature,
+ fullscreenFeature,
+ pipFeature,
+ remotePlaybackFeature,
+ controlsFeature,
+ textTrackFeature,
+ errorFeature,
+];
+
+export const liveVideoFeatures: VideoFeatures = [
+ playbackFeature,
+ volumeFeature,
+ timeFeature,
+ sourceFeature,
+ bufferFeature,
+ fullscreenFeature,
+ pipFeature,
+ remotePlaybackFeature,
+ controlsFeature,
+ textTrackFeature,
+ errorFeature,
+];
+
+export const audioFeatures: AudioFeatures = [
+ playbackFeature,
+ playbackRateFeature,
+ volumeFeature,
+ timeFeature,
+ sourceFeature,
+ bufferFeature,
+ errorFeature,
+];
+
+export const liveAudioFeatures: AudioFeatures = [
+ playbackFeature,
+ volumeFeature,
+ timeFeature,
+ sourceFeature,
+ bufferFeature,
+ errorFeature,
+];
+```
+
+`streamTypeFeature` is intentionally **not** included. The live skin assumes live, so it doesn't need runtime detection. When a live-only affordance actually needs stream-type state (e.g., surface an error if a VOD source is loaded, or drive a "behind live" badge derived from seekable + streamType), it's an additive, non-breaking change to append it to `liveVideoFeatures` / `liveAudioFeatures`.
+
+No live-specific time feature either. `timeFeature` already resolves `duration` to `seekable.end(last)` when the native duration is `Infinity`, which is all the live skin needs for now. If `currentTime` or other time values need live-specific handling later, that's also additive.
+
+Beyond `playbackRateFeature`, the live feature arrays start as near-copies of their VOD counterparts. That's a conservative starting point, not a commitment that live needs every remaining feature. Further bundle wins for live-only are expected to come primarily from the skin (smaller DOM, fewer subscribers — see below). Trimming additional features from the live arrays is a follow-up once the skins land and we can measure.
+
+### Preset packages
+
+```ts
+// packages/react/src/presets/live-video/index.ts
+export { liveVideoFeatures } from '@videojs/core/dom';
+export { LiveVideo, type LiveVideoProps } from '@/media/live-video';
+export * from './skin';
+export * from './skin.tailwind';
+export * from './minimal-skin';
+export * from './minimal-skin.tailwind';
+```
+
+```ts
+// packages/html/src/presets/live-video.ts
+export { liveVideoFeatures } from '@videojs/core/dom';
+export { LiveVideoSkinElement } from '../define/live-video/skin';
+export { LiveVideoSkinTailwindElement } from '../define/live-video/skin.tailwind';
+export { MinimalLiveVideoSkinElement } from '../define/live-video/minimal-skin';
+export { MinimalLiveVideoSkinTailwindElement } from '../define/live-video/minimal-skin.tailwind';
+```
+
+`live-audio` mirrors this.
+
+### Live skin is live-only
+
+The live skin renders live controls unconditionally — a live indicator, a "jump to live edge" button, and (where applicable) a DVR-window slider derived from `seekable`. It does not read `streamType`, and it does not attempt to recover to a VOD layout when the source turns out to be on-demand:
+
+```tsx
+// packages/react/src/presets/live-video/skin.tsx
+
+
+
+
+```
+
+Pointing a `live-video` preset at a VOD source produces the live skin on top of a VOD stream — working playback, but with live affordances that don't match the content. That's an intentional degradation, not a supported configuration — the mirror of pointing `video` at a live source.
+
+### Base skin is VOD-only
+
+`VideoSkin` / `AudioSkin` never read `streamType` (and can't — the slice isn't in the store). They unconditionally render the on-demand time region. Pointing a `video` preset at a live source produces working playback with a duration of `Infinity` (or `seekable.end(last)` once `timeFeature` resolves it) and the standard scrubber on the seekable window, with no live-edge affordance. That's an intentional degradation, not a supported configuration — authors who expect live sources use a live preset.
+
+## Alternatives Considered
+
+- **Live skin branches on `streamType` to cover VOD too.** Ship `liveVideoFeatures = [...videoFeatures, streamTypeFeature]` and render live vs. on-demand time controls inside the live skin based on detection, with a neutral placeholder while `streamType === 'unknown'`. Supports runtime source switches between live and VOD within one preset.
+- **Branch the existing `video` / `audio` skins on `streamType`.** One preset per medium, one skin that covers both modes by reading `streamType`. Pattern already used for `volumeAvailability === 'unsupported'` (see `VolumePopover` in `packages/react/src/presets/video/skin.tsx:60`).
+- **Single preset, runtime-selected skin.** Keep one `video` / `audio` preset but have it mount a different skin tree based on `streamType`. Hides the split from the author but retains the unknown-state and dead-code problems.
+
+## Rationale: Split live from VOD
+
+The dominant case is VOD — the vast majority of `video` / `audio` integrations never play live. Every byte the VOD preset carries for a capability it doesn't use is a byte the dominant case pays for something it doesn't get.
+
+A shared `video` preset covering both modes forces every VOD player to ship:
+
+- The `streamType` state slice and the events, subscribers, and predicate helpers that maintain it.
+- The live UI tree — live indicator, jump-to-live-edge button, DVR slider — rendered conditionally but present in the bundle.
+- The branching logic itself (detection, unknown-state handling, transitions).
+
+A separate `live-video` preset keeps that cost with the apps that actually use live. The VOD store has no `streamType` slice; `VideoSkin` has no live branches; nothing in the default import path references live code. The bundle-size report already separates `/video/skin` from a future `/live-video/skin` entry — the split is visible and measurable.
+
+Secondary wins:
+
+- **Explicit opt-in matches author intent.** Picking `live-video` is a one-word signal that live is the supported path — clearer than "use `video` and set some prop" and more discoverable than "`video` happens to work if the source is live."
+- **Smaller type surface in the common store.** `VideoPlayerStore` / `AudioPlayerStore` keep their current state shape. Adding `streamType` to the base ripples into every consumer of those types.
+- **Targetable visual regression tests.** Each skin snapshots independently; the VOD snapshot stays stable without a `streamType` harness.
+
+## Rationale: Live preset renders live UI only
+
+Given we're shipping a dedicated live preset, it could still support VOD internally — branch the live skin on `streamType` and fall back to VOD controls when the source isn't live. We're not doing that either.
+
+1. **Smaller live bundle.** Live-only (especially non-DVR) unlocks a meaningfully smaller UI: no time slider, no thumbnail previews, no seek buttons, no remaining-time display. A dual-mode live skin has to ship all of those for the VOD branch, plus the branching. Live-only gets to be genuinely smaller, not just differently shaped.
+
+2. **No unknown-state flash.** A dual-mode live skin starts every load in `streamType === 'unknown'` and resolves to `live` (or `on-demand`) after manifest detection. Either we render a neutral placeholder during that window — a third UI state to design and test — or we pick a default and flip visibly once detection completes. A live-only skin has one shape from first paint.
+
+3. **Fork-template clarity.** Skins are reference implementations authors copy and mutate. A live-only skin gives live-app authors a starting point with only live concepts in the tree — no conditional helpers, no `unknown` placeholder, no dead VOD branches to delete. VOD authors forking `VideoSkin` get the same treatment from the other side.
+
+4. **Headroom for divergence.** Live UI tends to accumulate bespoke affordances — latency indicator, "behind live" badge, DVR-aware scrubber interactions, live chat/reactions slot. Each lands naturally inside `LiveVideoSkin` without leaking concepts into the dual-mode branching.
+
+5. **Easy to add, hard to remove.** If we later find authors commonly need one preset that handles live → VOD replay or cross-mode source swaps, we can add `streamTypeFeature` and branch the skin — additive. Shipping a dual-mode skin and later deciding the unknown-state handling and dead code aren't worth it is a breaking change for anyone on the VOD path.
+
+### What we give up
+
+- **Runtime mode switching.** Neither preset adapts if its source's stream type changes. A live preset pointed at a VOD replay shows live UI over a VOD stream; a VOD preset pointed at a live source shows VOD UI over a live stream. Authors needing both modes in one player either wait for a future iteration or compose their own.
+- **A story for live replay / DVR archive flows.** Some products want a single embed that starts live and keeps playing after the stream ends, or that plays back a recorded segment inside the same UI as the live stream. Deferred until a concrete product need surfaces.
+
+### Trade-offs
+
+| Gain | Cost |
+| -------------------------------------------------- | ------------------------------------------------------ |
+| VOD-only apps ship no stream-type state or live UI | Source-type switches at runtime aren't covered |
+| Live-only apps ship a smaller live-only UI | Live replay / post-stream VOD flows aren't served yet |
+| First paint has a single, correct UI shape | Two more React skins + four HTML skin elements to ship |
+| Easy to layer dual-mode support on later if needed | Shared skin idioms must be kept in sync across trees |
+| Room to add live-only affordances without churn | Authors must know their mode at preset-selection time |
+
+## Consequences
+
+- In `@videojs/core/dom`:
+ - Add `liveVideoFeatures` and `liveAudioFeatures` exports in `packages/core/src/dom/store/features/presets.ts`. Initially they match `videoFeatures` / `audioFeatures` minus `playbackRateFeature`, and without `streamTypeFeature`.
+ - `streamTypeFeature` stays a standalone export for custom compositions; it's not wired into any preset feature array.
+ - No change to `VideoFeatures` / `AudioFeatures` or the derived store types.
+- In `@videojs/react`:
+ - Add `packages/react/src/presets/live-video/` with `skin.tsx`, `skin.tailwind.tsx`, `minimal-skin.tsx`, `minimal-skin.tailwind.tsx`.
+ - Add `packages/react/src/presets/live-audio/` mirroring the above.
+ - Live skins render live controls unconditionally; they do not import `selectStreamType` and do not branch on stream type.
+- In `@videojs/html`:
+ - Add `packages/html/src/presets/live-video.ts` and `packages/html/src/presets/live-audio.ts`, plus the corresponding `define/live-video/*` and `define/live-audio/*` custom element wrappers.
+- Site API reference pages for the new presets; update the presets overview to list live variants and clarify that the base presets are VOD-only and the live presets are live-only.
+- `selectStreamType` remains a core export; its existing contract (return `undefined` when the feature isn't configured) covers both presets naturally — neither preset configures `streamTypeFeature` today, so callers get `undefined`.
+
+## Open Questions
+
+1. **When to add `streamTypeFeature` to the live feature arrays.** What signal tells us we're paying for live-only's minimalism — authors writing the same "is this VOD?" guard themselves, Mux-specific mis-configurations we want to surface as an error, or something else? Worth revisiting after the first few live integrations.
+2. **Further trimming the live feature array.** `playbackRateFeature` is dropped on day one. The clearest next candidate is splitting `textTrackFeature` — it bundles captions (needed for live) with chapters and thumbnail cues (VOD-only). A `captionsFeature` / `chaptersFeature` / `thumbnailsFeature` split lets live take captions only and drop the cue iteration and `