// Babel/CDN環境向けに変換: react/react-dom はグローバル、アイコンはインラインSVG
const { useState, useMemo, useEffect } = React;

// --- アイコン(lucide風 インラインSVG) ---
const _svgBase = (size, fill, color) => ({
  xmlns: 'http://www.w3.org/2000/svg', width: size, height: size, viewBox: '0 0 24 24',
  fill: fill, stroke: color || 'currentColor', strokeWidth: 2,
  strokeLinecap: 'round', strokeLinejoin: 'round'
});
const makeIcon = (children, defaultFill = 'none') => ({ size = 24, color, fill = defaultFill, ...rest }) => (
  <svg {..._svgBase(size, fill, color)} {...rest}>{children}</svg>
);
const Filter = makeIcon(<polygon points="22 3 2 3 10 12.46 10 19 14 21 14 12.46 22 3" />);
const Info = makeIcon(<><circle cx="12" cy="12" r="10" /><line x1="12" y1="16" x2="12" y2="12" /><line x1="12" y1="8" x2="12.01" y2="8" /></>);
const ChevronDown = makeIcon(<polyline points="6 9 12 15 18 9" />);
const ChevronUp = makeIcon(<polyline points="18 15 12 9 6 15" />);
const CheckCircle2 = makeIcon(<><circle cx="12" cy="12" r="10" /><path d="m9 12 2 2 4-4" /></>);
const XCircle = makeIcon(<><circle cx="12" cy="12" r="10" /><line x1="15" y1="9" x2="9" y2="15" /><line x1="9" y1="9" x2="15" y2="15" /></>);
const PieChart = makeIcon(<><path d="M21.21 15.89A10 10 0 1 1 8 2.83" /><path d="M22 12A10 10 0 0 0 12 2v10z" /></>);
const Star = makeIcon(<polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2" />);
const Trash2 = makeIcon(<><polyline points="3 6 5 6 21 6" /><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2" /><line x1="10" y1="11" x2="10" y2="17" /><line x1="14" y1="11" x2="14" y2="17" /></>);
const Settings = makeIcon(<><path d="M12.22 2h-.44a2 2 0 0 0-2 2v.18a2 2 0 0 1-1 1.73l-.43.25a2 2 0 0 1-2 0l-.15-.08a2 2 0 0 0-2.73.73l-.22.38a2 2 0 0 0 .73 2.73l.15.1a2 2 0 0 1 1 1.72v.51a2 2 0 0 1-1 1.74l-.15.09a2 2 0 0 0-.73 2.73l.22.38a2 2 0 0 0 2.73.73l.15-.08a2 2 0 0 1 2 0l.43.25a2 2 0 0 1 1 1.73V20a2 2 0 0 0 2 2h.44a2 2 0 0 0 2-2v-.18a2 2 0 0 1 1-1.73l.43-.25a2 2 0 0 1 2 0l.15.08a2 2 0 0 0 2.73-.73l.22-.39a2 2 0 0 0-.73-2.73l-.15-.08a2 2 0 0 1-1-1.74v-.5a2 2 0 0 1 1-1.74l.15-.09a2 2 0 0 0 .73-2.73l-.22-.38a2 2 0 0 0-2.73-.73l-.15.08a2 2 0 0 1-2 0l-.43-.25a2 2 0 0 1-1-1.73V4a2 2 0 0 0-2-2z" /><circle cx="12" cy="12" r="3" /></>);
const RotateCcw = makeIcon(<><polyline points="1 4 1 10 7 10" /><path d="M3.51 15a9 9 0 1 0 2.13-9.36L1 10" /></>);
const HelpCircle = makeIcon(<><circle cx="12" cy="12" r="10" /><path d="M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3" /><line x1="12" y1="17" x2="12.01" y2="17" /></>);

// JPX公式33業種(東証本則)
const SECTORS = [
  '水産・農林業', '鉱業', '建設業', '食料品', '繊維製品', 'パルプ・紙',
  '化学', '医薬品', '石油・石炭製品', 'ゴム製品', 'ガラス・土石製品',
  '鉄鋼', '非鉄金属', '金属製品', '機械', '電気機器', '輸送用機器',
  '精密機器', 'その他製品', '電気・ガス業', '陸運業', '海運業', '空運業',
  '倉庫・運輸関連業', '情報・通信業', '卸売業', '小売業', '銀行業',
  '証券、商品先物取引業', '保険業', 'その他金融業', '不動産業', 'サービス業'
];

const DEFENSIVE_SECTORS = ['食料品', '医薬品', '電気・ガス業', '陸運業', '情報・通信業', '小売業'];

// 業種別 × 6指標 の基準値(変更不可・参照用)
const SECTOR_THRESHOLDS_DEFAULT = {
  // 配当利回り 最低 (%)
  yield: {
    '水産・農林業': 2.5, '鉱業': 3.5, '建設業': 3.0, '食料品': 2.5, '繊維製品': 2.5,
    'パルプ・紙': 3.0, '化学': 3.0, '医薬品': 2.5, '石油・石炭製品': 3.5, 'ゴム製品': 3.0,
    'ガラス・土石製品': 3.0, '鉄鋼': 3.5, '非鉄金属': 3.0, '金属製品': 2.5, '機械': 2.5,
    '電気機器': 2.5, '輸送用機器': 2.5, '精密機器': 2.0, 'その他製品': 2.5, '電気・ガス業': 2.5,
    '陸運業': 2.5, '海運業': 3.0, '空運業': 2.0, '倉庫・運輸関連業': 3.0, '情報・通信業': 3.0,
    '卸売業': 3.0, '小売業': 2.0, '銀行業': 3.5, '証券、商品先物取引業': 3.5, '保険業': 3.0,
    'その他金融業': 3.0, '不動産業': 3.0, 'サービス業': 2.5
  },
  // 配当性向 最大 (%)
  payout: {
    '水産・農林業': 60, '鉱業': 50, '建設業': 50, '食料品': 60, '繊維製品': 50,
    'パルプ・紙': 60, '化学': 50, '医薬品': 70, '石油・石炭製品': 50, 'ゴム製品': 50,
    'ガラス・土石製品': 50, '鉄鋼': 50, '非鉄金属': 50, '金属製品': 50, '機械': 40,
    '電気機器': 40, '輸送用機器': 30, '精密機器': 40, 'その他製品': 50, '電気・ガス業': 70,
    '陸運業': 50, '海運業': 50, '空運業': 50, '倉庫・運輸関連業': 50, '情報・通信業': 60,
    '卸売業': 40, '小売業': 60, '銀行業': 40, '証券、商品先物取引業': 50, '保険業': 50,
    'その他金融業': 50, '不動産業': 50, 'サービス業': 50
  },
  // 自己資本比率 最低 (%)
  equityRatio: {
    '水産・農林業': 30, '鉱業': 40, '建設業': 30, '食料品': 40, '繊維製品': 40,
    'パルプ・紙': 30, '化学': 40, '医薬品': 50, '石油・石炭製品': 40, 'ゴム製品': 40,
    'ガラス・土石製品': 40, '鉄鋼': 30, '非鉄金属': 30, '金属製品': 40, '機械': 40,
    '電気機器': 40, '輸送用機器': 35, '精密機器': 50, 'その他製品': 40, '電気・ガス業': 25,
    '陸運業': 25, '海運業': 30, '空運業': 25, '倉庫・運輸関連業': 35, '情報・通信業': 35,
    '卸売業': 30, '小売業': 30, '銀行業': 5, '証券、商品先物取引業': 10, '保険業': 5,
    'その他金融業': 10, '不動産業': 25, 'サービス業': 35
  },
  // 営業利益率 最低 (%)
  opMargin: {
    '水産・農林業': 3, '鉱業': 15, '建設業': 5, '食料品': 5, '繊維製品': 5,
    'パルプ・紙': 5, '化学': 8, '医薬品': 15, '石油・石炭製品': 5, 'ゴム製品': 8,
    'ガラス・土石製品': 8, '鉄鋼': 5, '非鉄金属': 5, '金属製品': 5, '機械': 8,
    '電気機器': 8, '輸送用機器': 8, '精密機器': 10, 'その他製品': 8, '電気・ガス業': 8,
    '陸運業': 8, '海運業': 10, '空運業': 5, '倉庫・運輸関連業': 8, '情報・通信業': 12,
    '卸売業': 3, '小売業': 3, '銀行業': 25, '証券、商品先物取引業': 15, '保険業': 5,
    'その他金融業': 10, '不動産業': 10, 'サービス業': 8
  },
  // PER 上限基準 (倍)
  per: {
    '水産・農林業': 15, '鉱業': 10, '建設業': 12, '食料品': 18, '繊維製品': 14,
    'パルプ・紙': 13, '化学': 14, '医薬品': 20, '石油・石炭製品': 10, 'ゴム製品': 13,
    'ガラス・土石製品': 13, '鉄鋼': 10, '非鉄金属': 12, '金属製品': 13, '機械': 15,
    '電気機器': 16, '輸送用機器': 12, '精密機器': 18, 'その他製品': 15, '電気・ガス業': 13,
    '陸運業': 15, '海運業': 9, '空運業': 12, '倉庫・運輸関連業': 13, '情報・通信業': 15,
    '卸売業': 11, '小売業': 18, '銀行業': 12, '証券、商品先物取引業': 12, '保険業': 13,
    'その他金融業': 12, '不動産業': 13, 'サービス業': 18
  },
  // PBR 上限基準 (倍)
  pbr: {
    '水産・農林業': 1.5, '鉱業': 0.8, '建設業': 1.2, '食料品': 1.8, '繊維製品': 1.2,
    'パルプ・紙': 1.0, '化学': 1.5, '医薬品': 2.0, '石油・石炭製品': 0.8, 'ゴム製品': 1.2,
    'ガラス・土石製品': 1.2, '鉄鋼': 1.0, '非鉄金属': 1.2, '金属製品': 1.2, '機械': 1.5,
    '電気機器': 1.8, '輸送用機器': 1.2, '精密機器': 2.0, 'その他製品': 1.5, '電気・ガス業': 1.0,
    '陸運業': 1.5, '海運業': 1.0, '空運業': 1.5, '倉庫・運輸関連業': 1.5, '情報・通信業': 2.0,
    '卸売業': 1.2, '小売業': 2.0, '銀行業': 0.8, '証券、商品先物取引業': 1.0, '保険業': 0.8,
    'その他金融業': 1.0, '不動産業': 1.2, 'サービス業': 1.8
  },
};

// 旧v5の15業種 → 新33業種 マイグレーションマップ
const SECTOR_MIGRATION_15_TO_33 = {
  '銀行':       ['銀行業'],
  '通信':       ['情報・通信業'],
  '食品・たばこ': ['食料品'],
  '医薬品':     ['医薬品'],
  '化学・タイヤ': ['化学', 'ゴム製品'],
  '自動車':     ['輸送用機器'],
  '商社':       ['卸売業'],
  'エネルギー': ['鉱業', '石油・石炭製品'],
  '電機':       ['電気機器'],
  '金融・物流': ['その他金融業', '倉庫・運輸関連業'],
  '海運':       ['海運業'],
  'リース':     ['その他金融業'],
  '電力・ガス': ['電気・ガス業'],
  '鉄道':       ['陸運業'],
  '小売(食品)': ['小売業'],
};

const SECTOR_THRESHOLDS_DEFAULT_V5 = {
  yield: { '銀行': 3.5, '通信': 3.0, '食品・たばこ': 3.0, '医薬品': 2.5, '化学・タイヤ': 3.0, '自動車': 2.5, '商社': 3.0, 'エネルギー': 3.0, '電機': 2.5, '金融・物流': 3.0, '海運': 3.0, 'リース': 3.0, '電力・ガス': 2.5, '鉄道': 2.5, '小売(食品)': 2.5 },
  payout: { '銀行': 40, '通信': 60, '食品・たばこ': 70, '医薬品': 70, '化学・タイヤ': 50, '自動車': 30, '商社': 40, 'エネルギー': 50, '電機': 40, '金融・物流': 60, '海運': 50, 'リース': 50, '電力・ガス': 70, '鉄道': 50, '小売(食品)': 70 },
  equityRatio: { '銀行': 5, '通信': 35, '食品・たばこ': 40, '医薬品': 50, '化学・タイヤ': 40, '自動車': 35, '商社': 30, 'エネルギー': 40, '電機': 40, '金融・物流': 10, '海運': 30, 'リース': 10, '電力・ガス': 25, '鉄道': 25, '小売(食品)': 30 },
  opMargin: { '銀行': 25, '通信': 15, '食品・たばこ': 10, '医薬品': 15, '化学・タイヤ': 10, '自動車': 8, '商社': 5, 'エネルギー': 10, '電機': 8, '金融・物流': 5, '海運': 10, 'リース': 10, '電力・ガス': 8, '鉄道': 12, '小売(食品)': 3 },
  per: { '銀行': 12, '通信': 14, '食品・たばこ': 15, '医薬品': 20, '化学・タイヤ': 13, '自動車': 11, '商社': 10, 'エネルギー': 10, '電機': 16, '金融・物流': 14, '海運': 9, 'リース': 12, '電力・ガス': 13, '鉄道': 16, '小売(食品)': 18 },
  pbr: { '銀行': 0.8, '通信': 2.0, '食品・たばこ': 1.5, '医薬品': 2.0, '化学・タイヤ': 1.2, '自動車': 1.0, '商社': 1.0, 'エネルギー': 0.8, '電機': 1.5, '金融・物流': 1.0, '海運': 1.0, 'リース': 1.0, '電力・ガス': 1.0, '鉄道': 1.5, '小売(食品)': 2.0 },
};

// 各指標のメタ情報
const INDICATOR_META = {
  yield: { label: '配当利回り', short: '利回り', unit: '%', direction: 'min', step: 0.1, sliderMin: 0, sliderMax: 8, tooltip: '配当 ÷ 株価。3〜5%が目安、高すぎは減配リスク' },
  payout: { label: '配当性向', short: '性向', unit: '%', direction: 'max', step: 1, sliderMin: 20, sliderMax: 120, tooltip: '配当 ÷ 利益。50%以下が健全、70%超は警戒' },
  equityRatio: { label: '自己資本比率', short: '自己資本', unit: '%', direction: 'min', step: 1, sliderMin: 0, sliderMax: 70, tooltip: '総資産に占める純資産。財務の安全度(業種で水準大きく違う)' },
  opMargin: { label: '営業利益率', short: '営業益率', unit: '%', direction: 'min', step: 1, sliderMin: 0, sliderMax: 40, tooltip: '本業の儲ける力(業種で水準大きく違う)' },
  per: { label: 'PER', short: 'PER', unit: '倍', direction: 'max', step: 0.5, sliderMin: 5, sliderMax: 40, hasTolerance: true, tooltip: '株価 ÷ EPS。割安度の指標' },
  pbr: { label: 'PBR', short: 'PBR', unit: '倍', direction: 'max', step: 0.1, sliderMin: 0.3, sliderMax: 5, hasTolerance: true, tooltip: '株価 ÷ BPS。1倍割れは東証改革対象になることも' },
};

const INDICATOR_KEYS = ['yield', 'payout', 'equityRatio', 'opMargin', 'per', 'pbr'];

// 高配当株でフォーカスされやすい主要17業種(よく使う)。残りは折りたたみ。
const PRIMARY_SECTORS = [
  '銀行業', '情報・通信業', '食料品', '医薬品', '化学', 'ゴム製品',
  '輸送用機器', '卸売業', '鉱業', '石油・石炭製品', '電気機器',
  'その他金融業', '倉庫・運輸関連業', '海運業', '電気・ガス業',
  '陸運業', '小売業',
];
const SECONDARY_SECTORS = SECTORS.filter(s => !PRIMARY_SECTORS.includes(s));

const TREND_LABELS = { up: { label: '右肩上がり', color: '#5a9a7a', icon: '↗' }, flat: { label: '横ばい', color: '#c9a876', icon: '→' }, down: { label: '下降', color: '#c97064', icon: '↘' }, unknown: { label: '不明', color: '#5a6478', icon: '?' } };

const STORAGE_KEYS = {
  favorites: 'dividend_favorites_v5',
  thresholds: 'dividend_thresholds_v6',  // v5(15業種)→v6(33業種)で書式変更
  thresholds_v5_legacy: 'dividend_thresholds_v5',
  perTolerance: 'dividend_per_tolerance_v5',
  pbrTolerance: 'dividend_pbr_tolerance_v5',
  filterFlags: 'dividend_filter_flags_v5',
  memos: 'dividend_memos_v6',
  sectorFilter: 'dividend_sector_filter_v6',
};

const DISPLAY_LIMIT_INITIAL = 100;
const DISPLAY_LIMIT_STEP = 100;

// 並び替えボタン列の項目定義(label, field)
const SORT_FIELDS = [
  ['sector', '業種'],
  ['yield', '利回り'],
  ['payout', '性向'],
  ['opMargin', '営業益率'],
  ['per', 'PER'],
  ['pbr', 'PBR'],
  ['growthYears', '連続増配'],
  ['marketCap', '時価総額'],
];

// localStorage 安全ラッパー
const storage = {
  get: (key, fallback) => {
    try {
      const v = localStorage.getItem(key);
      return v == null ? fallback : JSON.parse(v);
    } catch { return fallback; }
  },
  set: (key, value) => {
    try { localStorage.setItem(key, JSON.stringify(value)); } catch {}
  }
};

// 業種閾値の deep-merge(初期値補完)
function mergeThresholds(custom) {
  const out = {};
  for (const k of INDICATOR_KEYS) {
    out[k] = { ...SECTOR_THRESHOLDS_DEFAULT[k], ...(custom?.[k] || {}) };
  }
  return out;
}

// 旧v5の15業種カスタム値から、新33業種への移行
function migrateV5Thresholds(oldThresholds) {
  if (!oldThresholds) return null;
  const result = {};
  for (const ind of INDICATOR_KEYS) {
    result[ind] = { ...SECTOR_THRESHOLDS_DEFAULT[ind] };
    const oldInd = oldThresholds[ind] || {};
    const oldDef = SECTOR_THRESHOLDS_DEFAULT_V5[ind] || {};
    for (const [oldSec, newSecs] of Object.entries(SECTOR_MIGRATION_15_TO_33)) {
      const v = oldInd[oldSec];
      // カスタムされていた値だけ引き継ぐ(基準値ならスキップ)
      if (v != null && v !== oldDef[oldSec]) {
        for (const ns of newSecs) result[ind][ns] = v;
      }
    }
  }
  return result;
}

function loadThresholdsWithMigration() {
  const v6 = storage.get(STORAGE_KEYS.thresholds, null);
  if (v6) return mergeThresholds(v6);
  const v5 = storage.get(STORAGE_KEYS.thresholds_v5_legacy, null);
  if (v5) {
    const migrated = migrateV5Thresholds(v5);
    storage.set(STORAGE_KEYS.thresholds, migrated);
    return migrated;
  }
  return SECTOR_THRESHOLDS_DEFAULT;
}

const DEFAULT_FILTER_FLAGS = {
  yield: true, payout: true, equityRatio: true, opMargin: true, per: true, pbr: true,
  requireSalesUp: true, requireEpsUp: true, requireOpCfUp: true,
  requireDivUp: true, requireRetainedUp: false,
};

function DividendScreener() {
  const [filterFlags, setFilterFlags] = useState(DEFAULT_FILTER_FLAGS);
  const [minGrowthYears, setMinGrowthYears] = useState(0);

  const [sortBy, setSortBy] = useState('yield');
  const [sortDesc, setSortDesc] = useState(true);
  const [expanded, setExpanded] = useState(null);
  const [activeTab, setActiveTab] = useState('screen');

  const [favorites, setFavorites] = useState([]);
  const [thresholds, setThresholds] = useState(SECTOR_THRESHOLDS_DEFAULT);
  const [perTolerance, setPerTolerance] = useState(1.2);
  const [pbrTolerance, setPbrTolerance] = useState(1.2);
  const [activeIndicator, setActiveIndicator] = useState('yield');
  const [loaded, setLoaded] = useState(false);

  // 業種フィルター(チェック済みの業種だけ表示)、メモ、お気に入り絞込トグル
  const [sectorFilter, setSectorFilter] = useState(() => new Set(SECTORS));
  const [favSectorFilter, setFavSectorFilter] = useState(() => new Set(SECTORS));
  const [showSecondarySectors, setShowSecondarySectors] = useState(false);
  const [showFavSecondarySectors, setShowFavSecondarySectors] = useState(false);
  const [memos, setMemos] = useState({});
  const [applyScreeningToFav, setApplyScreeningToFav] = useState(false);
  const [groupBySector, setGroupBySector] = useState(false);
  const [groupBySectorFav, setGroupBySectorFav] = useState(false);

  // 表示上限(重さ対策)
  const [displayLimit, setDisplayLimit] = useState(DISPLAY_LIMIT_INITIAL);

  // データ取得
  const [stocks, setStocks] = useState([]);
  const [fetchedAt, setFetchedAt] = useState(null);
  const [dataStatus, setDataStatus] = useState('loading'); // 'loading' | 'ready' | 'error'
  const [dataError, setDataError] = useState(null);
  const [reloadCount, setReloadCount] = useState(0);

  // 初期ロード(設定の永続化)
  useEffect(() => {
    setFavorites(storage.get(STORAGE_KEYS.favorites, []));
    setThresholds(loadThresholdsWithMigration());
    setPerTolerance(storage.get(STORAGE_KEYS.perTolerance, 1.2));
    setPbrTolerance(storage.get(STORAGE_KEYS.pbrTolerance, 1.2));
    setFilterFlags({ ...DEFAULT_FILTER_FLAGS, ...storage.get(STORAGE_KEYS.filterFlags, {}) });
    setMemos(storage.get(STORAGE_KEYS.memos, {}));
    const savedSec = storage.get(STORAGE_KEYS.sectorFilter, null);
    if (Array.isArray(savedSec) && savedSec.length > 0) {
      setSectorFilter(new Set(savedSec));
    }
    const savedFavSec = storage.get('dividend_fav_sector_filter_v6', null);
    if (Array.isArray(savedFavSec) && savedFavSec.length > 0) {
      setFavSectorFilter(new Set(savedFavSec));
    }
    setGroupBySector(!!storage.get('dividend_group_by_sector_v6', false));
    setGroupBySectorFav(!!storage.get('dividend_group_by_sector_fav_v6', false));
    setLoaded(true);
  }, []);

  // data.json取得(初回 + reloadCount変化時)
  useEffect(() => {
    let cancelled = false;
    setDataStatus('loading');
    setDataError(null);
    // キャッシュバスター付きでネットワーク優先取得
    fetch(`./data.json?t=${Date.now()}`, { cache: reloadCount > 0 ? 'no-cache' : 'default' })
      .then(r => {
        if (!r.ok) throw new Error(`HTTP ${r.status}`);
        return r.json();
      })
      .then(d => {
        if (cancelled) return;
        setStocks(d.stocks || []);
        setFetchedAt(d.fetchedAt || null);
        setDataStatus('ready');
      })
      .catch(e => {
        if (cancelled) return;
        setDataError(e.message || String(e));
        setDataStatus('error');
      });
    return () => { cancelled = true; };
  }, [reloadCount]);

  const reloadData = () => setReloadCount(c => c + 1);

  // 永続化
  useEffect(() => { if (loaded) storage.set(STORAGE_KEYS.favorites, favorites); }, [favorites, loaded]);
  useEffect(() => { if (loaded) storage.set(STORAGE_KEYS.thresholds, thresholds); }, [thresholds, loaded]);
  useEffect(() => { if (loaded) storage.set(STORAGE_KEYS.perTolerance, perTolerance); }, [perTolerance, loaded]);
  useEffect(() => { if (loaded) storage.set(STORAGE_KEYS.pbrTolerance, pbrTolerance); }, [pbrTolerance, loaded]);
  useEffect(() => { if (loaded) storage.set(STORAGE_KEYS.filterFlags, filterFlags); }, [filterFlags, loaded]);
  useEffect(() => { if (loaded) storage.set(STORAGE_KEYS.memos, memos); }, [memos, loaded]);
  useEffect(() => { if (loaded) storage.set(STORAGE_KEYS.sectorFilter, Array.from(sectorFilter)); }, [sectorFilter, loaded]);
  useEffect(() => { if (loaded) storage.set('dividend_fav_sector_filter_v6', Array.from(favSectorFilter)); }, [favSectorFilter, loaded]);
  useEffect(() => { if (loaded) storage.set('dividend_group_by_sector_v6', groupBySector); }, [groupBySector, loaded]);
  useEffect(() => { if (loaded) storage.set('dividend_group_by_sector_fav_v6', groupBySectorFav); }, [groupBySectorFav, loaded]);

  const toggleFavSector = (sec) => {
    setFavSectorFilter(s => {
      const ns = new Set(s);
      if (ns.has(sec)) ns.delete(sec); else ns.add(sec);
      return ns;
    });
  };
  const setAllFavSectors = (val) => setFavSectorFilter(val ? new Set(SECTORS) : new Set());

  // 条件・並びが変わったら表示上限を初期に戻す
  useEffect(() => { setDisplayLimit(DISPLAY_LIMIT_INITIAL); }, [filterFlags, minGrowthYears, sectorFilter, sortBy, sortDesc]);

  // 業種チェック切替
  const toggleSector = (sec) => {
    setSectorFilter(s => {
      const ns = new Set(s);
      if (ns.has(sec)) ns.delete(sec); else ns.add(sec);
      return ns;
    });
  };
  const setAllSectors = (val) => setSectorFilter(val ? new Set(SECTORS) : new Set());
  const setMemo = (code, text) => {
    setMemos(m => {
      const nm = { ...m };
      if (text && text.trim()) nm[code] = text;
      else delete nm[code];
      return nm;
    });
  };

  const toggleFavorite = (code) => {
    setFavorites(favs => favs.includes(code) ? favs.filter(c => c !== code) : [...favs, code]);
  };

  const clearAllFavorites = () => {
    if (!confirm('お気に入りを全て削除しますか?')) return;
    setFavorites([]);
  };

  const updateThreshold = (indicator, sector, value) => {
    setThresholds(t => ({ ...t, [indicator]: { ...t[indicator], [sector]: value } }));
  };

  const resetSectorThreshold = (indicator, sector) => {
    setThresholds(t => ({ ...t, [indicator]: { ...t[indicator], [sector]: SECTOR_THRESHOLDS_DEFAULT[indicator][sector] } }));
  };

  const resetIndicator = (indicator) => {
    if (!confirm(`「${INDICATOR_META[indicator].label}」の全業種を基準値に戻しますか?`)) return;
    setThresholds(t => ({ ...t, [indicator]: { ...SECTOR_THRESHOLDS_DEFAULT[indicator] } }));
  };

  const resetAllThresholds = () => {
    if (!confirm('全指標・全業種を基準値に戻しますか?')) return;
    setThresholds(SECTOR_THRESHOLDS_DEFAULT);
    setPerTolerance(1.2);
    setPbrTolerance(1.2);
  };

  const setFlag = (key, val) => setFilterFlags(f => ({ ...f, [key]: val }));

  // 銘柄が指標条件をクリアしているか
  const passesIndicator = (s, indicator) => {
    const sectorVal = thresholds[indicator]?.[s.sector];
    if (sectorVal == null) return true;
    const v = s[indicator];
    if (v == null) return true;
    if (indicator === 'per') return v <= sectorVal * perTolerance;
    if (indicator === 'pbr') return v <= sectorVal * pbrTolerance;
    if (INDICATOR_META[indicator].direction === 'min') return v >= sectorVal;
    return v <= sectorVal;
  };

  // 銘柄リストにスクリーニング条件を適用するヘルパー(業種フィルター含まない)
  const passesAllConditions = (s) => (
    (s.growthYears ?? 0) >= minGrowthYears &&
    (!filterFlags.yield || passesIndicator(s, 'yield')) &&
    (!filterFlags.payout || ((s.payout ?? 0) > 0 && passesIndicator(s, 'payout'))) &&
    (!filterFlags.equityRatio || passesIndicator(s, 'equityRatio')) &&
    (!filterFlags.opMargin || passesIndicator(s, 'opMargin')) &&
    (!filterFlags.per || passesIndicator(s, 'per')) &&
    (!filterFlags.pbr || passesIndicator(s, 'pbr')) &&
    (!filterFlags.requireSalesUp || s.salesTrend === 'up') &&
    (!filterFlags.requireEpsUp || s.epsTrend === 'up') &&
    (!filterFlags.requireOpCfUp || (s.opCfTrend !== 'down' && s.opCfTrend !== 'unknown')) &&
    (!filterFlags.requireDivUp || s.divTrend === 'up') &&
    (!filterFlags.requireRetainedUp || s.retainedTrend === 'up')
  );

  const sortStocks = (arr) => {
    const sorted = [...arr];
    sorted.sort((a, b) => {
      let diff;
      if (sortBy === 'sector') {
        diff = String(a.sector || '').localeCompare(String(b.sector || ''), 'ja');
      } else {
        diff = (a[sortBy] ?? 0) - (b[sortBy] ?? 0);
      }
      return sortDesc ? -diff : diff;
    });
    return sorted;
  };

  const filtered = useMemo(() => {
    let result = stocks.filter(s => sectorFilter.has(s.sector) && passesAllConditions(s));
    const seen = new Set();
    result = result.filter(s => { if (seen.has(s.code)) return false; seen.add(s.code); return true; });
    return sortStocks(result);
  }, [stocks, filterFlags, minGrowthYears, thresholds, perTolerance, pbrTolerance, sortBy, sortDesc, sectorFilter]);

  const favoriteStocks = useMemo(() => {
    let arr = favorites.map(code => stocks.find(s => s.code === code)).filter(Boolean);
    arr = arr.filter(s => favSectorFilter.has(s.sector));
    if (applyScreeningToFav) arr = arr.filter(passesAllConditions);
    return sortStocks(arr);
  }, [favorites, stocks, applyScreeningToFav, favSectorFilter, filterFlags, minGrowthYears, thresholds, perTolerance, pbrTolerance, sortBy, sortDesc]);

  const favAvgYield = useMemo(() => {
    if (favoriteStocks.length === 0) return null;
    const sum = favoriteStocks.reduce((acc, s) => acc + (s.yield ?? 0), 0);
    return sum / favoriteStocks.length;
  }, [favoriteStocks]);

  const toggleSort = (field) => {
    if (sortBy === field) setSortDesc(!sortDesc);
    else { setSortBy(field); setSortDesc(true); }
  };

  // カスタマイズされた閾値の数(指標ごと、全業種合計)
  const customCountByIndicator = useMemo(() => {
    const out = {};
    for (const ind of INDICATOR_KEYS) {
      out[ind] = SECTORS.filter(s => thresholds[ind]?.[s] !== SECTOR_THRESHOLDS_DEFAULT[ind][s]).length;
    }
    return out;
  }, [thresholds]);

  const totalCustomCount = Object.values(customCountByIndicator).reduce((a, b) => a + b, 0);

  return (
    <div style={{ minHeight: '100vh', background: 'linear-gradient(180deg, #0a0e1a 0%, #131829 100%)', color: '#e8ecf1', fontFamily: '"Noto Serif JP", "Hiragino Mincho ProN", serif', padding: '2rem 1rem' }}>
      <div style={{ maxWidth: '1100px', margin: '0 auto' }}>

        <header style={{ borderBottom: '1px solid #2a3447', paddingBottom: '1.5rem', marginBottom: '1.5rem' }}>
          <div style={{ display: 'flex', alignItems: 'baseline', gap: '0.75rem', marginBottom: '0.5rem', flexWrap: 'wrap' }}>
            <div style={{ width: '4px', height: '32px', background: 'linear-gradient(180deg, #d4a574, #8b6914)' }} />
            <h1 style={{ fontSize: '1.6rem', fontWeight: 600, margin: 0, letterSpacing: '0.05em' }}>
              高配当株 スクリーナー
            </h1>
            <span style={{ fontSize: '0.7rem', color: '#7a8499', fontFamily: 'monospace', letterSpacing: '0.1em' }}>
              v6 · 33業種・実データ
            </span>
          </div>
          <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', flexWrap: 'wrap', gap: '0.5rem', marginTop: '0.5rem' }}>
            <p style={{ fontSize: '0.8rem', color: '#a0a8b8', margin: 0, lineHeight: 1.7 }}>
              JPX全銘柄(プライム/スタンダード/グロース)から、配当ありの銘柄を業種別6指標で絞り込みます。
            </p>
            <DataStatusBadge status={dataStatus} fetchedAt={fetchedAt} count={stocks.length} error={dataError} onReload={reloadData} />
          </div>
        </header>

        <div style={{ display: 'flex', gap: '0', marginBottom: '1.5rem', borderBottom: '1px solid #2a3447', overflowX: 'auto' }}>
          <TabButton active={activeTab === 'screen'} onClick={() => setActiveTab('screen')}>
            <Filter size={14} /> スクリーニング
          </TabButton>
          <TabButton active={activeTab === 'favorites'} onClick={() => setActiveTab('favorites')}>
            <Star size={14} /> お気に入り
            {favorites.length > 0 && <Badge>{favorites.length}</Badge>}
          </TabButton>
          <TabButton active={activeTab === 'settings'} onClick={() => setActiveTab('settings')}>
            <Settings size={14} /> 業種別設定
            {totalCustomCount > 0 && <Badge>{totalCustomCount}</Badge>}
          </TabButton>
        </div>

        {activeTab === 'screen' && (
          <>
            <section style={{ background: 'rgba(255,255,255,0.02)', border: '1px solid #2a3447', borderRadius: '4px', padding: '1.25rem', marginBottom: '1.5rem' }}>
              <SectionTitle>業績フィルター</SectionTitle>
              <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(220px, 1fr))', gap: '0.5rem' }}>
                <IndicatorCheckBox indicator="opMargin" checked={filterFlags.opMargin} onChange={v => setFlag('opMargin', v)} />
                <CheckBox label="売上が右肩上がり" checked={filterFlags.requireSalesUp} onChange={v => setFlag('requireSalesUp', v)} />
                <CheckBox label="EPSが右肩上がり" checked={filterFlags.requireEpsUp} onChange={v => setFlag('requireEpsUp', v)} />
                <CheckBox label="営業CFが黒字維持" checked={filterFlags.requireOpCfUp} onChange={v => setFlag('requireOpCfUp', v)} />
              </div>
            </section>

            <section style={{ background: 'rgba(255,255,255,0.02)', border: '1px solid #2a3447', borderRadius: '4px', padding: '1.25rem', marginBottom: '1.5rem' }}>
              <SectionTitle>配当フィルター</SectionTitle>
              <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(220px, 1fr))', gap: '0.5rem', marginBottom: '0.75rem' }}>
                <IndicatorCheckBox indicator="yield" checked={filterFlags.yield} onChange={v => setFlag('yield', v)} />
                <IndicatorCheckBox indicator="payout" checked={filterFlags.payout} onChange={v => setFlag('payout', v)} />
                <CheckBox label="1株配当金が減配なし・右肩上がり" checked={filterFlags.requireDivUp} onChange={v => setFlag('requireDivUp', v)} />
              </div>
              <div style={{ maxWidth: '320px' }}>
                <FilterSlider label="連続増配年数" value={minGrowthYears} setValue={setMinGrowthYears} min={0} max={30} step={1} unit="年 以上" />
              </div>
            </section>

            <section style={{ background: 'rgba(255,255,255,0.02)', border: '1px solid #2a3447', borderRadius: '4px', padding: '1.25rem', marginBottom: '1.5rem' }}>
              <SectionTitle>財務フィルター</SectionTitle>
              <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(220px, 1fr))', gap: '0.5rem' }}>
                <IndicatorCheckBox indicator="equityRatio" checked={filterFlags.equityRatio} onChange={v => setFlag('equityRatio', v)} />
                <IndicatorCheckBox indicator="per" checked={filterFlags.per} onChange={v => setFlag('per', v)} />
                <IndicatorCheckBox indicator="pbr" checked={filterFlags.pbr} onChange={v => setFlag('pbr', v)} />
                <CheckBox label="利益剰余金が右肩上がり" checked={filterFlags.requireRetainedUp} onChange={v => setFlag('requireRetainedUp', v)} />
              </div>
              {totalCustomCount > 0 && (
                <div style={{ marginTop: '0.75rem', padding: '0.75rem', background: 'rgba(212,165,116,0.05)', borderLeft: '2px solid #d4a574', fontSize: '0.75rem', color: '#a0a8b8' }}>
                  💡 業種別の閾値は「業種別設定」タブでカスタマイズできます(現在 {totalCustomCount}件 を変更中)
                </div>
              )}
            </section>

            <SectorFilter
              sectorFilter={sectorFilter} toggleSector={toggleSector} setAllSectors={setAllSectors}
              showSecondary={showSecondarySectors} setShowSecondary={setShowSecondarySectors}
            />

            <SortBar sortBy={sortBy} sortDesc={sortDesc} toggleSort={toggleSort} />

            <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: '1rem', padding: '0 0.5rem', flexWrap: 'wrap', gap: '0.5rem' }}>
              <div style={{ fontSize: '0.85rem', color: '#a0a8b8' }}>
                <span style={{ fontSize: '1.5rem', color: '#d4a574', fontWeight: 600, marginRight: '0.4rem', fontFamily: 'monospace' }}>{filtered.length}</span>
                <span>銘柄が条件に該当</span>
                {filtered.length > displayLimit && !groupBySector && <span style={{ marginLeft: '0.5rem', fontSize: '0.7rem', color: '#7a8499' }}>({displayLimit}件まで表示)</span>}
              </div>
              <label style={{ display: 'flex', alignItems: 'center', gap: '0.35rem', fontSize: '0.75rem', cursor: 'pointer', color: groupBySector ? '#d4a574' : '#a0a8b8' }}>
                <input type="checkbox" checked={groupBySector} onChange={e => setGroupBySector(e.target.checked)} style={{ accentColor: '#d4a574' }} />
                業種別にまとめる
              </label>
            </div>

            <StockTable
              stocks={filtered} expanded={expanded} setExpanded={setExpanded}
              favorites={favorites} toggleFavorite={toggleFavorite}
              sortBy={sortBy} sortDesc={sortDesc} toggleSort={toggleSort} showSort={true}
              memos={memos} setMemo={setMemo}
              displayLimit={groupBySector ? Infinity : displayLimit}
              groupBySector={groupBySector}
              onShowMore={() => setDisplayLimit(d => d + DISPLAY_LIMIT_STEP)}
              thresholds={thresholds} perTolerance={perTolerance} pbrTolerance={pbrTolerance}
            />
          </>
        )}

        {activeTab === 'favorites' && (
          <>
            <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: '1rem', padding: '0 0.5rem', flexWrap: 'wrap', gap: '0.5rem' }}>
              <div style={{ fontSize: '0.85rem', color: '#a0a8b8' }}>
                <span style={{ fontSize: '1.5rem', color: '#d4a574', fontWeight: 600, marginRight: '0.4rem', fontFamily: 'monospace' }}>{favoriteStocks.length}</span>
                <span>お気に入り銘柄</span>
                {favAvgYield != null && (
                  <span style={{ marginLeft: '1rem', fontFamily: 'monospace', fontSize: '0.8rem', color: '#5a9a7a' }}>
                    平均利回り: {favAvgYield.toFixed(2)}%
                  </span>
                )}
              </div>
              {favorites.length > 0 && (
                <button onClick={clearAllFavorites} style={btnDanger}>
                  <Trash2 size={12} /> 全て削除
                </button>
              )}
            </div>

            {favorites.length > 0 && (
              <>
                <SectorFilter
                  sectorFilter={favSectorFilter} toggleSector={toggleFavSector} setAllSectors={setAllFavSectors}
                  showSecondary={showFavSecondarySectors} setShowSecondary={setShowFavSecondarySectors}
                />
                <SortBar sortBy={sortBy} sortDesc={sortDesc} toggleSort={toggleSort} />
                <div style={{ marginBottom: '1rem', padding: '0.6rem 0.85rem', background: 'rgba(212,165,116,0.05)', border: '1px solid rgba(212,165,116,0.2)', borderRadius: '3px', fontSize: '0.78rem', display: 'flex', flexWrap: 'wrap', gap: '0.75rem 1.25rem', alignItems: 'center' }}>
                  <label style={{ display: 'flex', alignItems: 'center', gap: '0.5rem', cursor: 'pointer', color: groupBySectorFav ? '#d4a574' : 'inherit' }}>
                    <input type="checkbox" checked={groupBySectorFav} onChange={e => setGroupBySectorFav(e.target.checked)} style={{ accentColor: '#d4a574' }} />
                    <span>業種別にまとめる</span>
                  </label>
                  <label style={{ display: 'flex', alignItems: 'center', gap: '0.5rem', cursor: 'pointer', color: applyScreeningToFav ? '#d4a574' : 'inherit' }}>
                    <input type="checkbox" checked={applyScreeningToFav} onChange={e => setApplyScreeningToFav(e.target.checked)} style={{ accentColor: '#d4a574' }} />
                    <span>現在のスクリーニング条件で絞り込む</span>
                  </label>
                  <span style={{ marginLeft: 'auto', fontFamily: 'monospace', color: '#7a8499', fontSize: '0.72rem' }}>{favoriteStocks.length} / {favorites.length}件</span>
                </div>
              </>
            )}

            {!loaded ? (
              <div style={{ padding: '3rem', textAlign: 'center', color: '#7a8499' }}>読み込み中...</div>
            ) : favoriteStocks.length === 0 ? (
              <div style={{ background: 'rgba(255,255,255,0.02)', border: '1px dashed #2a3447', borderRadius: '4px', padding: '3rem 1.5rem', textAlign: 'center' }}>
                <Star size={32} color="#3a4459" style={{ marginBottom: '1rem' }} />
                <div style={{ color: '#a0a8b8', fontSize: '0.9rem', marginBottom: '0.5rem' }}>お気に入り銘柄がありません</div>
                <div style={{ color: '#7a8499', fontSize: '0.75rem' }}>「スクリーニング」タブで気になる銘柄の⭐をタップして追加してください</div>
              </div>
            ) : favoriteStocks.length === 0 ? (
              <div style={{ background: 'rgba(255,255,255,0.02)', border: '1px solid #2a3447', borderRadius: '4px', padding: '2rem 1.5rem', textAlign: 'center', color: '#a0a8b8', fontSize: '0.85rem' }}>
                条件に合うお気に入り銘柄がありません(業種フィルターやスクリーニング条件を見直してください)。
              </div>
            ) : (
              <StockTable
                stocks={favoriteStocks} expanded={expanded} setExpanded={setExpanded}
                favorites={favorites} toggleFavorite={toggleFavorite} showSort={true}
                sortBy={sortBy} sortDesc={sortDesc} toggleSort={toggleSort}
                thresholds={thresholds} perTolerance={perTolerance} pbrTolerance={pbrTolerance}
                memos={memos} setMemo={setMemo}
                displayLimit={Infinity}
                groupBySector={groupBySectorFav}
              />
            )}
          </>
        )}

        {activeTab === 'settings' && (
          <SettingsTab
            activeIndicator={activeIndicator} setActiveIndicator={setActiveIndicator}
            thresholds={thresholds} updateThreshold={updateThreshold}
            resetSectorThreshold={resetSectorThreshold} resetIndicator={resetIndicator}
            resetAllThresholds={resetAllThresholds}
            perTolerance={perTolerance} setPerTolerance={setPerTolerance}
            pbrTolerance={pbrTolerance} setPbrTolerance={setPbrTolerance}
            customCountByIndicator={customCountByIndicator}
          />
        )}

        <footer style={{ marginTop: '2rem', padding: '1.25rem', background: 'rgba(212,165,116,0.05)', borderLeft: '3px solid #d4a574', fontSize: '0.75rem', lineHeight: 1.7, color: '#a0a8b8' }}>
          <div style={{ display: 'flex', alignItems: 'flex-start', gap: '0.5rem' }}>
            <Info size={14} color="#d4a574" style={{ flexShrink: 0, marginTop: '2px' }} />
            <div>
              <strong style={{ color: '#d4a574' }}>使い方:</strong>
              {' '}業績・配当・財務の各指標とトレンド方向で絞り込み、業種ごとの閾値は自分の判断で調整できます。
              お気に入りや業種別設定はブラウザに保存され、次回起動時に引き継がれます。
              <br /><strong style={{ color: '#c9a876' }}>※投資判断は自己責任でお願いします。個別銘柄の推奨ではありません。</strong>
            </div>
          </div>
        </footer>
      </div>
    </div>
  );
}

const btnDanger = { background: 'rgba(201,112,100,0.1)', border: '1px solid rgba(201,112,100,0.3)', color: '#c97064', padding: '0.4rem 0.75rem', borderRadius: '3px', fontSize: '0.75rem', cursor: 'pointer', display: 'flex', alignItems: 'center', gap: '0.3rem', fontFamily: 'inherit' };
const btnGold = { background: 'rgba(212,165,116,0.1)', border: '1px solid rgba(212,165,116,0.3)', color: '#d4a574', padding: '0.4rem 0.75rem', borderRadius: '3px', fontSize: '0.75rem', cursor: 'pointer', display: 'flex', alignItems: 'center', gap: '0.3rem', fontFamily: 'inherit' };

function SettingsTab({ activeIndicator, setActiveIndicator, thresholds, updateThreshold, resetSectorThreshold, resetIndicator, resetAllThresholds, perTolerance, setPerTolerance, pbrTolerance, setPbrTolerance, customCountByIndicator }) {
  const meta = INDICATOR_META[activeIndicator];
  const tolerance = activeIndicator === 'per' ? perTolerance : activeIndicator === 'pbr' ? pbrTolerance : null;
  const setTolerance = activeIndicator === 'per' ? setPerTolerance : setPbrTolerance;
  const indCount = customCountByIndicator[activeIndicator] || 0;

  return (
    <>
      <section style={{ background: 'rgba(255,255,255,0.02)', border: '1px solid #2a3447', borderRadius: '4px', padding: '1.25rem', marginBottom: '1.5rem' }}>
        <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', flexWrap: 'wrap', gap: '0.5rem', marginBottom: '0.75rem' }}>
          <SectionTitle>調整する指標を選ぶ</SectionTitle>
          <button onClick={resetAllThresholds} style={btnGold}>
            <RotateCcw size={12} /> 全指標・全業種を基準値に戻す
          </button>
        </div>
        <div style={{ display: 'flex', gap: '4px', flexWrap: 'wrap' }}>
          {INDICATOR_KEYS.map(key => {
            const m = INDICATOR_META[key];
            const active = activeIndicator === key;
            const cnt = customCountByIndicator[key] || 0;
            return (
              <button key={key} onClick={() => setActiveIndicator(key)} style={{
                background: active ? 'rgba(212,165,116,0.15)' : 'rgba(255,255,255,0.02)',
                border: `1px solid ${active ? '#d4a574' : '#2a3447'}`,
                color: active ? '#d4a574' : '#a0a8b8',
                padding: '0.5rem 0.85rem', borderRadius: '3px',
                fontSize: '0.8rem', cursor: 'pointer', fontFamily: 'inherit',
                display: 'flex', alignItems: 'center', gap: '0.3rem',
              }}>
                {m.label}
                {cnt > 0 && <Badge>{cnt}</Badge>}
              </button>
            );
          })}
        </div>
      </section>

      {tolerance != null && (
        <section style={{ background: 'rgba(255,255,255,0.02)', border: '1px solid #2a3447', borderRadius: '4px', padding: '1.25rem', marginBottom: '1.5rem' }}>
          <SectionTitle>{meta.label} 許容倍率(全業種共通)</SectionTitle>
          <div style={{ fontSize: '0.75rem', color: '#a0a8b8', marginBottom: '1rem', lineHeight: 1.6 }}>
            各業種の{meta.label}基準に対して、何倍まで許容するかの設定です。
            <br />例: 銀行のPER基準 12倍 × 許容1.2倍 → 14.4倍まで合格扱い。
          </div>
          <FilterSlider label="許容倍率" value={tolerance} setValue={setTolerance} min={1.0} max={2.0} step={0.05} unit=" 倍" />
        </section>
      )}

      <section style={{ background: 'rgba(255,255,255,0.02)', border: '1px solid #2a3447', borderRadius: '4px', padding: '1.25rem' }}>
        <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', flexWrap: 'wrap', gap: '0.5rem', marginBottom: '0.75rem' }}>
          <SectionTitle>業種別 {meta.label}({meta.direction === 'min' ? '最低' : '上限'}基準)</SectionTitle>
          <button onClick={() => resetIndicator(activeIndicator)} style={btnGold}>
            <RotateCcw size={12} /> この指標を全業種基準値に
          </button>
        </div>
        <div style={{ fontSize: '0.75rem', color: '#a0a8b8', marginBottom: '1.25rem', lineHeight: 1.6, display: 'flex', alignItems: 'flex-start', gap: '0.4rem' }}>
          <HelpCircle size={14} color="#d4a574" style={{ flexShrink: 0, marginTop: '2px' }} />
          <span>{meta.tooltip}</span>
        </div>

        <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(280px, 1fr))', gap: '0.75rem' }}>
          {SECTORS.map(sector => {
            const baseline = SECTOR_THRESHOLDS_DEFAULT[activeIndicator][sector];
            const current = thresholds[activeIndicator]?.[sector] ?? baseline;
            const isCustom = current !== baseline;
            return (
              <div key={sector} style={{ padding: '1rem', background: isCustom ? 'rgba(212,165,116,0.06)' : 'rgba(255,255,255,0.02)', border: `1px solid ${isCustom ? 'rgba(212,165,116,0.3)' : '#2a3447'}`, borderRadius: '3px' }}>
                <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: '0.5rem' }}>
                  <span style={{ fontSize: '0.85rem', fontWeight: 500 }}>{sector}</span>
                  {isCustom && (
                    <button onClick={() => resetSectorThreshold(activeIndicator, sector)} title="この業種を基準値に戻す"
                      style={{ background: 'none', border: 'none', color: '#7a8499', cursor: 'pointer', padding: '2px', display: 'flex', alignItems: 'center' }}>
                      <RotateCcw size={12} />
                    </button>
                  )}
                </div>
                <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'baseline', marginBottom: '0.4rem' }}>
                  <span style={{ fontSize: '0.7rem', color: '#7a8499', fontFamily: 'monospace' }}>
                    基準値: <span style={{ color: '#a0a8b8' }}>{baseline}{meta.unit}</span>
                  </span>
                  <span style={{ fontFamily: 'monospace', color: isCustom ? '#d4a574' : '#e8ecf1', fontSize: '1rem', fontWeight: 600 }}>
                    {Number(current).toFixed(meta.step < 1 ? (meta.step <= 0.1 ? 1 : 2) : 0)}{meta.unit}
                  </span>
                </div>
                <input type="range"
                  min={meta.sliderMin} max={meta.sliderMax} step={meta.step}
                  value={current}
                  onChange={e => updateThreshold(activeIndicator, sector, parseFloat(e.target.value))}
                  style={{ width: '100%', accentColor: isCustom ? '#d4a574' : '#5a6478' }}
                />
                <div style={{ display: 'flex', justifyContent: 'space-between', fontSize: '0.65rem', color: '#5a6478', fontFamily: 'monospace', marginTop: '2px' }}>
                  <span>{meta.sliderMin}</span>
                  {tolerance != null ? (
                    <span style={{ color: '#7a8499' }}>※許容上限: {(current * tolerance).toFixed(meta.step <= 0.1 ? 1 : 1)}{meta.unit}</span>
                  ) : (
                    <span style={{ color: '#7a8499' }}>{meta.direction === 'min' ? 'これ以上' : 'これ以下'}を合格</span>
                  )}
                  <span>{meta.sliderMax}</span>
                </div>
              </div>
            );
          })}
        </div>

        {indCount > 0 && (
          <div style={{ marginTop: '1.25rem', padding: '0.75rem', background: 'rgba(212,165,116,0.05)', borderLeft: '2px solid #d4a574', fontSize: '0.75rem', color: '#a0a8b8' }}>
            ✏️ 「{meta.label}」は <span style={{ color: '#d4a574', fontFamily: 'monospace', fontWeight: 600 }}>{indCount}業種</span> がカスタム値に変更されています。
          </div>
        )}
      </section>
    </>
  );
}

function useViewportWidth() {
  const [w, setW] = useState(typeof window !== 'undefined' ? window.innerWidth : 1024);
  useEffect(() => {
    const handler = () => setW(window.innerWidth);
    window.addEventListener('resize', handler);
    return () => window.removeEventListener('resize', handler);
  }, []);
  return w;
}

// 狭い画面で動的に出せる「副カラム」のメタ情報
const NARROW_SECONDARY_FIELD_META = {
  per: { label: 'PER', render: (s, perOver) => ({ text: s.per != null ? s.per.toFixed(1) : '-', color: perOver ? '#c97064' : '#e8ecf1' }) },
  pbr: { label: 'PBR', render: (s, _, pbrOver) => ({ text: s.pbr != null ? s.pbr.toFixed(2) : '-', color: pbrOver ? '#c97064' : (s.pbr != null && s.pbr < 1.0 ? '#c9a876' : '#e8ecf1') }) },
  payout: { label: '性向', render: (s) => ({ text: s.payout != null ? `${s.payout.toFixed(1)}%` : '-', color: (s.payout ?? 0) > 60 ? '#c97064' : (s.payout ?? 0) >= 30 && (s.payout ?? 0) <= 50 ? '#5a9a7a' : '#e8ecf1' }) },
  opMargin: { label: '益率', render: (s) => ({ text: s.opMargin != null ? `${s.opMargin.toFixed(1)}%` : '-', color: (s.opMargin ?? 0) >= 10 ? '#5a9a7a' : '#e8ecf1' }) },
  growthYears: { label: '増配', render: (s) => ({ text: `${s.growthYears ?? 0}年`, color: '#e8ecf1' }) },
  marketCap: { label: '時価総額', render: (s) => ({ text: s.marketCap != null ? `${s.marketCap >= 10000 ? (s.marketCap/10000).toFixed(1)+'兆' : s.marketCap.toLocaleString()+'億'}` : '-', color: '#e8ecf1' }) },
};
const NARROW_SECONDARY_DEFAULT = 'per';

function StockTable({ stocks, expanded, setExpanded, favorites, toggleFavorite, sortBy, sortDesc, toggleSort, showSort, thresholds, perTolerance, pbrTolerance, memos = {}, setMemo, displayLimit = Infinity, onShowMore, groupBySector = false }) {
  const vw = useViewportWidth();
  const isNarrow = vw < 600;
  // 狭い画面の副カラム: 並び替えてる列がカラムにない場合、その項目を出す。なければPER
  const narrowSecField = isNarrow && NARROW_SECONDARY_FIELD_META[sortBy] ? sortBy : NARROW_SECONDARY_DEFAULT;
  const narrowSecMeta = NARROW_SECONDARY_FIELD_META[narrowSecField];
  // 狭い画面では限定列のみ表示: ⭐ コード 銘柄 利回り [動的] 詳細(▼)
  // 隠した列(性向・営業益率・PBR)は行をタップで展開すれば見られる
  const cols = isNarrow ? '28px 50px 1fr 60px 60px 24px' : '32px 56px 1fr 70px 60px 60px 56px 56px 24px';

  if (stocks.length === 0) {
    return (
      <div style={{ background: 'rgba(255,255,255,0.02)', border: '1px solid #2a3447', borderRadius: '4px', padding: '3rem 1rem', textAlign: 'center', color: '#7a8499' }}>
        銘柄がありません。
      </div>
    );
  }

  const visible = isFinite(displayLimit) ? stocks.slice(0, displayLimit) : stocks;
  const hidden = stocks.length - visible.length;

  // 業種別グルーピング(順序: PRIMARY_SECTORS優先、その他はアルファベット)
  let groups = null;
  if (groupBySector) {
    const map = {};
    for (const s of visible) {
      if (!map[s.sector]) map[s.sector] = [];
      map[s.sector].push(s);
    }
    const order = [
      ...PRIMARY_SECTORS.filter(sec => map[sec]),
      ...Object.keys(map).filter(sec => !PRIMARY_SECTORS.includes(sec)).sort((a, b) => a.localeCompare(b, 'ja')),
    ];
    groups = order.map(sec => ({ sector: sec, stocks: map[sec] }));
  }

  // 数値を安全に表示する(nullなら "-")
  const fmt = (v, digits = 2, suffix = '') => v == null || isNaN(v) ? '-' : `${v.toFixed(digits)}${suffix}`;

  return (
    <section style={{ background: 'rgba(255,255,255,0.02)', border: '1px solid #2a3447', borderRadius: '4px' }}>
      <div style={{ display: 'grid', gridTemplateColumns: cols, padding: '0.85rem 1rem', borderBottom: '1px solid #2a3447', fontSize: '0.7rem', color: '#7a8499', letterSpacing: '0.1em', fontFamily: 'monospace', alignItems: 'center', position: 'sticky', top: '42px', zIndex: 10, background: '#0e1322', boxShadow: '0 2px 4px rgba(0,0,0,0.3)' }}>
        <div></div>
        <div>コード</div>
        {showSort ? (
          <SortHeader label="銘柄" field="sector" current={sortBy} desc={sortDesc} onClick={toggleSort} hint="業種" />
        ) : (
          <div>銘柄</div>
        )}
        {showSort ? (
          <>
            <SortHeader label="利回り" field="yield" current={sortBy} desc={sortDesc} onClick={toggleSort} />
            {!isNarrow && <SortHeader label="性向" field="payout" current={sortBy} desc={sortDesc} onClick={toggleSort} />}
            {!isNarrow && <SortHeader label="営業益率" field="opMargin" current={sortBy} desc={sortDesc} onClick={toggleSort} />}
            {isNarrow ? (
              <SortHeader label={narrowSecMeta.label} field={narrowSecField} current={sortBy} desc={sortDesc} onClick={toggleSort} />
            ) : (
              <SortHeader label="PER" field="per" current={sortBy} desc={sortDesc} onClick={toggleSort} />
            )}
            {!isNarrow && <SortHeader label="PBR" field="pbr" current={sortBy} desc={sortDesc} onClick={toggleSort} />}
          </>
        ) : (
          <>
            <div>利回り</div>
            {!isNarrow && <div>性向</div>}
            {!isNarrow && <div>営業益率</div>}
            <div>{isNarrow ? narrowSecMeta.label : 'PER'}</div>
            {!isNarrow && <div>PBR</div>}
          </>
        )}
        <div></div>
      </div>

      {(() => {
        const renderRow = (s, i, listLen) => {
          const sectorPer = thresholds.per?.[s.sector] ?? 15;
          const sectorPbr = thresholds.pbr?.[s.sector] ?? 1.5;
          const sectorPayout = thresholds.payout?.[s.sector] ?? 50;
          const baselinePer = SECTOR_THRESHOLDS_DEFAULT.per[s.sector] ?? 15;
          const baselinePbr = SECTOR_THRESHOLDS_DEFAULT.pbr[s.sector] ?? 1.5;
          const isPerCustom = sectorPer !== baselinePer;
          const isPbrCustom = sectorPbr !== baselinePbr;
          const isFav = favorites.includes(s.code);
          const perOver = s.per != null && s.per > sectorPer * perTolerance;
          const pbrOver = s.pbr != null && s.pbr > sectorPbr * pbrTolerance;
          const hasMemo = !!(memos[s.code] && memos[s.code].trim());
          return (
            <div key={s.code}>
            <div style={{ display: 'grid', gridTemplateColumns: cols, padding: '0.9rem 1rem', borderBottom: i < listLen - 1 ? '1px solid #1f2737' : 'none', fontSize: '0.85rem', alignItems: 'center' }}>
              <button onClick={(e) => { e.stopPropagation(); toggleFavorite(s.code); }} style={{ background: 'none', border: 'none', cursor: 'pointer', padding: '4px', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
                <Star size={16} fill={isFav ? '#d4a574' : 'none'} color={isFav ? '#d4a574' : '#4a5567'} />
              </button>
              <div onClick={() => setExpanded(expanded === s.code ? null : s.code)} style={{ fontFamily: 'monospace', color: '#7a8499', fontSize: '0.75rem', cursor: 'pointer' }}>{s.code}</div>
              <div onClick={() => setExpanded(expanded === s.code ? null : s.code)} style={{ cursor: 'pointer', minWidth: 0 }}>
                <div style={{ fontWeight: 500, display: 'flex', alignItems: 'center', gap: '0.4rem', whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>
                  <span style={{ overflow: 'hidden', textOverflow: 'ellipsis' }}>{s.name}</span>
                  {s.isDefensive && <span style={{ fontSize: '0.6rem', padding: '1px 5px', background: 'rgba(90,154,122,0.2)', color: '#5a9a7a', borderRadius: '2px', flexShrink: 0 }}>DEF</span>}
                  {hasMemo && <span title="メモあり" style={{ fontSize: '0.65rem', color: '#d4a574', flexShrink: 0 }}>📝</span>}
                </div>
                <div style={{ fontSize: '0.7rem', color: '#7a8499', marginTop: '2px', whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>{s.sector}</div>
              </div>
              <div onClick={() => setExpanded(expanded === s.code ? null : s.code)} style={{ fontFamily: 'monospace', color: (s.yield ?? 0) >= 4 ? '#d4a574' : '#e8ecf1', fontWeight: 600, cursor: 'pointer' }}>{fmt(s.yield, 2, '%')}</div>
              {!isNarrow && <div onClick={() => setExpanded(expanded === s.code ? null : s.code)} style={{ fontFamily: 'monospace', color: (s.payout ?? 0) > 60 ? '#c97064' : (s.payout ?? 0) >= 30 && (s.payout ?? 0) <= 50 ? '#5a9a7a' : '#e8ecf1', cursor: 'pointer' }}>{fmt(s.payout, 1, '%')}</div>}
              {!isNarrow && <div onClick={() => setExpanded(expanded === s.code ? null : s.code)} style={{ fontFamily: 'monospace', color: (s.opMargin ?? 0) >= 10 ? '#5a9a7a' : '#e8ecf1', cursor: 'pointer' }}>{fmt(s.opMargin, 1, '%')}</div>}
              {isNarrow ? (() => {
                const r = narrowSecMeta.render(s, perOver, pbrOver);
                return <div onClick={() => setExpanded(expanded === s.code ? null : s.code)} style={{ fontFamily: 'monospace', color: r.color, cursor: 'pointer', fontSize: '0.8rem' }}>{r.text}</div>;
              })() : (
                <div onClick={() => setExpanded(expanded === s.code ? null : s.code)} style={{ fontFamily: 'monospace', color: perOver ? '#c97064' : '#e8ecf1', cursor: 'pointer' }}>{fmt(s.per, 1)}</div>
              )}
              {!isNarrow && <div onClick={() => setExpanded(expanded === s.code ? null : s.code)} style={{ fontFamily: 'monospace', color: pbrOver ? '#c97064' : s.pbr != null && s.pbr < 1.0 ? '#c9a876' : '#e8ecf1', cursor: 'pointer' }}>{fmt(s.pbr, 2)}</div>}
              <div onClick={() => setExpanded(expanded === s.code ? null : s.code)} style={{ color: '#7a8499', cursor: 'pointer' }}>
                {expanded === s.code ? <ChevronUp size={14} /> : <ChevronDown size={14} />}
              </div>
            </div>
            {expanded === s.code && (
              <div style={{ padding: '1rem 1.5rem 1.25rem', background: 'rgba(212,165,116,0.03)', borderBottom: i < listLen - 1 ? '1px solid #1f2737' : 'none', fontSize: '0.8rem' }}>
                <div style={{ marginBottom: '0.75rem' }}>
                  <div style={{ fontSize: '0.7rem', color: '#d4a574', letterSpacing: '0.1em', marginBottom: '0.5rem', fontFamily: 'monospace' }}>業績トレンド</div>
                  <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(110px, 1fr))', gap: '0.5rem' }}>
                    <TrendBadge label="売上" trend={s.salesTrend} />
                    <TrendBadge label="EPS" trend={s.epsTrend} />
                    <TrendBadge label="営業CF" trend={s.opCfTrend} />
                    <TrendBadge label="1株配当" trend={s.divTrend} />
                    <TrendBadge label="利益剰余金" trend={s.retainedTrend} />
                  </div>
                </div>
                <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(110px, 1fr))', gap: '0.6rem 0.75rem', paddingTop: '0.75rem', borderTop: '1px solid #2a3447' }}>
                  <Detail label="株価" value={s.price != null ? `¥${s.price.toLocaleString()}` : '-'} />
                  <Detail label="配当性向" value={s.payout != null ? `${s.payout.toFixed(1)}%` : '-'} customColor={(s.payout ?? 0) > 60 ? '#c97064' : null} />
                  <Detail label="営業利益率" value={s.opMargin != null ? `${s.opMargin.toFixed(1)}%` : '-'} customColor={(s.opMargin ?? 0) >= 10 ? '#5a9a7a' : null} />
                  <Detail label="PBR" value={s.pbr != null ? s.pbr.toFixed(2) : '-'} customColor={pbrOver ? '#c97064' : (s.pbr != null && s.pbr < 1.0) ? '#c9a876' : null} />
                  <Detail label="EPS" value={s.eps != null ? `¥${s.eps.toFixed(1)}` : '-'} />
                  <Detail label="連続増配" value={`${s.growthYears ?? 0}年`} />
                  <Detail label="自己資本比率" value={s.equityRatio != null ? `${s.equityRatio.toFixed(1)}%` : '-'} />
                  <Detail label="時価総額" value={s.marketCap != null ? `${s.marketCap.toLocaleString()}億円` : '-'} />
                  <Detail label="業種PER基準"
                    value={<span>{sectorPer}倍{isPerCustom && <span style={{ fontSize: '0.65rem', color: '#7a8499', marginLeft: '4px' }}>(基準: {baselinePer})</span>}</span>}
                    customColor={isPerCustom ? '#d4a574' : null} />
                  <Detail label="業種PBR基準"
                    value={<span>{sectorPbr}倍{isPbrCustom && <span style={{ fontSize: '0.65rem', color: '#7a8499', marginLeft: '4px' }}>(基準: {baselinePbr})</span>}</span>}
                    customColor={isPbrCustom ? '#d4a574' : null} />
                </div>

                {/* メモ欄 */}
                {setMemo && (
                  <div style={{ marginTop: '0.85rem', paddingTop: '0.75rem', borderTop: '1px solid #2a3447' }}>
                    <div style={{ fontSize: '0.7rem', color: '#d4a574', letterSpacing: '0.1em', marginBottom: '0.4rem', fontFamily: 'monospace' }}>メモ</div>
                    <textarea
                      value={memos[s.code] || ''}
                      onChange={(e) => setMemo(s.code, e.target.value)}
                      placeholder="この銘柄のメモ(なぜ気になったか、四季報チェック予定、買付タイミングなど)"
                      style={{ width: '100%', minHeight: '60px', background: 'rgba(0,0,0,0.25)', border: '1px solid #2a3447', borderRadius: '3px', color: '#e8ecf1', padding: '0.5rem 0.6rem', fontSize: '0.78rem', fontFamily: 'inherit', resize: 'vertical', boxSizing: 'border-box' }}
                    />
                  </div>
                )}

                {/* 警告 */}
                {s.payout != null && s.payout > 80 && <Warning text="配当性向が高い(80%超)。減配リスクに注意。" />}
                {s.payout != null && s.payout > sectorPayout && s.epsTrend === 'down' && <Warning level="danger" text="減配リスク高: 業種基準を超える配当性向 × EPS下降。" />}
                {(s.growthYears ?? 0) < 3 && s.payout != null && s.payout > sectorPayout * 0.8 && <Warning text="実績不足の高配当: 連続増配3年未満で業種基準の8割を超える配当性向。" />}
                {s.opMargin != null && s.opMargin < 5 && <Warning text="営業利益率が低め(5%未満)。" />}
                {perOver && <Warning text={`PERが許容上限(業種${sectorPer}倍 × ${perTolerance}倍 = ${(sectorPer * perTolerance).toFixed(1)}倍)を超過。`} />}
                {pbrOver && <Warning text={`PBRが許容上限(業種${sectorPbr}倍 × ${pbrTolerance}倍 = ${(sectorPbr * pbrTolerance).toFixed(2)}倍)を超過。`} />}
                {s.pbr != null && s.pbr < 1.0 && <Warning level="info" text="PBR1倍割れ。資本効率改善余地あり(東証改革対象の可能性)。" />}
                {s.epsTrend === 'down' && <Warning text="EPSが下降トレンド。将来の減配リスクあり。" />}
              </div>
            )}
          </div>
          );
        };

        if (groups) {
          return groups.map(g => (
            <div key={g.sector}>
              <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', padding: '0.5rem 1rem', background: '#1a1f33', borderTop: '1px solid #2a3447', borderBottom: '1px solid #2a3447', fontSize: '0.78rem', position: 'sticky', top: '79px', zIndex: 5 }}>
                <span style={{ color: '#d4a574', fontWeight: 600, letterSpacing: '0.05em' }}>{g.sector}</span>
                <span style={{ color: '#7a8499', fontFamily: 'monospace', fontSize: '0.7rem' }}>{g.stocks.length}社</span>
              </div>
              {g.stocks.map((s, i) => renderRow(s, i, g.stocks.length))}
            </div>
          ));
        }
        return visible.map((s, i) => renderRow(s, i, visible.length));
      })()}

      {hidden > 0 && onShowMore && (
        <div style={{ padding: '0.75rem 1rem', borderTop: '1px solid #2a3447', background: 'rgba(212,165,116,0.04)', textAlign: 'center' }}>
          <button onClick={onShowMore} style={{ background: 'rgba(212,165,116,0.15)', border: '1px solid rgba(212,165,116,0.3)', color: '#d4a574', padding: '0.5rem 1rem', borderRadius: '3px', fontSize: '0.8rem', cursor: 'pointer', fontFamily: 'inherit' }}>
            さらに{Math.min(hidden, DISPLAY_LIMIT_STEP)}件表示 (残り{hidden}件)
          </button>
        </div>
      )}
    </section>
  );
}

function SortBar({ sortBy, sortDesc, toggleSort }) {
  return (
    <div style={{ display: 'flex', gap: '0.3rem', alignItems: 'center', overflowX: 'auto', padding: '0.4rem 0.6rem', background: '#0e1322', border: '1px solid #2a3447', borderRadius: '3px', marginBottom: '0.75rem', WebkitOverflowScrolling: 'touch', position: 'sticky', top: 0, zIndex: 12, boxShadow: '0 2px 4px rgba(0,0,0,0.3)' }}>
      <span style={{ color: '#7a8499', fontSize: '0.7rem', flexShrink: 0, fontFamily: 'monospace', letterSpacing: '0.05em' }}>並び替え</span>
      {SORT_FIELDS.map(([f, label]) => {
        const active = sortBy === f;
        return (
          <button key={f} onClick={() => toggleSort(f)} style={{
            background: active ? 'rgba(212,165,116,0.18)' : 'rgba(255,255,255,0.04)',
            border: `1px solid ${active ? '#d4a574' : '#2a3447'}`,
            color: active ? '#d4a574' : '#a0a8b8',
            padding: '0.3rem 0.65rem',
            borderRadius: '3px',
            cursor: 'pointer',
            fontFamily: 'inherit',
            fontSize: '0.75rem',
            whiteSpace: 'nowrap',
            flexShrink: 0,
          }}>
            {label}{active && (sortDesc ? ' ↓' : ' ↑')}
          </button>
        );
      })}
    </div>
  );
}

function SectorFilter({ sectorFilter, toggleSector, setAllSectors, showSecondary, setShowSecondary }) {
  const totalSelected = sectorFilter.size;
  return (
    <section style={{ background: 'rgba(255,255,255,0.02)', border: '1px solid #2a3447', borderRadius: '4px', padding: '1.25rem', marginBottom: '1.5rem' }}>
      <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', flexWrap: 'wrap', gap: '0.5rem', marginBottom: '0.75rem' }}>
        <SectionTitle>業種フィルター</SectionTitle>
        <div style={{ display: 'flex', gap: '0.4rem', fontSize: '0.7rem', alignItems: 'center' }}>
          <span style={{ color: '#7a8499', fontFamily: 'monospace' }}>{totalSelected}/{SECTORS.length}選択</span>
          <button onClick={() => setAllSectors(true)} style={{ background: 'rgba(212,165,116,0.1)', border: '1px solid rgba(212,165,116,0.3)', color: '#d4a574', padding: '0.25rem 0.5rem', borderRadius: '3px', fontSize: '0.7rem', cursor: 'pointer', fontFamily: 'inherit' }}>全選択</button>
          <button onClick={() => setAllSectors(false)} style={{ background: 'rgba(255,255,255,0.04)', border: '1px solid #2a3447', color: '#a0a8b8', padding: '0.25rem 0.5rem', borderRadius: '3px', fontSize: '0.7rem', cursor: 'pointer', fontFamily: 'inherit' }}>全解除</button>
        </div>
      </div>
      <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(140px, 1fr))', gap: '0.3rem 0.5rem' }}>
        {PRIMARY_SECTORS.map(sec => (
          <SectorCheckBox key={sec} label={sec} checked={sectorFilter.has(sec)} onChange={() => toggleSector(sec)} />
        ))}
      </div>
      <div style={{ marginTop: '0.75rem' }}>
        <button onClick={() => setShowSecondary(s => !s)} style={{ background: 'none', border: 'none', color: '#7a8499', fontSize: '0.75rem', cursor: 'pointer', fontFamily: 'inherit', padding: '0.25rem 0' }}>
          {showSecondary ? '▲' : '▼'} その他{SECONDARY_SECTORS.length}業種を{showSecondary ? '隠す' : '表示'}
        </button>
        {showSecondary && (
          <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(140px, 1fr))', gap: '0.3rem 0.5rem', marginTop: '0.4rem' }}>
            {SECONDARY_SECTORS.map(sec => (
              <SectorCheckBox key={sec} label={sec} checked={sectorFilter.has(sec)} onChange={() => toggleSector(sec)} />
            ))}
          </div>
        )}
      </div>
    </section>
  );
}

function SectorCheckBox({ label, checked, onChange }) {
  return (
    <label style={{ display: 'flex', alignItems: 'center', gap: '0.4rem', cursor: 'pointer', fontSize: '0.75rem', padding: '0.25rem 0', color: checked ? '#e8ecf1' : '#7a8499' }}>
      <input type="checkbox" checked={checked} onChange={onChange} style={{ accentColor: '#d4a574' }} />
      <span style={{ overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{label}</span>
    </label>
  );
}

function FilterSlider({ label, value, setValue, min, max, step, unit }) {
  return (
    <div>
      <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'baseline', marginBottom: '0.5rem' }}>
        <span style={{ fontSize: '0.8rem', color: '#a0a8b8' }}>{label}</span>
        <span style={{ fontFamily: 'monospace', color: '#d4a574', fontSize: '0.9rem', fontWeight: 600 }}>
          {value}{unit}
        </span>
      </div>
      <input type="range" min={min} max={max} step={step} value={value} onChange={e => setValue(parseFloat(e.target.value))} style={{ width: '100%', accentColor: '#d4a574' }} />
    </div>
  );
}

function CheckBox({ label, checked, onChange, tooltip }) {
  return (
    <label style={{ display: 'flex', alignItems: 'center', gap: '0.5rem', cursor: 'pointer', fontSize: '0.8rem', padding: '0.4rem 0' }}>
      <input type="checkbox" checked={checked} onChange={e => onChange(e.target.checked)} style={{ accentColor: '#d4a574' }} />
      <span>{label}</span>
      {tooltip && <Tooltip text={tooltip} />}
    </label>
  );
}

function IndicatorCheckBox({ indicator, checked, onChange }) {
  const m = INDICATOR_META[indicator];
  const dirText = m.direction === 'min' ? '最低基準を満たす' : '上限基準内';
  return (
    <label style={{ display: 'flex', alignItems: 'center', gap: '0.5rem', cursor: 'pointer', fontSize: '0.8rem', padding: '0.4rem 0' }}>
      <input type="checkbox" checked={checked} onChange={e => onChange(e.target.checked)} style={{ accentColor: '#d4a574' }} />
      <span>{m.label}<span style={{ fontSize: '0.7rem', color: '#7a8499', marginLeft: '0.3rem' }}>({dirText})</span></span>
      <Tooltip text={m.tooltip} />
    </label>
  );
}

function Tooltip({ text }) {
  const [show, setShow] = useState(false);
  return (
    <span style={{ position: 'relative', display: 'inline-flex', alignItems: 'center' }}
      onMouseEnter={() => setShow(true)} onMouseLeave={() => setShow(false)}
      onClick={(e) => { e.preventDefault(); setShow(s => !s); }}>
      <HelpCircle size={12} color="#7a8499" style={{ cursor: 'help' }} />
      {show && (
        <span style={{ position: 'absolute', bottom: '100%', left: '50%', transform: 'translateX(-50%)', marginBottom: '6px', background: '#0a0e1a', border: '1px solid #d4a574', color: '#e8ecf1', fontSize: '0.7rem', padding: '0.5rem 0.7rem', borderRadius: '3px', whiteSpace: 'normal', minWidth: '200px', maxWidth: '260px', lineHeight: 1.5, zIndex: 10, boxShadow: '0 4px 12px rgba(0,0,0,0.4)' }}>
          {text}
        </span>
      )}
    </span>
  );
}

function SortHeader({ label, field, current, desc, onClick, hint }) {
  const active = current === field;
  return (
    <div onClick={() => onClick(field)} style={{ cursor: 'pointer', color: active ? '#d4a574' : '#7a8499', userSelect: 'none' }} title={hint ? `クリックで${hint}順に並び替え` : undefined}>
      {label}{hint && !active && <span style={{ fontSize: '0.6rem', marginLeft: '0.2rem', opacity: 0.6 }}>({hint}↕)</span>} {active && (desc ? '↓' : '↑')}
    </div>
  );
}

function Detail({ label, value, customColor }) {
  return (
    <div>
      <div style={{ fontSize: '0.7rem', color: '#7a8499', marginBottom: '2px', letterSpacing: '0.05em' }}>{label}</div>
      <div style={{ fontFamily: 'monospace', color: customColor || '#e8ecf1' }}>{value}</div>
    </div>
  );
}

function Warning({ text, level }) {
  const colors = level === 'danger'
    ? { bg: 'rgba(201,112,100,0.18)', border: '#c97064', text: '#c97064', icon: '🔴' }
    : level === 'info'
      ? { bg: 'rgba(201,168,118,0.1)', border: '#c9a876', text: '#c9a876', icon: 'ℹ️' }
      : { bg: 'rgba(201,112,100,0.1)', border: '#c97064', text: '#c97064', icon: '⚠' };
  return (
    <div style={{ marginTop: '0.6rem', padding: '0.5rem 0.75rem', background: colors.bg, borderLeft: `2px solid ${colors.border}`, fontSize: '0.75rem', color: colors.text }}>
      {colors.icon} {text}
    </div>
  );
}

function SectionTitle({ children }) {
  return (
    <div style={{ fontSize: '0.75rem', color: '#d4a574', letterSpacing: '0.15em', fontFamily: 'monospace', marginBottom: '1rem', paddingBottom: '0.5rem', borderBottom: '1px solid rgba(212,165,116,0.2)' }}>
      {children}
    </div>
  );
}

function TabButton({ active, onClick, children }) {
  return (
    <button onClick={onClick}
      style={{ background: 'none', border: 'none', color: active ? '#d4a574' : '#7a8499', padding: '0.75rem 1.25rem', fontSize: '0.85rem', cursor: 'pointer', borderBottom: active ? '2px solid #d4a574' : '2px solid transparent', marginBottom: '-1px', display: 'flex', alignItems: 'center', gap: '0.4rem', fontFamily: 'inherit', whiteSpace: 'nowrap' }}>
      {children}
    </button>
  );
}

function Badge({ children }) {
  return (
    <span style={{ background: '#d4a574', color: '#0a0e1a', fontSize: '0.65rem', padding: '1px 6px', borderRadius: '8px', fontWeight: 700, marginLeft: '4px', fontFamily: 'monospace' }}>
      {children}
    </span>
  );
}

function TrendBadge({ label, trend }) {
  const t = TREND_LABELS[trend];
  return (
    <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', padding: '0.4rem 0.6rem', background: 'rgba(255,255,255,0.02)', borderRadius: '3px', fontSize: '0.75rem' }}>
      <span style={{ color: '#a0a8b8' }}>{label}</span>
      <span style={{ color: t.color, fontWeight: 600 }}>{t.icon} {t.label}</span>
    </div>
  );
}

function CriteriaCheck({ label, value, target, pass, detail }) {
  return (
    <div style={{ padding: '1rem', background: pass ? 'rgba(90,154,122,0.08)' : 'rgba(201,112,100,0.08)', borderLeft: `3px solid ${pass ? '#5a9a7a' : '#c97064'}`, borderRadius: '2px' }}>
      <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start', marginBottom: '0.5rem' }}>
        <span style={{ fontSize: '0.85rem', color: '#e8ecf1' }}>{label}</span>
        {pass ? <CheckCircle2 size={16} color="#5a9a7a" /> : <XCircle size={16} color="#c97064" />}
      </div>
      <div style={{ fontFamily: 'monospace', fontSize: '1.3rem', color: pass ? '#5a9a7a' : '#c97064', fontWeight: 600, marginBottom: '0.25rem' }}>{value}</div>
      <div style={{ fontSize: '0.7rem', color: '#7a8499', fontFamily: 'monospace' }}>目標: {target}</div>
      <div style={{ fontSize: '0.7rem', color: '#a0a8b8', marginTop: '0.4rem' }}>{detail}</div>
    </div>
  );
}

function MiniStat({ label, value, highlight }) {
  return (
    <div style={{ padding: '0.75rem', background: 'rgba(255,255,255,0.02)', borderRadius: '3px', borderLeft: `2px solid ${highlight ? '#5a9a7a' : '#3a4459'}` }}>
      <div style={{ fontSize: '0.7rem', color: '#7a8499', marginBottom: '0.25rem' }}>{label}</div>
      <div style={{ fontFamily: 'monospace', fontSize: '1.1rem', color: highlight ? '#5a9a7a' : '#e8ecf1', fontWeight: 600 }}>{value}</div>
    </div>
  );
}

function DataStatusBadge({ status, fetchedAt, count, error, onReload }) {
  const fmt = (iso) => {
    if (!iso) return '';
    const d = new Date(iso);
    if (isNaN(d.getTime())) return iso;
    const pad = (n) => String(n).padStart(2, '0');
    return `${d.getFullYear()}/${pad(d.getMonth()+1)}/${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}`;
  };

  const baseStyle = { display: 'flex', alignItems: 'center', gap: '0.4rem', fontSize: '0.7rem', fontFamily: 'monospace', whiteSpace: 'nowrap' };
  const btn = { background: 'rgba(212,165,116,0.1)', border: '1px solid rgba(212,165,116,0.3)', color: '#d4a574', padding: '0.3rem 0.55rem', borderRadius: '3px', fontSize: '0.7rem', cursor: status === 'loading' ? 'wait' : 'pointer', display: 'flex', alignItems: 'center', gap: '0.25rem', fontFamily: 'inherit', opacity: status === 'loading' ? 0.6 : 1 };

  if (status === 'loading' && count === 0) {
    return <div style={{ ...baseStyle, color: '#7a8499' }}>データ取得中...</div>;
  }
  if (status === 'error') {
    return (
      <div style={baseStyle}>
        <span style={{ color: '#c97064' }}>取得失敗 {error ? `(${error})` : ''}</span>
        <button onClick={onReload} style={btn} disabled={status === 'loading'}>
          <RotateCcw size={11} /> 再取得
        </button>
      </div>
    );
  }
  return (
    <div style={baseStyle}>
      <span style={{ color: '#7a8499' }}>最終更新</span>
      <span style={{ color: '#d4a574' }}>{fmt(fetchedAt)}</span>
      <span style={{ color: '#7a8499' }}>· {count}銘柄</span>
      <button onClick={onReload} style={btn} disabled={status === 'loading'} title="サーバーからdata.jsonを再ダウンロード(キャッシュをクリア)">
        <RotateCcw size={11} /> {status === 'loading' ? '...' : '再読込'}
      </button>
    </div>
  );
}

// --- マウント ---
ReactDOM.createRoot(document.getElementById('root')).render(<DividendScreener />);
