diff --git a/.claude/prompt/blog-city-scoping-activate.md b/.claude/prompt/blog-city-scoping-activate.md
new file mode 100644
index 0000000..03cf7f7
--- /dev/null
+++ b/.claude/prompt/blog-city-scoping-activate.md
@@ -0,0 +1,156 @@
+
+
+# فعالسازی بلاگ شهر-محور (بعد از افزودن شهر در backend)
+
+## پروژه
+
+`nobat724_front`
+
+**پیشنیاز قطعی:** `clinicpro/.claude/prompt/blog-city-field.md` اجرا و deploy شده باشد.
+تا وقتی `GET /api/v1/blogs` فیلد شهر برنگرداند، این پرامپت کاری برای انجام ندارد.
+
+## زمینه
+
+منطق شهر-محورِ بلاگ سمت فرانت **از قبل نوشته شده** ولی چون دادهاش وجود ندارد غیرفعال است. این پرامپت آن را فعال، تکمیل و تست میکند.
+
+وضعیت داده هنگام نگارش: `GET /api/v1/blogs` صفر رکورد دارد (`totalRecords: 0`) و هیچ فیلد شهری ندارد.
+
+## وضعیت فعلی (کدی که آمادهٔ فعالشدن است)
+
+`app/blog/[slug]/page.js` — canonical و شهر از همان هلپر مشترک:
+
+```js
+const blogCityId = extractEntityCityId(blog);
+const blogCity = findCityById(blogCityId);
+const origin = await getEntityOrigin(blogCityId);
+
+return {
+ title,
+ description,
+ alternates: { canonical: `${origin}/blog/${slug}` },
+ // ...
+};
+```
+
+JSON-LD مقاله فقط با وجود شهر `spatialCoverage` میگیرد:
+
+```js
+...(blogCity && {
+ spatialCoverage: { "@type": "Place", name: blogCity.name },
+}),
+```
+
+`components/blog/head/index.js` — برچسب بصری، فقط با `cityName`:
+
+```jsx
+{cityName && (
+
+
+ مخصوص شهر:
+
+
+ {cityName}
+
+
+)}
+```
+
+`lib/domainHelpers.js` — استخراج شهر، هر سه شکل پاسخ را میپذیرد:
+
+```js
+export function extractEntityCityId(entity) {
+ if (!entity) return null;
+ const candidates = [
+ entity.city_id,
+ ...(Array.isArray(entity.city) ? entity.city.map((c) => c?.id) : [entity.city?.id]),
+ ...(Array.isArray(entity.address) ? entity.address.map((a) => a?.city?.id) : []),
+ ].filter((id) => id != null);
+ const withDomain = candidates.find((id) => findDomainByCityId(id));
+ return withDomain ?? candidates[0] ?? null;
+}
+```
+
+`app/sitemap.js` — مسیریابی پست به sitemap دامنهٔ canonical خودش:
+
+```js
+async function getBlogUrls(baseUrl, scope, currentDomain) {
+ const blogs = await fetchAllPages('/api/v1/blogs');
+ return blogs
+ .filter((b) => b?.slug || b?.uuid)
+ .filter((b) => {
+ const cityDomain = findDomainByCityId(extractEntityCityId(b));
+ return cityDomain ? cityDomain === currentDomain : scope.isRoot;
+ })
+ // ...
+}
+```
+
+## وظایف
+
+### ۱. تأیید سازگاری شکل پاسخ
+
+اول پاسخ واقعی را ببین:
+
+```bash
+curl -s "https://clinic-pro.ir/api/v1/blogs?page=1&limit=5" | jq '.data.data[0]'
+```
+
+بررسی کن `extractEntityCityId` روی شکل واقعی جواب میدهد. اگر backend شکل دیگری داد (مثلاً `city` رشته بهجای آبجکت)، `extractEntityCityId` را گسترش بده — **نه** یک استخراج جدا برای بلاگ بنویس.
+
+`normalizeBlog` در `helper/index.js` نباید فیلد شهر را حذف کند؛ اگر فیلدها را صریح map میکند، `city` را اضافه کن.
+
+### ۲. لیست بلاگ per-domain
+
+`app/blogs/page.js` روی هر دامنهٔ شهری باید پستهای آن شهر + سراسری را بگیرد. با پارامتر `city_id` که backend اضافه میکند:
+
+- روی دامنهٔ شهری: `city_id` شهر همان دامنه (از `getStateInfo`)
+- روی دامنهٔ ریشه (`isRoot`): بدون پارامتر — همهٔ پستها
+
+canonical لیست بلاگ **self روی همان دامنه** بماند (C1-a). این از قبل درست است — کانونیکال hardcode شده به دامنهٔ اصلی قبلاً حذف شده:
+
+```js
+// C1-a — لیست بلاگ per-domain است (پستهای همان شهر + سراسری)؛
+// canonical لایهٔ layout (self روی همان دامنه) درست است.
+```
+
+### ۳. عنوان و متادیتای شهرمحور
+
+`generateMetadata` صفحهٔ پست: اگر پست شهر دارد، نام شهر در `title`/`description` بیاید. اگر سراسری است، هیچ نام شهری اضافه نشود (نه نام شهرِ دامنهٔ سروکننده).
+
+### ۴. تست
+
+با دادهٔ واقعی (حداقل یک پست شهری + یک پست سراسری) تأیید کن:
+
+```bash
+S=
+G=
+
+# پست شهری: canonical به دامنهٔ شهر، روی هر دامنهای
+curl -s "https://yasuj-nobat.ir/blog/$S" | grep -o 'rel="canonical" href="[^"]*"' # → yasuj-nobat.ir
+curl -s "https://nobat724.com/blog/$S" | grep -o 'rel="canonical" href="[^"]*"' # → yasuj-nobat.ir
+curl -s "https://yasuj-nobat.ir/blog/$S" | grep -o "یاسوج" # نام شهر در HTML قابلمشاهده
+curl -s "https://yasuj-nobat.ir/blog/$S" | grep -o 'spatialCoverage'
+
+# پست سراسری: self-canonical، بدون نام شهر
+curl -s "https://nobat724.com/blog/$G" | grep -o 'rel="canonical" href="[^"]*"' # → nobat724.com
+curl -s "https://yasuj-nobat.ir/blog/$G" | grep -c 'spatialCoverage' # → 0
+
+# sitemap: هر پست فقط در دامنهٔ canonical خودش
+curl -s https://yasuj-nobat.ir/sitemap.xml | grep -c "$S" # → 1
+curl -s https://nobat724.com/sitemap.xml | grep -c "$S" # → 0
+curl -s https://nobat724.com/sitemap.xml | grep -c "$G" # → 1
+curl -s https://nobat724.com/sitemap.xml | grep -c "/blog/" # → بیشتر از ۰
+```
+
+تست واحد برای `extractEntityCityId` روی شکل واقعی پاسخ بلاگ به `lib/domainHelpers.test.js` اضافه کن.
+
+## نکات مهم
+
+- **پست سراسری حالت دائمی است، نه استثنا.** پستی بدون شهر باید روی دامنهٔ اصلی self-canonical شود و در همهٔ لیستها دیده شود. هرگز شهرِ دامنهٔ سروکننده را به پست بدون شهر نسبت نده.
+- `findCityById` رکورد ریشه (`id: 600` = `nobat724.com`) را شهر حساب نمیکند و `null` برمیگرداند — یعنی پستی که بهاشتباه به رکورد ریشه وصل شده، سراسری تلقی میشود. رفتار درست است.
+- برچسب بصری شهر با تایپوگرافی «نویسنده/تاریخ» همخانواده است؛ عنصر بصری ناهماهنگ اضافه نکن.
+- الگوی Soft-404 (`app/blog/[slug]/page.js`) بعد از تغییرات همچنان باید کار کند:
+ `curl -o /dev/null -w "%{http_code}" https://nobat724.com/blog/invalid-xxx` → `404`.
+ توجه: `app/blog/[slug]/loading.js` عمداً حذف شده — دوباره اضافهاش نکن، وگرنه Soft-404 برمیگردد.
+
+
diff --git a/.claude/prompt/seo-critical-fixes-live-audit.md b/.claude/prompt/seo-critical-fixes-live-audit.md
new file mode 100644
index 0000000..c7e68d6
--- /dev/null
+++ b/.claude/prompt/seo-critical-fixes-live-audit.md
@@ -0,0 +1,464 @@
+