feat: migrate landing from Next.js to Nuxt 4 (Vue 3)

Full 1:1 migration preserving all visuals, animations and behavior:
- Nuxt 4 + Vue 3 (script setup, TypeScript strict) with SSG output
- Tailwind CSS v4 with @tailwindcss/vite
- Ant Design Vue 4 (dark theme, RTL, Persian locale) for design system
- Pinia for state management (ui + contact stores)
- motion-v (framer-motion port) for all animations
- lucide-vue-next for icons
- SEO improvements: og:image, canonical fix, real 404 page, JSON-LD services
- JS bundle reduced from 302KB to 239KB gzip
- Docker/nginx deployment preserved (out/ -> nginx)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
S. Esmaeili
2026-10-06 16:28:05 +03:30
co-authored by Claude Opus 4.6
parent 1af5b555b8
commit e31ef5a21f
127 changed files with 12160 additions and 3464 deletions
+5 -3
View File
@@ -1,10 +1,12 @@
node_modules
.next
.nuxt
.output
.git
.gitignore
src
app
server
shared
public
_old_vite_backup
*.md
_dev.log
.env*
+6 -6
View File
@@ -1,10 +1,12 @@
# dependencies
node_modules/
# next.js
.next/
# nuxt
.nuxt/
.output/
.data/
dist
# out/ عمداً در git نگه داشته می‌شود (بیلد محلی → دیپلوی با Docker)
next-env.d.ts
# misc
.DS_Store
@@ -14,7 +16,5 @@ next-env.d.ts
npm-debug.log*
# local env files
.env
.env*.local
# old vite backup (remove once migration is verified and no longer needed)
_old_vite_backup/
+82 -71
View File
@@ -1,35 +1,64 @@
# آریا ایمن — لندینگ (Next.js)
# آریا ایمن — لندینگ (Nuxt)
لندینگ شرکت آریا ایمن، مهاجرت‌یافته از Vite/React به Next.js (App Router) با خروجی کاملاً استاتیک، بهینه برای سئو و سرعت.
لندینگ شرکت آریا ایمن، مهاجرت‌یافته از Next.js به **Nuxt 4 (Vue 3)** با خروجی کاملاً استاتیک (SSG)، بهینه برای سئو و سرعت.
ظاهر و رفتار صفحه دقیقاً همان نسخه Next.js است.
## نصب
| لایه | ابزار |
|---|---|
| فریم‌ورک | Nuxt 4 + Vue 3 (`<script setup>`, TypeScript strict) |
| استایل | Tailwind CSS v4 (`@tailwindcss/vite`) — توکن‌ها در `app/assets/css/main.css` |
| Design system | Ant Design Vue 4 — تم تیره با پالت پروژه در `app/design/theme.ts`، RTL و لوکال فارسی |
| State management | Pinia (`app/stores/`) |
| انیمیشن | motion-v (پورت Motion/framer-motion برای Vue) |
| آیکن | lucide-vue-next |
## نصب و اجرا
```bash
npm install
npm run dev # http://localhost:3000
npm run typecheck # بررسی TypeScript
```
## اجرا در حالت توسعه
```bash
npm run dev
```
سپس آدرس `http://localhost:3000` را باز کنید.
## Build (تولید نسخه نهایی استاتیک)
```bash
npm run build
npm run build # = nuxt generate
```
خروجی در پوشه `out/` ساخته می‌شود — مجموعه‌ای از فایل‌های HTML/CSS/JS کاملاً استاتیک که نیازی به سرور Node ندارند.
خروجی در پوشه `out/` ساخته می‌شود — فایل‌های HTML/CSS/JS کاملاً استاتیک که نیازی به سرور Node ندارند
(`index.html`، `404.html`، `robots.txt`، `sitemap.xml`، `manifest.webmanifest`، `_nuxt/`).
برای پیش‌نمایش محلی خروجی build:
پیش‌نمایش محلی خروجی:
```bash
npx serve out
npm run preview # = npx serve out
```
## ساختار پروژه
```
app/
app.vue ConfigProvider (antd) + سئوی سراسری + JSON-LD سازمان/وب‌سایت
error.vue صفحه ۴۰۴ / خطا (noindex) — در build به‌صورت out/404.html پیش‌رندر می‌شود
pages/index.vue صفحه اصلی (AppShell → ۱۰ سکشن + Footer)، canonical
components/layout/ AppShell, Header, Footer, Preloader, Cursor, BackToTop
components/sections/ Hero, TapeMarquee, Stats, CountUp, Products, About, Services, Standards, Industries, Testimonials, Contact
components/ui/ Logo, Reveal, SectionHead
composables/ useSiteSeo, useJsonLd, useTehranClock, useScrolledPast
stores/ ui (Preloader/منوی موبایل)، contact (فرم تماس)
design/theme.ts توکن‌های Ant Design (هم‌گام با @theme در main.css)
assets/css/ main.css (Tailwind + فونت‌ها)، antd-overrides.css
plugins/antd-style.ts استخراج CSS-in-JS آنت‌دیزاین در SSR (بدون FOUC)
shared/ config/site.ts و seo/schema.ts (مشترک بین app و server)
server/routes/ robots.txt، sitemap.xml، manifest.webmanifest
public/ فونت‌ها، تصاویر، favicon.svg
```
### نکات Ant Design + Tailwind
- استایل‌های Tailwind v4 داخل cascade layer هستند و CSS-in-JS آنت‌دیزاین بیرون از layer؛ بنابراین روی کامپوننت‌های antd کلاس ظاهری Tailwind نگذارید. ظاهر کامپوننت‌های antd از توکن‌ها (`app/design/theme.ts`) و `app/assets/css/antd-overrides.css` می‌آید.
- پالت رنگ در دو جا تعریف شده (`@theme` در `main.css` برای Tailwind و `palette` در `theme.ts` برای antd)؛ هنگام تغییر، هر دو را هم‌گام کنید.
## دیپلوی پروداکشن با Docker
بیلد روی ماشین توسعه انجام می‌شود؛ خروجی `out/` داخل گیت commit می‌شود و روی سرور فقط image ساخته و اجرا می‌شود (بدون `npm install` / `npm run build` در سرور).
@@ -61,78 +90,60 @@ docker build -t aria-imen-landing .
docker run -d --name aria-imen-landing -p 8080:80 --restart unless-stopped aria-imen-landing
```
تنظیمات Nginx داخل image مطابق export استاتیک Next.js است (`try_files`، کش `/_next/static/`، صفحه `404.html`).
تنظیمات Nginx داخل image مطابق خروجی استاتیک Nuxt است (`try_files`، کش immutable برای `/_nuxt/`، صفحه `404.html`).
## آپلود روی هاست (Static Hosting)
این پروژه با `output: 'export'` پیکربندی شده، یعنی:
1. `npm run build` را اجرا کنید.
2. کل محتوای پوشه `out/` را (نه خود پوشه، بلکه فایل‌های داخلش) در ریشه هاست (public_html، یا مشابه آن در cPanel/Nginx) آپلود کنید.
3. سایت بدون نیاز به Node.js یا هیچ سرور اختصاصی کار می‌کند.
### نکات مهم هاست استاتیک (cPanel / Nginx / هاست ایرانی)
- **ریدایرکت‌ها و هدرها** در `next.config.ts` روی static export اجرا نمی‌شوند. اگر بعداً نیاز به ریدایرکت ۳۰۱ داشتید (مثلاً از `/index.html`)، باید در سطح هاست تنظیم شود:
**Apache (.htaccess):**
```apache
<IfModule mod_expires.c>
ExpiresActive On
ExpiresByType text/css "access plus 1 year"
ExpiresByType application/javascript "access plus 1 year"
ExpiresByType font/woff2 "access plus 1 year"
</IfModule>
ErrorDocument 404 /404.html
```
**Apache (.htaccess):**
```apache
Redirect 301 /index.html /
<IfModule mod_expires.c>
ExpiresActive On
ExpiresByType text/css "access plus 1 year"
ExpiresByType application/javascript "access plus 1 year"
ExpiresByType image/webp "access plus 1 year"
ExpiresByType font/woff2 "access plus 1 year"
</IfModule>
ErrorDocument 404 /404.html
```
**Nginx:**
```nginx
location /_nuxt/ { expires 1y; add_header Cache-Control "public, immutable"; }
location / { try_files $uri $uri.html $uri/index.html =404; }
error_page 404 /404.html;
gzip on; gzip_types text/css application/javascript application/json image/svg+xml;
```
**Nginx:**
```nginx
location /_next/static/ { expires 1y; add_header Cache-Control "public, immutable"; }
location / { try_files $uri $uri.html $uri/index.html =404; }
error_page 404 /404.html;
gzip on; gzip_types text/css application/javascript image/svg+xml;
```
ریدایرکت `location = /index.html { return 301 /; }` را نگذارید؛ با `index index.html` حلقهٔ ۳۰۱ روی `/` می‌سازد.
ریدایرکت `location = /index.html { return 301 /; }` را نگذارید؛ با `index index.html` حلقهٔ ۳۰۱ روی `/` می‌سازد.
## پیش از دیپلوی واقعی — کارهایی که باید انجام دهید
این پروژه فعلاً با داده‌های تستی/placeholder برای موارد زیر ساخته شده و **باید قبل از انتشار واقعی بررسی و اصلاح شوند**:
1. **دامنه واقعی**: در فایل [`src/config/site.ts`](src/config/site.ts) مقدار `url: "https://ariaiman.ir"` باید با دامنه نهایی واقعی جایگزین شود (این مقدار روی canonical، Open Graph، sitemap و JSON-LD اثر مستقیم دارد).
1. **دامنه واقعی**: در [`shared/config/site.ts`](shared/config/site.ts) مقدار `url: "https://ariaiman.ir"` را با دامنه نهایی جایگزین کنید (روی canonical، Open Graph، sitemap، robots و JSON-LD اثر مستقیم دارد).
2. **لوگوی رسمی**: در همان فایل، `logo` فعلاً به تصویر hero اشاره می‌کند؛ یک فایل PNG/SVG لوگوی واقعی جایگزین شود.
3. **آیکون‌های PWA**: `src/app/manifest.ts` فعلاً بدون آیکون است. دو فایل `icon-192.png` و `icon-512.png` در `public/` قرار داده و در manifest معرفی شوند.
4. **تصویر Open Graph**: یک فایل `opengraph-image.png` با ابعاد ۱۲۰۰×۶۳۰ در `src/app/` قرار دهید (Next.js آن را به‌صورت خودکار شناسایی می‌کند).
5. **شبکه‌های اجتماعی**: در `src/config/site.ts`، آرایه `socials` خالی است. لینک‌های واقعی اینستاگرام/تلگرام/لینکدین را اضافه کنید تا در Schema.org (`sameAs`) درج شود.
6. **فرم تماس**: فرم بخش «تماس» فعلاً فقط یک شبیه‌سازی محلی است (بدون ارسال واقعی به سرور یا سرویس ایمیل) — دقیقاً همان رفتاری که در نسخه قبلی هم وجود داشت. برای فعال‌سازی واقعی باید به یک سرویس فرم (مثل فرم‌اسپری، ایمیل، یا API اختصاصی) متصل شود.
7. **npm audit**: یک آسیب‌پذیری «high» در نسخه postcss داخلی Next.js 15.5 گزارش می‌شود (`npm audit`). این مربوط به مرحله build/dev است، نه کد سمت کاربر نهایی. رفع کامل آن نیازمند ارتقا به Next.js 16 (breaking change) است — در صورت تمایل به‌روزرسانی جداگانه انجام شود.
3. **تصویر Open Graph**: `ogImage` فعلاً `hero.jpg` است؛ یک تصویر اختصاصی ۱۲۰۰×۶۳۰ در `public/` قرار دهید و مسیر/ابعاد را در `site.ts` به‌روز کنید.
4. **آیکون‌های PWA**: manifest فعلاً از `favicon.svg` استفاده می‌کند؛ برای سازگاری کامل، `icon-192.png`، `icon-512.png` و `apple-touch-icon.png` اضافه شوند.
5. **شبکه‌های اجتماعی**: آرایه `socials` در `site.ts` خالی است؛ لینک‌های واقعی را اضافه کنید تا در Schema.org (`sameAs`) درج شوند.
6. **فرم تماس**: اکشن `submit` در [`app/stores/contact.ts`](app/stores/contact.ts) فعلاً فقط ارسال را شبیه‌سازی می‌کند (مثل نسخه قبلی)؛ برای فعال‌سازی واقعی به API/سرویس فرم متصل شود.
## چک‌لیست سئوی پیاده‌سازی‌شده
## چک‌لیست سئو
| مورد | وضعیت | فایل |
|---|---|---|
| یک H1 با کلمه کلیدی اصلی | ✅ | `src/components/sections/Hero.tsx` |
| سلسله‌مراتب H2/H3 منطقی، بدون پرش سطح | ✅ | `src/components/ui/SectionHead.tsx` و همه سکشن‌ها |
| عنوان و توضیحات با طول مناسب | ✅ | `src/config/site.ts` |
| Canonical، metadataBase، Open Graph، Twitter Card | ✅ | `src/app/layout.tsx` |
| `lang="fa"` و `dir="rtl"` | ✅ | `src/app/layout.tsx` |
| JSON-LD (LocalBusiness + WebSite) | ✅ | `src/lib/seo/schema.ts`, `src/components/seo/JsonLd.tsx` |
| `sitemap.xml` | ✅ | `src/app/sitemap.ts` |
| `robots.txt` | ✅ | `src/app/robots.ts` |
| `manifest.webmanifest` | ✅ (بدون آیکون — نیاز به تکمیل) | `src/app/manifest.ts` |
| صفحه ۴۰۴ واقعی (بدون ریدایرکت به خانه) | ✅ | `src/app/not-found.tsx` |
| alt توصیفی روی همه تصاویر محتوایی | ✅ | همه کامپوننت‌های سکشن |
| تمام متن در HTML بدون نیاز به اجرای JS | ✅ | تأیید شده با بررسی خروجی build |
| لینک‌های داخلی سالم (`#anchor`) | ✅ | بدون تغییر نسبت به نسخه اصلی |
| فونت خودمیزبان (self-hosted) بدون CDN خارجی | ✅ | `public/fonts/`, `src/app/layout.tsx` |
| تصاویر بدون وابستگی به CDN خارجی | ✅ | تصاویر Industries از pexels دانلود و داخلی شدند |
## معماری فنی مهاجرت
- **Server Components برای محتوا**: تمام بخش‌های بدون تعامل (Hero, About, Standards, TapeMarquee, Footer) به‌صورت Server Component هستند و در HTML build نهایی کامل قابل مشاهده‌اند.
- **Client Components فقط برای تعامل**: منو موبایل، فرم تماس، اسلایدر نظرات، پنل‌های تعاملی محصولات/خدمات، شمارنده آمار، کرسر سفارشی و Preloader، هرکدام جداگانه `'use client'` هستند تا حجم JS اولیه صفحه کمینه بماند.
- **بدون بک‌اند**: هیچ `app/api`، دیتابیس یا server action اضافه نشده؛ طبق درخواست پروژه.
- **انیمیشن ورودی Hero با CSS خالص**: برخلاف نسخه اصلی (که H1 تا پایان انیمیشن Preloader با `opacity:0` مخفی بود)، اکنون محتوای Hero همیشه در HTML اولیه قابل مشاهده است و فقط با CSS keyframes (بدون وابستگی به اجرای JS) وارد صفحه می‌شود — این تغییر برای رفع مشکل LCP/SEO با تأیید شما انجام شد.
| یک H1 با کلمه کلیدی اصلی | ✅ | `app/components/sections/Hero.vue` |
| سلسله‌مراتب H2/H3 منطقی، بدون پرش سطح | ✅ | `app/components/ui/SectionHead.vue` و همه سکشن‌ها |
| عنوان، title template و توضیحات | ✅ | `shared/config/site.ts`, `app/composables/useSiteSeo.ts` |
| Canonical یکسان با sitemap (`https://ariaiman.ir/`) | ✅ | `app/pages/index.vue` |
| Open Graph و Twitter Card **همراه با تصویر** (og:image/twitter:image) | ✅ | `app/composables/useSiteSeo.ts` |
| `lang="fa"` و `dir="rtl"` | ✅ | `nuxt.config.ts` |
| JSON-LD: LocalBusiness (+ ساعات کاری، سال تأسیس، منطقه خدمات)، WebSite، Service ×۴ | ✅ | `shared/seo/schema.ts`, `app/composables/useJsonLd.ts` |
| `sitemap.xml` / `robots.txt` / `manifest.webmanifest` (با آیکون) | ✅ | `server/routes/` |
| صفحه ۴۰۴ واقعی پیش‌رندرشده، `noindex` و بدون canonical | ✅ | `app/error.vue`, `nuxt.config.ts` |
| preload تصویر hero و فونت‌ها | ✅ | `app/pages/index.vue`, `nuxt.config.ts` |
| alt توصیفی روی همه تصاویر محتوایی | ✅ | همه سکشن‌ها |
| تمام متن در HTML بدون نیاز به اجرای JS | ✅ | خروجی `out/index.html` |
| فونت و تصاویر خودمیزبان (بدون CDN) | ✅ | `public/` |
+19
View File
@@ -0,0 +1,19 @@
<script setup lang="ts">
import { ConfigProvider, StyleProvider } from "ant-design-vue";
import faIR from "ant-design-vue/es/locale/fa_IR";
import { antdTheme } from "~/design/theme";
import { organizationSchema, websiteSchema } from "#shared/seo/schema";
const { $antdCache } = useNuxtApp();
useSiteSeo();
useJsonLd([organizationSchema(), websiteSchema()]);
</script>
<template>
<StyleProvider :cache="$antdCache">
<ConfigProvider :theme="antdTheme" direction="rtl" :locale="faIR">
<NuxtPage />
</ConfigProvider>
</StyleProvider>
</template>
+118
View File
@@ -0,0 +1,118 @@
/*
* Ant Design Vue ↔ project look.
* antd's CSS-in-JS is unlayered while Tailwind v4 lives in cascade layers, so Tailwind classes
* can't restyle antd elements. These unlayered rules pin antd form controls to the exact
* pre-migration styling (same values as the old Tailwind `inputCls` / submit button).
*/
/* ---- form shell ---- */
.contact-form.ant-form {
font-family: inherit;
font-size: inherit;
line-height: inherit;
}
/* antd resets items and their grid wrappers to 14px/1.5714; inherit like the original markup did
(the control line box decides the gap under the inline-block textarea) */
.contact-form .ant-form-item,
.contact-form .ant-form-item .ant-row,
.contact-form .ant-form-item .ant-col {
margin-bottom: 0;
font-size: inherit;
line-height: inherit;
}
.contact-form .ant-form-item .ant-form-item-label {
padding: 0 0 8px; /* mb-2 */
line-height: 16px;
}
.contact-form .ant-form-item-label > label {
width: 100%; /* original labels were full-width blocks */
height: auto;
font-size: 12px; /* text-xs */
line-height: 16px;
color: var(--color-muted);
}
.contact-form .ant-form-item-explain {
font-size: 12px;
line-height: 20px;
padding-top: 4px;
}
/* ---- inputs (= w-full rounded-lg border border-line bg-bg px-4 py-3.5 text-sm text-ink) ---- */
.contact-form .ant-input {
width: 100%;
border-radius: 8px;
border: 1px solid var(--color-line);
background-color: var(--color-bg);
padding: 14px 16px;
font-family: inherit;
font-size: 14px;
line-height: 20px;
color: var(--color-ink);
box-shadow: none;
transition-property: color, background-color, border-color;
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
transition-duration: 150ms;
}
.contact-form .ant-input::placeholder {
color: color-mix(in oklab, var(--color-muted) 50%, transparent);
}
.contact-form .ant-input:hover {
border-color: var(--color-line);
}
.contact-form .ant-input:focus,
.contact-form .ant-input-focused {
border-color: var(--color-accent);
outline: none;
box-shadow: none;
}
/* antd's .ant-input-rtl forces direction:rtl; honour an explicit dir="ltr" (phone field) */
.contact-form .ant-input[dir="ltr"] {
direction: ltr;
text-align: left;
}
.contact-form textarea.ant-input {
resize: none;
min-height: 0;
vertical-align: baseline; /* native inline-block textarea alignment (antd sets bottom) */
}
/* ---- submit (= rounded-full bg-accent py-4 font-bold text-bg, hover:bg-accent-soft + glow) ---- */
.contact-form .ant-btn.contact-submit {
display: flex;
width: 100%;
height: auto;
align-items: center;
justify-content: center;
gap: 10px;
border: 0;
border-radius: calc(infinity * 1px); /* = tailwind rounded-full */
background-color: var(--color-accent);
padding: 16px 0;
font-family: inherit;
font-size: 16px;
line-height: 24px;
font-weight: 700;
color: var(--color-bg);
box-shadow: none;
cursor: default;
transition: all 300ms cubic-bezier(0.4, 0, 0.2, 1);
}
.contact-form .ant-btn.contact-submit > span {
display: inline;
}
@media (hover: hover) {
.contact-form .ant-btn.contact-submit:hover {
background-color: var(--color-accent-soft);
color: var(--color-bg);
box-shadow: 0 8px 45px rgba(255, 90, 0, 0.45);
}
}
.contact-form .ant-btn.contact-submit:disabled {
opacity: 0.6;
border: 0;
}
/* no click-wave ring (wasn't part of the original button) */
.contact-form .ant-btn.contact-submit::after,
.contact-form .ant-wave {
display: none;
}
@@ -1,8 +1,61 @@
@import "tailwindcss";
/* ---- local fonts (replaces next/font/local) ----
Same family names, fallback faces and metric overrides that next/font generated, so text
(including Persian glyphs inside font-mono, which fall back to the size-adjusted Arial face)
renders exactly as before. */
@font-face {
font-family: "vazirmatn";
src: url("/fonts/Vazirmatn-Variable.woff2") format("woff2");
font-weight: 100 900;
font-display: swap;
}
@font-face {
font-family: "vazirmatn Fallback";
src: local("Arial");
ascent-override: 102.54%;
descent-override: 53.71%;
line-gap-override: 0%;
size-adjust: 100%;
}
@font-face {
font-family: "ibmPlexMono";
src: url("/fonts/IBMPlexMono-Regular.woff2") format("woff2");
font-weight: 400;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: "ibmPlexMono";
src: url("/fonts/IBMPlexMono-Medium.woff2") format("woff2");
font-weight: 500;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: "ibmPlexMono";
src: url("/fonts/IBMPlexMono-SemiBold.woff2") format("woff2");
font-weight: 600;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: "ibmPlexMono Fallback";
src: local("Arial");
ascent-override: 77.95%;
descent-override: 20.91%;
line-gap-override: 0%;
size-adjust: 131.49%;
}
:root {
--font-vazirmatn: "vazirmatn", "vazirmatn Fallback", Tahoma, system-ui, sans-serif;
--font-ibm-plex-mono: "ibmPlexMono", "ibmPlexMono Fallback", ui-monospace, monospace;
}
@theme {
--font-sans: var(--font-vazirmatn), ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif; /* added for migration: next/font variable */
--font-mono: var(--font-ibm-plex-mono), "Vazirmatn", ui-monospace, monospace; /* added for migration: next/font variable */
--font-sans: var(--font-vazirmatn), ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif; /* font variables defined in :root above */
--font-mono: var(--font-ibm-plex-mono), "Vazirmatn", ui-monospace, monospace; /* font variables defined in :root above */
--color-bg: #0a0a0c;
--color-panel: #121216;
+24
View File
@@ -0,0 +1,24 @@
<script setup lang="ts">
import { AnimatePresence } from "motion-v";
import Preloader from "./Preloader.vue";
import Cursor from "./Cursor.vue";
import Header from "./Header.vue";
import BackToTop from "./BackToTop.vue";
const ui = useUiStore();
</script>
<template>
<AnimatePresence>
<Preloader v-if="!ui.ready" @complete="ui.setReady" />
</AnimatePresence>
<Cursor />
<div class="noise" aria-hidden="true" />
<Header />
<slot />
<BackToTop />
</template>
+22
View File
@@ -0,0 +1,22 @@
<script setup lang="ts">
import { AnimatePresence, motion } from "motion-v";
import { ArrowUp } from "lucide-vue-next";
const show = useScrolledPast(700);
</script>
<template>
<AnimatePresence>
<motion.a
v-if="show"
href="#home"
aria-label="بازگشت به بالا"
:initial="{ opacity: 0, y: 24 }"
:animate="{ opacity: 1, y: 0 }"
:exit="{ opacity: 0, y: 24 }"
class="fixed bottom-6 left-6 z-[70] flex size-12 items-center justify-center rounded-full border border-line bg-panel/90 text-ink shadow-[0_10px_40px_rgba(0,0,0,0.5)] backdrop-blur transition-colors hover:border-accent hover:text-accent"
>
<ArrowUp class="size-5" />
</motion.a>
</AnimatePresence>
</template>
+52
View File
@@ -0,0 +1,52 @@
<script setup lang="ts">
import { motion, useMotionValue, useSpring } from "motion-v";
const x = useMotionValue(-100);
const y = useMotionValue(-100);
const sx = useSpring(x, { stiffness: 550, damping: 45, mass: 0.4 });
const sy = useSpring(y, { stiffness: 550, damping: 45, mass: 0.4 });
const hover = ref(false);
const visible = ref(false);
const move = (e: MouseEvent) => {
x.set(e.clientX);
y.set(e.clientY);
visible.value = true;
};
const over = (e: MouseEvent) => {
const t = e.target as HTMLElement | null;
hover.value = !!t?.closest?.(
"a, button, input, textarea, select, label, [data-hover]",
);
};
onMounted(() => {
window.addEventListener("mousemove", move, { passive: true });
window.addEventListener("mouseover", over, { passive: true });
});
onBeforeUnmount(() => {
window.removeEventListener("mousemove", move);
window.removeEventListener("mouseover", over);
});
</script>
<template>
<div class="pointer-events-none fixed inset-0 z-[120] hidden [@media(pointer:fine)]:block">
<motion.div :style="{ x: sx, y: sy }" class="absolute -left-4 -top-4 size-8">
<motion.div
:animate="{
scale: hover ? 1.9 : 1,
opacity: visible ? 1 : 0,
rotate: hover ? 45 : 0,
}"
:transition="{ type: 'spring', stiffness: 320, damping: 24 }"
class="size-8 rounded-md border-2 border-accent/80"
/>
<div
:class="`absolute inset-0 m-auto size-1 rounded-full bg-accent transition-opacity duration-300 ${
visible ? 'opacity-100' : 'opacity-0'
}`"
/>
</motion.div>
</div>
</template>
+118
View File
@@ -0,0 +1,118 @@
<script setup lang="ts">
import { AtSign, Globe, Phone, Send } from "lucide-vue-next";
import Logo from "~/components/ui/Logo.vue";
const QUICK_LINKS = [
{ href: "#home", label: "خانه" },
{ href: "#products", label: "محصولات" },
{ href: "#about", label: "درباره ما" },
{ href: "#services", label: "خدمات" },
{ href: "#standards", label: "استانداردها" },
{ href: "#industries", label: "صنایع" },
];
const PRODUCT_LINKS = [
"کلاه ایمنی",
"دستکش ضدبرش",
"کفش ایمنی",
"عینک ایمنی",
"هارنس ضدسقوط",
"لباس کار بازتابنده",
];
const SOCIALS = [
{ icon: Globe, label: "وب‌سایت" },
{ icon: Send, label: "تلگرام" },
{ icon: AtSign, label: "ایمیل" },
{ icon: Phone, label: "تماس" },
];
</script>
<template>
<footer class="relative overflow-hidden border-t border-line">
<div class="hazard h-2.5" />
<div class="mx-auto max-w-7xl px-5 md:px-8">
<div class="grid gap-12 py-16 md:grid-cols-12 md:py-20">
<!-- brand -->
<div class="md:col-span-5">
<Logo />
<p class="mt-6 max-w-sm text-sm leading-8 text-muted">
تأمین‌کننده تخصصی تجهیزات حفاظت فردی و راهکارهای ایمنی صنعتی؛
همراه سازمان‌ها از سال ۱۳۸۶ در مسیر صفر شدن حوادث کاری.
</p>
<div class="mt-7 flex gap-3">
<a
v-for="s in SOCIALS"
:key="s.label"
href="#contact"
:aria-label="s.label"
class="flex size-10 items-center justify-center rounded-lg border border-line text-muted transition-all duration-300 hover:border-accent hover:bg-accent hover:text-bg"
>
<component :is="s.icon" class="size-4" />
</a>
</div>
</div>
<!-- quick links -->
<div class="md:col-span-2">
<div class="font-mono text-xs tracking-[0.25em] text-accent">// دسترسی</div>
<ul class="mt-5 space-y-3">
<li v-for="l in QUICK_LINKS" :key="l.href">
<a :href="l.href" class="text-sm text-muted transition-colors hover:text-ink">
{{ l.label }}
</a>
</li>
</ul>
</div>
<!-- products -->
<div class="md:col-span-2">
<div class="font-mono text-xs tracking-[0.25em] text-accent">// محصولات</div>
<ul class="mt-5 space-y-3">
<li v-for="p in PRODUCT_LINKS" :key="p">
<a href="#products" class="text-sm text-muted transition-colors hover:text-ink">
{{ p }}
</a>
</li>
</ul>
</div>
<!-- contact -->
<div class="md:col-span-3">
<div class="font-mono text-xs tracking-[0.25em] text-accent">// ارتباط</div>
<address class="mt-5 space-y-3 text-sm text-muted not-italic">
<p class="leading-7">
تهران، خیابان کارگر شمالی،
<br />
خیابان فر، کوچه دوم، پلاک ۱۲
</p>
<p class="font-mono tracking-wider" dir="ltr">۰۲۱ - ۹۱ ۰۰ ۸۸ ۰۰</p>
<p class="font-mono tracking-wider" dir="ltr">info@ariaiman.ir</p>
<p class="pt-2 text-xs leading-6">
شنبه تا پنجشنبه — ۸:۰۰ تا ۱۷:۰۰
<br />
پشتیبانی اضطراری: ۲۴/۷
</p>
</address>
</div>
</div>
<!-- giant wordmark -->
<div aria-hidden="true" class="pointer-events-none select-none pb-6">
<div class="text-outline text-center text-[17vw] font-black leading-[0.85] md:text-[11vw]">
آریا ایمن
</div>
</div>
<div
class="flex flex-wrap items-center justify-between gap-4 border-t border-line py-6 text-xs text-muted"
>
<div>© ۱۴۰۴ آریا ایمن — تمامی حقوق محفوظ است.</div>
<div class="font-mono text-[10px] tracking-[0.25em]">
ISO 9001:2015 — CE CERTIFIED PPE SUPPLIER
</div>
</div>
</div>
</footer>
</template>
+135
View File
@@ -0,0 +1,135 @@
<script setup lang="ts">
import { AnimatePresence, motion } from "motion-v";
import { Menu, Phone, X } from "lucide-vue-next";
import Logo from "~/components/ui/Logo.vue";
const LINKS = [
{ href: "#home", label: "خانه" },
{ href: "#products", label: "محصولات" },
{ href: "#about", label: "درباره ما" },
{ href: "#services", label: "خدمات" },
{ href: "#standards", label: "استانداردها" },
{ href: "#industries", label: "صنایع" },
{ href: "#contact", label: "تماس" },
];
const ui = useUiStore();
const scrolled = useScrolledPast(40);
const { time, shift } = useTehranClock();
watch(
() => ui.menuOpen,
(open) => {
document.body.style.overflow = open ? "hidden" : "";
},
);
onBeforeUnmount(() => {
document.body.style.overflow = "";
});
</script>
<template>
<motion.header
:initial="{ y: -90, opacity: 0 }"
:animate="ui.ready ? { y: 0, opacity: 1 } : {}"
:transition="{ duration: 0.9, delay: 0.15, ease: EASE }"
:class="`fixed inset-x-0 top-0 z-[80] transition-all duration-500 ${
scrolled ? 'border-b border-line bg-bg/85 py-3 backdrop-blur-xl' : 'py-5'
}`"
>
<div class="mx-auto flex max-w-7xl items-center justify-between px-5 md:px-8">
<Logo />
<nav class="hidden items-center gap-7 lg:flex">
<a
v-for="l in LINKS"
:key="l.href"
:href="l.href"
class="group relative py-2 text-sm text-ink/75 transition-colors hover:text-ink"
>
{{ l.label }}
<span
class="absolute bottom-0 start-0 h-0.5 w-full origin-right scale-x-0 bg-accent transition-transform duration-300 group-hover:scale-x-100"
/>
</a>
</nav>
<div class="flex items-center gap-3">
<div
class="hidden items-center gap-2.5 rounded-full border border-line px-4 py-2 text-[11px] text-muted xl:flex"
>
<span class="size-1.5 animate-pulse rounded-full bg-emerald-400" />
<span class="font-mono">{{ time }}</span>
<span class="text-muted/50">|</span>
<span>تهران — شیفت {{ shift }}</span>
</div>
<a
href="#contact"
class="hidden items-center gap-2 rounded-full bg-accent px-5 py-2.5 text-sm font-bold text-bg transition-all duration-300 hover:bg-accent-soft hover:shadow-[0_0_30px_rgba(255,90,0,0.4)] md:flex"
>
<Phone class="size-4" />
درخواست مشاوره
</a>
<button
aria-label="باز کردن منو"
class="flex size-11 items-center justify-center rounded-lg border border-line text-ink transition-colors hover:border-accent hover:text-accent lg:hidden"
@click="ui.openMenu"
>
<Menu class="size-5" />
</button>
</div>
</div>
</motion.header>
<AnimatePresence>
<motion.div
v-if="ui.menuOpen"
:initial="{ opacity: 0 }"
:animate="{ opacity: 1 }"
:exit="{ opacity: 0 }"
:transition="{ duration: 0.35 }"
class="fixed inset-0 z-[110] flex flex-col bg-bg/95 backdrop-blur-2xl"
>
<div class="flex items-center justify-between border-b border-line px-5 py-4 md:px-8">
<Logo />
<button
aria-label="بستن منو"
class="flex size-11 items-center justify-center rounded-lg border border-line transition-colors hover:border-accent hover:text-accent"
@click="ui.closeMenu"
>
<X class="size-5" />
</button>
</div>
<nav class="flex flex-1 flex-col justify-center gap-1 overflow-y-auto px-8">
<motion.a
v-for="(l, i) in LINKS"
:key="l.href"
:href="l.href"
:initial="{ opacity: 0, x: 44 }"
:animate="{ opacity: 1, x: 0 }"
:transition="{ delay: 0.08 + i * 0.055, duration: 0.55, ease: EASE }"
class="group flex items-baseline gap-5 border-b border-line/60 py-4"
@click="ui.closeMenu"
>
<span class="font-mono text-xs text-accent">
{{ faNum(String(i + 1).padStart(2, "0")) }}
</span>
<span class="text-3xl font-black transition-colors group-hover:text-accent">
{{ l.label }}
</span>
</motion.a>
</nav>
<div class="flex items-center justify-between border-t border-line p-8 text-sm">
<div class="flex items-center gap-3 text-muted">
<Phone class="size-4 text-accent" />
<span class="font-mono">۰۲۱-۹۱۰۰۸۸۰۰</span>
</div>
<div class="text-xs text-muted">تهران — شیفت {{ shift }}، {{ time }}</div>
</div>
</motion.div>
</AnimatePresence>
</template>
+78
View File
@@ -0,0 +1,78 @@
<script setup lang="ts">
import { motion } from "motion-v";
import { ShieldCheck } from "lucide-vue-next";
const STEPS = [
"بررسی سیستم‌های ایمنی",
"تأیید استانداردهای بین‌المللی",
"بارگذاری تجهیزات حفاظت فردی",
"محیط کاری آماده است",
];
const emit = defineEmits<{ complete: [] }>();
const progress = ref(0);
let done = false;
let t: ReturnType<typeof setInterval> | undefined;
onMounted(() => {
let v = 0;
t = setInterval(() => {
v += Math.floor(Math.random() * 11) + 7;
if (v >= 100) {
v = 100;
clearInterval(t);
if (!done) {
done = true;
setTimeout(() => emit("complete"), 450);
}
}
progress.value = v;
}, 140);
});
onBeforeUnmount(() => clearInterval(t));
const step = computed(
() =>
STEPS[
Math.min(STEPS.length - 1, Math.floor((progress.value / 100) * STEPS.length))
],
);
</script>
<template>
<motion.div
:exit="{ y: '-100%' }"
:transition="{ duration: 0.85, ease: [0.76, 0, 0.24, 1] }"
class="fixed inset-0 z-[200] flex flex-col items-center justify-center bg-bg"
>
<div class="hazard absolute inset-x-0 top-0 h-3" />
<div class="hazard absolute inset-x-0 bottom-0 h-3" />
<div class="flex items-center gap-4">
<span class="flex size-12 items-center justify-center rounded-xl bg-accent text-bg">
<ShieldCheck class="size-7" />
</span>
<span class="font-mono text-xs tracking-[0.35em] text-muted">
ARIA IMEN CO.
</span>
</div>
<div class="mt-12 font-mono text-7xl font-semibold tabular-nums md:text-8xl">
{{ progress.toLocaleString("fa-IR") }}<span class="text-accent">٪</span>
</div>
<div class="mt-8 h-[3px] w-64 overflow-hidden rounded-full bg-line md:w-80">
<div
class="h-full bg-accent transition-all duration-200"
:style="{ width: `${progress}%` }"
/>
</div>
<div class="mt-6 flex items-center gap-3 text-xs text-muted">
<span class="size-1.5 animate-pulse rounded-full bg-accent" />
{{ step }}
<span class="animate-blink text-accent">▍</span>
</div>
</motion.div>
</template>
+103
View File
@@ -0,0 +1,103 @@
<script setup lang="ts">
import { Globe, Headset, Users } from "lucide-vue-next";
import Reveal from "~/components/ui/Reveal.vue";
import SectionHead from "~/components/ui/SectionHead.vue";
const TIMELINE = [
{ year: "۱۳۸۶", text: "تأسیس آریا ایمن در تهران با تمرکز بر تجهیزات حفاظت فردی" },
{ year: "۱۳۹۲", text: "توسعه شبکه توزیع به تمام ۳۱ استان کشور" },
{ year: "۱۳۹۸", text: "راه‌اندازی آزمایشگاه کنترل کیفیت و تست تجهیزات" },
{ year: "۱۴۰۲", text: "افتتاح مرکز تخصصی آموزش و بازرسی HSE" },
];
const FEATURES = [
{ icon: Globe, label: "نماینده رسمی ۴۰+ برند جهانی" },
{ icon: Users, label: "تیم مهندسی و بازرسی HSE" },
{ icon: Headset, label: "پشتیبانی ۲۴/۷ قراردادها" },
];
</script>
<template>
<section
id="about"
aria-labelledby="about-title"
class="relative overflow-hidden border-t border-line py-24 md:py-32"
>
<div class="absolute inset-0 bg-grid-fine opacity-50" />
<div class="relative mx-auto max-w-7xl px-5 md:px-8">
<div class="grid items-center gap-14 lg:grid-cols-2">
<!-- ---- image ---- -->
<Reveal class="relative order-2 lg:order-1" :delay="0.1">
<div class="relative overflow-hidden rounded-2xl border border-line">
<img
src="/images/about.jpg"
alt="انبار تجهیزات ایمنی آریا ایمن"
width="800"
height="600"
loading="lazy"
decoding="async"
class="aspect-[4/3] w-full object-cover"
/>
<div class="absolute inset-0 bg-gradient-to-t from-bg/80 via-transparent to-transparent" />
<div
class="absolute bottom-5 start-5 flex items-center gap-4 rounded-xl border border-line bg-bg/80 px-5 py-4 backdrop-blur-md"
>
<span class="font-mono text-3xl font-bold text-accent">۱۸+</span>
<span class="text-xs leading-5 text-muted">
سال حفاظت از
<br />
جان نیروی کار
</span>
</div>
</div>
<div class="hazard absolute -top-3 -end-3 h-6 w-28 rounded-full opacity-90" />
</Reveal>
<!-- ---- text ---- -->
<div class="order-1 lg:order-2">
<SectionHead heading-id="about-title" tag="۰۲ — درباره ما">
<template #title>
ایمنی برای ما شعار نیست؛
<br />
<span class="text-accent">قرارداد است.</span>
</template>
</SectionHead>
<Reveal :delay="0.2">
<p class="mt-6 leading-9 text-muted">
از سال ۱۳۸۶، زمانی که اولین کلاه ایمنی را به یک کارگاه کوچک در
جنوب تهران فروختیم، باور ما همین یک جمله بود: پشت هر تجهیز
ایمنی، یک خانواده هست که منتظر بازگشت عزیزش است. امروز با بیش
از ۸۵۰ سازمان، از پالایشگاه‌ها تا معادن و سایت‌های ساختمانی، در
کنار کسانی هستیم که ایمنی را یک خط قرمز می‌دانند.
</p>
</Reveal>
<div class="mt-10 space-y-1">
<Reveal v-for="(t, i) in TIMELINE" :key="t.year" :delay="0.25 + i * 0.07">
<div
class="group border-s-2 border-accent/30 ps-6 transition-colors duration-300 hover:border-accent"
>
<span class="font-mono text-sm font-bold text-accent">{{ t.year }}</span>
<p class="mt-1 pb-5 text-sm leading-7 text-ink/75">{{ t.text }}</p>
</div>
</Reveal>
</div>
<Reveal :delay="0.5">
<div class="mt-4 flex flex-wrap gap-3">
<span
v-for="f in FEATURES"
:key="f.label"
class="inline-flex items-center gap-2 rounded-full border border-line bg-panel/60 px-4 py-2 text-xs text-ink/80 transition-colors hover:border-accent/50 hover:text-ink"
>
<component :is="f.icon" class="size-4 text-accent" />
{{ f.label }}
</span>
</div>
</Reveal>
</div>
</div>
</div>
</section>
</template>
+190
View File
@@ -0,0 +1,190 @@
<script setup lang="ts">
import { Button as AButton, Form as AForm, FormItem as AFormItem, Input as AInput, Textarea as ATextarea } from "ant-design-vue";
import type { Rule } from "ant-design-vue/es/form";
import {
CheckCircle2,
Clock,
Loader2,
Mail,
MapPin,
Phone,
Send,
Siren,
} from "lucide-vue-next";
import Reveal from "~/components/ui/Reveal.vue";
import SectionHead from "~/components/ui/SectionHead.vue";
const INFO = [
{
icon: MapPin,
label: "نشانی",
value: "تهران، خیابان کارگر شمالی، خیابان فر، کوچه دوم، پلاک ۱۲",
},
{ icon: Phone, label: "تلفن تماس", value: "۰۲۱ - ۹۱ ۰۰ ۸۸ ۰۰", ltr: true },
{ icon: Mail, label: "ایمیل", value: "info@ariaiman.ir", ltr: true },
{ icon: Clock, label: "ساعات کاری", value: "شنبه تا پنجشنبه — ۸:۰۰ تا ۱۷:۰۰" },
];
const required = (message: string): Rule[] => [
{ required: true, whitespace: true, message, trigger: "blur" },
];
const rules: Record<string, Rule[]> = {
name: required("لطفاً نام و نام خانوادگی را وارد کنید"),
org: required("لطفاً نام سازمان را وارد کنید"),
phone: required("لطفاً شماره تماس را وارد کنید"),
};
const contact = useContactStore();
</script>
<template>
<section
id="contact"
aria-labelledby="contact-title"
class="relative overflow-hidden border-t border-line py-24 md:py-32"
>
<div class="absolute inset-0 bg-grid-fine opacity-40" />
<div class="relative mx-auto max-w-7xl px-5 md:px-8">
<div class="grid gap-14 lg:grid-cols-2">
<!-- ---- info ---- -->
<div>
<SectionHead
heading-id="contact-title"
tag="۰۷ — تماس"
desc="فرم را پر کنید تا کارشناسان ما برای هماهنگی جلسه بازدید رایگان سایت شما تماس بگیرند؛ یا مستقیم با ما در ارتباط باشید."
>
<template #title>
بیایید ایمنی را
<br />
با هم جدی بگیریم
</template>
</SectionHead>
<div class="mt-10 space-y-1">
<Reveal v-for="(item, i) in INFO" :key="item.label" :delay="0.15 + i * 0.06">
<div class="group flex items-center gap-4 border-b border-line/70 py-4">
<span
class="flex size-11 shrink-0 items-center justify-center rounded-lg border border-line text-accent transition-colors duration-300 group-hover:border-accent/50"
>
<component :is="item.icon" class="size-5" />
</span>
<div class="min-w-0">
<div class="text-xs text-muted">{{ item.label }}</div>
<div
:class="`mt-1 text-sm font-medium ${item.ltr ? 'font-mono tracking-wider' : ''}`"
:dir="item.ltr ? 'ltr' : 'rtl'"
>
{{ item.value }}
</div>
</div>
</div>
</Reveal>
</div>
<Reveal :delay="0.45">
<div class="mt-8 flex items-start gap-4 rounded-xl border border-hazard/30 bg-hazard/5 p-5">
<Siren class="size-6 shrink-0 text-hazard" />
<div>
<div class="font-bold text-hazard">پاسخ‌گویی اضطراری ۲۴/۷</div>
<p class="mt-1.5 text-sm leading-7 text-muted">
برای مشتریان دارای قرارداد سازمانی، خط ویژه حوادث و تأمین
فوری تجهیزات در تمام ساعات شبانه‌روز فعال است.
</p>
</div>
</div>
</Reveal>
</div>
<!-- ---- form ---- -->
<Reveal :delay="0.15">
<div class="rounded-2xl border border-line bg-panel/60 p-6 backdrop-blur md:p-9">
<div class="font-mono text-xs tracking-[0.25em] text-accent">// فرم درخواست مشاوره</div>
<h3 class="mt-3 text-2xl font-black leading-snug">
در کمتر از ۲۴ ساعت
<br />
با شما تماس می‌گیریم
</h3>
<div
v-if="contact.status === 'success'"
class="mt-10 flex flex-col items-center rounded-xl border border-emerald-500/30 bg-emerald-500/5 p-10 text-center"
>
<CheckCircle2 class="size-14 text-emerald-400" />
<div class="mt-5 text-xl font-black">درخواست شما ثبت شد</div>
<p class="mt-2.5 max-w-sm text-sm leading-7 text-muted">
کارشناسان ما تا ۲۴ ساعت آینده برای هماهنگی جلسه بازدید با
شما تماس می‌گیرند.
</p>
<button
type="button"
class="mt-7 rounded-full border border-line px-7 py-3 text-sm transition-colors hover:border-accent hover:text-accent"
@click="contact.reset"
>
ثبت درخواست جدید
</button>
</div>
<AForm
v-else
class="contact-form"
layout="vertical"
:model="contact.form"
:rules="rules"
hide-required-mark
@finish="contact.submit"
>
<div class="mt-8 grid gap-5 sm:grid-cols-2">
<AFormItem name="name" label="نام و نام خانوادگی *" html-for="f-name">
<AInput id="f-name" v-model:value="contact.form.name" placeholder="مثلاً: علی رضایی" />
</AFormItem>
<AFormItem name="org" label="نام سازمان *" html-for="f-org">
<AInput
id="f-org"
v-model:value="contact.form.org"
placeholder="مثلاً: شرکت پتروشیمی خلیج"
/>
</AFormItem>
<AFormItem class="sm:col-span-2" name="phone" label="شماره تماس *" html-for="f-phone">
<AInput
id="f-phone"
v-model:value="contact.form.phone"
type="tel"
dir="ltr"
placeholder="0912 000 0000"
/>
</AFormItem>
<AFormItem class="sm:col-span-2" name="message" label="شرح درخواست" html-for="f-msg">
<ATextarea
id="f-msg"
v-model:value="contact.form.message"
:rows="4"
placeholder="تجهیزات موردنیاز، تعداد پرسنل یا هر نکته‌ای که لازم است بدانیم..."
/>
</AFormItem>
<div class="sm:col-span-2">
<AButton
type="primary"
html-type="submit"
class="contact-submit"
:disabled="contact.status === 'loading'"
>
<template v-if="contact.status === 'loading'">
<Loader2 class="size-5 animate-spin" />
در حال ارسال...
</template>
<template v-else>
<Send class="size-5" />
ارسال درخواست مشاوره
</template>
</AButton>
</div>
</div>
</AForm>
</div>
</Reveal>
</div>
</div>
</section>
</template>
+29
View File
@@ -0,0 +1,29 @@
<script setup lang="ts">
import { useInView } from "motion-v";
const props = withDefaults(
defineProps<{ end: number; suffix?: string; duration?: number }>(),
{ suffix: "", duration: 1800 },
);
const el = ref<HTMLSpanElement | null>(null);
const inView = useInView(el, { once: true, margin: "-60px" });
const val = ref(0);
let raf = 0;
watch(inView, (v) => {
if (!v) return;
const t0 = performance.now();
const tick = (t: number) => {
const p = Math.min(1, (t - t0) / props.duration);
val.value = Math.round(props.end * (1 - Math.pow(1 - p, 3)));
if (p < 1) raf = requestAnimationFrame(tick);
};
raf = requestAnimationFrame(tick);
});
onBeforeUnmount(() => cancelAnimationFrame(raf));
</script>
<template>
<span ref="el" class="tabular-nums">{{ val.toLocaleString("fa-IR") }}<span v-if="suffix" class="text-accent">{{ suffix }}</span></span>
</template>
+188
View File
@@ -0,0 +1,188 @@
<script setup lang="ts">
import {
ArrowLeft,
Award,
BadgeCheck,
ChevronDown,
HardHat,
ShieldCheck,
} from "lucide-vue-next";
const CHIPS = [
{ icon: ShieldCheck, label: "ISO 9001:2015" },
{ icon: BadgeCheck, label: "CE CERTIFIED" },
{ icon: Award, label: "EN STANDARDS" },
];
const MINI_STATS = [
{ value: "۱۸+", label: "سال تجربه در صنعت ایمنی" },
{ value: "۸۵۰+", label: "سازمان تحت پوشش" },
{ value: "۹۸٪", label: "رضایت مشتریان" },
];
</script>
<template>
<section id="home" class="relative flex min-h-screen flex-col overflow-hidden">
<!-- ---- background ---- -->
<div class="absolute inset-0">
<img
src="/images/hero.jpg"
alt="کارگر صنعتی با تجهیزات ایمنی کامل"
width="1792"
height="1008"
fetchpriority="high"
decoding="async"
class="absolute inset-0 size-full object-cover"
/>
<div class="absolute inset-0 bg-gradient-to-l from-bg via-bg/80 to-bg/25" />
<div class="absolute inset-0 bg-gradient-to-t from-bg via-transparent to-bg/60" />
<div class="absolute inset-0 bg-grid opacity-50" />
</div>
<!-- ---- side tape ---- -->
<div class="tape-v absolute bottom-0 end-0 z-10 h-44 w-1.5 opacity-70" />
<!-- ---- content ---- -->
<div
class="relative z-10 mx-auto flex w-full max-w-7xl flex-1 items-center px-5 pb-24 pt-36 md:px-8"
>
<div class="grid w-full items-center gap-12 lg:grid-cols-12">
<div class="lg:col-span-7">
<div
class="animate-fade-up-1 inline-flex items-center gap-2.5 rounded-full border border-accent/30 bg-accent/10 px-4 py-2 backdrop-blur"
>
<span class="size-1.5 animate-pulse rounded-full bg-accent" />
<span class="text-xs font-medium text-accent">
تأمین‌کننده رسمی تجهیزات حفاظت فردی (PPE)
</span>
</div>
<h1
class="animate-fade-up-2 mt-7 text-5xl font-black leading-[1.15] md:text-7xl md:leading-[1.1]"
>
ایمنی یعنی
<br />
<span
class="mt-3 inline-block -rotate-1 rounded-lg bg-hazard px-4 py-1.5 text-bg shadow-[0_10px_50px_rgba(255,184,0,0.25)]"
>
بازگشت به خانه
</span>
</h1>
<p
class="animate-fade-up-3 mt-7 max-w-xl text-base leading-9 text-ink/70 md:text-lg md:leading-10"
>
آریا ایمن با بیش از ۱۸ سال تجربه، تأمین‌کننده تخصصی تجهیزات ایمنی و
حفاظت فردی برای سازمان‌هایی است که جان انسان‌ها را جدی می‌گیرند؛ از
کلاه ایمنی تا هارنس نجات، از مشاوره تا بازرسی و آموزش.
</p>
<div class="animate-fade-up-4 mt-9 flex flex-wrap items-center gap-4">
<a
href="#contact"
class="group inline-flex items-center gap-2.5 rounded-full bg-accent px-7 py-4 font-bold text-bg transition-all duration-300 hover:bg-accent-soft hover:shadow-[0_8px_45px_rgba(255,90,0,0.5)]"
>
درخواست مشاوره رایگان
<ArrowLeft
class="size-5 transition-transform duration-300 group-hover:-translate-x-1.5"
/>
</a>
<a
href="#products"
class="inline-flex items-center gap-2.5 rounded-full border border-ink/25 px-7 py-4 font-bold text-ink backdrop-blur transition-colors duration-300 hover:border-accent hover:text-accent"
>
مشاهده محصولات
</a>
</div>
<div class="animate-fade-up-5 mt-10 flex flex-wrap gap-3">
<span
v-for="c in CHIPS"
:key="c.label"
class="inline-flex items-center gap-2 rounded-full border border-ink/15 bg-bg/50 px-4 py-2 font-mono text-[11px] tracking-wider text-ink/70 backdrop-blur transition-colors hover:border-accent/50 hover:text-ink"
>
<component :is="c.icon" class="size-4 text-accent" />
{{ c.label }}
</span>
</div>
</div>
<!-- ---- rotating badge + status card ---- -->
<div class="hidden flex-col items-center gap-10 lg:col-span-5 lg:flex">
<div class="animate-fade-up-6 relative">
<div class="relative size-44">
<div class="animate-spin-slow absolute inset-0">
<svg viewBox="0 0 120 120" class="size-full">
<defs>
<path
id="badge-circle"
d="M60,60 m-47,0 a47,47 0 1,1 94,0 a47,47 0 1,1 -94,0"
/>
</defs>
<text font-size="9" letter-spacing="2.6" class="fill-ink/60 font-mono">
<textPath href="#badge-circle">
ARIA IMEN SAFETY • ISO 9001 • CE CERTIFIED • EST 2007 •
</textPath>
</text>
</svg>
</div>
<div class="absolute inset-0 flex items-center justify-center">
<span
class="flex size-20 items-center justify-center rounded-full border border-accent/30 bg-accent/10"
>
<HardHat class="size-9 text-accent" />
</span>
</div>
</div>
</div>
<div
class="animate-fade-up-7 w-full max-w-sm border border-line bg-bg/70 p-5 backdrop-blur-md"
>
<div class="flex items-center justify-between font-mono text-[11px]">
<span class="text-muted">SITE STATUS</span>
<span class="flex items-center gap-2 text-emerald-400">
<span class="size-1.5 animate-pulse rounded-full bg-emerald-400" />
SECURE
</span>
</div>
<div class="mt-3.5 flex items-center justify-between font-mono text-[11px]">
<span class="text-muted">PPE COMPLIANCE</span>
<span class="font-semibold text-accent">۱۰۰٪</span>
</div>
<div class="mt-3.5 flex items-center justify-between font-mono text-[11px]">
<span class="text-muted">آخرین بازرسی</span>
<span>۱۴۰۴/۰۸/۱۲</span>
</div>
<div class="my-4 h-px bg-line" />
<div class="font-mono text-[11px] leading-6 text-muted">
سیستم پایش حرفه‌ای آریا ایمن<span class="animate-blink text-accent">▍</span>
</div>
</div>
</div>
</div>
</div>
<!-- ---- scroll hint ---- -->
<div class="animate-fade-up-9 absolute bottom-28 left-1/2 z-10 hidden md:block">
<div class="-translate-x-1/2 flex flex-col items-center gap-2">
<span class="font-mono text-[10px] tracking-[0.35em] text-muted">SCROLL</span>
<ChevronDown class="size-4 animate-bounce text-accent" />
</div>
</div>
<!-- ---- bottom bar ---- -->
<div class="animate-fade-up-8 relative z-10 border-t border-ink/10 backdrop-blur-sm">
<div class="mx-auto grid max-w-7xl grid-cols-1 gap-px bg-ink/10 sm:grid-cols-3">
<div
v-for="s in MINI_STATS"
:key="s.label"
class="flex items-baseline gap-4 bg-bg/60 px-6 py-5 backdrop-blur-md md:px-8"
>
<span class="text-2xl font-black text-ink md:text-3xl">{{ s.value }}</span>
<span class="text-xs leading-5 text-muted md:text-sm">{{ s.label }}</span>
</div>
</div>
</div>
</section>
</template>
+137
View File
@@ -0,0 +1,137 @@
<script setup lang="ts">
import { AnimatePresence, motion, useMotionValue, useSpring } from "motion-v";
import { ArrowLeft } from "lucide-vue-next";
import SectionHead from "~/components/ui/SectionHead.vue";
const INDUSTRIES = [
{ name: "نفت و گاز", en: "OIL & GAS", count: "۱۴۰+", img: "/images/industries/oil-gas.jpg" },
{
name: "ساختمان و عمران",
en: "CONSTRUCTION",
count: "۲۱۰+",
img: "/images/industries/construction.jpg",
},
{
name: "پتروشیمی",
en: "PETROCHEMICAL",
count: "۹۰+",
img: "/images/industries/petrochemical.jpg",
},
{ name: "صنایع معدنی", en: "MINING", count: "۷۵+", img: "/images/industries/mining.jpg" },
{
name: "برق و انرژی",
en: "POWER & ENERGY",
count: "۶۰+",
img: "/images/industries/power-energy.jpg",
},
{
name: "حمل‌ونقل و لجستیک",
en: "TRANSPORT",
count: "۴۵+",
img: "/images/industries/transport.jpg",
},
];
const hovered = ref<number | null>(null);
const x = useMotionValue(0);
const y = useMotionValue(0);
const sx = useSpring(x, { stiffness: 220, damping: 26, mass: 0.6 });
const sy = useSpring(y, { stiffness: 220, damping: 26, mass: 0.6 });
const onMove = (e: MouseEvent) => {
x.set(e.clientX);
y.set(e.clientY);
};
</script>
<template>
<section
id="industries"
aria-labelledby="industries-title"
class="relative border-t border-line py-24 md:py-32"
@mousemove="onMove"
>
<div class="mx-auto max-w-7xl px-5 md:px-8">
<SectionHead
heading-id="industries-title"
tag="۰۵ — صنایع"
desc="جایی که دمای محیط از ۵۰ درجه گذر می‌کند یا ارتفاع کار به ده‌ها متر می‌رسد، تجهیزات ما باید بی‌نقص عمل کنند — و عمل می‌کنند."
>
<template #title>
حضور در سخت‌ترین
<br />
میدان‌های کاری
</template>
</SectionHead>
<div class="mt-16" @mouseleave="hovered = null">
<motion.div
v-for="(ind, i) in INDUSTRIES"
:key="ind.name"
v-bind="fade(i * 0.05)"
data-hover
:class="`group relative flex cursor-pointer items-center justify-between gap-4 border-t border-line px-2 py-7 transition-colors duration-300 hover:bg-panel/40 md:px-4 md:py-9 ${
i === INDUSTRIES.length - 1 ? 'border-b' : ''
}`"
@mouseenter="hovered = i"
>
<div class="flex min-w-0 items-baseline gap-5 md:gap-8">
<span class="shrink-0 font-mono text-xs text-muted">
{{ faNum(String(i + 1).padStart(2, "0")) }}
</span>
<h3
class="truncate text-2xl font-black transition-all duration-300 group-hover:-translate-x-2 group-hover:text-accent md:text-5xl"
>
{{ ind.name }}
</h3>
<span
class="hidden shrink-0 font-mono text-[10px] tracking-[0.3em] text-muted/60 md:inline"
>
{{ ind.en }}
</span>
</div>
<div class="flex shrink-0 items-center gap-4 md:gap-8">
<span class="text-xs text-muted md:text-sm">{{ ind.count }} پروژه</span>
<span
class="flex size-10 items-center justify-center rounded-full border border-line text-muted transition-all duration-300 group-hover:-translate-x-1.5 group-hover:border-accent group-hover:text-accent md:size-12"
>
<ArrowLeft class="size-4 md:size-5" />
</span>
</div>
</motion.div>
</div>
</div>
<!-- ---- floating preview (desktop) ---- -->
<motion.div
:style="{ x: sx, y: sy }"
class="pointer-events-none fixed left-0 top-0 z-40 hidden lg:block"
>
<AnimatePresence>
<motion.div
v-if="hovered !== null"
:key="hovered"
:initial="{ opacity: 0, scale: 0.82, rotate: -3 }"
:animate="{ opacity: 1, scale: 1, rotate: 0 }"
:exit="{ opacity: 0, scale: 0.9, rotate: 2 }"
:transition="{ duration: 0.35, ease: EASE }"
class="relative -ml-40 -mt-28 h-56 w-80 overflow-hidden rounded-xl border border-accent/40 shadow-[0_25px_80px_rgba(0,0,0,0.6)]"
>
<img
:src="INDUSTRIES[hovered]!.img"
:alt="INDUSTRIES[hovered]!.name"
decoding="async"
class="absolute inset-0 size-full object-cover grayscale-[35%] transition-all duration-500"
/>
<div class="absolute inset-0 bg-accent/15 mix-blend-multiply" />
<div class="absolute inset-0 bg-gradient-to-t from-bg/70 to-transparent" />
<div
class="absolute bottom-3 start-3 rounded-md border border-line bg-bg/80 px-3 py-1.5 font-mono text-[10px] tracking-widest text-ink backdrop-blur"
>
{{ INDUSTRIES[hovered]!.en }}
</div>
</motion.div>
</AnimatePresence>
</motion.div>
</section>
</template>
+209
View File
@@ -0,0 +1,209 @@
<script setup lang="ts">
import { AnimatePresence, motion } from "motion-v";
import { ArrowLeft, BadgeCheck, PackageCheck, ShieldCheck } from "lucide-vue-next";
import Reveal from "~/components/ui/Reveal.vue";
import SectionHead from "~/components/ui/SectionHead.vue";
type Product = {
code: string;
name: string;
en: string;
desc: string;
img: string;
specs: string[];
};
const PRODUCTS: Product[] = [
{
code: "AP-101",
name: "کلاه ایمنی",
en: "SAFETY HELMET",
img: "/images/products/helmet.jpg",
desc: "کلاه‌های ایمنی با بدنه HDPE مقاوم به ضربه و ترتیه‌بند ضدتعریق؛ با قابلیت نصب هدفون و شیشه محافظ صورت برای محیط‌های ساختمانی و صنعتی.",
specs: ["EN 397", "مقاوم به ضربه ۵۰ ژول", "بدنه HDPE"],
},
{
code: "AP-205",
name: "دستکش ضدبرش",
en: "CUT-RESISTANT GLOVE",
img: "/images/products/gloves.jpg",
desc: "دستکش‌های سطح ۵ ضدبرش با پوشش نیتریل؛ طراحی‌شده برای کار با ورق فلز، شیشه و ابزار برنده با چسبندگی عالی در محیط خشک و چرب.",
specs: ["EN 388 — 4X43C", "سطح ۵ ضدبرش", "پوشش نیتریل"],
},
{
code: "AP-310",
name: "کفش ایمنی",
en: "SAFETY BOOTS",
img: "/images/products/shoes.jpg",
desc: "کفش‌های ایمنی با پنجه فولادی و کف مقاوم در برابر سوراخ‌شدگی و روغن؛ راحتی تمام‌روز برای شیفت‌های طولانی در سایت.",
specs: ["EN ISO 20345", "پنجه فولادی", "کف ضدروغن"],
},
{
code: "AP-412",
name: "عینک ایمنی",
en: "SAFETY GOGGLES",
img: "/images/products/goggles.jpg",
desc: "عینک‌های محافظ ضدخش با پوشش UV400 و تهویه ضدمه؛ سازگار با عینک طبی برای کارهای جوشکاری، سنگ‌زنی و محیط‌های شیمیایی.",
specs: ["EN 166", "عدسی ضدخش", "پوشش UV400"],
},
{
code: "AP-508",
name: "هارنس ضدسقوط",
en: "FULL BODY HARNESS",
img: "/images/products/harness.jpg",
desc: "هارنس تمام‌بدنی با دو شیر پیچی و نوارهای دوخته‌شده با نخ نارنجی سیگنال؛ تست‌شده برای کار در ارتفاع با ظرفیت ۱۰۰ کیلوگرم.",
specs: ["EN 361", "ظرفیت ۱۰۰ کیلوگرم", "شیر دوگانه"],
},
{
code: "AP-615",
name: "لباس کار بازتابنده",
en: "HI-VIS WORKWEAR",
img: "/images/products/vest.jpg",
desc: "جلیقه‌ها و لباس‌های کار بازتابنده کلاس ۲ با نوارهای ۳M؛ دیده‌شدن کامل پرسنل در شب، مه و محیط‌های پرتردد خودرویی.",
specs: ["EN ISO 20471", "کلاس ۲", "نوار ۳M"],
},
];
const TRUST = [
{
icon: BadgeCheck,
title: "گارانتی اصالت کالا",
desc: "تمام محصولات با فاکتور رسمی و کد رهگیری اصالت",
},
{
icon: PackageCheck,
title: "تست نمونه قبل از خرید",
desc: "نمونه‌های نمایشی برای ارزیابی تیم HSE شما",
},
{
icon: ShieldCheck,
title: "امکان قرارداد سالانه",
desc: "قیمت ثابت و تأمین مستمر برای سازمان‌های بزرگ",
},
];
const active = ref(0);
</script>
<template>
<section id="products" aria-labelledby="products-title" class="relative py-24 md:py-32">
<div class="mx-auto max-w-7xl px-5 md:px-8">
<div class="flex flex-wrap items-end justify-between gap-6">
<SectionHead
heading-id="products-title"
tag="۰۱ — محصولات"
desc="هر محصول پیش از عرضه، زیر ذره‌بین کنترل کیفیت و تطبیق با استانداردهای جهانی قرار می‌گیرد. روی هر پنل کلیک کنید."
>
<template #title>
خط اول دفاع،
<br />
تجهیزات ماست
</template>
</SectionHead>
<Reveal :delay="0.2" class="hidden md:block">
<a
href="#contact"
class="group inline-flex items-center gap-2 rounded-full border border-line px-6 py-3 text-sm text-muted transition-colors hover:border-accent hover:text-accent"
>
دریافت کاتالوگ کامل
<ArrowLeft class="size-4 transition-transform group-hover:-translate-x-1" />
</a>
</Reveal>
</div>
</div>
<div class="mx-auto mt-14 max-w-7xl px-5 md:px-8">
<div class="flex flex-col gap-2 md:h-[74vh] md:min-h-[580px] md:flex-row">
<div
v-for="(p, i) in PRODUCTS"
:key="p.code"
data-hover
:class="`group relative cursor-pointer overflow-hidden rounded-xl border transition-all duration-700 ease-[cubic-bezier(0.22,1,0.36,1)] ${
active === i
? 'h-[500px] flex-[3.4] border-accent/60 md:h-auto'
: 'h-24 flex-[1] border-line hover:border-ink/30 md:h-auto'
}`"
@mouseenter="active = i"
@click="active = i"
>
<img
:src="p.img"
:alt="p.name"
loading="lazy"
decoding="async"
:class="`absolute inset-0 size-full object-cover transition-all duration-700 ${
active === i ? 'scale-100 grayscale-0' : 'scale-110 grayscale group-hover:grayscale-0'
}`"
/>
<div
:class="`absolute inset-0 transition-opacity duration-700 ${
active === i
? 'bg-gradient-to-t from-bg via-bg/35 to-bg/5'
: 'bg-gradient-to-t from-bg/95 via-bg/30 to-transparent'
}`"
/>
<div class="absolute inset-x-0 top-0 flex items-center justify-between p-5">
<span class="font-mono text-[10px] tracking-widest text-ink/60">{{ p.code }}</span>
<span
:class="`font-mono text-xs transition-colors duration-500 ${
active === i ? 'text-accent' : 'text-ink/40'
}`"
>
{{ faNum(String(i + 1).padStart(2, "0")) }}
</span>
</div>
<div class="absolute inset-x-0 bottom-0 p-5 md:p-6">
<h3
:class="`font-black transition-all duration-500 ${
active === i ? 'text-2xl' : 'text-base md:text-sm'
}`"
>
{{ p.name }}
</h3>
<p class="mt-1 font-mono text-[10px] tracking-[0.2em] text-muted">{{ p.en }}</p>
<AnimatePresence>
<motion.div
v-if="active === i"
:initial="{ opacity: 0, y: 18 }"
:animate="{ opacity: 1, y: 0 }"
:exit="{ opacity: 0, y: 10 }"
:transition="{ duration: 0.45, delay: 0.12, ease: EASE }"
>
<p class="mt-4 max-w-md text-sm leading-8 text-ink/75">{{ p.desc }}</p>
<div class="mt-4 flex flex-wrap gap-2">
<span
v-for="s in p.specs"
:key="s"
class="rounded-full border border-accent/40 bg-accent/10 px-3 py-1 font-mono text-[10px] text-accent"
>
{{ s }}
</span>
</div>
</motion.div>
</AnimatePresence>
</div>
</div>
</div>
</div>
<div
class="mx-auto mt-10 grid max-w-7xl gap-px overflow-hidden rounded-xl border border-line bg-line md:grid-cols-3"
>
<Reveal v-for="(t, i) in TRUST" :key="t.title" :delay="i * 0.08">
<div class="flex h-full items-center gap-4 bg-panel/60 p-6">
<span
class="flex size-12 shrink-0 items-center justify-center rounded-xl bg-accent/10 text-accent"
>
<component :is="t.icon" class="size-6" />
</span>
<div>
<div class="font-bold">{{ t.title }}</div>
<div class="mt-1 text-xs leading-6 text-muted">{{ t.desc }}</div>
</div>
</div>
</Reveal>
</div>
</section>
</template>
+142
View File
@@ -0,0 +1,142 @@
<script setup lang="ts">
import { AnimatePresence, motion } from "motion-v";
import { ClipboardCheck, Gauge, GraduationCap, Plus, Truck } from "lucide-vue-next";
import Reveal from "~/components/ui/Reveal.vue";
import SectionHead from "~/components/ui/SectionHead.vue";
import { serviceSchema } from "#shared/seo/schema";
const SERVICES = [
{
icon: ClipboardCheck,
title: "بازرسی و ارزیابی ریسک",
desc: "تیم مهندسی ما با بازدید از سایت شما، خطرات را شناسایی، اولویت‌بندی و برای هر کدام راهکار اجرایی مشخص با برآورد هزینه ارائه می‌دهد.",
items: [
"ارزیابی HAZOP و HAZID",
"شناسایی و اولویت‌بندی خطرات",
"گزارش مدیریتی با راهکار اجرایی",
],
},
{
icon: GraduationCap,
title: "آموزش تخصصی ایمنی",
desc: "از دوره‌های عمومی تا تربیت مربی؛ آموزش‌های عملی و میدانی که ایمنی را از دفتر کلان‌ها به رفتار روزمره پرسنل تبدیل می‌کند.",
items: [
"دوره‌های عمومی و تخصصی HSE",
"تربیت مربی ایمنی سایت",
"مانور و شبیه‌سازی شرایط اضطراری",
],
},
{
icon: Gauge,
title: "تست و کالیبراسیون تجهیزات",
desc: "تجهیزات ایمنی فقط وقتی قابل اعتمادند که تست شده باشند. تست دوره‌ای، سرویس و صدور گواهی معتبر را به تیم تخصصی ما بسپارید.",
items: [
"تست هارنس و لاین نجات",
"کالیبراسیون آشکارسازهای گاز",
"سرویس دوره‌ای و صدور گواهی",
],
},
{
icon: Truck,
title: "تأمین و لجستیک سازمانی",
desc: "با قرارداد سالانه، قیمت‌گذاری شفاف و انبار محلی در سایت شما، نگرانی کمبود تجهیزات را از فهرست دغدغه‌هایتان حذف کنید.",
items: [
"قرارداد سالانه با قیمت ثابت",
"انبار محلی در سایت شما",
"تحویل سراسری در ۴۸ ساعت",
],
},
];
useJsonLd(
SERVICES.map((s) => serviceSchema({ name: s.title, description: s.desc })),
"services",
);
const open = ref(0);
</script>
<template>
<section id="services" aria-labelledby="services-title" class="border-t border-line py-24 md:py-32">
<div class="mx-auto max-w-7xl px-5 md:px-8">
<SectionHead
heading-id="services-title"
tag="۰۳ — خدمات"
desc="ما در آریا ایمن، تجهیزات را تحویل می‌دهیم و کنار شما می‌مانیم؛ از نخستین ارزیابی ریسک تا آخرین تست دوره‌ای."
>
<template #title>
فراتر از تأمین؛
<br />
همراهی کامل ایمنی
</template>
</SectionHead>
<div class="mt-16">
<Reveal v-for="(s, i) in SERVICES" :key="s.title" :delay="i * 0.05">
<div :class="`border-t border-line ${i === SERVICES.length - 1 ? 'border-b' : ''}`">
<button
class="group flex w-full items-center gap-5 py-7 text-start md:gap-10 md:py-9"
@click="open = open === i ? -1 : i"
>
<span
:class="`font-mono text-sm transition-colors duration-300 md:text-base ${
open === i ? 'text-accent' : 'text-muted'
}`"
>
{{ faNum(String(i + 1).padStart(2, "0")) }}
</span>
<span
:class="`flex-1 text-2xl font-black transition-colors duration-300 md:text-4xl ${
open === i ? 'text-accent' : 'text-ink group-hover:text-accent'
}`"
>
{{ s.title }}
</span>
<span
:class="`hidden size-12 shrink-0 items-center justify-center rounded-full border transition-all duration-500 md:flex ${
open === i
? 'rotate-45 border-accent bg-accent text-bg'
: 'border-line text-muted group-hover:border-accent/50 group-hover:text-accent'
}`"
>
<Plus class="size-5" />
</span>
</button>
<AnimatePresence :initial="false">
<motion.div
v-if="open === i"
:initial="{ height: 0, opacity: 0 }"
:animate="{ height: 'auto', opacity: 1 }"
:exit="{ height: 0, opacity: 0 }"
:transition="{ duration: 0.55, ease: EASE }"
class="overflow-hidden"
>
<div class="grid gap-8 pb-10 md:grid-cols-2 md:ps-16">
<div class="flex items-start gap-4">
<span
class="flex size-12 shrink-0 items-center justify-center rounded-xl bg-accent/10 text-accent"
>
<component :is="s.icon" class="size-6" />
</span>
<p class="text-sm leading-8 text-muted">{{ s.desc }}</p>
</div>
<ul class="space-y-3.5">
<li
v-for="it in s.items"
:key="it"
class="flex items-center gap-3 text-sm text-ink/80"
>
<span class="size-1.5 shrink-0 rounded-full bg-accent" />
{{ it }}
</li>
</ul>
</div>
</motion.div>
</AnimatePresence>
</div>
</Reveal>
</div>
</div>
</section>
</template>
+97
View File
@@ -0,0 +1,97 @@
<script setup lang="ts">
import { FileSearch, ShieldCheck } from "lucide-vue-next";
import Reveal from "~/components/ui/Reveal.vue";
import SectionHead from "~/components/ui/SectionHead.vue";
const STANDARDS = [
{ code: "EN 397", label: "کلاه ایمنی صنعتی" },
{ code: "EN 388", label: "دستکش‌های مکانیکی" },
{ code: "EN 166", label: "محافظ چشم و صورت" },
{ code: "EN 361", label: "هارنس ضدسقوط" },
{ code: "ISO 20345", label: "کفش ایمنی" },
{ code: "ISO 9001", label: "سیستم مدیریت کیفیت" },
];
</script>
<template>
<section
id="standards"
aria-labelledby="standards-title"
class="relative overflow-hidden border-t border-line py-24 md:py-32"
>
<!-- watermark -->
<div
aria-hidden="true"
class="text-outline pointer-events-none absolute -bottom-14 -start-8 select-none font-mono text-[24vw] font-bold leading-none opacity-60"
>
CE
</div>
<div class="relative mx-auto max-w-7xl px-5 md:px-8">
<div class="grid items-center gap-14 lg:grid-cols-12">
<div class="lg:col-span-5">
<SectionHead
heading-id="standards-title"
tag="۰۴ — استانداردها"
desc="ما فقط تجهیزاتی را عرضه می‌کنیم که تأییدیه معتبر بین‌المللی و تست آزمایشگاهی داشته باشند؛ بدون استثنا."
>
<template #title>
مهرتأیید جهانی
<br />
روی هر محصول
</template>
</SectionHead>
<Reveal :delay="0.25">
<div class="mt-8 flex items-start gap-4 rounded-xl border border-accent/25 bg-accent/5 p-5">
<FileSearch class="size-6 shrink-0 text-accent" />
<p class="text-sm leading-8 text-ink/80">
شماره سریال تمام محصولات در پایگاه داده استانداردهای اروپا
قابل استعلام است و گواهی تست هر بچ تحویل مشتری می‌شود.
</p>
</div>
</Reveal>
<Reveal :delay="0.35">
<a
href="#contact"
class="group mt-8 inline-flex items-center gap-2.5 rounded-full border border-line px-6 py-3.5 text-sm font-bold transition-colors hover:border-accent hover:text-accent"
>
<ShieldCheck class="size-4 text-accent" />
درخواست گواهی تست محصولات
</a>
</Reveal>
</div>
<div class="grid gap-4 sm:grid-cols-2 lg:col-span-7 xl:grid-cols-3">
<Reveal v-for="(s, i) in STANDARDS" :key="s.code" :delay="i * 0.06">
<div
data-hover
class="group relative h-full border border-line bg-panel/50 p-6 text-center transition-all duration-500 hover:-translate-y-1.5 hover:border-accent/50 hover:bg-panel"
>
<span class="absolute end-3 top-3 font-mono text-[9px] tracking-[0.2em] text-muted/50">
CERT
</span>
<div
class="mx-auto flex size-24 items-center justify-center rounded-full border border-dashed border-ink/20 transition-all duration-500 group-hover:rotate-12 group-hover:border-accent/60"
>
<div>
<div
class="font-mono text-base font-bold text-ink transition-colors duration-300 group-hover:text-accent"
>
{{ s.code }}
</div>
<div
class="mx-auto mt-1.5 h-px w-8 bg-line transition-colors duration-300 group-hover:bg-accent/50"
/>
<div class="mt-1.5 font-mono text-[8px] tracking-[0.25em] text-muted">
EURO NORM
</div>
</div>
</div>
<div class="mt-4 text-sm font-medium text-ink/85">{{ s.label }}</div>
</div>
</Reveal>
</div>
</div>
</div>
</section>
</template>
+38
View File
@@ -0,0 +1,38 @@
<script setup lang="ts">
import Reveal from "~/components/ui/Reveal.vue";
import CountUp from "./CountUp.vue";
const STATS = [
{ end: 18, suffix: "+", label: "سال تجربه در صنعت ایمنی", note: "SINCE 2007" },
{ end: 2400, suffix: "+", label: "کد کالای ایمنی", note: "PPE CATALOG" },
{ end: 850, suffix: "+", label: "مشتری سازمانی فعال", note: "B2B CLIENTS" },
{ end: 98, suffix: "٪", label: "رضایت مشتریان", note: "SATISFACTION" },
];
</script>
<template>
<section class="relative" aria-label="آمار و دستاوردهای آریا ایمن">
<div class="grid grid-cols-2 gap-px border-y border-line bg-line lg:grid-cols-4">
<div
v-for="(s, i) in STATS"
:key="s.label"
class="group relative bg-bg p-8 transition-colors duration-500 hover:bg-panel md:p-10"
>
<span
class="absolute end-5 top-5 font-mono text-[10px] tracking-[0.25em] text-muted/50 transition-colors duration-500 group-hover:text-accent/70"
>
{{ s.note }}
</span>
<Reveal :delay="i * 0.08">
<div class="text-4xl font-black md:text-6xl">
<CountUp :end="s.end" :suffix="s.suffix" />
</div>
<div class="mt-3 text-sm text-muted">{{ s.label }}</div>
</Reveal>
<span
class="absolute bottom-0 start-0 h-0.5 w-0 bg-accent transition-all duration-500 group-hover:w-full"
/>
</div>
</div>
</section>
</template>
+35
View File
@@ -0,0 +1,35 @@
<script setup lang="ts">
import { ShieldCheck } from "lucide-vue-next";
const ITEMS = [
"ISO 9001:2015",
"CE CERTIFIED",
"گارانتی اصالت کالا",
"تحویل سراسری کشوری",
"مشاوره و بازرسی تخصصی رایگان",
"EN STANDARDS",
"پشتیبانی ۲۴/۷ قراردادهای سازمانی",
"تست نمونه قبل از خرید",
];
</script>
<template>
<section aria-hidden="true" class="pointer-events-none relative z-20 overflow-hidden py-10">
<div class="-rotate-[1.6deg] scale-[1.04]">
<div class="hazard h-2.5" />
<div dir="ltr" class="overflow-hidden bg-hazard py-3.5 text-bg">
<div class="animate-marquee flex w-max">
<span
v-for="(item, i) in [...ITEMS, ...ITEMS]"
:key="i"
class="flex items-center gap-7 px-7 text-sm font-extrabold whitespace-nowrap"
>
{{ item }}
<ShieldCheck class="size-4 shrink-0" :stroke-width="2.5" />
</span>
</div>
</div>
<div class="hazard h-2.5" />
</div>
</section>
</template>
+94
View File
@@ -0,0 +1,94 @@
<script setup lang="ts">
import { AnimatePresence, motion } from "motion-v";
import { Quote } from "lucide-vue-next";
import SectionHead from "~/components/ui/SectionHead.vue";
const QUOTES = [
{
text: "آریا ایمن فقط تأمین‌کننده نیست؛ شریک ایمنی ماست. بازرسی‌های دوره‌ای و آموزش‌های میدانی‌شان نرخ حوادث سایت را به کمترین رقم تاریخ مجموعه رساند.",
name: "مهندس رضا کاظمی",
role: "مدیر HSE — مجتمع پتروشیمی خلیج فارس",
},
{
text: "کیفیت تجهیزات و سرعت پاسخ‌گویی در سطحی بود که قرارداد سالانه را بدون هیچ تردیدی تمدید کردیم؛ از خط تولید تا دفتر مرکزی.",
name: "سارا محمدی",
role: "سرپرست تدارکات — هلدینگ ساختمانی آرکان",
},
{
text: "۱۴۰ کیلومتر دورتر از سایت، در عرض ۲۴ ساعت تجهیزات حیاتی را تحویل گرفتیم. به این می‌گویند شریک واقعی کاری.",
name: "امیرحسین راد",
role: "مدیر پروژه — صنایع معدنی کاوه",
},
];
const idx = ref(0);
const q = computed(() => QUOTES[idx.value]!);
let t: ReturnType<typeof setInterval> | undefined;
onMounted(() => {
t = setInterval(() => (idx.value = (idx.value + 1) % QUOTES.length), 7000);
});
onBeforeUnmount(() => clearInterval(t));
</script>
<template>
<section
aria-labelledby="testimonials-title"
class="relative overflow-hidden border-t border-line py-24 md:py-32"
>
<div class="absolute inset-0 bg-grid opacity-40" />
<div
class="pointer-events-none absolute -top-10 -end-10 select-none font-mono text-[18vw] font-bold leading-none text-outline opacity-50"
>
"
</div>
<div class="relative mx-auto max-w-4xl px-5 text-center md:px-8">
<SectionHead
heading-id="testimonials-title"
center
tag="۰۶ — اعتماد"
title="آنچه مشتریان ما می‌گویند"
/>
<div class="mt-14 flex min-h-[320px] flex-col items-center md:min-h-[280px]">
<span
class="flex size-16 items-center justify-center rounded-full border border-accent/30 bg-accent/10"
>
<Quote class="size-7 text-accent" />
</span>
<AnimatePresence mode="wait">
<motion.blockquote
:key="idx"
:initial="{ opacity: 0, y: 26 }"
:animate="{ opacity: 1, y: 0 }"
:exit="{ opacity: 0, y: -26 }"
:transition="{ duration: 0.55, ease: EASE }"
class="mt-8"
>
<p class="text-xl font-bold leading-[2.1] text-ink/90 md:text-2xl md:leading-[2]">
«{{ q.text }}»
</p>
<footer class="mt-8">
<div class="text-lg font-black">{{ q.name }}</div>
<div class="mt-2 font-mono text-xs text-muted">{{ q.role }}</div>
</footer>
</motion.blockquote>
</AnimatePresence>
</div>
<div class="mt-12 flex items-center justify-center gap-3">
<button
v-for="(_, i) in QUOTES"
:key="i"
:aria-label="`نظر شماره ${faNum(i + 1)}`"
:class="`h-1.5 rounded-full transition-all duration-500 ${
i === idx ? 'w-12 bg-accent' : 'w-4 bg-line hover:bg-muted'
}`"
@click="idx = i"
/>
</div>
</div>
</section>
</template>
+19
View File
@@ -0,0 +1,19 @@
<script setup lang="ts">
import { ShieldCheck } from "lucide-vue-next";
</script>
<template>
<a href="#home" class="flex items-center gap-3" aria-label="آریا ایمن">
<span
class="flex size-10 items-center justify-center rounded-xl bg-accent text-bg shadow-[0_0_28px_rgba(255,90,0,0.35)]"
>
<ShieldCheck class="size-6" />
</span>
<span class="leading-tight">
<span class="block text-lg font-black">آریا ایمن</span>
<span class="block font-mono text-[9px] tracking-[0.32em] text-muted">
ARIA IMEN CO.
</span>
</span>
</a>
</template>
+12
View File
@@ -0,0 +1,12 @@
<script setup lang="ts">
import { motion } from "motion-v";
const props = withDefaults(defineProps<{ delay?: number }>(), { delay: 0 });
const motionProps = computed(() => fade(props.delay, 34));
</script>
<template>
<motion.div v-bind="motionProps">
<slot />
</motion.div>
</template>
+42
View File
@@ -0,0 +1,42 @@
<script setup lang="ts">
import Reveal from "./Reveal.vue";
withDefaults(
defineProps<{
tag: string;
/** plain-text title; use the #title slot for rich markup (line breaks, spans) */
title?: string;
desc?: string;
center?: boolean;
headingId?: string;
}>(),
{ center: false },
);
</script>
<template>
<div :class="center ? 'text-center' : ''">
<Reveal>
<div :class="`flex items-center gap-3 ${center ? 'justify-center' : ''}`">
<span class="font-mono text-sm text-accent">//</span>
<span class="font-mono text-xs tracking-[0.25em] text-muted">
{{ tag }}
</span>
<span class="h-px w-14 bg-line" />
</div>
</Reveal>
<Reveal :delay="0.08">
<h2
:id="headingId"
class="mt-5 text-3xl font-black leading-[1.25] md:text-5xl md:leading-[1.2]"
>
<slot name="title">{{ title }}</slot>
</h2>
</Reveal>
<Reveal v-if="desc" :delay="0.16">
<p :class="`mt-5 max-w-xl leading-8 text-muted ${center ? 'mx-auto' : ''}`">
{{ desc }}
</p>
</Reveal>
</div>
</template>
+16
View File
@@ -0,0 +1,16 @@
import { toJsonLd } from "#shared/seo/schema";
type Ld = Record<string, unknown>;
/** Renders JSON-LD <script> tags at the start of <body> (replaces the React <JsonLd> component). */
export function useJsonLd(data: Ld | Ld[], id = "site") {
const items = Array.isArray(data) ? data : [data];
useHead({
script: items.map((item, i) => ({
key: `ld-${id}-${i}`,
type: "application/ld+json",
innerHTML: toJsonLd(item),
tagPosition: "bodyOpen" as const,
})),
});
}
+13
View File
@@ -0,0 +1,13 @@
/** Reactive `window.scrollY > threshold`, evaluated after mount (passive listener). */
export function useScrolledPast(threshold: number) {
const past = ref(false);
const on = () => (past.value = window.scrollY > threshold);
onMounted(() => {
on();
window.addEventListener("scroll", on, { passive: true });
});
onBeforeUnmount(() => window.removeEventListener("scroll", on));
return past;
}
+28
View File
@@ -0,0 +1,28 @@
import { absUrl, siteConfig as s } from "#shared/config/site";
/** Site-wide <head> defaults shared by the app shell and the error page. */
export function useSiteSeo() {
useHead({
titleTemplate: (title) => (title ? `${title} | ${s.name}` : s.title),
});
useSeoMeta({
description: s.description,
themeColor: s.themeColor,
formatDetection: "telephone=no",
ogType: "website",
ogSiteName: s.name,
ogLocale: s.locale,
ogTitle: s.title,
ogDescription: s.description,
ogImage: absUrl(s.ogImage.src),
ogImageWidth: s.ogImage.width,
ogImageHeight: s.ogImage.height,
ogImageAlt: s.ogImage.alt,
twitterCard: "summary_large_image",
twitterTitle: s.title,
twitterDescription: s.description,
twitterImage: absUrl(s.ogImage.src),
twitterImageAlt: s.ogImage.alt,
});
}
+36
View File
@@ -0,0 +1,36 @@
/* Starts with "--:--" on the server so the first client render matches,
then sets the real Tehran time only after mount (avoids a hydration mismatch on the clock text) */
export function useTehranClock() {
const now = ref<Date | null>(null);
let t: ReturnType<typeof setInterval> | undefined;
onMounted(() => {
now.value = new Date();
t = setInterval(() => (now.value = new Date()), 15000);
});
onBeforeUnmount(() => clearInterval(t));
const time = computed(() =>
now.value
? new Intl.DateTimeFormat("fa-IR", {
hour: "2-digit",
minute: "2-digit",
timeZone: "Asia/Tehran",
}).format(now.value)
: "--:--",
);
const shift = computed(() => {
if (!now.value) return "";
const hour = Number(
new Intl.DateTimeFormat("en-US", {
hour: "numeric",
hour12: false,
timeZone: "Asia/Tehran",
}).format(now.value),
);
return hour >= 6 && hour < 14 ? "صبح" : hour >= 14 && hour < 22 ? "عصر" : "شب";
});
return { time, shift };
}
+44
View File
@@ -0,0 +1,44 @@
import { theme } from "ant-design-vue";
import type { ThemeConfig } from "ant-design-vue/es/config-provider/context";
/**
* Design-system palette — mirrors the Tailwind `@theme` tokens in assets/css/main.css.
* Keep both in sync: Tailwind drives layout/sections, Ant Design drives form controls.
*/
export const palette = {
bg: "#0a0a0c",
panel: "#121216",
panel2: "#17171c",
ink: "#edede6",
muted: "#8e8e96",
accent: "#ff5a00",
accentSoft: "#ff7a2a",
hazard: "#ffb800",
line: "#232329",
success: "#34d399", // tailwind emerald-400
} as const;
export const antdTheme: ThemeConfig = {
algorithm: theme.darkAlgorithm,
token: {
colorPrimary: palette.accent,
colorInfo: palette.accent,
colorWarning: palette.hazard,
colorSuccess: palette.success,
colorBgBase: palette.bg,
colorBgContainer: palette.bg,
colorBgElevated: palette.panel,
colorBorder: palette.line,
colorBorderSecondary: palette.line,
colorText: palette.ink,
colorTextDescription: palette.muted,
colorTextPlaceholder: "rgba(142, 142, 150, 0.5)",
colorTextLightSolid: palette.bg,
fontFamily: "var(--font-sans)",
fontSize: 14,
borderRadius: 8,
controlOutlineWidth: 0,
motionDurationMid: "0.15s",
wireframe: false,
},
};
+37
View File
@@ -0,0 +1,37 @@
<script setup lang="ts">
import type { NuxtError } from "#app";
import { ShieldAlert } from "lucide-vue-next";
const props = defineProps<{ error: NuxtError }>();
const notFound = computed(() => props.error.statusCode === 404);
useSiteSeo();
useHead({ title: () => (notFound.value ? "صفحه پیدا نشد" : "خطا") });
useSeoMeta({ robots: "noindex, follow" });
const goHome = () => clearError({ redirect: "/" });
</script>
<template>
<main class="flex min-h-screen flex-col items-center justify-center px-5 text-center">
<div class="hazard absolute inset-x-0 top-0 h-2.5" />
<ShieldAlert class="size-16 text-accent" />
<h1 class="mt-8 text-4xl font-black md:text-6xl">
{{ notFound ? "صفحه مورد نظر پیدا نشد" : "خطایی رخ داد" }}
</h1>
<p class="mt-5 max-w-md leading-8 text-muted">
{{
notFound
? "ممکن است آدرس اشتباه وارد شده باشد یا این صفحه جابه‌جا شده باشد."
: "لطفاً چند لحظه بعد دوباره تلاش کنید."
}}
</p>
<a
href="/"
class="mt-9 inline-flex items-center gap-2.5 rounded-full bg-accent px-7 py-4 font-bold text-bg transition-all duration-300 hover:bg-accent-soft"
@click.prevent="goHome"
>
بازگشت به صفحه اصلی
</a>
</main>
</template>
+46
View File
@@ -0,0 +1,46 @@
<script setup lang="ts">
import { absUrl } from "#shared/config/site";
import AppShell from "~/components/layout/AppShell.vue";
import Footer from "~/components/layout/Footer.vue";
import Hero from "~/components/sections/Hero.vue";
import TapeMarquee from "~/components/sections/TapeMarquee.vue";
import Stats from "~/components/sections/Stats.vue";
import Products from "~/components/sections/Products.vue";
import About from "~/components/sections/About.vue";
import Services from "~/components/sections/Services.vue";
import Standards from "~/components/sections/Standards.vue";
import Industries from "~/components/sections/Industries.vue";
import Testimonials from "~/components/sections/Testimonials.vue";
import Contact from "~/components/sections/Contact.vue";
const canonical = absUrl("/");
useHead({
link: [
{ rel: "canonical", href: canonical },
{ rel: "preload", as: "image", href: "/images/hero.jpg", fetchpriority: "high" },
],
});
useSeoMeta({
robots: "index, follow",
ogUrl: canonical,
});
</script>
<template>
<AppShell>
<main>
<Hero />
<TapeMarquee />
<Stats />
<Products />
<About />
<Services />
<Standards />
<Industries />
<Testimonials />
<Contact />
</main>
<Footer />
</AppShell>
</template>
+22
View File
@@ -0,0 +1,22 @@
import { createCache, extractStyle } from "ant-design-vue";
/**
* Ant Design Vue uses CSS-in-JS. During SSR/SSG we collect the generated styles into a cache
* and inline them into <head>, so prerendered HTML is fully styled before hydration (no FOUC).
*/
export default defineNuxtPlugin((nuxtApp) => {
const cache = createCache();
if (import.meta.server) {
nuxtApp.hook("app:rendered", ({ ssrContext }) => {
const css = extractStyle(cache, true);
if (css && ssrContext) {
ssrContext.head.push({
style: [{ key: "antd-cssinjs", innerHTML: css }],
});
}
});
}
return { provide: { antdCache: cache } };
});
+24
View File
@@ -0,0 +1,24 @@
import { defineStore } from "pinia";
export type ContactStatus = "idle" | "loading" | "success";
const emptyForm = () => ({ name: "", org: "", phone: "", message: "" });
export const useContactStore = defineStore("contact", {
state: () => ({
form: emptyForm(),
status: "idle" as ContactStatus,
}),
actions: {
// TODO: connect to the real API endpoint; currently simulates the request like the original
async submit() {
this.status = "loading";
await new Promise((r) => setTimeout(r, 1100));
this.status = "success";
},
reset() {
this.form = emptyForm();
this.status = "idle";
},
},
});
+21
View File
@@ -0,0 +1,21 @@
import { defineStore } from "pinia";
export const useUiStore = defineStore("ui", {
state: () => ({
/** true once the preloader has finished */
ready: false,
/** mobile navigation overlay */
menuOpen: false,
}),
actions: {
setReady() {
this.ready = true;
},
openMenu() {
this.menuOpen = true;
},
closeMenu() {
this.menuOpen = false;
},
},
});
+13
View File
@@ -0,0 +1,13 @@
export const EASE: [number, number, number, number] = [0.22, 1, 0.36, 1];
/** Convert latin digits to Persian digits */
export const faNum = (v: number | string): string =>
String(v).replace(/\d/g, (d) => "۰۱۲۳۴۵۶۷۸۹"[Number(d)]!);
/** While-in-view fade-up props for motion elements (spread with v-bind) */
export const fade = (delay = 0, y = 36) => ({
initial: { opacity: 0, y },
whileInView: { opacity: 1, y: 0 },
inViewOptions: { once: true, margin: "-60px" as const },
transition: { duration: 0.8, delay, ease: EASE },
});
-12
View File
@@ -1,12 +0,0 @@
import type { NextConfig } from "next";
const nextConfig: NextConfig = {
output: "export",
trailingSlash: false,
images: {
unoptimized: true,
},
poweredByHeader: false,
};
export default nextConfig;
+6 -1
View File
@@ -4,11 +4,16 @@ server {
root /usr/share/nginx/html;
index index.html;
location /_next/static/ {
location /_nuxt/ {
expires 1y;
add_header Cache-Control "public, immutable";
}
# mime.types پیش‌فرض nginx نوع webmanifest را ندارد
location = /manifest.webmanifest {
default_type application/manifest+json;
}
# $uri/index.html به‌جای $uri/ تا دایرکتوری / ریدایرکت ۳۰۱ نسازد
location / {
try_files $uri $uri.html $uri/index.html =404;
+70
View File
@@ -0,0 +1,70 @@
import { fileURLToPath } from "node:url";
import tailwindcss from "@tailwindcss/vite";
import { siteConfig } from "./shared/config/site";
/** Unmatched path rendered through error.vue at build time and saved as out/404.html */
const NOT_FOUND_ROUTE = "/__not-found";
export default defineNuxtConfig({
compatibilityDate: "2026-10-01",
ssr: true,
devtools: { enabled: false },
modules: ["@pinia/nuxt"],
css: ["~/assets/css/main.css", "~/assets/css/antd-overrides.css"],
app: {
head: {
htmlAttrs: { lang: siteConfig.lang, dir: siteConfig.dir },
bodyAttrs: {
class: "relative min-h-screen bg-bg font-sans text-ink antialiased",
},
charset: "utf-8",
viewport: "width=device-width, initial-scale=1",
link: [
{
rel: "preload",
href: "/fonts/Vazirmatn-Variable.woff2",
as: "font",
type: "font/woff2",
crossorigin: "",
},
{
rel: "preload",
href: "/fonts/IBMPlexMono-Regular.woff2",
as: "font",
type: "font/woff2",
crossorigin: "",
},
{ rel: "icon", type: "image/svg+xml", href: siteConfig.favicon },
{ rel: "manifest", href: "/manifest.webmanifest" },
],
},
},
vite: {
plugins: [tailwindcss()],
ssr: { noExternal: ["ant-design-vue", "@ant-design/icons-vue"] },
},
// Static export (`npm run build` → ./out), served by nginx in Docker — same flow as the Next build.
nitro: {
output: { publicDir: fileURLToPath(new URL("./out", import.meta.url)) },
prerender: {
crawlLinks: false,
routes: ["/", "/robots.txt", "/sitemap.xml", "/manifest.webmanifest", NOT_FOUND_ROUTE],
},
hooks: {
// Nuxt's own /404.html is an empty SPA shell. Replace it with a server-rendered error.vue
// (real content + noindex in the static HTML) produced by prerendering an unmatched route.
"prerender:generate"(route) {
if (route.route === "/404.html") route.skip = true;
if (route.route === NOT_FOUND_ROUTE && route.error?.statusCode === 404) {
route.error = undefined;
route.fileName = "/404.html";
}
},
},
},
typescript: { strict: true },
});
-1
View File
File diff suppressed because one or more lines are too long
@@ -1 +0,0 @@
self.__BUILD_MANIFEST=function(e,r,t){return{__rewrites:{afterFiles:[],beforeFiles:[],fallback:[]},__routerFilterStatic:{numItems:5,errorRate:1e-4,numBits:96,numHashes:14,bitArray:[1,1,0,e,e,0,r,r,e,r,e,e,e,r,e,r,r,e,e,e,e,r,e,r,r,e,e,r,e,r,r,r,r,r,r,r,r,e,r,e,e,r,e,e,r,e,r,e,r,e,e,r,e,r,r,r,e,r,e,e,e,e,r,e,e,e,r,r,r,e,e,e,r,r,e,r,r,r,e,e,e,e,e,e,r,r,r,e,e,r,r,r,e,e,r,r]},__routerFilterDynamic:{numItems:r,errorRate:1e-4,numBits:r,numHashes:null,bitArray:[]},"/_error":["static/chunks/pages/_error-013f4188946cdd04.js"],sortedPages:["/_app","/_error"]}}(1,0,1e-4),self.__BUILD_MANIFEST_CB&&self.__BUILD_MANIFEST_CB();
@@ -1 +0,0 @@
self.__SSG_MANIFEST=new Set([]);self.__SSG_MANIFEST_CB&&self.__SSG_MANIFEST_CB()
@@ -1 +0,0 @@
"use strict";(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[139],{5139:(e,t,l)=>{Object.defineProperty(t,"__esModule",{value:!0}),Object.defineProperty(t,"default",{enumerable:!0,get:function(){return s}});let u=l(4252),n=l(7876),a=u._(l(4232)),o=l(1033);async function r(e){let{Component:t,ctx:l}=e;return{pageProps:await (0,o.loadGetInitialProps)(t,l)}}class s extends a.default.Component{render(){let{Component:e,pageProps:t}=this.props;return(0,n.jsx)(e,{...t})}}s.origGetInitialProps=r,s.getInitialProps=r,("function"==typeof t.default||"object"==typeof t.default&&null!==t.default)&&void 0===t.default.__esModule&&(Object.defineProperty(t.default,"__esModule",{value:!0}),Object.assign(t.default,t),e.exports=t.default)}}]);
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1 +0,0 @@
(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[337,475,492,784],{4441:()=>{}},_=>{_.O(0,[441,255,358],()=>_(_.s=4441)),_N_E=_.O()}]);
@@ -1 +0,0 @@
(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[177],{1255:e=>{e.exports={style:{fontFamily:"'vazirmatn', 'vazirmatn Fallback', Tahoma, system-ui, sans-serif"},className:"__className_b3ce59",variable:"__variable_b3ce59"}},3795:()=>{},4393:e=>{e.exports={style:{fontFamily:"'ibmPlexMono', 'ibmPlexMono Fallback', ui-monospace, monospace"},className:"__className_d30186",variable:"__variable_d30186"}},5776:(e,a,s)=>{Promise.resolve().then(s.t.bind(s,1255,23)),Promise.resolve().then(s.t.bind(s,4393,23)),Promise.resolve().then(s.t.bind(s,3795,23))}},e=>{e.O(0,[266,441,255,358],()=>e(e.s=5776)),_N_E=e.O()}]);
@@ -1 +0,0 @@
(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[337,475,492,784],{4441:()=>{}},_=>{_.O(0,[441,255,358],()=>_(_.s=4441)),_N_E=_.O()}]);
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1 +0,0 @@
(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[337,475,492,784],{4441:()=>{}},_=>{_.O(0,[441,255,358],()=>_(_.s=4441)),_N_E=_.O()}]);
@@ -1 +0,0 @@
(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[337,475,492,784],{4441:()=>{}},_=>{_.O(0,[441,255,358],()=>_(_.s=4441)),_N_E=_.O()}]);
File diff suppressed because one or more lines are too long
@@ -1 +0,0 @@
(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[358],{7283:(e,s,n)=>{Promise.resolve().then(n.t.bind(n,7150,23)),Promise.resolve().then(n.t.bind(n,1959,23)),Promise.resolve().then(n.t.bind(n,7989,23)),Promise.resolve().then(n.t.bind(n,3886,23)),Promise.resolve().then(n.t.bind(n,9766,23)),Promise.resolve().then(n.t.bind(n,5278,23)),Promise.resolve().then(n.t.bind(n,8924,23)),Promise.resolve().then(n.t.bind(n,4431,23)),Promise.resolve().then(n.bind(n,622))},9393:()=>{}},e=>{var s=s=>e(e.s=s);e.O(0,[441,255],()=>(s(1666),s(7283))),_N_E=e.O()}]);
File diff suppressed because one or more lines are too long
@@ -1 +0,0 @@
(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[139,636],{326:(e,t,n)=>{(window.__NEXT_P=window.__NEXT_P||[]).push(["/_app",function(){return n(5139)}])},5139:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),Object.defineProperty(t,"default",{enumerable:!0,get:function(){return s}});let u=n(4252),l=n(7876),a=u._(n(4232)),o=n(1033);async function r(e){let{Component:t,ctx:n}=e;return{pageProps:await (0,o.loadGetInitialProps)(t,n)}}class s extends a.default.Component{render(){let{Component:e,pageProps:t}=this.props;return(0,l.jsx)(e,{...t})}}s.origGetInitialProps=r,s.getInitialProps=r,("function"==typeof t.default||"object"==typeof t.default&&null!==t.default)&&void 0===t.default.__esModule&&(Object.defineProperty(t.default,"__esModule",{value:!0}),Object.assign(t.default,t),e.exports=t.default)}},e=>{var t=t=>e(e.s=t);e.O(0,[593,792],()=>(t(326),t(6763))),_N_E=e.O()}]);
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1 +0,0 @@
(()=>{"use strict";var e={},t={};function r(o){var n=t[o];if(void 0!==n)return n.exports;var a=t[o]={exports:{}},i=!0;try{e[o](a,a.exports,r),i=!1}finally{i&&delete t[o]}return a.exports}r.m=e,(()=>{var e=[];r.O=(t,o,n,a)=>{if(o){a=a||0;for(var i=e.length;i>0&&e[i-1][2]>a;i--)e[i]=e[i-1];e[i]=[o,n,a];return}for(var u=1/0,i=0;i<e.length;i++){for(var[o,n,a]=e[i],l=!0,c=0;c<o.length;c++)(!1&a||u>=a)&&Object.keys(r.O).every(e=>r.O[e](o[c]))?o.splice(c--,1):(l=!1,a<u&&(u=a));if(l){e.splice(i--,1);var d=n();void 0!==d&&(t=d)}}return t}})(),r.n=e=>{var t=e&&e.__esModule?()=>e.default:()=>e;return r.d(t,{a:t}),t},(()=>{var e,t=Object.getPrototypeOf?e=>Object.getPrototypeOf(e):e=>e.__proto__;r.t=function(o,n){if(1&n&&(o=this(o)),8&n||"object"==typeof o&&o&&(4&n&&o.__esModule||16&n&&"function"==typeof o.then))return o;var a=Object.create(null);r.r(a);var i={};e=e||[null,t({}),t([]),t(t)];for(var u=2&n&&o;"object"==typeof u&&!~e.indexOf(u);u=t(u))Object.getOwnPropertyNames(u).forEach(e=>i[e]=()=>o[e]);return i.default=()=>o,r.d(a,i),a}})(),r.d=(e,t)=>{for(var o in t)r.o(t,o)&&!r.o(e,o)&&Object.defineProperty(e,o,{enumerable:!0,get:t[o]})},r.f={},r.e=e=>Promise.all(Object.keys(r.f).reduce((t,o)=>(r.f[o](e,t),t),[])),r.u=e=>"static/chunks/"+e+"."+({139:"7a5a8e93a21948c1",646:"f342b7cffc01feb0"})[e]+".js",r.miniCssF=e=>{},r.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||Function("return this")()}catch(e){if("object"==typeof window)return window}}(),r.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),(()=>{var e={},t="_N_E:";r.l=(o,n,a,i)=>{if(e[o])return void e[o].push(n);if(void 0!==a)for(var u,l,c=document.getElementsByTagName("script"),d=0;d<c.length;d++){var s=c[d];if(s.getAttribute("src")==o||s.getAttribute("data-webpack")==t+a){u=s;break}}u||(l=!0,(u=document.createElement("script")).charset="utf-8",u.timeout=120,r.nc&&u.setAttribute("nonce",r.nc),u.setAttribute("data-webpack",t+a),u.src=r.tu(o)),e[o]=[n];var f=(t,r)=>{u.onerror=u.onload=null,clearTimeout(p);var n=e[o];if(delete e[o],u.parentNode&&u.parentNode.removeChild(u),n&&n.forEach(e=>e(r)),t)return t(r)},p=setTimeout(f.bind(null,void 0,{type:"timeout",target:u}),12e4);u.onerror=f.bind(null,u.onerror),u.onload=f.bind(null,u.onload),l&&document.head.appendChild(u)}})(),r.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},(()=>{var e;r.tt=()=>(void 0===e&&(e={createScriptURL:e=>e},"undefined"!=typeof trustedTypes&&trustedTypes.createPolicy&&(e=trustedTypes.createPolicy("nextjs#bundler",e))),e)})(),r.tu=e=>r.tt().createScriptURL(e),r.p="/_next/",(()=>{var e={68:0,266:0};r.f.j=(t,o)=>{var n=r.o(e,t)?e[t]:void 0;if(0!==n)if(n)o.push(n[2]);else if(/^(266|68)$/.test(t))e[t]=0;else{var a=new Promise((r,o)=>n=e[t]=[r,o]);o.push(n[2]=a);var i=r.p+r.u(t),u=Error();r.l(i,o=>{if(r.o(e,t)&&(0!==(n=e[t])&&(e[t]=void 0),n)){var a=o&&("load"===o.type?"missing":o.type),i=o&&o.target&&o.target.src;u.message="Loading chunk "+t+" failed.\n("+a+": "+i+")",u.name="ChunkLoadError",u.type=a,u.request=i,n[1](u)}},"chunk-"+t,t)}},r.O.j=t=>0===e[t];var t=(t,o)=>{var n,a,[i,u,l]=o,c=0;if(i.some(t=>0!==e[t])){for(n in u)r.o(u,n)&&(r.m[n]=u[n]);if(l)var d=l(r)}for(t&&t(o);c<i.length;c++)a=i[c],r.o(e,a)&&e[a]&&e[a][0](),e[a]=0;return r.O(d)},o=self.webpackChunk_N_E=self.webpackChunk_N_E||[];o.forEach(t.bind(null,0)),o.push=t.bind(null,o.push.bind(o))})()})();
File diff suppressed because one or more lines are too long
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.

Before

Width:  |  Height:  |  Size: 306 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 201 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 108 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 119 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 116 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 188 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 101 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 265 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 132 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 244 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 123 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 191 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 198 KiB

-1
View File
File diff suppressed because one or more lines are too long
-75
View File
@@ -1,75 +0,0 @@
1:"$Sreact.fragment"
2:I[9766,[],""]
3:I[8924,[],""]
4:I[2619,["345","static/chunks/app/not-found-7b6fd15ee953c61c.js"],""]
5:I[1232,["421","static/chunks/421-20f474da4f0f7dbd.js","974","static/chunks/app/page-a20904802d61ed6a.js"],"AppShell"]
6:I[1356,["421","static/chunks/421-20f474da4f0f7dbd.js","974","static/chunks/app/page-a20904802d61ed6a.js"],"Image"]
1b:I[7150,[],""]
:HL["/_next/static/css/2a3679b3ddc69c91.css","style"]
0:{"P":null,"b":"71ES_mB02KTIaEPcPlWHR","p":"","c":["",""],"i":false,"f":[[["",{"children":["__PAGE__",{}]},"$undefined","$undefined",true],["",["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/_next/static/css/2a3679b3ddc69c91.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}]],["$","html",null,{"lang":"fa","dir":"rtl","className":"__variable_b3ce59 __variable_d30186","children":["$","body",null,{"className":"relative min-h-screen bg-bg font-sans text-ink antialiased","children":[[["$","script","0",{"type":"application/ld+json","dangerouslySetInnerHTML":{"__html":"{\"@context\":\"https://schema.org\",\"@type\":\"LocalBusiness\",\"@id\":\"https://ariaiman.ir/#organization\",\"name\":\"آریا ایمن\",\"url\":\"https://ariaiman.ir\",\"logo\":\"https://ariaiman.ir/images/hero.jpg\",\"image\":\"https://ariaiman.ir/images/hero.jpg\",\"email\":\"info@ariaiman.ir\",\"telephone\":\"+98-21-91008800\",\"address\":{\"@type\":\"PostalAddress\",\"streetAddress\":\"خیابان کارگر شمالی، خیابان فر، کوچه دوم، پلاک ۱۲\",\"addressLocality\":\"تهران\",\"addressCountry\":\"IR\"}}"}}],["$","script","1",{"type":"application/ld+json","dangerouslySetInnerHTML":{"__html":"{\"@context\":\"https://schema.org\",\"@type\":\"WebSite\",\"@id\":\"https://ariaiman.ir/#website\",\"url\":\"https://ariaiman.ir\",\"name\":\"آریا ایمن\",\"inLanguage\":\"fa\",\"publisher\":{\"@id\":\"https://ariaiman.ir/#organization\"}}"}}]],["$","$L2",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L3",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[["$","main",null,{"className":"flex min-h-screen flex-col items-center justify-center px-5 text-center","children":[["$","div",null,{"className":"hazard absolute inset-x-0 top-0 h-2.5"}],["$","svg",null,{"ref":"$undefined","xmlns":"http://www.w3.org/2000/svg","width":24,"height":24,"viewBox":"0 0 24 24","fill":"none","stroke":"currentColor","strokeWidth":2,"strokeLinecap":"round","strokeLinejoin":"round","className":"lucide lucide-shield-alert size-16 text-accent","children":[["$","path","oel41y",{"d":"M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z"}],["$","path","1got3b",{"d":"M12 8v4"}],["$","path","1drbdi",{"d":"M12 16h.01"}],"$undefined"]}],["$","h1",null,{"className":"mt-8 text-4xl font-black md:text-6xl","children":"صفحه مورد نظر پیدا نشد"}],["$","p",null,{"className":"mt-5 max-w-md leading-8 text-muted","children":"ممکن است آدرس اشتباه وارد شده باشد یا این صفحه جابه‌جا شده باشد."}],["$","$L4",null,{"href":"/","className":"mt-9 inline-flex items-center gap-2.5 rounded-full bg-accent px-7 py-4 font-bold text-bg transition-all duration-300 hover:bg-accent-soft","children":"بازگشت به صفحه اصلی"}]]}],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]]}]}]]}],{"children":["__PAGE__",["$","$1","c",{"children":[["$","$L5",null,{"children":[["$","main",null,{"children":[["$","section",null,{"id":"home","className":"relative flex min-h-screen flex-col overflow-hidden","children":[["$","div",null,{"className":"absolute inset-0","children":[["$","$L6",null,{"src":"/images/hero.jpg","alt":"کارگر صنعتی با تجهیزات ایمنی کامل","fill":true,"priority":true,"sizes":"100vw","className":"object-cover"}],["$","div",null,{"className":"absolute inset-0 bg-gradient-to-l from-bg via-bg/80 to-bg/25"}],["$","div",null,{"className":"absolute inset-0 bg-gradient-to-t from-bg via-transparent to-bg/60"}],["$","div",null,{"className":"absolute inset-0 bg-grid opacity-50"}]]}],["$","div",null,{"className":"tape-v absolute bottom-0 end-0 z-10 h-44 w-1.5 opacity-70"}],["$","div",null,{"className":"relative z-10 mx-auto flex w-full max-w-7xl flex-1 items-center px-5 pb-24 pt-36 md:px-8","children":["$","div",null,{"className":"grid w-full items-center gap-12 lg:grid-cols-12","children":[["$","div",null,{"className":"lg:col-span-7","children":[["$","div",null,{"className":"animate-fade-up-1 inline-flex items-center gap-2.5 rounded-full border border-accent/30 bg-accent/10 px-4 py-2 backdrop-blur","children":[["$","span",null,{"className":"size-1.5 animate-pulse rounded-full bg-accent"}],"$L7"]}],"$L8","$L9","$La","$Lb"]}],"$Lc"]}]}],"$Ld","$Le"]}],"$Lf","$L10","$L11","$L12","$L13","$L14","$L15","$L16","$L17"]}],"$L18"]}],null,"$L19"]}],{},null,false]},null,false],"$L1a",false]],"m":"$undefined","G":["$1b",[]],"s":false,"S":true}
29:I[1181,["421","static/chunks/421-20f474da4f0f7dbd.js","974","static/chunks/app/page-a20904802d61ed6a.js"],"Reveal"]
2a:I[470,["421","static/chunks/421-20f474da4f0f7dbd.js","974","static/chunks/app/page-a20904802d61ed6a.js"],"CountUp"]
2b:I[2236,["421","static/chunks/421-20f474da4f0f7dbd.js","974","static/chunks/app/page-a20904802d61ed6a.js"],"Products"]
2f:I[1180,["421","static/chunks/421-20f474da4f0f7dbd.js","974","static/chunks/app/page-a20904802d61ed6a.js"],"Services"]
37:I[9570,["421","static/chunks/421-20f474da4f0f7dbd.js","974","static/chunks/app/page-a20904802d61ed6a.js"],"Industries"]
38:I[5559,["421","static/chunks/421-20f474da4f0f7dbd.js","974","static/chunks/app/page-a20904802d61ed6a.js"],"Testimonials"]
39:I[4960,["421","static/chunks/421-20f474da4f0f7dbd.js","974","static/chunks/app/page-a20904802d61ed6a.js"],"Contact"]
40:I[4431,[],"OutletBoundary"]
42:I[5278,[],"AsyncMetadataOutlet"]
44:I[4431,[],"ViewportBoundary"]
46:I[4431,[],"MetadataBoundary"]
47:"$Sreact.suspense"
7:["$","span",null,{"className":"text-xs font-medium text-accent","children":"تأمین‌کننده رسمی تجهیزات حفاظت فردی (PPE)"}]
8:["$","h1",null,{"className":"animate-fade-up-2 mt-7 text-5xl font-black leading-[1.15] md:text-7xl md:leading-[1.1]","children":["ایمنی یعنی",["$","br",null,{}],["$","span",null,{"className":"mt-3 inline-block -rotate-1 rounded-lg bg-hazard px-4 py-1.5 text-bg shadow-[0_10px_50px_rgba(255,184,0,0.25)]","children":"بازگشت به خانه"}]]}]
9:["$","p",null,{"className":"animate-fade-up-3 mt-7 max-w-xl text-base leading-9 text-ink/70 md:text-lg md:leading-10","children":"آریا ایمن با بیش از ۱۸ سال تجربه، تأمین‌کننده تخصصی تجهیزات ایمنی و حفاظت فردی برای سازمان‌هایی است که جان انسان‌ها را جدی می‌گیرند؛ از کلاه ایمنی تا هارنس نجات، از مشاوره تا بازرسی و آموزش."}]
a:["$","div",null,{"className":"animate-fade-up-4 mt-9 flex flex-wrap items-center gap-4","children":[["$","a",null,{"href":"#contact","className":"group inline-flex items-center gap-2.5 rounded-full bg-accent px-7 py-4 font-bold text-bg transition-all duration-300 hover:bg-accent-soft hover:shadow-[0_8px_45px_rgba(255,90,0,0.5)]","children":["درخواست مشاوره رایگان",["$","svg",null,{"ref":"$undefined","xmlns":"http://www.w3.org/2000/svg","width":24,"height":24,"viewBox":"0 0 24 24","fill":"none","stroke":"currentColor","strokeWidth":2,"strokeLinecap":"round","strokeLinejoin":"round","className":"lucide lucide-arrow-left size-5 transition-transform duration-300 group-hover:-translate-x-1.5","children":[["$","path","1l729n",{"d":"m12 19-7-7 7-7"}],["$","path","x3x0zl",{"d":"M19 12H5"}],"$undefined"]}]]}],["$","a",null,{"href":"#products","className":"inline-flex items-center gap-2.5 rounded-full border border-ink/25 px-7 py-4 font-bold text-ink backdrop-blur transition-colors duration-300 hover:border-accent hover:text-accent","children":"مشاهده محصولات"}]]}]
b:["$","div",null,{"className":"animate-fade-up-5 mt-10 flex flex-wrap gap-3","children":[["$","span","ISO 9001:2015",{"className":"inline-flex items-center gap-2 rounded-full border border-ink/15 bg-bg/50 px-4 py-2 font-mono text-[11px] tracking-wider text-ink/70 backdrop-blur transition-colors hover:border-accent/50 hover:text-ink","children":[["$","svg",null,{"ref":"$undefined","xmlns":"http://www.w3.org/2000/svg","width":24,"height":24,"viewBox":"0 0 24 24","fill":"none","stroke":"currentColor","strokeWidth":2,"strokeLinecap":"round","strokeLinejoin":"round","className":"lucide lucide-shield-check size-4 text-accent","children":[["$","path","oel41y",{"d":"M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z"}],["$","path","dzmm74",{"d":"m9 12 2 2 4-4"}],"$undefined"]}],"ISO 9001:2015"]}],["$","span","CE CERTIFIED",{"className":"inline-flex items-center gap-2 rounded-full border border-ink/15 bg-bg/50 px-4 py-2 font-mono text-[11px] tracking-wider text-ink/70 backdrop-blur transition-colors hover:border-accent/50 hover:text-ink","children":[["$","svg",null,{"ref":"$undefined","xmlns":"http://www.w3.org/2000/svg","width":24,"height":24,"viewBox":"0 0 24 24","fill":"none","stroke":"currentColor","strokeWidth":2,"strokeLinecap":"round","strokeLinejoin":"round","className":"lucide lucide-badge-check size-4 text-accent","children":[["$","path","3c2336",{"d":"M3.85 8.62a4 4 0 0 1 4.78-4.77 4 4 0 0 1 6.74 0 4 4 0 0 1 4.78 4.78 4 4 0 0 1 0 6.74 4 4 0 0 1-4.77 4.78 4 4 0 0 1-6.75 0 4 4 0 0 1-4.78-4.77 4 4 0 0 1 0-6.76Z"}],["$","path","dzmm74",{"d":"m9 12 2 2 4-4"}],"$undefined"]}],"CE CERTIFIED"]}],["$","span","EN STANDARDS",{"className":"inline-flex items-center gap-2 rounded-full border border-ink/15 bg-bg/50 px-4 py-2 font-mono text-[11px] tracking-wider text-ink/70 backdrop-blur transition-colors hover:border-accent/50 hover:text-ink","children":[["$","svg",null,{"ref":"$undefined","xmlns":"http://www.w3.org/2000/svg","width":24,"height":24,"viewBox":"0 0 24 24","fill":"none","stroke":"currentColor","strokeWidth":2,"strokeLinecap":"round","strokeLinejoin":"round","className":"lucide lucide-award size-4 text-accent","children":[["$","path","1yiouv",{"d":"m15.477 12.89 1.515 8.526a.5.5 0 0 1-.81.47l-3.58-2.687a1 1 0 0 0-1.197 0l-3.586 2.686a.5.5 0 0 1-.81-.469l1.514-8.526"}],["$","circle","1vp47v",{"cx":"12","cy":"8","r":"6"}],"$undefined"]}],"EN STANDARDS"]}]]}]
c:["$","div",null,{"className":"hidden flex-col items-center gap-10 lg:col-span-5 lg:flex","children":[["$","div",null,{"className":"animate-fade-up-6 relative","children":["$","div",null,{"className":"relative size-44","children":[["$","div",null,{"className":"animate-spin-slow absolute inset-0","children":["$","svg",null,{"viewBox":"0 0 120 120","className":"size-full","children":[["$","defs",null,{"children":["$","path",null,{"id":"badge-circle","d":"M60,60 m-47,0 a47,47 0 1,1 94,0 a47,47 0 1,1 -94,0"}]}],["$","text",null,{"fontSize":"9","letterSpacing":"2.6","className":"fill-ink/60 font-mono","children":["$","textPath",null,{"href":"#badge-circle","children":"ARIA IMEN SAFETY • ISO 9001 • CE CERTIFIED • EST 2007 •"}]}]]}]}],["$","div",null,{"className":"absolute inset-0 flex items-center justify-center","children":["$","span",null,{"className":"flex size-20 items-center justify-center rounded-full border border-accent/30 bg-accent/10","children":["$","svg",null,{"ref":"$undefined","xmlns":"http://www.w3.org/2000/svg","width":24,"height":24,"viewBox":"0 0 24 24","fill":"none","stroke":"currentColor","strokeWidth":2,"strokeLinecap":"round","strokeLinejoin":"round","className":"lucide lucide-hard-hat size-9 text-accent","children":[["$","path","1p9q5i",{"d":"M10 10V5a1 1 0 0 1 1-1h2a1 1 0 0 1 1 1v5"}],["$","path","1hnv84",{"d":"M14 6a6 6 0 0 1 6 6v3"}],["$","path","9ciidu",{"d":"M4 15v-3a6 6 0 0 1 6-6"}],["$","rect","g3x8cw",{"x":"2","y":"15","width":"20","height":"4","rx":"1"}],"$undefined"]}]}]}]]}]}],["$","div",null,{"className":"animate-fade-up-7 w-full max-w-sm border border-line bg-bg/70 p-5 backdrop-blur-md","children":[["$","div",null,{"className":"flex items-center justify-between font-mono text-[11px]","children":[["$","span",null,{"className":"text-muted","children":"SITE STATUS"}],["$","span",null,{"className":"flex items-center gap-2 text-emerald-400","children":[["$","span",null,{"className":"size-1.5 animate-pulse rounded-full bg-emerald-400"}],"SECURE"]}]]}],["$","div",null,{"className":"mt-3.5 flex items-center justify-between font-mono text-[11px]","children":[["$","span",null,{"className":"text-muted","children":"PPE COMPLIANCE"}],["$","span",null,{"className":"font-semibold text-accent","children":"۱۰۰٪"}]]}],["$","div",null,{"className":"mt-3.5 flex items-center justify-between font-mono text-[11px]","children":[["$","span",null,{"className":"text-muted","children":"آخرین بازرسی"}],["$","span",null,{"children":"۱۴۰۴/۰۸/۱۲"}]]}],["$","div",null,{"className":"my-4 h-px bg-line"}],["$","div",null,{"className":"font-mono text-[11px] leading-6 text-muted","children":["سیستم پایش حرفه‌ای آریا ایمن",["$","span",null,{"className":"animate-blink text-accent","children":"▍"}]]}]]}]]}]
d:["$","div",null,{"className":"animate-fade-up-9 absolute bottom-28 left-1/2 z-10 hidden md:block","children":["$","div",null,{"className":"-translate-x-1/2 flex flex-col items-center gap-2","children":[["$","span",null,{"className":"font-mono text-[10px] tracking-[0.35em] text-muted","children":"SCROLL"}],["$","svg",null,{"ref":"$undefined","xmlns":"http://www.w3.org/2000/svg","width":24,"height":24,"viewBox":"0 0 24 24","fill":"none","stroke":"currentColor","strokeWidth":2,"strokeLinecap":"round","strokeLinejoin":"round","className":"lucide lucide-chevron-down size-4 animate-bounce text-accent","children":[["$","path","qrunsl",{"d":"m6 9 6 6 6-6"}],"$undefined"]}]]}]}]
e:["$","div",null,{"className":"animate-fade-up-8 relative z-10 border-t border-ink/10 backdrop-blur-sm","children":["$","div",null,{"className":"mx-auto grid max-w-7xl grid-cols-1 gap-px bg-ink/10 sm:grid-cols-3","children":[["$","div","سال تجربه در صنعت ایمنی",{"className":"flex items-baseline gap-4 bg-bg/60 px-6 py-5 backdrop-blur-md md:px-8","children":[["$","span",null,{"className":"text-2xl font-black text-ink md:text-3xl","children":"۱۸+"}],["$","span",null,{"className":"text-xs leading-5 text-muted md:text-sm","children":"سال تجربه در صنعت ایمنی"}]]}],["$","div","سازمان تحت پوشش",{"className":"flex items-baseline gap-4 bg-bg/60 px-6 py-5 backdrop-blur-md md:px-8","children":[["$","span",null,{"className":"text-2xl font-black text-ink md:text-3xl","children":"۸۵۰+"}],["$","span",null,{"className":"text-xs leading-5 text-muted md:text-sm","children":"سازمان تحت پوشش"}]]}],["$","div","رضایت مشتریان",{"className":"flex items-baseline gap-4 bg-bg/60 px-6 py-5 backdrop-blur-md md:px-8","children":[["$","span",null,{"className":"text-2xl font-black text-ink md:text-3xl","children":"۹۸٪"}],["$","span",null,{"className":"text-xs leading-5 text-muted md:text-sm","children":"رضایت مشتریان"}]]}]]}]}]
f:["$","section",null,{"aria-hidden":true,"className":"pointer-events-none relative z-20 overflow-hidden py-10","children":["$","div",null,{"className":"-rotate-[1.6deg] scale-[1.04]","children":[["$","div",null,{"className":"hazard h-2.5"}],["$","div",null,{"dir":"ltr","className":"overflow-hidden bg-hazard py-3.5 text-bg","children":["$","div",null,{"className":"animate-marquee flex w-max","children":[["$","span","0",{"className":"flex items-center gap-7 px-7 text-sm font-extrabold whitespace-nowrap","children":["ISO 9001:2015",["$","svg",null,{"ref":"$undefined","xmlns":"http://www.w3.org/2000/svg","width":24,"height":24,"viewBox":"0 0 24 24","fill":"none","stroke":"currentColor","strokeWidth":2.5,"strokeLinecap":"round","strokeLinejoin":"round","className":"lucide lucide-shield-check size-4 shrink-0","children":[["$","path","oel41y",{"d":"M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z"}],["$","path","dzmm74",{"d":"m9 12 2 2 4-4"}],"$undefined"]}]]}],["$","span","1",{"className":"flex items-center gap-7 px-7 text-sm font-extrabold whitespace-nowrap","children":["CE CERTIFIED",["$","svg",null,{"ref":"$undefined","xmlns":"http://www.w3.org/2000/svg","width":24,"height":24,"viewBox":"0 0 24 24","fill":"none","stroke":"currentColor","strokeWidth":2.5,"strokeLinecap":"round","strokeLinejoin":"round","className":"lucide lucide-shield-check size-4 shrink-0","children":[["$","path","oel41y",{"d":"M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z"}],["$","path","dzmm74",{"d":"m9 12 2 2 4-4"}],"$undefined"]}]]}],["$","span","2",{"className":"flex items-center gap-7 px-7 text-sm font-extrabold whitespace-nowrap","children":["گارانتی اصالت کالا",["$","svg",null,{"ref":"$undefined","xmlns":"http://www.w3.org/2000/svg","width":24,"height":24,"viewBox":"0 0 24 24","fill":"none","stroke":"currentColor","strokeWidth":2.5,"strokeLinecap":"round","strokeLinejoin":"round","className":"lucide lucide-shield-check size-4 shrink-0","children":[["$","path","oel41y",{"d":"M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z"}],["$","path","dzmm74",{"d":"m9 12 2 2 4-4"}],"$undefined"]}]]}],["$","span","3",{"className":"flex items-center gap-7 px-7 text-sm font-extrabold whitespace-nowrap","children":["تحویل سراسری کشوری",["$","svg",null,{"ref":"$undefined","xmlns":"http://www.w3.org/2000/svg","width":24,"height":24,"viewBox":"0 0 24 24","fill":"none","stroke":"currentColor","strokeWidth":2.5,"strokeLinecap":"round","strokeLinejoin":"round","className":"lucide lucide-shield-check size-4 shrink-0","children":[["$","path","oel41y",{"d":"M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z"}],["$","path","dzmm74",{"d":"m9 12 2 2 4-4"}],"$undefined"]}]]}],["$","span","4",{"className":"flex items-center gap-7 px-7 text-sm font-extrabold whitespace-nowrap","children":["مشاوره و بازرسی تخصصی رایگان",["$","svg",null,{"ref":"$undefined","xmlns":"http://www.w3.org/2000/svg","width":24,"height":24,"viewBox":"0 0 24 24","fill":"none","stroke":"currentColor","strokeWidth":2.5,"strokeLinecap":"round","strokeLinejoin":"round","className":"lucide lucide-shield-check size-4 shrink-0","children":[["$","path","oel41y",{"d":"M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z"}],["$","path","dzmm74",{"d":"m9 12 2 2 4-4"}],"$undefined"]}]]}],["$","span","5",{"className":"flex items-center gap-7 px-7 text-sm font-extrabold whitespace-nowrap","children":["EN STANDARDS",["$","svg",null,{"ref":"$undefined","xmlns":"http://www.w3.org/2000/svg","width":24,"height":24,"viewBox":"0 0 24 24","fill":"none","stroke":"currentColor","strokeWidth":2.5,"strokeLinecap":"round","strokeLinejoin":"round","className":"lucide lucide-shield-check size-4 shrink-0","children":["$L1c","$L1d","$undefined"]}]]}],"$L1e","$L1f","$L20","$L21","$L22","$L23","$L24","$L25","$L26","$L27"]}]}],"$L28"]}]}]
10:["$","section",null,{"className":"relative","aria-label":"آمار و دستاوردهای آریا ایمن","children":["$","div",null,{"className":"grid grid-cols-2 gap-px border-y border-line bg-line lg:grid-cols-4","children":[["$","div","سال تجربه در صنعت ایمنی",{"className":"group relative bg-bg p-8 transition-colors duration-500 hover:bg-panel md:p-10","children":[["$","span",null,{"className":"absolute end-5 top-5 font-mono text-[10px] tracking-[0.25em] text-muted/50 transition-colors duration-500 group-hover:text-accent/70","children":"SINCE 2007"}],["$","$L29",null,{"delay":0,"children":[["$","div",null,{"className":"text-4xl font-black md:text-6xl","children":["$","$L2a",null,{"end":18,"suffix":"+"}]}],["$","div",null,{"className":"mt-3 text-sm text-muted","children":"سال تجربه در صنعت ایمنی"}]]}],["$","span",null,{"className":"absolute bottom-0 start-0 h-0.5 w-0 bg-accent transition-all duration-500 group-hover:w-full"}]]}],["$","div","کد کالای ایمنی",{"className":"group relative bg-bg p-8 transition-colors duration-500 hover:bg-panel md:p-10","children":[["$","span",null,{"className":"absolute end-5 top-5 font-mono text-[10px] tracking-[0.25em] text-muted/50 transition-colors duration-500 group-hover:text-accent/70","children":"PPE CATALOG"}],["$","$L29",null,{"delay":0.08,"children":[["$","div",null,{"className":"text-4xl font-black md:text-6xl","children":["$","$L2a",null,{"end":2400,"suffix":"+"}]}],["$","div",null,{"className":"mt-3 text-sm text-muted","children":"کد کالای ایمنی"}]]}],["$","span",null,{"className":"absolute bottom-0 start-0 h-0.5 w-0 bg-accent transition-all duration-500 group-hover:w-full"}]]}],["$","div","مشتری سازمانی فعال",{"className":"group relative bg-bg p-8 transition-colors duration-500 hover:bg-panel md:p-10","children":[["$","span",null,{"className":"absolute end-5 top-5 font-mono text-[10px] tracking-[0.25em] text-muted/50 transition-colors duration-500 group-hover:text-accent/70","children":"B2B CLIENTS"}],["$","$L29",null,{"delay":0.16,"children":[["$","div",null,{"className":"text-4xl font-black md:text-6xl","children":["$","$L2a",null,{"end":850,"suffix":"+"}]}],["$","div",null,{"className":"mt-3 text-sm text-muted","children":"مشتری سازمانی فعال"}]]}],["$","span",null,{"className":"absolute bottom-0 start-0 h-0.5 w-0 bg-accent transition-all duration-500 group-hover:w-full"}]]}],["$","div","رضایت مشتریان",{"className":"group relative bg-bg p-8 transition-colors duration-500 hover:bg-panel md:p-10","children":[["$","span",null,{"className":"absolute end-5 top-5 font-mono text-[10px] tracking-[0.25em] text-muted/50 transition-colors duration-500 group-hover:text-accent/70","children":"SATISFACTION"}],["$","$L29",null,{"delay":0.24,"children":[["$","div",null,{"className":"text-4xl font-black md:text-6xl","children":["$","$L2a",null,{"end":98,"suffix":"٪"}]}],["$","div",null,{"className":"mt-3 text-sm text-muted","children":"رضایت مشتریان"}]]}],["$","span",null,{"className":"absolute bottom-0 start-0 h-0.5 w-0 bg-accent transition-all duration-500 group-hover:w-full"}]]}]]}]}]
11:["$","$L2b",null,{}]
12:["$","section",null,{"id":"about","aria-labelledby":"about-title","className":"relative overflow-hidden border-t border-line py-24 md:py-32","children":[["$","div",null,{"className":"absolute inset-0 bg-grid-fine opacity-50"}],["$","div",null,{"className":"relative mx-auto max-w-7xl px-5 md:px-8","children":["$","div",null,{"className":"grid items-center gap-14 lg:grid-cols-2","children":[["$","$L29",null,{"className":"relative order-2 lg:order-1","delay":0.1,"children":[["$","div",null,{"className":"relative overflow-hidden rounded-2xl border border-line","children":[["$","$L6",null,{"src":"/images/about.jpg","alt":"انبار تجهیزات ایمنی آریا ایمن","width":800,"height":600,"className":"aspect-[4/3] w-full object-cover"}],["$","div",null,{"className":"absolute inset-0 bg-gradient-to-t from-bg/80 via-transparent to-transparent"}],["$","div",null,{"className":"absolute bottom-5 start-5 flex items-center gap-4 rounded-xl border border-line bg-bg/80 px-5 py-4 backdrop-blur-md","children":[["$","span",null,{"className":"font-mono text-3xl font-bold text-accent","children":"۱۸+"}],["$","span",null,{"className":"text-xs leading-5 text-muted","children":["سال حفاظت از",["$","br",null,{}],"جان نیروی کار"]}]]}]]}],["$","div",null,{"className":"hazard absolute -top-3 -end-3 h-6 w-28 rounded-full opacity-90"}]]}],["$","div",null,{"className":"order-1 lg:order-2","children":[["$","div",null,{"className":"","children":[["$","$L29",null,{"children":["$","div",null,{"className":"flex items-center gap-3 ","children":[["$","span",null,{"className":"font-mono text-sm text-accent","children":"//"}],["$","span",null,{"className":"font-mono text-xs tracking-[0.25em] text-muted","children":"۰۲ — درباره ما"}],["$","span",null,{"className":"h-px w-14 bg-line"}]]}]}],["$","$L29",null,{"delay":0.08,"children":["$","h2",null,{"id":"about-title","className":"mt-5 text-3xl font-black leading-[1.25] md:text-5xl md:leading-[1.2]","children":["ایمنی برای ما شعار نیست؛",["$","br",null,{}],["$","span",null,{"className":"text-accent","children":"قرارداد است."}]]}]}],null]}],["$","$L29",null,{"delay":0.2,"children":["$","p",null,{"className":"mt-6 leading-9 text-muted","children":"از سال ۱۳۸۶، زمانی که اولین کلاه ایمنی را به یک کارگاه کوچک در جنوب تهران فروختیم، باور ما همین یک جمله بود: پشت هر تجهیز ایمنی، یک خانواده هست که منتظر بازگشت عزیزش است. امروز با بیش از ۸۵۰ سازمان، از پالایشگاه‌ها تا معادن و سایت‌های ساختمانی، در کنار کسانی هستیم که ایمنی را یک خط قرمز می‌دانند."}]}],["$","div",null,{"className":"mt-10 space-y-1","children":[["$","$L29","۱۳۸۶",{"delay":0.25,"children":["$","div",null,{"className":"group border-s-2 border-accent/30 ps-6 transition-colors duration-300 hover:border-accent","children":[["$","span",null,{"className":"font-mono text-sm font-bold text-accent","children":"۱۳۸۶"}],["$","p",null,{"className":"mt-1 pb-5 text-sm leading-7 text-ink/75","children":"تأسیس آریا ایمن در تهران با تمرکز بر تجهیزات حفاظت فردی"}]]}]}],["$","$L29","۱۳۹۲",{"delay":0.32,"children":["$","div",null,{"className":"group border-s-2 border-accent/30 ps-6 transition-colors duration-300 hover:border-accent","children":[["$","span",null,{"className":"font-mono text-sm font-bold text-accent","children":"۱۳۹۲"}],["$","p",null,{"className":"mt-1 pb-5 text-sm leading-7 text-ink/75","children":"توسعه شبکه توزیع به تمام ۳۱ استان کشور"}]]}]}],["$","$L29","۱۳۹۸",{"delay":0.39,"children":["$","div",null,{"className":"group border-s-2 border-accent/30 ps-6 transition-colors duration-300 hover:border-accent","children":[["$","span",null,{"className":"font-mono text-sm font-bold text-accent","children":"۱۳۹۸"}],["$","p",null,{"className":"mt-1 pb-5 text-sm leading-7 text-ink/75","children":"راه‌اندازی آزمایشگاه کنترل کیفیت و تست تجهیزات"}]]}]}],["$","$L29","۱۴۰۲",{"delay":0.46,"children":["$","div",null,{"className":"group border-s-2 border-accent/30 ps-6 transition-colors duration-300 hover:border-accent","children":[["$","span",null,{"className":"font-mono text-sm font-bold text-accent","children":"۱۴۰۲"}],["$","p",null,{"className":"mt-1 pb-5 text-sm leading-7 text-ink/75","children":"افتتاح مرکز تخصصی آموزش و بازرسی HSE"}]]}]}]]}],["$","$L29",null,{"delay":0.5,"children":["$","div",null,{"className":"mt-4 flex flex-wrap gap-3","children":["$L2c","$L2d","$L2e"]}]}]]}]]}]}]]}]
13:["$","$L2f",null,{}]
14:["$","section",null,{"id":"standards","aria-labelledby":"standards-title","className":"relative overflow-hidden border-t border-line py-24 md:py-32","children":[["$","div",null,{"aria-hidden":true,"className":"text-outline pointer-events-none absolute -bottom-14 -start-8 select-none font-mono text-[24vw] font-bold leading-none opacity-60","children":"CE"}],["$","div",null,{"className":"relative mx-auto max-w-7xl px-5 md:px-8","children":["$","div",null,{"className":"grid items-center gap-14 lg:grid-cols-12","children":[["$","div",null,{"className":"lg:col-span-5","children":[["$","div",null,{"className":"","children":[["$","$L29",null,{"children":["$","div",null,{"className":"flex items-center gap-3 ","children":[["$","span",null,{"className":"font-mono text-sm text-accent","children":"//"}],["$","span",null,{"className":"font-mono text-xs tracking-[0.25em] text-muted","children":"۰۴ — استانداردها"}],["$","span",null,{"className":"h-px w-14 bg-line"}]]}]}],["$","$L29",null,{"delay":0.08,"children":["$","h2",null,{"id":"standards-title","className":"mt-5 text-3xl font-black leading-[1.25] md:text-5xl md:leading-[1.2]","children":["مهرتأیید جهانی",["$","br",null,{}],"روی هر محصول"]}]}],["$","$L29",null,{"delay":0.16,"children":["$","p",null,{"className":"mt-5 max-w-xl leading-8 text-muted ","children":"ما فقط تجهیزاتی را عرضه می‌کنیم که تأییدیه معتبر بین‌المللی و تست آزمایشگاهی داشته باشند؛ بدون استثنا."}]}]]}],["$","$L29",null,{"delay":0.25,"children":["$","div",null,{"className":"mt-8 flex items-start gap-4 rounded-xl border border-accent/25 bg-accent/5 p-5","children":[["$","svg",null,{"ref":"$undefined","xmlns":"http://www.w3.org/2000/svg","width":24,"height":24,"viewBox":"0 0 24 24","fill":"none","stroke":"currentColor","strokeWidth":2,"strokeLinecap":"round","strokeLinejoin":"round","className":"lucide lucide-file-search size-6 shrink-0 text-accent","children":[["$","path","tnqrlb",{"d":"M14 2v4a2 2 0 0 0 2 2h4"}],["$","path","ms7g94",{"d":"M4.268 21a2 2 0 0 0 1.727 1H18a2 2 0 0 0 2-2V7l-5-5H6a2 2 0 0 0-2 2v3"}],["$","path","1j6qii",{"d":"m9 18-1.5-1.5"}],["$","circle","ufru5t",{"cx":"5","cy":"14","r":"3"}],"$undefined"]}],["$","p",null,{"className":"text-sm leading-8 text-ink/80","children":"شماره سریال تمام محصولات در پایگاه داده استانداردهای اروپا قابل استعلام است و گواهی تست هر بچ تحویل مشتری می‌شود."}]]}]}],["$","$L29",null,{"delay":0.35,"children":["$","a",null,{"href":"#contact","className":"group mt-8 inline-flex items-center gap-2.5 rounded-full border border-line px-6 py-3.5 text-sm font-bold transition-colors hover:border-accent hover:text-accent","children":[["$","svg",null,{"ref":"$undefined","xmlns":"http://www.w3.org/2000/svg","width":24,"height":24,"viewBox":"0 0 24 24","fill":"none","stroke":"currentColor","strokeWidth":2,"strokeLinecap":"round","strokeLinejoin":"round","className":"lucide lucide-shield-check size-4 text-accent","children":[["$","path","oel41y",{"d":"M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z"}],["$","path","dzmm74",{"d":"m9 12 2 2 4-4"}],"$undefined"]}],"درخواست گواهی تست محصولات"]}]}]]}],["$","div",null,{"className":"grid gap-4 sm:grid-cols-2 lg:col-span-7 xl:grid-cols-3","children":[["$","$L29","EN 397",{"delay":0,"children":["$","div",null,{"data-hover":true,"className":"group relative h-full border border-line bg-panel/50 p-6 text-center transition-all duration-500 hover:-translate-y-1.5 hover:border-accent/50 hover:bg-panel","children":[["$","span",null,{"className":"absolute end-3 top-3 font-mono text-[9px] tracking-[0.2em] text-muted/50","children":"CERT"}],["$","div",null,{"className":"mx-auto flex size-24 items-center justify-center rounded-full border border-dashed border-ink/20 transition-all duration-500 group-hover:rotate-12 group-hover:border-accent/60","children":["$","div",null,{"children":[["$","div",null,{"className":"font-mono text-base font-bold text-ink transition-colors duration-300 group-hover:text-accent","children":"EN 397"}],["$","div",null,{"className":"mx-auto mt-1.5 h-px w-8 bg-line transition-colors duration-300 group-hover:bg-accent/50"}],"$L30"]}]}],"$L31"]}]}],"$L32","$L33","$L34","$L35","$L36"]}]]}]}]]}]
15:["$","$L37",null,{}]
16:["$","$L38",null,{}]
17:["$","$L39",null,{}]
18:["$","footer",null,{"className":"relative overflow-hidden border-t border-line","children":[["$","div",null,{"className":"hazard h-2.5"}],["$","div",null,{"className":"mx-auto max-w-7xl px-5 md:px-8","children":[["$","div",null,{"className":"grid gap-12 py-16 md:grid-cols-12 md:py-20","children":[["$","div",null,{"className":"md:col-span-5","children":[["$","a",null,{"href":"#home","className":"flex items-center gap-3","aria-label":"آریا ایمن","children":[["$","span",null,{"className":"flex size-10 items-center justify-center rounded-xl bg-accent text-bg shadow-[0_0_28px_rgba(255,90,0,0.35)]","children":["$","svg",null,{"ref":"$undefined","xmlns":"http://www.w3.org/2000/svg","width":24,"height":24,"viewBox":"0 0 24 24","fill":"none","stroke":"currentColor","strokeWidth":2,"strokeLinecap":"round","strokeLinejoin":"round","className":"lucide lucide-shield-check size-6","children":[["$","path","oel41y",{"d":"M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z"}],["$","path","dzmm74",{"d":"m9 12 2 2 4-4"}],"$undefined"]}]}],["$","span",null,{"className":"leading-tight","children":[["$","span",null,{"className":"block text-lg font-black","children":"آریا ایمن"}],["$","span",null,{"className":"block font-mono text-[9px] tracking-[0.32em] text-muted","children":"ARIA IMEN CO."}]]}]]}],["$","p",null,{"className":"mt-6 max-w-sm text-sm leading-8 text-muted","children":"تأمین‌کننده تخصصی تجهیزات حفاظت فردی و راهکارهای ایمنی صنعتی؛ همراه سازمان‌ها از سال ۱۳۸۶ در مسیر صفر شدن حوادث کاری."}],["$","div",null,{"className":"mt-7 flex gap-3","children":[["$","a","وب‌سایت",{"href":"#contact","aria-label":"وب‌سایت","className":"flex size-10 items-center justify-center rounded-lg border border-line text-muted transition-all duration-300 hover:border-accent hover:bg-accent hover:text-bg","children":["$","svg",null,{"ref":"$undefined","xmlns":"http://www.w3.org/2000/svg","width":24,"height":24,"viewBox":"0 0 24 24","fill":"none","stroke":"currentColor","strokeWidth":2,"strokeLinecap":"round","strokeLinejoin":"round","className":"lucide lucide-globe size-4","children":[["$","circle","1mglay",{"cx":"12","cy":"12","r":"10"}],["$","path","13o1zl",{"d":"M12 2a14.5 14.5 0 0 0 0 20 14.5 14.5 0 0 0 0-20"}],["$","path","9i4pu4",{"d":"M2 12h20"}],"$undefined"]}]}],["$","a","تلگرام",{"href":"#contact","aria-label":"تلگرام","className":"flex size-10 items-center justify-center rounded-lg border border-line text-muted transition-all duration-300 hover:border-accent hover:bg-accent hover:text-bg","children":["$","svg",null,{"ref":"$undefined","xmlns":"http://www.w3.org/2000/svg","width":24,"height":24,"viewBox":"0 0 24 24","fill":"none","stroke":"currentColor","strokeWidth":2,"strokeLinecap":"round","strokeLinejoin":"round","className":"lucide lucide-send size-4","children":[["$","path","1ffxy3",{"d":"M14.536 21.686a.5.5 0 0 0 .937-.024l6.5-19a.496.496 0 0 0-.635-.635l-19 6.5a.5.5 0 0 0-.024.937l7.93 3.18a2 2 0 0 1 1.112 1.11z"}],["$","path","12cjpa",{"d":"m21.854 2.147-10.94 10.939"}],"$undefined"]}]}],["$","a","ایمیل",{"href":"#contact","aria-label":"ایمیل","className":"flex size-10 items-center justify-center rounded-lg border border-line text-muted transition-all duration-300 hover:border-accent hover:bg-accent hover:text-bg","children":["$","svg",null,{"ref":"$undefined","xmlns":"http://www.w3.org/2000/svg","width":24,"height":24,"viewBox":"0 0 24 24","fill":"none","stroke":"currentColor","strokeWidth":2,"strokeLinecap":"round","strokeLinejoin":"round","className":"lucide lucide-at-sign size-4","children":[["$","circle","4exip2",{"cx":"12","cy":"12","r":"4"}],["$","path","7n84p3",{"d":"M16 8v5a3 3 0 0 0 6 0v-1a10 10 0 1 0-4 8"}],"$undefined"]}]}],["$","a","تماس",{"href":"#contact","aria-label":"تماس","className":"flex size-10 items-center justify-center rounded-lg border border-line text-muted transition-all duration-300 hover:border-accent hover:bg-accent hover:text-bg","children":["$","svg",null,{"ref":"$undefined","xmlns":"http://www.w3.org/2000/svg","width":24,"height":24,"viewBox":"0 0 24 24","fill":"none","stroke":"currentColor","strokeWidth":2,"strokeLinecap":"round","strokeLinejoin":"round","className":"lucide lucide-phone size-4","children":["$L3a","$undefined"]}]}]]}]]}],"$L3b","$L3c","$L3d"]}],"$L3e","$L3f"]}]]}]
19:["$","$L40",null,{"children":["$L41",["$","$L42",null,{"promise":"$@43"}]]}]
1a:["$","$1","h",{"children":[null,[["$","$L44",null,{"children":"$L45"}],null],["$","$L46",null,{"children":["$","div",null,{"hidden":true,"children":["$","$47",null,{"fallback":null,"children":"$L48"}]}]}]]}]
1c:["$","path","oel41y",{"d":"M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z"}]
1d:["$","path","dzmm74",{"d":"m9 12 2 2 4-4"}]
1e:["$","span","6",{"className":"flex items-center gap-7 px-7 text-sm font-extrabold whitespace-nowrap","children":["پشتیبانی ۲۴/۷ قراردادهای سازمانی",["$","svg",null,{"ref":"$undefined","xmlns":"http://www.w3.org/2000/svg","width":24,"height":24,"viewBox":"0 0 24 24","fill":"none","stroke":"currentColor","strokeWidth":2.5,"strokeLinecap":"round","strokeLinejoin":"round","className":"lucide lucide-shield-check size-4 shrink-0","children":[["$","path","oel41y",{"d":"M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z"}],["$","path","dzmm74",{"d":"m9 12 2 2 4-4"}],"$undefined"]}]]}]
1f:["$","span","7",{"className":"flex items-center gap-7 px-7 text-sm font-extrabold whitespace-nowrap","children":["تست نمونه قبل از خرید",["$","svg",null,{"ref":"$undefined","xmlns":"http://www.w3.org/2000/svg","width":24,"height":24,"viewBox":"0 0 24 24","fill":"none","stroke":"currentColor","strokeWidth":2.5,"strokeLinecap":"round","strokeLinejoin":"round","className":"lucide lucide-shield-check size-4 shrink-0","children":[["$","path","oel41y",{"d":"M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z"}],["$","path","dzmm74",{"d":"m9 12 2 2 4-4"}],"$undefined"]}]]}]
20:["$","span","8",{"className":"flex items-center gap-7 px-7 text-sm font-extrabold whitespace-nowrap","children":["ISO 9001:2015",["$","svg",null,{"ref":"$undefined","xmlns":"http://www.w3.org/2000/svg","width":24,"height":24,"viewBox":"0 0 24 24","fill":"none","stroke":"currentColor","strokeWidth":2.5,"strokeLinecap":"round","strokeLinejoin":"round","className":"lucide lucide-shield-check size-4 shrink-0","children":[["$","path","oel41y",{"d":"M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z"}],["$","path","dzmm74",{"d":"m9 12 2 2 4-4"}],"$undefined"]}]]}]
21:["$","span","9",{"className":"flex items-center gap-7 px-7 text-sm font-extrabold whitespace-nowrap","children":["CE CERTIFIED",["$","svg",null,{"ref":"$undefined","xmlns":"http://www.w3.org/2000/svg","width":24,"height":24,"viewBox":"0 0 24 24","fill":"none","stroke":"currentColor","strokeWidth":2.5,"strokeLinecap":"round","strokeLinejoin":"round","className":"lucide lucide-shield-check size-4 shrink-0","children":[["$","path","oel41y",{"d":"M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z"}],["$","path","dzmm74",{"d":"m9 12 2 2 4-4"}],"$undefined"]}]]}]
22:["$","span","10",{"className":"flex items-center gap-7 px-7 text-sm font-extrabold whitespace-nowrap","children":["گارانتی اصالت کالا",["$","svg",null,{"ref":"$undefined","xmlns":"http://www.w3.org/2000/svg","width":24,"height":24,"viewBox":"0 0 24 24","fill":"none","stroke":"currentColor","strokeWidth":2.5,"strokeLinecap":"round","strokeLinejoin":"round","className":"lucide lucide-shield-check size-4 shrink-0","children":[["$","path","oel41y",{"d":"M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z"}],["$","path","dzmm74",{"d":"m9 12 2 2 4-4"}],"$undefined"]}]]}]
23:["$","span","11",{"className":"flex items-center gap-7 px-7 text-sm font-extrabold whitespace-nowrap","children":["تحویل سراسری کشوری",["$","svg",null,{"ref":"$undefined","xmlns":"http://www.w3.org/2000/svg","width":24,"height":24,"viewBox":"0 0 24 24","fill":"none","stroke":"currentColor","strokeWidth":2.5,"strokeLinecap":"round","strokeLinejoin":"round","className":"lucide lucide-shield-check size-4 shrink-0","children":[["$","path","oel41y",{"d":"M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z"}],["$","path","dzmm74",{"d":"m9 12 2 2 4-4"}],"$undefined"]}]]}]
24:["$","span","12",{"className":"flex items-center gap-7 px-7 text-sm font-extrabold whitespace-nowrap","children":["مشاوره و بازرسی تخصصی رایگان",["$","svg",null,{"ref":"$undefined","xmlns":"http://www.w3.org/2000/svg","width":24,"height":24,"viewBox":"0 0 24 24","fill":"none","stroke":"currentColor","strokeWidth":2.5,"strokeLinecap":"round","strokeLinejoin":"round","className":"lucide lucide-shield-check size-4 shrink-0","children":[["$","path","oel41y",{"d":"M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z"}],["$","path","dzmm74",{"d":"m9 12 2 2 4-4"}],"$undefined"]}]]}]
25:["$","span","13",{"className":"flex items-center gap-7 px-7 text-sm font-extrabold whitespace-nowrap","children":["EN STANDARDS",["$","svg",null,{"ref":"$undefined","xmlns":"http://www.w3.org/2000/svg","width":24,"height":24,"viewBox":"0 0 24 24","fill":"none","stroke":"currentColor","strokeWidth":2.5,"strokeLinecap":"round","strokeLinejoin":"round","className":"lucide lucide-shield-check size-4 shrink-0","children":[["$","path","oel41y",{"d":"M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z"}],["$","path","dzmm74",{"d":"m9 12 2 2 4-4"}],"$undefined"]}]]}]
26:["$","span","14",{"className":"flex items-center gap-7 px-7 text-sm font-extrabold whitespace-nowrap","children":["پشتیبانی ۲۴/۷ قراردادهای سازمانی",["$","svg",null,{"ref":"$undefined","xmlns":"http://www.w3.org/2000/svg","width":24,"height":24,"viewBox":"0 0 24 24","fill":"none","stroke":"currentColor","strokeWidth":2.5,"strokeLinecap":"round","strokeLinejoin":"round","className":"lucide lucide-shield-check size-4 shrink-0","children":[["$","path","oel41y",{"d":"M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z"}],["$","path","dzmm74",{"d":"m9 12 2 2 4-4"}],"$undefined"]}]]}]
27:["$","span","15",{"className":"flex items-center gap-7 px-7 text-sm font-extrabold whitespace-nowrap","children":["تست نمونه قبل از خرید",["$","svg",null,{"ref":"$undefined","xmlns":"http://www.w3.org/2000/svg","width":24,"height":24,"viewBox":"0 0 24 24","fill":"none","stroke":"currentColor","strokeWidth":2.5,"strokeLinecap":"round","strokeLinejoin":"round","className":"lucide lucide-shield-check size-4 shrink-0","children":[["$","path","oel41y",{"d":"M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z"}],["$","path","dzmm74",{"d":"m9 12 2 2 4-4"}],"$undefined"]}]]}]
28:["$","div",null,{"className":"hazard h-2.5"}]
2c:["$","span","نماینده رسمی ۴۰+ برند جهانی",{"className":"inline-flex items-center gap-2 rounded-full border border-line bg-panel/60 px-4 py-2 text-xs text-ink/80 transition-colors hover:border-accent/50 hover:text-ink","children":[["$","svg",null,{"ref":"$undefined","xmlns":"http://www.w3.org/2000/svg","width":24,"height":24,"viewBox":"0 0 24 24","fill":"none","stroke":"currentColor","strokeWidth":2,"strokeLinecap":"round","strokeLinejoin":"round","className":"lucide lucide-globe size-4 text-accent","children":[["$","circle","1mglay",{"cx":"12","cy":"12","r":"10"}],["$","path","13o1zl",{"d":"M12 2a14.5 14.5 0 0 0 0 20 14.5 14.5 0 0 0 0-20"}],["$","path","9i4pu4",{"d":"M2 12h20"}],"$undefined"]}],"نماینده رسمی ۴۰+ برند جهانی"]}]
2d:["$","span","تیم مهندسی و بازرسی HSE",{"className":"inline-flex items-center gap-2 rounded-full border border-line bg-panel/60 px-4 py-2 text-xs text-ink/80 transition-colors hover:border-accent/50 hover:text-ink","children":[["$","svg",null,{"ref":"$undefined","xmlns":"http://www.w3.org/2000/svg","width":24,"height":24,"viewBox":"0 0 24 24","fill":"none","stroke":"currentColor","strokeWidth":2,"strokeLinecap":"round","strokeLinejoin":"round","className":"lucide lucide-users size-4 text-accent","children":[["$","path","1yyitq",{"d":"M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2"}],["$","circle","nufk8",{"cx":"9","cy":"7","r":"4"}],["$","path","kshegd",{"d":"M22 21v-2a4 4 0 0 0-3-3.87"}],["$","path","1da9ce",{"d":"M16 3.13a4 4 0 0 1 0 7.75"}],"$undefined"]}],"تیم مهندسی و بازرسی HSE"]}]
2e:["$","span","پشتیبانی ۲۴/۷ قراردادها",{"className":"inline-flex items-center gap-2 rounded-full border border-line bg-panel/60 px-4 py-2 text-xs text-ink/80 transition-colors hover:border-accent/50 hover:text-ink","children":[["$","svg",null,{"ref":"$undefined","xmlns":"http://www.w3.org/2000/svg","width":24,"height":24,"viewBox":"0 0 24 24","fill":"none","stroke":"currentColor","strokeWidth":2,"strokeLinecap":"round","strokeLinejoin":"round","className":"lucide lucide-headset size-4 text-accent","children":[["$","path","12oyoe",{"d":"M3 11h3a2 2 0 0 1 2 2v3a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-5Zm0 0a9 9 0 1 1 18 0m0 0v5a2 2 0 0 1-2 2h-1a2 2 0 0 1-2-2v-3a2 2 0 0 1 2-2h3Z"}],["$","path","1x7m43",{"d":"M21 16v2a4 4 0 0 1-4 4h-5"}],"$undefined"]}],"پشتیبانی ۲۴/۷ قراردادها"]}]
30:["$","div",null,{"className":"mt-1.5 font-mono text-[8px] tracking-[0.25em] text-muted","children":"EURO NORM"}]
31:["$","div",null,{"className":"mt-4 text-sm font-medium text-ink/85","children":"کلاه ایمنی صنعتی"}]
32:["$","$L29","EN 388",{"delay":0.06,"children":["$","div",null,{"data-hover":true,"className":"group relative h-full border border-line bg-panel/50 p-6 text-center transition-all duration-500 hover:-translate-y-1.5 hover:border-accent/50 hover:bg-panel","children":[["$","span",null,{"className":"absolute end-3 top-3 font-mono text-[9px] tracking-[0.2em] text-muted/50","children":"CERT"}],["$","div",null,{"className":"mx-auto flex size-24 items-center justify-center rounded-full border border-dashed border-ink/20 transition-all duration-500 group-hover:rotate-12 group-hover:border-accent/60","children":["$","div",null,{"children":[["$","div",null,{"className":"font-mono text-base font-bold text-ink transition-colors duration-300 group-hover:text-accent","children":"EN 388"}],["$","div",null,{"className":"mx-auto mt-1.5 h-px w-8 bg-line transition-colors duration-300 group-hover:bg-accent/50"}],["$","div",null,{"className":"mt-1.5 font-mono text-[8px] tracking-[0.25em] text-muted","children":"EURO NORM"}]]}]}],["$","div",null,{"className":"mt-4 text-sm font-medium text-ink/85","children":"دستکش‌های مکانیکی"}]]}]}]
33:["$","$L29","EN 166",{"delay":0.12,"children":["$","div",null,{"data-hover":true,"className":"group relative h-full border border-line bg-panel/50 p-6 text-center transition-all duration-500 hover:-translate-y-1.5 hover:border-accent/50 hover:bg-panel","children":[["$","span",null,{"className":"absolute end-3 top-3 font-mono text-[9px] tracking-[0.2em] text-muted/50","children":"CERT"}],["$","div",null,{"className":"mx-auto flex size-24 items-center justify-center rounded-full border border-dashed border-ink/20 transition-all duration-500 group-hover:rotate-12 group-hover:border-accent/60","children":["$","div",null,{"children":[["$","div",null,{"className":"font-mono text-base font-bold text-ink transition-colors duration-300 group-hover:text-accent","children":"EN 166"}],["$","div",null,{"className":"mx-auto mt-1.5 h-px w-8 bg-line transition-colors duration-300 group-hover:bg-accent/50"}],["$","div",null,{"className":"mt-1.5 font-mono text-[8px] tracking-[0.25em] text-muted","children":"EURO NORM"}]]}]}],["$","div",null,{"className":"mt-4 text-sm font-medium text-ink/85","children":"محافظ چشم و صورت"}]]}]}]
34:["$","$L29","EN 361",{"delay":0.18,"children":["$","div",null,{"data-hover":true,"className":"group relative h-full border border-line bg-panel/50 p-6 text-center transition-all duration-500 hover:-translate-y-1.5 hover:border-accent/50 hover:bg-panel","children":[["$","span",null,{"className":"absolute end-3 top-3 font-mono text-[9px] tracking-[0.2em] text-muted/50","children":"CERT"}],["$","div",null,{"className":"mx-auto flex size-24 items-center justify-center rounded-full border border-dashed border-ink/20 transition-all duration-500 group-hover:rotate-12 group-hover:border-accent/60","children":["$","div",null,{"children":[["$","div",null,{"className":"font-mono text-base font-bold text-ink transition-colors duration-300 group-hover:text-accent","children":"EN 361"}],["$","div",null,{"className":"mx-auto mt-1.5 h-px w-8 bg-line transition-colors duration-300 group-hover:bg-accent/50"}],["$","div",null,{"className":"mt-1.5 font-mono text-[8px] tracking-[0.25em] text-muted","children":"EURO NORM"}]]}]}],["$","div",null,{"className":"mt-4 text-sm font-medium text-ink/85","children":"هارنس ضدسقوط"}]]}]}]
35:["$","$L29","ISO 20345",{"delay":0.24,"children":["$","div",null,{"data-hover":true,"className":"group relative h-full border border-line bg-panel/50 p-6 text-center transition-all duration-500 hover:-translate-y-1.5 hover:border-accent/50 hover:bg-panel","children":[["$","span",null,{"className":"absolute end-3 top-3 font-mono text-[9px] tracking-[0.2em] text-muted/50","children":"CERT"}],["$","div",null,{"className":"mx-auto flex size-24 items-center justify-center rounded-full border border-dashed border-ink/20 transition-all duration-500 group-hover:rotate-12 group-hover:border-accent/60","children":["$","div",null,{"children":[["$","div",null,{"className":"font-mono text-base font-bold text-ink transition-colors duration-300 group-hover:text-accent","children":"ISO 20345"}],["$","div",null,{"className":"mx-auto mt-1.5 h-px w-8 bg-line transition-colors duration-300 group-hover:bg-accent/50"}],["$","div",null,{"className":"mt-1.5 font-mono text-[8px] tracking-[0.25em] text-muted","children":"EURO NORM"}]]}]}],["$","div",null,{"className":"mt-4 text-sm font-medium text-ink/85","children":"کفش ایمنی"}]]}]}]
36:["$","$L29","ISO 9001",{"delay":0.3,"children":["$","div",null,{"data-hover":true,"className":"group relative h-full border border-line bg-panel/50 p-6 text-center transition-all duration-500 hover:-translate-y-1.5 hover:border-accent/50 hover:bg-panel","children":[["$","span",null,{"className":"absolute end-3 top-3 font-mono text-[9px] tracking-[0.2em] text-muted/50","children":"CERT"}],["$","div",null,{"className":"mx-auto flex size-24 items-center justify-center rounded-full border border-dashed border-ink/20 transition-all duration-500 group-hover:rotate-12 group-hover:border-accent/60","children":["$","div",null,{"children":[["$","div",null,{"className":"font-mono text-base font-bold text-ink transition-colors duration-300 group-hover:text-accent","children":"ISO 9001"}],["$","div",null,{"className":"mx-auto mt-1.5 h-px w-8 bg-line transition-colors duration-300 group-hover:bg-accent/50"}],["$","div",null,{"className":"mt-1.5 font-mono text-[8px] tracking-[0.25em] text-muted","children":"EURO NORM"}]]}]}],["$","div",null,{"className":"mt-4 text-sm font-medium text-ink/85","children":"سیستم مدیریت کیفیت"}]]}]}]
3a:["$","path","foiqr5",{"d":"M22 16.92v3a2 2 0 0 1-2.18 2 19.79 19.79 0 0 1-8.63-3.07 19.5 19.5 0 0 1-6-6 19.79 19.79 0 0 1-3.07-8.67A2 2 0 0 1 4.11 2h3a2 2 0 0 1 2 1.72 12.84 12.84 0 0 0 .7 2.81 2 2 0 0 1-.45 2.11L8.09 9.91a16 16 0 0 0 6 6l1.27-1.27a2 2 0 0 1 2.11-.45 12.84 12.84 0 0 0 2.81.7A2 2 0 0 1 22 16.92z"}]
3b:["$","div",null,{"className":"md:col-span-2","children":[["$","div",null,{"className":"font-mono text-xs tracking-[0.25em] text-accent","children":"// دسترسی"}],["$","ul",null,{"className":"mt-5 space-y-3","children":[["$","li","#home",{"children":["$","a",null,{"href":"#home","className":"text-sm text-muted transition-colors hover:text-ink","children":"خانه"}]}],["$","li","#products",{"children":["$","a",null,{"href":"#products","className":"text-sm text-muted transition-colors hover:text-ink","children":"محصولات"}]}],["$","li","#about",{"children":["$","a",null,{"href":"#about","className":"text-sm text-muted transition-colors hover:text-ink","children":"درباره ما"}]}],["$","li","#services",{"children":["$","a",null,{"href":"#services","className":"text-sm text-muted transition-colors hover:text-ink","children":"خدمات"}]}],["$","li","#standards",{"children":["$","a",null,{"href":"#standards","className":"text-sm text-muted transition-colors hover:text-ink","children":"استانداردها"}]}],["$","li","#industries",{"children":["$","a",null,{"href":"#industries","className":"text-sm text-muted transition-colors hover:text-ink","children":"صنایع"}]}]]}]]}]
3c:["$","div",null,{"className":"md:col-span-2","children":[["$","div",null,{"className":"font-mono text-xs tracking-[0.25em] text-accent","children":"// محصولات"}],["$","ul",null,{"className":"mt-5 space-y-3","children":[["$","li","کلاه ایمنی",{"children":["$","a",null,{"href":"#products","className":"text-sm text-muted transition-colors hover:text-ink","children":"کلاه ایمنی"}]}],["$","li","دستکش ضدبرش",{"children":["$","a",null,{"href":"#products","className":"text-sm text-muted transition-colors hover:text-ink","children":"دستکش ضدبرش"}]}],["$","li","کفش ایمنی",{"children":["$","a",null,{"href":"#products","className":"text-sm text-muted transition-colors hover:text-ink","children":"کفش ایمنی"}]}],["$","li","عینک ایمنی",{"children":["$","a",null,{"href":"#products","className":"text-sm text-muted transition-colors hover:text-ink","children":"عینک ایمنی"}]}],["$","li","هارنس ضدسقوط",{"children":["$","a",null,{"href":"#products","className":"text-sm text-muted transition-colors hover:text-ink","children":"هارنس ضدسقوط"}]}],["$","li","لباس کار بازتابنده",{"children":["$","a",null,{"href":"#products","className":"text-sm text-muted transition-colors hover:text-ink","children":"لباس کار بازتابنده"}]}]]}]]}]
3d:["$","div",null,{"className":"md:col-span-3","children":[["$","div",null,{"className":"font-mono text-xs tracking-[0.25em] text-accent","children":"// ارتباط"}],["$","address",null,{"className":"mt-5 space-y-3 text-sm text-muted not-italic","children":[["$","p",null,{"className":"leading-7","children":["تهران، خیابان کارگر شمالی،",["$","br",null,{}],"خیابان فر، کوچه دوم، پلاک ۱۲"]}],["$","p",null,{"className":"font-mono tracking-wider","dir":"ltr","children":"۰۲۱ - ۹۱ ۰۰ ۸۸ ۰۰"}],["$","p",null,{"className":"font-mono tracking-wider","dir":"ltr","children":"info@ariaiman.ir"}],["$","p",null,{"className":"pt-2 text-xs leading-6","children":["شنبه تا پنجشنبه — ۸:۰۰ تا ۱۷:۰۰",["$","br",null,{}],"پشتیبانی اضطراری: ۲۴/۷"]}]]}]]}]
3e:["$","div",null,{"aria-hidden":true,"className":"pointer-events-none select-none pb-6","children":["$","div",null,{"className":"text-outline text-center text-[17vw] font-black leading-[0.85] md:text-[11vw]","children":"آریا ایمن"}]}]
3f:["$","div",null,{"className":"flex flex-wrap items-center justify-between gap-4 border-t border-line py-6 text-xs text-muted","children":[["$","div",null,{"children":"© ۱۴۰۴ آریا ایمن — تمامی حقوق محفوظ است."}],["$","div",null,{"className":"font-mono text-[10px] tracking-[0.25em]","children":"ISO 9001:2015 — CE CERTIFIED PPE SUPPLIER"}]]}]
45:[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}],["$","meta","2",{"name":"theme-color","content":"#0a0a0c"}]]
41:null
49:I[622,[],"IconMark"]
43:{"metadata":[["$","title","0",{"children":"آریا ایمن | تجهیزات ایمنی و حفاظت فردی صنعتی"}],["$","meta","1",{"name":"description","content":"آریا ایمن — تأمین‌کننده تخصصی تجهیزات حفاظت فردی (PPE)، بازرسی، آموزش و راهکارهای ایمنی صنعتی برای سازمان‌هایی که جان انسان‌ها را جدی می‌گیرند."}],["$","link","2",{"rel":"manifest","href":"/manifest.webmanifest","crossOrigin":"$undefined"}],["$","meta","3",{"name":"robots","content":"index, follow"}],["$","link","4",{"rel":"canonical","href":"https://ariaiman.ir"}],["$","meta","5",{"name":"format-detection","content":"telephone=no"}],["$","meta","6",{"property":"og:title","content":"آریا ایمن | تجهیزات ایمنی و حفاظت فردی صنعتی"}],["$","meta","7",{"property":"og:description","content":"آریا ایمن — تأمین‌کننده تخصصی تجهیزات حفاظت فردی (PPE)، بازرسی، آموزش و راهکارهای ایمنی صنعتی برای سازمان‌هایی که جان انسان‌ها را جدی می‌گیرند."}],["$","meta","8",{"property":"og:url","content":"https://ariaiman.ir"}],["$","meta","9",{"property":"og:site_name","content":"آریا ایمن"}],["$","meta","10",{"property":"og:locale","content":"fa_IR"}],["$","meta","11",{"property":"og:type","content":"website"}],["$","meta","12",{"name":"twitter:card","content":"summary_large_image"}],["$","meta","13",{"name":"twitter:title","content":"آریا ایمن | تجهیزات ایمنی و حفاظت فردی صنعتی"}],["$","meta","14",{"name":"twitter:description","content":"آریا ایمن — تأمین‌کننده تخصصی تجهیزات حفاظت فردی (PPE)، بازرسی، آموزش و راهکارهای ایمنی صنعتی برای سازمان‌هایی که جان انسان‌ها را جدی می‌گیرند."}],["$","link","15",{"rel":"icon","href":"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'%3E%3Crect width='100' height='100' rx='22' fill='%23FF5A00'/%3E%3Cpath d='M50 20 L72 28 V48 C72 66 62 76 50 82 C38 76 28 66 28 48 V28 Z' fill='%230A0A0C'/%3E%3Cpath d='M40 50 L47 57 L62 42' stroke='%23FF5A00' stroke-width='7' fill='none' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E"}],["$","$L49","16",{}]],"error":null,"digest":"$undefined"}
48:"$43:metadata"
-1
View File
@@ -1 +0,0 @@
{"name":"آریا ایمن","short_name":"آریا ایمن","lang":"fa","dir":"rtl","start_url":"/","display":"standalone","background_color":"#0a0a0c","theme_color":"#0a0a0c","icons":[]}
-5
View File
@@ -1,5 +0,0 @@
User-Agent: *
Allow: /
Host: https://ariaiman.ir
Sitemap: https://ariaiman.ir/sitemap.xml
-9
View File
@@ -1,9 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<url>
<loc>https://ariaiman.ir/</loc>
<lastmod>2026-09-27T05:23:55.892Z</lastmod>
<changefreq>monthly</changefreq>
<priority>1</priority>
</url>
</urlset>
+9617 -978
View File
File diff suppressed because it is too large Load Diff
+20 -16
View File
@@ -1,27 +1,31 @@
{
"name": "aria-imen-landing",
"private": true,
"version": "0.1.0",
"version": "0.2.0",
"type": "module",
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "next lint"
"dev": "nuxt dev",
"build": "nuxt generate",
"preview": "npx serve out",
"typecheck": "nuxt typecheck",
"postinstall": "nuxt prepare"
},
"dependencies": {
"framer-motion": "^13.2.0",
"lucide-react": "^0.475.0",
"next": "15.5.25",
"react": "19.0.0",
"react-dom": "19.0.0"
"@pinia/nuxt": "^1.0.2",
"@vueuse/core": "^15.0.0",
"ant-design-vue": "^4.2.6",
"lucide-vue-next": "^0.475.0",
"motion-v": "^2.6.0",
"nuxt": "^4.5.2",
"pinia": "^4.0.3",
"vue": "^3.5.43",
"vue-router": "^5.3.1"
},
"devDependencies": {
"@tailwindcss/postcss": "4.1.17",
"@types/node": "22.19.17",
"@types/react": "19.0.8",
"@types/react-dom": "19.0.3",
"tailwindcss": "4.1.17",
"typescript": "5.9.3"
"@tailwindcss/vite": "^4.1.17",
"@types/node": "^22.20.5",
"tailwindcss": "^4.1.17",
"typescript": "^5.9.3",
"vue-tsc": "^3.3.12"
}
}
-7
View File
@@ -1,7 +0,0 @@
const config = {
plugins: {
"@tailwindcss/postcss": {},
},
};
export default config;
+1
View File
@@ -0,0 +1 @@
<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><rect width='100' height='100' rx='22' fill='#FF5A00'/><path d='M50 20 L72 28 V48 C72 66 62 76 50 82 C38 76 28 66 28 48 V28 Z' fill='#0A0A0C'/><path d='M40 50 L47 57 L62 42' stroke='#FF5A00' stroke-width='7' fill='none' stroke-linecap='round' stroke-linejoin='round'/></svg>

After

Width:  |  Height:  |  Size: 337 B

+23
View File
@@ -0,0 +1,23 @@
import { siteConfig } from "../../shared/config/site";
export default defineEventHandler((event) => {
setHeader(event, "Content-Type", "application/manifest+json; charset=utf-8");
return {
name: siteConfig.name,
short_name: siteConfig.name,
lang: siteConfig.lang,
dir: siteConfig.dir,
start_url: "/",
display: "standalone",
background_color: siteConfig.themeColor,
theme_color: siteConfig.themeColor,
icons: [
{
src: siteConfig.favicon,
sizes: "any",
type: "image/svg+xml",
purpose: "any",
},
],
};
});
+13
View File
@@ -0,0 +1,13 @@
import { siteConfig } from "../../shared/config/site";
export default defineEventHandler((event) => {
setHeader(event, "Content-Type", "text/plain; charset=utf-8");
return [
"User-Agent: *",
"Allow: /",
"",
`Host: ${siteConfig.url}`,
`Sitemap: ${siteConfig.url}/sitemap.xml`,
"",
].join("\n");
});
+15
View File
@@ -0,0 +1,15 @@
import { absUrl } from "../../shared/config/site";
const pages = ["/"];
export default defineEventHandler((event) => {
setHeader(event, "Content-Type", "application/xml; charset=utf-8");
const lastmod = new Date().toISOString();
const urls = pages
.map(
(p) =>
`<url>\n<loc>${absUrl(p)}</loc>\n<lastmod>${lastmod}</lastmod>\n<changefreq>monthly</changefreq>\n<priority>1</priority>\n</url>`,
)
.join("\n");
return `<?xml version="1.0" encoding="UTF-8"?>\n<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">\n${urls}\n</urlset>\n`;
});
+18 -1
View File
@@ -8,9 +8,23 @@ export const siteConfig = {
title: "آریا ایمن | تجهیزات ایمنی و حفاظت فردی صنعتی",
description:
"آریا ایمن — تأمین‌کننده تخصصی تجهیزات حفاظت فردی (PPE)، بازرسی، آموزش و راهکارهای ایمنی صنعتی برای سازمان‌هایی که جان انسان‌ها را جدی می‌گیرند.",
ogImage: "/opengraph-image.png",
// TODO: replace with a dedicated 1200×630 social image when available
ogImage: {
src: "/images/hero.jpg",
width: 1792,
height: 1008,
alt: "کارگر صنعتی با تجهیزات ایمنی کامل",
},
logo: "/images/hero.jpg", // TODO: replace with a dedicated PNG/SVG logo file when available
favicon: "/favicon.svg",
themeColor: "#0a0a0c",
foundingDate: "2007",
areaServed: "IR",
openingHours: {
days: ["Saturday", "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday"],
opens: "08:00",
closes: "17:00",
},
contact: {
phone: "+98-21-91008800",
phoneDisplay: "۰۲۱ - ۹۱ ۰۰ ۸۸ ۰۰",
@@ -23,3 +37,6 @@ export const siteConfig = {
},
socials: [] as string[], // TODO: add real social profile URLs when available
};
/** Absolute URL on the production domain. "/" → "https://domain/" (matches the sitemap entry). */
export const absUrl = (p: string) => new URL(p, siteConfig.url).toString();
+21 -6
View File
@@ -1,7 +1,8 @@
import { siteConfig as s } from "@/config/site";
import { absUrl as abs, siteConfig as s } from "../config/site";
const abs = (p: string) => new URL(p, s.url).toString();
const clean = <T extends object>(o: T) =>
type Json = Record<string, unknown>;
const clean = <T extends object>(o: T): Json =>
JSON.parse(
JSON.stringify(o, (_, v) =>
v === "" || (Array.isArray(v) && !v.length) ? undefined : v,
@@ -14,17 +15,27 @@ export const organizationSchema = () =>
"@type": "LocalBusiness",
"@id": `${s.url}/#organization`,
name: s.name,
url: s.url,
alternateName: s.nameEn,
url: abs("/"),
logo: abs(s.logo),
image: abs(s.logo),
description: s.description,
email: s.contact.email,
telephone: s.contact.phone,
foundingDate: s.foundingDate,
areaServed: s.areaServed,
address: {
"@type": "PostalAddress",
streetAddress: s.contact.address.streetAddress,
addressLocality: s.contact.address.addressLocality,
addressCountry: s.contact.address.addressCountry,
},
openingHoursSpecification: {
"@type": "OpeningHoursSpecification",
dayOfWeek: s.openingHours.days,
opens: s.openingHours.opens,
closes: s.openingHours.closes,
},
sameAs: s.socials,
});
@@ -32,7 +43,7 @@ export const websiteSchema = () => ({
"@context": "https://schema.org",
"@type": "WebSite",
"@id": `${s.url}/#website`,
url: s.url,
url: abs("/"),
name: s.name,
inLanguage: s.lang,
publisher: { "@id": `${s.url}/#organization` },
@@ -48,6 +59,10 @@ export const serviceSchema = (opts: {
"@type": "Service",
name: opts.name,
description: opts.description,
areaServed: opts.areaServed,
areaServed: opts.areaServed ?? s.areaServed,
provider: { "@id": `${s.url}/#organization` },
});
/** Serialize JSON-LD safely for an inline <script> (escape "<"). */
export const toJsonLd = (data: Json) =>
JSON.stringify(data).replace(/</g, "\\u003c");
-91
View File
@@ -1,91 +0,0 @@
import type { Metadata, Viewport } from "next";
import localFont from "next/font/local";
import { siteConfig } from "@/config/site";
import { JsonLd } from "@/components/seo/JsonLd";
import { organizationSchema, websiteSchema } from "@/lib/seo/schema";
import "@/styles/main.css";
const vazirmatn = localFont({
src: "../../public/fonts/Vazirmatn-Variable.woff2",
weight: "100 900",
display: "swap",
variable: "--font-vazirmatn",
fallback: ["Tahoma", "system-ui", "sans-serif"],
});
const ibmPlexMono = localFont({
src: [
{
path: "../../public/fonts/IBMPlexMono-Regular.woff2",
weight: "400",
style: "normal",
},
{
path: "../../public/fonts/IBMPlexMono-Medium.woff2",
weight: "500",
style: "normal",
},
{
path: "../../public/fonts/IBMPlexMono-SemiBold.woff2",
weight: "600",
style: "normal",
},
],
display: "swap",
variable: "--font-ibm-plex-mono",
fallback: ["ui-monospace", "monospace"],
});
export const metadata: Metadata = {
metadataBase: new URL(siteConfig.url),
title: {
default: siteConfig.title,
template: `%s | ${siteConfig.name}`,
},
description: siteConfig.description,
alternates: { canonical: "/" },
openGraph: {
type: "website",
url: "/",
siteName: siteConfig.name,
locale: siteConfig.locale,
title: siteConfig.title,
description: siteConfig.description,
},
twitter: {
card: "summary_large_image",
title: siteConfig.title,
description: siteConfig.description,
},
robots: { index: true, follow: true },
formatDetection: { telephone: false },
icons: {
icon:
"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'%3E%3Crect width='100' height='100' rx='22' fill='%23FF5A00'/%3E%3Cpath d='M50 20 L72 28 V48 C72 66 62 76 50 82 C38 76 28 66 28 48 V28 Z' fill='%230A0A0C'/%3E%3Cpath d='M40 50 L47 57 L62 42' stroke='%23FF5A00' stroke-width='7' fill='none' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E",
},
};
export const viewport: Viewport = {
themeColor: siteConfig.themeColor,
width: "device-width",
initialScale: 1,
};
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html
lang={siteConfig.lang}
dir={siteConfig.dir}
className={`${vazirmatn.variable} ${ibmPlexMono.variable}`}
>
<body className="relative min-h-screen bg-bg font-sans text-ink antialiased">
<JsonLd data={[organizationSchema(), websiteSchema()]} />
{children}
</body>
</html>
);
}

Some files were not shown because too many files have changed in this diff Show More