/* global window */
// Data: client types, services with real tier×commitment pricing, FAQ
// Source: official GoGorilla pricing sheet (provided by client 2026-04-24)

const CLIENT_TYPES = [
  {
    id: 'founder',
    subBrand: 'Founders',
    heading: 'For Founders',
    desc: "I'm a founder or business leader",
    icon: 'assets/icons/client-founders.webp',
    logo: 'assets/logos/gogorilla-founders.webp',
    perks: ['Multi-service bundles', 'Flexible 3-month commits', 'Free growth audit'],
    quickBullets: [
      'Live in 14 days, not 4 months',
      'Save up to 50% vs in-house',
      'Founders Portal access',
      'Multi-service discount up to 45%',
    ],
    valueStrip: [
      { icon: '✦', title: 'Transparent pricing', desc: 'Fixed monthly retainers. No surprise invoices.' },
      { icon: '◆', title: 'Bundle discounts', desc: 'Stack 2+ services and save up to 45%.' },
      { icon: '◇', title: 'No-lock pilots', desc: 'Try us on a 3-month commit before scaling up.' },
      { icon: '✧', title: 'Capital-efficient', desc: 'Built-in profit sharing aligns us with your runway.' },
    ],
    intents: [
      {
        id: 'founder-standard',
        title: 'I prefer standard pricing',
        desc: 'Transparent, fixed pricing with built-in Algorithmic Team Profit Sharing. A fully accountable team focused on your success, without complex pricing models.',
        icon: 'assets/icons/transparency.webp',
        info: 'Most predictable. Best for teams that want clear monthly costs.',
      },
      {
        id: 'founder-tailored',
        title: 'I need a tailored solution',
        desc: 'For founders who need a custom approach, want standalone services, or are unsure where to start. We prepare a proposal based on your specific needs.',
        icon: 'assets/icons/partnership.webp',
        info: 'Bespoke scope. Best for unique or complex situations.',
      },
    ],
  },
  {
    id: 'investor',
    subBrand: 'Ventures',
    heading: 'For Investors',
    desc: "I'm an angel, venture capital, or private equity investor",
    icon: 'assets/icons/client-ventures.webp',
    logo: 'assets/logos/gogorilla-ventures.webp',
    perks: ['Portfolio-wide rates', 'Dedicated portfolio CSM', 'Diligence-ready reporting'],
    quickBullets: [
      'Free Investor Network access',
      'AI-matched founder deal flow',
      '20% portfolio-company discount',
      'Trusted Partner Network',
    ],
    valueStrip: [
      { icon: '✦', title: 'Free DD assessments', desc: 'Repeatable marketing diligence for every deal.' },
      { icon: '◆', title: '20% portfolio discount', desc: 'Discounted rates across the companies you back.' },
      { icon: '◇', title: '10% recurring commission', desc: 'On every portfolio company you refer.' },
      { icon: '✧', title: 'Diligence-ready data', desc: 'Reporting that drops straight into your diligence notes.' },
    ],
    intents: [
      {
        id: 'investor-considering',
        title: "I'm considering an investment",
        desc: 'Get a complimentary marketing assessment to strengthen your due diligence.',
        icon: 'assets/icons/calculator.webp',
        info: 'Pre-investment diligence. Free, no obligation.',
      },
      {
        id: 'investor-portfolio',
        title: 'I want to grow my portfolio companies',
        desc: 'Get a tailored proposal to accelerate growth for your portfolio companies.',
        icon: 'assets/icons/seo.webp',
        info: 'Portfolio-wide engagement with consolidated reporting.',
      },
      {
        id: 'investor-partnership',
        title: "I'm interested in an investment partnership",
        desc: 'Access our investor network, portfolio benefits, and optional commission.',
        icon: 'assets/icons/partnership.webp',
        info: 'Long-term capital partnership with shared upside.',
      },
    ],
  },
  {
    id: 'agency',
    subBrand: 'Agencies',
    heading: 'For Agencies',
    desc: "I'm an agency, consultancy, or marketing professional",
    icon: 'assets/icons/client-agencies.webp',
    logo: 'assets/logos/gogorilla-agencies.webp',
    perks: ['40% white-label discount', 'Unbranded deliverables', 'SLA reporting'],
    quickBullets: [
      'Agency pricing',
      'Dedicated CSM',
      'GorillaPerks™ benefits',
    ],
    // Dynamic bullets shown after intent is selected (updates the strip in real-time)
    quickBulletsByIntent: {
      'agency-own': [
        'Agency pricing',
        'Dedicated CSM',
        'GorillaPerks™ benefits',
      ],
      'agency-whitelabel': [
        'Wholesale pricing',
        'Anonymous delivery',
        'GorillaPerks™ benefits',
      ],
    },
    valueStrip: [
      { icon: '✦', title: 'White-label ready', desc: 'Unbranded deliverables you ship as your own.' },
      { icon: '◆', title: '40% partner pricing', desc: 'Best margin tier in the industry. SLA-backed.' },
      { icon: '◇', title: 'Dedicated agency CSM', desc: 'A single point of contact across all clients.' },
      { icon: '✧', title: 'Reseller perks', desc: 'GorillaPerks for Resellers + 10% recurring referral commission, for the duration of the minimum commitment.' },
    ],
    discount: 0.4,
    intents: [
      {
        id: 'agency-own',
        title: 'Growing my own agency with GoGorilla',
        desc: "We use GoGorilla's services internally to grow our agency, strategy, ops, and content for our own brand.",
        icon: 'assets/icons/agency.webp',
        discount: 0.15,
        info: '15% partner discount on every service. Standard turnaround, GoGorilla-branded reporting.',
      },
      {
        id: 'agency-whitelabel',
        title: 'White-labelling GoGorilla services to my clients',
        desc: 'We resell GoGorilla as our own fulfilment, unbranded deliverables we hand off to our clients.',
        icon: 'assets/icons/affiliate.webp',
        discount: 0.4,
        info: '40% white-label discount, unbranded deliverables, SLA reporting, dedicated agency CSM.',
      },
      {
        id: 'agency-referral',
        title: 'Referring a client to GoGorilla',
        desc: 'You know a business that needs GoGorilla. Refer them and earn a 10% recurring commission for the duration of their minimum commitment. Your referred client also gets 10% off their plan for the same period.',
        icon: 'assets/icons/affiliate.webp',
        comingSoon: true,
      },
      {
        id: 'agency-proposal',
        title: 'Building a proposal for a specific client',
        desc: 'Build a white-label proposal for a client. We help you scope, price, and present it as your own.',
        icon: 'assets/icons/agency.webp',
        comingSoon: true,
      },
    ],
  },
];

const TIERS = [
  { id: 'starter',    name: 'Starter',    blurb: 'Essentials only' },
  { id: 'grow',       name: 'Grow',       blurb: 'Proven playbook' },
  { id: 'scale',      name: 'Scale',      blurb: 'Full managed pod' },
  { id: 'enterprise', name: 'Enterprise', blurb: 'Custom scope', isEnterprise: true },
];

// ── Tier feature pill chips ──────────────────────────────────────────────
// Short (≤4 words) feature labels rendered as pill chips under the tier
// description on each service card. Distilled from Airtable Services bullets
// (base appRgD9IZC7YHxw1d, table Services, "Title (from Options Ref)").
//
// Lookup: TIER_BADGES[serviceId][tierId] -> array of strings.
// Falls back to empty array (no chips rendered) when missing.
const TIER_BADGES = {
  sales: {
    starter:    ['Email and LinkedIn outreach', '750 prospects/mo', 'ICP-matched signals'],
    grow:       ['Email and call outreach', '1,000 prospects/mo', 'Multi-channel'],
    scale:      ['Email and call outreach', 'Advanced signals', 'Paid ad warm-up', '1,250 prospects/mo'],
    enterprise: ['All signals', 'Senior SDRs', 'Dedicated strategist', 'Unlimited prospects'],
  },
  'paid-ads': {
    starter:    ['1 search channel', 'GorillaAudit®'],
    grow:       ['1 social channel', 'Unlimited creatives', 'GorillaAudit®'],
    scale:      ['2 channels', 'Senior specialists', 'Unlimited creatives'],
    enterprise: ['All channels', 'Custom strategy', 'Dedicated team'],
  },
  email: {
    starter:    ['Domain authentication', 'Basic segmentation'],
    grow:       ['Intermediate design', 'Mid-level specialists', 'Segmentation'],
    scale:      ['Advanced design', 'Senior specialists', 'Authentication'],
    enterprise: ['Custom design', 'Predictive segmentation', 'Up to 12 specialists'],
  },
  smm: {
    starter:    ['Up to 5 specialists', 'Standard graphics', 'GorillaAudit®'],
    grow:       ['Up to 8 specialists', 'Custom graphics', 'Community engagement'],
    scale:      ['Senior specialists', 'Advanced graphics', 'Strategy optimisation'],
    enterprise: ['Up to 12 specialists', 'Premium graphics', 'Slack collaboration'],
  },
  content: {
    starter:    ['SEO articles', 'Editorial briefs'],
    grow:       ['SEO articles', 'Editorial calendar', 'Distribution'],
    scale:      ['Editorial team', 'Optimisation', 'Distribution'],
    enterprise: ['Custom strategy', 'Premium production', 'Dedicated team'],
  },
  motion: {
    starter:    ['Junior designer', 'Standard quality'],
    grow:       ['Mid-level designer', 'Custom designs'],
    scale:      ['Senior designer', 'Premium quality'],
    enterprise: ['Premium designer', 'Custom production', 'Dedicated team'],
  },
  whitelabel: {
    starter:    ['Self-serve catalogue', 'Unbranded reports', 'Pay-as-you-go'],
    grow:       ['White-label portal', 'Workspace sync', 'Multi-channel comms'],
    scale:      ['Senior account manager', 'Multi-service discounts', 'Priority onboarding'],
    enterprise: ['Dedicated delivery team', 'Co-branded proposals', 'Custom SLAs'],
  },
  fundraising: {
    starter:    ['Pitch deck', '3-year model', 'Founders Pro included'],
    grow:       ['Premium deck', '50 investors', 'Grant support'],
    scale:      ['5-year model', '150+ investors', 'Founders Pro+ included'],
    enterprise: ['Custom scope', 'Dedicated strategist'],
  },
  'founders-portal': {
    starter:    ['100 UK investors', 'AI matching', 'Personal dashboard'],
    grow:       ['7,500+ investors', 'Verified badge', 'Priority placement'],
    scale:      ['Strategy call', 'Readiness assessment', 'Curated investor list'],
  },
  'investor-portal': {
    starter:    ['Due diligence access', '20% portfolio discount', 'Partner Network'],
    grow:       ['Deal flow access', 'Warm intros', 'Portfolio discounts'],
    scale:      ['Buy-side sourcing', 'Deal origination', 'Mandate management'],
  },
};
window.TIER_BADGES = TIER_BADGES;

const COMMITMENTS = [
  { id: '3',  months: 3,  label: '3 months',  save: 0  },
  { id: '6',  months: 6,  label: '6 months',  save: 20 },
  { id: '12', months: 12, label: '12 months', save: 40 },
];

// Pricing helper: look up exact £/mo from matrix using THIS service's own minimum
// commitment (commitMonths), or 'Custom' for Enterprise/one-time.
// Returns { value: Number|null, label: String, custom: Boolean, oneTime: Boolean }
function priceFor(service, tierId, overrideCommitId) {
  if (!service || !tierId) return { value: 0, label: ',', custom: false };
  if (tierId === 'enterprise') return { value: 0, label: 'Contact for pricing', custom: true };
  if (service.oneTime) {
    const val = service.prices?.[tierId];
    if (val == null) return { value: 0, label: 'Custom', custom: true };
    return { value: val, label: window.fmt(val), custom: false, oneTime: true };
  }
  if (service.freeStarter && tierId === 'starter') return { value: 0, label: 'Free', custom: false };
  // Use override if the service supports a price at that commit; else fall back to the service default.
  const tierPrices = service.prices?.[tierId];
  let commitId;
  if (tierPrices && typeof tierPrices === 'object' && overrideCommitId != null && tierPrices[String(overrideCommitId)] != null) {
    commitId = String(overrideCommitId);
  } else {
    commitId = '12';
  }
  // Some services have a flat-number price per tier (no commit matrix), return that directly.
  if (tierPrices != null && typeof tierPrices !== 'object') {
    return { value: tierPrices, label: window.fmt(tierPrices), custom: false };
  }
  const val = tierPrices?.[commitId];
  if (val == null) return { value: 0, label: 'Custom', custom: true };
  return { value: val, label: window.fmt(val), custom: false, commitId };
}

// Returns the array of available commit options for a service, or null if the
// service has only a single (flat) price per tier and no commit choice applies.
function commitsFor(service) {
  if (!service || service.oneTime) return null;
  // Inspect any tier price entry, if it's an object keyed by commit ids, expose those.
  const tiers = service.prices ? Object.values(service.prices) : [];
  const matrix = tiers.find(t => t && typeof t === 'object');
  if (!matrix) return null;
  const ids = Object.keys(matrix);
  return window.COMMITMENTS.filter(c => ids.includes(c.id));
}

// Human-readable commitment label for a service. Returns null if the service
// doesn't have a recurring commitment (one-time deliverables).
function commitLabelFor(service, overrideCommitId) {
  if (!service || service.oneTime) return null;
  // Prefer the user's override when it's a valid option for the service
  const opts = window.commitsFor ? window.commitsFor(service) : null;
  let m = 12;
  if (overrideCommitId != null && opts && opts.some(o => o.id === String(overrideCommitId))) {
    const opt = opts.find(o => o.id === String(overrideCommitId));
    if (opt) m = opt.months;
  }
  return `${m}-month minimum commitment`;
}

const SERVICES = [
  {
    id: 'sales',
    name: 'Sales & Demand Generation',
    desc: 'Outbound SDR pods, booking, and pipeline management.',
    category: 'Growth',
    icon: 'assets/icons/algorithmic.webp',
    commitMonths: 6,
    waitlistTiers: [],
    tierFeatures: {
      starter:    ['Email and LinkedIn outreach', '150 contacts/week', 'Shared CRM'],
      grow:       ['Email and call outreach', '400 contacts/week', 'Dedicated CRM workspace', 'Weekly strategy call'],
      scale:      ['Email and call outreach', 'Advanced signals', '1,000+ contacts/week', 'Full pipeline ops', 'Multi-threaded outbound'],
      enterprise: ['All signals', 'Senior SDRs', 'Dedicated strategist', 'Local presence'],
    },
    prices: {
      starter: { '3': 1995, '6': 1395, '12':  995 },
      grow:    { '3': 3295, '6': 2295, '12': 1795 },
      scale:   { '3': 3995, '6': 2995, '12': 2495 },
    },
    // One-time setup fees per tier. Sourced from Airtable Services table.
    setupFees: {
      starter: 495,
      grow:    995,
      scale:   1995,
    },
    tierDesc: {
      starter: 'B2B founders looking for managed email outreach. We book qualified meetings to your calendar',
      grow: 'B2B founders looking for managed multitouch outbound. Our in-house human cold calling is sequenced in',
      scale: 'B2B sales leaders looking for managed outbound. We layer in web-intent, paid ad warm-up & multithreading',
      enterprise: 'B2B sales leaders scaling enterprise outbound with senior SDRs, local presence & a dedicated strategist',
    },
    incentives: [
      { type: 'built-in', label: 'Algorithmic Team Profit Sharing' },
      { type: 'built-in', label: 'Outcome-Linked Success Bonus' },
    ],
    addons: [
      // ── Outbound acceleration ──────────────────────────────────────────
      { id: 'byol',             group: 'Outbound acceleration', name: 'Bring Your Own List',                  desc: '15% off your retainer when you supply a verified prospect list.', tip: 'Supply your own verified list and we take 15% off your monthly retainer. We still enrich the data, write the copy, and run the full cadence for you.', priceLabel: '-15% off retainer', price: 0, unit: 'discount' },
      { id: 'li-profiles',      group: 'Outbound acceleration', name: 'Additional LinkedIn Profiles',           desc: 'Extra LinkedIn seats, warmed up and Sales Navigator-ready.', tip: 'Adds more LinkedIn sending profiles to your campaign, each warmed up and equipped with Sales Navigator, so you can widen daily outreach safely.',           priceLabel: 'From £399', price: 399, unit: '/mo' },
      { id: 'domains',          group: 'Outbound acceleration', name: 'Additional Email Domains & Accounts',    desc: 'Extra sending infrastructure for higher-volume campaigns.', tip: 'More sending domains and mailboxes so you can raise outbound volume while protecting deliverability and staying within safe per-inbox limits.',  priceLabel: 'From £199', price: 199, unit: '/mo' },
      // 2026-05-30: 'Supplementary Dialling' and 'Dedicated SDR' add-ons removed (no longer offered).
      { id: 'native-crm',       group: 'Outbound acceleration', name: 'Native CRM Sync',                     desc: 'Two-way sync with HubSpot, Pipedrive, or Salesforce.', tip: 'Keeps your CRM and our outbound platform in sync both ways in real time, so there are no manual exports and no lag on contact or activity data.', priceLabel: 'From £99', price: 99, unit: '/mo' },
      { id: 'overseas-calling', group: 'Outbound acceleration', name: 'Overseas Cold Calling',                desc: 'Extend calling into North America or APAC time zones beyond UK hours.', priceLabel: 'From £295', price: 295, unit: '/mo', tip: 'Overseas calling rates vary by country and region. We confirm your exact per-country rate at onboarding.' },
      // ── Paid Advertising Layer ───────────────────────────────────────────────
      { id: 'ads-lite',         group: 'Paid Advertising Layer',     name: 'Paid Advertising Integration (Lite)',    desc: 'A light-touch paid layer running alongside your outbound.', tip: 'A starter paid-media layer that supports outbound with retargeting and a few core campaigns. Management fee only, your ad spend is paid directly to the platforms.', priceLabel: 'From £799', price: 799, unit: '/mo' },
      { id: 'ads-full',         group: 'Paid Advertising Layer',     name: 'Paid Advertising Integration (Full)',    desc: 'Full-funnel paid media managed alongside your outbound.', tip: 'Full paid-media management across your funnel, integrated with outbound. Available on the Enterprise plan. Management fee only, ad spend is paid directly to the platforms.',                 priceLabel: 'See Paid Advertising plans', custom: true },
      // ── Premium Sourcing ─────────────────────────────────────────────────────
      { id: 'ps-social',        group: 'Premium Sourcing',           name: 'Premium Sourcing: Social & Professional Networks',    desc: 'Prospects sourced from LinkedIn, Instagram, and similar platforms.', tip: 'We source prospects from social and professional networks, then GDPR-validate and fully enrich each record before it enters your cadence. Priced per prospect.', priceLabel: 'From £2.50', price: 2.50, unit: '/prospect', onboardingStatus: 'limited' },
      { id: 'ps-review',        group: 'Premium Sourcing',           name: 'Premium Sourcing: Review & Reputation Platforms',     desc: 'Prospects sourced from G2, Trustpilot, Capterra, and similar sites.', tip: 'We source from review and reputation platforms, then validate and enrich each prospect. Useful for reaching buyers who are actively comparing tools. Priced per prospect.', priceLabel: 'From £1.75', price: 1.75, unit: '/prospect', onboardingStatus: 'limited' },
      { id: 'ps-directories',   group: 'Premium Sourcing',           name: 'Premium Sourcing: Business Directories & Registries', desc: 'Prospects from Companies House, trade bodies, and public registries.', tip: 'We source from UK and international directories and registries, then validate and enrich. Good for sector or location-specific targeting. Priced per prospect.', priceLabel: 'From £1.25', price: 1.25, unit: '/prospect', onboardingStatus: 'limited' },
      { id: 'ps-marketplaces',  group: 'Premium Sourcing',           name: 'Premium Sourcing: Marketplaces & Commerce',           desc: 'Sellers sourced from Amazon, Etsy, eBay Business, and similar.', tip: 'We identify and qualify sellers across major marketplace platforms, then enrich each record. Ideal for reaching e-commerce and DTC operators. Priced per prospect.', priceLabel: 'From £1.75', price: 1.75, unit: '/prospect', onboardingStatus: 'limited' },
      { id: 'ps-local',         group: 'Premium Sourcing',           name: 'Premium Sourcing: Local, Travel & Hospitality',       desc: 'Hotels, restaurants, travel, and local service businesses.', tip: 'We source from UK and international local, travel, and hospitality directories, then validate and enrich. Suited to location-based outreach. Priced per prospect.', priceLabel: 'From £1.50', price: 1.50, unit: '/prospect', onboardingStatus: 'limited' },
      { id: 'ps-custom',        group: 'Premium Sourcing',           name: 'Premium Sourcing: Custom / Bespoke Scrape',           desc: 'Bespoke sourcing from any platform or source you specify.', tip: 'Tell us the source and we scope a bespoke scrape, then validate and enrich the results. Scoped and quoted at onboarding, and minimum volumes apply.', priceLabel: 'From £4.50', price: 4.50, unit: '/prospect', onboardingStatus: 'limited' },
      // ── Signals & Buying Intent ──────────────────────────────────────────────
      { id: 'gorilla-signals',  group: 'Signals & Buying Intent',    name: 'GorillaSignals™: Web Intent & Visitor Intelligence', desc: 'Spot anonymous website visitors and reach out when intent is highest.', tip: 'We de-anonymise website visitors at IP level, score their intent, and enrol the strongest signals into outbound automatically, so you reach buyers while they are still looking.', priceLabel: 'From £249', price: 249, unit: '/mo' },
      { id: 'hiring-signals',   group: 'Signals & Buying Intent',    name: 'Hiring & Job-Change Signals',         desc: 'Flag prospects who are hiring, expanding, or changing roles.', tip: 'Adds real-time hiring and job-change data to your cadence, so prospects are contacted exactly when a trigger event makes them more likely to buy.', priceLabel: 'From £149', price: 149, unit: '/mo' },
      // ── Physical Touch & Field Coverage ─────────────────────────────────────
      { id: 'physical-mail',    group: 'Physical Touch & Field Coverage', name: 'Physical Mail: Printed & Handwritten Letters',    desc: 'Printed and handwritten letters sent from our UK base. UK only.', tip: 'We send printed or handwritten letters from our UK fulfilment base to cut through crowded inboxes for high-value prospects. Currently UK addresses only.', priceLabel: 'From £1.50', price: 1.50, unit: '/letter' },
      { id: 'field-sales',      group: 'Physical Touch & Field Coverage', name: 'In-Person Field Sales & Event Coverage',          desc: 'In-person reps for events, visits, and appointment confirmation. UK only.', tip: 'We provide in-person coverage for trade shows, store visits, and door-to-door or appointment confirmation, drawn from our UK university partnership network. UK only.', priceLabel: 'From £219', price: 219, unit: '/day' },
    ],
  },
  {
    id: 'paid-ads',
    name: 'Paid Advertising',
    desc: 'Google, Meta, TikTok, LinkedIn. Managed media spend.',
    category: 'Growth',
    icon: 'assets/icons/paid-ads.webp',
    badge: 'popular',
    commitMonths: 3,
    waitlistTiers: [],
    tierFeatures: {
      starter: ['1 channel', 'Up to £10k/mo ad spend', 'Monthly reporting'],
      grow:    ['3 channels', 'Up to £50k/mo ad spend', 'Weekly reporting', 'Creative refresh'],
      scale:   ['All channels', 'Unlimited managed spend', 'Daily pacing', 'Dedicated media buyer'],
    },
    prices: {
      starter: { '3':  795, '6':  595, '12':  495 },
      grow:    { '3': 1495, '6': 1095, '12':  895 },
      scale:   { '3': 2495, '6': 1895, '12': 1495 },
    },
    setupFees: {
      starter: 245,
      grow:    345,
      scale:   445,
    },
    tierDesc: {
      starter: 'DTC & B2B founders looking to acquire new customers through fully managed paid search advertising',
      grow: 'DTC & B2B founders looking to acquire new customers through fully managed paid social media advertising',
      scale: 'DTC & B2B founders looking to scale through fully managed multi-channel paid advertising with full-funnel optimisation',
      enterprise: 'DTC & B2B founders looking to scale through fully managed enterprise-scale paid with bespoke teams & category specialists',
    },
    incentives: [
      { type: 'built-in', label: 'Algorithmic Team Profit Sharing' },
      { type: 'optional', label: 'Performance Recognition Tips' },
      { type: 'add-on',  label: 'Outcome-Linked Success Bonus' },
    ],
    addons: [
      // ── Channel expansion ────────────────────────────────────────────
      { id: 'meta-retarget',     group: 'Channel expansion', name: 'Meta Retargeting',                             desc: 'Retargeting campaigns across Facebook and Instagram.', tip: 'We build and manage retargeting across Facebook and Instagram to bring warm audiences back. Management fee only, your ad spend goes directly to Meta.',                                             price: 99,   unit: '/mo',      badge: 'popular' },
      { id: 'google-display',    group: 'Channel expansion', name: 'Google Display Ads',                           desc: 'Programmatic display across the Google Display Network.', tip: 'We run programmatic display for reach and retargeting across the Google Display Network. Management fee only, ad spend is paid directly to Google.',                                         priceLabel: 'From £99',  price: 99,   unit: '/mo' },
      { id: 'google-shop',       group: 'Channel expansion', name: 'Google Shopping Ads',                          desc: 'Merchant Center feed management and Shopping campaigns.', tip: 'We manage your Merchant Center feed and run Shopping campaigns to put your products in front of high-intent searchers. Management fee only, ad spend paid directly to Google.',                                         priceLabel: 'From £99',  price: 99,   unit: '/mo' },
      { id: 'linkedin-retarget', group: 'Channel expansion', name: 'LinkedIn Ads Retargeting',                     desc: 'Retargeting and sponsored content across LinkedIn.', tip: 'We run LinkedIn retargeting and sponsored content to re-engage professional audiences. Management fee only, your ad spend goes directly to LinkedIn.',                                     price: 99,   unit: '/mo' },
      // ── B2B audience data for paid campaigns ─────────────────────────
      { id: 'b2b-leads-std',     group: 'B2B audience data for paid campaigns', name: 'Additional Standard Leads, Sourced & Enriched', desc: 'GDPR-validated B2B leads for custom-audience upload.', tip: 'We source and enrich B2B leads ready to upload as custom audiences on Meta, LinkedIn, Google, and X, so your paid targeting reaches the right companies. Priced per lead.',  price: 2.50, unit: '/lead' },
      { id: 'ps-social',         group: 'B2B audience data for paid campaigns', name: 'Premium Sourcing: Social & Professional Networks',  desc: 'Hand-verified audience data from social and professional networks.', tip: 'Hand-verified prospects from social and professional networks, enriched for custom-audience targeting on your paid channels. Priced per prospect.',              priceLabel: 'From £2.50', price: 2.50, unit: '/prospect' },
      { id: 'ps-review',         group: 'B2B audience data for paid campaigns', name: 'Premium Sourcing: Review & Comparison Sites',       desc: 'Audience data from review and comparison platforms.', tip: 'Prospects sourced from review and comparison sites, verified and enriched for paid audience upload. Good for reaching active buyers. Priced per prospect.',                    priceLabel: 'From £1.75', price: 1.75, unit: '/prospect' },
      { id: 'ps-directories',    group: 'B2B audience data for paid campaigns', name: 'Premium Sourcing: Business Directories',            desc: 'Audience data from business directories.', tip: 'Prospects sourced from business directories, verified and enriched for paid targeting by sector or location. Priced per prospect.',                          priceLabel: 'From £1.25', price: 1.25, unit: '/prospect' },
      { id: 'ps-marketplaces',   group: 'B2B audience data for paid campaigns', name: 'Premium Sourcing: Marketplaces & Procurement',      desc: 'Audience data from marketplace and procurement platforms.', tip: 'Prospects sourced from marketplace and procurement platforms, enriched for custom-audience upload. Priced per prospect.',                       priceLabel: 'From £1.75', price: 1.75, unit: '/prospect' },
      { id: 'ps-local',          group: 'B2B audience data for paid campaigns', name: 'Premium Sourcing: Local & Regional Sources',        desc: 'Audience data from local and regional directories.', tip: 'Prospects sourced from local and regional directories, verified and enriched for geo-targeted paid campaigns. Priced per prospect.',             priceLabel: 'From £1.50', price: 1.50, unit: '/prospect' },
      { id: 'ps-custom',         group: 'B2B audience data for paid campaigns', name: 'Premium Sourcing: Custom Source',                   desc: 'Bespoke audience data from any source you specify.', tip: 'Tell us the platform or source and we build a bespoke, fully verified and enriched audience for your paid campaigns. Priced per prospect.',                          priceLabel: 'From £4.50', price: 4.50, unit: '/prospect' },
      // ── Conversion infrastructure and reporting ───────────────────────
      { id: 'lp-build',          group: 'Conversion infrastructure and reporting', name: 'Landing Page Build (One-Off)',                   desc: 'A custom, conversion-focused landing page, built and tested.', tip: 'A one-off build of a conversion-focused landing page for your campaign, designed, built, and QA tested before launch.',                                                         priceLabel: 'From £1,495', price: 1495, unit: 'one-time', oneTime: true },
      { id: 'landing',           group: 'Conversion infrastructure and reporting', name: 'Landing Page Design and Optimisation',           desc: 'Ongoing landing-page management and A/B testing.', tip: 'Continuous management of your landing pages with structured A/B testing, so conversion rates keep improving over time rather than staying static.',                                                      priceLabel: 'From £149',   price: 149,  unit: '/mo' },
      { id: 'cro-prog',          group: 'Conversion infrastructure and reporting', name: 'Conversion Rate Optimisation Programme',         desc: 'Systematic testing and funnel analysis to lower your cost per lead.', tip: 'An ongoing programme of landing-page tests, copy experiments, and funnel analysis, aimed at steadily lowering your cost per lead.',                             priceLabel: 'From £149',   price: 149,  unit: '/mo' },
      { id: 'call-track',        group: 'Conversion infrastructure and reporting', name: 'Google Call Tracking Setup',                     desc: 'Call-level attribution with dynamic number insertion.', tip: 'We set up call tracking with dynamic number insertion and analytics integration, so phone enquiries are attributed to the right campaign and keyword.',                                        price: 99,   unit: 'one-time', oneTime: true, badge: 'recommended' },
      { id: 'adv-attribution',   group: 'Conversion infrastructure and reporting', name: 'Advanced Attribution and Server-Side Tracking',  desc: 'Server-side tracking and multi-touch attribution across paid.', tip: 'We implement server-side event tracking and multi-touch attribution across your paid channels for accurate data despite cookie and privacy restrictions.',                                       priceLabel: 'From £249',   price: 249,  unit: '/mo' },
      { id: 'dw-setup',          group: 'Conversion infrastructure and reporting', name: 'Data Warehouse Integration',                     desc: 'Wire your ad platforms, CRM, and analytics into BigQuery or Snowflake.', tip: 'A one-off setup connecting your ad platforms, CRM, and analytics into BigQuery or Snowflake, giving you a single source of truth for reporting.',                                                     priceLabel: 'From £499',   price: 499,  unit: 'one-time', oneTime: true },
      { id: 'dw-ongoing',        group: 'Conversion infrastructure and reporting', name: 'Data Warehouse Maintenance',                     desc: 'Monthly pipeline monitoring, model tuning, and cost control.', tip: 'Ongoing upkeep of your data warehouse pipelines, including monitoring, model tuning, and cost optimisation. Billed monthly.',                                                     price: 99,   unit: '/mo' },
      { id: 'zapier',            group: 'Conversion infrastructure and reporting', name: 'Zapier Automation Management',                   desc: 'Build and maintain automations across your paid-funnel tools.', tip: 'We build and maintain Zapier automations that connect the tools in your paid funnel, removing manual handoffs between platforms.',                                                        priceLabel: 'From £49',    price: 49,   unit: '/mo' },
    ],
  },
  {
    id: 'email',
    name: 'Email Marketing',
    desc: 'Lifecycle flows, broadcasts, deliverability.',
    category: 'Growth',
    icon: 'assets/icons/email-marketing.webp',
    commitMonths: 3,
    // Tier-level waiting list: grow + scale are at full capacity. They render
    // as "Waiting list" instead of a price, can still be added to a quote
    // (capturing demand in Airtable), but contribute £0 to the calculator.
    waitlistTiers: [],
    tierFeatures: {
      starter: ['2 broadcasts/mo', 'Basic flows (welcome, abandoned)', 'Klaviyo or Mailchimp'],
      grow:    ['8 broadcasts/mo', 'Full lifecycle (6 flows)', 'A/B testing', 'Deliverability audits'],
      scale:   ['Weekly broadcasts', 'Advanced segmentation', 'RFM + predictive', 'Dedicated IP management'],
    },
    prices: {
      starter: { '3': 1295, '6':  595, '12':  495 },
      grow:    { '3': 1495, '6': 1095, '12':  895 },
      scale:   { '3': 2495, '6': 1895, '12': 1495 },
    },
    setupFees: {
      starter: 245,
      grow:    345,
      scale:   995,
    },
    tierDesc: {
      starter: 'DTC & B2B founders looking to nurture leads & boost retention through fully managed email marketing',
      grow: 'DTC & B2B founders looking to drive repeat revenue through fully managed email marketing with segmentation',
      scale: 'DTC & B2B founders looking to scale through fully managed high-volume email marketing with advanced segmentation',
      enterprise: 'DTC & B2B founders looking to scale through fully managed enterprise email programmes across multiple brands, regions, & channels',
    },
    incentives: [
      { type: 'built-in', label: 'Algorithmic Team Profit Sharing' },
      { type: 'optional', label: 'Performance Recognition Tips' },
      { type: 'add-on',  label: 'Outcome-Linked Success Bonus' },
    ],
    addons: [
      // ── Campaign enhancements ─────────────────────────────────────────────
      { id: 'platform-migration', group: 'Campaign enhancements', name: 'Email Marketing on Any Platform',       desc: 'Full management on Klaviyo, Mailchimp, HubSpot, Braze, or your own.', tip: 'We run your email programme on whatever platform you already use, so there is no forced migration. Setup and ongoing management are included.',        price: 79,  unit: '/platform', oneTime: true },
      { id: 'sms',                group: 'Campaign enhancements', name: 'SMS Messaging Integration',             desc: 'Add SMS broadcasts and automated flows to your email programme.', tip: 'We add SMS broadcasts and automated flows alongside email, so time-sensitive messages reach customers on a second channel.',                             price: 359, unit: '/mo', badge: 'popular' },
      { id: 'deliverability-audit', group: 'Campaign enhancements', name: 'Deliverability Audit and Domain Warmup', desc: 'Inbox-placement audit, blacklist review, and domain warmup.', tip: 'A full audit of why your emails land where they do, plus blacklist checks and a structured warmup for each sending domain to lift inbox placement.', price: 249, unit: 'one-time', oneTime: true },
      { id: 'campaign-pack',      group: 'Campaign enhancements', name: 'Additional Campaign Production Pack',   desc: 'One extra broadcast campaign a month beyond your plan.', tip: 'Adds one more fully produced broadcast campaign per month on top of your plan\'s allocation, including design, copy, and scheduling.',                             price: 199, unit: '/mo' },
      { id: 'landing',            group: 'Campaign enhancements', name: 'Landing Page Design and Optimisation',  desc: 'Conversion-tuned landing pages for your major broadcasts.', tip: 'We design and manage conversion-tuned landing pages for your key email campaigns, then refine them month to month to lift results.',                        priceLabel: 'From £149', price: 149, unit: '/mo' },
      // ── Audience and automation ───────────────────────────────────────────
      { id: 'b2b-enrichment',     group: 'Audience and automation', name: 'B2B List Enrichment',                 desc: 'Up to 500 GDPR-validated B2B contact enrichments a month.', tip: 'We enrich up to 500 B2B contacts a month with GDPR-validated data, so your lists stay accurate and your segments stay useful.',                 price: 149, unit: '/mo' },
      { id: 'email-ab-test',      group: 'Audience and automation', name: 'Email A/B Testing Programme',         desc: 'Up to two structured A/B tests a month.', tip: 'Up to two structured A/B tests each month across subject lines, content, and send times, so the programme keeps improving on evidence, not guesswork.',               price: 89,  unit: '/mo' },
      { id: 'adv-segmentation',   group: 'Audience and automation', name: 'Advanced Segmentation and Personalisation Build', desc: 'Custom RFM, behavioural, and predictive segments, built once.', tip: 'A one-off project to build custom RFM, behavioural, and predictive segments, so your campaigns can be properly targeted and personalised.',   priceLabel: 'From £479', price: 479, unit: 'one-time', oneTime: true },
      { id: 'zapier',             group: 'Audience and automation', name: 'Zapier Automation Management',        desc: 'Build and maintain automations between your ESP, CRM, and stack.', tip: 'We build and maintain Zapier automations connecting your email platform, CRM, and product tools, removing manual data handoffs.',                priceLabel: 'From £49',  price: 49,  unit: '/mo' },
      // ── Data warehouse integration ────────────────────────────────────────
      { id: 'dw-setup',           group: 'Data warehouse integration', name: 'Data Warehouse Integration',       desc: 'Connect your email platform into your data warehouse.', tip: 'A one-off setup linking your email platform into your existing data warehouse for unified, cross-channel reporting.',  priceLabel: 'See Paid Advertising plans', price: 599, unit: 'one-time', oneTime: true,
        tip: 'This covers the initial integration of your email marketing platforms, including your existing data warehouse, custom API connections, and data centralisation. Because this work usually spans multiple marketing channels, we price and deliver it through our Paid Advertising pricing plans. If you already have a Paid Advertising plan with us, data warehouse integration is already included at no extra cost.' },
      { id: 'dw-ongoing',         group: 'Data warehouse integration', name: 'Data Warehouse Maintenance',       desc: 'Monthly pipeline monitoring, model tuning, and cost control.', tip: 'Ongoing upkeep of your data warehouse pipelines, including monitoring, tuning, and cost optimisation. Billed monthly.',                                  price: 99,  unit: '/mo' },
      // ── Strategic support ─────────────────────────────────────────────────
      { id: 'additional-reports', group: 'Strategic support', name: 'Additional Weekly Reports and Calls', desc: 'An extra weekly performance report and strategy call.', tip: 'Adds one more performance report and strategy call each week on top of your plan, for teams that want a closer feedback loop.', priceLabel: 'From £19/wk', price: 79, unit: '/wk' },
    ],
  },
  {
    id: 'smm',
    name: 'Social Media Management',
    desc: 'Owned channel growth, content calendars, community.',
    category: 'Creative',
    icon: 'assets/icons/smm.webp',
    commitMonths: 6,
    waitlistTiers: [],
    tierFeatures: {
      starter: ['2 channels', '8 posts/mo', 'Community monitoring'],
      grow:    ['4 channels', '20 posts/mo', 'DM management', 'Monthly trend report'],
      scale:   ['All channels', '60+ posts/mo', '24/7 community mgmt', 'Influencer coordination'],
    },
    prices: {
      starter: { '3': 1295, '6':  995, '12':  795 },
      grow:    { '3': 2495, '6': 1995, '12': 1495 },
      scale:   { '3': 5495, '6': 4295, '12': 3595 },
    },
    setupFees: {
      starter: 245,
      grow:    345,
      scale:   995,
    },
    tierDesc: {
      starter: 'DTC & B2B founders looking to grow brand visibility through fully managed social media on up to 2 channels',
      grow: 'DTC & B2B founders looking to grow audiences through fully managed multi-channel social media with custom creative',
      scale: 'DTC & B2B founders looking to scale through fully managed high-output, multi-channel social media with premium creative',
      enterprise: 'DTC & B2B founders looking to scale through fully managed enterprise social across multiple brands, regions, & platforms',
    },
    incentives: [
      { type: 'built-in', label: 'Algorithmic Team Profit Sharing' },
      { type: 'optional', label: 'Performance Recognition Tips' },
      { type: 'add-on',  label: 'Outcome-Linked Success Bonus' },
    ],
    addons: [
      { id: 'community',         name: 'Manual Posting and Community Management', desc: 'Human-led moderation, outreach, and DM replies on your channels.', tip: 'A real person manages your community day to day, replying to comments and DMs, welcoming followers, and flagging anything that needs your attention.',       priceLabel: 'From £299',    price: 299,   unit: '/mo',       standalone: true },
      { id: 'post-boost',        name: 'Paid Advertising Post Promotion',      desc: 'Boost your best organic posts to a wider audience.', tip: 'We put paid budget behind your strongest organic posts to extend their reach. Management fee only, your ad spend is paid directly to the platform.',                    price: 299,                 unit: '/mo',       badge: 'popular' },
      { id: 'extra-channel',     name: 'Extra Social Channel',                  desc: 'Add management of one more social channel beyond your plan.', tip: 'Adds full management of an additional social platform on top of the channels in your plan, with the same posting and reporting cadence.',                        price: 199,                 unit: '/mo' },
      { id: 'influencer',        name: 'Influencer Outreach and Partnership Management', desc: 'We find, approach, and manage influencer partnerships for you.', tip: 'We identify relevant influencers, handle outreach and negotiation, and manage the partnership end to end to amplify your brand reach.', priceLabel: 'From £799',    price: 799,   unit: '/mo' },
      { id: 'insta-story',       name: 'Instagram Story Posting',              desc: 'Professionally designed and scheduled Instagram Stories.', tip: 'We design and schedule on-brand Instagram Stories to keep your profile active between feed posts and drive day-to-day engagement.',            priceLabel: 'From £49',     price: 49,    unit: '/mo' },
      { id: 'custom-highlights', name: 'Custom Highlights Design',             desc: 'Branded Highlight cover icons for your Instagram profile.', tip: 'A set of custom-designed Highlight covers in your brand style, so your profile looks polished and on-brand at first glance.',   priceLabel: 'From £249',    price: 249,   unit: 'one-time', oneTime: true },
      { id: 'linkedin-posts',    name: 'Custom LinkedIn Social Posts',         desc: 'Bespoke LinkedIn posts to grow your professional presence.', tip: 'Original LinkedIn posts written and designed in your brand or founder voice, to build authority and reach with a professional audience.',  priceLabel: 'From £69',     price: 69,    unit: '/mo' },
      { id: 'extra-lang',        name: 'Additional Language or Market',       desc: 'Full social strategy and content in another language or market.', tip: 'We deliver a complete social presence in an additional language or for a new region, properly adapted to local culture rather than just translated.', priceLabel: 'From £399', price: 399, unit: '/mo/market' },
      { id: 'showcase',          name: 'Custom Showcase Page Design',          desc: 'A branded LinkedIn or Meta showcase or business page build.', tip: 'We design and set up a LinkedIn Showcase page or a Meta business page in your brand style, ready to publish.',            price: 299,                 unit: 'one-time', oneTime: true },
      { id: 'dw-setup',          group: 'Data & analytics', name: 'Data Warehouse Integration',           desc: 'Social engagement data stitched into your analytics warehouse.', tip: 'A one-off setup feeding your social engagement data into your analytics warehouse, so social sits alongside your other channels in reporting.',           priceLabel: 'See Paid Advertising plans', price: 599, unit: 'one-time', oneTime: true,
        tip: 'If you already have a Paid Advertising plan with us, data warehouse integration is already included at no extra cost.' },
    ],
  },
  {
    id: 'content',
    name: 'Content Creation',
    desc: 'Editorial content, case studies, thought leadership.',
    category: 'Creative',
    icon: 'assets/icons/content.webp',
    oneTime: true,
    waitlistTiers: ['starter', 'grow', 'scale', 'enterprise'],
    tierFeatures: {
      starter: ['6 long-form pieces', 'Ghostwriting support', 'Hub + spokes structure'],
      grow:    ['18 pieces', 'Case studies + newsletters', 'Editorial calendar', 'Quarterly retainers'],
      scale:   ['Newsroom model', 'Podcast + video included', 'Dedicated editor-in-chief', 'Annual content strategy'],
    },
    prices: { starter: 1495, grow: 2495, scale: 5995 },
    setupFees: {
      starter: 895,
      grow:    1295,
      scale:   1795,
    },
    tierDesc: {
      starter: 'DTC & B2B founders looking to kickstart brand awareness through fully managed creator-led video content',
      grow: 'DTC & B2B founders looking to scale customer acquisition through fully managed high-volume, creator-led video content',
      scale: 'DTC & B2B founders looking to scale through fully managed multi-channel content with creator-led & studio-produced video',
      enterprise: 'DTC & B2B founders looking to scale through fully managed enterprise content production across brands, regions, & formats',
    },
    incentives: [
      { type: 'built-in', label: 'Algorithmic Team Profit Sharing' },
      { type: 'optional', label: 'Performance Recognition Tips' },
      { type: 'add-on',  label: 'Outcome-Linked Success Bonus' },
    ],
    addons: [
      // ── Additional Video Content ─────────────────────────────────────────
      { id: 'content-video-creator', group: 'Additional Video Content', name: 'Additional Creator-Led Videos',   desc: 'Extra creator-produced videos beyond your plan allowance.', tip: 'Additional creator-led video pieces on top of your plan, filmed by vetted creators in an authentic, social-first style. Priced per video.',                                           price: 199,  unit: '/video',       badge: 'popular',     onboardingStatus: 'full' },
      { id: 'content-video-studio',  group: 'Additional Video Content', name: 'Additional In-House Studio Assets', desc: 'Extra studio-produced assets on top of your plan.', tip: 'Additional polished assets produced in our studio, suited to brand and product content where higher production value matters. Priced per asset.',                                       priceLabel: 'From £999', price: 999, unit: '/asset', oneTime: true, onboardingStatus: 'full' },
      // ── Content Rights & Access (rendered as sub-box chip group) ─────────
      { id: 'content-raw-footage',   group: 'Content Rights & Access',  name: 'Raw Footage Access',              desc: 'All unedited raw footage from your campaign, per production.', tip: 'We hand over the complete unedited footage from your shoot, so you can re-edit or repurpose it yourself. Priced per production.',                                  price: 499,  unit: 'one-time', oneTime: true,                onboardingStatus: 'full' },
      { id: 'content-rights-perp',   group: 'Content Rights & Access',  name: 'Perpetual',                          desc: 'Use the content on any channel, with no time limit.', tip: 'A perpetual licence to use the content across owned, paid, and earned channels with no expiry. The most flexible rights option.',   price: 599,  unit: 'one-time', oneTime: true,                subGroup: 'usage-rights',   onboardingStatus: 'full' },
      { id: 'content-rights-12m',    group: 'Content Rights & Access',  name: '12 Months',                          desc: 'A 12-month licence for paid and owned channel use.', tip: 'A rolling 12-month licence covering paid media placements and owned channel use. Renew or upgrade to perpetual later.',                               price: 349,  unit: '/mo',                                   subGroup: 'usage-rights',   onboardingStatus: 'full' },
      { id: 'content-rights-6m',     group: 'Content Rights & Access',  name: '6 Months',                           desc: 'A 6-month licence for paid and owned channel use.', tip: 'A rolling 6-month licence covering paid media placements and owned channel use. Best for short campaigns.',                                price: 199,  unit: '/mo',                                   subGroup: 'usage-rights',   onboardingStatus: 'full' },
      // ── Production Services ──────────────────────────────────────────────
      { id: 'content-rush',          group: 'Production Services',      name: 'Expedited Production: Rush Fee',  desc: 'Priority scheduling and faster turnaround for urgent campaigns.', tip: 'We move your production to the front of the queue and compress the timeline, for launches and time-sensitive campaigns.',                            priceLabel: 'From £999',   price: 999,  unit: 'one-time', oneTime: true, onboardingStatus: 'full' },
      { id: 'content-talent-emrg',   group: 'Production Services',      name: 'Emerging Talent',                 desc: 'Sourcing and on-set management of emerging creators.', tip: 'We source, contract, and manage up-and-coming creators and influencers for your shoot, a cost-effective way to add on-screen talent.',                     priceLabel: 'From £799',   price: 799,  unit: 'one-time', oneTime: true, subGroup: 'talent-casting', onboardingStatus: 'full' },
      { id: 'content-talent-pro',    group: 'Production Services',      name: 'Professional Talent',             desc: 'Sourcing and management of professional actors and presenters.', tip: 'We source and manage experienced actors, presenters, or on-screen talent, and handle contracting and on-set direction.',                       priceLabel: 'From £1,999', price: 1999, unit: 'one-time', oneTime: true, subGroup: 'talent-casting', onboardingStatus: 'full' },
      { id: 'content-shipment',      group: 'Production Services',      name: 'Product Shipment Coordination',   desc: 'We coordinate product delivery to creators or shoot locations.', tip: 'End-to-end handling of getting your product to creators, studios, or shoot locations on time, including tracking and logistics.',                    price: 299,  unit: '/campaign', oneTime: true,                 onboardingStatus: 'full' },
      { id: 'content-talent-celeb',  group: 'Production Services',      name: 'Celebrity Talent',                desc: 'Sourcing and management of celebrity talent.', tip: 'We source celebrity talent and handle bespoke negotiation, scheduling, usage rights, and on-set support. Priced on request.',   priceLabel: 'Contact for pricing', custom: true,              subGroup: 'talent-casting', onboardingStatus: 'full' },
      // ── Maintenance & Optimisation ───────────────────────────────────────
      { id: 'content-maintain-std',  group: 'Maintenance & Optimisation', name: 'Standard Maintenance',          desc: 'Monthly refresh, minor copy updates, and asset repurposing.', tip: 'A monthly service that keeps your content current with light updates and repurposing of evergreen assets. Billed monthly.',                          price: 899,  unit: '/mo',                                   onboardingStatus: 'full' },
      { id: 'content-maintain-adv',  group: 'Maintenance & Optimisation', name: 'Advanced Maintenance',          desc: 'Monthly overhaul with A/B testing and content reworking.', tip: 'A deeper monthly service including A/B testing, performance analysis, and reworking of underperforming content. Billed monthly.',     price: 1499, unit: '/mo',                                   onboardingStatus: 'full' },
      { id: 'content-perf-opt',      group: 'Maintenance & Optimisation', name: 'Performance Optimisation Service', desc: 'Data-driven tuning of existing content to lift results.', tip: 'We analyse how your content performs and optimise it for better reach, engagement, and conversion. Billed monthly.',          priceLabel: 'From £299', price: 299, unit: '/mo',             onboardingStatus: 'full' },
      // ── Platform & Distribution ──────────────────────────────────────────
      { id: 'content-multilang',     group: 'Platform & Distribution',  name: 'Multi-Language Localisation',     desc: 'Translation and cultural adaptation, per video per language.', tip: 'We translate and culturally adapt your video content for new markets, a proper localisation rather than just subtitles. Priced per video per language.',                    priceLabel: 'From £199', price: 199, unit: '/video per language', onboardingStatus: 'full' },
      { id: 'content-social-adapt',  group: 'Platform & Distribution',  name: 'Social Media Content Adaptation', desc: 'Format cut-downs and platform-specific versions for social.', tip: 'We re-cut and reformat your content for each social platform, so one shoot stretches across feeds, stories, and reels.',                      priceLabel: 'From £499', price: 499, unit: '/mo',                 onboardingStatus: 'full' },
      { id: 'content-platform-exp',  group: 'Platform & Distribution',  name: 'Platform Expansion',              desc: 'Setup and ongoing management for one more content platform.', tip: 'We set up and run distribution on an additional social or content platform beyond those in your plan. Billed monthly.',                price: 299,  unit: '/mo per channel',                       onboardingStatus: 'full' },
    ],
  },
  {
    id: 'motion',
    name: 'Photorealistic 3D Animation',
    desc: 'Motion graphics, 3D product renders, brand films.',
    category: 'Creative',
    icon: 'assets/icons/motion.webp',
    oneTime: true,
    badge: 'recommended',
    waitlistTiers: ['starter', 'grow', 'scale', 'enterprise'],
    tierFeatures: {
      starter: ['15-30s animation', 'Template-based scenes', 'HD delivery'],
      grow:    ['60-90s animation', 'Custom 3D scenes', '4K delivery', '2 revision rounds'],
      scale:   ['Hero brand film', 'Full custom production', 'Broadcast-grade', 'Unlimited revisions'],
    },
    prices: { starter: 795,  grow: 1995, scale: 4995  },
    setupFees: {
      starter: 195,
      grow:    395,
      scale:   795,
    },
    tierDesc: {
      starter: 'DTC and B2B founders launching quickly with AI-first photoreal product ads for multiplatform testing',
      grow: 'DTC and B2B founders scaling brand-led photoreal ads with custom avatars, bespoke sound, and localisation',
      scale: 'DTC and B2B founders running high-stakes campaigns needing AI-first scale with professional finishing',
      enterprise: 'Established brands needing always-on AI-first 3D animation at broadcast fidelity across multiple campaigns',
    },
    incentives: [
      { type: 'built-in', label: 'Algorithmic Team Profit Sharing' },
      { type: 'optional', label: 'Performance Recognition Tips' },
      { type: 'add-on',  label: 'Outcome-Linked Success Bonus' },
    ],
    addons: [
      // ── Additional 3D Animation Content (exclusive chip sub-group) ──
      { id: 'm-anim-ai',       name: 'AI-Accelerated',                   desc: 'AI-generated animations in two to three days.', tip: 'Fast, AI-assisted animations turned around in two to three days, ideal for A/B testing and seasonal campaigns. Priced per video.',            price: 499,  unit: '/video', group: 'Additional 3D Animation Content', subGroup: '3d-anim-content',   onboardingStatus: 'full' },
      { id: 'm-anim-hero',     name: 'Hero AI Video',                    desc: 'A flagship AI video for premium brand campaigns.', tip: 'Our highest-impact AI video, made for hero placements and premium brand campaigns. Priced per video.',                             price: 1295, unit: '/video', group: 'Additional 3D Animation Content', subGroup: '3d-anim-content',   onboardingStatus: 'full' },
      { id: 'm-anim-premium',  name: 'Premium AI Video',                 desc: 'A high-fidelity AI video for mid-tier campaigns.', tip: 'A high-quality AI video for product showcases and mid-tier campaigns, a step up from the AI-accelerated option. Priced per video.',                                  price: 795,  unit: '/video', group: 'Additional 3D Animation Content', subGroup: '3d-anim-content',   onboardingStatus: 'full' },

      // ── Render & Delivery Upgrades ──
      // Render Quality Upgrade (exclusive chip sub-group) — QHD → 4K → 8K
      { id: 'm-render-qhd',    name: 'QHD',                              desc: 'QHD (1440p) render for high-quality digital and social.', tip: 'Upgrades your render to QHD (1440p) for crisp digital and social placements. Priced per video.',                        price: 199,  unit: '/video', group: 'Render & Delivery Upgrades',    subGroup: '3d-render-quality', onboardingStatus: 'full' },
      { id: 'm-render-4k',     name: '4K',                               desc: '4K UHD render for broadcast, streaming, and premium digital.', tip: 'Upgrades your render to 4K UHD, suited to broadcast, streaming, and premium digital placements. Priced per video.',                   price: 399,  unit: '/video', group: 'Render & Delivery Upgrades',    subGroup: '3d-render-quality', onboardingStatus: 'full' },
      { id: 'm-render-8k',     name: '8K',                               desc: '8K render for cinema and large-format displays.', tip: 'Upgrades your render to 8K for cinema, large-format displays, and ultra-premium campaigns. Priced per video.',                 price: 999,  unit: '/video', group: 'Render & Delivery Upgrades',    subGroup: '3d-render-quality', onboardingStatus: 'full' },
      // Standalones
      { id: 'm-aspect-ratios', name: 'Additional Aspect Ratios',         desc: 'Extra aspect ratios for multi-platform delivery.', tip: 'Additional renders in other aspect ratios such as portrait, square, or ultrawide, so the animation fits every placement. Priced per ratio.',           price: 99,   unit: '/ratio', group: 'Render & Delivery Upgrades',    onboardingStatus: 'full' },
      { id: 'm-codec',         name: 'Advanced Codec Delivery',          desc: 'Delivery in broadcast codecs like ProRes or DNxHD.', tip: 'We deliver your animation in professional codecs such as ProRes or DNxHD for post-production and broadcast workflows. Priced per video.',                  price: 149,  unit: '/video', group: 'Render & Delivery Upgrades',    onboardingStatus: 'full' },

      // ── Advanced VFX & Simulations ──
      { id: 'm-cloth-sim',     name: 'Cloth Simulation',                 desc: 'Realistic fabric and cloth physics for your animation.', tip: 'Adds realistic fabric and cloth physics, ideal for product and fashion animations. Priced per video.',                    price: 299,  unit: '/video', group: 'Advanced VFX & Simulations',   onboardingStatus: 'full' },
      { id: 'm-custom-vfx',    name: 'Custom VFX',                       desc: 'Bespoke visual effects tailored to your campaign.', tip: 'Custom effects built for your concept, from particles to energy and destruction. Scoped per project, priced from £499 per video.',          price: 499,  unit: '/video', priceLabel: 'From £499',               group: 'Advanced VFX & Simulations', onboardingStatus: 'full' },
      { id: 'm-fluid-dyn',     name: 'Fluid Dynamics',                   desc: 'Realistic liquid, smoke, and fire simulations.', tip: 'Adds high-end liquid, smoke, and fire simulation, suited to luxury and high-impact campaigns. Priced per video.',                  price: 349,  unit: '/video', group: 'Advanced VFX & Simulations',   onboardingStatus: 'full' },
      { id: 'm-particles',     name: 'Particle Systems',                  desc: 'Dynamic particle effects like dust, sparks, and glitter.', tip: 'Adds dynamic particle animation such as dust, sparks, glitter, and atmospheric effects. Priced per video.',                        price: 249,  unit: '/video', group: 'Advanced VFX & Simulations',   onboardingStatus: 'full' },
      { id: 'm-physics-anim',  name: 'Physics-Based Animations',         desc: 'Animation driven by real-world physics.', tip: 'Adds physically accurate motion, including rigid body, soft body, and constraint simulations, for believable movement. Priced per video.',         price: 249,  unit: '/video', group: 'Advanced VFX & Simulations',   onboardingStatus: 'full' },

      // ── Voice-Over & Sound Design ──
      // Voice-Over (exclusive chip sub-group)
      { id: 'm-vo-pro',        name: 'Professional Voice-Over',          desc: 'Studio-recorded professional voice-over in your language.', tip: 'Professional voice-over talent recorded in a studio, in your preferred language and style. Priced per video.',                 price: 149,  unit: '/video', group: 'Voice-Over & Sound Design',    subGroup: '3d-voice-over',     onboardingStatus: 'full' },
      { id: 'm-vo-celeb',      name: 'Celebrity Voice-Over',             desc: 'Celebrity voice-over talent for your animation.', tip: 'Celebrity voice-over, priced on request based on the talent tier and usage rights you need.',               priceLabel: 'Contact for pricing',             group: 'Voice-Over & Sound Design',    subGroup: '3d-voice-over',     onboardingStatus: 'full' },
      // Standalones
      { id: 'm-music-comp',    name: 'Custom Music Composition',         desc: 'Original music composed for your animation.', tip: 'Bespoke music composed and produced specifically for your animation, rather than a stock track. Priced from £249 per video.',                                price: 249,  unit: '/video', priceLabel: 'From £249',               group: 'Voice-Over & Sound Design', onboardingStatus: 'full' },
      { id: 'm-sound-design',  name: 'Advanced Sound Design',            desc: 'Full bespoke sound design, effects, and ambience.', tip: 'Complete sound design including effects, ambience, and stem delivery, for a polished, immersive result. Priced per video.',                           price: 149,  unit: '/video', group: 'Voice-Over & Sound Design',    onboardingStatus: 'full' },

      // ── Maintenance & Iteration ──
      // Maintenance Plan (exclusive chip sub-group)
      { id: 'm-maint-std',     name: 'Standard Maintenance',             desc: 'One animation refresh a month, five-day turnaround.', tip: 'One refresh or iteration of your animation each month with a five-day turnaround. Billed monthly.',                               price: 699,  unit: '/mo',   group: 'Maintenance & Iteration',      subGroup: '3d-maintenance',    onboardingStatus: 'full' },
      { id: 'm-maint-adv',     name: 'Advanced Maintenance',             desc: 'Two refreshes a month with priority turnaround.', tip: 'Two animation refreshes each month with priority turnaround and a dedicated producer. Billed monthly.',                price: 1299, unit: '/mo',   group: 'Maintenance & Iteration',      subGroup: '3d-maintenance',    onboardingStatus: 'full' },
      // Standalone
      { id: 'm-perf-opt',      name: 'Performance Optimisation Service', desc: 'Ongoing tuning of your 3D assets for better results.', tip: 'We analyse and optimise your 3D animation assets for conversion and engagement over time. Priced from £399 a month.',        price: 399,  unit: '/mo',   priceLabel: 'From £399',               group: 'Maintenance & Iteration', onboardingStatus: 'full' },

      // ── Asset Licensing & Ownership ──
      // Extended Usage Rights (exclusive chip sub-group) — names and descs match Content Creation
      { id: 'm-eur-perpetual', name: 'Perpetual',                        desc: 'Use the animation on any channel, with no time limit.', tip: 'A perpetual licence to use your animation across owned, paid, and earned channels with no expiry. One-off fee.',   price: 799,  unit: 'one-time', oneTime: true, group: 'Asset Licensing & Ownership', subGroup: '3d-usage-rights', onboardingStatus: 'full' },
      { id: 'm-eur-6mo',       name: '6 Months',                         desc: 'A 6-month licence for paid and owned channel use.', tip: 'A rolling 6-month licence covering paid media and owned channel use. Billed monthly, best for short campaigns.',                               price: 299,  unit: '/mo',                   group: 'Asset Licensing & Ownership', subGroup: '3d-usage-rights', onboardingStatus: 'full' },
      { id: 'm-eur-12mo',      name: '12 Months',                        desc: 'A 12-month licence for paid and owned channel use.', tip: 'A rolling 12-month licence covering paid media and owned channel use. Billed monthly.',                              price: 499,  unit: '/mo',                   group: 'Asset Licensing & Ownership', subGroup: '3d-usage-rights', onboardingStatus: 'full' },
      // Standalones
      { id: 'm-asset-library', name: 'AI Brand Asset Library',           desc: 'A reusable 3D brand asset library in your visual identity.', tip: 'A custom library of 3D brand assets built in your visual identity, reusable across all future campaigns. Priced from £2,999.',  price: 2999, priceLabel: 'From £2,999', group: 'Asset Licensing & Ownership', onboardingStatus: 'full' },
      { id: 'm-proj-files',    name: 'Project File Access',              desc: 'Full source project files for in-house editing or handover.', tip: 'We hand over the source project files so your team or agency can edit, repurpose, or continue the work in-house. One-off fee.',          price: 999,  unit: 'one-time', oneTime: true, group: 'Asset Licensing & Ownership', onboardingStatus: 'full' },

      // ── Platform & Distribution ──
      // Multi-Language Localisation (exclusive chip sub-group) — chip names: Full, Text, Voice-Over
      { id: 'm-loc-full',      name: 'Full Localisation',                desc: 'Translation, voice-over, and on-screen text in one package.', tip: 'A complete localisation package combining translated on-screen text and localised voice-over. Priced per language.',                    price: 299,  unit: '/language', group: 'Platform & Distribution', subGroup: '3d-localisation', onboardingStatus: 'full' },
      { id: 'm-loc-text',      name: 'Text Localisation',                desc: 'On-screen text and subtitles translated and integrated.', tip: 'We translate and rebuild your on-screen text and subtitles in the target language, integrated into the animation. Priced per language.',                          price: 99,   unit: '/language', group: 'Platform & Distribution', subGroup: '3d-localisation', onboardingStatus: 'full' },
      { id: 'm-loc-vo',        name: 'Voice-Over',                       desc: 'Localised voice-over recorded in your target language.', tip: 'We record and mix a localised voice-over into your animation in the target language. Priced per language.',                 price: 199,  unit: '/language', group: 'Platform & Distribution', subGroup: '3d-localisation', onboardingStatus: 'full' },
      // Standalones
      { id: 'm-ctv',           name: 'Connected TV (CTV) Optimisation',  desc: 'Delivery optimised for connected TV and OTT platforms.', tip: 'We adapt and optimise your animation for connected TV and OTT streaming placements. Billed monthly.',                    price: 499,  unit: '/mo', group: 'Platform & Distribution', onboardingStatus: 'full' },
      { id: 'm-platform-exp',  name: 'Platform Expansion',               desc: 'A distribution package for one more platform or channel.', tip: 'Distribution setup and management for an additional platform such as Pinterest or TikTok. Billed monthly.',               price: 299,  unit: '/mo', group: 'Platform & Distribution', onboardingStatus: 'full' },
      { id: 'm-social-adapt',  name: 'Social Media Content Adaptation',  desc: 'Ongoing social-first cut-downs of your 3D animations.', tip: 'We continually adapt your 3D animations into social-first formats and aspect ratios for ongoing distribution. Priced from £499 a month.',              price: 499,  unit: '/mo', priceLabel: 'From £499', group: 'Platform & Distribution', onboardingStatus: 'full' },
    ],
  },
  // Dedicated Resources is split into two independent services so visitors can
  // pick BOTH Part-Time and Full-Time at the same time on the Talent step.
  // Each renders as its own card and tracks its own selection in state.
  {
    id: 'dedicated-pt',
    name: 'Dedicated Resources · Part-Time',
    desc: 'Flexible capacity with pre-vetted specialists from our DM-Refer / Hybrid R+1 talent pool. Day rates, scale up or down month-to-month.',
    category: 'Talent',
    icon: 'assets/icons/perks.webp',
    oneTime: false,
    discountExcluded: true, // Staffing cost — excluded from multi-service bundle discount
    // Single-tier service, the in-card PT/FT toggle inside <DedicatedFlow /> is
    // suppressed automatically when only one tier is available.
    tiers: [
      { id: 'parttime', name: 'Part Time', blurb: 'Pre-vetted specialists deemed primarily on Our DM Refer/Hybrid R+1 talent pool.' },
    ],
    tierFeatures: {
      parttime: ['Flexible day rates', 'Pre-vetted talent pool', 'Scale up or down month-to-month'],
    },
    // No `prices` keyed entry → priceFor() returns { custom: true } at the
    // service level; per-role day rates handle the actual numbers.
    prices: {},
    roles: {
      // Each PT role declares its own explicit day-rate ladder (1 / 5 / 10 / custom-11+).
      // These come from the official pricing sheet, they don't follow a single uniform
      // discount %, so we store the actual per-day amounts and let the UI derive the
      // savings copy ("X% off") from `dayRates.d1` (i.e. base) vs the lower tiers.
      parttime: [
        { id: 'pt-2d',           name: '2D Designer',              priceLabel: 'From £325/day', price: 325, unit: '/day',
          dayRates: { d1: 325, d5: 290, d10: 285, custom: 275 } },
        { id: 'pt-copy',         name: 'Copywriter',               priceLabel: 'From £325/day', price: 325, unit: '/day',
          dayRates: { d1: 325, d5: 290, d10: 285, custom: 275 } },
        { id: 'pt-video',        name: 'Video Editor',             priceLabel: 'From £355/day', price: 355, unit: '/day',
          dayRates: { d1: 355, d5: 320, d10: 310, custom: 300 } },
        { id: 'pt-motion',       name: 'Motion Graphics Designer', priceLabel: 'From £355/day', price: 355, unit: '/day',
          dayRates: { d1: 355, d5: 320, d10: 310, custom: 300 } },
        { id: 'pt-3d',           name: '3D Designer',              priceLabel: 'From £475/day', price: 475, unit: '/day',
          dayRates: { d1: 475, d5: 425, d10: 415, custom: 400 } },
        { id: 'pt-web',          name: 'Web Developer',            priceLabel: 'From £415/day', price: 415, unit: '/day',
          dayRates: { d1: 415, d5: 375, d10: 365, custom: 350 } },
        { id: 'pt-ai',           name: 'Automation & AI Agent',    priceLabel: 'From £590/day', price: 590, unit: '/day',
          dayRates: { d1: 590, d5: 530, d10: 520, custom: 500 } },
        { id: 'pt-va',           name: 'Virtual Assistant',        priceLabel: 'From £175/day', price: 175, unit: '/day',
          dayRates: { d1: 175, d5: 160, d10: 155, custom: 150 } },
        { id: 'pt-marketer',     name: 'General Marketer',         priceLabel: 'From £235/day', price: 235, unit: '/day',
          dayRates: { d1: 235, d5: 210, d10: 205, custom: 200 } },
        { id: 'pt-seo',          name: 'SEO Specialist',           priceLabel: 'From £445/day', price: 445, unit: '/day',
          dayRates: { d1: 445, d5: 400, d10: 390, custom: 375 } },
        { id: 'pt-appt',         name: 'Appointment Setter',       priceLabel: 'From £235/day', price: 235, unit: '/day',
          dayRates: { d1: 235, d5: 210, d10: 205, custom: 200 } },
        { id: 'pt-sales',        name: 'Sales Representative',     priceLabel: 'From £295/day', price: 295, unit: '/day',
          dayRates: { d1: 295, d5: 265, d10: 260, custom: 250 } },
      ],
    },
    // Part-Time has no addons, those (visa, buyout, IT equipment, etc.) only
    // make sense for embedded Full-Time hires, where they're shown.
    addons: [],
  },
  {
    id: 'dedicated-ft',
    name: 'Dedicated Resources · Full-Time',
    desc: 'Embed permanent specialists with comprehensive recruitment and long-term placement. Each role is custom-priced based on seniority and location.',
    category: 'Talent',
    icon: 'assets/icons/perks.webp',
    oneTime: false,
    discountExcluded: true, // Staffing cost — excluded from multi-service bundle discount
    tiers: [
      { id: 'fulltime', name: 'Full Time', blurb: 'Full-Time roles are sourced through our comprehensive recruitment process.', badge: 'popular' },
    ],
    tierFeatures: {
      fulltime: ['Dedicated monthly subscription', 'Comprehensive recruitment', 'Long-term placement'],
    },
    prices: {},
    roles: {
      fulltime: [
        // Pricing bands, indicative starting rates (Philippines floor); the
        // configure panel multiplies these by per-country / per-seniority
        // factors to surface a range. Final price is confirmed post-shortlist.
        //   £8/hour , Philippines baseline (admin, general creative, content)
        //   £12/hour, sales-line roles (SDR / CSM / AM)
        //   £14/hour, highly skilled tech + senior design
        { id: 'ft-mkt-ops',      name: 'Marketing & Ops Coordinator',         priceLabel: 'From £8/hour',  hourlyFrom: 8,  custom: true },
        { id: 'ft-graphic',      name: 'Graphic Designer',                    priceLabel: 'From £8/hour',  hourlyFrom: 8,  custom: true },
        { id: 'ft-copy',         name: 'Copywriter',                          priceLabel: 'From £8/hour',  hourlyFrom: 8,  custom: true },
        { id: 'ft-video',        name: 'Video Editor',                        priceLabel: 'From £8/hour',  hourlyFrom: 8,  custom: true },
        { id: 'ft-sdr',          name: 'SDR (Sales Development Representative)', priceLabel: 'From £12/hour', hourlyFrom: 12, custom: true },
        { id: 'ft-csm',          name: 'Customer Success Manager',            priceLabel: 'From £12/hour', hourlyFrom: 12, custom: true },
        { id: 'ft-am',           name: 'Account Manager',                     priceLabel: 'From £12/hour', hourlyFrom: 12, custom: true },
        { id: 'ft-fullstack',    name: 'Full-Stack Developer',                priceLabel: 'From £14/hour', hourlyFrom: 14, custom: true },
        { id: 'ft-ai',           name: 'AI Agent & Automation Specialist',    priceLabel: 'From £14/hour', hourlyFrom: 14, custom: true },
        { id: 'ft-data',         name: 'Data Analyst',                        priceLabel: 'From £14/hour', hourlyFrom: 14, custom: true },
        { id: 'ft-devops',       name: 'DevOps Engineer',                     priceLabel: 'From £14/hour', hourlyFrom: 14, custom: true },
        { id: 'ft-uiux',         name: 'UI/UX Designer',                      priceLabel: 'From £14/hour', hourlyFrom: 14, custom: true },
        { id: 'ft-3d',           name: '3D Designer',                         priceLabel: 'From £14/hour', hourlyFrom: 14, custom: true },
        { id: 'ft-vmotion',      name: 'Video & Motion Designer',             priceLabel: 'From £14/hour', hourlyFrom: 14, custom: true },
        { id: 'ft-exec',         name: 'Executive Assistant',                 priceLabel: 'From £8/hour',  hourlyFrom: 8,  custom: true },
        { id: 'ft-va',           name: 'Virtual Assistant',                   priceLabel: 'From £8/hour',  hourlyFrom: 8,  custom: true },
      ],
    },
    addons: [
      { id: 'buyout',       group: 'Standard add-ons', name: 'Talent Buyout Option',            desc: 'Convert a specialist into your direct employee whenever you are ready.', tip: 'Buyout fee is a declining multiple of the monthly management fee based on tenure:\n\u2022 12 to 18 months: 4x monthly fee\n\u2022 18 to 24 months: 3x monthly fee\n\u2022 24 months and beyond: 2x monthly fee', priceLabel: 'Contact for pricing', custom: true, oneTime: true },
      { id: 'it-equip',     group: 'Standard add-ons', name: 'IT Equipment Provisioning',       desc: 'We procure, set up, and ship your specialist\'s IT kit.', tip: 'We handle procurement, setup, and delivery of everything your specialist needs, such as a laptop and monitor, so they are productive from day one.', priceLabel: 'Contact for pricing', custom: true },
      { id: 'uk-direct',    group: 'Standard add-ons', name: 'UK Direct Hire Recruitment',      desc: 'A dedicated recruitment service for permanent UK hires.', tip: 'We use our talent pool to find and place the right permanent hire for a UK-based role, handling sourcing and shortlisting for you.', priceLabel: 'Contact for pricing', custom: true },
      { id: 'visa',         group: 'Enterprise support', name: 'Visa & Work Permit Sponsorship',  desc: 'Full support for UK visa and work permit sponsorship.', tip: 'We manage UK visa and work permit sponsorship for specialist hires end to end, including the application process and compliance guidance.', priceLabel: 'Contact for pricing', custom: true, onboardingStatus: 'full' },
      { id: 'benefits',     group: 'Enterprise support', name: 'Enhanced Benefits Package',       desc: 'Premium health, dental, and vision cover to attract top talent.', tip: 'A premium benefits package, including top-tier private health, dental, and vision insurance, to help you attract and retain the best specialists.', priceLabel: 'Contact for pricing', custom: true, onboardingStatus: 'full' },
      { id: 'coworking',    group: 'Enterprise support', name: 'Coworking Space Membership',      desc: 'A local coworking membership for your specialist.', tip: 'We provide your specialist with membership to a local coworking space, for a professional work environment and better day-to-day productivity.', priceLabel: 'Contact for pricing', custom: true, onboardingStatus: 'full' },
      { id: 'pro-dev',      group: 'Enterprise support', name: 'Professional Development Stipend', desc: 'An annual budget for training, certifications, and conferences.', tip: 'A dedicated annual budget for your specialist to pursue training, certifications, or industry conferences, so their skills stay current.', priceLabel: 'Contact for pricing', custom: true, onboardingStatus: 'full' },
    ],
  },
  {
    id: 'whitelabel',
    name: 'White Label Services',
    desc: 'Reseller fulfilment and client management for agencies.',
    category: 'Agency',
    icon: 'assets/icons/agency.webp',
    freeStarter: true,
    commitMonths: 3,
    // Tier-level waiting list: these tier ids render as "Waiting list" instead
    // of a price, can still be added to a quote (so we capture demand in
    // Airtable), but contribute £0 to the calculator total.
    // 2026-05-22: Airtable has only Grow (£99 Fulfilment + Client Management)
    // and Enterprise (Contact for Pricing) as active WL tiers. Starter (free
    // reseller catalogue) and the legacy Scale tier (£255, archived April
    // 2026) are hidden from the calculator entirely via the per-service
    // `tiers` override below. Enterprise stays visible but flagged waitlist.
    tiers: [
      { id: 'grow',       name: 'Grow',       blurb: 'For growing agencies who want GoGorilla to manage all day-to-day client communication under their brand' },
      { id: 'enterprise', name: 'Enterprise', blurb: 'For large agencies needing a dedicated elite team, custom reporting, and global support.', isEnterprise: true },
    ],
    waitlistTiers: [],
    tierFeatures: {
      starter: ['Reseller fulfilment', 'Unbranded deliverables', 'Self-serve portal'],
      grow:    ['Fulfilment + client mgmt', 'White-label reporting', 'Dedicated success mgr'],
      scale:   ['Full white-label agency', 'Co-branded proposals', 'Priority SLAs'],
    },
    prices: {
      starter: { '3': 0,   '6': 0,   '12': 0   },
      grow:    { '3': 99,  '6': 99,  '12': 99  },
      scale:   { '3': 255, '6': 255, '12': 255 },
    },
    addons: [],
  },
  {
    id: 'founders-portal',
    name: 'Founders Portal',
    desc: 'Investor matching, deal flow, and fundraising tools.',
    category: 'Capital',
    icon: 'assets/icons/algorithmic.webp',
    freeStarter: true,
    commitMonths: 3,
    tiers: [
      { id: 'starter', name: 'Founders Free' },
      { id: 'grow',    name: 'Founders Pro' },
      { id: 'scale',   name: 'Founders Pro+', badge: 'featured' },
    ],
    waitlistTiers: [],
    tierFeatures: {
      starter: ['Up to 50 investors from database', 'Verified contact details', 'Standard profile', 'Basic search & filters'],
      grow:    ['7,500+ verified investors', 'Verified badge + priority', 'Pitch deck & video upload', 'Advanced search & analytics'],
      scale:   ['Everything in Pro', 'White-glove intros', 'Dedicated capital associate', 'Referral rewards'],
    },
    prices: {
      starter: { '3': 0,   '6': 0,   '12': 0   },
      grow:    { '3': 249, '6': 199, '12': 99  },
      scale:   { '3': 499, '6': 499, '12': 499 },
    },
    incentives: [
      { type: 'built-in', label: 'Algorithmic Team Profit Sharing' },
      { type: 'optional', label: 'Performance Recognition Tips' },
      { type: 'add-on',  label: 'Outcome-Linked Success Bonus' },
    ],
    addons: [],
  },
  {
    id: 'fundraising',
    name: 'Fundraising Support',
    desc: 'Investor-ready materials, investor research, pitch coaching.',
    category: 'Capital',
    icon: 'assets/icons/partnership.webp',
    oneTime: true,
    waitlistTiers: [],
    tierFeatures: {
      starter: ['Pitch deck + one-pager', 'Investor research', 'Email intro templates'],
      grow:    ['Full data room', 'Financial model', 'Pitch coaching (3 sessions)', 'Investor outreach list'],
      scale:   ['End-to-end campaign', 'Live investor meetings', 'Roadshow coordination', 'Negotiation support'],
    },
    prices: { starter: 3495, grow: 6995, scale: 14995 },
    tierDesc: {
      starter: 'Pre-seed startups preparing their first raise who need professional materials and self-service outreach.',
      grow: 'Seed-stage startups raising £250k-£2M who need full support across materials, research, and strategy.',
      scale: 'Series A startups raising £2M+ who need premium materials, deep investor research, and hands-on strategy.',
      enterprise: 'Later-stage or complex raises that need a fully bespoke, hands-on fundraising programme.',
    },
    addons: [
      // ── Fundraising Materials ──
      { id: 'deck-var',   group: 'Fundraising Materials',            name: 'Additional Pitch Deck Variations', desc: 'Extra versions of your deck tailored to different investor audiences.', tip: 'We adapt your existing deck for specific audiences such as VCs, angels, or strategics, keeping one core story with audience-specific emphasis.', price: 1495, unit: 'one-time', oneTime: true, discountable: true },
      { id: 'deck-refresh',group:'Fundraising Materials',            name: 'Pitch Deck Refresh',               desc: 'A full rewrite and redesign of your existing deck for your next round.', tip: 'We rework the narrative, structure, and visuals of your current deck so it is round-ready, rather than building a new deck from scratch.',        price: 2495, unit: 'one-time', oneTime: true, discountable: true },
      { id: 'model-update',group:'Fundraising Materials',            name: 'Financial Model Update',           desc: 'We refresh the assumptions, scenarios, and sensitivities in your model.', tip: 'Best when you already have a model that needs updating for a new raise. We update the drivers, add scenarios, and pressure-test the sensitivities investors will probe.',price: 1995, unit: 'one-time', oneTime: true, discountable: true },
      { id: 'model-ext',  group: 'Fundraising Materials',            name: 'Extended Financial Model (5-7 Years)', desc: 'A long-horizon model with cohort tracking and unit economics built up.', tip: 'A deeper five to seven year model with cohort tracking and bottom-up unit economics, suited to Series A and later raises.',     price: 2995, unit: 'one-time', oneTime: true, discountable: true },
      { id: 'investor-up',group: 'Fundraising Materials',            name: 'Investor Update Template',         desc: 'A branded monthly or quarterly update template, with your first one drafted.', tip: 'A reusable investor-update format plus a first drafted edition, so your ongoing updates stay consistent and quick to send.',    price: 495, unit: 'one-time', oneTime: true, discountable: true },
      // ── Investor Outreach & Research ──
      { id: 'research50', group: 'Investor Outreach & Research',     name: 'Extended Investor Research (+50)', desc: 'Another 50 fit-matched investors, each with thesis and contact details.', tip: 'Adds 50 investors on top of your plan\'s research, matched to your stage and sector, each with verified contacts and a note on why they fit.',      price: 2995, unit: 'one-time', oneTime: true, discountable: true },
      { id: 'crm-setup',  group: 'Investor Outreach & Research',     name: 'Investor CRM Setup',               desc: 'A ready-to-use investor pipeline in HubSpot or Affinity.', tip: 'We build your outreach pipeline with stages, templates, and reporting in HubSpot or Affinity, so you can track every investor conversation from day one.', price:  995, unit: 'one-time', oneTime: true, discountable: true },
      { id: 'li-optim',   group: 'Investor Outreach & Research',     name: 'LinkedIn Profile Optimisation',    desc: 'Founder and executive profile rewrites tuned for investor discovery.', tip: 'We rewrite your founder and leadership LinkedIn profiles so they signal credibility and traction when investors look you up.',  price:  495, unit: 'one-time', oneTime: true, discountable: true },
      // ── Pitch Coaching & Preparation ──
      { id: 'coach',      group: 'Pitch Coaching & Preparation',     name: 'Additional Pitch Coaching Session', desc: 'A one-to-one rehearsal with live feedback from a former investor.', tip: 'A 60-minute session where a former investor hears your pitch and gives direct, practical feedback on your delivery and story.',          price:  995, unit: '/session',  oneTime: true, discountable: true, badge: 'popular' },
      { id: 'meet-prep',  group: 'Pitch Coaching & Preparation',     name: 'Investor Meeting Preparation',     desc: 'A pre-meeting brief, a question-and-answer drill, and a debrief afterwards.', tip: 'We prepare you for a specific meeting with a tailored brief, rehearse the likely questions, then debrief to sharpen your follow-up.',         price:  495, unit: '/meeting', oneTime: true, discountable: true },
      { id: 'mock-panel', group: 'Pitch Coaching & Preparation',     name: 'Mock Investor Panel Partner Intro', desc: 'A warm introduction to a partner who runs founder mock panels.', tip: 'We connect you with a trusted partner who runs realistic mock investor panels. The introduction is free, with any panel fees agreed directly with them.',       price: 0,    priceLabel: 'Free', free: true },
      // ── Grants & Non-Dilutive Funding ──
      { id: 'grant-app',  group: 'Grants & Non-Dilutive Funding',    name: 'Additional Grant Application',     desc: 'A full grant application, written, submitted, and followed up.', tip: 'We handle one complete grant application end to end, from drafting to submission and follow-up, so you can pursue non-dilutive funding without the admin.',              price: 2995, unit: '/application', oneTime: true, discountable: true },
      { id: 'rnd-intro',  group: 'Grants & Non-Dilutive Funding',    name: 'R&D Tax Credit Partner Intro',   desc: 'A warm introduction to a vetted R&D tax credit specialist.', tip: 'We connect you with a trusted R&D tax specialist who can assess and file your claim. The introduction is free, with their fees agreed directly with them.',              price: 0,    priceLabel: 'Free', free: true },
      { id: 'seis-intro', group: 'Grants & Non-Dilutive Funding',    name: 'SEIS/EIS Partner Intro',         desc: 'A warm introduction to an SEIS and EIS compliance partner.', tip: 'We connect you with a partner who handles SEIS and EIS advance assurance and compliance, which helps make your round more attractive to UK investors. Free introduction.',                   price: 0,    priceLabel: 'Free', free: true },
      { id: 'loan-intro', group: 'Grants & Non-Dilutive Funding',    name: 'Innovation Loan Partner Intro',  desc: 'A warm introduction to innovation-loan specialists such as Innovate UK.', tip: 'We connect you with specialists in innovation and grant-backed loans, including Innovate UK routes. The introduction is free, with any fees agreed directly with them.',   price: 0,    priceLabel: 'Free', free: true },
      // ── Crowdfunding ──
      { id: 'crowd-mkt',  group: 'Crowdfunding',                     name: 'Crowdfunding Marketing Support',   desc: 'Pre-launch audience building, campaign creative, and paid promotion.', tip: 'A full marketing push for your crowdfunding raise, covering pre-launch audience build, campaign assets, and paid promotion to drive pledges.',   price: 8995, unit: 'one-time', oneTime: true, discountable: true },
      { id: 'crowd-strat',group: 'Crowdfunding',                     name: 'Crowdfunding Campaign Strategy',   desc: 'Goal setting, reward tiers, narrative, and a launch plan.', tip: 'We shape your crowdfunding campaign, setting the target, designing reward tiers, and planning the launch sequence to build early momentum.',          price: 1995, unit: 'one-time', oneTime: true, discountable: true },
      // ── Strategic Advisory ──
      { id: 'termsheet',  group: 'Strategic Advisory',               name: 'Term Sheet Review',                desc: 'A line-by-line review flagging risks and founder-friendly alternatives.', tip: 'We go through your term sheet clause by clause, flag anything unusual or risky, and suggest founder-friendly alternatives to take back to investors.',price:  995, unit: '/term sheet', oneTime: true, discountable: true },
      { id: 'captable',   group: 'Strategic Advisory',               name: 'Cap Table Modelling',              desc: 'Dilution, option pool, and round-by-round scenarios modelled out.', tip: 'We model how your cap table evolves across rounds, including dilution and option-pool impact, so you can see the effect of different deal terms.',            price: 1495, unit: 'one-time',  oneTime: true, discountable: true },
      { id: 'dataroom',   group: 'Strategic Advisory',               name: 'Data Room Audit',                  desc: 'A review of your data room\'s structure, gaps, and narrative before diligence.', tip: 'We audit your data room before investors see it, flagging missing documents and weak spots so diligence runs smoothly.',    price:  995, unit: 'one-time',  oneTime: true, discountable: true },
      // ── Market Analysis ──
      { id: 'market-size',group: 'Market Analysis',                  name: 'Market Sizing Analysis',           desc: 'A defensible TAM, SAM, and SOM, built bottom-up and top-down.', tip: 'We size your market both bottom-up and top-down using credible sources, giving you figures that stand up to investor scrutiny.',       price: 2495, unit: 'one-time', oneTime: true, discountable: true },
      { id: 'competitor', group: 'Market Analysis',                  name: 'Competitive Analysis',             desc: 'A positioning map plus a feature and pricing teardown of your category.', tip: 'We map your competitors and break down their features and pricing, so you can position clearly and answer the "why you" question.', price: 2495, unit: 'one-time', oneTime: true, discountable: true },
      { id: 'cust-intro', group: 'Market Analysis',                  name: 'Customer Research Partner Intro',desc: 'A warm introduction to a vetted customer-research partner.', tip: 'We connect you with a research partner who can run customer interviews and surveys to validate demand. Free introduction, with their fees agreed directly.',              price: 0,    priceLabel: 'Free', free: true },
      // ── Ongoing Support ──
      { id: 'fundraise-ret', group: 'Ongoing Support',               name: 'Monthly Fundraising Support',      desc: 'A named capital associate managing your raise month to month.', tip: 'A dedicated associate who runs your raise day to day, from outreach and scheduling to materials and investor follow-up. Billed monthly.',     price: 1995, unit: '/mo', discountable: true, badge: 'recommended' },
      { id: 'social-ret',    group: 'Ongoing Support',               name: 'Monthly Social Media Management',  desc: 'Your founder and company social presence, run for you.', tip: 'We manage your founder and company social channels, building a consistent voice and posting cadence that supports your raise. Billed monthly.',                price:  995, unit: '/mo', discountable: true },
      { id: 'partner-onb',   group: 'Ongoing Support',               name: 'Partner Onboarding Package',       desc: 'White-glove onboarding across the GoGorilla partner network.', tip: 'We set you up across the GoGorilla partner network, handling introductions and account setup so you can start using every benefit quickly.',         price:  495, unit: 'one-time', oneTime: true, discountable: true },
    ],
  },
  {
    id: 'investor-portal',
    name: 'Investor Portal',
    desc: 'Deal flow, due diligence, and portfolio partner benefits.',
    category: 'Capital',
    icon: 'assets/icons/algorithmic.webp',
    freeStarter: true,
    commitMonths: 3,
    tiers: [
      { id: 'starter', name: 'Network Free' },
      { id: 'grow',    name: 'Partner Pro' },
      { id: 'scale',   name: 'Partner Pro+', badge: 'featured' },
    ],
    waitlistTiers: [],
    tierFeatures: {
      starter: ['Unlimited due diligence assessments (£2,500-£5,000 each)', '20% discount on GoGorilla services for portfolio companies', 'Access Trusted Partner Network (SEIS/EIS, legal, M&A, fractional execs)', 'Curated founder directory (predominantly UK & US-based)', 'Monthly general-interest deal flow digest', 'Optional reciprocal listing in Founders Portal'],
      grow:    ['Weekly thesis-matched deal flow digest', 'Smart Deal Matching (AI-powered by thesis, sector, stage, cheque size)', 'Exclusive Co-investment Network Access', 'Advanced search and filtering of founder directory', 'Personal opportunity tracker', 'Priority reciprocal listing in Founders Portal', 'Quarterly investor community calls'],
      scale:   ['Acquisition thesis calibration session', 'Dedicated buy-side deal origination', 'Vendor-advisor relationships and mandate access', 'Curated opportunity briefs with thesis match', 'Warm introductions and meeting facilitation', 'Live 12-month mandate with continuous sourcing', 'Named mandate lead with quarterly reviews', 'Partner firm introductions (legal, tax, banking)', 'Success-fee engagement, no upfront cost'],
    },
    prices: {
      starter: { '3': 0,    '6': 0,    '12': 0    },
      grow:    { '3': 199,  '6': 149,  '12': 99   },
      scale:   { '3': null, '6': null, '12': null },
    },
    addons: [
      { id: 'investor-dinner', name: 'Investor Dinner Hosting',         desc: 'A hosted investor dinner, organised and run end to end.', tip: 'We plan and host a private investor dinner on your behalf, from venue and guest list to running the evening, so you build relationships in the right setting. Priced from £4,995 per event.', price: 4995, priceLabel: 'From £4,995', unit: '/event', oneTime: true },
      { id: 'sector-research',  name: 'Bespoke Sector Research',          desc: 'An expert shortlist of 10 to 15 thesis-matched investors.', tip: 'We research your target sector and build a shortlist of 10 to 15 investors matched to your thesis, with the context you need to approach them. Priced from £2,995 per project.', price: 2995, priceLabel: 'From £2,995', unit: '/project', oneTime: true },
      { id: 'portfolio-pod',    name: 'Portfolio Marketing Pod',          desc: 'A dedicated marketing pod for one portfolio company, billed monthly.', tip: 'A dedicated marketing team for one of your portfolio companies. Starts with a 60 to 90 minute strategy call and a written assessment. Priced from £2,495 a month per company.', price: 2495, priceLabel: 'From £2,495', unit: '/mo' },
      { id: 'premium-dd',       name: 'Premium Due Diligence Report',     desc: 'A premium marketing due-diligence report on a target.', tip: 'A detailed marketing due-diligence report on a potential investment, covering positioning, channels, and growth potential, to strengthen your diligence. Priced from £995 per report.', price: 995, priceLabel: 'From £995', unit: '/report', oneTime: true },
    ],
  },
];

const FAQS = [
  { q: 'How does the instant quote work?',
    a: "Pick your client type, tier each service to the level you need, and the summary updates live. The number you see is the monthly price you'd pay based on your commitment, no hidden setup fees, no retainers, no surprises." },
  { q: "What if I'm an agency or reseller?",
    a: 'Pick "Agency / Reseller" as your client type and a 40% white-label discount applies automatically across every service. You can also apply any partner code on top.' },
  { q: 'Can I change service tiers mid-contract?',
    a: "Yes. Upgrade at any time by topping up the difference. Downgrades take effect at the start of the next billing period." },
  { q: "What happens after I pick 'I'm ready to sign up'?",
    a: "You'll be handed to Stripe Checkout with exactly the scope you built here, same price, same services. Onboarding kicks off within 48 hours with your GorillaAudit® blueprint." },
  { q: 'Do you work with companies outside the UK?',
    a: 'Yes. Pricing shown is GBP but we invoice in USD, EUR, or AUD on request. Our pods cover UK, US, and APAC timezones with tax-compliant entities across those regions.' },
];


// ── ADD-ON AVAILABILITY MATRIX ─────────────────────────────────────────────
// Defines which add-ons are available per (service, tier) and what price
// to show for each commitment period.
//
// Value per tier key:
//   false    → Not Available, hide this add-on entirely for this tier
//   'custom' → Show with "Contact for pricing" label
//   'inc'    → Show as Included in plan (free, treated as £0 in totals)
//   true     → Show at the default price defined in the service's addons array
//   <number> → Fixed price for all commitment periods (overrides default)
//   { '3': n, '6': n, '12': n } → Per-commitment price override
//              (omit a key to fall back to the nearest provided value)
//
// Services not listed here pass through all add-ons unchanged (no filtering).
const ADDON_MATRIX = {
  // Values sourced from Airtable base appRgD9IZC7YHxw1d (Services table).
  // Each entry is per-tier; the value is:
  //   false        → Not Available (hidden)
  //   true         → Available at the addon's default price
  //   <number>     → Fixed price override (all commitment periods)
  //   { '3':n, '6':n, '12':n } → Per-commitment price
  //   'custom'     → Custom / Contact us
  //   'inc'        → Included free in the plan

  // ── Paid Advertising ───────────────────────────────────────────────────────
  'paid-ads': {
    //                              Starter                              Grow                                  Scale                               Enterprise
    'meta-retarget':    { starter: { '3': 169, '6': 139, '12':  99 }, grow: { '3': 169, '6': 139, '12':  99 }, scale: { '3': 169, '6': 139, '12':  99 }, enterprise: 'custom'                         },
    'google-display':   { starter: { '3': 169, '6': 139, '12':  99 }, grow: false,                              scale: { '3': 169, '6': 139, '12':  99 }, enterprise: 'custom'                         },
    'google-shop':      { starter: { '3': 169, '6': 139, '12':  99 }, grow: false,                              scale: { '3': 169, '6': 139, '12':  99 }, enterprise: 'custom'                         },
    'linkedin-retarget':{ starter: { '3': 169, '6': 139, '12':  99 }, grow: { '3': 169, '6': 139, '12':  99 }, scale: { '3': 169, '6': 139, '12':  99 }, enterprise: 'custom'                         },
    'lp-build':         { starter: 1495,                               grow: 2495,                               scale: 4495,                               enterprise: 'custom'                         },
    'landing':          { starter: { '3': 249, '6': 209, '12': 149 }, grow: { '3': 249, '6': 209, '12': 149 }, scale: { '3': 249, '6': 209, '12': 149 }, enterprise: 'custom'                         },
    'cro-prog':         { starter: { '3': 249, '6': 209, '12': 149 }, grow: { '3': 679, '6': 559, '12': 399 }, scale: { '3': 999, '6': 839, '12': 599 }, enterprise: 'custom'                         },
    'call-track':       { starter: { '3': 169, '6': 139, '12':  99 }, grow: false,                              scale: { '3': 169, '6': 139, '12':  99 }, enterprise: 'inc'                            },
    'adv-attribution':  { starter: false,                              grow: { '3': 419, '6': 349, '12': 249 }, scale: { '3': 679, '6': 559, '12': 399 }, enterprise: 'custom'                         },
    'dw-setup':         { starter: false,                              grow: false,                              scale: 499,                                enterprise: 599                              },
    'dw-ongoing':       { starter: false,                              grow: false,                              scale: 99,                                 enterprise: false                            },
    'zapier':           { starter: { '3':  89, '6':  69, '12':  49 }, grow: { '3':  89, '6':  69, '12':  49 }, scale: { '3':  89, '6':  69, '12':  49 }, enterprise: { '3': 89, '6': 69, '12': 49 } },
  },

  // ── Social Media Management ────────────────────────────────────────────────
  'smm': {
    //                           Starter                              Grow                                    Scale                               Enterprise
    'community':  { starter: false,                              grow: { '3': 499, '6': 419, '12': 299 }, scale: { '3': 499, '6': 419, '12': 299 }, enterprise: { '3': 499, '6': 419, '12': 299 } },
    'post-boost': { starter: { '3': 499, '6': 419, '12': 299 }, grow: { '3': 499, '6': 419, '12': 299 }, scale: { '3': 499, '6': 419, '12': 299 }, enterprise: { '3': 499, '6': 419, '12': 299 } },
    'showcase':   { starter: false,                              grow: false,                              scale: false,                             enterprise: 299                                },
    'dw-setup':   { starter: false,                              grow: false,                              scale: false,                             enterprise: true                               },
    // New addons, Airtable sourced
    'additional-reports': { starter: { '3': 139, '6': 109, '12':  79 }, grow: { '3': 139, '6': 109, '12':  79 }, scale: { '3': 139, '6': 109, '12':  79 }, enterprise: false                                   },
    'insta-story':        { starter: { '3':  89, '6':  69, '12':  49 }, grow: { '3': 119, '6':  99, '12':  69 }, scale: { '3': 149, '6': 119, '12':  89 }, enterprise: { '3': 239, '6': 199, '12': 139 }        },
    'custom-highlights':  { starter: { '3': 419, '6': 349, '12': 249 }, grow: { '3': 419, '6': 349, '12': 249 }, scale: { '3': 419, '6': 349, '12': 249 }, enterprise: { '3': 419, '6': 349, '12': 249 }        },
    'linkedin-posts':     { starter: { '3': 119, '6':  99, '12':  69 }, grow: { '3': 149, '6': 119, '12':  89 }, scale: { '3': 189, '6': 159, '12': 109 }, enterprise: { '3': 239, '6': 199, '12': 139 }        },
    'extra-lang':         { starter: false,                              grow: 399,                                scale: 399,                               enterprise: 399                                      },
    'extra-channel':      { starter: false,                              grow: { '3': 299, '6': 249, '12': 199 }, scale: { '3': 299, '6': 249, '12': 199 }, enterprise: { '3': 299, '6': 249, '12': 199 }        },
    'influencer':         { starter: false,                              grow: { '3': 999, '6': 849, '12': 799 }, scale: { '3': 999, '6': 849, '12': 799 }, enterprise: { '3': 999, '6': 849, '12': 799 }        },
  },

  // ── Email Marketing ────────────────────────────────────────────────────────
  'email': {
    //                                Starter                              Grow                                  Scale                                 Enterprise
    'platform-migration': { starter: false,                              grow: false,                              scale: { '3': 139, '6': 109, '12':  79 }, enterprise: { '3': 139, '6': 109, '12':  79 } },
    'sms':                { starter: false,                              grow: false,                              scale: { '3': 599, '6': 509, '12': 359 }, enterprise: { '3': 599, '6': 509, '12': 359 } },
    'deliverability-audit': { starter: 249,                             grow: 199,                                scale: 149,                               enterprise: 99                                  },
    'campaign-pack':      { starter: true,                              grow: true,                               scale: true,                              enterprise: true                                },
    'landing':            { starter: { '3': 249, '6': 209, '12': 149 }, grow: { '3': 249, '6': 209, '12': 149 }, scale: { '3': 249, '6': 209, '12': 149 }, enterprise: 'custom'                             },
    'adv-segmentation':   { starter: false,                             grow: false,                              scale: 479,                               enterprise: 479                                 },
    'zapier':             { starter: { '3':  89, '6':  69, '12':  49 }, grow: { '3':  89, '6':  69, '12':  49 }, scale: { '3':  89, '6':  69, '12':  49 }, enterprise: { '3':  89, '6':  69, '12':  49 }  },
    'dw-setup':           { starter: false,                              grow: false,                              scale: false,                             enterprise: true                                },
    'dw-ongoing':         { starter: false,                              grow: false,                              scale: false,                             enterprise: false                               },
    'additional-reports': { starter: false,                              grow: false,                              scale: false,                             enterprise: false                               },
  },

  // ── Content Creation ─────────────────────────────────────────────────────
  'content': {
    //                                 Starter  Grow    Scale   Enterprise
    'content-raw-footage':  { starter: 499,     grow: 499,     scale: 499,     enterprise: 'inc' },
    'content-rights-perp':  { starter: 599,     grow: 599,     scale: 599,     enterprise: 'inc' },
    'content-rights-12m':   { starter: 349,     grow: 349,     scale: 349,     enterprise: 'inc' },
    'content-rights-6m':    { starter: 199,     grow: 199,     scale: 199,     enterprise: 'inc' },
  },

  // ── Sales & Demand Generation ──────────────────────────────────────────────
  'sales': {
    //                          Starter     Grow        Scale       Enterprise
    'ads-lite':         { starter: true,     grow: true,     scale: 'inc',    enterprise: 'inc'    },
    'ads-full':         { starter: false,     grow: false,     scale: false,    enterprise: 'inc'    },
  },
};

// Internal: resolve a single tier slot to { available, price?, custom?, included? }.
function _resolveAddonTierEntry(entry, commitId) {
  if (entry === false || entry === undefined) return { available: false };
  if (entry === 'custom') return { available: true, custom: true };
  if (entry === 'inc')    return { available: true, included: true };
  if (entry === true)     return { available: true };
  if (typeof entry === 'number') return { available: true, price: entry };
  if (typeof entry === 'object') {
    const cid = String(commitId || '12');
    const price = entry[cid] != null ? entry[cid]
                : entry['12'] != null ? entry['12']
                : entry['6']  != null ? entry['6']
                : entry['3'];
    return price != null ? { available: true, price } : { available: true };
  }
  return { available: true };
}

// Returns a filtered + price-adjusted copy of service.addons for the
// given (tierId, commitId) context.
//   • Add-ons marked Not Available for this tier are removed.
//   • Prices are updated to the correct value for the commitment period.
//   • Included add-ons get free:true + included:true (£0, non-selectable).
//   • Custom add-ons get custom:true.
// Services without an ADDON_MATRIX entry are returned unchanged.
function getAddonsForContext(service, tierId, commitId) {
  if (!service || !Array.isArray(service.addons)) return [];
  const svcMatrix = ADDON_MATRIX[service.id];
  if (!svcMatrix) return service.addons;           // no matrix → passthrough

  const result = [];
  for (const addon of service.addons) {
    const tierMap = svcMatrix[addon.id];
    if (tierMap === undefined) {
      result.push(addon);                          // addon not in matrix → keep as-is
      continue;
    }
    const r = _resolveAddonTierEntry(tierMap[tierId], commitId);
    if (!r.available) continue;                    // Not Available → skip

    if (r.custom) {
      result.push({ ...addon, custom: true, price: 0, priceLabel: null });
    } else if (r.included) {
      result.push({ ...addon, free: true, price: 0, priceLabel: null, included: true });
    } else if (r.price != null) {
      result.push({ ...addon, price: r.price, priceLabel: null });
    } else {
      result.push(addon);                          // true → use original
    }
  }
  return result;
}

Object.assign(window, { CLIENT_TYPES, TIERS, COMMITMENTS, SERVICES, FAQS, priceFor, commitLabelFor, commitsFor, ADDON_MATRIX, getAddonsForContext });

// #82: per-tier add-on discount shown on the Fundraising Support pricing page.
// Starter 20% off, Grow 25%, Scale 30% (Enterprise treated as Scale). Returns a
// price multiplier. Used for both the add-on card display and the cart total.
function fundraisingAddonMult(tierId) {
  switch (tierId) {
    case 'starter': return 0.80;
    case 'grow':    return 0.75;
    case 'scale':   return 0.70;
    case 'enterprise': return 0.70;
    default: return 0.75;
  }
}
window.fundraisingAddonMult = fundraisingAddonMult;

// ── Multi-page step model ──
// The pricing flow is composed dynamically from a CATALOG of step definitions
// keyed by `id`. The visible flow depends on the chosen client type:
//   • Agency  → client → WHITE-LABEL (route-setting) → growth → creative → talent → checkout
//                (whitelabel is its OWN step for agencies and is hidden from talent;
//                 fundraising is hidden, agencies aren't raising capital)
//   • Founder → client → growth → creative → talent → fundraising → checkout
//   • Investor→ client → growth → creative → talent → fundraising → checkout
//                (portfolio-wide pricing/discount logic to come later)
//   • No client type chosen → same as founder (default browse view)
const STEP_CATALOG = {
  client:      { id: 'client',      label: 'Client type',         icon: 'assets/icons/step-client.webp',      serviceIds: [] },
  qualifier:   { id: 'qualifier',   label: 'Tell us about you',   icon: 'assets/icons/step-client.webp',      serviceIds: [], isQualifier: true },
  whitelabel:  { id: 'whitelabel',  label: 'White-label plan',    icon: 'assets/icons/step-talent.webp',      serviceIds: ['whitelabel'] },
  growth:      { id: 'growth',      label: 'Growth services',     icon: 'assets/icons/step-growth.webp',      serviceIds: ['sales', 'paid-ads', 'email'] },
  creative:    { id: 'creative',    label: 'Creative services',   icon: 'assets/icons/step-creative.webp',    serviceIds: ['smm', 'content', 'motion'] },
  talent:      { id: 'talent',      label: 'Talent solutions',    icon: 'assets/icons/step-talent.webp',      serviceIds: ['dedicated-ft', 'dedicated-pt', 'whitelabel'] },
  fundraising: { id: 'fundraising', label: 'Capital solutions',   icon: 'assets/icons/step-fundraising.webp', serviceIds: ['founders-portal', 'fundraising'] },
  checkout:    { id: 'checkout',    label: 'Next steps',          icon: 'assets/icons/step-checkout.webp',    serviceIds: [] },
};

// Returns the ordered step definitions for a given client type. Each step is a
// CLONE so we can override `serviceIds` (e.g. removing `whitelabel` from talent
// for agencies, where it lives as its own step) without mutating the catalog.
function stepsForClient(clientTypeId, intentId) {
  if (clientTypeId === 'agency') {
    // Sprint 1: qualifier inserts right after client. White-label gets its own
    // dedicated step IMMEDIATELY after qualifier, it's the route-setting
    // choice that determines discount tier and pathway. Talent step keeps
    // `dedicated` only (whitelabel moves out).
    //
    // Whitelabel restriction: agencies on the 'agency-whitelabel' intent can
    // ONLY resell Growth + Creative services. The Talent step is omitted from
    // their flow entirely (they cannot resell GoGorilla talent). Agencies on
    // the 'agency-own' (internal, 15%) intent retain Talent access.
    const isWhitelabel = intentId === 'agency-whitelabel';
    // Sprint 5: 'Tell us about you' (qualifier) step has been removed.
    // Recommendation / default-tier / addon-rec functions all handle empty
    // qualifier objects already, so dropping the step is safe.
    const isAgencyOwn = intentId === 'agency-own';
    // 2026-05-26: removed the dedicated white-label plan step. The 40%
    // white-label discount is already triggered by intentId ===
    // 'agency-whitelabel' (see getAgencyMultiplier), so making the user
    // pick a WL tier on a dedicated page was redundant. Agencies now go
    // straight from client-type → growth services.
    const baseFlow = [
      { ...STEP_CATALOG.client },
      ...(isAgencyOwn
        ? [{ ...STEP_CATALOG.growth, serviceIds: ['sales'] }]
        : [{ ...STEP_CATALOG.growth }, { ...STEP_CATALOG.creative }]),
    ];
    if (!isWhitelabel) {
      baseFlow.push({ ...STEP_CATALOG.talent, serviceIds: ['dedicated-ft', 'dedicated-pt'] });
    }
    baseFlow.push({ ...STEP_CATALOG.checkout });
    return baseFlow;
  }
  // Founder / Investor / no-client-yet: hide the whitelabel service entirely.
  if (clientTypeId === 'investor') {
    return [
      { ...STEP_CATALOG.client },
      { ...STEP_CATALOG.growth },
      { ...STEP_CATALOG.creative },
      { ...STEP_CATALOG.talent, serviceIds: ['dedicated-ft', 'dedicated-pt'] },
      { ...STEP_CATALOG.fundraising, label: 'Investor Network', serviceIds: ['investor-portal'] },
      { ...STEP_CATALOG.checkout },
    ];
  }
  // Default (founder / no-client-yet): insert qualifier between client and growth.
  // For "no client yet" we still surface the qualifier step in the breadcrumb
  // so the layout looks coherent before a client is picked.
  if (!clientTypeId) {
    return [
      { ...STEP_CATALOG.client },
      { ...STEP_CATALOG.growth },
      { ...STEP_CATALOG.creative },
      { ...STEP_CATALOG.talent, serviceIds: ['dedicated-ft', 'dedicated-pt'] },
      { ...STEP_CATALOG.fundraising },
      { ...STEP_CATALOG.checkout },
    ];
  }
  // §8, Founder intent skip rules. `intent === 'portal'` (Sign up to Founders
  // Portal) skips Growth / Creative / Talent, jump straight to Capital where
  // the portal signup lives. `intent === 'explore'` (anonymous pricing browse)
  // additionally skips Capital, only About-you and Schedule-a-call remain.
  if (clientTypeId === 'founder' && intentId === 'portal') {
    return [
      { ...STEP_CATALOG.client },
      { ...STEP_CATALOG.fundraising },
      { ...STEP_CATALOG.checkout },
    ];
  }
  if (clientTypeId === 'founder' && intentId === 'explore') {
    return [
      { ...STEP_CATALOG.client },
      { ...STEP_CATALOG.checkout },
    ];
  }
  return [
    { ...STEP_CATALOG.client },
    { ...STEP_CATALOG.growth },
    { ...STEP_CATALOG.creative },
    { ...STEP_CATALOG.talent, serviceIds: ['dedicated-ft', 'dedicated-pt'] },
    { ...STEP_CATALOG.fundraising },
    { ...STEP_CATALOG.checkout },
  ];
}

// ── Breadcrumb label + tooltip overrides (founders-step-breadcrumb §9/§10) ─
// Map step id → display label per persona + intent. The base label comes from
// STEP_CATALOG; this function overrides it when persona/intent semantics call
// for different copy.
function getStepLabel(stepId, clientTypeId, intentId, fallbackLabel) {
  if (stepId === 'client') {
    if (clientTypeId === 'investor') return 'About you';
    if (clientTypeId === 'agency')   return 'About your agency';
    if (clientTypeId === 'founder')  return 'About you';
    return 'About you';
  }
  if (stepId === 'fundraising') {
    if (clientTypeId === 'founder' && intentId === 'portal') return 'Founders Portal';
    if (clientTypeId === 'investor') return 'Investors Portal';
  }
  if (stepId === 'checkout') {
    if (clientTypeId === 'founder' && intentId === 'explore') return 'View pricing';
    return 'Schedule a call';
  }
  return fallbackLabel || (window.STEP_CATALOG?.[stepId]?.label) || '';
}
window.getStepLabel = getStepLabel;

const STEP_TOOLTIPS_BASE = {
  client:      'Tell us about you so we can tailor pricing, tiers, and add-ons to your stage and ICP.',
  whitelabel:  'Pick your reseller route, it locks in your discount tier and fulfilment pathway.',
  growth:      'Fully managed sales and marketing. AI-matched across Sales & Demand Generation, Paid Advertising, and Email Marketing.',
  creative:    'Social, content, and 3D animation. Your owned channel and brand engine.',
  talent:      'Embed dedicated specialists from our team, marketers, creatives, ops, and engineering.',
  fundraising: 'Investor-ready materials, investor research, and access to our Founders Portal.',
  checkout:    'Book a call with our team to walk through your tailored proposal.',
};
function getStepTooltip(stepId, clientTypeId, intentId) {
  if (stepId === 'client') {
    if (clientTypeId === 'investor') return 'Tell us about your investment focus, typical cheque size, and intent.';
    if (clientTypeId === 'agency')   return 'About your agency. Tell us your model, size, and what you sell to clients.';
  }
  if (stepId === 'fundraising' && clientTypeId === 'investor') {
    return 'Investors Portal, apply for Network (free) or Partner Pro (paid).';
  }
  if (stepId === 'fundraising' && clientTypeId === 'founder' && intentId === 'portal') {
    return 'Founders Portal, get matched to 7,500+ UK investors with AI thesis-matching.';
  }
  if (stepId === 'checkout' && clientTypeId === 'founder' && intentId === 'explore') {
    return 'View anonymised pricing, no signup required.';
  }
  return STEP_TOOLTIPS_BASE[stepId] || '';
}
window.getStepTooltip = getStepTooltip;
window.STEP_TOOLTIPS_BASE = STEP_TOOLTIPS_BASE;

// Back-compat: a sane default array for code paths that haven't been threaded
// through `stepsForClient` yet (and for the initial render before client-type
// is known). Mirrors the founder/investor/no-client flow.
const BUILD_STEPS = stepsForClient(null);
window.BUILD_STEPS = BUILD_STEPS;
window.STEP_CATALOG = STEP_CATALOG;
window.stepsForClient = stepsForClient;

// Per-service tier override: services can declare their own `tiers` array
// (different ids/names, e.g. Dedicated Resources uses Part Time / Full Time).
// Falls back to the global TIERS when not specified.
function tiersFor(service) {
  return (service && Array.isArray(service.tiers) && service.tiers.length) ? service.tiers : window.TIERS;
}
function findTier(service, tierId) {
  return tiersFor(service).find(t => t.id === tierId) || null;
}
Object.assign(window, { tiersFor, findTier });

// Returns the multiplier to apply to discountable line items based on client type + chosen intent.
// Default: 1 (no discount). Agencies: 0.85 own / 0.6 white-label. No discount until an intent is chosen.
//
// Sprint 2, talent-on-white-label rule (handover §5 / agency spec §7.1.1):
// Talent services (dedicated-pt, dedicated-ft) under WHITE-LABEL intent use a
// 0.85 multiplier instead of 0.60. We protect GoGorilla's headcount margins
// and still pass a sensible 15% margin for the agency. Pass serviceId as the
// 2nd arg to apply the override; without serviceId, the legacy intent rate
// continues to apply (used for blanket UI displays like the discount alert).
const TALENT_SERVICE_IDS = new Set(['dedicated-pt', 'dedicated-ft']);
function getAgencyMultiplier(state, serviceId) {
  if (!state || state.clientTypeId !== 'agency') return 1;
  const ct = window.CLIENT_TYPES.find(c => c.id === 'agency');
  const intent = ct?.intents?.find(i => i.id === state.intentId);
  const d = intent?.discount ?? 0;
  // Sprint 2 override: talent under white-label gets 0.85 floor (15% margin),
  // regardless of whether the white-label intent's headline discount is 40%.
  if (serviceId && state.intentId === 'agency-whitelabel' && TALENT_SERVICE_IDS.has(serviceId)) {
    return 0.85;
  }
  return Math.max(0, 1 - d);
}
window.getAgencyMultiplier = getAgencyMultiplier;

/* 2026-05-31 (#120): Agency wholesale pricing - per-region wholesale for
   location-variable items. Core retainers are pod-delivered and stay on the
   single agency multiplier (fallbackMultiplier) until per-region service rates
   are populated from the pricing source (#83). On the white-label path only the
   Overseas Cold Calling add-on is region-variable today (Talent is not offered
   on white-label), so its confirmed per-region rates live here. RoW = by quote
   (null). MarginRow consumes this via the helpers below. */
window.WHOLESALE = {
  regions: ['uk', 'europe', 'north-america', 'apac', 'row'],
  regionLabels: { uk: 'UK', europe: 'Europe', 'north-america': 'North America', apac: 'APAC', row: 'Rest of World' },
  defaultRegion: 'uk',
  services: {},
  addons: {
    'overseas-calling': { uk: 295, europe: 325, 'north-america': 375, apac: 345, row: null },
  },
  fallbackMultiplier: 0.6,
};

window.wholesaleRegion = function (region) {
  var W = window.WHOLESALE;
  if (region && W.regions.indexOf(region) !== -1) return region;
  return W.defaultRegion;
};

window.wholesaleService = function (serviceId, tierId, region, rrp, mult) {
  var W = window.WHOLESALE;
  var r = window.wholesaleRegion(region);
  var svc = W.services[serviceId];
  var cell = (svc && svc[tierId]) ? svc[tierId][r] : undefined;
  if (typeof cell === 'number') return { value: cell, byQuote: false, explicit: true };
  if (cell === null) return { value: null, byQuote: true, explicit: true };
  var m = (typeof mult === 'number') ? mult : W.fallbackMultiplier;
  return { value: Math.round(rrp * m), byQuote: false, explicit: false };
};

window.wholesaleAddon = function (addonId, region, price, mult) {
  var W = window.WHOLESALE;
  var r = window.wholesaleRegion(region);
  var cfg = W.addons[addonId];
  if (cfg && Object.prototype.hasOwnProperty.call(cfg, r)) {
    var v = cfg[r];
    if (v === null) return { value: null, byQuote: true, explicit: true };
    if (typeof v === 'number') return { value: v, byQuote: false, explicit: true };
  }
  var m = (typeof mult === 'number') ? mult : W.fallbackMultiplier;
  if (typeof price !== 'number') return { value: null, byQuote: false, explicit: false };
  return { value: Math.round(price * m), byQuote: false, explicit: false };
};

/* 2026-05-31 (#120): map Overseas Cold Calling destination countries (from the
   add-on's own country picker) to a wholesale region band, so the per-region
   wholesale reflects where the calling actually happens. Countries without a
   confirmed band fall to 'row' (by quote). */
window.OVERSEAS_REGION_MAP = {
  US: 'north-america', CA: 'north-america',
  IE: 'europe', DE: 'europe', FR: 'europe', ES: 'europe', IT: 'europe', NL: 'europe', SE: 'europe', DK: 'europe', CH: 'europe',
  AU: 'apac', NZ: 'apac', SG: 'apac', HK: 'apac', JP: 'apac', IN: 'apac',
  AE: 'row', BR: 'row', MX: 'row',
};
// Given selected destination country codes, return the region to price calling
// at: the costliest priced band among them; 'row' (by quote) if any selected
// market is unpriced. { region, byQuote, regions:[...], hasSelection }.
window.regionForCountries = function (codes) {
  var list = Array.isArray(codes) ? codes : [];
  if (!list.length) return { region: null, byQuote: false, regions: [], hasSelection: false };
  var map = window.OVERSEAS_REGION_MAP || {};
  var bands = list.map(function (c) { return map[c] || 'row'; });
  var uniq = bands.filter(function (b, i) { return bands.indexOf(b) === i; });
  if (uniq.indexOf('row') !== -1) return { region: 'row', byQuote: true, regions: uniq, hasSelection: true };
  var order = ['north-america', 'apac', 'europe', 'uk']; // costliest first
  var pick = order.filter(function (b) { return uniq.indexOf(b) !== -1; })[0] || 'uk';
  return { region: pick, byQuote: false, regions: uniq, hasSelection: true };
};

// Given selected region-band codes (the calling regions the agency picked
// directly), return the region to price calling at: costliest priced band;
// 'row' (by quote) if Rest of World is selected. { region, byQuote, regions,
// hasSelection }.
window.callingRegion = function (regions) {
  var W = window.WHOLESALE;
  var valid = (W && W.regions) ? W.regions : ['uk', 'europe', 'north-america', 'apac', 'row'];
  // Only count recognised region-band codes. Guards against stale selections
  // that still hold individual country codes from before the region picker.
  var list = (Array.isArray(regions) ? regions.filter(Boolean) : []).filter(function (r) { return valid.indexOf(r) !== -1; });
  if (!list.length) return { region: null, byQuote: false, regions: [], hasSelection: false };
  if (list.indexOf('row') !== -1) return { region: 'row', byQuote: true, regions: list, hasSelection: true };
  var order = ['north-america', 'apac', 'europe', 'uk'];
  var pick = order.filter(function (b) { return list.indexOf(b) !== -1; })[0] || list[0];
  return { region: pick, byQuote: false, regions: list, hasSelection: true };
};
window.TALENT_SERVICE_IDS = TALENT_SERVICE_IDS;

// ── SPRINT 1: QUALIFIER QUESTIONS, INDUSTRIES, COHORT SCORING ──────────
// Q1, Q4 + conditional Q1.5 per founder spec §3 / handover §3 sprint 1.
// All three client types share the same qualifier, with conditional rendering
// of Q1.5 (S&DG tier preference) when Q1 indicates B2B + cold-outbound fit.
// ── Founders Step 1, 28-panel conditional question tree ────────────────
// Source of truth: founders-step-1-conditional-tree.md.
// Each question declares a `visibleIf(state)` predicate that the
// QualifierSection consults on every render to decide which panels mount.
// Some panels are multi-select chip groups (Q6) with an optional `exclusive`
// chip-value that clears all other selections when picked (and is itself
// cleared when any non-exclusive chip is picked).
//
// All questions are founders-only, for investor/agency personas the parent
// flow should render its own Step 1 (out of scope for this build).
const QUALIFIER_QUESTIONS = [
  {
    id: 'q1',
    label: 'What does your business do?',
    sub: 'Shapes which channels and services we recommend across your plan.',
    cols: 2,
    foundersOnly: true,
    options: [
      { id: 'dtc',         label: 'DTC / e-commerce',       desc: 'Physical or digital products sold to consumers' },
      { id: 'saas',        label: 'B2B SaaS',               desc: 'Software, platform, technology product' },
      { id: 'bservices',   label: 'B2B services',           desc: 'Agency, consultancy, recruitment, professional services' },
      { id: 'bcservices',  label: 'B2C services',           desc: 'Local, coaching, courses, healthcare, fitness, hospitality' },
      { id: 'both',        label: 'Both B2B and B2C',       desc: 'Hybrid revenue across both segments' },
      { id: 'mktplace',    label: 'Marketplace or platform', desc: 'Two-sided platform connecting supply and demand' },
    ],
    visibleIf: () => true,
  },
  // ── DTC follow-up chain ──
  {
    id: 'q1a', branch: 'dtc',
    label: 'What kind of products do you sell?',
    sub: 'Sharpens the mix of paid ads, social, content, and email we recommend across your plan.',
    cols: 2, foundersOnly: true,
    options: [
      { id: 'apparel',        label: 'Apparel & fashion' },
      { id: 'beauty',         label: 'Beauty & cosmetics' },
      { id: 'food',           label: 'Food & supplements' },
      { id: 'sportswellness', label: 'Sports & wellness' },
      { id: 'lifestyle',      label: 'Home & lifestyle' },
      { id: 'electronics',    label: 'Electronics & tech' },
      { id: 'jewellery',      label: 'Jewellery & accessories' },
      { id: 'other',          label: 'Other / multiple categories', desc: 'We tailor at kick-off' },
    ],
    visibleIf: (s) => s.q1 === 'dtc',
  },
  {
    id: 'q1aDtcAov',
    label: "What's your typical Average Order Value?",
    cols: 2, foundersOnly: true,
    options: [
      { id: 'sub50',    label: 'Under £50' },
      { id: '50-100',   label: '£50 to £100' },
      { id: '100-250',  label: '£100 to £250' },
      { id: '250-500',  label: '£250 to £500' },
      { id: '500-750',  label: '£500 to £750' },
      { id: '750plus',  label: '£750+',        desc: 'Recalibrated at kick-off' },
    ],
    visibleIf: (s) => s.q1 === 'dtc' && !!s.q1a,
  },
  {
    id: 'q1aDtcMargin',
    label: "What's your gross margin per order?",
    info: 'Revenue minus your direct production or fulfilment costs, as a percentage of revenue. Example: if you sell at £100 and it costs £40 to produce, your gross margin is 60%.',
    cols: 2, foundersOnly: true,
    options: [
      { id: 'sub20',   label: 'Under 20%' },
      { id: '20-40',   label: '20-40%' },
      { id: '40-60',   label: '40-60%' },
      { id: '60-80',   label: '60-80%' },
      { id: '80plus',  label: '80%+' },
    ],
    visibleIf: (s) => s.q1 === 'dtc' && !!s.q1aDtcAov,
  },
  {
    id: 'q1aDtcRepeat',
    label: 'How often do customers buy again?',
    cols: 2, foundersOnly: true,
    options: [
      { id: 'onetime',      label: 'One-time only' },
      { id: 'biannual',     label: 'Twice a year' },
      { id: 'quarterly',    label: 'Quarterly' },
      { id: 'monthly',      label: 'Monthly' },
      { id: 'subscription', label: 'Subscription' },
    ],
    visibleIf: (s) => s.q1 === 'dtc' && !!s.q1aDtcMargin,
  },
  // ── SaaS follow-up chain ──
  {
    id: 'q1a', branch: 'saas',
    label: "What's your sales motion?",
    sub: 'Tells us whether to lead with outbound or inbound across your plan.',
    cols: 3, foundersOnly: true,
    options: [
      { id: 'sales-led',   label: 'Sales-led',  desc: 'Outbound + AE-driven' },
      { id: 'product-led', label: 'Product-led', desc: 'Self-serve sign-up + free trial' },
      { id: 'hybrid',      label: 'Hybrid',      desc: 'PLG with sales-assisted close' },
    ],
    visibleIf: (s) => s.q1 === 'saas',
  },
  {
    id: 'saasCycle',
    label: 'How long is your typical sales cycle?',
    info: 'Shapes whether we lead with velocity outbound (short cycles) or ABM-style nurture (longer cycles).',
    cols: 2, foundersOnly: true,
    options: [
      { id: 'sub30',   label: 'Under 30 days',  desc: 'Velocity outbound, fast iteration' },
      { id: '30-90',   label: '30-90 days',     desc: 'Standard B2B SaaS cycle' },
      { id: '90-180',  label: '90-180 days',    desc: 'Mid-market evaluation cycle' },
      { id: '180plus', label: '180+ days',      desc: 'Enterprise / ABM territory' },
    ],
    visibleIf: (s) => s.q1 === 'saas' && !!s.q1a,
  },
  // ── B2B services follow-up chain ──
  {
    id: 'q1a', branch: 'bservices',
    label: 'How do you typically charge?',
    cols: 3, foundersOnly: true,
    options: [
      { id: 'project',  label: 'Project-based', desc: 'One-off or hourly engagements' },
      { id: 'retainer', label: 'Retainer',      desc: 'Recurring monthly fees or AUM-based' },
      { id: 'mixed',    label: 'Mixed',         desc: 'Both project and retainer, or hourly plus retainer' },
    ],
    visibleIf: (s) => s.q1 === 'bservices',
  },
  {
    id: 'bservicesCat',
    label: 'What kind of B2B service do you provide?',
    cols: 2, foundersOnly: true,
    options: [
      { id: 'legal',      label: 'Legal / accounting / compliance', desc: 'High-intent search + ABM' },
      { id: 'consulting', label: 'Consulting / advisory',           desc: 'LinkedIn + thought leadership' },
      { id: 'agency',     label: 'Creative / marketing agency',     desc: 'Portfolio + social + outbound' },
      { id: 'tech',       label: 'Tech services / IT',              desc: 'LinkedIn + Google + content' },
      { id: 'financial',  label: 'Financial / wealth services',     desc: 'Trust-led nurture + ABM' },
      { id: 'other',      label: 'Other / multiple categories',     desc: 'We tailor at kick-off' },
    ],
    visibleIf: (s) => s.q1 === 'bservices' && !!s.q1a,
  },
  {
    id: 'bservicesCycle',
    label: 'How long is your typical sales cycle?',
    cols: 3, foundersOnly: true,
    options: [
      { id: 'sub-30d', label: 'Under 30 days', desc: 'Fast-cycle, transactional engagements' },
      { id: '30-90d',  label: '30 to 90 days', desc: 'Standard professional-services cycle' },
      { id: '90dplus', label: '90+ days',      desc: 'Long-form trust-led nurture' },
    ],
    visibleIf: (s) => s.q1 === 'bservices' && !!s.bservicesCat,
  },
  // ── B2C services follow-up chain ──
  {
    id: 'q1a', branch: 'bcservices',
    label: 'Where do customers find you?',
    cols: 2, foundersOnly: true,
    options: [
      { id: 'local',         label: 'Local (city or area)' },
      { id: 'regional',      label: 'Regional' },
      { id: 'national',      label: 'National' },
      { id: 'international', label: 'International' },
    ],
    visibleIf: (s) => s.q1 === 'bcservices',
  },
  {
    id: 'bcservicesCat',
    label: 'What kind of consumer service do you provide?',
    cols: 2, foundersOnly: true,
    options: [
      { id: 'home',         label: 'Home services',          desc: 'Plumbers, electricians, cleaners, trades' },
      { id: 'wellness',     label: 'Wellness / fitness',     desc: 'Gyms, salons, therapists, coaches' },
      { id: 'education',    label: 'Education / tutoring',   desc: 'Schools, tutors, training providers' },
      { id: 'hospitality',  label: 'Hospitality / events',   desc: 'Restaurants, venues, event planners' },
      { id: 'professional', label: 'Professional services',  desc: 'Photography, consulting, freelance' },
      { id: 'other',        label: 'Other / multiple',       desc: 'We tailor at kick-off' },
    ],
    visibleIf: (s) => s.q1 === 'bcservices' && !!s.q1a,
  },
  // ── Both follow-up ──
  {
    id: 'q1a', branch: 'both',
    label: 'Where does most of your annual recurring revenue (ARR) come from?',
    cols: 3, foundersOnly: true,
    options: [
      { id: 'mostly-b2b', label: 'Mostly B2B' },
      { id: 'mostly-b2c', label: 'Mostly B2C' },
      { id: 'split',      label: 'Roughly split' },
    ],
    visibleIf: (s) => s.q1 === 'both',
  },
  // ── Marketplace follow-up ──
  {
    id: 'q1a', branch: 'mktplace',
    label: 'Which side do you need to grow?',
    cols: 3, foundersOnly: true,
    options: [
      { id: 'supply',     label: 'Supply-side', desc: 'Acquire sellers' },
      { id: 'demand',     label: 'Demand-side', desc: 'Acquire buyers' },
      { id: 'both-sides', label: 'Both sides',  desc: 'Blended plan' },
    ],
    visibleIf: (s) => s.q1 === 'mktplace',
  },
  // ── Common chain, q1b, q1c, q2, q2b, q3, trading, funding, q4, q5, q5b, urgency, q6 ──
  {
    id: 'q1b',
    label: 'Who do you primarily sell to?',
    sub: 'Shapes channel choice, copy, and which tier we recommend.',
    cols: 2, foundersOnly: true,
    options: [
      { id: 'midmarket',   label: 'Mid-market & enterprise',    desc: 'Companies with 250+ staff or significant ACV' },
      { id: 'smb',         label: 'SMB / small businesses',     desc: 'Under 250 staff, owner-operated or small teams' },
      { id: 'consumer',    label: 'Consumers (D2C)',            desc: 'End consumers buying for personal use' },
      { id: 'mixed',       label: 'Mixed audiences',            desc: 'Multiple segments across business and consumer' },
      { id: 'government',  label: 'Government / public sector', desc: 'Procurement-driven, RFP-led sales' },
    ],
    visibleIf: (s) => window.isQ1aChainComplete && window.isQ1aChainComplete(s),
  },
  {
    id: 'q1c',
    label: 'How specialised is your buyer profile?',
    cols: 2, foundersOnly: true,
    options: [
      { id: 'broad',       label: 'Broad, anyone with a budget could buy',  desc: 'Cross-industry, horizontal, or volume-driven' },
      { id: 'focused',     label: 'Focused, a clear vertical or function',  desc: 'e.g. CFOs at SaaS companies, marketing teams at DTC brands' },
      { id: 'niche',       label: 'Niche, small set of specific buyers',    desc: 'e.g. heads of clinical trials at biotechs, port operators' },
      { id: 'ultra-niche', label: 'Ultra-niche, under 500 prospects globally', desc: 'e.g. CIOs at top-100 hedge funds, CTOs at top-50 cybersecurity' },
      { id: 'unsure',      label: 'Still figuring this out', desc: 'Pre-revenue or pre-PMF; pick a slot above when you have one.' },
    ],
    visibleIf: (s) => !!s.q1b,
  },
  {
    id: 'q2',
    label: "What's your typical ACV (annual contract value)?",
    sub: 'Drives default tier and add-on recommendations.',
    cols: 2, foundersOnly: true,
    options: [
      { id: 'free',      label: 'Free / freemium' },
      { id: 'sub1k',     label: 'Under £1,000' },
      { id: '1k-5k',     label: '£1,000 to £5,000' },
      { id: '5k-10k',    label: '£5,000 to £10,000' },
      { id: '10k-100k',  label: '£10,000 to £100,000' },
      { id: '100kplus',  label: '£100,000+' },
    ],
    visibleIf: (s) => !!s.q1c && window.needsQ2 && window.needsQ2(s),
  },
  {
    id: 'q2b',
    label: "What's the average lifetime value of each customer?",
    info: 'Roughly: Average Order Value x number of repeat purchases x how many years they stay. If unsure, estimate. We will refine at proposal stage.',
    cols: 2, foundersOnly: true,
    options: [
      { id: 'sub500',   label: 'Under £500' },
      { id: '500-2k',   label: '£500 to £2,000' },
      { id: '2k-10k',   label: '£2,000 to £10,000' },
      { id: '10k-50k',  label: '£10,000 to £50,000' },
      { id: '50kplus',  label: '£50,000+' },
      { id: 'unknown',  label: "I'm not sure" },
    ],
    visibleIf: (s) => (window.needsQ2 && window.needsQ2(s) ? !!s.q2 : !!s.q1c),
  },
  {
    id: 'q3',
    label: "What's your current annual recurring revenue (ARR)?",
    sub: 'Drives the default tier across every service we recommend.',
    cols: 2, foundersOnly: true,
    options: [
      { id: 'prerevenue', label: 'Pre-revenue or under £100k' },
      { id: '100k-1m',    label: '£100k to £1M' },
      { id: '1m-5m',      label: '£1M to £5M' },
      { id: '5m-25m',     label: '£5M to £25M' },
      { id: '25mplus',    label: '£25M+' },
      { id: 'noanswer',   label: "I'd rather not say" },
    ],
    visibleIf: (s) => !!s.q2b,
  },
  {
    id: 'tradingLength',
    label: 'How long has your business been operating?',
    info: 'Affects our recommended commitment length, particularly for Paid Advertising and Sales & Demand Generation.',
    cols: 2, foundersOnly: true,
    options: [
      { id: 'under6m', label: 'Less than 6 months' },
      { id: '6-12m',   label: '6 to 12 months' },
      { id: '1-3y',    label: '1 to 3 years' },
      { id: '3-7y',    label: '3 to 7 years' },
      { id: '7yplus',  label: '7+ years' },
    ],
    visibleIf: (s) => !!s.q3,
  },
  {
    id: 'fundBacked',
    label: 'Are you investor-backed?',
    info: 'Helps us frame the proposal around your funding stage and pull relevant case studies.',
    cols: 3, foundersOnly: true,
    options: [
      { id: 'yes',     label: 'Yes',                        desc: 'We have raised from angels, VCs, or other investors' },
      { id: 'not-yet', label: 'Not yet, planning to raise', desc: 'Pre-raise, actively preparing or in conversations' },
      { id: 'no',      label: 'No, bootstrapped or self-funded', desc: 'No external investors and not planning to raise right now' },
    ],
    visibleIf: (s) => !!s.tradingLength,
  },
  {
    id: 'fundRaised',
    label: 'How much have you raised?',
    cols: 2, foundersOnly: true,
    options: [
      { id: 'sub100k',   label: 'Under £100k',  desc: 'Pre-seed or friends-and-family' },
      { id: '100k-500k', label: '£100k to £500k', desc: 'Angel round or small seed' },
      { id: '500k-2m',   label: '£500k to £2m',  desc: 'Seed territory' },
      { id: '2m-10m',    label: '£2m to £10m',   desc: 'Series A or large seed' },
      { id: '10mplus',   label: '£10m+',         desc: 'Series B and beyond' },
    ],
    visibleIf: (s) => s.fundBacked === 'yes',
  },
  {
    id: 'fundInvestorCount',
    label: 'How many investors are on your cap table?',
    cols: 2, foundersOnly: true,
    options: [
      { id: '1-5',     label: '1 to 5' },
      { id: '6-15',    label: '6 to 15' },
      { id: '16-50',   label: '16 to 50' },
      { id: '50plus',  label: '50+' },
    ],
    visibleIf: (s) => s.fundBacked === 'yes' && !!s.fundRaised,
  },
  {
    id: 'q4',
    label: "What's your monthly budget for our team's work?",
    info: 'This sets your default tier across every service. Excludes ad spend, which goes directly to Meta and Google. You can adjust per service in the next step.',
    cols: 2, foundersOnly: true,
    options: [
      { id: 'sub500',     label: 'Under £500 / mo' },
      { id: '500-2.5k',   label: '£500 to £2,500 / mo' },
      { id: '2.5k-7.5k',  label: '£2,500 to £7,500 / mo' },
      { id: '7.5k-20k',   label: '£7,500 to £20,000 / mo' },
      { id: '20k-50k',    label: '£20,000 to £50,000 / mo' },
      { id: '50kplus',    label: '£50,000+ or custom' },
      { id: 'notsure',    label: "I'm not sure yet" },
    ],
    visibleIf: (s) => (s.fundBacked && s.fundBacked !== 'yes') || (s.fundBacked === 'yes' && !!s.fundInvestorCount),
  },
  {
    id: 'q5',
    label: "What's your current outbound setup?",
    cols: 2, foundersOnly: true,
    options: [
      { id: 'none',         label: 'No outbound yet' },
      { id: 'adhoc',        label: 'Founder / team doing it ad-hoc' },
      { id: 'team-small',   label: '1-3 SDRs in-house' },
      { id: 'team-large',   label: '4 to 10 SDRs in-house' },
      { id: 'team-xlarge',  label: '10+ SDRs in-house' },
    ],
    visibleIf: (s) => !!s.q4 && window.isB2BPath && window.isB2BPath(s),
  },
  {
    id: 'q5b',
    label: 'Do your buyers leave visible buying signals?',
    info: 'Buying signals are public events that suggest a prospect is in-market: funding announcements, hiring spikes, tech-stack changes, and similar.',
    cols: 2, foundersOnly: true,
    options: [
      { id: 'yes',     label: "Yes, clear signals when they're buying", desc: 'Funding rounds, hiring waves, tech-stack changes' },
      { id: 'some',    label: 'Some, occasional indicators',            desc: "A few signals fire (hiring spikes maybe) but most buyers don't telegraph intent" },
      { id: 'no',      label: "No, buyers don't telegraph intent online", desc: 'Legacy industries, manufacturing, traditional retail' },
      { id: 'unknown', label: 'Not sure' },
    ],
    visibleIf: (s) => !!s.q5,
  },
  {
    id: 'urgency',
    label: 'When would you want to be live, if you decide to progress?',
    info: 'Affects your recommended start pace and commitment length. Longer commitments unlock the largest multi-service discounts.',
    cols: 2, foundersOnly: true,
    options: [
      { id: 'now',         label: 'This week',        desc: 'We move fast. We can onboard you this week.' },
      { id: '30d',         label: 'Within 30 days',   desc: 'Active buying window' },
      { id: '90d',         label: 'Within 3 months',  desc: 'Planning ahead, not blocked' },
      { id: 'q3plus',      label: 'Within 6 months',  desc: 'Planning ahead, broader research window' },
      { id: 'exploring',   label: 'Just exploring',   desc: 'No timeline pressure yet' },
    ],
    visibleIf: (s) => (window.isB2BPath && window.isB2BPath(s) ? !!s.q5b : !!s.q4),
  },
  {
    id: 'priorActivities',
    label: 'Which of these are you already doing today?',
    info: 'Multi-select. Tells us how much groundwork is already in place so we do not recommend things you are already covered for.',
    cols: 2, foundersOnly: true,
    multi: true,
    exclusive: 'none',
    options: [
      { id: 'sales',        label: 'Cold sales / outbound' },
      { id: 'paid',         label: 'Paid advertising' },
      { id: 'email',        label: 'Email marketing' },
      { id: 'content',      label: 'Content creation' },
      { id: 'smm',          label: 'Social media management' },
      { id: '3d',           label: '3D animation / video production' },
      { id: 'talent',       label: 'Hiring specialists or contractors' },
      { id: 'fundraising',  label: 'Fundraising or planning an exit' },
      { id: 'portal-active', label: 'Already on Founders Portal' },
      { id: 'none',         label: 'Starting fresh - none of the above' },
    ],
    visibleIf: (s) => !!s.urgency,
  },
];
window.QUALIFIER_QUESTIONS = QUALIFIER_QUESTIONS;

// ── Agency qualifier questions ───────────────────────────────────────────────
// Three-scenario flow: Resell (A), Grow own agency (B), Strategic Partner (C).
// All questions use visibleIf to route based on aq_scenario selection.
// No foundersOnly flag — these are exclusively for the agency persona.
// Text-input questions (client name, website, brand assets) are deferred to
// the proposal generator feature and are not included here.
// 2026-05-25: Added -- Batch 10. Updated -- Batch 12. Updated -- Batch 13.
const AGENCY_QUALIFIER_QUESTIONS = [

  // ── Q1: Routing ────────────────────────────────────────────────────────────
  {
    id: 'aq_scenario',
    label: 'How do you want to work with GoGorilla?',
    sub: 'Your answer determines which questions we show you next.',
    cols: 2,
    options: [
      { id: 'resell',  label: 'Resell to my clients',  desc: 'White-label our services under your brand: wholesale pricing and margin support.' },
      { id: 'grow',    label: 'Grow my own agency',    desc: 'Use GoGorilla to scale your agency: new clients, better clients, more capacity.' },
    ],
    visibleIf: (s) => !s.aq_scenario,  // Batch 13: auto-derived from intentId; shown only as fallback when not yet derived
  },

  // ── Scenario A: Reseller / White-Label ─────────────────────────────────────
  {
    id: 'aq_returning',
    label: 'Have you white-labelled with us before?',
    sub: 'Returning partners skip straight to onboarding questions.',
    cols: 2,
    options: [
      { id: 'returning',  label: 'Yes, returning partner' },
      { id: 'first_time', label: 'No, first time' },
    ],
    visibleIf: (s) => s.aq_scenario === 'resell',
  },
  {
    id: 'aq_type_a',
    label: 'Are you a team or a freelancer?',
    cols: 2,
    options: [
      { id: 'team',       label: 'Team / agency' },
      { id: 'freelancer', label: 'Freelancer / solo' },
    ],
    visibleIf: (s) => s.aq_scenario === 'resell' && s.aq_returning === 'first_time',
  },
  {
    id: 'aq_cat_a',
    label: 'What kind of agency are you?',
    cols: 2,
    options: [
      { id: 'full_service', label: 'Full-service' },
      { id: 'performance',  label: 'Performance / growth' },
      { id: 'creative',     label: 'Creative' },
      { id: 'specialist',   label: 'Specialist (PR, SEO, etc.)' },
      { id: 'ops',          label: 'Operations / outsourcing' },
      { id: 'other',        label: 'Other' },
    ],
    visibleIf: (s) => s.aq_scenario === 'resell' && s.aq_returning === 'first_time' && !!s.aq_type_a,
  },
  {
    id: 'aq_revenue_a',
    label: "What’s your agency’s monthly revenue?",
    sub: 'Confidential. Helps us recommend the right plan.',
    cols: 2,
    options: [
      { id: 'sub5k',    label: 'Under £5k' },
      { id: '5k_20k',   label: '£5k – £20k' },
      { id: '20k_50k',  label: '£20k – £50k' },
      { id: '50k_100k', label: '£50k – £100k' },
      { id: '100kplus', label: '£100k+' },
    ],
    visibleIf: (s) => s.aq_scenario === 'resell' && s.aq_returning === 'first_time' && !!s.aq_cat_a,
  },
  {
    id: 'aq_clients_a',
    label: 'How many active clients do you have today?',
    cols: 2,
    options: [
      { id: '1_3',   label: '1 – 3' },
      { id: '4_10',  label: '4 – 10' },
      { id: '11_25', label: '11 – 25' },
      { id: '25plus', label: '25+' },
    ],
    visibleIf: (s) => s.aq_scenario === 'resell' && s.aq_returning === 'first_time' && !!s.aq_revenue_a,
  },
  {
    id: 'aq_retainer_a',
    label: "What’s your typical client retainer or project value?",
    cols: 2,
    options: [
      { id: 'sub500',    label: 'Under £500/mo' },
      { id: '500_1500',  label: '£500 – £1,500/mo' },
      { id: '1500_5000', label: '£1,500 – £5,000/mo' },
      { id: '5000plus',  label: '£5,000+/mo' },
    ],
    visibleIf: (s) => s.aq_scenario === 'resell' && s.aq_returning === 'first_time' && !!s.aq_clients_a,
  },
  // 2026-05-30: 'Is this the first client...' (aq_first_client_a) removed. Redundant:
  // step 1 already asks if the agency has a client ready, and 'No' cannot proceed.
  // aq_client_status_a now chains off aq_retainer_a / returning instead.
  {
    id: 'aq_client_status_a',
    label: 'Is this for a prospective or existing client?',
    cols: 2,
    options: [
      { id: 'prospect', label: 'A prospect I am pitching' },
      { id: 'existing', label: 'An existing client moving across' },
    ],
    visibleIf: (s) => s.aq_scenario === 'resell' && (s.aq_returning === 'returning' || !!s.aq_retainer_a),
  },
  {
    id: 'aq_goal_a',
    label: 'What is your primary goal for partnering with us?',
    cols: 2,
    options: [
      { id: 'revenue',  label: 'Generate more revenue' },
      { id: 'fill_gap', label: 'Fill a service gap' },
      { id: 'scale',    label: 'Scale without hiring' },
      { id: 'wl_arm',   label: 'Add a white-label arm' },
      { id: 'other',    label: 'Other' },
    ],
    visibleIf: (s) => s.aq_scenario === 'resell' && !!s.aq_client_status_a,
  },
  {
    id: 'aq_start_a',
    label: 'When are you looking to get started?',
    cols: 2,
    options: [
      { id: 'now',       label: 'This week',       desc: 'We move fast. Onboarding starts straight away.' },
      { id: '30d',       label: 'Within 30 days',  desc: 'Active buying window' },
      { id: '90d',       label: 'Within 3 months', desc: 'Planning ahead' },
      { id: 'exploring', label: 'Just exploring',  desc: 'No timeline pressure yet' },
    ],
    visibleIf: (s) => s.aq_scenario === 'resell' && !!s.aq_goal_a,
  },

  // ── Scenario B: Own Growth ──────────────────────────────────────────────────
  {
    id: 'aq_type_b',
    label: 'Are you a team or a freelancer?',
    cols: 2,
    options: [
      { id: 'team',       label: 'Team / agency' },
      { id: 'freelancer', label: 'Freelancer / solo' },
    ],
    visibleIf: (s) => s.aq_scenario === 'grow',
  },
  {
    id: 'aq_cat_b',
    label: 'What kind of agency are you?',
    cols: 2,
    options: [
      { id: 'full_service', label: 'Full-service' },
      { id: 'performance',  label: 'Performance / growth' },
      { id: 'creative',     label: 'Creative' },
      { id: 'specialist',   label: 'Specialist (PR, SEO, etc.)' },
      { id: 'ops',          label: 'Operations / outsourcing' },
      { id: 'other',        label: 'Other' },
    ],
    visibleIf: (s) => s.aq_scenario === 'grow' && !!s.aq_type_b,
  },
  {
    id: 'aq_services_offer',
    label: 'Which services do you currently offer clients?',
    sub: 'Select all that apply.',
    cols: 2,
    multi: true,
    exclusive: 'none',
    options: [
      { id: 'paid_ads',  label: 'Paid Advertising',    desc: 'Meta, Google, TikTok, etc.' },
      { id: 'outbound',  label: 'Outbound & SDG',       desc: 'Cold email, cold calling, SDRs' },
      { id: 'email_crm', label: 'Email & CRM',          desc: 'Campaigns, automation, flows' },
      { id: 'content',   label: 'Content & Creative',   desc: 'Video, copy, design, social' },
      { id: 'web_tech',  label: 'Web & Technology',     desc: 'Dev, SEO, analytics, platforms' },
      { id: 'strategy',  label: 'Strategy & Advisory',  desc: 'Consulting, go-to-market, ops' },
      { id: 'none',      label: 'None yet',             desc: 'Just getting started' },
    ],
    visibleIf: (s) => s.aq_scenario === 'grow' && !!s.aq_cat_b,
  },
  {
    id: 'aq_client_types_b',
    label: 'What kinds of clients do you primarily serve?',
    sub: 'Select all that apply.',
    cols: 2,
    multi: true,
    exclusive: 'mixed',
    options: [
      { id: 'ecom_dtc',     label: 'E-commerce / DTC' },
      { id: 'b2b_saas',     label: 'B2B SaaS' },
      { id: 'b2b_services', label: 'B2B Services' },
      { id: 'prof_svcs',    label: 'Professional Services' },
      { id: 'local_smb',    label: 'Local / SMB' },
      { id: 'enterprise',   label: 'Enterprise' },
      { id: 'mixed',        label: 'Mixed verticals' },
      { id: 'other',        label: 'Other' },
    ],
    visibleIf: (s) => s.aq_scenario === 'grow' && Array.isArray(s.aq_services_offer) && s.aq_services_offer.length > 0,
  },
  {
    id: 'aq_team_size_b',
    label: 'How many people work at your agency?',
    cols: 2,
    options: [
      { id: 'solo',   label: 'Just me' },
      { id: '2_5',    label: '2 – 5' },
      { id: '6_15',   label: '6 – 15' },
      { id: '16_50',  label: '16 – 50' },
      { id: '50plus', label: '50+' },
    ],
    visibleIf: (s) => s.aq_scenario === 'grow' && Array.isArray(s.aq_client_types_b) && s.aq_client_types_b.length > 0,
  },
  {
    id: 'aq_revenue_b',
    label: "What’s your agency’s monthly revenue?",
    sub: 'Confidential. Helps us recommend the right plan.',
    cols: 2,
    options: [
      { id: 'sub5k',    label: 'Under £5k' },
      { id: '5k_20k',   label: '£5k – £20k' },
      { id: '20k_50k',  label: '£20k – £50k' },
      { id: '50k_100k', label: '£50k – £100k' },
      { id: '100kplus', label: '£100k+' },
    ],
    visibleIf: (s) => s.aq_scenario === 'grow' && !!s.aq_team_size_b,
  },
  {
    id: 'aq_winning_b',
    label: 'How are you currently winning new clients?',
    sub: 'Select all that apply. Deep sub-questions deferred to discovery call.',
    cols: 2,
    multi: true,
    exclusive: 'none',
    options: [
      { id: 'inbound',   label: 'Inbound / referrals' },
      { id: 'paid_ads',  label: 'Paid ads' },
      { id: 'outbound',  label: 'Cold outbound' },
      { id: 'events',    label: 'Events / networking' },
      { id: 'partners',  label: 'Partnerships' },
      { id: 'none',      label: 'Not consistently' },
    ],
    visibleIf: (s) => s.aq_scenario === 'grow' && !!s.aq_revenue_b,
  },
  {
    id: 'aq_challenges_b',
    label: 'What are the biggest challenges you are solving right now?',
    sub: 'Select all that apply.',
    cols: 2,
    multi: true,
    exclusive: 'none',
    options: [
      { id: 'leads',     label: 'Lead generation' },
      { id: 'hiring',    label: 'Hiring / capacity' },
      { id: 'margins',   label: 'Profit margins' },
      { id: 'retention', label: 'Client retention' },
      { id: 'scale',     label: 'Scalability' },
    ],
    visibleIf: (s) => s.aq_scenario === 'grow' && Array.isArray(s.aq_winning_b) && s.aq_winning_b.length > 0,
  },
  {
    id: 'aq_hiring_b',
    label: 'Are you currently hiring?',
    cols: 2,
    options: [
      { id: 'yes', label: 'Yes' },
      { id: 'no',  label: 'No' },
    ],
    visibleIf: (s) => s.aq_scenario === 'grow' && Array.isArray(s.aq_challenges_b) && s.aq_challenges_b.length > 0,
  },
  {
    id: 'aq_hiring_roles_b',
    label: 'Which roles are you hiring for?',
    sub: 'Select all that apply.',
    cols: 2,
    multi: true,
    exclusive: 'none',
    options: [
      { id: 'sales_bdr',  label: 'Sales / BDR' },
      { id: 'creative',   label: 'Creative & design' },
      { id: 'paid_media', label: 'Paid media' },
      { id: 'delivery',   label: 'Delivery & ops' },
      { id: 'other',      label: 'Other' },
    ],
    visibleIf: (s) => s.aq_scenario === 'grow' && s.aq_hiring_b === 'yes',
  },
  {
    id: 'aq_goal_b',
    label: 'What is your primary goal for partnering with us?',
    cols: 2,
    options: [
      { id: 'revenue',  label: 'Generate more revenue' },
      { id: 'fill_gap', label: 'Fill a service gap' },
      { id: 'scale',    label: 'Scale without hiring' },
      { id: 'margins',  label: 'Improve margins' },
      { id: 'other',    label: 'Other' },
    ],
    visibleIf: (s) => s.aq_scenario === 'grow' && !!s.aq_hiring_b,
  },
  {
    id: 'aq_start_b',
    label: 'When are you looking to get started?',
    cols: 2,
    options: [
      { id: 'now',       label: 'This week',       desc: 'We move fast. Onboarding starts straight away.' },
      { id: '30d',       label: 'Within 30 days',  desc: 'Active buying window' },
      { id: '90d',       label: 'Within 3 months', desc: 'Planning ahead' },
      { id: 'exploring', label: 'Just exploring',  desc: 'No timeline pressure yet' },
    ],
    visibleIf: (s) => s.aq_scenario === 'grow' && !!s.aq_goal_b,
  },
];
window.AGENCY_QUALIFIER_QUESTIONS = AGENCY_QUALIFIER_QUESTIONS;

// Returns true when the agency qualifier is considered complete for the
// selected scenario. Used by the Schedule a Call step to reveal the plan
// summary card (same gate as window.qualifierComplete for founders).
// Checks the final question of each scenario path — if answered, the user
// has worked through the whole flow.
function agencyQualifierComplete(qualifier, intentId) {
  if (!qualifier) return false;
  const s = qualifier.aq_scenario
    || (intentId === 'agency-whitelabel' ? 'resell'
        : intentId === 'agency-own' ? 'grow' : null);
  if (s === 'resell')  return !!qualifier.aq_start_a;
  if (s === 'grow')    return !!qualifier.aq_start_b;
  return false;
}
window.agencyQualifierComplete = agencyQualifierComplete;

// Industry list for white-label agency Q0 (capture client industry so we can
// pre-fill Q1 ICP). Curated to common verticals; "other" is the safe escape.
const INDUSTRIES = [
  { id: 'saas',         label: 'SaaS / Tech' },
  { id: 'enterprise-sw',label: 'Enterprise software' },
  { id: 'ecommerce',    label: 'E-commerce / DTC' },
  { id: 'professional', label: 'Professional services' },
  { id: 'marketplace',  label: 'Marketplace / Platform' },
  { id: 'healthcare',   label: 'Healthcare' },
  { id: 'financial',    label: 'Financial services' },
  { id: 'education',    label: 'Education' },
  { id: 'media',        label: 'Media / Publishing' },
  { id: 'manufacturing',label: 'Manufacturing / Industrial' },
  { id: 'real-estate',  label: 'Real estate / PropTech' },
  { id: 'other',        label: 'Other' },
];
window.INDUSTRIES = INDUSTRIES;

// Industry → Q1 ICP pre-fill map (sprint 1, agency white-label only).
const INDUSTRY_TO_Q1_ICP = {
  saas:          'b2b-smb',
  'enterprise-sw':'b2b-mid',
  ecommerce:     'dtc',
  professional:  'b2b-smb',
  marketplace:   'marketplace',
  healthcare:    'b2b-mid',
  financial:     'b2b-mid',
  education:     'b2b-smb',
  media:         'b2b-smb',
  manufacturing: 'b2b-mid',
  'real-estate': 'b2b-smb',
  other:         null,
};
window.INDUSTRY_TO_Q1_ICP = INDUSTRY_TO_Q1_ICP;

// Compute warmth score 0-100 from qualifier answers.
// Per founder spec §4 + handover sprint 1.
function computeWarmth(qualifier) {
  // Cohort banner hidden in current UX. Return a coarse score so downstream
  // CRM tagging stays meaningful but no UI depends on it. Unknown values → 0.
  if (!qualifier) return 0;
  let w = 0;
  w += ({ 'saas': 25, 'bservices': 22, 'mktplace': 20, 'both': 18, 'dtc': 16, 'bcservices': 12 }[qualifier.q1] || 0);
  w += ({ '7yplus': 30, '3-7y': 26, '1-3y': 22, '6-12m': 14, 'under6m': 8 }[qualifier.tradingLength] || 0);
  w += ({ 'yes': 25, 'not-yet': 16, 'no': 12 }[qualifier.fundBacked] || 0);
  return Math.min(100, w);
}
window.computeWarmth = computeWarmth;

// Cohort tag A/B/C/D from warmth score. D = bypass to book-a-call.
function computeCohort(warmth) {
  if (warmth >= 75) return 'A';
  if (warmth >= 50) return 'B';
  if (warmth >= 25) return 'C';
  return 'D';
}
window.computeCohort = computeCohort;

// True only if every required qualifier question has been answered. Q1.5 is
// only required when its conditional dependency is met (q1 ∈ B2B set).
// ── Helpers for the 28-panel founders Step 1 tree ────────────────────────
function isQ1aChainComplete(s) {
  if (!s || !s.q1 || !s.q1a) return false;
  switch (s.q1) {
    case 'dtc':       return !!s.q1aDtcAov && !!s.q1aDtcMargin && !!s.q1aDtcRepeat;
    case 'saas':      return !!s.saasCycle;
    case 'bservices': return !!s.bservicesCat && !!s.bservicesCycle;
    case 'bcservices': return !!s.bcservicesCat;
    case 'both':
    case 'mktplace':  return true; // q1a is the only follow-up
    default:          return true;
  }
}
window.isQ1aChainComplete = isQ1aChainComplete;

function needsQ2(s) {
  if (!s) return false;
  const b2b = ['saas', 'bservices', 'mktplace'];
  if (b2b.includes(s.q1)) return true;
  if (s.q1 === 'both' && s.q1a !== 'mostly-b2c') return true;
  return false;
}
window.needsQ2 = needsQ2;

function isB2BPath(s) {
  if (!s) return false;
  return ['saas', 'bservices', 'both', 'mktplace'].includes(s.q1);
}
window.isB2BPath = isB2BPath;

// Counts non-null state in the canonical order list after `level`.
// `priorActivities` is counted as "answered" when its array has at least one entry.
function countDownstreamAnswers(level, s) {
  if (!s) return 0;
  const order = ['q1','q1a','q1b','q1c','q2','q2b','q3','q4','q5','q5b','priorActivities'];
  const idx = order.indexOf(level);
  if (idx < 0) return 0;
  let n = 0;
  for (let i = idx + 1; i < order.length; i++) {
    const k = order[i];
    if (k === 'priorActivities') { if (Array.isArray(s.priorActivities) && s.priorActivities.length > 0) n++; continue; }
    if (s[k] !== null && s[k] !== undefined) n++;
  }
  return n;
}
window.countDownstreamAnswers = countDownstreamAnswers;

function isReadyToAdvance(s, clientTypeId) {
  if (!s) return false;
  // Founders-only flow, for other personas, treat as ready (their Step 1 lives elsewhere).
  const founderLike = !clientTypeId || clientTypeId === 'founder';
  if (!founderLike) return true;
  if (!s.q1) return false;
  if (!isQ1aChainComplete(s)) return false;
  if (!s.q1b || !s.q1c) return false;
  if (needsQ2(s) && !s.q2) return false;
  if (!s.q2b || !s.q3) return false;
  if (!s.tradingLength || !s.fundBacked) return false;
  if (s.fundBacked === 'yes' && (!s.fundRaised || !s.fundInvestorCount)) return false;
  if (!s.q4) return false;
  if (isB2BPath(s) && (!s.q5 || !s.q5b)) return false;
  if (!s.urgency) return false;
  if (!Array.isArray(s.priorActivities) || s.priorActivities.length === 0) return false;
  // Q6 cascade gates per founders-q6-prior-activities-spec.md §13.
  // 'Skip' values count as answered (recording explicit decline).
  const prior = s.priorActivities;
  if (prior.includes('sales')) {
    if (!Array.isArray(s.priorOutboundChannels) || s.priorOutboundChannels.length === 0) return false;
    if (s.priorSalesCycle == null) return false;
  }
  if (prior.includes('paid')) {
    if (!Array.isArray(s.priorPaidPlatforms) || s.priorPaidPlatforms.length === 0) return false;
    if (s.priorPaidBudget == null) return false;
    if (s.priorPaidRoas == null) return false;
  }
  if (prior.includes('email')) {
    if (s.emailSubscriberCount == null) return false;
    if (s.emailCampaignsPerMonth == null) return false;
    if (s.priorEmailPlatform == null) return false;
  }
  if (prior.includes('smm')) {
    if (s.priorSocialFrequency == null) return false;
    if (!Array.isArray(s.priorSocialPlatforms) || s.priorSocialPlatforms.length === 0) return false;
  }
  if (prior.includes('talent')) {
    if (s.hiringTime == null) return false;
  }
  if (prior.includes('fundraising')) {
    if (s.fundraisingStage == null) return false;
    if (s.fundraisingRaiseSize == null) return false;
    if (!Array.isArray(s.fundraisingInvestorTypes) || s.fundraisingInvestorTypes.length === 0) return false;
    const needsTargetVal = ['seriesA', 'late-exit'].includes(s.fundraisingStage);
    if (needsTargetVal && s.fundraisingTargetValuation == null) return false;
    // fundraisingFlags is optional, no gate
  }
  return true;
}
window.isReadyToAdvance = isReadyToAdvance;
const qualifierComplete = isReadyToAdvance;
window.qualifierComplete = qualifierComplete;

// getStepGates, parent step router reads this off the answers.
function getStepGates(qualifier) {
  const q = qualifier || {};
  const ib = q.fundBacked;
  const od = q.tradingLength;
  const step4 = (od === 'under6m' && ib === 'no') ? 'render-with-soft-skip' : 'render';
  const step5 = ib === 'no' ? 'render-collapsed' : 'render';
  let step6CTA = 'Book a call with our team';
  if (ib === 'not-yet') step6CTA = "Book a call, we'll also cover raise-prep";
  else if (ib === 'no') step6CTA = "Book a call, we'll focus on payback-first plays";
  return { step2: 'render', step3: 'render', step4, step5, step6CTA };
}
window.getStepGates = getStepGates;

// ── STEP 1 → STEP 2+ RECOMMENDATION ENGINE V2 ─────────────────────────
// Rewired in v2 to read the 28-panel founders-step-1 qualifier shape
// (q1='dtc'|'saas'|'bservices'|'bcservices'|'both'|'mktplace';
//  q2=ACV bucket; q3=ARR bucket; q4=monthly budget; q5=outbound team;
//  q1c=ICP focus; q2b=LTV bucket; tradingLength; fundBacked; etc.).
//
// This block is the SINGLE source of truth for how Step 1 answers drive
// Step 2+ (service auto-recommendation, tier defaults, add-on visibility,
// channel ranking, multi-service discount, cohort kickoff, commit length).
//
// IMPORTANT: the visible "Recommended" badge on tier cards is currently
// hard-pinned to Grow per task #131. The tier-rec functions below are
// advisory only, they expose context-aware recommendations that future
// UI work can opt into without touching the badge logic.

// Service-id mapping. Spec §17 uses `sdg` and `3d`; the app uses `sales`
// and `motion`. Both forms are accepted on input; canonical output uses
// the app's internal ids so downstream selection code keeps working.
const _SERVICE_ID_ALIASES = { sdg: 'sales', '3d': 'motion' };
function _canonicalServiceId(s) { return _SERVICE_ID_ALIASES[s] || s; }
window._canonicalServiceId = _canonicalServiceId;

// Default tier per service from Step 1 inputs. Reads ACV (q2), ARR (q3),
// budget (q4), and outbound team (q5). For Sales (sdg) specifically, the
// SaaS sales-motion (q1a) gives an extra signal, sales-led + long cycle
// pushes Scale, product-led keeps it lean.
function defaultTierForService(serviceId, qualifier, intent) {
  // DISABLED, qualifier moved to Step 6; show full breadth. Default to Grow. (task #249)
  return 'grow';
  // eslint-disable-next-line no-unreachable
  if (!qualifier) return 'grow';
  const sid = _canonicalServiceId(serviceId);
  const q = qualifier;

  // S&DG (sales): SaaS sales motion gives a strong tier signal
  if (sid === 'sales' && q.q1 === 'saas' && q.q1a) {
    if (q.q1a === 'sales-led' && (q.saasCycle === '90-180' || q.saasCycle === '180plus')) return 'scale';
    if (q.q1a === 'product-led') return 'starter';
    if (q.q1a === 'hybrid') return 'grow';
  }

  // Established outbound team → bump tier on Sales
  if (sid === 'sales' && (q.q5 === 'team-large' || q.q5 === 'team-xlarge')) {
    return q.q3 === '25mplus' ? 'enterprise' : 'scale';
  }

  // ACV (B2B) drives default tier
  if (q.q2 === '100kplus') return 'scale';
  if (q.q2 === '10k-100k') return 'grow';
  if (q.q2 === 'free' || q.q2 === 'sub1k') return 'starter';

  // Monthly budget drives default tier
  if (q.q4 === 'sub500' || q.q4 === '500-2.5k') return 'starter';
  if (q.q4 === '20k-50k' || q.q4 === '50kplus') return 'scale';

  // ARR drives default tier
  if (q.q3 === '25mplus') return 'scale';
  if (q.q3 === '1m-5m' || q.q3 === '5m-25m') return 'grow';

  return 'grow';
}
window.defaultTierForService = defaultTierForService;

// Recommendation engine, returns { strength: 'strong' | 'subtle', reason }
// or null. Reads NEW qualifier keys. Gated on qualifierComplete so the
// "we recommend" copy only fires when we have enough signal to back it.
function computeServiceRecommendation(serviceId, qualifier, intent, clientTypeId) {
  // DISABLED, qualifier moved to Step 6; no qualifier-driven tier recommendations. (task #249)
  return null;
  // eslint-disable-next-line no-unreachable
  if (!qualifier || !window.qualifierComplete?.(qualifier)) return null;
  const sid = _canonicalServiceId(serviceId);
  const q = qualifier;
  const isB2BPath = window.isB2BPath ? window.isB2BPath(q) : ['saas','bservices','both','mktplace'].includes(q.q1);
  const isDTC = q.q1 === 'dtc';
  const isB2CServices = q.q1 === 'bcservices';
  const dealBig = ['10k-100k','100kplus'].includes(q.q2);
  const arrBig = ['1m-5m','5m-25m','25mplus'].includes(q.q3);
  const teamEstablished = ['team-small','team-large','team-xlarge'].includes(q.q5);

  switch (sid) {
    case 'sales': // Sales & Demand Generation (spec id: sdg)
      if (isB2BPath && (dealBig || arrBig)) {
        return { strength: 'strong', reason: 'B2B with mid-to-large deal sizes, outbound delivers fastest payback at your stage.' };
      }
      if (isB2BPath) return { strength: 'subtle', reason: 'Worth considering, B2B outbound is a reliable demand floor.' };
      return null;
    case 'paid-ads':
      if (isDTC) return { strength: 'strong', reason: 'DTC scales fastest through paid social + search at your stage.' };
      if (isB2CServices) return { strength: 'strong', reason: 'Local & B2C services rely on paid search and social to fill the pipeline.' };
      if (isB2BPath && dealBig) return { strength: 'subtle', reason: 'Targeted paid layered on outbound shortens the cycle.' };
      return null;
    case 'email':
      if (isDTC) return { strength: 'strong', reason: 'Repeat-purchase economics make email your highest-leverage owned channel.' };
      if (q.q1 === 'mktplace') return { strength: 'strong', reason: 'Lifecycle email is the cheapest reactivation lever for two-sided marketplaces.' };
      if (isB2BPath && q.q3 !== 'prerevenue') return { strength: 'subtle', reason: 'Lifecycle email lifts pipeline conversion 15-25%.' };
      return null;
    case 'smm':
      if (isDTC && ['apparel','beauty','sportswellness','lifestyle','jewellery'].includes(q.q1a)) {
        return { strength: 'strong', reason: 'Visual product categories compound through organic social.' };
      }
      if (isB2CServices && ['wellness','hospitality'].includes(q.bcservicesCat)) {
        return { strength: 'strong', reason: 'Local & lifestyle service buyers discover through social.' };
      }
      if (isDTC) return { strength: 'subtle', reason: 'Worth a measured starter cadence.' };
      return null;
    case 'content':
      if (q.q1 === 'saas' && (q.saasCycle === '90-180' || q.saasCycle === '180plus')) {
        return { strength: 'strong', reason: 'Long-cycle B2B SaaS, content lowers CAC and nurtures the buying committee.' };
      }
      if (q.q1 === 'bservices' && q.bservicesCat === 'consulting') {
        return { strength: 'strong', reason: 'Consulting buyers convert through thought leadership.' };
      }
      if (arrBig) return { strength: 'subtle', reason: 'Content compounds, invest now to lower CAC over 6-12 months.' };
      return null;
    case 'motion': // 3D / Video Production (spec id: 3d)
      if (isDTC && q.q1a === 'lifestyle' && ['250-500','500plus'].includes(q.q1aDtcAov)) {
        return { strength: 'strong', reason: 'High-AOV lifestyle products convert best with motion + 3D creative.' };
      }
      if (isDTC) return { strength: 'subtle', reason: 'Motion creative outperforms static on Meta + TikTok.' };
      return null;
    case 'dedicated-pt':
    case 'dedicated-ft':
      if (intent === 'agency-whitelabel') {
        return { strength: 'subtle', reason: 'Embedded talent is the cleanest white-label fulfilment.' };
      }
      if (arrBig) return { strength: 'subtle', reason: 'At your scale, dedicated headcount is more efficient than bursty engagements.' };
      return null;
    case 'whitelabel':
      if (intent === 'agency-whitelabel') return { strength: 'strong', reason: 'Required to unlock white-label fulfilment + 40% partner pricing.' };
      return null;
    case 'fundraising':
      if (clientTypeId === 'founder' && (q.fundBacked === 'not-yet' || q.fundBacked === 'yes')) {
        return { strength: 'subtle', reason: 'Aligns with where you are in the raise cycle.' };
      }
      return null;
    case 'investor-portal':
    case 'founders-portal':
      if ((sid === 'investor-portal' && clientTypeId === 'investor') ||
          (sid === 'founders-portal' && clientTypeId === 'founder')) {
        return { strength: 'subtle', reason: 'Foundational, a sensible default for your client type.' };
      }
      return null;
    default:
      return null;
  }
}
window.computeServiceRecommendation = computeServiceRecommendation;

// Channel ranking, reads NEW q1 enum. Sales motion comes from SaaS q1a
// (sales-led/product-led/hybrid) instead of the legacy q15 enum.
function rankedChannelsFor(serviceId, tierId, qualifier) {
  const base = window.channelsForTier ? window.channelsForTier(serviceId, tierId) : [];
  // DISABLED, qualifier moved to Step 6; show base channel order. (task #249)
  return base;
  // eslint-disable-next-line no-unreachable
  if (!qualifier || !qualifier.q1) return base;
  const sid = _canonicalServiceId(serviceId);
  if (sid === 'paid-ads') {
    const order = (() => {
      if (['saas','bservices','mktplace'].includes(qualifier.q1) ||
          (qualifier.q1 === 'both' && qualifier.q1a !== 'mostly-b2c')) {
        return ['google', 'linkedin', 'microsoft', 'meta', 'tiktok', 'pinterest'];
      }
      if (qualifier.q1 === 'dtc') {
        return ['meta', 'tiktok', 'google', 'pinterest', 'linkedin', 'microsoft'];
      }
      if (qualifier.q1 === 'bcservices') {
        return ['google', 'meta', 'tiktok', 'pinterest', 'linkedin', 'microsoft'];
      }
      return base;
    })();
    const allowed = new Set(base);
    return order.filter(c => allowed.has(c)).concat(base.filter(c => !order.includes(c)));
  }
  if (sid === 'sales') {
    const order = (() => {
      if (qualifier.q1 === 'saas' && qualifier.q1a === 'product-led') return ['email'];
      if (qualifier.q1 === 'saas' && qualifier.q1a === 'hybrid') return ['email', 'linkedin'];
      if (qualifier.q1 === 'saas' && qualifier.q1a === 'sales-led') return ['email', 'linkedin', 'phone'];
      return ['email', 'linkedin'];
    })();
    const allowed = new Set(base);
    return order.filter(c => allowed.has(c)).concat(base.filter(c => !order.includes(c)));
  }
  return base;
}
window.rankedChannelsFor = rankedChannelsFor;

// ── Multi-service discount tracker (5-45% cap) ─────────────────────
// Kept on the existing 1:0% / 2:5% / 3:15% / 4:25% / 5:35% / 6+:45%
// schedule rather than the spec's softer 1/2/3/5/10 table, the user has
// the more-generous schedule live and we don't want to silently reduce it.
const MULTI_SERVICE_DISCOUNT_PCT = { 1: 0, 2: 0.05, 3: 0.10, 4: 0.10, 5: 0.10, 6: 0.10 };
window.MULTI_SERVICE_DISCOUNT_PCT = MULTI_SERVICE_DISCOUNT_PCT;
function multiServiceDiscountPct(count) {
  if (!count || count < 1) return 0;
  if (count >= 3) return 0.10;
  if (count === 2) return 0.05;
  return 0;
}
window.multiServiceDiscountPct = multiServiceDiscountPct;
function combinedDiscountCap(state) {
  if (state?.intentId === 'agency-whitelabel') return 0.50;
  if (state?.intentId === 'agency-own') return 0.50;
  return 1;
}
window.combinedDiscountCap = combinedDiscountCap;

// Add-on recommendation overrides for top services (Sales, Paid Ads, Email,
// SMM, Content). Returns a Set of addon ids that render as "recommended" on
// top of any badge already declared in data. Anchored in the v2 qualifier
// shape (q1 enum, q1a sub-followup, q2 ACV, q3 ARR, q5 outbound team, q5b
// signals, priorActivities chips).
function recommendedAddonIds(serviceId, qualifier, intentId) {
  const out = new Set();
  // DISABLED, qualifier moved to Step 6; no qualifier-driven add-on badges. (task #249)
  return out;
  // eslint-disable-next-line no-unreachable
  if (!qualifier) return out;
  const sid = _canonicalServiceId(serviceId);
  const q = qualifier;
  const isB2BPath = window.isB2BPath ? window.isB2BPath(q) : ['saas','bservices','both','mktplace'].includes(q.q1);
  const isDTC = q.q1 === 'dtc';
  const dealBig = ['10k-100k','100kplus'].includes(q.q2);
  const arrEstablished = ['1m-5m','5m-25m','25mplus'].includes(q.q3);
  const prior = Array.isArray(q.priorActivities) ? q.priorActivities : [];

  if (sid === 'sales') {
    // SaaS sales motion drives add-on bundle
    if (q.q1 === 'saas' && q.q1a === 'product-led') out.add('byol');
    if (q.q1 === 'saas' && q.q1a === 'hybrid') { out.add('li-profiles'); }
    if (q.q1 === 'saas' && q.q1a === 'sales-led') { out.add('overseas-calling'); }
    if (dealBig) out.add('premium-sourcing');
    // Spec §19: GorillaSignals on SDG is recommended when q5b === 'yes' (clear signals)
    if (q.q5b === 'yes') out.add('hiring-signals');
    // Per-lead quality: deal-size drives standard vs premium
    // Single lead tier now: always recommend leads-standard (was Standard vs Premium split)
    if (dealBig || ['free','sub1k','1k-5k'].includes(q.q2)) out.add('leads-standard');
  } else if (sid === 'paid-ads') {
    if (isB2BPath) out.add('dm-list');
    if (isDTC) { out.add('meta-retarget'); out.add('google-shop'); }
    // CRO + landing recommended when budget is healthy
    if (q.q4 === '7.5k-20k' || q.q4 === '20k-50k' || q.q4 === '50kplus') { out.add('cro-prog'); out.add('landing'); }
    if (dealBig && isB2BPath) out.add('call-track');
  } else if (sid === 'email') {
    // SMS adds value for DTC + marketplace lifecycle programmes
    if (isDTC || q.q1 === 'mktplace') out.add('sms');
    // Established programmes benefit from custom reporting; bigger budgets warrant vCMO
    if (prior.includes('email')) out.add('additional-reports');
  } else if (sid === 'smm') {
    // UGC sourcing for DTC / B2C only (mirrors isSMMAddonHiddenForICP)
    if (isDTC || q.q1 === 'bcservices') out.add('smm-ugc-rights');
  } else if (sid === 'content') {
    // Long-cycle SaaS or consulting benefits from thought-leadership packages
    if ((q.q1 === 'saas' && (q.saasCycle === '90-180' || q.saasCycle === '180plus')) ||
        (q.q1 === 'bservices' && q.bservicesCat === 'consulting')) {
      out.add('thought-leadership');
    }
  } else if (sid === 'dedicated-ft') {
    // §3.8 / §10, dedicated-ft addons: surface a Recommended badge on any
    // addon scoring ≥ 5 via the Step 1 boost rules.
    if (typeof window.getDrFtAddonScore === 'function') {
      const score = window.getDrFtAddonScore(window.__lastBuildPageState || { qualifier, selections: {} });
      Object.entries(score).forEach(([aid, s]) => { if (s >= 5) out.add(aid); });
    }
  }
  return out;
}
window.recommendedAddonIds = recommendedAddonIds;

// ── Step 1 → Step 2+ HELPERS (spec §17-20) ────────────────────────
//
// All helpers below are PURE and ADVISORY. They do not auto-mutate state.
// Step 2 panels can opt into them at render time. Today the only consumer
// that does is the recommendation badge engine above (computeServiceRec).
// Future work can wire these into SET_CLIENT / SET_QUALIFIER reducers to
// pre-select services / tiers / add-ons; that's intentionally NOT done
// here so the "always Grow recommended" rule (task #131) stays in force.

// §17, service auto-recommendation. Returns array of canonical service ids
// (e.g. 'sales','paid-ads','email','content','smm','motion') that Step 2
// would pre-select for this founder profile.
function getGapRecommendations(qualifier) {
  if (!qualifier || !qualifier.q1) return [];
  const q = qualifier;
  switch (q.q1) {
    case 'dtc': {
      // Lifestyle promoted to home at £250+ AOV (high-ticket furniture/decor)
      const cat = (q.q1a === 'lifestyle' && ['250-500','500plus'].includes(q.q1aDtcAov)) ? 'home' : q.q1a;
      const visualCats = ['apparel','beauty','lifestyle','sportswellness','jewellery','home'];
      const gaps = ['paid-ads', 'email', 'content'];
      if (visualCats.includes(cat)) gaps.splice(2, 0, 'smm'); // splice SMM in before content
      // 3D rec for high-ticket lifestyle/home
      if (cat === 'home' && ['250-500','500plus'].includes(q.q1aDtcAov)) gaps.push('motion');
      return gaps;
    }
    case 'bcservices': {
      const map = {
        home:        ['paid-ads', 'smm', 'email'],
        wellness:    ['paid-ads', 'smm', 'email', 'content'],
        education:   ['paid-ads', 'email', 'content'],
        hospitality: ['paid-ads', 'smm', 'content', 'email'],
        professional:['paid-ads', 'email', 'content'],
      };
      return map[q.bcservicesCat] || ['paid-ads', 'email', 'smm'];
    }
    case 'saas': {
      if (q.q1a === 'product-led') return ['paid-ads', 'email', 'content'];
      if (q.q1a === 'hybrid') return ['sales', 'paid-ads', 'email'];
      if (q.q1a === 'sales-led') {
        if (q.saasCycle === '180plus') return ['sales', 'paid-ads', 'email', 'content'];
        if (q.saasCycle === '90-180' || ['1m-5m','5m-25m','25mplus'].includes(q.q3)) {
          return ['sales', 'paid-ads', 'email'];
        }
        return ['sales', 'paid-ads'];
      }
      return ['sales', 'paid-ads'];
    }
    case 'bservices': {
      if (q.bservicesCat === 'consulting') return ['sales', 'email', 'content'];
      if (q.bservicesCat === 'agency') return ['sales', 'paid-ads', 'email', 'content'];
      return ['sales', 'paid-ads', 'email'];
    }
    case 'mktplace': {
      if (q.q1a === 'supply') return ['sales', 'paid-ads', 'content', 'smm'];
      if (q.q1a === 'demand') return ['paid-ads', 'email', 'content', 'smm'];
      return ['paid-ads', 'email', 'content', 'smm'];
    }
    case 'both':
      // Blended default, refine when dual-track engine ships
      return ['paid-ads', 'email', 'content'];
    default:
      return [];
  }
}
window.getGapRecommendations = getGapRecommendations;

// §18, context-aware tier recommendation (advisory). Reads q2 (ACV),
// q3 (ARR), q4 (budget), q5 (outbound), q1c (ICP focus). Returns one of
// 'starter' | 'grow' | 'scale' | 'enterprise'. (We use 'grow' as the
// canonical mid-tier id rather than the spec's 'growth' to match data.)
function getRecommendedTierFromQualifier(state, serviceId) {
  const q = (state && state.qualifier) || state || {};
  let rec = 'grow';

  // 1. Sub-economic deal sizes → starter
  if (q.q2 === 'free' || q.q2 === 'sub1k') rec = 'starter';
  // 2. Low budget → starter
  else if (q.q4 === 'sub500' || q.q4 === '500-2.5k') rec = 'starter';
  // 3. Established outbound team → scale or enterprise
  else if (q.q5 === 'team-large' || q.q5 === 'team-xlarge') {
    rec = q.q3 === '25mplus' ? 'enterprise' : 'scale';
  }
  // 4. Small team but real budget → scale
  else if (q.q5 === 'team-small' && !['sub500','500-2.5k'].includes(q.q4)) rec = 'scale';
  // 5. Big-ticket ACV → scale
  else if (q.q2 === '100kplus') rec = 'scale';
  // 6. Big budget → scale
  else if (q.q4 === '20k-50k' || q.q4 === '50kplus') rec = 'scale';
  // 7. Mid-ticket ACV / mid revenue → grow
  else if (q.q2 === '10k-100k' || q.q3 === '1m-5m') rec = 'grow';

  // ── Caps applied last ──
  // Pre-revenue can't sustain Scale
  if (q.q3 === 'prerevenue' && rec === 'scale') rec = 'grow';
  // Niche/ultra-niche TAM cap
  if ((q.q1c === 'niche' || q.q1c === 'ultra-niche') && (rec === 'scale' || rec === 'enterprise')) {
    rec = 'grow';
  }
  return rec;
}
window.getRecommendedTierFromQualifier = getRecommendedTierFromQualifier;

// Soft warning: selected starter tier might not be enough volume for stage.
// Returns true when the user picked starter but their ARR + budget warrant Scale.
function starterNotEnoughVolume(state, currentTier) {
  if (currentTier !== 'starter') return false;
  const q = (state && state.qualifier) || state || {};
  const arrBig = q.q3 === '5m-25m' || q.q3 === '25mplus';
  const budgetBig = q.q4 === '20k-50k' || q.q4 === '50kplus';
  return arrBig && budgetBig;
}
window.starterNotEnoughVolume = starterNotEnoughVolume;

// §19, ICP gates on SMM add-ons
//   smm-ig-stories  hidden for pure-B2B paths (SaaS, consulting)
//   smm-ugc-rights  hidden outside DTC + B2C services
function isSMMAddonHiddenForICP(addonId, qualifier) {
  // DISABLED, qualifier moved to Step 6; show full breadth of add-ons. (task #249)
  return false;
  // eslint-disable-next-line no-unreachable
  if (!qualifier || !qualifier.q1) return false;
  const q = qualifier;
  if (addonId === 'smm-ig-stories') {
    if (q.q1 === 'saas') return true;
    if (q.q1 === 'bservices' && q.bservicesCat === 'consulting') return true;
  }
  if (addonId === 'smm-ugc-rights') {
    if (q.q1 !== 'dtc' && q.q1 !== 'bcservices') return true;
  }
  return false;
}
window.isSMMAddonHiddenForICP = isSMMAddonHiddenForICP;

// §19, Paid Ads daily spend pre-fill, driven by monthly budget and whether
// the founder has run paid before (priorActivities). Returns daily £ amount.
function paidRecommendedDailySpend(qualifier) {
  if (!qualifier) return 25;
  const q = qualifier;
  const prior = Array.isArray(q.priorActivities) ? q.priorActivities : [];
  const hasPaidHistory = prior.includes('paid');
  const budgetMap = {
    'sub500': 10,
    '500-2.5k': 25,
    '2.5k-7.5k': 75,
    '7.5k-20k': 200,
    '20k-50k': 500,
    '50kplus': 1000,
    'notsure': 50,
  };
  let base = budgetMap[q.q4] || 50;
  // Conservative bump down if no paid history and pre-rev / bootstrapped
  if (!hasPaidHistory && q.q3 === 'prerevenue' && q.fundBacked === 'no') base = Math.max(10, Math.round(base * 0.6));
  // Aggressive bump up if funded + established + prior paid
  if (hasPaidHistory && q.fundBacked === 'yes' && (q.q3 === '5m-25m' || q.q3 === '25mplus')) base = Math.round(base * 1.5);
  return base;
}
window.paidRecommendedDailySpend = paidRecommendedDailySpend;

// §20, Next cohort kickoff date label from urgency. Returns
// e.g. 'Mon 27 May' formatted in London time.
function getNextCohortKickoffLabel(urgency) {
  const bufferDays = ({ 'now': 0, '30d': 7, '90d': 14, 'q3plus': 14, 'exploring': 14 })[urgency] ?? 14;
  const now = new Date();
  const target = new Date(now.getTime() + bufferDays * 24 * 60 * 60 * 1000);
  // Snap to next Monday
  const dow = target.getDay();
  const daysToMonday = dow === 0 ? 1 : (dow === 1 ? 0 : 8 - dow);
  target.setDate(target.getDate() + daysToMonday);
  const days   = ['Sun','Mon','Tue','Wed','Thu','Fri','Sat'];
  const months = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'];
  return `${days[target.getDay()]} ${target.getDate()} ${months[target.getMonth()]}`;
}
window.getNextCohortKickoffLabel = getNextCohortKickoffLabel;

// §20, Recommended commit length per service. tradingLength sets the floor,
// urgency='now' shortens by one step (let new clients test before locking in).
function getRecommendedCommitLength(tradingLength, urgency, serviceId) {
  const sid = _canonicalServiceId(serviceId);
  const ladder = ['monthly', '3', '6', '12'];
  let idx = 1; // default 3-month
  if (tradingLength === 'under6m') idx = 0;
  else if (tradingLength === '6-12m') idx = 1;
  else if (tradingLength === '1-3y') idx = 2;
  else if (tradingLength === '3-7y' || tradingLength === '7yplus') idx = 3;
  // Performance-sensitive services prefer monthly when trading is short
  if ((sid === 'paid-ads' || sid === 'email') && tradingLength === 'under6m') idx = 0;
  // Urgency=now shortens by one step
  if (urgency === 'now' && idx > 0) idx -= 1;
  return ladder[idx];
}
window.getRecommendedCommitLength = getRecommendedCommitLength;

// §5.8, Future-work stub. When wired in later, this would hide trading /
// fund / urgency questions for solo + pre-revenue founders. NOT currently
// active; reveal logic above ignores this.
function shouldShowAdvancedStep1Questions(state) {
  const q = (state && state.qualifier) || state || {};
  if (!q) return true;
  if (q.q5 === 'none' && q.q3 === 'prerevenue') return false;
  return true;
}
window.shouldShowAdvancedStep1Questions = shouldShowAdvancedStep1Questions;

// ── Talent FT (Dedicated Resources), Step 1 → Step 4 defaults ─────
// Based on founders-talent-solutions-conditional-tree.md §5/§6/§8/§13.
// All helpers are pure and read off the v2 qualifier shape; they degrade
// safely to empty/false when qualifier is incomplete.

// Map of spec role slugs → the existing ft-* role ids used in the SERVICES
// catalog's dedicated-ft.roles.fulltime list. Roles in the spec that have
// no exact in-app counterpart (paid-ads-media-buyer, customer-service-rep,
// appointment-setter, 2d-designer) are remapped to the closest existing
// role or dropped silently. Keep this map narrow, if you add a role to
// SERVICES, also add it here.
const DR_FT_SLUG_TO_ID = {
  'marketing-ops-coordinator': 'ft-mkt-ops',
  'graphic-designer':          'ft-graphic',
  'copywriter':                'ft-copy',
  'video-editor':              'ft-video',
  'sdr':                       'ft-sdr',
  'customer-success-manager':  'ft-csm',
  'account-manager':           'ft-am',
  'fullstack-developer':       'ft-fullstack',
  'ai-automation':             'ft-ai',
  'data-analyst':              'ft-data',
  'devops-engineer':           'ft-devops',
  'uiux-designer':             'ft-uiux',
  '3d-designer':               'ft-3d',
  'video-motion-designer':     'ft-vmotion',
  'executive-assistant':       'ft-exec',
  'virtual-assistant':         'ft-va',
  // Remaps for slugs without an exact match
  'appointment-setter':        'ft-sdr',     // appointment setting ~= outbound SDR
  '2d-designer':               'ft-graphic', // 2D ~= graphic designer
  'paid-ads-media-buyer':      null,         // no app-side equivalent; skip
  'customer-service-rep':      null,         // no app-side equivalent; skip
};
window.DR_FT_SLUG_TO_ID = DR_FT_SLUG_TO_ID;

function _drftMap(slugs) {
  return slugs
    .map(s => DR_FT_SLUG_TO_ID[s])
    .filter(id => id != null);
}

// §5, default 4-ish-role pre-selection per Step 1 path. Returns the
// canonical ft-* ids (not the spec slugs) so the existing service card's
// role grid can read them directly.
function getDrFtDefaultRoles(state) {
  const q = (state && state.qualifier) || state || {};
  if (!q.q1) return [];
  const isPreRev = q.q3 === 'prerevenue';

  switch (q.q1) {
    case 'saas': {
      if (q.q1a === 'product-led') {
        return _drftMap(['fullstack-developer','data-analyst','uiux-designer','customer-success-manager','sdr','executive-assistant']);
      }
      // sales-led / hybrid / unset
      return _drftMap(['marketing-ops-coordinator','copywriter','sdr','customer-service-rep']);
    }
    case 'dtc': {
      // DTC: electronics + luxury get a creative-heavy roster (3D/UI-UX);
      // every other DTC sub gets ops + creative + paid-media-buyer.
      const isElectronicsOrLuxury = q.q1a === 'electronics' || q.q1a === 'jewellery';
      if (isElectronicsOrLuxury) {
        return _drftMap(['graphic-designer','3d-designer','uiux-designer','paid-ads-media-buyer']);
      }
      const base = ['marketing-ops-coordinator','graphic-designer','video-editor','paid-ads-media-buyer'];
      // Pre-rev DTC drops customer-service-rep per v33.110-SVC-T06 (already a no-op
      // here because that slug isn't in base, but matches spec rationale).
      return _drftMap(base);
    }
    case 'bservices':
      return _drftMap(['marketing-ops-coordinator','account-manager','copywriter','executive-assistant']);
    case 'bcservices':
      return _drftMap(['virtual-assistant','customer-service-rep','appointment-setter','2d-designer']);
    case 'mktplace': {
      if (q.q1a === 'supply') {
        return _drftMap(['ai-automation','account-manager','copywriter','data-analyst']);
      }
      return _drftMap(['marketing-ops-coordinator','ai-automation','paid-ads-media-buyer','account-manager']);
    }
    case 'both':
    default:
      return _drftMap(['marketing-ops-coordinator','graphic-designer','copywriter','virtual-assistant']);
  }
}
window.getDrFtDefaultRoles = getDrFtDefaultRoles;

// §8, Buyout option should pre-check when the user is funded Series A+
// AND has picked an SDR or CSM role. Returns true iff:
//   • role id is ft-sdr or ft-csm, AND
//   • fundBacked === 'yes', AND
//   • fundRaised indicates Series A or beyond (500k-2m / 2m-10m / 10mplus).
function shouldDefaultDrFtBuyout(state, ftRoleId) {
  const q = (state && state.qualifier) || state || {};
  if (ftRoleId !== 'ft-sdr' && ftRoleId !== 'ft-csm') return false;
  if (q.fundBacked !== 'yes') return false;
  const seriesAplus = ['500k-2m', '2m-10m', '10mplus'];
  return seriesAplus.includes(q.fundRaised);
}
window.shouldDefaultDrFtBuyout = shouldDefaultDrFtBuyout;

// §6, Talent FT multi-role discount within the dedicated-ft service.
// Stacks with the global multi-service ladder; final retainer capped at 50%
// floor at the plan-summary aggregator. Setup fees excluded.
// Exposed for future wiring, not yet applied to subtotals (FT roles are
// 'Custom' pricing right now so the discount is a no-op until pricing
// flips numeric).
const DR_FT_MULTI_ROLE_DISCOUNT = { 1: 0, 2: 0.03, 3: 0.05, 4: 0.08 };
window.DR_FT_MULTI_ROLE_DISCOUNT = DR_FT_MULTI_ROLE_DISCOUNT;
function drFtMultiRoleDiscountPct(state) {
  const sel = state?.selections?.['dedicated-ft'];
  const n = Array.isArray(sel?.roles) ? sel.roles.length : 0;
  if (n <= 1) return 0;
  if (n >= 4) return DR_FT_MULTI_ROLE_DISCOUNT[4];
  return DR_FT_MULTI_ROLE_DISCOUNT[n] || 0;
}
window.drFtMultiRoleDiscountPct = drFtMultiRoleDiscountPct;

// §13, Soft "too early for FT" warning. Surfaces a yellow banner inside
// the dedicated-ft card when the founder is pre-revenue with sub-£1k ACV.
// No hard gate, just nudges them toward Part-Time or skipping.
function isDrFtTooEarlyWarning(state) {
  const q = (state && state.qualifier) || state || {};
  return q.q3 === 'prerevenue' && (q.q2 === 'free' || q.q2 === 'sub1k');
}
window.isDrFtTooEarlyWarning = isDrFtTooEarlyWarning;

// ── Talent PT (Dedicated Resources Part-Time), Step 1 defaults ─────
// Based on founders-talent-dedicated-resources-full-spec.md §4.6. Maps the
// spec's PT role slugs to the existing app pt-* role ids in the dedicated-pt
// catalog. Slugs in the spec without an exact match remap to the closest
// existing role or drop silently.
const DR_PT_SLUG_TO_ID = {
  'virtual-assistant-pt':     'pt-va',
  'copywriter-pt':            'pt-copy',
  'video-editor-pt':          'pt-video',
  'motion-graphic-designer':  'pt-motion',
  'web-developer-pt':         'pt-web',
  'automation-ai-agent-pt':   'pt-ai',
  'general-marketer-pt':      'pt-marketer',
  'seo-specialist-pt':        'pt-seo',
  'appointment-setter-pt':    'pt-appt',
  'sales-rep-closer-pt':      'pt-sales',
  '2d-designer':              'pt-2d',
  '3d-designer-pt':           'pt-3d',
};
window.DR_PT_SLUG_TO_ID = DR_PT_SLUG_TO_ID;
function _drptMap(slugs) {
  return slugs.map(s => DR_PT_SLUG_TO_ID[s]).filter(id => id != null);
}

// §4.6, default PT role mix per Step 1 path. Returns canonical pt-* ids.
function getDrPtDefaultRoles(state) {
  const q = (state && state.qualifier) || state || {};
  if (!q.q1) return [];

  switch (q.q1) {
    case 'dtc': {
      if (q.q1a === 'electronics' || q.q1a === 'jewellery') {
        return _drptMap(['3d-designer-pt', '2d-designer', 'web-developer-pt']);
      }
      return _drptMap(['2d-designer', 'motion-graphic-designer', 'automation-ai-agent-pt', 'copywriter-pt']);
    }
    case 'saas': {
      if (q.q1a === 'product-led') {
        return _drptMap(['web-developer-pt', 'general-marketer-pt', 'copywriter-pt']);
      }
      // sales-led / hybrid / unset
      return _drptMap(['appointment-setter-pt', 'sales-rep-closer-pt', 'copywriter-pt']);
    }
    case 'bservices': {
      if (q.q1b === 'midmarket') {
        // 'enterprise' bucket per spec, closest in our q1b is 'midmarket'
        return _drptMap(['seo-specialist-pt', 'sales-rep-closer-pt', 'copywriter-pt']);
      }
      if (q.bservicesCat === 'agency') {
        return _drptMap(['web-developer-pt', 'general-marketer-pt', '2d-designer']);
      }
      return _drptMap(['general-marketer-pt', 'web-developer-pt', 'copywriter-pt']);
    }
    case 'bcservices':
      return _drptMap(['virtual-assistant-pt', 'appointment-setter-pt', '2d-designer']);
    case 'mktplace':
      return _drptMap(['general-marketer-pt', 'web-developer-pt', 'automation-ai-agent-pt']);
    case 'both':
    default: {
      if (q.q3 === 'prerevenue') {
        return _drptMap(['virtual-assistant-pt', 'copywriter-pt', '2d-designer']);
      }
      return _drptMap(['general-marketer-pt', 'copywriter-pt', '2d-designer']);
    }
  }
}
window.getDrPtDefaultRoles = getDrPtDefaultRoles;

// §3.2, Commit-length auto-recommendation for FT. Short trading length OR
// urgency=now defaults the commit to 3-mo; otherwise 12-mo (best rate).
function getDrFtRecommendedCommit(state) {
  const q = (state && state.qualifier) || state || {};
  if (q.tradingLength === 'under6m' || q.urgency === 'now') return '3';
  if (q.tradingLength === '6-12m') return '6';
  return '12';
}
window.getDrFtRecommendedCommit = getDrFtRecommendedCommit;

// §3.8 / §10, Add-on render-order score for the dedicated-ft addons.
// Returns a map of addonId → score. The service card sorts addons by score
// descending (ties by declaration order) and surfaces a Recommended ribbon
// on any addon with score ≥ 5.
function getDrFtAddonScore(state) {
  const q = (state && state.qualifier) || state || {};
  const sel = state?.selections?.['dedicated-ft'];
  const scoreMap = {
    'visa':      0,
    'buyout':    0,
    'uk-direct': 0,
    'it-equip':  0,
    'benefits':  0,
    'coworking': 0,
    'pro-dev':   0,
  };
  // UK Direct Hire for B2B enterprise audiences
  if (q.q1 === 'bservices' && q.q1b === 'midmarket') scoreMap['uk-direct'] += 10;
  // IT Equipment for engineering-heavy SaaS
  if (q.q1 === 'saas' && q.q1a === 'product-led') scoreMap['it-equip'] += 8;
  // Benefits + Coworking for mature revenue
  if (q.q3 === '25mplus') {
    scoreMap['benefits']  += 7;
    scoreMap['coworking'] += 6;
  }
  // Pro Dev for mid-stage growth
  if (q.q3 === '1m-5m' || q.q3 === '5m-25m') scoreMap['pro-dev'] += 5;
  // Visa for international roles (any UK-located role)
  if (Array.isArray(sel?.roles)) {
    const ukRoles = sel.roles.some(rid => sel.roleConfigs?.[rid]?.location === 'uk');
    if (ukRoles) scoreMap['visa'] += 5;
  }
  // Buyout surfaces high if any selected role qualifies
  if (Array.isArray(sel?.roles) && window.shouldDefaultDrFtBuyout) {
    const hasQualifier = sel.roles.some(rid => window.shouldDefaultDrFtBuyout(state, rid));
    if (hasQualifier) scoreMap['buyout'] += 6;
  }
  return scoreMap;
}
window.getDrFtAddonScore = getDrFtAddonScore;

function isDrFtAddonRecommended(state, addonId) {
  const score = getDrFtAddonScore(state);
  return (score[addonId] || 0) >= 5;
}
window.isDrFtAddonRecommended = isDrFtAddonRecommended;

// §3.6 + §11, Talent FT capacity. Returns { status, fill, caption } based on
// global capacity (always 'Open' for v1 since ops capacity sync isn't wired)
// plus per-state shifts: urgency=now → Limited, more roles selected → fill
// climbs. Status drives the badge color (Open=green, Limited=amber, Closed=red).
const TALENT_CAPACITY_CAPTIONS = {
  open:    'We currently have capacity to onboard new clients for this service. We recommend scheduling a call soon to secure your place with our specialist team.',
  limited: 'Capacity is filling fast. We can still take new clients this month but priority goes to existing customers and waitlist. Book a call to confirm a slot.',
  closed:  'Talent recruitment is at capacity right now. We will add you to the waitlist. Book a call to lock your slot.',
};
window.TALENT_CAPACITY_CAPTIONS = TALENT_CAPACITY_CAPTIONS;
function getDrFtTalentCapacity(state) {
  const q = (state && state.qualifier) || state || {};
  const sel = state?.selections?.['dedicated-ft'];
  const rolesCount = Array.isArray(sel?.roles) ? sel.roles.length : 0;
  // Base fill climbs as user selects roles (more roles = more capacity needed).
  const baseFill = Math.min(95, 40 + rolesCount * 8);
  // 'now' urgency proxy for the spec's hiringTime='within-14-days'.
  if (q.urgency === 'now') {
    return { status: 'limited', fill: Math.max(70, baseFill), caption: TALENT_CAPACITY_CAPTIONS.limited };
  }
  return { status: 'open', fill: baseFill, caption: TALENT_CAPACITY_CAPTIONS.open };
}
window.getDrFtTalentCapacity = getDrFtTalentCapacity;

// §3.7 + §12, Talent FT forecast. Returns { gorilla, inhouse, savings } for
// the side-by-side 12-month comparison. The Gorilla side uses the role's
// hourlyFrom × 160 hours/month × 12 (matches the "from £X/hour" headline
// shown on each role tile). The in-house side uses UK loaded cost = base
// salary midpoint × 1.4 loading factor.
//
// We don't have a full DR_FT_FEE_MATRIX populated yet, so the forecast is
// indicative based on the role's published `hourlyFrom` Philippines floor;
// when actual fee numbers ship, swap to drFtMonthlyForRole().
const DR_FT_UK_BASE_SALARY = {
  'ft-mkt-ops':  { junior: 28000, mid: 38000, senior: 52000 },
  'ft-graphic':  { junior: 26000, mid: 36000, senior: 48000 },
  'ft-copy':     { junior: 28000, mid: 38000, senior: 52000 },
  'ft-video':    { junior: 28000, mid: 40000, senior: 55000 },
  'ft-sdr':      { junior: 32000, mid: 42000, senior: 58000 },
  'ft-csm':      { junior: 35000, mid: 48000, senior: 65000 },
  'ft-am':       { junior: 35000, mid: 48000, senior: 65000 },
  'ft-fullstack':{ junior: 42000, mid: 60000, senior: 85000 },
  'ft-ai':       { junior: 45000, mid: 65000, senior: 90000 },
  'ft-data':     { junior: 40000, mid: 55000, senior: 75000 },
  'ft-devops':   { junior: 45000, mid: 65000, senior: 88000 },
  'ft-uiux':     { junior: 38000, mid: 55000, senior: 75000 },
  'ft-3d':       { junior: 38000, mid: 52000, senior: 70000 },
  'ft-vmotion':  { junior: 32000, mid: 48000, senior: 65000 },
  'ft-exec':     { junior: 26000, mid: 35000, senior: 48000 },
  'ft-va':       { junior: 22000, mid: 30000, senior: 42000 },
};
window.DR_FT_UK_BASE_SALARY = DR_FT_UK_BASE_SALARY;

function drFtInhouseAnnual(roleId, seniority) {
  const salaries = DR_FT_UK_BASE_SALARY[roleId];
  if (!salaries) return 0;
  const base = salaries[seniority || 'mid'] || salaries.mid || 0;
  // UK loaded cost = base × 1.4 (NI 13.8%, pension 5%, benefits, equipment,
  // training, recruitment fee, admin overhead). Conservative single multiplier.
  return Math.round(base * 1.4);
}
window.drFtInhouseAnnual = drFtInhouseAnnual;

function drFtForecast(state) {
  const sel = state?.selections?.['dedicated-ft'];
  if (!sel || !Array.isArray(sel.roles) || sel.roles.length === 0) {
    return { gorilla: { rows: [], total: 0 }, inhouse: { rows: [], total: 0 }, savings: 0 };
  }
  const svc = (window.SERVICES || []).find(s => s.id === 'dedicated-ft');
  const tierRoles = svc?.roles?.fulltime || [];
  // Commit multiplier (12mo = 0.6, 6mo = 0.8, 3mo = 1.0) per spec §8, match
  // the discount badges shown on the commit pills.
  const commitMult = ({ '3': 1.0, '6': 0.8, '12': 0.6 })[String(sel.commitId || '12')] || 0.6;
  const HOURS_PER_MONTH = 160; // 8h/day × 20 working days
  const gorillaRows = [];
  const inhouseRows = [];
  let gorillaTotal = 0;
  let inhouseTotal = 0;
  for (const rid of sel.roles) {
    const def = tierRoles.find(r => r.id === rid);
    if (!def) continue;
    const cfg = sel.roleConfigs?.[rid] || {};
    const seniority = cfg.seniority || 'mid';
    const baseHourly = def.hourlyFrom || 8;
    // Seniority multiplier on hourly: junior 0.85, mid 1.0, senior 1.35
    const senMult = ({ junior: 0.85, mid: 1.0, senior: 1.35 })[seniority] || 1.0;
    // Location multiplier: philippines 1.0, south-africa 1.3, uk 2.4
    const locMult = ({ philippines: 1.0, 'south-africa': 1.3, uk: 2.4 })[cfg.location || 'philippines'] || 1.0;
    const monthly = Math.round(baseHourly * senMult * locMult * HOURS_PER_MONTH * commitMult);
    const annual = monthly * 12;
    gorillaRows.push({ roleId: rid, name: def.name, monthly, annual });
    gorillaTotal += annual;
    const ihAnnual = drFtInhouseAnnual(rid, seniority);
    inhouseRows.push({ roleId: rid, name: def.name, annual: ihAnnual });
    inhouseTotal += ihAnnual;
  }
  return {
    gorilla: { rows: gorillaRows, total: gorillaTotal },
    inhouse: { rows: inhouseRows, total: inhouseTotal },
    savings: Math.max(0, inhouseTotal - gorillaTotal),
  };
}
window.drFtForecast = drFtForecast;

// ── Step 6 (Ready to launch) helpers per founders-step-6 spec §4.1, §5 ─────
// Summarises 2-3 Step 1 highlights for the plan-summary eyebrow.
// Examples:
//   DTC → "£175 AOV · 50% margin · monthly repeat"
//   SaaS sales-led → "Sales-led · 30-90 day cycle · £5M ARR"
function _aovLabel(v) {
  return ({ sub50:'£<50 AOV','50-100':'£50-100 AOV','100-250':'£100-250 AOV','250-500':'£250-500 AOV','500-750':'£500-750 AOV','750plus':'£750+ AOV','500plus':'£500+ AOV' })[v] || '';
}
function _marginLabel(v) {
  return ({ sub20:'<20% margin','20-40':'20-40% margin','40-60':'40-60% margin','60-80':'60-80% margin','80plus':'80%+ margin' })[v] || '';
}
function _repeatLabel(v) {
  return ({ onetime:'one-time', biannual:'twice-a-year', quarterly:'quarterly repeat', monthly:'monthly repeat', subscription:'subscription' })[v] || '';
}
function _saasCycleLabel(v) {
  return ({ 'sub30':'<30 day cycle','30-90':'30-90 day cycle','90-180':'90-180 day cycle','180plus':'180+ day cycle' })[v] || '';
}
function _arrLabel(v) {
  return ({ prerevenue:'pre-revenue','100k-1m':'£100k, 1M ARR','1m-5m':'£1-5M ARR','5m-25m':'£5-25M ARR','25mplus':'£25M+ ARR' })[v] || '';
}
function summarizeForEyebrow(state) {
  const q = (state && state.qualifier) || state || {};
  const parts = [];
  switch (q.q1) {
    case 'dtc': {
      if (q.q1aDtcAov)    parts.push(_aovLabel(q.q1aDtcAov));
      if (q.q1aDtcMargin) parts.push(_marginLabel(q.q1aDtcMargin));
      if (q.q1aDtcRepeat) parts.push(_repeatLabel(q.q1aDtcRepeat));
      break;
    }
    case 'saas': {
      if (q.q1a === 'sales-led')   parts.push('Sales-led');
      else if (q.q1a === 'product-led') parts.push('Product-led');
      else if (q.q1a === 'hybrid') parts.push('Hybrid');
      if (q.saasCycle) parts.push(_saasCycleLabel(q.saasCycle));
      if (q.q3) parts.push(_arrLabel(q.q3));
      break;
    }
    case 'bservices': {
      if (q.bservicesCat) parts.push(q.bservicesCat.charAt(0).toUpperCase() + q.bservicesCat.slice(1));
      if (q.q1a) parts.push(q.q1a.charAt(0).toUpperCase() + q.q1a.slice(1));
      if (q.bservicesCycle) parts.push(q.bservicesCycle.replace('-d','d'));
      break;
    }
    case 'bcservices': {
      if (q.q1a) parts.push(q.q1a.charAt(0).toUpperCase() + q.q1a.slice(1));
      if (q.bcservicesCat) parts.push(q.bcservicesCat.charAt(0).toUpperCase() + q.bcservicesCat.slice(1));
      break;
    }
    case 'both': {
      parts.push('Mixed B2B / B2C');
      if (q.q1a === 'mostly-b2b') parts.push('mostly B2B');
      else if (q.q1a === 'mostly-b2c') parts.push('mostly B2C');
      else if (q.q1a === 'split') parts.push('roughly split');
      if (q.q3) parts.push(_arrLabel(q.q3));
      break;
    }
    case 'mktplace': {
      if (q.q1a === 'supply') parts.push('Supply-side marketplace');
      else if (q.q1a === 'demand') parts.push('Demand-side marketplace');
      else parts.push('Marketplace');
      if (q.q3) parts.push(_arrLabel(q.q3));
      break;
    }
    default:
      if (q.q3) parts.push(_arrLabel(q.q3));
  }
  return parts.filter(Boolean).slice(0, 3).join(' · ');
}
window.summarizeForEyebrow = summarizeForEyebrow;

// Plan title per client type (§4.1)
function getPlanTitle(clientTypeId) {
  if (clientTypeId === 'investor') return 'Investor portfolio plan';
  if (clientTypeId === 'agency') return 'Agency partner plan';
  return 'Founders growth plan';
}
window.getPlanTitle = getPlanTitle;

// §5.6, Submit button copy per call preference.
function getStep6SubmitCopy(callPref) {
  switch (callPref) {
    case 'email-first':      return 'Email me the proposal →';
    case 'already-had':      return 'Send the proposal to my team →';
    case 'contract-payment': return 'Request my payment link →';
    case 'schedule':
    default:                 return 'Continue to scheduling →';
  }
}
window.getStep6SubmitCopy = getStep6SubmitCopy;

// §5.5, Next-step hint per call preference.
function getStep6NextHint(callPref) {
  switch (callPref) {
    case 'email-first': return 'Next: we email the proposal within one business day';
    case 'already-had': return null; // hidden, early-exit caption replaces it
    case 'schedule':
    default:            return 'Next: schedule a call after you submit';
  }
}
window.getStep6NextHint = getStep6NextHint;

// §5.7, Trust line per call preference.
function getStep6TrustLine(callPref) {
  if (callPref === 'email-first') {
    return 'No commitment. We email the proposal within one business day; book a call any time after.';
  }
  if (callPref === 'already-had') {
    return 'We will send your proposal to your team within one business day. No further steps are needed from you.';
  }
  if (callPref === 'contract-payment') {
    return 'We will send your payment link within one to two hours so you can start straight away.';
  }
  return 'No commitment, cancel any time. We email your proposal within one business day, then you choose a time to review it with us.';
}
window.getStep6TrustLine = getStep6TrustLine;

// Sprint 3, S&DG meeting-volume estimates per tier × deal-size band.
// Conservative defaults; flagged as "confirm against active client data" in
// the handover Open Questions §6 #4. Used by costPerMeeting() helper below.
const SDG_MEETINGS_PER_MONTH = {
  starter: { 'lt-100': 8, '100-1k': 5, '1k-10k': 4, '10k-100k': 3, 'gt-100k': 2 },
  grow:    { 'lt-100':16, '100-1k':12, '1k-10k': 9, '10k-100k': 6, 'gt-100k': 4 },
  scale:   { 'lt-100':30, '100-1k':22, '1k-10k':16, '10k-100k':11, 'gt-100k': 8 },
};
window.SDG_MEETINGS_PER_MONTH = SDG_MEETINGS_PER_MONTH;

// Lead-volume defaults shipped with each tier (for cost-per-meeting math).
// Increase ratio: more leads → more meetings, with reasonable conversion.
const SDG_LEADS_PER_MONTH = { starter: 600, grow: 1600, scale: 4000 };
window.SDG_LEADS_PER_MONTH = SDG_LEADS_PER_MONTH;

// Returns the all-in cost-per-meeting for S&DG given tier + commit + qualifier
// answers. Includes the monthly retainer (after agency multiplier) + per-lead
// pricing × leads volume. Returns null if any input is missing.
// Sprint 4, GorillaPerks: bundled benefits unlocked on every paid plan.
// Universal perks apply to all plans; client-type perks layer on top.
const GORILLA_PERKS = {
  universal: [
    { id: 'gp-shield',  icon: '◆', label: 'GorillaShield warranty',     desc: 'If we miss the agreed quality bar, the next month is free.' },
    { id: 'gp-review',  icon: '✦', label: 'Monthly strategy review',     desc: 'A 30-min review with your CSM each month, every month.' },
    { id: 'gp-tools',   icon: '◇', label: 'Stack tools included',         desc: 'No extra licence fees for the SaaS we run on top, Apollo, Clay, SendSpark, Smartlead, etc.' },
  ],
  founder: [
    { id: 'gp-fp',      icon: '✧', label: 'Founders Portal access',       desc: 'Free read-only access to our investor matching tool.' },
    { id: 'gp-audit',   icon: '◆', label: 'Free baseline growth audit',   desc: 'Pre-engagement audit of your channels, copy, funnel.' },
  ],
  agency: [
    { id: 'gp-csm',     icon: '✧', label: 'Dedicated agency CSM',         desc: 'A single point of contact across all your client engagements.' },
    { id: 'gp-reseller',icon: '◇', label: 'Reseller perks',               desc: '10% recurring referral commission for the duration of the client\'s minimum commitment. Your referred client also receives 10% off their plan for the same period.' },
    { id: 'gp-wlbrand', icon: '◆', label: 'White-label branding',         desc: 'Unbranded deliverables you ship as your own.' },
  ],
  investor: [
    { id: 'gp-pcsm',    icon: '✧', label: 'Portfolio CSM',                desc: 'Dedicated CSM coordinating across every portfolio company we onboard.' },
    { id: 'gp-fund',    icon: '◇', label: 'Fund-wide pricing',            desc: '20% discount on every portfolio company we onboard.' },
    { id: 'gp-comms',   icon: '◆', label: '10% recurring commission',     desc: 'On every portfolio company that ends up signing with us.' },
  ],
};
window.GORILLA_PERKS = GORILLA_PERKS;

// Returns the merged perks list for a client type, universal + client-specific.
function gorillaPerksFor(clientTypeId) {
  const universal = GORILLA_PERKS.universal || [];
  const specific = GORILLA_PERKS[clientTypeId] || [];
  return [...universal, ...specific];
}
window.gorillaPerksFor = gorillaPerksFor;

// ── Tier comparison matrices ─────────────────────────────────────────────
// Powers the "Compare all tiers" modal launched from inside each service
// card. Per-service object with a subtitle + a rows[] array. Row types:
//
//   { type: 'header', label: 'CHANNELS' }      , section heading
//   { type: 'price', tiers: { starter, grow, scale, enterprise } }
//   { type: 'check', name: 'Email outbound',
//     tiers: { starter: 'yes'|'no'|'addon', ... } } , ✓/− matrix row
//   { type: 'text',  tiers: { starter, grow, scale, enterprise } }
//                                             , free-text per-tier rows
//   { type: 'best',  tiers: { starter, grow, scale, enterprise } }
//                                             , emphasis "Best for" row
//
// Data distilled from Airtable Services bullets (base appRgD9IZC7YHxw1d).
const TIER_COMPARISON = {
  sales: {
    subtitle: 'Same SDR pod, more channels and richer signals as you scale. Switch tiers any time after the initial term, no penalty, no renegotiation.',
    rows: [
      { type: 'header', label: 'Channels' },
      { type: 'check', name: 'Email outbound',
        tiers: { starter: 'yes', grow: 'yes', scale: 'yes', enterprise: 'yes' } },
      { type: 'check', name: 'Phone (cold calling)',
        tiers: { starter: 'no', grow: 'yes', scale: 'yes', enterprise: 'yes' } },
      { type: 'check', name: 'LinkedIn outbound',
        tiers: { starter: 'addon', grow: 'addon', scale: 'yes', enterprise: 'yes' } },
      { type: 'check', name: 'Paid ad warm-up',
        tiers: { starter: 'no', grow: 'no', scale: 'yes', enterprise: 'yes' } },
      { type: 'check', name: 'WhatsApp follow-up',
        tiers: { starter: 'addon', grow: 'addon', scale: 'addon', enterprise: 'yes' } },

      { type: 'header', label: 'Volume' },
      { type: 'text', tiers: {
          starter: '750 prospects/mo',
          grow: '1,000 prospects/mo',
          scale: '1,250 prospects/mo',
          enterprise: 'Custom volume',
      } },
      { type: 'text', tiers: {
          starter: 'Standard databases · premium niche sources available as add-ons',
          grow: 'Standard databases · premium niche sources available as add-ons',
          scale: 'Standard databases · premium niche sources available as add-ons',
          enterprise: 'Bespoke + niche & gated sources included',
      } },

      { type: 'header', label: 'Signals' },
      { type: 'text', tiers: {
          starter: 'PersonaFit™ + GorillaSignals™',
          grow: 'PersonaFit™ + GorillaSignals™ with deeper intent layering',
          scale: 'PersonaFit™ + GorillaSignals™ with web-intent',
          enterprise: 'All signals + predictive modelling + custom triggers',
      } },

      { type: 'header', label: 'Cadence & team' },
      { type: 'text', tiers: {
          starter: '4-touch email cadence',
          grow: '6-touch multi-channel cadence',
          scale: '10-touch multi-channel cadence',
          enterprise: 'Custom cadence + buying-committee orchestration',
      } },
      { type: 'text', tiers: {
          starter: 'Up to 3 in-house specialists',
          grow: 'Up to 8 in-house specialists',
          scale: 'Up to 8 specialists + dedicated AM',
          enterprise: 'Dedicated senior team across channels',
      } },

      { type: 'header', label: 'Best for' },
      { type: 'best', tiers: {
          starter: 'Founders testing outbound, sub-£1k deal sizes, or local/regional businesses.',
          grow: '£1k-£10k deals, mid-market SaaS or B2B services with proven product-market fit.',
          scale: '£10k+ deals, mid-market/enterprise with 4+ SDRs, multi-thread outbound.',
          enterprise: '£25M+ revenue, multi-product portfolios, dedicated programme manager.',
      } },
    ],
  },

  email: {
    subtitle: 'Same managed email program, more depth and senior specialists as you scale.',
    rows: [
      { type: 'header', label: 'Team' },
      { type: 'text', tiers: {
          starter: 'Up to 4 junior specialists',
          grow: 'Up to 6 mid-level specialists',
          scale: 'Up to 7 senior specialists',
          enterprise: 'Up to 12 senior specialists',
      } },
      { type: 'header', label: 'Topic research' },
      { type: 'text', tiers: {
          starter: 'Standard',
          grow: 'Intermediate',
          scale: 'Advanced',
          enterprise: 'Complex / custom',
      } },
      { type: 'header', label: 'Email design' },
      { type: 'text', tiers: {
          starter: 'Basic',
          grow: 'Intermediate',
          scale: 'Advanced',
          enterprise: 'Custom design',
      } },
      { type: 'header', label: 'Segmentation' },
      { type: 'text', tiers: {
          starter: 'Basic',
          grow: 'Intermediate',
          scale: 'Advanced',
          enterprise: 'Granular + predictive',
      } },
      { type: 'header', label: 'Domain authentication' },
      { type: 'check', name: 'Authentication & verification',
        tiers: { starter: 'yes', grow: 'yes', scale: 'yes', enterprise: 'yes' } },
    ],
  },

  'paid-ads': {
    subtitle: 'Same managed paid program, more channels and senior specialists as you scale.',
    rows: [
      { type: 'header', label: 'Channels' },
      { type: 'text', tiers: {
          starter: '1 search channel',
          grow: '1 social channel',
          scale: '2 channels (cross-channel)',
          enterprise: 'All channels',
      } },
      { type: 'header', label: 'Creatives' },
      { type: 'check', name: 'Unlimited graphic + video creatives',
        tiers: { starter: 'no', grow: 'yes', scale: 'yes', enterprise: 'yes' } },
      { type: 'header', label: 'Team' },
      { type: 'text', tiers: {
          starter: 'Up to 5 specialists',
          grow: 'Up to 6 specialists',
          scale: 'Up to 7 senior specialists',
          enterprise: 'Dedicated senior team',
      } },
      { type: 'header', label: 'Strategy' },
      { type: 'check', name: 'GorillaAudit® review',
        tiers: { starter: 'yes', grow: 'yes', scale: 'yes', enterprise: 'yes' } },
      { type: 'check', name: 'GorillaGrowth® optimisation',
        tiers: { starter: 'yes', grow: 'yes', scale: 'yes', enterprise: 'yes' } },
    ],
  },

  smm: {
    subtitle: 'Same managed social program, more team depth and creative quality as you scale.',
    rows: [
      { type: 'header', label: 'Team' },
      { type: 'text', tiers: {
          starter: 'Up to 5 junior specialists',
          grow: 'Up to 8 mid-level specialists',
          scale: 'Up to 10 senior specialists',
          enterprise: 'Up to 12 senior specialists',
      } },
      { type: 'header', label: 'Graphics & video' },
      { type: 'text', tiers: {
          starter: 'Standard',
          grow: 'Custom',
          scale: 'Advanced',
          enterprise: 'Premium',
      } },
      { type: 'header', label: 'Community engagement' },
      { type: 'check', name: 'Community management',
        tiers: { starter: 'no', grow: 'yes', scale: 'yes', enterprise: 'yes' } },
      { type: 'header', label: 'Strategy' },
      { type: 'check', name: 'GorillaAudit® strategy review',
        tiers: { starter: 'yes', grow: 'yes', scale: 'yes', enterprise: 'yes' } },
    ],
  },
};
window.TIER_COMPARISON = TIER_COMPARISON;

// ── Monthly Lead Volume widget (SDG) ─────────────────────────────────────────
// Single source: Standard databases. Each tier includes 750 leads/mo
// free. Additional leads beyond 750 are charged at a flat £4/lead.
// Slider capped at 5,000.
// 2026-05-22: lowered included tier amount 1000 -> 750.
// 2026-05-22: flat £4/lead (was £2.50 with 10% bulk discount).
const LEAD_PRICE_PER_UNIT = 4;
const LEAD_BULK_DISCOUNT = 0;
// 2026-05-26: included leads now mirror the tier feature pills shown on each
// SDG tier card. Starter 750, Grow 1,000, Scale 1,250 — these are the
// "prospects/mo" numbers users see when picking a tier.
const LEAD_INCLUDED_BY_TIER = { starter: 750, grow: 1000, scale: 1250 };
const LEAD_INCLUDED_DEFAULT = 750;
const LEAD_VOLUME_MAX = 5000;
function leadIncludedForTier(tierId) {
  if (tierId && Object.prototype.hasOwnProperty.call(LEAD_INCLUDED_BY_TIER, tierId)) {
    return LEAD_INCLUDED_BY_TIER[tierId];
  }
  return LEAD_INCLUDED_DEFAULT;
}
function computeAdditionalLeadCost(monthlyLeads, tierId) {
  const included = leadIncludedForTier(tierId);
  const additional = Math.max(0,
    Math.min(LEAD_VOLUME_MAX, Number(monthlyLeads) || 0) - included
  );
  // 2026-05-30 (#19): Starter gets a reduced GBP2/lead rate (Alexander Loom 6).
  const _leadRate = (tierId === 'starter') ? 2 : LEAD_PRICE_PER_UNIT;
  if (additional === 0) return { additional: 0, cost: 0, included };
  const cost = Math.round(additional * _leadRate * (1 - LEAD_BULK_DISCOUNT));
  return { additional, cost, included };
}
// Back-compat: LEAD_INCLUDED_PER_TIER / LEAD_VOLUME_MIN exported for any
// caller still referring to the old constants. Both resolve to the Starter
// floor (750), which keeps existing math correct when no tier is picked yet.
const LEAD_INCLUDED_PER_TIER = LEAD_INCLUDED_DEFAULT;
const LEAD_VOLUME_MIN = LEAD_INCLUDED_DEFAULT;
window.LEAD_PRICE_PER_UNIT = LEAD_PRICE_PER_UNIT;
window.LEAD_BULK_DISCOUNT = LEAD_BULK_DISCOUNT;
window.LEAD_INCLUDED_BY_TIER = LEAD_INCLUDED_BY_TIER;
window.LEAD_INCLUDED_PER_TIER = LEAD_INCLUDED_PER_TIER;
window.LEAD_VOLUME_MIN = LEAD_VOLUME_MIN;
window.LEAD_VOLUME_MAX = LEAD_VOLUME_MAX;
window.leadIncludedForTier = leadIncludedForTier;
window.computeAdditionalLeadCost = computeAdditionalLeadCost;

