/* Collections + Craftsmanship + Projects */ function Collections({ onNav, onOpenCarpet }) { const [active, setActive] = useState('all'); const [sort, setSort] = useState('curated'); const list = useMemo(() => { let arr = active === 'all' ? window.ZULEYA.CARPETS : window.ZULEYA.CARPETS.filter(c => c.collection === active); arr = [...arr]; if (sort === 'price-asc') arr.sort((a, b) => a.priceAed - b.priceAed); if (sort === 'price-desc') arr.sort((a, b) => b.priceAed - a.priceAed); if (sort === 'size') arr.sort((a, b) => parseFloat(a.size) * parseFloat(a.size.split('×')[1] || 1) - parseFloat(b.size) * parseFloat(b.size.split('×')[1] || 1)); return arr; }, [active, sort]); const counts = useMemo(() => { const c = { all: window.ZULEYA.CARPETS.length }; window.ZULEYA.CARPETS.forEach(x => { c[x.collection] = (c[x.collection] || 0) + 1; }); return c; }, []); const activeMeta = window.ZULEYA.COLLECTIONS.find(c => c.id === active); return ( Seven drawings, sixteen carpets, one loom.} lead="Every Zuleya is a one-of-one. Each is signed by the artisan who finished it, shipped with a certificate of origin and the cartoon it was woven from." />
{window.ZULEYA.COLLECTIONS.map(col => ( ))}
{activeMeta && activeMeta.desc && (

{activeMeta.desc}

SORT
)}
{list.map(c => )}
{list.length === 0 && (
No carpets here yet — try another collection.
)}
One-of-one

Every Zuleya carries the same paperwork as a museum piece.

    {[ { k: 'Signed', v: 'By the artisan who finished the carpet, in script on the reverse.' }, { k: 'Certificate of origin', v: 'Specifying loom number, weaver name, materials and weave date.' }, { k: 'Cartoon', v: 'The 1:1 drawing the carpet was woven from is shipped with the piece.' }, { k: 'Provenance booklet', v: 'A short letter from the loom, with photographs of the weavers and the wool.' }, { k: 'Lifetime care', v: 'Cleaning, repair and storage included for the life of the carpet.' } ].map(r => (
  • {r.k} {r.v}
  • ))}
); } function Craftsmanship({ onNav }) { const [step, setStep] = useState(0); const steps = window.ZULEYA.PROCESS_STEPS; const images = [ 'media/media-05-1.webp', 'media/media-06-2.webp', 'media/media-07-3.webp', 'media/media-08-4.webp', 'media/media-09-5.webp' ]; return ( From a herder’s fleece to a knot — five stages, fourteen months.} lead="A single Zuleya carpet passes through more than thirty pairs of hands. Walk the loom from raw wool to the cut-off, one stage at a time." />
{steps.map((s, i) => (
setStep(i)} style={{ padding: '28px 0', borderTop: '1px solid var(--line)', borderBottom: i === steps.length - 1 ? '1px solid var(--line)' : 'none', cursor: 'pointer', opacity: i === step ? 1 : 0.45, transition: 'opacity 220ms ease', display: 'grid', gridTemplateColumns: '64px 1fr', gap: 16 }}>
{s.num}

{s.title}

{i === step && (

{s.body}

)}
))}
FIG. {String(step + 1).padStart(2, '0')} · {steps[step].title.toUpperCase()}
Materials

Wool, plant, water — and a generation of hands.

{[ { k: 'Wool', v: 'Long-staple highland fleece, bought direct from Afghan herders in Balkh and Samangan provinces. Hand-sorted, hand-washed.' }, { k: 'Dye', v: 'Madder root for red, indigo for blue, pomegranate skin and walnut hull for yellows and browns, oak gall for black. No synthetic dyes, ever.' }, { k: 'Foundation', v: 'Cotton warp and weft, sourced inside Afghanistan. The cotton is the only thing in the carpet that is not visible to you — it is the bones.' }, { k: 'Knot', v: 'Persian / asymmetric, three to ten knots per centimetre depending on the drawing. A 3 × 2 m carpet is around 750,000 individual knots.' } ].map(r => (
{r.k} {r.v}
))}
The numbers behind a single carpet

A medium-format Zuleya, in figures.

{[ { n: '4.2 kg', l: 'Raw highland fleece' }, { n: '~580 km', l: 'Of hand-spun single-ply yarn' }, { n: '720,000', l: 'Persian knots, tied by hand' }, { n: '9 months', l: 'At the loom, two artisans' } ].map((s, i) => (
{s.n}
{s.l}
))}
); } function Projects({ onNav }) { return ( Resorts, palaces, royal suites. Carpets drawn for the room they were always going to live in.} lead="A short selection from the last five years. Most projects we run are under non-disclosure; the studios below have allowed us to show photography and scope." dark />
{window.ZULEYA.PROJECTS.map(p => (
onNav('contact')}>
{p.year} {p.sqm} {p.scope}

{p.name}

{p.blurb}

Project notes →
))}
{window.ZULEYA.TESTIMONIALS.map((t, i) => (
{t.quote}
{t.name}
{t.role}
))}
); } Object.assign(window, { Collections, Craftsmanship, Projects });