//#region ../store/dist/dev/core/abort-controller-registry.js
var AbortControllerRegistry = class {
	#base = new AbortController();
	#keys = /* @__PURE__ */ new Map();
	/** The attach-scoped signal. Aborts on detach or reattach. */
	get base() {
		return this.#base.signal;
	}
	/** Clears all keyed signals, leaving base intact. */
	clear() {
		for (const controller of this.#keys.values()) controller.abort();
		this.#keys.clear();
	}
	/** Resets base and clears all keyed signals. */
	reset() {
		this.clear();
		this.#base.abort();
		this.#base = new AbortController();
	}
	/** Creates a new signal for the key, superseding any previous signal. */
	supersede(key) {
		this.#keys.get(key)?.abort();
		const controller = new AbortController();
		this.#keys.set(key, controller);
		return AbortSignal.any([this.#base.signal, controller.signal]);
	}
};

//#endregion
//#region ../store/dist/dev/core/combine.js
/**
* Combines multiple slices into a single slice.
*
* @param slices - The slices to combine.
* @returns A new slice that represents the combination of the input slices.
*/
function combine(...slices) {
	return {
		state: (ctx) => {
			const states = slices.map((slice) => slice.state(ctx));
			return Object.assign({}, ...states);
		},
		attach: (ctx) => {
			for (const slice of slices) try {
				slice.attach?.(ctx);
			} catch (err) {
				ctx.reportError(err);
			}
		}
	};
}

//#endregion
//#region ../store/dist/dev/core/errors.js
var StoreError = class extends Error {
	code;
	cause;
	constructor(code, options) {
		super(options?.message ?? code);
		this.name = "StoreError";
		this.code = code;
		this.cause = options?.cause;
	}
};
function throwNoTargetError() {
	throw new StoreError("NO_TARGET");
}
function throwDestroyedError() {
	throw new StoreError("DESTROYED");
}

//#endregion
//#region ../utils/dist/predicate/predicate.js
function isFunction(value) {
	return typeof value === "function";
}
function isNull(value) {
	return value === null;
}
function isUndefined(value) {
	return typeof value === "undefined";
}
/**
* Check if a value is an object, excluding null.
*/
function isObject(value) {
	return value !== null && typeof value === "object";
}

//#endregion
//#region ../utils/dist/object/pick.js
/**
* Creates a new object with only the specified keys.
*
* @example
* const obj = { a: 1, b: 2, c: 3 };
* pick(obj, ['a', 'c']); // { a: 1, c: 3 }
*/
function pick(obj, keys) {
	const result = {};
	for (const key of keys) result[key] = obj[key];
	return result;
}

//#endregion
//#region ../store/dist/dev/core/selector.js
const stateContext = {
	target: throwNoTargetError,
	signals: new AbortControllerRegistry(),
	set: throwNoTargetError
};
/**
* Create a type-safe selector for a slice's state.
*
* The selector returns the slice's state, or `undefined` if the slice
* is not configured in the store.
*
* @example
* ```ts
* const selectPlayback = createSelector(playbackSlice);
* selectPlayback(store.state); // { paused, play, pause, ... } | undefined
* selectPlayback.displayName;  // 'playback' (from slice name)
* ```
*
* @param slice - The slice to create a selector for.
*/
function createSelector(slice) {
	const initialState = slice.state(stateContext);
	const keys = Object.keys(initialState);
	const firstKey = keys[0];
	if (!firstKey) return Object.assign(() => void 0, { displayName: slice.name });
	return Object.assign((state) => {
		if (!(firstKey in state)) return void 0;
		return pick(state, keys);
	}, { displayName: slice.name });
}

//#endregion
//#region ../store/dist/dev/core/shallow-equal.js
const hasOwn = Object.prototype.hasOwnProperty;
function shallowEqual(a, b) {
	if (Object.is(a, b)) return true;
	if (typeof a !== "object" || a === null || typeof b !== "object" || b === null) return false;
	const keysA = Object.keys(a);
	const keysB = Object.keys(b);
	if (keysA.length !== keysB.length) return false;
	for (const key of keysA) if (!hasOwn.call(b, key) || !Object.is(a[key], b[key])) return false;
	return true;
}

//#endregion
//#region ../store/dist/dev/core/slice.js
function defineSlice() {
	return (config) => config;
}

//#endregion
//#region ../utils/dist/function/noop.js
function noop(..._args) {}

//#endregion
//#region ../store/dist/dev/core/state.js
let isFlushScheduled = false;
function scheduleFlush() {
	if (isFlushScheduled) return;
	isFlushScheduled = true;
	queueMicrotask(flush);
}
const pendingContainers = /* @__PURE__ */ new Set();
function flush() {
	isFlushScheduled = false;
	for (const container of pendingContainers) container.flush();
	pendingContainers.clear();
}
const hasOwnProp = Object.prototype.hasOwnProperty;
var StateContainer = class {
	#current;
	#listeners = /* @__PURE__ */ new Set();
	#pending = false;
	constructor(initial) {
		this.#current = Object.freeze({ ...initial });
	}
	get current() {
		return this.#current;
	}
	patch(partial) {
		const next = { ...this.#current };
		let changed = false;
		for (const key in partial) {
			if (!hasOwnProp.call(partial, key)) continue;
			const value = partial[key];
			if (!Object.is(this.#current[key], value)) {
				next[key] = value;
				changed = true;
			}
		}
		if (changed) {
			this.#current = Object.freeze(next);
			this.#markPending();
		}
	}
	subscribe(callback, options) {
		const signal = options?.signal;
		if (signal?.aborted) return noop;
		this.#listeners.add(callback);
		if (!signal) return () => this.#listeners.delete(callback);
		const onAbort = () => this.#listeners.delete(callback);
		signal.addEventListener("abort", onAbort, { once: true });
		return () => {
			signal.removeEventListener("abort", onAbort);
			this.#listeners.delete(callback);
		};
	}
	flush() {
		if (!this.#pending) return;
		this.#pending = false;
		for (const fn of this.#listeners) fn();
	}
	#markPending() {
		this.#pending = true;
		pendingContainers.add(this);
		scheduleFlush();
	}
};
function createState(initial) {
	return new StateContainer(initial);
}

//#endregion
//#region ../store/dist/dev/core/store.js
const STORE_SYMBOL = Symbol("@videojs/store");
function createStore() {
	return (slice, options = {}) => {
		let target = null;
		let destroyed = false;
		const setupAbort = new AbortController();
		const signals = new AbortControllerRegistry();
		let state;
		function validate() {
			if (destroyed) throwDestroyedError();
			if (!target) throwNoTargetError();
		}
		const initialState = slice.state({
			target: () => {
				validate();
				return target;
			},
			signals,
			set: (partial) => state.patch(partial)
		});
		state = createState(initialState);
		const store = {
			[STORE_SYMBOL]: true,
			get $state() {
				return state;
			},
			get target() {
				return target;
			},
			get destroyed() {
				return destroyed;
			},
			get state() {
				return state.current;
			},
			attach,
			destroy,
			subscribe
		};
		for (const key of Object.keys(initialState)) Object.defineProperty(store, key, {
			get: () => state.current[key],
			enumerable: true
		});
		try {
			options.onSetup?.({
				store,
				signal: setupAbort.signal
			});
		} catch (error) {
			reportError(error);
		}
		return store;
		function attach(newTarget) {
			if (destroyed) throwDestroyedError();
			signals.reset();
			target = newTarget;
			const attachContext = {
				target: newTarget,
				signal: signals.base,
				get: () => state.current,
				set: (partial) => state.patch(partial),
				reportError,
				store: {
					get state() {
						return state.current;
					},
					subscribe
				}
			};
			try {
				slice.attach?.(attachContext);
			} catch (error) {
				reportError(error);
			}
			try {
				options.onAttach?.({
					store,
					target: newTarget,
					signal: signals.base
				});
			} catch (error) {
				reportError(error);
			}
			return detach;
		}
		function detach() {
			if (isNull(target)) return;
			signals.reset();
			target = null;
			state.patch(initialState);
		}
		function destroy() {
			if (destroyed) return;
			destroyed = true;
			detach();
			setupAbort.abort();
		}
		function subscribe(callback, options) {
			return state.subscribe(callback, options);
		}
		function reportError(error) {
			if (options.onError) options.onError({
				store,
				error
			});
			else console.error("[vjs-store]", error);
		}
	};
}
function isStore(value) {
	return isObject(value) && STORE_SYMBOL in value;
}

//#endregion
//#region ../core/dist/dev/dom/feature.js
const definePlayerFeature = defineSlice();

//#endregion
//#region ../utils/dist/dom/attributes.js
function namedNodeMapToObject(namedNodeMap) {
	const obj = {};
	for (const attr of namedNodeMap) obj[attr.name] = attr.value;
	return obj;
}

//#endregion
//#region ../utils/dist/dom/event.js
function onEvent(target, type, options) {
	return new Promise((resolve, reject) => {
		const handleAbort = () => {
			reject(options?.signal?.reason ?? "Aborted");
		};
		if (options?.signal?.aborted) {
			handleAbort();
			return;
		}
		options?.signal?.addEventListener("abort", handleAbort, { once: true });
		target.addEventListener(type, (event) => {
			options?.signal?.removeEventListener("abort", handleAbort);
			resolve(event);
		}, {
			...options,
			once: true
		});
	});
}

//#endregion
//#region ../utils/dist/dom/listen.js
function listen(target, type, listener, options) {
	target.addEventListener(type, listener, options);
	return () => target.removeEventListener(type, listener, options);
}

//#endregion
//#region ../utils/dist/dom/text-track.js
/** Find the `<track>` element that owns the given `TextTrack`. */
function findTrackElement(media, track) {
	for (const el of media.querySelectorAll("track")) if (el.track === track) return el;
	return null;
}
function getTextTrackList(media, filterPred) {
	if (!media?.textTracks) return [];
	return Array.from(media.textTracks).filter(filterPred).sort(sortByTextTrackKind);
}
function sortByTextTrackKind(a, b) {
	return a.kind >= b.kind ? 1 : -1;
}

//#endregion
//#region ../utils/dist/dom/time-ranges.js
/** Converts a TimeRanges object to an array of [start, end] tuples. */
function serializeTimeRanges(ranges) {
	const result = [];
	for (let i = 0; i < ranges.length; i++) result.push([ranges.start(i), ranges.end(i)]);
	return result;
}

//#endregion
//#region ../core/dist/dev/dom/store/features/buffer.js
const bufferFeature = definePlayerFeature({
	name: "buffer",
	state: () => ({
		buffered: [],
		seekable: []
	}),
	attach({ target, signal, set }) {
		const { media } = target;
		const sync = () => set({
			buffered: serializeTimeRanges(media.buffered),
			seekable: serializeTimeRanges(media.seekable)
		});
		sync();
		listen(media, "progress", sync, { signal });
		listen(media, "emptied", sync, { signal });
	}
});

//#endregion
//#region ../core/dist/dev/dom/store/features/controls.js
const IDLE_DELAY = 2e3;
const TAP_THRESHOLD = 250;
const controlsFeature = definePlayerFeature({
	name: "controls",
	state: () => ({
		userActive: true,
		controlsVisible: true
	}),
	attach({ target, signal, get, set }) {
		const { media, container } = target;
		if (isNull(container)) {
			console.warn("[vjs] controlsFeature requires a container element for activity tracking.");
			return;
		}
		function computeVisible(userActive) {
			return userActive || media.paused;
		}
		let idleTimer;
		function clearIdle() {
			clearTimeout(idleTimer);
			idleTimer = void 0;
		}
		function scheduleIdle() {
			clearIdle();
			idleTimer = setTimeout(setInactive, IDLE_DELAY);
		}
		function setActive() {
			if (!get().userActive) set({
				userActive: true,
				controlsVisible: true
			});
			scheduleIdle();
		}
		function setInactive() {
			clearIdle();
			set({
				userActive: false,
				controlsVisible: computeVisible(false)
			});
		}
		let pointerDownTime = 0;
		function onPointerDown() {
			pointerDownTime = Date.now();
		}
		function onPointerUp(event) {
			if (event.pointerType === "touch" && Date.now() - pointerDownTime < TAP_THRESHOLD) {
				const isMediaOrContainer = [media, container].includes(event.target);
				if (get().controlsVisible && isMediaOrContainer) setInactive();
				else setActive();
			} else setActive();
		}
		function onPlaybackChange() {
			const { userActive } = get();
			set({ controlsVisible: computeVisible(userActive) });
			if (!media.paused && userActive) scheduleIdle();
		}
		listen(container, "pointermove", setActive, { signal });
		listen(container, "pointerdown", onPointerDown, { signal });
		listen(container, "pointerup", onPointerUp, { signal });
		listen(container, "keyup", setActive, { signal });
		listen(container, "focusin", setActive, { signal });
		listen(container, "mouseleave", setInactive, { signal });
		listen(media, "play", onPlaybackChange, { signal });
		listen(media, "pause", onPlaybackChange, { signal });
		listen(media, "ended", onPlaybackChange, { signal });
		signal.addEventListener("abort", clearIdle, { once: true });
		scheduleIdle();
	}
});

//#endregion
//#region ../core/dist/dev/dom/store/features/error.js
const errorFeature = definePlayerFeature({
	name: "error",
	state: ({ set }) => ({
		error: null,
		dismissError() {
			set({ error: null });
		}
	}),
	attach({ target, signal, set }) {
		const { media } = target;
		const syncError = () => set({ error: media.error });
		listen(media, "error", syncError, { signal });
		listen(media, "emptied", () => set({ error: null }), { signal });
	}
});

//#endregion
//#region ../core/dist/dev/dom/presentation/fullscreen.js
/** Check if the Fullscreen API is supported on this platform. */
function isFullscreenEnabled() {
	const doc = document;
	if (doc.fullscreenEnabled || doc.webkitFullscreenEnabled) return true;
	return document.createElement("video").webkitSupportsFullscreen === true;
}
/** Get the current fullscreen element from the document. */
function getFullscreenElement() {
	const doc = document;
	return doc.fullscreenElement ?? doc.webkitFullscreenElement ?? null;
}
/**
* Check if a specific element (or its media) is currently in fullscreen.
*
* Uses `:fullscreen` pseudo-class which works across Shadow DOM boundaries.
*/
function isFullscreenElement(container, media) {
	const video = media;
	if (video.webkitDisplayingFullscreen && video.webkitPresentationMode === "fullscreen") return true;
	const target = container ?? media;
	if (getFullscreenElement() === target) return true;
	try {
		return target.matches(":fullscreen");
	} catch {
		return false;
	}
}
/**
* Request fullscreen mode.
*
* Tries container first (to show custom UI), falls back to media element
* for platforms that only support video fullscreen (iOS Safari).
*/
async function requestFullscreen(container, media) {
	const video = media;
	if (container) {
		const el = container;
		if (isFunction(el.requestFullscreen)) return el.requestFullscreen();
		if (isFunction(el.webkitRequestFullscreen)) return el.webkitRequestFullscreen();
		if (isFunction(el.webkitRequestFullScreen)) return el.webkitRequestFullScreen();
	}
	if (isFunction(video.webkitEnterFullscreen)) {
		video.webkitEnterFullscreen();
		return;
	}
	if (isFunction(media.requestFullscreen)) return media.requestFullscreen();
	throw new DOMException("Fullscreen not supported", "NotSupportedError");
}
/** Exit fullscreen mode. */
async function exitFullscreen() {
	const doc = document;
	const video = getFullscreenElement();
	if (isFunction(doc.exitFullscreen)) return doc.exitFullscreen();
	if (isFunction(doc.webkitExitFullscreen)) return doc.webkitExitFullscreen();
	if (isFunction(doc.webkitCancelFullScreen)) return doc.webkitCancelFullScreen();
	if (video && isFunction(video.webkitExitFullscreen)) {
		video.webkitExitFullscreen();
		return;
	}
}

//#endregion
//#region ../core/dist/dev/dom/presentation/pip.js
function resolveMediaTarget(media) {
	const target = media.target;
	return target instanceof HTMLMediaElement ? target : media;
}
/**
* Check if Picture-in-Picture is supported on this platform.
*
* Note: Safari PWAs don't support PiP even though the API exists.
*/
function isPictureInPictureEnabled() {
	if (document.pictureInPictureEnabled) {
		const isSafari = /.*Version\/.*Safari\/.*/.test(navigator.userAgent);
		const isPWA = typeof matchMedia === "function" && matchMedia("(display-mode: standalone)").matches;
		return !isSafari || !isPWA;
	}
	return isFunction(document.createElement("video").webkitSetPresentationMode);
}
/**
* Check if Picture-in-Picture is currently active for a media element.
*/
function isPictureInPictureElement(media) {
	const target = resolveMediaTarget(media);
	if (document.pictureInPictureElement === target) return true;
	return target.webkitPresentationMode === "picture-in-picture";
}
/**
* Request Picture-in-Picture mode.
*
* Uses standard API where available, falls back to iOS Safari's
* WebKit presentation mode.
*/
async function requestPictureInPicture(media) {
	const video = resolveMediaTarget(media);
	if (isFunction(video.requestPictureInPicture)) {
		await video.requestPictureInPicture();
		return;
	}
	if (isFunction(video.webkitSetPresentationMode)) {
		video.webkitSetPresentationMode("picture-in-picture");
		return;
	}
	throw new DOMException("Picture-in-Picture not supported", "NotSupportedError");
}
/**
* Exit Picture-in-Picture mode.
*
* Uses standard API where available, falls back to iOS Safari's
* WebKit presentation mode.
*/
async function exitPictureInPicture(media) {
	if (isFunction(document.exitPictureInPicture)) try {
		await document.exitPictureInPicture();
		return;
	} catch {}
	if (media) {
		const video = resolveMediaTarget(media);
		const mode = video.webkitPresentationMode;
		if (isFunction(video.webkitSetPresentationMode) && (!mode || mode === "picture-in-picture")) {
			video.webkitSetPresentationMode("inline");
			return;
		}
	}
}

//#endregion
//#region ../core/dist/dev/dom/store/features/fullscreen.js
const fullscreenFeature = definePlayerFeature({
	name: "fullscreen",
	state: ({ target }) => ({
		fullscreen: false,
		fullscreenAvailability: "unavailable",
		async requestFullscreen() {
			const { media, container } = target();
			if (isPictureInPictureElement(media)) await exitPictureInPicture(media);
			return requestFullscreen(container, media);
		},
		async exitFullscreen() {
			return exitFullscreen();
		}
	}),
	attach({ target, signal, set }) {
		const { media, container } = target;
		set({ fullscreenAvailability: isFullscreenEnabled() ? "available" : "unsupported" });
		const sync = () => set({ fullscreen: isFullscreenElement(container, media) });
		sync();
		listen(document, "fullscreenchange", sync, { signal });
		listen(document, "webkitfullscreenchange", sync, { signal });
		if ("webkitPresentationMode" in media) listen(media, "webkitpresentationmodechanged", sync, { signal });
	}
});

//#endregion
//#region ../core/dist/dev/dom/store/features/pip.js
const pipFeature = definePlayerFeature({
	name: "pip",
	state: ({ target }) => ({
		pip: false,
		pipAvailability: "unavailable",
		async requestPictureInPicture() {
			const { media, container } = target();
			if (isFullscreenElement(container, media)) await exitFullscreen();
			return requestPictureInPicture(media);
		},
		async exitPictureInPicture() {
			const { media } = target();
			return exitPictureInPicture(media);
		}
	}),
	attach({ target, signal, set }) {
		const { media } = target;
		set({ pipAvailability: isPictureInPictureEnabled() ? "available" : "unsupported" });
		const sync = () => set({ pip: isPictureInPictureElement(media) });
		sync();
		listen(media, "enterpictureinpicture", sync, { signal });
		listen(media, "leavepictureinpicture", sync, { signal });
		if ("webkitPresentationMode" in media) listen(media, "webkitpresentationmodechanged", sync, { signal });
	}
});

//#endregion
//#region ../core/dist/dev/dom/store/features/playback.js
const playbackFeature = definePlayerFeature({
	name: "playback",
	state: ({ target }) => ({
		paused: true,
		ended: false,
		started: false,
		waiting: false,
		play() {
			return target().media.play();
		},
		pause() {
			target().media.pause();
		}
	}),
	attach({ target, signal, set }) {
		const { media } = target;
		const sync = () => set({
			paused: media.paused,
			ended: media.ended,
			started: !media.paused || media.currentTime > 0,
			waiting: media.readyState < HTMLMediaElement.HAVE_FUTURE_DATA && !media.paused
		});
		sync();
		listen(media, "emptied", sync, { signal });
		listen(media, "play", sync, { signal });
		listen(media, "pause", sync, { signal });
		listen(media, "ended", sync, { signal });
		listen(media, "playing", sync, { signal });
		listen(media, "waiting", sync, { signal });
	}
});

//#endregion
//#region ../core/dist/dev/dom/store/features/playback-rate.js
const DEFAULT_RATES = [
	1,
	1.2,
	1.5,
	1.7,
	2
];
const playbackRateFeature = definePlayerFeature({
	name: "playbackRate",
	state: ({ target }) => ({
		playbackRates: DEFAULT_RATES,
		playbackRate: 1,
		setPlaybackRate(rate) {
			target().media.playbackRate = rate;
		}
	}),
	attach({ target, signal, set }) {
		const { media } = target;
		const sync = () => set({ playbackRate: media.playbackRate });
		sync();
		listen(media, "ratechange", sync, { signal });
	}
});

//#endregion
//#region ../core/dist/dev/dom/store/features/source.js
const sourceFeature = definePlayerFeature({
	name: "source",
	state: ({ target, signals }) => ({
		source: null,
		canPlay: false,
		loadSource(src) {
			signals.clear();
			const { media } = target();
			media.src = src;
			media.load();
			return src;
		}
	}),
	attach({ target, signal, set }) {
		const { media } = target;
		const sync = () => set({
			source: media.currentSrc || media.src || null,
			canPlay: media.readyState >= HTMLMediaElement.HAVE_ENOUGH_DATA
		});
		sync();
		listen(media, "canplay", sync, { signal });
		listen(media, "canplaythrough", sync, { signal });
		listen(media, "loadstart", sync, { signal });
		listen(media, "emptied", sync, { signal });
	}
});

//#endregion
//#region ../core/dist/dev/dom/store/features/text-track.js
const textTrackFeature = definePlayerFeature({
	name: "textTrack",
	state: ({ target }) => ({
		chaptersCues: [],
		thumbnailCues: [],
		thumbnailTrackSrc: null,
		textTrackList: [],
		subtitlesShowing: false,
		toggleSubtitles(forceShow) {
			const subtitlesTracks = getTextTrackList(target().media, (track) => track.kind === "subtitles" || track.kind === "captions");
			if (!subtitlesTracks.length) return false;
			const showing = subtitlesTracks.some((track) => track.mode === "showing");
			const nextShowing = forceShow ?? !showing;
			for (const track of subtitlesTracks) track.mode = nextShowing ? "showing" : "disabled";
			return nextShowing;
		}
	}),
	attach({ target, signal, set }) {
		const { media } = target;
		let trackCleanup = null;
		function sync() {
			trackCleanup?.abort();
			trackCleanup = new AbortController();
			let chaptersTrack = null;
			let thumbnailTrack = null;
			const textTrackList = [];
			let subtitlesShowing = false;
			for (let i = 0; i < media.textTracks.length; i++) {
				const track = media.textTracks[i];
				if (!chaptersTrack && track.kind === "chapters") chaptersTrack = track;
				if (!thumbnailTrack && track.kind === "metadata" && track.label === "thumbnails") thumbnailTrack = track;
				textTrackList.push({
					kind: track.kind,
					label: track.label,
					language: track.language,
					mode: track.mode
				});
				if ((track.kind === "captions" || track.kind === "subtitles") && track.mode === "showing") subtitlesShowing = true;
			}
			const chaptersCues = chaptersTrack?.cues ? Array.from(chaptersTrack.cues) : [];
			const thumbnailCues = thumbnailTrack?.cues ? Array.from(thumbnailTrack.cues) : [];
			let thumbnailTrackSrc = null;
			if (thumbnailTrack) thumbnailTrackSrc = findTrackElement(media, thumbnailTrack)?.src ?? null;
			for (const trackEl of media.querySelectorAll?.("track") ?? []) if (!trackEl.track?.cues?.length) listen(trackEl, "load", sync, { signal: trackCleanup.signal });
			set({
				chaptersCues,
				thumbnailCues,
				thumbnailTrackSrc,
				textTrackList,
				subtitlesShowing
			});
		}
		sync();
		listen(media.textTracks, "addtrack", sync, { signal });
		listen(media.textTracks, "removetrack", sync, { signal });
		listen(media.textTracks, "change", sync, { signal });
		listen(media, "loadstart", sync, { signal });
		signal.addEventListener("abort", () => trackCleanup?.abort(), { once: true });
	}
});

//#endregion
//#region ../core/dist/dev/dom/media/predicate.js
function hasMetadata(media) {
	return media.readyState >= HTMLMediaElement.HAVE_METADATA;
}

//#endregion
//#region ../core/dist/dev/dom/store/signal-keys.js
const signalKeys = { seek: Symbol.for("@videojs/seek") };

//#endregion
//#region ../core/dist/dev/dom/store/features/time.js
const timeFeature = definePlayerFeature({
	name: "time",
	state: ({ target, signals, set }) => ({
		currentTime: 0,
		duration: 0,
		seeking: false,
		async seek(time) {
			const { media } = target(), signal = signals.supersede(signalKeys.seek);
			if (!hasMetadata(media)) {
				if (!await onEvent(media, "loadedmetadata", { signal }).catch(() => false)) return media.currentTime;
			}
			const clampedTime = Math.max(0, Math.min(time, media.duration || Infinity));
			set({
				currentTime: clampedTime,
				seeking: true
			});
			media.currentTime = clampedTime;
			await onEvent(media, "seeked", { signal }).catch(noop);
			return media.currentTime;
		}
	}),
	attach({ target, signal, set }) {
		const { media } = target;
		const sync = () => set({
			currentTime: media.currentTime,
			duration: Number.isFinite(media.duration) ? media.duration : 0,
			seeking: media.seeking
		});
		sync();
		listen(media, "timeupdate", sync, { signal });
		listen(media, "durationchange", sync, { signal });
		listen(media, "seeking", sync, { signal });
		listen(media, "seeked", sync, { signal });
		listen(media, "loadedmetadata", sync, { signal });
		listen(media, "emptied", sync, { signal });
	}
});

//#endregion
//#region ../core/dist/dev/dom/store/features/volume.js
/** Volume to restore when unmuting at zero. */
const UNMUTE_VOLUME = .25;
const volumeFeature = definePlayerFeature({
	name: "volume",
	state: ({ target }) => ({
		volume: 1,
		muted: false,
		volumeAvailability: "unavailable",
		setVolume(volume) {
			const { media } = target();
			const clamped = Math.max(0, Math.min(1, volume));
			if (clamped > 0 && media.muted) media.muted = false;
			media.volume = clamped;
			return media.volume;
		},
		toggleMuted() {
			const { media } = target();
			if (media.muted || media.volume === 0) {
				media.muted = false;
				if (media.volume === 0) media.volume = UNMUTE_VOLUME;
			} else media.muted = true;
			return media.muted;
		}
	}),
	attach({ target, signal, set }) {
		const { media } = target;
		set({ volumeAvailability: canSetVolume() });
		const sync = () => set({
			volume: media.volume,
			muted: media.muted
		});
		sync();
		listen(media, "volumechange", sync, { signal });
	}
});
/** Check if volume can be programmatically set (fails on iOS Safari). */
function canSetVolume() {
	const video = document.createElement("video");
	try {
		video.volume = .5;
		return video.volume === .5 ? "available" : "unsupported";
	} catch {
		return "unsupported";
	}
}

//#endregion
//#region ../core/dist/dev/dom/store/features/presets.js
const backgroundFeatures = [];

//#endregion
//#region ../core/dist/dev/dom/store/selectors.js
/** Select the buffer state (buffered ranges, percent buffered). */
const selectBuffer = createSelector(bufferFeature);
/** Select the controls state (controls visible, user-active). */
const selectControls = createSelector(controlsFeature);
/** Select the error state (error, dismissed, dismissError). */
const selectError = createSelector(errorFeature);
/** Select the fullscreen state (fullscreen active, availability). */
const selectFullscreen = createSelector(fullscreenFeature);
/** Select the PiP state (picture-in-picture active, availability). */
const selectPiP = createSelector(pipFeature);
/** Select the playback state (paused, ended, play, pause, toggle). */
const selectPlayback = createSelector(playbackFeature);
/** Select the playback rate state (playbackRate, playbackRates, setPlaybackRate). */
const selectPlaybackRate = createSelector(playbackRateFeature);
/** Select the source state (src, type). */
const selectSource = createSelector(sourceFeature);
/** Select the text track state (chapters cues, thumbnail cues). */
const selectTextTrack = createSelector(textTrackFeature);
/** Select the time state (currentTime, duration, seek). */
const selectTime = createSelector(timeFeature);
/** Select the volume state (volume, muted, setVolume, setMuted). */
const selectVolume = createSelector(volumeFeature);

//#endregion
//#region ../../node_modules/.pnpm/@lit+context@1.1.6/node_modules/@lit/context/development/lib/context-request-event.js
/**
* @license
* Copyright 2021 Google LLC
* SPDX-License-Identifier: BSD-3-Clause
*/
/**
* An event fired by a context requester to signal it desires a specified context with the given key.
*
* A provider should inspect the `context` property of the event to determine if it has a value that can
* satisfy the request, calling the `callback` with the requested value if so.
*
* If the requested context event contains a truthy `subscribe` value, then a provider can call the callback
* multiple times if the value is changed, if this is the case the provider should pass an `unsubscribe`
* method to the callback which consumers can invoke to indicate they no longer wish to receive these updates.
*
* If no `subscribe` value is present in the event, then the provider can assume that this is a 'one time'
* request for the context and can therefore not track the consumer.
*/
var ContextRequestEvent = class extends Event {
	/**
	*
	* @param context the context key to request
	* @param contextTarget the original context target of the requester
	* @param callback the callback that should be invoked when the context with the specified key is available
	* @param subscribe when, true indicates we want to subscribe to future updates
	*/
	constructor(context, contextTarget, callback, subscribe) {
		super("context-request", {
			bubbles: true,
			composed: true
		});
		this.context = context;
		this.contextTarget = contextTarget;
		this.callback = callback;
		this.subscribe = subscribe ?? false;
	}
};

//#endregion
//#region ../../node_modules/.pnpm/@lit+context@1.1.6/node_modules/@lit/context/development/lib/create-context.js
/**
* @license
* Copyright 2021 Google LLC
* SPDX-License-Identifier: BSD-3-Clause
*/
/**
* Creates a typed Context.
*
* Contexts are compared with strict equality.
*
* If you want two separate `createContext()` calls to referer to the same
* context, then use a key that will by equal under strict equality like a
* string for `Symbol.for()`:
*
* ```ts
* // true
* createContext('my-context') === createContext('my-context')
* // true
* createContext(Symbol.for('my-context')) === createContext(Symbol.for('my-context'))
* ```
*
* If you want a context to be unique so that it's guaranteed to not collide
* with other contexts, use a key that's unique under strict equality, like
* a `Symbol()` or object.:
*
* ```
* // false
* createContext({}) === createContext({})
* // false
* createContext(Symbol('my-context')) === createContext(Symbol('my-context'))
* ```
*
* @param key a context key value
* @template ValueType the type of value that can be provided by this context.
* @returns the context key value cast to `Context<K, ValueType>`
*/
function createContext(key) {
	return key;
}

//#endregion
//#region ../../node_modules/.pnpm/@lit+context@1.1.6/node_modules/@lit/context/development/lib/controllers/context-consumer.js
/**
* @license
* Copyright 2021 Google LLC
* SPDX-License-Identifier: BSD-3-Clause
*/
/**
* A ReactiveController which adds context consuming behavior to a custom
* element by dispatching `context-request` events.
*
* When the host element is connected to the document it will emit a
* `context-request` event with its context key. When the context request
* is satisfied the controller will invoke the callback, if present, and
* trigger a host update so it can respond to the new value.
*
* It will also call the dispose method given by the provider when the
* host element is disconnected.
*/
var ContextConsumer = class {
	constructor(host, contextOrOptions, callback, subscribe) {
		this.subscribe = false;
		this.provided = false;
		this.value = void 0;
		this._callback = (value, unsubscribe) => {
			if (this.unsubscribe) {
				if (this.unsubscribe !== unsubscribe) {
					this.provided = false;
					this.unsubscribe();
				}
				if (!this.subscribe) this.unsubscribe();
			}
			this.value = value;
			this.host.requestUpdate();
			if (!this.provided || this.subscribe) {
				this.provided = true;
				if (this.callback) this.callback(value, unsubscribe);
			}
			this.unsubscribe = unsubscribe;
		};
		this.host = host;
		if (contextOrOptions.context !== void 0) {
			const options = contextOrOptions;
			this.context = options.context;
			this.callback = options.callback;
			this.subscribe = options.subscribe ?? false;
		} else {
			this.context = contextOrOptions;
			this.callback = callback;
			this.subscribe = subscribe ?? false;
		}
		this.host.addController(this);
	}
	hostConnected() {
		this.dispatchRequest();
	}
	hostDisconnected() {
		if (this.unsubscribe) {
			this.unsubscribe();
			this.unsubscribe = void 0;
		}
	}
	dispatchRequest() {
		this.host.dispatchEvent(new ContextRequestEvent(this.context, this.host, this._callback, this.subscribe));
	}
};

//#endregion
//#region ../../node_modules/.pnpm/@lit+context@1.1.6/node_modules/@lit/context/development/lib/value-notifier.js
/**
* @license
* Copyright 2021 Google LLC
* SPDX-License-Identifier: BSD-3-Clause
*/
/**
* A simple class which stores a value, and triggers registered callbacks when
* the value is changed via its setter.
*
* An implementor might use other observable patterns such as MobX or Redux to
* get behavior like this. But this is a pretty minimal approach that will
* likely work for a number of use cases.
*/
var ValueNotifier = class {
	get value() {
		return this._value;
	}
	set value(v) {
		this.setValue(v);
	}
	setValue(v, force = false) {
		const update = force || !Object.is(v, this._value);
		this._value = v;
		if (update) this.updateObservers();
	}
	constructor(defaultValue) {
		this.subscriptions = /* @__PURE__ */ new Map();
		this.updateObservers = () => {
			for (const [callback, { disposer }] of this.subscriptions) callback(this._value, disposer);
		};
		if (defaultValue !== void 0) this.value = defaultValue;
	}
	addCallback(callback, consumerHost, subscribe) {
		if (!subscribe) {
			callback(this.value);
			return;
		}
		if (!this.subscriptions.has(callback)) this.subscriptions.set(callback, {
			disposer: () => {
				this.subscriptions.delete(callback);
			},
			consumerHost
		});
		const { disposer } = this.subscriptions.get(callback);
		callback(this.value, disposer);
	}
	clearCallbacks() {
		this.subscriptions.clear();
	}
};

//#endregion
//#region ../../node_modules/.pnpm/@lit+context@1.1.6/node_modules/@lit/context/development/lib/controllers/context-provider.js
/**
* @license
* Copyright 2021 Google LLC
* SPDX-License-Identifier: BSD-3-Clause
*/
var ContextProviderEvent = class extends Event {
	/**
	*
	* @param context the context which this provider can provide
	* @param contextTarget the original context target of the provider
	*/
	constructor(context, contextTarget) {
		super("context-provider", {
			bubbles: true,
			composed: true
		});
		this.context = context;
		this.contextTarget = contextTarget;
	}
};
/**
* A ReactiveController which adds context provider behavior to a
* custom element.
*
* This controller simply listens to the `context-request` event when
* the host is connected to the DOM and registers the received callbacks
* against its observable Context implementation.
*
* The controller may also be attached to any HTML element in which case it's
* up to the user to call hostConnected() when attached to the DOM. This is
* done automatically for any custom elements implementing
* ReactiveControllerHost.
*/
var ContextProvider = class extends ValueNotifier {
	constructor(host, contextOrOptions, initialValue) {
		super(contextOrOptions.context !== void 0 ? contextOrOptions.initialValue : initialValue);
		this.onContextRequest = (ev) => {
			if (ev.context !== this.context) return;
			const consumerHost = ev.contextTarget ?? ev.composedPath()[0];
			if (consumerHost === this.host) return;
			ev.stopPropagation();
			this.addCallback(ev.callback, consumerHost, ev.subscribe);
		};
		/**
		* When we get a provider request event, that means a child of this element
		* has just woken up. If it's a provider of our context, then we may need to
		* re-parent our subscriptions, because is a more specific provider than us
		* for its subtree.
		*/
		this.onProviderRequest = (ev) => {
			if (ev.context !== this.context) return;
			if ((ev.contextTarget ?? ev.composedPath()[0]) === this.host) return;
			const seen = /* @__PURE__ */ new Set();
			for (const [callback, { consumerHost }] of this.subscriptions) {
				if (seen.has(callback)) continue;
				seen.add(callback);
				consumerHost.dispatchEvent(new ContextRequestEvent(this.context, consumerHost, callback, true));
			}
			ev.stopPropagation();
		};
		this.host = host;
		if (contextOrOptions.context !== void 0) this.context = contextOrOptions.context;
		else this.context = contextOrOptions;
		this.attachListeners();
		this.host.addController?.(this);
	}
	attachListeners() {
		this.host.addEventListener("context-request", this.onContextRequest);
		this.host.addEventListener("context-provider", this.onProviderRequest);
	}
	hostConnected() {
		this.host.dispatchEvent(new ContextProviderEvent(this.context, this.host));
	}
};

//#endregion
//#region src/player/context.ts
const PLAYER_CONTEXT_KEY = Symbol("@videojs/player");
/**
* The default player context instance for consuming the player store in controllers.
*
* @public
*/
const playerContext = createContext(PLAYER_CONTEXT_KEY);

//#endregion
//#region src/store/container-mixin.ts
/**
* Create a mixin that consumes player context and auto-attaches media elements.
*
* @param context - Player context to consume from an ancestor provider.
*/
function createContainerMixin(context) {
	return (BaseClass) => {
		class PlayerContainerElement extends BaseClass {
			#detach = noop;
			#observer = null;
			#contextStore = null;
			constructor(...args) {
				super(...args);
				new ContextConsumer(this, {
					context,
					callback: (value) => {
						this.#contextStore = value ?? null;
						this.#attachMedia();
					},
					subscribe: true
				});
			}
			get store() {
				return this.#contextStore;
			}
			connectedCallback() {
				super.connectedCallback();
				this.#observer = new MutationObserver((records) => {
					if (records.some(hasMediaElement)) this.#attachMedia();
				});
				this.#observer.observe(this, {
					childList: true,
					subtree: true,
					attributes: true,
					attributeFilter: ["name"]
				});
				this.addEventListener("slotchange", this.#onSlotChange);
				this.#attachMedia();
			}
			disconnectedCallback() {
				super.disconnectedCallback();
				this.#observer?.disconnect();
				this.#observer = null;
				this.removeEventListener("slotchange", this.#onSlotChange);
				this.#detach();
			}
			#onSlotChange = () => {
				this.#attachMedia();
			};
			#getSlottedMedia() {
				const slot = this.querySelector("slot[name=\"media\"]");
				if (!slot) return null;
				for (const el of slot.assignedElements({ flatten: true })) if (isMediaElement(el)) return el;
				return null;
			}
			#findMediaElement() {
				const media = Array.from(this.children).find(isMediaElement);
				if (media) return media;
				return null;
			}
			#attachMedia() {
				const store = this.#contextStore ?? this.store;
				if (!store) return;
				const media = this.querySelector("video, audio") ?? this.#findMediaElement() ?? this.#getSlottedMedia();
				if (!media) {
					this.#detach();
					this.#detach = noop;
					return;
				}
				if (isCustomMediaElement(media)) globalThis.customElements?.upgrade?.(media);
				const target = {
					media,
					container: this
				};
				const hasMediaChanged = store.target?.media !== target.media, hasContainerChanged = store.target?.container !== target.container;
				if (hasMediaChanged || hasContainerChanged) {
					this.#detach();
					this.#detach = store.attach(target);
				}
			}
		}
		return PlayerContainerElement;
	};
}
function isMediaElement(node) {
	return node instanceof HTMLMediaElement || isCustomMediaElement(node);
}
function isCustomMediaElement(node) {
	return node instanceof HTMLElement && (node.localName.endsWith("-audio") || node.localName.endsWith("-video"));
}
function isMediaSlotElement(node) {
	return node instanceof HTMLSlotElement && node.name === "media";
}
function hasMediaElement(record) {
	if (isMediaSlotElement(record.target)) return true;
	for (const node of record.addedNodes) if (isMediaElement(node) || isMediaSlotElement(node)) return true;
	for (const node of record.removedNodes) if (isMediaElement(node) || isMediaSlotElement(node)) return true;
	return false;
}

//#endregion
//#region ../element/dist/dev/destroy-mixin.js
/**
* Mixin that adds a deferred destruction lifecycle to a `ReactiveElement`.
*
* On disconnect, schedules destruction after two animation frames.
* If the element reconnects before the frames fire (e.g. DOM shuffling,
* framework reconciliation), the `isConnected` check prevents destruction.
*
* The `keep-alive` attribute prevents automatic destruction entirely —
* call `destroy()` manually when done.
*
* Subclasses override `destroyCallback()` (calling `super.destroyCallback()`)
* to release heavy resources like stores or imperative APIs.
*
* Mirrors `addController`/`removeController` to track controllers
* (needed because `ReactiveElement.#controllers` is hard-private),
* calls `hostDestroyed()` on all tracked controllers in `destroyCallback`,
* and guards `performUpdate()` so no updates run after destruction.
*/
function DestroyMixin(SuperClass) {
	class DestroyableElement extends SuperClass {
		#destroyed = false;
		#trackedControllers = /* @__PURE__ */ new Set();
		get destroyed() {
			return this.#destroyed;
		}
		destroy() {
			if (this.#destroyed) return;
			this.#destroyed = true;
			this.destroyCallback();
		}
		destroyCallback() {
			for (const c of this.#trackedControllers) c.hostDestroyed?.();
		}
		addController(controller) {
			super.addController(controller);
			this.#trackedControllers.add(controller);
		}
		removeController(controller) {
			super.removeController(controller);
			this.#trackedControllers.delete(controller);
		}
		connectedCallback() {
			if (this.#destroyed) return;
			super.connectedCallback();
		}
		disconnectedCallback() {
			super.disconnectedCallback();
			if (!this.#destroyed && !this.hasAttribute("keep-alive")) requestAnimationFrame(() => {
				requestAnimationFrame(() => {
					if (!this.isConnected) this.destroy();
				});
			});
		}
		performUpdate() {
			if (this.#destroyed) return;
			super.performUpdate();
		}
	}
	return DestroyableElement;
}

//#endregion
//#region ../element/dist/dev/reactive-element.js
const cache = /* @__PURE__ */ new WeakMap();
const propertyKeys = /* @__PURE__ */ new Map();
/**
* Lightweight reactive custom element base class.
*
* Drop-in subset of Lit's `ReactiveElement` — supports `static properties`,
* attribute reflection, batched async updates, and reactive controllers.
* No Shadow DOM, no `static styles`, no decorators.
*
* Updates are batched using the same Promise-based scheduling as Lit:
* property changes enqueue a microtask, and the update is gated behind
* `connectedCallback` so the first update only runs once the element
* is in the document.
*
* Subclasses that extend another element with properties must spread them:
*
* @example
* ```ts
* class MyButton extends ReactiveElement {
*   static override properties = {
*     label: { type: String },
*     disabled: { type: Boolean },
*   };
*
*   label = 'Click me';
*   disabled = false;
*
*   protected override update(changed: PropertyValues): void {
*     super.update(changed);
*     this.textContent = this.label;
*   }
* }
*
* // Inheritance — spread parent properties
* class FancyButton extends MyButton {
*   static override properties = {
*     ...MyButton.properties,
*     variant: { type: String },
*   };
*
*   variant = 'primary';
* }
* ```
*/
var ReactiveElement = class extends HTMLElement {
	static {
		this.properties = {};
	}
	/**
	* Returns a list of attributes corresponding to the registered properties.
	*/
	static get observedAttributes() {
		return [...resolve(this).attrToProp.keys()];
	}
	#controllers = /* @__PURE__ */ new Set();
	#changedProperties = /* @__PURE__ */ new Map();
	#instanceProperties;
	/**
	* Promise that gates the first update until `connectedCallback`. Also
	* used to serialize updates — each `#enqueueUpdate` awaits the previous
	* `#updatePromise`, so property changes are batched and updates never
	* overlap. Matches Lit's scheduling model.
	*/
	#updatePromise;
	constructor() {
		super();
		this.isUpdatePending = false;
		this.hasUpdated = false;
		this.#updatePromise = new Promise((res) => this.enableUpdating = res);
		const { props } = resolve(this.constructor);
		for (const name of props.keys()) if (Object.hasOwn(this, name)) {
			(this.#instanceProperties ??= /* @__PURE__ */ new Map()).set(name, this[name]);
			delete this[name];
		}
		this.requestUpdate();
	}
	/**
	* Note, this method should be considered final and not overridden. It is
	* overridden on the element instance with a function that triggers the
	* first update.
	*/
	enableUpdating(_requestedUpdate) {}
	/**
	* Registers a {@linkcode ReactiveController} to participate in the
	* element's reactive update cycle. The element automatically calls into
	* any registered controllers during its lifecycle callbacks.
	*
	* If the element is connected when `addController()` is called, the
	* controller's `hostConnected()` callback will be immediately called.
	*/
	addController(controller) {
		this.#controllers.add(controller);
		if (this.isConnected) controller.hostConnected?.();
	}
	/** Removes a {@linkcode ReactiveController} from the element. */
	removeController(controller) {
		this.#controllers.delete(controller);
	}
	/**
	* On first connection, enables updating and notifies controllers.
	*/
	connectedCallback() {
		this.enableUpdating(true);
		for (const c of this.#controllers) c.hostConnected?.();
	}
	disconnectedCallback() {
		for (const c of this.#controllers) c.hostDisconnected?.();
	}
	/**
	* Synchronizes property values when attributes change.
	*
	* Specifically, when an attribute is set, the corresponding property is
	* set. You should rarely need to implement this callback. If this method
	* is overridden, `super.attributeChangedCallback(name, _old, value)` must
	* be called.
	*/
	attributeChangedCallback(attr, oldValue, newValue) {
		if (oldValue === newValue) return;
		const { props, attrToProp } = resolve(this.constructor);
		const propName = attrToProp.get(attr);
		if (!propName) return;
		const decl = props.get(propName);
		if (!decl) return;
		let value = newValue;
		if (decl.type === Boolean) value = newValue !== null;
		else if (decl.type === Number) value = newValue === null ? null : Number(newValue);
		this[propName] = value;
	}
	/**
	* Requests an update which is processed asynchronously. This should be
	* called when an element should update based on some state not triggered
	* by setting a reactive property. In this case, pass no arguments. It
	* should also be called when manually implementing a property setter. In
	* this case, pass the property `name` and `oldValue` to ensure that any
	* configured property options are honored.
	*/
	requestUpdate(name, oldValue) {
		if (name !== void 0) this.#changedProperties.set(name, oldValue);
		if (this.isUpdatePending) return;
		this.#updatePromise = this.#enqueueUpdate();
	}
	/**
	* Sets up the element to asynchronously update. Awaits the previous
	* `#updatePromise` which both serializes updates and (on first update)
	* waits for `connectedCallback` to resolve the gate.
	*/
	async #enqueueUpdate() {
		this.isUpdatePending = true;
		try {
			await this.#updatePromise;
		} catch (e) {
			Promise.reject(e);
		}
		const result = this.scheduleUpdate();
		if (result != null) await result;
		return !this.isUpdatePending;
	}
	/**
	* Schedules an element update. You can override this method to change the
	* timing of updates by returning a Promise. The update will await the
	* returned Promise, and you should resolve the Promise to allow the update
	* to proceed. If this method is overridden, `super.scheduleUpdate()` must
	* be called.
	*
	* For instance, to schedule updates to occur just before the next frame:
	*
	* ```ts
	* override protected async scheduleUpdate(): Promise<unknown> {
	*   await new Promise((resolve) => requestAnimationFrame(() => resolve()));
	*   super.scheduleUpdate();
	* }
	* ```
	*/
	scheduleUpdate() {
		this.performUpdate();
	}
	/**
	* Performs an element update. Note, if an exception is thrown during the
	* update, `firstUpdated` and `updated` will not be called.
	*
	* Call `performUpdate()` to immediately process a pending update. This
	* should generally not be needed, but it can be done in rare cases when
	* you need to update synchronously.
	*/
	performUpdate() {
		if (!this.isUpdatePending) return;
		if (!this.hasUpdated && this.#instanceProperties) {
			for (const [name, value] of this.#instanceProperties) this[name] = value;
			this.#instanceProperties = void 0;
		}
		const changed = this.#changedProperties;
		this.willUpdate(changed);
		for (const c of this.#controllers) c.hostUpdate?.();
		this.update(changed);
		this.#changedProperties = /* @__PURE__ */ new Map();
		this.isUpdatePending = false;
		for (const c of this.#controllers) c.hostUpdated?.();
		if (!this.hasUpdated) {
			this.hasUpdated = true;
			this.firstUpdated(changed);
		}
		this.updated(changed);
	}
	/**
	* Invoked before `update()` to compute values needed during the update.
	*
	* Implement `willUpdate` to compute property values that depend on other
	* properties and are used in the rest of the update process.
	*
	* ```ts
	* willUpdate(changed) {
	*   if (changed.has('firstName') || changed.has('lastName')) {
	*     this.sha = computeSHA(`${this.firstName} ${this.lastName}`);
	*   }
	* }
	* ```
	*/
	willUpdate(_changed) {}
	/**
	* Updates the element. This method reflects property values to attributes
	* and can be overridden to render and keep updated element DOM. Setting
	* properties inside this method will *not* trigger another update.
	*/
	update(_changed) {}
	/**
	* Invoked when the element is first updated. Implement to perform one
	* time work on the element after update.
	*
	* Setting properties inside this method will trigger the element to
	* update again after this update cycle completes.
	*/
	firstUpdated(_changed) {}
	/**
	* Invoked whenever the element is updated. Implement to perform
	* post-updating tasks via DOM APIs, for example, focusing an element.
	*
	* Setting properties inside this method will trigger the element to
	* update again after this update cycle completes.
	*/
	updated(_changed) {}
	/**
	* Returns a Promise that resolves when the element has completed updating.
	* The Promise value is a boolean that is `true` if the element completed
	* the update without triggering another update. The Promise result is
	* `false` if a property was set inside `updated()`.
	*/
	get updateComplete() {
		return this.#updatePromise;
	}
};
/**
* Resolve `ctor.properties` into lookup Maps and install reactive accessors
* on the prototype. Runs once per class, result is cached.
*
* Subclasses that need parent properties must spread them:
* `static override properties = { ...Parent.properties, ... }`.
*/
function resolve(ctor) {
	const existing = cache.get(ctor);
	if (existing) return existing;
	const props = /* @__PURE__ */ new Map();
	const attrToProp = /* @__PURE__ */ new Map();
	for (const [name, decl] of Object.entries(ctor.properties)) {
		props.set(name, decl);
		attrToProp.set(decl.attribute ?? name, name);
		if (!Object.getOwnPropertyDescriptor(ctor.prototype, name)?.get) {
			let key = propertyKeys.get(name);
			if (!key) {
				key = Symbol(name);
				propertyKeys.set(name, key);
			}
			Object.defineProperty(ctor.prototype, name, {
				get() {
					return this[key];
				},
				set(value) {
					const old = this[key];
					this[key] = value;
					if (!Object.is(old, value)) this.requestUpdate(name, old);
				},
				configurable: true,
				enumerable: true
			});
		}
	}
	const meta = {
		props,
		attrToProp
	};
	cache.set(ctor, meta);
	return meta;
}

//#endregion
//#region src/ui/media-element.ts
/** Base class for interactive media UI elements. */
var MediaElement = class extends DestroyMixin(ReactiveElement) {};

//#endregion
//#region src/media/container-element.ts
const ContainerMixin = createContainerMixin(playerContext);
var MediaContainerElement = class extends ContainerMixin(MediaElement) {
	static {
		this.tagName = "media-container";
	}
};

//#endregion
//#region src/store/provider-mixin.ts
/**
* Create a mixin that provides player context to descendant elements.
*
* @param context - Player context to provide to descendants.
* @param factory - Factory function that creates a store instance.
*/
function createProviderMixin(context, factory) {
	return (BaseClass) => {
		class PlayerProviderElement extends BaseClass {
			#store = factory();
			#provider = new ContextProvider(this, {
				context,
				initialValue: this.store
			});
			get store() {
				if (isNull(this.#store)) this.#store = factory();
				return this.#store;
			}
			connectedCallback() {
				super.connectedCallback();
				this.#provider.setValue(this.store);
			}
			destroyCallback() {
				this.#store?.destroy();
				this.#store = null;
				super.destroyCallback();
			}
		}
		return PlayerProviderElement;
	};
}

//#endregion
//#region ../store/dist/dev/html/controllers/snapshot-controller.js
/**
* Subscribe to a `State<T>` container with optional selector.
*
* Without selector: returns full state, re-renders on any state change.
* With selector: returns selected slice, re-renders only when the slice changes (shallowEqual).
*
* @example
* ```ts
* #state = new SnapshotController(this, sliderState, (s) => s.value);
* ```
*/
var SnapshotController = class {
	#host;
	#selector;
	#state;
	#cached;
	#unsubscribe = noop;
	constructor(host, state, selector) {
		this.#host = host;
		this.#state = state;
		this.#selector = selector;
		host.addController(this);
	}
	get value() {
		if (!this.#selector) return this.#state.current;
		this.#cached ??= this.#selector(this.#state.current);
		return this.#cached;
	}
	/** Switch to tracking a different state container. */
	track(state) {
		this.#state = state;
		this.#subscribe();
	}
	hostConnected() {
		this.#subscribe();
	}
	hostDisconnected() {
		this.#unsubscribe();
		this.#unsubscribe = noop;
		this.#cached = void 0;
	}
	#subscribe() {
		this.#unsubscribe();
		if (!this.#selector) {
			this.#unsubscribe = this.#state.subscribe(() => this.#host.requestUpdate());
			return;
		}
		const selector = this.#selector;
		this.#cached = selector(this.#state.current);
		this.#unsubscribe = this.#state.subscribe(() => {
			const next = selector(this.#state.current);
			if (!shallowEqual(this.#cached, next)) {
				this.#cached = next;
				this.#host.requestUpdate();
			}
		});
	}
};

//#endregion
//#region ../store/dist/dev/html/store-accessor.js
/**
* Resolves a store from either a direct instance or context.
*
* When given a direct store, provides immediate access.
* When given a context, sets up a ContextConsumer to receive the store.
*
* @example Direct store
* ```ts
* const accessor = new StoreAccessor(host, store, (s) => console.log('available', s));
* accessor.value; // Store (immediately available)
* ```
*
* @example Context source
* ```ts
* const accessor = new StoreAccessor(host, context, (s) => console.log('available', s));
* accessor.value; // null until context provides store
* ```
*/
var StoreAccessor = class {
	#onAvailable;
	#consumer;
	#directStore;
	constructor(host, source, onAvailable) {
		this.#onAvailable = onAvailable ?? noop;
		if (isStore(source)) {
			this.#directStore = source;
			this.#consumer = null;
		} else {
			this.#directStore = null;
			this.#consumer = new ContextConsumer(host, {
				context: source,
				callback: (store) => this.#onAvailable(store),
				subscribe: false
			});
		}
		host.addController(this);
	}
	/** Returns the store, or null if not yet available from context. */
	get value() {
		if (this.#consumer) return this.#consumer.value ?? null;
		return this.#directStore;
	}
	hostConnected() {
		if (this.#directStore) this.#onAvailable(this.#directStore);
	}
};

//#endregion
//#region ../store/dist/dev/html/controllers/store-controller.js
/**
* Access store state and actions.
*
* Without selector: Returns the store, does NOT subscribe to changes.
* With selector: Returns selected state, triggers update when selected state changes (shallowEqual).
*
* @example
* ```ts
* // Store access (no subscription) - access actions
* class Controls extends LitElement {
*   #store = new StoreController(this, storeSource);
*
*   handleClick() {
*     this.#store.value.setVolume(0.5);
*   }
* }
*
* // Selector-based subscription - re-renders when playback changes
* class PlayButton extends LitElement {
*   #playback = new StoreController(this, storeSource, selectPlayback);
*
*   render() {
*     const playback = this.#playback.value;
*     if (!playback) return nothing;
*     return html`<button @click=${playback.toggle}>
*       ${playback.paused ? 'Play' : 'Pause'}
*     </button>`;
*   }
* }
* ```
*/
var StoreController = class {
	#host;
	#selector;
	#accessor;
	#snapshot = null;
	constructor(host, source, selector) {
		this.#host = host;
		this.#selector = selector;
		this.#accessor = new StoreAccessor(host, source, (store) => this.#connect(store));
		host.addController(this);
	}
	get value() {
		const store = this.#accessor.value;
		if (isNull(store)) throw new Error("Store not available");
		if (isUndefined(this.#selector)) return store;
		return this.#snapshot.value;
	}
	hostConnected() {}
	#connect(store) {
		if (isUndefined(this.#selector)) return;
		if (!this.#snapshot) this.#snapshot = new SnapshotController(this.#host, store.$state, this.#selector);
		else this.#snapshot.track(store.$state);
	}
};

//#endregion
//#region src/player/player-controller.ts
/**
* Reactive controller for accessing player store state.
*
* Without selector: Returns the store, does NOT subscribe to changes.
* With selector: Returns selected state, subscribes with shallowEqual comparison.
*
* @example
* ```ts
* // Store access (no subscription)
* class Controls extends MediaElement {
*   #player = new PlayerController(this, playerContext);
*
*   handleClick() {
*     this.#player.value.setVolume(0.5);
*   }
* }
*
* // Selector-based subscription
* class PlayButton extends MediaElement {
*   #playback = new PlayerController(this, playerContext, selectPlayback);
* }
* ```
*/
var PlayerController = class {
	#host;
	#selector;
	#consumer;
	#store = null;
	constructor(host, context, selector) {
		this.#host = host;
		this.#selector = selector;
		this.#consumer = new ContextConsumer(host, {
			context,
			callback: (ctx) => this.#connect(ctx),
			subscribe: true
		});
		host.addController(this);
	}
	get value() {
		const store = this.#consumer.value;
		if (!store) return void 0;
		if (!this.#selector) return store;
		return this.#store?.value;
	}
	get displayName() {
		return this.#selector?.displayName;
	}
	hostConnected() {
		const store = this.#consumer.value;
		if (store) this.#connect(store);
	}
	hostDisconnected() {
		this.#store = null;
	}
	#connect(store) {
		if (!this.#store && this.#selector) this.#store = new StoreController(this.#host, store, this.#selector);
	}
};

//#endregion
//#region src/player/create-player.ts
function createPlayer(config) {
	const slice = combine(...config.features);
	function create() {
		return createStore()(slice);
	}
	return {
		context: playerContext,
		create,
		PlayerController,
		ProviderMixin: createProviderMixin(playerContext, create),
		ContainerMixin: createContainerMixin(playerContext)
	};
}

//#endregion
//#region src/define/safe-define.ts
/** Define a custom element only if not already registered. */
function safeDefine(element) {
	if (!customElements.get(element.tagName)) customElements.define(element.tagName, element);
}

//#endregion
//#region src/define/background/player.ts
const { ProviderMixin } = createPlayer({ features: backgroundFeatures });
var BackgroundVideoPlayerElement = class extends ProviderMixin(MediaElement) {
	static {
		this.tagName = "background-video-player";
	}
};
safeDefine(BackgroundVideoPlayerElement);
safeDefine(MediaContainerElement);

//#endregion
//#region src/define/background/skin.ts
function getTemplateHTML(_attrs) {
	return `
    <media-container>
      <slot name="media" slot="media"></slot>
    </media-container>
  `;
}
var BackgroundVideoSkinElement = class extends ReactiveElement {
	static {
		this.tagName = "background-video-skin";
	}
	static {
		this.shadowRootOptions = { mode: "open" };
	}
	static {
		this.getTemplateHTML = getTemplateHTML;
	}
	constructor() {
		super();
		if (!this.shadowRoot) {
			this.attachShadow(this.constructor.shadowRootOptions);
			this.shadowRoot.innerHTML = getTemplateHTML(namedNodeMapToObject(this.attributes));
		}
	}
};
safeDefine(BackgroundVideoSkinElement);

//#endregion
//# sourceMappingURL=background.dev.js.map