seaislee1209 acbd2e30ad Initial commit: Air Spark project
- frontend/: Next.js 16 app (App Router, React 19, Tailwind v4)
- skills/: project skills (seedance, automation, trae-agents, etc.)
- Docs: PRD, UI-Design-System, DEV-LOG, seedance integration notes
- skills-lock.json: skills version lock

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-14 16:08:49 +08:00

1395 lines
66 KiB
TypeScript

"use client";
import { useState } from "react";
import {
Zap,
Settings,
Users,
Check,
X,
RefreshCw,
ChevronRight,
ChevronDown,
Play,
AlertTriangle,
Pencil,
Loader2,
Info,
FolderPlus,
Search,
ChevronsUpDown,
Trash2,
Hash,
User,
} from "lucide-react";
/* ─────────────────────────────────────────────
Section wrapper
───────────────────────────────────────────── */
function Section({
title,
children,
}: {
title: string;
children: React.ReactNode;
}) {
return (
<section className="mb-16">
<h2 className="font-[family-name:var(--font-heading)] text-2xl font-semibold mb-6 text-text-primary">
{title}
</h2>
{children}
</section>
);
}
/* ─────────────────────────────────────────────
Main Showcase Page
───────────────────────────────────────────── */
export default function ShowcasePage() {
const [logOpen, setLogOpen] = useState(false);
const [confirmCountdown, setConfirmCountdown] = useState<number | null>(null);
const [selectOpen, setSelectOpen] = useState(false);
const [selectValue, setSelectValue] = useState("Original Animation");
const [activeTab, setActiveTab] = useState("overview");
const [activeSeg, setActiveSeg] = useState("Grid");
const [lang, setLang] = useState<"en" | "zh">("en");
const t = lang === "zh" ? {
// Page
title: "组件展示",
subtitle: "Air Spark 设计系统 v0.4 — 视觉规范源文件",
// Nav
navProject: "T仔的上班日记 / 第一集",
// Sections
s2: "2. 色彩 & 玻璃层",
bgLayers: "背景层",
glassCards: "玻璃卡片",
stdGlass: "标准玻璃",
stdGlassDesc: "bg-white/[0.06] + 背景模糊",
activeGlass: "激活玻璃",
activeGlassDesc: "+ 品牌光晕边框",
hoverGlass: "悬停玻璃",
hoverGlassDesc: "悬停 → bg-white/[0.12]",
accentColors: "品牌 & 状态色",
s3: "3. 字体排版",
typoDisplay: "Air Spark",
typoH1: "流水线总览",
typoH2: "第六阶段 — 视频生成",
typoH3: "角色设计审核",
typoBody: "导演与 AI 对话生成剧本,一键触发七阶段自动化流水线,输出成片视频。整个过程全并发执行,无需手动干预。",
typoCn: "思源黑体用于中文正文排版,笔画均匀、重心稳定,与 DM Sans 在视觉密度上高度匹配。",
s4: "4. 按钮",
btnConfirm: "确认并启动",
btnContinue: "继续编辑",
btnWaiting: "等待中...",
btnProcessing: "处理中...",
s5: "5. 状态格子(第六阶段片段网格)",
stageTitle: "第六阶段 — 视频生成",
completed: "已完成",
remaining: "56% — 预计剩余 ~8 分钟",
done: "完成",
running: "生成中",
pending: "队列中",
failed: "失败",
seg08fail: "片段 08 — 重试 3 次后超时失败",
editPrompt: "编辑提示词",
retry: "重跑",
s6: "6. 流水线步骤条",
steps: ["剧本", "提示词提取", "图片资产", "分镜", "切分", "视频生成", "时间轴"],
s7: "7. 审核卡片(图片资产审核)",
charRef: "角色参考图",
approved: "已通过",
approveStatus: "3 / 4 角色已通过。请重跑失败角色后继续。",
confirmAll: "全部确认 & 继续",
cancel: "取消",
launchIn: "即将启动",
s8: "8. 骨架屏加载",
s9: "9. 剧本确认(第一阶段)",
scriptReady: "剧本已就绪",
scriptInfo: "共 8 场景 / 预估 4 分 20 秒",
confirmScript: "确认剧本,启动流水线",
continueEdit: "继续修改剧本",
s10: "10. 可折叠日志面板",
sysLog: "系统日志",
s11: "11. 模态框(内联演示)",
regenTitle: "重新生成片段?",
regenDesc: "将使用修改后的提示词重新提交片段 08 到 Seedance。之前的结果将被覆盖。",
regenBtn: "重新生成",
s12: "12. 表单元素",
projectName: "项目名称",
projectPlaceholder: "输入项目名称...",
contentType: "内容类型",
scriptPrompt: "剧本提示词",
scriptPlaceholder: "描述你的故事...",
apiKey: "API Key",
apiError: "无效的 API Key 格式",
s13: "13. Toast 通知(左边框)",
toastSuccess: "保存成功",
toastSuccessDesc: "剧本已保存为正式版本",
toastError: "片段 08 失败",
toastErrorDesc: "Seedance API 超时,已重试 3 次",
toastWarning: "等待审核",
toastWarningDesc: "角色设计需要导演审核",
toastInfo: "流水线已启动",
toastInfoDesc: "正在运行第二阶段 — 提示词提取",
s14: "14. 空状态",
noProjects: "还没有项目",
noProjectsDesc: "创建你的第一个动画项目",
createProject: "创建项目",
noResults: "没有找到匹配项",
noResultsDesc: "试试其他关键词或筛选条件",
s15: "15. 徽章 / 标签",
statusBadges: "状态徽章",
contentTags: "内容标签",
s16: "16. 头像",
avatarGroup: "头像组",
s17: "17. 导航路径",
projects: "项目",
pipeline: "流水线",
s18: "18. 标签页 / 分段控制",
underlineTabs: "下划线标签页",
segCtrl: "分段控制",
activeTabLabel: "当前标签页",
tabs: ["概览", "资产", "设置"],
s19: "19. 危险按钮 & 搜索框",
dangerBtns: "危险按钮",
deleteProject: "删除项目",
confirmDelete: "确认删除",
searchInput: "搜索框",
searchPlaceholder: "搜索项目、剧集...",
searchNote: "带前置搜索图标 — 左内边距 pl-10",
s20: "20. 提示气泡",
tooltipSettings: "项目设置",
tooltipRetry: "重跑失败项",
tooltipHint: "悬停图标查看提示气泡位置",
s21: "21. 文字对比度验证",
primaryContrast: "主文字 — #f1f0ff on #07070f",
secondaryContrast: "次要文字 — #8b8ea8 on #07070f",
mutedContrast: "弱化文字 — #4c4f6b on #07070f",
mutedNote: "仅用于装饰",
footer: "液态玻璃 · 影院暗色 · AI 原生 · 高端工具",
selectOptions: ["原创动画", "网文改编", "短片 / PV", "自定义"],
} : {
title: "Component Showcase",
subtitle: "Air Spark Design System v0.4 — Visual Source of Truth",
navProject: "T仔的上班日记 / EP01",
s2: "2. Colors & Glass Layers",
bgLayers: "Background Layers",
glassCards: "Glass Cards",
stdGlass: "Standard Glass",
stdGlassDesc: "bg-white/[0.06] + backdrop-blur-2xl",
activeGlass: "Active Glass",
activeGlassDesc: "+ accent glow border",
hoverGlass: "Hover Glass",
hoverGlassDesc: "hover → bg-white/[0.12]",
accentColors: "Accent & Status Colors",
s3: "3. Typography",
typoDisplay: "Air Spark",
typoH1: "Pipeline Overview",
typoH2: "Stage 6 — Video Generation",
typoH3: "Image Assets Review",
typoBody: "Directors chat with AI to generate scripts, one-click triggers a 7-stage automated pipeline, and outputs finished video. Fully concurrent, no manual intervention needed.",
typoCn: "Noto Sans SC for Chinese body text — even strokes, stable center of gravity, visually matched with DM Sans density.",
s4: "4. Buttons",
btnConfirm: "Confirm & Launch",
btnContinue: "Continue Editing",
btnWaiting: "Waiting...",
btnProcessing: "Processing...",
s5: "5. Status Tiles (Stage 6 Segment Grid)",
stageTitle: "Stage 6 — Video Generation",
completed: "completed",
remaining: "56% — est. ~8 min remaining",
done: "Done",
running: "Running",
pending: "Pending",
failed: "Failed",
seg08fail: "Segment 08 — Failed after 3 retries (timeout)",
editPrompt: "Edit Prompt",
retry: "Retry",
s6: "6. Pipeline Stepper",
steps: ["Script", "Prompt Extract", "Image Assets", "Keyshots", "Segments", "Video Gen", "Timeline"],
s7: "7. Review Cards (Image Assets Approval)",
charRef: "Character Reference Sheet",
approved: "Approved",
approveStatus: "3 / 4 characters approved. Retry failed character to proceed.",
confirmAll: "Confirm All & Continue",
cancel: "Cancel",
launchIn: "Launching in",
s8: "8. Skeleton Loading",
s9: "9. Script Confirm CTA (Stage 1)",
scriptReady: "Script Ready",
scriptInfo: "8 scenes / estimated 4m 20s",
confirmScript: "Confirm Script & Launch Pipeline",
continueEdit: "Continue editing script",
s10: "10. Collapsible Log Panel",
sysLog: "System Log",
s11: "11. Modal Dialog (Inline Demo)",
regenTitle: "Regenerate Segment?",
regenDesc: "This will re-submit segment 08 to Seedance with modified prompt. Previous result will be overwritten.",
regenBtn: "Regenerate",
s12: "12. Form Elements",
projectName: "Project Name",
projectPlaceholder: "Enter project name...",
contentType: "Content Type",
scriptPrompt: "Script Prompt",
scriptPlaceholder: "Describe your story...",
apiKey: "API Key",
apiError: "Invalid API key format",
s13: "13. Toast Notifications (Left Border)",
toastSuccess: "Save successful",
toastSuccessDesc: "Script saved as official version",
toastError: "Segment 08 failed",
toastErrorDesc: "Seedance API timeout after 3 retries",
toastWarning: "Awaiting approval",
toastWarningDesc: "Character designs need director review",
toastInfo: "Pipeline started",
toastInfoDesc: "Running Stage 2 — asset & keyshot planning",
s14: "14. Empty States",
noProjects: "No Projects Yet",
noProjectsDesc: "Create your first animation project",
createProject: "Create Project",
noResults: "No Results Found",
noResultsDesc: "Try different keywords or filters",
s15: "15. Badge / Tag",
statusBadges: "Status Badges",
contentTags: "Content Tags",
s16: "16. Avatar",
avatarGroup: "Avatar Group",
s17: "17. Breadcrumb",
projects: "Projects",
pipeline: "Pipeline",
s18: "18. Tab / Segmented Control",
underlineTabs: "Underline Tabs",
segCtrl: "Segmented Control",
activeTabLabel: "Active tab",
tabs: ["overview", "assets", "settings"],
s19: "19. Danger Button & Search Input",
dangerBtns: "Danger Buttons",
deleteProject: "Delete Project",
confirmDelete: "Confirm Delete",
searchInput: "Search Input",
searchPlaceholder: "Search projects, episodes...",
searchNote: "With leading search icon — prefix padding pl-10",
s20: "20. Tooltip",
tooltipSettings: "Project Settings",
tooltipRetry: "Retry Failed",
tooltipHint: "Hover the icons to see tooltip positions",
s21: "21. Text Contrast Verification",
primaryContrast: "Primary text — #f1f0ff on #07070f",
secondaryContrast: "Secondary text — #8b8ea8 on #07070f",
mutedContrast: "Muted text — #4c4f6b on #07070f",
mutedNote: "decorative only",
footer: "Liquid Glass · Cinematic Dark · AI Native · Premium Tool",
selectOptions: ["Original Animation", "Web Novel Adaptation", "Short Film / PV", "Custom"],
};
const handleConfirm = () => {
setConfirmCountdown(3);
const id = setInterval(() => {
setConfirmCountdown((prev) => {
if (prev === null || prev <= 1) {
clearInterval(id);
return null;
}
return prev - 1;
});
}, 1000);
};
return (
<div className="relative z-10 min-h-screen text-text-primary">
{/* ═══════════════════════════════════════
1. TOP NAV BAR (Glass, fixed)
═══════════════════════════════════════ */}
<nav className="fixed top-0 left-0 right-0 z-30 !rounded-none border-b border-white/[0.06]" style={{ background: 'rgba(7, 7, 15, 0.85)', backdropFilter: 'blur(20px) saturate(180%)', WebkitBackdropFilter: 'blur(20px) saturate(180%)' }}>
<div className="max-w-7xl mx-auto px-6 h-16 flex items-center justify-between">
<div className="flex items-center gap-3">
<div className="w-8 h-8 rounded-lg bg-accent flex items-center justify-center">
<Zap className="w-4 h-4 text-white" />
</div>
<span className="font-[family-name:var(--font-heading)] text-lg font-bold tracking-tight">
Air Spark
</span>
</div>
<div className="font-[family-name:var(--font-body)] text-sm text-text-secondary">
{t.navProject}
</div>
<div className="flex items-center gap-3">
<button
onClick={() => setLang(lang === "en" ? "zh" : "en")}
className="px-3 py-1.5 rounded-lg text-xs font-medium bg-white/[0.06] border border-white/10 hover:bg-white/[0.12] motion-safe:transition-colors cursor-pointer text-text-secondary"
>
{lang === "en" ? "中文" : "EN"}
</button>
<button
className="p-2 rounded-lg hover:bg-glass-03 motion-safe:transition-colors cursor-pointer"
aria-label="设置"
>
<Settings className="w-5 h-5 text-text-secondary" />
</button>
<button
className="p-2 rounded-lg hover:bg-glass-03 motion-safe:transition-colors cursor-pointer"
aria-label="成员"
>
<Users className="w-5 h-5 text-text-secondary" />
</button>
</div>
</div>
</nav>
{/* Spacer for fixed nav */}
<div className="h-16" />
{/* Main content */}
<div className="max-w-7xl mx-auto px-6 py-12">
{/* Page title */}
<div className="mb-16">
<h1 className="font-[family-name:var(--font-heading)] text-5xl font-bold tracking-tight leading-tight mb-3"
style={{ letterSpacing: "-0.02em" }}>
{t.title}
</h1>
<p className="text-text-secondary text-lg">
{t.subtitle}
</p>
</div>
{/* ═══════════════════════════════════════
2. COLORS & GLASS
═══════════════════════════════════════ */}
<Section title={t.s2}>
{/* Background layers */}
<div className="mb-8">
<h3 className="font-[family-name:var(--font-heading)] text-lg font-semibold mb-4">
{t.bgLayers}
</h3>
<div className="grid grid-cols-3 gap-4">
{[
{ name: "bg-base", color: "#07070f" },
{ name: "bg-surface", color: "#0d0d1a" },
{ name: "bg-elevated", color: "#12121f" },
].map((item) => (
<div
key={item.name}
className="h-24 rounded-2xl flex items-end p-4 border border-white/5"
style={{ background: item.color }}
>
<div>
<p className="text-sm font-medium">{item.name}</p>
<p className="font-[family-name:var(--font-mono)] text-xs text-text-muted">
{item.color}
</p>
</div>
</div>
))}
</div>
</div>
{/* Glass cards */}
<div className="mb-8">
<h3 className="font-[family-name:var(--font-heading)] text-lg font-semibold mb-4">
{t.glassCards}
</h3>
<div className="grid grid-cols-3 gap-6">
<div className="glass-card p-6">
<p className="text-sm font-medium mb-1">{t.stdGlass}</p>
<p className="text-xs text-text-secondary">
{t.stdGlassDesc}
</p>
</div>
<div className="glass-card glass-card-active p-6">
<p className="text-sm font-medium mb-1">{t.activeGlass}</p>
<p className="text-xs text-text-secondary">
{t.activeGlassDesc}
</p>
</div>
<div className="glass-card p-6 hover:bg-white/[0.12] motion-safe:transition-colors cursor-pointer">
<p className="text-sm font-medium mb-1">{t.hoverGlass}</p>
<p className="text-xs text-text-secondary">
{t.hoverGlassDesc}
</p>
</div>
</div>
</div>
{/* Accent colors */}
<div>
<h3 className="font-[family-name:var(--font-heading)] text-lg font-semibold mb-4">
{t.accentColors}
</h3>
<div className="flex flex-wrap gap-4">
{[
{ name: "Accent", color: "#6c63ff", tw: "bg-accent" },
{ name: "Blue", color: "#3b82f6", tw: "bg-accent-blue" },
{ name: "Completed", color: "#10b981", tw: "bg-emerald-500" },
{ name: "Failed", color: "#ef4444", tw: "bg-red-500" },
{ name: "Waiting", color: "#f59e0b", tw: "bg-amber-500" },
{ name: "Pending", color: "#4b5563", tw: "bg-gray-600" },
].map((item) => (
<div key={item.name} className="flex items-center gap-3">
<div
className="w-10 h-10 rounded-xl"
style={{ background: item.color }}
/>
<div>
<p className="text-sm font-medium">{item.name}</p>
<p className="font-[family-name:var(--font-mono)] text-xs text-text-muted">
{item.color}
</p>
</div>
</div>
))}
</div>
</div>
</Section>
{/* ═══════════════════════════════════════
3. TYPOGRAPHY
═══════════════════════════════════════ */}
<Section title={t.s3}>
<div className="glass-card p-8 space-y-6">
<div>
<p className="text-xs text-text-muted mb-1 font-[family-name:var(--font-mono)]">
Display / 48px / Space Grotesk 700
</p>
<p
className="font-[family-name:var(--font-heading)] text-5xl font-bold"
style={{ letterSpacing: "-0.02em" }}
>
{t.typoDisplay}
</p>
</div>
<div>
<p className="text-xs text-text-muted mb-1 font-[family-name:var(--font-mono)]">
H1 / 32px / Space Grotesk 700
</p>
<p
className="font-[family-name:var(--font-heading)] text-3xl font-bold"
style={{ letterSpacing: "-0.01em" }}
>
{t.typoH1}
</p>
</div>
<div>
<p className="text-xs text-text-muted mb-1 font-[family-name:var(--font-mono)]">
H2 / 24px / Space Grotesk 600
</p>
<p className="font-[family-name:var(--font-heading)] text-2xl font-semibold">
{t.typoH2}
</p>
</div>
<div>
<p className="text-xs text-text-muted mb-1 font-[family-name:var(--font-mono)]">
H3 / 18px / Space Grotesk 600
</p>
<p className="font-[family-name:var(--font-heading)] text-lg font-semibold">
{t.typoH3}
</p>
</div>
<div>
<p className="text-xs text-text-muted mb-1 font-[family-name:var(--font-mono)]">
Body / 15px / DM Sans 400
</p>
<p className="text-[15px] leading-relaxed text-text-primary max-w-xl">
{t.typoBody}
</p>
</div>
<div>
<p className="text-xs text-text-muted mb-1 font-[family-name:var(--font-mono)]">
CN Body / 15px / Noto Sans SC 400
</p>
<p className="font-[family-name:var(--font-cn)] text-[15px] leading-relaxed text-text-primary max-w-xl">
{t.typoCn}
</p>
</div>
<div>
<p className="text-xs text-text-muted mb-1 font-[family-name:var(--font-mono)]">
Small / 13px / DM Sans 400
</p>
<p className="text-[13px] text-text-secondary">
seg_001 · 00:00.000 00:08.500 · scene_01
</p>
</div>
<div>
<p className="text-xs text-text-muted mb-1 font-[family-name:var(--font-mono)]">
Mono / 13px / JetBrains Mono 400
</p>
<p className="font-[family-name:var(--font-mono)] text-[13px] text-text-secondary">
cell_num = floor((seg_start - scene_start) / cell_duration) + 1
</p>
</div>
</div>
</Section>
{/* ═══════════════════════════════════════
4. BUTTONS
═══════════════════════════════════════ */}
<Section title={t.s4}>
<div className="flex flex-wrap items-center gap-6">
{/* Primary CTA */}
<button className="bg-gradient-to-br from-violet-500 to-violet-700 shadow-[0_0_20px_rgba(139,92,246,0.4),0_4px_12px_rgba(0,0,0,0.3)] border border-white/15 rounded-xl px-8 py-3.5 text-white font-semibold text-[15px] hover:shadow-[0_0_30px_rgba(139,92,246,0.6)] motion-safe:transition-all motion-safe:duration-200 active:scale-[0.98] cursor-pointer focus-visible:ring-2 focus-visible:ring-violet-400 focus-visible:ring-offset-2 focus-visible:ring-offset-bg-base">
{t.btnConfirm}
</button>
{/* Secondary */}
<button className="glass-card !rounded-xl px-6 py-3 text-[15px] font-medium text-text-primary hover:bg-white/[0.12] motion-safe:transition-colors cursor-pointer focus-visible:ring-2 focus-visible:ring-violet-400 focus-visible:ring-offset-2 focus-visible:ring-offset-bg-base">
{t.btnContinue}
</button>
{/* Disabled */}
<button
disabled
className="bg-white/[0.04] border border-white/5 rounded-xl px-6 py-3 text-[15px] font-medium text-text-muted cursor-not-allowed"
>
{t.btnWaiting}
</button>
{/* Loading */}
<button
disabled
className="bg-gradient-to-br from-violet-500/50 to-violet-700/50 border border-white/10 rounded-xl px-6 py-3 text-[15px] font-medium text-white/70 cursor-not-allowed flex items-center gap-2"
>
<Loader2 className="w-4 h-4 motion-safe:animate-spin" />
{t.btnProcessing}
</button>
{/* Icon button */}
<button
className="p-2.5 rounded-xl glass-card hover:bg-white/[0.12] motion-safe:transition-colors cursor-pointer focus-visible:ring-2 focus-visible:ring-violet-400 focus-visible:ring-offset-2 focus-visible:ring-offset-bg-base"
aria-label="Retry"
>
<RefreshCw className="w-5 h-5 text-text-secondary" />
</button>
</div>
</Section>
{/* ═══════════════════════════════════════
5. STATUS TILES (Stage 6 Grid)
═══════════════════════════════════════ */}
<Section title={t.s5}>
<div className="glass-card p-6">
{/* Progress bar */}
<div className="mb-6">
<div className="flex items-center justify-between mb-2">
<span className="font-[family-name:var(--font-heading)] text-lg font-semibold">
{t.stageTitle}
</span>
<span className="text-sm text-text-secondary">
18 / 32 {t.completed}
</span>
</div>
<div className="h-2 bg-white/[0.06] rounded-full overflow-hidden">
<div
className="h-full bg-gradient-to-r from-accent to-accent-blue rounded-full motion-safe:transition-all motion-safe:duration-500"
style={{ width: "56%" }}
/>
</div>
<p className="text-xs text-text-muted mt-2">
{t.remaining}
</p>
</div>
{/* Segment grid */}
<div className="grid grid-cols-8 gap-2.5 mb-6">
{Array.from({ length: 32 }, (_, i) => {
const num = i + 1;
let status: "done" | "running" | "pending" | "failed";
if (num === 8) status = "failed";
else if (num <= 12) status = "done";
else if (num <= 18) status = "running";
else status = "pending";
const styles = {
done: "bg-emerald-500/15 border-emerald-400/30 text-emerald-400",
running:
"bg-blue-500/20 border-blue-400/40 text-blue-400 motion-safe:animate-pulse",
pending: "bg-white/[0.03] border-white/[0.06] text-text-muted",
failed: "bg-red-500/15 border-red-400/30 text-red-400",
};
const icons = {
done: <Check className="w-3.5 h-3.5" />,
running: (
<div className="w-2 h-2 rounded-full bg-blue-400" />
),
pending: (
<div className="w-2 h-2 rounded-full bg-white/20" />
),
failed: <X className="w-3.5 h-3.5" />,
};
return (
<div
key={num}
className={`border rounded-lg p-2.5 flex flex-col items-center gap-1.5 cursor-pointer hover:bg-white/[0.08] motion-safe:transition-colors ${styles[status]}`}
>
<span className="font-[family-name:var(--font-mono)] text-xs">
{String(num).padStart(2, "0")}
</span>
{icons[status]}
</div>
);
})}
</div>
{/* Legend */}
<div className="flex items-center gap-6 text-xs text-text-secondary">
<span className="flex items-center gap-1.5">
<Check className="w-3 h-3 text-emerald-400" /> {t.done}
</span>
<span className="flex items-center gap-1.5">
<div className="w-2 h-2 rounded-full bg-blue-400" /> {t.running}
</span>
<span className="flex items-center gap-1.5">
<div className="w-2 h-2 rounded-full bg-white/20" /> {t.pending}
</span>
<span className="flex items-center gap-1.5">
<X className="w-3 h-3 text-red-400" /> {t.failed}
</span>
</div>
{/* Failed segment action */}
<div className="mt-4 p-4 rounded-xl bg-red-500/[0.06] border border-red-400/20 flex items-center justify-between">
<div className="flex items-center gap-3">
<AlertTriangle className="w-5 h-5 text-red-400" />
<span className="text-sm">
{t.seg08fail}
</span>
</div>
<div className="flex items-center gap-2">
<button className="px-3 py-1.5 rounded-lg text-xs font-medium glass-card hover:bg-white/[0.12] motion-safe:transition-colors cursor-pointer flex items-center gap-1.5">
<Pencil className="w-3 h-3" /> {t.editPrompt}
</button>
<button className="px-3 py-1.5 rounded-lg text-xs font-medium bg-red-500/20 border border-red-400/30 hover:bg-red-500/30 motion-safe:transition-colors cursor-pointer flex items-center gap-1.5">
<RefreshCw className="w-3 h-3" /> {t.retry}
</button>
</div>
</div>
</div>
</Section>
{/* ═══════════════════════════════════════
6. PIPELINE STEPPER
═══════════════════════════════════════ */}
<Section title={t.s6}>
<div className="glass-card p-6">
<div className="flex items-center justify-between">
{[
{ num: 1, name: t.steps[0], status: "done" as const },
{ num: 2, name: t.steps[1], status: "done" as const },
{ num: 3, name: t.steps[2], status: "waiting" as const },
{ num: 4, name: t.steps[3], status: "idle" as const },
{ num: 5, name: t.steps[4], status: "idle" as const },
{ num: 6, name: t.steps[5], status: "idle" as const },
{ num: 7, name: t.steps[6], status: "idle" as const },
].map((step, idx) => (
<div key={step.num} className="flex items-center">
<div className="flex flex-col items-center gap-2">
<div
className={`w-10 h-10 rounded-full flex items-center justify-center text-sm font-semibold border motion-safe:transition-colors ${
step.status === "done"
? "bg-emerald-500/20 border-emerald-400/40 text-emerald-400"
: step.status === "waiting"
? "bg-amber-500/20 border-amber-400/40 text-amber-400 motion-safe:animate-pulse"
: "bg-white/[0.04] border-white/[0.08] text-text-muted"
}`}
>
{step.status === "done" ? (
<Check className="w-4 h-4" />
) : (
step.num
)}
</div>
<span
className={`text-xs font-medium ${
step.status === "done"
? "text-emerald-400"
: step.status === "waiting"
? "text-amber-400"
: "text-text-muted"
}`}
>
{step.name}
</span>
</div>
{idx < 6 && (
<ChevronRight className="w-4 h-4 text-text-muted mx-3 mt-[-20px]" />
)}
</div>
))}
</div>
</div>
</Section>
{/* ═══════════════════════════════════════
7. REVIEW CARDS (Stage 3 Character Approval)
═══════════════════════════════════════ */}
<Section title={t.s7}>
<div className="grid grid-cols-4 gap-4 mb-6">
{[
{ name: "T仔", status: "approved" as const },
{ name: "特特", status: "approved" as const },
{ name: "班长", status: "approved" as const },
{ name: "路人甲", status: "failed" as const },
].map((char) => (
<div key={char.name} className="glass-card overflow-hidden">
{/* Image placeholder */}
<div
className={`h-40 flex items-center justify-center ${
char.status === "failed"
? "bg-red-500/[0.06]"
: "bg-white/[0.03]"
}`}
>
<span className="text-4xl text-text-muted/30 font-[family-name:var(--font-heading)] font-bold">
{char.name[0]}
</span>
</div>
<div className="p-4">
<p className="font-[family-name:var(--font-heading)] font-semibold mb-1">
{char.name}
</p>
<p className="text-xs text-text-secondary mb-3">
{t.charRef}
</p>
{char.status === "approved" ? (
<button className="w-full py-2 rounded-lg text-xs font-medium bg-emerald-500/15 border border-emerald-400/30 text-emerald-400 flex items-center justify-center gap-1.5 cursor-pointer">
<Check className="w-3 h-3" /> {t.approved}
</button>
) : (
<button className="w-full py-2 rounded-lg text-xs font-medium bg-red-500/15 border border-red-400/30 text-red-400 flex items-center justify-center gap-1.5 cursor-pointer hover:bg-red-500/25 motion-safe:transition-colors">
<RefreshCw className="w-3 h-3" /> {t.retry}
</button>
)}
</div>
</div>
))}
</div>
{/* Confirm CTA with countdown */}
<div className="glass-card p-6 text-center">
{confirmCountdown !== null ? (
<div>
<p className="text-sm text-text-secondary mb-3">
{t.launchIn} {confirmCountdown}s...
</p>
<button
onClick={() => setConfirmCountdown(null)}
className="px-6 py-2 rounded-lg text-sm font-medium glass-card hover:bg-white/[0.12] motion-safe:transition-colors cursor-pointer"
>
{t.cancel}
</button>
</div>
) : (
<div>
<p className="text-sm text-text-secondary mb-4">
{t.approveStatus}
</p>
<button
onClick={handleConfirm}
disabled
className="bg-white/[0.04] border border-white/5 rounded-xl px-10 py-3.5 text-[15px] font-semibold text-text-muted cursor-not-allowed"
>
{t.confirmAll}
</button>
</div>
)}
</div>
</Section>
{/* ═══════════════════════════════════════
8. SKELETON LOADING
═══════════════════════════════════════ */}
<Section title={t.s8}>
<div className="grid grid-cols-3 gap-4">
{[1, 2, 3].map((i) => (
<div
key={i}
className="glass-card overflow-hidden motion-safe:animate-pulse"
>
<div className="h-40 bg-white/[0.06]" />
<div className="p-4 space-y-3">
<div className="h-4 bg-white/[0.08] rounded w-3/4" />
<div className="h-3 bg-white/[0.06] rounded w-1/2" />
<div className="h-8 bg-white/[0.04] rounded-lg mt-4" />
</div>
</div>
))}
</div>
</Section>
{/* ═══════════════════════════════════════
9. CONFIRM CTA (Stage 1 Script)
═══════════════════════════════════════ */}
<Section title={t.s9}>
<div className="glass-card p-8 text-center max-w-lg mx-auto">
<div className="flex items-center justify-center gap-2 mb-2">
<Check className="w-5 h-5 text-emerald-400" />
<span className="font-[family-name:var(--font-heading)] font-semibold">
{t.scriptReady}
</span>
</div>
<p className="text-sm text-text-secondary mb-6">
{t.scriptInfo}
</p>
<button
onClick={handleConfirm}
className="w-full bg-gradient-to-br from-violet-500 to-violet-700 shadow-[0_0_20px_rgba(139,92,246,0.4),0_4px_12px_rgba(0,0,0,0.3)] border border-white/15 rounded-xl px-8 py-4 text-white font-semibold text-base hover:shadow-[0_0_30px_rgba(139,92,246,0.6)] motion-safe:transition-all motion-safe:duration-200 active:scale-[0.98] cursor-pointer flex items-center justify-center gap-2"
>
{t.confirmScript}
<Play className="w-4 h-4" />
</button>
<button className="mt-3 text-sm text-text-secondary hover:text-text-primary motion-safe:transition-colors cursor-pointer">
{t.continueEdit}
</button>
</div>
</Section>
{/* ═══════════════════════════════════════
10. COLLAPSIBLE LOG PANEL
═══════════════════════════════════════ */}
<Section title={t.s10}>
<div className="glass-card overflow-hidden">
<button
onClick={() => setLogOpen(!logOpen)}
className="w-full px-6 py-4 flex items-center justify-between hover:bg-white/[0.04] motion-safe:transition-colors cursor-pointer"
>
<span className="text-sm font-medium text-text-secondary">
{t.sysLog}
</span>
{logOpen ? (
<ChevronDown className="w-4 h-4 text-text-muted" />
) : (
<ChevronRight className="w-4 h-4 text-text-muted" />
)}
</button>
{logOpen && (
<div className="px-6 pb-4 border-t border-white/5 pt-4">
<div className="space-y-2 font-[family-name:var(--font-mono)] text-xs text-text-secondary">
<p>
<span className="text-text-muted">[14:32:01]</span>{" "}
<span className="text-emerald-400">OK</span> Stage 2
completed 3 characters, 5 scenes
</p>
<p>
<span className="text-text-muted">[14:32:03]</span>{" "}
<span className="text-accent-blue">INFO</span> Starting
Stage 3 Banana Pro batch (8 images)
</p>
<p>
<span className="text-text-muted">[14:32:15]</span>{" "}
<span className="text-emerald-400">OK</span>{" "}
character_001_front.jpg generated
</p>
<p>
<span className="text-text-muted">[14:32:28]</span>{" "}
<span className="text-red-400">ERR</span>{" "}
character_004 Banana Pro 429, retrying in 4s
</p>
<p>
<span className="text-text-muted">[14:32:32]</span>{" "}
<span className="text-amber-400">WARN</span>{" "}
character_004 retry 2/3
</p>
</div>
</div>
)}
</div>
</Section>
{/* ═══════════════════════════════════════
11. MODAL (Confirm Dialog)
═══════════════════════════════════════ */}
<Section title={t.s11}>
<div className="relative rounded-2xl bg-bg-surface p-8 overflow-hidden">
{/* Overlay simulation */}
<div className="absolute inset-0 bg-black/60 backdrop-blur-sm rounded-2xl" />
<div className="relative z-10 max-w-md mx-auto p-8 text-center rounded-2xl border border-white/[0.08]" style={{ background: 'rgba(13, 13, 26, 0.92)', backdropFilter: 'blur(24px) saturate(180%)', WebkitBackdropFilter: 'blur(24px) saturate(180%)' }}>
<div className="w-12 h-12 rounded-full bg-amber-500/20 flex items-center justify-center mx-auto mb-4">
<AlertTriangle className="w-6 h-6 text-amber-400" />
</div>
<h3 className="font-[family-name:var(--font-heading)] text-lg font-semibold mb-2">
{t.regenTitle}
</h3>
<p className="text-sm text-text-secondary mb-6">
{t.regenDesc}
</p>
<div className="flex gap-3">
<button className="flex-1 py-3 rounded-xl glass-card text-sm font-medium hover:bg-white/[0.12] motion-safe:transition-colors cursor-pointer">
{t.cancel}
</button>
<button className="flex-1 py-3 rounded-xl bg-gradient-to-br from-violet-500 to-violet-700 border border-white/15 text-sm font-medium text-white hover:shadow-[0_0_20px_rgba(139,92,246,0.4)] motion-safe:transition-all cursor-pointer">
{t.regenBtn}
</button>
</div>
</div>
</div>
</Section>
{/* ═══════════════════════════════════════
12. FORM ELEMENTS
═══════════════════════════════════════ */}
<Section title={t.s12}>
<div className="glass-card p-8 max-w-lg">
<div className="space-y-4">
{/* Label + Input */}
<div>
<label className="block text-sm font-medium text-text-secondary mb-2">
{t.projectName}
</label>
<input
type="text"
placeholder={t.projectPlaceholder}
className="w-full bg-white/[0.04] border border-white/10 rounded-lg px-4 py-3 text-[15px] text-text-primary placeholder:text-text-muted focus:outline-none focus:border-accent/50 focus:ring-3 focus:ring-accent/15 transition-all duration-200"
/>
</div>
{/* Custom Select */}
<div>
<label className="block text-sm font-medium text-text-secondary mb-2">
{t.contentType}
</label>
<div className="relative">
<button
type="button"
onClick={() => setSelectOpen(!selectOpen)}
className={`w-full bg-white/[0.04] border rounded-lg px-4 py-3 text-[15px] text-text-primary text-left flex items-center justify-between transition-all duration-200 cursor-pointer ${
selectOpen ? "border-accent/50 ring-3 ring-accent/15" : "border-white/10"
}`}
>
{selectValue}
<ChevronsUpDown className="w-4 h-4 text-text-muted shrink-0" />
</button>
{selectOpen && (
<div className="absolute z-20 mt-1 w-full rounded-lg border border-white/10 overflow-hidden" style={{ background: 'rgba(13, 13, 26, 0.95)', backdropFilter: 'blur(20px)', WebkitBackdropFilter: 'blur(20px)' }}>
{t.selectOptions.map((opt) => (
<button
key={opt}
type="button"
onClick={() => { setSelectValue(opt); setSelectOpen(false); }}
className={`w-full px-4 py-2.5 text-[15px] text-left transition-colors cursor-pointer ${
selectValue === opt
? "text-accent bg-accent/10"
: "text-text-primary hover:bg-white/[0.06]"
}`}
>
{opt}
</button>
))}
</div>
)}
</div>
</div>
{/* Textarea */}
<div>
<label className="block text-sm font-medium text-text-secondary mb-2">
{t.scriptPrompt} <span className="text-red-400">*</span>
</label>
<textarea
placeholder={t.scriptPlaceholder}
className="w-full bg-white/[0.04] border border-white/10 rounded-lg px-4 py-3 text-[15px] text-text-primary placeholder:text-text-muted focus:outline-none focus:border-accent/50 focus:ring-3 focus:ring-accent/15 transition-all duration-200 resize-none min-h-[120px]"
/>
</div>
{/* Error state */}
<div>
<label className="block text-sm font-medium text-text-secondary mb-2">
{t.apiKey}
</label>
<input
type="text"
defaultValue="sk-invalid-key"
className="w-full bg-white/[0.04] border border-red-500/50 rounded-lg px-4 py-3 text-[15px] text-text-primary focus:outline-none focus:ring-3 focus:ring-red-500/12 transition-all duration-200"
/>
<p className="text-sm text-red-400 mt-1">{t.apiError}</p>
</div>
</div>
</div>
</Section>
{/* ═══════════════════════════════════════
13. TOAST WITH BORDER
═══════════════════════════════════════ */}
<Section title={t.s13}>
<div className="space-y-3 max-w-md">
<div className="glass-card !rounded-xl px-4 py-3 flex items-start gap-3 border-l-[3px] border-l-emerald-500">
<Check className="w-5 h-5 text-emerald-400 mt-0.5 shrink-0" />
<div className="flex-1">
<p className="text-sm font-medium">{t.toastSuccess}</p>
<p className="text-xs text-text-secondary mt-0.5">{t.toastSuccessDesc}</p>
</div>
<button className="text-text-muted hover:text-text-secondary cursor-pointer" aria-label="Close">
<X className="w-4 h-4" />
</button>
</div>
<div className="glass-card !rounded-xl px-4 py-3 flex items-start gap-3 border-l-[3px] border-l-red-500">
<X className="w-5 h-5 text-red-400 mt-0.5 shrink-0" />
<div className="flex-1">
<p className="text-sm font-medium">{t.toastError}</p>
<p className="text-xs text-text-secondary mt-0.5">{t.toastErrorDesc}</p>
</div>
<button className="text-text-muted hover:text-text-secondary cursor-pointer" aria-label="Close">
<X className="w-4 h-4" />
</button>
</div>
<div className="glass-card !rounded-xl px-4 py-3 flex items-start gap-3 border-l-[3px] border-l-amber-500">
<AlertTriangle className="w-5 h-5 text-amber-400 mt-0.5 shrink-0" />
<div className="flex-1">
<p className="text-sm font-medium">{t.toastWarning}</p>
<p className="text-xs text-text-secondary mt-0.5">{t.toastWarningDesc}</p>
</div>
<button className="text-text-muted hover:text-text-secondary cursor-pointer" aria-label="Close">
<X className="w-4 h-4" />
</button>
</div>
<div className="glass-card !rounded-xl px-4 py-3 flex items-start gap-3 border-l-[3px] border-l-blue-500">
<Info className="w-5 h-5 text-blue-400 mt-0.5 shrink-0" />
<div className="flex-1">
<p className="text-sm font-medium">{t.toastInfo}</p>
<p className="text-xs text-text-secondary mt-0.5">{t.toastInfoDesc}</p>
</div>
<button className="text-text-muted hover:text-text-secondary cursor-pointer" aria-label="Close">
<X className="w-4 h-4" />
</button>
</div>
</div>
</Section>
{/* ═══════════════════════════════════════
14. EMPTY STATES
═══════════════════════════════════════ */}
<Section title={t.s14}>
<div className="grid grid-cols-2 gap-6">
{/* No projects */}
<div className="glass-card flex flex-col items-center justify-center py-16 text-center">
<div className="w-12 h-12 rounded-2xl bg-white/[0.04] flex items-center justify-center mb-4">
<FolderPlus className="w-6 h-6 text-text-muted" />
</div>
<h3 className="font-[family-name:var(--font-heading)] text-lg font-semibold text-text-primary mb-1">
{t.noProjects}
</h3>
<p className="text-sm text-text-secondary mb-6">
{t.noProjectsDesc}
</p>
<button className="bg-gradient-to-br from-violet-500 to-violet-700 border border-white/15 rounded-xl px-6 py-2.5 text-sm text-white font-medium hover:shadow-[0_0_20px_rgba(139,92,246,0.4)] transition-all duration-200 cursor-pointer">
{t.createProject}
</button>
</div>
{/* No search results */}
<div className="glass-card flex flex-col items-center justify-center py-16 text-center">
<div className="w-12 h-12 rounded-2xl bg-white/[0.04] flex items-center justify-center mb-4">
<Search className="w-6 h-6 text-text-muted" />
</div>
<h3 className="font-[family-name:var(--font-heading)] text-lg font-semibold text-text-primary mb-1">
{t.noResults}
</h3>
<p className="text-sm text-text-secondary">
{t.noResultsDesc}
</p>
</div>
</div>
</Section>
{/* ═══════════════════════════════════════
15. BADGE / TAG
═══════════════════════════════════════ */}
<Section title={t.s15}>
<div className="glass-card p-8">
<h3 className="font-[family-name:var(--font-heading)] text-lg font-semibold mb-4">
{t.statusBadges}
</h3>
<div className="flex flex-wrap items-center gap-3 mb-8">
<span className="inline-flex items-center gap-1.5 px-2.5 py-1 rounded-md text-xs font-medium bg-emerald-500/15 text-emerald-400 border border-emerald-400/20">
<Check className="w-3 h-3" /> Completed
</span>
<span className="inline-flex items-center gap-1.5 px-2.5 py-1 rounded-md text-xs font-medium bg-blue-500/15 text-blue-400 border border-blue-400/20">
<Loader2 className="w-3 h-3 motion-safe:animate-spin" /> Running
</span>
<span className="inline-flex items-center gap-1.5 px-2.5 py-1 rounded-md text-xs font-medium bg-amber-500/15 text-amber-400 border border-amber-400/20">
<AlertTriangle className="w-3 h-3" /> Waiting
</span>
<span className="inline-flex items-center gap-1.5 px-2.5 py-1 rounded-md text-xs font-medium bg-red-500/15 text-red-400 border border-red-400/20">
<X className="w-3 h-3" /> Failed
</span>
<span className="inline-flex items-center gap-1.5 px-2.5 py-1 rounded-md text-xs font-medium bg-white/[0.06] text-text-secondary border border-white/10">
Pending
</span>
</div>
<h3 className="font-[family-name:var(--font-heading)] text-lg font-semibold mb-4">
{t.contentTags}
</h3>
<div className="flex flex-wrap items-center gap-2">
<span className="inline-flex items-center gap-1.5 px-3 py-1 rounded-full text-xs font-medium bg-accent/15 text-accent border border-accent/20">
<Hash className="w-3 h-3" /> screenplay-skill
</span>
<span className="inline-flex items-center gap-1.5 px-3 py-1 rounded-full text-xs font-medium bg-accent/15 text-accent border border-accent/20">
<Hash className="w-3 h-3" /> storyboard-skill
</span>
<span className="inline-flex items-center gap-1.5 px-3 py-1 rounded-full text-xs font-medium bg-accent-blue/15 text-accent-blue border border-accent-blue/20">
EP01
</span>
<span className="inline-flex items-center gap-1.5 px-3 py-1 rounded-full text-xs font-medium bg-accent-blue/15 text-accent-blue border border-accent-blue/20">
EP02
</span>
</div>
</div>
</Section>
{/* ═══════════════════════════════════════
16. AVATAR
═══════════════════════════════════════ */}
<Section title={t.s16}>
<div className="glass-card p-8">
<div className="flex items-end gap-6 mb-8">
{/* Sizes */}
{[
{ size: "w-8 h-8", text: "text-xs", label: "SM" },
{ size: "w-10 h-10", text: "text-sm", label: "MD" },
{ size: "w-12 h-12", text: "text-base", label: "LG" },
{ size: "w-16 h-16", text: "text-lg", label: "XL" },
].map((a) => (
<div key={a.label} className="flex flex-col items-center gap-2">
<div className={`${a.size} rounded-full bg-gradient-to-br from-violet-500 to-accent-blue flex items-center justify-center ${a.text} font-semibold text-white`}>
T
</div>
<span className="text-xs text-text-muted">{a.label}</span>
</div>
))}
{/* With icon fallback */}
<div className="flex flex-col items-center gap-2">
<div className="w-10 h-10 rounded-full bg-white/[0.06] border border-white/10 flex items-center justify-center">
<User className="w-5 h-5 text-text-muted" />
</div>
<span className="text-xs text-text-muted">Fallback</span>
</div>
{/* With status dot */}
<div className="flex flex-col items-center gap-2">
<div className="relative">
<div className="w-10 h-10 rounded-full bg-gradient-to-br from-violet-500 to-accent-blue flex items-center justify-center text-sm font-semibold text-white">
D
</div>
<div className="absolute -bottom-0.5 -right-0.5 w-3.5 h-3.5 rounded-full bg-emerald-500 border-2 border-bg-base" />
</div>
<span className="text-xs text-text-muted">Online</span>
</div>
</div>
{/* Avatar group */}
<h3 className="font-[family-name:var(--font-heading)] text-lg font-semibold mb-4">
{t.avatarGroup}
</h3>
<div className="flex -space-x-2">
{["T", "D", "B", "L"].map((initial, i) => (
<div key={i} className="w-10 h-10 rounded-full bg-gradient-to-br from-violet-500 to-accent-blue flex items-center justify-center text-sm font-semibold text-white border-2 border-bg-base" style={{ opacity: 1 - i * 0.15 }}>
{initial}
</div>
))}
<div className="w-10 h-10 rounded-full bg-white/[0.08] border-2 border-bg-base flex items-center justify-center text-xs font-medium text-text-secondary">
+3
</div>
</div>
</div>
</Section>
{/* ═══════════════════════════════════════
17. BREADCRUMB
═══════════════════════════════════════ */}
<Section title={t.s17}>
<div className="glass-card p-6">
<nav aria-label="Breadcrumb" className="flex items-center gap-2 text-sm">
<a href="#" className="text-text-secondary hover:text-text-primary motion-safe:transition-colors cursor-pointer">
{t.projects}
</a>
<ChevronRight className="w-3.5 h-3.5 text-text-muted" />
<a href="#" className="text-text-secondary hover:text-text-primary motion-safe:transition-colors cursor-pointer">
T仔的上班日记
</a>
<ChevronRight className="w-3.5 h-3.5 text-text-muted" />
<a href="#" className="text-text-secondary hover:text-text-primary motion-safe:transition-colors cursor-pointer">
EP01
</a>
<ChevronRight className="w-3.5 h-3.5 text-text-muted" />
<span className="text-text-primary font-medium">{t.pipeline}</span>
</nav>
</div>
</Section>
{/* ═══════════════════════════════════════
18. TAB / SEGMENTED CONTROL
═══════════════════════════════════════ */}
<Section title={t.s18}>
<div className="glass-card p-8">
{/* Underline tabs */}
<h3 className="font-[family-name:var(--font-heading)] text-lg font-semibold mb-4">
{t.underlineTabs}
</h3>
<div className="flex border-b border-white/10 mb-6">
{t.tabs.map((tab) => (
<button
key={tab}
onClick={() => setActiveTab(tab)}
className={`px-4 py-3 text-sm font-medium capitalize motion-safe:transition-colors cursor-pointer relative ${
activeTab === tab
? "text-accent"
: "text-text-secondary hover:text-text-primary"
}`}
>
{tab}
{activeTab === tab && (
<div className="absolute bottom-0 left-0 right-0 h-0.5 bg-accent rounded-full" />
)}
</button>
))}
</div>
<div className="text-sm text-text-secondary p-4 rounded-lg bg-white/[0.02]">
{t.activeTabLabel}: <span className="text-text-primary font-medium capitalize">{activeTab}</span>
</div>
{/* Segmented control (pill style) */}
<h3 className="font-[family-name:var(--font-heading)] text-lg font-semibold mb-4 mt-8">
{t.segCtrl}
</h3>
<div className="inline-flex bg-white/[0.04] rounded-lg p-1 border border-white/[0.06]">
{["Grid", "List", "Timeline"].map((seg) => (
<button
key={seg}
onClick={() => setActiveSeg(seg)}
className={`px-4 py-2 rounded-md text-sm font-medium motion-safe:transition-all cursor-pointer ${
activeSeg === seg
? "bg-accent/20 text-accent shadow-sm"
: "text-text-secondary hover:text-text-primary"
}`}
>
{seg}
</button>
))}
</div>
</div>
</Section>
{/* ═══════════════════════════════════════
19. DANGER BUTTON + SEARCH INPUT
═══════════════════════════════════════ */}
<Section title={t.s19}>
<div className="grid grid-cols-2 gap-6">
{/* Danger buttons */}
<div className="glass-card p-8">
<h3 className="font-[family-name:var(--font-heading)] text-lg font-semibold mb-4">
{t.dangerBtns}
</h3>
<div className="flex flex-wrap items-center gap-4">
<button className="bg-red-500/15 border border-red-400/30 rounded-xl px-6 py-3 text-[15px] font-medium text-red-400 hover:bg-red-500/25 motion-safe:transition-colors cursor-pointer flex items-center gap-2 focus-visible:ring-2 focus-visible:ring-red-400 focus-visible:ring-offset-2 focus-visible:ring-offset-bg-base">
<Trash2 className="w-4 h-4" />
{t.deleteProject}
</button>
<button className="bg-red-600 border border-red-500 rounded-xl px-6 py-3 text-[15px] font-medium text-white hover:bg-red-700 motion-safe:transition-colors cursor-pointer focus-visible:ring-2 focus-visible:ring-red-400 focus-visible:ring-offset-2 focus-visible:ring-offset-bg-base">
{t.confirmDelete}
</button>
</div>
</div>
{/* Search input */}
<div className="glass-card p-8">
<h3 className="font-[family-name:var(--font-heading)] text-lg font-semibold mb-4">
{t.searchInput}
</h3>
<div className="relative">
<Search className="absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 text-text-muted" />
<input
type="text"
placeholder={t.searchPlaceholder}
className="w-full bg-white/[0.04] border border-white/10 rounded-lg pl-10 pr-4 py-3 text-[15px] text-text-primary placeholder:text-text-muted focus:outline-none focus:border-accent/50 focus:ring-3 focus:ring-accent/15 transition-all duration-200"
/>
</div>
<p className="text-xs text-text-muted mt-2">
{t.searchNote}
</p>
</div>
</div>
</Section>
{/* ═══════════════════════════════════════
20. TOOLTIP (Hover Demo)
═══════════════════════════════════════ */}
<Section title={t.s20}>
<div className="glass-card p-8">
<div className="flex items-center gap-8">
{/* Top tooltip */}
<div className="group relative">
<button className="p-2.5 rounded-xl glass-card hover:bg-white/[0.12] motion-safe:transition-colors cursor-pointer" aria-label="Settings">
<Settings className="w-5 h-5 text-text-secondary" />
</button>
<div className="absolute bottom-full left-1/2 -translate-x-1/2 mb-2 px-3 py-1.5 rounded-lg text-xs font-medium text-text-primary whitespace-nowrap opacity-0 group-hover:opacity-100 motion-safe:transition-opacity pointer-events-none border border-white/10" style={{ background: 'rgba(13, 13, 26, 0.95)', backdropFilter: 'blur(12px)' }}>
{t.tooltipSettings}
<div className="absolute top-full left-1/2 -translate-x-1/2 w-2 h-2 rotate-45 border-b border-r border-white/10" style={{ background: 'rgba(13, 13, 26, 0.95)' }} />
</div>
</div>
{/* Right tooltip */}
<div className="group relative">
<button className="p-2.5 rounded-xl glass-card hover:bg-white/[0.12] motion-safe:transition-colors cursor-pointer" aria-label="Retry">
<RefreshCw className="w-5 h-5 text-text-secondary" />
</button>
<div className="absolute left-full top-1/2 -translate-y-1/2 ml-2 px-3 py-1.5 rounded-lg text-xs font-medium text-text-primary whitespace-nowrap opacity-0 group-hover:opacity-100 motion-safe:transition-opacity pointer-events-none border border-white/10" style={{ background: 'rgba(13, 13, 26, 0.95)', backdropFilter: 'blur(12px)' }}>
{t.tooltipRetry}
<div className="absolute right-full top-1/2 -translate-y-1/2 w-2 h-2 rotate-45 border-l border-b border-white/10" style={{ background: 'rgba(13, 13, 26, 0.95)' }} />
</div>
</div>
<p className="text-sm text-text-secondary ml-4">
{t.tooltipHint}
</p>
</div>
</div>
</Section>
{/* ═══════════════════════════════════════
21. TEXT CONTRAST CHECK
═══════════════════════════════════════ */}
<Section title={t.s21}>
<div className="glass-card p-6 space-y-4">
<div className="flex items-center justify-between">
<span className="text-text-primary">
{t.primaryContrast}
</span>
<span className="font-[family-name:var(--font-mono)] text-xs text-emerald-400">
18.5:1 AAA
</span>
</div>
<div className="flex items-center justify-between">
<span className="text-text-secondary">
{t.secondaryContrast}
</span>
<span className="font-[family-name:var(--font-mono)] text-xs text-emerald-400">
6.2:1 AA
</span>
</div>
<div className="flex items-center justify-between">
<span className="text-text-muted">
{t.mutedContrast}
</span>
<span className="font-[family-name:var(--font-mono)] text-xs text-amber-400">
3.1:1 {t.mutedNote}
</span>
</div>
</div>
</Section>
{/* Footer */}
<div className="text-center py-12 border-t border-white/5">
<p className="text-sm text-text-muted">
Air Spark Design System v0.4
</p>
<p className="text-xs text-text-muted mt-1">
{t.footer}
</p>
</div>
</div>
</div>
);
}