// ── Result — painted preview, palette, step-by-step (PaintMini) ──────
// Reads real project data from /api/projects/:id.

// Inject shimmer / spinner / fade keyframes once — no build step, so we
// register them from JS and guard against duplicate insertion.
function ensureResultStyles() {
  if (typeof document === 'undefined') return;
  if (document.getElementById('pm-result-styles')) return;
  const el = document.createElement('style');
  el.id = 'pm-result-styles';
  el.textContent = `
    @keyframes pm-shimmer {
      0%   { background-position: 200% 0; }
      100% { background-position: -200% 0; }
    }
    @keyframes pm-spin { to { transform: rotate(360deg); } }
    @keyframes pm-fade-in { from { opacity: 0; transform: scale(1.01); } to { opacity: 1; transform: scale(1); } }
    @keyframes pm-status-in { from { opacity: 0; transform: translateY(4px); } to { opacity: 1; transform: translateY(0); } }
    @keyframes pm-sweep { 0% { transform: translateY(-100%); } 100% { transform: translateY(200%); } }
    @keyframes pm-dab { 0% { opacity: 0; transform: scale(0.2); } 60% { transform: scale(1.15); } 100% { opacity: 1; transform: scale(1); } }
    @keyframes pm-pulse { 0%,100% { opacity: 1; } 50% { opacity: 0.35; } }
    @keyframes pm-tip-in { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: translateY(0); } }
    .res-actions { display: flex; align-items: center; gap: 8px; flex-shrink: 0; }
    .res-menu-btn { display: none; align-items: center; justify-content: center; width: 40px; height: 40px; border-radius: 8px; background: rgba(246,244,239,0.08); color: var(--sidebar-ink); flex: none; }
    @media (max-width: 760px) {
      .res-actions { display: none; }
      .res-menu-btn { display: inline-flex; }
    }
    @media (max-width: 820px) {
      .res-render-col { position: static !important; }
    }
    .pm-shimmer {
      background: linear-gradient(
        100deg,
        var(--surface-2) 8%,
        var(--surface-3) 22%,
        var(--brand-soft) 40%,
        var(--surface-3) 58%,
        var(--surface-2) 74%
      );
      background-size: 200% 100%;
      animation: pm-shimmer 1.7s ease-in-out infinite;
    }
  `;
  document.head.appendChild(el);
}

const PM_LOADING_STAGES = [
  'Reading the sculpt',
  'Mixing your palette',
  'Blocking in basecoats',
  'Layering & shadows',
  'Edge highlights',
  'Final varnish',
];

const PM_PAINTING_TIPS = [
  'Thin your paints — two thin coats beat one thick one.',
  'Let each layer dry fully so the next one doesn’t lift it.',
  'A wash flows into recesses to create instant shadow.',
  'Edge highlights catch light on the sharpest raised edges.',
  'Keep a wet palette so paint doesn’t dry on the brush.',
  'Basecoat first — every technique builds on a clean base.',
  'Paint from the inside out, darkest recesses to brightest edges.',
];

function Result({ navigate, user, credits, onSignOut, projectId, initialDifficulty, onProjectChanged }) {
  const [project, setProject] = useState(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);
  const [variant, setVariant] = useState(initialDifficulty === 'beginner' ? 'beginner' : 'advanced');
  const [busy, setBusy] = useState(false);
  const [confirmDelete, setConfirmDelete] = useState(false);
  const [shareOpen, setShareOpen] = useState(false);

  useEffect(() => { ensureResultStyles(); }, []);

  // Initial fetch
  useEffect(() => {
    let cancelled = false;
    setLoading(true);
    Api.getProject(projectId)
      .then(p => { if (!cancelled) { setProject(p); setLoading(false); } })
      .catch(err => { if (!cancelled) { setError(err.message); setLoading(false); } });
    return () => { cancelled = true; };
  }, [projectId]);

  // Default variant: respect the difficulty the user chose in the editor.
  // Fall back to whichever scheme is available.
  useEffect(() => {
    if (!project) return;
    if (initialDifficulty === 'beginner' && project.beginner_scheme) setVariant('beginner');
    else if (initialDifficulty === 'advanced' && project.advanced_scheme) setVariant('advanced');
    else if (project.advanced_scheme) setVariant('advanced');
    else if (project.beginner_scheme) setVariant('beginner');
  }, [project, initialDifficulty]);

  // Poll for the painted image + steps while they're still missing for the
  // active variant. Background generation in the editor writes them as they
  // finish; we surface them as soon as they land.
  useEffect(() => {
    if (!project) return;
    const needsImage = variant === 'beginner'
      ? !project.generated_beginner_image
      : !project.generated_advanced_image;
    const needsSteps = variant === 'beginner'
      ? !Array.isArray(project.steps_beginner) || project.steps_beginner.length === 0
      : !Array.isArray(project.steps_advanced) || project.steps_advanced.length === 0;
    if (!needsImage && !needsSteps) return;

    let cancelled = false;
    const timer = setInterval(() => {
      Api.getProject(projectId).then(p => {
        if (cancelled) return;
        setProject(p);
      }).catch(() => {});
    }, 4000);
    return () => { cancelled = true; clearInterval(timer); };
  }, [project, variant, projectId]);

  async function handleDelete() {
    setBusy(true);
    try {
      await Api.deleteProject(projectId);
      onProjectChanged && onProjectChanged();
      navigate('studio');
    } catch (err) {
      setError(err.message);
      setBusy(false);
    }
  }

  // Start a new design from the same base photo. Each generation is its own
  // entry — re-using a base never overwrites a previous design.
  function handleNewFromBase() {
    if (!project) return;
    navigate('editor', { seedBaseUrl: project.miniature_image });
  }

  if (loading) {
    return (
      <div data-screen-label="result">
        <TopBar id="········" onStudio={() => navigate('studio')} disabled />
        <div className="container" style={{ padding: '120px 32px', textAlign: 'center', color: 'var(--ink-3)' }}>
          <span style={{
            display: 'inline-block', width: 30, height: 30, borderRadius: '50%',
            border: '3px solid var(--line-2)', borderTopColor: 'var(--brand)',
            animation: 'pm-spin 0.8s linear infinite', marginBottom: 16,
          }} />
          <div className="mono" style={{ fontSize: 11, letterSpacing: '0.14em', textTransform: 'uppercase' }}>
            Loading project…
          </div>
        </div>
      </div>
    );
  }
  if (error || !project) {
    return (
      <div data-screen-label="result">
        <TopBar id="········" onStudio={() => navigate('studio')} />
        <div className="container" style={{ padding: '120px 32px', textAlign: 'center' }}>
          <h1 className="display" style={{ fontSize: 40, marginBottom: 12 }}><em>Project not found</em></h1>
          <div style={{ color: 'var(--ink-3)', marginBottom: 24 }}>{error || 'It may have been deleted.'}</div>
          <button className="btn btn-ghost" onClick={() => navigate('studio')}>← Back to studio</button>
        </div>
      </div>
    );
  }

  const scheme = (variant === 'beginner' ? project.beginner_scheme : project.advanced_scheme) || {};
  const steps  = (variant === 'beginner' ? project.steps_beginner : project.steps_advanced) || [];
  const paintedImage = variant === 'beginner' ? project.generated_beginner_image : project.generated_advanced_image;
  const paintedReady = Boolean(paintedImage);

  const archetype = scheme.theme || 'Custom miniature';
  const projectName = scheme.theme || 'Untitled scheme';
  // Prefer the scheme's own tier (Beginner / Intermediate / Expert) over the
  // coarse beginner/advanced storage slot, so Intermediate isn't shown as Expert.
  const difficultyLabel = scheme.difficulty || (variant === 'beginner' ? 'Beginner' : 'Advanced');
  const themeText = scheme.description || scheme.color_placement || 'Your custom painting scheme.';
  const timeStr = scheme.estimated_time || '—';
  const paintCount = scheme.colors?.length || 0;
  const techniques = inferTechniques(steps, variant);
  const idTag = project.id.slice(0, 8).toUpperCase();

  const hasBeginner = Boolean(project.beginner_scheme);
  const hasAdvanced = Boolean(project.advanced_scheme);

  return (
    <div data-screen-label="result" style={{ background: 'var(--bg)', minHeight: '100vh' }}>
      <TopBar
        id={idTag}
        onStudio={() => navigate('studio')}
        onDownload={() => downloadPdf(project.id)}
        onNewFromBase={handleNewFromBase}
        onNewUpload={() => navigate('editor')}
        onShare={() => setShareOpen(true)}
        onDelete={() => setConfirmDelete(true)}
        disabled={busy}
      />

      {confirmDelete && (
        <Modal onClose={() => !busy && setConfirmDelete(false)}>
          <div style={{ padding: '26px 26px 22px' }}>
            <div style={{
              width: 44, height: 44, borderRadius: 12, marginBottom: 16,
              background: 'rgba(154,27,31,.10)', border: '1px solid rgba(154,27,31,.28)',
              display: 'grid', placeItems: 'center', color: 'var(--crimson)',
            }}><PMIcon name="trash" size={20} /></div>
            <h2 className="display" style={{ fontSize: 26, marginBottom: 8 }}>Delete <em>{projectName}</em>?</h2>
            <p style={{ color: 'var(--ink-2)', fontSize: 14, lineHeight: 1.55, margin: '0 0 22px' }}>
              This permanently removes the design and both painting schemes. This cannot be undone.
            </p>
            <div style={{ display: 'flex', gap: 10, justifyContent: 'flex-end' }}>
              <button className="btn btn-ghost" onClick={() => setConfirmDelete(false)} disabled={busy}>Cancel</button>
              <button
                onClick={handleDelete}
                disabled={busy}
                className="btn"
                style={{ background: 'var(--crimson)', color: '#fff' }}
              >{busy ? 'Deleting…' : 'Delete design'}</button>
            </div>
          </div>
        </Modal>
      )}

      {shareOpen && (
        <ShareModal
          onClose={() => setShareOpen(false)}
          title={projectName}
          shareUrl={`${window.location.origin}/share/${project.id}`}
          imageUrl={paintedImage}
        />
      )}

      {/* Title block */}
      <div className="container" style={{ padding: '26px 32px 22px', borderBottom: '1px solid var(--line)' }}>
        <div style={{
          display: 'flex', alignItems: 'flex-end', justifyContent: 'space-between',
          gap: 24, flexWrap: 'wrap',
        }}>
          <div style={{ minWidth: 0 }}>
            <span className="eyebrow" style={{ color: 'var(--brand-deep)' }}>
              {archetype} · {difficultyLabel}
            </span>
            <h1 className="display" style={{ fontSize: 'clamp(40px, 5.5vw, 64px)', lineHeight: 1, letterSpacing: '-0.02em', margin: '8px 0 8px' }}>
              <em>{projectName}</em>
            </h1>
            <p style={{ color: 'var(--ink-2)', fontSize: 14.5, maxWidth: 480, margin: 0, lineHeight: 1.55 }}>
              {themeText}
            </p>
            {(hasBeginner && hasAdvanced) && (
              <DifficultyToggle variant={variant} setVariant={setVariant} />
            )}
          </div>
          <div style={{ display: 'flex', gap: 26 }}>
            <Stat label="Time" value={timeStr} />
            <Stat label="Steps" value={steps.length || '—'} />
            <Stat label="Paints" value={paintCount || '—'} />
          </div>
        </div>
      </div>

      {/* Main grid */}
      <div className="container" style={{ padding: '24px 32px 64px' }}>
        <div className="pm-split" style={{
          ['--split']: '0.85fr 1.15fr',
          gap: 24,
          alignItems: 'start',
        }}>
          <RenderColumn
            paintedImage={paintedImage}
            paintedReady={paintedReady}
            baseImage={project.miniature_image}
            scheme={scheme}
            variant={variant}
            archetype={archetype}
            techniques={techniques}
          />
          <StepsColumn steps={steps} scheme={scheme} variant={variant} timeStr={timeStr} />
        </div>
      </div>
    </div>
  );
}

// ── Slim dark top bar (per design) ─────────────────────────────────
function TopBar({ id, onStudio, onDownload, onNewFromBase, onNewUpload, onShare, onDelete, disabled }) {
  const [menuOpen, setMenuOpen] = useState(false);
  const linkStyle = {
    display: 'inline-flex', alignItems: 'center', gap: 6, height: 30,
    padding: '0 13px', borderRadius: 6,
    border: '1px solid var(--sidebar-line)',
    background: 'transparent', color: 'var(--sidebar-ink)',
    fontFamily: 'var(--sans)', fontSize: 11.5, fontWeight: 600,
    letterSpacing: '0.03em', textTransform: 'uppercase',
    cursor: disabled ? 'default' : 'pointer',
    opacity: disabled ? 0.5 : 1,
    transition: 'border-color 160ms, background 160ms',
  };
  const actions = [
    onDownload && { label: '⤓ Download PDF', onClick: onDownload },
    onNewFromBase && { label: '↻ New from base', onClick: onNewFromBase },
    onNewUpload && { label: '+ New upload', onClick: onNewUpload },
    onShare && { label: '↗ Share', onClick: onShare, primary: true },
    onDelete && { label: '✕ Delete', onClick: onDelete, danger: true },
  ].filter(Boolean);
  const run = (fn) => { setMenuOpen(false); fn(); };

  useEffect(() => {
    if (!menuOpen) return;
    document.body.style.overflow = 'hidden';
    const onKey = (e) => { if (e.key === 'Escape') setMenuOpen(false); };
    document.addEventListener('keydown', onKey);
    return () => { document.body.style.overflow = ''; document.removeEventListener('keydown', onKey); };
  }, [menuOpen]);

  return (
    <div style={{
      background: 'var(--sidebar)', color: 'var(--sidebar-ink)',
      borderBottom: '1px solid rgba(0,0,0,0.4)',
    }}>
      <div className="container res-topbar-inner" style={{
        display: 'flex', alignItems: 'center', gap: 12,
        minHeight: 52, padding: '0 clamp(16px,4vw,32px)',
      }}>
        <button onClick={onStudio} style={{
          background: 'transparent', border: 'none', cursor: 'pointer',
          color: 'var(--sidebar-ink-2)', fontFamily: 'var(--sans)',
          fontSize: 12.5, fontWeight: 600, flexShrink: 0,
        }}>← Studio</button>
        <img className="pm-logo pm-logo--dark pm-logo--sm" src="/assets/logo-new.png" alt="PaintMini" style={{ marginLeft: 4, flexShrink: 0 }} />
        <div className="res-topbar-spacer" style={{ flex: 1 }} />
        <div className="res-actions">
          {actions.map((a, i) => (
            <button key={i} disabled={disabled} onClick={a.onClick}
              style={a.primary ? { ...linkStyle, background: 'var(--brand)', color: '#242424', borderColor: 'var(--brand)' }
                : a.danger ? { ...linkStyle, color: 'var(--brand-bright)', borderColor: 'rgba(242,140,56,0.4)' }
                : linkStyle}>{a.label}</button>
          ))}
        </div>
        {/* Mobile: hamburger → actions drawer */}
        <button className="res-menu-btn" aria-label="Actions" onClick={() => setMenuOpen(true)}>
          <svg width="20" height="20" viewBox="0 0 20 20" fill="none" stroke="currentColor" strokeWidth="1.7" strokeLinecap="round"><path d="M3 6h14"/><path d="M3 10h14"/><path d="M3 14h14"/></svg>
        </button>
      </div>

      {menuOpen && (
        <div className="pm-drawer-overlay" onClick={() => setMenuOpen(false)}>
          <aside className="pm-drawer" style={{ left: 'auto', right: 0, animationName: 'pmDrawerInRight' }} onClick={(e) => e.stopPropagation()}>
            <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 18 }}>
              <span className="mono" style={{ fontSize: 10, letterSpacing: '0.14em', color: 'var(--sidebar-ink-3)' }}>DESIGN ACTIONS</span>
              <button aria-label="Close" onClick={() => setMenuOpen(false)} style={{ color: 'var(--sidebar-ink)', width: 34, height: 34, borderRadius: 8, display: 'grid', placeItems: 'center' }}>
                <svg width="18" height="18" viewBox="0 0 18 18" fill="none" stroke="currentColor" strokeWidth="1.7" strokeLinecap="round"><path d="M4 4l10 10M14 4L4 14"/></svg>
              </button>
            </div>
            <div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
              {actions.map((a, i) => (
                <button key={i} disabled={disabled} onClick={() => run(a.onClick)} style={{
                  textAlign: 'left', padding: '13px 14px', borderRadius: 10, fontSize: 14.5, fontWeight: 600,
                  background: a.primary ? 'var(--brand)' : 'transparent',
                  color: a.primary ? '#242424' : a.danger ? 'var(--brand-bright)' : 'var(--sidebar-ink)',
                  border: a.primary ? 'none' : '1px solid var(--sidebar-line)',
                }}>{a.label}</button>
              ))}
            </div>
          </aside>
        </div>
      )}
    </div>
  );
}

function DifficultyToggle({ variant, setVariant }) {
  const opt = (key, label) => {
    const active = variant === key;
    return (
      <button
        onClick={() => setVariant(key)}
        style={{
          flex: 1, padding: '7px 14px', borderRadius: 6, border: 'none',
          background: active ? 'var(--surface)' : 'transparent',
          boxShadow: active ? '0 1px 2px rgba(0,0,0,0.08)' : 'none',
          color: active ? 'var(--ink)' : 'var(--ink-3)',
          fontFamily: 'var(--sans)', fontSize: 12, fontWeight: 600,
          cursor: 'pointer', transition: 'all 160ms',
        }}
      >{label}</button>
    );
  };
  return (
    <div style={{
      display: 'inline-flex', gap: 3, marginTop: 16,
      padding: 3, borderRadius: 8,
      background: 'var(--surface-2)', border: '1px solid var(--line)',
    }}>
      {opt('beginner', 'Beginner')}
      {opt('advanced', 'Advanced')}
    </div>
  );
}

function inferTechniques(steps, variant) {
  const seen = new Set();
  for (const s of steps || []) {
    if (isRealTechnique(s.technique)) seen.add(s.technique);
  }
  if (seen.size === 0) {
    return variant === 'beginner' ? ['Basecoat', 'Wash'] : ['Basecoat', 'Layering', 'Edge highlight', 'Wash'];
  }
  return Array.from(seen);
}

async function downloadPdf(projectId) {
  try {
    const headers = { 'Content-Type': 'application/json' };
    // Use the Clerk session token (same as Api).
    let token = null;
    try { token = await window.__clerk?.session?.getToken(); } catch {}
    if (token) headers.Authorization = `Bearer ${token}`;
    const resp = await fetch('/api/generate-pdf', {
      method: 'POST', headers,
      body: JSON.stringify({ projectId }),
    });
    if (!resp.ok) {
      const t = await resp.text().catch(() => '');
      throw new Error(`PDF: ${resp.status}${t ? ' — ' + t.slice(0, 120) : ''}`);
    }
    const blob = await resp.blob();
    const url = URL.createObjectURL(blob);
    const a = document.createElement('a');
    a.href = url; a.download = `paintmini-guide-${projectId.slice(0,8)}.pdf`;
    document.body.appendChild(a); a.click(); a.remove();
    URL.revokeObjectURL(url);
  } catch (err) {
    alert('PDF download failed: ' + err.message);
  }
}

function Stat({ label, value }) {
  return (
    <div style={{ textAlign: 'left' }}>
      <div style={{ fontFamily: 'var(--serif)', fontSize: 30, fontWeight: 600, color: 'var(--ink)', lineHeight: 1 }}>
        {value}
      </div>
      <div className="mono" style={{ fontSize: 9, letterSpacing: '0.12em', color: 'var(--ink-3)', textTransform: 'uppercase', marginTop: 5 }}>
        {label}
      </div>
    </div>
  );
}

// ── LEFT column: image card + palette + techniques ─────────────────
function RenderColumn({ paintedImage, paintedReady, baseImage, scheme, variant, archetype, techniques }) {
  const difficultyLabel = scheme.difficulty || (variant === 'beginner' ? 'Beginner' : 'Advanced');
  const [showOriginal, setShowOriginal] = useState(false);
  const canCompare = paintedReady && !!baseImage;
  const shownSrc = showOriginal && baseImage ? baseImage : paintedImage;
  return (
    <div className="res-render-col" style={{ display: 'flex', flexDirection: 'column', gap: 16, position: 'sticky', top: 20 }}>
      <div className="card" style={{ overflow: 'hidden', padding: 0 }}>
        <div style={{ position: 'relative', aspectRatio: '3 / 4', background: 'var(--surface-2)' }}>
          {paintedReady ? (
            <img
              key={showOriginal ? 'orig' : 'painted'}
              src={shownSrc}
              alt={archetype}
              style={{
                position: 'absolute', inset: 0, width: '100%', height: '100%',
                objectFit: 'cover', animation: 'pm-fade-in 400ms ease-out',
              }}
            />
          ) : (
            <PaintingLoader baseImage={baseImage} scheme={scheme} />
          )}

          {paintedReady && (
            <>
              <span className="mono" style={{
                position: 'absolute', top: 12, left: 14,
                fontSize: 9, letterSpacing: '0.1em', color: '#fff',
                textShadow: '0 1px 3px rgba(0,0,0,0.6)',
              }}>
                {showOriginal ? 'YOUR PHOTO · UNPAINTED' : `SCHEME ${variant === 'beginner' ? 'A' : 'B'} · ${difficultyLabel.toUpperCase()}`}
              </span>
              {!showOriginal && (
                <span className="mono" style={{
                  position: 'absolute', top: 10, right: 12,
                  padding: '4px 10px', borderRadius: 4,
                  background: 'var(--brand)', color: '#242424',
                  fontSize: 9, fontWeight: 600, letterSpacing: '0.06em',
                }}>
                  {difficultyLabel.toUpperCase()}
                </span>
              )}

              {/* Original ⟷ Painted toggle */}
              {canCompare && (
                <div style={{
                  position: 'absolute', bottom: 12, left: '50%', transform: 'translateX(-50%)',
                  display: 'flex', background: 'rgba(28,26,23,0.72)', backdropFilter: 'blur(6px)',
                  borderRadius: 100, padding: 3, gap: 2,
                }}>
                  {[['painted', 'Painted'], ['original', 'Original']].map(([key, label]) => {
                    const on = (key === 'original') === showOriginal;
                    return (
                      <button key={key} onClick={() => setShowOriginal(key === 'original')} style={{
                        padding: '6px 14px', borderRadius: 100, fontSize: 11, fontWeight: 600,
                        background: on ? 'var(--brand)' : 'transparent',
                        color: on ? '#242424' : 'rgba(246,244,239,0.85)',
                        transition: 'background 160ms, color 160ms',
                      }}>{label}</button>
                    );
                  })}
                </div>
              )}
            </>
          )}
        </div>
      </div>

      {/* Palette */}
      <div className="card" style={{ padding: 0, overflow: 'hidden' }}>
        <div style={{
          borderBottom: '2px solid var(--ink)', padding: '11px 16px',
          display: 'flex', justifyContent: 'space-between', alignItems: 'center',
        }}>
          <span style={{ fontSize: 12, fontWeight: 800, letterSpacing: '0.05em', textTransform: 'uppercase' }}>Palette</span>
          <span className="mono" style={{ fontSize: 9, color: 'var(--ink-3)' }}>
            {(scheme.colors || []).length} PAINTS
          </span>
        </div>
        {(scheme.colors || []).length === 0 ? (
          <div style={{ padding: '12px 16px', display: 'flex', flexDirection: 'column', gap: 10 }}>
            {[0, 1, 2, 3].map(i => (
              <div key={i} style={{ display: 'grid', gridTemplateColumns: '22px 1fr 44px', gap: 11, alignItems: 'center' }}>
                <div className="pm-shimmer" style={{ width: 22, height: 22, borderRadius: 4 }} />
                <div className="pm-shimmer" style={{ height: 11, borderRadius: 3, width: '70%' }} />
                <div className="pm-shimmer" style={{ height: 9, borderRadius: 3 }} />
              </div>
            ))}
          </div>
        ) : (
          (scheme.colors || []).map((hex, i) => {
            const paint = paintFor(hex);
            const isLast = i === scheme.colors.length - 1;
            return (
              <div key={`${hex}-${i}`} style={{
                display: 'grid', gridTemplateColumns: '24px 1fr auto', gap: 12, alignItems: 'center',
                padding: '9px 16px',
                borderBottom: isLast ? 'none' : '1px solid var(--line)',
              }}>
                <span style={{ width: 24, height: 24, borderRadius: 5, background: hex, boxShadow: 'inset 0 0 0 1px rgba(0,0,0,0.2)' }} />
                <span style={{ fontSize: 12.5, fontWeight: 500 }}>{paint?.name || `Colour ${i + 1}`}</span>
                <span className="mono" style={{ fontSize: 9.5, color: 'var(--ink-3)', letterSpacing: '0.04em' }}>
                  {paint ? `${paint.brand.toUpperCase()} · ${paint.code}` : hex.toUpperCase()}
                </span>
              </div>
            );
          })
        )}
      </div>

      {/* Techniques */}
      <div className="card" style={{ padding: '13px 16px' }}>
        <div style={{ fontSize: 12, fontWeight: 800, letterSpacing: '0.05em', textTransform: 'uppercase', marginBottom: 10 }}>Techniques</div>
        <div style={{ display: 'flex', flexWrap: 'wrap', gap: 6 }}>
          {techniques.map(t => (
            <span key={t} style={{
              padding: '4px 11px', borderRadius: 4,
              background: 'var(--surface-2)', border: '1px solid var(--line)',
              fontSize: 11, color: 'var(--ink-2)',
            }}>{t}</span>
          ))}
        </div>
      </div>
    </div>
  );
}

// ── Best-in-class painting loader ──────────────────────────────────
function PaintingLoader({ baseImage, scheme }) {
  const [stage, setStage] = useState(0);
  const [tip, setTip] = useState(0);
  const [elapsed, setElapsed] = useState(0);
  const palette = (scheme?.colors || []).slice(0, 6);

  // Advance stages over time (hold on the final one until the image arrives).
  useEffect(() => {
    const id = setInterval(() => {
      setStage(s => Math.min(s + 1, PM_LOADING_STAGES.length - 1));
    }, 7000);
    return () => clearInterval(id);
  }, []);
  // Rotate painting tips.
  useEffect(() => {
    const id = setInterval(() => setTip(t => (t + 1) % PM_PAINTING_TIPS.length), 4500);
    return () => clearInterval(id);
  }, []);
  // Elapsed timer.
  useEffect(() => {
    const id = setInterval(() => setElapsed(e => e + 1), 1000);
    return () => clearInterval(id);
  }, []);

  const mm = String(Math.floor(elapsed / 60)).padStart(1, '0');
  const ss = String(elapsed % 60).padStart(2, '0');
  // Progress creeps toward ~92% and holds until done (never fake-completes).
  const pct = Math.min(92, Math.round(((stage + 1) / PM_LOADING_STAGES.length) * 88) + 6);

  return (
    <div style={{ position: 'absolute', inset: 0, overflow: 'hidden', background: 'var(--sidebar)' }}>
      {/* the unpainted photo, desaturated, being "painted" */}
      {baseImage && (
        <img src={baseImage} alt="" style={{
          position: 'absolute', inset: 0, width: '100%', height: '100%',
          objectFit: 'cover', filter: 'grayscale(1) brightness(0.5) contrast(1.05)',
        }} />
      )}
      {/* amber paint sweep wiping down the plate */}
      <div style={{
        position: 'absolute', left: 0, right: 0, height: '60%',
        background: 'linear-gradient(180deg, transparent, rgba(242,140,56,0.35) 50%, transparent)',
        animation: 'pm-sweep 2.4s ease-in-out infinite',
      }} />
      {/* legibility scrim */}
      <div style={{ position: 'absolute', inset: 0, background: 'linear-gradient(180deg, rgba(28,26,23,0.55), rgba(28,26,23,0.30) 40%, rgba(28,26,23,0.75))' }} />

      <div style={{ position: 'absolute', inset: 0, display: 'flex', flexDirection: 'column', padding: 18, color: 'var(--sidebar-ink)' }}>
        {/* top row: label + elapsed */}
        <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
          <span className="mono" style={{ display: 'inline-flex', alignItems: 'center', gap: 6, fontSize: 9.5, letterSpacing: '0.14em', color: 'var(--brand-bright)' }}>
            <span style={{ width: 6, height: 6, borderRadius: '50%', background: 'var(--brand-bright)', flex: 'none' }} />PAINTING
          </span>
          <span className="mono" style={{ fontSize: 10, color: 'rgba(246,244,239,0.7)' }}>{mm}:{ss}</span>
        </div>

        {/* center: palette dabs + current stage */}
        <div style={{ flex: 1, display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', gap: 14, textAlign: 'center' }}>
          {palette.length > 0 && (
            <div style={{ display: 'flex', gap: 6 }}>
              {palette.map((hex, i) => (
                <span key={i} style={{
                  width: 18, height: 18, borderRadius: '50%', background: hex,
                  boxShadow: '0 0 0 1px rgba(0,0,0,0.25), 0 2px 6px rgba(0,0,0,0.4)',
                  animation: `pm-dab 500ms cubic-bezier(.2,.7,.2,1) both`, animationDelay: `${i * 120}ms`,
                }} />
              ))}
            </div>
          )}
          <div key={stage} style={{ fontFamily: 'var(--serif)', fontStyle: 'italic', fontSize: 21, animation: 'pm-status-in 400ms ease-out' }}>
            {PM_LOADING_STAGES[stage]}…
          </div>
          {/* staged progress bar */}
          <div style={{ width: 'min(220px, 78%)' }}>
            <div style={{ height: 5, borderRadius: 100, background: 'rgba(246,244,239,0.18)', overflow: 'hidden' }}>
              <div style={{ height: '100%', width: `${pct}%`, borderRadius: 100, background: 'var(--brand)', transition: 'width 900ms cubic-bezier(.4,0,.2,1)' }} />
            </div>
            <div style={{ display: 'flex', justifyContent: 'space-between', marginTop: 7 }}>
              {PM_LOADING_STAGES.map((_, i) => (
                <span key={i} style={{
                  width: 6, height: 6, borderRadius: '50%',
                  background: i <= stage ? 'var(--brand)' : 'rgba(246,244,239,0.25)',
                  animation: i === stage ? 'pm-pulse 1.2s ease-in-out infinite' : 'none',
                }} />
              ))}
            </div>
          </div>
        </div>

        {/* bottom: rotating painter's tip */}
        <div style={{
          background: 'rgba(246,244,239,0.10)', border: '1px solid rgba(246,244,239,0.16)',
          borderRadius: 10, padding: '10px 12px', backdropFilter: 'blur(4px)',
        }}>
          <div className="mono" style={{ fontSize: 8, letterSpacing: '0.14em', color: 'var(--brand-bright)', marginBottom: 4 }}>PAINTER’S TIP</div>
          <div key={tip} style={{ fontSize: 12, lineHeight: 1.45, color: 'rgba(246,244,239,0.92)', animation: 'pm-tip-in 450ms ease-out' }}>
            {PM_PAINTING_TIPS[tip]}
          </div>
        </div>
      </div>
    </div>
  );
}

// Resolve a paint from either a hex value (palette) or a paint name (steps).
function paintFor(value) {
  if (!value || typeof value !== 'string') return null;
  const v = value.trim().toLowerCase();
  return PAINTS.find(p => p.hex.toLowerCase() === v)
      || PAINTS.find(p => p.name.toLowerCase() === v)
      || PAINTS.find(p => p.name.toLowerCase().includes(v) || v.includes(p.name.toLowerCase()))
      || null;
}

const isHexColor = (c) => /^#?[0-9a-fA-F]{6}$/.test(String(c || '').trim());
const isRealTechnique = (t) => {
  const s = String(t || '').trim().toLowerCase();
  return s && s !== 'null' && s !== 'none' && s !== 'n/a';
};
// ── RIGHT column: step by step ─────────────────────────────────────
function StepsColumn({ steps, scheme, variant, timeStr }) {
  const generating = steps.length === 0;
  return (
    <div className="card" style={{ padding: 0, overflow: 'hidden', height: 'max-content' }}>
      <div style={{
        borderBottom: '2px solid var(--ink)', padding: '14px 22px',
        display: 'flex', justifyContent: 'space-between', alignItems: 'center',
      }}>
        <span style={{ fontSize: 13, fontWeight: 800, letterSpacing: '0.05em', textTransform: 'uppercase' }}>Step by Step</span>
        <span className="mono" style={{ fontSize: 10, color: 'var(--ink-3)' }}>
          {generating ? 'GENERATING…' : `${steps.length} STEPS · ${(timeStr || '').toUpperCase()}`}
        </span>
      </div>

      {generating ? (
        <div>
          {[0, 1, 2, 3, 4].map(i => (
            <div key={i} style={{
              display: 'grid', gridTemplateColumns: '28px 1fr', gap: 14,
              padding: '16px 22px', borderBottom: i === 4 ? 'none' : '1px solid var(--line)',
            }}>
              <div className="pm-shimmer" style={{ width: 28, height: 28, borderRadius: '50%' }} />
              <div style={{ display: 'flex', flexDirection: 'column', gap: 8, paddingTop: 2 }}>
                <div className="pm-shimmer" style={{ height: 12, borderRadius: 4, width: '92%' }} />
                <div className="pm-shimmer" style={{ height: 12, borderRadius: 4, width: '64%' }} />
                <div className="pm-shimmer" style={{ height: 18, borderRadius: 4, width: 80, marginTop: 2 }} />
              </div>
            </div>
          ))}
        </div>
      ) : (
        <div>
          {steps.map((s, i) => (
            <StepRow key={i} step={s} index={s.step || i + 1} last={i === steps.length - 1} />
          ))}
        </div>
      )}
    </div>
  );
}

function StepRow({ step, index, last }) {
  const [done, setDone] = useState(false);
  return (
    <div style={{
      display: 'grid', gridTemplateColumns: '28px 1fr', gap: 14,
      padding: '16px 22px',
      borderBottom: last ? 'none' : '1px solid var(--line)',
      transition: 'background 180ms', opacity: done ? 0.5 : 1,
    }}
      onMouseEnter={(e) => e.currentTarget.style.background = 'var(--surface-2)'}
      onMouseLeave={(e) => e.currentTarget.style.background = 'transparent'}
    >
      <button
        onClick={() => setDone(d => !d)}
        style={{
          width: 28, height: 28, borderRadius: '50%',
          background: done ? 'var(--brand)' : 'var(--sidebar)',
          border: 'none', color: done ? '#242424' : '#fff',
          fontFamily: 'var(--mono)', fontSize: 11, fontWeight: 600,
          display: 'grid', placeItems: 'center', cursor: 'pointer',
          transition: 'all 160ms', alignSelf: 'flex-start',
        }}
      >
        {done ? '✓' : index}
      </button>
      <div>
        <div style={{
          fontSize: 14, lineHeight: 1.5, color: 'var(--ink)',
          textDecoration: done ? 'line-through' : 'none', textDecorationColor: 'var(--ink-4)',
        }}>
          {step.action}
        </div>
        {step.tip && (
          <div style={{
            fontSize: 12.5, color: 'var(--ink-3)', fontStyle: 'italic',
            margin: '8px 0 0', paddingLeft: 10, borderLeft: '2px solid var(--line-2)',
          }}>
            {step.tip}
          </div>
        )}
        <div style={{ display: 'flex', flexWrap: 'wrap', gap: 6, alignItems: 'center', marginTop: 8 }}>
          {isRealTechnique(step.technique) && (
            <span className="mono" style={{
              padding: '3px 9px', borderRadius: 4,
              background: 'var(--brand-soft)', color: '#B3611F',
              fontSize: 9, fontWeight: 600, letterSpacing: '0.06em', textTransform: 'uppercase',
            }}>{step.technique}</span>
          )}
          {(step.colors || [])
            .filter(c => c && String(c).trim().toLowerCase() !== 'null')
            .map((c, i) => {
              const paint = paintFor(c);
              const swatch = paint?.hex || (isHexColor(c) ? (String(c)[0] === '#' ? c : '#' + c) : null);
              const label = paint?.name || (isHexColor(c) ? 'Colour' : c);
              return (
                <span key={i} style={{
                  display: 'inline-flex', alignItems: 'center', gap: 6,
                  padding: '3px 10px 3px 4px', borderRadius: 100,
                  background: 'var(--surface-2)', border: '1px solid var(--line)',
                  fontSize: 11, color: 'var(--ink-2)',
                }}>
                  <span style={{
                    width: 12, height: 12, borderRadius: 3,
                    background: swatch || 'var(--surface-3)',
                    boxShadow: 'inset 0 0 0 1px rgba(0,0,0,0.2)',
                  }} />
                  <span style={{ fontSize: 11 }}>{label}</span>
                </span>
              );
            })}
        </div>
      </div>
    </div>
  );
}

function NotesPanel({ project }) {
  const [note, setNote] = useState('');
  return (
    <div className="card" style={{
      padding: 26,
      display: 'grid', gridTemplateColumns: '1fr 280px', gap: 28, alignItems: 'start',
    }}>
      <div>
        <span className="eyebrow">Painter's Notes</span>
        <h3 className="display" style={{ fontSize: 26, margin: '8px 0 16px' }}><em>Mark what you change</em></h3>
        <textarea
          value={note}
          onChange={e => setNote(e.target.value)}
          placeholder="The shoulder pad came out richer with two thin coats of Wazdakka over Mephiston. Next time, glaze Hull Red in the recesses…"
          style={{
            width: '100%', minHeight: 120,
            background: 'var(--surface-2)', border: '1px solid var(--line-2)', borderRadius: 10,
            padding: 16, color: 'var(--ink)', fontSize: 14, lineHeight: 1.6,
            fontFamily: 'var(--serif)', fontStyle: 'italic', resize: 'vertical', outline: 'none',
          }}
        />
        <div style={{ marginTop: 14, color: 'var(--ink-3)', fontSize: 12 }}>
          Notes are local to this session — full persistence coming soon.
        </div>
      </div>

      <div style={{ background: 'var(--bg-2)', border: '1px solid var(--line)', borderRadius: 12, padding: 18 }}>
        <span className="mono" style={{ fontSize: 10, letterSpacing: '0.14em', color: 'var(--ink-3)' }}>HISTORY</span>
        <div style={{ display: 'flex', flexDirection: 'column', gap: 12, marginTop: 12 }}>
          {[
            ['Started', new Date(project.created_at).toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' })],
            ['Beginner scheme', project.beginner_scheme ? 'Ready' : '—'],
            ['Advanced scheme', project.advanced_scheme ? 'Ready' : '—'],
            ['Painted plate', (project.generated_beginner_image || project.generated_advanced_image) ? 'Rendered' : '—'],
          ].map(([k, v]) => (
            <div key={k} style={{ display: 'flex', justifyContent: 'space-between', fontSize: 12 }}>
              <span style={{ color: 'var(--ink-3)' }}>{k}</span>
              <span style={{ color: 'var(--ink-2)' }}>{v}</span>
            </div>
          ))}
        </div>
      </div>
    </div>
  );
}

// ── Modal shell — centered dialog with backdrop + ESC close ──────────
function Modal({ children, onClose, width = 460 }) {
  useEffect(() => {
    function onKey(e) { if (e.key === 'Escape') onClose && onClose(); }
    document.addEventListener('keydown', onKey);
    document.body.style.overflow = 'hidden';
    return () => { document.removeEventListener('keydown', onKey); document.body.style.overflow = ''; };
  }, [onClose]);
  return (
    <div
      onClick={onClose}
      style={{
        position: 'fixed', inset: 0, zIndex: 200,
        background: 'rgba(28,26,23,0.45)', backdropFilter: 'blur(3px)',
        display: 'grid', placeItems: 'center', padding: 20,
        animation: 'pm-fade-in 160ms ease both',
      }}>
      <div
        onClick={(e) => e.stopPropagation()}
        style={{
          width: '100%', maxWidth: width,
          background: 'var(--surface)', color: 'var(--ink)',
          border: '1px solid var(--line)', borderRadius: 16,
          boxShadow: '0 40px 80px -24px rgba(28,26,23,0.5)',
          overflow: 'hidden',
          animation: 'pm-modal-in 200ms cubic-bezier(.2,.7,.2,1) both',
        }}>
        {children}
      </div>
      <style>{`
        @keyframes pm-modal-in { from { opacity: 0; transform: translateY(12px) scale(.98); } to { opacity: 1; transform: none; } }
      `}</style>
    </div>
  );
}

// ── Share dialog — copy link, native share, download, socials ────────
function ShareModal({ onClose, title, shareUrl, imageUrl }) {
  const [copied, setCopied] = useState(false);
  const canNativeShare = typeof navigator !== 'undefined' && !!navigator.share;
  const shareText = `Check out my painted miniature — ${title}`;

  async function copy() {
    try {
      await navigator.clipboard.writeText(shareUrl);
      setCopied(true);
      setTimeout(() => setCopied(false), 1800);
    } catch {
      window.prompt('Copy this link:', shareUrl);
    }
  }

  async function nativeShare() {
    try { await navigator.share({ title, text: shareText, url: shareUrl }); }
    catch { /* user cancelled */ }
  }

  const enc = encodeURIComponent;
  const socials = [
    { label: 'X', href: `https://twitter.com/intent/tweet?text=${enc(shareText)}&url=${enc(shareUrl)}` },
    { label: 'Facebook', href: `https://www.facebook.com/sharer/sharer.php?u=${enc(shareUrl)}` },
    { label: 'WhatsApp', href: `https://wa.me/?text=${enc(shareText + ' ' + shareUrl)}` },
    { label: 'Reddit', href: `https://www.reddit.com/submit?url=${enc(shareUrl)}&title=${enc(title)}` },
    { label: 'Email', href: `mailto:?subject=${enc(title)}&body=${enc(shareText + '\n\n' + shareUrl)}` },
  ];

  return (
    <Modal onClose={onClose} width={440}>
      <div style={{ padding: '24px 26px 22px' }}>
        <div style={{ display: 'flex', alignItems: 'center', gap: 12, marginBottom: 16 }}>
          {imageUrl ? (
            <img src={imageUrl} alt="" style={{ width: 52, height: 52, borderRadius: 10, objectFit: 'cover', border: '1px solid var(--line)' }} />
          ) : (
            <div style={{ width: 44, height: 44, borderRadius: 12, background: 'var(--brand-soft)', border: '1px solid var(--brand)', display: 'grid', placeItems: 'center', color: 'var(--brand-deep)', fontSize: 20 }}>↗</div>
          )}
          <div style={{ minWidth: 0 }}>
            <h2 className="display" style={{ fontSize: 24, lineHeight: 1 }}>Share <em>{title}</em></h2>
            <div style={{ fontSize: 12, color: 'var(--ink-3)', marginTop: 3 }}>
              A public page — anyone with this link sees the painted plate + full guide. No sign-in needed.
            </div>
          </div>
        </div>

        <div style={{ display: 'flex', gap: 8, marginBottom: 14 }}>
          <input readOnly value={shareUrl} onFocus={e => e.target.select()} className="input"
            style={{ flex: 1, height: 44, fontSize: 12.5, fontFamily: 'var(--mono)' }} />
          <button className="btn btn-gold" style={{ height: 44 }} onClick={copy}>{copied ? '✓ Copied' : 'Copy'}</button>
        </div>

        <div style={{ display: 'flex', gap: 8, marginBottom: 18, flexWrap: 'wrap' }}>
          {canNativeShare && (
            <button className="btn btn-ghost btn-sm" onClick={nativeShare}>↗ Share…</button>
          )}
          {imageUrl && (
            <a className="btn btn-ghost btn-sm" href={imageUrl} target="_blank" rel="noopener noreferrer" download>⤓ Open image</a>
          )}
        </div>

        <div className="mono" style={{ fontSize: 9.5, letterSpacing: '0.12em', color: 'var(--ink-3)', marginBottom: 10 }}>SHARE TO</div>
        <div style={{ display: 'flex', gap: 8, flexWrap: 'wrap' }}>
          {socials.map(s => (
            <a key={s.label} href={s.href} target="_blank" rel="noopener noreferrer"
              style={{
                flex: '1 1 auto', textAlign: 'center', padding: '9px 12px', borderRadius: 8,
                border: '1px solid var(--line-2)', background: 'var(--surface-2)',
                fontSize: 12, fontWeight: 600, color: 'var(--ink-2)',
              }}>{s.label}</a>
          ))}
        </div>
      </div>
    </Modal>
  );
}

Object.assign(window, { Result });
