(() => {
    'use strict';

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

    const VIEW_NAMES = new Set(['select', 'configure', 'review', 'progress', 'error', 'results']);
    const { isRecord } = Studio.store;
    const { element, destinationLabel } = Studio.viewModel;
    const icons = window.FotogenicAI.Shared?.ui?.icon;
    if (!icons) throw new Error('Fotogenic AI shared icon helper is required.');

    const createShell = ({ root, elements: providedElements } = {}) => {
        const elements = providedElements || {};
        const views = elements.views || new Map(
            [...root.querySelectorAll('[data-fa-view]')].map((view) => [view.dataset.faView, view])
        );
        const liveRegion = elements.liveRegion || root.querySelector('[data-fa-live-region]');
        const sourceTypesMount = elements.sourceTypesMount || root.querySelector('[data-fa-mount="source-types"]');
        const workflowControl = elements.workflowControl || root.querySelector('[data-fa-control="workflow"]');
        const toolbarRoot = elements.toolbarRoot || root.querySelector('[data-fa-shared-toolbar]');
        const toolbarApi = window.FotogenicAI.Shared?.ui?.studioToolbar;
        const toolbarController = elements.toolbarController
            || toolbarApi?.get(toolbarRoot)
            || toolbarApi?.create({ root: toolbarRoot });
        const stageRoot = elements.stageRoot || root.querySelector('[data-fa-stage-navigation]');
        const stageApi = window.FotogenicAI.Shared?.ui?.stageNavigation;
        const stageController = elements.stageController
            || stageApi?.get(stageRoot)
            || stageApi?.create({ root: stageRoot });
        let stageRequestHandler = null;
        let currentView = 'select';
        let announcementTimer = null;
		let reviewSortCleanup = null;
		let sourceTypeLabel = null;
		let sourceTypeControl = null;
		let sourceTypeIcon = null;
		let sourceTypeOnChange = null;

		const handleSourceTypeChange = () => {
			if (typeof sourceTypeOnChange === 'function') {
				sourceTypeOnChange(sourceTypeControl?.value || '');
			}
		};

        stageController?.bindRequest((target) => {
            if (typeof stageRequestHandler === 'function') {
                stageRequestHandler(target);
                return;
            }
            setView(target);
        });

        const announce = (message) => {
            if (!liveRegion) return;
            const nextMessage = String(message || '').trim();
            if (!nextMessage) return;
            if (announcementTimer !== null) {
                window.clearTimeout(announcementTimer);
                announcementTimer = null;
            }
            liveRegion.textContent = '';
            announcementTimer = window.setTimeout(() => {
                announcementTimer = null;
                liveRegion.textContent = nextMessage;
            }, 20);
        };

        const setView = (nextView, { focus = true, announcement = '' } = {}) => {
            if (!VIEW_NAMES.has(nextView) || !views.has(nextView)) {
                return false;
            }
            const target = views.get(nextView);
            if (nextView !== 'review') {
                reviewSortCleanup?.();
                reviewSortCleanup = null;
            }
            target.hidden = false;
            target.removeAttribute?.('inert');
            target.inert = false;

            const heading = target.querySelector?.('h1[tabindex="-1"], h2[tabindex="-1"], [data-fa-view-heading], h1, h2') || null;
            if (focus && heading?.focus) {
                heading.focus({ preventScroll: true });
            }

            views.forEach((view, name) => {
                if (name === nextView) return;
                view.hidden = true;
                view.setAttribute?.('inert', '');
                view.inert = true;
            });
            currentView = nextView;
            root.dataset.faCurrentView = nextView;
            if (Studio.feedbackContext?.state) {
                Studio.feedbackContext.state.currentStepKey = nextView;
            }
            stageController?.setView(nextView);
            if (announcement) announce(announcement);
            return true;
        };

        const setStageAvailability = (availability = {}) => stageController?.setAvailability(availability);

        const sourceTypeHasFocus = () => Boolean(sourceTypesMount?.contains(document.activeElement));

        const renderSourceTypes = ({ workflow, route, wooAvailable, restoreFocus = false, onChange }) => {
            if (!sourceTypesMount) return;
            const hadFocus = restoreFocus || sourceTypeHasFocus();
			sourceTypeOnChange = onChange;
			if (!sourceTypeControl) {
				sourceTypeLabel = element('label', 'fotogenic-ai-source-types fotogenic-ai-source-type-select');
				sourceTypeLabel.appendChild(element('span', 'fotogenic-ai-studio-sr-only', __( 'Source type', 'fotogenic-ai' )));
				sourceTypeControl = element('select', 'fotogenic-ai-studio-control');
				sourceTypeControl.setAttribute('data-fa-control', 'source-type');
				sourceTypeControl.setAttribute('aria-label', __( 'Source type', 'fotogenic-ai' ));
				sourceTypeControl.addEventListener('change', handleSourceTypeChange);
				const caret = icons.create('caret-down', { weight: 'bold', size: 14 });
				caret.classList.add('fotogenic-ai-source-type-select__caret');
				sourceTypeLabel.append(sourceTypeControl, caret);
				sourceTypesMount.replaceChildren(sourceTypeLabel);
			}
            const sourceIcons = {
                product: 'package',
                media: 'image-square',
                post: 'rows',
                page: 'squares-four',
            };
			const nextIcon = icons.create(sourceIcons[route.sourceType] || 'image-square', { weight: 'fill', size: 17 });
			nextIcon.classList.add('fotogenic-ai-source-type-select__icon');
			if (sourceTypeIcon) sourceTypeIcon.replaceWith(nextIcon);
			else sourceTypeLabel.insertBefore(nextIcon, sourceTypeControl);
			sourceTypeIcon = nextIcon;
			const options = workflow.sources.flatMap((source) => {
				if (source.requiresWooCommerce && !wooAvailable) return [];
				if (!source.scopes.includes(route.scope)) return [];
                const option = element('option', '', sprintf(
                    __( '%s source', 'fotogenic-ai' ),
                    labels[source.id] || source.id
                ));
                option.value = source.id;
                option.selected = source.id === route.sourceType;
				return [option];
            });
			sourceTypeControl.replaceChildren(...options);
			sourceTypeControl.value = route.sourceType;
            if (hadFocus) {
                window.requestAnimationFrame(() => {
					if (sourceTypeControl.isConnected) {
						sourceTypeControl.focus({ preventScroll: true });
                    }
                });
            }
        };

        const syncHeaderControls = ({ manifest, route, wooAvailable }) => {
            toolbarController?.sync({ manifest, route, wooAvailable });
            const sourceTypeControl = sourceTypesMount?.querySelector('[data-fa-control="source-type"]');
            if (sourceTypeControl) sourceTypeControl.value = route.sourceType;
            root.dataset.workflow = route.workflow;
            root.dataset.sourceType = route.sourceType;
            root.dataset.scope = route.scope;
        };

        const normalizeOptions = (options) => (Array.isArray(options) ? options : [])
            .map((option) => {
                if (isRecord(option)) {
                    const value = option.value ?? option.id;
                    if (value === undefined) return null;
                    return { value: String(value), label: String(option.label || option.name || value) };
                }
                return { value: String(option), label: String(option) };
            })
            .filter(Boolean);

        const optionEntries = (manifest, field, path) => {
            if (Array.isArray(field?.enum)) return normalizeOptions(field.enum);
            if (Array.isArray(field?.options)) return normalizeOptions(field.options);
            const sourceName = String(field?.optionSource || '');
            const source = sourceName && isRecord(manifest?.optionSources) ? manifest.optionSources[sourceName] : null;
            if (Array.isArray(source)) return normalizeOptions(source);
            if (!isRecord(source)) return [];
            return normalizeOptions(source[path.join('.')] || source[path[path.length - 1]] || []);
        };

        const optionLabel = (manifest, field, path, value) => {
            const normalizedValue = isRecord(value) ? value.id ?? value.value : value;
            const match = optionEntries(manifest, field, path)
                .find((option) => option.value === String(normalizedValue));
            return match?.label || String(normalizedValue ?? '');
        };

        const reviewRecipeRows = (recipe, schema, manifest, path = [], prefix = '') => {
            if (!isRecord(recipe) || !isRecord(schema?.fields)) return [];
            return Object.entries(schema.fields).flatMap(([name, field]) => {
				if (
					name === 'version'
					|| (path.length === 0 && name === 'count')
					|| field?.exposed === false
					|| !Object.prototype.hasOwnProperty.call(recipe, name)
				) return [];
                const value = recipe[name];
                if (value === '' || value === null || value === undefined) return [];
                const label = Studio.recipeForm.fieldLabel(name);
				const rowLabel = prefix ? `${prefix}: ${label}` : label;
                const nextPath = [...path, name];
                if (field?.type === 'object' && !field.optionSource) {
                    return reviewRecipeRows(value, field, manifest, nextPath, rowLabel);
                }
                if (field?.type === 'integer' && /(ImageId|AttachmentId)$/i.test(name)) {
                    return Number(value) > 0 ? [[rowLabel, __( 'Selected', 'fotogenic-ai' )]] : [];
                }
                if (field?.optionSource || Array.isArray(field?.enum) || Array.isArray(field?.options)) {
                    return [[rowLabel, optionLabel(manifest, field, nextPath, value)]];
                }
                if (field?.type === 'boolean') return [[rowLabel, value === true ? __( 'Yes', 'fotogenic-ai' ) : __( 'No', 'fotogenic-ai' )]];
                if (isRecord(value) || Array.isArray(value)) return [];
				const text = String(value).trim();
				return [[rowLabel, text.length > 180 ? `${text.slice(0, 180).replace(/\s+\S*$/, '')}…` : text]];
            });
        };

        const renderReview = ({ canonicalDraft, estimate, selectedItems, workflow, manifest, onEdit, onRemoveSource, onLoadSourcePage, onLoadEstimate, onGenerate, onFeedback, canGenerate, generateReason = '' }) => {
            const review = views.get('review');
            if (!review) return;
			reviewSortCleanup?.();
			reviewSortCleanup = null;

            const numberFormatter = new Intl.NumberFormat(undefined, { maximumFractionDigits: 2 });
			const formatEstimate = (value) => value == null ? '—' : numberFormatter.format(value);
            const selectedIds = Array.isArray(canonicalDraft.selection?.selectedIds)
                ? canonicalDraft.selection.selectedIds.map(Number).filter((id) => Number.isSafeInteger(id) && id > 0)
                : [];
            const selected = selectedIds.map((id) => selectedItems.get(id)).filter(Boolean);
            const campaignGroups = canonicalDraft.workflow === 'campaign_set' && Array.isArray(canonicalDraft.sourcePolicy?.groups)
                ? canonicalDraft.sourcePolicy.groups
                : [];
            const runItemCount = estimate.totalItems || campaignGroups.length || selected.length;
            const querySelection = canonicalDraft.selection.strategy === 'query';
            const pagedQuery = querySelection && typeof onLoadSourcePage === 'function';
            const sourceCount = querySelection ? runItemCount : new Set(campaignGroups.length
                ? campaignGroups.map((group) => Number(group.subjectId)).filter((id) => id > 0)
                : selectedIds).size;
            const outputsPerItem = runItemCount > 0
                ? Math.max(1, Math.round(Number(estimate.totalOutputs) / runItemCount))
                : 0;
			const campaignOutputCounts = campaignGroups
				.map((group) => Number(group.recipe?.count))
				.filter((count) => Number.isSafeInteger(count) && count > 0);
			const outputCountSummary = (() => {
				if (canonicalDraft.workflow === 'increase_resolution') {
					return [__( 'Output per item', 'fotogenic-ai' ), numberFormatter.format(outputsPerItem)];
				}
				if (canonicalDraft.workflow === 'campaign_set') {
					const uniqueCounts = [...new Set(campaignOutputCounts)].sort((left, right) => left - right);
					const value = uniqueCounts.length > 1
						? `${numberFormatter.format(uniqueCounts[0])}-${numberFormatter.format(uniqueCounts.at(-1))}`
						: numberFormatter.format(uniqueCounts[0] || outputsPerItem);
					return [__( 'Outputs per set', 'fotogenic-ai' ), value];
				}
				return [__( 'Outputs per item', 'fotogenic-ai' ), numberFormatter.format(outputsPerItem)];
			})();
            const recipeRows = reviewRecipeRows(canonicalDraft.recipe, workflow?.recipe, manifest);
            const workflowLabel = labels[canonicalDraft.workflow] || canonicalDraft.workflow;
            const sourceTypeLabel = labels[canonicalDraft.sourceType] || canonicalDraft.sourceType;
            const destination = destinationLabel(canonicalDraft);

            const sourceGroups = new Map();
            if (campaignGroups.length) {
                campaignGroups.forEach((group, index) => {
                    const subjectId = Number(group.subjectId);
                    const item = selectedItems.get(subjectId) || selected.find((entry) => entry.id === subjectId) || null;
                    const target = item?.target || group.target || {};
                    const groupSourceCount = Math.max(
                        1,
                        (Number(group.primaryAttachmentId) > 0 ? 1 : 0)
                            + (Array.isArray(group.supportingAttachmentIds) ? group.supportingAttachmentIds.length : 0)
                    );
                    const groupKey = Number(target.parentProductId) > 0 ? Number(target.parentProductId) : subjectId;
                    const current = sourceGroups.get(groupKey) || {
                        id: groupKey,
                        platformId: groupKey,
                        label: item?.label || sprintf( __( 'Exact target %d', 'fotogenic-ai' ), subjectId ),
                        imageUrl: item?.imageUrl || '',
                        detail: item?.detail || '',
                        eligible: item?.eligible !== false,
                        children: [],
                    };
                    current.children.push({
                        id: String(group.groupId || group.id || `${subjectId}:${index}`),
                        label: sprintf( __( 'Set %1$d · %2$s', 'fotogenic-ai' ), index + 1, item?.label || current.label ),
                        detail: sprintf(
                            _n( '%d source image', '%d source images', groupSourceCount, 'fotogenic-ai' ),
                            groupSourceCount
                        ),
                        imageUrl: item?.imageUrl || current.imageUrl,
                        platformId: subjectId,
                        outputs: Math.max(1, Math.round(Number(group.recipe?.count) || outputsPerItem)),
                    });
                    sourceGroups.set(groupKey, current);
                });
            } else {
                selected.forEach((item) => {
                    const parentId = Number(item.target?.parentProductId) || 0;
                    const groupKey = canonicalDraft.sourceType === 'product' && parentId > 0 ? parentId : item.id;
                    const isParentProduct = canonicalDraft.sourceType === 'product'
                        && item.target?.targetType === 'product'
                        && Number(item.target?.targetId) === groupKey;
                    const current = sourceGroups.get(groupKey) || {
                        id: groupKey,
                        platformId: groupKey,
                        label: item.label,
                        imageUrl: item.imageUrl,
                        detail: item.detail,
                        eligible: item.eligible !== false,
                        hasParentProduct: isParentProduct,
                        children: [],
                    };
                    if (isParentProduct) {
                        current.label = item.label;
                        current.imageUrl = item.imageUrl;
                        current.detail = item.detail;
                        current.hasParentProduct = true;
                    }
                    current.children.push({
                        id: item.id,
                        label: item.label,
                        detail: item.detail,
                        imageUrl: item.imageUrl,
                        platformId: item.id,
                        outputs: outputsPerItem,
                    });
                    current.eligible = current.eligible && item.eligible !== false;
                    sourceGroups.set(groupKey, current);
                });
            }

            const reviewSources = [...sourceGroups.values()].map((item, selectionIndex) => ({
                ...item,
                selectionIndex,
                platformId: item.children.length === 1 && Number(item.children[0]?.platformId) > 0
                    ? Number(item.children[0].platformId)
                    : item.platformId,
                runItems: item.children.length,
                outputs: item.children.reduce((total, child) => total + child.outputs, 0),
            }));
            let sourceSort = 'selection';
            let sourcePage = 1;
            let sourcePageSize = 10;
            let queryItems = [];
            let queryTotal = sourceCount;
            let queryPending = pagedQuery;
            let generatePending = false;
            let queryError = '';
            let queryController = null;
            let queryRequest = 0;
            let reviewDisposed = false;
            const desktopMedia = window.matchMedia?.('(min-width: 1200px)');
            let infinite = desktopMedia?.matches === true && (!querySelection || pagedQuery);
            let renderedCount = 0;
            let infiniteObserver = null;
            const expandedSourceIds = new Set();

            const state = element('div', 'fotogenic-ai-review-view');
            state.dataset.screen = 'generate-review';
            const intro = element('header', 'fotogenic-ai-review-intro');
            intro.appendChild(element('span', 'fotogenic-ai-studio-eyebrow', __( 'Step 3 of 4 · Generate', 'fotogenic-ai' )));
            const heading = element('h1', '', __( 'Ready to generate', 'fotogenic-ai' ));
            heading.tabIndex = -1;
            intro.append(
                heading,
                element('p', '', __( 'Review your run. Pricing and balance are checked when you generate.', 'fotogenic-ai' ))
            );

            const workspace = element('div', 'fotogenic-ai-review-workspace');
            const ledger = element('section', 'fotogenic-ai-verification-ledger');
            ledger.setAttribute('aria-labelledby', 'fotogenic-ai-verification-ledger-title');
            const commandHeader = element('header', 'fotogenic-ai-verification-command-header');
            const ledgerHeading = element('h2', '', __( 'Run items', 'fotogenic-ai' ));
            ledgerHeading.id = 'fotogenic-ai-verification-ledger-title';
            const runCount = element(
                'span',
                'fotogenic-ai-verification-run-count',
                sprintf(
					_n( '%d run item', '%d run items', runItemCount, 'fotogenic-ai' ),
					runItemCount
                )
            );
            const sort = element('div', 'fotogenic-ai-verification-sort');
            const sortTrigger = element('button', 'fotogenic-ai-verification-sort-trigger');
            sortTrigger.type = 'button';
            sortTrigger.setAttribute('aria-expanded', 'false');
            sortTrigger.setAttribute('aria-controls', 'fotogenic-ai-review-source-sorting');
            const sortCaret = icons.create('caret-down', { weight: 'bold', size: 14 });
            sortCaret.classList.add('fotogenic-ai-control-caret');
            sortTrigger.append(icons.create('funnel', { size: 17 }), document.createTextNode(__( 'Sort', 'fotogenic-ai' )), sortCaret);
            const sortMenu = element('div', 'fotogenic-ai-verification-sort-menu');
            sortMenu.id = 'fotogenic-ai-review-source-sorting';
            sortMenu.hidden = true;
            sortMenu.setAttribute('role', 'group');
            sortMenu.setAttribute('aria-label', __( 'Sort review sources', 'fotogenic-ai' ));
            [
                ['selection', __( 'Selection order', 'fotogenic-ai' )],
                ['name', __( 'Product name', 'fotogenic-ai' )],
                ['items', __( 'Most items', 'fotogenic-ai' )],
            ].forEach(([value, label]) => {
                const control = element('button');
                control.type = 'button';
                control.dataset.sort = value;
                control.setAttribute('aria-pressed', value === sourceSort ? 'true' : 'false');
                control.appendChild(element('span', '', label));
                if (value === sourceSort) control.appendChild(icons.create('check', { weight: 'bold', size: 15 }));
                sortMenu.appendChild(control);
            });
            sort.append(sortTrigger, sortMenu);
            sort.hidden = querySelection;
            const ledgerTools = element('div', 'fotogenic-ai-verification-tools');
            let pageSizeLabel;
            if ((pagedQuery ? sourceCount : reviewSources.length) > 10) {
                pageSizeLabel = element('label', 'fotogenic-ai-verification-page-size');
                pageSizeLabel.hidden = infinite;
                pageSizeLabel.appendChild(element('span', 'fotogenic-ai-studio-sr-only', __( 'Sources per page', 'fotogenic-ai' )));
                const pageSize = element('select');
                [10, 25, 50].forEach((size) => {
                    const option = element('option', '', sprintf(__( '%d per page', 'fotogenic-ai' ), size));
                    option.value = String(size);
                    option.selected = size === sourcePageSize;
                    pageSize.appendChild(option);
                });
                pageSize.addEventListener('change', () => {
                    sourcePageSize = Number(pageSize.value) || 10;
                    sourcePage = 1;
                    if (querySelection) void loadQueryPage();
                    else renderLedger();
                });
                const pageSizeCaret = icons.create('caret-down', { weight: 'bold', size: 14 });
                pageSizeCaret.classList.add('fotogenic-ai-control-caret');
                pageSizeLabel.append(pageSize, pageSizeCaret);
                ledgerTools.appendChild(pageSizeLabel);
            }
            ledgerTools.appendChild(sort);
            commandHeader.append(ledgerHeading, runCount, ledgerTools);

            const tableWrap = element('div', 'fotogenic-ai-verification-table-wrap');
            tableWrap.tabIndex = 0;
            tableWrap.setAttribute('role', 'region');
            tableWrap.setAttribute('aria-label', __( 'Run items', 'fotogenic-ai' ));
            const pagination = element('footer', 'fotogenic-ai-verification-pagination');
            const sentinel = element('div', 'fotogenic-ai-verification-infinite-trigger');
            sentinel.hidden = true;
            const infiniteState = element('div', 'fotogenic-ai-verification-load-state fotogenic-ai-verification-infinite-state');
            infiniteState.setAttribute('role', 'status');
            infiniteState.hidden = true;
            const hasMore = () => sourcePage * sourcePageSize < (querySelection ? queryTotal : reviewSources.length);
            const renderInfiniteState = () => {
                sentinel.hidden = !infinite || generatePending || (!queryPending && !queryError && !hasMore());
                sentinel.replaceChildren();
                infiniteState.hidden = !infinite || generatePending || (!queryPending && !queryError);
                infiniteState.setAttribute('role', queryError ? 'alert' : 'status');
                infiniteState.replaceChildren();
                if (sentinel.hidden) return;
                if (queryPending) {
                    const spinner = icons.create('arrows-clockwise', { size: 16 });
                    spinner.classList.add('fotogenic-ai-icon--spin');
                    infiniteState.append(spinner, document.createTextNode(__( 'Loading products…', 'fotogenic-ai' )));
                    return;
                }
                const more = element('button', 'fotogenic-ai-secondary-action', queryError
                    ? __( 'Retry', 'fotogenic-ai' ) : __( 'Load more sources', 'fotogenic-ai' ));
                more.type = 'button';
                if (queryError) {
                    infiniteState.appendChild(document.createTextNode(queryError));
                    more.setAttribute('data-fa-review-retry', '');
                }
                more.addEventListener('click', () => queryError ? void loadQueryPage() : loadMore());
                (queryError ? infiniteState : sentinel).appendChild(more);
            };
            const loadMore = () => {
                if (!infinite || reviewDisposed || queryPending || queryError || generatePending || !hasMore()) return;
                sourcePage += 1;
                if (pagedQuery) void loadQueryPage();
                else renderLedger(true);
            };
            const summary = element('aside', 'fotogenic-ai-run-summary');
            summary.setAttribute('aria-labelledby', 'fotogenic-ai-run-summary-title');
            const summaryHeading = element('header', 'fotogenic-ai-review-card-heading');
            const summaryTitle = element('h2', '', __( 'Run summary', 'fotogenic-ai' ));
            summaryTitle.id = 'fotogenic-ai-run-summary-title';
            summaryHeading.appendChild(summaryTitle);
            const summaryMetrics = element('dl', 'fotogenic-ai-run-summary-metrics');
            const metricValues = [];
            [
                [__( 'Outputs', 'fotogenic-ai' ), formatEstimate(estimate.totalOutputs)],
                [__( 'Estimated credits', 'fotogenic-ai' ), formatEstimate(estimate.totalCredits)],
                [__( 'Estimated balance', 'fotogenic-ai' ), formatEstimate(estimate.balanceAfter)],
                [__( 'Run items', 'fotogenic-ai' ), numberFormatter.format(runItemCount)],
            ].forEach(([label, value]) => {
                const row = element('div');
                row.append(element('dt', '', label), element('dd', '', value));
                summaryMetrics.appendChild(row);
                metricValues.push(row.lastElementChild);
            });
            const estimateStatus = element('div', 'fotogenic-ai-review-estimate-status');
            estimateStatus.setAttribute('role', 'status');
            estimateStatus.hidden = true;
            const recipeHeading = element('div', 'fotogenic-ai-run-recipe-heading');
            recipeHeading.append(element('h3', '', __( 'Recipe summary', 'fotogenic-ai' )), element('span', '', workflowLabel));
            const recipeList = element('dl', 'fotogenic-ai-run-recipe');
            const conciseRows = [
                [__( 'Source', 'fotogenic-ai' ), sourceTypeLabel],
				outputCountSummary,
                ...recipeRows.slice(0, 4),
                [__( 'Destination', 'fotogenic-ai' ), destination],
            ];
            let fullRecipeOpen = false;
            const renderRecipe = () => {
                recipeList.replaceChildren();
                const rows = fullRecipeOpen
                    ? [[__( 'Run items', 'fotogenic-ai' ), numberFormatter.format(runItemCount)], ...conciseRows, ...recipeRows.slice(4)]
                    : conciseRows;
                rows.forEach(([label, value]) => {
                    const row = element('div');
                    row.append(element('dt', '', label), element('dd', '', value));
                    recipeList.appendChild(row);
                });
            };
            const recipeToggle = element('button', 'fotogenic-ai-run-recipe-toggle');
            recipeToggle.type = 'button';
            recipeToggle.setAttribute('aria-expanded', 'false');
            recipeToggle.append(icons.create('arrow-square-out', { size: 16 }), element('span', '', __( 'View full recipe', 'fotogenic-ai' )));
            recipeToggle.addEventListener('click', () => {
                fullRecipeOpen = !fullRecipeOpen;
                recipeToggle.setAttribute('aria-expanded', fullRecipeOpen ? 'true' : 'false');
                recipeToggle.lastElementChild.textContent = fullRecipeOpen
                    ? __( 'Show concise recipe', 'fotogenic-ai' )
                    : __( 'View full recipe', 'fotogenic-ai' );
                renderRecipe();
            });
            renderRecipe();
            summary.append(summaryHeading, summaryMetrics, estimateStatus, recipeHeading, recipeList, recipeToggle);

            const renderLedger = (append = false) => {
                const sorted = querySelection ? queryItems.map((item) => ({
                    id: item.id,
                    platformId: item.id,
                    label: item.label,
                    imageUrl: item.imageUrl,
                    detail: item.detail,
                    eligible: item.eligible,
                    runItems: 1,
                    outputs: outputsPerItem,
                    children: [],
                })) : [...reviewSources];
                if (sourceSort === 'name') {
                    sorted.sort((a, b) => a.label.localeCompare(b.label) || a.selectionIndex - b.selectionIndex);
                }
                if (sourceSort === 'items') {
                    sorted.sort((a, b) => b.runItems - a.runItems || a.selectionIndex - b.selectionIndex);
                }
                const total = querySelection ? queryTotal : sorted.length;
                const pageCount = Math.max(1, Math.ceil(total / sourcePageSize));
                sourcePage = Math.min(sourcePage, pageCount);
                const start = infinite ? 0 : (sourcePage - 1) * sourcePageSize;
                const visible = querySelection ? ((!infinite && (queryPending || queryError)) ? [] : sorted)
                    : sorted.slice(start, infinite ? sourcePage * sourcePageSize : start + sourcePageSize);
                const appendRows = append && infinite && renderedCount > 0;
                const table = element('table', 'fotogenic-ai-verification-table');
                const caption = element('caption', 'fotogenic-ai-studio-sr-only', __( 'Sources and exact items selected for this generation run', 'fotogenic-ai' ));
                const tableHead = element('thead');
                const headingRow = element('tr');
                [__( 'Source', 'fotogenic-ai' ), __( 'Platform ID', 'fotogenic-ai' ), __( 'Items', 'fotogenic-ai' ), __( 'Outputs', 'fotogenic-ai' ), __( 'Eligibility', 'fotogenic-ai' ), '']
                    .forEach((label) => headingRow.appendChild(element('th', '', label)));
                tableHead.appendChild(headingRow);
                const body = element('tbody');
                if (querySelection && !visible.length && (!infinite || (!queryPending && !queryError))) {
                    const row = element('tr');
                    const detail = element('td');
                    detail.colSpan = 6;
                    const message = element('div', 'fotogenic-ai-verification-load-state');
                    message.setAttribute('role', queryError ? 'alert' : 'status');
                    if (queryPending) {
                        const spinner = icons.create('arrows-clockwise', { size: 16 });
                        spinner.classList.add('fotogenic-ai-icon--spin');
                        message.appendChild(spinner);
                    }
                    message.appendChild(element('span', '', !pagedQuery
                        ? sprintf(_n('%d matching source', '%d matching sources', sourceCount, 'fotogenic-ai'), sourceCount)
                        : queryPending ? __( 'Loading products…', 'fotogenic-ai' )
                        : queryError || __( 'No products remain. Return to Select to choose products.', 'fotogenic-ai' )));
                    if (queryError) {
                        const retry = element('button', 'fotogenic-ai-secondary-action', __( 'Retry', 'fotogenic-ai' ));
                        retry.type = 'button';
                        retry.setAttribute('data-fa-review-retry', '');
                        retry.addEventListener('click', () => void loadQueryPage());
                        message.appendChild(retry);
                    }
                    detail.appendChild(message);
                    row.appendChild(detail);
                    body.appendChild(row);
                }
                (appendRows ? visible.slice(renderedCount) : visible).forEach((item) => {
                    const expandable = item.children.length > 1 || campaignGroups.length > 0;
                    const expanded = expandable && expandedSourceIds.has(item.id);
                    const row = element('tr', `fotogenic-ai-verification-parent-row${expanded ? ' is-expanded' : ''}`);
                    const sourceCell = element('td');
                    const source = element('div', 'fotogenic-ai-verification-source');
                    if (item.imageUrl) {
                        const image = element('img');
                        image.src = item.imageUrl;
                        image.alt = '';
                        image.decoding = 'async';
                        source.appendChild(image);
                    } else {
                        const placeholder = icons.create('image-square', { size: 20 });
                        placeholder.classList.add('fotogenic-ai-verification-source-placeholder');
                        source.appendChild(placeholder);
                    }
                    const sourceCopy = element('span');
                    sourceCopy.appendChild(element('strong', '', item.label));
                    if (item.runItems > 1) {
                        sourceCopy.appendChild(element('small', '', campaignGroups.length
                            ? sprintf(_n('%d independent Campaign Set', '%d independent Campaign Sets', item.runItems, 'fotogenic-ai'), item.runItems)
                            : item.hasParentProduct
                                ? sprintf(_n('%d selected item', '%d selected items', item.runItems, 'fotogenic-ai'), item.runItems)
                                : sprintf(_n('%d selected variation', '%d selected variations', item.runItems, 'fotogenic-ai'), item.runItems)));
                    }
                    if (item.detail) sourceCopy.appendChild(element('small', 'fotogenic-ai-verification-source-meta', item.detail));
                    source.appendChild(sourceCopy);
                    sourceCell.appendChild(source);
                    row.appendChild(sourceCell);
                    row.appendChild(element('td', 'fotogenic-ai-verification-id', sprintf(__( 'ID %d', 'fotogenic-ai' ), item.platformId)));
                    const itemsCell = element('td', 'fotogenic-ai-verification-count');
                    itemsCell.append(element('span', 'fotogenic-ai-verification-mobile-label', __( 'Items', 'fotogenic-ai' )), document.createTextNode(numberFormatter.format(item.runItems)));
                    row.appendChild(itemsCell);
                    const outputsCell = element('td', 'fotogenic-ai-verification-count');
                    outputsCell.append(element('span', 'fotogenic-ai-verification-mobile-label', __( 'Outputs', 'fotogenic-ai' )), document.createTextNode(numberFormatter.format(item.outputs)));
                    row.appendChild(outputsCell);
                    const eligibilityCell = element('td');
                    const eligibility = element('span', item.eligible ? 'fotogenic-ai-verification-ready' : 'fotogenic-ai-verification-blocked');
                    eligibility.append(
                        icons.create(item.eligible ? 'check-circle' : 'warning', { weight: 'fill', size: 17 }),
                        document.createTextNode(item.eligible ? __( 'Ready', 'fotogenic-ai' ) : __( 'Unavailable', 'fotogenic-ai' ))
                    );
                    eligibilityCell.appendChild(eligibility);
                    row.appendChild(eligibilityCell);
                    const actionCell = element('td', 'fotogenic-ai-verification-action');
                    if (expandable) {
                        const disclosure = element('button', `fotogenic-ai-variation-disclosure${expanded ? ' is-expanded' : ''}`);
                        disclosure.type = 'button';
                        disclosure.setAttribute('aria-expanded', expanded ? 'true' : 'false');
                        disclosure.setAttribute('aria-label', sprintf(
                            expanded ? __( 'Collapse exact items for %s', 'fotogenic-ai' ) : __( 'Expand exact items for %s', 'fotogenic-ai' ),
                            item.label
                        ));
                        disclosure.appendChild(icons.create('caret-down', { weight: 'bold', size: 14 }));
                        disclosure.addEventListener('click', () => {
                            if (expandedSourceIds.has(item.id)) expandedSourceIds.delete(item.id);
                            else expandedSourceIds.add(item.id);
                            renderLedger();
                        });
                        actionCell.appendChild(disclosure);
                    }
                    if (typeof onRemoveSource === 'function') {
                        const remove = element('button', 'fotogenic-ai-verification-remove');
                        remove.type = 'button';
                        remove.title = __( 'Remove from this run', 'fotogenic-ai' );
                        remove.setAttribute('aria-label', sprintf(__( 'Remove %s from this run', 'fotogenic-ai' ), item.label));
                        remove.appendChild(icons.create('x', { size: 14 }));
                        remove.addEventListener('click', () => onRemoveSource(item));
                        actionCell.appendChild(remove);
                    }
                    row.appendChild(actionCell);
                    body.appendChild(row);

                    if (expanded) {
                        const childRow = element('tr', 'fotogenic-ai-verification-detail-row');
                        const childCell = element('td');
                        childCell.colSpan = 6;
                        const children = element('div', 'fotogenic-ai-verification-details');
                        item.children.forEach((child) => {
                            const childItem = element('div', 'fotogenic-ai-verification-detail');
                            const identity = element('span', 'fotogenic-ai-verification-detail-identity');
                            if (child.imageUrl) {
                                const childImage = element('img');
                                childImage.src = child.imageUrl;
                                childImage.alt = '';
                                identity.appendChild(childImage);
                            }
                            const childCopy = element('span');
                            childCopy.appendChild(element('strong', '', child.label));
                            if (child.detail) childCopy.appendChild(element('small', '', child.detail));
                            identity.appendChild(childCopy);
                            childItem.append(
                                identity,
                                element('span', 'fotogenic-ai-verification-id', sprintf(__( 'ID %d', 'fotogenic-ai' ), child.platformId)),
                                element('span', 'fotogenic-ai-verification-count', '1'),
                                element('span', 'fotogenic-ai-verification-count', numberFormatter.format(child.outputs))
                            );
                            const ready = element('span', 'fotogenic-ai-verification-ready');
                            ready.append(icons.create('check-circle', { weight: 'fill', size: 16 }), document.createTextNode(__( 'Ready', 'fotogenic-ai' )));
                            childItem.appendChild(ready);
                            children.appendChild(childItem);
                        });
                        childCell.appendChild(children);
                        childRow.appendChild(childCell);
                        body.appendChild(childRow);
                    }
                });
                table.append(caption, tableHead, body);
                if (appendRows) tableWrap.querySelector('tbody').append(...body.children);
                else tableWrap.replaceChildren(table, sentinel);
                renderedCount = visible.length;
                renderInfiniteState();

                pagination.replaceChildren();
                pagination.hidden = infinite || (querySelection && !pagedQuery);
                if (pagination.hidden) return;
                const pageControls = element('div');
                if (pageCount > 1) {
                    const previous = element('button');
                    previous.type = 'button';
                    previous.disabled = sourcePage === 1 || queryPending;
                    previous.setAttribute('aria-label', __( 'Previous source page', 'fotogenic-ai' ));
                    previous.appendChild(icons.create('arrow-left', { size: 15 }));
                    previous.addEventListener('click', () => {
                        sourcePage = Math.max(1, sourcePage - 1);
                        if (querySelection) void loadQueryPage();
                        else renderLedger();
                    });
                    const current = element('button', 'is-current', String(sourcePage));
                    current.type = 'button';
                    current.setAttribute('aria-current', 'page');
                    const next = element('button');
                    next.type = 'button';
                    next.disabled = sourcePage === pageCount || queryPending;
                    next.setAttribute('aria-label', __( 'Next source page', 'fotogenic-ai' ));
                    next.appendChild(icons.create('arrow-right', { size: 15 }));
                    next.addEventListener('click', () => {
                        sourcePage = Math.min(pageCount, sourcePage + 1);
                        if (querySelection) void loadQueryPage();
                        else renderLedger();
                    });
                    pageControls.append(previous, current, next);
                }
                pagination.append(
                    pageControls,
                    element('span', '', queryPending || queryError
                        ? sprintf(__( 'Page %1$d of %2$d', 'fotogenic-ai' ), sourcePage, pageCount)
                        : sprintf(
						__( 'Showing %1$s-%2$s of %3$s sources', 'fotogenic-ai' ),
                        total ? numberFormatter.format(start + 1) : '0',
                        numberFormatter.format(Math.min(start + sourcePageSize, total)),
                        numberFormatter.format(total)
                    ))
                );
            };

            const closeSortMenu = ({ restoreFocus = false } = {}) => {
				if (sortMenu.hidden) return;
				sortMenu.hidden = true;
				sortTrigger.setAttribute('aria-expanded', 'false');
				document.removeEventListener('pointerdown', dismissSortMenu, true);
				if (restoreFocus) sortTrigger.focus({ preventScroll: true });
			};
			const dismissSortMenu = (event) => {
				if (sort.contains(event.target)) return;
				closeSortMenu();
			};
			reviewSortCleanup = () => {
				document.removeEventListener('pointerdown', dismissSortMenu, true);
                reviewDisposed = true;
                queryController?.abort();
                infiniteObserver?.disconnect();
                desktopMedia?.removeEventListener('change', onLayoutChange);
                tableWrap.removeEventListener('scroll', onScroll);
			};
			sortTrigger.addEventListener('click', () => {
                const open = sortMenu.hidden;
				if (!open) {
					closeSortMenu({ restoreFocus: true });
					return;
				}
				sortMenu.hidden = false;
				sortTrigger.setAttribute('aria-expanded', 'true');
				document.addEventListener('pointerdown', dismissSortMenu, true);
				sortMenu.querySelector('[aria-pressed="true"]')?.focus({ preventScroll: true });
            });
            sortMenu.addEventListener('click', (event) => {
                const control = event.target.closest('button[data-sort]');
                if (!control) return;
                sourceSort = control.dataset.sort;
                sourcePage = 1;
				closeSortMenu();
                [...sortMenu.querySelectorAll('button')].forEach((button) => {
                    const selectedSort = button.dataset.sort === sourceSort;
                    button.setAttribute('aria-pressed', selectedSort ? 'true' : 'false');
                    button.querySelector('.fotogenic-ai-icon--check')?.remove();
                    if (selectedSort) button.appendChild(icons.create('check', { weight: 'bold', size: 15 }));
                });
                renderLedger();
                sortTrigger.focus({ preventScroll: true });
            });
            sortMenu.addEventListener('keydown', (event) => {
                if (event.key !== 'Escape') return;
                event.preventDefault();
				closeSortMenu({ restoreFocus: true });
            });
            renderLedger();
            ledger.append(commandHeader, tableWrap, pagination, infiniteState);
            workspace.append(ledger, summary);

            const footer = element('footer', 'fotogenic-ai-review-footer');
            footer.setAttribute('data-studio-workspace-footer', '');
            const notice = element(
                'div',
                'fotogenic-ai-review-status fotogenic-ai-studio-sr-only',
                canGenerate
                    ? __( 'Current pricing and balance will be checked before generation starts.', 'fotogenic-ai' )
                    : (generateReason || __( 'This run is not ready to start. Return to Configure and review the supported options.', 'fotogenic-ai' ))
            );
            notice.id = 'fotogenic-ai-review-action-reason';
            const actions = element('div', 'fotogenic-ai-review-footer-actions');
            const edit = element('button', 'fotogenic-ai-secondary-action');
            edit.type = 'button';
            edit.append(icons.create('arrow-left', { size: 17 }), document.createTextNode(__( 'Back to configure', 'fotogenic-ai' )));
            edit.addEventListener('click', onEdit);
            const actionLabel = canonicalDraft.workflow === 'increase_resolution'
                ? __( 'Increase resolution', 'fotogenic-ai' )
                : sprintf(_n('Generate %s image', 'Generate %s images', estimate.totalOutputs, 'fotogenic-ai'), numberFormatter.format(estimate.totalOutputs));
            let generateLabel = estimate.totalCredits === null ? actionLabel
                : sprintf(__( '%1$s · est. %2$s credits', 'fotogenic-ai' ), actionLabel, numberFormatter.format(estimate.totalCredits));
            const generate = element('button', 'fotogenic-ai-primary-action');
            generate.type = 'button';
            generate.append(icons.create('sparkle', { weight: 'fill', size: 18 }), document.createTextNode(generateLabel));
            generate.disabled = !canGenerate || typeof onGenerate !== 'function';
            generate.setAttribute('aria-describedby', notice.id);
            generate.addEventListener('click', async () => {
                if (generatePending || generate.disabled || typeof onGenerate !== 'function') return;
                generatePending = true;
                queryController?.abort();
                queryRequest += 1;
                queryPending = false;
                tableWrap.setAttribute('aria-busy', 'false');
                renderInfiniteState();
                generate.disabled = true;
                generate.setAttribute('aria-busy', 'true');
                const pendingIcon = icons.create('arrows-clockwise', { size: 18 });
				pendingIcon.classList.add('fotogenic-ai-icon--spin');
                generate.replaceChildren(pendingIcon, document.createTextNode(__( 'Starting…', 'fotogenic-ai' )));
                try {
                    const started = await onGenerate();
                    if (started === true) return;
                    if (review.firstElementChild !== state || currentView !== 'review') return;
                    generatePending = false;
                    generate.disabled = true;
                    generate.removeAttribute('aria-busy');
                    generate.replaceChildren(icons.create('sparkle', { weight: 'fill', size: 18 }), document.createTextNode(generateLabel));
                    const message = __( 'The reviewed request is no longer current. Return to Configure and review it again.', 'fotogenic-ai' );
                    notice.textContent = message;
                    onFeedback?.({
                        type: 'warning',
                        title: __( 'Review needs attention', 'fotogenic-ai' ),
                        message,
                        dedupeKey: 'studio-review-no-longer-current',
                    });
                } catch (_) {
                    if (review.firstElementChild !== state || currentView !== 'review') return;
                    generatePending = false;
                    generate.disabled = true;
                    generate.removeAttribute('aria-busy');
                    generate.replaceChildren(icons.create('sparkle', { weight: 'fill', size: 18 }), document.createTextNode(generateLabel));
                    const message = __( 'The generation action could not be prepared. Return to Configure and review the request again.', 'fotogenic-ai' );
                    notice.textContent = message;
                    onFeedback?.({
                        type: 'error',
                        title: __( 'Generation did not start', 'fotogenic-ai' ),
                        message,
                        dedupeKey: 'studio-review-start-failed',
                    });
                }
            });
            actions.append(edit, generate);
            footer.append(notice, actions);
            state.append(intro, workspace, footer);
            review.replaceChildren(state);

            const updateEstimate = (next) => {
                if (reviewDisposed || generatePending) return;
                estimate = next;
                metricValues[1].textContent = formatEstimate(next.totalCredits);
                metricValues[2].textContent = formatEstimate(next.balanceAfter);
                generateLabel = next.totalCredits === null ? actionLabel
                    : sprintf(__( '%1$s · est. %2$s credits', 'fotogenic-ai' ), actionLabel, numberFormatter.format(next.totalCredits));
                generate.replaceChildren(icons.create('sparkle', { weight: 'fill', size: 18 }), document.createTextNode(generateLabel));
                if (next.totalCredits !== null && next.balanceAfter !== null) estimateStatus.hidden = true;
            };
            const loadEstimate = async (retry = false) => {
                if (typeof onLoadEstimate !== 'function' || reviewDisposed) return;
                estimateStatus.hidden = false;
                const spinner = icons.create('arrows-clockwise', { size: 14 });
                spinner.classList.add('fotogenic-ai-icon--spin');
                estimateStatus.replaceChildren(spinner, document.createTextNode(__( 'Loading estimate…', 'fotogenic-ai' )));
                try { updateEstimate(await onLoadEstimate({ retry })); }
                catch (_) {
                    estimateStatus.textContent = __( 'Estimate could not be loaded.', 'fotogenic-ai' );
                }
                if (reviewDisposed || estimateStatus.hidden) return;
                const retryButton = element('button', 'fotogenic-ai-secondary-action', __( 'Retry', 'fotogenic-ai' ));
                retryButton.type = 'button';
                retryButton.setAttribute('aria-label', __( 'Retry loading estimate', 'fotogenic-ai' ));
                retryButton.addEventListener('click', () => void loadEstimate(true));
                estimateStatus.replaceChildren(document.createTextNode(__( 'Estimate could not be loaded.', 'fotogenic-ai' )), retryButton);
            };

            async function loadQueryPage() {
                if (generatePending || reviewDisposed) return;
                queryController?.abort();
                queryController = new AbortController();
                const requestId = ++queryRequest;
                const appending = infinite && renderedCount > 0;
                queryPending = true;
                queryError = '';
                if (!appending) generate.disabled = true;
                tableWrap.setAttribute('aria-busy', 'true');
                if (appending) renderInfiniteState();
                else renderLedger();
                let result;
                try {
                    result = typeof onLoadSourcePage === 'function'
                        ? await onLoadSourcePage({ page: sourcePage, perPage: sourcePageSize, signal: queryController.signal })
                        : null;
                } catch (error) {
                    result = { ok: false, aborted: error?.name === 'AbortError' };
                }
                if (reviewDisposed || requestId !== queryRequest) return;
                queryPending = false;
                tableWrap.setAttribute('aria-busy', 'false');
                if (result?.ok && Array.isArray(result.data?.items)) {
                    const knownIds = new Set(queryItems.map(item => item.id));
                    queryItems = infinite && sourcePage > 1
                        ? [...queryItems, ...result.data.items.filter(item => !knownIds.has(item.id))]
                        : result.data.items;
                    queryTotal = Math.max(0, Number(result.data.totalItems) || 0);
                    const lastPage = Math.max(1, Math.ceil(queryTotal / sourcePageSize));
                    if (sourcePage > lastPage) {
                        sourcePage = lastPage;
                        void loadQueryPage();
                        return;
                    }
                } else {
                    queryError = result?.message || __( 'Products could not be loaded. Try again.', 'fotogenic-ai' );
                }
                if (!appending || queryTotal < 1) {
                    generate.disabled = !canGenerate || typeof onGenerate !== 'function' || Boolean(queryError) || queryTotal < 1;
                }
                renderLedger(infinite);
            }
            const onLayoutChange = () => {
                const next = desktopMedia.matches && (!querySelection || pagedQuery);
                if (next === infinite || reviewDisposed) return;
                infinite = next;
                sourcePage = 1;
                sourcePageSize = 10;
                queryItems = [];
                renderedCount = 0;
                if (pageSizeLabel) {
                    pageSizeLabel.hidden = infinite;
                    pageSizeLabel.querySelector('select').value = '10';
                }
                tableWrap.scrollTop = 0;
                if (pagedQuery) void loadQueryPage();
                else renderLedger();
            };
            const onScroll = () => {
                if (tableWrap.scrollHeight - tableWrap.scrollTop - tableWrap.clientHeight < 160) loadMore();
            };
            desktopMedia?.addEventListener('change', onLayoutChange);
            if (typeof window.IntersectionObserver === 'function') {
                infiniteObserver = new window.IntersectionObserver((entries) => {
                    if (entries.some(entry => entry.isIntersecting)) loadMore();
                }, { root: tableWrap, rootMargin: '0px 0px 160px 0px', threshold: 0.01 });
                infiniteObserver.observe(sentinel);
            } else tableWrap.addEventListener('scroll', onScroll, { passive: true });
            if (pagedQuery) void loadQueryPage();
            if (estimate.totalCredits === null || estimate.balanceAfter === null) void loadEstimate();
            return Object.freeze({ updateEstimate });
        };

        const showBlocking = (message) => {
            const error = views.get('error');
            if (!error) return;
            error.replaceChildren();
            const state = element('div', 'fotogenic-ai-studio-state');
            const header = element('header', 'fotogenic-ai-studio-state__header');
            const heading = element('h2', '', __( 'Studio unavailable', 'fotogenic-ai' ));
            heading.tabIndex = -1;
            header.append(heading, element('p', '', String(message || __( 'The Studio workflow definition is unavailable.', 'fotogenic-ai' ))));
            const actions = element('div', 'fotogenic-ai-studio-actions');
            const reload = element('button', 'primary', __( 'Reload Studio', 'fotogenic-ai' ));
            reload.type = 'button';
            reload.addEventListener('click', () => window.location?.reload?.());
            actions.appendChild(reload);
            state.append(header, actions);
            error.appendChild(state);
            setView('error', { announcement: __( 'Studio unavailable.', 'fotogenic-ai' ) });
            if (workflowControl) workflowControl.disabled = true;
			toolbarController?.setDisabled?.(true);
        };

        return Object.freeze({
            setView,
            sourceTypeHasFocus,
            renderSourceTypes,
            syncHeaderControls,
            renderReview,
            showBlocking,
            announce,
            setStageAvailability,
            bindStageNavigation(handler) {
                stageRequestHandler = typeof handler === 'function' ? handler : null;
            },
            getCurrentView: () => currentView,
        });
    };

    Studio.shell = Object.freeze({ createShell });
})();
