// p02-home.jsx — Home screen: search + recent seeds + all seeds + my pouches
// States: default / empty (first user) / full (pouch full) / offline

function ScreenHome({ state, onNavigate, user }) {
  const T = window.T;
  const offline = state === 'offline';
  const isFirst = state === 'empty';
  const isFull = state === 'full' || (user && user.todayPouch >= 10);
  // Preset filters for shareable URL captures (state=filter / filterEmpty / filterSheet)
  const presetLang =
    state === 'filter'       ? 'es' :   // 스페인어 — 1개 매칭
    state === 'filterEmpty'  ? 'vi' :   // 베트남어
    state === 'filterSheet'  ? 'es' :
    null;
  const presetCat =
    state === 'filterEmpty'  ? 'food' : // 베트남어 + 식당 = 0
    null;
  const [langFilter, setLangFilter] = React.useState(presetLang);
  const [catFilter, setCatFilter] = React.useState(presetCat);
  const [playing, setPlaying] = React.useState(state === 'playing');
  const [playIdx, setPlayIdx] = React.useState(
    state === 'playing' ? 1 :
    state === 'paused'  ? 2 :
    0
  );
  const [showFilters, setShowFilters] = React.useState(state === 'filterSheet');
  // Pouch widget state
  const [pouchLangFilter, setPouchLangFilter] = React.useState(null);
  const [showPouchFilters, setShowPouchFilters] = React.useState(false);
  const [playingPouchId, setPlayingPouchId] = React.useState(null);
  const [playingPouchIdx, setPlayingPouchIdx] = React.useState(0);
  const seedScrollRef = React.useRef(null);

  const allSeeds = window.MOCK_SEEDS;
  const filtered = allSeeds.filter(s =>
    (!langFilter || s.lang === langFilter) &&
    (!catFilter  || s.categories.includes(catFilter))
  );
  const recent = (user?.recentSeedIds || [])
    .map(id => allSeeds.find(s => s.id === id)).filter(Boolean);
  const pouches = state === 'noPouches'
    ? []
    : window.MOCK_POUCHES;
  const filteredPouches = pouches.filter(p =>
    !pouchLangFilter || (p.langCodes || []).includes(pouchLangFilter)
  );
  const pouchLangObj = pouchLangFilter && window.MOCK_LANGS.find(l => l.code === pouchLangFilter);
  const pouchesEmpty = pouches.length === 0;

  // Auto-advance the playing pouch through its seeds; stop after the last one.
  const playingPouchTotal = (pouches.find(p => p.id === playingPouchId)?.seedIds.length) || 0;
  React.useEffect(() => {
    if (!playingPouchId || playingPouchTotal === 0) return;
    const t = setInterval(() => {
      setPlayingPouchIdx(i => {
        if (i + 1 >= playingPouchTotal) {
          setPlayingPouchId(null);   // stop
          return 0;
        }
        return i + 1;
      });
    }, 2400);
    return () => clearInterval(t);
  }, [playingPouchId, playingPouchTotal]);
  // If the filter removes the currently-playing pouch, stop playback
  React.useEffect(() => {
    if (playingPouchId && !filteredPouches.some(p => p.id === playingPouchId)) {
      setPlayingPouchId(null);
      setPlayingPouchIdx(0);
    }
  }, [filteredPouches.length, playingPouchId]);
  const langs = window.MOCK_LANGS;
  const cats  = window.MOCK_CATEGORIES;

  const activeFilters = [
    langFilter && langs.find(l => l.code === langFilter),
    catFilter  && cats.find(c => c.id === catFilter),
  ].filter(Boolean);

  // Auto-advance the play index — stops AND fully resets after one full pass
  React.useEffect(() => {
    if (!playing || filtered.length === 0) return;
    const t = setInterval(() => {
      setPlayIdx(i => {
        if (i + 1 >= filtered.length) {
          setPlaying(false);   // stop
          return 0;            // return to initial state (button shows "N개 재생" again)
        }
        return i + 1;
      });
    }, 2400);
    return () => clearInterval(t);
  }, [playing, filtered.length]);
  // Clamp playIdx when filter changes
  React.useEffect(() => {
    if (playIdx >= filtered.length) setPlayIdx(0);
  }, [filtered.length]);

  // Auto-scroll the seed carousel so the playing card stays at the leftmost slot.
  // Also resets to start when stopped (idx returns to 0).
  React.useEffect(() => {
    const el = seedScrollRef.current;
    if (!el) return;
    const CARD_W = 200, GAP = 10;          // SeedCardMini width + gap
    const target = playIdx * (CARD_W + GAP);
    el.scrollTo({ left: target, behavior: 'smooth' });
  }, [playing, playIdx]);
  const nowPlaying = playing && filtered[playIdx];

  return (
    <div style={{
      position: 'absolute', inset: 0,
      background: T.bg,
      display: 'flex', flexDirection: 'column',
    }}>
      {/* Header */}
      <div style={{
        flexShrink: 0,
        padding: '8px 20px 6px',
        display: 'flex', alignItems: 'center', gap: 10,
      }}>
        {/* Hamster avatar — cream stage so it doesn't darken in dark mode */}
        <div style={{
          width: 36, height: 36, borderRadius: '50%',
          background: '#F2EBE0',
          overflow: 'hidden',
          display: 'flex', alignItems: 'center', justifyContent: 'center',
          flexShrink: 0,
          border: '0.5px solid rgba(60,42,20,0.10)',
        }}>
          <Hamster count={user?.todayPouch || 0} max={10} size={36} headOnly/>
        </div>
        <div style={{ flex: 1, fontSize: 18, fontWeight: 800, color: T.ink, letterSpacing: '-0.03em' }}>
          CheekPouch
        </div>
        {offline && <OfflineBadge/>}
      </div>

      {/* Scrollable content */}
      <div style={{ flex: 1, overflowY: 'auto', overflowX: 'hidden', paddingBottom: 76 }}>
        {/* Search bar */}
        <div style={{ padding: '6px 20px 14px' }}>
          <div onClick={() => onNavigate && onNavigate('P07')}>
            <SearchBar value="" onChange={() => {}} />
          </div>
        </div>

        {/* First-time empty state */}
        {isFirst ? (
          <div style={{ padding: '20px 20px' }}>
            <div style={{
              background: T.surface, borderRadius: 18,
              border: `0.5px solid ${T.hairline}`,
              padding: '32px 24px', textAlign: 'center',
              display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 12,
            }}>
              <Hamster count={0} max={10} size={140}/>
              <div style={{
                fontSize: 18, fontWeight: 700, color: T.ink,
                letterSpacing: '-0.02em', marginTop: 4,
              }}>안녕! 같이 첫 씨앗 담아볼까요?</div>
              <div style={{
                fontSize: 14, color: T.ink2,
                lineHeight: 1.5, maxWidth: 260,
              }}>한국어로 말하면 외국어·발음·음성까지 한 장에 담아드려요</div>
              <Button kind="primary" onClick={() => onNavigate('P06')}
                      icon={<Icons.Plus size={18} color="#FFF"/>}
                      style={{ marginTop: 8 }}>
                첫 씨앗 담기
              </Button>
            </div>
          </div>
        ) : (
          <React.Fragment>
            {/* Recent seeds */}
            {recent.length > 0 && (
              <div style={{ padding: '0 0 18px' }}>
                <div style={{ padding: '0 20px' }}>
                  <SectionHeader title="최근 본 씨앗"/>
                </div>
                <div style={{
                  display: 'flex', gap: 10, overflowX: 'auto',
                  padding: '0 20px 4px',
                  scrollbarWidth: 'none',
                }}>
                  {recent.slice(0, 6).map(s => (
                    <SeedCardMini key={s.id} seed={s} onClick={() => onNavigate('P05', { seedId: s.id })}/>
                  ))}
                </div>
              </div>
            )}

            {/* All seeds section */}
            <div style={{ padding: '0 0 18px' }}>
              <div style={{ padding: '0 20px' }}>
              <SectionHeader
                title="내 씨앗"
                count={
                  activeFilters.length > 0
                    ? <span style={{ display: 'inline-flex', alignItems: 'baseline', gap: 4 }}>
                        <span style={{ color: T.ink3, fontWeight: 500 }}>{allSeeds.length}개 중</span>
                        <b style={{ color: T.accent, fontWeight: 800, fontSize: 14 }}>{filtered.length}개</b>
                      </span>
                    : `· ${allSeeds.length}개`
                }
                onTitleClick={() => onNavigate('P03')}
                action={
                  <div style={{ display: 'flex', gap: 6 }}>
                    {/* Filter icon-only button with active dot indicator */}
                    <button onClick={() => setShowFilters(true)}
                            aria-label={activeFilters.length > 0 ? `필터 ${activeFilters.length}개 적용됨` : '필터'}
                            style={{
                      position: 'relative',
                      width: 36, height: 36, borderRadius: 12,
                      background: T.surface,
                      border: `0.5px solid ${T.hairline}`,
                      cursor: 'pointer', padding: 0,
                      display: 'flex', alignItems: 'center', justifyContent: 'center',
                      fontFamily: 'inherit',
                    }}>
                      <FilterIcon size={16} color={T.ink}/>
                      {activeFilters.length > 0 && (
                        <span style={{
                          position: 'absolute', top: 7, right: 7,
                          width: 7, height: 7, borderRadius: '50%',
                          background: T.accent,
                          border: `1.5px solid ${T.surface}`,
                        }}/>
                      )}
                    </button>
                    <IconButton
                      icon={<Icons.Plus size={20} color={isFull || offline ? T.ink3 : T.ink}/>}
                      onClick={() => !isFull && !offline && onNavigate('P06')}
                      disabled={isFull || offline}
                      bg={T.surface}
                      style={{ borderRadius: 12, width: 36, height: 36, border: `0.5px solid ${T.hairline}` }}
                    />
                    {/* Player control — single pill when stopped, segmented pill (toggle + stop) when active */}
                    {(playing || playIdx > 0) ? (
                      <div style={{
                        display: 'inline-flex', alignItems: 'center',
                        height: 36, borderRadius: 12,
                        background: playing ? T.accent : T.surface,
                        border: playing ? 'none' : `0.5px solid ${T.hairline}`,
                        boxShadow: playing ? `0 0 0 6px ${T.accentSoft}` : 'none',
                        transition: 'box-shadow .25s, background .15s',
                        overflow: 'hidden',
                      }}>
                        {/* Pause / Resume — preserves playIdx */}
                        <button onClick={() => {
                                  if (!filtered.length) return;
                                  setPlaying(p => !p);
                                }}
                                style={{
                          height: '100%', padding: '0 12px 0 11px',
                          background: 'transparent', border: 'none',
                          color: playing ? '#FFF' : T.ink,
                          fontFamily: 'inherit', fontSize: 13, fontWeight: 600,
                          cursor: 'pointer',
                          display: 'flex', alignItems: 'center', gap: 6,
                          whiteSpace: 'nowrap', letterSpacing: '-0.01em',
                        }}>
                          {playing
                            ? <Icons.Pause size={14} color="#FFF"/>
                            : <Icons.Play size={14} color={T.ink} fillTri/>}
                          {playIdx + 1}/{filtered.length}
                        </button>

                        {/* Divider */}
                        <div style={{
                          width: 0.5, height: 18, flexShrink: 0,
                          background: playing ? 'rgba(255,255,255,0.25)' : T.hairline,
                        }}/>

                        {/* Stop — full reset */}
                        <button onClick={() => { setPlaying(false); setPlayIdx(0); }}
                                aria-label="정지"
                                style={{
                          width: 32, height: '100%',
                          background: 'transparent', border: 'none',
                          cursor: 'pointer', padding: 0,
                          display: 'flex', alignItems: 'center', justifyContent: 'center',
                          fontFamily: 'inherit',
                        }}>
                          <Icons.Stop size={12} color={playing ? '#FFF' : T.ink2}/>
                        </button>
                      </div>
                    ) : (
                      <button onClick={() => {
                                if (!filtered.length) return;
                                setPlayIdx(0);
                                setPlaying(true);
                              }}
                              disabled={filtered.length === 0}
                              style={{
                        height: 36, padding: '0 14px', borderRadius: 12,
                        background: T.surface,
                        border: `0.5px solid ${T.hairline}`,
                        color: T.ink,
                        fontFamily: 'inherit', fontSize: 13, fontWeight: 600,
                        cursor: filtered.length ? 'pointer' : 'default',
                        opacity: filtered.length ? 1 : 0.4,
                        display: 'flex', alignItems: 'center', gap: 6,
                        whiteSpace: 'nowrap', letterSpacing: '-0.01em',
                      }}>
                        <Icons.Play size={14} color={T.ink} fillTri/>
                        {filtered.length}개 재생
                      </button>
                    )}
                  </div>
                }
              />

              {/* Active filter chips — only renders when filters are set */}
              {activeFilters.length > 0 && (
                <div style={{
                  display: 'flex', gap: 6, overflowX: 'auto', alignItems: 'center',
                  marginBottom: 12, paddingBottom: 4, scrollbarWidth: 'none',
                }}>
                  {langFilter && (
                    <RemovableChip
                      label={langs.find(l => l.code === langFilter)?.label}
                      onRemove={() => setLangFilter(null)}/>
                  )}
                  {catFilter && (
                    <RemovableChip
                      label={cats.find(c => c.id === catFilter)?.label}
                      icon={cats.find(c => c.id === catFilter)?.icon}
                      onRemove={() => setCatFilter(null)}/>
                  )}
                  <button onClick={() => { setLangFilter(null); setCatFilter(null); }} style={{
                    background: 'transparent', border: 'none',
                    color: T.ink3, fontSize: 12, fontWeight: 600,
                    padding: '0 6px', cursor: 'pointer', flexShrink: 0,
                    fontFamily: 'inherit',
                  }}>지우기</button>
                </div>
              )}

              {/* Now-playing strip removed — playing card itself communicates state, auto-scrolls into view */}

              {/* Empty-filter message */}
              {filtered.length === 0 && (
                <div style={{
                  background: T.surface, borderRadius: 12,
                  border: `1px dashed ${T.hairlineStrong}`,
                  padding: '20px 16px', marginBottom: 12,
                  fontSize: 13, color: T.ink2, textAlign: 'center',
                  lineHeight: 1.5,
                }}>
                  조건에 맞는 씨앗이 없어요.<br/>
                  필터를 바꿔보세요.
                </div>
              )}

              {isFull && !offline && (
                <div style={{
                  marginTop: 12,
                  background: T.accentSoft, borderRadius: 12,
                  padding: '12px 14px',
                  display: 'flex', alignItems: 'center', gap: 10,
                }}>
                  <div style={{ flex: 1 }}>
                    <div style={{ fontSize: 13.5, fontWeight: 600, color: T.accent, marginBottom: 2 }}>
                      오늘 볼주머니가 가득 찼어요
                    </div>
                    <div style={{ fontSize: 12, color: T.ink2 }}>
                      내일 또 같이 채워봐요. 광고로 더 받을 수도 있어요.
                    </div>
                  </div>
                  <button onClick={() => onNavigate('P12')} style={{
                    background: T.accent, color: '#FFF',
                    border: 'none', borderRadius: 14,
                    padding: '8px 12px', fontSize: 12, fontWeight: 700,
                    fontFamily: 'inherit', cursor: 'pointer',
                  }}>나에서 받기</button>
                </div>
              )}
              </div>{/* /inner padded */}

              {/* Horizontal seed scroll — matches 최근 본 씨앗 rhythm */}
              {filtered.length > 0 && (
                <div ref={seedScrollRef} style={{
                  display: 'flex', gap: 10, overflowX: 'auto',
                  padding: '4px 20px 4px',
                  scrollbarWidth: 'none',
                  scrollBehavior: 'smooth',
                }}>
                  {filtered.map((s, i) => (
                    <SeedCardMini key={s.id} seed={s}
                      playing={playing && i === playIdx}
                      paused={!playing && playIdx > 0 && i === playIdx}
                      onClick={() => onNavigate('P05', { seedId: s.id })}/>
                  ))}
                  {/* See-all card — only when there are more seeds than a quick scan */}
                  {filtered.length >= 4 && (
                    <div onClick={() => onNavigate('P03')} style={{
                      width: 120, flexShrink: 0,
                      background: 'transparent', borderRadius: 14,
                      border: `0.5px dashed ${T.hairlineStrong}`,
                      padding: 14, cursor: 'pointer',
                      display: 'flex', flexDirection: 'column',
                      alignItems: 'center', justifyContent: 'center',
                      gap: 6, color: T.ink2,
                    }}>
                      <div style={{
                        width: 32, height: 32, borderRadius: 16,
                        background: T.surface,
                        border: `0.5px solid ${T.hairline}`,
                        display: 'flex', alignItems: 'center', justifyContent: 'center',
                        fontSize: 16, color: T.ink2,
                      }}>→</div>
                      <div style={{ fontSize: 12, fontWeight: 600, letterSpacing: '-0.01em' }}>전체 보기</div>
                      <div style={{ fontSize: 11, color: T.ink3 }}>{filtered.length}개</div>
                    </div>
                  )}
                </div>
              )}
            </div>{/* /All seeds section */}

            {/* My pouches — simple widget; full browsing on P04a */}
            <div style={{ padding: '0 0 28px' }}>
              <div style={{ padding: '0 20px' }}>
                <SectionHeader
                  title="내 주머니"
                  count={
                    pouchesEmpty
                      ? null
                      : pouchLangObj
                        ? <span style={{ display: 'inline-flex', alignItems: 'baseline', gap: 4 }}>
                            <span style={{ color: T.ink3, fontWeight: 500 }}>{pouches.length}개 중</span>
                            <b style={{ color: T.accent, fontWeight: 800, fontSize: 14 }}>{filteredPouches.length}개</b>
                          </span>
                        : `· ${pouches.length}개`
                  }
                  onTitleClick={pouchesEmpty ? null : () => onNavigate('P04a')}
                  action={
                    <div style={{ display: 'flex', gap: 6 }}>
                      {/* Filter button — only when there's something to filter */}
                      {!pouchesEmpty && (
                        <button onClick={() => setShowPouchFilters(true)}
                                aria-label={pouchLangObj ? `필터 적용됨: ${pouchLangObj.label}` : '필터'}
                                style={{
                          position: 'relative',
                          width: 36, height: 36, borderRadius: 12,
                          background: T.surface,
                          border: `0.5px solid ${T.hairline}`,
                          cursor: 'pointer', padding: 0,
                          display: 'flex', alignItems: 'center', justifyContent: 'center',
                          fontFamily: 'inherit',
                        }}>
                          <FilterIcon size={16} color={T.ink}/>
                          {pouchLangObj && (
                            <span style={{
                              position: 'absolute', top: 7, right: 7,
                              width: 7, height: 7, borderRadius: '50%',
                              background: T.accent,
                              border: `1.5px solid ${T.surface}`,
                            }}/>
                          )}
                        </button>
                      )}
                      <IconButton
                        icon={<Icons.Plus size={20} color={offline ? T.ink3 : T.ink}/>}
                        onClick={() => !offline && onNavigate('P08')}
                        disabled={offline}
                        bg={T.surface}
                        style={{ borderRadius: 12, width: 36, height: 36, border: `0.5px solid ${T.hairline}` }}
                      />
                    </div>
                  }
                />

                {/* Active pouch filter chip */}
                {pouchLangObj && (
                  <div style={{
                    display: 'flex', gap: 6, alignItems: 'center',
                    marginBottom: 12, paddingBottom: 4,
                  }}>
                    <RemovableChip
                      label={pouchLangObj.label}
                      onRemove={() => setPouchLangFilter(null)}/>
                    <button onClick={() => setPouchLangFilter(null)} style={{
                      background: 'transparent', border: 'none',
                      color: T.ink3, fontSize: 12, fontWeight: 600,
                      padding: '0 6px', cursor: 'pointer',
                      fontFamily: 'inherit',
                    }}>지우기</button>
                  </div>
                )}
              </div>

              {pouchesEmpty ? (
                /* User has no pouches yet — onboarding card */
                <div style={{ padding: '0 20px' }}>
                  <div style={{
                    background: T.surface, borderRadius: 14,
                    border: `0.5px solid ${T.hairline}`,
                    padding: '18px 18px 16px',
                    display: 'flex', flexDirection: 'column', gap: 12,
                  }}>
                    {/* Visual hint — placeholder pouch chips, fading */}
                    <div style={{ display: 'flex', gap: 6 }}>
                      <div style={{
                        width: 38, height: 26, borderRadius: 7,
                        background: `linear-gradient(135deg, ${T.pouchA} 0%, ${T.pouchA}CC 100%)`,
                        opacity: 0.85,
                      }}/>
                      <div style={{
                        width: 38, height: 26, borderRadius: 7,
                        background: `linear-gradient(135deg, ${T.pouchC} 0%, ${T.pouchC}CC 100%)`,
                        opacity: 0.55,
                      }}/>
                      <div style={{
                        width: 38, height: 26, borderRadius: 7,
                        background: `linear-gradient(135deg, ${T.pouchE} 0%, ${T.pouchE}CC 100%)`,
                        opacity: 0.3,
                      }}/>
                    </div>
                    <div>
                      <div style={{
                        fontSize: 15, fontWeight: 700, color: T.ink,
                        letterSpacing: '-0.02em', marginBottom: 4,
                      }}>씨앗을 주머니에 묶어보세요</div>
                      <div style={{
                        fontSize: 13, color: T.ink2, lineHeight: 1.5,
                      }}>도쿄여행, 호텔에서처럼 주제별로 모아두면 한 번에 재생할 수 있어요</div>
                    </div>
                    <div style={{ display: 'flex', gap: 8, marginTop: 2 }}>
                      <Button kind="primary" size="sm"
                              icon={<Icons.Plus size={14} color="#FFF"/>}
                              onClick={() => !offline && onNavigate('P08')}
                              disabled={offline}>
                        첫 주머니 만들기
                      </Button>
                      <Button kind="ghost" size="sm"
                              onClick={() => onNavigate('P14')}>
                        자세히 알아보기
                      </Button>
                    </div>
                  </div>
                </div>
              ) : filteredPouches.length === 0 ? (
                <div style={{ padding: '0 20px' }}>
                  <div style={{
                    background: T.surface, borderRadius: 12,
                    border: `1px dashed ${T.hairlineStrong}`,
                    padding: '20px 16px',
                    fontSize: 13, color: T.ink2, textAlign: 'center',
                    lineHeight: 1.5,
                  }}>
                    조건에 맞는 주머니가 없어요.<br/>
                    필터를 바꿔보세요.
                  </div>
                </div>
              ) : (
                <div style={{
                  display: 'flex', gap: 10, overflowX: 'auto',
                  padding: '0 20px 4px',
                  scrollbarWidth: 'none',
                }}>
                  {filteredPouches.slice(0, 4).map(p => (
                    <PouchCardMini key={p.id} pouch={p}
                      playing={playingPouchId === p.id}
                      playIdx={playingPouchId === p.id ? playingPouchIdx : 0}
                      onClick={() => onNavigate('P04', { pouchId: p.id })}
                      onPlay={() => { setPlayingPouchIdx(0); setPlayingPouchId(p.id); }}
                      onStop={() => { setPlayingPouchId(null); setPlayingPouchIdx(0); }}/>
                  ))}
                  {filteredPouches.length >= 4 && (
                    <div onClick={() => onNavigate('P04a')} style={{
                      width: 96, flexShrink: 0,
                      background: 'transparent', borderRadius: 14,
                      border: `0.5px dashed ${T.hairlineStrong}`,
                      padding: 14, cursor: 'pointer',
                      display: 'flex', flexDirection: 'column',
                      alignItems: 'center', justifyContent: 'center',
                      gap: 6, color: T.ink2,
                    }}>
                      <div style={{
                        width: 32, height: 32, borderRadius: 16,
                        background: T.surface,
                        border: `0.5px solid ${T.hairline}`,
                        display: 'flex', alignItems: 'center', justifyContent: 'center',
                        fontSize: 16, color: T.ink2,
                      }}>→</div>
                      <div style={{ fontSize: 12, fontWeight: 600, letterSpacing: '-0.01em' }}>전체 보기</div>
                      <div style={{ fontSize: 11, color: T.ink3 }}>{filteredPouches.length}개</div>
                    </div>
                  )}
                </div>
              )}
            </div>
          </React.Fragment>
        )}
      </div>
      {/* Filter sheet — scalable home for all current/future filter axes */}
      <BottomSheet open={showFilters} onClose={() => setShowFilters(false)} title="필터">
        <FilterSheet
          langs={langs} cats={cats}
          langFilter={langFilter} catFilter={catFilter}
          setLang={setLangFilter} setCat={setCatFilter}
          matchCount={filtered.length}
          onClose={() => setShowFilters(false)}
          onNavigate={onNavigate}
        />
      </BottomSheet>

      {/* Pouch filter sheet — language only (simpler axis set) */}
      <BottomSheet open={showPouchFilters} onClose={() => setShowPouchFilters(false)} title="주머니 필터">
        <PouchFilterSheet
          langs={langs} pouches={pouches}
          langFilter={pouchLangFilter}
          setLang={setPouchLangFilter}
          onClose={() => setShowPouchFilters(false)}
        />
      </BottomSheet>
    </div>
  );
}

// ──────────────────────────────────────────────────────────────
// Small components used by P02
// ──────────────────────────────────────────────────────────────

// Filter icon (3 horizontal bars w/ dots)
function FilterIcon({ size = 14, color }) {
  return (
    <svg width={size} height={size} viewBox="0 0 16 16" fill="none">
      <path d="M2 4h12M2 8h12M2 12h12" stroke={color || 'currentColor'} strokeWidth="1.8" strokeLinecap="round"/>
      <circle cx="5" cy="4" r="1.5" fill={color || 'currentColor'}/>
      <circle cx="11" cy="8" r="1.5" fill={color || 'currentColor'}/>
      <circle cx="6" cy="12" r="1.5" fill={color || 'currentColor'}/>
    </svg>
  );
}

// Removable filter chip
function RemovableChip({ label, icon, onRemove }) {
  const T = window.T;
  return (
    <div style={{
      height: 28, padding: '0 4px 0 10px',
      background: T.accentSoft, color: T.accent,
      borderRadius: 14,
      fontSize: 12, fontWeight: 600, letterSpacing: '-0.01em',
      display: 'inline-flex', alignItems: 'center', gap: 4,
      flexShrink: 0,
    }}>
      {icon && <span style={{ fontSize: 12 }}>{icon}</span>}
      <span>{label}</span>
      <button onClick={onRemove} style={{
        width: 20, height: 20, borderRadius: 10,
        background: 'transparent', border: 'none',
        color: T.accent,
        display: 'flex', alignItems: 'center', justifyContent: 'center',
        cursor: 'pointer', padding: 0, marginLeft: 1,
      }}>
        <Icons.Close size={10} color={T.accent}/>
      </button>
    </div>
  );
}

// Animated 3-bar wave indicator for "playing"
function PlayingWave({ color = '#FFF', size = 14 }) {
  const bars = [0, 1, 2];
  return (
    <div style={{ display: 'inline-flex', alignItems: 'center', gap: 2.5, height: size, flexShrink: 0 }}>
      {bars.map(i => (
        <div key={i} style={{
          width: 3, height: size, borderRadius: 2, background: color,
          animation: `cp-wave 0.85s ease-in-out infinite ${i * 0.15}s`,
          transformOrigin: 'center',
        }}/>
      ))}
    </div>
  );
}

// Filter bottom sheet content — all filter axes in one place
function FilterSheet({ langs, cats, langFilter, catFilter, setLang, setCat, matchCount, onClose, onNavigate, favOnly, setFavOnly, showFav }) {
  const T = window.T;
  const [ln, setLn] = React.useState(langFilter);
  const [ct, setCt] = React.useState(catFilter);
  const [fv, setFv] = React.useState(!!favOnly);

  // Live preview count using local state (곳간 포함)
  const favSet = window.__cpSeedFavs || [];
  const previewCount = window.MOCK_SEEDS.filter(s =>
    (!ln || s.lang === ln) && (!ct || s.categories.includes(ct)) &&
    (!fv || favSet.includes(s.id))
  ).length;

  const apply = () => { setLang(ln); setCat(ct); if (setFavOnly) setFavOnly(fv); onClose(); };
  const reset = () => { setLn(null); setCt(null); setFv(false); };

  return (
    <div style={{ padding: '4px 20px 20px', paddingBottom: 92 }}>
      {/* 곳간 — 즐겨찾기만 보기 (P03에서만 노출) */}
      {showFav && (
        <div style={{ marginBottom: 22 }}>
          <div style={{ fontSize: 11.5, fontWeight: 700, color: T.ink3,
                        letterSpacing: '0.08em', textTransform: 'uppercase',
                        marginBottom: 10 }}>모음</div>
          <Chip active={fv} onClick={() => setFv(!fv)}>
            <span style={{ display: 'inline-flex', alignItems: 'center', gap: 5 }}>
              <Icons.Star size={13} color={fv ? '#FFF' : T.accent} filled/>
              곳간에 담은 것만
            </span>
          </Chip>
        </div>
      )}

      {/* Languages */}
      <div style={{ marginBottom: 22 }}>
        <div style={{ fontSize: 11.5, fontWeight: 700, color: T.ink3,
                      letterSpacing: '0.08em', textTransform: 'uppercase',
                      marginBottom: 10 }}>언어</div>
        <div style={{ display: 'flex', flexWrap: 'wrap', gap: 6 }}>
          <Chip active={!ln} onClick={() => setLn(null)}>전체</Chip>
          {langs.map(l => (
            <Chip key={l.code} active={ln === l.code}
                  onClick={() => setLn(ln === l.code ? null : l.code)}>
              {l.label}
            </Chip>
          ))}
        </div>
      </div>

      {/* Categories */}
      <div style={{ marginBottom: 6 }}>
        <CategoryChipsField cats={cats} value={ct} onChange={setCt}
                            onManage={onNavigate ? () => { onClose(); onNavigate('P13b'); } : null}/>
      </div>

      {/* Apply bar - sticky */}
      <div style={{
        position: 'absolute', left: 0, right: 0, bottom: 0,
        background: T.bg, borderTop: `0.5px solid ${T.hairline}`,
        padding: '12px 20px 20px',
        display: 'flex', gap: 8, alignItems: 'center',
      }}>
        <button onClick={reset} style={{
          height: 48, padding: '0 16px',
          background: 'transparent', border: 'none',
          color: T.ink2, fontSize: 14, fontWeight: 600,
          fontFamily: 'inherit', cursor: 'pointer',
          letterSpacing: '-0.01em',
        }}>초기화</button>
        <button onClick={apply}
                disabled={previewCount === 0}
                style={{
          flex: 1, height: 48, borderRadius: 14,
          background: previewCount === 0 ? T.ink4 : T.accent,
          color: '#FFF', border: 'none',
          fontFamily: 'inherit', fontSize: 15, fontWeight: 700,
          letterSpacing: '-0.01em',
          cursor: previewCount === 0 ? 'default' : 'pointer',
          opacity: previewCount === 0 ? 0.6 : 1,
        }}>
          {previewCount === 0 ? '결과 없음' : `${previewCount}개 보기`}
        </button>
      </div>
    </div>
  );
}

Object.assign(window, { ScreenHome, FilterIcon, RemovableChip, PlayingWave, FilterSheet, CategoryChipsField });

// ──────────────────────────────────────────────────────────────
// CategoryChipsField — reusable section used in filter sheets.
// Sorts by usage, shows top 6 inline, rest behind "더 보기".
// Pulls live store (window.__cpCats) so adds from P13b / P06 / P05c
// propagate without a refresh. Optional [관리] link in the heading.
// ──────────────────────────────────────────────────────────────
function CategoryChipsField({ cats, value, onChange, onManage }) {
  const T = window.T;
  const live = window.__cpCats || cats || window.MOCK_CATEGORIES;
  const sorted = window.sortCatsByUsage
    ? window.sortCatsByUsage(live)
    : live;
  const TOP = 6;
  const [showMore, setShowMore] = React.useState(false);
  // Always expand if the active filter sits in the tail
  const forceOpen = value && sorted.slice(TOP).some(c => c.id === value);
  const open = showMore || forceOpen;
  const visible = open ? sorted : sorted.slice(0, TOP);
  const hidden = Math.max(0, sorted.length - TOP);

  return (
    <React.Fragment>
      <div style={{
        display: 'flex', alignItems: 'center', justifyContent: 'space-between',
        marginBottom: 10,
      }}>
        <div style={{ fontSize: 11.5, fontWeight: 700, color: T.ink3,
                      letterSpacing: '0.08em', textTransform: 'uppercase' }}>
          카테고리
          {sorted.length > 0 && (
            <span style={{ marginLeft: 6, color: T.ink4, fontWeight: 600,
                           textTransform: 'none', letterSpacing: '-0.005em' }}>
              {sorted.length}개
            </span>
          )}
        </div>
        {onManage && (
          <button onClick={onManage} style={{
            background: 'transparent', border: 'none',
            color: T.accent, fontFamily: 'inherit',
            fontSize: 11.5, fontWeight: 700, cursor: 'pointer', padding: 0,
            letterSpacing: '-0.005em',
            display: 'inline-flex', alignItems: 'center', gap: 3,
          }}>관리 →</button>
        )}
      </div>
      <div style={{ display: 'flex', flexWrap: 'wrap', gap: 6 }}>
        <Chip active={!value} onClick={() => onChange(null)}>전체</Chip>
        {visible.map(c => (
          <Chip key={c.id} active={value === c.id}
                onClick={() => onChange(value === c.id ? null : c.id)}>
            {c.icon
              ? <React.Fragment>{c.icon} {c.label}</React.Fragment>
              : <React.Fragment>
                  <span style={{
                    display: 'inline-block', minWidth: 16, marginRight: 4,
                    fontSize: 10, fontWeight: 800, letterSpacing: '-0.04em',
                    background: value === c.id ? 'rgba(255,255,255,0.22)' : T.accentSoft,
                    color: value === c.id ? '#FFF' : T.accent,
                    borderRadius: 4, padding: '1px 4px',
                  }}>{window.monogramOf(c.label)}</span>
                  {c.label}
                </React.Fragment>}
          </Chip>
        ))}
        {!open && hidden > 0 && (
          <button onClick={() => setShowMore(true)} style={{
            height: 32, padding: '0 12px', borderRadius: 16,
            background: 'transparent', border: `1px dashed ${T.hairlineStrong}`,
            color: T.ink2, fontSize: 12.5, fontWeight: 600,
            fontFamily: 'inherit', cursor: 'pointer',
            display: 'inline-flex', alignItems: 'center', gap: 3,
            letterSpacing: '-0.005em',
          }}>
            <Icons.ChevronD size={11} color={T.ink2}/> {hidden}개 더
          </button>
        )}
        {open && hidden > 0 && !forceOpen && (
          <button onClick={() => setShowMore(false)} style={{
            height: 32, padding: '0 12px', borderRadius: 16,
            background: 'transparent', border: 'none',
            color: T.ink3, fontSize: 12.5, fontWeight: 600,
            fontFamily: 'inherit', cursor: 'pointer',
            display: 'inline-flex', alignItems: 'center', gap: 3,
          }}>
            <Icons.ChevronU size={11} color={T.ink3}/> 접기
          </button>
        )}
      </div>
    </React.Fragment>
  );
}

// Pouch filter sheet — language axis only; mirrors FilterSheet layout
function PouchFilterSheet({ langs, pouches, langFilter, setLang, onClose }) {
  const T = window.T;
  const [ln, setLn] = React.useState(langFilter);
  const previewCount = pouches.filter(p =>
    !ln || (p.langCodes || []).includes(ln)
  ).length;
  const apply = () => { setLang(ln); onClose(); };
  const reset = () => setLn(null);

  return (
    <div style={{ padding: '4px 20px 20px', paddingBottom: 92 }}>
      <div style={{ marginBottom: 22 }}>
        <div style={{ fontSize: 11.5, fontWeight: 700, color: T.ink3,
                      letterSpacing: '0.08em', textTransform: 'uppercase',
                      marginBottom: 10 }}>언어</div>
        <div style={{ display: 'flex', flexWrap: 'wrap', gap: 6 }}>
          <Chip active={!ln} onClick={() => setLn(null)}>전체</Chip>
          {langs.map(l => {
            const n = pouches.filter(p => (p.langCodes || []).includes(l.code)).length;
            if (n === 0) return null;
            return (
              <Chip key={l.code} active={ln === l.code}
                    onClick={() => setLn(ln === l.code ? null : l.code)}>
                {l.label}
              </Chip>
            );
          })}
        </div>
      </div>

      {/* Apply bar - sticky */}
      <div style={{
        position: 'absolute', left: 0, right: 0, bottom: 0,
        background: T.bg, borderTop: `0.5px solid ${T.hairline}`,
        padding: '12px 20px 20px',
        display: 'flex', gap: 8, alignItems: 'center',
      }}>
        <button onClick={reset} style={{
          height: 48, padding: '0 16px',
          background: 'transparent', border: 'none',
          color: T.ink2, fontSize: 14, fontWeight: 600,
          fontFamily: 'inherit', cursor: 'pointer',
          letterSpacing: '-0.01em',
        }}>초기화</button>
        <button onClick={apply}
                disabled={previewCount === 0}
                style={{
          flex: 1, height: 48, borderRadius: 14,
          background: previewCount === 0 ? T.ink4 : T.accent,
          color: '#FFF', border: 'none',
          fontFamily: 'inherit', fontSize: 15, fontWeight: 700,
          letterSpacing: '-0.01em',
          cursor: previewCount === 0 ? 'default' : 'pointer',
          opacity: previewCount === 0 ? 0.6 : 1,
        }}>
          {previewCount === 0 ? '결과 없음' : `${previewCount}개 보기`}
        </button>
      </div>
    </div>
  );
}

Object.assign(window, { PouchFilterSheet });