// ── Generator — upload → pick paints (collections/brand) → complexity → render ──
// Wired: real upload → /api/upload, scheme + painted image + steps generation.
// Paint source can be a user's saved Collection (preselects paints) or the full
// catalogue filtered by brand/family.

function Editor({ navigate, user, credits, onSignOut, onProjectReady, seedBaseUrl }) {
  const [file, setFile] = useState(null);       // local File for preview
  const [upload, setUpload] = useState(null);   // { projectId, imageUrl }
  const [uploading, setUploading] = useState(false);
  const [dragging, setDragging] = useState(false);
  const [selected, setSelected] = useState([]);
  const [source, setSource] = useState('collection'); // 'collection' | 'brand'
  const [collections, setCollections] = useState([]);
  const [activeCollectionId, setActiveCollectionId] = useState(null);
  const [family, setFamily] = useState('all');
  const [brand, setBrand] = useState('all');
  const [query, setQuery] = useState('');
  const [title, setTitle] = useState('Untitled miniature');
  const [complexity, setComplexity] = useState('intermediate');
  const [generating, setGenerating] = useState(false);
  const [error, setError] = useState(null);

  // Load the user's saved collections.
  useEffect(() => {
    let cancelled = false;
    Api.listCollections()
      .then(r => {
        if (cancelled) return;
        const list = r.collections || [];
        setCollections(list);
        if (!list.length) setSource('brand');
      })
      .catch(() => { if (!cancelled) setSource('brand'); });
    return () => { cancelled = true; };
  }, []);

  // Re-use an existing base image if navigated here with one.
  useEffect(() => {
    if (!seedBaseUrl) return;
    let cancelled = false;
    setUploading(true);
    setFile({ name: 'base.jpg', size: 0, url: seedBaseUrl });
    Api.newFromBase(seedBaseUrl)
      .then(resp => { if (!cancelled) setUpload({ projectId: resp.projectId, imageUrl: resp.imageUrl }); })
      .catch(err => { if (!cancelled) setError(err.message); })
      .finally(() => { if (!cancelled) setUploading(false); });
    return () => { cancelled = true; };
  }, [seedBaseUrl]);

  const filtered = useMemo(() => {
    let p = PAINTS;
    if (family !== 'all') p = p.filter(x => x.family === family);
    if (brand !== 'all') p = p.filter(x => x.brand === brand);
    if (query) {
      const q = query.toLowerCase();
      p = p.filter(x =>
        x.name.toLowerCase().includes(q) ||
        x.code.toLowerCase().includes(q) ||
        x.brand.toLowerCase().includes(q)
      );
    }
    return p;
  }, [family, brand, query]);

  const selectedPaints = selected.map(id => PAINTS.find(p => p.id === id)).filter(Boolean);
  const complexityMin = { beginner: 3, intermediate: 6, expert: 10 }[complexity];
  const complexityMax = { beginner: 5, intermediate: 9, expert: 15 }[complexity];
  const canGenerate = upload && selectedPaints.length >= 1 && !generating;
  const difficulty = complexity === 'beginner' ? 'beginner' : 'advanced';

  // Suggest a complexity tier from how many paints were chosen.
  const suggested = selectedPaints.length === 0 ? null
    : selectedPaints.length <= 5 ? 'beginner'
    : selectedPaints.length <= 9 ? 'intermediate'
    : 'expert';

  // Selecting a paint is cumulative and never resets the collection/brand
  // filter — you build one selection while browsing across sources.
  function toggle(id) {
    setSelected(prev => prev.includes(id) ? prev.filter(x => x !== id) : [...prev, id]);
  }

  async function saveAsCollection() {
    if (!selectedPaints.length) return;
    const name = window.prompt('Name this collection', 'My collection');
    if (!name || !name.trim()) return;
    try {
      const created = await Api.createCollection(name.trim(), selected);
      setCollections(prev => [created, ...prev.filter(c => c.id !== created.id)]);
      setActiveCollectionId(created.id);
    } catch (err) {
      setError(err.message || 'Could not save collection');
    }
  }

  async function handleFile(f) {
    if (!f) { setFile(null); setUpload(null); return; }
    setError(null);
    const url = URL.createObjectURL(f);
    setFile({ name: f.name, size: f.size, url });
    setUploading(true);
    try {
      const resp = await Api.uploadImage(f);
      setUpload({ projectId: resp.projectId, imageUrl: resp.imageUrl });
    } catch (err) {
      setError(`Upload failed: ${err.message}`);
      setUpload(null);
    } finally {
      setUploading(false);
    }
  }

  async function startGenerate() {
    if (!upload) { setError('Image not yet uploaded.'); return; }
    setError(null);
    setGenerating(true);
    try {
      const paints = selectedPaints.map(p => ({ name: p.name, hex_color: p.hex }));
      const resp = await Api.generateScheme({
        projectId: upload.projectId,
        imageUrl: upload.imageUrl,
        paints,
        difficulty,
        complexity,
      });
      const scheme = resp.scheme;
      if (!scheme) throw new Error('Scheme generation returned no payload');

      // Fire-and-forget the heavy renders — the Result page polls for them.
      Api.generatePaintedImage({
        projectId: upload.projectId,
        imageUrl: upload.imageUrl,
        scheme,
        difficulty,
      }).catch(err => {
        console.error('painted image failed:', err);
        if (err.status === 402) navigate('credits');
      });

      // Pass the chosen paints so the guide references paint NAMES, not hex.
      Api.generateSteps({
        projectId: upload.projectId,
        scheme,
        difficulty,
        paints,
        complexity,
      }).catch(err => console.error('steps failed:', err));

      onProjectReady && onProjectReady();
      navigate('result', { projectId: upload.projectId, difficulty });
    } catch (err) {
      setError(err.message || 'Generation failed');
      setGenerating(false);
    }
  }

  const complexityLabel = { beginner: 'Beginner', intermediate: 'Intermediate', expert: 'Expert' }[complexity];
  const checklist = [
    { label: 'Upload photo', done: !!upload, note: uploading ? 'Uploading…' : upload ? 'Ready' : 'Drop a photo' },
    { label: 'Pick paints',  done: selectedPaints.length > 0, note: selectedPaints.length ? `${selectedPaints.length} chosen` : 'None yet' },
    { label: 'Complexity',   done: true, note: complexityLabel },
    { label: 'Render',       done: false, ready: canGenerate, note: canGenerate ? 'Ready · 1 credit' : 'Not ready' },
  ];

  return (
    <div data-screen-label="editor" className="pm-shell">
      {/* Step sidebar */}
      <aside className="pm-sidebar">
        <a href="#" onClick={(e) => { e.preventDefault(); navigate('studio'); }} className="pm-brand" style={{ marginBottom: 24 }}>
          <img className="pm-logo pm-logo--dark" src="/assets/logo-new.png" alt="PaintMini" style={{ height: 30 }} />
        </a>
        <div className="pm-eyebrow" style={{ color: 'var(--sidebar-ink-3)', marginBottom: 14 }}>Progress</div>
        <div style={{ display: 'flex', flexDirection: 'column', gap: 14, fontSize: 12 }}>
          {checklist.map((s, i) => {
            const state = s.done ? 'done' : s.ready ? 'ready' : 'todo';
            return (
              <div key={i} style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
                <span style={{
                  width: 22, height: 22, borderRadius: '50%',
                  background: state === 'done' ? 'var(--brand)' : 'transparent',
                  border: state === 'todo' ? '1px solid var(--sidebar-line)' : `1.5px solid var(--brand)`,
                  color: state === 'done' ? '#242424' : 'var(--brand-bright)',
                  fontFamily: 'var(--mono)', fontSize: 11, fontWeight: 600,
                  display: 'grid', placeItems: 'center', flex: 'none',
                  boxShadow: state === 'ready' ? '0 0 0 3px rgba(242,140,56,0.18)' : 'none',
                }}>{state === 'done' ? '✓' : state === 'ready' ? '→' : ''}</span>
                <span style={{ minWidth: 0 }}>
                  <span style={{ display: 'block', color: state === 'todo' ? 'var(--sidebar-ink-3)' : 'var(--sidebar-ink)', fontWeight: state === 'ready' ? 600 : 400 }}>{s.label}</span>
                  <span style={{ display: 'block', fontFamily: 'var(--mono)', fontSize: 9, letterSpacing: '0.04em', color: state === 'ready' ? 'var(--brand-bright)' : 'var(--sidebar-ink-3)' }}>{s.note}</span>
                </span>
              </div>
            );
          })}
        </div>
        <div className="pm-side-spacer"></div>
        <button className="pm-credit-pill" onClick={() => navigate('credits')} style={{ width: '100%' }}>
          <span style={{ display: 'inline-flex', alignItems: 'center', gap: 6 }}>
            <span style={{ width: 6, height: 6, borderRadius: '50%', background: 'var(--brand)', flex: 'none' }} />
            {typeof credits === 'number' ? credits : '—'} CREDITS
          </span>
          <span className="pm-credit-add">+</span>
        </button>
        {upload && (
          <div className="mono" style={{ fontSize: 9, color: 'var(--sidebar-ink-3)' }}>
            DESIGN-{upload.projectId.slice(0, 8).toUpperCase()}
          </div>
        )}
      </aside>
      <PMMobileBar navigate={navigate} active={null} credits={credits} onSignOut={onSignOut} />

      {/* Content: stage + rail */}
      <div className="pm-content pm-split" style={{ overflow: 'hidden', ['--split']: '1.05fr 1fr' }}>
        <StagePanel
          file={file} onFile={handleFile}
          uploading={uploading} dragging={dragging} setDragging={setDragging}
          title={title} setTitle={setTitle}
          selectedPaints={selectedPaints}
        />
        <RailPanel
          source={source} setSource={setSource}
          collections={collections} activeCollectionId={activeCollectionId}
          setActiveCollectionId={setActiveCollectionId}
          hasCollections={collections.length > 0}
          family={family} setFamily={setFamily}
          brand={brand} setBrand={setBrand}
          query={query} setQuery={setQuery}
          filtered={filtered}
          selected={selected} toggle={toggle}
          selectedPaints={selectedPaints}
          saveAsCollection={saveAsCollection}
          complexity={complexity} setComplexity={setComplexity}
          suggested={suggested}
          credits={credits}
          canGenerate={canGenerate} generating={generating}
          error={error}
          onGenerate={startGenerate}
        />
      </div>
    </div>
  );
}

// ── Left: photo stage + working title + scheme plan ─────────────────
function StagePanel({ file, onFile, uploading, dragging, setDragging, title, setTitle, selectedPaints }) {
  const inputRef = useRef(null);
  function onDrop(e) {
    e.preventDefault(); setDragging(false);
    const f = e.dataTransfer.files?.[0];
    if (f && f.type.startsWith('image/')) onFile(f);
  }
  return (
    <div style={{ padding: 22, borderRight: '1px solid var(--line)', display: 'flex', flexDirection: 'column', background: 'var(--bg-2)' }}>
      <div className="pm-section-head" style={{ marginBottom: 12 }}>
        <span className="mono" style={{ fontSize: 10, color: 'var(--brand-deep)', fontWeight: 600 }}>01</span>
        <h2 style={{ fontSize: 12.5, fontWeight: 800, letterSpacing: '0.05em', textTransform: 'uppercase', margin: 0 }}>Your Photo</h2>
      </div>

      {!file ? (
        <div
          onDrop={onDrop}
          onDragOver={(e) => { e.preventDefault(); setDragging(true); }}
          onDragLeave={() => setDragging(false)}
          onClick={() => inputRef.current?.click()}
          style={{
            border: `2px dashed ${dragging ? 'var(--brand)' : 'var(--line-2)'}`,
            background: dragging ? 'var(--brand-soft)' : 'var(--surface)',
            borderRadius: 10, padding: '36px 20px',
            aspectRatio: '4 / 3',
            display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center',
            gap: 12, cursor: 'pointer', textAlign: 'center',
            transition: 'border-color 160ms, background 160ms',
          }}>
          <input ref={inputRef} type="file" accept="image/*" style={{ display: 'none' }}
            onChange={(e) => onFile(e.target.files?.[0])} />
          <div style={{
            width: 52, height: 52, borderRadius: '50%',
            background: 'var(--brand-soft)', border: '1px solid var(--brand)',
            display: 'grid', placeItems: 'center', color: 'var(--brand-deep)',
          }}>
            <svg width="22" height="22" viewBox="0 0 22 22" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round">
              <path d="M11 2 V 14 M5 8 L 11 2 L 17 8 M3 18 H 19"/>
            </svg>
          </div>
          <div>
            <div className="display" style={{ fontSize: 22 }}><em>Drop your photo</em></div>
            <div style={{ fontSize: 12, color: 'var(--ink-3)', marginTop: 4 }}>or click to choose · JPG, PNG · up to 20 MB</div>
          </div>
          <div className="mono" style={{ fontSize: 9.5, letterSpacing: '0.12em', color: 'var(--ink-4)' }}>
            PRIMED GREY OR BLACK WORKS BEST
          </div>
        </div>
      ) : (
        <div style={{ position: 'relative', border: '2px dashed var(--line-2)', borderRadius: 10, padding: 8, background: 'var(--surface)' }}>
          <div style={{ position: 'relative', borderRadius: 6, overflow: 'hidden', aspectRatio: '4 / 3', background: 'var(--surface-3)' }}>
            <img src={file.url} alt="" style={{ width: '100%', height: '100%', objectFit: 'contain', display: 'block' }}/>
            <div style={{ position: 'absolute', inset: 0, background: 'linear-gradient(180deg, rgba(28,26,23,.22), transparent 45%, rgba(28,26,23,.45))' }}/>
            <span style={{
              position: 'absolute', top: 10, left: 10,
              fontFamily: 'var(--mono)', fontSize: 8.5, fontWeight: 600, letterSpacing: '0.1em',
              padding: '4px 8px', borderRadius: 5, background: 'rgba(28,26,23,.72)', color: '#fff',
            }}>{uploading ? 'UPLOADING…' : 'UNPAINTED · AWAITING RENDER'}</span>
            <button onClick={() => onFile(null)} style={{
              position: 'absolute', top: 9, right: 9, height: 26, padding: '0 11px',
              display: 'grid', placeItems: 'center', borderRadius: 5,
              background: 'rgba(255,255,255,.92)', color: '#242424', fontSize: 10.5, fontWeight: 600,
            }}>⤒ Replace</button>
          </div>
          <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', padding: '8px 6px 4px', fontFamily: 'var(--mono)', fontSize: 9, letterSpacing: '0.06em', color: 'var(--ink-3)' }}>
            <span style={{ maxWidth: '62%', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>✓ {file.name.toUpperCase()}{file.size ? ` · ${(file.size / 1024 / 1024).toFixed(1)} MB` : ''}</span>
            <span>DRAG TO REPLACE</span>
          </div>
        </div>
      )}

      <div style={{ marginTop: 14 }}>
        <div className="mono" style={{ fontSize: 9, letterSpacing: '0.12em', textTransform: 'uppercase', color: 'var(--ink-3)', marginBottom: 6 }}>Working title</div>
        <input className="input" value={title} onChange={e => setTitle(e.target.value)} style={{ height: 38, fontSize: 14 }}/>
      </div>
    </div>
  );
}

// ── Right: pick paints (collection/brand) + complexity + render ─────
function RailPanel(props) {
  const {
    source, setSource, collections, activeCollectionId, setActiveCollectionId, hasCollections,
    family, setFamily, brand, setBrand, query, setQuery, filtered,
    selected, toggle, selectedPaints, saveAsCollection,
    complexity, setComplexity, suggested, credits,
    canGenerate, generating, error, onGenerate,
  } = props;

  // The pickable list depends on the source: a chosen collection's paints, the
  // union of all collections, or the brand/family/search-filtered catalogue.
  const activeCollection = collections.find(c => c.id === activeCollectionId) || null;
  let gridPaints, gridCaption;
  if (source === 'brand') {
    gridPaints = filtered;
    gridCaption = null;
  } else if (activeCollection) {
    gridPaints = (activeCollection.paintIds || []).map(id => PAINTS.find(p => p.id === id)).filter(Boolean);
    gridCaption = activeCollection.name;
  } else {
    const union = [...new Set(collections.flatMap(c => c.paintIds || []))];
    gridPaints = union.map(id => PAINTS.find(p => p.id === id)).filter(Boolean);
    gridCaption = 'All collections';
  }

  const complexityOptions = [
    { id: 'beginner',     label: 'Beginner',     range: '3–5',   detail: 'Flat colour + one wash', dots: 1 },
    { id: 'intermediate', label: 'Intermediate', range: '6–9',   detail: 'Layering + edge highlights + wash', dots: 2 },
    { id: 'expert',       label: 'Expert',       range: '10–15', detail: 'NMM, OSL, wet-blend, freehand', dots: 3 },
  ];
  const suggestNote = {
    beginner: 'a few paints usually means flat colour and a wash.',
    intermediate: 'this many paints usually means layering + edge highlights.',
    expert: 'a large palette usually means NMM, OSL and blending.',
  };

  return (
    <div style={{ display: 'flex', flexDirection: 'column', overflow: 'hidden' }}>
      <div style={{ padding: '18px 20px 14px', overflowY: 'auto', flex: 1 }}>
        {/* STEP 2 */}
        <div className="pm-section-head" style={{ marginBottom: 10 }}>
          <span className="mono" style={{ fontSize: 10, color: 'var(--brand-deep)', fontWeight: 600 }}>02</span>
          <h2 style={{ fontSize: 12.5, fontWeight: 800, letterSpacing: '0.05em', textTransform: 'uppercase', margin: 0 }}>Pick Paints</h2>
        </div>

        {/* source toggle */}
        <div style={{ display: 'flex', background: 'var(--surface-2)', borderRadius: 8, padding: 3, marginBottom: 11 }}>
          {[
            { id: 'collection', label: 'My collection' },
            { id: 'brand', label: 'By brand' },
          ].map(t => {
            const on = source === t.id;
            return (
              <button key={t.id} onClick={() => setSource(t.id)} style={{
                flex: 1, textAlign: 'center', padding: 7, borderRadius: 6,
                background: on ? 'var(--surface)' : 'transparent',
                boxShadow: on ? '0 1px 2px rgba(0,0,0,.08)' : 'none',
                fontSize: 11.5, fontWeight: on ? 600 : 400,
                color: on ? 'var(--ink)' : 'var(--ink-2)',
              }}>{t.label}</button>
            );
          })}
        </div>

        {source === 'collection' ? (
          hasCollections ? (
            <div style={{ display: 'flex', gap: 5, flexWrap: 'wrap', marginBottom: 11 }}>
              <button onClick={() => setActiveCollectionId(null)} style={{
                padding: '5px 11px', borderRadius: 100, whiteSpace: 'nowrap',
                background: !activeCollectionId ? 'var(--brand-soft)' : 'transparent',
                color: !activeCollectionId ? 'var(--brand-deep)' : 'var(--ink-3)',
                border: `1px solid ${!activeCollectionId ? 'var(--brand)' : 'var(--line)'}`,
                fontSize: 10.5, fontFamily: 'var(--mono)', letterSpacing: '0.05em',
              }}>ALL</button>
              {collections.map(c => {
                const on = activeCollectionId === c.id;
                return (
                  <button key={c.id} onClick={() => setActiveCollectionId(c.id)} style={{
                    padding: '5px 11px', borderRadius: 100, whiteSpace: 'nowrap',
                    background: on ? 'var(--brand)' : 'var(--surface)',
                    color: on ? '#242424' : 'var(--ink)',
                    border: `1px solid ${on ? 'var(--brand)' : 'var(--line-2)'}`,
                    fontSize: 11, fontWeight: on ? 600 : 400,
                  }}>{c.name} · {c.paintIds?.length || 0}</button>
                );
              })}
            </div>
          ) : (
            <div style={{ padding: '12px 14px', borderRadius: 8, background: 'var(--surface-2)', border: '1px dashed var(--line-2)', fontSize: 11.5, color: 'var(--ink-3)', lineHeight: 1.5, marginBottom: 11 }}>
              No collections yet. Switch to <strong style={{ color: 'var(--ink-2)' }}>By brand</strong>, pick paints, then <strong style={{ color: 'var(--ink-2)' }}>Save as collection</strong> — or build one on the <strong style={{ color: 'var(--ink-2)' }}>Paints</strong> page.
            </div>
          )
        ) : (
          <>
            <div style={{ position: 'relative', marginBottom: 9 }}>
              <input className="input" placeholder="Search name, code, or brand" value={query}
                onChange={e => setQuery(e.target.value)} style={{ height: 38, paddingLeft: 12, fontSize: 13 }}/>
            </div>
            <div style={{ display: 'flex', gap: 5, marginBottom: 8, overflowX: 'auto', paddingBottom: 3 }}>
              {[
                { id: 'all', label: 'All' },
                { id: 'Citadel', label: 'Citadel' },
                { id: 'Vallejo', label: 'Vallejo' },
                { id: 'Scale75', label: 'Scale75' },
                { id: 'Custom', label: 'Custom' },
              ].map(b => (
                <button key={b.id} onClick={() => setBrand(b.id)} style={{
                  padding: '5px 11px', borderRadius: 100, whiteSpace: 'nowrap',
                  background: brand === b.id ? 'var(--brand-soft)' : 'transparent',
                  color: brand === b.id ? 'var(--brand-deep)' : 'var(--ink-3)',
                  border: `1px solid ${brand === b.id ? 'var(--brand)' : 'var(--line)'}`,
                  fontSize: 10.5, fontFamily: 'var(--mono)', letterSpacing: '0.05em',
                }}>{b.label}</button>
              ))}
            </div>
            <div style={{ display: 'flex', gap: 5, marginBottom: 11, overflowX: 'auto', paddingBottom: 3 }}>
              {FAMILIES.map(f => (
                <button key={f.id} onClick={() => setFamily(f.id)} style={{
                  padding: '5px 11px', borderRadius: 100, whiteSpace: 'nowrap',
                  background: family === f.id ? 'var(--ink)' : 'var(--surface-2)',
                  color: family === f.id ? 'var(--paper)' : 'var(--ink-2)',
                  border: '1px solid var(--line-2)', fontSize: 11,
                }}>{f.label}</button>
              ))}
            </div>
          </>
        )}

        {/* Pickable list — select colours that belong to the chosen source */}
        {(source === 'brand' || hasCollections) && (
          <>
            {gridCaption && (
              <div className="mono" style={{ fontSize: 9, letterSpacing: '0.1em', color: 'var(--ink-4)', margin: '0 0 6px' }}>
                {gridCaption.toUpperCase()} · {gridPaints.length} COLOUR{gridPaints.length === 1 ? '' : 'S'}
              </div>
            )}
            <div style={{ maxHeight: 220, overflowY: 'auto', display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 6, marginBottom: 16 }}>
              {gridPaints.map(p => {
                const on = selected.includes(p.id);
                return (
                  <button key={p.id} onClick={() => toggle(p.id)} style={{
                    display: 'flex', alignItems: 'center', gap: 8, padding: '7px 9px', borderRadius: 6,
                    border: `1.5px solid ${on ? 'var(--brand)' : 'var(--line)'}`,
                    background: on ? 'var(--brand-soft)' : 'var(--surface)', textAlign: 'left', minWidth: 0,
                  }}>
                    <span style={{ width: 22, height: 22, borderRadius: 4, background: p.hex, flexShrink: 0, boxShadow: 'inset 0 0 0 1px rgba(0,0,0,.12)' }}/>
                    <span style={{ minWidth: 0, flex: 1 }}>
                      <div style={{ fontSize: 11, fontWeight: 500, whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>{p.name}</div>
                      <div className="mono" style={{ fontSize: 8, color: 'var(--ink-4)' }}>{p.code}</div>
                    </span>
                    {on && <span style={{ color: 'var(--brand-deep)', fontSize: 12, fontWeight: 700 }}>✓</span>}
                  </button>
                );
              })}
              {gridPaints.length === 0 && (
                <div style={{ gridColumn: '1/-1', padding: 20, textAlign: 'center', color: 'var(--ink-3)', fontSize: 12 }}>
                  {source === 'collection' ? 'This collection has no paints yet.' : 'No paints match — try another filter.'}
                </div>
              )}
            </div>
          </>
        )}

        {/* SELECTED COLOURS — persists across collection/brand switches */}
        <div style={{ border: '1px solid var(--line)', borderRadius: 10, background: 'var(--surface-2)', padding: '12px 13px', marginBottom: 16 }}>
          <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: selectedPaints.length ? 9 : 0 }}>
            <span className="mono" style={{ fontSize: 9.5, letterSpacing: '0.1em', color: 'var(--ink-3)' }}>
              SELECTED COLOURS · {selectedPaints.length}
            </span>
            <span style={{ display: 'flex', gap: 10 }}>
              {selectedPaints.length > 0 && (
                <button onClick={saveAsCollection} style={{ fontSize: 10.5, color: 'var(--brand-deep)', fontWeight: 600 }}>＋ Save as collection</button>
              )}
              {selectedPaints.length > 0 && (
                <button onClick={() => selectedPaints.forEach(p => toggle(p.id))} style={{ fontSize: 10.5, color: 'var(--ink-3)' }}>Clear</button>
              )}
            </span>
          </div>
          {selectedPaints.length === 0 ? (
            <div style={{ fontSize: 11.5, color: 'var(--ink-4)' }}>Tap colours above to add them. They stay here even if you switch collection or brand.</div>
          ) : (
            <div style={{ display: 'flex', flexWrap: 'wrap', gap: 5 }}>
              {selectedPaints.map(p => (
                <button key={p.id} onClick={() => toggle(p.id)} title={`Remove ${p.name}`} style={{
                  display: 'inline-flex', alignItems: 'center', gap: 6,
                  padding: '4px 8px 4px 4px', background: 'var(--surface)', borderRadius: 100,
                  fontSize: 11, border: '1px solid var(--line-2)',
                }}>
                  <span style={{ width: 15, height: 15, borderRadius: 3, background: p.hex, boxShadow: 'inset 0 0 0 1px rgba(0,0,0,.15)' }}/>
                  {p.name}
                  <span style={{ color: 'var(--ink-4)', fontSize: 13, lineHeight: 1, marginLeft: 1 }}>×</span>
                </button>
              ))}
            </div>
          )}
        </div>

        {/* STEP 3 complexity */}
        <div className="pm-section-head" style={{ marginBottom: 9 }}>
          <span className="mono" style={{ fontSize: 10, color: 'var(--brand-deep)', fontWeight: 600 }}>03</span>
          <h2 style={{ fontSize: 12.5, fontWeight: 800, letterSpacing: '0.05em', textTransform: 'uppercase', margin: 0 }}>Complexity</h2>
        </div>
        {suggested && (
          <div style={{ display: 'flex', alignItems: 'center', gap: 8, padding: '9px 12px', borderRadius: 8, background: 'var(--brand-soft)', border: '1px solid rgba(242,140,56,.5)', marginBottom: 10 }}>
            <span style={{ width: 20, height: 20, borderRadius: '50%', background: 'var(--brand)', color: '#242424', display: 'grid', placeItems: 'center', flex: 'none' }}><PMIcon name="spark" size={12} /></span>
            <span style={{ fontSize: 11.5, lineHeight: 1.35 }}>
              <strong>Suggested: {suggested[0].toUpperCase() + suggested.slice(1)}.</strong>{' '}
              <span style={{ color: 'var(--ink-2)' }}>{selectedPaints.length} paints — {suggestNote[suggested]}</span>
            </span>
          </div>
        )}
        <div style={{ display: 'flex', flexDirection: 'column', gap: 6 }}>
          {complexityOptions.map(o => {
            const on = complexity === o.id;
            const isSuggested = suggested === o.id;
            return (
              <button key={o.id} onClick={() => setComplexity(o.id)} style={{
                display: 'flex', alignItems: 'center', gap: 11, padding: '9px 12px', borderRadius: 8,
                border: on ? '2px solid var(--brand)' : '1px solid var(--line-2)',
                background: 'var(--surface)', textAlign: 'left',
              }}>
                <span style={{ display: 'flex', gap: 2 }}>
                  {[0,1,2].map(i => (
                    <span key={i} style={{ width: 5, height: 5, borderRadius: '50%', background: i < o.dots ? (on ? 'var(--brand)' : 'var(--ink)') : 'rgba(28,26,23,.15)' }}/>
                  ))}
                </span>
                <div style={{ flex: 1 }}>
                  <div style={{ fontSize: 12, fontWeight: on ? 700 : 600 }}>
                    {o.label}
                    {isSuggested && <span className="mono" style={{ fontSize: 8, background: 'var(--brand)', color: '#242424', padding: '2px 6px', borderRadius: 100, letterSpacing: '0.06em', marginLeft: 6 }}>SUGGESTED</span>}
                  </div>
                  <div style={{ fontSize: 10.5, color: 'var(--ink-2)' }}>{o.detail}</div>
                </div>
                <span className="mono" style={{ fontSize: 9, color: 'var(--ink-3)' }}>{o.range}</span>
              </button>
            );
          })}
        </div>

        {error && (
          <div style={{ marginTop: 12, padding: '9px 12px', borderRadius: 8, background: 'rgba(154,27,31,.08)', border: '1px solid rgba(154,27,31,.28)', color: 'var(--crimson)', fontSize: 12 }}>{error}</div>
        )}
      </div>

      {/* footer: render */}
      <div style={{ padding: '13px 20px', borderTop: '1px solid var(--line)', display: 'flex', alignItems: 'center', gap: 12, background: 'var(--surface)' }}>
        <div>
          <div className="mono" style={{ fontSize: 9, color: 'var(--ink-3)' }}>
            {selectedPaints.length} PAINT{selectedPaints.length === 1 ? '' : 'S'} · {complexity.slice(0, 6).toUpperCase()}
          </div>
          <div style={{ fontSize: 11, color: 'var(--ink-2)' }}>
            Uses <strong style={{ color: 'var(--ink)' }}>1 credit</strong>{typeof credits === 'number' ? ` · ${credits} left` : ''}
          </div>
        </div>
        <button
          disabled={!canGenerate}
          onClick={onGenerate}
          className={canGenerate ? 'btn btn-gold' : 'btn btn-ghost'}
          style={{ flex: 1, height: 48, borderRadius: 7, opacity: canGenerate || generating ? 1 : 0.5, cursor: canGenerate ? 'pointer' : 'not-allowed' }}>
          {generating ? <><Spinner/> Rendering…</> : <>Render · 1 credit ↦</>}
        </button>
      </div>
    </div>
  );
}

function Spinner() {
  return (
    <span style={{
      width: 14, height: 14, borderRadius: '50%',
      border: '2px solid rgba(28,26,23,0.25)', borderTopColor: '#242424',
      animation: 'pmspin 0.7s linear infinite', display: 'inline-block',
    }}>
      <style>{`@keyframes pmspin { to { transform: rotate(360deg); } }`}</style>
    </span>
  );
}

Object.assign(window, { Editor });
