Compare commits

..

6 Commits

Author SHA1 Message Date
Sanil Chawla
bdd322a279 feat(page): animate hero words individually and fix mobile background sizing
All checks were successful
Deploy to Cloudflare Pages / deploy (push) Successful in 26s
2026-02-13 00:39:42 -08:00
Sanil Chawla
a942ae91f2 feat: update logo variants
All checks were successful
Deploy to Cloudflare Pages / deploy (push) Successful in 26s
2026-02-12 17:07:46 -08:00
Sanil Chawla
755cfca516 feat: add more logo variants
All checks were successful
Deploy to Cloudflare Pages / deploy (push) Successful in 26s
2026-02-12 17:03:39 -08:00
Sanil Chawla
e73868f3ed feat(page): update social meta tags, favicons, and OG banner
All checks were successful
Deploy to Cloudflare Pages / deploy (push) Successful in 26s
Replace single favicon with full icon set (PNG, SVG, ICO, Apple touch
icon, web manifest). Update OG/Twitter image to banner.jpg. Align
title and description meta tags with current page copy.
2026-02-12 00:18:35 -08:00
Sanil Chawla
1275e58d55 docs(config): add deploy command, update README and CLAUDE.md
All checks were successful
Deploy to Cloudflare Pages / deploy (push) Successful in 27s
Adds bun run deploy script, deployment docs to README, and updates
CLAUDE.md with deployment info, current patterns, and rule to keep
docs in sync with changes.
2026-02-12 00:00:10 -08:00
Sanil Chawla
b750970770 ci(config): add Cloudflare Pages deployment via Gitea Actions
All checks were successful
Deploy to Cloudflare Pages / deploy (push) Successful in 22s
Adds wrangler.toml for Pages config, Gitea Actions workflow that
builds with Bun and deploys to Cloudflare Pages on pushes to main.
Requires CLOUDFLARE_API_TOKEN and CLOUDFLARE_ACCOUNT_ID secrets.
Also disables subject-case rule in commitlint.
2026-02-11 23:57:41 -08:00
27 changed files with 158 additions and 28 deletions

View File

@@ -0,0 +1,33 @@
name: Deploy to Cloudflare Pages
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: "22"
- name: Setup Bun
uses: oven-sh/setup-bun@v2
- name: Install dependencies
run: bun install --frozen-lockfile
- name: Build
run: bun run build
- name: Deploy to Cloudflare Pages
uses: cloudflare/wrangler-action@v3
with:
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
command: pages deploy

View File

@@ -1,8 +1,13 @@
# CLAUDE.md # CLAUDE.md
## Important Rules
- **Always update `CLAUDE.md` and `README.md`** when making changes that affect project structure, commands, deployment, tooling, or conventions. Keep them in sync.
- Do not include co-author lines or "Generated by" attributions in commit messages.
## Project Overview ## Project Overview
Lofty is a 501(c)(4) social welfare organization connecting creative professionals with the progressive movement. This repo is the **pre-launch landing page** — a single-page static site to collect email signups. The Lofty Project is a 501(c)(4) connecting creative talent with leftist organizing. This repo is the **pre-launch landing page** — a single-page static site with interest signup links.
**Stack:** Astro 5 (static output), TypeScript, Bun 1.3+ **Stack:** Astro 5 (static output), TypeScript, Bun 1.3+
@@ -11,10 +16,13 @@ Lofty is a 501(c)(4) social welfare organization connecting creative professiona
``` ```
src/ src/
layouts/Layout.astro # HTML shell, meta tags, Typekit fonts, design tokens, CSS reset layouts/Layout.astro # HTML shell, meta tags, Typekit fonts, design tokens, CSS reset
pages/index.astro # All page sections (hero, pillars, signup form, footer), scoped CSS, client JS pages/index.astro # All page sections (hero, pillars, signup, footer), scoped CSS, client JS
public/ public/
favicon.svg # Stylized "L" mark favicon.svg # Stylized "L" mark
hero-bg.webp/.jpg # Hero background image (WebP + JPEG fallback)
astro.config.mjs # Static output, site URL, dev toolbar disabled astro.config.mjs # Static output, site URL, dev toolbar disabled
wrangler.toml # Cloudflare Pages deployment config
.gitea/workflows/ # CI/CD via Gitea Actions
``` ```
Two-file architecture: `Layout.astro` (global concerns) + `index.astro` (all content). No component files — single page with no reuse opportunity. Two-file architecture: `Layout.astro` (global concerns) + `index.astro` (all content). No component files — single page with no reuse opportunity.
@@ -27,6 +35,16 @@ bun run build # Static build to dist/
bun run preview # Preview production build bun run preview # Preview production build
``` ```
## Deployment
Deploys to **Cloudflare Pages** via Gitea Actions on push to `main`.
```bash
bun run deploy # Build + deploy to Cloudflare Pages (manual)
```
Config lives in `wrangler.toml`. CI requires `CLOUDFLARE_API_TOKEN` and `CLOUDFLARE_ACCOUNT_ID` as Gitea secrets.
## Linting & Formatting ## Linting & Formatting
Uses **Biome** for both linting and formatting. Uses **Biome** for both linting and formatting.
@@ -53,8 +71,6 @@ Format: `type(scope): subject`
bun run commit # Guided commit via commitizen (cz-git) bun run commit # Guided commit via commitizen (cz-git)
``` ```
Do not include co-author lines or "Generated by" attributions in commit messages.
## Git Hooks (Lefthook) ## Git Hooks (Lefthook)
- **pre-commit:** Biome check on staged files - **pre-commit:** Biome check on staged files
@@ -63,19 +79,17 @@ Do not include co-author lines or "Generated by" attributions in commit messages
## Design Tokens ## Design Tokens
Colors, typography, and spacing are defined as CSS custom properties in `Layout.astro`: Colors, typography, spacing, and easing are defined as CSS custom properties in `Layout.astro`:
- **Colors:** cream (#F5F0EB), dusty-rose (#C48B8B), slate-blue (#8A9BB5), navy (#1A1F2E) - **Colors:** cream (#F5F0EB), dusty-rose (#C48B8B), slate-blue (#8A9BB5), navy (#1A1F2E)
- **Fonts:** `neue-haas-grotesk-display` (body, weight 500), `meno-text` (display/headings) via Typekit - **Fonts:** `neue-haas-grotesk-display` (body, weight 500), `meno-text` (display/headings) via Typekit
- **Spacing/type:** Fluid scales using `clamp()` - **Spacing/type:** Fluid scales using `clamp()`
- **Easing:** `--ease-out-quart` (hover), `--ease-in-out` (layout/entrance animations)
## Environment Variables
- `PUBLIC_SIGNUP_ENDPOINT` — Form submission endpoint (defaults to `/api/signup`). See `.env.example`.
## Key Patterns ## Key Patterns
- All scroll animations use `.reveal` class + IntersectionObserver (threshold 0.1, -40px root margin) - All scroll animations use `.reveal` class + IntersectionObserver (threshold 0.1, -40px root margin)
- Hero animations are CSS-only with staggered delays via `--i` custom property - Hero animations: line-by-line blur-in reveal with staggered delays, quartic ease-out
- Hover interactions use `--ease-out-quart`, layout/entrance animations use `--ease-in-out`
- `prefers-reduced-motion` is detected via inline script and disables all animations via `.reduce-motion` class - `prefers-reduced-motion` is detected via inline script and disables all animations via `.reduce-motion` class
- Form handler uses progressive enhancement — works as standard POST without JS - Hero background image uses blur + desaturation + cream overlay for glassmorphic effect

View File

@@ -15,6 +15,16 @@ bun run build # Static build to dist/
bun run preview # Preview production build bun run preview # Preview production build
``` ```
## Deployment
Deploys to Cloudflare Pages via Gitea Actions on push to `main`. To deploy manually:
```sh
bun run deploy # Build + deploy to Cloudflare Pages
```
Requires `CLOUDFLARE_API_TOKEN` and `CLOUDFLARE_ACCOUNT_ID` (set as Gitea secrets for CI, or via `wrangler login` locally).
## Linting & Formatting ## Linting & Formatting
```sh ```sh

View File

@@ -11,6 +11,7 @@
"@biomejs/biome": "^2.3.10", "@biomejs/biome": "^2.3.10",
"@commitlint/cli": "^20.2.0", "@commitlint/cli": "^20.2.0",
"@commitlint/config-conventional": "^20.2.0", "@commitlint/config-conventional": "^20.2.0",
"chrome-devtools-mcp": "^0.17.0",
"commitizen": "^4.3.1", "commitizen": "^4.3.1",
"cz-git": "^1.12.0", "cz-git": "^1.12.0",
"lefthook": "^2.0.12", "lefthook": "^2.0.12",
@@ -352,6 +353,8 @@
"chokidar": ["chokidar@5.0.0", "", { "dependencies": { "readdirp": "^5.0.0" } }, "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw=="], "chokidar": ["chokidar@5.0.0", "", { "dependencies": { "readdirp": "^5.0.0" } }, "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw=="],
"chrome-devtools-mcp": ["chrome-devtools-mcp@0.17.0", "", { "bin": { "chrome-devtools-mcp": "build/src/index.js" } }, "sha512-vMi2zXq2ph2EG6amyyApcvuKJcEFj4cGK1XQVb6x8vQYHk8D9ZnSxdtFqD0cRnG7SbUOrg3GhjOZEJAD1dZWSQ=="],
"ci-info": ["ci-info@4.4.0", "", {}, "sha512-77PSwercCZU2Fc4sX94eF8k8Pxte6JAwL4/ICZLFjJLqegs7kCuAsqqj/70NQF6TvDpgFjkubQB2FW2ZZddvQg=="], "ci-info": ["ci-info@4.4.0", "", {}, "sha512-77PSwercCZU2Fc4sX94eF8k8Pxte6JAwL4/ICZLFjJLqegs7kCuAsqqj/70NQF6TvDpgFjkubQB2FW2ZZddvQg=="],
"cli-boxes": ["cli-boxes@3.0.0", "", {}, "sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g=="], "cli-boxes": ["cli-boxes@3.0.0", "", {}, "sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g=="],

View File

@@ -21,7 +21,7 @@ export default {
], ],
"scope-enum": [1, "always", ["page", "style", "config", "deps"]], "scope-enum": [1, "always", ["page", "style", "config", "deps"]],
"scope-empty": [1, "never"], "scope-empty": [1, "never"],
"subject-case": [2, "always", "lower-case"], "subject-case": [0],
"subject-empty": [2, "never"], "subject-empty": [2, "never"],
"subject-full-stop": [2, "never", "."], "subject-full-stop": [2, "never", "."],
"body-leading-blank": [2, "always"], "body-leading-blank": [2, "always"],

View File

@@ -12,6 +12,7 @@
"format": "biome format --write .", "format": "biome format --write .",
"format:check": "biome format .", "format:check": "biome format .",
"commit": "cz", "commit": "cz",
"deploy": "astro build && wrangler pages deploy",
"prepare": "lefthook install" "prepare": "lefthook install"
}, },
"dependencies": { "dependencies": {
@@ -21,6 +22,7 @@
"@biomejs/biome": "^2.3.10", "@biomejs/biome": "^2.3.10",
"@commitlint/cli": "^20.2.0", "@commitlint/cli": "^20.2.0",
"@commitlint/config-conventional": "^20.2.0", "@commitlint/config-conventional": "^20.2.0",
"chrome-devtools-mcp": "^0.17.0",
"commitizen": "^4.3.1", "commitizen": "^4.3.1",
"cz-git": "^1.12.0", "cz-git": "^1.12.0",
"lefthook": "^2.0.12" "lefthook": "^2.0.12"

BIN
public/banner.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 163 KiB

BIN
public/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 420 B

After

Width:  |  Height:  |  Size: 9.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

BIN
public/icons/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

3
public/icons/favicon.svg Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 9.8 KiB

View File

@@ -0,0 +1,21 @@
{
"name": "Lofty",
"short_name": "Lofty",
"icons": [
{
"src": "/icons/web-app-manifest-192x192.png",
"sizes": "192x192",
"type": "image/png",
"purpose": "maskable"
},
{
"src": "/icons/web-app-manifest-512x512.png",
"sizes": "512x512",
"type": "image/png",
"purpose": "maskable"
}
],
"theme_color": "#ffffff",
"background_color": "#ffffff",
"display": "standalone"
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 5.8 KiB

BIN
public/wordmark-full.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

3
public/wordmark-full.svg Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 5.8 KiB

BIN
public/wordmark-white.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

View File

@@ -0,0 +1,3 @@
<svg width="377" height="192" viewBox="0 0 377 192" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M4.05 148V142.45C19.2 141.7 21.75 136.3 21.75 120.85V70.6C21.75 54.4 19.05 49.75 4.05 49.15V43.6C19.2 44.05 22.5 44.2 32.4 44.2C42.3 44.2 51.15 43.75 58.95 43.6V49.15C45 50.2 41.4 53.8 41.4 71.35V120.7C41.4 136.9 44.85 139 60.15 139C76.65 139 86.85 131.95 91.2 113.8H96.9L95.7 131.8C94.95 141.1 93.15 144.7 89.7 148C73.95 147.55 63 147.4 48.9 147.4C31.95 147.4 14.85 147.7 4.05 148ZM143.946 72.25C166.896 72.25 181.746 88.45 181.746 110.95C181.746 133.75 166.746 149.65 143.946 149.65C121.296 149.65 106.446 133.75 106.446 110.95C106.446 88.15 121.296 72.25 143.946 72.25ZM143.946 143.2C156.396 143.2 162.696 130.45 162.696 110.95C162.696 91.75 156.396 78.7 143.946 78.7C131.646 78.7 125.346 91.75 125.346 110.95C125.346 130.45 131.646 143.2 143.946 143.2ZM272.777 149.5C250.277 150.1 245.327 143.35 245.477 120.85C245.627 111.1 245.627 86.95 245.627 83.95H216.977V126.85C216.977 139.45 220.427 142.3 228.077 142.9V148C218.627 147.7 216.527 147.55 209.027 147.55C201.527 147.55 195.827 147.7 188.177 148V142.9C195.677 142.45 200.627 139.75 200.627 127V83.95H190.877L189.827 78.1C199.277 74.95 199.577 72.55 202.277 64.15C204.977 55.9 208.727 48.85 214.877 43.3C220.577 38.2 228.227 34.75 236.777 34.75C249.077 34.75 257.477 40.15 257.477 48.1C257.477 51.25 256.127 54.25 253.877 56.05L248.927 57.85C244.877 50.5 238.277 43 230.327 43C222.227 43 216.977 50.2 216.977 63.25V74.35H241.727C248.177 69.85 253.577 63.1 258.077 56.05L263.777 57.25C262.577 62.8 262.127 69.4 262.127 74.35L285.977 73.9L284.327 83.95H261.977C261.977 86.95 261.977 109.75 261.827 118.9C261.827 133.45 268.877 137.35 278.477 137.35C281.777 137.35 285.227 136.9 288.677 136.15L289.877 140.5C285.077 143.5 278.327 147.1 272.777 149.5ZM374.849 73.9V79C368.699 79.45 365.549 82.3 357.599 100.3C349.649 118.45 339.149 143.05 329.099 167.5C326.999 172.75 318.599 191.35 303.449 191.35C294.899 191.35 288.749 185.65 287.849 176.95L290.849 174.1C294.149 176.5 297.449 177.4 301.949 177.4C311.699 177.4 319.649 168.7 326.549 151.45C319.499 133.75 310.499 113.8 304.049 98.95C295.949 80.35 292.349 79.75 287.249 79V73.9C292.349 74.2 299.549 74.35 306.599 74.35C313.949 74.35 319.649 74.2 326.549 73.9V79C319.649 79.45 316.349 82.3 321.149 95.65C324.299 104.8 330.899 120.55 335.549 131.05C339.749 120.25 346.499 102.25 348.149 96.55C351.749 84.25 348.599 79.3 339.749 79V73.9C346.499 74.35 349.949 74.35 356.399 74.35C361.799 74.35 368.399 74.35 374.849 73.9Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 2.5 KiB

BIN
public/wordmark.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 KiB

3
public/wordmark.svg Normal file
View File

@@ -0,0 +1,3 @@
<svg width="377" height="192" viewBox="0 0 377 192" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M4.05 148V142.45C19.2 141.7 21.75 136.3 21.75 120.85V70.6C21.75 54.4 19.05 49.75 4.05 49.15V43.6C19.2 44.05 22.5 44.2 32.4 44.2C42.3 44.2 51.15 43.75 58.95 43.6V49.15C45 50.2 41.4 53.8 41.4 71.35V120.7C41.4 136.9 44.85 139 60.15 139C76.65 139 86.85 131.95 91.2 113.8H96.9L95.7 131.8C94.95 141.1 93.15 144.7 89.7 148C73.95 147.55 63 147.4 48.9 147.4C31.95 147.4 14.85 147.7 4.05 148ZM143.946 72.25C166.896 72.25 181.746 88.45 181.746 110.95C181.746 133.75 166.746 149.65 143.946 149.65C121.296 149.65 106.446 133.75 106.446 110.95C106.446 88.15 121.296 72.25 143.946 72.25ZM143.946 143.2C156.396 143.2 162.696 130.45 162.696 110.95C162.696 91.75 156.396 78.7 143.946 78.7C131.646 78.7 125.346 91.75 125.346 110.95C125.346 130.45 131.646 143.2 143.946 143.2ZM272.777 149.5C250.277 150.1 245.327 143.35 245.477 120.85C245.627 111.1 245.627 86.95 245.627 83.95H216.977V126.85C216.977 139.45 220.427 142.3 228.077 142.9V148C218.627 147.7 216.527 147.55 209.027 147.55C201.527 147.55 195.827 147.7 188.177 148V142.9C195.677 142.45 200.627 139.75 200.627 127V83.95H190.877L189.827 78.1C199.277 74.95 199.577 72.55 202.277 64.15C204.977 55.9 208.727 48.85 214.877 43.3C220.577 38.2 228.227 34.75 236.777 34.75C249.077 34.75 257.477 40.15 257.477 48.1C257.477 51.25 256.127 54.25 253.877 56.05L248.927 57.85C244.877 50.5 238.277 43 230.327 43C222.227 43 216.977 50.2 216.977 63.25V74.35H241.727C248.177 69.85 253.577 63.1 258.077 56.05L263.777 57.25C262.577 62.8 262.127 69.4 262.127 74.35L285.977 73.9L284.327 83.95H261.977C261.977 86.95 261.977 109.75 261.827 118.9C261.827 133.45 268.877 137.35 278.477 137.35C281.777 137.35 285.227 136.9 288.677 136.15L289.877 140.5C285.077 143.5 278.327 147.1 272.777 149.5ZM374.849 73.9V79C368.699 79.45 365.549 82.3 357.599 100.3C349.649 118.45 339.149 143.05 329.099 167.5C326.999 172.75 318.599 191.35 303.449 191.35C294.899 191.35 288.749 185.65 287.849 176.95L290.849 174.1C294.149 176.5 297.449 177.4 301.949 177.4C311.699 177.4 319.649 168.7 326.549 151.45C319.499 133.75 310.499 113.8 304.049 98.95C295.949 80.35 292.349 79.75 287.249 79V73.9C292.349 74.2 299.549 74.35 306.599 74.35C313.949 74.35 319.649 74.2 326.549 73.9V79C319.649 79.45 316.349 82.3 321.149 95.65C324.299 104.8 330.899 120.55 335.549 131.05C339.749 120.25 346.499 102.25 348.149 96.55C351.749 84.25 348.599 79.3 339.749 79V73.9C346.499 74.35 349.949 74.35 356.399 74.35C361.799 74.35 368.399 74.35 374.849 73.9Z" fill="#003049"/>
</svg>

After

Width:  |  Height:  |  Size: 2.5 KiB

View File

@@ -5,11 +5,11 @@ interface Props {
} }
const { const {
title = "Lofty — Creative power for the progressive movement", title = "Lofty — Talent, media, and tech for collective power",
description = "Lofty connects creative professionals with progressive campaigns, produces original political media, and builds digital infrastructure for progressive organizing.", description = "The Lofty Project bridges talented creatives with leftist movements and progressive campaigns — connecting designers, filmmakers, and strategists to build a better country for all.",
} = Astro.props; } = Astro.props;
const ogImage = "/og.png"; const ogImage = "/banner.jpg";
--- ---
<!doctype html> <!doctype html>
@@ -17,7 +17,12 @@ const ogImage = "/og.png";
<head> <head>
<meta charset="utf-8" /> <meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" /> <meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" /> <link rel="icon" type="image/png" href="/icons/favicon-96x96.png" sizes="96x96" />
<link rel="icon" type="image/svg+xml" href="/icons/favicon.svg" />
<link rel="shortcut icon" href="/icons/favicon.ico" />
<link rel="apple-touch-icon" sizes="180x180" href="/icons/apple-touch-icon.png" />
<meta name="apple-mobile-web-app-title" content="Lofty" />
<link rel="manifest" href="/icons/site.webmanifest" />
<meta name="generator" content={Astro.generator} /> <meta name="generator" content={Astro.generator} />
<title>{title}</title> <title>{title}</title>

View File

@@ -3,7 +3,10 @@ import Layout from "../layouts/Layout.astro";
// const signupEndpoint = import.meta.env.PUBLIC_SIGNUP_ENDPOINT || "/api/signup"; // const signupEndpoint = import.meta.env.PUBLIC_SIGNUP_ENDPOINT || "/api/signup";
const heroLines = ["Talent, media, and tech", "for collective power"]; const heroWords = [
["Talent,", "media,", "and", "tech"],
["for", "collective", "power"],
];
const pillars = [ const pillars = [
{ {
@@ -43,8 +46,15 @@ const pillars = [
</header> </header>
<div class="container hero-inner"> <div class="container hero-inner">
<h1 class="hero-headline"> <h1 class="hero-headline">
{heroLines.map((line, i) => ( {heroWords.map((group, groupIndex) => (
<span class="line-reveal" style={`--i: ${i}`}>{line}</span> <span class="word-line">
{group.map((word, wordIndex) => {
const wordNum = heroWords.slice(0, groupIndex).flat().length + wordIndex;
return (
<span class="word-reveal" style={`--i: ${wordNum}`}>{word}</span>
);
})}
</span>
))} ))}
</h1> </h1>
<p class="hero-sub"> <p class="hero-sub">
@@ -191,6 +201,8 @@ const pillars = [
.hero-bg img { .hero-bg img {
width: 100%; width: 100%;
height: 100%; height: 100%;
min-height: 100vh;
min-height: 100dvh;
object-fit: cover; object-fit: cover;
object-position: center center; object-position: center center;
filter: blur(2px) saturate(0.15) brightness(1.2); filter: blur(2px) saturate(0.15) brightness(1.2);
@@ -255,13 +267,28 @@ const pillars = [
line-height: 1.05; line-height: 1.05;
} }
.line-reveal { .word-line {
display: block; display: inline;
}
.word-reveal {
display: inline-block;
opacity: 0; opacity: 0;
filter: blur(10px); filter: blur(10px);
transform: translateY(0.4em); transform: translateY(0.4em);
animation: line-clip 1.4s var(--ease-out-quart) forwards; animation: line-clip 1.4s var(--ease-out-quart) forwards;
animation-delay: calc(0.3s + var(--i) * 0.35s); animation-delay: calc(0.3s + var(--i) * 0.12s);
margin-right: 0.25em;
}
.word-reveal:last-child {
margin-right: 0;
}
@media (min-width: 768px) {
.word-line {
display: block;
}
} }
@keyframes line-clip { @keyframes line-clip {

3
wrangler.toml Normal file
View File

@@ -0,0 +1,3 @@
name = "lofty-landing"
pages_build_output_dir = "dist"
compatibility_date = "2026-02-01"