const { useEffect, useMemo, useRef, useState } = React;

const PREVIEW_ITEMS = [
  { id: 'img-01', type: 'image', title: 'Chromatic Editorial', model: 'Seedream 5.0', ratio: '4:5', prompt: 'Editorial portrait of a creative director in a translucent cobalt raincoat, soft apricot light, reflective floor, restrained luxury campaign, crisp skin texture, 85mm lens.', src: '/showcase/assets/gpt2-typography.jpg', favorite: true, span: 'tall', age: '12 min ago' },
  { id: 'vid-01', type: 'video', title: 'Coastal Hypercar', model: 'Seedance 2.0', ratio: '16:9', prompt: 'A graphite hypercar launches along a coastal highway at blue hour, low rear-wheel tracking shot, clean city light trails, wet asphalt reflections, premium automotive film.', src: '/showcase/assets/posters/seedance20-tiger-demo.jpg', video: '/showcase/assets/seedance20-tiger-demo.mp4', favorite: false, span: 'wide', age: '28 min ago' },
  { id: 'img-02', type: 'image', title: 'Glass Garden', model: 'Nano Banana 2', ratio: '1:1', prompt: 'A botanical perfume bottle in a miniature glass garden, dewdrops, pale jade and cream palette, morning side light, high-end product macro photography.', src: '/showcase/assets/nb2-t2i.png', favorite: false, span: 'square', age: '46 min ago' },
  { id: 'aud-01', type: 'audio', title: 'Midnight Dispatch', model: 'Seedance Audio', ratio: '01:24', prompt: 'Warm, confident documentary narration with restrained pacing, subtle room tone and a clear Singaporean English cadence.', src: '/showcase/assets/posters/seedance20-dialogue-official.jpg', audio: '/showcase/assets/seedaudio-drama.mp3', favorite: true, span: 'audio', age: '1 hr ago' },
  { id: 'vid-02', type: 'video', title: 'One Shot Kitchen', model: 'Seedance 2.5', ratio: '16:9', prompt: 'One continuous camera move through a compact restaurant kitchen during service, confident choreography, natural steam, warm practical lights, energetic but readable.', src: '/showcase/assets/posters/seedance25-oneshot-1.jpg', video: '/showcase/assets/seedance25-oneshot-1.mp4', favorite: true, span: 'wide', age: '2 hr ago' },
  { id: 'img-03', type: 'image', title: 'Layered Storyboard', model: 'Seedream 5.0', ratio: '3:2', prompt: 'Six-frame cinematic storyboard for a quiet morning coffee campaign, consistent character and wardrobe, natural window light, understated warm palette.', src: '/showcase/assets/seedream5-storyboard.jpg', favorite: false, span: 'wide', age: 'Yesterday' },
  { id: 'vid-03', type: 'video', title: 'Waterfall Reveal', model: 'Veo 3.1', ratio: '16:9', prompt: 'Begin inches from moss and running water, crane backward through a rock opening to reveal a huge hidden valley of tiered waterfalls, golden mist, continuous smooth move.', src: '/showcase/assets/posters/veo31-t2v-demo.jpg', video: '/showcase/assets/veo31-t2v-demo.mp4', favorite: false, span: 'wide', age: 'Yesterday' },
  { id: 'aud-02', type: 'audio', title: 'Founder Voice', model: 'Voice Clone', ratio: '00:38', prompt: 'Clear and conversational founder voice, calm authority, short natural pauses, suitable for a product announcement.', src: '/showcase/assets/seedream5-editing-output.jpg', audio: '/showcase/assets/seedaudio-clone-priya.mp3', favorite: false, span: 'audio', age: '2 days ago' },
  { id: 'img-04', type: 'image', title: 'Arabic Launch Key Visual', model: 'Seedream 5.0', ratio: '4:5', prompt: 'Premium launch poster with precise Arabic typography, deep navy background, glowing amber object, generous negative space and refined editorial hierarchy.', src: '/showcase/assets/seedream5-lang-ar.jpg', favorite: true, span: 'tall', age: '3 days ago' },
];

const PAGE_COPY = {
  explore: { eyebrow: 'Creative feed', title: 'Explore what is possible', sub: 'A curated starting point for image, motion and sound. Open any work to study the prompt or use it as a starting point.' },
  image: { eyebrow: 'Image studio', title: 'Turn an idea into a visual system', sub: 'Keep the canvas dominant. The controls stay close to the prompt and out of the way.' },
  video: { eyebrow: 'Video studio', title: 'Direct motion with clarity', sub: 'Start with the story and camera. Expand advanced controls only when the shot needs them.' },
  audio: { eyebrow: 'Audio studio', title: 'Give the work a voice', sub: 'Create narration, dialogue and sound with a composer built around listening.' },
  library: { eyebrow: 'Your work', title: 'Everything you have created', sub: 'Return to a prompt, compare outputs and continue from any generation.' },
};

const CASE_GUIDES = {
  image: {
    purpose: 'Build campaign-ready key visuals, product scenes and visual directions from one controlled art direction.',
    bestFor: 'Concept development, social assets, storyboards, product photography and consistent campaign systems.',
    method: 'Define the subject first, then composition, light, material and the intended commercial use. Add references only when they improve consistency.',
  },
  video: {
    purpose: 'Turn a readable action into a directed shot with deliberate camera movement, timing and atmosphere.',
    bestFor: 'Advertising shots, product reveals, social motion, cinematic transitions and pre-visualisation.',
    method: 'Describe one action, one camera instruction and one final beat. Use a reference frame for continuity and media references for style or motion cues.',
  },
  audio: {
    purpose: 'Create narration, dialogue and sound direction that matches the pace and emotional character of the work.',
    bestFor: 'Voice-over, explainers, brand films, dialogue tests, product announcements and sound concepts.',
    method: 'Write for the ear: specify voice, pacing, accent, emotion and room tone before technical detail.',
  },
};

const SHOWCASE_FEATURES = {
  video: {
    eyebrow: 'Featured motion workflow',
    title: 'Direct a complete shot, not just a moving picture.',
    summary: 'Use references, camera language and timing together so the model understands what must stay consistent and what is allowed to move.',
    itemId: 'vid-01',
    bestFor: ['Product films', 'Social motion', 'Cinematic reveals'],
    steps: [
      ['01', 'Anchor', 'Choose a start frame or named media references when identity and art direction must remain stable.'],
      ['02', 'Direct', 'Describe one readable action, the camera position and the intended final beat.'],
      ['03', 'Budget', 'Select duration and resolution while watching the credit estimate update before generation.'],
    ],
  },
  image: {
    eyebrow: 'Featured image workflow',
    title: 'Build a reusable visual direction from one clear idea.',
    summary: 'Treat the prompt as art direction: subject, composition, light and material stay connected instead of becoming an unstructured list of adjectives.',
    itemId: 'img-01',
    bestFor: ['Campaign concepts', 'Product scenes', 'Storyboards'],
    steps: [
      ['01', 'Frame', 'Start with the subject and the commercial purpose of the image.'],
      ['02', 'Style', 'Control light, material, lens and palette as one visual system.'],
      ['03', 'Continue', 'Reuse the prompt and references across formats to keep a campaign consistent.'],
    ],
  },
  audio: {
    eyebrow: 'Featured sound workflow',
    title: 'Write a direction that can be heard before it is generated.',
    summary: 'Voice, pacing, emotion and room tone are presented as creative choices, making audio easier to compare and reuse across a project.',
    itemId: 'aud-01',
    bestFor: ['Voice-over', 'Explainers', 'Brand films'],
    steps: [
      ['01', 'Voice', 'Define accent, character and confidence before technical settings.'],
      ['02', 'Pacing', 'Describe pauses, emphasis and the rhythm of the delivery.'],
      ['03', 'Environment', 'Add room tone and context so the voice belongs in the scene.'],
    ],
  },
};

const VIDEO_MODELS = [
  { id: 'seedance-2.0', label: 'Seedance 2.0', vendor: 'ByteDance', badge: 'Recommended', modes: ['text-to-video', 'image-to-video', 'reference-to-video'], resolutions: ['480p', '720p', '1080p', '4k'], ratios: ['21:9', '16:9', '4:3', '1:1', '3:4', '9:16'], duration: { min: 4, max: 15, default: 5 }, audio: true, endFrame: true, media: true, rates: { '480p': 14, '720p': 27, '1080p': 74, '4k': 140 }, note: 'Best all-round option with synced audio and multi-media references.' },
  { id: 'seedance-2.0-mini', label: 'Seedance 2.0 Mini', vendor: 'ByteDance', badge: 'Best value', modes: ['text-to-video', 'image-to-video', 'reference-to-video'], resolutions: ['480p', '720p'], ratios: ['21:9', '16:9', '4:3', '1:1', '3:4', '9:16'], duration: { min: 4, max: 15, default: 5 }, audio: true, endFrame: true, media: true, rates: { '480p': 8, '720p': 16 }, note: 'Faster 480p/720p generations with the same reference workflow.' },
  { id: 'kling-2.5', label: 'Kling 2.5 Turbo Pro', vendor: 'Kuaishou', modes: ['text-to-video', 'image-to-video'], ratios: ['16:9', '9:16', '1:1'], durations: [5, 10], duration: { min: 5, max: 10, default: 5 }, negative: true, prices: { 5: 70, 10: 140 }, note: 'Cinematic motion with fixed 5s or 10s output and no generated audio.' },
  { id: 'hailuo-02', label: 'MiniMax Hailuo 02', vendor: 'MiniMax', modes: ['text-to-video', 'image-to-video'], ratios: ['16:9'], durations: [6, 10], duration: { min: 6, max: 10, default: 6 }, flatRate: 9, note: 'Simple fixed-768p workflow for quick motion tests.' },
  { id: 'veo-3.1', label: 'Google Veo 3.1', vendor: 'Google', badge: 'Native audio', modes: ['text-to-video', 'image-to-video'], resolutions: ['720p', '1080p'], ratios: ['16:9', '9:16'], durations: [4, 6, 8], duration: { min: 4, max: 8, default: 8 }, audio: true, endFrame: true, negative: true, rates: { '720p': 40, '1080p': 40 }, audioRates: { '720p': 52, '1080p': 52 }, note: 'Premium native-audio video with start and end-frame control.' },
  { id: 'seedance-2.5', label: 'Seedance 2.5', vendor: 'ByteDance', badge: 'Coming soon', disabled: true, modes: [], resolutions: [], ratios: [], duration: { min: 5, max: 10, default: 5 }, note: 'Premium 30-second one-shot and 4K tier; API access is not available yet.' },
];

const VIDEO_MODE_LABELS = {
  'text-to-video': 'Text → Video',
  'image-to-video': 'Frame → Video',
  'reference-to-video': 'Media → Video',
};

function estimatePreviewVideoCredits(model, duration, resolution, audio) {
  if (!model || model.disabled) return 0;
  if (model.prices) return Number(model.prices[duration] || model.prices[model.duration.default] || 0);
  if (model.flatRate) return Math.ceil(model.flatRate * duration);
  const table = model.audioRates && audio ? model.audioRates : model.rates;
  if (!table) return 0;
  const rate = table[resolution] || table[Object.keys(table)[0]] || 0;
  return Math.ceil(rate * duration);
}

const ICONS = {
  explore: '<path d="M4 5.5h7v6H4zM13 5.5h7v10h-7zM4 13.5h7v5H4zM13 17.5h7v1h-7z"/>',
  image: '<rect x="3.5" y="4" width="17" height="16" rx="3"/><circle cx="9" cy="9" r="1.5"/><path d="m5.5 17 4.3-4.3 3.2 3.2 2.3-2.3 3.2 3.4"/>',
  video: '<rect x="3" y="5" width="14" height="14" rx="3"/><path d="m17 10 4-2v8l-4-2z"/>',
  audio: '<path d="M5 10v4M9 7v10M13 4v16M17 8v8M21 10v4"/>',
  library: '<path d="M4 5.5h16M4 11.5h16M4 17.5h16"/><circle cx="7" cy="5.5" r="1.5" fill="currentColor"/><circle cx="15" cy="11.5" r="1.5" fill="currentColor"/><circle cx="10" cy="17.5" r="1.5" fill="currentColor"/>',
};

function Icon({ name, size = 20 }) {
  return <svg width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.7" strokeLinecap="round" strokeLinejoin="round" dangerouslySetInnerHTML={{ __html: ICONS[name] || ICONS.explore }} />;
}

function Logo() {
  return <div className="pv-logo" aria-label="YEA"><span>Y</span><i></i></div>;
}

function PreviewNotice() {
  const authenticated = window.__GEN_AUTH_PREVIEW === true;
  return <div className="pv-notice"><span className="pv-notice-dot"></span><strong>{authenticated ? 'Authenticated Preview' : 'UI Preview'}</strong><span>{authenticated ? 'Signed in · read-only fixtures' : 'Read-only demo · no live data'}</span></div>;
}

function Sidebar({ page, setPage }) {
  return <aside className="pv-sidebar">
    <div className="pv-sidebar-head"><Logo /></div>
    <nav className="pv-nav" aria-label="Primary">
      {['explore', 'image', 'video', 'audio', 'library'].map(name =>
        <button key={name} className={page === name ? 'active' : ''} onClick={() => setPage(name)} title={name[0].toUpperCase() + name.slice(1)}>
          <Icon name={name} /><span>{name[0].toUpperCase() + name.slice(1)}</span>
        </button>
      )}
    </nav>
    <div className="pv-sidebar-foot">
      <button title="Help"><span>?</span></button>
      <div className="pv-avatar">YX</div>
    </div>
  </aside>;
}

function Header({ page }) {
  const authenticated = window.__GEN_AUTH_PREVIEW === true;
  function signOut() { if (authenticated && window.__auth) window.__auth.signOut(); }
  return <header className="pv-topbar">
    <div className="pv-mobile-brand"><Logo /><b>YEA Create</b></div>
    <div className="pv-crumb">YEA Create <span>/</span> {page[0].toUpperCase() + page.slice(1)}</div>
    <PreviewNotice />
    <div className="pv-quota"><span>Monthly credits</span><b>8,420</b><i style={{ '--quota': '68%' }}></i></div>
    <button className="pv-icon-btn" aria-label={authenticated ? 'Sign out' : 'Notifications'} title={authenticated ? 'Sign out' : 'Notifications'} onClick={authenticated ? signOut : undefined}>{authenticated ? '↪' : '○'}</button>
  </header>;
}

function PageIntro({ page, children }) {
  const copy = PAGE_COPY[page];
  return <div className="pv-intro"><div><span>{copy.eyebrow}</span><h1>{copy.title}</h1><p>{copy.sub}</p></div>{children}</div>;
}

function TypePill({ type }) {
  return <span className={'pv-type-pill ' + type}><Icon name={type === 'image' ? 'image' : type === 'video' ? 'video' : 'audio'} size={13} />{type}</span>;
}

function MediaCard({ item, onOpen, compact = false }) {
  const [playing, setPlaying] = useState(false);
  const mediaRef = useRef(null);
  function togglePlay(e) {
    e.stopPropagation();
    const node = mediaRef.current;
    if (!node) return;
    if (node.paused) { node.play(); setPlaying(true); } else { node.pause(); setPlaying(false); }
  }
  return <article className={'pv-media-card ' + item.span + (compact ? ' compact' : '')} onClick={() => onOpen(item)}>
    <div className="pv-media-frame">
      {item.type === 'video'
        ? <video ref={mediaRef} src={item.video} poster={item.src} muted loop playsInline onEnded={() => setPlaying(false)} />
        : <img src={item.src} alt="" />}
      <div className="pv-card-wash"></div>
      <div className="pv-card-top"><TypePill type={item.type} /><button aria-label="Favorite" onClick={e => e.stopPropagation()}>{item.favorite ? '♥' : '♡'}</button></div>
      {item.type === 'video' && <button className="pv-play" onClick={togglePlay}>{playing ? 'Ⅱ' : '▶'}</button>}
      {item.type === 'audio' && <AudioBars />}
      <div className="pv-card-info"><h3>{item.title}</h3><p>{item.model} · {item.ratio}</p></div>
    </div>
  </article>;
}

function AudioBars() {
  return <div className="pv-audio-bars" aria-hidden="true">{Array.from({ length: 34 }, (_, i) => <i key={i} style={{ height: (16 + ((i * 17) % 48)) + '%' }}></i>)}</div>;
}

function DetailDrawer({ item, onClose, onUse }) {
  const [copied, setCopied] = useState(false);
  if (!item) return null;
  const guide = CASE_GUIDES[item.type] || CASE_GUIDES.image;
  function copyPrompt() {
    navigator.clipboard?.writeText(item.prompt).catch(() => {});
    setCopied(true); setTimeout(() => setCopied(false), 1200);
  }
  return <div className="pv-drawer-backdrop" onMouseDown={e => e.target === e.currentTarget && onClose()}>
    <section className="pv-drawer" role="dialog" aria-modal="true" aria-label={item.title}>
      <div className="pv-drawer-media">
        {item.type === 'video' ? <video src={item.video} poster={item.src} controls autoPlay muted loop /> : item.type === 'audio' ? <div className="pv-audio-stage"><img src={item.src} alt="" /><AudioPlayer item={item} /></div> : <img src={item.src} alt="" />}
        <button className="pv-close" onClick={onClose}>×</button>
      </div>
      <aside className="pv-drawer-info">
        <TypePill type={item.type} />
        <h2>{item.title}</h2><p className="pv-muted">Created {item.age}</p>
        <div className="pv-meta-row"><span>Model<b>{item.model}</b></span><span>Format<b>{item.ratio}</b></span></div>
        <div className="pv-prompt-label"><span>Prompt</span><button onClick={copyPrompt}>{copied ? 'Copied' : 'Copy'}</button></div>
        <div className="pv-prompt-box">{item.prompt}</div>
        <div className="pv-case-guide">
          <div><span>What it does</span><p>{guide.purpose}</p></div>
          <div><span>Best used for</span><p>{guide.bestFor}</p></div>
          <div><span>How it works</span><p>{guide.method}</p></div>
        </div>
        <div className="pv-drawer-actions"><button className="secondary" onClick={onClose}>Close</button><button className="primary" onClick={() => onUse(item)}>Use prompt</button></div>
        <p className="pv-safe-note"><span>●</span> Preview actions never write to your account.</p>
      </aside>
    </section>
  </div>;
}

function AudioPlayer({ item }) {
  const ref = useRef(null); const [playing, setPlaying] = useState(false);
  return <div className="pv-audio-player"><button onClick={() => { const a = ref.current; if (a.paused) { a.play(); setPlaying(true); } else { a.pause(); setPlaying(false); } }}>{playing ? 'Ⅱ' : '▶'}</button><AudioBars /><span>{item.ratio}</span><audio ref={ref} src={item.audio} onEnded={() => setPlaying(false)} /></div>;
}

function Explore({ openItem, useItem }) {
  const [filter, setFilter] = useState('all');
  const [featureType, setFeatureType] = useState('video');
  const items = filter === 'all' ? PREVIEW_ITEMS : PREVIEW_ITEMS.filter(i => i.type === filter);
  const feature = SHOWCASE_FEATURES[featureType];
  const featureItem = PREVIEW_ITEMS.find(item => item.id === feature.itemId);
  return <main className="pv-page pv-explore">
    <PageIntro page="explore"><button className="pv-primary" onClick={() => useItem(PREVIEW_ITEMS[0])}>Start creating <span>→</span></button></PageIntro>
    <section className="pv-showcase-spotlight">
      <div className="pv-showcase-visual" onClick={() => openItem(featureItem)}>
        {featureItem.type === 'video' ? <video src={featureItem.video} poster={featureItem.src} muted loop autoPlay playsInline /> : <img src={featureItem.src} alt="" />}
        {featureItem.type === 'audio' && <AudioBars />}
        <div className="pv-showcase-visual-copy"><TypePill type={featureItem.type} /><strong>{featureItem.title}</strong><span>{featureItem.model} · {featureItem.ratio}</span></div>
      </div>
      <div className="pv-showcase-story">
        <nav aria-label="Featured workflow type">{['video', 'image', 'audio'].map(type => <button key={type} className={featureType === type ? 'active' : ''} onClick={() => setFeatureType(type)}>{type[0].toUpperCase() + type.slice(1)}</button>)}</nav>
        <span>{feature.eyebrow}</span><h2>{feature.title}</h2><p>{feature.summary}</p>
        <div className="pv-showcase-tags">{feature.bestFor.map(tag => <i key={tag}>{tag}</i>)}</div>
        <div className="pv-showcase-steps">{feature.steps.map(step => <article key={step[0]}><b>{step[0]}</b><div><strong>{step[1]}</strong><p>{step[2]}</p></div></article>)}</div>
        <div className="pv-showcase-actions"><button onClick={() => openItem(featureItem)}>Open full breakdown</button><button className="primary" onClick={() => useItem(featureItem)}>Use this direction →</button></div>
      </div>
    </section>
    <div className="pv-showcase-section-title"><div><span>Curated gallery</span><h2>Browse real workflows and inspect every decision.</h2></div><p>Each card opens with the full prompt, intended use and a practical method—not just a finished image.</p></div>
    <div className="pv-filter-row"><div>{['all', 'image', 'video', 'audio'].map(f => <button key={f} onClick={() => setFilter(f)} className={filter === f ? 'active' : ''}>{f === 'all' ? 'All work' : f[0].toUpperCase() + f.slice(1)}</button>)}</div><span>{items.length} curated works</span></div>
    <div className="pv-bento">{items.map(item => <MediaCard key={item.id} item={item} onOpen={openItem} />)}</div>
    <section className="pv-explore-explainer">
      <div><span>Explore is more than a gallery</span><h2>Study the creative decision, not only the prompt.</h2><p>Every example keeps enough room to explain what the feature does, where it is useful and how the result was directed. Open any card to see the complete breakdown.</p></div>
      <div>{[['01','Understand','See the intended use and the creative problem this workflow solves.'],['02','Inspect','Read the prompt together with model and format—not as isolated text.'],['03','Continue','Take the direction into Image, Video or Audio and adapt it to your project.']].map(step => <article key={step[0]}><b>{step[0]}</b><h3>{step[1]}</h3><p>{step[2]}</p></article>)}</div>
    </section>
  </main>;
}

function ExploreHub({ openItem, useItem }) {
  const [version, setVersion] = useState('classic');
  return <div className="pv-explore-hub">
    <div className="pv-showcase-version-bar">
      <div><span>Explore Showcase</span><p>Keep the original model guide, or review the new workflow-led Showcase.</p></div>
      <nav aria-label="Showcase version">
        <button className={version === 'classic' ? 'active' : ''} onClick={() => setVersion('classic')}>Original Showcase</button>
        <button className={version === 'new' ? 'active' : ''} onClick={() => setVersion('new')}>Workflow Showcase</button>
      </nav>
    </div>
    {version === 'classic'
      ? <div className="pv-classic-showcase"><iframe src="/showcase/index.html" title="Classic Showcase" /></div>
      : <Explore openItem={openItem} useItem={useItem} />}
  </div>;
}

const VIDEO_PROMPT_LAYERS = [
  { label: 'Subject', color: '#F05A28', template: '[Subject]' },
  { label: 'Camera', color: '#4A7FD4', template: '[Camera]' },
  { label: 'Motion', color: '#7B5FC7', template: '[Motion]' },
  { label: 'Lighting', color: '#E89C24', template: '[Lighting]' },
];

function Composer({ type, prompt, setPrompt, onPreview, layers, children, hideGenerate = false }) {
  function addLayer(layer) {
    setPrompt(previous => {
      const current = previous.trimEnd();
      return current + (current ? '\n' : '') + layer.template + ' ';
    });
  }
  return <div className={'pv-composer ' + type}>
    {type === 'video' && <div className="pv-composer-heading"><b>Prompt</b><span>Build in layers or write freely</span></div>}
    <textarea value={prompt} onChange={e => setPrompt(e.target.value)} placeholder={'Describe the ' + (type === 'audio' ? 'voice or sound' : 'scene you want to create') + '…'} />
    {layers && <div className="pv-prompt-layers"><span>Add prompt layer</span><div>{layers.map(layer => <button key={layer.label} onClick={() => addLayer(layer)} style={{ '--layer-color': layer.color }}><i></i>＋ {layer.label}</button>)}</div></div>}
    <div className="pv-composer-bottom"><div className="pv-composer-tools">{children}</div>{!hideGenerate && <button className="pv-generate" onClick={onPreview}><span>✦</span> Generate <small>Preview</small></button>}</div>
  </div>;
}

function SelectChip({ label, value }) { return <button className="pv-select-chip"><span>{label}</span>{value}<b>⌄</b></button>; }

function ImageStudio({ seedPrompt, openItem }) {
  const [prompt, setPrompt] = useState(seedPrompt || 'A refined editorial portrait with sculptural lighting, soft mineral colours and precise art direction.');
  const [toast, setToast] = useState(false);
  useEffect(() => { if (seedPrompt) setPrompt(seedPrompt); }, [seedPrompt]);
  return <main className="pv-studio-page"><PageIntro page="image" />
    <div className="pv-canvas image-canvas">
      <div className="pv-canvas-toolbar"><span><i></i> Draft canvas</span><div><button>−</button><b>82%</b><button>＋</button></div></div>
      <div className="pv-image-result" onClick={() => openItem(PREVIEW_ITEMS[0])}><img src="/showcase/assets/gpt2-typography.jpg" alt="Preview generation" /><span>Open details</span></div>
      <div className="pv-floating-composer"><Composer type="image" prompt={prompt} setPrompt={setPrompt} onPreview={() => { setToast(true); setTimeout(() => setToast(false), 1800); }}><button className="pv-add">＋</button><SelectChip label="Model" value="Seedream 5.0" /><SelectChip label="Ratio" value="4:5" /><SelectChip label="Outputs" value="2" /></Composer></div>
      {toast && <div className="pv-toast">Preview only — no generation was submitted</div>}
    </div>
    <div className="pv-under-canvas"><h2>Recent images</h2><button>View all in Library →</button></div>
    <div className="pv-recent-strip">{PREVIEW_ITEMS.filter(i => i.type === 'image').map(i => <MediaCard compact key={i.id} item={i} onOpen={openItem} />)}</div>
  </main>;
}

function VideoStudio({ seedPrompt, openItem }) {
  const [prompt, setPrompt] = useState(seedPrompt || PREVIEW_ITEMS[1].prompt);
  const [modelId, setModelId] = useState('seedance-2.0');
  const [mode, setMode] = useState('text-to-video');
  const [ratio, setRatio] = useState('16:9');
  const [duration, setDuration] = useState(5);
  const [resolution, setResolution] = useState('720p');
  const [audio, setAudio] = useState(true);
  const [negativePrompt, setNegativePrompt] = useState('');
  const [seed, setSeed] = useState('');
  const [advanced, setAdvanced] = useState(false);
  const [toast, setToast] = useState(false);
  const [section, setSection] = useState('history');
  const activeModel = VIDEO_MODELS.find(model => model.id === modelId) || VIDEO_MODELS[0];
  const estimatedCredits = estimatePreviewVideoCredits(activeModel, duration, resolution, audio);
  const balanceAfter = Math.max(0, 8420 - estimatedCredits);
  function chooseModel(nextId) {
    const next = VIDEO_MODELS.find(model => model.id === nextId);
    if (!next || next.disabled) return;
    setModelId(next.id);
    setMode(next.modes[0]);
    setDuration(next.duration.default);
    setResolution(next.resolutions && next.resolutions.length ? next.resolutions[0] : '768p');
    setRatio(next.ratios[0] === '21:9' && next.ratios.includes('16:9') ? '16:9' : next.ratios[0]);
    setAudio(!!next.audio);
  }
  useEffect(() => { if (seedPrompt) setPrompt(seedPrompt); }, [seedPrompt]);
  return <main className="pv-studio-page video-page"><PageIntro page="video" />
    <div className="pv-video-workspace">
      <aside className="pv-control-rail pv-video-rail">
        <div className="pv-control-title"><div><span>Shot setup</span><small>Every cost-changing option is visible here.</small></div><b>01</b></div>
        <div className="pv-video-mode-row">{activeModel.modes.map(option => <button key={option} className={mode === option ? 'active' : ''} onClick={() => setMode(option)}>{VIDEO_MODE_LABELS[option]}</button>)}</div>
        {mode === 'image-to-video' && <label className="pv-video-field">Start frame<div className="pv-dropzone pv-video-dropzone"><span>＋</span><b>Add start frame</b><small>PNG, JPG or WebP</small></div></label>}
        {mode === 'reference-to-video' && <div className="pv-media-reference-box"><span>Media references</span><p>Use named assets directly inside the prompt.</p><div><button>＋ Images <small>0 / 9</small></button><button>＋ Video <small>0 / 3</small></button><button>＋ Audio <small>0 / 3</small></button></div></div>}
        <section className="pv-video-settings">
          <div className="pv-video-settings-head"><div><span>Production settings</span><small>These choices update the estimate below.</small></div><i>{activeModel.badge || activeModel.vendor}</i></div>
          <label className="pv-video-field">Model<select value={modelId} onChange={e => chooseModel(e.target.value)}>{VIDEO_MODELS.map(model => <option key={model.id} value={model.id} disabled={model.disabled}>{model.label}{model.disabled ? ' · coming soon' : ''}</option>)}</select></label>
          <p className="pv-model-note">{activeModel.note}</p>
          <div className="pv-video-setting-grid">
            <label className="pv-video-field">Ratio<select value={ratio} onChange={e => setRatio(e.target.value)}>{activeModel.ratios.map(option => <option key={option}>{option}</option>)}</select></label>
            <label className="pv-video-field">Duration{activeModel.durations ? <select value={duration} onChange={e => setDuration(Number(e.target.value))}>{activeModel.durations.map(option => <option key={option} value={option}>{option} sec</option>)}</select> : <div className="pv-video-duration"><input type="range" min={activeModel.duration.min} max={activeModel.duration.max} value={duration} onChange={e => setDuration(Number(e.target.value))} /><b>{duration}s</b></div>}</label>
            <label className="pv-video-field">Resolution{activeModel.resolutions && activeModel.resolutions.length ? <select value={resolution} onChange={e => setResolution(e.target.value)}>{activeModel.resolutions.map(option => <option key={option}>{option}</option>)}</select> : <div className="pv-fixed-value">768p fixed</div>}</label>
            <label className="pv-video-field">Audio<button className={'pv-audio-choice ' + (audio ? 'active' : '')} disabled={!activeModel.audio} onClick={() => activeModel.audio && setAudio(!audio)}><span>{activeModel.audio ? (audio ? 'On' : 'Off') : 'Not supported'}</span><i></i></button></label>
          </div>
          <details className="pv-model-comparison"><summary>Compare all models and starting costs <span>＋</span></summary><div>{VIDEO_MODELS.map(model => <article key={model.id} className={model.disabled ? 'disabled' : ''}><div><b>{model.label}</b><span>{model.vendor}</span></div><p>{model.note}</p><strong>{model.disabled ? 'Coming soon' : model.id === 'seedance-2.0' ? 'from 56 cr' : model.id === 'seedance-2.0-mini' ? 'from 32 cr' : model.id === 'kling-2.5' ? 'from 70 cr' : model.id === 'hailuo-02' ? 'from 54 cr' : 'from 160 cr'}</strong></article>)}</div></details>
        </section>
        <button className="pv-advanced" onClick={() => setAdvanced(!advanced)}>Advanced controls <small>End frame · negative prompt · seed</small><span>{advanced ? '−' : '+'}</span></button>
        {advanced && <div className="pv-video-advanced-body">
          {mode === 'image-to-video' && activeModel.endFrame && <label className="pv-video-field">End frame (optional)<button className="pv-advanced-upload">＋ Add end frame</button></label>}
          {activeModel.negative && <label className="pv-video-field">Negative prompt<textarea value={negativePrompt} onChange={e => setNegativePrompt(e.target.value)} placeholder="What should the model avoid?" /></label>}
          <label className="pv-video-field">Seed (optional)<input type="number" value={seed} onChange={e => setSeed(e.target.value)} placeholder="Random" /></label>
          <button className="pv-enhance-preview">✦ Enhance prompt</button>
        </div>}
        <Composer type="video" prompt={prompt} setPrompt={setPrompt} layers={VIDEO_PROMPT_LAYERS} hideGenerate onPreview={() => {}}><button className="pv-reference-add" onClick={() => { setToast(true); setTimeout(() => setToast(false), 1800); }}>＋ Add reference</button><span className="pv-char-count">{prompt.length} / 2,000</span></Composer>
        <section className="pv-video-budget">
          <div className="pv-budget-heading"><div><span>Estimated cost</span><small>Updates as you change settings</small></div><strong>{estimatedCredits.toLocaleString()} <i>credits</i></strong></div>
          <div className="pv-budget-breakdown"><span>{activeModel.label}</span><b>{duration}s · {resolution} · {ratio}{activeModel.audio ? (audio ? ' · audio on' : ' · audio off') : ''}</b></div>
          <div className="pv-budget-balance"><span>Available <b>8,420</b></span><span>After generation <b>{balanceAfter.toLocaleString()}</b></span></div>
          <button onClick={() => { setToast(true); setTimeout(() => setToast(false), 1800); }}><span>✦ Generate</span><b>{estimatedCredits.toLocaleString()} credits</b></button>
          <p>Preview only · no generation or credit charge will be submitted.</p>
        </section>
      </aside>
      <section className="pv-video-content"><div className="pv-video-content-head"><div className="pv-segment"><button className={section === 'history' ? 'active' : ''} onClick={() => setSection('history')}>History</button><button className={section === 'guide' ? 'active' : ''} onClick={() => setSection('guide')}>How it works</button></div><div className="pv-budget-overview"><span>Estimated budget</span><strong>{estimatedCredits.toLocaleString()} credits</strong><small>8,420 available · {balanceAfter.toLocaleString()} after</small></div></div>{section === 'history' ? <div className="pv-video-grid">{PREVIEW_ITEMS.filter(i => i.type === 'video').map(i => <MediaCard key={i.id} item={i} onOpen={openItem} />)}</div> : <HowItWorks />}</section>
      {toast && <div className="pv-toast">Preview only — no generation was submitted</div>}
    </div>
  </main>;
}

function HowItWorks() { return <div className="pv-how"><span>Three decisions make a strong shot</span><div>{[['01','Subject','Define one readable action.'],['02','Camera','Describe position and movement.'],['03','Timing','Set the rhythm and final beat.']].map(x => <article key={x[0]}><b>{x[0]}</b><h3>{x[1]}</h3><p>{x[2]}</p></article>)}</div><blockquote>“A controlled prompt leaves room for the model to create, while keeping the shot easy to direct.”</blockquote></div>; }

function AudioStudio({ seedPrompt, openItem }) {
  const [prompt, setPrompt] = useState(seedPrompt || PREVIEW_ITEMS[3].prompt); const [toast, setToast] = useState(false);
  useEffect(() => { if (seedPrompt) setPrompt(seedPrompt); }, [seedPrompt]);
  const audios = PREVIEW_ITEMS.filter(i => i.type === 'audio');
  return <main className="pv-studio-page audio-page"><PageIntro page="audio" />
    <div className="pv-audio-layout"><section className="pv-audio-list"><div className="pv-section-head"><div><h2>Recent audio</h2><p>Listen, compare and reuse a voice direction.</p></div><button>View library</button></div>{audios.map((item, index) => <article className="pv-audio-row" key={item.id} onClick={() => openItem(item)}><div className="pv-cover"><img src={item.src} alt="" /><span>{index + 1}</span></div><div className="pv-audio-copy"><h3>{item.title}</h3><p>{item.model} · {item.age}</p></div><AudioPlayer item={item} /><button className="pv-more">···</button></article>)}</section><aside className="pv-audio-guide"><span>Sound direction</span><h2>Write for the ear.</h2><p>Describe pace, emotion, accent and the room before adding technical detail.</p><div><b>Voice</b><span>Warm · assured · natural</span></div><div><b>Pacing</b><span>Measured with short pauses</span></div><div><b>Environment</b><span>Dry studio, subtle room tone</span></div></aside></div>
    <div className="pv-audio-composer-wrap"><Composer type="audio" prompt={prompt} setPrompt={setPrompt} onPreview={() => { setToast(true); setTimeout(() => setToast(false), 1800); }}><SelectChip label="Mode" value="Text to speech" /><SelectChip label="Voice" value="YEA Warm" /><SelectChip label="Length" value="Auto" /></Composer></div>{toast && <div className="pv-toast">Preview only — no generation was submitted</div>}
  </main>;
}

function Library({ openItem }) {
  const [filter, setFilter] = useState('all'); const [query, setQuery] = useState('');
  const items = PREVIEW_ITEMS.filter(i => (filter === 'all' || filter === 'recent' || (filter === 'favorites' ? i.favorite : i.type === filter)) && (!query || (i.title + i.prompt + i.model).toLowerCase().includes(query.toLowerCase())));
  return <main className="pv-page pv-library"><PageIntro page="library"><button className="pv-primary">＋ New collection</button></PageIntro><div className="pv-library-tools"><div className="pv-search"><span>⌕</span><input value={query} onChange={e => setQuery(e.target.value)} placeholder="Search prompts, models and titles" /></div><button>Sort: Newest ⌄</button></div><div className="pv-library-shell"><aside><span>Library</span>{[['all','My generations'],['favorites','Favorites'],['recent','Recent'],['image','Images'],['video','Videos'],['audio','Audio']].map(x => <button key={x[0]} className={filter === x[0] ? 'active' : ''} onClick={() => setFilter(x[0])}><Icon name={x[0] === 'all' || x[0] === 'recent' || x[0] === 'favorites' ? 'library' : x[0]} size={16} />{x[1]}<b>{x[0] === 'favorites' ? PREVIEW_ITEMS.filter(i => i.favorite).length : x[0] === 'all' || x[0] === 'recent' ? PREVIEW_ITEMS.length : PREVIEW_ITEMS.filter(i => i.type === x[0]).length}</b></button>)}<span className="collections">Collections</span><button><i className="pv-folder"></i>Campaign concepts<b>6</b></button><button><i className="pv-folder"></i>Social experiments<b>3</b></button><p>Collections are a UI preview. No Project records are created.</p></aside><section><div className="pv-library-heading"><h2>{filter === 'all' ? 'My generations' : filter[0].toUpperCase() + filter.slice(1)}</h2><span>{items.length} items</span></div>{items.length ? <div className="pv-library-grid">{items.map(i => <MediaCard compact key={i.id} item={i} onOpen={openItem} />)}</div> : <div className="pv-empty"><span>⌕</span><h3>No work found</h3><p>Try another title, prompt or model.</p></div>}</section></div></main>;
}

function MobileNav({ page, setPage }) { return <nav className="pv-mobile-nav">{['explore','image','video','audio','library'].map(name => <button key={name} onClick={() => setPage(name)} className={page === name ? 'active' : ''}><Icon name={name} size={18}/><span>{name}</span></button>)}</nav>; }

function YEAPreviewApp() {
  const [page, setPage] = useState('explore'); const [detail, setDetail] = useState(null); const [seed, setSeed] = useState({ type: '', prompt: '' });
  function useItem(item) { setDetail(null); setSeed({ type: item.type, prompt: item.prompt }); setPage(item.type); window.scrollTo({ top: 0, behavior: 'smooth' }); }
  useEffect(() => { document.body.classList.add('pv-body'); return () => document.body.classList.remove('pv-body'); }, []);
  return <div className="pv-app"><Sidebar page={page} setPage={setPage} /><div className="pv-main"><Header page={page} />{page === 'explore' && <ExploreHub openItem={setDetail} useItem={useItem} />}{page === 'image' && <ImageStudio seedPrompt={seed.type === 'image' ? seed.prompt : ''} openItem={setDetail} />}{page === 'video' && <VideoStudio seedPrompt={seed.type === 'video' ? seed.prompt : ''} openItem={setDetail} />}{page === 'audio' && <AudioStudio seedPrompt={seed.type === 'audio' ? seed.prompt : ''} openItem={setDetail} />}{page === 'library' && <Library openItem={setDetail} />}</div><MobileNav page={page} setPage={setPage} /><DetailDrawer item={detail} onClose={() => setDetail(null)} onUse={useItem} /></div>;
}

function YEAProductionShell() {
  const [page, setPage] = useState('video');
  const [detail, setDetail] = useState(null);
  const [seed, setSeed] = useState(null);
  function choosePage(next) { setPage(next); window.scrollTo({ top: 0, behavior: 'smooth' }); }
  function useItem(item) {
    setDetail(null);
    setSeed({ type: item.type, prompt: item.prompt, key: Date.now() });
    choosePage(item.type);
  }
  function syncStudioTab(next) { setPage(next === 'showcase' ? 'explore' : next); }
  return <div className="pv-app pv-live-app">
    <Sidebar page={page} setPage={choosePage} />
    <div className="pv-main">
      {page === 'explore' && <React.Fragment>
        <div className="pv-topbar pv-live-explore-bar"><div className="pv-crumb">YEA Create <span>/</span> Explore</div><div className="pv-live-badge">Curated inspiration</div></div>
        <ExploreHub openItem={setDetail} useItem={useItem} />
      </React.Fragment>}
      <div className="pv-live-studio-mount" style={{ display: page === 'explore' ? 'none' : 'contents' }}>
        <window.AIStudioPanel onClose={() => {}} shellMode={true} externalTab={page === 'explore' ? 'video' : page} externalSeed={seed} onExternalTabChange={syncStudioTab} />
      </div>
    </div>
    <MobileNav page={page} setPage={choosePage} />
    <DetailDrawer item={detail} onClose={() => setDetail(null)} onUse={useItem} />
  </div>;
}

window.YEAPreviewApp = YEAPreviewApp;
window.YEAProductionShell = YEAProductionShell;
