/* Custom Cuvée Corporate Gifts V20.2 — PUBLIC frontend
Publieke configuratorfrontend.
API: https://customcuvee-corporate-gifts.long-queen-43a7.workers.dev
*/
(function(){
"use strict";
function init(){
const root=document.getElementById("cc-corporate-gifts");
if(!root||root.dataset.ccV20==="1")return;root.dataset.ccV20="1";
const $=id=>root.querySelector("#"+id);
const PUBLIC={"version":"2.2-api","minimum_bottles":50,"occasions":{"relations":{"name":"Klanten & relaties","hint":"Representatief, breed toegankelijk en zichtbaar branded."},"employees":{"name":"Medewerkers","hint":"Een warm cadeau met goede prijs-kwaliteit en voldoende inhoud."},"event":{"name":"Jubileum & event","hint":"Feestelijk en geschikt om direct te overhandigen."},"other":{"name":"Anders","hint":"We houden de aanbeveling breed en praktisch."}},"countries":{"Frankrijk":{"verified":["Vigné-Lourac"],"families":2},"Italië":{"verified":["Feudi Salentini · 125"],"families":1},"Spanje":{"verified":["Cavas Masachs · Dignitat","Bodegas Alceño · Gold Senses"],"families":5},"Duitsland":{"verified":[],"families":2},"Oostenrijk":{"verified":[],"families":1},"Zuid-Afrika":{"verified":[],"families":1},"Geen voorkeur":{"verified":[],"families":1}},"wine_glasses":{"max_standard_box_capacity":4}};
const API_BASE="https://customcuvee-corporate-gifts.long-queen-43a7.workers.dev";
const fallbackBudgets=[
{id:"custom",label:"Eigen budget / speciaal verzoek",min:0,max:999,copy:"Geef je doelbedrag of afwijkende wens mee.",badge:"Maatwerk"}
];
const tastes=[
{id:"accessible",label:"Breed toegankelijk",copy:"Veilige keuze voor een brede groep."},
{id:"fresh",label:"Fris & modern",copy:"Levendig, droog en eigentijds."},
{id:"round",label:"Rond & rijk",copy:"Meer body en cadeaugevoel."},
{id:"full",label:"Vol & uitgesproken",copy:"Voor een wijn die echt blijft hangen."},
{id:"elegant",label:"Elegant & feestelijk",copy:"Verfijnd of mousserend."},
{id:"distinctive",label:"Verras me",copy:"Iets minder voor de hand liggend."}
];
const state={index:0,occasion:null,qty:100,qtyPlus:false,budget:null,customBudget:"",specialRequest:"",drink:null,wineCount:1,composition:null,taste:null,origin:"any",recommendation:null,beerCount:4,packageId:null,branding:true,glassCount:0};
const remote={options:null,recommendations:null,calc:null,upsell:null,finish:null,seq:0};
let refreshTimer=null;
const money=v=>new Intl.NumberFormat("nl-NL",{style:"currency",currency:"EUR"}).format(Number(v||0));
const esc=s=>String(s||"").replace(/[&<>"']/g,ch=>({"&":"&","<":"<",">":">","\"":""","'":"'"}[ch]));
const sameIds=(a,b)=>!!a&&!!b&&[...a].sort().join("|")===[...b].sort().join("|");
function budget(){
const list=remote.options?.budgetOptions||fallbackBudgets;
const b=list.find(x=>x.id===state.budget);
if(!b)return null;
if(b.id==="custom"&&Number(state.customBudget)>0){
const t=Number(state.customBudget);
return {...b,min:Math.max(0,t-2.5),max:t+2.5}
}
return b
}
function steps(){
const c=["occasion","quantity","drink"];
if(state.drink==="wine")return c.concat(["wineFormat","budget","wineComposition","wineTaste","wineOrigin","wineRecommendation","finish","result"]);
if(state.drink==="beer")return c.concat(["beerChoice","budget","finish","result"]);
return c
}
const currentKey=()=>steps()[state.index];
const minGift=n=>Math.ceil(PUBLIC.minimum_bottles/Math.max(1,Number(n)||1));
const total=n=>Number(state.qty||0)*Math.max(1,Number(n)||1);
function moq(n,label="flessen"){const t=total(n),g=minGift(n);return t>=PUBLIC.minimum_bottles?`
${t} ${label} totaal · minimum van ${PUBLIC.minimum_bottles} gehaald.
`:`
${t} ${label} totaal · minimaal ${PUBLIC.minimum_bottles} nodig. Kies ten minste ${g} geschenken.
`}
function api(path,payload){
if(!/^https:\/\//.test(API_BASE))return Promise.reject(new Error("V20 API is nog niet gekoppeld"));
return fetch(API_BASE.replace(/\/$/,"")+path,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(payload)})
.then(async r=>{const j=await r.json().catch(()=>({}));if(!r.ok)throw new Error(j.error||"API-fout");return j})
}
function invalidate(level="all"){remote.seq++;remote.calc=null;remote.upsell=null;remote.finish=null;if(level==="all"){remote.options=null;remote.recommendations=null}else if(level==="recommend")remote.recommendations=null}
function scheduleRefresh(ms=120){clearTimeout(refreshTimer);refreshTimer=setTimeout(refreshRemote,ms)}
function renderProgress(){const p=$("progress");p.innerHTML="";steps().forEach((_,i)=>{const s=document.createElement("span");if(i<=state.index)s.className="on";p.appendChild(s)})}
function renderPath(){const bits=[];if(state.occasion)bits.push(PUBLIC.occasions[state.occasion]?.name);if(state.qty)bits.push(`${state.qtyPlus?"500+":state.qty} geschenken`);if(state.budget)bits.push(state.budget==="custom"?(state.customBudget?`Maatwerk ± €${Number(state.customBudget).toFixed(2).replace(".",",")}`:"Maatwerk"):budget()?.label);if(state.drink)bits.push(state.drink==="wine"?"Wijn":"Speciaalbier");if(state.drink==="wine")bits.push(`${state.wineCount} fles${state.wineCount>1?"sen":""} p/geschenk`);if(state.drink==="beer")bits.push(`${state.beerCount} bieren p/geschenk`);if(state.taste)bits.push(tastes.find(x=>x.id===state.taste)?.label);if(state.origin&&state.origin!=="any")bits.push(state.origin);if(state.glassCount)bits.push(`${state.glassCount} wijnglas${state.glassCount>1?"zen":""}`);const p=$("pathline");if(bits.length){p.style.display="block";p.innerHTML=`Jouw keuzes: ${bits.join(" · ")}`}else{p.style.display="none";p.innerHTML=""}}
function renderQuantity(){const i=$("qty"),d=$("qtyDisplay"),h=$("qtyHint");i.value=state.qty;state.qtyPlus=state.qty>=500;d.textContent=state.qtyPlus?"500+":state.qty;i.style.setProperty("--range-progress",Math.max(0,Math.min(100,((state.qty-1)/499)*100))+"%");let n=null,l="flessen";if(state.drink==="wine")n=state.wineCount;if(state.drink==="beer"){n=state.beerCount;l="bierflessen"}if(n){const t=total(n);h.textContent=t``).join("")}
function renderBudget(){
const list=remote.options?.budgetOptions||[];
if(!state.drink){
$("budgetOptions").innerHTML='
Kies eerst wijn of speciaalbier en de inhoud per geschenk.
We berekenen passende budgetopties voor deze oplage…
';
}else{
$("budgetOptions").innerHTML=list.map(b=>``).join("")
}
$("customBudgetPanel").style.display=state.budget==="custom"?"block":"none"
}
function renderFormats(){
$("formatOptions").innerHTML=[1,2,3].map(n=>{
const from=remote.options?.formatPrices?.[n];
const copy=n===1?"Compact en sterk branded.":n===2?"Wit + rood of twee stijlen uit één familie.":"Een complete selectie met maximale cadeauwaarde.";
const valid=total(n)>=PUBLIC.minimum_bottles;
return ``
}).join("")+moq(state.wineCount)
}
function compositionDefs(){if(state.wineCount===1)return [["white","Wit","Van fris Sauvignon tot rijke Chardonnay."],["red","Rood","Van soepele Merlot tot volle Primitivo."],["rose","Rosé","Fris, fruitig en breed inzetbaar."],["sparkling","Mousserend","Feestelijk: Mousseux of Cava."],["any","Laat mij adviseren","Kleur is ondergeschikt aan de beste match."]];if(state.wineCount===2)return [["white_red","Wit + rood","De klassieke zakelijke combinatie."],["two_white","Twee witte wijnen","Bijvoorbeeld twee stijlen uit één wijnhuis."],["two_red","Twee rode wijnen","Voor een voller, winterser cadeau."],["sparkling_still","Bubbel + stille wijn","Feestelijker, binnen één land waar mogelijk."],["any","Laat mij adviseren","Wij kiezen het sterkste coherente duo."]];return [["white_red_sparkling","Wit + rood + bubbel","Complete, feestelijke selectie."],["white_rose_red","Wit + rosé + rood","Breed en toegankelijk."],["one_winery","Drie uit één wijnhuis","Maximale inhoudelijke samenhang."],["any","Laat mij adviseren","De sterkste trio-combinatie binnen één familie/land."]]}
function renderCompositions(){if(!state.composition)state.composition=state.wineCount===1?"any":state.wineCount===2?"white_red":"one_winery";$("compositionOptions").innerHTML=compositionDefs().map(([id,n,c])=>``).join("")}
function renderTastes(){$("tasteOptions").innerHTML=tastes.map(t=>``).join("")}
function originDescription(c){const o=PUBLIC.countries[c];if(!o)return "";if(o.verified?.length)return `O.a. ${o.verified.join(" en ")}.`;return `${o.families} private-label familielijn${o.families===1?"":"en"} beschikbaar.`}
function renderOrigins(){const cs=remote.options?.validOrigins||Object.keys(PUBLIC.countries).sort();if(state.origin!=="any"&&!cs.includes(state.origin))state.origin="any";$("originOptions").innerHTML=[["any","Geen voorkeur","Laat smaak en samenhang beslissen."]].concat(cs.map(c=>[c,c,originDescription(c)])).map(([id,n,d])=>``).join("")}
function colorName(c){return {white:"wit",red:"rood",rose:"rosé",sparkling:"mousserend"}[c]||c}
function recReason(x){const bits=[x.coherence.type==="Eén wijnhuis"?`Alles van ${x.coherence.label}`:x.coherence.type==="Eén familielijn"?`Eén consistente familielijn uit ${x.wines[0].country}`:`Alles uit ${x.wines[0].country}`];const t=tastes.find(z=>z.id===state.taste);if(t)bits.push(`sluit aan op “${t.label.toLowerCase()}”`);return bits.join(" · ")+"."}
function renderRecommendations(){const opts=remote.recommendations;if(!opts){$("wineRecommendations").innerHTML='
`}}
function renderSummary(){const c=remote.calc,rows=$("summaryRows");if(!state.drink){rows.innerHTML="";$("unitPrice").textContent="—";$("orderTotal").textContent="";return}if(!c){rows.innerHTML='
`;rows.innerHTML=html;$("summaryHint").textContent="";$("unitPrice").textContent=money(c.unit);$("orderTotal").textContent=(state.qtyPlus?"Indicatieve orderwaarde vanaf ":"Indicatieve orderwaarde ")+money(c.revenue);const b=budget(),st=$("budgetStatus");if(state.budget==="custom"&&!Number(state.customBudget)){st.style.display="block";st.className="status good";st.textContent="Je speciale verzoek nemen we mee in de offerte."}else if(b&&b.id!=="open"){st.style.display="block";if(c.unit>=b.min&&c.unit<=b.max){st.className="status good";st.textContent="Deze samenstelling past binnen je richtbudget."}else if(c.unit>b.max){st.className="status warn";st.textContent="Deze samenstelling ligt boven je richtbudget."}else{st.className="status good";st.textContent="Je blijft onder budget. Er is ruimte voor een upgrade."}}else st.style.display="none";const u=remote.upsell,up=$("sidebarUpsell");up.innerHTML=u?`