"use client";

import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import { GoogleLogin } from "@react-oauth/google";
import LoginMenu from "@/components/LoginMenu";
import LanguageSwitcher from "@/components/LanguageSwitcher";
import { api } from "@/lib/api";
import type { Restaurant, Channel, Review, Memo } from "@/lib/api";
import { useAuth } from "@/lib/auth-context";
import MapView, { MapBounds, FlyTo } from "@/components/MapView";
import SearchBar from "@/components/SearchBar";
import RestaurantList from "@/components/RestaurantList";
import RestaurantDetail from "@/components/RestaurantDetail";
import MyReviewsList from "@/components/MyReviewsList";
import BottomSheet from "@/components/BottomSheet";
import FilterSheet, { FilterOption } from "@/components/FilterSheet";
import { getCuisineIcon, getPhosphorCuisineIcon } from "@/lib/cuisine-icons";
import { passesMinMentions } from "@/lib/filters";
import Icon from "@/components/Icon";
import FoodIcon from "@/components/FoodIcon";
import * as PhosphorIcons from "@phosphor-icons/react";

function useDragScroll() {
  const ref = useRef<HTMLDivElement>(null);
  const drag = useRef({ active: false, startX: 0, sl: 0, moved: false });

  const onMouseDown = useCallback((e: React.MouseEvent) => {
    const el = ref.current;
    if (!el) return;
    drag.current = { active: true, startX: e.clientX, sl: el.scrollLeft, moved: false };
    el.style.cursor = "grabbing";
  }, []);

  const onMouseMove = useCallback((e: React.MouseEvent) => {
    const d = drag.current;
    if (!d.active) return;
    const el = ref.current;
    if (!el) return;
    const dx = e.clientX - d.startX;
    if (Math.abs(dx) > 5) d.moved = true;
    el.scrollLeft = d.sl - dx;
  }, []);

  const onMouseUp = useCallback(() => {
    drag.current.active = false;
    const el = ref.current;
    if (el) el.style.cursor = "grab";
  }, []);

  const onClickCapture = useCallback((e: React.MouseEvent) => {
    if (drag.current.moved) {
      e.preventDefault();
      e.stopPropagation();
      drag.current.moved = false;
    }
  }, []);

  return { ref, onMouseDown, onMouseMove, onMouseUp, onMouseLeave: onMouseUp, onClickCapture, style: { cursor: "grab" } as const };
}

const CUISINE_TAXONOMY: { category: string; items: string[] }[] = [
  { category: "한식", items: ["백반/한정식", "국밥/해장국", "찌개/전골/탕", "삼겹살/돼지구이", "소고기/한우구이", "곱창/막창", "닭/오리구이", "족발/보쌈", "회/횟집", "해산물", "분식", "면", "죽/죽집", "순대/순대국", "장어/민물", "주점/포차", "파인다이닝/코스"] },
  { category: "일식", items: ["스시/오마카세", "라멘", "돈카츠", "텐동/튀김", "이자카야", "야키니쿠", "카레", "소바/우동", "파인다이닝/코스"] },
  { category: "중식", items: ["중화요리", "마라/훠궈", "딤섬/만두", "양꼬치", "파인다이닝/코스"] },
  { category: "양식", items: ["파스타/이탈리안", "스테이크", "햄버거", "피자", "프렌치", "바베큐", "브런치", "비건/샐러드", "파인다이닝/코스"] },
  { category: "아시아", items: ["베트남", "태국", "인도/중동", "동남아기타"] },
  { category: "기타", items: ["치킨", "카페/디저트", "베이커리", "뷔페", "퓨전"] },
];

function matchCuisineFilter(cuisineType: string | null, filter: string): boolean {
  if (!cuisineType || !filter) return false;
  // filter can be a category ("한식") or full type ("한식|백반/한정식")
  if (filter.includes("|")) return cuisineType === filter;
  return cuisineType.startsWith(filter);
}

const PRICE_GROUPS: { label: string; test: (p: string) => boolean }[] = [
  {
    label: "저렴 (~5천원)",
    test: (p) => /저렴|착한|[3-5]천원대?$|^\d천원$/.test(p),
  },
  {
    label: "가성비 (5천~1만원)",
    test: (p) => /가성비|만원 이하|[6-9]천원|^1만원대$|^[5-9],?\d{3}원/.test(p),
  },
  {
    label: "보통 (1~3만원)",
    test: (p) => /[1-2]만원대|1-[23]만|인당 [12]\d?,?\d*원|1[2-9],?\d{3}원|2[0-9],?\d{3}원/.test(p),
  },
  {
    label: "프리미엄 (3~5만원)",
    test: (p) => /[3-4]만원대?|3-[45]만|인당 [34]\d?,?\d*원|3[0-9],?\d{3}원|4[0-9],?\d{3}원/.test(p),
  },
  {
    label: "럭셔리 (5만원~)",
    test: (p) => /[5-9]만원|고가|10만원|[1-9]\d만원|인당 [5-9]\d?,?\d*원|5[0-9],?\d{3}원|[6-9][0-9],?\d{3}원/.test(p),
  },
];

function matchPriceGroup(priceRange: string | null, group: string): boolean {
  if (!priceRange) return false;
  const g = PRICE_GROUPS.find((g) => g.label === group);
  if (!g) return false;
  return g.test(priceRange);
}

/** Parse pipe-delimited region "나라|시|구" into parts. */
function parseRegion(region: string | null): { country: string; city: string; district: string } | null {
  if (!region) return null;
  const parts = region.split("|");
  return {
    country: parts[0] || "",
    city: parts[1] || "",
    district: parts[2] || "",
  };
}

/** Build 3-level tree: country → city → district[] */
function buildRegionTree(restaurants: Restaurant[]) {
  const tree = new Map<string, Map<string, Set<string>>>();
  for (const r of restaurants) {
    const p = parseRegion(r.region);
    if (!p || !p.country || p.country === "나라") continue;
    if (!tree.has(p.country)) tree.set(p.country, new Map());
    const cityMap = tree.get(p.country)!;
    if (p.city) {
      if (!cityMap.has(p.city)) cityMap.set(p.city, new Set());
      if (p.district) cityMap.get(p.city)!.add(p.district);
    }
  }
  return tree;
}

/** Compute centroid + appropriate zoom from a set of restaurants. */
function computeFlyTo(rests: Restaurant[]): FlyTo | null {
  if (rests.length === 0) return null;
  const lat = rests.reduce((s, r) => s + r.latitude, 0) / rests.length;
  const lng = rests.reduce((s, r) => s + r.longitude, 0) / rests.length;
  // Pick zoom based on geographic spread
  const latSpread = Math.max(...rests.map((r) => r.latitude)) - Math.min(...rests.map((r) => r.latitude));
  const lngSpread = Math.max(...rests.map((r) => r.longitude)) - Math.min(...rests.map((r) => r.longitude));
  const spread = Math.max(latSpread, lngSpread);
  let zoom = 13;
  if (spread > 2) zoom = 8;
  else if (spread > 1) zoom = 9;
  else if (spread > 0.5) zoom = 10;
  else if (spread > 0.2) zoom = 11;
  else if (spread > 0.1) zoom = 12;
  else if (spread > 0.02) zoom = 14;
  else zoom = 15;
  return { lat, lng, zoom };
}

/** Find best matching country + city from user's coordinates using restaurant data. */
function findRegionFromCoords(
  lat: number,
  lng: number,
  restaurants: Restaurant[],
): { country: string; city: string } | null {
  // Group restaurants by country|city and compute centroids
  const groups = new Map<string, { country: string; city: string; lats: number[]; lngs: number[] }>();
  for (const r of restaurants) {
    const p = parseRegion(r.region);
    if (!p || !p.country || p.country === "나라" || !p.city) continue;
    const key = `${p.country}|${p.city}`;
    if (!groups.has(key)) groups.set(key, { country: p.country, city: p.city, lats: [], lngs: [] });
    const g = groups.get(key)!;
    g.lats.push(r.latitude);
    g.lngs.push(r.longitude);
  }
  let best: { country: string; city: string } | null = null;
  let bestDist = Infinity;
  // #320 — 유클리드 거리는 적도/극지에서 경도·위도의 실거리 차이가 커서 왜곡됨.
  // cos(lat) 가중치(equirectangular approximation)로 위도 의존 보정.
  const cosLat = Math.cos((lat * Math.PI) / 180);
  for (const g of groups.values()) {
    const cLat = g.lats.reduce((a, b) => a + b, 0) / g.lats.length;
    const cLng = g.lngs.reduce((a, b) => a + b, 0) / g.lngs.length;
    const dLat = cLat - lat;
    const dLng = (cLng - lng) * cosLat;
    const dist = dLat * dLat + dLng * dLng;
    if (dist < bestDist) {
      bestDist = dist;
      best = { country: g.country, city: g.city };
    }
  }
  return best;
}

export default function Home() {
  const { user, login, logout, isLoading: authLoading } = useAuth();
  const [restaurants, setRestaurants] = useState<Restaurant[]>([]);
  const [selected, setSelected] = useState<Restaurant | null>(null);
  const [loading, setLoading] = useState(true);
  const [showDetail, setShowDetail] = useState(false);
  const [channels, setChannels] = useState<Channel[]>([]);
  const [channelFilter, setChannelFilter] = useState("");
  const [cuisineFilter, setCuisineFilter] = useState("");
  const [priceFilter, setPriceFilter] = useState("");
  const [minMentions, setMinMentions] = useState(0); // #533 — 의미있는 언급 N회 미만 식당 제외 (0=전체)
  const [viewMode, setViewMode] = useState<"map" | "list">("list");
  const [mobileTab, setMobileTab] = useState<"home" | "list" | "nearby" | "favorites" | "profile">("home");
  const [showMobileFilters, setShowMobileFilters] = useState(false);
  const [mapBounds, setMapBounds] = useState<MapBounds | null>(null);
  const [boundsFilterOn, setBoundsFilterOn] = useState(false);
  const [countryFilter, setCountryFilter] = useState("");
  const [cityFilter, setCityFilter] = useState("");
  const [districtFilter, setDistrictFilter] = useState("");
  const [openSheet, setOpenSheet] = useState<"cuisine" | "price" | "country" | "city" | "district" | null>(null);
  const [regionFlyTo, setRegionFlyTo] = useState<FlyTo | null>(null);
  const [showFavorites, setShowFavorites] = useState(false);
  const [showMyReviews, setShowMyReviews] = useState(false);
  const [myReviews, setMyReviews] = useState<(Review & { restaurant_id: string; restaurant_name: string | null })[]>([]);
  const [myMemos, setMyMemos] = useState<(Memo & { restaurant_name: string | null })[]>([]);
  const [visits, setVisits] = useState<{ today: number; total: number } | null>(null);
  const [userLoc, setUserLoc] = useState<{ lat: number; lng: number }>({ lat: 37.498, lng: 127.0276 });
  const [isSearchResult, setIsSearchResult] = useState(false);
  const [resetCount, setResetCount] = useState(0);
  const geoApplied = useRef(false);
  const dd = useDragScroll();
  const dm = useDragScroll();
  const dg = useDragScroll(); // genre card drag scroll

  const regionTree = useMemo(() => buildRegionTree(restaurants), [restaurants]);
  const countries = useMemo(() => [...regionTree.keys()].sort(), [regionTree]);
  const cities = useMemo(() => {
    if (!countryFilter) return [];
    const cityMap = regionTree.get(countryFilter);
    return cityMap ? [...cityMap.keys()].sort() : [];
  }, [regionTree, countryFilter]);
  const districts = useMemo(() => {
    if (!countryFilter || !cityFilter) return [];
    const cityMap = regionTree.get(countryFilter);
    if (!cityMap) return [];
    const set = cityMap.get(cityFilter);
    return set ? [...set].sort() : [];
  }, [regionTree, countryFilter, cityFilter]);

  const filteredRestaurants = useMemo(() => {
    const dist = (r: Restaurant) =>
      (r.latitude - userLoc.lat) ** 2 + (r.longitude - userLoc.lng) ** 2;
    if (isSearchResult) {
      return [...restaurants].sort((a, b) => {
        const da = dist(a), db = dist(b);
        if (da !== db) return da - db;
        return (b.rating || 0) - (a.rating || 0);
      });
    }
    return restaurants.filter((r) => {
      if (channelFilter && !(r.channels || []).includes(channelFilter)) return false;
      if (cuisineFilter && !matchCuisineFilter(r.cuisine_type, cuisineFilter)) return false;
      if (priceFilter && !matchPriceGroup(r.price_range, priceFilter)) return false;
      if (!passesMinMentions(r.video_count, minMentions)) return false;
      if (countryFilter) {
        const parsed = parseRegion(r.region);
        if (!parsed || parsed.country !== countryFilter) return false;
        if (cityFilter && parsed.city !== cityFilter) return false;
        if (districtFilter && parsed.district !== districtFilter) return false;
      }
      if (boundsFilterOn) {
        if (mapBounds) {
          if (r.latitude < mapBounds.south || r.latitude > mapBounds.north) return false;
          if (r.longitude < mapBounds.west || r.longitude > mapBounds.east) return false;
        } else {
          // 지도가 없으면 (모바일 리스트 탭 등) userLoc 기준 ~2km 반경
          const dlat = r.latitude - userLoc.lat;
          const dlng = r.longitude - userLoc.lng;
          if (dlat * dlat + dlng * dlng > 0.0013) return false; // ~4km
        }
      }
      return true;
    }).sort((a, b) => {
      const da = dist(a), db = dist(b);
      if (da !== db) return da - db;
      return (b.rating || 0) - (a.rating || 0);
    });
  }, [restaurants, isSearchResult, channelFilter, cuisineFilter, priceFilter, minMentions, countryFilter, cityFilter, districtFilter, boundsFilterOn, mapBounds, userLoc]);

  // FilterSheet option builders
  const cuisineOptions = useMemo<FilterOption[]>(() => {
    const opts: FilterOption[] = [];
    for (const g of CUISINE_TAXONOMY) {
      opts.push({ label: `${g.category} 전체`, value: g.category, group: g.category });
      for (const item of g.items) {
        opts.push({ label: item, value: `${g.category}|${item}`, group: g.category });
      }
    }
    return opts;
  }, []);

  const priceOptions = useMemo<FilterOption[]>(() =>
    PRICE_GROUPS.map((g) => ({ label: g.label, value: g.label })),
  []);

  const countryOptions = useMemo<FilterOption[]>(() =>
    countries.map((c) => ({ label: c, value: c })),
  [countries]);

  const cityOptions = useMemo<FilterOption[]>(() =>
    cities.map((c) => ({ label: c, value: c })),
  [cities]);

  const districtOptions = useMemo<FilterOption[]>(() =>
    districts.map((d) => ({ label: d, value: d })),
  [districts]);

  // Set desktop default to map mode on mount + get user location
  useEffect(() => {
    if (window.innerWidth >= 768) setViewMode("map");
    if (navigator.geolocation) {
      navigator.geolocation.getCurrentPosition(
        (pos) => setUserLoc({ lat: pos.coords.latitude, lng: pos.coords.longitude }),
        () => {},
        { timeout: 5000 },
      );
    }
  }, []);

  // Load channels + record visit on mount
  useEffect(() => {
    api.getChannels().then(setChannels).catch(console.error);
    api.recordVisit().then(() => api.getVisits().then(setVisits)).catch(console.error);
  }, []);

  // Load restaurants on mount and when channel filter changes
  useEffect(() => {
    setLoading(true);
    setIsSearchResult(false);
    const params: { limit: number; channel?: string } = { limit: 500 };
    if (channelFilter) params.channel = channelFilter;
    api
      .getRestaurants(params)
      .then(setRestaurants)
      .catch(console.error)
      .finally(() => setLoading(false));
  }, [channelFilter]);

  // Auto-select region from user's geolocation (once)
  useEffect(() => {
    if (geoApplied.current || restaurants.length === 0) return;
    if (!navigator.geolocation) return;
    navigator.geolocation.getCurrentPosition(
      (pos) => {
        if (geoApplied.current) return;
        geoApplied.current = true;
        const match = findRegionFromCoords(pos.coords.latitude, pos.coords.longitude, restaurants);
        if (match) {
          setCountryFilter(match.country);
          setCityFilter(match.city);
        }
        const mobile = window.innerWidth < 768;
        setRegionFlyTo({ lat: pos.coords.latitude, lng: pos.coords.longitude, zoom: mobile ? 13 : 16 });
      },
      () => { /* user denied or error — do nothing */ },
      { timeout: 5000 },
    );
  }, [restaurants]);

  const handleSearch = useCallback(
    async (query: string, mode: "keyword" | "semantic" | "hybrid") => {
      setLoading(true);
      try {
        const results = await api.search(query, mode);
        setRestaurants(results);
        setSelected(null);
        setShowDetail(false);
        setIsSearchResult(true);
        // 검색 시 필터 초기화
        setChannelFilter("");
        setCuisineFilter("");
        setPriceFilter("");
        setMinMentions(0);
        setCountryFilter("");
        setCityFilter("");
        setDistrictFilter("");
        setBoundsFilterOn(false);
        // 검색 결과에 맞게 지도 이동
        const flyTo = computeFlyTo(results);
        if (flyTo) setRegionFlyTo(flyTo);
      } catch (e) {
        console.error("Search failed:", e);
      } finally {
        setLoading(false);
      }
    },
    []
  );

  const handleSelectRestaurant = useCallback((r: Restaurant) => {
    setSelected(r);
    setShowDetail(true);
    // selected 변경 자체가 MapView 내부의 useEffect에서 중앙 이동을 처리.
    // (flyTo를 같이 set하면 줌 16으로 강제되어 클러스터 expansion 후 마커 클릭 시 다시 묶임)
  }, []);

  const handleCloseDetail = useCallback(() => {
    setShowDetail(false);
  }, []);

  const handleBoundsChanged = useCallback((bounds: MapBounds) => {
    setMapBounds(bounds);
  }, []);

  const handleMyLocation = useCallback(() => {
    if (navigator.geolocation) {
      navigator.geolocation.getCurrentPosition(
        (pos) => {
          setUserLoc({ lat: pos.coords.latitude, lng: pos.coords.longitude });
          setRegionFlyTo({ lat: pos.coords.latitude, lng: pos.coords.longitude, zoom: 17 });
        },
        () => setRegionFlyTo({ lat: 37.498, lng: 127.0276, zoom: 16 }),
        { timeout: 5000 },
      );
    } else {
      setRegionFlyTo({ lat: 37.498, lng: 127.0276, zoom: 16 });
    }
  }, []);

  // 검색결과 모드에서 필터 변경 시 검색 결과가 무시되는 결함(#302) 해결.
  // 검색 모드 플래그를 풀고 원본 restaurants를 다시 로드한다.
  const exitSearchMode = useCallback(() => {
    setIsSearchResult(false);
    setResetCount((c) => c + 1);
    api.getRestaurants({ limit: 500 }).then(setRestaurants).catch(() => {});
  }, []);

  const handleCountryChange = useCallback((country: string) => {
    if (isSearchResult) exitSearchMode();
    setCountryFilter(country);
    setCityFilter("");
    setDistrictFilter("");
    setBoundsFilterOn(false);
    if (!country) { setRegionFlyTo(null); return; }
    const matched = restaurants.filter((r) => {
      const p = parseRegion(r.region);
      return p && p.country === country;
    });
    setRegionFlyTo(computeFlyTo(matched));
  }, [restaurants, isSearchResult, exitSearchMode]);

  const handleCityChange = useCallback((city: string) => {
    if (isSearchResult) exitSearchMode();
    setCityFilter(city);
    setDistrictFilter("");
    if (!city) {
      // Re-fly to country level
      const matched = restaurants.filter((r) => {
        const p = parseRegion(r.region);
        return p && p.country === countryFilter;
      });
      setRegionFlyTo(computeFlyTo(matched));
      return;
    }
    const matched = restaurants.filter((r) => {
      const p = parseRegion(r.region);
      return p && p.country === countryFilter && p.city === city;
    });
    setRegionFlyTo(computeFlyTo(matched));
  }, [restaurants, countryFilter, isSearchResult, exitSearchMode]);

  const handleDistrictChange = useCallback((district: string) => {
    if (isSearchResult) exitSearchMode();
    setDistrictFilter(district);
    if (!district) {
      const matched = restaurants.filter((r) => {
        const p = parseRegion(r.region);
        return p && p.country === countryFilter && p.city === cityFilter;
      });
      setRegionFlyTo(computeFlyTo(matched));
      return;
    }
    const matched = restaurants.filter((r) => {
      const p = parseRegion(r.region);
      return p && p.country === countryFilter && p.city === cityFilter && p.district === district;
    });
    setRegionFlyTo(computeFlyTo(matched));
  }, [restaurants, countryFilter, cityFilter, isSearchResult, exitSearchMode]);

  const handleReset = useCallback(() => {
    setLoading(true);
    setChannelFilter("");
    setCuisineFilter("");
    setPriceFilter("");
    setMinMentions(0);
    setCountryFilter("");
    setCityFilter("");
    setDistrictFilter("");
    setRegionFlyTo(null);
    setBoundsFilterOn(false);
    setShowFavorites(false);
    setShowMyReviews(false);
    setIsSearchResult(false);
    setResetCount((c) => c + 1);
    api
      .getRestaurants({ limit: 500 })
      .then((data) => {
        setRestaurants(data);
        setSelected(null);
        setShowDetail(false);
      })
      .catch(console.error)
      .finally(() => setLoading(false));
  }, []);

  const handleMobileTab = useCallback(async (tab: "home" | "list" | "nearby" | "favorites" | "profile") => {
    // 홈 탭 재클릭 = 리셋
    if (tab === "home" && mobileTab === "home") {
      handleReset();
      return;
    }

    setMobileTab(tab);
    setShowDetail(false);
    setShowMobileFilters(false);
    setSelected(null);

    if (tab === "nearby") {
      setBoundsFilterOn(true);
      if (navigator.geolocation) {
        navigator.geolocation.getCurrentPosition(
          (pos) => setRegionFlyTo({ lat: pos.coords.latitude, lng: pos.coords.longitude, zoom: 13 }),
          () => setRegionFlyTo({ lat: 37.498, lng: 127.0276, zoom: 13 }),
          { timeout: 5000 },
        );
      } else {
        setRegionFlyTo({ lat: 37.498, lng: 127.0276, zoom: 13 });
      }
      // 내주변에서 돌아올 때를 위해 favorites/reviews 해제
      if (showFavorites || showMyReviews) {
        setShowFavorites(false);
        setShowMyReviews(false);
        setMyReviews([]);
        api.getRestaurants({ limit: 500 }).then(setRestaurants);
      }
      return;
    }

    setBoundsFilterOn(false);

    if (tab === "favorites") {
      if (!user) return;
      setShowMyReviews(false);
      setMyReviews([]);
      try {
        const favs = await api.getMyFavorites();
        setRestaurants(favs);
        setShowFavorites(true);
      } catch { /* ignore */ }
    } else if (tab === "profile") {
      if (!user) return;
      setShowFavorites(false);
      try {
        const reviews = await api.getMyReviews();
        setMyReviews(reviews);
        setShowMyReviews(true);
      } catch { /* ignore */ }
      // 프로필에서는 식당 목록을 원래대로 복원
      if (showFavorites) {
        api.getRestaurants({ limit: 500 }).then(setRestaurants);
      }
    } else {
      // 홈 / 식당 목록 - 항상 전체 식당 목록으로 복원
      const needReload = showFavorites || showMyReviews;
      setShowFavorites(false);
      setShowMyReviews(false);
      setMyReviews([]);
      if (needReload) {
        const data = await api.getRestaurants({ limit: 500 });
        setRestaurants(data);
      }
    }
  }, [user, showFavorites, showMyReviews, mobileTab, handleReset]);

  const handleToggleFavorites = async () => {
    if (showFavorites) {
      setShowFavorites(false);
      const data = await api.getRestaurants({ limit: 500 });
      setRestaurants(data);
    } else {
      try {
        const favs = await api.getMyFavorites();
        setRestaurants(favs);
        setShowFavorites(true);
        setShowMyReviews(false);
        setMyReviews([]);
        setSelected(null);
        setShowDetail(false);
      } catch { /* ignore */ }
    }
  };

  const handleToggleMyReviews = async () => {
    if (showMyReviews) {
      setShowMyReviews(false);
      setMyReviews([]);
      setMyMemos([]);
    } else {
      try {
        const [reviews, memos] = await Promise.all([
          api.getMyReviews(),
          api.getMyMemos(),
        ]);
        setMyReviews(reviews);
        setMyMemos(memos);
        setShowMyReviews(true);
        setShowFavorites(false);
        setSelected(null);
        setShowDetail(false);
      } catch { /* ignore */ }
    }
  };

  // Desktop sidebar: shows detail inline
  const sidebarContent = showMyReviews ? (
    <MyReviewsList
      reviews={myReviews}
      memos={myMemos}
      onClose={() => { setShowMyReviews(false); setMyReviews([]); setMyMemos([]); }}
      onSelectRestaurant={async (restaurantId) => {
        try {
          const r = await api.getRestaurant(restaurantId);
          handleSelectRestaurant(r);
          setShowMyReviews(false);
          setMyReviews([]);
        } catch { /* ignore */ }
      }}
    />
  ) : showDetail && selected ? (
    <RestaurantDetail
      restaurant={selected}
      onClose={handleCloseDetail}
    />
  ) : (
    <RestaurantList
      restaurants={filteredRestaurants}
      selectedId={selected?.id}
      onSelect={handleSelectRestaurant}
      loading={loading}
      keyPrefix="d-"
    />
  );

  // Mobile list: always shows list (detail goes to bottom sheet)
  const mobileListContent = showMyReviews ? (
    <MyReviewsList
      reviews={myReviews}
      memos={myMemos}
      onClose={() => { setShowMyReviews(false); setMyReviews([]); setMyMemos([]); }}
      onSelectRestaurant={async (restaurantId) => {
        try {
          const r = await api.getRestaurant(restaurantId);
          handleSelectRestaurant(r);
          setShowMyReviews(false);
          setMyReviews([]);
        } catch { /* ignore */ }
      }}
    />
  ) : (
    <RestaurantList
      restaurants={filteredRestaurants}
      selectedId={selected?.id}
      onSelect={handleSelectRestaurant}
      loading={loading}
      keyPrefix="m-"
    />
  );

  return (
    <div className="h-screen flex flex-col bg-background">
      {/* ── Header row 1: Logo + User ── */}
      <header className="bg-surface/80 backdrop-blur-md border-b dark:border-gray-800 shrink-0">
        <div className="px-5 py-3 flex items-center justify-between">
          <button onClick={handleReset} className="shrink-0">
            <img src="/logo-80h.png" alt="Tasteby" className="h-7 md:h-8" />
          </button>

          {/* Desktop: search + filters */}
          <div className="hidden md:flex flex-col gap-2 mx-6 flex-1 min-w-0">
            {/* Row 1: Search + actions */}
            <div className="flex items-center gap-2">
              <div className="w-80 shrink-0">
                <SearchBar key={resetCount} onSearch={handleSearch} isLoading={loading} />
              </div>
              <button
                onClick={handleReset}
                className="p-1.5 text-gray-500 dark:text-gray-500 hover:text-brand-500 dark:hover:text-brand-400 transition-colors"
                title="초기화"
              >
                <Icon name="refresh" size={18} />
              </button>
              <span className="text-xs text-gray-500 dark:text-gray-500 tabular-nums">
                {filteredRestaurants.length}개
              </span>
              <div className="w-px h-4 bg-gray-200 dark:bg-gray-700" />
              <button
                onClick={() => setViewMode(viewMode === "map" ? "list" : "map")}
                className={`px-2.5 py-1 text-xs rounded-lg border transition-colors ${
                  viewMode === "map"
                    ? "bg-blue-50 dark:bg-blue-900/30 border-blue-300 dark:border-blue-700 text-blue-600 dark:text-blue-400"
                    : "border-gray-200 dark:border-gray-700 text-gray-500 dark:text-gray-400 hover:border-blue-300 hover:text-blue-500"
                }`}
              >
                <Icon name={viewMode === "map" ? "map" : "list"} size={14} className="mr-0.5" />{viewMode === "map" ? "지도우선" : "목록우선"}
              </button>
              {user && (
                <>
                  <button
                    onClick={handleToggleFavorites}
                    className={`px-2.5 py-1 text-xs rounded-lg border transition-colors ${
                      showFavorites
                        ? "bg-rose-50 dark:bg-rose-900/30 border-rose-300 dark:border-rose-700 text-rose-600 dark:text-rose-400"
                        : "border-gray-200 dark:border-gray-700 text-gray-500 dark:text-gray-400 hover:border-rose-300 hover:text-rose-500"
                    }`}
                  >
                    <Icon name="favorite" size={14} filled={showFavorites} className="mr-0.5" />{showFavorites ? "내 찜" : "찜"}
                  </button>
                  <button
                    onClick={handleToggleMyReviews}
                    className={`px-2.5 py-1 text-xs rounded-lg border transition-colors ${
                      showMyReviews
                        ? "bg-brand-50 dark:bg-brand-900/30 border-brand-300 dark:border-brand-700 text-brand-600 dark:text-brand-400"
                        : "border-gray-200 dark:border-gray-700 text-gray-500 dark:text-gray-400 hover:border-brand-300 hover:text-brand-500"
                    }`}
                  >
                    {showMyReviews ? "✎ 내 리뷰" : "✎ 리뷰"}
                  </button>
                </>
              )}
              <div className="flex-1" />
              {/* #352 — Language switcher */}
              <LanguageSwitcher />
              {/* Desktop user area */}
              {authLoading ? null : user ? (
                <div className="flex items-center gap-2 shrink-0">
                  {user.avatar_url ? (
                    <img src={user.avatar_url} alt="" className="w-7 h-7 rounded-full border border-gray-200" />
                  ) : (
                    <div className="w-7 h-7 rounded-full bg-brand-100 text-brand-700 flex items-center justify-center text-xs font-semibold border border-brand-200">
                      {(user.nickname || user.email || "?").charAt(0).toUpperCase()}
                    </div>
                  )}
                  <span className="text-xs font-medium text-gray-600 dark:text-gray-300 max-w-[80px] truncate">
                    {user.nickname || user.email}
                  </span>
                  <button
                    onClick={logout}
                    className="px-2 py-0.5 text-[10px] text-gray-500 dark:text-gray-500 border border-gray-200 dark:border-gray-700 rounded-full hover:bg-gray-100 dark:hover:bg-gray-800 hover:text-gray-600 transition-colors"
                  >
                    로그아웃
                  </button>
                </div>
              ) : (
                <div className="shrink-0">
                  <LoginMenu onGoogleSuccess={(credential) => login(credential).catch(console.error)} />
                </div>
              )}
            </div>
            {/* Row 2: Channel cards (toggle filter) */}
            {!isSearchResult && channels.length > 0 && (
              <div ref={dd.ref} onMouseDown={dd.onMouseDown} onMouseMove={dd.onMouseMove} onMouseUp={dd.onMouseUp} onMouseLeave={dd.onMouseLeave} onClickCapture={dd.onClickCapture} style={dd.style} className="overflow-x-auto scrollbar-hide select-none">
                <div className="flex gap-2">
                  {channels.map((ch) => (
                    <button
                      key={ch.id}
                      onClick={() => {
                        setChannelFilter(channelFilter === ch.channel_name ? "" : ch.channel_name);
                        setSelected(null);
                        setShowDetail(false);
                      }}
                      className={`shrink-0 flex items-center gap-2 rounded-lg px-3 py-1.5 border transition-all text-left ${
                        channelFilter === ch.channel_name
                          ? "bg-brand-50 dark:bg-brand-900/30 border-brand-300 dark:border-brand-700"
                          : "bg-white dark:bg-gray-800 border-gray-200 dark:border-gray-700 hover:border-brand-200 dark:hover:border-brand-800"
                      }`}
                      style={{ width: "200px" }}
                    >
                      <Icon name="play_circle" size={16} filled className="shrink-0 text-red-500" />
                      <div className="min-w-0 flex-1">
                        <p className={`text-xs font-semibold truncate ${
                          channelFilter === ch.channel_name ? "text-brand-600 dark:text-brand-400" : "dark:text-gray-200"
                        }`}>{ch.channel_name}</p>
                        {ch.description && <p className="text-[10px] text-gray-500 dark:text-gray-500 truncate">{ch.description}</p>}
                      </div>
                    </button>
                  ))}
                </div>
              </div>
            )}
            {/* Row 3: Filters — grouped by category */}
            <div className="flex items-center gap-1.5 text-xs">
              {/* 음식 필터 그룹 */}
              <div className="flex items-center gap-1.5 bg-gray-50 dark:bg-gray-800/50 rounded-lg px-2 py-1">
                <span className="text-gray-500 dark:text-gray-500 text-[10px] font-medium shrink-0">음식</span>
                <select
                  value={cuisineFilter}
                  onChange={(e) => { setCuisineFilter(e.target.value); if (e.target.value) setBoundsFilterOn(false); }}
                  className={`bg-transparent border-none outline-none cursor-pointer pr-1 ${
                    cuisineFilter ? "text-brand-600 dark:text-brand-400 font-medium" : "text-gray-500 dark:text-gray-400"
                  }`}
                >
                  <option value="">장르</option>
                  {CUISINE_TAXONOMY.map((g) => (
                    <optgroup key={g.category} label={`── ${g.category} ──`}>
                      <option value={g.category}>{g.category} 전체</option>
                      {g.items.map((item) => (
                        <option key={`${g.category}|${item}`} value={`${g.category}|${item}`}>
                          &nbsp;&nbsp;{item}
                        </option>
                      ))}
                    </optgroup>
                  ))}
                </select>
                <div className="w-px h-3 bg-gray-200 dark:bg-gray-700" />
                <select
                  value={priceFilter}
                  onChange={(e) => { setPriceFilter(e.target.value); if (e.target.value) setBoundsFilterOn(false); }}
                  className={`bg-transparent border-none outline-none cursor-pointer pr-1 ${
                    priceFilter ? "text-brand-600 dark:text-brand-400 font-medium" : "text-gray-500 dark:text-gray-400"
                  }`}
                >
                  <option value="">가격</option>
                  {PRICE_GROUPS.map((g) => (
                    <option key={g.label} value={g.label}>{g.label}</option>
                  ))}
                </select>
                <div className="w-px h-3 bg-gray-200 dark:bg-gray-700" />
                <select
                  value={minMentions}
                  onChange={(e) => { setMinMentions(Number(e.target.value)); if (Number(e.target.value)) setBoundsFilterOn(false); }}
                  className={`bg-transparent border-none outline-none cursor-pointer pr-1 ${
                    minMentions > 0 ? "text-brand-600 dark:text-brand-400 font-medium" : "text-gray-500 dark:text-gray-400"
                  }`}
                  title="영상에서 충분히 언급된(관련도 높은) 식당만 보기"
                >
                  <option value={0}>언급</option>
                  <option value={2}>2회+</option>
                  <option value={3}>3회+</option>
                  <option value={5}>5회+</option>
                </select>
                {(cuisineFilter || priceFilter || minMentions > 0) && (
                  <button
                    onClick={() => { setCuisineFilter(""); setPriceFilter(""); setMinMentions(0); }}
                    className="p-1.5 -mr-1 text-gray-400 hover:text-brand-500 transition-colors touch-manipulation"
                    title="음식 필터 초기화"
                  >
                    <Icon name="close" size={14} />
                  </button>
                )}
              </div>
              {/* 지역 필터 그룹 */}
              <div className="flex items-center gap-1.5 bg-gray-50 dark:bg-gray-800/50 rounded-lg px-2 py-1">
                <span className="text-gray-500 dark:text-gray-500 text-[10px] font-medium shrink-0">지역</span>
                <select
                  value={countryFilter}
                  onChange={(e) => handleCountryChange(e.target.value)}
                  className={`bg-transparent border-none outline-none cursor-pointer pr-1 ${
                    countryFilter ? "text-brand-600 dark:text-brand-400 font-medium" : "text-gray-500 dark:text-gray-400"
                  }`}
                >
                  <option value="">나라</option>
                  {countries.map((c) => (
                    <option key={c} value={c}>{c}</option>
                  ))}
                </select>
                {countryFilter && cities.length > 0 && (
                  <>
                    <div className="w-px h-3 bg-gray-200 dark:bg-gray-700" />
                    <select
                      value={cityFilter}
                      onChange={(e) => handleCityChange(e.target.value)}
                      className={`bg-transparent border-none outline-none cursor-pointer pr-1 ${
                        cityFilter ? "text-brand-600 dark:text-brand-400 font-medium" : "text-gray-500 dark:text-gray-400"
                      }`}
                    >
                      <option value="">시/도</option>
                      {cities.map((c) => (
                        <option key={c} value={c}>{c}</option>
                      ))}
                    </select>
                  </>
                )}
                {cityFilter && districts.length > 0 && (
                  <>
                    <div className="w-px h-3 bg-gray-200 dark:bg-gray-700" />
                    <select
                      value={districtFilter}
                      onChange={(e) => handleDistrictChange(e.target.value)}
                      className={`bg-transparent border-none outline-none cursor-pointer pr-1 ${
                        districtFilter ? "text-brand-600 dark:text-brand-400 font-medium" : "text-gray-500 dark:text-gray-400"
                      }`}
                    >
                      <option value="">구/군</option>
                      {districts.map((d) => (
                        <option key={d} value={d}>{d}</option>
                      ))}
                    </select>
                  </>
                )}
                {countryFilter && (
                  <button
                    onClick={() => { setCountryFilter(""); setCityFilter(""); setDistrictFilter(""); setRegionFlyTo(null); }}
                    className="p-1.5 -mr-1 text-gray-400 hover:text-brand-500 transition-colors touch-manipulation"
                    title="지역 필터 초기화"
                  >
                    <Icon name="close" size={14} />
                  </button>
                )}
              </div>
              {/* 필터 전체 해제 */}
              {(channelFilter || cuisineFilter || priceFilter || countryFilter) && (
                <button
                  onClick={() => {
                    setChannelFilter("");
                    setCuisineFilter("");
                    setPriceFilter("");
                    setCountryFilter("");
                    setCityFilter("");
                    setDistrictFilter("");
                    setRegionFlyTo(null);
                  }}
                  className="flex items-center gap-1 rounded-lg px-2.5 py-1.5 bg-gray-50 dark:bg-gray-800/50 text-gray-500 dark:text-gray-400 hover:text-brand-500 transition-colors touch-manipulation"
                >
                  <Icon name="close" size={14} />
                  <span>전체보기</span>
                </button>
              )}
              {/* 내위치 토글 */}
              <button
                onClick={() => {
                  const next = !boundsFilterOn;
                  setBoundsFilterOn(next);
                  if (next) {
                    // 내위치 ON 시 다른 필터 초기화
                    setCuisineFilter("");
                    setPriceFilter("");
                    setCountryFilter("");
                    setCityFilter("");
                    setDistrictFilter("");
                    if (navigator.geolocation) {
                      navigator.geolocation.getCurrentPosition(
                        (pos) => { setUserLoc({ lat: pos.coords.latitude, lng: pos.coords.longitude }); setRegionFlyTo({ lat: pos.coords.latitude, lng: pos.coords.longitude, zoom: 15 }); },
                        () => setRegionFlyTo({ lat: 37.498, lng: 127.0276, zoom: 15 }),
                        { timeout: 5000 },
                      );
                    } else {
                      setRegionFlyTo({ lat: 37.498, lng: 127.0276, zoom: 15 });
                    }
                  }
                }}
                className={`flex items-center gap-1 rounded-lg px-2 py-1 transition-colors ${
                  boundsFilterOn
                    ? "bg-brand-50 dark:bg-brand-900/30 text-brand-600 dark:text-brand-400"
                    : "bg-gray-50 dark:bg-gray-800/50 text-gray-500 dark:text-gray-400 hover:text-brand-500"
                }`}
                title="내 위치 주변 식당만 표시"
              >
                <Icon name="location_on" size={14} />
                <span>{boundsFilterOn ? "내위치 ON" : "내위치"}</span>
              </button>
            </div>
          </div>

          {/* User area (mobile only - desktop moved to Row 1) */}
          <div className="shrink-0 flex items-center gap-2 ml-auto md:hidden">
            {user && (
              <>
                <button
                  onClick={handleToggleFavorites}
                  className={`px-2 py-0.5 text-[10px] rounded-full border transition-colors ${
                    showFavorites
                      ? "bg-rose-50 dark:bg-rose-900/30 border-rose-300 dark:border-rose-700 text-rose-600 dark:text-rose-400"
                      : "border-gray-200 dark:border-gray-700 text-gray-500 dark:text-gray-400"
                  }`}
                >
                  <Icon name="favorite" size={14} filled={showFavorites} className="mr-0.5" />{showFavorites ? "찜" : "찜"}
                </button>
                <button
                  onClick={handleToggleMyReviews}
                  className={`px-2 py-0.5 text-[10px] rounded-full border transition-colors ${
                    showMyReviews
                      ? "bg-brand-50 dark:bg-brand-900/30 border-brand-300 dark:border-brand-700 text-brand-600 dark:text-brand-400"
                      : "border-gray-200 dark:border-gray-700 text-gray-500 dark:text-gray-400"
                  }`}
                >
                  {showMyReviews ? "✎ 리뷰" : "✎ 리뷰"}
                </button>
              </>
            )}
            {authLoading ? null : user ? (
              <div className="flex items-center">
                {user.avatar_url ? (
                  <img src={user.avatar_url} alt="" className="w-7 h-7 rounded-full border border-gray-200" />
                ) : (
                  <div className="w-7 h-7 rounded-full bg-brand-100 text-brand-700 flex items-center justify-center text-xs font-semibold border border-brand-200">
                    {(user.nickname || user.email || "?").charAt(0).toUpperCase()}
                  </div>
                )}
              </div>
            ) : (
              <LoginMenu onGoogleSuccess={(credential) => login(credential).catch(console.error)} />
            )}
          </div>
        </div>

        {/* ── Header row 2 (mobile only): search + toolbar ── */}
        <div className={`md:hidden px-4 pb-3 space-y-2 ${mobileTab === "favorites" || mobileTab === "profile" ? "hidden" : ""}`}>
          {/* Row 1: Search */}
          <SearchBar key={resetCount} onSearch={handleSearch} isLoading={loading} />
          {/* Channel cards - toggle filter */}
          {(mobileTab === "home" || mobileTab === "list" || mobileTab === "nearby") && !isSearchResult && channels.length > 0 && (
            <div ref={dm.ref} onMouseDown={dm.onMouseDown} onMouseMove={dm.onMouseMove} onMouseUp={dm.onMouseUp} onMouseLeave={dm.onMouseLeave} onClickCapture={dm.onClickCapture} style={dm.style} className="flex gap-2 overflow-x-auto pb-1 -mx-1 px-1 scrollbar-hide select-none">
              {channels.map((ch) => (
                <button
                  key={ch.id}
                  onClick={() => {
                    setChannelFilter(channelFilter === ch.channel_name ? "" : ch.channel_name);
                    setSelected(null);
                    setShowDetail(false);
                  }}
                  className={`shrink-0 rounded-xl px-3 py-2 text-left border transition-all ${
                    channelFilter === ch.channel_name
                      ? "bg-brand-50 dark:bg-brand-900/30 border-brand-300 dark:border-brand-700"
                      : "bg-white dark:bg-gray-800 border-gray-200 dark:border-gray-700"
                  }`}
                  style={{ minWidth: "140px", maxWidth: "170px" }}
                >
                  <div className="flex items-center gap-1.5">
                    <Icon name="play_circle" size={14} filled className="shrink-0 text-red-500" />
                    <p className={`text-xs font-semibold truncate ${
                      channelFilter === ch.channel_name ? "text-brand-600 dark:text-brand-400" : "dark:text-gray-200"
                    }`}>{ch.channel_name}</p>
                  </div>
                  {ch.description && <p className="text-[10px] text-gray-500 dark:text-gray-400 truncate mt-0.5">{ch.description}</p>}
                  {ch.tags && (
                    <div className="flex gap-1 mt-1 overflow-hidden">
                      {ch.tags.split(",").slice(0, 2).map((t) => (
                        <span key={t} className="text-[9px] px-1.5 py-0.5 bg-gray-100 dark:bg-gray-700 text-gray-500 dark:text-gray-400 rounded-full whitespace-nowrap">{t.trim()}</span>
                      ))}
                    </div>
                  )}
                </button>
              ))}
            </div>
          )}

          {/* Row 2: Filters */}
          <div className="space-y-1.5">
            {/* Home tab: 장르 가로 스크롤 */}
            {mobileTab === "home" && (
              <div ref={dg.ref} onMouseDown={dg.onMouseDown} onMouseMove={dg.onMouseMove} onMouseUp={dg.onMouseUp} onMouseLeave={dg.onMouseLeave} onClickCapture={dg.onClickCapture} style={dg.style} className="flex gap-2 overflow-x-auto scrollbar-hide -mx-1 px-1 pb-1 select-none">
                {(() => {
                  const allCards = [
                    { label: "전체", value: "", icon: "ForkKnife" },
                    ...CUISINE_TAXONOMY.flatMap((g) => [
                      { label: g.category, value: g.category, icon: getPhosphorCuisineIcon(g.category) },
                      ...g.items.map((item) => ({ label: item, value: `${g.category}|${item}`, icon: getPhosphorCuisineIcon(`${g.category}|${item}`) })),
                    ]),
                  ];
                  return allCards.map((card) => {
                    const isCategory = card.value === "" || !card.value.includes("|");
                    const selected = card.value === ""
                      ? !cuisineFilter
                      : isCategory
                        ? cuisineFilter === card.value || cuisineFilter.startsWith(card.value + "|")
                        : cuisineFilter === card.value;
                    const PhIcon = (PhosphorIcons as unknown as Record<string, React.ComponentType<{ size?: number; weight?: string; className?: string }>>)[card.icon] || PhosphorIcons.ForkKnife;
                    return (
                      <button
                        key={card.value || "__all__"}
                        onClick={() => {
                          if (card.value === "") { setCuisineFilter(""); }
                          else if (cuisineFilter === card.value) { setCuisineFilter(""); }
                          else { setCuisineFilter(card.value); setBoundsFilterOn(false); }
                        }}
                        className={`shrink-0 flex flex-col items-center gap-1 rounded-xl px-2.5 py-2 min-w-[56px] transition-all ${
                          selected
                            ? "bg-brand-500 text-white shadow-sm"
                            : isCategory
                              ? "bg-brand-50 border border-brand-200 text-brand-700"
                              : "bg-white border border-gray-100 text-gray-500"
                        }`}
                      >
                        {card.icon.startsWith("food:") ? (
                          <FoodIcon name={card.icon.slice(5)} size={22} className={selected ? "text-white" : isCategory ? "text-brand-500" : "text-gray-400"} />
                        ) : (
                          <PhIcon size={22} className={selected ? "text-white" : isCategory ? "text-brand-500" : "text-gray-400"} />
                        )}
                        <span className={`text-[11px] whitespace-nowrap ${isCategory ? "font-semibold" : "font-medium"}`}>{card.label}</span>
                      </button>
                    );
                  });
                })()}
              </div>
            )}
            {/* Home tab: 가격 + 지역 + 내위치 + 개수 */}
            {mobileTab === "home" && (
              <div className="flex items-center gap-1.5 text-xs flex-wrap">
                <button
                  onClick={() => setOpenSheet("price")}
                  className={`inline-flex items-center rounded-full px-3 py-1.5 ${
                    priceFilter ? "bg-brand-50 dark:bg-brand-900/30 ring-1 ring-brand-300 dark:ring-brand-700" : "bg-gray-100 dark:bg-gray-800"
                  }`}
                >
                  <Icon name="payments" size={14} className={`mr-1 ${priceFilter ? "text-brand-500" : "text-gray-400"}`} />
                  <span className={priceFilter ? "text-brand-600 dark:text-brand-400 font-medium" : "text-gray-500 dark:text-gray-400"}>
                    {priceFilter || "가격"}
                  </span>
                  <Icon name="expand_more" size={14} className="ml-0.5 text-gray-400" />
                </button>
                <button
                  onClick={() => setOpenSheet("country")}
                  className={`inline-flex items-center rounded-full px-3 py-1.5 ${
                    countryFilter ? "bg-brand-50 dark:bg-brand-900/30 ring-1 ring-brand-300 dark:ring-brand-700" : "bg-gray-100 dark:bg-gray-800"
                  }`}
                >
                  <Icon name="public" size={14} className={`mr-1 ${countryFilter ? "text-brand-500" : "text-gray-400"}`} />
                  <span className={countryFilter ? "text-brand-600 dark:text-brand-400 font-medium" : "text-gray-500 dark:text-gray-400"}>
                    {countryFilter || "나라"}
                  </span>
                  <Icon name="expand_more" size={14} className="ml-0.5 text-gray-400" />
                </button>
                {countryFilter && cities.length > 0 && (
                  <button
                    onClick={() => setOpenSheet("city")}
                    className={`inline-flex items-center rounded-full px-3 py-1.5 ${
                      cityFilter ? "bg-brand-50 dark:bg-brand-900/30 ring-1 ring-brand-300 dark:ring-brand-700" : "bg-gray-100 dark:bg-gray-800"
                    }`}
                  >
                    <span className={cityFilter ? "text-brand-600 dark:text-brand-400 font-medium" : "text-gray-500 dark:text-gray-400"}>
                      {cityFilter || "시/도"}
                    </span>
                    <Icon name="expand_more" size={14} className="ml-0.5 text-gray-400" />
                  </button>
                )}
                {cityFilter && districts.length > 0 && (
                  <button
                    onClick={() => setOpenSheet("district")}
                    className={`inline-flex items-center rounded-full px-3 py-1.5 ${
                      districtFilter ? "bg-brand-50 dark:bg-brand-900/30 ring-1 ring-brand-300 dark:ring-brand-700" : "bg-gray-100 dark:bg-gray-800"
                    }`}
                  >
                    <span className={districtFilter ? "text-brand-600 dark:text-brand-400 font-medium" : "text-gray-500 dark:text-gray-400"}>
                      {districtFilter || "구/군"}
                    </span>
                    <Icon name="expand_more" size={14} className="ml-0.5 text-gray-400" />
                  </button>
                )}
                {(cuisineFilter || priceFilter || countryFilter) && (
                  <button onClick={() => { setCuisineFilter(""); setPriceFilter(""); setCountryFilter(""); setCityFilter(""); setDistrictFilter(""); }} className="text-gray-400 hover:text-brand-500">
                    <Icon name="close" size={14} />
                  </button>
                )}
                <button
                  onClick={() => {
                    const next = !boundsFilterOn;
                    setBoundsFilterOn(next);
                    if (next) {
                      setCuisineFilter("");
                      setPriceFilter("");
                      setCountryFilter("");
                      setCityFilter("");
                      setDistrictFilter("");
                      if (navigator.geolocation) {
                        navigator.geolocation.getCurrentPosition(
                          (pos) => { setUserLoc({ lat: pos.coords.latitude, lng: pos.coords.longitude }); setRegionFlyTo({ lat: pos.coords.latitude, lng: pos.coords.longitude, zoom: 15 }); },
                          () => setRegionFlyTo({ lat: 37.498, lng: 127.0276, zoom: 15 }),
                          { timeout: 5000 },
                        );
                      } else {
                        setRegionFlyTo({ lat: 37.498, lng: 127.0276, zoom: 15 });
                      }
                    }
                  }}
                  className={`inline-flex items-center gap-0.5 rounded-full px-3 py-1.5 transition-colors ${
                    boundsFilterOn
                      ? "bg-brand-50 dark:bg-brand-900/30 ring-1 ring-brand-300 dark:ring-brand-700 text-brand-600 dark:text-brand-400"
                      : "bg-gray-100 dark:bg-gray-800 text-gray-500 dark:text-gray-400"
                  }`}
                >
                  <Icon name="location_on" size={14} />
                  <span>{boundsFilterOn ? "내위치 ON" : "내위치"}</span>
                </button>
                <span className="text-[10px] text-gray-400 ml-auto tabular-nums">{filteredRestaurants.length}개</span>
              </div>
            )}
            {/* List tab: 기존 필터 UI */}
            {mobileTab === "list" && (
              <>
                <div className="flex items-center gap-1.5 text-xs">
                  <button
                    onClick={() => setOpenSheet("cuisine")}
                    className={`inline-flex items-center rounded-full px-3 py-1.5 ${
                      cuisineFilter ? "bg-brand-50 dark:bg-brand-900/30 ring-1 ring-brand-300 dark:ring-brand-700" : "bg-gray-100 dark:bg-gray-800"
                    }`}
                  >
                    <Icon name="restaurant" size={14} className={`mr-1 ${cuisineFilter ? "text-brand-500" : "text-gray-400"}`} />
                    <span className={cuisineFilter ? "text-brand-600 dark:text-brand-400 font-medium" : "text-gray-500 dark:text-gray-400"}>
                      {cuisineFilter ? (cuisineFilter.includes("|") ? cuisineFilter.split("|")[1] : cuisineFilter) : "장르"}
                    </span>
                    <Icon name="expand_more" size={14} className="ml-0.5 text-gray-400" />
                  </button>
                  <button
                    onClick={() => setOpenSheet("price")}
                    className={`inline-flex items-center rounded-full px-3 py-1.5 ${
                      priceFilter ? "bg-brand-50 dark:bg-brand-900/30 ring-1 ring-brand-300 dark:ring-brand-700" : "bg-gray-100 dark:bg-gray-800"
                    }`}
                  >
                    <Icon name="payments" size={14} className={`mr-1 ${priceFilter ? "text-brand-500" : "text-gray-400"}`} />
                    <span className={priceFilter ? "text-brand-600 dark:text-brand-400 font-medium" : "text-gray-500 dark:text-gray-400"}>
                      {priceFilter || "가격"}
                    </span>
                    <Icon name="expand_more" size={14} className="ml-0.5 text-gray-400" />
                  </button>
                  {(cuisineFilter || priceFilter) && (
                    <button onClick={() => { setCuisineFilter(""); setPriceFilter(""); }} className="text-gray-400 hover:text-brand-500">
                      <Icon name="close" size={14} />
                    </button>
                  )}
                  <span className="text-[10px] text-gray-400 ml-auto tabular-nums">{filteredRestaurants.length}개</span>
                </div>
                <div className="flex items-center gap-1.5 text-xs">
                  <button
                    onClick={() => setOpenSheet("country")}
                    className={`inline-flex items-center rounded-full px-3 py-1.5 ${
                      countryFilter ? "bg-brand-50 dark:bg-brand-900/30 ring-1 ring-brand-300 dark:ring-brand-700" : "bg-gray-100 dark:bg-gray-800"
                    }`}
                  >
                    <Icon name="public" size={14} className={`mr-1 ${countryFilter ? "text-brand-500" : "text-gray-400"}`} />
                    <span className={countryFilter ? "text-brand-600 dark:text-brand-400 font-medium" : "text-gray-500 dark:text-gray-400"}>
                      {countryFilter || "나라"}
                    </span>
                    <Icon name="expand_more" size={14} className="ml-0.5 text-gray-400" />
                  </button>
                  {countryFilter && cities.length > 0 && (
                    <button
                      onClick={() => setOpenSheet("city")}
                      className={`inline-flex items-center rounded-full px-3 py-1.5 ${
                        cityFilter ? "bg-brand-50 dark:bg-brand-900/30 ring-1 ring-brand-300 dark:ring-brand-700" : "bg-gray-100 dark:bg-gray-800"
                      }`}
                    >
                      <span className={cityFilter ? "text-brand-600 dark:text-brand-400 font-medium" : "text-gray-500 dark:text-gray-400"}>
                        {cityFilter || "시/도"}
                      </span>
                      <Icon name="expand_more" size={14} className="ml-0.5 text-gray-400" />
                    </button>
                  )}
                  {cityFilter && districts.length > 0 && (
                    <button
                      onClick={() => setOpenSheet("district")}
                      className={`inline-flex items-center rounded-full px-3 py-1.5 ${
                        districtFilter ? "bg-brand-50 dark:bg-brand-900/30 ring-1 ring-brand-300 dark:ring-brand-700" : "bg-gray-100 dark:bg-gray-800"
                      }`}
                    >
                      <span className={districtFilter ? "text-brand-600 dark:text-brand-400 font-medium" : "text-gray-500 dark:text-gray-400"}>
                        {districtFilter || "구/군"}
                      </span>
                      <Icon name="expand_more" size={14} className="ml-0.5 text-gray-400" />
                    </button>
                  )}
                  {countryFilter && (
                    <button onClick={() => { setCountryFilter(""); setCityFilter(""); setDistrictFilter(""); setRegionFlyTo(null); }} className="text-gray-400 hover:text-brand-500">
                      <Icon name="close" size={14} />
                    </button>
                  )}
                  <button
                    onClick={() => {
                      const next = !boundsFilterOn;
                      setBoundsFilterOn(next);
                      if (next) {
                        setCuisineFilter("");
                        setPriceFilter("");
                        setCountryFilter("");
                        setCityFilter("");
                        setDistrictFilter("");
                        if (navigator.geolocation) {
                          navigator.geolocation.getCurrentPosition(
                            (pos) => { setUserLoc({ lat: pos.coords.latitude, lng: pos.coords.longitude }); setRegionFlyTo({ lat: pos.coords.latitude, lng: pos.coords.longitude, zoom: 15 }); },
                            () => setRegionFlyTo({ lat: 37.498, lng: 127.0276, zoom: 15 }),
                            { timeout: 5000 },
                          );
                        } else {
                          setRegionFlyTo({ lat: 37.498, lng: 127.0276, zoom: 15 });
                        }
                      }
                    }}
                    className={`inline-flex items-center gap-0.5 rounded-full px-3 py-1.5 transition-colors ${
                      boundsFilterOn
                        ? "bg-brand-50 dark:bg-brand-900/30 ring-1 ring-brand-300 dark:ring-brand-700 text-brand-600 dark:text-brand-400"
                        : "bg-gray-100 dark:bg-gray-800 text-gray-500 dark:text-gray-400"
                    }`}
                  >
                    <Icon name="location_on" size={14} />
                    <span>{boundsFilterOn ? "내위치 ON" : "내위치"}</span>
                  </button>
                </div>
              </>
            )}
          </div>
        </div>
      </header>

      {/* ── Body: Desktop = side-by-side, Mobile = stacked ── */}

      {/* Desktop layout */}
      <div className="hidden md:flex flex-1 overflow-hidden">
        {viewMode === "map" ? (
          <>
            <aside className="w-80 bg-surface border-r dark:border-gray-800 overflow-y-auto shrink-0">
              {sidebarContent}
            </aside>
            <main className="flex-1 relative">
              <MapView
                restaurants={filteredRestaurants}
                selected={selected}
                onSelectRestaurant={handleSelectRestaurant}
                onBoundsChanged={handleBoundsChanged}
                flyTo={regionFlyTo}
                onMyLocation={handleMyLocation}
                activeChannel={channelFilter || undefined}
              />
              {visits && (
                <div className="absolute bottom-1 right-2 bg-white/60 backdrop-blur-sm text-gray-700 text-[10px] px-2.5 py-1 rounded-lg shadow-sm">
                  오늘 {visits.today} · 전체 {visits.total.toLocaleString()}
                </div>
              )}
            </main>
          </>
        ) : (
          <>
            <aside className="flex-1 bg-surface overflow-y-auto">
              {sidebarContent}
            </aside>
            <main className="w-[40%] shrink-0 relative border-l dark:border-gray-800">
              <MapView
                restaurants={filteredRestaurants}
                selected={selected}
                onSelectRestaurant={handleSelectRestaurant}
                onBoundsChanged={handleBoundsChanged}
                flyTo={regionFlyTo}
                onMyLocation={handleMyLocation}
                activeChannel={channelFilter || undefined}
              />
              {visits && (
                <div className="absolute bottom-1 right-2 bg-white/60 backdrop-blur-sm text-gray-700 text-[10px] px-2.5 py-1 rounded-lg shadow-sm">
                  오늘 {visits.today} · 전체 {visits.total.toLocaleString()}
                </div>
              )}
            </main>
          </>
        )}
      </div>

      {/* Mobile layout */}
      <div className="md:hidden flex-1 flex flex-col overflow-hidden pb-14">
        {/* Tab content — takes all remaining space above fixed nav */}
        {mobileTab === "nearby" ? (
          /* 내주변: 지도만 전체 표시, 영역필터 ON */
          <div className="flex-1 relative overflow-hidden">
            <MapView
              restaurants={filteredRestaurants}
              selected={selected}
              onSelectRestaurant={handleSelectRestaurant}
              onBoundsChanged={handleBoundsChanged}
              flyTo={regionFlyTo}
              onMyLocation={handleMyLocation}
              activeChannel={channelFilter || undefined}
            />
            <div className="absolute top-2 left-2 bg-surface/90 backdrop-blur-sm rounded-lg px-3 py-1.5 shadow-sm z-10">
              <span className="text-xs font-medium text-brand-600 dark:text-brand-400">
                내 주변 {filteredRestaurants.length}개
              </span>
            </div>
            {visits && (
              <div className="absolute bottom-1 right-2 bg-white/60 backdrop-blur-sm text-gray-700 text-[10px] px-2.5 py-1 rounded-lg shadow-sm z-10">
                오늘 {visits.today} · 전체 {visits.total.toLocaleString()}
              </div>
            )}
          </div>
        ) : mobileTab === "profile" ? (
          /* 내정보 */
          <div className="flex-1 overflow-y-auto bg-surface">
            {!user ? (
              <div className="flex flex-col items-center justify-center h-full gap-4 px-6">
                <p className="text-gray-500 dark:text-gray-400 text-sm">로그인하고 리뷰와 찜 목록을 관리하세요</p>
                <GoogleLogin
                  onSuccess={(res) => {
                    if (res.credential) login(res.credential).catch(console.error);
                  }}
                  onError={() => console.error("Google login failed")}
                  size="large"
                  text="signin_with"
                />
              </div>
            ) : (
              <div className="p-4 space-y-4">
                {/* 프로필 헤더 */}
                <div className="flex items-center gap-3 pb-4 border-b dark:border-gray-800">
                  {user.avatar_url ? (
                    <img src={user.avatar_url} alt="" className="w-12 h-12 rounded-full border border-gray-200" />
                  ) : (
                    <div className="w-12 h-12 rounded-full bg-brand-100 text-brand-700 flex items-center justify-center text-lg font-semibold border border-brand-200">
                      {(user.nickname || user.email || "?").charAt(0).toUpperCase()}
                    </div>
                  )}
                  <div className="flex-1">
                    <p className="font-semibold text-sm dark:text-gray-100">{user.nickname || user.email}</p>
                    {user.nickname && user.email && (
                      <p className="text-xs text-gray-400">{user.email}</p>
                    )}
                  </div>
                  <button
                    onClick={logout}
                    className="px-3 py-1.5 text-xs text-gray-500 border border-gray-300 dark:border-gray-700 rounded-lg hover:bg-gray-100 dark:hover:bg-gray-800"
                  >
                    로그아웃
                  </button>
                </div>
                {/* 내 리뷰 */}
                <div>
                  <h3 className="font-semibold text-sm mb-2 dark:text-gray-200">내 리뷰</h3>
                  {myReviews.length === 0 ? (
                    <p className="text-sm text-gray-400">작성한 리뷰가 없습니다</p>
                  ) : (
                    <MyReviewsList
                      reviews={myReviews}
                      memos={myMemos}
                      onClose={() => {}}
                      onSelectRestaurant={async (restaurantId) => {
                        try {
                          const r = await api.getRestaurant(restaurantId);
                          handleSelectRestaurant(r);
                        } catch { /* ignore */ }
                      }}
                    />
                  )}
                </div>
              </div>
            )}
          </div>
        ) : (
          /* 홈 / 식당 목록 / 찜: 리스트 표시 */
          <div className="flex-1 overflow-y-auto bg-surface">
            {mobileTab === "favorites" && !user ? (
              <div className="flex flex-col items-center justify-center h-full gap-4 px-6">
                <p className="text-gray-500 dark:text-gray-400 text-sm">로그인하고 찜 목록을 확인하세요</p>
                <GoogleLogin
                  onSuccess={(res) => {
                    if (res.credential) login(res.credential).catch(console.error);
                  }}
                  onError={() => console.error("Google login failed")}
                  size="large"
                  text="signin_with"
                />
              </div>
            ) : (
              mobileListContent
            )}
          </div>
        )}

        {/* Mobile Bottom Sheet for restaurant detail */}
        <BottomSheet open={showDetail && !!selected} onClose={handleCloseDetail}>
          {selected && (
            <RestaurantDetail restaurant={selected} onClose={handleCloseDetail} />
          )}
        </BottomSheet>
      </div>

      {/* ── Mobile Bottom Nav (fixed) ── */}
      <nav className="md:hidden fixed bottom-0 left-0 right-0 z-40 border-t dark:border-gray-800 bg-surface safe-area-bottom">
        <div className="flex items-stretch h-14">
            {([
              { key: "home", label: "홈", iconName: "home" },
              { key: "list", label: "식당 목록", iconName: "restaurant_menu" },
              { key: "nearby", label: "내주변", iconName: "near_me" },
              { key: "favorites", label: "찜", iconName: "favorite" },
              { key: "profile", label: "내정보", iconName: "person" },
            ] as { key: "home" | "list" | "nearby" | "favorites" | "profile"; label: string; iconName: string }[]).map((tab) => (
              <button
                key={tab.key}
                onClick={() => handleMobileTab(tab.key)}
                className={`flex-1 flex flex-col items-center justify-center gap-0.5 py-2 transition-colors ${
                  mobileTab === tab.key
                    ? "text-brand-600 dark:text-brand-400"
                    : "text-gray-500 dark:text-gray-500"
                }`}
              >
                <Icon name={tab.iconName} size={22} filled={mobileTab === tab.key} />
                <span className="text-[10px] font-medium">{tab.label}</span>
              </button>
            ))}
          </div>
        </nav>

      {/* Desktop Footer */}
      <footer className="hidden md:flex shrink-0 border-t dark:border-gray-800 bg-surface/60 backdrop-blur-sm py-2.5 items-center justify-center gap-2 text-[11px] text-gray-500 dark:text-gray-500 group">
        <div className="relative">
          <img
            src="/icon.jpg"
            alt="SDJ Labs"
            className="w-6 h-6 rounded-full border-2 border-brand-200 shadow-sm group-hover:scale-110 group-hover:rotate-12 transition-all duration-300"
          />
          <span className="absolute -top-0.5 -right-0.5 w-2 h-2 bg-brand-300 rounded-full animate-ping opacity-75" />
          <span className="absolute -top-0.5 -right-0.5 w-2 h-2 bg-brand-400 rounded-full" />
        </div>
        <span className="font-medium tracking-wide group-hover:text-gray-600 transition-colors">
          SDJ Labs Co., Ltd.
        </span>
      </footer>
      {/* Mobile Filter Sheets */}
      <FilterSheet
        open={openSheet === "cuisine"}
        onClose={() => setOpenSheet(null)}
        title="음식 장르"
        options={cuisineOptions}
        value={cuisineFilter}
        onChange={(v) => { if (isSearchResult) exitSearchMode(); setCuisineFilter(v); if (v) setBoundsFilterOn(false); }}
      />
      <FilterSheet
        open={openSheet === "price"}
        onClose={() => setOpenSheet(null)}
        title="가격대"
        options={priceOptions}
        value={priceFilter}
        onChange={(v) => { if (isSearchResult) exitSearchMode(); setPriceFilter(v); if (v) setBoundsFilterOn(false); }}
      />
      <FilterSheet
        open={openSheet === "country"}
        onClose={() => setOpenSheet(null)}
        title="나라"
        options={countryOptions}
        value={countryFilter}
        onChange={handleCountryChange}
      />
      <FilterSheet
        open={openSheet === "city"}
        onClose={() => setOpenSheet(null)}
        title="시/도"
        options={cityOptions}
        value={cityFilter}
        onChange={handleCityChange}
      />
      <FilterSheet
        open={openSheet === "district"}
        onClose={() => setOpenSheet(null)}
        title="구/군"
        options={districtOptions}
        value={districtFilter}
        onChange={handleDistrictChange}
      />
    </div>
  );
}
