// CheekPouch — main app with screen navigation
// Screens: home → detail → input → processing

const { useState, useEffect, useRef, useMemo } = React;

// ─────────────────────────────────────────────────────────────
// Sample data — use expanded set from home.jsx if available
// ─────────────────────────────────────────────────────────────
const CARDS = window.EXPANDED_CARDS || [
  {
    id: 'c1', lang: 'ja', langLabel: '일본어',
    foreign: 'おすすめのメニューは何ですか？',
    pronunciation: '오스스메노 메뉴와 난데스카?',
    korean: '추천 메뉴가 뭐예요?',
    category: '식당/카페',
    group: '도쿄 식당 표현',
    dialogue: [
      { who: '점원', foreign: 'いらっしゃいませ、メニューはこちらです。', pron: '이랏샤이마세, 메뉴-와 코치라데스.', korean: '어서오세요, 메뉴는 이쪽입니다.' },
      { who: '나',  foreign: 'おすすめのメニューは何ですか？', pron: '오스스메노 메뉴와 난데스카?', korean: '추천 메뉴가 뭐예요?' },
      { who: '점원', foreign: '豚骨ラーメンがおすすめです。', pron: '톤코츠 라-멘가 오스스메데스.', korean: '돈코츠 라멘을 추천드려요.' },
    ],
  },
  {
    id: 'c2', lang: 'ja', langLabel: '일본어',
    foreign: '水をください。',
    pronunciation: '미즈오 쿠다사이.',
    korean: '물 좀 주세요.',
    category: '식당/카페',
    group: '도쿄 식당 표현',
  },
  {
    id: 'c3', lang: 'ja', langLabel: '일본어',
    foreign: 'お会計お願いします。',
    pronunciation: '오카이케- 오네가이시마스.',
    korean: '계산해 주세요.',
    category: '식당/카페',
    group: '도쿄 식당 표현',
  },
  {
    id: 'c4', lang: 'ja', langLabel: '일본어',
    foreign: '駅までどう行きますか？',
    pronunciation: '에키마데 도- 이키마스카?',
    korean: '역까지 어떻게 가나요?',
    category: '교통/길찾기',
    group: '도쿄 길찾기',
  },
  {
    id: 'c5', lang: 'es', langLabel: '스페인어',
    foreign: '¿Dónde está el baño?',
    pronunciation: '돈데 에스따 엘 바뇨?',
    korean: '화장실이 어디예요?',
    category: '교통/길찾기',
    group: '바르셀로나 휴가',
  },
  {
    id: 'c6', lang: 'es', langLabel: '스페인어',
    foreign: 'La cuenta, por favor.',
    pronunciation: '라 꾸엔따, 뽀르 파보르.',
    korean: '계산서 주세요.',
    category: '식당/카페',
    group: '바르셀로나 휴가',
  },
  {
    id: 'c7', lang: 'en', langLabel: '영어',
    foreign: 'Could I get an extra towel, please?',
    pronunciation: '쿠드 아이 겟 언 엑스트라 타올, 플리즈?',
    korean: '수건 하나만 더 주실 수 있을까요?',
    category: '숙소',
    group: '뉴욕 호텔',
  },
];

const LANGS = (() => {
  const counts = {};
  for (const c of CARDS) counts[c.lang] = (counts[c.lang] || 0) + 1;
  const order = [
    { code: 'all', label: '전체' },
    { code: 'ja',  label: '일본어' },
    { code: 'es',  label: '스페인어' },
    { code: 'en',  label: '영어' },
    { code: 'fr',  label: '프랑스어' },
    { code: 'zh',  label: '중국어' },
    { code: 'th',  label: '태국어' },
  ];
  return order
    .map(o => ({ ...o, count: o.code === 'all' ? CARDS.length : (counts[o.code] || 0) }))
    .filter(o => o.code === 'all' || o.count > 0);
})();

const CATS = [
  '전체', '식당/카페', '교통/길찾기', '숙소', '쇼핑', '관광/구경', '인사/사교', '응급/의료',
];

// ─────────────────────────────────────────────────────────────
// Theme tokens — light + dark (warm espresso)
// ─────────────────────────────────────────────────────────────
const T = window.__cpMakeTheme();

const ACCENTS = {
  walnut:    '#8B6F47',
  terracotta:'#C97B5C',
  forest:    '#4A6B4F',
  plum:      '#8B5C7A',
};

// ─────────────────────────────────────────────────────────────
// Small UI atoms
// ─────────────────────────────────────────────────────────────
function Chip({ children, active, onClick, count, accent }) {
  return (
    <button onClick={onClick} style={{
      flex: '0 0 auto',
      height: 34,
      padding: '0 14px',
      borderRadius: 999,
      border: `0.5px solid ${active ? 'transparent' : T.hairlineStrong}`,
      background: active ? T.ink : 'transparent',
      color: active ? T.surface : T.ink,
      fontSize: 14, fontWeight: 500, letterSpacing: '-0.02em',
      display: 'inline-flex', alignItems: 'center', gap: 6,
      cursor: 'pointer',
      fontFamily: 'inherit',
      whiteSpace: 'nowrap',
      transition: 'background 0.18s ease, color 0.18s ease, border-color 0.18s ease',
      WebkitTapHighlightColor: 'transparent',
    }}>
      <span style={{ whiteSpace: 'nowrap' }}>{children}</span>
      {count !== undefined && (
        <span style={{
          fontSize: 12, fontWeight: 500,
          color: active ? 'rgba(251,247,240,0.55)' : T.ink3,
        }}>{count}</span>
      )}
    </button>
  );
}

function IconButton({ children, onClick, size = 38 }) {
  return (
    <button onClick={onClick} style={{
      width: size, height: size, borderRadius: size,
      border: 'none', background: 'transparent',
      display: 'inline-flex', alignItems: 'center', justifyContent: 'center',
      cursor: 'pointer', color: T.ink, padding: 0,
      WebkitTapHighlightColor: 'transparent',
    }}>{children}</button>
  );
}

function PlayBtn({ playing, onClick, size = 'lg', accent }) {
  const isLg = size === 'lg';
  const h = isLg ? 52 : 34;
  const fs = isLg ? 16 : 13;
  return (
    <button onClick={onClick} style={{
      height: h,
      padding: isLg ? '0 22px' : '0 14px',
      borderRadius: 999,
      background: playing ? T.ink : accent,
      color: '#FFF',
      border: 'none',
      display: 'inline-flex', alignItems: 'center', gap: 9,
      fontSize: fs, fontWeight: 600, letterSpacing: '-0.01em',
      fontFamily: 'inherit',
      cursor: 'pointer',
      transition: 'background 0.18s ease, transform 0.12s ease',
      WebkitTapHighlightColor: 'transparent',
    }}
    onMouseDown={(e) => e.currentTarget.style.transform = 'scale(0.97)'}
    onMouseUp={(e) => e.currentTarget.style.transform = 'scale(1)'}
    onMouseLeave={(e) => e.currentTarget.style.transform = 'scale(1)'}
    >
      <PlayGlyph playing={playing} size={isLg ? 14 : 11} />
      <span>{isLg ? '내 표현 듣기' : '듣기'}</span>
    </button>
  );
}

function PlaySecondary({ playing, onClick, accent, children = '대화 전체 듣기' }) {
  return (
    <button onClick={onClick} style={{
      height: 46,
      padding: '0 18px',
      borderRadius: 999,
      background: 'transparent',
      color: playing ? accent : T.ink,
      border: `1px solid ${playing ? accent : T.hairlineStrong}`,
      display: 'inline-flex', alignItems: 'center', gap: 9,
      fontSize: 15, fontWeight: 500, letterSpacing: '-0.01em',
      fontFamily: 'inherit', cursor: 'pointer',
      transition: 'all 0.18s ease',
      WebkitTapHighlightColor: 'transparent',
    }}>
      <PlayGlyph playing={playing} size={12} color={playing ? accent : T.ink} />
      <span>{children}</span>
    </button>
  );
}

function PlayGlyph({ playing, size = 14, color = '#FFF' }) {
  return (
    <svg width={size} height={size} viewBox="0 0 14 14" style={{ transition: 'transform 0.2s ease' }}>
      {playing ? (
        <g>
          <rect x="3"  y="2.5" width="2.6" height="9" rx="0.4" fill={color} />
          <rect x="8.4" y="2.5" width="2.6" height="9" rx="0.4" fill={color} />
        </g>
      ) : (
        <path d="M3 1.8 L3 12.2 L12 7 Z" fill={color} />
      )}
    </svg>
  );
}

function MiniPlay({ playing, onClick, accent }) {
  return (
    <button onClick={onClick} style={{
      width: 32, height: 32, borderRadius: 32,
      background: playing ? accent : 'transparent',
      border: `1px solid ${playing ? 'transparent' : T.hairlineStrong}`,
      display: 'inline-flex', alignItems: 'center', justifyContent: 'center',
      cursor: 'pointer', padding: 0,
      transition: 'all 0.18s ease',
      WebkitTapHighlightColor: 'transparent',
      flexShrink: 0,
    }}>
      <PlayGlyph playing={playing} size={11} color={playing ? '#FFF' : T.ink} />
    </button>
  );
}

function PlayAllPill({ accent, count }) {
  const [playing, setPlaying] = useState(false);
  const [idx, setIdx] = useState(0);
  useEffect(() => {
    if (!playing) { setIdx(0); return; }
    const t = setInterval(() => {
      setIdx(i => {
        if (i + 1 >= count) { setPlaying(false); return 0; }
        return i + 1;
      });
    }, 1300);
    return () => clearInterval(t);
  }, [playing, count]);

  return (
    <button onClick={(e) => { e.stopPropagation(); setPlaying(p => !p); }} style={{
      height: 32, padding: '0 12px 0 10px',
      borderRadius: 999,
      background: playing ? accent : 'transparent',
      border: `1px solid ${playing ? 'transparent' : T.hairlineStrong}`,
      color: playing ? '#FFF' : T.ink,
      display: 'inline-flex', alignItems: 'center', gap: 7,
      cursor: 'pointer', fontFamily: 'inherit',
      fontSize: 13, fontWeight: 500, letterSpacing: '-0.01em',
      whiteSpace: 'nowrap', flexShrink: 0,
      transition: 'all 0.18s ease',
      WebkitTapHighlightColor: 'transparent',
    }}>
      <PlayGlyph playing={playing} size={10} color={playing ? '#FFF' : T.ink} />
      <span>{playing ? `${idx + 1} / ${count}` : '전체 재생'}</span>
    </button>
  );
}

// ─────────────────────────────────────────────────────────────
// Screen header — replaces default ios nav for non-stack screens
// ─────────────────────────────────────────────────────────────
function Header({ title, lead, trail, big, sub }) {
  return (
    <div style={{ padding: '54px 22px 0' }}>
      <div style={{
        display: 'flex', alignItems: 'center', justifyContent: 'space-between',
        minHeight: 44,
      }}>
        <div>{lead}</div>
        <div style={{ display: 'flex', gap: 6 }}>{trail}</div>
      </div>
      {big ? (
        <div style={{
          fontFamily: 'Pretendard, system-ui',
          fontSize: 32, fontWeight: 700, letterSpacing: '-0.03em',
          color: T.ink, marginTop: 18, lineHeight: 1.1,
        }}>{title}</div>
      ) : null}
      {sub ? (
        <div style={{ fontSize: 14, color: T.ink2, marginTop: 6, letterSpacing: '-0.01em' }}>{sub}</div>
      ) : null}
    </div>
  );
}

// ─────────────────────────────────────────────────────────────
// HOME (legacy all-cards filter view, now drill-down)
// ─────────────────────────────────────────────────────────────
function AllCards({ accent, onBack, onOpenCard }) {
  const [lang, setLang] = useState('all');
  const [cat, setCat] = useState('전체');

  const filtered = useMemo(() => {
    return CARDS.filter(c =>
      (lang === 'all' || c.lang === lang) &&
      (cat === '전체' || c.category === cat)
    );
  }, [lang, cat]);

  return (
    <div style={{ paddingBottom: 40 }}>
      {/* Top bar with back */}
      <div style={{
        padding: '54px 16px 4px',
        display: 'flex', alignItems: 'center', justifyContent: 'space-between',
      }}>
        <IconButton onClick={onBack}>
          <svg width="22" height="22" viewBox="0 0 22 22" fill="none">
            <path d="M14 4L7 11l7 7" stroke={T.ink} strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round"/>
          </svg>
        </IconButton>
        <div style={{
          fontSize: 16, fontWeight: 600, color: T.ink, letterSpacing: '-0.01em',
          whiteSpace: 'nowrap',
        }}>전체 씨앗</div>
        <div style={{ width: 38 }} />
      </div>

      <div style={{ padding: '8px 22px 0' }}>
        <div style={{
          fontSize: 13, color: T.ink2, letterSpacing: '-0.01em',
        }}>담아둔 모든 표현을 언어·카테고리로 걸러볼 수 있어요</div>
      </div>

      {/* Filter section */}
      <div style={{ padding: '14px 0 0' }}>
        <div style={{ padding: '0 22px 8px', fontSize: 11, fontWeight: 600, color: T.ink3, letterSpacing: '0.06em', textTransform: 'uppercase' }}>언어</div>
        <ChipRow>
          {LANGS.map(l => (
            <Chip key={l.code} active={lang === l.code} onClick={() => setLang(l.code)} count={l.count} accent={accent}>
              {l.label}
            </Chip>
          ))}
        </ChipRow>
      </div>

      <div style={{ padding: '14px 0 0' }}>
        <div style={{ padding: '0 22px 8px', fontSize: 11, fontWeight: 600, color: T.ink3, letterSpacing: '0.06em', textTransform: 'uppercase' }}>카테고리</div>
        <ChipRow>
          {CATS.map(c => (
            <Chip key={c} active={cat === c} onClick={() => setCat(c)} accent={accent}>
              {c.replace('/', ' / ')}
            </Chip>
          ))}
        </ChipRow>
      </div>

      {/* Result count + play all */}
      <div style={{
        margin: '24px 22px 12px',
        display: 'flex', alignItems: 'center', justifyContent: 'space-between',
        gap: 12,
      }}>
        <div style={{
          display: 'flex', alignItems: 'baseline', gap: 8,
          fontSize: 14, letterSpacing: '-0.01em',
          whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis',
          minWidth: 0,
        }}>
          <span style={{ color: T.ink, fontWeight: 600 }}>{filtered.length}개</span>
          {(() => {
            const parts = [];
            if (lang !== 'all') parts.push(LANGS.find(l => l.code === lang)?.label);
            if (cat !== '전체') parts.push(cat);
            return parts.length > 0 ? (
              <span style={{ color: T.ink2, overflow: 'hidden', textOverflow: 'ellipsis' }}>· {parts.join(' · ')}</span>
            ) : null;
          })()}
        </div>
        {filtered.length > 0 && (
          <PlayAllPill accent={accent} count={filtered.length} />
        )}
      </div>

      {/* Card list */}
      <div style={{ padding: '0 22px', display: 'flex', flexDirection: 'column', gap: 10 }}>
        {filtered.map(card => (
          <CardRow key={card.id} card={card} accent={accent} onClick={() => onOpenCard(card)} />
        ))}
        {filtered.length === 0 && (
          <div style={{
            margin: '12px 0',
            padding: '36px 18px',
            border: '1px dashed rgba(60,42,20,0.18)',
            borderRadius: 16,
            textAlign: 'center',
            color: T.ink3, fontSize: 14, letterSpacing: '-0.01em',
          }}>조건에 맞는 씨앗이 없어요</div>
        )}
      </div>
    </div>
  );
}

// ─────────────────────────────────────────────────────────────
// LEGACY Home (kept for reference, no longer mounted)
// ─────────────────────────────────────────────────────────────
function Home({ accent, dailyCount, onAdd, onOpenCard }) {
  const [lang, setLang] = useState('all');
  const [cat, setCat] = useState('전체');

  const filtered = useMemo(() => {
    return CARDS.filter(c =>
      (lang === 'all' || c.lang === lang) &&
      (cat === '전체' || c.category === cat)
    );
  }, [lang, cat]);

  return (
    <div style={{ paddingBottom: 40 }}>
      <Header
        big
        title="CheekPouch"
        lead={null}
        trail={[
          <IconButton key="search" onClick={() => {}}>
            <svg width="20" height="20" viewBox="0 0 20 20" fill="none">
              <circle cx="9" cy="9" r="6.2" stroke={T.ink} strokeWidth="1.6"/>
              <path d="M13.6 13.6L17 17" stroke={T.ink} strokeWidth="1.6" strokeLinecap="round"/>
            </svg>
          </IconButton>,
          <IconButton key="add" onClick={onAdd}>
            <svg width="22" height="22" viewBox="0 0 22 22" fill="none">
              <path d="M11 4v14M4 11h14" stroke={T.ink} strokeWidth="1.8" strokeLinecap="round"/>
            </svg>
          </IconButton>,
        ]}
      />

      {/* Daily cheek indicator hero */}
      <div style={{
        margin: '20px 22px 8px',
        padding: '16px 20px 18px',
        background: T.surface,
        borderRadius: 22,
        border: `0.5px solid ${T.hairline}`,
        display: 'flex', alignItems: 'center', gap: 16,
      }}>
        <CheekPouch count={dailyCount} accent={accent} size={110} />
        <div style={{ flex: 1, minWidth: 0 }}>
          <div style={{
            fontSize: 12, fontWeight: 500, color: T.ink3,
            letterSpacing: '0.04em', textTransform: 'uppercase',
          }}>오늘의 볼주머니</div>
          <div style={{
            marginTop: 4,
            fontSize: 19, fontWeight: 600, color: T.ink,
            letterSpacing: '-0.02em', lineHeight: 1.25,
          }}>
            {dailyCount === 0 ? '오늘은 비어있어요' :
             dailyCount >= 10 ? '볼이 빵빵해요!' :
             <>오늘 <span style={{ color: accent }}>{dailyCount}개</span> 담았어요</>}
          </div>
          <div style={{ marginTop: 8, display: 'flex', gap: 5 }}>
            {Array.from({ length: 10 }).map((_, i) => (
              <div key={i} style={{
                width: 6, height: 6, borderRadius: 6,
                background: i < dailyCount ? accent : T.hairlineStrong,
                transition: 'background 0.3s ease',
              }} />
            ))}
          </div>
        </div>
      </div>

      {/* Filter section */}
      <div style={{ padding: '14px 0 0' }}>
        <div style={{ padding: '0 22px 8px', fontSize: 11, fontWeight: 600, color: T.ink3, letterSpacing: '0.06em', textTransform: 'uppercase' }}>언어</div>
        <ChipRow>
          {LANGS.map(l => (
            <Chip key={l.code} active={lang === l.code} onClick={() => setLang(l.code)} count={l.count} accent={accent}>
              {l.label}
            </Chip>
          ))}
        </ChipRow>
      </div>

      <div style={{ padding: '14px 0 0' }}>
        <div style={{ padding: '0 22px 8px', fontSize: 11, fontWeight: 600, color: T.ink3, letterSpacing: '0.06em', textTransform: 'uppercase' }}>카테고리</div>
        <ChipRow>
          {CATS.map(c => (
            <Chip key={c} active={cat === c} onClick={() => setCat(c)} accent={accent}>
              {c.replace('/', ' / ')}
            </Chip>
          ))}
        </ChipRow>
      </div>

      {/* Result count + play all */}
      <div style={{
        margin: '24px 22px 12px',
        display: 'flex', alignItems: 'center', justifyContent: 'space-between',
        gap: 12,
      }}>
        <div style={{
          display: 'flex', alignItems: 'baseline', gap: 8,
          fontSize: 14, letterSpacing: '-0.01em',
          whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis',
          minWidth: 0,
        }}>
          <span style={{ color: T.ink, fontWeight: 600 }}>{filtered.length}개</span>
          {(() => {
            const parts = [];
            if (lang !== 'all') parts.push(LANGS.find(l => l.code === lang)?.label);
            if (cat !== '전체') parts.push(cat);
            return parts.length > 0 ? (
              <span style={{ color: T.ink2, overflow: 'hidden', textOverflow: 'ellipsis' }}>· {parts.join(' · ')}</span>
            ) : null;
          })()}
        </div>
        {filtered.length > 0 && (
          <PlayAllPill accent={accent} count={filtered.length} />
        )}
      </div>

      {/* Card list */}
      <div style={{ padding: '0 22px', display: 'flex', flexDirection: 'column', gap: 10 }}>
        {filtered.map(card => (
          <CardRow key={card.id} card={card} accent={accent} onClick={() => onOpenCard(card)} />
        ))}
      </div>
    </div>
  );
}

function ChipRow({ children }) {
  return (
    <div style={{
      display: 'flex', gap: 8, padding: '0 22px',
      overflowX: 'auto', overflowY: 'hidden',
      scrollbarWidth: 'none',
    }}>{children}</div>
  );
}

function CardRow({ card, accent, onClick }) {
  const [playing, setPlaying] = useState(false);
  const langFont = card.lang === 'ja' ? '"Noto Sans JP", Pretendard' :
                   card.lang === 'es' || card.lang === 'en' ? 'Noto Sans, Pretendard' :
                   'Pretendard';
  return (
    <div onClick={onClick} style={{
      background: T.surface,
      borderRadius: 18,
      border: `0.5px solid ${T.hairline}`,
      padding: '16px 16px 14px',
      cursor: 'pointer',
      display: 'flex', alignItems: 'flex-start', gap: 12,
      transition: 'transform 0.12s ease, background 0.18s ease',
      WebkitTapHighlightColor: 'transparent',
    }}
    onMouseDown={(e) => e.currentTarget.style.transform = 'scale(0.985)'}
    onMouseUp={(e) => e.currentTarget.style.transform = 'scale(1)'}
    onMouseLeave={(e) => e.currentTarget.style.transform = 'scale(1)'}
    >
      <div style={{ flex: 1, minWidth: 0 }}>
        <div style={{
          fontFamily: langFont,
          fontSize: 17, fontWeight: 500, color: T.ink,
          letterSpacing: '-0.01em', lineHeight: 1.35,
          marginBottom: 4,
        }}>{card.foreign}</div>
        <div style={{
          fontSize: 13, color: T.ink2,
          letterSpacing: '-0.01em', lineHeight: 1.4,
        }}>{card.pronunciation}</div>
        <div style={{
          marginTop: 10, display: 'flex', alignItems: 'center', gap: 8,
          fontSize: 11, color: T.ink3, letterSpacing: '0.02em',
          whiteSpace: 'nowrap',
        }}>
          <span style={{
            padding: '2px 7px', borderRadius: 4,
            background: 'rgba(60,42,20,0.05)', color: T.ink2,
            fontWeight: 500,
          }}>{card.category}</span>
          <span>·</span>
          <span style={{ display: 'inline-flex', alignItems: 'center', gap: 4 }}>
            <span style={{ fontSize: 12, lineHeight: 1 }}>{
              ({ja:'🇯🇵',es:'🇪🇸',en:'🇺🇸',fr:'🇫🇷',zh:'🇨🇳',th:'🇹🇭',de:'🇩🇪',it:'🇮🇹',vi:'🇻🇳',pt:'🇵🇹',id:'🇮🇩'})[card.lang] || '🌐'
            }</span>
            <span>{card.langLabel}</span>
          </span>
        </div>
      </div>
      <MiniPlay playing={playing} accent={accent} onClick={(e) => { e.stopPropagation(); setPlaying(p => !p); }} />
    </div>
  );
}

// ─────────────────────────────────────────────────────────────
// CARD DETAIL
// ─────────────────────────────────────────────────────────────
function CardDetail({ card, accent, onBack, groups, onMove, onShare, onDelete, onOpenGroup, onSetCategories }) {
  const [playMine, setPlayMine] = useState(false);
  const [playDialogue, setPlayDialogue] = useState(false);
  const [currentLine, setCurrentLine] = useState(-1);
  const [wordIdx, setWordIdx] = useState(-1);
  const [sheet, setSheet] = useState(null); // 'actions' | 'move' | 'delete' | 'categories' | null

  // Split foreign sentence into spoken tokens.
  // For JA, split by grapheme runs (kana/kanji clusters of 1-3 chars); else split on whitespace.
  const wordTokens = React.useMemo(() => {
    const f = card.foreign || '';
    if (card.lang === 'ja' || card.lang === 'zh') {
      // Group every 2-3 CJK characters into a word-ish chunk; preserve punctuation as own tokens
      const out = [];
      let buf = '';
      const flush = () => { if (buf) { out.push(buf); buf = ''; } };
      for (const ch of [...f]) {
        if (/[\s\p{P}]/u.test(ch)) {
          flush();
          out.push(ch);
        } else {
          buf += ch;
          if (buf.length >= 2) flush();
        }
      }
      flush();
      return out;
    }
    // latin scripts: split keeping spaces/punct attached to previous word
    return f.split(/(\s+)/).filter(t => t.length > 0);
  }, [card.foreign, card.lang]);

  const speakableIndices = React.useMemo(() =>
    wordTokens.map((t, i) => /[\p{L}\p{N}]/u.test(t) ? i : -1).filter(i => i >= 0)
  , [wordTokens]);

  useEffect(() => {
    if (!playDialogue) { setCurrentLine(-1); return; }
    let i = 0;
    setCurrentLine(0);
    const tick = setInterval(() => {
      i++;
      if (i >= (card.dialogue?.length || 3)) {
        clearInterval(tick);
        setPlayDialogue(false);
        setCurrentLine(-1);
      } else {
        setCurrentLine(i);
      }
    }, 1600);
    return () => clearInterval(tick);
  }, [playDialogue, card]);

  useEffect(() => {
    if (!playMine) { setWordIdx(-1); return; }
    // Step through speakable tokens. Total duration ~1900ms keeps prior behaviour;
    // step = total / count.
    const total = Math.max(1400, Math.min(2800, 220 * speakableIndices.length + 600));
    const step = total / Math.max(1, speakableIndices.length);
    let k = 0;
    setWordIdx(speakableIndices[0] ?? -1);
    const tick = setInterval(() => {
      k++;
      if (k >= speakableIndices.length) {
        clearInterval(tick);
        setPlayMine(false);
        setWordIdx(-1);
      } else {
        setWordIdx(speakableIndices[k]);
      }
    }, step);
    return () => clearInterval(tick);
  }, [playMine, speakableIndices]);

  const langFont = card.lang === 'ja' ? '"Noto Sans JP", Pretendard' : 'Noto Sans, Pretendard';
  const dialogue = card.dialogue;

  return (
    <div style={{ paddingBottom: 60 }}>
      {/* Top bar — sticky */}
      <div style={{
        position: 'sticky', top: 0, zIndex: 30,
        background: T.bg,
        padding: '54px 16px 8px',
        display: 'flex', alignItems: 'center', justifyContent: 'space-between',
      }}>
        <IconButton onClick={onBack}>
          <svg width="22" height="22" viewBox="0 0 22 22" fill="none">
            <path d="M14 4L7 11l7 7" stroke={T.ink} strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round"/>
          </svg>
        </IconButton>
        <IconButton onClick={() => setSheet('actions')}>
          <svg width="20" height="6" viewBox="0 0 20 6">
            <circle cx="3" cy="3" r="1.6" fill={T.ink}/>
            <circle cx="10" cy="3" r="1.6" fill={T.ink}/>
            <circle cx="17" cy="3" r="1.6" fill={T.ink}/>
          </svg>
        </IconButton>
      </div>

      {/* Headline area */}
      <div style={{ padding: '14px 24px 0' }}>
        <div style={{
          display: 'inline-flex', alignItems: 'center', gap: 6,
          fontSize: 11, fontWeight: 600, letterSpacing: '0.08em',
          color: T.ink3, textTransform: 'uppercase',
        }}>
          <span style={{ fontSize: 14, lineHeight: 1, letterSpacing: 0 }}>{
            ({ja:'🇯🇵',es:'🇪🇸',en:'🇺🇸',fr:'🇫🇷',zh:'🇨🇳',th:'🇹🇭',de:'🇩🇪',it:'🇮🇹',vi:'🇻🇳',pt:'🇵🇹',id:'🇮🇩'})[card.lang] || '🌐'
          }</span>
          <span>{card.langLabel}</span>
        </div>
        <div style={{
          marginTop: 12,
          fontFamily: langFont,
          fontSize: 30, fontWeight: 600,
          color: T.ink, lineHeight: 1.35, letterSpacing: '-0.02em',
          textWrap: 'balance',
        }}>
          {wordTokens.map((tok, i) => {
            const speakable = /[\p{L}\p{N}]/u.test(tok);
            const isCurrent = playMine && i === wordIdx;
            const isPast = playMine && wordIdx >= 0 && i < wordIdx && speakable;
            return (
              <span key={i} style={{
                background: isCurrent ? `${accent}33` : 'transparent',
                color: isCurrent ? accent : (isPast ? T.ink2 : T.ink),
                borderRadius: 4,
                padding: isCurrent ? '0 2px' : 0,
                transition: 'background 0.16s ease, color 0.16s ease',
              }}>{tok}</span>
            );
          })}
        </div>

        <div style={{
          marginTop: 12,
          fontSize: 17, color: T.ink2,
          letterSpacing: '-0.01em', lineHeight: 1.45,
        }}>{card.pronunciation}</div>

        <div style={{
          marginTop: 8,
          fontSize: 15, color: T.ink3,
          letterSpacing: '-0.01em', lineHeight: 1.45,
        }}>{card.korean}</div>

        {/* Collection membership chips */}
        {(() => {
          const memberships = (groups || []).filter(g =>
            (g.cards || []).some(c => c.id === card.id)
          );
          return (
            <div style={{
              marginTop: 16,
              display: 'flex', flexWrap: 'wrap', gap: 6,
              alignItems: 'center',
            }}>
              {memberships.map(g => (
                <button key={g.name}
                        onClick={() => onOpenGroup && onOpenGroup(g)}
                        style={{
                  height: 28, padding: '0 11px 0 9px',
                  borderRadius: 999,
                  background: `${g.color || accent}14`,
                  border: `0.5px solid ${g.color || accent}30`,
                  color: g.color || accent,
                  display: 'inline-flex', alignItems: 'center', gap: 5,
                  fontFamily: 'inherit',
                  fontSize: 12, fontWeight: 600,
                  letterSpacing: '-0.01em',
                  cursor: 'pointer',
                  WebkitTapHighlightColor: 'transparent',
                }}>
                  <span style={{
                    width: 6, height: 6, borderRadius: 6,
                    background: g.color || accent,
                  }} />
                  {g.name}
                </button>
              ))}
              <button onClick={() => setSheet('move')} style={{
                height: 28, padding: '0 10px 0 8px',
                borderRadius: 999,
                background: 'transparent',
                border: `1px dashed ${T.hairlineStrong}`,
                color: T.ink2,
                display: 'inline-flex', alignItems: 'center', gap: 3,
                fontFamily: 'inherit',
                fontSize: 12, fontWeight: 500,
                letterSpacing: '-0.01em',
                cursor: 'pointer',
                WebkitTapHighlightColor: 'transparent',
              }}>
                <svg width="11" height="11" viewBox="0 0 14 14" fill="none">
                  <path d="M7 2v10M2 7h10" stroke={T.ink2} strokeWidth="1.6" strokeLinecap="round"/>
                </svg>
                {memberships.length === 0 ? '주머니에 추가' : '추가'}
              </button>
            </div>
          );
        })()}
      </div>

      {/* Primary play button */}
      <div style={{ padding: '24px 24px 0' }}>
        <PlayBtn playing={playMine} accent={accent} onClick={() => setPlayMine(p => !p)} />
      </div>

      {/* Dialogue context */}
      {dialogue && dialogue.length > 0 && (
      <div style={{ padding: '32px 24px 0' }}>
        <div style={{
          display: 'flex', alignItems: 'center', gap: 10,
          fontSize: 11, fontWeight: 600, letterSpacing: '0.08em',
          color: T.ink3, textTransform: 'uppercase',
        }}>
          <div style={{ height: 0.5, background: T.hairlineStrong, flex: 1 }} />
          <span style={{ whiteSpace: 'nowrap' }}>이런 상황에서</span>
          <div style={{ height: 0.5, background: T.hairlineStrong, flex: 1 }} />
        </div>

        <div style={{ marginTop: 18, display: 'flex', flexDirection: 'column', gap: 18 }}>
          {dialogue.map((line, i) => {
            const isMe = line.who === '나';
            const isActive = currentLine === i;
            return (
              <div key={i} style={{
                background: isActive
                  ? `${accent}14`
                  : (isMe ? T.surface : 'transparent'),
                border: isActive
                  ? `1.5px solid ${accent}`
                  : (isMe ? `0.5px solid ${T.hairline}` : 'none'),
                borderRadius: isActive ? 14 : (isMe ? 14 : 10),
                padding: isActive ? '13px 15px' : (isMe ? '14px 16px' : '4px 6px'),
                transition: 'background 0.25s ease, border-color 0.25s ease, padding 0.18s ease',
                position: 'relative',
                boxShadow: isActive ? `0 4px 16px ${accent}1F` : 'none',
              }}>
                {isActive && (
                  <div style={{
                    position: 'absolute', left: -10, top: 8, bottom: 8,
                    width: 4, borderRadius: 4,
                    background: accent,
                  }} />
                )}
                <div style={{
                  display: 'flex', alignItems: 'center', gap: 6,
                  marginBottom: 6,
                }}>
                  <div style={{
                    fontSize: 11, fontWeight: 600,
                    color: isActive ? accent : (isMe ? accent : T.ink3),
                    letterSpacing: '0.04em',
                  }}>{line.who}</div>
                  {isActive && (
                    <span style={{
                      display: 'inline-flex', alignItems: 'center', gap: 3,
                      height: 14, padding: '0 5px', borderRadius: 999,
                      background: accent, color: '#FFF',
                      fontSize: 9, fontWeight: 700,
                      letterSpacing: '0.04em', textTransform: 'uppercase',
                    }}>
                      <svg width="6" height="8" viewBox="0 0 6 8" fill="none" aria-hidden="true">
                        <rect x="0"   y="2" width="1.2" height="4" rx="0.6" fill="#FFF">
                          <animate attributeName="height" values="2;4;2" dur="0.7s" repeatCount="indefinite" />
                          <animate attributeName="y" values="3;2;3" dur="0.7s" repeatCount="indefinite" />
                        </rect>
                        <rect x="2.4" y="1" width="1.2" height="6" rx="0.6" fill="#FFF">
                          <animate attributeName="height" values="4;6;4" dur="0.6s" begin="0.1s" repeatCount="indefinite" />
                          <animate attributeName="y" values="2;1;2" dur="0.6s" begin="0.1s" repeatCount="indefinite" />
                        </rect>
                        <rect x="4.8" y="2.5" width="1.2" height="3" rx="0.6" fill="#FFF">
                          <animate attributeName="height" values="2;3;2" dur="0.8s" begin="0.2s" repeatCount="indefinite" />
                          <animate attributeName="y" values="3;2.5;3" dur="0.8s" begin="0.2s" repeatCount="indefinite" />
                        </rect>
                      </svg>
                      재생 중
                    </span>
                  )}
                </div>
                <div style={{
                  fontFamily: langFont,
                  fontSize: 16,
                  fontWeight: isActive ? 600 : 500,
                  color: isActive ? T.ink : T.ink,
                  letterSpacing: '-0.01em', lineHeight: 1.4,
                  transition: 'font-weight 0.2s ease',
                }}>{line.foreign}</div>
                {line.pron && (
                  <div style={{
                    marginTop: 3,
                    fontSize: 13,
                    color: isActive ? accent : T.ink2,
                    fontWeight: isActive ? 500 : 400,
                    letterSpacing: '-0.01em', lineHeight: 1.4,
                    transition: 'color 0.2s ease',
                  }}>{line.pron}</div>
                )}
                <div style={{
                  marginTop: 3,
                  fontSize: 13,
                  color: isActive ? T.ink2 : T.ink3,
                  letterSpacing: '-0.01em', lineHeight: 1.4,
                  transition: 'color 0.2s ease',
                }}>{line.korean}</div>
              </div>
            );
          })}
        </div>

        <div style={{ marginTop: 22 }}>
          <PlaySecondary playing={playDialogue} accent={accent} onClick={() => setPlayDialogue(p => !p)} />
        </div>
      </div>
      )}

      {/* Footer: categories (multi) + tone (fixed, set at creation) */}
      {(() => {
        const categories = (card.categories && card.categories.length > 0)
          ? card.categories
          : (card.category ? [card.category] : []);
        const tone = card.tone || '자동';
        return (
          <div style={{
            margin: '32px 24px 0',
            padding: '20px 0 0',
            borderTop: `0.5px solid ${T.hairlineStrong}`,
          }}>
            <div style={{
              fontSize: 11, fontWeight: 600, color: T.ink3,
              letterSpacing: '0.08em', textTransform: 'uppercase',
              padding: '0 0 10px',
            }}>분류</div>
            <div style={{ display: 'flex', gap: 8, flexWrap: 'wrap' }}>
              {categories.map(c => (
                <CategoryChip key={c} accent={accent}
                  onRemove={categories.length > 1
                    ? () => onSetCategories && onSetCategories(categories.filter(x => x !== c))
                    : null
                  }>{c}</CategoryChip>
              ))}
              <button onClick={() => setSheet('categories')} style={{
                height: 32, padding: '0 12px', borderRadius: 999,
                border: `1px dashed ${T.hairlineStrong}`,
                background: 'transparent',
                display: 'inline-flex', alignItems: 'center', gap: 4,
                fontFamily: 'inherit', fontSize: 13, color: T.ink2,
                letterSpacing: '-0.01em', cursor: 'pointer',
                WebkitTapHighlightColor: 'transparent',
              }}>
                <svg width="10" height="10" viewBox="0 0 10 10" fill="none">
                  <path d="M5 1.5v7M1.5 5h7" stroke={T.ink2} strokeWidth="1.5" strokeLinecap="round"/>
                </svg>
                분류 추가
              </button>
            </div>

            <div style={{
              marginTop: 18,
              display: 'flex', alignItems: 'center', gap: 10,
            }}>
              <div style={{
                fontSize: 11, fontWeight: 600, color: T.ink3,
                letterSpacing: '0.08em', textTransform: 'uppercase',
                flexShrink: 0,
              }}>톤</div>
              <div style={{
                height: 28, padding: '0 11px', borderRadius: 999,
                background: 'rgba(60,42,20,0.05)',
                display: 'inline-flex', alignItems: 'center', gap: 6,
                fontSize: 12.5, color: T.ink2, letterSpacing: '-0.01em',
              }}>
                <span style={{ color: T.ink, fontWeight: 500 }}>{tone}</span>
                <span style={{ color: T.ink3, fontSize: 11 }}>· 생성 시 선택</span>
              </div>
            </div>
          </div>
        );
      })()}

      {/* Card actions sheet */}
      {sheet === 'actions' && (() => {
        const cats = (card.categories && card.categories.length > 0)
          ? card.categories
          : (card.category ? [card.category] : []);
        const colls = [card.group, ...(card.extraGroups || [])].filter(Boolean);
        return (
        <SheetShell onClose={() => setSheet(null)}>
          {/* Card identity */}
          <div style={{ padding: '0 24px 14px' }}>
            <div style={{
              display: 'flex', alignItems: 'flex-start', gap: 10,
            }}>
              <div style={{ flex: 1, minWidth: 0 }}>
                <div style={{
                  fontFamily: langFont,
                  fontSize: 17, fontWeight: 600, color: T.ink,
                  letterSpacing: '-0.02em', lineHeight: 1.3,
                  display: '-webkit-box', WebkitLineClamp: 2, WebkitBoxOrient: 'vertical',
                  overflow: 'hidden',
                  textOverflow: 'ellipsis',
                  wordBreak: 'break-word',
                }}>{card.foreign}</div>
                <div style={{
                  marginTop: 4,
                  fontSize: 13, color: T.ink2,
                  letterSpacing: '-0.01em', lineHeight: 1.4,
                  display: '-webkit-box', WebkitLineClamp: 1, WebkitBoxOrient: 'vertical',
                  overflow: 'hidden',
                  textOverflow: 'ellipsis',
                }}>{card.korean}</div>
              </div>
              <span style={{
                flexShrink: 0,
                height: 24, padding: '0 9px', borderRadius: 999,
                background: 'rgba(60,42,20,0.05)',
                display: 'inline-flex', alignItems: 'center', gap: 4,
                fontSize: 11, fontWeight: 600, color: T.ink2,
                letterSpacing: '-0.01em',
              }}>
                <svg width="10" height="10" viewBox="0 0 12 12" fill="none" aria-hidden="true">
                  <circle cx="6" cy="6" r="4.5" stroke={T.ink3} strokeWidth="1.1" fill="none"/>
                  <path d="M1.5 6h9M6 1.5c1.5 1.6 1.5 7.4 0 9M6 1.5c-1.5 1.6-1.5 7.4 0 9"
                        stroke={T.ink3} strokeWidth="1.1" fill="none"/>
                </svg>
                {card.langLabel || ''}
              </span>
            </div>

            {/* Meta — grouped rows */}
            {(colls.length > 0 || cats.length > 0) && (
              <div style={{
                marginTop: 14,
                display: 'flex', flexDirection: 'column', gap: 8,
              }}>
                {colls.length > 0 && (
                  <MetaRow icon={
                    <svg width="13" height="13" viewBox="0 0 14 14" fill="none">
                      <path d="M1.5 4a1.5 1.5 0 0 1 1.5-1.5h2.6l1.2 1.2H11a1.5 1.5 0 0 1 1.5 1.5v5.3A1.5 1.5 0 0 1 11 12H3a1.5 1.5 0 0 1-1.5-1.5V4z"
                            stroke={T.ink3} strokeWidth="1.2" strokeLinejoin="round" fill="none"/>
                    </svg>
                  }>
                    {colls.map(c => (
                      <SheetMetaChip key={c} accent={accent}
                        onClick={() => { setSheet(null); onOpenGroup && onOpenGroup({ name: c }); }}>
                        {c}
                      </SheetMetaChip>
                    ))}
                  </MetaRow>
                )}
                {cats.length > 0 && (
                  <MetaRow icon={
                    <svg width="13" height="13" viewBox="0 0 14 14" fill="none">
                      <path d="M2 7l5-5h4a1 1 0 0 1 1 1v4l-5 5a1 1 0 0 1-1.4 0L2 8.4a1 1 0 0 1 0-1.4z"
                            stroke={T.ink3} strokeWidth="1.2" strokeLinejoin="round" fill="none"/>
                      <circle cx="9.4" cy="4.6" r="0.9" fill={T.ink3}/>
                    </svg>
                  }>
                    {cats.map(c => (
                      <SheetMetaChip key={c}>{c}</SheetMetaChip>
                    ))}
                  </MetaRow>
                )}
              </div>
            )}
          </div>

          <div style={{
            margin: '4px 16px 0',
            background: T.surface2,
            borderRadius: 14,
            border: `0.5px solid ${T.hairline}`,
            overflow: 'hidden',
          }}>
            <CardActionRow icon={<MoveCardGlyph />} label="주머니에 담기"
                           sub={colls.length > 0 ? `${colls.length}개 주머니에 담겨 있어요` : '아직 주머니에 없어요'}
                           onClick={() => setSheet('move')} />
            <CardActionRow icon={<ShareCardGlyph />} label="씨앗 공유"
                           sub="친구·카톡·링크"
                           onClick={() => setSheet('share')} />
          </div>
          <div style={{
            margin: '10px 16px 0',
            background: T.surface2,
            borderRadius: 14,
            border: `0.5px solid ${T.hairline}`,
            overflow: 'hidden',
          }}>
            <CardActionRow icon={<TrashCardGlyph color="#B5483C" />} label="씨앗 삭제"
                           danger onClick={() => setSheet('delete')} />
          </div>
        </SheetShell>
        );
      })()}

      {sheet === 'move' && (
        <SheetShell title="주머니에 추가" onClose={() => setSheet(null)}>
          <div style={{
            padding: '0 24px 12px',
            fontSize: 12.5, color: T.ink2, letterSpacing: '-0.01em',
            textAlign: 'center', lineHeight: 1.5,
          }}>여러 주머니에 동시에 담을 수 있어요</div>
          {(groups || []).map(g => {
            const isIn = (g.cards || []).some(c => c.id === card.id);
            return (
              <button key={g.name}
                      onClick={() => onMove && onMove(g.name, !isIn)}
                      style={{
                width: '100%', display: 'flex', alignItems: 'center', gap: 12,
                padding: '12px 16px',
                border: 'none', background: 'transparent',
                cursor: 'pointer',
                textAlign: 'left', fontFamily: 'inherit',
                WebkitTapHighlightColor: 'transparent',
              }}>
                <span style={{
                  width: 22, height: 22, borderRadius: 6,
                  background: isIn ? (g.color || accent) : 'transparent',
                  border: isIn ? 'none' : `1.5px solid ${T.hairlineStrong}`,
                  display: 'inline-flex', alignItems: 'center', justifyContent: 'center',
                  flexShrink: 0,
                  transition: 'background 0.15s ease',
                }}>
                  {isIn && (
                    <svg width="12" height="12" 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={{
                  width: 10, height: 10, borderRadius: 10,
                  background: g.color || T.ink3, flexShrink: 0,
                }} />
                <span style={{ flex: 1,
                  fontSize: 15, fontWeight: 500, color: T.ink,
                  letterSpacing: '-0.01em',
                }}>{g.name}</span>
                <span style={{ fontSize: 12, color: T.ink3 }}>{g.count}개</span>
              </button>
            );
          })}
          {(!groups || groups.length === 0) && (
            <div style={{
              padding: '24px 16px', textAlign: 'center',
              color: T.ink3, fontSize: 13,
            }}>주머니가 없어요</div>
          )}
          <div style={{ padding: '14px 22px 0' }}>
            <button onClick={() => setSheet(null)} style={{
              width: '100%', height: 48, borderRadius: 14,
              background: accent, color: '#FFF',
              border: 'none', fontFamily: 'inherit',
              fontSize: 15, fontWeight: 600, letterSpacing: '-0.01em',
              cursor: 'pointer', WebkitTapHighlightColor: 'transparent',
            }}>완료</button>
          </div>
        </SheetShell>
      )}

      {sheet === 'categories' && (() => {
        const current = (card.categories && card.categories.length > 0)
          ? card.categories
          : (card.category ? [card.category] : []);
        const toggle = (c) => {
          const has = current.includes(c);
          const next = has ? current.filter(x => x !== c) : [...current, c];
          onSetCategories && onSetCategories(next);
        };
        return (
          <SheetShell title="분류 선택" onClose={() => setSheet(null)}>
            <div style={{
              padding: '0 24px 12px',
              fontSize: 12.5, color: T.ink2, letterSpacing: '-0.01em',
              textAlign: 'center', lineHeight: 1.5,
            }}>여러 분류에 동시에 담을 수 있어요</div>
            {BASE_CATS.map(c => {
              const isIn = current.includes(c);
              return (
                <button key={c} onClick={() => toggle(c)} style={{
                  width: '100%', display: 'flex', alignItems: 'center', gap: 12,
                  padding: '12px 16px',
                  border: 'none', background: 'transparent',
                  cursor: 'pointer',
                  textAlign: 'left', fontFamily: 'inherit',
                  WebkitTapHighlightColor: 'transparent',
                }}>
                  <span style={{
                    width: 22, height: 22, borderRadius: 6,
                    background: isIn ? accent : 'transparent',
                    border: isIn ? 'none' : `1.5px solid ${T.hairlineStrong}`,
                    display: 'inline-flex', alignItems: 'center', justifyContent: 'center',
                    flexShrink: 0,
                    transition: 'background 0.15s ease',
                  }}>
                    {isIn && (
                      <svg width="12" height="12" 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={{
                    flex: 1, fontSize: 15, fontWeight: 500, color: T.ink,
                    letterSpacing: '-0.01em',
                  }}>{c}</span>
                </button>
              );
            })}
            <div style={{ padding: '14px 22px 0' }}>
              <button onClick={() => setSheet(null)} style={{
                width: '100%', height: 48, borderRadius: 14,
                background: accent, color: '#FFF',
                border: 'none', fontFamily: 'inherit',
                fontSize: 15, fontWeight: 600, letterSpacing: '-0.01em',
                cursor: 'pointer', WebkitTapHighlightColor: 'transparent',
              }}>완료</button>
            </div>
          </SheetShell>
        );
      })()}

      {sheet === 'share' && (
        <CardShareSheet card={card} accent={accent}
                        onClose={() => setSheet(null)}
                        onSent={(to) => {
                          setSheet(null);
                          onShare && onShare(to);
                        }} />
      )}

      {sheet === 'delete' && (
        <SheetShell onClose={() => setSheet(null)}>
          <div style={{ padding: '4px 24px 0', textAlign: 'center' }}>
            <div style={{
              width: 56, height: 56, borderRadius: 56,
              background: 'rgba(181, 72, 60, 0.10)',
              display: 'inline-flex', alignItems: 'center', justifyContent: 'center',
              marginBottom: 12,
            }}>
              <TrashCardGlyph color="#B5483C" size={22} />
            </div>
            <div style={{
              fontSize: 18, fontWeight: 700, color: T.ink,
              letterSpacing: '-0.02em', lineHeight: 1.3,
            }}>이 씨앗을 삭제할까요?</div>
            <div style={{
              marginTop: 8,
              fontSize: 13.5, color: T.ink2,
              letterSpacing: '-0.01em', lineHeight: 1.55,
            }}>되돌릴 수 없어요. 볼주머니 자리 한 칸이 다시 비워져요.</div>
          </div>
          <div style={{
            padding: '20px 22px 0',
            display: 'flex', flexDirection: 'column', gap: 8,
          }}>
            <button onClick={() => { onDelete && onDelete(); setSheet(null); }} style={{
              height: 48, borderRadius: 14,
              background: '#B5483C', color: '#FFF',
              border: 'none', fontFamily: 'inherit',
              fontSize: 15, fontWeight: 600, letterSpacing: '-0.01em',
              cursor: 'pointer', WebkitTapHighlightColor: 'transparent',
            }}>삭제</button>
            <button onClick={() => setSheet(null)} style={{
              height: 48, borderRadius: 14,
              background: 'transparent', color: T.ink,
              border: 'none',
              fontFamily: 'inherit', fontSize: 15, fontWeight: 500,
              cursor: 'pointer', WebkitTapHighlightColor: 'transparent',
            }}>취소</button>
          </div>
        </SheetShell>
      )}
    </div>
  );
}

function CardActionRow({ icon, label, sub, onClick, danger }) {
  return (
    <button onClick={onClick} style={{
      width: '100%', display: 'flex', alignItems: 'center', gap: 12,
      padding: '14px 14px',
      borderTop: 'inherit',
      background: 'transparent', border: 'none',
      cursor: 'pointer', textAlign: 'left', fontFamily: 'inherit',
      WebkitTapHighlightColor: 'transparent',
    }}>
      <span style={{
        width: 28, height: 28, borderRadius: 9,
        background: danger ? 'rgba(181,72,60,0.12)' : 'rgba(60,42,20,0.06)',
        display: 'inline-flex', alignItems: 'center', justifyContent: 'center',
        flexShrink: 0,
      }}>{icon}</span>
      <span style={{ flex: 1, minWidth: 0 }}>
        <span style={{
          display: 'block',
          fontSize: 15, fontWeight: 500,
          color: danger ? '#B5483C' : T.ink,
          letterSpacing: '-0.01em',
        }}>{label}</span>
        {sub && (
          <span style={{
            display: 'block', marginTop: 2,
            fontSize: 12, color: T.ink3,
            letterSpacing: '-0.01em',
          }}>{sub}</span>
        )}
      </span>
    </button>
  );
}

function MoveCardGlyph() {
  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={T.ink} strokeWidth="1.4"
            strokeLinejoin="round" fill="none"/>
      <path d="M8 7v4M6 9l2 2 2-2" stroke={T.ink} strokeWidth="1.4"
            strokeLinecap="round" strokeLinejoin="round"/>
    </svg>
  );
}
function ShareCardGlyph() {
  return (
    <svg width="15" height="15" viewBox="0 0 16 16" fill="none">
      <path d="M8 2v8" stroke={T.ink} strokeWidth="1.4" strokeLinecap="round"/>
      <path d="M5 5l3-3 3 3" stroke={T.ink} strokeWidth="1.4"
            strokeLinecap="round" strokeLinejoin="round" fill="none"/>
      <path d="M3 9v4h10V9" stroke={T.ink} strokeWidth="1.4"
            strokeLinecap="round" strokeLinejoin="round" fill="none"/>
    </svg>
  );
}
function TrashCardGlyph({ color, 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 || T.ink} strokeWidth="1.4"
            strokeLinecap="round" strokeLinejoin="round" fill="none"/>
    </svg>
  );
}

// ─────────────────────────────────────────────────────────────
// CardShareSheet — in-app friends + external channels
// ─────────────────────────────────────────────────────────────
function CardShareSheet({ card, accent, onClose, onSent }) {
  const friends = window.SAMPLE_FRIENDS || [];
  const [picked, setPicked] = useState(new Set());
  const [copied, setCopied] = useState(false);

  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({
      kind: 'friends',
      target,
      title: `${target}님에게 씨앗을 보냈어요`,
      message: card.foreign,
    });
  };

  const sendExternal = (channel) => {
    if (channel === 'copy') {
      onSent({ kind: 'external', target: channel,
               title: '공유 링크가 복사됐어요', message: '원하는 곳에 붙여넣어 보세요' });
    } else {
      onSent({ kind: 'external', target: channel,
               title: `${channel}으로 씨앗을 공유했어요`, message: card.foreign });
    }
  };

  const langFont = card.lang === 'ja' ? '"Noto Sans JP", Pretendard' : 'Noto Sans, Pretendard';

  return (
    <SheetShell onClose={onClose}>
      <div style={{ padding: '0 24px 4px' }}>
        <div style={{
          fontSize: 19, fontWeight: 700, color: T.ink,
          letterSpacing: '-0.02em',
        }}>씨앗 공유</div>
        <div style={{
          marginTop: 6,
          fontSize: 12.5, color: T.ink2,
          letterSpacing: '-0.01em', lineHeight: 1.5,
        }}>외국어·발음·한국어가 함께 전달돼요</div>
      </div>

      {/* Card preview */}
      <div style={{
        margin: '14px 22px 0',
        padding: '14px 16px',
        background: T.surface2,
        border: `0.5px solid ${T.hairline}`,
        borderRadius: 14,
      }}>
        <div style={{
          display: 'inline-flex', alignItems: 'center', gap: 5,
          padding: '3px 9px 3px 7px',
          background: 'rgba(60,42,20,0.06)',
          borderRadius: 999,
          marginBottom: 10,
        }}>
          <span style={{ fontSize: 12, lineHeight: 1 }}>{
            ({ja:'🇯🇵',es:'🇪🇸',en:'🇺🇸',fr:'🇫🇷',zh:'🇨🇳',th:'🇹🇭',de:'🇩🇪',it:'🇮🇹',vi:'🇻🇳',pt:'🇵🇹',id:'🇮🇩'})[card.lang] || '🌐'
          }</span>
          <span style={{
            fontSize: 10.5, fontWeight: 600, color: T.ink2,
            letterSpacing: '0.04em', textTransform: 'uppercase',
          }}>{card.langLabel}</span>
        </div>
        <div style={{
          fontFamily: langFont,
          fontSize: 17, fontWeight: 500, color: T.ink,
          letterSpacing: '-0.01em', lineHeight: 1.35,
          display: '-webkit-box', WebkitLineClamp: 2, WebkitBoxOrient: 'vertical',
          overflow: 'hidden',
        }}>{card.foreign}</div>
        <div style={{
          marginTop: 4,
          fontSize: 12.5, color: T.ink2,
          letterSpacing: '-0.01em', lineHeight: 1.4,
          display: '-webkit-box', WebkitLineClamp: 1, WebkitBoxOrient: 'vertical',
          overflow: 'hidden',
        }}>{card.pronunciation}</div>
        <div style={{
          marginTop: 6, paddingTop: 8,
          borderTop: `0.5px dashed ${T.hairline}`,
          fontSize: 12.5, color: T.ink3,
          letterSpacing: '-0.01em', lineHeight: 1.4,
          display: '-webkit-box', WebkitLineClamp: 1, WebkitBoxOrient: 'vertical',
          overflow: 'hidden',
        }}>{card.korean}</div>
      </div>

      {/* In-app friends */}
      <div style={{ padding: '20px 22px 0' }}>
        <div style={{
          padding: '0 4px 10px',
          fontSize: 11, fontWeight: 600, color: T.ink3,
          letterSpacing: '0.08em', textTransform: 'uppercase',
        }}>앱 내 친구</div>
        <div style={{
          display: 'flex', gap: 10, overflowX: 'auto',
          paddingBottom: 6,
          scrollbarWidth: 'none',
        }}>
          {friends.length === 0 ? (
            <div style={{
              width: '100%', padding: '14px 14px',
              border: `1px dashed ${T.hairlineStrong}`,
              borderRadius: 12,
              fontSize: 12.5, color: T.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 ' + T.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 : T.ink,
                  letterSpacing: '-0.01em',
                  whiteSpace: 'nowrap',
                }}>{f.name}</span>
              </button>
            );
          })}
        </div>
      </div>

      {/* External channels */}
      <div style={{ padding: '18px 22px 0' }}>
        <div style={{
          padding: '0 4px 10px',
          fontSize: 11, fontWeight: 600, color: T.ink3,
          letterSpacing: '0.08em', textTransform: 'uppercase',
        }}>다른 앱으로 보내기</div>
        <div style={{
          display: 'grid', gridTemplateColumns: 'repeat(4, 1fr)', gap: 8,
        }}>
          <ExternalButton label="카톡"  bg="#FEE500" color="#191919"
                          onClick={() => sendExternal('카카오톡')}
                          icon={<KakaoMiniGlyph />} />
          <ExternalButton label="메시지" bg="#34C759" color="#FFF"
                          onClick={() => sendExternal('메시지')}
                          icon={<MsgMiniGlyph />} />
          <ExternalButton label="메일"   bg="#0A84FF" color="#FFF"
                          onClick={() => sendExternal('메일')}
                          icon={<MailMiniGlyph />} />
          <ExternalButton label={copied ? '복사됨' : '링크'}
                          bg="rgba(60,42,20,0.06)" color={T.ink}
                          onClick={() => {
                            setCopied(true);
                            setTimeout(() => sendExternal('copy'), 200);
                          }}
                          icon={copied ? <CheckMiniGlyph color={T.ink} /> : <LinkMiniGlyph color={T.ink} />} />
        </div>
      </div>

      {/* Footer action — appears when friends selected */}
      <div style={{ padding: '20px 22px 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" stroke="#FFF"
                    strokeWidth="0.5" strokeLinejoin="round"/>
            </svg>
            {picked.size === 1
              ? `${friends.find(f => f.id === [...picked][0])?.name}님에게 보내기`
              : `${picked.size}명에게 보내기`}
          </button>
        ) : (
          <button onClick={onClose} style={{
            width: '100%', height: 50, borderRadius: 14,
            background: 'transparent', color: T.ink,
            border: `1px solid ${T.hairlineStrong}`,
            fontFamily: 'inherit', fontSize: 15, fontWeight: 500,
            cursor: 'pointer', WebkitTapHighlightColor: 'transparent',
          }}>닫기</button>
        )}
      </div>
    </SheetShell>
  );
}

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

function KakaoMiniGlyph() {
  return (
    <svg width="22" height="22" viewBox="0 0 22 22" fill="none">
      <path d="M11 4C7 4 4 6.5 4 9.5c0 1.9 1.3 3.6 3.2 4.6l-.6 2.3c-.1.2.2.4.4.3l2.9-1.9c.4 0 .7.1 1.1.1 4 0 7-2.5 7-5.4S15 4 11 4z"
            fill="#191919"/>
    </svg>
  );
}
function MsgMiniGlyph() {
  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 MailMiniGlyph() {
  return (
    <svg width="22" height="22" viewBox="0 0 22 22" fill="none">
      <rect x="3" y="6" width="16" height="11" rx="2" fill="#FFF"/>
      <path d="M3.5 7l7.5 5 7.5-5" stroke="#0A84FF" strokeWidth="1.5"
            strokeLinecap="round" strokeLinejoin="round" fill="none"/>
    </svg>
  );
}
function LinkMiniGlyph({ color }) {
  return (
    <svg width="22" height="22" viewBox="0 0 22 22" fill="none">
      <path d="M9 13l-1.5 1.5a3 3 0 0 1-4.2-4.2l3-3a3 3 0 0 1 4.2 0M13 9l1.5-1.5a3 3 0 0 1 4.2 4.2l-3 3a3 3 0 0 1-4.2 0M8.5 13.5l5-5"
            stroke={color} strokeWidth="1.6" strokeLinecap="round" strokeLinejoin="round" fill="none"/>
    </svg>
  );
}
function CheckMiniGlyph({ color }) {
  return (
    <svg width="20" height="20" viewBox="0 0 22 22" fill="none">
      <path d="M5 11.5l4 4L17 7" stroke={color} strokeWidth="2.2"
            strokeLinecap="round" strokeLinejoin="round"/>
    </svg>
  );
}

function FooterChip({ children }) {
  return (
    <div style={{
      height: 32, padding: '0 12px', borderRadius: 999,
      border: `0.5px solid ${T.hairlineStrong}`,
      display: 'inline-flex', alignItems: 'center', gap: 6,
      fontSize: 13, color: T.ink, letterSpacing: '-0.01em',
      cursor: 'pointer', whiteSpace: 'nowrap',
    }}>{children}</div>
  );
}

function MetaRow({ icon, children }) {
  return (
    <div style={{
      display: 'flex', alignItems: 'flex-start', gap: 9,
    }}>
      <span style={{
        width: 18, height: 22,
        display: 'inline-flex', alignItems: 'center', justifyContent: 'center',
        flexShrink: 0, color: T.ink3,
      }}>{icon}</span>
      <div style={{
        flex: 1, minWidth: 0,
        display: 'flex', flexWrap: 'wrap', gap: 5,
        paddingTop: 1,
      }}>{children}</div>
    </div>
  );
}

function SheetMetaChip({ children, accent, onClick }) {
  const interactive = !!onClick;
  return (
    <button onClick={onClick} disabled={!interactive} style={{
      height: 24, padding: '0 9px', borderRadius: 999,
      background: accent ? `${accent}12` : 'rgba(60,42,20,0.05)',
      border: accent ? `0.5px solid ${accent}33` : `0.5px solid ${T.hairline}`,
      color: T.ink,
      fontFamily: 'inherit', fontSize: 12, fontWeight: 500,
      letterSpacing: '-0.01em', whiteSpace: 'nowrap',
      cursor: interactive ? 'pointer' : 'default',
      maxWidth: 220, overflow: 'hidden', textOverflow: 'ellipsis',
      WebkitTapHighlightColor: 'transparent',
      display: 'inline-flex', alignItems: 'center',
    }}>{children}</button>
  );
}

function CategoryChip({ children, accent, onRemove }) {
  return (
    <div style={{
      height: 32, padding: onRemove ? '0 6px 0 12px' : '0 12px',
      borderRadius: 999,
      background: `${accent}12`,
      border: `0.5px solid ${accent}33`,
      display: 'inline-flex', alignItems: 'center', gap: 6,
      fontSize: 13, color: T.ink, letterSpacing: '-0.01em',
      whiteSpace: 'nowrap',
    }}>
      <span>{children}</span>
      {onRemove && (
        <button onClick={(e) => { e.stopPropagation(); onRemove(); }}
                aria-label="이 분류 제거"
                style={{
          width: 20, height: 20, borderRadius: 999, padding: 0,
          background: 'rgba(60,42,20,0.08)',
          border: 'none', cursor: 'pointer',
          display: 'inline-flex', alignItems: 'center', justifyContent: 'center',
          color: T.ink2, fontFamily: 'inherit',
          WebkitTapHighlightColor: 'transparent',
        }}>
          <svg width="8" height="8" viewBox="0 0 8 8" fill="none">
            <path d="M1.5 1.5l5 5M6.5 1.5l-5 5" stroke="currentColor"
                  strokeWidth="1.4" strokeLinecap="round"/>
          </svg>
        </button>
      )}
    </div>
  );
}

function Caret() {
  return (
    <svg width="9" height="6" viewBox="0 0 9 6" style={{ marginLeft: 2 }}>
      <path d="M1 1.5l3.5 3 3.5-3" stroke={T.ink2} strokeWidth="1.4" fill="none" strokeLinecap="round" strokeLinejoin="round"/>
    </svg>
  );
}

// ─────────────────────────────────────────────────────────────
// INPUT (typing)
// ─────────────────────────────────────────────────────────────
const TONE_OPTIONS = [
  { value: '자동',    desc: 'AI가 맥락에 맞게' },
  { value: '정중하게', desc: '존중하는 느낌으로' },
  { value: '친근하게', desc: '편한 사이처럼' },
];

const LANG_OPTIONS = [
  { value: '일본어',    flag: '🇯🇵' },
  { value: '영어',      flag: '🇺🇸' },
  { value: '스페인어',  flag: '🇪🇸' },
  { value: '중국어',    flag: '🇨🇳' },
  { value: '프랑스어',  flag: '🇫🇷' },
  { value: '베트남어',  flag: '🇻🇳' },
  { value: '태국어',    flag: '🇹🇭' },
];

const BASE_CATS = [
  '식당/카페', '교통/길찾기', '숙소', '쇼핑',
  '관광/구경', '인사/사교', '응급/의료', '일상/기타',
];

function InputScreen({ accent, onCancel, onSubmit, onPickScreenshot, customCats, addCustomCat, micPermission = 'granted' }) {
  const [text, setText] = useState('');
  const [langSel, setLangSel] = useState('일본어');
  const [tone, setTone] = useState('자동');
  const [cat, setCat] = useState('자동');
  const [sheetOpen, setSheetOpen] = useState(null); // 'lang' | 'tone' | 'cat' | 'source' | 'voice' | null
  const textareaRef = React.useRef(null);

  return (
    <div>
      {/* Bar */}
      <div style={{
        padding: '54px 22px 6px',
        display: 'flex', alignItems: 'center', justifyContent: 'space-between',
      }}>
        <button onClick={onCancel} style={{
          background: 'none', border: 'none', padding: 0,
          fontSize: 16, color: T.ink2, cursor: 'pointer',
          fontFamily: 'inherit',
        }}>취소</button>
        <div style={{
          fontSize: 16, fontWeight: 600, color: T.ink, letterSpacing: '-0.01em',
          whiteSpace: 'nowrap',
        }}>새 표현 담기</div>
        <button onClick={() => onSubmit(text, { tone, cat: cat === '자동' ? null : cat, langSel })} disabled={!text.trim()} style={{
          background: 'none', border: 'none', padding: 0,
          fontSize: 16, fontWeight: 600,
          color: text.trim() ? accent : T.ink4,
          cursor: text.trim() ? 'pointer' : 'default',
          fontFamily: 'inherit',
        }}>완료</button>
      </div>

      {/* Lang / tone / category selectors */}
      <div style={{
        padding: '18px 22px 0',
        display: 'flex', gap: 6, flexWrap: 'nowrap',
      }}>
        <Selector label="언어" value={langSel}
                  arrow prefix={(LANG_OPTIONS.find(o => o.value === langSel) || {}).flag}
                  onClick={() => setSheetOpen('lang')} />
        <Selector label="톤"   value={tone}    onClick={() => setSheetOpen('tone')} />
        <Selector label="카테고리" value={cat === '자동' ? '자동' : cat}
                  isAuto={cat === '자동'} accent={accent}
                  onClick={() => setSheetOpen('cat')} />
      </div>

      {/* Textarea */}
      <div style={{ padding: '20px 22px 0' }}>
        <textarea
          ref={textareaRef}
          value={text}
          onChange={(e) => setText(e.target.value)}
          placeholder="한국어로 적어주세요…"
          autoFocus
          style={{
            width: '100%', minHeight: 180,
            background: 'transparent',
            border: 'none', outline: 'none',
            resize: 'none', padding: 0,
            fontFamily: 'inherit',
            fontSize: 22, fontWeight: 500,
            letterSpacing: '-0.02em',
            lineHeight: 1.4,
            color: T.ink,
          }}
        />
      </div>

      <div style={{ padding: '20px 22px 0' }}>
        <div style={{
          fontSize: 13, color: T.ink3, letterSpacing: '-0.01em', lineHeight: 1.55,
        }}>
          <span style={{ color: T.ink2, fontWeight: 500 }}>
            한국어로 적으면 {langSel}로 변환돼요
          </span>
          <br/>여러 문장도 괜찮아요. 자동으로 나누어 씨앗으로 만들어드려요.
        </div>
      </div>

      {/* Quick input mode toggles */}
      <div style={{
        position: 'absolute', bottom: 30, left: 22, right: 22,
        display: 'flex', gap: 10,
      }}>
        <ModeButton icon={<MicIcon />} label="음성"
          onClick={() => setSheetOpen('voice')} />
        <ModeButton icon={<PhotoIcon />} label="스크린샷"
          onClick={() => setSheetOpen('source')} />
      </div>

      {/* Sheets */}
      {sheetOpen === 'voice' && (
        <VoiceSheet
          accent={accent}
          permission={micPermission}
          langSel={langSel}
          onClose={() => setSheetOpen(null)}
          onConfirm={(transcript) => {
            setText(prev => prev ? `${prev} ${transcript}` : transcript);
            setSheetOpen(null);
            // refocus textarea so the user can keep editing
            setTimeout(() => textareaRef.current?.focus(), 50);
          }}
        />
      )}
      {sheetOpen === 'lang' && (
        <PickerSheet
          title="어느 나라 말로 담을까요?"
          options={LANG_OPTIONS.map(o => ({ value: o.value, label: o.value, prefix: o.flag }))}
          current={langSel}
          accent={accent}
          onSelect={(v) => { setLangSel(v); setSheetOpen(null); }}
          onClose={() => setSheetOpen(null)}
        />
      )}
      {sheetOpen === 'tone' && (
        <PickerSheet
          title="어떤 톤으로 만들까요?"
          options={TONE_OPTIONS.map(o => ({ value: o.value, label: o.value, desc: o.desc }))}
          current={tone}
          accent={accent}
          onSelect={(v) => { setTone(v); setSheetOpen(null); }}
          onClose={() => setSheetOpen(null)}
        />
      )}
      {sheetOpen === 'cat' && (
        <CategorySheet
          current={cat}
          baseCats={BASE_CATS}
          customCats={customCats}
          accent={accent}
          onSelect={(v) => { setCat(v); setSheetOpen(null); }}
          onAddCustom={(v) => { addCustomCat(v); setCat(v); setSheetOpen(null); }}
          onClose={() => setSheetOpen(null)}
        />
      )}
      {sheetOpen === 'source' && (
        <ScreenshotSourceSheet
          accent={accent}
          onClose={() => setSheetOpen(null)}
          onPick={(item) => {
            setSheetOpen(null);
            // Only the 'real' demo screenshot has detection wired up
            if (item.kind === 'real') onPickScreenshot && onPickScreenshot();
            else onPickScreenshot && onPickScreenshot();
          }}
        />
      )}
    </div>
  );
}

function Selector({ label, value, onClick, isAuto, accent, arrow, prefix }) {
  return (
    <button onClick={() => { console.log('Selector clicked:', label); onClick && onClick(); }} style={{
      height: 34, padding: '0 10px',
      borderRadius: 10,
      border: `0.5px solid ${isAuto ? accent : T.hairlineStrong}`,
      background: isAuto ? `${accent}12` : T.surface,
      display: 'inline-flex', alignItems: 'center', gap: 5,
      fontFamily: 'inherit', fontSize: 12.5, color: T.ink,
      cursor: 'pointer', letterSpacing: '-0.01em',
      whiteSpace: 'nowrap', flexShrink: 0,
    }}>
      <span style={{ color: T.ink3, fontSize: 11.5 }}>{label}</span>
      {arrow && (
        <svg width="10" height="8" viewBox="0 0 11 9" fill="none" aria-hidden="true"
             style={{ marginLeft: -1, marginRight: -1 }}>
          <path d="M1 4.5h8.2M6 1.2l3.4 3.3L6 7.8"
                stroke={T.ink3} strokeWidth="1.3"
                strokeLinecap="round" strokeLinejoin="round" fill="none"/>
        </svg>
      )}
      {prefix && <span style={{ fontSize: 13, lineHeight: 1 }}>{prefix}</span>}
      <span style={{ fontWeight: 500, color: isAuto ? accent : T.ink }}>{value}</span>
      <Caret />
    </button>
  );
}

function ModeButton({ icon, label, onClick }) {
  return (
    <button onClick={onClick} style={{
      flex: 1, height: 56, borderRadius: 16,
      background: T.surface,
      border: `0.5px solid ${T.hairline}`,
      display: 'inline-flex', alignItems: 'center', justifyContent: 'center', gap: 8,
      fontFamily: 'inherit', fontSize: 14, color: T.ink, fontWeight: 500,
      letterSpacing: '-0.01em',
      cursor: 'pointer',
      WebkitTapHighlightColor: 'transparent',
      transition: 'background 0.16s ease',
    }}>
      {icon}{label}
    </button>
  );
}

function MicIcon() {
  return (
    <svg width="18" height="18" viewBox="0 0 18 18" fill="none">
      <rect x="6" y="2" width="6" height="10" rx="3" stroke={T.ink} strokeWidth="1.5"/>
      <path d="M3.5 8.5c0 3 2.5 5 5.5 5s5.5-2 5.5-5M9 13.5V16" stroke={T.ink} strokeWidth="1.5" strokeLinecap="round"/>
    </svg>
  );
}

function PhotoIcon() {
  return (
    <svg width="18" height="18" viewBox="0 0 18 18" fill="none">
      <rect x="2" y="4" width="14" height="11" rx="2.5" stroke={T.ink} strokeWidth="1.5"/>
      <circle cx="9" cy="9.5" r="2.8" stroke={T.ink} strokeWidth="1.5"/>
      <path d="M6.5 4l1-1.5h3l1 1.5" stroke={T.ink} strokeWidth="1.5"/>
    </svg>
  );
}

// ─────────────────────────────────────────────────────────────
// BOTTOM SHEETS
// ─────────────────────────────────────────────────────────────
function SheetShell({ children, onClose, title }) {
  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.32)',
        animation: 'cp-fade-bg 0.25s ease',
      }} />
      <div style={{
        position: 'relative',
        background: T.surface,
        borderTopLeftRadius: 26, borderTopRightRadius: 26,
        paddingBottom: 30,
        maxHeight: '78%',
        display: 'flex', flexDirection: 'column',
        boxShadow: '0 -8px 32px rgba(31,24,18,0.12)',
        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',
        }} />
        {title && (
          <div style={{
            padding: '0 24px 16px',
            fontSize: 17, fontWeight: 600, color: T.ink,
            letterSpacing: '-0.02em',
            textAlign: 'center',
          }}>{title}</div>
        )}
        <div style={{ overflowY: 'auto', padding: '0 12px' }}>
          {children}
        </div>
      </div>
    </div>
  );
}

function SheetRow({ children, selected, accent, onClick, prefix, desc, intent }) {
  const isAdd = intent === 'add';
  return (
    <button onClick={onClick} style={{
      width: '100%',
      display: 'flex', alignItems: 'center', gap: 12,
      padding: '14px 16px',
      borderRadius: 12,
      border: 'none', background: 'transparent',
      cursor: 'pointer', textAlign: 'left',
      fontFamily: 'inherit', color: isAdd ? accent : T.ink,
      letterSpacing: '-0.01em',
      WebkitTapHighlightColor: 'transparent',
    }}>
      {prefix && (
        <span style={{
          width: 28, height: 28, borderRadius: 14,
          display: 'inline-flex', alignItems: 'center', justifyContent: 'center',
          fontSize: 18, flexShrink: 0,
          background: isAdd ? `${accent}18` : 'transparent',
          color: isAdd ? accent : T.ink2,
        }}>{prefix}</span>
      )}
      <div style={{ flex: 1, minWidth: 0 }}>
        <div style={{
          fontSize: 16, fontWeight: 500,
          color: isAdd ? accent : T.ink,
        }}>{children}</div>
        {desc && (
          <div style={{
            fontSize: 12, color: T.ink3, marginTop: 2,
          }}>{desc}</div>
        )}
      </div>
      {selected && (
        <svg width="18" height="18" viewBox="0 0 18 18" fill="none" style={{ flexShrink: 0 }}>
          <path d="M4 9.5l3.5 3.5L14 5.5" stroke={accent} strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/>
        </svg>
      )}
    </button>
  );
}

function SheetDivider({ label }) {
  return (
    <div style={{
      display: 'flex', alignItems: 'center', gap: 10,
      padding: '12px 16px 6px',
      fontSize: 11, fontWeight: 600, letterSpacing: '0.08em',
      color: T.ink3, textTransform: 'uppercase',
    }}>
      {label && <span style={{ whiteSpace: 'nowrap' }}>{label}</span>}
      <div style={{ flex: 1, height: 0.5, background: T.hairline }} />
    </div>
  );
}

function PickerSheet({ title, options, current, accent, onSelect, onClose }) {
  return (
    <SheetShell title={title} onClose={onClose}>
      {options.map(o => (
        <SheetRow
          key={o.value}
          prefix={o.prefix}
          desc={o.desc}
          selected={current === o.value}
          accent={accent}
          onClick={() => onSelect(o.value)}
        >{o.label}</SheetRow>
      ))}
    </SheetShell>
  );
}

// ─────────────────────────────────────────────────────────────
// VOICE SHEET — option 2: small modal over the typing composer
// States: idle → listening → processing → onConfirm(text)
// If permission==='denied', renders the permission-denied screen instead.
// Real app would use SFSpeechRecognizer; here we fake a transcript.
// ─────────────────────────────────────────────────────────────
const VOICE_SAMPLES_BY_LANG = {
  '일본어':    '여기 정말 맛있어요. 추천 메뉴 있나요?',
  '영어':      '체크아웃 시간이 언제예요?',
  '스페인어':  '계산서 좀 주세요. 정말 맛있었어요.',
  '중국어':    '이거 얼마예요?',
  '프랑스어':  '커피 한 잔 주세요.',
  '베트남어':  '쌀국수 작은 거 하나 주세요.',
  '태국어':    '안 맵게 해주세요.',
};

function VoiceSheet({ accent, permission = 'granted', langSel, onClose, onConfirm }) {
  const denied = permission === 'denied';
  const [state, setState] = useState('idle'); // 'idle' | 'listening' | 'processing'
  const [transcript, setTranscript] = useState('');
  const sampleText = VOICE_SAMPLES_BY_LANG[langSel] || VOICE_SAMPLES_BY_LANG['일본어'];

  // Fake speech recognition: type out sample over ~2.6s while "listening".
  useEffect(() => {
    if (state !== 'listening') return;
    setTranscript('');
    const total = sampleText.length;
    const step = Math.max(40, Math.min(90, 2400 / Math.max(1, total)));
    let i = 0;
    const tick = setInterval(() => {
      i++;
      setTranscript(sampleText.slice(0, i));
      if (i >= total) {
        clearInterval(tick);
        // Brief pause to show full transcript, then enter processing.
        setTimeout(() => setState('processing'), 450);
      }
    }, step);
    return () => clearInterval(tick);
  }, [state, sampleText]);

  // Processing → auto-confirm
  useEffect(() => {
    if (state !== 'processing') return;
    const t = setTimeout(() => onConfirm(transcript), 700);
    return () => clearTimeout(t);
  }, [state, transcript, onConfirm]);

  const start = () => { if (!denied) setState('listening'); };
  const stopEarly = () => {
    if (transcript.trim().length === 0) {
      // no audio captured — back to idle
      setState('idle');
      return;
    }
    setState('processing');
  };

  return (
    <SheetShell onClose={onClose}>
      {denied ? (
        <VoiceDeniedBody accent={accent} onClose={onClose} />
      ) : (
        <VoiceCaptureBody
          accent={accent}
          state={state}
          transcript={transcript}
          onStart={start}
          onStopEarly={stopEarly}
          onClose={onClose}
        />
      )}
    </SheetShell>
  );
}

function VoiceCaptureBody({ accent, state, transcript, onStart, onStopEarly, onClose }) {
  const isListening = state === 'listening';
  const isProcessing = state === 'processing';
  const isIdle = state === 'idle';

  // Status copy by state
  const headline = isIdle ? '음성으로 담기'
                 : isListening ? '듣고 있어요'
                 : '담는 중…';
  const subline = isIdle ? '마이크 버튼을 누르고 한국어로 말해주세요'
                : isListening ? '말이 끝나면 다시 눌러주세요'
                : '잠시만요';

  return (
    <div style={{ padding: '4px 24px 8px' }}>
      {/* Headline */}
      <div style={{
        textAlign: 'center',
        fontSize: 20, fontWeight: 600, color: T.ink,
        letterSpacing: '-0.02em',
        marginTop: 4,
      }}>{headline}</div>
      <div style={{
        textAlign: 'center', marginTop: 6,
        fontSize: 13, color: T.ink3, letterSpacing: '-0.01em',
      }}>{subline}</div>

      {/* Transcript area — reserved space so the mic doesn't jump */}
      <div style={{
        minHeight: 84, marginTop: 22,
        display: 'flex', alignItems: 'center', justifyContent: 'center',
        padding: '0 6px',
      }}>
        {transcript ? (
          <div style={{
            textAlign: 'center',
            fontSize: 22, fontWeight: 500, color: T.ink,
            letterSpacing: '-0.02em', lineHeight: 1.35,
            textWrap: 'balance',
          }}>
            {transcript}
            {isListening && <span style={{
              display: 'inline-block', width: 2, height: 22,
              background: accent, marginLeft: 4, verticalAlign: 'middle',
              animation: 'cp-caret-blink 0.9s steps(2) infinite',
            }} />}
          </div>
        ) : (
          <div style={{
            fontSize: 14, color: T.ink4, letterSpacing: '-0.01em',
            fontStyle: isIdle ? 'normal' : 'italic',
          }}>{isIdle ? '\u00A0' : '소리를 듣고 있어요…'}</div>
        )}
      </div>

      {/* Big mic button + waveform */}
      <div style={{
        marginTop: 6,
        display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 18,
      }}>
        <button
          onClick={isIdle ? onStart : isListening ? onStopEarly : undefined}
          disabled={isProcessing}
          aria-label={isIdle ? '녹음 시작' : isListening ? '녹음 끝내기' : '처리 중'}
          style={{
            position: 'relative',
            width: 92, height: 92, borderRadius: 999,
            background: isListening ? accent : isProcessing ? `${accent}30` : T.surface,
            border: isListening ? 'none' : `1.5px solid ${isProcessing ? `${accent}40` : `${accent}50`}`,
            color: isListening ? '#FFF' : accent,
            boxShadow: isListening
              ? `0 0 0 8px ${accent}1A, 0 8px 24px ${accent}40`
              : `0 2px 10px rgba(31,24,18,0.06)`,
            cursor: isProcessing ? 'default' : 'pointer',
            display: 'inline-flex', alignItems: 'center', justifyContent: 'center',
            transition: 'background 0.2s ease, box-shadow 0.25s ease, transform 0.15s ease',
            WebkitTapHighlightColor: 'transparent',
            animation: isListening ? 'cp-mic-pulse 1.6s ease-in-out infinite' : 'none',
          }}
        >
          {isListening ? (
            // Stop square — invites "tap to stop"
            <svg width="22" height="22" viewBox="0 0 22 22" fill="none">
              <rect x="5" y="5" width="12" height="12" rx="2.5" fill="#FFF"/>
            </svg>
          ) : isProcessing ? (
            <svg width="28" height="28" viewBox="0 0 28 28" fill="none"
                 style={{ animation: 'cp-spin 0.9s linear infinite' }}>
              <circle cx="14" cy="14" r="10" stroke={`${accent}33`} strokeWidth="2.4" fill="none"/>
              <path d="M14 4a10 10 0 0 1 10 10" stroke={accent} strokeWidth="2.4"
                    strokeLinecap="round" fill="none"/>
            </svg>
          ) : (
            <svg width="34" height="34" viewBox="0 0 24 24" fill="none">
              <rect x="8" y="2.5" width="8" height="13" rx="4"
                    stroke="currentColor" strokeWidth="1.7"/>
              <path d="M4.5 11.5c0 4 3.4 6.8 7.5 6.8s7.5-2.8 7.5-6.8M12 18.3V22"
                    stroke="currentColor" strokeWidth="1.7" strokeLinecap="round"/>
            </svg>
          )}
        </button>

        {/* Waveform — 5 bars, animated only while listening */}
        <div style={{
          height: 22, display: 'flex', alignItems: 'center', gap: 4,
          opacity: isListening ? 1 : isProcessing ? 0.35 : 0.25,
          transition: 'opacity 0.25s ease',
        }} aria-hidden="true">
          {[0, 1, 2, 3, 4].map(i => (
            <div key={i} style={{
              width: 3, height: 6, borderRadius: 2,
              background: isListening ? accent : T.ink4,
              transformOrigin: 'center',
              animation: isListening
                ? `cp-wave ${0.7 + i * 0.08}s ease-in-out ${i * 0.07}s infinite`
                : 'none',
            }} />
          ))}
        </div>
      </div>

      {/* Footer actions */}
      <div style={{
        marginTop: 22, paddingTop: 14,
        borderTop: `0.5px solid ${T.hairline}`,
        display: 'flex', justifyContent: 'center',
      }}>
        <button onClick={onClose} disabled={isProcessing} style={{
          background: 'none', border: 'none', padding: '8px 14px',
          fontFamily: 'inherit', fontSize: 14, color: T.ink2,
          cursor: isProcessing ? 'default' : 'pointer',
          opacity: isProcessing ? 0.5 : 1,
        }}>{isListening ? '취소' : '닫기'}</button>
      </div>
    </div>
  );
}

function VoiceDeniedBody({ accent, onClose }) {
  return (
    <div style={{ padding: '4px 24px 8px' }}>
      {/* Slashed mic glyph */}
      <div style={{
        margin: '6px auto 18px',
        width: 76, height: 76, borderRadius: 999,
        background: 'rgba(31,24,18,0.05)',
        display: 'flex', alignItems: 'center', justifyContent: 'center',
      }}>
        <svg width="38" height="38" viewBox="0 0 24 24" fill="none">
          <rect x="8" y="2.5" width="8" height="13" rx="4"
                stroke={T.ink3} strokeWidth="1.7"/>
          <path d="M4.5 11.5c0 4 3.4 6.8 7.5 6.8s7.5-2.8 7.5-6.8M12 18.3V22"
                stroke={T.ink3} strokeWidth="1.7" strokeLinecap="round"/>
          <line x1="3.5" y1="3" x2="20.5" y2="21" stroke={T.surface} strokeWidth="3.4" strokeLinecap="round"/>
          <line x1="3.5" y1="3" x2="20.5" y2="21" stroke={T.ink2} strokeWidth="1.6" strokeLinecap="round"/>
        </svg>
      </div>

      <div style={{
        textAlign: 'center',
        fontSize: 19, fontWeight: 600, color: T.ink,
        letterSpacing: '-0.02em',
      }}>마이크 권한이 필요해요</div>
      <div style={{
        textAlign: 'center', marginTop: 8,
        fontSize: 14, color: T.ink2, letterSpacing: '-0.01em',
        lineHeight: 1.55,
        textWrap: 'pretty',
        padding: '0 8px',
      }}>음성으로 표현을 담으려면 마이크 권한이 필요해요.<br/>
        iOS 설정에서 권한을 켜고 다시 시도해주세요.</div>

      {/* "Path" hint — small breadcrumb mimicking iOS settings path */}
      <div style={{
        margin: '18px auto 0', maxWidth: 280,
        padding: '10px 14px',
        background: T.surface2 || 'rgba(31,24,18,0.04)',
        borderRadius: 12,
        display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 6,
        flexWrap: 'wrap',
        fontSize: 12.5, color: T.ink3, letterSpacing: '-0.01em',
      }}>
        <span style={{ color: T.ink2, fontWeight: 500 }}>설정</span>
        <SmallCaret />
        <span style={{ color: T.ink2, fontWeight: 500 }}>CheekPouch</span>
        <SmallCaret />
        <span style={{ color: accent, fontWeight: 600 }}>마이크</span>
      </div>

      {/* Actions */}
      <div style={{ marginTop: 22, display: 'flex', flexDirection: 'column', gap: 10 }}>
        <button
          onClick={() => {
            // Prototype-only: pretend to deep-link to Settings; we just close.
            onClose();
          }}
          style={{
            height: 50, borderRadius: 14,
            background: accent, color: '#FFF',
            border: 'none', cursor: 'pointer',
            fontFamily: 'inherit', fontSize: 15.5, fontWeight: 600,
            letterSpacing: '-0.01em',
            boxShadow: `0 6px 16px ${accent}33`,
          }}>설정 열기</button>
        <button
          onClick={onClose}
          style={{
            height: 46, borderRadius: 14,
            background: 'transparent', color: T.ink2,
            border: 'none', cursor: 'pointer',
            fontFamily: 'inherit', fontSize: 14.5, fontWeight: 500,
            letterSpacing: '-0.01em',
          }}>타이핑으로 입력할게요</button>
      </div>
    </div>
  );
}

function SmallCaret() {
  return (
    <svg width="6" height="9" viewBox="0 0 6 9" fill="none" aria-hidden="true">
      <path d="M1 1l3.5 3.5L1 8" stroke="currentColor" strokeOpacity="0.55"
            strokeWidth="1.2" strokeLinecap="round" strokeLinejoin="round" fill="none"/>
    </svg>
  );
}

function CategorySheet({ current, baseCats, customCats, accent, onSelect, onAddCustom, onClose }) {
  const [adding, setAdding] = useState(false);
  const [newCat, setNewCat] = useState('');
  const inputRef = useRef(null);

  useEffect(() => {
    if (adding && inputRef.current) inputRef.current.focus();
  }, [adding]);

  const submitNew = () => {
    const v = newCat.trim();
    if (!v) return;
    onAddCustom(v);
  };

  return (
    <SheetShell title="카테고리" onClose={onClose}>
      <SheetRow
        prefix={<SparkleGlyph color={accent} />}
        desc="AI가 알아서 골라드려요"
        selected={current === '자동'}
        accent={accent}
        onClick={() => onSelect('자동')}
      >자동 분류</SheetRow>

      <SheetDivider label="기본 카테고리" />
      {baseCats.map(c => (
        <SheetRow key={c} selected={current === c} accent={accent}
                  onClick={() => onSelect(c)}>{c}</SheetRow>
      ))}

      {customCats.length > 0 && (
        <>
          <SheetDivider label="내가 만든 카테고리" />
          {customCats.map(c => (
            <SheetRow key={c} selected={current === c} accent={accent}
                      onClick={() => onSelect(c)}>{c}</SheetRow>
          ))}
        </>
      )}

      <SheetDivider />
      {!adding ? (
        <SheetRow
          intent="add"
          accent={accent}
          prefix={<PlusGlyph color={accent} />}
          onClick={() => setAdding(true)}
        >새 카테고리 만들기</SheetRow>
      ) : (
        <div style={{
          margin: '8px 8px 4px',
          padding: 12,
          background: `${accent}10`,
          borderRadius: 14,
          border: `1px solid ${accent}40`,
        }}>
          <div style={{ fontSize: 11, fontWeight: 600, color: accent, letterSpacing: '0.06em', textTransform: 'uppercase', marginBottom: 8 }}>
            새 카테고리
          </div>
          <input
            ref={inputRef}
            value={newCat}
            onChange={(e) => setNewCat(e.target.value)}
            onKeyDown={(e) => { if (e.key === 'Enter') submitNew(); }}
            placeholder="예: 카페 추천, 가족 여행, 비행기 안에서"
            style={{
              width: '100%', height: 38,
              background: T.surface2,
              border: `1px solid ${T.hairlineStrong}`,
              borderRadius: 10,
              padding: '0 12px',
              fontFamily: 'inherit', fontSize: 15,
              color: T.ink, outline: 'none',
              letterSpacing: '-0.01em',
              boxSizing: 'border-box',
            }}
          />
          <div style={{ display: 'flex', gap: 8, marginTop: 10 }}>
            <button onClick={() => { setAdding(false); setNewCat(''); }} style={{
              flex: 1, height: 36, borderRadius: 10,
              background: 'transparent', color: T.ink2,
              border: `1px solid ${T.hairlineStrong}`,
              fontFamily: 'inherit', fontSize: 14, fontWeight: 500,
              cursor: 'pointer',
            }}>취소</button>
            <button onClick={submitNew} disabled={!newCat.trim()} style={{
              flex: 1, height: 36, borderRadius: 10,
              background: newCat.trim() ? accent : `${accent}30`,
              color: '#FFF',
              border: 'none',
              fontFamily: 'inherit', fontSize: 14, fontWeight: 600,
              cursor: newCat.trim() ? 'pointer' : 'default',
            }}>만들기</button>
          </div>
        </div>
      )}
    </SheetShell>
  );
}

function SparkleGlyph({ color }) {
  return (
    <svg width="16" height="16" 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={color}/>
      <path d="M13 11L13.4 12.6L15 13L13.4 13.4L13 15L12.6 13.4L11 13L12.6 12.6L13 11Z" fill={color} fillOpacity="0.5"/>
    </svg>
  );
}

function PlusGlyph({ color }) {
  return (
    <svg width="14" height="14" viewBox="0 0 14 14" fill="none">
      <path d="M7 2v10M2 7h10" stroke={color} strokeWidth="2" strokeLinecap="round"/>
    </svg>
  );
}

// ─────────────────────────────────────────────────────────────
// PROCESSING
// ─────────────────────────────────────────────────────────────
function ProcessingScreen({ accent }) {
  return (
    <div style={{
      height: '100%',
      display: 'flex', flexDirection: 'column',
      alignItems: 'center', justifyContent: 'center',
      padding: '0 32px',
    }}>
      <CheekPouchAnimated accent={accent} size={220} />
      <div style={{
        marginTop: 36,
        fontSize: 22, fontWeight: 600, color: T.ink,
        letterSpacing: '-0.02em',
        whiteSpace: 'nowrap',
      }}>볼주머니에 담는 중…</div>
      <div style={{
        marginTop: 10,
        fontSize: 14, color: T.ink2, letterSpacing: '-0.01em',
        whiteSpace: 'nowrap',
      }}>보통 3초 안에 끝나요</div>

      {/* dots loader */}
      <div style={{ marginTop: 28, display: 'flex', gap: 8 }}>
        {[0, 1, 2].map(i => (
          <div key={i} style={{
            width: 7, height: 7, borderRadius: 7,
            background: accent,
            animation: `cp-bounce 1.2s ${i * 0.15}s infinite ease-in-out`,
          }} />
        ))}
      </div>
    </div>
  );
}

// ─────────────────────────────────────────────────────────────
// MAIN APP
// ─────────────────────────────────────────────────────────────
const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{
  "accent": "#8B6F47",
  "mode": "system",
  "fontSize": "M"
}/*EDITMODE-END*/;

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

  // ─── Dark mode wiring ──────────────────────────────────────
  // t.mode: 'light' | 'dark' | 'system'. Toggle the `cp-dark` class on <html>
  // so theme Proxies (T, HT, CT, …) and CSS pick up the right palette.
  const resolveIsDark = (mode) => {
    if (mode === 'dark') return true;
    if (mode === 'light') return false;
    return typeof window !== 'undefined' && window.matchMedia &&
           window.matchMedia('(prefers-color-scheme: dark)').matches;
  };
  const [isDarkResolved, setIsDarkResolved] = useState(() => resolveIsDark(TWEAK_DEFAULTS.mode));
  useEffect(() => {
    const apply = () => {
      const next = resolveIsDark(t.mode || 'system');
      document.documentElement.classList.toggle('cp-dark', next);
      setIsDarkResolved(next);
    };
    apply();
    if ((t.mode || 'system') === 'system') {
      const mq = window.matchMedia('(prefers-color-scheme: dark)');
      const handler = () => apply();
      mq.addEventListener ? mq.addEventListener('change', handler) : mq.addListener(handler);
      return () => {
        mq.removeEventListener ? mq.removeEventListener('change', handler) : mq.removeListener(handler);
      };
    }
  }, [t.mode]);

  // Accent gets pastelized in dark mode for softer contrast.
  const effectiveAccent = isDarkResolved ? window.__cpPastelize(t.accent) : t.accent;

  const initialScreen = new URLSearchParams(location.search).get('s') || 'login';
  const [screen, setScreen] = useState(initialScreen);
  const [card, setCard] = useState(initialScreen === 'detail' ? CARDS[0] : null);
  const [groupName, setGroupName] = useState(null);
  const [sharedGroupView, setSharedGroupView] = useState(null);
  const [customCats, setCustomCats] = useState([]);
  const [micPermission, setMicPermission] = useState('granted'); // 'granted' | 'denied'

  // Profile state
  const [profile, setProfile] = useState({ name: '윤재', face: 'basic' });
  const [settingsFocus, setSettingsFocus] = useState(null);
  const [showProfileEdit, setShowProfileEdit] = useState(false);
  const [showRating, setShowRating] = useState(false);
  const [showSocial, setShowSocial] = useState(false);

  // Tab bar state
  const [showInvite, setShowInvite] = useState(false);
  const [selectedFriend, setSelectedFriend] = useState(null);
  const [nudgeTarget, setNudgeTarget] = useState(null);
  const [nudgeToast, setNudgeToast] = useState(null);

  // Share request state — pending requests from friends + dynamically accepted
  // received collections (combined with module-level RECEIVED_COLLECTIONS on home).
  const [shareRequests, setShareRequests] = useState(() => window.SAMPLE_SHARE_REQUESTS || []);
  const [acceptedExtras, setAcceptedExtras] = useState([]);
  const combinedReceived = useMemo(() => {
    const base = window.RECEIVED_COLLECTIONS || [];
    return [...acceptedExtras, ...base];
  }, [acceptedExtras]);

  // Collection state
  const [cards, setCards] = useState(CARDS);
  const [extraCollections, setExtraCollections] = useState([]);
  const [pinnedSet, setPinnedSet] = useState(new Set());
  const [collSheet, setCollSheet] = useState(null); // 'new' | 'actions' | 'edit' | 'delete' | 'cards' | 'share'
  const [collEditing, setCollEditing] = useState(false);

  // Daily "볼주머니" — resets at midnight (conceptually). Today's count = quotaUsed.
  // Cap = dailyCap (permanent, grows by friend invites) + todayBoost (today-only, from ads).
  const [quotaUsed, setQuotaUsed] = useState(3);
  const [dailyCap, setDailyCap] = useState(10);
  const DAILY_CAP_MAX = 15;
  const [todayBoost, setTodayBoost] = useState(0);
  const quotaTotal = dailyCap + todayBoost;
  // Alias for legacy consumers expecting `dailyCount`
  const dailyCount = quotaUsed;
  const setDailyCount = setQuotaUsed;
  const [showUpgrade, setShowUpgrade] = useState(false);

  const groups = useMemo(
    () => (window.deriveGroups ? window.deriveGroups(cards, extraCollections) : []),
    [cards, extraCollections]
  );
  const currentGroup = (() => {
    if (!groupName) return null;
    const local = groups.find(g => g.name === groupName);
    if (local) return local;
    if (sharedGroupView && sharedGroupView.name === groupName) return sharedGroupView;
    return null;
  })();

  // Screenshot flow state
  const [ssSelected, setSsSelected] = useState(new Set());
  const [ssItems, setSsItems] = useState([]);
  const [ssLang, setSsLang] = useState('일본어');
  const [ssTone, setSsTone] = useState('자동');
  const [ssCat, setSsCat] = useState('자동');

  // history stack for back nav
  const historyRef = useRef(['home']);

  // Tab routing — which tab is "home" for back nav
  const tabRoots = new Set(['home', 'friends', 'me']);
  const isOnTabScreen = tabRoots.has(screen);
  const activeTab = ['friends', 'me'].includes(screen) ? screen : 'home';

  const switchTab = (tab) => {
    historyRef.current = [tab];
    setScreen(tab);
  };

  const goto = (next, payload) => {
    historyRef.current.push(next);
    if (next === 'detail') setCard(payload);
    if (next === 'collection') {
      const name = typeof payload === 'string' ? payload : payload?.name;
      setGroupName(name);
      if (typeof payload === 'object' && payload?.isShared) {
        setSharedGroupView(payload);
      } else {
        setSharedGroupView(null);
      }
    }
    setScreen(next);
  };

  const back = () => {
    historyRef.current.pop();
    const prev = historyRef.current[historyRef.current.length - 1] || 'home';
    setScreen(prev);
  };

  // ─── Hash deep-link routing (for screenshot capture & cross-doc links) ──
  // Maps `location.hash` like `#home`, `#card/c1`, `#collection/도쿄식당`,
  // `#input/screenshot/detect` to the appropriate setScreen + payload.
  // Additive — `?s=...` query and tweak buttons still work.
  useEffect(() => {
    const apply = () => {
      const raw = (location.hash || '').replace(/^#/, '');
      if (!raw) return;
      const [path, query] = raw.split('?');
      const parts = path.split('/').filter(Boolean);
      const head = parts[0];
      const arg  = parts[1] ? decodeURIComponent(parts[1]) : null;
      const sub  = parts[2] || null;
      const params = new URLSearchParams(query || '');

      switch (head) {
        case 'login':       historyRef.current = ['login']; setScreen('login'); break;
        case 'home':        historyRef.current = ['home'];  setScreen('home');  break;
        case 'search':      historyRef.current = ['home', 'search']; setScreen('search'); break;
        case 'allcards':    historyRef.current = ['home', 'allCards']; setScreen('allCards'); break;
        case 'friends':     historyRef.current = ['friends']; setScreen('friends'); break;
        case 'me':          historyRef.current = ['me']; setScreen('me'); break;
        case 'settings':    historyRef.current = ['home', 'settings']; setScreen('settings'); break;
        case 'help':        historyRef.current = ['home', 'settings', 'help']; setScreen('help'); break;
        case 'info':
        case 'about':       historyRef.current = ['home', 'settings', 'about']; setScreen('about'); break;
        case 'processing':  setScreen('processing'); break;
        case 'collections': historyRef.current = ['home']; setScreen('home'); break;
        case 'new-collection':
          historyRef.current = ['home'];
          setScreen('home');
          setCollSheet && setCollSheet('new');
          break;
        case 'collection': {
          if (arg) setGroupName(arg);
          historyRef.current = ['home', 'collection'];
          setScreen('collection');
          break;
        }
        case 'card': {
          if (arg) {
            const found = CARDS.find(c => c.id === arg) || CARDS[0];
            setCard(found);
          }
          historyRef.current = ['home', 'detail'];
          setScreen('detail');
          // Sheet sub-route (?sheet=actions|move|categories|share|delete)
          // Sheets open via Card Detail's internal state, so this is a
          // hint for the user-facing URL; the sheet must be opened
          // manually once the screen renders.
          break;
        }
        case 'input': {
          if (parts[1] === 'screenshot') {
            if (sub === 'detect') {
              setSsSelected(new Set(SAMPLE_REGIONS.slice(0, 5).map(r => r.id)));
              historyRef.current = ['home', 'input', 'screenshotDetect'];
              setScreen('screenshotDetect');
            } else if (sub === 'confirm') {
              const ids = new Set(SAMPLE_REGIONS.slice(0, 5).map(r => r.id));
              setSsSelected(ids);
              setSsItems(SAMPLE_REGIONS.filter(r => ids.has(r.id)).map(r => ({ id: r.id, text: r.text })));
              historyRef.current = ['home', 'input', 'screenshotConfirm'];
              setScreen('screenshotConfirm');
            } else {
              // source sheet — opens within Input screen
              historyRef.current = ['home', 'input'];
              setScreen('input');
            }
          } else {
            // typing / voice / default
            historyRef.current = ['home', 'input'];
            setScreen('input');
          }
          break;
        }
        case 'tweaks':
          // Tweaks panel toggles via the floating button; just go home.
          historyRef.current = ['home']; setScreen('home');
          break;
        default:
          // Unknown hash — no-op
          break;
      }
    };
    apply();
    window.addEventListener('hashchange', apply);
    return () => window.removeEventListener('hashchange', apply);
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, []);

  // expose for screenshots (set during render so it's always current)
  window.__cp = {
    goto: (s) => { historyRef.current.push(s); setScreen(s); },
    openCard: (i) => { setCard(CARDS[i]); historyRef.current.push('detail'); setScreen('detail'); },
    home: () => { historyRef.current = ['home']; setScreen('home'); },
    setDaily: setDailyCount,
    // Screenshot flow shortcuts
    ssDetect: () => {
      setSsSelected(new Set(SAMPLE_REGIONS.slice(0, 5).map(r => r.id)));
      historyRef.current.push('screenshotDetect');
      setScreen('screenshotDetect');
    },
    ssConfirm: () => {
      const ids = new Set(SAMPLE_REGIONS.slice(0, 5).map(r => r.id));
      setSsSelected(ids);
      setSsItems(SAMPLE_REGIONS.filter(r => ids.has(r.id)).map(r => ({ id: r.id, text: r.text })));
      historyRef.current.push('screenshotConfirm');
      setScreen('screenshotConfirm');
    },
    openCollection: (name) => {
      const g = groups.find(x => x.name === name) || groups[0];
      if (!g) return;
      setGroupName(g.name);
      setSharedGroupView(null);
      historyRef.current.push('collection');
      setScreen('collection');
    },
    openFriend: (idx) => {
      const friends = window.SAMPLE_FRIENDS || [];
      const f = friends[idx || 0];
      if (!f) return;
      setSelectedFriend(f);
      historyRef.current.push('friendDetail');
      setScreen('friendDetail');
    },
  };

  const handleAdd = () => goto('input');

  const handlePickScreenshot = () => {
    setSsSelected(new Set());
    historyRef.current.push('screenshotDetect');
    setScreen('screenshotDetect');
  };

  const handleDetectNext = () => {
    // Build items from selection (in original region order)
    const items = SAMPLE_REGIONS
      .filter(r => ssSelected.has(r.id))
      .map(r => ({ id: r.id, text: r.text }));
    setSsItems(items);
    historyRef.current.push('screenshotConfirm');
    setScreen('screenshotConfirm');
  };

  const handleScreenshotSubmit = (items) => {
    setScreen('processing');
    historyRef.current = ['home', 'processing'];
    setTimeout(() => {
      setQuotaUsed(c => Math.min(c + items.length, quotaTotal));
      // Reset screenshot state and go home
      setSsSelected(new Set());
      setSsItems([]);
      historyRef.current = ['home'];
      setScreen('home');
    }, 2400);
  };

  const handleSubmit = (text, opts = {}) => {
    setScreen('processing');
    historyRef.current = ['home', 'processing'];
    // Stash submission params so the result card can adopt them
    window.__cpLastSubmit = opts;
    setTimeout(() => {
      setQuotaUsed(c => Math.min(c + 1, quotaTotal));
      // simulate result -> show first card as result
      const submitOpts = window.__cpLastSubmit || {};
      const newCard = {
        ...CARDS[0],
        korean: text || CARDS[0].korean,
        tone: submitOpts.tone || '자동',
        category: submitOpts.cat || CARDS[0].category,
      };
      setCard(newCard);
      setScreen('detail');
      historyRef.current = ['home', 'detail'];
    }, 2600);
  };

  return (
    <div data-cp-text={['S','M','L'].includes(t.fontSize) ? t.fontSize : 'M'} style={{
      width: '100%', height: '100%',
      background: T.bg,
      position: 'relative',
      overflow: 'hidden',
      fontFamily: 'Pretendard, -apple-system, system-ui, sans-serif',
      color: T.ink,
    }}>
      <ScreenSwitcher screen={screen}>
        <Pane key="login" screen={screen} name="login">
          <LoginScreen accent={effectiveAccent} onSignedIn={() => {
            historyRef.current = ['home'];
            setScreen('home');
          }} />
        </Pane>
        <Pane key="friends" screen={screen} name="friends">
          <FriendsScreen accent={effectiveAccent}
                         requests={shareRequests}
                         received={combinedReceived}
                         onAccept={(req) => {
                           // Move request → acceptedExtras (with receivedDays=0)
                           setShareRequests(arr => arr.filter(r => r.id !== req.id));
                           setAcceptedExtras(arr => [{
                             ...req,
                             id: 'rc_' + req.id,
                             receivedDays: 0,
                           }, ...arr]);
                           setNudgeToast({
                             name: req.from,
                             title: `"${req.name}" 주머니를 받았어요`,
                             message: `${req.from}님이 보낸 씨앗 ${req.count}개`,
                           });
                         }}
                         onReject={(req) => {
                           setShareRequests(arr => arr.filter(r => r.id !== req.id));
                         }}
                         onOpenFriend={(f) => { setSelectedFriend(f); historyRef.current.push('friendDetail'); setScreen('friendDetail'); }}
                         onOpenReceived={(r) => {
                           // Open as a shared collection — same flow as home
                           goto('collection', {
                             name: r.name, color: r.color, cover: r.cover,
                             cards: r.cards || [], count: r.count,
                             primaryLang: r.primaryLang,
                             primaryLangLabel: r.primaryLangLabel || '여러 언어',
                             isMultiLang: r.isMultiLang || false,
                             isShared: true, sharedFrom: r.from, sharedFromFace: r.fromFace,
                             sharedFromAccent: r.fromAccent,
                             latestDays: r.receivedDays != null ? r.receivedDays : r.requestedDays,
                           });
                         }}
                         onOpenInvite={() => setShowInvite(true)}
                         onOpenNudge={(f) => setNudgeTarget(f)} />
        </Pane>
        <Pane key="me" screen={screen} name="me">
          <MeScreen accent={effectiveAccent} profile={profile}
                    quotaUsed={quotaUsed} quotaTotal={quotaTotal}
                    dailyCap={dailyCap} todayBoost={todayBoost}
                    onEditProfile={() => setShowProfileEdit(true)}
                    onOpenSettings={() => { setSettingsFocus(null); goto('settings'); }}
                    onOpenNotifications={() => { setSettingsFocus('notifications'); goto('settings'); }}
                    onOpenHelp={() => goto('help')}
                    onOpenAbout={() => goto('about')}
                    onUpgradeQuota={() => setShowUpgrade(true)} />
        </Pane>
        <Pane key="friendDetail" screen={screen} name="friendDetail">
          {selectedFriend &&
            <FriendDetailScreen friend={selectedFriend} accent={effectiveAccent}
                                 onBack={back} />}
        </Pane>
        <Pane key="home" screen={screen} name="home">
          <HomeV2 accent={effectiveAccent} dailyCount={dailyCount}
                  cards={cards} groups={groups}
                  pinnedSet={pinnedSet}
                  profile={profile}
                  receivedCollections={combinedReceived}
                  quotaUsed={quotaUsed} quotaTotal={quotaTotal}
                  onAdd={handleAdd}
                  onOpenCard={(c) => goto('detail', c)}
                  onOpenGroup={(g) => goto('collection', g)}
                  onOpenAll={() => goto('allCards')}
                  onOpenSearch={() => goto('search')}
                  onOpenSettings={() => goto('settings')}
                  onCreateCollection={() => setCollSheet('new')}
                  onUpgradeQuota={() => setShowUpgrade(true)} />
        </Pane>
        <Pane key="settings" screen={screen} name="settings">
          <SettingsScreen accent={effectiveAccent} mode={t.mode || 'system'}
                          fontSize={['S','M','L'].includes(t.fontSize) ? t.fontSize : 'M'}
                          onBack={back}
                          onOpenAbout={() => goto('about')}
                          onOpenHowTo={() => goto('howto')}
                          onOpenContact={() => goto('contact')}
                          onOpenTerms={() => goto('terms')}
                          onOpenPrivacy={() => goto('privacy')}
                          profile={profile} setProfile={setProfile}
                          focusSection={settingsFocus}
                          setTweak={setTweak} />
        </Pane>
        <Pane key="help" screen={screen} name="help">
          <HelpScreen accent={effectiveAccent} onBack={back} />
        </Pane>
        <Pane key="howto" screen={screen} name="howto">
          <HowToScreen accent={effectiveAccent} onBack={back} />
        </Pane>
        <Pane key="contact" screen={screen} name="contact">
          <ContactScreen accent={effectiveAccent} onBack={back} />
        </Pane>
        <Pane key="terms" screen={screen} name="terms">
          <TermsScreen accent={effectiveAccent} onBack={back} />
        </Pane>
        <Pane key="privacy" screen={screen} name="privacy">
          <PrivacyScreen accent={effectiveAccent} onBack={back} />
        </Pane>
        <Pane key="licenses" screen={screen} name="licenses">
          <LicensesScreen accent={effectiveAccent} onBack={back} />
        </Pane>
        <Pane key="about" screen={screen} name="about">
          <AboutScreen accent={effectiveAccent} onBack={back}
                       onOpenTerms={() => goto('terms')}
                       onOpenPrivacy={() => goto('privacy')}
                       onOpenLicenses={() => goto('licenses')}
                       onOpenContact={() => goto('contact')}
                       onOpenRating={() => setShowRating(true)}
                       onOpenSocial={() => setShowSocial(true)} />
        </Pane>
        <Pane key="allCards" screen={screen} name="allCards">
          <AllCards accent={effectiveAccent} onBack={back}
                    onOpenCard={(c) => goto('detail', c)} />
        </Pane>
        <Pane key="collection" screen={screen} name="collection">
          {currentGroup && <CollectionDetail group={currentGroup} accent={effectiveAccent}
                                       onBack={back}
                                       editing={collEditing} setEditing={setCollEditing}
                                       onOpenCard={(c) => goto('detail', c)}
                                       onOpenActions={() => setCollSheet('actions')}
                                       onAddCard={handleAdd}
                                       onAddExistingCards={() => setCollSheet('cards')}
                                       onRemoveCard={(cardId) => {
                                         setCards(arr => arr.map(c =>
                                           c.id === cardId ? { ...c, group: null } : c));
                                       }} />}
        </Pane>
        <Pane key="search" screen={screen} name="search">
          <SearchScreen accent={effectiveAccent}
                        onClose={back}
                        onOpenCard={(c) => goto('detail', c)}
                        onOpenGroup={(g) => goto('collection', g)} />
        </Pane>
        <Pane key="detail" screen={screen} name="detail">
          {card && <CardDetail card={card} accent={effectiveAccent} onBack={back}
                                groups={groups}
                                onOpenGroup={(g) => { setGroupName(g.name); historyRef.current.push('collection'); setScreen('collection'); }}
                                onMove={(toName, addIn) => {
                                  setCards(arr => arr.map(c => {
                                    if (c.id !== card.id) return c;
                                    const set = new Set([c.group, ...(c.extraGroups || [])].filter(Boolean));
                                    if (addIn) set.add(toName); else set.delete(toName);
                                    const list = [...set];
                                    return { ...c, group: list[0] || null, extraGroups: list.slice(1) };
                                  }));
                                  // Update local card prop reflection
                                  setCard(c => {
                                    if (!c) return c;
                                    const set = new Set([c.group, ...(c.extraGroups || [])].filter(Boolean));
                                    if (addIn) set.add(toName); else set.delete(toName);
                                    const list = [...set];
                                    return { ...c, group: list[0] || null, extraGroups: list.slice(1) };
                                  });
                                }}
                                onShare={(payload) => {
                                  if (payload && payload.title) {
                                    setNudgeToast({
                                      name: payload.target || '씨앗',
                                      title: payload.title,
                                      message: payload.message,
                                    });
                                  } else if (payload && payload.message) {
                                    setNudgeToast({ name: payload.target || '씨앗', message: payload.message });
                                  } else {
                                    setNudgeToast({ name: '씨앗', title: '공유 링크가 복사됐어요', message: '원하는 곳에 붙여넣어 보세요' });
                                  }
                                }}
                                onDelete={() => {
                                  setCards(arr => arr.filter(c => c.id !== card.id));
                                  back();
                                }}
                                onSetCategories={(cats) => {
                                  setCards(arr => arr.map(c => {
                                    if (c.id !== card.id) return c;
                                    return { ...c, categories: cats, category: cats[0] || c.category };
                                  }));
                                  setCard(c => c ? { ...c, categories: cats, category: cats[0] || c.category } : c);
                                }} />}
        </Pane>
        <Pane key="input" screen={screen} name="input">
          <InputScreen
            accent={effectiveAccent}
            onCancel={back}
            onSubmit={handleSubmit}
            onPickScreenshot={handlePickScreenshot}
            customCats={customCats}
            addCustomCat={(c) => setCustomCats(arr => arr.includes(c) ? arr : [...arr, c])}
            micPermission={micPermission}
          />
        </Pane>
        <Pane key="screenshotDetect" screen={screen} name="screenshotDetect" dark>
          <ScreenshotDetect
            accent={effectiveAccent}
            regions={SAMPLE_REGIONS}
            selected={ssSelected}
            setSelected={setSsSelected}
            onBack={back}
            onNext={handleDetectNext}
          />
        </Pane>
        <Pane key="screenshotConfirm" screen={screen} name="screenshotConfirm">
          <ScreenshotConfirm
            accent={effectiveAccent}
            items={ssItems}
            setItems={setSsItems}
            langSel={ssLang} setLangSel={setSsLang}
            tone={ssTone} setTone={setSsTone}
            cat={ssCat} setCat={setSsCat}
            customCats={customCats}
            addCustomCat={(c) => setCustomCats(arr => arr.includes(c) ? arr : [...arr, c])}
            onBack={back}
            onSubmit={handleScreenshotSubmit}
          />
        </Pane>
        <Pane key="processing" screen={screen} name="processing">
          <ProcessingScreen accent={effectiveAccent} />
        </Pane>
      </ScreenSwitcher>

      {/* Tab bar — only on tab-root screens */}
      {isOnTabScreen && (
        <TabBar active={activeTab} accent={effectiveAccent}
                badges={{ friends: 2 }}
                onChange={switchTab} />
      )}

      {/* Invite sheet (from Friends tab) */}
      {showInvite && (
        <InviteSheet accent={effectiveAccent} onClose={() => setShowInvite(false)} />
      )}
      {nudgeTarget && (
        <NudgeSheet friend={nudgeTarget} accent={effectiveAccent}
                    onClose={() => setNudgeTarget(null)}
                    onSend={({ text }) => {
                      const friendName = nudgeTarget.name;
                      setNudgeTarget(null);
                      setNudgeToast({ name: friendName, message: text });
                    }} />
      )}
      {nudgeToast && (
        <NudgeToast friendName={nudgeToast.name} message={nudgeToast.message}
                    title={nudgeToast.title}
                    accent={effectiveAccent}
                    onClose={() => setNudgeToast(null)} />
      )}
      {showProfileEdit && window.ProfileEditSheet && (
        <window.ProfileEditSheet
          accent={effectiveAccent}
          name={profile.name} face={profile.face}
          onClose={() => setShowProfileEdit(false)}
          onSave={({ name: n, face: f }) => {
            setProfile({ name: n, face: f });
            setShowProfileEdit(false);
          }}
        />
      )}
      {showRating && window.RatingSheet && (
        <window.RatingSheet accent={effectiveAccent} onClose={() => setShowRating(false)} />
      )}
      {showSocial && window.SocialLinksSheet && (
        <window.SocialLinksSheet accent={effectiveAccent} onClose={() => setShowSocial(false)} />
      )}

      <TweaksPanel>
        <TweakSection label="포인트 컬러" />
        <TweakColor label="Accent" value={t.accent}
                    options={[ACCENTS.walnut, ACCENTS.terracotta, ACCENTS.forest, ACCENTS.plum]}
                    onChange={(v) => setTweak('accent', v)} />
        <TweakSection label="주요 화면" />
        <TweakButton label="로그인" onClick={() => { historyRef.current = ['login']; setScreen('login'); }}>로그인</TweakButton>
        <TweakButton label="홈으로" onClick={() => { historyRef.current = ['home']; setScreen('home'); }}>홈 (새 홈)</TweakButton>
        <TweakButton label="검색 화면" onClick={() => { historyRef.current = ['home', 'search']; setScreen('search'); }}>검색</TweakButton>
        <TweakButton label="주머니 상세" onClick={() => { setGroupName(groups[0]?.name); historyRef.current = ['home', 'collection']; setScreen('collection'); }}>도쿄 식당 주머니</TweakButton>
        <TweakButton label="전체 씨앗" onClick={() => { historyRef.current = ['home', 'allCards']; setScreen('allCards'); }}>전체 씨앗 (필터)</TweakButton>
        <TweakButton label="설정" onClick={() => { historyRef.current = ['home', 'settings']; setScreen('settings'); }}>설정</TweakButton>
        <TweakButton label="About" onClick={() => { historyRef.current = ['home', 'settings', 'about']; setScreen('about'); }}>About</TweakButton>
        <TweakSection label="스크린샷 흐름" />
        <TweakButton label="1. 입력 화면" onClick={() => { historyRef.current = ['home', 'input']; setScreen('input'); }}>텍스트 입력</TweakButton>
        <TweakButton label="2. 탐지/선택" onClick={() => window.__cp.ssDetect()}>탐지 화면</TweakButton>
        <TweakButton label="3. 씨앗 확인" onClick={() => window.__cp.ssConfirm()}>확인 화면</TweakButton>
        <TweakButton label="새 주머니" onClick={() => setCollSheet('new')}>+ 새 주머니</TweakButton>
        <TweakSection label="음성 입력" />
        <TweakRadio label="마이크 권한" value={micPermission}
                    options={['granted', 'denied']}
                    onChange={(v) => setMicPermission(v)} />
        <TweakSection label="볼주머니" />
        <TweakButton label="씨앗 1개 담기" onClick={() => setQuotaUsed(c => Math.min(c + 1, quotaTotal))}>+ 1개 담기</TweakButton>
        <TweakButton label="오늘 비우기" onClick={() => { setQuotaUsed(0); setTodayBoost(0); }}>오늘 비우기 (자정 시뮬)</TweakButton>
      </TweaksPanel>

      {/* Collection sheets */}
      {showUpgrade && (
        <UpgradeQuotaSheet
          accent={effectiveAccent}
          used={quotaUsed}
          total={quotaTotal}
          dailyCap={dailyCap}
          dailyCapMax={DAILY_CAP_MAX}
          todayBoost={todayBoost}
          onWatchAd={() => setTodayBoost(b => b + 1)}
          onInviteFriend={() => setDailyCap(c => Math.min(DAILY_CAP_MAX, c + 1))}
          onClose={() => setShowUpgrade(false)}
        />
      )}
      {collSheet === 'new' && (
        <NewCollectionSheet
          accent={effectiveAccent}
          suggestedNames={['다음 주 도쿄 여행', '명동 맛집 메모', '엠마와의 연습']}
          existingNames={groups.map(g => g.name)}
          onClose={() => setCollSheet(null)}
          onCreate={({ name, color, cover }) => {
            setExtraCollections(arr => [...arr, { name, color, cover }]);
            setCollSheet(null);
            setGroupName(name);
            historyRef.current.push('collection');
            setScreen('collection');
          }}
        />
      )}
      {collSheet === 'actions' && currentGroup && (
        <CollectionActionsSheet
          collection={currentGroup}
          pinned={pinnedSet.has(currentGroup.name)}
          onClose={() => setCollSheet(null)}
          onEdit={() => setCollSheet('edit')}
          onManageCards={() => setCollSheet('cards')}
          onCleanup={() => { setCollEditing(true); setCollSheet(null); }}
          onShare={() => setCollSheet('share')}
          onTogglePin={() => {
            setPinnedSet(s => {
              const next = new Set(s);
              if (next.has(currentGroup.name)) next.delete(currentGroup.name);
              else next.add(currentGroup.name);
              return next;
            });
            setCollSheet(null);
          }}
          onDelete={() => setCollSheet('delete')}
        />
      )}
      {collSheet === 'edit' && currentGroup && (
        <EditCollectionSheet
          collection={currentGroup}
          accent={effectiveAccent}
          existingNames={groups.map(g => g.name)}
          onClose={() => setCollSheet(null)}
          onSave={({ name, color, cover }) => {
            const oldName = currentGroup.name;
            if (name !== oldName) {
              setCards(arr => arr.map(c => c.group === oldName ? { ...c, group: name } : c));
            }
            setExtraCollections(arr => {
              const idx = arr.findIndex(e => e.name === oldName);
              if (idx >= 0) {
                const copy = [...arr];
                copy[idx] = { ...copy[idx], name, color, cover };
                return copy;
              }
              // For auto-derived groups being edited, add an override entry
              return [...arr, { name, color, cover, override: true }];
            });
            setGroupName(name);
            setCollSheet(null);
          }}
        />
      )}
      {collSheet === 'cards' && currentGroup && (
        <CardPickerSheet
          collection={currentGroup}
          allCards={cards}
          accent={effectiveAccent}
          onClose={() => setCollSheet(null)}
          onConfirm={(ids) => {
            const idSet = new Set(ids);
            setCards(arr => arr.map(c =>
              idSet.has(c.id) ? { ...c, group: currentGroup.name } : c));
            setCollSheet(null);
          }}
        />
      )}
      {collSheet === 'share' && currentGroup && (
        <ShareCollectionSheet
          collection={currentGroup}
          accent={effectiveAccent}
          onClose={() => setCollSheet(null)}
          onSent={(payload) => {
            setCollSheet(null);
            if (payload && payload.title) {
              setNudgeToast({
                name: payload.target || '주머니',
                title: payload.title,
                message: payload.message,
              });
            }
          }}
        />
      )}
      {collSheet === 'delete' && currentGroup && (
        <DeleteConfirmSheet
          collection={currentGroup}
          onClose={() => setCollSheet(null)}
          onConfirm={() => {
            const name = currentGroup.name;
            // Remove cards' group field
            setCards(arr => arr.map(c => c.group === name ? { ...c, group: null } : c));
            // Remove from extras
            setExtraCollections(arr => arr.filter(e => e.name !== name));
            setCollSheet(null);
            historyRef.current = ['home'];
            setScreen('home');
            setGroupName(null);
          }}
        />
      )}
    </div>
  );
}

function ScreenSwitcher({ children }) {
  return <div style={{ width: '100%', height: '100%', position: 'relative' }}>{children}</div>;
}

function Pane({ screen, name, children, dark }) {
  const visible = screen === name;
  if (!visible) return null;
  return (
    <div data-pane={name} style={{
      position: 'absolute', inset: 0,
      background: dark ? '#171210' : T.bg,
      overflowY: dark ? 'hidden' : 'auto',
      overflowX: 'hidden',
      WebkitOverflowScrolling: 'touch',
    }}>{children}</div>
  );
}

// Mount
const rootEl = document.getElementById('app-root');
ReactDOM.createRoot(rootEl).render(
  <IOSDevice width={402} height={874}>
    <App />
  </IOSDevice>
);
