// 타운카 제휴업체 — 상세 모달 (바텀시트)

// 외부 링크 열기 헬퍼 — interactions.js의 window.tcOpenExternal로 위임.
// 앱 컨텍스트에선 ExternalSheet으로, 일반 웹에선 새 탭으로 분기됨.
function openExternalLink(url) {
  if (!url) return;
  if (window.tcOpenExternal) window.tcOpenExternal(url);
  else window.open(url, '_blank', 'noopener,noreferrer');
}

// ── 네이버 지도 미리보기 컴포넌트
// 마커: data.js의 partner.coord 필드(지역 기반 대략 좌표) 직접 사용. Geocoding API 호출 없음.
// 확장 버튼: 네이버 지도 웹/앱에서 주소 검색으로 정확한 위치 확인.
// Naver Maps JS v3가 index.html에서 async로 로드됨.
function _naverMapsReady() {
  if (typeof window === 'undefined') return Promise.reject();
  if (window.naver && window.naver.maps) return Promise.resolve();
  if (!window.__naverMapsReadyPromise) {
    window.__naverMapsReadyPromise = new Promise((resolve) => {
      const existing = document.querySelector('script[src*="oapi.map.naver.com/openapi/v3/maps.js"]');
      const tick = () => {
        if (window.naver && window.naver.maps) { resolve(); return true; }
        return false;
      };
      if (tick()) return;
      if (existing) {
        existing.addEventListener('load', () => tick());
        existing.addEventListener('error', () => tick());
      }
      const id = setInterval(() => { if (tick()) clearInterval(id); }, 50);
      setTimeout(() => { clearInterval(id); resolve(); }, 15000);
    });
  }
  return window.__naverMapsReadyPromise;
}

// partner.coord — data.js의 coord(area) 함수가 [lat, lng] 배열 형태로 줌. 객체도 호환.
function _resolveCoord(partner) {
  const c = partner?.coord;
  if (Array.isArray(c) && c.length >= 2 && Number.isFinite(c[0]) && Number.isFinite(c[1])) {
    return { lat: c[0], lng: c[1] };
  }
  if (c && Number.isFinite(c.lat) && Number.isFinite(c.lng)) {
    return { lat: c.lat, lng: c.lng };
  }
  // 최종 fallback — 서울 시청
  return { lat: 37.5665, lng: 126.9780 };
}

function NaverMapPreview({ partner }) {
  const mapRef = React.useRef(null);
  const [coord, setCoord] = React.useState(null);

  React.useEffect(() => {
    if (!partner) return;
    let cancelled = false;
    _naverMapsReady().then(() => {
      if (cancelled || !window.naver?.maps) return;
      setCoord(_resolveCoord(partner));
    });
    return () => { cancelled = true; };
  }, [partner?.id]);

  // 좌표 확정된 후에만 지도 그림 → 깜빡임 제거
  React.useEffect(() => {
    if (!coord || !mapRef.current || !window.naver?.maps) return;
    const naver = window.naver;
    const center = new naver.maps.LatLng(coord.lat, coord.lng);

    const map = new naver.maps.Map(mapRef.current, {
      center, zoom: 16,
      // 미리보기에서도 드래그·핀치줌·휠줌·더블탭 모두 활성. 컨트롤 UI는 가리고, 확장 버튼은 별도.
      draggable: true, pinchZoom: true, scrollWheel: true,
      keyboardShortcuts: false,
      zoomControl: false, scaleControl: false, mapDataControl: false,
      logoControl: true, logoControlOptions: { position: naver.maps.Position.BOTTOM_LEFT },
    });

    const marker = new naver.maps.Marker({ position: center, map });

    return () => {
      try { marker.setMap(null); } catch (e) {}
      try { map.destroy && map.destroy(); } catch (e) {}
    };
  }, [coord]);

  // 확장 버튼 → 네이버 지도로 이동
  // 우선순위: (1) 노션 DB에 박힌 네이버 플레이스 URL (naver.me/...) — 해당 매장 페이지 직행
  //          (2) 폴백 — 주소 검색
  const openInNaverMap = (e) => {
    e.stopPropagation();
    const placeUrl = partner.naverPlace || partner.naverPlaceUrl;
    let webUrl;
    if (placeUrl) {
      webUrl = placeUrl;
    } else {
      const q = partner.address || partner.name;
      webUrl = 'https://map.naver.com/p/search/' + encodeURIComponent(q);
    }
    if (window.tcOpenExternal) window.tcOpenExternal(webUrl);
    else window.open(webUrl, '_blank', 'noopener,noreferrer');
  };

  return (
    <div style={{
      position: 'relative', margin: '0 16px 4px',
      borderRadius: 12, overflow: 'hidden',
      border: '1px solid ' + TC.line,
      background: TC.fillSubtle,
    }}>
      {/* 지도 자체에서 드래그·핀치줌 가능 */}
      <div ref={mapRef} style={{ width: '100%', height: 240 }} />
      {!coord && (
        <div style={{
          position: 'absolute', inset: 0,
          display: 'flex', alignItems: 'center', justifyContent: 'center',
          fontSize: 12, color: TC.textAlt, letterSpacing: '-0.005em',
          pointerEvents: 'none',
        }}>지도 불러오는 중…</div>
      )}
      {/* 우측 상단 확장 버튼 — 네이버 지도 앱/웹으로 이동 */}
      <button onClick={openInNaverMap} aria-label="네이버 지도에서 보기" style={{
        all: 'unset', cursor: 'pointer',
        position: 'absolute', top: 8, right: 8,
        width: 34, height: 34, borderRadius: 10,
        background: 'rgba(255,255,255,0.96)',
        boxShadow: '0 2px 8px rgba(0,0,0,0.12)',
        display: 'flex', alignItems: 'center', justifyContent: 'center',
        zIndex: 2,
      }}>
        <Icon name="maximize-2" size={16} color={TC.textStrong} stroke={2.2} />
      </button>
    </div>
  );
}

function DetailModal({ partner, onClose, onCall, onCopy, onDirections }) {
  const [open, setOpen] = React.useState(false);
  const [closing, setClosing] = React.useState(false);
  const [dragY, setDragY] = React.useState(0);   // 현재 드래그 거리 (px)
  const [dragging, setDragging] = React.useState(false);
  const dragRef = React.useRef({
    startY: 0, lastY: 0, lastT: 0, scrollAtStart: 0, captured: false
  });
  const scrollBodyRef = React.useRef(null);
  const useVTAPI = (typeof document !== 'undefined') && ('startViewTransition' in document);

  // useLayoutEffect: flushSync 안에서 commit 직후 동기 실행되도록 → VTAPI 스냅샷이 최종 상태를 잡음
  React.useLayoutEffect(() => {
    if (partner) {
      setClosing(false);
      setDragY(0);
      // 항상 rAF로 슬라이드업 (CSS transition 트리거) — 아래에서 위로 올라오는 애니메이션
      requestAnimationFrame(() => setOpen(true));
    } else {
      setOpen(false);
    }
  }, [partner?.id]);

  // 햅틱은 paint 후 useEffect로
  React.useEffect(() => {
    if (partner && window.haptic) window.haptic.impact();
  }, [partner?.id]);

  const handleClose = () => {
    setClosing(true);
    setOpen(false);
    if (window.haptic) window.haptic.light();
    setTimeout(() => {
      setClosing(false);
      setDragY(0);
      onClose && onClose();
    }, 260);
  };

  // ───── 드래그-닫기 (헤더 영역 잡고 아래로 끌면 닫힘) ─────
  const DISMISS_THRESHOLD = 120;     // px — 이만큼 끌면 닫힘
  const VELOCITY_THRESHOLD = 0.6;    // px/ms — 빠른 플릭

  const onDragStart = (e) => {
    if (!open) return;
    // 스크롤 본문이 최상단이 아니면 (=내용 스크롤 중) 드래그 시작 안 함
    const sb = scrollBodyRef.current;
    if (sb && e.target.closest && e.target.closest('[data-modal-scroll]') && sb.scrollTop > 0) return;
    dragRef.current = {
      startY: e.clientY,
      lastY: e.clientY,
      lastT: performance.now(),
      scrollAtStart: sb ? sb.scrollTop : 0,
      captured: false,
    };
    setDragging(true);
  };

  const onDragMove = (e) => {
    if (!dragging) return;
    const dy = e.clientY - dragRef.current.startY;
    if (dy < 0) { setDragY(0); return; } // 위로 드래그는 무시
    // 처음 임계값(8px)을 넘으면 포인터 캡처해서 스크롤과 분리
    if (!dragRef.current.captured && dy > 8) {
      try { e.currentTarget.setPointerCapture && e.currentTarget.setPointerCapture(e.pointerId); } catch (err) {}
      dragRef.current.captured = true;
    }
    if (dragRef.current.captured) {
      // 저항감 — 끌수록 살짝 빡빡하게
      const eased = dy < 200 ? dy : 200 + (dy - 200) * 0.5;
      setDragY(eased);
      dragRef.current.lastY = e.clientY;
      dragRef.current.lastT = performance.now();
      if (e.cancelable) e.preventDefault();
    }
  };

  const onDragEnd = (e) => {
    if (!dragging) return;
    setDragging(false);
    const dy = e.clientY - dragRef.current.startY;
    const dt = Math.max(1, performance.now() - dragRef.current.lastT);
    const v = (e.clientY - dragRef.current.lastY) / dt; // 양수 = 아래로

    if (dragRef.current.captured && (dy > DISMISS_THRESHOLD || v > VELOCITY_THRESHOLD)) {
      handleClose();
    } else {
      // 스프링 복귀
      setDragY(0);
    }
  };

  if (!partner && !closing) return null;
  const p = partner;

  // 카테고리/브랜드 라벨
  const cat = window.CATEGORIES.find(c => c.id === p?.category);
  // 세부(하위) 카테고리 라벨 — 있으면 상위 카테고리 대신 표시
  const subDef = p?.sub
    ? [...(window.ETC_SUBS || []), ...(window.FINANCING_SUBS || [])].find(s => s.id === p.sub)
    : null;

  return (
    <div style={{
      position: 'fixed', inset: 0, zIndex: 100,
      pointerEvents: open || closing ? 'auto' : 'none',
      fontFamily: FF,
      display: 'flex', justifyContent: 'center',
    }}>
      {/* dim — 전체 뷰포트 덮기 (드래그 거리에 따라 옅어짐) */}
      <div onClick={handleClose} style={{
        position: 'fixed', inset: 0,
        background: 'rgba(23,23,25,0.55)',
        opacity: open ? Math.max(0, 1 - dragY / 400) : 0,
        transition: dragging ? 'none' : 'opacity 240ms cubic-bezier(0.2,0,0,1)',
      }} />

      {/* sheet — 모바일 컨테이너 너비에 맞춰 가운데 */}
      <div style={{
        position: 'fixed', left: '50%', bottom: 0,
        transform: open
          ? `translate(-50%, ${dragY}px)`
          : 'translate(-50%, 100%)',
        width: '100%', maxWidth: 480,
        background: '#fff', borderRadius: '24px 24px 0 0',
        boxShadow: '0 -8px 32px rgba(0,0,0,0.18)',
        transition: dragging ? 'none' : 'transform 320ms cubic-bezier(0.32,0.72,0,1)',
        maxHeight: '85vh',
        display: 'flex', flexDirection: 'column',
        overflow: 'hidden',
        touchAction: 'pan-y',
        willChange: 'transform',
        // View Transitions API — 카드와 매칭되어 모핑 (interactions.js의 tcCardOpen)
        viewTransitionName: open ? 'tc-detail-shared' : undefined,
      }}>
        {/* drag handle — 여기 + 헤더 영역 잡고 아래로 끌면 닫힘 */}
        <div
          data-no-haptic
          onPointerDown={onDragStart}
          onPointerMove={onDragMove}
          onPointerUp={onDragEnd}
          onPointerCancel={onDragEnd}
          style={{
            display: 'flex', justifyContent: 'center',
            paddingTop: 8, paddingBottom: 12,
            cursor: 'grab', touchAction: 'none',
          }}
        >
          <div style={{
            width: 40, height: 4, borderRadius: 999,
            background: dragging ? TC.text : TC.fillStrong,
            transition: dragging ? 'none' : 'background 160ms',
          }} />
        </div>

        {/* close button */}
        <button onClick={handleClose} style={{
          all: 'unset', cursor: 'pointer',
          position: 'absolute', top: 14, right: 12,
          width: 32, height: 32, borderRadius: 999,
          background: TC.fillSubtle,
          display: 'flex', alignItems: 'center', justifyContent: 'center',
          zIndex: 1,
        }}><Icon name="x" size={18} color={TC.text} stroke={2.4} /></button>

        {/* scroll body */}
        <div ref={scrollBodyRef} data-modal-scroll style={{
          flex: 1, overflowY: 'auto', WebkitOverflowScrolling: 'touch',
          overscrollBehavior: 'contain',
        }}>
          {p && (
            <>
              {/* header block — 리스트 카드 디자인을 모달 헤더에 그대로 (아이콘 크게,
                   카테고리·브랜드 작게 윗줄 / 업체명 크게 아랫줄) */}
              <div style={{ padding: '8px 20px 4px' }}>
                <div style={{ display: 'flex', alignItems: 'center', gap: 14 }}>
                  <window.PartnerAsset partner={p} size={56} />
                  <div style={{ flex: 1, minWidth: 0, paddingRight: 36 }}>
                    {(p.brand || cat) && (
                      <div style={{
                        fontSize: 13, fontWeight: 600, color: TC.textNeutral,
                        letterSpacing: '-0.005em', marginBottom: 2,
                        whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis',
                      }}>
                        {/* 브랜드(신차) → 세부분류(노션 입력) → 서브분류 라벨 → 상위 카테고리 순으로 표시 */}
                        {p.brand || p.subLabel || (subDef && subDef.label) || (cat && cat.label)}
                      </div>
                    )}
                    <h2 style={{
                      margin: 0, fontSize: 22, fontWeight: 700,
                      letterSpacing: '-0.025em', lineHeight: '28px',
                      color: TC.textStrong,
                    }}>{p.name}</h2>
                  </div>
                </div>

                {/* badges */}
                {(p.flagship || (p.badges && p.badges.length)) && (
                  <div style={{ display: 'flex', flexWrap: 'wrap', gap: 4, marginTop: 10 }}>
                    {p.flagship && <MiniBadge tone="yellow">플래그십 스토어</MiniBadge>}
                    {p.badges && p.badges.map((b, i) => (
                      <MiniBadge key={i} tone={/타운카 차주/.test(b) ? 'accent' : 'info'}>{b}</MiniBadge>
                    ))}
                  </div>
                )}

                {p.discount && (
                  <div style={{
                    marginTop: 12, padding: 12, borderRadius: 14,
                    background: TC.primaryWeak,
                    border: '1px solid ' + TC.primaryWeakBorder,
                  }}>
                    <div style={{ display: 'flex', alignItems: 'flex-start', gap: 8 }}>
                      <div style={{
                        flexShrink: 0, width: 28, height: 28, borderRadius: 999,
                        background: '#fff', display: 'flex', alignItems: 'center', justifyContent: 'center',
                        boxShadow: '0 1px 2px rgba(11,181,59,0.16)',
                      }}>
                        <Icon name="tag" size={14} color={TC.primary} stroke={2.2} />
                      </div>
                      <div style={{ flex: 1 }}>
                        <div style={{ fontSize: 12, fontWeight: 700, color: TC.primaryPressed, letterSpacing: '-0.005em', marginBottom: 2 }}>
                          타운카 전용 혜택
                        </div>
                        <div style={{ fontSize: 13.5, color: TC.text, lineHeight: '19px', letterSpacing: '-0.005em' }}>
                          {p.discount}
                        </div>
                      </div>
                    </div>
                  </div>
                )}
              </div>

              {/* 헤더 ↔ 정보 행 사이 라인은 제거 — 헤더 아래 여백으로만 분리 */}

              {/* 네이버 지도 미리보기 — 헤더 직후, 가장 위에 노출. 주소 있는 경우만. */}
              {p.address && <div style={{ padding: '12px 0 4px' }}><NaverMapPreview partner={p} /></div>}

              {(() => {
                const hasContact = p.address || p.phone || p.manager;
                return (
                  <>
                    {/* 정보가 없는 매장: 깔끔한 빈 상태 */}
                    {!hasContact && (
                      <div style={{
                        padding: '24px 20px 20px',
                        display: 'flex', flexDirection: 'column', alignItems: 'center', textAlign: 'center',
                      }}>
                        <div style={{
                          width: 48, height: 48, borderRadius: 999,
                          background: TC.fillSubtle,
                          display: 'flex', alignItems: 'center', justifyContent: 'center',
                          marginBottom: 12,
                        }}>
                          <Icon name="store" size={22} color={TC.textNeutral} stroke={1.8} />
                        </div>
                        <div style={{
                          fontSize: 15, fontWeight: 700, color: TC.textStrong,
                          letterSpacing: '-0.010em', marginBottom: 6,
                        }}>지점 안내 정보가 준비 중이에요</div>
                        <div style={{
                          fontSize: 13, color: TC.textNeutral, letterSpacing: '0.015em',
                          lineHeight: '19px', wordBreak: 'keep-all', maxWidth: 280,
                        }}>지점에 방문하시거나 타운카 고객센터로 문의하시면<br />상담받으실 수 있어요.</div>
                      </div>
                    )}

                    {/* info rows */}
                    {(hasContact || p.note || p.website) && (() => {
                      // 정보 행들을 배열로 모아서, 사이사이에 얇은 구분선을 끼워 렌더링
                      const rows = [];
                      if (p.address) {
                        rows.push(
                          <InfoRow key="addr" icon="map-pin" label="주소"
                            value={p.address}
                            trailing={
                              <button onClick={() => onCopy && onCopy(p.address)} style={{
                                all: 'unset', cursor: 'pointer',
                                flexShrink: 0, display: 'inline-flex', alignItems: 'center', gap: 4,
                                height: 30, padding: '0 13px', borderRadius: 999,
                                background: TC.fill, color: TC.textNeutral,
                                fontSize: 13, fontWeight: 600, letterSpacing: '-0.005em',
                              }}>
                                <Icon name="copy" size={13} color={TC.textNeutral} stroke={2.2} />복사
                              </button>
                            }
                          />
                        );
                      }
                      if (p.phone || p.manager) {
                        // 전화 배지 — 하단 sticky CTA 제거에 따라 이게 주 전화 액션이 됨.
                        // 디자인 시스템의 TC.primary (#0BB53B Towncar green) 으로 통일.
                        const buildPhoneBadge = (phoneNumber) => (
                          <a
                            href={telUrl(phoneNumber)}
                            target="_top"
                            rel="noopener"
                            onClick={(e) => {
                              if (window.haptic) window.haptic.medium();
                              onCall && onCall(p);
                              try {
                                if (window.top && window.top !== window.self) {
                                  e.preventDefault();
                                  window.top.location.href = telUrl(phoneNumber);
                                }
                              } catch (err) { /* cross-origin이면 기본 <a> 동작 */ }
                            }}
                            style={{
                              cursor: 'pointer', textDecoration: 'none',
                              flexShrink: 0, display: 'inline-flex', alignItems: 'center', gap: 4,
                              height: 32, padding: '0 14px', borderRadius: 999,
                              background: TC.primary, color: '#fff',
                              fontSize: 13, fontWeight: 700, letterSpacing: '-0.005em',
                              boxSizing: 'border-box',
                            }}>
                            <Icon name="phone" size={13} color="#fff" stroke={2.4} />전화
                          </a>
                        );
                        rows.push(
                          <InfoRow key="phone" icon="phone" label={p.manager ? '담당자' : '연락처'}
                            value={
                              p.manager ? (
                                <div style={{ fontSize: 15.5, fontWeight: 600, color: TC.textStrong, letterSpacing: '-0.005em' }}>{p.manager}</div>
                              ) : (
                                <div style={{ fontSize: 15.5, color: TC.text, letterSpacing: '0.010em', fontVariantNumeric: 'tabular-nums' }}>
                                  {p.phone}
                                </div>
                              )
                            }
                            trailing={p.phone && buildPhoneBadge(p.phone)}
                          />
                        );
                        // 추가 담당자 행들 (extraContacts) — 같은 "담당자" 라벨, 같은 디자인
                        if (p.extraContacts && p.extraContacts.length) {
                          p.extraContacts.forEach((c, i) => {
                            if (!c || !c.name) return;
                            rows.push(
                              <InfoRow key={'extra-' + i} icon="phone" label="담당자"
                                value={<div style={{ fontSize: 15.5, fontWeight: 600, color: TC.textStrong, letterSpacing: '-0.005em' }}>{c.name}</div>}
                                trailing={c.phone && buildPhoneBadge(c.phone)}
                              />
                            );
                          });
                        }
                      }
                      if (p.note) {
                        rows.push(
                          // 길면 한 줄만 보이고 "더보기" 토글로 펼치기. 아이콘은 상단 정렬.
                          <InfoRow key="note" icon="alert-circle" label="안내사항" align="top"
                            value={<NoteValue text={p.note} />}
                          />
                        );
                      }
                      if (p.website) {
                        rows.push(
                          <InfoRow key="web" icon={p.websiteLabel ? 'gift' : 'globe'} label={p.websiteLabel || '홈페이지'}
                            value={
                              <a href={p.website} target="_blank" rel="noopener noreferrer"
                                onClick={(e) => { e.preventDefault(); openExternalLink(p.website); }}
                                style={{
                                fontSize: 14.5, color: TC.text, letterSpacing: '0.010em',
                                textDecoration: 'none', wordBreak: 'break-all',
                                display: 'inline-flex', alignItems: 'center', gap: 4,
                              }}>
                                {/* 쿼리스트링/경로 다 잘라내고 호스트네임만 노출 — partsmanager.co.kr/?NaPm=... 같은 추적 URL 정리 */}
                                {p.websiteLabel ? '프로모션 페이지' : p.website.replace(/^https?:\/\//, '').replace(/^www\./, '').split('/')[0].split('?')[0]}
                                <Icon name="external-link" size={12} color={TC.textNeutral} stroke={2} />
                              </a>
                            }
                            trailing={
                              <a href={p.website} target="_blank" rel="noopener noreferrer"
                                onClick={(e) => { e.preventDefault(); openExternalLink(p.website); }}
                                style={{
                                flexShrink: 0, display: 'inline-flex', alignItems: 'center', gap: 4,
                                height: 30, padding: '0 13px', borderRadius: 999,
                                background: TC.fill, color: TC.textNeutral,
                                fontSize: 13, fontWeight: 600, letterSpacing: '-0.005em',
                                textDecoration: 'none',
                              }}>
                                <Icon name="arrow-up-right" size={13} color={TC.textNeutral} stroke={2.2} />
                                이동
                              </a>
                            }
                          />
                        );
                      }
                      return (
                        <div style={{
                          padding: hasContact ? '16px 20px 4px' : '8px 20px 4px',
                          display: 'flex', flexDirection: 'column',
                        }}>
                          {rows.map((row, i) => (
                            <React.Fragment key={i}>
                              <div style={{ padding: i === 0 ? '0 0 20px' : '20px 0' }}>{row}</div>
                              {i < rows.length - 1 && (
                                <div style={{ height: 1, background: TC.line }} />
                              )}
                            </React.Fragment>
                          ))}
                        </div>
                      );
                    })()}
                  </>
                );
              })()}

              {/* tips — 카테고리 콜아웃(요약) + 차주 안내 */}
              {(() => {
                const intro = window.INTROS && window.INTROS[p.category];
                // 서브분류별 전용 문구가 있으면 카테고리 공용 문구 대신 사용
                const subIntro = window.SUB_INTROS && window.SUB_INTROS[p.sub];
                const calloutText = subIntro || intro?.modalDesc || intro?.desc || null;
                return (
                  <div style={{
                    margin: '0 16px 4px', padding: '14px 16px', borderRadius: 12,
                    background: TC.fillSubtle,
                    display: 'flex', alignItems: 'flex-start', gap: 10,
                  }}>
                    <Icon name="info" size={16} color={TC.textNeutral} stroke={2} style={{ marginTop: 1, flexShrink: 0 }} />
                    <div style={{ flex: 1, fontSize: 12.5, color: TC.text, lineHeight: '19px', letterSpacing: '0.005em', wordBreak: 'keep-all' }}>
                      {calloutText && <>{calloutText} </>}
                      업체에 전화하거나 방문하실 때 <strong style={{ color: TC.textStrong, fontWeight: 700 }}>"타운카 차주입니다"</strong>라고 먼저 말씀해 주세요.
                    </div>
                  </div>
                );
              })()}

              {/* sticky CTA 가림 방지용 최소 spacer */}
              <div style={{ height: 8 }} />
            </>
          )}
        </div>

        {/* sticky bottom CTA */}
        {p && (() => {
          const hasContact = p.address || p.phone || p.manager;
          if (!hasContact) {
            // 정보 없는 매장: 단일 닫기 버튼
            return (
              <div style={{
                padding: '12px 16px calc(12px + env(safe-area-inset-bottom, 0px))',
                background: '#fff',
              }}>
                <button onClick={handleClose} style={{
                  all: 'unset', cursor: 'pointer', width: '100%',
                  height: 48, borderRadius: 12, background: TC.textStrong,
                  color: '#fff',
                  fontSize: 15, fontWeight: 700, letterSpacing: '0.010em',
                  display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 6,
                  boxSizing: 'border-box',
                }}>
                  확인
                </button>
              </div>
            );
          }
          // 연락처/주소가 있는 경우엔 하단 sticky CTA를 두지 않음 —
          // 지도 미리보기의 확장 버튼 + 정보 행의 '전화' 배지로 동일 기능 커버됨.
          // 본문 공간을 확보해 지도 미리보기를 더 크게 노출.
          return null;
        })()}
      </div>
    </div>
  );
}

function InfoRow({ icon, label, value, trailing, align }) {
  // align="top" → 다중 라인 콘텐츠(예: 안내사항)에서 아이콘이 상단에 정렬
  const isTop = align === 'top';
  return (
    <div style={{ display: 'flex', alignItems: isTop ? 'flex-start' : 'center', gap: 12 }}>
      <div style={{
        flexShrink: 0, width: 34, height: 34, borderRadius: 999,
        background: TC.fillSubtle, display: 'flex',
        alignItems: 'center', justifyContent: 'center',
      }}>
        <Icon name={icon} size={17} color={TC.text} stroke={2} />
      </div>
      <div style={{ flex: 1, minWidth: 0, display: 'flex', flexDirection: 'column', gap: 2 }}>
        <div style={{ fontSize: 12, fontWeight: 600, color: TC.textNeutral, letterSpacing: '0.030em' }}>{label}</div>
        <div style={{ fontSize: 15.5, color: TC.textStrong, lineHeight: '22px', letterSpacing: '0.010em', wordBreak: 'keep-all' }}>{value}</div>
      </div>
      {trailing && <div style={{ flexShrink: 0 }}>{trailing}</div>}
    </div>
  );
}

// ── 긴 안내사항 — 첫 줄만 노출 + "더보기" 토글
function NoteValue({ text }) {
  const [expanded, setExpanded] = React.useState(false);
  const trimmed = String(text || '').trim();
  // \n 기준으로 나누되, 빈 줄은 무시
  const lines = trimmed.split(/\r?\n/).map((l) => l.trim()).filter(Boolean);
  const isLong = lines.length > 1 || trimmed.length > 60;
  const base = { fontSize: 14.5, color: TC.textNeutral, lineHeight: '21px', whiteSpace: 'pre-wrap', wordBreak: 'keep-all' };

  if (!isLong) {
    return <span style={base}>{trimmed}</span>;
  }
  if (expanded) {
    return (
      <>
        <span style={base}>{trimmed}</span>
        <button
          onClick={(e) => { e.stopPropagation(); setExpanded(false); }}
          style={{
            all: 'unset', cursor: 'pointer',
            display: 'inline-flex', alignItems: 'center', gap: 3,
            marginTop: 6, fontSize: 13, fontWeight: 600,
            color: TC.textNeutral, letterSpacing: '-0.005em',
          }}>
          접기
          <Icon name="chevron-up" size={14} color={TC.textNeutral} stroke={2.2} />
        </button>
      </>
    );
  }
  return (
    <>
      <span style={{ ...base, display: '-webkit-box', WebkitLineClamp: 1, WebkitBoxOrient: 'vertical', overflow: 'hidden' }}>
        {lines[0]}
      </span>
      <button
        onClick={(e) => { e.stopPropagation(); setExpanded(true); }}
        style={{
          all: 'unset', cursor: 'pointer',
          display: 'inline-flex', alignItems: 'center', gap: 3,
          marginTop: 6, fontSize: 13, fontWeight: 600,
          color: TC.textNeutral, letterSpacing: '-0.005em',
        }}>
        더보기
        <Icon name="chevron-down" size={14} color={TC.textNeutral} stroke={2.2} />
      </button>
    </>
  );
}

Object.assign(window, { DetailModal, InfoRow });
