Compare commits
39 Commits
02102dd046
...
finn-stagi
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
74793fbc11 | ||
|
|
a8229e543e | ||
|
|
37055ca74a | ||
|
|
2a2d5fb9e5 | ||
|
|
0220aa5ff8 | ||
|
|
7f7b44415e | ||
|
|
61ef7c14de | ||
| b8103ea072 | |||
| a481c382a6 | |||
| 5f9e8db7e5 | |||
| fe14ca30ff | |||
| cf9cbb6134 | |||
| 77485eee63 | |||
| 6e90a4adb6 | |||
| 3933cf42c0 | |||
| df6cff4895 | |||
|
|
7b45ca94a6 | ||
| 82e3a23df3 | |||
|
|
c09ba76350 | ||
| 66f52a2d6e | |||
|
|
ff7e4395ea | ||
| b6e6178466 | |||
| ce1095088d | |||
|
|
a8474fd208 | ||
|
|
c587df063b | ||
|
|
c7a205e40c | ||
|
|
92bd81aed4 | ||
|
|
33b1f7f9dd | ||
|
|
d31a13cbbe | ||
|
|
6f8d36140a | ||
| e5bcb7bad2 | |||
|
|
dbda554d28 | ||
|
|
93049e9044 | ||
|
|
a6bd0ca864 | ||
|
|
dbaad19d0b | ||
|
|
4134aec1f8 | ||
|
|
a23dcf0a95 | ||
|
|
b0329e3863 | ||
|
|
ede669051b |
10
.env.deploy.example
Normal file
@@ -0,0 +1,10 @@
|
||||
# Copy to .env.deploy for local runs: set -a && source .env.deploy && set +a && ./scripts/deploy-talkpro.sh
|
||||
#
|
||||
# Gitea Actions: only this secret is required (Settings → Secrets → Actions):
|
||||
# TALKPRO_SSH_PRIVATE_KEY = full PEM file contents
|
||||
# Host/user/path defaults are in .gitea/workflows/deploy-talkpro.yml
|
||||
|
||||
TALKPRO_HOST=13.214.179.69
|
||||
TALKPRO_USER=ubuntu
|
||||
TALKPRO_REMOTE_ROOT=/home/ubuntu/talkpro
|
||||
TALKPRO_SSH_KEY_FILE=/absolute/path/to/luis-only.pem
|
||||
57
.gitea/workflows/deploy-talkpro.yml
Normal file
@@ -0,0 +1,57 @@
|
||||
# Build talk-pro and rsync dist/ to the marketing VPS (talkpro.info).
|
||||
#
|
||||
# Required Gitea repo secret (Settings → Secrets → Actions):
|
||||
# TALKPRO_SSH_PRIVATE_KEY full PEM for ubuntu@talkpro (same as luis-only.pem)
|
||||
#
|
||||
# Host/user/path defaults are in the `env:` block below (edit there if needed).
|
||||
#
|
||||
# Requires a runner with: node 22+, ssh, ssh-keyscan, tar (rsync optional; no sudo needed).
|
||||
|
||||
name: Deploy to talkpro
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- master
|
||||
workflow_dispatch:
|
||||
|
||||
env:
|
||||
TALKPRO_HOST: "13.214.179.69"
|
||||
TALKPRO_USER: ubuntu
|
||||
TALKPRO_REMOTE_ROOT: /home/ubuntu/talkpro
|
||||
|
||||
jobs:
|
||||
build-and-sync:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: "22"
|
||||
cache: npm
|
||||
|
||||
- name: Check deploy secrets
|
||||
env:
|
||||
TALKPRO_SSH_PRIVATE_KEY: ${{ secrets.TALKPRO_SSH_PRIVATE_KEY }}
|
||||
run: |
|
||||
if [ -z "${TALKPRO_SSH_PRIVATE_KEY}" ]; then
|
||||
echo "ERROR: Missing Gitea secret TALKPRO_SSH_PRIVATE_KEY"
|
||||
echo "Add it under Repository → Settings → Secrets (Actions)."
|
||||
echo "Value: full contents of your ubuntu@talkpro SSH private key (PEM)."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: Trust host key
|
||||
run: |
|
||||
mkdir -p ~/.ssh
|
||||
chmod 700 ~/.ssh
|
||||
ssh-keyscan -H "$TALKPRO_HOST" >> ~/.ssh/known_hosts 2>/dev/null || true
|
||||
|
||||
- name: Build and deploy to talkpro
|
||||
env:
|
||||
TALKPRO_SSH_PRIVATE_KEY: ${{ secrets.TALKPRO_SSH_PRIVATE_KEY }}
|
||||
run: bash scripts/deploy-talkpro.sh
|
||||
2
.gitignore
vendored
@@ -5,3 +5,5 @@ dist/
|
||||
.env
|
||||
.env.*
|
||||
!.env.example
|
||||
!.env.deploy.example
|
||||
.env.deploy
|
||||
|
||||
22
README.md
@@ -26,6 +26,28 @@ Open [http://localhost:4321](http://localhost:4321)
|
||||
| `npm run build` | Build for production into `dist/` |
|
||||
| `npm run preview` | Preview production build locally |
|
||||
|
||||
## Deploy (Gitea Actions → talkpro.info)
|
||||
|
||||
Pushes to **`main`** or **`master`** run [`.gitea/workflows/deploy-talkpro.yml`](.gitea/workflows/deploy-talkpro.yml): `npm ci` → `npm run build` → `rsync` `dist/` to the marketing server (`/home/ubuntu/talkpro`).
|
||||
|
||||
**Repository secret** (Gitea → Settings → Secrets → Actions) — **required**:
|
||||
|
||||
| Secret | Value |
|
||||
|--------|--------|
|
||||
| `TALKPRO_SSH_PRIVATE_KEY` | Full PEM text of the `ubuntu@talkpro` deploy key |
|
||||
|
||||
Host (`13.214.179.69`), user (`ubuntu`), and web root (`/home/ubuntu/talkpro`) are set in the workflow. Optional secrets `TALKPRO_HOST`, `TALKPRO_USER`, `TALKPRO_REMOTE_ROOT` override those defaults.
|
||||
|
||||
Manual deploy from this repo:
|
||||
|
||||
```bash
|
||||
cp .env.deploy.example .env.deploy # edit paths
|
||||
set -a && source .env.deploy && set +a
|
||||
bash scripts/deploy-talkpro.sh
|
||||
```
|
||||
|
||||
`/api/site-links` (APK / App Store URLs) is still updated via the parent **talkpro** repo: `./scripts/post-talkpro-site-links.sh` on your laptop.
|
||||
|
||||
## Project Structure
|
||||
|
||||
```
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
<svg preserveAspectRatio="none" width="100%" height="100%" overflow="visible" style="display: block;" viewBox="0 0 44 44" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g id="Icon">
|
||||
<rect width="44" height="44" rx="12" fill="var(--fill-0, #D55F31)"/>
|
||||
<path id="Vector" d="M23.8954 10H15.6259C14.728 10 14 10.8028 14 11.793V32.207C14 33.1972 14.728 34 15.6259 34H28.3741C29.272 34 30 33.1972 30 32.207V16.7328C30 16.1165 29.7778 15.5252 29.3827 15.0895L25.3848 10.6807C24.9897 10.245 24.4536 10 23.8947 10H23.8954Z" fill="var(--fill-0, white)"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 559 B |
@@ -1,6 +0,0 @@
|
||||
<svg preserveAspectRatio="none" width="100%" height="100%" overflow="visible" style="display: block;" viewBox="0 0 22.0004 26.9856" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g id="Group">
|
||||
<path id="Vector" d="M16.1918 0C16.2546 0 16.3174 0 16.3838 0C16.5378 1.90308 15.8115 3.32506 14.9286 4.35481C14.0624 5.37746 12.8762 6.36929 10.9578 6.21879C10.8298 4.34297 11.5574 3.02645 12.439 1.99907C13.2566 1.0416 14.7556 0.189597 16.1918 0Z" fill="var(--fill-0, white)"/>
|
||||
<path id="Vector_2" d="M22.0004 19.8082C22.0004 19.8271 22.0004 19.8437 22.0004 19.8615C21.4612 21.4944 20.6922 22.8939 19.7537 24.1926C18.8969 25.3717 17.847 26.9584 15.9724 26.9584C14.3525 26.9584 13.2766 25.9168 11.6164 25.8883C9.86025 25.8599 8.89449 26.7593 7.28883 26.9856C7.10516 26.9856 6.92149 26.9856 6.74137 26.9856C5.56231 26.815 4.61077 25.8812 3.91756 25.0399C1.87346 22.5538 0.293876 19.3425 0 15.233C0 14.8301 0 14.4284 0 14.0255C0.124423 11.0843 1.55351 8.69304 3.45304 7.53413C4.45554 6.91794 5.83367 6.39299 7.36823 6.62762C8.02589 6.72953 8.69778 6.95467 9.28672 7.17745C9.84484 7.39193 10.5428 7.77231 11.204 7.75217C11.6519 7.73913 12.0975 7.50569 12.549 7.34098C13.8714 6.86343 15.1678 6.31597 16.8765 6.57311C18.9301 6.88357 20.3876 7.79601 21.2882 9.20377C19.551 10.3094 18.1776 11.9754 18.4123 14.8206C18.6208 17.405 20.1234 18.9171 22.0004 19.8082Z" fill="var(--fill-0, white)"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 716 KiB After Width: | Height: | Size: 414 KiB |
|
Before Width: | Height: | Size: 427 KiB After Width: | Height: | Size: 386 KiB |
|
Before Width: | Height: | Size: 602 KiB After Width: | Height: | Size: 316 KiB |
|
Before Width: | Height: | Size: 417 KiB After Width: | Height: | Size: 1.1 MiB |
|
Before Width: | Height: | Size: 2.8 MiB After Width: | Height: | Size: 473 KiB |
@@ -1,6 +1,13 @@
|
||||
<svg preserveAspectRatio="none" width="100%" height="100%" overflow="visible" style="display: block;" viewBox="0 0 44 44" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g id="Icon">
|
||||
<rect width="44" height="44" rx="12" fill="var(--fill-0, #D55F31)"/>
|
||||
<path id="Vector" d="M23.8954 10H15.6259C14.728 10 14 10.8028 14 11.793V32.207C14 33.1972 14.728 34 15.6259 34H28.3741C29.272 34 30 33.1972 30 32.207V16.7328C30 16.1165 29.7778 15.5252 29.3827 15.0895L25.3848 10.6807C24.9897 10.245 24.4536 10 23.8947 10H23.8954Z" fill="var(--fill-0, white)"/>
|
||||
<svg width="28" height="28" viewBox="0 0 28 28" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_9846_29800)">
|
||||
<path d="M3.79923 9.12891H3.7275C2.80804 9.12891 2.05835 9.88098 2.05835 10.7981V18.0653C2.05835 18.986 2.80804 19.7356 3.7275 19.7356H3.80045C4.71991 19.7356 5.4696 18.9835 5.4696 18.0653V10.798C5.46838 9.88098 4.71753 9.12891 3.79923 9.12891Z" fill="white"/>
|
||||
<path d="M6.26831 20.8732C6.26831 21.7173 6.95819 22.4048 7.80233 22.4048H9.4416V26.3302C9.4416 27.252 10.1937 28.0017 11.1108 28.0017H11.1825C12.1032 28.0017 12.854 27.2509 12.854 26.3302V22.4048H15.1449V26.3302C15.1449 27.252 15.8994 28.0017 16.8165 28.0017H16.887C17.8077 28.0017 18.5573 27.2509 18.5573 26.3302V22.4048H20.1978C21.0407 22.4048 21.7306 21.7173 21.7306 20.8732V9.39844H6.26831V20.8732Z" fill="white"/>
|
||||
<path d="M17.8506 2.43795L19.1527 0.428069C19.2364 0.301301 19.1993 0.127973 19.0714 0.045438C18.9447 -0.0382608 18.7713 -0.00356184 18.6887 0.126753L17.3389 2.20603C16.3262 1.79114 15.1951 1.558 14.0006 1.558C12.8049 1.558 11.6762 1.79114 10.6611 2.20603L9.3136 0.126753C9.23112 -0.00356184 9.05651 -0.0382608 8.92858 0.045438C8.80065 0.127917 8.76357 0.301301 8.84727 0.428069L10.1505 2.43795C7.80103 3.58939 6.2168 5.75951 6.2168 8.24886C6.2168 8.4019 6.22639 8.55256 6.23952 8.70199H21.7628C21.7759 8.55256 21.7843 8.4019 21.7843 8.24886C21.7843 5.75951 20.1989 3.58939 17.8506 2.43795ZM10.4016 6.03688C9.98912 6.03688 9.65432 5.70447 9.65432 5.2908C9.65432 4.87713 9.98912 4.54588 10.4016 4.54588C10.8165 4.54588 11.1489 4.87707 11.1489 5.2908C11.1489 5.70453 10.8141 6.03688 10.4016 6.03688ZM17.5983 6.03688C17.1858 6.03688 16.851 5.70447 16.851 5.2908C16.851 4.87713 17.1858 4.54588 17.5983 4.54588C18.012 4.54588 18.3444 4.87707 18.3444 5.2908C18.3444 5.70447 18.012 6.03688 17.5983 6.03688Z" fill="white"/>
|
||||
<path d="M24.2714 9.12891H24.2021C23.2826 9.12891 22.5305 9.88098 22.5305 10.7981V18.0653C22.5305 18.986 23.2838 19.7356 24.2021 19.7356H24.2726C25.1932 19.7356 25.9418 18.9835 25.9418 18.0653V10.798C25.9418 9.88098 25.1909 9.12891 24.2714 9.12891Z" fill="white"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_9846_29800">
|
||||
<rect width="28" height="28" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 559 B After Width: | Height: | Size: 2.2 KiB |
@@ -1,5 +1,5 @@
|
||||
<svg preserveAspectRatio="none" width="100%" height="100%" overflow="visible" style="display: block;" viewBox="0 0 1920 923" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g id="Layer_1" clip-path="url(#clip0_0_16)">
|
||||
<svg preserveAspectRatio="xMidYMid slice" width="1920" height="923" overflow="visible" style="display: block;" viewBox="0 0 1920 923" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g id="Layer_1" clip-path="url(#clip0_0_4)">
|
||||
<path id="Vector" d="M12.1851 -29.997L0.00488281 -17.7969L12.1851 -5.59676L24.3653 -17.7969L12.1851 -29.997Z" fill="var(--fill-0, #FFF0E8)"/>
|
||||
<path id="Vector_2" d="M102.45 -30.0001L90.2725 -17.7969L102.45 -5.59361L114.627 -17.7969L102.45 -30.0001Z" fill="var(--fill-0, #FFF0E8)"/>
|
||||
<path id="Vector_3" d="M192.717 -30.0001L180.54 -17.7969L192.717 -5.59361L204.894 -17.7969L192.717 -30.0001Z" fill="var(--fill-0, #FFF0E8)"/>
|
||||
@@ -332,7 +332,7 @@
|
||||
<path id="Vector_330" d="M1907.82 1018.59L1895.65 1030.8L1907.82 1043L1920 1030.8L1907.82 1018.59Z" fill="var(--fill-0, #FFF0E8)"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_0_16">
|
||||
<clipPath id="clip0_0_4">
|
||||
<rect width="1920" height="923" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
|
Before Width: | Height: | Size: 45 KiB After Width: | Height: | Size: 45 KiB |
|
Before Width: | Height: | Size: 6.1 KiB After Width: | Height: | Size: 6.1 KiB |
|
Before Width: | Height: | Size: 343 KiB After Width: | Height: | Size: 369 KiB |
|
Before Width: | Height: | Size: 201 KiB After Width: | Height: | Size: 34 KiB |
|
Before Width: | Height: | Size: 284 KiB After Width: | Height: | Size: 206 KiB |
@@ -1,3 +0,0 @@
|
||||
<svg preserveAspectRatio="none" width="100%" height="100%" overflow="visible" style="display: block;" viewBox="0 0 1180 1" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<line id="Line" y1="0.5" x2="1180" y2="0.5" stroke="var(--stroke-0, #E3D9D1)"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 258 B |
|
Before Width: | Height: | Size: 848 KiB After Width: | Height: | Size: 880 KiB |
|
Before Width: | Height: | Size: 1.1 MiB |
|
Before Width: | Height: | Size: 716 KiB |
|
Before Width: | Height: | Size: 427 KiB |
|
Before Width: | Height: | Size: 602 KiB |
|
Before Width: | Height: | Size: 417 KiB |
|
Before Width: | Height: | Size: 161 KiB |
|
Before Width: | Height: | Size: 1.5 MiB |
@@ -1,14 +0,0 @@
|
||||
<svg preserveAspectRatio="none" width="100%" height="100%" overflow="visible" style="display: block;" viewBox="0 0 77.9999 27.3466" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g id="Group 8">
|
||||
<path id="Vector" d="M40.9405 20.8327C43.8201 20.792 46.7009 20.8546 49.5757 21.02C58.0611 21.5051 67.1544 23.0697 75.207 25.9203C75.7287 26.1051 76.704 26.4048 77.0609 26.8008C77.2687 28.0927 72.5969 26.7 72.1119 26.5908C63.0951 24.587 53.8933 23.5076 44.6529 23.3707C33.1497 23.0089 20.9234 24.7233 9.72826 27.2833C9.29807 27.3815 8.61557 27.3918 8.39051 26.9468C8.49032 26.3331 10.397 25.7556 10.9663 25.5599C20.5216 22.2771 30.8642 21.0698 40.9405 20.8327Z" fill="var(--fill-0, #F28A4B)"/>
|
||||
<path id="Vector_2" d="M31.318 0.0224661C31.8649 0.0139438 32.8866 -0.104058 33.2606 0.300423C33.3065 0.889771 32.4703 4.77527 32.3052 5.60259C32.0856 6.70196 31.7116 9.06001 31.422 10.1043C32.5555 9.18653 33.6855 8.09699 34.7424 7.13332C36.0662 5.92709 36.4364 5.25449 38.3791 5.48983C38.8939 5.55211 39.7945 5.27874 40.0876 5.81696C40.07 5.99789 40.0826 6.0228 39.9339 6.16768C38.2986 7.76135 36.4564 9.19244 34.775 10.7363C35.619 12.4178 36.9093 14.4474 37.8796 16.1165C38.0982 16.4652 38.3254 16.7563 38.3624 17.1608C38.3115 17.324 38.2538 17.3896 38.1055 17.4787C37.5409 17.8183 35.5396 17.7435 34.913 17.5403C34.2374 17.322 32.3776 13.308 31.9903 12.5528C31.5792 12.9003 31.1699 13.2497 30.7626 13.6017C30.5008 14.6369 30.4536 15.7264 30.2 16.7504C29.9334 17.7652 29.0813 17.7055 28.3005 17.6603C27.1169 17.5915 26.5033 17.9455 26.7986 16.441C27.0193 15.3173 27.29 14.1039 27.5094 12.994L28.896 5.9284C29.2375 4.25738 29.5313 2.57456 29.8657 0.90223C30.0467 -0.00309788 30.5349 0.0375439 31.318 0.0224661Z" fill="var(--fill-0, #2E2A28)"/>
|
||||
<path id="Vector_3" d="M0.941515 0.37534C3.25959 0.264915 5.62425 0.356276 7.94687 0.356276C9.60223 0.356276 11.5002 0.247168 13.1259 0.366795C13.2646 0.377311 13.366 0.393748 13.4863 0.469994C13.6176 0.552812 13.6619 0.717133 13.6716 0.862394C13.704 1.34682 13.2587 2.8691 12.9071 3.19709C12.4474 3.62564 9.05615 3.40611 8.28287 3.39953C7.47585 6.82599 6.89241 10.4187 6.11747 13.8899C5.92381 14.8114 5.75465 16.7208 5.20897 17.4136C4.89788 17.808 2.41612 17.8363 1.98082 17.3577C1.89186 16.7839 4.39226 4.90867 4.68484 3.41333C3.72145 3.39296 0.616716 3.61249 0.0536389 3.1353C-0.061151 2.88487 0.0320916 2.45895 0.11222 2.19932C0.347797 1.43621 0.126322 0.759855 0.941515 0.37534Z" fill="var(--fill-0, #2E2A28)"/>
|
||||
<path id="Vector_4" d="M25.4674 0.0148448C25.9427 0.00694851 27.0579 -0.0542471 27.4724 0.147108C27.895 0.35307 27.4717 1.69149 27.391 2.14421C27.2885 2.71866 27.1388 3.37932 27.0188 3.98339L25.756 10.4031C25.3468 12.5107 25.0186 14.6447 24.5911 16.7504C24.4225 17.5808 24.2599 17.6907 23.4616 17.692C22.8596 17.6828 21.6356 17.8295 21.2147 17.4591C21.1268 17.3821 21.125 17.3492 21.136 17.209C21.2783 15.3817 21.8208 13.3984 22.1653 11.6119L23.56 4.36044C23.797 3.14967 24.0258 1.93562 24.2946 0.731434C24.459 -0.00489494 24.8585 0.0306374 25.4674 0.0148448Z" fill="var(--fill-0, #2E2A28)"/>
|
||||
<g id="Vector_5">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M71.6495 5.11303C74.6824 4.86166 77.5554 6.28123 77.9503 9.53783C78.1755 11.4579 77.6281 13.3892 76.4288 14.906C75.0142 16.6748 73.1939 17.5443 70.9747 17.7839C67.818 18.0877 64.8171 16.6472 64.4884 13.2195C64.3149 11.2569 64.9282 9.30557 66.1935 7.79467C67.6121 6.10827 69.4964 5.30877 71.6495 5.11303ZM71.3595 7.84935C69.1568 8.28547 67.9375 10.1587 67.9572 12.3425C67.975 14.3657 69.1902 15.2459 71.1368 15.0896C72.2798 14.8336 73.2163 14.2735 73.795 13.2497C75.1541 10.8445 74.911 7.55083 71.3595 7.84935Z" fill="var(--fill-0, #F28A4B)"/>
|
||||
<path d="M64.0954 4.9784C64.4103 4.94111 65.2562 5.06895 65.2588 5.37122C65.2661 6.07163 64.7563 7.87458 64.4288 8.43917C63.9897 8.46314 63.4642 8.42253 63.0303 8.47379C60.3232 8.79071 59.8827 10.8613 59.4615 13.0438C59.2766 14.0012 59.1082 14.9626 58.957 15.9266C58.7602 17.1524 58.8349 17.8707 57.3221 17.6837C56.7807 17.6164 55.7896 17.8554 55.3974 17.4839C55.2865 17.2476 55.3069 17.2409 55.3452 16.9886C55.7044 15.5025 57.3981 5.79532 57.7177 5.49439C57.8893 5.3326 58.2492 5.282 58.471 5.27135C58.9484 5.24871 60.3179 5.1788 60.6474 5.47707C60.8355 5.84792 60.5846 6.93582 60.5212 7.42784C61.4258 6.0683 62.4315 5.14485 64.0954 4.9784Z" fill="var(--fill-0, #F28A4B)"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M48.9446 0.1562C50.955 0.158164 53.5275 0.122138 55.0666 1.63374C57.201 3.7307 56.339 7.94429 54.3235 9.84468C52.1625 11.8833 49.6855 11.9808 46.8987 11.9619L46.1184 11.957C45.9559 12.7548 45.3084 17.0471 44.9719 17.3691C44.6001 17.7241 43.1814 17.6145 42.6409 17.6191C42.2972 17.6217 41.9482 17.6038 41.6916 17.3525C41.5596 16.8275 43.0455 9.96145 43.2288 8.98823L44.2737 3.62983C44.4556 2.70432 44.6085 1.7591 44.8166 0.838817C44.905 0.448118 45.0992 0.373253 45.4329 0.183544C45.7078 0.175033 45.9841 0.168595 46.259 0.164012C47.1544 0.15616 48.0492 0.153582 48.9446 0.1562ZM47.8303 3.18061C47.6215 4.33916 47.4035 5.49572 47.175 6.65034C47.0402 7.34484 46.8186 8.33409 46.7727 9.01557C48.4072 9.0241 49.6081 9.14689 51.0706 8.34956C53.0184 7.11729 53.6271 3.65427 50.7307 3.25483C49.8037 3.12652 48.7737 3.17538 47.8303 3.18061Z" fill="var(--fill-0, #F28A4B)"/>
|
||||
</g>
|
||||
<path id="Subtract" fill-rule="evenodd" clip-rule="evenodd" d="M14.8502 5.24274C19.7083 4.68825 21.102 7.28866 19.9723 11.6041C19.8429 12.0983 19.3743 14.3233 19.3122 14.7652C18.9984 16.9987 20.4166 17.7218 17.1188 17.6675C16.048 17.64 15.8944 17.0865 15.943 16.1431C14.502 17.603 12.8943 18.2777 10.8043 17.7808C10.0268 17.5955 9.52384 17.1424 9.01625 16.5611C7.80468 14.6731 8.71157 12.0689 10.6413 11.064C12.5185 10.0867 14.8779 10.1417 16.9508 10.0972C17.405 7.84867 16.1659 7.37086 14.1364 7.89899C13.2123 8.13925 12.8419 8.64924 12.0778 9.13434L12.0084 9.17829C11.9265 9.13578 11.8462 9.09028 11.7672 9.04255C9.15458 7.48582 11.4606 6.12449 13.2497 5.55622C13.7891 5.38473 14.2902 5.31212 14.8502 5.24274ZM16.4821 12.2779C15.8553 12.2759 15.1975 12.2591 14.5758 12.314C14.1695 12.354 13.8972 12.3897 13.4528 12.4781C12.2772 12.7118 11.0575 14.261 12.2916 15.1998C12.6616 15.4819 13.2647 15.5124 13.7487 15.483C15.5022 15.0856 16.2509 13.9879 16.4821 12.2779Z" fill="var(--fill-0, #2E2A28)"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 6.1 KiB |
|
Before Width: | Height: | Size: 2.3 MiB |
BIN
public/assets/why-illustration.mp4
Normal file
310
public/site-links-client.js
Normal file
@@ -0,0 +1,310 @@
|
||||
/**
|
||||
* Mirrors talkpro static site: /api/site-links, APK default, in-app + App Store modals.
|
||||
* Strings come from #site-links-i18n (JSON) rendered by DownloadCTA.astro.
|
||||
*/
|
||||
(function () {
|
||||
const DEFAULT_APK = 'https://talkspro.xyz/download';
|
||||
let apkDownloadUrl = DEFAULT_APK;
|
||||
|
||||
function getApkDownloadUrl() {
|
||||
return apkDownloadUrl;
|
||||
}
|
||||
|
||||
function getI18n() {
|
||||
const el = document.getElementById('site-links-i18n');
|
||||
if (!el || !el.textContent.trim()) return {};
|
||||
try {
|
||||
return JSON.parse(el.textContent);
|
||||
} catch {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
function detectInAppBrowser() {
|
||||
const ua = navigator.userAgent || '';
|
||||
if (/MicroMessenger/i.test(ua)) return 'wechat';
|
||||
try {
|
||||
if (typeof window.WeixinJSBridge !== 'undefined') return 'wechat';
|
||||
} catch {
|
||||
/* ignore */
|
||||
}
|
||||
if (/QQ\//i.test(ua) && /MQQBrowser/i.test(ua)) return 'qq';
|
||||
if (/Weibo/i.test(ua)) return 'weibo';
|
||||
return '';
|
||||
}
|
||||
|
||||
function isIOSDevice() {
|
||||
const ua = navigator.userAgent || '';
|
||||
if (/iPhone|iPad|iPod/i.test(ua)) return true;
|
||||
if (navigator.platform === 'MacIntel' && navigator.maxTouchPoints > 1) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
function isAndroidDevice() {
|
||||
const ua = navigator.userAgent || '';
|
||||
return /Android/i.test(ua) && !isIOSDevice();
|
||||
}
|
||||
|
||||
const MODAL_WRAP =
|
||||
'position:fixed;inset:0;z-index:10050;display:flex;align-items:center;justify-content:center;padding:16px;box-sizing:border-box;';
|
||||
const MODAL_BACK = 'position:absolute;inset:0;background:rgba(0,0,0,.45);cursor:pointer;';
|
||||
const MODAL_PANEL =
|
||||
'position:relative;max-width:420px;width:100%;margin:auto;padding:20px 22px;border-radius:12px;background:#fff;color:#1a1a1a;font:15px/1.45 system-ui,-apple-system,sans-serif;box-shadow:0 12px 40px rgba(0,0,0,.18);';
|
||||
const MODAL_TITLE = 'margin:0 0 10px;font-size:18px;font-weight:700;line-height:1.25;';
|
||||
const MODAL_BODY = 'margin:0 0 18px;font-size:14px;color:#444;';
|
||||
const MODAL_ACTIONS = 'display:flex;flex-wrap:wrap;gap:10px;justify-content:flex-end;';
|
||||
const BTN =
|
||||
'cursor:pointer;border-radius:8px;padding:10px 14px;font-size:14px;font-weight:600;border:1px solid #ccc;background:#f5f5f5;color:#1a1a1a;';
|
||||
const BTN_PRIMARY = 'border-color:#1a6cff;background:#1a6cff;color:#fff;';
|
||||
const BTN_ORANGE = 'cursor:pointer;border-radius:14px;padding:10px 14px;font-size:14px;font-weight:600;border:1px solid #f28a4b;background:#f28a4b;color:#fff;';
|
||||
|
||||
function initInAppBrowserModal() {
|
||||
if (document.getElementById('inapp-browser-modal')) return;
|
||||
const wrap = document.createElement('div');
|
||||
wrap.id = 'inapp-browser-modal';
|
||||
wrap.setAttribute('role', 'dialog');
|
||||
wrap.setAttribute('aria-modal', 'true');
|
||||
wrap.setAttribute('aria-hidden', 'true');
|
||||
wrap.style.cssText = MODAL_WRAP + 'display:none;';
|
||||
wrap.innerHTML =
|
||||
'<div data-inapp-close style="' +
|
||||
MODAL_BACK +
|
||||
'"></div>' +
|
||||
'<div style="' +
|
||||
MODAL_PANEL +
|
||||
'">' +
|
||||
'<h3 id="inapp-modal-title" style="' +
|
||||
MODAL_TITLE +
|
||||
'"></h3>' +
|
||||
'<p id="inapp-modal-body" style="' +
|
||||
MODAL_BODY +
|
||||
'"></p>' +
|
||||
'<div style="' +
|
||||
MODAL_ACTIONS +
|
||||
'">' +
|
||||
'<button type="button" id="inapp-copy-apk" style="' +
|
||||
BTN +
|
||||
BTN_PRIMARY +
|
||||
'"></button>' +
|
||||
'<button type="button" id="inapp-try-chrome" style="' +
|
||||
BTN +
|
||||
'" hidden></button>' +
|
||||
'<button type="button" id="inapp-got-it" data-inapp-close style="' +
|
||||
BTN +
|
||||
'"></button>' +
|
||||
'</div></div>';
|
||||
document.body.appendChild(wrap);
|
||||
|
||||
const close = () => {
|
||||
wrap.style.display = 'none';
|
||||
wrap.setAttribute('aria-hidden', 'true');
|
||||
};
|
||||
wrap.querySelectorAll('[data-inapp-close]').forEach((el) => {
|
||||
el.addEventListener('click', close);
|
||||
});
|
||||
|
||||
const i18n = () => getI18n();
|
||||
document.getElementById('inapp-copy-apk').addEventListener('click', async () => {
|
||||
const btn = document.getElementById('inapp-copy-apk');
|
||||
const dict = i18n();
|
||||
const done = dict.inapp_browser_copied || 'Copied';
|
||||
try {
|
||||
await navigator.clipboard.writeText(getApkDownloadUrl());
|
||||
const prev = btn.textContent;
|
||||
btn.textContent = done;
|
||||
setTimeout(() => {
|
||||
btn.textContent = dict.inapp_browser_copy || prev;
|
||||
}, 1600);
|
||||
} catch {
|
||||
window.prompt(dict.inapp_browser_copy || 'Copy', getApkDownloadUrl());
|
||||
}
|
||||
});
|
||||
|
||||
document.getElementById('inapp-try-chrome').addEventListener('click', () => {
|
||||
const enc = encodeURIComponent(getApkDownloadUrl());
|
||||
const intent =
|
||||
getApkDownloadUrl().replace(/^https:/i, 'intent:') +
|
||||
'#Intent;' +
|
||||
'scheme=https;action=android.intent.action.VIEW;category=android.intent.category.BROWSABLE;' +
|
||||
'package=com.android.chrome;S.browser_fallback_url=' +
|
||||
enc +
|
||||
';end';
|
||||
window.location.href = intent;
|
||||
});
|
||||
}
|
||||
|
||||
function openInAppBrowserModal() {
|
||||
const modal = document.getElementById('inapp-browser-modal');
|
||||
if (!modal) return;
|
||||
const dict = getI18n();
|
||||
const tryChrome = modal.querySelector('#inapp-try-chrome');
|
||||
const showChrome = isAndroidDevice() && !!detectInAppBrowser();
|
||||
if (tryChrome) {
|
||||
tryChrome.hidden = !showChrome;
|
||||
tryChrome.setAttribute('aria-hidden', showChrome ? 'false' : 'true');
|
||||
}
|
||||
modal.style.display = 'flex';
|
||||
modal.setAttribute('aria-hidden', 'false');
|
||||
const titleEl = modal.querySelector('#inapp-modal-title');
|
||||
const bodyEl = modal.querySelector('#inapp-modal-body');
|
||||
if (titleEl) titleEl.textContent = dict.inapp_browser_title || '';
|
||||
if (bodyEl) {
|
||||
const iosWechat = isIOSDevice() && detectInAppBrowser() === 'wechat';
|
||||
const bodyKey = iosWechat ? 'inapp_browser_body_ios' : 'inapp_browser_body';
|
||||
bodyEl.innerHTML =
|
||||
dict[bodyKey] || dict.inapp_browser_body || '';
|
||||
}
|
||||
const copyBtn = modal.querySelector('#inapp-copy-apk');
|
||||
const gotBtn = modal.querySelector('#inapp-got-it');
|
||||
if (copyBtn) copyBtn.textContent = dict.inapp_browser_copy || '';
|
||||
if (tryChrome) tryChrome.textContent = dict.inapp_browser_try_chrome || '';
|
||||
if (gotBtn) gotBtn.textContent = dict.inapp_browser_got_it || 'OK';
|
||||
}
|
||||
|
||||
function initAppStoreSoonModal() {
|
||||
if (document.getElementById('app-store-soon-modal')) return;
|
||||
const wrap = document.createElement('div');
|
||||
wrap.id = 'app-store-soon-modal';
|
||||
wrap.setAttribute('role', 'dialog');
|
||||
wrap.setAttribute('aria-modal', 'true');
|
||||
wrap.setAttribute('aria-hidden', 'true');
|
||||
wrap.style.cssText = MODAL_WRAP + 'display:none;';
|
||||
wrap.innerHTML =
|
||||
'<div data-app-soon-close style="' +
|
||||
MODAL_BACK +
|
||||
'"></div>' +
|
||||
'<div style="' +
|
||||
MODAL_PANEL +
|
||||
'">' +
|
||||
'<h3 id="app-soon-title" style="' +
|
||||
MODAL_TITLE +
|
||||
'"></h3>' +
|
||||
'<p id="app-soon-body" style="' +
|
||||
MODAL_BODY +
|
||||
'"></p>' +
|
||||
'<div style="' +
|
||||
MODAL_ACTIONS +
|
||||
'">' +
|
||||
'<button type="button" data-app-soon-close style="' +
|
||||
BTN_ORANGE +
|
||||
'"></button>' +
|
||||
'</div></div>';
|
||||
document.body.appendChild(wrap);
|
||||
const close = () => {
|
||||
wrap.style.display = 'none';
|
||||
wrap.setAttribute('aria-hidden', 'true');
|
||||
};
|
||||
wrap.querySelectorAll('[data-app-soon-close]').forEach((el) => {
|
||||
el.addEventListener('click', close);
|
||||
});
|
||||
}
|
||||
|
||||
function openAppStoreSoonModal() {
|
||||
const modal = document.getElementById('app-store-soon-modal');
|
||||
if (!modal) return;
|
||||
const dict = getI18n();
|
||||
const tEl = modal.querySelector('#app-soon-title');
|
||||
const bEl = modal.querySelector('#app-soon-body');
|
||||
const ok = modal.querySelector('button[data-app-soon-close]');
|
||||
if (tEl) tEl.textContent = dict.app_store_soon_title || '';
|
||||
if (bEl) bEl.innerHTML = dict.app_store_soon_body || '';
|
||||
if (ok) ok.textContent = dict.app_store_soon_ok || 'OK';
|
||||
modal.style.display = 'flex';
|
||||
modal.setAttribute('aria-hidden', 'false');
|
||||
}
|
||||
|
||||
function bindBrowserDownloadLinks() {
|
||||
document.querySelectorAll('a.store-badge--browser').forEach((a) => {
|
||||
const openForInApp = (e) => {
|
||||
if (!detectInAppBrowser()) return;
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
openInAppBrowserModal();
|
||||
};
|
||||
a.addEventListener('click', openForInApp);
|
||||
a.addEventListener(
|
||||
'touchend',
|
||||
(e) => {
|
||||
if (!detectInAppBrowser()) return;
|
||||
e.preventDefault();
|
||||
openInAppBrowserModal();
|
||||
},
|
||||
{ passive: false }
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
function bindAppStoreAppleBadges() {
|
||||
document.querySelectorAll('a.store-badge--apple[data-app-soon]').forEach((a) => {
|
||||
const stopAndOpen = (e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
openAppStoreSoonModal();
|
||||
};
|
||||
a.addEventListener('click', stopAndOpen);
|
||||
a.addEventListener(
|
||||
'touchend',
|
||||
(e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
openAppStoreSoonModal();
|
||||
},
|
||||
{ passive: false }
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
function loadSiteLinksThenBind() {
|
||||
if (window.location.protocol === 'file:') {
|
||||
bindBrowserDownloadLinks();
|
||||
bindAppStoreAppleBadges();
|
||||
return;
|
||||
}
|
||||
fetch('/api/site-links', { credentials: 'same-origin' })
|
||||
.then((r) => {
|
||||
if (!r.ok) throw new Error('bad status');
|
||||
return r.json();
|
||||
})
|
||||
.then((j) => {
|
||||
const apk = String(j.apk_download_url || '').trim();
|
||||
const ios = String(j.app_store_url || '').trim();
|
||||
if (apk.startsWith('https://')) apkDownloadUrl = apk;
|
||||
document.querySelectorAll('a.store-badge--browser').forEach((a) => {
|
||||
if (apk.startsWith('https://')) a.setAttribute('href', apk);
|
||||
});
|
||||
document.querySelectorAll('a.store-badge--apple').forEach((a) => {
|
||||
if (ios.startsWith('https://')) {
|
||||
a.setAttribute('href', ios);
|
||||
a.removeAttribute('data-app-soon');
|
||||
a.setAttribute('rel', 'noopener noreferrer');
|
||||
a.setAttribute('target', '_blank');
|
||||
}
|
||||
});
|
||||
const meta = document.getElementById('site-links-meta');
|
||||
if (meta) {
|
||||
const dict = getI18n();
|
||||
const u = String(j.updated_at || '').trim();
|
||||
if (u) {
|
||||
meta.textContent =
|
||||
(dict.linksMetaPrefix || 'Store links file updated: ') +
|
||||
u +
|
||||
(dict.linksMetaSuffix || ' (GMT+8)');
|
||||
meta.hidden = false;
|
||||
} else {
|
||||
meta.hidden = true;
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
/* keep defaults */
|
||||
})
|
||||
.finally(() => {
|
||||
bindBrowserDownloadLinks();
|
||||
bindAppStoreAppleBadges();
|
||||
});
|
||||
}
|
||||
|
||||
initInAppBrowserModal();
|
||||
initAppStoreSoonModal();
|
||||
loadSiteLinksThenBind();
|
||||
})();
|
||||
71
scripts/deploy-talkpro.sh
Executable file
@@ -0,0 +1,71 @@
|
||||
#!/usr/bin/env bash
|
||||
# Build Astro dist/ and sync to the Talk Pro marketing host (talkpro.info).
|
||||
# Used by Gitea Actions and for manual deploys from this repo.
|
||||
#
|
||||
# Required (CI): TALKPRO_SSH_PRIVATE_KEY — PEM contents (Gitea secret)
|
||||
# Optional: TALKPRO_SSH_KEY_FILE — path to PEM (local)
|
||||
# Defaults match .env.deploy.example / Gitea workflow job env:
|
||||
# TALKPRO_HOST=13.214.179.69 TALKPRO_USER=ubuntu TALKPRO_REMOTE_ROOT=/home/ubuntu/talkpro
|
||||
set -euo pipefail
|
||||
|
||||
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
|
||||
cd "$ROOT"
|
||||
|
||||
HOST="${TALKPRO_HOST:-13.214.179.69}"
|
||||
USER="${TALKPRO_USER:-ubuntu}"
|
||||
REMOTE_ROOT="${TALKPRO_REMOTE_ROOT:-/home/ubuntu/talkpro}"
|
||||
[[ -n "$HOST" ]] || HOST=13.214.179.69
|
||||
[[ -n "$USER" ]] || USER=ubuntu
|
||||
[[ -n "$REMOTE_ROOT" ]] || REMOTE_ROOT=/home/ubuntu/talkpro
|
||||
|
||||
KEY_FILE="${TALKPRO_SSH_KEY_FILE:-}"
|
||||
TMP_KEY=""
|
||||
cleanup() {
|
||||
[[ -n "$TMP_KEY" && -f "$TMP_KEY" ]] && rm -f "$TMP_KEY"
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
if [[ -n "${TALKPRO_SSH_PRIVATE_KEY:-}" ]]; then
|
||||
TMP_KEY="$(mktemp)"
|
||||
chmod 600 "$TMP_KEY"
|
||||
printf '%s\n' "$TALKPRO_SSH_PRIVATE_KEY" | tr -d '\r' > "$TMP_KEY"
|
||||
KEY_FILE="$TMP_KEY"
|
||||
elif [[ -z "$KEY_FILE" || ! -f "$KEY_FILE" ]]; then
|
||||
echo "ERROR: Set TALKPRO_SSH_PRIVATE_KEY (Gitea secret / env) or TALKPRO_SSH_KEY_FILE (local path)" >&2
|
||||
exit 1
|
||||
fi
|
||||
if [[ ! -s "$KEY_FILE" ]]; then
|
||||
echo "ERROR: SSH key file is empty — check TALKPRO_SSH_PRIVATE_KEY secret in Gitea" >&2
|
||||
exit 1
|
||||
fi
|
||||
chmod 600 "$KEY_FILE"
|
||||
|
||||
SSH_OPTS=(-i "$KEY_FILE" -o BatchMode=yes -o StrictHostKeyChecking=accept-new)
|
||||
RSYNC_SSH="ssh -i \"$KEY_FILE\" -o BatchMode=yes -o StrictHostKeyChecking=accept-new"
|
||||
REMOTE="${USER}@${HOST}"
|
||||
|
||||
if [[ -f package-lock.json ]]; then
|
||||
npm ci
|
||||
else
|
||||
npm install
|
||||
fi
|
||||
npm run build
|
||||
|
||||
command -v ssh >/dev/null || { echo "ERROR: ssh not found (install openssh-client)" >&2; exit 127; }
|
||||
|
||||
ssh "${SSH_OPTS[@]}" "$REMOTE" "mkdir -p ${REMOTE_ROOT}"
|
||||
|
||||
if command -v rsync >/dev/null 2>&1; then
|
||||
echo "Uploading with rsync..."
|
||||
rsync -avz --delete \
|
||||
-e "$RSYNC_SSH" \
|
||||
dist/ \
|
||||
"${REMOTE}:${REMOTE_ROOT}/"
|
||||
else
|
||||
command -v tar >/dev/null || { echo "ERROR: need rsync or tar on the runner" >&2; exit 127; }
|
||||
echo "Uploading with tar over ssh (rsync not available on runner)..."
|
||||
ssh "${SSH_OPTS[@]}" "$REMOTE" "mkdir -p ${REMOTE_ROOT} && rm -rf ${REMOTE_ROOT:?}/*"
|
||||
tar -C dist -cf - . | ssh "${SSH_OPTS[@]}" "$REMOTE" "tar -C ${REMOTE_ROOT} -xf -"
|
||||
fi
|
||||
|
||||
echo "Deployed dist/ → ${REMOTE}:${REMOTE_ROOT}/"
|
||||
5
src/assets.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
const assetVersion = '20260518-1'
|
||||
|
||||
export function assetPath(path: string) {
|
||||
return `${path}?v=${assetVersion}`
|
||||
}
|
||||
@@ -1,91 +1,89 @@
|
||||
---
|
||||
import { assetPath } from '../assets'
|
||||
import type { Translations } from '../i18n/translations'
|
||||
|
||||
export interface Props {
|
||||
t: Translations['preview']
|
||||
}
|
||||
|
||||
const { t } = Astro.props
|
||||
const slides = [
|
||||
"/assets/preview-phone.png",
|
||||
"/assets/preview-phone.png",
|
||||
"/assets/preview-phone.png",
|
||||
assetPath("/assets/preview-phone.png"),
|
||||
assetPath("/assets/preview-phone.png"),
|
||||
assetPath("/assets/preview-phone.png"),
|
||||
]
|
||||
const arrowLeft = assetPath("/assets/preview-arrow-left.svg")
|
||||
const arrowRight = assetPath("/assets/preview-arrow-right.svg")
|
||||
---
|
||||
|
||||
<section class="bg-[#fef0eb] w-full flex flex-col gap-[60px] items-center pt-[120px] px-[130px] overflow-hidden">
|
||||
<!-- Heading -->
|
||||
<div class="flex flex-col gap-[40px] items-start overflow-clip w-[940px]">
|
||||
<p class="font-bold text-[#1a1a1a] text-[48px] text-center tracking-[-1.16px] leading-[1.2] w-full">
|
||||
A Familiar App Experience, Reimagined with a Modern Look
|
||||
<section class="app-preview">
|
||||
<div class="app-preview__header">
|
||||
<p class="app-preview__title">
|
||||
{t.title}
|
||||
</p>
|
||||
<p class="font-normal text-[#7a726d] text-[18px] text-center tracking-[-0.33px] leading-[1.5] w-full">
|
||||
TalkPro keeps the communication experience familiar while refining the visual layer with updated icons, colors, spacing, and interface details.
|
||||
<p class="app-preview__description">
|
||||
{t.description}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Carousel -->
|
||||
<div class="flex gap-[20px] items-end justify-center shrink-0" id="carousel">
|
||||
|
||||
<!-- Left phone -->
|
||||
<div class="relative h-[396px] w-[336px] opacity-20 shrink-0 overflow-hidden pointer-events-none transition-opacity duration-300">
|
||||
<img id="phone-left" alt="" class="absolute h-[175.34%] left-0 max-w-none top-[-0.03%] w-full transition-opacity duration-300" src={slides[slides.length - 1]} />
|
||||
<div class="app-preview__carousel" id="carousel">
|
||||
<div class="app-preview__side-phone">
|
||||
<img id="phone-left" alt="" class="app-preview__phone-image" src={slides[slides.length - 1]} />
|
||||
</div>
|
||||
|
||||
<!-- Prev button -->
|
||||
<div class="flex items-end self-stretch shrink-0">
|
||||
<div class="flex items-center justify-center h-full">
|
||||
<button id="btn-prev" class="bg-[#f08458] flex items-center justify-center rounded-[9999px] size-[48px] hover:bg-[#e07a3b] active:scale-95 transition-all shrink-0 cursor-pointer">
|
||||
<img alt="Previous" class="block size-[24px]" src="/assets/preview-arrow-left.svg" />
|
||||
<div class="app-preview__control-wrap">
|
||||
<div class="app-preview__control-inner">
|
||||
<button id="btn-prev" class="app-preview__button">
|
||||
<img alt={t.previous} class="app-preview__button-icon" src={arrowLeft} />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Center phone -->
|
||||
<div class="relative h-[542px] w-[459px] shrink-0 overflow-hidden pointer-events-none">
|
||||
<img id="phone-center" alt="TalkPro app preview" class="absolute h-[175.34%] left-0 max-w-none top-[-0.03%] w-full transition-opacity duration-300" src={slides[0]} />
|
||||
<div class="app-preview__center-phone">
|
||||
<img id="phone-center" alt={t.phoneAlt} class="app-preview__phone-image" src={slides[0]} />
|
||||
</div>
|
||||
|
||||
<!-- Next button -->
|
||||
<div class="flex items-end self-stretch shrink-0">
|
||||
<div class="flex items-center justify-center h-full">
|
||||
<button id="btn-next" class="bg-[#f08458] flex items-center justify-center rounded-[9999px] size-[48px] hover:bg-[#e07a3b] active:scale-95 transition-all shrink-0 cursor-pointer">
|
||||
<img alt="Next" class="block size-[24px] rotate-180" src="/assets/preview-arrow-right.svg" />
|
||||
<div class="app-preview__control-wrap">
|
||||
<div class="app-preview__control-inner">
|
||||
<button id="btn-next" class="app-preview__button">
|
||||
<img alt={t.next} class="app-preview__button-icon app-preview__button-icon--next" src={arrowRight} />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Right phone -->
|
||||
<div class="relative h-[396px] w-[336px] opacity-20 shrink-0 overflow-hidden pointer-events-none transition-opacity duration-300">
|
||||
<img id="phone-right" alt="" class="absolute h-[175.34%] left-0 max-w-none top-[-0.03%] w-full transition-opacity duration-300" src={slides[1]} />
|
||||
<div class="app-preview__side-phone">
|
||||
<img id="phone-right" alt="" class="app-preview__phone-image" src={slides[1]} />
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<script>
|
||||
const slides: string[] = (window as any).__carouselSlides || [];
|
||||
|
||||
// Read slides injected via data attribute
|
||||
const carousel = document.getElementById('carousel')!;
|
||||
const allSlides: string[] = JSON.parse(carousel.dataset.slides || '[]');
|
||||
|
||||
const leftImg = document.getElementById('phone-left') as HTMLImageElement;
|
||||
const leftImg = document.getElementById('phone-left') as HTMLImageElement;
|
||||
const centerImg = document.getElementById('phone-center') as HTMLImageElement;
|
||||
const rightImg = document.getElementById('phone-right') as HTMLImageElement;
|
||||
const btnPrev = document.getElementById('btn-prev')!;
|
||||
const btnNext = document.getElementById('btn-next')!;
|
||||
const rightImg = document.getElementById('phone-right') as HTMLImageElement;
|
||||
const btnPrev = document.getElementById('btn-prev')!;
|
||||
const btnNext = document.getElementById('btn-next')!;
|
||||
|
||||
let current = 0;
|
||||
|
||||
function mod(n: number, m: number) { return ((n % m) + m) % m; }
|
||||
|
||||
function transition(next: number) {
|
||||
[leftImg, centerImg, rightImg].forEach(img => img.style.opacity = '0');
|
||||
[leftImg, centerImg, rightImg].forEach(img => img && (img.style.opacity = '0'));
|
||||
setTimeout(() => {
|
||||
current = mod(next, allSlides.length);
|
||||
leftImg.src = allSlides[mod(current - 1, allSlides.length)];
|
||||
centerImg.src = allSlides[current];
|
||||
rightImg.src = allSlides[mod(current + 1, allSlides.length)];
|
||||
[leftImg, centerImg, rightImg].forEach(img => img.style.opacity = '1');
|
||||
if (leftImg) leftImg.src = allSlides[mod(current - 1, allSlides.length)];
|
||||
if (centerImg) centerImg.src = allSlides[current];
|
||||
if (rightImg) rightImg.src = allSlides[mod(current + 1, allSlides.length)];
|
||||
[leftImg, centerImg, rightImg].forEach(img => img && (img.style.opacity = '1'));
|
||||
}, 200);
|
||||
}
|
||||
|
||||
btnPrev.addEventListener('click', () => transition(current - 1));
|
||||
btnNext.addEventListener('click', () => transition(current + 1));
|
||||
if (btnPrev) btnPrev.addEventListener('click', () => transition(current - 1));
|
||||
if (btnNext) btnNext.addEventListener('click', () => transition(current + 1));
|
||||
</script>
|
||||
|
||||
<script define:vars={{ slides }}>
|
||||
|
||||
@@ -1,66 +1,47 @@
|
||||
---
|
||||
const halftone = "/assets/core-halftone-bg.png";
|
||||
const iconPrivate = "/assets/core-icon-private.png";
|
||||
const iconGroups = "/assets/core-icon-groups.png";
|
||||
const iconChannels = "/assets/core-icon-channels.png";
|
||||
const iconVoice = "/assets/core-icon-voice.png";
|
||||
const iconVideo = "/assets/core-icon-video.png";
|
||||
const iconMedia = "/assets/core-icon-media.png";
|
||||
import { assetPath } from '../assets'
|
||||
import type { Translations } from '../i18n/translations'
|
||||
|
||||
const cards = [
|
||||
{ img: iconPrivate, title: 'Private Messaging', desc: 'Stay connected through fast and familiar one-on-one conversations.' },
|
||||
{ img: iconGroups, title: 'Group Chats', desc: 'Create spaces for friends, teams, communities, and shared discussions.' },
|
||||
{ img: iconChannels, title: 'Channels', desc: 'Follow updates, announcements, and content from the people or communities you care about.' },
|
||||
{ img: iconVoice, title: 'Voice Calls', desc: 'Talk in real time whenever messages are not enough.' },
|
||||
{ img: iconVideo, title: 'Video Calls', desc: 'Connect face-to-face with a simple and reliable video call experience.' },
|
||||
{ img: iconMedia, title: 'Media Sharing', desc: 'Share photos, videos, files, and updates in your conversations.' },
|
||||
export interface Props {
|
||||
t: Translations['core']
|
||||
}
|
||||
|
||||
const { t } = Astro.props
|
||||
const halftone = assetPath("/assets/core-halftone-bg.png");
|
||||
const icons = [
|
||||
assetPath("/assets/core-icon-private.png"),
|
||||
assetPath("/assets/core-icon-groups.png"),
|
||||
assetPath("/assets/core-icon-channels.png"),
|
||||
assetPath("/assets/core-icon-voice.png"),
|
||||
assetPath("/assets/core-icon-video.png"),
|
||||
assetPath("/assets/core-icon-media.png"),
|
||||
]
|
||||
---
|
||||
|
||||
<section id="features" class="relative w-full flex flex-col gap-[60px] items-center py-[120px] overflow-hidden">
|
||||
<!-- Background halftone -->
|
||||
<img alt="" class="absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 h-[1319px] w-[2363px] object-cover pointer-events-none opacity-20" src={halftone} />
|
||||
<section id="features" class="features">
|
||||
<img alt="" class="features__bg" src={halftone} />
|
||||
|
||||
<!-- Section header -->
|
||||
<div class="relative flex flex-col gap-[24px] items-center justify-end overflow-clip px-[180px] w-[1280px]">
|
||||
<div class="bg-white border border-[#fbbfa3] flex items-center justify-center px-[24px] py-[12px] rounded-[9999px] shrink-0">
|
||||
<span class="font-bold text-[#f08458] text-[14px] text-center tracking-[-0.04px] whitespace-nowrap leading-normal">CORE FEATURES</span>
|
||||
<div class="features__header">
|
||||
<div class="section-eyebrow">
|
||||
<span class="section-eyebrow__text">{t.eyebrow}</span>
|
||||
</div>
|
||||
<p class="font-bold text-[#1a1a1a] text-[48px] text-center tracking-[-1.16px] leading-[1.2] w-full">Everything You Need to Communicate</p>
|
||||
<p class="font-normal text-[#7a726d] text-[18px] text-center tracking-[-0.33px] leading-[1.5] w-full">
|
||||
Different identities, different conversations, and different privacy levels should not be forced into one flat interface. TalkPro lets them exist in order inside one platform.
|
||||
<p class="features__title">{t.title}</p>
|
||||
<p class="features__description">
|
||||
{t.description}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Card grid -->
|
||||
<div class="relative flex flex-col gap-[22px] items-start w-[1280px]">
|
||||
<!-- Row 1 -->
|
||||
<div class="flex gap-[22px] items-start w-full">
|
||||
{cards.slice(0, 3).map(card => (
|
||||
<div class="bg-[rgba(255,255,255,0.78)] flex flex-1 flex-col gap-[16px] items-center min-w-0 p-[32px] rounded-[30px] self-stretch">
|
||||
<div class="relative shrink-0 size-[160px] overflow-hidden">
|
||||
<img alt={card.title} class="absolute inset-0 max-w-none size-full object-contain" src={card.img} />
|
||||
</div>
|
||||
<div class="flex flex-col gap-[12px] items-start text-center w-full">
|
||||
<p class="font-bold text-[#2e2a28] text-[24px] tracking-[-0.5px] leading-normal w-full">{card.title}</p>
|
||||
<p class="font-normal text-[#7a726d] text-[15px] tracking-[-0.13px] leading-[1.5] w-full">{card.desc}</p>
|
||||
</div>
|
||||
<div class="features__grid">
|
||||
{t.cards.map((card, index) => (
|
||||
<div class="feature-card">
|
||||
<div class="feature-card__icon-frame">
|
||||
<img alt={card.title} class="feature-card__icon" src={icons[index]} />
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<!-- Row 2 -->
|
||||
<div class="flex gap-[22px] items-center w-full">
|
||||
{cards.slice(3).map(card => (
|
||||
<div class="bg-[rgba(255,255,255,0.78)] flex flex-1 flex-col gap-[16px] items-center min-w-0 p-[32px] rounded-[30px] self-stretch">
|
||||
<div class="relative shrink-0 size-[160px] overflow-hidden">
|
||||
<img alt={card.title} class="absolute inset-0 max-w-none size-full object-contain" src={card.img} />
|
||||
</div>
|
||||
<div class="flex flex-col gap-[12px] items-start text-center w-full">
|
||||
<p class="font-bold text-[#2e2a28] text-[24px] tracking-[-0.5px] leading-normal w-full">{card.title}</p>
|
||||
<p class="font-normal text-[#7a726d] text-[15px] tracking-[-0.13px] leading-[1.5] w-full">{card.desc}</p>
|
||||
</div>
|
||||
<div class="feature-card__copy">
|
||||
<p class="feature-card__title">{card.title}</p>
|
||||
<p class="feature-card__description">{card.desc}</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@@ -1,60 +1,81 @@
|
||||
---
|
||||
const bgPattern = "/assets/cta-bg-pattern.png";
|
||||
const talkproLogo = "/assets/cta-talkpro-logo.png";
|
||||
const androidIcon = "/assets/cta-android-icon.svg";
|
||||
const appleIcon = "/assets/cta-apple-icon.svg";
|
||||
const phoneArt = "/assets/cta-phone-art.png";
|
||||
import { assetPath } from '../assets'
|
||||
import type { Translations } from '../i18n/translations'
|
||||
|
||||
export interface Props {
|
||||
t: Translations['download']
|
||||
siteLinks: Translations['siteLinks']
|
||||
}
|
||||
|
||||
const { t, siteLinks } = Astro.props
|
||||
const bgPattern = assetPath("/assets/cta-bg-pattern.svg");
|
||||
const talkproLogo = assetPath("/assets/cta-talkpro-logo.svg");
|
||||
const androidIcon = assetPath("/assets/cta-android-icon.svg");
|
||||
const appleIcon = assetPath("/assets/cta-apple-icon.svg");
|
||||
const phoneArt = assetPath("/assets/cta-phone-art.png");
|
||||
const defaultApkHref = "https://talkspro.xyz/download";
|
||||
const siteLinksJson = JSON.stringify(siteLinks);
|
||||
---
|
||||
|
||||
<section id="download" class="relative bg-white border-t border-[#eec8b8] w-full flex items-center justify-center overflow-hidden h-[600px]">
|
||||
<!-- Background pattern -->
|
||||
<img alt="" class="absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 h-[923px] w-full max-w-[1920px] pointer-events-none" src={bgPattern} />
|
||||
<section id="download" class="download-cta">
|
||||
<img alt="" class="download-cta__pattern" src={bgPattern} />
|
||||
|
||||
<div class="relative flex items-center w-[1280px]">
|
||||
<!-- Left: download panel -->
|
||||
<div class="flex flex-1 flex-col gap-[36px] items-start justify-center min-w-0 overflow-clip">
|
||||
<div class="bg-white border border-[#fbbfa3] flex items-center justify-center px-[24px] py-[12px] rounded-[9999px] shrink-0">
|
||||
<span class="font-bold text-[#f08458] text-[14px] text-center tracking-[-0.04px] whitespace-nowrap leading-normal">DOWNLOAD</span>
|
||||
<div class="download-cta__inner">
|
||||
<div class="download-cta__content">
|
||||
<div class="section-eyebrow">
|
||||
<span class="section-eyebrow__text">{t.eyebrow}</span>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col gap-[16px] items-start w-full">
|
||||
<div class="flex gap-[20px] items-start shrink-0">
|
||||
<p class="font-bold text-[#1a1a1a] text-[48px] tracking-[-1.16px] leading-[1.2] whitespace-nowrap">Download</p>
|
||||
<div class="relative shrink-0 h-[72px] w-[188px]">
|
||||
<img alt="TalkPro" class="absolute inset-0 block max-w-none size-full" src={talkproLogo} />
|
||||
<div class="download-cta__copy">
|
||||
<div class="download-cta__heading">
|
||||
<p class="download-cta__title">{t.title}</p>
|
||||
<div class="download-cta__logo-frame">
|
||||
<img alt={t.logoAlt} class="download-cta__logo" src={talkproLogo} />
|
||||
</div>
|
||||
</div>
|
||||
<p class="font-normal text-[#7a726d] text-[18px] tracking-[-0.33px] leading-[1.5] max-w-[542px]">
|
||||
Download TalkPro and experience a cleaner, simpler, and more modern way to stay connected.
|
||||
<p class="download-cta__description">
|
||||
{t.description}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Store badges -->
|
||||
<div class="flex gap-[16px] items-center overflow-clip shrink-0">
|
||||
<div class="bg-[#f28a4b] border border-[#c5834e] flex gap-[8px] h-[70px] items-center overflow-clip px-[16px] py-[12px] rounded-[20px] shrink-0 w-[260px]">
|
||||
<img alt="Android" class="block shrink-0 size-[44px]" src={androidIcon} />
|
||||
<div class="flex flex-col gap-[3px] items-start overflow-clip shrink-0 whitespace-nowrap">
|
||||
<p class="font-semibold text-[#ffd6bc] text-[11px] tracking-[0.05px] leading-normal">ANDROID</p>
|
||||
<p class="font-semibold text-white text-[15px] tracking-[-0.13px] leading-normal">APK Coming Soon</p>
|
||||
<div class="store-badges">
|
||||
<a
|
||||
class="store-badge store-badge--android store-badge--browser"
|
||||
href={defaultApkHref}
|
||||
rel="noopener noreferrer"
|
||||
target="_blank"
|
||||
>
|
||||
<div class="store-badge__icon-frame">
|
||||
<img alt={t.androidAlt} class="store-badge__android-icon" src={androidIcon} />
|
||||
</div>
|
||||
</div>
|
||||
<div class="bg-[#383838] border border-[#141414] flex gap-[8px] h-[70px] items-center overflow-clip px-[16px] py-[12px] rounded-[20px] shrink-0 w-[260px]">
|
||||
<div class="bg-[#151515] flex items-center justify-center rounded-[12px] shrink-0 size-[44px]">
|
||||
<img alt="Apple" class="block h-[27px] w-[22px]" src={appleIcon} />
|
||||
<div class="store-badge__copy">
|
||||
<p class="store-badge__platform">{t.android}</p>
|
||||
<p class="store-badge__label">{t.androidCta}</p>
|
||||
</div>
|
||||
<div class="flex flex-col gap-[3px] items-start overflow-clip shrink-0 whitespace-nowrap">
|
||||
<p class="font-semibold text-[#ccc] text-[11px] tracking-[0.05px] leading-normal">IOS</p>
|
||||
<p class="font-semibold text-white text-[15px] tracking-[-0.13px] leading-normal">Coming on App Store</p>
|
||||
</a>
|
||||
<a
|
||||
class="store-badge store-badge--ios store-badge--apple"
|
||||
href="#"
|
||||
data-app-soon="1"
|
||||
>
|
||||
<div class="store-badge__icon-frame">
|
||||
<img alt={t.appleAlt} class="store-badge__apple-icon" src={appleIcon} />
|
||||
</div>
|
||||
</div>
|
||||
<div class="store-badge__copy">
|
||||
<p class="store-badge__platform">{t.ios}</p>
|
||||
<p class="store-badge__label">{t.iosCta}</p>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- Right: phone art -->
|
||||
<div class="relative shrink-0 h-[510px] w-[418px]">
|
||||
<div class="absolute inset-0 overflow-hidden pointer-events-none">
|
||||
<img alt="TalkPro on phone" class="absolute h-[136.1%] left-[3.8%] max-w-none top-[-18.05%] w-[92.43%]" src={phoneArt} />
|
||||
<div class="download-cta__phone">
|
||||
<div class="download-cta__phone-crop">
|
||||
<img alt={t.phoneAlt} class="download-cta__phone-image" src={phoneArt} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<script type="application/json" id="site-links-i18n" set:html={siteLinksJson} />
|
||||
|
||||
@@ -1,65 +1,52 @@
|
||||
---
|
||||
import { assetPath } from '../assets'
|
||||
import type { Translations } from '../i18n/translations'
|
||||
|
||||
export interface Props {
|
||||
t: Translations['experience']
|
||||
}
|
||||
|
||||
const { t } = Astro.props
|
||||
const images = [
|
||||
assetPath("/assets/exp-card-1.png"),
|
||||
assetPath("/assets/exp-card-2.png"),
|
||||
assetPath("/assets/exp-card-3.png"),
|
||||
]
|
||||
const imageClasses = ['experience-card__image--one', 'experience-card__image--two', 'experience-card__image--three']
|
||||
---
|
||||
|
||||
<section id="experience" class="bg-white w-full flex flex-col items-center justify-center py-[120px]">
|
||||
<div class="flex flex-col gap-[40px] items-center justify-center w-[1008px]">
|
||||
<p class="font-bold text-[#1a1a1a] text-[48px] text-center tracking-[-1.16px] leading-[1.2] w-full">
|
||||
A Cleaner, More Comfortable Messaging Experience
|
||||
</p>
|
||||
|
||||
<div class="flex flex-col items-start w-full">
|
||||
<!-- 3 cards row -->
|
||||
<div class="flex gap-[24px] h-[477px] items-center w-full shrink-0">
|
||||
|
||||
<!-- Card 1: Clear Interface -->
|
||||
<div class="bg-gradient-to-b from-[#fef0eb] to-white flex flex-col gap-[36px] h-full items-center overflow-clip pb-[36px] px-[36px] rounded-tl-[30px] rounded-tr-[30px] shrink-0 w-[320px]">
|
||||
<div class="relative h-[232px] shrink-0 w-[320px]">
|
||||
<div class="absolute inset-0 overflow-hidden pointer-events-none">
|
||||
<img alt="Clear interface screenshot" class="absolute h-[298.5%] left-0 max-w-none top-[-58.58%] w-full" src="/assets/exp-card-1.png" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex flex-col gap-[16px] items-start overflow-clip text-center w-full">
|
||||
<p class="font-semibold text-[#0d0d0d] text-[24px] tracking-[-0.47px] leading-[1.4] w-full">Clear Interface</p>
|
||||
<p class="font-medium text-[#7a726d] text-[16px] tracking-[-0.18px] leading-[1.5] w-full">A clean layout that makes conversations easy to follow.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Card 2: Smooth Navigation -->
|
||||
<div class="bg-gradient-to-b from-[#fef0eb] to-white flex flex-col gap-[36px] h-full items-center overflow-clip pb-[36px] px-[36px] rounded-tl-[30px] rounded-tr-[30px] shrink-0 w-[320px]">
|
||||
<div class="bg-[#ffeddf] flex flex-col items-start shrink-0 w-[320px]">
|
||||
<div class="relative h-[232px] shrink-0 w-full">
|
||||
<div class="absolute inset-0 overflow-hidden pointer-events-none">
|
||||
<img alt="Smooth navigation screenshot" class="absolute h-[411.28%] left-[-5.95%] max-w-none top-[-335.6%] w-[137.79%]" src="/assets/exp-card-2.png" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex flex-col gap-[16px] items-start overflow-clip text-center w-full">
|
||||
<p class="font-semibold text-[#0d0d0d] text-[24px] tracking-[-0.47px] leading-[1.4] w-full">Smooth Navigation</p>
|
||||
<p class="font-medium text-[#7a726d] text-[16px] tracking-[-0.18px] leading-[1.5] w-full">Move between chats, groups, and channels with familiar controls.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Card 3: Refined Visual Design -->
|
||||
<div class="bg-gradient-to-b from-[#fef0eb] to-white flex flex-col gap-[36px] h-full items-center overflow-clip pb-[36px] px-[36px] rounded-tl-[30px] rounded-tr-[30px] shrink-0 w-[320px]">
|
||||
<div class="bg-[#ffeddf] flex flex-col h-[232px] items-start overflow-clip shrink-0 w-[320px]">
|
||||
<div class="flex-1 min-h-0 relative w-full">
|
||||
<div class="absolute inset-0 overflow-hidden pointer-events-none">
|
||||
<img alt="Refined visual design screenshot" class="absolute h-[298.5%] left-0 max-w-none top-[-99.23%] w-full" src="/assets/exp-card-3.png" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex flex-col gap-[16px] items-start overflow-clip text-center w-full">
|
||||
<p class="font-semibold text-[#0d0d0d] text-[24px] tracking-[-0.47px] leading-[1.4] w-full">Refined Visual Design</p>
|
||||
<p class="font-medium text-[#7a726d] text-[16px] tracking-[-0.18px] leading-[1.5] w-full">Modern icons, colors, and interface details create a more polished experience.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- Footer caption -->
|
||||
<p class="font-normal text-[#7a726d] text-[18px] text-center tracking-[-0.33px] leading-[1.5] w-full mt-0">
|
||||
TalkPro is designed with clarity in mind. Every screen is built to help users focus on their conversations, reduce distractions, and move naturally between chats, groups, channels, and calls.
|
||||
<section id="experience" class="experience">
|
||||
<div class="experience__inner">
|
||||
<div class="experience__heading">
|
||||
<p class="experience__title">
|
||||
{t.title}
|
||||
</p>
|
||||
|
||||
<p class="experience__caption">
|
||||
{t.caption}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="experience__body">
|
||||
<div class="experience__grid">
|
||||
{t.cards.map((card, index) => (
|
||||
<div class="experience-card">
|
||||
<div class={`experience-card__media ${index > 0 ? 'experience-card__media--tinted' : ''}`}>
|
||||
<div class="experience-card__media-crop">
|
||||
<img
|
||||
alt={card.alt}
|
||||
class={`experience-card__image ${imageClasses[index]}`}
|
||||
src={images[index]}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="experience-card__copy">
|
||||
<p class="experience-card__title">{card.title}</p>
|
||||
<p class="experience-card__description">{card.desc}</p>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@@ -1,65 +1,34 @@
|
||||
---
|
||||
const logoFull = "/assets/footer-logo.png";
|
||||
const androidIcon = "/assets/footer-android-icon.svg";
|
||||
const appleIcon = "/assets/footer-apple-icon.svg";
|
||||
import { assetPath } from '../assets'
|
||||
import type { Translations } from '../i18n/translations'
|
||||
|
||||
export interface Props {
|
||||
t: Translations['footer']
|
||||
}
|
||||
|
||||
const { t } = Astro.props
|
||||
const logoFull = assetPath("/assets/footer-logo.png");
|
||||
---
|
||||
|
||||
<footer class="bg-[#fef0eb] w-full flex flex-col items-center justify-center py-[120px]">
|
||||
<div class="flex flex-col gap-[36px] items-start w-[1280px]">
|
||||
<!-- Top row -->
|
||||
<div class="flex items-start justify-between w-full">
|
||||
<!-- Brand col -->
|
||||
<div class="flex flex-col gap-[24px] items-start overflow-clip shrink-0">
|
||||
<div class="relative h-[64px] w-[220px]">
|
||||
<img alt="TalkPro" class="absolute inset-0 max-w-none object-cover size-full pointer-events-none" src={logoFull} />
|
||||
<footer class="site-footer">
|
||||
<div class="site-footer__inner">
|
||||
<div class="site-footer__top">
|
||||
<div class="site-footer__brand">
|
||||
<div class="site-footer__logo-frame">
|
||||
<img alt={t.logoAlt} class="site-footer__logo" src={logoFull} />
|
||||
</div>
|
||||
<p class="font-normal text-[#7a726d] text-[14px] tracking-[-0.09px] leading-[1.5] max-w-[320px]">
|
||||
TalkPro is a modern communication app designed for messaging, group conversations, channels, voice calls, and video calls.
|
||||
<p class="site-footer__description">
|
||||
{t.description}
|
||||
</p>
|
||||
<!-- Store badges -->
|
||||
<div class="flex gap-[16px] items-center overflow-clip shrink-0">
|
||||
<div class="bg-[#f28a4b] border border-[#c5834e] flex gap-[8px] h-[70px] items-center overflow-clip px-[16px] py-[12px] rounded-[20px] shrink-0 w-[260px]">
|
||||
<img alt="Android" class="block shrink-0 size-[44px]" src={androidIcon} />
|
||||
<div class="flex flex-col gap-[3px] items-start overflow-clip shrink-0 whitespace-nowrap">
|
||||
<p class="font-semibold text-[#ffd6bc] text-[11px] tracking-[0.05px] leading-normal">ANDROID</p>
|
||||
<p class="font-semibold text-white text-[15px] tracking-[-0.13px] leading-normal">APK Coming Soon</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="bg-[#383838] border border-[#141414] flex gap-[8px] h-[70px] items-center overflow-clip px-[16px] py-[12px] rounded-[20px] shrink-0 w-[260px]">
|
||||
<div class="bg-[#151515] flex items-center justify-center rounded-[12px] shrink-0 size-[44px]">
|
||||
<img alt="Apple" class="block h-[27px] w-[22px]" src={appleIcon} />
|
||||
</div>
|
||||
<div class="flex flex-col gap-[3px] items-start overflow-clip shrink-0 whitespace-nowrap">
|
||||
<p class="font-semibold text-[#ccc] text-[11px] tracking-[0.05px] leading-normal">IOS</p>
|
||||
<p class="font-semibold text-white text-[15px] tracking-[-0.13px] leading-normal">Coming on App Store</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Link cols -->
|
||||
<div class="flex gap-[32px] items-center leading-normal shrink-0">
|
||||
<div class="flex flex-col gap-[24px] items-start overflow-clip w-[160px]">
|
||||
<a href="#download" class="font-normal text-[#4a4a4a] text-[14px] whitespace-nowrap hover:text-[#f28a4b] transition-colors">Download</a>
|
||||
<a href="#features" class="font-normal text-[#4a4a4a] text-[14px] whitespace-nowrap hover:text-[#f28a4b] transition-colors">Features</a>
|
||||
<a href="#" class="font-normal text-[#4a4a4a] text-[14px] whitespace-nowrap hover:text-[#f28a4b] transition-colors">About</a>
|
||||
</div>
|
||||
<div class="flex flex-col gap-[24px] items-start overflow-clip w-[160px]">
|
||||
<p class="font-medium text-[#4a4a4a] text-[14px] whitespace-nowrap">Contact</p>
|
||||
<p class="font-normal text-[#4a4a4a] text-[14px] whitespace-nowrap">email@hotmail.com</p>
|
||||
<p class="font-normal text-[#4a4a4a] text-[14px] whitespace-nowrap">+01 123 45562334</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Divider -->
|
||||
<div class="w-full h-px bg-[#e3d9d1]"></div>
|
||||
<div class="site-footer__divider"></div>
|
||||
|
||||
<!-- Bottom row -->
|
||||
<div class="flex gap-[20px] items-center overflow-clip w-full">
|
||||
<p class="font-normal text-[#7a726d] text-[14px] tracking-[-0.09px] whitespace-nowrap leading-normal">© 2026 TalkPro. All rights reserved.</p>
|
||||
<div class="flex-1 min-w-0"></div>
|
||||
<p class="font-normal text-[#7a726d] text-[14px] tracking-[-0.09px] whitespace-nowrap leading-normal">Terms of Use · Privacy Policy · Support</p>
|
||||
<div class="site-footer__bottom">
|
||||
<p class="site-footer__legal">{t.copyright}</p>
|
||||
<div class="site-footer__spacer"></div>
|
||||
<p class="site-footer__legal">{t.legal}</p>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
@@ -1,41 +1,181 @@
|
||||
---
|
||||
const logoIcon = "/assets/header-logo-icon.png";
|
||||
const logoWordmark = "/assets/header-logo-wordmark.svg";
|
||||
const globeIcon = "/assets/header-globe.svg";
|
||||
import { assetPath } from '../assets'
|
||||
import { getLocalePath, languageLabels, languageNames, languages, type Lang, type Translations } from '../i18n/translations'
|
||||
|
||||
export interface Props {
|
||||
lang: Lang
|
||||
t: Translations['header']
|
||||
}
|
||||
|
||||
const { lang, t } = Astro.props
|
||||
const logoIcon = assetPath("/assets/header-logo-icon.png");
|
||||
const logoWordmark = assetPath("/assets/header-logo-wordmark.svg");
|
||||
const globeIcon = assetPath("/assets/header-globe.svg");
|
||||
const navItems = [
|
||||
{ href: '#hero', label: t.nav.home },
|
||||
{ href: '#features', label: t.nav.features },
|
||||
{ href: '#experience', label: t.nav.experience },
|
||||
{ href: '#use-cases', label: t.nav.useCases },
|
||||
{ href: '#reliability', label: t.nav.reliability },
|
||||
]
|
||||
const languageOptions = languages.map(item => ({
|
||||
lang: item,
|
||||
href: getLocalePath(item),
|
||||
label: `${languageLabels[item]} - ${languageNames[item]}`,
|
||||
}))
|
||||
---
|
||||
|
||||
<header class="bg-white border-b border-[#e3d9d1] w-full flex items-center justify-center h-[72px] sticky top-0 z-50">
|
||||
<div class="flex items-center w-[1280px]">
|
||||
<!-- Logo -->
|
||||
<div class="flex flex-1 items-center min-w-0">
|
||||
<a href="/" class="relative h-[42px] w-[143px] shrink-0 block">
|
||||
<div class="absolute h-[42px] left-0 top-0 w-[53px] overflow-hidden">
|
||||
<img alt="TalkPro icon" class="absolute h-[126.98%] left-0 max-w-none top-[-13.49%] w-full" src={logoIcon} />
|
||||
<header id="site-header" class="site-header">
|
||||
<div class="site-header__bar">
|
||||
<div class="site-header__brand">
|
||||
<a href={getLocalePath(lang)} class="site-logo">
|
||||
<div class="site-logo__icon-frame">
|
||||
<img alt={t.logoIconAlt} class="site-logo__icon" src={logoIcon} />
|
||||
</div>
|
||||
<div class="absolute inset-[26.97%_0_7.92%_45.45%]">
|
||||
<img alt="TalkPro" class="absolute block inset-0 max-w-none size-full" src={logoWordmark} />
|
||||
<div class="site-logo__wordmark-frame">
|
||||
<img alt={t.logoAlt} class="site-logo__wordmark" src={logoWordmark} />
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- Nav -->
|
||||
<nav class="flex items-center gap-[32px] shrink-0">
|
||||
<a href="#" class="font-semibold text-[14px] text-[#f28a4b] tracking-[-0.09px] whitespace-nowrap leading-normal">Home</a>
|
||||
<a href="#features" class="font-semibold text-[14px] text-[#7a726d] tracking-[-0.09px] whitespace-nowrap leading-normal hover:text-[#f28a4b] transition-colors">Features</a>
|
||||
<a href="#experience" class="font-semibold text-[14px] text-[#7a726d] tracking-[-0.09px] whitespace-nowrap leading-normal hover:text-[#f28a4b] transition-colors">Experience</a>
|
||||
<a href="#use-cases" class="font-semibold text-[14px] text-[#7a726d] tracking-[-0.09px] whitespace-nowrap leading-normal hover:text-[#f28a4b] transition-colors">Use Cases</a>
|
||||
<a href="#reliability" class="font-semibold text-[14px] text-[#7a726d] tracking-[-0.09px] whitespace-nowrap leading-normal hover:text-[#f28a4b] transition-colors">Reliability</a>
|
||||
<nav class="site-nav" aria-label={t.navLabel}>
|
||||
{navItems.map((item, index) => (
|
||||
<a href={item.href} class={`site-nav__link ${index === 0 ? 'is-active' : ''}`} data-nav-link>{item.label}</a>
|
||||
))}
|
||||
</nav>
|
||||
|
||||
<!-- Actions -->
|
||||
<div class="flex flex-1 gap-[12px] items-center justify-end min-w-0">
|
||||
<button class="border border-[rgba(46,42,40,0.3)] flex gap-[8px] h-[43px] items-center justify-center pl-[12px] pr-[16px] rounded-[17px] shrink-0 hover:border-[#f28a4b] transition-colors">
|
||||
<img alt="Language" class="block size-[26px] object-contain" src={globeIcon} />
|
||||
<span class="font-semibold text-[14px] text-[#2e2a28] tracking-[-0.09px] whitespace-nowrap leading-[14px]">EN</span>
|
||||
</button>
|
||||
<a href="#download" class="bg-[#f28a4b] flex items-center justify-center px-[22px] py-[13px] rounded-[17px] shrink-0 hover:bg-[#e07a3b] transition-colors">
|
||||
<span class="font-bold text-[14px] text-white leading-normal whitespace-nowrap">Download</span>
|
||||
<div class="site-header__actions">
|
||||
<div class="language-switcher">
|
||||
<button
|
||||
id="language-toggle"
|
||||
class="language-switcher__button"
|
||||
type="button"
|
||||
aria-label={t.languageAlt}
|
||||
aria-expanded="false"
|
||||
aria-controls="language-menu"
|
||||
>
|
||||
<img alt="" class="language-switcher__icon" src={globeIcon} />
|
||||
<span class="language-switcher__current">{languageLabels[lang]}</span>
|
||||
</button>
|
||||
<div id="language-menu" class="language-switcher__menu is-hidden" aria-hidden="true">
|
||||
{languageOptions.map(option => (
|
||||
<a
|
||||
href={option.href}
|
||||
class={`language-switcher__option ${option.lang === lang ? 'is-active' : ''}`}
|
||||
aria-current={option.lang === lang ? 'page' : undefined}
|
||||
>
|
||||
{option.label}
|
||||
</a>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<a href="#download" class="site-header__download">
|
||||
<span class="site-header__download-label">{t.download}</span>
|
||||
</a>
|
||||
|
||||
<button
|
||||
id="menu-toggle"
|
||||
class="menu-toggle"
|
||||
type="button"
|
||||
aria-label={t.openMenu}
|
||||
aria-expanded="false"
|
||||
aria-controls="mobile-nav"
|
||||
data-open-label={t.openMenu}
|
||||
data-close-label={t.closeMenu}
|
||||
>
|
||||
<span id="bar-1" class="menu-toggle__bar"></span>
|
||||
<span id="bar-2" class="menu-toggle__bar"></span>
|
||||
<span id="bar-3" class="menu-toggle__bar"></span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="mobile-nav" class="mobile-nav is-hidden" aria-hidden="true">
|
||||
<ul class="mobile-nav__list">
|
||||
{navItems.map((item, index) => (
|
||||
<li><a href={item.href} class={`mobile-nav__link ${index === navItems.length - 1 ? 'mobile-nav__link--last' : ''}`} data-nav-link>{item.label}</a></li>
|
||||
))}
|
||||
<li class="mobile-nav__languages">
|
||||
{languages.map(item => (
|
||||
<a href={getLocalePath(item)} class={`mobile-nav__language-link ${item === lang ? 'is-active' : ''}`} aria-current={item === lang ? 'page' : undefined}>
|
||||
{languageNames[item]}
|
||||
</a>
|
||||
))}
|
||||
</li>
|
||||
<li class="mobile-nav__download-item">
|
||||
<a href="#download" class="mobile-nav__download">
|
||||
{t.download}
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<script>
|
||||
const toggle = document.getElementById('menu-toggle') as HTMLButtonElement;
|
||||
const nav = document.getElementById('mobile-nav') as HTMLDivElement;
|
||||
const bar1 = document.getElementById('bar-1') as HTMLSpanElement;
|
||||
const bar2 = document.getElementById('bar-2') as HTMLSpanElement;
|
||||
const bar3 = document.getElementById('bar-3') as HTMLSpanElement;
|
||||
const languageToggle = document.getElementById('language-toggle') as HTMLButtonElement | null;
|
||||
const languageMenu = document.getElementById('language-menu') as HTMLDivElement | null;
|
||||
|
||||
function closeLanguageMenu() {
|
||||
if (!languageToggle || !languageMenu) return;
|
||||
languageToggle.setAttribute('aria-expanded', 'false');
|
||||
languageMenu.classList.add('is-hidden');
|
||||
languageMenu.setAttribute('aria-hidden', 'true');
|
||||
}
|
||||
|
||||
function openLanguageMenu() {
|
||||
if (!languageToggle || !languageMenu) return;
|
||||
languageToggle.setAttribute('aria-expanded', 'true');
|
||||
languageMenu.classList.remove('is-hidden');
|
||||
languageMenu.removeAttribute('aria-hidden');
|
||||
}
|
||||
|
||||
function openMenu() {
|
||||
toggle.setAttribute('aria-expanded', 'true');
|
||||
toggle.setAttribute('aria-label', toggle.dataset.closeLabel || 'Close menu');
|
||||
nav.classList.remove('is-hidden');
|
||||
nav.removeAttribute('aria-hidden');
|
||||
bar1.style.transform = 'translateY(7px) rotate(45deg)';
|
||||
bar2.style.opacity = '0';
|
||||
bar3.style.transform = 'translateY(-7px) rotate(-45deg)';
|
||||
}
|
||||
|
||||
function closeMenu() {
|
||||
toggle.setAttribute('aria-expanded', 'false');
|
||||
toggle.setAttribute('aria-label', toggle.dataset.openLabel || 'Open menu');
|
||||
nav.classList.add('is-hidden');
|
||||
nav.setAttribute('aria-hidden', 'true');
|
||||
bar1.style.transform = '';
|
||||
bar2.style.opacity = '';
|
||||
bar3.style.transform = '';
|
||||
}
|
||||
|
||||
toggle.addEventListener('click', () => {
|
||||
toggle.getAttribute('aria-expanded') === 'true' ? closeMenu() : openMenu();
|
||||
});
|
||||
|
||||
languageToggle?.addEventListener('click', event => {
|
||||
event.stopPropagation();
|
||||
languageToggle.getAttribute('aria-expanded') === 'true' ? closeLanguageMenu() : openLanguageMenu();
|
||||
});
|
||||
|
||||
languageMenu?.addEventListener('click', event => event.stopPropagation());
|
||||
|
||||
document.addEventListener('click', closeLanguageMenu);
|
||||
|
||||
document.addEventListener('keydown', event => {
|
||||
if (event.key === 'Escape') closeLanguageMenu();
|
||||
});
|
||||
|
||||
nav.querySelectorAll('a').forEach(a => a.addEventListener('click', closeMenu));
|
||||
|
||||
window.addEventListener('resize', () => {
|
||||
if (window.innerWidth >= 1024) closeMenu();
|
||||
if (window.innerWidth < 1024) closeLanguageMenu();
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -1,57 +1,82 @@
|
||||
---
|
||||
const heroBg = "/assets/hero-bg.png";
|
||||
const phoneMockup = "/assets/hero-phone.png";
|
||||
import { assetPath } from '../assets'
|
||||
import type { Translations } from '../i18n/translations'
|
||||
|
||||
export interface Props {
|
||||
t: Translations['hero']
|
||||
download: Translations['download']
|
||||
}
|
||||
|
||||
const { t, download } = Astro.props
|
||||
const heroBg = assetPath("/assets/hero-bg.png");
|
||||
const phoneMockup = assetPath("/assets/hero-phone.png");
|
||||
const androidIcon = assetPath("/assets/cta-android-icon.svg");
|
||||
const appleIcon = assetPath("/assets/cta-apple-icon.svg");
|
||||
const defaultApkHref = "https://talkspro.xyz/download";
|
||||
---
|
||||
|
||||
<section class="relative w-full flex items-start justify-center h-[891px] overflow-hidden">
|
||||
<img alt="" class="absolute inset-0 max-w-none object-cover size-full pointer-events-none" src={heroBg} />
|
||||
<section id="hero" class="hero">
|
||||
<img alt="" class="hero__bg" src={heroBg} />
|
||||
|
||||
<div class="relative flex gap-[40px] items-start w-[1280px] h-full">
|
||||
<!-- Phone mockup -->
|
||||
<div class="flex flex-1 items-center min-w-0 pt-[60px] h-full">
|
||||
<div class="relative w-full" style="aspect-ratio: 673/1108;">
|
||||
<div class="absolute inset-0 overflow-hidden pointer-events-none">
|
||||
<img alt="TalkPro app on iPhone" class="absolute h-[114.36%] left-[-2.67%] max-w-none top-[-7.18%] w-[105.35%]" src={phoneMockup} />
|
||||
<div class="hero__inner">
|
||||
<div class="hero__phone-column">
|
||||
<div class="hero__phone-frame">
|
||||
<div class="hero__phone-crop">
|
||||
<img alt={t.phoneAlt} class="hero__phone" src={phoneMockup} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Hero copy -->
|
||||
<div class="flex flex-col gap-[60px] h-full items-start justify-center overflow-clip shrink-0 w-[660px]">
|
||||
<!-- Pill -->
|
||||
<div class="bg-white border border-[#f08458] flex items-center overflow-clip px-[16px] py-[12px] rounded-[999px] shrink-0">
|
||||
<p class="font-bold text-[#0d0d0d] text-[14px] tracking-[-0.09px] leading-normal">✦ Available on iOS and Android</p>
|
||||
<div class="hero__content">
|
||||
<div class="hero__badge">
|
||||
<p class="hero__badge-text">{t.badge}</p>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col gap-[24px] items-start shrink-0 w-full">
|
||||
<div class="font-bold text-[#2e2a28] text-[72px] tracking-[-1.61px] w-full">
|
||||
<p class="leading-[1.1] mb-0">A Modern Way to</p>
|
||||
<p class="leading-[1.1]">Stay Connected</p>
|
||||
<div class="hero__copy">
|
||||
<div class="hero__title">
|
||||
<p class="hero__title-line">{t.titleLine1}</p>
|
||||
<p class="hero__title-line">{t.titleLine2}</p>
|
||||
</div>
|
||||
<p class="font-normal text-[#7a726d] text-[18px] tracking-[-0.33px] leading-[1.5] w-full">
|
||||
TalkPro is a modern messaging app designed for clear, simple, and reliable communication. From private chats to group conversations, channels, voice calls, and video calls, TalkPro helps people stay connected in one familiar experience.
|
||||
<p class="hero__description">
|
||||
{t.description}
|
||||
</p>
|
||||
<div class="flex gap-[14px] items-center overflow-clip shrink-0">
|
||||
<a href="#download" class="bg-[#f28a4b] flex items-center justify-center overflow-clip px-[24px] py-[14px] rounded-[17px] shrink-0 hover:bg-[#e07a3b] transition-colors">
|
||||
<span class="font-bold text-white text-[15px] tracking-[-0.13px] leading-normal">Download TalkPro ↓</span>
|
||||
<div class="hero__actions">
|
||||
<a
|
||||
href={defaultApkHref}
|
||||
class="store-badge store-badge--android store-badge--browser hero__store-badge"
|
||||
rel="noopener noreferrer"
|
||||
target="_blank"
|
||||
>
|
||||
<div class="store-badge__icon-frame">
|
||||
<img alt={download.androidAlt} class="store-badge__android-icon" src={androidIcon} />
|
||||
</div>
|
||||
<div class="store-badge__copy">
|
||||
<p class="store-badge__platform">{download.android}</p>
|
||||
<p class="store-badge__label">{download.androidCta}</p>
|
||||
</div>
|
||||
</a>
|
||||
<a href="#features" class="bg-[rgba(255,255,255,0.55)] border border-[#e3d9d1] flex items-center justify-center overflow-clip px-[24px] py-[14px] rounded-[17px] shrink-0 hover:bg-white transition-colors">
|
||||
<span class="font-bold text-[#2e2a28] text-[15px] tracking-[-0.13px] leading-normal">Explore Features →</span>
|
||||
<a
|
||||
href="#"
|
||||
class="store-badge store-badge--ios store-badge--apple hero__store-badge"
|
||||
data-app-soon="1"
|
||||
>
|
||||
<div class="store-badge__icon-frame">
|
||||
<img alt={download.appleAlt} class="store-badge__apple-icon" src={appleIcon} />
|
||||
</div>
|
||||
<div class="store-badge__copy">
|
||||
<p class="store-badge__platform">{download.ios}</p>
|
||||
<p class="store-badge__label">{download.iosCta}</p>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Tags -->
|
||||
<div class="flex gap-[10px] items-center overflow-clip shrink-0">
|
||||
<div class="bg-[rgba(255,255,255,0.68)] border border-white flex items-center overflow-clip px-[14px] py-[9px] rounded-[999px] shrink-0">
|
||||
<span class="font-bold text-[#7a726d] text-[14px] tracking-[-0.09px] whitespace-nowrap leading-normal">Identity Layer</span>
|
||||
</div>
|
||||
<div class="bg-[rgba(255,255,255,0.68)] border border-white flex items-center overflow-clip px-[14px] py-[9px] rounded-[999px] shrink-0">
|
||||
<span class="font-bold text-[#7a726d] text-[14px] tracking-[-0.09px] whitespace-nowrap leading-normal">AI Native Messaging</span>
|
||||
</div>
|
||||
<div class="bg-[rgba(255,255,255,0.68)] border border-white flex items-center overflow-clip px-[14px] py-[9px] rounded-[999px] shrink-0">
|
||||
<span class="font-bold text-[#7a726d] text-[14px] tracking-[-0.09px] whitespace-nowrap leading-normal">Adaptive Privacy</span>
|
||||
</div>
|
||||
<div class="hero__tags">
|
||||
{t.tags.map(tag => (
|
||||
<div class="hero__tag">
|
||||
<span class="hero__tag-text">{tag}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,96 +1,62 @@
|
||||
---
|
||||
import { assetPath } from '../assets'
|
||||
import type { Translations } from '../i18n/translations'
|
||||
|
||||
export interface Props {
|
||||
t: Translations['trust']
|
||||
}
|
||||
|
||||
const { t } = Astro.props
|
||||
const trustIconSprite = assetPath("/assets/trust-icon-sprite.png")
|
||||
const trustIconImprovement = assetPath("/assets/trust-icon-improvement.png")
|
||||
const trustDivider = assetPath("/assets/trust-divider.svg")
|
||||
const iconClasses = [
|
||||
'trust-card__icon--one',
|
||||
'trust-card__icon--two',
|
||||
'trust-card__icon--three',
|
||||
'trust-card__icon--four',
|
||||
]
|
||||
---
|
||||
|
||||
<section id="reliability" class="bg-white w-full flex flex-col items-center justify-center p-[120px]">
|
||||
<div class="flex flex-col gap-[40px] items-start w-[1280px]">
|
||||
|
||||
<!-- Header -->
|
||||
<div class="flex flex-col gap-[24px] items-start overflow-clip w-full">
|
||||
<div class="bg-white border border-[#fbbfa3] flex items-center justify-center px-[24px] py-[12px] rounded-[9999px] shrink-0">
|
||||
<span class="font-bold text-[#f08458] text-[14px] text-center tracking-[-0.04px] whitespace-nowrap leading-normal">RELIABILITY</span>
|
||||
<section id="reliability" class="trust">
|
||||
<div class="trust__inner">
|
||||
<div class="trust__header">
|
||||
<div class="section-eyebrow">
|
||||
<span class="section-eyebrow__text">{t.eyebrow}</span>
|
||||
</div>
|
||||
<p class="font-bold text-[#1a1a1a] text-[48px] tracking-[-1.16px] leading-[1.2] w-full">Built with User Trust in Mind</p>
|
||||
<p class="font-normal text-[#7a726d] text-[18px] tracking-[-0.33px] leading-[1.5] w-full">
|
||||
TalkPro is designed with a privacy-conscious approach and a focus on dependable communication. The app keeps the user experience simple while supporting the core communication features people expect from a modern messaging platform.
|
||||
<p class="trust__title">{t.title}</p>
|
||||
<p class="trust__description">
|
||||
{t.description}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- 4-column feature row -->
|
||||
<div class="flex gap-[24px] items-center justify-center w-full">
|
||||
|
||||
<!-- Card 1: Privacy-Conscious Design -->
|
||||
<div class="flex flex-1 flex-col gap-[16px] items-center justify-end min-w-0 p-[24px] rounded-[30px]">
|
||||
<div class="relative shrink-0 size-[128px]">
|
||||
<div class="absolute inset-0 overflow-hidden pointer-events-none">
|
||||
<img alt="" class="absolute h-[174.55%] left-[-31.48%] max-w-none top-[7.04%] w-[312.73%]" src="/assets/trust-icon-sprite.png" />
|
||||
<div class="trust__grid">
|
||||
{t.cards.map((card, index) => (
|
||||
<>
|
||||
<div class="trust-card">
|
||||
<div class="trust-card__icon-frame">
|
||||
<div class="trust-card__icon-crop">
|
||||
<img
|
||||
alt=""
|
||||
class={`trust-card__icon ${iconClasses[index]}`}
|
||||
src={index === 3 ? trustIconImprovement : trustIconSprite}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="trust-card__copy">
|
||||
<p class="trust-card__title">{card.title}</p>
|
||||
<p class="trust-card__description">{card.desc}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex flex-col gap-[8px] items-start w-full">
|
||||
<p class="font-semibold text-[#0d0d0d] text-[16px] leading-[22px] w-full">Privacy-Conscious Design</p>
|
||||
<p class="font-medium text-[#7a726d] text-[15px] leading-[1.5] w-full">Built with careful consideration for user communication and data handling.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Divider -->
|
||||
<div class="relative h-[118px] shrink-0 w-0">
|
||||
<div class="absolute inset-[0_-0.5px]">
|
||||
<img alt="" class="block max-w-none size-full" src="/assets/trust-divider.svg" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Card 2: Minimal and Focused -->
|
||||
<div class="flex flex-1 flex-col gap-[16px] items-center justify-end min-w-0 p-[24px] rounded-[30px]">
|
||||
<div class="relative shrink-0 size-[128px]">
|
||||
<div class="absolute inset-0 overflow-hidden pointer-events-none">
|
||||
<img alt="" class="absolute h-[187.32%] left-[-164.72%] max-w-none top-[3.1%] w-[335.61%]" src="/assets/trust-icon-sprite.png" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex flex-col gap-[8px] items-start w-full">
|
||||
<p class="font-semibold text-[#0d0d0d] text-[16px] leading-[22px] w-full">Minimal and Focused</p>
|
||||
<p class="font-medium text-[#7a726d] text-[15px] leading-[1.5] w-full">Designed around essential messaging features without unnecessary complexity.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Divider -->
|
||||
<div class="relative h-[118px] shrink-0 w-0">
|
||||
<div class="absolute inset-[0_-0.5px]">
|
||||
<img alt="" class="block max-w-none size-full" src="/assets/trust-divider.svg" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Card 3: Reliable Experience -->
|
||||
<div class="flex flex-1 flex-col gap-[16px] items-center justify-end min-w-0 p-[24px] rounded-[30px]">
|
||||
<div class="relative shrink-0 size-[128px]">
|
||||
<div class="absolute inset-0 overflow-hidden pointer-events-none">
|
||||
<img alt="" class="absolute h-[211.46%] left-[-187.93%] max-w-none top-[-105.62%] w-[378.86%]" src="/assets/trust-icon-sprite.png" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex flex-col gap-[8px] items-start w-full">
|
||||
<p class="font-semibold text-[#0d0d0d] text-[16px] leading-[22px] w-full">Reliable Experience</p>
|
||||
<p class="font-medium text-[#7a726d] text-[15px] leading-[1.5] w-full">Focused on providing a stable and familiar communication experience.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Divider -->
|
||||
<div class="relative h-[118px] shrink-0 w-0">
|
||||
<div class="absolute inset-[0_-0.5px]">
|
||||
<img alt="" class="block max-w-none size-full" src="/assets/trust-divider.svg" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Card 4: Continuous Improvement -->
|
||||
<div class="flex flex-1 flex-col gap-[16px] items-center justify-end min-w-0 p-[24px] rounded-[30px]">
|
||||
<div class="relative shrink-0 size-[128px]">
|
||||
<div class="absolute inset-0 overflow-hidden pointer-events-none">
|
||||
<img alt="" class="absolute left-[4.14%] max-w-none size-full top-0" src="/assets/trust-icon-improvement.png" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex flex-col gap-[8px] items-start w-full">
|
||||
<p class="font-semibold text-[#0d0d0d] text-[16px] leading-[22px] w-full">Continuous Improvement</p>
|
||||
<p class="font-medium text-[#7a726d] text-[15px] leading-[1.5] w-full">TalkPro will continue to improve its interface, features, and overall user experience.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{index < t.cards.length - 1 && (
|
||||
<div class="trust__divider">
|
||||
<div class="trust__divider-frame">
|
||||
<img alt="" class="trust__divider-image" src={trustDivider} />
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@@ -1,36 +1,35 @@
|
||||
---
|
||||
const rows = [
|
||||
{ title: 'Personal Conversations', desc: 'Message friends, family, and close contacts in a simple private chat experience.' },
|
||||
{ title: 'Communities', desc: 'Join group conversations and stay active in shared interest spaces.' },
|
||||
{ title: 'Teams and Projects', desc: 'Coordinate discussions, updates, and quick decisions in group chats.' },
|
||||
{ title: 'News and Updates', desc: 'Follow channels for announcements, information, and community content.' },
|
||||
]
|
||||
import type { Translations } from '../i18n/translations'
|
||||
|
||||
export interface Props {
|
||||
t: Translations['useCases']
|
||||
}
|
||||
|
||||
const { t } = Astro.props
|
||||
---
|
||||
|
||||
<section id="use-cases" class="bg-[#fef0eb] w-full flex items-start justify-center px-[130px] py-[120px]">
|
||||
<div class="flex gap-[40px] items-center w-[1280px]">
|
||||
<!-- Left: heading -->
|
||||
<div class="flex flex-col gap-[24px] items-start overflow-clip shrink-0 w-[540px]">
|
||||
<div class="bg-white border border-[#fbbfa3] flex items-center justify-center px-[24px] py-[12px] rounded-[9999px] shrink-0">
|
||||
<span class="font-bold text-[#f08458] text-[14px] text-center tracking-[-0.04px] whitespace-nowrap leading-normal">USE CASES</span>
|
||||
<section id="use-cases" class="use-cases">
|
||||
<div class="use-cases__inner">
|
||||
<div class="use-cases__copy">
|
||||
<div class="section-eyebrow">
|
||||
<span class="section-eyebrow__text">{t.eyebrow}</span>
|
||||
</div>
|
||||
<p class="font-bold text-[#1a1a1a] text-[48px] tracking-[-1.16px] leading-[1.2] w-full">
|
||||
Made for Personal, Social, and Community Communication
|
||||
<p class="use-cases__title">
|
||||
{t.title}
|
||||
</p>
|
||||
<p class="font-normal text-[#7a726d] text-[18px] tracking-[-0.33px] leading-[1.5] w-full">
|
||||
With separate spaces for every context, TalkPro keeps your personal, social, and professional communications distinct and organized.
|
||||
<p class="use-cases__description">
|
||||
{t.description}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Right: rows -->
|
||||
<div class="flex flex-1 flex-col items-start min-w-0 overflow-clip rounded-[30px] gap-px">
|
||||
{rows.map(row => (
|
||||
<div class="bg-[#faede8] flex h-[120px] items-center overflow-clip w-full shrink-0">
|
||||
<div class="bg-[#f08458] flex h-full items-center px-[36px] py-[24px] shrink-0 w-[300px]">
|
||||
<p class="flex-1 font-semibold text-[20px] text-white tracking-[-0.6px] leading-normal min-w-0">{row.title}</p>
|
||||
<div class="use-cases__rows">
|
||||
{t.rows.map(row => (
|
||||
<div class="use-case-row">
|
||||
<div class="use-case-row__title-cell">
|
||||
<p class="use-case-row__title">{row.title}</p>
|
||||
</div>
|
||||
<div class="bg-white flex flex-1 h-full items-center min-w-0 px-[36px] py-[24px]">
|
||||
<p class="font-medium text-[#7a726d] text-[15px] leading-[1.5] flex-1 min-w-0">{row.desc}</p>
|
||||
<div class="use-case-row__description-cell">
|
||||
<p class="use-case-row__description">{row.desc}</p>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
|
||||
@@ -1,89 +1,73 @@
|
||||
---
|
||||
const underline = "/assets/why-underline.svg";
|
||||
const iconSimple = "/assets/why-icon-simple.svg";
|
||||
const iconFamiliar = "/assets/why-icon-familiar.svg";
|
||||
const iconConn = "/assets/why-icon-connected.svg";
|
||||
const iconModern = "/assets/why-icon-modern.svg";
|
||||
import { assetPath } from '../assets'
|
||||
import type { Translations } from '../i18n/translations'
|
||||
|
||||
export interface Props {
|
||||
t: Translations['why']
|
||||
}
|
||||
|
||||
const { t } = Astro.props
|
||||
const underline = assetPath("/assets/why-underline.svg");
|
||||
const icons = [
|
||||
assetPath("/assets/why-icon-simple.svg"),
|
||||
assetPath("/assets/why-icon-familiar.svg"),
|
||||
assetPath("/assets/why-icon-connected.svg"),
|
||||
assetPath("/assets/why-icon-modern.svg"),
|
||||
]
|
||||
const illustrationVideo = assetPath("/assets/why-illustration.mp4")
|
||||
const iconClasses = ['why-card__icon--simple', 'why-card__icon--familiar', 'why-card__icon--connected', 'why-card__icon--modern']
|
||||
---
|
||||
|
||||
<section class="bg-white w-full flex flex-col items-center justify-center py-[120px]">
|
||||
<div class="flex flex-col gap-[40px] items-start w-[1280px]">
|
||||
|
||||
<!-- Header row -->
|
||||
<div class="flex gap-[36px] items-start overflow-clip w-full">
|
||||
<!-- Left: pill + heading + description -->
|
||||
<div class="flex flex-1 flex-col gap-[36px] items-start min-w-0">
|
||||
<div class="bg-white border border-[#fbbfa3] flex items-center justify-center px-[24px] py-[12px] rounded-[9999px] shrink-0">
|
||||
<span class="font-bold text-[#f08458] text-[14px] text-center tracking-[-0.04px] whitespace-nowrap leading-normal">WHY TALKPRO</span>
|
||||
<section class="why">
|
||||
<div class="why__inner">
|
||||
<div class="why__intro">
|
||||
<div class="why__copy">
|
||||
<div class="section-eyebrow">
|
||||
<span class="section-eyebrow__text">{t.eyebrow}</span>
|
||||
</div>
|
||||
<div class="flex flex-col gap-[24px] items-start w-full">
|
||||
<div class="font-bold text-[#1a1a1a] text-[48px] tracking-[-1.16px] w-full">
|
||||
<p class="leading-[1.2] mb-0">Designed for the Way</p>
|
||||
<p class="leading-[1.2]">People Communicate Today</p>
|
||||
<div class="why__text">
|
||||
<div class="why__title">
|
||||
<p class="why__title-line">{t.titleLine1}</p>
|
||||
<p class="why__title-line">{t.titleLine2}</p>
|
||||
</div>
|
||||
<!-- Underline decoration -->
|
||||
<div class="relative h-0 w-[295.5px] shrink-0">
|
||||
<div class="absolute inset-[-0.5px_0]">
|
||||
<img alt="" class="block max-w-none size-full" src={underline} />
|
||||
<div class="why__underline">
|
||||
<div class="why__underline-frame">
|
||||
<img alt="" class="why__underline-image" src={underline} />
|
||||
</div>
|
||||
</div>
|
||||
<p class="font-normal text-[#7a726d] text-[18px] tracking-[-0.33px] leading-[1.5] w-full">
|
||||
Communication today happens across personal conversations, communities, work groups, and content channels. TalkPro brings these essential communication experiences together in a simple and familiar interface, making it easier for users to connect, share, and stay updated.
|
||||
<p class="why__description">
|
||||
{t.description}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Right: illustration -->
|
||||
<div class="relative shrink-0 size-[480px]">
|
||||
<img alt="People communicating with TalkPro" class="absolute inset-0 size-full object-contain" src="/assets/why-illustration.png" />
|
||||
<div class="why__illustration">
|
||||
<video
|
||||
aria-label={t.illustrationAlt}
|
||||
class="why__illustration-image"
|
||||
autoplay
|
||||
loop
|
||||
muted
|
||||
playsinline
|
||||
preload="metadata"
|
||||
>
|
||||
<source src={illustrationVideo} type="video/mp4" />
|
||||
</video>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 2×2 card grid -->
|
||||
<div class="flex flex-col gap-[24px] items-start overflow-clip w-full">
|
||||
<!-- Row 1 -->
|
||||
<div class="flex gap-[24px] items-center w-full">
|
||||
<div class="bg-[#fef0eb] border border-[#e8e4de] flex flex-1 gap-[24px] h-[152px] items-center min-w-0 overflow-clip p-[36px] rounded-[30px]">
|
||||
<div class="bg-[#f08458] aspect-square h-full flex items-center justify-center overflow-clip rounded-[9999px] shrink-0">
|
||||
<img alt="" class="block size-[44px]" src={iconSimple} />
|
||||
<div class="why__grid">
|
||||
{t.cards.map((card, index) => (
|
||||
<div class="why-card">
|
||||
<div class="why-card__icon-frame">
|
||||
<img alt="" class={`why-card__icon ${iconClasses[index]}`} src={icons[index]} />
|
||||
</div>
|
||||
<div class="flex flex-1 flex-col gap-[12px] items-start min-w-0 overflow-clip">
|
||||
<p class="font-semibold text-[#0d0d0d] text-[24px] tracking-[-0.47px] leading-[20px] w-full">Simple</p>
|
||||
<p class="font-medium text-[#7a726d] text-[16px] tracking-[-0.18px] leading-[1.5] w-full">An easy-to-use experience designed for everyday communication.</p>
|
||||
<div class="why-card__copy">
|
||||
<p class="why-card__title">{card.title}</p>
|
||||
<p class="why-card__description">{card.desc}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="bg-[#fef0eb] border border-[#e8e4de] flex flex-1 gap-[24px] h-[152px] items-center min-w-0 overflow-clip p-[36px] rounded-[30px]">
|
||||
<div class="bg-[#f08458] aspect-square h-full flex items-center justify-center overflow-clip rounded-[9999px] shrink-0">
|
||||
<img alt="" class="block" style="width:38px;height:40px;" src={iconFamiliar} />
|
||||
</div>
|
||||
<div class="flex flex-1 flex-col gap-[12px] items-start min-w-0 overflow-clip">
|
||||
<p class="font-semibold text-[#0d0d0d] text-[24px] tracking-[-0.47px] leading-[20px] w-full">Familiar</p>
|
||||
<p class="font-medium text-[#7a726d] text-[16px] tracking-[-0.18px] leading-[1.5] w-full">A messaging structure that feels natural from the first use.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Row 2 -->
|
||||
<div class="flex gap-[24px] items-center w-full">
|
||||
<div class="bg-[#fef0eb] border border-[#e8e4de] flex flex-1 gap-[24px] h-[152px] items-center min-w-0 overflow-clip p-[36px] rounded-[30px]">
|
||||
<div class="bg-[#f08458] aspect-square h-full flex items-center justify-center overflow-clip rounded-[9999px] shrink-0">
|
||||
<img alt="" class="block size-[44px]" src={iconConn} />
|
||||
</div>
|
||||
<div class="flex flex-1 flex-col gap-[12px] items-start min-w-0 overflow-clip">
|
||||
<p class="font-semibold text-[#0d0d0d] text-[24px] tracking-[-0.47px] leading-[20px] w-full">Connected</p>
|
||||
<p class="font-medium text-[#7a726d] text-[16px] tracking-[-0.18px] leading-[1.5] w-full">Private chats, groups, channels, voice, and video in one app.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="bg-[#fef0eb] border border-[#e8e4de] flex flex-1 gap-[24px] h-[152px] items-center min-w-0 overflow-clip p-[36px] rounded-[30px]">
|
||||
<div class="bg-[#f08458] aspect-square h-full flex items-center justify-center overflow-clip rounded-[9999px] shrink-0">
|
||||
<img alt="" class="block" style="width:24px;height:44px;" src={iconModern} />
|
||||
</div>
|
||||
<div class="flex flex-1 flex-col gap-[12px] items-start min-w-0 overflow-clip">
|
||||
<p class="font-semibold text-[#0d0d0d] text-[24px] tracking-[-0.47px] leading-[20px] w-full">Modern</p>
|
||||
<p class="font-medium text-[#7a726d] text-[16px] tracking-[-0.18px] leading-[1.5] w-full">A refined interface with clean visuals and smooth interaction.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</section>
|
||||
|
||||
2738
src/i18n/translations.ts
Normal file
@@ -3,22 +3,106 @@ import '../styles/global.css'
|
||||
|
||||
export interface Props {
|
||||
title?: string
|
||||
description?: string
|
||||
lang?: string
|
||||
}
|
||||
const { title = 'Talk Pro — One User. Multiple Worlds.' } = Astro.props
|
||||
|
||||
const {
|
||||
title = 'Talk Pro - One User. Multiple Worlds.',
|
||||
description = 'Talk Pro is a modern messaging app for private chats, group conversations, channels, voice and video calls.',
|
||||
lang = 'en',
|
||||
} = Astro.props
|
||||
---
|
||||
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<html lang={lang}>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<meta name="description" content="Talk Pro is a modern messaging app for private chats, group conversations, channels, voice and video calls." />
|
||||
<meta name="description" content={description} />
|
||||
<title>{title}</title>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;700&display=swap" rel="stylesheet" />
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet" />
|
||||
</head>
|
||||
<body class="bg-surface font-sans overflow-x-hidden">
|
||||
<slot />
|
||||
<script src="/site-links-client.js" defer></script>
|
||||
<script>
|
||||
(() => {
|
||||
const header = document.getElementById('site-header');
|
||||
const getOffset = () => header ? header.offsetHeight : 0;
|
||||
let activeScrollAnimation = 0;
|
||||
|
||||
const easeInOutCubic = (t: number) => (
|
||||
t < 0.5 ? 4 * t * t * t : 1 - Math.pow(-2 * t + 2, 3) / 2
|
||||
);
|
||||
|
||||
const animateScrollTo = (targetTop: number) => {
|
||||
const startTop = window.scrollY;
|
||||
const distance = targetTop - startTop;
|
||||
|
||||
if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) {
|
||||
window.scrollTo(0, targetTop);
|
||||
return;
|
||||
}
|
||||
|
||||
if (activeScrollAnimation) {
|
||||
cancelAnimationFrame(activeScrollAnimation);
|
||||
}
|
||||
|
||||
const duration = Math.min(1300, Math.max(650, Math.abs(distance) * 0.7));
|
||||
const startTime = performance.now();
|
||||
|
||||
const step = (now: number) => {
|
||||
const elapsed = now - startTime;
|
||||
const progress = Math.min(elapsed / duration, 1);
|
||||
const easedProgress = easeInOutCubic(progress);
|
||||
|
||||
window.scrollTo(0, startTop + distance * easedProgress);
|
||||
|
||||
if (progress < 1) {
|
||||
activeScrollAnimation = requestAnimationFrame(step);
|
||||
} else {
|
||||
activeScrollAnimation = 0;
|
||||
}
|
||||
};
|
||||
|
||||
activeScrollAnimation = requestAnimationFrame(step);
|
||||
};
|
||||
|
||||
document.querySelectorAll('a[href^="#"]').forEach(link => {
|
||||
link.addEventListener('click', e => {
|
||||
const href = link.getAttribute('href');
|
||||
if (!href || href === '#') return;
|
||||
const target = document.querySelector(href);
|
||||
if (!target) return;
|
||||
e.preventDefault();
|
||||
const top = target.getBoundingClientRect().top + window.scrollY - getOffset();
|
||||
animateScrollTo(top);
|
||||
history.pushState(null, '', href);
|
||||
});
|
||||
});
|
||||
|
||||
const navLinks = document.querySelectorAll('[data-nav-link]');
|
||||
const sections = Array.from(navLinks)
|
||||
.map(l => document.querySelector(l.getAttribute('href') ?? ''))
|
||||
.filter(Boolean) as Element[];
|
||||
|
||||
if ('IntersectionObserver' in window && sections.length) {
|
||||
const observer = new IntersectionObserver(entries => {
|
||||
const visible = entries
|
||||
.filter(e => e.isIntersecting)
|
||||
.sort((a, b) => b.intersectionRatio - a.intersectionRatio)[0];
|
||||
if (!visible) return;
|
||||
navLinks.forEach(link => {
|
||||
const active = link.getAttribute('href') === `#${visible.target.id}`;
|
||||
link.classList.toggle('is-active', active);
|
||||
});
|
||||
}, { rootMargin: '-30% 0px -60% 0px', threshold: 0 });
|
||||
sections.forEach(s => observer.observe(s));
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
36
src/pages/[lang]/index.astro
Normal file
@@ -0,0 +1,36 @@
|
||||
---
|
||||
import Base from '../../layouts/Base.astro'
|
||||
import Header from '../../components/Header.astro'
|
||||
import Hero from '../../components/Hero.astro'
|
||||
import WhyTalkPro from '../../components/WhyTalkPro.astro'
|
||||
import CoreSystem from '../../components/CoreSystem.astro'
|
||||
import Experience from '../../components/Experience.astro'
|
||||
import UseCases from '../../components/UseCases.astro'
|
||||
import Trust from '../../components/Trust.astro'
|
||||
import DownloadCTA from '../../components/DownloadCTA.astro'
|
||||
import Footer from '../../components/Footer.astro'
|
||||
import { defaultLang, getTranslations, isLang, languages } from '../../i18n/translations'
|
||||
|
||||
export function getStaticPaths() {
|
||||
return languages
|
||||
.filter(lang => lang !== defaultLang)
|
||||
.map(lang => ({ params: { lang } }))
|
||||
}
|
||||
|
||||
const currentLang = Astro.params.lang
|
||||
const lang = isLang(currentLang) ? currentLang : defaultLang
|
||||
const t = getTranslations(lang)
|
||||
---
|
||||
|
||||
<Base lang={lang} title={t.meta.title} description={t.meta.description}>
|
||||
<Header lang={lang} t={t.header} />
|
||||
<Hero t={t.hero} download={t.download} />
|
||||
<WhyTalkPro t={t.why} />
|
||||
<CoreSystem t={t.core} />
|
||||
<Experience t={t.experience} />
|
||||
<UseCases t={t.useCases} />
|
||||
<Trust t={t.trust} />
|
||||
<!-- AppPreview section disabled per lead request. -->
|
||||
<DownloadCTA t={t.download} siteLinks={t.siteLinks} />
|
||||
<Footer t={t.footer} />
|
||||
</Base>
|
||||
@@ -7,20 +7,23 @@ import CoreSystem from '../components/CoreSystem.astro'
|
||||
import Experience from '../components/Experience.astro'
|
||||
import UseCases from '../components/UseCases.astro'
|
||||
import Trust from '../components/Trust.astro'
|
||||
import AppPreview from '../components/AppPreview.astro'
|
||||
import DownloadCTA from '../components/DownloadCTA.astro'
|
||||
import Footer from '../components/Footer.astro'
|
||||
import { defaultLang, getTranslations } from '../i18n/translations'
|
||||
|
||||
const lang = defaultLang
|
||||
const t = getTranslations(lang)
|
||||
---
|
||||
|
||||
<Base>
|
||||
<Header />
|
||||
<Hero />
|
||||
<WhyTalkPro />
|
||||
<CoreSystem />
|
||||
<Experience />
|
||||
<UseCases />
|
||||
<Trust />
|
||||
<AppPreview />
|
||||
<DownloadCTA />
|
||||
<Footer />
|
||||
<Base lang={lang} title={t.meta.title} description={t.meta.description}>
|
||||
<Header lang={lang} t={t.header} />
|
||||
<Hero t={t.hero} download={t.download} />
|
||||
<WhyTalkPro t={t.why} />
|
||||
<CoreSystem t={t.core} />
|
||||
<Experience t={t.experience} />
|
||||
<UseCases t={t.useCases} />
|
||||
<Trust t={t.trust} />
|
||||
<!-- AppPreview section disabled per lead request. -->
|
||||
<DownloadCTA t={t.download} siteLinks={t.siteLinks} />
|
||||
<Footer t={t.footer} />
|
||||
</Base>
|
||||
|
||||
344
src/styles/download.css
Normal file
@@ -0,0 +1,344 @@
|
||||
.download-cta {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
min-height: 400px;
|
||||
overflow: hidden;
|
||||
background: #fff;
|
||||
border-top: 1px solid #eec8b8;
|
||||
}
|
||||
|
||||
.download-cta__pattern {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
width: 1920px;
|
||||
max-width: none;
|
||||
height: 923px;
|
||||
pointer-events: none;
|
||||
transform: translate(-50%, -50%);
|
||||
}
|
||||
|
||||
.download-cta__inner {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
max-width: 1280px;
|
||||
gap: 0px;
|
||||
margin: 0 auto;
|
||||
padding: 60px 16px;
|
||||
}
|
||||
|
||||
.download-cta__content {
|
||||
display: flex;
|
||||
order: 2;
|
||||
flex: 1;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-width: 0;
|
||||
gap: 36px;
|
||||
overflow: clip;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.download-cta__copy {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.download-cta__heading {
|
||||
--download-heading-title-size: 32px;
|
||||
display: flex;
|
||||
flex-wrap: nowrap;
|
||||
align-items: center;
|
||||
flex-shrink: 0;
|
||||
gap: 16px;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.download-cta__title {
|
||||
position: relative;
|
||||
top: -4px;
|
||||
margin: 0;
|
||||
flex-shrink: 0;
|
||||
font-size: var(--download-heading-title-size);
|
||||
font-weight: 700;
|
||||
line-height: 1.2;
|
||||
letter-spacing: var(--ls-32);
|
||||
color: #1a1a1a;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.download-cta__logo-frame {
|
||||
position: relative;
|
||||
flex-shrink: 0;
|
||||
width: calc(var(--download-heading-title-size) * 3.9167);
|
||||
height: calc(var(--download-heading-title-size) * 1.5);
|
||||
}
|
||||
|
||||
.download-cta__logo {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
display: block;
|
||||
width: 100%;
|
||||
max-width: none;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.download-cta__description {
|
||||
max-width: 542px;
|
||||
margin: 0;
|
||||
font-size: 16px;
|
||||
font-weight: 400;
|
||||
line-height: 1.5;
|
||||
letter-spacing: var(--ls-16);
|
||||
color: #7a726d;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.store-badges {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex-wrap: nowrap;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-shrink: 0;
|
||||
width: 100%;
|
||||
gap: 16px;
|
||||
overflow: clip;
|
||||
}
|
||||
|
||||
.store-badge {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-shrink: 0;
|
||||
width: 260px;
|
||||
max-width: 100%;
|
||||
height: 70px;
|
||||
gap: 8px;
|
||||
padding: 12px 16px;
|
||||
overflow: clip;
|
||||
border-radius: 20px;
|
||||
}
|
||||
|
||||
a.store-badge {
|
||||
box-sizing: border-box;
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.download-cta__links-meta {
|
||||
font-size: 13px;
|
||||
color: #7a726d;
|
||||
margin: 14px auto 0;
|
||||
max-width: min(1180px, calc(100% - 40px));
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.store-badge--android {
|
||||
background: #f28a4b;
|
||||
border: 1px solid #c5834e;
|
||||
}
|
||||
|
||||
.store-badge--ios {
|
||||
background: #383838;
|
||||
border: 1px solid #141414;
|
||||
}
|
||||
|
||||
.store-badge__icon {
|
||||
display: block;
|
||||
flex-shrink: 0;
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
}
|
||||
|
||||
.store-badge__icon-frame {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-shrink: 0;
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
background: #151515;
|
||||
border-radius: 12px;
|
||||
}
|
||||
|
||||
.store-badge__apple-icon {
|
||||
display: block;
|
||||
width: 22px;
|
||||
height: 27px;
|
||||
}
|
||||
|
||||
.store-badge--android .store-badge__icon-frame {
|
||||
background: #d55f31;
|
||||
}
|
||||
|
||||
.store-badge__android-icon {
|
||||
display: block;
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
}
|
||||
|
||||
.store-badge__copy {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
flex-shrink: 0;
|
||||
gap: 3px;
|
||||
overflow: clip;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.store-badge__platform {
|
||||
margin: 0;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
line-height: normal;
|
||||
letter-spacing: var(--ls-13);
|
||||
}
|
||||
|
||||
.store-badge--android .store-badge__platform {
|
||||
color: #ffd6bc;
|
||||
}
|
||||
|
||||
.store-badge--ios .store-badge__platform {
|
||||
color: #ccc;
|
||||
}
|
||||
|
||||
.store-badge__label {
|
||||
margin: 0;
|
||||
font-size: 15px;
|
||||
font-weight: 600;
|
||||
line-height: normal;
|
||||
letter-spacing: var(--ls-15);
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.download-cta__phone {
|
||||
position: relative;
|
||||
display: block;
|
||||
order: 1;
|
||||
flex-shrink: 0;
|
||||
width: 240px;
|
||||
height: 292px;
|
||||
}
|
||||
|
||||
@media (min-width: 578px) {
|
||||
.download-cta__phone {
|
||||
width: min(418px, calc(100vw - 32px));
|
||||
height: auto;
|
||||
aspect-ratio: 418 / 510;
|
||||
}
|
||||
}
|
||||
|
||||
.download-cta__phone-crop {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
overflow: hidden;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.download-cta__phone-image {
|
||||
position: absolute;
|
||||
top: -18.05%;
|
||||
left: 3.8%;
|
||||
width: 92.43%;
|
||||
max-width: none;
|
||||
height: 136.1%;
|
||||
}
|
||||
|
||||
@media (max-width: 397px) {
|
||||
.download-cta__heading {
|
||||
--download-heading-title-size: clamp(24px, 8vw, 32px);
|
||||
gap: clamp(8px, 2.5vw, 12px);
|
||||
}
|
||||
|
||||
.download-cta__title {
|
||||
line-height: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 569px) {
|
||||
.store-badge {
|
||||
width: min(100%, calc(100vw - 32px));
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 570px) {
|
||||
.download-cta__heading {
|
||||
--download-heading-title-size: 40px;
|
||||
}
|
||||
|
||||
.download-cta__title {
|
||||
letter-spacing: var(--ls-40);
|
||||
}
|
||||
|
||||
.download-cta__description {
|
||||
font-size: 18px;
|
||||
letter-spacing: var(--ls-18);
|
||||
}
|
||||
|
||||
.store-badges {
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 1024px) {
|
||||
.download-cta {
|
||||
height: 600px;
|
||||
}
|
||||
|
||||
.download-cta__inner {
|
||||
flex-direction: row;
|
||||
gap: 16px;
|
||||
padding: 0 24px;
|
||||
}
|
||||
|
||||
.download-cta__content {
|
||||
order: 1;
|
||||
align-items: flex-start;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.download-cta__copy {
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.download-cta__description {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.store-badges {
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
.download-cta__heading {
|
||||
--download-heading-title-size: 48px;
|
||||
}
|
||||
|
||||
.download-cta__title {
|
||||
letter-spacing: var(--ls-48);
|
||||
}
|
||||
|
||||
.download-cta__phone {
|
||||
order: 2;
|
||||
width: 418px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 1280px) {
|
||||
.download-cta__inner {
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
209
src/styles/features.css
Normal file
@@ -0,0 +1,209 @@
|
||||
.features {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
gap: 60px;
|
||||
padding: 60px 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.features__bg {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
width: 2363px;
|
||||
height: 1319px;
|
||||
object-fit: cover;
|
||||
opacity: 0.2;
|
||||
pointer-events: none;
|
||||
transform: translate(-50%, -50%);
|
||||
}
|
||||
|
||||
.features__header {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
width: 100%;
|
||||
max-width: 1280px;
|
||||
gap: 24px;
|
||||
margin: 0 auto;
|
||||
padding: 0 16px;
|
||||
overflow: clip;
|
||||
}
|
||||
|
||||
.features__title {
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
font-size: 28px;
|
||||
font-weight: 700;
|
||||
line-height: 1.2;
|
||||
letter-spacing: var(--ls-28);
|
||||
color: #1a1a1a;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.features__description {
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
font-size: 15px;
|
||||
font-weight: 400;
|
||||
line-height: 1.5;
|
||||
letter-spacing: var(--ls-15);
|
||||
color: #7a726d;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.features__grid {
|
||||
position: relative;
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 1fr);
|
||||
width: 100%;
|
||||
max-width: 1280px;
|
||||
gap: 22px;
|
||||
margin: 0 auto;
|
||||
padding: 0 16px;
|
||||
}
|
||||
|
||||
.feature-card {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 24px;
|
||||
padding: 36px;
|
||||
background: rgba(255, 255, 255, 0.78);
|
||||
border-radius: 30px;
|
||||
}
|
||||
|
||||
.feature-card__icon-frame {
|
||||
position: relative;
|
||||
flex-shrink: 0;
|
||||
width: 160px;
|
||||
height: 160px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.feature-card__icon {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
width: 100%;
|
||||
max-width: none;
|
||||
height: 100%;
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
.feature-card__copy {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
width: 100%;
|
||||
gap: 12px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.feature-card__title {
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
font-size: 20px;
|
||||
font-weight: 700;
|
||||
line-height: normal;
|
||||
letter-spacing: var(--ls-20);
|
||||
color: #2e2a28;
|
||||
}
|
||||
|
||||
.feature-card__description {
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
font-size: 15px;
|
||||
font-weight: 400;
|
||||
line-height: 1.5;
|
||||
letter-spacing: var(--ls-15);
|
||||
color: #7a726d;
|
||||
}
|
||||
|
||||
@media (min-width: 440px) {
|
||||
.features__header,
|
||||
.features__grid {
|
||||
padding-left: 20px;
|
||||
padding-right: 20px;
|
||||
}
|
||||
|
||||
.features__title {
|
||||
font-size: 32px;
|
||||
letter-spacing: var(--ls-32);
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 576px) {
|
||||
.features__header,
|
||||
.features__grid {
|
||||
padding-left: 24px;
|
||||
padding-right: 24px;
|
||||
}
|
||||
|
||||
.features__title {
|
||||
font-size: 36px;
|
||||
letter-spacing: var(--ls-36);
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.features__header,
|
||||
.features__grid {
|
||||
padding-left: 36px;
|
||||
padding-right: 36px;
|
||||
}
|
||||
|
||||
.features__title {
|
||||
font-size: 42px;
|
||||
letter-spacing: var(--ls-42);
|
||||
}
|
||||
|
||||
.features__grid {
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
}
|
||||
|
||||
.feature-card__title {
|
||||
font-size: 24px;
|
||||
letter-spacing: var(--ls-24);
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 1024px) {
|
||||
.features__header {
|
||||
padding: 0 clamp(36px, 14vw, 180px);
|
||||
}
|
||||
|
||||
.features__grid {
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
padding: 0 36px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 1200px) {
|
||||
.features__title {
|
||||
font-size: 48px;
|
||||
letter-spacing: var(--ls-48);
|
||||
}
|
||||
|
||||
.features__description {
|
||||
font-size: 18px;
|
||||
letter-spacing: var(--ls-18);
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 1280px) {
|
||||
.features__grid {
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 1376px) {
|
||||
.features {
|
||||
padding-top: 120px;
|
||||
padding-bottom: 120px;
|
||||
}
|
||||
}
|
||||
131
src/styles/footer.css
Normal file
@@ -0,0 +1,131 @@
|
||||
.site-footer {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
padding: 60px 0;
|
||||
background: #fef0eb;
|
||||
}
|
||||
|
||||
.site-footer__inner {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
width: 100%;
|
||||
max-width: 1280px;
|
||||
gap: 36px;
|
||||
margin: 0 auto;
|
||||
padding: 0 16px;
|
||||
}
|
||||
|
||||
.site-footer__top {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
gap: 40px;
|
||||
}
|
||||
|
||||
.site-footer__brand {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
gap: 24px;
|
||||
overflow: clip;
|
||||
}
|
||||
|
||||
.site-footer__logo-frame {
|
||||
position: relative;
|
||||
width: 220px;
|
||||
height: 64px;
|
||||
}
|
||||
|
||||
.site-footer__logo {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
width: 100%;
|
||||
max-width: none;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.site-footer__description {
|
||||
max-width: 320px;
|
||||
margin: 0;
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
line-height: 1.5;
|
||||
letter-spacing: var(--ls-14);
|
||||
color: #7a726d;
|
||||
}
|
||||
|
||||
.site-footer__divider {
|
||||
width: 100%;
|
||||
height: 1px;
|
||||
background: #e3d9d1;
|
||||
}
|
||||
|
||||
.site-footer__bottom {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
gap: 12px;
|
||||
overflow: clip;
|
||||
}
|
||||
|
||||
.site-footer__legal {
|
||||
margin: 0;
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
line-height: normal;
|
||||
letter-spacing: var(--ls-14);
|
||||
color: #7a726d;
|
||||
}
|
||||
|
||||
.site-footer__spacer {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.site-footer__bottom {
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.site-footer__spacer {
|
||||
display: block;
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 1024px) {
|
||||
.site-footer {
|
||||
padding: 60px 0;
|
||||
}
|
||||
|
||||
.site-footer__inner {
|
||||
padding: 0 24px;
|
||||
}
|
||||
|
||||
.site-footer__top {
|
||||
flex-direction: row;
|
||||
align-items: flex-start;
|
||||
justify-content: space-between;
|
||||
gap: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 1280px) {
|
||||
.site-footer__inner {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.site-footer__legal {
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
@@ -1 +1,79 @@
|
||||
/* UnoCSS handles reset and utilities via astro.config.mjs injectReset */
|
||||
@import './header.css';
|
||||
@import './hero.css';
|
||||
@import './sections.css';
|
||||
@import './download.css';
|
||||
@import './footer.css';
|
||||
@import './why.css';
|
||||
@import './features.css';
|
||||
@import './trust.css';
|
||||
@import './preview.css';
|
||||
|
||||
body {
|
||||
font-family: "Inter", sans-serif;
|
||||
--ls-12: 0.01px;
|
||||
--ls-13: -0.04px;
|
||||
--ls-14: -0.09px;
|
||||
--ls-15: -0.13px;
|
||||
--ls-16: -0.18px;
|
||||
--ls-17: -0.22px;
|
||||
--ls-18: -0.26px;
|
||||
--ls-19: -0.30px;
|
||||
--ls-20: -0.33px;
|
||||
--ls-21: -0.37px;
|
||||
--ls-22: -0.40px;
|
||||
--ls-23: -0.44px;
|
||||
--ls-24: -0.47px;
|
||||
--ls-25: -0.50px;
|
||||
--ls-26: -0.53px;
|
||||
--ls-27: -0.56px;
|
||||
--ls-28: -0.59px;
|
||||
--ls-29: -0.61px;
|
||||
--ls-30: -0.64px;
|
||||
--ls-31: -0.67px;
|
||||
--ls-32: -0.69px;
|
||||
--ls-33: -0.72px;
|
||||
--ls-34: -0.74px;
|
||||
--ls-35: -0.77px;
|
||||
--ls-36: -0.79px;
|
||||
--ls-37: -0.81px;
|
||||
--ls-38: -0.84px;
|
||||
--ls-39: -0.86px;
|
||||
--ls-40: -0.89px;
|
||||
--ls-41: -0.91px;
|
||||
--ls-42: -0.93px;
|
||||
--ls-43: -0.95px;
|
||||
--ls-44: -0.98px;
|
||||
--ls-45: -1.00px;
|
||||
--ls-46: -1.02px;
|
||||
--ls-47: -1.05px;
|
||||
--ls-48: -1.07px;
|
||||
--ls-49: -1.09px;
|
||||
--ls-50: -1.11px;
|
||||
--ls-51: -1.14px;
|
||||
--ls-52: -1.16px;
|
||||
--ls-53: -1.18px;
|
||||
--ls-54: -1.20px;
|
||||
--ls-55: -1.23px;
|
||||
--ls-56: -1.25px;
|
||||
--ls-57: -1.27px;
|
||||
--ls-58: -1.29px;
|
||||
--ls-59: -1.32px;
|
||||
--ls-60: -1.34px;
|
||||
--ls-61: -1.36px;
|
||||
--ls-62: -1.38px;
|
||||
--ls-63: -1.40px;
|
||||
--ls-64: -1.43px;
|
||||
--ls-65: -1.45px;
|
||||
--ls-66: -1.47px;
|
||||
--ls-67: -1.49px;
|
||||
--ls-68: -1.52px;
|
||||
--ls-69: -1.54px;
|
||||
--ls-70: -1.56px;
|
||||
--ls-71: -1.58px;
|
||||
--ls-72: -1.61px;
|
||||
}
|
||||
|
||||
:target {
|
||||
scroll-margin-top: 72px;
|
||||
}
|
||||
|
||||
354
src/styles/header.css
Normal file
@@ -0,0 +1,354 @@
|
||||
.site-header {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 50;
|
||||
width: 100%;
|
||||
background: #fff;
|
||||
border-bottom: 1px solid #e3d9d1;
|
||||
}
|
||||
|
||||
.site-header__bar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
max-width: 1280px;
|
||||
height: 72px;
|
||||
gap: 24px;
|
||||
margin: 0 auto;
|
||||
padding: 0 16px;
|
||||
}
|
||||
|
||||
.site-header__brand,
|
||||
.site-header__actions {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.site-header__brand {
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.site-header__actions {
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.site-logo {
|
||||
position: relative;
|
||||
display: block;
|
||||
flex-shrink: 0;
|
||||
width: 143px;
|
||||
height: 42px;
|
||||
}
|
||||
|
||||
.site-logo__icon-frame {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 53px;
|
||||
height: 42px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.site-logo__icon {
|
||||
position: absolute;
|
||||
top: -13.49%;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
max-width: none;
|
||||
height: 126.98%;
|
||||
}
|
||||
|
||||
.site-logo__wordmark-frame {
|
||||
position: absolute;
|
||||
inset: 26.97% 0 7.92% 45.45%;
|
||||
}
|
||||
|
||||
.site-logo__wordmark {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
display: block;
|
||||
width: 100%;
|
||||
max-width: none;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.site-nav {
|
||||
display: none;
|
||||
align-items: center;
|
||||
flex-shrink: 0;
|
||||
gap: 32px;
|
||||
}
|
||||
|
||||
.site-nav__link {
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
line-height: normal;
|
||||
letter-spacing: var(--ls-14);
|
||||
color: #7a726d;
|
||||
text-decoration: none;
|
||||
white-space: nowrap;
|
||||
transition: color 160ms ease;
|
||||
}
|
||||
|
||||
.site-nav__link:hover,
|
||||
.site-nav__link.is-active {
|
||||
color: #f28a4b;
|
||||
}
|
||||
|
||||
.language-switcher {
|
||||
position: relative;
|
||||
display: none;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.language-switcher__button {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 43px;
|
||||
gap: 8px;
|
||||
padding: 0 16px 0 12px;
|
||||
background: #fff;
|
||||
border: 1px solid rgba(46, 42, 40, 0.3);
|
||||
border-radius: 17px;
|
||||
cursor: pointer;
|
||||
transition: border-color 160ms ease;
|
||||
}
|
||||
|
||||
.language-switcher__button:hover,
|
||||
.language-switcher__button[aria-expanded="true"] {
|
||||
border-color: #f28a4b;
|
||||
}
|
||||
|
||||
.language-switcher__icon {
|
||||
display: block;
|
||||
width: 26px;
|
||||
height: 26px;
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
.language-switcher__current {
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
line-height: 14px;
|
||||
letter-spacing: var(--ls-14);
|
||||
color: #2e2a28;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.language-switcher__menu {
|
||||
position: absolute;
|
||||
top: 52px;
|
||||
right: 0;
|
||||
z-index: 60;
|
||||
width: 240px;
|
||||
max-height: min(520px, calc(100vh - 96px));
|
||||
overflow-y: auto;
|
||||
background: #fff;
|
||||
border: 1px solid #e3d9d1;
|
||||
border-radius: 18px;
|
||||
box-shadow: 0 16px 40px rgba(46, 42, 40, 0.16);
|
||||
}
|
||||
|
||||
.language-switcher__menu.is-hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.language-switcher__option {
|
||||
display: block;
|
||||
padding: 14px 24px;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
line-height: normal;
|
||||
letter-spacing: var(--ls-14);
|
||||
color: #7a726d;
|
||||
text-decoration: none;
|
||||
transition:
|
||||
background-color 160ms ease,
|
||||
color 160ms ease;
|
||||
}
|
||||
|
||||
.language-switcher__option:hover {
|
||||
color: #f28a4b;
|
||||
background: #fef0eb;
|
||||
}
|
||||
|
||||
.language-switcher__option.is-active {
|
||||
color: #2e2a28;
|
||||
background: #f8f3ee;
|
||||
}
|
||||
|
||||
.site-header__download {
|
||||
display: none;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-shrink: 0;
|
||||
padding: 13px 22px;
|
||||
background: #f28a4b;
|
||||
border-radius: 17px;
|
||||
text-decoration: none;
|
||||
transition: background-color 160ms ease;
|
||||
}
|
||||
|
||||
.site-header__download:hover {
|
||||
background: #e07a3b;
|
||||
}
|
||||
|
||||
.site-header__download-label {
|
||||
font-size: 14px;
|
||||
font-weight: 700;
|
||||
line-height: normal;
|
||||
letter-spacing: var(--ls-14);
|
||||
color: #fff;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.menu-toggle {
|
||||
display: flex;
|
||||
flex-shrink: 0;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
gap: 5px;
|
||||
padding: 8px;
|
||||
background: transparent;
|
||||
border: 0;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.menu-toggle__bar {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 2px;
|
||||
background: #2e2a28;
|
||||
border-radius: 2px;
|
||||
transform-origin: center;
|
||||
transition:
|
||||
transform 240ms ease,
|
||||
opacity 240ms ease;
|
||||
}
|
||||
|
||||
.mobile-nav {
|
||||
max-height: calc(100vh - 72px);
|
||||
overflow-y: auto;
|
||||
background: #fff;
|
||||
border-top: 1px solid #e3d9d1;
|
||||
}
|
||||
|
||||
.mobile-nav.is-hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.mobile-nav__list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 16px 24px;
|
||||
margin: 0;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.mobile-nav__link {
|
||||
display: block;
|
||||
padding: 16px 0;
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
letter-spacing: var(--ls-16);
|
||||
color: #2e2a28;
|
||||
text-decoration: none;
|
||||
border-bottom: 1px solid #e3d9d1;
|
||||
transition: color 160ms ease;
|
||||
}
|
||||
|
||||
.mobile-nav__link.is-active {
|
||||
color: #f28a4b;
|
||||
}
|
||||
|
||||
.mobile-nav__link--last {
|
||||
border-bottom: 0;
|
||||
}
|
||||
|
||||
.mobile-nav__languages {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
align-items: center;
|
||||
gap: 12px 16px;
|
||||
padding: 16px 0;
|
||||
border-top: 1px solid #e3d9d1;
|
||||
}
|
||||
|
||||
.mobile-nav__language-link {
|
||||
min-width: 0;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
line-height: 1.25;
|
||||
letter-spacing: var(--ls-14);
|
||||
color: #2e2a28;
|
||||
text-align: center;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.mobile-nav__language-link.is-active {
|
||||
color: #f28a4b;
|
||||
}
|
||||
|
||||
@media (max-width: 500px) {
|
||||
.mobile-nav__languages {
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 501px) and (max-width: 1024px) {
|
||||
.mobile-nav__languages {
|
||||
grid-template-columns: repeat(5, minmax(0, 1fr));
|
||||
}
|
||||
}
|
||||
|
||||
.mobile-nav__download-item {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
margin: 16px 0 8px;
|
||||
}
|
||||
|
||||
.mobile-nav__download {
|
||||
display: block;
|
||||
width: 250px;
|
||||
max-width: 100%;
|
||||
padding: 14px 24px;
|
||||
font-size: 15px;
|
||||
font-weight: 700;
|
||||
letter-spacing: var(--ls-15);
|
||||
color: #fff;
|
||||
text-align: center;
|
||||
text-decoration: none;
|
||||
background: #f28a4b;
|
||||
border-radius: 17px;
|
||||
transition: background-color 160ms ease;
|
||||
}
|
||||
|
||||
.mobile-nav__download:hover {
|
||||
background: #e07a3b;
|
||||
}
|
||||
|
||||
@media (min-width: 1024px) {
|
||||
.site-header__bar {
|
||||
padding: 0 24px;
|
||||
}
|
||||
|
||||
.site-nav,
|
||||
.language-switcher,
|
||||
.site-header__download {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.menu-toggle {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.mobile-nav {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
382
src/styles/hero.css
Normal file
@@ -0,0 +1,382 @@
|
||||
.hero {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
min-height: 600px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.hero__bg {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
width: 100%;
|
||||
max-width: none;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.hero__inner {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
width: 100%;
|
||||
max-width: 1280px;
|
||||
height: 100%;
|
||||
gap: 40px;
|
||||
margin: 0 auto;
|
||||
padding: 0 16px;
|
||||
}
|
||||
|
||||
.hero__phone-column {
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
padding-top: 24px;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.hero__phone-frame {
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
width: min(320px, 100%);
|
||||
height: 527px;
|
||||
}
|
||||
|
||||
.hero__phone-crop {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
overflow: hidden;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.hero__phone {
|
||||
position: absolute;
|
||||
top: -7.18%;
|
||||
left: -2.67%;
|
||||
width: 105.35%;
|
||||
max-width: none;
|
||||
height: 114.36%;
|
||||
}
|
||||
|
||||
.hero__content {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
justify-content: center;
|
||||
flex-shrink: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
gap: 60px;
|
||||
padding: 32px 0;
|
||||
overflow: clip;
|
||||
}
|
||||
|
||||
.hero__badge {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-shrink: 0;
|
||||
padding: 12px 16px;
|
||||
overflow: clip;
|
||||
background: #fff;
|
||||
border: 1px solid #f08458;
|
||||
border-radius: 999px;
|
||||
}
|
||||
|
||||
.hero__badge-text {
|
||||
margin: 0;
|
||||
font-size: 14px;
|
||||
font-weight: 700;
|
||||
line-height: normal;
|
||||
letter-spacing: var(--ls-14);
|
||||
color: #0d0d0d;
|
||||
}
|
||||
|
||||
.hero__copy {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
flex-shrink: 0;
|
||||
width: 100%;
|
||||
gap: 24px;
|
||||
}
|
||||
|
||||
.hero__title {
|
||||
width: 100%;
|
||||
font-size: 36px;
|
||||
font-weight: 700;
|
||||
line-height: 1.1;
|
||||
letter-spacing: var(--ls-36);
|
||||
color: #2e2a28;
|
||||
}
|
||||
|
||||
.hero__title-line {
|
||||
margin: 0;
|
||||
line-height: 1.1;
|
||||
}
|
||||
|
||||
.hero__description {
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
font-size: 15px;
|
||||
font-weight: 400;
|
||||
line-height: 1.5;
|
||||
letter-spacing: var(--ls-15);
|
||||
color: #7a726d;
|
||||
}
|
||||
|
||||
.hero__actions {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
flex-shrink: 0;
|
||||
width: 100%;
|
||||
gap: 14px;
|
||||
overflow: clip;
|
||||
}
|
||||
|
||||
.hero__store-badge {
|
||||
text-decoration: none;
|
||||
transition:
|
||||
transform 160ms ease,
|
||||
filter 160ms ease;
|
||||
}
|
||||
|
||||
.hero__store-badge:hover {
|
||||
filter: brightness(0.96);
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
.hero__tags {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
flex-shrink: 0;
|
||||
gap: 10px;
|
||||
overflow: clip;
|
||||
}
|
||||
|
||||
.hero__tag {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-shrink: 0;
|
||||
padding: 9px 14px;
|
||||
overflow: clip;
|
||||
background: rgba(255, 255, 255, 0.68);
|
||||
border: 1px solid #fff;
|
||||
border-radius: 999px;
|
||||
}
|
||||
|
||||
.hero__tag-text {
|
||||
font-size: 14px;
|
||||
font-weight: 700;
|
||||
line-height: normal;
|
||||
letter-spacing: var(--ls-14);
|
||||
color: #7a726d;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
@media (max-width: 433px) {
|
||||
.hero__store-badge {
|
||||
min-width: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 1024px) {
|
||||
.hero__actions {
|
||||
flex-wrap: nowrap;
|
||||
}
|
||||
|
||||
.hero__store-badge {
|
||||
flex: 1 1 calc((100% - 14px) / 2);
|
||||
width: calc((100% - 14px) / 2);
|
||||
min-width: 0;
|
||||
padding-right: 10px;
|
||||
padding-left: 10px;
|
||||
}
|
||||
|
||||
.hero__store-badge .store-badge__copy,
|
||||
.hero__store-badge .store-badge__label {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 540px) {
|
||||
.hero__actions {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.hero__store-badge {
|
||||
flex-basis: auto;
|
||||
width: min(100%, calc(100vw - 32px));
|
||||
}
|
||||
|
||||
.hero__tags {
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 440px) {
|
||||
.hero__title {
|
||||
font-size: 42px;
|
||||
letter-spacing: var(--ls-42);
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 576px) {
|
||||
.hero__title {
|
||||
font-size: 48px;
|
||||
letter-spacing: var(--ls-48);
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.hero__title {
|
||||
font-size: 56px;
|
||||
letter-spacing: var(--ls-56);
|
||||
}
|
||||
|
||||
.hero__phone-column {
|
||||
padding-top: 60px;
|
||||
}
|
||||
|
||||
.hero__phone-frame {
|
||||
width: min(580px, 100%);
|
||||
height: auto;
|
||||
aspect-ratio: 116 / 191;
|
||||
}
|
||||
|
||||
.hero__phone-crop {
|
||||
position: relative;
|
||||
inset: auto;
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
.hero__phone {
|
||||
position: static;
|
||||
width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 1024px) {
|
||||
.hero {
|
||||
height: 891px;
|
||||
}
|
||||
|
||||
.hero__inner {
|
||||
flex-direction: row;
|
||||
padding: 0 24px;
|
||||
}
|
||||
|
||||
.hero__phone-column {
|
||||
position: relative;
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.hero__phone-frame {
|
||||
position: absolute;
|
||||
top: 60px;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.hero__phone-crop {
|
||||
position: relative;
|
||||
inset: auto;
|
||||
height: 100%;
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
.hero__phone {
|
||||
position: static;
|
||||
width: auto;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.hero__actions {
|
||||
flex-wrap: nowrap;
|
||||
}
|
||||
|
||||
.hero__store-badge {
|
||||
flex: 0 1 auto;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.hero__content {
|
||||
width: clamp(560px, 44vw, 600px);
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 1200px) {
|
||||
.hero__title {
|
||||
font-size: 64px;
|
||||
letter-spacing: var(--ls-64);
|
||||
}
|
||||
|
||||
.hero__description {
|
||||
font-size: 18px;
|
||||
letter-spacing: var(--ls-18);
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 1280px) {
|
||||
.hero__inner {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.hero__phone-column {
|
||||
align-items: flex-start;
|
||||
padding-top: 60px;
|
||||
}
|
||||
|
||||
.hero__phone-frame {
|
||||
position: static;
|
||||
top: auto;
|
||||
right: auto;
|
||||
bottom: auto;
|
||||
overflow: visible;
|
||||
flex: 1 0 0;
|
||||
width: auto;
|
||||
max-width: calc(955px * 116 / 191);
|
||||
height: 955px;
|
||||
aspect-ratio: 116 / 191;
|
||||
}
|
||||
|
||||
.hero__phone-crop {
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.hero__phone {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.hero__content {
|
||||
width: clamp(560px, 52vw, 660px);
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 1376px) {
|
||||
.hero__title {
|
||||
font-size: 72px;
|
||||
letter-spacing: var(--ls-72);
|
||||
}
|
||||
}
|
||||
243
src/styles/preview.css
Normal file
@@ -0,0 +1,243 @@
|
||||
.app-preview {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
gap: 60px;
|
||||
padding: 64px 16px 0;
|
||||
overflow: hidden;
|
||||
background: #fef0eb;
|
||||
}
|
||||
|
||||
.app-preview__header {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
width: 100%;
|
||||
max-width: 940px;
|
||||
gap: 40px;
|
||||
margin: 0 auto;
|
||||
overflow: clip;
|
||||
}
|
||||
|
||||
.app-preview__title {
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
font-size: 32px;
|
||||
font-weight: 700;
|
||||
line-height: 1.2;
|
||||
color: #1a1a1a;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.app-preview__description {
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
font-size: 15px;
|
||||
font-weight: 400;
|
||||
line-height: 1.5;
|
||||
letter-spacing: var(--ls-15);
|
||||
color: #7a726d;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.app-preview__carousel {
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
justify-content: center;
|
||||
flex-shrink: 0;
|
||||
width: 100%;
|
||||
gap: 24px;
|
||||
padding: 0 16px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.app-preview__side-phone {
|
||||
position: relative;
|
||||
display: none;
|
||||
flex-shrink: 0;
|
||||
width: 336px;
|
||||
height: 396px;
|
||||
overflow: hidden;
|
||||
opacity: 0.2;
|
||||
pointer-events: none;
|
||||
transition: opacity 300ms ease;
|
||||
}
|
||||
|
||||
.app-preview__phone-image {
|
||||
position: absolute;
|
||||
top: -0.03%;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
max-width: none;
|
||||
height: 175.34%;
|
||||
transition: opacity 300ms ease;
|
||||
}
|
||||
|
||||
.app-preview__control-wrap {
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
align-self: stretch;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.app-preview__control-inner {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.app-preview__button {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-shrink: 0;
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
background: #f08458;
|
||||
border: 0;
|
||||
border-radius: 9999px;
|
||||
cursor: pointer;
|
||||
transition:
|
||||
background-color 160ms ease,
|
||||
transform 160ms ease;
|
||||
}
|
||||
|
||||
.app-preview__button:hover {
|
||||
background: #e07a3b;
|
||||
}
|
||||
|
||||
.app-preview__button:active {
|
||||
transform: scale(0.95);
|
||||
}
|
||||
|
||||
.app-preview__button-icon {
|
||||
display: block;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
}
|
||||
|
||||
.app-preview__button-icon--next {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
|
||||
.app-preview__center-phone {
|
||||
position: relative;
|
||||
flex-shrink: 0;
|
||||
width: min(420px, calc(100vw - 168px));
|
||||
aspect-ratio: 459 / 542;
|
||||
overflow: hidden;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
@media (max-width: 594px) {
|
||||
.app-preview__carousel {
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.app-preview__button {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
}
|
||||
|
||||
.app-preview__button-icon {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
}
|
||||
|
||||
.app-preview__center-phone {
|
||||
width: min(420px, calc(100vw - 120px));
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 554px) {
|
||||
.app-preview__carousel {
|
||||
gap: 8px;
|
||||
padding: 0 12px;
|
||||
}
|
||||
|
||||
.app-preview__button {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
}
|
||||
|
||||
.app-preview__button-icon {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
}
|
||||
|
||||
.app-preview__center-phone {
|
||||
width: min(420px, calc(100vw - 104px));
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 540px) {
|
||||
.app-preview__center-phone {
|
||||
width: min(390px, calc(100vw - 120px));
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.app-preview__title {
|
||||
font-size: 40px;
|
||||
}
|
||||
|
||||
.app-preview__carousel {
|
||||
gap: clamp(10px, 1.4vw, 16px);
|
||||
padding: 0 clamp(16px, 3vw, 32px);
|
||||
}
|
||||
|
||||
.app-preview__side-phone,
|
||||
.app-preview__control-wrap {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.app-preview__side-phone {
|
||||
display: block;
|
||||
width: clamp(128px, 17vw, 190px);
|
||||
height: auto;
|
||||
aspect-ratio: 336 / 396;
|
||||
}
|
||||
|
||||
.app-preview__center-phone {
|
||||
width: clamp(220px, 31vw, 320px);
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 1024px) {
|
||||
.app-preview {
|
||||
padding: 120px clamp(32px, 6vw, 130px) 0;
|
||||
}
|
||||
|
||||
.app-preview__title {
|
||||
font-size: 48px;
|
||||
}
|
||||
|
||||
.app-preview__description {
|
||||
font-size: 18px;
|
||||
letter-spacing: var(--ls-18);
|
||||
}
|
||||
|
||||
.app-preview__side-phone,
|
||||
.app-preview__control-wrap {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.app-preview__carousel {
|
||||
gap: clamp(16px, 1.4vw, 20px);
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.app-preview__side-phone {
|
||||
display: block;
|
||||
width: clamp(190px, 20vw, 336px);
|
||||
}
|
||||
|
||||
.app-preview__center-phone {
|
||||
width: clamp(300px, 27vw, 459px);
|
||||
height: auto;
|
||||
aspect-ratio: 459 / 542;
|
||||
}
|
||||
}
|
||||
488
src/styles/sections.css
Normal file
@@ -0,0 +1,488 @@
|
||||
.experience {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
padding: 60px 16px;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.experience__inner {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
max-width: 1008px;
|
||||
gap: 40px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.experience__heading {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
gap: 24px;
|
||||
}
|
||||
|
||||
.experience__title {
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
font-size: 28px;
|
||||
font-weight: 700;
|
||||
line-height: 1.2;
|
||||
letter-spacing: var(--ls-28);
|
||||
color: #1a1a1a;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.experience__body {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.experience__grid {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 1fr);
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
gap: 24px;
|
||||
}
|
||||
|
||||
.experience-card {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
gap: 36px;
|
||||
padding: 0 0 36px;
|
||||
overflow: hidden;
|
||||
background: linear-gradient(to bottom, #fef0eb, #fff);
|
||||
border-radius: 30px 30px 0 0;
|
||||
}
|
||||
|
||||
.experience-card__media {
|
||||
position: relative;
|
||||
flex-shrink: 0;
|
||||
width: 100%;
|
||||
aspect-ratio: 320 / 232;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.experience-card__media--tinted {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
background: #ffeddf;
|
||||
}
|
||||
|
||||
.experience-card__media-crop {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
overflow: hidden;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.experience-card__image {
|
||||
position: absolute;
|
||||
max-width: none;
|
||||
}
|
||||
|
||||
.experience-card__image--one {
|
||||
top: -86.5%;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 298.5%;
|
||||
}
|
||||
|
||||
.experience-card__image--two {
|
||||
top: -137.86%;
|
||||
left: -3.13%;
|
||||
width: 130.62%;
|
||||
height: 389.91%;
|
||||
}
|
||||
|
||||
.experience-card__image--three {
|
||||
top: -115.66%;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 298.5%;
|
||||
}
|
||||
|
||||
.experience-card__copy {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
width: 100%;
|
||||
gap: 16px;
|
||||
max-width: 248px;
|
||||
padding: 0;
|
||||
overflow: clip;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.experience-card__title {
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
font-size: 24px;
|
||||
font-weight: 600;
|
||||
line-height: 1.4;
|
||||
letter-spacing: var(--ls-24);
|
||||
color: #0d0d0d;
|
||||
}
|
||||
|
||||
.experience-card__description {
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
line-height: 1.5;
|
||||
letter-spacing: var(--ls-16);
|
||||
color: #7a726d;
|
||||
}
|
||||
|
||||
.experience__caption {
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
font-size: 15px;
|
||||
font-weight: 400;
|
||||
line-height: 1.5;
|
||||
letter-spacing: var(--ls-15);
|
||||
color: #7a726d;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.use-cases {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
padding: 60px 16px;
|
||||
background: #fef0eb;
|
||||
}
|
||||
|
||||
.use-cases__inner {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 1fr);
|
||||
width: 100%;
|
||||
max-width: 1280px;
|
||||
gap: 32px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.use-cases__copy {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
width: 100%;
|
||||
gap: 24px;
|
||||
overflow: clip;
|
||||
}
|
||||
|
||||
@media (max-width: 1294px) {
|
||||
.use-cases__inner {
|
||||
grid-template-columns: minmax(0, 1128px);
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.use-cases__copy {
|
||||
justify-self: start;
|
||||
width: min(580px, 100%);
|
||||
max-width: 580px;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.use-cases__title,
|
||||
.use-cases__description {
|
||||
max-width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.section-eyebrow {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-shrink: 0;
|
||||
padding: 12px 24px;
|
||||
background: #fff;
|
||||
border: 1px solid #fbbfa3;
|
||||
border-radius: 9999px;
|
||||
}
|
||||
|
||||
.section-eyebrow__text {
|
||||
font-size: 14px;
|
||||
font-weight: 700;
|
||||
line-height: normal;
|
||||
letter-spacing: var(--ls-14);
|
||||
color: #f08458;
|
||||
text-align: center;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.use-cases__title {
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
font-size: 28px;
|
||||
font-weight: 700;
|
||||
line-height: 1.2;
|
||||
letter-spacing: var(--ls-28);
|
||||
color: #1a1a1a;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.use-cases__description {
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
font-size: 15px;
|
||||
font-weight: 400;
|
||||
line-height: 1.5;
|
||||
letter-spacing: var(--ls-15);
|
||||
color: #7a726d;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.use-cases__copy {
|
||||
align-items: flex-start;
|
||||
justify-self: stretch;
|
||||
width: 100%;
|
||||
max-width: none;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.use-cases__title,
|
||||
.use-cases__description {
|
||||
text-align: left;
|
||||
}
|
||||
}
|
||||
|
||||
.use-cases__rows {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
width: 100%;
|
||||
max-width: 1128px;
|
||||
min-width: 0;
|
||||
margin: 0 auto;
|
||||
overflow: hidden;
|
||||
border-radius: 30px;
|
||||
gap: 1px;
|
||||
}
|
||||
|
||||
.use-case-row {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 1fr);
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
background: #faede8;
|
||||
}
|
||||
|
||||
.use-case-row__title-cell,
|
||||
.use-case-row__description-cell {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
min-width: 0;
|
||||
padding: 16px 24px;
|
||||
}
|
||||
|
||||
.use-case-row__title-cell {
|
||||
background: #f08458;
|
||||
}
|
||||
|
||||
.use-case-row__description-cell {
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.use-case-row__title {
|
||||
min-width: 0;
|
||||
margin: 0;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
line-height: normal;
|
||||
letter-spacing: var(--ls-18);
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.use-case-row__description {
|
||||
min-width: 0;
|
||||
margin: 0;
|
||||
font-size: 15px;
|
||||
font-weight: 500;
|
||||
line-height: 1.5;
|
||||
letter-spacing: var(--ls-15);
|
||||
color: #7a726d;
|
||||
}
|
||||
|
||||
@media (max-width: 578px) {
|
||||
.use-cases__rows {
|
||||
gap: 24px;
|
||||
overflow: visible;
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
.use-case-row {
|
||||
overflow: hidden;
|
||||
background: #fff;
|
||||
border-radius: 16px;
|
||||
}
|
||||
|
||||
.use-case-row__title-cell,
|
||||
.use-case-row__description-cell {
|
||||
padding: 24px;
|
||||
}
|
||||
|
||||
.use-case-row__title {
|
||||
font-size: 20px;
|
||||
letter-spacing: var(--ls-20);
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 440px) {
|
||||
.experience,
|
||||
.use-cases {
|
||||
padding-left: 20px;
|
||||
padding-right: 20px;
|
||||
}
|
||||
|
||||
.experience__title,
|
||||
.use-cases__title {
|
||||
font-size: 32px;
|
||||
letter-spacing: var(--ls-32);
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 576px) {
|
||||
.experience,
|
||||
.use-cases {
|
||||
padding-left: 24px;
|
||||
padding-right: 24px;
|
||||
}
|
||||
|
||||
.experience__title,
|
||||
.use-cases__title {
|
||||
font-size: 36px;
|
||||
letter-spacing: var(--ls-36);
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 578px) {
|
||||
.experience__grid {
|
||||
grid-template-columns: repeat(2, minmax(0, 320px));
|
||||
}
|
||||
|
||||
.experience-card {
|
||||
max-width: 320px;
|
||||
justify-self: center;
|
||||
}
|
||||
|
||||
.experience-card:nth-child(3):last-child {
|
||||
grid-column: 1 / -1;
|
||||
justify-self: center;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 578px) {
|
||||
.use-case-row {
|
||||
grid-template-columns: minmax(220px, 300px) minmax(280px, 1fr);
|
||||
height: 120px;
|
||||
}
|
||||
|
||||
.use-case-row__title-cell,
|
||||
.use-case-row__description-cell {
|
||||
padding: 24px 36px;
|
||||
}
|
||||
|
||||
.use-case-row__title {
|
||||
font-size: 20px;
|
||||
letter-spacing: var(--ls-20);
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.experience,
|
||||
.use-cases {
|
||||
padding-left: 36px;
|
||||
padding-right: 36px;
|
||||
}
|
||||
|
||||
.experience__title,
|
||||
.use-cases__title {
|
||||
font-size: 42px;
|
||||
letter-spacing: var(--ls-42);
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 1200px) {
|
||||
.experience__grid {
|
||||
grid-template-columns: repeat(3, minmax(0, 320px));
|
||||
}
|
||||
|
||||
.experience-card:nth-child(3):last-child {
|
||||
grid-column: auto;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 576px) {
|
||||
.experience-card__title {
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 1024px) {
|
||||
.experience {
|
||||
padding-top: 60px;
|
||||
padding-bottom: 60px;
|
||||
padding-left: 36px;
|
||||
padding-right: 36px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 1200px) {
|
||||
.experience__title,
|
||||
.use-cases__title {
|
||||
font-size: 48px;
|
||||
letter-spacing: var(--ls-48);
|
||||
}
|
||||
|
||||
.experience__caption,
|
||||
.use-cases__description {
|
||||
font-size: 18px;
|
||||
letter-spacing: var(--ls-18);
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 1201px) {
|
||||
.use-cases {
|
||||
padding: 60px 64px;
|
||||
}
|
||||
|
||||
.use-cases__inner {
|
||||
grid-template-columns: minmax(420px, 540px) minmax(560px, 1fr);
|
||||
align-items: center;
|
||||
gap: 40px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 1376px) {
|
||||
.experience {
|
||||
padding-top: 120px;
|
||||
padding-bottom: 120px;
|
||||
padding-left: 0;
|
||||
padding-right: 0;
|
||||
}
|
||||
|
||||
.use-cases {
|
||||
padding-top: 120px;
|
||||
padding-bottom: 120px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 1440px) {
|
||||
.use-cases {
|
||||
padding-right: 130px;
|
||||
padding-left: 130px;
|
||||
}
|
||||
}
|
||||
328
src/styles/trust.css
Normal file
@@ -0,0 +1,328 @@
|
||||
.trust {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
padding: 60px 16px;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.trust__inner {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
width: 100%;
|
||||
max-width: 1280px;
|
||||
gap: 40px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.trust__header {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
width: 100%;
|
||||
gap: 24px;
|
||||
overflow: clip;
|
||||
}
|
||||
|
||||
.trust__title {
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
font-size: 28px;
|
||||
font-weight: 700;
|
||||
line-height: 1.2;
|
||||
letter-spacing: var(--ls-28);
|
||||
color: #1a1a1a;
|
||||
}
|
||||
|
||||
.trust__description {
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
font-size: 15px;
|
||||
font-weight: 400;
|
||||
line-height: 1.5;
|
||||
letter-spacing: var(--ls-15);
|
||||
color: #7a726d;
|
||||
}
|
||||
|
||||
.trust__grid {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 1fr);
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
gap: 32px;
|
||||
}
|
||||
|
||||
.trust-card {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
min-width: 0;
|
||||
gap: 16px;
|
||||
padding: 24px;
|
||||
border-radius: 30px;
|
||||
}
|
||||
|
||||
.trust-card__icon-frame {
|
||||
position: relative;
|
||||
flex-shrink: 0;
|
||||
width: 128px;
|
||||
height: 128px;
|
||||
}
|
||||
|
||||
.trust-card__icon-crop {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
overflow: hidden;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.trust-card__icon {
|
||||
position: absolute;
|
||||
max-width: none;
|
||||
}
|
||||
|
||||
.trust-card__icon--one {
|
||||
top: 7.04%;
|
||||
left: -31.48%;
|
||||
width: 312.73%;
|
||||
height: 174.55%;
|
||||
}
|
||||
|
||||
.trust-card__icon--two {
|
||||
top: 3.1%;
|
||||
left: -164.72%;
|
||||
width: 335.61%;
|
||||
height: 187.32%;
|
||||
}
|
||||
|
||||
.trust-card__icon--three {
|
||||
top: -105.62%;
|
||||
left: -187.93%;
|
||||
width: 378.86%;
|
||||
height: 211.46%;
|
||||
}
|
||||
|
||||
.trust-card__icon--four {
|
||||
top: 0;
|
||||
left: 4.14%;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.trust-card__copy {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
width: 100%;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.trust-card__title {
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
line-height: 22px;
|
||||
letter-spacing: var(--ls-16);
|
||||
color: #0d0d0d;
|
||||
}
|
||||
|
||||
.trust-card__description {
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
font-size: 15px;
|
||||
font-weight: 500;
|
||||
line-height: 1.5;
|
||||
letter-spacing: var(--ls-15);
|
||||
color: #7a726d;
|
||||
}
|
||||
|
||||
.trust__divider {
|
||||
position: relative;
|
||||
display: none; /* shown only in desktop flex row via 1023px breakpoint */
|
||||
flex-shrink: 0;
|
||||
width: 0;
|
||||
height: 118px;
|
||||
}
|
||||
|
||||
.trust__divider-frame {
|
||||
position: absolute;
|
||||
inset: 0 -0.5px;
|
||||
}
|
||||
|
||||
.trust__divider-image {
|
||||
display: block;
|
||||
width: 100%;
|
||||
max-width: none;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
@media (max-width: 1023px) {
|
||||
.trust-card__copy {
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.trust-card__title,
|
||||
.trust-card__description {
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 440px) {
|
||||
.trust {
|
||||
padding-left: 20px;
|
||||
padding-right: 20px;
|
||||
}
|
||||
|
||||
.trust__title {
|
||||
font-size: 32px;
|
||||
letter-spacing: var(--ls-32);
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 576px) {
|
||||
.trust {
|
||||
padding-left: 24px;
|
||||
padding-right: 24px;
|
||||
}
|
||||
|
||||
.trust__title {
|
||||
font-size: 36px;
|
||||
letter-spacing: var(--ls-36);
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.trust {
|
||||
padding-left: 36px;
|
||||
padding-right: 36px;
|
||||
}
|
||||
|
||||
.trust__title {
|
||||
font-size: 42px;
|
||||
letter-spacing: var(--ls-42);
|
||||
}
|
||||
|
||||
.trust__grid {
|
||||
position: relative;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 768px) and (max-width: 1023px) {
|
||||
.trust__grid > .trust-card:nth-child(1),
|
||||
.trust__grid > .trust-card:nth-child(5) {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.trust__grid > .trust-card:nth-child(1)::after,
|
||||
.trust__grid > .trust-card:nth-child(5)::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
right: -16px;
|
||||
transform: translateY(-50%);
|
||||
width: 0;
|
||||
height: 118px;
|
||||
border-left: 1px solid rgba(240, 132, 88, 0.5);
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 1024px) {
|
||||
.trust {
|
||||
padding-top: 60px;
|
||||
padding-bottom: 60px;
|
||||
padding-left: 28px;
|
||||
padding-right: 28px;
|
||||
}
|
||||
|
||||
.trust__grid {
|
||||
display: flex;
|
||||
gap: 14px;
|
||||
}
|
||||
|
||||
.trust-card {
|
||||
flex: 1;
|
||||
padding: 16px 8px;
|
||||
}
|
||||
|
||||
.trust-card__icon-frame {
|
||||
width: 112px;
|
||||
height: 112px;
|
||||
}
|
||||
|
||||
.trust-card__copy {
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.trust-card__title {
|
||||
font-size: 15px;
|
||||
line-height: 20px;
|
||||
letter-spacing: var(--ls-15);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.trust-card__description {
|
||||
font-size: 14px;
|
||||
line-height: 1.45;
|
||||
letter-spacing: var(--ls-14);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.trust__divider {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 1200px) {
|
||||
.trust__title {
|
||||
font-size: 48px;
|
||||
letter-spacing: var(--ls-48);
|
||||
}
|
||||
|
||||
.trust__description {
|
||||
font-size: 18px;
|
||||
letter-spacing: var(--ls-18);
|
||||
}
|
||||
|
||||
.trust__grid {
|
||||
gap: 24px;
|
||||
}
|
||||
|
||||
.trust-card {
|
||||
padding: 24px;
|
||||
}
|
||||
|
||||
.trust-card__icon-frame {
|
||||
width: 128px;
|
||||
height: 128px;
|
||||
}
|
||||
|
||||
.trust-card__title {
|
||||
font-size: 16px;
|
||||
line-height: 22px;
|
||||
letter-spacing: var(--ls-16);
|
||||
}
|
||||
|
||||
.trust-card__description {
|
||||
font-size: 15px;
|
||||
line-height: 1.5;
|
||||
letter-spacing: var(--ls-15);
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 1376px) {
|
||||
.trust {
|
||||
padding-top: 120px;
|
||||
padding-bottom: 120px;
|
||||
padding-left: 120px;
|
||||
padding-right: 120px;
|
||||
}
|
||||
}
|
||||
317
src/styles/why.css
Normal file
@@ -0,0 +1,317 @@
|
||||
.why {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
padding: 60px 16px;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.why__inner {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
width: 100%;
|
||||
max-width: 1280px;
|
||||
gap: 40px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.why__intro {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
width: 100%;
|
||||
gap: 36px;
|
||||
overflow: clip;
|
||||
}
|
||||
|
||||
.why__copy {
|
||||
display: flex;
|
||||
order: 2;
|
||||
flex: 1;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
min-width: 0;
|
||||
gap: 36px;
|
||||
}
|
||||
|
||||
.why__text {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
width: 100%;
|
||||
gap: 24px;
|
||||
}
|
||||
|
||||
.why__title {
|
||||
width: 100%;
|
||||
font-size: 28px;
|
||||
font-weight: 700;
|
||||
letter-spacing: var(--ls-28);
|
||||
color: #1a1a1a;
|
||||
}
|
||||
|
||||
.why__title-line {
|
||||
margin: 0;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
.why__underline {
|
||||
position: relative;
|
||||
flex-shrink: 0;
|
||||
width: 295.5px;
|
||||
height: 0;
|
||||
}
|
||||
|
||||
.why__underline-frame {
|
||||
position: absolute;
|
||||
inset: -0.5px 0;
|
||||
}
|
||||
|
||||
.why__underline-image {
|
||||
display: block;
|
||||
width: 100%;
|
||||
max-width: none;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.why__description {
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
font-size: 15px;
|
||||
font-weight: 400;
|
||||
line-height: 1.5;
|
||||
letter-spacing: var(--ls-15);
|
||||
color: #7a726d;
|
||||
}
|
||||
|
||||
@media (min-width: 1200px) {
|
||||
.why__description {
|
||||
font-size: 18px;
|
||||
letter-spacing: var(--ls-18);
|
||||
}
|
||||
}
|
||||
|
||||
.why__illustration {
|
||||
position: relative;
|
||||
display: block;
|
||||
order: 1;
|
||||
align-self: center;
|
||||
flex-shrink: 0;
|
||||
width: 480px;
|
||||
max-width: calc(100vw - 32px);
|
||||
height: 480px;
|
||||
max-height: calc(100vw - 32px);
|
||||
}
|
||||
|
||||
.why__illustration-image {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
.why__grid {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 1fr);
|
||||
width: 100%;
|
||||
gap: 24px;
|
||||
}
|
||||
|
||||
.why-card {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
min-width: 0;
|
||||
gap: 20px;
|
||||
padding: 20px 16px;
|
||||
overflow: clip;
|
||||
background: #fef0eb;
|
||||
border: 1px solid #e8e4de;
|
||||
border-radius: 30px;
|
||||
}
|
||||
|
||||
.why-card__icon-frame {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-shrink: 0;
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
overflow: clip;
|
||||
background: #f08458;
|
||||
border-radius: 9999px;
|
||||
}
|
||||
|
||||
.why-card__icon {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.why-card__icon--simple {
|
||||
width: 33.001px;
|
||||
height: 32.973px;
|
||||
}
|
||||
|
||||
.why-card__icon--familiar {
|
||||
width: 28.5px;
|
||||
height: 30px;
|
||||
}
|
||||
|
||||
.why-card__icon--connected {
|
||||
width: 33px;
|
||||
height: 32.936px;
|
||||
}
|
||||
|
||||
.why-card__icon--modern {
|
||||
width: 18.125px;
|
||||
height: 33px;
|
||||
}
|
||||
|
||||
.why-card__copy {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
min-width: 0;
|
||||
gap: 12px;
|
||||
overflow: clip;
|
||||
}
|
||||
|
||||
.why-card__title {
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
font-size: 20px;
|
||||
font-weight: 600;
|
||||
line-height: 23px;
|
||||
letter-spacing: var(--ls-20);
|
||||
color: #0d0d0d;
|
||||
}
|
||||
|
||||
.why-card__description {
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
line-height: 1.5;
|
||||
letter-spacing: var(--ls-16);
|
||||
color: #7a726d;
|
||||
}
|
||||
|
||||
@media (min-width: 440px) {
|
||||
.why {
|
||||
padding-left: 20px;
|
||||
padding-right: 20px;
|
||||
}
|
||||
|
||||
.why-card {
|
||||
gap: 24px;
|
||||
}
|
||||
|
||||
.why-card__icon-frame {
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
}
|
||||
|
||||
.why-card__icon--simple {
|
||||
width: 44.001px;
|
||||
height: 43.964px;
|
||||
}
|
||||
|
||||
.why-card__icon--familiar {
|
||||
width: 38px;
|
||||
height: 40px;
|
||||
}
|
||||
|
||||
.why-card__icon--connected {
|
||||
width: 44px;
|
||||
height: 43.914px;
|
||||
}
|
||||
|
||||
.why-card__icon--modern {
|
||||
width: 24.168px;
|
||||
height: 44px;
|
||||
}
|
||||
|
||||
.why__title {
|
||||
font-size: 32px;
|
||||
letter-spacing: var(--ls-32);
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 576px) {
|
||||
.why {
|
||||
padding-left: 24px;
|
||||
padding-right: 24px;
|
||||
}
|
||||
|
||||
.why__title {
|
||||
font-size: 36px;
|
||||
letter-spacing: var(--ls-36);
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.why {
|
||||
padding-left: 36px;
|
||||
padding-right: 36px;
|
||||
}
|
||||
|
||||
.why__title {
|
||||
font-size: 42px;
|
||||
letter-spacing: var(--ls-42);
|
||||
}
|
||||
|
||||
.why-card {
|
||||
padding: 36px;
|
||||
min-height: 152px;
|
||||
}
|
||||
|
||||
.why-card__title {
|
||||
font-size: 24px;
|
||||
letter-spacing: var(--ls-24);
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 1024px) {
|
||||
.why {
|
||||
padding-top: 60px;
|
||||
padding-bottom: 60px;
|
||||
padding-left: 36px;
|
||||
padding-right: 36px;
|
||||
}
|
||||
|
||||
.why__grid {
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
}
|
||||
|
||||
.why__intro {
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.why__copy {
|
||||
order: 1;
|
||||
}
|
||||
|
||||
.why__illustration {
|
||||
order: 2;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 1200px) {
|
||||
.why__title {
|
||||
font-size: 48px;
|
||||
letter-spacing: var(--ls-48);
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 1376px) {
|
||||
.why {
|
||||
padding-top: 120px;
|
||||
padding-bottom: 120px;
|
||||
padding-left: 0;
|
||||
padding-right: 0;
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,3 @@
|
||||
{
|
||||
"extends": "astro/tsconfigs/strict",
|
||||
"compilerOptions": {
|
||||
"baseUrl": ".",
|
||||
"paths": {}
|
||||
}
|
||||
"extends": "./node_modules/astro/tsconfigs/strict.json"
|
||||
}
|
||||
|
||||