// ===== 経営分析 =====
function AnalyticsPage({ bookings, customers, settings, blocks, onSetSettings }) {
  blocks = blocks || [];
  // 月別売上・日別売上のナビ用 state
  const _now = new Date();
  const [monthlyYear, setMonthlyYear] = useState(_now.getFullYear());
  const [dailyYM, setDailyYM] = useState({ year: _now.getFullYear(), month: _now.getMonth() + 1 });
  const [opYM, setOpYM] = useState({ year: _now.getFullYear(), month: _now.getMonth() + 1 });
  // チャネル別広告費テーブルの表示年（過去年も切り替えできる）
  const [channelYear, setChannelYear] = useState(_now.getFullYear());
  const todayISO = D.isoDate(new Date());
  const today = new Date();
  const thisYear = today.getFullYear();
  const thisMonth = today.getMonth() + 1; // 1-12

  // done 予約だけ集計対象
  const doneBookings = useMemo(() => bookings.filter(b => b.status === 'done' && b.date), [bookings]);

  // ----- 年別売上 -----
  const yearlyRevenue = useMemo(() => {
    const m = new Map();
    doneBookings.forEach(b => {
      const y = b.date.slice(0, 4);
      m.set(y, (m.get(y) || 0) + (b.price || 0));
    });
    return Array.from(m.entries()).sort((a, b) => a[0].localeCompare(b[0]));
  }, [doneBookings]);

  // ----- 月別売上（選択された年）-----
  const monthlyRevenue = useMemo(() => {
    const arr = Array.from({ length: 12 }, (_, i) => ({ month: i + 1, revenue: 0, count: 0 }));
    doneBookings.forEach(b => {
      const y = parseInt(b.date.slice(0, 4), 10);
      const mo = parseInt(b.date.slice(5, 7), 10);
      if (y === monthlyYear && mo >= 1 && mo <= 12) {
        arr[mo - 1].revenue += (b.price || 0);
        arr[mo - 1].count += 1;
      }
    });
    return arr;
  }, [doneBookings, monthlyYear]);
  const monthlyTotal = useMemo(() => monthlyRevenue.reduce((s, m) => s + m.revenue, 0), [monthlyRevenue]);

  // ----- 日別売上（選択された年月）-----
  const dailyRevenue = useMemo(() => {
    const ym = `${dailyYM.year}-${String(dailyYM.month).padStart(2, '0')}`;
    const lastDay = new Date(dailyYM.year, dailyYM.month, 0).getDate();
    const arr = Array.from({ length: lastDay }, (_, i) => ({ day: i + 1, revenue: 0, count: 0, items: [] }));
    doneBookings.forEach(b => {
      if (b.date.startsWith(ym)) {
        const d = parseInt(b.date.slice(8, 10), 10);
        if (d >= 1 && d <= lastDay) {
          arr[d - 1].revenue += (b.price || 0);
          arr[d - 1].count += 1;
          arr[d - 1].items.push({
            time: b.time || '',
            name: b.name || b.customer_name || '—',
            menu: b.menu || b.menu_name || '',
            price: b.price || 0,
          });
        }
      }
    });
    arr.forEach(d => d.items.sort((a, b) => (a.time || '').localeCompare(b.time || '')));
    return arr;
  }, [doneBookings, dailyYM]);
  const dailyTotal = useMemo(() => dailyRevenue.reduce((s, d) => s + d.revenue, 0), [dailyRevenue]);
  // 曜日表記
  const WEEKDAY_JA = ['日','月','火','水','木','金','土'];

  // ----- 顧客指標 -----
  const totalRevenue = useMemo(() => doneBookings.reduce((s, b) => s + (b.price || 0), 0), [doneBookings]);
  const customersWithVisits = customers.filter(c => (c.visits || 0) >= 1);
  const ltv = customersWithVisits.length === 0 ? 0 : Math.round(totalRevenue / customersWithVisits.length);
  const repeaters = customers.filter(c => (c.visits || 0) >= 2).length;
  const repeatRate = customersWithVisits.length === 0 ? 0 : Math.round(repeaters / customersWithVisits.length * 100);

  // ----- 営業日数・稼働率・時間単価・分単価 -----
  const operationalMetrics = useMemo(() => {
    const opYear = opYM.year;
    const opMonth = opYM.month;
    const lastDay = new Date(opYear, opMonth, 0).getDate();
    const blockedAllDay = new Set(
      (blocks || [])
        .filter(b => b.allDay || b.type === 'full_day')
        .map(b => b.date || b.start_date)
        .filter(Boolean)
    );
    let businessDays = 0;
    let businessMinutes = 0;
    const hours = (settings && Array.isArray(settings.hours)) ? settings.hours : [];
    for (let day = 1; day <= lastDay; day++) {
      const d = new Date(opYear, opMonth - 1, day);
      const dow = d.getDay();
      const cfg = hours.find(h => h.day === dow);
      if (!cfg || !cfg.open) continue;
      const dateISO = `${opYear}-${String(opMonth).padStart(2,'0')}-${String(day).padStart(2,'0')}`;
      if (blockedAllDay.has(dateISO)) continue;
      // 営業時間（分）
      const fromMin = D.timeToMin(cfg.from || '9:00');
      const toMin = D.timeToMin(cfg.to || '20:00');
      const dayMinutes = Math.max(0, toMin - fromMin);
      businessDays += 1;
      businessMinutes += dayMinutes;
    }

    // 対象月の done 予約の実施術分数・売上
    const monthStr = `${opYear}-${String(opMonth).padStart(2,'0')}`;
    const monthDoneList = doneBookings.filter(b => b.date && b.date.startsWith(monthStr));
    const treatmentMinutes = monthDoneList.reduce((s, b) => s + (b.duration || 60), 0);
    const monthRevenue = monthDoneList.reduce((s, b) => s + (b.price || 0), 0);
    const visitCount = monthDoneList.length;

    // 1日の最大施術人数（設定で変更可、デフォルト8人）
    const maxPerDay = Number(settings.maxCustomersPerDay) || 8;
    // 月の最大施術可能枠（営業日数 × 1日MAX人数）
    const maxSlots = businessDays * maxPerDay;
    // 稼働率（枠ベース）：来店件数 / 最大枠
    const utilization = maxSlots === 0 ? 0 : (visitCount / maxSlots * 100);
    // 時間単価（実施術ベース）：1時間施術するといくら
    const hourPrice = treatmentMinutes === 0 ? 0 : Math.round(monthRevenue / (treatmentMinutes / 60));
    // 分単価（施術ベース）：1分施術するといくら（実働分あたりの売上）
    const minutePrice = treatmentMinutes === 0 ? 0 : Math.round(monthRevenue / treatmentMinutes);
    // 来院単価（1回の来院あたり）
    const visitPrice = visitCount === 0 ? 0 : Math.round(monthRevenue / visitCount);
    // ユニーク顧客数（同じ顧客を1人としてカウント）
    const uniqueCustomerIds = new Set(monthDoneList.map(b => b.customerId || b.customer_id || b.name).filter(Boolean));
    const uniqueCustomers = uniqueCustomerIds.size;
    // 顧客月額単価（1人の客が月にいくら支払うか）
    const monthlyPerCustomer = uniqueCustomers === 0 ? 0 : Math.round(monthRevenue / uniqueCustomers);

    // 営業利益 = 月売上 − その月の広告費 − 月固定費
    const opMonthKey = `${opYear}-${String(opMonth).padStart(2,'0')}`;
    const adByChannel = (settings && settings.adCostByChannel && settings.adCostByChannel[opMonthKey]) || {};
    const monthlyAdCost = Object.values(adByChannel).reduce((s, v) => s + (Number(v) || 0), 0);
    // 月固定費：配列形式（monthlyFixedCosts）優先、無ければ旧形式（monthlyFixedCost: number）にフォールバック
    let monthlyFixedCost = 0;
    if (Array.isArray(settings.monthlyFixedCosts) && settings.monthlyFixedCosts.length > 0) {
      monthlyFixedCost = settings.monthlyFixedCosts.reduce((s, it) => s + (Number(it.amount) || 0), 0);
    } else {
      monthlyFixedCost = Number(settings.monthlyFixedCost) || 0;
    }
    const operatingProfit = monthRevenue - monthlyAdCost - monthlyFixedCost;
    const profitRate = monthRevenue === 0 ? 0 : Math.round(operatingProfit / monthRevenue * 100);

    return {
      businessDays, businessMinutes, treatmentMinutes, monthRevenue, visitCount,
      utilization, hourPrice, minutePrice,
      visitPrice, uniqueCustomers, monthlyPerCustomer,
      maxPerDay, maxSlots,
      monthlyAdCost, monthlyFixedCost, operatingProfit, profitRate,
    };
  }, [opYM, settings, blocks, doneBookings]);

  // ----- 年度別 運営効率の平均 -----
  const yearlyOperational = useMemo(() => {
    // doneBookings から年×月マップを作る
    const byYearMonth = new Map(); // 'YYYY-MM' -> [bookings]
    doneBookings.forEach(b => {
      if (!b.date) return;
      const ym = b.date.slice(0, 7);
      if (!byYearMonth.has(ym)) byYearMonth.set(ym, []);
      byYearMonth.get(ym).push(b);
    });
    const blockedAllDay = new Set(
      (blocks || [])
        .filter(b => b.type === 'date_full' || (!b.start_time && !b.end_time))
        .map(b => b.start_date || b.startDate)
        .filter(Boolean)
    );
    const hours = (settings && Array.isArray(settings.hours)) ? settings.hours : [];
    const maxPerDay = Number(settings.maxCustomersPerDay) || 8;

    // 年ごとに集計
    const byYear = new Map();
    for (const [ym, bks] of byYearMonth.entries()) {
      const [year, month] = ym.split('-').map(n => parseInt(n, 10));
      // 営業日数・営業分
      const lastDay = new Date(year, month, 0).getDate();
      let bDays = 0, bMinutes = 0;
      for (let day = 1; day <= lastDay; day++) {
        const d = new Date(year, month - 1, day);
        const cfg = hours.find(h => h.day === d.getDay());
        if (!cfg || !cfg.open) continue;
        const dateISO = `${year}-${String(month).padStart(2,'0')}-${String(day).padStart(2,'0')}`;
        if (blockedAllDay.has(dateISO)) continue;
        bDays += 1;
        bMinutes += Math.max(0, D.timeToMin(cfg.to || '20:00') - D.timeToMin(cfg.from || '9:00'));
      }
      const treatmentMin = bks.reduce((s, b) => s + (b.duration || 60), 0);
      const rev = bks.reduce((s, b) => s + (b.price || 0), 0);
      const visits = bks.length;
      const uniq = new Set(bks.map(b => b.customerId || b.customer_id || b.name).filter(Boolean)).size;
      const maxSlots = bDays * maxPerDay;
      const monthMetric = {
        month, businessDays: bDays, businessMinutes: bMinutes,
        treatmentMinutes: treatmentMin, revenue: rev, visits, uniqueCustomers: uniq,
        utilization: maxSlots === 0 ? 0 : (visits / maxSlots * 100),
        hourPrice: treatmentMin === 0 ? 0 : Math.round(rev / (treatmentMin / 60)),
        minutePrice: treatmentMin === 0 ? 0 : Math.round(rev / treatmentMin),
        visitPrice: visits === 0 ? 0 : Math.round(rev / visits),
        monthlyPerCustomer: uniq === 0 ? 0 : Math.round(rev / uniq),
      };
      if (!byYear.has(year)) byYear.set(year, []);
      byYear.get(year).push(monthMetric);
    }
    // 年ごとに月平均を計算
    return Array.from(byYear.entries())
      .sort((a, b) => a[0] - b[0])
      .map(([year, months]) => {
        const cnt = months.length;
        const avg = (key) => Math.round(months.reduce((s, m) => s + m[key], 0) / cnt);
        const avgPct = (key) => Math.round(months.reduce((s, m) => s + m[key], 0) / cnt * 10) / 10;
        return {
          year,
          monthsCount: cnt,
          avgBusinessDays: avg('businessDays'),
          avgRevenue: avg('revenue'),
          avgVisits: avg('visits'),
          avgUniqueCustomers: avg('uniqueCustomers'),
          avgUtilization: avgPct('utilization'),
          avgHourPrice: avg('hourPrice'),
          avgMinutePrice: avg('minutePrice'),
          avgVisitPrice: avg('visitPrice'),
          avgMonthlyPerCustomer: avg('monthlyPerCustomer'),
          totalRevenue: months.reduce((s, m) => s + m.revenue, 0),
          totalVisits: months.reduce((s, m) => s + m.visits, 0),
        };
      });
  }, [doneBookings, settings, blocks]);

  // ----- 回数券・月額プラン 保有者一覧 -----
  const ticketHolders = useMemo(() => {
    const todayISO = D.isoDate(new Date());
    const list = [];
    customers.forEach(c => {
      const tickets = Array.isArray(c.tickets) ? c.tickets : [];
      tickets.forEach(t => {
        let typeLabel = t.type;
        let purchasedAt = t.purchasedAt || t.contractedAt || '';
        let remaining = '—';
        let total = '—';
        let status = 'active';
        if (t.type === 'ticket10') {
          const used = t.usedCount || 0;
          remaining = (t.totalCount || 10) - used;
          total = t.totalCount || 10;
          typeLabel = '回数券10回';
          if (remaining <= 0) status = 'used_up';
          if (t.expiresAt && t.expiresAt < todayISO) status = 'expired';
        } else if (t.type === 'monthly4') {
          // 期限切れなら新サイクル相当を表示
          let cur = t;
          if (todayISO > t.cycleEnd) {
            cur = { ...t, cycleStart: todayISO, cycleEnd: todayISO, cycleUsed: 0 };
          }
          const used = cur.cycleUsed || 0;
          remaining = (cur.monthlyTotal || 4) - used;
          total = cur.monthlyTotal || 4;
          typeLabel = '月額プラン';
        }
        list.push({
          customerId: c.id,
          customerName: c.name,
          ticketId: t.id,
          type: t.type,
          typeLabel,
          purchasedAt,
          remaining,
          total,
          pricePerVisit: t.pricePerVisit || 0,
          status,
          cycleEnd: t.cycleEnd,
        });
      });
    });
    // 購入日降順
    list.sort((a, b) => (b.purchasedAt || '').localeCompare(a.purchasedAt || ''));
    return list;
  }, [customers]);

  // ----- 回数券・月額プラン集計 -----
  const ticketStats = useMemo(() => {
    const monthStr = `${thisYear}-${String(thisMonth).padStart(2, '0')}`;
    let ticket10ThisMonth = 0;
    let monthly4ThisMonth = 0;
    let activeTicket10 = 0;
    let activeMonthly4 = 0;
    let totalRemaining10 = 0;
    customers.forEach(c => {
      const tickets = Array.isArray(c.tickets) ? c.tickets : [];
      tickets.forEach(t => {
        if (t.type === 'ticket10') {
          if (t.purchasedAt && t.purchasedAt.startsWith(monthStr)) ticket10ThisMonth += 1;
          if ((t.usedCount || 0) < (t.totalCount || 10)) {
            activeTicket10 += 1;
            totalRemaining10 += ((t.totalCount || 10) - (t.usedCount || 0));
          }
        }
        if (t.type === 'monthly4') {
          if (t.contractedAt && t.contractedAt.startsWith(monthStr)) monthly4ThisMonth += 1;
          if (t.status === 'active') activeMonthly4 += 1;
        }
      });
    });
    return {
      ticket10ThisMonth,
      monthly4ThisMonth,
      activeTicket10,
      activeMonthly4,
      totalRemaining10,
      ticket10MonthSales: ticket10ThisMonth * 72000,
      monthly4MonthSales: monthly4ThisMonth * 28000,
    };
  }, [customers, thisYear, thisMonth]);

  // ----- リピートステージ別の分析 -----
  const stageAnalysis = useMemo(() => {
    const stages = [
      { key: 'first',   label: '初回のみ', test: (c) => (c.visits || 0) === 1 },
      { key: 'second',  label: '2回目',   test: (c) => (c.visits || 0) === 2 },
      { key: 'regular', label: '3回以上', test: (c) => (c.visits || 0) >= 3 },
    ];
    const total = customersWithVisits.length;
    return stages.map(s => {
      const list = customers.filter(c => s.test(c));
      const cnt = list.length;
      const rev = list.reduce((sum, c) => sum + (c.totalSpent || 0), 0);
      const ltv = cnt === 0 ? 0 : Math.round(rev / cnt);
      const ratio = total === 0 ? 0 : Math.round(cnt / total * 100);
      return { ...s, count: cnt, revenue: rev, ltv, ratio };
    });
  }, [customers, customersWithVisits]);

  // ----- 男女比・年代別・平均年齢（来院済顧客のみ） -----
  const demographics = useMemo(() => {
    const visited = customers.filter(c => (c.visits || 0) >= 1);
    const total = visited.length;
    // 男女比
    const female = visited.filter(c => c.gender === '女性').length;
    const male = visited.filter(c => c.gender === '男性').length;
    const otherG = visited.filter(c => c.gender && c.gender !== '男性' && c.gender !== '女性').length;
    const unknownG = total - female - male - otherG;
    // 年代別
    const ageBuckets = { '10s':0, '20s':0, '30s':0, '40s':0, '50s':0, '60s':0, '70s':0, '80+':0, unknown:0 };
    let ageSum = 0, ageCount = 0;
    visited.forEach(c => {
      const a = Number(c.age) || 0;
      if (a > 0) {
        ageSum += a; ageCount++;
        if (a < 20) ageBuckets['10s']++;
        else if (a < 30) ageBuckets['20s']++;
        else if (a < 40) ageBuckets['30s']++;
        else if (a < 50) ageBuckets['40s']++;
        else if (a < 60) ageBuckets['50s']++;
        else if (a < 70) ageBuckets['60s']++;
        else if (a < 80) ageBuckets['70s']++;
        else ageBuckets['80+']++;
      } else {
        ageBuckets.unknown++;
      }
    });
    const avgAge = ageCount === 0 ? 0 : Math.round(ageSum / ageCount * 10) / 10;
    return { total, female, male, otherG, unknownG, ageBuckets, avgAge, ageCount };
  }, [customers]);

  // 新規 vs リピーター 売上構成比
  // 今年（thisYear）：今年初回来店した人 vs それ以外 の今年売上
  // 累計（参考）：これまで1回しか来ていない人 vs 2回以上来た人 の累計売上
  const { newRevYear, repRevYear, newRevAll, repRevAll } = useMemo(() => {
    let nry = 0, rry = 0, nra = 0, rra = 0;
    doneBookings.forEach(b => {
      const c = customers.find(x => x.id === b.customerId);
      if (!c) return;
      const price = b.price || 0;
      // 累計（リピート定着度）
      if ((c.visits || 0) >= 2) rra += price;
      else nra += price;
      // 今年分のみ：bookingが今年で、顧客の初回来店年が今年なら新規
      if (b.date && b.date.startsWith(`${thisYear}-`)) {
        const fv = c.firstVisit || '';
        if (fv && fv.startsWith(`${thisYear}-`)) {
          nry += price;
        } else {
          rry += price;
        }
      }
    });
    return { newRevYear: nry, repRevYear: rry, newRevAll: nra, repRevAll: rra };
  }, [doneBookings, customers, thisYear]);
  // 旧コード互換のため残す
  const newRev = newRevYear;
  const repRev = repRevYear;

  // ----- 流入経路別の新規顧客数 -----
  const sourceBreakdown = useMemo(() => {
    const m = new Map();
    customers.forEach(c => {
      const src = c.acquisitionSource || '未設定';
      m.set(src, (m.get(src) || 0) + 1);
    });
    return Array.from(m.entries()).sort((a, b) => b[1] - a[1]);
  }, [customers]);

  // ----- 年組分析（初回来院年でグルーピング）+ 広告費・回収率 -----
  // ----- リテンション分析（最終来院日からの経過で分類） -----
  const retentionAnalysis = useMemo(() => {
    const today = new Date();
    const visited = customers.filter(c => c.lastVisit);
    const buckets = {
      active: { label: '直近3ヶ月（アクティブ）', count: 0, customers: [], color: 'var(--green-700)', advice: '最重要：リピート促進' },
      warm:   { label: '3〜6ヶ月（休眠予備軍）',  count: 0, customers: [], color: '#a87320', advice: 'LINE/お電話で安否確認' },
      cool:   { label: '6〜12ヶ月（離脱予備軍）', count: 0, customers: [], color: '#d6651e', advice: '再来店割引・特別案内' },
      lost:   { label: '1年以上（離脱）',         count: 0, customers: [], color: 'var(--red)',     advice: '大型企画で一斉アプローチ' },
      // 年別分布（過去5年分）
    };
    const byYear = new Map();
    visited.forEach(c => {
      const lv = new Date(c.lastVisit);
      const monthsDiff = (today.getFullYear() - lv.getFullYear()) * 12 + (today.getMonth() - lv.getMonth());
      if (monthsDiff < 3) buckets.active.count++;
      else if (monthsDiff < 6) buckets.warm.count++;
      else if (monthsDiff < 12) buckets.cool.count++;
      else buckets.lost.count++;
      const y = c.lastVisit.slice(0, 4);
      byYear.set(y, (byYear.get(y) || 0) + 1);
    });
    const yearList = Array.from(byYear.entries())
      .sort((a, b) => b[0].localeCompare(a[0]));
    return {
      total: visited.length,
      buckets: [buckets.active, buckets.warm, buckets.cool, buckets.lost],
      byYear: yearList,
    };
  }, [customers]);

  const cohortAnalysis = useMemo(() => {
    const groups = new Map();
    customers.forEach(c => {
      if (!c.firstVisit) return;
      const y = c.firstVisit.slice(0, 4);
      if (!groups.has(y)) groups.set(y, []);
      groups.get(y).push(c);
    });
    // 年ごとの広告費合計（settings.adCostByChannel から計算）
    const adByMonth = (settings && settings.adCostByChannel) || {};
    const yearAdCost = (year) => {
      let sum = 0;
      Object.keys(adByMonth).forEach(monthKey => {
        if (monthKey.startsWith(year + '-')) {
          const monthCosts = adByMonth[monthKey] || {};
          Object.values(monthCosts).forEach(v => { sum += Number(v) || 0; });
        }
      });
      return sum;
    };
    return Array.from(groups.entries())
      .sort((a, b) => a[0].localeCompare(b[0]))
      .map(([year, list]) => {
        const cnt = list.length;
        const totalRev = list.reduce((s, c) => s + (c.totalSpent || 0), 0);
        const repCnt = list.filter(c => (c.visits || 0) >= 2).length;
        const repRate = cnt === 0 ? 0 : Math.round(repCnt / cnt * 100);
        const ltv = cnt === 0 ? 0 : Math.round(totalRev / cnt);
        const adCost = yearAdCost(year);
        const recoveryRate = adCost === 0 ? null : Math.round(totalRev / adCost * 100);
        const cpa = cnt === 0 ? 0 : Math.round(adCost / cnt);
        return { year, count: cnt, revenue: totalRev, ltv, repRate, adCost, recoveryRate, cpa };
      });
  }, [customers, settings]);

  // ----- CPA計算（チャネル別） -----
  const AD_CHANNELS = (window.getActiveAdChannels ? window.getActiveAdChannels(settings) : (window.DEFAULT_AD_CHANNELS || []));
  const PAID_CHANNELS = AD_CHANNELS.filter(c => c.isPaid);
  const FREE_CHANNELS = AD_CHANNELS.filter(c => !c.isPaid);
  // 旧形式（単純な月額数値）から新形式（チャネル別オブジェクト）への移行も許容
  const adCostByChannel = (settings.adCostByChannel) || {};
  // { '2026-04': { google: 30000, newspaper: 0, ... } }

  // 月ごとの新規来院数（acquisitionSource別）— channelYear の年を表示
  const monthlyChannelData = useMemo(() => {
    const arr = Array.from({ length: 12 }, (_, i) => {
      const monthKey = `${channelYear}-${String(i + 1).padStart(2, '0')}`;
      const cost = adCostByChannel[monthKey] || {};
      const counts = {};
      AD_CHANNELS.forEach(c => { counts[c.key] = 0; });
      return { month: i + 1, monthKey, costs: cost, newByChannel: counts };
    });
    customers.forEach(c => {
      if (!c.firstVisit) return;
      const y = parseInt(c.firstVisit.slice(0, 4), 10);
      const mo = parseInt(c.firstVisit.slice(5, 7), 10);
      if (y === channelYear && mo >= 1 && mo <= 12) {
        const ch = AD_CHANNELS.find(x => x.label === c.acquisitionSource);
        if (ch) arr[mo - 1].newByChannel[ch.key] = (arr[mo - 1].newByChannel[ch.key] || 0) + 1;
      }
    });
    return arr;
  }, [customers, adCostByChannel, channelYear]);

  // 年間チャネル別サマリー
  const yearlyChannelSummary = useMemo(() => {
    return AD_CHANNELS.map(ch => {
      let totalCost = 0, totalNew = 0;
      monthlyChannelData.forEach(row => {
        totalCost += row.costs[ch.key] || 0;
        totalNew += row.newByChannel[ch.key] || 0;
      });
      const cpa = ch.isPaid && totalNew > 0 ? Math.round(totalCost / totalNew) : 0;
      return { ...ch, totalCost, totalNew, cpa };
    });
  }, [monthlyChannelData]);

  const updateChannelCost = (month, channelKey, amount) => {
    const monthKey = `${channelYear}-${String(month).padStart(2, '0')}`;
    const cur = adCostByChannel[monthKey] || {};
    const next = { ...cur, [channelKey]: amount };
    onSetSettings(prev => ({ ...prev, adCostByChannel: { ...adCostByChannel, [monthKey]: next } }));
  };

  // 前月の値を当月にコピー（全チャネル分）
  const copyPrevMonth = (month) => {
    if (month <= 1) { alert('1月は前月コピー不可'); return; }
    const prevKey = `${channelYear}-${String(month - 1).padStart(2, '0')}`;
    const curKey = `${channelYear}-${String(month).padStart(2, '0')}`;
    const prev = adCostByChannel[prevKey] || {};
    const cur = { ...prev }; // 全チャネルをコピー
    onSetSettings(prevS => ({ ...prevS, adCostByChannel: { ...adCostByChannel, [curKey]: cur } }));
  };

  // チャネルの全月一括設定（毎月同額の月固定費用）
  const setAllMonthsForChannel = (channelKey, amount) => {
    const next = { ...adCostByChannel };
    for (let m = 1; m <= 12; m++) {
      const monthKey = `${channelYear}-${String(m).padStart(2, '0')}`;
      next[monthKey] = { ...(next[monthKey] || {}), [channelKey]: amount };
    }
    onSetSettings(prev => ({ ...prev, adCostByChannel: next }));
  };

  // ===== 描画ヘルパー =====
  const BarChart = ({ data, valueKey, labelKey, formatter, max }) => {
    const m = max || Math.max(1, ...data.map(d => d[valueKey]));
    return (
      <div style={{ display: 'flex', alignItems: 'flex-end', gap: 4, height: 180, padding: '12px 6px 0' }}>
        {data.map((d, i) => {
          const h = Math.max(0, (d[valueKey] / m) * 150);
          return (
            <div key={i} style={{ flex: 1, display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 4 }}>
              <div style={{ fontSize: 10, color: 'var(--ink-mute)', fontWeight: 600 }}>
                {d[valueKey] > 0 ? (formatter ? formatter(d[valueKey]) : d[valueKey]) : ''}
              </div>
              <div
                style={{
                  width: '100%',
                  height: h + 'px',
                  background: 'linear-gradient(180deg, var(--green-500), var(--green-700))',
                  borderRadius: '4px 4px 0 0',
                }}
              />
              <div style={{ fontSize: 10, color: 'var(--ink-soft)' }}>{d[labelKey]}</div>
            </div>
          );
        })}
      </div>
    );
  };

  const yen = (v) => '¥' + (v || 0).toLocaleString();
  const yenK = (v) => v >= 10000 ? Math.round(v / 1000) / 10 + '万' : '¥' + (v || 0).toLocaleString();

  return (
    <div>
      {/* ============ サマリー指標 ============ */}
      <div className="stats-grid" style={{ gridTemplateColumns: 'repeat(4, 1fr)' }}>
        <div className="stat-tile">
          <div className="stat-tile-icon amber"><Icon name="yen" size={18}/></div>
          <div className="stat-tile-label">累計売上（全期間）</div>
          <div className="stat-tile-value">{yen(totalRevenue)}</div>
          <div className="stat-tile-delta">来院済 {doneBookings.length} 件</div>
        </div>
        <div className="stat-tile">
          <div className="stat-tile-icon"><Icon name="star" size={18}/></div>
          <div className="stat-tile-label">LTV（顧客平均）</div>
          <div className="stat-tile-value">{yen(ltv)}</div>
          <div className="stat-tile-delta">{customersWithVisits.length}名で計算</div>
        </div>
        <div className="stat-tile">
          <div className="stat-tile-icon orange"><Icon name="users" size={18}/></div>
          <div className="stat-tile-label">リピート率</div>
          <div className="stat-tile-value">{repeatRate}<span className="unit">%</span></div>
          <div className="stat-tile-delta">{repeaters} / {customersWithVisits.length} 名</div>
        </div>
        <div className="stat-tile">
          <div className="stat-tile-icon"><Icon name="users" size={18}/></div>
          <div className="stat-tile-label">登録顧客（累計）</div>
          <div className="stat-tile-value">{customers.length}<span className="unit">名</span></div>
          <div className="stat-tile-delta">来院あり {customersWithVisits.length} 名</div>
        </div>
      </div>

      {/* ============ 今月の運営効率 ============ */}
      <div className="card">
        <div className="card-header" style={{display:'flex', alignItems:'center', gap:8}}>
          <h3 className="card-title" style={{margin:0}}>{opYM.year}年{opYM.month}月 の運営効率</h3>
          <div style={{marginLeft:'auto', display:'flex', alignItems:'center', gap:4}}>
            <button className="btn sm" onClick={() => setOpYM(({year, month}) => {
              const m = month - 1;
              if (m < 1) return { year: year - 1, month: 12 };
              return { year, month: m };
            })} title="前月">‹</button>
            <span style={{fontSize:13.5, fontWeight:700, minWidth:96, textAlign:'center'}}>{opYM.year}年{opYM.month}月</span>
            <button className="btn sm" onClick={() => setOpYM(({year, month}) => {
              const m = month + 1;
              if (m > 12) return { year: year + 1, month: 1 };
              return { year, month: m };
            })} title="翌月">›</button>
            <button className="btn sm" onClick={() => setOpYM({year: _now.getFullYear(), month: _now.getMonth() + 1})} style={{marginLeft:4}}>今月</button>
          </div>
        </div>
        <div className="card-body">
          <div className="stats-grid" style={{gridTemplateColumns:'repeat(4, 1fr)', marginBottom:0}}>
            <div className="stat-tile">
              <div className="stat-tile-label">営業日数</div>
              <div className="stat-tile-value">{operationalMetrics.businessDays}<span className="unit">日</span></div>
              <div className="stat-tile-delta">営業 {Math.round(operationalMetrics.businessMinutes / 60)}時間</div>
            </div>
            <div className="stat-tile">
              <div className="stat-tile-icon orange"><Icon name="star" size={18}/></div>
              <div className="stat-tile-label">稼働率（枠ベース）</div>
              <div className="stat-tile-value">{Math.round(operationalMetrics.utilization * 10) / 10}<span className="unit">%</span></div>
              <div className="stat-tile-delta">{operationalMetrics.visitCount}件 / 最大{operationalMetrics.maxSlots}枠（{operationalMetrics.maxPerDay}人/日 × {operationalMetrics.businessDays}日）</div>
            </div>
            <div className="stat-tile">
              <div className="stat-tile-icon amber"><Icon name="yen" size={18}/></div>
              <div className="stat-tile-label">時間単価（施術ベース）</div>
              <div className="stat-tile-value">{yen(operationalMetrics.hourPrice)}</div>
              <div className="stat-tile-delta">1時間施術あたり</div>
            </div>
            <div className="stat-tile">
              <div className="stat-tile-icon amber"><Icon name="yen" size={18}/></div>
              <div className="stat-tile-label">分単価（施術ベース）</div>
              <div className="stat-tile-value">{yen(operationalMetrics.minutePrice)}</div>
              <div className="stat-tile-delta">施術1分あたり</div>
            </div>
          </div>
          <div className="stats-grid" style={{gridTemplateColumns:'repeat(4, 1fr)', marginTop:14, marginBottom:0}}>
            <div className="stat-tile">
              <div className="stat-tile-label">のべ来院数</div>
              <div className="stat-tile-value">{operationalMetrics.visitCount}<span className="unit">件</span></div>
              <div className="stat-tile-delta">来院済の予約</div>
            </div>
            <div className="stat-tile">
              <div className="stat-tile-label">来院単価</div>
              <div className="stat-tile-value">{yen(operationalMetrics.visitPrice)}</div>
              <div className="stat-tile-delta">1来院あたり（メニュー設計の指標）</div>
            </div>
            <div className="stat-tile">
              <div className="stat-tile-label">顧客月額単価</div>
              <div className="stat-tile-value">{yen(operationalMetrics.monthlyPerCustomer)}</div>
              <div className="stat-tile-delta">1人/月平均（{operationalMetrics.uniqueCustomers}名・リピート深化の指標）</div>
            </div>
            <div className="stat-tile">
              <div className="stat-tile-label">月売上</div>
              <div className="stat-tile-value">{yen(operationalMetrics.monthRevenue)}</div>
              <div className="stat-tile-delta">来院済の合計</div>
            </div>
            <div className="stat-tile" style={{
              background: operationalMetrics.operatingProfit >= 0 ? 'var(--green-50)' : '#fee2e2',
              borderColor: operationalMetrics.operatingProfit >= 0 ? 'var(--green-200, #c8e0c0)' : '#fecaca',
            }}>
              <div className="stat-tile-icon amber"><Icon name="yen" size={18}/></div>
              <div className="stat-tile-label">営業利益（概算）</div>
              <div className="stat-tile-value" style={{ color: operationalMetrics.operatingProfit >= 0 ? 'var(--green-900)' : 'var(--red)' }}>
                {operationalMetrics.operatingProfit >= 0 ? '' : '−'}{yen(Math.abs(operationalMetrics.operatingProfit))}
              </div>
              <div className="stat-tile-delta">利益率 {operationalMetrics.profitRate}%（売上 − 広告費{yen(operationalMetrics.monthlyAdCost)} − 固定費{yen(operationalMetrics.monthlyFixedCost)}）</div>
            </div>
          </div>
          <div style={{ fontSize: 11, color: 'var(--ink-mute)', marginTop: 12 }}>
            ※ 営業日数：店舗設定の営業時間と休業ブロックを考慮 ／ 稼働率：来店件数÷（営業日数×1日MAX{operationalMetrics.maxPerDay}人）／ 時間単価：売上÷実施術時間 ／ 分単価：売上÷実施術分<br/>
            ※ 1日MAX施術人数は店舗設定で変更可能（デフォルト 8人/日）
          </div>
        </div>
      </div>

      {/* ============ 年度別 運営効率の平均 ============ */}
      <div className="card">
        <div className="card-header"><h3 className="card-title">年度別 運営効率の平均（月平均値）</h3></div>
        <div className="card-body">
          {yearlyOperational.length === 0 ? (
            <div className="text-muted" style={{ fontSize: 12, padding: '20px 0', textAlign: 'center' }}>データがありません</div>
          ) : (
            <div className="table-wrap" style={{ overflowX: 'auto' }}>
              <table className="table" style={{ fontSize: 12.5, minWidth: 720 }}>
                <thead>
                  <tr>
                    <th>年度</th>
                    <th style={{ textAlign: 'right' }}>対象月数</th>
                    <th style={{ textAlign: 'right' }}>平均営業日</th>
                    <th style={{ textAlign: 'right' }}>平均稼働率</th>
                    <th style={{ textAlign: 'right' }}>平均月売上</th>
                    <th style={{ textAlign: 'right' }}>平均来院数</th>
                    <th style={{ textAlign: 'right' }}>来院単価</th>
                    <th style={{ textAlign: 'right' }}>顧客月額単価</th>
                    <th style={{ textAlign: 'right' }}>時間単価</th>
                  </tr>
                </thead>
                <tbody>
                  {yearlyOperational.map(y => (
                    <tr key={y.year}>
                      <td style={{ fontWeight: 700 }}>{y.year}年</td>
                      <td className="mono" style={{ textAlign: 'right', color: 'var(--ink-mute)' }}>{y.monthsCount}ヶ月</td>
                      <td className="mono" style={{ textAlign: 'right' }}>{y.avgBusinessDays}日</td>
                      <td className="mono" style={{ textAlign: 'right', fontWeight: 700, color: 'var(--green-900)' }}>{y.avgUtilization}%</td>
                      <td className="mono" style={{ textAlign: 'right', fontWeight: 700 }}>{yen(y.avgRevenue)}</td>
                      <td className="mono" style={{ textAlign: 'right' }}>{y.avgVisits}件</td>
                      <td className="mono" style={{ textAlign: 'right' }}>{yen(y.avgVisitPrice)}</td>
                      <td className="mono" style={{ textAlign: 'right' }}>{yen(y.avgMonthlyPerCustomer)}</td>
                      <td className="mono" style={{ textAlign: 'right' }}>{yen(y.avgHourPrice)}</td>
                    </tr>
                  ))}
                </tbody>
              </table>
            </div>
          )}
          <div style={{ fontSize: 11, color: 'var(--ink-mute)', marginTop: 8, lineHeight: 1.7 }}>
            ※ その年に来院があった月のみ平均（来院ゼロの月は対象外）<br/>
            ※ 前年比で「平均稼働率」「平均月売上」「来院単価」が伸びていれば事業成長中
          </div>
        </div>
      </div>

      {/* ============ 売上推移 ============ */}
      <div className="card">
        <div className="card-header"><h3 className="card-title">年別売上</h3></div>
        <div className="card-body">
          {yearlyRevenue.length === 0 ? (
            <div className="text-muted" style={{ fontSize: 12, padding: '20px 0', textAlign: 'center' }}>データがありません</div>
          ) : (
            <BarChart
              data={yearlyRevenue.map(([y, r]) => ({ year: y, revenue: r }))}
              valueKey="revenue"
              labelKey="year"
              formatter={yenK}
            />
          )}
        </div>
      </div>

      <div className="two-col">
        <div className="card">
          <div className="card-header" style={{display:'flex', alignItems:'center', gap:8}}>
            <h3 className="card-title" style={{margin:0}}>月別売上</h3>
            <div style={{marginLeft:'auto', display:'flex', alignItems:'center', gap:4}}>
              <button className="btn sm" onClick={() => setMonthlyYear(y => y - 1)} title="前年">‹</button>
              <span style={{fontSize:13.5, fontWeight:700, minWidth:64, textAlign:'center'}}>{monthlyYear}年</span>
              <button className="btn sm" onClick={() => setMonthlyYear(y => y + 1)} title="翌年">›</button>
              <button className="btn sm" onClick={() => setMonthlyYear(_now.getFullYear())} style={{marginLeft:4}}>今年</button>
            </div>
          </div>
          <div className="card-body">
            <div style={{fontSize:11.5, color:'var(--ink-mute)', marginBottom:4}}>
              年計 <span className="mono" style={{fontWeight:700, color:'var(--green-900)'}}>{yen(monthlyTotal)}</span>
            </div>
            <BarChart
              data={monthlyRevenue.map(d => ({ ...d, label: d.month + '月' }))}
              valueKey="revenue"
              labelKey="label"
              formatter={yenK}
            />
          </div>
        </div>

        <div className="card">
          <div className="card-header" style={{display:'flex', alignItems:'center', gap:8}}>
            <h3 className="card-title" style={{margin:0}}>日別売上</h3>
            <div style={{marginLeft:'auto', display:'flex', alignItems:'center', gap:4}}>
              <button className="btn sm" onClick={() => setDailyYM(({year, month}) => {
                const m = month - 1;
                if (m < 1) return { year: year - 1, month: 12 };
                return { year, month: m };
              })} title="前月">‹</button>
              <span style={{fontSize:13.5, fontWeight:700, minWidth:96, textAlign:'center'}}>{dailyYM.year}年{dailyYM.month}月</span>
              <button className="btn sm" onClick={() => setDailyYM(({year, month}) => {
                const m = month + 1;
                if (m > 12) return { year: year + 1, month: 1 };
                return { year, month: m };
              })} title="翌月">›</button>
              <button className="btn sm" onClick={() => setDailyYM({year: _now.getFullYear(), month: _now.getMonth() + 1})} style={{marginLeft:4}}>今月</button>
            </div>
          </div>
          <div className="card-body">
            <div style={{fontSize:12, color:'var(--ink-mute)', marginBottom:10}}>
              月計 <span className="mono" style={{fontWeight:700, color:'var(--green-900)', fontSize:14}}>{yen(dailyTotal)}</span>
              <span style={{marginLeft:10}}>来院 {dailyRevenue.reduce((s,d)=>s+d.count,0)} 件</span>
            </div>
            <div style={{maxHeight:520, overflowY:'auto', border:'1px solid var(--line)', borderRadius:8}}>
              {dailyRevenue.filter(d => d.count > 0).length === 0 ? (
                <div style={{padding:20, textAlign:'center', color:'var(--ink-mute)', fontSize:12.5}}>この月の来院済みデータはありません</div>
              ) : (
                dailyRevenue.map(d => {
                  if (d.count === 0) return null;
                  const dt = new Date(dailyYM.year, dailyYM.month - 1, d.day);
                  const wk = WEEKDAY_JA[dt.getDay()];
                  const isWeekend = dt.getDay() === 0 || dt.getDay() === 6;
                  return (
                    <div key={d.day} style={{borderBottom:'1px solid var(--line)', padding:'8px 12px'}}>
                      <div style={{display:'flex', alignItems:'center', gap:10, marginBottom:4}}>
                        <span style={{
                          fontWeight:800, fontSize:13.5, minWidth:54,
                          color: isWeekend ? (dt.getDay()===0?'var(--red)':'var(--blue,#2966a3)') : 'var(--ink)'
                        }}>
                          {dailyYM.month}/{d.day}（{wk}）
                        </span>
                        <span className="mono" style={{fontWeight:700, color:'var(--green-900)', fontSize:13.5}}>{yen(d.revenue)}</span>
                        <span style={{fontSize:11.5, color:'var(--ink-mute)'}}>{d.count}件</span>
                      </div>
                      <div style={{paddingLeft:64, fontSize:12, lineHeight:1.7}}>
                        {d.items.map((it, i) => (
                          <div key={i} style={{display:'flex', alignItems:'baseline', gap:8}}>
                            <span style={{color:'var(--ink-mute)', minWidth:42, fontSize:11.5}} className="mono">{it.time || '—'}</span>
                            <span style={{flex:1, overflow:'hidden', textOverflow:'ellipsis', whiteSpace:'nowrap'}}>
                              {it.name} 様
                              <span style={{color:'var(--ink-mute)', fontSize:11, marginLeft:6}}>{it.menu}</span>
                            </span>
                            <span className="mono" style={{fontWeight:600, minWidth:70, textAlign:'right'}}>{yen(it.price)}</span>
                          </div>
                        ))}
                      </div>
                    </div>
                  );
                })
              )}
            </div>
          </div>
        </div>
      </div>

      {/* ============ 回数券・月額プラン ============ */}
      <div className="card">
        <div className="card-header"><h3 className="card-title">回数券</h3></div>
        <div className="card-body">
          <div className="stats-grid" style={{gridTemplateColumns:'repeat(4, 1fr)', marginBottom:0}}>
            <div className="stat-tile">
              <div className="stat-tile-label">今月の回数券販売</div>
              <div className="stat-tile-value">{ticketStats.ticket10ThisMonth}<span className="unit">枚</span></div>
              <div className="stat-tile-delta">¥{ticketStats.ticket10MonthSales.toLocaleString()}（前受け）</div>
            </div>
            <div className="stat-tile">
              <div className="stat-tile-label">今月の月額プラン契約</div>
              <div className="stat-tile-value">{ticketStats.monthly4ThisMonth}<span className="unit">件</span></div>
              <div className="stat-tile-delta">¥{ticketStats.monthly4MonthSales.toLocaleString()}（前受け）</div>
            </div>
            <div className="stat-tile">
              <div className="stat-tile-label">回数券保有中</div>
              <div className="stat-tile-value">{ticketStats.activeTicket10}<span className="unit">枚</span></div>
              <div className="stat-tile-delta">残 {ticketStats.totalRemaining10} 回分</div>
            </div>
            <div className="stat-tile">
              <div className="stat-tile-label">月額プラン契約中</div>
              <div className="stat-tile-value">{ticketStats.activeMonthly4}<span className="unit">件</span></div>
              <div className="stat-tile-delta">継続課金中</div>
            </div>
          </div>
          <div style={{ fontSize: 11, color: 'var(--ink-mute)', marginTop: 12 }}>
            ※ 売上は「利用ごと按分」のため、上記の販売額は前受けで、累計売上には1回利用ごとに按分計上されます。
          </div>

          {ticketHolders.length > 0 && (
            <div style={{ marginTop: 18 }}>
              <h4 className="card-title" style={{ margin: '0 0 10px', fontSize: 13.5 }}>購入済 一覧（{ticketHolders.length}件）</h4>
              <table className="table" style={{ margin: 0, fontSize: 12.5 }}>
                <thead>
                  <tr>
                    <th>顧客名</th>
                    <th style={{ width: 110 }}>種類</th>
                    <th style={{ width: 110 }}>購入日</th>
                    <th style={{ width: 100, textAlign: 'right' }}>残り / 合計</th>
                    <th style={{ width: 100, textAlign: 'right' }}>1回単価</th>
                    <th style={{ width: 110 }}>ステータス</th>
                  </tr>
                </thead>
                <tbody>
                  {ticketHolders.map(h => (
                    <tr key={h.ticketId}>
                      <td style={{ fontWeight: 600 }}>{h.customerName} 様</td>
                      <td>
                        <span
                          className="pill"
                          style={{
                            background: h.type === 'ticket10' ? '#e8f0e6' : '#fdf0d6',
                            color: h.type === 'ticket10' ? '#4d7d3a' : '#a87320',
                            fontWeight: 700,
                            fontSize: 11,
                          }}
                        >
                          {h.typeLabel}
                        </span>
                      </td>
                      <td className="mono">{h.purchasedAt || '—'}</td>
                      <td className="mono" style={{ textAlign: 'right', fontWeight: 700 }}>
                        {h.remaining} / {h.total}
                      </td>
                      <td className="mono" style={{ textAlign: 'right' }}>¥{(h.pricePerVisit || 0).toLocaleString()}</td>
                      <td>
                        {h.status === 'active' && (
                          <span className="pill" style={{ background: '#d8e6cf', color: '#1f3a14', fontWeight: 700, fontSize: 11 }}>
                            利用中{h.type === 'monthly4' && h.cycleEnd ? `（〜${h.cycleEnd}）` : ''}
                          </span>
                        )}
                        {h.status === 'used_up' && (
                          <span className="pill" style={{ background: '#eee', color: '#777', fontSize: 11 }}>使い切り</span>
                        )}
                        {h.status === 'expired' && (
                          <span className="pill" style={{ background: '#f3e2dc', color: '#8a3a1f', fontSize: 11 }}>期限切れ</span>
                        )}
                      </td>
                    </tr>
                  ))}
                </tbody>
              </table>
            </div>
          )}
        </div>
      </div>

      {/* ============ リピートステージ別 ============ */}
      <div className="card">
        <div className="card-header"><h3 className="card-title">リピートステージ別</h3></div>
        <div className="card-body">
          <table className="table" style={{ margin: 0, fontSize: 12.5 }}>
            <thead>
              <tr>
                <th>ステージ</th>
                <th style={{ width: 100, textAlign: 'right' }}>人数</th>
                <th style={{ width: 100, textAlign: 'right' }}>割合</th>
                <th style={{ width: 140, textAlign: 'right' }}>累計売上</th>
                <th style={{ width: 140, textAlign: 'right' }}>LTV（平均）</th>
              </tr>
            </thead>
            <tbody>
              {stageAnalysis.map(s => (
                <tr key={s.key}>
                  <td style={{ fontWeight: 700 }}>
                    <span
                      className="pill"
                      style={{
                        background: s.key === 'first' ? '#fdf0d6' : s.key === 'second' ? '#e8f0e6' : '#d8e6cf',
                        color: s.key === 'first' ? '#a87320' : s.key === 'second' ? '#4d7d3a' : '#1f3a14',
                        fontWeight: 700,
                      }}
                    >{s.label}</span>
                  </td>
                  <td className="mono" style={{ textAlign: 'right' }}>{s.count} 名</td>
                  <td className="mono" style={{ textAlign: 'right' }}>{s.ratio}%</td>
                  <td className="mono" style={{ textAlign: 'right' }}>{yen(s.revenue)}</td>
                  <td className="mono" style={{ textAlign: 'right', fontWeight: 700 }}>{yen(s.ltv)}</td>
                </tr>
              ))}
            </tbody>
          </table>
          <div style={{ fontSize: 11, color: 'var(--ink-mute)', marginTop: 8 }}>
            ※ 「初回のみ」が多いと離脱率が高め。「3回以上（常連）」が増えると安定収益。
          </div>
        </div>
      </div>

      {/* ============ 顧客属性（性別・年代・平均年齢） ============ */}
      <div className="card">
        <div className="card-header"><h3 className="card-title">顧客属性（来院あり {demographics.total} 名）</h3></div>
        <div className="card-body">
          <div className="two-col">
            {/* 男女比 */}
            <div>
              <h4 className="card-title" style={{ margin: '0 0 8px', fontSize: 13 }}>男女比</h4>
              {demographics.total === 0 ? (
                <div className="text-muted" style={{ fontSize: 12, padding: '20px 0', textAlign: 'center' }}>データがありません</div>
              ) : (
                <table className="table" style={{ margin: 0, fontSize: 12.5 }}>
                  <tbody>
                    <tr>
                      <td>女性</td>
                      <td className="mono" style={{ textAlign: 'right' }}>{demographics.female} 名</td>
                      <td className="mono" style={{ textAlign: 'right', width: 70, color: 'var(--ink-mute)' }}>
                        {demographics.total > 0 ? Math.round(demographics.female / demographics.total * 100) : 0}%
                      </td>
                    </tr>
                    <tr>
                      <td>男性</td>
                      <td className="mono" style={{ textAlign: 'right' }}>{demographics.male} 名</td>
                      <td className="mono" style={{ textAlign: 'right', color: 'var(--ink-mute)' }}>
                        {demographics.total > 0 ? Math.round(demographics.male / demographics.total * 100) : 0}%
                      </td>
                    </tr>
                    {demographics.otherG > 0 && (
                      <tr>
                        <td>その他</td>
                        <td className="mono" style={{ textAlign: 'right' }}>{demographics.otherG} 名</td>
                        <td className="mono" style={{ textAlign: 'right', color: 'var(--ink-mute)' }}>
                          {Math.round(demographics.otherG / demographics.total * 100)}%
                        </td>
                      </tr>
                    )}
                    {demographics.unknownG > 0 && (
                      <tr>
                        <td className="text-muted">未登録</td>
                        <td className="mono text-muted" style={{ textAlign: 'right' }}>{demographics.unknownG} 名</td>
                        <td className="mono text-muted" style={{ textAlign: 'right' }}>
                          {Math.round(demographics.unknownG / demographics.total * 100)}%
                        </td>
                      </tr>
                    )}
                  </tbody>
                </table>
              )}
            </div>
            {/* 年代別 */}
            <div>
              <h4 className="card-title" style={{ margin: '0 0 8px', fontSize: 13 }}>
                年代別 <span style={{ fontWeight: 400, color: 'var(--ink-mute)', fontSize: 11.5, marginLeft: 6 }}>
                  平均 <span className="mono" style={{ fontWeight: 700, color: 'var(--green-900)' }}>{demographics.avgAge}</span> 歳
                </span>
              </h4>
              {demographics.ageCount === 0 ? (
                <div className="text-muted" style={{ fontSize: 12, padding: '20px 0', textAlign: 'center' }}>年齢データがありません</div>
              ) : (
                <table className="table" style={{ margin: 0, fontSize: 12.5 }}>
                  <tbody>
                    {[
                      ['10代', '10s'], ['20代', '20s'], ['30代', '30s'], ['40代', '40s'],
                      ['50代', '50s'], ['60代', '60s'], ['70代', '70s'], ['80代以上', '80+'],
                    ].map(([label, key]) => {
                      const cnt = demographics.ageBuckets[key] || 0;
                      if (cnt === 0) return null;
                      const ratio = demographics.ageCount > 0 ? Math.round(cnt / demographics.ageCount * 100) : 0;
                      return (
                        <tr key={key}>
                          <td>{label}</td>
                          <td className="mono" style={{ textAlign: 'right' }}>{cnt} 名</td>
                          <td className="mono" style={{ textAlign: 'right', width: 70, color: 'var(--ink-mute)' }}>{ratio}%</td>
                        </tr>
                      );
                    })}
                    {demographics.ageBuckets.unknown > 0 && (
                      <tr>
                        <td className="text-muted">年齢未登録</td>
                        <td className="mono text-muted" style={{ textAlign: 'right' }}>{demographics.ageBuckets.unknown} 名</td>
                        <td className="mono text-muted" style={{ textAlign: 'right' }}>
                          {demographics.total > 0 ? Math.round(demographics.ageBuckets.unknown / demographics.total * 100) : 0}%
                        </td>
                      </tr>
                    )}
                  </tbody>
                </table>
              )}
            </div>
          </div>
          <div style={{ fontSize: 11, color: 'var(--ink-mute)', marginTop: 10 }}>
            ※ 来院済の顧客（visits ≥ 1）が対象です。年代・性別が未登録の顧客は「未登録」に集計されます。
          </div>
        </div>
      </div>

      {/* ============ リテンション分析（最終来院別） ============ */}
      <div className="card">
        <div className="card-header"><h3 className="card-title">リテンション分析（最終来院日からの経過）</h3></div>
        <div className="card-body">
          <div className="two-col">
            {/* 経過期間別 */}
            <div>
              <h4 className="card-title" style={{ margin: '0 0 8px', fontSize: 13 }}>経過期間別</h4>
              {retentionAnalysis.total === 0 ? (
                <div className="text-muted" style={{ fontSize: 12, padding: '20px 0', textAlign: 'center' }}>データがありません</div>
              ) : (
                <table className="table" style={{ margin: 0, fontSize: 12.5 }}>
                  <tbody>
                    {retentionAnalysis.buckets.map((b, i) => {
                      const ratio = retentionAnalysis.total > 0 ? Math.round(b.count / retentionAnalysis.total * 100) : 0;
                      return (
                        <tr key={i}>
                          <td style={{ borderLeft: `4px solid ${b.color}`, paddingLeft: 10 }}>
                            <div style={{ fontWeight: 700, color: b.color, fontSize: 12.5 }}>{b.label}</div>
                            <div style={{ fontSize: 11, color: 'var(--ink-mute)' }}>{b.advice}</div>
                          </td>
                          <td className="mono" style={{ textAlign: 'right', fontWeight: 700, width: 80 }}>{b.count}名</td>
                          <td className="mono" style={{ textAlign: 'right', width: 60, color: 'var(--ink-mute)' }}>{ratio}%</td>
                        </tr>
                      );
                    })}
                  </tbody>
                </table>
              )}
            </div>
            {/* 年別最終来院 */}
            <div>
              <h4 className="card-title" style={{ margin: '0 0 8px', fontSize: 13 }}>最終来院年別</h4>
              {retentionAnalysis.byYear.length === 0 ? (
                <div className="text-muted" style={{ fontSize: 12, padding: '20px 0', textAlign: 'center' }}>データがありません</div>
              ) : (
                <table className="table" style={{ margin: 0, fontSize: 12.5 }}>
                  <thead>
                    <tr>
                      <th>年</th>
                      <th style={{ textAlign: 'right' }}>人数</th>
                      <th style={{ textAlign: 'right', width: 60 }}>割合</th>
                    </tr>
                  </thead>
                  <tbody>
                    {retentionAnalysis.byYear.map(([year, count]) => {
                      const ratio = retentionAnalysis.total > 0 ? Math.round(count / retentionAnalysis.total * 100) : 0;
                      return (
                        <tr key={year}>
                          <td style={{ fontWeight: 700 }}>{year}年</td>
                          <td className="mono" style={{ textAlign: 'right' }}>{count}名</td>
                          <td className="mono" style={{ textAlign: 'right', color: 'var(--ink-mute)' }}>{ratio}%</td>
                        </tr>
                      );
                    })}
                  </tbody>
                </table>
              )}
            </div>
          </div>
          <div style={{ fontSize: 11, color: 'var(--ink-mute)', marginTop: 10, lineHeight: 1.6 }}>
            ※ 顧客リストの「最終来院」フィルタタブと連動。アクティブ層は守り、休眠予備軍はLINE等で再アプローチ
          </div>
        </div>
      </div>

      {/* ============ 新規 vs リピーター売上 ============ */}
      <div className="two-col">
        <div className="card">
          <div className="card-header"><h3 className="card-title">{thisYear}年 新規 vs リピーター 売上構成</h3></div>
          <div className="card-body">
            {(newRevYear + repRevYear) === 0 ? (
              <div className="text-muted" style={{ fontSize: 12, padding: '20px 0', textAlign: 'center' }}>{thisYear}年のデータがまだありません</div>
            ) : (
              <>
                <div style={{ display: 'flex', height: 32, borderRadius: 6, overflow: 'hidden', marginBottom: 12 }}>
                  <div style={{ width: ((newRevYear / (newRevYear + repRevYear)) * 100) + '%', background: 'var(--orange)', display: 'flex', alignItems: 'center', justifyContent: 'center', color: '#fff', fontSize: 11, fontWeight: 700 }}>
                    新規 {Math.round(newRevYear / (newRevYear + repRevYear) * 100)}%
                  </div>
                  <div style={{ width: ((repRevYear / (newRevYear + repRevYear)) * 100) + '%', background: 'var(--green-700)', display: 'flex', alignItems: 'center', justifyContent: 'center', color: '#fff', fontSize: 11, fontWeight: 700 }}>
                    リピーター {Math.round(repRevYear / (newRevYear + repRevYear) * 100)}%
                  </div>
                </div>
                <div className="detail-grid">
                  <dt>{thisYear}年 新規さんからの売上</dt><dd className="mono">{yen(newRevYear)}</dd>
                  <dt>{thisYear}年 リピーターからの売上</dt><dd className="mono">{yen(repRevYear)}</dd>
                  <dt>{thisYear}年 売上合計</dt><dd className="mono">{yen(newRevYear + repRevYear)}</dd>
                </div>
                <div style={{fontSize:11, color:'var(--ink-mute)', marginTop:6, lineHeight:1.5}}>
                  ※「新規」=今年（{thisYear}年）に初めて来店した方<br/>
                  ※「リピーター」=去年以前から継続してきている方
                </div>
                {(newRevAll + repRevAll) > 0 && (
                  <div style={{marginTop:12, padding:'8px 10px', background:'var(--mist)', borderRadius:6, fontSize:11.5, color:'var(--ink-soft)'}}>
                    <strong>📊 累計（参考）：</strong>1回限り来店 <strong>{Math.round(newRevAll/(newRevAll+repRevAll)*100)}%</strong>（{yen(newRevAll)}） / 2回以上来店 <strong>{Math.round(repRevAll/(newRevAll+repRevAll)*100)}%</strong>（{yen(repRevAll)}）<br/>
                    リピート定着度の指標：2回目以降の比率が高いほど安定経営
                  </div>
                )}
              </>
            )}
          </div>
        </div>

        <div className="card">
          <div className="card-header"><h3 className="card-title">流入経路別 新規顧客</h3></div>
          <div className="card-body">
            {sourceBreakdown.length === 0 ? (
              <div className="text-muted" style={{ fontSize: 12, padding: '20px 0', textAlign: 'center' }}>データがありません</div>
            ) : (
              <table className="table" style={{ margin: 0, fontSize: 12.5 }}>
                <thead><tr><th>流入経路</th><th style={{ width: 80, textAlign: 'right' }}>人数</th><th style={{ width: 80, textAlign: 'right' }}>割合</th></tr></thead>
                <tbody>
                  {sourceBreakdown.map(([src, n]) => (
                    <tr key={src}>
                      <td>{src}</td>
                      <td className="mono" style={{ textAlign: 'right' }}>{n}名</td>
                      <td className="mono" style={{ textAlign: 'right' }}>{Math.round(n / customers.length * 100)}%</td>
                    </tr>
                  ))}
                </tbody>
              </table>
            )}
          </div>
        </div>
      </div>

      {/* ============ CPA / 広告費（チャネル別） ============ */}
      <div className="card">
        <div className="card-header" style={{display:'flex', alignItems:'center', justifyContent:'space-between', flexWrap:'wrap', gap:8}}>
          <h3 className="card-title">{channelYear}年 チャネル別 広告費 / 新規 / CPA</h3>
          <div style={{display:'flex', alignItems:'center', gap:6}}>
            <button className="btn sm" onClick={() => setChannelYear(channelYear - 1)} title="前年">‹</button>
            <select className="select" style={{height:28, padding:'2px 8px', fontSize:13, fontWeight:600, minWidth:90}}
              value={channelYear} onChange={e => setChannelYear(parseInt(e.target.value, 10))}>
              {[2022, 2023, 2024, 2025, 2026, 2027].map(y => <option key={y} value={y}>{y}年</option>)}
            </select>
            <button className="btn sm" onClick={() => setChannelYear(channelYear + 1)} title="翌年" disabled={channelYear >= thisYear}>›</button>
            {channelYear !== thisYear && (
              <button className="btn sm" onClick={() => setChannelYear(thisYear)} title="今年に戻る">今年</button>
            )}
          </div>
        </div>
        <div className="card-body">
          {/* 月×チャネル 入力マトリクス */}
          <div className="table-wrap" style={{ overflowX: 'auto' }}>
            <table className="table" style={{ fontSize: 12.5, minWidth: 1200 }}>
              <thead>
                <tr>
                  <th style={{ width: 60 }}>月</th>
                  {PAID_CHANNELS.map(ch => (
                    <th key={ch.key} style={{ width: 150, minWidth: 150, textAlign: 'right' }}>
                      <div style={{display:'flex', alignItems:'center', justifyContent:'flex-end', gap:4}}>
                        <span>{ch.label}</span>
                        <button style={{
                          width: 18, height: 18, padding: 0, fontSize: 11, lineHeight: 1,
                          border: '1px solid var(--line)', borderRadius: 3, cursor: 'pointer',
                          background: 'var(--green-50)', color: 'var(--green-900)'
                        }}
                          title="全月一括：このチャネルの1〜12月に同じ金額を設定（月額固定費用）"
                          onClick={() => {
                            const v = prompt(`「${ch.label}」を 1〜12月の全月に同額で設定します。\n金額を入力（円）：`, '0');
                            if (v === null) return;
                            const n = parseInt(v.replace(/[^0-9]/g, ''), 10) || 0;
                            setAllMonthsForChannel(ch.key, n);
                          }}>≡</button>
                      </div>
                    </th>
                  ))}
                  <th style={{ width: 90, textAlign: 'right', background: 'var(--green-50)' }}>新規合計</th>
                </tr>
              </thead>
              <tbody>
                {monthlyChannelData.map(row => {
                  const totalNew = AD_CHANNELS.reduce((s, ch) => s + (row.newByChannel[ch.key] || 0), 0);
                  return (
                    <tr key={row.month}>
                      <td style={{whiteSpace:'nowrap'}}>
                        <span style={{ fontWeight: 700 }}>{row.month}月</span>
                        {row.month > 1 && (
                          <button style={{
                            marginLeft: 4, width: 18, height: 18, padding: 0, fontSize: 11, lineHeight: 1,
                            border: '1px solid var(--line)', borderRadius: 3, cursor: 'pointer',
                            background: 'transparent', color: 'var(--ink-soft)'
                          }}
                            title="前月の値を全チャネルにコピー"
                            onClick={() => copyPrevMonth(row.month)}>↑</button>
                        )}
                      </td>
                      {PAID_CHANNELS.map(ch => {
                        const cost = row.costs[ch.key] || 0;
                        const newC = row.newByChannel[ch.key] || 0;
                        const cpa = newC > 0 ? Math.round(cost / newC) : 0;
                        return (
                          <td key={ch.key} style={{ textAlign: 'right' }}>
                            <input
                              type="number"
                              className="input"
                              style={{ width: '100%', minWidth: 110, height: 30, padding: '2px 8px', fontSize: 13, fontWeight: 600, textAlign: 'right' }}
                              value={cost || ''}
                              onChange={e => updateChannelCost(row.month, ch.key, parseInt(e.target.value, 10) || 0)}
                              placeholder="0"
                            />
                            <div style={{ fontSize: 11, color: 'var(--ink-mute)', marginTop: 3 }}>
                              {newC}名 {cpa > 0 ? `/ ${yen(cpa)}` : ''}
                            </div>
                          </td>
                        );
                      })}
                      <td className="mono" style={{ textAlign: 'right', fontWeight: 700, background: 'var(--green-50)' }}>{totalNew}名</td>
                    </tr>
                  );
                })}
              </tbody>
            </table>
          </div>

          {/* 年間チャネル別サマリー */}
          <h4 className="card-title" style={{ margin: '20px 0 8px', fontSize: 13 }}>{channelYear}年 チャネル別サマリー</h4>
          <div className="table-wrap">
            <table className="table" style={{ fontSize: 12.5 }}>
              <thead>
                <tr>
                  <th>チャネル</th>
                  <th style={{ width: 80, textAlign: 'right' }}>区分</th>
                  <th style={{ width: 130, textAlign: 'right' }}>年間広告費</th>
                  <th style={{ width: 100, textAlign: 'right' }}>新規獲得</th>
                  <th style={{ width: 130, textAlign: 'right' }}>CPA</th>
                </tr>
              </thead>
              <tbody>
                {yearlyChannelSummary.map(row => (
                  <tr key={row.key}>
                    <td style={{ fontWeight: 600 }}>{row.label}</td>
                    <td style={{ textAlign: 'right', fontSize: 11, color: row.isPaid ? 'var(--orange)' : 'var(--ink-mute)' }}>
                      {row.isPaid ? '費用発生' : 'ゼロ円'}
                    </td>
                    <td className="mono" style={{ textAlign: 'right' }}>
                      {row.isPaid ? yen(row.totalCost) : <span className="text-muted">—</span>}
                    </td>
                    <td className="mono" style={{ textAlign: 'right' }}>{row.totalNew}名</td>
                    <td className="mono" style={{ textAlign: 'right', fontWeight: 700, color: row.cpa > 0 ? 'var(--green-900)' : 'var(--ink-mute)' }}>
                      {row.isPaid ? (row.cpa > 0 ? yen(row.cpa) : '—') : '—'}
                    </td>
                  </tr>
                ))}
                {/* 合計行 */}
                {(() => {
                  const totalCost = yearlyChannelSummary.reduce((s, r) => s + (r.isPaid ? (r.totalCost || 0) : 0), 0);
                  const totalNew = yearlyChannelSummary.reduce((s, r) => s + (r.totalNew || 0), 0);
                  const overallCPA = totalNew > 0 ? Math.round(totalCost / totalNew) : 0;
                  return (
                    <tr style={{ background: 'var(--green-50)', borderTop: '2px solid var(--green-700)', fontSize: 13.5 }}>
                      <td style={{ fontWeight: 800, color: 'var(--green-900)' }}>合計</td>
                      <td></td>
                      <td className="mono" style={{ textAlign: 'right', fontWeight: 800, color: 'var(--green-900)' }}>{yen(totalCost)}</td>
                      <td className="mono" style={{ textAlign: 'right', fontWeight: 800, color: 'var(--green-900)' }}>{totalNew}名</td>
                      <td className="mono" style={{ textAlign: 'right', fontWeight: 800, color: 'var(--green-900)' }}>
                        {overallCPA > 0 ? yen(overallCPA) : '—'}
                      </td>
                    </tr>
                  );
                })()}
              </tbody>
            </table>
          </div>

          <div style={{ fontSize: 11, color: 'var(--ink-mute)', marginTop: 8, lineHeight: 1.6 }}>
            ※ 広告費は月初に1回入力。顧客の「流入経路」と各チャネルが一致した新規来院数で CPA を自動算出。<br/>
            ※ 「ゼロ円」チャネル（GBP・紹介・看板など）は人数だけ集計。看板の設置費は別途メモで管理してください。
          </div>
        </div>
      </div>

      {/* ============ 年組分析 ============ */}
      <div className="card">
        <div className="card-header"><h3 className="card-title">年組分析（初回来院年ごとのグループ）</h3></div>
        <div className="card-body">
          {cohortAnalysis.length === 0 ? (
            <div className="text-muted" style={{ fontSize: 12, padding: '20px 0', textAlign: 'center' }}>データがありません</div>
          ) : (
            <table className="table" style={{ margin: 0, fontSize: 12.5 }}>
              <thead>
                <tr>
                  <th>初回来院年</th>
                  <th style={{ textAlign: 'right' }}>顧客数</th>
                  <th style={{ textAlign: 'right' }}>リピート率</th>
                  <th style={{ textAlign: 'right' }}>累計売上</th>
                  <th style={{ textAlign: 'right' }}>LTV（平均）</th>
                  <th style={{ textAlign: 'right' }}>広告費</th>
                  <th style={{ textAlign: 'right' }}>CPA</th>
                  <th style={{ textAlign: 'right', background: 'var(--green-50)' }}>回収率</th>
                </tr>
              </thead>
              <tbody>
                {cohortAnalysis.map(g => {
                  const recColor = g.recoveryRate === null ? 'var(--ink-mute)'
                    : g.recoveryRate >= 200 ? 'var(--green-900)'
                    : g.recoveryRate >= 100 ? 'var(--green-700)'
                    : 'var(--red)';
                  return (
                    <tr key={g.year}>
                      <td style={{ fontWeight: 700 }}>{g.year}年組</td>
                      <td className="mono" style={{ textAlign: 'right' }}>{g.count}名</td>
                      <td className="mono" style={{ textAlign: 'right' }}>{g.repRate}%</td>
                      <td className="mono" style={{ textAlign: 'right' }}>{yen(g.revenue)}</td>
                      <td className="mono" style={{ textAlign: 'right', fontWeight: 700 }}>{yen(g.ltv)}</td>
                      <td className="mono" style={{ textAlign: 'right', color: g.adCost > 0 ? 'var(--ink)' : 'var(--ink-mute)' }}>
                        {g.adCost > 0 ? yen(g.adCost) : '—'}
                      </td>
                      <td className="mono" style={{ textAlign: 'right', color: 'var(--ink-mute)' }}>
                        {g.adCost > 0 && g.count > 0 ? yen(g.cpa) : '—'}
                      </td>
                      <td className="mono" style={{ textAlign: 'right', fontWeight: 800, background: 'var(--green-50)', color: recColor }}>
                        {g.recoveryRate === null ? '—' : g.recoveryRate + '%'}
                      </td>
                    </tr>
                  );
                })}
              </tbody>
            </table>
          )}
          <div style={{ fontSize: 11, color: 'var(--ink-mute)', marginTop: 10, lineHeight: 1.7 }}>
            ※ <b>回収率 = その年に獲得した顧客の累計売上 ÷ その年の広告費合計 × 100%</b><br/>
            ※ 100%以上 = 黒字、200%以上 = 優良。新しい年組は累計売上が育つにつれ回収率が上がります（新規組は赤字スタートが普通）<br/>
            ※ 広告費は店舗設定→経営分析の「チャネル別広告費」入力値の年合計で算出
          </div>
        </div>
      </div>
    </div>
  );
}

window.AnalyticsPage = AnalyticsPage;
