// collections.jsx — User-created collection management
// Sheets: NewCollectionSheet, EditCollectionSheet, CollectionActionsSheet, DeleteConfirmSheet

const CT = window.__cpMakeTheme();

const COLL_LANG_FLAG = {
  ja: '🇯🇵', es: '🇪🇸', en: '🇺🇸', fr: '🇫🇷',
  zh: '🇨🇳', th: '🇹🇭', de: '🇩🇪', it: '🇮🇹',
  vi: '🇻🇳', pt: '🇵🇹', id: '🇮🇩',
};
function collLangFlag(lang) { return COLL_LANG_FLAG[lang] || '🌐'; }

const COLLECTION_COLORS = [
  { value: '#8B6F47', name: '월넛' },
  { value: '#C97B5C', name: '테라코타' },
  { value: '#4A6B4F', name: '포레스트' },
  { value: '#8B5C7A', name: '플럼' },
  { value: '#C9A86A', name: '골든' },
  { value: '#6B5D4A', name: '슬레이트' },
];

// ─────────────────────────────────────────────────────────────
// Cover icon set
// ─────────────────────────────────────────────────────────────
function CoverIcon({ name, size = 36, color = '#FFF' }) {
  const sw = 1.5;
  const p = { stroke: color, strokeWidth: sw, fill: 'none', strokeLinecap: 'round', strokeLinejoin: 'round' };
  const svg = (children) => (
    <svg width={size} height={size} viewBox="0 0 24 24">{children}</svg>
  );
  switch (name) {
    case 'plane':
      return svg(<path {...p} d="M3 13l18-9-7 18-3-7-8-2z"/>);
    case 'fork':
      return svg(<g {...p}>
        <path d="M9 3v8a2 2 0 0 0 4 0V3M11 11v10"/>
        <path d="M17 3c-1.5 0-2.5 1.5-2.5 4s1 4 2.5 4v10"/>
      </g>);
    case 'cup':
      return svg(<g {...p}>
        <path d="M5 8h12v6a5 5 0 0 1-5 5h-2a5 5 0 0 1-5-5V8z"/>
        <path d="M17 10h2a2 2 0 0 1 0 4h-2"/>
        <path d="M8 3c0 1 1 1.5 1 2.5S8 7 8 8M12 3c0 1 1 1.5 1 2.5S12 7 12 8"/>
      </g>);
    case 'map':
      return svg(<g {...p}>
        <path d="M12 21s7-7 7-12a7 7 0 1 0-14 0c0 5 7 12 7 12z"/>
        <circle cx="12" cy="9" r="2.5"/>
      </g>);
    case 'bed':
      return svg(<g {...p}>
        <path d="M3 18v-7a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2v7M3 14h18M3 18h18"/>
        <circle cx="8" cy="11.5" r="1.5"/>
      </g>);
    case 'shop':
      return svg(<g {...p}>
        <path d="M5 9h14l-1 12H6L5 9z"/>
        <path d="M9 9V6a3 3 0 0 1 6 0v3"/>
      </g>);
    case 'globe':
      return svg(<g {...p}>
        <circle cx="12" cy="12" r="9"/>
        <path d="M3 12h18M12 3a14 14 0 0 1 0 18M12 3a14 14 0 0 0 0 18"/>
      </g>);
    case 'heart':
      return svg(<path {...p} d="M12 20s-7-4.5-7-10a4 4 0 0 1 7-2.5A4 4 0 0 1 19 10c0 5.5-7 10-7 10z"/>);
    case 'star':
      return svg(<path {...p} d="M12 3l2.6 5.6L20 9.4l-4 4 1 5.6-5-2.8-5 2.8 1-5.6-4-4 5.4-.8z"/>);
    case 'home':
      return svg(<g {...p}>
        <path d="M3 11l9-7 9 7v9a1 1 0 0 1-1 1h-5v-7h-6v7H4a1 1 0 0 1-1-1v-9z"/>
      </g>);
    case 'umbrella':
      return svg(<g {...p}>
        <path d="M3 11a9 9 0 0 1 18 0H3z"/>
        <path d="M12 11v8a2 2 0 0 1-4 0"/>
      </g>);
    case 'leaf':
      return svg(<g {...p}>
        <path d="M5 19c0-9 6-15 15-15 0 9-6 15-15 15z"/>
        <path d="M5 19c4-4 8-8 12-12"/>
      </g>);
    case 'mountain':
      return svg(<g {...p}>
        <path d="M3 20l5-10 4 6 3-4 6 8H3z"/>
        <circle cx="17" cy="6" r="1.5"/>
      </g>);
    case 'book':
      return svg(<g {...p}>
        <path d="M5 4h7v16H6a1 1 0 0 1-1-1V4z"/>
        <path d="M12 4h7v15a1 1 0 0 1-1 1h-6V4z"/>
      </g>);
    case 'camera':
      return svg(<g {...p}>
        <rect x="3" y="7" width="18" height="13" rx="2"/>
        <circle cx="12" cy="13.5" r="3.5"/>
        <path d="M8 7l1.5-2h5L16 7"/>
      </g>);
    case 'waves':
      return svg(<g {...p}>
        <path d="M3 9c2 0 2-2 4-2s2 2 4 2 2-2 4-2 2 2 4 2 2-2 2-2"/>
        <path d="M3 14c2 0 2-2 4-2s2 2 4 2 2-2 4-2 2 2 4 2 2-2 2-2"/>
        <path d="M3 19c2 0 2-2 4-2s2 2 4 2 2-2 4-2 2 2 4 2 2-2 2-2"/>
      </g>);
    default:
      return null;
  }
}

const COVER_ICONS_LIST = [
  'plane', 'fork', 'cup', 'map', 'bed', 'shop',
  'globe', 'heart', 'star', 'home', 'umbrella', 'leaf',
  'mountain', 'book', 'camera', 'waves',
];

// ─────────────────────────────────────────────────────────────
// CoverArt — used by tile + sheets. Renders by cover.kind.
// ─────────────────────────────────────────────────────────────
function CoverArt({ cover, color, sampleCard, slotId, height = 92, isEmpty }) {
  const kind = cover?.kind || 'auto';

  if (kind === 'icon') {
    return (
      <div style={{
        position: 'relative',
        height,
        background: `linear-gradient(135deg, ${color} 0%, ${shade(color, -22)} 100%)`,
        display: 'flex', alignItems: 'center', justifyContent: 'center',
      }}>
        <CoverIcon name={cover.icon || 'star'} size={Math.round(height * 0.42)} color="#FFF" />
      </div>
    );
  }

  if (kind === 'photo') {
    return (
      <div style={{
        position: 'relative',
        height,
        background: `${color}22`,
      }}>
        <image-slot
          id={slotId}
          shape="rect"
          placeholder="이미지 끌어다 놓기"
          style={{
            display: 'block', width: '100%', height: '100%',
          }}
        />
      </div>
    );
  }

  // 'auto' — pouch with seeds illustration (hamster metaphor)
  return (
    <div style={{
      position: 'relative',
      height,
      background: `linear-gradient(180deg, ${color}12 0%, ${color}04 100%)`,
      overflow: 'hidden',
    }}>
      {/* Background scattered seeds — subtle texture */}
      {!isEmpty && window.SeedCluster && (
        <div style={{
          position: 'absolute', inset: 0, opacity: 0.18, pointerEvents: 'none',
        }}>
          <window.SeedCluster count={8} width={180} height={height}
                              color={shade(color, -30)} layout="scatter" />
        </div>
      )}
      {/* Pouch centerpiece */}
      <div style={{
        position: 'absolute', inset: 0,
        display: 'flex', alignItems: 'center', justifyContent: 'center',
      }}>
        {window.PouchShape && (
          <window.PouchShape size={Math.round(height * 0.78)}
                             fill={color}
                             seeds={isEmpty ? 0 : 4}
                             seedFill={shade(color, -45)} />
        )}
      </div>
      {/* Empty caption */}
      {isEmpty && (
        <div style={{
          position: 'absolute',
          bottom: 6, left: 0, right: 0,
          textAlign: 'center',
          fontSize: 10, color: CT.ink3,
          letterSpacing: '-0.01em', fontStyle: 'italic',
        }}>씨앗을 담아보세요</div>
      )}
    </div>
  );
}

function shade(hex, percent) {
  const h = hex.replace('#', '');
  const r = parseInt(h.slice(0, 2), 16);
  const g = parseInt(h.slice(2, 4), 16);
  const b = parseInt(h.slice(4, 6), 16);
  const adj = (n) => Math.max(0, Math.min(255, Math.round(n + (percent / 100) * 255)));
  return '#' + [adj(r), adj(g), adj(b)].map(n => n.toString(16).padStart(2, '0')).join('');
}

function slotIdFor(name) {
  return 'collcover_' + (name || '').replace(/[^a-zA-Z0-9_가-힣]/g, '_');
}

// ─────────────────────────────────────────────────────────────
// Shared sheet shell
// ─────────────────────────────────────────────────────────────
function CSheet({ children, onClose, maxHeight = '82%' }) {
  return (
    <div style={{
      position: 'absolute', inset: 0, zIndex: 200,
      display: 'flex', flexDirection: 'column', justifyContent: 'flex-end',
    }}>
      <div onClick={onClose} style={{
        position: 'absolute', inset: 0,
        background: 'rgba(31, 24, 18, 0.40)',
        animation: 'cp-fade-bg 0.25s ease',
      }} />
      <div style={{
        position: 'relative',
        background: CT.surface,
        borderTopLeftRadius: 26, borderTopRightRadius: 26,
        paddingBottom: 30,
        maxHeight,
        display: 'flex', flexDirection: 'column',
        boxShadow: '0 -8px 32px rgba(31,24,18,0.16)',
        animation: 'cp-sheet-up 0.32s cubic-bezier(0.22, 1, 0.36, 1)',
      }}>
        <div style={{
          width: 38, height: 4.5, borderRadius: 4,
          background: 'rgba(31,24,18,0.18)',
          margin: '8px auto 14px',
          flexShrink: 0,
        }} />
        {children}
      </div>
    </div>
  );
}

// ─────────────────────────────────────────────────────────────
// Preview tile (matches CollectionTile look)
// ─────────────────────────────────────────────────────────────
function CollectionPreviewTile({ name, color, cover, sampleCard, slotId }) {
  const isEmpty = !sampleCard;
  const c = cover || { kind: 'auto' };
  const onColor = '#FFF';
  const onColorSoft = 'rgba(255,255,255,0.74)';

  return (
    <div style={{
      position: 'relative',
      width: 160, minHeight: 196,
      borderRadius: 16,
      overflow: 'hidden',
      background: c.kind === 'photo' ? '#000'
                : `linear-gradient(155deg, ${color} 0%, ${shade(color, -18)} 100%)`,
      boxShadow: '0 4px 18px rgba(31,24,18,0.10), 0 1px 0 rgba(255,255,255,0.16) inset',
      display: 'flex', flexDirection: 'column',
    }}>
      {/* Photo bg layer */}
      {c.kind === 'photo' && (
        <div style={{ position: 'absolute', inset: 0, pointerEvents: 'none' }}>
          <image-slot
            id={slotId}
            shape="rect"
            placeholder=""
            style={{ display: 'block', width: '100%', height: '100%', pointerEvents: 'none' }}
          />
          <div style={{
            position: 'absolute', inset: 0,
            background: 'linear-gradient(180deg, rgba(0,0,0,0.10) 0%, rgba(0,0,0,0.60) 100%)',
          }} />
        </div>
      )}

      {/* Decorative quote for auto mode */}
      {c.kind === 'auto' && !isEmpty && (
        <div style={{
          position: 'absolute',
          top: -16, right: -6,
          fontSize: 120, lineHeight: 1,
          color: 'rgba(255,255,255,0.12)',
          fontFamily: 'Georgia, "Times New Roman", serif',
          fontWeight: 700,
          pointerEvents: 'none',
          userSelect: 'none',
        }}>"</div>
      )}

      {/* Top: count badge */}
      <div style={{
        position: 'relative', zIndex: 1,
        padding: '12px 14px',
        display: 'flex', alignItems: 'center', justifyContent: 'space-between',
      }}>
        <div style={{
          height: 22, padding: '0 10px', borderRadius: 999,
          background: 'rgba(0,0,0,0.18)',
          display: 'inline-flex', alignItems: 'center', gap: 3,
          fontSize: 11.5, fontWeight: 600, color: onColor,
          letterSpacing: '-0.01em',
          backdropFilter: 'blur(8px)',
        }}>
          <span>{isEmpty ? 0 : 1}</span>
          <span style={{ opacity: 0.7, fontSize: 10 }}>개</span>
        </div>
      </div>

      {/* Middle hero */}
      {c.kind === 'icon' && (
        <div style={{
          position: 'relative', zIndex: 1,
          flex: 1,
          display: 'flex', alignItems: 'center', justifyContent: 'center',
        }}>
          <CoverIcon name={c.icon || 'star'} size={42} color={onColor} />
        </div>
      )}

      {c.kind === 'auto' && (
        <div style={{
          position: 'relative', zIndex: 1,
          flex: 1,
          padding: '4px 14px 6px',
          display: 'flex', alignItems: 'center',
        }}>
          {isEmpty ? (
            <div style={{
              fontSize: 13, color: onColorSoft,
              letterSpacing: '-0.01em', fontStyle: 'italic',
              lineHeight: 1.4,
            }}>표현을<br/>담아보세요</div>
          ) : (
            <div style={{
              fontFamily: sampleCard?.lang === 'ja' ? '"Noto Sans JP", Pretendard' : 'Noto Sans, Pretendard',
              fontSize: 13.5, fontWeight: 500, color: onColor,
              letterSpacing: '-0.01em', lineHeight: 1.35,
              display: '-webkit-box',
              WebkitLineClamp: 3, WebkitBoxOrient: 'vertical',
              overflow: 'hidden',
              textWrap: 'balance',
            }}>{sampleCard.foreign}</div>
          )}
        </div>
      )}

      {c.kind === 'photo' && (
        <div style={{ flex: 1 }} />
      )}

      {/* Bottom: divider + title + meta */}
      <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: 15, fontWeight: 700, color: onColor,
          letterSpacing: '-0.02em', lineHeight: 1.2,
          display: '-webkit-box',
          WebkitLineClamp: 2, WebkitBoxOrient: 'vertical',
          overflow: 'hidden',
          minHeight: 18,
        }}>{name || '주머니 이름'}</div>
        <div style={{
          marginTop: 4,
          fontSize: 11, color: onColorSoft, letterSpacing: '-0.01em',
        }}>비어있음</div>
      </div>
    </div>
  );
}

// ─────────────────────────────────────────────────────────────
// CoverEditor — segmented selector + per-kind picker
// ─────────────────────────────────────────────────────────────
function CoverEditor({ cover, color, onChange, slotId }) {
  const kind = cover?.kind || 'auto';
  return (
    <div>
      <div style={{
        display: 'flex', gap: 2, padding: 3, borderRadius: 10,
        background: 'rgba(60,42,20,0.06)',
      }}>
        {[
          { value: 'auto',  label: '자동' },
          { value: 'icon',  label: '아이콘' },
          { value: 'photo', label: '사진' },
        ].map(o => {
          const on = kind === o.value;
          return (
            <button key={o.value} onClick={() => onChange({ ...cover, kind: o.value })} style={{
              flex: 1, height: 32, border: 'none', borderRadius: 7,
              background: on ? CT.surface2 : 'transparent',
              boxShadow: on ? '0 1px 2px rgba(31,24,18,0.08)' : 'none',
              fontFamily: 'inherit', fontSize: 13, fontWeight: 500,
              color: on ? CT.ink : CT.ink2,
              cursor: 'pointer', letterSpacing: '-0.01em',
              WebkitTapHighlightColor: 'transparent',
            }}>{o.label}</button>
          );
        })}
      </div>

      {kind === 'auto' && (
        <div style={{
          marginTop: 14, padding: '12px 14px',
          background: 'rgba(60,42,20,0.04)',
          borderRadius: 12,
          fontSize: 12.5, color: CT.ink2,
          letterSpacing: '-0.01em', lineHeight: 1.5,
        }}>주머니 안의 츠드가 쌓여있는 모습으로 표지가 만들어져요.</div>
      )}

      {kind === 'icon' && (
        <div style={{
          marginTop: 14,
          display: 'grid', gridTemplateColumns: 'repeat(6, 1fr)', gap: 8,
        }}>
          {COVER_ICONS_LIST.map(n => {
            const on = cover?.icon === n;
            return (
              <button key={n} onClick={() => onChange({ ...cover, kind: 'icon', icon: n })}
                style={{
                  aspectRatio: '1', padding: 0,
                  borderRadius: 10,
                  background: on ? `linear-gradient(135deg, ${color} 0%, ${shade(color, -22)} 100%)` : 'rgba(60,42,20,0.05)',
                  border: on ? 'none' : `0.5px solid ${CT.hairline}`,
                  display: 'flex', alignItems: 'center', justifyContent: 'center',
                  cursor: 'pointer',
                  transition: 'all 0.15s ease',
                  WebkitTapHighlightColor: 'transparent',
                }}>
                <CoverIcon name={n} size={20} color={on ? '#FFF' : CT.ink2} />
              </button>
            );
          })}
        </div>
      )}

      {kind === 'photo' && (
        <div style={{
          marginTop: 14,
          borderRadius: 12, overflow: 'hidden',
          border: `1px dashed ${CT.hairlineStrong}`,
          background: `${color}10`,
        }}>
          <image-slot
            id={slotId}
            shape="rect"
            placeholder="사진을 이 자리에 끌어다 놓아주세요"
            style={{
              display: 'block', width: '100%', height: '140px',
            }}
          />
        </div>
      )}
    </div>
  );
}

// ─────────────────────────────────────────────────────────────
// Color picker grid
// ─────────────────────────────────────────────────────────────
function ColorPickerGrid({ value, onChange }) {
  return (
    <div style={{
      display: 'flex', gap: 10, flexWrap: 'wrap',
    }}>
      {COLLECTION_COLORS.map(c => {
        const on = c.value === value;
        return (
          <button key={c.value} onClick={() => onChange(c.value)}
            aria-label={c.name}
            style={{
              width: 40, height: 40, borderRadius: 40, padding: 0,
              background: c.value,
              border: 'none',
              outline: on ? `2.5px solid ${CT.ink}` : 'none',
              outlineOffset: on ? 2 : 0,
              cursor: 'pointer',
              display: 'inline-flex', alignItems: 'center', justifyContent: 'center',
              transition: 'transform 0.12s ease',
              WebkitTapHighlightColor: 'transparent',
            }}>
            {on && (
              <svg width="14" height="14" viewBox="0 0 14 14" fill="none">
                <path d="M3 7.2l2.8 2.8L11 5" stroke="#FFF" strokeWidth="2.4"
                      strokeLinecap="round" strokeLinejoin="round"/>
              </svg>
            )}
          </button>
        );
      })}
    </div>
  );
}

// ─────────────────────────────────────────────────────────────
// NewCollectionSheet — create from scratch
// ─────────────────────────────────────────────────────────────
function NewCollectionSheet({ accent, suggestedNames = [], existingNames = [], onClose, onCreate }) {
  const [name, setName] = React.useState('');
  const [color, setColor] = React.useState(COLLECTION_COLORS[0].value);
  const [cover, setCover] = React.useState({ kind: 'auto' });
  const inputRef = React.useRef(null);
  const slotId = React.useMemo(() => 'new_collcover_' + Date.now(), []);

  React.useEffect(() => {
    if (inputRef.current) {
      const t = setTimeout(() => inputRef.current.focus(), 280);
      return () => clearTimeout(t);
    }
  }, []);

  const trimmed = name.trim();
  const dup = trimmed && existingNames.includes(trimmed);
  const canCreate = trimmed.length > 0 && !dup;

  const submit = () => {
    if (!canCreate) return;
    onCreate({ name: trimmed, color, cover, slotId });
  };

  return (
    <CSheet onClose={onClose}>
      <div style={{ padding: '0 24px 4px' }}>
        <div style={{
          fontSize: 19, fontWeight: 700, color: CT.ink,
          letterSpacing: '-0.02em', lineHeight: 1.2,
        }}>새 주머니</div>
        <div style={{
          marginTop: 6,
          fontSize: 13, color: CT.ink2, letterSpacing: '-0.01em', lineHeight: 1.5,
        }}>표현들을 폴더처럼 묶어 보관해요. 여행 단위로 만들면 다시 찾기 쉬워요.</div>
      </div>

      <div style={{ overflowY: 'auto', paddingBottom: 8 }}>
        {/* Preview */}
        <div style={{
          margin: '20px 0 4px',
          padding: '0 24px',
          display: 'flex', justifyContent: 'center',
        }}>
          <CollectionPreviewTile name={trimmed} color={color} cover={cover} slotId={slotId} />
        </div>

        {/* Name */}
        <div style={{ padding: '20px 24px 0' }}>
          <div style={{
            fontSize: 11, fontWeight: 600, color: CT.ink3,
            letterSpacing: '0.08em', textTransform: 'uppercase',
            marginBottom: 8,
          }}>이름</div>
          <input
            ref={inputRef}
            value={name}
            onChange={(e) => setName(e.target.value)}
            onKeyDown={(e) => { if (e.key === 'Enter') submit(); }}
            placeholder="예: 다음 주 도쿄 여행, 엄마와의 통화"
            maxLength={30}
            style={{
              width: '100%', height: 48, borderRadius: 12,
              background: CT.surface2,
              border: `1px solid ${dup ? CT.danger : CT.hairlineStrong}`,
              padding: '0 14px',
              fontFamily: 'inherit', fontSize: 16, color: CT.ink,
              outline: 'none',
              letterSpacing: '-0.01em',
              boxSizing: 'border-box',
            }}
          />
          {dup && (
            <div style={{
              marginTop: 8, fontSize: 12, color: CT.danger,
              letterSpacing: '-0.01em',
            }}>같은 이름의 주머니가 이미 있어요</div>
          )}
          {!dup && suggestedNames.length > 0 && !trimmed && (
            <div style={{ marginTop: 12 }}>
              <div style={{
                fontSize: 11, fontWeight: 600, color: CT.ink3,
                letterSpacing: '0.06em', textTransform: 'uppercase',
                marginBottom: 8,
              }}>이런 주머니는 어때요</div>
              <div style={{ display: 'flex', gap: 8, flexWrap: 'wrap' }}>
                {suggestedNames.map(s => (
                  <button key={s} onClick={() => setName(s)} style={{
                    height: 30, padding: '0 12px', borderRadius: 999,
                    background: CT.surface2,
                    border: `0.5px solid ${CT.hairlineStrong}`,
                    fontFamily: 'inherit', fontSize: 12.5, color: CT.ink2,
                    cursor: 'pointer', letterSpacing: '-0.01em',
                    WebkitTapHighlightColor: 'transparent',
                  }}>{s}</button>
                ))}
              </div>
            </div>
          )}
        </div>

        {/* Color */}
        <div style={{ padding: '24px 24px 0' }}>
          <div style={{
            fontSize: 11, fontWeight: 600, color: CT.ink3,
            letterSpacing: '0.08em', textTransform: 'uppercase',
            marginBottom: 12,
          }}>색상</div>
          <ColorPickerGrid value={color} onChange={setColor} />
        </div>

        {/* Cover */}
        <div style={{ padding: '24px 24px 0' }}>
          <div style={{
            fontSize: 11, fontWeight: 600, color: CT.ink3,
            letterSpacing: '0.08em', textTransform: 'uppercase',
            marginBottom: 12,
          }}>표지</div>
          <CoverEditor cover={cover} color={color}
                       onChange={setCover} slotId={slotId} />
        </div>
      </div>

      {/* Action bar */}
      <div style={{
        padding: '16px 22px 0',
        display: 'flex', gap: 10, flexShrink: 0,
      }}>
        <button onClick={onClose} style={{
          flex: 1, height: 48, borderRadius: 14,
          background: 'transparent', color: CT.ink2,
          border: `1px solid ${CT.hairlineStrong}`,
          fontFamily: 'inherit', fontSize: 15, fontWeight: 500,
          cursor: 'pointer',
          WebkitTapHighlightColor: 'transparent',
        }}>취소</button>
        <button onClick={submit} disabled={!canCreate} style={{
          flex: 2, height: 48, borderRadius: 14,
          background: canCreate ? color : `${color}40`,
          color: '#FFF', border: 'none',
          fontFamily: 'inherit', fontSize: 15, fontWeight: 600,
          letterSpacing: '-0.01em',
          cursor: canCreate ? 'pointer' : 'default',
          WebkitTapHighlightColor: 'transparent',
        }}>주머니 만들기</button>
      </div>
    </CSheet>
  );
}

// ─────────────────────────────────────────────────────────────
// EditCollectionSheet — rename + recolor
// ─────────────────────────────────────────────────────────────
function EditCollectionSheet({ collection, accent, existingNames = [], onClose, onSave }) {
  const [name, setName] = React.useState(collection.name);
  const [color, setColor] = React.useState(collection.color);
  const [cover, setCover] = React.useState(collection.cover || { kind: 'auto' });
  const inputRef = React.useRef(null);
  const slotId = slotIdFor(collection.name);

  React.useEffect(() => {
    if (inputRef.current) {
      const t = setTimeout(() => {
        inputRef.current.focus();
        inputRef.current.select();
      }, 280);
      return () => clearTimeout(t);
    }
  }, []);

  const trimmed = name.trim();
  const dup = trimmed && trimmed !== collection.name && existingNames.includes(trimmed);
  const canSave = trimmed.length > 0 && !dup;
  const sampleCard = collection.cards && collection.cards[0];

  return (
    <CSheet onClose={onClose}>
      <div style={{ padding: '0 24px 4px' }}>
        <div style={{
          fontSize: 19, fontWeight: 700, color: CT.ink,
          letterSpacing: '-0.02em',
        }}>주머니 편집</div>
      </div>

      <div style={{ overflowY: 'auto', paddingBottom: 8 }}>
        <div style={{
          margin: '20px 0 4px',
          padding: '0 24px',
          display: 'flex', justifyContent: 'center',
        }}>
          <CollectionPreviewTile name={trimmed}
                                 color={color}
                                 cover={cover}
                                 sampleCard={sampleCard}
                                 slotId={slotId} />
        </div>

        <div style={{ padding: '20px 24px 0' }}>
          <div style={{
            fontSize: 11, fontWeight: 600, color: CT.ink3,
            letterSpacing: '0.08em', textTransform: 'uppercase',
            marginBottom: 8,
          }}>이름</div>
          <input
            ref={inputRef}
            value={name}
            onChange={(e) => setName(e.target.value)}
            onKeyDown={(e) => { if (e.key === 'Enter' && canSave) onSave({ name: trimmed, color, cover }); }}
            maxLength={30}
            style={{
              width: '100%', height: 48, borderRadius: 12,
              background: CT.surface2,
              border: `1px solid ${dup ? CT.danger : CT.hairlineStrong}`,
              padding: '0 14px',
              fontFamily: 'inherit', fontSize: 16, color: CT.ink,
              outline: 'none', letterSpacing: '-0.01em',
              boxSizing: 'border-box',
            }}
          />
          {dup && (
            <div style={{
              marginTop: 8, fontSize: 12, color: CT.danger,
              letterSpacing: '-0.01em',
            }}>같은 이름의 주머니가 이미 있어요</div>
          )}
        </div>

        <div style={{ padding: '24px 24px 0' }}>
          <div style={{
            fontSize: 11, fontWeight: 600, color: CT.ink3,
            letterSpacing: '0.08em', textTransform: 'uppercase',
            marginBottom: 12,
          }}>색상</div>
          <ColorPickerGrid value={color} onChange={setColor} />
        </div>

        <div style={{ padding: '24px 24px 0' }}>
          <div style={{
            fontSize: 11, fontWeight: 600, color: CT.ink3,
            letterSpacing: '0.08em', textTransform: 'uppercase',
            marginBottom: 12,
          }}>표지</div>
          <CoverEditor cover={cover} color={color}
                       onChange={setCover} slotId={slotId} />
        </div>
      </div>

      <div style={{
        padding: '16px 22px 0',
        display: 'flex', gap: 10, flexShrink: 0,
      }}>
        <button onClick={onClose} style={{
          flex: 1, height: 48, borderRadius: 14,
          background: 'transparent', color: CT.ink2,
          border: `1px solid ${CT.hairlineStrong}`,
          fontFamily: 'inherit', fontSize: 15, fontWeight: 500,
          cursor: 'pointer',
          WebkitTapHighlightColor: 'transparent',
        }}>취소</button>
        <button onClick={() => canSave && onSave({ name: trimmed, color, cover })}
                disabled={!canSave} style={{
          flex: 2, height: 48, borderRadius: 14,
          background: canSave ? color : `${color}40`,
          color: '#FFF', border: 'none',
          fontFamily: 'inherit', fontSize: 15, fontWeight: 600,
          letterSpacing: '-0.01em',
          cursor: canSave ? 'pointer' : 'default',
          WebkitTapHighlightColor: 'transparent',
        }}>저장</button>
      </div>
    </CSheet>
  );
}

// ─────────────────────────────────────────────────────────────
// CollectionActionsSheet — ⋯ menu
// ─────────────────────────────────────────────────────────────
function CollectionActionsSheet({ collection, pinned, onClose, onEdit, onManageCards, onCleanup, onShare, onTogglePin, onDelete }) {
  return (
    <CSheet onClose={onClose} maxHeight="auto">
      <div style={{
        padding: '0 24px 8px',
        textAlign: 'center',
      }}>
        <div style={{
          fontSize: 15, fontWeight: 600, color: CT.ink,
          letterSpacing: '-0.01em',
        }}>{collection.name}</div>
        <div style={{
          marginTop: 4,
          fontSize: 12, color: CT.ink3, letterSpacing: '-0.01em',
        }}>{collection.count}개</div>
      </div>

      <div style={{
        margin: '12px 16px 0',
        background: CT.surface2,
        borderRadius: 14,
        border: `0.5px solid ${CT.hairline}`,
        overflow: 'hidden',
      }}>
        <ActionRow icon={<EditGlyph />} label="이름·색상·표지 편집" onClick={onEdit} />
        <ActionRow icon={<MoveGlyph />} label="씨앗 추가·제거"
                   onClick={onManageCards} />
        <ActionRow icon={<CleanupGlyph />} label="씨앗 정리 모드"
                   onClick={onCleanup} />
        <ActionRow icon={<PinGlyph size={15} color={CT.ink} />}
                   label={pinned ? '핀 해제' : '홈에 고정'}
                   onClick={onTogglePin} />
        <ActionRow icon={<ShareGlyph />} label="공유"
                   onClick={onShare} />
      </div>

      <div style={{
        margin: '10px 16px 0',
        background: CT.surface2,
        borderRadius: 14,
        border: `0.5px solid ${CT.hairline}`,
        overflow: 'hidden',
      }}>
        <ActionRow icon={<TrashGlyph color={CT.danger} />}
                   label="주머니 삭제"
                   danger
                   onClick={onDelete} />
      </div>
    </CSheet>
  );
}

function CleanupGlyph() {
  return (
    <svg width="15" height="15" viewBox="0 0 16 16" fill="none">
      <path d="M3 7h10M3 11h10M5 4h6M6 14h4" stroke={CT.ink} strokeWidth="1.4"
            strokeLinecap="round"/>
    </svg>
  );
}

function ActionRow({ icon, label, onClick, danger, disabled }) {
  return (
    <button onClick={disabled ? undefined : onClick} style={{
      width: '100%',
      display: 'flex', alignItems: 'center', gap: 14,
      minHeight: 50,
      padding: '12px 16px',
      border: 'none',
      borderTop: 'inherit',
      background: 'transparent',
      cursor: disabled ? 'default' : 'pointer',
      textAlign: 'left',
      fontFamily: 'inherit',
      opacity: disabled ? 0.42 : 1,
      WebkitTapHighlightColor: 'transparent',
    }}>
      <span style={{
        width: 28, height: 28, borderRadius: 8,
        background: danger ? `${CT.danger}14` : 'rgba(60,42,20,0.06)',
        display: 'inline-flex', alignItems: 'center', justifyContent: 'center',
        flexShrink: 0,
      }}>{icon}</span>
      <span style={{
        flex: 1,
        fontSize: 15, fontWeight: 500,
        color: danger ? CT.danger : CT.ink,
        letterSpacing: '-0.01em',
      }}>{label}</span>
      {disabled && (
        <span style={{
          fontSize: 11, color: CT.ink3,
          padding: '3px 7px', borderRadius: 4,
          background: 'rgba(60,42,20,0.06)',
        }}>준비 중</span>
      )}
    </button>
  );
}

// ─────────────────────────────────────────────────────────────
// DeleteConfirmSheet
// ─────────────────────────────────────────────────────────────
function DeleteConfirmSheet({ collection, onClose, onConfirm }) {
  return (
    <CSheet onClose={onClose} maxHeight="auto">
      <div style={{ padding: '4px 24px 0', textAlign: 'center' }}>
        <div style={{
          width: 56, height: 56, borderRadius: 56,
          background: `${CT.danger}14`,
          display: 'inline-flex', alignItems: 'center', justifyContent: 'center',
          marginBottom: 12,
        }}>
          <TrashGlyph color={CT.danger} size={22} />
        </div>
        <div style={{
          fontSize: 19, fontWeight: 700, color: CT.ink,
          letterSpacing: '-0.02em', lineHeight: 1.3,
        }}>"{collection.name}"<br/>주머니를 삭제할까요?</div>
        <div style={{
          marginTop: 10,
          fontSize: 13.5, color: CT.ink2,
          letterSpacing: '-0.01em', lineHeight: 1.5,
        }}>
          {collection.count > 0
            ? <>안에 있는 <strong style={{ color: CT.ink }}>{collection.count}개의 씨앗</strong>은 그대로 두고 주머니만 사라져요.<br/>전체 씨앗 화면에서 다시 찾을 수 있어요.</>
            : '되돌릴 수 없어요.'}
        </div>
      </div>

      <div style={{
        padding: '24px 22px 0',
        display: 'flex', flexDirection: 'column', gap: 8,
      }}>
        <button onClick={onConfirm} style={{
          height: 50, borderRadius: 14,
          background: CT.danger, color: '#FFF',
          border: 'none',
          fontFamily: 'inherit', fontSize: 15, fontWeight: 600,
          letterSpacing: '-0.01em',
          cursor: 'pointer',
          WebkitTapHighlightColor: 'transparent',
        }}>삭제</button>
        <button onClick={onClose} style={{
          height: 50, borderRadius: 14,
          background: 'transparent', color: CT.ink,
          border: 'none',
          fontFamily: 'inherit', fontSize: 15, fontWeight: 500,
          letterSpacing: '-0.01em',
          cursor: 'pointer',
          WebkitTapHighlightColor: 'transparent',
        }}>취소</button>
      </div>
    </CSheet>
  );
}

// ─────────────────────────────────────────────────────────────
// Glyphs
// ─────────────────────────────────────────────────────────────
function EditGlyph() {
  return (
    <svg width="15" height="15" viewBox="0 0 16 16" fill="none">
      <path d="M11 2.5l2.5 2.5L5 13.5H2.5V11L11 2.5Z" stroke={CT.ink} strokeWidth="1.4"
            strokeLinecap="round" strokeLinejoin="round"/>
    </svg>
  );
}
function MoveGlyph() {
  return (
    <svg width="15" height="15" viewBox="0 0 16 16" fill="none">
      <path d="M2 4.5L4 2.5h4l1.5 1.5h4.5v9h-12V4.5z" stroke={CT.ink} strokeWidth="1.4"
            strokeLinejoin="round" fill="none"/>
      <path d="M8 7v4M6 9l2 2 2-2" stroke={CT.ink} strokeWidth="1.4"
            strokeLinecap="round" strokeLinejoin="round"/>
    </svg>
  );
}
function ShareGlyph() {
  return (
    <svg width="15" height="15" viewBox="0 0 16 16" fill="none">
      <path d="M8 2v8" stroke={CT.ink} strokeWidth="1.4" strokeLinecap="round"/>
      <path d="M5 5l3-3 3 3" stroke={CT.ink} strokeWidth="1.4"
            strokeLinecap="round" strokeLinejoin="round" fill="none"/>
      <path d="M3 9v4h10V9" stroke={CT.ink} strokeWidth="1.4"
            strokeLinecap="round" strokeLinejoin="round" fill="none"/>
    </svg>
  );
}
function TrashGlyph({ color = CT.ink, size = 15 }) {
  return (
    <svg width={size} height={size} viewBox="0 0 16 16" fill="none">
      <path d="M3 4.5h10M6 4.5V3h4v1.5M4.5 4.5l.5 9h6l.5-9M7 7v4M9 7v4"
            stroke={color} strokeWidth="1.4"
            strokeLinecap="round" strokeLinejoin="round" fill="none"/>
    </svg>
  );
}

// ─────────────────────────────────────────────────────────────
// "Add Collection" tile (used in home grid)
// ─────────────────────────────────────────────────────────────
function AddCollectionTile({ onClick, accent }) {
  return (
    <button onClick={onClick} style={{
      padding: 0,
      background: 'transparent',
      border: `1.5px dashed ${CT.hairlineStrong}`,
      borderRadius: 16,
      cursor: 'pointer',
      fontFamily: 'inherit',
      textAlign: 'left',
      WebkitTapHighlightColor: 'transparent',
      overflow: 'hidden',
      minHeight: 184,
      display: 'flex', flexDirection: 'column',
      alignItems: 'center', justifyContent: 'center', gap: 10,
      color: CT.ink2,
      transition: 'all 0.15s ease',
    }}>
      <span style={{
        width: 60, height: 60,
        display: 'inline-flex', alignItems: 'center', justifyContent: 'center',
        position: 'relative',
      }}>
        {window.PouchShape && (
          <window.PouchShape size={56}
                             fill="transparent"
                             cord={accent}
                             shade="transparent"
                             highlight="transparent"
                             seeds={0} />
        )}
        {/* Outline-only pouch via stroke overlay */}
        <svg width="56" height="56" viewBox="0 0 64 64" fill="none"
             style={{ position: 'absolute', inset: 0 }}>
          <path d="M22 19 C12 22, 8 34, 11 46 C13 55, 22 60, 32 60 C42 60, 51 55, 53 46 C56 34, 52 22, 42 19 Z"
                stroke={accent} strokeWidth="1.6" fill="none" strokeLinejoin="round"/>
          <rect x="20" y="14" width="24" height="8" rx="3" stroke={accent} strokeWidth="1.6" fill="none"/>
          <path d="M24 14 C22 9, 18 8, 16 11" stroke={accent} strokeWidth="1.6" strokeLinecap="round" fill="none"/>
          <path d="M40 14 C42 9, 46 8, 48 11" stroke={accent} strokeWidth="1.6" strokeLinecap="round" fill="none"/>
          {/* + sign inside */}
          <path d="M32 32 v12 M26 38 h12" stroke={accent} strokeWidth="2.2" strokeLinecap="round"/>
        </svg>
      </span>
      <div style={{
        fontSize: 13, fontWeight: 600, color: CT.ink2,
        letterSpacing: '-0.01em',
      }}>새 주머니</div>
    </button>
  );
}

// ─────────────────────────────────────────────────────────────
// Empty state for a collection with no cards
// ─────────────────────────────────────────────────────────────
function EmptyCollectionBody({ accent, color, onAddCard }) {
  return (
    <div style={{
      margin: '32px 22px 0',
      padding: '40px 22px',
      background: CT.surface,
      border: `0.5px dashed ${CT.hairlineStrong}`,
      borderRadius: 18,
      textAlign: 'center',
      display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 14,
    }}>
      <div style={{
        width: 88, height: 88,
        display: 'inline-flex', alignItems: 'center', justifyContent: 'center',
      }}>
        {window.PouchWithSeeds
          ? <window.PouchWithSeeds size={88} fill={color || accent} empty={true} />
          : <svg width="32" height="32" viewBox="0 0 24 24" fill="none">
              <rect x="3" y="6" width="18" height="13" rx="2.5" stroke={color || accent} strokeWidth="1.6" fill="none"/>
            </svg>}
      </div>
      <div>
        <div style={{
          fontSize: 16, fontWeight: 600, color: CT.ink,
          letterSpacing: '-0.02em',
        }}>아직 씨앗이 없어요</div>
        <div style={{
          marginTop: 5,
          fontSize: 13, color: CT.ink2,
          letterSpacing: '-0.01em', lineHeight: 1.45,
          maxWidth: 240,
        }}>새 표현을 담아 이 주머니를 시작해보세요</div>
      </div>
      <button onClick={onAddCard} style={{
        height: 42, padding: '0 18px', borderRadius: 999,
        background: color || accent, color: '#FFF',
        border: 'none', fontFamily: 'inherit',
        fontSize: 14, fontWeight: 600, letterSpacing: '-0.01em',
        cursor: 'pointer',
        display: 'inline-flex', alignItems: 'center', gap: 6,
        WebkitTapHighlightColor: 'transparent',
      }}>
        <svg width="13" height="13" viewBox="0 0 14 14" fill="none">
          <path d="M7 2v10M2 7h10" stroke="#FFF" strokeWidth="1.8" strokeLinecap="round"/>
        </svg>
        첫 씨앗 담기
      </button>
    </div>
  );
}

// ─────────────────────────────────────────────────────────────
// CardPickerSheet — pick existing cards to add to a collection
// ─────────────────────────────────────────────────────────────
function CardPickerSheet({ collection, allCards, accent, onClose, onConfirm }) {
  const [q, setQ] = React.useState('');
  const [picked, setPicked] = React.useState(new Set());
  const inputRef = React.useRef(null);
  React.useEffect(() => {
    const t = setTimeout(() => inputRef.current && inputRef.current.focus(), 280);
    return () => clearTimeout(t);
  }, []);

  const inThis = new Set((collection.cards || []).map(c => c.id));
  const candidates = React.useMemo(() => {
    const query = q.trim().toLowerCase();
    return allCards.filter(c => {
      if (inThis.has(c.id)) return false;
      if (!query) return true;
      return c.foreign.toLowerCase().includes(query) ||
             c.korean.toLowerCase().includes(query) ||
             c.pronunciation.toLowerCase().includes(query) ||
             (c.group || '').toLowerCase().includes(query);
    });
  }, [allCards, q, collection]);

  // Group candidates by source collection
  const grouped = React.useMemo(() => {
    const map = new Map();
    for (const c of candidates) {
      const key = c.group || '미분류';
      if (!map.has(key)) map.set(key, []);
      map.get(key).push(c);
    }
    return [...map.entries()];
  }, [candidates]);

  const toggle = (id) => {
    const next = new Set(picked);
    if (next.has(id)) next.delete(id); else next.add(id);
    setPicked(next);
  };

  return (
    <CSheet onClose={onClose} maxHeight="90%">
      <div style={{ padding: '0 24px 4px' }}>
        <div style={{
          fontSize: 19, fontWeight: 700, color: CT.ink,
          letterSpacing: '-0.02em', lineHeight: 1.2,
        }}>"{collection.name}"에 추가</div>
        <div style={{
          marginTop: 6,
          fontSize: 13, color: CT.ink2, letterSpacing: '-0.01em', lineHeight: 1.5,
        }}>다른 주머니가나 미분류 씨앗에서 골라보세요.<br/>선택한 씨앗은 이 주머니로 옮겨져요.</div>
      </div>

      <div style={{ padding: '16px 22px 0' }}>
        <div style={{
          height: 44, borderRadius: 12,
          background: CT.surface2,
          border: `0.5px solid ${CT.hairlineStrong}`,
          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={CT.ink3} strokeWidth="1.6"/>
            <path d="M13.6 13.6L17 17" stroke={CT.ink3} strokeWidth="1.6" strokeLinecap="round"/>
          </svg>
          <input
            ref={inputRef}
            value={q}
            onChange={(e) => setQ(e.target.value)}
            placeholder="씨앗 검색"
            style={{
              flex: 1, height: '100%',
              background: 'transparent', border: 'none', outline: 'none',
              fontFamily: 'inherit', fontSize: 15, color: CT.ink,
              letterSpacing: '-0.01em',
            }}
          />
        </div>
      </div>

      <div style={{ flex: 1, overflowY: 'auto', padding: '14px 12px 8px', minHeight: 200 }}>
        {grouped.length === 0 ? (
          <div style={{
            margin: '20px 12px',
            padding: '40px 18px',
            border: '1px dashed rgba(60,42,20,0.18)',
            borderRadius: 14,
            textAlign: 'center',
            color: CT.ink3, fontSize: 14,
          }}>
            {q ? `"${q}"에 대한 결과가 없어요` : '추가할 수 있는 씨앗이 없어요'}
          </div>
        ) : grouped.map(([groupName, list]) => (
          <div key={groupName} style={{ marginBottom: 10 }}>
            <div style={{
              padding: '8px 10px 6px',
              fontSize: 11, fontWeight: 600, color: CT.ink3,
              letterSpacing: '0.06em', textTransform: 'uppercase',
            }}>{groupName} · {list.length}개</div>
            <div style={{ display: 'flex', flexDirection: 'column', gap: 4 }}>
              {list.map(c => {
                const on = picked.has(c.id);
                return (
                  <button key={c.id} onClick={() => toggle(c.id)} style={{
                    width: '100%',
                    background: on ? `${accent}10` : 'transparent',
                    border: 'none',
                    borderRadius: 10,
                    padding: '10px 12px',
                    display: 'flex', alignItems: 'flex-start', gap: 11,
                    cursor: 'pointer', textAlign: 'left',
                    fontFamily: 'inherit',
                    WebkitTapHighlightColor: 'transparent',
                  }}>
                    <div style={{
                      width: 22, height: 22, borderRadius: 22,
                      marginTop: 1,
                      background: on ? accent : 'transparent',
                      border: on ? 'none' : `1.5px solid ${CT.hairlineStrong}`,
                      display: 'inline-flex', alignItems: 'center', justifyContent: 'center',
                      flexShrink: 0,
                    }}>
                      {on && (
                        <svg width="11" height="11" viewBox="0 0 14 14" fill="none">
                          <path d="M3 7.2l2.8 2.8L11 5" stroke="#FFF" strokeWidth="2.4"
                                strokeLinecap="round" strokeLinejoin="round"/>
                        </svg>
                      )}
                    </div>
                    <div style={{ flex: 1, minWidth: 0 }}>
                      <div style={{
                        fontFamily: c.lang === 'ja' ? '"Noto Sans JP", Pretendard' : 'Noto Sans, Pretendard',
                        fontSize: 14, fontWeight: 500, color: CT.ink,
                        letterSpacing: '-0.01em', lineHeight: 1.35,
                        overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap',
                      }}>{c.foreign}</div>
                      <div style={{
                        marginTop: 2,
                        fontSize: 12, color: CT.ink3, letterSpacing: '-0.01em',
                        overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap',
                      }}>{c.korean}</div>
                    </div>
                  </button>
                );
              })}
            </div>
          </div>
        ))}
      </div>

      <div style={{
        padding: '12px 22px 0',
        display: 'flex', gap: 10, flexShrink: 0,
        borderTop: `0.5px solid ${CT.hairline}`,
      }}>
        <button onClick={onClose} style={{
          flex: 1, height: 48, borderRadius: 14,
          background: 'transparent', color: CT.ink2,
          border: `1px solid ${CT.hairlineStrong}`,
          fontFamily: 'inherit', fontSize: 15, fontWeight: 500,
          cursor: 'pointer',
          WebkitTapHighlightColor: 'transparent',
          marginTop: 12,
        }}>취소</button>
        <button onClick={() => picked.size > 0 && onConfirm([...picked])}
                disabled={picked.size === 0} style={{
          flex: 2, height: 48, borderRadius: 14,
          background: picked.size > 0 ? accent : `${accent}30`,
          color: '#FFF', border: 'none',
          fontFamily: 'inherit', fontSize: 15, fontWeight: 600,
          letterSpacing: '-0.01em',
          cursor: picked.size > 0 ? 'pointer' : 'default',
          WebkitTapHighlightColor: 'transparent',
          marginTop: 12,
        }}>{picked.size === 0 ? '씨앗을 골라주세요' : `${picked.size}개 추가`}</button>
      </div>
    </CSheet>
  );
}

// ─────────────────────────────────────────────────────────────
// ShareCollectionSheet — mock share UI
// ─────────────────────────────────────────────────────────────
function shareCodeFor(name) {
  // Deterministic short code from name (just for demo)
  let h = 0;
  for (let i = 0; i < name.length; i++) h = ((h << 5) - h) + name.charCodeAt(i) | 0;
  return Math.abs(h).toString(36).slice(0, 6);
}

function MockQR({ size = 132, code, color = '#1F1812' }) {
  // Render a 13x13 QR-looking grid deterministically from code chars
  const cells = 13;
  const cell = size / cells;
  const seed = (code || 'abc').split('').reduce((s, c, i) => s + (c.charCodeAt(0) * (i+1)), 0);
  const rng = (n) => {
    const x = Math.sin(seed + n) * 10000;
    return x - Math.floor(x);
  };
  const dots = [];
  for (let r = 0; r < cells; r++) {
    for (let c = 0; c < cells; c++) {
      // Skip finder pattern corners
      if ((r < 4 && c < 4) || (r < 4 && c >= cells - 4) || (r >= cells - 4 && c < 4)) continue;
      if (rng(r * cells + c) > 0.55) {
        dots.push(<rect key={`${r}-${c}`} x={c * cell} y={r * cell} width={cell} height={cell} fill={color}/>);
      }
    }
  }
  const finder = (cx, cy) => (
    <g key={`f-${cx}-${cy}`}>
      <rect x={cx} y={cy} width={cell * 3} height={cell * 3} fill={color}/>
      <rect x={cx + cell * 0.5} y={cy + cell * 0.5} width={cell * 2} height={cell * 2} fill="#FFF"/>
      <rect x={cx + cell} y={cy + cell} width={cell} height={cell} fill={color}/>
    </g>
  );
  return (
    <svg width={size} height={size} viewBox={`0 0 ${size} ${size}`}>
      <rect width={size} height={size} fill="#FFF"/>
      {dots}
      {finder(0, 0)}
      {finder((cells - 3) * cell, 0)}
      {finder(0, (cells - 3) * cell)}
    </svg>
  );
}

// ─────────────────────────────────────────────────────────────
// CardStackPreview — fanned mini-card stack used in share sheet
// ─────────────────────────────────────────────────────────────
function CardStackPreview({ collection, accent }) {
  const cards = (collection.cards || []).slice(0, 3);
  // Pad with placeholders if collection has < 3 cards
  const items = cards.length >= 3 ? cards :
                cards.length === 2 ? [...cards, null] :
                cards.length === 1 ? [...cards, null, null] :
                [null, null, null];

  // Card stack: 3 cards with subtle rotation/offset, front card is most prominent.
  // Position: layered, front is centered, others peek to the sides.
  return (
    <div style={{
      position: 'relative',
      height: 200,
      margin: '0 22px',
      display: 'flex', justifyContent: 'center', alignItems: 'center',
    }}>
      {items.slice().reverse().map((c, idx) => {
        // idx 0 = back, 2 = front
        const isFront = idx === items.length - 1;
        const depth = items.length - 1 - idx; // 0..2
        const angle = depth === 0 ? 0 : (depth === 1 ? -5 : 5);
        const xOff = depth === 0 ? 0 : (depth === 1 ? -22 : 22);
        const yOff = depth === 0 ? 0 : 10;
        const scale = 1 - depth * 0.04;
        const opacity = 1;
        const z = 10 - depth;
        return (
          <div key={depth} style={{
            position: 'absolute',
            width: 220,
            height: 170,
            transform: `translate(${xOff}px, ${yOff}px) rotate(${angle}deg) scale(${scale})`,
            transformOrigin: 'center 110%',
            zIndex: z,
            opacity,
            transition: 'transform 0.3s ease',
          }}>
            <MiniShareCard card={c} accent={accent} dim={!isFront} collectionColor={collection.color} />
          </div>
        );
      })}
    </div>
  );
}

function MiniShareCard({ card, accent, dim, collectionColor }) {
  const T = {
    surface:   '#FBF7F0',
    surface2:  '#FFFFFF',
    hairline:  'rgba(60, 42, 20, 0.09)',
    ink:       '#1F1812',
    ink2:      '#6B5D4A',
    ink3:      '#A8997F',
  };
  const langFont = card?.lang === 'ja' ? '"Noto Sans JP", Pretendard' :
                   card?.lang === 'zh' ? '"Noto Sans SC", Pretendard' :
                   'Noto Sans, Pretendard';

  if (!card) {
    // placeholder
    return (
      <div style={{
        width: '100%', height: '100%',
        background: T.surface,
        border: `0.5px solid ${T.hairline}`,
        borderRadius: 16,
        boxShadow: dim
          ? '0 6px 20px rgba(31,24,18,0.10)'
          : '0 8px 28px rgba(31,24,18,0.18)',
        padding: 18,
        display: 'flex', flexDirection: 'column', gap: 8,
        opacity: 0.85,
      }}>
        <div style={{
          width: 36, height: 6, borderRadius: 6,
          background: 'rgba(60,42,20,0.08)',
        }} />
        <div style={{
          width: '80%', height: 12, borderRadius: 6,
          background: 'rgba(60,42,20,0.06)', marginTop: 4,
        }} />
        <div style={{
          width: '60%', height: 12, borderRadius: 6,
          background: 'rgba(60,42,20,0.05)',
        }} />
        <div style={{ flex: 1 }} />
        <div style={{
          width: '40%', height: 9, borderRadius: 6,
          background: 'rgba(60,42,20,0.05)',
        }} />
      </div>
    );
  }

  return (
    <div style={{
      width: '100%', height: '100%',
      background: T.surface,
      border: `0.5px solid ${T.hairline}`,
      borderRadius: 16,
      boxShadow: dim
        ? '0 6px 20px rgba(31,24,18,0.10)'
        : '0 8px 28px rgba(31,24,18,0.20)',
      padding: '16px 18px',
      display: 'flex', flexDirection: 'column',
      overflow: 'hidden',
    }}>
      {/* Lang chip */}
      <div style={{
        display: 'inline-flex', alignItems: 'center', gap: 5,
        marginBottom: 10,
      }}>
        <span style={{ fontSize: 13, lineHeight: 1 }}>
          {collLangFlag(card.lang)}
        </span>
        <span style={{
          fontSize: 10, fontWeight: 600, color: T.ink3,
          letterSpacing: '0.06em', textTransform: 'uppercase',
        }}>{card.langLabel}</span>
      </div>

      {/* Foreign text */}
      <div style={{
        fontFamily: langFont,
        fontSize: 15.5, fontWeight: 500, color: T.ink,
        letterSpacing: '-0.01em', lineHeight: 1.35,
        display: '-webkit-box',
        WebkitLineClamp: 2, WebkitBoxOrient: 'vertical',
        overflow: 'hidden',
      }}>{card.foreign}</div>

      {/* Pronunciation */}
      <div style={{
        marginTop: 4,
        fontSize: 11.5, color: T.ink2,
        letterSpacing: '-0.01em', lineHeight: 1.35,
        display: '-webkit-box',
        WebkitLineClamp: 1, WebkitBoxOrient: 'vertical',
        overflow: 'hidden',
      }}>{card.pronunciation}</div>

      <div style={{ flex: 1 }} />

      {/* Korean (footer) */}
      <div style={{
        marginTop: 8,
        paddingTop: 8,
        borderTop: `0.5px dashed ${T.hairline}`,
        fontSize: 11, color: T.ink3,
        letterSpacing: '-0.01em', lineHeight: 1.35,
        display: '-webkit-box',
        WebkitLineClamp: 1, WebkitBoxOrient: 'vertical',
        overflow: 'hidden',
      }}>{card.korean}</div>
    </div>
  );
}

function ShareCollectionSheet({ collection, accent, onClose, onSent }) {
  const friends = window.SAMPLE_FRIENDS || [];
  const [editable, setEditable] = React.useState(false);
  const [copied, setCopied] = React.useState(false);
  const [picked, setPicked] = React.useState(new Set());
  const [message, setMessage] = React.useState('');
  const code = shareCodeFor(collection.name + (editable ? '_e' : ''));
  const url = `cheekpouch.app/c/${code}`;
  const sampleCard = collection.cards && collection.cards[0];

  const togglePick = (id) => {
    const next = new Set(picked);
    if (next.has(id)) next.delete(id); else next.add(id);
    setPicked(next);
  };

  const sendToFriends = () => {
    if (picked.size === 0) return;
    const names = [...picked].map(id => friends.find(f => f.id === id)?.name).filter(Boolean);
    const target = names.length === 1 ? names[0] : `${names[0]} 외 ${names.length - 1}명`;
    onSent && onSent({
      kind: 'friends',
      target,
      title: `${target}님에게 주머니를 보냈어요`,
      message: message.trim() || `"${collection.name}" · ${collection.count}개`,
    });
  };

  const sendExternal = (channel) => {
    if (channel === 'copy') {
      setCopied(true);
      setTimeout(() => setCopied(false), 1600);
      onSent && onSent({
        kind: 'external', target: channel,
        title: '공유 링크가 복사됐어요', message: '원하는 곳에 붙여넣어 보세요',
      });
    } else {
      onSent && onSent({
        kind: 'external', target: channel,
        title: `${channel}으로 주머니를 공유했어요`,
        message: `"${collection.name}" · ${collection.count}개`,
      });
    }
  };

  const copyLink = () => {
    setCopied(true);
    setTimeout(() => setCopied(false), 1600);
    onSent && onSent({
      kind: 'external', target: 'copy',
      title: '공유 링크가 복사됐어요', message: '원하는 곳에 붙여넣어 보세요',
    });
  };

  return (
    <CSheet onClose={onClose} maxHeight="92%">
      <div style={{ padding: '0 24px 4px' }}>
        <div style={{
          fontSize: 19, fontWeight: 700, color: CT.ink,
          letterSpacing: '-0.02em', lineHeight: 1.2,
        }}>주머니 공유</div>
        <div style={{
          marginTop: 6,
          fontSize: 13, color: CT.ink2, letterSpacing: '-0.01em', lineHeight: 1.5,
        }}>받는 사람이 씨앗을 듣고 자기 주머니에 담을 수 있어요.</div>
      </div>

      <div style={{ overflowY: 'auto', padding: '14px 0 8px' }}>
        {/* Card stack preview */}
        <CardStackPreview collection={collection} accent={accent} />

        {/* Meta row */}
        <div style={{
          margin: '14px 22px 0',
          padding: '12px 14px',
          background: CT.surface2,
          border: `0.5px solid ${CT.hairline}`,
          borderRadius: 14,
          display: 'flex', alignItems: 'center', gap: 11,
        }}>
          <div style={{
            width: 34, height: 34, borderRadius: 10,
            background: `linear-gradient(155deg, ${collection.color} 0%, ${collection.color}cc 100%)`,
            display: 'inline-flex', alignItems: 'center', justifyContent: 'center',
            flexShrink: 0,
          }}>
            {window.CoverIcon &&
              <window.CoverIcon name={(collection.cover && collection.cover.icon) || 'star'} size={17} color="#FFF" />}
          </div>
          <div style={{ flex: 1, minWidth: 0 }}>
            <div style={{
              fontSize: 14.5, fontWeight: 700, color: CT.ink,
              letterSpacing: '-0.02em', lineHeight: 1.2,
              overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap',
            }}>{collection.name}</div>
            <div style={{
              marginTop: 3,
              display: 'inline-flex', alignItems: 'center', gap: 5,
              fontSize: 11.5, color: CT.ink3, letterSpacing: '-0.01em',
            }}>
              <span style={{ fontSize: 12, lineHeight: 1 }}>
                {collection.isMultiLang ? '🌐' : collLangFlag(collection.primaryLang)}
              </span>
              <span>{collection.isMultiLang ? '여러 언어' : collection.primaryLangLabel}</span>
              <span>·</span>
              <span style={{ color: CT.ink2, fontWeight: 600 }}>{collection.count}개</span>
            </div>
          </div>
        </div>

        {/* In-app friends */}
        <div style={{ padding: '18px 22px 0' }}>
          <div style={{
            padding: '0 4px 10px',
            fontSize: 11, fontWeight: 700, color: CT.ink3,
            letterSpacing: '0.08em', textTransform: 'uppercase',
          }}>앱 내 친구</div>

          <div style={{
            display: 'flex', gap: 10, overflowX: 'auto',
            paddingBottom: 4,
            scrollbarWidth: 'none',
          }}>
            {friends.length === 0 ? (
              <div style={{
                width: '100%', padding: '14px',
                border: `1px dashed ${CT.hairlineStrong}`,
                borderRadius: 12,
                fontSize: 12.5, color: CT.ink3,
                textAlign: 'center', letterSpacing: '-0.01em',
              }}>아직 친구가 없어요</div>
            ) : friends.map(f => {
              const on = picked.has(f.id);
              return (
                <button key={f.id} onClick={() => togglePick(f.id)} style={{
                  flex: '0 0 auto',
                  padding: '8px 6px 6px',
                  background: on ? `${accent}14` : 'transparent',
                  border: on ? `1.5px solid ${accent}` : `1px solid transparent`,
                  borderRadius: 14,
                  display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 5,
                  cursor: 'pointer', fontFamily: 'inherit',
                  WebkitTapHighlightColor: 'transparent',
                  minWidth: 64,
                  position: 'relative',
                  transition: 'all 0.12s ease',
                }}>
                  {window.HamsterAvatar &&
                    <window.HamsterAvatar size={44} accent={f.accent}
                                          count={3} face={f.face} />}
                  {on && (
                    <span style={{
                      position: 'absolute',
                      top: 4, right: 4,
                      width: 18, height: 18, borderRadius: 18,
                      background: accent,
                      border: `2px solid ${CT.surface}`,
                      display: 'inline-flex', alignItems: 'center', justifyContent: 'center',
                    }}>
                      <svg width="10" height="10" viewBox="0 0 14 14" fill="none">
                        <path d="M3 7.2l2.8 2.8L11 5" stroke="#FFF" strokeWidth="2.4"
                              strokeLinecap="round" strokeLinejoin="round"/>
                      </svg>
                    </span>
                  )}
                  <span style={{
                    fontSize: 11.5,
                    fontWeight: on ? 700 : 500,
                    color: on ? accent : CT.ink,
                    letterSpacing: '-0.01em',
                    whiteSpace: 'nowrap',
                  }}>{f.name}</span>
                </button>
              );
            })}
          </div>

          {/* Message field — appears once a friend is picked, slides in naturally */}
          {picked.size > 0 && (
            <div style={{
              marginTop: 14,
              padding: '12px 14px',
              background: CT.surface2,
              border: `0.5px solid ${accent}40`,
              borderRadius: 14,
              animation: 'cp-modal-in 0.22s cubic-bezier(0.22, 1, 0.36, 1)',
            }}>
              <div style={{
                display: 'flex', alignItems: 'center', gap: 6,
                marginBottom: 6,
                fontSize: 11, fontWeight: 700, color: accent,
                letterSpacing: '0.04em',
              }}>
                <svg width="11" height="11" viewBox="0 0 14 14" fill="none">
                  <path d="M2 4a2 2 0 0 1 2-2h6a2 2 0 0 1 2 2v4a2 2 0 0 1-2 2H6l-3 2v-2a2 2 0 0 1-1-2V4z"
                        stroke={accent} strokeWidth="1.5" strokeLinejoin="round" fill="none"/>
                </svg>
                <span>함께 보낼 메시지 · 선택</span>
              </div>
              <textarea
                value={message}
                onChange={(e) => setMessage(e.target.value)}
                placeholder="예: 다음 주 도쿄 갈 때 같이 외워보자!"
                rows={2}
                maxLength={120}
                style={{
                  width: '100%',
                  background: 'transparent',
                  border: 'none', outline: 'none',
                  resize: 'none', padding: 0,
                  fontFamily: 'inherit',
                  fontSize: 14, color: CT.ink,
                  letterSpacing: '-0.01em', lineHeight: 1.5,
                }}
              />
              {message.length > 0 && (
                <div style={{
                  display: 'flex', alignItems: 'center', justifyContent: 'space-between',
                  marginTop: 4,
                }}>
                  <button onClick={() => setMessage('')} style={{
                    background: 'transparent', border: 'none', padding: 0,
                    fontSize: 11, color: CT.ink3, letterSpacing: '-0.01em',
                    cursor: 'pointer', fontFamily: 'inherit',
                  }}>지우기</button>
                  <span style={{
                    fontSize: 10.5, color: CT.ink3,
                    fontVariantNumeric: 'tabular-nums',
                  }}>{message.length} / 120</span>
                </div>
              )}
            </div>
          )}
        </div>

        {/* Editable toggle */}
        <div style={{
          margin: '14px 22px 0',
          padding: '12px 14px',
          background: CT.surface2,
          border: `0.5px solid ${CT.hairline}`,
          borderRadius: 14,
          display: 'flex', alignItems: 'center', gap: 12,
        }}>
          <div style={{ flex: 1, minWidth: 0 }}>
            <div style={{
              fontSize: 14, fontWeight: 600, color: CT.ink,
              letterSpacing: '-0.01em',
            }}>받은 사람도 편집 가능</div>
            <div style={{
              marginTop: 2,
              fontSize: 11.5, color: CT.ink3,
              letterSpacing: '-0.01em', lineHeight: 1.4,
            }}>꺼두면 씨앗을 듣고 자기 주머니로 복사만 할 수 있어요</div>
          </div>
          <button onClick={() => setEditable(e => !e)} style={{
            width: 42, height: 26, borderRadius: 999,
            background: editable ? (collection.color || accent) : 'rgba(60,42,20,0.18)',
            border: 'none', padding: 0,
            position: 'relative',
            cursor: 'pointer', flexShrink: 0,
            transition: 'background 0.2s ease',
            WebkitTapHighlightColor: 'transparent',
          }}>
            <span style={{
              position: 'absolute', top: 2, left: editable ? 18 : 2,
              width: 22, height: 22, borderRadius: 22,
              background: '#FFF',
              boxShadow: '0 1.5px 3px rgba(0,0,0,0.18)',
              transition: 'left 0.2s ease',
            }} />
          </button>
        </div>

        {/* External channels */}
        <div style={{ padding: '18px 22px 0' }}>
          <div style={{
            padding: '0 4px 10px',
            fontSize: 11, fontWeight: 700, color: CT.ink3,
            letterSpacing: '0.08em', textTransform: 'uppercase',
          }}>다른 앱으로 보내기</div>
          <div style={{
            display: 'grid', gridTemplateColumns: 'repeat(4, 1fr)', gap: 8,
          }}>
            <ShareDestButton label="카톡" bg="#FEE500"
                             icon={<KakaoMini />}
                             onClick={() => sendExternal('카카오톡')} />
            <ShareDestButton label="메시지" bg="#34C759"
                             icon={<MsgMini />}
                             onClick={() => sendExternal('메시지')} />
            <ShareDestButton label="메일" bg="#0A84FF"
                             icon={<MailMini />}
                             onClick={() => sendExternal('메일')} />
            <ShareDestButton label={copied ? '복사됨' : '링크'}
                             bg="rgba(60,42,20,0.10)" inkColor={CT.ink}
                             icon={copied
                                ? <CheckMini color={CT.ink} />
                                : <LinkMini color={CT.ink} />}
                             onClick={copyLink} />
          </div>
        </div>

        {/* QR (smaller, at bottom — useful in person) */}
        <div style={{
          margin: '20px 22px 0',
          padding: '14px 16px',
          background: CT.surface2,
          border: `0.5px solid ${CT.hairline}`,
          borderRadius: 14,
          display: 'flex', alignItems: 'center', gap: 12,
        }}>
          <div style={{
            padding: 6,
            background: '#FFF',
            border: `0.5px solid ${CT.hairlineStrong}`,
            borderRadius: 10,
            flexShrink: 0,
          }}>
            <MockQR size={58} code={code} color={collection.color} />
          </div>
          <div style={{ flex: 1, minWidth: 0 }}>
            <div style={{
              fontSize: 12.5, fontWeight: 600, color: CT.ink,
              letterSpacing: '-0.01em',
            }}>QR로 바로 받기</div>
            <div style={{
              marginTop: 2,
              fontFamily: 'ui-monospace, SFMono-Regular, monospace',
              fontSize: 11, color: CT.ink3,
              letterSpacing: '-0.01em',
              overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap',
            }}>{url}</div>
          </div>
        </div>
      </div>

      {/* Send action */}
      <div style={{ padding: '12px 22px 0', flexShrink: 0 }}>
        {picked.size > 0 ? (
          <button onClick={sendToFriends} style={{
            width: '100%', height: 50, borderRadius: 14,
            background: accent, color: '#FFF', border: 'none',
            fontFamily: 'inherit', fontSize: 15, fontWeight: 700,
            letterSpacing: '-0.01em',
            cursor: 'pointer',
            display: 'inline-flex', alignItems: 'center', justifyContent: 'center', gap: 6,
            WebkitTapHighlightColor: 'transparent',
          }}>
            <svg width="14" height="14" viewBox="0 0 16 16" fill="none">
              <path d="M2 8L14 2L11 14L8 9L2 8Z" fill="#FFF"/>
            </svg>
            {picked.size === 1
              ? `${friends.find(f => f.id === [...picked][0])?.name}님에게 보내기`
              : `${picked.size}명에게 보내기`}
          </button>
        ) : (
          <button onClick={onClose} style={{
            width: '100%', height: 48, borderRadius: 14,
            background: 'transparent', color: CT.ink,
            border: `1px solid ${CT.hairlineStrong}`,
            fontFamily: 'inherit', fontSize: 15, fontWeight: 500,
            letterSpacing: '-0.01em',
            cursor: 'pointer',
            WebkitTapHighlightColor: 'transparent',
          }}>닫기</button>
        )}
      </div>
    </CSheet>
  );
}

function ShareDestButton({ label, bg, inkColor, icon, onClick }) {
  return (
    <button onClick={onClick} style={{
      padding: '10px 6px 8px',
      background: 'transparent',
      border: 'none',
      cursor: 'pointer', fontFamily: 'inherit',
      display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 6,
      WebkitTapHighlightColor: 'transparent',
    }}>
      <span style={{
        width: 46, height: 46, borderRadius: 14,
        background: bg,
        display: 'inline-flex', alignItems: 'center', justifyContent: 'center',
        flexShrink: 0,
      }}>{icon}</span>
      <span style={{
        fontSize: 11, color: CT.ink2,
        letterSpacing: '-0.01em', whiteSpace: 'nowrap',
      }}>{label}</span>
    </button>
  );
}

function KakaoMini() {
  return (
    <svg width="22" height="22" viewBox="0 0 22 22" fill="none">
      <path d="M11 4C7.13 4 4 6.46 4 9.5c0 1.93 1.26 3.62 3.18 4.6L6.5 17l2.7-1.75c.59.1 1.2.15 1.8.15 3.87 0 7-2.46 7-5.5S14.87 4 11 4z"
            fill="#191919"/>
    </svg>
  );
}
function MsgMini() {
  return (
    <svg width="22" height="22" viewBox="0 0 22 22" fill="none">
      <path d="M3 7a3 3 0 0 1 3-3h10a3 3 0 0 1 3 3v6a3 3 0 0 1-3 3H9l-4 3v-3a3 3 0 0 1-2-3V7z"
            fill="#FFF"/>
    </svg>
  );
}
function MailMini() {
  return (
    <svg width="22" height="22" viewBox="0 0 22 22" fill="none">
      <rect x="3" y="5" width="16" height="12" rx="2" fill="#FFF"/>
      <path d="M3 6l8 6 8-6" stroke="#0A84FF" strokeWidth="1.5" fill="none"
            strokeLinecap="round" strokeLinejoin="round"/>
    </svg>
  );
}
function LinkMini({ color }) {
  return (
    <svg width="20" height="20" viewBox="0 0 22 22" fill="none">
      <path d="M9 13l4-4M11 8a3 3 0 0 1 4.2 4.2l-2.2 2.2a3 3 0 0 1-4.2-4.2"
            stroke={color} strokeWidth="1.7" strokeLinecap="round"/>
      <path d="M11 13a3 3 0 0 1-4.2-4.2l2.2-2.2a3 3 0 0 1 4.2 4.2"
            stroke={color} strokeWidth="1.7" strokeLinecap="round"/>
    </svg>
  );
}
function CheckMini({ color }) {
  return (
    <svg width="18" height="18" viewBox="0 0 18 18" fill="none">
      <path d="M3 9.2L7.5 13.7L15 5.3" stroke={color} strokeWidth="2.2"
            strokeLinecap="round" strokeLinejoin="round"/>
    </svg>
  );
}

// ─────────────────────────────────────────────────────────────
// PinGlyph
// ─────────────────────────────────────────────────────────────
function PinGlyph({ size = 12, color = '#FFF' }) {
  return (
    <svg width={size} height={size} viewBox="0 0 14 14" fill="none">
      <path d="M5 1.5h4l-.6 3.5 2.5 2-1 1H8v3.5l-1 1.5-1-1.5V8H4l-1-1 2.5-2L5 1.5z"
            fill={color} stroke={color} strokeWidth="0.6" strokeLinejoin="round"/>
    </svg>
  );
}

// ─────────────────────────────────────────────────────────────
// CollectionTileCard — flat colored "note" style
// ─────────────────────────────────────────────────────────────
function CollectionTileCard({ group, accent, pinned, onClick }) {
  const previewCard = group.cards && group.cards[0];
  const dot = group.color || '#8B6F47';
  const isEmpty = !previewCard;
  const cover = group.cover || { kind: 'auto' };
  const slotId = slotIdFor(group.name);

  const onColor = '#FFF';
  const onColorSoft = 'rgba(255,255,255,0.74)';

  return (
    <button onClick={onClick} style={{
      position: 'relative',
      padding: 0,
      borderRadius: 16,
      cursor: 'pointer',
      fontFamily: 'inherit',
      textAlign: 'left',
      WebkitTapHighlightColor: 'transparent',
      overflow: 'hidden',
      border: 'none',
      background: cover.kind === 'photo' ? '#000' : `linear-gradient(155deg, ${dot} 0%, ${shade(dot, -18)} 100%)`,
      minHeight: 184,
      display: 'flex', flexDirection: 'column',
      boxShadow: '0 1px 0 rgba(255,255,255,0.16) inset',
    }}>
      {/* Photo bg layer */}
      {cover.kind === 'photo' && (
        <div style={{ position: 'absolute', inset: 0, pointerEvents: 'none' }}>
          <image-slot
            id={slotId}
            shape="rect"
            placeholder=""
            style={{ display: 'block', width: '100%', height: '100%', pointerEvents: 'none' }}
          />
          <div style={{
            position: 'absolute', inset: 0,
            background: 'linear-gradient(180deg, rgba(0,0,0,0.10) 0%, rgba(0,0,0,0.60) 100%)',
          }} />
        </div>
      )}

      {/* Decorative pouch watermark for auto mode (fills negative space) */}
      {cover.kind === 'auto' && window.PouchShape && (
        <div style={{
          position: 'absolute',
          top: -10, right: -18,
          opacity: isEmpty ? 0.22 : 0.16,
          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={isEmpty ? 0 : 3}
                             seedFill="rgba(255,255,255,0.85)" />
        </div>
      )}
      {/* Scattered seed dust along bottom for auto mode (only when has cards) */}
      {cover.kind === 'auto' && !isEmpty && window.SeedCluster && (
        <div style={{
          position: 'absolute', left: -6, right: -6, bottom: -2, height: 38,
          opacity: 0.32, pointerEvents: 'none',
        }}>
          <window.SeedCluster count={7} width={220} height={38}
                              color="#FFFFFF" layout="scatter" />
        </div>
      )}

      {/* Top row: count + pin */}
      <div style={{
        position: 'relative', zIndex: 1,
        padding: '12px 14px',
        display: 'flex', alignItems: 'center', justifyContent: 'space-between',
      }}>
        <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: onColor,
          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' }}>{group.count}</span>
          <span style={{ opacity: 0.7, fontSize: 10 }}>개</span>
        </div>
        {pinned && (
          <span style={{
            width: 22, height: 22, borderRadius: 22,
            background: 'rgba(255,255,255,0.22)',
            display: 'inline-flex', alignItems: 'center', justifyContent: 'center',
          }}>
            <PinGlyph size={11} color={onColor} />
          </span>
        )}
      </div>

      {/* Cover area (icon mode — fills middle) */}
      {cover.kind === 'icon' && (
        <div style={{
          position: 'relative', zIndex: 1,
          flex: 1,
          display: 'flex', alignItems: 'center', justifyContent: 'center',
        }}>
          <CoverIcon name={cover.icon || 'star'} size={48} color={onColor} />
        </div>
      )}

      {/* Hero area for auto mode — quote takes the empty space */}
      {cover.kind === 'auto' && (
        <div style={{
          position: 'relative', zIndex: 1,
          flex: 1,
          padding: '4px 14px 6px',
          display: 'flex', alignItems: 'center',
        }}>
          {isEmpty ? (
            <div style={{
              fontSize: 13, color: onColorSoft,
              letterSpacing: '-0.01em', fontStyle: 'italic',
              textAlign: 'left', lineHeight: 1.4,
            }}>씨앗을<br/>담아보세요</div>
          ) : (
            <div style={{
              fontFamily: previewCard?.lang === 'ja' ? '"Noto Sans JP", Pretendard' : 'Noto Sans, Pretendard',
              fontSize: 15, fontWeight: 500, color: onColor,
              letterSpacing: '-0.01em', lineHeight: 1.35,
              display: '-webkit-box',
              WebkitLineClamp: 3, WebkitBoxOrient: 'vertical',
              overflow: 'hidden',
              textWrap: 'balance',
            }}>{previewCard.foreign}</div>
          )}
        </div>
      )}

      {/* Photo mode: empty middle so photo shows through */}
      {cover.kind === 'photo' && (
        <div style={{ flex: 1 }} />
      )}

      {/* Bottom: thin divider + title + meta */}
      <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: onColor,
          letterSpacing: '-0.02em', lineHeight: 1.2,
          display: '-webkit-box',
          WebkitLineClamp: 2, WebkitBoxOrient: 'vertical',
          overflow: 'hidden',
        }}>{group.name}</div>
        <div style={{
          marginTop: 4,
          fontSize: 11, color: onColorSoft, letterSpacing: '-0.01em',
          display: 'inline-flex', alignItems: 'center', gap: 5,
        }}>
          {!isEmpty && (
            <span style={{ fontSize: 13, lineHeight: 1, filter: 'drop-shadow(0 1px 1px rgba(0,0,0,0.15))' }}>
              {group.isMultiLang ? '🌐' : collLangFlag(group.primaryLang)}
            </span>
          )}
          <span>
            {isEmpty ? '비어있음' :
              (group.isMultiLang ? '여러 언어' : group.primaryLangLabel)}
          </span>
        </div>
      </div>
    </button>
  );
}

// ─────────────────────────────────────────────────────────────
// CollectionListRow — compact horizontal row for list mode
// ─────────────────────────────────────────────────────────────
function CollectionListRow({ group, accent, pinned, onClick }) {
  const dot = group.color || '#8B6F47';
  const previewCard = group.cards && group.cards[0];
  const isEmpty = !previewCard;
  const cover = group.cover || { kind: 'auto' };
  const slotId = slotIdFor(group.name);

  return (
    <button onClick={onClick} style={{
      width: '100%',
      background: CT.surface,
      border: `0.5px solid ${CT.hairline}`,
      borderRadius: 14,
      padding: 6,
      cursor: 'pointer',
      fontFamily: 'inherit',
      textAlign: 'left',
      display: 'flex', alignItems: 'stretch', gap: 12,
      WebkitTapHighlightColor: 'transparent',
      overflow: 'hidden',
    }}>
      {/* Cover block — matches Card tile aesthetic */}
      <div style={{
        position: 'relative',
        width: 64, height: 64, borderRadius: 10,
        overflow: 'hidden', flexShrink: 0,
        background: cover.kind === 'photo' ? '#000'
                  : `linear-gradient(155deg, ${dot} 0%, ${shade(dot, -18)} 100%)`,
        display: 'flex', alignItems: 'center', justifyContent: 'center',
      }}>
        {cover.kind === 'photo' ? (
          <div style={{ position: 'absolute', inset: 0, pointerEvents: 'none' }}>
            <image-slot id={slotId} shape="rect" placeholder=""
                        style={{ display: 'block', width: '100%', height: '100%', pointerEvents: 'none' }} />
          </div>
        ) : cover.kind === 'icon' ? (
          <CoverIcon name={cover.icon || 'star'} size={28} color="#FFF" />
        ) : (
          // auto mode: pouch silhouette with seed count
          <div style={{
            position: 'relative', width: '100%', height: '100%',
            display: 'flex', alignItems: 'center', justifyContent: 'center',
          }}>
            {window.PouchShape && (
              <div style={{ position: 'absolute', inset: 0,
                display: 'flex', alignItems: 'center', justifyContent: 'center',
                opacity: 0.35,
              }}>
                <window.PouchShape size={56} fill="#FFFFFF"
                                   cord="rgba(255,255,255,0.7)"
                                   shade="rgba(255,255,255,0.4)"
                                   highlight="rgba(255,255,255,0.95)"
                                   seeds={isEmpty ? 0 : 0} />
              </div>
            )}
            <div style={{
              position: 'relative',
              display: 'flex', flexDirection: 'column', alignItems: 'center',
              color: '#FFF', lineHeight: 1,
            }}>
              <span style={{
                fontSize: 22, fontWeight: 700, letterSpacing: '-0.04em',
                fontVariantNumeric: 'tabular-nums',
                textShadow: '0 1px 2px rgba(0,0,0,0.18)',
              }}>{isEmpty ? '·' : group.count}</span>
              <span style={{
                marginTop: 3,
                fontSize: 9, fontWeight: 600, letterSpacing: '0.08em',
                opacity: 0.82,
              }}>개</span>
            </div>
          </div>
        )}
        {/* Tiny seed nestled at corner for auto-mode non-empty */}
        {cover.kind === 'auto' && !isEmpty && window.SeedShape && (
          <div style={{
            position: 'absolute', top: 4, right: 5,
            opacity: 0.7,
            transform: 'rotate(18deg)',
            pointerEvents: 'none',
          }}>
            <window.SeedShape size={8} fill="rgba(255,255,255,0.85)"
                              highlight="rgba(255,255,255,0.3)" />
          </div>
        )}
      </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: CT.ink,
            letterSpacing: '-0.02em',
            overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap',
            flex: 1, minWidth: 0,
          }}>{group.name}</span>
          {pinned && (
            <span style={{ flexShrink: 0, display: 'inline-flex' }}>
              <PinGlyph size={11} color={dot} />
            </span>
          )}
        </div>

        {/* Quote / preview line — fades out gracefully if empty */}
        {!isEmpty && cover.kind !== 'photo' && (
          <div style={{
            marginTop: 4,
            fontFamily: previewCard.lang === 'ja' ? '"Noto Sans JP", Pretendard' : 'Noto Sans, Pretendard',
            fontSize: 12.5, color: CT.ink2,
            letterSpacing: '-0.01em', lineHeight: 1.35,
            overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap',
          }}>{previewCard.foreign}</div>
        )}

        <div style={{
          marginTop: 5,
          display: 'flex', alignItems: 'center', gap: 6,
          fontSize: 11, color: CT.ink3, letterSpacing: '-0.01em',
        }}>
          {isEmpty ? (
            <>
              <span style={{ width: 4, height: 4, borderRadius: 4, background: dot, flexShrink: 0 }} />
              <span style={{ fontStyle: 'italic' }}>비어있음</span>
            </>
          ) : (
            <>
              <span style={{ fontSize: 12, lineHeight: 1, flexShrink: 0 }}>
                {group.isMultiLang ? '🌐' : collLangFlag(group.primaryLang)}
              </span>
              <span style={{
                overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap',
              }}>{group.isMultiLang ? '여러 언어' : group.primaryLangLabel}</span>
              <span>·</span>
              <span style={{ fontWeight: 600, color: CT.ink2 }}>{group.count}개</span>
              {group.latestDays != null && group.latestDays <= 7 && (
                <>
                  <span>·</span>
                  <span style={{ color: dot, fontWeight: 600 }}>
                    {group.latestDays === 0 ? '오늘' :
                     group.latestDays === 1 ? '어제' :
                     `${group.latestDays}일 전`}
                  </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={CT.ink3} strokeWidth="1.6"
                strokeLinecap="round" strokeLinejoin="round"/>
        </svg>
      </div>
    </button>
  );
}

// ─────────────────────────────────────────────────────────────
// AllCardsTile — companion tile in the collection grid
// ─────────────────────────────────────────────────────────────
function AllCardsTile({ total, 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, #2A2620 0%, #1F1812 100%)',
      minHeight: 184,
      display: 'flex', flexDirection: 'column',
      boxShadow: '0 1px 0 rgba(255,255,255,0.08) inset',
    }}>
      {/* Top: count badge */}
      <div style={{
        position: 'relative', zIndex: 1,
        padding: '12px 14px',
        display: 'flex', alignItems: 'center', justifyContent: 'space-between',
      }}>
        <div style={{
          height: 22, padding: '0 10px 0 7px', borderRadius: 999,
          background: 'rgba(255,255,255,0.10)',
          display: 'inline-flex', alignItems: 'center', gap: 5,
          fontSize: 11.5, fontWeight: 600, color: '#FFF',
          letterSpacing: '-0.01em',
        }}>
          {window.SeedShape && (
            <window.SeedShape size={9} fill="#FFFFFF"
                              highlight="rgba(255,255,255,0.45)" />
          )}
          <span style={{ fontVariantNumeric: 'tabular-nums' }}>{total}</span>
          <span style={{ opacity: 0.7, fontSize: 10 }}>개</span>
        </div>
      </div>

      {/* Hero — heap of seeds (a "hamster's stash") */}
      <div style={{
        position: 'relative', zIndex: 1,
        flex: 1,
        display: 'flex', alignItems: 'center', justifyContent: 'center',
        overflow: 'hidden',
      }}>
        {window.SeedCluster && (
          <div style={{
            position: 'absolute', inset: 0,
            display: 'flex', alignItems: 'center', justifyContent: 'center',
          }}>
            <window.SeedCluster count={22} width={180} height={92}
                                color="#FFFFFF" opacity={0.85}
                                layout="pile" seed={11} />
          </div>
        )}
        {/* Soft warm light gradient on top */}
        <div style={{
          position: 'absolute', inset: 0,
          background: 'radial-gradient(60% 80% at 50% 30%, rgba(201,168,106,0.18) 0%, transparent 70%)',
          pointerEvents: 'none',
        }} />
      </div>

      {/* Bottom: title + meta */}
      <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,
        }}>전체 씨앗</div>
        <div style={{
          marginTop: 4,
          fontSize: 11, color: 'rgba(255,255,255,0.66)',
          letterSpacing: '-0.01em',
        }}>언어·카테고리로 둘러보기</div>
      </div>
    </button>
  );
}

// ─────────────────────────────────────────────────────────────
// UpgradeQuotaSheet — quota top-up options
// ─────────────────────────────────────────────────────────────
function UpgradeQuotaSheet({ accent, used, total, dailyCap, dailyCapMax, todayBoost, onWatchAd, onInviteFriend, onClose }) {
  const remaining = Math.max(0, total - used);
  const inviteAtMax = dailyCap >= dailyCapMax;
  const [adState, setAdState] = React.useState('idle'); // idle | playing | done
  const [inviteState, setInviteState] = React.useState('idle'); // idle | sharing | done | max
  const [justAdded, setJustAdded] = React.useState(null); // {n, label}

  const flashAdded = (n, label) => {
    setJustAdded({ n, label });
    setTimeout(() => setJustAdded(null), 1700);
  };

  const handleWatchAd = () => {
    if (adState !== 'idle') return;
    setAdState('playing');
    setTimeout(() => {
      onWatchAd && onWatchAd();
      setAdState('done');
      flashAdded(1, '오늘만');
      setTimeout(() => setAdState('idle'), 1500);
    }, 1500);
  };

  const handleInvite = () => {
    if (inviteState !== 'idle') return;
    if (inviteAtMax) {
      setInviteState('max');
      setTimeout(() => setInviteState('idle'), 1500);
      return;
    }
    setInviteState('sharing');
    const finish = () => {
      onInviteFriend && onInviteFriend();
      setInviteState('done');
      flashAdded(1, '매일');
      setTimeout(() => setInviteState('idle'), 1500);
    };
    const shareData = {
      title: 'CheekPouch',
      text: '나랑 같이 CheekPouch 써볼래? 가입하면 우리 둘 다 매일 볼주머니가 +1개씩 늘어나요!',
      url: 'https://cheekpouch.app/invite/min',
    };
    try {
      if (navigator.share) {
        navigator.share(shareData).then(finish).catch(finish);
      } else if (navigator.clipboard) {
        navigator.clipboard.writeText(shareData.url).then(finish).catch(finish);
      } else {
        setTimeout(finish, 500);
      }
    } catch (e) {
      setTimeout(finish, 500);
    }
  };

  return (
    <CSheet onClose={onClose} maxHeight="92%">
      <div style={{ padding: '0 24px 4px', position: 'relative' }}>
        <div style={{
          fontSize: 20, fontWeight: 700, color: CT.ink,
          letterSpacing: '-0.02em', lineHeight: 1.2,
          textWrap: 'balance',
        }}>오늘 볼주머니가<br/>꽉 찼나요?</div>
        <div style={{
          marginTop: 8,
          fontSize: 13.5, color: CT.ink2, letterSpacing: '-0.01em', lineHeight: 1.5,
        }}>오늘은 <strong style={{ color: CT.ink, fontWeight: 700 }}>{total}개</strong>까지 담을 수 있고,
        매일 자정에 다시 비워져요. 한 자리 더 만들 수 있는 방법이 있어요.</div>

        {justAdded && (
          <div key={`${justAdded.label}-${total}`} style={{
            position: 'absolute', top: -2, right: 18,
            display: 'inline-flex', alignItems: 'center', gap: 5,
            height: 26, padding: '0 11px', borderRadius: 999,
            background: accent, color: '#FFF',
            fontSize: 12, fontWeight: 700, letterSpacing: '-0.01em',
            boxShadow: `0 6px 18px ${accent}55`,
            animation: 'cp-toast-in 240ms cubic-bezier(.2,.7,.2,1) both',
          }}>
            <svg width="11" height="11" viewBox="0 0 16 16" fill="none">
              <path d="M8 1.5L9 6.5L14 7.5L9 8.5L8 13.5L7 8.5L2 7.5L7 6.5L8 1.5Z" fill="#FFF"/>
            </svg>
            {justAdded.label} +{justAdded.n}자리
          </div>
        )}
      </div>

      <div style={{ overflowY: 'auto', padding: '20px 22px 8px' }}>
        {/* Visual: sunflower seeds in the cheek pouch */}
        <SeedPouchVisual used={used} dailyCap={dailyCap} todayBoost={todayBoost} accent={accent} />

        {/* Hidden — Pro card removed */}
        {false && <button style={{
          width: '100%',
          padding: '20px 20px',
          borderRadius: 18,
          background: `linear-gradient(135deg, ${accent} 0%, ${shade(accent, -22)} 100%)`,
          border: 'none',
          color: '#FFF',
          textAlign: 'left',
          cursor: 'pointer',
          fontFamily: 'inherit',
          position: 'relative',
          overflow: 'hidden',
          WebkitTapHighlightColor: 'transparent',
        }}>
          <div style={{
            position: 'absolute', top: -20, right: -20,
            fontSize: 100, lineHeight: 1,
            fontFamily: 'Georgia, serif', fontWeight: 700,
            color: 'rgba(255,255,255,0.12)',
            pointerEvents: 'none',
          }}>∞</div>
          <div style={{
            display: 'inline-flex', alignItems: 'center', gap: 6,
            height: 22, padding: '0 9px', borderRadius: 999,
            background: 'rgba(255,255,255,0.18)',
            fontSize: 10.5, fontWeight: 700, letterSpacing: '0.06em',
          }}>
            <SparkleSmall />
            CHEEKPOUCH PRO
          </div>
          <div style={{
            marginTop: 12,
            fontSize: 24, fontWeight: 700,
            letterSpacing: '-0.03em', lineHeight: 1.15,
            textWrap: 'balance',
          }}>한도 없이 마음껏 담아요</div>
          <div style={{
            marginTop: 8,
            fontSize: 13, color: 'rgba(255,255,255,0.86)',
            letterSpacing: '-0.01em', lineHeight: 1.5,
          }}>모든 주머니·언어 무제한 · 스크린샷 자동 분류 · iCloud 동기화 · 더 많은 음성</div>
          <div style={{
            marginTop: 14,
            display: 'flex', alignItems: 'flex-end', gap: 8,
          }}>
            <span style={{
              fontSize: 22, fontWeight: 700, letterSpacing: '-0.02em',
            }}>₩4,900</span>
            <span style={{
              fontSize: 12, color: 'rgba(255,255,255,0.7)',
              marginBottom: 4, letterSpacing: '-0.01em',
            }}>/ 월</span>
            <span style={{
              marginLeft: 'auto', marginBottom: 2,
              height: 28, padding: '0 12px', borderRadius: 999,
              background: '#FFF',
              color: shade(accent, -8),
              display: 'inline-flex', alignItems: 'center', gap: 4,
              fontSize: 12, fontWeight: 700,
              letterSpacing: '-0.01em',
            }}>
              7일 무료 체험
              <svg width="7" height="11" viewBox="0 0 8 14" fill="none">
                <path d="M1.5 1.5L6.5 7L1.5 12.5"
                      stroke={shade(accent, -8)} strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round"/>
              </svg>
            </span>
          </div>
        </button>}

        {/* Free options */}
        <div style={{
          fontSize: 11, fontWeight: 600, color: CT.ink3,
          letterSpacing: '0.08em', textTransform: 'uppercase',
          padding: '20px 4px 10px',
        }}>볼주머니 늘리는 방법</div>

        <div style={{
          background: CT.surface2,
          borderRadius: 14,
          border: `0.5px solid ${CT.hairline}`,
          overflow: 'hidden',
        }}>
          <QuotaOption icon={<PlayCircleGlyph />} title="광고 1편 보기"
                       sub={adState === 'playing' ? '광고 재생 중…' :
                            adState === 'done'    ? '오늘만 +1자리 늘었어요!' :
                                                    '오늘만 씨앗 한 개 더 (자정에 사라져요)'}
                       reward="+1 오늘만" accent={accent} first
                       state={adState}
                       onClick={handleWatchAd} />
          <QuotaOption icon={<FriendsGlyph />}    title="친구 초대하기"
                       sub={inviteState === 'sharing' ? '초대 링크를 공유하는 중…' :
                            inviteState === 'done'    ? '매일 +1자리! 친구가 가입하면 둘 다 받아요' :
                            inviteState === 'max'     ? `최대 ${dailyCapMax}개까지 늘릴 수 있어요` :
                            inviteAtMax              ? `볼주머니가 최대치(${dailyCapMax}개)예요` :
                                                       '가입하면 매일 받는 씨앗이 영구 +1개'}
                       reward={inviteAtMax ? '최대치' : '+1 매일'} accent={accent}
                       state={inviteState === 'max' ? 'done' : inviteState}
                       dim={inviteAtMax && inviteState === 'idle'}
                       trailing={
                         <div style={{
                           display: 'inline-flex', alignItems: 'center', gap: 4,
                           fontSize: 10.5, color: CT.ink3,
                           fontVariantNumeric: 'tabular-nums',
                           letterSpacing: '-0.01em',
                         }}>
                           <span style={{
                             fontWeight: 700,
                             color: inviteAtMax ? CT.ink3 : accent,
                           }}>{dailyCap}</span>
                           <span>/ {dailyCapMax}</span>
                         </div>
                       }
                       onClick={handleInvite} />
        </div>

        <div style={{
          marginTop: 14,
          padding: '11px 14px',
          background: 'rgba(60,42,20,0.05)',
          borderRadius: 12,
          fontSize: 12, color: CT.ink2,
          letterSpacing: '-0.01em', lineHeight: 1.5,
          display: 'flex', gap: 8, alignItems: 'flex-start',
        }}>
          <svg width="14" height="14" viewBox="0 0 14 14" fill="none" style={{ flexShrink: 0, marginTop: 1 }}>
            <circle cx="7" cy="7" r="6" stroke={CT.ink3} strokeWidth="1.3"/>
            <path d="M7 4.5v3M7 9.5h.01" stroke={CT.ink3} strokeWidth="1.5" strokeLinecap="round"/>
          </svg>
          <div>
            햄스터는 매일 자정에 집에 가서 볼주머니를 비워요. 담아둔 씨앗은 주머니에 그대로 모여 있으니 걱정 마세요.
          </div>
        </div>
      </div>

      <div style={{ padding: '12px 22px 0', flexShrink: 0 }}>
        <button onClick={onClose} style={{
          width: '100%', height: 48, borderRadius: 14,
          background: 'transparent', color: CT.ink,
          border: `1px solid ${CT.hairlineStrong}`,
          fontFamily: 'inherit', fontSize: 15, fontWeight: 500,
          cursor: 'pointer',
          WebkitTapHighlightColor: 'transparent',
        }}>나중에</button>
      </div>
    </CSheet>
  );
}

function QuotaOption({ icon, title, sub, reward, accent, first, chevron, onClick, state, dim, trailing }) {
  const busy = state === 'playing' || state === 'sharing';
  const done = state === 'done';
  return (
    <button onClick={onClick} disabled={busy} style={{
      width: '100%', display: 'flex', alignItems: 'center', gap: 12,
      padding: '14px 14px',
      borderTop: first ? 'none' : `0.5px solid ${CT.hairline}`,
      background: 'transparent',
      border: 'none',
      cursor: busy ? 'default' : 'pointer',
      textAlign: 'left',
      fontFamily: 'inherit',
      WebkitTapHighlightColor: 'transparent',
      opacity: busy ? 0.92 : dim ? 0.55 : 1,
      transition: 'opacity 160ms',
    }}>
      <span style={{
        width: 36, height: 36, borderRadius: 10,
        background: done ? `${accent}` : `${accent}14`,
        color: done ? '#FFF' : accent,
        display: 'inline-flex', alignItems: 'center', justifyContent: 'center',
        flexShrink: 0,
        transition: 'background 200ms, color 200ms',
      }}>
        {done ? (
          <svg width="18" height="18" viewBox="0 0 20 20" fill="none">
            <path d="M4.5 10.5 L8.5 14.5 L15.5 6" stroke="#FFF" strokeWidth="2"
                  strokeLinecap="round" strokeLinejoin="round"/>
          </svg>
        ) : busy ? (
          <svg width="18" height="18" viewBox="0 0 20 20" fill="none"
               style={{ animation: 'cp-spin 0.9s linear infinite' }}>
            <circle cx="10" cy="10" r="7" stroke="currentColor" strokeOpacity="0.25" strokeWidth="2"/>
            <path d="M17 10a7 7 0 0 0-7-7" stroke="currentColor" strokeWidth="2" strokeLinecap="round"/>
          </svg>
        ) : icon}
      </span>
      <div style={{ flex: 1, minWidth: 0 }}>
        <div style={{
          fontSize: 15, fontWeight: 600, color: CT.ink,
          letterSpacing: '-0.01em',
        }}>{title}</div>
        <div style={{
          marginTop: 2,
          fontSize: 12, color: done ? accent : CT.ink3,
          letterSpacing: '-0.01em', lineHeight: 1.4,
          transition: 'color 200ms',
        }}>{sub}</div>
      </div>
      <div style={{
        display: 'flex', flexDirection: 'column', alignItems: 'flex-end', gap: 4,
        flexShrink: 0,
      }}>
        <span style={{
          height: 24, padding: '0 10px', borderRadius: 999,
          background: done ? accent : `${accent}1A`,
          color: done ? '#FFF' : accent,
          display: 'inline-flex', alignItems: 'center',
          fontSize: 12, fontWeight: 700, letterSpacing: '-0.01em',
          transition: 'background 200ms, color 200ms',
        }}>{reward}</span>
        {trailing}
      </div>
      {chevron && (
        <svg width="7" height="11" viewBox="0 0 8 14" fill="none" style={{ flexShrink: 0, marginLeft: -4 }}>
          <path d="M1.5 1.5L6.5 7L1.5 12.5" stroke={CT.ink3} strokeWidth="1.6"
                strokeLinecap="round" strokeLinejoin="round"/>
        </svg>
      )}
    </button>
  );
}

// ─────────────────────────────────────────────────────────────
// SeedPouchVisual — sunflower-seed visualization of today's pouch
// Each seed slot = one card. Filled = stored, empty = available.
// Boost seeds (today-only) sit at the end with a sparkle accent.
// ─────────────────────────────────────────────────────────────
function SeedPouchVisual({ used, dailyCap, todayBoost, accent }) {
  const total = dailyCap + todayBoost;
  const remaining = Math.max(0, total - used);
  const seeds = [];
  for (let i = 0; i < dailyCap; i++) {
    seeds.push({ type: 'daily', filled: i < used });
  }
  for (let i = 0; i < todayBoost; i++) {
    seeds.push({ type: 'boost', filled: (dailyCap + i) < used });
  }
  const full = used >= total;
  return (
    <div style={{
      padding: '16px 16px 14px',
      background: `linear-gradient(180deg, ${accent}0A 0%, ${accent}05 100%)`,
      border: `0.5px solid ${accent}28`,
      borderRadius: 16,
    }}>
      <div style={{
        display: 'flex', alignItems: 'baseline', justifyContent: 'space-between',
        marginBottom: 11,
      }}>
        <div style={{
          fontSize: 12.5, fontWeight: 600, color: CT.ink,
          letterSpacing: '-0.01em',
          display: 'inline-flex', alignItems: 'center', gap: 5,
        }}>
          <SunflowerGlyph color={accent} />
          오늘 모은 씨앗
        </div>
        <div style={{
          fontSize: 12, color: CT.ink2, letterSpacing: '-0.01em',
          fontVariantNumeric: 'tabular-nums',
        }}>
          <strong style={{ color: full ? accent : CT.ink, fontWeight: 700, fontSize: 13 }}>{used}</strong>
          <span style={{ color: CT.ink3 }}> / {total}개</span>
        </div>
      </div>
      <div style={{
        display: 'flex', flexWrap: 'wrap', gap: 5,
        padding: '2px 0',
      }}>
        {seeds.map((s, i) => <Seed key={i} {...s} accent={accent} />)}
      </div>
      <div style={{
        marginTop: 11,
        fontSize: 11.5, color: CT.ink3, letterSpacing: '-0.01em',
        display: 'flex', alignItems: 'center', gap: 10, flexWrap: 'wrap',
      }}>
        {full ? (
          <span style={{ color: accent, fontWeight: 600 }}>
            볼주머니가 가득 찼어요 — 내일 다시 비워져요
          </span>
        ) : (
          <span>오늘 <strong style={{ color: CT.ink, fontWeight: 700 }}>{remaining}개</strong> 더 담을 수 있어요</span>
        )}
        {todayBoost > 0 && (
          <span style={{
            display: 'inline-flex', alignItems: 'center', gap: 3,
            color: accent, fontWeight: 600,
          }}>
            <Seed type="boost" filled={false} accent={accent} mini />
            오늘만 +{todayBoost}
          </span>
        )}
      </div>
    </div>
  );
}

function Seed({ type, filled, accent, mini }) {
  const w = mini ? 8 : 13;
  const h = mini ? 12 : 17;
  // Sunflower seed: teardrop with subtle stripe
  const isBoost = type === 'boost';
  const fill = filled ? accent : (isBoost ? 'rgba(255,255,255,0.55)' : 'rgba(255,255,255,0.7)');
  const stroke = filled ? accent : (isBoost ? accent : CT.hairlineStrong);
  const strokeDash = isBoost && !filled ? '2 1.5' : undefined;
  return (
    <svg width={w} height={h} viewBox="0 0 13 17" style={{ flexShrink: 0 }} aria-hidden="true">
      <path d="M6.5 1 C9.8 3.5 11 7 11 10 C11 13.5 9 16 6.5 16 C4 16 2 13.5 2 10 C2 7 3.2 3.5 6.5 1 Z"
            fill={fill}
            stroke={stroke}
            strokeWidth={1.1}
            strokeDasharray={strokeDash}
            strokeLinejoin="round" />
      {filled && (
        <path d="M6.5 4.5 C5.5 6.5 5.2 9 5.5 12"
              stroke={shade(accent, -25)} strokeOpacity="0.45"
              strokeWidth={0.8} fill="none" strokeLinecap="round" />
      )}
      {isBoost && (
        <path d="M9.5 2.5 L10 4 L11.5 4.5 L10 5 L9.5 6.5 L9 5 L7.5 4.5 L9 4 Z"
              fill={accent} opacity={filled ? 0.7 : 1} />
      )}
    </svg>
  );
}

function SunflowerGlyph({ color = '#8B6F47' }) {
  return (
    <svg width="14" height="14" viewBox="0 0 14 14" fill="none" aria-hidden="true">
      {[0, 45, 90, 135, 180, 225, 270, 315].map(a => (
        <ellipse key={a} cx="7" cy="2.5" rx="1.5" ry="2.4"
                 fill={color} opacity="0.7"
                 transform={`rotate(${a} 7 7)`}/>
      ))}
      <circle cx="7" cy="7" r="2.2" fill={shade(color, -20)}/>
    </svg>
  );
}

function SparkleSmall() {
  return (
    <svg width="11" height="11" viewBox="0 0 16 16" fill="none">
      <path d="M8 1.5L9 6.5L14 7.5L9 8.5L8 13.5L7 8.5L2 7.5L7 6.5L8 1.5Z" fill="#FFF"/>
    </svg>
  );
}
function PlayCircleGlyph() {
  return (
    <svg width="18" height="18" viewBox="0 0 20 20" fill="none">
      <circle cx="10" cy="10" r="8" stroke="currentColor" strokeWidth="1.5"/>
      <path d="M8 6.5 L8 13.5 L14 10 Z" fill="currentColor"/>
    </svg>
  );
}
function FriendsGlyph() {
  return (
    <svg width="18" height="18" viewBox="0 0 20 20" fill="none">
      <circle cx="7" cy="7" r="3" stroke="currentColor" strokeWidth="1.5"/>
      <path d="M2 17c0-2.5 2-4.5 5-4.5s5 2 5 4.5" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round"/>
      <circle cx="14" cy="6" r="2.2" stroke="currentColor" strokeWidth="1.5"/>
      <path d="M11.5 13.5c.3-1.2 1.4-2 2.5-2 1.8 0 3 1.2 3 3" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round"/>
    </svg>
  );
}
function TrashSmallGlyph() {
  return (
    <svg width="18" height="18" viewBox="0 0 18 18" fill="none">
      <path d="M3 5h12M6.5 5V3.5h5V5M4.5 5l.5 10h8l.5-10M8 8v5M10 8v5"
            stroke="currentColor" strokeWidth="1.5"
            strokeLinecap="round" strokeLinejoin="round" fill="none"/>
    </svg>
  );
}

// ─────────────────────────────────────────────────────────────
// Expose
// ─────────────────────────────────────────────────────────────
Object.assign(window, {
  NewCollectionSheet,
  EditCollectionSheet,
  CollectionActionsSheet,
  DeleteConfirmSheet,
  AddCollectionTile,
  EmptyCollectionBody,
  CoverArt,
  CoverIcon,
  CardPickerSheet,
  ShareCollectionSheet,
  COLLECTION_COLORS,
  slotIdFor,
  CollectionTileCard,
  CollectionListRow,
  PinGlyph,
  AllCardsTile,
  UpgradeQuotaSheet,
  Seed,
  SunflowerGlyph,
});
