// Mock franchise data — Hawke & Blade HQ (10 shops: 8 franchised + 2 corporate flagships)
// Numbers reconciled to HawkeAndBlade_Chain_Demo_Data_sep_2026.xlsx + Finance workbook (anchor month: Aug 2026).

const NETWORK = {
  brand: 'Hawke & Blade',
  totalLocations: 10,
  franchised: 8,
  corporate: 2,
};

function seedRandom(seed) {
  let s = seed;
  return () => { s = (s * 9301 + 49297) % 233280; return s / 233280; };
}

// Per-period scaling multipliers (relative to today)
const PERIOD_MULT = { T: 1, WTD: 6, MTD: 26, QTD: 78, YTD: 188 };

// Top-line KPIs — from the finance workbook roll-up (T = typical Aug trading day; YTD = Jan–Aug 2026)
const KPIS_BY_PERIOD = {
  T:   { netSales:   24000, serviceSales:  20200, productSales:  3800, availableHours:   550, bookedHours:   337, utilization: 61.3, revPerHour: 59.94, cancellationRate: 4.3, noShowRate: 4.3, royalties:   1460, nps: 30, complianceOpen: 3 },
  WTD: { netSales:  144000, serviceSales: 121200, productSales: 22800, availableHours:  3300, bookedHours:  2023, utilization: 61.3, revPerHour: 59.91, cancellationRate: 4.3, noShowRate: 4.3, royalties:   8770, nps: 30, complianceOpen: 5 },
  MTD: { netSales:  624104, serviceSales: 525286, productSales: 98818, availableHours: 14250, bookedHours:  8706, utilization: 61.1, revPerHour: 60.34, cancellationRate: 4.3, noShowRate: 4.3, royalties:  38000, nps: 30, complianceOpen: 6 },
  QTD: { netSales: 1838778, serviceSales:1547209, productSales:291569, availableHours: 42750, bookedHours: 26118, utilization: 61.1, revPerHour: 59.24, cancellationRate: 4.3, noShowRate: 4.3, royalties: 112100, nps: 30, complianceOpen: 9 },
  YTD: { netSales: 4517299, serviceSales:3809022, productSales:708276, availableHours:113900, bookedHours: 69641, utilization: 61.1, revPerHour: 54.70, cancellationRate: 4.3, noShowRate: 4.3, royalties: 273800, nps: 30, complianceOpen: 14 },
};

// 10 locations — 2 corporate flagships + 8 franchised. Revenue = typical day (Aug ÷ 26);
// growth = Jun–Aug vs Mar–May net revenue; trend = last 10 months net revenue ($K).
const LOCATIONS = [
  { id:'HB001', name:'Hawke & Blade — Toronto Downtown', city:'Toronto, ON',       type:'corporate',  opened:'2020-06', revenue: 2917, sssYoY:  5.2, utilization: 73.0, revPerHour: 61.52, nps: 30, royalties: 0,   casl: 100, pipeda: 100, retention: 58, loyaltyPenetration: 73, referrals: 50, redemptions: 118, engagement: 78, trend: [70,75,63,56,68,68,74,70,75,76], compliance:'green' },
  { id:'HB002', name:'Hawke & Blade — Mississauga',      city:'Mississauga, ON',   type:'corporate',  opened:'2021-03', revenue: 2818, sssYoY:  4.7, utilization: 73.0, revPerHour: 59.31, nps: 31, royalties: 0,   casl: 100, pipeda: 100, retention: 57, loyaltyPenetration: 72, referrals: 51, redemptions: 112, engagement: 76, trend: [70,76,61,60,67,65,75,71,73,73], compliance:'green' },
  { id:'HB003', name:'Hawke & Blade — Oakville',         city:'Oakville, ON',      type:'franchised', opened:'2022-09', revenue: 2589, sssYoY: 10.0, utilization: 65.0, revPerHour: 61.20, nps: 28, royalties: 207, casl: 100, pipeda: 100, retention: 59, loyaltyPenetration: 74, referrals: 53, redemptions: 109, engagement: 74, trend: [63,68,53,49,58,60,63,63,69,67], compliance:'green' },
  { id:'HB004', name:'Hawke & Blade — Burlington',       city:'Burlington, ON',    type:'franchised', opened:'2022-11', revenue: 2368, sssYoY:  8.6, utilization: 63.7, revPerHour: 57.10, nps: 30, royalties: 189, casl:  98, pipeda: 100, retention: 58, loyaltyPenetration: 73, referrals: 57, redemptions: 105, engagement: 72, trend: [62,71,54,49,57,57,60,62,65,62], compliance:'amber' },
  { id:'HB005', name:'Hawke & Blade — Hamilton',         city:'Hamilton, ON',      type:'franchised', opened:'2023-02', revenue: 2608, sssYoY: 15.0, utilization: 63.7, revPerHour: 62.90, nps: 33, royalties: 209, casl: 100, pipeda: 100, retention: 56, loyaltyPenetration: 71, referrals: 45, redemptions:  98, engagement: 71, trend: [60,67,52,47,55,57,62,65,67,68], compliance:'green' },
  { id:'HB006', name:'Hawke & Blade — Vaughan',          city:'Vaughan, ON',       type:'franchised', opened:'2023-05', revenue: 2404, sssYoY:  2.8, utilization: 62.6, revPerHour: 59.00, nps: 31, royalties: 192, casl:  96, pipeda:  98, retention: 57, loyaltyPenetration: 72, referrals: 53, redemptions:  96, engagement: 70, trend: [61,70,51,47,56,60,61,59,60,62], compliance:'amber' },
  { id:'HB007', name:'Hawke & Blade — Markham',          city:'Markham, ON',       type:'franchised', opened:'2023-08', revenue: 2380, sssYoY:  0.0, utilization: 63.9, revPerHour: 57.20, nps: 29, royalties: 190, casl: 100, pipeda: 100, retention: 59, loyaltyPenetration: 74, referrals: 33, redemptions:  74, engagement: 68, trend: [65,72,54,48,58,59,63,59,59,62], compliance:'green' },
  { id:'HB008', name:'Hawke & Blade — Brampton',         city:'Brampton, ON',      type:'franchised', opened:'2024-11', revenue: 2105, sssYoY: 11.5, utilization: 51.3, revPerHour: 63.10, nps: 30, royalties: 168, casl: 100, pipeda: 100, retention: 55, loyaltyPenetration: 70, referrals: 55, redemptions:  88, engagement: 66, trend: [51,55,38,36,45,47,53,53,54,55], compliance:'green' },
  { id:'HB009', name:'Hawke & Blade — Richmond Hill',    city:'Richmond Hill, ON', type:'franchised', opened:'2025-04', revenue: 2033, sssYoY: 15.5, utilization: 48.9, revPerHour: 63.90, nps: 27, royalties: 163, casl:  82, pipeda:  98, retention: 56, loyaltyPenetration: 71, referrals: 59, redemptions:  92, engagement: 64, trend: [47,56,35,36,41,44,49,49,52,53], compliance:'red', flags:['CASL'] },
  { id:'HB010', name:'Hawke & Blade — Scarborough',      city:'Scarborough, ON',   type:'franchised', opened:'2025-10', revenue: 1783, sssYoY:  3.6, utilization: 46.9, revPerHour: 58.40, nps: 31, royalties: 143, casl:  76, pipeda:  92, retention: 52, loyaltyPenetration: 67, referrals: 43, redemptions:  60, engagement: 52, trend: [50,59,39,36,43,42,47,45,46,46], compliance:'red', flags:['CASL','PIPEDA'] },
];

// Today's revenue breakdown (typical Aug trading day, network)
const REVENUE_BREAKDOWN = [
  { name:'Service Sales',  value: 20200, color:'#16A34A' },
  { name:'Product Sales',  value:  3800, color:'#0EA5E9' },
  { name:'Tips',           value:  4150, color:'#A855F7' },
  { name:'Gift Cards',     value:  1370, color:'#F59E0B' },
];

// Top services across network — 12-month revenue ÷ 312 trading days; shares of total service revenue
const TOP_SERVICES = [
  { name:'Haircut & Beard Trim',       revenue: 1761, share: 9.2, yoy:  6.2, color:'#16a34a' },
  { name:'Skin Fade',                  revenue: 1371, share: 7.2, yoy:  4.8, color:'#0ea5e9' },
  { name:'Adult Haircut',              revenue: 1358, share: 7.1, yoy:  3.1, color:'#f97316' },
  { name:'Wash, Cut & Style',          revenue: 1170, share: 6.1, yoy:  9.4, color:'#a855f7' },
  { name:'Boys Haircut',               revenue: 1124, share: 5.9, yoy:  2.6, color:'#eab308' },
  { name:'Women’s Haircut',       revenue: 1028, share: 5.4, yoy:  5.1, color:'#64748b' },
];

// Top products — month-scale units and revenue from Product Sales
const TOP_PRODUCTS = [
  { name:'Product Shampoo',              sku:'MZ887', units: 231, revenue: 4628, stock: 'healthy', yoy: 12.1, color:'#16a34a' },
  { name:'2-in-1 Shampoo & Conditioner', sku:'MZ889', units: 229, revenue: 4570, stock: 'low',     yoy:  9.4, color:'#0ea5e9' },
  { name:'Product Conditioner',          sku:'MZ888', units: 223, revenue: 4462, stock: 'low',     yoy:  8.2, color:'#f97316' },
  { name:'Sea Salt Spray',               sku:'MZ897', units: 205, revenue: 3681, stock: 'low',     yoy: 14.6, color:'#a855f7' },
  { name:'Mustache Wax',                 sku:'MZ903', units: 167, revenue: 3681, stock: 'healthy', yoy:  6.8, color:'#eab308' },
  { name:'Beard Balm',                   sku:'MZ899', units: 166, revenue: 3658, stock: 'critical',yoy: -3.1, color:'#fb7185' },
];

// Alerts — CEO-level only: cross-shop systemic, brand-damaging, money-shifting
const ALERTS = [
  { kind:'casl',      title:'CASL violation — Scarborough',    detail:'12 SMS sent to non-consenting customers on "Back to School Kids Cut". Jordan M. filed complaint Aug 18. Brand-level legal exposure.',   cta:'Open incident',        count:  1, severity:'critical' },
  { kind:'royalty',   title:'Royalty projection trending low',  detail:'Projected royalty from 2 franchised shops (Richmond Hill, Scarborough) trending below trailing-90-day baseline. Estimate only — confirm at reconciliation.', cta:'Review franchisees', count:  2 },
  { kind:'nps',       title:'NPS dip — Richmond Hill',         detail:'Score down 6 pts week-over-week. 3 detractor reviews cite the same barber. Brand reputation risk.',      cta:'View NPS feed',        count: 3 },
  { kind:'vendor',    title:'Vendor SLA breach',              detail:'Beard Balm (MZ899) — Northbridge missed the 14-day cover commitment. 2 shops stocked out, 1 low. Auto-POs fired, awaiting fulfill.', cta:'Open vendor file',     count:  3 },
  { kind:'underperf', title:'Underperformer watch',           detail:'Scarborough & Richmond Hill trailing network on revenue and utilization for 30+ days. Franchisee support flag.',   cta:'Open watch list',      count:  2 },
];

// CASL incident — full drill-down data
const CASL_INCIDENT = {
  id: 'INC-2026-031',
  location: 'HB010',
  locationName: 'Hawke & Blade — Scarborough',
  campaign: 'Back to School Kids Cut',
  channel: 'SMS',
  sent: 1483,
  violationCount: 12,
  sentAt: '2026-08-16 09:05 EDT',
  complaintFiledBy: 'Jordan M.',
  complaintFiledAt: '2026-08-18 14:38 EDT',
  complaintChannel: 'Unsubscribe form',
  complaintCite: 'CASL §6 — no consent',
  rootCause: 'List source: CSV uploaded by franchisee on Aug 12, bypassing consent gate. CSV importer is enabled for this account.',
  priorIncidents: 1,
  priorWindowDays: 90,
  agreementClause: '§12.3 — escalation after 3 violations',
  recipients: [
    { name:'Jordan M.',    addedOn:'2026-08-12', consent:false, source:'CSV import', status:'complained' },
    { name:'Priya S.',     addedOn:'2026-08-12', consent:false, source:'CSV import', status:'unsubscribed' },
    { name:'Marcus T.',    addedOn:'2026-08-12', consent:false, source:'CSV import', status:'opened' },
    { name:'Hassan K.',    addedOn:'2026-08-12', consent:false, source:'CSV import', status:'unopened' },
    { name:'Linnea R.',    addedOn:'2026-08-12', consent:false, source:'CSV import', status:'unsubscribed' },
    { name:'David W.',     addedOn:'2026-08-12', consent:false, source:'CSV import', status:'unopened' },
    { name:'Yuki H.',      addedOn:'2026-08-12', consent:false, source:'CSV import', status:'opened' },
    { name:'Amir F.',      addedOn:'2026-08-12', consent:false, source:'CSV import', status:'unopened' },
    { name:'Chen L.',      addedOn:'2026-08-12', consent:false, source:'CSV import', status:'unopened' },
    { name:'Sofia M.',     addedOn:'2026-08-12', consent:false, source:'CSV import', status:'unopened' },
    { name:'Reza B.',      addedOn:'2026-08-12', consent:false, source:'CSV import', status:'unopened' },
    { name:'Aisha N.',     addedOn:'2026-08-12', consent:false, source:'CSV import', status:'unopened' },
  ],
  auditTrail: [
    { ts:'2026-08-12 11:04', actor:'Franchisee · K. Manderson', action:'Uploaded CSV list (210 contacts)' },
    { ts:'2026-08-12 11:04', actor:'System',                    action:'Consent gate bypassed — CSV importer enabled' },
    { ts:'2026-08-16 09:05', actor:'Franchisee · K. Manderson', action:'Sent campaign "Back to School Kids Cut" to 1,483' },
    { ts:'2026-08-18 14:38', actor:'Customer · Jordan M.',      action:'Filed complaint via unsubscribe form citing CASL §6' },
    { ts:'2026-08-18 14:39', actor:'System',                    action:'Auto-flagged as CASL violation — severity raised to CRITICAL' },
  ],
};

// Marketing — real campaigns from the chain workbook (revenue ÷ spend = ROAS)
const MARKETING = [
  { name:'Refer a Mate',            channel:'WhatsApp',  roas:115.6, spend:  329, revenue: 37981, sent: 10951, opened: 10819, clicked: 1155, status:'ended' },
  { name:'Win Back Wednesday',      channel:'In-Store',  roas:276.5, spend:   52, revenue: 14465, sent:  2616, opened:  2366, clicked:  356, status:'ended' },
  { name:'Summer Splash 20% Off',   channel:'Email',     roas: 53.3, spend:  153, revenue:  8144, sent: 15270, opened:  3613, clicked:  537, status:'ended' },
  { name:'Spring Refresh 15% Off',  channel:'Email',     roas: 50.4, spend:  153, revenue:  7703, sent: 15270, opened:  3547, clicked:  501, status:'ended' },
  { name:'Retail Bundle Deal',      channel:'Email',     roas: 52.7, spend:  110, revenue:  5768, sent: 10951, opened:  2900, clicked:  382, status:'live' },
  { name:'Back to School Kids Cut', channel:'SMS',       roas:  2.5, spend:  764, revenue:  1914, sent: 15270, opened: 15238, clicked: 1198, status:'live' },
];

const CHANNEL_COLORS = {
  'Email':    '#0EA5E9',
  'SMS':      '#22C55E',
  'WhatsApp': '#10B981',
  'In-Store': '#F59E0B',
  'In-app':   '#A855F7',
  'Referral': '#F97316',
  'Loyalty':  '#EC4899',
};

// Real barbers per store (from the Team sheet) for the drill-down staff list
const BARBERS_BY_STORE = {
  HB001: ['Steven Rossi','Evan Evans','Dev Richardson','Kai Ricci','Tatiana Siddiqui'],
  HB002: ['Jian Gonzalez','Luis Lopez','Hassan Ahmed','Elliot Iyer','Mila Sheikh'],
  HB003: ['Alessandro Jackson','Ricardo Gonzalez','Marcus Sokolov','Diya Ali','Quinn Garcia'],
  HB004: ['Samuel Rashid','Lucas Thomas','Antonio Fedorov','Steven Parker','Natalia Phillips'],
  HB005: ['Pooja Esposito','Igor Ward','Freya Price','Paul Long','Mark Foster'],
  HB006: ['Isaiah Rodriguez','Dylan Gagne','Rashid Martin','Nadia Gonzalez','Francesca Bouchard'],
  HB007: ['Igor Morin','Vivian Mikhailov','Anton Cooper','Feng Esposito','Ursula Perez'],
  HB008: ['Kyle Smith','Trevor Bianchi','Madison Kumar','Nicholas Wilson','Marina Reyes'],
  HB009: ['Igor Harris','Anthony Russell','Min Ramirez','Jasper Taylor','Isabella Murphy'],
  HB010: ['Tariq Mitchell','Kai Ahmed','Rosalind Sokolov','Olivia Mehta','Chiara Edwards'],
};

// Drill-down builder
function buildLocationDetail(loc) {
  const rng = seedRandom(parseInt(loc.id.replace(/\D/g, ''), 10) || 1);
  const days = ['Mon','Tue','Wed','Thu','Fri','Sat','Sun'];
  const weekly = days.map(d => ({ d, rev: Math.round(loc.revenue/7 * (0.7 + rng() * 0.6)) }));
  const services = TOP_SERVICES.slice(0, 5).map(s => ({
    name: s.name,
    revenue: Math.round(loc.revenue * (s.share/100) * (0.6 + rng() * 0.8)),
    share: Math.round(s.share * (0.7 + rng() * 0.6) * 10) / 10,
  }));
  const staff = (BARBERS_BY_STORE[loc.id] || BARBERS_BY_STORE.HB001).slice(0, 5).map(n => ({
    name: n,
    util: Math.round((40 + rng() * 50) * 10) / 10,
    revenue: Math.round(loc.revenue / 5 * (0.6 + rng() * 0.8)),
  })).sort((a,b) => b.revenue - a.revenue);
  return { weekly, services, staff };
}

window.FRANCHISE_DATA = {
  NETWORK, KPIS_BY_PERIOD, PERIOD_MULT, LOCATIONS, REVENUE_BREAKDOWN,
  TOP_SERVICES, TOP_PRODUCTS, ALERTS, CASL_INCIDENT, MARKETING, CHANNEL_COLORS,
  BARBERS_BY_STORE, buildLocationDetail,
};
