const { useState, useRef, useEffect } = React;

/* ─── DATA ─────────────────────────────────────────────────────────── */

const STATES_DATA = {
  "Alabama":["Birmingham","Montgomery","Huntsville","Mobile","Tuscaloosa","Hoover","Dothan","Auburn","Decatur","Madison"],
  "Alaska":["Anchorage","Fairbanks","Juneau","Sitka","Ketchikan","Wasilla","Kenai","Kodiak","Bethel","Palmer"],
  "Arizona":["Phoenix","Tucson","Mesa","Chandler","Scottsdale","Glendale","Gilbert","Tempe","Peoria","Surprise"],
  "Arkansas":["Little Rock","Fort Smith","Fayetteville","Springdale","Jonesboro","Rogers","Conway","North Little Rock","Bentonville","Pine Bluff"],
  "California":["Los Angeles","San Diego","San Jose","San Francisco","Fresno","Sacramento","Long Beach","Oakland","Bakersfield","Anaheim"],
  "Colorado":["Denver","Colorado Springs","Aurora","Fort Collins","Lakewood","Thornton","Arvada","Westminster","Pueblo","Centennial"],
  "Connecticut":["Bridgeport","New Haven","Hartford","Stamford","Waterbury","Norwalk","Danbury","New Britain","Bristol","Meriden"],
  "Delaware":["Wilmington","Dover","Newark","Middletown","Smyrna","Milford","Seaford","Georgetown","Elsmere","New Castle"],
  "Florida":["Jacksonville","Miami","Tampa","Orlando","St. Petersburg","Hialeah","Tallahassee","Fort Lauderdale","Port St. Lucie","Cape Coral"],
  "Georgia":["Atlanta","Augusta","Columbus","Macon","Savannah","Athens","Sandy Springs","Roswell","Johns Creek","Albany"],
  "Hawaii":["Honolulu","Pearl City","Hilo","Kailua","Waipahu","Kaneohe","Mililani","Kahului","Ewa Beach","Kihei"],
  "Idaho":["Boise","Meridian","Nampa","Idaho Falls","Caldwell","Pocatello","Coeur d'Alene","Twin Falls","Post Falls","Lewiston"],
  "Illinois":["Chicago","Aurora","Rockford","Joliet","Naperville","Springfield","Peoria","Elgin","Waukegan","Champaign"],
  "Indiana":["Indianapolis","Fort Wayne","Evansville","South Bend","Carmel","Fishers","Bloomington","Hammond","Gary","Lafayette"],
  "Iowa":["Des Moines","Cedar Rapids","Davenport","Sioux City","Iowa City","Waterloo","Ames","West Des Moines","Council Bluffs","Ankeny"],
  "Kansas":["Wichita","Overland Park","Kansas City","Olathe","Topeka","Lawrence","Shawnee","Manhattan","Lenexa","Salina"],
  "Kentucky":["Louisville","Lexington","Bowling Green","Owensboro","Covington","Richmond","Georgetown","Florence","Hopkinsville","Nicholasville"],
  "Louisiana":["New Orleans","Baton Rouge","Shreveport","Metairie","Lafayette","Lake Charles","Kenner","Bossier City","Monroe","Alexandria"],
  "Maine":["Portland","Lewiston","Bangor","South Portland","Auburn","Biddeford","Sanford","Saco","Westbrook","Augusta"],
  "Maryland":["Baltimore","Columbia","Germantown","Silver Spring","Waldorf","Glen Burnie","Ellicott City","Frederick","Dundalk","Rockville"],
  "Massachusetts":["Boston","Worcester","Springfield","Lowell","Cambridge","New Bedford","Brockton","Quincy","Lynn","Fall River"],
  "Michigan":["Detroit","Grand Rapids","Warren","Sterling Heights","Ann Arbor","Lansing","Flint","Dearborn","Livonia","Troy"],
  "Minnesota":["Minneapolis","St. Paul","Rochester","Duluth","Bloomington","Brooklyn Park","Plymouth","Maple Grove","Woodbury","St. Cloud"],
  "Mississippi":["Jackson","Gulfport","Southaven","Hattiesburg","Biloxi","Meridian","Tupelo","Olive Branch","Greenville","Horn Lake"],
  "Missouri":["Kansas City","St. Louis","Springfield","Columbia","Independence","Lee's Summit","O'Fallon","St. Joseph","St. Charles","Blue Springs"],
  "Montana":["Billings","Missoula","Great Falls","Bozeman","Butte","Helena","Kalispell","Havre","Anaconda","Miles City"],
  "Nebraska":["Omaha","Lincoln","Bellevue","Grand Island","Kearney","Fremont","Hastings","Norfolk","North Platte","Columbus"],
  "Nevada":["Las Vegas","Henderson","Reno","North Las Vegas","Sparks","Carson City","Fernley","Elko","Mesquite","Boulder City"],
  "New Hampshire":["Manchester","Nashua","Concord","Derry","Dover","Rochester","Salem","Merrimack","Hudson","Londonderry"],
  "New Jersey":["Newark","Jersey City","Paterson","Elizabeth","Lakewood","Edison","Woodbridge","Toms River","Hamilton","Trenton"],
  "New Mexico":["Albuquerque","Las Cruces","Rio Rancho","Santa Fe","Roswell","Farmington","Clovis","Hobbs","Alamogordo","Carlsbad"],
  "New York":["New York City","Buffalo","Rochester","Yonkers","Syracuse","Albany","New Rochelle","Mount Vernon","Schenectady","Utica"],
  "North Carolina":["Charlotte","Raleigh","Greensboro","Durham","Winston-Salem","Fayetteville","Cary","Wilmington","High Point","Concord"],
  "North Dakota":["Fargo","Bismarck","Grand Forks","Minot","West Fargo","Williston","Dickinson","Mandan","Jamestown","Wahpeton"],
  "Ohio":["Columbus","Cleveland","Cincinnati","Toledo","Akron","Dayton","Parma","Canton","Youngstown","Lorain"],
  "Oklahoma":["Oklahoma City","Tulsa","Norman","Broken Arrow","Edmond","Lawton","Moore","Midwest City","Enid","Stillwater"],
  "Oregon":["Portland","Salem","Eugene","Gresham","Hillsboro","Beaverton","Bend","Medford","Springfield","Corvallis"],
  "Pennsylvania":["Philadelphia","Pittsburgh","Allentown","Reading","Erie","Bethlehem","Scranton","Lancaster","Harrisburg","York"],
  "Rhode Island":["Providence","Warwick","Cranston","Pawtucket","East Providence","Woonsocket","Coventry","Cumberland","North Providence","South Kingstown"],
  "South Carolina":["Charleston","Columbia","North Charleston","Mount Pleasant","Rock Hill","Greenville","Summerville","Goose Creek","Hilton Head","Florence"],
  "South Dakota":["Sioux Falls","Rapid City","Aberdeen","Brookings","Watertown","Mitchell","Yankton","Pierre","Huron","Vermillion"],
  "Tennessee":["Nashville","Memphis","Knoxville","Chattanooga","Clarksville","Murfreesboro","Franklin","Johnson City","Jackson","Cleveland"],
  "Texas":["Houston","San Antonio","Dallas","Austin","Fort Worth","El Paso","Arlington","Corpus Christi","Plano","Laredo"],
  "Utah":["Salt Lake City","West Valley City","Provo","West Jordan","Orem","Sandy","Ogden","St. George","Layton","South Jordan"],
  "Vermont":["Burlington","South Burlington","Rutland","Barre","Montpelier","Winooski","St. Albans","Newport","Vergennes","Middlebury"],
  "Virginia":["Virginia Beach","Norfolk","Chesapeake","Richmond","Newport News","Alexandria","Hampton","Roanoke","Portsmouth","Suffolk"],
  "Washington":["Seattle","Spokane","Tacoma","Vancouver","Bellevue","Kent","Everett","Renton","Federal Way","Spokane Valley"],
  "West Virginia":["Charleston","Huntington","Morgantown","Parkersburg","Wheeling","Weirton","Fairmont","Martinsburg","Beckley","Clarksburg"],
  "Wisconsin":["Milwaukee","Madison","Green Bay","Kenosha","Racine","Appleton","Waukesha","Oshkosh","Eau Claire","Janesville"],
  "Wyoming":["Cheyenne","Casper","Laramie","Gillette","Rock Springs","Sheridan","Green River","Evanston","Riverton","Jackson"]
};

const FONTS = [
  { name: "Impact", family: "Impact", loaded: true, w: 900 },
  { name: "Bebas Neue", family: "'Bebas Neue'", gf: "Bebas+Neue", w: 400 },
  { name: "Oswald", family: "'Oswald'", gf: "Oswald:wght@700", w: 700 },
  { name: "Anton", family: "'Anton'", gf: "Anton", w: 400 },
  { name: "Teko", family: "'Teko'", gf: "Teko:wght@700", w: 700 },
  { name: "Black Ops One", family: "'Black Ops One'", gf: "Black+Ops+One", w: 400 },
  { name: "Rubik Mono One", family: "'Rubik Mono One'", gf: "Rubik+Mono+One", w: 400 },
  { name: "Bungee", family: "'Bungee'", gf: "Bungee", w: 400 },
  { name: "Permanent Marker", family: "'Permanent Marker'", gf: "Permanent+Marker", w: 400 },
  { name: "Russo One", family: "'Russo One'", gf: "Russo+One", w: 400 },
  { name: "Righteous", family: "'Righteous'", gf: "Righteous", w: 400 },
  { name: "Bangers", family: "'Bangers'", gf: "Bangers", w: 400 },
];

const NAMES = ["James","John","Robert","Michael","David","William","Richard","Joseph","Thomas","Christopher","Daniel","Matthew","Anthony","Mark","Steven","Andrew","Joshua","Kenneth","Kevin","Brian","Jason","Eric","Brandon","Ryan","Justin","Tyler","Aaron","Adam","Nathan","Patrick","Logan","Cameron","Dylan","Austin","Caleb","Mason","Ethan","Hunter","Jordan","Kyle","Casey","Morgan","Skyler","Taylor","Riley","Peyton","Avery","Parker","Devon","Blake","Kayla","Emily","Jessica","Ashley","Sarah","Amanda","Nicole","Stephanie","Lauren","Rachel","Samantha"];
const LI = "ABCDEFGHIJKLMNOPRSTUVW".split("");
const EMJ = ["🎰","🎉","🍀","🎯","💎","🔥","⭐","🏆","💫","🎪","🎲","🃏"];

/* ─── HELPERS ──────────────────────────────────────────────────────── */

const rI = (a, b) => Math.floor(Math.random() * (b - a + 1)) + a;
const rC = (a) => a[Math.floor(Math.random() * a.length)];
const fM = (n) => "$" + n.toLocaleString("en-US");

function genW(casino, state, cnt = 10) {
  const cs = STATES_DATA[state] || ["City"];
  const u = new Set();
  const w = [];
  const t = [rI(1100000, 1300000), rI(1050000, 1200000), rI(1000000, 1150000)];
  const r = [];
  for (let i = 0; i < cnt - 3; i++) r.push(rI(500000, 980000));
  r.sort((a, b) => b - a);
  const all = [...t, ...r];
  for (let i = 0; i < cnt; i++) {
    let n;
    do { n = rC(NAMES) + " " + rC(LI) + "."; } while (u.has(n));
    u.add(n);
    w.push({ name: n, city: rC(cs), amount: all[i] });
  }
  return w;
}

function buildTxt(casino, state, winners, bonus, spins) {
  const m = ["🥇","🥈","🥉"];
  let t = `🎉 CONGRATULATIONS TO YESTERDAY'S WINNERS IN ${state.toUpperCase()}! 🎉\nTHE LEGENDARY ${casino.toUpperCase()} IS NOW ONLINE\nFEEL THE THRILL OF REAL WINS TODAY!\n`;
  winners.forEach((w, i) => {
    const p = i < 3 ? m[i] : rC(EMJ);
    t += `${p} ${w.name.toUpperCase()} (${w.city.toUpperCase()}) – ${fM(w.amount)}\n`;
  });
  t += `\n✨ TRY YOUR LUCK AT ${casino.toUpperCase()}\nAND BECOME THE NEXT WINNER 🏆\n🎁 GRAB YOUR WELCOME BONUS ${bonus} + ${spins} FS 🎁`;
  return t;
}

function parseTxt(text) {
  if (!text) return { hdr: [], top: [], rest: [], cta: [] };
  const lines = text.split("\n").filter(l => l.trim());
  const hdr = [], top = [], rest = [], cta = [];
  let wi = 0;
  lines.forEach(l => {
    const hasAmt = /\$[\d,]+/.test(l) && !l.includes("BONUS");
    if (hasAmt) {
      if (wi < 3) { top.push(l); wi++; }
      else { rest.push(l); wi++; }
    } else if (l.includes("TRY YOUR") || l.includes("GRAB YOUR") || l.includes("BECOME") || l.includes("DOWNLOAD")) {
      cta.push(l);
    } else {
      hdr.push(l);
    }
  });
  return { hdr, top, rest, cta };
}

/* ─── Amt COMPONENT ────────────────────────────────────────────────── */

function Amt({ line, color }) {
  const c = color || "#00FF00";
  const parts = [];
  let last = 0;
  const rx = /(\$[\d,.\s]+\d|\d+\s*FS)/g;
  let m;
  while ((m = rx.exec(line)) !== null) {
    if (m.index > last) parts.push({ text: line.slice(last, m.index), green: false });
    parts.push({ text: m[1], green: true });
    last = m.index + m[0].length;
  }
  if (last < line.length) parts.push({ text: line.slice(last), green: false });
  if (parts.length === 0) return React.createElement("span", null, line);
  return React.createElement(React.Fragment, null,
    parts.map((p, i) =>
      p.green
        ? React.createElement("span", { key: i, style: { color: c, fontWeight: 900 } }, p.text)
        : React.createElement("span", { key: i }, p.text)
    )
  );
}

/* ─── PREVIEW COMPONENT ────────────────────────────────────────────── */

function Preview({ casino, text, logoUrl, bgUrl, fs, hs, blur, bright, overlay, font, logoSize, ctaFs }) {
  const p = parseTxt(text);
  const s = fs / 100;
  const h = hs / 100;
  const ls = logoSize / 100;
  const cs = ctaFs / 100;
  const fontObj = FONTS.find(f => f.name === font) || FONTS[0];
  const fw = fontObj.w || 700;

  return (
    <div style={{
      width: 420, aspectRatio: "4/5", borderRadius: 14, overflow: "hidden", position: "relative",
      background: "#0a0a1a", border: "2px solid #2a2a3a",
      fontFamily: fontObj.family + ",Impact,'Arial Black',sans-serif"
    }}>
      {bgUrl && <img src={bgUrl} style={{
        position: "absolute", inset: 0, width: "100%", height: "100%", objectFit: "cover",
        filter: `blur(${blur}px) brightness(${bright / 100})`, zIndex: 0
      }} onError={e => { e.target.remove(); }} />}
      <div style={{
        position: "absolute", inset: 0, zIndex: 1,
        background: bgUrl ? `rgba(0,0,0,${overlay / 100})` : "linear-gradient(180deg,#0a0a1a 0%,#151530 40%,#0d1520 100%)"
      }} />
      <div style={{
        position: "relative", zIndex: 2, padding: "16px 18px", height: "100%", boxSizing: "border-box",
        display: "flex", flexDirection: "column", alignItems: "center", textAlign: "center", overflow: "hidden"
      }}>
        {logoUrl
          ? <img src={logoUrl} style={{
              maxHeight: 60 * ls, maxWidth: (75 * ls) + "%", objectFit: "contain", marginBottom: 8
            }} onError={e => { e.target.style.display = "none"; }} />
          : <div style={{
              fontSize: 26 * ls, fontWeight: fw, color: "#FFD700",
              textShadow: "2px 3px 6px rgba(0,0,0,0.9)", marginBottom: 6, letterSpacing: 1,
              textTransform: "uppercase"
            }}>{casino}</div>
        }

        {p.hdr.map((l, i) => {
          const isY = l.includes("YESTERDAY");
          const isG = l.includes("LEGENDARY") || l.includes("NOW ONLINE");
          if (isY) {
            const pts = l.split(/(YESTERDAY'S WINNERS)/);
            return (
              <div key={i} style={{
                fontSize: 13 * h, fontWeight: fw, color: "#fff", lineHeight: 1.35, marginBottom: 2,
                textShadow: "1px 2px 4px rgba(0,0,0,0.9)", letterSpacing: 0.5
              }}>
                {pts.map((x, j) =>
                  x === "YESTERDAY'S WINNERS"
                    ? <span key={j} style={{ color: "#FFD700" }}>{x}</span>
                    : <span key={j}>{x}</span>
                )}
              </div>
            );
          }
          return (
            <div key={i} style={{
              fontSize: (isG ? 12.5 : 11.5) * h, fontWeight: fw,
              color: isG ? "#00DD00" : "#ddd", lineHeight: 1.35, marginBottom: 2,
              textShadow: "1px 2px 4px rgba(0,0,0,0.9)", letterSpacing: 0.3
            }}>{l}</div>
          );
        })}

        <div style={{ height: 8 }} />

        {p.top.map((l, i) => (
          <div key={i} style={{
            fontSize: 15 * s, fontWeight: fw, color: "#fff", lineHeight: 1.55,
            textShadow: "2px 2px 5px rgba(0,0,0,0.95)", letterSpacing: 0.3
          }}>
            <Amt line={l} />
          </div>
        ))}

        {p.rest.length > 0 && <div style={{ height: 4 }} />}

        {p.rest.map((l, i) => (
          <div key={i} style={{
            fontSize: 13 * s, fontWeight: fw, color: "#e0e0e0", lineHeight: 1.5,
            textShadow: "1px 2px 4px rgba(0,0,0,0.9)"
          }}>
            <Amt line={l} />
          </div>
        ))}

        <div style={{ flex: 1, minHeight: 6 }} />

        {p.cta.map((l, i) => (
          <div key={i} style={{
            fontSize: 12 * cs, fontWeight: fw, color: "#fff", lineHeight: 1.45,
            textShadow: "1px 2px 4px rgba(0,0,0,0.9)"
          }}>
            {l.includes("$") ? <Amt line={l} color="#00FF00" /> : l}
          </div>
        ))}
      </div>
    </div>
  );
}

/* ─── MAIN APP ─────────────────────────────────────────────────────── */

function App() {
  const [casino, setCasino] = useState("Harrah's Casino");
  const [state, setState] = useState("Tennessee");
  const [wc, setWc] = useState(10);
  const [text, setText] = useState("");
  const [bonus, setBonus] = useState("$1500");
  const [spins, setSpins] = useState("150");
  const [cc, setCc] = useState(5);
  const [tab, setTab] = useState("text");
  const [logo, setLogo] = useState("");
  const [logoServer, setLogoServer] = useState("");   // /api/file/xxx after upload
  const [logoUploading, setLogoUploading] = useState(false);
  const [logoUploadErr, setLogoUploadErr] = useState("");
  const [bg, setBg] = useState("");
  const [music, setMusic] = useState([]);
  const [selTracks, setSelTracks] = useState([]);
  const [playingId, setPlayingId] = useState(null);
  const audioRef = useRef(null);
  const [renderJob, setRenderJob] = useState(null);
  const [fs, setFs] = useState(100);
  const [hs, setHs] = useState(100);
  const [ctaFs, setCtaFs] = useState(100);
  const [blur, setBlur] = useState(5);
  const [bright, setBright] = useState(45);
  const [overlay, setOverlay] = useState(40);
  const [font, setFont] = useState("Impact");
  const [logoSize, setLogoSize] = useState(100);
  const [bgList, setBgList] = useState([]);
  const [bgSearch, setBgSearch] = useState("");
  const [bgResults, setBgResults] = useState([]);
  const [bgLoading, setBgLoading] = useState(false);
  const [vars, setVars] = useState([]);
  const [showV, setShowV] = useState(false);

  // Decorations (particle images)
  const [decoSearch, setDecoSearch] = useState("");
  const [decoResults, setDecoResults] = useState([]);
  const [decoLoading, setDecoLoading] = useState(false);
  const [decoList, setDecoList] = useState([]); // selected particle image URLs [{name, url}]
  const [decoUploaded, setDecoUploaded] = useState([]); // uploaded decoration files [{name, url}]
  const [animSpeed, setAnimSpeed] = useState(100); // percentage 30-300
  const [animCount, setAnimCount] = useState(16); // particle count 3-60
  const [animSize, setAnimSize] = useState(100); // size percentage 30-300
  const [musicUpload, setMusicUpload] = useState(null); // { done, total, current }

  // Fetch music + decorations on mount
  useEffect(() => {
    fetch("/api/music").then(r => r.json()).then(d => {
      if (d.files) setMusic(d.files.map((f, i) => ({ id: i, name: f.name, url: f.url })));
    }).catch(() => {});
    fetch("/api/decorations").then(r => r.json()).then(d => {
      if (d.files) setDecoUploaded(d.files);
    }).catch(() => {});
  }, []);

  // Upload logo blob to server, returns server URL or throws
  const uploadLogoBlob = async (blob) => {
    const fd = new FormData();
    fd.append('files', blob, 'logo.png');
    const resp = await fetch('/api/upload', { method: 'POST', body: fd });
    const data = await resp.json();
    if (data.ok && data.files?.[0]?.url) return data.files[0].url;
    throw new Error('Upload failed');
  };

  // Auto-upload logo to server whenever it changes
  useEffect(() => {
    if (!logo) { setLogoServer(''); setLogoUploadErr(''); return; }
    if (logo.startsWith('/api/')) { setLogoServer(logo); setLogoUploadErr(''); return; }

    setLogoUploading(true);
    setLogoUploadErr('');
    setLogoServer('');

    (async () => {
      try {
        let blob;
        if (logo.startsWith('data:')) {
          // Data URI → fetch as blob directly
          blob = await fetch(logo).then(r => r.blob());
        } else if (logo.startsWith('http')) {
          // External URL: try browser-side fetch first (CDN usually allows it)
          try {
            const r = await fetch(logo);
            if (!r.ok) throw new Error('HTTP ' + r.status);
            blob = await r.blob();
          } catch (browserErr) {
            // Fallback: proxy through server (server downloads the URL)
            const resp = await fetch('/api/proxy-image', {
              method: 'POST', headers: { 'Content-Type': 'application/json' },
              body: JSON.stringify({ url: logo })
            });
            const data = await resp.json();
            if (data.ok && data.url) {
              setLogoServer(data.url);
              setLogoUploading(false);
              return;
            }
            throw new Error(data.error || 'Сервер не смог скачать URL');
          }
        } else {
          setLogoUploading(false);
          return;
        }

        const serverUrl = await uploadLogoBlob(blob);
        setLogoServer(serverUrl);
      } catch (e) {
        setLogoUploadErr('Не удалось загрузить лого: ' + e.message);
        console.error('Logo upload error:', e);
      } finally {
        setLogoUploading(false);
      }
    })();
  }, [logo]);

  // Ctrl+V / Cmd+V paste logo from clipboard (image blob, URL, or HTML img)
  useEffect(() => {
    const handlePaste = (e) => {
      // Don't intercept paste in textarea/input (let text paste work normally)
      const tag = e.target?.tagName?.toLowerCase();
      const isTextInput = tag === 'textarea' || tag === 'input';

      const dt = e.clipboardData;
      if (!dt) return;

      // 1. Check for image file/blob in items (screenshot, copied image)
      if (dt.items) {
        for (let i = 0; i < dt.items.length; i++) {
          const item = dt.items[i];
          if (item.kind === 'file' && item.type.startsWith('image/')) {
            e.preventDefault();
            e.stopPropagation();
            const file = item.getAsFile();
            if (file) {
              const reader = new FileReader();
              reader.onload = (ev) => setLogo(ev.target.result);
              reader.readAsDataURL(file);
            }
            return;
          }
        }
      }

      // 2. Check files array (fallback for older browsers)
      if (dt.files && dt.files.length > 0) {
        for (let i = 0; i < dt.files.length; i++) {
          if (dt.files[i].type.startsWith('image/')) {
            e.preventDefault();
            e.stopPropagation();
            const reader = new FileReader();
            reader.onload = (ev) => setLogo(ev.target.result);
            reader.readAsDataURL(dt.files[i]);
            return;
          }
        }
      }

      // Don't intercept text paste in text fields for URL/HTML checks
      if (isTextInput) return;

      // 3. Check for image URL in plain text
      const text = dt.getData('text/plain');
      if (text && /^https?:\/\/.+\.(png|jpe?g|webp|gif)(\?.*)?$/i.test(text.trim())) {
        e.preventDefault();
        e.stopPropagation();
        setLogo(text.trim());
        return;
      }

      // 4. Check for <img> in HTML clipboard data (copied image from webpage)
      const html = dt.getData('text/html');
      if (html) {
        const m = html.match(/<img[^>]+src=["']([^"']+)["']/i);
        if (m && m[1] && m[1].startsWith('http')) {
          e.preventDefault();
          e.stopPropagation();
          setLogo(m[1]);
          return;
        }
      }
    };
    document.addEventListener('paste', handlePaste, true);
    return () => document.removeEventListener('paste', handlePaste, true);
  }, []);

  // Google Fonts loading
  useEffect(() => {
    const link = document.createElement('link');
    link.href = 'https://fonts.googleapis.com/css2?family=Bebas+Neue&family=Oswald:wght@700&family=Anton&family=Teko:wght@700&family=Black+Ops+One&family=Rubik+Mono+One&family=Bungee&family=Permanent+Marker&family=Russo+One&family=Righteous&family=Bangers&display=swap';
    link.rel = 'stylesheet';
    document.head.appendChild(link);
  }, []);

  /* ── Music helpers ── */
  const uploadMusic = async (files) => {
    const total = files.length;
    setMusicUpload({ done: 0, total, current: files[0]?.name || "" });
    for (let i = 0; i < total; i++) {
      setMusicUpload({ done: i, total, current: files[i].name });
      try {
        const fd = new FormData();
        fd.append("files", files[i]);
        await fetch("/api/music", { method: "POST", body: fd });
      } catch (e) { console.error("Upload error:", files[i].name, e); }
    }
    setMusicUpload(null);
    try {
      const r = await fetch("/api/music");
      const d = await r.json();
      if (d.files) setMusic(d.files.map((f, i) => ({ id: i, name: f.name, url: f.url })));
    } catch (e) { console.error(e); }
  };

  const deleteTrack = async (name) => {
    try {
      await fetch(`/api/music/${encodeURIComponent(name)}`, { method: "DELETE" });
      setMusic(p => p.filter(m => m.name !== name));
    } catch (e) { console.error(e); }
  };

  /* ── Generate text ── */
  const gen = () => {
    const w = genW(casino, state, wc);
    setText(buildTxt(casino, state, w, bonus, spins));
  };

  /* ── Background search ── */
  const searchBg = async (query) => {
    if (!query.trim()) return;
    setBgLoading(true);
    setBgResults([]);
    try {
      const r = await fetch(`/api/images?q=${encodeURIComponent(query)}`);
      const d = await r.json();
      setBgResults(d.images || []);
    } catch (e) { console.error(e); setBgResults([]); }
    setBgLoading(false);
  };

  const toggleBg = (url) => {
    setBgList(p => p.includes(url) ? p.filter(x => x !== url) : [...p, url]);
    if (!bg) setBg(url);
  };

  /* ── Decorations search ── */
  const searchDeco = async (query) => {
    if (!query.trim()) return;
    setDecoLoading(true);
    setDecoResults([]);
    try {
      const r = await fetch(`/api/images?q=${encodeURIComponent(query + " png transparent")}`);
      const d = await r.json();
      setDecoResults(d.images || []);
    } catch (e) { console.error(e); setDecoResults([]); }
    setDecoLoading(false);
  };

  const [decoProxying, setDecoProxying] = useState({});
  const [decoError, setDecoError] = useState("");
  const toggleDeco = async (url) => {
    // If already in list (by original URL), remove
    if (decoList.find(d => d.orig === url)) {
      setDecoList(p => p.filter(d => d.orig !== url));
      return;
    }
    // Proxy-download through server
    setDecoProxying(p => ({ ...p, [url]: true }));
    try {
      const r = await fetch('/api/proxy-image', {
        method: 'POST', headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ url })
      });
      const d = await r.json();
      if (d.ok) {
        setDecoList(p => [...p, { orig: url, proxy: d.url }]);
        setDecoError("");
      } else {
        setDecoError('Не удалось загрузить: ' + (d.error || 'неизвестная ошибка'));
      }
    } catch (e) { setDecoError('Ошибка сети: ' + e.message); }
    setDecoProxying(p => { const n = { ...p }; delete n[url]; return n; });
  };

  // Upload decoration PNG files
  const uploadDecoFiles = async (files) => {
    const fd = new FormData();
    for (const f of files) fd.append('files', f);
    try {
      const r = await fetch('/api/decorations', { method: 'POST', body: fd });
      const d = await r.json();
      if (d.ok && d.files) {
        setDecoUploaded(prev => {
          const existing = new Set(prev.map(p => p.name));
          const newOnes = d.files.filter(f => !existing.has(f.name));
          return [...prev, ...newOnes];
        });
      }
    } catch (e) { setDecoError('Ошибка загрузки: ' + e.message); }
  };

  // Toggle uploaded decoration selection
  const toggleUploadedDeco = (deco) => {
    const found = decoList.find(d => d.orig === deco.url);
    if (found) {
      setDecoList(p => p.filter(d => d.orig !== deco.url));
    } else {
      setDecoList(p => [...p, { orig: deco.url, proxy: deco.url }]);
    }
  };

  // Delete uploaded decoration
  const deleteUploadedDeco = async (deco) => {
    try {
      await fetch(`/api/decorations/${encodeURIComponent(deco.name)}`, { method: 'DELETE' });
      setDecoUploaded(p => p.filter(d => d.name !== deco.name));
      setDecoList(p => p.filter(d => d.orig !== deco.url));
    } catch (e) { console.error(e); }
  };

  /* ── Variants ── */
  const genVars = (count) => {
    const c = count || cc;
    const v = [];
    for (let i = 0; i < c; i++) {
      const w = genW(casino, state, wc);
      const bgPick = bgList.length > 0 ? bgList[i % bgList.length] : bg;
      v.push({ id: i + 1, text: buildTxt(casino, state, w, bonus, spins), bg: bgPick });
    }
    setVars(v);
    setShowV(true);
    return v;
  };

  /* ── Render ── */
  const startRender = async () => {
    let v = vars;
    if (!v.length) {
      v = genVars(cc);
    }

    // Check logo upload status
    if (logoUploading) {
      setError('Лого ещё загружается, подождите секунду и попробуй снова');
      return;
    }
    if (logo && !logoServer) {
      setError(logoUploadErr || 'Лого не удалось загрузить на сервер. Попробуй вставить через Ctrl+V или использовать другой URL.');
      return;
    }
    const renderLogoUrl = logoServer || '';

    const musicList = selTracks.length ? selTracks : music.map(m => m.name);
    const configs = v.map((variant, i) => ({
      casino,
      text: variant.text,
      logoUrl: renderLogoUrl,
      bgUrl: variant.bg || bg,
      font,
      blur,
      brightness: bright,
      overlay,
      logoSize,
      fs,
      hs,
      ctaFs,
      animType: decoList.length > 0 ? 'images' : 'none',
      animSpeed: animSpeed / 100,
      animCount,
      animSize: animSize / 100,
      particleUrls: decoList.length > 0 ? decoList.map(d => d.proxy) : undefined,
      music_name: musicList.length ? musicList[i % musicList.length] : undefined,
    }));

    setRenderJob({ status: "running", done: 0, total: configs.length, startTime: Date.now() });

    try {
      const resp = await fetch('/api/render', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ configs })
      });
      const { jobId } = await resp.json();

      if (!jobId) {
        setRenderJob({ status: "error", error: "No jobId returned" });
        return;
      }

      setRenderJob(prev => ({ ...prev, jobId }));

      const poll = setInterval(async () => {
        try {
          const r = await fetch(`/api/render/${jobId}`);
          const data = await r.json();
          if (data.status === 'done') {
            clearInterval(poll);
            setRenderJob(prev => ({
              ...prev,
              status: "done",
              done: data.progress || data.total,
              total: data.total,
              results: data.files.map((f, idx) => ({ id: idx + 1, filename: f.filename, url: `/api/download/${f.filename}` })),
              zip_url: `/api/zip/${jobId}`,
              elapsed: Math.round((Date.now() - prev.startTime) / 1000)
            }));
          } else if (data.status === 'error') {
            clearInterval(poll);
            setRenderJob({ status: "error", error: data.error || "Unknown error" });
          } else {
            setRenderJob(prev => ({ ...prev, done: data.progress || 0 }));
          }
        } catch (e) { /* ignore poll errors */ }
      }, 3000);
    } catch (e) {
      setRenderJob({ status: "error", error: e.message });
    }
  };

  /* ── Shared styles ── */
  const S = {
    i: {
      width: "100%", padding: "10px 12px", fontSize: 14, boxSizing: "border-box",
      background: "rgba(255,255,255,0.06)", border: "1px solid rgba(255,215,0,0.2)",
      borderRadius: 8, color: "#fff", outline: "none"
    },
    l: { fontSize: 10, color: "#FFD700", textTransform: "uppercase", letterSpacing: 1.5, fontWeight: 700 },
    l2: { fontSize: 10, color: "#888", textTransform: "uppercase", letterSpacing: 1 },
    btn: {
      padding: "9px 16px", fontSize: 13, fontWeight: 700, border: "none", borderRadius: 8, cursor: "pointer"
    }
  };

  /* ── Elapsed timer ── */
  const [elapsed, setElapsed] = useState(0);
  useEffect(() => {
    if (renderJob?.status === "running" && renderJob?.startTime) {
      const iv = setInterval(() => setElapsed(Math.round((Date.now() - renderJob.startTime) / 1000)), 1000);
      return () => clearInterval(iv);
    }
    setElapsed(0);
  }, [renderJob?.status, renderJob?.startTime]);

  /* ───────────────────── RENDER ───────────────────── */
  return (
    <div style={{
      minHeight: "100vh",
      background: "linear-gradient(135deg,#0a0a0f,#1a1025 50%,#0d1117)",
      color: "#e0e0e0",
      fontFamily: "'JetBrains Mono','SF Mono',monospace"
    }}>
      {/* Header */}
      <div style={{
        padding: "14px 20px", borderBottom: "1px solid rgba(255,215,0,0.15)",
        background: "rgba(0,0,0,0.4)", display: "flex", alignItems: "center", gap: 12
      }}>
        <div style={{
          width: 38, height: 38, borderRadius: 10,
          background: "linear-gradient(135deg,#FFD700,#FF6B00)",
          display: "flex", alignItems: "center", justifyContent: "center",
          fontSize: 18, fontWeight: "bold", color: "#000"
        }}>CF</div>
        <div>
          <div style={{ fontSize: 17, fontWeight: 800, color: "#FFD700", letterSpacing: 1 }}>CREO FACTORY</div>
          <div style={{ fontSize: 10, color: "#888" }}>Генератор креативов v2</div>
        </div>
      </div>

      {/* Two-column layout */}
      <div style={{ display: "flex", minHeight: "calc(100vh - 66px)" }}>

        {/* LEFT COLUMN: Preview (55%) */}
        <div style={{
          flex: "0 0 55%", display: "flex", flexDirection: "column", alignItems: "center",
          justifyContent: "center", padding: 24, borderRight: "1px solid rgba(255,255,255,0.06)"
        }}>
          <div style={{ fontSize: 10, color: "#888", marginBottom: 10, textTransform: "uppercase", letterSpacing: 1.5, fontWeight: 700 }}>
            Превью креатива
          </div>
          <Preview
            casino={casino} text={text} logoUrl={logo} bgUrl={bg}
            fs={fs} hs={hs} blur={blur} bright={bright} overlay={overlay}
            font={font} logoSize={logoSize} ctaFs={ctaFs}
          />
          <div style={{ fontSize: 10, color: "#555", marginTop: 8 }}>
            1080x1350 (4:5) | Шрифт: {font} | Блюр: {blur}px | Яркость: {bright}%
          </div>
        </div>

        {/* RIGHT COLUMN: Tabs (45%) */}
        <div style={{ flex: "0 0 45%", display: "flex", flexDirection: "column", overflow: "hidden" }}>

          {/* Tab bar */}
          <div style={{
            display: "flex", borderBottom: "1px solid rgba(255,255,255,0.06)", flexShrink: 0
          }}>
            {[["text", "Текст"], ["media", "Медиа"], ["style", "Стиль"], ["deco", "Украшения"], ["render", "Рендер"]].map(([id, lb]) => (
              <div key={id} onClick={() => setTab(id)} style={{
                flex: 1, padding: "13px 14px", fontSize: 13, fontWeight: 700, cursor: "pointer",
                color: tab === id ? "#FFD700" : "#666",
                borderBottom: tab === id ? "2px solid #FFD700" : "2px solid transparent",
                textAlign: "center", transition: "all 0.2s", textTransform: "uppercase", letterSpacing: 1
              }}>{lb}</div>
            ))}
          </div>

          {/* Tab content */}
          <div style={{ flex: 1, padding: "16px 20px", overflowY: "auto" }}>

            {/* ──── TEXT TAB ──── */}
            {tab === "text" && (
              <div>
                <div style={{ display: "flex", gap: 10, flexWrap: "wrap", marginBottom: 12 }}>
                  <div style={{ flex: 1, minWidth: 150 }}>
                    <label style={S.l}>Название казино</label>
                    <input value={casino} onChange={e => setCasino(e.target.value)} style={{ ...S.i, marginTop: 6 }} />
                  </div>
                  <div style={{ flex: 1, minWidth: 150 }}>
                    <label style={S.l}>Штат / ГЕО</label>
                    <select value={state} onChange={e => setState(e.target.value)} style={{ ...S.i, marginTop: 6 }}>
                      {Object.keys(STATES_DATA).map(s => (
                        <option key={s} value={s} style={{ background: "#1a1a2e" }}>{s}</option>
                      ))}
                    </select>
                  </div>
                </div>

                <div style={{ display: "flex", gap: 8, marginBottom: 12, flexWrap: "wrap" }}>
                  {[["Бонус", bonus, setBonus], ["Фри спины", spins, setSpins]].map(([l, v, fn]) => (
                    <div key={l} style={{ flex: 1, minWidth: 90 }}>
                      <label style={S.l2}>{l}</label>
                      <input value={v} onChange={e => fn(e.target.value)}
                        style={{ ...S.i, marginTop: 4, fontSize: 13, padding: "8px 10px", border: "1px solid rgba(255,255,255,0.1)" }} />
                    </div>
                  ))}
                  <div style={{ flex: 1, minWidth: 90 }}>
                    <label style={S.l2}>Виннеры</label>
                    <input type="number" value={wc} onChange={e => setWc(+e.target.value || 10)} min={3} max={15}
                      style={{ ...S.i, marginTop: 4, fontSize: 13, padding: "8px 10px", border: "1px solid rgba(255,255,255,0.1)" }} />
                  </div>
                </div>

                <div style={{ display: "flex", gap: 8, marginBottom: 12 }}>
                  <button onClick={gen} style={{
                    ...S.btn, background: "linear-gradient(135deg,#FFD700,#FF8C00)", color: "#000"
                  }}>Сгенерировать текст</button>
                </div>

                <textarea value={text} onChange={e => setText(e.target.value)} rows={14}
                  style={{
                    width: "100%", padding: 10, fontSize: 12, lineHeight: 1.5, background: "rgba(0,0,0,0.4)",
                    border: "1px solid rgba(255,255,255,0.1)", borderRadius: 8, color: "#ddd", outline: "none",
                    resize: "vertical", fontFamily: "monospace", boxSizing: "border-box"
                  }}
                  placeholder="Нажми 'Сгенерировать текст' или вставь текст сюда..." />

                <div style={{ marginTop: 8, fontSize: 10, color: "#666" }}>
                  Виннеры: {fs}% | Заголовок: {hs}% | CTA: {ctaFs}% | Лого: {logoSize}%
                </div>
              </div>
            )}

            {/* ──── MEDIA TAB ──── */}
            {tab === "media" && (
              <div>
                {/* Logo section */}
                <div style={{ marginBottom: 20 }}>
                  <div style={{ fontSize: 13, fontWeight: 700, color: "#FFD700", marginBottom: 8 }}>Логотип</div>
                  <label style={{
                    display: "flex", alignItems: "center", justifyContent: "center", padding: 16, borderRadius: 10,
                    cursor: "pointer", background: "rgba(255,215,0,0.04)", border: "2px dashed rgba(255,215,0,0.2)", marginBottom: 10
                  }}>
                    <input type="file" accept="image/*" onChange={e => {
                      const f = e.target.files[0];
                      if (f) { const r = new FileReader(); r.onload = ev => setLogo(ev.target.result); r.readAsDataURL(f); }
                    }} style={{ display: "none" }} />
                    <span style={{ fontSize: 13, color: "#FFD700" }}>Загрузить лого (PNG/JPG)</span>
                  </label>
                  <div style={{ fontSize: 11, color: "#666", marginBottom: 6 }}>Или вставь URL:</div>
                  <input value={logo.startsWith("data:") ? "" : logo} onChange={e => setLogo(e.target.value)}
                    placeholder="https://..." style={S.i} />
                  <div style={{ fontSize: 10, color: "#555", marginTop: 4 }}>Ctrl+V для вставки из буфера</div>
                  <div style={{
                    marginTop: 10, padding: 16, borderRadius: 10, textAlign: "center", minHeight: 70,
                    background: "rgba(255,215,0,0.04)", border: "1px dashed rgba(255,215,0,0.2)"
                  }}>
                    {logo
                      ? <div style={{ position: "relative", display: "inline-block" }}>
                          <img src={logo} style={{ maxHeight: 80, maxWidth: "90%" }} onError={e => { e.target.alt = "Load error"; }} />
                          <button onClick={() => { setLogo(""); setLogoServer(""); }} style={{
                            position: "absolute", top: -6, right: -6, width: 20, height: 20, borderRadius: 10,
                            background: "rgba(255,0,0,0.85)", color: "#fff", fontSize: 11, fontWeight: 900,
                            border: "none", cursor: "pointer", display: "flex", alignItems: "center", justifyContent: "center"
                          }}>x</button>
                        </div>
                      : <span style={{ color: "#555" }}>Нет лого</span>
                    }
                    {logoUploading && <div style={{ fontSize: 11, color: "#FFD700", marginTop: 4 }}>⏳ Загружаем на сервер...</div>}
                    {!logoUploading && logoServer && <div style={{ fontSize: 11, color: "#00FF88", marginTop: 4 }}>✓ Загружено, готово к рендеру</div>}
                    {!logoUploading && logoUploadErr && <div style={{ fontSize: 11, color: "#FF4444", marginTop: 4 }}>{logoUploadErr}</div>}
                  </div>
                </div>

                {/* Background section */}
                <div style={{ marginBottom: 20 }}>
                  <div style={{ fontSize: 13, fontWeight: 700, color: "#4ECDC4", marginBottom: 8 }}>Фон</div>
                  <div style={{ display: "flex", gap: 6, marginBottom: 10 }}>
                    <input value={bgSearch} onChange={e => setBgSearch(e.target.value)}
                      onKeyDown={e => e.key === "Enter" && searchBg(bgSearch || casino + " building exterior")}
                      placeholder={`${casino} building exterior`}
                      style={{ ...S.i, flex: 1 }} />
                    <button onClick={() => searchBg(bgSearch || casino + " building exterior")}
                      disabled={bgLoading}
                      style={{
                        ...S.btn, background: bgLoading ? "#333" : "linear-gradient(135deg,#4ECDC4,#45B7AA)",
                        color: "#000", whiteSpace: "nowrap"
                      }}>
                      {bgLoading ? "Ищем..." : "Поиск"}
                    </button>
                  </div>

                  {bgResults.length > 0 && (
                    <div style={{ marginBottom: 10 }}>
                      <div style={{ fontSize: 11, color: "#888", marginBottom: 6 }}>Нажми для выбора (несколько = варианты):</div>
                      <div style={{
                        display: "grid", gridTemplateColumns: "repeat(auto-fill,minmax(95px,1fr))", gap: 6,
                        maxHeight: 200, overflowY: "auto", padding: 2
                      }}>
                        {bgResults.map((img, i) => {
                          const sel = bgList.includes(img.url);
                          return (
                            <div key={i} onClick={() => toggleBg(img.url)} style={{
                              cursor: "pointer", borderRadius: 8, overflow: "hidden", position: "relative",
                              border: sel ? "3px solid #4ECDC4" : "3px solid rgba(255,255,255,0.1)",
                              opacity: sel ? 1 : 0.65, transition: "all 0.15s"
                            }}>
                              <img src={img.url} alt="" style={{
                                width: "100%", height: 70, objectFit: "cover", display: "block", background: "#111"
                              }} onError={e => { e.target.parentElement.style.display = "none"; }} />
                              {sel && <div style={{
                                position: "absolute", top: 3, right: 3, width: 18, height: 18, borderRadius: 9,
                                background: "#4ECDC4", color: "#000", fontSize: 11, fontWeight: 900,
                                display: "flex", alignItems: "center", justifyContent: "center"
                              }}>ok</div>}
                            </div>
                          );
                        })}
                      </div>
                    </div>
                  )}

                  {bgList.length > 0 && (
                    <div>
                      <div style={{ fontSize: 11, color: "#4ECDC4", marginBottom: 6 }}>Выбрано: {bgList.length} фонов</div>
                      <div style={{ display: "flex", gap: 6, flexWrap: "wrap", marginBottom: 10 }}>
                        {bgList.map((url, i) => (
                          <div key={i} style={{ position: "relative", borderRadius: 6, overflow: "hidden", border: "2px solid #4ECDC4", width: 60, height: 50 }}>
                            <img src={url} style={{ width: "100%", height: "100%", objectFit: "cover", display: "block" }} />
                            <div onClick={e => { e.stopPropagation(); setBgList(p => p.filter(x => x !== url)); if (bg === url) setBg(bgList.find(x => x !== url) || ""); }}
                              style={{
                                position: "absolute", top: 1, right: 1, width: 16, height: 16, borderRadius: 8,
                                background: "rgba(255,0,0,0.85)", color: "#fff", fontSize: 10, fontWeight: 900,
                                display: "flex", alignItems: "center", justifyContent: "center", cursor: "pointer"
                              }}>x</div>
                          </div>
                        ))}
                      </div>
                    </div>
                  )}

                  <div style={{ borderTop: "1px solid rgba(255,255,255,0.06)", paddingTop: 8, marginTop: 4 }}>
                    <div style={{ fontSize: 11, color: "#666", marginBottom: 4 }}>Или вставь URL вручную:</div>
                    <div style={{ display: "flex", gap: 6 }}>
                      <input value={bg} onChange={e => setBg(e.target.value)} placeholder="https://..." style={{ ...S.i, flex: 1, fontSize: 12 }} />
                      {bg && !bgList.includes(bg) && (
                        <button onClick={() => toggleBg(bg)}
                          style={{
                            padding: "8px 12px", fontSize: 12, fontWeight: 700,
                            background: "rgba(255,215,0,0.15)", color: "#FFD700",
                            border: "1px solid rgba(255,215,0,0.3)", borderRadius: 8, cursor: "pointer"
                          }}>+</button>
                      )}
                    </div>
                  </div>

                  {bg && (
                    <div style={{ marginTop: 10, borderRadius: 8, overflow: "hidden", border: "1px solid rgba(255,255,255,0.1)", maxHeight: 120 }}>
                      <img src={bg} style={{ width: "100%", height: 100, objectFit: "cover", display: "block" }} />
                    </div>
                  )}
                </div>

                {/* Music section */}
                <div>
                  <div style={{ fontSize: 13, fontWeight: 700, color: "#FF6B6B", marginBottom: 8 }}>Музыка</div>
                  <label style={{
                    display: "flex", alignItems: "center", justifyContent: "center", padding: 16, borderRadius: 10,
                    cursor: "pointer", background: "rgba(255,107,107,0.06)", border: "2px dashed rgba(255,107,107,0.25)", marginBottom: 10
                  }}>
                    <input type="file" accept="audio/*" multiple onChange={e => uploadMusic(Array.from(e.target.files))} style={{ display: "none" }} />
                    <span style={{ fontSize: 13, color: "#FF6B6B" }}>{musicUpload ? "Загружается..." : "Загрузить MP3 (несколько файлов)"}</span>
                  </label>

                  {musicUpload && (
                    <div style={{ marginBottom: 12, padding: 10, borderRadius: 8, background: "rgba(255,107,107,0.06)", border: "1px solid rgba(255,107,107,0.15)" }}>
                      <div style={{ display: "flex", justifyContent: "space-between", marginBottom: 6 }}>
                        <span style={{ fontSize: 12, color: "#FF6B6B", fontWeight: 700 }}>
                          Загрузка: {musicUpload.done + 1}/{musicUpload.total}
                        </span>
                        <span style={{ fontSize: 10, color: "#888" }}>
                          {Math.round(((musicUpload.done) / musicUpload.total) * 100)}%
                        </span>
                      </div>
                      <div style={{ height: 6, borderRadius: 3, background: "rgba(255,255,255,0.06)", overflow: "hidden", marginBottom: 4 }}>
                        <div style={{
                          height: "100%", borderRadius: 3, transition: "width 0.3s",
                          background: "linear-gradient(90deg,#FF6B6B,#FF8C00)",
                          width: `${(musicUpload.done / musicUpload.total) * 100}%`
                        }} />
                      </div>
                      <div style={{ fontSize: 10, color: "#666", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>
                        {musicUpload.current}
                      </div>
                    </div>
                  )}

                  {music.length > 0 && (
                    <div>
                      <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: 6 }}>
                        <span style={{ fontSize: 11, color: "#888" }}>Треки: {music.length}</span>
                        <span style={{ fontSize: 10, color: selTracks.length ? "#4ECDC4" : "#666" }}>
                          {selTracks.length ? `Выбрано: ${selTracks.length}` : "Рандом при рендере"}
                        </span>
                      </div>
                      {music.map(m => {
                        const isSel = selTracks.includes(m.name);
                        const isPlay = playingId === m.id;
                        return (
                          <div key={m.id} style={{
                            display: "flex", alignItems: "center", gap: 8, padding: "6px 10px", margin: "3px 0", borderRadius: 7,
                            background: isSel ? "rgba(78,205,196,0.12)" : "rgba(255,107,107,0.05)",
                            border: isSel ? "1px solid rgba(78,205,196,0.3)" : "1px solid rgba(255,255,255,0.05)",
                            transition: "all 0.15s"
                          }}>
                            <button onClick={() => {
                              if (isPlay) { audioRef.current?.pause(); setPlayingId(null); }
                              else {
                                if (audioRef.current) audioRef.current.pause();
                                const a = new Audio(m.url);
                                a.onended = () => setPlayingId(null);
                                a.play();
                                audioRef.current = a;
                                setPlayingId(m.id);
                              }
                            }} style={{
                              width: 28, height: 28, borderRadius: 14, border: "none", cursor: "pointer", fontSize: 13,
                              background: isPlay ? "rgba(255,107,107,0.3)" : "rgba(255,255,255,0.08)", color: "#fff",
                              display: "flex", alignItems: "center", justifyContent: "center"
                            }}>
                              {isPlay ? "||" : ">"}
                            </button>
                            <div style={{ flex: 1, fontSize: 11, color: isSel ? "#4ECDC4" : "#ccc", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>
                              {m.name}
                            </div>
                            <button onClick={() => setSelTracks(p => isSel ? p.filter(x => x !== m.name) : [...p, m.name])}
                              style={{
                                padding: "3px 8px", fontSize: 10, borderRadius: 4, border: "none", cursor: "pointer",
                                background: isSel ? "rgba(78,205,196,0.25)" : "rgba(255,255,255,0.06)",
                                color: isSel ? "#4ECDC4" : "#888"
                              }}>
                              {isSel ? "Выбрано" : "Выбрать"}
                            </button>
                            <button onClick={() => {
                              if (isPlay) { audioRef.current?.pause(); setPlayingId(null); }
                              setSelTracks(p => p.filter(x => x !== m.name));
                              deleteTrack(m.name);
                            }} style={{ fontSize: 12, color: "#555", background: "none", border: "none", cursor: "pointer", padding: 2 }}>
                              x
                            </button>
                          </div>
                        );
                      })}
                      <div style={{ marginTop: 8, fontSize: 10, color: "#666", lineHeight: 1.6 }}>
                        Треки хранятся на сервере. Выбранные треки чередуются в креативах. Без выбора = рандом.
                      </div>
                    </div>
                  )}
                  {music.length === 0 && (
                    <div style={{ fontSize: 11, color: "#555", textAlign: "center", padding: 10 }}>
                      Загрузи MP3 файлы для генерации видео
                    </div>
                  )}
                </div>
              </div>
            )}

            {/* ──── STYLE TAB ──── */}
            {tab === "style" && (
              <div>
                <div style={{ fontSize: 14, fontWeight: 700, color: "#FFD700", marginBottom: 14 }}>Настройки стиля</div>

                {/* Font selector */}
                <div style={{ marginBottom: 16 }}>
                  <label style={S.l2}>Шрифт</label>
                  <div style={{ display: "grid", gridTemplateColumns: "repeat(2,1fr)", gap: 6, marginTop: 6 }}>
                    {FONTS.map(f => (
                      <div key={f.name} onClick={() => setFont(f.name)} style={{
                        padding: "8px 10px", borderRadius: 8, cursor: "pointer", textAlign: "center",
                        background: font === f.name ? "rgba(255,215,0,0.12)" : "rgba(255,255,255,0.03)",
                        border: font === f.name ? "1px solid rgba(255,215,0,0.4)" : "1px solid rgba(255,255,255,0.06)",
                        transition: "all 0.15s"
                      }}>
                        <div style={{
                          fontFamily: f.family + ",sans-serif", fontSize: 14, fontWeight: f.w || 700,
                          color: font === f.name ? "#FFD700" : "#aaa"
                        }}>{f.name}</div>
                      </div>
                    ))}
                  </div>
                </div>

                {/* Sliders */}
                {[
                  [fs, setFs, "Размер виннеров", "#FFD700", 70, 140, "%"],
                  [hs, setHs, "Размер заголовка", "#4ECDC4", 70, 140, "%"],
                  [ctaFs, setCtaFs, "Размер CTA", "#FF6B6B", 70, 140, "%"],
                  [logoSize, setLogoSize, "Размер лого", "#a78bfa", 50, 200, "%"],
                  [blur, setBlur, "Блюр", "#FF6B6B", 0, 20, "px"],
                  [bright, setBright, "Яркость", "#FFA500", 10, 100, "%"],
                  [overlay, setOverlay, "Затемнение", "#888", 0, 80, "%"]
                ].map(([v, fn, l, c, mn, mx, u]) => (
                  <div key={l} style={{ marginBottom: 12 }}>
                    <div style={{ display: "flex", justifyContent: "space-between", marginBottom: 3 }}>
                      <label style={S.l2}>{l}</label>
                      <span style={{ fontSize: 12, color: c, fontWeight: 700 }}>{v}{u}</span>
                    </div>
                    <input type="range" min={mn} max={mx} value={v} onChange={e => fn(+e.target.value)}
                      style={{ width: "100%", accentColor: c }} />
                  </div>
                ))}
              </div>
            )}

            {/* ──── DECORATIONS TAB ──── */}
            {tab === "deco" && (
              <div>
                <div style={{ fontSize: 14, fontWeight: 700, color: "#e879f9", marginBottom: 14 }}>Украшения (падающие элементы)</div>

                {/* Error display */}
                {decoError && (
                  <div style={{ padding: 8, marginBottom: 10, borderRadius: 6, background: "rgba(255,0,0,0.1)", border: "1px solid rgba(255,0,0,0.2)", fontSize: 11, color: "#f66" }}>
                    {decoError}
                    <span onClick={() => setDecoError("")} style={{ marginLeft: 8, cursor: "pointer", color: "#888" }}>x</span>
                  </div>
                )}

                {/* Upload own PNGs */}
                <div style={{ marginBottom: 14 }}>
                  <div style={{ fontSize: 12, fontWeight: 700, color: "#e879f9", marginBottom: 6 }}>Загрузить свои PNG</div>
                  <label style={{
                    display: "flex", alignItems: "center", justifyContent: "center", padding: 14, borderRadius: 10,
                    cursor: "pointer", background: "rgba(232,121,249,0.06)", border: "2px dashed rgba(232,121,249,0.25)", marginBottom: 8
                  }}>
                    <input type="file" accept="image/png,image/jpeg,image/webp" multiple onChange={e => {
                      if (e.target.files.length) uploadDecoFiles(Array.from(e.target.files));
                    }} style={{ display: "none" }} />
                    <span style={{ fontSize: 12, color: "#e879f9" }}>Выбрать PNG/JPG файлы (несколько)</span>
                  </label>

                  {/* Uploaded decorations grid */}
                  {decoUploaded.length > 0 && (
                    <div>
                      <div style={{ fontSize: 11, color: "#888", marginBottom: 6 }}>Загруженные ({decoUploaded.length}). Нажми для выбора:</div>
                      <div style={{
                        display: "grid", gridTemplateColumns: "repeat(auto-fill,minmax(64px,1fr))", gap: 6,
                        maxHeight: 180, overflowY: "auto", padding: 2
                      }}>
                        {decoUploaded.map((d, i) => {
                          const sel = decoList.find(x => x.orig === d.url);
                          return (
                            <div key={i} onClick={() => toggleUploadedDeco(d)} style={{
                              cursor: "pointer", borderRadius: 6, overflow: "hidden", position: "relative",
                              border: sel ? "3px solid #e879f9" : "3px solid rgba(255,255,255,0.1)",
                              opacity: sel ? 1 : 0.6, transition: "all 0.15s",
                              background: "repeating-conic-gradient(#222 0% 25%, #333 0% 50%) 50% / 12px 12px"
                            }}>
                              <img src={d.url} alt={d.name} style={{ width: "100%", height: 56, objectFit: "contain", display: "block" }} />
                              {sel && <div style={{
                                position: "absolute", top: 2, right: 2, width: 16, height: 16, borderRadius: 8,
                                background: "#e879f9", color: "#000", fontSize: 10, fontWeight: 900,
                                display: "flex", alignItems: "center", justifyContent: "center"
                              }}>ok</div>}
                              <div onClick={e => { e.stopPropagation(); deleteUploadedDeco(d); }} style={{
                                position: "absolute", top: 2, left: 2, width: 16, height: 16, borderRadius: 8,
                                background: "rgba(255,0,0,0.8)", color: "#fff", fontSize: 10, fontWeight: 900,
                                display: "flex", alignItems: "center", justifyContent: "center", cursor: "pointer"
                              }}>x</div>
                            </div>
                          );
                        })}
                      </div>
                    </div>
                  )}
                </div>

                {/* Google search (secondary) */}
                <div style={{ borderTop: "1px solid rgba(255,255,255,0.06)", paddingTop: 12, marginBottom: 10 }}>
                  <div style={{ fontSize: 12, fontWeight: 700, color: "#a855f7", marginBottom: 6 }}>Или найти через Google</div>
                  <div style={{ display: "flex", gap: 6, marginBottom: 8 }}>
                    <input value={decoSearch} onChange={e => setDecoSearch(e.target.value)}
                      onKeyDown={e => e.key === "Enter" && searchDeco(decoSearch)}
                      placeholder="dollar png, slot symbols, coins..."
                      style={{ ...S.i, flex: 1 }} />
                    <button onClick={() => searchDeco(decoSearch)}
                      disabled={decoLoading}
                      style={{
                        ...S.btn, background: decoLoading ? "#333" : "linear-gradient(135deg,#e879f9,#a855f7)",
                        color: "#000", whiteSpace: "nowrap"
                      }}>
                      {decoLoading ? "Ищем..." : "Поиск"}
                    </button>
                  </div>
                  <div style={{ display: "flex", gap: 6, flexWrap: "wrap", marginBottom: 10 }}>
                    {["dollar bill", "gold coins", "slot machine symbols", "casino chips", "playing cards", "money rain", "diamond gem"].map(q => (
                      <button key={q} onClick={() => { setDecoSearch(q); searchDeco(q); }} style={{
                        padding: "5px 10px", fontSize: 10, borderRadius: 6, cursor: "pointer",
                        background: "rgba(232,121,249,0.08)", color: "#e879f9",
                        border: "1px solid rgba(232,121,249,0.2)"
                      }}>{q}</button>
                    ))}
                  </div>
                </div>

                {/* Search results */}
                {decoResults.length > 0 && (
                  <div style={{ marginBottom: 12 }}>
                    <div style={{ fontSize: 11, color: "#888", marginBottom: 6 }}>Нажми для выбора:</div>
                    <div style={{
                      display: "grid", gridTemplateColumns: "repeat(auto-fill,minmax(80px,1fr))", gap: 6,
                      maxHeight: 220, overflowY: "auto", padding: 2
                    }}>
                      {decoResults.map((img, i) => {
                        const sel = decoList.find(d => d.orig === img.url);
                        const loading = decoProxying[img.url];
                        return (
                          <div key={i} onClick={() => !loading && toggleDeco(img.url)} style={{
                            cursor: loading ? "wait" : "pointer", borderRadius: 8, overflow: "hidden", position: "relative",
                            border: sel ? "3px solid #e879f9" : "3px solid rgba(255,255,255,0.1)",
                            opacity: loading ? 0.4 : sel ? 1 : 0.6, transition: "all 0.15s",
                            background: "repeating-conic-gradient(#222 0% 25%, #333 0% 50%) 50% / 16px 16px"
                          }}>
                            <img src={img.url} alt="" style={{
                              width: "100%", height: 70, objectFit: "contain", display: "block"
                            }} onError={e => { e.target.parentElement.style.display = "none"; }} />
                            {sel && <div style={{
                              position: "absolute", top: 3, right: 3, width: 18, height: 18, borderRadius: 9,
                              background: "#e879f9", color: "#000", fontSize: 11, fontWeight: 900,
                              display: "flex", alignItems: "center", justifyContent: "center"
                            }}>ok</div>}
                          </div>
                        );
                      })}
                    </div>
                  </div>
                )}

                {/* Selected decorations */}
                {decoList.length > 0 && (
                  <div style={{ marginBottom: 14 }}>
                    <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: 6 }}>
                      <span style={{ fontSize: 11, color: "#e879f9" }}>Выбрано: {decoList.length} картинок</span>
                      <button onClick={() => setDecoList([])} style={{
                        padding: "3px 8px", fontSize: 10, borderRadius: 4, border: "none", cursor: "pointer",
                        background: "rgba(255,0,0,0.15)", color: "#f66"
                      }}>Очистить все</button>
                    </div>
                    <div style={{ display: "flex", gap: 6, flexWrap: "wrap" }}>
                      {decoList.map((d, i) => (
                        <div key={i} style={{
                          position: "relative", borderRadius: 6, overflow: "hidden",
                          border: "2px solid #e879f9", width: 54, height: 54,
                          background: "repeating-conic-gradient(#222 0% 25%, #333 0% 50%) 50% / 12px 12px"
                        }}>
                          <img src={d.proxy} style={{ width: "100%", height: "100%", objectFit: "contain", display: "block" }} />
                          <div onClick={e => { e.stopPropagation(); setDecoList(p => p.filter(x => x.orig !== d.orig)); }}
                            style={{
                              position: "absolute", top: 1, right: 1, width: 16, height: 16, borderRadius: 8,
                              background: "rgba(255,0,0,0.85)", color: "#fff", fontSize: 10, fontWeight: 900,
                              display: "flex", alignItems: "center", justifyContent: "center", cursor: "pointer"
                            }}>x</div>
                        </div>
                      ))}
                    </div>
                  </div>
                )}

                {decoList.length === 0 && !decoResults.length && decoUploaded.length === 0 && (
                  <div style={{
                    padding: 16, borderRadius: 10, textAlign: "center",
                    background: "rgba(232,121,249,0.04)", border: "2px dashed rgba(232,121,249,0.15)",
                    marginBottom: 14
                  }}>
                    <div style={{ fontSize: 12, color: "#888", marginBottom: 4 }}>Без украшений = чистое видео без падающих элементов</div>
                    <div style={{ fontSize: 10, color: "#666" }}>Загрузи свои PNG или найди через Google</div>
                  </div>
                )}

                {/* Animation controls */}
                <div style={{ borderTop: "1px solid rgba(255,255,255,0.06)", paddingTop: 12 }}>
                  <div style={{ fontSize: 12, fontWeight: 700, color: "#e879f9", marginBottom: 10 }}>Настройки анимации</div>
                  {[
                    [animCount, setAnimCount, "Количество", "#e879f9", 3, 60, "шт"],
                    [animSpeed, setAnimSpeed, "Скорость", "#a855f7", 30, 300, "%"],
                    [animSize, setAnimSize, "Размер", "#c084fc", 30, 300, "%"],
                  ].map(([v, fn, l, c, mn, mx, u]) => (
                    <div key={l} style={{ marginBottom: 10 }}>
                      <div style={{ display: "flex", justifyContent: "space-between", marginBottom: 3 }}>
                        <label style={S.l2}>{l}</label>
                        <span style={{ fontSize: 12, color: c, fontWeight: 700 }}>{v}{u}</span>
                      </div>
                      <input type="range" min={mn} max={mx} value={v} onChange={e => fn(+e.target.value)}
                        style={{ width: "100%", accentColor: c }} />
                    </div>
                  ))}
                </div>
              </div>
            )}

            {/* ──── RENDER TAB ──── */}
            {tab === "render" && (
              <div>
                <div style={{ fontSize: 14, fontWeight: 700, color: "#FFD700", marginBottom: 12 }}>Рендер видео</div>

                {/* Batch count */}
                <div style={{ display: "flex", gap: 8, marginBottom: 12, flexWrap: "wrap" }}>
                  {[1, 3, 5, 10].map(n => (
                    <button key={n} onClick={() => setCc(n)} style={{
                      padding: "9px 18px", fontSize: 14, fontWeight: 700, borderRadius: 8, cursor: "pointer",
                      background: cc === n ? "linear-gradient(135deg,#FFD700,#FF8C00)" : "rgba(255,255,255,0.05)",
                      color: cc === n ? "#000" : "#888",
                      border: cc === n ? "none" : "1px solid rgba(255,255,255,0.1)"
                    }}>{n}</button>
                  ))}
                </div>

                {/* Info summary */}
                <div style={{
                  padding: 10, borderRadius: 8, marginBottom: 12, background: "rgba(255,255,255,0.03)",
                  border: "1px solid rgba(255,255,255,0.06)", fontSize: 12, color: "#ccc", lineHeight: 1.7
                }}>
                  <strong style={{ color: "#FFD700" }}>{casino}</strong> | <strong style={{ color: "#4ECDC4" }}>{state}</strong><br />
                  {wc} виннеров | {music.length || "--"} треков {selTracks.length ? `(${selTracks.length} выбрано)` : "(рандом)"} | Лого: {logo ? "Да" : "Нет"} | Фон: {bgList.length || bg ? "Да " + Math.max(bgList.length, bg ? 1 : 0) : "Нет"}<br />
                  Шрифт: {font} | Блюр: {blur}px | Яркость: {bright}% | Затемнение: {overlay}%<br />
                  Виннеры: {fs}% | Заголовок: {hs}% | CTA: {ctaFs}% | Лого: {logoSize}%<br />
                  Украшения: {decoList.length > 0 ? `${decoList.length} картинок` : "нет"} | Кол-во: {animCount} | Скорость: {animSpeed}% | Размер: {animSize}%<br />
                  Итого: <strong style={{ color: "#FF6B6B", fontSize: 16 }}>{cc} видео</strong> (~{Math.round(cc * 30 / 60)} мин рендер)
                </div>

                {/* Action buttons */}
                <div style={{ display: "flex", gap: 8, marginBottom: 10 }}>
                  <button onClick={() => genVars()} style={{
                    flex: 1, padding: 11, fontSize: 14, fontWeight: 700,
                    background: "rgba(255,215,0,0.12)", color: "#FFD700",
                    border: "1px solid rgba(255,215,0,0.3)", borderRadius: 8, cursor: "pointer"
                  }}>Сгенерировать варианты</button>
                  <button onClick={startRender} disabled={renderJob?.status === "running"} style={{
                    flex: 1, padding: 11, fontSize: 14, fontWeight: 800,
                    background: renderJob?.status === "running" ? "#333" : "linear-gradient(135deg,#FF6B00,#FF4444)",
                    color: renderJob?.status === "running" ? "#888" : "#fff",
                    border: "none", borderRadius: 8,
                    cursor: renderJob?.status === "running" ? "wait" : "pointer"
                  }}>
                    {renderJob?.status === "running" ? "Рендерится..." : "Начать рендер"}
                  </button>
                </div>

                {/* Variants preview */}
                {showV && vars.length > 0 && !renderJob && (
                  <div style={{ marginTop: 8 }}>
                    <div style={{ fontSize: 12, fontWeight: 700, color: "#4f4", marginBottom: 6 }}>{vars.length} текстов сгенерировано</div>
                    <div style={{ maxHeight: 160, overflowY: "auto" }}>
                      {vars.slice(0, 3).map(v => (
                        <details key={v.id} style={{ marginBottom: 3 }}>
                          <summary style={{
                            padding: "4px 8px", fontSize: 11, cursor: "pointer",
                            background: "rgba(255,255,255,0.03)", borderRadius: 5, color: "#aaa"
                          }}>#{v.id} | bg: {v.bg ? "yes" : "gradient"}</summary>
                          <pre style={{
                            padding: 6, fontSize: 10, lineHeight: 1.3, background: "rgba(0,0,0,0.4)",
                            borderRadius: "0 0 5px 5px", color: "#888", whiteSpace: "pre-wrap"
                          }}>{v.text}</pre>
                        </details>
                      ))}
                      {vars.length > 3 && <div style={{ fontSize: 10, color: "#666", padding: 4 }}>...{vars.length - 3} more</div>}
                    </div>
                  </div>
                )}

                {/* Render progress */}
                {renderJob && renderJob.status === "running" && (
                  <div style={{ marginTop: 10 }}>
                    <div style={{ fontSize: 13, fontWeight: 700, color: "#FFA500", marginBottom: 8 }}>
                      Рендерится: {renderJob.done}/{renderJob.total} ({elapsed}с)
                    </div>
                    <div style={{ height: 8, borderRadius: 4, background: "rgba(255,255,255,0.06)", overflow: "hidden" }}>
                      <div style={{
                        height: "100%", borderRadius: 4, transition: "width 0.5s",
                        background: "linear-gradient(90deg,#FFD700,#FF6B00)",
                        width: `${renderJob.total ? (renderJob.done / renderJob.total) * 100 : 0}%`
                      }} />
                    </div>
                    <div style={{ fontSize: 10, color: "#666", marginTop: 4 }}>
                      ~{Math.round((renderJob.total - renderJob.done) * 30)} сек осталось
                    </div>
                  </div>
                )}

                {/* Render done */}
                {renderJob && renderJob.status === "done" && (
                  <div style={{ marginTop: 10 }}>
                    <div style={{ fontSize: 14, fontWeight: 800, color: "#4f4", marginBottom: 8 }}>
                      Готово! {renderJob.results?.length} видео ({renderJob.elapsed}с)
                    </div>
                    {renderJob.zip_url && (
                      <a href={renderJob.zip_url} style={{
                        display: "block", padding: 12, textAlign: "center", fontSize: 15, fontWeight: 800,
                        background: "linear-gradient(135deg,#4ECDC4,#45B7AA)", color: "#000",
                        borderRadius: 8, textDecoration: "none", marginBottom: 8
                      }}>
                        Скачать ZIP ({renderJob.results?.length} видео)
                      </a>
                    )}
                    <div style={{ maxHeight: 160, overflowY: "auto" }}>
                      {renderJob.results?.map((r, i) => (
                        <div key={i} style={{
                          display: "flex", alignItems: "center", gap: 8, padding: "4px 8px", margin: "2px 0",
                          borderRadius: 5, background: "rgba(0,255,0,0.05)", fontSize: 11, color: "#aaa"
                        }}>
                          <span>#{r.id}</span>
                          {r.url
                            ? <a href={r.url} style={{ color: "#4ECDC4" }} download>{r.filename}</a>
                            : <span style={{ color: "#f44" }}>Ошибка</span>
                          }
                        </div>
                      ))}
                    </div>
                    <button onClick={() => setRenderJob(null)} style={{
                      marginTop: 8, padding: "6px 14px", fontSize: 12,
                      background: "rgba(255,255,255,0.06)", color: "#888",
                      border: "1px solid rgba(255,255,255,0.1)", borderRadius: 6, cursor: "pointer"
                    }}>Новый рендер</button>
                  </div>
                )}

                {/* Render error */}
                {renderJob && renderJob.status === "error" && (
                  <div style={{
                    marginTop: 10, padding: 10, borderRadius: 8,
                    background: "rgba(255,0,0,0.08)", border: "1px solid rgba(255,0,0,0.2)", fontSize: 12, color: "#f66"
                  }}>
                    Ошибка: {renderJob.error}
                    <button onClick={() => setRenderJob(null)} style={{
                      display: "block", marginTop: 6, padding: "4px 10px", fontSize: 11,
                      background: "rgba(255,255,255,0.06)", color: "#888",
                      border: "1px solid rgba(255,255,255,0.1)", borderRadius: 4, cursor: "pointer"
                    }}>Попробовать снова</button>
                  </div>
                )}
              </div>
            )}

          </div>
        </div>
      </div>
    </div>
  );
}

/* ─── MOUNT ────────────────────────────────────────────────────────── */
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(React.createElement(App));
