Göndermeden önce doldurun
[sunucu adı][ofis adı][şehir, çalıştığım ilçeler, satılık mı kiralık mı][belge no][telefon][WhatsApp numarası][e-posta][ofis adresi][alan adı][ilçe-mahalle-satilik-oda-tür][kısa-ad][[FIYAT]
Opus Growth hosting sunucumdaki [sunucu adı] sitesine, [ofis adı] emlak ofisim için ilanları tek bir JSON dosyasından üreten, statik ve hızlı bir ilan sitesi kur. Ofis: [şehir, çalıştığım ilçeler, satılık mı kiralık mı]. Taşınmaz Ticareti Yetki Belgesi numaram: [belge no]. İlanlar: [her ilan için ilan no, başlık, ilçe, mahalle, oda, brüt ve net metrekare, kat, yapım yılı, ısınma, aidat, tapu durumu, fiyat, ilan tarihi, iki paragraf açıklama, öne çıkan dört özellik, fotoğraf bağlantıları ve ev sahibinin onay verdiği konum düzeyi]. İletişim: [telefon], [WhatsApp numarası], [e-posta], [ofis adresi]. Alan adı: [alan adı].
Sayfalar: ana sayfa (ilçe, oda ve en çok fiyat seçen bir arama formu, öne çıkan ilanlar, ilçe rehberi, SSS), ilanlar (/ilanlar/: filtre, sonuç sayısı, ilan ızgarası, ilan SSS'si), her ilan için ayrı sayfa (/ilanlar/[ilçe-mahalle-satilik-oda-tür]/), hakkımızda (/hakkimizda/: alım sürecinin adımları ve yetki belgesi), iletişim (/iletisim/, form yok), gizlilik (/gizlilik/) ve 404.html.
Tarz: sıcak editoryal emlak. Açık kum rengi zemin, mürekkep metin, çam yeşili gibi tek koyu aksan (düğmede beyaz metinle), başlık ve fiyatta bir Fontshare serif (ör. Sentient 500), gövdede bir sans (ör. Ranade 400). İlan kartında 4:3 fotoğraf, büyük serif fiyat, oda, metrekare ve kat için küçük etiketler. Kart fotoğrafı hover'da hafif yakınlaşır. Harita kütüphanesi, Google Maps iframe'i, galeri ya da kaydırıcı kütüphanesi YÜKLENMEZ.
Tek kaynak ve üretim:
1. Bütün ilan verisi `assets/ilanlar.json` dosyasında durur. Küçük bir `ilan-build.mjs` yaz ve site_run_command ile `node ilan-build.mjs` olarak çalıştır: (a) `_src/ilan.tpl` şablonundan her ilan için `ilanlar/[kısa-ad]/index.html` üretir, (b) bütün kartları `ilanlar/index.html` içindeki `<!-- ilanlar:start --><!-- ilanlar:end -->` arasına, öne çıkanları ana sayfadaki `<!-- one-cikan:start --><!-- one-cikan:end -->` arasına basar, (c) ilan listesinin ItemList şemasını `<!-- ilan-ld:start --><!-- ilan-ld:end -->` arasına yazar. Şablonda `{{...}}` değil `[[FIYAT]]` gibi yer tutucu kullan. Sıra: ilan-build, chrome-stamp, Tailwind derlemesi, seo-stamp. Yeni ilan eklemek ya da satılanı kaldırmak yalnız JSON'u değiştirip bu sırayı yeniden çalıştırmak olsun; satılan ilanın sayfası silinirse adresini site_redirects ile /ilanlar/'a yönlendir.
2. Kartlar sunucuda üretilmiş statik HTML'dir; JS kapalıyken ve arama motorları için bütün ilanlar görünür. Her kart filtre için data-ilce, data-oda, data-fiyat, data-m2 ve data-tarih nitelikleri taşır; bileşen sınıf adları Tailwind yardımcılarıyla çakışmasın (lcard gibi önek kullan).
Filtre kuralları:
1. Filtre, action="/ilanlar/" ve method="get" olan gerçek bir form: ilçe, oda, en az fiyat, en çok fiyat ve sıralama için label'lı select'ler ve bir "Filtrele" düğmesi. JS kapalıyken form adresi doldurur ama liste değişmez; bunu `<noscript>` içinde bir cümleyle söyle.
2. `ilan-filtre.js` yalnız /ilanlar/ sayfasında defer ile yüklenir: sayfa açılırken URLSearchParams ile ?ilce=&oda=&min=&max=&sira= değerlerini okur (yalnız select'te var olan değerleri kabul eder), kartları hidden ile gizler ve gösterir, sıralamayı DOM'da yeniden dizer, değişiklikte history.replaceState ile adresi günceller. Böylece filtreli liste bağlantı olarak paylaşılır ve geri tuşu geçmişi şişirmez.
3. Sonuç sayısını role="status" bir cümlede söyle ("6 ilandan 2 tanesi filtrelerinize uyuyor"). Sonuç yoksa boş durum mesajı ve "Filtreleri temizle" bağlantısı göster. Filtre kutusu ilk ekranda olduğundan JS hiçbir öğeyi eklemesin ya da gizlemesin; yalnız kartlar değişsin, yoksa kayma (CLS) oluşur.
4. İlanlar sayfasında ilk kartın fotoğrafı lazy olmasın, diğerleri lazy olsun; kart başlıkları bu sayfada h2, ana sayfada "Öne çıkan ilanlar" h2'sinin altında h3 olsun.
İlan sayfası:
1. İlk paragraf ilanı tek cümlede özetlesin: mahalle, ilçe, oda, metrekare, yapım yılı ve fiyat. Ardından fotoğraf şeridi: JS'siz CSS scroll-snap ile yatay kayan figure'lar ve altında her fotoğrafa giden bölüm içi bağlantı olan küçük resimler. `galeri.js` (yalnız ilan sayfasında) küçük resim tıklamasını yakalar, sayfayı zıplatmadan yalnız şeridi kaydırır ve görünen fotoğrafın küçük resmini IntersectionObserver ile aria-current="true" yapar. İlk fotoğraf sayfanın LCP öğesidir: fetchpriority="high", lazy değil.
2. Sağ sütunda yapışkan fiyat kutusu: fiyat, künye tablosu (dl: fiyat, ilan no, oda, brüt ve net metrekare, kat, yapım yılı, ısınma, aidat, tapu, ilan tarihi), ilan numarasını içeren hazır mesajlı WhatsApp bağlantısı, telefon ve yetki belgesi numarası. Türkiye'de emlak ilanlarında yetki belgesi numarası yer almalıdır; bu numarayı alt bilgiye de yaz.
3. Konum: tam adres yazma. Ev sahibinin onay verdiği düzeyde (genellikle mahalle) yaklaşık konumu gösteren yerel bir statik harita görseli kullan ve altına "tam adres gösterim randevusunda paylaşılır" yaz. Harita görselini OpenStreetMap karolarından üretiyorsan karo kullanım politikasına uy (ilan başına bir kez, birkaç karo; toplu indirme yok): karoları site_fetch ile `_src/harita/` altına indir, site_run_command ile background=true vererek `npm i -D sharp` kur ve küçük bir betikle karoları birleştir, merkezi kırp, yaklaşık alanı yarı saydam bir daireyle işaretle ve 480 ile 768 piksellik WebP olarak kaydet. Kırpım karo alanının dışına taşarsa kenarda siyah şerit çıkar; merkezin çevresinde en az 5x3 karo indir. "© OpenStreetMap katkıcıları" atfını hem görselin içine hem altına bağlantı olarak yaz. Harita lazy yüklenir ve sayfaya dış harita betiği girmez.
Şema: ilan sayfasında RealEstateListing (url, name, description, datePosted, image dizisi, offers içinde Offer: price, priceCurrency "TRY", availability, seller olarak işletmenin @id'si) ve mainEntity içinde Apartment ya da SingleFamilyResidence (numberOfRooms, numberOfBedrooms, numberOfBathroomsTotal, floorSize QuantitativeValue unitCode "MTK", yearBuilt, address içinde yalnız ilçe, il ve ülke). Google bu türler için ayrı bir zengin sonuç göstermez; amaç, arama ve yapay zeka motorlarının fiyatı, oda sayısını ve konumu doğru okumasıdır. İlanlar sayfasında CollectionPage ve ItemList, işletme için seo-stamp'te businessType "RealEstateAgent", her iç sayfada BreadcrumbList. Fiyat, metrekare, aidat ve tapu bilgisini yalnız ev sahibinin yazılı beyanından al; bilmediğin alanı JSON'a da şemaya da koyma.
Kurulum ve bilinen tuzaklar (demo ölçümünde her biri puanı düşürdü, atlama):
1. Sunucuda yayında bir site varsa önce bana söyle, çünkü kurulum index.html ve assets klasörünü ezer. scaffold_site'ı önce confirm=false ile önizle, onayımla confirm=true çağır. Sonra site_design_guide ve site_seo_guide'ı oku. Koda başlamadan tasarım yönünü (tipografi, renk, ızgara, imza öğesi, hareket dili ve YÜKLENMEYECEK kütüphaneler) assets dışında kısa bir ART.md dosyasına yaz ve bana göster.
2. Tailwind'i derle, CDN'den yükleme. site_run_command ile background=true vererek `npm i -D tailwindcss@3` kur ve sonucu site_command_status ile izle. `tailwind.config.js` içindeki content yolları bütün HTML dosyalarını, `_src` şablonlarını ve sınıf yazan JS dosyalarını kapsasın. `_src/tw.css` dosyası `@import "../assets/app.css";` satırı ve üç `@tailwind` satırından oluşsun; `npx tailwindcss -c tailwind.config.js -i _src/tw.css -o assets/site.css --minify` ile tek dosyaya derle. Sayfalardan cdn.tailwindcss.com betiğini ve satır içi tailwind.config'i kaldır, yalnız `/assets/site.css` bağla. Her HTML değişikliğinden sonra yeniden derle.
3. Fontları kendi sunucunda barındır. Seçtiğin Fontshare ailesinin CSS'ini oku, her ağırlığın woff2 dosyasını ayrı ayrı site_fetch ile `assets/fonts/` altına indir ve @font-face kurallarını `font-display: swap` ile `assets/app.css` içine yaz; design-check özgün fontu orada arar. İndirdiğin CSS'teki font-family adını ve ailenin ı, İ, ş, ğ harflerini içerdiğini kontrol et (Tanker'de ı yok). İlk ekranda kullandığın HER ağırlığı `<link rel="preload" as="font" type="font/woff2" crossorigin>` ile önceden yükle ve bu satırları stil dosyası satırından önce yaz; üç dosyayı geçiyorsa ağırlık sayısını azalt. Demoda preload edilmeyen tek bir gövde ağırlığı mobil LCP'yi 2,6 saniyeye itti ve performansı 97'ye düşürdü. Fontshare fontlarının çoğunda ₺ işareti yok, fiyatları "TL" ile yaz.
4. Menüyü ve alt bilgiyi statik HTML yap. chrome.js, motion.js ve site.config.js'i sayfalara yükleme. site.config.js'teki nav listesini okuyup `_src/header.tpl` ve `_src/footer.tpl` şablonlarını her sayfadaki `<!-- nav:start --><!-- nav:end -->` ve `<!-- footer:start --><!-- footer:end -->` işaretlerinin arasına basan küçük bir `chrome-stamp.mjs` yaz; etkin sayfanın bağlantısına aria-current="page" eklesin. Şablonlarda `{{...}}` yerine `[[NAV]]` gibi yer tutucu kullan, uzantıları .html olmasın; yoksa design-check ve seo-check onları yarım sayfa sanar. Mobil menüyü JS'siz `<details>` ile kur. Alt bilgide işletme adı, adres ve telefon düz metin olarak dursun; alt bilgi ve kırıntı bağlantıları en az 44 piksel yüksekliğinde olsun.
5. Görselleri site_fetch ile indirirken dönüştür: aynı görseli resize="480x", "800x" ve "1200x", image_format="webp", quality=60 ile ayrı dosyalara yaz (site_fetch kırpmaz; kırpım gerekiyorsa kaynağı o oranda iste). Her `<img>` için srcset, sizes ve gerçek orana uyan width ve height ver. Sayfanın LCP öğesi olan görsel fetchpriority="high" olsun ve lazy olmasın; diğer bütün görseller loading="lazy" decoding="async" olsun. Mobilde ilk ekranı dolduran bir görsel varsa `<picture>` ile mobile daha küçük bir kırpım ver.
6. Her sayfaya `<link rel="icon" href="/favicon.svg" type="image/svg+xml">` ekle ve logodan site_fetch ile resize="180x180" bir apple-touch-icon üret. Favicon bağlantısı yoksa tarayıcı /favicon.ico ister, konsola 404 düşer ve En İyi Uygulamalar puanı 96'ya iner.
7. HTML doğrulayıcısının kuralları: belge büyük harfli `<!DOCTYPE html>` ile başlasın; tel: bağlantılarının görünen metnindeki her boşluk ` ` olsun ("Ara: 0212..." gibi önekteki boşluk dahil); style="" niteliği kullanma; her düğmeye type yaz; aria-label'ı yalnız landmark, form ya da etkileşimli öğeye koy (ul ya da rolsüz div'e koymak hata verdi); aynı adla etiketlenmiş iki landmark olmasın; satır sonunda boşluk bırakma.
8. Başlık sırası atlamasın: liste sayfasında h1'den sonra kart başlıkları h3 olunca erişilebilirlik 98'e düştü; kartları yeniden kullanırken başlık düzeyini sayfaya göre ver (liste sayfasında h2, bir h2 bölümünün içinde h3).
9. Hareket için niteliğin adı data-reveal olsun (design-check hareketi yalnız bu adla tanır). Gizli başlangıcı JS yalnız sayfa açıldığında ekranın altında kalan bloklara versin, ilk ekrandaki içerik hiç gizlenmesin; transform kullan, opaklıkla soldurma kontrast denetimini düşürür. Niteliği tek başlığa değil başlığı ve açıklamasını saran bloğa koy; demoda yalnız başlığa konunca bekleyen başlık alttaki paragrafın üstüne bindi. prefers-reduced-motion: reduce olduğunda hiçbir şey gizlenmesin ve animasyonlar kapansın.
SEO, AEO ve GEO:
1. Adresleri dizin biçiminde ve sonu eğik çizgili kur. site.config.js'in seo bloğunu doldur ve `node seo-stamp.mjs` çalıştır. Sayfaya özel şemayı ayrı `<script type="application/ld+json">` bloklarına yaz ve @id ile temel grafiğe bağla. BreadcrumbList'i @graph içine gömme, kendi bloğunda dursun; seo-check onu yalnız böyle görür.
2. Title en çok 60 karakter olsun (seo-check 25'in altını ve 65'in üstünü hata sayar), description 130 ile 158 karakter, her sayfada tek h1 ve her sayfanın ana içeriği en az 150 kelime. İletişim, gizlilik ve liste sayfaları bu sınırın altında kalmaya yatkın; sayfaya özgü bilgi ekle, dolgu yazma.
3. Her sayfanın ilk paragrafı sayfanın sorusuna doğrudan cevap versin. Sayfaya özgü SSS'yi `<details>` ile yaz ve FAQPage olarak işaretle. Yayımlayan kurumu ve "Son güncelleme" tarihini `<time>` ile göster.
4. robots.txt'te GPTBot, OAI-SearchBot, ChatGPT-User, ClaudeBot, Claude-User, Claude-SearchBot, PerplexityBot, Perplexity-User, Google-Extended, Applebot-Extended, Bingbot ve CCBot için kararını gerekçesiyle yaz ve sitemap'i göster. sitemap.xml'de gerçek lastmod olsun. llms.txt'te sitenin iki cümlelik doğru özeti ve önemli sayfaların listesi bulunsun.
5. Uydurma yok: bana sormadan yorum, puan, ödül, müşteri sayısı, sertifika, çalışma saati, koordinat ya da fiyat yazma. Bilmediğin alanı şemaya hiç koyma.
Kapılar (sırayla; hepsi geçmeden "bitti" deme):
1. site_run_command ile `node chrome-stamp.mjs`, Tailwind derlemesi, `node seo-stamp.mjs`, `node seo-check.mjs` (0 hata, 0 uyarı), `node design-check.mjs` (0 hata; kalan uyarıyı gerekçesiyle yaz) ve `npx html-validate "**/*.html"` (0 hata).
2. Her sayfayı site_preview ile view="mobile" ve view="desktop" olarak gör. site_preview sayfayı hareket kapalıyken (reduced-motion) çeker; bu görüntüde içerik eksiksiz görünmüyorsa hareketsiz hal bozuk demektir. Tam sayfa görüntüsünde sabit konumlu öğeler (yapışkan çubuk gibi) sayfanın ortasında görünebilir ve aşağıdaki lazy görseller boş çıkabilir; bunları hata sayma ama yolunu kontrol et.
3. Her sayfa için core_web_vitals'ı strategy="mobile" ve strategy="desktop" ile çalıştır. Kabul: mobilde Performance en az 95 (hedef 99 ile 100), Accessibility, Best Practices ve SEO 100; masaüstünde dördü de 100. Tutmayan sayfada resources=true ile hangi dosyanın geciktirdiğini bul, düzelt ve yeniden ölç. Ardından ana sayfada seo_audit'i strategy="mobile" ve runs=2 ile çalıştır. Ağ istekleri listesinde kendi alan adın dışında bir adres kalmamalı.
4. 360, 390, 768 ve 1440 piksel genişlikte yatay kaydırma olmasın, dokunma hedefleri en az 44 piksel olsun, konsolda hata olmasın.
Etkileşim kontrolü: site_preview filtreyi kullanamaz. Bana /ilanlar/?ilce=[ilçe]&oda=3%2B1 adresini açınca yalnız uyan ilanların göründüğünü ve formun dolduğunu, bir seçimi değiştirince adres çubuğunun güncellendiğini, hiç sonuç kalmayınca boş durum mesajının çıktığını, JavaScript kapalıyken bütün ilanların listelendiğini ve ilan sayfasında küçük resme tıklayınca sayfanın zıplamadan yalnız fotoğraf şeridinin kaydığını nasıl kontrol edeceğimi yaz.
Yayından sonra: site_headers ile `/assets/* Cache-Control: public, max-age=31536000, immutable` kuralını önizle ve onayımla uygula; bundan sonra CSS ya da JS değiştiğinde dosya adını sürümle (site-v2.css gibi). gsc_submit_sitemap ile sitemap.xml'i gönder ve gsc_url_inspect ile ana sayfanın durumuna bak. En sonda bana şunları ver: sayfa sayfa puan tablosu (sayfa, mobil dört puan, masaüstü dört puan, LCP, CLS), kapıların sonucu ve uyguladığın düzeltmelerin listesi.