/* Shop product page — Vercel-style hero + tabs layout.
* Reads window.PRODUCT_SLUG → merges window.PEPTIDE_DB[slug] + window.SHOP_DB[slug].
* Per-peptide accent color from window.PEPTIDE_ACCENTS.
* Pulls window.PROTOCOL_DB to derive "Pairs well with" recommendations.
*/
const slug = window.PRODUCT_SLUG;
const peptideData = window.PEPTIDE_DB[slug];
const shopData = (window.SHOP_DB || {})[slug] || {};
const data = { ...peptideData, ...shopData };
if (!peptideData) {
document.getElementById("root").innerHTML =
'
';
}
const PRICING_TIERS = [
{ qty: 1, label: "1 month", multiplier: 1.0, savings: 0, badge: null },
{ qty: 3, label: "3 months", multiplier: 2.7, savings: 10, badge: "SAVE 10%" },
{ qty: 6, label: "6 months", multiplier: 5.1, savings: 15, badge: "BEST VALUE" },
];
const SLUG_TO_NAME = {
"sermorelin": "Sermorelin", "nad-plus": "NAD+", "bpc-157": "BPC-157",
"ipamorelin": "Ipamorelin", "ghk-cu": "GHK-Cu", "glutathione": "Glutathione",
"selank": "Selank", "dsip": "DSIP", "b12-mic": "B12 / MIC", "cjc-1295": "CJC-1295",
};
const NAME_TO_SLUG = Object.fromEntries(Object.entries(SLUG_TO_NAME).map(([k, v]) => [v, k]));
// Derive "pairs well with" from PROTOCOL_DB:
// any peptide that appears alongside this one in any protocol, ranked by frequency.
function getPairings(currentSlug) {
const protocols = window.PROTOCOL_DB || {};
const counts = {};
const protocolNames = {};
Object.values(protocols).forEach(p => {
const slugs = (p.stack || []).map(name => NAME_TO_SLUG[name]).filter(Boolean);
if (!slugs.includes(currentSlug)) return;
slugs.forEach(s => {
if (s === currentSlug) return;
counts[s] = (counts[s] || 0) + 1;
protocolNames[s] = [...(protocolNames[s] || []), p.name];
});
});
return Object.entries(counts)
.sort((a, b) => b[1] - a[1])
.slice(0, 3)
.map(([s, n]) => ({ slug: s, name: SLUG_TO_NAME[s], count: n, protocols: protocolNames[s] }));
}
/* ============ STAR ICON ============ */
const StarIcon = ({ size = 14, color = "currentColor" }) => (
);
/* ============ HERO ============ */
const Hero = ({ d, accent }) => {
const [tier, setTier] = React.useState(0);
const [subscribe, setSubscribe] = React.useState(true);
const [view, setView] = React.useState(0);
const opt = PRICING_TIERS[tier];
const baseTotal = Math.round(d.price * opt.multiplier);
const total = subscribe ? Math.round(baseTotal * 0.85) : baseTotal;
const perMonth = Math.round(total / opt.qty);
// Per-product palette (real product photo on colored backdrop) — pulled from brand.jsx PRODUCT_PALETTES
const pal = (window.PRODUCT_PALETTES || {})[d.name] || {};
// 3 thumbnail variants — same vial photo, slightly different backdrops
const stageVariants = [
{ bg: pal.bg, label: "★ BATCH 26-A15" },
{ bg: pal.cap || pal.bg, label: "WITH CARTON" },
{ bg: "#1a1a1a", label: "LYO · STERILE", dark: true },
];
const v = stageVariants[view];
return (
{/* Breadcrumb */}
{/* LEFT: real product shot */}
{v.label}
{/* Color-tinted overlay matching cap so each peptide reads visually distinct */}
{d.name}
{/* Thumbs */}
{stageVariants.map((vv, i) => (
setView(i)} className="shop-thumb" style={{
background: vv.bg,
borderColor: view === i ? "var(--ink)" : "var(--paper-3)",
borderWidth: view === i ? 2 : 1,
position: "relative",
overflow: "hidden",
}}>
))}
{/* RIGHT: buy box */}
★ MOST POPULAR
RX
{(d.class_label || "").split("(")[0].trim().toUpperCase()}
{d.name}
{d.class_label}
{d.hero_subtitle || d.tagline}
{[1,2,3,4,5].map(i => )}
4.9
{(1200 + (d.price * 7)).toLocaleString()} verified reviews
{d.one_liner}
{/* Pricing tier picker */}
How long you'll order
{PRICING_TIERS.map((o, i) => {
const t = subscribe ? Math.round(d.price * o.multiplier * 0.85) : Math.round(d.price * o.multiplier);
const pm = Math.round(t / o.qty);
return (
setTier(i)} className="shop-tier" style={{
borderColor: tier === i ? "var(--ink)" : "var(--paper-3)",
borderWidth: tier === i ? 2 : 1,
background: tier === i ? "var(--ink)" : "var(--paper)",
color: tier === i ? "var(--paper)" : "var(--ink)",
}}>
{o.badge && {o.badge} }
{o.label.toUpperCase()}
${pm}/mo
{o.qty > 1 && ${t} TOTAL
}
);
})}
{/* Subscribe banner */}
setSubscribe(s => !s)} style={{ background: subscribe ? "var(--ink)" : "var(--paper-2)", color: subscribe ? "var(--paper)" : "var(--ink)", borderColor: subscribe ? "var(--ink)" : "var(--paper-3)" }}>
SUBSCRIBE & SAVE 15%
Auto-ship monthly. Pause or cancel anytime.
{/* Price + CTA */}
${perMonth}/mo
{subscribe && ${Math.round(d.price * opt.multiplier / opt.qty)} }
{(d.dose_ladder?.[0]?.range || "").replace(/^Start:\s*/, "")} · per shipment
Start consultation →
First, explain it to me
A real U.S. clinician reviews your medical history before anything ships. 15-min online consult. From a 503A licensed pharmacy in 48 hours after approval.
);
};
/* ============ TABS ============ */
const Tabs = ({ d, accent }) => {
const [tab, setTab] = React.useState("what");
const tabs = [
{ id: "what", label: "What it does" },
{ id: "how", label: "How you use it" },
{ id: "pairs", label: "Pairs well with" },
{ id: "science", label: "The science" },
{ id: "faq", label: "Questions" },
];
return (
{tabs.map(t => (
setTab(t.id)} className="shop-tab" style={{
borderTopColor: tab === t.id ? accent : "transparent",
color: tab === t.id ? "var(--ink)" : "var(--muted)",
fontWeight: tab === t.id ? 700 : 500,
}}>
{t.label}
))}
{tab === "what" &&
}
{tab === "how" && }
{tab === "pairs" && }
{tab === "science" && }
{tab === "faq" && }
);
};
/* ───────────────────────── WHAT IT DOES ───────────────────────── */
const WhatTab = ({ d, accent }) => {
// Split each indication on em-dash into title + desc; benefits cap at 4
const benefits = (d.indications || []).slice(0, 4).map(it => {
const parts = it.split(/—|–|-\s/);
return parts.length > 1
? { title: parts[0].trim(), desc: parts.slice(1).join(" — ").trim() }
: { title: it, desc: "" };
});
const ICONS = ["✿", "❋", "★", "▲"];
return (
★ WHAT IT DOES
It's not a drug — it's a signal your body forgot how to send.
{d.mechanism}
{d.the_promise && (
★ WHAT TO EXPECT
{d.the_promise}
)}
{benefits.map((b, i) => (
{ICONS[i % ICONS.length]}
{b.title}
{b.desc &&
{b.desc}
}
))}
);
};
const PathwayDiagram = ({ d, accent }) => {
const Glyph = window.PEPTIDE_GLYPH;
const outcomes = (d.indications || []).slice(0, 3).map(it => {
const t = it.split(/—|–|-\s/)[0].trim();
return t.length > 28 ? t.slice(0, 26) + "…" : t;
});
return (
★ WHAT {d.name.toUpperCase()} DOES IN YOUR BODY
{Glyph && }
{d.name}
{(d.class_label || "").split("(")[0].trim()}
{outcomes.map((o, i) => (
{String(i + 1).padStart(2, "0")}
{o}
))}
Average outcomes from {d.evidence?.length ? `${d.evidence.length}+ peer-reviewed studies` : "decades of clinical research"}.
);
};
/* ───────────────────────── HOW YOU USE IT ───────────────────────── */
const HowTab = ({ d, accent }) => {
const steps = d.how_you_use_it || (d.dose_ladder || []).map(dose => ({ step: dose.range, note: dose.note }));
return (
★ HOW YOU USE IT
Simple, daily, no guesswork.
Your prescriber sets the exact dose for you. The numbers below are typical starting points.
{steps.map((s, i) => (
{String(i + 1).padStart(2, "0")}
{s.step || s.range}
{s.note}
))}
{d.whats_in_the_box && (
★ WHAT SHIPS IN THE BOX
{d.whats_in_the_box.map((b, i) => (
))}
)}
);
};
const ShopTimeline = ({ d, accent }) => (
★ WHAT TO EXPECT
Week by week.
{d.timeline.map((r, i) => (
{r.wk}
{i < d.timeline.length - 1 &&
}
{r.what}
))}
);
/* ───────────────────────── PAIRS WELL WITH ───────────────────────── */
const PairsTab = ({ d, accent }) => {
const pairs = getPairings(d.slug);
const PALETTES = window.PRODUCT_PALETTES || {};
const ACCENTS = window.PEPTIDE_ACCENTS || {};
return (
★ PAIRS WELL WITH
Want to go further? These work alongside {d.name}.
You don't need them — {d.name} does plenty on its own. These are the most common stack-mates we prescribe and what they add.
{pairs.length === 0 ? (
{d.name} stands alone — your prescriber will add things based on your goals from the BioReveal.
Start the BioReveal →
) : (
)}
);
};
/* ───────────────────────── THE SCIENCE ───────────────────────── */
const ScienceTab = ({ d, accent }) => {
const stats = [
{ v: `${d.evidence?.length || 0}+`, l: "Cited studies" },
{ v: (d.fda_status || "").match(/\d{4}/)?.[0] || "—", l: "Year established" },
{ v: d.half_life ? d.half_life.split(/[\s—]/)[0] : "—", l: "Half-life" },
{ v: "99.6%", l: "Avg lot purity" },
];
return (
★ THE SCIENCE
For when you want the receipts.
{d.fda_status}
{(d.evidence || []).map((e, i) => (
{String(i + 1).padStart(2, "0")}
))}
);
};
/* ───────────────────────── FAQ ───────────────────────── */
const FAQItem = ({ q, a, accent, initialOpen }) => {
const [open, setOpen] = React.useState(initialOpen);
return (
setOpen(!open)} className="shop-faq-q">
{q}
{open ? "−" : "+"}
{open &&
{a}
}
);
};
const FAQTab = ({ d, accent }) => {
const faqs = d.faq || peptideData?.faq || [];
return (
★ QUESTIONS
The ones we get most.
{faqs.map((f, i) => (
))}
);
};
/* ───────────────────────── REVIEW (if present) ───────────────────────── */
const ReviewSection = ({ d, accent }) => {
if (!d.review) return null;
return (
{[1,2,3,4,5].map(i => )}
"{d.review.quote}"
— {d.review.name}, {d.review.age} · {d.review.location}
);
};
/* ───────────────────────── FINAL CTA ───────────────────────── */
const FinalCTA = ({ d, accent }) => (
★ NOT SURE YET?
Is {d.name} right for you?
Take the 4-minute BioReveal. Tell us what you want to fix. We'll tell you whether {d.name} is the right starting point — or point you to a different peptide that fits better.
);
/* ───────────────────────── PAGE ───────────────────────── */
function ProductPage() {
const d = data;
const accent = (window.PEPTIDE_ACCENTS || {})[d.slug] || "#6b6b6b";
return (
);
}
ReactDOM.createRoot(document.getElementById("root")).render( );