// Main app — form on left, live cover preview on right, PNG export
const { useState, useEffect, useRef, useMemo } = React;

function getStudioStorage() {
  try {
    return window.localStorage;
  } catch (error) {
    return null;
  }
}

function addThemesToList(existingThemes, incomingThemes) {
  const existing = existingThemes || [];
  const used = new Set(existing.map(t => t.id));
  const nextThemes = incomingThemes.map((theme, index) => {
    const baseId = theme.id || `custom-theme-${Date.now()}-${index}`;
    let id = baseId;
    let suffix = 2;
    while (used.has(id)) {
      id = `${baseId}-${suffix}`;
      suffix += 1;
    }
    used.add(id);
    return { ...theme, id };
  });
  return [...existing, ...nextThemes];
}

function visibilityToggle(theme, updateShow, key) {
  const hidden = theme.show && theme.show[key] === false;
  return {
    hidden,
    onChange: (nextHidden) => updateShow(key, !nextHidden),
  };
}

function tierVisibilityToggle(tierData, tier, updateTier, key) {
  const hidden = tierData && tierData[key] === false;
  return {
    hidden,
    onChange: (nextHidden) => updateTier(tier, key, !nextHidden),
  };
}

function App() {
  const utils = window.CoverStudioUtils;
  const [studioState, setStudioState] = useState(() => utils.loadStudioState(getStudioStorage(), window.DEFAULTS));
  const activeUserId = studioState.activeUserId;
  const activeUser = studioState.users[activeUserId] || studioState.users[utils.DEFAULT_USER_ID];
  const data = activeUser.data;
  const view = activeUser.view || 'advanced';
  const activeTierKeys = window.tierKeys(data);
  const focusedTier = activeTierKeys.includes(data.focusTier) ? data.focusTier : activeTierKeys[activeTierKeys.length - 1];
  const safeView = view === 'combined' || activeTierKeys.includes(view) ? view : focusedTier;
  const customThemes = activeUser.customThemes || [];
  const [exporting, setExporting] = useState(false);
  const [saveStatus, setSaveStatus] = useState('Saved');
  const [themeImportStatus, setThemeImportStatus] = useState('');
  const [styleTab, setStyleTab] = useState('presets');
  const [mobilePanelOpen, setMobilePanelOpen] = useState(false);
  const [supportModalOpen, setSupportModalOpen] = useState(false);
  const stageRef = useRef(null);

  useEffect(() => {
    const saved = utils.saveStudioState(getStudioStorage(), studioState);
    setSaveStatus(saved ? 'Saved' : 'Not saved');
  }, [studioState, utils]);

  const updateActiveProfile = (updater) => {
    setStudioState(prev => {
      const currentId = prev.users[prev.activeUserId] ? prev.activeUserId : utils.DEFAULT_USER_ID;
      const current = prev.users[currentId] || utils.createUserProfile(currentId, 'Default', window.DEFAULTS);
      const nextProfile = updater(current);
      return {
        ...prev,
        activeUserId: currentId,
        users: { ...prev.users, [currentId]: nextProfile },
      };
    });
  };

  const setData = (updater) => {
    updateActiveProfile(profile => ({
      ...profile,
      data: typeof updater === 'function' ? updater(profile.data) : updater,
    }));
  };

  const setView = (updater) => {
    updateActiveProfile(profile => ({
      ...profile,
      view: typeof updater === 'function' ? updater(profile.view || 'advanced') : updater,
    }));
  };

  const setCustomThemes = (updater) => {
    updateActiveProfile(profile => ({
      ...profile,
      customThemes: typeof updater === 'function' ? updater(profile.customThemes || []) : updater,
    }));
  };

  const addUser = () => {
    const name = window.prompt('Profile name', 'New user');
    if (!name || !name.trim()) return;
    setStudioState(prev => {
      const id = utils.uniqueUserId(name, prev.users);
      return {
        activeUserId: id,
        users: {
          ...prev.users,
          [id]: utils.createUserProfile(id, name.trim(), window.DEFAULTS),
        },
      };
    });
  };

  const removeUser = () => {
    if (activeUserId === utils.DEFAULT_USER_ID) return;
    if (!window.confirm(`Remove "${activeUser.name}" and its autosaved values?`)) return;
    setStudioState(prev => {
      const users = { ...prev.users };
      delete users[prev.activeUserId];
      return { activeUserId: utils.DEFAULT_USER_ID, users };
    });
  };

  const resetActiveUser = () => {
    if (!window.confirm(`Reset "${activeUser.name}" to defaults? Custom saved themes will stay available.`)) return;
    const nextProfile = utils.createUserProfile(activeUserId, activeUser.name, window.DEFAULTS);
    setStudioState(prev => {
      const current = prev.users[activeUserId] || activeUser;
      return {
        ...prev,
        activeUserId,
        users: {
          ...prev.users,
          [activeUserId]: {
            ...nextProfile,
            customThemes: current.customThemes || [],
          },
        },
      };
    });
    setThemeImportStatus('');
    setStyleTab('presets');
  };

  const renameUser = (name) => {
    updateActiveProfile(profile => ({ ...profile, name }));
  };

  const selectUser = (id) => {
    if (!studioState.users[id]) return;
    setStudioState(prev => ({ ...prev, activeUserId: id }));
  };

  const updateTier = (tier, key, val) => {
    setData(d => {
      const tierData = d.tiers[tier] || (window.DEFAULTS.tiers && window.DEFAULTS.tiers[tier]) || {};
      return { ...d, tiers: { ...d.tiers, [tier]: { ...tierData, [key]: val } } };
    });
  };
  const updateTierFeat = (tier, fk, val) => {
    setData(d => ({
      ...d,
      tiers: {
        ...d.tiers,
        [tier]: {
          ...(d.tiers[tier] || (window.DEFAULTS.tiers && window.DEFAULTS.tiers[tier]) || {}),
          features: { ...((d.tiers[tier] && d.tiers[tier].features) || {}), [fk]: val },
        }
      }
    }));
  };
  const updateFeatureLabel = (fk, val) => {
    setData(d => ({ ...d, featureLabels: { ...(d.featureLabels || {}), [fk]: val } }));
  };
  const addFeature = (tier) => {
    setData(d => {
      const labels = window.featureLabels(d);
      const featureOrder = Object.keys(labels);
      let id = 'feature';
      let suffix = featureOrder.length + 1;
      while (labels[id] || featureOrder.includes(id)) {
        id = `feature-${suffix}`;
        suffix += 1;
      }
      const tiers = {};
      window.TIER_KEYS.forEach(tierKey => {
        const tierData = d.tiers[tierKey] || (window.DEFAULTS.tiers && window.DEFAULTS.tiers[tierKey]) || {};
        tiers[tierKey] = {
          ...tierData,
          features: { ...(tierData.features || {}), [id]: tierKey === tier },
        };
      });
      return {
        ...d,
        featureLabels: { ...(d.featureLabels || {}), [id]: `Feature ${featureOrder.length + 1}` },
        featureOrder: [...featureOrder, id],
        tiers,
      };
    });
  };
  const removeFeature = (fk) => {
    setData(d => {
      const featureOrder = Object.keys(window.featureLabels(d)).filter(key => key !== fk);
      const featureLabels = { ...(d.featureLabels || {}) };
      delete featureLabels[fk];
      const tiers = {};
      window.TIER_KEYS.forEach(tierKey => {
        const tierData = d.tiers[tierKey] || (window.DEFAULTS.tiers && window.DEFAULTS.tiers[tierKey]) || {};
        const features = { ...(tierData.features || {}) };
        delete features[fk];
        tiers[tierKey] = { ...tierData, features };
      });
      return { ...d, featureLabels, featureOrder, tiers };
    });
  };
  const update = (k, v) => setData(d => ({ ...d, [k]: v }));
  const updateTheme = (k, v) => setData(d => ({ ...d, theme: { ...d.theme, [k]: v } }));
  const updateShow = (k, v) => setData(d => ({
    ...d,
    theme: { ...d.theme, show: { ...d.theme.show, [k]: v } }
  }));

  const updateTierCount = (nextCount) => {
    updateActiveProfile(profile => {
      const count = window.clampTierCount(nextCount);
      const nextData = { ...profile.data, tierCount: count };
      const keys = window.tierKeys(nextData);
      const focusTier = keys.includes(nextData.focusTier) ? nextData.focusTier : keys[keys.length - 1];
      const nextView = profile.view === 'combined' || keys.includes(profile.view) ? profile.view : focusTier;
      return { ...profile, view: nextView, data: { ...nextData, focusTier } };
    });
  };

  const applyPreset = (preset) => {
    setData(d => ({
      ...d,
      theme: utils.applyThemePatch(d.theme, {
        ...preset,
        patternColor: preset.patternColor || preset.accent,
        patternColor2: preset.patternColor2 || preset.accent2,
      })
    }));
  };

  const uploadThemes = async (file) => {
    if (!file) return;
    try {
      const text = await file.text();
      const parsed = utils.parseThemeFileContent(text, data.style);
      if (parsed.error) {
        setThemeImportStatus(parsed.error);
        return;
      }

      setStudioState(prev => {
        const currentId = prev.users[prev.activeUserId] ? prev.activeUserId : utils.DEFAULT_USER_ID;
        const profile = prev.users[currentId];
        const nextThemes = addThemesToList(profile.customThemes || [], parsed.themes);
        const firstTheme = parsed.themes[0];
        const nextData = {
          ...profile.data,
          theme: utils.applyThemePatch(profile.data.theme, {
            ...firstTheme,
            patternColor: firstTheme.patternColor || firstTheme.accent,
            patternColor2: firstTheme.patternColor2 || firstTheme.accent2,
          }),
        };
        return {
          ...prev,
          activeUserId: currentId,
          users: {
            ...prev.users,
            [currentId]: { ...profile, customThemes: nextThemes, data: nextData },
          },
        };
      });
      setThemeImportStatus(`Imported ${parsed.themes.length} theme${parsed.themes.length === 1 ? '' : 's'}`);
    } catch (error) {
      setThemeImportStatus('Could not read that theme file.');
    }
  };

  const saveCurrentTheme = () => {
    const styleName = (window.STYLES.find(s => s.id === data.style) || {}).name || 'Custom';
    const name = window.prompt('Theme name', `${styleName} custom`);
    if (!name || !name.trim()) return;

    const theme = utils.createThemeFromCurrent(name.trim(), data.style, data.theme, customThemes);
    if (!theme) {
      setThemeImportStatus('Theme needs at least one color, font, or option.');
      return;
    }
    setCustomThemes(themes => addThemesToList(themes, [theme]));
    setThemeImportStatus('Theme saved');
  };

  // Auto-fit preview
  const platform = window.PLATFORMS[data.platform];
  const wrapRef = useRef(null);
  const previewScale = data.previewScale == null ? 0.92 : data.previewScale;
  const [autoScale, setAutoScale] = useState(1);
  const [measuredH, setMeasuredH] = useState(platform.h);
  const scale = autoScale * previewScale;
  // Canvas width stays at platform's native width; height grows with content if fonts overflow.
  const coverW = platform.w;
  const coverH = Math.max(platform.h, measuredH);

  // Reset measured height when platform changes
  useEffect(() => {
    setMeasuredH(platform.h);
  }, [data.platform]);

  // Measure cover height whenever data/theme/view changes
  useEffect(() => {
    if (!stageRef.current) return;
    const cover = stageRef.current.querySelector('.cover');
    if (!cover) return;
    const measure = () => {
      const h = cover.scrollHeight;
      setMeasuredH(prev => Math.abs(prev - h) < 2 ? prev : h);
    };
    measure();
    const ro = new ResizeObserver(measure);
    ro.observe(cover);
    return () => ro.disconnect();
  }, [data]);
  useEffect(() => {
    const fit = () => {
      if (!wrapRef.current) return;
      const pad = window.matchMedia('(max-width: 700px)').matches ? 32 : 96;
      const aw = Math.max(1, wrapRef.current.clientWidth - pad);
      const ah = Math.max(1, wrapRef.current.clientHeight - pad);
      const s = Math.min(aw / coverW, ah / coverH, 1);
      setAutoScale(s);
    };
    fit();
    const ro = new ResizeObserver(fit);
    if (wrapRef.current) ro.observe(wrapRef.current);
    return () => ro.disconnect();
  }, [data.platform, coverW, coverH]);

  const captureCover = async (overrides = {}) => {
    const platform = window.PLATFORMS[data.platform];
    const captureView = overrides.view || safeView;
    const tierForCover = captureView === 'combined' ? focusedTier : captureView;
    const isCombined = captureView === 'combined';

    const off = document.createElement('div');
    off.style.position = 'fixed';
    off.style.left = '-99999px';
    off.style.top = '0';
    const w = platform.w;
    off.style.width = w + 'px';
    // height is auto so cover can grow with content
    off.style.zIndex = '-1';
    off.style.pointerEvents = 'none';
    document.body.appendChild(off);

    const root = ReactDOM.createRoot(off);
    root.render(
      <window.Cover
        style={data.style}
        tier={tierForCover}
        data={data}
        platform={data.platform}
        combined={isCombined}
      />
    );

    await document.fonts.ready;
    await new Promise(r => setTimeout(r, 250));

    const node = off.querySelector('.cover');
    // Use actual rendered height (>= platform.h)
    const h = Math.max(platform.h, node.scrollHeight);
    const canvas = await window.html2canvas(node, {
      width: w, height: h,
      windowWidth: w, windowHeight: h,
      backgroundColor: null, scale: 2,
      logging: false, useCORS: true,
    });

    root.unmount();
    document.body.removeChild(off);
    return { canvas, platform, view: captureView };
  };

  const downloadCanvas = (canvas, viewName) => {
    const url = canvas.toDataURL('image/png');
    const a = document.createElement('a');
    a.download = `${data.brand.toLowerCase()}-${data.style}-${viewName}-${coverW}x${coverH}.png`;
    a.href = url;
    a.click();
  };

  const exportPNG = async () => {
    setExporting(true);
    try {
      const { canvas, view: v } = await captureCover();
      downloadCanvas(canvas, v);
    } catch (e) { console.error(e); alert('Export failed: ' + e.message); }
    finally { setExporting(false); }
  };

  const exportAll = async () => {
    setExporting(true);
    try {
      const exportViews = [...activeTierKeys, 'combined'];
      for (const v of exportViews) {
        const { canvas } = await captureCover({ view: v });
        downloadCanvas(canvas, v);
        await new Promise(r => setTimeout(r, 250));
      }
    } catch (e) { console.error(e); alert('Export failed: ' + e.message); }
    finally { setExporting(false); }
  };

  const tierForCover = safeView === 'combined' ? focusedTier : safeView;
  const isCombined = safeView === 'combined';

  return (
    <div className={`app ${mobilePanelOpen ? 'is-panel-open' : ''}`}>
      <button
        className="mobile-panel-backdrop"
        type="button"
        aria-label="Close controls"
        onClick={() => setMobilePanelOpen(false)}
      />
      {supportModalOpen && (
        <div className="support-modal-backdrop" onClick={() => setSupportModalOpen(false)}>
          <div
            className="support-modal"
            role="dialog"
            aria-modal="true"
            aria-labelledby="support-modal-title"
            onClick={e => e.stopPropagation()}
          >
            <button
              className="support-modal-close"
              type="button"
              aria-label="Close author and support modal"
              onClick={() => setSupportModalOpen(false)}
            >
              x
            </button>
            <div className="support-modal-heart" aria-hidden="true">&hearts;</div>
            <h2 id="support-modal-title">Thanks for using Cover Studio</h2>
            <p>
              I hope this helps you package your freelance offers with a little more polish.
              You can see more work from SEMKIV/LABS or support the project below.
            </p>
            <div className="support-modal-links">
              <a href="https://semkivlabs.tech/" target="_blank" rel="noopener noreferrer">
                semkivlabs.tech
              </a>
              <a className="support-modal-coffee" href="https://buymeacoffee.com/romansemkiv" target="_blank" rel="noopener noreferrer">
                Buy me a coffee
              </a>
            </div>
          </div>
        </div>
      )}
      <aside className="panel">
        <div className="panel-head">
          <div className="panel-brand">
            <div>
              <div className="panel-brand-name">Cover Studio</div>
              <div className="panel-brand-sub">Upwork · Fiverr · gig covers</div>
            </div>
          </div>
          <div className="panel-actions">
            <button
              className="heart-btn"
              type="button"
              aria-label="Open author and support modal"
              title="About and support"
              onClick={() => setSupportModalOpen(true)}
            >
              <span aria-hidden="true">&hearts;</span>
            </button>
            <button
              className="mobile-panel-close"
              type="button"
              aria-label="Close controls"
              onClick={() => setMobilePanelOpen(false)}
            >
              x
            </button>
          </div>
        </div>

        <UserPanel
          activeUserId={activeUserId}
          users={studioState.users}
          user={activeUser}
          saveStatus={saveStatus}
          selectUser={selectUser}
          addUser={addUser}
          removeUser={removeUser}
          renameUser={renameUser}
          resetActiveUser={resetActiveUser}
        />

        <Section title="Style">
          <div className="style-tabs">
            <button type="button" className={`style-tab ${styleTab === 'presets' ? 'is-active' : ''}`} onClick={() => setStyleTab('presets')}>
              Presets
            </button>
            <button type="button" className={`style-tab ${styleTab === 'custom' ? 'is-active' : ''}`} onClick={() => setStyleTab('custom')}>
              Custom
            </button>
          </div>

          {styleTab === 'presets' ? (
            <div className="style-presets-panel">
              <div className="style-grid">
                {window.STYLES.map(s => (
                  <button key={s.id} className={`style-card ${data.style === s.id ? 'is-active' : ''}`} onClick={() => update('style', s.id)}>
                    <div className={`style-thumb style-thumb--${s.id}`}><div className="style-thumb-inner" /></div>
                    <div className="style-card-name">{s.name}</div>
                    <div className="style-card-sub">{s.sub}</div>
                  </button>
                ))}
              </div>
              <ThemePresetPicker data={data} customThemes={customThemes} applyPreset={applyPreset} />
            </div>
          ) : (
            <div className="style-custom-panel">
              <StyleThemePreview data={data} view={safeView} />
              <ThemeConfigurator
                data={data}
                updateTheme={updateTheme}
                uploadThemes={uploadThemes}
                saveCurrentTheme={saveCurrentTheme}
                themeImportStatus={themeImportStatus}
              />
              <ComponentVisibilityConfigurator theme={data.theme} style={data.style} updateShow={updateShow} />
            </div>
          )}
        </Section>

        <Section title="Platform">
          <div className="seg">
            {Object.entries(window.PLATFORMS).map(([k, p]) => (
              <button key={k} className={`seg-btn ${data.platform === k ? 'is-active' : ''}`} onClick={() => update('platform', k)}>
                <span className="seg-btn-name">{p.label}</span>
                <span className="seg-btn-sub">{p.w}×{p.h}</span>
              </button>
            ))}
          </div>
        </Section>

        <Section title="View">
          <div className="seg seg--views">
            {[...activeTierKeys, 'combined'].map(v => (
              <button key={v} className={`seg-btn ${safeView === v ? 'is-active' : ''}`} onClick={() => setView(v)}>
                <span className="seg-btn-name">{v === 'combined' ? 'Compare' : window.tierLabel(data, v, { case: 'title' })}</span>
              </button>
            ))}
          </div>
          <Field label={`Preview scale (${Math.round(previewScale * 100)}%)`}>
            <input
              type="range" min="0.55" max="1.35" step="0.01"
              value={previewScale}
              onChange={e => update('previewScale', parseFloat(e.target.value))}
              className="inp-range"
            />
          </Field>
        </Section>

        <Section title="Brand">
          <Field label="Brand name" hideToggle={visibilityToggle(data.theme, updateShow, 'brand')}>
            <input className="inp" value={data.brand} onChange={e => update('brand', e.target.value)} />
          </Field>
          <Field label="Tagline" hideToggle={visibilityToggle(data.theme, updateShow, 'tagline')}>
            <input className="inp" value={data.tagline} onChange={e => update('tagline', e.target.value)} />
          </Field>
          <Field label={`Compare-view headline (${data.style}) — use Enter for line break`}>
            <textarea
              className="inp"
              rows="3"
              value={(data.combineHeadlines && data.combineHeadlines[data.style]) || ''}
              onChange={e => {
                const next = { ...(data.combineHeadlines || {}), [data.style]: e.target.value };
                update('combineHeadlines', next);
              }}
              style={{ resize: 'vertical', fontFamily: 'inherit', lineHeight: 1.4 }}
            />
          </Field>
        </Section>

        <Section title="Pricing">
          <Field label="Launch pricing (first 3 clients)">
            <Toggle on={data.launchPricing} onChange={v => update('launchPricing', v)} />
          </Field>
          <Field label="Number of tiers">
            <div className="tier-count-row">
              <input
                type="number"
                min={window.MIN_TIERS}
                max={window.MAX_TIERS}
                className="inp tier-count-input"
                value={window.tierCount(data)}
                onChange={e => updateTierCount(parseInt(e.target.value))}
              />
              <input
                type="range"
                min={window.MIN_TIERS}
                max={window.MAX_TIERS}
                step="1"
                value={window.tierCount(data)}
                onChange={e => updateTierCount(parseInt(e.target.value))}
                className="inp-range"
              />
            </div>
          </Field>
          <Field label="Highlighted tier">
            <div className="seg">
              {activeTierKeys.map(k => (
                <button key={k} className={`seg-btn ${focusedTier === k ? 'is-active' : ''}`} onClick={() => update('focusTier', k)}>
                  <span className="seg-btn-name">{window.tierLabel(data, k, { case: 'title' })}</span>
                </button>
              ))}
            </div>
          </Field>
        </Section>

        {activeTierKeys.map(tk => (
          <TierEditor key={tk} tier={tk} data={data} updateTier={updateTier} updateTierFeat={updateTierFeat} updateFeatureLabel={updateFeatureLabel} addFeature={addFeature} removeFeature={removeFeature} updateShow={updateShow} />
        ))}

        <AboutPanel />

        <div className="export-bar">
          <button className="btn btn--primary" disabled={exporting} onClick={exportPNG}>
            {exporting ? 'Exporting…' : `↓ Export PNG (${coverW}×${coverH})`}
          </button>
          <button className="btn btn--ghost" disabled={exporting} onClick={exportAll}>
            ↓ Export all {activeTierKeys.length + 1} views
          </button>
        </div>
      </aside>

      <main className="stage" ref={wrapRef}>
        <button
          className="mobile-panel-toggle"
          type="button"
          aria-expanded={mobilePanelOpen}
          aria-label="Open controls"
          onClick={() => setMobilePanelOpen(true)}
        >
          Controls
        </button>
        <div className="stage-meta">
          <div className="stage-meta-l">
            <span>{window.STYLES.find(s => s.id === data.style).name.toUpperCase()}</span>
            <span className="sep">/</span>
            <span>{platform.label}</span>
            <span className="sep">/</span>
            <span>{safeView === 'combined' ? 'Compare all tiers' : window.tierLabel(data, safeView)}</span>
          </div>
          <div className="stage-meta-r">
            <span>{coverW} × {coverH}px</span>
            <span className="sep">·</span>
            <span>scale {(scale * 100).toFixed(0)}%</span>
          </div>
        </div>

        <div className="stage-frame" style={{ width: coverW * scale, height: coverH * scale }}>
          <div className="stage-canvas" ref={stageRef}
            style={{ width: coverW, height: coverH, transform: `scale(${scale})`, transformOrigin: 'top left' }}>
            <window.Cover
              style={data.style}
              tier={tierForCover}
              data={data}
              platform={data.platform}
              combined={isCombined}
            />
          </div>
        </div>

        <div className="stage-foot">Live preview · edits update instantly · export saves at native resolution</div>
      </main>
    </div>
  );
}

function AboutPanel() {
  return (
    <Section title="About">
      <div className="about-card">
        <div className="about-copy">
          <strong>Built by SEMKIV/LABS</strong>
          <span>Independent tools for sharper freelance work.</span>
        </div>
        <div className="about-links">
          <a className="about-link" href="https://semkivlabs.tech/" target="_blank" rel="noopener noreferrer">
            semkivlabs.tech
          </a>
          <a className="about-link about-link--coffee" href="https://buymeacoffee.com/romansemkiv" target="_blank" rel="noopener noreferrer">
            Buy me a coffee
          </a>
        </div>
      </div>
    </Section>
  );
}

function UserPanel({ activeUserId, users, user, saveStatus, selectUser, addUser, removeUser, renameUser, resetActiveUser }) {
  const userList = Object.values(users);
  return (
    <Section title="User">
      <Field label="Profile">
        <div className="profile-row">
          <select className="inp profile-select" value={activeUserId} onChange={e => selectUser(e.target.value)}>
            {userList.map(u => (
              <option key={u.id} value={u.id}>{u.name || u.id}</option>
            ))}
          </select>
          <button className="mini-btn" type="button" onClick={addUser} title="Add profile">+</button>
          <button
            className="mini-btn"
            type="button"
            onClick={removeUser}
            disabled={activeUserId === window.CoverStudioUtils.DEFAULT_USER_ID || userList.length < 2}
            title="Remove profile"
          >
            -
          </button>
        </div>
      </Field>
      <Field label="Name">
        <input className="inp" value={user.name || ''} onChange={e => renameUser(e.target.value)} />
      </Field>
      <div className="profile-actions">
        <button className="btn btn--ghost btn--compact reset-btn" type="button" onClick={resetActiveUser}>
          Reset user
        </button>
      </div>
      <div className={`save-state ${saveStatus === 'Saved' ? 'is-saved' : 'is-error'}`}>{saveStatus}</div>
    </Section>
  );
}

function ThemePresetPicker({ data, customThemes, applyPreset }) {
  const presets = window.THEME_PRESETS[data.style] || [];
  const userPresets = (customThemes || []).filter(t => !t.style || t.style === data.style);
  const t = data.theme;

  return (
    <div className="style-custom-group">
      <Field label="Theme presets">
        <div className="preset-grid">
          {presets.map(p => (
            <button key={p.id} className="preset-card" onClick={() => applyPreset(p)} title={p.name}>
              <div className="preset-swatch" style={{ background: p.bg }}>
                <div className="preset-dot" style={{ background: p.text }} />
                <div className="preset-dot" style={{ background: p.accent }} />
                <div className="preset-dot" style={{ background: p.accent2 }} />
              </div>
              <div className="preset-name">{p.name}</div>
            </button>
          ))}
        </div>
      </Field>

      {userPresets.length > 0 && (
        <Field label="My themes">
          <div className="preset-grid">
            {userPresets.map(p => (
              <button key={p.id} className="preset-card preset-card--custom" onClick={() => applyPreset(p)} title={p.name}>
                <div className="preset-swatch" style={{ background: p.bg || t.bg }}>
                  <div className="preset-dot" style={{ background: p.text || t.text }} />
                  <div className="preset-dot" style={{ background: p.accent || t.accent }} />
                  <div className="preset-dot" style={{ background: p.accent2 || t.accent2 }} />
                </div>
                <div className="preset-name">{p.name}</div>
              </button>
            ))}
          </div>
        </Field>
      )}
    </div>
  );
}

function StyleThemePreview({ data, view }) {
  const tierPartVisible = window.tierPartVisible;
  const styleName = (window.STYLES.find(s => s.id === data.style) || {}).name || data.style;
  const activeTierKeys = window.tierKeys(data);
  const focusTier = activeTierKeys.includes(data.focusTier) ? data.focusTier : activeTierKeys[activeTierKeys.length - 1];
  const tier = view === 'combined' ? focusTier : view;
  const tierData = data.tiers[tier] || data.tiers[focusTier] || {};
  const price = data.launchPricing ? tierData.launchPrice : tierData.price;
  const priceTag = window.tierPriceTag(data, tier, '');
  const theme = data.theme;
  const showPriceTag = tierPartVisible(data, tier, 'showPriceTag');

  return (
    <div
      className="style-preview"
      style={{
        '--preview-bg': theme.bg,
        '--preview-text': theme.text,
        '--preview-muted': theme.muted,
        '--preview-accent': theme.accent,
        '--preview-accent2': theme.accent2,
        '--preview-pattern': theme.patternColor || theme.accent,
        '--preview-pattern2': theme.patternColor2 || theme.accent2,
        fontFamily: theme.bodyFont,
      }}
    >
      <div className="style-preview-top">
        <span>Live style preview</span>
        <span>{styleName}</span>
      </div>
      <div className="style-preview-card">
        <div className="style-preview-glow" />
        <div className="style-preview-kicker">{window.tierLabel(data, tier)}</div>
        <div className="style-preview-title" style={{ fontFamily: theme.displayFont }}>{tierData.title || 'Package title'}</div>
        <div className="style-preview-meta">
          <span>${Number(price || 0).toLocaleString()}</span>
          {showPriceTag && priceTag && <span className="c-price-tag style-preview-price-tag">{priceTag}</span>}
        </div>
        <div className="style-preview-bars">
          <span />
          <span />
          <span />
        </div>
      </div>
    </div>
  );
}

function ThemeCreationGuide({ theme }) {
  const tokens = [
    { key: 'bg', label: 'Canvas', value: theme.bg },
    { key: 'text', label: 'Text', value: theme.text },
    { key: 'muted', label: 'Muted', value: theme.muted },
    { key: 'accent', label: 'Accent', value: theme.accent },
    { key: 'accent2', label: 'Accent 2', value: theme.accent2 },
    { key: 'patternColor', label: 'Pattern', value: theme.patternColor || theme.accent },
    { key: 'patternColor2', label: 'Glow', value: theme.patternColor2 || theme.accent2 },
  ];

  return (
    <div className="theme-instructions">
      <div className="theme-instructions-title">Theme creation guide</div>
      <div className="theme-flow">
        <div><span>1</span>Colors</div>
        <div><span>2</span>Fonts</div>
        <div><span>3</span>Options</div>
        <div><span>4</span>Upload</div>
      </div>
      <div className="theme-token-grid">
        {tokens.map(token => (
          <div key={token.key} className="theme-token">
            <span className="theme-token-swatch" style={{ background: token.value }} />
            <span className="theme-token-label">{token.label}</span>
            <code>{token.key}</code>
          </div>
        ))}
      </div>
      <pre className="theme-json-card"><code>{`{
  "name": "Client Neon",
  "bg": "${theme.bg}",
  "text": "${theme.text}",
  "accent": "${theme.accent}",
  "patternColor": "${theme.patternColor || theme.accent}",
  "patternColor2": "${theme.patternColor2 || theme.accent2}",
  "descriptionPosition": "${theme.descriptionPosition || 'auto'}",
  "layout": "${theme.layout || 'auto'}",
  "fontScale": ${theme.fontScale || 1},
  "show": { "price": true }
}`}</code></pre>
      <div className="theme-instructions-copy">
        Required color keys: <code>bg, text, muted, accent, accent2</code>. Optional keys: <code>patternColor, patternColor2, displayFont, bodyFont, monoFont, background, descriptionPosition, layout</code>.
      </div>
    </div>
  );
}

function ThemeConfigurator({ data, updateTheme, uploadThemes, saveCurrentTheme, themeImportStatus }) {
  const fonts = window.FONT_OPTIONS;
  const t = data.theme;

  return (
    <div className="style-custom-group">
      <div className="style-custom-title">Theme configurator</div>
      <div className="style-custom-body">
        <Field label="Custom themes">
          <div className="theme-actions">
            <button className="btn btn--ghost btn--compact" type="button" onClick={saveCurrentTheme}>Save current</button>
            <label className="btn btn--ghost btn--compact upload-btn">
              Upload JSON
              <input
                type="file"
                accept=".json,application/json"
                onChange={async e => {
                  await uploadThemes(e.target.files && e.target.files[0]);
                  e.target.value = '';
                }}
              />
            </label>
          </div>
          {themeImportStatus && <div className="theme-status">{themeImportStatus}</div>}
        </Field>

        <ThemeCreationGuide theme={t} />

        <div className="color-grid">
          <ColorField label="Background" value={t.bg} onChange={v => updateTheme('bg', v)} />
          <ColorField label="Text" value={t.text} onChange={v => updateTheme('text', v)} />
          <ColorField label="Muted" value={t.muted} onChange={v => updateTheme('muted', v)} />
          <ColorField label="Accent" value={t.accent} onChange={v => updateTheme('accent', v)} />
          <ColorField label="Accent 2" value={t.accent2} onChange={v => updateTheme('accent2', v)} />
        </div>

        <Field label="Display font (titles)">
          <FontSelect options={fonts.display} value={t.displayFont} onChange={v => updateTheme('displayFont', v)} />
        </Field>
        <Field label="Body font (descriptions)">
          <FontSelect options={fonts.body} value={t.bodyFont} onChange={v => updateTheme('bodyFont', v)} />
        </Field>
        <Field label="Mono font (code, labels)">
          <FontSelect options={fonts.mono} value={t.monoFont} onChange={v => updateTheme('monoFont', v)} />
        </Field>
        <Field label={`Font size (${Math.round((t.fontScale || 1) * 100)}%)`}>
          <input
            type="range" min="0.7" max="2.0" step="0.05"
            value={t.fontScale || 1}
            onChange={e => updateTheme('fontScale', parseFloat(e.target.value))}
            className="inp-range"
          />
        </Field>
        <Field label="Background pattern">
          <select className="inp" value={t.background || 'auto'} onChange={e => updateTheme('background', e.target.value)}>
            {window.BACKGROUND_OPTIONS.map(b => (
              <option key={b.id} value={b.id}>{b.name}</option>
            ))}
          </select>
        </Field>
        <Field label="Description position">
          <select className="inp" value={t.descriptionPosition || 'auto'} onChange={e => updateTheme('descriptionPosition', e.target.value)}>
            {window.DESCRIPTION_POSITION_OPTIONS.map(option => (
              <option key={option.id} value={option.id}>{option.name}</option>
            ))}
          </select>
        </Field>
        <Field label="Layout">
          <select className="inp" value={t.layout || 'auto'} onChange={e => updateTheme('layout', e.target.value)}>
            {window.LAYOUT_OPTIONS.map(option => (
              <option key={option.id} value={option.id}>{option.name}</option>
            ))}
          </select>
        </Field>
        <div className="color-grid">
          <ColorField label="Pattern color" value={t.patternColor || t.accent} onChange={v => updateTheme('patternColor', v)} />
          <ColorField label="Glow gradient" value={t.patternColor2 || t.accent2} onChange={v => updateTheme('patternColor2', v)} />
        </div>
      </div>
    </div>
  );
}

function ColorField({ label, value, onChange }) {
  return (
    <div className="cf">
      <div className="cf-label">{label}</div>
      <div className="cf-row">
        <input type="color" className="cf-pick" value={value} onChange={e => onChange(e.target.value)} />
        <input className="cf-text" value={value} onChange={e => onChange(e.target.value)} />
      </div>
    </div>
  );
}

function FontSelect({ options, value, onChange }) {
  // Match by stack
  return (
    <select className="inp" value={value} onChange={e => onChange(e.target.value)} style={{ fontFamily: value }}>
      {options.map(f => (
        <option key={f.id} value={f.stack} style={{ fontFamily: f.stack }}>{f.name}</option>
      ))}
    </select>
  );
}

function ComponentVisibilityConfigurator({ theme, style, updateShow }) {
  const list = (window.STYLE_COMPONENTS && window.STYLE_COMPONENTS[style]) || window.COMPONENT_KEYS;
  const styleName = (window.STYLES.find(s => s.id === style) || {}).name || '';
  return (
    <div className="style-custom-group">
      <div className="style-custom-title">
        Show / hide <span className="sec-title-sub">· {styleName.toLowerCase()}</span>
      </div>
      <div className="style-custom-body">
        <div className="vis-grid">
          {list.map(c => (
            <label key={c.id} className="vis-row">
              <Toggle on={theme.show[c.id] !== false} onChange={v => updateShow(c.id, v)} />
              <span>{c.label}</span>
            </label>
          ))}
        </div>
      </div>
    </div>
  );
}

function Section({ title, children }) {
  return (
    <div className="sec">
      <div className="sec-title">{title}</div>
      <div className="sec-body">{children}</div>
    </div>
  );
}

function Field({ label, children, hideToggle }) {
  return (
    <div className="field">
      <div className="field-head">
        <div className="field-label">{label}</div>
        {hideToggle && (
          <label className="field-hide">
            <input
              type="checkbox"
              checked={hideToggle.hidden}
              onChange={e => hideToggle.onChange(e.target.checked)}
            />
            <span>Hide</span>
          </label>
        )}
      </div>
      <div className="field-control">{children}</div>
    </div>
  );
}

function Toggle({ on, onChange }) {
  return (
    <button className={`tog ${on ? 'is-on' : ''}`} onClick={() => onChange(!on)} type="button">
      <span className="tog-thumb" />
    </button>
  );
}

function TierEditor({ tier, data, updateTier, updateTierFeat, updateFeatureLabel, addFeature, removeFeature, updateShow }) {
  const [open, setOpen] = useState(tier === data.focusTier);
  const t = data.tiers[tier];
  const theme = data.theme;
  return (
    <div className={`tier-ed ${open ? 'is-open' : ''}`}>
      <button className="tier-ed-head" onClick={() => setOpen(o => !o)}>
        <div className="tier-ed-head-l">
          <span className="tier-ed-num">{window.tierNumber(data, tier)}</span>
          <span className="tier-ed-name">{window.tierLabel(data, tier)}</span>
        </div>
        <div className="tier-ed-head-r">
          <span className="tier-ed-price">${(data.launchPricing ? t.launchPrice : t.price).toLocaleString()}</span>
          <span className="tier-ed-chev">{open ? '−' : '+'}</span>
        </div>
      </button>
      {open && (
        <div className="tier-ed-body">
          <Field label="Tier stamp" hideToggle={tierVisibilityToggle(t, tier, updateTier, 'showTierStamp')}>
            <input className="inp" value={t.label || ''} onChange={e => updateTier(tier, 'label', e.target.value)} />
          </Field>
          <Field label="Tier number" hideToggle={tierVisibilityToggle(t, tier, updateTier, 'showTierNumber')}>
            <input className="inp" value={t.num || window.tierNumber(data, tier)} onChange={e => updateTier(tier, 'num', e.target.value)} />
          </Field>
          <Field label="Price tag" hideToggle={tierVisibilityToggle(t, tier, updateTier, 'showPriceTag')}>
            <input className="inp" value={t.priceTag || ''} onChange={e => updateTier(tier, 'priceTag', e.target.value)} />
          </Field>
          <Field label="Price tag icon">
            <select className="inp" value={t.priceTagIcon || 'tag'} onChange={e => updateTier(tier, 'priceTagIcon', e.target.value)}>
              {window.PRICE_TAG_ICONS.map(icon => (
                <option key={icon.id} value={icon.id}>{icon.glyph ? `${icon.glyph} ${icon.name}` : icon.name}</option>
              ))}
            </select>
          </Field>
          <Field label="Title">
            <input className="inp" value={t.title} onChange={e => updateTier(tier, 'title', e.target.value)} />
          </Field>
          <Field label="Description" hideToggle={visibilityToggle(theme, updateShow, 'description')}>
            <textarea className="inp inp--ta" value={t.desc} rows="3" onChange={e => updateTier(tier, 'desc', e.target.value)} />
          </Field>
          <div className="row3">
            <Field label="Days" hideToggle={visibilityToggle(theme, updateShow, 'days')}>
              <input type="number" className="inp" value={t.days} onChange={e => updateTier(tier, 'days', parseInt(e.target.value) || 0)} />
            </Field>
            <Field label="Revisions" hideToggle={visibilityToggle(theme, updateShow, 'revisions')}>
              <input type="number" className="inp" value={t.revisions} onChange={e => updateTier(tier, 'revisions', parseInt(e.target.value) || 0)} />
            </Field>
            <Field label="Price ($)" hideToggle={visibilityToggle(theme, updateShow, 'price')}>
              <input type="number" className="inp" value={t.price} onChange={e => updateTier(tier, 'price', parseInt(e.target.value) || 0)} />
            </Field>
          </div>
          <Field label="Launch price ($)" hideToggle={visibilityToggle(theme, updateShow, 'price')}>
            <input type="number" className="inp" value={t.launchPrice} onChange={e => updateTier(tier, 'launchPrice', parseInt(e.target.value) || 0)} />
          </Field>
          <Field label="Features included" hideToggle={visibilityToggle(theme, updateShow, 'features')}>
            <div className="feat-list">
              {Object.entries(window.featureLabels(data)).map(([fk, label]) => (
                <div key={fk} className="feat-row">
                  <input type="checkbox" checked={t.features[fk]} onChange={e => updateTierFeat(tier, fk, e.target.checked)} />
                  <input className="inp feature-label-input" value={label} onChange={e => updateFeatureLabel(fk, e.target.value)} />
                  <button
                    className="btn btn--ghost feature-remove-btn"
                    type="button"
                    aria-label={`Remove feature: ${label}`}
                    title="Remove feature"
                    onClick={() => removeFeature(fk)}
                  >-</button>
                </div>
              ))}
              <button className="btn btn--ghost feature-add-btn" type="button" onClick={() => addFeature(tier)}>Add feature</button>
            </div>
          </Field>
        </div>
      )}
    </div>
  );
}

ReactDOM.createRoot(document.getElementById('root')).render(<App />);
