/* ============================================================
   Admin → Artist pages
   Multi-page editor (Linktree-style) with media upload + live preview.
   ============================================================ */

const { useState: useStateAA, useEffect: useEffectAA, useMemo: useMemoAA, useRef: useRefAA } = React;

const ALL_ICONS = [
  "spotify", "apple-music", "apple", "tidal", "amazon-music", "itunes",
  "youtube", "youtube-music", "soundcloud", "bandcamp", "deezer", "pandora",
  "instagram", "tiktok", "twitter", "x", "facebook", "threads", "linkedin",
  "website", "email", "phone", "link",
];
const PLATFORM_COLORS = {
  spotify: "#1DB954", "apple-music": "#FA243C", apple: "#000000", tidal: "#000000",
  "amazon-music": "#00A8E1", itunes: "#EA4CC0", youtube: "#FF0000",
  "youtube-music": "#FF0000", soundcloud: "#FF5500", bandcamp: "#629AA9",
  deezer: "#A238FF", pandora: "#005483",
  instagram: "#E1306C", tiktok: "#000000", twitter: "#1DA1F2", x: "#000000",
  facebook: "#1877F2", threads: "#000000", linkedin: "#0A66C2",
  website: "#f5c14b", email: "#f5c14b", phone: "#f5c14b", link: "#ff3d9a",
};
const ICON_LABELS = {
  spotify: "Spotify", "apple-music": "Apple Music", apple: "Apple", tidal: "Tidal",
  "amazon-music": "Amazon Music", itunes: "iTunes Store", youtube: "YouTube",
  "youtube-music": "YouTube Music", soundcloud: "SoundCloud", bandcamp: "Bandcamp",
  deezer: "Deezer", pandora: "Pandora",
  instagram: "Instagram", tiktok: "TikTok", twitter: "Twitter", x: "X",
  facebook: "Facebook", threads: "Threads", linkedin: "LinkedIn",
  website: "Website", email: "Email", phone: "Phone", link: "Link",
};

function randId() { return "l" + Math.random().toString(36).slice(2, 9); }
function slugify(s) {
  return String(s).toLowerCase().normalize("NFD").replace(/[̀-ͯ]/g, "")
    .replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 48) || "page";
}

// ---------------------------------------------------------------
// Top-level: page list → editor
// ---------------------------------------------------------------
function ArtistPageSection({ data, reload }) {
  const [pages, setPages] = useStateAA([]);
  const [activeSlug, setActiveSlug] = useStateAA(null);
  const [listLoading, setListLoading] = useStateAA(true);

  async function loadList() {
    setListLoading(true);
    try {
      const res = await fetch("/api/admin/artist-pages", { credentials: "include" });
      if (res.ok) setPages(await res.json());
    } catch (e) {}
    setListLoading(false);
  }
  useEffectAA(() => { loadList(); }, []);
  useEffectAA(() => {
    if (!activeSlug && pages.length > 0) setActiveSlug(pages[0].slug);
  }, [pages, activeSlug]);

  async function createPage() {
    const raw = prompt("Slug for the new page (e.g. 'papa-cool', used in URL):");
    if (!raw) return;
    const slug = slugify(raw);
    if (!slug) return;
    if (pages.find(p => p.slug === slug)) {
      alert("That slug already exists.");
      setActiveSlug(slug);
      return;
    }
    const albumOpts = data?.albums || [];
    const seed = {
      slug, artistName: "", bio: "", verified: false, showOrigin: true,
      featured: albumOpts[0] ? { albumId: albumOpts[0].id, eyebrow: "NEW RELEASE", genre: "", durationLabel: "" } : null,
      primary: { id: "primary", label: "Listen on Spotify", url: "", icon: "spotify", color: "#1DB954" },
      links: [], socials: [],
    };
    const res = await fetch("/api/admin/artist-page/" + encodeURIComponent(slug), {
      method: "PUT", credentials: "include",
      headers: { "content-type": "application/json" },
      body: JSON.stringify(seed),
    });
    if (res.ok) {
      await loadList();
      setActiveSlug(slug);
    } else {
      alert("Could not create page");
    }
  }
  async function deletePage(slug) {
    if (!confirm(`Delete artist page "${slug}"? This cannot be undone.`)) return;
    const res = await fetch("/api/admin/artist-page/" + encodeURIComponent(slug), {
      method: "DELETE", credentials: "include",
    });
    if (res.ok) {
      if (activeSlug === slug) setActiveSlug(null);
      await loadList();
    }
  }

  return (
    <div style={{ display: "flex", flexDirection: "column", gap: 18 }}>
      <div style={{ display: "flex", flexWrap: "wrap", gap: 8, alignItems: "center" }}>
        {listLoading ? (
          <div style={{ color: "var(--ink-3)" }}>Loading…</div>
        ) : pages.length === 0 ? (
          <div style={{ color: "var(--ink-3)", fontSize: 13 }}>No pages yet.</div>
        ) : (
          pages.map(p => (
            <button
              key={p.slug}
              onClick={() => setActiveSlug(p.slug)}
              style={{
                ...(activeSlug === p.slug ? activeChipStyle : chipStyle),
                display: "inline-flex", alignItems: "center", gap: 8,
              }}
            >
              <span>{p.artistName || p.slug}</span>
              <span style={{ fontSize: 9, opacity: 0.6, fontFamily: "var(--f-mono)" }}>/a/{p.slug}</span>
            </button>
          ))
        )}
        <button onClick={createPage} style={pinkBtnAA}>+ New page</button>
      </div>

      {activeSlug && (
        <PageEditor
          key={activeSlug}
          slug={activeSlug}
          albumOptions={data?.albums || []}
          onDeleted={() => deletePage(activeSlug)}
          onUpdated={loadList}
        />
      )}
    </div>
  );
}

// ---------------------------------------------------------------
// Per-page editor
// ---------------------------------------------------------------
function PageEditor({ slug, albumOptions, onDeleted, onUpdated }) {
  const [doc, setDoc] = useStateAA(null);
  const [stats, setStats] = useStateAA({ views: 0, clicks: {} });
  const [loading, setLoading] = useStateAA(true);
  const [saving, setSaving] = useStateAA(false);
  const [err, setErr] = useStateAA("");
  const [showPreview, setShowPreview] = useStateAA(true);
  const [previewKey, setPreviewKey] = useStateAA(0);

  async function load() {
    setLoading(true); setErr("");
    try {
      const res = await fetch("/api/admin/artist-page/" + encodeURIComponent(slug), { credentials: "include" });
      if (!res.ok) throw new Error("HTTP " + res.status);
      const j = await res.json();
      setDoc(j.page);
      setStats(j.stats);
    } catch (e) { setErr(e.message); }
    setLoading(false);
  }
  useEffectAA(() => { load(); /* eslint-disable-next-line */ }, [slug]);

  async function save() {
    if (!doc) return;
    setSaving(true); setErr("");
    try {
      const res = await fetch("/api/admin/artist-page/" + encodeURIComponent(slug), {
        method: "PUT", credentials: "include",
        headers: { "content-type": "application/json" },
        body: JSON.stringify(doc),
      });
      if (!res.ok) {
        const j = await res.json().catch(() => ({}));
        setErr(j.error || "Save failed");
      } else {
        setPreviewKey(k => k + 1);
        await load();
        onUpdated?.();
      }
    } catch (e) { setErr(e.message); }
    setSaving(false);
  }
  function patch(p) { setDoc(d => ({ ...d, ...p })); }
  function patchFeatured(p) { setDoc(d => ({ ...d, featured: { ...(d.featured || {}), ...p } })); }
  function patchPrimary(p) { setDoc(d => ({ ...d, primary: { ...(d.primary || { id: "primary", icon: "spotify" }), ...p } })); }

  function addLink(kind = "links") {
    const id = randId();
    const icon = kind === "socials" ? "instagram" : "spotify";
    setDoc(d => ({ ...d, [kind]: [...(d[kind] || []), { id, label: ICON_LABELS[icon], url: "", icon }] }));
  }
  function updateLink(kind, i, p) {
    setDoc(d => ({ ...d, [kind]: d[kind].map((x, j) => j === i ? { ...x, ...p } : x) }));
  }
  function removeLink(kind, i) {
    setDoc(d => ({ ...d, [kind]: d[kind].filter((_, j) => j !== i) }));
  }
  function moveLink(kind, i, dir) {
    setDoc(d => {
      const arr = [...d[kind]];
      const j = i + dir;
      if (j < 0 || j >= arr.length) return d;
      [arr[i], arr[j]] = [arr[j], arr[i]];
      return { ...d, [kind]: arr };
    });
  }

  if (loading || !doc) return <div style={{ color: "var(--ink-3)" }}>{err || "Loading editor…"}</div>;

  const previewUrl = `/a/${slug}?v=${previewKey}`;
  return (
    <div style={{ display: "grid", gridTemplateColumns: showPreview ? "1fr 360px" : "1fr", gap: 24, alignItems: "start" }}>
      <div style={{ display: "flex", flexDirection: "column", gap: 16, minWidth: 0 }}>
        <div style={{ display: "flex", alignItems: "center", gap: 12, flexWrap: "wrap" }}>
          <a href={`/a/${slug}`} target="_blank" rel="noreferrer" style={{
            fontFamily: "var(--f-mono)", fontSize: 13, color: "var(--gold)",
            textDecoration: "underline", textUnderlineOffset: 3,
          }}>discotheque.pages.dev/a/{slug}</a>
          <span style={{ fontSize: 11, color: "var(--ink-4)", letterSpacing: "0.18em", textTransform: "uppercase", fontWeight: 700 }}>
            {stats.views} views
          </span>
          <span style={{ flex: 1 }} />
          <button onClick={() => setShowPreview(v => !v)} style={ghostBtnAA}>
            {showPreview ? "Hide preview" : "Show preview"}
          </button>
          <button onClick={save} disabled={saving} style={pinkBtnAA}>{saving ? "Saving…" : "Save"}</button>
          <button onClick={onDeleted} style={{ ...ghostBtnAA, color: "var(--pink)" }}>Delete page</button>
        </div>

        {err && <div style={{ color: "var(--pink)", fontSize: 13 }}>{err}</div>}

        {/* Cover media (image/video override) */}
        <Card title="Cover">
          <CoverMediaPicker slug={slug} doc={doc} onChanged={load} />
        </Card>

        {/* Identity */}
        <Card title="Identity">
          <Field label="Artist name">
            <input value={doc.artistName} onChange={e => patch({ artistName: e.target.value })} style={inputAA} />
          </Field>
          <Field label="Tagline / bio">
            <textarea
              value={doc.bio}
              onChange={e => patch({ bio: e.target.value })}
              rows={2}
              maxLength={280}
              style={{ ...inputAA, resize: "vertical", fontFamily: "inherit" }}
            />
            <div style={hint}>{(doc.bio || "").length}/280</div>
          </Field>
          <Field label="">
            <label style={{ display: "flex", alignItems: "center", gap: 8, fontSize: 13, color: "var(--ink-2)" }}>
              <input type="checkbox" checked={!!doc.verified} onChange={e => patch({ verified: e.target.checked })} style={{ accentColor: "var(--gold)" }} />
              Show verified check next to artist name
            </label>
          </Field>
        </Card>

        {/* Share preview / OG tags */}
        <Card title="Share preview (iMessage, WhatsApp, etc.)">
          <div style={hint}>
            How your page looks when shared in chat apps. The title acts as the
            "call to action" since chat previews don't have buttons. Leave blank
            for auto-defaults based on the featured album + platform list.
          </div>
          <Field label="Title (acts as CTA)">
            <input
              value={doc.ogTitle || ""}
              onChange={e => patch({ ogTitle: e.target.value })}
              placeholder={doc.featured?.albumId
                ? `Listen to ${(albumOptions.find(a => a.id === doc.featured.albumId) || {}).title || "…"} — ${doc.artistName || "Artist"}`
                : doc.artistName || "Artist"}
              maxLength={120}
              style={inputAA}
            />
            <div style={hint}>{(doc.ogTitle || "").length}/120</div>
          </Field>
          <Field label="Description (sub-line under the title)">
            <textarea
              value={doc.ogDescription || ""}
              onChange={e => patch({ ogDescription: e.target.value })}
              placeholder="Out now on Spotify · Apple Music · Tidal · Amazon Music · iTunes"
              rows={2}
              maxLength={280}
              style={{ ...inputAA, resize: "vertical", fontFamily: "inherit" }}
            />
            <div style={hint}>{(doc.ogDescription || "").length}/280</div>
          </Field>
          <div style={{
            marginTop: 8, padding: 12,
            background: "var(--bg-1)", border: "1px solid var(--line)", borderRadius: 10,
            display: "flex", gap: 12, alignItems: "center",
          }}>
            <div style={{
              width: 56, height: 56, borderRadius: 8, overflow: "hidden",
              background: "var(--bg-2)", flex: "0 0 56px",
            }}>
              {doc.featured?.albumId && <img src={`/api/cover/${doc.featured.albumId}`} alt="" style={{ width: "100%", height: "100%", objectFit: "cover" }} />}
            </div>
            <div style={{ flex: 1, minWidth: 0 }}>
              <div style={{ fontSize: 14, fontWeight: 700, color: "var(--ink)", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>
                {doc.ogTitle ||
                  (doc.featured?.albumId
                    ? `Listen to ${(albumOptions.find(a => a.id === doc.featured.albumId) || {}).title || "…"} — ${doc.artistName || "Artist"}`
                    : (doc.artistName || "Artist"))}
              </div>
              <div style={{ fontSize: 12, color: "var(--ink-3)", marginTop: 2, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>
                {doc.ogDescription ||
                  (() => {
                    const labels = [];
                    if (doc.primary?.label) labels.push(doc.primary.label.replace(/^Listen on\s+|^Stream on\s+/i, "").trim());
                    for (const l of (doc.links || [])) {
                      const n = (l.label || l.icon || "").replace(/^Listen on\s+|^Stream on\s+/i, "").trim();
                      if (n && !labels.includes(n)) labels.push(n);
                    }
                    return labels.length ? `Out now on ${labels.slice(0, 5).join(" · ")}` : (doc.artistName || "Artist") + " on Discothèque";
                  })()}
              </div>
              <div style={{ fontSize: 10, color: "var(--ink-4)", marginTop: 4, letterSpacing: "0.04em", textTransform: "uppercase" }}>
                discotheque.pages.dev
              </div>
            </div>
          </div>
        </Card>

        {/* Featured release */}
        <Card title="Featured release">
          <Field label="Album">
            <select
              value={doc.featured?.albumId || ""}
              onChange={e => patchFeatured({ albumId: e.target.value })}
              style={inputAA}
            >
              <option value="">— None —</option>
              {albumOptions.map(a => (
                <option key={a.id} value={a.id}>{a.title} ({a.artist})</option>
              ))}
            </select>
            <div style={hint}>Used for the page title, meta (track count, duration) and fallback cover image.</div>
          </Field>
          <div style={{ display: "grid", gridTemplateColumns: "1.4fr 1fr 1fr", gap: 10 }}>
            <Field label="Eyebrow label">
              <input value={doc.featured?.eyebrow || ""} placeholder="NEW ALBUM · OUT NOW" onChange={e => patchFeatured({ eyebrow: e.target.value })} style={inputAA} />
            </Field>
            <Field label="Genre">
              <input value={doc.featured?.genre || ""} placeholder="Eurodisco" onChange={e => patchFeatured({ genre: e.target.value })} style={inputAA} />
            </Field>
            <Field label="Duration label">
              <input value={doc.featured?.durationLabel || ""} placeholder="50 min" onChange={e => patchFeatured({ durationLabel: e.target.value })} style={inputAA} />
            </Field>
          </div>
        </Card>

        {/* Primary CTA */}
        <Card title="Primary call-to-action">
          <div style={hint}>Big colored button above the platform list. If a link in the list has the same URL, it's hidden automatically.</div>
          <div style={{ display: "grid", gridTemplateColumns: "180px 1fr", gap: 10 }}>
            <Field label="Icon / platform">
              <IconPicker
                value={doc.primary?.icon || "spotify"}
                onChange={(icon) => patchPrimary({ icon, label: doc.primary?.label || ICON_LABELS[icon], color: doc.primary?.color || PLATFORM_COLORS[icon] || "#ff3d9a" })}
              />
            </Field>
            <div style={{ display: "grid", gridTemplateColumns: "1fr 110px", gap: 10 }}>
              <Field label="Button label">
                <input value={doc.primary?.label || ""} placeholder="Listen on Spotify" onChange={e => patchPrimary({ label: e.target.value })} style={inputAA} />
              </Field>
              <Field label="Color">
                <div style={{ display: "flex", gap: 4 }}>
                  <input type="color" value={doc.primary?.color || PLATFORM_COLORS[doc.primary?.icon || "spotify"] || "#ff3d9a"}
                    onChange={e => patchPrimary({ color: e.target.value })}
                    style={{ width: 36, height: 38, padding: 0, border: "1px solid var(--line-strong)", borderRadius: 8, background: "var(--bg-1)" }} />
                  <input value={doc.primary?.color || ""} placeholder="#1DB954" onChange={e => patchPrimary({ color: e.target.value })} style={{ ...inputAA, fontFamily: "var(--f-mono)", fontSize: 12 }} />
                </div>
              </Field>
            </div>
          </div>
          <Field label="URL">
            <input value={doc.primary?.url || ""} placeholder="https://open.spotify.com/album/…" onChange={e => patchPrimary({ url: e.target.value })} style={inputAA} />
          </Field>
          <Field label="Optional sub-label">
            <input value={doc.primary?.sub || ""} placeholder="Most popular" onChange={e => patchPrimary({ sub: e.target.value })} style={inputAA} />
          </Field>
          {doc.primary && (
            <button onClick={() => patch({ primary: null })} style={{ alignSelf: "flex-start", color: "var(--pink)", fontSize: 11, letterSpacing: "0.18em", textTransform: "uppercase", fontWeight: 700 }}>Remove primary CTA</button>
          )}
        </Card>

        {/* Platform links */}
        <Card title="Platform links">
          <div style={{ display: "flex", flexDirection: "column", gap: 8 }}>
            {(doc.links || []).map((l, i) => (
              <LinkRow key={l.id || i} value={l} clicks={stats.clicks[l.id] || 0}
                onChange={p => updateLink("links", i, p)}
                onRemove={() => removeLink("links", i)}
                onUp={() => moveLink("links", i, -1)}
                onDown={() => moveLink("links", i, +1)}
                disableUp={i === 0}
                disableDown={i === (doc.links || []).length - 1} />
            ))}
            <button onClick={() => addLink("links")} style={{ ...ghostBtnAA, alignSelf: "flex-start" }}>+ Add link</button>
          </div>
        </Card>

        {/* Socials */}
        <Card title="Social icons">
          <div style={hint}>Small round buttons at the bottom of the page.</div>
          <div style={{ display: "flex", flexDirection: "column", gap: 8 }}>
            {(doc.socials || []).map((s, i) => (
              <LinkRow key={s.id || i} value={s} clicks={stats.clicks[s.id] || 0}
                onChange={p => updateLink("socials", i, p)}
                onRemove={() => removeLink("socials", i)}
                onUp={() => moveLink("socials", i, -1)}
                onDown={() => moveLink("socials", i, +1)}
                disableUp={i === 0}
                disableDown={i === (doc.socials || []).length - 1} />
            ))}
            <button onClick={() => addLink("socials")} style={{ ...ghostBtnAA, alignSelf: "flex-start" }}>+ Add social</button>
          </div>
        </Card>

        <Card title="Footer">
          <label style={{ display: "flex", alignItems: "center", gap: 8, fontSize: 13, color: "var(--ink-2)" }}>
            <input type="checkbox" checked={doc.showOrigin !== false} onChange={e => patch({ showOrigin: e.target.checked })} style={{ accentColor: "var(--gold)" }} />
            Show "Powered by Discothèque"
          </label>
        </Card>

        <div style={{ display: "flex", gap: 10, padding: "12px 0 40px" }}>
          <button onClick={save} disabled={saving} style={pinkBtnAA}>{saving ? "Saving…" : "Save changes"}</button>
          <a href={`/a/${slug}`} target="_blank" rel="noreferrer" style={ghostBtnAA}>Open page →</a>
        </div>
      </div>

      {showPreview && (
        <div style={{ position: "sticky", top: 16 }}>
          <div style={{ fontSize: 11, color: "var(--ink-4)", letterSpacing: "0.18em", textTransform: "uppercase", fontWeight: 700, marginBottom: 8 }}>
            Live preview
          </div>
          <div style={{ border: "1px solid var(--line)", borderRadius: 24, overflow: "hidden", background: "var(--bg-0)", aspectRatio: "9 / 19.5", position: "relative" }}>
            <iframe key={previewKey} src={previewUrl} title="Artist page preview" style={{ width: "100%", height: "100%", border: 0, display: "block" }} />
          </div>
          <div style={{ fontSize: 11, color: "var(--ink-4)", marginTop: 6, textAlign: "center" }}>Save to refresh preview</div>
        </div>
      )}
    </div>
  );
}

// ---------------------------------------------------------------
// Cover media picker — upload image or video, or use album cover fallback.
// ---------------------------------------------------------------
function CoverMediaPicker({ slug, doc, onChanged }) {
  const inputRef = useRefAA(null);
  const [busy, setBusy] = useStateAA(false);
  const [progress, setProgress] = useStateAA(0);
  const [err, setErr] = useStateAA("");
  const cm = doc.coverMedia;

  async function upload(file) {
    if (!file) return;
    const isVideo = /^video\//.test(file.type) || /\.(mp4|mov|webm|m4v)$/i.test(file.name);
    const type = isVideo ? "video" : "image";
    const mime = file.type || (isVideo ? "video/mp4" : "image/jpeg");

    if (isVideo && file.size > 25 * 1024 * 1024) {
      const ok = confirm(`This video is ${(file.size/1e6).toFixed(1)} MB. Large videos make the page slow to load. Upload anyway?`);
      if (!ok) return;
    }

    setBusy(true); setErr(""); setProgress(0);
    try {
      const res = await xhrPut(
        `/api/admin/artist-page/${encodeURIComponent(slug)}/media?type=${type}`,
        file, mime, p => setProgress(p),
      );
      if (res.status >= 200 && res.status < 300) {
        onChanged?.();
      } else {
        setErr("Upload failed: HTTP " + res.status);
      }
    } catch (e) {
      setErr(e.message);
    }
    setBusy(false);
    setProgress(0);
  }
  function xhrPut(url, body, contentType, onProgress) {
    return new Promise((resolve, reject) => {
      const xhr = new XMLHttpRequest();
      xhr.open("PUT", url);
      xhr.setRequestHeader("content-type", contentType);
      xhr.withCredentials = true;
      xhr.upload.onprogress = e => { if (e.lengthComputable) onProgress(e.loaded / e.total); };
      xhr.onload = () => resolve({ status: xhr.status });
      xhr.onerror = () => reject(new Error("Network error"));
      xhr.send(body);
    });
  }
  async function removeMedia() {
    if (!confirm("Remove the custom cover? The page will fall back to the album cover image.")) return;
    setBusy(true);
    try {
      await fetch(`/api/admin/artist-page/${encodeURIComponent(slug)}/media`, { method: "DELETE", credentials: "include" });
      onChanged?.();
    } catch (e) {}
    setBusy(false);
  }

  return (
    <div style={{ display: "flex", flexDirection: "column", gap: 10 }}>
      <div style={hint}>
        Drop an image (JPG/PNG/WEBP) or a short video (MP4/MOV/WEBM). Videos loop muted on the page.
        If nothing's set, the page uses the featured album's cover.
      </div>
      <div style={{ display: "flex", gap: 14, alignItems: "center", flexWrap: "wrap" }}>
        <div style={{
          width: 96, height: 96, borderRadius: 12, overflow: "hidden",
          background: "var(--bg-1)", border: "1px solid var(--line-strong)",
          display: "grid", placeItems: "center",
        }}>
          {cm?.type === "video" ? (
            <video src={`/a/${slug}/media?bust=${doc.updatedAt}`} muted loop autoPlay playsInline style={{ width: "100%", height: "100%", objectFit: "cover" }} />
          ) : cm?.type === "image" ? (
            <img src={`/a/${slug}/media?bust=${doc.updatedAt}`} style={{ width: "100%", height: "100%", objectFit: "cover" }} alt="" />
          ) : doc.featured?.albumId ? (
            <img src={`/api/cover/${doc.featured.albumId}`} style={{ width: "100%", height: "100%", objectFit: "cover" }} alt="" />
          ) : (
            <span style={{ color: "var(--ink-4)", fontSize: 10, letterSpacing: "0.18em" }}>NO ART</span>
          )}
        </div>
        <div style={{ flex: 1, minWidth: 200 }}>
          <div style={{ fontSize: 13, color: "var(--ink-2)" }}>
            {cm ? (cm.type === "video" ? "Custom video cover" : "Custom image cover") : "Using album cover"}
          </div>
          {cm && <div style={{ fontSize: 11, color: "var(--ink-4)", fontFamily: "var(--f-mono)", marginTop: 2 }}>{cm.mime}</div>}
          <div style={{ display: "flex", gap: 8, marginTop: 8, flexWrap: "wrap" }}>
            <button onClick={() => inputRef.current?.click()} disabled={busy} style={pinkBtnAA}>
              {busy ? `Uploading ${Math.round(progress * 100)}%…` : "Upload media"}
            </button>
            {cm && <button onClick={removeMedia} disabled={busy} style={{ ...ghostBtnAA, color: "var(--pink)" }}>Remove</button>}
          </div>
          {err && <div style={{ color: "var(--pink)", fontSize: 12, marginTop: 6 }}>{err}</div>}
          <input
            ref={inputRef}
            type="file"
            accept="image/jpeg,image/png,image/webp,image/gif,video/mp4,video/quicktime,video/webm"
            hidden
            onChange={e => { const f = e.target.files?.[0]; if (f) upload(f); e.target.value = ""; }}
          />
        </div>
      </div>
      <div style={hint}>
        Tip: videos render best at 720×720, &lt;2 MB, H.264, no audio.
        For the included <code style={codeStyle}>scripts/upload-cover-media.mjs</code> path, the CLI also generates an optimized 720p version + poster image automatically.
      </div>
    </div>
  );
}

// ---------------------------------------------------------------
// Sub-components
// ---------------------------------------------------------------
function Card({ title, children }) {
  return (
    <div style={{
      background: "var(--bg-2)",
      border: "1px solid var(--line)",
      borderRadius: 14,
      padding: 18,
      display: "flex", flexDirection: "column", gap: 10,
    }}>
      <h3 style={{
        fontFamily: "var(--f-display)", fontWeight: 800, fontSize: 13,
        letterSpacing: "0.18em", textTransform: "uppercase",
        margin: "0 0 4px", color: "var(--ink-2)",
      }}>{title}</h3>
      {children}
    </div>
  );
}
function Field({ label, children }) {
  return (
    <label style={{ display: "flex", flexDirection: "column", gap: 4 }}>
      {label && <span style={{ fontSize: 11, color: "var(--ink-3)", letterSpacing: "0.05em" }}>{label}</span>}
      {children}
    </label>
  );
}
function IconPicker({ value, onChange }) {
  return (
    <select value={value} onChange={e => onChange(e.target.value)} style={inputAA}>
      {ALL_ICONS.map(i => <option key={i} value={i}>{ICON_LABELS[i]}</option>)}
    </select>
  );
}
function LinkRow({ value, clicks, onChange, onRemove, onUp, onDown, disableUp, disableDown }) {
  return (
    <div style={{
      display: "grid",
      gridTemplateColumns: "auto 160px 1fr 1.4fr auto auto",
      gap: 8,
      alignItems: "center",
      background: "var(--bg-1)",
      border: "1px solid var(--line)",
      borderRadius: 10,
      padding: "8px 10px",
    }}>
      <div style={{ display: "flex", flexDirection: "column", gap: 2 }}>
        <button onClick={onUp} disabled={disableUp} style={miniBtnAA}>▲</button>
        <button onClick={onDown} disabled={disableDown} style={miniBtnAA}>▼</button>
      </div>
      <select value={value.icon || "link"}
        onChange={e => onChange({ icon: e.target.value, label: value.label || ICON_LABELS[e.target.value] })}
        style={{ ...inputAA, fontSize: 12, padding: "8px 10px" }}>
        {ALL_ICONS.map(i => <option key={i} value={i}>{ICON_LABELS[i]}</option>)}
      </select>
      <input value={value.label || ""} onChange={e => onChange({ label: e.target.value })} placeholder="Label" style={{ ...inputAA, fontSize: 13, padding: "8px 10px" }} />
      <input value={value.url || ""} onChange={e => onChange({ url: e.target.value })} placeholder="https://…" style={{ ...inputAA, fontSize: 12, padding: "8px 10px", fontFamily: "var(--f-mono)" }} />
      <span style={{ fontSize: 11, color: clicks > 0 ? "var(--gold)" : "var(--ink-4)", fontFamily: "var(--f-mono)", minWidth: 32, textAlign: "right" }}>
        {clicks}↗
      </span>
      <button onClick={onRemove} style={{ color: "var(--ink-4)", fontSize: 14, padding: "0 4px" }}>×</button>
    </div>
  );
}

const inputAA = {
  background: "var(--bg-1)",
  border: "1px solid var(--line-strong)",
  borderRadius: 8,
  padding: "10px 12px",
  fontSize: 14,
  color: "var(--ink)",
  outline: "none",
  fontFamily: "inherit",
  width: "100%",
};
const pinkBtnAA = {
  background: "var(--pink)", color: "var(--bg-0)",
  padding: "10px 18px", borderRadius: 999,
  fontFamily: "var(--f-display)", fontWeight: 800, fontSize: 12,
  letterSpacing: "0.18em", textTransform: "uppercase",
  cursor: "pointer", border: 0,
};
const ghostBtnAA = {
  background: "var(--bg-3)", color: "var(--ink)",
  padding: "10px 18px", borderRadius: 999,
  fontFamily: "var(--f-display)", fontWeight: 800, fontSize: 12,
  letterSpacing: "0.18em", textTransform: "uppercase",
  cursor: "pointer", border: "1px solid var(--line-strong)",
  textDecoration: "none",
  display: "inline-block",
};
const chipStyle = {
  ...ghostBtnAA,
  padding: "6px 12px",
  background: "var(--bg-2)",
  fontSize: 11,
  color: "var(--ink-2)",
};
const activeChipStyle = {
  ...chipStyle,
  background: "var(--gold)",
  color: "var(--bg-0)",
  borderColor: "var(--gold)",
};
const miniBtnAA = {
  background: "var(--bg-2)", color: "var(--ink-3)",
  border: "1px solid var(--line)",
  borderRadius: 4, padding: "1px 4px", fontSize: 9,
  cursor: "pointer",
};
const codeStyle = {
  background: "var(--bg-0)", padding: "1px 4px",
  borderRadius: 3, fontFamily: "var(--f-mono)", fontSize: 11,
  color: "var(--ink-2)",
};
const hint = { fontSize: 11, color: "var(--ink-4)", marginTop: 2, lineHeight: 1.45 };

Object.assign(window, { ArtistPageSection });
