// bkg-data.jsx — Booking Analytics data + i18n strings

const BKG = {
  // ============ Section 1 — Snapshot ============
  snapshot: {
    default: {
      total:    { value: '141',    delta: '+12',  sign: 'pos', sub: 'новых броней' },
      nights:   { value: '245',    delta: '+18',  sign: 'pos', sub: 'включая отменённые' },
      confirm:  { value: '218',    delta: '+14',  sign: 'pos', sub: 'за вычетом отмен' },
      otb:      { value: '€18 400', delta: '+€2 100', sign: 'pos', sub: 'забронировано на будущее', stayDate: true },
      forecast: { value: '72%',    delta: '+5 п.п.', sign: 'pos', sub: 'на следующий месяц', stayDate: true },
      lead:     { value: '14 дней', delta: '→',   sign: 'flat', sub: 'медиана между броней и заездом' },
      alos:     { value: '1.8',    delta: '−0.1', sign: 'flat', sub: 'средняя длительность стоянки' },
      cancel:   { value: '11%',    delta: '+1.2 п.п.', sign: 'neg', sub: '16 броней отменены' },
      noshow:   { value: '1.2%',   delta: '→',    sign: 'flat', sub: 'не доехали' },
      window:   { value: '68%',    delta: '+4 п.п.', sign: 'pos', sub: 'окно 60 дней заполнено' },
    },
    alert: {
      total:    { value: '128',    delta: '−8',   sign: 'neg', sub: 'падение объёма' },
      nights:   { value: '226',    delta: '−14',  sign: 'neg', sub: 'включая отменённые' },
      confirm:  { value: '186',    delta: '−22',  sign: 'neg', sub: 'за вычетом отмен' },
      otb:      { value: '€14 200', delta: '−€2 800', sign: 'neg', sub: 'забронировано на будущее', stayDate: true, alert: true },
      forecast: { value: '58%',    delta: '−9 п.п.', sign: 'neg', sub: 'риск недозагрузки', stayDate: true, alert: true },
      lead:     { value: '8 дней', delta: '−6',   sign: 'neg', sub: 'last-minute давление' },
      alos:     { value: '1.6',    delta: '−0.3', sign: 'neg', sub: 'короче стоянки' },
      cancel:   { value: '17%',    delta: '+5.4 п.п.', sign: 'neg', sub: '24 отмены — выше нормы', alert: true },
      noshow:   { value: '2.8%',   delta: '+1.6 п.п.', sign: 'neg', sub: 'рост no-show', alert: true },
      window:   { value: '54%',    delta: '−10 п.п.', sign: 'neg', sub: 'окно недозаполнено' },
    },
  },

  // ============ Section 2 — Pace & Pickup ============
  // Pace curve: array of 90 days, each with current OTB and historical median + band
  pace: (() => {
    const days = 90;
    const arr = [];
    for (let i = 0; i < days; i++) {
      // base sine seasonality
      const wk = Math.sin((i / 90) * Math.PI * 2 + 0.4);
      const base = 60 + wk * 18;
      // current line drops toward end of horizon (less filled the further out)
      const fill = Math.max(0.25, 1 - (i / 90) * 0.85);
      const current = Math.min(100, Math.max(0, base * fill + (Math.sin(i * 0.7) * 4)));
      const median = Math.min(100, Math.max(0, base * Math.max(0.4, 1 - (i / 90) * 0.6)));
      arr.push({
        day: i,
        current: Math.round(current * 10) / 10,
        median: Math.round(median * 10) / 10,
        bandLo: Math.round(Math.max(0, median - 12) * 10) / 10,
        bandHi: Math.round(Math.min(100, median + 12) * 10) / 10,
      });
    }
    return arr;
  })(),

  // Booking curve: -90..0 days before arrival; current vs avg
  bookingCurve: {
    targetDate: 'Сб, 15 июня · Latvia',
    daysBefore: Array.from({ length: 91 }, (_, i) => -90 + i),
    current: Array.from({ length: 91 }, (_, i) => {
      const d = -90 + i;
      // S-curve accumulation, current at day -28 (today)
      if (d < -28) return 0; // not yet today's data range
      const t = (d + 28) / 28; // normalized 0..1 from today to arrival
      const v = 100 / (1 + Math.exp(-6 * (t - 0.6)));
      return Math.round(v * 10) / 10;
    }),
    average: Array.from({ length: 91 }, (_, i) => {
      const d = -90 + i;
      const t = (d + 90) / 90;
      const v = 100 / (1 + Math.exp(-5 * (t - 0.55)));
      return Math.round(v * 10) / 10;
    }),
    todayDay: -28,
    daysToGo: 28,
  },

  pickup: {
    rows: [
      { month: 'Май 2025',  otb: 218, p1: 4,  p7: 22, p30: 68 },
      { month: 'Июнь 2025', otb: 184, p1: 6,  p7: 28, p30: 92 },
      { month: 'Июль 2025', otb: 156, p1: 3,  p7: 18, p30: 84 },
      { month: 'Авг 2025',  otb: 88,  p1: 2,  p7: 14, p30: 56 },
      { month: 'Сен 2025',  otb: 42,  p1: 1,  p7: 6,  p30: 28 },
      { month: 'Окт 2025',  otb: 18,  p1: 0,  p7: 2,  p30: 12 },
    ],
  },

  // 5 cabins × 90 days; each cell = state code: 'b' booked, 'a' available, 'c' cancelled, 'l' low ADR
  paceHeatmap: (() => {
    const cabins = ['Cyprus', 'Bali', 'Portofino', 'Latvia', 'Maldives'];
    const days = 90;
    const grid = cabins.map((cab, ci) => {
      const row = { cabin: cab, cells: [] };
      for (let i = 0; i < days; i++) {
        // Booked density decays with horizon
        const filled = Math.random() < Math.max(0.15, 0.85 - (i / days) * 0.6 - ci * 0.05);
        let state = filled ? 'b' : 'a';
        if (state === 'a' && Math.random() < 0.05) state = 'c';
        if (state === 'b' && Math.random() < 0.08) state = 'l';
        row.cells.push(state);
      }
      return row;
    });
    return grid;
  })(),

  // ============ Section 3 — Lead time ============
  leadDistribution: {
    bins: [
      { lbl: '0',       pct: 4,  count: 6 },
      { lbl: '1—3',     pct: 9,  count: 13 },
      { lbl: '4—7',     pct: 16, count: 23 },
      { lbl: '8—14',    pct: 24, count: 34 },
      { lbl: '15—30',   pct: 28, count: 39 },
      { lbl: '31—60',   pct: 13, count: 18 },
      { lbl: '61—90',   pct: 4,  count: 6 },
      { lbl: '90+',     pct: 2,  count: 2 },
    ],
    median: 14,
    insightRu: '50% броней совершаются в течение 14 дней до заезда. 80% — в течение 30 дней.',
    insightEn: '50% of bookings happen within 14 days. 80% within 30 days.',
  },
  leadByChannel: [
    { ch: 'booking', name: 'Booking.com', median: 22, lt7: 18, gt30: 38 },
    { ch: 'airbnb',  name: 'Airbnb',     median: 18, lt7: 22, gt30: 28 },
    { ch: 'direct',  name: 'Сайт',        median: 12, lt7: 38, gt30: 18 },
    { ch: 'whatsapp',name: 'WhatsApp',   median: 6,  lt7: 64, gt30: 4  },
    { ch: 'instagram',name:'Instagram',   median: 8,  lt7: 52, gt30: 8  },
  ],
  leadTrend: (() => {
    // 52 weeks
    const arr = [];
    for (let w = 0; w < 52; w++) {
      // gradual decline 18 -> 12 with noise
      const t = w / 51;
      const v = 18 - t * 6 + (Math.sin(w * 0.6) * 1.5);
      arr.push(Math.max(8, Math.round(v * 10) / 10));
    }
    return arr;
  })(),

  // ============ Section 4 — ALOS & patterns ============
  alosDistribution: [
    { nights: '1', pct: 22, dominant: false },
    { nights: '2', pct: 48, dominant: true },
    { nights: '3', pct: 16, dominant: false },
    { nights: '4', pct: 8,  dominant: false },
    { nights: '5', pct: 3,  dominant: false },
    { nights: '6', pct: 2,  dominant: false },
    { nights: '7+',pct: 1,  dominant: false },
  ],
  alosSeasonality: (() => {
    const months = ['Я','Ф','М','А','М','И','И','А','С','О','Н','Д'];
    const bins = ['1N','2N','3N','4+N'];
    // for each month, distribution across bins (sums 100)
    const distrib = [
      [28,52,14,6], [26,54,14,6], [24,52,16,8], [22,48,18,12],
      [18,42,22,18], [14,38,26,22], [12,32,28,28], [14,36,28,22],
      [20,46,20,14], [24,52,16,8],  [28,52,14,6], [26,50,16,8],
    ];
    return { months, bins, grid: distrib };
  })(),
  arrivalsByDow: [
    { d: 'Пн', pct: 4 }, { d: 'Вт', pct: 6 }, { d: 'Ср', pct: 8 }, { d: 'Чт', pct: 12 },
    { d: 'Пт', pct: 32 },{ d: 'Сб', pct: 28 },{ d: 'Вс', pct: 10 },
  ],
  departuresByDow: [
    { d: 'Пн', pct: 32 },{ d: 'Вт', pct: 8 }, { d: 'Ср', pct: 6 }, { d: 'Чт', pct: 6 },
    { d: 'Пт', pct: 4 }, { d: 'Сб', pct: 6 }, { d: 'Вс', pct: 38 },
  ],
  patternMatrix: {
    rows: ['Пн','Вт','Ср','Чт','Пт','Сб','Вс'],
    cols: ['1N','2N','3N','4+N'],
    // % of total bookings
    grid: [
      [1, 2, 0, 1],   // Mon
      [2, 3, 1, 0],   // Tue
      [3, 4, 1, 1],   // Wed
      [4, 5, 7, 2],   // Thu
      [4, 24, 4, 2],  // Fri (peak)
      [3, 20, 6, 2],  // Sat
      [4, 5, 1, 1],   // Sun
    ],
    insightRu: 'Топ паттерны: Пт+2N (24%), Сб+2N (20%), Чт+3N (7%). Эти три формата дают 51% всех броней.',
    insightEn: 'Top patterns: Fri+2N (24%), Sat+2N (20%), Thu+3N (7%). These three account for 51% of all bookings.',
  },

  // ============ Section 5 — Cancellations ============
  cancelFunnel: {
    default: {
      created: 165, cancelled: 18, noshow: 2, stayed: 145,
      lostRevenue: 8600, potential: 64800,
    },
    alert: {
      created: 165, cancelled: 28, noshow: 5, stayed: 132,
      lostRevenue: 14200, potential: 64800,
    },
  },
  cancelTiming: [
    { lbl: '30+', pct: 12, days: '30+ дней до' },
    { lbl: '15—30', pct: 22, days: '15—30 дней до' },
    { lbl: '8—14', pct: 38, days: '8—14 дней до' },
    { lbl: '3—7',  pct: 18, days: '3—7 дней до' },
    { lbl: '0—2',  pct: 10, days: '0—2 дня до' },
  ],
  cancelByChannel: {
    default: [
      { ch: 'airbnb',   name: 'Airbnb',      rate: 18, count: 6, tone: 'warn' },
      { ch: 'booking',  name: 'Booking.com', rate: 14, count: 5, tone: 'warn' },
      { ch: 'instagram',name: 'Instagram',   rate: 11, count: 2, tone: 'flat' },
      { ch: 'direct',   name: 'Сайт',         rate: 7,  count: 3, tone: 'good' },
      { ch: 'whatsapp', name: 'WhatsApp',    rate: 5,  count: 2, tone: 'good' },
    ],
    alert: [
      { ch: 'airbnb',   name: 'Airbnb',      rate: 28, count: 9, tone: 'bad' },
      { ch: 'booking',  name: 'Booking.com', rate: 22, count: 8, tone: 'bad' },
      { ch: 'instagram',name: 'Instagram',   rate: 16, count: 4, tone: 'warn' },
      { ch: 'direct',   name: 'Сайт',         rate: 14, count: 5, tone: 'warn' },
      { ch: 'whatsapp', name: 'WhatsApp',    rate: 8,  count: 2, tone: 'flat' },
    ],
  },
  // Reasons + Refunds intentionally empty for empty-state demo
  cancelReasons: { empty: true },
  refunds: { empty: true },

  // ============ Section 6 — Inventory & pricing ============
  cabins: {
    default: [
      { name: 'Cyprus',    type: 'Premium · 4 чел.',  nights: 52, available: 60, occ: 87, adr: '€280', rev: '€14 560', cancel: 8,  warn: false },
      { name: 'Bali',      type: 'Premium · 4 чел.',  nights: 50, available: 60, occ: 83, adr: '€260', rev: '€13 000', cancel: 10, warn: false },
      { name: 'Portofino', type: 'Standard · 2 чел.', nights: 44, available: 60, occ: 73, adr: '€220', rev: '€9 680',  cancel: 14, warn: false },
      { name: 'Latvia',    type: 'Standard · 2 чел.', nights: 42, available: 60, occ: 70, adr: '€220', rev: '€9 240',  cancel: 12, warn: false },
      { name: 'Maldives',  type: 'Premium · 4 чел.',  nights: 30, available: 60, occ: 50, adr: '€240', rev: '€7 200',  cancel: 18, warn: true },
      { name: 'Итого',     type: '',                  nights: 218, available: 300, occ: 73, adr: '€244', rev: '€53 680', cancel: 11, total: true },
    ],
  },
  adrDistribution: [
    { range: '€180—200', pct: 8 },
    { range: '€200—220', pct: 14 },
    { range: '€220—240', pct: 28 },
    { range: '€240—260', pct: 18 },
    { range: '€260—280', pct: 16 },
    { range: '€280—300', pct: 10 },
    { range: '€300+',    pct: 6 },
  ],
  adrMedian: '€244',
  dowPricing: [
    { d: 'Пн', adr: 200, occ: 38 },
    { d: 'Вт', adr: 200, occ: 42 },
    { d: 'Ср', adr: 210, occ: 52 },
    { d: 'Чт', adr: 230, occ: 68 },
    { d: 'Пт', adr: 280, occ: 96, yieldOpp: false },
    { d: 'Сб', adr: 240, occ: 94, yieldOpp: true },
    { d: 'Вс', adr: 220, occ: 76 },
  ],
  forecast6m: [
    { m: 'Май',  occ: 73, adr: 244, ly: 71 },
    { m: 'Июнь', occ: 84, adr: 268, ly: 78 },
    { m: 'Июль', occ: 92, adr: 295, ly: 86 },
    { m: 'Авг',  occ: 88, adr: 285, ly: 82 },
    { m: 'Сен',  occ: 71, adr: 240, ly: 68 },
    { m: 'Окт',  occ: 58, adr: 220, ly: 56 },
  ],
};

// =============== i18n strings for booking tab ===============
const BKG_T = {
  ru: {
    sec1: 'Booking Snapshot',
    sec1sub: 'Объёмы и поведение',
    sec2: 'Pace & Pickup',
    sec2sub: 'Динамика загрузки и pickup',
    sec3: 'Lead Time',
    sec3sub: 'Окно бронирования',
    sec4: 'Length of Stay & Patterns',
    sec4sub: 'Длительность стоянки и паттерны',
    sec5: 'Cancellations & No-shows',
    sec5sub: 'Отмены и неприезды',
    sec6: 'Inventory & Pricing',
    sec6sub: 'Загрузка и ценообразование',
    snap: {
      total: 'Всего броней', nights: 'Ночей забронировано', confirm: 'Подтверждённых ночей',
      otb: 'OTB Revenue', forecast: 'Forecast Occ. 30D',
      lead: 'Avg Lead Time', alos: 'ALOS', cancel: 'Cancel Rate',
      noshow: 'No-show Rate', window: 'Window Util.',
    },
    timeAxis: 'Ось времени',
    byBooking: 'По дате брони',
    byStay: 'По дате заезда',
    timeAxisHint: 'Booking date — «сколько броней пришло». Stay date — «сколько ночей было занято». Дают разные числа за один период.',
    alwaysStay: 'Всегда по дате заезда',
    alwaysBooking: 'Всегда по дате брони',
    paceTitle: 'Pace curve · следующие 90 дней',
    paceSub: 'Текущая загрузка vs медиана',
    paceCurrent: 'Текущая OTB',
    paceMedian: 'Медиана (90D)',
    paceBand: 'Типичный диапазон',
    daily: 'По дням', weekly: 'По неделям', pct: '%', nights: 'Ночи',
    bcTitle: 'Booking curve для выбранной даты',
    bcDate: 'Целевая дата',
    bcCurrent: 'Текущая аккумуляция',
    bcAvg: 'Средняя аккумуляция (похожие даты)',
    bcToday: 'Сегодня',
    bcDaysToGo: 'дней до заезда',
    pickupTitle: 'Pickup по будущим месяцам',
    pkCol: { month: 'Месяц', otb: 'Total OTB', p1: '1-day', p7: '7-day', p30: '30-day' },
    heatTitle: '90-дневный inventory heatmap',
    heatLeg: { b: 'Забронировано', a: 'Свободно', c: 'Недавно отменено', l: 'Ниже среднего ADR' },
    filterAll: 'Все', filterAvail: 'Только свободные', filterCancel: 'Недавно отменённые', filterLow: 'Ниже среднего ADR',
    leadDistTitle: 'Lead time · все брони',
    leadDistMedian: 'Медиана',
    leadChTitle: 'Lead time по каналам',
    leadChCol: { ch: 'Канал', median: 'Медиана', lt7: '<7д', gt30: '>30д' },
    leadTrendTitle: 'Lead time evolution · 12 месяцев',
    leadTrendInsight: 'Lead time снизился с 18 до 12 дней за 6 месяцев. Возможная причина: рост last-minute промо.',
    alosTitle: 'Распределение длительности стоянки',
    alosInsight: '2-night stays доминируют (48%). Средняя выручка на брони: €420.',
    alosSeasonTitle: 'ALOS сезонность',
    dowTitle: 'Заезды и выезды по дням недели',
    arrivals: 'Заезды', departures: 'Выезды',
    dowInsight: '60% заездов — пятница и суббота. 70% выездов — воскресенье и понедельник.',
    matrixTitle: 'Матрица: день заезда × длительность',
    matrixSub: '% от всех броней',
    cancelFunnelTitle: 'Куда уходят брони',
    cancelCreated: 'Создано', cancelCancelled: 'Отменено', cancelNoshow: 'No-show', cancelStayed: 'Состоялись',
    cancelLost: 'Потенциальные потери:',
    cancelOf: 'из',
    cancelTimingTitle: 'Когда происходят отмены',
    cancelTimingInsight: 'Концентрация в диапазоне 8—14 дней (типичный паттерн).',
    cancelTimingAlert: 'Всплеск в 0—2 дня (10% — изучить коммуникацию).',
    cancelByChTitle: 'Cancel rate по каналам',
    cancelReasonsTitle: 'Причины отмен',
    cancelReasonsEmpty: 'Настройте трекинг причин отмен в Cloudbeds, чтобы включить эту панель.',
    refundsTitle: 'Refunds tracking',
    refundsEmpty: 'Данные о возвратах не подключены. Подключите интеграцию для трекинга.',
    cabinsTitle: 'Performance по домикам',
    cabCol: { name: 'Домик', type: 'Тип', nights: 'Ночей', avail: 'Доступно', occ: 'Occ', adr: 'ADR', rev: 'Выручка', cancel: 'Cancel %' },
    adrDistTitle: 'Распределение цен',
    adrDistMedian: 'Медиана ADR',
    adrInsight: 'Диапазон €220—260 покрывает 46% броней. Ценообразование сконцентрировано в среднем тире.',
    dowPriceTitle: 'Ценообразование по дням недели',
    dowPriceLegA: 'Avg ADR', dowPriceLegO: 'Occupancy %',
    yieldOpp: 'Yield-возможность',
    fcTitle: 'Forecast occupancy и ADR · 6 месяцев',
    fcSummary: 'Прогноз: +8% YoY в летние месяцы, +4% в shoulder.',
    fcLegOcc: 'Occ %', fcLegAdr: 'ADR (€)', fcLegLy: 'LY occ',
  },
  en: {
    sec1: 'Booking Snapshot',
    sec1sub: 'Volumes and behavior',
    sec2: 'Pace & Pickup',
    sec2sub: 'Pace and pickup dynamics',
    sec3: 'Lead Time',
    sec3sub: 'Booking window',
    sec4: 'Length of Stay & Patterns',
    sec4sub: 'Length of stay and patterns',
    sec5: 'Cancellations & No-shows',
    sec5sub: 'Cancellations and no-shows',
    sec6: 'Inventory & Pricing',
    sec6sub: 'Occupancy and pricing patterns',
    snap: {
      total: 'Total Bookings', nights: 'Nights Booked', confirm: 'Confirmed Nights',
      otb: 'OTB Revenue', forecast: 'Forecast Occ. 30D',
      lead: 'Avg Lead Time', alos: 'ALOS', cancel: 'Cancel Rate',
      noshow: 'No-show Rate', window: 'Window Util.',
    },
    timeAxis: 'Time axis',
    byBooking: 'Booking date',
    byStay: 'Stay date',
    timeAxisHint: 'Booking date answers "how many bookings did we get?". Stay date — "how many nights were occupied?".',
    alwaysStay: 'Always by stay date',
    alwaysBooking: 'Always by booking date',
    paceTitle: 'Pace curve · next 90 days',
    paceSub: 'Current OTB vs median',
    paceCurrent: 'Current OTB',
    paceMedian: 'Median (90D)',
    paceBand: 'Typical range',
    daily: 'Daily', weekly: 'Weekly', pct: '%', nights: 'Nights',
    bcTitle: 'Booking curve for selected date',
    bcDate: 'Target date',
    bcCurrent: 'Current accumulation',
    bcAvg: 'Average accumulation (similar dates)',
    bcToday: 'Today',
    bcDaysToGo: 'days to go',
    pickupTitle: 'Pickup by future month',
    pkCol: { month: 'Month', otb: 'Total OTB', p1: '1-day', p7: '7-day', p30: '30-day' },
    heatTitle: '90-day inventory heatmap',
    heatLeg: { b: 'Booked', a: 'Available', c: 'Recently cancelled', l: 'Below avg ADR' },
    filterAll: 'All', filterAvail: 'Available only', filterCancel: 'Recently cancelled', filterLow: 'Below avg ADR',
    leadDistTitle: 'Lead time · all bookings',
    leadDistMedian: 'Median',
    leadChTitle: 'Lead time by channel',
    leadChCol: { ch: 'Channel', median: 'Median', lt7: '<7d', gt30: '>30d' },
    leadTrendTitle: 'Lead time evolution · 12 months',
    leadTrendInsight: 'Lead time has decreased from 18 to 12 days over 6 months. Possible cause: increased last-minute promotions.',
    alosTitle: 'Length of stay distribution',
    alosInsight: '2-night stays dominate (48%). Average revenue per stay: €420.',
    alosSeasonTitle: 'ALOS seasonality',
    dowTitle: 'Arrivals and departures by day of week',
    arrivals: 'Arrivals', departures: 'Departures',
    dowInsight: '60% of arrivals on Friday and Saturday. 70% of departures on Sunday and Monday.',
    matrixTitle: 'Arrival × length of stay matrix',
    matrixSub: '% of all bookings',
    cancelFunnelTitle: 'Where bookings end up',
    cancelCreated: 'Created', cancelCancelled: 'Cancelled', cancelNoshow: 'No-show', cancelStayed: 'Stayed',
    cancelLost: 'Lost potential revenue:',
    cancelOf: 'of',
    cancelTimingTitle: 'When cancellations happen',
    cancelTimingInsight: 'Concentration in 8—14 days range (typical pattern).',
    cancelTimingAlert: 'Spike in 0—2 days range (10% — investigate communication).',
    cancelByChTitle: 'Cancel rate by channel',
    cancelReasonsTitle: 'Cancellation reasons',
    cancelReasonsEmpty: 'Set up cancellation reason tracking in Cloudbeds to enable this view.',
    refundsTitle: 'Refunds tracking',
    refundsEmpty: 'Refunds data not connected. Set up integration to enable tracking.',
    cabinsTitle: 'Performance by cabin',
    cabCol: { name: 'Cabin', type: 'Type', nights: 'Nights', avail: 'Avail', occ: 'Occ', adr: 'ADR', rev: 'Revenue', cancel: 'Cancel %' },
    adrDistTitle: 'Price point distribution',
    adrDistMedian: 'Median ADR',
    adrInsight: 'Range €220—260 captures 46% of bookings. Pricing concentrated in mid-tier.',
    dowPriceTitle: 'Pricing by day of week',
    dowPriceLegA: 'Avg ADR', dowPriceLegO: 'Occupancy %',
    yieldOpp: 'Yield opportunity',
    fcTitle: 'Forecast occupancy and ADR · 6 months',
    fcSummary: 'Forecast: +8% YoY in summer months, +4% in shoulder season.',
    fcLegOcc: 'Occ %', fcLegAdr: 'ADR (€)', fcLegLy: 'LY occ',
  },
};

const BKG_HELP = {
  ru: {
    total: { title: 'Всего броней', body: 'Для чего: показывает объем входящих бронирований за период. Как работает: считает созданные брони по выбранной оси времени, включая отмененные, чтобы видеть общий спрос.' },
    nights: { title: 'Ночей забронировано', body: 'Для чего: показывает, сколько ночей было продано или запрошено. Как работает: суммирует длительность всех броней, включая отмененные, поэтому это индикатор спроса до очистки.' },
    confirm: { title: 'Подтвержденные ночи', body: 'Для чего: показывает реальную занятость после отмен. Как работает: из забронированных ночей исключаются отмены и no-show; этот показатель ближе к операционной загрузке.' },
    otb: { title: 'OTB Revenue', body: 'Для чего: показывает будущую выручку, уже стоящую on the books. Как работает: суммирует подтвержденные будущие брони по датам проживания.' },
    forecast: { title: 'Forecast Occ. 30D', body: 'Для чего: оценивает ожидаемую загрузку на ближайшие 30 дней. Как работает: сочетает текущий OTB, типичный pickup и исторический pace.' },
    lead: { title: 'Avg Lead Time', body: 'Для чего: показывает, насколько заранее гости бронируют. Как работает: считает средний интервал между датой брони и датой заезда.' },
    alos: { title: 'ALOS', body: 'Для чего: показывает среднюю длительность проживания. Как работает: суммирует ночи и делит на количество броней; помогает управлять минимальными ночами и пакетами.' },
    cancel: { title: 'Cancel Rate', body: 'Для чего: показывает долю броней, которые не дошли до проживания из-за отмен. Как работает: отмененные брони делятся на все созданные брони периода.' },
    noshow: { title: 'No-show Rate', body: 'Для чего: показывает гостей, которые не приехали без отмены. Как работает: no-show делится на все подтвержденные брони; рост требует предоплат или напоминаний.' },
    window: { title: 'Window Utilization', body: 'Для чего: показывает, насколько заполнено 60-дневное окно продаж. Как работает: сравнивает занятые будущие ночи с доступным фондом на горизонте 60 дней.' },
    paceCurve: { title: 'Pace curve', body: 'Для чего: заранее видеть, хватает ли будущих броней. Как работает: текущий OTB сравнивается с медианой и типичным диапазоном по дням вперед.' },
    bookingCurve: { title: 'Booking curve', body: 'Для чего: понять, нормально ли набирается конкретная дата заезда. Как работает: показывает аккумуляцию броней от 90 дней до arrival и сравнивает с похожими датами.' },
    pickup: { title: 'Pickup по месяцам', body: 'Для чего: видеть, какие будущие месяцы набирают брони прямо сейчас. Как работает: показывает прирост OTB за 1, 7 и 30 дней по каждому месяцу проживания.' },
    inventoryHeatmap: { title: 'Inventory heatmap', body: 'Для чего: быстро увидеть свободные, занятые и рискованные даты по каждому домику. Как работает: каждая ячейка — домик × день на 90 дней вперед.' },
    leadDist: { title: 'Lead time distribution', body: 'Для чего: понять типичное окно принятия решения гостем. Как работает: группирует брони по числу дней между booking date и arrival.' },
    leadChannel: { title: 'Lead time по каналам', body: 'Для чего: увидеть, какие каналы дают ранние или last-minute брони. Как работает: считает медиану lead time и доли <7 / >30 дней для каждого канала.' },
    leadTrend: { title: 'Lead time evolution', body: 'Для чего: заметить сдвиг спроса в сторону ранних или поздних броней. Как работает: строит недельный тренд медианного lead time за 12 месяцев.' },
    alosDist: { title: 'Распределение ALOS', body: 'Для чего: понять, на сколько ночей чаще приезжают гости. Как работает: группирует брони по длительности проживания и подсвечивает доминирующий формат.' },
    alosSeason: { title: 'ALOS сезонность', body: 'Для чего: увидеть, как длительность проживания меняется по месяцам. Как работает: каждый месяц раскладывается по долям 1N, 2N, 3N и 4+N.' },
    dow: { title: 'Заезды и выезды по дням', body: 'Для чего: планировать уборку, ресепшен и загрузку команды. Как работает: показывает распределение arrival и departure по дням недели.' },
    patternMatrix: { title: 'Arrival × length matrix', body: 'Для чего: найти самые частые паттерны проживания. Как работает: пересекает день заезда и длительность, показывая долю каждого сценария от всех броней.' },
    cancelFunnel: { title: 'Куда уходят брони', body: 'Для чего: понять, сколько созданных броней реально превращается в проживание. Как работает: раскладывает брони на stayed, cancelled и no-show, плюс считает потерянную выручку.' },
    cancelTiming: { title: 'Когда происходят отмены', body: 'Для чего: найти период, где чаще всего теряются брони. Как работает: группирует отмены по числу дней до заезда и подсвечивает опасные окна.' },
    cancelChannel: { title: 'Cancel rate по каналам', body: 'Для чего: понять, какие каналы дают более рискованные брони. Как работает: отмены делятся на созданные брони внутри каждого канала.' },
    cancelReasons: { title: 'Причины отмен', body: 'Для чего: видеть управляемые причины потерь. Как работает: панель заполнится, когда причины отмен начнут системно передаваться из Cloudbeds.' },
    refunds: { title: 'Refunds tracking', body: 'Для чего: контролировать возвраты и финансовые потери после отмен. Как работает: панель ждет подключение данных по refund-транзакциям.' },
    cabins: { title: 'Performance по домикам', body: 'Для чего: сравнить домики по загрузке, ADR, выручке и отменам. Как работает: считает показатели отдельно по каждому домику и строку total.' },
    adrDist: { title: 'Распределение цен', body: 'Для чего: понять, в каких ценовых диапазонах реально продаются ночи. Как работает: группирует брони по ADR и показывает медианный уровень.' },
    dowPricing: { title: 'Ценообразование по дням недели', body: 'Для чего: найти дни, где цена не соответствует спросу. Как работает: сравнивает occupancy и ADR по дням недели, подсвечивая yield-возможности.' },
    forecast: { title: 'Forecast occupancy and ADR', body: 'Для чего: видеть ожидаемую загрузку и цену на 6 месяцев. Как работает: объединяет прогноз occupancy, ADR и прошлогоднюю загрузку для сезонного сравнения.' },
  },
  en: {
    total: { title: 'Total bookings', body: 'Purpose: shows booking volume for the period. How it works: counts created bookings on the selected time axis, including cancelled bookings, to show raw demand.' },
    nights: { title: 'Nights booked', body: 'Purpose: shows how many nights were requested or sold. How it works: sums booking length including cancelled stays, so it reflects demand before cleanup.' },
    confirm: { title: 'Confirmed nights', body: 'Purpose: shows real occupancy after cancellations. How it works: booked nights minus cancelled and no-show nights; closer to operational occupancy.' },
    otb: { title: 'OTB Revenue', body: 'Purpose: shows future revenue already on the books. How it works: sums confirmed upcoming stays by stay date.' },
    forecast: { title: 'Forecast Occ. 30D', body: 'Purpose: estimates occupancy for the next 30 days. How it works: combines current OTB, typical pickup, and historical pace.' },
    lead: { title: 'Avg Lead Time', body: 'Purpose: shows how far in advance guests book. How it works: averages the gap between booking date and arrival date.' },
    alos: { title: 'ALOS', body: 'Purpose: shows average length of stay. How it works: total nights divided by bookings; useful for minimum-night rules and packages.' },
    cancel: { title: 'Cancel Rate', body: 'Purpose: shows the share of bookings lost to cancellation. How it works: cancelled bookings divided by all created bookings in the period.' },
    noshow: { title: 'No-show Rate', body: 'Purpose: shows guests who did not arrive without cancelling. How it works: no-shows divided by confirmed bookings; growth may require deposits or reminders.' },
    window: { title: 'Window Utilization', body: 'Purpose: shows how full the 60-day sales window is. How it works: booked future nights compared with available inventory over the next 60 days.' },
    paceCurve: { title: 'Pace curve', body: 'Purpose: spots whether future bookings are sufficient. How it works: compares current OTB with median and typical range by days ahead.' },
    bookingCurve: { title: 'Booking curve', body: 'Purpose: checks whether a specific arrival date is filling normally. How it works: shows booking accumulation from 90 days before arrival versus similar dates.' },
    pickup: { title: 'Pickup by month', body: 'Purpose: shows which future months are gaining bookings now. How it works: displays OTB pickup over 1, 7, and 30 days by stay month.' },
    inventoryHeatmap: { title: 'Inventory heatmap', body: 'Purpose: scans available, booked, and risky dates by cabin. How it works: each cell is one cabin-day over the next 90 days.' },
    leadDist: { title: 'Lead time distribution', body: 'Purpose: shows the normal decision window. How it works: groups bookings by days between booking date and arrival.' },
    leadChannel: { title: 'Lead time by channel', body: 'Purpose: shows which channels book early or last-minute. How it works: calculates median lead time and <7 / >30 day shares for each channel.' },
    leadTrend: { title: 'Lead time evolution', body: 'Purpose: catches demand shifts toward earlier or later bookings. How it works: plots weekly median lead time over 12 months.' },
    alosDist: { title: 'ALOS distribution', body: 'Purpose: shows how many nights guests usually stay. How it works: groups bookings by length of stay and highlights the dominant pattern.' },
    alosSeason: { title: 'ALOS seasonality', body: 'Purpose: shows how stay length changes by month. How it works: each month is split into 1N, 2N, 3N, and 4+N shares.' },
    dow: { title: 'Arrivals and departures', body: 'Purpose: helps plan housekeeping, reception, and staffing. How it works: distributes arrivals and departures by day of week.' },
    patternMatrix: { title: 'Arrival × length matrix', body: 'Purpose: finds the most common stay patterns. How it works: crosses arrival weekday with length of stay and shows share of all bookings.' },
    cancelFunnel: { title: 'Where bookings end up', body: 'Purpose: shows how many created bookings turn into actual stays. How it works: splits bookings into stayed, cancelled, and no-show, plus lost revenue.' },
    cancelTiming: { title: 'Cancellation timing', body: 'Purpose: identifies when bookings are most often lost. How it works: groups cancellations by days before arrival and highlights risky windows.' },
    cancelChannel: { title: 'Cancel rate by channel', body: 'Purpose: shows which channels produce riskier bookings. How it works: cancellations divided by created bookings within each channel.' },
    cancelReasons: { title: 'Cancellation reasons', body: 'Purpose: reveals controllable causes of booking loss. How it works: this panel fills once cancellation reasons are consistently tracked in Cloudbeds.' },
    refunds: { title: 'Refunds tracking', body: 'Purpose: controls refunds and financial loss after cancellations. How it works: waits for refund transaction data to be connected.' },
    cabins: { title: 'Cabin performance', body: 'Purpose: compares cabins by occupancy, ADR, revenue, and cancellations. How it works: calculates metrics per cabin plus a total row.' },
    adrDist: { title: 'Price distribution', body: 'Purpose: shows which price bands actually sell. How it works: groups bookings by ADR and displays the median price level.' },
    dowPricing: { title: 'Pricing by day of week', body: 'Purpose: finds days where price and demand are misaligned. How it works: compares occupancy and ADR by weekday and flags yield opportunities.' },
    forecast: { title: 'Forecast occupancy and ADR', body: 'Purpose: shows expected occupancy and price for the next 6 months. How it works: combines forecast occupancy, ADR, and last-year occupancy for seasonal comparison.' },
  },
};

window.BKG = BKG;
window.BKG_T = BKG_T;
window.BKG_HELP = BKG_HELP;
