// Project case study template — the hero deliverable.
// Metadata bar (sector · location · scope · area · year · services), build narrative
// (brief → construction approach → joinery → approvals → outcome), gallery w/ lightbox,
// related work, and a "start a similar project" CTA.
function CaseStudy({ id, navigate }) {
const data = window.DS_DATA;
const project = data.projects.find((p) => p.id === id);
const [lightbox, setLightbox] = useState(null); // index or null
useEffect(() => { window.scrollTo({ top: 0, behavior: "instant" }); }, [id]);
if (!project) {
return (
Project not found.
The case study you were looking for has been moved or unpublished.
);
}
const related = data.projects.filter((p) => p.id !== project.id && p.sector === project.sector).slice(0, 3);
const fallbackRelated = data.projects.filter((p) => p.id !== project.id).slice(0, 3);
const relatedList = related.length >= 2 ? related : fallbackRelated;
// Build the build-narrative as an ordered list.
const stages = [
{ code: "01", label: "Brief", body: project.brief },
{ code: "02", label: "Construction approach", body: project.build },
{ code: "03", label: "Joinery & finishes", body: project.joinery },
{ code: "04", label: "Approvals", body: project.approvals },
{ code: "05", label: "Outcome", body: project.outcome },
];
// Gallery layout — alternates between split (7/5) and full (12) rows.
const gallerySpans = ["span-7", "span-5", "span-12", "span-6", "span-6"];
return (
{/* Breadcrumb */}
/
/
{project.name}
{/* Header */}
Case study — {String(data.projects.indexOf(project) + 1).padStart(2, "0")}
{project.name}
{project.summary}
{/* Hero image */}
{/* Metadata bar */}
{/* Narrative */}
The build
How we delivered this one.
{stages.map((s, i) => (
-
{s.code} ·
))}
{/* Gallery */}
Gallery
Photography credit: in confirmation
{project.gallery.map((src, i) => (
setLightbox(i)}>
))}
{/* If gallery is short, pad with relevant process imagery so the layout never feels empty */}
{project.gallery.length < 3 ? (
<>
setLightbox(project.gallery.length)}>
setLightbox(project.gallery.length + 1)}>
>
) : null}
{/* Stage progress strip — before / during / after placeholder */}
{["Before", "During build", "Handover"].map((label, i) => {
const imgs = [
["media/process-earthwork.webp", "media/capability-building.webp", project.heroImage],
[project.heroImage, "media/process-joinery.webp", "media/capability-building.webp"],
][i % 2];
return (
{label}
{i === 0 ? "Pre-mob" : i === 1 ? "Mid programme" : "On handover"}
);
})}
{/* CTA */}
Building something like this?
Tell us the scope and programme — we'll come back within two working days with a sector lead and a build approach.
{/* Related */}
navigate({ name: "projects", filter: project.sector })}>All {project.sectorLabel}}
/>
{relatedList.map((p) => (
navigate({ name: "project", id: p.id })} />
))}
{lightbox !== null ? (
setLightbox(null)}
/>
) : null}
);
}
function MetaBar({ project }) {
const cells = [
{ label: "Sector", value: project.sectorLabel },
{ label: "Location", value: project.location },
{ label: "Scope", value: project.scope },
{ label: "Area", value: project.area },
{ label: "Year", value: project.year },
{ label: "Services", value: project.services.join(" · ") },
];
return (
{cells.map((c, i) => (
{c.label}
{c.value}
))}
);
}
Object.assign(window, { CaseStudy });