(() => {
    'use strict';

    window.FotogenicAI = window.FotogenicAI || {};
    const Studio = window.FotogenicAI.Studio = window.FotogenicAI.Studio || {};
    const { __, _n, sprintf } = Studio.i18n;
    const { isRecord } = Studio.store;

	const createSnapshotErrorPresenter = ({ renderReadiness, notify, reload }) => {
		if (typeof notify?.error !== 'function') {
			throw new TypeError('Fotogenic AI: notification system is required for snapshot errors.');
		}
		return (message, dedupeKey) => {
			renderReadiness();
			return notify.error(message, {
				title: __( 'Generation did not start', 'fotogenic-ai' ),
				ttlMs: 0,
				dedupeKey,
				actions: [{
					label: __( 'Reload Studio', 'fotogenic-ai' ),
					onClick: reload,
				}],
			});
		};
	};

    const createApp = async ({ root, boot = {}, confirmDiscard = window.confirm?.bind(window), onAuthExpired } = {}) => {
        const modules = Studio;
        const shell = modules.shell.createShell({ root });
		let authConnected = boot.auth?.connected === true;
		const handleTransportAuthExpired = (detail) => {
			authConnected = false;
			if (typeof onAuthExpired === 'function') onAuthExpired(detail);
		};
		if (typeof modules.notify?.error !== 'function') {
			const reason = __( 'Studio notifications are unavailable. Reload Studio and try again.', 'fotogenic-ai' );
			shell.showBlocking(reason);
			return Object.freeze({
				blocked: true,
				reason,
				destroy() {},
			});
		}
        const sourceMount = root.querySelector('[data-fa-mount="source"]');
        const sourceTypesMount = root.querySelector('[data-fa-mount="source-types"]');
        const recipeMount = root.querySelector('[data-fa-mount="recipe"]');
		const runSettingsMount = root.querySelector('[data-fa-mount="run-settings"]');
		const recipeToolsMount = root.querySelector('[data-fa-mount="recipe-tools"]');
        const summaryMount = root.querySelector('[data-fa-mount="summary"]');
        const recipeEmpty = root.querySelector('[data-fa-empty="recipe"]');
        const selectionSummary = root.querySelector('[data-fa-selection-summary]');
        const validation = root.querySelector('[data-fa-validation]');
        const configureButton = root.querySelector('[data-fa-action="configure"]');
		const configureBackButton = root.querySelector('[data-fa-action="configure-back"]');
		const configureBackLabel = configureBackButton?.querySelector('[data-fa-configure-back-label]');
		const configureNextButton = root.querySelector('[data-fa-action="configure-next"]');
        const reviewButton = root.querySelector('[data-fa-action="review"]');
        const workflowControl = root.querySelector('[data-fa-control="workflow"]');
        const transport = modules.transport.createTransport({ boot, onAuthExpired: handleTransportAuthExpired });
        const estimateCache = modules.summary.createEstimateCache({
            pricing: boot.moduleData?.pricing, balance: boot.creditsBalance,
            creditsCache: window.FotogenicAI.Shared?.creditsCache,
            fetchContext: (options) => transport.getEstimateContext(options),
        });
        let updateReviewCredits = () => {};

        if (workflowControl) workflowControl.disabled = true;
        if (configureButton) configureButton.disabled = true;
        if (reviewButton) reviewButton.disabled = true;
        if (validation) validation.textContent = __( 'Loading the current Studio workflow definition…', 'fotogenic-ai' );

        const bootManifestResult = modules.store.validateManifest(boot.workflowManifest);
        if (!bootManifestResult.ok) {
            shell.showBlocking(bootManifestResult.error);
            return Object.freeze({
                blocked: true,
                reason: bootManifestResult.error,
                destroy() {},
            });
        }

        const manifestResponse = await transport.getWorkflowManifest();
        const serverManifestResult = manifestResponse.ok
            ? modules.store.validateManifest(manifestResponse.data)
            : { ok: false, error: manifestResponse.message || __( 'The Studio workflow definition is unavailable.', 'fotogenic-ai' ) };
        if (!serverManifestResult.ok) {
            shell.showBlocking(serverManifestResult.error);
            return Object.freeze({
                blocked: true,
                reason: serverManifestResult.error,
                destroy() {},
            });
        }
        if (
            modules.store.stableStringify(bootManifestResult.manifest)
            !== modules.store.stableStringify(serverManifestResult.manifest)
        ) {
            shell.showBlocking(__( 'The Studio workflow definition changed while this page was loading. Refresh the page before continuing.', 'fotogenic-ai' ));
            return Object.freeze({
                blocked: true,
                reason: __( 'Workflow definition mismatch.', 'fotogenic-ai' ),
                destroy() {},
            });
        }

        const manifest = serverManifestResult.manifest;
        manifest.optionSources = {};
        let configurationState = 'disconnected';
        if (authConnected) {
            const cachedOptionSources = modules.transport.normalizeConfigurationOptionSources(boot.moduleData?.configuration || {});
            const configurationResponse = await transport.getConfigurationOptions();
            if (authConnected) {
                manifest.optionSources = { ...(configurationResponse.ok ? configurationResponse.data : cachedOptionSources) };
                configurationState = configurationResponse.ok || modules.transport.hasConfigurationOptions(manifest.optionSources)
                    ? 'ready' : 'error';
            }
        }
        let configurationController = null;
        let configurationToken = 0;
        const wooAvailable = boot.woocommerceAvailable === true;
        const requestedRoute = modules.store.normalizeInitialRoute(
            isRecord(boot.initialContext) ? boot.initialContext : {},
            root.dataset || {},
            manifest,
            wooAvailable
        );
        if (!requestedRoute) {
            shell.showBlocking(__( 'No supported Studio workflow is available for this site.', 'fotogenic-ai' ));
            return Object.freeze({
                blocked: true,
                reason: __( 'No supported workflow.', 'fotogenic-ai' ),
                destroy() {},
            });
        }

        const operationalLimits = manifest.operationalLimits;
        const storedRun = modules.runController.readStoredIdentity(window.sessionStorage, operationalLimits);
        const storedRoute = storedRun ? {
            workflow: storedRun.workflow,
            sourceType: storedRun.sourceType,
            scope: storedRun.scope,
        } : null;
        const initialRoute = storedRoute && modules.store.routeSupported(manifest, storedRoute, wooAvailable)
            ? storedRoute
            : requestedRoute;

        const store = modules.store.createStore({ manifest, initialRoute });
        const selectedItems = new Map();
        const listeners = [];
        let sourcePicker = null;
        let recipeForm = null;
        let summary = null;
        let runController = null;
        let preflightController = null;
        let preflightToken = 0;
        let snapshotController = null;
        let snapshotToken = 0;
		let pendingStoreQueue = null;
        let bulkMeta = { matchingCount: 0, selectedCount: 0, maximum: 1, strategy: 'explicit' };
        let hydrateToken = 0;
        let generationCheck = null;
        let reviewReady = false;
        let destroyed = false;
        let lastReadiness = null;
        let reviewFeedbackId = null;

        if (!sourceMount || !sourceTypesMount || !recipeMount || !summaryMount || !validation || !reviewButton) {
            shell.showBlocking(__( 'The Studio interface could not be initialized.', 'fotogenic-ai' ));
            return Object.freeze({
                blocked: true,
                reason: __( 'Required Studio mount is missing.', 'fotogenic-ai' ),
                destroy() {},
            });
        }
        if (workflowControl) workflowControl.disabled = false;

        const getRoute = () => store.getState().route;
        const getSelectedIds = () => store.getCanonical().draft.selection.selectedIds;
        const getSelection = () => store.getCanonical().draft.selection;
        const getSourcePolicy = () => store.getCanonical().draft.sourcePolicy;
        const getSourceDefinition = () => store.getDefinitions().source;
        const getExactItemCount = () => {
            const draft = store.getCanonical().draft;
            if (draft.workflow === 'campaign_set' && Array.isArray(draft.sourcePolicy?.groups)) {
                return draft.sourcePolicy.groups.length;
            }
            if (draft.selection?.strategy === 'query') {
                return Math.max(0, Math.floor(Number(bulkMeta.selectedCount)) || 0);
            }
            return Array.isArray(draft.selection?.selectedIds) ? draft.selection.selectedIds.length : 0;
        };
        const getSourceAttachmentLimit = () => {
            const route = store.getCanonical().draft;
            const source = getSourceDefinition();
            const scopeKey = route.scope === 'bulk' ? 'bulkItem' : 'single';
            let maximum = Math.max(1, Number(source?.inputCardinality?.[scopeKey]?.primary?.maximum) || 1);
            if (route.workflow === 'product_images') {
                const recipe = store.getCanonical().draft.recipe;
                const referenceCount = ['subjectImageId', 'inspirationImageId', 'environmentImageId']
                    .reduce((count, key) => count + (Number(recipe[key]) > 0 ? 1 : 0), 0);
				const totalMaximum = Math.max(1, Number(source?.inputCardinality?.[scopeKey]?.total?.maximum) || maximum);
                maximum = Math.min(maximum, Math.max(1, totalMaximum - referenceCount));
            }
            return maximum;
        };
        const preflightEndpointAvailable = () => modules.transport.safeRelativePath(boot.rest?.universalPreflight);
        const bulkSnapshotEndpointAvailable = () => modules.transport.safeRelativePath(boot.rest?.bulkSnapshot);
		const isStoreQueueDraft = (draft) => draft?.workflow === 'product_images'
			&& draft?.sourceType === 'product'
			&& draft?.scope === 'bulk'
			&& draft?.selection?.strategy === 'query';
        const bulkRuntimeAvailable = () => [
            'bulkStart',
            'bulkActive',
            'bulkProgress',
            'bulkPause',
            'bulkResume',
            'bulkStop',
        ].every((name) => modules.transport.safeRelativePath(boot.rest?.[name]));
		const storeQueueRuntimeAvailable = () => [
			'storeQueueSnapshot',
			'storeQueueStart',
			'storeQueueActive',
			'storeQueueProgress',
			'storeQueuePause',
			'storeQueueResume',
			'storeQueueStop',
		].every((name) => modules.transport.safeRelativePath(boot.rest?.[name]));

        const seedInitialContext = () => {
            const context = isRecord(boot.initialContext) ? boot.initialContext : {};
            const route = getRoute();
            const sourceId = Math.floor(Number(context.sourceId));
            const routeMatches = context.workflow === route.workflow
                && context.sourceType === route.sourceType
                && context.scope === route.scope;
            if (routeMatches && sourceId > 0 && isRecord(context.sourceData) && !getSelectedIds().length) {
                const item = modules.sourcePicker.normalizeItem(
                    { ...context.sourceData, id: sourceId },
                    route.sourceType
                );
                if (item?.eligible) {
                    selectedItems.set(item.id, item);
                    store.setSelection([item.id], 'initial-context');
                    if (['post', 'page'].includes(route.sourceType) && item.sourceAttachmentId > 0) {
                        store.setSourcePolicy({ attachmentIds: [item.sourceAttachmentId] });
                    }
                }
            }
        };

		const updateEmptyStates = () => {
			const selectedCount = getExactItemCount();
			const querySelection = store.getCanonical().draft.selection?.strategy === 'query';
			if (recipeEmpty) recipeEmpty.hidden = recipeMount.childElementCount > 0;
			if (selectionSummary) {
				selectionSummary.textContent = selectedCount > 0
					? (querySelection
						? __( 'Matching sources selected. Exact eligible items will be confirmed in Review.', 'fotogenic-ai' )
						: sprintf(
						_n( '%d exact item selected.', '%d exact items selected.', selectedCount, 'fotogenic-ai' ),
						selectedCount
						))
					: __( 'Select at least one exact source to continue.', 'fotogenic-ai' );
			}
            if (configureButton) configureButton.disabled = selectedCount < 1;
            shell.setStageAvailability({ configure: selectedCount > 0 });
        };

        const renderSummary = () => {
            const state = store.getState();
            const canonical = store.getCanonical();
            const { workflow } = store.getDefinitions();
            recipeForm?.updateErrors(canonical.issues);
            lastReadiness = summary.render({
                state,
                canonical,
                workflow,
                authConnected,
                preflightEndpointAvailable: preflightEndpointAvailable(),
                bulkSnapshotEndpointAvailable: bulkSnapshotEndpointAvailable(),
                bulkMeta,
            });
            updateEmptyStates();
            if (!authConnected || (configurationState !== 'ready' && workflow.id !== 'increase_resolution')) {
                reviewButton.disabled = true;
                validation.hidden = true;
                lastReadiness = { ...lastReadiness, ready: false };
            }
            return lastReadiness;
        };

		const notifyActivity = (type, message, dedupeKey) => {
			Studio.notify?.[type]?.(message, {
				dedupeKey,
			});
		};
		const notifyReviewFeedback = ({ type = 'info', title = '', message = '', dedupeKey = '' } = {}) => {
			if (!message || typeof Studio.notify?.[type] !== 'function') return;
            if (reviewFeedbackId) Studio.notify.dismiss(reviewFeedbackId);
			reviewFeedbackId = Studio.notify[type](message, {
				title,
				ttlMs: 0,
				dedupeKey,
			});
		};

		const reloadStudio = () => window.location.reload();
		const showSnapshotError = createSnapshotErrorPresenter({
			renderReadiness: renderSummary,
			notify: Studio.notify,
			reload: reloadStudio,
		});
        const cancelPreflight = () => {
            preflightToken += 1;
            preflightController?.abort?.();
            preflightController = null;
        };

        const cancelSnapshot = () => {
            snapshotToken += 1;
            snapshotController?.abort?.();
            snapshotController = null;
			pendingStoreQueue = null;
        };

        const cancelGenerationCheck = () => {
            generationCheck = null;
            cancelPreflight();
            cancelSnapshot();
        };

        const runPreflight = async (canonicalDraft) => {
            if (destroyed) return null;
            const canonical = store.getCanonical();
            if (
				modules.store.stableStringify(canonicalDraft) !== modules.store.stableStringify(canonical.draft)
				||
                canonical.draft.selection.selectedIds.length === 0
                || canonical.issues.length > 0
                || canonical.draft.scope === 'bulk'
                || !authConnected
                || !preflightEndpointAvailable()
            ) {
				return null;
            }
            const canonicalString = modules.store.stableStringify(canonical.draft);
			cancelPreflight();
            const token = ++preflightToken;
            preflightController = typeof AbortController === 'function' ? new AbortController() : null;
            store.setPreflightPending(canonicalString);
            const result = await transport.preflight(canonical.draft, { signal: preflightController?.signal });
            if (destroyed || token !== preflightToken || result.aborted) {
				return null;
            }
            preflightController = null;
            if (!result.ok) {
                store.setPreflightError(canonicalString, result.message || __( 'Authoritative pricing is unavailable.', 'fotogenic-ai' ));
				return null;
            }
            const exact = modules.summary.normalizeAuthoritativePreflight(result.data);
            if (!exact) {
                store.setPreflightError(canonicalString, __( 'Authoritative pricing is unavailable.', 'fotogenic-ai' ));
				return null;
            }
			store.setPreflightReady(canonicalString, exact);
			return exact;
        };

        const hydrateSelectedItems = async () => {
            const token = ++hydrateToken;
            const route = getRoute();
            const ids = getSelectedIds().filter((id) => !selectedItems.has(id));
            const batchSize = 5;
            for (let offset = 0; offset < ids.length; offset += batchSize) {
                await Promise.all(ids.slice(offset, offset + batchSize).map(async (id) => {
                    const result = await transport.getSourceSummary(route.sourceType, id);
                    if (destroyed || token !== hydrateToken || !result.ok) return;
                    const item = modules.sourcePicker.normalizeItem(result.data, route.sourceType);
                    if (item) {
                        selectedItems.set(item.id, item);
                        if (
                            store.getCanonical().draft.scope === 'single'
                            && ['post', 'page'].includes(route.sourceType)
                            && item.sourceAttachmentId > 0
                            && !getSourcePolicy().attachmentIds?.length
                        ) {
                            store.setSourcePolicy({ attachmentIds: [item.sourceAttachmentId] });
                        }
                    }
                }));
                if (destroyed || token !== hydrateToken) return;
            }
            if (!destroyed && token === hydrateToken) {
                sourcePicker?.update(route);
				recipeForm?.updateContext();
            }
        };

        const confirmRouteChange = () => {
			if (shell.getCurrentView() === 'select') return true;
            if (!store.isDirty()) return true;
            return typeof confirmDiscard !== 'function' || confirmDiscard(
                __( 'Changing workflow or source will switch to a separate saved draft. Continue?', 'fotogenic-ai' )
            );
        };

        const requestRoute = (partial) => {
            if (runController?.hasStoredRun?.()) {
                if (!runController.leaveTerminalRun?.()) {
					const current = getRoute();
					shell.syncHeaderControls({ manifest, route: current, wooAvailable });
					shell.announce(__( 'Finish or leave the current generation view before changing workflows.', 'fotogenic-ai' ));
					return;
				}
            }
			const current = getRoute();
            const preferred = { ...current, ...partial };
            const next = modules.store.firstSupportedRoute(manifest, preferred, wooAvailable);
            if (!next || modules.store.stableStringify(next) === modules.store.stableStringify(current)) {
                shell.syncHeaderControls({ manifest, route: current, wooAvailable });
                return;
            }
            if (!confirmRouteChange()) {
                shell.syncHeaderControls({ manifest, route: current, wooAvailable });
                return;
            }
            cancelGenerationCheck();
            bulkMeta = { matchingCount: 0, selectedCount: 0, maximum: 1, strategy: 'explicit' };
            reviewReady = false;
            shell.setStageAvailability({ select: true, configure: false, generate: false, summary: false });
            selectedItems.clear();
            store.setRoute(next);
			if (current.sourceType !== next.sourceType) store.clearSourceSelection();
        };

		const resetComposeDraft = (requestedRoute) => {
			if (
				!isRecord(requestedRoute)
				|| Object.keys(requestedRoute).sort().join(',') !== 'scope,sourceType,workflow'
			) return false;
			let restoredRoute = requestedRoute;
			const wooFallback = !modules.store.routeSupported(manifest, restoredRoute, wooAvailable)
				&& !wooAvailable
				&& requestedRoute.sourceType === 'product';
			if (wooFallback) {
				restoredRoute = modules.store.firstSupportedRoute(manifest, requestedRoute, wooAvailable);
			}
			if (!restoredRoute) return false;
			if (!modules.store.routeSupported(manifest, restoredRoute, wooAvailable)) return false;
			const currentRoute = getRoute();
			const composeRoute = ['product_images', 'campaign_set', 'increase_resolution'].includes(restoredRoute.workflow) && restoredRoute.scope !== 'bulk'
				? modules.store.firstSupportedRoute(manifest, { ...restoredRoute, scope: 'bulk' }, wooAvailable)
				: restoredRoute;
			if (!composeRoute || !modules.store.routeSupported(manifest, composeRoute, wooAvailable)) return false;
			hydrateToken += 1;
			if (modules.store.stableStringify(composeRoute) !== modules.store.stableStringify(currentRoute)) {
				store.setRoute(composeRoute);
				hydrateToken += 1;
			}
            selectedItems.clear();
            reviewReady = false;
            store.clearDrafts();
			sourcePicker?.update(composeRoute, { rehydrateVisible: true });
            store.setView('select');
			shell.setStageAvailability({ select: true, configure: false, generate: false, summary: false });
			shell.setView('select', { announcement: __( 'Generation draft cleared. Select a source for a new run.', 'fotogenic-ai' ) });
			return true;
		};

        const renderRoute = () => {
            const route = getRoute();
            const { workflow } = store.getDefinitions();
            shell.syncHeaderControls({ manifest, route, wooAvailable });
            const restoreSourceTypeFocus = shell.sourceTypeHasFocus();
            sourcePicker.update(route);
            shell.renderSourceTypes({
                workflow,
                route,
                wooAvailable,
                restoreFocus: restoreSourceTypeFocus,
                onChange(sourceType) {
                    requestRoute({ sourceType });
                },
            });
            recipeForm.render(workflow, route);
            store.setView('select');
            shell.setStageAvailability({ select: true, configure: false, generate: false, summary: false });
            shell.setView('select', { focus: false });
            renderSummary();
            hydrateSelectedItems();
			cancelPreflight();
        };

        sourcePicker = modules.sourcePicker.createSourcePicker({
            mount: sourceMount,
            sourceTypesMount,
            transport,
            getRoute,
            getSelectedIds,
            getSelection,
            getSourcePolicy,
            getSourceDefinition,
			getSourceAttachmentLimit,
			getCampaignGroupDefaults() {
				const canonical = store.getCanonical().draft;
				return {
					recipe: modules.store.clone(canonical.recipe || {}),
					resultPolicy: modules.store.clone(canonical.resultPolicy || {}),
				};
			},
			selectedItems,
			onSelectionChange(selection) {
                if (selection.strategy === 'query') {
					return store.setBulkQuery(selection.query, {
                        selectedIds: selection.selectedIds,
                        excludedIds: selection.excludedIds,
                    });
                }
                store.setSelection(selection.selectedIds);
				if (['post', 'page'].includes(getRoute().sourceType)) {
					const selectedIds = getSelectedIds();
					const selected = selectedIds.length === 1 ? selectedItems.get(Number(selectedIds[0])) : null;
					store.setSourcePolicy(
						selected?.sourceAttachmentId > 0
							? { attachmentIds: [selected.sourceAttachmentId] }
							: {}
					);
				}
				return true;
            },
            onBulkMeta(meta) {
                bulkMeta = { ...bulkMeta, ...meta };
                if (summary) renderSummary();
            },
            onSourcePolicyChange(sourcePolicy) {
                store.setSourcePolicy(sourcePolicy);
            },
            onSourceServiceError(error, actions = {}) {
                Studio.errors?.normalizeAndReport?.(
                    error,
                    { source: 'network' },
                    {
                        onRetry: actions.retry,
                        onExit: actions.exit,
                        retryLabel: __( 'Retry sources', 'fotogenic-ai' ),
                    }
                );
            },
        });
        const retryConfiguration = async () => {
            if (destroyed || !authConnected || configurationState === 'loading') return;
            configurationState = 'loading';
            const token = ++configurationToken;
            configurationController = new AbortController();
            recipeForm.render(store.getDefinitions().workflow, getRoute());
            renderSummary();
            const result = await transport.getConfigurationOptions({ signal: configurationController.signal });
            if (destroyed || token !== configurationToken || result.aborted) return;
            configurationController = null;
            configurationState = result.ok ? 'ready' : 'error';
            if (result.ok) manifest.optionSources = { ...result.data };
            recipeForm.render(store.getDefinitions().workflow, getRoute());
            renderSummary();
        };
        recipeForm = modules.recipeForm.createRecipeForm({
            mount: recipeMount,
			runSettingsMount,
			toolsMount: recipeToolsMount,
            manifest,
            getState: () => store.getState(),
            getCanonical: () => store.getCanonical(),
            transport,
            isAuthConnected: () => authConnected,
            getConfigurationState: () => configurationState,
            onRetryConfiguration: retryConfiguration,
            onChange(recipe) {
                store.setRecipe(recipe);
            },
            onSourcePolicyChange(sourcePolicy) {
                store.setSourcePolicy(sourcePolicy);
            },
            onResultPolicyChange(resultPolicy) {
                store.setResultPolicy(resultPolicy);
            },
			onSectionChange(navigation) {
				const hasNext = navigation?.hasSections === true && navigation?.hasNext === true;
				if (configureBackLabel) {
					configureBackLabel.textContent = getRoute().workflow === 'increase_resolution'
						? __( 'Back to selection', 'fotogenic-ai' )
						: __( 'Back', 'fotogenic-ai' );
				}
				if (configureNextButton) configureNextButton.hidden = !hasNext;
				reviewButton.hidden = hasNext;
				validation.hidden = hasNext;
			},
			onSectionReset(sectionLabel) {
				const message = sprintf(
					__( '%s restored to its default settings.', 'fotogenic-ai' ),
					sectionLabel
				);
				notifyActivity('success', message, 'studio-section-reset');
			},
			onRecipeReset() {
				notifyActivity(
					'success',
					__( 'The full recipe was restored to its default settings.', 'fotogenic-ai' ),
					'studio-recipe-reset'
				);
			},
			onAutomaticSettingsApplied() {
				notifyActivity(
					'success',
					__( 'Automatic settings applied. Review the recipe or continue when ready.', 'fotogenic-ai' ),
					'studio-automatic-settings'
				);
			},
			onBackToSelection() {
                cancelGenerationCheck();
				store.setView('select');
				shell.setView('select', { announcement: __( 'Select step.', 'fotogenic-ai' ) });
			},
			getRunItemCount: getExactItemCount,
            getSelectedItems() {
                return [...selectedItems.values()];
            },
        });
        summary = modules.summary.createSummary({
            mount: summaryMount,
            validation,
            reviewButton,
            onFixIssue(issue) {
                recipeForm.focusIssue(issue);
            },
        });
        runController = modules.runController.createRunController({
            transport,
            shell,
			operationalLimits,
            mounts: {
                progress: root.querySelector('[data-fa-mount="progress"]'),
                error: root.querySelector('[data-fa-mount="error"]'),
                results: root.querySelector('[data-fa-mount="results"]'),
            },
            confirmAction: typeof window.confirm === 'function' ? window.confirm.bind(window) : undefined,
			getSource(subjectId) {
                const item = selectedItems.get(Number(subjectId));
                return item ? {
                    label: item.label,
                    imageUrl: item.imageUrl,
                    attachmentId: item.sourceAttachmentId || (getRoute().sourceType === 'media' ? item.id : 0),
                } : null;
			},
			onCreditsRefreshRequested(detail) {
				window.dispatchEvent(new CustomEvent('fotogenic-ai:credits-refresh-requested', {
					detail,
				}));
			},
			onRunLifecycle({ status, runId }) {
				const presentation = {
					started: ['info', __( 'Generation started', 'fotogenic-ai' ), __( 'Your generation is running. You can follow it from Run history.', 'fotogenic-ai' )],
					completed: ['success', __( 'Generation completed', 'fotogenic-ai' ), __( 'Your generated images are ready to review.', 'fotogenic-ai' )],
					partial: ['warning', __( 'Generation partially completed', 'fotogenic-ai' ), __( 'Saved images are ready, and some requested outputs need attention.', 'fotogenic-ai' )],
					failed: ['error', __( 'Generation failed', 'fotogenic-ai' ), __( 'The run stopped without completing all requested outputs.', 'fotogenic-ai' )],
					stopped: ['warning', __( 'Generation stopped', 'fotogenic-ai' ), __( 'Completed images were kept and pending items were stopped.', 'fotogenic-ai' )],
				}[status];
				if (!presentation || typeof Studio.notify?.[presentation[0]] !== 'function') return;
				Studio.notify[presentation[0]](presentation[2], {
					title: presentation[1],
					dedupeKey: `generation:${status}`,
					episode: runId,
				});
			},
			onNavigationStateChange(state) {
				shell.setStageAvailability({ summary: state?.summaryAvailable === true });
			},
			onBackToCompose() {
				store.setView('configure');
			},
            onReturnToReview() {
                if (onReview()) return;
                if (getExactItemCount() > 0) openConfigure();
                else openSelect();
            },
			onCreateAnother(route) {
				return resetComposeDraft(route);
            },
        });

		const unsubscribe = store.subscribe((state, reason) => {
            if (destroyed) return;
            if (reason === 'route') {
                renderRoute();
                return;
            }
			if (['selection', 'bulk-query', 'source-policy', 'result-policy', 'recipe', 'drafts-cleared'].includes(reason)) {
                cancelGenerationCheck();
                reviewReady = false;
                shell.setStageAvailability({ generate: false, summary: false });
                sourcePicker.update(state.route);
            }
            recipeForm.updateContext();
            renderSummary();
			if (['initial-context', 'forbidden-recipe'].includes(reason)) {
                cancelGenerationCheck();
            }
        });

        const onWorkflowChange = () => requestRoute({ workflow: workflowControl.value });
        workflowControl?.addEventListener('change', onWorkflowChange);
        if (workflowControl) listeners.push(() => workflowControl.removeEventListener('change', onWorkflowChange));

        const openReview = (canonical, estimate = (store.isPreflightCurrent()
			? modules.summary.normalizeAuthoritativePreflight(store.getState().preflight.data) : null) || estimateCache.estimate({
            draft: canonical,
            selectedCount: getExactItemCount(),
        })) => {
            const reviewedCanonical = store.getCanonicalString();
			const storeQueueDraft = isStoreQueueDraft(canonical);
			const canGenerate = authConnected
                && preflightEndpointAvailable()
				&& (canonical.scope === 'single' || (storeQueueDraft ? storeQueueRuntimeAvailable() : bulkRuntimeAvailable()));
            const { workflow } = store.getDefinitions();
			const blockedReason = canonical.scope === 'bulk'
				? (storeQueueDraft
					? __( 'The complete Product queue route set is unavailable.', 'fotogenic-ai' )
					: __( 'The complete Bulk start and recovery route set is unavailable.', 'fotogenic-ai' ))
                : __( 'Reconnect Fotogenic or reload Studio before generating.', 'fotogenic-ai' );
            const projection = shell.renderReview({
                canonicalDraft: canonical,
                estimate,
                selectedItems,
                workflow,
                manifest,
                canGenerate,
                onLoadEstimate: async (options) => {
                    const next = await estimateCache.load({ draft: canonical, selectedCount: getExactItemCount() }, options);
                    if (!generationCheck && authConnected && reviewedCanonical === store.getCanonicalString()) estimate = next;
                    return estimate;
                },
				generateReason: blockedReason,
                onLoadSourcePage: storeQueueDraft ? async (options) => {
                    const result = await transport.getProductSelectionPage(canonical.selection, options);
                    if (!result.ok) return result;
                    return {
                        ...result,
                        data: {
                            ...result.data,
                            items: result.data.items.map((item) => modules.sourcePicker.normalizeItem(item, 'product')).filter(Boolean)
                                .map((item) => ({
                                    ...item,
                                    eligible: modules.sourcePicker.sourceEligibleForMode(item, canonical, canonical.sourcePolicy?.sourceMode),
                                })),
                        },
                    };
                } : undefined,
                onRemoveSource(source) {
                    if (!source || generationCheck || shell.getCurrentView() !== 'review') return;
                    cancelGenerationCheck();
                    const currentDraft = store.getCanonical().draft;
                    const removedIds = new Set((Array.isArray(source.children) ? source.children : [])
                        .map((child) => Number(child?.platformId))
                        .filter((id) => Number.isSafeInteger(id) && id > 0));
                    if (!removedIds.size && Number(source.platformId) > 0) removedIds.add(Number(source.platformId));
                    const currentIds = Array.isArray(currentDraft.selection?.selectedIds)
                        ? currentDraft.selection.selectedIds.map(Number).filter((id) => Number.isSafeInteger(id) && id > 0)
                        : [];
                    let nextIds = currentIds.filter((id) => !removedIds.has(id));
                    if (currentDraft.selection?.strategy === 'query') {
                        const excludedIds = [...new Set([
                            ...(currentDraft.selection.excludedIds || []),
                            ...[...removedIds].filter((id) => !currentIds.includes(id)),
                        ])];
                        bulkMeta.selectedCount = Math.max(0, getExactItemCount() - removedIds.size);
                        store.setBulkQuery(currentDraft.selection.query, { selectedIds: nextIds, excludedIds });
                        if (getExactItemCount() < 1) openSelect();
                        else openReview(store.getCanonical().draft);
                        return;
                    }
                    if (currentDraft.workflow === 'campaign_set') {
                        const groups = (Array.isArray(currentDraft.sourcePolicy?.groups) ? currentDraft.sourcePolicy.groups : [])
                            .filter((group) => !removedIds.has(Number(group.subjectId)));
                        nextIds = [...new Set(groups.map((group) => Number(group.subjectId)).filter((id) => id > 0))];
                        store.setSelection(nextIds);
                        store.setSourcePolicy({ ...currentDraft.sourcePolicy, groups });
                    } else {
                        store.setSelection(nextIds);
                    }
                    if (!nextIds.length) {
                        openSelect();
                        return;
                    }
                    const readiness = renderSummary();
                    if (readiness?.ready) openReview(store.getCanonical().draft, readiness.exact || undefined);
                },
                async onGenerate() {
                    if (generationCheck || destroyed || !authConnected || !canGenerate
                        || runController.hasStoredRun() || shell.getCurrentView() !== 'review'
                        || reviewedCanonical !== store.getCanonicalString()) return false;
                    const check = {};
                    generationCheck = check;
                    if (reviewFeedbackId) Studio.notify.dismiss(reviewFeedbackId);
                    reviewFeedbackId = null;
                    try {
                        const exact = canonical.scope === 'bulk'
							? (storeQueueDraft ? await prepareStoreQueueSnapshot(canonical) : await prepareBulkSnapshot(canonical))
                            : await runPreflight(canonical);
                        if (generationCheck !== check || destroyed || !authConnected || shell.getCurrentView() !== 'review') return false;
                        const currentDraft = store.getCanonical().draft;
                        if (!exact) {
                            if (reviewedCanonical !== store.getCanonicalString()) return false;
                            openReview(currentDraft, estimate);
                            if (canonical.scope !== 'bulk') notifyReviewFeedback({
                                type: 'error', title: __( 'Generation did not start', 'fotogenic-ai' ),
                                message: store.getState().preflight.error || __( 'Pricing could not be checked. Try again. No credits were used.', 'fotogenic-ai' ),
                                dedupeKey: 'studio-final-pricing-failed',
                            });
                            return false;
                        }
						const creditsPerItem = exact.totalItems > 0 ? exact.totalCredits / exact.totalItems : exact.totalCredits;
						const partialQueueBalance = exact.storeQueue === true
							&& exact.balance + 0.000001 >= creditsPerItem
							&& exact.balanceAfter < 0;
						const insufficient = exact.storeQueue === true
							? exact.balance + 0.000001 < creditsPerItem
							: exact.balance + 0.000001 < exact.totalCredits || exact.balanceAfter < 0;
                        const changed = reviewedCanonical !== store.getCanonicalString()
                            || estimate.totalItems !== exact.totalItems || estimate.totalOutputs !== exact.totalOutputs;
                        const higherPrice = estimate.totalCredits === null || exact.totalCredits > estimate.totalCredits + 0.000001;
                        if (insufficient || changed || higherPrice) {
                            openReview(currentDraft, exact);
                            notifyReviewFeedback({
                                type: insufficient ? 'error' : 'warning',
                                title: insufficient ? __( 'Not enough credits', 'fotogenic-ai' )
									: changed ? __( 'Selection updated', 'fotogenic-ai' )
										: estimate.totalCredits === null ? __( 'Price confirmed', 'fotogenic-ai' ) : __( 'Price changed', 'fotogenic-ai' ),
                                message: insufficient
                                    ? __( 'Your balance is too low for this run. Add credits, then try again. No generation has started.', 'fotogenic-ai' )
									: changed ? __( 'The exact run items or output count changed. Review the updated summary, then click Generate to confirm. No credits were used.', 'fotogenic-ai' )
										: sprintf(__( 'The current price is %s credits. Click Generate to approve this charge. No credits were used.', 'fotogenic-ai' ), new Intl.NumberFormat(undefined, { maximumFractionDigits: 2 }).format(exact.totalCredits)),
                                dedupeKey: 'studio-final-review-updated',
                            });
                            return false;
                        }
						let allowPartialBalance = false;
						if (partialQueueBalance) {
							allowPartialBalance = confirmDiscard(__( 'Your current balance covers only part of this Product queue. Start now and pause automatically when credits run out?', 'fotogenic-ai' ));
							if (!allowPartialBalance) return false;
						}
						const start = exact.storeQueue === true
							? runController.startStoreQueue
							: (currentDraft.scope === 'bulk' ? runController.startBulk : runController.startSingle);
                        const selectedItem = currentDraft.scope === 'single'
                            ? selectedItems.get(Number(currentDraft.selection.selectedIds[0])) : null;
                        return await start({
                            draft: currentDraft, exact, target: selectedItem?.target || null,
                            snapshotFingerprint: currentDraft.scope === 'bulk' ? store.getBulkSnapshot()?.snapshotFingerprint : '',
                            preflightCurrent: store.isPreflightCurrent(),
							allowPartialBalance,
                        });
                    } finally {
                        if (generationCheck === check) generationCheck = null;
                    }
                },
				onFeedback: notifyReviewFeedback,
                onEdit() {
                    cancelGenerationCheck();
                    store.setView('configure');
                    shell.setView('configure', { announcement: __( 'Configure step.', 'fotogenic-ai' ) });
                },
            });
            updateReviewCredits = () => {
                if (destroyed || !authConnected || generationCheck || shell.getCurrentView() !== 'review'
                    || reviewedCanonical !== store.getCanonicalString()) return;
                const { balance } = estimateCache.estimate({ draft: canonical, selectedCount: getExactItemCount() });
                if (balance === null) return;
                estimate = { ...estimate, balance, balanceAfter: estimate.totalCredits === null ? null : balance - estimate.totalCredits };
                projection?.updateEstimate(estimate);
            };
			if (!canGenerate) {
				notifyReviewFeedback({
					type: 'warning',
					title: __( 'Review needs attention', 'fotogenic-ai' ),
					message: blockedReason,
					dedupeKey: 'studio-review-blocked',
				});
			}
            reviewReady = true;
            shell.setStageAvailability({ generate: true });
            store.setView('review');
			shell.setView('review', { announcement: __( 'Generate step review. No generation has started.', 'fotogenic-ai' ) });
		};

		const prepareStoreQueueSnapshot = async (originDraft) => {
			const originCanonical = modules.store.stableStringify(originDraft);
			if (!isStoreQueueDraft(originDraft)) return false;
			if (!pendingStoreQueue || pendingStoreQueue.canonical !== originCanonical) {
				const queueUuid = String(window.FotogenicAI.Shared?.identity?.createSecureUuid?.() || '').toLowerCase();
				if (!/^[a-f0-9]{8}-[a-f0-9]{4}-[1-5][a-f0-9]{3}-[89ab][a-f0-9]{3}-[a-f0-9]{12}$/.test(queueUuid)) {
					reviewFeedbackId = showSnapshotError(
						__( 'Studio could not create a secure Product queue identity. Reload Studio and try again. No credits were used.', 'fotogenic-ai' ),
						'studio-store-queue-identity-failed'
					);
					return false;
				}
				pendingStoreQueue = { canonical: originCanonical, queueUuid };
			}
			const token = ++snapshotToken;
			snapshotController?.abort?.();
			snapshotController = typeof AbortController === 'function' ? new AbortController() : null;
			const querySelection = originDraft.selection;
			const query = querySelection.query || {};
			const categories = Array.isArray(query.categories) ? query.categories : [];
			const stock = Array.isArray(query.stock) ? query.stock : [];
			const search = String(query.search || '').trim();
			const strategy = !search && !categories.length && !stock.length
				? 'all'
				: (!search && categories.length && !stock.length ? 'categories' : 'query');
			const envelope = {
				queueUuid: pendingStoreQueue.queueUuid,
				draft: originDraft,
				selection: {
					strategy,
					search,
					categories,
					stock,
					selectedIds: Array.isArray(querySelection.selectedIds) ? querySelection.selectedIds : [],
					excludedIds: Array.isArray(querySelection.excludedIds) ? querySelection.excludedIds : [],
				},
			};
			for (let pageIndex = 0; pageIndex < 2000; pageIndex += 1) {
				const result = await transport.snapshotStoreQueue(envelope, { signal: snapshotController?.signal });
				if (
					destroyed
					|| token !== snapshotToken
					|| result.aborted
					|| originCanonical !== store.getCanonicalString()
				) return false;
				if (!result.ok) {
					snapshotController = null;
					reviewFeedbackId = showSnapshotError(
						result.message || __( 'Studio could not prepare this Product queue. No credits were used.', 'fotogenic-ai' ),
						'studio-store-queue-prepare-failed'
					);
					return false;
				}
				const queue = result.data?.queue;
				if (!queue || String(queue.queueUuid || '').toLowerCase() !== envelope.queueUuid) return false;
				const exact = modules.summary.normalizeStoreQueueReview(result.data?.review);
				if (exact) {
					snapshotController = null;
					pendingStoreQueue.review = exact;
					return exact;
				}
				if (['paused', 'failed', 'stopped', 'uncertain'].includes(String(queue.status || ''))) {
					snapshotController = null;
					reviewFeedbackId = showSnapshotError(
						__( 'Product queue preparation needs attention. No credits were used.', 'fotogenic-ai' ),
						'studio-store-queue-prepare-paused'
					);
					return false;
				}
				await new Promise((resolve) => window.setTimeout(resolve, 120));
			}
			snapshotController = null;
			reviewFeedbackId = showSnapshotError(
				__( 'Product queue preparation did not finish in this browser session. No credits were used.', 'fotogenic-ai' ),
				'studio-store-queue-prepare-timeout'
			);
			return false;
		};

		const prepareBulkSnapshot = async (originDraft) => {
            const originCanonical = modules.store.stableStringify(originDraft);
            const originSelection = originDraft.selection;
            const token = ++snapshotToken;
            snapshotController?.abort?.();
            snapshotController = typeof AbortController === 'function' ? new AbortController() : null;
            const result = await transport.snapshotBulk(originDraft, { signal: snapshotController?.signal });
            if (
                destroyed
                || token !== snapshotToken
                || result.aborted
                || originCanonical !== store.getCanonicalString()
            ) {
                return false;
            }
            snapshotController = null;
			if (!result.ok) {
				reviewFeedbackId = showSnapshotError(
					__( 'Studio could not check your selections, so generation did not start. Reload Studio and try again. No credits were used.', 'fotogenic-ai' ),
					'studio-generation-review-request-failed'
				);
                return false;
            }
            const payload = result.data;
            const exact = modules.summary.normalizeAuthoritativePreflight(payload.preflight);
            const ids = Array.isArray(payload.draft?.selection?.selectedIds)
                ? payload.draft.selection.selectedIds.map(Number)
                : [];
            const summaries = (Array.isArray(payload.selected) ? payload.selected : [])
                .map((item) => modules.sourcePicker.normalizeItem(item, payload.draft?.sourceType))
                .filter(Boolean);
            const snapshotItemCount = payload.draft?.workflow === 'campaign_set'
                && Array.isArray(payload.draft?.sourcePolicy?.groups)
                ? payload.draft.sourcePolicy.groups.length
                : ids.length;
            const countDerivedScale = payload.draft?.workflow === 'product_images';
            const validScale = countDerivedScale
                ? payload.draft?.scope === (ids.length === 1 ? 'single' : 'bulk')
                : payload.draft?.scope === 'bulk';
            const exactSummaries = payload.draft?.workflow !== 'campaign_set'
                || payload.draft.sourcePolicy.groups.every((group) => {
                    const summary = summaries.find((item) => item.id === Number(group.subjectId));
                    return summary?.target
                        && summary.target.targetType === group.target?.targetType
                        && summary.target.targetId === Number(group.target?.targetId)
                        && summary.target.parentProductId === Number(group.target?.parentProductId)
                        && summary.target.variationId === Number(group.target?.variationId);
                });
            if (
                !exact
                || exact.draftFingerprint !== payload.draftFingerprint
                || !/^[a-f0-9]{64}$/.test(String(payload.snapshotFingerprint || ''))
                || ids.length < 1
                || !validScale
                || ids.length !== summaries.length
                || ids.some((id, index) => id !== summaries[index].id)
                || Math.floor(Number(payload.selectedCount)) !== snapshotItemCount
                || Math.floor(Number(payload.selectedSubjectCount)) !== ids.length
                || !exactSummaries
			) {
				reviewFeedbackId = showSnapshotError(
					__( 'Studio could not confirm your selections, so generation did not start. Reload Studio and try again. No credits were used.', 'fotogenic-ai' ),
					'studio-generation-review-invalid'
				);
                return false;
            }
            selectedItems.clear();
            summaries.forEach((item) => selectedItems.set(item.id, item));
            bulkMeta = {
                matchingCount: ids.length,
                selectedCount: Math.max(ids.length, Math.floor(Number(payload.selectedCount)) || 0),
                maximum: Math.max(ids.length, Math.floor(Number(payload.maximum)) || 1),
                strategy: 'explicit',
            };
			if (payload.draft.scope === 'single') {
                if (!store.applySingleSnapshot(payload.draft, exact)) {
					reviewFeedbackId = showSnapshotError(
						__( 'Studio needs to refresh your selections before generation can start. Reload Studio, review them, and try again. No credits were used.', 'fotogenic-ai' ),
						'studio-generation-review-changed'
					);
                    return false;
				}
				sourcePicker.update(getRoute());
                return exact;
            }
			if (!store.applyBulkSnapshot(payload.draft, exact, {
                fingerprint: payload.draftFingerprint,
                snapshotFingerprint: payload.snapshotFingerprint,
                originSelection,
            })) {
				reviewFeedbackId = showSnapshotError(
					__( 'Studio needs to refresh your selections before generation can start. Reload Studio, review them, and try again. No credits were used.', 'fotogenic-ai' ),
					'studio-generation-review-changed'
				);
				return false;
			}
			sourcePicker.update(getRoute());
            return exact;
        };

        const onReview = () => {
            if (generationCheck || reviewButton.disabled) return false;
            if (configurationState !== 'ready' && getRoute().workflow !== 'increase_resolution') return false;
            const readiness = renderSummary();
            if (!readiness?.ready) return false;
            openReview(store.getCanonical().draft, readiness.exact || undefined);
            return true;
        };
        reviewButton.addEventListener('click', onReview);
        listeners.push(() => reviewButton.removeEventListener('click', onReview));

        const openSelect = () => {
            if (runController?.hasStoredRun?.()) {
				if (!runController.leaveTerminalRun?.()) {
					shell.announce(__( 'Finish or leave the current generation before changing its source.', 'fotogenic-ai' ));
					return;
				}
            }
            cancelGenerationCheck();
            store.setView('select');
            shell.setView('select', { announcement: __( 'Select step.', 'fotogenic-ai' ) });
        };
        const openConfigure = () => {
            if (runController?.hasStoredRun?.()) {
				if (!runController.leaveTerminalRun?.()) {
					shell.announce(__( 'Finish or leave the current generation before changing its configuration.', 'fotogenic-ai' ));
					return;
				}
            }
			if (getExactItemCount() < 1) return;
            cancelGenerationCheck();
            recipeForm.resetSection();
            store.setView('configure');
            shell.setView('configure', { announcement: __( 'Configure step.', 'fotogenic-ai' ) });
        };
		const moveConfigureForward = () => {
			if (recipeForm?.moveSection?.('next', { focus: true })) {
				shell.announce(__( 'Next recipe section.', 'fotogenic-ai' ));
			}
		};
		const moveConfigureBack = () => {
			if (recipeForm?.moveSection?.('previous', { focus: true })) {
				shell.announce(__( 'Previous recipe section.', 'fotogenic-ai' ));
				return;
			}
			openSelect();
		};
        configureButton?.addEventListener('click', openConfigure);
		configureBackButton?.addEventListener('click', moveConfigureBack);
		configureNextButton?.addEventListener('click', moveConfigureForward);
        if (configureButton) listeners.push(() => configureButton.removeEventListener('click', openConfigure));
		if (configureBackButton) listeners.push(() => configureBackButton.removeEventListener('click', moveConfigureBack));
		if (configureNextButton) listeners.push(() => configureNextButton.removeEventListener('click', moveConfigureForward));
        shell.bindStageNavigation((target) => {
            if (target === 'select') {
                openSelect();
                return;
            }
            if (target === 'configure') {
                openConfigure();
                return;
            }
            if (target === 'review' && reviewReady && !runController?.hasStoredRun?.()) {
                onReview();
				return;
            }
			if (target === 'results') runController?.openSummary?.();
        });

        const setAuth = (authStatus = {}) => {
            const wasConnected = authConnected;
            authConnected = authStatus?.connected === true;
            if (!authConnected) {
                configurationToken += 1;
                configurationController?.abort();
                configurationController = null;
                configurationState = 'disconnected';
                manifest.optionSources = {};
                recipeForm.render(store.getDefinitions().workflow, getRoute());
                estimateCache.clear();
				const reviewWasOpen = shell.getCurrentView() === 'review';
				const reviewReason = __( 'Reconnect Fotogenic and review this request again before generating.', 'fotogenic-ai' );
                store.invalidateAcceptedReview(
                    store.getCanonicalString(),
                    __( 'Reconnect Fotogenic and review the current request before generating.', 'fotogenic-ai' )
                );
                reviewReady = false;
                shell.setStageAvailability({ generate: false, summary: false });
                const reviewStart = root.querySelector('[data-fa-view="review"] .fotogenic-ai-primary-action');
                if (reviewStart) reviewStart.disabled = true;
				if (reviewWasOpen) {
					const reviewReasonNode = root.querySelector('#fotogenic-ai-review-action-reason');
					if (reviewReasonNode) reviewReasonNode.textContent = reviewReason;
					notifyReviewFeedback({
						type: 'warning',
						title: __( 'Fotogenic account disconnected', 'fotogenic-ai' ),
						message: reviewReason,
						dedupeKey: 'studio-review-auth-required',
					});
                }
            }
            cancelGenerationCheck();
            renderSummary();
            if (authConnected && !wasConnected && configurationState !== 'ready') retryConfiguration();
        };

        const onAuthEvent = (event) => setAuth(event.detail?.auth || event.detail || {});
        const onOpenHistoryEvent = () => {
            cancelGenerationCheck();
            void runController.openGlobalHistory();
        };
		const onOpenHistoryRunEvent = (event) => {
			cancelGenerationCheck();
			void runController.openGlobalHistoryRunById(event.detail?.runId);
		};
        const onOpenSelectEvent = () => openSelect();
        const onStorage = (event) => {
            if (event.key !== 'fotogenic_ai_auth_sync' || !event.newValue) return;
            try {
                const payload = JSON.parse(event.newValue);
                if (payload.connected === false) setAuth({ connected: false });
            } catch (_) {
                return;
            }
        };
        window.addEventListener('fotogenic-ai:auth-changed', onAuthEvent);
        const onCreditsEvent = () => updateReviewCredits();
        window.addEventListener('fotogenic-ai:credits-updated', onCreditsEvent);
        window.addEventListener('fotogenic-ai:open-history', onOpenHistoryEvent);
		window.addEventListener('fotogenic-ai:open-history-run', onOpenHistoryRunEvent);
        window.addEventListener('fotogenic-ai:open-select', onOpenSelectEvent);
        window.addEventListener('storage', onStorage);
        listeners.push(
            () => window.removeEventListener('fotogenic-ai:credits-updated', onCreditsEvent),
            () => window.removeEventListener('fotogenic-ai:auth-changed', onAuthEvent),
            () => window.removeEventListener('fotogenic-ai:open-history', onOpenHistoryEvent),
			() => window.removeEventListener('fotogenic-ai:open-history-run', onOpenHistoryRunEvent),
            () => window.removeEventListener('fotogenic-ai:open-select', onOpenSelectEvent),
            () => window.removeEventListener('storage', onStorage)
        );

        seedInitialContext();
        renderRoute();
		await runController.restore();
        if (String(boot.initialContext?.view || '').toLowerCase() === 'history') {
			await runController.openGlobalHistory();
		}
		const page = root.closest('[data-fa-studio-page][data-fa-ui-state="pending"]');
		if (page) {
			page.dataset.faUiState = 'ready';
			page.removeAttribute('aria-busy');
		}

		return Object.freeze({
            blocked: false,
            setAuth,
            getSnapshot() {
                const state = store.getState();
                return {
                    route: { ...state.route },
                    view: state.view,
                    canonicalDraft: store.getCanonical().draft,
                    preflightReady: store.isPreflightCurrent(),
                };
            },
            destroy() {
                if (destroyed) return;
                destroyed = true;
                configurationToken += 1;
                configurationController?.abort();
                estimateCache.clear();
                cancelGenerationCheck();
                hydrateToken += 1;
                unsubscribe();
                listeners.splice(0).forEach((remove) => remove());
                sourcePicker.destroy();
                recipeForm.destroy();
                runController.destroy();
                store.destroy();
                Studio.notificationCenter?.destroy?.();
                Studio.notify?.destroy?.();
                Studio.errors?.destroy?.();
            },
        });
    };

	Studio.createSnapshotErrorPresenter = createSnapshotErrorPresenter;
    Studio.createApp = createApp;
})();
