const { useState, useEffect, useRef } = React;

/* ─── data ─────────────────────────────────────────────────────────────── */

// Celebrity photo categories
const CATEGORIES = [
  'Red Carpet',
  'Candid',
  'Events',
  'Award Shows',
  'Premieres',
  'Interviews',
  'Behind the Scenes',
  'Photoshoots',
  'Portraits',
  'Magazine',
  'Fashion',
  'Street Style',
  'Concerts',
  'Performances',
  'Public Appearances',
  'Promotional',
  'Movie Stills',
  'TV Stills',
  'Game Characters',
  'Game Screenshots',
  'Character Art',
  'Concept Art',
  'Official Artwork',
  'Anime',
  'Cosplay',
  'Fan Art',
  'Wallpapers',
  'Group Photos',
  'Selfies',
  'Throwbacks',
  'Rare Photos',
  'Archives',
  'Other'
];

function initial(name) {
  return (name || '?').split(' ').map(w => w[0]).join('').toUpperCase();
}
function fmt(n) {
  if (n >= 1e6) return (Math.round(n / 1e5) / 10) + 'M';
  if (n >= 1e3) return (Math.round(n / 1e2) / 10) + 'K';
  return String(n);
}
// Deterministic color per name, since celebrities are created by users
// rather than seeded with a fixed palette.
function hueFromName(name) {
  let hash = 0;
  for (let i = 0; i < (name || '').length; i++) hash = (hash * 31 + name.charCodeAt(i)) % 360;
  return hash < 0 ? hash + 360 : hash;
}

// Curated gradient pairs (not a raw hue-from-name) so a banner never lands
// in an ugly high-chroma olive/yellow-green zone — every pair is drawn from
// hues that already appear in the ambient background blobs, so profile
// banners always feel like part of the same glass palette.
const BANNER_GRADIENTS = [
  ['oklch(66% 0.19 350)', 'oklch(60% 0.16 290)'],
  ['oklch(64% 0.17 255)', 'oklch(70% 0.13 205)'],
  ['oklch(62% 0.18 290)', 'oklch(66% 0.19 350)'],
  ['oklch(68% 0.14 205)', 'oklch(64% 0.17 255)'],
  ['oklch(68% 0.17 320)', 'oklch(64% 0.17 255)'],
  ['oklch(66% 0.19 350)', 'oklch(70% 0.13 205)'],
];
function bannerGradient(name) {
  const [a, b] = BANNER_GRADIENTS[hueFromName(name) % BANNER_GRADIENTS.length];
  return `linear-gradient(135deg, ${a}, ${b})`;
}

// Smart celebrity search: case-insensitive fuzzy matching that guesses what you're looking for
function searchCelebrities(query, celebList) {
  if (!query || !query.trim()) return [];
  const q = query.toLowerCase().trim();

  // Score each celebrity based on match quality
  const scored = celebList.map(c => {
    const name = c.name.toLowerCase();
    let score = 0;

    // Exact match (highest priority)
    if (name === q) score = 1000;
    // Starts with query
    else if (name.startsWith(q)) score = 500;
    // Contains query as whole word (e.g., "taylor" in "taylor swift")
    else if (name.split(/\s+/).some(word => word.startsWith(q))) score = 300;
    // Contains query anywhere
    else if (name.includes(q)) score = 100;
    // Fuzzy: all chars of query are in name in order (loose matching)
    else {
      let qIdx = 0;
      for (let i = 0; i < name.length && qIdx < q.length; i++) {
        if (name[i] === q[qIdx]) qIdx++;
      }
      if (qIdx === q.length) score = 50;
    }

    return { ...c, score };
  }).filter(c => c.score > 0)
    .sort((a, b) => b.score - a.score);

  return scored.slice(0, 5);
}

// Category names as rule34-style tags ("Red Carpet" -> "red_carpet"), so
// category is just another tag rather than a separate filter mechanism.
function slugTag(str) {
  return (str || '').toLowerCase().trim().replace(/\s+/g, '_');
}
function itemTags(item) {
  return [...new Set([...(item.tags || []).map(t => t.toLowerCase()), slugTag(item.category)])];
}
// Tag frequency across all media, for the popular-tags cloud and autocomplete.
function tagCounts(mediaList) {
  const counts = {};
  mediaList.forEach(m => itemTags(m).forEach(t => { counts[t] = (counts[t] || 0) + 1; }));
  return Object.entries(counts).sort((a, b) => b[1] - a[1]);
}
// Parse a rule34-style space-separated tag query; every token must match.
function parseTagQuery(query) {
  return query.toLowerCase().trim().split(/\s+/).filter(Boolean);
}

function getInitialDarkMode() {
  try {
    const saved = localStorage.getItem('r34vault-theme');
    if (saved) return saved === 'dark';
  } catch (e) {}
  return !!(window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches);
}

// Tiny hash router: '#/c/<id>' profile, '#/m/<id>' media page, '#/f/<id>'
// public folder, '#/folders' explore public folders, '#/me' library, else home.
function parseHash(hash) {
  const parts = (hash || '').replace(/^#\/?/, '').split('/').filter(Boolean);
  if (parts[0] === 'c' && parts[1]) return { view: 'profile', id: parts[1] };
  if (parts[0] === 'm' && parts[1]) return { view: 'media', id: parts[1] };
  if (parts[0] === 'f' && parts[1]) return { view: 'folder', id: parts[1] };
  if (parts[0] === 'folders') return { view: 'explore' };
  if (parts[0] === 'me') return { view: 'me' };
  return { view: 'home' };
}

function freshDraft() {
  return {
    file: null, fileSelected: false, fileType: null, previewUrl: null,
    celebrityQuery: '', celebrityId: null, newCelebName: null,
    category: null, caption: '', tagsInput: '',
  };
}

/* ─── theme ──────────────────────────────────────────────────────────────── */
function makeTheme(dark) {
  // Glass surfaces need a translucent, blurred background so the floating
  // color blobs behind the page show through — everything here is
  // deliberately semi-transparent rather than a flat fill.
  return dark ? {
    dark: true,
    bg: 'transparent',
    text: 'oklch(96% 0.006 75)',
    muted: 'oklch(76% 0.012 75)',
    card: 'oklch(28% 0.02 275 / 0.42)',
    subtle: 'oklch(32% 0.02 275 / 0.35)',
    border: 'oklch(100% 0 0 / 0.14)',
    glassHighlight: 'oklch(100% 0 0 / 0.22)',
    navBg: 'oklch(14% 0.02 275 / 0.55)',
    overlay: 'oklch(8% 0.02 275 / 0.55)',
    accent: 'oklch(68% 0.19 350)',
    accent2: 'oklch(66% 0.16 255)',
    accentBorder: 'oklch(68% 0.19 350 / 0.5)',
    glow: 'oklch(68% 0.19 350 / 0.55)',
    danger: 'oklch(75% 0.16 25)',
    dangerBg: 'oklch(35% 0.08 25 / 0.4)',
  } : {
    dark: false,
    bg: 'transparent',
    text: 'oklch(24% 0.02 275)',
    muted: 'oklch(42% 0.02 275)',
    card: 'oklch(100% 0 0 / 0.45)',
    subtle: 'oklch(100% 0 0 / 0.32)',
    border: 'oklch(100% 0 0 / 0.5)',
    glassHighlight: 'oklch(100% 0 0 / 0.75)',
    navBg: 'oklch(100% 0 0 / 0.4)',
    overlay: 'oklch(20% 0.02 275 / 0.35)',
    accent: 'oklch(60% 0.19 350)',
    accent2: 'oklch(58% 0.16 255)',
    accentBorder: 'oklch(60% 0.19 350 / 0.5)',
    glow: 'oklch(60% 0.19 350 / 0.45)',
    danger: 'oklch(55% 0.18 25)',
    dangerBg: 'oklch(96% 0.03 25 / 0.7)',
  };
}

/* CSS custom properties so plain CSS (index.html) can share theme colors for
   hover states that inline styles can't express (:hover, :active). */
function themeVars(theme) {
  return { '--glass-hover': theme.subtle, '--glass-border': theme.glassHighlight, '--glow': theme.glow };
}

/* Frosted-glass surface: translucent fill + blur + a bright top/left inner
   highlight, which is what reads as "glass" rather than plain transparency. */
function glassStyle(theme, { strong } = {}) {
  return {
    background: strong ? theme.card : theme.subtle,
    backdropFilter: `blur(${strong ? 22 : 14}px) saturate(160%)`,
    WebkitBackdropFilter: `blur(${strong ? 22 : 14}px) saturate(160%)`,
    border: `1px solid ${theme.border}`,
    boxShadow: `inset 0 1px 0 ${theme.glassHighlight}, 0 8px 30px oklch(0% 0 0 / ${theme.dark ? 0.35 : 0.12})`,
  };
}

/* ─── Icon: Google Material Symbols instead of emoji, so glyphs render
   consistently across OS/browser font support instead of relying on the
   system emoji font. ── */
function Icon({ name, size = 18, filled = false, style, className }) {
  return (
    <span
      className={`gicon${filled ? ' filled' : ''}${className ? ' ' + className : ''}`}
      style={{ fontSize: size, ...style }}
      aria-hidden="true"
    >
      {name}
    </span>
  );
}

/* ─── style helpers ─────────────────────────────────────────────────────── */
const chipBase = { padding: '8px 16px', borderRadius: '999px', fontSize: '13px', fontWeight: '600', cursor: 'pointer', whiteSpace: 'nowrap', userSelect: 'none' };
const chipOff = theme => ({ ...chipBase, ...glassStyle(theme), color: theme.text });
const chipOn = theme => ({ ...chipBase, border: `1px solid ${theme.accentBorder}`, background: `linear-gradient(135deg, ${theme.accent}, ${theme.accent2})`, color: 'white', boxShadow: `0 4px 16px ${theme.glow}` });

/* ─── Avatar (real Wikipedia photo when available, else colored initials) ── */
function Avatar({ name, thumbnail, size, border }) {
  const hue = hueFromName(name);
  return (
    <div style={{
      width: size, height: size, borderRadius: '50%', flex: 'none',
      display: 'flex', alignItems: 'center', justifyContent: 'center',
      fontFamily: "'Bebas Neue'", fontSize: Math.round(size * 0.34) + 'px',
      color: 'oklch(30% 0.02 75)',
      backgroundColor: `oklch(88% 0.05 ${hue})`,
      backgroundImage: thumbnail ? `url("${thumbnail}")` : 'none',
      backgroundSize: 'cover', backgroundPosition: 'center',
      border: border || 'none',
    }}>
      {!thumbnail && initial(name)}
    </div>
  );
}

/* ─── MediaCard ─────────────────────────────────────────────────────────── */
// Cards keep the image's natural aspect ratio in the masonry flow (no
// cropping) so any photo — tall portrait or wide landscape — sits right; a
// short fade-in on load smooths the pop-in for slow or very large images.
function MediaCard({ item, showCelebName, liked, canDelete, saved, theme, onOpen, onLike, onDelete, onSave }) {
  const [hovered, setHovered] = useState(false);
  const [loaded, setLoaded] = useState(false);
  return (
    <div
      className="glass-card"
      onClick={onOpen}
      onMouseEnter={() => setHovered(true)}
      onMouseLeave={() => setHovered(false)}
      style={{ breakInside: 'avoid', marginBottom: '18px', borderRadius: '20px', overflow: 'hidden', position: 'relative', cursor: 'pointer', ...glassStyle(theme) }}
    >
      {item.type === 'video' ? (
        <video src={item.url} muted preload="metadata" onLoadedData={() => setLoaded(true)}
          className={`mc-media${loaded ? ' loaded' : ''}`} style={{ width: '100%', display: 'block' }} />
      ) : (
        <img src={item.url} alt={item.caption || ''} loading="lazy" onLoad={() => setLoaded(true)}
          className={`mc-media${loaded ? ' loaded' : ''}`} style={{ width: '100%', display: 'block' }} />
      )}
      {item.type === 'video' && (
        <div style={{ position: 'absolute', top: '50%', left: '50%', transform: 'translate(-50%,-50%)', width: '44px', height: '44px', borderRadius: '50%', background: 'oklch(0% 0 0 / 0.45)', color: 'white', display: 'flex', alignItems: 'center', justifyContent: 'center', pointerEvents: 'none' }}><Icon name="play_arrow" filled size={22} /></div>
      )}
      {canDelete && (
        <button onClick={e => { e.stopPropagation(); onDelete(e); }} title="Delete"
          style={{ position: 'absolute', top: '8px', right: '8px', width: '26px', height: '26px', borderRadius: '50%', border: '1px solid oklch(100% 0 0 / 0.3)', background: 'oklch(20% 0 0 / 0.4)', backdropFilter: 'blur(6px)', WebkitBackdropFilter: 'blur(6px)', color: 'white', cursor: 'pointer', display: 'flex', alignItems: 'center', justifyContent: 'center', opacity: hovered ? 1 : 0, transition: 'opacity 0.15s ease' }}>
          <Icon name="close" size={15} />
        </button>
      )}
      {onSave && (
        <button onClick={e => { e.stopPropagation(); onSave(e); }} title="Save to folder"
          style={{ position: 'absolute', top: '8px', left: '8px', width: '26px', height: '26px', borderRadius: '50%', border: '1px solid oklch(100% 0 0 / 0.3)', background: 'oklch(20% 0 0 / 0.4)', backdropFilter: 'blur(6px)', WebkitBackdropFilter: 'blur(6px)', color: 'white', cursor: 'pointer', display: 'flex', alignItems: 'center', justifyContent: 'center', opacity: (hovered || saved) ? 1 : 0, transition: 'opacity 0.15s ease' }}>
          <Icon name="bookmark" filled={saved} size={15} />
        </button>
      )}
      <div style={{
        position: 'absolute', right: '10px', bottom: '10px', left: '10px', opacity: hovered ? 1 : 0, transform: hovered ? 'translateY(0)' : 'translateY(6px)', transition: 'opacity 0.2s ease, transform 0.2s ease',
        display: 'flex', flexDirection: 'column', padding: '10px 12px', borderRadius: '14px',
        background: 'oklch(15% 0.02 275 / 0.45)', backdropFilter: 'blur(14px) saturate(160%)', WebkitBackdropFilter: 'blur(14px) saturate(160%)',
        border: '1px solid oklch(100% 0 0 / 0.18)', boxShadow: 'inset 0 1px 0 oklch(100% 0 0 / 0.25)',
      }}>
        {showCelebName && item.celebName && (
          <div style={{ color: 'white', fontSize: '13px', fontWeight: '700', marginBottom: '6px' }}>{item.celebName}</div>
        )}
        <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: '8px' }}>
          <div style={{ color: 'white', fontSize: '11px', opacity: 0.9, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{item.caption}</div>
          <div
            onClick={e => { e.stopPropagation(); onLike(e); }}
            style={{ display: 'inline-flex', alignItems: 'center', gap: '4px', fontSize: '12px', fontWeight: '700', padding: '5px 10px', borderRadius: '999px', cursor: 'pointer', flexShrink: 0, background: liked ? `linear-gradient(135deg, ${theme.accent}, ${theme.accent2})` : 'oklch(100% 0 0 / 0.22)', boxShadow: liked ? `0 3px 12px ${theme.glow}` : 'none', color: 'white' }}
          >
            <Icon name="favorite" filled={liked} size={14} /> {fmt(item.likedBy.length)}
          </div>
        </div>
      </div>
    </div>
  );
}

/* ─── App ────────────────────────────────────────────────────────────────── */
function App() {
  const [view, setView] = useState('home');
  const [selectedId, setSelectedId] = useState(null);
  const [searchQuery, setSearchQuery] = useState('');
  const [searchFocused, setSearchFocused] = useState(false);
  const [profileTab, setProfileTab] = useState('All');
  const [myTab, setMyTab] = useState('All');
  const [currentUser, setCurrentUser] = useState(null);
  const [signInOpen, setSignInOpen] = useState(false);
  const [signInEmail, setSignInEmail] = useState('');
  const [signInPassword, setSignInPassword] = useState('');
  const [authError, setAuthError] = useState(null);
  const [authBusy, setAuthBusy] = useState(false);
  const [pendingAction, setPendingAction] = useState(null);
  const [uploadOpen, setUploadOpen] = useState(false);
  const [uploadStep, setUploadStep] = useState(1);
  const [submitting, setSubmitting] = useState(false);
  const [draft, setDraft] = useState(freshDraft);
  const [mediaId, setMediaId] = useState(null);
  const [darkMode, setDarkMode] = useState(getInitialDarkMode);
  const [toast, setToast] = useState(null);
  const [celebs, setCelebs] = useState([]);
  const [media, setMedia] = useState([]);
  const [wikiCache, setWikiCache] = useState({});
  const [initError, setInitError] = useState(null);
  const [uploadCountToday, setUploadCountToday] = useState(0);
  const [addCelebOpen, setAddCelebOpen] = useState(false);
  const [addCelebQuery, setAddCelebQuery] = useState('');
  const [addCelebPreview, setAddCelebPreview] = useState(null);
  const [addCelebLoading, setAddCelebLoading] = useState(false);
  const [addCelebImage, setAddCelebImage] = useState(null);
  const [addCelebImagePreview, setAddCelebImagePreview] = useState(null);
  const [addCelebImageUrl, setAddCelebImageUrl] = useState('');
  const [editCelebOpen, setEditCelebOpen] = useState(false);
  const [editCelebId, setEditCelebId] = useState(null);
  const [editCelebName, setEditCelebName] = useState('');
  const [editCelebBio, setEditCelebBio] = useState('');
  const [editCelebImage, setEditCelebImage] = useState(null);
  const [editCelebImageUrl, setEditCelebImageUrl] = useState('');
  const [editCelebImagePreview, setEditCelebImagePreview] = useState(null);
  const [editCelebLoading, setEditCelebLoading] = useState(false);
  const [folders, setFolders] = useState([]);
  const [libraryFolder, setLibraryFolder] = useState('all'); // 'all' | 'liked' | a folder id
  const [viewFolderId, setViewFolderId] = useState(null);
  const [viewedFolder, setViewedFolder] = useState(undefined); // undefined = loading, null = not found/private
  const [privacyCache, setPrivacyCache] = useState({}); // uid -> current isPrivate, looked up live (not frozen at upload time)
  const [publicFolders, setPublicFolders] = useState([]);
  const [exploreCat, setExploreCat] = useState('All');
  const [saveModalItem, setSaveModalItem] = useState(null);
  const [editProfileOpen, setEditProfileOpen] = useState(false);
  const [editProfileName, setEditProfileName] = useState('');
  const [editProfileImage, setEditProfileImage] = useState(null);
  const [editProfileImagePreview, setEditProfileImagePreview] = useState(null);
  const [editProfilePrivate, setEditProfilePrivate] = useState(false);
  const [editProfileLoading, setEditProfileLoading] = useState(false);
  const toastTimer = useRef(null);
  const searchInputRef = useRef(null);

  const getCeleb = id => celebs.find(c => c.id === id);
  const theme = makeTheme(darkMode);

  useEffect(() => {
    try { localStorage.setItem('r34vault-theme', darkMode ? 'dark' : 'light'); } catch (e) {}
    document.body.classList.toggle('dark', darkMode);
  }, [darkMode]);

  /* ── hash routing: URL drives view/selectedId/mediaId, so profiles and
     media pages are directly linkable and back/forward work as expected ── */
  useEffect(() => {
    const applyRoute = () => {
      const route = parseHash(window.location.hash);
      if (route.view === 'profile') { setView('profile'); setSelectedId(route.id); setProfileTab('All'); setSearchQuery(''); }
      else if (route.view === 'media') { setView('media'); setMediaId(route.id); }
      else if (route.view === 'folder') { setView('folder'); setViewFolderId(route.id); }
      else if (route.view === 'explore') { setView('explore'); setExploreCat('All'); }
      else if (route.view === 'me') { setView('me'); setSearchQuery(''); }
      else { setView('home'); setSelectedId(null); }
    };
    applyRoute();
    window.addEventListener('hashchange', applyRoute);
    return () => window.removeEventListener('hashchange', applyRoute);
  }, []);

  const showToast = msg => {
    if (toastTimer.current) clearTimeout(toastTimer.current);
    setToast(msg);
    toastTimer.current = setTimeout(() => setToast(null), 2600);
  };

  /* ── live data ── */
  useEffect(() => {
    let unsubAuth, unsubCelebs, unsubMedia;
    window.BackendReady.then(() => {
      unsubAuth = window.Backend.onAuthChange(setCurrentUser);
      unsubCelebs = window.Backend.subscribeCelebrities(setCelebs);
      unsubMedia = window.Backend.subscribeMedia(setMedia);
    }).catch(err => {
      console.error(err);
      setInitError(err.message || String(err));
    });
    return () => { unsubAuth && unsubAuth(); unsubCelebs && unsubCelebs(); unsubMedia && unsubMedia(); };
  }, []);

  /* ── keep the "X/5 used today" count fresh on My Library, not just
     when the upload modal is opened — otherwise it shows a stale 0 on
     devices/sessions where the upload modal was never opened ── */
  useEffect(() => {
    if (view !== 'me' || !currentUser) return;
    window.Backend.getUploadCountToday(currentUser.uid)
      .then(setUploadCountToday)
      .catch(err => console.error(err));
  }, [view, currentUser]);

  /* ── personal save/bookmark folders — subscribed whenever signed in,
     regardless of view, so the save-to-folder button works from anywhere ── */
  useEffect(() => {
    if (!currentUser) { setFolders([]); setLibraryFolder('all'); return; }
    const unsub = window.Backend.subscribeFolders(currentUser.uid, setFolders);
    return () => unsub && unsub();
  }, [currentUser]);

  /* ── public folder page (#/f/<id>) — a plain one-off fetch, not a
     subscription; the security rule decides whether it comes back at all ── */
  useEffect(() => {
    if (view !== 'folder' || !viewFolderId) return;
    setViewedFolder(undefined);
    window.Backend.getFolder(viewFolderId)
      .then(f => setViewedFolder(f && (f.isPublic || (currentUser && f.ownerUid === currentUser.uid)) ? f : null))
      .catch(() => setViewedFolder(null));
  }, [view, viewFolderId, currentUser]);

  /* ── explore public folders (#/folders) ── */
  useEffect(() => {
    if (view !== 'explore') return;
    const unsub = window.Backend.subscribePublicFolders(setPublicFolders);
    return () => unsub && unsub();
  }, [view]);

  /* ── Wikipedia enrichment for celebrity avatars/bios ── */
  useEffect(() => {
    celebs.forEach(c => {
      if (wikiCache[c.name] !== undefined) return;
      setWikiCache(prev => (prev[c.name] !== undefined ? prev : { ...prev, [c.name]: null }));
      window.Backend.fetchWikiSummary(c.name)
        .then(summary => setWikiCache(prev => ({ ...prev, [c.name]: summary })))
        .catch(() => {});
    });
    // eslint-disable-next-line
  }, [celebs]);

  /* ── live privacy lookup for the media page's "Uploaded by" line — the
     poster's *current* isPrivate setting governs every post they've ever
     made, not just posts uploaded after they flipped it, so this can't use
     the posterIsPrivate value frozen onto the media doc at upload time ── */
  useEffect(() => {
    const item = mediaId ? media.find(m => m.id === mediaId) : null;
    if (!item || privacyCache[item.uploaderUid] !== undefined) return;
    window.Backend.getUserSettings(item.uploaderUid)
      .then(settings => setPrivacyCache(prev => ({ ...prev, [item.uploaderUid]: !!settings.isPrivate })))
      .catch(() => {});
    // eslint-disable-next-line
  }, [mediaId, media]);

  // Safe here (after all hooks above, before any hooks below) — an early
  // return can't sit between two hook calls without breaking the Rules of
  // Hooks, but nothing after this point in the component calls a hook.
  if (initError) {
    return (
      <div style={{ minHeight: '100vh', display: 'flex', alignItems: 'center', justifyContent: 'center', fontFamily: "'Work Sans',system-ui,sans-serif", padding: '2rem', textAlign: 'center' }}>
        <div>
          <div style={{ fontSize: '18px', fontWeight: '700', marginBottom: '10px' }}>Couldn't connect</div>
          <div style={{ color: theme.muted, fontSize: '13px' }}>{initError}</div>
        </div>
      </div>
    );
  }

  /* navigation — pushing a hash keeps profile/media pages linkable and
     lets browser back/forward work; the hashchange listener above applies
     the resulting state */
  const goHome = () => { window.location.hash = ''; setView('home'); setSelectedId(null); };
  const openProfile = id => { window.location.hash = `#/c/${id}`; setView('profile'); setSelectedId(id); setProfileTab('All'); setSearchQuery(''); };
  const openMedia = id => { window.location.hash = `#/m/${id}`; setView('media'); setMediaId(id); };

  /* tag search: clicking a popular tag toggles it in the query (AND search);
     clicking an autocomplete suggestion completes whatever's being typed. */
  const toggleSearchTag = tag => {
    const tokens = parseTagQuery(searchQuery);
    const idx = tokens.indexOf(tag);
    const next = idx >= 0 ? tokens.filter((_, i) => i !== idx) : [...tokens, tag];
    setSearchQuery(next.join(' '));
  };
  const applyTagSuggestion = tag => {
    const tokens = parseTagQuery(searchQuery);
    tokens[tokens.length - 1] = tag;
    setSearchQuery(tokens.join(' ') + ' ');
    if (searchInputRef.current) searchInputRef.current.focus();
  };

  /* auth gate: run `action(user)` now if signed in, else prompt sign-in
     first and run it once that succeeds. Passes the fresh user object
     from the sign-in result rather than relying on `currentUser` state,
     since the onAuthChange listener updating that state can lag slightly
     behind the sign-in promise resolving. */
  const requireAuth = action => {
    if (currentUser) { action(currentUser); return; }
    setPendingAction(() => action);
    setSignInOpen(true);
  };

  const finishSignIn = user => {
    setSignInOpen(false);
    setSignInEmail(''); setSignInPassword(''); setAuthError(null);
    if (pendingAction) { const action = pendingAction; setPendingAction(null); action(user); }
  };

  const onSignInSubmit = async e => {
    e.preventDefault();
    setAuthError(null); setAuthBusy(true);
    try {
      const cred = await window.Backend.signInOrSignUp(signInEmail, signInPassword);
      finishSignIn(cred.user);
    } catch (err) {
      setAuthError(err.message.replace('Firebase: ', ''));
    } finally {
      setAuthBusy(false);
    }
  };

  const onGoogleSignIn = async () => {
    setAuthError(null); setAuthBusy(true);
    try {
      const cred = await window.Backend.signInGoogle();
      finishSignIn(cred.user);
    } catch (err) {
      setAuthError(err.message.replace('Firebase: ', ''));
    } finally {
      setAuthBusy(false);
    }
  };

  const onSignOut = () => window.Backend.signOutUser();

  /* navigation: your own library, mirrors a celebrity profile page */
  const goToMyLibrary = () => {
    requireAuth(() => { window.location.hash = '#/me'; setView('me'); setSearchQuery(''); });
  };

  /* follow / like */
  const toggleFollow = (id, e) => {
    if (e) e.stopPropagation();
    requireAuth(async user => {
      const celeb = getCeleb(id);
      if (!celeb) return;
      const isFollowing = celeb.followedBy.includes(user.uid);
      try {
        await window.Backend.toggleFollow(id, user.uid, isFollowing);
      } catch (err) {
        console.error(err);
        showToast('Could not update follow — try again.');
      }
    });
  };

  const toggleLike = (id, e) => {
    if (e) e.stopPropagation();
    requireAuth(async user => {
      const item = media.find(m => m.id === id);
      if (!item) return;
      const isLiked = item.likedBy.includes(user.uid);
      try {
        await window.Backend.toggleLike(id, user.uid, isLiked);
      } catch (err) {
        console.error(err);
        showToast('Could not update like — try again.');
      }
    });
  };

  const deleteMedia = (id, e) => {
    if (e) e.stopPropagation();
    if (!confirm('Delete this upload? This can\'t be undone.')) return;
    window.Backend.deleteMedia(id)
      .then(() => { if (mediaId === id) goHome(); })
      .catch(err => {
        console.error(err);
        showToast('Could not delete — try again.');
      });
  };

  /* folders: personal save/bookmark boards, always private to the owner */
  const selectLibraryFolder = key => { setLibraryFolder(key); setMyTab('All'); };

  const openNewFolder = () => {
    requireAuth(async user => {
      const name = (prompt('Folder name:') || '').trim();
      if (!name) return;
      try {
        const id = await window.Backend.createFolder(user.uid, name);
        selectLibraryFolder(id);
      } catch (err) {
        console.error(err);
        showToast('Could not create folder — try again.');
      }
    });
  };

  const renameActiveFolder = folder => {
    const name = (prompt('Rename folder:', folder.name) || '').trim();
    if (!name || name === folder.name) return;
    window.Backend.renameFolder(folder.id, name).catch(err => {
      console.error(err);
      showToast('Could not rename folder — try again.');
    });
  };

  const deleteActiveFolder = folder => {
    if (!confirm(`Delete "${folder.name}"? This can't be undone.`)) return;
    window.Backend.deleteFolder(folder.id)
      .then(() => selectLibraryFolder('all'))
      .catch(err => {
        console.error(err);
        showToast('Could not delete folder — try again.');
      });
  };

  const toggleActiveFolderPublic = folder => {
    window.Backend.setFolderPublic(folder.id, !folder.isPublic).catch(err => {
      console.error(err);
      showToast('Could not update folder — try again.');
    });
  };

  const setActiveFolderCategory = (folder, category) => {
    window.Backend.setFolderCategory(folder.id, category || null).catch(err => {
      console.error(err);
      showToast('Could not update folder — try again.');
    });
  };

  const copyFolderLink = folder => {
    const url = `${window.location.origin}${window.location.pathname}#/f/${folder.id}`;
    navigator.clipboard.writeText(url)
      .then(() => showToast('Folder link copied.'))
      .catch(() => showToast(url));
  };

  const openSaveModal = (item, e) => {
    if (e) e.stopPropagation();
    requireAuth(() => setSaveModalItem(item));
  };
  const closeSaveModal = () => setSaveModalItem(null);

  const toggleSaveInFolder = (folder, mediaItem) => {
    const inFolder = (folder.mediaIds || []).includes(mediaItem.id);
    window.Backend.toggleFolderItem(folder.id, mediaItem.id, inFolder).catch(err => {
      console.error(err);
      showToast('Could not update folder — try again.');
    });
  };

  const createFolderAndSave = () => {
    requireAuth(async user => {
      const name = (prompt('Folder name:') || '').trim();
      if (!name || !saveModalItem) return;
      try {
        const id = await window.Backend.createFolder(user.uid, name);
        await window.Backend.toggleFolderItem(id, saveModalItem.id, false);
      } catch (err) {
        console.error(err);
        showToast('Could not create folder — try again.');
      }
    });
  };

  /* profile: name, photo, privacy */
  const openEditProfile = () => {
    if (!currentUser) return;
    setEditProfileName(currentUser.displayName || '');
    setEditProfileImage(null);
    setEditProfileImagePreview(currentUser.photoURL || null);
    setEditProfilePrivate(false);
    setEditProfileOpen(true);
    window.Backend.getUserSettings(currentUser.uid)
      .then(settings => setEditProfilePrivate(!!settings.isPrivate))
      .catch(err => console.error(err));
  };

  const closeEditProfile = () => {
    if (editProfileImagePreview && !editProfileImagePreview.startsWith('http')) {
      URL.revokeObjectURL(editProfileImagePreview);
    }
    setEditProfileOpen(false);
    setEditProfileImage(null);
    setEditProfileImagePreview(null);
  };

  const handleEditProfileImageChange = e => {
    const file = e.target.files[0];
    if (!file) return;
    setEditProfileImage(file);
    setEditProfileImagePreview(URL.createObjectURL(file));
  };

  const submitEditProfile = async () => {
    if (!editProfileName.trim() || !currentUser) return;
    setEditProfileLoading(true);
    try {
      let photoURL = editProfileImagePreview;
      if (editProfileImage) {
        const uploaded = await window.Backend.uploadToR2(editProfileImage);
        photoURL = uploaded.url;
      }
      await window.Backend.updateUserProfile({ displayName: editProfileName.trim(), photoURL: photoURL || null });
      await window.Backend.setUserPrivate(currentUser.uid, editProfilePrivate);
      // updateProfile mutates the same auth.currentUser object in place, so
      // React won't see a changed reference — force a re-render with a copy.
      setCurrentUser(u => u ? { ...u } : u);
      closeEditProfile();
      showToast('Profile updated.');
    } catch (err) {
      console.error(err);
      showToast('Could not update profile — try again.');
    } finally {
      setEditProfileLoading(false);
    }
  };

  /* upload */
  const openUpload = () => {
    requireAuth(async user => {
      setUploadOpen(true); setUploadStep(1); setDraft(freshDraft());
      try {
        setUploadCountToday(await window.Backend.getUploadCountToday(user.uid));
      } catch (err) {
        console.error(err);
      }
    });
  };
  const closeUpload = () => {
    if (draft.previewUrl) URL.revokeObjectURL(draft.previewUrl);
    setUploadOpen(false); setUploadStep(1); setDraft(freshDraft());
  };

  const handleFileChange = e => {
    const file = e.target.files[0];
    if (!file) return;
    const fileType = file.type.startsWith('video/') ? 'video' : 'image';
    const previewUrl = URL.createObjectURL(file);
    setDraft(d => ({ ...d, file, fileSelected: true, fileType, previewUrl }));
  };

  const submitUpload = async () => {
    setSubmitting(true);
    try {
      let celebId = draft.celebrityId;
      let celebName;
      if (celebId === '__new__') {
        const newName = draft.newCelebName || draft.celebrityQuery || 'New celebrity';
        celebId = await window.Backend.createCelebrity({ name: newName, bio: '' });
        celebName = newName;
      } else {
        celebName = (getCeleb(celebId) || {}).name;
      }
      const tags = draft.tagsInput.split(',').map(t => t.trim()).filter(Boolean);
      const category = draft.category || 'Candid';
      await window.Backend.uploadMedia({
        file: draft.file,
        celebrityId: celebId,
        category,
        caption: draft.caption || '',
        tags: tags.length ? tags : [celebName ? celebName.toLowerCase() : 'new'],
        uploaderUid: currentUser.uid,
        uploaderHandle: currentUser.displayName || (currentUser.email || '').split('@')[0] || 'user',
      });
      setUploadCountToday(n => n + 1);
      closeUpload();
      showToast(`Uploaded to ${celebName || 'new page'} · ${category}`);
    } catch (err) {
      console.error(err);
      showToast(`Upload failed: ${err.message}`);
    } finally {
      setSubmitting(false);
    }
  };

  /* add celebrity by name with Wikipedia preview */
  const openAddCeleb = () => {
    setAddCelebOpen(true);
    setAddCelebQuery('');
    setAddCelebPreview(null);
  };
  const closeAddCeleb = () => {
    if (addCelebImagePreview) URL.revokeObjectURL(addCelebImagePreview);
    setAddCelebOpen(false);
    setAddCelebQuery('');
    setAddCelebPreview(null);
    setAddCelebImage(null);
    setAddCelebImagePreview(null);
    setAddCelebImageUrl('');
  };

  const handleAddCelebImageChange = e => {
    const file = e.target.files[0];
    if (!file) return;
    const previewUrl = URL.createObjectURL(file);
    setAddCelebImage(file);
    setAddCelebImagePreview(previewUrl);
    setAddCelebImageUrl('');
  };

  const handleAddCelebImageUrl = (url) => {
    setAddCelebImageUrl(url);
    if (url.trim()) {
      setAddCelebImage(null);
      setAddCelebImagePreview(url);
    } else {
      setAddCelebImagePreview(null);
    }
  };

  const openEditCeleb = (celebId) => {
    const celeb = getCeleb(celebId);
    if (!celeb) return;
    setEditCelebId(celebId);
    setEditCelebName(celeb.name);
    setEditCelebBio(celeb.bio || '');
    const currentThumbnail = celeb.thumbnail || wikiCache[celeb.name]?.thumbnail || '';
    setEditCelebImageUrl('');
    setEditCelebImage(null);
    setEditCelebImagePreview(currentThumbnail);
    setEditCelebOpen(true);
  };

  const closeEditCeleb = () => {
    if (editCelebImagePreview && !editCelebImagePreview.startsWith('http')) {
      URL.revokeObjectURL(editCelebImagePreview);
    }
    setEditCelebOpen(false);
    setEditCelebId(null);
    setEditCelebName('');
    setEditCelebBio('');
    setEditCelebImage(null);
    setEditCelebImageUrl('');
    setEditCelebImagePreview(null);
  };

  const handleEditCelebImageChange = e => {
    const file = e.target.files[0];
    if (!file) return;
    const previewUrl = URL.createObjectURL(file);
    setEditCelebImage(file);
    setEditCelebImagePreview(previewUrl);
    setEditCelebImageUrl('');
  };

  const handleEditCelebImageUrl = (url) => {
    setEditCelebImageUrl(url);
    if (url.trim()) {
      setEditCelebImage(null);
      setEditCelebImagePreview(url);
    } else {
      setEditCelebImagePreview(null);
    }
  };

  const submitEditCeleb = async () => {
    if (!editCelebName.trim() || !editCelebId) return;
    setEditCelebLoading(true);
    try {
      let thumbnailUrl = editCelebImagePreview;

      // Upload file if selected
      if (editCelebImage) {
        try {
          const uploaded = await window.Backend.uploadToR2(editCelebImage);
          thumbnailUrl = uploaded.url;
        } catch (err) {
          console.error('Image upload failed:', err);
        }
      }

      await window.Backend.updateCelebrity(editCelebId, {
        name: editCelebName,
        bio: editCelebBio,
        thumbnail: thumbnailUrl || null
      });

      setWikiCache(prev => ({
        ...prev,
        [editCelebName]: {
          title: editCelebName,
          description: editCelebBio,
          thumbnail: thumbnailUrl
        }
      }));
      closeEditCeleb();
      showToast(`Updated ${editCelebName}!`);
    } catch (err) {
      console.error(err);
      showToast(`Error updating celebrity: ${err.message}`);
    } finally {
      setEditCelebLoading(false);
    }
  };

  const handleSearchCeleb = async (name) => {
    if (!name.trim()) {
      setAddCelebPreview(null);
      return;
    }
    setAddCelebLoading(true);
    try {
      const preview = await window.Backend.fetchWikiSummary(name);
      setAddCelebPreview(preview);
    } catch (err) {
      setAddCelebPreview({ name, error: 'Could not find Wikipedia entry. You can still create a page manually.' });
    } finally {
      setAddCelebLoading(false);
    }
  };

  const submitAddCeleb = async () => {
    const name = addCelebQuery.trim();
    if (!name) return;
    setAddCelebLoading(true);
    try {
      let celebThumbnail = null;
      let celebBio = '';
      const hasCustomImage = addCelebImage || addCelebImageUrl.trim();

      // Only use wiki info if no custom image provided
      if (!hasCustomImage) {
        celebThumbnail = addCelebPreview?.thumbnail || null;
        celebBio = addCelebPreview?.description || '';
      }

      // Upload custom image if provided
      if (addCelebImage) {
        try {
          const uploaded = await window.Backend.uploadToR2(addCelebImage);
          celebThumbnail = uploaded.url;
        } catch (err) {
          console.error('Image upload failed:', err);
        }
      } else if (addCelebImageUrl.trim()) {
        celebThumbnail = addCelebImageUrl.trim();
      }

      const celebId = await window.Backend.createCelebrity({
        name,
        bio: celebBio,
        thumbnail: celebThumbnail || null
      });

      setWikiCache(prev => ({
        ...prev,
        [name]: {
          title: name,
          description: celebBio,
          thumbnail: celebThumbnail
        }
      }));
      closeAddCeleb();
      showToast(`Added ${name} to r34vault!`);
      openProfile(celebId);
    } catch (err) {
      console.error(err);
      showToast(`Error creating celebrity: ${err.message}`);
    } finally {
      setAddCelebLoading(false);
    }
  };

  const MAX_UPLOADS_PER_DAY = window.Backend?.MAX_UPLOADS_PER_DAY || 5;
  const dailyLimitReached = uploadCountToday >= MAX_UPLOADS_PER_DAY;

  /* ── computed ── */
  const q = searchQuery.trim().toLowerCase();

  // rule34-style search: space-separated tags, every token must match a tag
  // (exact or prefix) on the item — category counts as a tag too.
  const allTagCounts = tagCounts(media);
  const popularTags = allTagCounts.slice(0, 24);
  const searchTokens = parseTagQuery(searchQuery);

  const homeMedia = (() => {
    let ms = media;
    if (searchTokens.length) {
      ms = ms.filter(m => {
        const tags = itemTags(m);
        return searchTokens.every(tok => tags.some(t => t === tok || t.startsWith(tok)));
      });
    }
    return ms.map(m => ({ ...m, celebName: (getCeleb(m.celebrityId) || {}).name || '' }));
  })();

  // Autocomplete: suggest full tags completing whatever's currently being typed.
  const searchPartial = (searchQuery && !searchQuery.endsWith(' ')) ? (searchTokens[searchTokens.length - 1] || '') : '';
  const tagSuggestions = (searchFocused && searchPartial)
    ? allTagCounts.filter(([t]) => t !== searchPartial && t.startsWith(searchPartial)).slice(0, 8)
    : [];

  const matchingCelebs = q ? searchCelebrities(searchQuery, celebs) : [];
  const selectedCeleb = selectedId ? getCeleb(selectedId) : null;
  const profileMedia = selectedCeleb ? media.filter(m => m.celebrityId === selectedCeleb.id) : [];
  const profileCats = [...new Set(profileMedia.map(m => m.category))];
  const profileFiltered = profileTab === 'All' ? profileMedia : profileMedia.filter(m => m.category === profileTab);
  const mItem = mediaId ? media.find(m => m.id === mediaId) : null;
  const mCeleb = mItem ? getCeleb(mItem.celebrityId) : null;
  const mMore = mItem ? media.filter(m => m.celebrityId === mItem.celebrityId && m.id !== mItem.id).slice(0, 8) : [];

  const myMedia = currentUser
    ? media.filter(m => m.uploaderUid === currentUser.uid).map(m => ({ ...m, celebName: (getCeleb(m.celebrityId) || {}).name || '' }))
    : [];
  const likedMedia = currentUser
    ? media.filter(m => m.likedBy.includes(currentUser.uid)).map(m => ({ ...m, celebName: (getCeleb(m.celebrityId) || {}).name || '' }))
    : [];
  const activeFolder = (libraryFolder !== 'all' && libraryFolder !== 'liked') ? folders.find(f => f.id === libraryFolder) : null;
  const folderMedia = activeFolder
    ? media.filter(m => (activeFolder.mediaIds || []).includes(m.id)).map(m => ({ ...m, celebName: (getCeleb(m.celebrityId) || {}).name || '' }))
    : [];
  // Reset to 'all' if the selected folder got deleted out from under us.
  const libraryBase = libraryFolder === 'all' ? myMedia : libraryFolder === 'liked' ? likedMedia : (activeFolder ? folderMedia : myMedia);
  const myCats = [...new Set(libraryBase.map(m => m.category))];
  const myFiltered = myTab === 'All' ? libraryBase : libraryBase.filter(m => m.category === myTab);
  const isSaved = id => folders.some(f => (f.mediaIds || []).includes(id));

  const viewedFolderMedia = viewedFolder
    ? media.filter(m => (viewedFolder.mediaIds || []).includes(m.id)).map(m => ({ ...m, celebName: (getCeleb(m.celebrityId) || {}).name || '' }))
    : [];

  const exploreCats = [...new Set(publicFolders.map(f => f.category).filter(Boolean))];
  const exploreFiltered = exploreCat === 'All' ? publicFolders : publicFolders.filter(f => f.category === exploreCat);

  /* upload draft helpers */
  const chosenCeleb = draft.celebrityId && draft.celebrityId !== '__new__' ? getCeleb(draft.celebrityId) : null;
  const isNewCeleb = draft.celebrityId === '__new__';
  const suggestions = (draft.celebrityQuery && !draft.celebrityId)
    ? searchCelebrities(draft.celebrityQuery, celebs)
    : [];
  const step2Valid = !!(chosenCeleb || isNewCeleb) && !!draft.category;
  const stepValid = (uploadStep === 1 && draft.fileSelected) || (uploadStep === 2 && step2Valid) || uploadStep >= 3;
  const reviewCelebName = isNewCeleb ? (draft.newCelebName || draft.celebrityQuery || 'New celebrity') : (chosenCeleb ? chosenCeleb.name : '—');
  const chosenLabel = isNewCeleb ? `New page: "${draft.newCelebName || draft.celebrityQuery}"` : (chosenCeleb ? chosenCeleb.name : '');

  /* ── render ── */
  return (
    <div style={{ minHeight: '100vh', background: theme.bg, fontFamily: "'Work Sans',system-ui,sans-serif", color: theme.text, ...themeVars(theme) }}>

      {/* ── Navbar: frosted glass bar floating over the color blobs ── */}
      <div style={{ position: 'sticky', top: 0, zIndex: 40, display: 'flex', alignItems: 'center', gap: '12px', padding: '14px 16px', background: theme.navBg, backdropFilter: 'blur(18px) saturate(160%)', WebkitBackdropFilter: 'blur(18px) saturate(160%)', borderBottom: `1px solid ${theme.border}`, boxShadow: `inset 0 -1px 0 ${theme.glassHighlight}`, flexWrap: 'wrap' }}>
        <div onClick={goHome} className="navbar-title" style={{ fontFamily: "'Bebas Neue',system-ui", fontSize: '30px', letterSpacing: '0.5px', cursor: 'pointer', userSelect: 'none', color: theme.accent }}>R34VAULT</div>
        <div className="navbar-search" style={{ flex: 1, minWidth: '200px', maxWidth: '460px', position: 'relative' }}>
          <input ref={searchInputRef} type="text" placeholder="Search tags, e.g. red_carpet candid…" value={searchQuery}
            onChange={e => setSearchQuery(e.target.value)}
            onFocus={() => setSearchFocused(true)}
            onBlur={() => setTimeout(() => setSearchFocused(false), 120)}
            style={{ width: '100%', padding: '10px 16px', borderRadius: '999px', ...glassStyle(theme), color: theme.text }} />
          {tagSuggestions.length > 0 && (
            <div style={{ position: 'absolute', top: 'calc(100% + 6px)', left: 0, right: 0, borderRadius: '14px', overflow: 'hidden', zIndex: 50, ...glassStyle(theme, { strong: true }) }}>
              {tagSuggestions.map(([tag, count]) => (
                <div key={tag} className="suggest-row" onMouseDown={() => applyTagSuggestion(tag)}
                  style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', padding: '9px 14px', cursor: 'pointer', fontSize: '13px' }}>
                  <span>#{tag}</span>
                  <span style={{ color: theme.muted, fontSize: '11px' }}>{fmt(count)}</span>
                </div>
              ))}
            </div>
          )}
        </div>
        <div className="navbar-flex-grow" style={{ flex: 1 }} />
        <button className="navbar-hidden-sm glass-btn" onClick={openAddCeleb}
          style={{ color: theme.text, padding: '10px 20px', borderRadius: '999px', fontWeight: '600', fontSize: '14px', cursor: 'pointer', ...glassStyle(theme) }}>
          + Add
        </button>
        <button onClick={openUpload} className="glow-btn"
          style={{ background: `linear-gradient(135deg, ${theme.accent}, ${theme.accent2})`, color: 'white', border: 'none', padding: '10px 22px', borderRadius: '999px', fontWeight: '700', fontSize: '14px', cursor: 'pointer', boxShadow: `0 4px 18px ${theme.glow}`, '--glow': theme.glow }}>
          Upload
        </button>
        <button onClick={() => { window.location.hash = '#/folders'; setView('explore'); setExploreCat('All'); }} title="Explore public folders" className="glass-btn"
          style={{ color: theme.text, width: '38px', height: '38px', flex: 'none', borderRadius: '50%', fontSize: '15px', cursor: 'pointer', display: 'flex', alignItems: 'center', justifyContent: 'center', ...glassStyle(theme) }}>
          <Icon name="folder_open" size={18} />
        </button>
        <button onClick={() => setDarkMode(d => !d)} title={darkMode ? 'Switch to light mode' : 'Switch to dark mode'} className="glass-btn"
          style={{ color: theme.text, width: '38px', height: '38px', flex: 'none', borderRadius: '50%', fontSize: '15px', cursor: 'pointer', display: 'flex', alignItems: 'center', justifyContent: 'center', ...glassStyle(theme) }}>
          <Icon name={darkMode ? 'light_mode' : 'dark_mode'} size={19} />
        </button>
        {currentUser ? (
          <div onClick={goToMyLibrary} title="My library" style={{ cursor: 'pointer' }}>
            <Avatar name={currentUser.displayName || currentUser.email || 'U'} thumbnail={currentUser.photoURL} size={36} />
          </div>
        ) : (
          <button onClick={() => setSignInOpen(true)} className="glass-btn"
            style={{ color: theme.text, padding: '10px 20px', borderRadius: '999px', fontWeight: '600', fontSize: '14px', cursor: 'pointer', ...glassStyle(theme) }}>
            Sign in
          </button>
        )}
      </div>

      {/* ── Home ── */}
      {view === 'home' && (
        <div style={{ maxWidth: '1400px', margin: '0 auto', padding: '24px 16px 100px' }} className="content-padding">

          {/* Celebrity strip */}
          {celebs.length === 0 ? (
            <div style={{ padding: '14px 4px 22px', fontSize: '13px', color: theme.muted }}>
              No celebrity pages yet — upload a photo to create the first one.
            </div>
          ) : (
            <div style={{ display: 'flex', gap: '22px', overflowX: 'auto', padding: '4px 4px 22px' }}>
              {celebs.map(c => (
                <div key={c.id} onClick={() => openProfile(c.id)} style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: '6px', cursor: 'pointer', flex: 'none', width: '76px' }}>
                  <Avatar name={c.name} thumbnail={c.thumbnail || wikiCache[c.name]?.thumbnail} size={64} />
                  <div style={{ fontSize: '12px', textAlign: 'center', lineHeight: 1.25 }}>{c.name}</div>
                </div>
              ))}
            </div>
          )}

          {/* Popular tags — rule34-style tag cloud, click to add/remove from the search */}
          {popularTags.length > 0 && (
            <div style={{ marginBottom: '24px' }}>
              <div style={{ fontSize: '11px', textTransform: 'uppercase', letterSpacing: '0.08em', color: theme.muted, marginBottom: '10px', fontWeight: '700' }}>Popular tags</div>
              <div style={{ display: 'flex', gap: '10px', flexWrap: 'wrap' }}>
                {popularTags.map(([tag, count]) => (
                  <div key={tag} className="glass-chip" onClick={() => toggleSearchTag(tag)}
                    style={searchTokens.includes(tag) ? chipOn(theme) : chipOff(theme)}>
                    #{tag} <span style={{ opacity: 0.7 }}>{fmt(count)}</span>
                  </div>
                ))}
              </div>
            </div>
          )}

          {/* People results */}
          {q && matchingCelebs.length > 0 && (
            <div style={{ marginBottom: '28px' }}>
              <div style={{ fontSize: '11px', textTransform: 'uppercase', letterSpacing: '0.08em', color: theme.muted, marginBottom: '10px', fontWeight: '700' }}>People</div>
              <div style={{ display: 'flex', gap: '12px', flexWrap: 'wrap' }}>
                {matchingCelebs.map(mc => (
                  <div key={mc.id} className="glass-chip" onClick={() => openProfile(mc.id)}
                    style={{ display: 'flex', alignItems: 'center', gap: '10px', padding: '8px 16px 8px 8px', borderRadius: '999px', cursor: 'pointer', ...glassStyle(theme) }}>
                    <Avatar name={mc.name} thumbnail={mc.thumbnail || wikiCache[mc.name]?.thumbnail} size={32} />
                    <div style={{ fontSize: '13px', fontWeight: '600' }}>{mc.name}</div>
                  </div>
                ))}
              </div>
            </div>
          )}

          {/* Masonry grid */}
          {homeMedia.length === 0 ? (
            <div style={{ textAlign: 'center', color: theme.muted, padding: '60px 0', fontSize: '14px' }}>
              No photos yet — be the first to upload one!
            </div>
          ) : (
            <div className="masonry-grid" style={{ columnCount: 4, columnGap: '16px' }}>
              {homeMedia.map(item => (
                <MediaCard key={item.id} item={item} showCelebName={true} theme={theme}
                  liked={!!(currentUser && item.likedBy.includes(currentUser.uid))}
                  canDelete={!!(currentUser && item.uploaderUid === currentUser.uid)}
                  saved={isSaved(item.id)}
                  onOpen={() => openMedia(item.id)}
                  onLike={e => toggleLike(item.id, e)}
                  onDelete={e => deleteMedia(item.id, e)}
                  onSave={e => openSaveModal(item, e)} />
              ))}
            </div>
          )}
        </div>
      )}

      {/* ── Profile ── */}
      {view === 'profile' && selectedCeleb && (
        <div style={{ maxWidth: '1400px', margin: '0 auto', paddingBottom: '100px' }}>
          <div onClick={goHome} style={{ padding: '16px 16px 0', fontSize: '13px', fontWeight: '600', color: theme.muted, cursor: 'pointer', width: 'fit-content' }}>← Back to r34vault</div>

          {/* Banner — a saturated color wash the glass panel below will overlap */}
          <div className="profile-banner" style={{ height: '200px', margin: '16px 16px 0', borderRadius: '24px', background: bannerGradient(selectedCeleb.name) }} />

          {/* Frosted glass info panel — its own negative margin overlaps the
              banner, but the panel's height is independent (grows downward
              with content), so long names/bios never get clipped. */}
          <div className="profile-padding" style={{ padding: '0 16px', marginTop: '-56px', position: 'relative' }}>
            <div style={{ ...glassStyle(theme, { strong: true }), borderRadius: '24px', padding: '22px', display: 'flex', alignItems: 'center', gap: '20px', flexWrap: 'wrap' }}>
              <Avatar name={selectedCeleb.name} thumbnail={wikiCache[selectedCeleb.name]?.thumbnail || selectedCeleb.thumbnail} size={96} border={`3px solid ${theme.glassHighlight}`} />
              <div style={{ flex: 1, minWidth: '200px' }}>
                <div className="profile-title" style={{ fontFamily: "'Bebas Neue'", fontSize: '38px', letterSpacing: '0.5px', lineHeight: 1 }}>{selectedCeleb.name}</div>
                <div style={{ fontSize: '13px', color: theme.muted, marginTop: '6px' }}>
                  {wikiCache[selectedCeleb.name]?.description || selectedCeleb.bio || 'No bio yet'} · {fmt(selectedCeleb.followedBy.length)} followers
                </div>
              </div>
              <div style={{ display: 'flex', gap: '10px', flexWrap: 'wrap' }}>
                <button onClick={() => openEditCeleb(selectedCeleb.id)} className="glass-btn"
                  style={{ color: theme.text, padding: '11px 22px', borderRadius: '999px', fontWeight: '700', fontSize: '14px', cursor: 'pointer', ...glassStyle(theme) }}>
                  Edit
                </button>
                <button onClick={e => toggleFollow(selectedCeleb.id, e)}
                  className={currentUser && selectedCeleb.followedBy.includes(currentUser.uid) ? 'glass-btn' : 'glow-btn'}
                  style={currentUser && selectedCeleb.followedBy.includes(currentUser.uid)
                    ? { color: theme.text, padding: '11px 22px', borderRadius: '999px', fontWeight: '700', fontSize: '14px', cursor: 'pointer', ...glassStyle(theme) }
                    : { background: `linear-gradient(135deg, ${theme.accent}, ${theme.accent2})`, color: 'white', border: 'none', padding: '11px 22px', borderRadius: '999px', fontWeight: '700', fontSize: '14px', cursor: 'pointer', boxShadow: `0 4px 18px ${theme.glow}` }}>
                  {currentUser && selectedCeleb.followedBy.includes(currentUser.uid) ? 'Following' : 'Follow'}
                </button>
              </div>
            </div>
          </div>

          {/* Profile tabs */}
          <div style={{ display: 'flex', gap: '10px', flexWrap: 'wrap', padding: '26px 16px 20px' }}>
            {['All', ...profileCats].map(cat => (
              <div key={cat} className="glass-chip" onClick={() => setProfileTab(cat)} style={cat === profileTab ? chipOn(theme) : chipOff(theme)}>{cat}</div>
            ))}
          </div>

          {/* Profile masonry */}
          {profileFiltered.length === 0 ? (
            <div style={{ textAlign: 'center', color: theme.muted, padding: '60px 0', fontSize: '14px' }}>
              No photos for {selectedCeleb.name} yet.
            </div>
          ) : (
            <div style={{ padding: '0 16px', columnCount: 4, columnGap: '16px' }} className="masonry-grid">
              {profileFiltered.map(item => (
                <MediaCard key={item.id} item={item} showCelebName={false} theme={theme}
                  liked={!!(currentUser && item.likedBy.includes(currentUser.uid))}
                  canDelete={!!(currentUser && item.uploaderUid === currentUser.uid)}
                  saved={isSaved(item.id)}
                  onOpen={() => openMedia(item.id)}
                  onLike={e => toggleLike(item.id, e)}
                  onDelete={e => deleteMedia(item.id, e)}
                  onSave={e => openSaveModal(item, e)} />
              ))}
            </div>
          )}
        </div>
      )}

      {/* ── My Library ── */}
      {view === 'me' && currentUser && (
        <div style={{ maxWidth: '1400px', margin: '0 auto', paddingBottom: '100px' }}>
          <div onClick={goHome} style={{ padding: '16px 16px 0', fontSize: '13px', fontWeight: '600', color: theme.muted, cursor: 'pointer', width: 'fit-content' }}>← Back to r34vault</div>

          {/* Banner */}
          <div className="profile-banner" style={{ height: '200px', margin: '16px 16px 0', borderRadius: '24px', background: bannerGradient(currentUser.displayName || currentUser.email || 'U') }} />

          {/* Frosted glass info panel */}
          <div className="profile-padding" style={{ padding: '0 16px', marginTop: '-56px', position: 'relative' }}>
            <div style={{ ...glassStyle(theme, { strong: true }), borderRadius: '24px', padding: '22px', display: 'flex', alignItems: 'center', gap: '20px', flexWrap: 'wrap' }}>
              <Avatar name={currentUser.displayName || currentUser.email || 'U'} thumbnail={currentUser.photoURL} size={96} border={`3px solid ${theme.glassHighlight}`} />
              <div style={{ flex: 1, minWidth: '200px' }}>
                <div className="profile-title" style={{ fontFamily: "'Bebas Neue'", fontSize: '38px', letterSpacing: '0.5px', lineHeight: 1 }}>{currentUser.displayName || currentUser.email}</div>
                <div style={{ fontSize: '13px', color: theme.muted, marginTop: '6px' }}>
                  {myMedia.length} upload{myMedia.length === 1 ? '' : 's'} · {uploadCountToday}/{MAX_UPLOADS_PER_DAY} used today
                </div>
              </div>
              <div style={{ display: 'flex', gap: '10px' }}>
                <button onClick={openEditProfile} className="glass-btn"
                  style={{ color: theme.text, padding: '11px 22px', borderRadius: '999px', fontWeight: '700', fontSize: '14px', cursor: 'pointer', ...glassStyle(theme) }}>
                  Edit profile
                </button>
                <button onClick={onSignOut} className="glass-btn"
                  style={{ color: theme.text, padding: '11px 22px', borderRadius: '999px', fontWeight: '700', fontSize: '14px', cursor: 'pointer', ...glassStyle(theme) }}>
                  Sign out
                </button>
              </div>
            </div>
          </div>

          {/* Folders: your uploads, a private Liked folder, and any custom
              save/bookmark folders you've created ── */}
          <div style={{ display: 'flex', gap: '10px', flexWrap: 'wrap', padding: '26px 16px 0' }}>
            <div className="glass-chip" onClick={() => selectLibraryFolder('all')} style={libraryFolder === 'all' ? chipOn(theme) : chipOff(theme)}>
              All uploads
            </div>
            <div className="glass-chip" onClick={() => selectLibraryFolder('liked')} style={libraryFolder === 'liked' ? chipOn(theme) : chipOff(theme)}>
              <Icon name="favorite" size={13} style={{ verticalAlign: '-2px', marginRight: '4px' }} /> Liked
            </div>
            {folders.map(f => (
              <div key={f.id} className="glass-chip" onClick={() => selectLibraryFolder(f.id)} style={libraryFolder === f.id ? chipOn(theme) : chipOff(theme)}>
                {f.name}{f.isPublic && <Icon name="public" size={12} style={{ verticalAlign: '-2px', marginLeft: '5px' }} />}
              </div>
            ))}
            <div className="glass-chip" onClick={openNewFolder} style={chipOff(theme)}>
              + New folder
            </div>
          </div>
          {activeFolder && (
            <div style={{ display: 'flex', alignItems: 'center', gap: '16px', padding: '10px 16px 0', fontSize: '12px' }}>
              <div onClick={() => renameActiveFolder(activeFolder)} style={{ cursor: 'pointer', fontWeight: '600', color: theme.muted }}>Rename folder</div>
              <div onClick={() => toggleActiveFolderPublic(activeFolder)} style={{ cursor: 'pointer', fontWeight: '600', color: theme.muted }}>
                {activeFolder.isPublic ? 'Make private' : 'Make public'}
              </div>
              <div onClick={() => deleteActiveFolder(activeFolder)} style={{ cursor: 'pointer', fontWeight: '600', color: theme.danger }}>Delete folder</div>
              {activeFolder.isPublic && (
                <React.Fragment>
                  <div onClick={() => copyFolderLink(activeFolder)} style={{ cursor: 'pointer', fontWeight: '600', color: theme.muted }}>Copy link</div>
                  <select value={activeFolder.category || ''} onChange={e => setActiveFolderCategory(activeFolder, e.target.value)}
                    style={{ padding: '5px 8px', borderRadius: '8px', border: `1px solid ${theme.border}`, background: theme.subtle, color: theme.text, fontSize: '12px' }}>
                    <option value="">Uncategorized</option>
                    {CATEGORIES.map(cat => <option key={cat} value={cat}>{cat}</option>)}
                  </select>
                  <div style={{ color: theme.muted }}>Listed in Explore folders so others can find it.</div>
                </React.Fragment>
              )}
            </div>
          )}
          {libraryFolder === 'liked' && (
            <div style={{ padding: '10px 16px 0', fontSize: '12px', color: theme.muted }}>Only visible to you.</div>
          )}

          {/* Library tabs */}
          <div style={{ display: 'flex', gap: '10px', flexWrap: 'wrap', padding: '20px 16px 20px' }}>
            {['All', ...myCats].map(cat => (
              <div key={cat} className="glass-chip" onClick={() => setMyTab(cat)} style={cat === myTab ? chipOn(theme) : chipOff(theme)}>{cat}</div>
            ))}
          </div>

          {/* Library masonry */}
          {myFiltered.length === 0 ? (
            <div style={{ textAlign: 'center', color: theme.muted, padding: '60px 0', fontSize: '14px' }}>
              {libraryFolder === 'all' && "You haven't uploaded anything yet."}
              {libraryFolder === 'liked' && "You haven't liked anything yet."}
              {activeFolder && 'This folder is empty — save posts here from anywhere on the site.'}
            </div>
          ) : (
            <div style={{ padding: '0 16px', columnCount: 4, columnGap: '16px' }} className="masonry-grid">
              {myFiltered.map(item => (
                <MediaCard key={item.id} item={item} showCelebName={true} theme={theme}
                  liked={!!(currentUser && item.likedBy.includes(currentUser.uid))}
                  canDelete={!!(currentUser && item.uploaderUid === currentUser.uid)}
                  saved={isSaved(item.id)}
                  onOpen={() => openMedia(item.id)}
                  onLike={e => toggleLike(item.id, e)}
                  onDelete={e => deleteMedia(item.id, e)}
                  onSave={e => openSaveModal(item, e)} />
              ))}
            </div>
          )}
        </div>
      )}

      {/* ── Public folder page (#/f/<id>) — the folder owner's identity is
          never shown here, even though the caller is authorized to view the
          folder: exposing "created by @handle" would undermine a private
          profile's whole point if that same user made one folder public. ── */}
      {view === 'folder' && (
        <div style={{ maxWidth: '1400px', margin: '0 auto', paddingBottom: '100px' }}>
          <div onClick={goHome} style={{ padding: '16px 16px 0', fontSize: '13px', fontWeight: '600', color: theme.muted, cursor: 'pointer', width: 'fit-content' }}>← Back to r34vault</div>

          {viewedFolder === undefined ? (
            <div style={{ textAlign: 'center', color: theme.muted, padding: '80px 0', fontSize: '14px' }}>Loading…</div>
          ) : viewedFolder === null ? (
            <div style={{ textAlign: 'center', color: theme.muted, padding: '80px 0', fontSize: '14px' }}>This folder is private or doesn't exist.</div>
          ) : (
            <React.Fragment>
              <div style={{ padding: '16px 16px 0' }}>
                <div style={{ fontFamily: "'Bebas Neue'", fontSize: '38px', letterSpacing: '0.5px', lineHeight: 1 }}>{viewedFolder.name}</div>
                <div style={{ fontSize: '13px', color: theme.muted, marginTop: '8px' }}>
                  {viewedFolderMedia.length} item{viewedFolderMedia.length === 1 ? '' : 's'}
                </div>
              </div>

              {viewedFolderMedia.length === 0 ? (
                <div style={{ textAlign: 'center', color: theme.muted, padding: '60px 0', fontSize: '14px' }}>This folder is empty.</div>
              ) : (
                <div style={{ padding: '20px 16px 0', columnCount: 4, columnGap: '16px' }} className="masonry-grid">
                  {viewedFolderMedia.map(item => (
                    <MediaCard key={item.id} item={item} showCelebName={true} theme={theme}
                      liked={!!(currentUser && item.likedBy.includes(currentUser.uid))}
                      canDelete={!!(currentUser && item.uploaderUid === currentUser.uid)}
                      saved={isSaved(item.id)}
                      onOpen={() => openMedia(item.id)}
                      onLike={e => toggleLike(item.id, e)}
                      onDelete={e => deleteMedia(item.id, e)}
                      onSave={e => openSaveModal(item, e)} />
                  ))}
                </div>
              )}
            </React.Fragment>
          )}
        </div>
      )}

      {/* ── Explore public folders (#/folders) — the only way to find a
          public folder without already having its link. Folder ownership
          is never shown here either, same reasoning as the folder page
          itself. ── */}
      {view === 'explore' && (
        <div style={{ maxWidth: '1400px', margin: '0 auto', paddingBottom: '100px' }}>
          <div style={{ padding: '16px 16px 0' }}>
            <div style={{ fontFamily: "'Bebas Neue'", fontSize: '38px', letterSpacing: '0.5px', lineHeight: 1 }}>Explore folders</div>
            <div style={{ fontSize: '13px', color: theme.muted, marginTop: '8px' }}>Public folders other people have shared.</div>
          </div>

          <div style={{ display: 'flex', gap: '10px', flexWrap: 'wrap', padding: '26px 16px 20px' }}>
            {['All', ...exploreCats].map(cat => (
              <div key={cat} className="glass-chip" onClick={() => setExploreCat(cat)} style={cat === exploreCat ? chipOn(theme) : chipOff(theme)}>{cat}</div>
            ))}
          </div>

          {exploreFiltered.length === 0 ? (
            <div style={{ textAlign: 'center', color: theme.muted, padding: '60px 0', fontSize: '14px' }}>
              No public folders yet — make one of yours public from My Library.
            </div>
          ) : (
            <div style={{ padding: '0 16px', display: 'flex', flexWrap: 'wrap', gap: '14px' }}>
              {exploreFiltered.map(f => (
                <div key={f.id} onClick={() => { window.location.hash = `#/f/${f.id}`; setView('folder'); setViewFolderId(f.id); }}
                  style={{ width: '220px', padding: '18px', borderRadius: '18px', cursor: 'pointer', ...glassStyle(theme) }}>
                  <div style={{ fontSize: '15px', fontWeight: '700', marginBottom: '6px' }}>{f.name}</div>
                  <div style={{ fontSize: '12px', color: theme.muted }}>
                    {(f.mediaIds || []).length} item{(f.mediaIds || []).length === 1 ? '' : 's'}{f.category ? ` · ${f.category}` : ''}
                  </div>
                </div>
              ))}
            </div>
          )}
        </div>
      )}

      {/* ── Media page: every photo/video gets its own linkable, shareable
          page (#/m/<id>) instead of a modal, so it can be opened directly
          or shared. ── */}
      {view === 'media' && (
        <div style={{ maxWidth: '1100px', margin: '0 auto', paddingBottom: '100px' }}>
          {!mItem ? (
            <div style={{ textAlign: 'center', color: theme.muted, padding: '80px 0', fontSize: '14px' }}>
              {media.length === 0 ? 'Loading…' : "This upload doesn't exist or was removed."}
            </div>
          ) : (
            <React.Fragment>
              <div onClick={() => mCeleb ? openProfile(mCeleb.id) : goHome()}
                style={{ padding: '16px 16px 0', fontSize: '13px', fontWeight: '600', color: theme.muted, cursor: 'pointer', width: 'fit-content' }}>
                ← Back to {mCeleb ? mCeleb.name : 'r34vault'}
              </div>

              <div style={{ display: 'flex', flexWrap: 'wrap', gap: '20px', padding: '16px', alignItems: 'flex-start' }}>
                {/* Media */}
                <div style={{ flex: '3 1 480px', minWidth: '280px', minHeight: '300px', background: 'black', borderRadius: '24px', overflow: 'hidden', display: 'flex', alignItems: 'center', justifyContent: 'center', border: `1px solid ${theme.border}` }}>
                  {mItem.type === 'video' ? (
                    <video src={mItem.url} controls autoPlay style={{ maxWidth: '100%', maxHeight: '80vh' }} />
                  ) : (
                    <img src={mItem.url} alt={mItem.caption || ''} style={{ maxWidth: '100%', maxHeight: '80vh', objectFit: 'contain' }} />
                  )}
                </div>

                {/* Details */}
                <div style={{ flex: '1 1 280px', minWidth: '260px', display: 'flex', flexDirection: 'column', gap: '16px', borderRadius: '24px', padding: '22px', ...glassStyle(theme, { strong: true }) }}>
                  {mCeleb && (
                    <div onClick={() => openProfile(mCeleb.id)} style={{ display: 'flex', alignItems: 'center', gap: '10px', cursor: 'pointer' }}>
                      <Avatar name={mCeleb.name} thumbnail={mCeleb.thumbnail || wikiCache[mCeleb.name]?.thumbnail} size={38} />
                      <div style={{ fontWeight: '700', fontSize: '15px' }}>{mCeleb.name}</div>
                    </div>
                  )}

                  <div style={{ fontSize: '14px', lineHeight: 1.5 }}>{mItem.caption}</div>

                  <div style={{ display: 'inline-flex', width: 'fit-content', padding: '5px 12px', borderRadius: '999px', fontSize: '12px', fontWeight: '600', ...glassStyle(theme) }}>
                    {mItem.category}
                  </div>

                  <div style={{ display: 'flex', gap: '8px', flexWrap: 'wrap' }}>
                    {mItem.tags.map(tag => (
                      <div key={tag} style={{ fontSize: '12px', padding: '4px 10px', borderRadius: '999px', border: `1px solid ${theme.border}`, color: theme.muted }}>#{tag}</div>
                    ))}
                  </div>

                  <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
                    <div style={{ display: 'flex', gap: '8px' }}>
                      <div onClick={e => toggleLike(mItem.id, e)} className="glass-chip"
                        style={(currentUser && mItem.likedBy.includes(currentUser.uid))
                          ? { display: 'inline-flex', alignItems: 'center', gap: '5px', fontSize: '13px', fontWeight: '700', padding: '7px 14px', borderRadius: '999px', cursor: 'pointer', background: `linear-gradient(135deg, ${theme.accent}, ${theme.accent2})`, color: 'white', boxShadow: `0 4px 16px ${theme.glow}` }
                          : { display: 'inline-flex', alignItems: 'center', gap: '5px', fontSize: '13px', fontWeight: '700', padding: '7px 14px', borderRadius: '999px', cursor: 'pointer', color: theme.text, ...glassStyle(theme) }}>
                        <Icon name="favorite" filled={!!(currentUser && mItem.likedBy.includes(currentUser.uid))} size={15} /> {fmt(mItem.likedBy.length)}
                      </div>
                      <div onClick={e => openSaveModal(mItem, e)} className="glass-chip"
                        style={isSaved(mItem.id)
                          ? { display: 'inline-flex', alignItems: 'center', gap: '5px', fontSize: '13px', fontWeight: '700', padding: '7px 14px', borderRadius: '999px', cursor: 'pointer', background: `linear-gradient(135deg, ${theme.accent}, ${theme.accent2})`, color: 'white', boxShadow: `0 4px 16px ${theme.glow}` }
                          : { display: 'inline-flex', alignItems: 'center', gap: '5px', fontSize: '13px', fontWeight: '700', padding: '7px 14px', borderRadius: '999px', cursor: 'pointer', color: theme.text, ...glassStyle(theme) }}>
                        <Icon name="bookmark" filled={isSaved(mItem.id)} size={15} /> Save
                      </div>
                    </div>
                    <div style={{ fontSize: '12px', color: theme.muted }}>
                      {(privacyCache[mItem.uploaderUid] ?? mItem.posterIsPrivate) ? 'Posted by r34vault' : `Uploaded by @${mItem.uploaderHandle}`}
                    </div>
                  </div>

                  <div style={{ display: 'flex', gap: '16px' }}>
                    <div onClick={() => showToast('Reported — thanks for flagging this.')}
                      style={{ fontSize: '12px', color: theme.danger, cursor: 'pointer', width: 'fit-content' }}>
                      Report content
                    </div>
                    {currentUser && mItem.uploaderUid === currentUser.uid && (
                      <div onClick={e => deleteMedia(mItem.id, e)}
                        style={{ fontSize: '12px', color: theme.danger, cursor: 'pointer', width: 'fit-content', fontWeight: '600' }}>
                        Delete this upload
                      </div>
                    )}
                  </div>
                </div>
              </div>

              {/* More from this celeb */}
              {mCeleb && mMore.length > 0 && (
                <div style={{ padding: '8px 16px 0' }}>
                  <div style={{ fontSize: '11px', textTransform: 'uppercase', letterSpacing: '0.08em', color: theme.muted, marginBottom: '10px', fontWeight: '700' }}>More from {mCeleb.name}</div>
                  <div className="masonry-grid" style={{ columnCount: 4, columnGap: '16px' }}>
                    {mMore.map(item => (
                      <MediaCard key={item.id} item={item} showCelebName={false} theme={theme}
                        liked={!!(currentUser && item.likedBy.includes(currentUser.uid))}
                        canDelete={!!(currentUser && item.uploaderUid === currentUser.uid)}
                        saved={isSaved(item.id)}
                        onOpen={() => openMedia(item.id)}
                        onLike={e => toggleLike(item.id, e)}
                        onDelete={e => deleteMedia(item.id, e)}
                        onSave={e => openSaveModal(item, e)} />
                    ))}
                  </div>
                </div>
              )}
            </React.Fragment>
          )}
        </div>
      )}

      {/* ── Sign In ── */}
      {signInOpen && (
        <div onClick={() => { setSignInOpen(false); setPendingAction(null); setAuthError(null); }}
          style={{ position: 'fixed', top: 0, right: 0, bottom: 0, left: 0, background: theme.overlay, backdropFilter: 'blur(6px)', WebkitBackdropFilter: 'blur(6px)', zIndex: 110, display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
          <form onClick={e => e.stopPropagation()} onSubmit={onSignInSubmit}
            style={{ padding: '32px', borderRadius: '24px', width: '360px', maxWidth: '90vw', display: 'flex', flexDirection: 'column', gap: '14px', ...glassStyle(theme, { strong: true }) }}>
            <div style={{ fontFamily: "'Bebas Neue'", fontSize: '28px' }}>Sign in to r34vault</div>
            <div style={{ fontSize: '13px', color: theme.muted }}>New here? Just enter an email + password — an account is created automatically.</div>
            <input type="email" required placeholder="you@email.com" value={signInEmail} onChange={e => setSignInEmail(e.target.value)}
              style={{ padding: '11px 14px', borderRadius: '10px', border: `1px solid ${theme.border}`, background: theme.subtle, color: theme.text, fontSize: '14px' }} />
            <input type="password" required minLength={6} placeholder="Password" value={signInPassword} onChange={e => setSignInPassword(e.target.value)}
              style={{ padding: '11px 14px', borderRadius: '10px', border: `1px solid ${theme.border}`, background: theme.subtle, color: theme.text, fontSize: '14px' }} />
            {authError && <div style={{ fontSize: '12px', color: theme.danger }}>{authError}</div>}
            <button type="submit" disabled={authBusy} className="glow-btn"
              style={{ background: `linear-gradient(135deg, ${theme.accent}, ${theme.accent2})`, color: 'white', padding: '12px', borderRadius: '10px', border: 'none', fontWeight: '600', fontSize: '14px', cursor: authBusy ? 'not-allowed' : 'pointer', opacity: authBusy ? 0.7 : 1, boxShadow: `0 4px 18px ${theme.glow}` }}>
              {authBusy ? 'Please wait…' : 'Continue'}
            </button>
            <div style={{ textAlign: 'center', fontSize: '12px', color: theme.muted }}>or</div>
            <button type="button" onClick={onGoogleSignIn} disabled={authBusy} className="glass-btn"
              style={{ color: theme.text, padding: '11px', borderRadius: '10px', fontWeight: '600', fontSize: '14px', cursor: authBusy ? 'not-allowed' : 'pointer', ...glassStyle(theme) }}>
              Continue with Google
            </button>
          </form>
        </div>
      )}

      {/* ── Upload ── */}
      {uploadOpen && (
        <div onClick={closeUpload}
          style={{ position: 'fixed', top: 0, right: 0, bottom: 0, left: 0, background: theme.overlay, backdropFilter: 'blur(6px)', WebkitBackdropFilter: 'blur(6px)', zIndex: 120, display: 'flex', alignItems: 'center', justifyContent: 'center', padding: '20px' }}>
          <div onClick={e => e.stopPropagation()}
            style={{ borderRadius: '24px', width: '480px', maxWidth: '100%', maxHeight: '90vh', overflow: 'auto', padding: '28px', display: 'flex', flexDirection: 'column', gap: '20px', ...glassStyle(theme, { strong: true }) }}>

            {/* Header */}
            <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
              <div style={{ fontFamily: "'Bebas Neue'", fontSize: '26px' }}>Upload to r34vault</div>
              <div onClick={closeUpload} style={{ cursor: 'pointer', color: theme.muted }}><Icon name="close" size={20} /></div>
            </div>

            {/* Step dots */}
            <div style={{ display: 'flex', gap: '8px' }}>
              {[1, 2, 3, 4].map(i => (
                <div key={i} style={{ width: '8px', height: '8px', borderRadius: '50%', background: i <= uploadStep ? theme.accent : theme.border }} />
              ))}
            </div>

            {dailyLimitReached ? (
              <div style={{ fontSize: '13px', color: theme.danger, background: theme.dangerBg, padding: '12px 14px', borderRadius: '8px' }}>
                You've used all {MAX_UPLOADS_PER_DAY} uploads for today — try again tomorrow.
              </div>
            ) : (
              <div style={{ fontSize: '12px', color: theme.muted }}>
                {uploadCountToday} of {MAX_UPLOADS_PER_DAY} uploads used today
              </div>
            )}

            {/* Step 1 — file */}
            {uploadStep === 1 && (
              <div style={{ display: 'flex', flexDirection: 'column', gap: '14px' }}>
                <div style={{ fontSize: '14px', fontWeight: '700' }}>Choose a photo or video</div>
                <input type="file" accept="image/*,video/*" onChange={handleFileChange} />
                {draft.previewUrl && (
                  draft.fileType === 'video' ? (
                    <video src={draft.previewUrl} controls style={{ width: '100%', borderRadius: '12px', maxHeight: '240px' }} />
                  ) : (
                    <img src={draft.previewUrl} alt="" style={{ width: '100%', borderRadius: '12px', maxHeight: '240px', objectFit: 'cover' }} />
                  )
                )}
              </div>
            )}

            {/* Step 2 — celebrity + category */}
            {uploadStep === 2 && (
              <div style={{ display: 'flex', flexDirection: 'column', gap: '12px' }}>
                <div style={{ fontSize: '14px', fontWeight: '700' }}>Tag the celebrity</div>

                {!(chosenCeleb || isNewCeleb) ? (
                  <div>
                    <input type="text" placeholder="Type a name…" value={draft.celebrityQuery}
                      onChange={e => { const v = e.target.value; setDraft(d => ({ ...d, celebrityQuery: v, celebrityId: null, newCelebName: null })); }}
                      style={{ width: '100%', padding: '11px 14px', borderRadius: '10px', border: `1px solid ${theme.border}`, background: theme.subtle, color: theme.text, fontSize: '14px' }} />
                    {suggestions.length > 0 && (
                      <div style={{ border: `1px solid ${theme.border}`, borderRadius: '10px', marginTop: '6px', overflow: 'hidden' }}>
                        {suggestions.map(s => (
                          <div key={s.id} className="suggest-row"
                            onClick={() => setDraft(d => ({ ...d, celebrityId: s.id, celebrityQuery: s.name }))}
                            style={{ padding: '10px 14px', cursor: 'pointer', fontSize: '13px', borderBottom: `1px solid ${theme.border}` }}>
                            {s.name}
                          </div>
                        ))}
                      </div>
                    )}
                    {draft.celebrityQuery && suggestions.length === 0 && (
                      <div onClick={() => setDraft(d => ({ ...d, newCelebName: d.celebrityQuery, celebrityId: '__new__' }))}
                        style={{ marginTop: '6px', padding: '10px 14px', borderRadius: '10px', border: `1px dashed ${theme.border}`, fontSize: '13px', cursor: 'pointer', color: theme.accent, fontWeight: '600' }}>
                        + Create new page for "{draft.celebrityQuery}"
                      </div>
                    )}
                  </div>
                ) : (
                  <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', padding: '11px 14px', borderRadius: '10px', background: theme.subtle }}>
                    <div style={{ fontSize: '13px', fontWeight: '700' }}>{chosenLabel}</div>
                    <div onClick={() => setDraft(d => ({ ...d, celebrityId: null, newCelebName: null, celebrityQuery: '' }))}
                      style={{ cursor: 'pointer', fontSize: '12px', color: theme.muted, fontWeight: '600' }}>Change</div>
                  </div>
                )}

                <div style={{ fontSize: '14px', fontWeight: '700', marginTop: '8px' }}>Category</div>
                <div style={{ display: 'flex', gap: '8px', flexWrap: 'wrap' }}>
                  {CATEGORIES.map(cat => (
                    <div key={cat} className="glass-chip" onClick={() => setDraft(d => ({ ...d, category: cat }))} style={cat === draft.category ? chipOn(theme) : chipOff(theme)}>{cat}</div>
                  ))}
                </div>
              </div>
            )}

            {/* Step 3 — caption + tags */}
            {uploadStep === 3 && (
              <div style={{ display: 'flex', flexDirection: 'column', gap: '12px' }}>
                <div style={{ fontSize: '14px', fontWeight: '700' }}>Add details</div>
                <textarea placeholder="Caption…" value={draft.caption}
                  onChange={e => { const v = e.target.value; setDraft(d => ({ ...d, caption: v })); }}
                  style={{ padding: '11px 14px', borderRadius: '10px', border: `1px solid ${theme.border}`, background: theme.subtle, color: theme.text, minHeight: '76px', fontSize: '14px', resize: 'vertical' }} />
                <input type="text" placeholder="Tags, comma separated" value={draft.tagsInput}
                  onChange={e => { const v = e.target.value; setDraft(d => ({ ...d, tagsInput: v })); }}
                  style={{ padding: '11px 14px', borderRadius: '10px', border: `1px solid ${theme.border}`, background: theme.subtle, color: theme.text, fontSize: '14px' }} />
              </div>
            )}

            {/* Step 4 — review */}
            {uploadStep === 4 && (
              <div style={{ display: 'flex', flexDirection: 'column', gap: '14px' }}>
                <div style={{ fontSize: '14px', fontWeight: '700' }}>Review & submit</div>
                <div style={{ display: 'flex', gap: '14px', alignItems: 'center', padding: '14px', borderRadius: '16px', background: theme.subtle }}>
                  {draft.previewUrl && (
                    draft.fileType === 'video' ? (
                      <video src={draft.previewUrl} style={{ width: '64px', height: '64px', borderRadius: '10px', flex: 'none', objectFit: 'cover' }} />
                    ) : (
                      <img src={draft.previewUrl} alt="" style={{ width: '64px', height: '64px', borderRadius: '10px', flex: 'none', objectFit: 'cover' }} />
                    )
                  )}
                  <div>
                    <div style={{ fontSize: '13px', fontWeight: '700' }}>{reviewCelebName}</div>
                    <div style={{ fontSize: '12px', color: theme.muted, marginTop: '2px' }}>{draft.category || 'Candid'} · {draft.fileType === 'video' ? 'Video' : 'Photo'}</div>
                    <div style={{ fontSize: '12px', color: theme.muted, marginTop: '2px' }}>{draft.caption || 'No caption'}</div>
                  </div>
                </div>
              </div>
            )}

            {/* Nav buttons */}
            <div style={{ display: 'flex', justifyContent: 'space-between', marginTop: '4px' }}>
              <button onClick={() => setUploadStep(s => Math.max(1, s - 1))} className={uploadStep === 1 ? '' : 'glass-btn'}
                style={uploadStep === 1
                  ? { visibility: 'hidden', background: 'transparent', border: 'none', padding: '12px 20px' }
                  : { color: theme.text, padding: '11px 20px', borderRadius: '999px', fontWeight: '600', fontSize: '14px', cursor: 'pointer', ...glassStyle(theme) }}>
                Back
              </button>
              {uploadStep === 4 ? (
                <button onClick={submitUpload} disabled={submitting || dailyLimitReached} className="glow-btn"
                  style={{ background: `linear-gradient(135deg, ${theme.accent}, ${theme.accent2})`, color: 'white', border: 'none', padding: '12px 26px', borderRadius: '999px', fontWeight: '700', fontSize: '14px', cursor: (submitting || dailyLimitReached) ? 'not-allowed' : 'pointer', opacity: (submitting || dailyLimitReached) ? 0.7 : 1, boxShadow: `0 4px 18px ${theme.glow}` }}>
                  {submitting ? 'Uploading…' : 'Submit'}
                </button>
              ) : (
                <button onClick={() => setUploadStep(s => Math.min(4, s + 1))} disabled={!stepValid || dailyLimitReached} className={(stepValid && !dailyLimitReached) ? 'glow-btn' : ''}
                  style={{ border: 'none', padding: '12px 26px', borderRadius: '999px', fontWeight: '700', fontSize: '14px', background: (stepValid && !dailyLimitReached) ? `linear-gradient(135deg, ${theme.accent}, ${theme.accent2})` : theme.border, color: (stepValid && !dailyLimitReached) ? 'white' : theme.muted, cursor: (stepValid && !dailyLimitReached) ? 'pointer' : 'not-allowed', boxShadow: (stepValid && !dailyLimitReached) ? `0 4px 18px ${theme.glow}` : 'none' }}>
                  Next
                </button>
              )}
            </div>
          </div>
        </div>
      )}

      {/* ── Add Celebrity ── */}
      {addCelebOpen && (
        <div onClick={closeAddCeleb}
          style={{ position: 'fixed', top: 0, right: 0, bottom: 0, left: 0, background: theme.overlay, backdropFilter: 'blur(6px)', WebkitBackdropFilter: 'blur(6px)', zIndex: 120, display: 'flex', alignItems: 'center', justifyContent: 'center', padding: '20px' }}>
          <div onClick={e => e.stopPropagation()}
            style={{ borderRadius: '24px', width: '480px', maxWidth: '100%', maxHeight: '90vh', overflow: 'auto', padding: '28px', display: 'flex', flexDirection: 'column', gap: '20px', ...glassStyle(theme, { strong: true }) }}>

            {/* Header */}
            <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
              <div style={{ fontFamily: "'Bebas Neue'", fontSize: '26px' }}>Add a Celebrity</div>
              <div onClick={closeAddCeleb} style={{ cursor: 'pointer', color: theme.muted }}><Icon name="close" size={20} /></div>
            </div>

            {/* Search input */}
            <div style={{ display: 'flex', flexDirection: 'column', gap: '12px' }}>
              <div style={{ fontSize: '14px', fontWeight: '700' }}>Celebrity name</div>
              <input type="text" placeholder="e.g., Taylor Swift, Timothée Chalamet…"
                value={addCelebQuery}
                onChange={e => {
                  const val = e.target.value;
                  setAddCelebQuery(val);
                  if (val.trim()) handleSearchCeleb(val);
                  else setAddCelebPreview(null);
                }}
                style={{ width: '100%', padding: '11px 14px', borderRadius: '10px', border: `1px solid ${theme.border}`, background: theme.subtle, color: theme.text, fontSize: '14px' }} />
            </div>

            {/* Wikipedia preview */}
            {addCelebLoading && (
              <div style={{ textAlign: 'center', color: theme.muted, fontSize: '13px', padding: '20px 0' }}>Searching…</div>
            )}

            {!addCelebLoading && addCelebPreview && !addCelebPreview.error && (
              <div style={{ display: 'flex', gap: '14px', padding: '14px', borderRadius: '16px', background: theme.subtle, border: `1px solid ${theme.border}` }}>
                {addCelebPreview.thumbnail && (
                  <img src={addCelebPreview.thumbnail} alt={addCelebPreview.title}
                    style={{ width: '80px', height: '80px', borderRadius: '10px', objectFit: 'cover', flex: 'none' }} />
                )}
                <div style={{ flex: 1, minWidth: 0 }}>
                  <div style={{ fontSize: '14px', fontWeight: '700', marginBottom: '4px' }}>{addCelebPreview.title}</div>
                  <div style={{ fontSize: '12px', color: theme.muted, lineHeight: 1.4, display: '-webkit-box', WebkitLineClamp: 2, WebkitBoxOrient: 'vertical', overflow: 'hidden' }}>
                    {addCelebPreview.description}
                  </div>
                  {addCelebPreview.pageUrl && (
                    <div style={{ fontSize: '11px', color: theme.accent, marginTop: '6px', cursor: 'pointer', textDecoration: 'underline' }}
                      onClick={() => window.open(addCelebPreview.pageUrl, '_blank')}>
                      View on Wikipedia
                    </div>
                  )}
                </div>
              </div>
            )}

            {!addCelebLoading && addCelebPreview?.error && (
              <div style={{ fontSize: '13px', color: theme.muted, background: theme.subtle, padding: '12px 14px', borderRadius: '10px' }}>
                {addCelebPreview.error}
              </div>
            )}

            {!addCelebLoading && addCelebQuery.trim() && !addCelebPreview && (
              <div style={{ fontSize: '13px', color: theme.muted, textAlign: 'center', padding: '20px 0' }}>
                No Wikipedia entry found — you can still create the page.
              </div>
            )}

            {/* Image upload */}
            {addCelebQuery.trim() && (
              <div style={{ display: 'flex', flexDirection: 'column', gap: '12px', paddingTop: '12px', borderTop: `1px solid ${theme.border}` }}>
                <div style={{ fontSize: '14px', fontWeight: '700' }}>Profile photo (optional)</div>
                <div style={{ fontSize: '12px', color: theme.muted, fontStyle: 'italic' }}>
                  {addCelebImageUrl.trim() ? 'Using URL image' : (addCelebImage ? 'Using file upload' : 'Choose one or leave blank')}
                </div>
                <div style={{ display: 'flex', flexDirection: 'column', gap: '8px' }}>
                  <input type="file" accept="image/*,video/*" onChange={handleAddCelebImageChange}
                    style={{ fontSize: '13px' }} />
                  <div style={{ fontSize: '12px', textAlign: 'center', color: theme.muted }}>or</div>
                  <input type="url" placeholder="Image URL (paste link)"
                    value={addCelebImageUrl}
                    onChange={e => handleAddCelebImageUrl(e.target.value)}
                    style={{ width: '100%', padding: '10px 12px', borderRadius: '10px', border: `1px solid ${theme.border}`, background: theme.subtle, color: theme.text, fontSize: '13px' }} />
                </div>
                {addCelebImagePreview && (
                  <img src={addCelebImagePreview} alt="Celebrity preview"
                    style={{ width: '100%', maxHeight: '200px', borderRadius: '10px', objectFit: 'cover' }} />
                )}
              </div>
            )}

            {/* Submit button */}
            <div style={{ display: 'flex', gap: '12px' }}>
              <button onClick={closeAddCeleb} className="glass-btn"
                style={{ flex: 1, color: theme.text, padding: '11px 20px', borderRadius: '999px', fontWeight: '600', fontSize: '14px', cursor: 'pointer', ...glassStyle(theme) }}>
                Cancel
              </button>
              <button onClick={submitAddCeleb} disabled={!addCelebQuery.trim() || addCelebLoading} className={addCelebQuery.trim() && !addCelebLoading ? 'glow-btn' : ''}
                style={{ flex: 1, background: addCelebQuery.trim() && !addCelebLoading ? `linear-gradient(135deg, ${theme.accent}, ${theme.accent2})` : theme.border, color: addCelebQuery.trim() && !addCelebLoading ? 'white' : theme.muted, border: 'none', padding: '11px 20px', borderRadius: '999px', fontWeight: '600', fontSize: '14px', cursor: addCelebQuery.trim() && !addCelebLoading ? 'pointer' : 'not-allowed', boxShadow: addCelebQuery.trim() && !addCelebLoading ? `0 4px 18px ${theme.glow}` : 'none' }}>
                {addCelebLoading ? 'Loading…' : 'Add to r34vault'}
              </button>
            </div>
          </div>
        </div>
      )}

      {/* ── Edit Celebrity ── */}
      {editCelebOpen && editCelebId && (
        <div onClick={closeEditCeleb}
          style={{ position: 'fixed', top: 0, right: 0, bottom: 0, left: 0, background: theme.overlay, backdropFilter: 'blur(6px)', WebkitBackdropFilter: 'blur(6px)', zIndex: 120, display: 'flex', alignItems: 'center', justifyContent: 'center', padding: '20px' }}>
          <div onClick={e => e.stopPropagation()}
            style={{ borderRadius: '24px', width: '480px', maxWidth: '100%', maxHeight: '90vh', overflow: 'auto', padding: '28px', display: 'flex', flexDirection: 'column', gap: '20px', ...glassStyle(theme, { strong: true }) }}>

            {/* Header */}
            <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
              <div style={{ fontFamily: "'Bebas Neue'", fontSize: '26px' }}>Edit {editCelebName}</div>
              <div onClick={closeEditCeleb} style={{ cursor: 'pointer', color: theme.muted }}><Icon name="close" size={20} /></div>
            </div>

            {/* Name input */}
            <div style={{ display: 'flex', flexDirection: 'column', gap: '8px' }}>
              <div style={{ fontSize: '14px', fontWeight: '700' }}>Name</div>
              <input type="text" value={editCelebName}
                onChange={e => setEditCelebName(e.target.value)}
                style={{ width: '100%', padding: '11px 14px', borderRadius: '10px', border: `1px solid ${theme.border}`, background: theme.subtle, color: theme.text, fontSize: '14px' }} />
            </div>

            {/* Bio input */}
            <div style={{ display: 'flex', flexDirection: 'column', gap: '8px' }}>
              <div style={{ fontSize: '14px', fontWeight: '700' }}>Bio</div>
              <textarea value={editCelebBio}
                onChange={e => setEditCelebBio(e.target.value)}
                placeholder="Add or update their bio…"
                style={{ width: '100%', padding: '11px 14px', borderRadius: '10px', border: `1px solid ${theme.border}`, background: theme.subtle, color: theme.text, fontSize: '14px', minHeight: '80px', resize: 'vertical' }} />
            </div>

            {/* Image upload */}
            <div style={{ display: 'flex', flexDirection: 'column', gap: '12px', paddingTop: '12px', borderTop: `1px solid ${theme.border}` }}>
              <div style={{ fontSize: '14px', fontWeight: '700' }}>Profile photo</div>
              <div style={{ fontSize: '12px', color: theme.muted, fontStyle: 'italic' }}>
                {editCelebImageUrl.trim() ? 'Using URL image' : (editCelebImage ? 'Using file upload' : 'Current image shown above')}
              </div>
              <div style={{ display: 'flex', flexDirection: 'column', gap: '8px' }}>
                <input type="file" accept="image/*,video/*" onChange={handleEditCelebImageChange}
                  style={{ fontSize: '13px' }} />
                <div style={{ fontSize: '12px', textAlign: 'center', color: theme.muted }}>or</div>
                <input type="url" placeholder="Image URL (paste link)"
                  value={editCelebImageUrl}
                  onChange={e => handleEditCelebImageUrl(e.target.value)}
                  style={{ width: '100%', padding: '10px 12px', borderRadius: '10px', border: `1px solid ${theme.border}`, background: theme.subtle, color: theme.text, fontSize: '13px' }} />
              </div>
              {editCelebImagePreview && (
                <img src={editCelebImagePreview} alt="Celebrity preview"
                  style={{ width: '100%', maxHeight: '200px', borderRadius: '10px', objectFit: 'cover' }} />
              )}
            </div>

            {/* Submit button */}
            <div style={{ display: 'flex', gap: '12px' }}>
              <button onClick={closeEditCeleb} className="glass-btn"
                style={{ flex: 1, color: theme.text, padding: '11px 20px', borderRadius: '999px', fontWeight: '600', fontSize: '14px', cursor: 'pointer', ...glassStyle(theme) }}>
                Cancel
              </button>
              <button onClick={submitEditCeleb} disabled={!editCelebName.trim() || editCelebLoading} className={editCelebName.trim() && !editCelebLoading ? 'glow-btn' : ''}
                style={{ flex: 1, background: editCelebName.trim() && !editCelebLoading ? `linear-gradient(135deg, ${theme.accent}, ${theme.accent2})` : theme.border, color: editCelebName.trim() && !editCelebLoading ? 'white' : theme.muted, border: 'none', padding: '11px 20px', borderRadius: '999px', fontWeight: '600', fontSize: '14px', cursor: editCelebName.trim() && !editCelebLoading ? 'pointer' : 'not-allowed', boxShadow: editCelebName.trim() && !editCelebLoading ? `0 4px 18px ${theme.glow}` : 'none' }}>
                {editCelebLoading ? 'Saving…' : 'Save Changes'}
              </button>
            </div>
          </div>
        </div>
      )}

      {/* ── Save to folder ── */}
      {saveModalItem && (
        <div onClick={closeSaveModal}
          style={{ position: 'fixed', top: 0, right: 0, bottom: 0, left: 0, background: theme.overlay, backdropFilter: 'blur(6px)', WebkitBackdropFilter: 'blur(6px)', zIndex: 120, display: 'flex', alignItems: 'center', justifyContent: 'center', padding: '20px' }}>
          <div onClick={e => e.stopPropagation()}
            style={{ borderRadius: '24px', width: '380px', maxWidth: '100%', maxHeight: '90vh', overflow: 'auto', padding: '28px', display: 'flex', flexDirection: 'column', gap: '16px', ...glassStyle(theme, { strong: true }) }}>

            <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
              <div style={{ fontFamily: "'Bebas Neue'", fontSize: '26px' }}>Save to folder</div>
              <div onClick={closeSaveModal} style={{ cursor: 'pointer', color: theme.muted }}><Icon name="close" size={20} /></div>
            </div>

            {folders.length === 0 && (
              <div style={{ fontSize: '13px', color: theme.muted }}>No folders yet — create one below.</div>
            )}

            <div style={{ display: 'flex', flexDirection: 'column', gap: '8px' }}>
              {folders.map(f => {
                const inFolder = (f.mediaIds || []).includes(saveModalItem.id);
                return (
                  <div key={f.id} onClick={() => toggleSaveInFolder(f, saveModalItem)}
                    style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', padding: '11px 14px', borderRadius: '10px', cursor: 'pointer', ...glassStyle(theme) }}>
                    <div style={{ fontSize: '13px', fontWeight: '600' }}>{f.name}</div>
                    <Icon name="check" size={17} style={{ opacity: inFolder ? 1 : 0, color: theme.accent }} />
                  </div>
                );
              })}
            </div>

            <button onClick={createFolderAndSave} className="glass-btn"
              style={{ color: theme.text, padding: '11px 20px', borderRadius: '999px', fontWeight: '600', fontSize: '14px', cursor: 'pointer', ...glassStyle(theme) }}>
              + New folder
            </button>
          </div>
        </div>
      )}

      {/* ── Edit Profile ── */}
      {editProfileOpen && currentUser && (
        <div onClick={closeEditProfile}
          style={{ position: 'fixed', top: 0, right: 0, bottom: 0, left: 0, background: theme.overlay, backdropFilter: 'blur(6px)', WebkitBackdropFilter: 'blur(6px)', zIndex: 120, display: 'flex', alignItems: 'center', justifyContent: 'center', padding: '20px' }}>
          <div onClick={e => e.stopPropagation()}
            style={{ borderRadius: '24px', width: '440px', maxWidth: '100%', maxHeight: '90vh', overflow: 'auto', padding: '28px', display: 'flex', flexDirection: 'column', gap: '20px', ...glassStyle(theme, { strong: true }) }}>

            <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
              <div style={{ fontFamily: "'Bebas Neue'", fontSize: '26px' }}>Edit profile</div>
              <div onClick={closeEditProfile} style={{ cursor: 'pointer', color: theme.muted }}><Icon name="close" size={20} /></div>
            </div>

            <div style={{ display: 'flex', flexDirection: 'column', gap: '8px' }}>
              <div style={{ fontSize: '14px', fontWeight: '700' }}>Name</div>
              <input type="text" value={editProfileName}
                onChange={e => setEditProfileName(e.target.value)}
                style={{ width: '100%', padding: '11px 14px', borderRadius: '10px', border: `1px solid ${theme.border}`, background: theme.subtle, color: theme.text, fontSize: '14px' }} />
            </div>

            <div style={{ display: 'flex', flexDirection: 'column', gap: '12px', paddingTop: '4px' }}>
              <div style={{ fontSize: '14px', fontWeight: '700' }}>Profile photo</div>
              <div style={{ display: 'flex', alignItems: 'center', gap: '16px' }}>
                <Avatar name={editProfileName || currentUser.email || 'U'} thumbnail={editProfileImagePreview} size={64} />
                <input type="file" accept="image/*" onChange={handleEditProfileImageChange} style={{ fontSize: '13px' }} />
              </div>
            </div>

            <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', paddingTop: '12px', borderTop: `1px solid ${theme.border}` }}>
              <div>
                <div style={{ fontSize: '14px', fontWeight: '700' }}>Private profile</div>
                <div style={{ fontSize: '12px', color: theme.muted, marginTop: '2px', maxWidth: '260px' }}>
                  New uploads will show as "Posted by r34vault" instead of your name.
                </div>
              </div>
              <div onClick={() => setEditProfilePrivate(p => !p)}
                style={{ width: '44px', height: '26px', borderRadius: '999px', cursor: 'pointer', padding: '3px', flex: 'none', display: 'flex', justifyContent: editProfilePrivate ? 'flex-end' : 'flex-start', background: editProfilePrivate ? `linear-gradient(135deg, ${theme.accent}, ${theme.accent2})` : theme.border }}>
                <div style={{ width: '20px', height: '20px', borderRadius: '50%', background: 'white' }} />
              </div>
            </div>

            <div style={{ display: 'flex', gap: '12px' }}>
              <button onClick={closeEditProfile} className="glass-btn"
                style={{ flex: 1, color: theme.text, padding: '11px 20px', borderRadius: '999px', fontWeight: '600', fontSize: '14px', cursor: 'pointer', ...glassStyle(theme) }}>
                Cancel
              </button>
              <button onClick={submitEditProfile} disabled={!editProfileName.trim() || editProfileLoading} className={editProfileName.trim() && !editProfileLoading ? 'glow-btn' : ''}
                style={{ flex: 1, background: editProfileName.trim() && !editProfileLoading ? `linear-gradient(135deg, ${theme.accent}, ${theme.accent2})` : theme.border, color: editProfileName.trim() && !editProfileLoading ? 'white' : theme.muted, border: 'none', padding: '11px 20px', borderRadius: '999px', fontWeight: '600', fontSize: '14px', cursor: editProfileName.trim() && !editProfileLoading ? 'pointer' : 'not-allowed', boxShadow: editProfileName.trim() && !editProfileLoading ? `0 4px 18px ${theme.glow}` : 'none' }}>
                {editProfileLoading ? 'Saving…' : 'Save Changes'}
              </button>
            </div>
          </div>
        </div>
      )}

      {/* ── Toast ── */}
      {toast && (
        <div style={{ position: 'fixed', bottom: '28px', left: '50%', transform: 'translateX(-50%)', color: theme.text, padding: '12px 22px', borderRadius: '999px', fontSize: '13px', fontWeight: '600', zIndex: 200, animation: 'toastIn 0.25s ease', whiteSpace: 'nowrap', ...glassStyle(theme, { strong: true }) }}>
          {toast}
        </div>
      )}

    </div>
  );
}

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