Qualité dyal tswirat : 6/10 · m9iyma b IA
Majorelle, Marrakech
1 500 000 MAD
10 274 MAD/m²
Positionnement dyal taman
−31% t7t l'wasat dyal l'7ouma
L'wasat Majorelle : 14 783 MAD/m² · 23 annonces m7allila
Rendement locatif estimé ≈ 9,2 % f 3am · Fin tinvesti f Marrakech ?
Chouf l'as3ar dyal l'7oumaCaractéristiques
Annonces mchabhin
1 500 000 MAD
10 274 MAD/m²
Surface
146 m²
Biout
2
Sna dyal lbni
2001
Description
🏡 Appartement spacieux à vendre – Majorelle Guéliz, Marrakech 📍 Localisation : Quartier Guéliz – à deux pas du Jardin Majorelle et du Musée YSL Superficie habitable : 146 m² Étage : 2ème (sans ascenseur) * Emplacement recherché près du Jardin Majorelle / Musée YSL. * Deux grands salons, facilement transformables en chambres supplémentaires. * Cuisine séparée et spacieuse. * 1 salle de bain fonctionnelle. * Appartement vendu vide, idéal pour une rénovation et décoration moderne. * Zone à forte demande locative – parfait pour un investissement Airbnb/Booking. 🏠 Description Cet appartement de 146 m² bénéficie d’une localisation exceptionnelle, à deux pas du Jardin Majorelle, l’un des sites les plus visités de Marrakech. Les deux grands salons permettent différentes configurations : séjour, chambres, bureau ou espace locatif. La cuisine séparée est vaste et lumineuse, parfaitement adaptée pour une famille ou une future mise en location saisonnière. Vendu vide, il offre une excellente opportunité de personnalisation et de mise en valeur. 📈 Opportunité d’investissement * Marrakech est en plein boom touristique, avec une forte demande pour des appartements de grande surface dans Guéliz. * Avec un aménagement et une décoration modernes, l’appartement peut générer un rendement locatif élevé via Airbnb ou Booking. 📄 Détails pratiques * Superficie : 146 m² * 2 grands salons lumineux et modulables * 2 chambres spacieuses * 1 salle de bain * Grande cuisine séparée * 2ème étage (sans ascenseur) * Vendu vide * Prix demandé : 1.500.000 MAD – négociable 📞 Contact direct propriétaire (Téléphone / WhatsApp) document.addEventListener(`DOMContentLoaded`,function(){let e=document.getElementById(`voir-plus`),t=document.querySelector(`.label`);e.addEventListener(`change`,function(){e.checked||t.scrollIntoView({behavior:`smooth`})})}); Annonce par le propriétaire Localisation Normal Satellite Information financières --> --> Annonces Similaires Il y a 14 heures 2 650 000 DH 4 Ch 2 sdb 200 m² Appartement à vendre Marrakech -Samlalia Annonce par : Sélection agenz (function(){const id = ""; const similarLen = 1; document.addEventListener("DOMContentLoaded", function () { const carousel = document.getElementById(`Cards-Container${id}`); const arrows = document.getElementById(`arrowsId${id}`); const indicators = document.getElementById(`indicators${id}`); // const prevStroke = document.getElementById(`prev-stroke${id}`); const nextButtons = document.querySelectorAll(`.next-btn${id}`); const prevButtons = document.querySelectorAll(`.prev-btn${id}`); if (!carousel) return; /* Card #4103 — measure the real card "step" (card width + gap) * by reading the two first children's offsetLeft. Falls back to * the first child's offsetWidth if there's only one card. This is * what makes the arrow navigation actually land on card edges * instead of being off by the gap. */ let currentIndex = 0; const firstCard = carousel.firstElementChild; const cardWidth = firstCard instanceof HTMLElement ? firstCard.offsetWidth || 320 : 320; const measureStep = () => { const first = carousel.children[0]; const second = carousel.children[1]; if (!first || !(first instanceof HTMLElement)) return cardWidth; if (second && second instanceof HTMLElement) { const delta = second.offsetLeft - first.offsetLeft; if (delta > 0) return delta; } return first.offsetWidth || cardWidth; }; let step = measureStep(); /* maxScroll is the true end of the scroll container. The card- * counted maxIndex is derived from it so the last "next" click * lands at scrollLeft = maxScroll and the final card sits fully * inside the viewport, no partial visibility. */ const getMaxScroll = () => Math.max(0, carousel.scrollWidth - carousel.clientWidth); let maxScroll = getMaxScroll(); let maxIndex = step > 0 ? Math.ceil(maxScroll / step) : 0; const maxDots = 5; const isRTL = getComputedStyle(carousel).direction === "rtl"; const recompute = () => { step = measureStep(); maxScroll = getMaxScroll(); maxIndex = step > 0 ? Math.ceil(maxScroll / step) : 0; }; const updateArrowsVisibility = () => { if (!arrows || !carousel) return; arrows.style.visibility = maxScroll > 0 ? "visible" : "hidden"; }; /* Dots window over the VALID scroll positions [0, maxIndex], * not over similarLen. Showing more dots than reachable positions * was the source of the "active dot stuck on an earlier position" * bug for cards = N where N - visibleCards maxDots and there's more content beyond an edge, * the dots closest to that edge shrink (1.0 → 0.7 → 0.5) so the * user gets a visual "there's more in this direction" cue without * the dot strip itself growing. */ const updateIndicators = () => { if (!indicators) return; const dots = indicators.querySelectorAll(".dot-indicator"); const pageCount = maxIndex + 1; // number of valid scroll positions let startDot = 0; if (pageCount > maxDots) { const half = Math.floor(maxDots / 2); if (currentIndex = pageCount - half - 1) { startDot = pageCount - maxDots; } else { startDot = currentIndex - half; } } const endDot = startDot + maxDots - 1; const overflowsAtStart = pageCount > maxDots && startDot > 0; const overflowsAtEnd = pageCount > maxDots && endDot { const actualIndex = startDot + dotPosition; const isVisible = actualIndex { if (!carousel) return; recompute(); currentIndex = Math.max(0, Math.min(index, maxIndex)); /* The container has `scroll-snap-type: x mandatory` and the cards * default to start-aligned snap. If we just scrollTo(maxScroll) * the browser snaps back to the PREVIOUS card's left edge when * maxScroll isn't an exact multiple of `step` — that's what * leaves the last card partially hidden behind the gap. * * For the final index, pin the LAST card's inline-end edge to * the container's inline-end edge via scrollIntoView. That * overrides snap proximity and guarantees full visibility of * the last card, no matter the gap or padding-right. */ if ( currentIndex >= maxIndex && maxIndex > 0 && carousel.children.length > 0 ) { const lastCard = carousel.children[carousel.children.length - 1]; if (lastCard instanceof HTMLElement) { lastCard.scrollIntoView({ inline: isRTL ? "start" : "end", block: "nearest", behavior: smooth ? "smooth" : "auto", }); } else { carousel.scrollTo({ left: maxScroll, behavior: smooth ? "smooth" : "auto", }); } } else { const scrollLeft = Math.min(currentIndex * step, maxScroll); carousel.scrollTo({ left: scrollLeft, behavior: smooth ? "smooth" : "auto", }); } updateIndicators(); prevButtons.forEach((btn) => { btn.style.display = currentIndex > 0 ? 'flex' : 'none'; }); // Hide next button when there are no more cards to scroll to. nextButtons.forEach((btn) => { btn.style.display = currentIndex { if (!carousel) return; // In RTL, browsers report scrollLeft as a negative value (or inverted), // so take the absolute distance from the start edge. const scrollPosition = Math.abs(carousel.scrollLeft); /* If the user dragged all the way to maxScroll, snap the index * to maxIndex even if scrollPosition / step rounds short. */ if (maxScroll > 0 && scrollPosition >= maxScroll - 1) { currentIndex = maxIndex; } else { currentIndex = step > 0 ? Math.round(scrollPosition / step) : 0; } currentIndex = Math.max(0, Math.min(currentIndex, maxIndex)); updateIndicators(); prevButtons.forEach((btn) => { btn.style.display = currentIndex > 0 ? 'flex' : 'none'; }); // Hide next button when there are no more cards to scroll to. nextButtons.forEach((btn) => { btn.style.display = currentIndex { if (resizeRaf) return; resizeRaf = requestAnimationFrame(() => { resizeRaf = 0; recompute(); updateArrowsVisibility(); updateIndicators(); if (currentIndex > maxIndex) { scrollToIndex(maxIndex, false); } else { // Re-evaluate next/prev visibility against the fresh maxIndex. prevButtons.forEach((btn) => { btn.style.display = currentIndex > 0 ? "flex" : "none"; }); nextButtons.forEach((btn) => { btn.style.display = currentIndex window.removeEventListener("resize", onResize), { once: true }, ); carousel.addEventListener("scroll
Localisation
Position ta9ribiya