/* ============================================================
CSL Landing — Sections B: Services, Recognition, Why, Steps,
Testimonials, FAQ, Closer + Footer, Page mount
============================================================ */
const { Button: B2 } = window.ContentStackLabDesignSystem_cbb53c;
const Ic2 = window.Icons;
const D2 = window.CSL;
const ArrowB = window.Arrow;
const { useState: uS, useRef: uR } = React;
// hover-to-open only where a real pointer exists — on touch, the emulated
// mouseenter would open the item right before the tap's click closed it.
const canHover = window.matchMedia("(hover: hover)").matches;
const ChevronDown = () => (
);
// Chips accept admin markdown links: "[label](url)" becomes a linked pill.
const CHIP_LINK = /^\s*\[([^\]]+)\]\(([^)]+)\)\s*$/;
function renderChip(p) {
const m = CHIP_LINK.exec(p);
if (m) {
return {m[1]} ;
}
return {p} ;
}
/* ---------------- Services accordion ---------------- */
function Services() {
const [open, setOpen] = uS(-1);
return (
What I actually do
Three ways I help B2B tech brands grow.
{D2.services.map((s, i) => {
const isOpen = open === i;
return (
setOpen(i) : undefined}>
setOpen(isOpen ? -1 : i)} aria-expanded={isOpen}>
{s.title}
{s.tagline}
{s.points.map(renderChip)}
);
})}
}>Explore services
);
}
/* ---------------- Recognition ---------------- */
function Recognition() {
return (
Recognized for excellence
{D2.recognition.map((r, i) => (
))}
);
}
/* ---------------- Why CSL ---------------- */
// Hardcoded here, not admin-managed. Icons must be keys in window.Icons.
const WHY = [
{ icon: "chair", title: "I've sat in your chair", body: "17 years on the brand side means I don't give advice from the sidelines. I've run the campaigns, managed the budgets, and reported to the CEO." },
{ icon: "userCheck", title: "No junior handoffs", body: "You talk to me. You work with me. No bait-and-switch where a senior sells you and a junior delivers." },
{ icon: "layers", title: "Complex made clear", body: "Cybersecurity, OT/ICS, EHS, enterprise AI. I translate complex products into messaging buyers actually understand." },
{ icon: "scale", title: "Both sides of the table", body: "I've worked inside the tech companies and inside the industrial organizations they sell to. I know your product and your buyer's world." },
{ icon: "sparkles", title: "AI-augmented, not AI-replaced", body: "I use modern AI tools to move faster and deliver more, but the thinking is human. Your brand deserves better than a prompt and a prayer." }
];
function Why() {
return (
Why work with me
Here's what's different, and why it matters.
{WHY.map((w) => (
{Ic2[w.icon]({ width: 26, height: 26 })}
{w.title}
{w.body}
))}
);
}
/* ---------------- Steps ---------------- */
function Steps() {
return (
How we work together
Pick your engagement style.
{D2.steps.map((s) => (
{s.n}
{s.name}
{s.body} {s.best}
))}
);
}
/* ---------------- Testimonials ---------------- */
function Testimonials() {
const [i, setI] = uS(0);
const [drag, setDrag] = uS(0);
const [downX, setDownX] = uS(null);
const [paused, setPaused] = uS(false); // manual interaction stops auto-play for good
const hovered = uR(false);
const t = D2.testimonials;
if (!t.length) return null;
React.useEffect(() => {
if (downX !== null || paused) return;
const timer = setInterval(() => {
if (!hovered.current) setI((prev) => (prev + 1) % t.length);
}, 5000);
return () => clearInterval(timer);
}, [downX, paused, t.length]);
const go = (n) => { setPaused(true); setI((n + t.length) % t.length); };
const onPointerDown = (e) => {
setDownX(e.clientX);
e.target.setPointerCapture(e.pointerId);
};
const onPointerMove = (e) => {
if (downX === null) return;
setDrag(e.clientX - downX);
};
const onPointerUp = (e) => {
if (downX === null) return;
if (drag < -50) go(i + 1);
else if (drag > 50) go(i - 1);
setDownX(null);
setDrag(0);
};
return (
Don't take my word for it
What people say after working with me.
{ hovered.current = true; }} onMouseLeave={() => { hovered.current = false; }}>
{t.map((cur, n) => {
const initials = cur.name.split(" ").slice(0, 2).map((w) => w[0]).join("");
return (
{Ic2.quote({ width: 56, height: 56 })}
{cur.quote}
{initials}
{cur.name}
{cur.company}
);
})}
{t.map((_, n) => go(n)} />)}
);
}
/* ---------------- FAQ ---------------- */
function Faq() {
const [open, setOpen] = uS(-1);
return (
You ask, we answer
The questions we get most.
{D2.faqs.map(([q, a], i) => {
const isOpen = open === i;
return (
setOpen(i) : undefined}>
setOpen(isOpen ? -1 : i)} aria-expanded={isOpen}>
{q}
);
})}
);
}
/* ---------------- Closer + Footer ---------------- */
function Closer() {
return (
);
}
/* ---------------- Page ---------------- */
function Page() {
window.useReveal();
return (
);
}
ReactDOM.createRoot(document.getElementById("root")).render( );