// AI chat assistant with Telegram handoff.
//
// Set CHAT_ENDPOINT to your deployed Cloudflare Worker URL to go live.
// See CHATBOT-SETUP.md. While it is '', the widget uses the preview-only AI
// helper if present, and otherwise falls back to the WhatsApp handoff so the
// published site never shows a dead chat box.
const CHAT_ENDPOINT = 'https://pl-chat.sales-e8f.workers.dev';

// Preview-only helper, present inside the design environment.
const PREVIEW_AI = typeof window !== 'undefined' && !!(window.claude && window.claude.complete);
const CHAT_LIVE = !!CHAT_ENDPOINT || PREVIEW_AI;

const PL_SYSTEM_PROMPT = `You are the assistant on the website of P&L England Ltd (P&L stands for Produce & Logistics), an English-speaking fresh produce exporter sourcing from Rungis International Market, Paris.

Your name is John. You are an AI assistant built for the P&L England website. If someone asks what you are, be upfront: you are an AI assistant, not a member of staff, built specifically to answer questions about P&L England's produce, pricing, documentation and shipping — not a general-purpose chatbot. Never pretend to be human.

Offer to connect people to a real person whenever it would genuinely help: when they want a quote or a price, when the question needs someone who knows their account, when they seem unsure, or when you simply cannot answer. Say it plainly — "I can put you through to someone on the team" — and point them at WhatsApp (+447808034216) or booking a 30-minute call at https://calendly.com/plengland-sales/30min. If a colleague joins the conversation, step back and let them take over.

=== HOW TO ANSWER ===
Use British English. Never use emoji. Keep replies to 2-4 short sentences unless the question genuinely needs a list.

Be straight rather than salesy. A buyer testing us out respects honesty far more than enthusiasm:
- When something is a genuine limitation, say so plainly. Open with "Honest answer:" or "I'll be straight with you" and name the constraint. Never pretend we can do something we can't.
- When a buyer makes a fair criticism, concede it before you respond. "Fair pushback" is a better opening than a defence.
- If you don't have a figure or a fact to hand, say exactly that instead of reaching for a vague claim.
- Never oversell. No marketing language, no superlatives, no "world-class".

Use concrete specifics over generalities. Name the variety, the origin, the calibre, the pack size, the document. "Canadian Sweetheart and Skeena cherries, calibres 28-34, 5kg boxes, July to August" beats "we supply premium cherries".

When a question has several parts, take them in order and answer each one. Say so: "Three points there, I'll take them in turn."

End almost every reply with either a question back to the buyer or a clear next step. Ask what they're sourcing, where they're shipping to, what volumes they need, which market they sell into. The aim is a real conversation, not an FAQ lookup. Where a question needs actual numbers or a commitment, point them at WhatsApp (+447808034216) or booking a 30-minute call at https://calendly.com/plengland-sales/30min.

=== WHAT WE DO ===
We export fresh European fruit and vegetables end to end: sourcing, quality control, cold-chain air freight, and customs documentation. Buyers place an order; we handle the rest. We are English-speaking and on the ground at Rungis daily, with longstanding relationships with growers and producers, not just the market floor.

=== SOURCING ===
Rungis International Market, Paris: 234 hectares, over 1,200 businesses, the largest fresh produce market in the world.
Our France facility: 121 Rue de Chateaurenard, 94572 Rungis Cedex, France. It is FDA-registered (a prerequisite for shipping food into the USA).
London office: 3rd Floor, 86-90 Paul Street, London EC2A 4NE.

=== THE FIVE STEPS ===
1. You place your order (what you need, quantities, destination).
2. We prepare your documentation (phytosanitary certificates, commercial invoice, packing list, certificate of origin) before goods move.
3. We source at Rungis in Paris, handpicked.
4. We pack and ship: ice-packed, held at 2-8C, flown direct from Paris CDG.
5. Delivered to your door with full documentation.

=== PRODUCE ===
Around 206 product lines across 22 categories: berries, cherries, stone fruit, plums, figs, grapes, citrus, pears, apples, exotic, mediterranean/melons, asparagus, mushrooms, tomatoes, vegetables, squash, salad, cabbage, root veg, heritage veg, baby vegetables, herbs.
Notable lines: cherries (French Burlat, Summit, Folfer, Giant Red, Duroni; Belgian Kordia, Rainier; Canadian Sweetheart/Skeena/Lapins, calibres 28-34, 5kg, July-August), British strawberries (Vibrant, Honeoye, Malling Centenary), Gariguette strawberries, endive (red and white), shallots (fresh and dry), Agria potatoes, Charentais melon, chanterelle mushrooms, asparagus (green, white, purple).
Seasons vary by line and origin. If asked whether something is in season, say it depends on the exact variety and origin, and offer to check with the team. Do not guess specific months confidently.

=== PRICING ===
An indicative price list is on the site, updated weekly, shown in EUR, GBP, USD, AED and ZAR, downloadable as separate fruit and vegetable PDFs.
Prices are ex-Rungis per unit and EXCLUDE air freight, documentation and duties.
Rungis prices change every week with supply and demand. We quote an all-in price covering produce and freight, and will break out produce and freight costs separately for full transparency.
NEVER quote a specific price yourself. Always hand pricing requests to the team.

=== ORDERS ===
Minimum order quantity: 500kg. We may accept a smaller first order as a test shipment so a buyer can assess quality.
Mixed shipments: yes, any combination of products, no minimum per item.
Payment: bank transfer only. We can invoice in the currency of the buyer's country.

=== DOCUMENTATION ===
Handled for every shipment: Phytosanitary Certificate; Commercial Invoice with origin and gross/net weight; Air Waybill with temperature instructions; FSVP documentation; FDA Prior Notice filed before every flight; Import Permit where required (for the USA, USDA-APHIS PPQ Form 587); EUR.1 form to reduce or eliminate import duties.
For US shipments we have an appointed customs broker who handles clearance, customs fees and onward delivery.
Where a document must be in the buyer's own name (an import permit, for example) we get on the phone and guide them through it step by step.

=== FREIGHT & ROUTES ===
Air freight from Paris CDG. Cargo partners include Emirates SkyCargo, Qatar Airways, KLM, Air France, Turkish, Ethiopian, Oman Air and French Bee.
Destinations include Dar es Salaam, Johannesburg, Cape Town, Dubai, Riyadh, Doha, Miami, New York, Seychelles and Mauritius. Over 20 destinations across Africa, the Middle East and the USA.
Cold chain maintained at 2-8C. A temperature-recording device can be included for an additional cost.
Customs clearance is usually under 24 hours.

=== DAMAGES ===
Rare, but if damage is caused by us we offer credit against the next order or a full refund. We need clear photographs on arrival with a camera-roll timestamp, plus video. If the airline caused it we still compensate, though there may be a delay while we claim through our cargo partner.

=== CONTACT ===
WhatsApp: +447808034216. Email: sales@plengland.co.uk. Book a 30-minute call: https://calendly.com/plengland-sales/30min
Customer portal: https://books.zohosecure.eu/portal/plengland

=== RULES ===
- Never invent prices, delivery dates, stock availability or certifications we do not hold.
- If you do not know, say so and offer to put them in touch with the team.
- For quotes, order placement or negotiation, hand off to WhatsApp or a booked call.
- Do not discuss competitors or other suppliers.
- If asked something unrelated to fresh produce export, politely redirect.`;

const PL_SUGGESTIONS = [
  'What do you export?',
  'Can you ship to my country?',
  'How does pricing work?',
];

// Only name and email are required — every extra required field costs replies.
const LEAD_FIELDS = [
  { k:'name',     label:'Name',                 required:true,  type:'text',  ph:'Your name' },
  { k:'email',    label:'Email',                required:true,  type:'email', ph:'you@company.com' },
  { k:'company',  label:'Company',              required:false, type:'text',  ph:'Optional' },
  { k:'country',  label:'Country / destination',required:false, type:'text',  ph:'Optional' },
  { k:'whatsapp', label:'WhatsApp',             required:false, type:'tel',   ph:'Optional' },
];

// sessionStorage throws in Safari private browsing and sandboxed iframes, so
// every access goes through these. ?chatreset=1 ignores stored state entirely,
// which makes the greeting and lead card reviewable without clearing storage.
const CHAT_RESET = typeof location !== 'undefined' && /[?&]chatreset=1/.test(location.search);
const ss = {
  get(k){ if (CHAT_RESET) return null; try { return sessionStorage.getItem(k); } catch { return null; } },
  set(k,v){ if (CHAT_RESET) return; try { sessionStorage.setItem(k,v); } catch {} },
};

const convId = (() => {
  let id = ss.get('pl-chat-id');
  if (!id) { id = 'c' + Date.now().toString(36) + Math.random().toString(36).slice(2, 8); ss.set('pl-chat-id', id); }
  return id;
})();

// How far down the page someone must get before the assistant says hello.
// A fixed pixel depth, not a fraction: this page is ~14,000px tall, so even
// 10% meant scrolling well past the hero before the greeting appeared.
// ~300px is roughly "has started reading" at any page length.
const GREET_SCROLL_PX = 300;
const GREET_MIN_DWELL_MS = 2500;
// Details are requested after this many questions, not before the first.
const LEAD_AFTER_TURNS = 2;

function ChatBot(){
  const [open, setOpen] = useState(false);
  const [teaser, setTeaser] = useState(false);
  const [lead, setLead] = useState(() => {
    try { return JSON.parse(ss.get('pl-chat-lead') || 'null'); } catch { return null; }
  });
  const [leadAsked, setLeadAsked] = useState(false);
  const [leadDismissed, setLeadDismissed] = useState(false);
  const [form, setForm] = useState({ name:'', email:'', company:'', country:'', whatsapp:'' });
  const [formErr, setFormErr] = useState('');
  const [msgs, setMsgs] = useState([]);
  const [input, setInput] = useState('');
  const [busy, setBusy] = useState(false);
  const [human, setHuman] = useState(false);
  // The Worker reports UK working hours on every /chat reply, so the promise of
  // a person is only made when someone is actually at their desk.
  const [inHours, setInHours] = useState(false);
  const scrollRef = useRef(null);
  const inputRef = useRef(null);
  const lastPoll = useRef(0);

  // Opening line, written the moment the panel is first opened.
  useEffect(() => {
    if (open && msgs.length === 0) {
      setMsgs([{ role:'assistant', content: lead
        ? `Welcome back ${lead.name.split(' ')[0]} — John here again. What can I help with?`
        : "Hi, I'm John — an AI assistant here to help with any questions about our produce, pricing, documentation or shipping. Ask away, or I can put you straight through to someone on the team." }]);
    }
  }, [open]);

  // Say hello once the visitor has actually engaged: scrolled past the hero
  // AND spent a little time reading. A greeting that fires on load is an
  // interruption; one that waits until they're browsing is an offer of help.
  useEffect(() => {
    if (open) ss.set('pl-chat-greeted', '1');
  }, [open]);

  useEffect(() => {
    if (!CHAT_LIVE) return;
    if (ss.get('pl-chat-greeted')) return;
    const landed = Date.now();
    let done = false;
    const check = () => {
      if (done || open) return;
      const se = document.scrollingElement || document.documentElement;
      const depth = se.scrollTop || window.scrollY || 0;
      if (depth >= GREET_SCROLL_PX && Date.now() - landed >= GREET_MIN_DWELL_MS) {
        done = true;
        setTeaser(true);
        ss.set('pl-chat-greeted', '1');
        window.removeEventListener('scroll', check);
        clearInterval(iv);
      }
    };
    // Scroll handles depth; the interval covers someone who scrolls early
    // then reads for a while without moving.
    window.addEventListener('scroll', check, { passive: true });
    const iv = setInterval(check, 2000);
    return () => { window.removeEventListener('scroll', check); clearInterval(iv); };
  }, [open]);

  // Once they have asked a couple of questions, invite details inline.
  const userTurns = msgs.filter(m => m.role === 'user').length;
  useEffect(() => {
    if (!lead && !leadDismissed && userTurns >= LEAD_AFTER_TURNS && !busy) setLeadAsked(true);
  }, [userTurns, busy, lead, leadDismissed]);

  useEffect(() => {
    const el = scrollRef.current;
    if (el) el.scrollTop = el.scrollHeight;
  }, [msgs, busy]);

  useEffect(() => {
    if (open && lead && inputRef.current) inputRef.current.focus();
  }, [open, lead]);

  // Auto-grow the composer from its value, so it resizes however text arrives.
  useEffect(() => {
    const el = inputRef.current;
    if (!el) return;
    el.style.height = 'auto';
    el.style.height = Math.min(el.scrollHeight, 96) + 'px';
  }, [input, open, lead]);

  // Poll for replies you typed on Telegram. Runs as soon as the visitor has
  // sent anything — NOT only once they've filled in the lead form, since most
  // people ask a question first and would otherwise never see your reply.
  useEffect(() => {
    if (!CHAT_ENDPOINT) return;
    const hasConversation = msgs.some(m => m.role === 'user');
    if (!hasConversation) return;
    const tick = async () => {
      try {
        const r = await fetch(`${CHAT_ENDPOINT}/poll?conversationId=${convId}&since=${lastPoll.current}`);
        const d = await r.json();
        if (d.humanTakeover) setHuman(true);
        if (d.messages && d.messages.length) {
          lastPoll.current = d.messages[d.messages.length - 1].ts;
          setMsgs(m => [...m, ...d.messages.map(x => ({ role:'assistant', content:x.text, fromTeam:true }))]);
        }
      } catch {}
    };
    tick();
    const iv = setInterval(tick, 4000);
    return () => clearInterval(iv);
  }, [msgs.some(m => m.role === 'user')]);

  function submitLead(e){
    e && e.preventDefault();
    const missing = LEAD_FIELDS.filter(f => f.required && !form[f.k].trim());
    if (missing.length) { setFormErr(`Please add your ${missing.map(f=>f.label.toLowerCase()).join(' and ')}.`); return; }
    if (!/^\S+@\S+\.\S+$/.test(form.email.trim())) { setFormErr('That email address doesn\'t look right.'); return; }
    setFormErr('');
    const clean = Object.fromEntries(Object.entries(form).map(([k,v]) => [k, v.trim()]));
    setLead(clean);
    setLeadAsked(false);
    setMsgs(m => [...m, { role:'assistant', content:`Thanks ${clean.name.split(' ')[0]} — the team has your details and will pick this up. Anything else I can help with?` }]);
    ss.set('pl-chat-lead', JSON.stringify(clean));
    if (CHAT_ENDPOINT) {
      fetch(`${CHAT_ENDPOINT}/lead`, {
        method:'POST', headers:{'content-type':'application/json'},
        body: JSON.stringify({ conversationId: convId, lead: clean, page: location.pathname }),
      }).catch(()=>{});
    }
  }

  async function send(text){
    const q = (text || '').trim();
    if (!q || busy) return;
    setInput('');
    const next = [...msgs.filter(m => !m.fromTeam), { role:'user', content:q }];
    setMsgs(m => [...m, { role:'user', content:q }]);
    setBusy(true);
    try {
      let reply;
      if (CHAT_ENDPOINT) {
        const r = await fetch(`${CHAT_ENDPOINT}/chat`, {
          method:'POST', headers:{'content-type':'application/json'},
          body: JSON.stringify({ conversationId: convId, messages: next, lead, page: location.pathname }),
        });
        const d = await r.json();
        if (typeof d.workingHours === 'boolean') setInHours(d.workingHours);
        if (d.humanTakeover) { setHuman(true); setBusy(false); return; }
        if (!r.ok) throw new Error(d.message || 'error');
        reply = d.reply;
      } else {
        reply = await window.claude.complete({
          system: PL_SYSTEM_PROMPT,
          max_tokens: 600,
          messages: next.map(m => ({ role: m.role === 'assistant' ? 'assistant' : 'user', content: m.content })),
        });
      }
      setMsgs(m => [...m, { role:'assistant', content: String(reply || '').trim() || "Sorry — I didn't catch that. Could you rephrase?" }]);
    } catch {
      setMsgs(m => [...m, { role:'assistant', content:"Sorry, I couldn't reach our system just then. You can message the team directly on WhatsApp and they'll come straight back to you." }]);
    } finally {
      setBusy(false);
    }
  }

  const waHref = 'http://wa.me/+447808034216';
  const inputStyle = {width:'100%', fontSize:13, padding:'9px 11px'};

  return (
    <div style={{position:'fixed', bottom:24, right:24, zIndex:100, display:'flex', flexDirection:'column', alignItems:'flex-end', gap:12}}>
      {open && (
        <div className="wa-fab-panel" style={{
          background:'var(--paper)', border:'1px solid var(--line)',
          borderRadius:'var(--radius-lg)', overflow:'hidden',
          width:352, boxShadow:'0 20px 50px -20px rgba(0,0,0,0.25)',
          display:'flex', flexDirection:'column', maxHeight:'min(600px, calc(100vh - 120px))'
        }}>
          <div style={{display:'flex', justifyContent:'space-between', alignItems:'center', padding:'14px 16px', borderBottom:'1px solid var(--line)', background:'var(--paper-2)', flexShrink:0}}>
            <span style={{display:'flex', alignItems:'center', gap:8}}>
              <Logo size={30} iconOnly={true} badge={true}/>
              <span style={{display:'flex', flexDirection:'column', lineHeight:1.2}}>
                <span style={{fontFamily:'var(--sans)', fontSize:14.5, fontWeight:600}}>Ask P&L England</span>
                <span style={{display:'flex', alignItems:'center', gap:6, fontSize:11, color:'var(--ink-3)', whiteSpace:'nowrap'}}>
                  <span style={{width:7, height:7, borderRadius:'50%', background:'var(--leaf, #2F8F3F)', flexShrink:0}}/>
                  {human ? 'Team is replying' : CHAT_LIVE ? 'Replies instantly' : 'Online'}
                </span>
              </span>
            </span>
            <button onClick={()=>setOpen(false)} aria-label="Close chat"
              style={{color:'var(--ink-2)', width:34, height:34, display:'flex', alignItems:'center', justifyContent:'center', margin:'-8px -6px -8px 0'}}>
              <svg width="17" height="17" viewBox="0 0 24 24" fill="none"><path d="M6 9l6 6 6-6" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/></svg>
            </button>
          </div>

          {!CHAT_LIVE ? (
            /* No backend configured: keep the original WhatsApp handoff. */
            <div style={{padding:16, display:'flex', flexDirection:'column', gap:10}}>
              <div style={{background:'var(--paper-2)', borderRadius:'var(--radius)', padding:'10px 14px', fontSize:13.5, lineHeight:1.5, color:'var(--ink)'}}>
                Have a question about P&L England? Message us on WhatsApp and we'll come straight back to you.
              </div>
              <a href={waHref} target="_blank" rel="noopener noreferrer" className="btn btn-whatsapp" style={{width:'100%', justifyContent:'center'}}>
                <Icon.Whatsapp size={16}/> Continue on WhatsApp
              </a>
            </div>
          ) : (
            <>
              <div ref={scrollRef} style={{padding:16, display:'flex', flexDirection:'column', gap:10, overflowY:'auto', flex:'1 1 auto', minHeight:0}}>
                {msgs.map((m,i)=>(
                  <div key={i} style={{alignSelf: m.role === 'user' ? 'flex-end' : 'flex-start', maxWidth: i === 0 ? '100%' : '86%', display:'flex', flexDirection:'column', gap:3}}>
                    {m.fromTeam && <span style={{fontSize:10.5, color:'var(--ink-3)', paddingLeft:4}}>P&L England team</span>}
                    <div style={ i === 0 && m.role === 'assistant'
                      ? {fontSize:14.5, lineHeight:1.55, color:'var(--ink)', whiteSpace:'pre-wrap', padding:'2px 0'}
                      : {
                          background: m.role === 'user' ? 'var(--ink)' : m.fromTeam ? 'var(--forest-deep)' : 'var(--paper-2)',
                          color: m.role === 'user' || m.fromTeam ? 'var(--cream)' : 'var(--ink)',
                          borderRadius:'var(--radius)', padding:'10px 14px',
                          fontSize:13.5, lineHeight:1.5, whiteSpace:'pre-wrap'
                        }
                    }>{m.content}</div>
                  </div>
                ))}
                {busy && (
                  <div style={{alignSelf:'flex-start', background:'var(--paper-2)', borderRadius:'var(--radius)', padding:'12px 14px', display:'flex', gap:5}}>
                    {[0,1,2].map(i=>(<span key={i} className="chat-typing-dot" style={{animationDelay:`${i*0.16}s`}}/>))}
                  </div>
                )}
                {leadAsked && !lead && (
                  <form onSubmit={submitLead} style={{
                    border:'1px solid var(--line)', borderRadius:'var(--radius)',
                    padding:14, display:'flex', flexDirection:'column', gap:8, background:'var(--paper-2)'
                  }}>
                    <div style={{fontSize:13, lineHeight:1.5, color:'var(--ink)'}}>
                      Shall I pass this to the team? Leave your details and they'll follow up.
                    </div>
                    {LEAD_FIELDS.map(fl=>(
                      <label key={fl.k} style={{display:'flex', flexDirection:'column', gap:3}}>
                        <span style={{fontSize:11, color:'var(--ink-3)', fontFamily:'var(--sans)'}}>
                          {fl.label}{fl.required && <span style={{color:'var(--tomato)'}}> *</span>}
                        </span>
                        <input type={fl.type} value={form[fl.k]} placeholder={fl.ph} style={inputStyle}
                          onChange={e=>setForm(s=>({...s, [fl.k]: e.target.value}))}/>
                      </label>
                    ))}
                    {formErr && <div style={{fontSize:11.5, color:'var(--tomato)'}}>{formErr}</div>}
                    <div style={{display:'flex', gap:8, alignItems:'center'}}>
                      <button type="submit" className="btn btn-primary" style={{flex:1, justifyContent:'center', padding:'9px 14px', fontSize:13}}>
                        Send to the team
                      </button>
                      <button type="button" onClick={()=>{ setLeadAsked(false); setLeadDismissed(true); }}
                        style={{
                          fontSize:12, color:'var(--ink-3)', cursor:'pointer',
                          padding:'0 12px', minHeight:44, display:'flex', alignItems:'center', flexShrink:0
                        }}>
                        Not now
                      </button>
                    </div>
                  </form>
                )}
              </div>

              {msgs.length === 1 && !busy && (
                <div style={{display:'flex', flexDirection:'column', alignItems:'flex-start', gap:7, padding:'0 16px 14px', flexShrink:1, minHeight:0, overflowY:'auto'}}>
                  {PL_SUGGESTIONS.map(s=>(
                    <button key={s} onClick={()=>send(s)} style={{
                      fontSize:12.5, padding:'9px 15px', borderRadius:999,
                      border:'1px solid var(--line)', background:'var(--paper)',
                      color:'var(--ink)', cursor:'pointer', fontFamily:'var(--sans)',
                      textAlign:'left', minHeight:38
                    }}>{s}</button>
                  ))}
                </div>
              )}
              <div style={{borderTop:'1px solid var(--line)', padding:12, display:'flex', flexDirection:'column', gap:8, flexShrink:0}}>
                <div style={{display:'flex', gap:8, alignItems:'flex-end'}}>
                  <textarea ref={inputRef} value={input} rows={1}
                    onChange={e=>setInput(e.target.value)}
                    onKeyDown={e=>{ if(e.key==='Enter' && !e.shiftKey){ e.preventDefault(); send(input); } }}
                    placeholder="Ask a question…"
                    style={{flex:1, fontSize:13.5, padding:'10px 12px', resize:'none', maxHeight:96, lineHeight:1.45, overflowY: input.length > 90 ? 'auto' : 'hidden'}}/>
                  <button onClick={()=>send(input)} disabled={!input.trim() || busy} aria-label="Send"
                    style={{
                      width:38, height:38, borderRadius:'50%', flexShrink:0,
                      background: input.trim() && !busy ? 'var(--tomato)' : 'var(--line)',
                      color:'#fff', display:'flex', alignItems:'center', justifyContent:'center',
                      cursor: input.trim() && !busy ? 'pointer' : 'default', transition:'background 0.18s'
                    }}>
                    <Icon.Arrow size={16}/>
                  </button>
                </div>
                {inHours && !human && msgs.some(m => m.role === 'user') && (
                  <div style={{fontSize:11.5, color:'var(--ink-3)', display:'flex', alignItems:'center', gap:6, justifyContent:'center'}}>
                    <span style={{width:6, height:6, borderRadius:'50%', background:'var(--leaf, #2F8F3F)', flexShrink:0}}/>
                    Someone from the team will be with you shortly
                  </div>
                )}
              </div>
            </>
          )}
        </div>
      )}
      <div className="chat-row" style={{display:'flex', flexDirection:'column', alignItems:'flex-end', gap:12}}>
      {teaser && !open && (
        <div className="chat-teaser" style={{position:'relative', flexShrink:1, minWidth:0, marginTop:8, marginRight:2}}>
          <div style={{
            maxWidth:250, background:'var(--paper)', border:'1px solid var(--line)',
            borderRadius:'var(--radius)', borderBottomRightRadius:5,
            padding:'11px 14px', boxShadow:'0 12px 30px -12px rgba(0,0,0,0.26)',
            cursor:'pointer', fontSize:13, lineHeight:1.4, color:'var(--ink)'
          }} onClick={()=>{ setTeaser(false); setOpen(true); }}>
            Hi! Ask me anything about P&L England.
          </div>
          {/* 44px hit area with a small visual circle inside, overhanging the
              bubble's top-right corner so the bubble itself stays flush right. */}
          <button onClick={e=>{ e.stopPropagation(); setTeaser(false); }} aria-label="Dismiss"
            style={{
              position:'absolute', top:-14, right:-14,
              width:44, height:44, minHeight:44, padding:0, background:'none', border:'none',
              display:'flex', alignItems:'center', justifyContent:'center', cursor:'pointer'
            }}>
            <span style={{
              width:22, height:22, borderRadius:'50%',
              background:'var(--paper)', border:'1px solid var(--line)',
              color:'var(--ink-2)', display:'flex', alignItems:'center', justifyContent:'center',
              boxShadow:'0 2px 6px rgba(0,0,0,0.1)'
            }}>
              <Icon.X size={11}/>
            </span>
          </button>
        </div>
      )}
      <button className="wa-fab" onClick={()=>{ setTeaser(false); setOpen(!open); }} aria-label="Open chat" style={{
        width:56, height:56, flexShrink:0, borderRadius:'50%', background:'var(--ink)', color:'var(--cream)',
        display:'flex', alignItems:'center', justifyContent:'center',
        boxShadow:'0 12px 32px -8px rgba(0,0,0,0.4)', cursor:'pointer', position:'relative'
      }}>
        {open ? <Icon.X size={22}/> : <Icon.Chat size={24}/>}
        {teaser && !open && <span className="chat-fab-ping"/>}
      </button>
      </div>
      <style>{`
        .chat-typing-dot { width:6px; height:6px; border-radius:50%; background:var(--ink-3); display:block; animation:chatTyping 1.1s ease-in-out infinite; }
        .chat-teaser { animation: teaserIn 0.45s cubic-bezier(0.16,1,0.3,1) both; }
        @keyframes teaserIn { from{opacity:0; transform:translateY(10px) scale(0.97)} to{opacity:1; transform:none} }
        .chat-fab-ping { position:absolute; top:2px; right:2px; width:11px; height:11px; border-radius:50%; background:var(--tomato); border:2px solid var(--ink); }
        @media (prefers-reduced-motion: reduce){ .chat-teaser{animation:none} }
        @keyframes chatTyping { 0%,60%,100%{opacity:0.25;transform:translateY(0)} 30%{opacity:1;transform:translateY(-3px)} }
        @media (max-width: 520px){ .wa-fab-panel { width: calc(100vw - 32px) !important; } }
      `}</style>
    </div>
  );
}

Object.assign(window, { ChatBot });
