ui updated and logo added
This commit is contained in:
parent
85cddaf9a6
commit
2daf634260
|
|
@ -31,6 +31,7 @@
|
||||||
"stackAlt": "የተከማቹ የብረት መገለጫዎች ምስል፦ ቦረት፣ ፍላት ባር፣ ቧንቧ እና ባዶ ክፍል"
|
"stackAlt": "የተከማቹ የብረት መገለጫዎች ምስል፦ ቦረት፣ ፍላት ባር፣ ቧንቧ እና ባዶ ክፍል"
|
||||||
},
|
},
|
||||||
"services": {
|
"services": {
|
||||||
|
"eyebrow": "ልዩ ብቃታችን",
|
||||||
"title": "አገልግሎቶቻችን",
|
"title": "አገልግሎቶቻችን",
|
||||||
"subtitle": "ለኢንዱስትሪ ደረጃ ቁሳቁሶች፣ ሰነዶች፣ ሎጂስቲክስ እና የፕሮጀክት ድጋፍ ከአንድ ሰርተጅ።",
|
"subtitle": "ለኢንዱስትሪ ደረጃ ቁሳቁሶች፣ ሰነዶች፣ ሎጂስቲክስ እና የፕሮጀክት ድጋፍ ከአንድ ሰርተጅ።",
|
||||||
"cut": {
|
"cut": {
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,7 @@
|
||||||
"stackAlt": "Illustration of stacked steel profiles: rebar, flat bar, pipe, and hollow section"
|
"stackAlt": "Illustration of stacked steel profiles: rebar, flat bar, pipe, and hollow section"
|
||||||
},
|
},
|
||||||
"services": {
|
"services": {
|
||||||
|
"eyebrow": "Our Expertise",
|
||||||
"title": "Our services",
|
"title": "Our services",
|
||||||
"subtitle": "Industrial-grade materials, documentation, logistics, and project support from one desk.",
|
"subtitle": "Industrial-grade materials, documentation, logistics, and project support from one desk.",
|
||||||
"cut": {
|
"cut": {
|
||||||
|
|
|
||||||
BIN
public/Logo/TrustLogo-removebg-preview.png
Normal file
BIN
public/Logo/TrustLogo-removebg-preview.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 46 KiB |
|
|
@ -12,7 +12,7 @@ export default async function CartPage() {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="mx-auto max-w-3xl px-4 py-10 sm:px-6 lg:px-8">
|
<div className="mx-auto max-w-3xl px-4 py-10 sm:px-6 lg:px-8">
|
||||||
<h1 className="text-3xl font-semibold tracking-tight text-neutral-900">
|
<h1 className="text-4xl font-semibold tracking-tight text-brand-navy">
|
||||||
{t("title")}
|
{t("title")}
|
||||||
</h1>
|
</h1>
|
||||||
<div className="mt-8">
|
<div className="mt-8">
|
||||||
|
|
|
||||||
|
|
@ -11,10 +11,10 @@ export default async function CheckoutPage() {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="mx-auto max-w-2xl px-4 py-10 sm:px-6 lg:px-8">
|
<div className="mx-auto max-w-2xl px-4 py-10 sm:px-6 lg:px-8">
|
||||||
<h1 className="text-3xl font-semibold tracking-tight text-neutral-900">
|
<h1 className="text-4xl font-semibold tracking-tight text-brand-navy">
|
||||||
{t("title")}
|
{t("title")}
|
||||||
</h1>
|
</h1>
|
||||||
<p className="mt-2 text-neutral-600">{t("subtitle")}</p>
|
<p className="mt-3 text-base font-normal text-neutral-600">{t("subtitle")}</p>
|
||||||
<div className="mt-8">
|
<div className="mt-8">
|
||||||
<CheckoutForm />
|
<CheckoutForm />
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -8,34 +8,35 @@ export default async function HomePage() {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<section className="relative overflow-hidden border-b border-neutral-200/90 bg-gradient-to-b from-white to-neutral-50">
|
{/* 1. Hero -> bg-brand-navy */}
|
||||||
<div className="mx-auto grid max-w-[1440px] gap-10 px-4 py-14 sm:px-6 lg:grid-cols-2 lg:items-center lg:py-20 lg:px-8">
|
<section className="relative overflow-hidden bg-brand-navy">
|
||||||
|
<div className="mx-auto grid max-w-7xl gap-10 px-4 py-16 sm:px-6 lg:grid-cols-2 lg:items-center lg:py-20 lg:px-8">
|
||||||
<div>
|
<div>
|
||||||
<p className="text-[11px] font-semibold uppercase tracking-widest text-neutral-400">
|
<p className="text-[11px] font-semibold uppercase tracking-widest text-brand-gold">
|
||||||
{t("hero.eyebrow")}
|
{t("hero.eyebrow")}
|
||||||
</p>
|
</p>
|
||||||
<h1 className="mt-3 text-3xl font-semibold tracking-tight text-neutral-900 sm:text-4xl lg:text-5xl">
|
<h1 className="mt-3 text-4xl font-semibold tracking-tight text-white sm:text-5xl lg:text-6xl">
|
||||||
{t("hero.title")}
|
{t("hero.title")}
|
||||||
</h1>
|
</h1>
|
||||||
<p className="mt-4 text-base leading-relaxed text-neutral-600 sm:text-lg">
|
<p className="mt-4 text-base font-normal leading-relaxed text-brand-navy100 sm:text-lg">
|
||||||
{t("hero.subtitle")}
|
{t("hero.subtitle")}
|
||||||
</p>
|
</p>
|
||||||
<div className="mt-8 flex flex-col gap-3 sm:flex-row">
|
<div className="mt-8 flex flex-col gap-3 sm:flex-row">
|
||||||
<Link
|
<Link
|
||||||
href="/catalog"
|
href="/catalog"
|
||||||
className="inline-flex items-center justify-center rounded-lg bg-neutral-900 px-5 py-3.5 text-sm font-semibold text-white transition hover:bg-neutral-800"
|
className="inline-flex items-center justify-center rounded-lg bg-brand-gold px-6 py-3 text-sm font-semibold text-brand-navy transition duration-150 hover:bg-brand-gold700"
|
||||||
>
|
>
|
||||||
{t("hero.ctaCatalog")}
|
{t("hero.ctaCatalog")}
|
||||||
</Link>
|
</Link>
|
||||||
<Link
|
<Link
|
||||||
href="/checkout"
|
href="/checkout"
|
||||||
className="inline-flex items-center justify-center rounded-lg border border-neutral-200 bg-white px-5 py-3.5 text-sm font-semibold text-neutral-600 transition hover:border-neutral-300 hover:text-neutral-900"
|
className="inline-flex items-center justify-center rounded-lg border border-white/30 px-6 py-3 text-sm font-semibold text-white transition duration-150 hover:border-white hover:bg-white/5"
|
||||||
>
|
>
|
||||||
{t("hero.ctaQuote")}
|
{t("hero.ctaQuote")}
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="relative aspect-[4/3] overflow-hidden rounded-lg bg-[#e2e8f0] border border-neutral-200">
|
<div className="relative aspect-[4/3] overflow-hidden rounded-lg border border-white/10 bg-brand-navy600/20">
|
||||||
<Image
|
<Image
|
||||||
src="/metal/hero-stack.svg"
|
src="/metal/hero-stack.svg"
|
||||||
alt={th("stackAlt")}
|
alt={th("stackAlt")}
|
||||||
|
|
@ -49,20 +50,28 @@ export default async function HomePage() {
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
{/* 2. Trust bar missing - move to Services */}
|
||||||
|
|
||||||
|
{/* 3. Services -> bg-neutral-100 */}
|
||||||
<section
|
<section
|
||||||
id="services"
|
id="services"
|
||||||
className="border-y border-neutral-200/80 bg-neutral-50/80"
|
className="relative bg-neutral-100"
|
||||||
aria-labelledby="services-heading"
|
aria-labelledby="services-heading"
|
||||||
>
|
>
|
||||||
<div className="mx-auto max-w-6xl px-4 py-16 sm:px-6 lg:px-8">
|
<div className="mx-auto max-w-7xl px-4 py-20 sm:px-6 lg:px-8">
|
||||||
|
<p className="text-[11px] font-semibold uppercase tracking-widest text-brand-gold">
|
||||||
|
{t("services.eyebrow") || "Our Services"}
|
||||||
|
</p>
|
||||||
<h2
|
<h2
|
||||||
id="services-heading"
|
id="services-heading"
|
||||||
className="text-2xl font-semibold text-neutral-900 sm:text-3xl"
|
className="mt-3 text-3xl font-semibold text-brand-navy"
|
||||||
>
|
>
|
||||||
{t("services.title")}
|
{t("services.title")}
|
||||||
</h2>
|
</h2>
|
||||||
<p className="mt-2 max-w-2xl text-neutral-600">{t("services.subtitle")}</p>
|
<p className="mt-4 max-w-2xl text-sm font-normal text-neutral-700">
|
||||||
<ul className="mt-10 grid gap-6 sm:grid-cols-2 lg:grid-cols-3">
|
{t("services.subtitle")}
|
||||||
|
</p>
|
||||||
|
<ul className="mt-12 grid gap-8 sm:grid-cols-2 lg:grid-cols-3">
|
||||||
{(
|
{(
|
||||||
[
|
[
|
||||||
"cut",
|
"cut",
|
||||||
|
|
@ -75,9 +84,9 @@ export default async function HomePage() {
|
||||||
).map((key) => (
|
).map((key) => (
|
||||||
<li
|
<li
|
||||||
key={key}
|
key={key}
|
||||||
className="rounded-lg bg-white p-5 border border-neutral-100"
|
className="group relative rounded-lg border border-neutral-200 bg-neutral-0 p-6 border-l-2 border-l-brand-gold transition duration-150 hover:border-neutral-300"
|
||||||
>
|
>
|
||||||
<div className="flex h-10 w-10 items-center justify-center rounded-lg bg-neutral-900 text-white">
|
<div className="flex h-10 w-10 items-center justify-center rounded-lg bg-brand-gold50 text-brand-gold">
|
||||||
{key === "cut" && (
|
{key === "cut" && (
|
||||||
<svg className="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={1.5}>
|
<svg className="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={1.5}>
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" d="M14.121 14.121L19 19m-7-7l7-7m-7 7l-2.879 2.879M12 12L9.121 9.121m0 5.758a3 3 0 11-4.243 4.243 3 3 0 014.243-4.243zm0-5.758a3 3 0 11-4.243-4.243 3 3 0 014.243 4.243z" />
|
<path strokeLinecap="round" strokeLinejoin="round" d="M14.121 14.121L19 19m-7-7l7-7m-7 7l-2.879 2.879M12 12L9.121 9.121m0 5.758a3 3 0 11-4.243 4.243 3 3 0 014.243-4.243zm0-5.758a3 3 0 11-4.243-4.243 3 3 0 014.243 4.243z" />
|
||||||
|
|
@ -109,10 +118,10 @@ export default async function HomePage() {
|
||||||
</svg>
|
</svg>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<h3 className="mt-4 text-lg font-semibold text-neutral-900">
|
<h3 className="mt-4 text-base font-semibold text-brand-navy">
|
||||||
{t(`services.${key}.title`)}
|
{t(`services.${key}.title`)}
|
||||||
</h3>
|
</h3>
|
||||||
<p className="mt-2 text-sm leading-relaxed text-neutral-600">
|
<p className="mt-2 text-sm font-normal leading-relaxed text-neutral-600">
|
||||||
{t(`services.${key}.body`)}
|
{t(`services.${key}.body`)}
|
||||||
</p>
|
</p>
|
||||||
</li>
|
</li>
|
||||||
|
|
@ -121,25 +130,29 @@ export default async function HomePage() {
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section className="bg-white">
|
{/* 4. About / How it works -> bg-neutral-0 */}
|
||||||
<div className="mx-auto max-w-6xl px-4 py-16 sm:px-6 lg:px-8">
|
<section className="bg-neutral-0">
|
||||||
<h2 className="text-2xl font-semibold text-neutral-900 sm:text-3xl">
|
<div className="mx-auto max-w-7xl px-4 py-20 sm:px-6 lg:px-8">
|
||||||
|
<p className="text-[11px] font-semibold uppercase tracking-widest text-brand-gold">
|
||||||
|
Process
|
||||||
|
</p>
|
||||||
|
<h2 className="mt-3 text-3xl font-semibold text-brand-navy">
|
||||||
{t("how.title")}
|
{t("how.title")}
|
||||||
</h2>
|
</h2>
|
||||||
<p className="mt-2 text-neutral-600">{t("how.subtitle")}</p>
|
<p className="mt-4 text-sm font-normal text-neutral-600">{t("how.subtitle")}</p>
|
||||||
<ol className="mt-10 grid gap-6 sm:grid-cols-2 lg:grid-cols-4">
|
<ol className="mt-12 grid gap-8 sm:grid-cols-2 lg:grid-cols-4">
|
||||||
{([1, 2, 3, 4] as const).map((n) => (
|
{([1, 2, 3, 4] as const).map((n) => (
|
||||||
<li
|
<li
|
||||||
key={n}
|
key={n}
|
||||||
className="relative rounded-lg border border-neutral-100 bg-neutral-50/80 p-5"
|
className="relative rounded-lg border border-neutral-200 bg-neutral-0 p-6 transition duration-150 hover:border-neutral-300"
|
||||||
>
|
>
|
||||||
<span className="text-3xl font-semibold text-neutral-300">
|
<span className="text-3xl font-semibold text-neutral-200">
|
||||||
{n}
|
0{n}
|
||||||
</span>
|
</span>
|
||||||
<h3 className="mt-2 font-semibold text-neutral-900">
|
<h3 className="mt-4 text-base font-semibold text-brand-navy">
|
||||||
{t(`how.step${n}.title` as "how.step1.title")}
|
{t(`how.step${n}.title` as "how.step1.title")}
|
||||||
</h3>
|
</h3>
|
||||||
<p className="mt-2 text-sm text-neutral-600">
|
<p className="mt-2 text-sm font-normal text-neutral-600">
|
||||||
{t(`how.step${n}.body` as "how.step1.body")}
|
{t(`how.step${n}.body` as "how.step1.body")}
|
||||||
</p>
|
</p>
|
||||||
</li>
|
</li>
|
||||||
|
|
@ -148,27 +161,31 @@ export default async function HomePage() {
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
{/* 5. FAQ -> bg-neutral-100 */}
|
||||||
<section
|
<section
|
||||||
id="faq"
|
id="faq"
|
||||||
className="border-t border-neutral-100 bg-neutral-50/80"
|
className="bg-neutral-100"
|
||||||
aria-labelledby="faq-heading"
|
aria-labelledby="faq-heading"
|
||||||
>
|
>
|
||||||
<div className="mx-auto max-w-6xl px-4 py-16 sm:px-6 lg:px-8">
|
<div className="mx-auto max-w-7xl px-4 py-20 sm:px-6 lg:px-8">
|
||||||
<div className="mx-auto max-w-3xl">
|
<div className="mx-auto max-w-3xl">
|
||||||
|
<p className="text-[11px] font-semibold uppercase tracking-widest text-brand-gold text-center">
|
||||||
|
Questions
|
||||||
|
</p>
|
||||||
<h2
|
<h2
|
||||||
id="faq-heading"
|
id="faq-heading"
|
||||||
className="text-2xl font-semibold text-neutral-900 sm:text-3xl"
|
className="mt-3 text-3xl font-semibold text-brand-navy text-center"
|
||||||
>
|
>
|
||||||
{t("faq.title")}
|
{t("faq.title")}
|
||||||
</h2>
|
</h2>
|
||||||
<p className="mt-2 text-neutral-600">{t("faq.subtitle")}</p>
|
<p className="mt-4 text-center text-sm font-normal text-neutral-700">{t("faq.subtitle")}</p>
|
||||||
<div className="mt-10 space-y-3">
|
<div className="mt-12 space-y-4">
|
||||||
{([1, 2, 3, 4, 5, 6] as const).map((n) => (
|
{([1, 2, 3, 4, 5, 6] as const).map((n) => (
|
||||||
<details
|
<details
|
||||||
key={n}
|
key={n}
|
||||||
className="group rounded-lg border border-neutral-200 bg-white px-4 py-1"
|
className="group rounded-lg border border-neutral-200 bg-neutral-0 px-6 py-2 transition duration-150"
|
||||||
>
|
>
|
||||||
<summary className="flex cursor-pointer list-none items-center justify-between gap-3 py-3 text-left text-sm font-semibold text-neutral-900 [&::-webkit-details-marker]:hidden">
|
<summary className="flex cursor-pointer list-none items-center justify-between gap-3 py-3 text-left text-sm font-semibold text-brand-navy [&::-webkit-details-marker]:hidden">
|
||||||
<span>{t(`faq.i${n}.q` as "faq.i1.q")}</span>
|
<span>{t(`faq.i${n}.q` as "faq.i1.q")}</span>
|
||||||
<svg
|
<svg
|
||||||
className="faq-chevron h-5 w-5 shrink-0 text-neutral-400 transition-transform duration-200"
|
className="faq-chevron h-5 w-5 shrink-0 text-neutral-400 transition-transform duration-200"
|
||||||
|
|
@ -183,9 +200,9 @@ export default async function HomePage() {
|
||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
</summary>
|
</summary>
|
||||||
<p className="border-t border-neutral-100 pb-4 pt-3 text-sm leading-relaxed text-neutral-600">
|
<div className="border-t border-neutral-100 pb-5 pt-4 text-sm font-normal leading-relaxed text-neutral-600">
|
||||||
{t(`faq.i${n}.a` as "faq.i1.a")}
|
{t(`faq.i${n}.a` as "faq.i1.a")}
|
||||||
</p>
|
</div>
|
||||||
</details>
|
</details>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -193,27 +210,35 @@ export default async function HomePage() {
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section className="mx-auto max-w-6xl px-4 py-16 sm:px-6 lg:px-8">
|
{/* 6. CTA Band -> bg-neutral-100 */}
|
||||||
<div className="overflow-hidden rounded-lg bg-neutral-900 px-6 py-10 text-center text-white sm:px-12">
|
<section className="bg-neutral-100 py-24 sm:py-32 border-t border-neutral-200">
|
||||||
<h2 className="text-2xl font-semibold sm:text-3xl">{t("ctaBand.title")}</h2>
|
<div className="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
|
||||||
<p className="mx-auto mt-3 max-w-xl text-sm text-neutral-400 sm:text-base">
|
<div className="text-center">
|
||||||
|
<p className="text-[11px] font-semibold uppercase tracking-widest text-brand-gold">
|
||||||
|
Get Started
|
||||||
|
</p>
|
||||||
|
<h2 className="mt-3 text-3xl font-semibold text-brand-navy sm:text-4xl">
|
||||||
|
{t("ctaBand.title")}
|
||||||
|
</h2>
|
||||||
|
<p className="mx-auto mt-4 max-w-xl text-base font-normal text-neutral-600">
|
||||||
{t("ctaBand.body")}
|
{t("ctaBand.body")}
|
||||||
</p>
|
</p>
|
||||||
<div className="mt-8 flex flex-col justify-center gap-3 sm:flex-row">
|
<div className="mt-10 flex flex-col justify-center gap-4 sm:flex-row">
|
||||||
<Link
|
<Link
|
||||||
href="/checkout"
|
href="/checkout"
|
||||||
className="inline-flex justify-center rounded-lg bg-white px-6 py-3.5 text-sm font-semibold text-neutral-900 transition hover:bg-neutral-100"
|
className="inline-flex justify-center rounded-lg bg-brand-navy px-10 py-4 text-sm font-semibold text-white transition duration-150 hover:bg-brand-navy800"
|
||||||
>
|
>
|
||||||
{t("ctaBand.primary")}
|
{t("ctaBand.primary")}
|
||||||
</Link>
|
</Link>
|
||||||
<a
|
<a
|
||||||
href={`tel:${(process.env.NEXT_PUBLIC_CONTACT_PHONE ?? "+251911000000").replace(/\s/g, "")}`}
|
href={`tel:${(process.env.NEXT_PUBLIC_CONTACT_PHONE ?? "+251911000000").replace(/\s/g, "")}`}
|
||||||
className="inline-flex justify-center rounded-lg border border-white/20 bg-white/10 px-6 py-3.5 text-sm font-semibold text-white transition hover:bg-white/20"
|
className="inline-flex justify-center rounded-lg border border-neutral-200 bg-white px-10 py-4 text-sm font-semibold text-brand-navy transition duration-150 hover:border-neutral-300"
|
||||||
>
|
>
|
||||||
{t("ctaBand.secondary")}
|
{t("ctaBand.secondary")}
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</section>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -39,7 +39,7 @@ export default async function ProductPage({ params }: Props) {
|
||||||
<div className="mx-auto max-w-[1440px] px-4 py-10 sm:px-6 lg:px-8">
|
<div className="mx-auto max-w-[1440px] px-4 py-10 sm:px-6 lg:px-8">
|
||||||
<Link
|
<Link
|
||||||
href="/catalog"
|
href="/catalog"
|
||||||
className="text-sm font-medium text-neutral-500 transition hover:text-neutral-900"
|
className="inline-flex items-center text-sm font-semibold text-neutral-500 transition hover:text-brand-navy"
|
||||||
>
|
>
|
||||||
← {t("back")}
|
← {t("back")}
|
||||||
</Link>
|
</Link>
|
||||||
|
|
@ -47,12 +47,12 @@ export default async function ProductPage({ params }: Props) {
|
||||||
<div className="mt-8 grid gap-10 lg:grid-cols-2 lg:gap-16">
|
<div className="mt-8 grid gap-10 lg:grid-cols-2 lg:gap-16">
|
||||||
<div>
|
<div>
|
||||||
<ProductGallery slug={slug} sources={product.gallery} />
|
<ProductGallery slug={slug} sources={product.gallery} />
|
||||||
<div className="mt-6">
|
<div className="mt-8">
|
||||||
<p className="text-[11px] font-semibold uppercase tracking-widest text-neutral-400">
|
<p className="text-[11px] font-semibold uppercase tracking-widest text-brand-gold">
|
||||||
{t("thicknessLabel")}
|
{t("thicknessLabel")}
|
||||||
</p>
|
</p>
|
||||||
<div
|
<div
|
||||||
className="mt-2 flex flex-wrap gap-2"
|
className="mt-3 flex flex-wrap gap-2"
|
||||||
role="list"
|
role="list"
|
||||||
aria-label={tc("thicknessList")}
|
aria-label={tc("thicknessList")}
|
||||||
>
|
>
|
||||||
|
|
@ -60,7 +60,7 @@ export default async function ProductPage({ params }: Props) {
|
||||||
<span
|
<span
|
||||||
key={mm}
|
key={mm}
|
||||||
role="listitem"
|
role="listitem"
|
||||||
className="inline-flex rounded-md border border-neutral-200 bg-neutral-50 px-3 py-1.5 text-sm font-semibold tabular-nums text-neutral-800"
|
className="inline-flex rounded-lg border border-neutral-200 bg-neutral-50 px-3 py-1.5 text-sm font-semibold tabular-nums text-neutral-800 transition hover:border-neutral-300"
|
||||||
>
|
>
|
||||||
{t("thicknessValue", {
|
{t("thicknessValue", {
|
||||||
value: formatThicknessMm(mm),
|
value: formatThicknessMm(mm),
|
||||||
|
|
@ -72,34 +72,34 @@ export default async function ProductPage({ params }: Props) {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<p className="text-[11px] font-semibold uppercase tracking-widest text-neutral-400">
|
<p className="text-[11px] font-semibold uppercase tracking-widest text-brand-gold">
|
||||||
{tcl(product.catalogLineKey)}
|
{tcl(product.catalogLineKey)}
|
||||||
</p>
|
</p>
|
||||||
<p className="mt-2 text-sm font-semibold text-neutral-900">
|
<p className="mt-2 text-sm font-semibold text-neutral-700">
|
||||||
{tn("supplier")}
|
{tn("supplier")}
|
||||||
</p>
|
</p>
|
||||||
<h1 className="mt-1 text-2xl font-semibold leading-tight tracking-tight text-neutral-900 sm:text-3xl lg:text-4xl">
|
<h1 className="mt-1 text-3xl font-semibold leading-tight tracking-tight text-brand-navy sm:text-4xl lg:text-5xl">
|
||||||
{tp(`${slug}.name`)}
|
{tp(`${slug}.name`)}
|
||||||
</h1>
|
</h1>
|
||||||
<p className="mt-4 text-base leading-relaxed text-neutral-600">
|
<p className="mt-4 text-base font-normal leading-relaxed text-neutral-600">
|
||||||
{tp(`${slug}.short`)}
|
{tp(`${slug}.short`)}
|
||||||
</p>
|
</p>
|
||||||
<p className="mt-6 text-xl font-semibold text-neutral-900">
|
<p className="mt-6 text-2xl font-semibold text-brand-navy">
|
||||||
{product.pricePerUnit.toFixed(2)}{" "}
|
{product.pricePerUnit.toFixed(2)}{" "}
|
||||||
<span className="text-sm font-medium text-neutral-500">
|
<span className="text-sm font-normal text-neutral-500">
|
||||||
{tu(product.unitKey)}
|
{tu(product.unitKey)}
|
||||||
</span>
|
</span>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<div className="mt-8">
|
<div className="mt-10">
|
||||||
<ProductAddToCart product={product} />
|
<ProductAddToCart product={product} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="mt-8 rounded-lg border border-neutral-100 bg-neutral-50/80 p-5">
|
<div className="mt-10 rounded-lg border border-neutral-200 bg-neutral-50 p-6">
|
||||||
<h2 className="text-[11px] font-semibold uppercase tracking-widest text-neutral-400">
|
<h2 className="text-[11px] font-semibold uppercase tracking-widest text-brand-gold">
|
||||||
{t("details")}
|
{t("details")}
|
||||||
</h2>
|
</h2>
|
||||||
<p className="mt-2 text-sm leading-relaxed text-neutral-600">
|
<p className="mt-3 text-sm font-normal leading-relaxed text-neutral-700">
|
||||||
{tp(`${slug}.short`)}
|
{tp(`${slug}.short`)}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -9,35 +9,32 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
@theme inline {
|
@theme inline {
|
||||||
--color-background: var(--background);
|
--color-brand-navy: #1E2263;
|
||||||
--color-foreground: var(--foreground);
|
--color-brand-navy800: #2C3390;
|
||||||
|
--color-brand-navy600: #5C64B8;
|
||||||
|
--color-brand-navy100: #CDD0EE;
|
||||||
|
--color-brand-navy50: #EEEFFE;
|
||||||
|
--color-brand-gold: #E8A020;
|
||||||
|
--color-brand-gold700: #B07800;
|
||||||
|
--color-brand-gold300: #F4C56A;
|
||||||
|
--color-brand-gold50: #FEF8EC;
|
||||||
|
|
||||||
|
--color-neutral-0: #FFFFFF;
|
||||||
|
--color-neutral-50: #F9F9F9;
|
||||||
|
--color-neutral-100: #F0F0F0;
|
||||||
|
--color-neutral-200: #E0E0E0;
|
||||||
|
--color-neutral-300: #C2C2C2;
|
||||||
|
--color-neutral-400: #9E9E9E;
|
||||||
|
--color-neutral-500: #767676;
|
||||||
|
--color-neutral-600: #545454;
|
||||||
|
--color-neutral-700: #3D3D3D;
|
||||||
|
--color-neutral-800: #242424;
|
||||||
|
--color-neutral-900: #111111;
|
||||||
|
|
||||||
--font-sans: var(--font-dm-sans), ui-sans-serif, system-ui, sans-serif;
|
--font-sans: var(--font-dm-sans), ui-sans-serif, system-ui, sans-serif;
|
||||||
|
|
||||||
/* Navy (lower chroma + lightness than default Tailwind blue) */
|
--radius-lg: 0.5rem;
|
||||||
--color-blue-50: oklch(96.5% 0.012 262);
|
--radius-md: 0.375rem;
|
||||||
--color-blue-100: oklch(90.5% 0.028 262);
|
|
||||||
--color-blue-200: oklch(83% 0.045 262);
|
|
||||||
--color-blue-300: oklch(70% 0.07 262);
|
|
||||||
--color-blue-400: oklch(56% 0.095 262);
|
|
||||||
--color-blue-500: oklch(44% 0.11 262);
|
|
||||||
--color-blue-600: oklch(36% 0.105 262);
|
|
||||||
--color-blue-700: oklch(30% 0.09 262);
|
|
||||||
--color-blue-800: oklch(25% 0.075 262);
|
|
||||||
--color-blue-900: oklch(21% 0.06 262);
|
|
||||||
--color-blue-950: oklch(16% 0.045 262);
|
|
||||||
|
|
||||||
/* Burnt orange (darker than default orange-500/400) */
|
|
||||||
--color-orange-50: oklch(97% 0.018 55);
|
|
||||||
--color-orange-100: oklch(92.5% 0.038 52);
|
|
||||||
--color-orange-200: oklch(86% 0.065 48);
|
|
||||||
--color-orange-300: oklch(74% 0.1 46);
|
|
||||||
--color-orange-400: oklch(62% 0.14 44);
|
|
||||||
--color-orange-500: oklch(52% 0.155 42);
|
|
||||||
--color-orange-600: oklch(44% 0.14 40);
|
|
||||||
--color-orange-700: oklch(37% 0.12 39);
|
|
||||||
--color-orange-800: oklch(31% 0.095 38);
|
|
||||||
--color-orange-900: oklch(26% 0.075 37);
|
|
||||||
--color-orange-950: oklch(19% 0.05 36);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
|
|
@ -57,3 +54,14 @@ body {
|
||||||
details[open] .faq-chevron {
|
details[open] .faq-chevron {
|
||||||
transform: rotate(180deg);
|
transform: rotate(180deg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.bg-grid {
|
||||||
|
background-size: 60px 80px;
|
||||||
|
background-image:
|
||||||
|
linear-gradient(to right, oklch(9.693% 0.00001 271.152 / 0.03) 1px, transparent 1px),
|
||||||
|
linear-gradient(to bottom, oklch(0% 0 0 / 0.03) 1px, transparent 1px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.bg-grid-mask {
|
||||||
|
mask-image: radial-gradient(circle at center, black, transparent 80%);
|
||||||
|
}
|
||||||
|
|
@ -9,7 +9,7 @@ export function CartBadge() {
|
||||||
|
|
||||||
if (count === 0) return null;
|
if (count === 0) return null;
|
||||||
return (
|
return (
|
||||||
<span className="absolute -right-1.5 -top-1.5 flex h-5 min-w-5 items-center justify-center rounded-full bg-neutral-900 px-1 text-[10px] font-semibold text-white">
|
<span className="absolute -right-1.5 -top-1.5 flex h-5 min-w-5 items-center justify-center rounded-full bg-brand-gold px-1 text-[10px] font-semibold text-white">
|
||||||
{count > 99 ? "99+" : count}
|
{count > 99 ? "99+" : count}
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ export function CartCheckoutLink() {
|
||||||
<div className="mt-8 flex justify-end">
|
<div className="mt-8 flex justify-end">
|
||||||
<Link
|
<Link
|
||||||
href="/checkout"
|
href="/checkout"
|
||||||
className="inline-flex rounded-lg bg-neutral-900 px-6 py-3 text-sm font-semibold text-white transition hover:bg-neutral-800"
|
className="inline-flex rounded-lg bg-brand-navy px-8 py-3.5 text-sm font-semibold text-white transition hover:bg-brand-navy800"
|
||||||
>
|
>
|
||||||
{t("checkout")}
|
{t("checkout")}
|
||||||
</Link>
|
</Link>
|
||||||
|
|
|
||||||
|
|
@ -17,11 +17,11 @@ export function CartView() {
|
||||||
|
|
||||||
if (items.length === 0) {
|
if (items.length === 0) {
|
||||||
return (
|
return (
|
||||||
<div className="mx-auto max-w-lg rounded-lg border border-neutral-100 bg-white p-10 text-center">
|
<div className="mx-auto max-w-lg rounded-lg border border-neutral-200 bg-neutral-50 p-10 text-center">
|
||||||
<p className="text-neutral-600">{t("empty")}</p>
|
<p className="text-sm font-normal text-neutral-600">{t("empty")}</p>
|
||||||
<Link
|
<Link
|
||||||
href="/catalog"
|
href="/catalog"
|
||||||
className="mt-6 inline-flex rounded-lg bg-neutral-900 px-5 py-3 text-sm font-semibold text-white transition hover:bg-neutral-800"
|
className="mt-8 inline-flex rounded-lg bg-brand-navy px-8 py-3 text-sm font-semibold text-white transition hover:bg-brand-navy800"
|
||||||
>
|
>
|
||||||
{t("emptyCta")}
|
{t("emptyCta")}
|
||||||
</Link>
|
</Link>
|
||||||
|
|
@ -79,13 +79,13 @@ function CartLineRow({
|
||||||
const lineTotal = price * line.quantity;
|
const lineTotal = price * line.quantity;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<li className="flex flex-col gap-4 rounded-lg border border-neutral-100 bg-white p-4 sm:flex-row sm:items-center sm:justify-between">
|
<li className="flex flex-col gap-6 rounded-lg border border-neutral-200 bg-neutral-0 p-5 sm:flex-row sm:items-center sm:justify-between">
|
||||||
<div className="min-w-0 flex-1">
|
<div className="min-w-0 flex-1">
|
||||||
<p className="font-semibold text-neutral-900">{name}</p>
|
<p className="text-lg font-semibold text-brand-navy">{name}</p>
|
||||||
{sub ? (
|
{sub ? (
|
||||||
<p className="mt-1 text-sm text-neutral-500">{sub}</p>
|
<p className="mt-1 text-sm font-normal text-neutral-500">{sub}</p>
|
||||||
) : null}
|
) : null}
|
||||||
<p className="mt-2 text-sm text-neutral-600">
|
<p className="mt-2 text-sm font-normal text-neutral-600">
|
||||||
{price.toFixed(2)}{" "}
|
{price.toFixed(2)}{" "}
|
||||||
{product ? tu(product.unitKey) : ""} {t("each")}
|
{product ? tu(product.unitKey) : ""} {t("each")}
|
||||||
</p>
|
</p>
|
||||||
|
|
@ -103,9 +103,9 @@ function CartLineRow({
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
updateQuantity(line.lineId, Number(e.target.value) || 1)
|
updateQuantity(line.lineId, Number(e.target.value) || 1)
|
||||||
}
|
}
|
||||||
className="w-20 rounded-lg border border-neutral-200 bg-neutral-50 px-2 py-1.5 text-sm font-semibold outline-none transition duration-150 focus:border-neutral-300 focus:bg-white focus:ring-2 focus:ring-neutral-900/8"
|
className="w-20 rounded-lg border border-neutral-200 bg-white px-3 py-2 text-sm font-semibold tabular-nums text-neutral-800 outline-none transition duration-150 focus:border-brand-navy600 focus:ring-2 focus:ring-brand-navy/8"
|
||||||
/>
|
/>
|
||||||
<p className="text-sm font-semibold text-neutral-900">
|
<p className="text-lg font-semibold text-brand-navy">
|
||||||
{lineTotal.toFixed(2)}
|
{lineTotal.toFixed(2)}
|
||||||
</p>
|
</p>
|
||||||
<button
|
<button
|
||||||
|
|
|
||||||
|
|
@ -97,10 +97,10 @@ export function CatalogExplorer({
|
||||||
const filterPanel = (
|
const filterPanel = (
|
||||||
<div className="space-y-6">
|
<div className="space-y-6">
|
||||||
<div>
|
<div>
|
||||||
<p className="text-[11px] font-semibold uppercase tracking-widest text-neutral-400">
|
<p className="text-[11px] font-semibold uppercase tracking-widest text-brand-gold">
|
||||||
{t("filterCategory")}
|
{t("filterCategory")}
|
||||||
</p>
|
</p>
|
||||||
<ul className="mt-3 space-y-1">
|
<ul className="mt-4 space-y-1">
|
||||||
{categoryList.map((c) => (
|
{categoryList.map((c) => (
|
||||||
<li key={c}>
|
<li key={c}>
|
||||||
<button
|
<button
|
||||||
|
|
@ -109,10 +109,10 @@ export function CatalogExplorer({
|
||||||
setCat(c);
|
setCat(c);
|
||||||
setFiltersOpen(false);
|
setFiltersOpen(false);
|
||||||
}}
|
}}
|
||||||
className={`flex w-full rounded-lg px-3 py-2 text-left text-sm font-medium transition ${
|
className={`flex w-full rounded-lg px-3 py-2 text-left text-sm font-semibold transition duration-150 ${
|
||||||
cat === c
|
cat === c
|
||||||
? "bg-neutral-900 text-white"
|
? "bg-brand-navy text-white"
|
||||||
: "text-neutral-500 hover:bg-neutral-50 hover:text-neutral-900"
|
: "text-neutral-500 hover:bg-neutral-50 hover:text-brand-navy"
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
{c === "all" ? t("all") : tc(c)}
|
{c === "all" ? t("all") : tc(c)}
|
||||||
|
|
@ -123,13 +123,13 @@ export function CatalogExplorer({
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<p className="text-[11px] font-semibold uppercase tracking-widest text-neutral-400">
|
<p className="text-[11px] font-semibold uppercase tracking-widest text-brand-gold">
|
||||||
{t("filterSort")}
|
{t("filterSort")}
|
||||||
</p>
|
</p>
|
||||||
<select
|
<select
|
||||||
value={sort}
|
value={sort}
|
||||||
onChange={(e) => setSort(e.target.value as SortKey)}
|
onChange={(e) => setSort(e.target.value as SortKey)}
|
||||||
className="mt-3 w-full rounded-lg border border-neutral-200 bg-neutral-50 px-3 py-2.5 text-sm font-medium text-neutral-900 outline-none transition duration-150 focus:border-neutral-300 focus:bg-white focus:ring-2 focus:ring-neutral-900/8"
|
className="mt-4 w-full rounded-lg border border-neutral-200 bg-neutral-50 px-3 py-2.5 text-sm font-normal text-neutral-800 outline-none transition duration-150 focus:border-brand-navy600 focus:ring-2 focus:ring-brand-navy/8"
|
||||||
>
|
>
|
||||||
<option value="name-asc">{t("sortNameAsc")}</option>
|
<option value="name-asc">{t("sortNameAsc")}</option>
|
||||||
<option value="name-desc">{t("sortNameDesc")}</option>
|
<option value="name-desc">{t("sortNameDesc")}</option>
|
||||||
|
|
@ -138,9 +138,9 @@ export function CatalogExplorer({
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="rounded-lg border border-neutral-100 bg-neutral-50/80 p-4">
|
<div className="rounded-lg border border-neutral-200 bg-neutral-50 p-5">
|
||||||
<div className="flex items-center justify-between gap-3">
|
<div className="flex items-center justify-between gap-3">
|
||||||
<span className="text-sm font-medium text-neutral-800">
|
<span className="text-sm font-semibold text-neutral-700">
|
||||||
{t("galvToggle")}
|
{t("galvToggle")}
|
||||||
</span>
|
</span>
|
||||||
<button
|
<button
|
||||||
|
|
@ -149,25 +149,25 @@ export function CatalogExplorer({
|
||||||
aria-checked={preferGalv}
|
aria-checked={preferGalv}
|
||||||
aria-label={t("galvToggle")}
|
aria-label={t("galvToggle")}
|
||||||
onClick={() => setPreferGalv((v) => !v)}
|
onClick={() => setPreferGalv((v) => !v)}
|
||||||
className={`relative h-7 w-12 shrink-0 rounded-full transition-colors ${
|
className={`relative h-7 w-12 shrink-0 rounded-full transition-colors duration-200 ${
|
||||||
preferGalv ? "bg-neutral-900" : "bg-neutral-300"
|
preferGalv ? "bg-brand-gold" : "bg-neutral-300"
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
className={`absolute top-1 h-5 w-5 rounded-full bg-white transition-all ${
|
className={`absolute top-1 h-5 w-5 rounded-full bg-white transition-all duration-200 ${
|
||||||
preferGalv ? "left-[calc(100%-1.25rem-0.25rem)]" : "left-1"
|
preferGalv ? "left-[calc(100%-1.25rem-0.25rem)]" : "left-1"
|
||||||
}`}
|
}`}
|
||||||
/>
|
/>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<p className="mt-2 text-xs leading-relaxed text-neutral-500">
|
<p className="mt-3 text-xs font-normal leading-relaxed text-neutral-500">
|
||||||
{t("sidebarGalvBody")}
|
{t("sidebarGalvBody")}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="rounded-lg border border-neutral-100 bg-white p-4">
|
<div className="rounded-lg border border-neutral-200 bg-neutral-50 p-5">
|
||||||
<p className="text-sm font-semibold text-neutral-900">{t("sidebarDocs")}</p>
|
<p className="text-sm font-semibold text-neutral-700">{t("sidebarDocs")}</p>
|
||||||
<p className="mt-2 text-xs leading-relaxed text-neutral-600">
|
<p className="mt-2 text-xs font-normal leading-relaxed text-neutral-600">
|
||||||
{t("sidebarDocsBody")}
|
{t("sidebarDocsBody")}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -175,27 +175,30 @@ export function CatalogExplorer({
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen bg-neutral-50">
|
<div className="min-h-screen bg-neutral-0">
|
||||||
<div className="mx-auto max-w-[1440px] px-4 py-8 sm:py-10 lg:px-8">
|
<div className="mx-auto max-w-7xl px-4 py-16 sm:px-6 lg:px-8">
|
||||||
<header className="max-w-3xl">
|
<header className="max-w-3xl">
|
||||||
<h1 className="text-3xl font-semibold tracking-tight text-neutral-900 sm:text-4xl">
|
<p className="text-[11px] font-semibold uppercase tracking-widest text-brand-gold">
|
||||||
|
Catalog
|
||||||
|
</p>
|
||||||
|
<h1 className="mt-3 text-4xl font-semibold tracking-tight text-brand-navy">
|
||||||
{t("title")}
|
{t("title")}
|
||||||
</h1>
|
</h1>
|
||||||
<p className="mt-3 text-sm leading-relaxed text-neutral-600 sm:text-base">
|
<p className="mt-4 text-base font-normal leading-relaxed text-neutral-600">
|
||||||
{t("subtitle")}
|
{t("subtitle")}
|
||||||
</p>
|
</p>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<form
|
<form
|
||||||
onSubmit={submitSearch}
|
onSubmit={submitSearch}
|
||||||
className="relative mt-6 md:hidden"
|
className="relative mt-10 md:hidden"
|
||||||
role="search"
|
role="search"
|
||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
value={search}
|
value={search}
|
||||||
onChange={(e) => setSearch(e.target.value)}
|
onChange={(e) => setSearch(e.target.value)}
|
||||||
placeholder={tn("searchPlaceholder")}
|
placeholder={tn("searchPlaceholder")}
|
||||||
className="w-full rounded-lg border border-neutral-200 bg-neutral-50 py-3 pl-11 pr-4 text-sm text-neutral-900 outline-none transition duration-150 placeholder:text-neutral-400 focus:border-neutral-300 focus:bg-white focus:ring-2 focus:ring-neutral-900/8"
|
className="w-full rounded-lg border border-neutral-200 bg-neutral-50 py-3 pl-11 pr-4 text-sm font-normal text-neutral-800 outline-none transition duration-150 focus:border-brand-navy600 focus:bg-white focus:ring-2 focus:ring-brand-navy/8 placeholder:text-neutral-400"
|
||||||
/>
|
/>
|
||||||
<span className="pointer-events-none absolute left-3.5 top-1/2 -translate-y-1/2 text-neutral-400">
|
<span className="pointer-events-none absolute left-3.5 top-1/2 -translate-y-1/2 text-neutral-400">
|
||||||
<svg className="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={1.75}>
|
<svg className="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={1.75}>
|
||||||
|
|
@ -204,10 +207,10 @@ export function CatalogExplorer({
|
||||||
</span>
|
</span>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<div className="mt-8 flex flex-wrap items-center gap-3">
|
<div className="mt-8 flex flex-wrap items-center gap-4">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className="inline-flex items-center gap-2 rounded-lg border border-neutral-200 bg-white px-4 py-2.5 text-sm font-semibold text-neutral-800 transition hover:border-neutral-300 lg:hidden"
|
className="inline-flex items-center gap-2 rounded-lg border border-neutral-200 bg-white px-5 py-2.5 text-sm font-semibold text-neutral-700 transition hover:border-neutral-300 hover:text-brand-navy lg:hidden"
|
||||||
onClick={() => setFiltersOpen(true)}
|
onClick={() => setFiltersOpen(true)}
|
||||||
>
|
>
|
||||||
<svg className="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
|
<svg className="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
|
||||||
|
|
@ -222,10 +225,10 @@ export function CatalogExplorer({
|
||||||
key={c}
|
key={c}
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => setCat(c)}
|
onClick={() => setCat(c)}
|
||||||
className={`shrink-0 rounded-md px-4 py-2 text-sm font-semibold transition ${
|
className={`shrink-0 rounded-lg px-5 py-2 text-sm font-semibold transition duration-150 ${
|
||||||
cat === c
|
cat === c
|
||||||
? "bg-neutral-900 text-white"
|
? "bg-brand-navy text-white shadow-sm"
|
||||||
: "border border-neutral-200 bg-white text-neutral-500 hover:border-neutral-300 hover:text-neutral-900"
|
: "border border-neutral-200 bg-white text-neutral-500 hover:border-neutral-300 hover:text-brand-navy"
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
{c === "all" ? t("allShort") : tc(c)}
|
{c === "all" ? t("allShort") : tc(c)}
|
||||||
|
|
@ -233,7 +236,7 @@ export function CatalogExplorer({
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="hidden items-center gap-2 sm:flex">
|
<div className="hidden items-center gap-3 sm:flex">
|
||||||
<label htmlFor="catalog-sort" className="sr-only">
|
<label htmlFor="catalog-sort" className="sr-only">
|
||||||
{t("sort")}
|
{t("sort")}
|
||||||
</label>
|
</label>
|
||||||
|
|
@ -241,7 +244,7 @@ export function CatalogExplorer({
|
||||||
id="catalog-sort"
|
id="catalog-sort"
|
||||||
value={sort}
|
value={sort}
|
||||||
onChange={(e) => setSort(e.target.value as SortKey)}
|
onChange={(e) => setSort(e.target.value as SortKey)}
|
||||||
className="rounded-lg border border-neutral-200 bg-neutral-50 py-2 pl-3 pr-8 text-sm font-medium text-neutral-900 outline-none transition duration-150 focus:border-neutral-300 focus:bg-white focus:ring-2 focus:ring-neutral-900/8"
|
className="rounded-lg border border-neutral-200 bg-neutral-50 py-2 pl-3 pr-8 text-sm font-semibold text-neutral-700 outline-none transition duration-150 focus:border-brand-navy600 focus:bg-white focus:ring-2 focus:ring-brand-navy/8"
|
||||||
>
|
>
|
||||||
<option value="name-asc">{t("sortNameAsc")}</option>
|
<option value="name-asc">{t("sortNameAsc")}</option>
|
||||||
<option value="name-desc">{t("sortNameDesc")}</option>
|
<option value="name-desc">{t("sortNameDesc")}</option>
|
||||||
|
|
@ -251,36 +254,37 @@ export function CatalogExplorer({
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="mt-10 flex gap-10">
|
<div className="mt-12 flex gap-12">
|
||||||
<aside className="hidden w-72 shrink-0 lg:block">{filterPanel}</aside>
|
<aside className="hidden w-64 shrink-0 lg:block">{filterPanel}</aside>
|
||||||
|
|
||||||
<div className="min-w-0 flex-1">
|
<div className="min-w-0 flex-1">
|
||||||
{filtered.length === 0 ? (
|
{filtered.length === 0 ? (
|
||||||
<p className="py-20 text-center text-sm text-neutral-500">{t("empty")}</p>
|
<p className="py-24 text-center text-sm font-normal text-neutral-500">{t("empty")}</p>
|
||||||
) : (
|
) : (
|
||||||
<ul className="grid grid-cols-1 gap-x-6 gap-y-10 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 2xl:grid-cols-5">
|
<ul className="grid grid-cols-1 gap-x-8 gap-y-12 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4">
|
||||||
{filtered.map((p) => (
|
{filtered.map((p) => (
|
||||||
<li key={p.slug}>
|
<li key={p.slug}>
|
||||||
<article className="group flex h-full flex-col">
|
<article className="group flex h-full flex-col">
|
||||||
<Link
|
<Link
|
||||||
href={`/product/${p.slug}`}
|
href={`/product/${p.slug}`}
|
||||||
className="group relative block overflow-hidden rounded-lg border border-neutral-100"
|
className="group relative block overflow-hidden rounded-lg border border-neutral-200 bg-neutral-50"
|
||||||
>
|
>
|
||||||
<div className="relative aspect-square">
|
<div className="relative aspect-square">
|
||||||
<MetalProfileVisual
|
<MetalProfileVisual
|
||||||
src={p.gallery[0]}
|
src={p.gallery[0]}
|
||||||
alt={tp(`${p.slug}.imageAlt`)}
|
alt={tp(`${p.slug}.imageAlt`)}
|
||||||
className="h-full w-full rounded-lg"
|
className="h-full w-full rounded-lg mix-blend-multiply"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</Link>
|
</Link>
|
||||||
|
|
||||||
<div className="mt-4 px-0.5">
|
<div className="mt-5 flex flex-col flex-1">
|
||||||
<p className="text-[11px] font-semibold uppercase tracking-widest text-neutral-400">
|
<div>
|
||||||
|
<p className="text-[11px] font-semibold uppercase tracking-widest text-brand-gold">
|
||||||
{t("thicknessLabel")}
|
{t("thicknessLabel")}
|
||||||
</p>
|
</p>
|
||||||
<div
|
<div
|
||||||
className="mt-1.5 flex flex-wrap gap-1.5"
|
className="mt-2 flex flex-wrap gap-1.5"
|
||||||
role="list"
|
role="list"
|
||||||
aria-label={t("thicknessList")}
|
aria-label={t("thicknessList")}
|
||||||
>
|
>
|
||||||
|
|
@ -288,7 +292,7 @@ export function CatalogExplorer({
|
||||||
<span
|
<span
|
||||||
key={`${p.slug}-${mm}`}
|
key={`${p.slug}-${mm}`}
|
||||||
role="listitem"
|
role="listitem"
|
||||||
className="inline-flex rounded-md border border-neutral-200 bg-neutral-50 px-2.5 py-1 text-xs font-semibold tabular-nums text-neutral-800"
|
className="inline-flex rounded-lg border border-neutral-200 bg-neutral-50 px-2.5 py-1 text-xs font-semibold tabular-nums text-neutral-700"
|
||||||
>
|
>
|
||||||
{t("thicknessValue", {
|
{t("thicknessValue", {
|
||||||
value: formatThicknessMm(mm),
|
value: formatThicknessMm(mm),
|
||||||
|
|
@ -298,37 +302,40 @@ export function CatalogExplorer({
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<p className="mt-3 text-[11px] font-semibold uppercase tracking-widest text-neutral-400">
|
<div className="mt-4">
|
||||||
|
<p className="text-[11px] font-semibold uppercase tracking-widest text-neutral-500">
|
||||||
{tcl(p.catalogLineKey)}
|
{tcl(p.catalogLineKey)}
|
||||||
</p>
|
</p>
|
||||||
<p className="mt-1 text-sm font-semibold text-neutral-900">
|
<p className="mt-1 text-xs font-semibold text-neutral-700">
|
||||||
{tn("supplier")}
|
{tn("supplier")}
|
||||||
</p>
|
</p>
|
||||||
<h2 className="mt-0.5 text-sm font-normal leading-snug text-neutral-600">
|
<h2 className="mt-1 text-lg font-semibold tracking-tight text-brand-navy">
|
||||||
<Link
|
<Link
|
||||||
href={`/product/${p.slug}`}
|
href={`/product/${p.slug}`}
|
||||||
className="transition hover:text-neutral-900"
|
className="transition duration-150 hover:text-brand-navy800"
|
||||||
>
|
>
|
||||||
{tp(`${p.slug}.name`)}
|
{tp(`${p.slug}.name`)}
|
||||||
</Link>
|
</Link>
|
||||||
</h2>
|
</h2>
|
||||||
|
</div>
|
||||||
|
|
||||||
<p className="mt-3 text-sm text-neutral-500">
|
<div className="mt-4 flex items-baseline gap-1.5">
|
||||||
<span className="text-[11px] font-semibold uppercase tracking-widest">
|
<span className="text-[11px] font-semibold uppercase tracking-widest text-neutral-500">
|
||||||
{t("from")}
|
{t("from")}
|
||||||
</span>{" "}
|
</span>
|
||||||
<span className="text-base font-semibold text-neutral-900">
|
<span className="text-xl font-semibold text-brand-navy">
|
||||||
{p.pricePerUnit.toFixed(2)}
|
{p.pricePerUnit.toFixed(2)}
|
||||||
</span>{" "}
|
</span>
|
||||||
<span className="text-neutral-500">{tu(p.unitKey)}</span>
|
<span className="text-xs font-normal text-neutral-500">{tu(p.unitKey)}</span>
|
||||||
</p>
|
</div>
|
||||||
|
|
||||||
<Link
|
<Link
|
||||||
href={`/product/${p.slug}`}
|
href={`/product/${p.slug}`}
|
||||||
className="mt-4 w-full rounded-lg bg-neutral-900 py-2.5 text-center text-sm font-semibold text-white transition hover:bg-neutral-800"
|
className="mt-6 w-full rounded-lg bg-brand-navy py-3 text-center text-sm font-semibold text-white transition duration-150 hover:bg-brand-navy800"
|
||||||
>
|
>
|
||||||
{t("addToCart")}
|
{t("addToCart")}
|
||||||
</Link>
|
</Link>
|
||||||
|
</div>
|
||||||
</article>
|
</article>
|
||||||
</li>
|
</li>
|
||||||
))}
|
))}
|
||||||
|
|
@ -342,16 +349,16 @@ export function CatalogExplorer({
|
||||||
<>
|
<>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className="fixed inset-0 z-50 bg-neutral-950/40 backdrop-blur-[1px] lg:hidden"
|
className="fixed inset-0 z-50 bg-neutral-900/40 backdrop-blur-[2px] lg:hidden"
|
||||||
aria-label={t("closeFilters")}
|
aria-label={t("closeFilters")}
|
||||||
onClick={() => setFiltersOpen(false)}
|
onClick={() => setFiltersOpen(false)}
|
||||||
/>
|
/>
|
||||||
<div className="fixed inset-y-0 left-0 z-[60] w-[min(22rem,100vw)] overflow-y-auto border-r border-neutral-100 bg-white p-6 lg:hidden">
|
<div className="fixed inset-y-0 left-0 z-[60] w-[min(20rem,100vw)] overflow-y-auto bg-white p-8 lg:hidden shadow-2xl">
|
||||||
<div className="mb-6 flex items-center justify-between">
|
<div className="mb-8 flex items-center justify-between">
|
||||||
<p className="text-lg font-semibold text-neutral-900">{t("filters")}</p>
|
<p className="text-xl font-semibold text-brand-navy">{t("filters")}</p>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className="rounded-lg p-2 text-neutral-500 transition hover:bg-neutral-50 hover:text-neutral-900"
|
className="rounded-lg p-2 text-neutral-400 transition hover:bg-neutral-50 hover:text-brand-navy"
|
||||||
onClick={() => setFiltersOpen(false)}
|
onClick={() => setFiltersOpen(false)}
|
||||||
>
|
>
|
||||||
<span className="sr-only">{t("closeFilters")}</span>
|
<span className="sr-only">{t("closeFilters")}</span>
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ const formSchema = z.object({
|
||||||
type FormValues = z.infer<typeof formSchema>;
|
type FormValues = z.infer<typeof formSchema>;
|
||||||
|
|
||||||
const inputClass =
|
const inputClass =
|
||||||
"mt-1 w-full rounded-lg border border-neutral-200 bg-neutral-50 px-3 py-2.5 text-sm text-neutral-900 outline-none transition duration-150 placeholder:text-neutral-400 focus:border-neutral-300 focus:bg-white focus:ring-2 focus:ring-neutral-900/8";
|
"mt-1.5 w-full rounded-lg border border-neutral-200 bg-white px-3 py-2.5 text-sm font-normal text-neutral-800 outline-none transition duration-150 placeholder:text-neutral-400 focus:border-brand-navy600 focus:ring-2 focus:ring-brand-navy/8";
|
||||||
|
|
||||||
export function CheckoutForm() {
|
export function CheckoutForm() {
|
||||||
const t = useTranslations("checkout");
|
const t = useTranslations("checkout");
|
||||||
|
|
@ -46,11 +46,11 @@ export function CheckoutForm() {
|
||||||
|
|
||||||
if (items.length === 0 && status !== "success") {
|
if (items.length === 0 && status !== "success") {
|
||||||
return (
|
return (
|
||||||
<div className="rounded-lg border border-neutral-100 bg-white p-8 text-center">
|
<div className="rounded-lg border border-neutral-200 bg-neutral-50 p-10 text-center">
|
||||||
<p className="text-neutral-600">{t("cartEmpty")}</p>
|
<p className="text-sm font-normal text-neutral-600">{t("cartEmpty")}</p>
|
||||||
<Link
|
<Link
|
||||||
href="/cart"
|
href="/cart"
|
||||||
className="mt-4 inline-flex rounded-lg bg-neutral-900 px-5 py-3 text-sm font-semibold text-white transition hover:bg-neutral-800"
|
className="mt-6 inline-flex rounded-lg bg-brand-navy px-8 py-3 text-sm font-semibold text-white transition hover:bg-brand-navy800"
|
||||||
>
|
>
|
||||||
{t("goCart")}
|
{t("goCart")}
|
||||||
</Link>
|
</Link>
|
||||||
|
|
@ -60,22 +60,22 @@ export function CheckoutForm() {
|
||||||
|
|
||||||
if (status === "success" && requestId) {
|
if (status === "success" && requestId) {
|
||||||
return (
|
return (
|
||||||
<div className="rounded-lg border border-neutral-100 bg-white p-8">
|
<div className="rounded-lg border border-neutral-200 bg-neutral-50 p-8">
|
||||||
<h2 className="text-xl font-semibold text-green-800">{t("successTitle")}</h2>
|
<h2 className="text-2xl font-semibold text-brand-navy">{t("successTitle")}</h2>
|
||||||
<p className="mt-2 text-sm text-neutral-600">
|
<p className="mt-3 text-sm font-normal text-neutral-700">
|
||||||
{t("successBody")}{" "}
|
{t("successBody")}{" "}
|
||||||
<span className="font-mono font-semibold text-neutral-900">
|
<span className="font-mono font-semibold text-brand-navy">
|
||||||
{requestId}
|
{requestId}
|
||||||
</span>
|
</span>
|
||||||
</p>
|
</p>
|
||||||
{emailWarn ? (
|
{emailWarn ? (
|
||||||
<p className="mt-4 rounded-lg border border-amber-200 bg-amber-50 p-3 text-sm text-amber-900">
|
<p className="mt-4 rounded-lg border border-brand-gold/20 bg-brand-gold/5 p-4 text-sm font-normal text-neutral-800">
|
||||||
{t("emailWarn")}
|
{t("emailWarn")}
|
||||||
</p>
|
</p>
|
||||||
) : null}
|
) : null}
|
||||||
<Link
|
<Link
|
||||||
href="/catalog"
|
href="/catalog"
|
||||||
className="mt-6 inline-flex rounded-lg bg-neutral-900 px-5 py-3 text-sm font-semibold text-white transition hover:bg-neutral-800"
|
className="mt-8 inline-flex rounded-lg bg-brand-navy px-8 py-3.5 text-sm font-semibold text-white transition hover:bg-brand-navy800"
|
||||||
>
|
>
|
||||||
{t("successCta")}
|
{t("successCta")}
|
||||||
</Link>
|
</Link>
|
||||||
|
|
@ -147,7 +147,7 @@ export function CheckoutForm() {
|
||||||
return (
|
return (
|
||||||
<form
|
<form
|
||||||
onSubmit={onFormSubmit}
|
onSubmit={onFormSubmit}
|
||||||
className="space-y-6 rounded-lg border border-neutral-100 bg-white p-6 sm:p-8"
|
className="space-y-8 rounded-lg border border-neutral-200 bg-neutral-50 p-6 sm:p-10"
|
||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
|
|
@ -160,7 +160,7 @@ export function CheckoutForm() {
|
||||||
|
|
||||||
<div className="grid gap-4 sm:grid-cols-2">
|
<div className="grid gap-4 sm:grid-cols-2">
|
||||||
<div>
|
<div>
|
||||||
<label className="text-sm font-medium text-neutral-700" htmlFor="name">
|
<label className="text-sm font-semibold text-neutral-700" htmlFor="name">
|
||||||
{t("name")}
|
{t("name")}
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
|
|
@ -173,7 +173,7 @@ export function CheckoutForm() {
|
||||||
) : null}
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label className="text-sm font-medium text-neutral-700" htmlFor="phone">
|
<label className="text-sm font-semibold text-neutral-700" htmlFor="phone">
|
||||||
{t("phone")}
|
{t("phone")}
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
|
|
@ -188,7 +188,7 @@ export function CheckoutForm() {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label className="text-sm font-medium text-neutral-700" htmlFor="email">
|
<label className="text-sm font-semibold text-neutral-700" htmlFor="email">
|
||||||
{t("email")}
|
{t("email")}
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
|
|
@ -203,7 +203,7 @@ export function CheckoutForm() {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label className="text-sm font-medium text-neutral-700" htmlFor="company">
|
<label className="text-sm font-semibold text-neutral-700" htmlFor="company">
|
||||||
{t("company")}
|
{t("company")}
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
|
|
@ -214,7 +214,7 @@ export function CheckoutForm() {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label className="text-sm font-medium text-neutral-700" htmlFor="message">
|
<label className="text-sm font-semibold text-neutral-700" htmlFor="message">
|
||||||
{t("message")}
|
{t("message")}
|
||||||
</label>
|
</label>
|
||||||
<textarea
|
<textarea
|
||||||
|
|
@ -226,7 +226,7 @@ export function CheckoutForm() {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label className="text-sm font-medium text-neutral-700" htmlFor="refs">
|
<label className="text-sm font-semibold text-neutral-700" htmlFor="refs">
|
||||||
{t("referenceImages")}
|
{t("referenceImages")}
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
|
|
@ -242,7 +242,7 @@ export function CheckoutForm() {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label className="text-sm font-medium text-neutral-700" htmlFor="proforma">
|
<label className="text-sm font-semibold text-neutral-700" htmlFor="proforma">
|
||||||
{t("proforma")}
|
{t("proforma")}
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
|
|
@ -265,7 +265,7 @@ export function CheckoutForm() {
|
||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
disabled={status === "sending"}
|
disabled={status === "sending"}
|
||||||
className="w-full rounded-lg bg-neutral-900 py-3.5 text-sm font-semibold text-white transition hover:bg-neutral-800 disabled:cursor-not-allowed disabled:opacity-60 sm:w-auto sm:px-10"
|
className="w-full rounded-lg bg-brand-navy py-4 text-sm font-semibold text-white transition duration-150 hover:bg-brand-navy800 disabled:cursor-not-allowed disabled:opacity-60 sm:w-auto sm:px-12"
|
||||||
>
|
>
|
||||||
{status === "sending" ? t("sending") : t("submit")}
|
{status === "sending" ? t("sending") : t("submit")}
|
||||||
</button>
|
</button>
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ import {
|
||||||
publicContactPhone,
|
publicContactPhone,
|
||||||
telHref,
|
telHref,
|
||||||
} from "@/lib/public-contact";
|
} from "@/lib/public-contact";
|
||||||
|
import Image from "next/image";
|
||||||
|
|
||||||
function SocialGlyph({
|
function SocialGlyph({
|
||||||
name,
|
name,
|
||||||
|
|
@ -54,45 +55,41 @@ export async function Footer() {
|
||||||
const socials = getPublicSocialLinks();
|
const socials = getPublicSocialLinks();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<footer className="border-t border-neutral-100 bg-white">
|
<footer className="bg-brand-navy">
|
||||||
<div className="mx-auto max-w-6xl px-4 py-14 sm:px-6 lg:px-8">
|
<div className="mx-auto max-w-7xl px-4 py-16 sm:px-6 lg:px-8">
|
||||||
<div className="grid gap-12 sm:grid-cols-2 lg:grid-cols-4">
|
<div className="grid gap-12 sm:grid-cols-2 lg:grid-cols-4">
|
||||||
|
|
||||||
{/* Brand column */}
|
{/* Brand column */}
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<span className="flex h-8 w-8 items-center justify-center rounded-lg bg-neutral-900 text-white">
|
<Link href="/" className="text-2xl font-semibold tracking-tight">
|
||||||
<svg width="14" height="14" viewBox="0 0 32 32" fill="none" aria-hidden>
|
<span className="text-white">Trust</span>
|
||||||
<path d="M6 24 16 6l10 18H6Z" stroke="currentColor" strokeWidth="2.5" strokeLinejoin="round" />
|
<span className="text-brand-gold">Win</span>
|
||||||
</svg>
|
</Link>
|
||||||
</span>
|
|
||||||
<span className="text-[15px] font-semibold tracking-tight text-neutral-900">
|
|
||||||
TrustWin
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
<p className="max-w-xs text-sm leading-relaxed text-neutral-500">
|
<p className="max-w-xs text-sm font-normal leading-relaxed text-brand-navy100">
|
||||||
{t("tagline")}
|
{t("tagline")}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Links column */}
|
{/* Links column */}
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<p className="text-[11px] font-semibold uppercase tracking-widest text-neutral-400">
|
<p className="text-[11px] font-semibold uppercase tracking-widest text-brand-gold">
|
||||||
{tn("catalog")}
|
{tn("catalog")}
|
||||||
</p>
|
</p>
|
||||||
<ul className="space-y-2.5 text-sm">
|
<ul className="space-y-2.5 text-sm">
|
||||||
<li>
|
<li>
|
||||||
<Link href="/" className="text-neutral-500 transition hover:text-neutral-900">
|
<Link href="/" className="font-normal text-brand-navy100 transition hover:text-white">
|
||||||
{tn("home")}
|
{tn("home")}
|
||||||
</Link>
|
</Link>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<Link href="/catalog" className="text-neutral-500 transition hover:text-neutral-900">
|
<Link href="/catalog" className="font-normal text-brand-navy100 transition hover:text-white">
|
||||||
{tn("products")}
|
{tn("products")}
|
||||||
</Link>
|
</Link>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<Link href="/cart" className="text-neutral-500 transition hover:text-neutral-900">
|
<Link href="/cart" className="font-normal text-brand-navy100 transition hover:text-white">
|
||||||
{tn("cart")}
|
{tn("cart")}
|
||||||
</Link>
|
</Link>
|
||||||
</li>
|
</li>
|
||||||
|
|
@ -101,14 +98,14 @@ export async function Footer() {
|
||||||
|
|
||||||
{/* Contact column */}
|
{/* Contact column */}
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<p className="text-[11px] font-semibold uppercase tracking-widest text-neutral-400">
|
<p className="text-[11px] font-semibold uppercase tracking-widest text-brand-gold">
|
||||||
{t("contactTitle")}
|
{t("contactTitle")}
|
||||||
</p>
|
</p>
|
||||||
<ul className="space-y-2.5 text-sm">
|
<ul className="space-y-2.5 text-sm">
|
||||||
<li>
|
<li>
|
||||||
<a
|
<a
|
||||||
href={telHref(publicContactPhone)}
|
href={telHref(publicContactPhone)}
|
||||||
className="font-medium text-neutral-800 transition-colors hover:text-neutral-500"
|
className="font-normal text-brand-navy100 transition hover:text-white"
|
||||||
>
|
>
|
||||||
{publicContactPhone}
|
{publicContactPhone}
|
||||||
</a>
|
</a>
|
||||||
|
|
@ -117,37 +114,37 @@ export async function Footer() {
|
||||||
<li>
|
<li>
|
||||||
<a
|
<a
|
||||||
href={`mailto:${publicContactEmail}`}
|
href={`mailto:${publicContactEmail}`}
|
||||||
className="text-neutral-500 underline underline-offset-2 decoration-neutral-300 transition-colors hover:text-neutral-800 hover:decoration-neutral-500"
|
className="font-normal text-brand-navy100 transition hover:text-white"
|
||||||
>
|
>
|
||||||
{publicContactEmail}
|
{publicContactEmail}
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
)}
|
)}
|
||||||
{publicContactAddress && (
|
{publicContactAddress && (
|
||||||
<li className="whitespace-pre-line text-neutral-500">
|
<li className="whitespace-pre-line font-normal text-brand-navy100">
|
||||||
{publicContactAddress}
|
{publicContactAddress}
|
||||||
</li>
|
</li>
|
||||||
)}
|
)}
|
||||||
{publicContactHours && (
|
{publicContactHours && (
|
||||||
<li className="text-neutral-500">{publicContactHours}</li>
|
<li className="font-normal text-brand-navy100">{publicContactHours}</li>
|
||||||
)}
|
)}
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Social column */}
|
{/* Social column */}
|
||||||
<div className="flex flex-col items-center space-y-4 sm:col-span-2 lg:col-span-1 lg:items-center">
|
<div className="flex flex-col items-start space-y-4 sm:col-span-2 lg:col-span-1 lg:items-start">
|
||||||
<p className="text-[11px] font-semibold uppercase tracking-widest text-neutral-400">
|
<p className="text-[11px] font-semibold uppercase tracking-widest text-brand-gold">
|
||||||
{t("socialTitle")}
|
{t("socialTitle")}
|
||||||
</p>
|
</p>
|
||||||
{socials.length > 0 ? (
|
{socials.length > 0 ? (
|
||||||
<ul className="flex flex-wrap justify-center gap-2">
|
<ul className="flex flex-wrap gap-2">
|
||||||
{socials.map(({ key, href }) => (
|
{socials.map(({ key, href }) => (
|
||||||
<li key={key}>
|
<li key={key}>
|
||||||
<a
|
<a
|
||||||
href={href}
|
href={href}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
className="flex h-9 w-9 items-center justify-center rounded-lg border border-neutral-200 bg-white text-neutral-500 transition hover:border-neutral-900 hover:bg-neutral-900 hover:text-white"
|
className="flex h-9 w-9 items-center justify-center rounded-lg border border-white/20 text-white transition hover:border-white hover:bg-white/10"
|
||||||
aria-label={t(`social.${key}`)}
|
aria-label={t(`social.${key}`)}
|
||||||
>
|
>
|
||||||
<SocialGlyph name={key} />
|
<SocialGlyph name={key} />
|
||||||
|
|
@ -156,7 +153,7 @@ export async function Footer() {
|
||||||
))}
|
))}
|
||||||
</ul>
|
</ul>
|
||||||
) : (
|
) : (
|
||||||
<p className="max-w-xs text-center text-sm leading-relaxed text-neutral-400">
|
<p className="max-w-xs text-sm font-normal leading-relaxed text-brand-navy100 opacity-70">
|
||||||
{t("socialHint")}
|
{t("socialHint")}
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
|
|
@ -164,8 +161,8 @@ export async function Footer() {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Bottom bar */}
|
{/* Bottom bar */}
|
||||||
<div className="mt-14 flex flex-col items-center justify-center gap-3 border-t border-neutral-100 pt-8 sm:flex-row">
|
<div className="mt-16 flex flex-col items-center justify-between gap-3 border-t border-white/10 pt-8 sm:flex-row">
|
||||||
<p className="text-xs font-semibold text-black-300">
|
<p className="text-xs font-normal text-brand-navy100">
|
||||||
© {year} TrustWin. {t("rights")}
|
© {year} TrustWin. {t("rights")}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -6,33 +6,33 @@ import { Link } from "@/i18n/navigation";
|
||||||
import { LanguageSwitcher } from "@/components/LanguageSwitcher";
|
import { LanguageSwitcher } from "@/components/LanguageSwitcher";
|
||||||
import { CartBadge } from "@/components/CartBadge";
|
import { CartBadge } from "@/components/CartBadge";
|
||||||
import { HeaderCatalogSearch } from "@/components/HeaderCatalogSearch";
|
import { HeaderCatalogSearch } from "@/components/HeaderCatalogSearch";
|
||||||
|
import Image from "next/image";
|
||||||
export function Header() {
|
export function Header() {
|
||||||
const t = useTranslations("nav");
|
const t = useTranslations("nav");
|
||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
|
|
||||||
const navLinkClass =
|
const navLinkClass =
|
||||||
"relative text-sm font-medium text-neutral-500 transition-colors duration-150 hover:text-neutral-900 after:absolute after:-bottom-0.5 after:left-0 after:h-px after:w-0 after:bg-neutral-900 after:transition-all after:duration-200 hover:after:w-full";
|
"relative text-sm font-semibold text-neutral-500 transition-colors duration-150 hover:text-brand-navy after:absolute after:-bottom-0.5 after:left-0 after:h-[2px] after:w-0 after:bg-brand-gold after:transition-all after:duration-150 hover:after:w-full";
|
||||||
|
|
||||||
const mobileLinks = (
|
const mobileLinks = (
|
||||||
<>
|
<>
|
||||||
<Link
|
<Link
|
||||||
href="/"
|
href="/"
|
||||||
className="rounded-lg px-3 py-2.5 text-sm font-medium text-neutral-700 transition hover:bg-neutral-50 hover:text-neutral-900"
|
className="rounded-lg px-3 py-2.5 text-sm font-semibold text-neutral-700 transition hover:bg-neutral-50 hover:text-brand-navy"
|
||||||
onClick={() => setOpen(false)}
|
onClick={() => setOpen(false)}
|
||||||
>
|
>
|
||||||
{t("home")}
|
{t("home")}
|
||||||
</Link>
|
</Link>
|
||||||
<Link
|
<Link
|
||||||
href="/catalog"
|
href="/catalog"
|
||||||
className="rounded-lg px-3 py-2.5 text-sm font-medium text-neutral-700 transition hover:bg-neutral-50 hover:text-neutral-900"
|
className="rounded-lg px-3 py-2.5 text-sm font-semibold text-neutral-700 transition hover:bg-neutral-50 hover:text-brand-navy"
|
||||||
onClick={() => setOpen(false)}
|
onClick={() => setOpen(false)}
|
||||||
>
|
>
|
||||||
{t("catalog")}
|
{t("catalog")}
|
||||||
</Link>
|
</Link>
|
||||||
<Link
|
<Link
|
||||||
href="/cart"
|
href="/cart"
|
||||||
className="rounded-lg px-3 py-2.5 text-sm font-medium text-neutral-700 transition hover:bg-neutral-50 hover:text-neutral-900"
|
className="rounded-lg px-3 py-2.5 text-sm font-semibold text-neutral-700 transition hover:bg-neutral-50 hover:text-brand-navy"
|
||||||
onClick={() => setOpen(false)}
|
onClick={() => setOpen(false)}
|
||||||
>
|
>
|
||||||
{t("cart")}
|
{t("cart")}
|
||||||
|
|
@ -40,7 +40,7 @@ export function Header() {
|
||||||
<div className="pt-2">
|
<div className="pt-2">
|
||||||
<Link
|
<Link
|
||||||
href="/checkout"
|
href="/checkout"
|
||||||
className="block rounded-lg bg-neutral-900 px-4 py-2.5 text-center text-sm font-semibold text-white transition hover:bg-neutral-800"
|
className="block rounded-lg bg-brand-navy px-4 py-2.5 text-center text-sm font-semibold text-white transition hover:bg-brand-navy800"
|
||||||
onClick={() => setOpen(false)}
|
onClick={() => setOpen(false)}
|
||||||
>
|
>
|
||||||
{t("joinCta")}
|
{t("joinCta")}
|
||||||
|
|
@ -50,32 +50,23 @@ export function Header() {
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<header className="sticky top-0 z-40 border-b border-neutral-100 bg-white/98 backdrop-blur-sm">
|
<header className="sticky top-0 z-40 border-b border-neutral-200 bg-neutral-0">
|
||||||
<div className="mx-auto flex max-w-[1440px] items-center gap-6 px-4 py-3.5 sm:gap-8 lg:px-8">
|
<div className="mx-auto flex max-w-7xl items-center px-4 py-1 sm:gap-8 lg:px-8">
|
||||||
|
|
||||||
{/* Logo */}
|
{/* Logo */}
|
||||||
<Link
|
<Link
|
||||||
href="/"
|
href="/"
|
||||||
className="group flex shrink-0 items-center gap-2 text-neutral-900"
|
className="group flex items-center"
|
||||||
onClick={() => setOpen(false)}
|
onClick={() => setOpen(false)}
|
||||||
>
|
>
|
||||||
<span
|
<Image
|
||||||
className="flex h-8 w-8 items-center justify-center rounded-lg bg-neutral-900 text-white transition duration-200 group-hover:bg-neutral-700"
|
src="/Logo/TrustLogo-removebg-preview.png"
|
||||||
aria-hidden
|
alt={t("brand")}
|
||||||
>
|
width={120}
|
||||||
<svg width="16" height="16" viewBox="0 0 32 32" fill="none">
|
height={40}
|
||||||
<path
|
className="h-16 w-auto object-contain"
|
||||||
d="M6 24 16 6l10 18H6Z"
|
priority
|
||||||
stroke="currentColor"
|
|
||||||
strokeWidth="2.5"
|
|
||||||
strokeLinejoin="round"
|
|
||||||
fill="none"
|
|
||||||
/>
|
/>
|
||||||
</svg>
|
|
||||||
</span>
|
|
||||||
<span className="hidden text-[15px] font-semibold tracking-tight sm:inline">
|
|
||||||
{t("brand")}
|
|
||||||
</span>
|
|
||||||
</Link>
|
</Link>
|
||||||
|
|
||||||
{/* Desktop nav */}
|
{/* Desktop nav */}
|
||||||
|
|
@ -100,7 +91,7 @@ export function Header() {
|
||||||
{/* Cart */}
|
{/* Cart */}
|
||||||
<Link
|
<Link
|
||||||
href="/cart"
|
href="/cart"
|
||||||
className="relative rounded-lg p-2 text-neutral-500 transition hover:bg-neutral-50 hover:text-neutral-900"
|
className="relative rounded-lg p-2 text-neutral-500 transition hover:bg-neutral-100 hover:text-neutral-800"
|
||||||
aria-label={t("cart")}
|
aria-label={t("cart")}
|
||||||
>
|
>
|
||||||
<svg
|
<svg
|
||||||
|
|
@ -123,7 +114,7 @@ export function Header() {
|
||||||
{/* CTA */}
|
{/* CTA */}
|
||||||
<Link
|
<Link
|
||||||
href="/checkout"
|
href="/checkout"
|
||||||
className="hidden rounded-lg bg-neutral-900 px-4 py-2 text-sm font-semibold text-white transition hover:bg-neutral-800 sm:inline-flex"
|
className="hidden rounded-lg bg-brand-navy px-4 py-2 text-sm font-semibold text-white transition hover:bg-brand-navy800 sm:inline-flex"
|
||||||
>
|
>
|
||||||
{t("joinCta")}
|
{t("joinCta")}
|
||||||
</Link>
|
</Link>
|
||||||
|
|
@ -131,7 +122,7 @@ export function Header() {
|
||||||
{/* Mobile menu toggle */}
|
{/* Mobile menu toggle */}
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className="rounded-lg p-2 text-neutral-500 transition hover:bg-neutral-50 hover:text-neutral-900 lg:hidden"
|
className="rounded-lg p-2 text-neutral-500 transition hover:bg-neutral-100 hover:text-neutral-800 lg:hidden"
|
||||||
aria-expanded={open}
|
aria-expanded={open}
|
||||||
aria-controls="mobile-nav"
|
aria-controls="mobile-nav"
|
||||||
onClick={() => setOpen((v) => !v)}
|
onClick={() => setOpen((v) => !v)}
|
||||||
|
|
|
||||||
|
|
@ -54,10 +54,10 @@ function HeaderCatalogSearchInner() {
|
||||||
placeholder={t("searchPlaceholder")}
|
placeholder={t("searchPlaceholder")}
|
||||||
className="
|
className="
|
||||||
w-full rounded-lg border border-neutral-200 bg-neutral-50 py-2.5 pl-10 pr-10
|
w-full rounded-lg border border-neutral-200 bg-neutral-50 py-2.5 pl-10 pr-10
|
||||||
text-sm text-neutral-900 outline-none transition duration-150
|
text-sm text-neutral-800 outline-none transition duration-150
|
||||||
placeholder:text-neutral-400
|
placeholder:text-neutral-400
|
||||||
hover:border-neutral-300 hover:bg-white
|
hover:border-neutral-300
|
||||||
focus:border-neutral-300 focus:bg-white focus:ring-2 focus:ring-neutral-900/8
|
focus:bg-neutral-0 focus:border-brand-navy600 focus:ring-2 focus:ring-brand-navy/8
|
||||||
"
|
"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
|
@ -65,7 +65,7 @@ function HeaderCatalogSearchInner() {
|
||||||
{q.trim() && (
|
{q.trim() && (
|
||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
className="absolute right-2.5 top-1/2 -translate-y-1/2 rounded-md bg-neutral-900 px-2 py-1 text-[11px] font-semibold text-white transition hover:bg-neutral-700"
|
className="absolute right-2.5 top-1/2 -translate-y-1/2 rounded-md bg-brand-navy px-2 py-1 text-[11px] font-semibold text-white transition hover:bg-brand-navy800"
|
||||||
>
|
>
|
||||||
↵
|
↵
|
||||||
</button>
|
</button>
|
||||||
|
|
|
||||||
|
|
@ -13,8 +13,8 @@ export function LanguageSwitcher() {
|
||||||
href={pathname}
|
href={pathname}
|
||||||
locale="en"
|
locale="en"
|
||||||
className={`rounded-md px-2.5 py-1.5 text-[11px] font-semibold tracking-wide transition-all duration-150 ${locale === "en"
|
className={`rounded-md px-2.5 py-1.5 text-[11px] font-semibold tracking-wide transition-all duration-150 ${locale === "en"
|
||||||
? "bg-white text-neutral-900"
|
? "bg-white text-brand-navy"
|
||||||
: "text-neutral-400 hover:text-neutral-700"
|
: "text-neutral-500 hover:text-brand-navy"
|
||||||
}`}
|
}`}
|
||||||
aria-current={locale === "en" ? "true" : undefined}
|
aria-current={locale === "en" ? "true" : undefined}
|
||||||
>
|
>
|
||||||
|
|
@ -24,8 +24,8 @@ export function LanguageSwitcher() {
|
||||||
href={pathname}
|
href={pathname}
|
||||||
locale="am"
|
locale="am"
|
||||||
className={`rounded-md px-2.5 py-1.5 text-[11px] font-semibold tracking-wide transition-all duration-150 ${locale === "am"
|
className={`rounded-md px-2.5 py-1.5 text-[11px] font-semibold tracking-wide transition-all duration-150 ${locale === "am"
|
||||||
? "bg-white text-neutral-900"
|
? "bg-white text-brand-navy"
|
||||||
: "text-neutral-400 hover:text-neutral-700"
|
: "text-neutral-500 hover:text-brand-navy"
|
||||||
}`}
|
}`}
|
||||||
aria-current={locale === "am" ? "true" : undefined}
|
aria-current={locale === "am" ? "true" : undefined}
|
||||||
>
|
>
|
||||||
|
|
|
||||||
|
|
@ -40,18 +40,18 @@ export function ProductAddToCart({ product }: { product: Product }) {
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="rounded-lg border border-neutral-100 bg-white p-5">
|
<div className="rounded-lg border border-neutral-200 bg-neutral-50 p-6">
|
||||||
<h2 className="text-[11px] font-semibold uppercase tracking-widest text-neutral-400">
|
<h2 className="text-[11px] font-semibold uppercase tracking-widest text-brand-gold">
|
||||||
{t("options")}
|
{t("options")}
|
||||||
</h2>
|
</h2>
|
||||||
<div className="mt-4 space-y-4">
|
<div className="mt-4 space-y-4">
|
||||||
{product.variants.map((v) => (
|
{product.variants.map((v) => (
|
||||||
<div key={v.key}>
|
<div key={v.key}>
|
||||||
<label className="text-xs font-medium text-neutral-600">
|
<label className="text-sm font-semibold text-neutral-700">
|
||||||
{tv(v.labelKey)}
|
{tv(v.labelKey)}
|
||||||
</label>
|
</label>
|
||||||
<select
|
<select
|
||||||
className="mt-1 w-full rounded-lg border border-neutral-200 bg-neutral-50 px-3 py-2.5 text-sm font-medium text-neutral-900 outline-none transition duration-150 focus:border-neutral-300 focus:bg-white focus:ring-2 focus:ring-neutral-900/8"
|
className="mt-1.5 w-full rounded-lg border border-neutral-200 bg-white px-3 py-2.5 text-sm font-normal text-neutral-800 outline-none transition duration-150 focus:border-brand-navy600 focus:ring-2 focus:ring-brand-navy/8"
|
||||||
value={selections[v.key]}
|
value={selections[v.key]}
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
setSelections((s) => ({ ...s, [v.key]: e.target.value }))
|
setSelections((s) => ({ ...s, [v.key]: e.target.value }))
|
||||||
|
|
@ -68,7 +68,7 @@ export function ProductAddToCart({ product }: { product: Product }) {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="mt-6">
|
<div className="mt-6">
|
||||||
<label htmlFor="qty" className="text-xs font-medium text-neutral-600">
|
<label htmlFor="qty" className="text-sm font-semibold text-neutral-700">
|
||||||
{t("qty")}
|
{t("qty")}
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
|
|
@ -78,13 +78,13 @@ export function ProductAddToCart({ product }: { product: Product }) {
|
||||||
max={9999}
|
max={9999}
|
||||||
value={qty}
|
value={qty}
|
||||||
onChange={(e) => setQty(Math.max(1, Number(e.target.value) || 1))}
|
onChange={(e) => setQty(Math.max(1, Number(e.target.value) || 1))}
|
||||||
className="mt-2 w-full rounded-lg border border-neutral-200 bg-neutral-50 px-4 py-2.5 text-sm font-semibold outline-none transition duration-150 focus:border-neutral-300 focus:bg-white focus:ring-2 focus:ring-neutral-900/8"
|
className="mt-1.5 w-full rounded-lg border border-neutral-200 bg-white px-4 py-2.5 text-sm font-normal text-neutral-800 outline-none transition duration-150 focus:border-brand-navy600 focus:ring-2 focus:ring-brand-navy/8"
|
||||||
/>
|
/>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={handleAdd}
|
onClick={handleAdd}
|
||||||
disabled={!canSubmit}
|
disabled={!canSubmit}
|
||||||
className="mt-4 w-full rounded-lg bg-neutral-900 py-3 text-sm font-semibold text-white transition hover:bg-neutral-800 disabled:cursor-not-allowed disabled:opacity-40"
|
className="mt-6 w-full rounded-lg bg-brand-navy py-3 text-sm font-semibold text-white transition duration-150 hover:bg-brand-navy800 disabled:cursor-not-allowed disabled:opacity-40"
|
||||||
>
|
>
|
||||||
{added ? `✓ ${t("add")}` : t("add")}
|
{added ? `✓ ${t("add")}` : t("add")}
|
||||||
</button>
|
</button>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user