// Hero + about + trust strip
const HERO_PRODUCE = ['Peach','Cherry','Strawberry','Lemon','Mango','Tomato','Pepper','Grape'];

// Resolves an asset through the bundler's inlined resource map when the page has
// been compiled to a standalone file, falling back to the relative path in dev.
const R = (id, path) => (typeof window !== 'undefined' && window.__resources && window.__resources[id]) || path;

// Plays a list of MP4s in sequence, crossfading between clips so there is no
// flash of a static frame. Two stacked <video> elements; the off-screen one
// preloads the next clip while the on-screen one is playing.
//
// On poor connections the videos can fail to load entirely. We:
//   - show `poster` (the hero photo) underneath at all times, so the hero is
//     never blank
//   - if no clip has reached `canplay` within FALLBACK_MS, give up on video
//     and let the poster carry the hero
// Detect networks where ~12-25MB MP4s won't realistically load. We skip the
// video entirely on save-data, 2g/3g, or anything reporting <1.5Mbps downlink.
function isSlowConnection() {
  if (typeof navigator === 'undefined') return false;
  const c = navigator.connection || navigator.mozConnection || navigator.webkitConnection;
  if (!c) return false;
  if (c.saveData) return true;
  if (c.effectiveType && /^(2g|slow-2g)$/.test(c.effectiveType)) return true;
  if (typeof c.downlink === 'number' && c.downlink > 0 && c.downlink < 0.7) return true;
  return false;
}

function VideoCycle({ sources = [], poster = '' }){
  const FALLBACK_MS = 15000; // give cellular networks plenty of time to buffer
  const [i, setI] = React.useState(0);
  const [active, setActive] = React.useState(0);
  const [failed, setFailed] = React.useState(() => isSlowConnection());
  const [ready, setReady] = React.useState(false);
  const refs = [React.useRef(null), React.useRef(null)];

  const nextIdx = sources.length ? (i + 1) % sources.length : 0;

  // If the active clip never reaches canplay (slow connection, blocked CDN),
  // surrender and show the poster.
  React.useEffect(() => {
    if (failed || ready) return;
    const t = setTimeout(() => { if (!ready) setFailed(true); }, FALLBACK_MS);
    return () => clearTimeout(t);
  }, [failed, ready]);

  React.useEffect(() => {
    if (failed) return;
    const v = refs[active].current;
    if (!v) return;
    v.muted = true;
    const p = v.play();
    if (p && typeof p.catch === 'function') p.catch(() => {});
  }, [active, i, failed]);

  // iOS often blocks muted autoplay on cellular until a user gesture. We
  // listen on every touch/scroll/click — NOT `once` — so each interaction
  // gets another shot at kicking the video off until it actually plays.
  React.useEffect(() => {
    if (ready) return;
    const tryPlay = () => {
      setFailed(false);
      const v = refs[active].current;
      if (!v) return;
      v.muted = true;
      v.playsInline = true;
      try { if (v.readyState < 2) v.load(); } catch (e) {}
      const p = v.play();
      if (p && typeof p.catch === 'function') p.catch(() => {});
    };
    const opts = { passive: true };
    window.addEventListener('touchstart', tryPlay, opts);
    window.addEventListener('touchend',   tryPlay, opts);
    window.addEventListener('scroll',     tryPlay, opts);
    window.addEventListener('click',      tryPlay, opts);
    return () => {
      window.removeEventListener('touchstart', tryPlay);
      window.removeEventListener('touchend',   tryPlay);
      window.removeEventListener('scroll',     tryPlay);
      window.removeEventListener('click',      tryPlay);
    };
  }, [active, ready, failed]);

  const handleEnded = () => {
    if (sources.length <= 1) return;
    setActive(active === 0 ? 1 : 0);
    setI(nextIdx);
  };

  return (
    <div style={{position:'relative', width:'100%', height:'100%'}}>
      {/* Poster only shows once we've decided videos can't load.
          While loading or playing successfully, leave background plain. */}
      {poster && failed && (
        <div style={{
          position:'absolute', inset:0, backgroundImage:`url("${poster}")`,
          backgroundSize:'cover', backgroundPosition:'center right',
          backgroundRepeat:'no-repeat'
        }}/>
      )}
      {!failed && [0, 1].map((slotIdx) => {
        const srcIdx = slotIdx === active ? i : nextIdx;
        const isVisible = slotIdx === active && ready;
        return (
          <video
            key={slotIdx}
            ref={refs[slotIdx]}
            src={sources[srcIdx]}
            autoPlay={slotIdx === active}
            muted
            loop={sources.length === 1}
            playsInline
            preload="auto"
            controls={false}
            onCanPlay={slotIdx === active ? () => setReady(true) : undefined}
            onError={slotIdx === active ? () => setFailed(true) : undefined}
            onStalled={slotIdx === active ? () => { /* keep poster, video will catch up if it can */ } : undefined}
            onEnded={slotIdx === active ? handleEnded : undefined}
            disablePictureInPicture
            disableRemotePlayback
            x-webkit-airplay="deny"
            style={{
              // Keep the video in the DOM at opacity 0 so iOS Safari is
              // actually buffering it. `display:none` blocks preloading and
              // means nothing is ready to play when the user taps.
              position:'absolute', inset:0, width:'100%', height:'100%',
              objectFit:'cover', display:'block',
              opacity: isVisible ? 1 : 0,
              transition:'opacity 600ms ease',
              background:'transparent',
              pointerEvents:'none'
            }}
          />
        );
      })}
    </div>
  );
}

// Live counters in the hero meta row — pulls statuses from the Google Sheet
// and tallies "in transit" / "delivered" / "in customs" with a soft pulse.
// DELIVERED_BASELINE accounts for historical shipments that aren't in the
// sheet — bump it up over time so the running total stays accurate.
const DELIVERED_BASELINE = 132;
function ShipmentCounters(){
  const data = (typeof useShipments === 'function') ? useShipments() : { shipments:null };
  const counts = React.useMemo(() => {
    const list = data.shipments || [];
    let inTransit = 0, delivered = 0, customs = 0;
    for (const s of list) {
      const st = String(s.status || '').toLowerCase();
      if (/deliver/.test(st)) delivered++;
      else if (/custom|hold|clearance/.test(st)) customs++;
      else inTransit++; // in-transit, shipped, on the way, confirmed, etc.
    }
    return { inTransit, delivered: delivered + DELIVERED_BASELINE, customs };
  }, [data.shipments]);

  const Pill = ({color, dot, label, n}) => (
    <span className="hero-counter" style={{
      display:'inline-flex', alignItems:'center', gap:9,
      padding:'8px 16px 8px 12px', borderRadius:99,
      background:'var(--paper)', border:'1px solid var(--line)'
    }}>
      <span className="counter-dot" style={{background:color, boxShadow:`0 0 0 4px ${dot}`}}/>
      <span style={{fontFamily:'var(--sans)', fontWeight:500, fontSize:14, color:'var(--ink)'}}>{n}</span>
      <span style={{fontFamily:'var(--sans)', fontSize:11, fontWeight:600, letterSpacing:'0.05em', textTransform:'uppercase', color:'var(--ink-3)'}}>{label}</span>
    </span>
  );

  return (
    <div className="hero-meta-row" style={{display:'flex', justifyContent:'flex-start', alignItems:'center', padding:0, position:'relative', zIndex:1, gap:10, flexWrap:'wrap'}}>
      <Pill color="#E15A3C" dot="rgba(225,90,60,0.18)" label="in transit" n={counts.inTransit}/>
      <Pill color="#C9A227" dot="rgba(201,162,39,0.18)" label="in customs" n={counts.customs}/>
      <Pill color="#2E7D4F" dot="rgba(46,125,79,0.18)" label="delivered" n={counts.delivered}/>
    </div>
  );
}

// Hero background treatments — eight options the user can flip via Tweaks.
// All sit BEHIND the headline + container content; nothing hijacks the type.
function HeroBackground({ variant='none' }){
  if (variant === 'none') return null;

  if (variant === 'grid') {
    return (
      <div aria-hidden="true" style={{
        position:'absolute', inset:0, pointerEvents:'none', zIndex:0,
        backgroundImage:`
          linear-gradient(to right, color-mix(in oklab, var(--forest) 14%, transparent) 1px, transparent 1px),
          linear-gradient(to bottom, color-mix(in oklab, var(--forest) 14%, transparent) 1px, transparent 1px)
        `,
        backgroundSize:'48px 48px',
        maskImage:'radial-gradient(ellipse at 30% 40%, #000 30%, transparent 80%)',
        WebkitMaskImage:'radial-gradient(ellipse at 30% 40%, #000 30%, transparent 80%)'
      }}/>
    );
  }

  if (variant === 'video-cherries') {
    // Cycling playlist: cherries → nectarines → strawberries (when supplied).
    // VideoCycle handles autoplay, mute, loop on last clip until next file is
    // available. Free commercial use, no attribution required (Pexels).
    return (
      <div aria-hidden="true" style={{
        position:'absolute', top:0, bottom:0, left:'50%', width:'min(100%, var(--container))', transform:'translateX(-50%)', pointerEvents:'none', zIndex:0, overflow:'hidden'
      }}>
        <div className="hero-bg-media" style={{
          position:'absolute', top:0, right:0, width:'56%', height:'100%',
          maskImage:'linear-gradient(to left, #000 70%, transparent 100%), linear-gradient(to bottom, #000 80%, transparent 100%)',
          WebkitMaskImage:'linear-gradient(to left, #000 70%, transparent 100%), linear-gradient(to bottom, #000 80%, transparent 100%)',
          maskComposite:'intersect',
          WebkitMaskComposite:'source-in'
        }}>
          <VideoCycle
            poster={R('heroPoster','assets/hero-stonefruit.jpg')}
            sources={[
              'https://pub-9b2814a73e8546d4824315d3460fcfd2.r2.dev/4513018-uhd_4096_2160_24fps.mp4',
              'https://pub-9b2814a73e8546d4824315d3460fcfd2.r2.dev/4513019-uhd_4096_2160_24fps.mp4',
              'https://pub-9b2814a73e8546d4824315d3460fcfd2.r2.dev/4513022-uhd_4096_2160_24fps.mp4'
            ]}
          />
        </div>
      </div>
    );
  }

  if (variant === 'photo-right') {
    // Real product photography supplied by P&L England — stone-fruit medley
    // (cherries, plums, peaches, nectarines, apricots) on white. Sits on the
    // right ~52% of the hero, with a soft mask fading the very far edge into
    // the paper so themes with non-white backgrounds (forest, market) don't
    // show a hard edge.
    return (
      <div aria-hidden="true" style={{
        position:'absolute', top:0, bottom:0, left:'50%', width:'min(100%, var(--container))', transform:'translateX(-50%)', pointerEvents:'none', zIndex:0, overflow:'hidden'
      }}>
        <div className="hero-bg-media" style={{
          position:'absolute', top:0, right:0, width:'56%', height:'100%',
          backgroundImage:`url("${R('heroPoster','assets/hero-stonefruit.jpg')}")`,
          backgroundSize:'cover', backgroundPosition:'center right',
          backgroundRepeat:'no-repeat',
          maskImage:'linear-gradient(to left, #000 70%, transparent 100%), linear-gradient(to bottom, #000 80%, transparent 100%)',
          WebkitMaskImage:'linear-gradient(to left, #000 70%, transparent 100%), linear-gradient(to bottom, #000 80%, transparent 100%)',
          maskComposite:'intersect',
          WebkitMaskComposite:'source-in'
        }}/>
      </div>
    );
  }

  if (variant === 'photo-strip') {
    const photos = [
      'https://images.unsplash.com/photo-1553279768-865429fa0078?w=600&q=80', // mango
      'https://images.unsplash.com/photo-1519996529931-28324d5a630e?w=600&q=80', // avocado
      'https://images.unsplash.com/photo-1574631818020-72147f6b30db?w=600&q=80', // passion fruit
      'https://images.unsplash.com/photo-1528821128474-27f963b062bf?w=600&q=80', // cherries
      'https://images.unsplash.com/photo-1515872474884-c6a1b13a78fa?w=600&q=80', // asparagus
    ];
    return (
      <div aria-hidden="true" style={{
        position:'absolute', left:0, right:0, bottom:0, height:240, zIndex:0, display:'flex',
        borderTop:'1px solid var(--line)'
      }}>
        {photos.map((src,i)=>(
          <div key={i} style={{
            flex:1, backgroundImage:`url("${src}")`, backgroundSize:'cover', backgroundPosition:'center',
            filter:'saturate(1.05)'
          }}/>
        ))}
      </div>
    );
  }

  if (variant === 'topo') {
    // Faint dotted route lines + tiny city dots — "we ship globally" without the
    // glossy world-map cliché.
    return (
      <svg aria-hidden="true" viewBox="0 0 1200 600" preserveAspectRatio="xMidYMid slice"
           style={{position:'absolute', inset:0, width:'100%', height:'100%', zIndex:0, opacity:0.55}}>
        <defs>
          <pattern id="topoDots" width="6" height="6" patternUnits="userSpaceOnUse">
            <circle cx="1" cy="1" r="0.6" fill="var(--forest)" opacity="0.18"/>
          </pattern>
        </defs>
        <rect width="1200" height="600" fill="url(#topoDots)"/>
        {[
          ['M 200 360 Q 460 180 760 220','Lima','Nairobi'],
          ['M 760 220 Q 880 280 1040 200','Nairobi','London'],
          ['M 1040 200 Q 1080 230 1140 260','London','Dubai'],
          ['M 200 360 Q 540 480 880 440','Madrid','Mombasa'],
        ].map((p,i)=>(
          <path key={i} d={p[0]} fill="none" stroke="var(--forest)" strokeWidth="1.2" strokeDasharray="3 5" opacity="0.5"/>
        ))}
        {[[200,360],[760,220],[1040,200],[1140,260],[880,440],[540,480]].map(([x,y],i)=>(
          <circle key={i} cx={x} cy={y} r="3.5" fill="var(--tomato)" opacity="0.7"/>
        ))}
      </svg>
    );
  }

  if (variant === 'texture') {
    // Paper-fibre noise — SVG turbulence, very low opacity.
    return (
      <svg aria-hidden="true" style={{position:'absolute', inset:0, width:'100%', height:'100%', zIndex:0, opacity:0.35, mixBlendMode:'multiply'}}>
        <filter id="paperGrain">
          <feTurbulence type="fractalNoise" baseFrequency="0.85" numOctaves="2" stitchTiles="stitch"/>
          <feColorMatrix values="0 0 0 0 0.45  0 0 0 0 0.40  0 0 0 0 0.30  0 0 0 0.18 0"/>
        </filter>
        <rect width="100%" height="100%" filter="url(#paperGrain)"/>
      </svg>
    );
  }

  if (variant === 'botanical') {
    // Hand-drawn line-art mango branch in tomato red, large in the corner.
    return (
      <svg aria-hidden="true" viewBox="0 0 600 600"
           style={{position:'absolute', top:-40, right:-80, width:'48vw', maxWidth:680, height:'auto', zIndex:0, opacity:0.18}}>
        <g fill="none" stroke="var(--tomato)" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round">
          {/* branch */}
          <path d="M 60 480 C 180 380, 280 360, 360 280"/>
          <path d="M 360 280 C 400 250, 440 220, 480 160"/>
          {/* leaf 1 */}
          <path d="M 200 410 C 260 360, 320 360, 340 410 C 320 440, 240 440, 200 410 Z"/>
          <path d="M 220 412 C 260 405, 300 405, 330 412"/>
          {/* leaf 2 */}
          <path d="M 290 360 C 350 320, 410 330, 430 380 C 410 410, 320 410, 290 360 Z"/>
          <path d="M 310 365 C 350 360, 390 365, 420 380"/>
          {/* mango fruit */}
          <ellipse cx="380" cy="240" rx="78" ry="58" transform="rotate(-25 380 240)"/>
          <path d="M 320 230 C 360 215, 400 215, 440 235" opacity="0.6"/>
          {/* small leaf at top */}
          <path d="M 460 170 C 490 145, 520 145, 530 175 C 520 195, 480 195, 460 170 Z"/>
        </g>
      </svg>
    );
  }

  if (variant === 'data-strip') {
    // Quiet morning-status line at the very bottom of the hero. Doesn't fight
    // the headline; says "real working business."
    return (
      <div aria-hidden="true" style={{
        position:'absolute', left:0, right:0, bottom:0, zIndex:0,
        borderTop:'1px solid var(--line)',
        background:'color-mix(in oklab, var(--paper) 70%, var(--paper-2))',
        padding:'12px 0'
      }}>
        <div className="container">
          <div style={{
            display:'flex', justifyContent:'space-between', alignItems:'center', gap:24, flexWrap:'wrap',
            fontFamily:'ui-monospace, "SF Mono", Menlo, monospace',
            fontSize:11.5, letterSpacing:'0.12em', textTransform:'uppercase',
            color:'var(--ink-2)'
          }}>
            <span style={{display:'inline-flex', alignItems:'center', gap:8}}>
              <span style={{width:7, height:7, borderRadius:'50%', background:'#22C55E'}}/>
              This morning, {new Date().toLocaleDateString('en-GB',{weekday:'short', day:'numeric', month:'short'})}
            </span>
            <span>NBO → LHR · KQ100 · 4.2T mango + passion</span>
            <span>AMS → DXB · KL427 · 1.8T mixed pallet</span>
            <span style={{color:'var(--tomato)', fontWeight:500}}>14 AWBs in transit</span>
          </div>
        </div>
      </div>
    );
  }

  if (variant === 'glow') {
    return (
      <div aria-hidden="true" style={{
        position:'absolute', inset:0, pointerEvents:'none', zIndex:0,
        background:`
          radial-gradient(ellipse 60% 50% at 100% 0%, color-mix(in oklab, var(--tomato) 22%, transparent) 0%, transparent 60%),
          radial-gradient(ellipse 50% 60% at 0% 100%, color-mix(in oklab, var(--forest) 18%, transparent) 0%, transparent 60%)
        `
      }}/>
    );
  }

  return null;
}

function Hero({ variant='editorial', heroBg='none' }){
  const isPhotoRight = heroBg === 'photo-right' || heroBg === 'video-cherries';
  return (
    <section style={{position:'relative', overflow:'hidden', paddingTop:24, paddingBottom: heroBg === 'photo-strip' ? 240 : (heroBg === 'data-strip' ? 56 : 0)}}>
      <HeroBackground variant={heroBg}/>
      <div className="container" style={{position:'relative'}}>
        {/* Grid lines — hidden on photo/video hero variants where they read
            as accidental scratches across the imagery. */}
        {!isPhotoRight && (
          <div style={{position:'absolute', inset:'0 var(--gutter)', pointerEvents:'none', opacity:0.5}}>
            <div style={{position:'absolute', top:0, bottom:0, left:'25%', width:1, background:'var(--line-soft)'}}/>
            <div style={{position:'absolute', top:0, bottom:0, left:'50%', width:1, background:'var(--line-soft)'}}/>
            <div style={{position:'absolute', top:0, bottom:0, left:'75%', width:1, background:'var(--line-soft)'}}/>
          </div>
        )}

        {/* Headline row — big headline left, short supporting copy + CTA
            right, side by side at the same baseline (mirrors the reference:
            one clear headline, one short line, one button — no extra tiers). */}
        <div className={isPhotoRight ? 'hero-col-left' : ''} style={{
          position:'relative', zIndex:1, paddingTop:16, paddingBottom: isPhotoRight ? 0 : 32,
          maxWidth: isPhotoRight ? '66%' : 'none'
        }}>
          <div className="eyebrow" style={{marginBottom:20, marginLeft:0, marginRight:0, textTransform:'none', display:'flex', alignItems:'center', gap:6, fontSize:11, padding:'5px 12px', boxShadow:'none', background:'rgba(255,255,255,0.08)', color:'inherit'}}>
            <span>Fresh Produce Export · Paris</span>
            <span aria-hidden="true" style={{fontSize:'1.1em'}}>✈</span>
            <span>Worldwide</span>
          </div>
          <Typewriter className="h-display" style={{maxWidth: isPhotoRight ? '72%' : '13ch', margin:'0 0 24px'}}/>
          {!isPhotoRight && (
            <div style={{display:'flex', flexDirection:'column', gap:24, marginTop:32, maxWidth:520}}>
              <p className="lead" style={{margin:0, color:'var(--ink-2)'}}>
                Sourced directly from <em style={{fontStyle:'italic'}}>Rungis International Market, Paris</em> — the world's largest fresh produce market. We export fruit and vegetables from Paris Rungis via fresh produce air freight from France, delivered to wholesalers, distributors and retailers worldwide.
              </p>
              <div className="hero-cta-row" style={{display:'flex', gap:12, flexWrap:'wrap', justifyContent:'center'}}>
                <a href="https://calendly.com/plengland-sales/30min" target="_blank" rel="noopener noreferrer" className="btn btn-primary">Book a call <Icon.Arrow size={14}/></a>
              </div>
            </div>
          )}
          <style>{`
            @keyframes typeCaret {
              0%, 50% { opacity: 1; }
              51%, 100% { opacity: 0; }
            }
            .typewriter-caret {
              display: inline-block;
              width: 0.06em;
              height: 0.85em;
              background: var(--tomato);
              vertical-align: -0.08em;
              margin-left: 0.04em;
              animation: typeCaret 0.85s step-end infinite;
              transform: translateY(0.05em);
            }
            @media (max-width: 900px) {
              .hero-col-left { max-width: 100% !important; }
              .hero-row { grid-template-columns: 1fr !important; gap: 24px !important; align-items: start !important; }
              .hero-overlay-copy {
                position: relative !important; top: auto !important; right: auto !important;
                width: 100% !important; max-width: 100% !important;
                margin-top: 4px !important; text-align: center !important;
                align-items: center !important;
              }
              .hero-overlay-copy .lead { margin-left: auto !important; margin-right: auto !important; }
              .hero-overlay-copy .hero-cta-row { justify-content: center !important; }
              .hero-overlay-copy .lead, .hero-overlay-copy p { text-shadow: none !important; color: var(--ink-2) !important; }
              .hero-overlay-copy .btn[style*="rgba(255,255,255,0.12)"] { background: var(--paper-2) !important; color: var(--ink) !important; border-color: var(--line) !important; }
            }
@media (max-width: 900px) {
              .hero-col-left { padding-bottom: 4px !important; }
            }
          `}</style>
        </div>

        {/* Copy + CTA, overlaid on the video/photo backdrop when one is
            active — sits in the right-hand media area rather than squeezing
            beside the headline. */}
        {isPhotoRight && (
          <div className="hero-overlay-copy" style={{
            position:'absolute', zIndex:2, right:'calc(var(--gutter) + 4%)', top:220,
            width:'38%', maxWidth:400,
            display:'flex', flexDirection:'column', gap:20
          }}>
            <p className="lead" style={{margin:0, color:'var(--cream)', textShadow:'0 2px 16px rgba(0,0,0,0.35)'}}>
              Sourced directly from <em style={{fontStyle:'italic'}}>Rungis International Market, Paris</em> — the world's largest fresh produce market.
            </p>
            <div className="hero-cta-row" style={{display:'flex', gap:12, flexWrap:'wrap', justifyContent:'center'}}>
              <a href="https://calendly.com/plengland-sales/30min" target="_blank" rel="noopener noreferrer" className="btn btn-primary">Book a call <Icon.Arrow size={14}/></a>
            </div>
          </div>
        )}

        {/* Hero visual: live shipment card — always shown so customers can
            click straight through to the live AWB. Sits below the headline. */}
        <div style={{position:'relative', zIndex:1, marginTop:40, marginBottom:40}}>
          <HeroVisual/>
        </div>
      </div>
    </section>
  );
}

// Typewriter — animates the H1 in three "phrases" so the reveal feels written
// rather than stamped. Uses requestAnimationFrame, respects prefers-reduced-motion,
// and renders the final markup statically once finished so SEO + accessibility
// keep working. Speeds tuned so the whole reveal lands in ~3.5s.
function Typewriter({ className, style }){
  // Each segment carries the rendered React node + a flat string used for
  // measuring how many chars to reveal. We split into 3 phrases for natural
  // pauses between them.
  const segments = React.useMemo(() => ([
    { plain: 'We source premium fresh produce', node: 'We source premium fresh produce' },
    { plain: ' and deliver directly to ', node: ' and deliver directly to ' },
    { plain: 'wholesalers, distributors and retailers', node: <span className="leaf" key="leaf">wholesalers, distributors and retailers</span>, italic:false },
    { plain: ' ', node: ' ' },
    { plain: 'worldwide.', node: <em key="em">worldwide.</em> },
  ]), []);

  const total = segments.reduce((a,s)=>a + s.plain.length, 0);
  const reduce = typeof window !== 'undefined'
    && window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches;
  const [n, setN] = React.useState(reduce ? total : 0);

  React.useEffect(() => {
    if (reduce) return;
    let raf;
    let start = null;
    // Char-per-second pacing with small extra delay between segments to fake
    // a "thinking" pause between phrases.
    const cps = 38;
    const tick = (t) => {
      if (start == null) start = t;
      const elapsed = (t - start) / 1000;
      // Compute target n with phrase-pauses baked in: a 220ms pause at the
      // boundary of phrases 0|1, 2|3, 3|4.
      const pauses = [0, 0, 0.18, 0.18, 0.05];
      let consumed = 0;
      let chars = elapsed * cps;
      let target = 0;
      for (let i = 0; i < segments.length; i++) {
        const need = segments[i].plain.length;
        const pause = pauses[i] * cps;
        if (chars > pause) {
          chars -= pause;
          if (chars >= need) { chars -= need; target += need; consumed += need; }
          else { target += chars; chars = 0; break; }
        } else { break; }
      }
      target = Math.min(total, Math.floor(target));
      setN(target);
      if (target < total) raf = requestAnimationFrame(tick);
    };
    raf = requestAnimationFrame(tick);
    return () => cancelAnimationFrame(raf);
  }, [reduce, total, segments]);

  // Render up to `n` characters across segments.
  let remaining = n;
  const rendered = [];
  for (let i = 0; i < segments.length; i++) {
    const s = segments[i];
    if (remaining <= 0) break;
    const take = Math.min(remaining, s.plain.length);
    if (take >= s.plain.length) {
      rendered.push(<React.Fragment key={i}>{s.node}</React.Fragment>);
    } else {
      // Mid-segment: show partial plain text inside the same wrapper as the
      // final node so styling stays consistent.
      const partial = s.plain.slice(0, take);
      if (typeof s.node === 'string') {
        rendered.push(<React.Fragment key={i}>{partial}</React.Fragment>);
      } else if (s.node && s.node.type === 'em') {
        rendered.push(<em key={i}>{partial}</em>);
      } else if (s.node && s.node.props && s.node.props.className === 'leaf') {
        rendered.push(<span key={i} className="leaf">{partial}</span>);
      } else {
        rendered.push(<React.Fragment key={i}>{partial}</React.Fragment>);
      }
    }
    remaining -= take;
  }

  return (
    <h1 className={className} style={{...style, position:'relative'}} aria-label="We source premium fresh produce and deliver directly to wholesalers, distributors and retailers worldwide.">
      {/* Invisible mirror of the final headline — reserves the full height up
          front so the section doesn't reflow as characters are added (which
          previously caused the right-side hero photo to "zoom" each frame). */}
      <span aria-hidden="true" style={{
        visibility:'hidden', display:'block', whiteSpace:'normal'
      }}>
        We source premium fresh produce and deliver directly to <span className="leaf">wholesalers, distributors and retailers</span> <em>worldwide.</em>
      </span>
      <span style={{position:'absolute', inset:0, display:'block'}}>
        {rendered}
        {n < total && <span className="typewriter-caret" aria-hidden="true"/>}
      </span>
    </h1>
  );
}

// Pull a bracketed IATA code out of "Dar-es-Salaam (DAR)" when present,
// otherwise fall back to the plain sheet value.
function airportCode(v){
  const m = String(v || '').match(/\(([A-Za-z]{3})\)/);
  return m ? m[1].toUpperCase() : String(v || '');
}

// Status glyphs reuse the site's existing icon set (graphics.jsx) so weights and
// linecaps match the rest of the page. "Departed" is the same Icon.Plane rotated
// nose-up rather than a separately drawn path.
// Arrived uses the shared stroke Icon.Check. Departed/In Transit use the ✈
// glyph already established in the hero eyebrow ("Paris ✈ Worldwide") — a solid
// aeroplane silhouette reads far better than a stroke icon at marker size.
const StatusIcon = ({ kind, size=16 }) => (
  <span aria-hidden="true" style={{fontSize:size, lineHeight:1, display:'block'}}>✈</span>
);

const HERO_TRACKER_MAX = 3;
function HeroVisual(){
  // Pull live shipments off the Google Sheet. When several are moving at once
  // (multiple departed / in transit) we stack up to HERO_TRACKER_MAX rows in one
  // card rather than showing only the newest AWB. Falls back to a static
  // placeholder if the sheet is unreachable on first paint.
  const { shipments } = useShipments();
  const list = React.useMemo(() => {
    const all = shipments || [];
    // Stacking is for genuinely in-flight shipments (departed / in transit).
    // With none in the air we show a single most-recent row — three identical
    // "Arrived" rows would triple the card height and add nothing.
    const active = all.filter(s => shipmentState(s.status).key !== 'arrived');
    // Defensive de-dup: rows with the same status and route carry no extra
    // information once stacked, so keep only the first of each.
    const seen = new Set();
    const unique = active.filter(s => {
      const sig = `${shipmentState(s.status).key}|${airportCode(s.origin)}|${airportCode(s.destination)}`;
      if (seen.has(sig)) return false;
      seen.add(sig);
      return true;
    });
    const chosen = unique.length ? unique.slice(0, HERO_TRACKER_MAX) : all.slice(0, 1);
    return chosen.length ? chosen : [{
      awb: '176-23239882',
      airline: 'Emirates SkyCargo',
      origin: 'Paris (CDG)',
      destination: 'Dar-es-Salaam (DAR)',
      status: 'In Transit',
      eta: '',
      trackingUrl: 'https://eskycargo.emirates.com/app/offerandorder/#/shipments/list?type=D&values=17623239882',
    }];
  }, [shipments]);
  const activeTotal = (shipments || []).filter(s => shipmentState(s.status).key !== 'arrived').length;
  return <TrackerCard list={list} moreCount={Math.max(0, activeTotal - list.length)}/>;
}

// Accepts either a single shipment (`s`) or a list, so the same card serves the
// one-shipment case and the multi-shipment hero stack.
function TrackerCard({ s, list, moreCount = 0 }){
  const rows = list && list.length ? list : (s ? [s] : []);
  return (
    <div className="hero-tracker-bar" style={{
      position:'relative',
      borderRadius:'var(--radius-lg)',
      overflow:'hidden',
      background:'var(--forest-deep)',
      color:'var(--cream)',
      border:'1px solid rgba(255,255,255,0.08)',
      boxShadow:'0 20px 48px -24px rgba(0,0,0,0.45)',
    }}>
      {rows.map((row, i) => (
        <TrackerRow key={row.awb || i} s={row} divider={i > 0}/>
      ))}
      {moreCount > 0 && (
        <a href="#tracker" style={{
          display:'block', padding:'10px 24px', fontSize:11.5, fontWeight:500,
          color:'rgba(244,239,226,0.6)', textDecoration:'none',
          borderTop:'1px solid rgba(255,255,255,0.08)', background:'rgba(255,255,255,0.02)'
        }}>
          +{moreCount} more shipment{moreCount === 1 ? '' : 's'} in the air →
        </a>
      )}
    </div>
  );
}

function TrackerRow({ s, divider }){
  const state = shipmentState(s.status);
  const showEta = state.key !== 'arrived' && !!s.eta;
  const label = state.key === 'arrived' && s.destination
    ? `Arrived — ${s.destination}`
    : state.label;
  // Marker rides the progress line; inset slightly at 100% so the glyph stays
  // fully inside the track instead of being clipped at the destination code.
  const markerLeft = Math.min(state.progress, 96);
  return (
      <a href={s.trackingUrl || '#'}
         target="_blank" rel="noopener noreferrer"
         style={{
           position:'relative', display:'flex', alignItems:'center', gap:18,
           padding:'16px 24px', color:'inherit', textDecoration:'none', cursor:'pointer', flexWrap:'wrap',
           borderTop: divider ? '1px solid rgba(255,255,255,0.08)' : 'none'
         }}
         onMouseEnter={e=>{ const t = e.currentTarget.querySelector('[data-track-cta]'); if(t){ t.style.background='var(--cream)'; t.style.color='var(--forest-deep)'; } }}
         onMouseLeave={e=>{ const t = e.currentTarget.querySelector('[data-track-cta]'); if(t){ t.style.background='rgba(255,255,255,0.1)'; t.style.color='var(--cream)'; } }}
      >
        {/* Status: dot + label (the plane/check glyph rides the route line) */}
        <span style={{display:'inline-flex', alignItems:'center', gap:8, flexShrink:0, color:state.color}}>
          <span className={state.pulse ? 'tracker-dot tracker-dot-pulse' : 'tracker-dot'} style={{background:state.color}}/>
          <span style={{fontSize:12.5, fontWeight:600, letterSpacing:'0.01em', whiteSpace:'nowrap'}}>{label}</span>
        </span>

        {/* Route */}
        <span style={{display:'flex', alignItems:'center', gap:10, fontSize:11.5, fontWeight:600, letterSpacing:'0.06em', color:'rgba(244,239,226,0.6)', flex:'1 1 160px', minWidth:120}}>
          <span style={{whiteSpace:'nowrap'}}>{airportCode(s.origin)}</span>
          <span style={{position:'relative', flex:1, height:2, background:'rgba(255,255,255,0.14)', borderRadius:1, minWidth:40}}>
            <span style={{position:'absolute', top:0, left:0, height:2, width:`${state.progress}%`, background:state.color, borderRadius:1, transition:'width 0.6s ease'}}/>
            <span className="tracker-marker" style={{
              position:'absolute', left:`${markerLeft}%`, top:'50%',
              transform:'translate(-50%,-50%)', color:state.color, display:'flex',
              flex:'none', width:20, height:20, borderRadius:'50%',
              background:'var(--forest-deep)',
              alignItems:'center', justifyContent:'center',
              transition:'left 0.6s ease'
            }}>
              <StatusIcon kind={state.icon} size={16}/>
            </span>
          </span>
          <span style={{whiteSpace:'nowrap'}}>{airportCode(s.destination)}</span>
        </span>

        {showEta && (
          <span style={{fontSize:11.5, color:'rgba(244,239,226,0.55)', flexShrink:0, whiteSpace:'nowrap'}}>
            ETA <span style={{color:'var(--cream)', fontWeight:500}}>{s.eta}</span>
          </span>
        )}

        {s.awb && (
          <span className="mono" style={{fontSize:10.5, letterSpacing:'0.02em', color:'rgba(244,239,226,0.4)', flexShrink:0}}>
            AWB {s.awb}
          </span>
        )}

        <span data-track-cta style={{
          display:'inline-flex', alignItems:'center', gap:6, flexShrink:0,
          padding:'7px 14px', borderRadius:99,
          background:'rgba(255,255,255,0.1)',
          color:'var(--cream)', fontSize:11.5, fontWeight:500,
          letterSpacing:'0.02em', transition:'background 0.15s, color 0.15s',
          whiteSpace:'nowrap', marginLeft:'auto'
        }}>
          Track live <span style={{fontSize:12}}>↗</span>
        </span>
      </a>
  );
}

function TrustStrip(){
  // Each partner: src + alt + a height multiplier so visually-different lockups
  // (tall stacked vs. wide horizontal) optically balance at the same row height.
  const partners = [
    { src:R('cargoEmirates','assets/cargo-emirates.png'),  alt:'Emirates SkyCargo',     scale: 1.45 }, // stacked, needs more height
    { src:R('cargoQatar','assets/cargo-qatar.png'),     alt:'Qatar Airways Cargo',   scale: 1.05 },
    { src:R('cargoTurkish','assets/cargo-turkish.png'),   alt:'Turkish Airlines Cargo',scale: 0.85 },
    { src:R('cargoKlm','assets/cargo-klm.webp'),      alt:'KLM Royal Dutch Airlines', scale: 0.72 },
    { src:R('cargoAirfrance','assets/cargo-airfrance.webp'),alt:'Air France Cargo',      scale: 0.62 }, // very wide
    { src:R('cargoEthiopian','assets/cargo-ethiopian.png'), alt:'Ethiopian Cargo',       scale: 1.05 },
    { src:R('cargoOman','assets/cargo-oman.png'),      alt:'Oman Air Cargo',        scale: 0.80 },
    { src:R('cargoFrenchbee','assets/cargo-frenchbee.svg'), alt:'French Bee Cargo',      scale: 0.85 },
  ];
  const baseH = 44; // px
  return (
    <section style={{padding:'32px 0', background:'var(--paper-2)', borderTop:'1px solid var(--line)', borderBottom:'1px solid var(--line)'}}>
      <div className="container cargo-partners-row" style={{display:'flex', alignItems:'center', gap:48, flexWrap:'wrap'}}>
        <div style={{flexShrink:0}}>
          <div className="eyebrow" style={{marginLeft:0, marginRight:0, fontSize:12, marginTop:4}}>Cargo Partners</div>
        </div>
        <div style={{flex:1, overflow:'hidden', minWidth:280}}>
          <div className="marquee">
            <div className="marquee-track" style={{alignItems:'center', gap:64}}>
              {[...partners, ...partners].map((p,i)=>(
                <img key={i} src={p.src} alt={p.alt}
                  style={{
                    height: Math.round(baseH * p.scale),
                    width:'auto', objectFit:'contain', flexShrink:0,
                    opacity:0.95,
                    transition:'opacity 0.25s ease'
                  }}
                  onMouseEnter={e=>{e.currentTarget.style.opacity=1;}}
                  onMouseLeave={e=>{e.currentTarget.style.opacity=0.95;}}
                />
              ))}
            </div>
          </div>
        </div>
      </div>
      <style>{`
        @media (max-width: 700px) {
          .cargo-partners-row { flex-direction: column !important; gap: 12px !important; align-items: center !important; }
          .cargo-partners-row > div:first-child { align-self: center !important; }
          .cargo-partners-row > div:last-child { width: 100% !important; min-width: 0 !important; }
        }
      `}</style>
    </section>
  );
}

function About(){
  const stats = [
    {n:'80+', l:'Fruit varieties'},
    {n:'50+', l:'Vegetable lines'},
    {n:'12', l:'Source countries'},
    {n:'72h', l:'Doc turnaround'},
  ];
  return (
    <section id="about" className="section-pad">
      <div className="container">
        <div style={{maxWidth:960, margin:'0 auto'}} className="about-grid">
          <div style={{textAlign:'center', marginBottom:40}}>
            <div className="eyebrow">About P&L England</div>
            <h2 className="h-1" style={{margin:'20px auto 0'}}>
              Specialists in moving fresh produce, <em>without the friction.</em>
            </h2>
          </div>
          <div style={{display:'flex', flexDirection:'column', gap:36}}>
            <p style={{fontSize:18, lineHeight:1.6, color:'var(--ink)', margin:0}}>
              P&L England — <strong style={{fontWeight:500}}>Produce &amp; Logistics</strong> — was born from a simple observation: the world's finest fresh produce market was inaccessible to most of the world's buyers.
            </p>
            <div className="about-cols" style={{display:'grid', gridTemplateColumns:'repeat(3, 1fr)', gap:32}}>
              <div>
                <div style={{fontSize:13, fontWeight:600, letterSpacing:'0.06em', textTransform:'uppercase', color:'var(--tomato)', marginBottom:8}}>The market</div>
                <p style={{fontSize:15.5, lineHeight:1.6, color:'var(--ink-2)', margin:0}}>
                  Rungis International Market sits at the gates of Paris — 234 hectares, over 1,200 businesses, the largest fresh produce market in the world.
                </p>
              </div>
              <div>
                <div style={{fontSize:13, fontWeight:600, letterSpacing:'0.06em', textTransform:'uppercase', color:'var(--tomato)', marginBottom:8}}>The problem</div>
                <p style={{fontSize:15.5, lineHeight:1.6, color:'var(--ink-2)', margin:0}}>
                  For international buyers, accessing Rungis directly is genuinely difficult — the language, the French paperwork, relationships that take years to build.
                </p>
              </div>
              <div>
                <div style={{fontSize:13, fontWeight:600, letterSpacing:'0.06em', textTransform:'uppercase', color:'var(--tomato)', marginBottom:8}}>What we do</div>
                <p style={{fontSize:15.5, lineHeight:1.6, color:'var(--ink-2)', margin:0}}>
                  Our team is on the ground at Rungis daily, handling every document and every step of the cold chain to your door.
                </p>
              </div>
            </div>
            <p style={{fontSize:18, lineHeight:1.5, color:'var(--ink)', fontWeight:500, margin:0}}>
              You place the order. We handle the rest.
            </p>
          </div>
        </div>

      </div>
      <style>{`
        @media (max-width: 900px) {
          .stats-grid { grid-template-columns: repeat(2, 1fr) !important; }
          .about-cols { grid-template-columns: 1fr !important; gap: 24px !important; }
        }
      `}</style>
    </section>
  );
}

Object.assign(window, { Hero, TrustStrip, About, ShipmentCounters, TrackerCard, TrackerRow });
