// ABOUTME: Main App — composes the Atelier editor: layers list, layer editor, scene gallery, library, export.
// ABOUTME: Owns state, persists draft to localStorage, and stores uploaded photos in IndexedDB.

const {
  KOBO_PORTRAIT, KOBO_LANDSCAPE, FONTS, COLORS, DIVIDERS, STORAGE_KEYS,
  loadJSON, saveJSON,
  Section, Label, TextField, Slider, FontPills, ColorSwatches, DividerPills, Toggle,
  ScreensaverPreview,
  exportScreensaver, downloadDataUrl, readFileAsDataURL, compressImage,
  SCENES_DEF, SceneRenderer,
} = window;

let _layerCounter = 0;
function newLayerId() { return `L${Date.now().toString(36)}${(_layerCounter++).toString(36)}`; }

function makeTextLayer(overrides = {}) {
  return {
    id: newLayerId(),
    kind: "text",
    text: "New text",
    font: "cormorant",
    size: 110,
    color: "starlight",
    glow: 70,
    opacity: 1,
    x: 0.5, y: 0.5,
    ...overrides,
  };
}
function makeDividerLayer(overrides = {}) {
  return {
    id: newLayerId(),
    kind: "divider",
    text: "✦ ✦ ✦",
    font: "cinzel",
    size: 36,
    color: "ember",
    glow: 60,
    opacity: 0.9,
    x: 0.5, y: 0.5,
    ...overrides,
  };
}

const DEFAULT_STATE = {
  layers: [
    makeTextLayer({ text: "The Court",                       size: 140, font: "cormorant", color: "starlight", glow: 70, x: 0.32, y: 0.42 }),
    makeTextLayer({ text: "of Dreams",                       size: 140, font: "cormorant", color: "starlight", glow: 70, x: 0.50, y: 0.52 }),
    makeTextLayer({ text: "Welcome back\nto Velaris, my Lady", size: 42,  font: "cinzel",    color: "starlight", glow: 55, x: 0.70, y: 0.86 }),
  ],
  customColor: "#fff8e0",
  scene: "bay",
  uploadedImage: null,
  uploadedName: null,
  orient: "portrait",
};

// Migrates legacy drafts that used title/subtitle/divider into the layers schema.
function migrateOldDraft(d) {
  if (!d) return null;
  if (Array.isArray(d.layers)) return d;
  const layers = [];
  if (d.title) layers.push(makeTextLayer({
    text: d.title, font: d.font || "cormorant", color: d.color || "starlight",
    glow: d.shadow ?? 70, size: d.titleSize ?? 110,
    x: d.titlePos?.x ?? 0.5, y: d.titlePos?.y ?? 0.42,
  }));
  if (d.divider && d.divider !== "none" && d.title && d.subtitle) {
    const dRender = (DIVIDERS.find(x => x.id === d.divider) || {}).render || "✦ ✦ ✦";
    layers.push(makeDividerLayer({
      text: dRender, color: d.color || "starlight", glow: d.shadow ?? 70,
      size: Math.max(20, (d.titleSize ?? 110) * 0.32),
      x: ((d.titlePos?.x ?? 0.5) + (d.subtitlePos?.x ?? 0.5)) / 2,
      y: ((d.titlePos?.y ?? 0.42) + (d.subtitlePos?.y ?? 0.55)) / 2,
    }));
  }
  if (d.subtitle) layers.push(makeTextLayer({
    text: d.subtitle, font: d.font || "cormorant", color: d.color || "starlight",
    glow: d.shadow ?? 70, size: d.subtitleSize ?? 170,
    x: d.subtitlePos?.x ?? 0.5, y: d.subtitlePos?.y ?? 0.55,
  }));
  return {
    layers,
    customColor: d.customColor || "#fff8e0",
    scene: d.scene || "starfall",
    uploadedImage: d.uploadedImage || null,
    uploadedName: d.uploadedName || null,
    orient: d.orient || "portrait",
  };
}

function App() {
  const [state, setState] = React.useState(() => {
    const d = loadJSON(STORAGE_KEYS.draft, null);
    const migrated = migrateOldDraft(d);
    return migrated ? { ...DEFAULT_STATE, ...migrated } : DEFAULT_STATE;
  });
  const [uploads, setUploads] = React.useState([]);
  React.useEffect(() => {
    (async () => {
      let items = [];
      try { items = await idbAll(); } catch (e) { console.warn("[velaris] idb load failed", e); }
      // One-time migration from old localStorage uploads
      try {
        const legacy = loadJSON(STORAGE_KEYS.uploads, []);
        if (Array.isArray(legacy) && legacy.length) {
          const existingIds = new Set(items.map(i => i.id));
          for (const l of legacy) {
            if (!existingIds.has(l.id)) {
              const entry = { ...l, thumb: l.thumb || l.dataUrl };
              await idbPut(entry).catch(() => {});
              items.push(entry);
            }
          }
          try { localStorage.removeItem(STORAGE_KEYS.uploads); } catch {}
        }
      } catch (e) { console.warn("[velaris] legacy migration skipped", e); }
      items.sort((a, b) => (b.ts || 0) - (a.ts || 0));
      setUploads(items);
    })();
  }, []);
  const [library, setLibrary] = React.useState(() => loadJSON(STORAGE_KEYS.library, []));
  const [exporting, setExporting] = React.useState(false);
  const [toast, setToast] = React.useState(null);
  const [selectedId, setSelectedId] = React.useState(() => null);

  React.useEffect(() => {
    if (!selectedId && state.layers && state.layers.length) {
      setSelectedId(state.layers[0].id);
    }
  }, []);

  React.useEffect(() => { saveJSON(STORAGE_KEYS.draft, state); }, [state]);
  React.useEffect(() => { saveJSON(STORAGE_KEYS.library, library); }, [library]);

  const update = (patch) => setState(s => ({ ...s, ...patch }));
  const updateLayer = (id, patch) => setState(s => ({
    ...s, layers: s.layers.map(l => l.id === id ? { ...l, ...patch } : l),
  }));
  const moveLayer = (id, pos) => updateLayer(id, pos);
  const addLayer = (kind = "text") => {
    const newL = kind === "divider"
      ? makeDividerLayer({ x: 0.5, y: 0.7 })
      : makeTextLayer({ text: "New text", x: 0.5, y: 0.7 });
    setState(s => ({ ...s, layers: [...s.layers, newL] }));
    setSelectedId(newL.id);
  };
  const deleteLayer = (id) => setState(s => {
    const layers = s.layers.filter(l => l.id !== id);
    return { ...s, layers };
  });
  const duplicateLayer = (id) => setState(s => {
    const src = s.layers.find(l => l.id === id);
    if (!src) return s;
    const dup = { ...src, id: newLayerId(), x: Math.min(0.95, src.x + 0.04), y: Math.min(0.95, src.y + 0.04) };
    return { ...s, layers: [...s.layers, dup] };
  });
  const reorderLayer = (id, dir /* -1 up / +1 down in stack */) => setState(s => {
    const i = s.layers.findIndex(l => l.id === id);
    if (i < 0) return s;
    const j = i + dir;
    if (j < 0 || j >= s.layers.length) return s;
    const layers = [...s.layers];
    [layers[i], layers[j]] = [layers[j], layers[i]];
    return { ...s, layers };
  });

  const showToast = (msg) => { setToast(msg); setTimeout(() => setToast(null), 2200); };

  const handleUpload = async (files) => {
    for (const file of files) {
      try {
        const raw = await readFileAsDataURL(file);
        const compressed = await compressImage(raw, 2400, 0.88);
        const thumb = await compressImage(raw, 600, 0.7);
        const id = `up_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
        const entry = { id, name: file.name, dataUrl: compressed, thumb, ts: Date.now() };
        await idbPut(entry).catch(e => console.warn("idb put failed", e));
        setUploads(u => [entry, ...u].slice(0, 60));
        update({ uploadedImage: compressed, uploadedName: file.name, scene: null });
        showToast(`Loaded "${file.name}"`);
      } catch (e) { console.error(e); showToast("Couldn't load that image"); }
    }
  };

  const onPickScene = (sceneId) => update({ scene: sceneId, uploadedImage: null, uploadedName: null });
  const onPickUpload = (entry) => update({ uploadedImage: entry.dataUrl, uploadedName: entry.name, scene: null });
  const onDeleteUpload = (id, e) => {
    e.stopPropagation();
    idbDelete(id).catch(() => {});
    setUploads(u => u.filter(x => x.id !== id));
  };

  const handleExport = async (format) => {
    setExporting(true);
    try {
      const { dataUrl } = await exportScreensaver(state, state.orient, format);
      const W = state.orient === "portrait" ? 1264 : 1680;
      const H = state.orient === "portrait" ? 1680 : 1264;
      const firstText = (state.layers.find(l => l.kind === "text") || {}).text || "velaris";
      const slug = firstText.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "") || "velaris";
      downloadDataUrl(dataUrl, `${slug}-${W}x${H}.${format === "jpeg" ? "jpg" : "png"}`);
      showToast(`Exported ${W}×${H} ${format.toUpperCase()}`);
    } catch (e) { console.error(e); showToast("Export failed"); }
    finally { setExporting(false); }
  };

  const handleSaveToLibrary = async () => {
    try {
      const { dataUrl } = await exportScreensaver(state, state.orient, "jpeg");
      const thumb = await compressImage(dataUrl, 480, 0.8);
      const firstText = (state.layers.find(l => l.kind === "text") || {}).text || "Untitled";
      const entry = {
        id: `lib_${Date.now()}_${Math.random().toString(36).slice(2, 6)}`,
        title: firstText,
        orient: state.orient,
        thumb,
        state: { ...state },
        ts: Date.now(),
      };
      setLibrary(l => [entry, ...l].slice(0, 30));
      showToast("Saved to library");
    } catch (e) { console.error(e); showToast("Couldn't save"); }
  };

  const onLoadFromLibrary = (entry) => {
    setState({ ...DEFAULT_STATE, ...entry.state });
    setSelectedId(entry.state.layers?.[0]?.id || null);
    showToast("Loaded from library");
  };
  const onDeleteFromLibrary = (id, e) => { e.stopPropagation(); setLibrary(l => l.filter(x => x.id !== id)); };

  const [previewW, setPreviewW] = React.useState(380);
  React.useEffect(() => {
    const compute = () => {
      const vh = window.innerHeight - 100;
      const aspect = state.orient === "portrait" ? (1264 / 1680) : (1680 / 1264);
      const wByH = vh * aspect;
      const wMax = window.innerWidth - 600;
      setPreviewW(Math.max(280, Math.min(wByH, wMax, 460)));
    };
    compute();
    window.addEventListener('resize', compute);
    return () => window.removeEventListener('resize', compute);
  }, [state.orient]);

  const selectedLayer = state.layers.find(l => l.id === selectedId) || null;

  return (
    <div style={{
      width: "100vw", height: "100vh", overflow: "hidden",
      background: "radial-gradient(ellipse at 30% 10%, #3a2878 0%, #1a103e 55%, #0f0a2e 100%)",
      color: "#f5e9d3", fontFamily: "'Cormorant Garamond', Georgia, serif",
      display: "flex", position: "relative",
    }}>
      <StarField />

      {/* Left sidebar — layers + selected layer editor */}
      <aside style={{
        width: 320, padding: "26px 24px 22px",
        background: "rgba(15,10,46,0.55)",
        backdropFilter: "blur(10px)",
        WebkitBackdropFilter: "blur(10px)",
        borderRight: "1px solid rgba(106,74,176,0.4)",
        display: "flex", flexDirection: "column", gap: 18,
        overflowY: "auto", zIndex: 1, flexShrink: 0,
      }}>
        <header>
          <div style={{ fontFamily: "'Cinzel', serif", fontSize: 11, letterSpacing: 4, color: "#c9a8e8", marginBottom: 4 }}>VELARIS · ATELIER</div>
          <div style={{ fontSize: 24, lineHeight: 1.05, fontStyle: "italic", color: "#fff8e0" }}>Dress the night<br/>for sleepy times</div>
          <button
            onClick={() => window.velarisShowIntro && window.velarisShowIntro()}
            style={{
              marginTop: 10, padding: 0,
              background: "transparent", border: "none",
              fontFamily: "'Cormorant Garamond', serif", fontStyle: "italic",
              fontSize: 13, color: "rgba(201,168,232,0.75)",
              cursor: "pointer", textAlign: "left",
              textDecoration: "underline",
              textDecorationColor: "rgba(201,168,232,0.3)",
              textUnderlineOffset: 3,
            }}
            onMouseEnter={e => { e.currentTarget.style.color = "#fff8e0"; }}
            onMouseLeave={e => { e.currentTarget.style.color = "rgba(201,168,232,0.75)"; }}
          >✦ read the letter again</button>
        </header>

        <DividerLine />

        {/* Layers list */}
        <Section label="Layers">
          <div style={{ display: "flex", flexDirection: "column", gap: 4 }}>
            {state.layers.map((l, i) => (
              <LayerRow
                key={l.id}
                layer={l}
                active={l.id === selectedId}
                onSelect={() => setSelectedId(l.id)}
                onDelete={() => { deleteLayer(l.id); if (selectedId === l.id) setSelectedId(null); }}
                onDuplicate={() => duplicateLayer(l.id)}
                onUp={i > 0 ? () => reorderLayer(l.id, -1) : null}
                onDown={i < state.layers.length - 1 ? () => reorderLayer(l.id, +1) : null}
              />
            ))}
          </div>
          <div style={{ display: "flex", gap: 6, marginTop: 8 }}>
            <button onClick={() => addLayer("text")} style={btnAdd}>+ TEXT</button>
            <button onClick={() => addLayer("divider")} style={btnAdd}>+ DIVIDER</button>
          </div>
        </Section>

        <DividerLine />

        {/* Selected layer editor */}
        {selectedLayer ? (
          <LayerEditor
            layer={selectedLayer}
            customColor={state.customColor}
            onChange={(patch) => updateLayer(selectedLayer.id, patch)}
            onCustom={(v) => update({ customColor: v })}
          />
        ) : (
          <div style={{ fontSize: 13, fontStyle: "italic", color: "rgba(245,233,211,0.5)", padding: "8px 0" }}>
            Select a layer to edit, or add a new one.
          </div>
        )}

        <DividerLine />

        <Section label="Saved library">
          {library.length === 0 ? (
            <div style={{ fontSize: 13, fontStyle: "italic", color: "rgba(245,233,211,0.5)" }}>Nothing saved yet</div>
          ) : (
            <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr 1fr", gap: 6 }}>
              {library.map(item => (
                <button key={item.id} onClick={() => onLoadFromLibrary(item)} style={{
                  position: "relative", padding: 0, border: "1px solid rgba(106,74,176,0.6)",
                  background: "transparent", borderRadius: 4, overflow: "hidden", cursor: "pointer",
                  aspectRatio: item.orient === "portrait" ? "1264/1680" : "1680/1264",
                }}>
                  <img src={item.thumb} style={{ width: "100%", height: "100%", objectFit: "cover", display: "block" }} />
                  <span onClick={(e) => onDeleteFromLibrary(item.id, e)} style={{
                    position: "absolute", top: 2, right: 2, width: 16, height: 16,
                    borderRadius: 8, background: "rgba(15,10,46,0.85)", color: "#f5e9d3",
                    fontSize: 11, lineHeight: "16px", textAlign: "center", cursor: "pointer",
                  }}>×</span>
                </button>
              ))}
            </div>
          )}
        </Section>

        <div style={{ marginTop: "auto", paddingTop: 12, display: "flex", flexDirection: "column", gap: 8 }}>
          <button onClick={handleSaveToLibrary} disabled={exporting} style={btnSecondary}>✦ &nbsp; SAVE TO LIBRARY</button>
          <div style={{ display: "flex", gap: 8 }}>
            <button onClick={() => handleExport("png")} disabled={exporting} style={{ ...btnPrimary, flex: 2 }}>
              {exporting ? "EXPORTING…" : "EXPORT PNG"}
            </button>
            <button onClick={() => handleExport("jpeg")} disabled={exporting} style={{ ...btnSecondary, flex: 1 }}>JPG</button>
          </div>
        </div>
      </aside>

      {/* Center — canvas */}
      <main style={{
        flex: 1, display: "flex", flexDirection: "column",
        alignItems: "center", justifyContent: "center",
        padding: 28, position: "relative", zIndex: 1, minWidth: 0,
      }}>
        <div style={{ display: "flex", alignItems: "center", gap: 16, marginBottom: 22 }}>
          <Toggle
            value={state.orient}
            onChange={v => update({ orient: v })}
            options={[
              { value: "portrait",  label: "PORTRAIT · 1264 × 1680" },
              { value: "landscape", label: "LANDSCAPE · 1680 × 1264" },
            ]}
          />
        </div>

        <ScreensaverPreview
          state={state}
          width={state.orient === "portrait" ? previewW : previewW * (1680 / 1264)}
          orient={state.orient}
          interactive
          selectedLayerId={selectedId}
          onSelectLayer={setSelectedId}
          onLayerMove={moveLayer}
        />

        <div style={{ marginTop: 16, fontSize: 12, color: "rgba(201,168,232,0.7)", fontFamily: "'Cinzel', serif", letterSpacing: 2.5 }}>
          CLICK A TEXT · DRAG TO REPOSITION
        </div>
      </main>

      {/* Right — gallery */}
      <aside style={{
        width: 200, padding: "28px 18px 24px",
        background: "rgba(15,10,46,0.55)",
        backdropFilter: "blur(10px)",
        WebkitBackdropFilter: "blur(10px)",
        borderLeft: "1px solid rgba(106,74,176,0.4)",
        display: "flex", flexDirection: "column", gap: 10,
        overflowY: "auto", zIndex: 1, flexShrink: 0,
      }}>
        <Label style={{ marginBottom: 4 }}>Scenes</Label>
        {SCENES_DEF.map(s => (
          <SceneTile
            key={s.id} name={s.name} sceneId={s.id}
            active={state.scene === s.id && !state.uploadedImage}
            onClick={() => onPickScene(s.id)}
          />
        ))}

        <Label style={{ marginTop: 16, marginBottom: 4 }}>Your photos</Label>
        <UploadDropzone onFiles={handleUpload} />
        {uploads.map(u => (
          <button key={u.id} onClick={() => onPickUpload(u)} style={{
            position: "relative", padding: 0, background: "transparent",
            border: state.uploadedImage === u.dataUrl ? "1.5px solid #ffe2a8" : "1px solid rgba(106,74,176,0.6)",
            boxShadow: state.uploadedImage === u.dataUrl ? "0 0 14px rgba(255,226,168,0.4)" : "none",
            borderRadius: 4, overflow: "hidden", cursor: "pointer", aspectRatio: "3/4",
            width: "100%", flexShrink: 0, display: "block",
          }}>
            <img src={u.thumb || u.dataUrl} style={{ width: "100%", height: "100%", objectFit: "cover", display: "block" }} />
            <span onClick={(e) => onDeleteUpload(u.id, e)} style={{
              position: "absolute", top: 4, right: 4, width: 18, height: 18,
              borderRadius: 9, background: "rgba(15,10,46,0.85)", color: "#f5e9d3",
              fontSize: 12, lineHeight: "18px", textAlign: "center", cursor: "pointer",
            }}>×</span>
          </button>
        ))}
      </aside>

      {toast && (
        <div style={{
          position: "fixed", bottom: 32, left: "50%", transform: "translateX(-50%)",
          padding: "12px 24px", background: "rgba(15,10,46,0.92)",
          border: "1px solid rgba(201,168,232,0.6)", borderRadius: 999,
          boxShadow: "0 0 30px rgba(201,168,232,0.3)",
          fontFamily: "'Cinzel', serif", fontSize: 11, letterSpacing: 2.5,
          color: "#fff8e0", zIndex: 100,
        }}>{toast}</div>
      )}
    </div>
  );
}

function LayerRow({ layer, active, onSelect, onDelete, onDuplicate, onUp, onDown }) {
  const isDivider = layer.kind === "divider";
  const preview = layer.text || (isDivider ? "✦" : "Text");
  return (
    <div onClick={onSelect} style={{
      display: "flex", alignItems: "center", gap: 6,
      padding: "7px 8px",
      background: active ? "rgba(201,168,232,0.16)" : "rgba(15,10,46,0.4)",
      border: active ? "1px solid rgba(255,226,168,0.6)" : "1px solid rgba(106,74,176,0.4)",
      borderRadius: 4, cursor: "pointer",
    }}>
      <span style={{
        fontFamily: "'Cinzel', serif", fontSize: 9, letterSpacing: 1.5,
        color: isDivider ? "#ffe2a8" : "#c9a8e8",
        width: 28, flexShrink: 0,
      }}>{isDivider ? "DIV" : "TXT"}</span>
      <span style={{
        flex: 1, minWidth: 0,
        whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis",
        fontStyle: "italic", color: "#f5e9d3", fontSize: 14,
      }}>{preview}</span>
      <IconBtn title="Up" disabled={!onUp} onClick={(e) => { e.stopPropagation(); onUp && onUp(); }}>↑</IconBtn>
      <IconBtn title="Down" disabled={!onDown} onClick={(e) => { e.stopPropagation(); onDown && onDown(); }}>↓</IconBtn>
      <IconBtn title="Duplicate" onClick={(e) => { e.stopPropagation(); onDuplicate(); }}>⎘</IconBtn>
      <IconBtn title="Delete" onClick={(e) => { e.stopPropagation(); onDelete(); }}>×</IconBtn>
    </div>
  );
}

function IconBtn({ children, onClick, disabled, title }) {
  return (
    <button onClick={onClick} disabled={disabled} title={title} style={{
      width: 20, height: 20, padding: 0,
      background: "transparent", color: disabled ? "rgba(201,168,232,0.25)" : "#c9a8e8",
      border: "1px solid rgba(106,74,176,0.5)", borderRadius: 3,
      cursor: disabled ? "default" : "pointer",
      fontSize: 11, lineHeight: 1, flexShrink: 0,
    }}>{children}</button>
  );
}

function LayerEditor({ layer, customColor, onChange, onCustom }) {
  const isDivider = layer.kind === "divider";
  const dividerPresets = ["✦ ✦ ✦", "❦", "◆ ◆ ◆", "·  ·  ·", "✧ ✦ ✧", "— ✦ —"];
  return (
    <div style={{ display: "flex", flexDirection: "column", gap: 16 }}>
      <Section label={isDivider ? "Divider glyph" : "Text"}>
        <TextField value={layer.text} onChange={v => onChange({ text: v })} italic={!isDivider} multiline={!isDivider} />
        {isDivider && (
          <div style={{ display: "flex", gap: 4, flexWrap: "wrap", marginTop: 6 }}>
            {dividerPresets.map(d => (
              <button key={d} onClick={() => onChange({ text: d })} style={{
                padding: "4px 9px", fontSize: 13, fontFamily: "'Cinzel', serif",
                background: layer.text === d ? "#c9a8e8" : "transparent",
                color: layer.text === d ? "#1a103e" : "#c9a8e8",
                border: "1px solid rgba(201,168,232,0.6)", borderRadius: 999, cursor: "pointer",
              }}>{d}</button>
            ))}
          </div>
        )}
      </Section>

      {!isDivider && (
        <Section label="Typeface">
          <FontPills value={layer.font} onChange={v => onChange({ font: v })} />
        </Section>
      )}

      <Section label="Size">
        <Slider value={layer.size} min={20} max={isDivider ? 120 : 320} step={2} onChange={v => onChange({ size: v })} />
      </Section>

      <Section label="Color">
        <ColorSwatches
          value={layer.color}
          onChange={v => onChange({ color: v })}
          customColor={customColor}
          onCustom={onCustom}
        />
      </Section>

      <Section label="Glow">
        <Slider value={layer.glow ?? 70} min={0} max={100} step={1} onChange={v => onChange({ glow: v })} />
      </Section>

      <Section label="Opacity">
        <Slider value={Math.round((layer.opacity ?? 1) * 100)} min={10} max={100} step={1} onChange={v => onChange({ opacity: v / 100 })} />
      </Section>
    </div>
  );
}

const btnPrimary = {
  padding: "11px 14px", fontFamily: "'Cinzel', serif", fontSize: 11, letterSpacing: 2.5,
  background: "linear-gradient(135deg, #fff8e0 0%, #ffe2a8 100%)",
  color: "#1a103e", border: "none", borderRadius: 999, cursor: "pointer",
  boxShadow: "0 0 24px rgba(255,226,168,0.35)",
};
const btnSecondary = {
  padding: "10px 14px", fontFamily: "'Cinzel', serif", fontSize: 10, letterSpacing: 2.5,
  background: "transparent", color: "#fff8e0",
  border: "1px solid rgba(201,168,232,0.6)", borderRadius: 999, cursor: "pointer",
};
const btnAdd = {
  flex: 1, padding: "7px 10px", fontFamily: "'Cinzel', serif",
  fontSize: 10, letterSpacing: 2,
  background: "transparent", color: "#c9a8e8",
  border: "1px dashed rgba(201,168,232,0.6)", borderRadius: 4, cursor: "pointer",
};

function DividerLine() {
  return <div style={{ height: 1, background: "linear-gradient(90deg, transparent, rgba(201,168,232,0.4), transparent)" }} />;
}

function SceneTile({ name, sceneId, active, onClick }) {
  return (
    <button onClick={onClick} style={{
      position: "relative", padding: 0, background: "transparent",
      border: active ? "1.5px solid #ffe2a8" : "1px solid rgba(106,74,176,0.6)",
      boxShadow: active ? "0 0 14px rgba(255,226,168,0.4)" : "none",
      borderRadius: 4, overflow: "hidden", cursor: "pointer",
      aspectRatio: "1264/1680",
    }}>
      <SceneRenderer sceneId={sceneId} id={`tile-${sceneId}`} />
      <div style={{
        position: "absolute", bottom: 0, left: 0, right: 0,
        padding: "5px 7px",
        background: "linear-gradient(transparent, rgba(15,10,46,0.85))",
        fontFamily: "'Cinzel', serif", fontSize: 9, letterSpacing: 1.8,
        color: "#f5e9d3", textAlign: "left",
      }}>{name.toUpperCase()}</div>
    </button>
  );
}

function UploadDropzone({ onFiles }) {
  const inputRef = React.useRef(null);
  const [dragOver, setDragOver] = React.useState(false);
  return (
    <div
      onClick={() => inputRef.current?.click()}
      onDragOver={e => { e.preventDefault(); setDragOver(true); }}
      onDragLeave={() => setDragOver(false)}
      onDrop={e => {
        e.preventDefault(); setDragOver(false);
        const files = [...e.dataTransfer.files].filter(f => f.type.startsWith('image/'));
        if (files.length) onFiles(files);
      }}
      style={{
        aspectRatio: "1264/1680",
        border: dragOver ? "1.5px dashed #ffe2a8" : "1px dashed rgba(201,168,232,0.6)",
        borderRadius: 4,
        display: "flex", alignItems: "center", justifyContent: "center",
        flexDirection: "column", gap: 4,
        fontFamily: "'Cinzel', serif", fontSize: 10, letterSpacing: 1.8,
        color: dragOver ? "#ffe2a8" : "#c9a8e8",
        cursor: "pointer", padding: 8, textAlign: "center",
        background: dragOver ? "rgba(255,226,168,0.08)" : "transparent",
        transition: "all 0.15s",
      }}
    >
      <div style={{ fontSize: 18 }}>+</div>
      <div>DROP OR<br/>CHOOSE</div>
      <input ref={inputRef} type="file" accept="image/*" multiple
        onChange={e => { if (e.target.files?.length) onFiles([...e.target.files]); e.target.value = ""; }}
        style={{ display: "none" }} />
    </div>
  );
}

function StarField() {
  const stars = React.useMemo(() => {
    const arr = [];
    for (let i = 0; i < 60; i++) {
      arr.push({ x: Math.random() * 100, y: Math.random() * 100, r: Math.random() * 1.2 + 0.3, o: Math.random() * 0.6 + 0.2 });
    }
    return arr;
  }, []);
  return (
    <svg style={{ position: "absolute", inset: 0, width: "100%", height: "100%", pointerEvents: "none", zIndex: 0 }}>
      {stars.map((s, i) => (
        <circle key={i} cx={`${s.x}%`} cy={`${s.y}%`} r={s.r} fill="#fff8e0" opacity={s.o} />
      ))}
    </svg>
  );
}

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