const BADGE_SERIES = {
  '기록': { color: '#E8A33D', tint: 'rgba(232,163,61,.14)', desc: '꾸준히 쌓은 나의 축적' },
  '제보': { color: '#1B8F5A', tint: 'rgba(27,143,90,.10)', desc: '지도를 정확하게 만든 기여' },
  '탐험': { color: '#3B6FD9', tint: 'rgba(59,111,217,.10)', desc: '넓힌 반경' },
  '커뮤니티': { color: '#E31E24', tint: 'rgba(227,30,36,.09)', desc: '남에게 닿은 결과' },
  '시즌': { color: '#1A1A1A', tint: 'rgba(26,26,26,.07)', desc: '기간이 지나면 못 얻음' }
};
const BADGE_CATALOG = [
  { s: '기록', i: 'camera', n: '첫 한 컵', c: '첫 게시물 등록', r: '상시', got: '2026.03.02', pct: 92 },
  { s: '기록', i: 'flame', n: '사흘 연속', c: '3일 연속 업로드', r: '상시', got: '2026.03.14', pct: 54 },
  { s: '기록', i: 'calendar-check', n: '한 달 개근', c: '30일 연속 업로드', r: '상시', cur: 12, max: 30, pct: 6 },
  { s: '기록', i: 'stack-2', n: '백 장의 밥', c: '게시물 100개', r: '상시', cur: 74, max: 100, pct: 11 },
  { s: '기록', i: 'video', n: '영상 기록가', c: '동영상 게시물 10개', r: '상시', got: '2026.06.09', pct: 18 },
  { s: '기록', i: 'layout-grid', n: '다섯 컷 장인', c: '5장 꽉 채운 게시물 20개', r: '상시', cur: 7, max: 20, pct: 9 },
  { s: '기록', i: 'door', n: '오늘의 첫 손님', c: '그 가게 첫 게시물 10회', r: '상시', cur: 4, max: 10, pct: 14 },
  { s: '기록', i: 'moon', n: '새벽 기록', c: '오전 4~6시 업로드 5회', r: '숨김', got: '2026.05.21', pct: 3 },
  { s: '기록', i: 'cloud-rain', n: '비 오는 날', c: '비 오는 날 업로드 3회', r: '숨김', cur: 0, max: 3, pct: 7 },
  { s: '제보', i: 'map-pin-check', n: '첫 제보', c: '위치 제보 1건 채택', r: '상시', got: '2026.03.20', pct: 61 },
  { s: '제보', i: 'shield-check', n: '동네 지킴이', c: '제보 10건 채택', r: '상시', got: '2026.06.02', pct: 12 },
  { s: '제보', i: 'pin', n: '좌표 요정', c: '제보 50건 채택', r: '상시', cur: 23, max: 50, pct: 2 },
  { s: '제보', i: 'pencil', n: '오탐 정정', c: '잘못된 위치 수정 5건', r: '상시', cur: 2, max: 5, pct: 16 },
  { s: '제보', i: 'bolt', n: '빠른 손', c: '제보 요청 10분 내 응답 3회', r: '상시', got: '2026.07.11', pct: 8 },
  { s: '제보', i: 'user-check', n: '만장일치', c: '3인 이상 동의로 채택', r: '상시', got: '2026.04.05', pct: 24 },
  { s: '제보', i: 'door-exit', n: '폐업 알림', c: '폐업 제보 3건 채택', r: '상시', cur: 1, max: 3, pct: 10 },
  { s: '제보', i: 'route', n: '골목 해설사', c: '도로명 없는 곳 제보 5건', r: '숨김', cur: 0, max: 5, pct: 1 },
  { s: '제보', i: 'map-2', n: '지도 완성자', c: '한 동네 미확인 스팟 전부', r: '숨김', cur: 0, max: 1, pct: 1 },
  { s: '탐험', i: 'footprint', n: '첫 걸음', c: '내 주변 100m 첫 탐색', r: '상시', got: '2026.03.01', pct: 96 },
  { s: '탐험', i: 'map', n: '다섯 동네', c: '5개 동에서 기록', r: '상시', got: '2026.04.18', pct: 38 },
  { s: '탐험', i: 'world', n: '스물다섯 동네', c: '25개 동에서 기록', r: '상시', cur: 11, max: 25, pct: 5 },
  { s: '탐험', i: 'category', n: '전 카테고리', c: '8개 카테고리 전부', r: '상시', cur: 6, max: 8, pct: 21 },
  { s: '탐험', i: 'building-store', n: '노포 순례', c: '20년 이상 가게 10곳', r: '상시', cur: 3, max: 10, pct: 7 },
  { s: '탐험', i: 'basket', n: '시장 탐험', c: '전통시장 5곳', r: '상시', cur: 1, max: 5, pct: 13 },
  { s: '탐험', i: 'anchor', n: '섬 상륙', c: '섬 지역 업로드 1회', r: '상시', cur: 0, max: 1, pct: 4 },
  { s: '탐험', i: 'plane', n: '국경 넘기', c: '해외 업로드 1회', r: '숨김', cur: 0, max: 1, pct: 6 },
  { s: '탐험', i: 'arrows-diff', n: '반경 밖', c: '같은 날 50km 떨어진 두 곳', r: '숨김', cur: 0, max: 1, pct: 2 },
  { s: '커뮤니티', i: 'heart', n: '첫 하트', c: '좋아요 1개 받음', r: '상시', got: '2026.03.02', pct: 89 },
  { s: '커뮤니티', i: 'hearts', n: '백 하트', c: '좋아요 100개', r: '상시', got: '2026.04.22', pct: 31 },
  { s: '커뮤니티', i: 'star', n: '천 하트', c: '좋아요 1,000개', r: '상시', cur: 640, max: 1000, pct: 6 },
  { s: '커뮤니티', i: 'message', n: '대화의 시작', c: '댓글 50개 받음', r: '상시', got: '2026.06.30', pct: 19 },
  { s: '커뮤니티', i: 'message-2', n: '친절한 답장', c: '내 글 댓글 90% 답장', r: '상시', cur: 68, max: 90, pct: 11 },
  { s: '커뮤니티', i: 'bookmark', n: '저장됨', c: '내 글 100회 저장', r: '상시', cur: 41, max: 100, pct: 9 },
  { s: '커뮤니티', i: 'share', n: '추천의 추천', c: '내 글로 방문 10건', r: '숨김', cur: 0, max: 10, pct: 3 },
  { s: '커뮤니티', i: 'trophy', n: '이달의 기록자', c: '월간 랭킹 10위 이내', r: '한정', got: '2026.08.01', pct: 1, hold: '다음 집계까지만 보유' },
  { s: '시즌', i: 'flower', n: '벚꽃 시즌 2026', c: '시즌 미션 완주 · 4월', r: '한정', got: '2026.04.30', pct: 27, hold: '기간 종료 — 영구 보유' },
  { s: '시즌', i: 'bowl', n: '여름 냉면 2026', c: '여름 냉면 3곳 · 7~8월', r: '한정', cur: 1, max: 3, pct: 22, ends: '8월 31일까지' },
  { s: '시즌', i: 'soup', n: '겨울 국밥 2026', c: '겨울 국밥 5곳 · 12~1월', r: '한정', cur: 0, max: 5, pct: 0, ends: '12월 1일 시작' },
  { s: '시즌', i: 'moon-stars', n: '야식 위크', c: '이벤트 기간 심야 3회', r: '한정', got: '2026.07.18', pct: 15, hold: '기간 종료 — 영구 보유' },
  { s: '시즌', i: 'cake', n: '창립일', c: '서비스 기념일 접속', r: '한정', cur: 0, max: 1, pct: 44, ends: '11월 9일 하루' }
];
window.BADGE_SERIES = BADGE_SERIES;
window.BADGE_CATALOG = BADGE_CATALOG;
function badgeList(noneOwned) { return noneOwned ? BADGE_CATALOG.map(b => Object.assign({}, b, { got: null, cur: 0, max: b.max || 1 })) : BADGE_CATALOG; }
window.badgeList = badgeList;

function rarityChip(r) {
  return r === '한정' ? { bg: 'var(--bg-warning)', fg: 'var(--text-warning)' }
    : r === '숨김' ? { bg: 'var(--bg-ink)', fg: '#fff' }
      : { bg: 'var(--surface-2)', fg: 'var(--text-secondary)' };
}

function BadgeTile({ b, onClick }) {
  const S = BADGE_SERIES[b.s];
  const owned = !!b.got;
  const secret = !owned && b.r === '숨김';
  const cur = b.cur || 0, max = b.max || 1;
  return (
    <div onClick={onClick} style={{ background: 'var(--surface-1)', borderRadius: 'var(--radius-tile)', padding: '15px 8px 13px', display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 7, cursor: 'pointer', border: owned ? '1px solid transparent' : '1px dashed var(--border)', position: 'relative' }}>
      <div style={{ width: 46, height: 46, borderRadius: '50%', background: owned ? S.tint : 'var(--surface-2)', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
        <i className={'ti ti-' + (secret ? 'lock' : b.i)} style={{ fontSize: secret ? 18 : 21, color: owned ? S.color : 'var(--text-muted)', opacity: owned ? 1 : .55 }}></i>
      </div>
      <span style={{ font: 'var(--text-micro)', color: owned ? 'var(--text-secondary)' : 'var(--text-muted)', textAlign: 'center', lineHeight: 1.35 }}>{secret ? '???' : b.n}</span>
      {!owned && !secret && (
        <div style={{ width: '72%', height: 3, borderRadius: 2, background: 'var(--surface-2)', overflow: 'hidden' }}>
          <div style={{ width: Math.min(100, Math.round(cur / max * 100)) + '%', height: '100%', background: S.color, opacity: .5 }}></div>
        </div>
      )}
      {owned && b.r === '한정' && <div style={{ position: 'absolute', top: 8, right: 8, width: 6, height: 6, borderRadius: '50%', background: 'var(--text-warning)' }}></div>}
    </div>
  );
}

function BadgeCollection({ onOpen, noneOwned }) {
  const [filter, setFilter] = React.useState('전체');
  const [ownedOnly, setOwnedOnly] = React.useState(false);
  const CAT = badgeList(noneOwned);
  const total = CAT.length;
  const ownedCount = CAT.filter(b => b.got).length;
  const tabs = ['전체'].concat(Object.keys(BADGE_SERIES));
  const list = CAT.filter(b => (filter === '전체' || b.s === filter) && (!ownedOnly || b.got));
  const groups = filter === '전체' ? Object.keys(BADGE_SERIES) : [filter];
  return (
    <div>
      <div style={{ margin: '0 20px 14px', background: 'var(--surface-1)', borderRadius: 'var(--radius-tile)', padding: '15px 16px' }}>
        <div style={{ display: 'flex', alignItems: 'baseline', justifyContent: 'space-between', marginBottom: 10 }}>
          <span style={{ font: 'var(--text-body-strong)' }}>배지 도감</span>
          <span style={{ font: '700 14px/1 var(--font-family)', color: 'var(--text-accent)' }}>{ownedCount}<span style={{ font: 'var(--text-small)', color: 'var(--text-muted)' }}> / {total}</span></span>
        </div>
        <div style={{ height: 6, borderRadius: 3, background: 'var(--surface-2)', overflow: 'hidden' }}>
          <div style={{ width: Math.round(ownedCount / total * 100) + '%', height: '100%', background: 'var(--text-accent)', borderRadius: 3 }}></div>
        </div>
        {ownedCount === 0 && (
          <div style={{ display: 'flex', gap: 9, marginTop: 13, paddingTop: 13, borderTop: '1px solid var(--border)' }}>
            <i className="ti ti-sparkles" style={{ fontSize: 16, color: 'var(--text-accent)', flexShrink: 0, marginTop: 1 }}></i>
            <span style={{ font: 'var(--text-small)', color: 'var(--text-secondary)', lineHeight: 1.5 }}>아직 받은 배지가 없어요. 첫 게시물을 올리면 <b style={{ color: 'var(--text-primary)' }}>첫 한 컵</b>을 바로 받아요.</span>
          </div>
        )}
      </div>
      <div style={{ display: 'flex', gap: 7, overflowX: 'auto', padding: '0 20px 12px', scrollbarWidth: 'none' }}>
        {tabs.map(t => (
          <span key={t} onClick={() => setFilter(t)} style={{ flexShrink: 0, font: '600 13px/1 var(--font-family)', padding: '9px 13px', borderRadius: 999, cursor: 'pointer', background: filter === t ? 'var(--bg-ink)' : 'var(--surface-1)', color: filter === t ? '#fff' : 'var(--text-secondary)', border: filter === t ? '1px solid transparent' : '1px solid var(--border)' }}>{t}</span>
        ))}
      </div>
      <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', padding: '0 20px 12px' }}>
        <span style={{ font: 'var(--text-micro)', color: 'var(--text-muted)' }}>미획득 배지는 조건과 진행도를 함께 보여줘요</span>
        <span onClick={() => setOwnedOnly(v => !v)} style={{ flexShrink: 0, display: 'flex', alignItems: 'center', gap: 5, cursor: 'pointer', font: '600 12px/1 var(--font-family)', color: ownedOnly ? 'var(--text-accent)' : 'var(--text-secondary)' }}>
          <i className={'ti ti-' + (ownedOnly ? 'square-check' : 'square')} style={{ fontSize: 15 }}></i>보유만
        </span>
      </div>
      <div style={{ padding: '0 20px', display: 'flex', flexDirection: 'column', gap: 20 }}>
        {ownedOnly && !list.length && (
          <div style={{ background: 'var(--surface-1)', borderRadius: 'var(--radius-tile)', padding: '34px 20px', display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 10 }}>
            <div style={{ width: 52, height: 52, borderRadius: '50%', background: 'var(--surface-2)', display: 'flex', alignItems: 'center', justifyContent: 'center' }}><i className="ti ti-award" style={{ fontSize: 23, color: 'var(--text-muted)' }}></i></div>
            <span style={{ font: 'var(--text-body-strong)', fontSize: 14 }}>보유한 배지가 없어요</span>
            <span style={{ font: 'var(--text-small)', color: 'var(--text-secondary)', textAlign: 'center', lineHeight: 1.5 }}>체크를 풀면 받을 수 있는 배지 40개와<br />조건을 볼 수 있어요</span>
            <span onClick={() => setOwnedOnly(false)} style={{ marginTop: 6, font: '600 13px/1 var(--font-family)', color: 'var(--text-accent)', cursor: 'pointer' }}>전체 보기</span>
          </div>
        )}
        {groups.map(g => {
          const items = list.filter(b => b.s === g);
          if (!items.length) return null;
          const S = BADGE_SERIES[g];
          const gOwned = CAT.filter(b => b.s === g && b.got).length, gAll = CAT.filter(b => b.s === g).length;
          return (
            <div key={g}>
              <div style={{ display: 'flex', alignItems: 'center', gap: 7, marginBottom: 10 }}>
                <span style={{ width: 7, height: 7, borderRadius: '50%', background: S.color, flexShrink: 0 }}></span>
                <span style={{ font: 'var(--text-body-strong)', fontSize: 14 }}>{g}</span>
                <span style={{ font: 'var(--text-micro)', color: 'var(--text-muted)' }}>{S.desc}</span>
                <span style={{ marginLeft: 'auto', font: 'var(--text-micro)', color: 'var(--text-secondary)', flexShrink: 0 }}>{gOwned}/{gAll}</span>
              </div>
              <div style={{ display: 'grid', gridTemplateColumns: 'repeat(3,1fr)', gap: 10 }}>
                {items.map(b => <BadgeTile key={b.n} b={b} onClick={() => onOpen(b)} />)}
              </div>
            </div>
          );
        })}
      </div>
    </div>
  );
}
window.BadgeCollection = BadgeCollection;

function BadgeDetailSheet({ badge, onClose }) {
  const b = badge, S = BADGE_SERIES[b.s], owned = !!b.got, secret = !owned && b.r === '숨김';
  const rc = rarityChip(b.r), cur = b.cur || 0, max = b.max || 1;
  return (
    <div onClick={onClose} style={{ position: 'absolute', inset: 0, background: 'rgba(20,20,20,.42)', display: 'flex', alignItems: 'flex-end', zIndex: 70 }}>
      <div onClick={e => e.stopPropagation()} style={{ width: '100%', background: 'var(--surface-1)', borderRadius: '20px 20px 0 0', padding: '10px 20px 26px' }}>
        <div style={{ width: 38, height: 4, borderRadius: 2, background: 'var(--border-strong)', margin: '0 auto 20px' }}></div>
        <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 12, marginBottom: 20 }}>
          <div style={{ width: 68, height: 68, borderRadius: '50%', background: owned ? S.tint : 'var(--surface-2)', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
            <i className={'ti ti-' + (secret ? 'lock' : b.i)} style={{ fontSize: secret ? 26 : 31, color: owned ? S.color : 'var(--text-muted)', opacity: owned ? 1 : .55 }}></i>
          </div>
          <div style={{ textAlign: 'center' }}>
            <div style={{ font: '700 18px/1.3 var(--font-family)', color: owned ? 'var(--text-primary)' : 'var(--text-secondary)' }}>{secret ? '아직 열리지 않은 배지' : b.n}</div>
            <div style={{ display: 'flex', gap: 6, justifyContent: 'center', marginTop: 9 }}>
              <span style={{ font: '600 11px/1 var(--font-family)', padding: '6px 9px', borderRadius: 999, background: S.tint, color: S.color }}>{b.s}</span>
              <span style={{ font: '600 11px/1 var(--font-family)', padding: '6px 9px', borderRadius: 999, background: rc.bg, color: rc.fg }}>{b.r}</span>
            </div>
          </div>
        </div>
        <div style={{ background: 'var(--surface-2)', borderRadius: 'var(--radius-tile)', padding: '14px 16px', marginBottom: 12 }}>
          <span style={{ font: 'var(--text-micro)', color: 'var(--text-muted)', display: 'block', marginBottom: 5 }}>획득 조건</span>
          <span style={{ font: 'var(--text-body)', color: 'var(--text-primary)' }}>{secret ? '조건 비공개 — 활동하다 보면 저절로 열려요' : b.c}</span>
        </div>
        {owned ? (
          <div style={{ display: 'flex', flexDirection: 'column', gap: 1, borderRadius: 'var(--radius-tile)', overflow: 'hidden' }}>
            <div style={{ display: 'flex', justifyContent: 'space-between', background: 'var(--surface-2)', padding: '13px 16px' }}>
              <span style={{ font: 'var(--text-small)', color: 'var(--text-secondary)' }}>획득일</span>
              <span style={{ font: 'var(--text-body-strong)', fontSize: 13 }}>{b.got}</span>
            </div>
            <div style={{ display: 'flex', justifyContent: 'space-between', background: 'var(--surface-2)', padding: '13px 16px' }}>
              <span style={{ font: 'var(--text-small)', color: 'var(--text-secondary)' }}>보유자</span>
              <span style={{ font: 'var(--text-body-strong)', fontSize: 13 }}>상위 사용자 {b.pct}%</span>
            </div>
            {b.hold && (
              <div style={{ display: 'flex', justifyContent: 'space-between', background: 'var(--surface-2)', padding: '13px 16px' }}>
                <span style={{ font: 'var(--text-small)', color: 'var(--text-secondary)' }}>보유 기간</span>
                <span style={{ font: 'var(--text-body-strong)', fontSize: 13, color: 'var(--text-warning)' }}>{b.hold}</span>
              </div>
            )}
          </div>
        ) : (
          <div style={{ background: 'var(--surface-2)', borderRadius: 'var(--radius-tile)', padding: '14px 16px' }}>
            <div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: 9 }}>
              <span style={{ font: 'var(--text-small)', color: 'var(--text-secondary)' }}>진행도</span>
              <span style={{ font: 'var(--text-body-strong)', fontSize: 13, color: secret ? 'var(--text-muted)' : 'var(--text-primary)' }}>{secret ? '비공개' : cur + ' / ' + max}</span>
            </div>
            <div style={{ height: 6, borderRadius: 3, background: 'var(--surface-1)', overflow: 'hidden' }}>
              <div style={{ width: (secret ? 0 : Math.min(100, Math.round(cur / max * 100))) + '%', height: '100%', background: S.color, borderRadius: 3 }}></div>
            </div>
            {b.ends && <span style={{ font: 'var(--text-micro)', color: 'var(--text-warning)', display: 'block', marginTop: 10 }}><i className="ti ti-clock" style={{ fontSize: 12, marginRight: 4 }}></i>{b.ends}</span>}
          </div>
        )}
        <div onClick={onClose} style={{ marginTop: 20, textAlign: 'center', background: 'var(--bg-ink)', color: '#fff', borderRadius: 999, padding: '15px 0', font: '600 15px/1 var(--font-family)', cursor: 'pointer' }}>닫기</div>
      </div>
    </div>
  );
}
window.BadgeDetailSheet = BadgeDetailSheet;

function FeaturedBadgeSheet({ current, onClose, onSelect, noneOwned, onSeeAll }) {
  const owned = badgeList(noneOwned).filter(b => b.got);
  const [sel, setSel] = React.useState(current || null);
  const selName = sel ? sel.n : null;
  if (!owned.length) return (
    <div onClick={onClose} style={{ position: 'absolute', inset: 0, background: 'rgba(20,20,20,.42)', display: 'flex', alignItems: 'flex-end', zIndex: 70 }}>
      <div onClick={e => e.stopPropagation()} style={{ width: '100%', background: 'var(--surface-1)', borderRadius: '20px 20px 0 0', padding: '10px 20px 26px' }}>
        <div style={{ width: 38, height: 4, borderRadius: 2, background: 'var(--border-strong)', margin: '0 auto 22px' }}></div>
        <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 11, padding: '10px 0 26px' }}>
          <div style={{ width: 62, height: 62, borderRadius: '50%', background: 'var(--surface-2)', display: 'flex', alignItems: 'center', justifyContent: 'center' }}><i className="ti ti-award" style={{ fontSize: 27, color: 'var(--text-muted)' }}></i></div>
          <span style={{ font: '700 17px/1.3 var(--font-family)' }}>아직 보유한 배지가 없어요</span>
          <span style={{ font: 'var(--text-small)', color: 'var(--text-secondary)', textAlign: 'center', lineHeight: 1.6 }}>배지를 하나라도 받으면 프로필 사진 옆에<br />대표로 걸어둘 수 있어요</span>
        </div>
        <div onClick={() => (onSeeAll || onClose)()} style={{ textAlign: 'center', background: 'var(--bg-ink)', color: '#fff', borderRadius: 999, padding: '15px 0', font: '600 15px/1 var(--font-family)', cursor: 'pointer' }}>받을 수 있는 배지 보기</div>
        <div onClick={onClose} style={{ marginTop: 10, textAlign: 'center', color: 'var(--text-secondary)', padding: '13px 0', font: '600 14px/1 var(--font-family)', cursor: 'pointer' }}>닫기</div>
      </div>
    </div>
  );
  return (
    <div onClick={onClose} style={{ position: 'absolute', inset: 0, background: 'rgba(20,20,20,.42)', display: 'flex', alignItems: 'flex-end', zIndex: 70 }}>
      <div onClick={e => e.stopPropagation()} style={{ width: '100%', background: 'var(--surface-1)', borderRadius: '20px 20px 0 0', padding: '10px 0 26px', maxHeight: '82%', display: 'flex', flexDirection: 'column' }}>
        <div style={{ width: 38, height: 4, borderRadius: 2, background: 'var(--border-strong)', margin: '0 auto 18px', flexShrink: 0 }}></div>
        <div style={{ padding: '0 20px 14px', flexShrink: 0 }}>
          <span style={{ font: '700 17px/1.3 var(--font-family)', display: 'block' }}>대표 배지</span>
          <span style={{ font: 'var(--text-small)', color: 'var(--text-secondary)', display: 'block', marginTop: 5 }}>프로필 사진 옆과 게시물에 하나만 보여요 · 보유 {owned.length}개</span>
        </div>
        <div style={{ overflowY: 'auto', padding: '0 20px', flex: 1 }}>
          <div onClick={() => setSel(null)} style={{ display: 'flex', alignItems: 'center', gap: 12, padding: '13px 14px', borderRadius: 'var(--radius-tile)', background: 'var(--surface-2)', cursor: 'pointer', marginBottom: 12, border: selName === null ? '2px solid var(--text-accent)' : '2px solid transparent' }}>
            <div style={{ width: 36, height: 36, borderRadius: '50%', background: 'var(--surface-1)', display: 'flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0 }}><i className="ti ti-eye-off" style={{ fontSize: 16, color: 'var(--text-muted)' }}></i></div>
            <span style={{ font: 'var(--text-body-strong)', fontSize: 14 }}>표시하지 않기</span>
            {selName === null && <i className="ti ti-circle-check-filled" style={{ marginLeft: 'auto', fontSize: 19, color: 'var(--text-accent)' }}></i>}
          </div>
          <div style={{ display: 'grid', gridTemplateColumns: 'repeat(3,1fr)', gap: 10 }}>
            {owned.map(b => {
              const S = BADGE_SERIES[b.s], on = selName === b.n;
              return (
                <div key={b.n} onClick={() => setSel(b)} style={{ background: 'var(--surface-2)', borderRadius: 'var(--radius-tile)', padding: '15px 8px 13px', display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 7, cursor: 'pointer', border: on ? '2px solid var(--text-accent)' : '2px solid transparent', position: 'relative' }}>
                  <div style={{ width: 44, height: 44, borderRadius: '50%', background: S.tint, display: 'flex', alignItems: 'center', justifyContent: 'center' }}><i className={'ti ti-' + b.i} style={{ fontSize: 20, color: S.color }}></i></div>
                  <span style={{ font: 'var(--text-micro)', color: 'var(--text-secondary)', textAlign: 'center', lineHeight: 1.35 }}>{b.n}</span>
                  {on && <i className="ti ti-circle-check-filled" style={{ position: 'absolute', top: 6, right: 6, fontSize: 16, color: 'var(--text-accent)' }}></i>}
                </div>
              );
            })}
          </div>
        </div>
        <div style={{ padding: '18px 20px 0', display: 'flex', gap: 10, flexShrink: 0 }}>
          <div onClick={onClose} style={{ flex: 1, textAlign: 'center', background: 'var(--surface-2)', color: 'var(--text-primary)', borderRadius: 999, padding: '15px 0', font: '600 15px/1 var(--font-family)', cursor: 'pointer' }}>취소</div>
          <div onClick={() => onSelect(sel)} style={{ flex: 2, textAlign: 'center', background: 'var(--bg-ink)', color: '#fff', borderRadius: 999, padding: '15px 0', font: '600 15px/1 var(--font-family)', cursor: 'pointer' }}>저장</div>
        </div>
      </div>
    </div>
  );
}
window.FeaturedBadgeSheet = FeaturedBadgeSheet;
