var {useState,useEffect,useRef,useCallback} = React;

/* ============================================================
   ASK WITH AI: LaLaLand travel assistant
   Floating launcher + full-screen conversational interface.
   Powered by window.claude.complete (travel-only persona).
   ============================================================ */

/* ---- the site's real journeys, for in-chat recommendations ---- */
const AI_PACKAGES = [
  {n:"Spiritual Journeys", dur:"6 days", places:"Varanasi · Prayagraj · Ayodhya", src:"varanasi", tone:"t-himalaya",
   re:/spiritual|varanasi|kashi|prayagraj|ayodhya|ganga|aarti|pilgrim|temple|jyotirling/i},
  {n:"God's Own Country", dur:"7 days", places:"Kerala", src:"houseboat", tone:"t-ocean",
   re:/kerala|backwater|munnar|alleppey|alappuzha|houseboat|kochi|kovalam|tea (estate|hill)/i},
  {n:"Royal Rajasthan Tour", dur:"9 days", places:"Udaipur · Jaipur · Jodhpur · Jaisalmer", src:"rajasthan", tone:"t-desert",
   re:/rajasthan|udaipur|jaipur|jodhpur|jaisalmer|pink city|blue city|fort|palace|thar|camel/i},
  {n:"Gujarat Heritage & Spiritual", dur:"7 days", places:"Somnath · Dwarka · Bhuj · Kutch", src:"whiteRann", tone:"t-desert",
   re:/gujarat|somnath|dwarka|kutch|bhuj|rann|white desert/i},
  {n:"International Getaways", dur:"6 days", places:"Singapore & Kuala Lumpur", src:"singapore", tone:"t-ocean",
   re:/singapore|kuala lumpur|malaysia|sentosa|marina bay|southeast asia/i},
  {n:"Dubai & UAE Experiences", dur:"6 days", places:"Dubai · Abu Dhabi", src:"dubai", tone:"t-sunset",
   re:/dubai|abu dhabi|\buae\b|emirates|burj|desert safari/i},
];
const matchPackages = (text) => {
  const seen=new Set(), out=[];
  AI_PACKAGES.forEach(p=>{ if(p.re.test(text) && !seen.has(p.n)){ seen.add(p.n); out.push(p); } });
  return out.slice(0,2);
};

/* ---- welcome-screen content ---- */
const POPULAR = [
  {name:"Kerala", key:"kerala", blurb:"Backwaters & misty tea hills", q:"Plan a relaxed 6-day Kerala trip for a couple"},
  {name:"Rajasthan", key:"rajasthan", blurb:"Forts, palaces & golden desert", q:"Build a 9-day Royal Rajasthan itinerary"},
  {name:"Ayodhya & Kashi", key:"varanasi", blurb:"Spiritual North India", q:"Plan a spiritual North India tour with Ayodhya & Varanasi"},
  {name:"Dubai", key:"dubai", blurb:"Skylines & desert luxury", q:"What does a 6-day Dubai & Abu Dhabi trip look like?"},
  {name:"Singapore", key:"singapore", blurb:"City lights & theme parks", q:"Family-friendly 6-day Singapore plan"},
  {name:"Goa", key:"beachAerial", blurb:"Beaches & Portuguese charm", q:"Suggest a 4-day Goa beach getaway"},
];
const SUGGESTIONS = [
  "Plan a 7-day Rajasthan itinerary for 2",
  "Best time to visit Leh–Ladakh?",
  "Honeymoon ideas in the Andamans",
  "Do I need a visa for Dubai?",
  "Family trip under ₹60,000",
  "Most beautiful places in Kerala",
];
const QUICK_ACTIONS = [
  {icon:"route",   label:"Build an itinerary", q:"Help me build a custom travel itinerary: ask me what you need to know."},
  {icon:"compass", label:"Where should I go?",  q:"Suggest a destination for me based on a few quick questions."},
  {icon:"shield",  label:"Visa & travel tips",  q:"What visa and travel tips should I know for an international trip?"},
  {icon:"sparkle", label:"Surprise me",         q:"Surprise me with an unusual hidden-gem destination and why I'd love it."},
];

/* ---- the persona / guardrails sent on every call ---- */
const SYSTEM = `You are "LaLaLand AI", the friendly expert travel assistant for LaLaLand Holidays: a boutique travel company based in Bengaluru, India, specialising in the hidden gems of India and curated international trips.

#1 MOST IMPORTANT RULE: REACH A CONCLUSION FAST: Ask AT MOST ONE round of clarifying questions. The moment the traveller answers that first round (or if their very first message already gives you enough to work with), your NEXT reply MUST be a COMPLETE conclusion: a full itinerary or concrete recommendation. After that first round you must NOT ask any further questions and must NOT show any more option blocks; make reasonable assumptions for anything still unknown and state them briefly. Never drag the conversation past two assistant turns before delivering the plan.

#2 RULE: TAPPABLE OPTIONS (first round only): The ONE clarifying round you are allowed must be presented as tappable options, never as prose or a numbered list. Write at most ONE short friendly intro line, then the block, in EXACTLY this format with nothing after it:
[[OPTIONS]]
Trip length :: 3 days | 5 days | 7 days | 10+ days
Budget :: Luxury | Mid-range | Budget
Travellers :: Solo | Couple | Family | Group
Travel style :: Heritage | Adventure | Spiritual | Food | Mix
[[/OPTIONS]]
Rules: one question per line as "Label :: opt | opt | opt"; 2–6 options each; option labels under ~22 characters; put EVERYTHING you need to know into this single block so you never need to ask again. The traveller taps to answer, so do not also restate the questions in sentences.

CONCLUSION FORMAT, when you deliver the plan, give a complete, self-contained itinerary the traveller could act on: a one-line summary (destination, days, travellers, style), then each day as "## Day 1: Title" with 2–4 bullets, then a short note on stays/budget. End with a single inviting line (no options block).

YOUR SCOPE: you ONLY discuss travel: itineraries, holiday packages, flights, hotels & stays, visa guidance, destination suggestions, budgets, travel tips, transport, local attractions & activities, travel insurance, group tours and custom trips.

HARD RULE: if a user asks about anything NOT related to travel (technology, politics, health, coding, news, maths, relationships, etc.), do NOT answer it. Reply with EXACTLY this sentence and nothing else:
"I'm your travel assistant and can only help with travel-related queries. Please ask me about destinations, itineraries, hotels, flights, visas, or travel planning."

STYLE: warm, concise, and genuinely useful. Use markdown: short paragraphs, **bold** for key terms, and bullet lists. Keep replies focused; don't pad.

PERSONALISATION: factor in destination, duration, budget, number of travellers and travel style. Collect anything you need in the single allowed options round; after that, assume sensible defaults rather than asking again.

LALALAND JOURNEYS you can recommend by name when relevant:
1. Spiritual Journeys: 6 days: Varanasi · Prayagraj · Ayodhya
2. God's Own Country: 7 days: Kerala
3. Royal Rajasthan Tour: 9 days: Udaipur · Jaipur · Jodhpur · Jaisalmer
4. Gujarat Heritage & Spiritual Circuit: 7 days: Somnath · Dwarka · Bhuj · Kutch
5. International Getaways: 6 days: Singapore & Kuala Lumpur
6. Dubai & UAE Experiences: 6 days: Dubai · Abu Dhabi
When a relevant journey exists, mention it naturally and suggest the traveller can book it with LaLaLand. Prices are shared on request via the team. Never invent flight prices or exact fares: give ranges and say the team confirms live rates.`;
const PRIMED_ACK = "Understood. I'm LaLaLand AI: I only help with travel, and I'll use the exact refusal line for anything off-topic. Ready to help plan a trip.";
const GREETING = "Namaste! I'm **LaLaLand AI** ✦ your personal travel planner. Tell me where you'd like to go, or what kind of trip you're dreaming of, and I'll craft an itinerary, suggest stays, sort visas and more.";

/* ============================================================
   STANDARD REPLIES (FAQ)
   ------------------------------------------------------------
   Instant canned answers for common general questions. These are
   answered locally and DO NOT count toward the enquiry limit, so
   visitors can always get the basics. Each entry: a test (regex or
   fn) + the reply. First match wins; order from specific → general.
   ============================================================ */
var WA_DISPLAY = "+91 77603 67456";
const FAQS = [
  { id:"greeting",
    test:/^(hi+|hey+|hello+|namaste|yo|hola|good (morning|afternoon|evening)|greetings)\b[\s!.]*$/i,
    a:"Namaste! 👋 Welcome to **LaLaLand Holidays**. I can help with destinations, itineraries, the best time to travel, visas, our packages and more. Where would you like to go, or what would you like to know?" },
  { id:"thanks",
    test:/\b(thanks|thank you|thankyou|thx|appreciate it|great help|awesome|perfect)\b/i,
    a:"You're most welcome! 🙏 If you'd like, tell me a destination or trip idea and I'll help you plan it — or reach our team anytime on WhatsApp at **"+WA_DISPLAY+"**." },
  { id:"who",
    test:/\b(who are you|what (is|are) (you|lalaland)|about (lalaland|you)|tell me about (lalaland|your company)|what do you do)\b/i,
    a:"**LaLaLand Holidays** is a customized travel & destination-management company. We craft private, personalised journeys across India and worldwide — bespoke itineraries, corporate retreats, destination weddings and yoga & wellness escapes. Our motto: *Our places. Your stories.*" },
  { id:"services",
    test:/\b(services|what do you offer|offerings|packages|what kind of trips|types? of (trip|tour)|specialit|specialty|do you do)\b/i,
    a:"We specialise in:\n\n• **Custom itineraries** across India & abroad\n• **Corporate retreats & events** (Goa and beyond)\n• **Destination weddings** (Udaipur palaces & more)\n• **Yoga & Ayurveda** wellness journeys (Kerala)\n• **International getaways** — Dubai, Singapore, Thailand, Vietnam & more\n\nTell me which interests you and I'll share ideas." },
  { id:"contact",
    test:/\b(contact|phone|whats ?app|whatsapp|number|call you|reach you|get in touch|email|talk to (someone|a human|your team)|customer (care|support)|book(ing)? (a call|now)|enquir)\b/i,
    a:"You can reach the LaLaLand team directly on **WhatsApp: "+WA_DISPLAY+"** — tap the button below to start a chat. You can also use the **Contact** page on our site and we'll get right back to you.",
    wa:"Hi LaLaLand! I'd like to get in touch about planning a trip." },
  { id:"location",
    test:/\b(where are you (based|located)|your (office|location|address)|which city are you|head ?office)\b/i,
    a:"We're an India-based travel company planning journeys across the country and around the world. The easiest way to reach us is **WhatsApp: "+WA_DISPLAY+"** — we'll help you plan from wherever you are.",
    wa:"Hi LaLaLand! I'd like to know more about your trips." },
  { id:"payment",
    test:/\b(payment|pay|cost|pricing|price|how much|charges|fees?|budget|deposit|refund|cancellation)\b/i,
    a:"Every journey is custom, so pricing depends on your destination, dates, length and style. Share those details and I'll give you a ballpark range — for an exact quote, our team will confirm on **WhatsApp: "+WA_DISPLAY+"**.",
    wa:"Hi LaLaLand! I'd like a quote for a trip." },
  { id:"booking",
    test:/\b(how (do i|to) book|booking process|how does (it|booking) work|how to plan|next steps?|get started)\b/i,
    a:"It's simple:\n\n1. Tell me your destination, dates, travellers & style\n2. I'll suggest an itinerary and ideas\n3. Our team finalises stays, transport & bookings with you on **WhatsApp: "+WA_DISPLAY+"**\n\nWant to start now? Just tell me where you'd like to go." },
  { id:"besttime",
    test:/\b(best time to (visit|travel|go to) india|when (to|should i) visit india|season|weather in india)\b/i,
    a:"For most of India, **October to March** brings the most pleasant weather. The Himalayas (Leh, Spiti) are best **May–September**, and Kerala is lovely **September–March**. Tell me your destination and I'll pin down the ideal window." },
];
function matchFAQ(text){
  const t=(text||"").trim();
  if(!t || t.length>160) return null;        // long messages are real planning enquiries
  for(const f of FAQS){
    const hit = typeof f.test==="function" ? f.test(t) : f.test.test(t);
    if(hit) return f;
  }
  return null;
}

/* ---- tiny markdown → html (chat-safe) ---- */
function aiEscape(s){ return s.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;"); }
function aiInline(s){
  return s
    .replace(/\*\*(.+?)\*\*/g,"<strong>$1</strong>")
    .replace(/\*(?!\s)(.+?)(?!\s)\*/g,"<em>$1</em>")
    .replace(/`(.+?)`/g,"<code>$1</code>")
    .replace(/\[(.+?)\]\((https?:[^)]+)\)/g,'<a href="$2" target="_blank" rel="noopener">$1</a>');
}
function renderMarkdown(md){
  const lines = aiEscape(md||"").split(/\n/);
  let html="", list=null;
  const close=()=>{ if(list){ html+="</"+list+">"; list=null; } };
  for(let raw of lines){
    const line=raw.replace(/\s+$/,"");
    if(!line.trim()){ close(); continue; }
    let m;
    if((m=line.match(/^###\s+(.*)/))){ close(); html+="<h4>"+aiInline(m[1])+"</h4>"; continue; }
    if((m=line.match(/^##\s+(.*)/))){ close(); const t=aiInline(m[1]); const day=/^day\s*\d+/i.test(m[1]);
      html+='<h3 class="'+(day?"ai-day":"")+'">'+t+"</h3>"; continue; }
    if((m=line.match(/^#\s+(.*)/))){ close(); html+="<h2>"+aiInline(m[1])+"</h2>"; continue; }
    if((m=line.match(/^>\s+(.*)/))){ close(); html+="<blockquote>"+aiInline(m[1])+"</blockquote>"; continue; }
    if((m=line.match(/^\s*[-*•]\s+(.*)/))){ if(list!=="ul"){ close(); html+="<ul>"; list="ul"; } html+="<li>"+aiInline(m[1])+"</li>"; continue; }
    if((m=line.match(/^\s*\d+[.)]\s+(.*)/))){ if(list!=="ol"){ close(); html+="<ol>"; list="ol"; } html+="<li>"+aiInline(m[1])+"</li>"; continue; }
    close(); html+="<p>"+aiInline(line)+"</p>";
  }
  close();
  return html;
}

/* ---- package recommendation card ---- */
function PkgCard({p}){
  const img = window.IMG[p.src] || "";
  return (
    <button className="ai-pkg" onClick={()=>window.openWA('Hi LaLaLand! I read about the "'+p.n+'" ('+p.dur+') journey via your AI assistant: I\'d love to plan it.')}>
      <span className="ai-pkg-thumb">
        <span className={"ph-grad "+p.tone}></span>
        {img && <img src={img} alt={p.n} loading="lazy"/>}
      </span>
      <span className="ai-pkg-meta">
        <span className="ai-pkg-name">{p.n}</span>
        <span className="ai-pkg-places">{p.places}</span>
        <span className="ai-pkg-foot"><span className="ai-pkg-dur">{p.dur}</span><span className="ai-pkg-go">Plan this <window.Icon name="arrowUR" size={13}/></span></span>
      </span>
    </button>
  );
}

/* ---- parse the model's [[OPTIONS]] block into selectable groups ---- */
function parseOptions(content){
  const m=(content||"").match(/\[\[OPTIONS\]\]([\s\S]*?)\[\[\/OPTIONS\]\]/i);
  if(!m) return {text:content||"", groups:[]};
  const before=content.slice(0,m.index).trim();
  const after=content.slice(m.index+m[0].length).trim();
  const text=(before+(after?"\n\n"+after:"")).trim();
  const groups=[];
  m[1].split(/\n/).forEach(line=>{
    line=line.trim(); if(!line) return;
    let label="", rest=line;
    const sep=line.split(/\s*::\s*/);
    if(sep.length>=2){ label=sep[0].replace(/[:?]+$/,"").trim(); rest=sep.slice(1).join("::"); }
    const opts=rest.split(/\s*\|\s*/).map(s=>s.trim()).filter(Boolean);
    if(opts.length) groups.push({label,opts});
  });
  return {text, groups};
}

/* ---- strip markdown to plain text (for WhatsApp prefill) ---- */
function stripMarkdown(s){
  return (s||"")
    .replace(/\[\[OPTIONS\]\][\s\S]*?\[\[\/OPTIONS\]\]/gi,"")
    .replace(/^#{1,6}\s+/gm,"")
    .replace(/\*\*(.+?)\*\*/g,"$1")
    .replace(/\*(.+?)\*/g,"$1")
    .replace(/`(.+?)`/g,"$1")
    .replace(/^\s*[-*•]\s+/gm,"• ")
    .replace(/\n{3,}/g,"\n\n")
    .trim();
}
/* ---- build a brief WhatsApp enquiry from a reply + the question ---- */
function buildEnquiry(botText, userText){
  let plain=stripMarkdown(botText).replace(/[ \t]+/g," ").trim();
  if(plain.length>1600) plain=plain.slice(0,1600).replace(/\s+\S*$/,"")+"…";
  let msg="Hi LaLaLand! 👋 I was planning a trip with your AI assistant";
  const q=(userText||"").replace(/\s+/g," ").trim();
  if(q) msg+=' (I asked: "'+(q.length>120?q.slice(0,120)+"…":q)+'")';
  msg+=".\n\nHere's what it suggested:\n"+plain+"\n\nI'd love to take this forward: can your team help me plan it?";
  return msg;
}
/* ---- WhatsApp message once the in-chat enquiry limit is reached ---- */
function buildLimitEnquiry(questions){
  let msg="Hi LaLaLand! 👋 I was planning a trip with your AI assistant and would like to continue with your team.";
  const qs=(questions||[]).map(q=>(q||"").replace(/\s+/g," ").trim()).filter(Boolean);
  if(qs.length){
    msg+="\n\nWhat I asked about:\n"+qs.map((q,i)=>(i+1)+". "+(q.length>140?q.slice(0,140)+"…":q)).join("\n");
  }
  msg+="\n\nCould you help me take this forward?";
  return msg;
}

/* ---- safety net: if the model asks something but forgot the [[OPTIONS]]
   block, synthesise tappable chips from the question text so EVERY question
   the assistant asks is answerable with a tap ---- */
function synthesizeOptions(text){
  if(!/\?/.test(text||"")) return [];
  const t=(text||"").toLowerCase();
  const groups=[];
  const add=(label,opts)=>groups.push({label,opts});
  if(/how many days|how long|duration|number of days|days do you|how many nights/.test(t)) add("Trip length",["3 days","5 days","7 days","10+ days"]);
  if(/budget|spend|price range|per (head|person|night)|cost|afford/.test(t)) add("Budget",["Luxury","Mid-range","Budget"]);
  if(/how many (people|travel|guests|of you|adults)|number of travel|group size|\bfamily\b|kids|children|travellers|travelers|solo|couple/.test(t)) add("Travellers",["Solo","Couple","Family","Group"]);
  if(/when |which month|what time of year|\bdates\b|season|planning to travel|travel date|time of year/.test(t)) add("When",["This month","Next 1–3 months","Later this year","Flexible"]);
  if(/travel style|interested in|prefer|theme|kind of (trip|experience)|\bpace\b|\bvibe\b|type of trip/.test(t)) add("Style",["Heritage","Adventure","Spiritual","Food","Mix"]);
  if(/\bfly|flight|\bdrive|overland|\btrain|how.*get there|depart|arrive from/.test(t)) add("Getting there",["Flight","Train","Road trip","Not sure"]);
  if(!groups.length) add("","You decide for me · suggest the best plan");
  return groups.slice(0,5);
}

/* ---- tappable quick-reply options under an assistant message ---- */
function OptionsBlock({groups,active,onSubmit}){
  const single = groups.length===1;
  const [sel,setSel]=useState({});
  const pick=(gi,opt)=>{
    if(!active) return;
    if(single){ onSubmit && onSubmit(opt); return; }
    setSel(s=>({...s,[gi]: s[gi]===opt?undefined:opt}));
  };
  const chosenCount=groups.filter((g,gi)=>sel[gi]!=null).length;
  const submit=()=>{
    if(!chosenCount) return;
    const parts=groups.map((g,gi)=> sel[gi]!=null ? (g.label? g.label+": "+sel[gi] : sel[gi]) : null).filter(Boolean);
    onSubmit && onSubmit(parts.join("\n"));
  };
  return (
    <div className="ai-opts">
      {groups.map((g,gi)=>(
        <div className="ai-opt-group" key={gi}>
          {g.label && <span className="ai-opt-q">{g.label}</span>}
          <div className="ai-opt-chips">
            {g.opts.map(o=>(
              <button key={o} className={"ai-opt"+(sel[gi]===o?" sel":"")} disabled={!active}
                onClick={()=>pick(gi,o)}>{o}</button>
            ))}
          </div>
        </div>
      ))}
      {!single && active && (
        <button className="ai-opt-send" disabled={!chosenCount} onClick={submit}>
          {chosenCount?("Continue ("+chosenCount+")"):"Pick to continue"} <window.Icon name="arrow" size={15}/>
        </button>
      )}
    </div>
  );
}

/* ---- a single chat row ---- */
function Bubble({m,last,busy,onAnswer,prevUser,conclude}){
  if(m.role==="user") return <div className="ai-row user"><div className="ai-bubble user">{m.content}</div></div>;
  // enquiry-limit hand-off message
  if(m.limit){
    return (
      <div className="ai-row bot">
        <div className="ai-avatar"><window.Icon name="sparkle" size={16}/></div>
        <div className="ai-bot-col">
          <div className="ai-bubble bot">{m.content}</div>
          <button className="ai-enquire limit" onClick={()=>window.openWA(m.waMsg)}>
            <window.Icon name="wa" size={16}/> Continue on WhatsApp
          </button>
        </div>
      </div>
    );
  }
  const parsed=parseOptions(m.content);
  const text=parsed.text;
  // FAQ / standard reply — markdown answer, optional WhatsApp button, no chips
  if(m.faq){
    return (
      <div className="ai-row bot">
        <div className="ai-avatar"><window.Icon name="sparkle" size={16}/></div>
        <div className="ai-bot-col">
          <div className="ai-bubble bot" dangerouslySetInnerHTML={{__html:renderMarkdown(m.content)}}/>
          {m.waMsg && (
            <button className="ai-enquire" onClick={()=>window.openWA(m.waMsg)}>
              <window.Icon name="wa" size={16}/> Chat on WhatsApp
            </button>
          )}
        </div>
      </div>
    );
  }
  // after the first round of options has been answered we stop asking: the
  // reply is treated as the final plan: no more chips, always offer enquire.
  let groups = conclude ? [] : parsed.groups;
  if(!conclude && groups.length===0 && !!last && !busy && !m.answered) groups=synthesizeOptions(text);
  const active = !!last && !busy && !m.answered && groups.length>0;
  const plain = stripMarkdown(m.content);
  const isRefusal = /only help with travel-related queries/i.test(plain);
  const showEnquire = !m.error && !isRefusal && (conclude ? plain.length>24 : (plain.length>24 && groups.length===0));
  return (
    <div className="ai-row bot">
      <div className="ai-avatar"><window.Icon name="sparkle" size={16}/></div>
      <div className="ai-bot-col">
        <div className={"ai-bubble bot"+(m.error?" err":"")} dangerouslySetInnerHTML={{__html:renderMarkdown(text)}}/>
        {groups.length>0 && <OptionsBlock groups={groups} active={active} onSubmit={onAnswer}/>}
        {m.pkgs && m.pkgs.length>0 && (
          <div className="ai-pkg-row">
            <span className="ai-pkg-lead"><window.Icon name="pin" size={13}/> Curated LaLaLand journeys</span>
            <div className="ai-pkg-grid">{m.pkgs.map(p=><PkgCard key={p.n} p={p}/>)}</div>
          </div>
        )}
        {m.error && m.waMsg && (
          <button className="ai-enquire" onClick={()=>window.openWA(m.waMsg)}>
            <window.Icon name="wa" size={16}/> Continue on WhatsApp
          </button>
        )}
        {showEnquire && (
          <button className="ai-enquire" onClick={()=>window.openWA(buildEnquiry(m.content, prevUser))}>
            <window.Icon name="wa" size={16}/> Enquire about this on WhatsApp
          </button>
        )}
      </div>
    </div>
  );
}

/* ---- cartoon globe + orbiting plane loader (matches site loader) ---- */
window.GlobeLoader = function({size=58}){
  return (
    <span className="ai-tl" style={{width:size,height:size}}>
      <span className="ai-tl-ring"></span>
      <span className="ai-tl-pin">
        <svg viewBox="0 0 24 24"><path d="M12 2C8.1 2 5 5.1 5 9c0 4.7 6 11.5 6.5 12.1.27.3.73.3 1 0C13 20.5 19 13.7 19 9c0-3.9-3.1-7-7-7z" fill="#3f63b4"/><circle cx="12" cy="9" r="2.7" fill="#fbf9f4"/></svg>
      </span>
      <span className="ai-tl-orbit">
        <span className="ai-tl-plane">
          <svg viewBox="0 0 24 24"><path d="M22 16v-2l-8.5-5V3.5C13.5 2.67 12.83 2 12 2s-1.5.67-1.5 1.5V9L2 14v2l8.5-2.5V19L8 20.5V22l4-1.2 4 1.2v-1.5L13.5 19v-5.5L22 16z" fill="#23429a"/></svg>
        </span>
      </span>
    </span>
  );
};

function Typing(){
  const msgs=["Charting your route…","Scouting the best stays…","Pinning hidden gems…","Checking the best season…"];
  const [i,setI]=useState(0);
  useEffect(()=>{ const t=setInterval(()=>setI(p=>(p+1)%msgs.length),1700); return ()=>clearInterval(t); },[]);
  return (
    <div className="ai-row bot">
      <div className="ai-avatar"><window.Icon name="sparkle" size={16}/></div>
      <div className="ai-think">
        <window.GlobeLoader size={58}/>
        <span className="ai-think-txt" key={i}>{msgs[i]}</span>
      </div>
    </div>
  );
}

/* ---- welcome / empty state ---- */
function Welcome({onPick}){
  return (
    <div className="ai-welcome">
      <div className="ai-hero">
        <span className="ai-badge"><window.Icon name="sparkle" size={14}/> Ask with AI</span>
        <h2 className="ai-hero-title">Where to next?</h2>
        <p className="ai-hero-sub" dangerouslySetInnerHTML={{__html:renderMarkdown(GREETING)}}/>
      </div>

      <div className="ai-quick">
        {QUICK_ACTIONS.map(a=>(
          <button key={a.label} className="ai-quick-btn" onClick={()=>onPick(a.q)}>
            <window.Icon name={a.icon} size={18}/><span>{a.label}</span>
          </button>
        ))}
      </div>

      <div className="ai-pop-head"><span className="eyebrow">Popular right now</span></div>
      <div className="ai-pop-grid">
        {POPULAR.map(d=>{
          const img=window.IMG[d.key]||"";
          return (
            <button key={d.name} className="ai-pop" onClick={()=>onPick(d.q)}>
              <span className="ai-pop-img">{img && <img src={img} alt={d.name} loading="lazy"/>}<span className="ai-pop-shade"></span></span>
              <span className="ai-pop-text"><span className="ai-pop-name">{d.name}</span><span className="ai-pop-blurb">{d.blurb}</span></span>
            </button>
          );
        })}
      </div>

      <div className="ai-sugg">
        {SUGGESTIONS.map(s=>(
          <button key={s} className="ai-chip" onClick={()=>onPick(s)}>{s}</button>
        ))}
      </div>
    </div>
  );
}

/* ============================================================
   AI PROVIDER LAYER
   Built-in model (default) · OpenAI (ChatGPT) · Google Gemini.
   The API key is stored ONLY in this browser (localStorage) and
   calls go directly from this page to the chosen provider. If a
   provider call fails (bad key / CORS / rate-limit) we transparently
   fall back to the built-in model so the assistant never dies.
   ============================================================ */
const AI_CFG_KEY="lalaland_ai_provider_v1";
const DEFAULT_MODEL={ openai:"gpt-4o-mini", gemini:"gemini-2.5-flash" };
const PROVIDER_LABEL={ builtin:"Built-in AI", openai:"ChatGPT", gemini:"Gemini" };
function getAICfg(){
  try{ const c=JSON.parse(localStorage.getItem(AI_CFG_KEY)); if(c&&c.provider) return {model:"",key:"",...c}; }catch(_){}
  // default: Gemini using the site's Google key, but ONLY if it's a valid
  // Google key (AIza…). Otherwise use built-in / graceful fallback.
  if(window.GOOGLE_API_KEY && window.GOOGLE_KEY_OK) return {provider:"gemini", key:window.GOOGLE_API_KEY, model:""};
  return {provider:"builtin", key:"", model:""};
}
function setAICfgLS(c){ try{ localStorage.setItem(AI_CFG_KEY, JSON.stringify(c)); }catch(_){} }

async function callOpenAI(apiMessages, key, model){
  const r=await fetch("https://api.openai.com/v1/chat/completions",{
    method:"POST",
    headers:{ "Content-Type":"application/json", "Authorization":"Bearer "+key },
    body:JSON.stringify({ model:model||DEFAULT_MODEL.openai, messages:apiMessages, temperature:0.7 })
  });
  if(!r.ok) throw new Error("OpenAI "+r.status);
  const j=await r.json();
  return (j.choices && j.choices[0] && j.choices[0].message && j.choices[0].message.content) || "";
}
async function callGemini(apiMessages, key, model){
  // apiMessages[0] is our SYSTEM (sent as user), [1] the priming ack: fold the
  // system text into systemInstruction and send the real turns as contents.
  const convo=apiMessages.slice(2);
  const contents=convo.map(m=>({ role:m.role==="assistant"?"model":"user", parts:[{text:m.content}] }));
  const body={ contents, systemInstruction:{ parts:[{text:apiMessages[0].content}] } };
  const url="https://generativelanguage.googleapis.com/v1beta/models/"+(model||DEFAULT_MODEL.gemini)+":generateContent";
  // New AI Studio keys (AQ.… prefix) must be sent as the x-goog-api-key header,
  // NOT as a ?key= URL param (the legacy method 401s for AQ. keys).
  const r=await fetch(url,{ method:"POST",
    headers:{"Content-Type":"application/json","x-goog-api-key":key},
    body:JSON.stringify(body) });
  if(!r.ok) throw new Error("Gemini "+r.status);
  const j=await r.json();
  const c=j.candidates && j.candidates[0];
  return (c && c.content && c.content.parts && c.content.parts.map(p=>p.text).join("")) || "";
}
async function askAI(apiMessages){
  const cfg=getAICfg();
  // try the configured external provider first
  try{
    if(cfg.provider==="openai" && cfg.key) return await callOpenAI(apiMessages, cfg.key, cfg.model);
    if(cfg.provider==="gemini" && cfg.key) return await callGemini(apiMessages, cfg.key, cfg.model);
  }catch(err){
    // built-in AI exists ONLY inside the design preview, not on a real host
    if(window.claude && window.claude.complete) return await window.claude.complete({messages:apiMessages});
    throw err;
  }
  // no external provider configured: use built-in if present, else signal no-AI
  if(window.claude && window.claude.complete) return await window.claude.complete({messages:apiMessages});
  throw new Error("no-ai-provider");
}

/* ---- provider settings popover ---- */
function AISettings({onClose}){
  const [cfg,setCfg]=useState(getAICfg);
  const set=(patch)=>setCfg(c=>({...c,...patch}));
  const save=()=>{ setAICfgLS(cfg); onClose(true); };
  const needsKey = cfg.provider!=="builtin";
  return (
    <div className="ai-cfg" onClick={e=>e.stopPropagation()}>
      <div className="ai-cfg-head">
        <strong>AI engine</strong>
        <button className="ai-cfg-x" onClick={()=>onClose(false)} aria-label="Close"><window.Icon name="close" size={15}/></button>
      </div>
      <div className="ai-cfg-opts">
        {["builtin","openai","gemini"].map(p=>(
          <button key={p} className={"ai-cfg-opt"+(cfg.provider===p?" on":"")} onClick={()=>set({provider:p})}>
            {PROVIDER_LABEL[p]}
          </button>
        ))}
      </div>
      {needsKey && (
        <div className="ai-cfg-fields">
          <label className="ai-cfg-l">{cfg.provider==="openai"?"OpenAI API key":"Google AI API key"}</label>
          <input className="ai-cfg-in" type="password" placeholder={cfg.provider==="openai"?"sk-…":"AIza…"}
            value={cfg.key} onChange={e=>set({key:e.target.value.trim()})} autoComplete="off" spellCheck="false"/>
          <label className="ai-cfg-l">Model <span className="ai-cfg-hint">(optional)</span></label>
          <input className="ai-cfg-in" type="text" placeholder={DEFAULT_MODEL[cfg.provider]}
            value={cfg.model} onChange={e=>set({model:e.target.value.trim()})} autoComplete="off" spellCheck="false"/>
          <p className="ai-cfg-note">Stored only in your browser · calls go directly to {PROVIDER_LABEL[cfg.provider]}. If it fails, the built-in AI takes over.</p>
        </div>
      )}
      <button className="ai-cfg-save" onClick={save} disabled={needsKey && !cfg.key}>Save</button>
    </div>
  );
}

/* ============================================================ */
const STORE_KEY = "lalaland_ai_chat_v1";

window.AskAI = function(){
  const [open,setOpen]=useState(false);
  const [messages,setMessages]=useState(()=>{
    try{ const s=JSON.parse(localStorage.getItem(STORE_KEY)); if(Array.isArray(s)) return s; }catch(_){}
    return [];
  });
  const [input,setInput]=useState("");
  const [busy,setBusy]=useState(false);
  const [cfgOpen,setCfgOpen]=useState(false);
  const [providerLabel,setProviderLabel]=useState(()=>PROVIDER_LABEL[getAICfg().provider]);
  const threadRef=useRef(null);
  const inputRef=useRef(null);
  const taRef=useRef(null);

  // persist conversation
  useEffect(()=>{ try{ localStorage.setItem(STORE_KEY, JSON.stringify(messages.slice(-40))); }catch(_){} },[messages]);

  // body scroll-lock + focus + esc while open
  useEffect(()=>{
    if(!open) return;
    const prev=document.body.style.overflow;
    document.body.style.overflow="hidden";
    const t=setTimeout(()=>{ taRef.current && taRef.current.focus(); },340);
    const onKey=(e)=>{ if(e.key==="Escape") setOpen(false); };
    window.addEventListener("keydown",onKey);
    return ()=>{ document.body.style.overflow=prev; clearTimeout(t); window.removeEventListener("keydown",onKey); };
  },[open]);

  // keep thread pinned to bottom
  useEffect(()=>{
    const el=threadRef.current; if(el) el.scrollTop=el.scrollHeight;
  },[messages,busy,open]);

  const grow=()=>{ const ta=taRef.current; if(ta){ ta.style.height="auto"; ta.style.height=Math.min(ta.scrollHeight,140)+"px"; } };
  // chat is "limited" once the hand-off message has been shown
  const limited = messages.length>0 && messages[messages.length-1].limit;

  const send = useCallback(async (raw)=>{
    const text=(raw||"").trim();
    if(!text || busy) return;

    // STANDARD REPLIES (FAQ) — instant canned answer for general questions.
    // These do NOT count toward the enquiry limit.
    const faq = matchFAQ(text);
    const next=[...messages,{role:"user",content:text, faq: !!faq}];
    setMessages(next);
    setInput(""); if(taRef.current) taRef.current.style.height="auto";

    if(faq){
      setMessages(m=>[...m,{role:"assistant", faq:true, content:faq.a,
        waMsg: faq.wa || null }]);
      setTimeout(()=>taRef.current&&taRef.current.focus(),20);
      return;
    }

    // ENQUIRY LIMIT — allow 2 real (non-FAQ) AI answers per chat. On the 3rd
    // planning question, hand off to the team on WhatsApp.
    const realCount = next.filter(m=>m.role==="user" && !m.faq).length;
    if(realCount > 2){
      const questions = next.filter(m=>m.role==="user" && !m.faq).map(m=>m.content);
      setMessages(m=>[...m,{role:"assistant", limit:true,
        content:"I've loved helping you explore the options! To keep things personal from here, our travel team will take over. Tap below and we'll continue your enquiry on WhatsApp straight away — or start a new chat for another quick question.",
        waMsg: buildLimitEnquiry(questions)}]);
      setTimeout(()=>taRef.current&&taRef.current.focus(),20);
      return;
    }

    setBusy(true);
    try{
      const api=[{role:"user",content:SYSTEM},{role:"assistant",content:PRIMED_ACK},
        ...next.map(m=>({role:m.role,content:m.content}))];
      const reply=await askAI(api);
      const clean=(reply||"").trim() || "Sorry, I didn't quite catch that: could you rephrase your travel question?";
      const isRefusal=/only help with travel-related queries/i.test(clean);
      setMessages(m=>[...m,{role:"assistant",content:clean, pkgs:isRefusal?[]:matchPackages(text+" "+clean)}]);
    }catch(e){
      setMessages(m=>[...m,{role:"assistant",error:true,
        content:"For a detailed, personalised plan our travel experts are your best bet. Tap below to continue on WhatsApp and we'll help you straight away.",
        waMsg: buildLimitEnquiry(next.filter(x=>x.role==="user" && !x.faq).map(x=>x.content))}]);
    }finally{ setBusy(false); setTimeout(()=>taRef.current&&taRef.current.focus(),20); }
  },[messages,busy]);

  const newChat=()=>{ setMessages([]); setInput(""); setTimeout(()=>taRef.current&&taRef.current.focus(),50); };

  // answer a tappable-options prompt: lock that message, then send the choice(s)
  const answer = useCallback((idx,text)=>{
    setMessages(ms=>ms.map((mm,k)=>k===idx?{...mm,answered:true}:mm));
    send(text);
  },[send]);

  return (
    <React.Fragment>
      {/* launcher */}
      <button className={"ai-fab"+(open?" hidden":"")} onClick={()=>setOpen(true)} aria-label="Ask with AI: travel assistant">
        <span className="ai-fab-glow"></span>
        <window.Icon name="sparkle" size={20}/>
        <span className="ai-fab-label">Ask with AI</span>
      </button>

      {/* full-screen interface */}
      <div className={"ai-overlay"+(open?" open":"")} role="dialog" aria-modal="true" aria-label="LaLaLand AI travel assistant">
        <div className="ai-bg" aria-hidden="true"></div>
        <div className="ai-shell">
          <header className="ai-head">
            <div className="ai-head-id">
              <span className="ai-head-mark"><window.Icon name="sparkle" size={17}/></span>
              <span className="ai-head-txt">
                <strong>LaLaLand AI</strong>
                <small><span className="ai-dot"></span> Travel assistant</small>
              </span>
            </div>
            <div className="ai-head-act">
              {messages.length>0 && (
                <button className="ai-ghost" onClick={newChat} title="Start a new chat">
                  <window.Icon name="plus" size={16}/><span>New chat</span>
                </button>
              )}
              <button className="ai-close" onClick={()=>setOpen(false)} aria-label="Close assistant">
                <window.Icon name="close" size={18}/>
              </button>
            </div>
          </header>

          <div className="ai-thread" ref={threadRef}>
            <div className="ai-thread-inner">
              {messages.length===0 && !busy
                ? <Welcome onPick={(q)=>send(q)}/>
                : <React.Fragment>
                    {messages.map((m,i)=>{
                       const priorUserAnswers=messages.slice(0,i).filter(x=>x.role==="user").length;
                       return <Bubble key={i} m={m} last={i===messages.length-1} busy={busy}
                         prevUser={i>0&&messages[i-1].role==="user"?messages[i-1].content:""}
                         conclude={m.role==="assistant" && priorUserAnswers>=2}
                         onAnswer={(t)=>answer(i,t)}/>;
                     })}
                    {busy && <Typing/>}
                  </React.Fragment>}
            </div>
          </div>

          <div className="ai-composer">
            <div className="ai-composer-inner">
              <textarea ref={taRef} className="ai-input" rows={1} value={input}
                placeholder={limited ? "Start a new chat to ask again…" : "Ask about a destination, itinerary, visa, budget…"}
                disabled={limited}
                onChange={(e)=>{setInput(e.target.value);grow();}}
                onKeyDown={(e)=>{ if(e.key==="Enter" && !e.shiftKey){ e.preventDefault(); send(input); } }}/>
              <button className="ai-send" disabled={!input.trim()||busy||limited} onClick={()=>send(input)} aria-label="Send">
                <window.Icon name="arrow" size={18}/>
              </button>
            </div>
            <p className="ai-fineprint">{limited
              ? "You've reached the quick-question limit · continue with our team on WhatsApp"
              : "LaLaLand AI focuses on travel only · replies are AI-generated: confirm key details with our team."}</p>
          </div>
        </div>
      </div>
    </React.Fragment>
  );
};

/* ============================================================
   STYLES
   ============================================================ */
(function(){
  if(document.getElementById("ai-style")) return;
  const s=document.createElement("style"); s.id="ai-style";
  s.textContent = `
  .ai-fab{position:fixed;right:clamp(16px,3vw,30px);bottom:clamp(16px,3vw,30px);z-index:8500;
    display:inline-flex;align-items:center;gap:10px;padding:13px 20px 13px 16px;border-radius:100px;
    background:linear-gradient(135deg,var(--forest-2),var(--forest));color:var(--cream);
    box-shadow:0 18px 40px -14px rgba(21,34,79,.6),0 0 0 1px rgba(255,255,255,.08) inset;
    font-weight:600;font-size:15px;letter-spacing:-.01em;cursor:pointer;
    transition:transform .5s var(--ease-out),box-shadow .5s var(--ease-out),opacity .3s,visibility .3s}
  .ai-fab:hover{transform:translateY(-3px) scale(1.02);box-shadow:0 26px 54px -16px rgba(21,34,79,.7),0 0 0 1px rgba(255,255,255,.14) inset}
  .ai-fab svg{position:relative;z-index:2;animation:aiSpark 3.4s ease-in-out infinite}
  .ai-fab-label{position:relative;z-index:2;white-space:nowrap}
  .ai-fab-glow{position:absolute;inset:-2px;border-radius:inherit;z-index:1;pointer-events:none;
    background:radial-gradient(60% 120% at 22% 50%,rgba(107,142,209,.55),transparent 70%);
    opacity:.0;transition:opacity .5s}
  .ai-fab:hover .ai-fab-glow{opacity:1}
  .ai-fab.hidden{opacity:0;visibility:hidden;transform:translateY(16px) scale(.9)}
  @keyframes aiSpark{0%,100%{transform:rotate(0) scale(1)}50%{transform:rotate(90deg) scale(1.12)}}
  @media(max-width:560px){.ai-fab-label{display:none}.ai-fab{padding:14px}}

  .ai-overlay{position:fixed;inset:0;z-index:9500;opacity:0;visibility:hidden;
    transition:opacity .4s var(--ease-out),visibility .4s}
  .ai-overlay.open{opacity:1;visibility:visible}
  .ai-bg{position:absolute;inset:0;background:
     radial-gradient(120% 90% at 85% -10%,rgba(63,99,180,.30),transparent 55%),
     radial-gradient(100% 80% at 0% 110%,rgba(107,142,209,.22),transparent 55%),
     linear-gradient(160deg,#0f1a44 0%,#15224f 48%,#101a40 100%)}
  .ai-bg::after{content:"";position:absolute;inset:0;opacity:.05;mix-blend-mode:overlay;
    background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='2'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E")}
  .ai-shell{position:absolute;inset:0;display:flex;flex-direction:column;
    transform:translateY(22px) scale(.985);opacity:0;transition:transform .5s var(--ease-out),opacity .5s}
  .ai-overlay.open .ai-shell{transform:none;opacity:1;transition-delay:.06s}

  .ai-head{flex:0 0 auto;display:flex;align-items:center;justify-content:space-between;gap:16px;
    padding:16px clamp(16px,4vw,40px);border-bottom:1px solid rgba(202,222,251,.14);
    background:rgba(15,26,68,.55);backdrop-filter:blur(18px);-webkit-backdrop-filter:blur(18px)}
  .ai-head-id{display:flex;align-items:center;gap:13px;min-width:0}
  .ai-head-mark{width:42px;height:42px;border-radius:13px;flex:0 0 auto;display:grid;place-items:center;color:var(--cream);
    background:linear-gradient(135deg,var(--royal),var(--forest-2));box-shadow:0 8px 20px -8px rgba(63,99,180,.8),0 0 0 1px rgba(255,255,255,.12) inset}
  .ai-head-mark svg{animation:aiSpark 3.4s ease-in-out infinite}
  .ai-head-txt{display:flex;flex-direction:column;line-height:1.15;color:var(--cream)}
  .ai-head-txt strong{font-size:17px;letter-spacing:-.02em;font-weight:700}
  .ai-head-txt small{font-family:'JetBrains Mono',monospace;font-size:11px;letter-spacing:.05em;color:rgba(202,222,251,.72);
    display:flex;align-items:center;gap:7px;margin-top:3px}
  .ai-dot{width:7px;height:7px;border-radius:50%;background:#4fd39a;box-shadow:0 0 0 0 rgba(79,211,154,.6);animation:aiPulse 2.2s infinite}
  @keyframes aiPulse{0%{box-shadow:0 0 0 0 rgba(79,211,154,.55)}70%{box-shadow:0 0 0 7px rgba(79,211,154,0)}100%{box-shadow:0 0 0 0 rgba(79,211,154,0)}}
  .ai-head-act{display:flex;align-items:center;gap:10px;position:relative}
  .ai-cfg{position:absolute;top:54px;right:0;z-index:30;width:290px;padding:16px;border-radius:16px;
    background:#fff;color:var(--ink);box-shadow:0 30px 70px -24px rgba(0,0,0,.55),0 0 0 1px rgba(21,34,79,.08);
    animation:aiCfgPop .3s var(--ease-out)}
  @keyframes aiCfgPop{from{opacity:0;transform:translateY(-8px)}to{opacity:1;transform:none}}
  .ai-cfg-head{display:flex;align-items:center;justify-content:space-between;margin-bottom:12px}
  .ai-cfg-head strong{font-size:15px;letter-spacing:-.01em}
  .ai-cfg-x{width:26px;height:26px;border-radius:8px;display:grid;place-items:center;color:var(--moss);transition:background .3s}
  .ai-cfg-x:hover{background:rgba(21,34,79,.07)}
  .ai-cfg-opts{display:flex;gap:6px;background:var(--paper-2-solid);padding:4px;border-radius:11px}
  .ai-cfg-opt{flex:1;padding:8px 6px;border-radius:8px;font-size:12.5px;font-weight:600;color:var(--ink-soft);transition:all .3s}
  .ai-cfg-opt.on{background:#fff;color:var(--forest);box-shadow:0 2px 8px -2px rgba(21,34,79,.25)}
  .ai-cfg-fields{display:flex;flex-direction:column;gap:5px;margin-top:13px}
  .ai-cfg-l{font-size:11.5px;font-weight:600;color:var(--ink-soft);margin-top:5px}
  .ai-cfg-hint{font-weight:400;color:var(--moss)}
  .ai-cfg-in{padding:10px 12px;border-radius:10px;border:1px solid var(--line-strong);font-size:13px;color:var(--ink);background:var(--paper-solid)}
  .ai-cfg-in:focus{outline:none;border-color:var(--royal)}
  .ai-cfg-note{font-size:11px;line-height:1.45;color:var(--moss);margin-top:6px}
  .ai-cfg-save{margin-top:14px;width:100%;padding:11px;border-radius:11px;font-weight:700;font-size:13.5px;color:#fff;
    background:var(--grad-dark);transition:filter .3s,opacity .3s}
  .ai-cfg-save:hover:not(:disabled){filter:brightness(1.12)}
  .ai-cfg-save:disabled{opacity:.45;cursor:not-allowed}
  .ai-ghost{display:inline-flex;align-items:center;gap:7px;padding:9px 15px;border-radius:100px;color:var(--cream);
    font-size:13.5px;font-weight:600;border:1px solid rgba(202,222,251,.2);background:rgba(202,222,251,.04);
    transition:all .35s var(--ease-out)}
  .ai-ghost:hover{background:rgba(202,222,251,.12);border-color:rgba(202,222,251,.4)}
  .ai-close{width:42px;height:42px;border-radius:12px;display:grid;place-items:center;color:var(--cream);
    border:1px solid rgba(202,222,251,.2);background:rgba(202,222,251,.04);transition:all .35s var(--ease-out)}
  .ai-close:hover{background:rgba(255,120,120,.16);border-color:rgba(255,150,150,.4);transform:rotate(90deg)}
  @media(max-width:560px){.ai-ghost span{display:none}.ai-ghost{padding:9px}}

  .ai-thread{flex:1 1 auto;overflow-y:auto;overflow-x:hidden;scroll-behavior:smooth}
  .ai-thread-inner{max-width:820px;margin:0 auto;padding:30px clamp(16px,4vw,40px) 22px;
    display:flex;flex-direction:column;gap:20px}

  .ai-row{display:flex;gap:13px;animation:aiRise .5s var(--ease-out) both}
  @keyframes aiRise{from{opacity:0;transform:translateY(12px)}to{opacity:1;transform:none}}
  .ai-row.user{justify-content:flex-end}
  .ai-avatar{flex:0 0 auto;width:34px;height:34px;border-radius:11px;display:grid;place-items:center;color:var(--cream);
    margin-top:2px;background:linear-gradient(135deg,var(--royal),var(--forest-2));box-shadow:0 6px 16px -8px rgba(63,99,180,.9)}
  .ai-bot-col{min-width:0;max-width:min(640px,86%);display:flex;flex-direction:column;gap:12px}
  .ai-bubble{border-radius:18px;padding:14px 17px;font-size:15.5px;line-height:1.6;word-wrap:break-word;overflow-wrap:anywhere}
  .ai-bubble.user{background:linear-gradient(135deg,var(--royal),var(--forest-3));color:#fff;border-bottom-right-radius:6px;
    max-width:min(560px,82%);box-shadow:0 12px 28px -16px rgba(63,99,180,.9);white-space:pre-wrap}
  .ai-bubble.bot{background:rgba(251,249,244,.96);color:var(--ink);border-bottom-left-radius:6px;
    box-shadow:0 18px 44px -26px rgba(0,0,0,.5)}
  .ai-bubble.bot.err{background:rgba(255,238,236,.97);color:#7a2a2a}
  .ai-bubble.bot p{margin:0 0 9px}.ai-bubble.bot p:last-child{margin-bottom:0}
  .ai-bubble.bot strong{font-weight:700;color:var(--forest)}
  .ai-bubble.bot em{font-style:italic}
  .ai-bubble.bot code{font-family:'JetBrains Mono',monospace;font-size:.86em;background:rgba(21,34,79,.08);padding:1px 6px;border-radius:6px}
  .ai-bubble.bot h2{font-size:19px;letter-spacing:-.02em;margin:6px 0 8px;color:var(--forest)}
  .ai-bubble.bot h3{font-size:16px;letter-spacing:-.01em;margin:14px 0 7px;color:var(--ink)}
  .ai-bubble.bot h3.ai-day{padding-left:13px;border-left:3px solid var(--royal);color:var(--forest);font-weight:700}
  .ai-bubble.bot h4{font-size:14px;margin:11px 0 5px;color:var(--ink-soft)}
  .ai-bubble.bot ul,.ai-bubble.bot ol{margin:4px 0 10px;padding-left:20px}
  .ai-bubble.bot li{margin:3px 0}
  .ai-bubble.bot li::marker{color:var(--royal)}
  .ai-bubble.bot blockquote{margin:8px 0;padding:6px 14px;border-left:3px solid var(--terra-soft);
    background:rgba(63,99,180,.06);border-radius:0 10px 10px 0;color:var(--ink-soft)}
  .ai-bubble.bot a{color:var(--accent-blue);text-decoration:underline;text-underline-offset:2px}
  .ai-bubble.bot>*:first-child{margin-top:0}.ai-bubble.bot>*:last-child{margin-bottom:0}

  .ai-bubble.typing{display:inline-flex;gap:6px;align-items:center;padding:16px 18px}
  .ai-bubble.typing i{width:8px;height:8px;border-radius:50%;background:var(--moss-2);animation:aiBlink 1.3s infinite}
  .ai-bubble.typing i:nth-child(2){animation-delay:.18s}.ai-bubble.typing i:nth-child(3){animation-delay:.36s}
  @keyframes aiBlink{0%,80%,100%{opacity:.3;transform:translateY(0)}40%{opacity:1;transform:translateY(-4px)}}

  .ai-pkg-row{display:flex;flex-direction:column;gap:9px}
  .ai-pkg-lead{display:inline-flex;align-items:center;gap:7px;font-family:'JetBrains Mono',monospace;font-size:11px;
    letter-spacing:.12em;text-transform:uppercase;color:rgba(202,222,251,.8)}
  .ai-pkg-lead svg{color:var(--terra-soft)}
  .ai-pkg-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:11px}
  .ai-pkg{display:flex;gap:12px;text-align:left;padding:10px;border-radius:15px;cursor:pointer;
    background:rgba(251,249,244,.07);border:1px solid rgba(202,222,251,.16);transition:all .4s var(--ease-out)}
  .ai-pkg:hover{background:rgba(251,249,244,.13);border-color:rgba(202,222,251,.4);transform:translateY(-2px)}
  .ai-pkg-thumb{position:relative;flex:0 0 auto;width:62px;height:62px;border-radius:11px;overflow:hidden}
  .ai-pkg-thumb .ph-grad{position:absolute;inset:0}
  .ai-pkg-thumb img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover}
  .ai-pkg-meta{display:flex;flex-direction:column;min-width:0;gap:3px;flex:1;color:var(--cream)}
  .ai-pkg-name{font-weight:700;font-size:14.5px;letter-spacing:-.01em;line-height:1.2}
  .ai-pkg-places{font-size:12px;color:rgba(202,222,251,.72);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
  .ai-pkg-foot{display:flex;align-items:center;justify-content:space-between;margin-top:auto;padding-top:4px}
  .ai-pkg-dur{font-family:'JetBrains Mono',monospace;font-size:11px;letter-spacing:.04em;color:var(--terra-soft)}
  .ai-pkg-go{display:inline-flex;align-items:center;gap:4px;font-size:12px;font-weight:700;color:var(--cream)}
  .ai-pkg-go svg{transition:transform .35s var(--ease-out)}
  .ai-pkg:hover .ai-pkg-go svg{transform:translate(2px,-2px)}

  /* enquire-on-whatsapp button under a reply */
  .ai-enquire{align-self:flex-start;display:inline-flex;align-items:center;gap:9px;margin-top:2px;
    padding:11px 18px;border-radius:100px;font-size:13.5px;font-weight:700;letter-spacing:-.01em;cursor:pointer;
    color:#fff;background:linear-gradient(135deg,#25b85a,#15914a);
    box-shadow:0 12px 26px -14px rgba(20,150,74,.9),0 0 0 1px rgba(255,255,255,.12) inset;
    transition:transform .35s var(--ease-out),box-shadow .35s var(--ease-out)}
  .ai-enquire svg{flex:0 0 auto}
  .ai-enquire:hover{transform:translateY(-2px);box-shadow:0 18px 34px -14px rgba(20,150,74,.95),0 0 0 1px rgba(255,255,255,.2) inset}
  .ai-enquire.limit{margin-top:8px;padding:13px 22px;font-size:14.5px}
  .ai-input:disabled{opacity:.6;cursor:not-allowed}
  .ai-send:disabled{opacity:.4;cursor:not-allowed}

  /* welcome */
  .ai-welcome{display:flex;flex-direction:column;gap:24px;animation:aiRise .6s var(--ease-out) both;padding-top:8px}
  .ai-hero{text-align:center;max-width:640px;margin:0 auto}
  .ai-badge{display:inline-flex;align-items:center;gap:8px;padding:7px 15px;border-radius:100px;color:var(--terra-soft);
    font-family:'JetBrains Mono',monospace;font-size:11.5px;letter-spacing:.18em;text-transform:uppercase;
    border:1px solid rgba(202,222,251,.22);background:rgba(202,222,251,.05)}
  .ai-hero-title{font-size:clamp(34px,6vw,52px);letter-spacing:-.03em;color:var(--cream);margin:18px 0 12px;font-weight:700;line-height:1}
  .ai-hero-sub{font-size:16.5px;line-height:1.6;color:rgba(238,243,253,.84)}
  .ai-hero-sub strong{color:#fff;font-weight:700}

  .ai-quick{display:grid;grid-template-columns:repeat(4,1fr);gap:11px;max-width:720px;margin:2px auto 0;width:100%}
  .ai-quick-btn{display:flex;flex-direction:column;align-items:flex-start;gap:11px;padding:16px;border-radius:16px;text-align:left;
    color:var(--cream);background:rgba(251,249,244,.06);border:1px solid rgba(202,222,251,.16);cursor:pointer;
    font-weight:600;font-size:14.5px;line-height:1.25;transition:all .4s var(--ease-out)}
  .ai-quick-btn svg{color:var(--terra-soft)}
  .ai-quick-btn:hover{background:rgba(251,249,244,.12);border-color:rgba(202,222,251,.42);transform:translateY(-3px)}
  @media(max-width:720px){.ai-quick{grid-template-columns:repeat(2,1fr)}}

  .ai-pop-head{max-width:920px;margin:6px auto -6px;width:100%}
  .ai-pop-head .eyebrow{color:rgba(202,222,251,.78)}
  .ai-pop-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:13px;max-width:920px;margin:0 auto;width:100%}
  .ai-pop{position:relative;border-radius:17px;overflow:hidden;cursor:pointer;aspect-ratio:16/10;
    border:1px solid rgba(202,222,251,.14);transition:transform .5s var(--ease-out),box-shadow .5s}
  .ai-pop:hover{transform:translateY(-4px);box-shadow:0 26px 50px -24px rgba(0,0,0,.7)}
  .ai-pop-img{position:absolute;inset:0}
  .ai-pop-img img{width:100%;height:100%;object-fit:cover;transition:transform .8s var(--ease-out)}
  .ai-pop:hover .ai-pop-img img{transform:scale(1.07)}
  .ai-pop-shade{position:absolute;inset:0;background:linear-gradient(180deg,transparent 30%,rgba(10,18,46,.85))}
  .ai-pop-text{position:absolute;left:0;right:0;bottom:0;padding:14px;display:flex;flex-direction:column;gap:2px;color:var(--cream);z-index:2}
  .ai-pop-name{font-weight:700;font-size:16px;letter-spacing:-.02em}
  .ai-pop-blurb{font-size:12.5px;color:rgba(202,222,251,.86)}
  @media(max-width:720px){.ai-pop-grid{grid-template-columns:repeat(2,1fr)}}

  .ai-sugg{display:flex;flex-wrap:wrap;gap:9px;justify-content:center;max-width:760px;margin:4px auto 6px}
  .ai-chip{padding:9px 16px;border-radius:100px;font-size:13.5px;font-weight:500;color:var(--cream);cursor:pointer;
    background:rgba(202,222,251,.06);border:1px solid rgba(202,222,251,.18);transition:all .35s var(--ease-out)}
  .ai-chip:hover{background:var(--terra-soft);color:var(--forest);border-color:var(--terra-soft);transform:translateY(-2px)}

  .ai-composer{flex:0 0 auto;padding:14px clamp(16px,4vw,40px) 18px;
    background:linear-gradient(0deg,rgba(15,26,68,.85),rgba(15,26,68,0));backdrop-filter:blur(8px)}
  .ai-composer-inner{max-width:820px;margin:0 auto;display:flex;align-items:flex-end;gap:10px;
    background:rgba(251,249,244,.97);border-radius:20px;padding:8px 8px 8px 18px;
    box-shadow:0 24px 60px -28px rgba(0,0,0,.7),0 0 0 1px rgba(255,255,255,.12) inset}
  .ai-input{flex:1;border:none;outline:none;background:none;resize:none;font-size:15.5px;line-height:1.5;color:var(--ink);
    padding:9px 0;max-height:140px;font-family:inherit}
  .ai-input::placeholder{color:var(--moss)}
  .ai-send{flex:0 0 auto;width:44px;height:44px;border-radius:14px;display:grid;place-items:center;color:#fff;
    background:linear-gradient(135deg,var(--royal),var(--forest-2));transition:all .35s var(--ease-out)}
  .ai-send:hover:not(:disabled){transform:translateY(-2px) scale(1.04);box-shadow:0 12px 26px -10px rgba(63,99,180,.9)}
  .ai-send:disabled{opacity:.4;cursor:not-allowed}
  .ai-fineprint{max-width:820px;margin:10px auto 0;text-align:center;font-size:11.5px;color:rgba(202,222,251,.6);
    font-family:'JetBrains Mono',monospace;letter-spacing:.02em}

  /* tappable quick-reply options */
  .ai-opts{display:flex;flex-direction:column;gap:13px;margin-top:2px}
  .ai-opt-group{display:flex;flex-direction:column;gap:8px}
  .ai-opt-q{font-size:12.5px;font-weight:700;letter-spacing:.01em;color:rgba(218,231,251,.92)}
  .ai-opt-chips{display:flex;flex-wrap:wrap;gap:8px}
  .ai-opt{padding:9px 16px;border-radius:100px;font-size:13.5px;font-weight:600;color:var(--cream);cursor:pointer;
    background:rgba(202,222,251,.08);border:1px solid rgba(202,222,251,.24);transition:all .3s var(--ease-out)}
  .ai-opt:hover:not(:disabled){background:rgba(202,222,251,.17);border-color:rgba(202,222,251,.55);transform:translateY(-1px)}
  .ai-opt.sel{background:var(--terra-soft);color:var(--forest);border-color:var(--terra-soft)}
  .ai-opt:disabled{cursor:default}
  .ai-opt:disabled:not(.sel){opacity:.45}
  .ai-opt-send{align-self:flex-start;margin-top:3px;display:inline-flex;align-items:center;gap:8px;padding:11px 22px;border-radius:100px;
    font-weight:700;font-size:14px;color:#fff;background:linear-gradient(135deg,var(--royal),var(--forest-2));
    transition:all .35s var(--ease-out);box-shadow:0 12px 26px -14px rgba(63,99,180,.9)}
  .ai-opt-send:hover:not(:disabled){transform:translateY(-2px)}
  .ai-opt-send:disabled{opacity:.4;cursor:default;box-shadow:none}

  /* AI thinking: globe loader bubble */
  .ai-think{display:inline-flex;align-items:center;gap:13px;padding:10px 18px 10px 12px;border-radius:18px;
    border-bottom-left-radius:6px;background:rgba(251,249,244,.96);box-shadow:0 18px 44px -26px rgba(0,0,0,.5)}
  .ai-think-txt{font-size:14.5px;font-weight:600;color:var(--ink-soft);animation:aiThinkFade .5s var(--ease-out)}
  @keyframes aiThinkFade{from{opacity:0;transform:translateY(4px)}to{opacity:1;transform:none}}
  .ai-tl{position:relative;display:inline-block;flex:0 0 auto}
  .ai-tl-ring{position:absolute;inset:6%;border-radius:50%;border:1.5px dashed rgba(63,99,180,.32);animation:aiTlSpin 16s linear infinite}
  .ai-tl-orbit{position:absolute;inset:0;animation:aiTlSpin 3s linear infinite}
  .ai-tl-plane{position:absolute;top:0;left:50%;width:34%;height:34%;margin-left:-17%;filter:drop-shadow(0 2px 3px rgba(0,0,0,.22))}
  .ai-tl-plane svg{display:block;width:100%;height:100%;transform:rotate(90deg)}
  .ai-tl-pin{position:absolute;top:50%;left:50%;width:46%;height:46%;transform:translate(-50%,-54%)}
  .ai-tl-pin svg{display:block;width:100%;height:100%;animation:aiTlBob 2.4s ease-in-out infinite}
  @keyframes aiTlSpin{to{transform:rotate(360deg)}}
  @keyframes aiTlBob{0%,100%{transform:translateY(0)}50%{transform:translateY(-3px)}}
  `;
  document.head.appendChild(s);
})();
