Swipe left or right to read more reviews ➔
★★★★★
Installed sheer and blackout curtains at my apartment. Did a fabulous job - very efficient and great end product. Vinaka!
Meliki Tuinamuana (Suva)

Verified Google Review
★★★★★
The team did a very neat installation and was a pleasure working with Shahil and Shekar.
Sadhana Rambisessar (Suva)

Verified Google Review
★★★★★
Friendly and professional staff, always attentive.
CJ Tunidau (Suva)

Verified Google Review
★★★★★
Laisa is always friendly and helpful. Great quality fabrics.
SellyAnne Paulo (Suva)

Verified Google Review
★★★★★
Exceptional service and high-quality home décor products. Highly recommended.
Dpika 86 (Namaka)

Verified Google Review
★★★★★
Very happy with towel quality — soft, durable and great value.
Krishant Chand (Namaka)

Verified Google Review
const track = document.querySelector('.review-track');
const isMobile = window.innerWidth <= 768;
if (isMobile && track) {
// Duplicate cards for infinite wrap around
const originalCards = Array.from(track.children);
const totalOriginals = originalCards.length;
originalCards.forEach(card => track.appendChild(card.cloneNode(true)));
originalCards.forEach(card => track.appendChild(card.cloneNode(true)));
const allCards = document.querySelectorAll('.review-card');
let currentIndex = totalOriginals; // Start in middle set
let isDown = false;
let startX = 0;
let currentTranslate = 0;
let prevTranslate = 0;
let velocity = 0;
let lastX = 0;
let lastTime = 0;
function cardWidth() {
return allCards[0].offsetWidth + 15;
}
function setPositionByIndex(index, animated = true) {
if (animated) {
track.style.transition = "transform 0.35s ease";
} else {
track.style.transition = "none";
}
currentTranslate = -index * cardWidth();
prevTranslate = currentTranslate;
track.style.transform = `translateX(${currentTranslate}px)`;
}
// Initial positioning
setPositionByIndex(currentIndex, false);
track.addEventListener('transitionend', () => {
// Seamless infinite reset without transition animation
if (currentIndex >= totalOriginals * 2) {
currentIndex -= totalOriginals;
setPositionByIndex(currentIndex, false);
} else if (currentIndex < totalOriginals) {
currentIndex += totalOriginals;
setPositionByIndex(currentIndex, false);
}
});
function getX(e) {
return e.touches ? e.touches[0].clientX : e.clientX;
}
function start(e) {
isDown = true;
startX = getX(e);
lastX = startX;
lastTime = Date.now();
track.style.transition = "none";
}
function move(e) {
if (!isDown) return;
const x = getX(e);
const diff = x - startX;
const now = Date.now();
const dt = now - lastTime;
velocity = (x - lastX) / (dt || 1);
lastX = x;
lastTime = now;
currentTranslate = prevTranslate + diff;
track.style.transform = `translateX(${currentTranslate}px)`;
}
function end() {
if (!isDown) return;
isDown = false;
if (velocity < -0.3) currentIndex++;
else if (velocity > 0.3) currentIndex--;
else currentIndex = Math.round(-currentTranslate / cardWidth());
setPositionByIndex(currentIndex, true);
}
track.addEventListener('touchstart', start);
track.addEventListener('touchmove', move, { passive: false });
track.addEventListener('touchend', end);
track.addEventListener('mousedown', start);
track.addEventListener('mousemove', move);
track.addEventListener('mouseup', end);
track.addEventListener('mouseleave', end);
}