# Custom Player Controls

The plugin uses a fully custom branded video player — no third-party player libraries. The player is implemented twice: once in vanilla DOM (`view.js` for the frontend) and once in React (`video-preview.js` for the block editor). Both share the same CSS classes from `style.css`.

## Brand Colors

- Teal: `#19bad1` (CSS variable `--muvi-teal`) — progress gradient start, focus rings, hover accents
- Magenta: `#d721b4` (CSS variable `--muvi-magenta`) — progress gradient end, handle, subscribe buttons, active states

## Player States

The player cycles through states managed via CSS classes on `.muvi-container`:

| State | Class | UI |
|---|---|---|
| `initial` | `muvi-container--initial` | Poster + play icon + "Click to play" |
| `playing` | `muvi-container--playing` | Video playing, controls auto-hide after 3.5s |
| `paused` | `muvi-container--paused` | Semi-transparent play icon overlay, controls visible |
| `ended` | `muvi-container--ended` | Replay icon + "Replay" text, controls visible |
| `unauthorized` | `muvi-container--unauthorized` | Poster + amber warning triangle, no controls |

## Control Bar Elements

Left to right: Play/Pause button, progress bar, time display, captions toggle, volume (with hover-expand slider), fullscreen, kebab menu.

### Progress Bar

The gradient uses `clip-path: inset()` to reveal the played portion. This preserves the full gradient appearance (teal-to-magenta across the entire bar width) rather than a width-based approach which would compress the gradient into the played portion.

```css
.muvi-progress-played {
    width: 100%;
    background: linear-gradient(90deg, var(--muvi-teal), var(--muvi-magenta));
    clip-path: inset(0 <remaining>% 0 0);
}
```

### Auto-Hide (Frontend Only)

On non-touch devices, the control bar hides after `AUTOHIDE_DELAY` (3500ms) of no mouse movement during playback. On touch devices, tapping the video toggles visibility. The editor keeps controls always visible via:

```css
.muvi-container--editor .muvi-controls { opacity: 1 !important; }
```

### Captions

- WebVTT files loaded from `assets.toothority.com/vtt/{topic}/en.vtt`
- User preference persisted to `localStorage` key `muvi-captions`
- Active state indicated by teal color on the CC button

### Kebab Menu

Small popup above the kebab icon with two items:
- "Toothority WordPress Plugin" — links to wordpress.org plugin page
- Version number (e.g. "v3.0.0")

### Custom Context Menu

Right-clicking the video shows a custom context menu (replacing the browser default) with the same two items as the kebab menu. Positioned at click coordinates, dismisses on click or subsequent right-click.

## Fullscreen

Uses the native video element's fullscreen API (not container fullscreen). Temporarily enables native controls while in fullscreen, then removes them on exit. Safari fallback uses `webkitEnterFullscreen()`.

## Editor Integration Gotchas

### Pointer Capture for Drag-to-Seek

The block editor intercepts pointer events for block dragging/selection. The progress bar uses `setPointerCapture()` on `pointerdown` to force all subsequent pointer events to route directly to the bar element, bypassing the editor's interception.

### Stop Propagation on Controls

The control bar uses `onPointerDown={ (e) => e.stopPropagation() }` to prevent clicks on controls from triggering block selection.

### HLS in Editor

The editor preview (`video-preview.js`) imports `hls.js` directly. A `useEffect` hook manages the HLS instance lifecycle:
- **Authorized sites**: Sets up HLS streaming (same endpoint as frontend), with proper cleanup via `hls.destroy()` on topic change or unmount
- **Unauthorized sites**: Falls back to sample MP4 from `assets.toothority.com/samples/`

The video `src` is set imperatively in the effect (not via JSX attribute) to allow HLS.js attachment.

### Picker Modal Auto-Open

When the block is first added (no topic selected), the picker modal opens immediately. The initial state is `useState(!hasVideo)`. Existing blocks with a topic already set open with the picker closed.

## Overlay Design

All overlays share the base `.muvi-overlay` class (absolute positioned, flexbox centered, fade transition). Variants:

- **Initial**: Dark scrim (0.35 opacity), 80px play icon with white border, "Click to play" text, scale-on-hover
- **Paused**: Lighter scrim (0.3 opacity), 64px frosted-glass play icon
- **Ended**: Darker scrim (0.55 opacity), 64px replay icon with white border, "Replay" text
- **Unauthorized**: Dark scrim (0.55 opacity), 80px amber warning icon (`#f5a623`), no interactivity (`cursor: default`)
