<template>
	<div :class="prefixCls">
		<header class="vg-hero">
			<div class="vg-hero-text">
				<h1 class="vg-hero-title">视频生成</h1>
				<p class="vg-hero-desc">文生视频、图生视频，选择模型后生成成片</p>
			</div>
			<div class="vg-type-select" @click.stop>
				<span class="vg-hero-mode-label">生成模式</span>
				<button
					type="button"
					class="vg-type-trigger"
					:class="{ 'is-open': typeMenuOpen }"
					@click.stop="typeMenuOpen = !typeMenuOpen"
					:aria-expanded="typeMenuOpen">
					<span class="vg-type-label">{{ videoModeLabel }}</span>
					<svg class="vg-type-chevron" width="1em" height="1em" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
						<path fill-rule="evenodd" clip-rule="evenodd" d="M21.01 7.982A1.2 1.2 0 0 1 21 9.679l-8.156 8.06a1.2 1.2 0 0 1-1.688 0L3 9.68a1.2 1.2 0 0 1 1.687-1.707L12 15.199l7.313-7.227a1.2 1.2 0 0 1 1.697.01Z" fill="currentColor" />
					</svg>
				</button>
				<transition name="vg-fade">
					<div v-show="typeMenuOpen" class="vg-type-dropdown">
						<div class="vg-type-dropdown-bg" />
						<ul class="vg-type-options" role="listbox">
							<li
								v-for="opt in videoModeOptions"
								:key="opt.value"
								:class="['vg-type-option', { active: videoMode === opt.value }]"
								role="option"
								@click="pickVideoMode(opt.value)">
								{{ opt.label }}
							</li>
						</ul>
					</div>
				</transition>
			</div>
		</header>

		<div class="vg-body">
			<!-- 左侧：生成器 -->
			<div class="vg-generator">
				<div class="vg-generator-inner">
					<!-- 参考上传区（图生视频）；文生视频时显示轻量占位 -->
					<div class="vg-refs">
						<div class="vg-ref-group">
							<div class="vg-ref-bg" />
							<div v-if="isImageVideoMode" class="vg-ref-content">
								<div class="vg-ref-tilt">
									<div class="upload-inner">
										<div class="upload-title">
											<div class="upload-title-left">
												{{ imageUploadPrimaryLabel }}
											</div>
											<div class="upload-title-tip">
												{{ $t('common.uploadImageTip') || '支持PNG/JPG，最大10MB' }}
											</div>
										</div>
										<mf-image-upload
											v-if="videoMode !== 'image-reference'"
											listType="picture-card"
											uploadHeight="140px"
											:show-file-list="false"
											:content="$t('common.uploadFirstPlaceholder') || '点击上传'"
											v-model="firstUrl" />
										<mf-image-upload
											v-else
											listType="picture-card"
											uploadHeight="140px"
											:show-file-list="false"
											:content="'上传参考图'"
											v-model="referenceUrl" />
										<div class="last-frame" v-if="videoMode === 'image-first-last-frame'">
											<div class="upload-title-left">
												{{ $t('common.uploadLastPlaceholder') || '尾帧（可选）' }}
											</div>
											<mf-image-upload
												listType="picture-card"
												uploadHeight="100px"
												:show-file-list="false"
												:content="$t('common.uploadLastPlaceholder') || '上传尾帧'"
												v-model="lastUrl" />
										</div>
									</div>
								</div>
							</div>
							<div v-else class="vg-ref-placeholder">
								<div class="vg-ref-placeholder-icon" aria-hidden="true">
									<svg width="28" height="28" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
										<path fill-rule="evenodd" clip-rule="evenodd" d="M10.8 20a1.2 1.2 0 0 0 2.4 0v-6.8H20a1.2 1.2 0 1 0 0-2.4h-6.8V4a1.2 1.2 0 0 0-2.4 0v6.8H4a1.2 1.2 0 0 0 0 2.4h6.8V20Z" fill="currentColor" />
									</svg>
								</div>
								<p class="vg-ref-placeholder-text">切换到「图生视频」可上传参考图</p>
							</div>
						</div>
					</div>

					<div class="vg-main-column">
						<div class="rich-editor-container">
							<RichTextEditor
								v-model="editorContent"
								:placeholder="
									$t('common.textVideoPlaceholder') ||
									'描述画面与动态，例如：阳光下的女孩在海边起舞…'
								"
								:uploaded-images="uploadedImages"
								@text-change="handleTextChange"
								@image-upload="handleImageUpload"
							/>
						</div>

						<div class="vg-toolbar">
							<div class="vg-toolbar-settings">
								<div class="vg-model-wrap">
									<span class="vg-model-icon" aria-hidden="true">
										<svg width="1em" height="1em" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
											<path fill="currentColor" d="M11.805 5.786c1.25-.926 2.193-1.373 2.471-1.096.489.488-1.261 3.03-3.909 5.677-4.33 4.331-6.715 8.968-5.326 10.358.29.29.723.416 1.264.394.421.017.92-.07 1.48-.249-2.117.9-3.859 1.005-4.76.104-1.874-1.874.61-7.402 5.553-12.353l.022-.02c.03-.032.062-.063.093-.094l.065-.064.11-.108c.97-.95 1.96-1.804 2.937-2.549Zm5.55 11.57c1.532-1.531 3.2-2.347 3.725-1.822.525.525-.29 2.192-1.822 3.724-1.532 1.531-3.2 2.347-3.725 1.822-.524-.525.291-2.192 1.822-3.724Z" />
										</svg>
									</span>
									<a-select v-model="selectedModel" class="vg-model-select">
										<a-option v-for="option in modelOptions" :key="option.value" :value="option.value">
											{{ option.label }}
										</a-option>
									</a-select>
								</div>
							</div>
							<div class="vg-toolbar-actions">
								<button
									type="button"
									class="vg-submit-circle"
									:disabled="generateLoading"
									@click="generateVideo"
									:title="price ? $t('common.createVideo', { price }) : $t('common.generateVideo')">
									<span class="vg-submit-circle-inner">
										<svg width="22" height="22" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
											<path fill="currentColor" d="M12.002 3c.424 0 .806.177 1.079.46l5.98 5.98.103.114a1.5 1.5 0 0 1-2.225 2.006l-3.437-3.436V19.5l-.008.153a1.5 1.5 0 0 1-2.985 0l-.007-.153V8.122l-3.44 3.438a1.5 1.5 0 0 1-2.225-2.006l.103-.115 6-5.999.025-.025.059-.052.044-.037c.029-.023.06-.044.09-.065l.014-.01a1.43 1.43 0 0 1 .101-.062l.03-.017c.209-.11.447-.172.699-.172Z" />
										</svg>
									</span>
								</button>
							</div>
						</div>

						<div class="vg-submit-hint">
							{{
								price
									? `${$t('common.createVideo', { price: price })} · ${$t('common.generateTip') || '生成将消耗余额'}`
									: $t('common.generateTip') || '生成视频需要消耗余额'
							}}
						</div>
					</div>
				</div>
			</div>

			<!-- 右侧预览 -->
			<div class="right" :class="{ 'has-result': showResult }">
				<div v-if="!showResult" class="empty-state">
					<div class="empty-visual">
						<div class="empty-ring" />
						<img src="/images/empty-video.png" alt="" />
					</div>
					<p>生成的视频将在这里显示</p>
				</div>

				<div v-else class="result-container">
					<div class="result-video-wrapper">
						<video
							v-if="videoUrl"
							:src="videoUrl"
							controls
							autoplay
							class="result-video"
							:poster="posterUrl">
							您的浏览器不支持视频播放。
						</video>
					</div>

					<div class="result-actions">
						<mf-button v-if="canCancel" @click="cancelTask" type="danger"> 取消任务 </mf-button>
						<mf-button @click="saveVideo" :disabled="!videoUrl">
							<a-image src="/images/btn_save.png" /> 保存视频
						</mf-button>
						<mf-button @click="closeResult">
							<a-image src="/images/btn_close.png" /> 关闭
						</mf-button>
					</div>
				</div>
			</div>
		</div>

		<section class="vg-task-section">
			<div class="vg-task-head">
				<h3 class="vg-task-title">我的视频任务</h3>
				<button type="button" class="vg-task-refresh" @click="loadTaskList">刷新</button>
			</div>
			<p v-if="!taskRows.length" class="vg-task-empty">暂无任务，生成后将显示在此</p>
			<ul v-else class="vg-task-list">
				<li v-for="row in taskRows" :key="row.id" class="vg-task-item">
					<div class="vg-task-meta">
						<span class="vg-task-id">{{ row.result || '—' }}</span>
						<span class="vg-task-st">{{ taskStatusText(row) }}</span>
					</div>
					<div class="vg-task-actions">
						<button type="button" class="vg-link" v-if="row.result && row.status === 0" @click="cancelRowTask(row)">
							取消
						</button>
					</div>
				</li>
			</ul>
		</section>
	</div>
</template>

<script>
import { mapGetters } from 'vuex'
import RichTextEditor from '@/components/RichTextEditor.vue'

export default {
	name: 'VideoGen',
	data() {
		return {
			prefixCls: 'video-gen',
			videoMode: 'text-to-video',
			videoModeOptions: [
				{ value: 'text-to-video', label: '文生视频' },
				{ value: 'image-first-frame', label: '图生视频 · 首帧' },
				{ value: 'image-first-last-frame', label: '图生视频 · 首尾帧' },
				{ value: 'image-reference', label: '图生视频 · 参考图' }
			],
			firstUrl: '',
			lastUrl: '',
			referenceUrl: '',
			editorContent: '',
			interval: null,
			videoUrl: null,
			videoLoading: false,
			generateLoading: false,
			videoId: null,
			showResult: false,
			price: null,
			id: null,
			modelOptions: [
				{ label: 'Seedance 2.0', value: 'ep-20260326165811-dlkth' },
				{ label: 'Seedance 2.0 Fast', value: 'ep-20260326170056-dkj9m' }
			],
			selectedModel: 'ep-20260326165811-dlkth',
			uploadedImages: [],
			maxPollAttempts: 40,
			typeMenuOpen: false,
			taskRows: []
		}
	},
	components: {
		RichTextEditor
	},
	computed: {
		...mapGetters(['lang']),
		canCancel() {
			return !!this.videoId && !this.videoUrl
		},
		videoModeLabel() {
			const o = this.videoModeOptions.find((x) => x.value === this.videoMode)
			return o ? o.label : '文生视频'
		},
		isImageVideoMode() {
			return this.videoMode !== 'text-to-video'
		},
		imageUploadPrimaryLabel() {
			if (this.videoMode === 'image-reference') return '上传参考图'
			if (this.videoMode === 'image-first-last-frame') return '上传首帧'
			return this.$t('common.uploadFirstImage') || '上传首帧'
		},
		posterUrl() {
			const f = this.videoMode === 'image-reference' ? this.referenceUrl : this.firstUrl
			if (!f) return ''
			return typeof f === 'object' ? (f.url || '') : f
		}
	},
	mounted() {
		this.loadPriceInfo()
		this.loadTaskList()
		document.addEventListener('click', this.onDocClick)
	},
	beforeUnmount() {
		this.destroyInterval()
		document.removeEventListener('click', this.onDocClick)
	},
	methods: {
		onDocClick() {
			this.typeMenuOpen = false
		},
		pickVideoMode(m) {
			this.videoMode = m
			this.firstUrl = ''
			this.lastUrl = ''
			this.referenceUrl = ''
			this.editorContent = ''
			this.uploadedImages = []
			this.typeMenuOpen = false
		},

		plainPromptText() {
			if (!this.editorContent || !String(this.editorContent).trim()) return ''
			const s = String(this.editorContent)
			if (s.indexOf('<') < 0) return s.trim()
			const d = document.createElement('div')
			d.innerHTML = s
			return (d.textContent || d.innerText || '').trim()
		},

		async loadTaskList() {
			try {
				const res = await this.$axios({
					url: 'api/portal/video/tasks',
					method: 'GET',
					data: { pageNum: 1, pageSize: 30 }
				})
				if (res.code === 200 && res.rows) this.taskRows = res.rows
			} catch (_) {
				/* 未登录等忽略 */
			}
		},

		taskStatusText(row) {
			if (row.status === 1) return '已完成'
			if (row.status === 2) return '已失败/已取消'
			return '进行中'
		},

		async cancelRowTask(row) {
			const tid = row.result
			if (!tid) return
			await this.cancelTaskById(tid)
			await this.loadTaskList()
		},

		async loadPriceInfo() {
			this.$axios({
				url: 'api/manager/selectInfo',
				method: 'GET',
				data: { aiType: '21' }
			}).then((res) => {
				this.price = res.data?.price
				this.id = res.data?.id
			})
		},

		handleTextChange(content) {
			this.editorContent = content
		},

		handleImageUpload(imageInfo) {
			if (imageInfo && imageInfo.url) {
				this.uploadedImages.push({
					url: imageInfo.url,
					name: imageInfo.name || 'image'
				})
			}
		},

		generateVideo() {
			const text = this.plainPromptText() || '一个优雅的女孩在阳光下跳舞'

			if (this.isImageVideoMode) {
				if (this.videoMode === 'image-reference') {
					const ref = typeof this.referenceUrl === 'object' ? this.referenceUrl?.url : this.referenceUrl
					if (!ref) {
						this.$message.error('请上传参考图')
						return
					}
				} else {
					const firstImageUrl = typeof this.firstUrl === 'object' ? this.firstUrl?.url : this.firstUrl
					if (!firstImageUrl) {
						this.$message.error(this.$t('common.uploadFirstImageError') || '请上传首帧图')
						return
					}
					if (this.videoMode === 'image-first-last-frame') {
						const last = typeof this.lastUrl === 'object' ? this.lastUrl?.url : this.lastUrl
						if (!last) {
							this.$message.error('请上传尾帧图')
							return
						}
					}
				}
			} else if (!this.plainPromptText()) {
				this.$message.error('请输入视频描述文本')
				return
			}

			this.generateLoading = true

			const params = {
				text,
				functionType: '21',
				model: this.selectedModel
			}

			const urlMap = {
				'text-to-video': 'api/portal/video/text-to-video',
				'image-first-frame': 'api/portal/video/image-first-frame',
				'image-first-last-frame': 'api/portal/video/image-first-last-frame',
				'image-reference': 'api/portal/video/image-reference'
			}
			const path = urlMap[this.videoMode]
			if (this.videoMode === 'image-first-frame' || this.videoMode === 'image-first-last-frame') {
				params.firstUrl = typeof this.firstUrl === 'object' ? this.firstUrl.url : this.firstUrl
			}
			if (this.videoMode === 'image-first-last-frame') {
				params.lastUrl = typeof this.lastUrl === 'object' ? this.lastUrl.url : this.lastUrl
			}
			if (this.videoMode === 'image-reference') {
				params.referenceUrl = typeof this.referenceUrl === 'object' ? this.referenceUrl.url : this.referenceUrl
			}

			this.$axios({
				url: path,
				method: 'POST',
				data: params
			}).then((res) => {
				this.generateLoading = false
				if (res.code == 200) {
					this.videoId = res.data.id
					this.showResult = true
					this.getVideo(res.data.id)
					this.loadTaskList()
				} else if (res.code == -1) {
					this.$confirm({
						title: this.$t('common.notice'),
						content: this.$t('common.balenceLow'),
						onOk: () => this.$router.push('/recharge')
					})
				}
			}).catch(() => {
				this.generateLoading = false
			})
		},

		getVideo(videoId) {
			let attempts = 0
			this.interval = setInterval(() => {
				attempts++
				if (attempts > this.maxPollAttempts) {
					this.$message.warning('视频生成超时')
					this.destroyInterval()
					return
				}

				this.$axios({
					url: `api/portal/video/tasks/${videoId}`,
					method: 'GET'
				}).then((res) => {
					if (res.code == 200 && res.data.status === 'succeeded') {
						this.videoUrl = res.data.content?.video_url || res.data.video_url
						this.destroyInterval()
					}
				})
			}, 3000)
		},

		destroyInterval() {
			if (this.interval) {
				clearInterval(this.interval)
				this.interval = null
			}
		},

		saveVideo() {
			if (!this.videoUrl) return
			const link = document.createElement('a')
			link.href = this.videoUrl
			link.download = `video_${Date.now()}.mp4`
			document.body.appendChild(link)
			link.click()
			document.body.removeChild(link)
		},

		closeResult() {
			this.showResult = false
			this.videoUrl = null
			this.videoId = null
			this.taskStatus = null
		},

		async cancelTask() {
			if (!this.videoId) return
			await this.cancelTaskById(this.videoId)
		},

		async cancelTaskById(taskId) {
			return new Promise((resolve) => {
				this.$confirm({
					title: '取消任务',
					content: '确定要取消当前视频生成任务吗？取消后余额将退回。',
					okText: '确定取消',
					cancelText: '关闭',
					onOk: async () => {
						try {
							const res = await this.$axios({
								url: `api/portal/video/tasks/${taskId}`,
								method: 'DELETE'
							})

							if (res.code === 200) {
								this.$message.success(res.msg || '任务已取消，余额已退回')
								this.destroyInterval()
								this.showResult = false
								this.videoUrl = null
								this.videoId = null
								this.loadTaskList()
							} else {
								this.$message.error(res.msg || '取消失败')
							}
						} catch (error) {
							this.$message.error('取消请求失败')
						}
						resolve()
					},
					onCancel: () => resolve()
				})
			})
		}
	}
}
</script>

<style lang="less" scoped>
.video-gen {
	--vg-cyan: #00cae0;
	--vg-ink: #0a0b0d;
	--vg-panel: rgba(22, 24, 30, 0.92);
	--vg-border: rgba(255, 255, 255, 0.08);
	--vg-text: rgba(255, 255, 255, 0.88);
	--vg-muted: rgba(255, 255, 255, 0.45);

	display: flex;
	flex-direction: column;
	min-height: 100%;
	gap: 20px;
	padding: 28px 28px 32px;
	background: radial-gradient(120% 80% at 50% -20%, rgba(0, 202, 224, 0.12), transparent 55%),
		radial-gradient(80% 50% at 100% 30%, rgba(33, 151, 255, 0.08), transparent 45%), var(--vg-ink);
	box-sizing: border-box;
}

/* —— Hero —— */
.vg-hero {
	display: flex;
	flex-wrap: wrap;
	align-items: flex-end;
	justify-content: space-between;
	gap: 16px 24px;
	padding: 4px 0 16px;
	border-bottom: 1px solid var(--vg-border);
	margin-bottom: 4px;
}

.vg-hero-text {
	flex: 1;
	min-width: 200px;
}

.vg-hero-title {
	margin: 0 0 6px;
	font-size: clamp(22px, 3vw, 30px);
	font-weight: 700;
	letter-spacing: -0.02em;
	color: var(--vg-text);
	line-height: 1.2;
}

.vg-hero-desc {
	margin: 0;
	font-size: 14px;
	color: var(--vg-muted);
	line-height: 1.5;
}

.vg-type-select {
	position: relative;
	display: inline-flex;
	align-items: center;
	flex-wrap: wrap;
	gap: 10px 12px;
}

.vg-hero-mode-label {
	font-size: 13px;
	color: var(--vg-muted);
	white-space: nowrap;
}

.vg-type-trigger {
	display: inline-flex;
	align-items: center;
	gap: 8px;
	padding: 8px 14px;
	margin: 0;
	background: linear-gradient(180deg, rgba(255, 255, 255, 0.1), rgba(255, 255, 255, 0.03));
	border: 1px solid var(--vg-border);
	border-radius: 12px;
	color: #fff;
	font-size: 15px;
	font-weight: 700;
	font-family: inherit;
	cursor: pointer;
	transition: border-color 0.2s, box-shadow 0.2s;
	box-shadow: 0 0 0 1px rgba(0, 202, 224, 0.15);

	&:hover {
		border-color: rgba(0, 202, 224, 0.45);
	}

	&.is-open .vg-type-chevron {
		transform: rotate(180deg);
	}
}

.vg-type-label {
	background: linear-gradient(90deg, #fff, rgba(0, 202, 224, 0.95));
	-webkit-background-clip: text;
	background-clip: text;
	color: transparent;
}

.vg-type-chevron {
	width: 0.85em;
	height: 0.85em;
	opacity: 0.75;
	transition: transform 0.2s cubic-bezier(0.15, 0.75, 0.3, 1);
}

.vg-type-dropdown {
	position: absolute;
	left: 0;
	top: calc(100% + 8px);
	min-width: 100%;
	z-index: 50;
	border-radius: 14px;
	overflow: hidden;
	box-shadow: 0 16px 48px rgba(0, 0, 0, 0.45);
}

.vg-type-dropdown-bg {
	position: absolute;
	inset: 0;
	background: rgba(18, 20, 26, 0.98);
	backdrop-filter: blur(12px);
	border: 1px solid var(--vg-border);
	border-radius: 14px;
}

.vg-type-options {
	position: relative;
	list-style: none;
	margin: 0;
	padding: 6px;
	z-index: 1;
}

.vg-type-option {
	padding: 12px 16px;
	border-radius: 10px;
	color: var(--vg-muted);
	font-size: 15px;
	font-weight: 600;
	cursor: pointer;
	transition: background 0.15s, color 0.15s;

	&:hover {
		background: rgba(255, 255, 255, 0.06);
		color: var(--vg-text);
	}

	&.active {
		color: var(--vg-cyan);
		background: rgba(0, 202, 224, 0.1);
	}
}

.vg-fade-enter-active,
.vg-fade-leave-active {
	transition: opacity 0.15s ease, transform 0.15s ease;
}
.vg-fade-enter-from,
.vg-fade-leave-to {
	opacity: 0;
	transform: translateY(-6px);
}

/* —— Body —— */
.vg-body {
	display: flex;
	flex: 1;
	gap: 20px;
	align-items: stretch;
	min-height: 420px;
}

.vg-generator {
	flex: 0 0 min(520px, 44vw);
	min-width: 300px;
	display: flex;
}

.vg-generator-inner {
	flex: 1;
	display: flex;
	flex-direction: column;
	gap: 16px;
	padding: 22px;
	background: var(--vg-panel);
	border: 1px solid var(--vg-border);
	border-radius: 20px;
	box-shadow: 0 24px 64px rgba(0, 0, 0, 0.35), inset 0 1px 0 rgba(255, 255, 255, 0.06);
}

.vg-refs {
	flex-shrink: 0;
}

.vg-ref-group {
	position: relative;
	border-radius: 16px;
	overflow: hidden;
	min-height: 92px;
}

.vg-ref-bg {
	position: absolute;
	inset: 0;
	background: linear-gradient(135deg, rgba(0, 202, 224, 0.08), rgba(33, 151, 255, 0.05));
	border: 1px dashed rgba(255, 255, 255, 0.12);
	border-radius: 16px;
	pointer-events: none;
}

.vg-ref-content {
	position: relative;
	padding: 12px;
}

.vg-ref-tilt {
	transform: rotate(-1.5deg);
	transition: transform 0.25s ease;

	&:hover {
		transform: rotate(0deg);
	}
}

.upload-inner {
	transform: rotate(1.5deg);

	.upload-title {
		margin-bottom: 10px;
	}
	.upload-title-left {
		color: var(--vg-text);
		font-weight: 600;
		font-size: 14px;
	}
	.upload-title-tip {
		color: var(--vg-muted);
		font-size: 12px;
		margin-top: 4px;
	}
	.last-frame {
		margin-top: 14px;
	}
}

.vg-ref-placeholder {
	position: relative;
	display: flex;
	align-items: center;
	gap: 14px;
	padding: 18px 20px;
	min-height: 88px;
}

.vg-ref-placeholder-icon {
	flex-shrink: 0;
	width: 48px;
	height: 48px;
	display: flex;
	align-items: center;
	justify-content: center;
	border-radius: 14px;
	background: rgba(255, 255, 255, 0.06);
	color: var(--vg-cyan);
}

.vg-ref-placeholder-text {
	margin: 0;
	font-size: 13px;
	line-height: 1.5;
	color: var(--vg-muted);
}

.vg-main-column {
	display: flex;
	flex-direction: column;
	flex: 1;
	min-height: 0;
}

.rich-editor-container {
	flex: 1;
	min-height: 260px;
	margin: 0;
	display: flex;
	flex-direction: column;

	:deep(.rich-editor-root) {
		flex: 1;
		display: flex;
		flex-direction: column;
	}

	:deep(.rich-editor) {
		flex: 1;
		min-height: 220px !important;
		font-size: 15px;
		line-height: 1.7;
		padding: 16px 18px;
		border-radius: 14px !important;
		background: rgba(0, 0, 0, 0.25) !important;
		border: 1px solid var(--vg-border) !important;
	}
}

.vg-toolbar {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 16px;
	margin-top: 14px;
	padding-top: 14px;
	border-top: 1px solid var(--vg-border);
}

.vg-toolbar-settings {
	flex: 1;
	min-width: 0;
}

.vg-model-wrap {
	display: flex;
	align-items: center;
	gap: 10px;
	padding: 4px 12px;
	background: rgba(0, 0, 0, 0.2);
	border-radius: 12px;
	border: 1px solid var(--vg-border);
}

.vg-model-icon {
	display: flex;
	color: var(--vg-cyan);
	opacity: 0.85;
}

.vg-model-select {
	flex: 1;
	min-width: 0;

	:deep(.arco-select-view-single) {
		background: transparent !important;
		border: none !important;
		color: var(--vg-text) !important;
	}
	:deep(.arco-select-view-value) {
		color: var(--vg-text) !important;
	}
}

.vg-toolbar-actions {
	flex-shrink: 0;
}

.vg-submit-circle {
	width: 52px;
	height: 52px;
	padding: 0;
	border: none;
	border-radius: 50%;
	cursor: pointer;
	background: linear-gradient(145deg, #00d4e8, #0090a8);
	box-shadow: 0 0 0 2px rgba(0, 202, 224, 0.35), 0 8px 24px rgba(0, 150, 180, 0.35);
	color: #fff;
	display: flex;
	align-items: center;
	justify-content: center;
	transition: transform 0.2s, box-shadow 0.2s, opacity 0.2s;

	&:hover:not(:disabled) {
		transform: scale(1.04);
		box-shadow: 0 0 0 3px rgba(0, 202, 224, 0.5), 0 12px 28px rgba(0, 150, 180, 0.45);
	}

	&:disabled {
		opacity: 0.45;
		cursor: not-allowed;
		transform: none;
	}
}

.vg-submit-circle-inner {
	display: flex;
	align-items: center;
	justify-content: center;
}

.vg-submit-hint {
	margin-top: 12px;
	font-size: 12px;
	color: var(--vg-muted);
	line-height: 1.5;
}

/* —— 右侧预览 —— */
.right {
	flex: 1;
	background: rgba(0, 0, 0, 0.35);
	border: 1px solid var(--vg-border);
	border-radius: 20px;
	display: flex;
	align-items: center;
	justify-content: center;
	position: relative;
	overflow: hidden;
	min-height: 360px;

	&.has-result {
		align-items: flex-start;
		padding: 20px;
	}
}

.empty-state {
	text-align: center;
	color: var(--vg-muted);
	padding: 24px;

	.empty-visual {
		position: relative;
		display: inline-block;
		margin-bottom: 16px;
	}

	.empty-ring {
		position: absolute;
		inset: -12px;
		border-radius: 50%;
		border: 1px solid rgba(0, 202, 224, 0.2);
		animation: vg-pulse 2.4s ease-in-out infinite;
	}

	img {
		position: relative;
		width: 120px;
		opacity: 0.55;
	}

	p {
		margin: 0;
		font-size: 14px;
	}
}

@keyframes vg-pulse {
	0%,
	100% {
		transform: scale(1);
		opacity: 0.5;
	}
	50% {
		transform: scale(1.08);
		opacity: 0.85;
	}
}

.result-video-wrapper {
	width: 100%;
	max-width: 720px;
}

.result-video {
	width: 100%;
	border-radius: 12px;
}

.result-actions {
	margin-top: 16px;
	display: flex;
	flex-wrap: wrap;
	gap: 10px;
}

@media (max-width: 640px) {
	.vg-body {
		flex-direction: column;
	}
	.vg-generator {
		flex: none;
		width: 100%;
	}
	.vg-hero {
		flex-direction: column;
		align-items: stretch;
	}
	.vg-type-select {
		flex-direction: column;
		align-items: flex-start;
	}
}

.vg-task-empty {
	margin: 0;
	font-size: 13px;
	color: var(--vg-muted);
}

.vg-task-section {
	margin-top: 8px;
	padding: 18px 22px;
	background: var(--vg-panel);
	border: 1px solid var(--vg-border);
	border-radius: 16px;
}

.vg-task-head {
	display: flex;
	align-items: center;
	justify-content: space-between;
	margin-bottom: 12px;
}

.vg-task-title {
	margin: 0;
	font-size: 15px;
	font-weight: 600;
	color: var(--vg-text);
}

.vg-task-refresh {
	font-size: 13px;
	padding: 6px 12px;
	border-radius: 8px;
	border: 1px solid var(--vg-border);
	background: rgba(255, 255, 255, 0.06);
	color: var(--vg-muted);
	cursor: pointer;
}

.vg-task-refresh:hover {
	color: var(--vg-cyan);
	border-color: rgba(0, 202, 224, 0.35);
}

.vg-task-list {
	list-style: none;
	margin: 0;
	padding: 0;
	display: flex;
	flex-direction: column;
	gap: 8px;
	max-height: 200px;
	overflow: auto;
}

.vg-task-item {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 12px;
	padding: 10px 12px;
	border-radius: 10px;
	background: rgba(0, 0, 0, 0.2);
	font-size: 12px;
}

.vg-task-meta {
	display: flex;
	flex-direction: column;
	gap: 4px;
	min-width: 0;
}

.vg-task-id {
	color: var(--vg-muted);
	word-break: break-all;
}

.vg-task-st {
	color: var(--vg-cyan);
	font-weight: 600;
}

.vg-link {
	background: none;
	border: none;
	padding: 0;
	color: #ff6b6b;
	cursor: pointer;
	font-size: 12px;
}

.vg-link:hover {
	text-decoration: underline;
}
</style>
