/* global React */

/* ============================================================
   SERVICES PAGE
   ============================================================ */
window.ServicesPage = function ServicesPage({ setRoute }) {
  const services = [
  {
    id: 'svc-revscout', tag: '01 · RevScout',
    title: 'RevScout Fruit Mapping',
    lede: 'Field cameras count every fruit on every tree. Mounted on an ATV or tractor, they take up to 5 photos of each tree on both sides — the algorithm picks the two highest counts as the tree’s reading. The block splits into 5 zones from high to low; calibrate any zone with a hand count to convert to T/ha.',
    points: [
    'Up to 10 photos per tree (5 each side); algorithm picks the best 2',
    'Real-time processing in RevToolbox',
    '5 calibration zones, high to low, per block',
    'Variable-rate Potassium files at tree resolution',
    'Yield estimation calibrated to harvest T/ha'],

    visual: <window.ScoutDiagram />,
    visual2: <window.CalibrationScatter />,
    v2Label: 'Calibration: 60 trees, R² = 0.94'
  },
  {
    id: 'svc-revtoolbox', tag: '02 · RevToolbox',
    title: 'RevToolbox Data Portal',
    lede: 'Your full orchard, every layer, in one browser. Two satellites — one for monitoring (10 m, every 5 days), one for high-accuracy applications (0.5–0.8 m, on-demand). Stack soil, canopy and fruit, then export prescription files in three clicks.',
    points: [
    '10 m crop-health imagery, refreshed every 5 days',
    '0.5–0.8 m on-demand imagery for variable-rate spreaders',
    'Phenological-period averages (fruitset, ripening, harvest)',
    'Variable-rate exports (Trimble, John Deere, AGCO ready)',
    'Google Earth export for in-field inspection on phone'],

    visual: <window.MiniMap variant="ndvi" />,
    visual2: <window.TimeSeries />,
    v2Label: '4 years NDVI history per block'
  },
  {
    id: 'svc-canopy', tag: '03 · Canopy',
    title: 'Tree-Level Canopy Mapping',
    lede: '50–80 cm resolution satellite imagery on demand. Single shot over an entire farm — no UAV, no flight conditions to worry about. Booked in two weeks, delivered to RevToolbox.',
    points: [
    '80 cm standard resolution; 50 cm available',
    'Cultivar isolation — pollinator rows excluded',
    'Tree-level Nitrogen prescription generation',
    'Anywhere in the world — no flight permits'],

    visual: <window.MiniMap variant="ndvi" />,
    visual2: <window.VRMap />,
    v2Label: 'Variable-rate N zones, 4 levels'
  },
  {
    id: 'svc-sizing', tag: '04 · RevSizer',
    title: 'Fruit Size Monitoring',
    lede: 'The Rev-Sizer digital caliper measures fruit circumference on the tree to 0.5 mm accuracy — no picking required, up to 3 000 fruit per day, water-resistant, Wi-Fi sync. Pre-load blocks on the platform; save measurements block-by-block. Weekly readings build a growth curve per block; the platform predicts harvest size by market class.',
    points: [
    'Rev-Sizer caliper: 0.5 mm accuracy, banded-trigger design',
    'Measures fruit on the tree — no picking required',
    'Up to 3 000 fruit per day; blocks pre-loaded for block-by-block save',
    'Wi-Fi sync to RevSizing module inside RevToolbox V4',
    'Regional cultivar-based growth models',
    'Predicted size-class distribution — packhouse-ready'],

    visual: <window.SizeDistribution />,
    visual2: <window.TimeSeries accent="#70913C" />,
    v2Label: 'Growth curve vs. predicted target'
  },
  {
    id: 'svc-emi', tag: '05 · EMI',
    title: 'EMI Soil Scanning',
    lede: 'Where standard chemical grids only sample every 50 × 50 m, EMI scanning fills in the gaps — mapping soil Electrical Conductivity at three depths (25, 50 and 90 cm) at a practical 5 × 5 m resolution. The foundation every other layer sits on.',
    points: [
    'EC at 25, 50 and 90 cm — root, sub-root, drainage zones',
    '5 × 5 m practical resolution',
    'Targeted soil sampling instead of grid',
    'Drainage and irrigation block design',
    'Soil moisture probe placement recommendations'],

    visual: <window.SoilProfile />,
    visual2: <window.MiniMap variant="soil" />,
    v2Label: 'Block-level EC variation'
  }];


  return (
    <>
      <header className="page-head hero-bg" data-img-file="assets/ndvi-mosaic.png" style={{ '--page-head-bg': "url('assets/ndvi-mosaic.png')" }}>
        <div className="container-wide">
          <div className="crumbs">
            <a href="#" onClick={(e) => {e.preventDefault();setRoute('home');}}>Home</a>
            <span>/</span>
            <span>Data Services</span>
          </div>
          <h1>Five layers, <em>captured at tree resolution</em>.</h1>
          <p className="lede">Soil, canopy, fruit count, fruit size, prescription. Every service feeds the same RevToolbox portal, so every layer compares with every other. That’s where the answers come from.</p>
          <div style={{ display: 'flex', gap: 8, flexWrap: 'wrap', marginTop: 'var(--space-3)' }}>
            {services.map((s) =>
            <a key={s.id} href={`#${s.id}`} className="tag" style={{ padding: '6px 12px', fontSize: 12 }}>
                {s.tag.split(' · ')[1]}
              </a>
            )}
          </div>        </div>
      </header>

      {services.map((s, i) =>
      <section key={s.id} id={s.id} className={`section ${i % 2 === 1 ? 'warm' : ''}`}>
          <div className="container-wide">
            <div className="grid-2" style={{ alignItems: 'start' }}>
              <div>
                <div className="section-eyebrow">{s.tag}</div>
                <h2 className="section-title" style={{ fontSize: 'clamp(32px, 3.6vw, 48px)' }}>{s.title}</h2>
                <p className="section-lede" style={{ marginBottom: 'var(--space-3)' }}>{s.lede}</p>
                <ul style={{ listStyle: 'none', padding: 0, margin: 0, display: 'flex', flexDirection: 'column', gap: 10 }}>
                  {s.points.map((p, j) =>
                <li key={j} style={{ display: 'flex', gap: 12, fontSize: 15, lineHeight: 1.55 }}>
                      <span style={{ fontFamily: 'JetBrains Mono', color: 'var(--accent)', fontSize: 12, marginTop: 4, minWidth: 24 }}>
                        {String(j + 1).padStart(2, '0')}
                      </span>
                      <span>{p}</span>
                    </li>
                )}
                </ul>
                <div style={{ marginTop: 'var(--space-3)' }}>
                  <a className="btn btn-ghost" href="#" onClick={(e) => {e.preventDefault();setRoute('pricing');}}>See pricing →</a>
                </div>
              </div>
              <div style={{ display: 'flex', flexDirection: 'column', gap: 16 }}>
                <div style={{
                background: 'var(--paper)', border: '1px solid var(--line)',
                borderRadius: 'var(--radius-lg)', overflow: 'hidden', aspectRatio: '4/3'
              }}>
                  {s.visual}
                </div>
                <div style={{
                background: 'var(--paper)', border: '1px solid var(--line)',
                borderRadius: 'var(--radius-lg)', padding: 20
              }}>
                  <div style={{ fontFamily: 'JetBrains Mono', fontSize: 11, color: 'var(--muted)', marginBottom: 12, textTransform: 'uppercase', letterSpacing: '0.08em' }}>
                    {s.v2Label}
                  </div>
                  <div style={{ height: 180 }}>{s.visual2}</div>
                </div>
              </div>
            </div>
          </div>
        </section>
      )}

      {/* Zonal vs Grid editorial block — teases case study */}
      <section className="section warm" data-reveal>
        <div className="container-wide">
          <div style={{ display: 'grid', gridTemplateColumns: '0.95fr 1.05fr', gap: 'var(--space-5)', alignItems: 'center' }}>
            <div>
              <div className="section-eyebrow">From the field · 2024–25 season</div>
              <h2 className="section-title" style={{ marginBottom: 18 }}>
                Grid sampling tells you the <em>average.</em><br />
                EMI tells you <em>where to act.</em>
              </h2>
              <p className="section-lede" style={{ marginBottom: 18 }}>
                Across 301 hectares and eight farms last season, we measured what zonal sampling actually pays back vs. the standard one-composite-per-block grid. The answer was bigger than we expected.
              </p>
              <div className="grid-stats-row" style={{ marginBottom: 24 }}>
                <div>
                  <div style={{ fontFamily: 'var(--font-display)', fontSize: 'clamp(28px, 3vw, 44px)', fontWeight: 600, letterSpacing: '-0.02em', color: 'var(--accent)', lineHeight: 1 }}>9.1×</div>
                  <div style={{ fontFamily: 'JetBrains Mono, monospace', fontSize: 10, letterSpacing: '0.16em', color: 'var(--muted)', marginTop: 8, textTransform: 'uppercase' }}>ROI on EMI spend</div>
                </div>
                <div>
                  <div style={{ fontFamily: 'var(--font-display)', fontSize: 'clamp(28px, 3vw, 44px)', fontWeight: 600, letterSpacing: '-0.02em', color: 'var(--accent)', lineHeight: 1 }}>1.7–10.9%</div>
                  <div style={{ fontFamily: 'JetBrains Mono, monospace', fontSize: 10, letterSpacing: '0.16em', color: 'var(--muted)', marginTop: 8, textTransform: 'uppercase' }}>K range, single block</div>
                </div>
                <div>
                  <div style={{ fontFamily: 'var(--font-display)', fontSize: 'clamp(28px, 3vw, 44px)', fontWeight: 600, letterSpacing: '-0.02em', color: 'var(--accent)', lineHeight: 1 }}>954</div>
                  <div style={{ fontFamily: 'JetBrains Mono, monospace', fontSize: 10, letterSpacing: '0.16em', color: 'var(--muted)', marginTop: 8, textTransform: 'uppercase' }}>zone samples</div>
                </div>
              </div>
              <a className="btn btn-primary" href="#" onClick={(e) => {e.preventDefault();setRoute('case-study');}}>Read the case study →</a>
            </div>
            <div style={{ borderRadius: 14, overflow: 'hidden', border: '1px solid var(--line)', background: '#0E2E10' }} data-img-file="assets/emi-vigour-vs-ec.png">
              <img src="assets/emi-vigour-vs-ec.png" alt="Side-by-side NDVI vigour vs EC conductivity" style={{ display: 'block', width: '100%', height: 'auto' }} />
            </div>
          </div>
        </div>
      </section>

      <section className="cta-banner">
        <div className="container-wide" style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', flexWrap: 'wrap', gap: 'var(--space-3)' }}>
          <div>
            <h2 style={{ marginBottom: 0 }}>Want all five layers <em style={{ fontFamily: "'Instrument Serif', serif", color: '#B8DC73' }}>working together?</em></h2>
            <p style={{ marginTop: 12 }}>Soil scan + canopy + fruit map = a full optimization plan for the season.</p>
          </div>
          <div style={{ display: 'flex', gap: 10 }}>
            <a className="btn btn-primary btn-lg" href="#" onClick={(e) => {e.preventDefault();setRoute('optimize');}}>See applications →</a>
            <a className="btn btn-light btn-lg" href="#" onClick={(e) => {e.preventDefault();setRoute('contact');}}>Talk to us</a>
          </div>
        </div>
      </section>
    </>);

};

/* OptimizePage moved to page-optimize.jsx */


/* ============================================================
   PRICING PAGE
   ============================================================ */
window.PricingCalculator = function PricingCalculator({ setRoute }) {
  const { useState } = React;

  // Tiered + flat pricing from the 2026 Revolute pricing sheet.
  // bands: [threshold, ratePerHa] sorted DESC by threshold; first threshold <= ha wins.
  const PRODUCTS = [
  { id: 'emi', name: 'EMI Soil Scanning', kind: 'once', tag: 'Once-off', perHa: true,
    note: 'The soil foundation — scanned once, used every season after.',
    bands: [[1000, 910], [500, 1010], [300, 1060], [100, 1170], [0, 1380]] },
  { id: 'sat', name: 'RevToolbox Satellite · NDVI', kind: 'annual', tag: 'Per season', perHa: true,
    note: '10 m crop-health imagery every 5 days, all season.',
    bands: [[500, 140], [300, 160], [100, 220], [0, 270]] },
  { id: 'fruitmap', name: 'RevScout Fruit Mapping', kind: 'annual', tag: 'Per season', perHa: true,
    includes: ['sat'], note: 'Tree-level fruit load. RevToolbox NDVI included for the season.',
    bands: [[1001, 480], [601, 560], [301, 640], [101, 800], [0, 910]] },
  { id: 'revscouts', name: 'RevScout-S · Sizing + Mapping', kind: 'annual', tag: 'Per season', perHa: true,
    includes: ['sat', 'sizesw'], note: 'Count AND size in one pass. RevToolbox + Fruit Sizing included.',
    bands: [[1001, 560], [601, 650], [301, 740], [101, 920], [0, 1050]] },
  { id: 'sizesw', name: 'Fruit Sizing Software', kind: 'annual', tag: 'Per season', perHa: true,
    note: 'RevSizing platform — growth curves & harvest-size prediction.',
    bands: [[101, 220], [0, 320]] },
  { id: 'hires', name: 'High-res 80 cm Canopy', kind: 'annual', tag: 'Per season', perFarm: true,
    flat: 18550, note: 'On-demand tree-level canopy image, billed per farm.' },
  { id: 'caliper', name: 'Rev-Sizer Digital Caliper', kind: 'once', tag: 'Once-off', perUnit: true,
    unitPrice: 10070, note: 'Hardware — one per picker. Wi-Fi sync to the portal.' },
  { id: 'packhouse', name: 'Packhouse / Marketing Package', kind: 'annual', tag: 'Per season', flat: 93280,
    note: 'Unlimited farms & blocks — size forecasts for the whole packhouse.' }];


  const CURRENCIES = [
  { code: 'ZAR', symbol: 'R',   name: 'ZAR' },
  { code: 'USD', symbol: '$',   name: 'USD' },
  { code: 'EUR', symbol: '€',   name: 'EUR' },
  { code: 'GBP', symbol: '£',   name: 'GBP' },
  { code: 'AED', symbol: 'AED', name: 'AED' },
  { code: 'AUD', symbol: 'A$',  name: 'AUD' }];

  const FX_FALLBACK = { ZAR: 1, USD: 0.054, EUR: 0.050, GBP: 0.043, AED: 0.198, AUD: 0.082 };

  const [ha, setHa] = useState(250);
  const [farms, setFarms] = useState(1);
  const [calipers, setCalipers] = useState(2);
  const [sel, setSel] = useState({ emi: true, revscouts: true, sizesw: false, caliper: true });
  const [currency, setCurrency] = useState('ZAR');
  const [rates, setRates] = useState(FX_FALLBACK);

  React.useEffect(() => {
    fetch('https://open.er-api.com/v6/latest/ZAR')
      .then((r) => r.json())
      .then((d) => {
        if (d.rates) {
          const pick = {};
          CURRENCIES.forEach((c) => { if (d.rates[c.code] !== undefined) pick[c.code] = d.rates[c.code]; });
          setRates({ ZAR: 1, ...pick });
        }
      })
      .catch(() => {});
  }, []);

  const currDef = CURRENCIES.find((c) => c.code === currency);
  const fmt = (n) => {
    const r = Math.round(n * (rates[currency] || 1));
    return (currDef ? currDef.symbol : currency) + ' ' + r.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ' ');
  };
  const rateFor = (bands, h) => {for (const [thr, rate] of bands) {if (h >= thr) return rate;}return bands[bands.length - 1][1];};

  const includedSet = new Set();
  PRODUCTS.forEach((p) => {if (sel[p.id] && p.includes) p.includes.forEach((x) => includedSet.add(x));});

  // Fruit Sizing Software is per-hectare, but never costs more than the enterprise
  // (Packhouse / Marketing) package — at scale you'd buy that instead.
  const ENTERPRISE_CAP = PRODUCTS.find((p) => p.id === 'packhouse').flat;

  const lines = PRODUCTS.filter((p) => sel[p.id] || includedSet.has(p.id)).map((p) => {
    const included = includedSet.has(p.id);
    let cost = 0,detail = '';
    if (included) {cost = 0;detail = 'Included';} else
    if (p.perHa) {
      const r = rateFor(p.bands, ha);cost = r * ha;detail = fmt(r) + '/ha × ' + ha + ' ha';
      if (p.id === 'sizesw' && cost > ENTERPRISE_CAP) {cost = ENTERPRISE_CAP;detail = 'Enterprise cap · ' + fmt(ENTERPRISE_CAP);}
    } else
    if (p.perFarm) {cost = p.flat * farms;detail = fmt(p.flat) + ' × ' + farms + ' farm' + (farms > 1 ? 's' : '');} else
    if (p.perUnit) {cost = p.unitPrice * calipers;detail = fmt(p.unitPrice) + ' × ' + calipers + ' unit' + (calipers > 1 ? 's' : '');} else
    if (p.flat) {cost = p.flat;detail = 'Flat rate';}
    return { ...p, cost, detail, included };
  });

  const startup = lines.filter((l) => l.kind === 'once').reduce((s, l) => s + l.cost, 0);
  const running = lines.filter((l) => l.kind === 'annual').reduce((s, l) => s + l.cost, 0);
  const year1 = startup + running;

  const toggle = (id) => setSel((s) => ({ ...s, [id]: !s[id] }));
  const anySelected = lines.length > 0;
  const showFarms = sel.hires;
  const showCalipers = sel.caliper;

  const accent = 'var(--accent)';
  const stepBtn = { width: 32, height: 32, borderRadius: 8, border: '1px solid var(--line)', background: 'var(--paper)', cursor: 'pointer', fontFamily: 'var(--font-mono)', fontSize: 16, lineHeight: 1, color: 'var(--charcoal)' };

  return (
    <div className="pcalc">
      <style>{`
        .pcalc{display:grid;grid-template-columns:1.25fr 1fr;gap:var(--space-4);align-items:start}
        .pcalc-tools{display:grid;grid-template-columns:1fr 1fr;gap:12px}
        .pcalc-tool{display:flex;gap:12px;align-items:flex-start;text-align:left;padding:14px 16px;
          background:var(--paper);border:1px solid var(--line);border-radius:var(--radius-md);cursor:pointer;
          transition:border-color .15s,box-shadow .15s,background .15s}
        .pcalc-tool:hover{border-color:var(--accent)}
        .pcalc-tool.on{border-color:var(--accent);background:var(--accent-soft);box-shadow:0 6px 20px -12px rgba(28,65,0,.4)}
        .pcalc-tool.forced{cursor:default}
        .pcalc-check{flex:0 0 auto;width:20px;height:20px;border-radius:6px;border:1.5px solid var(--line);
          display:grid;place-items:center;margin-top:2px;font-size:12px;color:#fff;background:var(--paper)}
        .pcalc-tool.on .pcalc-check{background:var(--accent);border-color:var(--accent)}
        .pcalc-tool-name{display:block;font-weight:600;font-size:14.5px;letter-spacing:-0.01em;line-height:1.2}
        .pcalc-tool-tag{display:block;font-family:var(--font-mono);font-size:9px;letter-spacing:.12em;text-transform:uppercase;
          color:var(--muted);margin-top:4px}
        .pcalc-tool-note{display:block;font-size:12px;color:var(--muted);line-height:1.45;margin-top:7px}
        .pcalc-summary{position:sticky;top:90px;background:var(--charcoal);color:#fff;border-radius:var(--radius-lg);
          padding:var(--space-4);box-shadow:0 24px 64px -28px rgba(0,0,0,.5)}
        .pcalc-bignum{display:flex;flex-direction:column;gap:2px;margin-bottom:18px}
        .pcalc-bignum .lab{font-family:var(--font-mono);font-size:10px;letter-spacing:.16em;text-transform:uppercase;color:rgba(255,255,255,.5)}
        .pcalc-bignum .num{font-family:var(--font-display);font-size:clamp(30px,3.4vw,42px);font-weight:600;letter-spacing:-0.02em;line-height:1.05}
        .pcalc-split{display:grid;grid-template-columns:1fr 1fr;gap:14px;padding:16px 0;border-top:1px solid rgba(255,255,255,.14);border-bottom:1px solid rgba(255,255,255,.14)}
        .pcalc-split .lab{font-family:var(--font-mono);font-size:9.5px;letter-spacing:.12em;text-transform:uppercase;color:rgba(255,255,255,.5)}
        .pcalc-split .num{font-family:var(--font-display);font-size:22px;font-weight:600;margin-top:4px}
        .pcalc-split .sub{font-size:11px;color:rgba(255,255,255,.45);margin-top:2px}
        .pcalc-lines{list-style:none;padding:0;margin:16px 0 0;display:flex;flex-direction:column;gap:9px}
        .pcalc-lines li{display:grid;grid-template-columns:1fr auto;gap:10px;align-items:baseline;font-size:13px}
        .pcalc-lines .nm{color:rgba(255,255,255,.9)}
        .pcalc-lines .dt{font-family:var(--font-mono);font-size:10px;color:rgba(255,255,255,.4);display:block;margin-top:1px}
        .pcalc-lines .ct{font-family:var(--font-mono);font-size:13px;color:#B8DC73;white-space:nowrap}
        .pcalc-lines .ct.inc{color:rgba(255,255,255,.4)}
        .pcalc-empty{color:rgba(255,255,255,.5);font-size:13px;padding:8px 0}
        .pcalc-field{margin-bottom:18px}
        .pcalc-field-lab{display:flex;justify-content:space-between;align-items:baseline;margin-bottom:8px}
        .pcalc-field-lab span:first-child{font-family:var(--font-mono);font-size:11px;letter-spacing:.1em;text-transform:uppercase;color:var(--muted)}
        .pcalc-field-lab .val{font-family:var(--font-display);font-size:24px;font-weight:600;color:var(--charcoal)}
        .pcalc-range{width:100%;accent-color:var(--accent);height:4px}
        .pcalc-stepper{display:flex;align-items:center;gap:12px}
        .pcalc-stepper .q{font-family:var(--font-display);font-size:20px;font-weight:600;min-width:28px;text-align:center}
        @media (max-width:860px){
          .pcalc{grid-template-columns:1fr}
          .pcalc-tools{grid-template-columns:1fr}
          .pcalc-summary{position:static}
        }
      `}</style>

      {/* Controls */}
      <div>
        <div className="pcalc-field">
          <div className="pcalc-field-lab">
            <span>Orchard hectares</span>
            <span className="val">{ha} ha</span>
          </div>
          <input className="pcalc-range" type="range" min="1" max="2000" step="1" value={ha}
          onChange={(e) => setHa(parseInt(e.target.value, 10))} aria-label="Hectares" />
          <div style={{ display: 'flex', justifyContent: 'space-between', fontFamily: 'var(--font-mono)', fontSize: 10, color: 'var(--muted)', marginTop: 4 }}>
            <span>1 ha</span><span>2 000 ha</span>
          </div>
        </div>

        <div className="pcalc-field-lab" style={{ marginBottom: 12 }}>
          <span>Choose your tools</span>
        </div>
        <div className="pcalc-tools">
          {PRODUCTS.map((p) => {
            const forced = includedSet.has(p.id);
            const on = !!sel[p.id] || forced;
            return (
              <button key={p.id} type="button" className={'pcalc-tool' + (on ? ' on' : '') + (forced ? ' forced' : '')}
              onClick={() => {if (!forced) toggle(p.id);}} aria-pressed={on} aria-disabled={forced}>
                <span className="pcalc-check" aria-hidden="true">{on ? '✓' : ''}</span>
                <span style={{ minWidth: 0 }}>
                  <span className="pcalc-tool-name">{p.name}</span>
                  <span className="pcalc-tool-tag">{forced ? 'Included automatically' : p.tag}</span>
                  <span className="pcalc-tool-note">{p.note}</span>
                </span>
              </button>);

          })}
        </div>

        {(showFarms || showCalipers) &&
        <div style={{ display: 'flex', gap: 'var(--space-4)', marginTop: 'var(--space-3)', flexWrap: 'wrap' }}>
            {showFarms &&
          <div>
                <div className="pcalc-field-lab" style={{ marginBottom: 8 }}><span>Farms (hi-res image)</span></div>
                <div className="pcalc-stepper">
                  <button type="button" style={stepBtn} onClick={() => setFarms((f) => Math.max(1, f - 1))} aria-label="Fewer farms">−</button>
                  <span className="q">{farms}</span>
                  <button type="button" style={stepBtn} onClick={() => setFarms((f) => f + 1)} aria-label="More farms">+</button>
                </div>
              </div>
          }
            {showCalipers &&
          <div>
                <div className="pcalc-field-lab" style={{ marginBottom: 8 }}><span>Calipers</span></div>
                <div className="pcalc-stepper">
                  <button type="button" style={stepBtn} onClick={() => setCalipers((c) => Math.max(1, c - 1))} aria-label="Fewer calipers">−</button>
                  <span className="q">{calipers}</span>
                  <button type="button" style={stepBtn} onClick={() => setCalipers((c) => c + 1)} aria-label="More calipers">+</button>
                </div>
              </div>
          }
          </div>
        }
        <p style={{ fontSize: 12, color: 'var(--muted)', marginTop: 'var(--space-3)', lineHeight: 1.5 }}>
          Per-hectare rates step down automatically as your area grows. All figures ex-VAT
          {currency === 'ZAR' ? ', in ZAR.' : `, converted from ZAR to ${currency} at indicative rates.`}
          {' '}Quote confirmed with an agronomist.
        </p>
      </div>

      {/* Summary */}
      <div className="pcalc-summary">
        {/* Currency picker */}
        <div style={{ display: 'flex', gap: 5, flexWrap: 'wrap', marginBottom: 16 }}>
          {CURRENCIES.map((c) => (
            <button key={c.code} type="button"
              onClick={() => setCurrency(c.code)}
              style={{
                fontFamily: 'var(--font-mono)', fontSize: 10.5, letterSpacing: '0.10em',
                padding: '4px 9px', borderRadius: 4, cursor: 'pointer', transition: 'all .15s',
                border: currency === c.code ? '1px solid rgba(184,220,115,0.7)' : '1px solid rgba(255,255,255,0.14)',
                background: currency === c.code ? 'rgba(184,220,115,0.15)' : 'transparent',
                color: currency === c.code ? '#B8DC73' : 'rgba(255,255,255,0.50)'
              }}>{c.code}</button>
          ))}
        </div>
        <div className="pcalc-bignum">
          <span className="lab">Year 1 total</span>
          <span className="num">{fmt(year1)}</span>
        </div>
        <div className="pcalc-split">
          <div>
            <div className="lab">Startup · once-off</div>
            <div className="num">{fmt(startup)}</div>
            <div className="sub">EMI + hardware</div>
          </div>
          <div>
            <div className="lab">Running · per season</div>
            <div className="num">{fmt(running)}</div>
            <div className="sub">Year 2 onward</div>
          </div>
        </div>

        {anySelected ?
        <ul className="pcalc-lines">
            {lines.map((l) =>
          <li key={l.id}>
                <span className="nm">{l.name}<span className="dt">{l.detail}{l.kind === 'once' && !l.included ? ' · once-off' : ''}</span></span>
                <span className={'ct' + (l.included ? ' inc' : '')}>{l.included ? 'incl.' : fmt(l.cost)}</span>
              </li>
          )}
          </ul> :

        <div className="pcalc-empty">Select at least one tool to see a quote.</div>
        }

        <a className="btn btn-primary btn-lg" href="#" onClick={(e) => {e.preventDefault();setRoute('contact');}}
        style={{ width: '100%', justifyContent: 'center', marginTop: 'var(--space-3)' }}>
          Get this quote confirmed →
        </a>
      </div>
    </div>);

};

/* ============================================================
   PRICING PAGE
   ============================================================ */
window.PricingPage = function PricingPage({ setRoute }) {
  return (
    <>
      <header className="page-head">
        <div className="container-wide">
          <div className="crumbs">
            <a href="#" onClick={(e) => {e.preventDefault();setRoute('home');}}>Home</a>
            <span>/</span>
            <span>Pricing</span>
          </div>
          <h1>Build your <em>precision plan</em>.</h1>
          <p className="lede">Pick the tools you want and your block size — the calculator splits once-off startup from your per-season running cost. All prices ex-VAT, in South African Rand. EMI is a one-time scan; everything else runs by season.</p>
        </div>
      </header>

      <section className="section">
        <div className="container-wide">
          <window.PricingCalculator setRoute={setRoute} />
        </div>
      </section>

      {/* Comparison table removed per feedback (Jacobus) */}

      <section className="cta-banner">
        <div className="container-wide" style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', flexWrap: 'wrap', gap: 'var(--space-3)' }}>
          <div>
            <h2 style={{ marginBottom: 0 }}>Need a <em style={{ fontFamily: "'Instrument Serif', serif", color: '#B8DC73' }}>custom quote?</em></h2>
            <p style={{ marginTop: 12 }}>Multi-farm, multi-cultivar or group buys — we'll put together a plan.</p>
          </div>
          <a className="btn btn-primary btn-lg" href="#" onClick={(e) => {e.preventDefault();setRoute('contact');}}>Request a quote →</a>
        </div>
      </section>
    </>);

};

/* ============================================================
   CONTACT PAGE
   ============================================================ */
window.ContactPage = function ContactPage({ setRoute }) {
  const [submitted, setSubmitted] = React.useState(false);
  const [submitting, setSubmitting] = React.useState(false);
  const [error, setError] = React.useState(false);

  // Form-to-email endpoint. Formspree works on any static host
  // (Cloudflare Pages included) — it posts the form fields and
  // emails them to the address that owns this form ID. To point it
  // at your own inbox, create a form at formspree.io and replace the
  // ID below, or swap in another provider (Web3Forms, Getform, …).
  const FORM_ENDPOINT = 'https://formspree.io/f/xpwzgnje';

  const handleSubmit = async (e) => {
    e.preventDefault();
    setSubmitting(true);
    setError(false);
    const form = e.target;
    const data = new FormData(form);
    try {
      const res = await fetch(FORM_ENDPOINT, {
        method: 'POST',
        body: data,
        headers: { Accept: 'application/json' }
      });
      if (res.ok) {
        setSubmitted(true);
        form.reset();
      } else {
        // Surface the failure instead of faking success, so a broken
        // or unconfigured endpoint is obvious rather than silent.
        setError(true);
      }
    } catch {
      setError(true);
    }
    setSubmitting(false);
  };
  return (
    <>
      <header className="page-head">
        <div className="container-wide">
          <div className="crumbs">
            <a href="#" onClick={(e) => {e.preventDefault();setRoute('home');}}>Home</a>
            <span>/</span>
            <span>Contact</span>
          </div>
          <h1>Let's get <em>your orchard</em> online.</h1>
          <p className="lede">Tell us about your farm. We'll come back within one working day with a plan, pricing and a survey window.</p>
        </div>
      </header>

      <section className="section">
        <div className="container-wide">
          <div style={{ display: 'grid', gridTemplateColumns: '1.4fr 1fr', gap: 'var(--space-5)', alignItems: 'start' }}>
            <div style={{ background: 'var(--paper)', border: '1px solid var(--line)', borderRadius: 'var(--radius-lg)', padding: 'var(--space-4)' }}>
              {submitted ?
              <div style={{ padding: 'var(--space-4) 0', textAlign: 'center' }}>
                  <div style={{ fontFamily: "'Instrument Serif', serif", fontSize: 56, color: 'var(--accent)', fontStyle: 'italic' }}>Thanks!</div>
                  <p style={{ color: 'var(--muted)', maxWidth: 400, margin: '12px auto 0' }}>We've received your message and will be back within one working day.</p>
                </div> :

              <>
                  <div className="section-eyebrow" style={{ marginBottom: 16 }}>New project enquiry</div>
                  {error && (
                    <div role="alert" style={{ marginBottom: 16, padding: '14px 16px', borderRadius: 'var(--radius-md, 8px)', border: '1px solid rgba(180,60,40,0.35)', background: 'rgba(180,60,40,0.07)', fontSize: 14, lineHeight: 1.6, color: 'var(--charcoal)' }}>
                      We couldn't send your message just now. Please try again, or email us directly at{' '}
                      <a href="mailto:info@revolutesystems.com" style={{ color: 'var(--accent)' }}>info@revolutesystems.com</a>.
                    </div>
                  )}
                  <form className="form-grid" onSubmit={handleSubmit}>
                    {/* Formspree: subject line + honeypot spam trap */}
                    <input type="hidden" name="_subject" value="New RevField / Revolute Systems enquiry" />
                    <input type="text" name="_gotcha" tabIndex={-1} autoComplete="off" aria-hidden="true" style={{ position: 'absolute', left: '-9999px', width: 1, height: 1, opacity: 0 }} />
                    <div className="field">
                      <label>First name</label>
                      <input type="text" name="firstName" required />
                    </div>
                    <div className="field">
                      <label>Last name</label>
                      <input type="text" name="lastName" required />
                    </div>
                    <div className="field">
                      <label>Email</label>
                      <input type="email" name="email" required />
                    </div>
                    <div className="field">
                      <label>Phone</label>
                      <input type="tel" name="phone" />
                    </div>
                    <div className="field">
                      <label>Farm name</label>
                      <input type="text" name="farmName" />
                    </div>
                    <div className="field">
                      <label>Total hectares</label>
                      <input type="number" name="hectares" placeholder="e.g. 240" />
                    </div>
                    <div className="field span-2">
                      <label>Cultivar(s)</label>
                      <input type="text" name="cultivars" placeholder="e.g. Apple — Gala, Royal Gala, Granny Smith" />
                    </div>
                    <div className="field span-2">
                      <label>What are you most interested in?</label>
                      <select name="interest" defaultValue="">
                        <option value="" disabled>Choose a starting point…</option>
                        <option>EMI soil scan (foundation)</option>
                        <option>RevToolbox satellite subscription</option>
                        <option>RevScout fruit mapping</option>
                        <option>RevScout S — fruit mapping + sizing</option>
                        <option>RevSizer fruit sizing</option>
                        <option>The full Combo (soil + canopy + yield)</option>
                        <option>Just exploring — talk me through it</option>
                      </select>
                    </div>
                    <div className="field span-2">
                      <label>Tell us about your operation</label>
                      <textarea name="message" placeholder="Block layout, current challenges, what you've tried, harvest window…"></textarea>
                    </div>
                    <button type="submit" className="btn btn-primary btn-lg span-2" style={{ justifyContent: 'center' }} disabled={submitting}>
                      {submitting ? 'Sending…' : 'Send enquiry →'}
                    </button>
                  </form>
                </>
              }
            </div>

            <aside style={{ display: 'flex', flexDirection: 'column', gap: 'var(--space-3)' }}>
              <div style={{ background: 'var(--paper-warm)', border: '1px solid var(--line)', borderRadius: 'var(--radius-lg)', padding: 'var(--space-3)' }}>
                <div className="section-eyebrow" style={{ marginBottom: 12 }}>Operations</div>
                <div style={{ fontSize: 14, lineHeight: 1.7 }}>
                  <strong style={{ display: 'block', fontSize: 16, marginBottom: 2 }}>Jacobus</strong>
                  <span style={{ color: 'var(--muted)' }}>Field surveys & operations</span><br />
                  <a href="tel:+27623516182" style={{ color: 'var(--accent)' }}>+27 62 351 6182</a><br />
                  <a href="mailto:Jacobus@revolutesystems.com" style={{ color: 'var(--accent)' }}>Jacobus@revolutesystems.com</a>
                </div>
              </div>
              <div style={{ background: 'var(--paper-warm)', border: '1px solid var(--line)', borderRadius: 'var(--radius-lg)', padding: 'var(--space-3)' }}>
                <div className="section-eyebrow" style={{ marginBottom: 12 }}>Software</div>
                <div style={{ fontSize: 14, lineHeight: 1.7 }}>
                  <strong style={{ display: 'block', fontSize: 16, marginBottom: 2 }}>Berno</strong>
                  <span style={{ color: 'var(--muted)' }}>RevToolbox & data portal</span><br />
                  <a href="mailto:Berno@revolutesystems.com" style={{ color: 'var(--accent)' }}>Berno@revolutesystems.com</a>
                </div>
              </div>
              <div style={{ background: 'var(--paper-warm)', border: '1px solid var(--line)', borderRadius: 'var(--radius-lg)', padding: 'var(--space-3)' }}>
                <div className="section-eyebrow" style={{ marginBottom: 12 }}>Office</div>
                <div style={{ fontSize: 14, lineHeight: 1.7, color: 'var(--charcoal)' }}>
                  Datavoice House<br />
                  16 Elektron Avenue, Technopark<br />
                  Stellenbosch<br />
                  Western Cape, South Africa, 7699
                </div>
              <div data-img-file="assets/portal-vigour-mapping.png" style={{ marginTop: 16, height: 120, borderRadius: 8, overflow: 'hidden', backgroundImage: "url('assets/portal-vigour-mapping.png')", backgroundSize: 'cover', backgroundPosition: 'center', border: '1px solid var(--line)' }} role="img" aria-label="Block vigour mapping preview" />
              </div>
            </aside>
          </div>
        </div>
      </section>
    </>);

};

Object.assign(window, {
  ServicesPage: window.ServicesPage,
  OptimizePage: window.OptimizePage,
  PricingPage: window.PricingPage,
  ContactPage: window.ContactPage
});