Repository navigation
Expand file tree
/
Copy pathscript.js
More file actions
144 lines (132 loc) · 22.5 KB
/
Copy pathscript.js
File metadata and controls
144 lines (132 loc) · 22.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
const products=[
{id:"p1",name:"Soft Current Cleanser",category:"Cleanser",price:890,size:"120 ml",badge:"Bestseller",concerns:["dry","balanced","gentle"],description:"A fictional milky cleanser concept designed to communicate a soft, non-stripping daily ritual.",image:"https://images.unsplash.com/photo-1608248543803-ba4f8c70ae0b?auto=format&fit=crop&w=1000&q=88"},
{id:"p2",name:"Dewline Gel Cleanser",category:"Cleanser",price:790,size:"120 ml",badge:"New",concerns:["oily","combination","fresh"],description:"A fictional lightweight gel cleanser for a fresh-feeling ecommerce product story.",image:"https://images.unsplash.com/photo-1608571423902-eed4a5ad8108?auto=format&fit=crop&w=1000&q=88"},
{id:"p3",name:"Meadow Drop Serum",category:"Serum",price:1490,size:"30 ml",badge:"Bestseller",concerns:["balanced","maintenance","light"],description:"A fictional daily serum concept with a fluid texture and simple routine positioning.",image:"https://images.unsplash.com/photo-1620916566398-39f1143ab7be?auto=format&fit=crop&w=1000&q=88"},
{id:"p4",name:"Barrier Cloud Cream",category:"Moisturiser",price:1290,size:"50 g",badge:"New",concerns:["dry","comfort","rich"],description:"A fictional cushiony cream concept for comfort-focused product discovery.",image:"https://images.unsplash.com/photo-1556228578-8c89e6adf883?auto=format&fit=crop&w=1000&q=88"},
{id:"p5",name:"Water Veil Gel",category:"Moisturiser",price:1090,size:"50 g",badge:"",concerns:["oily","combination","light"],description:"A fictional lightweight gel moisturiser concept for warm weather and minimal routines.",image:"https://images.unsplash.com/photo-1556228720-195a672e8a03?auto=format&fit=crop&w=1000&q=88"},
{id:"p6",name:"Day Halo SPF 50",category:"Suncare",price:990,size:"50 ml",badge:"Bestseller",concerns:["sun","balanced","fluid"],description:"A fictional daily sun-care product used only to demonstrate ecommerce merchandising.",image:"https://images.unsplash.com/photo-1556228720-195a672e8a03?auto=format&fit=crop&w=1000&q=88"},
{id:"p7",name:"Night Orchard Oil",category:"Serum",price:1690,size:"30 ml",badge:"Limited",concerns:["dry","rich","night"],description:"A fictional facial oil concept positioned as an evening ritual without therapeutic claims.",image:"https://images.unsplash.com/photo-1608571423539-e951b7468f42?auto=format&fit=crop&w=1000&q=88"},
{id:"p8",name:"Quiet Lip Balm",category:"Moisturiser",price:590,size:"12 g",badge:"",concerns:["dry","comfort","travel"],description:"A fictional compact balm concept that demonstrates lower-ticket add-on merchandising.",image:"https://images.unsplash.com/photo-1608248597279-f99d160bfcbc?auto=format&fit=crop&w=1000&q=88"}
];
const themes=[
{name:"Cocoa",accent:"#7B5D45",soft:"#D8C5B6"},{name:"Sage",accent:"#62745A",soft:"#C5D0C0"},{name:"Rosewood",accent:"#8A5663",soft:"#D5BBC2"},{name:"Olive",accent:"#757046",soft:"#D0CDAF"},{name:"Terracotta",accent:"#9B5F49",soft:"#DAB9AC"},{name:"Plum",accent:"#745A70",soft:"#CABDC7"},{name:"Ocean",accent:"#52737A",soft:"#BED0D3"},{name:"Charcoal",accent:"#5C5A58",soft:"#C5C4C2"}];
const fonts=["Cormorant Garamond","Playfair Display","Bodoni Moda","DM Serif Display","Prata","Manrope"];
const root=document.documentElement,reduced=matchMedia("(prefers-reduced-motion: reduce)").matches,systemDark=matchMedia("(prefers-color-scheme: dark)");
let themeIndex=0,fontIndex=0,colourLocked=false,fontLocked=false,selectedMode="light",activeFilter="All",cart={},wishlist=new Set(),currentProduct=null,bundle=new Set(),routineIds=["p1","p4","p6"],cadence="Every 30 days";
const swatches=document.getElementById("themeSwatches"),customColour=document.getElementById("customColour"),customHex=document.getElementById("customHex"),fontSelect=document.getElementById("fontSelect"),colourStatus=document.getElementById("colourStatus"),fontStatus=document.getElementById("fontStatus"),modeStatus=document.getElementById("modeStatus"),lockColour=document.getElementById("lockColour"),lockFont=document.getElementById("lockFont"),lockBoth=document.getElementById("lockBoth");
function money(n){return new Intl.NumberFormat("en-IN",{style:"currency",currency:"INR",maximumFractionDigits:0}).format(n)}
function hexToRgb(hex){const c=String(hex).replace("#","");if(!/^[0-9a-fA-F]{6}$/.test(c))return null;return[parseInt(c.slice(0,2),16),parseInt(c.slice(2,4),16),parseInt(c.slice(4,6),16)]}
function mix(hex,a=.64){const r=hexToRgb(hex);return r?`#${r.map(v=>Math.round(v+(255-v)*a).toString(16).padStart(2,"0")).join("")}`:"#D8C5B6"}
function applyTheme(t,i=-1){const rgb=hexToRgb(t.accent);if(!rgb)return;root.style.setProperty("--accent",t.accent);root.style.setProperty("--accent-rgb",rgb.join(","));root.style.setProperty("--accent-soft",t.soft||mix(t.accent));customColour.value=t.accent;customHex.value=t.accent.toUpperCase();document.querySelector('meta[name="theme-color"]').content=t.accent;[...swatches.children].forEach((b,n)=>b.classList.toggle("active",n===i))}
function applyFont(f){root.style.setProperty("--display",`"${f}",Georgia,serif`);fontSelect.value=f}
function resolveMode(m){root.dataset.mode=m==="system"?(systemDark.matches?"dark":"light"):m;modeStatus.textContent=m[0].toUpperCase()+m.slice(1);document.querySelectorAll("[data-mode-value]").forEach(b=>b.classList.toggle("active",b.dataset.modeValue===m))}
function updateLocks(){lockColour.classList.toggle("locked",colourLocked);lockFont.classList.toggle("locked",fontLocked);lockBoth.classList.toggle("locked",colourLocked&&fontLocked);lockColour.querySelector("b").textContent=colourLocked?"Locked":"Unlocked";lockFont.querySelector("b").textContent=fontLocked?"Locked":"Unlocked";colourStatus.textContent=colourLocked?"Locked":reduced?"Manual":"Auto · 3 sec";fontStatus.textContent=fontLocked?"Locked":reduced?"Manual":"Auto · 3 sec";lockBoth.textContent=colourLocked&&fontLocked?"Unlock Both":"Lock Both"}
themes.forEach((t,i)=>{const b=document.createElement("button");b.type="button";b.style.setProperty("--swatch",t.accent);b.title=t.name;b.onclick=()=>{themeIndex=i;applyTheme(t,i)};swatches.appendChild(b)});
applyTheme(themes[0],0);applyFont(fonts[0]);resolveMode("light");updateLocks();
if(!reduced){setInterval(()=>{if(!colourLocked){themeIndex=(themeIndex+1)%themes.length;applyTheme(themes[themeIndex],themeIndex)}},3000);setInterval(()=>{if(!fontLocked){fontIndex=(fontIndex+1)%fonts.length;applyFont(fonts[fontIndex])}},3000)}
customColour.oninput=e=>applyTheme({accent:e.target.value.toUpperCase(),soft:mix(e.target.value)});customHex.onchange=e=>{let v=e.target.value.trim();if(!v.startsWith("#"))v="#"+v;hexToRgb(v)?applyTheme({accent:v.toUpperCase(),soft:mix(v)}):e.target.value=getComputedStyle(root).getPropertyValue("--accent").trim()};fontSelect.onchange=e=>{fontIndex=Math.max(0,fonts.indexOf(e.target.value));applyFont(e.target.value)};lockColour.onclick=()=>{colourLocked=!colourLocked;updateLocks()};lockFont.onclick=()=>{fontLocked=!fontLocked;updateLocks()};lockBoth.onclick=()=>{const v=!(colourLocked&&fontLocked);colourLocked=v;fontLocked=v;updateLocks()};document.querySelectorAll("[data-mode-value]").forEach(b=>b.onclick=()=>{selectedMode=b.dataset.modeValue;resolveMode(selectedMode)});systemDark.onchange=()=>selectedMode==="system"&&resolveMode("system");document.getElementById("resetCustomizer").onclick=()=>{themeIndex=fontIndex=0;colourLocked=fontLocked=false;selectedMode="light";applyTheme(themes[0],0);applyFont(fonts[0]);resolveMode("light");updateLocks()};
const customizer=document.querySelector(".customizer"),customizerToggle=document.getElementById("customizerToggle");function setCustomizer(o){customizer.classList.toggle("open",o);customizerToggle.setAttribute("aria-expanded",o)}customizerToggle.onclick=()=>setCustomizer(!customizer.classList.contains("open"));document.getElementById("customizerClose").onclick=()=>setCustomizer(false);
const body=document.body,menuButton=document.getElementById("menuButton"),mainNav=document.getElementById("mainNav");menuButton.onclick=()=>{const o=mainNav.classList.toggle("open");body.classList.toggle("locked",o);menuButton.setAttribute("aria-expanded",o)};mainNav.querySelectorAll("a").forEach(a=>a.onclick=()=>{mainNav.classList.remove("open");body.classList.remove("locked")});
document.querySelectorAll(".reveal").forEach(el=>{if(reduced)el.classList.add("visible");else new IntersectionObserver((entries,obs)=>entries.forEach(e=>{if(e.isIntersecting){e.target.classList.add("visible");obs.unobserve(e.target)}}),{threshold:.1}).observe(el)});
const searchPanel=document.getElementById("searchPanel"),globalSearch=document.getElementById("globalSearch"),productSearch=document.getElementById("productSearch");
function openSearch(){searchPanel.hidden=false;globalSearch.focus()}function closeSearch(){searchPanel.hidden=true}
document.getElementById("searchToggle").onclick=openSearch;document.getElementById("mobileSearch").onclick=openSearch;document.getElementById("searchClose").onclick=closeSearch;
globalSearch.oninput=()=>{productSearch.value=globalSearch.value;renderProducts();document.getElementById("shop").scrollIntoView({behavior:reduced?"auto":"smooth"})};
const productGrid=document.getElementById("productGrid"),productEmpty=document.getElementById("productEmpty"),sortProducts=document.getElementById("sortProducts");
function renderProducts(){
const q=productSearch.value.trim().toLowerCase();
let list=products.filter(p=>(activeFilter==="All"||p.category===activeFilter||p.badge===activeFilter)&&(!q||`${p.name} ${p.category} ${p.concerns.join(" ")} ${p.description}`.toLowerCase().includes(q)));
if(sortProducts.value==="low")list.sort((a,b)=>a.price-b.price);if(sortProducts.value==="high")list.sort((a,b)=>b.price-a.price);if(sortProducts.value==="name")list.sort((a,b)=>a.name.localeCompare(b.name));
productGrid.innerHTML=list.map(p=>`
<article class="product-card" data-product-id="${p.id}">
<div class="product-image">
<img src="${p.image}" alt="${p.name}, fictional skincare product" loading="lazy">
${p.badge?`<span class="product-badge">${p.badge}</span>`:""}
<button class="wishlist-button ${wishlist.has(p.id)?"active":""}" type="button" data-wishlist="${p.id}" aria-label="Save ${p.name}">
<svg viewBox="0 0 24 24"><path d="M12 21S3 16 3 8a5 5 0 0 1 9-3 5 5 0 0 1 9 3c0 8-9 13-9 13Z"/></svg>
</button>
</div>
<div class="product-copy">
<small>${p.category.toUpperCase()} · ${p.size}</small>
<h3>${p.name}</h3>
<p>${p.description}</p>
<div class="rating">★★★★★ <span>Demo rating · not real</span></div>
<div class="product-price"><strong>${money(p.price)}<span>Fictional price</span></strong></div>
<div class="product-actions">
<button class="add-cart" type="button" data-add="${p.id}">Add to Cart</button>
<button class="view-product" type="button" data-view="${p.id}">View</button>
</div>
</div>
</article>`).join("");
productEmpty.hidden=list.length!==0;
bindProductButtons();
}
document.querySelectorAll("[data-filter]").forEach(b=>b.onclick=()=>{activeFilter=b.dataset.filter;document.querySelectorAll("[data-filter]").forEach(x=>x.classList.toggle("active",x===b));renderProducts()});
document.querySelectorAll("[data-scroll-filter]").forEach(a=>a.onclick=()=>{activeFilter=a.dataset.scrollFilter;document.querySelectorAll("[data-filter]").forEach(x=>x.classList.toggle("active",x.dataset.filter===activeFilter));renderProducts()});
productSearch.oninput=renderProducts;sortProducts.onchange=renderProducts;
function productById(id){return products.find(p=>p.id===id)}
function addToCart(id,qty=1){cart[id]=(cart[id]||0)+qty;updateCart();openCart()}
function toggleWishlist(id){wishlist.has(id)?wishlist.delete(id):wishlist.add(id);updateWishlist();renderProducts()}
function bindProductButtons(){
document.querySelectorAll("[data-add]").forEach(b=>b.onclick=()=>addToCart(b.dataset.add));
document.querySelectorAll("[data-view]").forEach(b=>b.onclick=()=>openProduct(b.dataset.view));
document.querySelectorAll("[data-wishlist]").forEach(b=>b.onclick=()=>toggleWishlist(b.dataset.wishlist));
}
document.querySelectorAll("[data-quick-add]").forEach(b=>b.onclick=()=>addToCart(b.dataset.quickAdd));
const cartDrawer=document.getElementById("cartDrawer"),wishlistDrawer=document.getElementById("wishlistDrawer");
function closeDrawers(){cartDrawer.classList.remove("open");wishlistDrawer.classList.remove("open");cartDrawer.setAttribute("aria-hidden","true");wishlistDrawer.setAttribute("aria-hidden","true");body.classList.remove("locked")}
function openCart(){wishlistDrawer.classList.remove("open");cartDrawer.classList.add("open");cartDrawer.setAttribute("aria-hidden","false");body.classList.add("locked")}
function openWishlist(){cartDrawer.classList.remove("open");wishlistDrawer.classList.add("open");wishlistDrawer.setAttribute("aria-hidden","false");body.classList.add("locked")}
document.getElementById("cartToggle").onclick=openCart;document.getElementById("mobileCart").onclick=openCart;document.getElementById("wishlistToggle").onclick=openWishlist;document.getElementById("mobileWishlist").onclick=openWishlist;document.getElementById("cartClose").onclick=closeDrawers;document.getElementById("wishlistClose").onclick=closeDrawers;
function updateCart(){
const entries=Object.entries(cart).filter(([,q])=>q>0),count=entries.reduce((s,[,q])=>s+q,0),subtotal=entries.reduce((s,[id,q])=>s+productById(id).price*q,0);
document.getElementById("cartCount").textContent=count;document.getElementById("mobileCartCount").textContent=count;document.getElementById("cartSubtotal").textContent=money(subtotal);
document.getElementById("cartEmpty").hidden=entries.length!==0;document.getElementById("checkoutButton").disabled=entries.length===0;
document.getElementById("cartItems").innerHTML=entries.map(([id,q])=>{const p=productById(id);return `<article class="drawer-line"><img src="${p.image}" alt="${p.name}"><div><h3>${p.name}</h3><p>${p.size} · ${money(p.price)}</p><div class="qty-control"><button type="button" data-qty-minus="${id}">−</button><span>${q}</span><button type="button" data-qty-plus="${id}">+</button></div></div><button class="remove-line" type="button" data-remove="${id}" aria-label="Remove ${p.name}">×</button></article>`}).join("");
document.querySelectorAll("[data-qty-minus]").forEach(b=>b.onclick=()=>{cart[b.dataset.qtyMinus]-=1;if(cart[b.dataset.qtyMinus]<=0)delete cart[b.dataset.qtyMinus];updateCart()});
document.querySelectorAll("[data-qty-plus]").forEach(b=>b.onclick=()=>{cart[b.dataset.qtyPlus]+=1;updateCart()});
document.querySelectorAll("[data-remove]").forEach(b=>b.onclick=()=>{delete cart[b.dataset.remove];updateCart()});
}
function updateWishlist(){
const ids=[...wishlist];document.getElementById("wishlistCount").textContent=ids.length;document.getElementById("wishlistEmpty").hidden=ids.length!==0;
document.getElementById("wishlistItems").innerHTML=ids.map(id=>{const p=productById(id);return `<article class="drawer-line"><img src="${p.image}" alt="${p.name}"><div><h3>${p.name}</h3><p>${p.category} · ${money(p.price)}</p><button class="button button-accent" type="button" data-wish-add="${id}">Add to Cart</button></div><button class="remove-line" type="button" data-wish-remove="${id}">×</button></article>`}).join("");
document.querySelectorAll("[data-wish-add]").forEach(b=>b.onclick=()=>addToCart(b.dataset.wishAdd));
document.querySelectorAll("[data-wish-remove]").forEach(b=>b.onclick=()=>toggleWishlist(b.dataset.wishRemove));
}
document.getElementById("checkoutButton").onclick=()=>{
const lines=Object.entries(cart).filter(([,q])=>q>0).map(([id,q])=>{const p=productById(id);return `${p.name} × ${q} = ${money(p.price*q)}`});
const total=Object.entries(cart).reduce((s,[id,q])=>s+productById(id).price*q,0);
const msg=["Hello Ajay, I am testing the Velune Skin ecommerce demo.","","Demo cart:",...lines,`Subtotal: ${money(total)}`,"","No product purchase is requested. I want information about an ecommerce website with cart and WhatsApp checkout."].join("\n");
open(`https://wa.me/919929562585?text=${encodeURIComponent(msg)}`,"_blank","noopener");
};
const productModal=document.getElementById("productModal");
function openProduct(id){currentProduct=productById(id);document.getElementById("productModalTitle").textContent=currentProduct.name;document.getElementById("productModalCategory").textContent=currentProduct.category;document.getElementById("productModalSize").textContent=currentProduct.size;document.getElementById("productModalPrice").textContent=money(currentProduct.price);document.getElementById("productModalDescription").textContent=currentProduct.description;productModal.querySelector(".product-modal-media").style.backgroundImage=`url("${currentProduct.image}")`;productModal.showModal();body.classList.add("locked")}
document.getElementById("productClose").onclick=()=>{productModal.close();body.classList.remove("locked")};productModal.onclick=e=>{if(e.target===productModal){productModal.close();body.classList.remove("locked")}};document.getElementById("modalAddCart").onclick=()=>{productModal.close();body.classList.remove("locked");addToCart(currentProduct.id)};document.getElementById("modalWishlist").onclick=()=>toggleWishlist(currentProduct.id);
const bundleProducts=document.getElementById("bundleProducts");
function renderBundle(){
const subset=products.slice(0,6);
bundleProducts.innerHTML=subset.map(p=>`<article class="bundle-card ${bundle.has(p.id)?"selected":""}"><img src="${p.image}" alt="${p.name}" loading="lazy"><h3>${p.name}</h3><p>${money(p.price)} · ${p.category}</p><button type="button" data-bundle="${p.id}">${bundle.has(p.id)?"Selected":"Select Product"}</button></article>`).join("");
const ids=[...bundle],total=ids.reduce((s,id)=>s+productById(id).price,0),discounted=ids.length===3?Math.round(total*.9):total;
document.getElementById("bundleCount").textContent=`${ids.length} of 3 selected`;document.getElementById("bundleItems").innerHTML=ids.length?ids.map(id=>`<div class="bundle-item"><span>${productById(id).name}</span><strong>${money(productById(id).price)}</strong></div>`).join(""):"<p>Select products to begin.</p>";document.getElementById("bundleTotal").textContent=money(discounted);document.getElementById("addBundle").disabled=ids.length!==3;
document.querySelectorAll("[data-bundle]").forEach(b=>b.onclick=()=>{if(bundle.has(b.dataset.bundle))bundle.delete(b.dataset.bundle);else if(bundle.size<3)bundle.add(b.dataset.bundle);renderBundle()});
}
document.getElementById("addBundle").onclick=()=>{[...bundle].forEach(id=>cart[id]=(cart[id]||0)+1);updateCart();openCart()};renderBundle();
const quizModal=document.getElementById("quizModal"),quizResult=document.getElementById("quizResult");
document.querySelectorAll("[data-open-quiz]").forEach(b=>b.onclick=()=>{quizModal.showModal();body.classList.add("locked")});document.getElementById("quizClose").onclick=()=>{quizModal.close();body.classList.remove("locked")};quizModal.onclick=e=>{if(e.target===quizModal){quizModal.close();body.classList.remove("locked")}};
document.getElementById("quizForm").onsubmit=e=>{
e.preventDefault();const d=new FormData(e.currentTarget),feel=d.get("feel"),goal=d.get("goal"),texture=d.get("texture");
if(feel.includes("Dry")||goal.includes("Comfort")){routineIds=["p1","p4","p6"];document.getElementById("quizResultTitle").textContent="Comfort Ritual"}else if(feel.includes("Oily")||texture.includes("gel")){routineIds=["p2","p5","p6"];document.getElementById("quizResultTitle").textContent="Lightweight Ritual"}else{routineIds=["p1","p3","p6"];document.getElementById("quizResultTitle").textContent="Daily Balance Ritual"}
document.getElementById("quizResultText").textContent=routineIds.map(id=>productById(id).name).join(" · ")+" · Fictional product recommendation only.";quizResult.hidden=false;
};
document.getElementById("addRoutine").onclick=()=>{routineIds.forEach(id=>cart[id]=(cart[id]||0)+1);updateCart();quizModal.close();body.classList.remove("locked");openCart()};
document.querySelectorAll("[data-cadence]").forEach(b=>b.onclick=()=>{cadence=b.dataset.cadence;document.querySelectorAll("[data-cadence]").forEach(x=>x.classList.toggle("active",x===b))});
function openSimple(eyebrow,title,text,message){document.getElementById("simpleEyebrow").textContent=eyebrow;document.getElementById("simpleTitle").textContent=title;document.getElementById("simpleText").textContent=text;document.getElementById("simpleLink").href=`https://wa.me/919929562585?text=${encodeURIComponent(message)}`;document.getElementById("simpleModal").showModal();body.classList.add("locked")}
document.getElementById("subscriptionDemo").onclick=()=>openSimple("SUBSCRIPTION FEATURE · DEMO",cadence,"No real subscription is created. Discuss recurring-order controls with AJAYNXT.",`Hello Ajay, I am viewing the Velune Skin demo. I want an ecommerce subscription feature with cadence: ${cadence}.`);
document.querySelectorAll("[data-open-business]").forEach(b=>b.onclick=()=>openSimple("ECOMMERCE PROJECT","Turn products into a complete store.","This enquiry opens WhatsApp with AJAYNXT.","Hello Ajay, I want a premium ecommerce website like the Velune Skin demo."));
document.getElementById("simpleClose").onclick=()=>{document.getElementById("simpleModal").close();body.classList.remove("locked")};document.getElementById("simpleModal").onclick=e=>{if(e.target===document.getElementById("simpleModal")){e.currentTarget.close();body.classList.remove("locked")}};
document.querySelectorAll(".faq-list button").forEach(b=>b.onclick=()=>{const o=b.getAttribute("aria-expanded")==="true";document.querySelectorAll(".faq-list button").forEach(x=>x.setAttribute("aria-expanded","false"));b.setAttribute("aria-expanded",String(!o))});
function setErr(input,msg){input.setAttribute("aria-invalid",String(Boolean(msg)));const s=input.closest("label")?.querySelector(".field-error");if(s)s.textContent=msg;return!msg}
function validate(i){if(i.required&&!i.value.trim())return setErr(i,"This field is required.");if(i.name==="phone"&&!/^[0-9+\s()-]{8,18}$/.test(i.value.trim()))return setErr(i,"Enter a valid phone number.");return setErr(i,"")}
function validForm(f){const fields=[...f.querySelectorAll("[required]")],ok=fields.map(validate).every(Boolean);if(!ok)fields.find(x=>x.getAttribute("aria-invalid")==="true")?.focus();return ok}
const priceForm=document.getElementById("priceForm");priceForm.onsubmit=e=>{e.preventDefault();if(!validForm(priceForm))return;const d=new FormData(priceForm),msg=["Hello Ajay, I want an ecommerce website price.","",`Name: ${d.get("name")}`,`Phone: ${d.get("phone")}`,`Brand / Business: ${d.get("business")||""}`,`Store Type: ${d.get("type")}`,`Project Details: ${d.get("message")||""}`,"","Reference: Velune Skin Ecommerce Demo"].join("\n");open(`https://wa.me/919929562585?text=${encodeURIComponent(msg)}`,"_blank","noopener")};
renderProducts();updateCart();updateWishlist();