diff --git a/AGENTS.md b/AGENTS.md
index 7593b11..11813cb 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -58,6 +58,10 @@ Fonts: IRANYekan (fa, local `public/fonts`) + Montserrat (latin, next/font).
mark, not موبایل معلم) — the wiring is correct and will pick up the right
asset automatically the moment it's replaced in the dashboard; don't
"fix" this by hardcoding the local fallback again.
+- Header/footer logo renders at `h-[108px]` (3x the original `h-9`/36px, on
+ request). If the wrong logo above still looks small at that size, it's
+ because that specific PNG has a lot of baked-in transparent padding around
+ the mark — `object-contain` can't fix that, only the replacement asset can.
- Contact data (phone, mobile, branch addresses, Instagram/Telegram) in
`src/data/site.ts` is real, pulled from the live theme's footer — `GET
/website/business-info` still returns empty `phoneNumbers`/`addresses`/
@@ -142,10 +146,21 @@ business's upload order lines up with this site's category sort order. It
doesn't, and there's no contract that it ever would — live data showed a
laptop photo (`linkUrl` correctly said `?category=laptop`) landing on
"موبایل" and "لوازم برقی" appearing with no photo uploaded for it at all,
-purely because of where it fell in the array. `titleFa` isn't reliable
-either — the same laptop image was titled "تبلت" (stale/copy-pasted text),
-contradicting its own `linkUrl`. The `linkUrl` query param was the one field
-that matched the actual photo content every time it was checked.
+purely because of where it fell in the array. The visible caption under each
+circle is that image's own `titleFa`, taken as typed — even on the one
+sample where it disagreed with the photo (that same laptop image was titled
+"تبلت"), it's still the business's own editorial text for the tile, and the
+`href` (built from the `linkUrl` slug, resolved to this site's own canonical
+`/products/category/{id}/{slug}` rather than linking off-site to the literal
+`linkUrl`) is what actually has to be correct for navigation to work.
+
+**Category circle hover:** use a real `border` (1px, transparent → `border-brand`
+on `group-hover`), not a `ring`/box-shadow. A ring rendered inside a Swiper
+slide painted like it was "leaking" into the row above — box-shadow doesn't
+participate in normal layout the way a border does, and interacts oddly with
+Swiper's transformed `.swiper-wrapper` track. `border` with this site's
+global `box-sizing: border-box` reset adds zero layout shift, so it's the
+safer default for any future hover-outline effect inside a carousel.
Homepage banner placement (mirrors the logilook.com reference given for this
layout): hero → categories → hot deals → **three-banner-bg** → موبایل
@@ -222,3 +237,14 @@ npm run dev # localhost:3000
npm run build
node scripts/gen-category-placeholders.mjs # refresh category thumb placeholders
```
+
+## Deployment
+
+`origin` is `gitea.meshkee.com/meshkee-websites/moalem-shop.git`, `main` branch.
+Whatever serves the business's preview/live URL deploys from `origin/main` —
+**committing locally is not enough**, push too. A whole session's worth of
+work (2026-09-06: about/contact/installments pages, the category-slot
+identity fix, live logo/favicon wiring) once sat 3 commits ahead of
+`origin/main` unpushed, and the business reported bugs that were already
+fixed locally — they were looking at the old deployed code. Check
+`git status` for "ahead of origin" before telling anyone a fix is live.
diff --git a/src/components/CategoryCarousel.tsx b/src/components/CategoryCarousel.tsx
index 96a6875..c9ebe10 100644
--- a/src/components/CategoryCarousel.tsx
+++ b/src/components/CategoryCarousel.tsx
@@ -12,10 +12,11 @@ import type { CategoryNode, StaticImageSlot } from "@/lib/types";
// Fully slot-driven: each image in the "categories" slot carries its own
// linkUrl (e.g. "...?category=mobile"), whose `category` param is the real
-// DB slug — that's what identifies which category a photo is for, not the
-// image's position in the list or its (business-typed, sometimes wrong)
-// titleFa. A category the business hasn't uploaded a photo for simply isn't
-// shown here — no placeholder, per the "only what's in the panel" brief.
+// DB slug — that's what identifies which category a photo is FOR (used to
+// build the real, in-site link target). The visible caption is the
+// business's own titleFa for that image, taken as typed — a category the
+// business hasn't uploaded a photo for simply isn't shown here, no
+// placeholder, per the "only what's in the panel" brief.
// See AGENTS.md "Static-image slots" for the mismatch this replaced.
export default function CategoryCarousel({
roots,
@@ -29,9 +30,10 @@ export default function CategoryCarousel({
const catSlug = categorySlugFromLinkUrl(img.linkUrl);
const cat = catSlug ? findNodeBySlug(roots, catSlug) : undefined;
if (!cat) return null;
- return { key: img.id, art: img.url, cat };
+ const label = img.titleFa || img.titleEn || cat.nameFa || cat.name;
+ return { key: img.id, art: img.url, label, href: categoryHref(cat.id, cat.nameFa, cat.name) };
})
- .filter((x): x is { key: string; art: string; cat: CategoryNode } => Boolean(x));
+ .filter((x): x is { key: string; art: string; label: string; href: string } => Boolean(x));
if (items.length === 0) return null;
@@ -49,21 +51,29 @@ export default function CategoryCarousel({
1024: { slidesPerView: 7 },
}}
>
- {items.map(({ key, art, cat }) => {
- const label = cat.nameFa || cat.name;
- return (
-
-
-
-
-
-
- {label}
-
-
-
- );
- })}
+ {items.map(({ key, art, label, href }) => (
+
+
+ {/* A real border, not ring/box-shadow — box-shadow rings can
+ paint oddly against Swiper's transformed slide track
+ (looked like the border "leaking" into the row above).
+ border-box sizing (this site's global reset) means the
+ 1px border adds no layout shift. */}
+
+
+
+
+ {label}
+
+
+
+ ))}
);
diff --git a/src/components/Footer.tsx b/src/components/Footer.tsx
index a6d4775..887a302 100644
--- a/src/components/Footer.tsx
+++ b/src/components/Footer.tsx
@@ -17,9 +17,15 @@ export default function Footer({