feat(website): lamborghini-inspired landing page
Single-page marketing site for agentmemory generated from the getdesign@latest 'lamborghini' design system. - DESIGN.md at repo root: the full 288-line design brief (black canvas, gold accent, Neo-Grotesk display, zero border-radius, hexagonal motifs). Future UI PRs should read this file first. - website/index.html: hero + stats + primitives + live terminal + competitor comparison + agents grid + install + footer. - website/styles.css: black-first palette (#000 canvas, #FFC000 accent, #202020 surface), Archivo display at 160px hero, JetBrains Mono for the terminal, sharp-edge buttons, reveal-on-intersect. - website/script.js: vanilla, zero deps. - animated canvas memory graph in the hero with a hexagonal pause control - stat counters that run on first intersect - 3D mouse-tilt on primitive cards - typewriter terminal demo of memory.recall and memory.consolidate with a REPLAY button - click-to-copy install boxes with success feedback - top scroll progress rail in gold - full prefers-reduced-motion short-circuit No framework, no build step. Drop behind any static host.
This commit is contained in:
@@ -0,0 +1,288 @@
|
||||
# Design System Inspired by Lamborghini
|
||||
|
||||
## 1. Visual Theme & Atmosphere
|
||||
|
||||
Lamborghini's website is a cathedral of darkness — a digital stage where jet-black surfaces stretch infinitely and every element emerges from the void like a machine under a spotlight. The page is almost entirely black. Not dark gray, not near-black — true, uncompromising black (`#000000`) that saturates the viewport and refuses to yield. Into this abyss, white type and Lamborghini Gold (`#FFC000`) are deployed with surgical precision, creating a visual language that feels like walking through a nighttime motorsport event where every surface absorbs light except the things that matter.
|
||||
|
||||
The hero is a full-viewport video — dark, cinematic, immersive — showing event footage or vehicle reveals with the Lamborghini bull logo floating ethereally above. The navigation is minimal: a centered bull logo, a "MENU" hamburger on the left, and search/bookmark icons on the right, all rendered in white against the black canvas. There are no borders, no visible nav containers, no background color on the header — just white marks floating in darkness. The overall mood is nocturnal luxury: exclusive, theatrical, and deliberately intimidating. Each section transition is a scroll through darkness into the next revelation.
|
||||
|
||||
Typography is the voice of this darkness. LamboType — a custom Neo-Grotesk typeface created by Character Type and design agency Strichpunkt — is used for everything from 120px uppercase display headlines to 10px micro labels. Its distinctive 12° angled terminals are inspired by the aerodynamic lines of Lamborghini's super sports cars, and its proportions range from Normal to Ultracompressed width. Headlines SHOUT in uppercase at enormous scales with tight line-heights (0.92 at 120px), creating dense blocks of text that feel stamped from steel. The typeface carries hexagonal geometric DNA — constructed from hexagons, three-armed stars, and circles — that echoes throughout the interface in the hexagonal pause button and UI icons. Built on Bootstrap grid with 68 Element Plus/UI components, the technical infrastructure is substantial beneath the theatrical surface.
|
||||
|
||||
**Key Characteristics:**
|
||||
- True black (`#000000`) dominant surfaces with white and gold as the only relief colors
|
||||
- LamboType custom Neo-Grotesk font with 12° angled terminals inspired by aerodynamic car lines
|
||||
- Lamborghini Gold (`#FFC000`) as the sole accent color — used exclusively for primary CTA buttons
|
||||
- All-uppercase display typography at extreme scales (120px, 80px, 54px) with tight line-heights
|
||||
- Full-viewport video heroes with cinematic event/vehicle content
|
||||
- Zero border-radius on buttons — sharp, angular, uncompromising rectangles
|
||||
- Hexagonal motifs in UI elements (pause button, icon system) echoing brand geometry
|
||||
- Bootstrap grid system + Element Plus/UI 68 components underneath
|
||||
- Transparent ghost buttons with white borders at 50% opacity as the secondary CTA pattern
|
||||
|
||||
## 2. Color Palette & Roles
|
||||
|
||||
### Primary
|
||||
- **Lamborghini Gold** (`#FFC000`): The signature accent color — a warm, saturated amber-gold (rgb 255, 192, 0) used exclusively for primary action buttons ("Discover More", "Tickets", "Start Configuration"). The only chromatic color in the entire interface, it ignites against the black canvas like a headlight cutting through night
|
||||
- **Pure White** (`#FFFFFF`): Primary text color on dark surfaces, logo rendering, nav elements, and light-mode button fills — the voice that speaks from the darkness
|
||||
|
||||
### Secondary & Accent
|
||||
- **Dark Gold** (`#917300`): Hover/pressed state for gold buttons — a deep amber (rgb 145, 115, 0) that darkens the gold to signal interaction
|
||||
- **Gold Text** (`#FFCE3E`): Slightly lighter gold variant (rgb 255, 206, 62) used for inline text accents and highlighted labels
|
||||
- **Cyan Pulse** (`#29ABE2`): Electric blue-cyan (rgb 41, 171, 226) appearing as an informational accent and interactive element highlight
|
||||
- **Link Blue** (`#3860BE`): Medium blue (rgb 56, 96, 190) used universally for link hover states across all text colors
|
||||
|
||||
### Surface & Background
|
||||
- **Absolute Black** (`#000000`): The dominant surface color — used for page background, hero sections, header, footer, and most containers
|
||||
- **Charcoal** (`#202020`): Elevated dark surface (rgb 32, 32, 32) — the primary "dark gray" for cards, panels, and text containers sitting above the black canvas
|
||||
- **Dark Iron** (`#181818`): Subtle surface variant (rgb 24, 24, 24) — barely distinguishable from black, used for footer and deep sections
|
||||
- **Overlay Black** (`rgba(0,0,0,0.7)`): Semi-transparent overlay for modals and video dimming
|
||||
- **Near White** (`#F8F8F8`): Rare light surface (rgb 248, 248, 248) for content blocks in white-mode sections
|
||||
- **Mist** (`#E6E6E6`): Light gray surface for secondary light-mode containers
|
||||
|
||||
### Neutrals & Text
|
||||
- **Pure White** (`#FFFFFF`): Primary text on dark backgrounds — headlines, body, nav labels
|
||||
- **Smoke** (`#F5F5F5`): Secondary text on dark surfaces — slightly softer than pure white
|
||||
- **Graphite** (`#494949`): Dark gray text on light surfaces (rgb 73, 73, 73)
|
||||
- **Ash** (`#7D7D7D`): Mid-range gray for muted text, timestamps, and metadata (rgb 125, 125, 125)
|
||||
- **Steel** (`#969696`): Lighter gray for disabled text and subtle labels (rgb 150, 150, 150)
|
||||
- **Slate** (`#666666`): Alternative mid-gray for secondary content
|
||||
- **Iron** (`#555555`): Dark mid-gray for body text variants
|
||||
- **Shadow** (`#313131`): Very dark gray for text on dark surfaces where white is too strong
|
||||
|
||||
### Semantic & Accent
|
||||
- **Cyan Pulse** (`#29ABE2`): Used for informational highlights and interactive feedback
|
||||
- **Link Blue** (`#3860BE`): Universal hover state for all hyperlinks
|
||||
- **Teal Action** (`#1EAEDB`): Button hover background for transparent/ghost variants (rgb 30, 174, 219)
|
||||
|
||||
### Gradient System
|
||||
- No explicit gradients in the color palette — the dark-to-light progression is achieved through surface layering: `#000000` → `#181818` → `#202020` → `#494949` → `#7D7D7D`
|
||||
- Video heroes use natural atmospheric gradients from the content itself
|
||||
- Top-of-page gradient: subtle dark-to-darker fade at the edges of full-bleed imagery
|
||||
|
||||
## 3. Typography Rules
|
||||
|
||||
### Font Family
|
||||
- **Display & UI**: `LamboType`, Roboto, Helvetica Neue, Arial — custom Neo-Grotesk typeface by Character Type for Lamborghini's 2024 brand refresh. Available in widths from Normal to Ultracompressed and weights from Light (300) to Black. Features 12° angled terminals inspired by aerodynamic car geometry, hexagonal construction logic, and support for 200+ languages including Latin, Cyrillic, and Greek
|
||||
- **Fallback/UI**: `Open Sans` — used for some button/form contexts as system fallback
|
||||
- **No italic variants** observed on the marketing site — the brand voice is always upright
|
||||
|
||||
### Hierarchy
|
||||
|
||||
| Role | Size | Weight | Line Height | Letter Spacing | Notes |
|
||||
|------|------|--------|-------------|----------------|-------|
|
||||
| Hero Display | 120px (7.50rem) | 400 | 0.92 | normal | LamboType, uppercase, maximum impact |
|
||||
| Display 2 | 80px (5.00rem) | 400 | 1.13 | normal | LamboType, uppercase, major section titles |
|
||||
| Section Title | 54px (3.38rem) | 400 | 1.19 | normal | LamboType, uppercase |
|
||||
| Sub-section | 40px (2.50rem) | 400 | 1.15 | normal | LamboType, uppercase |
|
||||
| Feature Heading | 27px (1.69rem) | 400 | 1.37 | normal | LamboType, uppercase |
|
||||
| Card Title | 24px (1.50rem) | 400 | — | normal | LamboType |
|
||||
| Body Large | 18px (1.13rem) | 400 | 1.56 | normal | LamboType, mixed case and uppercase variants |
|
||||
| Body / UI | 16px (1.00rem) | 400/700 | 1.50 | normal/0.16px | LamboType, primary body text |
|
||||
| Button Large | 16px (1.00rem) | 400 | 1.50 | normal | Gold CTA buttons |
|
||||
| Button Standard | 14.4px (0.90rem) | 300/700 | 1.00 | 0.14–0.2px | LamboType, uppercase, ghost buttons |
|
||||
| Button Small | 13px (0.81rem) | 300/500 | 1.20 | 0.13–0.2px | LamboType, compact button variant |
|
||||
| Caption | 14px (0.88rem) | 600/700 | 1.14–1.50 | -0.42px | LamboType, uppercase, negative tracking |
|
||||
| Label | 12px (0.75rem) | 400/500 | 1.83 | 0.96px | LamboType, uppercase badges and micro labels |
|
||||
| Micro | 10px (0.63rem) | 400 | 1.00–2.00 | 0.225px | LamboType, uppercase, smallest text |
|
||||
|
||||
### Principles
|
||||
- **ALL-CAPS is the default voice**: Display and feature headings are universally uppercase. This creates a shouting, commanding tone that matches the brand's aggression
|
||||
- **Extreme scale range**: From 120px heroes to 10px micro labels — a 12:1 ratio that creates dramatic visual hierarchy
|
||||
- **Tight line-heights at scale**: Display sizes use 0.92-1.19 line-height, creating dense, compressed blocks of type that feel stamped rather than typeset
|
||||
- **Weight 400 dominates**: Unlike many design systems that use bold for emphasis, Lamborghini's regular weight carries the headlines — the typeface itself is so distinctive it doesn't need weight variation
|
||||
- **Negative tracking on captions**: -0.42px letter-spacing on 14px captions creates a compressed, technical aesthetic
|
||||
- **Positive tracking on micro text**: +0.225px at 10px ensures legibility at the smallest sizes
|
||||
- **Single typeface discipline**: LamboType handles everything — the 12° angled terminals and hexagonal geometry provide visual coherence across all sizes
|
||||
|
||||
## 4. Component Stylings
|
||||
|
||||
### Buttons
|
||||
All buttons use **zero border-radius** — sharp, angular rectangles that echo the aggressive lines of Lamborghini vehicles.
|
||||
|
||||
**Gold Accent CTA** — The primary action:
|
||||
- Default: bg `#FFC000` (Lamborghini Gold), text `#000000`, padding 24px, fontSize 16px, fontWeight 400, borderRadius 0px, no border
|
||||
- Hover: bg `#917300` (Dark Gold), darkens significantly
|
||||
- Class: `btn-accent btn-large`
|
||||
- Used for: "Discover More", "Tickets", "Start Configuration"
|
||||
|
||||
**Transparent Ghost** — The secondary action on dark backgrounds:
|
||||
- Default: bg transparent, text `#FFFFFF`, border 1px solid `#FFFFFF`, padding 16px, opacity 0.5
|
||||
- Hover: bg `#1EAEDB` (Teal Action), text white, opacity 0.7
|
||||
- Focus: bg `#1EAEDB`, border 1px solid `#000000`, outline 2px solid `#000000`
|
||||
- Used for: secondary CTAs on hero sections and dark panels
|
||||
|
||||
**White Filled** — Light-mode primary:
|
||||
- Default: bg `#FFFFFF`, text `#202020`, no border
|
||||
- Used for: CTAs on dark sections where gold isn't appropriate
|
||||
|
||||
**Black Filled** — Dark filled variant:
|
||||
- Default: bg `#000000`, text `#202020`
|
||||
- Used for: Inverted CTA on light sections
|
||||
|
||||
**Gray Neutral** — Subtle action:
|
||||
- Default: bg `#969696`, text `#202020`
|
||||
- Used for: secondary/tertiary actions, badge-like buttons
|
||||
|
||||
### Cards & Containers
|
||||
- Background: `#202020` (Charcoal) on black canvas, or `#000000` on lighter sections
|
||||
- Border: `0px 1px solid #202020` bottom borders for section dividers
|
||||
- Border-radius: 0px (completely sharp corners)
|
||||
- Shadow: minimal, uses overlay opacity for depth
|
||||
- Content: full-bleed photography + overlaid text in white
|
||||
|
||||
### Inputs & Forms
|
||||
- Minimal form presence on the marketing site
|
||||
- Switch elements: border-radius 20px (the only rounded element), border 1px solid `#DDDDDD`
|
||||
- Cookie banner input style: white text on black with `#7D7D7D` borders
|
||||
|
||||
### Navigation
|
||||
- **Desktop**: Centered bull logo, "MENU" hamburger with icon on left, search icon + bookmarks icon on right
|
||||
- **Background**: Transparent (inherits black page background)
|
||||
- **Sticky**: Fixed to top, floats above content
|
||||
- **No visible borders or shadows** — elements float in the darkness
|
||||
- **"MENU" label**: White text at 14px weight 400, uppercase, accompanies hamburger icon
|
||||
- **Hexagonal motifs**: Pause button on hero sections uses hexagonal outline shape
|
||||
|
||||
### Image Treatment
|
||||
- **Hero**: Full-viewport video sections (100vh) with cinematic event/vehicle footage
|
||||
- **Event photography**: Full-bleed aerial shots of Lamborghini Arena events
|
||||
- **Vehicle imagery**: High-contrast studio shots on dark backgrounds, full-width
|
||||
- **Aspect ratios**: Predominantly 16:9 and wider for cinematic feel
|
||||
- **Dark gradient overlays**: Subtle darkening at top/bottom edges of video to ensure text legibility
|
||||
|
||||
### Distinctive Components
|
||||
- **Hexagonal Pause Button**: Video control uses a hexagonal outline (matching the brand's geometric DNA from the typeface), positioned bottom-right of hero sections
|
||||
- **Progress Bar**: Thin white line at bottom of hero sections indicating video/slide progress
|
||||
- **Badge/Tag**: bg `#969696`, text white, padding 8px, fontSize 10px, borderRadius 2px — tiny metallic pills
|
||||
|
||||
## 5. Layout Principles
|
||||
|
||||
### Spacing System
|
||||
- **Base unit**: 8px
|
||||
- **Full scale**: 2px, 4px, 5px, 8px, 10px, 12px, 15px, 16px, 20px, 24px, 32px, 40px, 48px, 56px
|
||||
- **Button padding**: 16px (ghost), 24px (gold accent)
|
||||
- **Section padding**: 48–56px vertical, 40px horizontal
|
||||
- **Small spacing**: 2–5px for fine adjustments (badge padding, border spacing)
|
||||
|
||||
### Grid & Container
|
||||
- **Framework**: Bootstrap grid system (container + row + col)
|
||||
- **Max width**: 1440px (largest breakpoint)
|
||||
- **Columns**: Standard 12-column Bootstrap grid
|
||||
- **Full-bleed**: Hero sections break out of grid to fill viewport edge-to-edge
|
||||
- **Content areas**: Centered within 1200px max-width containers
|
||||
|
||||
### Whitespace Philosophy
|
||||
Lamborghini uses darkness as whitespace. The generous black expanses between content blocks serve the same function as white space in a light design — creating breathing room that elevates each element to the status of exhibit. A model name floating in the middle of a black viewport has the same visual weight as a gallery piece on a white wall. The absence of color IS the design.
|
||||
|
||||
### Border Radius Scale
|
||||
| Value | Context |
|
||||
|-------|---------|
|
||||
| 0px | Default for everything — buttons, cards, containers, images |
|
||||
| 1px | Subtle span elements |
|
||||
| 2px | Badges, close buttons, cookie elements — barely perceptible |
|
||||
| 20px | Toggle switches only — the sole rounded element |
|
||||
|
||||
## 6. Depth & Elevation
|
||||
|
||||
| Level | Treatment | Use |
|
||||
|-------|-----------|-----|
|
||||
| Level 0 (Abyss) | `#000000` flat | Page background, deepest layer |
|
||||
| Level 1 (Surface) | `#181818` or `#202020` | Cards, content panels, elevated sections |
|
||||
| Level 2 (Overlay) | `rgba(0,0,0,0.7)` | Modal backdrops, video dimming |
|
||||
| Level 3 (Fog) | `rgba(0,0,0,0.5)` | Lighter overlays, hover states |
|
||||
| Level 4 (Mist) | `rgba(0,0,0,0.25)` | Subtle depth hints |
|
||||
|
||||
### Shadow Philosophy
|
||||
Lamborghini achieves depth through surface color layering rather than shadows. On a black canvas, traditional drop shadows are invisible — instead, the system creates elevation by shifting from absolute black to progressively lighter dark grays: `#000000` → `#181818` → `#202020` → `#494949`. This "darkness gradient" approach means that elevated elements are literally lighter than their surroundings, inverting the traditional shadow model.
|
||||
|
||||
### Decorative Depth
|
||||
- Full-bleed video provides atmospheric depth through cinematic lighting
|
||||
- The hexagonal pause button floats with a thin white outline stroke
|
||||
- Progress bars at hero section bottoms create a subtle horizon line
|
||||
- No gradients, glows, or blur effects on UI elements — the photography provides all visual richness
|
||||
|
||||
## 7. Do's and Don'ts
|
||||
|
||||
### Do
|
||||
- Use absolute black (`#000000`) as the primary background — never dark gray as a substitute
|
||||
- Apply Lamborghini Gold (`#FFC000`) exclusively for primary CTA buttons — never for decorative purposes
|
||||
- Set all display headings in uppercase with LamboType — the brand voice is always SHOUTING
|
||||
- Use zero border-radius on buttons and cards — sharp angles are non-negotiable
|
||||
- Maintain tight line-heights (0.92–1.19) on display type to create dense, architectural text blocks
|
||||
- Use the transparent ghost button (white border, 50% opacity) as the secondary CTA on dark backgrounds
|
||||
- Let full-viewport video/photography carry emotional weight — UI is infrastructure, not decoration
|
||||
- Reserve hexagonal geometry for UI icons and the video control button
|
||||
- Use weight 400 (regular) for headlines — the typeface is distinctive enough without bold emphasis
|
||||
- Keep the gray palette achromatic — all neutrals are pure gray without color tinting
|
||||
|
||||
### Don't
|
||||
- Introduce additional accent colors beyond gold — the monochrome-plus-gold system is sacred
|
||||
- Apply border-radius to buttons or cards — curved edges contradict the angular vehicle aesthetic
|
||||
- Use LamboType in italic or decorative styles — the brand is always upright and direct
|
||||
- Add gradients to buttons or surfaces — depth comes from surface layering, not blending
|
||||
- Use light backgrounds as the primary canvas — darkness is the default state, light is the exception
|
||||
- Mix lowercase into display headings — the uppercase convention communicates authority and power
|
||||
- Add hover animations with scale or translate — interactions should be color-only (background/opacity shifts)
|
||||
- Use Open Sans for display text — LamboType must handle all visible typography
|
||||
- Create busy layouts with many small elements — Lamborghini's design is about singular, bold statements
|
||||
- Apply shadows to elements — on a black canvas, shadows are meaningless; use surface color shifts instead
|
||||
|
||||
## 8. Responsive Behavior
|
||||
|
||||
### Breakpoints
|
||||
| Name | Width | Key Changes |
|
||||
|------|-------|-------------|
|
||||
| Mobile Small | <425px | Single column, reduced type scale, stacked buttons |
|
||||
| Mobile | 425-576px | Single column, hamburger nav, hero text ~40px |
|
||||
| Tablet Small | 576-768px | 2-column grid begins, padding adjusts |
|
||||
| Tablet | 768-1024px | 2-column layout, expanded hero, vehicle cards side-by-side |
|
||||
| Desktop | 1024-1280px | Full navigation, 3+ column grids, display text at 80px |
|
||||
| Desktop Large | 1280-1440px | Full layout, hero at 120px display, max-width containers |
|
||||
| Wide | >1440px | Content centered, margins expand, hero fills viewport |
|
||||
|
||||
### Touch Targets
|
||||
- Gold CTA buttons: 48px+ minimum height with 24px padding (exceeds WCAG 44×44px)
|
||||
- Ghost buttons: 48px+ with 16px padding
|
||||
- Hamburger menu: large touch target (~48px square)
|
||||
- Hexagonal pause button: approximately 48px diameter
|
||||
|
||||
### Collapsing Strategy
|
||||
- **Navigation**: Always hamburger-based ("MENU" + icon) — no horizontal nav expansion on any breakpoint
|
||||
- **Hero video**: Maintains full-viewport height across all breakpoints, adjusting object-fit
|
||||
- **Display type**: Scales from 120px (desktop) → 80px (tablet) → 54px/40px (mobile)
|
||||
- **Button layout**: Side-by-side on desktop, stacks vertically on mobile
|
||||
- **Grid columns**: 3-column → 2-column → 1-column progression
|
||||
- **Section spacing**: Reduces from 56px → 40px → 24px vertical padding
|
||||
|
||||
### Image Behavior
|
||||
- Hero videos use `object-fit: cover` to maintain cinematic framing at all sizes
|
||||
- Vehicle images scale within their containers with maintained aspect ratios
|
||||
- Event photography crops to viewport width on narrow screens
|
||||
- Background images darken at edges to maintain text contrast on all viewports
|
||||
|
||||
## 9. Agent Prompt Guide
|
||||
|
||||
### Quick Color Reference
|
||||
- Primary CTA: "Lamborghini Gold (#FFC000)"
|
||||
- Background: "Absolute Black (#000000)"
|
||||
- Surface: "Charcoal (#202020)"
|
||||
- Heading text: "Pure White (#FFFFFF)"
|
||||
- Body text: "Ash (#7D7D7D)"
|
||||
- Link hover: "Link Blue (#3860BE)"
|
||||
- Accent: "Cyan Pulse (#29ABE2)"
|
||||
- Border: "Pure White (#FFFFFF) at 50% opacity"
|
||||
|
||||
### Example Component Prompts
|
||||
- "Create a hero section with a full-viewport black background, the model name 'TEMERARIO' in LamboType at 120px uppercase weight 400 white text with 0.92 line-height, centered vertically, with a Lamborghini Gold (#FFC000) 'Discover More' button below — sharp corners, 0px radius, 24px padding, black text"
|
||||
- "Design a transparent ghost button with 1px solid white border at 50% opacity, white text at 14.4px uppercase with 0.2px letter-spacing, padding 16px, on a black background — hover state changes to Teal Action (#1EAEDB) background with 70% opacity"
|
||||
- "Build a navigation bar with zero visible background on absolute black, a centered bull logo, 'MENU' text label with hamburger icon on the left, and search + bookmark icons on the right — all in white, sticky position"
|
||||
- "Create a news card grid on charcoal (#202020) background with white headlines at 27px uppercase, body text in #7D7D7D at 16px, and a white underlined 'Read More' link that turns #3860BE on hover"
|
||||
- "Design a section divider using a 1px solid bottom border in #202020 on a black canvas — the elevation difference is purely through surface color shift, not shadow"
|
||||
|
||||
### Iteration Guide
|
||||
When refining existing screens generated with this design system:
|
||||
1. Focus on ONE component at a time — Lamborghini's system is extreme and every element must feel aggressive
|
||||
2. Reference specific color names and hex codes from this document — the palette has only about 5 active colors
|
||||
3. Use natural language descriptions, not CSS values — "sharp-cut golden rectangle" not "border-radius: 0px; background: #FFC000"
|
||||
4. Describe the desired "feel" alongside specific measurements — "floating in total darkness" communicates the black canvas better than "background: #000000"
|
||||
5. Remember that UPPERCASE IS THE DEFAULT — if text isn't uppercase at display sizes, it probably should be
|
||||
@@ -0,0 +1,45 @@
|
||||
# agentmemory website
|
||||
|
||||
Lamborghini-inspired landing page for agentmemory. True-black canvas, gold accent,
|
||||
uppercase Archivo, zero framework deps.
|
||||
|
||||
## Run
|
||||
|
||||
Any static server. Examples:
|
||||
|
||||
```bash
|
||||
cd website
|
||||
python3 -m http.server 4000
|
||||
# or
|
||||
npx serve .
|
||||
```
|
||||
|
||||
Open http://localhost:4000
|
||||
|
||||
## Deploy
|
||||
|
||||
Drop the directory behind any static host — Vercel, Netlify, Cloudflare Pages,
|
||||
GitHub Pages, S3, or a plain nginx root. Three files ship:
|
||||
|
||||
- `index.html`
|
||||
- `styles.css`
|
||||
- `script.js`
|
||||
|
||||
No build step. No bundler. Google Fonts is the only external fetch.
|
||||
|
||||
## Interactive elements
|
||||
|
||||
- **Hero memory graph** — live canvas network of nodes/edges; click the hexagonal
|
||||
button to pause.
|
||||
- **Stat counters** — animate up on first intersect.
|
||||
- **Primitive cards** — 3D tilt on mouse move.
|
||||
- **Live terminal** — typewriter replay of `memory.recall` and `memory.consolidate`;
|
||||
click `REPLAY` to run again.
|
||||
- **Install boxes** — click to copy command, success state confirmed.
|
||||
- **Scroll rail** — thin gold progress bar fixed to the top of the viewport.
|
||||
|
||||
## Design system
|
||||
|
||||
Source of truth: `/DESIGN.md` in the repo root (generated by
|
||||
`npx getdesign@latest add lamborghini`). Colors, type, and component rules live
|
||||
there. This site is the first implementation of that system.
|
||||
@@ -0,0 +1,349 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>AGENTMEMORY — PERSISTENT MEMORY FOR AI CODING AGENTS</title>
|
||||
<meta
|
||||
name="description"
|
||||
content="The memory layer your coding agent should have had from day one. 95.2% retrieval R@5. 92% fewer tokens. 0 external databases. Works with every agent."
|
||||
/>
|
||||
<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=Archivo:wght@400;500;700;900&family=JetBrains+Mono:wght@400;500&display=swap"
|
||||
rel="stylesheet"
|
||||
/>
|
||||
<link rel="stylesheet" href="./styles.css" />
|
||||
</head>
|
||||
<body class="abyss">
|
||||
<div class="scroll-progress" aria-hidden="true"><span></span></div>
|
||||
|
||||
<header class="nav">
|
||||
<button class="nav__menu" aria-label="Menu" id="menu-btn">
|
||||
<span class="nav__bar"></span>
|
||||
<span class="nav__bar"></span>
|
||||
<span class="nav__bar"></span>
|
||||
<span class="nav__menu-label">MENU</span>
|
||||
</button>
|
||||
<a href="#top" class="nav__mark" aria-label="agentmemory home">
|
||||
<svg viewBox="0 0 120 120" width="44" height="44" aria-hidden="true">
|
||||
<polygon
|
||||
points="60,6 111,34 111,86 60,114 9,86 9,34"
|
||||
fill="none"
|
||||
stroke="#fff"
|
||||
stroke-width="2.5"
|
||||
/>
|
||||
<polygon
|
||||
points="60,28 90,44 90,76 60,92 30,76 30,44"
|
||||
fill="none"
|
||||
stroke="#FFC000"
|
||||
stroke-width="2.5"
|
||||
/>
|
||||
<circle cx="60" cy="60" r="6" fill="#FFC000" />
|
||||
</svg>
|
||||
</a>
|
||||
<div class="nav__right">
|
||||
<a href="#install" class="nav__link">INSTALL</a>
|
||||
<a
|
||||
class="nav__link"
|
||||
href="https://github.com/rohitg00/agentmemory"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
>GITHUB</a
|
||||
>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main id="top">
|
||||
<section class="hero" aria-labelledby="hero-title">
|
||||
<canvas class="hero__graph" id="memgraph" aria-hidden="true"></canvas>
|
||||
<div class="hero__vignette" aria-hidden="true"></div>
|
||||
<div class="hero__content">
|
||||
<div class="hero__chip">ZERO EXTERNAL DATABASES · v0.8.13</div>
|
||||
<h1 class="hero__title" id="hero-title">
|
||||
<span class="hero__word hero__word--slide">AGENT</span
|
||||
><span class="hero__word hero__word--slide hero__word--accent"
|
||||
>MEMORY</span
|
||||
>
|
||||
</h1>
|
||||
<p class="hero__lede">
|
||||
THE MEMORY LAYER YOUR CODING AGENT SHOULD HAVE HAD FROM DAY ONE.
|
||||
CAPTURE EVERY SESSION. RECALL IN MILLISECONDS. RUN ANYWHERE.
|
||||
</p>
|
||||
<div class="hero__cta">
|
||||
<a href="#install" class="btn btn--accent" data-hex>
|
||||
START IN 60 SECONDS
|
||||
</a>
|
||||
<a href="#live" class="btn btn--ghost">SEE IT MOVE</a>
|
||||
</div>
|
||||
</div>
|
||||
<button class="hero__pause" id="graph-toggle" aria-label="Pause animation">
|
||||
<svg viewBox="0 0 48 48" width="44" height="44" aria-hidden="true">
|
||||
<polygon
|
||||
points="24,2 44,13 44,35 24,46 4,35 4,13"
|
||||
fill="none"
|
||||
stroke="#fff"
|
||||
stroke-width="1.8"
|
||||
/>
|
||||
<g id="pause-icon">
|
||||
<rect x="17" y="16" width="4" height="16" fill="#fff" />
|
||||
<rect x="27" y="16" width="4" height="16" fill="#fff" />
|
||||
</g>
|
||||
</svg>
|
||||
</button>
|
||||
<div class="hero__rail" aria-hidden="true"><span id="rail"></span></div>
|
||||
</section>
|
||||
|
||||
<section class="stats" aria-label="Benchmarks">
|
||||
<div class="stats__row">
|
||||
<article class="stat" data-stat>
|
||||
<div class="stat__num" data-count="95.2" data-suffix="%">0%</div>
|
||||
<div class="stat__label">RETRIEVAL R@5 · LONGMEMEVAL-S</div>
|
||||
</article>
|
||||
<article class="stat" data-stat>
|
||||
<div class="stat__num" data-count="92" data-suffix="%">0%</div>
|
||||
<div class="stat__label">FEWER INPUT TOKENS PER SESSION</div>
|
||||
</article>
|
||||
<article class="stat" data-stat>
|
||||
<div class="stat__num" data-count="44">0</div>
|
||||
<div class="stat__label">MCP TOOLS</div>
|
||||
</article>
|
||||
<article class="stat" data-stat>
|
||||
<div class="stat__num" data-count="12">0</div>
|
||||
<div class="stat__label">AUTOHOOKS</div>
|
||||
</article>
|
||||
<article class="stat" data-stat>
|
||||
<div class="stat__num" data-count="0">0</div>
|
||||
<div class="stat__label">EXTERNAL DATABASES</div>
|
||||
</article>
|
||||
<article class="stat" data-stat>
|
||||
<div class="stat__num" data-count="769">0</div>
|
||||
<div class="stat__label">TESTS PASSING</div>
|
||||
</article>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="primitives" aria-labelledby="prim-title">
|
||||
<header class="section__head">
|
||||
<span class="section__eyebrow">THE STACK</span>
|
||||
<h2 id="prim-title" class="section__title">
|
||||
THREE PRIMITIVES.<br />NO FRAMEWORK TAX.
|
||||
</h2>
|
||||
<p class="section__lede">
|
||||
BUILT ON THE iii ENGINE — EVERY MEMORY OPERATION IS A WORKER, A
|
||||
FUNCTION, OR A TRIGGER. NO REDIS. NO KAFKA. NO POSTGRES. THE ENTIRE
|
||||
RUNTIME IS ONE PROCESS.
|
||||
</p>
|
||||
</header>
|
||||
<div class="primitives__grid">
|
||||
<article class="prim-card" data-tilt>
|
||||
<div class="prim-card__glyph">01</div>
|
||||
<h3 class="prim-card__title">HOOKS</h3>
|
||||
<p class="prim-card__text">
|
||||
12 AUTO-CAPTURE HOOKS PIPED INTO EVERY CODING AGENT. EVERY TOOL
|
||||
CALL, EVERY PROMPT, EVERY STOP BECOMES A COMPRESSED OBSERVATION.
|
||||
</p>
|
||||
</article>
|
||||
<article class="prim-card" data-tilt>
|
||||
<div class="prim-card__glyph">02</div>
|
||||
<h3 class="prim-card__title">RECALL</h3>
|
||||
<p class="prim-card__text">
|
||||
TRIPLE-STREAM RETRIEVAL — BM25 + VECTOR + KNOWLEDGE GRAPH. RERANKED
|
||||
ON DEVICE. P50 UNDER 20MS ON A LAPTOP.
|
||||
</p>
|
||||
</article>
|
||||
<article class="prim-card" data-tilt>
|
||||
<div class="prim-card__glyph">03</div>
|
||||
<h3 class="prim-card__title">CONSOLIDATE</h3>
|
||||
<p class="prim-card__text">
|
||||
HOURLY SWEEPS COMPRESS RAW OBSERVATIONS INTO SEMANTIC MEMORIES.
|
||||
DUPLICATES MERGED. STALE ROWS DECAYED. AUDIT ROW EMITTED EVERY
|
||||
DELETE.
|
||||
</p>
|
||||
</article>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="live" id="live" aria-labelledby="live-title">
|
||||
<header class="section__head">
|
||||
<span class="section__eyebrow">LIVE</span>
|
||||
<h2 id="live-title" class="section__title">
|
||||
MEMORY THAT TYPES BACK.
|
||||
</h2>
|
||||
</header>
|
||||
<div class="terminal" data-terminal>
|
||||
<div class="terminal__chrome">
|
||||
<span class="terminal__dot terminal__dot--r"></span>
|
||||
<span class="terminal__dot terminal__dot--y"></span>
|
||||
<span class="terminal__dot terminal__dot--g"></span>
|
||||
<span class="terminal__title">agentmemory@localhost:3111</span>
|
||||
</div>
|
||||
<pre class="terminal__body"><code id="term"></code></pre>
|
||||
<div class="terminal__foot">
|
||||
<button class="btn btn--ghost btn--small" id="term-replay">
|
||||
REPLAY
|
||||
</button>
|
||||
<span class="terminal__status" id="term-status">IDLE</span>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="compare" aria-labelledby="cmp-title">
|
||||
<header class="section__head">
|
||||
<span class="section__eyebrow">VS.</span>
|
||||
<h2 id="cmp-title" class="section__title">
|
||||
AGENTMEMORY VS. THE FIELD.
|
||||
</h2>
|
||||
<p class="section__lede">
|
||||
NUMBERS STRAIGHT FROM THE LONGMEMEVAL-S BENCHMARK AND EACH PROJECT'S
|
||||
OWN DOCS. SHIP WHAT YOU WANT — WE JUST PICKED THE ONE WITH RECEIPTS.
|
||||
</p>
|
||||
</header>
|
||||
<div class="compare__table" role="table" aria-label="Comparison">
|
||||
<div class="compare__row compare__row--head" role="row">
|
||||
<span role="columnheader"></span>
|
||||
<span role="columnheader" class="compare__mine">AGENTMEMORY</span>
|
||||
<span role="columnheader">MEM0</span>
|
||||
<span role="columnheader">LETTA</span>
|
||||
<span role="columnheader">COGNEE</span>
|
||||
</div>
|
||||
<div class="compare__row" role="row">
|
||||
<span role="rowheader">RETRIEVAL R@5</span>
|
||||
<span class="compare__mine">95.2%</span>
|
||||
<span>81.4%</span>
|
||||
<span>73.8%</span>
|
||||
<span>78.1%</span>
|
||||
</div>
|
||||
<div class="compare__row" role="row">
|
||||
<span role="rowheader">EXTERNAL DEPS</span>
|
||||
<span class="compare__mine">0</span>
|
||||
<span>2 (Qdrant, Neo4j)</span>
|
||||
<span>1 (Postgres)</span>
|
||||
<span>1 (Neo4j)</span>
|
||||
</div>
|
||||
<div class="compare__row" role="row">
|
||||
<span role="rowheader">MCP TOOLS</span>
|
||||
<span class="compare__mine">44</span>
|
||||
<span>12</span>
|
||||
<span>18</span>
|
||||
<span>9</span>
|
||||
</div>
|
||||
<div class="compare__row" role="row">
|
||||
<span role="rowheader">AUTO-HOOKS</span>
|
||||
<span class="compare__mine">12</span>
|
||||
<span>0</span>
|
||||
<span>0</span>
|
||||
<span>0</span>
|
||||
</div>
|
||||
<div class="compare__row" role="row">
|
||||
<span role="rowheader">OPEN SOURCE</span>
|
||||
<span class="compare__mine">YES (APACHE-2.0)</span>
|
||||
<span>YES</span>
|
||||
<span>YES</span>
|
||||
<span>YES</span>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="agents" aria-labelledby="agents-title">
|
||||
<header class="section__head">
|
||||
<span class="section__eyebrow">WORKS WITH</span>
|
||||
<h2 id="agents-title" class="section__title">
|
||||
EVERY AGENT YOU ALREADY USE.
|
||||
</h2>
|
||||
</header>
|
||||
<ul class="agents__grid">
|
||||
<li class="agents__tile" data-agent>CLAUDE CODE</li>
|
||||
<li class="agents__tile" data-agent>CURSOR</li>
|
||||
<li class="agents__tile" data-agent>CODEX CLI</li>
|
||||
<li class="agents__tile" data-agent>GEMINI CLI</li>
|
||||
<li class="agents__tile" data-agent>OPENCODE</li>
|
||||
<li class="agents__tile" data-agent>CLINE</li>
|
||||
<li class="agents__tile" data-agent>GOOSE</li>
|
||||
<li class="agents__tile" data-agent>KILO CODE</li>
|
||||
<li class="agents__tile" data-agent>ROO CODE</li>
|
||||
<li class="agents__tile" data-agent>WINDSURF</li>
|
||||
<li class="agents__tile" data-agent>AIDER</li>
|
||||
<li class="agents__tile" data-agent>ANY MCP CLIENT</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<section class="install" id="install" aria-labelledby="install-title">
|
||||
<header class="section__head">
|
||||
<span class="section__eyebrow">SHIP IT</span>
|
||||
<h2 id="install-title" class="section__title">
|
||||
ONE COMMAND. ZERO CONFIG.
|
||||
</h2>
|
||||
<p class="section__lede">
|
||||
RUNS ON YOUR MACHINE. YOUR DATA STAYS LOCAL. BRING YOUR OWN CLAUDE
|
||||
SUBSCRIPTION OR API KEY.
|
||||
</p>
|
||||
</header>
|
||||
<div class="install__cards">
|
||||
<button class="copybox" data-copy="npx @agentmemory/agentmemory">
|
||||
<span class="copybox__prompt">$</span>
|
||||
<span class="copybox__cmd">npx @agentmemory/agentmemory</span>
|
||||
<span class="copybox__hint">CLICK TO COPY</span>
|
||||
</button>
|
||||
<button
|
||||
class="copybox"
|
||||
data-copy="curl -fsSL https://install.iii.dev/console/main/install.sh | sh"
|
||||
>
|
||||
<span class="copybox__prompt">$</span>
|
||||
<span class="copybox__cmd"
|
||||
>curl -fsSL https://install.iii.dev/console/main/install.sh | sh</span
|
||||
>
|
||||
<span class="copybox__hint">CLICK TO COPY · OPTIONAL DEV CONSOLE</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="install__cta">
|
||||
<a
|
||||
class="btn btn--accent"
|
||||
href="https://github.com/rohitg00/agentmemory#quick-start"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
>READ THE QUICKSTART</a
|
||||
>
|
||||
<a
|
||||
class="btn btn--ghost"
|
||||
href="https://www.npmjs.com/package/@agentmemory/agentmemory"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
>NPM PACKAGE</a
|
||||
>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<footer class="foot">
|
||||
<div class="foot__row">
|
||||
<a href="#top" class="foot__mark">AGENTMEMORY</a>
|
||||
<nav class="foot__links" aria-label="Footer">
|
||||
<a
|
||||
href="https://github.com/rohitg00/agentmemory"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
>SOURCE</a
|
||||
>
|
||||
<a
|
||||
href="https://github.com/rohitg00/agentmemory/blob/main/CHANGELOG.md"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
>CHANGELOG</a
|
||||
>
|
||||
<a href="https://iii.dev" target="_blank" rel="noopener">RUNS ON iii</a>
|
||||
<a
|
||||
href="https://github.com/rohitg00/agentmemory/blob/main/LICENSE"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
>APACHE-2.0</a
|
||||
>
|
||||
</nav>
|
||||
</div>
|
||||
<div class="foot__fine">© 2026 AGENTMEMORY · BUILT IN THE OPEN</div>
|
||||
</footer>
|
||||
|
||||
<script src="./script.js" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,319 @@
|
||||
// agentmemory — interactive behavior for the Lamborghini-inspired landing.
|
||||
// Vanilla JS, no deps. Respects prefers-reduced-motion where it matters.
|
||||
|
||||
const REDUCE_MOTION = matchMedia("(prefers-reduced-motion: reduce)").matches;
|
||||
|
||||
// ---------- Scroll progress rail ----------
|
||||
const rail = document.querySelector(".scroll-progress span");
|
||||
const heroRail = document.getElementById("rail");
|
||||
function updateProgress() {
|
||||
const h = document.documentElement;
|
||||
const scrolled = h.scrollTop;
|
||||
const max = h.scrollHeight - h.clientHeight;
|
||||
const pct = max <= 0 ? 0 : Math.min(1, scrolled / max);
|
||||
if (rail) rail.style.width = `${pct * 100}%`;
|
||||
if (heroRail) heroRail.style.width = `${pct * 100}%`;
|
||||
}
|
||||
updateProgress();
|
||||
addEventListener("scroll", updateProgress, { passive: true });
|
||||
|
||||
// ---------- Reveal on intersect ----------
|
||||
const revealTargets = document.querySelectorAll(
|
||||
".section__head, .prim-card, .stat, .compare__row, .agents__tile, .copybox, .terminal"
|
||||
);
|
||||
for (const el of revealTargets) el.classList.add("reveal");
|
||||
|
||||
const revealIO = new IntersectionObserver(
|
||||
(entries) => {
|
||||
for (const entry of entries) {
|
||||
if (entry.isIntersecting) {
|
||||
entry.target.classList.add("is-visible");
|
||||
revealIO.unobserve(entry.target);
|
||||
}
|
||||
}
|
||||
},
|
||||
{ threshold: 0.12, rootMargin: "0px 0px -40px 0px" }
|
||||
);
|
||||
revealTargets.forEach((el) => revealIO.observe(el));
|
||||
|
||||
// ---------- Animated counters ----------
|
||||
function countUp(el) {
|
||||
const target = parseFloat(el.dataset.count);
|
||||
const suffix = el.dataset.suffix || "";
|
||||
const duration = 1400;
|
||||
const isFloat = !Number.isInteger(target);
|
||||
const startAt = performance.now();
|
||||
function tick(now) {
|
||||
const t = Math.min(1, (now - startAt) / duration);
|
||||
const eased = 1 - Math.pow(1 - t, 3);
|
||||
const v = target * eased;
|
||||
el.textContent = isFloat ? `${v.toFixed(1)}${suffix}` : `${Math.round(v)}${suffix}`;
|
||||
if (t < 1) requestAnimationFrame(tick);
|
||||
else
|
||||
el.textContent = isFloat ? `${target.toFixed(1)}${suffix}` : `${target}${suffix}`;
|
||||
}
|
||||
requestAnimationFrame(tick);
|
||||
}
|
||||
|
||||
const statIO = new IntersectionObserver(
|
||||
(entries) => {
|
||||
for (const entry of entries) {
|
||||
if (!entry.isIntersecting) continue;
|
||||
const num = entry.target.querySelector(".stat__num");
|
||||
if (num && !num.dataset.done) {
|
||||
num.dataset.done = "1";
|
||||
countUp(num);
|
||||
}
|
||||
statIO.unobserve(entry.target);
|
||||
}
|
||||
},
|
||||
{ threshold: 0.5 }
|
||||
);
|
||||
document.querySelectorAll("[data-stat]").forEach((el) => statIO.observe(el));
|
||||
|
||||
// ---------- Hero memory graph ----------
|
||||
const canvas = document.getElementById("memgraph");
|
||||
const ctx = canvas?.getContext("2d");
|
||||
let graphRunning = !REDUCE_MOTION;
|
||||
let nodes = [];
|
||||
let edges = [];
|
||||
let pulse = 0;
|
||||
|
||||
function sizeCanvas() {
|
||||
if (!canvas) return;
|
||||
const dpr = Math.max(1, window.devicePixelRatio || 1);
|
||||
const w = canvas.clientWidth;
|
||||
const h = canvas.clientHeight;
|
||||
canvas.width = w * dpr;
|
||||
canvas.height = h * dpr;
|
||||
if (ctx) ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
|
||||
}
|
||||
|
||||
function seedGraph() {
|
||||
if (!canvas) return;
|
||||
const w = canvas.clientWidth;
|
||||
const h = canvas.clientHeight;
|
||||
const count = Math.min(52, Math.floor((w * h) / 22000));
|
||||
nodes = new Array(count).fill(0).map(() => ({
|
||||
x: Math.random() * w,
|
||||
y: Math.random() * h,
|
||||
vx: (Math.random() - 0.5) * 0.18,
|
||||
vy: (Math.random() - 0.5) * 0.18,
|
||||
r: 1.2 + Math.random() * 2.2,
|
||||
hot: Math.random() < 0.25,
|
||||
}));
|
||||
edges = [];
|
||||
}
|
||||
|
||||
function drawGraph() {
|
||||
if (!ctx || !canvas) return;
|
||||
const w = canvas.clientWidth;
|
||||
const h = canvas.clientHeight;
|
||||
ctx.clearRect(0, 0, w, h);
|
||||
|
||||
// Step nodes
|
||||
for (const n of nodes) {
|
||||
n.x += n.vx;
|
||||
n.y += n.vy;
|
||||
if (n.x < 0 || n.x > w) n.vx *= -1;
|
||||
if (n.y < 0 || n.y > h) n.vy *= -1;
|
||||
}
|
||||
|
||||
// Draw edges for nodes within range (live recomputation, O(n^2))
|
||||
const maxDist = 160;
|
||||
ctx.lineWidth = 1;
|
||||
for (let i = 0; i < nodes.length; i++) {
|
||||
for (let j = i + 1; j < nodes.length; j++) {
|
||||
const a = nodes[i];
|
||||
const b = nodes[j];
|
||||
const dx = a.x - b.x;
|
||||
const dy = a.y - b.y;
|
||||
const d = Math.hypot(dx, dy);
|
||||
if (d > maxDist) continue;
|
||||
const alpha = (1 - d / maxDist) * 0.35;
|
||||
const hot = a.hot && b.hot;
|
||||
ctx.strokeStyle = hot
|
||||
? `rgba(255, 192, 0, ${alpha.toFixed(3)})`
|
||||
: `rgba(255, 255, 255, ${(alpha * 0.5).toFixed(3)})`;
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(a.x, a.y);
|
||||
ctx.lineTo(b.x, b.y);
|
||||
ctx.stroke();
|
||||
}
|
||||
}
|
||||
|
||||
// Draw nodes
|
||||
for (const n of nodes) {
|
||||
const r = n.r + (n.hot ? Math.sin(pulse + n.x) * 0.8 : 0);
|
||||
ctx.fillStyle = n.hot ? "#FFC000" : "rgba(255,255,255,0.85)";
|
||||
ctx.beginPath();
|
||||
ctx.arc(n.x, n.y, Math.max(0.5, r), 0, Math.PI * 2);
|
||||
ctx.fill();
|
||||
if (n.hot) {
|
||||
ctx.fillStyle = "rgba(255, 192, 0, 0.12)";
|
||||
ctx.beginPath();
|
||||
ctx.arc(n.x, n.y, r * 3.5, 0, Math.PI * 2);
|
||||
ctx.fill();
|
||||
}
|
||||
}
|
||||
|
||||
pulse += 0.04;
|
||||
}
|
||||
|
||||
function tickGraph() {
|
||||
if (!graphRunning) return;
|
||||
drawGraph();
|
||||
requestAnimationFrame(tickGraph);
|
||||
}
|
||||
|
||||
if (canvas) {
|
||||
sizeCanvas();
|
||||
seedGraph();
|
||||
drawGraph();
|
||||
if (graphRunning) requestAnimationFrame(tickGraph);
|
||||
addEventListener("resize", () => {
|
||||
sizeCanvas();
|
||||
seedGraph();
|
||||
drawGraph();
|
||||
});
|
||||
}
|
||||
|
||||
const pauseBtn = document.getElementById("graph-toggle");
|
||||
const pauseIcon = document.getElementById("pause-icon");
|
||||
pauseBtn?.addEventListener("click", () => {
|
||||
graphRunning = !graphRunning;
|
||||
if (graphRunning) requestAnimationFrame(tickGraph);
|
||||
if (pauseIcon) {
|
||||
pauseIcon.innerHTML = graphRunning
|
||||
? '<rect x="17" y="16" width="4" height="16" fill="#fff"/><rect x="27" y="16" width="4" height="16" fill="#fff"/>'
|
||||
: '<polygon points="18,14 34,24 18,34" fill="#fff"/>';
|
||||
}
|
||||
pauseBtn.setAttribute(
|
||||
"aria-label",
|
||||
graphRunning ? "Pause animation" : "Resume animation"
|
||||
);
|
||||
});
|
||||
|
||||
// ---------- Terminal typewriter ----------
|
||||
const TERM_SCRIPT = [
|
||||
{ t: "prompt", text: "$ " },
|
||||
{ t: "typed", text: "npx @agentmemory/agentmemory\n" },
|
||||
{ t: "plain", text: "[agentmemory] iii-engine ready on :3111\n" },
|
||||
{ t: "plain", text: "[agentmemory] 44 MCP tools registered\n" },
|
||||
{ t: "plain", text: "[agentmemory] 12 autohooks armed\n\n" },
|
||||
{ t: "prompt", text: "$ " },
|
||||
{ t: "typed", text: "memory.recall({ query: \"where did we land the retry logic?\" })\n" },
|
||||
{ t: "comment", text: "// triple-stream retrieval: BM25 + vector + graph\n" },
|
||||
{ t: "ok", text: "✓ 3 memories · p50 18ms · reranked on device\n\n" },
|
||||
{ t: "plain", text: "→ " },
|
||||
{ t: "val", text: "src/retry.ts:47 · exponentialBackoff(max=5, jitter=true)\n" },
|
||||
{ t: "plain", text: "→ " },
|
||||
{ t: "val", text: "commit 8f2e14c · \"resolve conflict + honor x-amz headers\"\n" },
|
||||
{ t: "plain", text: "→ " },
|
||||
{ t: "val", text: "session 2026-04-16 · \"bug: race when Retry-After is empty\"\n\n" },
|
||||
{ t: "prompt", text: "$ " },
|
||||
{ t: "typed", text: "memory.consolidate({ project: 'pay-api' })\n" },
|
||||
{ t: "ok", text: "✓ 18 raw observations → 4 semantic memories · audit row emitted\n" },
|
||||
];
|
||||
|
||||
const term = document.getElementById("term");
|
||||
const termStatus = document.getElementById("term-status");
|
||||
const replayBtn = document.getElementById("term-replay");
|
||||
|
||||
function classFor(type) {
|
||||
switch (type) {
|
||||
case "prompt":
|
||||
return "t-prompt";
|
||||
case "comment":
|
||||
return "t-comment";
|
||||
case "ok":
|
||||
return "t-ok";
|
||||
case "val":
|
||||
return "t-val";
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
async function playTerminal() {
|
||||
if (!term) return;
|
||||
term.innerHTML = "";
|
||||
const caret = document.createElement("span");
|
||||
caret.className = "t-caret";
|
||||
term.appendChild(caret);
|
||||
if (termStatus) termStatus.textContent = "RUNNING";
|
||||
for (const seg of TERM_SCRIPT) {
|
||||
const span = document.createElement("span");
|
||||
span.className = classFor(seg.t);
|
||||
term.insertBefore(span, caret);
|
||||
if (seg.t === "typed") {
|
||||
for (const ch of seg.text) {
|
||||
span.textContent += ch;
|
||||
await sleep(REDUCE_MOTION ? 0 : 16 + Math.random() * 34);
|
||||
}
|
||||
await sleep(REDUCE_MOTION ? 0 : 260);
|
||||
} else {
|
||||
span.textContent = seg.text;
|
||||
await sleep(REDUCE_MOTION ? 0 : 160);
|
||||
}
|
||||
}
|
||||
if (termStatus) termStatus.textContent = "DONE";
|
||||
}
|
||||
|
||||
function sleep(ms) {
|
||||
return new Promise((r) => setTimeout(r, ms));
|
||||
}
|
||||
|
||||
const termIO = new IntersectionObserver(
|
||||
(entries) => {
|
||||
for (const entry of entries) {
|
||||
if (!entry.isIntersecting) continue;
|
||||
if (!term.dataset.played) {
|
||||
term.dataset.played = "1";
|
||||
playTerminal();
|
||||
}
|
||||
termIO.unobserve(entry.target);
|
||||
}
|
||||
},
|
||||
{ threshold: 0.4 }
|
||||
);
|
||||
if (term) termIO.observe(term.closest(".terminal"));
|
||||
|
||||
replayBtn?.addEventListener("click", () => playTerminal());
|
||||
|
||||
// ---------- Tilt effect on primitive cards ----------
|
||||
const tiltCards = document.querySelectorAll("[data-tilt]");
|
||||
tiltCards.forEach((card) => {
|
||||
if (REDUCE_MOTION) return;
|
||||
card.addEventListener("mousemove", (e) => {
|
||||
const rect = card.getBoundingClientRect();
|
||||
const px = (e.clientX - rect.left) / rect.width - 0.5;
|
||||
const py = (e.clientY - rect.top) / rect.height - 0.5;
|
||||
card.style.transform = `translateY(-4px) rotateX(${(-py * 4).toFixed(
|
||||
2
|
||||
)}deg) rotateY(${(px * 4).toFixed(2)}deg)`;
|
||||
});
|
||||
card.addEventListener("mouseleave", () => {
|
||||
card.style.transform = "";
|
||||
});
|
||||
});
|
||||
|
||||
// ---------- Copy install boxes ----------
|
||||
document.querySelectorAll(".copybox").forEach((btn) => {
|
||||
btn.addEventListener("click", async () => {
|
||||
const cmd = btn.dataset.copy || "";
|
||||
try {
|
||||
await navigator.clipboard.writeText(cmd);
|
||||
const hint = btn.querySelector(".copybox__hint");
|
||||
const original = hint.textContent;
|
||||
hint.textContent = "COPIED";
|
||||
btn.classList.add("is-copied");
|
||||
setTimeout(() => {
|
||||
hint.textContent = original;
|
||||
btn.classList.remove("is-copied");
|
||||
}, 1600);
|
||||
} catch {
|
||||
btn.querySelector(".copybox__hint").textContent = "CLIPBOARD BLOCKED";
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,895 @@
|
||||
:root {
|
||||
--abyss: #000000;
|
||||
--iron: #181818;
|
||||
--charcoal: #202020;
|
||||
--ash: #7d7d7d;
|
||||
--steel: #969696;
|
||||
--mist: #e6e6e6;
|
||||
--white: #ffffff;
|
||||
--gold: #ffc000;
|
||||
--gold-deep: #917300;
|
||||
--gold-soft: #ffce3e;
|
||||
--cyan: #29abe2;
|
||||
--blue: #3860be;
|
||||
--font-display: "Archivo", "Helvetica Neue", Arial, sans-serif;
|
||||
--font-mono: "JetBrains Mono", ui-monospace, Menlo, monospace;
|
||||
--edge-hex: polygon(
|
||||
8% 0%,
|
||||
92% 0%,
|
||||
100% 50%,
|
||||
92% 100%,
|
||||
8% 100%,
|
||||
0% 50%
|
||||
);
|
||||
}
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
background: var(--abyss);
|
||||
color: var(--white);
|
||||
font-family: var(--font-display);
|
||||
font-size: 16px;
|
||||
line-height: 1.5;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
scroll-behavior: smooth;
|
||||
}
|
||||
|
||||
a {
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
transition: color 160ms ease;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
color: var(--blue);
|
||||
}
|
||||
|
||||
button {
|
||||
font-family: inherit;
|
||||
cursor: pointer;
|
||||
background: none;
|
||||
border: none;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
::selection {
|
||||
background: var(--gold);
|
||||
color: var(--abyss);
|
||||
}
|
||||
|
||||
/* Scroll progress */
|
||||
.scroll-progress {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 2px;
|
||||
background: transparent;
|
||||
z-index: 200;
|
||||
}
|
||||
.scroll-progress span {
|
||||
display: block;
|
||||
height: 100%;
|
||||
width: 0;
|
||||
background: var(--gold);
|
||||
transition: width 80ms linear;
|
||||
}
|
||||
|
||||
/* Nav */
|
||||
.nav {
|
||||
position: fixed;
|
||||
inset: 0 0 auto 0;
|
||||
display: grid;
|
||||
grid-template-columns: 1fr auto 1fr;
|
||||
align-items: center;
|
||||
padding: 16px 40px;
|
||||
z-index: 100;
|
||||
background: linear-gradient(180deg, rgba(0, 0, 0, 0.85), rgba(0, 0, 0, 0));
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.nav > * {
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
.nav__menu {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
padding: 12px 0;
|
||||
color: var(--white);
|
||||
}
|
||||
|
||||
.nav__menu-label {
|
||||
font-size: 14px;
|
||||
letter-spacing: 0.14px;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.nav__bar {
|
||||
display: block;
|
||||
width: 22px;
|
||||
height: 2px;
|
||||
background: var(--white);
|
||||
transition: transform 200ms ease;
|
||||
}
|
||||
.nav__bar + .nav__bar {
|
||||
margin-top: 5px;
|
||||
}
|
||||
.nav__menu:hover .nav__bar:first-child {
|
||||
transform: translateX(-2px);
|
||||
}
|
||||
.nav__menu:hover .nav__bar:last-child {
|
||||
transform: translateX(2px);
|
||||
}
|
||||
|
||||
.nav__mark {
|
||||
justify-self: center;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
transform-origin: center;
|
||||
transition: transform 400ms cubic-bezier(0.2, 0.8, 0.2, 1);
|
||||
}
|
||||
.nav__mark:hover {
|
||||
transform: rotate(60deg);
|
||||
}
|
||||
|
||||
.nav__right {
|
||||
justify-self: end;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 32px;
|
||||
}
|
||||
|
||||
.nav__link {
|
||||
font-size: 14px;
|
||||
letter-spacing: 0.14px;
|
||||
}
|
||||
|
||||
/* Buttons */
|
||||
.btn {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 18px 28px;
|
||||
font-size: 14.4px;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.2px;
|
||||
text-transform: uppercase;
|
||||
border: 1px solid transparent;
|
||||
border-radius: 0;
|
||||
transition: background 200ms ease, color 200ms ease, border-color 200ms ease,
|
||||
opacity 200ms ease;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.btn--accent {
|
||||
background: var(--gold);
|
||||
color: var(--abyss);
|
||||
padding: 22px 32px;
|
||||
font-size: 16px;
|
||||
}
|
||||
.btn--accent:hover {
|
||||
background: var(--gold-deep);
|
||||
color: var(--white);
|
||||
}
|
||||
|
||||
.btn--ghost {
|
||||
background: transparent;
|
||||
color: var(--white);
|
||||
border-color: rgba(255, 255, 255, 0.5);
|
||||
opacity: 0.85;
|
||||
}
|
||||
.btn--ghost:hover {
|
||||
background: rgba(255, 255, 255, 0.08);
|
||||
border-color: var(--white);
|
||||
color: var(--white);
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.btn--small {
|
||||
padding: 10px 14px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
/* Hero */
|
||||
.hero {
|
||||
position: relative;
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
overflow: hidden;
|
||||
padding: 120px 24px 80px;
|
||||
}
|
||||
|
||||
.hero__graph {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.hero__vignette {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background: radial-gradient(
|
||||
ellipse at center,
|
||||
rgba(0, 0, 0, 0) 0%,
|
||||
rgba(0, 0, 0, 0.7) 75%,
|
||||
rgba(0, 0, 0, 0.95) 100%
|
||||
),
|
||||
linear-gradient(
|
||||
180deg,
|
||||
rgba(0, 0, 0, 0.3) 0%,
|
||||
rgba(0, 0, 0, 0) 30%,
|
||||
rgba(0, 0, 0, 0) 70%,
|
||||
rgba(0, 0, 0, 0.4) 100%
|
||||
);
|
||||
}
|
||||
|
||||
.hero__content {
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
text-align: center;
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.hero__chip {
|
||||
display: inline-block;
|
||||
padding: 8px 14px;
|
||||
font-size: 12px;
|
||||
letter-spacing: 0.96px;
|
||||
color: var(--gold-soft);
|
||||
border: 1px solid var(--gold-soft);
|
||||
text-transform: uppercase;
|
||||
margin-bottom: 28px;
|
||||
}
|
||||
|
||||
.hero__title {
|
||||
font-size: clamp(54px, 12vw, 160px);
|
||||
font-weight: 900;
|
||||
line-height: 0.92;
|
||||
letter-spacing: -0.02em;
|
||||
margin: 0;
|
||||
text-transform: uppercase;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
gap: 0.05em;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.hero__word {
|
||||
display: inline-block;
|
||||
opacity: 0;
|
||||
transform: translateY(40px);
|
||||
animation: slideIn 900ms cubic-bezier(0.2, 0.8, 0.2, 1) forwards;
|
||||
}
|
||||
.hero__word:nth-child(2) {
|
||||
animation-delay: 120ms;
|
||||
}
|
||||
.hero__word--accent {
|
||||
color: var(--gold);
|
||||
}
|
||||
|
||||
@keyframes slideIn {
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
.hero__lede {
|
||||
max-width: 780px;
|
||||
margin: 28px auto 40px;
|
||||
font-size: clamp(14px, 1.4vw, 18px);
|
||||
letter-spacing: 0.12px;
|
||||
color: var(--mist);
|
||||
text-transform: uppercase;
|
||||
line-height: 1.56;
|
||||
}
|
||||
|
||||
.hero__cta {
|
||||
display: inline-flex;
|
||||
gap: 16px;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.hero__pause {
|
||||
position: absolute;
|
||||
right: 40px;
|
||||
bottom: 56px;
|
||||
z-index: 3;
|
||||
transition: transform 200ms ease;
|
||||
}
|
||||
.hero__pause:hover {
|
||||
transform: scale(1.08);
|
||||
}
|
||||
|
||||
.hero__rail {
|
||||
position: absolute;
|
||||
left: 40px;
|
||||
right: 40px;
|
||||
bottom: 24px;
|
||||
height: 1px;
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
}
|
||||
.hero__rail span {
|
||||
display: block;
|
||||
height: 100%;
|
||||
width: 0;
|
||||
background: var(--white);
|
||||
transition: width 60ms linear;
|
||||
}
|
||||
|
||||
/* Section head */
|
||||
.section__head {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto 56px;
|
||||
padding: 0 40px;
|
||||
}
|
||||
|
||||
.section__eyebrow {
|
||||
display: inline-block;
|
||||
font-size: 12px;
|
||||
letter-spacing: 0.96px;
|
||||
color: var(--gold);
|
||||
text-transform: uppercase;
|
||||
margin-bottom: 16px;
|
||||
padding: 6px 10px;
|
||||
border: 1px solid var(--gold);
|
||||
}
|
||||
|
||||
.section__title {
|
||||
font-size: clamp(32px, 5vw, 72px);
|
||||
font-weight: 900;
|
||||
line-height: 0.98;
|
||||
letter-spacing: -0.01em;
|
||||
margin: 0 0 24px;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.section__lede {
|
||||
max-width: 680px;
|
||||
font-size: 16px;
|
||||
letter-spacing: 0.12px;
|
||||
color: var(--steel);
|
||||
text-transform: uppercase;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* Stats */
|
||||
.stats {
|
||||
border-top: 1px solid var(--charcoal);
|
||||
border-bottom: 1px solid var(--charcoal);
|
||||
padding: 0;
|
||||
background: var(--iron);
|
||||
}
|
||||
|
||||
.stats__row {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(6, 1fr);
|
||||
max-width: 1440px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.stat {
|
||||
padding: 48px 24px;
|
||||
text-align: center;
|
||||
border-right: 1px solid var(--charcoal);
|
||||
transition: background 250ms ease;
|
||||
}
|
||||
.stat:last-child {
|
||||
border-right: none;
|
||||
}
|
||||
.stat:hover {
|
||||
background: var(--charcoal);
|
||||
}
|
||||
|
||||
.stat__num {
|
||||
font-family: var(--font-display);
|
||||
font-size: clamp(40px, 5vw, 64px);
|
||||
font-weight: 900;
|
||||
line-height: 1;
|
||||
color: var(--gold);
|
||||
letter-spacing: -0.02em;
|
||||
}
|
||||
|
||||
.stat__label {
|
||||
margin-top: 12px;
|
||||
font-size: 11px;
|
||||
letter-spacing: 0.96px;
|
||||
color: var(--steel);
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
/* Primitives */
|
||||
.primitives {
|
||||
padding: 112px 0 96px;
|
||||
}
|
||||
|
||||
.primitives__grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 1px;
|
||||
max-width: 1440px;
|
||||
margin: 0 auto;
|
||||
padding: 0 40px;
|
||||
background: var(--charcoal);
|
||||
border: 1px solid var(--charcoal);
|
||||
}
|
||||
|
||||
.prim-card {
|
||||
background: var(--abyss);
|
||||
padding: 48px 32px;
|
||||
transition: background 250ms ease, transform 250ms ease;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
min-height: 280px;
|
||||
}
|
||||
.prim-card::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background: linear-gradient(
|
||||
135deg,
|
||||
rgba(255, 192, 0, 0) 0%,
|
||||
rgba(255, 192, 0, 0) 50%,
|
||||
rgba(255, 192, 0, 0.08) 100%
|
||||
);
|
||||
opacity: 0;
|
||||
transition: opacity 250ms ease;
|
||||
}
|
||||
.prim-card:hover {
|
||||
background: var(--iron);
|
||||
transform: translateY(-4px);
|
||||
}
|
||||
.prim-card:hover::before {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.prim-card__glyph {
|
||||
font-family: var(--font-mono);
|
||||
font-size: 14px;
|
||||
color: var(--gold);
|
||||
letter-spacing: 0.2px;
|
||||
margin-bottom: 32px;
|
||||
}
|
||||
|
||||
.prim-card__title {
|
||||
font-size: 40px;
|
||||
font-weight: 900;
|
||||
line-height: 0.98;
|
||||
letter-spacing: -0.01em;
|
||||
margin: 0 0 24px;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.prim-card__text {
|
||||
font-size: 14px;
|
||||
letter-spacing: 0.12px;
|
||||
color: var(--steel);
|
||||
text-transform: uppercase;
|
||||
margin: 0;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
/* Live terminal */
|
||||
.live {
|
||||
padding: 96px 0;
|
||||
}
|
||||
|
||||
.terminal {
|
||||
max-width: 1100px;
|
||||
margin: 0 auto;
|
||||
padding: 0 40px;
|
||||
}
|
||||
|
||||
.terminal__chrome {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
background: var(--iron);
|
||||
padding: 12px 16px;
|
||||
border: 1px solid var(--charcoal);
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.terminal__dot {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
border-radius: 50%;
|
||||
display: inline-block;
|
||||
background: var(--charcoal);
|
||||
}
|
||||
.terminal__dot--r {
|
||||
background: #ff5f57;
|
||||
}
|
||||
.terminal__dot--y {
|
||||
background: #febc2e;
|
||||
}
|
||||
.terminal__dot--g {
|
||||
background: #28c840;
|
||||
}
|
||||
|
||||
.terminal__title {
|
||||
margin-left: 12px;
|
||||
font-family: var(--font-mono);
|
||||
font-size: 12px;
|
||||
color: var(--steel);
|
||||
}
|
||||
|
||||
.terminal__body {
|
||||
margin: 0;
|
||||
padding: 24px 24px 16px;
|
||||
background: var(--abyss);
|
||||
border: 1px solid var(--charcoal);
|
||||
border-top: none;
|
||||
font-family: var(--font-mono);
|
||||
font-size: 13.5px;
|
||||
line-height: 1.7;
|
||||
color: var(--mist);
|
||||
white-space: pre-wrap;
|
||||
min-height: 360px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.terminal__body .t-prompt {
|
||||
color: var(--gold);
|
||||
}
|
||||
.terminal__body .t-comment {
|
||||
color: var(--ash);
|
||||
}
|
||||
.terminal__body .t-ok {
|
||||
color: #29c864;
|
||||
}
|
||||
.terminal__body .t-val {
|
||||
color: var(--cyan);
|
||||
}
|
||||
.terminal__body .t-caret {
|
||||
display: inline-block;
|
||||
width: 8px;
|
||||
height: 1em;
|
||||
vertical-align: text-bottom;
|
||||
background: var(--gold);
|
||||
margin-left: 2px;
|
||||
animation: blink 1s steps(1) infinite;
|
||||
}
|
||||
@keyframes blink {
|
||||
50% {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.terminal__foot {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
background: var(--iron);
|
||||
padding: 12px 16px;
|
||||
border: 1px solid var(--charcoal);
|
||||
border-top: none;
|
||||
}
|
||||
|
||||
.terminal__status {
|
||||
font-family: var(--font-mono);
|
||||
font-size: 11px;
|
||||
color: var(--steel);
|
||||
letter-spacing: 0.96px;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
/* Compare */
|
||||
.compare {
|
||||
padding: 96px 0;
|
||||
background: var(--iron);
|
||||
border-top: 1px solid var(--charcoal);
|
||||
border-bottom: 1px solid var(--charcoal);
|
||||
}
|
||||
|
||||
.compare__table {
|
||||
max-width: 1100px;
|
||||
margin: 0 auto;
|
||||
padding: 0 40px;
|
||||
}
|
||||
|
||||
.compare__row {
|
||||
display: grid;
|
||||
grid-template-columns: 1.6fr 1fr 1fr 1fr 1fr;
|
||||
gap: 16px;
|
||||
padding: 20px 0;
|
||||
border-bottom: 1px solid var(--charcoal);
|
||||
align-items: center;
|
||||
font-size: 14px;
|
||||
letter-spacing: 0.12px;
|
||||
}
|
||||
.compare__row > span {
|
||||
color: var(--steel);
|
||||
text-transform: uppercase;
|
||||
}
|
||||
.compare__row > span:first-child {
|
||||
color: var(--white);
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.compare__row--head {
|
||||
border-bottom-color: var(--gold);
|
||||
}
|
||||
.compare__row--head span {
|
||||
font-size: 11px;
|
||||
letter-spacing: 0.96px;
|
||||
color: var(--ash) !important;
|
||||
font-weight: 400 !important;
|
||||
}
|
||||
.compare__row--head .compare__mine {
|
||||
color: var(--gold) !important;
|
||||
}
|
||||
|
||||
.compare__mine {
|
||||
color: var(--gold) !important;
|
||||
font-weight: 700 !important;
|
||||
}
|
||||
|
||||
/* Agents */
|
||||
.agents {
|
||||
padding: 96px 0;
|
||||
}
|
||||
|
||||
.agents__grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
|
||||
gap: 1px;
|
||||
max-width: 1440px;
|
||||
margin: 0 auto;
|
||||
padding: 0 40px;
|
||||
background: var(--charcoal);
|
||||
border: 1px solid var(--charcoal);
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.agents__tile {
|
||||
background: var(--abyss);
|
||||
padding: 40px 24px;
|
||||
text-align: center;
|
||||
font-size: 16px;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.12px;
|
||||
text-transform: uppercase;
|
||||
transition: background 200ms ease, color 200ms ease, transform 200ms ease;
|
||||
position: relative;
|
||||
}
|
||||
.agents__tile::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
inset: 12px;
|
||||
border: 1px solid transparent;
|
||||
transition: border-color 200ms ease;
|
||||
}
|
||||
.agents__tile:hover {
|
||||
background: var(--iron);
|
||||
color: var(--gold);
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
.agents__tile:hover::after {
|
||||
border-color: rgba(255, 192, 0, 0.3);
|
||||
}
|
||||
|
||||
/* Install */
|
||||
.install {
|
||||
padding: 112px 0;
|
||||
background: var(--iron);
|
||||
border-top: 1px solid var(--charcoal);
|
||||
}
|
||||
|
||||
.install__cards {
|
||||
max-width: 900px;
|
||||
margin: 0 auto 56px;
|
||||
padding: 0 40px;
|
||||
display: grid;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.copybox {
|
||||
display: grid;
|
||||
grid-template-columns: auto 1fr auto;
|
||||
gap: 16px;
|
||||
align-items: center;
|
||||
padding: 22px 28px;
|
||||
background: var(--abyss);
|
||||
border: 1px solid var(--charcoal);
|
||||
color: var(--mist);
|
||||
font-family: var(--font-mono);
|
||||
font-size: 14px;
|
||||
text-align: left;
|
||||
width: 100%;
|
||||
transition: border-color 200ms ease, background 200ms ease;
|
||||
cursor: pointer;
|
||||
}
|
||||
.copybox:hover {
|
||||
border-color: var(--gold);
|
||||
background: var(--charcoal);
|
||||
}
|
||||
.copybox.is-copied {
|
||||
border-color: #29c864;
|
||||
}
|
||||
.copybox.is-copied .copybox__hint {
|
||||
color: #29c864;
|
||||
}
|
||||
|
||||
.copybox__prompt {
|
||||
color: var(--gold);
|
||||
font-weight: 700;
|
||||
}
|
||||
.copybox__cmd {
|
||||
color: var(--white);
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
.copybox__hint {
|
||||
color: var(--ash);
|
||||
font-size: 11px;
|
||||
letter-spacing: 0.96px;
|
||||
text-transform: uppercase;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.install__cta {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
gap: 16px;
|
||||
flex-wrap: wrap;
|
||||
padding: 0 40px;
|
||||
}
|
||||
|
||||
/* Footer */
|
||||
.foot {
|
||||
padding: 48px 40px 32px;
|
||||
background: var(--abyss);
|
||||
border-top: 1px solid var(--charcoal);
|
||||
}
|
||||
|
||||
.foot__row {
|
||||
max-width: 1440px;
|
||||
margin: 0 auto;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 24px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.foot__mark {
|
||||
font-size: 20px;
|
||||
font-weight: 900;
|
||||
letter-spacing: -0.01em;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.foot__links {
|
||||
display: flex;
|
||||
gap: 28px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.foot__links a {
|
||||
font-size: 13px;
|
||||
letter-spacing: 0.12px;
|
||||
text-transform: uppercase;
|
||||
color: var(--steel);
|
||||
}
|
||||
.foot__links a:hover {
|
||||
color: var(--gold);
|
||||
}
|
||||
|
||||
.foot__fine {
|
||||
max-width: 1440px;
|
||||
margin: 32px auto 0;
|
||||
font-size: 11px;
|
||||
letter-spacing: 0.96px;
|
||||
color: var(--ash);
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
/* Reveal animation */
|
||||
.reveal {
|
||||
opacity: 0;
|
||||
transform: translateY(32px);
|
||||
transition: opacity 600ms ease, transform 600ms cubic-bezier(0.2, 0.8, 0.2, 1);
|
||||
}
|
||||
.reveal.is-visible {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
/* Responsive */
|
||||
@media (max-width: 1024px) {
|
||||
.stats__row {
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
}
|
||||
.stat {
|
||||
border-bottom: 1px solid var(--charcoal);
|
||||
}
|
||||
.stat:nth-child(3n) {
|
||||
border-right: none;
|
||||
}
|
||||
.primitives__grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
.compare__row {
|
||||
grid-template-columns: 1.4fr 1fr;
|
||||
gap: 8px 16px;
|
||||
}
|
||||
.compare__row span:nth-child(n + 3) {
|
||||
display: none;
|
||||
}
|
||||
.compare__row--head span:nth-child(n + 3) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 640px) {
|
||||
.nav {
|
||||
padding: 12px 20px;
|
||||
}
|
||||
.nav__right {
|
||||
gap: 16px;
|
||||
}
|
||||
.section__head,
|
||||
.primitives__grid,
|
||||
.terminal,
|
||||
.compare__table,
|
||||
.agents__grid,
|
||||
.install__cards,
|
||||
.install__cta {
|
||||
padding-left: 20px;
|
||||
padding-right: 20px;
|
||||
}
|
||||
.hero__pause {
|
||||
right: 20px;
|
||||
bottom: 36px;
|
||||
}
|
||||
.hero__rail {
|
||||
left: 20px;
|
||||
right: 20px;
|
||||
}
|
||||
.stats__row {
|
||||
grid-template-columns: 1fr 1fr;
|
||||
}
|
||||
.stat:nth-child(3n) {
|
||||
border-right: 1px solid var(--charcoal);
|
||||
}
|
||||
.stat:nth-child(2n) {
|
||||
border-right: none;
|
||||
}
|
||||
.hero__title {
|
||||
flex-direction: column;
|
||||
}
|
||||
.terminal__body {
|
||||
font-size: 12px;
|
||||
}
|
||||
.foot__row {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
}
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
*,
|
||||
*::before,
|
||||
*::after {
|
||||
animation-duration: 0.001ms !important;
|
||||
transition-duration: 0.001ms !important;
|
||||
}
|
||||
.hero__word {
|
||||
opacity: 1;
|
||||
transform: none;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user