// UR租房助手 — main screen, API-driven

const { useState: useStateMain, useEffect: useEffectMain, useMemo: useMemoMain, useCallback: useCallbackMain } = React;
const BRAND_LOGO_URL = '/assets/logo-urrent.png';
const STATIC_REGION_LANDING_KEYS = new Set(['tokyo', 'osaka', 'fukuoka']);
const REGION_SEO_NAMES = {
  hokkaido: '北海道',
  miyagi: '宫城县',
  tokyo: '东京',
  kanagawa: '神奈川',
  chiba: '千叶',
  saitama: '埼玉',
  ibaraki: '茨城',
  aichi: '爱知',
  mie: '三重',
  gifu: '岐阜',
  osaka: '大阪',
  hyogo: '兵库',
  kyoto: '京都',
  shiga: '滋贺',
  nara: '奈良',
  wakayama: '和歌山',
  okayama: '冈山',
  hiroshima: '广岛',
  yamaguchi: '山口',
  fukuoka: '福冈',
};
const DEFAULT_REGION_KEY = 'tokyo';

function getInitialLocationFilter() {
  const fallback = { regions: [DEFAULT_REGION_KEY], areas: [] };
  if (typeof window === 'undefined') return fallback;
  const validRegionKeys = new Set(REGIONS.flatMap(group => group.prefs.map(pref => pref.key)));
  const pathRegion = window.location.pathname.replace(/^\/+|\/+$/g, '');
  if (pathRegion && validRegionKeys.has(pathRegion)) {
    return { regions: [pathRegion], areas: [] };
  }
  const params = new URLSearchParams(window.location.search);
  const regionParam = params.get('region');
  if (!regionParam) return fallback;

  const regions = regionParam
    .split(',')
    .map(item => item.trim())
    .filter(item => validRegionKeys.has(item));

  return regions.length > 0 ? { regions, areas: [] } : fallback;
}

function setMetaContent(selector, content) {
  const node = document.querySelector(selector);
  if (node && content) node.setAttribute('content', content);
}

function setLinkHref(selector, href) {
  const node = document.querySelector(selector);
  if (node && href) node.setAttribute('href', href);
}

function buildSeoPayload(selectedKeys) {
  const regions = selectedKeys.length > 0 ? selectedKeys : [DEFAULT_REGION_KEY];
  if (regions.length === 1) {
    const regionKey = regions[0];
    const regionName = REGION_SEO_NAMES[regionKey] || regionKey;
    return {
      title: `${regionName}UR租房 | UR租房帮手｜日本UR公寓中文查询`,
      description: `查看${regionName}UR租房房源，支持按地区、租金和户型筛选，浏览户型图、物件照片与房间详情，帮助华人用户更高效找到合适的日本UR公寓。`,
    };
  }

  return {
    title: '日本UR租房 | UR租房帮手｜东京·大阪·福冈等地区UR公寓查询',
    description: 'UR租房帮手为华人用户提供日本UR租房信息查询，覆盖东京、大阪、福冈等地区，支持按地区、租金、房型筛选，查看房间详情、户型图与物件照片。',
  };
}

function buildCanonicalUrl(selectedKeys) {
  const base = new URL(window.location.origin);

  if (selectedKeys.length === 1 && STATIC_REGION_LANDING_KEYS.has(selectedKeys[0])) {
    base.pathname = `/${selectedKeys[0]}/`;
    return base.toString();
  }

  base.pathname = '/';
  if (selectedKeys.length === 1) {
    base.searchParams.set('region', selectedKeys[0]);
  } else if (selectedKeys.length > 1) {
    base.searchParams.set('region', selectedKeys.join(','));
  }
  return base.toString();
}

function App() {
  const [tab, setTab] = useStateMain('listings');
  const [locSheetOpen, setLocSheetOpen] = useStateMain(false);
  const [rentSheetOpen, setRentSheetOpen] = useStateMain(false);
  const [districtSheetOpen, setDistrictSheetOpen] = useStateMain(false);
  const [locationFilter, setLocationFilter] = useStateMain(getInitialLocationFilter);
  const [selectedDistricts, setSelectedDistricts] = useStateMain([]);
  const [rent, setRent] = useStateMain({ min: 0, max: MAX_RENT });
  const [sort, setSort] = useStateMain('recommend');
  const [areasByRegion, setAreasByRegion] = useStateMain({});
  const [showRefreshHint, setShowRefreshHint] = useStateMain(false);

  const [properties, setProperties] = useStateMain([]);
  const [loading, setLoading] = useStateMain(false);
  const [error, setError] = useStateMain(null);
  const [activeRoom, setActiveRoom] = useStateMain(null); // { property, room }

  const openRoomByKeys = useCallbackMain(async (propertyKey, roomKey) => {
    if (!propertyKey || !roomKey) return;
    try {
      const property = await apiFetch('/api/v1/properties/' + encodeURIComponent(propertyKey));
      const rooms = property.rooms || [];
      const room = rooms.find((item) => item.roomKey === roomKey || item.id === roomKey || item.canonicalRoomId === roomKey);
      if (!room) return;
      setTab('listings');
      setActiveRoom({ property, room });
    } catch (err) {
      console.warn('open room by keys failed', err);
    }
  }, []);

  const selectedKeys = locationFilter.regions;
  const selectedAreaKeys = locationFilter.areas;

  const ensureAreas = useCallbackMain(async (regionKey) => {
    if (!regionKey) return;
    const current = areasByRegion[regionKey];
    if (current?.loading || current?.loaded) return;

    setAreasByRegion(prev => ({
      ...prev,
      [regionKey]: {
        data: prev[regionKey]?.data || [],
        loading: true,
        loaded: false,
        error: null,
      },
    }));

    try {
      const res = await apiFetch('/api/v1/areas?regionKey=' + encodeURIComponent(regionKey));
      const data = Array.isArray(res) ? res : (res.data || res.areas || res.items || []);
      setAreasByRegion(prev => ({
        ...prev,
        [regionKey]: {
          data,
          loading: false,
          loaded: true,
          error: null,
        },
      }));
    } catch (err) {
      setAreasByRegion(prev => ({
        ...prev,
        [regionKey]: {
          data: prev[regionKey]?.data || [],
          loading: false,
          loaded: false,
          error: err.message || '区域加载失败',
        },
      }));
    }
  }, [areasByRegion]);

  useEffectMain(() => {
    selectedKeys.forEach(ensureAreas);
  }, [selectedKeys, ensureAreas]);

  const loadProperties = useCallbackMain(async () => {
    if (selectedKeys.length === 0) { setProperties([]); return; }
    setLoading(true); setError(null);
    try {
      const results = await Promise.all(
        selectedKeys.map(k => apiFetch('/api/v1/properties?regionKey=' + encodeURIComponent(k))
          .catch(err => { console.warn('region ' + k + ' failed', err); return null; }))
      );
      const merged = [];
      const seen = new Set();
      let anyOk = false;
      for (const res of results) {
        if (!res) continue;
        anyOk = true;
        const arr = Array.isArray(res) ? res : (res.data || res.properties || res.items || []);
        for (const p of arr) {
          const key = p.propertyKey || p.id;
          if (seen.has(key)) continue;
          seen.add(key);
          merged.push(p);
        }
      }
      if (!anyOk) throw new Error('API 无法从浏览器访问（CORS 未开启）。请在 api.repilot.jp 添加 Access-Control-Allow-Origin 响应头。');
      setProperties(merged);
      return true;
    } catch (e) {
      setError(e.message || '加载失败');
      setProperties([]);
      return false;
    } finally {
      setLoading(false);
    }
  }, [selectedKeys]);

  useEffectMain(() => { loadProperties(); }, [loadProperties]);

  useEffectMain(() => {
    if (!showRefreshHint) return undefined;
    const timer = window.setTimeout(() => setShowRefreshHint(false), 1800);
    return () => window.clearTimeout(timer);
  }, [showRefreshHint]);

  useEffectMain(() => {
    const params = new URLSearchParams(window.location.search);
    const propertyKey = params.get('propertyKey');
    const roomKey = params.get('roomKey');
    const open = params.get('open');
    if (open === 'room' && propertyKey && roomKey) {
      openRoomByKeys(propertyKey, roomKey);
    }
  }, [openRoomByKeys]);

  useEffectMain(() => {
    const normalizedRegions = selectedKeys.length > 0 ? selectedKeys : [DEFAULT_REGION_KEY];
    const payload = buildSeoPayload(normalizedRegions);
    const canonicalUrl = buildCanonicalUrl(selectedKeys);
    document.title = payload.title;
    setMetaContent('meta[name="description"]', payload.description);
    setMetaContent('meta[property="og:title"]', payload.title);
    setMetaContent('meta[property="og:description"]', payload.description);
    setMetaContent('meta[property="og:url"]', canonicalUrl);
    setMetaContent('meta[name="twitter:title"]', payload.title);
    setMetaContent('meta[name="twitter:description"]', payload.description);
    setLinkHref('link[rel="canonical"]', canonicalUrl);
    window.history.replaceState({}, '', canonicalUrl);
  }, [selectedKeys]);

  const handleRefresh = useCallbackMain(async () => {
    const ok = await loadProperties();
    if (ok) setShowRefreshHint(true);
  }, [loadProperties]);

  const filtered = useMemoMain(() => {
    let list = properties.filter(p => {
      const parsed = parseRentRange(p.rent);
      // property matches if its min rent ≤ rent.max and max rent ≥ rent.min
      if (parsed.max < rent.min) return false;
      if (parsed.min > rent.max) return false;
      if (selectedAreaKeys.length > 0 && !selectedAreaKeys.includes(p.areaKey)) return false;
      if (selectedDistricts.length > 0 && !selectedDistricts.includes(normalizeDistrictName(p.skcs || p.districtName))) return false;
      return (p.activeRoomCount ?? (p.rooms || []).length) > 0;
    });
    if (sort === 'cheap') list.sort((a, b) => parseRentRange(a.rent).min - parseRentRange(b.rent).min);
    if (sort === 'expensive') list.sort((a, b) => parseRentRange(b.rent).max - parseRentRange(a.rent).max);
    return list;
  }, [properties, rent, sort, selectedAreaKeys, selectedDistricts]);

  const totalRooms = filtered.reduce((s, p) => s + (p.activeRoomCount || 0), 0);
  const districtOptions = useMemoMain(() => (
    selectedAreaKeys.length > 0
      ? buildDistrictOptions(
          properties,
          selectedAreaKeys,
          areasByRegion,
          (property) => property.skcs || property.districtName || ''
        )
      : []
  ), [properties, selectedAreaKeys, areasByRegion]);

  useEffectMain(() => {
    const validDistricts = new Set(districtOptions.map((item) => item.key));
    setSelectedDistricts((prev) => {
      const next = prev.filter((item) => validDistricts.has(item));
      return next.length === prev.length ? prev : next;
    });
  }, [districtOptions]);

  const labelOf = (key) => {
    for (const r of REGIONS) {
      const m = r.prefs.find(p => p.key === key);
      if (m) return m.label;
    }
    return key;
  };
  const areaLabelOf = (areaKey) => {
    const regionKey = areaKey.split('__')[0];
    const area = areasByRegion[regionKey]?.data?.find(item => item.areaKey === areaKey);
    return area?.name || areaKey;
  };

  const locLabel = selectedAreaKeys.length > 0
    ? selectedAreaKeys.length === 1 ? areaLabelOf(selectedAreaKeys[0]) : `${areaLabelOf(selectedAreaKeys[0])} 等${selectedAreaKeys.length}个片区`
    : selectedKeys.length === 0
    ? '位置'
    : selectedKeys.length === 1 ? labelOf(selectedKeys[0]) : `${labelOf(selectedKeys[0])} 等${selectedKeys.length}个`;

  const rentLabel = (rent.min === 0 && rent.max === MAX_RENT)
    ? '租金'
    : rent.max >= MAX_RENT
      ? `¥${manYen(rent.min)}〜`
      : rent.min === 0
        ? `〜¥${manYen(rent.max)}`
        : `¥${manYen(rent.min)}〜${manYen(rent.max)}`;
  const districtLabel = selectedDistricts.length > 0
    ? selectedDistricts.length === 1
      ? selectedDistricts[0]
      : `${selectedDistricts[0]} 等${selectedDistricts.length}个区域`
    : '区域';

  return (
    <div style={appStyles.shell}>
      {/* Top bar */}
      <div style={appStyles.topBar}>
        <div style={appStyles.brand}>
          <div style={appStyles.logoMark}>
            <img src={BRAND_LOGO_URL} alt="UR租房帮手 logo" style={appStyles.logoImg}/>
          </div>
          <div>
            <div style={appStyles.brandTitle}>UR租房帮手</div>
            <div style={appStyles.brandSub}>
              {loading ? '加载中…' : '日本UR租赁住宅'}
            </div>
          </div>
        </div>
        <button style={appStyles.iconBtn} onClick={handleRefresh} aria-label="refresh">
          <div style={loading ? appStyles.refreshSpin : null}>
            <Icon.Refresh size={16} color="#1A1A1A"/>
          </div>
        </button>
      </div>

      {showRefreshHint && (
        <div style={appStyles.refreshToast}>房源已更新</div>
      )}

      {tab === 'listings' ? (
        <div style={appStyles.content}>
          <div style={appStyles.filterRow}>
            <button
              style={{
                ...appStyles.filterPill,
                background: selectedKeys.length > 0 ? '#1A1A1A' : '#FFFFFF',
                color: selectedKeys.length > 0 ? '#FAF8F5' : '#1A1A1A',
                borderColor: selectedKeys.length > 0 ? '#1A1A1A' : '#E4DFD6',
              }}
              onClick={() => setLocSheetOpen(true)}
            >
              <Icon.Location size={13} color={selectedKeys.length > 0 ? '#FAF8F5' : '#1A1A1A'}/>
              <span style={{ maxWidth: 120, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>
                {locLabel}
              </span>
              <Icon.Chevron size={11} color={selectedKeys.length > 0 ? '#FAF8F5' : '#1A1A1A'}/>
            </button>
            <button
              style={{
                ...appStyles.filterPill,
                background: (rent.min > 0 || rent.max < MAX_RENT) ? '#1A1A1A' : '#FFFFFF',
                color: (rent.min > 0 || rent.max < MAX_RENT) ? '#FAF8F5' : '#1A1A1A',
                borderColor: (rent.min > 0 || rent.max < MAX_RENT) ? '#1A1A1A' : '#E4DFD6',
              }}
              onClick={() => setRentSheetOpen(true)}
            >
              <Icon.Yen size={13} color={(rent.min > 0 || rent.max < MAX_RENT) ? '#FAF8F5' : '#1A1A1A'}/>
              <span>{rentLabel}</span>
              <Icon.Chevron size={11} color={(rent.min > 0 || rent.max < MAX_RENT) ? '#FAF8F5' : '#1A1A1A'}/>
            </button>
            {selectedAreaKeys.length > 0 && districtOptions.length > 0 && (
              <button
                style={{
                  ...appStyles.filterPill,
                  background: selectedDistricts.length > 0 ? '#1A1A1A' : '#FFFFFF',
                  color: selectedDistricts.length > 0 ? '#FAF8F5' : '#1A1A1A',
                  borderColor: selectedDistricts.length > 0 ? '#1A1A1A' : '#E4DFD6',
                }}
                onClick={() => setDistrictSheetOpen(true)}
              >
                <Icon.Location size={13} color={selectedDistricts.length > 0 ? '#FAF8F5' : '#1A1A1A'}/>
                <span style={{ maxWidth: 120, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>
                  {districtLabel}
                </span>
                <Icon.Chevron size={11} color={selectedDistricts.length > 0 ? '#FAF8F5' : '#1A1A1A'}/>
              </button>
            )}
          </div>

          <div style={appStyles.resultsMeta}>
            <div style={appStyles.resultsCount}>
              <b style={{ fontWeight: 600, color: '#1A1A1A' }}>{filtered.length}</b> 个房源
              <span style={appStyles.resultsDivider}>·</span>
              共 <b style={{ fontWeight: 600, color: '#1A1A1A' }}>{totalRooms}</b> 套
            </div>
            <div style={appStyles.sortWrap}>
              <select value={sort} onChange={e => setSort(e.target.value)} style={appStyles.sortSelect}>
                <option value="recommend">推荐排序</option>
                <option value="cheap">租金从低到高</option>
                <option value="expensive">租金从高到低</option>
              </select>
              <Icon.Sort size={12} color="#6B6258"/>
            </div>
          </div>

          <div style={appStyles.cardsWrap}>
            {loading && properties.length === 0 ? (
              <>
                <SkeletonCard/><SkeletonCard/><SkeletonCard/>
              </>
            ) : error ? (
              <div style={appStyles.empty}>
                <div style={appStyles.emptyIcon}>
                  <svg width="36" height="36" viewBox="0 0 36 36" fill="none">
                    <circle cx="18" cy="18" r="14" stroke="#C6572B" strokeWidth="1.5"/>
                    <path d="M18 11v8M18 23v1" stroke="#C6572B" strokeWidth="1.8" strokeLinecap="round"/>
                  </svg>
                </div>
                <div style={appStyles.emptyTitle}>加载失败</div>
                <div style={appStyles.emptySub}>{error}</div>
                <button style={appStyles.retryBtn} onClick={loadProperties}>重新加载</button>
              </div>
            ) : filtered.length === 0 ? (
              <div style={appStyles.empty}>
                <div style={appStyles.emptyIcon}>
                  <svg width="40" height="40" viewBox="0 0 40 40" fill="none">
                    <circle cx="17" cy="17" r="10" stroke="#C4BBAA" strokeWidth="1.5"/>
                    <path d="M25 25l8 8" stroke="#C4BBAA" strokeWidth="1.5" strokeLinecap="round"/>
                  </svg>
                </div>
                <div style={appStyles.emptyTitle}>没有找到符合条件的房源</div>
                <div style={appStyles.emptySub}>请尝试调整地区或租金范围</div>
              </div>
            ) : (
              filtered.map(p => (
                <PropertyCard
                  key={p.propertyKey || p.id}
                  property={p}
                  onRoomClick={(property, room) => setActiveRoom({ property, room })}
                />
              ))
            )}
            {filtered.length > 0 && (
              <div style={appStyles.endHint}>— 共 {filtered.length} 个房源 —</div>
            )}
          </div>
        </div>
      ) : (
        <WatchScreen onOpenRoom={openRoomByKeys}/>
      )}

      {activeRoom && (
        <RoomDetail
          property={activeRoom.property}
          room={activeRoom.room}
          onBack={() => setActiveRoom(null)}
        />
      )}

      <LocationSheet open={locSheetOpen} onClose={() => setLocSheetOpen(false)}
        selectedRegions={selectedKeys}
        selectedAreas={selectedAreaKeys}
        onChange={setLocationFilter}
        areasByRegion={areasByRegion}
        ensureAreas={ensureAreas}
        countMode="rooms"/>
      <RentSheet open={rentSheetOpen} onClose={() => setRentSheetOpen(false)}
        value={rent} onChange={setRent}/>
      <DistrictSheet
        open={districtSheetOpen}
        onClose={() => setDistrictSheetOpen(false)}
        options={districtOptions}
        selectedDistricts={selectedDistricts}
        onChange={setSelectedDistricts}
        countMode="rooms"
      />

      {!activeRoom && (
        <div style={appStyles.tabBar}>
          <button style={{ ...appStyles.tab, color: tab === 'listings' ? '#1A1A1A' : '#9A9082' }}
            onClick={() => setTab('listings')}>
            <Icon.Home size={22} color={tab === 'listings' ? '#1A1A1A' : '#9A9082'} fill={tab === 'listings'}/>
            <span style={{ fontWeight: tab === 'listings' ? 600 : 500 }}>可租房源</span>
          </button>
          <button style={{ ...appStyles.tab, color: tab === 'subscribe' ? '#1A1A1A' : '#9A9082' }}
            onClick={() => setTab('subscribe')}>
            <Icon.Bell size={22} color={tab === 'subscribe' ? '#1A1A1A' : '#9A9082'} fill={tab === 'subscribe'}/>
            <span style={{ fontWeight: tab === 'subscribe' ? 600 : 500 }}>蹲房</span>
          </button>
        </div>
      )}
    </div>
  );
}

function SkeletonCard() {
  return (
    <div style={{ background: '#FFFFFF', borderRadius: 16, padding: 14, marginBottom: 12, border: '1px solid #EDE6D9' }}>
      <div style={{ display: 'flex', gap: 12, marginBottom: 12 }}>
        <div style={{ width: 88, height: 88, borderRadius: 10, background: '#F2EBDE' }}/>
        <div style={{ flex: 1, display: 'flex', flexDirection: 'column', gap: 6, paddingTop: 4 }}>
          <div style={{ width: '40%', height: 10, background: '#F2EBDE', borderRadius: 4 }}/>
          <div style={{ width: '75%', height: 14, background: '#EDE6D9', borderRadius: 4 }}/>
          <div style={{ width: '55%', height: 10, background: '#F2EBDE', borderRadius: 4, marginTop: 4 }}/>
        </div>
      </div>
      <div style={{ height: 54, background: '#FAF6EE', borderRadius: 10, marginBottom: 12 }}/>
      <div style={{ height: 48, borderTop: '1px dashed #EDE6D9' }}/>
    </div>
  );
}

const appStyles = {
  shell: { width: '100%', height: '100%', background: '#FAF8F5', display: 'flex', flexDirection: 'column', fontFamily: '"Noto Sans JP", "Hiragino Sans", system-ui, sans-serif', color: '#1A1A1A', position: 'relative', overflow: 'hidden' },
  topBar: { padding: 'calc(12px + env(safe-area-inset-top, 0px)) 18px 10px', display: 'flex', alignItems: 'center', justifyContent: 'space-between', background: '#FAF8F5', borderBottom: '1px solid #EDE6D9' },
  brand: { display: 'flex', alignItems: 'center', gap: 10 },
  logoMark: { width: 40, height: 40, borderRadius: 12, overflow: 'hidden', background: '#F4EADB', display: 'flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0 },
  logoImg: { width: '100%', height: '100%', objectFit: 'cover', display: 'block' },
  brandTitle: { fontSize: 15, fontWeight: 700, color: '#1A1A1A', letterSpacing: '0.01em', lineHeight: 1.2 },
  brandSub: { fontSize: 10, color: '#8A7F72', fontWeight: 500, marginTop: 1, letterSpacing: '0.04em' },
  iconBtn: { width: 36, height: 36, borderRadius: 18, border: '1px solid #EDE6D9', background: '#FFFFFF', display: 'flex', alignItems: 'center', justifyContent: 'center', cursor: 'pointer' },
  refreshSpin: { display: 'flex', animation: 'spin 900ms linear infinite' },
  refreshToast: {
    position: 'absolute', top: 'calc(64px + env(safe-area-inset-top, 0px))', left: '50%',
    transform: 'translateX(-50%)', zIndex: 40,
    padding: '8px 14px', borderRadius: 999,
    background: 'rgba(26,26,26,0.88)', color: '#FFFFFF',
    fontSize: 12, fontWeight: 600, letterSpacing: '0.04em',
    boxShadow: '0 8px 24px rgba(20,18,14,0.16)',
    animation: 'fadeIn 180ms ease',
  },
  content: { flex: 1, overflowY: 'auto', overflowX: 'hidden', paddingBottom: 'calc(110px + env(safe-area-inset-bottom, 0px))' },
  filterRow: { display: 'flex', flexWrap: 'wrap', gap: 8, padding: '12px 16px 8px', position: 'sticky', top: 0, background: '#FAF8F5', zIndex: 5 },
  filterPill: { display: 'inline-flex', alignItems: 'center', gap: 6, padding: '9px 14px', borderRadius: 999, border: '1px solid #E4DFD6', fontSize: 13, fontWeight: 500, fontFamily: 'inherit', cursor: 'pointer', transition: 'all 150ms ease' },
  resultsMeta: { display: 'flex', alignItems: 'center', justifyContent: 'space-between', padding: '4px 18px 10px' },
  resultsCount: { fontSize: 12, color: '#6B6258', fontWeight: 500 },
  resultsDivider: { margin: '0 6px', color: '#C4BBAA' },
  sortWrap: { display: 'flex', alignItems: 'center', gap: 4, padding: '5px 8px 5px 10px', borderRadius: 999, background: '#F4EFE4' },
  sortSelect: { border: 'none', background: 'transparent', fontSize: 12, color: '#6B6258', fontFamily: 'inherit', fontWeight: 500, cursor: 'pointer', appearance: 'none', paddingRight: 2 },
  cardsWrap: { padding: '4px 14px 16px' },
  endHint: { textAlign: 'center', fontSize: 11, color: '#B0A695', padding: '16px 0 8px', letterSpacing: '0.1em' },
  empty: { textAlign: 'center', padding: '60px 20px', display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 10 },
  emptyIcon: { width: 72, height: 72, borderRadius: 36, background: '#F4EFE4', display: 'flex', alignItems: 'center', justifyContent: 'center', marginBottom: 4 },
  emptyTitle: { fontSize: 15, fontWeight: 600, color: '#1A1A1A' },
  emptySub: { fontSize: 12, color: '#8A7F72', maxWidth: 240 },
  retryBtn: { marginTop: 8, padding: '10px 20px', borderRadius: 10, border: '1px solid #E4DFD6', background: '#FFFFFF', color: '#1A1A1A', fontSize: 13, fontWeight: 500, fontFamily: 'inherit', cursor: 'pointer' },
  tabBar: { position: 'fixed', bottom: 0, left: 0, right: 0, zIndex: 80, background: 'rgba(250, 248, 245, 0.92)', backdropFilter: 'blur(16px) saturate(180%)', WebkitBackdropFilter: 'blur(16px) saturate(180%)', borderTop: '1px solid #EDE6D9', display: 'grid', gridTemplateColumns: '1fr 1fr', padding: '8px 12px calc(16px + env(safe-area-inset-bottom, 0px))' },
  tab: { display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 4, padding: '6px', background: 'transparent', border: 'none', cursor: 'pointer', fontSize: 11, fontFamily: 'inherit', letterSpacing: '0.02em' },
  comingWrap: { flex: 1, display: 'flex', alignItems: 'center', justifyContent: 'center', padding: '20px 20px calc(120px + env(safe-area-inset-bottom, 0px))' },
  comingCard: { width: '100%', background: '#FFFFFF', borderRadius: 20, padding: '32px 24px 24px', textAlign: 'center', border: '1px solid #EDE6D9', boxShadow: '0 2px 10px rgba(60, 50, 30, 0.04)' },
  comingIconRing: { width: 92, height: 92, margin: '0 auto 18px', position: 'relative', display: 'flex', alignItems: 'center', justifyContent: 'center' },
  comingIconInner: { width: 64, height: 64, borderRadius: 32, background: 'linear-gradient(135deg, #FBEFE6, #F4EADB)', display: 'flex', alignItems: 'center', justifyContent: 'center', border: '1px solid #F4D9C5' },
  ringDot1: { position: 'absolute', top: 6, right: 10, width: 10, height: 10, borderRadius: 5, background: '#C6572B', opacity: 0.85 },
  ringDot2: { position: 'absolute', bottom: 10, left: 4, width: 6, height: 6, borderRadius: 3, background: '#4D8B5E', opacity: 0.7 },
  comingBadge: { display: 'inline-block', padding: '3px 10px', borderRadius: 999, background: '#F4EADB', color: '#8a7256', fontSize: 10, fontWeight: 600, letterSpacing: '0.1em', marginBottom: 10 },
  comingTitle: { fontSize: 22, fontWeight: 700, color: '#1A1A1A', letterSpacing: '0.02em' },
  comingTitleJp: { fontSize: 13, color: '#8A7F72', fontWeight: 500, marginTop: 2, letterSpacing: '0.15em' },
  comingDesc: { fontSize: 13, color: '#6B6258', lineHeight: 1.7, margin: '14px 0 20px' },
  comingFeatures: { display: 'flex', flexDirection: 'column', gap: 10, textAlign: 'left', padding: '14px 4px', borderTop: '1px dashed #EDE6D9', borderBottom: '1px dashed #EDE6D9', marginBottom: 18 },
  comingFeat: { display: 'flex', gap: 10, alignItems: 'flex-start' },
  comingFeatDot: { width: 6, height: 6, borderRadius: 3, background: '#C6572B', marginTop: 7, flexShrink: 0 },
  comingFeatT: { fontSize: 13, fontWeight: 600, color: '#1A1A1A' },
  comingFeatD: { fontSize: 11, color: '#8A7F72', marginTop: 2 },
  comingCta: { width: '100%', padding: '14px', borderRadius: 12, border: 'none', background: '#1A1A1A', color: '#FAF8F5', fontSize: 14, fontWeight: 600, fontFamily: 'inherit', cursor: 'pointer', letterSpacing: '0.02em' },
};

Object.assign(window, { App });
