diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 0000000..746e0cd
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,26 @@
+FROM node:24-alpine AS build
+ENV PNPM_HOME="/pnpm"
+ENV PATH="$PNPM_HOME:$PATH"
+ENV CI=true
+RUN corepack enable
+
+WORKDIR /app
+
+COPY package.json pnpm-lock.yaml pnpm-workspace.yaml ./
+RUN pnpm install --frozen-lockfile
+
+COPY . .
+RUN pnpm run build
+
+FROM node:24-alpine AS runtime
+WORKDIR /app
+ENV NODE_ENV=production
+ENV HOST=0.0.0.0
+ENV PORT=4321
+
+RUN addgroup -S nodejs && adduser -S astro -G nodejs
+COPY --from=build /app/dist ./dist
+
+USER astro
+EXPOSE 4321
+CMD ["node", "dist/server/entry.mjs"]
diff --git a/astro.config.mjs b/astro.config.mjs
index 1e77727..2da1aa0 100644
--- a/astro.config.mjs
+++ b/astro.config.mjs
@@ -10,7 +10,6 @@ import node from "@astrojs/node";
export default defineConfig({
site: "https://midcreative.eu",
integrations: [mdx(), sitemap()],
- output: "static",
fonts: [
{
diff --git a/docker-compose.local.yaml b/docker-compose.local.yaml
new file mode 100644
index 0000000..95f8ca7
--- /dev/null
+++ b/docker-compose.local.yaml
@@ -0,0 +1,30 @@
+services:
+ midcreative:
+ image: midcreative:latest
+ build:
+ context: .
+ dockerfile: Dockerfile
+ container_name: midcreative
+ restart: unless-stopped
+ environment:
+ NODE_ENV: production
+ HOST: 0.0.0.0
+ PORT: 4321
+ # Добавьте эту секцию для локальной проверки:
+ ports:
+ - "4321:4321"
+ expose:
+ - "4321"
+ networks:
+ - traefik_proxy
+ labels:
+ - "traefik.enable=true"
+ - "traefik.http.routers.midcreative.rule=Host(`midcreative.eu`) || Host(`www.midcreative.eu`)"
+ - "traefik.http.routers.midcreative.entrypoints=websecure"
+ - "traefik.http.routers.midcreative.tls=true"
+ - "traefik.http.routers.midcreative.tls.certresolver=letsencrypt"
+ - "traefik.http.services.midcreative.loadbalancer.server.port=4321"
+
+networks:
+ traefik_proxy:
+ external: true
diff --git a/docker-compose.yaml b/docker-compose.yaml
new file mode 100644
index 0000000..6198137
--- /dev/null
+++ b/docker-compose.yaml
@@ -0,0 +1,28 @@
+services:
+ midcreative:
+ # Оставляем image, чтобы CI/CD мог за push/pull обновлять образ
+ image: midcreative:latest
+ build:
+ context: .
+ dockerfile: Dockerfile
+ container_name: midcreative
+ restart: unless-stopped
+ environment:
+ NODE_ENV: production
+ HOST: 0.0.0.0
+ PORT: 4321
+ expose:
+ - "4321"
+ networks:
+ - traefik_proxy
+ labels:
+ - "traefik.enable=true"
+ - "traefik.http.routers.midcreative.rule=Host(`midcreative.eu`) || Host(`www.midcreative.eu`)"
+ - "traefik.http.routers.midcreative.entrypoints=websecure"
+ - "traefik.http.routers.midcreative.tls=true"
+ - "traefik.http.routers.midcreative.tls.certresolver=letsencrypt"
+ - "traefik.http.services.midcreative.loadbalancer.server.port=4321"
+
+networks:
+ traefik_proxy:
+ external: true
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index ed33463..0adf2b2 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -10,10 +10,10 @@ importers:
dependencies:
'@astrojs/mdx':
specifier: ^7.0.3
- version: 7.0.3(@astrojs/markdown-satteri@0.3.4)(astro@7.1.3(@astrojs/markdown-remark@7.2.1)(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.2)(@types/node@24.13.3))
+ version: 7.0.4(@astrojs/markdown-satteri@0.3.4)(astro@7.1.4(@astrojs/markdown-remark@7.2.1)(@emnapi/core@2.0.0-alpha.3)(@emnapi/runtime@2.0.0-alpha.3)(@types/node@24.13.3))
'@astrojs/node':
specifier: ^11.0.3
- version: 11.0.3(astro@7.1.3(@astrojs/markdown-remark@7.2.1)(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.2)(@types/node@24.13.3))
+ version: 11.0.3(astro@7.1.4(@astrojs/markdown-remark@7.2.1)(@emnapi/core@2.0.0-alpha.3)(@emnapi/runtime@2.0.0-alpha.3)(@types/node@24.13.3))
'@astrojs/rss':
specifier: ^4.0.19
version: 4.0.19
@@ -22,10 +22,10 @@ importers:
version: 3.7.3
astro:
specifier: ^7.1.3
- version: 7.1.3(@astrojs/markdown-remark@7.2.1)(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.2)(@types/node@24.13.3)
+ version: 7.1.4(@astrojs/markdown-remark@7.2.1)(@emnapi/core@2.0.0-alpha.3)(@emnapi/runtime@2.0.0-alpha.3)(@types/node@24.13.3)
motion:
specifier: ^12.42.2
- version: 12.42.2(react-dom@19.2.8(react@19.2.8))(react@19.2.8)
+ version: 12.43.0(react-dom@19.2.8(react@19.2.8))(react@19.2.8)
react:
specifier: ^19.2.8
version: 19.2.8
@@ -125,8 +125,8 @@ packages:
'@astrojs/markdown-satteri@0.3.4':
resolution: {integrity: sha512-6Lvt/bQZEBW+zzdhPblvfZEy5PGEYJaUsUqaCgwHeRPxZJL1gc9I+DRLKWJjjYTWDzVUTzXlMq4WwSK+X34CVw==}
- '@astrojs/mdx@7.0.3':
- resolution: {integrity: sha512-RxyIwU0uFam5ftwqKOjpIdhnFxZ/kEikeimLyQy3eGXbHT8WgRGzzesOIHVU8+m9TY8ag5WVOyvV24/GyqPdPQ==}
+ '@astrojs/mdx@7.0.4':
+ resolution: {integrity: sha512-fH4ouVZCgmLOH5z+GYnJMDOSUohn9U2W3p79Ck7PDK3EdGpe/crZAtT6Sp1ziEurj5NSQh7TxP8MSR4NOcC6fQ==}
engines: {node: '>=22.12.0'}
peerDependencies:
'@astrojs/markdown-satteri': ^0.3.1
@@ -235,15 +235,24 @@ packages:
'@emnapi/core@1.11.1':
resolution: {integrity: sha512-RSvbQmHzdKzNsLYa/wHrbc3KN4sYLKAdPZxqiM2HATqv/SBk2/ENSHpvXGaLOMcsAyz0poEGqkmmKYG3OWiJEQ==}
+ '@emnapi/core@2.0.0-alpha.3':
+ resolution: {integrity: sha512-AZypUeJ/yByuxyS7BlSNRDOMLMlROYtjYdIAuBmJssVz1UJDSeYxLrdizhXCFYhedC5bqd/ASy8EuNXbVVXp9g==}
+
'@emnapi/runtime@1.11.1':
resolution: {integrity: sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw==}
- '@emnapi/runtime@1.11.2':
- resolution: {integrity: sha512-kyOl3X0DuTiT1h2ft8r2fYO8JYtU9a9Xis/zBSiGArNaagCOWx90N1k2wxp18czFDH+OgcWGb5ZP/XMt3dcyPA==}
+ '@emnapi/runtime@1.11.3':
+ resolution: {integrity: sha512-Xz4Tpyki7XyrpbUK1jR1AhdAdaXyhhY4lZ3neLodmhpuWfy2PAQN5B46sAiU4liOXGLkHypn/qU+jvfWSCYYLA==}
+
+ '@emnapi/runtime@2.0.0-alpha.3':
+ resolution: {integrity: sha512-hFPAhMUjJD9BSyCANEISPOogeXC9Zo9ZQl7L6vKnaVsMkCtzznaW/naYypeyl0Gv5rYfWYsZbpixTMpjDJzQeA==}
'@emnapi/wasi-threads@1.2.2':
resolution: {integrity: sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA==}
+ '@emnapi/wasi-threads@2.0.1':
+ resolution: {integrity: sha512-9DsSk+o5NBX0CCJT8s0EROGSGxjR/tKu6aBTaVyq+SjAEQH4XcdcRxPBRzsBLizTTJ49MJjF+jgu3qnO9GLQcQ==}
+
'@esbuild/aix-ppc64@0.28.1':
resolution: {integrity: sha512-Svl7tq8k/08+p6CXPpRjQ1fKX+1odH/BQbb48fV6fj3CWHhsoIOoY87w1oHXm0qEpkIK3ZfVgp0hed3XBXzXMQ==}
engines: {node: '>=18'}
@@ -568,11 +577,12 @@ packages:
'@mdx-js/mdx@3.1.1':
resolution: {integrity: sha512-f6ZO2ifpwAQIpzGWaBQT2TXxPv6z3RBzQKpVftEWN78Vl/YweF1uwussDx8ECAXVtr3Rs89fKyG9YlzUs9DyGQ==}
- '@napi-rs/wasm-runtime@1.1.6':
- resolution: {integrity: sha512-ZLv/JdUfkvOy9eCnnBaGfiO+XimbjebAeO+MRQqD/B+FR1tnRN0tpKSJHRbE8sFfS6aqsXZ67TQjfwfsxULVbg==}
+ '@napi-rs/wasm-runtime@1.2.0':
+ resolution: {integrity: sha512-kDoONqMa+VnZ4vvvu/ZUurpJ4gkZU57e7g69qpNgWhYcZFPUHZM2CEMKm+cG6ufDVALbjMvfmMjFVqaK7uEMnA==}
+ engines: {node: ^20.19.0 || ^22.13.0 || >=23.5.0}
peerDependencies:
- '@emnapi/core': ^1.7.1
- '@emnapi/runtime': ^1.7.1
+ '@emnapi/core': ^2.0.0-alpha.3
+ '@emnapi/runtime': ^2.0.0-alpha.3
'@nodable/entities@3.0.0':
resolution: {integrity: sha512-8L9xFeTYKhm49xfIypoe2W5wV1m/3Z58kT+7kR9A8OyFxcPduI4VmxaUMQyKYrRjUoLLSXv6EKKID5Tvj9cUVw==}
@@ -768,8 +778,8 @@ packages:
peerDependencies:
acorn: ^6.0.0 || ^7.0.0 || ^8.0.0
- acorn@8.17.0:
- resolution: {integrity: sha512-xRQbDb9BnwDafYNn6Vwl839DYVjqXYb1XVGtWAZ1kcDc6iwAL4hg3B1dZlRiuENFeO2H53gFG3in621AdERVAg==}
+ acorn@8.18.0:
+ resolution: {integrity: sha512-lGq+9yr1/GuAWaVYIHRjvvySG5/4VfKIvC8EWxStPdcDh/Ka7FG3twP6v4d5BkravUilhIAsG4Qj83t02LWUPQ==}
engines: {node: '>=0.4.0'}
hasBin: true
@@ -801,8 +811,8 @@ packages:
resolution: {integrity: sha512-LElXdjswlqjWrPpJFg1Fx4wpkOCxj1TDHlSV4PlaRxHGWko024xICaa97ZkMfs6DRKlCguiAI+rbXv5GWwXIkg==}
hasBin: true
- astro@7.1.3:
- resolution: {integrity: sha512-4dhPyAAXthf3xLEYnG8SeL7yr/nTPPABfY7e9YF0yuO+vK9Xp+8Q5j4xzsmL3GueukQv4oNwGNTBepLOiDGeJA==}
+ astro@7.1.4:
+ resolution: {integrity: sha512-e0gkBReJECAZuuTgpEB5JMUc6J4mM6boD6wuVE1pBf/fMywG47f8qm9XQoA6kZB1RWHiHmUlLuQ2jd9Q5+72HQ==}
engines: {node: '>=22.12.0', npm: '>=9.6.5', pnpm: '>=7.1.0'}
hasBin: true
peerDependencies:
@@ -1054,8 +1064,8 @@ packages:
resolution: {integrity: sha512-Wp1zXWPVUPBmfoa3Cqc9ctaKuzKAV6uLstRqlR56kSjplf5uAce+qeyYym7F+PHbGTk+tCEdkCW6RD7DX/gBZw==}
engines: {node: '>=20'}
- framer-motion@12.42.2:
- resolution: {integrity: sha512-5XY9luDiu0oHfHBjpDthFMh0ES+122w6p/papSJBweMkO8Sn+PW2QaEgRblQBpWFnuvZS5qvarpt/hO2pjGmnw==}
+ framer-motion@12.43.0:
+ resolution: {integrity: sha512-1eaL3RvR/kAlbG7UYcpMptEyzPoENO0c6w7ZnB3/hh2vSAz/6uGAFn6fdoqTBguNstf3MsFhJHsD/0DHiclG+g==}
peerDependencies:
'@emotion/is-prop-valid': '*'
react: ^18.0.0 || ^19.0.0
@@ -1257,8 +1267,8 @@ packages:
resolution: {integrity: sha512-4pfM1Ff0x50o0tQwb5ucw/RzNyD0/YJME6IVcStalZuMWxdt3sR3huStTtxz4PUmvZfRguvDejasvQ2kifR11g==}
engines: {node: 20 || >=22}
- magic-string@0.30.21:
- resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==}
+ magic-string@1.1.0:
+ resolution: {integrity: sha512-kS3VHe0nEPST2saQV4Rbkchcd3UBRkVTQHo1D3h/ZTwFDhai/mfKkmtPAtD129EOI7K3HlHIsFOt0WrI2/oU9g==}
magicast@0.5.3:
resolution: {integrity: sha512-pVKE4UdSQ7DvHzivsCIFx2BJn1mHG6KsyrFcaxFx6tONdneEuThrDx0Cj3AMg58KyN4pzYT+LHOotxDQDjNvkw==}
@@ -1440,14 +1450,14 @@ packages:
resolution: {integrity: sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==}
engines: {node: '>=18'}
- motion-dom@12.42.2:
- resolution: {integrity: sha512-5gIMWLp/PycBtJRJWRgjxke5n8dlvkSn2DrYW+tr3XcqAZY1xZh6BJyooJXCM8wdfM7wfMjkBJNLge1CKPUIRA==}
+ motion-dom@12.43.0:
+ resolution: {integrity: sha512-azKON4d9S65PEoFUiQTMTgPheEmzf2QngdRc50AKfJp9Q9mmcBVw22c8eMq9k8kxOFHdL7+WZY7N/5F/lwiDag==}
motion-utils@12.39.0:
resolution: {integrity: sha512-8nadJAJjTtqRkmRF36FoJTrywK9nnFmnPwnSMyxaOCU7GDjN9RTMJIxx9De8ErM+vpPhMccr/6fo5WciyQLnMQ==}
- motion@12.42.2:
- resolution: {integrity: sha512-Atvv11yUKIid41cVrRBDVX5m8tF8kNpExRSlbpt6APClhDjtwQssgFHhQzejxw7/7YYbjHSPKBVbHo05BuJT5Q==}
+ motion@12.43.0:
+ resolution: {integrity: sha512-BQgQbSa9Hn3/mtbib0MK53y6JSANa+YKUKlaYnWzAVDH424RYQ5LVpV3pNiWH00BA2z4ojsSdMzqT7g2FQwjuQ==}
peerDependencies:
'@emotion/is-prop-valid': '*'
react: ^18.0.0 || ^19.0.0
@@ -1482,8 +1492,8 @@ packages:
node-fetch-native@1.6.7:
resolution: {integrity: sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q==}
- node-mock-http@1.0.4:
- resolution: {integrity: sha512-8DY+kFsDkNXy1sJglUfuODx1/opAGJGyrTuFqEoN90oRc2Vk0ZbD4K2qmKXBBEhZQzdKHIVfEJpDU8Ak2NJEvQ==}
+ node-mock-http@1.0.5:
+ resolution: {integrity: sha512-KQyt/wLjG3TAc7DOUhpqWzgd4ERxR80JOlTK5VE5R1S12IaPVN5qkj4klBce9HPG1Njuup4Sb5bljaT34lIyjw==}
normalize-path@3.0.0:
resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==}
@@ -1554,8 +1564,8 @@ packages:
resolution: {integrity: sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==}
engines: {node: '>=12'}
- postcss@8.5.22:
- resolution: {integrity: sha512-KBDEIpLrvpv16pp3K0Fw+UCoZfopFjjgeB+0tA/aaThfEE74kKDLrgg603YvOWJyg3+WYtyq3xYsQWsIyZlPqQ==}
+ postcss@8.5.24:
+ resolution: {integrity: sha512-8RyVklq0owXUTa4xlpzu4l9AaVKIdQvAcOHZWaMh98HgySsUtxRVf/chRe3dsSLqb6i40BzGRzEUddRaI+9TSw==}
engines: {node: ^10 || ^12 || >=14}
prettier-plugin-astro@0.14.1:
@@ -1678,8 +1688,8 @@ packages:
satteri@0.9.5:
resolution: {integrity: sha512-ZuWVl+vnM64y+/TtX8Kosv2c00W+hLQiiwnEL6H0UKVVrxFqMw4D2CJHHQaouVd89OAhtBBfjWLqhKi3TVUV4w==}
- sax@1.6.0:
- resolution: {integrity: sha512-6R3J5M4AcbtLUdZmRv2SygeVaM7IhrLXu9BmnOGmmACak8fiUtOsYNWUS4uK7upbmHIBbLBeFeI//477BKLBzA==}
+ sax@1.6.1:
+ resolution: {integrity: sha512-42tBVwLWnaQvW5zc4HbZrTuWccECCZfBi92FDuwtqxasH+JbPB3/FOKb1m222K42R4WxuxzzMsTswfzgtSu64Q==}
engines: {node: '>=11.0.0'}
scheduler@0.27.0:
@@ -1721,8 +1731,8 @@ packages:
engines: {node: '>=20.19.5', npm: '>=10.8.2'}
hasBin: true
- smol-toml@1.7.0:
- resolution: {integrity: sha512-aqVvWoyO21L23mb+drl4RmMXbf6N7FdHjAhTRA9ZBL7apWBgfWC16KjrASI+1p9GAroljyMHj6fK67i0UiTNvQ==}
+ smol-toml@1.7.1:
+ resolution: {integrity: sha512-PPlsspAZ4jbMBu5DMFhfUGDQLu/vrL4SyBROVS37x8ynnVmFIs1VPBz1Co8Xks3TvpIaZXmU85y4DrQ+UyVFoQ==}
engines: {node: '>= 18'}
source-map-js@1.2.1:
@@ -2005,9 +2015,9 @@ snapshots:
'@astrojs/compiler-binding-linux-x64-musl@0.3.1':
optional: true
- '@astrojs/compiler-binding-wasm32-wasi@0.3.1(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.2)':
+ '@astrojs/compiler-binding-wasm32-wasi@0.3.1(@emnapi/core@2.0.0-alpha.3)(@emnapi/runtime@2.0.0-alpha.3)':
dependencies:
- '@napi-rs/wasm-runtime': 1.1.6(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.2)
+ '@napi-rs/wasm-runtime': 1.2.0(@emnapi/core@2.0.0-alpha.3)(@emnapi/runtime@2.0.0-alpha.3)
transitivePeerDependencies:
- '@emnapi/core'
- '@emnapi/runtime'
@@ -2019,7 +2029,7 @@ snapshots:
'@astrojs/compiler-binding-win32-x64-msvc@0.3.1':
optional: true
- '@astrojs/compiler-binding@0.3.1(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.2)':
+ '@astrojs/compiler-binding@0.3.1(@emnapi/core@2.0.0-alpha.3)(@emnapi/runtime@2.0.0-alpha.3)':
optionalDependencies:
'@astrojs/compiler-binding-darwin-arm64': 0.3.1
'@astrojs/compiler-binding-darwin-x64': 0.3.1
@@ -2027,16 +2037,16 @@ snapshots:
'@astrojs/compiler-binding-linux-arm64-musl': 0.3.1
'@astrojs/compiler-binding-linux-x64-gnu': 0.3.1
'@astrojs/compiler-binding-linux-x64-musl': 0.3.1
- '@astrojs/compiler-binding-wasm32-wasi': 0.3.1(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.2)
+ '@astrojs/compiler-binding-wasm32-wasi': 0.3.1(@emnapi/core@2.0.0-alpha.3)(@emnapi/runtime@2.0.0-alpha.3)
'@astrojs/compiler-binding-win32-arm64-msvc': 0.3.1
'@astrojs/compiler-binding-win32-x64-msvc': 0.3.1
transitivePeerDependencies:
- '@emnapi/core'
- '@emnapi/runtime'
- '@astrojs/compiler-rs@0.3.1(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.2)':
+ '@astrojs/compiler-rs@0.3.1(@emnapi/core@2.0.0-alpha.3)(@emnapi/runtime@2.0.0-alpha.3)':
dependencies:
- '@astrojs/compiler-binding': 0.3.1(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.2)
+ '@astrojs/compiler-binding': 0.3.1(@emnapi/core@2.0.0-alpha.3)(@emnapi/runtime@2.0.0-alpha.3)
transitivePeerDependencies:
- '@emnapi/core'
- '@emnapi/runtime'
@@ -2051,7 +2061,7 @@ snapshots:
picomatch: 4.0.5
retext-smartypants: 6.2.0
shiki: 4.3.1
- smol-toml: 1.7.0
+ smol-toml: 1.7.1
unified: 11.0.5
'@astrojs/internal-helpers@0.10.2':
@@ -2062,7 +2072,7 @@ snapshots:
picomatch: 4.0.5
retext-smartypants: 6.2.0
shiki: 4.3.1
- smol-toml: 1.7.0
+ smol-toml: 1.7.1
unified: 11.0.5
'@astrojs/markdown-remark@7.2.1':
@@ -2095,13 +2105,13 @@ snapshots:
hast-util-from-html: 2.0.3
satteri: 0.9.5
- '@astrojs/mdx@7.0.3(@astrojs/markdown-satteri@0.3.4)(astro@7.1.3(@astrojs/markdown-remark@7.2.1)(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.2)(@types/node@24.13.3))':
+ '@astrojs/mdx@7.0.4(@astrojs/markdown-satteri@0.3.4)(astro@7.1.4(@astrojs/markdown-remark@7.2.1)(@emnapi/core@2.0.0-alpha.3)(@emnapi/runtime@2.0.0-alpha.3)(@types/node@24.13.3))':
dependencies:
'@astrojs/internal-helpers': 0.10.1
'@astrojs/markdown-remark': 7.2.1
'@mdx-js/mdx': 3.1.1
- acorn: 8.17.0
- astro: 7.1.3(@astrojs/markdown-remark@7.2.1)(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.2)(@types/node@24.13.3)
+ acorn: 8.18.0
+ astro: 7.1.4(@astrojs/markdown-remark@7.2.1)(@emnapi/core@2.0.0-alpha.3)(@emnapi/runtime@2.0.0-alpha.3)(@types/node@24.13.3)
es-module-lexer: 2.3.1
estree-util-visit: 2.0.0
hast-util-to-html: 9.0.5
@@ -2117,10 +2127,10 @@ snapshots:
transitivePeerDependencies:
- supports-color
- '@astrojs/node@11.0.3(astro@7.1.3(@astrojs/markdown-remark@7.2.1)(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.2)(@types/node@24.13.3))':
+ '@astrojs/node@11.0.3(astro@7.1.4(@astrojs/markdown-remark@7.2.1)(@emnapi/core@2.0.0-alpha.3)(@emnapi/runtime@2.0.0-alpha.3)(@types/node@24.13.3))':
dependencies:
'@astrojs/internal-helpers': 0.10.2
- astro: 7.1.3(@astrojs/markdown-remark@7.2.1)(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.2)(@types/node@24.13.3)
+ astro: 7.1.4(@astrojs/markdown-remark@7.2.1)(@emnapi/core@2.0.0-alpha.3)(@emnapi/runtime@2.0.0-alpha.3)(@types/node@24.13.3)
send: 1.2.1
server-destroy: 1.0.1
transitivePeerDependencies:
@@ -2184,7 +2194,7 @@ snapshots:
dependencies:
'@emnapi/core': 1.11.1
'@emnapi/runtime': 1.11.1
- '@napi-rs/wasm-runtime': 1.1.6(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)
+ '@napi-rs/wasm-runtime': 1.2.0(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)
optional: true
'@bruits/satteri-win32-arm64-msvc@0.9.5':
@@ -2215,12 +2225,23 @@ snapshots:
tslib: 2.8.1
optional: true
+ '@emnapi/core@2.0.0-alpha.3':
+ dependencies:
+ '@emnapi/wasi-threads': 2.0.1
+ tslib: 2.8.1
+ optional: true
+
'@emnapi/runtime@1.11.1':
dependencies:
tslib: 2.8.1
optional: true
- '@emnapi/runtime@1.11.2':
+ '@emnapi/runtime@1.11.3':
+ dependencies:
+ tslib: 2.8.1
+ optional: true
+
+ '@emnapi/runtime@2.0.0-alpha.3':
dependencies:
tslib: 2.8.1
optional: true
@@ -2230,6 +2251,11 @@ snapshots:
tslib: 2.8.1
optional: true
+ '@emnapi/wasi-threads@2.0.1':
+ dependencies:
+ tslib: 2.8.1
+ optional: true
+
'@esbuild/aix-ppc64@0.28.1':
optional: true
@@ -2397,7 +2423,7 @@ snapshots:
'@img/sharp-wasm32@0.35.3':
dependencies:
- '@emnapi/runtime': 1.11.2
+ '@emnapi/runtime': 1.11.3
optional: true
'@img/sharp-webcontainers-wasm32@0.35.3':
@@ -2422,7 +2448,7 @@ snapshots:
'@types/estree-jsx': 1.0.5
'@types/hast': 3.0.5
'@types/mdx': 2.0.14
- acorn: 8.17.0
+ acorn: 8.18.0
collapse-white-space: 2.1.0
devlop: 1.1.0
estree-util-is-identifier-name: 3.0.0
@@ -2431,7 +2457,7 @@ snapshots:
hast-util-to-jsx-runtime: 2.3.6
markdown-extensions: 2.0.0
recma-build-jsx: 1.0.0
- recma-jsx: 1.0.1(acorn@8.17.0)
+ recma-jsx: 1.0.1(acorn@8.18.0)
recma-stringify: 1.0.0
rehype-recma: 1.0.0
remark-mdx: 3.1.1
@@ -2446,17 +2472,17 @@ snapshots:
transitivePeerDependencies:
- supports-color
- '@napi-rs/wasm-runtime@1.1.6(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)':
+ '@napi-rs/wasm-runtime@1.2.0(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)':
dependencies:
'@emnapi/core': 1.11.1
'@emnapi/runtime': 1.11.1
'@tybys/wasm-util': 0.10.3
optional: true
- '@napi-rs/wasm-runtime@1.1.6(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.2)':
+ '@napi-rs/wasm-runtime@1.2.0(@emnapi/core@2.0.0-alpha.3)(@emnapi/runtime@2.0.0-alpha.3)':
dependencies:
- '@emnapi/core': 1.11.1
- '@emnapi/runtime': 1.11.2
+ '@emnapi/core': 2.0.0-alpha.3
+ '@emnapi/runtime': 2.0.0-alpha.3
'@tybys/wasm-util': 0.10.3
optional: true
@@ -2506,7 +2532,7 @@ snapshots:
dependencies:
'@emnapi/core': 1.11.1
'@emnapi/runtime': 1.11.1
- '@napi-rs/wasm-runtime': 1.1.6(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)
+ '@napi-rs/wasm-runtime': 1.2.0(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)
optional: true
'@rolldown/binding-win32-arm64-msvc@1.1.5':
@@ -2608,11 +2634,11 @@ snapshots:
'@ungap/structured-clone@1.3.3': {}
- acorn-jsx@5.3.2(acorn@8.17.0):
+ acorn-jsx@5.3.2(acorn@8.18.0):
dependencies:
- acorn: 8.17.0
+ acorn: 8.18.0
- acorn@8.17.0: {}
+ acorn@8.18.0: {}
am-i-vibing@0.4.0:
dependencies:
@@ -2635,9 +2661,9 @@ snapshots:
astring@1.9.0: {}
- astro@7.1.3(@astrojs/markdown-remark@7.2.1)(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.2)(@types/node@24.13.3):
+ astro@7.1.4(@astrojs/markdown-remark@7.2.1)(@emnapi/core@2.0.0-alpha.3)(@emnapi/runtime@2.0.0-alpha.3)(@types/node@24.13.3):
dependencies:
- '@astrojs/compiler-rs': 0.3.1(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.2)
+ '@astrojs/compiler-rs': 0.3.1(@emnapi/core@2.0.0-alpha.3)(@emnapi/runtime@2.0.0-alpha.3)
'@astrojs/internal-helpers': 0.10.1
'@astrojs/markdown-satteri': 0.3.4
'@astrojs/telemetry': 3.3.3
@@ -2665,7 +2691,7 @@ snapshots:
http-cache-semantics: 4.2.0
js-yaml: 4.3.0
jsonc-parser: 3.3.1
- magic-string: 0.30.21
+ magic-string: 1.1.0
magicast: 0.5.3
mrmime: 2.0.1
neotraverse: 1.0.1
@@ -2677,7 +2703,7 @@ snapshots:
picomatch: 4.0.5
semver: 7.8.5
shiki: 4.3.1
- smol-toml: 1.7.0
+ smol-toml: 1.7.1
svgo: 4.0.2
tinyclip: 0.1.15
tinyexec: 1.2.4
@@ -2858,7 +2884,7 @@ snapshots:
esast-util-from-js@2.0.1:
dependencies:
'@types/estree-jsx': 1.0.5
- acorn: 8.17.0
+ acorn: 8.18.0
esast-util-from-estree: 2.0.0
vfile-message: 4.0.3
@@ -2974,9 +3000,9 @@ snapshots:
dependencies:
tiny-inflate: 1.0.3
- framer-motion@12.42.2(react-dom@19.2.8(react@19.2.8))(react@19.2.8):
+ framer-motion@12.43.0(react-dom@19.2.8(react@19.2.8))(react@19.2.8):
dependencies:
- motion-dom: 12.42.2
+ motion-dom: 12.43.0
motion-utils: 12.39.0
tslib: 2.8.1
optionalDependencies:
@@ -3001,7 +3027,7 @@ snapshots:
defu: 6.1.7
destr: 2.0.5
iron-webcrypto: 1.2.1
- node-mock-http: 1.0.4
+ node-mock-http: 1.0.5
radix3: 1.1.2
ufo: 1.6.4
uncrypto: 0.1.3
@@ -3230,7 +3256,7 @@ snapshots:
lru-cache@11.5.2: {}
- magic-string@0.30.21:
+ magic-string@1.1.0:
dependencies:
'@jridgewell/sourcemap-codec': 1.5.5
@@ -3536,8 +3562,8 @@ snapshots:
micromark-extension-mdxjs@3.0.0:
dependencies:
- acorn: 8.17.0
- acorn-jsx: 5.3.2(acorn@8.17.0)
+ acorn: 8.18.0
+ acorn-jsx: 5.3.2(acorn@8.18.0)
micromark-extension-mdx-expression: 3.0.1
micromark-extension-mdx-jsx: 3.0.2
micromark-extension-mdx-md: 2.0.0
@@ -3687,15 +3713,15 @@ snapshots:
dependencies:
mime-db: 1.54.0
- motion-dom@12.42.2:
+ motion-dom@12.43.0:
dependencies:
motion-utils: 12.39.0
motion-utils@12.39.0: {}
- motion@12.42.2(react-dom@19.2.8(react@19.2.8))(react@19.2.8):
+ motion@12.43.0(react-dom@19.2.8(react@19.2.8))(react@19.2.8):
dependencies:
- framer-motion: 12.42.2(react-dom@19.2.8(react@19.2.8))(react@19.2.8)
+ framer-motion: 12.43.0(react-dom@19.2.8(react@19.2.8))(react@19.2.8)
tslib: 2.8.1
optionalDependencies:
react: 19.2.8
@@ -3715,7 +3741,7 @@ snapshots:
node-fetch-native@1.6.7: {}
- node-mock-http@1.0.4: {}
+ node-mock-http@1.0.5: {}
normalize-path@3.0.0: {}
@@ -3791,7 +3817,7 @@ snapshots:
picomatch@4.0.5: {}
- postcss@8.5.22:
+ postcss@8.5.24:
dependencies:
nanoid: 3.3.16
picocolors: 1.1.1
@@ -3830,10 +3856,10 @@ snapshots:
estree-util-build-jsx: 3.0.1
vfile: 6.0.3
- recma-jsx@1.0.1(acorn@8.17.0):
+ recma-jsx@1.0.1(acorn@8.18.0):
dependencies:
- acorn: 8.17.0
- acorn-jsx: 5.3.2(acorn@8.17.0)
+ acorn: 8.18.0
+ acorn-jsx: 5.3.2(acorn@8.18.0)
estree-util-to-js: 2.0.0
recma-parse: 1.0.0
recma-stringify: 1.0.0
@@ -4002,7 +4028,7 @@ snapshots:
'@bruits/satteri-win32-arm64-msvc': 0.9.5
'@bruits/satteri-win32-x64-msvc': 0.9.5
- sax@1.6.0: {}
+ sax@1.6.1: {}
scheduler@0.27.0: {}
@@ -4079,9 +4105,9 @@ snapshots:
'@types/node': 24.13.3
'@types/sax': 1.2.7
arg: 5.0.2
- sax: 1.6.0
+ sax: 1.6.1
- smol-toml@1.7.0: {}
+ smol-toml@1.7.1: {}
source-map-js@1.2.1: {}
@@ -4122,7 +4148,7 @@ snapshots:
css-what: 6.2.2
csso: 5.0.5
picocolors: 1.1.1
- sax: 1.6.0
+ sax: 1.6.1
tiny-inflate@1.0.3: {}
@@ -4243,7 +4269,7 @@ snapshots:
dependencies:
lightningcss: 1.33.0
picomatch: 4.0.5
- postcss: 8.5.22
+ postcss: 8.5.24
rolldown: 1.1.5
tinyglobby: 0.2.17
optionalDependencies:
diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml
index 4a22b6b..98ef76b 100644
--- a/pnpm-workspace.yaml
+++ b/pnpm-workspace.yaml
@@ -1,6 +1,5 @@
allowBuilds:
esbuild: true
- sharp: true
minimumReleaseAgeExclude:
- '@astrojs/internal-helpers@0.10.2'
- '@astrojs/node@11.0.3'
diff --git a/src/components/ContactForm/ContactForm.astro b/src/components/ContactForm/ContactForm.astro
index f28d627..f96aabf 100644
--- a/src/components/ContactForm/ContactForm.astro
+++ b/src/components/ContactForm/ContactForm.astro
@@ -24,7 +24,7 @@ const t = useTranslations(lang);
/>
-
diff --git a/src/pages/api/subscribe.ts b/src/pages/api/subscribe.ts
deleted file mode 100644
index 29e43b9..0000000
--- a/src/pages/api/subscribe.ts
+++ /dev/null
@@ -1,61 +0,0 @@
-import type { APIRoute } from "astro";
-
-// Говорим Astro, что этот эндпоинт НЕ должен пререндериться в статику
-export const prerender = false;
-
-export const POST: APIRoute = async ({ request }) => {
- try {
- const data = await request.json();
- const { email, name, phone } = data;
-
- if (!email) {
- return new Response(JSON.stringify({ message: "Email required" }), {
- status: 400,
- });
- }
-
- const listmonkUrl = import.meta.env.LISTMONK_URL;
- const username = import.meta.env.LISTMONK_USER;
- const password = import.meta.env.LISTMONK_PASS;
-
- const authHeader =
- "Basic " + Buffer.from(`${username}:${password}`).toString("base64");
-
- const response = await fetch(`${listmonkUrl}/api/subscribers`, {
- method: "POST",
- headers: {
- "Content-Type": "application/json",
- Authorization: authHeader,
- },
- body: JSON.stringify({
- email: email,
- name: name || "",
- status: "enabled",
- lists: [6], // Ваш ID 6
- attribs: {
- phone: phone || "",
- },
- }),
- });
-
- const resData = await response.json();
-
- if (!response.ok) {
- return new Response(
- JSON.stringify({ message: resData.message || "Error subscribing" }),
- {
- status: response.status,
- },
- );
- }
-
- return new Response(JSON.stringify({ success: true, data: resData }), {
- status: 200,
- });
- } catch (error: any) {
- return new Response(
- JSON.stringify({ message: error.message || "Server error" }),
- { status: 500 },
- );
- }
-};
diff --git a/src/styles/index.css b/src/styles/index.css
index 6b68f97..4e477b3 100644
--- a/src/styles/index.css
+++ b/src/styles/index.css
@@ -1,594 +1,668 @@
/* intro */
.intro {
- height: calc(100vh - 112px);
- grid-template-rows: calc(100vh - 112px - 85px - 30px) min-content;
- row-gap: unset;
- padding-top: 112px;
-
- .title {
- grid-column: 1/8;
- grid-row: 1/2;
- display: flex;
- flex-direction: column;
- justify-content: center;
-
- h1 {
- line-height: 120%;
- }
-
- div {
- display: flex;
-
- p {
- text-transform: uppercase;
- color: var(--color-accent-secondary);
-
- &:first-child {
- margin-right: 39px;
- }
-
- &:last-child {
- margin-left: 34px;
- }
- }
- }
- }
-
- .image {
- grid-column: 5/13;
- grid-row: 1/2;
- display: flex;
- align-items: center;
- background-image: url("../assets/images/main/intro/astronaut.png");
- background-repeat: no-repeat;
- background-size: contain;
- background-position: right;
- transform: translateX(150px);
- z-index: -1;
- }
-
- .rocket {
- grid-column: 1/13;
- grid-row: 2/3;
-
- * {
- margin: 0 auto;
- width: max-content;
- }
-
- p {
- color: var(--color-text-secondary);
- margin-top: 15px;
- }
- }
-
- @media (max-width: 1350px) {
- .image {
- transform: translateX(50px);
- }
- }
-
- @media (max-width: 767px) {
- height: auto;
- padding-top: 106px;
- grid-template-rows: 400px min-content;
-
- .title,
- .rocket,
- .image {
- grid-column: 1/7;
- }
-
- .image {
- grid-row: 1/2;
- transform: none;
- justify-content: center;
- background-position: center;
- }
+ height: calc(100vh - 112px);
+ grid-template-rows: calc(100vh - 112px - 85px - 30px) min-content;
+ row-gap: unset;
+ padding-top: 112px;
.title {
- grid-row: 2/3;
-
- h1 {
- text-align: center;
-
- span {
- display: none;
- }
- }
-
- div {
- width: 100%;
- text-align: center;
+ grid-column: 1/8;
+ grid-row: 1/2;
display: flex;
- justify-content: space-between;
- margin-top: 16px;
- }
+ flex-direction: column;
+ justify-content: center;
+
+ h1 {
+ line-height: 120%;
+ }
+
+ div {
+ display: flex;
+
+ p {
+ text-transform: uppercase;
+ color: var(--color-accent-secondary);
+
+ &:first-child {
+ margin-right: 39px;
+ }
+
+ &:last-child {
+ margin-left: 34px;
+ }
+ }
+ }
+ }
+
+ .image {
+ grid-column: 5/13;
+ grid-row: 1/2;
+ display: flex;
+ align-items: center;
+ background-image: url("../assets/images/main/intro/astronaut.png");
+ background-repeat: no-repeat;
+ background-size: contain;
+ background-position: right;
+ transform: translateX(150px);
+ z-index: -1;
}
.rocket {
- display: none;
+ grid-column: 1/13;
+ grid-row: 2/3;
+
+ * {
+ margin: 0 auto;
+ width: max-content;
+ }
+
+ p {
+ color: var(--color-text-secondary);
+ margin-top: 15px;
+ }
+ }
+
+ @media (max-width: 1350px) {
+ .image {
+ transform: translateX(50px);
+ }
+ }
+
+ @media (max-width: 767px) {
+ height: auto;
+ padding-top: 106px;
+ grid-template-rows: 400px min-content;
+
+ .title,
+ .rocket,
+ .image {
+ grid-column: 1/7;
+ }
+
+ .image {
+ grid-row: 1/2;
+ transform: none;
+ justify-content: center;
+ background-position: center;
+ }
+
+ .title {
+ grid-row: 2/3;
+
+ h1 {
+ text-align: center;
+
+ span {
+ display: none;
+ }
+ }
+
+ div {
+ width: 100%;
+ text-align: center;
+ display: flex;
+ justify-content: space-between;
+ margin-top: 16px;
+ }
+ }
+
+ .rocket {
+ display: none;
+ }
}
- }
}
/* section */
section.content {
- margin-top: 192px;
+ margin-top: 192px;
- h2 {
- text-align: center;
- margin-bottom: 88px;
+ h2 {
+ text-align: center;
+ margin-bottom: 88px;
- @media all and (max-width: 767px) {
- margin-bottom: 54px;
+ @media all and (max-width: 767px) {
+ margin-bottom: 54px;
+ }
}
- }
}
/* services */
#services {
- scroll-margin-block-start: 120px;
+ scroll-margin-block-start: 120px;
}
.services {
- background: radial-gradient(
- 48.62% 45.5% at 50% 50%,
- rgba(225, 104, 154, 0.15) 0%,
- rgba(141, 162, 243, 0.063) 49.1%,
- rgba(23, 23, 23, 0) 100%
- );
- position: relative;
+ background: radial-gradient(
+ 48.62% 45.5% at 50% 50%,
+ rgba(225, 104, 154, 0.15) 0%,
+ rgba(141, 162, 243, 0.063) 49.1%,
+ rgba(23, 23, 23, 0) 100%
+ );
+ position: relative;
- .circle {
- position: absolute;
- top: -140px;
- left: -530px;
+ .circle {
+ position: absolute;
+ top: -140px;
+ left: -530px;
- @media all and (max-width: 767px) {
- top: 10%;
- right: 50%;
- left: unset;
+ @media all and (max-width: 767px) {
+ top: 10%;
+ right: 50%;
+ left: unset;
+ }
}
- }
}
/* card */
.card {
- background: rgba(255, 255, 255, 0.01);
- box-shadow: inset 2px 2px 5px rgba(255, 255, 255, 0.15);
- backdrop-filter: blur(7px);
- border-radius: 33px;
- padding: 63px 55px;
- display: flex;
- flex-direction: column;
- align-items: center;
+ background: rgba(255, 255, 255, 0.01);
+ box-shadow: inset 2px 2px 5px rgba(255, 255, 255, 0.15);
+ backdrop-filter: blur(7px);
+ border-radius: 33px;
+ padding: 63px 55px;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
- img {
- min-height: 99px;
- }
+ img {
+ min-height: 99px;
+ }
- h3 {
- margin-top: 63px;
- margin-bottom: 16px;
- }
+ h3 {
+ margin-top: 63px;
+ margin-bottom: 16px;
+ }
- h3,
- p {
- text-align: center;
- }
-
- &:nth-of-type(1) {
- grid-column: 1/5;
- }
-
- &:nth-of-type(2) {
- grid-column: 5/9;
- }
-
- &:nth-of-type(3) {
- grid-column: 9/13;
- }
-
- @media (max-width: 767px) {
- padding: 46px 30px;
+ h3,
+ p {
+ text-align: center;
+ }
&:nth-of-type(1) {
- grid-column: 1/7;
+ grid-column: 1/5;
}
&:nth-of-type(2) {
- grid-column: 1/7;
+ grid-column: 5/9;
}
&:nth-of-type(3) {
- grid-column: 1/7;
+ grid-column: 9/13;
+ }
+
+ @media (max-width: 767px) {
+ padding: 46px 30px;
+
+ &:nth-of-type(1) {
+ grid-column: 1/7;
+ }
+
+ &:nth-of-type(2) {
+ grid-column: 1/7;
+ }
+
+ &:nth-of-type(3) {
+ grid-column: 1/7;
+ }
}
- }
}
/* about */
#about-us {
- scroll-margin-block-start: 120px;
+ scroll-margin-block-start: 120px;
}
.about-us {
- margin-top: -80px;
+ margin-top: -80px;
- .header5 {
- text-align: left;
- margin-bottom: 0;
- }
-
- .rocket-img {
- width: 100%;
- height: auto;
- }
-
- > div {
- &:first-child {
- grid-column: 1/7;
+ .header5 {
+ text-align: left;
+ margin-bottom: 0;
}
- &:last-child {
- grid-column: 7/13;
- display: flex;
- flex-direction: column;
- justify-content: center;
-
- .text {
- margin-top: 16px;
- color: var(--color-text-secondary);
- }
-
- .stats {
- display: flex;
- justify-content: space-between;
- margin-top: 64px;
+ .rocket-img {
width: 100%;
-
- .swiper-slide {
- width: 33%;
- }
-
- @media (max-width: 767px) {
- text-align: center;
-
- & > div {
- padding: 4px;
- }
- }
- }
-
- @media (max-width: 767px) {
- grid-column: 1/7;
-
- h2,
- .text {
- text-align: center;
- }
- }
+ height: auto;
+ }
+
+ > div {
+ &:first-child {
+ grid-column: 1/7;
+ }
+
+ &:last-child {
+ grid-column: 7/13;
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+
+ .text {
+ margin-top: 16px;
+ color: var(--color-text-secondary);
+ }
+
+ .stats {
+ display: flex;
+ justify-content: space-between;
+ margin-top: 64px;
+ width: 100%;
+
+ .swiper-slide {
+ width: 33%;
+ }
+
+ @media (max-width: 767px) {
+ text-align: center;
+
+ & > div {
+ padding: 4px;
+ }
+ }
+ }
+
+ @media (max-width: 767px) {
+ grid-column: 1/7;
+
+ h2,
+ .text {
+ text-align: center;
+ }
+ }
+ }
}
- }
}
/* projects */
#projects {
- scroll-margin-block-start: 120px;
+ scroll-margin-block-start: 120px;
}
.projects-grid {
- display: grid;
- grid-template-columns: repeat(12, minmax(0, 1fr));
- gap: 24px;
-
- .project-card {
- grid-column: span 6;
- background: linear-gradient(
- 145deg,
- rgba(255, 255, 255, 0.08),
- rgba(255, 255, 255, 0.03)
- );
- border: 1px solid rgba(255, 255, 255, 0.12);
- border-radius: 24px;
- overflow: hidden;
display: grid;
- grid-template-rows: auto 1fr;
- backdrop-filter: blur(8px);
- }
-
- .project-card__media {
- aspect-ratio: 16 / 10;
- overflow: hidden;
-
- img {
- width: 100%;
- height: 100%;
- object-fit: cover;
- display: block;
- transition: transform 0.35s ease;
- }
- }
-
- .project-card__content {
- padding: 22px 22px 24px;
-
- h3 {
- margin-bottom: 12px;
- }
-
- p {
- color: var(--color-text-secondary);
- }
- }
-
- .project-card__tags {
- margin: 0 0 14px;
- padding: 0;
- list-style: none;
- display: flex;
- flex-wrap: wrap;
- gap: 8px;
-
- li {
- border: 1px solid rgba(255, 255, 255, 0.2);
- border-radius: 999px;
- padding: 6px 10px;
- color: var(--color-text-primary);
- font-size: 0.75rem;
- text-transform: uppercase;
- letter-spacing: 0.04em;
- }
- }
-
- .project-card:hover .project-card__media img {
- transform: scale(1.04);
- }
-
- @media (max-width: 1024px) {
- .project-card {
- grid-column: span 12;
- }
- }
-
- @media (max-width: 767px) {
- gap: 16px;
+ grid-template-columns: repeat(12, minmax(0, 1fr));
+ gap: 24px;
.project-card {
- border-radius: 18px;
+ grid-column: span 6;
+ background: linear-gradient(
+ 145deg,
+ rgba(255, 255, 255, 0.08),
+ rgba(255, 255, 255, 0.03)
+ );
+ border: 1px solid rgba(255, 255, 255, 0.12);
+ border-radius: 24px;
+ overflow: hidden;
+ display: grid;
+ grid-template-rows: auto 1fr;
+ backdrop-filter: blur(8px);
+ }
+
+ .project-card__media {
+ aspect-ratio: 16 / 10;
+ overflow: hidden;
+
+ img {
+ width: 100%;
+ height: 100%;
+ object-fit: cover;
+ display: block;
+ transition: transform 0.35s ease;
+ }
}
.project-card__content {
- padding: 16px;
+ padding: 22px 22px 24px;
+
+ h3 {
+ margin-bottom: 12px;
+ }
+
+ p {
+ color: var(--color-text-secondary);
+ }
+ }
+
+ .project-card__tags {
+ margin: 0 0 14px;
+ padding: 0;
+ list-style: none;
+ display: flex;
+ flex-wrap: wrap;
+ gap: 8px;
+
+ li {
+ border: 1px solid rgba(255, 255, 255, 0.2);
+ border-radius: 999px;
+ padding: 6px 10px;
+ color: var(--color-text-primary);
+ font-size: 0.75rem;
+ text-transform: uppercase;
+ letter-spacing: 0.04em;
+ }
+ }
+
+ .project-card:hover .project-card__media img {
+ transform: scale(1.04);
+ }
+
+ @media (max-width: 1024px) {
+ .project-card {
+ grid-column: span 12;
+ }
+ }
+
+ @media (max-width: 767px) {
+ gap: 16px;
+
+ .project-card {
+ border-radius: 18px;
+ }
+
+ .project-card__content {
+ padding: 16px;
+ }
}
- }
}
/* blog */
.blog-hero {
- min-height: 360px;
- align-content: center;
- padding-top: 112px;
- padding-bottom: 32px;
+ min-height: 360px;
+ align-content: center;
+ padding-top: 112px;
+ padding-bottom: 32px;
- h1 {
- text-align: center;
- margin-bottom: 14px;
- }
+ h1 {
+ text-align: center;
+ margin-bottom: 14px;
+ }
- p {
- text-align: center;
- max-width: 680px;
- margin: 0 auto;
- }
+ p {
+ text-align: center;
+ max-width: 680px;
+ margin: 0 auto;
+ }
- @media (max-width: 767px) {
- min-height: 300px;
- padding-top: 106px;
- }
+ @media (max-width: 767px) {
+ min-height: 300px;
+ padding-top: 106px;
+ }
}
#blog-list {
- margin-top: 88px;
- padding-bottom: 80px;
+ margin-top: 88px;
+ padding-bottom: 80px;
- @media (max-width: 767px) {
- margin-top: 56px;
- padding-bottom: 56px;
- }
+ @media (max-width: 767px) {
+ margin-top: 56px;
+ padding-bottom: 56px;
+ }
}
.blog-grid {
- display: grid;
- grid-template-columns: repeat(12, minmax(0, 1fr));
- gap: 24px;
+ display: grid;
+ grid-template-columns: repeat(12, minmax(0, 1fr));
+ gap: 24px;
}
.blog-card {
- grid-column: span 6;
- border: 1px solid rgba(255, 255, 255, 0.1);
- border-radius: 22px;
- overflow: hidden;
- background: linear-gradient(
- 160deg,
- rgba(255, 255, 255, 0.08),
- rgba(255, 255, 255, 0.02)
- );
- backdrop-filter: blur(7px);
-
- .blog-card__link {
- display: grid;
- grid-template-rows: auto 1fr;
- height: 100%;
- }
-
- .blog-card__media {
- aspect-ratio: 16 / 9;
+ grid-column: span 6;
+ border: 1px solid rgba(255, 255, 255, 0.1);
+ border-radius: 22px;
overflow: hidden;
- }
+ background: linear-gradient(
+ 160deg,
+ rgba(255, 255, 255, 0.08),
+ rgba(255, 255, 255, 0.02)
+ );
+ backdrop-filter: blur(7px);
- .blog-card__media img {
- width: 100%;
- height: 100%;
- object-fit: cover;
- display: block;
- transition: transform 0.35s ease;
- }
+ .blog-card__link {
+ display: grid;
+ grid-template-rows: auto 1fr;
+ height: 100%;
+ }
- .blog-card__content {
- padding: 18px 20px 22px;
- }
+ .blog-card__media {
+ aspect-ratio: 16 / 9;
+ overflow: hidden;
+ }
- h3 {
- margin: 0 0 8px;
- line-height: 1.2;
- }
+ .blog-card__media img {
+ width: 100%;
+ height: 100%;
+ object-fit: cover;
+ display: block;
+ transition: transform 0.35s ease;
+ }
- .blog-card__date {
- color: var(--color-accent-secondary);
- margin: 0 0 12px;
- font-size: 0.95rem;
- text-transform: uppercase;
- letter-spacing: 0.03em;
- }
+ .blog-card__content {
+ padding: 18px 20px 22px;
+ }
- .blog-card__excerpt {
- color: var(--color-text-secondary);
- margin: 0;
- }
+ h3 {
+ margin: 0 0 8px;
+ line-height: 1.2;
+ }
- &:hover .blog-card__media img {
- transform: scale(1.04);
- }
+ .blog-card__date {
+ color: var(--color-accent-secondary);
+ margin: 0 0 12px;
+ font-size: 0.95rem;
+ text-transform: uppercase;
+ letter-spacing: 0.03em;
+ }
+
+ .blog-card__excerpt {
+ color: var(--color-text-secondary);
+ margin: 0;
+ }
+
+ &:hover .blog-card__media img {
+ transform: scale(1.04);
+ }
}
@media (max-width: 1024px) {
- .blog-card {
- grid-column: span 12;
- }
+ .blog-card {
+ grid-column: span 12;
+ }
}
/* blog post */
.blog-post-page main {
- width: auto;
- max-width: 100%;
- margin: 0 2rem;
- padding-top: 176px;
+ width: auto;
+ max-width: 100%;
+ margin: 0 2rem;
+ padding-top: 176px;
}
@media (max-width: 767px) {
- .blog-post-page main {
- width: auto;
- margin: 0 1rem;
- padding-top: 124px;
- }
+ .blog-post-page main {
+ width: auto;
+ margin: 0 1rem;
+ padding-top: 124px;
+ }
}
.blog-post-page .hero-image {
- width: 100%;
+ width: 100%;
}
.blog-post-page .hero-image__media {
- width: 100%;
- max-width: 1020px;
- height: auto;
- display: block;
- margin: 0 auto;
- border-radius: 12px;
- box-shadow: var(--box-shadow);
+ width: 100%;
+ max-width: 1020px;
+ height: auto;
+ display: block;
+ margin: 0 auto;
+ border-radius: 12px;
+ box-shadow: var(--box-shadow);
}
.blog-post-page .prose {
- width: 720px;
- max-width: calc(100% - 2em);
- margin: auto;
- padding: 1em;
- color: rgb(var(--gray-dark));
+ width: 720px;
+ max-width: calc(100% - 2em);
+ margin: auto;
+ padding: 1em;
+ color: rgb(var(--gray-dark));
}
.blog-post-page .prose picture {
- display: block;
- width: 100%;
- max-width: 1020px;
- margin: 1.25rem auto;
+ display: block;
+ width: 100%;
+ max-width: 1020px;
+ margin: 1.25rem auto;
}
.blog-post-page .prose img {
- width: 100%;
- max-width: 1020px;
- height: auto;
- display: block;
- margin: 1.25rem auto;
- border-radius: 12px;
- box-shadow: var(--box-shadow);
+ width: 100%;
+ max-width: 1020px;
+ height: auto;
+ display: block;
+ margin: 1.25rem auto;
+ border-radius: 12px;
+ box-shadow: var(--box-shadow);
}
.blog-post-page .title {
- margin-bottom: 1em;
- padding: 1em 0;
- text-align: center;
- line-height: 1;
+ margin-bottom: 1em;
+ padding: 1em 0;
+ text-align: center;
+ line-height: 1;
}
.blog-post-page .title h1 {
- margin: 0 0 0.5em 0;
+ margin: 0 0 0.5em 0;
}
.blog-post-page .date {
- margin-bottom: 0.5em;
- color: rgb(var(--gray));
+ margin-bottom: 0.5em;
+ color: rgb(var(--gray));
}
.blog-post-page .last-updated-on {
- font-style: italic;
+ font-style: italic;
}
.blog-post-page .blog-nav-link {
- display: inline-flex;
- align-items: center;
- gap: 10px;
- border: 1px solid rgba(255, 255, 255, 0.24);
- border-radius: 999px;
- padding: 10px 16px;
- font-size: 0.875rem;
- letter-spacing: 0.04em;
- text-transform: uppercase;
- transition:
- border-color 0.25s ease,
+ display: inline-flex;
+ align-items: center;
+ gap: 10px;
+ border: 1px solid rgba(255, 255, 255, 0.24);
+ border-radius: 999px;
+ padding: 10px 16px;
+ font-size: 0.875rem;
+ letter-spacing: 0.04em;
+ text-transform: uppercase;
+ transition: border-color 0.25s ease,
transform 0.25s ease,
background-color 0.25s ease;
- background: rgba(255, 255, 255, 0.04);
+ background: rgba(255, 255, 255, 0.04);
}
.blog-post-page .blog-nav-link:hover {
- border-color: rgba(255, 255, 255, 0.48);
- background: rgba(255, 255, 255, 0.08);
- transform: translateY(-2px);
+ border-color: rgba(255, 255, 255, 0.48);
+ background: rgba(255, 255, 255, 0.08);
+ transform: translateY(-2px);
}
.blog-post-page .blog-nav-link::before {
- content: "\2190";
- font-size: 1rem;
- line-height: 1;
+ content: "\2190";
+ font-size: 1rem;
+ line-height: 1;
}
.blog-post-page .blog-nav-link.bottom {
- margin: 28px auto 0;
+ margin: 28px auto 0;
}
.blog-post-page .blog-nav-wrap {
- width: 720px;
- max-width: calc(100% - 2em);
- margin: 0 auto;
- padding: 0 1em;
- display: flex;
- justify-content: center;
+ width: 720px;
+ max-width: calc(100% - 2em);
+ margin: 0 auto;
+ padding: 0 1em;
+ display: flex;
+ justify-content: center;
+}
+
+/* If you already have a design token, replace this variable */
+:root {
+ --color-text-primary: #fff;
+}
+
+@keyframes example {
+ 0% {
+ background-position: -100% -100%;
+ }
+ 100% {
+ background-position: 100% 100%;
+ }
+}
+
+button.btn {
+ background: radial-gradient(
+ 35.83% 70.83% at 0% 0%,
+ rgba(93, 52, 69, 0.15) 0%,
+ rgba(161, 183, 254, 0) 100%
+ ),
+ linear-gradient(
+ 180deg,
+ rgba(255, 255, 255, 0.05) 0%,
+ rgba(255, 255, 255, 0) 100%
+ ),
+ #111;
+ border-radius: 37.5px;
+ color: var(--color-text-primary);
+ text-transform: uppercase;
+ font-weight: 700;
+ padding-top: 16px;
+ padding-bottom: 16px;
+ border: 2px solid transparent;
+ position: relative;
+ background-clip: padding-box;
+}
+
+button.btn::after {
+ content: "";
+ position: absolute;
+ inset: -2px;
+ z-index: -1;
+ border-radius: 37.5px;
+ background: linear-gradient(
+ 172.29deg,
+ rgba(241, 161, 254, 0.3) 6.23%,
+ rgba(161, 183, 254, 0) 94.36%
+ ),
+ linear-gradient(
+ 180deg,
+ rgba(255, 255, 255, 0.5) 0%,
+ rgba(170, 170, 170, 0.04) 100%
+ );
+}
+
+button.btn:hover {
+ background: linear-gradient(
+ 180deg,
+ rgba(194, 64, 118, 0.25) 0%,
+ rgba(0, 40, 170, 0) 100%
+ ),
+ linear-gradient(
+ 180deg,
+ rgba(255, 255, 255, 0.05) 0%,
+ rgba(255, 255, 255, 0) 100%
+ ),
+ #111;
+ background-clip: padding-box;
+ background-size: 100% 100%;
+}
+
+button.btn:hover::after {
+ background: none;
}