// app.jsx — root component, navigation, palettes, tweaks

const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{
  "feedStyle": "tiktok",
  "shape": "rounded",
  "palette": "twilight",
  "type": "modern",
  "density": "regular",
  "dark": false,
  "endpoint": "prod"
}/*EDITMODE-END*/;

const PALETTES = {
  sage: {
    // "Twilight sage" — darker than original cream but warmer than pure dark
    bg: '#E2DFD2', surface: '#F2EFE3', surface2: '#D4D0BF',
    ink: '#1A2417', ink2: '#4F5A48',
    accent: '#728C66', accent2: '#A8C29A', accentSoft: '#CADBBE', accentInk: '#2A3A24',
  },
  matcha: {
    bg: '#F1EFE5', surface: '#FFFFFF', surface2: '#E5E1D2',
    ink: '#1A2814', ink2: '#576349',
    accent: '#7D9C6D', accent2: '#B8D2A4', accentSoft: '#DCEBCD', accentInk: '#33492A',
  },
  pistachio: {
    bg: '#F2F4EB', surface: '#FFFFFF', surface2: '#E8ECDA',
    ink: '#1B2418', ink2: '#5B6850',
    accent: '#A8C988', accent2: '#D4E5B6', accentSoft: '#E8F1D5', accentInk: '#3F5A2E',
  },
  fog: {
    bg: '#EFEFE9', surface: '#FFFFFF', surface2: '#E2E3D8',
    ink: '#1F2520', ink2: '#5E665C',
    accent: '#9CADA0', accent2: '#C7D3C9', accentSoft: '#DEE5DE', accentInk: '#3E4D40',
  },
};

const DARK = {
  bg: '#0E120D', surface: '#181D17', surface2: '#222820',
  ink: '#F0EFEA', ink2: '#9AA295',
  accent: '#A8C29A', accent2: '#3F5236', accentSoft: '#283120', accentInk: '#C9DCC0',
};

const FONT_STACKS = {
  modern: { display: '"Plus Jakarta Sans", -apple-system, system-ui', body: '"Plus Jakarta Sans", -apple-system, system-ui' },
  editorial: { display: '"Fraunces", Georgia, serif', body: '"DM Sans", -apple-system, system-ui' },
  classic: { display: '"Instrument Serif", Georgia, serif', body: '"Geist", -apple-system, system-ui' },
  rounded: { display: '"Familjen Grotesk", -apple-system, system-ui', body: '"Familjen Grotesk", -apple-system, system-ui' },
};

const PLAN_LIMITS = { free: 5, standard: 30, premium: 100 };

function loadUser() {
  try { return JSON.parse(localStorage.getItem('deanna_user')) || null; } catch { return null; }
}
function loadBoards() {
  try { return JSON.parse(localStorage.getItem('deanna_boards')) || []; } catch { return []; }
}
function getTodayKey() {
  return 'deanna_daily_' + new Date().toISOString().slice(0, 10);
}
function getDailyCount() {
  return parseInt(localStorage.getItem(getTodayKey()) || '0', 10);
}
function incrementDaily() {
  try { localStorage.setItem(getTodayKey(), getDailyCount() + 1); } catch {}
}

function applyPalette(p, dark) {
  const v = dark ? DARK : PALETTES[p] || PALETTES.sage;
  const r = document.documentElement;
  r.style.setProperty('--bg', v.bg);
  r.style.setProperty('--surface', v.surface);
  r.style.setProperty('--surface-2', v.surface2);
  r.style.setProperty('--ink', v.ink);
  r.style.setProperty('--ink-2', v.ink2);
  r.style.setProperty('--accent', v.accent);
  r.style.setProperty('--accent-2', v.accent2);
  r.style.setProperty('--accent-soft', v.accentSoft);
  r.style.setProperty('--accent-ink', v.accentInk);
}

function applyFonts(stackKey) {
  const s = FONT_STACKS[stackKey] || FONT_STACKS.modern;
  document.documentElement.style.setProperty('--font-display', s.display);
  document.documentElement.style.setProperty('--font-body', s.body);
}

function App() {
  const [t, setTweak] = useTweaks(TWEAK_DEFAULTS);

  // Effective palette: dark mode overrides palette swatches but accent still varies
  React.useEffect(() => { applyPalette(t.palette, t.dark); }, [t.palette, t.dark]);
  React.useEffect(() => { applyFonts(t.fontStack); }, [t.fontStack]);

  // Land directly on the home feed; onboarding is opt-in via Tweaks button.
  const [screen, setScreen] = React.useState('tabs'); // onboarding | tabs
  const [tab, setTab] = React.useState('home'); // home | search | drop | saved | profile
  const [overlay, setOverlay] = React.useState(null); // {kind:'store',id} | {kind:'product',product,store}
  const [saved, setSaved] = React.useState(new Set());
  const [opened, setOpened] = React.useState(0);
  const [authWall, setAuthWall] = React.useState(null); // null | 'limit' | 'save' | 'create' | 'upgrade' | 'onboarding'
  const [user, setUser] = React.useState(loadUser);
  const [boards, setBoards] = React.useState(loadBoards);
  const [boardPicker, setBoardPicker] = React.useState(null); // storeId | null

  const [isDesktop, setIsDesktop] = React.useState(window.innerWidth >= 768);
  React.useEffect(() => {
    const fn = () => setIsDesktop(window.innerWidth >= 768);
    window.addEventListener('resize', fn);
    return () => window.removeEventListener('resize', fn);
  }, []);

  const saveUser = (u) => {
    setUser(u);
    localStorage.setItem('deanna_user', JSON.stringify(u));
    if (u?.darkMode !== undefined) applyPalette(t.palette, u.darkMode);
  };
  const clearUser = () => {
    setUser(null);
    localStorage.removeItem('deanna_user');
    applyPalette(t.palette, false);
  };
  const signOut = async () => {
    const base = window.resolveDeannaApiBase ? window.resolveDeannaApiBase() : (window.__DEANNA_ENDPOINT__ ?? '');
    try { await fetch(`${base}/api/auth/logout`, { method: 'POST', credentials: 'include' }); } catch {}
    clearUser();
    setTab('home');
  };
  const saveBoards = (bOrFn) => {
    setBoards(prev => {
      const next = typeof bOrFn === 'function' ? bOrFn(prev) : bOrFn;
      localStorage.setItem('deanna_boards', JSON.stringify(next));
      return next;
    });
  };
  React.useEffect(() => {
    if (user?.darkMode !== undefined) applyPalette(t.palette, user.darkMode);
  }, []); // only on mount — user toggles handle subsequent changes
  // Verify session cookie still valid on load; clear stale localStorage if not
  React.useEffect(() => {
    if (!user?.id) return;
    const base = window.resolveDeannaApiBase ? window.resolveDeannaApiBase() : (window.__DEANNA_ENDPOINT__ ?? '');
    fetch(`${base}/api/user/${user.id}`, { credentials: 'include' })
      .then(r => { if (r.status === 401) clearUser(); })
      .catch(() => {}); // silent — keep optimistic user on network error
  }, []);
  React.useEffect(() => {
    // Use relative URLs (empty string) so requests go through the dev-server proxy,
    // which handles CORS and strips the Secure cookie flag for localhost.
    // Switch to direct localhost:3000 only when explicitly targeting local deanna2u.
    window.__DEANNA_ENDPOINT__ = t.endpoint === 'dev' ? 'http://localhost:3000' : '';
  }, [t.endpoint]);

  const onSave = (id) => {
    if (!user) { setAuthWall('save'); return; }
    // check if already saved anywhere
    const alreadySaved = boards.some(b => b.storeIds.includes(id));
    if (alreadySaved) {
      // unsave: remove from all boards
      const next = boards.map(b => ({ ...b, storeIds: b.storeIds.filter(s => s !== id) }));
      saveBoards(next);
      setSaved(prev => { const n = new Set(prev); n.delete(id); return n; });
      return;
    }
    if (boards.length === 0) {
      // first save: create default board
      const defaultBoard = { id: Date.now().toString(), name: 'My Favorites', storeIds: [id] };
      saveBoards([defaultBoard]);
      setSaved(prev => new Set([...prev, id]));
    } else if (boards.length === 1) {
      // one board: add directly
      const next = [{ ...boards[0], storeIds: [...boards[0].storeIds, id] }];
      saveBoards(next);
      setSaved(prev => new Set([...prev, id]));
    } else {
      // multiple boards: show picker
      setBoardPicker(id);
    }
  };
  const openStore = (id) => {
    if (!user && opened >= 5) { setAuthWall('limit'); return; }
    setOpened((n) => n + 1);
    setOverlay({ kind: 'store', id });
  };
  const openProduct = (product, store) => setOverlay({ kind: 'product', product, store });
  const closeOverlay = () => setOverlay(null);

  const [, setStoresSig] = React.useState(0);

  React.useEffect(() => {
    const bump = () => setStoresSig((v) => v + 1);
    window.addEventListener('deanna:stores-patch', bump);
    return () => window.removeEventListener('deanna:stores-patch', bump);
  }, []);

  React.useEffect(() => {
    const handler = (e) => {
      const newBoard = { id: Date.now().toString(), name: e.detail.name, storeIds: [] };
      saveBoards(prev => [...prev, newBoard]);
    };
    window.addEventListener('deanna:new-board', handler);
    return () => window.removeEventListener('deanna:new-board', handler);
  }, []);

  React.useEffect(() => {
    const handler = async (e) => {
      const { bookId, slug, id } = e.detail || {};
      const storeId = bookId ?? id;
      if (!storeId) return;
      const strId = String(storeId);

      // If the store is already in window.STORES, open it immediately.
      if (window.findStore && window.findStore(strId)) {
        openStore(strId);
        return;
      }

      // Fetch the newly created store from /api/share/:slug so StoreDetail has data.
      try {
        const base = window.resolveDeannaApiBase ? window.resolveDeannaApiBase() : (window.__DEANNA_ENDPOINT__ ?? '');
        const shareSlug = slug || strId;
        const r = await fetch(`${base}/api/share/${encodeURIComponent(shareSlug)}`, { credentials: 'include' });
        if (r.ok) {
          const payload = await r.json().catch(() => ({}));
          if (typeof window.exploreRowFromShareApi === 'function' && typeof window.mapApiStore === 'function') {
            const row = window.exploreRowFromShareApi(shareSlug, payload);
            if (row) {
              const mapped = window.mapApiStore(row, 0);
              window.STORES = [mapped, ...(window.STORES || [])];
              window.findStore = (id) => window.STORES.find((s) => s.id === id || String(s.bookId) === String(id));
              window.dispatchEvent(new CustomEvent('deanna:stores-patch'));
            }
          }
        }
      } catch {}

      openStore(strId);
    };
    window.addEventListener('deanna:open-book', handler);
    return () => window.removeEventListener('deanna:open-book', handler);
  }, []);

  const fetchExploreIntoStores = React.useCallback(() => {
    const base = window.resolveDeannaApiBase ? window.resolveDeannaApiBase() : (window.__DEANNA_ENDPOINT__ ?? '');
    fetch(`${base}/api/explore`)
      .then((r) => (r.ok ? r.json() : Promise.reject(r.status)))
      .then(async ({ trending = [], recent = [], explore: ex = [] }) => {
        const seen = new Set();
        const merged = [...trending, ...recent, ...ex].filter(({ id }) => {
          if (id == null) return false;
          if (seen.has(id)) return false;
          seen.add(id);
          return true;
        });

        const sc = window.SHOWCASE_BOOK_IDS_CONFIG;
        const extra =
          sc && sc.enabled &&
          typeof window.fetchExploreRowsFromShareSlugs === 'function' &&
          Array.isArray(sc.extraShareSlugs) &&
          sc.extraShareSlugs.length
            ? await window.fetchExploreRowsFromShareSlugs(sc.extraShareSlugs, base)
            : [];

        const seenCombined = new Set();
        let allRows = [...merged, ...extra].filter(({ id }) => {
          if (id == null) return false;
          if (seenCombined.has(id)) return false;
          seenCombined.add(id);
          return true;
        });

        if (sc && sc.enabled && typeof window.showcaseBookIdInRange === 'function') {
          const inShowcase = allRows.filter((row) => window.showcaseBookIdInRange(row.id, sc));
          if (inShowcase.length > 0) {
            allRows = inShowcase;
          } else if (allRows.length > 0) {
            console.warn(
              '[deanna] Showcase: ningún id en 21650–21700 en esta respuesta de explore — mostrando lista completa',
            );
          }
        }

        allRows.sort((x, y) => Number(x.id) - Number(y.id));

        if (!allRows.length) {
          window.STORES = [];
          window.DAILY_DROP = [];
          window.findStore = () => undefined;
          setStoresSig((v) => v + 1);
          return;
        }
        const mapped = allRows.map((s, i) => window.mapApiStore(s, i));
        if (typeof window.applyRelatedAndAssign === 'function') window.applyRelatedAndAssign(mapped);
        window.STORES = mapped;
        window.findStore = (id) =>
          mapped.find((s) => s.id === id || String(s.bookId) === String(id));
        window.DAILY_DROP = allRows.slice(0, 4).map((s) =>
          window.shareSlugFromUrl?.(s.url) || String(s.id));
        setStoresSig((v) => v + 1);
      })
      .catch((e) => {
        console.warn('[deanna] /api/explore failed (no ministores):', e);
        window.STORES = [];
        window.DAILY_DROP = [];
        window.findStore = () => undefined;
        setStoresSig((v) => v + 1);
      });
  }, []);

  React.useEffect(() => {
    fetchExploreIntoStores();
  }, [fetchExploreIntoStores, t.endpoint]);

  // When logged in, prepend the user's own ministores.
  React.useEffect(() => {
    if (!user) return;
    const base = window.resolveDeannaApiBase ? window.resolveDeannaApiBase() : (window.__DEANNA_ENDPOINT__ ?? '');
    fetch(`${base}/api/my-ministores`, { credentials: 'include' })
      .then(r => r.ok ? r.json() : Promise.reject(r.status))
      .then(({ ministores = [] }) => {
        if (!ministores.length) return;
        const myMapped = ministores.map((s, i) => window.mapApiStore(s, i));
        const myIds = new Set(myMapped.map(s => s.id));
        const merged = [...myMapped, ...window.STORES.filter(s => !myIds.has(s.id))];
        if (typeof window.applyRelatedAndAssign === 'function') window.applyRelatedAndAssign(merged);
        window.STORES = merged;
        window.findStore = (id) =>
          merged.find((s) => s.id === id || String(s.bookId) === String(id));
        setStoresSig(v => v + 1);
      })
      .catch(e => console.warn('[deanna] /api/my-ministores failed:', e));
  }, [user]);

  const isLocal = window.location.hostname === 'localhost' || window.location.hostname === '127.0.0.1';
  const tryCreate = () => {
    if (!user) { setAuthWall('create'); return false; }
    if (!isLocal) {
      const limit = PLAN_LIMITS[user.plan || 'free'];
      if (getDailyCount() >= limit) { setAuthWall('upgrade'); return false; }
      incrementDaily();
    }
    return true;
  };

  // ─── content for current tab ────
  const renderTab = () => {
    if (tab === 'home') return (
      <HomeFeed saved={saved} onSave={onSave} onOpenStore={openStore}
        onCreateAttempt={tryCreate}
        feedStyle={t.feedStyle} shape={t.shape} density={t.density} dark={t.dark} desktop={isDesktop} />
    );
    if (tab === 'explore') return <ExploreTab onOpenStore={openStore} shape={t.shape} onCreateAttempt={tryCreate} />;
    if (tab === 'drop') return <DailyDropTab onOpenStore={openStore} shape={t.shape} />;
    if (tab === 'saved') return (
      <SavedScreen
        boards={boards}
        onOpenStore={openStore}
        shape={t.shape}
        user={user}
        onUpgrade={() => setOverlay({ kind: 'pricing' })}
      />
    );
    if (tab === 'profile') return (
      <ProfileScreen
        user={user}
        onSignOut={signOut}
        onOpenPricing={() => setOverlay({ kind: 'pricing' })}
        onOpenStore={openStore}
        shape={t.shape}
        boards={boards}
        onToggleDark={() => {
          if (user) saveUser({ ...user, darkMode: !user.darkMode });
        }}
        todayCount={getDailyCount()}
      />
    );
  };

  const renderDesktopMain = () => (
    <div style={{ padding: '24px 28px', minHeight: '100%' }}>
      {tab === 'home' && (
        <div style={{ marginBottom: 16 }}>
          <CreateBar onCreateAttempt={tryCreate} />
        </div>
      )}
      {renderTab()}
    </div>
  );

  const overlayContent = (
    <>
      {overlay?.kind === 'store' && (
        <StoreDetail storeId={overlay.id} onBack={closeOverlay}
          onOpenStore={(id) => setOverlay({ kind: 'store', id })}
          onOpenProduct={(product, store) => setOverlay({ kind: 'product', product, store })}
          onOpenFull={(s) => setOverlay({ kind: 'iframe', store: s })}
          saved={saved} onSave={onSave} shape={t.shape} />
      )}
      {overlay?.kind === 'pricing' && (
        <PricingScreen onBack={closeOverlay} currentPlan={user?.plan || 'free'}
          onSelectPlan={(planId) => { if (user) saveUser({ ...user, plan: planId }); }} />
      )}
      {overlay?.kind === 'iframe' && (
        <StoreIframeOverlay store={overlay.store} onClose={closeOverlay} />
      )}
      {overlay?.kind === 'product' && (
        <ProductDetail product={overlay.product} store={overlay.store}
          onBack={closeOverlay} shape={t.shape} />
      )}
    </>
  );

  const sheetsContent = (desktop) => (
    <>
      {authWall && <AuthSheet reason={authWall} isDesktop={desktop}
        onClose={() => { setAuthWall(null); if (screen === 'onboarding') setScreen('tabs'); }}
        onSignup={(u) => { saveUser(u); setAuthWall(null); if (screen === 'onboarding') setScreen('tabs'); }}
        onOpenPricing={() => { setAuthWall(null); setOverlay({ kind: 'pricing' }); }} />}
      {boardPicker && (
        <BoardPickerSheet storeId={boardPicker} boards={boards} isDesktop={desktop}
          onAddToBoard={(boardId) => {
            const next = boards.map(b => b.id === boardId ? { ...b, storeIds: [...b.storeIds, boardPicker] } : b);
            saveBoards(next); setSaved(prev => new Set([...prev, boardPicker])); setBoardPicker(null);
          }}
          onNewBoard={(name) => {
            const newBoard = { id: Date.now().toString(), name, storeIds: [boardPicker] };
            saveBoards(prev => [...prev, newBoard]); setSaved(prev => new Set([...prev, boardPicker])); setBoardPicker(null);
          }}
          onClose={() => setBoardPicker(null)} />
      )}
    </>
  );

  return (
    <div style={{ fontFamily: 'var(--font-body, system-ui)' }}>
      {isDesktop ? (
        <div style={{ display: 'flex', height: '100vh', overflow: 'hidden' }}>
          <Sidebar tab={tab} setTab={setTab} user={user} dark={t.dark}
            onShowAuth={() => setAuthWall('create')} onCreateAttempt={tryCreate}
            todayCount={getDailyCount()} />
          <div style={{ flex: 1, overflowY: 'auto', position: 'relative', background: 'var(--bg)', color: 'var(--ink)' }}>
            {screen === 'onboarding' ? (
              <OnboardingScreen onDone={() => setScreen('tabs')} dark={t.dark}
                user={user} onShowAuth={() => setAuthWall('onboarding')} />
            ) : renderDesktopMain()}
            <Overlay open={!!overlay} onClose={closeOverlay}>{overlayContent}</Overlay>
            {sheetsContent(true)}
          </div>
        </div>
      ) : (
        <div style={{ position: 'relative', width: '100vw', height: '100dvh', background: 'var(--bg)', color: 'var(--ink)', overflow: 'hidden' }}>
          {screen === 'onboarding' ? (
            <OnboardingScreen onDone={() => setScreen('tabs')} dark={t.dark}
              user={user} onShowAuth={() => setAuthWall('onboarding')} />
          ) : (
            <>
              {renderTab()}
              <TabBar tab={tab} setTab={setTab} dark={t.dark} />
            </>
          )}
          <Overlay open={!!overlay} onClose={closeOverlay}>{overlayContent}</Overlay>
          {sheetsContent(false)}
        </div>
      )}
      <TweaksPanel title="Tweaks">
        <TweakSection label="Feed" />
        <TweakRadio label="Feed style" value={t.feedStyle}
          options={[
            { value: 'tiktok', label: 'TikTok' },
            { value: 'pinterest', label: 'Pin' },
            { value: 'hybrid', label: 'Hybrid' },
          ]}
          onChange={(v) => setTweak('feedStyle', v)} />
        <TweakRadio label="Density" value={t.density}
          options={['compact', 'regular', 'comfy']}
          onChange={(v) => setTweak('density', v)} />

        <TweakSection label="Shape" />
        <TweakRadio label="Cards" value={t.shape}
          options={[
            { value: 'sharp', label: 'Sharp' },
            { value: 'rounded', label: 'Rounded' },
            { value: 'blob', label: 'Blob' },
          ]}
          onChange={(v) => setTweak('shape', v)} />

        <TweakSection label="Color & Type" />
        <TweakSelect label="Palette" value={t.palette}
          options={[
            { value: 'sage', label: 'Sage (default)' },
            { value: 'matcha', label: 'Matcha' },
            { value: 'pistachio', label: 'Pistacho' },
            { value: 'fog', label: 'Fog' },
          ]}
          onChange={(v) => setTweak('palette', v)} />
        <TweakSelect label="Typography" value={t.fontStack}
          options={[
            { value: 'modern', label: 'Plus Jakarta (modern)' },
            { value: 'editorial', label: 'Fraunces (editorial)' },
            { value: 'classic', label: 'Instrument Serif' },
            { value: 'rounded', label: 'Familjen (rounded)' },
          ]}
          onChange={(v) => setTweak('fontStack', v)} />
        <TweakToggle label="Dark mode" value={t.dark}
          onChange={(v) => setTweak('dark', v)} />

        <TweakSection label="Backend" />
        <TweakRadio label="Endpoint" value={t.endpoint}
          options={[
            { value: 'prod', label: 'Prod' },
            { value: 'dev',  label: 'Dev (localhost:3000)' },
          ]}
          onChange={(v) => {
            setTweak('endpoint', v);
            window.__DEANNA_ENDPOINT__ = v === 'dev' ? 'http://localhost:3000' : '';
          }} />

        <TweakSection label="Demo" />
        <TweakButton label="Restart onboarding"
          onClick={() => { setScreen('onboarding'); setOverlay(null); setOpened(0); clearUser(); }} />
        <TweakButton label={user ? 'Sign out (demo)' : 'Simulate login'}
          onClick={() => user ? clearUser() : saveUser({ name: 'Demo', email: 'demo@deanna.pro', plan: 'standard', darkMode: false })} />
      </TweaksPanel>
    </div>
  );
}

// ─── Daily Drop tab ──────────────────────────────────────────────────────
function DailyDropTab({ onOpenStore, shape }) {
  return (
    <div style={{ height: '100%', overflowY: 'auto', background: 'var(--bg)',
      padding: '60px 16px 100px', color: 'var(--ink)' }}>
      <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 6 }}>
        {Icon.flame('var(--accent-ink)')}
        <div style={{ fontSize: 12, fontWeight: 600, letterSpacing: 0.5,
          textTransform: 'uppercase', color: 'var(--accent-ink)' }}>Daily Drop</div>
      </div>
      <div style={{ fontSize: 30, fontWeight: 700, letterSpacing: -0.7,
        fontFamily: 'var(--font-display)' }}>4 Fresh MiniStores Today</div>
      <div style={{ fontSize: 13, color: 'var(--ink-2)', marginTop: 4, marginBottom: 22 }}>
        Curated by our AI while you slept · Refreshes in 6h 12m
      </div>
      <div style={{ display: 'flex', flexDirection: 'column', gap: 14 }}>
        {(window.DAILY_DROP.length ? window.DAILY_DROP : (window.STORES || []).slice(0, 4).map((x) => x.id)).map((id, i) => {
          const s = window.findStore(id);
          if (!s) return null;
          return (
            <div key={`${id}-${i}`} onClick={() => onOpenStore(id)} style={{
              position: 'relative', borderRadius: 22, overflow: 'hidden',
              height: 220, cursor: 'pointer', background: s.gradient,
              boxShadow: '0 10px 24px rgba(40,55,32,0.14)',
            }}>
              {s.cover ? (
                <img src={s.cover} alt="" style={{ position: 'absolute', inset: 0,
                  width: '100%', height: '100%', objectFit: 'cover',
                  mixBlendMode: 'multiply', opacity: 0.94 }} />
              ) : null}
              <div style={{ position: 'absolute', inset: 0,
                background: 'linear-gradient(180deg, rgba(15,25,12,0.1) 30%, rgba(15,25,12,0.78))' }} />
              <div style={{ position: 'absolute', top: 14, left: 14,
                padding: '5px 10px', borderRadius: 999,
                background: 'rgba(255,255,255,0.92)',
                color: '#1A2A14', fontSize: 11, fontWeight: 700,
                letterSpacing: 0.4 }}>#{i+1} TODAY</div>
              <div style={{ position: 'absolute', left: 16, right: 16, bottom: 14, color: '#fff' }}>
                <div style={{ fontSize: 20, fontWeight: 600, letterSpacing: -0.4,
                  fontFamily: 'var(--font-display)' }}>{s.title}</div>
                <div style={{ fontSize: 12, opacity: 0.85, marginTop: 4 }}>{s.vibe}</div>
              </div>
            </div>
          );
        })}
      </div>
    </div>
  );
}

// ─── Explore tab ─────────────────────────────────────────────────────────
function ExploreTab({ onOpenStore, shape, onCreateAttempt }) {
  const [q, setQ] = React.useState('');
  const [, sig] = React.useReducer((x) => x + 1, 0);
  React.useEffect(() => {
    const h = () => sig();
    window.addEventListener('deanna:stores-patch', h);
    return () => window.removeEventListener('deanna:stores-patch', h);
  }, []);

  const allStores = [...(window.STORES || [])].reverse(); // newest bookId last → show newest first
  const filtered = q
    ? allStores.filter((s) =>
        `${s.title} ${s.vibe} ${s.creatorName || ''} ${(s.products || []).map((p) => p.name).join(' ')}`.toLowerCase()
          .includes(q.toLowerCase()))
    : allStores;

  return (
    <div style={{ height: '100%', overflowY: 'auto', background: 'var(--bg)',
      color: 'var(--ink)', padding: '60px 16px 100px' }}>

      {/* Header */}
      <div style={{ marginBottom: 14 }}>
        <div style={{ fontSize: 28, fontWeight: 700, letterSpacing: -0.7,
          fontFamily: 'var(--font-display)', marginBottom: 4 }}>Explore</div>
        <div style={{ fontSize: 13, color: 'var(--ink-2)' }}>
          {allStores.length} MiniStore{allStores.length !== 1 ? 's' : ''} · newest first
        </div>
      </div>

      {/* Search bar */}
      <div style={{
        display: 'flex', alignItems: 'center', gap: 8,
        padding: '10px 14px', borderRadius: 14,
        background: 'var(--surface-2)', marginBottom: 16,
      }}>
        {Icon.search('var(--ink-2)', 2)}
        <input value={q} onChange={(e) => setQ(e.target.value)}
          placeholder="Search MiniStores, vibes, products…"
          style={{ flex: 1, border: 'none', outline: 'none',
            background: 'transparent', fontSize: 14, color: 'var(--ink)',
            fontFamily: 'inherit' }} />
        {q && <button onClick={() => setQ('')} style={{ border: 'none', background: 'none',
          cursor: 'pointer', color: 'var(--ink-2)', fontSize: 20, lineHeight: 1, padding: 0 }}>×</button>}
      </div>

      {/* AI create strip */}
      <CreateBar onCreateAttempt={onCreateAttempt} />

      {/* Grid */}
      {filtered.length === 0 ? (
        <div style={{ marginTop: 40, textAlign: 'center', color: 'var(--ink-2)', fontSize: 14 }}>
          {q ? `No results for "${q}"` : 'No MiniStores yet. Create the first one above!'}
        </div>
      ) : (
        <div style={{ display: 'grid', gridTemplateColumns: 'repeat(2,1fr)', gap: 10, marginTop: 4 }}>
          {filtered.map((s) => (
            <div key={s.id} onClick={() => onOpenStore(s.id)} style={{
              borderRadius: 16, overflow: 'hidden', cursor: 'pointer',
              background: 'var(--surface)', boxShadow: '0 1px 4px rgba(0,0,0,0.07)',
            }}>
              <div style={{ height: 130, background: s.gradient || 'var(--surface-2)', position: 'relative' }}>
                {s.cover && <img src={s.cover} alt={s.title} style={{
                  position: 'absolute', inset: 0, width: '100%', height: '100%',
                  objectFit: 'cover', mixBlendMode: 'multiply', opacity: 0.92,
                }} />}
                <div style={{ position: 'absolute', inset: 0,
                  background: 'linear-gradient(180deg,transparent 40%,rgba(15,25,12,0.6) 100%)' }} />
                <div style={{ position: 'absolute', bottom: 8, left: 8,
                  background: 'rgba(26,36,23,0.65)', color: '#fff',
                  fontSize: 8, fontWeight: 700, padding: '3px 7px', borderRadius: 20,
                  letterSpacing: 0.5 }}>● MINISTORE</div>
              </div>
              <div style={{ padding: '9px 11px 11px' }}>
                <div style={{ fontSize: 12, fontWeight: 600, color: 'var(--ink)',
                  marginBottom: 2, lineHeight: '16px' }}>{s.title}</div>
                <div style={{ fontSize: 11, color: 'var(--ink-2)' }}>
                  {s.vibe || (s.author ? `by ${s.author}` : '')}
                </div>
              </div>
            </div>
          ))}
        </div>
      )}
    </div>
  );
}

// ─── Sidebar (desktop) ───────────────────────────────────────────────────
function Sidebar({ tab, setTab, user, dark, onShowAuth, onCreateAttempt, todayCount }) {
  const items = [
    { id: 'home',    label: 'Home',       icon: Icon.home },
    { id: 'explore', label: 'Explore',    icon: Icon.search },
    { id: 'drop',    label: 'Daily Drop', icon: Icon.drop },
    { id: 'saved',   label: 'Saved',      icon: Icon.saved },
    { id: 'profile', label: 'Profile',    icon: Icon.user },
  ];
  const limit = user ? (PLAN_LIMITS[user.plan || 'free']) : 5;
  return (
    <div style={{
      width: 200, flexShrink: 0, background: '#1A2417',
      display: 'flex', flexDirection: 'column', height: '100vh',
    }}>
      <div style={{ padding: '24px 20px 20px', borderBottom: '1px solid rgba(255,255,255,0.08)' }}>
        <div style={{ fontSize: 18, fontWeight: 700, color: '#C9DCC0', letterSpacing: -0.5 }}>✦ deanna</div>
        <div style={{ fontSize: 10, color: 'rgba(201,220,192,0.5)', marginTop: 2, letterSpacing: 0.5, textTransform: 'uppercase' }}>MiniStores</div>
      </div>
      <div style={{ flex: 1, padding: '12px 10px', display: 'flex', flexDirection: 'column', gap: 2 }}>
        {items.map(it => {
          const on = tab === it.id;
          return (
            <button key={it.id} onClick={() => setTab(it.id)} style={{
              display: 'flex', alignItems: 'center', gap: 10,
              padding: '9px 12px', borderRadius: 10, border: 'none', cursor: 'pointer',
              background: on ? 'rgba(201,220,192,0.12)' : 'transparent',
              color: on ? '#C9DCC0' : 'rgba(201,220,192,0.55)',
              fontSize: 13, fontWeight: on ? 600 : 400,
              width: '100%', textAlign: 'left',
            }}>
              {it.icon(on ? '#C9DCC0' : 'rgba(201,220,192,0.55)', 2)}
              <span>{it.label}</span>
            </button>
          );
        })}
      </div>
      <div style={{ padding: '12px 10px', borderTop: '1px solid rgba(255,255,255,0.08)' }}>
        {user ? (
          <div style={{ display: 'flex', alignItems: 'center', gap: 8, padding: '6px 4px' }}>
            <div style={{
              width: 28, height: 28, borderRadius: '50%', background: '#8FA882',
              display: 'flex', alignItems: 'center', justifyContent: 'center',
              fontSize: 12, color: '#1A2417', fontWeight: 700, flexShrink: 0, textTransform: 'uppercase',
            }}>{(user.name || 'U')[0]}</div>
            <div>
              <div style={{ fontSize: 11, color: '#C9DCC0', fontWeight: 600 }}>{user.name}</div>
              <div style={{ fontSize: 9, color: 'rgba(201,220,192,0.45)' }}>
                {user.plan ? user.plan[0].toUpperCase() + user.plan.slice(1) : 'Free'} · {todayCount}/{limit} today
              </div>
            </div>
          </div>
        ) : (
          <button onClick={onShowAuth} style={{
            width: '100%', padding: '8px 14px', borderRadius: 10, border: 'none', cursor: 'pointer',
            background: 'transparent', color: 'rgba(201,220,192,0.55)', fontSize: 12,
          }}>Sign in →</button>
        )}
      </div>
    </div>
  );
}

// ─── Tab bar ──────────────────────────────────────────────────────────────
function TabBar({ tab, setTab, dark }) {
  const items = [
    { id: 'home',    label: 'Feed',    icon: Icon.home },
    { id: 'explore', label: 'Explore', icon: Icon.search },
    { id: 'drop',    label: 'Drop',    icon: Icon.drop },
    { id: 'saved',   label: 'Saved',   icon: Icon.saved },
    { id: 'profile', label: 'Profile', icon: Icon.user },
  ];
  return (
    <div style={{
      position: 'absolute', left: 12, right: 12, bottom: 22, zIndex: 60,
      display: 'flex', justifyContent: 'space-around',
      padding: '8px 6px',
      borderRadius: 999,
      background: dark ? 'rgba(20,25,18,0.78)' : 'rgba(255,255,255,0.78)',
      backdropFilter: 'blur(20px) saturate(180%)',
      WebkitBackdropFilter: 'blur(20px) saturate(180%)',
      border: dark ? '0.5px solid rgba(255,255,255,0.08)' : '0.5px solid rgba(0,0,0,0.06)',
      boxShadow: '0 8px 24px rgba(0,0,0,0.10)',
    }}>
      {items.map((it) => {
        const on = tab === it.id;
        const c = on ? 'var(--accent-ink)' : 'var(--ink-2)';
        return (
          <button key={it.id} onClick={() => setTab(it.id)} style={{
            border: 'none', background: 'transparent', cursor: 'pointer',
            padding: '6px 10px', display: 'flex', flexDirection: 'column',
            alignItems: 'center', gap: 2,
          }}>
            {it.icon(c, 2)}
            <span style={{ fontSize: 10, fontWeight: 600, color: c, letterSpacing: -0.1 }}>
              {it.label}
            </span>
          </button>
        );
      })}
    </div>
  );
}

// ─── Overlay (slide up) ───────────────────────────────────────────────────
function Overlay({ open, onClose, children }) {
  const [render, setRender] = React.useState(false);
  const [shown, setShown] = React.useState(false);
  React.useEffect(() => {
    if (open) {
      setRender(true);
      requestAnimationFrame(() => setShown(true));
    } else {
      setShown(false);
      const t = setTimeout(() => setRender(false), 280);
      return () => clearTimeout(t);
    }
  }, [open]);
  if (!render) return null;
  return (
    <div style={{
      position: 'absolute', inset: 0, zIndex: 70,
      transform: shown ? 'translateY(0)' : 'translateY(100%)',
      transition: 'transform 280ms cubic-bezier(.2,.8,.2,1)',
      background: 'var(--bg)', overflow: 'hidden',
    }}>{children}</div>
  );
}

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