Spaces:
Running
Running
Upload 35 files
#198
by
archjayte
- opened
- .astro/content-assets.mjs +1 -0
- .astro/content-modules.mjs +1 -0
- .astro/data-store.json +1 -0
- .astro/settings.json +5 -0
- .astro/types.d.ts +1 -0
- .bolt/config.json +3 -0
- .codesandbox/Dockerfile +1 -0
- .gitignore +15 -32
- .vscode/extensions.json +4 -0
- .vscode/launch.json +11 -0
- README.md +48 -22
- astro.config.mjs +30 -0
- package-lock.json +0 -0
- package.json +16 -58
- public/4b1c9cde-efca-46d2-9e6a-77f89da9b914 copy.JPG +0 -0
- public/favicon.svg +9 -0
- public/logo.jpg +0 -0
- public/picsvg_download.webm +0 -0
- src/components/About.astro +134 -0
- src/components/Contact.astro +169 -0
- src/components/Footer.astro +175 -0
- src/components/Header.astro +142 -0
- src/components/Hero.astro +89 -0
- src/components/LanguageSelector.astro +74 -0
- src/components/Services.astro +133 -0
- src/components/ThemeToggle.astro +74 -0
- src/components/Workflow.astro +226 -0
- src/i18n/ui.ts +265 -0
- src/layouts/Layout.astro +142 -0
- src/pages/[...lang]/index.astro +35 -0
- src/pages/en/index.astro +29 -0
- src/pages/index.astro +34 -0
- src/styles/global.css +159 -0
- tailwind.config.mjs +45 -0
- tsconfig.json +3 -25
.astro/content-assets.mjs
ADDED
@@ -0,0 +1 @@
|
|
|
|
|
1 |
+
export default new Map();
|
.astro/content-modules.mjs
ADDED
@@ -0,0 +1 @@
|
|
|
|
|
1 |
+
export default new Map();
|
.astro/data-store.json
ADDED
@@ -0,0 +1 @@
|
|
|
|
|
1 |
+
[["Map",1,2],"meta::meta",["Map",3,4,5,6],"astro-version","5.10.1","astro-config-digest","{\"root\":{},\"srcDir\":{},\"publicDir\":{},\"outDir\":{},\"cacheDir\":{},\"site\":\"https://blueprintrak.com\",\"compressHTML\":true,\"base\":\"/\",\"trailingSlash\":\"ignore\",\"output\":\"static\",\"scopedStyleStrategy\":\"attribute\",\"build\":{\"format\":\"directory\",\"client\":{},\"server\":{},\"assets\":\"_astro\",\"serverEntry\":\"entry.mjs\",\"redirects\":true,\"inlineStylesheets\":\"auto\",\"concurrency\":1},\"server\":{\"open\":false,\"host\":false,\"port\":4321,\"streaming\":true,\"allowedHosts\":[]},\"redirects\":{},\"image\":{\"endpoint\":{\"route\":\"/_image\"},\"service\":{\"entrypoint\":\"astro/assets/services/sharp\",\"config\":{}},\"domains\":[],\"remotePatterns\":[],\"responsiveStyles\":false},\"devToolbar\":{\"enabled\":true},\"markdown\":{\"syntaxHighlight\":{\"type\":\"shiki\",\"excludeLangs\":[\"math\"]},\"shikiConfig\":{\"langs\":[],\"langAlias\":{},\"theme\":\"github-dark\",\"themes\":{},\"wrap\":false,\"transformers\":[]},\"remarkPlugins\":[],\"rehypePlugins\":[],\"remarkRehype\":{},\"gfm\":true,\"smartypants\":true},\"i18n\":{\"defaultLocale\":\"ar\",\"locales\":[\"ar\",\"en\"],\"routing\":{\"prefixDefaultLocale\":false,\"redirectToDefaultLocale\":true,\"fallbackType\":\"redirect\"}},\"security\":{\"checkOrigin\":true},\"env\":{\"schema\":{},\"validateSecrets\":false},\"experimental\":{\"clientPrerender\":false,\"contentIntellisense\":false,\"headingIdCompat\":false,\"preserveScriptOrder\":false,\"liveContentCollections\":false,\"csp\":false},\"legacy\":{\"collections\":false}}"]
|
.astro/settings.json
ADDED
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"_variables": {
|
3 |
+
"lastUpdateCheck": 1751112608444
|
4 |
+
}
|
5 |
+
}
|
.astro/types.d.ts
ADDED
@@ -0,0 +1 @@
|
|
|
|
|
1 |
+
/// <reference types="astro/client" />
|
.bolt/config.json
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"template": "astro"
|
3 |
+
}
|
.codesandbox/Dockerfile
ADDED
@@ -0,0 +1 @@
|
|
|
|
|
1 |
+
FROM node:18-bullseye
|
.gitignore
CHANGED
@@ -1,41 +1,24 @@
|
|
1 |
-
#
|
|
|
2 |
|
3 |
-
#
|
4 |
-
/
|
5 |
-
/.pnp
|
6 |
-
.pnp.*
|
7 |
-
.yarn/*
|
8 |
-
!.yarn/patches
|
9 |
-
!.yarn/plugins
|
10 |
-
!.yarn/releases
|
11 |
-
!.yarn/versions
|
12 |
-
|
13 |
-
# testing
|
14 |
-
/coverage
|
15 |
-
|
16 |
-
# next.js
|
17 |
-
/.next/
|
18 |
-
/out/
|
19 |
-
|
20 |
-
# production
|
21 |
-
/build
|
22 |
|
23 |
-
#
|
24 |
-
|
25 |
-
*.pem
|
26 |
|
27 |
-
#
|
28 |
npm-debug.log*
|
29 |
yarn-debug.log*
|
30 |
yarn-error.log*
|
31 |
-
|
32 |
|
33 |
-
#
|
34 |
-
.env
|
|
|
35 |
|
36 |
-
#
|
37 |
-
.
|
38 |
|
39 |
-
#
|
40 |
-
|
41 |
-
next-env.d.ts
|
|
|
1 |
+
# build output
|
2 |
+
dist/
|
3 |
|
4 |
+
# generated types
|
5 |
+
.astro/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
6 |
|
7 |
+
# dependencies
|
8 |
+
node_modules/
|
|
|
9 |
|
10 |
+
# logs
|
11 |
npm-debug.log*
|
12 |
yarn-debug.log*
|
13 |
yarn-error.log*
|
14 |
+
pnpm-debug.log*
|
15 |
|
16 |
+
# environment variables
|
17 |
+
.env
|
18 |
+
.env.production
|
19 |
|
20 |
+
# macOS-specific files
|
21 |
+
.DS_Store
|
22 |
|
23 |
+
# jetbrains setting folder
|
24 |
+
.idea/
|
|
.vscode/extensions.json
ADDED
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"recommendations": ["astro-build.astro-vscode"],
|
3 |
+
"unwantedRecommendations": []
|
4 |
+
}
|
.vscode/launch.json
ADDED
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"version": "0.2.0",
|
3 |
+
"configurations": [
|
4 |
+
{
|
5 |
+
"command": "./node_modules/.bin/astro dev",
|
6 |
+
"name": "Development server",
|
7 |
+
"request": "launch",
|
8 |
+
"type": "node-terminal"
|
9 |
+
}
|
10 |
+
]
|
11 |
+
}
|
README.md
CHANGED
@@ -1,22 +1,48 @@
|
|
1 |
-
|
2 |
-
|
3 |
-
|
4 |
-
|
5 |
-
|
6 |
-
|
7 |
-
|
8 |
-
|
9 |
-
|
10 |
-
|
11 |
-
|
12 |
-
|
13 |
-
|
14 |
-
|
15 |
-
|
16 |
-
|
17 |
-
|
18 |
-
|
19 |
-
|
20 |
-
|
21 |
-
|
22 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Astro Starter Kit: Basics
|
2 |
+
|
3 |
+
```sh
|
4 |
+
npm create astro@latest -- --template basics
|
5 |
+
```
|
6 |
+
|
7 |
+
[](https://stackblitz.com/github/withastro/astro/tree/latest/examples/basics)
|
8 |
+
[](https://codesandbox.io/p/sandbox/github/withastro/astro/tree/latest/examples/basics)
|
9 |
+
[](https://codespaces.new/withastro/astro?devcontainer_path=.devcontainer/basics/devcontainer.json)
|
10 |
+
|
11 |
+
> 🧑🚀 **Seasoned astronaut?** Delete this file. Have fun!
|
12 |
+
|
13 |
+

|
14 |
+
|
15 |
+
## 🚀 Project Structure
|
16 |
+
|
17 |
+
Inside of your Astro project, you'll see the following folders and files:
|
18 |
+
|
19 |
+
```text
|
20 |
+
/
|
21 |
+
├── public/
|
22 |
+
│ └── favicon.svg
|
23 |
+
├── src/
|
24 |
+
│ ├── layouts/
|
25 |
+
│ │ └── Layout.astro
|
26 |
+
│ └── pages/
|
27 |
+
│ └── index.astro
|
28 |
+
└── package.json
|
29 |
+
```
|
30 |
+
|
31 |
+
To learn more about the folder structure of an Astro project, refer to [our guide on project structure](https://docs.astro.build/en/basics/project-structure/).
|
32 |
+
|
33 |
+
## 🧞 Commands
|
34 |
+
|
35 |
+
All commands are run from the root of the project, from a terminal:
|
36 |
+
|
37 |
+
| Command | Action |
|
38 |
+
| :------------------------ | :----------------------------------------------- |
|
39 |
+
| `npm install` | Installs dependencies |
|
40 |
+
| `npm run dev` | Starts local dev server at `localhost:4321` |
|
41 |
+
| `npm run build` | Build your production site to `./dist/` |
|
42 |
+
| `npm run preview` | Preview your build locally, before deploying |
|
43 |
+
| `npm run astro ...` | Run CLI commands like `astro add`, `astro check` |
|
44 |
+
| `npm run astro -- --help` | Get help using the Astro CLI |
|
45 |
+
|
46 |
+
## 👀 Want to learn more?
|
47 |
+
|
48 |
+
Feel free to check [our documentation](https://docs.astro.build) or jump into our [Discord server](https://astro.build/chat).
|
astro.config.mjs
ADDED
@@ -0,0 +1,30 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
// @ts-check
|
2 |
+
import { defineConfig } from 'astro/config';
|
3 |
+
import tailwind from '@astrojs/tailwind';
|
4 |
+
import sitemap from '@astrojs/sitemap';
|
5 |
+
|
6 |
+
// https://astro.build/config
|
7 |
+
export default defineConfig({
|
8 |
+
site: 'https://blueprintrak.com',
|
9 |
+
integrations: [
|
10 |
+
tailwind({
|
11 |
+
applyBaseStyles: false,
|
12 |
+
}),
|
13 |
+
sitemap({
|
14 |
+
i18n: {
|
15 |
+
defaultLocale: 'ar',
|
16 |
+
locales: {
|
17 |
+
ar: 'ar-AE',
|
18 |
+
en: 'en-US'
|
19 |
+
}
|
20 |
+
}
|
21 |
+
})
|
22 |
+
],
|
23 |
+
i18n: {
|
24 |
+
defaultLocale: 'ar',
|
25 |
+
locales: ['ar', 'en'],
|
26 |
+
routing: {
|
27 |
+
prefixDefaultLocale: false
|
28 |
+
}
|
29 |
+
}
|
30 |
+
});
|
package-lock.json
CHANGED
The diff for this file is too large to render.
See raw diff
|
|
package.json
CHANGED
@@ -1,64 +1,22 @@
|
|
1 |
{
|
2 |
-
"name": "
|
3 |
-
"
|
|
|
4 |
"private": true,
|
5 |
"scripts": {
|
6 |
-
"dev": "
|
7 |
-
"build": "
|
8 |
-
"
|
9 |
-
"
|
10 |
},
|
11 |
"dependencies": {
|
12 |
-
"
|
13 |
-
"@
|
14 |
-
"@
|
15 |
-
"
|
16 |
-
"
|
17 |
-
"@
|
18 |
-
"@
|
19 |
-
"
|
20 |
-
"@radix-ui/react-popover": "^1.1.14",
|
21 |
-
"@radix-ui/react-select": "^2.2.5",
|
22 |
-
"@radix-ui/react-slot": "^1.2.3",
|
23 |
-
"@radix-ui/react-switch": "^1.2.5",
|
24 |
-
"@radix-ui/react-tabs": "^1.1.12",
|
25 |
-
"@radix-ui/react-toggle": "^1.1.9",
|
26 |
-
"@radix-ui/react-toggle-group": "^1.1.10",
|
27 |
-
"@radix-ui/react-tooltip": "^1.2.7",
|
28 |
-
"@tanstack/eslint-plugin-query": "^5.78.0",
|
29 |
-
"@tanstack/react-query": "^5.80.6",
|
30 |
-
"@tanstack/react-query-devtools": "^5.80.6",
|
31 |
-
"axios": "^1.9.0",
|
32 |
-
"class-variance-authority": "^0.7.1",
|
33 |
-
"classnames": "^2.5.1",
|
34 |
-
"clsx": "^2.1.1",
|
35 |
-
"date-fns": "^4.1.0",
|
36 |
-
"lucide-react": "^0.513.0",
|
37 |
-
"monaco-editor": "^0.52.2",
|
38 |
-
"mongoose": "^8.15.1",
|
39 |
-
"next": "15.3.3",
|
40 |
-
"next-themes": "^0.4.6",
|
41 |
-
"react": "^19.0.0",
|
42 |
-
"react-dom": "^19.0.0",
|
43 |
-
"react-icons": "^5.5.0",
|
44 |
-
"react-use": "^17.6.0",
|
45 |
-
"redaxios": "^0.5.1",
|
46 |
-
"sonner": "^2.0.5",
|
47 |
-
"tailwind-merge": "^3.3.0",
|
48 |
-
"zod": "^3.25.57"
|
49 |
-
},
|
50 |
-
"devDependencies": {
|
51 |
-
"@eslint/eslintrc": "^3",
|
52 |
-
"@tailwindcss/postcss": "^4",
|
53 |
-
"@types/node": "^20",
|
54 |
-
"@types/react": "^19",
|
55 |
-
"@types/react-dom": "^19",
|
56 |
-
"eslint": "^9",
|
57 |
-
"eslint-config-next": "15.3.3",
|
58 |
-
"file-loader": "^6.2.0",
|
59 |
-
"tailwindcss": "^4",
|
60 |
-
"tw-animate-css": "^1.3.4",
|
61 |
-
"typescript": "^5",
|
62 |
-
"url-loader": "^4.1.1"
|
63 |
}
|
64 |
-
}
|
|
|
1 |
{
|
2 |
+
"name": "blueprint-engineering-consultancy",
|
3 |
+
"type": "module",
|
4 |
+
"version": "1.0.0",
|
5 |
"private": true,
|
6 |
"scripts": {
|
7 |
+
"dev": "astro dev",
|
8 |
+
"build": "astro build",
|
9 |
+
"preview": "astro preview",
|
10 |
+
"astro": "astro"
|
11 |
},
|
12 |
"dependencies": {
|
13 |
+
"astro": "^5.2.5",
|
14 |
+
"@astrojs/tailwind": "^5.1.1",
|
15 |
+
"@astrojs/sitemap": "^3.2.1",
|
16 |
+
"tailwindcss": "^3.4.0",
|
17 |
+
"aos": "^2.3.4",
|
18 |
+
"@fontsource/cairo": "^5.0.18",
|
19 |
+
"@fontsource/poppins": "^5.0.8",
|
20 |
+
"lucide-astro": "^0.447.0"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
21 |
}
|
22 |
+
}
|
public/4b1c9cde-efca-46d2-9e6a-77f89da9b914 copy.JPG
ADDED
|
public/favicon.svg
ADDED
|
public/logo.jpg
ADDED
![]() |
public/picsvg_download.webm
ADDED
Binary file (51.8 kB). View file
|
|
src/components/About.astro
ADDED
@@ -0,0 +1,134 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
---
|
2 |
+
import { getLangFromUrl, useTranslations } from '../i18n/ui';
|
3 |
+
import { Target, Eye, Heart, Award } from 'lucide-astro';
|
4 |
+
|
5 |
+
const lang = getLangFromUrl(Astro.url);
|
6 |
+
const t = useTranslations(lang);
|
7 |
+
|
8 |
+
const values = [
|
9 |
+
{
|
10 |
+
icon: Target,
|
11 |
+
title: t('about.values.precision.title'),
|
12 |
+
description: t('about.values.precision.description')
|
13 |
+
},
|
14 |
+
{
|
15 |
+
icon: Heart,
|
16 |
+
title: t('about.values.innovation.title'),
|
17 |
+
description: t('about.values.innovation.description')
|
18 |
+
},
|
19 |
+
{
|
20 |
+
icon: Award,
|
21 |
+
title: t('about.values.quality.title'),
|
22 |
+
description: t('about.values.quality.description')
|
23 |
+
}
|
24 |
+
];
|
25 |
+
---
|
26 |
+
|
27 |
+
<section id="about" class="section-padding bg-blueprint-light dark:bg-slate-900">
|
28 |
+
<div class="container-custom">
|
29 |
+
<!-- Section Header -->
|
30 |
+
<div class="text-center mb-16" data-aos="fade-up">
|
31 |
+
<h2 class="text-4xl md:text-5xl font-bold text-blueprint-primary dark:text-white mb-6">
|
32 |
+
{t('about.title')}
|
33 |
+
</h2>
|
34 |
+
<div class="w-24 h-1 bg-blueprint-accent mx-auto mb-8"></div>
|
35 |
+
</div>
|
36 |
+
|
37 |
+
<div class="grid grid-cols-1 lg:grid-cols-2 gap-16 items-center">
|
38 |
+
<!-- Left Content -->
|
39 |
+
<div data-aos="fade-right">
|
40 |
+
<h3 class="text-3xl font-bold text-blueprint-primary dark:text-white mb-6">
|
41 |
+
{t('about.subtitle')}
|
42 |
+
</h3>
|
43 |
+
|
44 |
+
<div class="space-y-6 text-lg text-blueprint-dark dark:text-blueprint-light leading-relaxed">
|
45 |
+
<p>{t('about.description.1')}</p>
|
46 |
+
<p>{t('about.description.2')}</p>
|
47 |
+
<p>{t('about.description.3')}</p>
|
48 |
+
</div>
|
49 |
+
|
50 |
+
<!-- Vision & Mission -->
|
51 |
+
<div class="mt-12 space-y-8">
|
52 |
+
<div class="flex items-start space-x-4 rtl:space-x-reverse">
|
53 |
+
<div class="flex items-center justify-center w-12 h-12 bg-blueprint-accent/20 rounded-lg flex-shrink-0">
|
54 |
+
<Eye class="w-6 h-6 text-blueprint-accent" />
|
55 |
+
</div>
|
56 |
+
<div>
|
57 |
+
<h4 class="text-xl font-bold text-blueprint-primary dark:text-white mb-2">
|
58 |
+
{t('about.vision.title')}
|
59 |
+
</h4>
|
60 |
+
<p class="text-blueprint-dark dark:text-blueprint-light">
|
61 |
+
{t('about.vision.description')}
|
62 |
+
</p>
|
63 |
+
</div>
|
64 |
+
</div>
|
65 |
+
|
66 |
+
<div class="flex items-start space-x-4 rtl:space-x-reverse">
|
67 |
+
<div class="flex items-center justify-center w-12 h-12 bg-blueprint-accent/20 rounded-lg flex-shrink-0">
|
68 |
+
<Target class="w-6 h-6 text-blueprint-accent" />
|
69 |
+
</div>
|
70 |
+
<div>
|
71 |
+
<h4 class="text-xl font-bold text-blueprint-primary dark:text-white mb-2">
|
72 |
+
{t('about.mission.title')}
|
73 |
+
</h4>
|
74 |
+
<p class="text-blueprint-dark dark:text-blueprint-light">
|
75 |
+
{t('about.mission.description')}
|
76 |
+
</p>
|
77 |
+
</div>
|
78 |
+
</div>
|
79 |
+
</div>
|
80 |
+
</div>
|
81 |
+
|
82 |
+
<!-- Right Content - Values -->
|
83 |
+
<div data-aos="fade-left">
|
84 |
+
<!-- Values -->
|
85 |
+
<div class="space-y-6">
|
86 |
+
<h4 class="text-2xl font-bold text-blueprint-primary dark:text-white text-center mb-8">
|
87 |
+
{t('about.values.title')}
|
88 |
+
</h4>
|
89 |
+
|
90 |
+
{values.map((value, index) => (
|
91 |
+
<div
|
92 |
+
class="flex items-center space-x-4 rtl:space-x-reverse p-6 bg-white dark:bg-blueprint-primary rounded-xl shadow-md hover:shadow-lg transition-shadow duration-300"
|
93 |
+
data-aos="fade-up"
|
94 |
+
data-aos-delay={index * 150}
|
95 |
+
>
|
96 |
+
<div class="flex items-center justify-center w-16 h-16 bg-blueprint-accent/20 rounded-lg flex-shrink-0">
|
97 |
+
<value.icon class="w-8 h-8 text-blueprint-accent" />
|
98 |
+
</div>
|
99 |
+
<div>
|
100 |
+
<h5 class="text-xl font-bold text-blueprint-primary dark:text-white mb-2">
|
101 |
+
{value.title}
|
102 |
+
</h5>
|
103 |
+
<p class="text-blueprint-dark dark:text-blueprint-light">
|
104 |
+
{value.description}
|
105 |
+
</p>
|
106 |
+
</div>
|
107 |
+
</div>
|
108 |
+
))}
|
109 |
+
</div>
|
110 |
+
|
111 |
+
<!-- Why Choose Blueprint -->
|
112 |
+
<div class="mt-12 p-6 bg-blueprint-accent/10 rounded-xl border border-blueprint-accent/20" data-aos="fade-up" data-aos-delay="600">
|
113 |
+
<h4 class="text-xl font-bold text-blueprint-primary dark:text-white mb-4">
|
114 |
+
{t('about.why.title')}
|
115 |
+
</h4>
|
116 |
+
<ul class="space-y-2 text-blueprint-dark dark:text-blueprint-light">
|
117 |
+
<li class="flex items-start space-x-2 rtl:space-x-reverse">
|
118 |
+
<span class="text-blueprint-accent mt-1">•</span>
|
119 |
+
<span>{t('about.why.point1')}</span>
|
120 |
+
</li>
|
121 |
+
<li class="flex items-start space-x-2 rtl:space-x-reverse">
|
122 |
+
<span class="text-blueprint-accent mt-1">•</span>
|
123 |
+
<span>{t('about.why.point2')}</span>
|
124 |
+
</li>
|
125 |
+
<li class="flex items-start space-x-2 rtl:space-x-reverse">
|
126 |
+
<span class="text-blueprint-accent mt-1">•</span>
|
127 |
+
<span>{t('about.why.point3')}</span>
|
128 |
+
</li>
|
129 |
+
</ul>
|
130 |
+
</div>
|
131 |
+
</div>
|
132 |
+
</div>
|
133 |
+
</div>
|
134 |
+
</section>
|
src/components/Contact.astro
ADDED
@@ -0,0 +1,169 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
---
|
2 |
+
import { getLangFromUrl, useTranslations } from '../i18n/ui';
|
3 |
+
import { MapPin, Phone, Mail, MessageCircle, Clock } from 'lucide-astro';
|
4 |
+
|
5 |
+
const lang = getLangFromUrl(Astro.url);
|
6 |
+
const t = useTranslations(lang);
|
7 |
+
---
|
8 |
+
|
9 |
+
<section id="contact" class="section-padding bg-white dark:bg-blueprint-primary">
|
10 |
+
<div class="container-custom">
|
11 |
+
<!-- Section Header -->
|
12 |
+
<div class="text-center mb-16" data-aos="fade-up">
|
13 |
+
<h2 class="text-4xl md:text-5xl font-bold text-blueprint-primary dark:text-white mb-6">
|
14 |
+
{t('contact.title')}
|
15 |
+
</h2>
|
16 |
+
<p class="text-xl text-blueprint-dark dark:text-blueprint-light max-w-3xl mx-auto">
|
17 |
+
{t('contact.description')}
|
18 |
+
</p>
|
19 |
+
</div>
|
20 |
+
|
21 |
+
<!-- Contact Information - Full Width -->
|
22 |
+
<div class="max-w-4xl mx-auto" data-aos="fade-up">
|
23 |
+
<h3 class="text-3xl font-bold text-blueprint-primary dark:text-white mb-12 text-center">
|
24 |
+
{t('contact.info.title')}
|
25 |
+
</h3>
|
26 |
+
|
27 |
+
<div class="grid grid-cols-1 md:grid-cols-2 gap-8 mb-12">
|
28 |
+
<!-- Address -->
|
29 |
+
<div class="flex items-start space-x-4 rtl:space-x-reverse p-6 bg-blueprint-light dark:bg-slate-800 rounded-2xl shadow-lg">
|
30 |
+
<div class="flex items-center justify-center w-12 h-12 bg-blueprint-accent/20 rounded-lg flex-shrink-0">
|
31 |
+
<MapPin class="w-6 h-6 text-blueprint-accent" />
|
32 |
+
</div>
|
33 |
+
<div>
|
34 |
+
<h4 class="font-bold text-blueprint-primary dark:text-white mb-2">
|
35 |
+
{t('contact.address.title')}
|
36 |
+
</h4>
|
37 |
+
<p class="text-blueprint-dark dark:text-blueprint-light leading-relaxed" set:html={t('contact.address.text')}></p>
|
38 |
+
<a
|
39 |
+
href="https://maps.app.goo.gl/Xys7qA59S2tpJPWS9"
|
40 |
+
target="_blank"
|
41 |
+
class="inline-flex items-center text-blueprint-accent hover:text-blueprint-accent/80 transition-colors duration-300 mt-2"
|
42 |
+
>
|
43 |
+
{t('contact.address.map')}
|
44 |
+
</a>
|
45 |
+
</div>
|
46 |
+
</div>
|
47 |
+
|
48 |
+
<!-- Phone -->
|
49 |
+
<div class="flex items-start space-x-4 rtl:space-x-reverse p-6 bg-blueprint-light dark:bg-slate-800 rounded-2xl shadow-lg">
|
50 |
+
<div class="flex items-center justify-center w-12 h-12 bg-blueprint-accent/20 rounded-lg flex-shrink-0">
|
51 |
+
<Phone class="w-6 h-6 text-blueprint-accent" />
|
52 |
+
</div>
|
53 |
+
<div>
|
54 |
+
<h4 class="font-bold text-blueprint-primary dark:text-white mb-2">
|
55 |
+
{t('contact.phone.title')}
|
56 |
+
</h4>
|
57 |
+
<a
|
58 |
+
href="tel:+971501611234"
|
59 |
+
class="text-blueprint-dark dark:text-blueprint-light hover:text-blueprint-accent transition-colors duration-300 text-lg block"
|
60 |
+
>
|
61 |
+
+971 50 161 1234
|
62 |
+
</a>
|
63 |
+
<p class="text-sm text-blueprint-dark dark:text-blueprint-light mt-1">
|
64 |
+
{t('contact.phone.note')}
|
65 |
+
</p>
|
66 |
+
</div>
|
67 |
+
</div>
|
68 |
+
|
69 |
+
<!-- Email -->
|
70 |
+
<div class="flex items-start space-x-4 rtl:space-x-reverse p-6 bg-blueprint-light dark:bg-slate-800 rounded-2xl shadow-lg">
|
71 |
+
<div class="flex items-center justify-center w-12 h-12 bg-blueprint-accent/20 rounded-lg flex-shrink-0">
|
72 |
+
<Mail class="w-6 h-6 text-blueprint-accent" />
|
73 |
+
</div>
|
74 |
+
<div>
|
75 |
+
<h4 class="font-bold text-blueprint-primary dark:text-white mb-2">
|
76 |
+
{t('contact.email.title')}
|
77 |
+
</h4>
|
78 |
+
<a
|
79 |
+
href="mailto:[email protected]"
|
80 |
+
class="text-blueprint-dark dark:text-blueprint-light hover:text-blueprint-accent transition-colors duration-300 text-lg break-all"
|
81 |
+
>
|
82 | |
83 |
+
</a>
|
84 |
+
</div>
|
85 |
+
</div>
|
86 |
+
|
87 |
+
<!-- Working Hours -->
|
88 |
+
<div class="flex items-start space-x-4 rtl:space-x-reverse p-6 bg-blueprint-light dark:bg-slate-800 rounded-2xl shadow-lg">
|
89 |
+
<div class="flex items-center justify-center w-12 h-12 bg-blueprint-accent/20 rounded-lg flex-shrink-0">
|
90 |
+
<Clock class="w-6 h-6 text-blueprint-accent" />
|
91 |
+
</div>
|
92 |
+
<div class="flex-1">
|
93 |
+
<h4 class="font-bold text-blueprint-primary dark:text-white mb-4">
|
94 |
+
{t('contact.hours.title')}
|
95 |
+
</h4>
|
96 |
+
|
97 |
+
<!-- Working Hours Schedule -->
|
98 |
+
<div class="space-y-3">
|
99 |
+
<!-- Sunday to Thursday -->
|
100 |
+
<div>
|
101 |
+
<div class="font-semibold text-blueprint-primary dark:text-white mb-1">
|
102 |
+
{lang === 'ar' ? 'الأحد - الخميس' : 'Sunday - Thursday'}
|
103 |
+
</div>
|
104 |
+
<div class="space-y-1 text-sm">
|
105 |
+
<div class="flex items-center justify-between">
|
106 |
+
<span class="text-blueprint-dark dark:text-blueprint-light">
|
107 |
+
{lang === 'ar' ? 'الفترة الصباحية' : 'Morning'}
|
108 |
+
</span>
|
109 |
+
<span class="font-medium text-blueprint-accent">08:00 - 14:00</span>
|
110 |
+
</div>
|
111 |
+
<div class="flex items-center justify-between">
|
112 |
+
<span class="text-blueprint-dark dark:text-blueprint-light">
|
113 |
+
{lang === 'ar' ? 'الفترة المسائية' : 'Evening'}
|
114 |
+
</span>
|
115 |
+
<span class="font-medium text-blueprint-accent">17:00 - 20:30</span>
|
116 |
+
</div>
|
117 |
+
</div>
|
118 |
+
</div>
|
119 |
+
|
120 |
+
<!-- Friday -->
|
121 |
+
<div class="pt-2 border-t border-blueprint-accent/10">
|
122 |
+
<div class="flex items-center justify-between">
|
123 |
+
<span class="font-semibold text-blueprint-primary dark:text-white">
|
124 |
+
{lang === 'ar' ? 'الجمعة' : 'Friday'}
|
125 |
+
</span>
|
126 |
+
<span class="font-medium text-blueprint-accent">08:00 - 12:00</span>
|
127 |
+
</div>
|
128 |
+
</div>
|
129 |
+
|
130 |
+
<!-- Saturday -->
|
131 |
+
<div class="pt-2 border-t border-blueprint-accent/10">
|
132 |
+
<div class="text-center">
|
133 |
+
<span class="text-blueprint-dark dark:text-blueprint-light text-sm">
|
134 |
+
{lang === 'ar' ? 'السبت: عطلة' : 'Saturday: Closed'}
|
135 |
+
</span>
|
136 |
+
</div>
|
137 |
+
</div>
|
138 |
+
</div>
|
139 |
+
</div>
|
140 |
+
</div>
|
141 |
+
</div>
|
142 |
+
|
143 |
+
<!-- WhatsApp CTA - Centered -->
|
144 |
+
<div class="max-w-2xl mx-auto">
|
145 |
+
<div class="p-8 bg-green-50 dark:bg-green-900/20 rounded-2xl border border-green-200 dark:border-green-800 text-center">
|
146 |
+
<div class="flex items-center justify-center mb-6">
|
147 |
+
<div class="flex items-center justify-center w-16 h-16 bg-green-500 rounded-full">
|
148 |
+
<MessageCircle class="w-8 h-8 text-white" />
|
149 |
+
</div>
|
150 |
+
</div>
|
151 |
+
<h4 class="text-2xl font-bold text-green-800 dark:text-green-300 mb-3">
|
152 |
+
{t('contact.whatsapp.title')}
|
153 |
+
</h4>
|
154 |
+
<p class="text-green-700 dark:text-green-400 mb-6 text-lg">
|
155 |
+
{t('contact.whatsapp.description')}
|
156 |
+
</p>
|
157 |
+
<a
|
158 |
+
href="https://wa.me/971501611234?text=استشارة"
|
159 |
+
target="_blank"
|
160 |
+
class="inline-flex items-center bg-green-500 hover:bg-green-600 text-white px-8 py-4 rounded-lg font-semibold transition-colors duration-300 text-lg"
|
161 |
+
>
|
162 |
+
<MessageCircle class="w-6 h-6 mr-3" />
|
163 |
+
{t('contact.whatsapp.button')}
|
164 |
+
</a>
|
165 |
+
</div>
|
166 |
+
</div>
|
167 |
+
</div>
|
168 |
+
</div>
|
169 |
+
</section>
|
src/components/Footer.astro
ADDED
@@ -0,0 +1,175 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
---
|
2 |
+
import { getLangFromUrl, useTranslations, languages } from '../i18n/ui';
|
3 |
+
import { MapPin, Phone, Mail, MessageCircle, Facebook, Instagram, Linkedin } from 'lucide-astro';
|
4 |
+
|
5 |
+
const lang = getLangFromUrl(Astro.url);
|
6 |
+
const t = useTranslations(lang);
|
7 |
+
---
|
8 |
+
|
9 |
+
<footer class="bg-blueprint-primary dark:bg-slate-900 text-white">
|
10 |
+
<!-- Main Footer Content -->
|
11 |
+
<div class="section-padding border-b border-blueprint-accent/20">
|
12 |
+
<div class="container-custom">
|
13 |
+
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-8">
|
14 |
+
<!-- Company Info -->
|
15 |
+
<div class="lg:col-span-2">
|
16 |
+
<div class="flex items-center mb-6">
|
17 |
+
<img src="/logo.jpg" alt="Blueprint Engineering Consultancy Logo" class="h-20 w-auto" />
|
18 |
+
</div>
|
19 |
+
|
20 |
+
<p class="text-blueprint-light leading-relaxed mb-6 max-w-md">
|
21 |
+
{t('footer.description')}
|
22 |
+
</p>
|
23 |
+
|
24 |
+
<!-- Social Media Links -->
|
25 |
+
<div class="flex space-x-4 rtl:space-x-reverse">
|
26 |
+
<a href="#" class="w-10 h-10 bg-blueprint-accent/20 rounded-lg flex items-center justify-center hover:bg-blueprint-accent transition-colors duration-300">
|
27 |
+
<Facebook class="w-5 h-5" />
|
28 |
+
</a>
|
29 |
+
<a href="#" class="w-10 h-10 bg-blueprint-accent/20 rounded-lg flex items-center justify-center hover:bg-blueprint-accent transition-colors duration-300">
|
30 |
+
<Instagram class="w-5 h-5" />
|
31 |
+
</a>
|
32 |
+
<a href="#" class="w-10 h-10 bg-blueprint-accent/20 rounded-lg flex items-center justify-center hover:bg-blueprint-accent transition-colors duration-300">
|
33 |
+
<Linkedin class="w-5 h-5" />
|
34 |
+
</a>
|
35 |
+
</div>
|
36 |
+
</div>
|
37 |
+
|
38 |
+
<!-- Quick Links -->
|
39 |
+
<div>
|
40 |
+
<h4 class="text-xl font-bold mb-6 text-blueprint-accent">
|
41 |
+
{t('footer.quick_links')}
|
42 |
+
</h4>
|
43 |
+
<ul class="space-y-3">
|
44 |
+
<li>
|
45 |
+
<a href="#about" class="text-blueprint-light hover:text-blueprint-accent transition-colors duration-300">
|
46 |
+
{t('nav.about')}
|
47 |
+
</a>
|
48 |
+
</li>
|
49 |
+
<li>
|
50 |
+
<a href="#services" class="text-blueprint-light hover:text-blueprint-accent transition-colors duration-300">
|
51 |
+
{t('nav.services')}
|
52 |
+
</a>
|
53 |
+
</li>
|
54 |
+
<li>
|
55 |
+
<a href="#workflow" class="text-blueprint-light hover:text-blueprint-accent transition-colors duration-300">
|
56 |
+
{t('nav.workflow')}
|
57 |
+
</a>
|
58 |
+
</li>
|
59 |
+
<li>
|
60 |
+
<a href="#contact" class="text-blueprint-light hover:text-blueprint-accent transition-colors duration-300">
|
61 |
+
{t('nav.contact')}
|
62 |
+
</a>
|
63 |
+
</li>
|
64 |
+
</ul>
|
65 |
+
</div>
|
66 |
+
|
67 |
+
<!-- Contact Info -->
|
68 |
+
<div>
|
69 |
+
<h4 class="text-xl font-bold mb-6 text-blueprint-accent">
|
70 |
+
{t('footer.contact_info')}
|
71 |
+
</h4>
|
72 |
+
<ul class="space-y-4">
|
73 |
+
<li class="flex items-start space-x-3 rtl:space-x-reverse">
|
74 |
+
<MapPin class="w-5 h-5 text-blueprint-accent mt-1 flex-shrink-0" />
|
75 |
+
<div class="text-blueprint-light text-sm" set:html={t('contact.address.text')}></div>
|
76 |
+
</li>
|
77 |
+
<li class="flex items-center space-x-3 rtl:space-x-reverse">
|
78 |
+
<Phone class="w-5 h-5 text-blueprint-accent flex-shrink-0" />
|
79 |
+
<a href="tel:+971501611234" class="text-blueprint-light hover:text-blueprint-accent transition-colors duration-300">
|
80 |
+
+971 50 161 1234
|
81 |
+
</a>
|
82 |
+
</li>
|
83 |
+
<li class="flex items-center space-x-3 rtl:space-x-reverse">
|
84 |
+
<Mail class="w-5 h-5 text-blueprint-accent flex-shrink-0" />
|
85 |
+
<a href="mailto:[email protected]" class="text-blueprint-light hover:text-blueprint-accent transition-colors duration-300 break-all">
|
86 | |
87 |
+
</a>
|
88 |
+
</li>
|
89 |
+
</ul>
|
90 |
+
|
91 |
+
<!-- WhatsApp CTA -->
|
92 |
+
<div class="mt-6">
|
93 |
+
<a
|
94 |
+
href="https://wa.me/971501611234"
|
95 |
+
target="_blank"
|
96 |
+
class="inline-flex items-center bg-green-500 hover:bg-green-600 text-white px-4 py-2 rounded-lg font-semibold transition-colors duration-300"
|
97 |
+
>
|
98 |
+
<MessageCircle class="w-4 h-4 mr-2" />
|
99 |
+
{t('footer.whatsapp')}
|
100 |
+
</a>
|
101 |
+
</div>
|
102 |
+
</div>
|
103 |
+
</div>
|
104 |
+
</div>
|
105 |
+
</div>
|
106 |
+
|
107 |
+
<!-- Bottom Footer -->
|
108 |
+
<div class="py-6">
|
109 |
+
<div class="container-custom">
|
110 |
+
<div class="flex flex-col md:flex-row justify-between items-center space-y-4 md:space-y-0">
|
111 |
+
<div class="text-sm text-blueprint-light">
|
112 |
+
© <span id="current-year"></span> {t('footer.rights')}
|
113 |
+
</div>
|
114 |
+
|
115 |
+
<!-- Language Selector in Footer -->
|
116 |
+
<div class="flex items-center space-x-2 rtl:space-x-reverse text-sm">
|
117 |
+
<span class="text-blueprint-light">{t('footer.language')}</span>
|
118 |
+
<select
|
119 |
+
class="bg-blueprint-primary border border-blueprint-accent/30 text-white rounded px-2 py-1 text-sm"
|
120 |
+
data-footer-language-selector
|
121 |
+
>
|
122 |
+
{Object.entries(languages).map(([code, name]) => (
|
123 |
+
<option value={code} selected={code === lang}>
|
124 |
+
{name}
|
125 |
+
</option>
|
126 |
+
))}
|
127 |
+
</select>
|
128 |
+
</div>
|
129 |
+
</div>
|
130 |
+
</div>
|
131 |
+
</div>
|
132 |
+
</footer>
|
133 |
+
|
134 |
+
<!-- WhatsApp Floating Button -->
|
135 |
+
<div class="fixed bottom-6 right-6 z-50" data-aos="zoom-in" data-aos-delay="1000">
|
136 |
+
<a
|
137 |
+
href="https://wa.me/971501611234?text=مرحباً، أريد استشارة هندسية"
|
138 |
+
target="_blank"
|
139 |
+
class="flex items-center justify-center w-14 h-14 bg-green-500 hover:bg-green-600 text-white rounded-full shadow-lg hover:shadow-xl transition-all duration-300 animate-bounce-slow"
|
140 |
+
title={lang === 'ar' ? 'تواصل عبر واتساب' : 'Contact via WhatsApp'}
|
141 |
+
>
|
142 |
+
<MessageCircle class="w-6 h-6" />
|
143 |
+
</a>
|
144 |
+
</div>
|
145 |
+
|
146 |
+
<script>
|
147 |
+
// Set current year dynamically
|
148 |
+
document.addEventListener('DOMContentLoaded', () => {
|
149 |
+
const currentYearElement = document.getElementById('current-year');
|
150 |
+
if (currentYearElement) {
|
151 |
+
currentYearElement.textContent = new Date().getFullYear().toString();
|
152 |
+
}
|
153 |
+
});
|
154 |
+
|
155 |
+
// Footer language selector
|
156 |
+
const footerLanguageSelector = document.querySelector('[data-footer-language-selector]') as HTMLSelectElement;
|
157 |
+
|
158 |
+
if (footerLanguageSelector) {
|
159 |
+
footerLanguageSelector.addEventListener('change', (e) => {
|
160 |
+
const selectedLang = (e.target as HTMLSelectElement).value;
|
161 |
+
const currentPath = window.location.pathname;
|
162 |
+
|
163 |
+
let newPath;
|
164 |
+
if (selectedLang === 'ar') {
|
165 |
+
// Going to Arabic (default) - remove /en prefix
|
166 |
+
newPath = currentPath === '/en' || currentPath === '/en/' ? '/' : currentPath.replace('/en', '') || '/';
|
167 |
+
} else {
|
168 |
+
// Going to English - add /en prefix
|
169 |
+
newPath = currentPath === '/' ? '/en' : `/en${currentPath}`;
|
170 |
+
}
|
171 |
+
|
172 |
+
window.location.href = newPath;
|
173 |
+
});
|
174 |
+
}
|
175 |
+
</script>
|
src/components/Header.astro
ADDED
@@ -0,0 +1,142 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
---
|
2 |
+
import { getLangFromUrl, useTranslations } from '../i18n/ui';
|
3 |
+
import LanguageSelector from './LanguageSelector.astro';
|
4 |
+
import ThemeToggle from './ThemeToggle.astro';
|
5 |
+
import { Menu, X } from 'lucide-astro';
|
6 |
+
|
7 |
+
const lang = getLangFromUrl(Astro.url);
|
8 |
+
const t = useTranslations(lang);
|
9 |
+
---
|
10 |
+
|
11 |
+
<header class="fixed top-0 left-0 right-0 z-50 transition-all duration-300" data-header>
|
12 |
+
<nav class="container-custom">
|
13 |
+
<div class="flex items-center justify-between h-20 px-4 transition-all duration-300" data-nav-container>
|
14 |
+
<!-- Logo -->
|
15 |
+
<a href={lang === 'ar' ? '/' : `/${lang}`} class="flex items-center">
|
16 |
+
<img
|
17 |
+
src="/logo.jpg"
|
18 |
+
alt="Blueprint Engineering Consultancy Logo"
|
19 |
+
class="h-14 w-auto transition-all duration-300"
|
20 |
+
data-logo
|
21 |
+
/>
|
22 |
+
</a>
|
23 |
+
|
24 |
+
<!-- Desktop Navigation -->
|
25 |
+
<div class="hidden lg:flex items-center space-x-8 rtl:space-x-reverse">
|
26 |
+
<a href="#about" class="nav-link">{t('nav.about')}</a>
|
27 |
+
<a href="#services" class="nav-link">{t('nav.services')}</a>
|
28 |
+
<a href="#workflow" class="nav-link">{t('nav.workflow')}</a>
|
29 |
+
<a href="#contact" class="nav-link">{t('nav.contact')}</a>
|
30 |
+
</div>
|
31 |
+
|
32 |
+
<!-- Language Selector & Theme Toggle -->
|
33 |
+
<div class="flex items-center space-x-4 rtl:space-x-reverse">
|
34 |
+
<ThemeToggle />
|
35 |
+
<LanguageSelector />
|
36 |
+
|
37 |
+
<!-- Mobile Menu Button -->
|
38 |
+
<button class="lg:hidden text-white p-2" data-mobile-menu-toggle>
|
39 |
+
<Menu class="w-6 h-6" data-menu-icon />
|
40 |
+
<X class="w-6 h-6 hidden" data-close-icon />
|
41 |
+
</button>
|
42 |
+
</div>
|
43 |
+
</div>
|
44 |
+
|
45 |
+
<!-- Mobile Navigation -->
|
46 |
+
<div class="lg:hidden hidden bg-blueprint-primary/95 backdrop-blur-md border-t border-blueprint-accent/20" data-mobile-menu>
|
47 |
+
<div class="px-4 py-4 space-y-4">
|
48 |
+
<a href="#about" class="block nav-link">{t('nav.about')}</a>
|
49 |
+
<a href="#services" class="block nav-link">{t('nav.services')}</a>
|
50 |
+
<a href="#workflow" class="block nav-link">{t('nav.workflow')}</a>
|
51 |
+
<a href="#contact" class="block nav-link">{t('nav.contact')}</a>
|
52 |
+
</div>
|
53 |
+
</div>
|
54 |
+
</nav>
|
55 |
+
</header>
|
56 |
+
|
57 |
+
<script>
|
58 |
+
// Mobile menu toggle
|
59 |
+
const mobileMenuToggle = document.querySelector('[data-mobile-menu-toggle]');
|
60 |
+
const mobileMenu = document.querySelector('[data-mobile-menu]');
|
61 |
+
const menuIcon = document.querySelector('[data-menu-icon]');
|
62 |
+
const closeIcon = document.querySelector('[data-close-icon]');
|
63 |
+
|
64 |
+
mobileMenuToggle?.addEventListener('click', () => {
|
65 |
+
mobileMenu?.classList.toggle('hidden');
|
66 |
+
menuIcon?.classList.toggle('hidden');
|
67 |
+
closeIcon?.classList.toggle('hidden');
|
68 |
+
});
|
69 |
+
|
70 |
+
// Header scroll effects
|
71 |
+
let lastScrollTop = 0;
|
72 |
+
const header = document.querySelector('[data-header]') as HTMLElement;
|
73 |
+
const navContainer = document.querySelector('[data-nav-container]') as HTMLElement;
|
74 |
+
const logo = document.querySelector('[data-logo]') as HTMLElement;
|
75 |
+
|
76 |
+
function updateHeader() {
|
77 |
+
const scrollTop = window.pageYOffset || document.documentElement.scrollTop;
|
78 |
+
|
79 |
+
if (scrollTop > 50) {
|
80 |
+
// Scrolled state
|
81 |
+
header.classList.add('bg-blueprint-primary/95', 'backdrop-blur-md', 'border-b', 'border-blueprint-accent/20', 'shadow-lg');
|
82 |
+
navContainer.classList.add('h-16');
|
83 |
+
navContainer.classList.remove('h-20');
|
84 |
+
logo.classList.add('h-10');
|
85 |
+
logo.classList.remove('h-14');
|
86 |
+
} else {
|
87 |
+
// Top state
|
88 |
+
header.classList.remove('bg-blueprint-primary/95', 'backdrop-blur-md', 'border-b', 'border-blueprint-accent/20', 'shadow-lg');
|
89 |
+
navContainer.classList.remove('h-16');
|
90 |
+
navContainer.classList.add('h-20');
|
91 |
+
logo.classList.remove('h-10');
|
92 |
+
logo.classList.add('h-14');
|
93 |
+
}
|
94 |
+
|
95 |
+
// Hide/show header on scroll
|
96 |
+
if (scrollTop > lastScrollTop && scrollTop > 100) {
|
97 |
+
// Scrolling down
|
98 |
+
header.style.transform = 'translateY(-100%)';
|
99 |
+
} else {
|
100 |
+
// Scrolling up
|
101 |
+
header.style.transform = 'translateY(0)';
|
102 |
+
}
|
103 |
+
|
104 |
+
lastScrollTop = scrollTop <= 0 ? 0 : scrollTop;
|
105 |
+
}
|
106 |
+
|
107 |
+
window.addEventListener('scroll', updateHeader);
|
108 |
+
|
109 |
+
// Initial call
|
110 |
+
updateHeader();
|
111 |
+
|
112 |
+
// Smooth scroll for anchor links
|
113 |
+
document.querySelectorAll('a[href^="#"]').forEach(anchor => {
|
114 |
+
anchor.addEventListener('click', function (e) {
|
115 |
+
e.preventDefault();
|
116 |
+
const target = document.querySelector(this.getAttribute('href'));
|
117 |
+
if (target) {
|
118 |
+
const headerHeight = header?.offsetHeight || 0;
|
119 |
+
const targetPosition = target.offsetTop - headerHeight - 20;
|
120 |
+
window.scrollTo({
|
121 |
+
top: targetPosition,
|
122 |
+
behavior: 'smooth'
|
123 |
+
});
|
124 |
+
|
125 |
+
// Close mobile menu if open
|
126 |
+
mobileMenu?.classList.add('hidden');
|
127 |
+
menuIcon?.classList.remove('hidden');
|
128 |
+
closeIcon?.classList.add('hidden');
|
129 |
+
}
|
130 |
+
});
|
131 |
+
});
|
132 |
+
</script>
|
133 |
+
|
134 |
+
<style>
|
135 |
+
.nav-link {
|
136 |
+
@apply text-white hover:text-blueprint-accent transition-colors duration-300 font-medium;
|
137 |
+
}
|
138 |
+
|
139 |
+
.nav-link:hover {
|
140 |
+
@apply text-blueprint-accent;
|
141 |
+
}
|
142 |
+
</style>
|
src/components/Hero.astro
ADDED
@@ -0,0 +1,89 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
---
|
2 |
+
import { getLangFromUrl, useTranslations } from '../i18n/ui';
|
3 |
+
import { ArrowRight, MessageCircle } from 'lucide-astro';
|
4 |
+
|
5 |
+
const lang = getLangFromUrl(Astro.url);
|
6 |
+
const t = useTranslations(lang);
|
7 |
+
const isRtl = lang === 'ar' || lang === 'ur' || lang === 'ps';
|
8 |
+
---
|
9 |
+
|
10 |
+
<section class="relative min-h-screen flex items-center justify-center overflow-hidden bg-gradient-to-br from-blueprint-primary via-blueprint-primary to-blue-900 dark:from-blueprint-primary dark:via-slate-900 dark:to-blueprint-primary">
|
11 |
+
<!-- Animated Background -->
|
12 |
+
<div class="absolute inset-0">
|
13 |
+
<div class="floating-shapes"></div>
|
14 |
+
<div class="blueprint-pattern opacity-10"></div>
|
15 |
+
</div>
|
16 |
+
|
17 |
+
<!-- Animated Blueprint Lines -->
|
18 |
+
<div class="absolute inset-0 overflow-hidden">
|
19 |
+
<svg class="absolute top-1/4 left-1/4 w-96 h-96 opacity-5" viewBox="0 0 400 400">
|
20 |
+
<defs>
|
21 |
+
<pattern id="blueprint-grid" width="40" height="40" patternUnits="userSpaceOnUse">
|
22 |
+
<path d="M 40 0 L 0 0 0 40" fill="none" stroke="#C69C49" stroke-width="1"/>
|
23 |
+
</pattern>
|
24 |
+
</defs>
|
25 |
+
<rect width="100%" height="100%" fill="url(#blueprint-grid)" />
|
26 |
+
<rect x="50" y="50" width="300" height="200" fill="none" stroke="#C69C49" stroke-width="2" opacity="0.6">
|
27 |
+
<animate attributeName="stroke-dasharray" values="0 800;800 0;0 800" dur="8s" repeatCount="indefinite"/>
|
28 |
+
</rect>
|
29 |
+
</svg>
|
30 |
+
</div>
|
31 |
+
|
32 |
+
<div class="container-custom relative z-10">
|
33 |
+
<div class="text-center px-4" data-aos="fade-up" data-aos-duration="1000">
|
34 |
+
<!-- Logo Animation -->
|
35 |
+
<div class="mb-8" data-aos="zoom-in" data-aos-delay="200">
|
36 |
+
<div class="inline-flex items-center justify-center mb-6">
|
37 |
+
<img src="/logo.jpg" alt="Blueprint Engineering Consultancy Logo" class="h-28 w-auto animate-pulse-slow" />
|
38 |
+
</div>
|
39 |
+
</div>
|
40 |
+
|
41 |
+
<!-- Hero Title -->
|
42 |
+
<h1 class="text-4xl md:text-6xl lg:text-7xl font-bold text-white mb-6 leading-tight" data-aos="fade-up" data-aos-delay="400">
|
43 |
+
<span class="block text-blueprint-accent animate-fade-in">{t('hero.title')}</span>
|
44 |
+
<span class="block text-2xl md:text-3xl lg:text-4xl font-normal mt-2 text-blueprint-accent">شركة بلو برينت للاستشارات الهندسية</span>
|
45 |
+
</h1>
|
46 |
+
|
47 |
+
<!-- Hero Description -->
|
48 |
+
<p class="text-xl md:text-2xl text-blueprint-light max-w-4xl mx-auto mb-8 leading-relaxed" data-aos="fade-up" data-aos-delay="600">
|
49 |
+
{t('hero.description')}
|
50 |
+
</p>
|
51 |
+
|
52 |
+
<!-- CTA Buttons -->
|
53 |
+
<div class="flex flex-col sm:flex-row gap-4 justify-center items-center" data-aos="fade-up" data-aos-delay="800">
|
54 |
+
<a href="#contact" class="btn-primary text-lg px-8 py-4 inline-flex items-center space-x-2 rtl:space-x-reverse group">
|
55 |
+
<MessageCircle class="w-5 h-5" />
|
56 |
+
<span>{t('hero.cta.consultation')}</span>
|
57 |
+
</a>
|
58 |
+
<a href="#services" class="btn-secondary text-lg px-8 py-4 inline-flex items-center space-x-2 rtl:space-x-reverse text-white border-white hover:bg-white hover:text-blueprint-primary group">
|
59 |
+
<span>{t('hero.cta.services')}</span>
|
60 |
+
<ArrowRight class={`w-5 h-5 transition-transform group-hover:translate-x-1 ${isRtl ? 'rtl-flip' : ''}`} />
|
61 |
+
</a>
|
62 |
+
</div>
|
63 |
+
|
64 |
+
<!-- Scroll Indicator -->
|
65 |
+
<div class="absolute bottom-8 left-1/2 transform -translate-x-1/2" data-aos="fade-up" data-aos-delay="1000">
|
66 |
+
<div class="w-6 h-10 border-2 border-white/50 rounded-full flex justify-center">
|
67 |
+
<div class="w-1 h-3 bg-blueprint-accent rounded-full mt-2 animate-bounce"></div>
|
68 |
+
</div>
|
69 |
+
</div>
|
70 |
+
</div>
|
71 |
+
</div>
|
72 |
+
</section>
|
73 |
+
|
74 |
+
<style>
|
75 |
+
@keyframes typewriter {
|
76 |
+
from {
|
77 |
+
width: 0;
|
78 |
+
}
|
79 |
+
to {
|
80 |
+
width: 100%;
|
81 |
+
}
|
82 |
+
}
|
83 |
+
|
84 |
+
.typewriter {
|
85 |
+
overflow: hidden;
|
86 |
+
white-space: nowrap;
|
87 |
+
animation: typewriter 4s steps(40, end) 1s 1 normal both;
|
88 |
+
}
|
89 |
+
</style>
|
src/components/LanguageSelector.astro
ADDED
@@ -0,0 +1,74 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
---
|
2 |
+
import { languages, getLangFromUrl } from '../i18n/ui';
|
3 |
+
import { Globe } from 'lucide-astro';
|
4 |
+
|
5 |
+
const lang = getLangFromUrl(Astro.url);
|
6 |
+
const currentPath = Astro.url.pathname;
|
7 |
+
---
|
8 |
+
|
9 |
+
<div class="relative" data-language-selector>
|
10 |
+
<button class="flex items-center space-x-2 rtl:space-x-reverse text-white hover:text-blueprint-accent transition-colors duration-300 p-2 rounded-lg hover:bg-white/10" data-language-button>
|
11 |
+
<Globe class="w-5 h-5" />
|
12 |
+
<span class="text-sm font-medium hidden sm:block">{languages[lang]}</span>
|
13 |
+
</button>
|
14 |
+
|
15 |
+
<div class="absolute top-full right-0 mt-2 w-32 bg-white dark:bg-blueprint-primary rounded-lg shadow-xl border border-blueprint-accent/20 hidden z-50" data-language-dropdown>
|
16 |
+
<div class="py-2">
|
17 |
+
{Object.entries(languages).map(([code, name]) => {
|
18 |
+
return (
|
19 |
+
<button
|
20 |
+
class={`block w-full text-left px-4 py-2 text-sm transition-colors duration-200 ${
|
21 |
+
code === lang
|
22 |
+
? 'bg-blueprint-accent text-white'
|
23 |
+
: 'text-blueprint-dark dark:text-white hover:bg-blueprint-light dark:hover:bg-blueprint-accent/20'
|
24 |
+
}`}
|
25 |
+
data-lang={code}
|
26 |
+
>
|
27 |
+
{name}
|
28 |
+
</button>
|
29 |
+
);
|
30 |
+
})}
|
31 |
+
</div>
|
32 |
+
</div>
|
33 |
+
</div>
|
34 |
+
|
35 |
+
<script>
|
36 |
+
// Language selector dropdown
|
37 |
+
const languageSelector = document.querySelector('[data-language-selector]');
|
38 |
+
const languageButton = document.querySelector('[data-language-button]');
|
39 |
+
const languageDropdown = document.querySelector('[data-language-dropdown]');
|
40 |
+
|
41 |
+
languageButton?.addEventListener('click', (e) => {
|
42 |
+
e.stopPropagation();
|
43 |
+
languageDropdown?.classList.toggle('hidden');
|
44 |
+
});
|
45 |
+
|
46 |
+
document.addEventListener('click', (e) => {
|
47 |
+
if (!languageSelector?.contains(e.target as Node)) {
|
48 |
+
languageDropdown?.classList.add('hidden');
|
49 |
+
}
|
50 |
+
});
|
51 |
+
|
52 |
+
// Handle language selection
|
53 |
+
languageDropdown?.addEventListener('click', (e) => {
|
54 |
+
const target = e.target as HTMLElement;
|
55 |
+
const selectedLang = target.getAttribute('data-lang');
|
56 |
+
|
57 |
+
if (selectedLang) {
|
58 |
+
const currentPath = window.location.pathname;
|
59 |
+
|
60 |
+
let newPath;
|
61 |
+
if (selectedLang === 'ar') {
|
62 |
+
// Going to Arabic (default) - remove /en prefix
|
63 |
+
newPath = currentPath === '/en' || currentPath === '/en/' ? '/' : currentPath.replace('/en', '') || '/';
|
64 |
+
} else {
|
65 |
+
// Going to English - add /en prefix
|
66 |
+
newPath = currentPath === '/' ? '/en' : `/en${currentPath}`;
|
67 |
+
}
|
68 |
+
|
69 |
+
window.location.href = newPath;
|
70 |
+
}
|
71 |
+
|
72 |
+
languageDropdown.classList.add('hidden');
|
73 |
+
});
|
74 |
+
</script>
|
src/components/Services.astro
ADDED
@@ -0,0 +1,133 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
---
|
2 |
+
import { getLangFromUrl, useTranslations } from '../i18n/ui';
|
3 |
+
import { Building, Zap, FileText, Shield, Users, CheckCircle } from 'lucide-astro';
|
4 |
+
|
5 |
+
const lang = getLangFromUrl(Astro.url);
|
6 |
+
const t = useTranslations(lang);
|
7 |
+
|
8 |
+
const services = [
|
9 |
+
{
|
10 |
+
icon: Building,
|
11 |
+
title: t('services.architectural.title'),
|
12 |
+
description: t('services.architectural.description'),
|
13 |
+
features: [
|
14 |
+
lang === 'ar' ? 'مخططات معمارية مبدئية ونهائية' : 'Preliminary and final architectural plans',
|
15 |
+
lang === 'ar' ? 'تصميم الواجهات والمقاطع' : 'Facade and section design',
|
16 |
+
lang === 'ar' ? 'الالتزام بكود البناء المحلي' : 'Compliance with local building codes'
|
17 |
+
]
|
18 |
+
},
|
19 |
+
{
|
20 |
+
icon: Shield,
|
21 |
+
title: t('services.structural.title'),
|
22 |
+
description: t('services.structural.description'),
|
23 |
+
features: [
|
24 |
+
lang === 'ar' ? 'تحليل إنشائي متكامل' : 'Comprehensive structural analysis',
|
25 |
+
lang === 'ar' ? 'مخططات حديد التسليح' : 'Reinforcement steel plans',
|
26 |
+
lang === 'ar' ? 'التوافق مع شروط السلامة' : 'Compliance with safety requirements'
|
27 |
+
]
|
28 |
+
},
|
29 |
+
{
|
30 |
+
icon: Zap,
|
31 |
+
title: t('services.mep.title'),
|
32 |
+
description: t('services.mep.description'),
|
33 |
+
features: [
|
34 |
+
lang === 'ar' ? 'أنظمة الكهرباء والإنارة' : 'Electrical and lighting systems',
|
35 |
+
lang === 'ar' ? 'التكييف والتهوية' : 'HVAC and ventilation',
|
36 |
+
lang === 'ar' ? 'المياه والصرف الصحي' : 'Water and drainage systems'
|
37 |
+
]
|
38 |
+
},
|
39 |
+
{
|
40 |
+
icon: FileText,
|
41 |
+
title: t('services.permit.title'),
|
42 |
+
description: t('services.permit.description'),
|
43 |
+
features: [
|
44 |
+
lang === 'ar' ? 'تجهيز ملف المشروع' : 'Project file preparation',
|
45 |
+
lang === 'ar' ? 'التقديم الإلكتروني' : 'Electronic submission',
|
46 |
+
lang === 'ar' ? 'متابعة الإجراءات' : 'Procedure follow-up'
|
47 |
+
]
|
48 |
+
},
|
49 |
+
{
|
50 |
+
icon: Users,
|
51 |
+
title: t('services.supervision.title'),
|
52 |
+
description: t('services.supervision.description'),
|
53 |
+
features: [
|
54 |
+
lang === 'ar' ? 'زيارات ميدانية دورية' : 'Regular site visits',
|
55 |
+
lang === 'ar' ? 'تقارير إشراف' : 'Supervision reports',
|
56 |
+
lang === 'ar' ? 'مراجعة المخططات' : 'Plan reviews'
|
57 |
+
]
|
58 |
+
},
|
59 |
+
{
|
60 |
+
icon: CheckCircle,
|
61 |
+
title: t('services.completion.title'),
|
62 |
+
description: t('services.completion.description'),
|
63 |
+
features: [
|
64 |
+
lang === 'ar' ? 'مخططات As Built' : 'As-built drawings',
|
65 |
+
lang === 'ar' ? 'شهادة الإنجاز' : 'Completion certificate',
|
66 |
+
lang === 'ar' ? 'اعتماد الجهات الرسمية' : 'Official authority approval'
|
67 |
+
]
|
68 |
+
}
|
69 |
+
];
|
70 |
+
---
|
71 |
+
|
72 |
+
<section id="services" class="section-padding bg-white dark:bg-blueprint-primary">
|
73 |
+
<div class="container-custom">
|
74 |
+
<!-- Section Header -->
|
75 |
+
<div class="text-center mb-16" data-aos="fade-up">
|
76 |
+
<h2 class="text-4xl md:text-5xl font-bold text-blueprint-primary dark:text-white mb-6">
|
77 |
+
{t('services.title')}
|
78 |
+
</h2>
|
79 |
+
<p class="text-xl text-blueprint-dark dark:text-blueprint-light max-w-3xl mx-auto">
|
80 |
+
{t('services.description')}
|
81 |
+
</p>
|
82 |
+
</div>
|
83 |
+
|
84 |
+
<!-- Services Grid -->
|
85 |
+
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8">
|
86 |
+
{services.map((service, index) => (
|
87 |
+
<div
|
88 |
+
class="group bg-blueprint-light dark:bg-blueprint-primary/50 rounded-2xl p-8 shadow-lg hover:shadow-2xl transition-all duration-500 hover:-translate-y-2 border border-transparent hover:border-blueprint-accent/30"
|
89 |
+
data-aos="fade-up"
|
90 |
+
data-aos-delay={index * 100}
|
91 |
+
>
|
92 |
+
<!-- Icon -->
|
93 |
+
<div class="flex items-center justify-center w-16 h-16 bg-blueprint-accent/20 rounded-xl mb-6 group-hover:bg-blueprint-accent group-hover:text-white transition-all duration-300">
|
94 |
+
<service.icon class="w-8 h-8 text-blueprint-accent group-hover:text-white transition-colors duration-300" />
|
95 |
+
</div>
|
96 |
+
|
97 |
+
<!-- Title -->
|
98 |
+
<h3 class="text-2xl font-bold text-blueprint-primary dark:text-white mb-4 group-hover:text-blueprint-accent transition-colors duration-300">
|
99 |
+
{service.title}
|
100 |
+
</h3>
|
101 |
+
|
102 |
+
<!-- Description -->
|
103 |
+
<p class="text-blueprint-dark dark:text-blueprint-light mb-6 leading-relaxed">
|
104 |
+
{service.description}
|
105 |
+
</p>
|
106 |
+
|
107 |
+
<!-- Features -->
|
108 |
+
<ul class="space-y-2">
|
109 |
+
{service.features.map((feature) => (
|
110 |
+
<li class="flex items-start space-x-2 rtl:space-x-reverse text-sm text-blueprint-dark dark:text-blueprint-light">
|
111 |
+
<CheckCircle class="w-4 h-4 text-blueprint-accent mt-0.5 flex-shrink-0" />
|
112 |
+
<span>{feature}</span>
|
113 |
+
</li>
|
114 |
+
))}
|
115 |
+
</ul>
|
116 |
+
|
117 |
+
<!-- Hover Effect -->
|
118 |
+
<div class="absolute inset-0 bg-gradient-to-r from-blueprint-accent/5 to-transparent opacity-0 group-hover:opacity-100 transition-opacity duration-300 rounded-2xl"></div>
|
119 |
+
</div>
|
120 |
+
))}
|
121 |
+
</div>
|
122 |
+
|
123 |
+
<!-- CTA Section -->
|
124 |
+
<div class="text-center mt-16" data-aos="fade-up" data-aos-delay="600">
|
125 |
+
<p class="text-lg text-blueprint-dark dark:text-blueprint-light mb-8">
|
126 |
+
{t('services.cta.question')}
|
127 |
+
</p>
|
128 |
+
<a href="#contact" class="btn-primary text-lg px-8 py-4 inline-flex items-center space-x-2 rtl:space-x-reverse">
|
129 |
+
<span>{t('services.cta.button')}</span>
|
130 |
+
</a>
|
131 |
+
</div>
|
132 |
+
</div>
|
133 |
+
</section>
|
src/components/ThemeToggle.astro
ADDED
@@ -0,0 +1,74 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
---
|
2 |
+
import { Sun, Moon } from 'lucide-astro';
|
3 |
+
---
|
4 |
+
|
5 |
+
<button
|
6 |
+
class="relative p-2 text-white hover:text-blueprint-accent transition-colors duration-300 rounded-lg hover:bg-white/10 overflow-hidden"
|
7 |
+
data-theme-toggle
|
8 |
+
aria-label="Toggle dark/light theme"
|
9 |
+
>
|
10 |
+
<div class="relative w-5 h-5">
|
11 |
+
<Sun class="w-5 h-5 absolute inset-0 transition-all duration-500 transform" data-sun-icon />
|
12 |
+
<Moon class="w-5 h-5 absolute inset-0 transition-all duration-500 transform" data-moon-icon />
|
13 |
+
</div>
|
14 |
+
</button>
|
15 |
+
|
16 |
+
<script>
|
17 |
+
function initTheme() {
|
18 |
+
const themeToggle = document.querySelector('[data-theme-toggle]');
|
19 |
+
const htmlElement = document.documentElement;
|
20 |
+
const sunIcon = document.querySelector('[data-sun-icon]') as HTMLElement;
|
21 |
+
const moonIcon = document.querySelector('[data-moon-icon]') as HTMLElement;
|
22 |
+
|
23 |
+
// Get saved theme or default to dark
|
24 |
+
const savedTheme = localStorage.getItem('theme') || 'dark';
|
25 |
+
|
26 |
+
// Function to update icons with animation
|
27 |
+
function updateIcons(isDark: boolean) {
|
28 |
+
if (isDark) {
|
29 |
+
// Dark mode - show moon, hide sun
|
30 |
+
sunIcon.style.transform = 'rotate(180deg) scale(0)';
|
31 |
+
sunIcon.style.opacity = '0';
|
32 |
+
moonIcon.style.transform = 'rotate(0deg) scale(1)';
|
33 |
+
moonIcon.style.opacity = '1';
|
34 |
+
} else {
|
35 |
+
// Light mode - show sun, hide moon
|
36 |
+
moonIcon.style.transform = 'rotate(-180deg) scale(0)';
|
37 |
+
moonIcon.style.opacity = '0';
|
38 |
+
sunIcon.style.transform = 'rotate(0deg) scale(1)';
|
39 |
+
sunIcon.style.opacity = '1';
|
40 |
+
}
|
41 |
+
}
|
42 |
+
|
43 |
+
// Apply theme
|
44 |
+
if (savedTheme === 'dark') {
|
45 |
+
htmlElement.classList.add('dark');
|
46 |
+
updateIcons(true);
|
47 |
+
} else {
|
48 |
+
htmlElement.classList.remove('dark');
|
49 |
+
updateIcons(false);
|
50 |
+
}
|
51 |
+
|
52 |
+
// Theme toggle functionality
|
53 |
+
themeToggle?.addEventListener('click', () => {
|
54 |
+
const isDark = htmlElement.classList.contains('dark');
|
55 |
+
|
56 |
+
if (isDark) {
|
57 |
+
htmlElement.classList.remove('dark');
|
58 |
+
localStorage.setItem('theme', 'light');
|
59 |
+
updateIcons(false);
|
60 |
+
} else {
|
61 |
+
htmlElement.classList.add('dark');
|
62 |
+
localStorage.setItem('theme', 'dark');
|
63 |
+
updateIcons(true);
|
64 |
+
}
|
65 |
+
});
|
66 |
+
}
|
67 |
+
|
68 |
+
// Initialize on DOM load
|
69 |
+
if (document.readyState === 'loading') {
|
70 |
+
document.addEventListener('DOMContentLoaded', initTheme);
|
71 |
+
} else {
|
72 |
+
initTheme();
|
73 |
+
}
|
74 |
+
</script>
|
src/components/Workflow.astro
ADDED
@@ -0,0 +1,226 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
---
|
2 |
+
import { getLangFromUrl, useTranslations } from '../i18n/ui';
|
3 |
+
import {
|
4 |
+
MessageCircle,
|
5 |
+
Package,
|
6 |
+
FileText,
|
7 |
+
MapPin,
|
8 |
+
Drafting,
|
9 |
+
Eye,
|
10 |
+
CheckCircle,
|
11 |
+
Building,
|
12 |
+
FileCheck,
|
13 |
+
Calculator,
|
14 |
+
Handshake,
|
15 |
+
HardHat,
|
16 |
+
Award,
|
17 |
+
Wrench,
|
18 |
+
ClipboardCheck
|
19 |
+
} from 'lucide-astro';
|
20 |
+
|
21 |
+
const lang = getLangFromUrl(Astro.url);
|
22 |
+
const t = useTranslations(lang);
|
23 |
+
|
24 |
+
const workflowSteps = [
|
25 |
+
{
|
26 |
+
number: '1',
|
27 |
+
icon: MessageCircle,
|
28 |
+
title: lang === 'ar' ? 'طلب الاستشارة الأولية' : 'Initial Consultation Request',
|
29 |
+
description: lang === 'ar'
|
30 |
+
? 'نستمع لرؤيتك، نُحلل احتياجاتك، ونقدم لك استشارة مهنية أولية توجهك للبداية الصحيحة.'
|
31 |
+
: 'We listen to your vision, analyze your needs, and provide you with an initial professional consultation to guide you to the right start.'
|
32 |
+
},
|
33 |
+
{
|
34 |
+
number: '2',
|
35 |
+
icon: Package,
|
36 |
+
title: lang === 'ar' ? 'اختيار الباقة المناسبة (تصميم + إشراف)' : 'Choosing the Right Package (Design + Supervision)',
|
37 |
+
description: lang === 'ar'
|
38 |
+
? 'نُقدّم لك خيارات باقات التصميم والإشراف حسب طبيعة مشروعك وميزانيتك، مع شرح الخدمات التي تتضمنها كل باقة.'
|
39 |
+
: 'We offer you design and supervision package options according to your project nature and budget, with explanation of services included in each package.'
|
40 |
+
},
|
41 |
+
{
|
42 |
+
number: '3',
|
43 |
+
icon: FileText,
|
44 |
+
title: lang === 'ar' ? 'توقيع عقد التصميم' : 'Design Contract Signing',
|
45 |
+
description: lang === 'ar'
|
46 |
+
? 'توقيع عقد رسمي شامل يوضح نطاق العمل، المخرجات، والجدول الزمني.'
|
47 |
+
: 'Signing a comprehensive official contract that clarifies the scope of work, deliverables, and timeline.'
|
48 |
+
},
|
49 |
+
{
|
50 |
+
number: '4',
|
51 |
+
icon: MapPin,
|
52 |
+
title: lang === 'ar' ? 'زيارة الموقع والرفع المساحي' : 'Site Visit and Survey',
|
53 |
+
description: lang === 'ar'
|
54 |
+
? 'إجراء رفع مساحي دقيق باستخدام أجهزة معتمدة وتوثيق معلومات الموقع.'
|
55 |
+
: 'Conducting accurate surveying using certified equipment and documenting site information.'
|
56 |
+
},
|
57 |
+
{
|
58 |
+
number: '5',
|
59 |
+
icon: Drafting,
|
60 |
+
title: lang === 'ar' ? 'إعداد التصاميم والمخططات الهندسية' : 'Preparing Designs and Engineering Plans',
|
61 |
+
description: lang === 'ar'
|
62 |
+
? 'تصميم معماري، إنشائي، كهربائي وميكانيكي متكامل، وفق كودات البلدية والاشتراطات البيئية والاستدامة.'
|
63 |
+
: 'Comprehensive architectural, structural, electrical and mechanical design, according to municipal codes and environmental and sustainability requirements.'
|
64 |
+
},
|
65 |
+
{
|
66 |
+
number: '6',
|
67 |
+
icon: Eye,
|
68 |
+
title: lang === 'ar' ? 'مراجعة المالك للمخططات' : 'Owner Review of Plans',
|
69 |
+
description: lang === 'ar'
|
70 |
+
? 'عرض المخططات للمالك لمراجعتها وإبداء الملاحظات الفنية والجمالية.'
|
71 |
+
: 'Presenting plans to the owner for review and providing technical and aesthetic comments.'
|
72 |
+
},
|
73 |
+
{
|
74 |
+
number: '7',
|
75 |
+
icon: CheckCircle,
|
76 |
+
title: lang === 'ar' ? 'الاعتماد النهائي من المالك' : 'Final Approval from Owner',
|
77 |
+
description: lang === 'ar'
|
78 |
+
? 'اعتماد نهائي للتصاميم المعدلة تمهيدًا للتقديم على الجهات المختصة.'
|
79 |
+
: 'Final approval of modified designs in preparation for submission to relevant authorities.'
|
80 |
+
},
|
81 |
+
{
|
82 |
+
number: '8',
|
83 |
+
icon: Building,
|
84 |
+
title: lang === 'ar' ? 'اعتماد الجهات الرسمية (بلدية رأس الخيمة)' : 'Official Authority Approval (Ras Al Khaimah Municipality)',
|
85 |
+
description: lang === 'ar'
|
86 |
+
? 'تقديم الملف الهندسي الكامل للبلدية ومتابعة الإجراءات حتى إصدار رخصة البناء.'
|
87 |
+
: 'Submitting the complete engineering file to the municipality and following up procedures until building permit issuance.'
|
88 |
+
},
|
89 |
+
{
|
90 |
+
number: '9',
|
91 |
+
icon: FileCheck,
|
92 |
+
title: lang === 'ar' ? 'إعداد وثائق المناقصة وطرحها' : 'Preparing and Issuing Tender Documents',
|
93 |
+
description: lang === 'ar'
|
94 |
+
? 'إعداد مستندات الطرح (مخططات تنفيذية، BOQ، مواصفات)، وتوزيعها على مقاولين معتمدين.'
|
95 |
+
: 'Preparing tender documents (shop drawings, BOQ, specifications), and distributing them to certified contractors.'
|
96 |
+
},
|
97 |
+
{
|
98 |
+
number: '10',
|
99 |
+
icon: Calculator,
|
100 |
+
title: lang === 'ar' ? 'تحليل العطاءات واختيار المقاول' : 'Bid Analysis and Contractor Selection',
|
101 |
+
description: lang === 'ar'
|
102 |
+
? 'تحليل العروض فنيًا وماليًا، وتقديم توصية مهنية للمالك لاختيار الأنسب.'
|
103 |
+
: 'Technical and financial analysis of offers, and providing professional recommendation to the owner for selecting the most suitable.'
|
104 |
+
},
|
105 |
+
{
|
106 |
+
number: '11',
|
107 |
+
icon: Handshake,
|
108 |
+
title: lang === 'ar' ? 'توقيع عقد التنفيذ وتسليم الموقع' : 'Execution Contract Signing and Site Handover',
|
109 |
+
description: lang === 'ar'
|
110 |
+
? 'توقيع عقد المقاولة، ثم إصدار محضر تسليم الموقع الرسمي بحضور الاستشاري.'
|
111 |
+
: 'Signing the contracting agreement, then issuing official site handover minutes in the presence of the consultant.'
|
112 |
+
},
|
113 |
+
{
|
114 |
+
number: '12',
|
115 |
+
icon: HardHat,
|
116 |
+
title: lang === 'ar' ? 'الإشراف الهندسي خلال التنفيذ' : 'Engineering Supervision During Execution',
|
117 |
+
description: lang === 'ar'
|
118 |
+
? 'متابعة التنفيذ ميدانيًا، إصدار تقارير إشراف، اعتماد المواد، والمطابقة مع كود البناء والسلامة.'
|
119 |
+
: 'Field monitoring of execution, issuing supervision reports, material approval, and compliance with building and safety codes.'
|
120 |
+
},
|
121 |
+
{
|
122 |
+
number: '13',
|
123 |
+
icon: Award,
|
124 |
+
title: lang === 'ar' ? 'استخراج شهادة الإنجاز' : 'Completion Certificate Issuance',
|
125 |
+
description: lang === 'ar'
|
126 |
+
? 'إعداد وتقديم طلب شهادة الإنجاز من بلدية رأس الخيمة، والتي تُستخدم لتوصيل الكهرباء، المياه، والاتصالات.'
|
127 |
+
: 'Preparing and submitting completion certificate application from Ras Al Khaimah Municipality, which is used for electricity, water, and telecommunications connection.'
|
128 |
+
},
|
129 |
+
{
|
130 |
+
number: '14',
|
131 |
+
icon: Wrench,
|
132 |
+
title: lang === 'ar' ? 'متابعة أعمال الصيانة خلال الضمان' : 'Maintenance Work Follow-up During Warranty',
|
133 |
+
description: lang === 'ar'
|
134 |
+
? 'الإشراف على تنفيذ الملاحظات المسجلة في الاستلام الابتدائي، والتأكد من جاهزية الموقع.'
|
135 |
+
: 'Supervising the implementation of observations recorded in preliminary handover, and ensuring site readiness.'
|
136 |
+
},
|
137 |
+
{
|
138 |
+
number: '15',
|
139 |
+
icon: ClipboardCheck,
|
140 |
+
title: lang === 'ar' ? 'الاستلام النهائي للمشروع' : 'Final Project Handover',
|
141 |
+
description: lang === 'ar'
|
142 |
+
? 'إصدار محضر استلام نهائي رسمي، وإغلاق الملف الهندسي للمشروع.'
|
143 |
+
: 'Issuing official final handover minutes, and closing the engineering file for the project.'
|
144 |
+
}
|
145 |
+
];
|
146 |
+
---
|
147 |
+
|
148 |
+
<section id="workflow" class="section-padding bg-white dark:bg-blueprint-primary">
|
149 |
+
<div class="container-custom">
|
150 |
+
<!-- Section Header -->
|
151 |
+
<div class="text-center mb-16" data-aos="fade-up">
|
152 |
+
<h2 class="text-4xl md:text-5xl font-bold text-blueprint-primary dark:text-white mb-6">
|
153 |
+
{t('workflow.title')}
|
154 |
+
</h2>
|
155 |
+
<h3 class="text-2xl md:text-3xl font-semibold text-blueprint-accent mb-6">
|
156 |
+
{t('workflow.subtitle')}
|
157 |
+
</h3>
|
158 |
+
<div class="w-24 h-1 bg-blueprint-accent mx-auto mb-8"></div>
|
159 |
+
<p class="text-xl text-blueprint-dark dark:text-blueprint-light max-w-4xl mx-auto">
|
160 |
+
{t('workflow.description')}
|
161 |
+
</p>
|
162 |
+
</div>
|
163 |
+
|
164 |
+
<!-- Timeline -->
|
165 |
+
<div class="relative">
|
166 |
+
<!-- Timeline Line -->
|
167 |
+
<div class="absolute left-1/2 transform -translate-x-1/2 w-1 bg-blueprint-accent/30 h-full hidden lg:block"></div>
|
168 |
+
|
169 |
+
<!-- Steps -->
|
170 |
+
<div class="space-y-12">
|
171 |
+
{workflowSteps.map((step, index) => (
|
172 |
+
<div
|
173 |
+
class={`flex items-center ${index % 2 === 0 ? 'lg:flex-row' : 'lg:flex-row-reverse'} flex-col lg:space-x-8 rtl:lg:space-x-reverse`}
|
174 |
+
data-aos="fade-up"
|
175 |
+
data-aos-delay={index * 100}
|
176 |
+
>
|
177 |
+
<!-- Content -->
|
178 |
+
<div class={`flex-1 ${index % 2 === 0 ? 'lg:text-right rtl:lg:text-left' : 'lg:text-left rtl:lg:text-right'} text-center lg:text-left`}>
|
179 |
+
<div class="bg-blueprint-light dark:bg-slate-800 p-6 rounded-2xl shadow-lg hover:shadow-xl transition-shadow duration-300 border border-blueprint-accent/10">
|
180 |
+
<div class="flex items-center justify-center lg:hidden mb-4">
|
181 |
+
<div class="flex items-center justify-center w-16 h-16 bg-blueprint-accent rounded-full text-white font-bold text-xl">
|
182 |
+
{step.number}
|
183 |
+
</div>
|
184 |
+
</div>
|
185 |
+
|
186 |
+
<h3 class="text-xl font-bold text-blueprint-primary dark:text-white mb-3">
|
187 |
+
{step.title}
|
188 |
+
</h3>
|
189 |
+
<p class="text-blueprint-dark dark:text-blueprint-light leading-relaxed">
|
190 |
+
{step.description}
|
191 |
+
</p>
|
192 |
+
</div>
|
193 |
+
</div>
|
194 |
+
|
195 |
+
<!-- Timeline Node -->
|
196 |
+
<div class="relative flex-shrink-0 hidden lg:flex">
|
197 |
+
<div class="flex items-center justify-center w-20 h-20 bg-blueprint-accent rounded-full text-white font-bold text-xl shadow-lg z-10">
|
198 |
+
{step.number}
|
199 |
+
</div>
|
200 |
+
<div class="absolute inset-0 flex items-center justify-center w-20 h-20 bg-blueprint-accent/20 rounded-full animate-pulse"></div>
|
201 |
+
</div>
|
202 |
+
|
203 |
+
<!-- Empty space for alternating layout -->
|
204 |
+
<div class="flex-1 hidden lg:block"></div>
|
205 |
+
</div>
|
206 |
+
))}
|
207 |
+
</div>
|
208 |
+
</div>
|
209 |
+
|
210 |
+
<!-- Bottom CTA -->
|
211 |
+
<div class="text-center mt-16" data-aos="fade-up" data-aos-delay="800">
|
212 |
+
<div class="bg-blueprint-accent/10 rounded-2xl p-8 border border-blueprint-accent/20">
|
213 |
+
<h3 class="text-2xl font-bold text-blueprint-primary dark:text-white mb-4">
|
214 |
+
{t('workflow.cta.title')}
|
215 |
+
</h3>
|
216 |
+
<p class="text-lg text-blueprint-dark dark:text-blueprint-light mb-6">
|
217 |
+
{t('workflow.cta.subtitle')}
|
218 |
+
</p>
|
219 |
+
<a href="#contact" class="btn-primary text-lg px-8 py-4 inline-flex items-center space-x-2 rtl:space-x-reverse">
|
220 |
+
<MessageCircle class="w-5 h-5" />
|
221 |
+
<span>{t('workflow.cta.button')}</span>
|
222 |
+
</a>
|
223 |
+
</div>
|
224 |
+
</div>
|
225 |
+
</div>
|
226 |
+
</section>
|
src/i18n/ui.ts
ADDED
@@ -0,0 +1,265 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
export const languages = {
|
2 |
+
ar: 'العربية',
|
3 |
+
en: 'English'
|
4 |
+
};
|
5 |
+
|
6 |
+
export const defaultLang = 'ar';
|
7 |
+
|
8 |
+
export const ui = {
|
9 |
+
ar: {
|
10 |
+
// Navigation
|
11 |
+
'nav.about': 'من نحن',
|
12 |
+
'nav.services': 'خدماتنا',
|
13 |
+
'nav.workflow': 'رحلتك مع بلو برينت',
|
14 |
+
'nav.contact': 'تواصل معنا',
|
15 |
+
|
16 |
+
// Hero Section
|
17 |
+
'hero.title': 'الابتكار برؤية، التنفيذ بدقة',
|
18 |
+
'hero.subtitle': 'شركة بلو برينت للاستشارات الهندسية',
|
19 |
+
'hero.description': 'نقدم حلول هندسية متكاملة من التصميم المعماري إلى الإشراف على التنفيذ، مع التزام كامل بمعايير الجودة والسلامة.',
|
20 |
+
'hero.cta.consultation': 'احجز استشارة مجانية',
|
21 |
+
'hero.cta.services': 'تصفح خدماتنا',
|
22 |
+
|
23 |
+
// About Section
|
24 |
+
'about.title': 'من نحن',
|
25 |
+
'about.subtitle': 'شركة بلو برينت للاستشارات الهندسية',
|
26 |
+
'about.description.1': 'في عالم تتسارع فيه التحولات العمرانية والهندسية، برزت "بلو برينت للاستشارات الهندسية" كمكتب استثنائي يجمع بين الفكر الإبداعي والدقة الفنية والالتزام التشغيلي.',
|
27 |
+
'about.description.2': 'نحن لسنا مجرد مكتب هندسي يضع مخططات على ورق؛ نحن نؤمن أن كل خط في المخطط هو بداية لحياة، وكل زاوية تحمل رؤية، وكل مساحة تروي حكاية تصميم متقن.',
|
28 |
+
'about.description.3': 'من قلب إمارة رأس الخيمة، ننطلق بشغف لتقديم حلول هندسية متكاملة تُلبي أعلى المعايير وتتفوق على التوقعات.',
|
29 |
+
'about.vision.title': 'رؤيتنا',
|
30 |
+
'about.vision.description': 'أن نصبح الاسم الأول في الاستشارات الهندسية عندما يتعلق الأمر بالثقة، الذوق، والتميز',
|
31 |
+
'about.mission.title': 'مهمتنا',
|
32 |
+
'about.mission.description': 'تقديم حلول هندسية متكاملة تجمع بين الابتكار والجودة والالتزام بالمعايير العالمية',
|
33 |
+
'about.values.title': 'قيمنا الأساسية',
|
34 |
+
'about.values.precision.title': 'الدقة',
|
35 |
+
'about.values.precision.description': 'التفاصيل هي ما يصنع الفارق في كل مشروع',
|
36 |
+
'about.values.innovation.title': 'الابتكار',
|
37 |
+
'about.values.innovation.description': 'لا نكرر، بل نبدع ونطور حلول جديدة',
|
38 |
+
'about.values.quality.title': 'الجودة',
|
39 |
+
'about.values.quality.description': 'التزام كامل بأعلى معايير الجودة والسلامة',
|
40 |
+
'about.why.title': 'لماذا تختار "بلو برينت"؟',
|
41 |
+
'about.why.point1': 'نُترجم الطموحات إلى تصميم والتصميم إلى قيمة',
|
42 |
+
'about.why.point2': 'نجمع بين أحدث التقنيات وأفضل المعايير',
|
43 |
+
'about.why.point3': 'نرافقك من أول فكرة حتى شهادة الإنجاز',
|
44 |
+
|
45 |
+
// Services Section
|
46 |
+
'services.title': 'خدماتنا الهندسية',
|
47 |
+
'services.description': 'نقدم مجموعة شاملة من الخدمات الهندسية المتخصصة لضمان نجاح مشروعك من البداية إلى النهاية',
|
48 |
+
'services.architectural.title': 'التصميم المعماري',
|
49 |
+
'services.architectural.description': 'تصميم معماري احترافي يشمل المخططات والواجهات وفقاً لأحدث معايير البناء',
|
50 |
+
'services.structural.title': 'التصميم الإنشائي',
|
51 |
+
'services.structural.description': 'تحليل إنشائي متكامل وتصميم الأساسات والأعمدة والأسقف بكفاءة عالية',
|
52 |
+
'services.mep.title': 'التصميم الكهربائي والميكانيكي',
|
53 |
+
'services.mep.description': 'أنظمة كهربائية وميكانيكية متطورة تشمل التكييف والإنارة والصرف الصحي',
|
54 |
+
'services.permit.title': 'استخراج رخصة البناء',
|
55 |
+
'services.permit.description': 'تجهيز ملف المشروع ومتابعة الإجراءات حتى الحصول على الرخصة رسمياً',
|
56 |
+
'services.supervision.title': 'الإشراف الهندسي',
|
57 |
+
'services.supervision.description': 'إشراف ميداني دوري وتقارير متابعة لضمان التزام المقاول بالمخططات',
|
58 |
+
'services.completion.title': 'شهادة الإنجاز',
|
59 |
+
'services.completion.description': 'مخططات ما بعد التنفيذ وإصدار شهادة الإنجاز من الجهات المختصة',
|
60 |
+
'services.cta.question': 'هل تحتاج إلى استشارة مخصصة لمشروعك؟',
|
61 |
+
'services.cta.button': 'احجز استشارة مجانية',
|
62 |
+
|
63 |
+
// Workflow Section
|
64 |
+
'workflow.title': 'رحلتك مع بلو برينت',
|
65 |
+
'workflow.subtitle': 'من الفكرة إلى شهادة الإنجاز',
|
66 |
+
'workflow.description': 'في بلو برينت للاستشارات الهندسية، نرسم لك طريقًا واضحًا وآمنًا لإنجاز مشروعك من أول استشارة حتى آخر توقيع. نتّبع خطوات معتمدة هندسيًا وإداريًا تتماشى مع أنظمة بلدية رأس الخيمة والجهات التنظيمية ذات العلاقة، ونرافقك في كل مرحلة.',
|
67 |
+
'workflow.cta.title': 'بلو برينت... نرسم لك مسارًا هندسيًا واضحًا',
|
68 |
+
'workflow.cta.subtitle': 'ونرافقك خطوة بخطوة حتى الإنجاز',
|
69 |
+
'workflow.cta.button': 'ابدأ رحلتك معنا',
|
70 |
+
|
71 |
+
// Contact Section
|
72 |
+
'contact.title': 'تواصل معنا',
|
73 |
+
'contact.description': 'نحن هنا لنرسم لك البداية. تواصل معنا للحصول على استشارة مجانية أو لمناقشة مشروعك',
|
74 |
+
'contact.info.title': 'معلومات التواصل',
|
75 |
+
'contact.address.title': 'العنوان',
|
76 |
+
'contact.address.text': 'سدروه بلو برينت – طابق الميزان – مكتب رقم M-01<br>إمارة رأس الخيمة – الإمارات العربية المتحدة',
|
77 |
+
'contact.address.map': 'عرض على الخريطة',
|
78 |
+
'contact.phone.title': 'الهاتف',
|
79 |
+
'contact.phone.note': '(متاح عبر الاتصال أو الواتساب)',
|
80 |
+
'contact.email.title': 'البريد الإلكتروني',
|
81 |
+
'contact.hours.title': 'أوقات العمل',
|
82 |
+
'contact.whatsapp.title': 'احجز استشارتك المجانية',
|
83 |
+
'contact.whatsapp.description': 'أرسل كلمة "استشارة" وسيتواصل معك أحد مهندسينا خلال 24 ساعة',
|
84 |
+
'contact.whatsapp.button': 'تواصل واتساب',
|
85 |
+
'contact.form.title': 'أرسل لنا رسالة',
|
86 |
+
'contact.form.name': 'الاسم الكامل',
|
87 |
+
'contact.form.name.placeholder': 'أدخل اسمك الكامل',
|
88 |
+
'contact.form.phone': 'رقم الهاتف',
|
89 |
+
'contact.form.email': 'البريد الإلكتروني',
|
90 |
+
'contact.form.project_type': 'نوع المشروع',
|
91 |
+
'contact.form.project_type.placeholder': 'اختر نوع المشروع',
|
92 |
+
'contact.form.project_type.residential': 'سكني',
|
93 |
+
'contact.form.project_type.commercial': 'تجاري',
|
94 |
+
'contact.form.project_type.industrial': 'صناعي',
|
95 |
+
'contact.form.project_type.renovation': 'تجديد',
|
96 |
+
'contact.form.project_type.consultation': 'استشارة',
|
97 |
+
'contact.form.message': 'تفاصيل المشروع',
|
98 |
+
'contact.form.message.placeholder': 'اكتب تفاصيل مشروعك أو استفسارك هنا...',
|
99 |
+
'contact.form.submit': 'إرسال الرسالة',
|
100 |
+
'contact.form.sending': 'جاري الإرسال...',
|
101 |
+
'contact.form.success.title': 'تم إرسال رسالتك بنجاح!',
|
102 |
+
'contact.form.success.message': 'شكراً لتواصلك معنا. سيقوم فريقنا بالرد عليك خلال 24 ساعة.',
|
103 |
+
'contact.form.error.title': 'حدث خطأ في الإرسال',
|
104 |
+
'contact.form.error.message': 'يرجى المحاولة مرة أخرى أو التواصل معنا عبر الواتساب.',
|
105 |
+
|
106 |
+
// Footer
|
107 |
+
'footer.description': 'نقدم حلول هندسية متكاملة من التصميم المعماري إلى الإشراف على التنفيذ، مع التزام كامل بمعايير الجودة والسلامة.',
|
108 |
+
'footer.quick_links': 'روابط سريعة',
|
109 |
+
'footer.contact_info': 'معلومات التواصل',
|
110 |
+
'footer.whatsapp': 'واتساب',
|
111 |
+
'footer.rights': 'جميع الحقوق محفوظة لشركة بلو برينت للاستشارات الهندسية',
|
112 |
+
'footer.language': 'اللغة:',
|
113 |
+
|
114 |
+
// Common
|
115 |
+
'common.read_more': 'اقرأ المزيد',
|
116 |
+
'common.learn_more': 'تعرف أكثر',
|
117 |
+
'common.get_started': 'ابدأ الآن',
|
118 |
+
'common.contact_us': 'تواصل معنا',
|
119 |
+
'common.view_all': 'عرض الكل',
|
120 |
+
'common.back_to_top': 'العودة للأعلى'
|
121 |
+
},
|
122 |
+
en: {
|
123 |
+
// Navigation
|
124 |
+
'nav.about': 'About Us',
|
125 |
+
'nav.services': 'Our Services',
|
126 |
+
'nav.workflow': 'Your Journey with Blueprint',
|
127 |
+
'nav.contact': 'Contact Us',
|
128 |
+
|
129 |
+
// Hero Section
|
130 |
+
'hero.title': 'Innovation with Vision, Execution with Precision',
|
131 |
+
'hero.subtitle': 'Blueprint Engineering Consultancy',
|
132 |
+
'hero.description': 'We provide comprehensive engineering solutions from architectural design to construction supervision, with full commitment to quality and safety standards.',
|
133 |
+
'hero.cta.consultation': 'Book Free Consultation',
|
134 |
+
'hero.cta.services': 'Explore Our Services',
|
135 |
+
|
136 |
+
// About Section
|
137 |
+
'about.title': 'About Us',
|
138 |
+
'about.subtitle': 'Blueprint Engineering Consultancy',
|
139 |
+
'about.description.1': 'In a world where urban and engineering transformations are accelerating, "Blueprint Engineering Consultancy" has emerged as an exceptional office that combines creative thinking, technical precision, and operational commitment.',
|
140 |
+
'about.description.2': 'We are not just an engineering office that puts plans on paper; we believe that every line in the plan is the beginning of life, every angle carries a vision, and every space tells the story of meticulous design.',
|
141 |
+
'about.description.3': 'From the heart of Ras Al Khaimah Emirate, we passionately deliver comprehensive engineering solutions that meet the highest standards and exceed expectations.',
|
142 |
+
'about.vision.title': 'Our Vision',
|
143 |
+
'about.vision.description': 'To become the first name in engineering consultancy when it comes to trust, taste, and excellence',
|
144 |
+
'about.mission.title': 'Our Mission',
|
145 |
+
'about.mission.description': 'Providing comprehensive engineering solutions that combine innovation, quality, and commitment to international standards',
|
146 |
+
'about.values.title': 'Our Core Values',
|
147 |
+
'about.values.precision.title': 'Precision',
|
148 |
+
'about.values.precision.description': 'Details make the difference in every project',
|
149 |
+
'about.values.innovation.title': 'Innovation',
|
150 |
+
'about.values.innovation.description': 'We don\'t repeat, we innovate and develop new solutions',
|
151 |
+
'about.values.quality.title': 'Quality',
|
152 |
+
'about.values.quality.description': 'Full commitment to highest quality and safety standards',
|
153 |
+
'about.why.title': 'Why Choose "Blueprint"?',
|
154 |
+
'about.why.point1': 'We translate ambitions into design and design into value',
|
155 |
+
'about.why.point2': 'We combine the latest technologies with the best standards',
|
156 |
+
'about.why.point3': 'We accompany you from the first idea to the completion certificate',
|
157 |
+
|
158 |
+
// Services Section
|
159 |
+
'services.title': 'Our Engineering Services',
|
160 |
+
'services.description': 'We provide a comprehensive range of specialized engineering services to ensure your project success from start to finish',
|
161 |
+
'services.architectural.title': 'Architectural Design',
|
162 |
+
'services.architectural.description': 'Professional architectural design including plans and facades according to latest building standards',
|
163 |
+
'services.structural.title': 'Structural Design',
|
164 |
+
'services.structural.description': 'Comprehensive structural analysis and design of foundations, columns and slabs with high efficiency',
|
165 |
+
'services.mep.title': 'MEP Design',
|
166 |
+
'services.mep.description': 'Advanced electrical and mechanical systems including HVAC, lighting and drainage',
|
167 |
+
'services.permit.title': 'Building Permit',
|
168 |
+
'services.permit.description': 'Project file preparation and procedure follow-up until obtaining official permit',
|
169 |
+
'services.supervision.title': 'Engineering Supervision',
|
170 |
+
'services.supervision.description': 'Regular field supervision and follow-up reports to ensure contractor compliance',
|
171 |
+
'services.completion.title': 'Completion Certificate',
|
172 |
+
'services.completion.description': 'As-built drawings and completion certificate issuance from relevant authorities',
|
173 |
+
'services.cta.question': 'Need a customized consultation for your project?',
|
174 |
+
'services.cta.button': 'Book Free Consultation',
|
175 |
+
|
176 |
+
// Workflow Section
|
177 |
+
'workflow.title': 'Your Journey with Blueprint',
|
178 |
+
'workflow.subtitle': 'From Idea to Completion Certificate',
|
179 |
+
'workflow.description': 'At Blueprint Engineering Consultancy, we draw you a clear and safe path to complete your project from the first consultation to the last signature. We follow engineering and administrative approved steps that align with Ras Al Khaimah Municipality systems and related regulatory bodies, and we accompany you at every stage.',
|
180 |
+
'workflow.cta.title': 'Blueprint... We draw you a clear engineering path',
|
181 |
+
'workflow.cta.subtitle': 'And we accompany you step by step until completion',
|
182 |
+
'workflow.cta.button': 'Start Your Journey With Us',
|
183 |
+
|
184 |
+
// Contact Section
|
185 |
+
'contact.title': 'Contact Us',
|
186 |
+
'contact.description': 'We are here to draw your beginning. Contact us for a free consultation or to discuss your project',
|
187 |
+
'contact.info.title': 'Contact Information',
|
188 |
+
'contact.address.title': 'Address',
|
189 |
+
'contact.address.text': 'Sadroh Blue Print - Mezzanine Floor - Office No. M-01<br>Ras Al Khaimah Emirate - United Arab Emirates',
|
190 |
+
'contact.address.map': 'View on Map',
|
191 |
+
'contact.phone.title': 'Phone',
|
192 |
+
'contact.phone.note': '(Available via call or WhatsApp)',
|
193 |
+
'contact.email.title': 'Email',
|
194 |
+
'contact.hours.title': 'Working Hours',
|
195 |
+
'contact.whatsapp.title': 'Book Your Free Consultation',
|
196 |
+
'contact.whatsapp.description': 'Send "Consultation" and one of our engineers will contact you within 24 hours',
|
197 |
+
'contact.whatsapp.button': 'WhatsApp Contact',
|
198 |
+
'contact.form.title': 'Send us a Message',
|
199 |
+
'contact.form.name': 'Full Name',
|
200 |
+
'contact.form.name.placeholder': 'Enter your full name',
|
201 |
+
'contact.form.phone': 'Phone Number',
|
202 |
+
'contact.form.email': 'Email Address',
|
203 |
+
'contact.form.project_type': 'Project Type',
|
204 |
+
'contact.form.project_type.placeholder': 'Select project type',
|
205 |
+
'contact.form.project_type.residential': 'Residential',
|
206 |
+
'contact.form.project_type.commercial': 'Commercial',
|
207 |
+
'contact.form.project_type.industrial': 'Industrial',
|
208 |
+
'contact.form.project_type.renovation': 'Renovation',
|
209 |
+
'contact.form.project_type.consultation': 'Consultation',
|
210 |
+
'contact.form.message': 'Project Details',
|
211 |
+
'contact.form.message.placeholder': 'Write your project details or inquiry here...',
|
212 |
+
'contact.form.submit': 'Send Message',
|
213 |
+
'contact.form.sending': 'Sending...',
|
214 |
+
'contact.form.success.title': 'Message sent successfully!',
|
215 |
+
'contact.form.success.message': 'Thank you for contacting us. Our team will respond within 24 hours.',
|
216 |
+
'contact.form.error.title': 'Error sending message',
|
217 |
+
'contact.form.error.message': 'Please try again or contact us via WhatsApp.',
|
218 |
+
|
219 |
+
// Footer
|
220 |
+
'footer.description': 'We provide comprehensive engineering solutions from architectural design to construction supervision, with full commitment to quality and safety standards.',
|
221 |
+
'footer.quick_links': 'Quick Links',
|
222 |
+
'footer.contact_info': 'Contact Info',
|
223 |
+
'footer.whatsapp': 'WhatsApp',
|
224 |
+
'footer.rights': 'All rights reserved to Blueprint Engineering Consultancy',
|
225 |
+
'footer.language': 'Language:',
|
226 |
+
|
227 |
+
// Common
|
228 |
+
'common.read_more': 'Read More',
|
229 |
+
'common.learn_more': 'Learn More',
|
230 |
+
'common.get_started': 'Get Started',
|
231 |
+
'common.contact_us': 'Contact Us',
|
232 |
+
'common.view_all': 'View All',
|
233 |
+
'common.back_to_top': 'Back to Top'
|
234 |
+
}
|
235 |
+
} as const;
|
236 |
+
|
237 |
+
export function getLangFromUrl(url: URL) {
|
238 |
+
const [, lang] = url.pathname.split('/');
|
239 |
+
if (lang in ui) return lang as keyof typeof ui;
|
240 |
+
return defaultLang;
|
241 |
+
}
|
242 |
+
|
243 |
+
export function useTranslations(lang: keyof typeof ui) {
|
244 |
+
return function t(key: keyof typeof ui[typeof defaultLang]) {
|
245 |
+
return ui[lang][key] || ui[defaultLang][key];
|
246 |
+
}
|
247 |
+
}
|
248 |
+
|
249 |
+
export function getRouteFromUrl(url: URL): string | undefined {
|
250 |
+
const pathname = new URL(url).pathname;
|
251 |
+
const parts = pathname?.split('/');
|
252 |
+
const path = parts.pop() || parts.pop();
|
253 |
+
|
254 |
+
if (path === undefined) {
|
255 |
+
return '/';
|
256 |
+
}
|
257 |
+
|
258 |
+
const currentLang = getLangFromUrl(url);
|
259 |
+
|
260 |
+
if (currentLang === defaultLang) {
|
261 |
+
return path ? `/${path}` : '/';
|
262 |
+
}
|
263 |
+
|
264 |
+
return `/${currentLang}${path ? `/${path}` : ''}`;
|
265 |
+
}
|
src/layouts/Layout.astro
ADDED
@@ -0,0 +1,142 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
---
|
2 |
+
import '../styles/global.css';
|
3 |
+
|
4 |
+
export interface Props {
|
5 |
+
title: string;
|
6 |
+
description?: string;
|
7 |
+
lang?: string;
|
8 |
+
}
|
9 |
+
|
10 |
+
const { title, description = "Blueprint Engineering Consultancy - Professional engineering services in Ras Al Khaimah", lang = "ar" } = Astro.props;
|
11 |
+
|
12 |
+
const isRtl = lang === 'ar' || lang === 'ur' || lang === 'ps';
|
13 |
+
---
|
14 |
+
|
15 |
+
<!DOCTYPE html>
|
16 |
+
<html lang={lang} dir={isRtl ? 'rtl' : 'ltr'} class="dark">
|
17 |
+
<head>
|
18 |
+
<meta charset="UTF-8" />
|
19 |
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
20 |
+
<meta name="description" content={description} />
|
21 |
+
<meta name="generator" content={Astro.generator} />
|
22 |
+
|
23 |
+
<!-- Open Graph / Facebook -->
|
24 |
+
<meta property="og:type" content="website" />
|
25 |
+
<meta property="og:url" content={Astro.url} />
|
26 |
+
<meta property="og:title" content={title} />
|
27 |
+
<meta property="og:description" content={description} />
|
28 |
+
<meta property="og:image" content="/og-image.jpg" />
|
29 |
+
|
30 |
+
<!-- Twitter -->
|
31 |
+
<meta property="twitter:card" content="summary_large_image" />
|
32 |
+
<meta property="twitter:url" content={Astro.url} />
|
33 |
+
<meta property="twitter:title" content={title} />
|
34 |
+
<meta property="twitter:description" content={description} />
|
35 |
+
<meta property="twitter:image" content="/og-image.jpg" />
|
36 |
+
|
37 |
+
<!-- Favicon -->
|
38 |
+
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
39 |
+
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png" />
|
40 |
+
|
41 |
+
<!-- Fonts -->
|
42 |
+
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
43 |
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
44 |
+
|
45 |
+
<!-- AOS CSS -->
|
46 |
+
<link rel="stylesheet" href="https://unpkg.com/[email protected]/dist/aos.css" />
|
47 |
+
|
48 |
+
<title>{title}</title>
|
49 |
+
</head>
|
50 |
+
<body class="bg-white dark:bg-blueprint-primary text-blueprint-dark dark:text-white">
|
51 |
+
<!-- Loading Screen -->
|
52 |
+
<div class="loading-overlay" id="loading">
|
53 |
+
<div class="blueprint-logo-loading">
|
54 |
+
<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
|
55 |
+
<rect x="10" y="10" width="80" height="60" stroke-width="3" fill="none"/>
|
56 |
+
<rect x="20" y="20" width="25" height="20" stroke-width="2" fill="none"/>
|
57 |
+
<rect x="55" y="20" width="25" height="20" stroke-width="2" fill="none"/>
|
58 |
+
<rect x="20" y="45" width="60" height="15" stroke-width="2" fill="none"/>
|
59 |
+
<circle cx="25" cy="85" r="3"/>
|
60 |
+
<circle cx="50" cy="85" r="3"/>
|
61 |
+
<circle cx="75" cy="85" r="3"/>
|
62 |
+
</svg>
|
63 |
+
</div>
|
64 |
+
</div>
|
65 |
+
|
66 |
+
<slot />
|
67 |
+
|
68 |
+
<!-- AOS JavaScript -->
|
69 |
+
<script src="https://unpkg.com/[email protected]/dist/aos.js" is:inline></script>
|
70 |
+
|
71 |
+
<script is:inline>
|
72 |
+
// Initialize AOS
|
73 |
+
AOS.init({
|
74 |
+
duration: 800,
|
75 |
+
easing: 'ease-out-cubic',
|
76 |
+
once: true,
|
77 |
+
offset: 50
|
78 |
+
});
|
79 |
+
|
80 |
+
// Loading screen
|
81 |
+
window.addEventListener('load', () => {
|
82 |
+
const loading = document.getElementById('loading');
|
83 |
+
if (loading) {
|
84 |
+
setTimeout(() => {
|
85 |
+
loading.classList.add('fade-out');
|
86 |
+
setTimeout(() => {
|
87 |
+
loading.style.display = 'none';
|
88 |
+
}, 500);
|
89 |
+
}, 1500);
|
90 |
+
}
|
91 |
+
});
|
92 |
+
|
93 |
+
// Smooth scrolling for anchor links
|
94 |
+
document.querySelectorAll('a[href^="#"]').forEach(anchor => {
|
95 |
+
anchor.addEventListener('click', function (e) {
|
96 |
+
e.preventDefault();
|
97 |
+
const target = document.querySelector(this.getAttribute('href'));
|
98 |
+
if (target) {
|
99 |
+
target.scrollIntoView({
|
100 |
+
behavior: 'smooth',
|
101 |
+
block: 'start'
|
102 |
+
});
|
103 |
+
}
|
104 |
+
});
|
105 |
+
});
|
106 |
+
|
107 |
+
// Auto-detect browser language and redirect if needed
|
108 |
+
function detectAndRedirectLanguage() {
|
109 |
+
const currentPath = window.location.pathname;
|
110 |
+
const supportedLangs = ['ar', 'en', 'zh', 'ja', 'ru', 'hi', 'ur', 'bn', 'ml', 'fr', 'fil', 'es', 'ta', 'ne', 'ps', 'si', 'id', 'te'];
|
111 |
+
const browserLang = navigator.language.split('-')[0];
|
112 |
+
|
113 |
+
// Check if we're on the root path and browser language is supported but not Arabic
|
114 |
+
if (currentPath === '/' && supportedLangs.includes(browserLang) && browserLang !== 'ar') {
|
115 |
+
// Don't redirect automatically, let user choose
|
116 |
+
return;
|
117 |
+
}
|
118 |
+
}
|
119 |
+
|
120 |
+
// Run language detection
|
121 |
+
detectAndRedirectLanguage();
|
122 |
+
|
123 |
+
// Add scroll-based header effects
|
124 |
+
let lastScrollY = window.scrollY;
|
125 |
+
const header = document.querySelector('header');
|
126 |
+
|
127 |
+
window.addEventListener('scroll', () => {
|
128 |
+
const currentScrollY = window.scrollY;
|
129 |
+
|
130 |
+
if (header) {
|
131 |
+
if (currentScrollY > 100) {
|
132 |
+
header.classList.add('bg-blueprint-primary/95');
|
133 |
+
} else {
|
134 |
+
header.classList.remove('bg-blueprint-primary/95');
|
135 |
+
}
|
136 |
+
}
|
137 |
+
|
138 |
+
lastScrollY = currentScrollY;
|
139 |
+
});
|
140 |
+
</script>
|
141 |
+
</body>
|
142 |
+
</html>
|
src/pages/[...lang]/index.astro
ADDED
@@ -0,0 +1,35 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
---
|
2 |
+
import Layout from '../../layouts/Layout.astro';
|
3 |
+
import Header from '../../components/Header.astro';
|
4 |
+
import Hero from '../../components/Hero.astro';
|
5 |
+
import About from '../../components/About.astro';
|
6 |
+
import Services from '../../components/Services.astro';
|
7 |
+
import Workflow from '../../components/Workflow.astro';
|
8 |
+
import Contact from '../../components/Contact.astro';
|
9 |
+
import Footer from '../../components/Footer.astro';
|
10 |
+
import { getLangFromUrl, useTranslations, languages } from '../../i18n/ui';
|
11 |
+
|
12 |
+
export function getStaticPaths() {
|
13 |
+
return Object.keys(languages).filter(lang => lang !== 'ar').map(lang => ({
|
14 |
+
params: { lang }
|
15 |
+
}));
|
16 |
+
}
|
17 |
+
|
18 |
+
const lang = getLangFromUrl(Astro.url);
|
19 |
+
const t = useTranslations(lang);
|
20 |
+
|
21 |
+
const title = t('hero.title') + ' - Blueprint Engineering Consultancy';
|
22 |
+
const description = t('hero.description');
|
23 |
+
---
|
24 |
+
|
25 |
+
<Layout title={title} description={description} lang={lang}>
|
26 |
+
<Header />
|
27 |
+
<main>
|
28 |
+
<Hero />
|
29 |
+
<About />
|
30 |
+
<Services />
|
31 |
+
<Workflow />
|
32 |
+
<Contact />
|
33 |
+
</main>
|
34 |
+
<Footer />
|
35 |
+
</Layout>
|
src/pages/en/index.astro
ADDED
@@ -0,0 +1,29 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
---
|
2 |
+
import Layout from '../../layouts/Layout.astro';
|
3 |
+
import Header from '../../components/Header.astro';
|
4 |
+
import Hero from '../../components/Hero.astro';
|
5 |
+
import About from '../../components/About.astro';
|
6 |
+
import Services from '../../components/Services.astro';
|
7 |
+
import Workflow from '../../components/Workflow.astro';
|
8 |
+
import Contact from '../../components/Contact.astro';
|
9 |
+
import Footer from '../../components/Footer.astro';
|
10 |
+
import { getLangFromUrl, useTranslations } from '../../i18n/ui';
|
11 |
+
|
12 |
+
const lang = getLangFromUrl(Astro.url);
|
13 |
+
const t = useTranslations(lang);
|
14 |
+
|
15 |
+
const title = 'Blueprint Engineering Consultancy - Ras Al Khaimah | Architectural Design & Engineering Supervision';
|
16 |
+
const description = 'Blueprint Engineering Consultancy in Ras Al Khaimah. We provide architectural, structural, electrical and mechanical design services, building permit extraction, and engineering supervision. Engineering with expertise, innovation with vision, execution with precision.';
|
17 |
+
---
|
18 |
+
|
19 |
+
<Layout title={title} description={description} lang={lang}>
|
20 |
+
<Header />
|
21 |
+
<main>
|
22 |
+
<Hero />
|
23 |
+
<About />
|
24 |
+
<Services />
|
25 |
+
<Workflow />
|
26 |
+
<Contact />
|
27 |
+
</main>
|
28 |
+
<Footer />
|
29 |
+
</Layout>
|
src/pages/index.astro
ADDED
@@ -0,0 +1,34 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
---
|
2 |
+
import Layout from '../layouts/Layout.astro';
|
3 |
+
import Header from '../components/Header.astro';
|
4 |
+
import Hero from '../components/Hero.astro';
|
5 |
+
import About from '../components/About.astro';
|
6 |
+
import Services from '../components/Services.astro';
|
7 |
+
import Workflow from '../components/Workflow.astro';
|
8 |
+
import Contact from '../components/Contact.astro';
|
9 |
+
import Footer from '../components/Footer.astro';
|
10 |
+
import { getLangFromUrl, useTranslations } from '../i18n/ui';
|
11 |
+
|
12 |
+
const lang = getLangFromUrl(Astro.url);
|
13 |
+
const t = useTranslations(lang);
|
14 |
+
|
15 |
+
const title = lang === 'ar'
|
16 |
+
? 'بلو برينت للاستشارات الهندسية - رأس الخيمة | تصميم معماري وإشراف هندسي'
|
17 |
+
: 'Blueprint Engineering Consultancy - Ras Al Khaimah | Architectural Design & Engineering Supervision';
|
18 |
+
|
19 |
+
const description = lang === 'ar'
|
20 |
+
? 'شركة بلو برينت للاستشارات الهندسية في رأس الخيمة. نقدم خدمات التصميم المعماري، الإنشائي، الكهربائي والميكانيكي، استخراج رخص البناء، والإشراف الهندسي. الهندسة بخبرة، الابتكار برؤية، التنفيذ بدقة.'
|
21 |
+
: 'Blueprint Engineering Consultancy in Ras Al Khaimah. We provide architectural, structural, electrical and mechanical design services, building permit extraction, and engineering supervision. Engineering with expertise, innovation with vision, execution with precision.';
|
22 |
+
---
|
23 |
+
|
24 |
+
<Layout title={title} description={description} lang={lang}>
|
25 |
+
<Header />
|
26 |
+
<main>
|
27 |
+
<Hero />
|
28 |
+
<About />
|
29 |
+
<Services />
|
30 |
+
<Workflow />
|
31 |
+
<Contact />
|
32 |
+
</main>
|
33 |
+
<Footer />
|
34 |
+
</Layout>
|
src/styles/global.css
ADDED
@@ -0,0 +1,159 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
@import url('https://fonts.googleapis.com/css2?family=Cairo:wght@300;400;600;700&display=swap');
|
2 |
+
@import url('https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700&display=swap');
|
3 |
+
@import 'aos/dist/aos.css';
|
4 |
+
@tailwind base;
|
5 |
+
@tailwind components;
|
6 |
+
@tailwind utilities;
|
7 |
+
|
8 |
+
@layer base {
|
9 |
+
html {
|
10 |
+
font-family: 'Cairo', 'Poppins', system-ui, sans-serif;
|
11 |
+
scroll-behavior: smooth;
|
12 |
+
}
|
13 |
+
|
14 |
+
body {
|
15 |
+
@apply transition-colors duration-300;
|
16 |
+
}
|
17 |
+
|
18 |
+
[dir="rtl"] {
|
19 |
+
font-family: 'Cairo', system-ui, sans-serif;
|
20 |
+
}
|
21 |
+
|
22 |
+
[dir="ltr"] {
|
23 |
+
font-family: 'Poppins', system-ui, sans-serif;
|
24 |
+
}
|
25 |
+
}
|
26 |
+
|
27 |
+
@layer components {
|
28 |
+
.btn-primary {
|
29 |
+
@apply bg-blueprint-accent hover:bg-opacity-90 text-white px-6 py-3 rounded-lg font-semibold transition-all duration-300 hover:shadow-lg hover:scale-105;
|
30 |
+
}
|
31 |
+
|
32 |
+
.btn-secondary {
|
33 |
+
@apply border-2 border-blueprint-accent text-blueprint-accent hover:bg-blueprint-accent hover:text-white px-6 py-3 rounded-lg font-semibold transition-all duration-300;
|
34 |
+
}
|
35 |
+
|
36 |
+
.section-padding {
|
37 |
+
@apply py-16 px-4 sm:px-6 lg:px-8;
|
38 |
+
}
|
39 |
+
|
40 |
+
.container-custom {
|
41 |
+
@apply max-w-7xl mx-auto;
|
42 |
+
}
|
43 |
+
|
44 |
+
.blueprint-pattern {
|
45 |
+
background-image:
|
46 |
+
linear-gradient(45deg, transparent 24%, rgba(199, 156, 73, 0.05) 25%, rgba(199, 156, 73, 0.05) 26%, transparent 27%, transparent 74%, rgba(199, 156, 73, 0.05) 75%, rgba(199, 156, 73, 0.05) 76%, transparent 77%, transparent),
|
47 |
+
linear-gradient(-45deg, transparent 24%, rgba(199, 156, 73, 0.05) 25%, rgba(199, 156, 73, 0.05) 26%, transparent 27%, transparent 74%, rgba(199, 156, 73, 0.05) 75%, rgba(199, 156, 73, 0.05) 76%, transparent 77%, transparent);
|
48 |
+
background-size: 50px 50px;
|
49 |
+
}
|
50 |
+
|
51 |
+
.floating-shapes {
|
52 |
+
position: absolute;
|
53 |
+
top: 0;
|
54 |
+
left: 0;
|
55 |
+
right: 0;
|
56 |
+
bottom: 0;
|
57 |
+
overflow: hidden;
|
58 |
+
pointer-events: none;
|
59 |
+
}
|
60 |
+
|
61 |
+
.floating-shapes::before {
|
62 |
+
content: '';
|
63 |
+
position: absolute;
|
64 |
+
width: 200px;
|
65 |
+
height: 200px;
|
66 |
+
background: linear-gradient(45deg, rgba(199, 156, 73, 0.1), rgba(27, 47, 92, 0.1));
|
67 |
+
border-radius: 50%;
|
68 |
+
top: 10%;
|
69 |
+
left: 10%;
|
70 |
+
animation: float 8s ease-in-out infinite;
|
71 |
+
}
|
72 |
+
|
73 |
+
.floating-shapes::after {
|
74 |
+
content: '';
|
75 |
+
position: absolute;
|
76 |
+
width: 150px;
|
77 |
+
height: 150px;
|
78 |
+
background: linear-gradient(-45deg, rgba(199, 156, 73, 0.08), rgba(27, 47, 92, 0.08));
|
79 |
+
border-radius: 20px;
|
80 |
+
top: 60%;
|
81 |
+
right: 15%;
|
82 |
+
animation: float 6s ease-in-out infinite reverse;
|
83 |
+
}
|
84 |
+
}
|
85 |
+
|
86 |
+
/* Arabic text improvements */
|
87 |
+
[dir="rtl"] {
|
88 |
+
text-align: right;
|
89 |
+
}
|
90 |
+
|
91 |
+
[dir="rtl"] .rtl-flip {
|
92 |
+
transform: scaleX(-1);
|
93 |
+
}
|
94 |
+
|
95 |
+
/* Custom scrollbar */
|
96 |
+
::-webkit-scrollbar {
|
97 |
+
width: 8px;
|
98 |
+
}
|
99 |
+
|
100 |
+
::-webkit-scrollbar-track {
|
101 |
+
@apply bg-blueprint-light dark:bg-blueprint-dark;
|
102 |
+
}
|
103 |
+
|
104 |
+
::-webkit-scrollbar-thumb {
|
105 |
+
@apply bg-blueprint-accent rounded-full;
|
106 |
+
}
|
107 |
+
|
108 |
+
::-webkit-scrollbar-thumb:hover {
|
109 |
+
@apply bg-opacity-80;
|
110 |
+
}
|
111 |
+
|
112 |
+
/* Loading animation */
|
113 |
+
.loading-overlay {
|
114 |
+
position: fixed;
|
115 |
+
top: 0;
|
116 |
+
left: 0;
|
117 |
+
right: 0;
|
118 |
+
bottom: 0;
|
119 |
+
background: linear-gradient(135deg, #1B2F5C 0%, #2a4a7a 100%);
|
120 |
+
display: flex;
|
121 |
+
align-items: center;
|
122 |
+
justify-content: center;
|
123 |
+
z-index: 9999;
|
124 |
+
transition: opacity 0.5s ease-out;
|
125 |
+
}
|
126 |
+
|
127 |
+
.loading-overlay.fade-out {
|
128 |
+
opacity: 0;
|
129 |
+
pointer-events: none;
|
130 |
+
}
|
131 |
+
|
132 |
+
.blueprint-logo-loading {
|
133 |
+
width: 120px;
|
134 |
+
height: 120px;
|
135 |
+
position: relative;
|
136 |
+
}
|
137 |
+
|
138 |
+
.blueprint-logo-loading svg {
|
139 |
+
width: 100%;
|
140 |
+
height: 100%;
|
141 |
+
stroke: #C69C49;
|
142 |
+
stroke-width: 2;
|
143 |
+
fill: none;
|
144 |
+
stroke-dasharray: 1000;
|
145 |
+
stroke-dashoffset: 1000;
|
146 |
+
animation: draw 3s ease-in-out infinite;
|
147 |
+
}
|
148 |
+
|
149 |
+
@keyframes draw {
|
150 |
+
0% {
|
151 |
+
stroke-dashoffset: 1000;
|
152 |
+
}
|
153 |
+
50% {
|
154 |
+
stroke-dashoffset: 0;
|
155 |
+
}
|
156 |
+
100% {
|
157 |
+
stroke-dashoffset: -1000;
|
158 |
+
}
|
159 |
+
}
|
tailwind.config.mjs
ADDED
@@ -0,0 +1,45 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/** @type {import('tailwindcss').Config} */
|
2 |
+
export default {
|
3 |
+
content: ['./src/**/*.{astro,html,js,jsx,md,mdx,svelte,ts,tsx,vue}'],
|
4 |
+
darkMode: 'class',
|
5 |
+
theme: {
|
6 |
+
extend: {
|
7 |
+
colors: {
|
8 |
+
blueprint: {
|
9 |
+
primary: '#1B2F5C',
|
10 |
+
secondary: '#FFFFFF',
|
11 |
+
accent: '#C69C49',
|
12 |
+
dark: '#3A3A3A',
|
13 |
+
light: '#F5F7FA',
|
14 |
+
gray: '#EDEDED'
|
15 |
+
}
|
16 |
+
},
|
17 |
+
fontFamily: {
|
18 |
+
cairo: ['Cairo', 'ui-sans-serif', 'system-ui'],
|
19 |
+
poppins: ['Poppins', 'ui-sans-serif', 'system-ui'],
|
20 |
+
},
|
21 |
+
animation: {
|
22 |
+
'fade-in': 'fadeIn 0.6s ease-out',
|
23 |
+
'slide-up': 'slideUp 0.8s ease-out',
|
24 |
+
'bounce-slow': 'bounce 2s infinite',
|
25 |
+
'pulse-slow': 'pulse 3s infinite',
|
26 |
+
'float': 'float 6s ease-in-out infinite',
|
27 |
+
},
|
28 |
+
keyframes: {
|
29 |
+
fadeIn: {
|
30 |
+
'0%': { opacity: '0' },
|
31 |
+
'100%': { opacity: '1' },
|
32 |
+
},
|
33 |
+
slideUp: {
|
34 |
+
'0%': { transform: 'translateY(30px)', opacity: '0' },
|
35 |
+
'100%': { transform: 'translateY(0)', opacity: '1' },
|
36 |
+
},
|
37 |
+
float: {
|
38 |
+
'0%, 100%': { transform: 'translateY(0px)' },
|
39 |
+
'50%': { transform: 'translateY(-20px)' },
|
40 |
+
}
|
41 |
+
}
|
42 |
+
},
|
43 |
+
},
|
44 |
+
plugins: [],
|
45 |
+
}
|
tsconfig.json
CHANGED
@@ -1,27 +1,5 @@
|
|
1 |
{
|
2 |
-
"
|
3 |
-
|
4 |
-
|
5 |
-
"allowJs": true,
|
6 |
-
"skipLibCheck": true,
|
7 |
-
"strict": true,
|
8 |
-
"noEmit": true,
|
9 |
-
"esModuleInterop": true,
|
10 |
-
"module": "esnext",
|
11 |
-
"moduleResolution": "bundler",
|
12 |
-
"resolveJsonModule": true,
|
13 |
-
"isolatedModules": true,
|
14 |
-
"jsx": "preserve",
|
15 |
-
"incremental": true,
|
16 |
-
"plugins": [
|
17 |
-
{
|
18 |
-
"name": "next"
|
19 |
-
}
|
20 |
-
],
|
21 |
-
"paths": {
|
22 |
-
"@/*": ["./*"]
|
23 |
-
}
|
24 |
-
},
|
25 |
-
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
|
26 |
-
"exclude": ["node_modules"]
|
27 |
}
|
|
|
1 |
{
|
2 |
+
"extends": "astro/tsconfigs/strict",
|
3 |
+
"include": [".astro/types.d.ts", "**/*"],
|
4 |
+
"exclude": ["dist"]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
5 |
}
|