/* WP Site Beam — WP Scope Estimator */

/* Hosting plan library — pre-filled from the live price sheet.
   cost/profit/margin carried through so Plan Comparison matches the
   legacy estimator exactly. */
const CD_PLANS = {
  /* ── M plans — hosting only, no care ─────────────────────────── */
  M: [
    { code:'CD-M-B2', label:'B2', support:'—', charge:100, cost:33.26, profit:66.74,  margin:66.7, hosting:15,  cats:['business'],                                 inc:'Business hosting · No care' },
    { code:'CD-M-B3', label:'B3', support:'—', charge:135, cost:44.59, profit:90.41,  margin:67.0, hosting:25,  cats:['business'],                                 inc:'Business+ hosting · No care' },
    { code:'CD-M-S2', label:'S2', support:'—', charge:165, cost:55.73, profit:109.27, margin:66.2, hosting:35,  cats:['business','ecommerce','lms','social'],     inc:'Standard hosting · No care' },
    { code:'CD-M-S3', label:'S3', support:'—', charge:195, cost:66.87, profit:128.13, margin:65.7, hosting:45,  cats:['business','ecommerce','lms','social'],     inc:'Standard+ hosting · No care' },
    { code:'CD-M-G2', label:'G2', support:'—', charge:527, cost:184.49,profit:342.51, margin:65.0, hosting:150, cats:['ecommerce','lms','social'],                inc:'Growth+ hosting · No care' },
    { code:'CD-M-G3', label:'G3', support:'—', charge:195, cost:66.87, profit:128.13, margin:65.7, hosting:45,  cats:['business'],                                 inc:'Growth tier · No care' },
    { code:'CD-M-P2', label:'P2', support:'—', charge:525, cost:234.41,profit:290.59, margin:55.4, hosting:200, cats:['ecommerce','lms','social'],                inc:'Pro+ hosting · No care' },
    { code:'CD-M-U2', label:'U2', support:'—', charge:725, cost:342.01,profit:382.99, margin:52.8, hosting:300, cats:['ecommerce','lms','social'],                inc:'Ultimate+ hosting · No care' },
  ],
  /* ── MWC plans — hosting + care bundled ──────────────────────── */
  MWC: [
    { code:'CD-MWC-B2-15', label:'B2', support:'15 min', charge:125, cost:34.21, profit:90.79,  margin:72.6, hosting:15,  cats:['business'],                                 inc:'Business + 15 min/mo' },
    { code:'CD-MWC-B3-15', label:'B3', support:'15 min', charge:155, cost:45.35, profit:109.65, margin:70.7, hosting:25,  cats:['business'],                                 inc:'Business+ + 15 min/mo' },
    { code:'CD-MWC-B3-30', label:'B3', support:'30 min', charge:185, cost:57.12, profit:127.88, margin:69.1, hosting:25,  cats:['business'],                                 inc:'Business+ + 30 min/mo' },
    { code:'CD-MWC-S2-15', label:'S2', support:'15 min', charge:185, cost:83.36, profit:101.64, margin:54.9, hosting:35,  cats:['business','ecommerce','lms','social'],     inc:'Standard+ + 15 min/mo' },
    { code:'CD-MWC-S3-15', label:'S3', support:'15 min', charge:215, cost:94.50, profit:120.50, margin:56.1, hosting:45,  cats:['business','ecommerce','lms','social'],     inc:'Standard Pro + 15 min/mo' },
    { code:'CD-MWC-G2-15', label:'G2', support:'15 min', charge:445, cost:213.26,profit:231.74, margin:52.1, hosting:150, cats:['ecommerce','lms','social'],                inc:'Growth+ + 15 min/mo' },
    { code:'CD-MWC-P2-15', label:'P2', support:'15 min', charge:595, cost:279.59,profit:315.41, margin:53.0, hosting:200, cats:['ecommerce','lms','social'],                inc:'Pro+ + 15 min/mo' },
    { code:'CD-MWC-U2-15', label:'U2', support:'15 min', charge:895, cost:390.99,profit:504.01, margin:56.3, hosting:300, cats:['ecommerce','lms','social'],                inc:'Ultimate+ + 15 min/mo' },
    { code:'CD-MWC-T1-15', label:'T1', support:'15 min', charge:1195,cost:584.53,profit:610.47, margin:51.1, hosting:500, cats:['ecommerce','lms','social'],                inc:'Enterprise + 15 min/mo' },
  ],
  CARE_ONLY: [
    { code:'CD-CARE-S1', label:'Support Starter', support:'30 min/mo',  charge:49,  inc:'30 min support · WP updates · uptime monitoring',       bestFor:'Personal/brochure sites' },
    { code:'CD-CARE-S2', label:'Support Basic',   support:'60 min/mo',  charge:79,  inc:'1 hr support · WP updates · backups · uptime',          bestFor:'Small business sites' },
    { code:'CD-CARE-S3', label:'Support Standard',support:'90 min/mo',  charge:119, inc:'90 min support · WP updates · backups · security scans',bestFor:'Active business sites' },
    { code:'CD-CARE-P1', label:'Support Pro',     support:'120 min/mo', charge:179, inc:'2 hr support · full managed care · priority response',  bestFor:'eCommerce / high-traffic' },
    { code:'CD-CARE-E1', label:'Support Elite',   support:'240 min/mo', charge:299, inc:'4 hr support · dedicated care · SLA response',          bestFor:'Enterprise / mission-critical' },
  ],
  CIVIC: [
    { code:'CIVIC-STARTER',      label:'Civic Starter',       pop:'Under 300',          pages:'5–15',     buildMin:5950,  buildMax:7950,  support:'30 min',  charge:249,  inc:'30 min support · Civic stack' },
    { code:'CIVIC-COMMUNITY',    label:'Civic Community',      pop:'300–5,000',          pages:'15–40',    buildMin:5950,  buildMax:8950,  support:'45 min',  charge:295,  inc:'45 min support · Doc storage' },
    { code:'CIVIC-ESSENTIAL',    label:'Civic Essential',      pop:'5,000–10,000',       pages:'25–75',    buildMin:8950,  buildMax:12500, support:'60 min',  charge:425,  inc:'60 min support · Full civic' },
    { code:'CIVIC-PROFESSIONAL', label:'Civic Professional',   pop:'10,000–25,000',      pages:'40–100',   buildMin:12500, buildMax:18500, support:'60 min',  charge:500,  inc:'60 min support · Gov tools' },
    { code:'CIVIC-ENTERPRISE',   label:'Civic Enterprise',     pop:'25,000–60,000',      pages:'75–150',   buildMin:18500, buildMax:26500, support:'90 min',  charge:875,  inc:'90 min support · Gov suite' },
    { code:'CIVIC-GOVPLUS',      label:'Civic Gov Plus',       pop:'60,000+',            pages:'100–250',  buildMin:26500, buildMax:35000, support:'120 min', charge:1095, inc:'120 min support · Full gov' },
    { code:'CIVIC-GOVENT',       label:'Civic Gov Enterprise', pop:'County / Multi-Dept',pages:'150–400+', buildMin:35000, buildMax:99000, support:'120 min', charge:1295, inc:'120 min support · Max gov' },
  ],
};
window.CD_PLANS = CD_PLANS;

function Estimator() {
  const { useState, useMemo, useEffect } = React;
  const [s] = window.useWPSBD();
  const [tab, setTab] = useState('estimate');
  const cart = (window.useProposalCart && window.useProposalCart()) || { items: [], tokens: {}, narrative: {} };
  const adaItems = (cart.items || []).filter(i => i.source === 'ada-scan' && i.included !== false);
  const [siteType, setSiteType] = useState('business');
  const [pages, setPages] = useState(12);
  const [popTier, setPopTier] = useState(1);
  const [tier, setTier] = useState('medium');
  const [addons, setAddons] = useState({
    cpt:true, smtp:true, seo:false, gbp:false, ssl:true, migration:true, training:false, woocommerce:false, membership:false,
    docLibrary:false, minutes:false, agenda:false, accessibility:false,
  });
  const [monthly, setMonthly] = useState({
    cloudflare:false, ada:false, plugin1:false, plugin2:false, retainer:false, appCivic:false,
  });
  const [hours, setHours] = useState(0);
  const [editing, setEditing] = useState(false);
  const [docLoad, setDocLoad] = useState('none');
  const [complexity, setComplexity] = useState('standard');
  const [rush, setRush] = useState('standard');

  // Auto-detected items from scanner (url + detected list)
  const [autoDetected, setAutoDetected] = useState({
    scannedSite: null,          // e.g. 'republiccounty.org'
    detected: [],               // [{ key, label, price, reason }]
  });

  // White Label & Branding (pre-filled from Agency Profile, customizable per-proposal)
  // Agency Plan+ feature
  const [brandOpen, setBrandOpen] = useState(false);
  const [brand, setBrand] = useState(() => {
    // Pulls from global brand profile (Account Settings → Brand)
    try {
      const saved = JSON.parse(localStorage.getItem('wpsb-agency-brand') || '{}');
      return {
        agencyName: 'Your Agency',
        tagline:    'WordPress care done right',
        primary:    'var(--orange)',
        accent:     'var(--beam)',
        logoUrl:    '',
        fontHead:   'Orbitron',
        fontBody:   'DM Sans',
        clientName: '',
        clientEmail:'',
        proposalFooter: '',
        ...saved,
      };
    } catch(e) {
      return { agencyName:'', tagline:'', primary:'var(--orange)', accent:'var(--beam)', logoUrl:'', fontHead:'Orbitron', fontBody:'DM Sans', clientName:'', clientEmail:'', proposalFooter:'' };
    }
  });
  function saveBrandGlobal() {
    try { localStorage.setItem('wpsb-agency-brand', JSON.stringify(brand)); } catch(e){}
  }

  const docLoadOpts = [
    { id:'none',     lbl:'None',     desc:'No docs',           mult:1.0  },
    { id:'light',    lbl:'Light',    desc:'Basic PDFs',        mult:1.0  },
    { id:'moderate', lbl:'Moderate', desc:'Depts + agendas',   mult:1.15 },
    { id:'heavy',    lbl:'Heavy',    desc:'Large archives',    mult:1.325},
  ];
  const complexityOpts = [
    { id:'standard', lbl:'Standard', mult:1.0 },
    { id:'complex',  lbl:'Complex',  mult:1.25 },
    { id:'high',     lbl:'High',     mult:1.5 },
  ];
  const rushOpts = [
    { id:'standard', lbl:'Standard', mult:1.0 },
    { id:'2weeks',   lbl:'2 Weeks',  mult:1.25 },
    { id:'1week',    lbl:'1 Week',   mult:1.5 },
  ];

  // Listen for Scanner → Estimator push payloads
  useEffect(() => {
    try {
      const raw = sessionStorage.getItem('wpsb_estimator_payload');
      if (raw) {
        const p = JSON.parse(raw);
        if (p.siteType) setSiteType(p.siteType);
        if (typeof p.pages === 'number') setPages(p.pages);
        if (p.tier) setTier(p.tier);
        if (p.popTier !== undefined) setPopTier(p.popTier);
        if (p.detected) {
          setAutoDetected({ scannedSite: p.scannedSite || 'acme.co', detected: p.detected });
          const pre = {};
          p.detected.forEach(d => { pre[d.key] = true; });
          setAddons(a => ({...a, ...pre}));
        }
        sessionStorage.removeItem('wpsb_estimator_payload');
      } else {
        // Demo mode: show auto-detected state for illustration
        setAutoDetected({
          scannedSite: 'republiccounty.org',
          detected: [
            { key:'ssl',           label:'SSL Install + Configuration',      price:150, reason:'No HTTPS detected' },
            { key:'migration',     label:'Content Migration (from old site)',price:650, reason:'44 pages on legacy CMS' },
            { key:'accessibility', label:'ADA / WCAG 2.1 AA Remediation',    price:675, reason:'Civic classification · 18 ADA issues' },
            { key:'docLibrary',    label:'Document Library Setup (Civic)',   price:850, reason:'312 PDFs on /documents' },
          ],
        });
        setAddons(a => ({...a, ssl:true, migration:true, accessibility:true, docLibrary:true}));
      }
    } catch(e){}
  }, []);

  const isCivic = siteType === 'civic';

  // Editable site classifications — matches old system
  const [siteTypes, setSiteTypes] = useState([
    { id:'business',   lbl:'Business',       emoji:'🏢', rate:95  },
    { id:'lms',        lbl:'LMS',            emoji:'🎓', rate:140 },
    { id:'ecommerce',  lbl:'eCommerce',      emoji:'🛒', rate:125 },
    { id:'social',     lbl:'Social',         emoji:'📱', rate:115 },
    { id:'civic',      lbl:'Civic / Gov',    emoji:'🏛️', rate:105 },
  ]);

  // Editable business tiers — matches old system: 5 tiers with hrs + page ranges.
  // priceMin/priceMax drive the Build Estimate range; pageMin/pageMax + hrsMin/hrsMax
  // feed the hours & page-count readout.
  const [bizTiers, setBizTiers] = useState([
    { id:'starter', lbl:'Starter',    basePrice:1950,  priceMin:1900,  priceMax:2900,  care:79,  pageMin:5,  pageMax:10,  hrsMin:9,  hrsMax:17,  pages:'5–10 pages',   hrs:'9–17 hrs',   desc:'Small brochure / solo' },
    { id:'small',   lbl:'Small',      basePrice:2950,  priceMin:2900,  priceMax:4200,  care:119, pageMin:10, pageMax:20,  hrsMin:17, hrsMax:24,  pages:'10–20 pages',  hrs:'17–24 hrs',  desc:'Small business' },
    { id:'medium',  lbl:'Medium',     basePrice:4850,  priceMin:4200,  priceMax:6900,  care:159, pageMin:20, pageMax:40,  hrsMin:31, hrsMax:46,  pages:'20–40 pages',  hrs:'31–46 hrs',  desc:'Growing business' },
    { id:'large',   lbl:'Large',      basePrice:7850,  priceMin:6900,  priceMax:10800, care:199, pageMin:40, pageMax:70,  hrsMin:31, hrsMax:68,  pages:'40–70 pages',  hrs:'31–68 hrs',  desc:'Established / complex' },
    { id:'custom',  lbl:'Enterprise', basePrice:12850, priceMin:10800, priceMax:18500, care:299, pageMin:70, pageMax:125, hrsMin:68, hrsMax:111, pages:'70–125 pages', hrs:'68–111 hrs', desc:'Enterprise / integrations' },
  ]);

  // Editable Civic plans (client-side copy so user edits don't mutate global)
  const [civicPlans, setCivicPlans] = useState(() => CD_PLANS.CIVIC.map(p => ({...p})));

  // Per-page adder rate (editable)
  const [perPageRate, setPerPageRate] = useState(85);

  function updateSiteRate(id, rate) {
    setSiteTypes(prev => prev.map(s => s.id === id ? {...s, rate} : s));
  }
  function updateTier(id, patch) {
    setBizTiers(prev => prev.map(t => t.id === id ? {...t, ...patch} : t));
  }
  function updateCivic(i, patch) {
    setCivicPlans(prev => prev.map((p, idx) => idx === i ? {...p, ...patch} : p));
  }

  const bizSetupAddons = [
    ['cpt',          'Custom Post Type Setup + Migration',     450],
    ['smtp',         'SMTP / Email Deliverability Setup',      225],
    ['seo',          'SEO Plugin Setup + Configuration',       350],
    ['gbp',          'Google Business Profile Setup',          275],
    ['ssl',          'SSL Install + Configuration',            150],
    ['migration',    'Content Migration (from old site)',      650],
    ['training',     'Client Training Session (90 min)',       225],
    ['woocommerce',  'WooCommerce Setup (up to 25 products)', 1250],
    ['membership',   'Membership / LMS Integration',          1850],
  ];
  const civicSetupAddons = [
    ['docLibrary',   'Document Library Setup (Civic)',         850],
    ['minutes',      'Commission Minutes Import',              450],
    ['agenda',       'Agenda Management System',               950],
    ['accessibility','ADA / WCAG 2.1 AA Remediation',          675],
    ['ssl',          'SSL Install + Configuration',            150],
    ['migration',    'Content Migration (from old site)',      650],
    ['training',     'Staff Training Session (90 min)',        225],
  ];
  const addonList = isCivic ? civicSetupAddons : bizSetupAddons;

  const monthlyList = [
    ['cloudflare', 'Cloudflare Pro Integration',  20],
    ['ada',        'Accessibility (ADA/WCAG) Tool', 30],
    ['plugin1',    'Custom Plugin License #1',     19],
    ['plugin2',    'Custom Plugin License #2',     12],
    ['retainer',   'Extended Care Retainer',       150],
    ...(isCivic ? [['appCivic', 'Web-to-App — Civic Plan', 249]] : []),
  ];

  const totals = useMemo(() => {
    let base, baseMin, baseMax, hrsMin, hrsMax, pageMin, pageMax, scopeLabel;
    if (isCivic) {
      const plan = civicPlans[popTier] || civicPlans[1];
      base = Math.round((plan.buildMin + plan.buildMax) / 2);
      baseMin = plan.buildMin;
      baseMax = plan.buildMax;
      hrsMin = plan.hrsMin || Math.round(plan.buildMin / 105);
      hrsMax = plan.hrsMax || Math.round(plan.buildMax / 105);
      const pp = (plan.pages || '').split('–');
      pageMin = Number(pp[0]) || 0;
      pageMax = Number((pp[1] || '').replace(/[^0-9]/g,'')) || pageMin;
      scopeLabel = plan.label;
    } else {
      const t = bizTiers.find(x => x.id === tier) || bizTiers[2];
      base = t.basePrice;
      const extraPages = Math.max(0, pages - 8) * perPageRate;
      baseMin = t.priceMin + extraPages;
      baseMax = t.priceMax + extraPages;
      base    = base      + extraPages;
      hrsMin  = t.hrsMin;
      hrsMax  = t.hrsMax;
      pageMin = t.pageMin;
      pageMax = t.pageMax;
      scopeLabel = t.lbl;
    }
    const addonSetup = addonList.reduce((s, [k,,p]) => s + (addons[k] ? p : 0), 0);
    const monthlyAddon = monthlyList.reduce((s, [k,,p]) => s + (monthly[k] ? p : 0), 0);
    const care = isCivic ? (civicPlans[popTier] || civicPlans[1]).charge
                         : bizTiers.find(t => t.id === tier).care;

    // ADA remediation totals — pulled from ProposalCart
    const adaLow  = adaItems.filter(i => !i.recurring).reduce((s,i) => s + (i.lowPrice  || 0), 0);
    const adaHigh = adaItems.filter(i => !i.recurring).reduce((s,i) => s + (i.highPrice || 0), 0);
    const adaMo   = adaItems.filter(i =>  i.recurring).reduce((s,i) => s + (i.lowPrice  || 0), 0);

    const docMult = isCivic ? (docLoadOpts.find(o => o.id === docLoad)?.mult || 1) : 1;
    const complexMult = complexityOpts.find(o => o.id === complexity)?.mult || 1;
    const rushMult    = rushOpts.find(o => o.id === rush)?.mult || 1;
    const totalMult   = docMult * complexMult * rushMult;

    const rawSetup    = base    + addonSetup;
    const rawSetupMin = baseMin + addonSetup;
    const rawSetupMax = baseMax + addonSetup;
    const adjustedSetup    = Math.round(rawSetup    * totalMult) + adaLow;
    const adjustedSetupMin = Math.round(rawSetupMin * totalMult) + adaLow;
    const adjustedSetupMax = Math.round(rawSetupMax * totalMult) + adaHigh;

    return {
      setup: adjustedSetup,
      setupMin: adjustedSetupMin,
      setupMax: adjustedSetupMax,
      rawSetup,
      monthly: care + monthlyAddon + hours * 90 + adaMo,
      careBase: care,
      monthlyAddon,
      addonSetup,
      adaLow, adaHigh, adaMo,
      adaCount: adaItems.length,
      base, baseMin, baseMax,
      hrsMin, hrsMax, pageMin, pageMax, scopeLabel,
      docMult, complexMult, rushMult, totalMult,
    };
  }, [tier, popTier, pages, addons, monthly, hours, siteType, bizTiers, civicPlans, perPageRate, docLoad, complexity, rush, adaItems]);

  /* ── Recommended plans: bottom-of-category (MIN) then next two
     business-cat matches, hidden items filtered out. Matches
     legacy estimator logic. ───────────────────────────────── */
  const recommend = useMemo(() => {
    const cat = isCivic ? 'business' : siteType;  // civic uses its own care via CIVIC array
    const matches = arr => arr.filter(p => !p.hidden && (!p.cats || p.cats.length === 0 || p.cats.includes(cat)))
                             .sort((a,b) => a.charge - b.charge);
    const mList = matches(CD_PLANS.M).slice(0, 3);
    const mwcList = matches(CD_PLANS.MWC).slice(0, 3);
    return {
      hosting: mList,
      care:    mwcList,
      hostingRange: mList.length ? [mList[0].charge, mList[mList.length-1].charge] : null,
      careRange:    mwcList.length ? [mwcList[0].charge, mwcList[mwcList.length-1].charge] : null,
    };
  }, [siteType, isCivic]);

  return (
    <div>
      <PageHead crumb="Operations" title="WP Scope Estimator"
        sub="Project cost estimator · Agency service catalog · Proposal generator."
        actions={<>
          <button className="btn btn-ghost btn-sm"><Icon name="download" size={13}/>Save preset</button>
          <button className="btn btn-primary btn-sm" onClick={() => setTab('proposal')}><Icon name="spark" size={13}/>Generate proposal</button>
        </>}/>

      <div className="sub-tabs" role="tablist">
        {[
          ['estimate','Estimator'],
          ['pagegap','Page Gap'],
          ['catalog','Service Catalog'],
          ['hosting','Hosting Plans'],
          ['rules','Trigger Rules'],
          ['proposal','Proposal'],
          ['email','Email Templates'],
        ].map(([id, lbl]) => (
          <button key={id} role="tab" aria-selected={(tab===id)} tabIndex={(tab===id)?0:-1} className={'sub-tab' + ((tab===id)?' active':'')} onClick={() => setTab(id)}>{lbl}</button>
        ))}
      </div>

      {tab === 'estimate' && (
        <div className="grid" style={{ gridTemplateColumns:'1fr 340px', marginTop:16, alignItems:'start', gridAutoRows:'auto' }}>
          <div>
            {/* White Label & Branding (Agency Plan+) */}
            <div className="card" style={{ marginBottom:14, borderColor: brandOpen ? 'var(--orange)' : 'var(--border)' }}>
              <div className="card-head" style={{ cursor:'pointer' }} onClick={() => setBrandOpen(v => !v)}>
                <h2 className="card-title" style={{ display:'flex', alignItems:'center', gap:10, flexWrap:'wrap' }}>
                  <span style={{ width:28, height:28, borderRadius:6, background:`linear-gradient(135deg, ${brand.primary}, ${brand.accent})`, display:'flex', alignItems:'center', justifyContent:'center', color:'#000', fontSize:'.7rem', fontWeight:800, fontFamily:'var(--font-brand)' }} aria-hidden="true">
                    {brand.agencyName.split(' ').map(w => w[0]).join('').slice(0,2)}
                  </span>
                  <span>White Label &amp; Branding</span>
                  <span className="tag sa">AGENCY PLAN+</span>
                  <span style={{ fontSize:'.72rem', color:'var(--dim)', fontFamily:'var(--font-mono)' }}>· {brand.agencyName}</span>
                </h2>
                <button className="btn btn-ghost btn-sm" onClick={e => { e.stopPropagation(); setBrandOpen(v => !v); }}>
                  <Icon name="chevron" size={12}/>{brandOpen ? 'Collapse' : 'Customize'}
                </button>
              </div>
              {brandOpen && (
                <div className="card-body">
                  <div className="box info" style={{ marginBottom:12, padding:'8px 12px', fontSize:'.78rem' }}>
                    <Icon name="info" size={12}/>
                    <div>
                      Pre-filled from <a href="#" onClick={e => { e.preventDefault(); window.WPSBD.switchTab('account'); }} style={{ color:'var(--beam)' }}>Agency Profile → Brand</a>. Overrides here apply to <strong>this proposal only</strong>.
                    </div>
                  </div>

                  {/* Gallery layout — preview left, compact form right */}
                  <div style={{ display:'grid', gridTemplateColumns:'260px 1fr', gap:14, alignItems:'stretch' }} className="brand-gallery">
                    {/* LEFT — live preview card */}
                    <div style={{ padding:14, borderRadius:10, background:'#fff', border:'1px solid var(--border)', display:'flex', flexDirection:'column', gap:10, minWidth:0 }}>
                      <div style={{ display:'flex', gap:10, alignItems:'center' }}>
                        <div style={{ width:40, height:40, borderRadius:8, background: brand.logoUrl ? `url(${brand.logoUrl}) center/contain no-repeat #fff` : `linear-gradient(135deg, ${brand.primary}, ${brand.accent})`, display:'flex', alignItems:'center', justifyContent:'center', color:'#000', fontWeight:800, fontFamily:`${brand.fontHead}, sans-serif`, fontSize:'.78rem', flexShrink:0, border: brand.logoUrl ? '1px solid #e5e7eb' : 'none' }}>
                          {!brand.logoUrl && brand.agencyName.split(' ').map(w=>w[0]).join('').slice(0,2)}
                        </div>
                        <div style={{ flex:1, minWidth:0 }}>
                          <div style={{ fontFamily:`${brand.fontHead}, sans-serif`, fontWeight:700, fontSize:'.88rem', color:'#0F172A', whiteSpace:'nowrap', overflow:'hidden', textOverflow:'ellipsis' }}>{brand.agencyName}</div>
                          <div style={{ fontFamily:`${brand.fontBody}, sans-serif`, fontSize:'.68rem', color:'var(--dim)', whiteSpace:'nowrap', overflow:'hidden', textOverflow:'ellipsis' }}>{brand.tagline || '—'}</div>
                        </div>
                      </div>
                      <div style={{ display:'flex', gap:6 }}>
                        <div style={{ width:26, height:26, borderRadius:4, background:brand.primary, border:'1px solid rgba(0,0,0,.08)' }} title={`Primary ${brand.primary}`}/>
                        <div style={{ width:26, height:26, borderRadius:4, background:brand.accent, border:'1px solid rgba(0,0,0,.08)' }} title={`Accent ${brand.accent}`}/>
                        <div style={{ flex:1, padding:'4px 8px', background:'#F8FAFC', border:'1px solid #e5e7eb', borderRadius:4, fontSize:'.64rem', color:'var(--dim)', display:'flex', alignItems:'center', fontFamily:'var(--font-mono)' }}>
                          {brand.fontHead} · {brand.fontBody}
                        </div>
                      </div>
                      <div style={{ padding:'8px 10px', background:'#F8FAFC', border:'1px solid #e5e7eb', borderRadius:6 }}>
                        <div style={{ fontFamily:`${brand.fontHead}, sans-serif`, fontWeight:700, fontSize:'.8rem', color:'#0F172A' }}>Proposal for {brand.clientName || 'Client'}</div>
                        <div style={{ fontFamily:`${brand.fontBody}, sans-serif`, fontSize:'.66rem', color:'var(--dim)', marginTop:2 }}>{brand.clientEmail || 'contact@client.co'}</div>
                      </div>
                      <button style={{ padding:'8px 12px', background: brand.primary, color:'#000', border:'none', borderRadius:6, fontFamily:`${brand.fontBody}, sans-serif`, fontWeight:700, fontSize:'.74rem', cursor:'pointer' }}>Accept Proposal</button>
                      <div style={{ fontSize:'.6rem', color:'var(--dim)', fontFamily:'var(--font-mono)', textAlign:'center', marginTop:'auto' }}>LIVE PREVIEW</div>
                    </div>

                    {/* RIGHT — compact control strip in 2-col grid */}
                    <div style={{ display:'grid', gridTemplateColumns:'1fr 1fr', gap:10, alignContent:'start', minWidth:0 }}>
                      <CompactField label="Agency name"><input value={brand.agencyName} onChange={e => setBrand({...brand, agencyName: e.target.value})}/></CompactField>
                      <CompactField label="Tagline"><input value={brand.tagline} onChange={e => setBrand({...brand, tagline: e.target.value})}/></CompactField>
                      <CompactField label="Client name"><input placeholder="Northwind Traders" value={brand.clientName} onChange={e => setBrand({...brand, clientName: e.target.value})}/></CompactField>
                      <CompactField label="Client email"><input placeholder="contact@client.co" value={brand.clientEmail} onChange={e => setBrand({...brand, clientEmail: e.target.value})}/></CompactField>
                      <CompactField label="Primary">
                        <div style={{ display:'flex', gap:4, alignItems:'center', minWidth:0 }}>
                          <input type="color" value={brand.primary} onChange={e => setBrand({...brand, primary: e.target.value})} style={{ width:30, height:28, padding:1, border:'1px solid var(--border)', background:'var(--surface-3)', borderRadius:4, flexShrink:0 }}/>
                          <input className="mono" value={brand.primary} onChange={e => setBrand({...brand, primary: e.target.value})} style={{ flex:1, minWidth:0, fontSize:'.72rem' }}/>
                        </div>
                      </CompactField>
                      <CompactField label="Accent">
                        <div style={{ display:'flex', gap:4, alignItems:'center', minWidth:0 }}>
                          <input type="color" value={brand.accent} onChange={e => setBrand({...brand, accent: e.target.value})} style={{ width:30, height:28, padding:1, border:'1px solid var(--border)', background:'var(--surface-3)', borderRadius:4, flexShrink:0 }}/>
                          <input className="mono" value={brand.accent} onChange={e => setBrand({...brand, accent: e.target.value})} style={{ flex:1, minWidth:0, fontSize:'.72rem' }}/>
                        </div>
                      </CompactField>
                      <CompactField label="Heading font"><FontPicker value={brand.fontHead} onChange={v => setBrand({...brand, fontHead: v})} ariaLabel="Heading font"/></CompactField>
                      <CompactField label="Body font"><FontPicker value={brand.fontBody} onChange={v => setBrand({...brand, fontBody: v})} ariaLabel="Body font"/></CompactField>
                      <CompactField label="Logo URL" span={2}>
                        <div style={{ display:'flex', gap:6, alignItems:'center', minWidth:0 }}>
                          <input placeholder="https://cdn.agency.co/logo.svg" value={brand.logoUrl} onChange={e => setBrand({...brand, logoUrl: e.target.value})} style={{ flex:1, minWidth:0 }}/>
                          <button className="btn btn-ghost btn-sm" style={{ flexShrink:0 }}>Upload</button>
                        </div>
                      </CompactField>
                      <CompactField label="Proposal footer" span={2}><input value={brand.proposalFooter} onChange={e => setBrand({...brand, proposalFooter: e.target.value})}/></CompactField>
                    </div>
                  </div>

                  <div style={{ display:'flex', gap:8, marginTop:12, flexWrap:'wrap', alignItems:'center' }}>
                    <button className="btn btn-ghost btn-sm" onClick={saveBrandGlobal}>
                      <Icon name="check" size={12}/>Save as global default
                    </button>
                    <button className="btn btn-ghost btn-sm" onClick={() => { try { localStorage.removeItem('wpsb-agency-brand'); } catch(e){} setBrand(b => ({...b, agencyName:'', primary:'var(--orange)', accent:'var(--beam)'})); }}>Reset to global</button>
                    <span style={{ flex:1 }}/>
                    <span className="mono" style={{ fontSize:'.66rem', color:'var(--dim)' }}>
                      WPSiteBeam branding never shown to client ✓
                    </span>
                  </div>
                </div>
              )}
            </div>

            {/* Site Classification */}
            <div className="card">
              <div className="card-head">
                <EditableTitle id="estimator-site-classification" role={s.role}>1. Site Classification</EditableTitle>
                <div style={{ display:'flex', gap:6, alignItems:'center' }}>
                  <span style={{ fontSize:'.68rem', color:'var(--dim)', fontFamily:'var(--font-mono)' }}>Rates editable</span>
                  <button className="btn btn-ghost btn-sm" onClick={() => setEditing(v => !v)} title="Toggle rate editing">
                    <Icon name={editing ? 'check' : 'sliders'} size={12}/>{editing ? 'Done' : 'Edit rates'}
                  </button>
                </div>
              </div>
              <div className="card-body">
                <div className="opt-grid">
                  {siteTypes.map(s => (
                    <button key={s.id} className={'opt-btn' + (siteType === s.id ? ' selected' : '')} onClick={() => setSiteType(s.id)}>
                      <div style={{ fontSize:'1.4rem', lineHeight:1, marginBottom:6 }}>{s.emoji}</div>
                      <div style={{ fontWeight:700, fontSize:'.86rem' }}>{s.lbl}</div>
                      {editing ? (
                        <div style={{ display:'flex', alignItems:'center', gap:4, marginTop:6 }} onClick={e => e.stopPropagation()}>
                          <span className="mono" style={{ fontSize:'.66rem', color:'var(--dim)' }}>$</span>
                          <input type="number" value={s.rate} onChange={e => updateSiteRate(s.id, Number(e.target.value))}
                            style={{ width:52, padding:'2px 4px', fontSize:'.7rem', textAlign:'center', background:'var(--surface)', border:'1px solid var(--orange)', borderRadius:4, color:'var(--text)', fontFamily:'var(--font-mono)' }}/>
                          <span className="mono" style={{ fontSize:'.66rem', color:'var(--dim)' }}>/hr</span>
                        </div>
                      ) : (
                        <div className="mono" style={{ fontSize:'.66rem', color:'var(--dim)', marginTop:4 }}>${s.rate}/hr blended</div>
                      )}
                    </button>
                  ))}
                </div>

                <div style={{ display:'grid', gridTemplateColumns:'1fr 1fr 1fr', gap:14, marginTop:16 }}>
                  <div className="field" style={{ margin:0 }}>
                    <label>{isCivic ? 'Content pages' : 'Page count'}</label>
                    <input type="number" value={pages} min={1} max={500} onChange={e => setPages(Number(e.target.value))}/>
                  </div>
                  {!isCivic && (
                    <div className="field" style={{ margin:0 }}>
                      <label>Per-page adder</label>
                      <div style={{ display:'flex', alignItems:'center', gap:4 }}>
                        <span className="mono" style={{ fontSize:'.72rem', color:'var(--dim)' }}>$</span>
                        <input type="number" value={perPageRate} onChange={e => setPerPageRate(Number(e.target.value))} disabled={!editing} style={{ flex:1 }}/>
                      </div>
                    </div>
                  )}
                  <div className="field" style={{ margin:0 }}>
                    <label>Client URL (optional)</label>
                    <input placeholder="https://acmecorp.com" />
                  </div>
                </div>
              </div>
            </div>

            {/* Civic: Population tier OR Business: Build tier */}
            {isCivic ? (
              <div className="card" style={{ marginTop:14 }}>
                <div className="card-head">
                  <EditableTitle id="estimator-population-tier" role={s.role}>2. Population Tier — Civic</EditableTitle>
                  <span className="tag">{civicPlans.length} TIERS</span>
                </div>
                <div className="card-body">
                  <div className="box info" style={{ marginBottom:12 }}>
                    <Icon name="info" size={14}/>
                    <div>Civic builds are priced by <strong>population served</strong>, not page count. Auto-populated when scanner detects civic classification.</div>
                  </div>
                  <div className="opt-grid">
                    {civicPlans.map((p, i) => (
                      <button key={p.code} className={'opt-btn tier' + (popTier === i ? ' selected' : '')} onClick={() => setPopTier(i)}>
                        <div style={{ fontWeight:700, fontSize:'.88rem' }}>{p.label}</div>
                        <div className="mono" style={{ fontSize:'.68rem', color:'var(--beam)', marginTop:3 }}>Pop: {p.pop}</div>
                        <div className="mono" style={{ fontSize:'.66rem', color:'var(--dim)', marginTop:3 }}>{p.pages} pages</div>
                        {editing ? (
                          <div style={{ display:'flex', flexDirection:'column', gap:3, marginTop:5 }} onClick={e => e.stopPropagation()}>
                            <div style={{ display:'flex', gap:3, alignItems:'center' }}>
                              <input type="number" value={p.buildMin} onChange={e => updateCivic(i, { buildMin: Number(e.target.value) })} style={estInput}/>
                              <span className="mono" style={{ fontSize:'.62rem', color:'var(--dim)' }}>–</span>
                              <input type="number" value={p.buildMax} onChange={e => updateCivic(i, { buildMax: Number(e.target.value) })} style={estInput}/>
                            </div>
                            <div style={{ display:'flex', gap:3, alignItems:'center' }}>
                              <span className="mono" style={{ fontSize:'.62rem', color:'var(--dim)' }}>+$</span>
                              <input type="number" value={p.charge} onChange={e => updateCivic(i, { charge: Number(e.target.value) })} style={estInput}/>
                              <span className="mono" style={{ fontSize:'.62rem', color:'var(--dim)' }}>/mo</span>
                            </div>
                          </div>
                        ) : (
                          <>
                            <div className="mono" style={{ fontSize:'.68rem', color:'var(--orange)', marginTop:3 }}>${p.buildMin.toLocaleString()}–${p.buildMax.toLocaleString()}</div>
                            <div className="mono" style={{ fontSize:'.64rem', color:'var(--muted)', marginTop:2 }}>+ ${p.charge}/mo · {p.support}</div>
                          </>
                        )}
                      </button>
                    ))}
                  </div>
                </div>
              </div>
            ) : (
              <div className="card" style={{ marginTop:14 }}>
                <div className="card-head">
                  <EditableTitle id="estimator-page-tier" role={s.role}>2. Page Tier — Build Size</EditableTitle>
                  <span className="tag">{bizTiers.length} TIERS</span>
                </div>
                <div className="card-body">
                  <div className="box info" style={{ marginBottom:12 }}>
                    <Icon name="info" size={14}/>
                    <div><strong>Content pages only</strong> — web policy pages (Privacy Policy, Terms, Disclaimer, Cookie Notice, EULA) are included in all plans.</div>
                  </div>
                  <div className="opt-grid">
                    {bizTiers.map(t => (
                      <button key={t.id} className={'opt-btn tier' + (tier === t.id ? ' selected' : '')} onClick={() => setTier(t.id)}>
                        <div style={{ fontWeight:700, fontSize:'.92rem' }}>{t.lbl}</div>
                        <div className="mono" style={{ fontSize:'.7rem', color:'var(--beam)', marginTop:4 }}>{t.pages}</div>
                        <div className="mono" style={{ fontSize:'.68rem', color:'var(--dim)', marginTop:2 }}>{t.hrs}</div>
                        {editing ? (
                          <div style={{ display:'flex', flexDirection:'column', gap:3, marginTop:6 }} onClick={e => e.stopPropagation()}>
                            <div style={{ display:'flex', gap:3, alignItems:'center' }}>
                              <span className="mono" style={{ fontSize:'.62rem', color:'var(--dim)' }}>$</span>
                              <input type="number" value={t.basePrice} onChange={e => updateTier(t.id, { basePrice: Number(e.target.value) })} style={estInput}/>
                            </div>
                            <div style={{ display:'flex', gap:3, alignItems:'center' }}>
                              <span className="mono" style={{ fontSize:'.62rem', color:'var(--dim)' }}>+$</span>
                              <input type="number" value={t.care} onChange={e => updateTier(t.id, { care: Number(e.target.value) })} style={estInput}/>
                              <span className="mono" style={{ fontSize:'.62rem', color:'var(--dim)' }}>/mo</span>
                            </div>
                          </div>
                        ) : (
                          <>
                            <div className="mono" style={{ fontSize:'.72rem', color:'var(--orange)', marginTop:5 }}>${t.basePrice.toLocaleString()}</div>
                            <div className="mono" style={{ fontSize:'.64rem', color:'var(--muted)', marginTop:2 }}>+ ${t.care}/mo care</div>
                          </>
                        )}
                      </button>
                    ))}
                  </div>
                </div>
              </div>
            )}

            {/* 3. Document Load Complexity — civic only */}
            {isCivic && (
              <div className="card" style={{ marginTop:14 }}>
                <div className="card-head">
                  <EditableTitle id="estimator-doc-load" role={s.role}>3. Document Load Complexity</EditableTitle>
                  <span className="tag rose">CIVIC</span>
                </div>
                <div className="card-body">
                  <div className="opt-grid">
                    {docLoadOpts.map(o => (
                      <button key={o.id} className={'opt-btn' + (docLoad === o.id ? ' selected' : '')} onClick={() => setDocLoad(o.id)}>
                        <div style={{ fontWeight:700, fontSize:'.9rem' }}>{o.lbl}</div>
                        <div style={{ fontSize:'.72rem', color:'var(--dim)', marginTop:4 }}>{o.desc}</div>
                        <div className="mono" style={{ fontSize:'.72rem', color:'var(--orange)', marginTop:6 }}>×{o.mult.toFixed(o.mult % 1 === 0 ? 1 : o.mult.toString().split('.')[1].length)}</div>
                      </button>
                    ))}
                  </div>
                </div>
              </div>
            )}

            {/* Project Multipliers */}
            <div className="card" style={{ marginTop:14 }}>
              <div className="card-head">
                <EditableTitle id="estimator-multipliers" role={s.role}>{(isCivic ? '4.' : '3.') + ' Project Multipliers'}</EditableTitle>
                <span className="tag warn">×{totals.totalMult.toFixed(2)} total</span>
              </div>
              <div className="card-body" style={{ paddingTop:12, paddingBottom:12 }}>
                <div style={{ display:'flex', flexDirection:'column', gap:14 }}>
                  <div className="field" style={{ margin:0 }}>
                    <label style={{ marginBottom:6 }}>Complexity</label>
                    <div className="opt-grid mult-grid" style={{ gridTemplateColumns:'repeat(3, 1fr)' }}>
                      {complexityOpts.map(o => (
                        <button key={o.id} className={'opt-btn mult-pill' + (complexity === o.id ? ' selected' : '')} onClick={() => setComplexity(o.id)}>
                          <span style={{ fontWeight:700, fontSize:'.82rem' }}>{o.lbl}</span>
                          <span className="mono" style={{ fontSize:'.7rem', color:'var(--orange)' }}>×{o.mult.toFixed(2)}</span>
                        </button>
                      ))}
                    </div>
                  </div>
                  <div className="field" style={{ margin:0 }}>
                    <label style={{ marginBottom:6 }}>Rush Factor</label>
                    <div className="opt-grid mult-grid" style={{ gridTemplateColumns:'repeat(3, 1fr)' }}>
                      {rushOpts.map(o => (
                        <button key={o.id} className={'opt-btn mult-pill' + (rush === o.id ? ' selected' : '')} onClick={() => setRush(o.id)}>
                          <span style={{ fontWeight:700, fontSize:'.78rem' }}>{o.lbl}</span>
                          <span className="mono" style={{ fontSize:'.7rem', color:'var(--orange)' }}>×{o.mult.toFixed(2)}</span>
                        </button>
                      ))}
                    </div>
                  </div>
                </div>
              </div>
            </div>

            {/* Auto-Detected Services */}
            {autoDetected.detected.length > 0 && (
              <div className="card" style={{ marginTop:14, borderColor:'var(--beam)' }}>
                <div className="card-head">
                  <h2 className="card-title">
                    <span style={{ marginRight:6 }}>✨</span>Auto-Detected Services
                  </h2>
                  <span className="tag beam">{autoDetected.detected.length} DETECTED</span>
                </div>
                <div className="card-body" style={{ padding:0 }}>
                  <div style={{ padding:'12px 18px', borderBottom:'1px solid var(--border)', background:'var(--beam-dim)', fontSize:'.78rem', color:'var(--text-2)', lineHeight:1.5 }}>
                    These line items were added automatically based on what <strong style={{ color:'var(--beam)' }}>WP Beam Scan</strong> detected on <strong className="mono">{autoDetected.scannedSite}</strong>. Toggle any item to include or exclude.
                  </div>
                  {autoDetected.detected.map((d, i) => (
                    <div key={d.key} className="toggle-row" style={{ borderBottom: i<autoDetected.detected.length-1 ? '1px solid var(--border)' : 'none' }}>
                      <div style={{ flex:1, minWidth:0 }}>
                        <div style={{ display:'flex', alignItems:'center', gap:6, flexWrap:'wrap' }}>
                          <span style={{ fontWeight:600, fontSize:'.86rem' }}>{d.label}</span>
                          <span className="tag beam" style={{ fontSize:'.58rem' }}>AUTO</span>
                        </div>
                        <div className="mono" style={{ fontSize:'.68rem', color:'var(--dim)', marginTop:2 }}>${d.price} · {d.reason}</div>
                      </div>
                      <Toggle on={!!addons[d.key]} onChange={v => setAddons({...addons, [d.key]: v})}/>
                    </div>
                  ))}
                </div>
              </div>
            )}

            {/* From ADA Scan (Proposal Cart) — pulls remediation items from Scanner → ADA tab */}
            {adaItems.length > 0 && (
              <div className="card" style={{ marginTop:14, borderColor:'var(--green, #22c55e)' }}>
                <div className="card-head" style={{ alignItems:'flex-start' }}>
                  <div style={{ flex:1, minWidth:0 }}>
                    <h2 className="card-title" style={{ display:'flex', alignItems:'center', gap:8 }}>
                      <Icon name="shield" size={15}/>
                      From ADA Scan
                      <span className="tag ok">{adaItems.length} IN CART</span>
                    </h2>
                    <div style={{ fontSize:'.72rem', color:'var(--dim)', marginTop:4, lineHeight:1.45 }}>
                      Remediation items added from the <strong style={{ color:'var(--text)' }}>Scanner → ADA</strong> tab via <strong>+ Cart</strong> actions. Edit pricing &amp; scope here — changes flow through to the proposal.
                    </div>
                    <div style={{ marginTop:8 }}>
                      <VolatilityNotice variant="compact" scope="estimator"/>
                    </div>
                  </div>
                  <button className="btn btn-ghost btn-sm" onClick={() => window.WPSBD.switchTab('scanner')}>
                    <Icon name="scanner" size={12}/>Back to ADA scan
                  </button>
                </div>
                <div className="card-body" style={{ padding:0, overflowX:'auto' }}>
                  <table className="table" style={{ minWidth: 720 }}>
                    <thead>
                      <tr>
                        <th scope="col">Line item</th>
                        <th scope="col">Priority</th>
                        <th scope="col" style={{ textAlign:'right' }}>Low</th>
                        <th scope="col" style={{ textAlign:'right' }}>High</th>
                        <th scope="col" style={{ textAlign:'right' }}>Hrs</th>
                        <th scope="col" style={{ textAlign:'center' }}>Include</th>
                        <th scope="col"></th>
                      </tr>
                    </thead>
                    <tbody>
                      {(cart.items || []).filter(i => i.source === 'ada-scan').map((item) => (
                        <tr key={item.id} style={{ opacity: item.included === false ? 0.5 : 1 }}>
                          <td>
                            <div style={{ fontWeight:600, fontSize:'.84rem' }}>{item.label}</div>
                            {item.description && <div style={{ fontSize:'.68rem', color:'var(--dim)', marginTop:2 }}>{item.description}</div>}
                            {item.scope && <div className="mono" style={{ fontSize:'.66rem', color:'var(--muted)', marginTop:2 }}>{item.scope}</div>}
                          </td>
                          <td>
                            <span className={'tag ' + (item.priority === 'high' ? 'warn' : item.priority === 'low' ? '' : 'beam')}>
                              {(item.priority || 'medium').toUpperCase()}
                            </span>
                          </td>
                          <td style={{ textAlign:'right' }}>
                            <input type="number" value={item.lowPrice || 0}
                              onChange={e => window.ProposalCart.updateItem(item.id, { lowPrice: Number(e.target.value) })}
                              style={{ width:80, padding:'2px 6px', fontSize:'.76rem', textAlign:'right', background:'var(--surface)', border:'1px solid var(--border)', borderRadius:4, color:'var(--text)', fontFamily:'var(--font-mono)' }}/>
                          </td>
                          <td style={{ textAlign:'right' }}>
                            <input type="number" value={item.highPrice || 0}
                              onChange={e => window.ProposalCart.updateItem(item.id, { highPrice: Number(e.target.value) })}
                              style={{ width:80, padding:'2px 6px', fontSize:'.76rem', textAlign:'right', background:'var(--surface)', border:'1px solid var(--border)', borderRadius:4, color:'var(--text)', fontFamily:'var(--font-mono)' }}/>
                          </td>
                          <td style={{ textAlign:'right' }}>
                            <input type="number" value={item.hours || 0}
                              onChange={e => window.ProposalCart.updateItem(item.id, { hours: Number(e.target.value) })}
                              style={{ width:54, padding:'2px 6px', fontSize:'.76rem', textAlign:'right', background:'var(--surface)', border:'1px solid var(--border)', borderRadius:4, color:'var(--text)', fontFamily:'var(--font-mono)' }}/>
                          </td>
                          <td style={{ textAlign:'center' }}>
                            <Toggle on={item.included !== false} onChange={v => window.ProposalCart.updateItem(item.id, { included: v })}/>
                          </td>
                          <td style={{ textAlign:'right' }}>
                            <button className="btn btn-ghost btn-sm" onClick={() => window.ProposalCart.removeItem(item.id)} aria-label={`Remove ${item.label}`}>
                              <Icon name="x" size={12}/>
                            </button>
                          </td>
                        </tr>
                      ))}
                    </tbody>
                    <tfoot>
                      <tr style={{ background:'var(--surface-3)', fontWeight:700 }}>
                        <td colSpan={2}>ADA remediation subtotal</td>
                        <td style={{ textAlign:'right' }} className="mono">${totals.adaLow.toLocaleString()}</td>
                        <td style={{ textAlign:'right' }} className="mono">${totals.adaHigh.toLocaleString()}</td>
                        <td colSpan={3}></td>
                      </tr>
                    </tfoot>
                  </table>
                </div>
              </div>
            )}

            {/* Setup add-ons */}
            <div className="card" style={{ marginTop:14 }}>
              <div className="card-head">
                <h2 className="card-title">{isCivic ? '5.' : '4.'} Setup Add-ons {isCivic && <span style={{fontSize:'.68rem',color:'var(--rose)',marginLeft:6}}>— Civic stack</span>}</h2>
                <span className="tag setup">ONE-TIME</span>
              </div>
              <div className="card-body" style={{ padding: 0 }}>
                {addonList.map(([k, lbl, price], i) => (
                  <div key={k} className="toggle-row" style={{ borderBottom: i<addonList.length-1 ? '1px solid var(--border)' : 'none' }}>
                    <div style={{ flex:1 }}>
                      <div style={{ fontWeight:500, fontSize:'.86rem' }}>{lbl}</div>
                      <div className="mono" style={{ fontSize:'.68rem', color:'var(--dim)', marginTop:2 }}>${price}</div>
                    </div>
                    <Toggle on={!!addons[k]} onChange={v => setAddons({...addons, [k]: v})}/>
                  </div>
                ))}
              </div>
            </div>

            {/* Monthly add-ons */}
            <div className="card" style={{ marginTop:14 }}>
              <div className="card-head">
                <EditableTitle id="estimator-monthly-addons" role={s.role}>4. Monthly Add-ons</EditableTitle>
                <span className="tag ok">MONTHLY</span>
              </div>
              <div className="card-body" style={{ padding: 0 }}>
                {monthlyList.map(([k, lbl, price], i) => (
                  <div key={k} className="toggle-row" style={{ borderBottom: i<monthlyList.length-1 ? '1px solid var(--border)' : 'none' }}>
                    <div style={{ flex:1 }}>
                      <div style={{ fontWeight:500, fontSize:'.86rem' }}>{lbl}</div>
                      <div className="mono" style={{ fontSize:'.68rem', color:'var(--dim)', marginTop:2 }}>${price}/mo</div>
                    </div>
                    <Toggle on={!!monthly[k]} onChange={v => setMonthly({...monthly, [k]: v})}/>
                  </div>
                ))}
                <div className="toggle-row">
                  <div style={{ flex:1 }}>
                    <div style={{ fontWeight:500, fontSize:'.86rem' }}>Prepaid Hour Block (hrs/mo)</div>
                    <div className="mono" style={{ fontSize:'.68rem', color:'var(--dim)', marginTop:2 }}>$90/hr</div>
                  </div>
                  <input type="number" value={hours} min={0} max={40} onChange={e => setHours(Number(e.target.value))} style={{ width:70 }}/>
                </div>
              </div>
            </div>
          </div>

          {/* Summary sticky panel */}
          <div>
            <div style={{ position:'sticky', top:16, display:'flex', flexDirection:'column', gap:14 }}>
              <div className="card summary-card">
                <div className="card-head">
                  <h2 className="card-title">Build Estimate</h2>
                  <span className="tag beam">LIVE</span>
                </div>
                <div className="card-body">
                  {/* Big range + hours/pages */}
                  <div className="be-amount">${totals.setupMin.toLocaleString()} – ${totals.setupMax.toLocaleString()}</div>
                  <div className="be-meta">
                    <span>{totals.hrsMin}–{totals.hrsMax} hrs</span>
                    <span className="be-meta-dot">·</span>
                    <span>{totals.pageMin}–{totals.pageMax} pages</span>
                    <span className="be-meta-dot">·</span>
                    <span>{totals.scopeLabel}</span>
                  </div>
                  <div style={{ height:1, background:'var(--border)', margin:'14px 0 10px' }}/>

                  <div className="sum-row">
                    <span>⚡ Build: {totals.scopeLabel} ({totals.pageMin}–{totals.pageMax} pages)</span>
                    <span className="mono">${totals.baseMin.toLocaleString()}–${totals.baseMax.toLocaleString()}</span>
                  </div>
                  {totals.addonSetup > 0 && (
                    <div className="sum-row"><span>Setup add-ons</span><span className="mono">${totals.addonSetup.toLocaleString()}</span></div>
                  )}
                  {totals.adaCount > 0 && (
                    <div className="sum-row">
                      <span style={{ color:'var(--green, #22c55e)' }}>◆ ADA remediation ({totals.adaCount} items)</span>
                      <span className="mono" style={{ color:'var(--green, #22c55e)' }}>${totals.adaLow.toLocaleString()}–${totals.adaHigh.toLocaleString()}</span>
                    </div>
                  )}
                  {totals.totalMult !== 1 && (
                    <div className="sum-row">
                      <span>Multipliers</span>
                      <span className="mono" style={{ color:'var(--warn)' }}>×{totals.totalMult.toFixed(2)}</span>
                    </div>
                  )}
                  <div style={{ height:1, background:'var(--border)', margin:'10px 0' }}/>
                  <div className="sum-row big">
                    <span>Total estimate</span>
                    <span className="mono" style={{ color:'var(--orange)' }}>${totals.setupMin.toLocaleString()}–${totals.setupMax.toLocaleString()}</span>
                  </div>

                  <div style={{ display:'flex', flexDirection:'column', gap:8, marginTop:16, minWidth:0 }}>
                    <button className="btn btn-primary" onClick={() => setTab('proposal')}><Icon name="spark" size={13}/>Generate Proposal</button>
                    <button className="btn btn-ghost btn-sm" onClick={() => setTab('pagegap')}>View Page Gap →</button>
                    <button className="btn btn-ghost btn-sm" onClick={() => window.wpsbToast('Pushed to GHL / CRM as a new opportunity', 'ok')}>Push to GHL / CRM</button>
                  </div>
                </div>
              </div>

              {/* Recommended Hosting (3 lowest M plans matching site type) */}
              {recommend.hosting.length > 0 && (
                <div className="card">
                  <div className="card-head">
                    <h2 className="card-title">Recommended Hosting</h2>
                    <span className="tag">M PLANS</span>
                  </div>
                  <div className="card-body">
                    <div className="rec-sub mono">Fully-Managed Hosting (Backend Only)</div>
                    <div className="rec-range">${recommend.hostingRange[0]}–${recommend.hostingRange[1]}/mo</div>
                    <div className="rec-list">
                      {recommend.hosting.map((p, i) => (
                        <div key={p.code} className="rec-row">
                          <div>
                            <div className="rec-row-name">
                              <strong>{p.code}</strong>
                              {i === 0 && <span className="rec-badge">MIN</span>}
                            </div>
                            <div className="rec-row-inc">{p.inc}</div>
                          </div>
                          <div className="rec-row-price">
                            <div className="mono rec-price">${p.charge}/mo</div>
                            <div className="rec-margin">{Math.round(p.margin)}% margin</div>
                          </div>
                        </div>
                      ))}
                    </div>
                  </div>
                </div>
              )}

              {/* Recommended Care (3 lowest MWC plans matching site type) */}
              {recommend.care.length > 0 && (
                <div className="card">
                  <div className="card-head">
                    <h2 className="card-title">Recommended Care Plan</h2>
                    <span className="tag ok">MWC PLANS</span>
                  </div>
                  <div className="card-body">
                    <div className="rec-sub mono">Fully-Managed Hosting + Care (Front and Backend)</div>
                    <div className="rec-range">${recommend.careRange[0]}–${recommend.careRange[1]}/mo</div>
                    <VolatilityNotice variant="compact" scope="carePlan" style={{ marginBottom: 10 }}/>
                    <div className="rec-list">
                      {recommend.care.map((p, i) => (
                        <div key={p.code} className="rec-row">
                          <div>
                            <div className="rec-row-name">
                              <strong>{p.code}</strong>
                              {i === 0 && <span className="rec-badge">MIN</span>}
                              <span className="rec-row-support">{p.support}</span>
                            </div>
                            <div className="rec-row-inc">{p.inc}</div>
                          </div>
                          <div className="rec-row-price">
                            <div className="mono rec-price">${p.charge}/mo</div>
                            <div className="rec-margin">{Math.round(p.margin)}% margin</div>
                          </div>
                        </div>
                      ))}
                    </div>
                  </div>
                </div>
              )}

              {/* Monthly Ongoing Costs */}
              <div className="card">
                <div className="card-head">
                  <h2 className="card-title"><span style={{ marginRight:6 }}>📅</span>Monthly Ongoing Costs</h2>
                  <span className="tag ok">MONTHLY</span>
                </div>
                <div className="card-body">
                  <div style={{ fontSize:'.72rem', color:'var(--dim)', marginBottom:10, lineHeight:1.4 }}>
                    Optional Add-ons · Added to monthly recurring total
                  </div>
                  <div className="sum-row"><span>{isCivic ? 'Civic care plan' : 'Care plan base'}</span><span className="mono">${totals.careBase}/mo</span></div>
                  <div className="sum-row"><span>Monthly add-ons</span><span className="mono">${totals.monthlyAddon}/mo</span></div>
                  {hours > 0 && <div className="sum-row"><span>{hours} hour block</span><span className="mono">${hours * 90}/mo</span></div>}
                  <div style={{ height:1, background:'var(--border)', margin:'10px 0' }}/>
                  <div className="sum-row big"><span>Monthly recurring</span><span className="mono" style={{ color:'var(--beam)' }}>${totals.monthly}/mo</span></div>
                  <div style={{ marginTop:10, padding:'8px 10px', background:'var(--surface-3)', borderRadius:6, fontSize:'.7rem', color:'var(--muted)', textAlign:'center' }}>
                    12-mo: <strong style={{ color:'var(--text)' }}>${(totals.monthly * 12).toLocaleString()}</strong>
                  </div>
                </div>
              </div>
            </div>
          </div>
        </div>
      )}

      {/* Full Plan Comparison (below grid, visible on estimate tab) */}
      {tab === 'estimate' && <PlanComparisonSection siteType={isCivic ? 'business' : siteType}/>}

      {tab === 'pagegap' && <PageGapTab/>}
      {tab === 'catalog' && <CatalogTab/>}
      {tab === 'hosting' && <HostingTab/>}
      {tab === 'rules'   && <TriggerRulesTab/>}
      {tab === 'proposal'&& <ProposalTab totals={totals} isCivic={isCivic} popTier={popTier} tier={tier} bizTiers={bizTiers} cart={cart} adaItems={adaItems} brand={brand}/>}
      {tab === 'email'   && <EmailTemplatesTab brand={brand} cart={cart} totals={totals}/>}
    </div>
  );
}

function Toggle({ on, onChange }) {
  return (
    <button type="button" onClick={() => onChange(!on)} className={'tg' + (on ? ' on' : '')}>
      <span className="tg-dot"/>
    </button>
  );
}

/* ── PLAN COMPARISON ─────────────
   Three-tab comparison (M / MWC / Civic) showing code, charge, cost,
   profit, margin and inclusions — carried over from the legacy
   WP Scope Estimator. The `highlightCat` arg dims rows that don't
   match the current site type so the relevant plans pop. */
function PlanComparisonSection({ siteType }) {
  const { useState } = React;
  const [tab, setTab] = useState('M');
  const rows = CD_PLANS[tab] || [];
  const marginTone = (m) => m >= 65 ? 'var(--green)' : m >= 55 ? 'var(--beam)' : m >= 50 ? 'var(--warn)' : 'var(--red)';
  return (
    <div className="card" style={{ marginTop:16 }}>
      <div className="card-head" style={{ gap:12, flexWrap:'wrap' }}>
        <h2 className="card-title"><span style={{ marginRight:6 }}>📋</span>Plan Comparison</h2>
        <div style={{ display:'flex', gap:6, flexWrap:'wrap' }}>
          {['M','MWC','CIVIC'].map(k => (
            <button key={k}
              className={'btn btn-sm ' + (tab === k ? 'btn-primary' : 'btn-ghost')}
              onClick={() => setTab(k)}>
              {k === 'M' ? 'M Plans' : k === 'MWC' ? 'MWC Plans' : 'Civic Plans'}
            </button>
          ))}
        </div>
      </div>
      <div className="card-body" style={{ padding:0 }}>
        <div className="plan-compare-scroll">
          {tab !== 'CIVIC' ? (
            <table className="plan-compare-tbl">
              <thead>
                <tr>
                  <th>Code</th>
                  <th className="num">Charge/mo</th>
                  <th className="num">Cost</th>
                  <th className="num">Profit</th>
                  <th className="num">Margin</th>
                  <th>Includes</th>
                </tr>
              </thead>
              <tbody>
                {rows.map(p => {
                  const matches = !p.cats || p.cats.length === 0 || p.cats.includes(siteType);
                  return (
                    <tr key={p.code} className={matches ? '' : 'dim'}>
                      <td><strong>{p.code}</strong></td>
                      <td className="num mono" style={{ color:'var(--beam)' }}>${p.charge}</td>
                      <td className="num mono">${p.cost?.toFixed?.(2) ?? '—'}</td>
                      <td className="num mono">${p.profit?.toFixed?.(2) ?? '—'}</td>
                      <td className="num mono" style={{ color: marginTone(p.margin) }}>{p.margin?.toFixed?.(1)}%</td>
                      <td>{p.inc}</td>
                    </tr>
                  );
                })}
              </tbody>
            </table>
          ) : (
            <table className="plan-compare-tbl">
              <thead>
                <tr>
                  <th>Code</th>
                  <th>Population</th>
                  <th>Pages</th>
                  <th className="num">Build</th>
                  <th>Support</th>
                  <th className="num">Charge/mo</th>
                  <th>Includes</th>
                </tr>
              </thead>
              <tbody>
                {rows.map(p => (
                  <tr key={p.code}>
                    <td><strong>{p.code}</strong></td>
                    <td>{p.pop}</td>
                    <td>{p.pages}</td>
                    <td className="num mono">${p.buildMin.toLocaleString()}–${p.buildMax.toLocaleString()}</td>
                    <td>{p.support}</td>
                    <td className="num mono" style={{ color:'var(--beam)' }}>${p.charge}</td>
                    <td>{p.inc}</td>
                  </tr>
                ))}
              </tbody>
            </table>
          )}
        </div>
      </div>
    </div>
  );
}

function PageGapTab() {
  const scanned = ['Home','About','Services','Pricing','Team','Contact','Blog','Blog: Post 1','Blog: Post 2','Careers','Case Studies','Privacy'];
  const standard = ['Home','About','Services','Pricing','Contact','Blog','Privacy','Terms','Sitemap','FAQ'];
  const matched = scanned.filter(p => standard.some(s => p.startsWith(s)));
  const missing = standard.filter(s => !scanned.some(p => p.startsWith(s)));
  return (
    <div style={{ marginTop:16 }}>
      <div className="grid grid-3">
        <div className="stat"><div className="stat-lbl">Pages scanned</div><div className="stat-val">{scanned.length}</div></div>
        <div className="stat"><div className="stat-lbl">Matched to standard</div><div className="stat-val ok">{matched.length}</div></div>
        <div className="stat"><div className="stat-lbl">Missing standard</div><div className="stat-val warn">{missing.length}</div></div>
      </div>
      <div className="grid grid-2" style={{ marginTop:16 }}>
        <div className="card">
          <div className="card-head"><h2 className="card-title">Pages on current site</h2><span className="tag beam">FROM SCAN</span></div>
          <div className="card-body" style={{ padding: 0 }}>
            {scanned.map((p, i) => (
              <div key={p} style={{ padding:'10px 16px', borderBottom: i<scanned.length-1 ? '1px solid var(--border)' : 'none', display:'flex', alignItems:'center', gap:8 }}>
                <span className="mono" style={{ color:'var(--muted)', fontSize:'.7rem', width:20 }}>{(i+1).toString().padStart(2,'0')}</span>
                <span style={{ flex:1, fontSize:'.84rem' }}>{p}</span>
                {standard.some(s => p.startsWith(s)) && <Icon name="check" size={14}/>}
              </div>
            ))}
          </div>
        </div>
        <div className="card">
          <div className="card-head"><h2 className="card-title">Standard pages (missing)</h2><span className="tag warn">{missing.length} GAPS</span></div>
          <div className="card-body">
            {missing.map(p => (
              <div key={p} style={{ display:'flex', gap:10, padding:'10px 0', borderBottom:'1px solid var(--border)', alignItems:'center' }}>
                <div style={{ width:24, height:24, borderRadius:6, background:'var(--warn-dim)', color:'var(--warn)', display:'flex', alignItems:'center', justifyContent:'center' }}>+</div>
                <div style={{ flex:1 }}>
                  <div style={{ fontWeight:600, fontSize:'.86rem' }}>{p}</div>
                  <div className="mono" style={{ fontSize:'.68rem', color:'var(--dim)' }}>Recommended page</div>
                </div>
                <button className="btn btn-ghost btn-sm">Add to scope</button>
              </div>
            ))}
          </div>
        </div>
      </div>
    </div>
  );
}

function CatalogTab() {
  const rows = [
    ['CPT Setup + Migration',         'setup',   450, 'Custom post type setup w/ ACF fields and content migration.'],
    ['SMTP Email Integration',        'setup',   225, 'SendGrid / Postmark wiring and DNS records.'],
    ['SSL Install + Configuration',   'setup',   150, 'Install certificate, force HTTPS, fix mixed content.'],
    ['WooCommerce Setup',             'setup',  1250, 'Products (up to 25), tax, shipping, payment gateway.'],
    ['Document Library Setup (Civic)','setup',   850, 'Categorized document repository with search & filters.'],
    ['Commission Minutes Import',     'setup',   450, 'Bulk import of historical meeting minutes as PDFs.'],
    ['Agenda Management System',      'setup',   950, 'Board/Council agenda publishing workflow.'],
    ['ADA / WCAG 2.1 AA Audit',       'setup',   675, 'Full accessibility remediation pass.'],
    ['Cloudflare Pro',                'monthly',  20, 'Pro CDN + security layer.'],
    ['Accessibility Tool (ADA)',      'monthly',  30, 'AccessiBe / UserWay subscription.'],
    ['Web-to-App — Civic',            'monthly', 249, 'Progressive web app shell for civic sites.'],
    ['Extended Care Retainer',        'monthly', 150, 'Dedicated hours & priority response.'],
  ];
  return (
    <div className="card" style={{ marginTop:16 }}>
      <div className="card-head"><h2 className="card-title">Service Catalog</h2><button className="btn btn-ghost btn-sm"><Icon name="plus" size={13}/>Add service</button></div>
      <div className="card-body" style={{ padding: 0 }}>
        <table className="table">
          <thead><tr><th scope="col">Service</th><th scope="col">Type</th><th scope="col">Price</th><th scope="col">Description</th><th scope="col"></th></tr></thead>
          <tbody>
            {rows.map(([n, t, p, d]) => (
              <tr key={n}>
                <td style={{ fontWeight:600 }}>{n}</td>
                <td><span className={`tag ${t==='setup'?'setup':'ok'}`}>{t.toUpperCase()}</span></td>
                <td className="mono" style={{ color:'var(--orange)' }}>${p}{t==='monthly' && '/mo'}</td>
                <td style={{ color:'var(--dim)', fontSize:'.78rem' }}>{d}</td>
                <td><a href="#" onClick={e=>e.preventDefault()}>Edit</a></td>
              </tr>
            ))}
          </tbody>
        </table>
      </div>
    </div>
  );
}

function HostingTab() {
  const { useState } = React;
  const [hostTab, setHostTab] = useState('MWC');
  const [view, setView] = useState('catalog'); // catalog | comparison | editor
  const tabs = [
    ['M',    `Managed (${CD_PLANS.M.length})`],
    ['MWC',  `Managed + Care (${CD_PLANS.MWC.length})`],
    ['CIVIC',`Civic (${CD_PLANS.CIVIC.length})`],
    ['CARE', `Care-Only (${CD_PLANS.CARE_ONLY.length})`],
  ];
  const plans = hostTab === 'CARE' ? CD_PLANS.CARE_ONLY : CD_PLANS[hostTab];
  const isCivic = hostTab === 'CIVIC';
  const isCare  = hostTab === 'CARE';

  // Subscribe to hosting cost edits so the Plan Comparison refreshes when
  // the user tweaks a value in the Cost Editor.
  const [costs] = window.useHostingCosts();

  // Proxy through the shared helpers — overrides + cost components win.
  const calcCost = (p) => window.calcPlanCost(p, hostTab);
  const chargeOf = (p) => window.planCharge(p);

  function rec(p) {
    // Flag best-margin "recommended" rows
    const cost = calcCost(p);
    const charge = chargeOf(p);
    const margin = (charge - cost) / charge;
    return margin >= 0.65 && charge <= 200;
  }

  return (
    <div style={{ marginTop:16 }}>
      <div className="box info" style={{ marginBottom:12 }}>
        <Icon name="info" size={14}/>
        <div><strong>Hosting Plan Library</strong> — full catalog pulled from the live Estimator. M = hosting only · MWC = hosting + care · Civic = population-tier plans · Care-Only = for sites hosted elsewhere.</div>
      </div>

      <div style={{ display:'flex', justifyContent:'space-between', alignItems:'center', gap:12, flexWrap:'wrap', marginBottom:4 }}>
        <div className="sub-tabs" style={{ marginBottom:0, flex:1 }}>
          {tabs.map(([id, lbl]) => (
            <button key={id} className={'sub-tab' + (hostTab === id ? ' active' : '')} onClick={() => setHostTab(id)}>{lbl}</button>
          ))}
        </div>
        <div className="tw-seg" style={{ maxWidth:380 }}>
          <button className={view === 'catalog' ? 'active' : ''} onClick={() => setView('catalog')}>Catalog</button>
          <button className={view === 'comparison' ? 'active' : ''} onClick={() => setView('comparison')}>📋 Plan Comparison</button>
          <button className={view === 'editor' ? 'active' : ''} onClick={() => setView('editor')}>💼 Cost Editor</button>
        </div>
      </div>

      {view === 'editor' && window.HostingCostEditor && <window.HostingCostEditor/>}

      {view === 'catalog' && (
      <div className="card" style={{ marginTop:16 }}>
        <div className="card-head">
          <h2 className="card-title">
            {hostTab === 'M'     && 'M Plans — Fully-Managed Hosting (Backend Only)'}
            {hostTab === 'MWC'   && 'MWC Plans — Hosting + Care (Front & Backend)'}
            {hostTab === 'CIVIC' && 'Civic Hosting & Care Plans'}
            {hostTab === 'CARE'  && 'Care-Only Support Plans (Non-Hosted Sites)'}
          </h2>
          <button className="btn btn-ghost btn-sm"><Icon name="plus" size={13}/>Add plan</button>
        </div>
        <div className="card-body" style={{ padding: 0, overflowX:'auto' }}>
          <table className="table" style={{ minWidth: 720 }}>
            <thead>
              <tr>
                <th scope="col">Code</th><th scope="col">Label</th>
                {isCivic && <><th scope="col">Population</th><th scope="col">Pages</th></>}
                {!isCivic && !isCare && <th scope="col">Categories</th>}
                {isCare && <th scope="col">Best For</th>}
                <th scope="col">Support</th>
                <th scope="col" style={{textAlign:'right'}}>Client Price</th>
                <th scope="col">Includes</th>
              </tr>
            </thead>
            <tbody>
              {plans.map(p => (
                <tr key={p.code}>
                  <td className="mono" style={{ fontSize:'.7rem', color:'var(--muted)' }}>{p.code}</td>
                  <td style={{ fontWeight:700 }}>{p.label}</td>
                  {isCivic && <>
                    <td className="mono" style={{ fontSize:'.72rem', color:'var(--beam)' }}>{p.pop}</td>
                    <td className="mono" style={{ fontSize:'.72rem' }}>{p.pages}</td>
                  </>}
                  {!isCivic && !isCare && (
                    <td>{(p.cats||[]).map(c => <span key={c} className="tag" style={{ marginRight:4 }}>{c}</span>)}</td>
                  )}
                  {isCare && <td style={{ fontSize:'.76rem', color:'var(--muted)' }}>{p.bestFor}</td>}
                  <td className="mono" style={{ fontSize:'.74rem' }}>{p.support}</td>
                  <td className="mono" style={{ textAlign:'right', color:'var(--orange)', fontWeight:700 }}>${p.charge}/mo</td>
                  <td style={{ fontSize:'.76rem', color:'var(--dim)' }}>{p.inc}</td>
                </tr>
              ))}
            </tbody>
          </table>
        </div>
      </div>
      )}

      {view === 'comparison' && (
        <div className="card" style={{ marginTop:16 }}>
          <div className="card-head">
            <h2 className="card-title">
              📋 Plan Comparison · {hostTab === 'M' ? 'M Plans' : hostTab === 'MWC' ? 'MWC Plans' : hostTab === 'CIVIC' ? 'Civic Plans' : 'Care-Only Plans'}
            </h2>
            <span className="tag sa">INTERNAL · MARGIN VIEW</span>
          </div>
          <div className="card-body" style={{ padding:0, overflowX:'auto' }}>
            <table className="table" style={{ minWidth: 820 }}>
              <thead>
                <tr>
                  <th scope="col">Code</th>
                  <th scope="col" style={{ textAlign:'right' }}>Charge/mo</th>
                  <th scope="col" style={{ textAlign:'right' }}>Cost</th>
                  <th scope="col" style={{ textAlign:'right' }}>Profit</th>
                  <th scope="col" style={{ textAlign:'right' }}>Margin</th>
                  <th scope="col">Includes</th>
                  <th scope="col" style={{ textAlign:'center' }}>Rec.</th>
                </tr>
              </thead>
              <tbody>
                {plans.map(p => {
                  const cost = calcCost(p);
                  const profit = Math.round((p.charge - cost) * 100) / 100;
                  const margin = ((profit / p.charge) * 100);
                  const marginTone = margin >= 65 ? 'var(--green)' : margin >= 55 ? 'var(--orange)' : 'var(--rose)';
                  const isRec = rec(p);
                  return (
                    <tr key={p.code} style={{ background: isRec ? 'var(--green-dim)' : 'transparent' }}>
                      <td className="mono" style={{ fontSize:'.72rem', fontWeight:700 }}>{p.code}</td>
                      <td className="mono" style={{ textAlign:'right', color:'var(--orange)', fontWeight:700 }}>${p.charge}</td>
                      <td className="mono" style={{ textAlign:'right', color:'var(--dim)' }}>${cost.toFixed(2)}</td>
                      <td className="mono" style={{ textAlign:'right', color:'var(--green)', fontWeight:600 }}>${profit.toFixed(2)}</td>
                      <td className="mono" style={{ textAlign:'right', color: marginTone, fontWeight:700 }}>{margin.toFixed(1)}%</td>
                      <td style={{ fontSize:'.76rem', color:'var(--dim)' }}>{p.inc}</td>
                      <td style={{ textAlign:'center' }}>{isRec ? <span style={{ color:'var(--green)', fontSize:'1rem' }}>✓</span> : <span style={{ color:'var(--dim)' }}>—</span>}</td>
                    </tr>
                  );
                })}
              </tbody>
              <tfoot>
                <tr style={{ background:'var(--surface-3)', fontWeight:700 }}>
                  <td>Averages ({plans.length})</td>
                  <td className="mono" style={{ textAlign:'right' }}>${Math.round(plans.reduce((s,p)=>s+p.charge,0)/plans.length)}</td>
                  <td className="mono" style={{ textAlign:'right', color:'var(--dim)' }}>${(plans.reduce((s,p)=>s+calcCost(p),0)/plans.length).toFixed(2)}</td>
                  <td className="mono" style={{ textAlign:'right', color:'var(--green)' }}>${(plans.reduce((s,p)=>s+(p.charge-calcCost(p)),0)/plans.length).toFixed(2)}</td>
                  <td className="mono" style={{ textAlign:'right', color:'var(--orange)' }}>
                    {(plans.reduce((s,p)=>{const c=calcCost(p); return s+((p.charge-c)/p.charge)*100;},0)/plans.length).toFixed(1)}%
                  </td>
                  <td colSpan={2}></td>
                </tr>
              </tfoot>
            </table>
          </div>

          <div className="card-body" style={{ borderTop:'1px solid var(--border)', background:'var(--surface-3)' }}>
            <div style={{ display:'flex', gap:14, flexWrap:'wrap', fontSize:'.76rem', color:'var(--dim)' }}>
              <div><span style={{ display:'inline-block', width:10, height:10, background:'var(--green-dim)', borderRadius:2, marginRight:4, verticalAlign:'middle' }}/> Recommended: ≥65% margin at ≤$200/mo price point</div>
              <div style={{ marginLeft:'auto' }}>Margin tones: <span style={{ color:'var(--green)' }}>≥65%</span> · <span style={{ color:'var(--orange)' }}>55–65%</span> · <span style={{ color:'var(--rose)' }}>&lt;55%</span></div>
            </div>
          </div>
        </div>
      )}
    </div>
  );
}

function TriggerRulesTab() {
  const { useState, useMemo } = React;

  // Scan field reference — what you can match against in IF conditions
  const SCAN_FIELDS = [
    { grp:'Site type',      fields:['site_type', 'civic_flag', 'ecommerce_flag', 'membership_flag'] },
    { grp:'Site scale',     fields:['population', 'pages_count', 'posts_count', 'products_count', 'users_count'] },
    { grp:'Detected tech',  fields:['gravity_forms_detected', 'learndash_detected', 'woocommerce_detected', 'elementor_detected', 'divi_detected'] },
    { grp:'Health signals', fields:['ssl_valid', 'wp_outdated', 'plugin_vulns_count', 'scan_404_count', 'ada_violations_big6'] },
    { grp:'Performance',    fields:['lcp_ms', 'cls', 'inp_ms', 'image_kb_total', 'page_weight_mb'] },
    { grp:'Ownership',      fields:['hosted_elsewhere', 'has_care_plan', 'current_mrr', 'months_with_cd'] },
  ];

  const OPERATORS = ['equals','not equals','>','<','>=','<=','contains','matches regex','detected','not detected'];
  const ACTIONS = [
    { id:'add_line_item',     lbl:'Add line item',        hint:'Append scoped service to Estimator cart' },
    { id:'recommend_hosting', lbl:'Recommend plan',       hint:'Promote a specific hosting plan in Plan Compare' },
    { id:'bump_tier',         lbl:'Bump scope tier',      hint:'Upgrade Basic→Scale or Scale→Growth' },
    { id:'recommend',         lbl:'Suggest add-on',       hint:'Surface recommendation without auto-add' },
    { id:'block_plan',        lbl:'Block plan',           hint:'Hide plan from options (e.g., too small)' },
    { id:'trigger_email',     lbl:'Trigger email template', hint:'Send tokenized email on scan complete' },
    { id:'flag_for_review',   lbl:'Flag for SA review',   hint:'Adds to SA triage queue' },
  ];

  const [rules, setRules] = useState([
    { id:'r1', on:true,  field:'site_type',               op:'equals',        val:'civic',          action:'add_line_item',     arg:'Document Library Setup',              note:'Every civic site gets doc library' },
    { id:'r2', on:true,  field:'population',              op:'<',             val:'5000',           action:'recommend_hosting', arg:'CIVIC-Community',                     note:'Small towns → Community tier' },
    { id:'r3', on:true,  field:'population',              op:'>',             val:'25000',          action:'recommend_hosting', arg:'CIVIC-Professional',                  note:'Mid-size cities → Professional' },
    { id:'r4', on:true,  field:'site_type',               op:'equals',        val:'ecommerce',      action:'recommend',         arg:'WooCommerce Setup, MWC-S2',           note:'Retail playbook' },
    { id:'r5', on:true,  field:'pages_count',             op:'>',             val:'50',             action:'bump_tier',         arg:'Scale',                               note:'Large site → Scale tier minimum' },
    { id:'r6', on:true,  field:'ssl_valid',               op:'not detected',  val:'',               action:'add_line_item',     arg:'SSL Setup + Configuration',           note:'Blocker — must fix before launch' },
    { id:'r7', on:true,  field:'membership_flag',         op:'detected',      val:'',               action:'recommend',         arg:'Membership / LMS Integration',        note:'BuddyPress / MemberPress / etc.' },
    { id:'r8', on:true,  field:'gravity_forms_detected',  op:'detected',      val:'',               action:'add_line_item',     arg:'Forms Migration (GF)',                note:'Forms = scope blocker; price explicitly' },
    { id:'r9', on:true,  field:'learndash_detected',      op:'detected',      val:'',               action:'recommend_hosting', arg:'MWC-G2',                              note:'LMS needs growth-tier resources' },
    { id:'r10',on:false, field:'ada_violations_big6',     op:'>',             val:'0',              action:'trigger_email',     arg:'ada-findings-template',               note:'Disabled — client opt-in required' },
    { id:'r11',on:true,  field:'page_weight_mb',          op:'>',             val:'5',              action:'add_line_item',     arg:'Image Tools Pipeline Cleanup',        note:'Auto-offer compression retainer' },
  ]);
  const [editingId, setEditingId] = useState(null);
  const [showRef, setShowRef] = useState(false);

  function patch(id, key, val) { setRules(rs => rs.map(r => r.id === id ? { ...r, [key]: val } : r)); }
  function addRule() {
    const id = 'r' + Math.random().toString(36).slice(2, 7);
    setRules(rs => [...rs, { id, on:true, field:'site_type', op:'equals', val:'', action:'add_line_item', arg:'', note:'' }]);
    setEditingId(id);
  }
  function deleteRule(id) { setRules(rs => rs.filter(r => r.id !== id)); }
  function exportJSON() {
    const json = JSON.stringify(rules, null, 2);
    const blob = new Blob([json], { type: 'application/json' });
    const url = URL.createObjectURL(blob);
    const a = document.createElement('a'); a.href = url; a.download = 'trigger-rules.json'; a.click();
    URL.revokeObjectURL(url);
    window.wpsbToast('Exported trigger-rules.json', 'ok');
  }
  function importJSON() {
    const input = document.createElement('input');
    input.type = 'file'; input.accept = 'application/json';
    input.onchange = () => {
      const f = input.files?.[0]; if (!f) return;
      const reader = new FileReader();
      reader.onload = (e) => {
        try {
          const parsed = JSON.parse(e.target.result);
          if (Array.isArray(parsed)) { setRules(parsed); window.wpsbToast(`Imported ${parsed.length} rules`, 'ok'); }
          else window.wpsbToast('Invalid — expected array of rules', 'warn');
        } catch { window.wpsbToast('Import failed — invalid JSON', 'warn'); }
      };
      reader.readAsText(f);
    };
    input.click();
  }

  const activeCount = rules.filter(r => r.on).length;

  return (
    <div style={{ marginTop:16, display:'grid', gridTemplateColumns:'minmax(0, 1fr) 280px', gap:16, alignItems:'start' }}>
      {/* ── MAIN RULES EDITOR ───────────────────────── */}
      <div className="card">
        <div className="card-head" style={{ flexWrap:'wrap', gap:8 }}>
          <div>
            <h2 className="card-title">Trigger Rules</h2>
            <div style={{ fontSize:'.72rem', color:'var(--dim)', marginTop:2 }}>
              {activeCount} active · {rules.length - activeCount} disabled · Run on every scan completion
            </div>
          </div>
          <div style={{ display:'flex', gap:6 }}>
            <button className="btn btn-ghost btn-sm" onClick={importJSON}><Icon name="upload" size={12}/>Import</button>
            <button className="btn btn-ghost btn-sm" onClick={exportJSON}><Icon name="download" size={12}/>Export</button>
            <button className="btn btn-ghost btn-sm" onClick={() => setShowRef(!showRef)}>
              <Icon name={showRef ? 'chevron-right' : 'chevron-left'} size={12}/>
              {showRef ? 'Hide' : 'Show'} field ref
            </button>
            <button className="btn btn-primary btn-sm" onClick={addRule}><Icon name="plus" size={12}/>New rule</button>
          </div>
        </div>
        <div className="card-body" style={{ padding: 0 }}>
          <div style={{ padding:'10px 14px', fontSize:'.68rem', fontFamily:'var(--font-mono)', color:'var(--dim)', letterSpacing:'.08em', background:'var(--surface)', borderBottom:'1px solid var(--border)', display:'grid', gridTemplateColumns:'32px 1.4fr .9fr 1fr 1.1fr 1.2fr 28px', gap:8, alignItems:'center' }}>
            <span>ON</span><span>IF FIELD</span><span>OP</span><span>VALUE</span><span>THEN ACTION</span><span>ARG / NOTE</span><span/>
          </div>
          {rules.map((r, i) => {
            const isEditing = editingId === r.id;
            const actionDef = ACTIONS.find(a => a.id === r.action);
            return (
              <div key={r.id} style={{
                padding:'10px 14px',
                borderBottom: i < rules.length - 1 ? '1px solid var(--border)' : 'none',
                display:'grid', gridTemplateColumns:'32px 1.4fr .9fr 1fr 1.1fr 1.2fr 28px', gap:8, alignItems:'center',
                opacity: r.on ? 1 : .5,
                background: isEditing ? 'var(--beam-dim)' : 'transparent',
              }}>
                <label style={{ display:'flex', alignItems:'center', cursor:'pointer' }} title={r.on ? 'Active' : 'Disabled'}>
                  <input type="checkbox" checked={r.on} onChange={e => patch(r.id, 'on', e.target.checked)}/>
                </label>
                <select value={r.field} onChange={e => patch(r.id, 'field', e.target.value)}
                        style={{ fontSize:'.76rem', fontFamily:'var(--font-mono)', padding:'4px 6px', background:'var(--surface-2)', border:'1px solid var(--border)', borderRadius:4, color:'var(--text)', width:'100%' }}>
                  {SCAN_FIELDS.flatMap(g => [
                    <option key={g.grp} disabled style={{ fontWeight:700 }}>── {g.grp} ──</option>,
                    ...g.fields.map(f => <option key={f} value={f}>{f}</option>),
                  ])}
                </select>
                <select value={r.op} onChange={e => patch(r.id, 'op', e.target.value)}
                        style={{ fontSize:'.76rem', fontFamily:'var(--font-mono)', padding:'4px 6px', background:'var(--surface-2)', border:'1px solid var(--border)', borderRadius:4, color:'var(--text)', width:'100%' }}>
                  {OPERATORS.map(op => <option key={op} value={op}>{op}</option>)}
                </select>
                <input type="text" value={r.val}
                       disabled={r.op === 'detected' || r.op === 'not detected'}
                       onChange={e => patch(r.id, 'val', e.target.value)}
                       placeholder={r.op === 'detected' ? '—' : 'value'}
                       style={{ fontSize:'.76rem', fontFamily:'var(--font-mono)', padding:'4px 6px', background:'var(--surface-2)', border:'1px solid var(--border)', borderRadius:4, color:'var(--text)', width:'100%' }}/>
                <select value={r.action} onChange={e => patch(r.id, 'action', e.target.value)}
                        title={actionDef?.hint}
                        style={{ fontSize:'.76rem', padding:'4px 6px', background:'var(--surface-2)', border:'1px solid var(--border)', borderRadius:4, color:'var(--text)', width:'100%' }}>
                  {ACTIONS.map(a => <option key={a.id} value={a.id}>{a.lbl}</option>)}
                </select>
                <input type="text" value={r.arg}
                       onChange={e => patch(r.id, 'arg', e.target.value)}
                       placeholder={r.action === 'add_line_item' ? 'Line item label' : 'Argument'}
                       title={r.note}
                       style={{ fontSize:'.76rem', padding:'4px 6px', background:'var(--surface-2)', border:'1px solid var(--border)', borderRadius:4, color:'var(--text)', width:'100%' }}/>
                <button className="btn btn-ghost btn-sm" style={{ padding:'2px 6px' }} onClick={() => deleteRule(r.id)} aria-label="Delete rule" title="Delete">
                  <Icon name="x" size={12}/>
                </button>
              </div>
            );
          })}
          {rules.length === 0 && (
            <div style={{ padding:'28px 20px', textAlign:'center', color:'var(--dim)', fontSize:'.82rem' }}>
              No trigger rules — add one to auto-suggest scope from scan results.
            </div>
          )}
        </div>
        <div className="card-foot" style={{ display:'flex', justifyContent:'space-between', alignItems:'center', padding:'10px 14px', background:'var(--surface)', borderTop:'1px solid var(--border)' }}>
          <span style={{ fontSize:'.7rem', color:'var(--dim)', fontFamily:'var(--font-mono)' }}>
            Rules run in order · top-down · first match per action wins
          </span>
          <button className="btn btn-ghost btn-sm" onClick={() => window.wpsbToast('Dry-running rules against last scan...', 'beam')}>
            <Icon name="scanner" size={12}/>Test against last scan
          </button>
        </div>
      </div>

      {/* ── SCAN FIELD REFERENCE (collapsible side panel) ──── */}
      {showRef && (
        <div className="card" style={{ position:'sticky', top:16 }}>
          <div className="card-head"><h2 className="card-title" style={{ fontSize:'.85rem' }}>Scan Field Reference</h2></div>
          <div className="card-body" style={{ padding:'10px 14px', maxHeight:'70vh', overflow:'auto' }}>
            <div style={{ fontSize:'.72rem', color:'var(--muted)', marginBottom:10, lineHeight:1.5 }}>
              Fields produced by Site Scanner. Click a name to copy — paste into a rule's IF column.
            </div>
            {SCAN_FIELDS.map(grp => (
              <div key={grp.grp} style={{ marginBottom:12 }}>
                <div style={{ fontSize:'.66rem', fontFamily:'var(--font-mono)', letterSpacing:'.08em', color:'var(--dim)', marginBottom:5 }}>{grp.grp.toUpperCase()}</div>
                <div style={{ display:'flex', flexDirection:'column', gap:3 }}>
                  {grp.fields.map(f => (
                    <button key={f} onClick={() => { navigator.clipboard?.writeText(f); window.wpsbToast(`Copied ${f}`, 'ok'); }}
                            className="btn btn-ghost btn-sm"
                            style={{ justifyContent:'flex-start', padding:'4px 8px', fontFamily:'var(--font-mono)', fontSize:'.74rem', color:'var(--text-2)' }}>
                      {f}
                    </button>
                  ))}
                </div>
              </div>
            ))}
          </div>
        </div>
      )}
    </div>
  );
}

function ProposalTab({ totals, isCivic, popTier, tier, bizTiers, cart, adaItems, brand }) {
  const { useState } = React;
  const name = isCivic ? CD_PLANS.CIVIC[popTier].label : bizTiers.find(t => t.id === tier).lbl;
  const tokens = (cart && cart.tokens) || {};
  const site = tokens.site || 'acmecorp.com';
  const clientName = (brand && brand.clientName) || (isCivic ? 'City of Northwind' : 'Northwind Traders');
  const clientEmail = (brand && brand.clientEmail) || 'contact@northwind.co';
  const agency = (brand && brand.agencyName) || 'Your Agency';
  const primary = (brand && brand.primary) || 'var(--orange)';
  const accent  = (brand && brand.accent)  || 'var(--beam)';
  const [showEvidence, setShowEvidence] = useState(true);
  const [showNarrative, setShowNarrative] = useState(true);

  // Pre-filled narrative (tokenized). Editable via ADA tab → Compliance Docs.
  const nar = (cart && cart.narrative) || {};
  const framework = nar.frameworkStake || (tokens.legalFramework
    ? `${clientName} operates under ${tokens.legalFramework}. Recent enforcement actions have made it clear that accessibility is no longer optional — digital assets must meet ${tokens.taxonomy || (window.WPSB && window.WPSB.CONSTANTS && window.WPSB.CONSTANTS.WCAG.DISPLAY) || 'WCAG 2.1 AA'} to avoid regulatory and legal exposure.`
    : `Public accessibility law (ADA Title II/III, Section 508, state parallels) requires that digital assets be usable by people with disabilities. Non-compliance carries legal, financial, and reputational risk.`);
  const approach = nar.approachStake || `We will remediate identified violations, produce the good-faith evidence expected under ADA enforcement (VPAT/ACR, methodology, remediation log, accessibility statement), and establish ongoing monitoring so ${site} stays compliant as content changes.`;
  const timeline = nar.timelineStake || (tokens.deadline
    ? `The ${tokens.deadline} compliance deadline is firm. Our critical-path plan delivers evidence-ready documentation within 4 weeks and full Big 6 remediation within 6–8 weeks.`
    : `Critical findings are addressed in the first 2 weeks, Big 6 violations within 6 weeks, and Level AA conformance within 8 weeks.`);

  const oneTimeLow  = (totals.baseMin + totals.addonSetup) + totals.adaLow;
  const oneTimeHigh = (totals.baseMax + totals.addonSetup) + totals.adaHigh;

  return (
    <div style={{ marginTop:16 }}>
      <div className="box info" style={{ marginBottom:14 }}>
        <Icon name="info" size={16}/>
        <div>
          Proposal uses your <strong>white-label branding</strong> (logo, colors, agency name) and pulls line items directly from the <strong>Proposal Cart</strong>. ADA narrative is tokenized from scan data — edit in <a href="#" style={{ color:'var(--beam)' }} onClick={e => { e.preventDefault(); window.WPSBD.switchTab('scanner'); }}>Scanner → ADA → Compliance Docs</a>. WPSiteBeam branding is always hidden from clients.
        </div>
      </div>

      {/* Toolbar */}
      <div style={{ display:'flex', gap:8, flexWrap:'wrap', marginBottom:12, alignItems:'center' }}>
        <button className="btn btn-ghost btn-sm" onClick={() => window.wpsbToast('Proposal PDF exported', 'ok')}>
          <Icon name="download" size={12}/>Download PDF
        </button>
        <button className="btn btn-ghost btn-sm" onClick={() => window.wpsbToast('Proposal link copied — share with client', 'ok')}>
          <Icon name="link" size={12}/>Copy share link
        </button>
        <button className="btn btn-ghost btn-sm" onClick={() => window.wpsbToast('Sent to DocuSign for signature', 'ok')}>
          <Icon name="check" size={12}/>Send for e-signature
        </button>
        <button className="btn btn-ghost btn-sm" onClick={() => window.wpsbToast('Pushed to GHL as an opportunity', 'ok')}>
          <Icon name="refresh" size={12}/>Push to GHL / CRM
        </button>
        <span style={{ flex:1 }}/>
        <label className="opt-check" style={{ fontSize:'.72rem' }}>
          <input type="checkbox" checked={showNarrative} onChange={e => setShowNarrative(e.target.checked)}/>
          <span>ADA narrative</span>
        </label>
        <label className="opt-check" style={{ fontSize:'.72rem' }}>
          <input type="checkbox" checked={showEvidence} onChange={e => setShowEvidence(e.target.checked)}/>
          <span>Evidence Appendix</span>
        </label>
      </div>

      <div className="proposal-paper">
        {/* ───── Header ───── */}
        <div className="proposal-header" style={{ borderBottom:`3px solid ${primary}` }}>
          <div>
            <div style={{ fontFamily:'var(--font-brand)', fontSize:'1.4rem', color:'#0F172A', letterSpacing:'.02em' }}>{agency.toUpperCase()}</div>
            <div style={{ fontSize:'.72rem', color:'var(--dim)', marginTop:2 }}>
              {isCivic ? 'Civic Website Proposal' : 'Website Redesign Proposal'}{adaItems.length > 0 ? ' + ADA Remediation' : ''} · Prepared Apr 18, 2026
            </div>
          </div>
          <div style={{ textAlign:'right', fontSize:'.72rem', color:'var(--dim)' }}>
            For: <strong style={{ color:'#0F172A' }}>{clientName}</strong><br/>
            {clientEmail}<br/>
            {site && <span className="mono" style={{ color:'var(--dim)' }}>{site}</span>}
          </div>
        </div>

        <h2>Recommended Plan · {name}</h2>
        <p style={{ fontSize:'.88rem', color:'var(--border)', margin:'0 0 14px' }}>{isCivic ? CD_PLANS.CIVIC[popTier].inc : bizTiers.find(t => t.id === tier).desc}</p>

        {/* ───── ADA Narrative ───── */}
        {showNarrative && adaItems.length > 0 && (
          <>
            <h2 style={{ color: primary }}>Accessibility Compliance — Why This Matters</h2>
            <div style={{ display:'grid', gridTemplateColumns:'1fr 1fr 1fr', gap:10, marginBottom:14 }}>
              <div style={{ padding:12, background:'#F8FAFC', borderLeft:`3px solid ${primary}`, borderRadius:4 }}>
                <div style={{ fontSize:'.64rem', color:'var(--dim)', textTransform:'uppercase', letterSpacing:'.08em', fontWeight:700, marginBottom:6 }}>Legal stake</div>
                <div style={{ fontSize:'.78rem', color:'var(--border)', lineHeight:1.5 }}>{framework}</div>
              </div>
              <div style={{ padding:12, background:'#F8FAFC', borderLeft:`3px solid ${accent}`, borderRadius:4 }}>
                <div style={{ fontSize:'.64rem', color:'var(--dim)', textTransform:'uppercase', letterSpacing:'.08em', fontWeight:700, marginBottom:6 }}>Our approach</div>
                <div style={{ fontSize:'.78rem', color:'var(--border)', lineHeight:1.5 }}>{approach}</div>
              </div>
              <div style={{ padding:12, background:'#F8FAFC', borderLeft:'3px solid #64748b', borderRadius:4 }}>
                <div style={{ fontSize:'.64rem', color:'var(--dim)', textTransform:'uppercase', letterSpacing:'.08em', fontWeight:700, marginBottom:6 }}>Timeline stake</div>
                <div style={{ fontSize:'.78rem', color:'var(--border)', lineHeight:1.5 }}>{timeline}</div>
              </div>
            </div>
          </>
        )}

        {/* ───── Investment ───── */}
        <h2>Project Investment</h2>
        <table className="proposal-table">
          <tbody>
            <tr><td>Build base — {name}</td><td>${totals.baseMin.toLocaleString()}–${totals.baseMax.toLocaleString()}</td></tr>
            {totals.addonSetup > 0 && <tr><td>Setup add-ons</td><td>${totals.addonSetup.toLocaleString()}</td></tr>}
            {totals.adaCount > 0 && (
              <tr><td style={{ color:'var(--green)' }}>ADA remediation ({totals.adaCount} items — from cart)</td><td style={{ color:'var(--green)' }}>${totals.adaLow.toLocaleString()}–${totals.adaHigh.toLocaleString()}</td></tr>
            )}
            {totals.totalMult !== 1 && (
              <tr><td>Project multipliers (complexity · rush · doc load)</td><td>×{totals.totalMult.toFixed(2)}</td></tr>
            )}
            <tr className="sub"><td><strong>One-time project total</strong></td><td><strong>${oneTimeLow.toLocaleString()}–${oneTimeHigh.toLocaleString()}</strong></td></tr>
          </tbody>
        </table>

        <h2>Ongoing Care Plan</h2>
        <table className="proposal-table">
          <tbody>
            <tr><td>{isCivic ? 'Civic care plan' : 'Care plan'}</td><td>${totals.careBase}/mo</td></tr>
            <tr><td>Monthly add-ons</td><td>${totals.monthlyAddon}/mo</td></tr>
            {totals.adaMo > 0 && <tr><td style={{ color:'var(--green)' }}>ADA monitoring &amp; quarterly audit</td><td style={{ color:'var(--green)' }}>${totals.adaMo}/mo</td></tr>}
            <tr className="sub"><td><strong>Monthly recurring total</strong></td><td><strong>${totals.monthly}/mo</strong></td></tr>
          </tbody>
        </table>

        <h2>Timeline</h2>
        <ol style={{ fontSize:'.88rem', lineHeight:1.8, color:'var(--border)', paddingLeft:18 }}>
          <li><strong>Week 1–2:</strong> Discovery, sitemap, wireframes{adaItems.length > 0 && ', critical ADA fixes (keyboard traps, missing form labels)'}</li>
          <li><strong>Week 3–5:</strong> Design mockups &amp; revisions{adaItems.length > 0 && ', Big 6 remediation pass'}</li>
          <li><strong>Week 6–9:</strong> Development &amp; content migration{adaItems.length > 0 && ', ADA re-scan + VPAT draft'}</li>
          <li><strong>Week 10:</strong> QA, final ADA audit, launch{adaItems.length > 0 && ', Accessibility Statement + Remediation Log published'}</li>
        </ol>

        {/* ───── Evidence Appendix ───── */}
        {showEvidence && adaItems.length > 0 && (
          <>
            <div style={{ marginTop:22, paddingTop:18, borderTop:`2px solid ${primary}` }}>
              <h2 style={{ color: primary, marginTop:0 }}>Appendix A · Evidence Summary</h2>
              <p style={{ fontSize:'.82rem', color:'var(--border)', marginTop:0 }}>
                The following findings were identified during our WCAG 2.1 AA audit of <strong>{site}</strong>. Each item in the remediation scope above traces back to the evidence below.
              </p>

              {tokens.pagesScanned > 0 && (
                <div style={{ display:'grid', gridTemplateColumns:'repeat(4, 1fr)', gap:10, marginBottom:16 }}>
                  <div style={{ padding:'10px 12px', background:'#F8FAFC', borderRadius:6, textAlign:'center' }}>
                    <div style={{ fontSize:'1.4rem', fontWeight:700, color:'#0F172A' }}>{tokens.pagesScanned}</div>
                    <div style={{ fontSize:'.64rem', color:'var(--dim)', textTransform:'uppercase', letterSpacing:'.06em' }}>Pages scanned</div>
                  </div>
                  <div style={{ padding:'10px 12px', background:'#F8FAFC', borderRadius:6, textAlign:'center' }}>
                    <div style={{ fontSize:'1.4rem', fontWeight:700, color:'var(--red)' }}>{tokens.violations || 0}</div>
                    <div style={{ fontSize:'.64rem', color:'var(--dim)', textTransform:'uppercase', letterSpacing:'.06em' }}>Violations</div>
                  </div>
                  <div style={{ padding:'10px 12px', background:'#F8FAFC', borderRadius:6, textAlign:'center' }}>
                    <div style={{ fontSize:'1.4rem', fontWeight:700, color:'#0F172A' }}>{tokens.pdfsFound || 0}</div>
                    <div style={{ fontSize:'.64rem', color:'var(--dim)', textTransform:'uppercase', letterSpacing:'.06em' }}>PDFs checked</div>
                  </div>
                  <div style={{ padding:'10px 12px', background:'#F8FAFC', borderRadius:6, textAlign:'center' }}>
                    <div style={{ fontSize:'1.4rem', fontWeight:700, color:'#0F172A' }}>{tokens.score || 0}</div>
                    <div style={{ fontSize:'.64rem', color:'var(--dim)', textTransform:'uppercase', letterSpacing:'.06em' }}>Score / 100</div>
                  </div>
                </div>
              )}

              <table className="proposal-table" style={{ fontSize:'.78rem' }}>
                <thead>
                  <tr style={{ background:'#F1F5F9' }}>
                    <th style={{ textAlign:'left', padding:'6px 8px', fontSize:'.68rem', color:'var(--dim)', textTransform:'uppercase', letterSpacing:'.06em' }}>Finding</th>
                    <th style={{ textAlign:'left', padding:'6px 8px', fontSize:'.68rem', color:'var(--dim)', textTransform:'uppercase', letterSpacing:'.06em' }}>Scope</th>
                    <th style={{ textAlign:'left', padding:'6px 8px', fontSize:'.68rem', color:'var(--dim)', textTransform:'uppercase', letterSpacing:'.06em' }}>Priority</th>
                    <th style={{ textAlign:'right', padding:'6px 8px', fontSize:'.68rem', color:'var(--dim)', textTransform:'uppercase', letterSpacing:'.06em' }}>Remediation</th>
                  </tr>
                </thead>
                <tbody>
                  {adaItems.map(item => (
                    <tr key={item.id}>
                      <td style={{ padding:'8px', verticalAlign:'top' }}>
                        <strong>{item.label}</strong>
                        {item.description && <div style={{ fontSize:'.72rem', color:'var(--dim)', marginTop:2 }}>{item.description}</div>}
                      </td>
                      <td style={{ padding:'8px', color:'var(--dim)', fontSize:'.76rem' }}>{item.scope || '—'}</td>
                      <td style={{ padding:'8px' }}>
                        <span style={{
                          fontSize:'.66rem', padding:'2px 6px', borderRadius:3, fontWeight:700, letterSpacing:'.04em',
                          /* 2026-05-20 — was hardcoded light-mode pastels (#fee2e2, #e5e7eb, #fef3c7) with
                             dark text on them. Unreadable in dark mode because the pale pastel becomes
                             a barely-visible smudge on the dark page bg. Switched to theme-aware *-dim
                             tokens with semantic color text — works in both modes. */
                          background: item.priority === 'high' ? 'var(--red-dim)' : item.priority === 'low' ? 'var(--surface-3)' : 'var(--warn-dim)',
                          color:     item.priority === 'high' ? 'var(--red)' : item.priority === 'low' ? 'var(--dim)' : 'var(--warn)',
                        }}>{(item.priority || 'medium').toUpperCase()}</span>
                      </td>
                      <td style={{ textAlign:'right', padding:'8px', fontFamily:'var(--font-mono)', fontSize:'.76rem' }}>
                        ${(item.lowPrice||0).toLocaleString()}–${(item.highPrice||0).toLocaleString()}
                      </td>
                    </tr>
                  ))}
                </tbody>
              </table>

              <p style={{ fontSize:'.72rem', color:'var(--dim)', marginTop:10, fontStyle:'italic' }}>
                Evidence methodology: axe-core 4.10 automated scan + manual keyboard / screen-reader review on top 10 pages + PDF tagging audit. Full findings log and per-page screenshots available on request.
              </p>
            </div>
          </>
        )}

        {/* Point-in-Time Disclosure — printed at bottom of every proposal */}
        <div style={{ marginTop:20, padding:'14px 16px', background:'#FFFBEB', border:'1px solid #FCD34D', borderLeft:'3px solid #D97706', borderRadius:4, fontSize:'.72rem', lineHeight:1.55, color:'#78350F' }}>
          <div style={{ fontSize:'.68rem', fontWeight:700, letterSpacing:'.08em', color:'#92400E', fontFamily:'monospace', marginBottom:6 }}>
            POINT-IN-TIME DISCLOSURE
          </div>
          <div>
            This proposal and its underlying scan reflect the site's state at the time of audit. WordPress sites are dynamic: plugin updates, theme changes, cache/CDN configuration, media uploads, content edits, and automatic security patches can all alter or invalidate findings in this document — sometimes within hours. Deliverables are priced and scoped against the scan date; discovery of additional issues introduced after the scan may require scope revision. Ongoing compliance (ADA, SEO, performance, security) requires continuous monitoring and is covered separately under our Care Plans. Regressions caused by third-party changes outside active monitoring are not {agency}'s liability.
          </div>
        </div>

        <div style={{ marginTop:20, paddingTop:14, borderTop:'1px solid #e5e7eb', fontSize:'.68rem', color:'var(--dim)', textAlign:'center' }}>
          {(brand && brand.proposalFooter) || `Prepared by ${agency} · © 2026`}
        </div>
      </div>
    </div>
  );
}

/* ── EMAIL TEMPLATES — scan-complete, proposal-ready, regression-alert ── */
function EmailTemplatesTab({ brand, cart, totals }) {
  const { useState } = React;
  const tokens = (cart && cart.tokens) || {};
  const site = tokens.site || 'acmecorp.com';
  const clientName = (brand && brand.clientName) || 'Northwind Traders';
  const clientEmail = (brand && brand.clientEmail) || 'contact@northwind.co';
  const agency = (brand && brand.agencyName) || 'Your Agency';
  const violations = tokens.violations || 0;
  const score = tokens.score || 0;
  const adaCount = (cart.items || []).filter(i => i.source === 'ada-scan').length;

  // 3 scenario templates w/ trigger config
  const TEMPLATES = [
    {
      id: 'scan-complete',
      label: 'Scan Complete',
      icon: 'scanner',
      color: 'var(--beam)',
      trigger: 'Scanner run finishes · sends automatically to assigned account manager + client if enabled',
      to: clientEmail,
      cc: `sales@${agency.toLowerCase().replace(/\s+/g,'')}.co`,
      subject: `{{site}} accessibility scan complete — {{violations}} issues found`,
      body: `Hi {{clientName}},\n\nWe just finished a WCAG 2.1 AA scan of {{site}}. Here's a quick summary:\n\n  · Accessibility score: {{score}} / 100\n  · Violations detected: {{violations}}\n  · Legal framework: {{legalFramework}}\n  · Pages scanned: {{pagesScanned}}\n  · PDFs audited: {{pdfsFound}}\n\nFull findings, Big 6 breakdown, and remediation priorities are in your dashboard. If the scan surfaced anything critical we'll be in touch today — otherwise we'll include this in your next proposal review.\n\nReply here if you'd like us to book a walkthrough.\n\n— {{agency}}`,
      cta: 'View scan results',
    },
    {
      id: 'proposal-ready',
      label: 'Proposal Ready',
      icon: 'spark',
      color: 'var(--orange)',
      trigger: 'Proposal generated & saved · sent to client with share link + PDF attachment',
      to: clientEmail,
      cc: '',
      subject: `{{clientName}} — your ADA remediation + redesign proposal is ready`,
      body: `Hi {{clientName}},\n\nYour proposal is ready for review:\n\n  · Project investment: {{oneTimeRange}}\n  · Monthly care: {{monthlyTotal}}/mo\n  · ADA remediation items: {{adaCount}}\n  · Timeline: 10 weeks to compliance + launch\n\nThe proposal includes our full evidence appendix (the {{violations}} violations we found, prioritized) and the good-faith compliance package — VPAT, Accessibility Statement, Remediation Log.\n\n{{shareLink}}\n\nHappy to walk through it live — book a time here: {{bookingLink}}\n\n— {{agency}}`,
      cta: 'Review proposal',
    },
    {
      id: 'regression-alert',
      label: 'Regression Alert',
      icon: 'activity',
      color: 'var(--red)',
      trigger: 'Monitoring detects new violations on next monthly scan vs. last signed-off baseline',
      to: clientEmail,
      cc: `support@${agency.toLowerCase().replace(/\s+/g,'')}.co`,
      subject: `⚠ Accessibility regression detected on {{site}}`,
      body: `Hi {{clientName}},\n\nOur monitoring just flagged new accessibility violations on {{site}} since the last sign-off:\n\n  · New violations: {{newViolations}} (was {{baselineViolations}})\n  · Affected pages: {{affectedPages}}\n  · Root cause (likely): recent content changes or plugin update\n\nUnder {{legalFramework}}, a regression can reset the compliance clock if not addressed promptly. We've already queued fixes — no action needed unless you want to skip.\n\nFull diff report: {{diffLink}}\n\n— {{agency}}`,
      cta: 'View regression diff',
    },
  ];

  const [active, setActive] = useState(TEMPLATES[0].id);
  const tpl = TEMPLATES.find(t => t.id === active);

  // Token substitution — mock values when cart lacks them
  const tokenMap = {
    site, clientName, agency, clientEmail,
    violations, score,
    legalFramework: tokens.legalFramework || 'ADA Title II',
    pagesScanned:   tokens.pagesScanned   || 47,
    pdfsFound:      tokens.pdfsFound      || 312,
    adaCount,
    oneTimeRange:   `$${(totals.setupMin||0).toLocaleString()}–$${(totals.setupMax||0).toLocaleString()}`,
    monthlyTotal:   `$${(totals.monthly||0).toLocaleString()}`,
    shareLink:      '',
    bookingLink:    'https://cal.com/acme-agency/15min',
    diffLink:       'https://wpsitebeam.com/scans/acme/diff/latest',
    newViolations:  3,
    baselineViolations: violations,
    affectedPages:  '/contact, /services/lms',
  };
  const fillT = (s) => (s || '').replace(/\{\{(\w+)\}\}/g, (_, k) => tokenMap[k] != null ? tokenMap[k] : `{{${k}}}`);

  return (
    <div style={{ marginTop:16 }}>
      <div className="box info" style={{ marginBottom:14 }}>
        <Icon name="info" size={16}/>
        <div>
          Automated emails fire on scan/proposal/monitoring events. Tokens auto-fill from the active scan &amp; proposal cart — configure trigger routing in <a href="#" style={{ color:'var(--beam)' }} onClick={e => { e.preventDefault(); window.WPSBD.switchTab('settings'); }}>Account → Integrations</a>.
        </div>
      </div>

      <div className="grid" style={{ gridTemplateColumns:'280px 1fr', gap:14, alignItems:'start' }}>
        {/* Template list */}
        <div style={{ display:'flex', flexDirection:'column', gap:8 }}>
          {TEMPLATES.map(t => (
            <button key={t.id} onClick={() => setActive(t.id)}
              className="card"
              style={{
                textAlign:'left', padding:0, cursor:'pointer',
                borderColor: active === t.id ? t.color : 'var(--border)',
                borderWidth: active === t.id ? 2 : 1,
                background: active === t.id ? 'var(--surface-2)' : 'var(--surface)',
              }}>
              <div style={{ padding:12 }}>
                <div style={{ display:'flex', alignItems:'center', gap:8, marginBottom:6 }}>
                  <div style={{ width:30, height:30, borderRadius:6, background: t.color + '22', color: t.color, display:'flex', alignItems:'center', justifyContent:'center' }}>
                    <Icon name={t.icon} size={14}/>
                  </div>
                  <div style={{ fontWeight:700, fontSize:'.86rem' }}>{t.label}</div>
                </div>
                <div style={{ fontSize:'.7rem', color:'var(--dim)', lineHeight:1.45 }}>{t.trigger}</div>
              </div>
              <div style={{
                padding:'6px 12px',
                borderTop:'1px solid var(--border)',
                fontSize:'.64rem', fontFamily:'var(--font-mono)',
                color: active === t.id ? t.color : 'var(--dim)',
                letterSpacing:'.08em', textTransform:'uppercase',
                background: 'var(--surface-3)',
              }}>
                {active === t.id ? '● Active' : 'Preview'}
              </div>
            </button>
          ))}

          {/* Token reference card */}
          <div className="card">
            <div className="card-head"><h2 className="card-title" style={{ fontSize:'.82rem' }}>Available tokens</h2></div>
            <div className="card-body" style={{ padding:0 }}>
              <div style={{ maxHeight:220, overflowY:'auto' }}>
                {Object.entries(tokenMap).map(([k, v]) => (
                  <div key={k} style={{ padding:'6px 12px', borderBottom:'1px solid var(--border)', display:'flex', gap:8, alignItems:'baseline' }}>
                    <code style={{ fontFamily:'var(--font-mono)', fontSize:'.68rem', color:'var(--beam)', flexShrink:0 }}>{`{{${k}}}`}</code>
                    <span style={{ fontSize:'.68rem', color:'var(--dim)', textAlign:'right', flex:1, overflow:'hidden', textOverflow:'ellipsis', whiteSpace:'nowrap' }}>{String(v)}</span>
                  </div>
                ))}
              </div>
            </div>
          </div>
        </div>

        {/* Template preview + editor */}
        <div style={{ display:'flex', flexDirection:'column', gap:14 }}>
          {/* Trigger config */}
          <div className="card">
            <div className="card-head" style={{ alignItems:'flex-start' }}>
              <div>
                <h2 className="card-title">
                  <Icon name="activity" size={14}/>&nbsp;Trigger: {tpl.label}
                </h2>
                <div style={{ fontSize:'.72rem', color:'var(--dim)', marginTop:4 }}>{tpl.trigger}</div>
              </div>
              <span className="tag ok">ENABLED</span>
            </div>
            <div className="card-body">
              <div style={{ display:'grid', gridTemplateColumns:'100px 1fr', gap:'8px 12px', alignItems:'center', fontSize:'.78rem' }}>
                <label style={{ color:'var(--dim)' }}>To</label>
                <input value={tpl.to} readOnly style={{ fontFamily:'var(--font-mono)', fontSize:'.76rem' }}/>
                <label style={{ color:'var(--dim)' }}>Cc</label>
                <input value={tpl.cc || ''} placeholder="(none)" readOnly style={{ fontFamily:'var(--font-mono)', fontSize:'.76rem' }}/>
                <label style={{ color:'var(--dim)' }}>Subject</label>
                <input value={fillT(tpl.subject)} readOnly style={{ fontFamily:'var(--font-mono)', fontSize:'.76rem' }}/>
              </div>
            </div>
          </div>

          {/* Tokenized template (source) */}
          <div className="card">
            <div className="card-head">
              <h2 className="card-title">Template source</h2>
              <span className="tag">TOKENIZED</span>
            </div>
            <div className="card-body">
              <textarea
                readOnly
                value={`Subject: ${tpl.subject}\n\n${tpl.body}`}
                style={{
                  width:'100%', minHeight:200, padding:12, borderRadius:6,
                  background:'var(--surface-2)', border:'1px solid var(--border)',
                  color:'var(--text)', fontFamily:'var(--font-mono)', fontSize:'.76rem',
                  lineHeight:1.55, resize:'vertical',
                }}/>
              <div style={{ fontSize:'.66rem', color:'var(--dim)', marginTop:6 }}>
                Tokens in <code style={{ color:'var(--beam)' }}>{'{{curly}}'}</code> braces are substituted at send time from the active scan &amp; proposal cart.
              </div>
            </div>
          </div>

          {/* Rendered preview */}
          <div className="card">
            <div className="card-head">
              <h2 className="card-title">Rendered preview</h2>
              <div style={{ display:'flex', gap:6 }}>
                <button className="btn btn-ghost btn-sm" onClick={() => window.wpsbToast('Test email sent to ' + tpl.to, 'ok')}>
                  <Icon name="spark" size={12}/>Send test
                </button>
                <button className="btn btn-ghost btn-sm" onClick={() => { navigator.clipboard && navigator.clipboard.writeText(fillT(tpl.subject) + '\n\n' + fillT(tpl.body)); window.wpsbToast('Copied to clipboard', 'ok'); }}>
                  <Icon name="link" size={12}/>Copy
                </button>
              </div>
            </div>
            <div className="card-body" style={{ padding:0 }}>
              <div style={{
                background:'#ffffff', color:'#0F172A', padding:24, borderRadius:'0 0 8px 8px',
                fontFamily:'system-ui, -apple-system, sans-serif',
              }}>
                <div style={{ borderBottom:`3px solid ${(brand && brand.primary) || 'var(--orange)'}`, paddingBottom:10, marginBottom:14 }}>
                  <div style={{ fontFamily:'var(--font-brand)', fontSize:'1.2rem', letterSpacing:'.02em' }}>{agency.toUpperCase()}</div>
                </div>
                <div style={{ fontSize:'.9rem', fontWeight:700, marginBottom:10, color:'#0F172A' }}>
                  {fillT(tpl.subject)}
                </div>
                <div style={{ whiteSpace:'pre-wrap', fontSize:'.84rem', lineHeight:1.65, color:'var(--border)' }}>
                  {fillT(tpl.body)}
                </div>
                <div style={{ marginTop:18 }}>
                  <button style={{ padding:'10px 20px', background:(brand && brand.primary) || 'var(--orange)', color:'#000', border:'none', borderRadius:6, fontWeight:700, fontSize:'.82rem', cursor:'pointer' }}>
                    {tpl.cta} →
                  </button>
                </div>
                <div style={{ marginTop:20, paddingTop:14, borderTop:'1px solid #e5e7eb', fontSize:'.66rem', color:'var(--dim)' }}>
                  {(brand && brand.proposalFooter) || `${agency} · © 2026`}
                </div>
              </div>
            </div>
          </div>
        </div>
      </div>
    </div>
  );
}

window.Estimator = Estimator;
