This commit is contained in:
2026-04-27 22:23:33 +02:00
parent 3bb0549109
commit 05efbb9910
33 changed files with 4069 additions and 243 deletions

58
greenlens-promo/AGENTS.md Normal file
View File

@@ -0,0 +1,58 @@
# HyperFrames Composition Project
## Skills
This project uses AI agent skills for framework-specific patterns. Install them if not already present:
```bash
npx skills add heygen-com/hyperframes
```
Skills encode patterns like `window.__timelines` registration, `data-*` attribute semantics, and shader-compatible CSS rules that are not in generic web docs. Using them produces correct compositions from the start.
## Commands
```bash
npx hyperframes preview # preview in browser (studio editor)
npx hyperframes render # render to MP4
npx hyperframes lint # validate compositions (errors + warnings)
npx hyperframes lint --json # machine-readable output for CI
npx hyperframes docs <topic> # reference docs in terminal
```
## Project Structure
- `index.html` — main composition (root timeline)
- `compositions/` — sub-compositions referenced via `data-composition-src`
- `assets/` — media files (video, audio, images)
- `meta.json` — project metadata (id, name)
- `transcript.json` — whisper word-level transcript (if generated)
## Linting — Always Run After Changes
After creating or editing any `.html` composition, run the linter before considering the task complete:
```bash
npx hyperframes lint
```
Fix all errors before presenting the result.
## Key Rules
1. Every timed element needs `data-start`, `data-duration`, and `data-track-index`
2. Visible timed elements **must** have `class="clip"` — the framework uses this for visibility control
3. GSAP timelines must be paused and registered on `window.__timelines`:
```js
window.__timelines = window.__timelines || {};
window.__timelines["composition-id"] = gsap.timeline({ paused: true });
```
4. Videos use `muted` with a separate `<audio>` element for the audio track
5. Sub-compositions use `data-composition-src="compositions/file.html"`
6. Only deterministic logic — no `Date.now()`, no `Math.random()`, no network fetches
## Documentation
Full docs: https://hyperframes.heygen.com/introduction
Machine-readable index for AI tools: https://hyperframes.heygen.com/llms.txt

73
greenlens-promo/CLAUDE.md Normal file
View File

@@ -0,0 +1,73 @@
# HyperFrames Composition Project
## Skills — USE THESE FIRST
**Always invoke the relevant skill before writing or modifying compositions.** Skills encode framework-specific patterns (e.g., `window.__timelines` registration, `data-*` attribute semantics, shader-compatible CSS rules) that are NOT in generic web docs. Skipping them produces broken compositions.
| Skill | Command | When to use |
| -------------------------- | ------------------------- | ------------------------------------------------------------------------------------------------- |
| **hyperframes** | `/hyperframes` | Creating or editing HTML compositions, captions, TTS, audio-reactive animation, marker highlights |
| **hyperframes-cli** | `/hyperframes-cli` | CLI commands: init, lint, preview, render, transcribe, tts |
| **hyperframes-registry** | `/hyperframes-registry` | Installing blocks and components via `hyperframes add` |
| **website-to-hyperframes** | `/website-to-hyperframes` | Capturing a URL and turning it into a video — full website-to-video pipeline |
| **gsap** | `/gsap` | GSAP animations for HyperFrames — tweens, timelines, easing, performance |
> **Skills not available?** Ask the user to run `npx hyperframes skills` and restart their
> agent session, or install manually: `npx skills add heygen-com/hyperframes`.
## Commands
```bash
npx hyperframes preview # preview in browser (studio editor)
npx hyperframes render # render to MP4
npx hyperframes lint # validate compositions (errors + warnings)
npx hyperframes lint --verbose # include info-level findings
npx hyperframes lint --json # machine-readable output for CI
npx hyperframes docs <topic> # reference docs in terminal
```
## Documentation
**For quick reference**, use the local CLI docs command (no network required):
```bash
npx hyperframes docs <topic>
```
Topics: `data-attributes`, `gsap`, `compositions`, `rendering`, `examples`, `troubleshooting`
**For full documentation**, discover pages via the machine-readable index — do NOT guess URLs:
```
https://hyperframes.heygen.com/llms.txt
```
## Project Structure
- `index.html` — main composition (root timeline)
- `compositions/` — sub-compositions referenced via `data-composition-src`
- `meta.json` — project metadata (id, name)
- `transcript.json` — whisper word-level transcript (if generated)
## Linting — ALWAYS RUN AFTER CHANGES
After creating or editing any `.html` composition, **always** run the linter before considering the task complete:
```bash
npx hyperframes lint
```
Fix all errors before presenting the result. Warnings are informational and usually safe to ignore.
## Key Rules
1. Every timed element needs `data-start`, `data-duration`, and `data-track-index`
2. Elements with timing **MUST** have `class="clip"` — the framework uses this for visibility control
3. Timelines must be paused and registered on `window.__timelines`:
```js
window.__timelines = window.__timelines || {};
window.__timelines["composition-id"] = gsap.timeline({ paused: true });
```
4. Videos use `muted` with a separate `<audio>` element for the audio track
5. Sub-compositions use `data-composition-src="compositions/file.html"` to reference other HTML files
6. Only deterministic logic — no `Date.now()`, no `Math.random()`, no network fetches

53
greenlens-promo/DESIGN.md Normal file
View File

@@ -0,0 +1,53 @@
# Design System
## Overview
GreenLens uses a premium botanical landing-page identity: dark forest surfaces, cream typography, warm coral CTAs, and layered plant imagery. The design combines editorial serif headlines with practical app UI cards, bento-style feature blocks, and cinematic plant photography. It should feel calm, intelligent, and tactile rather than loud or generic.
## Colors
- **Forest Surface**: `#131f16` - primary dark background.
- **Forest Alt**: `#1c2e21` - secondary panels and depth layers.
- **Leaf Green**: `#2a5c3f` - core brand green.
- **Leaf Mid**: `#3d7a56` - supporting accents and UI strokes.
- **Leaf Light**: `#56a074` - highlights, scan lines, and positive states.
- **Coral CTA**: `#e07a50` - primary action color.
- **Coral Hover**: `#c96840` - darker warm accent.
- **Cream**: `#f4f1e8` - main text on dark backgrounds.
- **Cream Alt**: `#eae6d8` - quiet surfaces and secondary text.
- **Muted Sage**: `#7a8c7d` - secondary labels.
## Typography
- **Display**: Playfair Display, Georgia, serif. Used for large brand headlines and emotional words. Weight 900 for confident title moments, italic for botanical emphasis.
- **Body/UI**: Inter, system sans-serif. Used for labels, feature chips, captions, and app-style interface cards. Weights 500-800 for product promo readability.
- **Hierarchy**: Promo hero text can sit between 86-132px. Scene headings should stay above 64px. Captions and UI labels should stay above 22px for encoded video clarity.
## Elevation
Depth comes from layered photos, soft radial glows, thin cream/green translucent borders, and rounded UI panels. Avoid generic hard drop shadows; use warm bloom, inset highlights, glassy overlays, and perspective tilt on app frames.
## Components
- **Cinematic Hero Split**: Editorial text on one side with a framed app/demo visual on the other.
- **Scan Badge**: Small rounded status label with a pulsing green dot.
- **Bento Feature Cards**: Rounded image cards with dark overlays, feature chips, and short action headlines.
- **Botanical Intelligence Panel**: AI analysis visual paired with compact capability rows.
- **Step Timeline**: Four numbered actions: photograph, identify, care plan, growth tracking.
- **Store CTA Row**: Two compact dark buttons with platform labels.
## Do's and Don'ts
### Do's
- Use `#131f16`, `#f4f1e8`, `#56a074`, and `#e07a50` as the recognizable brand anchors.
- Keep plant imagery visible and moving with slow zooms, pans, and layered parallax.
- Use Playfair Display for the largest words and Inter for product facts.
- Treat UI as crafted panels with borders, glows, and botanical scan details.
### Don'ts
- Do not switch into neon tech blues or generic SaaS purple.
- Do not make the promo text-only; the website depends on plant and app visuals.
- Do not use flat, static screenshots without motion treatment.
- Do not use hard black-white contrast when cream and forest tones are available.

11
greenlens-promo/SCRIPT.md Normal file
View File

@@ -0,0 +1,11 @@
# Script
## 20-second VO
What if every plant came with instructions?
Open GreenLens. Scan a leaf. Get the name, the care plan, and the next step in seconds.
Track watering, growth, notes, and plant health in one calm place.
GreenLens. Scan it. Track it. Grow it.

View File

@@ -0,0 +1,86 @@
# Storyboard
**Format:** 1920x1080 landscape
**Audio:** Voiceover-first promo. Light organic underscore can be added later.
**VO direction:** Calm, warm, confident, premium app-store register.
**Style basis:** DESIGN.md from the GreenLens landing page.
## Asset Audit
| Asset | Type | Assign to Beat | Role |
| --- | --- | --- | --- |
| `favicon.svg` | Logo | 1, 5 | Brand mark in opener and closer |
| `hero-plant.png` | Hero image | 1 | Full-bleed botanical hook |
| `greenlens.mp4` | Product video | 2 | Framed moving app/demo visual |
| `scan-feature.png` | Feature image | 2, 4 | Scan action and how-it-works panel |
| `ai-analysis.png` | Product image | 3 | Botanical intelligence / analysis |
| `track-feature.png` | Feature image | 4 | Tracking and reminders |
| `plant-collection.png` | Feature image | 4, 5 | Collection and CTA visual |
## Beat 1 - Hook, 0.0-4.0s
**Concept:** The viewer enters a calm botanical world. The plant photo breathes in the background while the question lands like a premium app-store promise.
**VO cue:** "What if every plant came with instructions?"
**Visual description:** Full-frame `hero-plant.png` slowly pushes forward. A dark forest overlay gives contrast. The GreenLens wordmark and favicon settle near the top. Large Playfair Display type fills the left-center. A thin scan line travels across the image and small leaf particles drift in the foreground.
**Transition:** Velocity-matched blur upward into the product demo.
## Beat 2 - Scan, 4.0-8.5s
**Concept:** The abstract promise becomes a concrete action: open, scan, identify. This beat should feel like the app is actively seeing the plant.
**VO cue:** "Open GreenLens. Scan a leaf. Get the name..."
**Visual description:** A tilted phone-like frame plays `greenlens.mp4`. `scan-feature.png` sits behind it as a botanical plate. Green scan brackets draw around the phone, a pulsing "AI Scan" badge appears, and compact result chips cascade in.
**Transition:** Whip-pan left into the care intelligence scene.
## Beat 3 - Care Plan, 8.5-13.0s
**Concept:** Identification becomes guidance. The frame shifts from seeing to understanding.
**VO cue:** "...the care plan, and the next step in seconds."
**Visual description:** `ai-analysis.png` becomes a large analysis panel. Three care cards slide in: Watering, Light, Health. The coral CTA accent draws a route from "scan" to "next step." Botanical labels count up and settle.
**Transition:** Blur-through into a wider tracking system.
## Beat 4 - Track, 13.0-17.0s
**Concept:** GreenLens is not a one-off scanner; it is the plant owner's quiet operating system.
**VO cue:** "Track watering, growth, notes, and plant health in one calm place."
**Visual description:** Three bento cards form a clean grid using `track-feature.png`, `plant-collection.png`, and `scan-feature.png`. Timeline ticks animate across the bottom. Four labels appear in rhythm: Watering, Growth, Notes, Health.
**Transition:** Soft zoom out into final CTA.
## Beat 5 - CTA, 17.0-20.0s
**Concept:** End on the product name and the landing page's tagline rhythm.
**VO cue:** "GreenLens. Scan it. Track it. Grow it."
**Visual description:** Cream background lightens the frame. The favicon and GreenLens wordmark center up. A coral button appears below with the final action line. `plant-collection.png` drifts as a soft rounded card in the background.
**Transition:** Hold to end.
## Production Architecture
```
greenlens-promo/
|-- index.html
|-- DESIGN.md
|-- SCRIPT.md
|-- STORYBOARD.md
|-- assets/
| |-- favicon.svg
| |-- hero-plant.png
| |-- greenlens.mp4
| |-- scan-feature.png
| |-- ai-analysis.png
| |-- track-feature.png
| |-- plant-collection.png
```

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

View File

@@ -0,0 +1,4 @@
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M16 4C11 10 6 14 4 18C2 22 4 28 10 28C14 28 16 26 16 26C16 26 18 28 22 28C28 28 30 22 28 18C26 14 21 10 16 4Z" fill="#2A5C3F"/>
<path d="M16 4C14 8 13 12 14 16C15 20 18 22 16 26" stroke="#F4F1E8" stroke-width="1.5" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 356 B

View File

@@ -0,0 +1,30 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1920 1080" width="1920" height="1080">
<defs>
<filter id="shadow" x="-20%" y="-20%" width="140%" height="140%">
<feDropShadow dx="0" dy="2" stdDeviation="2" flood-opacity="0.2"/>
</filter>
<g id="cursor-arrow">
<path d="M0 0 L 16 16 L 9.5 16 L 6.5 23 L 6.5 16 L 0 16 Z" fill="white" stroke="#000" stroke-width="1.5" stroke-linejoin="round" filter="url(#shadow)"/>
</g>
</defs>
<rect width="1920" height="1080" fill="#f0f0f0" />
<g id="cursor-designer" transform="translate(600, 500)">
<use href="#cursor-arrow" x="0" y="0"/>
<rect x="16" y="20" width="90" height="24" rx="4" fill="#A259FF"/>
<text x="61" y="36" font-family="Inter, sans-serif" font-size="12" fill="white" text-anchor="middle" font-weight="bold">Designer</text>
</g>
<g id="cursor-engineer" transform="translate(900, 400)">
<use href="#cursor-arrow" x="0" y="0"/>
<rect x="16" y="20" width="90" height="24" rx="4" fill="#1ABCFE"/>
<text x="61" y="36" font-family="Inter, sans-serif" font-size="12" fill="white" text-anchor="middle" font-weight="bold">Engineer</text>
</g>
<g id="cursor-pm" transform="translate(1200, 600)">
<use href="#cursor-arrow" x="0" y="0"/>
<rect x="16" y="20" width="50" height="24" rx="4" fill="#F24E1E"/>
<text x="41" y="36" font-family="Inter, sans-serif" font-size="12" fill="white" text-anchor="middle" font-weight="bold">PM</text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@@ -0,0 +1,15 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1920 1080" width="1920" height="1080">
<g transform="translate(880, 420)">
<!-- Top-left: rounded square -->
<path id="logo-tl" d="M 40 0 A 40 40 0 0 0 0 40 L 0 80 A 40 40 0 0 0 40 120 A 40 40 0 0 0 80 80 L 80 40 A 40 40 0 0 0 40 0 Z" fill="#F24E1E" />
<!-- Top-right: rounded square -->
<path id="logo-tr" d="M 120 0 A 40 40 0 0 0 80 40 L 80 80 A 40 40 0 0 0 120 120 A 40 40 0 0 0 160 80 L 160 40 A 40 40 0 0 0 120 0 Z" fill="#A259FF" />
<!-- Bottom-left: half-circle -->
<path id="logo-bl" d="M 0 120 A 40 120 0 0 0 80 120 Z" fill="#1ABCFE" />
<!-- Bottom-right: half-circle -->
<path id="logo-br" d="M 80 120 A 40 120 0 0 0 160 120 Z" fill="#0ACF83" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 771 B

View File

@@ -0,0 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1920 1080" width="1920" height="1080">
<rect id="pill-tl" x="870" y="410" width="80" height="120" rx="40" fill="#F24E1E" />
<rect id="pill-tr" x="970" y="410" width="80" height="120" rx="40" fill="#A259FF" />
<rect id="pill-bl" x="870" y="550" width="80" height="120" rx="40" fill="#1ABCFE" />
<rect id="pill-br" x="970" y="550" width="80" height="120" rx="40" fill="#0ACF83" />
</svg>

After

Width:  |  Height:  |  Size: 446 B

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 97 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 95 KiB

View File

@@ -0,0 +1,9 @@
{
"$schema": "https://hyperframes.heygen.com/schema/hyperframes.json",
"registry": "https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry",
"paths": {
"blocks": "compositions",
"components": "compositions/components",
"assets": "assets"
}
}

758
greenlens-promo/index.html Normal file
View File

@@ -0,0 +1,758 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=1920, height=1080" />
<title>GreenLens Product Promo</title>
<script src="https://cdn.jsdelivr.net/npm/gsap@3.14.2/dist/gsap.min.js"></script>
<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;500;600;700;800&family=Playfair+Display:ital,wght@0,700;0,900;1,700;1,900&display=block"
rel="stylesheet"
/>
<style>
:root {
--forest: #131f16;
--forest-alt: #1c2e21;
--green: #2a5c3f;
--green-mid: #3d7a56;
--green-light: #56a074;
--coral: #e07a50;
--coral-dark: #c96840;
--cream: #f4f1e8;
--cream-alt: #eae6d8;
--muted-sage: #7a8c7d;
--display: "Playfair Display", Georgia, serif;
--body: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
}
* {
box-sizing: border-box;
}
html,
body {
width: 1920px;
height: 1080px;
margin: 0;
overflow: hidden;
background: var(--forest);
color: var(--cream);
font-family: var(--body);
}
[data-composition-id="greenlens-promo"] {
position: relative;
width: 1920px;
height: 1080px;
overflow: hidden;
isolation: isolate;
background: var(--forest);
}
.scene {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
overflow: hidden;
opacity: 0;
}
.scene-content {
position: relative;
z-index: 3;
width: 100%;
height: 100%;
display: flex;
box-sizing: border-box;
}
.brand {
display: flex;
align-items: center;
gap: 16px;
font-family: var(--display);
font-weight: 900;
letter-spacing: 0.08em;
text-transform: uppercase;
}
.brand img {
width: 48px;
height: 48px;
}
.brand-mark {
width: 48px;
height: 48px;
border-radius: 999px;
background:
radial-gradient(circle at 52% 38%, var(--green-light), transparent 34%),
var(--green);
box-shadow: inset 0 0 0 2px rgba(244, 241, 232, 0.24), 0 0 34px rgba(86, 160, 116, 0.36);
}
.eyebrow {
display: inline-flex;
align-items: center;
gap: 12px;
color: var(--coral);
font-size: 18px;
font-weight: 800;
letter-spacing: 0.18em;
text-transform: uppercase;
}
.eyebrow::before {
content: "";
width: 10px;
height: 10px;
border-radius: 999px;
background: var(--green-light);
box-shadow: 0 0 28px rgba(86, 160, 116, 0.9);
}
h1,
h2 {
margin: 0;
font-family: var(--display);
font-weight: 900;
line-height: 0.96;
}
h1 {
font-size: 124px;
max-width: 880px;
}
h2 {
font-size: 92px;
}
em {
color: var(--green-light);
font-style: italic;
}
p {
margin: 0;
}
.caption {
font-size: 34px;
line-height: 1.35;
color: rgba(244, 241, 232, 0.76);
max-width: 680px;
}
.glass {
border: 1px solid rgba(244, 241, 232, 0.14);
background: rgba(19, 31, 22, 0.72);
box-shadow: 0 30px 110px rgba(0, 0, 0, 0.34);
backdrop-filter: blur(18px);
}
.image-fill {
width: 100%;
height: 100%;
object-fit: cover;
}
.scene-1 {
background: var(--forest);
}
.hero-bg {
position: absolute;
inset: 0;
z-index: 0;
}
.hero-bg img {
width: 100%;
height: 100%;
object-fit: cover;
opacity: 0.9;
}
.hero-overlay {
position: absolute;
inset: 0;
z-index: 1;
background:
radial-gradient(circle at 72% 42%, rgba(86, 160, 116, 0.36), transparent 32%),
linear-gradient(90deg, rgba(19, 31, 22, 0.96), rgba(19, 31, 22, 0.72) 46%, rgba(19, 31, 22, 0.2));
}
.scan-line {
position: absolute;
z-index: 2;
left: 0;
right: 0;
top: 0;
height: 3px;
background: linear-gradient(90deg, transparent, var(--green-light), transparent);
box-shadow: 0 0 42px rgba(86, 160, 116, 0.88);
opacity: 0.8;
}
.particle {
position: absolute;
z-index: 2;
width: 10px;
height: 22px;
border-radius: 999px 0 999px 0;
background: rgba(244, 241, 232, 0.22);
}
.scene-1 .scene-content {
flex-direction: column;
justify-content: center;
gap: 34px;
padding: 96px 120px 110px;
}
.scene-1 .brand {
position: absolute;
top: 72px;
left: 120px;
}
.scene-2 .scene-content {
align-items: center;
justify-content: space-between;
gap: 80px;
padding: 86px 128px;
background:
radial-gradient(circle at 30% 20%, rgba(86, 160, 116, 0.22), transparent 26%),
var(--forest);
}
.copy-stack {
width: 680px;
display: flex;
flex-direction: column;
gap: 28px;
}
.phone-stage {
position: relative;
width: 780px;
height: 820px;
display: flex;
align-items: center;
justify-content: center;
}
.botanical-plate {
position: absolute;
width: 620px;
height: 720px;
border-radius: 42px;
overflow: hidden;
opacity: 0.45;
border: 1px solid rgba(244, 241, 232, 0.15);
}
.phone-frame {
position: relative;
z-index: 2;
width: 420px;
height: 744px;
border-radius: 48px;
padding: 18px;
background: #0d130f;
border: 2px solid rgba(244, 241, 232, 0.22);
box-shadow: 0 42px 110px rgba(0, 0, 0, 0.44);
overflow: hidden;
}
.phone-frame video {
width: 100%;
height: 100%;
object-fit: cover;
border-radius: 34px;
}
.scan-bracket {
position: absolute;
z-index: 3;
width: 108px;
height: 108px;
border-color: var(--green-light);
opacity: 0.9;
}
.scan-bracket.tl {
top: 112px;
left: 120px;
border-top: 5px solid;
border-left: 5px solid;
}
.scan-bracket.br {
right: 120px;
bottom: 112px;
border-right: 5px solid;
border-bottom: 5px solid;
}
.badge {
display: inline-flex;
align-items: center;
gap: 12px;
width: fit-content;
padding: 12px 18px;
border-radius: 999px;
color: var(--cream);
font-size: 18px;
font-weight: 800;
letter-spacing: 0.12em;
text-transform: uppercase;
border: 1px solid rgba(86, 160, 116, 0.45);
background: rgba(42, 92, 63, 0.42);
}
.badge::before {
content: "";
width: 12px;
height: 12px;
border-radius: 999px;
background: var(--green-light);
}
.result-card {
position: absolute;
z-index: 4;
right: 26px;
bottom: 156px;
width: 318px;
padding: 24px;
border-radius: 24px;
}
.result-card strong {
display: block;
font-family: var(--display);
font-size: 34px;
line-height: 1;
}
.result-card span {
display: block;
margin-top: 8px;
color: rgba(244, 241, 232, 0.68);
font-size: 20px;
}
.scene-3 .scene-content {
align-items: center;
gap: 74px;
padding: 82px 120px;
background:
radial-gradient(circle at 74% 48%, rgba(224, 122, 80, 0.18), transparent 26%),
var(--forest-alt);
}
.analysis-frame {
width: 820px;
height: 760px;
border-radius: 38px;
overflow: hidden;
position: relative;
}
.analysis-frame img {
width: 100%;
height: 100%;
object-fit: cover;
}
.analysis-frame::after {
content: "";
position: absolute;
inset: 0;
background: linear-gradient(180deg, transparent, rgba(19, 31, 22, 0.64));
}
.care-grid {
display: grid;
grid-template-columns: 1fr;
gap: 18px;
width: 540px;
}
.care-card {
display: flex;
align-items: center;
gap: 20px;
min-height: 122px;
padding: 26px;
border-radius: 24px;
}
.care-icon {
width: 58px;
height: 58px;
display: flex;
align-items: center;
justify-content: center;
border-radius: 18px;
color: var(--forest);
background: var(--green-light);
font-size: 30px;
font-weight: 900;
}
.care-card strong {
display: block;
font-size: 28px;
}
.care-card span {
display: block;
margin-top: 6px;
color: rgba(244, 241, 232, 0.68);
font-size: 20px;
}
.route-line {
position: absolute;
left: 880px;
top: 562px;
z-index: 4;
width: 260px;
height: 5px;
border-radius: 999px;
background: var(--coral);
box-shadow: 0 0 36px rgba(224, 122, 80, 0.8);
}
.scene-4 .scene-content {
flex-direction: column;
justify-content: center;
gap: 34px;
padding: 82px 110px 96px;
background: var(--cream);
color: var(--forest);
}
.scene-4 .eyebrow {
color: var(--coral-dark);
}
.bento {
display: grid;
grid-template-columns: 1.2fr 1fr 1fr;
gap: 22px;
height: 560px;
}
.bento-card {
position: relative;
overflow: hidden;
border-radius: 30px;
border: 1px solid rgba(19, 31, 22, 0.14);
background: var(--forest);
}
.bento-card img {
width: 100%;
height: 100%;
object-fit: cover;
}
.bento-card.bg-track,
.bento-card.bg-collection,
.bento-card.bg-scan,
.final-image {
background-size: cover;
background-position: center;
}
.bento-card.bg-track {
background-image: url("assets/track-feature.png");
}
.bento-card.bg-collection,
.final-image {
background-image: url("assets/plant-collection.png");
}
.bento-card.bg-scan {
background-image: url("assets/scan-feature.png");
}
.bento-card::after {
content: "";
position: absolute;
inset: 0;
background: linear-gradient(180deg, rgba(19, 31, 22, 0.05), rgba(19, 31, 22, 0.75));
}
.bento-label {
position: absolute;
z-index: 2;
left: 30px;
right: 30px;
bottom: 30px;
display: flex;
flex-direction: column;
gap: 8px;
color: var(--cream);
}
.bento-label strong {
font-family: var(--display);
font-size: 44px;
line-height: 1;
}
.bento-label span {
font-size: 20px;
color: rgba(244, 241, 232, 0.76);
}
.timeline {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 18px;
}
.timeline-pill {
min-height: 72px;
display: flex;
align-items: center;
justify-content: center;
border-radius: 18px;
background: #ffffff;
border: 1px solid rgba(19, 31, 22, 0.1);
color: var(--green);
font-weight: 800;
font-size: 24px;
}
.scene-5 .scene-content {
align-items: center;
justify-content: center;
padding: 80px;
background:
radial-gradient(circle at 76% 38%, rgba(86, 160, 116, 0.2), transparent 28%),
var(--forest);
}
.final-card {
position: relative;
z-index: 3;
width: 980px;
min-height: 620px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 28px;
padding: 70px;
border-radius: 44px;
text-align: center;
}
.final-card .brand {
justify-content: center;
font-size: 34px;
}
.final-card h2 {
font-size: 110px;
}
.cta-button {
margin-top: 8px;
display: inline-flex;
align-items: center;
justify-content: center;
min-width: 300px;
height: 76px;
padding: 0 34px;
border-radius: 999px;
background: var(--coral);
color: white;
font-size: 24px;
font-weight: 800;
}
.final-image {
position: absolute;
z-index: 1;
right: 120px;
bottom: 92px;
width: 470px;
height: 580px;
border-radius: 36px;
overflow: hidden;
opacity: 0.34;
border: 1px solid rgba(244, 241, 232, 0.16);
}
.final-image img {
width: 100%;
height: 100%;
object-fit: cover;
}
</style>
</head>
<body>
<div
id="root"
data-composition-id="greenlens-promo"
data-start="0"
data-duration="20"
data-width="1920"
data-height="1080"
>
<section id="scene-1" class="scene clip scene-1" data-start="0" data-duration="3.82" data-track-index="1">
<div class="hero-bg"><img src="assets/hero-plant.png" alt="" /></div>
<div class="hero-overlay"></div>
<div class="scan-line"></div>
<div class="particle" style="left: 1400px; top: 210px"></div>
<div class="particle" style="left: 1620px; top: 660px"></div>
<div class="particle" style="left: 1040px; top: 780px"></div>
<div class="scene-content">
<div class="brand"><img src="assets/favicon.svg" alt="" /><span>GreenLens</span></div>
<div class="eyebrow">Plant care, decoded</div>
<h1>What if every plant came with <em>instructions?</em></h1>
<p class="caption">Instant identification and care guidance for the plants you live with.</p>
</div>
</section>
<section id="scene-2" class="scene clip scene-2" data-start="3.82" data-duration="4.48" data-track-index="1">
<div class="scene-content">
<div class="copy-stack">
<div class="eyebrow">AI Scan</div>
<h2>Open. Scan. <em>Know.</em></h2>
<p class="caption">Scan a leaf and get the name, context, and next step in seconds.</p>
<div class="badge">Live plant match</div>
</div>
<div class="phone-stage">
<div class="botanical-plate"><img class="image-fill" src="assets/scan-feature.png" alt="" /></div>
<div class="scan-bracket tl"></div>
<div class="scan-bracket br"></div>
<div class="phone-frame">
<video src="assets/greenlens.mp4" muted playsinline></video>
</div>
<div class="result-card glass">
<strong>Monstera</strong>
<span>Identified with care tips ready.</span>
</div>
</div>
</div>
</section>
<section id="scene-3" class="scene clip scene-3" data-start="8.3" data-duration="4.54" data-track-index="1">
<div class="scene-content">
<div class="analysis-frame glass"><img src="assets/ai-analysis.png" alt="" /></div>
<div class="copy-stack">
<div class="eyebrow">Botanical Intelligence</div>
<h2>From photo to <em>care plan.</em></h2>
<p class="caption">GreenLens turns identification into practical care decisions.</p>
<div class="care-grid">
<div class="care-card glass"><div class="care-icon">1</div><div><strong>Watering</strong><span>Personalized rhythm</span></div></div>
<div class="care-card glass"><div class="care-icon">2</div><div><strong>Light</strong><span>Location-aware guidance</span></div></div>
<div class="care-card glass"><div class="care-icon">3</div><div><strong>Health</strong><span>Early diagnosis cues</span></div></div>
</div>
</div>
<div class="route-line"></div>
</div>
</section>
<section id="scene-4" class="scene clip scene-4" data-start="12.84" data-duration="3.98" data-track-index="1">
<div class="scene-content">
<div class="eyebrow">Your plant system</div>
<h2>Track watering, growth, notes, and <em>health.</em></h2>
<div class="bento">
<div class="bento-card bg-track"><div class="bento-label"><strong>Track it.</strong><span>Care reminders stay organized.</span></div></div>
<div class="bento-card bg-collection"><div class="bento-label"><strong>Collect.</strong><span>Your plants in one place.</span></div></div>
<div class="bento-card bg-scan"><div class="bento-label"><strong>Grow.</strong><span>Build a better routine.</span></div></div>
</div>
<div class="timeline">
<div class="timeline-pill">Watering</div>
<div class="timeline-pill">Growth</div>
<div class="timeline-pill">Notes</div>
<div class="timeline-pill">Health</div>
</div>
</div>
</section>
<section id="scene-5" class="scene clip scene-5" data-start="16.82" data-duration="3.18" data-track-index="1">
<div class="final-image"></div>
<div class="scene-content">
<div class="final-card glass">
<div class="brand"><span class="brand-mark"></span><span>GreenLens</span></div>
<h2>Scan it.<br />Track it.<br /><em>Grow it.</em></h2>
<div class="cta-button">Start with one plant</div>
</div>
</div>
</section>
</div>
<script>
window.__timelines = window.__timelines || {};
const tl = gsap.timeline({ paused: true });
gsap.set("#scene-1", { opacity: 1 });
gsap.set(".scene:not(#scene-1)", { opacity: 0 });
gsap.set(".phone-frame", { transformPerspective: 1200, rotationY: -8, rotationX: 3 });
gsap.set(".botanical-plate", { transformPerspective: 1200, rotationY: 8, rotationX: -2 });
gsap.set(".analysis-frame", { transformPerspective: 1200, rotationY: 7 });
gsap.set(".final-image", { transformPerspective: 1200, rotationY: -10 });
tl.from("#scene-1 .brand", { y: -28, opacity: 0, duration: 0.55, ease: "power3.out" }, 0);
tl.from("#scene-1 .eyebrow", { y: 34, opacity: 0, duration: 0.55, ease: "power3.out" }, 0.22);
tl.from("#scene-1 h1", { y: 58, opacity: 0, duration: 0.72, ease: "power3.out" }, 0.42);
tl.from("#scene-1 .caption", { y: 30, opacity: 0, duration: 0.48, ease: "power2.out" }, 1.02);
tl.fromTo(".hero-bg img", { scale: 1.04 }, { scale: 1.11, duration: 4.0, ease: "none" }, 0);
tl.fromTo(".scan-line", { y: -20 }, { y: 1120, duration: 2.8, ease: "power1.inOut" }, 0.45);
tl.to(".particle", { y: -38, x: 18, opacity: 0.55, duration: 2.2, stagger: 0.25, ease: "sine.inOut" }, 0.7);
tl.to("#scene-1", { y: -120, filter: "blur(18px)", opacity: 0, duration: 0.4, ease: "power2.in" }, 3.6);
tl.set("#scene-2", { y: 120, filter: "blur(18px)", opacity: 1 }, 3.82);
tl.to("#scene-2", { y: 0, filter: "blur(0px)", duration: 0.55, ease: "power3.out" }, 3.82);
tl.from("#scene-2 .copy-stack > *", { y: 42, opacity: 0, duration: 0.55, stagger: 0.12, ease: "power3.out" }, 4.08);
tl.from(".phone-stage", { x: 90, opacity: 0, duration: 0.75, ease: "power3.out" }, 4.2);
tl.from(".scan-bracket", { scale: 0.55, opacity: 0, duration: 0.48, stagger: 0.08, ease: "back.out(1.8)" }, 4.75);
tl.from(".result-card", { y: 46, opacity: 0, duration: 0.5, ease: "power3.out" }, 5.35);
tl.to(".phone-frame", { y: -12, duration: 1.7, repeat: 2, yoyo: true, ease: "sine.inOut" }, 5.0);
tl.to(".botanical-plate img", { scale: 1.07, duration: 4.2, ease: "none" }, 4.0);
tl.to("#scene-2", { x: -360, filter: "blur(22px)", opacity: 0, duration: 0.35, ease: "power3.in" }, 8.15);
tl.set("#scene-3", { x: 360, filter: "blur(22px)", opacity: 1 }, 8.3);
tl.to("#scene-3", { x: 0, filter: "blur(0px)", duration: 0.48, ease: "power3.out" }, 8.3);
tl.from(".analysis-frame", { scale: 0.9, opacity: 0, duration: 0.58, ease: "power3.out" }, 8.48);
tl.from("#scene-3 .copy-stack > *:not(.care-grid)", { y: 38, opacity: 0, duration: 0.5, stagger: 0.1, ease: "power3.out" }, 8.65);
tl.from(".care-card", { x: 70, opacity: 0, duration: 0.48, stagger: 0.15, ease: "power3.out" }, 9.25);
tl.from(".route-line", { scaleX: 0, opacity: 0, duration: 0.65, ease: "power3.out" }, 9.65);
tl.to(".analysis-frame img", { scale: 1.06, duration: 4.5, ease: "none" }, 8.5);
tl.to(".care-icon", { scale: 1.08, duration: 0.5, repeat: 5, yoyo: true, ease: "sine.inOut" }, 10.0);
tl.to("#scene-3", { scale: 1.08, filter: "blur(18px)", opacity: 0, duration: 0.38, ease: "power2.in" }, 12.62);
tl.set("#scene-4", { scale: 0.94, filter: "blur(18px)", opacity: 1 }, 12.84);
tl.to("#scene-4", { scale: 1, filter: "blur(0px)", duration: 0.48, ease: "power3.out" }, 12.84);
tl.from("#scene-4 .eyebrow, #scene-4 h2", { y: 34, opacity: 0, duration: 0.5, stagger: 0.1, ease: "power3.out" }, 13.0);
tl.from(".bento-card", { y: 80, opacity: 0, duration: 0.55, stagger: 0.12, ease: "power3.out" }, 13.45);
tl.from(".timeline-pill", { y: 24, opacity: 0, duration: 0.36, stagger: 0.07, ease: "power2.out" }, 14.32);
tl.to(".timeline-pill", { y: -8, duration: 0.55, repeat: 3, yoyo: true, stagger: 0.08, ease: "sine.inOut" }, 15.0);
tl.to("#scene-4", { scale: 0.88, filter: "blur(18px)", opacity: 0, duration: 0.38, ease: "power2.in" }, 16.62);
tl.set("#scene-5", { scale: 1.1, filter: "blur(16px)", opacity: 1 }, 16.82);
tl.to("#scene-5", { scale: 1, filter: "blur(0px)", duration: 0.52, ease: "power3.out" }, 16.82);
tl.from(".final-card", { y: 52, opacity: 0, duration: 0.68, ease: "power3.out" }, 17.0);
tl.from(".final-card h2", { y: 42, opacity: 0, duration: 0.62, ease: "power3.out" }, 17.22);
tl.from(".cta-button", { y: 30, opacity: 0, scale: 0.92, duration: 0.5, ease: "back.out(1.7)" }, 17.82);
tl.from(".final-image", { x: 100, opacity: 0, duration: 0.8, ease: "power3.out" }, 17.0);
tl.to(".cta-button", { boxShadow: "0 0 44px rgba(224, 122, 80, 0.55)", duration: 0.8, repeat: 2, yoyo: true, ease: "sine.inOut" }, 18.35);
window.__timelines["greenlens-promo"] = tl;
</script>
</body>
</html>

View File

@@ -0,0 +1,5 @@
{
"id": "greenlens-promo",
"name": "greenlens-promo",
"createdAt": "2026-04-26T21:56:37.923Z"
}