// home.jsx — Home v2: search-first home with recent strip + collection grid
// Plus drill-down screens: CollectionDetail, SearchScreen
// Also expands the sample data to ~45 cards across 7 groups.

// ─────────────────────────────────────────────────────────────
// Theme tokens (kept local to avoid load-order issues)
// ─────────────────────────────────────────────────────────────
const HT = window.__cpMakeTheme();

const LANG_DOT = {
  ja: '#8B6F47',
  es: '#C97B5C',
  en: '#4A6B4F',
  fr: '#8B5C7A',
  th: '#C9A86A',
  zh: '#7A6A4A',
};

const LANG_FLAG = {
  ja: '🇯🇵',
  es: '🇪🇸',
  en: '🇺🇸',
  fr: '🇫🇷',
  th: '🇹🇭',
  zh: '🇨🇳',
  de: '🇩🇪',
  vi: '🇻🇳',
};

function langFlag(lang) {
  return LANG_FLAG[lang] || '🌐';
}

const langFontFor = (lang) => {
  if (lang === 'ja') return '"Noto Sans JP", Pretendard';
  return 'Noto Sans, Pretendard';
};

// ─────────────────────────────────────────────────────────────
// Expanded sample data — 7 groups, ~45 cards
// ─────────────────────────────────────────────────────────────
const EXPANDED_CARDS = [
  // 도쿄 식당 표현 (8)
  { id:'tk_r1', lang:'ja', langLabel:'일본어', foreign:'おすすめのメニューは何ですか？', pronunciation:'오스스메노 메뉴와 난데스카?', korean:'추천 메뉴가 뭐예요?', category:'식당/카페', group:'도쿄 식당 표현', addedDays:5 },
  { id:'tk_r2', lang:'ja', langLabel:'일본어', foreign:'水をください。', pronunciation:'미즈오 쿠다사이.', korean:'물 좀 주세요.', category:'식당/카페', group:'도쿄 식당 표현', addedDays:5 },
  { id:'tk_r3', lang:'ja', langLabel:'일본어', foreign:'お会計お願いします。', pronunciation:'오카이케- 오네가이시마스.', korean:'계산해 주세요.', category:'식당/카페', group:'도쿄 식당 표현', addedDays:5 },
  { id:'tk_r4', lang:'ja', langLabel:'일본어', foreign:'これは何ですか？', pronunciation:'코레와 난데스카?', korean:'이건 뭐예요?', category:'식당/카페', group:'도쿄 식당 표현', addedDays:4 },
  { id:'tk_r5', lang:'ja', langLabel:'일본어', foreign:'辛くしないでください。', pronunciation:'카라쿠 시나이데 쿠다사이.', korean:'안 맵게 해주세요.', category:'식당/카페', group:'도쿄 식당 표현', addedDays:4 },
  { id:'tk_r6', lang:'ja', langLabel:'일본어', foreign:'おすすめのお酒は？', pronunciation:'오스스메노 오사케와?', korean:'추천 술이 뭐예요?', category:'식당/카페', group:'도쿄 식당 표현', addedDays:3 },
  { id:'tk_r7', lang:'ja', langLabel:'일본어', foreign:'お持ち帰りできますか？', pronunciation:'오모치카에리 데키마스카?', korean:'포장 되나요?', category:'식당/카페', group:'도쿄 식당 표현', addedDays:3 },
  { id:'tk_r8', lang:'ja', langLabel:'일본어', foreign:'もう一杯ください。', pronunciation:'모- 잇파이 쿠다사이.', korean:'한 잔 더 주세요.', category:'식당/카페', group:'도쿄 식당 표현', addedDays:2 },

  // 도쿄 길찾기 (6)
  { id:'tk_w1', lang:'ja', langLabel:'일본어', foreign:'駅までどう行きますか？', pronunciation:'에키마데 도- 이키마스카?', korean:'역까지 어떻게 가나요?', category:'교통/길찾기', group:'도쿄 길찾기', addedDays:6 },
  { id:'tk_w2', lang:'ja', langLabel:'일본어', foreign:'渋谷駅で乗り換えですか？', pronunciation:'시부야에키데 노리카에데스카?', korean:'시부야역에서 환승인가요?', category:'교통/길찾기', group:'도쿄 길찾기', addedDays:6 },
  { id:'tk_w3', lang:'ja', langLabel:'일본어', foreign:'地図を見せてください。', pronunciation:'치즈오 미세테 쿠다사이.', korean:'지도를 보여주세요.', category:'교통/길찾기', group:'도쿄 길찾기', addedDays:6 },
  { id:'tk_w4', lang:'ja', langLabel:'일본어', foreign:'歩いてどのくらいですか？', pronunciation:'아루이테 도노쿠라이데스카?', korean:'걸어서 얼마나 걸려요?', category:'교통/길찾기', group:'도쿄 길찾기', addedDays:5 },
  { id:'tk_w5', lang:'ja', langLabel:'일본어', foreign:'タクシーを呼んでください。', pronunciation:'타쿠시-오 욘데 쿠다사이.', korean:'택시 좀 불러주세요.', category:'교통/길찾기', group:'도쿄 길찾기', addedDays:5 },
  { id:'tk_w6', lang:'ja', langLabel:'일본어', foreign:'ここで降ります。', pronunciation:'코코데 오리마스.', korean:'여기서 내릴게요.', category:'교통/길찾기', group:'도쿄 길찾기', addedDays:4 },

  // 바르셀로나 휴가 (9)
  { id:'bcn1', lang:'es', langLabel:'스페인어', foreign:'¿Dónde está el baño?', pronunciation:'돈데 에스따 엘 바뇨?', korean:'화장실이 어디예요?', category:'교통/길찾기', group:'바르셀로나 휴가', addedDays:14 },
  { id:'bcn2', lang:'es', langLabel:'스페인어', foreign:'La cuenta, por favor.', pronunciation:'라 꾸엔따, 뽀르 파보르.', korean:'계산서 주세요.', category:'식당/카페', group:'바르셀로나 휴가', addedDays:14 },
  { id:'bcn3', lang:'es', langLabel:'스페인어', foreign:'¿Tienen mesa para dos?', pronunciation:'띠에넨 메사 빠라 도스?', korean:'2명 자리 있어요?', category:'식당/카페', group:'바르셀로나 휴가', addedDays:13 },
  { id:'bcn4', lang:'es', langLabel:'스페인어', foreign:'Una caña, por favor.', pronunciation:'우나 까냐, 뽀르 파보르.', korean:'생맥주 한 잔 주세요.', category:'식당/카페', group:'바르셀로나 휴가', addedDays:13 },
  { id:'bcn5', lang:'es', langLabel:'스페인어', foreign:'¿Cuánto cuesta?', pronunciation:'꾸안또 꾸에스따?', korean:'얼마예요?', category:'쇼핑', group:'바르셀로나 휴가', addedDays:12 },
  { id:'bcn6', lang:'es', langLabel:'스페인어', foreign:'Está delicioso.', pronunciation:'에스따 델리시오소.', korean:'정말 맛있어요.', category:'식당/카페', group:'바르셀로나 휴가', addedDays:12 },
  { id:'bcn7', lang:'es', langLabel:'스페인어', foreign:'No hablo español.', pronunciation:'노 아블로 에스빠뇰.', korean:'스페인어 못해요.', category:'인사/사교', group:'바르셀로나 휴가', addedDays:11 },
  { id:'bcn8', lang:'es', langLabel:'스페인어', foreign:'¿A qué hora abren?', pronunciation:'아 께 오라 아브렌?', korean:'몇 시에 열어요?', category:'관광/구경', group:'바르셀로나 휴가', addedDays:11 },
  { id:'bcn9', lang:'es', langLabel:'스페인어', foreign:'Por favor, despacio.', pronunciation:'뽀르 파보르, 데스파시오.', korean:'천천히 말해 주세요.', category:'인사/사교', group:'바르셀로나 휴가', addedDays:10 },

  // 뉴욕 호텔 (5)
  { id:'ny1', lang:'en', langLabel:'영어', foreign:'Could I get an extra towel, please?', pronunciation:'쿠드 아이 겟 언 엑스트라 타올, 플리즈?', korean:'수건 하나만 더 주실 수 있을까요?', category:'숙소', group:'뉴욕 호텔', addedDays:30 },
  { id:'ny2', lang:'en', langLabel:'영어', foreign:"The Wi-Fi isn't working in my room.", pronunciation:'더 와이파이 이즌트 워킹 인 마이 룸.', korean:'방에 와이파이가 안 돼요.', category:'숙소', group:'뉴욕 호텔', addedDays:30 },
  { id:'ny3', lang:'en', langLabel:'영어', foreign:'Can I check out later?', pronunciation:'캔 아이 체크 아웃 레이터?', korean:'늦게 체크아웃 해도 될까요?', category:'숙소', group:'뉴욕 호텔', addedDays:29 },
  { id:'ny4', lang:'en', langLabel:'영어', foreign:"What's the breakfast time?", pronunciation:'왓츠 더 브렉퍼스트 타임?', korean:'조식은 몇 시인가요?', category:'숙소', group:'뉴욕 호텔', addedDays:29 },
  { id:'ny5', lang:'en', langLabel:'영어', foreign:'Could you call me a taxi?', pronunciation:'쿠드 유 콜 미 어 택시?', korean:'택시 좀 불러주실래요?', category:'교통/길찾기', group:'뉴욕 호텔', addedDays:28 },

  // 파리 카페 (6)
  { id:'par1', lang:'fr', langLabel:'프랑스어', foreign:"Un café, s'il vous plaît.", pronunciation:'앙 카페, 실 부 쁠레.', korean:'커피 한 잔 주세요.', category:'식당/카페', group:'파리 카페', addedDays:45 },
  { id:'par2', lang:'fr', langLabel:'프랑스어', foreign:"L'addition, s'il vous plaît.", pronunciation:'라디시옹, 실 부 쁠레.', korean:'계산서 부탁드려요.', category:'식당/카페', group:'파리 카페', addedDays:45 },
  { id:'par3', lang:'fr', langLabel:'프랑스어', foreign:'Quel est le plat du jour?', pronunciation:'껠 에 르 쁠라 뒤 주르?', korean:'오늘의 요리는 뭐예요?', category:'식당/카페', group:'파리 카페', addedDays:44 },
  { id:'par4', lang:'fr', langLabel:'프랑스어', foreign:"Pour emporter, s'il vous plaît.", pronunciation:'뿌르 앙뽀르떼, 실 부 쁠레.', korean:'포장해 주세요.', category:'식당/카페', group:'파리 카페', addedDays:44 },
  { id:'par5', lang:'fr', langLabel:'프랑스어', foreign:'Merci beaucoup.', pronunciation:'메르시 보꾸.', korean:'정말 감사합니다.', category:'인사/사교', group:'파리 카페', addedDays:43 },
  { id:'par6', lang:'fr', langLabel:'프랑스어', foreign:'Excusez-moi, où est…?', pronunciation:'엑스큐제 무아, 우 에…?', korean:'실례지만, …는 어디예요?', category:'교통/길찾기', group:'파리 카페', addedDays:43 },

  // 일상 회화 (5)
  { id:'da1', lang:'ja', langLabel:'일본어', foreign:'初めまして。', pronunciation:'하지메마시테.', korean:'처음 뵙겠습니다.', category:'인사/사교', group:'일상 회화', addedDays:60 },
  { id:'da2', lang:'ja', langLabel:'일본어', foreign:'もう一度お願いします。', pronunciation:'모- 이치도 오네가이시마스.', korean:'다시 한 번 부탁해요.', category:'인사/사교', group:'일상 회화', addedDays:58 },
  { id:'da3', lang:'ja', langLabel:'일본어', foreign:'今、何時ですか？', pronunciation:'이마, 난지데스카?', korean:'지금 몇 시예요?', category:'일상/기타', group:'일상 회화', addedDays:55 },
  { id:'da4', lang:'ja', langLabel:'일본어', foreign:'写真を撮ってもいいですか？', pronunciation:'샤신오 톳테모 이이데스카?', korean:'사진 찍어도 될까요?', category:'관광/구경', group:'일상 회화', addedDays:50 },
  { id:'da5', lang:'ja', langLabel:'일본어', foreign:'Wi-Fiのパスワードを教えてください。', pronunciation:'와이파이노 파스와-도오 오시에테 쿠다사이.', korean:'와이파이 비밀번호 알려주세요.', category:'일상/기타', group:'일상 회화', addedDays:48 },

  // 응급 표현 (4, mixed langs)
  { id:'em1', lang:'en', langLabel:'영어', foreign:'I need a doctor.', pronunciation:'아이 니드 어 닥터.', korean:'의사가 필요해요.', category:'응급/의료', group:'응급 표현', addedDays:90 },
  { id:'em2', lang:'ja', langLabel:'일본어', foreign:'救急車を呼んでください。', pronunciation:'큐큐샤오 욘데 쿠다사이.', korean:'구급차 좀 불러주세요.', category:'응급/의료', group:'응급 표현', addedDays:90 },
  { id:'em3', lang:'es', langLabel:'스페인어', foreign:'¿Dónde está el hospital?', pronunciation:'돈데 에스따 엘 오스삐딸?', korean:'병원이 어디예요?', category:'응급/의료', group:'응급 표현', addedDays:90 },
  { id:'em4', lang:'en', langLabel:'영어', foreign:'I lost my passport.', pronunciation:'아이 로스트 마이 패스포트.', korean:'여권을 잃어버렸어요.', category:'응급/의료', group:'응급 표현', addedDays:85 },
];

// Recent IDs (simulated: cards user has viewed lately)
const RECENT_IDS = ['tk_r1', 'bcn4', 'par3', 'tk_r8', 'ny2', 'da4'];

// Derive groups from current cards + user-created (custom) collection stubs.
function deriveGroups(cards, extras) {
  const map = new Map();
  for (const e of (extras || [])) {
    map.set(e.name, {
      name: e.name, color: e.color, isCustom: true,
      cover: e.cover || { kind: 'auto' },
      cards: [], langs: new Set(), addedDaysList: [],
    });
  }
  for (const c of (cards || [])) {
    // A card can belong to multiple collections via card.group (primary)
    // and card.extraGroups (additional). Include in all.
    const memberships = [c.group, ...(c.extraGroups || [])].filter(Boolean);
    for (const groupName of memberships) {
      if (!map.has(groupName)) {
        map.set(groupName, {
          name: groupName,
          color: LANG_DOT[c.lang] || HT.ink2,
          isCustom: false,
          cover: { kind: 'auto' },
          cards: [], langs: new Set(), addedDaysList: [],
        });
      }
      const g = map.get(groupName);
      g.cards.push(c);
      g.langs.add(c.lang);
      g.addedDaysList.push(c.addedDays);
    }
  }
  return [...map.values()].map(g => ({
    name: g.name,
    color: g.color || (g.cards[0] && LANG_DOT[g.cards[0].lang]) || HT.ink2,
    cover: g.cover || { kind: 'auto' },
    isCustom: g.isCustom,
    cards: g.cards,
    count: g.cards.length,
    primaryLang: g.cards[0]?.lang,
    primaryLangLabel: g.cards[0]?.langLabel,
    isMultiLang: g.langs.size > 1,
    latestDays: g.addedDaysList.length ? Math.min(...g.addedDaysList) : null,
  }));
}

const GROUPS = deriveGroups(EXPANDED_CARDS, []);

// Attach dialogues (loaded from dialogues.jsx) to each card
if (typeof window !== 'undefined' && window.CARD_DIALOGUES) {
  for (const c of EXPANDED_CARDS) {
    if (window.CARD_DIALOGUES[c.id]) c.dialogue = window.CARD_DIALOGUES[c.id];
  }
}

const daysAgo = (n) => {
  if (n === 0) return '오늘';
  if (n === 1) return '어제';
  if (n < 7) return `${n}일 전`;
  if (n < 30) return `${Math.floor(n / 7)}주 전`;
  return `${Math.floor(n / 30)}개월 전`;
};

// ─────────────────────────────────────────────────────────────
// HomeV2
// ─────────────────────────────────────────────────────────────
function HomeV2({ accent, dailyCount, onAdd, onOpenCard, onOpenGroup, onOpenAll, onOpenSearch, onOpenSettings, onCreateCollection, onUpgradeQuota, cards, groups, pinnedSet, quotaUsed, quotaTotal, profile, receivedCollections }) {
  const allCards = cards || EXPANDED_CARDS;
  const allGroups = groups || GROUPS;
  const totalCards = allCards.length;
  const recentCards = RECENT_IDS.map(id => allCards.find(c => c.id === id)).filter(Boolean);
  const pinned = pinnedSet || new Set();
  const [sortBy, setSortBy] = React.useState('activity'); // activity | name | count
  const [compact, setCompact] = React.useState(false);
  const qUsed = quotaUsed != null ? quotaUsed : 7;
  const qTotal = quotaTotal != null ? quotaTotal : 30;

  const sortFn = (a, b) => {
    if (sortBy === 'name') return a.name.localeCompare(b.name, 'ko');
    if (sortBy === 'count') return b.count - a.count;
    // activity: latestDays asc, empty groups last
    const la = a.latestDays ?? 9999;
    const lb = b.latestDays ?? 9999;
    return la - lb;
  };

  const pinnedGroups = allGroups.filter(g => pinned.has(g.name)).sort(sortFn);
  const otherGroups = allGroups.filter(g => !pinned.has(g.name)).sort(sortFn);

  return (
    <div style={{ paddingBottom: 110 }}>
      {/* Top bar — sticky */}
      <div style={{
        position: 'sticky', top: 0, zIndex: 30,
        background: HT.bg,
        padding: '54px 22px 12px',
        display: 'flex', alignItems: 'center', justifyContent: 'space-between',
        minHeight: 44,
      }}>
        <div style={{
          fontSize: 24, fontWeight: 700, letterSpacing: '-0.03em',
          color: HT.ink,
        }}>CheekPouch</div>
        <div style={{ display: 'flex', alignItems: 'center', gap: 4 }}>
          <button onClick={onOpenSettings} style={{
            width: 38, height: 38, borderRadius: 999,
            border: 'none', background: 'transparent',
            display: 'inline-flex', alignItems: 'center', justifyContent: 'center',
            cursor: 'pointer', padding: 0,
            WebkitTapHighlightColor: 'transparent',
          }}>
            {window.HamsterAvatar
              ? <window.HamsterAvatar size={30} accent={accent} count={3}
                                       face={profile?.face || 'basic'} />
              : <span style={{
                  width: 28, height: 28, borderRadius: 28,
                  background: `linear-gradient(135deg, ${accent} 0%, ${accent}cc 100%)`,
                }} />}
          </button>
          <button onClick={onAdd} style={{
            width: 38, height: 38, borderRadius: 999,
            border: 'none', background: 'transparent',
            display: 'inline-flex', alignItems: 'center', justifyContent: 'center',
            cursor: 'pointer', padding: 0,
            WebkitTapHighlightColor: 'transparent',
          }}>
            <svg width="22" height="22" viewBox="0 0 22 22" fill="none">
              <path d="M11 4v14M4 11h14" stroke={HT.ink} strokeWidth="1.8" strokeLinecap="round"/>
            </svg>
          </button>
        </div>
      </div>

      {/* Search bar */}
      <div style={{ padding: '14px 22px 0' }}>
        <button onClick={onOpenSearch} style={{
          width: '100%', height: 46, borderRadius: 14,
          background: HT.surface,
          border: `0.5px solid ${HT.hairline}`,
          display: 'flex', alignItems: 'center', gap: 10,
          padding: '0 16px',
          cursor: 'pointer',
          fontFamily: 'inherit',
          textAlign: 'left',
          WebkitTapHighlightColor: 'transparent',
        }}>
          <svg width="18" height="18" viewBox="0 0 20 20" fill="none">
            <circle cx="9" cy="9" r="6.2" stroke={HT.ink3} strokeWidth="1.6"/>
            <path d="M13.6 13.6L17 17" stroke={HT.ink3} strokeWidth="1.6" strokeLinecap="round"/>
          </svg>
          <span style={{
            fontSize: 15, color: HT.ink3, letterSpacing: '-0.01em',
            flex: 1,
          }}>표현, 단어, 주머니 검색</span>
          <span style={{
            fontSize: 11, color: HT.ink4,
            fontFamily: 'ui-monospace, SFMono-Regular, monospace',
            border: `0.5px solid ${HT.hairlineStrong}`,
            borderRadius: 4, padding: '2px 6px',
          }}>⌘ K</span>
        </button>
      </div>

      {/* Recent strip */}
      <SectionHeader title="최근에 본 씨앗" />
      <div style={{
        display: 'flex', gap: 10, padding: '0 22px',
        overflowX: 'auto', overflowY: 'hidden',
        scrollbarWidth: 'none',
        paddingBottom: 4,
      }}>
        {recentCards.map((c, i) => (
          <RecentMiniCard key={c.id} card={c} accent={accent} onClick={() => onOpenCard(c)} />
        ))}
      </div>

      {/* My collections */}
      <SectionHeader title="내 주머니" sub={`${allGroups.length}개`}
        action={
          <div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
            <button onClick={onOpenAll} style={{
              height: 28, padding: '0 10px', borderRadius: 8,
              background: 'transparent', border: 'none',
              display: 'inline-flex', alignItems: 'center', gap: 3,
              fontFamily: 'inherit', fontSize: 12, fontWeight: 600,
              color: accent, letterSpacing: '-0.01em',
              cursor: 'pointer',
              WebkitTapHighlightColor: 'transparent',
            }}>
              전체 {totalCards}개
              <svg width="7" height="11" viewBox="0 0 8 14" fill="none">
                <path d="M1.5 1.5L6.5 7L1.5 12.5" stroke={accent} strokeWidth="1.8"
                      strokeLinecap="round" strokeLinejoin="round"/>
              </svg>
            </button>
            <div style={{ width: 1, height: 14, background: HT.hairlineStrong }} />
            <SortMenu sortBy={sortBy} onChange={setSortBy} />
            <ViewToggle compact={compact} onChange={setCompact} />
          </div>
        }
      />

      {/* Pinned section */}
      {pinnedGroups.length > 0 && (
        <>
          <div style={{
            padding: '0 22px 8px',
            display: 'flex', alignItems: 'center', gap: 6,
            fontSize: 11, fontWeight: 600, color: HT.ink3,
            letterSpacing: '0.06em', textTransform: 'uppercase',
          }}>
            <window.PinGlyph size={10} color={HT.ink3} />
            <span>고정됨</span>
          </div>
          {renderCollectionItems(pinnedGroups, compact, accent, pinned, onOpenGroup)}
          <div style={{
            margin: '14px 22px 4px',
            height: 0.5, background: HT.hairline,
          }} />
        </>
      )}

      {compact ? (
        <div style={{
          padding: '0 22px',
          display: 'flex', flexDirection: 'column', gap: 8,
        }}>
          {otherGroups.map(g => (
            <window.CollectionListRow key={g.name} group={g} accent={accent}
                                       pinned={pinned.has(g.name)}
                                       onClick={() => onOpenGroup(g)} />
          ))}
          {onCreateCollection && (
            <button onClick={onCreateCollection} style={{
              width: '100%', padding: '14px 12px',
              background: 'transparent',
              border: `1.5px dashed ${HT.hairlineStrong}`,
              borderRadius: 14,
              display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 8,
              fontFamily: 'inherit', fontSize: 13.5, fontWeight: 600,
              color: accent, cursor: 'pointer',
              WebkitTapHighlightColor: 'transparent',
            }}>
              <svg width="14" height="14" viewBox="0 0 22 22" fill="none">
                <path d="M11 4v14M4 11h14" stroke={accent} strokeWidth="2" strokeLinecap="round"/>
              </svg>
              새 주머니
            </button>
          )}
        </div>
      ) : (
        <div style={{
          padding: '0 22px',
          display: 'grid',
          gridTemplateColumns: '1fr 1fr',
          gap: 10,
        }}>
          {otherGroups.map(g => (
            <window.CollectionTileCard key={g.name} group={g} accent={accent}
                                       pinned={pinned.has(g.name)}
                                       onClick={() => onOpenGroup(g)} />
          ))}
          {onCreateCollection && (
            <AddCollectionTile onClick={onCreateCollection} accent={accent} />
          )}
        </div>
      )}

      {/* All cards link removed — moved into the 주머니 section header trailing */}

      {/* Shared collections from friends */}
      {(() => {
        const sharedList = receivedCollections != null
          ? receivedCollections
          : (window.RECEIVED_COLLECTIONS || []);
        if (sharedList.length === 0) return null;
        return (
        <>
          <SectionHeader title="공유받은 주머니" sub={`${sharedList.length}개`} />
          {compact ? (
            <div style={{
              padding: '0 22px',
              display: 'flex', flexDirection: 'column', gap: 8,
            }}>
              {sharedList.map(r => (
                <SharedCollectionListRow key={r.id} item={r} accent={accent}
                                          onClick={() => onOpenGroup && onOpenGroup({
                                            name: r.name, color: r.color, cover: r.cover,
                                            cards: r.cards || [], count: r.count,
                                            primaryLang: r.primaryLang,
                                            primaryLangLabel: r.primaryLangLabel || '여러 언어',
                                            isMultiLang: r.isMultiLang || false,
                                            isShared: true, sharedFrom: r.from, sharedFromFace: r.fromFace,
                                            sharedFromAccent: r.fromAccent,
                                            latestDays: r.receivedDays,
                                          })} />
              ))}
            </div>
          ) : (
            <div style={{
              padding: '0 22px',
              display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 10,
            }}>
              {sharedList.map(r => (
                <SharedCollectionTile key={r.id} item={r} accent={accent}
                                       onClick={() => onOpenGroup && onOpenGroup({
                                         name: r.name, color: r.color, cover: r.cover,
                                         cards: r.cards || [], count: r.count,
                                         primaryLang: r.primaryLang,
                                         primaryLangLabel: r.primaryLangLabel || '여러 언어',
                                         isMultiLang: r.isMultiLang || false,
                                         isShared: true, sharedFrom: r.from, sharedFromFace: r.fromFace,
                                         sharedFromAccent: r.fromAccent,
                                         latestDays: r.receivedDays,
                                       })} />
              ))}
            </div>
          )}
        </>
        );
      })()}
    </div>
  );
}

function SharedCollectionListRow({ item, accent, onClick }) {
  const dot = item.color || '#8B6F47';
  return (
    <button onClick={onClick} style={{
      width: '100%',
      background: HT.surface,
      border: `0.5px solid ${HT.hairline}`,
      borderRadius: 14,
      padding: 6,
      cursor: 'pointer',
      fontFamily: 'inherit',
      textAlign: 'left',
      display: 'flex', alignItems: 'stretch', gap: 12,
      WebkitTapHighlightColor: 'transparent',
      overflow: 'hidden',
    }}>
      {/* Cover block matching tile aesthetic */}
      <div style={{
        position: 'relative',
        width: 64, height: 64, borderRadius: 10,
        overflow: 'hidden', flexShrink: 0,
        background: `linear-gradient(155deg, ${dot} 0%, ${dot}cc 100%)`,
        display: 'flex', alignItems: 'center', justifyContent: 'center',
      }}>
        {window.CoverIcon &&
          <window.CoverIcon name={(item.cover && item.cover.icon) || 'star'} size={28} color="#FFF" />}
      </div>

      {/* Content */}
      <div style={{ flex: 1, minWidth: 0,
        display: 'flex', flexDirection: 'column', justifyContent: 'center',
        paddingTop: 2, paddingBottom: 2, paddingRight: 4,
      }}>
        <div style={{
          display: 'flex', alignItems: 'center', gap: 6, minWidth: 0,
        }}>
          <span style={{
            fontSize: 15.5, fontWeight: 700, color: HT.ink,
            letterSpacing: '-0.02em',
            overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap',
            flex: 1, minWidth: 0,
          }}>{item.name}</span>
        </div>

        {/* From-badge row */}
        <div style={{
          marginTop: 5,
          display: 'flex', alignItems: 'center', gap: 5,
          minWidth: 0,
        }}>
          {window.HamsterAvatar &&
            <window.HamsterAvatar size={16} accent={item.fromAccent}
                                  count={3} face={item.fromFace} />}
          <span style={{
            fontSize: 11.5, color: HT.ink2,
            letterSpacing: '-0.01em', fontWeight: 600,
            overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap',
            flex: 1, minWidth: 0,
          }}>{item.from}님이 공유</span>
          <span style={{
            fontSize: 11, color: HT.ink3,
            letterSpacing: '-0.01em', whiteSpace: 'nowrap', flexShrink: 0,
          }}>{item.count}개</span>
        </div>
      </div>

      {/* Chevron */}
      <div style={{
        flexShrink: 0,
        display: 'inline-flex', alignItems: 'center', paddingRight: 6,
      }}>
        <svg width="8" height="14" viewBox="0 0 8 14" fill="none">
          <path d="M1.5 1.5L6.5 7L1.5 12.5" stroke={HT.ink3} strokeWidth="1.6"
                strokeLinecap="round" strokeLinejoin="round"/>
        </svg>
      </div>
    </button>
  );
}

function SharedCollectionTile({ item, accent, onClick }) {
  return (
    <button onClick={onClick} style={{
      position: 'relative',
      padding: 0,
      borderRadius: 16,
      cursor: 'pointer',
      fontFamily: 'inherit',
      textAlign: 'left',
      WebkitTapHighlightColor: 'transparent',
      overflow: 'hidden',
      border: 'none',
      background: `linear-gradient(155deg, ${item.color} 0%, ${item.color}cc 100%)`,
      minHeight: 184,
      display: 'flex', flexDirection: 'column',
      boxShadow: '0 1px 0 rgba(255,255,255,0.16) inset',
    }}>
      {/* Pouch watermark — same hamster motif as regular tiles */}
      {window.PouchShape && (
        <div style={{
          position: 'absolute',
          top: -10, right: -18,
          opacity: 0.15,
          pointerEvents: 'none', userSelect: 'none',
          transform: 'rotate(8deg)',
        }}>
          <window.PouchShape size={110} fill="#FFFFFF" cord="rgba(255,255,255,0.6)"
                             shade="rgba(255,255,255,0.4)" highlight="rgba(255,255,255,0.9)"
                             seeds={3}
                             seedFill="rgba(255,255,255,0.85)" />
        </div>
      )}
      {/* Top: from-badge + count */}
      <div style={{
        position: 'relative', zIndex: 1,
        padding: '12px 14px',
        display: 'flex', alignItems: 'center', justifyContent: 'space-between',
      }}>
        <div style={{
          height: 22, padding: '0 8px 0 6px', borderRadius: 999,
          background: 'rgba(0,0,0,0.22)',
          display: 'inline-flex', alignItems: 'center', gap: 5,
          fontSize: 11, fontWeight: 600, color: '#FFF',
          letterSpacing: '-0.01em',
          maxWidth: '70%',
        }}>
          {window.HamsterAvatar &&
            <window.HamsterAvatar size={16} accent={item.fromAccent}
                                  count={3} face={item.fromFace} />}
          <span style={{
            overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap',
          }}>{item.from}</span>
        </div>
        <div style={{
          height: 22, padding: '0 10px 0 7px', borderRadius: 999,
          background: 'rgba(0,0,0,0.22)',
          display: 'inline-flex', alignItems: 'center', gap: 5,
          fontSize: 11.5, fontWeight: 600, color: '#FFF',
          letterSpacing: '-0.01em',
          backdropFilter: 'blur(8px)',
        }}>
          {window.SeedShape && (
            <window.SeedShape size={9} fill="#FFFFFF"
                              highlight="rgba(255,255,255,0.45)" />
          )}
          <span style={{ fontVariantNumeric: 'tabular-nums' }}>{item.count}</span>
          <span style={{ opacity: 0.7, fontSize: 10 }}>개</span>
        </div>
      </div>

      {/* Middle: icon */}
      <div style={{
        position: 'relative', zIndex: 1,
        flex: 1,
        display: 'flex', alignItems: 'center', justifyContent: 'center',
      }}>
        {window.CoverIcon &&
          <window.CoverIcon name={(item.cover && item.cover.icon) || 'star'} size={42} color="#FFF" />}
      </div>

      {/* Bottom */}
      <div style={{
        position: 'relative', zIndex: 1,
        padding: '0 14px 14px',
      }}>
        <div style={{
          width: 18, height: 1.5, borderRadius: 1,
          background: 'rgba(255,255,255,0.45)',
          marginBottom: 8,
        }} />
        <div style={{
          fontSize: 16, fontWeight: 700, color: '#FFF',
          letterSpacing: '-0.02em', lineHeight: 1.2,
          display: '-webkit-box',
          WebkitLineClamp: 2, WebkitBoxOrient: 'vertical',
          overflow: 'hidden',
        }}>{item.name}</div>
        <div style={{
          marginTop: 4,
          fontSize: 11, color: 'rgba(255,255,255,0.74)',
          letterSpacing: '-0.01em',
        }}>{item.from}님이 공유</div>
      </div>
    </button>
  );
}

// Helpers used by HomeV2
function QuotaHero({ used, total, accent, onUpgrade }) {
  const remaining = Math.max(0, total - used);
  const ratio = Math.min(1, used / total);
  const isFull = remaining === 0;
  const isLow = remaining > 0 && remaining <= 5;

  const headlineMsg = isFull ? '더 이상 담을 자리가 없어요'
                   : isLow  ? '자리가 얼마 남지 않았어요'
                   : `${remaining}개 더 담을 수 있어요`;

  const dangerColor = '#B5483C';
  const barColor = isFull ? dangerColor : isLow ? '#D69A3A' : accent;
  const numberColor = isFull ? dangerColor : HT.ink;

  return (
    <div style={{
      margin: '16px 22px 0',
      padding: '16px 18px',
      background: HT.surface,
      borderRadius: 20,
      border: `0.5px solid ${HT.hairline}`,
      position: 'relative',
      overflow: 'hidden',
    }}>
      {/* Decorative soft pouch silhouette in background */}
      <div style={{
        position: 'absolute',
        top: -30, right: -30,
        width: 160, height: 160,
        borderRadius: 160,
        background: `radial-gradient(circle at 30% 30%, ${accent}10 0%, transparent 65%)`,
        pointerEvents: 'none',
      }} />

      <div style={{
        position: 'relative',
        display: 'flex', alignItems: 'center', gap: 14,
      }}>
        {/* Hamster character */}
        <div style={{ flexShrink: 0 }}>
          <CheekPouch count={used} max={total} accent={accent} size={86}
                      animateOnChange={true}
                      mood={isFull ? 'full' : 'happy'} />
        </div>

        {/* Info */}
        <div style={{ flex: 1, minWidth: 0 }}>
          <div style={{
            display: 'flex', alignItems: 'baseline', gap: 5,
            color: numberColor,
          }}>
            <span style={{
              fontSize: 30, fontWeight: 800,
              letterSpacing: '-0.04em', lineHeight: 1,
              fontVariantNumeric: 'tabular-nums',
            }}>{remaining}</span>
            <span style={{
              fontSize: 14, fontWeight: 600, color: HT.ink2,
              letterSpacing: '-0.01em', whiteSpace: 'nowrap',
            }}>개</span>
            <span style={{
              marginLeft: 6,
              fontSize: 13, color: HT.ink2,
              letterSpacing: '-0.01em',
              fontWeight: 500, whiteSpace: 'nowrap',
            }}>{isFull ? '자리 없음' : '남음'}</span>
          </div>

          {/* Progress bar */}
          <div style={{
            marginTop: 10,
            height: 6, borderRadius: 99,
            background: 'rgba(60,42,20,0.08)',
            overflow: 'hidden',
            position: 'relative',
          }}>
            <div style={{
              height: '100%', width: `${ratio * 100}%`,
              background: barColor,
              borderRadius: 99,
              transition: 'width 0.4s cubic-bezier(0.22, 1, 0.36, 1), background 0.3s',
            }} />
          </div>

          {/* Sub-meta */}
          <div style={{
            marginTop: 8,
            display: 'flex', alignItems: 'center', justifyContent: 'space-between',
            gap: 8,
          }}>
            <div style={{
              fontSize: 11.5, color: HT.ink3, letterSpacing: '-0.01em',
              fontVariantNumeric: 'tabular-nums',
              whiteSpace: 'nowrap',
            }}>
              <span style={{ fontWeight: 600, color: HT.ink2 }}>{used}개</span>
              <span> / </span>
              <span>{total}개 한도</span>
            </div>
            <button onClick={onUpgrade} style={{
              height: 24, padding: '0 10px', borderRadius: 999,
              background: isFull ? barColor : (isLow ? `${barColor}1F` : 'transparent'),
              color: isFull ? '#FFF' : (isLow ? barColor : accent),
              border: isFull ? 'none' : (isLow ? 'none' : `0.5px solid ${accent}40`),
              fontFamily: 'inherit', fontSize: 11.5, fontWeight: 600,
              letterSpacing: '-0.01em', whiteSpace: 'nowrap',
              display: 'inline-flex', alignItems: 'center', gap: 3,
              cursor: 'pointer', flexShrink: 0,
              WebkitTapHighlightColor: 'transparent',
            }}>
              {isFull ? '한도 늘리기' : isLow ? '자리 더 만들기' : '한도 늘리기'}
              <svg width="7" height="11" viewBox="0 0 8 14" fill="none">
                <path d="M1.5 1.5L6.5 7L1.5 12.5"
                      stroke={isFull ? '#FFF' : (isLow ? barColor : accent)}
                      strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round"/>
              </svg>
            </button>
          </div>

          {/* Headline tagline below — only for low/full states for urgency */}
          {(isLow || isFull) && (
            <div style={{
              marginTop: 8,
              fontSize: 12, color: numberColor,
              letterSpacing: '-0.01em', fontWeight: 600,
              lineHeight: 1.4,
            }}>{headlineMsg}</div>
          )}
        </div>
      </div>
    </div>
  );
}

function renderCollectionItems(list, compact, accent, pinned, onOpenGroup) {
  if (compact) {
    return (
      <div style={{
        padding: '0 22px',
        display: 'flex', flexDirection: 'column', gap: 8,
      }}>
        {list.map(g => (
          <window.CollectionListRow key={g.name} group={g} accent={accent}
                                     pinned={pinned.has(g.name)}
                                     onClick={() => onOpenGroup(g)} />
        ))}
      </div>
    );
  }
  return (
    <div style={{
      padding: '0 22px',
      display: 'grid',
      gridTemplateColumns: '1fr 1fr',
      gap: 10,
    }}>
      {list.map(g => (
        <window.CollectionTileCard key={g.name} group={g} accent={accent}
                                   pinned={pinned.has(g.name)}
                                   onClick={() => onOpenGroup(g)} />
      ))}
    </div>
  );
}

function SortMenu({ sortBy, onChange }) {
  const [open, setOpen] = React.useState(false);
  const label = sortBy === 'name' ? '이름순'
              : sortBy === 'count' ? '씨앗 많은순'
              : '활동순';
  return (
    <div style={{ position: 'relative' }}>
      <button onClick={() => setOpen(o => !o)} style={{
        height: 28, padding: '0 10px', borderRadius: 8,
        background: 'rgba(60,42,20,0.06)', border: 'none',
        display: 'inline-flex', alignItems: 'center', gap: 4,
        fontFamily: 'inherit', fontSize: 11.5, fontWeight: 500,
        color: HT.ink2, letterSpacing: '-0.01em',
        cursor: 'pointer',
        WebkitTapHighlightColor: 'transparent',
      }}>
        <svg width="10" height="10" viewBox="0 0 12 12" fill="none">
          <path d="M2 3h8M3 6h6M4 9h4" stroke={HT.ink2} strokeWidth="1.4" strokeLinecap="round"/>
        </svg>
        {label}
      </button>
      {open && (
        <>
          <div onClick={() => setOpen(false)} style={{
            position: 'fixed', inset: 0, zIndex: 50,
          }} />
          <div style={{
            position: 'absolute', top: 'calc(100% + 4px)', right: 0,
            zIndex: 60,
            background: HT.surface2,
            border: `0.5px solid ${HT.hairlineStrong}`,
            borderRadius: 12,
            boxShadow: '0 8px 28px rgba(31,24,18,0.18)',
            padding: 4, minWidth: 140,
            display: 'flex', flexDirection: 'column', gap: 2,
          }}>
            {[
              { value: 'activity', label: '활동순' },
              { value: 'name',     label: '이름순' },
              { value: 'count',    label: '씨앗 많은순' },
            ].map(o => (
              <button key={o.value} onClick={() => { onChange(o.value); setOpen(false); }} style={{
                width: '100%', textAlign: 'left',
                padding: '8px 12px', borderRadius: 8,
                background: sortBy === o.value ? `rgba(60,42,20,0.06)` : 'transparent',
                border: 'none',
                fontFamily: 'inherit', fontSize: 13, fontWeight: 500,
                color: HT.ink, cursor: 'pointer',
                display: 'flex', alignItems: 'center', justifyContent: 'space-between',
                WebkitTapHighlightColor: 'transparent',
              }}>
                {o.label}
                {sortBy === o.value && (
                  <svg width="12" height="12" viewBox="0 0 14 14" fill="none">
                    <path d="M3 7.2l2.8 2.8L11 5" stroke={HT.ink} strokeWidth="2"
                          strokeLinecap="round" strokeLinejoin="round"/>
                  </svg>
                )}
              </button>
            ))}
          </div>
        </>
      )}
    </div>
  );
}

function ViewToggle({ compact, onChange }) {
  return (
    <div style={{
      display: 'inline-flex', padding: 3, borderRadius: 8,
      background: 'rgba(60,42,20,0.06)',
    }}>
      <button onClick={() => onChange(false)} style={{
        width: 26, height: 22, padding: 0, border: 'none',
        background: !compact ? HT.surface : 'transparent',
        borderRadius: 6,
        boxShadow: !compact ? '0 1px 2px rgba(31,24,18,0.08)' : 'none',
        cursor: 'pointer', flexShrink: 0,
        display: 'inline-flex', alignItems: 'center', justifyContent: 'center',
      }}>
        <svg width="11" height="11" viewBox="0 0 12 12" fill="none">
          <rect x="1" y="1" width="4" height="4" rx="1" fill={!compact ? HT.ink : HT.ink3}/>
          <rect x="7" y="1" width="4" height="4" rx="1" fill={!compact ? HT.ink : HT.ink3}/>
          <rect x="1" y="7" width="4" height="4" rx="1" fill={!compact ? HT.ink : HT.ink3}/>
          <rect x="7" y="7" width="4" height="4" rx="1" fill={!compact ? HT.ink : HT.ink3}/>
        </svg>
      </button>
      <button onClick={() => onChange(true)} style={{
        width: 26, height: 22, padding: 0, border: 'none',
        background: compact ? HT.surface : 'transparent',
        borderRadius: 6,
        boxShadow: compact ? '0 1px 2px rgba(31,24,18,0.08)' : 'none',
        cursor: 'pointer', flexShrink: 0,
        display: 'inline-flex', alignItems: 'center', justifyContent: 'center',
      }}>
        <svg width="11" height="11" viewBox="0 0 12 12" fill="none">
          <rect x="1" y="1" width="10" height="2" rx="1" fill={compact ? HT.ink : HT.ink3}/>
          <rect x="1" y="5" width="10" height="2" rx="1" fill={compact ? HT.ink : HT.ink3}/>
          <rect x="1" y="9" width="10" height="2" rx="1" fill={compact ? HT.ink : HT.ink3}/>
        </svg>
      </button>
    </div>
  );
}

function SectionHeader({ title, sub, action }) {
  return (
    <div style={{
      position: 'sticky', top: 90, zIndex: 12,
      background: HT.bg,
      padding: '20px 22px 10px',
      marginTop: 4,
      display: 'flex', alignItems: 'baseline', justifyContent: 'space-between',
      gap: 10,
    }}>
      <div style={{
        display: 'flex', alignItems: 'baseline', gap: 8, minWidth: 0,
      }}>
        <span style={{
          fontSize: 17, fontWeight: 700, color: HT.ink,
          letterSpacing: '-0.02em', whiteSpace: 'nowrap',
        }}>{title}</span>
        {sub && (
          <span style={{
            fontSize: 13, color: HT.ink3, letterSpacing: '-0.01em',
            whiteSpace: 'nowrap',
          }}>{sub}</span>
        )}
      </div>
      {action}
    </div>
  );
}

function RecentMiniCard({ card, accent, onClick }) {
  return (
    <button onClick={onClick} style={{
      flex: '0 0 180px',
      padding: '14px 14px 12px',
      background: HT.surface,
      border: `0.5px solid ${HT.hairline}`,
      borderRadius: 14,
      cursor: 'pointer',
      fontFamily: 'inherit',
      textAlign: 'left',
      display: 'flex', flexDirection: 'column', gap: 8,
      WebkitTapHighlightColor: 'transparent',
    }}>
      <div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
        <span style={{ fontSize: 13, lineHeight: 1 }}>
          {langFlag(card.lang)}
        </span>
        <span style={{
          fontSize: 10, fontWeight: 600, color: HT.ink3,
          letterSpacing: '0.06em', textTransform: 'uppercase',
        }}>{card.langLabel}</span>
      </div>
      <div style={{
        fontFamily: langFontFor(card.lang),
        fontSize: 14, fontWeight: 500, color: HT.ink,
        letterSpacing: '-0.01em', lineHeight: 1.35,
        display: '-webkit-box',
        WebkitLineClamp: 2, WebkitBoxOrient: 'vertical',
        overflow: 'hidden',
        minHeight: 38,
      }}>{card.foreign}</div>
      <div style={{
        fontSize: 11, color: HT.ink3, letterSpacing: '-0.01em',
        lineHeight: 1.3,
        display: '-webkit-box',
        WebkitLineClamp: 1, WebkitBoxOrient: 'vertical',
        overflow: 'hidden',
      }}>{card.korean}</div>
    </button>
  );
}

// ─────────────────────────────────────────────────────────────
// Collection tile — stacked-cards illustration + meta
// ─────────────────────────────────────────────────────────────
function CollectionTile({ group, accent, onClick }) {
  const previewCard = group.cards && group.cards[0];
  const dot = group.color || LANG_DOT[group.primaryLang] || HT.ink2;
  const isEmpty = !previewCard;
  const cover = group.cover || { kind: 'auto' };
  return (
    <button onClick={onClick} style={{
      padding: 0,
      background: HT.surface,
      border: `0.5px solid ${HT.hairline}`,
      borderRadius: 16,
      cursor: 'pointer',
      fontFamily: 'inherit',
      textAlign: 'left',
      WebkitTapHighlightColor: 'transparent',
      overflow: 'hidden',
      display: 'flex', flexDirection: 'column',
    }}>
      {window.CoverArt
        ? <window.CoverArt cover={cover} color={dot} sampleCard={previewCard}
                           slotId={window.slotIdFor ? window.slotIdFor(group.name) : group.name}
                           height={92} isEmpty={isEmpty} />
        : null}

      {/* Meta */}
      <div style={{
        padding: '12px 14px 14px',
        display: 'flex', flexDirection: 'column', gap: 4,
      }}>
        <div style={{
          fontSize: 14, fontWeight: 600, color: HT.ink,
          letterSpacing: '-0.02em', lineHeight: 1.25,
          display: '-webkit-box',
          WebkitLineClamp: 1, WebkitBoxOrient: 'vertical',
          overflow: 'hidden',
        }}>{group.name}</div>
        <div style={{
          display: 'flex', alignItems: 'center', gap: 6,
          fontSize: 11, color: HT.ink3, letterSpacing: '-0.01em',
        }}>
          <span style={{ fontSize: 13, lineHeight: 1, flexShrink: 0 }}>
            {group.isMultiLang ? '🌐' : langFlag(group.primaryLang)}
          </span>
          {isEmpty ? (
            <span style={{ fontStyle: 'italic' }}>비어있음</span>
          ) : (
            <>
              <span>{group.isMultiLang ? '여러 언어' : group.primaryLangLabel}</span>
              <span>·</span>
              <span style={{ fontWeight: 600, color: HT.ink2 }}>{group.count}개</span>
            </>
          )}
        </div>
      </div>
    </button>
  );
}

// ─────────────────────────────────────────────────────────────
// CollectionDetail — drill-down for one group
// ─────────────────────────────────────────────────────────────
function CollectionDetail({ group, accent, onBack, onOpenCard, onOpenActions, onAddCard, onAddExistingCards, onRemoveCard, editing: editingProp, setEditing: setEditingProp }) {
  const [editingLocal, setEditingLocal] = React.useState(false);
  const editing = editingProp !== undefined ? editingProp : editingLocal;
  const setEditing = setEditingProp || setEditingLocal;
  const [sort, setSort] = React.useState('recent'); // recent | category
  const [playingIdx, setPlayingIdx] = React.useState(-1);
  const [playMode, setPlayMode] = React.useState(null); // 'all' | 'single' | null
  const dot = group.color || LANG_DOT[group.primaryLang] || HT.ink2;
  const isEmpty = !group.cards || group.cards.length === 0;

  // Exit edit mode when group becomes empty
  React.useEffect(() => {
    if (isEmpty && editing) setEditing(false);
  }, [isEmpty, editing]);

  const sortedCards = React.useMemo(() => {
    if (sort === 'recent') {
      return [...group.cards].sort((a, b) => a.addedDays - b.addedDays);
    }
    if (sort === 'category') {
      return [...group.cards].sort((a, b) => a.category.localeCompare(b.category));
    }
    return group.cards;
  }, [group, sort]);

  const isPlaying = playingIdx >= 0;
  const isPlayingAll = isPlaying && playMode === 'all';
  const playingCardId = isPlaying ? sortedCards[playingIdx]?.id : null;

  const stopPlay = React.useCallback(() => {
    setPlayingIdx(-1);
    setPlayMode(null);
  }, []);

  // Play-all timer: advance every 1.6s in 'all' mode.
  // 'single' mode: play for 1.6s then auto-stop.
  React.useEffect(() => {
    if (playingIdx < 0) return;
    if (playingIdx >= sortedCards.length) { stopPlay(); return; }
    const t = setTimeout(() => {
      if (playMode === 'single') {
        stopPlay();
      } else {
        setPlayingIdx(i => i + 1 >= sortedCards.length ? -1 : i + 1);
        // mode stays 'all'; if we landed on -1, the next effect run will clear mode.
      }
    }, 1600);
    return () => clearTimeout(t);
  }, [playingIdx, playMode, sortedCards.length, stopPlay]);

  // When playingIdx returns to -1, clear playMode too
  React.useEffect(() => {
    if (playingIdx < 0 && playMode !== null) setPlayMode(null);
  }, [playingIdx, playMode]);

  // Stop play when sort changes
  React.useEffect(() => { stopPlay(); }, [sort, stopPlay]);

  // For category sort, build sections
  const sections = React.useMemo(() => {
    if (sort !== 'category') return [{ title: null, cards: sortedCards }];
    const map = new Map();
    for (const c of sortedCards) {
      if (!map.has(c.category)) map.set(c.category, []);
      map.get(c.category).push(c);
    }
    return [...map.entries()].map(([title, cards]) => ({ title, cards }));
  }, [sortedCards, sort]);

  return (
    <div style={{ paddingBottom: 50 }}>
      {/* Top bar — sticky */}
      <div style={{
        position: 'sticky', top: 0, zIndex: 30,
        background: HT.bg,
        padding: '54px 16px 8px',
        display: 'flex', alignItems: 'center', justifyContent: 'space-between',
      }}>
        <button onClick={onBack} style={{
          background: 'transparent', border: 'none', padding: 0,
          width: 38, height: 38, borderRadius: 38,
          display: 'inline-flex', alignItems: 'center', justifyContent: 'center',
          cursor: 'pointer',
        }}>
          <svg width="22" height="22" viewBox="0 0 22 22" fill="none">
            <path d="M14 4L7 11l7 7" stroke={HT.ink} strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round"/>
          </svg>
        </button>
        <div style={{ display: 'flex', alignItems: 'center', gap: 2 }}>
          <button onClick={onAddExistingCards} style={{
            width: 38, height: 38, borderRadius: 38,
            background: 'transparent', border: 'none', padding: 0,
            display: 'inline-flex', alignItems: 'center', justifyContent: 'center',
            cursor: 'pointer',
            WebkitTapHighlightColor: 'transparent',
          }} title="씨앗 추가">
            <svg width="22" height="22" viewBox="0 0 22 22" fill="none">
              <path d="M11 4v14M4 11h14" stroke={HT.ink} strokeWidth="1.8" strokeLinecap="round"/>
            </svg>
          </button>
          <button onClick={onOpenActions} style={{
            background: 'transparent', border: 'none', padding: 0,
            width: 38, height: 38, borderRadius: 38,
            display: 'inline-flex', alignItems: 'center', justifyContent: 'center',
            cursor: 'pointer',
            WebkitTapHighlightColor: 'transparent',
          }}>
            <svg width="20" height="6" viewBox="0 0 20 6">
              <circle cx="3" cy="3" r="1.6" fill={HT.ink}/>
              <circle cx="10" cy="3" r="1.6" fill={HT.ink}/>
              <circle cx="17" cy="3" r="1.6" fill={HT.ink}/>
            </svg>
          </button>
        </div>
      </div>

      {/* Header */}
      <div style={{ padding: '12px 24px 0' }}>
        <div style={{
          display: 'flex', alignItems: 'center', gap: 6,
          fontSize: 11, fontWeight: 600, color: HT.ink3,
          letterSpacing: '0.06em', textTransform: 'uppercase',
        }}>
          <span style={{ fontSize: 14, lineHeight: 1 }}>
            {isEmpty ? '💭' : (group.isMultiLang ? '🌐' : langFlag(group.primaryLang))}
          </span>
          {isEmpty
            ? '비어있는 주머니'
            : (group.isMultiLang ? '여러 언어' : group.primaryLangLabel)}
        </div>
        <div style={{
          marginTop: 12,
          fontSize: 30, fontWeight: 700, color: HT.ink,
          letterSpacing: '-0.03em', lineHeight: 1.15,
        }}>{group.name}</div>
        <div style={{
          marginTop: 8,
          fontSize: 14, color: HT.ink2, letterSpacing: '-0.01em',
        }}>
          {isEmpty ? (
            <span>아직 씨앗이 없어요</span>
          ) : (
            <>
              <span style={{ fontWeight: 600, color: HT.ink }}>{group.count}개</span>
              <span> · 마지막 추가 {daysAgo(group.latestDays)}</span>
            </>
          )}
        </div>
      </div>

      {isEmpty ? (
        <EmptyCollectionBody accent={accent} color={dot} onAddCard={onAddCard} />
      ) : (
        <>

      {/* Play all + sort — single row */}
      <div style={{
        margin: '22px 22px 0',
        display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 12,
      }}>
        <button onClick={() => {
          if (isPlayingAll) { stopPlay(); }
          else { setPlayMode('all'); setPlayingIdx(0); }
        }} style={{
          height: 38, padding: '0 16px', borderRadius: 999,
          background: isPlayingAll ? HT.ink : accent,
          color: '#FFF', border: 'none',
          display: 'inline-flex', alignItems: 'center', gap: 8,
          fontFamily: 'inherit', fontSize: 13, fontWeight: 600,
          letterSpacing: '-0.01em', cursor: 'pointer',
          transition: 'background 0.18s ease',
          WebkitTapHighlightColor: 'transparent',
        }}>
          {isPlayingAll ? (
            <>
              <svg width="10" height="10" viewBox="0 0 10 10">
                <rect x="0" y="0" width="10" height="10" rx="1.5" fill="#FFF"/>
              </svg>
              정지
              <span style={{
                marginLeft: 2, opacity: 0.7,
                fontFamily: 'ui-monospace, SFMono-Regular, monospace',
                fontSize: 11,
                fontVariantNumeric: 'tabular-nums',
              }}>{playingIdx + 1} / {sortedCards.length}</span>
            </>
          ) : (
            <>
              <svg width="11" height="11" viewBox="0 0 14 14"><path d="M3 1.8 L3 12.2 L12 7 Z" fill="#FFF"/></svg>
              전체 재생
            </>
          )}
        </button>

        <div style={{
          display: 'flex', alignItems: 'center', gap: 2,
          padding: 3, borderRadius: 10,
          background: 'rgba(60,42,20,0.06)',
        }}>
          {[
            { id: 'recent', label: '최신순' },
            { id: 'category', label: '카테고리' },
          ].map(o => (
            <button key={o.id} onClick={() => setSort(o.id)} style={{
              height: 28, padding: '0 10px', borderRadius: 8, border: 'none',
              background: sort === o.id ? HT.surface : 'transparent',
              boxShadow: sort === o.id ? '0 1px 2px rgba(31,24,18,0.08)' : 'none',
              fontFamily: 'inherit', fontSize: 11.5, fontWeight: 500,
              color: sort === o.id ? HT.ink : HT.ink2,
              cursor: 'pointer', letterSpacing: '-0.01em',
              WebkitTapHighlightColor: 'transparent',
            }}>{o.label}</button>
          ))}
        </div>
      </div>

      {/* Card list */}
      <div style={{ padding: '18px 22px 0', display: 'flex', flexDirection: 'column', gap: 14 }}>
        {sections.map((sec, si) => (
          <div key={si}>
            {sec.title && (
              <div style={{
                padding: '4px 4px 10px',
                fontSize: 11, fontWeight: 600, color: HT.ink3,
                letterSpacing: '0.06em', textTransform: 'uppercase',
              }}>{sec.title} · {sec.cards.length}개</div>
            )}
            <div style={{ display: 'flex', flexDirection: 'column', gap: 10 }}>
              {sec.cards.map(c => {
                const cardIdx = sortedCards.findIndex(x => x.id === c.id);
                return (
                  <CompactCardRow key={c.id} card={c} accent={accent}
                                  editing={editing}
                                  playing={playingCardId === c.id}
                                  onClick={() => editing ? null : onOpenCard(c)}
                                  onTogglePlay={() => {
                                    if (editing) return;
                                    if (playingIdx === cardIdx) {
                                      stopPlay();
                                    } else {
                                      setPlayMode('single');
                                      setPlayingIdx(cardIdx);
                                    }
                                  }}
                                  onRemove={() => onRemoveCard && onRemoveCard(c.id)} />
                );
              })}
            </div>
          </div>
        ))}
      </div>
        </>
      )}
    </div>
  );
}

function CompactCardRow({ card, accent, onClick, editing, playing, onRemove, onTogglePlay }) {
  return (
    <div role="button" tabIndex={editing ? -1 : 0}
      onClick={onClick}
      onKeyDown={(e) => {
        if (editing) return;
        if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); onClick && onClick(); }
      }}
      style={{
      width: '100%', textAlign: 'left',
      background: playing ? `${accent}10` : HT.surface,
      border: playing ? `1.5px solid ${accent}` : `0.5px solid ${HT.hairline}`,
      borderRadius: 14,
      padding: playing ? '12px 13px' : '13px 14px',
      cursor: editing ? 'default' : 'pointer',
      fontFamily: 'inherit',
      display: 'flex', alignItems: 'center', gap: 10,
      WebkitTapHighlightColor: 'transparent',
      transition: 'padding 0.18s ease, background 0.18s ease, border-color 0.18s ease',
      position: 'relative',
      outline: 'none',
    }}>
      {editing && (
        <button onClick={(e) => { e.stopPropagation(); onRemove && onRemove(); }} style={{
          width: 24, height: 24, borderRadius: 24, padding: 0,
          background: '#B5483C',
          border: 'none',
          display: 'inline-flex', alignItems: 'center', justifyContent: 'center',
          cursor: 'pointer', flexShrink: 0,
          WebkitTapHighlightColor: 'transparent',
        }}>
          <svg width="10" height="2" viewBox="0 0 10 2">
            <rect x="0" y="0" width="10" height="2" rx="1" fill="#FFF"/>
          </svg>
        </button>
      )}
      {playing && !editing && (
        <span style={{
          width: 4, alignSelf: 'stretch', borderRadius: 4,
          background: accent,
          flexShrink: 0,
          marginRight: 2,
        }} />
      )}
      <div style={{ flex: 1, minWidth: 0 }}>
        <div style={{
          fontFamily: langFontFor(card.lang),
          fontSize: 15, fontWeight: playing ? 600 : 500, color: HT.ink,
          letterSpacing: '-0.01em', lineHeight: 1.35,
          overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap',
        }}>{card.foreign}</div>
        <div style={{
          marginTop: 3,
          fontSize: 12, color: playing ? accent : HT.ink3,
          letterSpacing: '-0.01em',
          overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap',
          fontWeight: playing ? 600 : 400,
        }}>{card.korean}</div>
      </div>
      {!editing && (
        playing ? (
          <button onClick={(e) => { e.stopPropagation(); onTogglePlay && onTogglePlay(); }}
                  aria-label="재생 멈추기"
                  style={{
            width: 28, height: 28, borderRadius: 28,
            background: accent,
            border: 'none', padding: 0,
            display: 'inline-flex', alignItems: 'center', justifyContent: 'center',
            flexShrink: 0,
            cursor: 'pointer',
            WebkitTapHighlightColor: 'transparent',
          }}>
            <svg width="14" height="14" viewBox="0 0 14 14">
              <rect x="3"  y="3" width="2" height="8" rx="1" fill="#FFF">
                <animate attributeName="height" values="3;8;3" dur="0.7s" repeatCount="indefinite" />
                <animate attributeName="y" values="5.5;3;5.5" dur="0.7s" repeatCount="indefinite" />
              </rect>
              <rect x="6"  y="2" width="2" height="10" rx="1" fill="#FFF">
                <animate attributeName="height" values="6;10;6" dur="0.6s" begin="0.1s" repeatCount="indefinite" />
                <animate attributeName="y" values="4;2;4" dur="0.6s" begin="0.1s" repeatCount="indefinite" />
              </rect>
              <rect x="9"  y="4" width="2" height="6" rx="1" fill="#FFF">
                <animate attributeName="height" values="4;6;4" dur="0.8s" begin="0.2s" repeatCount="indefinite" />
                <animate attributeName="y" values="5;4;5" dur="0.8s" begin="0.2s" repeatCount="indefinite" />
              </rect>
            </svg>
          </button>
        ) : (
          <button onClick={(e) => { e.stopPropagation(); onTogglePlay && onTogglePlay(); }}
                  aria-label="이 씨앗 재생"
                  style={{
            width: 28, height: 28, borderRadius: 28,
            background: 'transparent',
            border: `1px solid ${HT.hairlineStrong}`,
            padding: 0,
            display: 'inline-flex', alignItems: 'center', justifyContent: 'center',
            flexShrink: 0,
            cursor: 'pointer',
            fontFamily: 'inherit',
            WebkitTapHighlightColor: 'transparent',
            transition: 'background 0.15s ease, border-color 0.15s ease',
          }}
          onMouseEnter={(e) => { e.currentTarget.style.background = 'rgba(60,42,20,0.05)'; }}
          onMouseLeave={(e) => { e.currentTarget.style.background = 'transparent'; }}>
            <svg width="9" height="9" viewBox="0 0 14 14"><path d="M3 1.8 L3 12.2 L12 7 Z" fill={HT.ink}/></svg>
          </button>
        )
      )}
    </div>
  );
}

// ─────────────────────────────────────────────────────────────
// SearchScreen — full overlay
// ─────────────────────────────────────────────────────────────
const SEARCH_SUGGESTIONS = ['물', '계산', '추천', '화장실', '도쿄', '응급'];

function SearchScreen({ accent, onClose, onOpenCard, onOpenGroup }) {
  const [q, setQ] = React.useState('');
  const inputRef = React.useRef(null);
  React.useEffect(() => { inputRef.current && inputRef.current.focus(); }, []);

  const query = q.trim().toLowerCase();
  const hasQ = query.length > 0;

  const cardHits = React.useMemo(() => {
    if (!hasQ) return [];
    return EXPANDED_CARDS.filter(c =>
      c.foreign.toLowerCase().includes(query) ||
      c.pronunciation.toLowerCase().includes(query) ||
      c.korean.toLowerCase().includes(query) ||
      c.category.toLowerCase().includes(query)
    ).slice(0, 30);
  }, [query, hasQ]);

  const groupHits = React.useMemo(() => {
    if (!hasQ) return [];
    return GROUPS.filter(g => g.name.toLowerCase().includes(query));
  }, [query, hasQ]);

  return (
    <div style={{
      position: 'absolute', inset: 0,
      background: HT.bg,
      animation: 'cp-fade-bg 0.2s ease',
      display: 'flex', flexDirection: 'column',
    }}>
      {/* Search bar */}
      <div style={{
        padding: '54px 22px 10px',
        display: 'flex', alignItems: 'center', gap: 10,
      }}>
        <div style={{
          flex: 1, height: 44, borderRadius: 12,
          background: HT.surface,
          border: `0.5px solid ${HT.hairline}`,
          display: 'flex', alignItems: 'center', gap: 8,
          padding: '0 14px',
        }}>
          <svg width="16" height="16" viewBox="0 0 20 20" fill="none">
            <circle cx="9" cy="9" r="6.2" stroke={HT.ink3} strokeWidth="1.6"/>
            <path d="M13.6 13.6L17 17" stroke={HT.ink3} strokeWidth="1.6" strokeLinecap="round"/>
          </svg>
          <input
            ref={inputRef}
            value={q}
            onChange={(e) => setQ(e.target.value)}
            placeholder="표현, 단어, 주머니 검색"
            style={{
              flex: 1, minWidth: 0, height: '100%',
              background: 'transparent', border: 'none', outline: 'none',
              fontFamily: 'inherit', fontSize: 15, color: HT.ink,
              letterSpacing: '-0.01em',
            }}
          />
          {q && (
            <button onClick={() => setQ('')} style={{
              width: 18, height: 18, borderRadius: 18, padding: 0,
              background: 'rgba(60,42,20,0.18)', border: 'none',
              display: 'inline-flex', alignItems: 'center', justifyContent: 'center',
              cursor: 'pointer', flexShrink: 0,
            }}>
              <svg width="8" height="8" viewBox="0 0 10 10">
                <path d="M2 2l6 6M8 2l-6 6" stroke="#FFF" strokeWidth="1.6" strokeLinecap="round"/>
              </svg>
            </button>
          )}
        </div>
        <button onClick={onClose} style={{
          background: 'none', border: 'none', padding: 0,
          fontSize: 15, color: HT.ink2, cursor: 'pointer',
          fontFamily: 'inherit', letterSpacing: '-0.01em',
        }}>취소</button>
      </div>

      {/* Body */}
      <div style={{ flex: 1, overflowY: 'auto', overflowX: 'hidden' }}>
        {!hasQ ? (
          <>
            {/* Empty state — suggestions */}
            <div style={{ padding: '14px 22px 8px',
              fontSize: 11, fontWeight: 600, color: HT.ink3,
              letterSpacing: '0.06em', textTransform: 'uppercase',
            }}>이렇게 검색해보세요</div>
            <div style={{ padding: '0 22px',
              display: 'flex', gap: 8, flexWrap: 'wrap',
            }}>
              {SEARCH_SUGGESTIONS.map(s => (
                <button key={s} onClick={() => setQ(s)} style={{
                  height: 34, padding: '0 14px', borderRadius: 999,
                  background: HT.surface,
                  border: `0.5px solid ${HT.hairlineStrong}`,
                  fontFamily: 'inherit', fontSize: 13, color: HT.ink,
                  cursor: 'pointer', letterSpacing: '-0.01em',
                  display: 'inline-flex', alignItems: 'center', gap: 5,
                  WebkitTapHighlightColor: 'transparent',
                }}>
                  <svg width="11" height="11" viewBox="0 0 20 20" fill="none">
                    <circle cx="9" cy="9" r="6.2" stroke={HT.ink3} strokeWidth="1.6"/>
                    <path d="M13.6 13.6L17 17" stroke={HT.ink3} strokeWidth="1.6" strokeLinecap="round"/>
                  </svg>
                  {s}
                </button>
              ))}
            </div>

            <div style={{ padding: '26px 22px 8px',
              fontSize: 11, fontWeight: 600, color: HT.ink3,
              letterSpacing: '0.06em', textTransform: 'uppercase',
            }}>빠른 이동</div>
            <div style={{ padding: '0 22px',
              display: 'flex', flexDirection: 'column', gap: 8,
            }}>
              {GROUPS.slice(0, 4).map(g => (
                <button key={g.name} onClick={() => onOpenGroup(g)} style={{
                  width: '100%', textAlign: 'left',
                  padding: '12px 14px',
                  background: HT.surface,
                  border: `0.5px solid ${HT.hairline}`,
                  borderRadius: 12,
                  fontFamily: 'inherit',
                  cursor: 'pointer',
                  display: 'flex', alignItems: 'center', gap: 12,
                  WebkitTapHighlightColor: 'transparent',
                }}>
                  <span style={{
                    width: 8, height: 8, borderRadius: 8,
                    background: LANG_DOT[g.primaryLang] || HT.ink2,
                  }} />
                  <span style={{
                    flex: 1,
                    fontSize: 14, fontWeight: 500, color: HT.ink,
                    letterSpacing: '-0.01em',
                  }}>{g.name}</span>
                  <span style={{
                    fontSize: 12, color: HT.ink3,
                  }}>{g.count}개</span>
                </button>
              ))}
            </div>
          </>
        ) : (
          <>
            {groupHits.length > 0 && (
              <>
                <div style={{ padding: '12px 22px 8px',
                  fontSize: 11, fontWeight: 600, color: HT.ink3,
                  letterSpacing: '0.06em', textTransform: 'uppercase',
                }}>주머니 · {groupHits.length}개</div>
                <div style={{ padding: '0 22px',
                  display: 'flex', flexDirection: 'column', gap: 8,
                }}>
                  {groupHits.map(g => (
                    <button key={g.name} onClick={() => onOpenGroup(g)} style={{
                      width: '100%', textAlign: 'left',
                      padding: '12px 14px',
                      background: HT.surface,
                      border: `0.5px solid ${HT.hairline}`,
                      borderRadius: 12,
                      fontFamily: 'inherit',
                      cursor: 'pointer',
                      display: 'flex', alignItems: 'center', gap: 12,
                      WebkitTapHighlightColor: 'transparent',
                    }}>
                      <span style={{
                        width: 8, height: 8, borderRadius: 8,
                        background: LANG_DOT[g.primaryLang] || HT.ink2,
                      }} />
                      <span style={{ flex: 1,
                        fontSize: 14, fontWeight: 500, color: HT.ink,
                        letterSpacing: '-0.01em',
                      }}>{highlightMatch(g.name, query)}</span>
                      <span style={{ fontSize: 12, color: HT.ink3 }}>{g.count}개</span>
                    </button>
                  ))}
                </div>
              </>
            )}

            <div style={{ padding: '20px 22px 8px',
              fontSize: 11, fontWeight: 600, color: HT.ink3,
              letterSpacing: '0.06em', textTransform: 'uppercase',
            }}>표현 · {cardHits.length}개</div>
            {cardHits.length === 0 ? (
              <div style={{
                margin: '0 22px',
                padding: '32px 18px',
                border: '1px dashed rgba(60,42,20,0.18)',
                borderRadius: 14,
                textAlign: 'center',
                color: HT.ink3, fontSize: 14,
              }}>"{q}"에 대한 결과가 없어요</div>
            ) : (
              <div style={{ padding: '0 22px',
                display: 'flex', flexDirection: 'column', gap: 8,
              }}>
                {cardHits.map(c => (
                  <SearchResultRow key={c.id} card={c} query={query}
                                   accent={accent} onClick={() => onOpenCard(c)} />
                ))}
              </div>
            )}
          </>
        )}
      </div>
    </div>
  );
}

function highlightMatch(text, query) {
  if (!query) return text;
  const idx = text.toLowerCase().indexOf(query);
  if (idx < 0) return text;
  return (
    <>
      {text.slice(0, idx)}
      <mark style={{ background: 'rgba(201,123,92,0.22)', color: 'inherit',
                     padding: 0, borderRadius: 2 }}>
        {text.slice(idx, idx + query.length)}
      </mark>
      {text.slice(idx + query.length)}
    </>
  );
}

function SearchResultRow({ card, query, accent, onClick }) {
  return (
    <button onClick={onClick} style={{
      width: '100%', textAlign: 'left',
      padding: '12px 14px',
      background: HT.surface,
      border: `0.5px solid ${HT.hairline}`,
      borderRadius: 12,
      fontFamily: 'inherit',
      cursor: 'pointer',
      display: 'flex', alignItems: 'flex-start', gap: 10,
      WebkitTapHighlightColor: 'transparent',
    }}>
      <span style={{
        fontSize: 14, lineHeight: 1, marginTop: 4, flexShrink: 0,
      }}>{langFlag(card.lang)}</span>
      <div style={{ flex: 1, minWidth: 0 }}>
        <div style={{
          fontFamily: langFontFor(card.lang),
          fontSize: 14, fontWeight: 500, color: HT.ink,
          letterSpacing: '-0.01em', lineHeight: 1.35,
          overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap',
        }}>{highlightMatch(card.foreign, query)}</div>
        <div style={{
          marginTop: 3,
          fontSize: 12, color: HT.ink2, letterSpacing: '-0.01em',
          overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap',
        }}>{highlightMatch(card.korean, query)}</div>
        <div style={{
          marginTop: 5,
          fontSize: 10, color: HT.ink3, letterSpacing: '0.02em',
        }}>{card.group} · {card.category}</div>
      </div>
    </button>
  );
}

// ─────────────────────────────────────────────────────────────
// Expose to global
// ─────────────────────────────────────────────────────────────
Object.assign(window, {
  HomeV2,
  CollectionDetail,
  SearchScreen,
  EXPANDED_CARDS,
  GROUPS,
  RECENT_IDS,
  deriveGroups,
  LANG_DOT,
});
