// XBD Collective — page views
const { useState: useStateP, useEffect: useEffectP, useRef: useRefP, useMemo: useMemoP } = React;
// ============================================================
// HOME
// ============================================================
function HomeView({ t, ar, onNavigate, onOpenProject }) {
const projects = window.XBD.projects;
const featured = projects.filter(p => p.featured);
const F = window.XBD.firm;
const [heroIdx, setHeroIdx] = useStateP(0);
// Rotating hero
useEffectP(() => {
const id = setInterval(() => setHeroIdx(i => (i + 1) % featured.length), 6500);
return () => clearInterval(id);
}, [featured.length]);
const heroProj = featured[heroIdx];
return (
{/* ===== HERO — full bleed rotating, with side rail ===== */}
{featured.map((p, i) => (
))}
{/* Side rail */}
— Project
{String(heroIdx + 1).padStart(2, "0")}
of {String(featured.length).padStart(2, "0")}
SCROLL
{/* Hero content */}
{t.home.eyebrow}
{t.home.h1Pre} {t.home.h1Post}
{t.home.sub}
onNavigate("projects")}>
{ar ? "كل المشاريع" : "View 14 projects"} →
onNavigate("contact")}
style={{ borderColor: "rgba(241,236,227,0.4)", color: "var(--paper)" }}>
{t.cta.consult}
{/* Hero caption (current project) — generous breathing room */}
Now showing
onOpenProject(heroProj)} style={{ textAlign: "left", color: "inherit" }}>
{heroProj.name}
{heroProj.location} · {heroProj.year}{heroProj.visualisation ? " · Visualisation" : ""}
onOpenProject(heroProj)} className="mono link-u" style={{ color: "var(--paper)" }}>
Case study →
{featured.map((_, i) => (
setHeroIdx(i)} aria-label={`Hero ${i+1}`} style={{
width: i === heroIdx ? 40 : 18, height: 2,
background: i === heroIdx ? "var(--accent)" : "rgba(241,236,227,0.35)",
transition: "all 400ms"
}}/>
))}
{/* ===== PRESS / AWARDS MARQUEE ===== */}
{[...Array(2)].map((_, dup) => (
90+ awards
Arabian Property Awards
International Property Awards
Commercial Interior Design
SBID
Love That Design
Identity Design Awards
Rethinking The Future
Luxhabitat
CovetHouse
))}
{/* ===== INTRO STATEMENT ===== */}
— A note on practice
§ 01
We are an integrated studio — architects, interior designers, landscape, FF&E and project coordinators under one roof in Dubai Design District . From a single residence to a hotel — the same hand, from first sketch to last specification.
{/* ===== SELECTED WORK ===== */}
{/* Asymmetric editorial grid */}
onNavigate("projects")}>
{ar ? "كل المشاريع" : "All projects"} ({projects.length}) →
{/* ===== PROCESS BAND ===== */}
{[
{ n: "01", t: "Concept", body: "Brief, site analysis, references, and a first design intent — established before fees escalate.", k: "Weeks 1–4" },
{ n: "02", t: "Design", body: "Architecture, interiors and landscape developed in parallel. MEP and structure coordinated under one roof.", k: "Months 2–6" },
{ n: "03", t: "Coordination", body: "FF&E specification, technical drawings, vendor and contractor coordination. The design intent is locked.", k: "Months 5–10" },
{ n: "04", t: "Delivery", body: "On-site review, snagging, and handover. We protect the design intent through to the final fixture.", k: "Months 8–14" }
].map((s, i) => (
))}
{/* ===== SECTORS STRIP ===== */}
{[
{ id: "residential", img: featured[0].cover, label: "Residential", n: 7 },
{ id: "hospitality", img: "assets/kata-02.webp", label: "Hospitality", n: 4 },
{ id: "commercial", img: "assets/taws-01.webp", label: "Commercial", n: 1 },
{ id: "retail", img: "assets/juice-01.webp", label: "Retail", n: 1 },
{ id: "mixed-use", img: "assets/althuraya-aerial.webp", label: "Mixed-use", n: 1 }
].map((s, i) => (
onNavigate("projects", { sector: s.id })} style={{ width: "100%", textAlign: "left", color: "inherit", display: "block" }}>
{ const img = e.currentTarget.querySelector("img"); if (img) img.style.transform = "scale(1.06)"; }}
onMouseLeave={e => { const img = e.currentTarget.querySelector("img"); if (img) img.style.transform = "scale(1)"; }}>
0{i+1}
{s.label}
{s.n} {s.n === 1 ? "project" : "projects"} →
))}
{/* ===== FOUNDER PULL QUOTE ===== */}
Ellen Søhoel
Founder, 2015
"
Architecture, interiors, landscape — they are the same problem at different scales. We started XBD because that is how we wanted to work. Ten years in , that conviction hasn't changed.
— Ellen Søhoel, on founding XBD · attributed
{/* ===== CREDIBILITY ===== */}
Clients (per press, attributed)
{["Emaar","DAMAC","Almal","Rixos","St Regis"].map(c => (
{c}
))}
{/* ===== MATERIALS / PROCESS IMAGE ===== */}
§ 06 · Materials & process
We design the joint, not just the room.
Travertine, oak, plaster, bronze — specified, sourced, supervised on site. Our material library lives in d3, two minutes from the studio floor.
{["Travertine","Smoked oak","Lime plaster","Patinated brass"].map(m => (
))}
onNavigate("services")}
style={{ borderColor: "rgba(241,236,227,0.4)", color: "var(--paper)" }}>
Our services →
{/* ===== CTA BAND ===== */}
Start a project
A villa, a hotel, or a city block — tell us what you're building.
onNavigate("contact")}>{t.cta.start} →
onNavigate("contact")}>{t.cta.consult}
);
}
// ============================================================
// PROJECTS INDEX
// ============================================================
function ProjectsView({ t, ar, onOpenProject, initialFilter }) {
const projects = window.XBD.projects;
const [sector, setSector] = useStateP(initialFilter?.sector || "all");
const [scope, setScope] = useStateP("all");
const [view, setView] = useStateP("grid"); // grid | index
useEffectP(() => { if (initialFilter?.sector) setSector(initialFilter.sector); }, [initialFilter]);
const filtered = useMemoP(() => projects.filter(p => {
if (sector !== "all" && p.sector !== sector) return false;
if (scope !== "all" && !p.scope.includes(scope)) return false;
return true;
}), [sector, scope]);
return (
{/* Header */}
{t.projects.title}
{filtered.length} {filtered.length === 1 ? "project" : "projects"}
setView("grid")} style={{
padding: "9px 14px", border: "1px solid var(--hairline)",
background: view === "grid" ? "var(--fg)" : "transparent",
color: view === "grid" ? "var(--bg)" : "var(--fg-soft)"
}}>Grid
setView("index")} style={{
padding: "9px 14px", border: "1px solid var(--hairline)",
background: view === "index" ? "var(--fg)" : "transparent",
color: view === "index" ? "var(--bg)" : "var(--fg-soft)"
}}>Index
{/* Filters */}
{t.projects.filterSector}:
setSector("all")} />
{window.XBD.sectors.map(s => (
setSector(s.id)} />
))}
{t.projects.filterScope}:
setScope("all")} />
{window.XBD.scopes.map(s => (
setScope(s.id)} />
))}
{(sector !== "all" || scope !== "all") && (
{ setSector("all"); setScope("all"); }} style={{ color: "var(--accent)", borderBottom: "1px solid var(--accent)", paddingBottom: 2 }}>{t.projects.clear}
)}
{/* Grid */}
{view === "grid" ? (
{filtered.map((p, i) => (
))}
{filtered.length === 0 && (
No projects match this filter.
)}
) : (
№
Project
Location
Sector
Year
Scope
{filtered.map((p, i) => (
onOpenProject(p)} style={{
display: "grid", gridTemplateColumns: "60px 1fr 1fr 0.6fr 0.5fr 1.4fr 0.6fr",
gap: 24, padding: "26px 0", borderBottom: "1px solid var(--hairline)",
textAlign: "left", width: "100%", color: "inherit", alignItems: "center",
transition: "background 240ms"
}}
onMouseEnter={e => e.currentTarget.style.background = "var(--surface-elev)"}
onMouseLeave={e => e.currentTarget.style.background = "transparent"}>
{String(i+1).padStart(2,"0")}
{p.name}
{p.location}
{(window.XBD.sectors.find(s => s.id === p.sector) || {}).label}
{p.year}
{p.scope.join(" · ")}
View →
))}
)}
);
}
// ============================================================
// CASE STUDY
// ============================================================
function CaseStudyView({ t, ar, project, onNavigate, onOpenProject, layout = "editorial" }) {
const all = window.XBD.projects;
const idx = all.findIndex(p => p.slug === project.slug);
const next = all[(idx + 1) % all.length];
const related = all.filter(p => p.sector === project.sector && p.slug !== project.slug).slice(0, 3);
const sectorLabel = (window.XBD.sectors.find(s => s.id === project.sector) || {}).label;
return (
{/* HERO */}
onNavigate("projects")} className="mono fade-in" style={{
color: "rgba(241,236,227,0.7)", letterSpacing: "0.14em"
}}>← {t.nav.projects}
{sectorLabel} · {project.scope.join(" · ")}
{project.name}
{project.location}
{project.year} · {project.scope.join(" · ")} {project.visualisation ? "· VISUALISATION" : ""}
{/* META STRIP */}
{[
["Location", project.location],
["Sector", sectorLabel],
["Scope", project.scope.join(" · ")],
["Year", project.year],
["Photography", project.credit]
].map(([k, v]) => (
))}
{/* BRIEF + CONCEPT */}
01 · {t.caseStudy.brief}
{project.brief}
{project.concept && (
02 · {t.caseStudy.concept}
{project.concept}
)}
{/* HERO IMAGE — LARGE */}
{project.gallery[1] && (
)}
{/* MATERIALS */}
{project.materials && (
03 · {t.caseStudy.materials}
{project.materials.map((m, i) => (
M{String(i+1).padStart(2,"0")}
{m}
))}
)}
{/* GALLERY */}
04 · {t.caseStudy.gallery}
{project.gallery.map((src, i) => {
// Editorial asymmetric layout
const layouts = [
{ col: "1 / 5", ratio: "16 / 10" },
{ col: "5 / 7", ratio: "4 / 5" },
{ col: "1 / 3", ratio: "4 / 5" },
{ col: "3 / 7", ratio: "16 / 10" },
{ col: "1 / 4", ratio: "3 / 4" },
{ col: "4 / 7", ratio: "3 / 4" }
];
const L = layouts[i % layouts.length];
return (
);
})}
{/* OUTCOME */}
{project.outcome && (
05 · {t.caseStudy.outcome}
{project.outcome}
)}
{/* PROJECT ENQUIRY */}
Enquire about a similar project
Building something like {project.name}?
onNavigate("contact", { project: project.slug })}>{t.cta.enquire}
WhatsApp
Your enquiry will be tagged: {project.name}
{/* RELATED + NEXT */}
{related.length > 0 && (
{related.map((p, i) => (
))}
)}
{/* NEXT PROJECT footer */}
onOpenProject(next)}>
{t.caseStudy.next}
{next.name}
{next.location} · {next.year} →
);
}
// ============================================================
// SERVICES
// ============================================================
function ServicesView({ t, ar, onNavigate }) {
return (
{t.nav.services || "Services"}
Integrated. End-to-end.
One studio, one team, one accountability. From the first sketch to the last fixture — coordinated under one roof in Dubai Design District.
{window.XBD.services.map((s, i) => (
{String(i+1).padStart(2,"0")}
{s.title}
{s.body}
))}
);
}
// ============================================================
// STUDIO
// ============================================================
function StudioView({ t, ar, onNavigate }) {
const F = window.XBD.firm;
return (
{t.studio.title}
A studio of {F.staffClaim} , across three cities.
Ellen Søhoel
Founder & Creative Director
Portrait © emma stokwielder productions — rights TBC.
{t.studio.about}
XBD was founded in 2015 by Ellen Søhoel , a Norwegian interior architect drawn to Dubai's early promise. What began as a team of two is now 140+ across Dubai, London and Kochi.
10 years in. 90+ awards. Five sectors. Three cities. One conviction: integrated, in-house, end-to-end — from first sketch to last specification.
Press · attributed
Commercial Interior Design · Love That Design · Luxhabitat · Rethinking The Future · SBID
{/* LEADERSHIP */}
{window.XBD.team.map((m, i) => (
{String(i+1).padStart(2,"0")}
Portrait pending
{m.name}
{m.role}
{m.note &&
{m.note}
}
))}
+ 130 designers, architects, coordinators across Dubai, London and Kochi.
{/* AWARDS */}
{window.XBD.awards.map((a, i) => (
{a.year}
{a.name}
{a.issuer}
{a.project}
))}
{/* OFFICES */}
{F.offices.map((o, i) => (
{String(i+1).padStart(2,"0")} · {o.region}
{o.city}
{o.address}
{o.phone}
))}
);
}
// ============================================================
// CONTACT
// ============================================================
function ContactView({ t, ar, prefillProject }) {
const F = window.XBD.firm;
const [submitted, setSubmitted] = useStateP(false);
const [form, setForm] = useStateP({
name: "", email: "", phone: "", company: "",
scope: "interior", sector: "residential", budget: "",
message: prefillProject ? `Enquiry about a project similar to ${prefillProject}.\n\n` : ""
});
function update(k, v) { setForm(f => ({ ...f, [k]: v })); }
function submit(e) {
e.preventDefault();
setSubmitted(true);
}
const inputStyle = {
width: "100%",
background: "transparent",
border: 0,
borderBottom: "1px solid var(--hairline)",
padding: "14px 0",
color: "var(--fg)",
fontFamily: "var(--sans)",
fontSize: 16,
outline: "none",
transition: "border-color 240ms"
};
const labelStyle = { display: "block", marginBottom: 4, fontFamily: "var(--mono)", fontSize: 11, letterSpacing: "0.14em", color: "var(--fg-mute)", textTransform: "uppercase" };
return (
{t.contact.title}
Start a project.
{/* Form */}
{t.contact.form}
{submitted ? (
Thank you.
Your enquiry is logged and routed to the studio. We respond within two working days — usually with a request for a short call to understand the brief, the site, and your timeline.
{ setSubmitted(false); setForm({ name:"", email:"", phone:"", company:"", scope:"interior", sector:"residential", budget:"", message:"" }); }}>Send another
) : (
)}
{/* Sidebar */}
{t.contact.offices}
{F.offices.map(o => (
{o.city}
{o.region}
{o.address}
{o.phone}
))}
Response time
2 working days.
Every enquiry is read by a director. We respond within two working days, usually with a short call to understand the brief.
);
}
Object.assign(window, { HomeView, ProjectsView, CaseStudyView, ServicesView, StudioView, ContactView });