Files
megacurcumin_website/js/main.js
T

1122 lines
45 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* CurcuVance BioSciences - Main Application Controller
* Handles interactive navigation, product catalog filters, dynamic modals,
* bioavailability comparison simulator, and B2B sample inquiry workflows.
*/
document.addEventListener("DOMContentLoaded", () => {
initCopyrightYear();
initHeader();
initMobileNav();
initBiotechConsole();
initFormulationFinder();
initPlatformTabs();
initProducts();
initBioSimulator();
initRDPipeline();
initModals();
initContactForms();
initFaqAccordion();
initScrollReveal();
initSmoothScrollFeatures();
});
/* -------------------------------------------------------------
* 0. Automatic Dynamic Copyright Year
* ------------------------------------------------------------- */
function initCopyrightYear() {
const year = new Date().getFullYear();
document.querySelectorAll(".current-year").forEach(el => {
el.textContent = year;
});
}
/* -------------------------------------------------------------
* 1. Header & Navigation Controller
* ------------------------------------------------------------- */
function initHeader() {
const header = document.querySelector(".site-header");
if (!header) return;
window.addEventListener("scroll", () => {
if (window.scrollY > 40) {
header.classList.add("scrolled");
} else {
header.classList.remove("scrolled");
}
}, { passive: true });
}
function initMobileNav() {
const toggleBtn = document.querySelector(".mobile-toggle");
const navMenu = document.querySelector(".nav-menu");
if (!toggleBtn || !navMenu) return;
toggleBtn.addEventListener("click", () => {
navMenu.classList.toggle("open");
const isOpen = navMenu.classList.contains("open");
toggleBtn.setAttribute("aria-expanded", isOpen);
toggleBtn.innerHTML = isOpen
? `<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><line x1="18" y1="6" x2="6" y2="18"></line><line x1="6" y1="6" x2="18" y2="18"></line></svg>`
: `<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><line x1="3" y1="12" x2="21" y2="12"></line><line x1="3" y1="6" x2="21" y2="6"></line><line x1="3" y1="18" x2="21" y2="18"></line></svg>`;
});
// Close when clicking nav link
navMenu.querySelectorAll(".nav-link").forEach(link => {
link.addEventListener("click", () => {
navMenu.classList.remove("open");
toggleBtn.setAttribute("aria-expanded", "false");
});
});
}
/* -------------------------------------------------------------
* 2. Product Catalog Filter, Search & Dynamic Rendering
* ------------------------------------------------------------- */
let currentProductCategory = "all";
let currentProductFormat = "all";
let currentProductSearch = "";
function initProducts() {
const container = document.getElementById("product-grid-target");
const tabContainer = document.getElementById("product-tabs-target");
const searchInput = document.getElementById("product-search-input");
const clearBtn = document.getElementById("product-search-clear");
const formatFilters = document.getElementById("product-format-filters");
if (!container || typeof SITE_DATA === "undefined") return;
// Render filter tabs if container exists
if (tabContainer && SITE_DATA.categories) {
tabContainer.innerHTML = SITE_DATA.categories.map((cat, idx) => `
<button class="tab-btn ${idx === 0 ? 'active' : ''}" data-category="${cat.id}">
${cat.name}
</button>
`).join("");
tabContainer.addEventListener("click", (e) => {
const btn = e.target.closest(".tab-btn");
if (!btn) return;
tabContainer.querySelectorAll(".tab-btn").forEach(b => b.classList.remove("active"));
btn.classList.add("active");
currentProductCategory = btn.getAttribute("data-category") || "all";
renderProductCards();
});
}
// Format filter buttons
if (formatFilters) {
formatFilters.addEventListener("click", (e) => {
const btn = e.target.closest(".format-filter-btn");
if (!btn) return;
formatFilters.querySelectorAll(".format-filter-btn").forEach(b => b.classList.remove("active"));
btn.classList.add("active");
currentProductFormat = btn.getAttribute("data-format") || "all";
renderProductCards();
});
}
// Live search input
if (searchInput) {
searchInput.addEventListener("input", (e) => {
currentProductSearch = e.target.value.trim().toLowerCase();
if (clearBtn) {
clearBtn.style.display = currentProductSearch ? "flex" : "none";
}
renderProductCards();
});
if (clearBtn) {
clearBtn.addEventListener("click", () => {
searchInput.value = "";
currentProductSearch = "";
clearBtn.style.display = "none";
searchInput.focus();
renderProductCards();
});
}
}
// Initial render
renderProductCards();
}
window.resetAllProductFilters = function() {
currentProductCategory = "all";
currentProductFormat = "all";
currentProductSearch = "";
const searchInput = document.getElementById("product-search-input");
const clearBtn = document.getElementById("product-search-clear");
if (searchInput) searchInput.value = "";
if (clearBtn) clearBtn.style.display = "none";
const tabContainer = document.getElementById("product-tabs-target");
if (tabContainer) {
tabContainer.querySelectorAll(".tab-btn").forEach((b, i) => b.classList.toggle("active", i === 0));
}
const formatFilters = document.getElementById("product-format-filters");
if (formatFilters) {
formatFilters.querySelectorAll(".format-filter-btn").forEach((b, i) => b.classList.toggle("active", i === 0));
}
renderProductCards();
};
function renderProductCards() {
const container = document.getElementById("product-grid-target");
const countLabel = document.getElementById("product-results-count");
if (!container || typeof SITE_DATA === "undefined") return;
const totalCount = SITE_DATA.products.length;
const filtered = SITE_DATA.products.filter(prod => {
// 1. Category check
const matchesCat = currentProductCategory === "all" || prod.category === currentProductCategory;
if (!matchesCat) return false;
// 2. Format check
if (currentProductFormat !== "all") {
const formatsStr = (prod.dosageForms || []).join(" ").toLowerCase();
const targetFmt = currentProductFormat.toLowerCase();
if (!formatsStr.includes(targetFmt)) return false;
}
// 3. Search query check
if (currentProductSearch) {
const searchTarget = [
prod.name,
prod.tagline,
prod.shortDesc,
prod.fullDesc,
prod.actives,
prod.clinicalBenefit,
(prod.dosageForms || []).join(" ")
].join(" ").toLowerCase();
if (!searchTarget.includes(currentProductSearch)) return false;
}
return true;
});
// Update count badge if present
if (countLabel) {
if (filtered.length === totalCount && !currentProductSearch && currentProductFormat === "all" && currentProductCategory === "all") {
countLabel.textContent = `Showing all ${totalCount} formulations`;
} else if (filtered.length === 0) {
countLabel.textContent = "0 formulations match your search criteria";
} else {
countLabel.textContent = `Showing ${filtered.length} of ${totalCount} formulations`;
}
}
if (filtered.length === 0) {
container.innerHTML = `
<div style="grid-column: 1 / -1; text-align: center; padding: 4rem 1.5rem; background: #f8fafc; border-radius: 18px; border: 1.5px dashed #cbd5e1;">
<div style="width: 54px; height: 54px; border-radius: 50%; background: #e2e8f0; display: inline-flex; align-items: center; justify-content: center; margin-bottom: 1.25rem; color: #64748b;">
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg>
</div>
<h4 style="color: var(--primary-950); margin-bottom: 0.5rem; font-size: 1.2rem;">No Matching Formulations Found</h4>
<p style="color: #64748b; max-width: 480px; margin: 0 auto 1.5rem; font-size: 0.95rem; line-height: 1.6;">
No products matched your selected combination of category, dosage format, and search keywords.
</p>
<button type="button" class="btn btn-outline btn-sm" onclick="resetAllProductFilters()">Reset All Filters</button>
</div>
`;
return;
}
container.innerHTML = filtered.map(prod => `
<div class="product-card reveal active" data-id="${prod.id}">
<div class="product-card-top">
<div class="product-card-header">
<span class="product-card-category">${prod.categoryName}</span>
${prod.badge ? `<span class="product-card-badge">${prod.badge}</span>` : ''}
</div>
<h3>${prod.name}</h3>
<div class="product-tagline">${prod.tagline}</div>
<p class="product-desc">${prod.shortDesc}</p>
<div class="product-actives-box">
<div class="product-actives-header">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
<path d="M10 2v7.31M14 9.3V1.99M8.5 2h7M14 9.3a6.5 6.5 0 1 1-4 0"/>
</svg>
Standardized Bio-Actives
</div>
<div class="product-actives-val">${prod.actives}</div>
</div>
${prod.dosageForms && prod.dosageForms.length ? `
<div class="product-formats-row">
<span class="format-label">Formats:</span>
${prod.dosageForms.map(fmt => `<span class="format-pill">${fmt}</span>`).join('')}
</div>
` : ''}
</div>
<div class="product-card-footer">
<button class="view-product-detail-btn" data-id="${prod.id}" title="View technical specs for ${prod.name}">
<span>Learn More</span>
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M5 12h14M12 5l7 7-7 7"/></svg>
</button>
<button class="request-sample-btn" data-id="${prod.id}" data-name="${prod.name}" title="Request sample kit of ${prod.name}">
<span>Request Sample</span>
</button>
</div>
</div>
`).join("");
// Attach handlers
container.querySelectorAll(".view-product-detail-btn").forEach(btn => {
btn.addEventListener("click", () => {
const prodId = btn.getAttribute("data-id");
openProductDetailModal(prodId);
});
});
container.querySelectorAll(".request-sample-btn").forEach(btn => {
btn.addEventListener("click", () => {
const prodName = btn.getAttribute("data-name");
openSampleModal(prodName);
});
});
}
/* -------------------------------------------------------------
* 3. Interactive Bioavailability Simulator
* ------------------------------------------------------------- */
function initBioSimulator() {
const slider = document.getElementById("bio-time-slider");
const timeLabel = document.getElementById("bio-time-label");
const curcFill = document.getElementById("curcuvance-fill-bar");
const stdFill = document.getElementById("standard-fill-bar");
const curcConc = document.getElementById("curcuvance-conc-val");
const stdConc = document.getElementById("standard-conc-val");
if (!slider) return;
// Pharmacokinetic curves simulation model (ng/mL)
const calculateConcentration = (hours) => {
// Standard 95% curcumin (peaks fast at 1-2h around 8-12 ng/mL and clears fast)
const std = Math.max(0.5, Math.round((14 * Math.exp(-0.45 * Math.abs(hours - 1.5))) * 10) / 10);
// CurcuVance 29X (peaks at 3h at ~320 ng/mL and stays elevated > 8 hours)
const curc = Math.max(12, Math.round((340 * Math.exp(-0.18 * Math.abs(hours - 2.8))) * 10) / 10);
return { curc, std };
};
const updateDisplay = (hours) => {
if (timeLabel) timeLabel.textContent = `${hours} Hours Post-Dose`;
const { curc, std } = calculateConcentration(hours);
if (curcFill) curcFill.style.width = `${Math.min(100, Math.max(10, (curc / 340) * 100))}%`;
if (stdFill) stdFill.style.width = `${Math.min(100, Math.max(4, (std / 340) * 100))}%`;
if (curcConc) curcConc.textContent = `${curc} ng/mL (Free Curcumin)`;
if (stdConc) stdConc.textContent = `${std} ng/mL (Standard)`;
};
slider.addEventListener("input", (e) => {
updateDisplay(parseFloat(e.target.value));
});
// Init
updateDisplay(3);
}
/* -------------------------------------------------------------
* 4. Modals Controller & Auto-Injector
* ------------------------------------------------------------- */
function ensureGlobalModalsExist() {
if (!document.getElementById("b2b-sample-modal")) {
const modalHtml = `
<div class="modal-overlay" id="b2b-sample-modal" role="dialog" aria-modal="true">
<div class="modal-container">
<div class="modal-header">
<div>
<h3>Request Technical Sample & Dossier</h3>
<p style="font-size: 0.85rem; color: var(--neutral-600); margin-bottom: 0;">Cure Select Healthcare — MegaCurcumin™ Evaluation</p>
</div>
<button class="modal-close-btn" aria-label="Close modal">&times;</button>
</div>
<div class="modal-body">
<form class="ajax-form">
<div class="form-row">
<div class="form-group">
<label class="form-label">Full Name *</label>
<input type="text" class="form-input" placeholder="Your Name" required>
</div>
<div class="form-group">
<label class="form-label">Company Name *</label>
<input type="text" class="form-input" placeholder="Company Name" required>
</div>
</div>
<div class="form-row">
<div class="form-group">
<label class="form-label">Work Email *</label>
<input type="email" class="form-input" placeholder="name@company.com" required>
</div>
<div class="form-group">
<label class="form-label">Phone / WhatsApp</label>
<input type="tel" class="form-input" placeholder="+91 98765 43210">
</div>
</div>
<div class="form-row">
<div class="form-group">
<label class="form-label">Country of Operation *</label>
<input type="text" class="form-input" placeholder="Country" required>
</div>
<div class="form-group">
<label class="form-label">Specific Product / Technology of Interest</label>
<input type="text" id="sample-product-interest" class="form-input" placeholder="e.g. MegaCurcumin™ Joint or Micellar Form">
</div>
</div>
<div class="form-group">
<label class="form-label">Intended Formulation Format</label>
<select class="form-select">
<option value="Capsules/Tablets">Two-Piece Capsules / Direct Compression Tablets</option>
<option value="RTD Beverages">Ready-to-Drink (RTD) Clear Beverages / Liquid Shots</option>
<option value="Gummies">Functional Gummy Confections</option>
<option value="Powders/Stickpacks">Instant Dissolving Sachets / Effervescent Powders</option>
<option value="Evaluation">General Technical Benchmarking / R&D Evaluation</option>
</select>
</div>
<div class="form-group">
<label class="form-label">Project Details / Sample Quantity Requirements</label>
<textarea class="form-textarea" placeholder="Provide details on your project timeline, target health claims, or desired sample size (100g – 500g)..."></textarea>
</div>
<button type="submit" class="btn btn-gold btn-lg" style="width: 100%;">
Submit Sample & Technical Dossier Request
</button>
</form>
</div>
</div>
</div>
`;
document.body.insertAdjacentHTML("beforeend", modalHtml);
}
if (!document.getElementById("product-detail-modal")) {
const detailModalHtml = `
<div class="modal-overlay" id="product-detail-modal" role="dialog" aria-modal="true">
<div class="modal-container">
<div class="modal-header">
<h3 id="modal-prod-title">Product Details</h3>
<button class="modal-close-btn" aria-label="Close modal">&times;</button>
</div>
<div class="modal-body" id="modal-prod-body"></div>
</div>
</div>
`;
document.body.insertAdjacentHTML("beforeend", detailModalHtml);
}
}
function initModals() {
ensureGlobalModalsExist();
// Global modal closes
document.querySelectorAll(".modal-close-btn, .modal-overlay").forEach(el => {
el.addEventListener("click", (e) => {
if (e.target === el || el.classList.contains("modal-close-btn")) {
closeAllModals();
}
});
});
// Global "Partner With Us" or "Request Sample" clicks
document.querySelectorAll("[data-open-modal='partner']").forEach(btn => {
btn.addEventListener("click", (e) => {
e.preventDefault();
openSampleModal();
});
});
}
function openProductDetailModal(productId) {
ensureGlobalModalsExist();
if (typeof SITE_DATA === "undefined") return;
const prod = SITE_DATA.products.find(p => p.id === productId);
if (!prod) return;
const modal = document.getElementById("product-detail-modal");
const modalTitle = document.getElementById("modal-prod-title");
const modalBody = document.getElementById("modal-prod-body");
if (!modal || !modalTitle || !modalBody) return;
modalTitle.innerHTML = `${prod.name} <span class="badge badge-gold" style="vertical-align: middle; margin-left: 8px;">${prod.badge || 'Formulation'}</span>`;
modalBody.innerHTML = `
<div style="margin-bottom: 1.5rem;">
<p class="lead" style="color: var(--primary-800); font-weight: 600;">${prod.tagline}</p>
<p style="font-size: 0.95rem; color: var(--neutral-700); line-height: 1.7;">${prod.fullDesc}</p>
</div>
<div style="background: var(--neutral-50); border-radius: var(--radius-md); padding: 1.25rem; margin-bottom: 1.5rem; border-left: 4px solid var(--accent-500);">
<h4 style="font-size: 0.95rem; text-transform: uppercase; letter-spacing: 0.05em; color: var(--neutral-900); margin-bottom: 0.5rem;">Clinical & Pharmacological Benefits</h4>
<p style="font-size: 0.925rem; color: var(--neutral-700); margin-bottom: 0;">${prod.clinicalBenefit}</p>
</div>
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 1rem; margin-bottom: 1.75rem;">
<div style="background: var(--white); border: 1px solid var(--neutral-200); padding: 1rem; border-radius: var(--radius-sm);">
<span style="font-size: 0.75rem; text-transform: uppercase; color: var(--neutral-500); display: block;">Standardized Active Assay</span>
<strong style="color: var(--neutral-900); font-size: 0.9rem;">${prod.specs.purity || 'USP Standardized'}</strong>
</div>
<div style="background: var(--white); border: 1px solid var(--neutral-200); padding: 1rem; border-radius: var(--radius-sm);">
<span style="font-size: 0.75rem; text-transform: uppercase; color: var(--neutral-500); display: block;">Solubility & Matrix</span>
<strong style="color: var(--neutral-900); font-size: 0.9rem;">${prod.specs.solubility || 'Standard Extract'}</strong>
</div>
<div style="background: var(--white); border: 1px solid var(--neutral-200); padding: 1rem; border-radius: var(--radius-sm);">
<span style="font-size: 0.75rem; text-transform: uppercase; color: var(--neutral-500); display: block;">Documented Shelf-Life</span>
<strong style="color: var(--neutral-900); font-size: 0.9rem;">${prod.specs.shelfLife || '36 Months'}</strong>
</div>
</div>
<div style="margin-bottom: 2rem;">
<span style="font-size: 0.8rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.05em; color: var(--neutral-600); display: block; margin-bottom: 0.5rem;">Recommended Dosage Formats:</span>
<div style="display: flex; flex-wrap: wrap; gap: 0.5rem;">
${prod.dosageForms.map(f => `<span class="badge badge-green" style="font-size: 0.8rem;">${f}</span>`).join("")}
</div>
</div>
<div style="display: flex; gap: 1rem; justify-content: flex-end;">
<button class="btn btn-outline modal-close-btn">Close</button>
<button class="btn btn-gold modal-request-sample-trigger" data-name="${prod.name}">
Request Technical Dossier & Sample
</button>
</div>
`;
// Attach sample button inside modal
const sampleTrigger = modalBody.querySelector(".modal-request-sample-trigger");
if (sampleTrigger) {
sampleTrigger.addEventListener("click", () => {
closeAllModals();
openSampleModal(prod.name);
});
}
// Re-bind close buttons inside modal
modal.querySelectorAll(".modal-close-btn").forEach(btn => {
btn.addEventListener("click", closeAllModals);
});
modal.classList.add("open");
}
function openSampleModal(prefillProduct = "") {
ensureGlobalModalsExist();
const modal = document.getElementById("b2b-sample-modal");
if (!modal) return;
const productField = modal.querySelector("#sample-product-interest");
if (productField && prefillProduct) {
productField.value = prefillProduct;
}
// Re-bind close buttons inside modal
modal.querySelectorAll(".modal-close-btn").forEach(btn => {
btn.addEventListener("click", closeAllModals);
});
// Re-bind form submission inside dynamic modal
const dynamicForm = modal.querySelector("form.ajax-form");
if (dynamicForm && !dynamicForm.dataset.bound) {
bindAjaxForm(dynamicForm);
dynamicForm.dataset.bound = "true";
}
modal.classList.add("open");
}
function closeAllModals() {
document.querySelectorAll(".modal-overlay").forEach(m => m.classList.remove("open"));
}
/* -------------------------------------------------------------
* 5. Toast Notification System
* ------------------------------------------------------------- */
function showToast(message, type = "success") {
let container = document.querySelector(".toast-container");
if (!container) {
container = document.createElement("div");
container.className = "toast-container";
document.body.appendChild(container);
}
const toast = document.createElement("div");
toast.className = `toast-item toast-${type}`;
const iconSvg = type === "success"
? `<svg class="toast-icon" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><polyline points="20 6 9 17 4 12"/></svg>`
: `<svg class="toast-icon" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><circle cx="12" cy="12" r="10"/><line x1="12" y1="8" x2="12" y2="12"/><line x1="12" y1="16" x2="12.01" y2="16"/></svg>`;
toast.innerHTML = `
${iconSvg}
<span>${message}</span>
<button class="toast-close" aria-label="Dismiss">&times;</button>
`;
container.appendChild(toast);
requestAnimationFrame(() => toast.classList.add("toast-visible"));
const removeToast = () => {
toast.classList.remove("toast-visible");
setTimeout(() => toast.remove(), 300);
};
toast.querySelector(".toast-close").addEventListener("click", removeToast);
setTimeout(removeToast, 5000);
}
/* -------------------------------------------------------------
* 6. Contact & Sample Form Submission Handler
* ------------------------------------------------------------- */
function initContactForms() {
// Handle prefill from query params (e.g. ?interest=...)
if (window.location.search) {
const urlParams = new URLSearchParams(window.location.search);
const interestParam = urlParams.get("interest");
if (interestParam) {
const interestSelect = document.getElementById("form-interest");
const messageField = document.querySelector("textarea.form-textarea");
if (interestSelect) {
Array.from(interestSelect.options).forEach(opt => {
if (opt.text.toLowerCase().includes(interestParam.toLowerCase())) {
interestSelect.value = opt.value;
}
});
}
if (messageField && !messageField.value) {
messageField.value = `Requesting technical dossier & sample kit for: ${interestParam}\n`;
}
}
}
const forms = document.querySelectorAll("form.ajax-form");
forms.forEach(form => {
if (!form.dataset.bound) {
bindAjaxForm(form);
form.dataset.bound = "true";
}
});
}
function bindAjaxForm(form) {
form.addEventListener("submit", (e) => {
e.preventDefault();
const submitBtn = form.querySelector("button[type='submit']");
const originalText = submitBtn ? submitBtn.innerHTML : "Submit";
if (submitBtn) {
submitBtn.disabled = true;
submitBtn.innerHTML = `
<svg class="animate-spin" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" style="animation: spin 1s linear infinite;"><circle cx="12" cy="12" r="10" stroke-opacity="0.25"></circle><path d="M4 12a8 8 0 018-8"></path></svg>
Processing Request...
`;
}
setTimeout(() => {
showToast("Request submitted successfully! Our team will contact you within 24 hours.", "success");
form.innerHTML = `
<div style="text-align: center; padding: 2.5rem 1rem;">
<div style="width: 60px; height: 60px; background: var(--primary-100); color: var(--primary-700); border-radius: 50%; display: inline-flex; align-items: center; justify-content: center; margin-bottom: 1.25rem;">
<svg width="28" height="28" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><polyline points="20 6 9 17 4 12"/></svg>
</div>
<h3 style="color: var(--primary-900); margin-bottom: 0.75rem;">Thank You for Your Enquiry</h3>
<p style="color: var(--neutral-600); max-width: 480px; margin: 0 auto 1.5rem; font-size: 0.95rem; line-height: 1.6;">
Your technical request has been routed to our B2B commercial & formulation team. A dedicated technical account specialist will contact you within 24 business hours with specifications and tracking for sample dispatch.
</p>
<button type="button" class="btn btn-outline btn-sm modal-close-btn" onclick="closeAllModals()">Close Window</button>
</div>
`;
}, 900);
});
}
/* -------------------------------------------------------------
* 7. FAQ Accordion Controller
* ------------------------------------------------------------- */
function initFaqAccordion() {
const items = document.querySelectorAll(".faq-accordion-item");
if (!items.length) return;
items.forEach(item => {
const trigger = item.querySelector(".faq-accordion-trigger");
const content = item.querySelector(".faq-accordion-content");
if (!trigger || !content) return;
trigger.addEventListener("click", () => {
const isActive = item.classList.contains("active");
// Close other accordion items
items.forEach(other => {
if (other !== item && other.classList.contains("active")) {
other.classList.remove("active");
const otherTrigger = other.querySelector(".faq-accordion-trigger");
const otherContent = other.querySelector(".faq-accordion-content");
if (otherTrigger) otherTrigger.setAttribute("aria-expanded", "false");
if (otherContent) otherContent.style.maxHeight = "0";
}
});
if (isActive) {
item.classList.remove("active");
trigger.setAttribute("aria-expanded", "false");
content.style.maxHeight = "0";
} else {
item.classList.add("active");
trigger.setAttribute("aria-expanded", "true");
content.style.maxHeight = content.scrollHeight + 30 + "px";
}
});
});
}
/* -------------------------------------------------------------
* 6. Scroll Reveal Observer
* ------------------------------------------------------------- */
function initScrollReveal() {
const reveals = document.querySelectorAll(".reveal");
if (!reveals.length || !("IntersectionObserver" in window)) return;
const observer = new IntersectionObserver((entries, obs) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
entry.target.classList.add("active");
obs.unobserve(entry.target);
}
});
}, { threshold: 0.12 });
reveals.forEach(el => observer.observe(el));
}
/* -------------------------------------------------------------
* 7. Modern Platform Tabs Filter (Legacy Compatibility)
* ------------------------------------------------------------- */
function initPlatformTabs() {
const tabBtns = document.querySelectorAll(".modern-tab-btn");
const cards = document.querySelectorAll(".cinematic-card, .modern-formulation-card");
if (!tabBtns.length || !cards.length) return;
tabBtns.forEach(btn => {
btn.addEventListener("click", () => {
tabBtns.forEach(b => b.classList.remove("active"));
btn.classList.add("active");
const targetPlatform = btn.getAttribute("data-platform");
cards.forEach(card => {
const platform = card.getAttribute("data-platform-card");
if (targetPlatform === "all" || targetPlatform === platform) {
card.style.display = "flex";
card.style.opacity = "1";
card.style.animation = "fadeIn 0.4s ease forwards";
} else {
card.style.display = "none";
card.style.opacity = "0";
}
});
});
});
}
/* -------------------------------------------------------------
* 8. Next-Gen Biotech Master Console Controller
* ------------------------------------------------------------- */
function initBiotechConsole() {
const navDeck = document.querySelector(".biotech-nav-deck");
if (!navDeck) return;
const navItems = navDeck.querySelectorAll(".biotech-nav-item");
const stagePanels = document.querySelectorAll(".biotech-stage-panel");
navItems.forEach(item => {
item.addEventListener("click", () => {
const targetStageId = item.getAttribute("data-target-stage");
if (!targetStageId) return;
// Update Nav active state
navItems.forEach(btn => {
btn.classList.remove("active");
btn.setAttribute("aria-selected", "false");
});
item.classList.add("active");
item.setAttribute("aria-selected", "true");
// Switch stage panels
stagePanels.forEach(panel => {
if (panel.id === targetStageId) {
panel.classList.add("active");
} else {
panel.classList.remove("active");
}
});
});
});
}
/* -------------------------------------------------------------
* 9. Formulation Navigator (Interactive Recommender Widget)
* ------------------------------------------------------------- */
function initFormulationFinder() {
const recomBtns = document.querySelectorAll(".app-recom-btn");
if (!recomBtns.length) return;
recomBtns.forEach(btn => {
btn.addEventListener("click", () => {
recomBtns.forEach(b => b.classList.remove("active"));
btn.classList.add("active");
const targetStage = btn.getAttribute("data-recom-platform");
if (!targetStage) return;
// Activate corresponding console nav item
const matchingNavItem = document.querySelector(`.biotech-nav-item[data-target-stage="${targetStage}"]`);
if (matchingNavItem) {
matchingNavItem.click();
}
// Smooth scroll to console
const consoleElem = document.getElementById("biotech-master-console");
if (consoleElem) {
const yOffset = -90;
const y = consoleElem.getBoundingClientRect().top + window.pageYOffset + yOffset;
window.scrollTo({ top: y, behavior: "smooth" });
}
});
});
}
/* -------------------------------------------------------------
* 10. Non-Card Concept-to-Commercialization R&D Pipeline Controller
* ------------------------------------------------------------- */
function initRDPipeline() {
const hub = document.querySelector(".rd-pipeline-hub");
if (!hub) return;
const rows = hub.querySelectorAll(".rd-stage-row");
const stageIdEl = document.getElementById("rd-monitor-stage-id");
const titleEl = document.getElementById("rd-monitor-title");
const descEl = document.getElementById("rd-monitor-desc");
const metricValEl = document.getElementById("rd-monitor-metric-val");
const metricLabelEl = document.getElementById("rd-monitor-metric-label");
const deliverablesEl = document.getElementById("rd-monitor-deliverables");
const protocolEl = document.getElementById("rd-monitor-protocol");
const STAGE_DATA = {
"1": {
id: "PHASE 01 / 08",
title: "Ingredient Research",
desc: "Screening and agronomic optimization of premium Curcuma longa botanicals. Selecting high-altitude rhizomes with optimal curcuminoid-to-turmerone ratios and certified pesticide-free agronomic pedigree.",
metricVal: "100%",
metricLabel: "Non-GMO Curcuma Longa Agronomy",
deliverables: [
"Botanical Cultivar Genetic Fingerprinting",
"Raw Biomass High-Resolution Assay",
"Pesticide & Heavy Metal Safety Screening"
],
protocol: "Screened in accordance with <strong>USP &lt;561&gt;</strong> botanical quality standards."
},
"2": {
id: "PHASE 02 / 08",
title: "Botanical Research",
desc: "Phytochemical extraction and standardization of curcuminoids and essential turmerones. Targeting exact ratios of Curcumin, DMC, BDMC, and volatile ar-turmerones via clean solvent-free technologies.",
metricVal: "95.0%",
metricLabel: "Total Standardized Curcuminoids",
deliverables: [
"HPLC-UV Chromatographic Profiling",
"Essential Turmerone Volatile Fractionation",
"Zero Petrochemical Solvent Residue"
],
protocol: "Assayed using validated <strong>HPLC &amp; LC-MS/MS</strong> quantification methodologies."
},
"3": {
id: "PHASE 03 / 08",
title: "Product Formulation",
desc: "Formulation engineering for capsules, tablets, clear beverages, and functional gummies. Optimizing physical flow, dissolution kinetics, and taste profiles across varied dietary formats.",
metricVal: "4 Formats",
metricLabel: "Capsules, RTD Liquids, Tablets, Gummies",
deliverables: [
"Automated Encapsulation Flow Compatibility",
"Aqueous Clarity in Functional Liquids",
"Gummy Thermal Cooking Tolerance"
],
protocol: "Engineered to satisfy <strong>USP &lt;2040&gt;</strong> disintegration &amp; dissolution guidelines."
},
"4": {
id: "PHASE 04 / 08",
title: "Delivery Technologies",
desc: "Micellar encapsulation, phytosome complexes, and cyclodextrin dispersions. Overcoming native lipophilic crystallization by engineering hydrophilic protective shells and phospholipid chaperones.",
metricVal: "&lt; 100nm",
metricLabel: "Sub-Micron Particle Dispersal",
deliverables: [
"Polar-Nonpolar Micellar Surfactant Wrapping",
"Phosphatidylcholine Cellular Bilayer Chaperones",
"Gamma-Cyclodextrin Supramolecular Cavities"
],
protocol: "Particle size distributions verified via <strong>Dynamic Light Scattering (DLS)</strong>."
},
"5": {
id: "PHASE 05 / 08",
title: "Bioavailability Enhancement",
desc: "Proven 29x enhancement in free curcumin plasma concentration over standard powder. Bypasses first-pass phase-II hepatic glucuronidation to deliver active un-metabolized molecules to target tissues.",
metricVal: "29X",
metricLabel: "Higher Free Curcumin Plasma AUC",
deliverables: [
"8-Hour Sustained Plasma Half-Life",
"Overcoming 11 ng/mL Water Solubility Barrier",
"Elimination of Synthetic Bio-Enhancers (Piperine)"
],
protocol: "Documented via randomized cross-over <strong>Human Clinical Pharmacokinetics</strong>."
},
"6": {
id: "PHASE 06 / 08",
title: "Stability Evaluation",
desc: "ICH stability chambers ensuring active ingredient shelf-life integrity over 36 months. Testing accelerated and long-term photolytic, thermal, and moisture degradation resistance.",
metricVal: "36 Mo",
metricLabel: "Documented Active Shelf-Life",
deliverables: [
"Zone IVb Tropical Accelerated Stress Testing",
"Photostability Resistance Under High Lux Light",
"Zero Physical Precipitation or Hard-Caking"
],
protocol: "Validated strictly per <strong>ICH Q1A(R2) &amp; Q1B</strong> stability guidelines."
},
"7": {
id: "PHASE 07 / 08",
title: "Scientific Validation",
desc: "Pharmacokinetic assays and clinical trial evidence backing health benefits. Collaborating with clinical research organizations to evaluate inflammatory modulation, joint mobility, and endothelial function.",
metricVal: "45+",
metricLabel: "Published Clinical Research Papers",
deliverables: [
"Randomized Placebo-Controlled Human Trials",
"WOMAC Joint Comfort Score Validations",
"Endothelial Flow-Mediated Dilation Metrics"
],
protocol: "All trials conducted under <strong>Good Clinical Practice (GCP) &amp; Helsinki Declaration</strong>."
},
"8": {
id: "PHASE 08 / 08",
title: "Commercialization Support",
desc: "Pilot-scale testing, industrial scale-up, and turnkey batch consistency. From kilogram bench batches to multi-ton commercial production with comprehensive regulatory documentation.",
metricVal: "Turnkey",
metricLabel: "Seamless Manufacturing Integration",
deliverables: [
"Full Master Batch Manufacturing Records (BMR)",
"Global Regulatory Dossiers &amp; Halal/Kosher Specs",
"Guaranteed Lot-to-Lot HPLC Assay Consistency"
],
protocol: "Manufactured in certified <strong>cGMP, ISO 9001:2015 &amp; ISO 22000</strong> facilities."
}
};
const updateStage = (stageNum) => {
const data = STAGE_DATA[stageNum];
if (!data) return;
rows.forEach(r => {
r.classList.toggle("active", r.getAttribute("data-stage") === String(stageNum));
});
if (stageIdEl) stageIdEl.textContent = data.id;
if (titleEl) titleEl.textContent = data.title;
if (descEl) descEl.textContent = data.desc;
if (metricValEl) metricValEl.textContent = data.metricVal;
if (metricLabelEl) metricLabelEl.textContent = data.metricLabel;
if (protocolEl) {
protocolEl.innerHTML = `
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"></path></svg>
<div>${data.protocol}</div>
`;
}
if (deliverablesEl) {
deliverablesEl.innerHTML = data.deliverables.map(item => `
<li>
<div style="display: flex; align-items: center; gap: 8px;">
<span class="deliverable-laser-dot"></span>
<span>${item}</span>
</div>
<span class="deliverable-status-tag">VERIFIED</span>
</li>
`).join("");
}
};
rows.forEach(row => {
row.addEventListener("click", () => {
const stage = row.getAttribute("data-stage");
updateStage(stage);
});
row.addEventListener("mouseenter", () => {
const stage = row.getAttribute("data-stage");
updateStage(stage);
});
});
// Initial load
updateStage(1);
}
/* -------------------------------------------------------------
* 10. Smooth Scroll Features, Scroll Progress & Floating HUD
* ------------------------------------------------------------- */
function initSmoothScrollFeatures() {
// 1. Create Top Reading Scroll Progress Bar
let progressTrack = document.querySelector(".scroll-progress-track");
if (!progressTrack) {
progressTrack = document.createElement("div");
progressTrack.className = "scroll-progress-track";
progressTrack.innerHTML = '<div class="scroll-progress-bar" id="scroll-progress-bar"></div>';
document.body.prepend(progressTrack);
}
const progressBar = document.getElementById("scroll-progress-bar");
// 2. Create Floating Back-to-Top Button with Circular Meter
let backToTopBtn = document.querySelector(".back-to-top-btn");
const radius = 20;
const circumference = 2 * Math.PI * radius; // ~125.66
if (!backToTopBtn) {
backToTopBtn = document.createElement("button");
backToTopBtn.className = "back-to-top-btn";
backToTopBtn.setAttribute("aria-label", "Back to top");
backToTopBtn.setAttribute("title", "Scroll back to top");
backToTopBtn.innerHTML = `
<svg class="back-to-top-progress-ring" viewBox="0 0 48 48">
<circle class="back-to-top-progress-circle" cx="24" cy="24" r="${radius}"
stroke-dasharray="${circumference}" stroke-dashoffset="${circumference}" />
</svg>
<svg class="icon-arrow" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
<polyline points="18 15 12 9 6 15"></polyline>
</svg>
`;
document.body.appendChild(backToTopBtn);
backToTopBtn.addEventListener("click", () => {
window.scrollTo({
top: 0,
behavior: "smooth"
});
});
}
const circle = backToTopBtn ? backToTopBtn.querySelector(".back-to-top-progress-circle") : null;
// Scroll event listener for Progress Bar & Back-to-Top Button
const handleScrollMetrics = () => {
const scrollTop = window.pageYOffset || document.documentElement.scrollTop;
const scrollHeight = document.documentElement.scrollHeight - document.documentElement.clientHeight;
const progress = scrollHeight > 0 ? (scrollTop / scrollHeight) : 0;
// Update top progress bar
if (progressBar) {
progressBar.style.width = Math.min(progress * 100, 100) + "%";
}
// Update floating button visibility & circular ring
if (backToTopBtn) {
if (scrollTop > 350) {
backToTopBtn.classList.add("visible");
} else {
backToTopBtn.classList.remove("visible");
}
if (circle) {
const offset = circumference - (progress * circumference);
circle.style.strokeDashoffset = Math.max(0, offset);
}
}
};
window.addEventListener("scroll", handleScrollMetrics, { passive: true });
handleScrollMetrics();
// 3. Smooth Anchor Link Handler with Header Offset
document.addEventListener("click", (e) => {
const anchor = e.target.closest('a[href*="#"]');
if (!anchor) return;
const href = anchor.getAttribute("href");
if (!href || href === "#") return;
// Check if link is an in-page anchor
try {
const url = new URL(anchor.href, window.location.href);
if (url.pathname === window.location.pathname && url.hash) {
const target = document.querySelector(url.hash);
if (target) {
e.preventDefault();
const headerOffset = 84;
const targetPosition = target.getBoundingClientRect().top + window.pageYOffset - headerOffset;
window.scrollTo({
top: targetPosition,
behavior: "smooth"
});
if (window.history && window.history.pushState) {
window.history.pushState(null, null, url.hash);
}
}
}
} catch (err) {
// Fallback for non-standard links
}
});
// 4. Initial Hash Arrival Alignment
if (window.location.hash) {
setTimeout(() => {
try {
const target = document.querySelector(window.location.hash);
if (target) {
const headerOffset = 84;
const targetPosition = target.getBoundingClientRect().top + window.pageYOffset - headerOffset;
window.scrollTo({
top: targetPosition,
behavior: "smooth"
});
}
} catch (err) {}
}, 150);
}
// 5. ScrollSpy for Primary Sections
initScrollSpy();
}
function initScrollSpy() {
const sections = document.querySelectorAll("section[id]");
const navLinks = document.querySelectorAll(".nav-menu .nav-link");
if (!sections.length || !navLinks.length) return;
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const id = entry.target.getAttribute("id");
navLinks.forEach(link => {
const href = link.getAttribute("href");
if (href === `#${id}` || (href === "index.html" && id === "hero-section")) {
navLinks.forEach(l => l.classList.remove("active"));
link.classList.add("active");
}
});
}
});
}, {
rootMargin: "-20% 0px -70% 0px"
});
sections.forEach(sec => observer.observe(sec));
}