@jaimevalasek/aioson 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +456 -0
- package/CODE_OF_CONDUCT.md +12 -0
- package/CONTRIBUTING.md +13 -0
- package/LICENSE +21 -0
- package/README.md +254 -0
- package/bin/aioson.js +4 -0
- package/docs/en/cli-reference.md +398 -0
- package/docs/en/i18n.md +52 -0
- package/docs/en/json-schemas.md +41 -0
- package/docs/en/mcp.md +56 -0
- package/docs/en/parallel.md +82 -0
- package/docs/en/qa-browser.md +339 -0
- package/docs/en/release-flow.md +22 -0
- package/docs/en/release-notes-template.md +41 -0
- package/docs/en/release.md +28 -0
- package/docs/en/schemas/agent-prompt.schema.json +17 -0
- package/docs/en/schemas/agents.schema.json +32 -0
- package/docs/en/schemas/context-validate.schema.json +36 -0
- package/docs/en/schemas/doctor.schema.json +89 -0
- package/docs/en/schemas/error.schema.json +24 -0
- package/docs/en/schemas/i18n-add.schema.json +15 -0
- package/docs/en/schemas/index.json +116 -0
- package/docs/en/schemas/info.schema.json +39 -0
- package/docs/en/schemas/init.schema.json +48 -0
- package/docs/en/schemas/install.schema.json +60 -0
- package/docs/en/schemas/locale-apply.schema.json +30 -0
- package/docs/en/schemas/mcp-doctor.schema.json +95 -0
- package/docs/en/schemas/mcp-init.schema.json +122 -0
- package/docs/en/schemas/package-test.schema.json +24 -0
- package/docs/en/schemas/parallel-assign.schema.json +57 -0
- package/docs/en/schemas/parallel-doctor.schema.json +86 -0
- package/docs/en/schemas/parallel-init.schema.json +53 -0
- package/docs/en/schemas/parallel-status.schema.json +94 -0
- package/docs/en/schemas/setup-context.schema.json +39 -0
- package/docs/en/schemas/smoke.schema.json +23 -0
- package/docs/en/schemas/update.schema.json +48 -0
- package/docs/en/schemas/workflow-plan.schema.json +30 -0
- package/docs/en/web3.md +54 -0
- package/docs/pt/README.md +46 -0
- package/docs/pt/advisor-spec.md +335 -0
- package/docs/pt/agentes.md +453 -0
- package/docs/pt/cenarios.md +1230 -0
- package/docs/pt/clientes-ai.md +224 -0
- package/docs/pt/comandos-cli.md +511 -0
- package/docs/pt/genome-3.0-spec.md +296 -0
- package/docs/pt/guia-engineer.md +226 -0
- package/docs/pt/inicio-rapido.md +138 -0
- package/docs/pt/profiler-system.md +214 -0
- package/docs/pt/runtime-observability.md +72 -0
- package/docs/pt/squad-genoma.md +777 -0
- package/docs/pt/web3.md +797 -0
- package/docs/testing/genome-2.0-manual-regression.md +23 -0
- package/docs/testing/genome-2.0-matrix.md +36 -0
- package/docs/testing/genome-2.0-rollout.md +184 -0
- package/package.json +50 -0
- package/src/agents.js +56 -0
- package/src/cli.js +497 -0
- package/src/commands/agents.js +142 -0
- package/src/commands/cloud.js +1767 -0
- package/src/commands/config.js +90 -0
- package/src/commands/context-validate.js +91 -0
- package/src/commands/doctor.js +123 -0
- package/src/commands/genome-doctor.js +41 -0
- package/src/commands/genome-migrate.js +49 -0
- package/src/commands/i18n-add.js +56 -0
- package/src/commands/info.js +41 -0
- package/src/commands/init.js +75 -0
- package/src/commands/install.js +68 -0
- package/src/commands/locale-apply.js +51 -0
- package/src/commands/locale-diff.js +126 -0
- package/src/commands/mcp-doctor.js +406 -0
- package/src/commands/mcp-init.js +379 -0
- package/src/commands/package-e2e.js +273 -0
- package/src/commands/parallel-assign.js +403 -0
- package/src/commands/parallel-doctor.js +437 -0
- package/src/commands/parallel-init.js +249 -0
- package/src/commands/parallel-status.js +290 -0
- package/src/commands/qa-doctor.js +185 -0
- package/src/commands/qa-init.js +161 -0
- package/src/commands/qa-report.js +58 -0
- package/src/commands/qa-run.js +873 -0
- package/src/commands/qa-scan.js +337 -0
- package/src/commands/runtime.js +948 -0
- package/src/commands/scan-project.js +1107 -0
- package/src/commands/setup-context.js +650 -0
- package/src/commands/smoke.js +426 -0
- package/src/commands/squad-doctor.js +358 -0
- package/src/commands/squad-export.js +46 -0
- package/src/commands/squad-pipeline.js +97 -0
- package/src/commands/squad-repair-genomes.js +39 -0
- package/src/commands/squad-status.js +424 -0
- package/src/commands/squad-validate.js +230 -0
- package/src/commands/test-agents.js +194 -0
- package/src/commands/update.js +55 -0
- package/src/commands/workflow-next.js +594 -0
- package/src/commands/workflow-plan.js +108 -0
- package/src/constants.js +314 -0
- package/src/context-parse-reason.js +22 -0
- package/src/context-writer.js +150 -0
- package/src/context.js +217 -0
- package/src/detector.js +261 -0
- package/src/doctor.js +289 -0
- package/src/execution-gateway.js +461 -0
- package/src/genome-files.js +198 -0
- package/src/genome-format.js +442 -0
- package/src/genome-schema.js +215 -0
- package/src/genomes/bindings.js +281 -0
- package/src/genomes.js +467 -0
- package/src/i18n/index.js +103 -0
- package/src/i18n/messages/en.js +784 -0
- package/src/i18n/messages/es.js +718 -0
- package/src/i18n/messages/fr.js +725 -0
- package/src/i18n/messages/pt-BR.js +818 -0
- package/src/i18n/scaffold.js +64 -0
- package/src/installer.js +232 -0
- package/src/lib/genomes/compat.js +206 -0
- package/src/lib/genomes/migrate.js +90 -0
- package/src/lib/squads/genome-repair.js +49 -0
- package/src/locales.js +84 -0
- package/src/onboarding.js +305 -0
- package/src/parser.js +53 -0
- package/src/prompt-tool.js +20 -0
- package/src/qa-html-report.js +472 -0
- package/src/runtime-store.js +1527 -0
- package/src/squads/apply-genome.js +21 -0
- package/src/squads/genome-binding-service.js +154 -0
- package/src/updater.js +32 -0
- package/src/utils.js +46 -0
- package/src/version.js +50 -0
- package/template/.aioson/advisors/.gitkeep +1 -0
- package/template/.aioson/agents/analyst.md +225 -0
- package/template/.aioson/agents/architect.md +221 -0
- package/template/.aioson/agents/dev.md +201 -0
- package/template/.aioson/agents/discovery-design-doc.md +196 -0
- package/template/.aioson/agents/genoma.md +300 -0
- package/template/.aioson/agents/orchestrator.md +107 -0
- package/template/.aioson/agents/pm.md +89 -0
- package/template/.aioson/agents/product.md +361 -0
- package/template/.aioson/agents/profiler-enricher.md +266 -0
- package/template/.aioson/agents/profiler-forge.md +188 -0
- package/template/.aioson/agents/profiler-researcher.md +245 -0
- package/template/.aioson/agents/qa.md +344 -0
- package/template/.aioson/agents/setup.md +381 -0
- package/template/.aioson/agents/squad.md +837 -0
- package/template/.aioson/agents/ux-ui.md +416 -0
- package/template/.aioson/config.md +56 -0
- package/template/.aioson/context/.gitkeep +0 -0
- package/template/.aioson/context/parallel/.gitkeep +0 -0
- package/template/.aioson/context/spec.md.template +37 -0
- package/template/.aioson/genomas/.gitkeep +0 -0
- package/template/.aioson/locales/en/agents/analyst.md +214 -0
- package/template/.aioson/locales/en/agents/architect.md +210 -0
- package/template/.aioson/locales/en/agents/dev.md +187 -0
- package/template/.aioson/locales/en/agents/discovery-design-doc.md +27 -0
- package/template/.aioson/locales/en/agents/genoma.md +212 -0
- package/template/.aioson/locales/en/agents/orchestrator.md +105 -0
- package/template/.aioson/locales/en/agents/pm.md +77 -0
- package/template/.aioson/locales/en/agents/product.md +310 -0
- package/template/.aioson/locales/en/agents/profiler-enricher.md +5 -0
- package/template/.aioson/locales/en/agents/profiler-forge.md +5 -0
- package/template/.aioson/locales/en/agents/profiler-researcher.md +5 -0
- package/template/.aioson/locales/en/agents/qa.md +214 -0
- package/template/.aioson/locales/en/agents/setup.md +342 -0
- package/template/.aioson/locales/en/agents/squad.md +247 -0
- package/template/.aioson/locales/en/agents/ux-ui.md +320 -0
- package/template/.aioson/locales/es/agents/analyst.md +203 -0
- package/template/.aioson/locales/es/agents/architect.md +208 -0
- package/template/.aioson/locales/es/agents/dev.md +183 -0
- package/template/.aioson/locales/es/agents/discovery-design-doc.md +19 -0
- package/template/.aioson/locales/es/agents/genoma.md +102 -0
- package/template/.aioson/locales/es/agents/orchestrator.md +108 -0
- package/template/.aioson/locales/es/agents/pm.md +81 -0
- package/template/.aioson/locales/es/agents/product.md +310 -0
- package/template/.aioson/locales/es/agents/profiler-enricher.md +5 -0
- package/template/.aioson/locales/es/agents/profiler-forge.md +5 -0
- package/template/.aioson/locales/es/agents/profiler-researcher.md +5 -0
- package/template/.aioson/locales/es/agents/qa.md +163 -0
- package/template/.aioson/locales/es/agents/setup.md +347 -0
- package/template/.aioson/locales/es/agents/squad.md +247 -0
- package/template/.aioson/locales/es/agents/ux-ui.md +201 -0
- package/template/.aioson/locales/fr/agents/analyst.md +203 -0
- package/template/.aioson/locales/fr/agents/architect.md +208 -0
- package/template/.aioson/locales/fr/agents/dev.md +183 -0
- package/template/.aioson/locales/fr/agents/discovery-design-doc.md +19 -0
- package/template/.aioson/locales/fr/agents/genoma.md +102 -0
- package/template/.aioson/locales/fr/agents/orchestrator.md +108 -0
- package/template/.aioson/locales/fr/agents/pm.md +81 -0
- package/template/.aioson/locales/fr/agents/product.md +310 -0
- package/template/.aioson/locales/fr/agents/profiler-enricher.md +5 -0
- package/template/.aioson/locales/fr/agents/profiler-forge.md +5 -0
- package/template/.aioson/locales/fr/agents/profiler-researcher.md +5 -0
- package/template/.aioson/locales/fr/agents/qa.md +163 -0
- package/template/.aioson/locales/fr/agents/setup.md +347 -0
- package/template/.aioson/locales/fr/agents/squad.md +247 -0
- package/template/.aioson/locales/fr/agents/ux-ui.md +201 -0
- package/template/.aioson/locales/pt-BR/agents/analyst.md +217 -0
- package/template/.aioson/locales/pt-BR/agents/architect.md +213 -0
- package/template/.aioson/locales/pt-BR/agents/dev.md +198 -0
- package/template/.aioson/locales/pt-BR/agents/discovery-design-doc.md +198 -0
- package/template/.aioson/locales/pt-BR/agents/genoma.md +297 -0
- package/template/.aioson/locales/pt-BR/agents/orchestrator.md +108 -0
- package/template/.aioson/locales/pt-BR/agents/pm.md +81 -0
- package/template/.aioson/locales/pt-BR/agents/product.md +316 -0
- package/template/.aioson/locales/pt-BR/agents/profiler-enricher.md +5 -0
- package/template/.aioson/locales/pt-BR/agents/profiler-forge.md +5 -0
- package/template/.aioson/locales/pt-BR/agents/profiler-researcher.md +5 -0
- package/template/.aioson/locales/pt-BR/agents/qa.md +217 -0
- package/template/.aioson/locales/pt-BR/agents/setup.md +371 -0
- package/template/.aioson/locales/pt-BR/agents/squad.md +772 -0
- package/template/.aioson/locales/pt-BR/agents/ux-ui.md +322 -0
- package/template/.aioson/mcp/servers.md +24 -0
- package/template/.aioson/profiler-reports/.gitkeep +1 -0
- package/template/.aioson/schemas/content-blueprint.schema.json +30 -0
- package/template/.aioson/schemas/genome-meta.schema.json +150 -0
- package/template/.aioson/schemas/genome.schema.json +115 -0
- package/template/.aioson/schemas/readiness.schema.json +27 -0
- package/template/.aioson/schemas/squad-blueprint.schema.json +172 -0
- package/template/.aioson/schemas/squad-manifest.schema.json +276 -0
- package/template/.aioson/skills/dynamic/README.md +30 -0
- package/template/.aioson/skills/dynamic/cardano-docs.md +16 -0
- package/template/.aioson/skills/dynamic/ethereum-docs.md +17 -0
- package/template/.aioson/skills/dynamic/flux-ui-docs.md +13 -0
- package/template/.aioson/skills/dynamic/laravel-docs.md +41 -0
- package/template/.aioson/skills/dynamic/npm-packages.md +16 -0
- package/template/.aioson/skills/dynamic/solana-docs.md +16 -0
- package/template/.aioson/skills/references/premium-command-center-ui/master-application-prompt.md +79 -0
- package/template/.aioson/skills/references/premium-command-center-ui/operational-ux-playbook.md +253 -0
- package/template/.aioson/skills/references/premium-command-center-ui/quality-validation-checklist.md +82 -0
- package/template/.aioson/skills/references/premium-command-center-ui/visual-system-and-component-patterns.md +270 -0
- package/template/.aioson/skills/static/django-patterns.md +342 -0
- package/template/.aioson/skills/static/fastapi-patterns.md +344 -0
- package/template/.aioson/skills/static/filament-patterns.md +267 -0
- package/template/.aioson/skills/static/flux-ui-components.md +262 -0
- package/template/.aioson/skills/static/git-conventions.md +227 -0
- package/template/.aioson/skills/static/interface-design.md +372 -0
- package/template/.aioson/skills/static/jetstream-setup.md +200 -0
- package/template/.aioson/skills/static/laravel-conventions.md +491 -0
- package/template/.aioson/skills/static/nextjs-patterns.md +321 -0
- package/template/.aioson/skills/static/node-express-patterns.md +317 -0
- package/template/.aioson/skills/static/node-typescript-patterns.md +282 -0
- package/template/.aioson/skills/static/premium-command-center-ui.md +190 -0
- package/template/.aioson/skills/static/rails-conventions.md +307 -0
- package/template/.aioson/skills/static/react-motion-patterns.md +577 -0
- package/template/.aioson/skills/static/static-html-patterns.md +1935 -0
- package/template/.aioson/skills/static/tall-stack-patterns.md +286 -0
- package/template/.aioson/skills/static/ui-ux-modern.md +75 -0
- package/template/.aioson/skills/static/web3-cardano-patterns.md +337 -0
- package/template/.aioson/skills/static/web3-ethereum-patterns.md +310 -0
- package/template/.aioson/skills/static/web3-security-checklist.md +284 -0
- package/template/.aioson/skills/static/web3-solana-patterns.md +324 -0
- package/template/.aioson/squads/.artisan/.gitkeep +0 -0
- package/template/.aioson/squads/.gitkeep +0 -0
- package/template/.aioson/squads/memory.md +5 -0
- package/template/.aioson/tasks/squad-analyze.md +83 -0
- package/template/.aioson/tasks/squad-create.md +99 -0
- package/template/.aioson/tasks/squad-design.md +100 -0
- package/template/.aioson/tasks/squad-export.md +20 -0
- package/template/.aioson/tasks/squad-extend.md +68 -0
- package/template/.aioson/tasks/squad-pipeline.md +122 -0
- package/template/.aioson/tasks/squad-repair.md +85 -0
- package/template/.aioson/tasks/squad-validate.md +58 -0
- package/template/.aioson/templates/squads/content-basic/template.json +21 -0
- package/template/.aioson/templates/squads/media-channel/template.json +24 -0
- package/template/.aioson/templates/squads/research-analysis/template.json +22 -0
- package/template/.aioson/templates/squads/software-delivery/template.json +21 -0
- package/template/.claude/commands/aioson/analyst.md +5 -0
- package/template/.claude/commands/aioson/architect.md +5 -0
- package/template/.claude/commands/aioson/dev.md +5 -0
- package/template/.claude/commands/aioson/orchestrator.md +5 -0
- package/template/.claude/commands/aioson/pm.md +5 -0
- package/template/.claude/commands/aioson/qa.md +5 -0
- package/template/.claude/commands/aioson/setup.md +5 -0
- package/template/.claude/commands/aioson/ux-ui.md +5 -0
- package/template/.gemini/GEMINI.md +10 -0
- package/template/.gemini/commands/aios-analyst.toml +4 -0
- package/template/.gemini/commands/aios-architect.toml +7 -0
- package/template/.gemini/commands/aios-dev.toml +8 -0
- package/template/.gemini/commands/aios-discovery-design-doc.toml +4 -0
- package/template/.gemini/commands/aios-orchestrator.toml +8 -0
- package/template/.gemini/commands/aios-pm.toml +8 -0
- package/template/.gemini/commands/aios-product.toml +4 -0
- package/template/.gemini/commands/aios-qa.toml +6 -0
- package/template/.gemini/commands/aios-setup.toml +3 -0
- package/template/.gemini/commands/aios-ux-ui.toml +8 -0
- package/template/AGENTS.md +67 -0
- package/template/CLAUDE.md +31 -0
- package/template/OPENCODE.md +24 -0
- package/template/aioson-models.json +40 -0
|
@@ -0,0 +1,1935 @@
|
|
|
1
|
+
# Static HTML Patterns — Landing Page Production Guide
|
|
2
|
+
|
|
3
|
+
> Read this skill when building any landing page (project_type=site).
|
|
4
|
+
> This covers structure, modern CSS techniques, performance, and the "wow factor" that makes users stop scrolling.
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## 0. THE HERO LAW (read this first, it overrides everything)
|
|
9
|
+
|
|
10
|
+
> **The hero section is NEVER a grid of cards.**
|
|
11
|
+
> The hero is: full viewport height — background (animated mesh OR full-bleed photograph) — ONE large headline — ONE or TWO supporting lines — TWO buttons — optional social proof strip below. That is it.
|
|
12
|
+
>
|
|
13
|
+
> Card grids belong in the Features or Tools section. A hero with cards looks like a dashboard, not a landing page.
|
|
14
|
+
>
|
|
15
|
+
> For Bold & Cinematic: the hero background must be ALIVE (animated mesh gradient or parallax image).
|
|
16
|
+
> For Clean & Luminous: the hero background must have generous whitespace and a large, confident headline.
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## 1. HTML Structure — The Production Shell
|
|
21
|
+
|
|
22
|
+
Every landing page follows this semantic skeleton:
|
|
23
|
+
|
|
24
|
+
```html
|
|
25
|
+
<!DOCTYPE html>
|
|
26
|
+
<html lang="pt-BR">
|
|
27
|
+
<head>
|
|
28
|
+
<meta charset="UTF-8">
|
|
29
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
30
|
+
<meta name="description" content="[real product description]">
|
|
31
|
+
<title>[Product Name] — [Value Proposition]</title>
|
|
32
|
+
|
|
33
|
+
<!-- Preload critical fonts (eliminate layout shift) -->
|
|
34
|
+
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
35
|
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
36
|
+
<link href="https://fonts.googleapis.com/css2?family=[ChosenFont]:wght@400;500;600;700;800&display=swap" rel="stylesheet">
|
|
37
|
+
|
|
38
|
+
<!-- Inline critical CSS (above-the-fold only: reset, header, hero) -->
|
|
39
|
+
<style>
|
|
40
|
+
/* === CRITICAL CSS — inline here, rest in external file === */
|
|
41
|
+
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
|
42
|
+
html { font-size: 16px; scroll-behavior: smooth; }
|
|
43
|
+
body { font-family: var(--font-body); background: var(--color-bg); color: var(--color-text); -webkit-font-smoothing: antialiased; }
|
|
44
|
+
img { max-width: 100%; display: block; object-fit: cover; }
|
|
45
|
+
a { text-decoration: none; color: inherit; }
|
|
46
|
+
button { cursor: pointer; border: none; font: inherit; }
|
|
47
|
+
/* rest of CSS below */
|
|
48
|
+
</style>
|
|
49
|
+
</head>
|
|
50
|
+
<body>
|
|
51
|
+
|
|
52
|
+
<!-- HEADER: sticky, transparent → solid on scroll -->
|
|
53
|
+
<header class="header" id="header">
|
|
54
|
+
<div class="container header__inner">
|
|
55
|
+
<a href="/" class="header__logo" aria-label="Home">
|
|
56
|
+
<span class="logo-mark">●</span>
|
|
57
|
+
<span class="logo-text">ProductName</span>
|
|
58
|
+
</a>
|
|
59
|
+
<nav class="header__nav" aria-label="Main navigation">
|
|
60
|
+
<a href="#features">Features</a>
|
|
61
|
+
<a href="#how-it-works">How it works</a>
|
|
62
|
+
<a href="#pricing">Pricing</a>
|
|
63
|
+
</nav>
|
|
64
|
+
<a href="#cta" class="btn btn--primary btn--sm">Get started</a>
|
|
65
|
+
<button class="header__burger" aria-label="Open menu" aria-expanded="false">
|
|
66
|
+
<span></span><span></span><span></span>
|
|
67
|
+
</button>
|
|
68
|
+
</div>
|
|
69
|
+
</header>
|
|
70
|
+
|
|
71
|
+
<main>
|
|
72
|
+
<!-- HERO: full viewport, image/gradient + content -->
|
|
73
|
+
<section class="hero" aria-labelledby="hero-title">
|
|
74
|
+
<div class="hero__bg">
|
|
75
|
+
<img src="https://images.unsplash.com/photo-[id]?w=1920&q=80&fit=crop"
|
|
76
|
+
alt="" role="presentation" loading="eager" fetchpriority="high">
|
|
77
|
+
<div class="hero__overlay" aria-hidden="true"></div>
|
|
78
|
+
</div>
|
|
79
|
+
<div class="container hero__content">
|
|
80
|
+
<span class="hero__label reveal">Category / Tagline</span>
|
|
81
|
+
<h1 class="hero__title reveal reveal-delay-1" id="hero-title">
|
|
82
|
+
Bold Headline That <em class="gradient-text">Changes Everything</em>
|
|
83
|
+
</h1>
|
|
84
|
+
<p class="hero__subtitle reveal reveal-delay-2">
|
|
85
|
+
Supporting text that adds real context — who benefits, how fast, what outcome.
|
|
86
|
+
</p>
|
|
87
|
+
<div class="hero__actions reveal reveal-delay-3">
|
|
88
|
+
<a href="#signup" class="btn btn--primary btn--lg">Primary CTA</a>
|
|
89
|
+
<a href="#demo" class="btn btn--ghost btn--lg">
|
|
90
|
+
<span class="btn__icon" aria-hidden="true">▶</span>
|
|
91
|
+
Watch demo
|
|
92
|
+
</a>
|
|
93
|
+
</div>
|
|
94
|
+
<div class="hero__social-proof reveal reveal-delay-4">
|
|
95
|
+
<span>Trusted by 2,000+ teams</span>
|
|
96
|
+
<div class="avatar-group" aria-label="User avatars">
|
|
97
|
+
<img src="https://images.unsplash.com/photo-1535713875002-d1d0cf377fde?w=40&h=40&fit=crop&crop=face" alt="User" width="40" height="40">
|
|
98
|
+
<img src="https://images.unsplash.com/photo-1494790108377-be9c29b29330?w=40&h=40&fit=crop&crop=face" alt="User" width="40" height="40">
|
|
99
|
+
<img src="https://images.unsplash.com/photo-1527980965255-d3b416303d12?w=40&h=40&fit=crop&crop=face" alt="User" width="40" height="40">
|
|
100
|
+
</div>
|
|
101
|
+
<div class="star-rating" aria-label="5 out of 5 stars">★★★★★ <span>4.9</span></div>
|
|
102
|
+
</div>
|
|
103
|
+
</div>
|
|
104
|
+
<!-- Decorative floating elements (aria-hidden) -->
|
|
105
|
+
<div class="hero__decor" aria-hidden="true">
|
|
106
|
+
<div class="orb orb--1"></div>
|
|
107
|
+
<div class="orb orb--2"></div>
|
|
108
|
+
<div class="orb orb--3"></div>
|
|
109
|
+
</div>
|
|
110
|
+
</section>
|
|
111
|
+
|
|
112
|
+
<!-- LOGOS: social proof bar -->
|
|
113
|
+
<section class="logos-bar" aria-label="Trusted by">
|
|
114
|
+
<div class="container">
|
|
115
|
+
<p class="logos-bar__label">Trusted by teams at</p>
|
|
116
|
+
<div class="logos-bar__track">
|
|
117
|
+
<!-- Logo images or text placeholders -->
|
|
118
|
+
</div>
|
|
119
|
+
</div>
|
|
120
|
+
</section>
|
|
121
|
+
|
|
122
|
+
<!-- FEATURES: 3 or 6 cards in grid -->
|
|
123
|
+
<section class="features" id="features" aria-labelledby="features-title">
|
|
124
|
+
<div class="container">
|
|
125
|
+
<div class="section-header">
|
|
126
|
+
<span class="section-label">Features</span>
|
|
127
|
+
<h2 class="section-title" id="features-title">Everything you need</h2>
|
|
128
|
+
<p class="section-subtitle">One sentence expanding on the value.</p>
|
|
129
|
+
</div>
|
|
130
|
+
<div class="features__grid">
|
|
131
|
+
<article class="feature-card reveal">
|
|
132
|
+
<div class="feature-card__icon" aria-hidden="true">⚡</div>
|
|
133
|
+
<h3 class="feature-card__title">Feature Name</h3>
|
|
134
|
+
<p class="feature-card__desc">What it does and why it matters to the user.</p>
|
|
135
|
+
</article>
|
|
136
|
+
<!-- repeat 5 more -->
|
|
137
|
+
</div>
|
|
138
|
+
</div>
|
|
139
|
+
</section>
|
|
140
|
+
|
|
141
|
+
<!-- HOW IT WORKS: numbered steps, alternating layout -->
|
|
142
|
+
<section class="how-it-works" id="how-it-works" aria-labelledby="hiw-title">
|
|
143
|
+
<div class="container">
|
|
144
|
+
<div class="section-header">
|
|
145
|
+
<span class="section-label">Process</span>
|
|
146
|
+
<h2 class="section-title" id="hiw-title">How it works</h2>
|
|
147
|
+
</div>
|
|
148
|
+
<ol class="steps" role="list">
|
|
149
|
+
<li class="step reveal">
|
|
150
|
+
<span class="step__number" aria-hidden="true">01</span>
|
|
151
|
+
<div class="step__content">
|
|
152
|
+
<h3>Step title</h3>
|
|
153
|
+
<p>Explanation focused on what the user does and gets.</p>
|
|
154
|
+
</div>
|
|
155
|
+
<div class="step__visual">
|
|
156
|
+
<img src="https://images.unsplash.com/photo-[id]?w=600&q=80" alt="[descriptive alt]" loading="lazy">
|
|
157
|
+
</div>
|
|
158
|
+
</li>
|
|
159
|
+
</ol>
|
|
160
|
+
</div>
|
|
161
|
+
</section>
|
|
162
|
+
|
|
163
|
+
<!-- TESTIMONIALS -->
|
|
164
|
+
<section class="testimonials" aria-labelledby="testimonials-title">
|
|
165
|
+
<div class="container">
|
|
166
|
+
<h2 class="section-title" id="testimonials-title">What teams say</h2>
|
|
167
|
+
<div class="testimonials__grid">
|
|
168
|
+
<blockquote class="testimonial-card reveal">
|
|
169
|
+
<p class="testimonial-card__quote">"Specific outcome they achieved using the product."</p>
|
|
170
|
+
<footer class="testimonial-card__author">
|
|
171
|
+
<img src="https://images.unsplash.com/photo-[id]?w=48&h=48&fit=crop&crop=face"
|
|
172
|
+
alt="[Name]" width="48" height="48" loading="lazy">
|
|
173
|
+
<div>
|
|
174
|
+
<cite class="testimonial-card__name">Full Name</cite>
|
|
175
|
+
<span class="testimonial-card__role">Role, Company</span>
|
|
176
|
+
</div>
|
|
177
|
+
</footer>
|
|
178
|
+
</blockquote>
|
|
179
|
+
</div>
|
|
180
|
+
</div>
|
|
181
|
+
</section>
|
|
182
|
+
|
|
183
|
+
<!-- FINAL CTA -->
|
|
184
|
+
<section class="cta-final" id="cta" aria-labelledby="cta-title">
|
|
185
|
+
<div class="container">
|
|
186
|
+
<h2 class="cta-final__title reveal" id="cta-title">Ready to get started?</h2>
|
|
187
|
+
<p class="cta-final__subtitle reveal reveal-delay-1">Join thousands of teams already using [Product].</p>
|
|
188
|
+
<a href="#signup" class="btn btn--primary btn--xl reveal reveal-delay-2">Start for free</a>
|
|
189
|
+
<p class="cta-final__note">No credit card required · Cancel anytime</p>
|
|
190
|
+
</div>
|
|
191
|
+
</section>
|
|
192
|
+
</main>
|
|
193
|
+
|
|
194
|
+
<footer class="footer" role="contentinfo">
|
|
195
|
+
<div class="container footer__inner">
|
|
196
|
+
<a href="/" class="footer__logo" aria-label="Home">ProductName</a>
|
|
197
|
+
<nav class="footer__nav" aria-label="Footer navigation">
|
|
198
|
+
<a href="/privacy">Privacy</a>
|
|
199
|
+
<a href="/terms">Terms</a>
|
|
200
|
+
<a href="/contact">Contact</a>
|
|
201
|
+
</nav>
|
|
202
|
+
<p class="footer__copy">© 2026 ProductName. All rights reserved.</p>
|
|
203
|
+
</div>
|
|
204
|
+
</footer>
|
|
205
|
+
|
|
206
|
+
<!-- Scripts: async, non-blocking -->
|
|
207
|
+
<script async src="main.js"></script>
|
|
208
|
+
</body>
|
|
209
|
+
</html>
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
---
|
|
213
|
+
|
|
214
|
+
## 2. CSS Design Systems
|
|
215
|
+
|
|
216
|
+
### 2a. Bold & Cinematic (Dark, AI/Tech, Gradient-rich)
|
|
217
|
+
|
|
218
|
+
```css
|
|
219
|
+
/* === TOKENS === */
|
|
220
|
+
:root {
|
|
221
|
+
/* Background scale */
|
|
222
|
+
--bg-base: hsl(240, 15%, 6%); /* deepest: page bg */
|
|
223
|
+
--bg-surface: hsl(240, 12%, 10%); /* cards, panels */
|
|
224
|
+
--bg-elevated: hsl(240, 10%, 14%); /* modals, popovers */
|
|
225
|
+
--bg-glass: hsla(240, 20%, 100%, 0.06); /* glassmorphism */
|
|
226
|
+
|
|
227
|
+
/* Text scale */
|
|
228
|
+
--text-primary: hsl(220, 30%, 96%);
|
|
229
|
+
--text-secondary: hsl(220, 15%, 70%);
|
|
230
|
+
--text-muted: hsl(220, 10%, 50%);
|
|
231
|
+
|
|
232
|
+
/* Brand & accent */
|
|
233
|
+
--accent-primary: hsl(265, 80%, 65%); /* purple */
|
|
234
|
+
--accent-glow: hsla(265, 80%, 65%, 0.35);
|
|
235
|
+
--accent-alt: hsl(190, 80%, 55%); /* cyan complement */
|
|
236
|
+
--gradient-brand: linear-gradient(135deg, hsl(265, 80%, 65%), hsl(310, 75%, 60%));
|
|
237
|
+
--gradient-hero: linear-gradient(135deg,
|
|
238
|
+
hsla(240, 50%, 8%, 0.95) 0%,
|
|
239
|
+
hsla(265, 60%, 20%, 0.75) 50%,
|
|
240
|
+
hsla(310, 40%, 12%, 0.5) 100%);
|
|
241
|
+
|
|
242
|
+
/* Borders */
|
|
243
|
+
--border-faint: hsla(220, 100%, 90%, 0.07);
|
|
244
|
+
--border-subtle: hsla(220, 100%, 90%, 0.12);
|
|
245
|
+
--border-glow: hsla(265, 80%, 65%, 0.3);
|
|
246
|
+
|
|
247
|
+
/* Spacing */
|
|
248
|
+
--space-xs: 4px; --space-sm: 8px; --space-md: 16px;
|
|
249
|
+
--space-lg: 24px; --space-xl: 40px; --space-2xl: 64px;
|
|
250
|
+
--space-3xl: 96px; --space-4xl: 160px;
|
|
251
|
+
|
|
252
|
+
/* Typography */
|
|
253
|
+
--font-display: 'Space Grotesk', 'Syne', sans-serif;
|
|
254
|
+
--font-body: 'Inter', system-ui, sans-serif;
|
|
255
|
+
--font-mono: 'JetBrains Mono', monospace;
|
|
256
|
+
|
|
257
|
+
/* Type scale */
|
|
258
|
+
--text-sm: 0.875rem; /* 14px */
|
|
259
|
+
--text-base: 1rem; /* 16px */
|
|
260
|
+
--text-lg: 1.125rem; /* 18px */
|
|
261
|
+
--text-xl: 1.25rem; /* 20px */
|
|
262
|
+
--text-2xl: 1.5rem; /* 24px */
|
|
263
|
+
--text-3xl: 1.875rem; /* 30px */
|
|
264
|
+
--text-4xl: 2.25rem; /* 36px */
|
|
265
|
+
--text-5xl: 3rem; /* 48px */
|
|
266
|
+
--text-6xl: 3.75rem; /* 60px */
|
|
267
|
+
--text-7xl: 4.5rem; /* 72px */
|
|
268
|
+
|
|
269
|
+
/* Radius */
|
|
270
|
+
--radius-sm: 6px; --radius-md: 12px; --radius-lg: 20px; --radius-pill: 9999px;
|
|
271
|
+
|
|
272
|
+
/* Motion */
|
|
273
|
+
--ease-out: cubic-bezier(0.16, 1, 0.3, 1);
|
|
274
|
+
--ease-spring: cubic-bezier(0.34, 1.56, 0.64, 1);
|
|
275
|
+
--duration-fast: 150ms; --duration-base: 250ms; --duration-slow: 400ms;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
/* === LAYOUT === */
|
|
279
|
+
.container {
|
|
280
|
+
width: 100%;
|
|
281
|
+
max-width: 1200px;
|
|
282
|
+
margin: 0 auto;
|
|
283
|
+
padding: 0 var(--space-xl);
|
|
284
|
+
}
|
|
285
|
+
@media (max-width: 768px) { .container { padding: 0 var(--space-lg); } }
|
|
286
|
+
|
|
287
|
+
/* === HEADER: glassmorphism, sticky === */
|
|
288
|
+
.header {
|
|
289
|
+
position: sticky; top: 0; left: 0; right: 0; z-index: 100;
|
|
290
|
+
height: 72px;
|
|
291
|
+
backdrop-filter: blur(20px) saturate(180%);
|
|
292
|
+
-webkit-backdrop-filter: blur(20px) saturate(180%);
|
|
293
|
+
background: hsla(240, 15%, 6%, 0.75);
|
|
294
|
+
border-bottom: 1px solid var(--border-faint);
|
|
295
|
+
transition: background var(--duration-base) ease;
|
|
296
|
+
}
|
|
297
|
+
.header.scrolled { background: hsla(240, 15%, 6%, 0.95); }
|
|
298
|
+
.header__inner {
|
|
299
|
+
height: 100%;
|
|
300
|
+
display: flex; align-items: center; gap: var(--space-xl);
|
|
301
|
+
}
|
|
302
|
+
.header__logo {
|
|
303
|
+
display: flex; align-items: center; gap: var(--space-sm);
|
|
304
|
+
font-family: var(--font-display); font-weight: 700; font-size: var(--text-lg);
|
|
305
|
+
color: var(--text-primary);
|
|
306
|
+
}
|
|
307
|
+
.logo-mark { color: var(--accent-primary); }
|
|
308
|
+
.header__nav {
|
|
309
|
+
display: flex; gap: var(--space-xl);
|
|
310
|
+
margin-left: auto; /* push right */
|
|
311
|
+
}
|
|
312
|
+
.header__nav a {
|
|
313
|
+
font-size: var(--text-sm); font-weight: 500;
|
|
314
|
+
color: var(--text-secondary);
|
|
315
|
+
transition: color var(--duration-fast) ease;
|
|
316
|
+
}
|
|
317
|
+
.header__nav a:hover { color: var(--text-primary); }
|
|
318
|
+
|
|
319
|
+
/* === HERO === */
|
|
320
|
+
.hero {
|
|
321
|
+
position: relative;
|
|
322
|
+
min-height: 100dvh;
|
|
323
|
+
display: flex; align-items: center; justify-content: center;
|
|
324
|
+
overflow: hidden;
|
|
325
|
+
}
|
|
326
|
+
.hero__bg {
|
|
327
|
+
position: absolute; inset: 0; z-index: 0;
|
|
328
|
+
}
|
|
329
|
+
.hero__bg img {
|
|
330
|
+
width: 100%; height: 100%;
|
|
331
|
+
object-fit: cover; object-position: center;
|
|
332
|
+
}
|
|
333
|
+
.hero__overlay {
|
|
334
|
+
position: absolute; inset: 0;
|
|
335
|
+
background: var(--gradient-hero);
|
|
336
|
+
}
|
|
337
|
+
.hero__content {
|
|
338
|
+
position: relative; z-index: 1;
|
|
339
|
+
text-align: center;
|
|
340
|
+
padding: var(--space-4xl) 0;
|
|
341
|
+
}
|
|
342
|
+
.hero__label {
|
|
343
|
+
display: inline-block;
|
|
344
|
+
padding: 6px 16px;
|
|
345
|
+
border-radius: var(--radius-pill);
|
|
346
|
+
border: 1px solid var(--border-glow);
|
|
347
|
+
background: hsla(265, 80%, 65%, 0.1);
|
|
348
|
+
font-size: var(--text-sm); font-weight: 500; letter-spacing: 0.05em; text-transform: uppercase;
|
|
349
|
+
color: var(--accent-primary);
|
|
350
|
+
margin-bottom: var(--space-xl);
|
|
351
|
+
}
|
|
352
|
+
.hero__title {
|
|
353
|
+
font-family: var(--font-display);
|
|
354
|
+
font-size: clamp(var(--text-4xl), 7vw, var(--text-7xl));
|
|
355
|
+
font-weight: 800; line-height: 1.05; letter-spacing: -0.03em;
|
|
356
|
+
color: var(--text-primary);
|
|
357
|
+
margin-bottom: var(--space-lg);
|
|
358
|
+
}
|
|
359
|
+
.hero__subtitle {
|
|
360
|
+
font-size: clamp(var(--text-lg), 2.5vw, var(--text-xl));
|
|
361
|
+
color: var(--text-secondary); line-height: 1.6; max-width: 600px; margin: 0 auto var(--space-xl);
|
|
362
|
+
}
|
|
363
|
+
.hero__actions { display: flex; gap: var(--space-md); justify-content: center; flex-wrap: wrap; }
|
|
364
|
+
|
|
365
|
+
/* Decorative glowing orbs */
|
|
366
|
+
.hero__decor { position: absolute; inset: 0; z-index: 0; pointer-events: none; }
|
|
367
|
+
.orb {
|
|
368
|
+
position: absolute; border-radius: 50%;
|
|
369
|
+
filter: blur(80px); opacity: 0.35;
|
|
370
|
+
}
|
|
371
|
+
.orb--1 {
|
|
372
|
+
width: 600px; height: 600px;
|
|
373
|
+
background: radial-gradient(circle, hsl(265, 80%, 65%), transparent 70%);
|
|
374
|
+
top: -200px; left: -100px;
|
|
375
|
+
}
|
|
376
|
+
.orb--2 {
|
|
377
|
+
width: 400px; height: 400px;
|
|
378
|
+
background: radial-gradient(circle, hsl(190, 80%, 55%), transparent 70%);
|
|
379
|
+
bottom: -100px; right: -50px;
|
|
380
|
+
}
|
|
381
|
+
.orb--3 {
|
|
382
|
+
width: 300px; height: 300px;
|
|
383
|
+
background: radial-gradient(circle, hsl(310, 75%, 60%), transparent 70%);
|
|
384
|
+
top: 50%; left: 50%; transform: translate(-50%, -50%);
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
/* === GRADIENT TEXT === */
|
|
388
|
+
.gradient-text {
|
|
389
|
+
background: var(--gradient-brand);
|
|
390
|
+
-webkit-background-clip: text; -webkit-text-fill-color: transparent;
|
|
391
|
+
background-clip: text; display: inline;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
/* === BUTTONS === */
|
|
395
|
+
.btn {
|
|
396
|
+
display: inline-flex; align-items: center; gap: var(--space-sm);
|
|
397
|
+
font-weight: 600; border-radius: var(--radius-md);
|
|
398
|
+
transition: all var(--duration-base) var(--ease-out);
|
|
399
|
+
white-space: nowrap;
|
|
400
|
+
}
|
|
401
|
+
.btn--sm { padding: 8px 16px; font-size: var(--text-sm); }
|
|
402
|
+
.btn--md { padding: 12px 24px; font-size: var(--text-base); }
|
|
403
|
+
.btn--lg { padding: 14px 32px; font-size: var(--text-lg); }
|
|
404
|
+
.btn--xl { padding: 18px 48px; font-size: var(--text-xl); }
|
|
405
|
+
.btn--primary {
|
|
406
|
+
background: var(--gradient-brand);
|
|
407
|
+
color: white;
|
|
408
|
+
box-shadow: 0 0 32px var(--accent-glow), 0 4px 16px rgba(0,0,0,0.3);
|
|
409
|
+
}
|
|
410
|
+
.btn--primary:hover {
|
|
411
|
+
box-shadow: 0 0 48px hsla(265, 80%, 65%, 0.55), 0 8px 24px rgba(0,0,0,0.4);
|
|
412
|
+
transform: translateY(-2px) scale(1.02);
|
|
413
|
+
}
|
|
414
|
+
.btn--primary:active { transform: translateY(0) scale(0.99); }
|
|
415
|
+
.btn--ghost {
|
|
416
|
+
background: var(--bg-glass);
|
|
417
|
+
color: var(--text-primary);
|
|
418
|
+
border: 1px solid var(--border-subtle);
|
|
419
|
+
backdrop-filter: blur(8px);
|
|
420
|
+
}
|
|
421
|
+
.btn--ghost:hover {
|
|
422
|
+
background: hsla(240, 20%, 100%, 0.1);
|
|
423
|
+
border-color: var(--border-glow);
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
/* === GLASSMORPHISM CARDS === */
|
|
427
|
+
.feature-card {
|
|
428
|
+
background: var(--bg-glass);
|
|
429
|
+
border: 1px solid var(--border-faint);
|
|
430
|
+
border-radius: var(--radius-lg);
|
|
431
|
+
padding: var(--space-xl);
|
|
432
|
+
backdrop-filter: blur(16px);
|
|
433
|
+
-webkit-backdrop-filter: blur(16px);
|
|
434
|
+
transition: all var(--duration-base) var(--ease-out);
|
|
435
|
+
position: relative; overflow: hidden;
|
|
436
|
+
}
|
|
437
|
+
.feature-card::before {
|
|
438
|
+
/* Shimmer top border */
|
|
439
|
+
content: '';
|
|
440
|
+
position: absolute; top: 0; left: 0; right: 0; height: 1px;
|
|
441
|
+
background: linear-gradient(90deg, transparent, var(--accent-primary), transparent);
|
|
442
|
+
opacity: 0;
|
|
443
|
+
transition: opacity var(--duration-base) ease;
|
|
444
|
+
}
|
|
445
|
+
.feature-card:hover {
|
|
446
|
+
border-color: var(--border-glow);
|
|
447
|
+
transform: translateY(-4px);
|
|
448
|
+
box-shadow: 0 20px 60px rgba(0,0,0,0.3), 0 0 40px var(--accent-glow);
|
|
449
|
+
}
|
|
450
|
+
.feature-card:hover::before { opacity: 1; }
|
|
451
|
+
.feature-card__icon {
|
|
452
|
+
font-size: var(--text-3xl); margin-bottom: var(--space-md);
|
|
453
|
+
}
|
|
454
|
+
.feature-card__title {
|
|
455
|
+
font-family: var(--font-display); font-size: var(--text-xl); font-weight: 700;
|
|
456
|
+
color: var(--text-primary); margin-bottom: var(--space-sm);
|
|
457
|
+
}
|
|
458
|
+
.feature-card__desc { font-size: var(--text-base); color: var(--text-secondary); line-height: 1.6; }
|
|
459
|
+
|
|
460
|
+
/* Grid: 3 cols → 2 → 1 */
|
|
461
|
+
.features__grid {
|
|
462
|
+
display: grid;
|
|
463
|
+
grid-template-columns: repeat(3, 1fr);
|
|
464
|
+
gap: var(--space-lg);
|
|
465
|
+
margin-top: var(--space-3xl);
|
|
466
|
+
}
|
|
467
|
+
@media (max-width: 900px) { .features__grid { grid-template-columns: repeat(2, 1fr); } }
|
|
468
|
+
@media (max-width: 600px) { .features__grid { grid-template-columns: 1fr; } }
|
|
469
|
+
|
|
470
|
+
/* === ANIMATED GRADIENT BORDER === */
|
|
471
|
+
.card--glow-border {
|
|
472
|
+
position: relative;
|
|
473
|
+
background: var(--bg-surface);
|
|
474
|
+
border-radius: var(--radius-lg);
|
|
475
|
+
}
|
|
476
|
+
.card--glow-border::before {
|
|
477
|
+
content: '';
|
|
478
|
+
position: absolute; inset: -1px;
|
|
479
|
+
border-radius: inherit;
|
|
480
|
+
background: linear-gradient(135deg, hsl(265,80%,65%), hsl(190,80%,55%), hsl(310,75%,60%));
|
|
481
|
+
z-index: -1;
|
|
482
|
+
opacity: 0;
|
|
483
|
+
transition: opacity var(--duration-base) ease;
|
|
484
|
+
}
|
|
485
|
+
.card--glow-border:hover::before { opacity: 1; }
|
|
486
|
+
|
|
487
|
+
/* === SECTION HEADERS === */
|
|
488
|
+
.section-header { text-align: center; max-width: 700px; margin: 0 auto var(--space-3xl); }
|
|
489
|
+
.section-label {
|
|
490
|
+
display: inline-block;
|
|
491
|
+
font-size: var(--text-sm); font-weight: 600; letter-spacing: 0.1em; text-transform: uppercase;
|
|
492
|
+
color: var(--accent-primary); margin-bottom: var(--space-md);
|
|
493
|
+
}
|
|
494
|
+
.section-title {
|
|
495
|
+
font-family: var(--font-display);
|
|
496
|
+
font-size: clamp(var(--text-3xl), 4vw, var(--text-5xl));
|
|
497
|
+
font-weight: 800; line-height: 1.1; letter-spacing: -0.02em;
|
|
498
|
+
color: var(--text-primary); margin-bottom: var(--space-md);
|
|
499
|
+
}
|
|
500
|
+
.section-subtitle { font-size: var(--text-lg); color: var(--text-secondary); line-height: 1.6; }
|
|
501
|
+
|
|
502
|
+
/* Sections padding */
|
|
503
|
+
.features, .how-it-works, .testimonials { padding: var(--space-4xl) 0; }
|
|
504
|
+
|
|
505
|
+
/* Alternating section background */
|
|
506
|
+
.how-it-works {
|
|
507
|
+
background: linear-gradient(180deg, var(--bg-base) 0%, var(--bg-surface) 50%, var(--bg-base) 100%);
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
/* === ANGULAR CLIP-PATH DIVIDER === */
|
|
511
|
+
.section--angled {
|
|
512
|
+
clip-path: polygon(0 4%, 100% 0%, 100% 96%, 0 100%);
|
|
513
|
+
margin: -40px 0;
|
|
514
|
+
padding: calc(var(--space-4xl) + 40px) 0;
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
/* === TESTIMONIAL CARDS === */
|
|
518
|
+
.testimonials__grid {
|
|
519
|
+
display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--space-lg);
|
|
520
|
+
}
|
|
521
|
+
.testimonial-card {
|
|
522
|
+
background: var(--bg-surface);
|
|
523
|
+
border: 1px solid var(--border-faint);
|
|
524
|
+
border-radius: var(--radius-lg);
|
|
525
|
+
padding: var(--space-xl);
|
|
526
|
+
transition: border-color var(--duration-base) ease;
|
|
527
|
+
}
|
|
528
|
+
.testimonial-card:hover { border-color: var(--border-glow); }
|
|
529
|
+
.testimonial-card__quote {
|
|
530
|
+
font-size: var(--text-base); line-height: 1.7; color: var(--text-secondary);
|
|
531
|
+
font-style: italic; margin-bottom: var(--space-lg);
|
|
532
|
+
}
|
|
533
|
+
.testimonial-card__quote::before { content: '"'; font-size: var(--text-3xl); color: var(--accent-primary); line-height: 0; vertical-align: -0.4em; margin-right: 4px; }
|
|
534
|
+
.testimonial-card__author { display: flex; align-items: center; gap: var(--space-md); }
|
|
535
|
+
.testimonial-card__author img { border-radius: 50%; border: 2px solid var(--border-subtle); }
|
|
536
|
+
.testimonial-card__name { display: block; font-weight: 600; color: var(--text-primary); }
|
|
537
|
+
.testimonial-card__role { font-size: var(--text-sm); color: var(--text-muted); }
|
|
538
|
+
@media (max-width: 900px) { .testimonials__grid { grid-template-columns: 1fr; } }
|
|
539
|
+
|
|
540
|
+
/* === FINAL CTA === */
|
|
541
|
+
.cta-final {
|
|
542
|
+
text-align: center; padding: var(--space-4xl) 0;
|
|
543
|
+
background: radial-gradient(ellipse 80% 60% at 50% 50%, hsla(265,60%,30%,0.3), transparent);
|
|
544
|
+
border-top: 1px solid var(--border-faint);
|
|
545
|
+
}
|
|
546
|
+
.cta-final__title {
|
|
547
|
+
font-family: var(--font-display);
|
|
548
|
+
font-size: clamp(var(--text-4xl), 5vw, var(--text-6xl));
|
|
549
|
+
font-weight: 800; line-height: 1.1; letter-spacing: -0.02em;
|
|
550
|
+
color: var(--text-primary); margin-bottom: var(--space-md);
|
|
551
|
+
}
|
|
552
|
+
.cta-final__subtitle { font-size: var(--text-xl); color: var(--text-secondary); margin-bottom: var(--space-xl); }
|
|
553
|
+
.cta-final__note { font-size: var(--text-sm); color: var(--text-muted); margin-top: var(--space-md); }
|
|
554
|
+
|
|
555
|
+
/* === FOOTER === */
|
|
556
|
+
.footer {
|
|
557
|
+
background: var(--bg-surface); border-top: 1px solid var(--border-faint);
|
|
558
|
+
padding: var(--space-xl) 0;
|
|
559
|
+
}
|
|
560
|
+
.footer__inner {
|
|
561
|
+
display: flex; align-items: center; gap: var(--space-xl);
|
|
562
|
+
flex-wrap: wrap;
|
|
563
|
+
}
|
|
564
|
+
.footer__logo { font-family: var(--font-display); font-weight: 700; color: var(--text-primary); }
|
|
565
|
+
.footer__nav { display: flex; gap: var(--space-lg); margin-left: auto; }
|
|
566
|
+
.footer__nav a { font-size: var(--text-sm); color: var(--text-muted); transition: color var(--duration-fast) ease; }
|
|
567
|
+
.footer__nav a:hover { color: var(--text-primary); }
|
|
568
|
+
.footer__copy { font-size: var(--text-sm); color: var(--text-muted); }
|
|
569
|
+
|
|
570
|
+
/* === SCROLL REVEAL ANIMATIONS === */
|
|
571
|
+
.reveal {
|
|
572
|
+
animation: fadeUp 0.7s var(--ease-out) both;
|
|
573
|
+
}
|
|
574
|
+
.reveal-delay-1 { animation-delay: 0.1s; }
|
|
575
|
+
.reveal-delay-2 { animation-delay: 0.2s; }
|
|
576
|
+
.reveal-delay-3 { animation-delay: 0.3s; }
|
|
577
|
+
.reveal-delay-4 { animation-delay: 0.4s; }
|
|
578
|
+
@keyframes fadeUp {
|
|
579
|
+
from { opacity: 0; transform: translateY(28px); }
|
|
580
|
+
to { opacity: 1; transform: translateY(0); }
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
/* === COUNTER ANIMATION (hero stats) === */
|
|
584
|
+
.stat-number {
|
|
585
|
+
font-family: var(--font-display); font-size: var(--text-5xl); font-weight: 800;
|
|
586
|
+
background: var(--gradient-brand);
|
|
587
|
+
-webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text;
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
/* === AVATAR GROUP === */
|
|
591
|
+
.avatar-group { display: flex; }
|
|
592
|
+
.avatar-group img {
|
|
593
|
+
width: 32px; height: 32px; border-radius: 50%;
|
|
594
|
+
border: 2px solid var(--bg-base);
|
|
595
|
+
margin-left: -8px;
|
|
596
|
+
}
|
|
597
|
+
.avatar-group img:first-child { margin-left: 0; }
|
|
598
|
+
|
|
599
|
+
/* === STAR RATING === */
|
|
600
|
+
.star-rating { color: hsl(45, 100%, 60%); font-size: var(--text-sm); font-weight: 600; }
|
|
601
|
+
.star-rating span { color: var(--text-secondary); margin-left: 4px; }
|
|
602
|
+
|
|
603
|
+
/* === ACCESSIBILITY === */
|
|
604
|
+
:focus-visible {
|
|
605
|
+
outline: 2px solid var(--accent-primary);
|
|
606
|
+
outline-offset: 3px;
|
|
607
|
+
border-radius: var(--radius-sm);
|
|
608
|
+
}
|
|
609
|
+
.sr-only {
|
|
610
|
+
position: absolute; width: 1px; height: 1px;
|
|
611
|
+
padding: 0; margin: -1px; overflow: hidden;
|
|
612
|
+
clip: rect(0,0,0,0); white-space: nowrap; border: 0;
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
/* === REDUCED MOTION === */
|
|
616
|
+
@media (prefers-reduced-motion: reduce) {
|
|
617
|
+
*, *::before, *::after { animation-duration: 0.01ms !important; transition-duration: 0.01ms !important; }
|
|
618
|
+
}
|
|
619
|
+
```
|
|
620
|
+
|
|
621
|
+
### 2a-extra. Mandatory "Wow" Techniques for Bold & Cinematic
|
|
622
|
+
|
|
623
|
+
These three techniques are **required** — not optional. They separate a premium landing page from a generic dark dashboard.
|
|
624
|
+
|
|
625
|
+
#### Animated mesh background (background breathes)
|
|
626
|
+
|
|
627
|
+
```css
|
|
628
|
+
/* Replace the static mesh with an animated one */
|
|
629
|
+
.hero__bg--mesh {
|
|
630
|
+
position: absolute; inset: 0; z-index: 0;
|
|
631
|
+
background:
|
|
632
|
+
radial-gradient(ellipse 120% 80% at -15% -10%, hsla(265,70%,55%,0.28), transparent 55%),
|
|
633
|
+
radial-gradient(ellipse 100% 70% at 115% 15%, hsla(190,70%,50%,0.25), transparent 50%),
|
|
634
|
+
radial-gradient(ellipse 120% 80% at 50% 110%, hsla(310,65%,55%,0.2), transparent 52%),
|
|
635
|
+
linear-gradient(160deg, hsl(240,18%,6%) 0%, hsl(240,15%,10%) 100%);
|
|
636
|
+
background-size: 200% 200%;
|
|
637
|
+
animation: meshDrift 20s ease infinite alternate;
|
|
638
|
+
}
|
|
639
|
+
@keyframes meshDrift {
|
|
640
|
+
0% { background-position: 0% 0%; }
|
|
641
|
+
33% { background-position: 60% 40%; }
|
|
642
|
+
66% { background-position: 40% 80%; }
|
|
643
|
+
100% { background-position: 100% 100%; }
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
@media (prefers-reduced-motion: reduce) {
|
|
647
|
+
.hero__bg--mesh { animation: none; }
|
|
648
|
+
}
|
|
649
|
+
```
|
|
650
|
+
|
|
651
|
+
**HTML usage:**
|
|
652
|
+
```html
|
|
653
|
+
<section class="hero">
|
|
654
|
+
<div class="hero__bg--mesh" aria-hidden="true"></div>
|
|
655
|
+
<!-- orbs still work on top of this -->
|
|
656
|
+
<div class="hero__decor" aria-hidden="true">
|
|
657
|
+
<div class="orb orb--1"></div>
|
|
658
|
+
<div class="orb orb--2"></div>
|
|
659
|
+
</div>
|
|
660
|
+
<div class="container hero__content"> ... </div>
|
|
661
|
+
</section>
|
|
662
|
+
```
|
|
663
|
+
|
|
664
|
+
---
|
|
665
|
+
|
|
666
|
+
#### Animated gradient text (headline that breathes color)
|
|
667
|
+
|
|
668
|
+
The headline `<em>` or key phrase has a gradient that slowly shifts — subtle but unmistakably premium.
|
|
669
|
+
|
|
670
|
+
```css
|
|
671
|
+
.gradient-text--animated {
|
|
672
|
+
background: linear-gradient(
|
|
673
|
+
135deg,
|
|
674
|
+
var(--accent-primary),
|
|
675
|
+
hsl(190, 80%, 55%),
|
|
676
|
+
hsl(310, 75%, 65%),
|
|
677
|
+
var(--accent-primary)
|
|
678
|
+
);
|
|
679
|
+
background-size: 300% 300%;
|
|
680
|
+
-webkit-background-clip: text;
|
|
681
|
+
-webkit-text-fill-color: transparent;
|
|
682
|
+
background-clip: text;
|
|
683
|
+
display: inline;
|
|
684
|
+
animation: textGradient 8s ease infinite;
|
|
685
|
+
}
|
|
686
|
+
@keyframes textGradient {
|
|
687
|
+
0%, 100% { background-position: 0% 50%; }
|
|
688
|
+
50% { background-position: 100% 50%; }
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
@media (prefers-reduced-motion: reduce) {
|
|
692
|
+
.gradient-text--animated { animation: none; }
|
|
693
|
+
}
|
|
694
|
+
```
|
|
695
|
+
|
|
696
|
+
**HTML usage:**
|
|
697
|
+
```html
|
|
698
|
+
<h1 class="hero__title">
|
|
699
|
+
The fastest way to <em class="gradient-text--animated">ship AI products</em>
|
|
700
|
+
</h1>
|
|
701
|
+
```
|
|
702
|
+
|
|
703
|
+
---
|
|
704
|
+
|
|
705
|
+
#### 3D Card Tilt on hover (cards feel physical)
|
|
706
|
+
|
|
707
|
+
Cards that tilt toward the cursor. One of the strongest single micro-interactions.
|
|
708
|
+
|
|
709
|
+
```css
|
|
710
|
+
/* CSS: enable GPU compositing and smooth reset */
|
|
711
|
+
.feature-card {
|
|
712
|
+
transform-style: preserve-3d;
|
|
713
|
+
transition: transform 0.5s cubic-bezier(0.16, 1, 0.3, 1),
|
|
714
|
+
box-shadow 0.3s ease;
|
|
715
|
+
will-change: transform;
|
|
716
|
+
}
|
|
717
|
+
```
|
|
718
|
+
|
|
719
|
+
```js
|
|
720
|
+
// JS: 3D tilt on mousemove, smooth reset on mouseleave
|
|
721
|
+
function initCardTilt(selector = '.feature-card') {
|
|
722
|
+
document.querySelectorAll(selector).forEach(card => {
|
|
723
|
+
card.addEventListener('mousemove', e => {
|
|
724
|
+
const r = card.getBoundingClientRect();
|
|
725
|
+
const x = (e.clientX - r.left) / r.width - 0.5; // -0.5 → +0.5
|
|
726
|
+
const y = (e.clientY - r.top) / r.height - 0.5;
|
|
727
|
+
card.style.transition = 'none';
|
|
728
|
+
card.style.transform =
|
|
729
|
+
`perspective(700px) rotateY(${x * 14}deg) rotateX(${-y * 14}deg) translateZ(10px)`;
|
|
730
|
+
});
|
|
731
|
+
card.addEventListener('mouseleave', () => {
|
|
732
|
+
card.style.transition = 'transform 0.6s cubic-bezier(0.16, 1, 0.3, 1)';
|
|
733
|
+
card.style.transform = '';
|
|
734
|
+
});
|
|
735
|
+
});
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
// Initialize on DOM ready
|
|
739
|
+
document.addEventListener('DOMContentLoaded', () => {
|
|
740
|
+
if (!window.matchMedia('(prefers-reduced-motion: reduce)').matches) {
|
|
741
|
+
initCardTilt('.feature-card');
|
|
742
|
+
initCardTilt('.tool-card');
|
|
743
|
+
}
|
|
744
|
+
});
|
|
745
|
+
```
|
|
746
|
+
|
|
747
|
+
**Important:** do NOT apply tilt on touch devices:
|
|
748
|
+
```js
|
|
749
|
+
if (window.matchMedia('(hover: none)').matches) return; // skip on touch
|
|
750
|
+
```
|
|
751
|
+
|
|
752
|
+
---
|
|
753
|
+
|
|
754
|
+
### 2b. Clean & Luminous (Light, Apple-like)
|
|
755
|
+
|
|
756
|
+
```css
|
|
757
|
+
:root {
|
|
758
|
+
--bg-base: hsl(0, 0%, 100%);
|
|
759
|
+
--bg-surface: hsl(220, 20%, 97%);
|
|
760
|
+
--bg-sunken: hsl(220, 15%, 94%);
|
|
761
|
+
|
|
762
|
+
--text-primary: hsl(220, 30%, 10%);
|
|
763
|
+
--text-secondary: hsl(220, 15%, 40%);
|
|
764
|
+
--text-muted: hsl(220, 10%, 65%);
|
|
765
|
+
|
|
766
|
+
--accent-primary: hsl(250, 90%, 58%);
|
|
767
|
+
--accent-light: hsla(250, 90%, 58%, 0.1);
|
|
768
|
+
--gradient-brand: linear-gradient(135deg, hsl(250,90%,58%), hsl(280,80%,65%));
|
|
769
|
+
|
|
770
|
+
--border-faint: hsl(220, 20%, 92%);
|
|
771
|
+
--border-subtle: hsl(220, 15%, 86%);
|
|
772
|
+
--shadow-sm: 0 1px 3px hsla(220,30%,10%,0.06), 0 4px 16px hsla(220,30%,10%,0.04);
|
|
773
|
+
--shadow-md: 0 4px 12px hsla(220,30%,10%,0.08), 0 16px 40px hsla(220,30%,10%,0.06);
|
|
774
|
+
--shadow-lg: 0 8px 24px hsla(220,30%,10%,0.1), 0 32px 64px hsla(220,30%,10%,0.08);
|
|
775
|
+
|
|
776
|
+
/* Same spacing/radius/motion tokens as Bold & Cinematic */
|
|
777
|
+
--font-display: 'Plus Jakarta Sans', sans-serif;
|
|
778
|
+
--font-body: 'Plus Jakarta Sans', sans-serif;
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
/* Header: white with subtle border */
|
|
782
|
+
.header {
|
|
783
|
+
background: hsla(0, 0%, 100%, 0.85);
|
|
784
|
+
backdrop-filter: blur(20px);
|
|
785
|
+
border-bottom: 1px solid var(--border-faint);
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
/* Cards with elegant hover */
|
|
789
|
+
.feature-card {
|
|
790
|
+
background: var(--bg-base);
|
|
791
|
+
border: 1px solid var(--border-faint);
|
|
792
|
+
border-radius: var(--radius-lg);
|
|
793
|
+
padding: var(--space-xl);
|
|
794
|
+
box-shadow: var(--shadow-sm);
|
|
795
|
+
transition: box-shadow var(--duration-base) ease, transform var(--duration-base) var(--ease-out);
|
|
796
|
+
}
|
|
797
|
+
.feature-card:hover {
|
|
798
|
+
box-shadow: var(--shadow-md);
|
|
799
|
+
transform: translateY(-4px);
|
|
800
|
+
}
|
|
801
|
+
|
|
802
|
+
/* Accent rule under section titles */
|
|
803
|
+
.section-title::after {
|
|
804
|
+
content: '';
|
|
805
|
+
display: block;
|
|
806
|
+
width: 48px; height: 3px;
|
|
807
|
+
border-radius: 2px;
|
|
808
|
+
background: var(--gradient-brand);
|
|
809
|
+
margin-top: 12px;
|
|
810
|
+
}
|
|
811
|
+
|
|
812
|
+
/* Soft section background alternation */
|
|
813
|
+
.how-it-works { background: var(--bg-surface); }
|
|
814
|
+
```
|
|
815
|
+
|
|
816
|
+
---
|
|
817
|
+
|
|
818
|
+
## 3. Performance Patterns (always apply)
|
|
819
|
+
|
|
820
|
+
```html
|
|
821
|
+
<!-- Critical: preconnect fonts before CSS -->
|
|
822
|
+
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
823
|
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
824
|
+
|
|
825
|
+
<!-- Hero image: eager + fetchpriority for LCP -->
|
|
826
|
+
<img src="hero.jpg" loading="eager" fetchpriority="high" alt="">
|
|
827
|
+
|
|
828
|
+
<!-- Below-fold images: lazy -->
|
|
829
|
+
<img src="feature.jpg" loading="lazy" alt="[descriptive]">
|
|
830
|
+
|
|
831
|
+
<!-- Non-critical CSS: preload then apply -->
|
|
832
|
+
<link rel="preload" href="styles.css" as="style" onload="this.onload=null;this.rel='stylesheet'">
|
|
833
|
+
<noscript><link rel="stylesheet" href="styles.css"></noscript>
|
|
834
|
+
|
|
835
|
+
<!-- Scripts: always async or defer -->
|
|
836
|
+
<script async src="analytics.js"></script>
|
|
837
|
+
<script defer src="interactions.js"></script>
|
|
838
|
+
```
|
|
839
|
+
|
|
840
|
+
**Critical CSS inline rule:** Only header + hero CSS in `<style>` tag. Everything else in external file.
|
|
841
|
+
|
|
842
|
+
---
|
|
843
|
+
|
|
844
|
+
## 4. Minimal JavaScript (interactions only)
|
|
845
|
+
|
|
846
|
+
```js
|
|
847
|
+
// Header: add .scrolled class after scroll
|
|
848
|
+
window.addEventListener('scroll', () => {
|
|
849
|
+
document.getElementById('header')
|
|
850
|
+
.classList.toggle('scrolled', window.scrollY > 40);
|
|
851
|
+
}, { passive: true });
|
|
852
|
+
|
|
853
|
+
// Mobile menu toggle
|
|
854
|
+
document.querySelector('.header__burger')?.addEventListener('click', function() {
|
|
855
|
+
const expanded = this.getAttribute('aria-expanded') === 'true';
|
|
856
|
+
this.setAttribute('aria-expanded', String(!expanded));
|
|
857
|
+
document.querySelector('.header__nav').classList.toggle('open', !expanded);
|
|
858
|
+
});
|
|
859
|
+
|
|
860
|
+
// Scroll reveal with IntersectionObserver
|
|
861
|
+
const observer = new IntersectionObserver((entries) => {
|
|
862
|
+
entries.forEach(entry => {
|
|
863
|
+
if (entry.isIntersecting) {
|
|
864
|
+
entry.target.classList.add('revealed');
|
|
865
|
+
observer.unobserve(entry.target);
|
|
866
|
+
}
|
|
867
|
+
});
|
|
868
|
+
}, { threshold: 0.15 });
|
|
869
|
+
document.querySelectorAll('.reveal').forEach(el => observer.observe(el));
|
|
870
|
+
|
|
871
|
+
// Counter animation (for stats section)
|
|
872
|
+
function animateCounter(el) {
|
|
873
|
+
const target = +el.dataset.target;
|
|
874
|
+
const duration = 1500;
|
|
875
|
+
const start = performance.now();
|
|
876
|
+
requestAnimationFrame(function tick(now) {
|
|
877
|
+
const progress = Math.min((now - start) / duration, 1);
|
|
878
|
+
const eased = 1 - Math.pow(1 - progress, 3); // ease-out cubic
|
|
879
|
+
el.textContent = Math.round(eased * target).toLocaleString();
|
|
880
|
+
if (progress < 1) requestAnimationFrame(tick);
|
|
881
|
+
});
|
|
882
|
+
}
|
|
883
|
+
```
|
|
884
|
+
|
|
885
|
+
---
|
|
886
|
+
|
|
887
|
+
## 5. CSS Naming Convention (BEM-lite)
|
|
888
|
+
|
|
889
|
+
```
|
|
890
|
+
component → .hero, .feature-card, .testimonial-card
|
|
891
|
+
component__element → .hero__title, .feature-card__icon
|
|
892
|
+
component--modifier → .btn--primary, .btn--lg, .card--glow-border
|
|
893
|
+
```
|
|
894
|
+
|
|
895
|
+
Rules:
|
|
896
|
+
- Class names are lowercase, hyphenated
|
|
897
|
+
- No deep nesting in CSS (max 2 levels: `.hero .hero__content`)
|
|
898
|
+
- No `id` for styling — only for anchor links and `aria-labelledby`
|
|
899
|
+
- Utility classes for single-purpose overrides: `.sr-only`, `.reveal`, `.gradient-text`
|
|
900
|
+
|
|
901
|
+
---
|
|
902
|
+
|
|
903
|
+
## 6. Responsive Strategy
|
|
904
|
+
|
|
905
|
+
**Do NOT just reflow columns on mobile. Also:**
|
|
906
|
+
- Hide secondary navigation links → show only logo + CTA + burger
|
|
907
|
+
- Reduce heading sizes with `clamp()` instead of media queries for font-size
|
|
908
|
+
- Stack `.hero__actions` buttons vertically below 480px
|
|
909
|
+
- Remove decorative orbs/blobs on mobile (performance + layout stability)
|
|
910
|
+
- Simplify card grids: 3 → 2 → 1 columns
|
|
911
|
+
- Show testimonials as carousel or stacked, not 3-column grid
|
|
912
|
+
|
|
913
|
+
```css
|
|
914
|
+
/* Prefer clamp() for fluid typography over breakpoint-heavy font-size */
|
|
915
|
+
.hero__title { font-size: clamp(2rem, 6vw, 4.5rem); }
|
|
916
|
+
.section-title { font-size: clamp(1.875rem, 4vw, 3rem); }
|
|
917
|
+
|
|
918
|
+
/* Hide decorative elements on mobile */
|
|
919
|
+
@media (max-width: 768px) {
|
|
920
|
+
.hero__decor { display: none; }
|
|
921
|
+
.header__nav { display: none; }
|
|
922
|
+
.header__nav.open { display: flex; flex-direction: column; }
|
|
923
|
+
.hero__actions { flex-direction: column; align-items: center; }
|
|
924
|
+
}
|
|
925
|
+
```
|
|
926
|
+
|
|
927
|
+
---
|
|
928
|
+
|
|
929
|
+
## 7. Accessibility Checklist (non-negotiable)
|
|
930
|
+
|
|
931
|
+
- [ ] All `<img>` have `alt` — decorative images use `alt=""` + `role="presentation"`
|
|
932
|
+
- [ ] All interactive elements reachable by `Tab` and operable by `Enter`/`Space`
|
|
933
|
+
- [ ] `:focus-visible` styles visible and clear (2px outline, offset, accent color)
|
|
934
|
+
- [ ] All `<button>` have `type="button"` or `type="submit"`
|
|
935
|
+
- [ ] Icon-only buttons have `aria-label`
|
|
936
|
+
- [ ] Sections have `aria-labelledby` pointing to their heading `id`
|
|
937
|
+
- [ ] Color contrast ≥ 4.5:1 for body text, ≥ 3:1 for large text and UI controls
|
|
938
|
+
- [ ] `prefers-reduced-motion: reduce` disables all animations
|
|
939
|
+
- [ ] Mobile menu uses `aria-expanded` toggling
|
|
940
|
+
- [ ] Footer `<nav>` has `aria-label="Footer navigation"` distinct from header nav
|
|
941
|
+
|
|
942
|
+
---
|
|
943
|
+
|
|
944
|
+
## 8. Curated Unsplash Images by Domain
|
|
945
|
+
|
|
946
|
+
Format: `https://images.unsplash.com/photo-{ID}?w=1920&q=80&fit=crop`
|
|
947
|
+
|
|
948
|
+
| Domain | Hero | Feature/Section |
|
|
949
|
+
|---|---|---|
|
|
950
|
+
| Tech/AI/SaaS | `1518770660439-4636190af475` | `1551288049-bebda4e38f71` |
|
|
951
|
+
| Business/Corp | `1497366216548-37526070297c` | `1522071820081-009f0129c71c` |
|
|
952
|
+
| Creative/Agency | `1558618666-fcd25c85cd64` | `1504607798333-52a30db54a5d` |
|
|
953
|
+
| Wellness/Health | `1506905925346-21bda4d32df4` | `1571019613454-1cb2f99b2d8b` |
|
|
954
|
+
| Food/Restaurant | `1414235077428-338989a2e8c0` | `1555939594-58d7cb561ad1` |
|
|
955
|
+
| Architecture | `1486325212027-8081e485255e` | `1460317442991-0ec209397118` |
|
|
956
|
+
| Nature/Travel | `1441974231531-c6227db76b6e` | `1506197603052-3cc9c3a201bd` |
|
|
957
|
+
| People/Teams | `1522202176988-66273c2fd55f` | `1582213782179-e0d53f98f2ca` |
|
|
958
|
+
| Avatars (face) | `1535713875002-d1d0cf377fde` | `1494790108377-be9c29b29330` |
|
|
959
|
+
|
|
960
|
+
Face crops (for testimonials): append `&w=48&h=48&fit=crop&crop=face`
|
|
961
|
+
|
|
962
|
+
---
|
|
963
|
+
|
|
964
|
+
## 9. GSAP Animations — Production Patterns (AI Agency / SaaS style)
|
|
965
|
+
|
|
966
|
+
Source: Aigocy / GSAP ScrollTrigger — gsap.com/docs/v3/Plugins/ScrollTrigger
|
|
967
|
+
|
|
968
|
+
### Setup
|
|
969
|
+
|
|
970
|
+
```html
|
|
971
|
+
<script src="https://cdn.jsdelivr.net/npm/gsap@3/dist/gsap.min.js" defer></script>
|
|
972
|
+
<script src="https://cdn.jsdelivr.net/npm/gsap@3/dist/ScrollTrigger.min.js" defer></script>
|
|
973
|
+
```
|
|
974
|
+
|
|
975
|
+
```js
|
|
976
|
+
// Register plugin once
|
|
977
|
+
gsap.registerPlugin(ScrollTrigger);
|
|
978
|
+
```
|
|
979
|
+
|
|
980
|
+
### Reusable animation helpers (add to main.js)
|
|
981
|
+
|
|
982
|
+
```js
|
|
983
|
+
// 1. Reveal elements on scroll (fade + translateY)
|
|
984
|
+
function revealOnScroll(selector, options = {}) {
|
|
985
|
+
const els = document.querySelectorAll(selector);
|
|
986
|
+
if (!els.length) return;
|
|
987
|
+
gsap.fromTo(els,
|
|
988
|
+
{ opacity: 0, y: options.y ?? 40 },
|
|
989
|
+
{
|
|
990
|
+
opacity: 1, y: 0,
|
|
991
|
+
duration: options.duration ?? 0.7,
|
|
992
|
+
ease: 'power3.out',
|
|
993
|
+
stagger: options.stagger ?? 0.1,
|
|
994
|
+
scrollTrigger: {
|
|
995
|
+
trigger: els[0].closest('section') ?? els[0],
|
|
996
|
+
start: 'top 80%',
|
|
997
|
+
once: true,
|
|
998
|
+
}
|
|
999
|
+
}
|
|
1000
|
+
);
|
|
1001
|
+
}
|
|
1002
|
+
|
|
1003
|
+
// 2. Hero intro sequence (run on load, not scroll)
|
|
1004
|
+
function heroIntroTimeline() {
|
|
1005
|
+
return gsap.timeline({ delay: 0.2 })
|
|
1006
|
+
.from('.hero__label', { opacity: 0, y: 20, duration: 0.5, ease: 'power2.out' })
|
|
1007
|
+
.from('.hero__title', { opacity: 0, y: 30, duration: 0.7, ease: 'power3.out' }, '-=0.2')
|
|
1008
|
+
.from('.hero__subtitle', { opacity: 0, y: 20, duration: 0.6, ease: 'power2.out' }, '-=0.4')
|
|
1009
|
+
.from('.hero__actions', { opacity: 0, y: 20, duration: 0.5, ease: 'power2.out' }, '-=0.3')
|
|
1010
|
+
.from('.hero__social-proof', { opacity: 0, duration: 0.4 }, '-=0.2');
|
|
1011
|
+
}
|
|
1012
|
+
|
|
1013
|
+
// 3. Card grid stagger (features, services, team)
|
|
1014
|
+
function cardsStaggerIn(sectionSelector) {
|
|
1015
|
+
const section = document.querySelector(sectionSelector);
|
|
1016
|
+
if (!section) return;
|
|
1017
|
+
gsap.fromTo(section.querySelectorAll('.card, .feature-card, .service-card'),
|
|
1018
|
+
{ opacity: 0, y: 50, scale: 0.97 },
|
|
1019
|
+
{
|
|
1020
|
+
opacity: 1, y: 0, scale: 1,
|
|
1021
|
+
duration: 0.6, ease: 'power3.out', stagger: 0.1,
|
|
1022
|
+
scrollTrigger: { trigger: section, start: 'top 75%', once: true }
|
|
1023
|
+
}
|
|
1024
|
+
);
|
|
1025
|
+
}
|
|
1026
|
+
|
|
1027
|
+
// 4. Light parallax on hero background
|
|
1028
|
+
function parallaxLight(selector) {
|
|
1029
|
+
gsap.to(selector, {
|
|
1030
|
+
yPercent: 30,
|
|
1031
|
+
ease: 'none',
|
|
1032
|
+
scrollTrigger: { trigger: selector, start: 'top top', end: 'bottom top', scrub: true }
|
|
1033
|
+
});
|
|
1034
|
+
}
|
|
1035
|
+
|
|
1036
|
+
// 5. Counter animation (stats section)
|
|
1037
|
+
function animateCounters() {
|
|
1038
|
+
document.querySelectorAll('[data-counter]').forEach(el => {
|
|
1039
|
+
const target = +el.dataset.counter;
|
|
1040
|
+
ScrollTrigger.create({
|
|
1041
|
+
trigger: el, start: 'top 85%', once: true,
|
|
1042
|
+
onEnter: () => {
|
|
1043
|
+
gsap.fromTo(el,
|
|
1044
|
+
{ textContent: 0 },
|
|
1045
|
+
{
|
|
1046
|
+
textContent: target, duration: 1.5, ease: 'power2.out',
|
|
1047
|
+
snap: { textContent: 1 },
|
|
1048
|
+
onUpdate() { el.textContent = Math.round(+el.textContent).toLocaleString(); }
|
|
1049
|
+
}
|
|
1050
|
+
);
|
|
1051
|
+
}
|
|
1052
|
+
});
|
|
1053
|
+
});
|
|
1054
|
+
}
|
|
1055
|
+
|
|
1056
|
+
// 6. Animated gradient border on hover (section highlight)
|
|
1057
|
+
function initGlowCards() {
|
|
1058
|
+
document.querySelectorAll('.card--glow-border').forEach(card => {
|
|
1059
|
+
card.addEventListener('mousemove', (e) => {
|
|
1060
|
+
const rect = card.getBoundingClientRect();
|
|
1061
|
+
const x = ((e.clientX - rect.left) / rect.width * 100).toFixed(1);
|
|
1062
|
+
const y = ((e.clientY - rect.top) / rect.height * 100).toFixed(1);
|
|
1063
|
+
card.style.setProperty('--mouse-x', `${x}%`);
|
|
1064
|
+
card.style.setProperty('--mouse-y', `${y}%`);
|
|
1065
|
+
});
|
|
1066
|
+
});
|
|
1067
|
+
}
|
|
1068
|
+
|
|
1069
|
+
// Initialize on DOM ready
|
|
1070
|
+
document.addEventListener('DOMContentLoaded', () => {
|
|
1071
|
+
heroIntroTimeline();
|
|
1072
|
+
revealOnScroll('.section-header', { y: 30 });
|
|
1073
|
+
revealOnScroll('.step', { stagger: 0.15 });
|
|
1074
|
+
revealOnScroll('.testimonial-card', { stagger: 0.1 });
|
|
1075
|
+
revealOnScroll('.cta-final__title', { y: 20 });
|
|
1076
|
+
cardsStaggerIn('.features');
|
|
1077
|
+
cardsStaggerIn('.services');
|
|
1078
|
+
parallaxLight('.hero__bg img');
|
|
1079
|
+
animateCounters();
|
|
1080
|
+
initGlowCards();
|
|
1081
|
+
});
|
|
1082
|
+
```
|
|
1083
|
+
|
|
1084
|
+
### Anti-patterns to avoid
|
|
1085
|
+
- Do NOT put all scroll animations in one giant master timeline
|
|
1086
|
+
- Do NOT animate `width`, `height`, or `padding` — only `transform` and `opacity`
|
|
1087
|
+
- Do NOT run heavy GSAP work in `scroll` event — always use ScrollTrigger
|
|
1088
|
+
- Always `once: true` for entrance animations (don't re-trigger on scroll up)
|
|
1089
|
+
- Disable GSAP on `prefers-reduced-motion`:
|
|
1090
|
+
|
|
1091
|
+
```js
|
|
1092
|
+
if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) {
|
|
1093
|
+
gsap.globalTimeline.timeScale(0); // or skip init entirely
|
|
1094
|
+
}
|
|
1095
|
+
```
|
|
1096
|
+
|
|
1097
|
+
---
|
|
1098
|
+
|
|
1099
|
+
## 10. Swiper Sliders — Production Patterns
|
|
1100
|
+
|
|
1101
|
+
Source: swiperjs.com/swiper-api
|
|
1102
|
+
|
|
1103
|
+
### CDN
|
|
1104
|
+
|
|
1105
|
+
```html
|
|
1106
|
+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/swiper@11/swiper-bundle.min.css">
|
|
1107
|
+
<script src="https://cdn.jsdelivr.net/npm/swiper@11/swiper-bundle.min.js" defer></script>
|
|
1108
|
+
```
|
|
1109
|
+
|
|
1110
|
+
### HTML Structure
|
|
1111
|
+
|
|
1112
|
+
```html
|
|
1113
|
+
<div class="swiper testimonials-swiper" data-swiper-options='{"slidesPerView":1,"spaceBetween":24}'>
|
|
1114
|
+
<div class="swiper-wrapper">
|
|
1115
|
+
<div class="swiper-slide"><!-- content --></div>
|
|
1116
|
+
<div class="swiper-slide"><!-- content --></div>
|
|
1117
|
+
</div>
|
|
1118
|
+
<div class="swiper-pagination" aria-hidden="true"></div>
|
|
1119
|
+
<button class="swiper-button-prev" aria-label="Previous slide"></button>
|
|
1120
|
+
<button class="swiper-button-next" aria-label="Next slide"></button>
|
|
1121
|
+
</div>
|
|
1122
|
+
```
|
|
1123
|
+
|
|
1124
|
+
### Universal Init (handles multiple sliders via data-attribute)
|
|
1125
|
+
|
|
1126
|
+
```js
|
|
1127
|
+
document.querySelectorAll('.swiper').forEach(el => {
|
|
1128
|
+
const options = el.dataset.swiperOptions ? JSON.parse(el.dataset.swiperOptions) : {};
|
|
1129
|
+
new Swiper(el, {
|
|
1130
|
+
loop: true,
|
|
1131
|
+
navigation: { nextEl: el.querySelector('.swiper-button-next'), prevEl: el.querySelector('.swiper-button-prev') },
|
|
1132
|
+
pagination: { el: el.querySelector('.swiper-pagination'), clickable: true },
|
|
1133
|
+
a11y: { prevSlideMessage: 'Previous slide', nextSlideMessage: 'Next slide' },
|
|
1134
|
+
breakpoints: {
|
|
1135
|
+
768: { slidesPerView: 2, spaceBetween: 24 },
|
|
1136
|
+
1024: { slidesPerView: 3, spaceBetween: 32 },
|
|
1137
|
+
},
|
|
1138
|
+
...options, // data-attribute options override defaults
|
|
1139
|
+
});
|
|
1140
|
+
});
|
|
1141
|
+
```
|
|
1142
|
+
|
|
1143
|
+
### CSS: fix slider CLS (layout shift)
|
|
1144
|
+
|
|
1145
|
+
```css
|
|
1146
|
+
.swiper { overflow: hidden; }
|
|
1147
|
+
.swiper-wrapper { display: flex; align-items: stretch; }
|
|
1148
|
+
.swiper-slide { height: auto; } /* equal height cards */
|
|
1149
|
+
```
|
|
1150
|
+
|
|
1151
|
+
---
|
|
1152
|
+
|
|
1153
|
+
## 11. SCSS Architecture (for larger projects)
|
|
1154
|
+
|
|
1155
|
+
```
|
|
1156
|
+
assets/scss/
|
|
1157
|
+
base/
|
|
1158
|
+
_reset.scss ← minimal reset + box-model
|
|
1159
|
+
_typography.scss ← font imports, type scale, body
|
|
1160
|
+
_helpers.scss ← .sr-only, .reveal, .gradient-text, .container
|
|
1161
|
+
tokens/
|
|
1162
|
+
_colors.scss ← CSS custom properties :root block
|
|
1163
|
+
_spacing.scss ← --space-* tokens
|
|
1164
|
+
_typography.scss ← --font-*, --text-* tokens
|
|
1165
|
+
_motion.scss ← --ease-*, --duration-* tokens
|
|
1166
|
+
layout/
|
|
1167
|
+
_header.scss
|
|
1168
|
+
_footer.scss
|
|
1169
|
+
_grid.scss
|
|
1170
|
+
components/
|
|
1171
|
+
_buttons.scss
|
|
1172
|
+
_cards.scss
|
|
1173
|
+
_forms.scss
|
|
1174
|
+
_modal.scss
|
|
1175
|
+
_accordion.scss
|
|
1176
|
+
_slider.scss
|
|
1177
|
+
sections/
|
|
1178
|
+
_hero.scss
|
|
1179
|
+
_features.scss
|
|
1180
|
+
_how-it-works.scss
|
|
1181
|
+
_testimonials.scss
|
|
1182
|
+
_pricing.scss
|
|
1183
|
+
_faq.scss
|
|
1184
|
+
_cta-final.scss
|
|
1185
|
+
main.scss ← @forward all partials in order
|
|
1186
|
+
```
|
|
1187
|
+
|
|
1188
|
+
**Compile:** `sass assets/scss/main.scss assets/css/main.min.css --style=compressed --watch`
|
|
1189
|
+
|
|
1190
|
+
---
|
|
1191
|
+
|
|
1192
|
+
## 12. Full Section Checklist (AI / SaaS Landing Page)
|
|
1193
|
+
|
|
1194
|
+
For complete AI agency or SaaS landing pages, include these sections in order:
|
|
1195
|
+
|
|
1196
|
+
| # | Section | Purpose |
|
|
1197
|
+
|---|---|---|
|
|
1198
|
+
| 1 | Header (sticky) | Logo + nav + CTA button |
|
|
1199
|
+
| 2 | Hero | Headline + sub + 2 CTAs + social proof |
|
|
1200
|
+
| 3 | Logos bar | "Trusted by" brand names |
|
|
1201
|
+
| 4 | Features grid | 3–6 cards with icon + title + description |
|
|
1202
|
+
| 5 | How it works | 3 numbered steps with image alternation |
|
|
1203
|
+
| 6 | Services | Cards with deeper service descriptions |
|
|
1204
|
+
| 7 | Stats / Numbers | Animated counters (clients, projects, uptime) |
|
|
1205
|
+
| 8 | Case studies | Portfolio cards with hover image reveal |
|
|
1206
|
+
| 9 | Testimonials | Swiper slider with quotes + avatars |
|
|
1207
|
+
| 10 | Pricing | 3-tier cards with "Most popular" badge |
|
|
1208
|
+
| 11 | FAQ | Accordion with open/close animation |
|
|
1209
|
+
| 12 | Final CTA | Single button, urgency, no distractions |
|
|
1210
|
+
| 13 | Footer | Dense: links + social + newsletter + copyright |
|
|
1211
|
+
|
|
1212
|
+
**For MICRO landing pages (single page, simple product):** sections 1, 2, 4, 9, 12, 13.
|
|
1213
|
+
|
|
1214
|
+
---
|
|
1215
|
+
|
|
1216
|
+
## 13. Pre-delivery Checklist
|
|
1217
|
+
|
|
1218
|
+
- [ ] Mobile menu opens/closes, body scroll locked when open
|
|
1219
|
+
- [ ] Sliders have correct breakpoints and accessible buttons
|
|
1220
|
+
- [ ] GSAP scroll animations work on mobile (use `start: 'top 85%'` for shorter viewports)
|
|
1221
|
+
- [ ] Hero image loads eagerly with `fetchpriority="high"`, all others `loading="lazy"`
|
|
1222
|
+
- [ ] No layout shift (CLS): img/video elements have explicit width+height
|
|
1223
|
+
- [ ] `prefers-reduced-motion: reduce` disables all animations
|
|
1224
|
+
- [ ] All interactive elements reachable by keyboard
|
|
1225
|
+
- [ ] Color contrast ≥ 4.5:1 checked
|
|
1226
|
+
- [ ] `<title>` and `<meta description>` are real content (not placeholders)
|
|
1227
|
+
- [ ] Open Graph meta tags present (`og:title`, `og:description`, `og:image`)
|
|
1228
|
+
- [ ] No placeholder text remains anywhere in the HTML
|
|
1229
|
+
|
|
1230
|
+
---
|
|
1231
|
+
|
|
1232
|
+
## 14. Premium Template Patterns (Aigocy-style)
|
|
1233
|
+
|
|
1234
|
+
Real patterns extracted from a production AI agency template (ThemeForest #61450410).
|
|
1235
|
+
Use these to elevate landing pages from "nice" to "award-worthy".
|
|
1236
|
+
|
|
1237
|
+
---
|
|
1238
|
+
|
|
1239
|
+
### 14a. effectFade Animations (signature 3D entrance)
|
|
1240
|
+
|
|
1241
|
+
Two animation modes used by the Aigocy template — add these classes to any element
|
|
1242
|
+
and trigger via GSAP ScrollTrigger or IntersectionObserver.
|
|
1243
|
+
|
|
1244
|
+
**HTML markup:**
|
|
1245
|
+
```html
|
|
1246
|
+
<!-- Simple fade up — most common -->
|
|
1247
|
+
<h2 class="effectFade fadeUp" data-delay="0">Headline</h2>
|
|
1248
|
+
<p class="effectFade fadeUp" data-delay="0.15">Supporting text</p>
|
|
1249
|
+
|
|
1250
|
+
<!-- 3D perspective rotate — signature "cinematic" entrance -->
|
|
1251
|
+
<div class="effectFade fadeRotateX" data-delay="0.1">Card content</div>
|
|
1252
|
+
```
|
|
1253
|
+
|
|
1254
|
+
**CSS:**
|
|
1255
|
+
```css
|
|
1256
|
+
/* Base state (invisible, ready to animate in) */
|
|
1257
|
+
.effectFade { opacity: 0; }
|
|
1258
|
+
.effectFade.fadeUp { transform: translateY(50px); }
|
|
1259
|
+
.effectFade.fadeRotateX {
|
|
1260
|
+
transform: perspective(800px) rotateX(25deg) translateY(40px);
|
|
1261
|
+
transform-origin: 50% 0%;
|
|
1262
|
+
}
|
|
1263
|
+
|
|
1264
|
+
/* Animated state (JS adds .animated class) */
|
|
1265
|
+
.effectFade.animated {
|
|
1266
|
+
opacity: 1;
|
|
1267
|
+
transform: none;
|
|
1268
|
+
transition: opacity 0.7s ease, transform 0.8s cubic-bezier(0.16, 1, 0.3, 1);
|
|
1269
|
+
}
|
|
1270
|
+
```
|
|
1271
|
+
|
|
1272
|
+
**JS (GSAP + data-delay stagger):**
|
|
1273
|
+
```js
|
|
1274
|
+
// Use data-delay for precise per-element stagger control
|
|
1275
|
+
gsap.utils.toArray('.effectFade').forEach(el => {
|
|
1276
|
+
const delay = parseFloat(el.dataset.delay ?? '0');
|
|
1277
|
+
gsap.fromTo(el,
|
|
1278
|
+
{ opacity: 0, y: el.classList.contains('fadeUp') ? 50 : 0,
|
|
1279
|
+
rotateX: el.classList.contains('fadeRotateX') ? 25 : 0,
|
|
1280
|
+
transformPerspective: 800, transformOrigin: '50% 0%' },
|
|
1281
|
+
{
|
|
1282
|
+
opacity: 1, y: 0, rotateX: 0,
|
|
1283
|
+
duration: 0.8, delay, ease: 'power3.out',
|
|
1284
|
+
scrollTrigger: { trigger: el, start: 'top 85%', once: true }
|
|
1285
|
+
}
|
|
1286
|
+
);
|
|
1287
|
+
});
|
|
1288
|
+
```
|
|
1289
|
+
|
|
1290
|
+
---
|
|
1291
|
+
|
|
1292
|
+
### 14b. Infinite Logo Marquee (CSS only, no library)
|
|
1293
|
+
|
|
1294
|
+
Infinitely scrolling partner/client logo bar. Works with just CSS — no JS needed.
|
|
1295
|
+
Duplicating the list items (`data-clone` attribute in Aigocy) prevents gaps.
|
|
1296
|
+
|
|
1297
|
+
**HTML:**
|
|
1298
|
+
```html
|
|
1299
|
+
<section class="logos-marquee" aria-label="Trusted by">
|
|
1300
|
+
<div class="marquee-track">
|
|
1301
|
+
<!-- Original items -->
|
|
1302
|
+
<ul class="marquee-list" aria-hidden="false">
|
|
1303
|
+
<li><img src="logo-1.svg" alt="Company A" height="32"></li>
|
|
1304
|
+
<li><img src="logo-2.svg" alt="Company B" height="32"></li>
|
|
1305
|
+
<li><img src="logo-3.svg" alt="Company C" height="32"></li>
|
|
1306
|
+
<li><img src="logo-4.svg" alt="Company D" height="32"></li>
|
|
1307
|
+
<li><img src="logo-5.svg" alt="Company E" height="32"></li>
|
|
1308
|
+
</ul>
|
|
1309
|
+
<!-- Cloned items for seamless loop (aria-hidden) -->
|
|
1310
|
+
<ul class="marquee-list" aria-hidden="true">
|
|
1311
|
+
<!-- same items, duplicated -->
|
|
1312
|
+
</ul>
|
|
1313
|
+
</div>
|
|
1314
|
+
</section>
|
|
1315
|
+
```
|
|
1316
|
+
|
|
1317
|
+
**CSS:**
|
|
1318
|
+
```css
|
|
1319
|
+
.logos-marquee { overflow: hidden; padding: var(--space-xl) 0; }
|
|
1320
|
+
|
|
1321
|
+
.marquee-track {
|
|
1322
|
+
display: flex;
|
|
1323
|
+
width: max-content;
|
|
1324
|
+
animation: infiniteSlide 24s linear infinite;
|
|
1325
|
+
}
|
|
1326
|
+
|
|
1327
|
+
.marquee-list {
|
|
1328
|
+
display: flex; align-items: center; gap: 64px;
|
|
1329
|
+
list-style: none; padding: 0 32px;
|
|
1330
|
+
}
|
|
1331
|
+
|
|
1332
|
+
.marquee-list img {
|
|
1333
|
+
height: 32px; width: auto;
|
|
1334
|
+
filter: grayscale(1) opacity(0.5);
|
|
1335
|
+
transition: filter 0.3s ease;
|
|
1336
|
+
}
|
|
1337
|
+
.marquee-list img:hover {
|
|
1338
|
+
filter: grayscale(0) opacity(1);
|
|
1339
|
+
}
|
|
1340
|
+
|
|
1341
|
+
@keyframes infiniteSlide {
|
|
1342
|
+
from { transform: translateX(0); }
|
|
1343
|
+
to { transform: translateX(-50%); } /* -50% because track = 2× list width */
|
|
1344
|
+
}
|
|
1345
|
+
|
|
1346
|
+
/* Pause on hover for accessibility */
|
|
1347
|
+
.logos-marquee:hover .marquee-track { animation-play-state: paused; }
|
|
1348
|
+
|
|
1349
|
+
@media (prefers-reduced-motion: reduce) {
|
|
1350
|
+
.marquee-track { animation: none; }
|
|
1351
|
+
}
|
|
1352
|
+
```
|
|
1353
|
+
|
|
1354
|
+
**JS (optional: auto-clone):**
|
|
1355
|
+
```js
|
|
1356
|
+
// Auto-clone the list so you only maintain one set of logos in HTML
|
|
1357
|
+
document.querySelectorAll('.marquee-track').forEach(track => {
|
|
1358
|
+
const list = track.querySelector('.marquee-list');
|
|
1359
|
+
const clone = list.cloneNode(true);
|
|
1360
|
+
clone.setAttribute('aria-hidden', 'true');
|
|
1361
|
+
track.appendChild(clone);
|
|
1362
|
+
});
|
|
1363
|
+
```
|
|
1364
|
+
|
|
1365
|
+
---
|
|
1366
|
+
|
|
1367
|
+
### 14c. SVG Animated Paths (hub-and-spoke diagram)
|
|
1368
|
+
|
|
1369
|
+
Dots traveling along SVG paths connecting a center image to surrounding icons.
|
|
1370
|
+
Pure SMIL animation — no JS or GSAP needed. Great for "how it works" or "integrations" sections.
|
|
1371
|
+
|
|
1372
|
+
**HTML:**
|
|
1373
|
+
```html
|
|
1374
|
+
<div class="tools-hub" aria-hidden="true">
|
|
1375
|
+
<!-- Center image -->
|
|
1376
|
+
<div class="hub-center">
|
|
1377
|
+
<img src="product-logo.svg" alt="">
|
|
1378
|
+
</div>
|
|
1379
|
+
|
|
1380
|
+
<!-- Surrounding tool icons (positioned absolutely) -->
|
|
1381
|
+
<div class="tool-item tool-item--1"><img src="icon-1.svg" alt=""></div>
|
|
1382
|
+
<div class="tool-item tool-item--2"><img src="icon-2.svg" alt=""></div>
|
|
1383
|
+
<div class="tool-item tool-item--3"><img src="icon-3.svg" alt=""></div>
|
|
1384
|
+
|
|
1385
|
+
<!-- SVG paths with animated dots -->
|
|
1386
|
+
<svg class="hub-svg" viewBox="0 0 600 600" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
1387
|
+
<!-- Path from center to tool-1 -->
|
|
1388
|
+
<path id="path-1" d="M300 300 C 240 240, 160 200, 100 160" stroke="hsla(265,80%,65%,0.3)" stroke-width="1" fill="none"/>
|
|
1389
|
+
<circle r="4" fill="hsl(265,80%,65%)">
|
|
1390
|
+
<animateMotion dur="3s" repeatCount="indefinite" begin="0s">
|
|
1391
|
+
<mpath href="#path-1"/>
|
|
1392
|
+
</animateMotion>
|
|
1393
|
+
</circle>
|
|
1394
|
+
|
|
1395
|
+
<!-- Path from center to tool-2 -->
|
|
1396
|
+
<path id="path-2" d="M300 300 C 360 240, 440 200, 500 160" stroke="hsla(265,80%,65%,0.3)" stroke-width="1" fill="none"/>
|
|
1397
|
+
<circle r="4" fill="hsl(310,75%,60%)">
|
|
1398
|
+
<animateMotion dur="4s" repeatCount="indefinite" begin="1s">
|
|
1399
|
+
<mpath href="#path-2"/>
|
|
1400
|
+
</animateMotion>
|
|
1401
|
+
</circle>
|
|
1402
|
+
</svg>
|
|
1403
|
+
</div>
|
|
1404
|
+
```
|
|
1405
|
+
|
|
1406
|
+
**CSS:**
|
|
1407
|
+
```css
|
|
1408
|
+
.tools-hub {
|
|
1409
|
+
position: relative; width: 600px; height: 600px;
|
|
1410
|
+
margin: 0 auto;
|
|
1411
|
+
}
|
|
1412
|
+
.hub-center {
|
|
1413
|
+
position: absolute; top: 50%; left: 50%;
|
|
1414
|
+
transform: translate(-50%, -50%);
|
|
1415
|
+
width: 120px; height: 120px;
|
|
1416
|
+
border-radius: 50%;
|
|
1417
|
+
background: var(--bg-surface);
|
|
1418
|
+
border: 1px solid var(--border-subtle);
|
|
1419
|
+
display: flex; align-items: center; justify-content: center;
|
|
1420
|
+
}
|
|
1421
|
+
.tool-item {
|
|
1422
|
+
position: absolute;
|
|
1423
|
+
width: 64px; height: 64px;
|
|
1424
|
+
border-radius: var(--radius-md);
|
|
1425
|
+
background: var(--bg-elevated);
|
|
1426
|
+
border: 1px solid var(--border-faint);
|
|
1427
|
+
display: flex; align-items: center; justify-content: center;
|
|
1428
|
+
}
|
|
1429
|
+
/* Position items around the hub */
|
|
1430
|
+
.tool-item--1 { top: 8%; left: 8%; }
|
|
1431
|
+
.tool-item--2 { top: 8%; right: 8%; }
|
|
1432
|
+
.tool-item--3 { bottom: 8%; left: 50%; transform: translateX(-50%); }
|
|
1433
|
+
|
|
1434
|
+
.hub-svg {
|
|
1435
|
+
position: absolute; inset: 0;
|
|
1436
|
+
width: 100%; height: 100%;
|
|
1437
|
+
pointer-events: none;
|
|
1438
|
+
}
|
|
1439
|
+
|
|
1440
|
+
@media (max-width: 768px) { .tools-hub { display: none; } /* hide on mobile */ }
|
|
1441
|
+
```
|
|
1442
|
+
|
|
1443
|
+
---
|
|
1444
|
+
|
|
1445
|
+
### 14d. Scroll-to-top with Circular Progress
|
|
1446
|
+
|
|
1447
|
+
A back-to-top button that shows reading progress as a circular arc.
|
|
1448
|
+
Uses a CSS custom property `--progress-angle` updated by JS.
|
|
1449
|
+
|
|
1450
|
+
**HTML:**
|
|
1451
|
+
```html
|
|
1452
|
+
<button class="scroll-top" id="goTop" aria-label="Back to top" title="Back to top">
|
|
1453
|
+
<svg class="scroll-top__ring" viewBox="0 0 48 48" aria-hidden="true">
|
|
1454
|
+
<circle class="scroll-top__track" cx="24" cy="24" r="20" fill="none" stroke-width="2"/>
|
|
1455
|
+
<circle class="scroll-top__progress" cx="24" cy="24" r="20" fill="none" stroke-width="2"
|
|
1456
|
+
style="stroke-dasharray: 125.66; stroke-dashoffset: var(--dash-offset, 125.66)"/>
|
|
1457
|
+
</svg>
|
|
1458
|
+
<span class="scroll-top__icon" aria-hidden="true">↑</span>
|
|
1459
|
+
</button>
|
|
1460
|
+
```
|
|
1461
|
+
|
|
1462
|
+
**CSS:**
|
|
1463
|
+
```css
|
|
1464
|
+
.scroll-top {
|
|
1465
|
+
position: fixed; bottom: 32px; right: 32px; z-index: 50;
|
|
1466
|
+
width: 48px; height: 48px;
|
|
1467
|
+
border-radius: 50%;
|
|
1468
|
+
background: var(--bg-surface);
|
|
1469
|
+
border: 1px solid var(--border-subtle);
|
|
1470
|
+
display: flex; align-items: center; justify-content: center;
|
|
1471
|
+
opacity: 0; pointer-events: none;
|
|
1472
|
+
transition: opacity 0.3s ease, transform 0.2s ease;
|
|
1473
|
+
}
|
|
1474
|
+
.scroll-top.visible { opacity: 1; pointer-events: auto; }
|
|
1475
|
+
.scroll-top:hover { transform: translateY(-2px); }
|
|
1476
|
+
|
|
1477
|
+
.scroll-top__ring {
|
|
1478
|
+
position: absolute; inset: 0;
|
|
1479
|
+
transform: rotate(-90deg); /* start progress from top */
|
|
1480
|
+
}
|
|
1481
|
+
.scroll-top__track { stroke: var(--border-faint); }
|
|
1482
|
+
.scroll-top__progress {
|
|
1483
|
+
stroke: var(--accent-primary);
|
|
1484
|
+
transition: stroke-dashoffset 0.1s linear;
|
|
1485
|
+
}
|
|
1486
|
+
.scroll-top__icon {
|
|
1487
|
+
font-size: 18px; font-weight: 700;
|
|
1488
|
+
color: var(--text-primary); line-height: 1;
|
|
1489
|
+
}
|
|
1490
|
+
```
|
|
1491
|
+
|
|
1492
|
+
**JS:**
|
|
1493
|
+
```js
|
|
1494
|
+
const goTop = document.getElementById('goTop');
|
|
1495
|
+
const progressCircle = goTop?.querySelector('.scroll-top__progress');
|
|
1496
|
+
const circumference = 125.66; // 2π × r (r=20)
|
|
1497
|
+
|
|
1498
|
+
window.addEventListener('scroll', () => {
|
|
1499
|
+
const scrolled = window.scrollY;
|
|
1500
|
+
const total = document.body.scrollHeight - window.innerHeight;
|
|
1501
|
+
const progress = scrolled / total;
|
|
1502
|
+
const offset = circumference * (1 - progress);
|
|
1503
|
+
|
|
1504
|
+
goTop?.classList.toggle('visible', scrolled > 300);
|
|
1505
|
+
progressCircle?.style.setProperty('--dash-offset', offset);
|
|
1506
|
+
}, { passive: true });
|
|
1507
|
+
|
|
1508
|
+
goTop?.addEventListener('click', () =>
|
|
1509
|
+
window.scrollTo({ top: 0, behavior: 'smooth' })
|
|
1510
|
+
);
|
|
1511
|
+
```
|
|
1512
|
+
|
|
1513
|
+
---
|
|
1514
|
+
|
|
1515
|
+
### 14e. Split Swiper (synchronized text + image sliders)
|
|
1516
|
+
|
|
1517
|
+
Two separate Swiper instances that scroll together — one shows testimonial text,
|
|
1518
|
+
the other shows the matching portrait. Classic premium agency pattern.
|
|
1519
|
+
|
|
1520
|
+
**HTML:**
|
|
1521
|
+
```html
|
|
1522
|
+
<section class="split-testimonials">
|
|
1523
|
+
<div class="container split-testimonials__grid">
|
|
1524
|
+
<!-- Left: text slides -->
|
|
1525
|
+
<div class="swiper testimonials-text-swiper">
|
|
1526
|
+
<div class="swiper-wrapper">
|
|
1527
|
+
<div class="swiper-slide">
|
|
1528
|
+
<blockquote class="split-quote">
|
|
1529
|
+
<p>"This is the most impactful tool we've adopted this year. Our team velocity doubled."</p>
|
|
1530
|
+
<footer>
|
|
1531
|
+
<cite class="split-quote__name">Maria Silva</cite>
|
|
1532
|
+
<span class="split-quote__role">CTO, Acme Corp</span>
|
|
1533
|
+
</footer>
|
|
1534
|
+
</blockquote>
|
|
1535
|
+
</div>
|
|
1536
|
+
<!-- more slides -->
|
|
1537
|
+
</div>
|
|
1538
|
+
<div class="split-testimonials__controls">
|
|
1539
|
+
<button class="swiper-button-prev" aria-label="Previous testimonial"></button>
|
|
1540
|
+
<div class="swiper-pagination"></div>
|
|
1541
|
+
<button class="swiper-button-next" aria-label="Next testimonial"></button>
|
|
1542
|
+
</div>
|
|
1543
|
+
</div>
|
|
1544
|
+
|
|
1545
|
+
<!-- Right: image slides (synchronized) -->
|
|
1546
|
+
<div class="swiper testimonials-image-swiper">
|
|
1547
|
+
<div class="swiper-wrapper">
|
|
1548
|
+
<div class="swiper-slide">
|
|
1549
|
+
<img src="https://images.unsplash.com/photo-1494790108377-be9c29b29330?w=480&h=560&fit=crop&crop=face"
|
|
1550
|
+
alt="Maria Silva" loading="lazy">
|
|
1551
|
+
</div>
|
|
1552
|
+
<!-- more slides -->
|
|
1553
|
+
</div>
|
|
1554
|
+
</div>
|
|
1555
|
+
</div>
|
|
1556
|
+
</section>
|
|
1557
|
+
```
|
|
1558
|
+
|
|
1559
|
+
**CSS:**
|
|
1560
|
+
```css
|
|
1561
|
+
.split-testimonials__grid {
|
|
1562
|
+
display: grid; grid-template-columns: 1fr 1fr; gap: var(--space-3xl);
|
|
1563
|
+
align-items: center;
|
|
1564
|
+
}
|
|
1565
|
+
.split-quote p {
|
|
1566
|
+
font-size: var(--text-xl); line-height: 1.7;
|
|
1567
|
+
color: var(--text-secondary); font-style: italic;
|
|
1568
|
+
margin-bottom: var(--space-xl);
|
|
1569
|
+
}
|
|
1570
|
+
.split-quote::before { content: '"'; font-size: 80px; color: var(--accent-primary); line-height: 0.6; display: block; }
|
|
1571
|
+
.split-quote__name { display: block; font-weight: 700; color: var(--text-primary); }
|
|
1572
|
+
.split-quote__role { font-size: var(--text-sm); color: var(--text-muted); }
|
|
1573
|
+
|
|
1574
|
+
.testimonials-image-swiper img {
|
|
1575
|
+
border-radius: var(--radius-lg); width: 100%; height: 480px; object-fit: cover;
|
|
1576
|
+
}
|
|
1577
|
+
|
|
1578
|
+
.split-testimonials__controls {
|
|
1579
|
+
display: flex; align-items: center; gap: var(--space-md); margin-top: var(--space-xl);
|
|
1580
|
+
}
|
|
1581
|
+
|
|
1582
|
+
@media (max-width: 768px) {
|
|
1583
|
+
.split-testimonials__grid { grid-template-columns: 1fr; }
|
|
1584
|
+
.testimonials-image-swiper { display: none; }
|
|
1585
|
+
}
|
|
1586
|
+
```
|
|
1587
|
+
|
|
1588
|
+
**JS:**
|
|
1589
|
+
```js
|
|
1590
|
+
const textSwiper = new Swiper('.testimonials-text-swiper', {
|
|
1591
|
+
loop: true,
|
|
1592
|
+
navigation: {
|
|
1593
|
+
nextEl: '.testimonials-text-swiper .swiper-button-next',
|
|
1594
|
+
prevEl: '.testimonials-text-swiper .swiper-button-prev',
|
|
1595
|
+
},
|
|
1596
|
+
pagination: { el: '.testimonials-text-swiper .swiper-pagination', clickable: true },
|
|
1597
|
+
});
|
|
1598
|
+
|
|
1599
|
+
const imageSwiper = new Swiper('.testimonials-image-swiper', {
|
|
1600
|
+
loop: true,
|
|
1601
|
+
allowTouchMove: false, // image slider is controlled only by text slider
|
|
1602
|
+
effect: 'fade', fadeEffect: { crossFade: true },
|
|
1603
|
+
});
|
|
1604
|
+
|
|
1605
|
+
// Synchronize: text controls image
|
|
1606
|
+
textSwiper.on('slideChange', () => {
|
|
1607
|
+
imageSwiper.slideTo(textSwiper.realIndex);
|
|
1608
|
+
});
|
|
1609
|
+
```
|
|
1610
|
+
|
|
1611
|
+
---
|
|
1612
|
+
|
|
1613
|
+
### 14f. Swiper with Progress Bar Navigation
|
|
1614
|
+
|
|
1615
|
+
Instead of dots, show a thin animated progress bar for long testimonial or case study sliders.
|
|
1616
|
+
|
|
1617
|
+
**HTML:**
|
|
1618
|
+
```html
|
|
1619
|
+
<div class="swiper portfolio-swiper">
|
|
1620
|
+
<div class="swiper-wrapper">
|
|
1621
|
+
<!-- slides -->
|
|
1622
|
+
</div>
|
|
1623
|
+
<div class="swiper-progress-bar" aria-hidden="true">
|
|
1624
|
+
<div class="swiper-progress-fill"></div>
|
|
1625
|
+
</div>
|
|
1626
|
+
<div class="swiper-nav">
|
|
1627
|
+
<button class="swiper-button-prev" aria-label="Previous"></button>
|
|
1628
|
+
<button class="swiper-button-next" aria-label="Next"></button>
|
|
1629
|
+
</div>
|
|
1630
|
+
</div>
|
|
1631
|
+
```
|
|
1632
|
+
|
|
1633
|
+
**CSS:**
|
|
1634
|
+
```css
|
|
1635
|
+
.swiper-progress-bar {
|
|
1636
|
+
width: 100%; height: 2px;
|
|
1637
|
+
background: var(--border-faint); border-radius: 2px;
|
|
1638
|
+
margin-top: var(--space-lg); overflow: hidden;
|
|
1639
|
+
}
|
|
1640
|
+
.swiper-progress-fill {
|
|
1641
|
+
height: 100%; background: var(--accent-primary);
|
|
1642
|
+
border-radius: 2px;
|
|
1643
|
+
transition: width 0.3s ease;
|
|
1644
|
+
}
|
|
1645
|
+
```
|
|
1646
|
+
|
|
1647
|
+
**JS:**
|
|
1648
|
+
```js
|
|
1649
|
+
const slider = new Swiper('.portfolio-swiper', {
|
|
1650
|
+
loop: false,
|
|
1651
|
+
navigation: {
|
|
1652
|
+
nextEl: '.portfolio-swiper .swiper-button-next',
|
|
1653
|
+
prevEl: '.portfolio-swiper .swiper-button-prev',
|
|
1654
|
+
},
|
|
1655
|
+
on: {
|
|
1656
|
+
init(swiper) { updateProgress(swiper); },
|
|
1657
|
+
slideChange(swiper) { updateProgress(swiper); },
|
|
1658
|
+
},
|
|
1659
|
+
});
|
|
1660
|
+
|
|
1661
|
+
function updateProgress(swiper) {
|
|
1662
|
+
const fill = document.querySelector('.swiper-progress-fill');
|
|
1663
|
+
if (!fill) return;
|
|
1664
|
+
const pct = ((swiper.activeIndex + 1) / swiper.slides.length) * 100;
|
|
1665
|
+
fill.style.width = pct + '%';
|
|
1666
|
+
}
|
|
1667
|
+
```
|
|
1668
|
+
|
|
1669
|
+
---
|
|
1670
|
+
|
|
1671
|
+
### 14g. box-white / box-black Section Alternation
|
|
1672
|
+
|
|
1673
|
+
Aigocy alternates full-width section containers between dark (`box-black`) and light (`box-white`)
|
|
1674
|
+
with decorative gradient glow images for depth. This creates the "cinematic layer cake" effect.
|
|
1675
|
+
|
|
1676
|
+
**HTML:**
|
|
1677
|
+
```html
|
|
1678
|
+
<div class="box-black">
|
|
1679
|
+
<img class="light-top" src="light-bg-top.png" alt="" aria-hidden="true">
|
|
1680
|
+
<section class="features"> ... </section>
|
|
1681
|
+
<img class="light-bot" src="light-bg-bot.png" alt="" aria-hidden="true">
|
|
1682
|
+
</div>
|
|
1683
|
+
|
|
1684
|
+
<div class="box-white">
|
|
1685
|
+
<section class="how-it-works"> ... </section>
|
|
1686
|
+
</div>
|
|
1687
|
+
```
|
|
1688
|
+
|
|
1689
|
+
**CSS (generate glow with CSS instead of images):**
|
|
1690
|
+
```css
|
|
1691
|
+
.box-black {
|
|
1692
|
+
background: var(--bg-base);
|
|
1693
|
+
position: relative; overflow: hidden;
|
|
1694
|
+
}
|
|
1695
|
+
.box-white {
|
|
1696
|
+
background: var(--bg-surface);
|
|
1697
|
+
position: relative; overflow: hidden;
|
|
1698
|
+
}
|
|
1699
|
+
|
|
1700
|
+
/* Decorative ambient glow (replaces light-top/light-bot PNG images) */
|
|
1701
|
+
.box-black::before {
|
|
1702
|
+
content: '';
|
|
1703
|
+
position: absolute; top: -200px; left: 50%;
|
|
1704
|
+
transform: translateX(-50%);
|
|
1705
|
+
width: 800px; height: 400px;
|
|
1706
|
+
background: radial-gradient(ellipse at center,
|
|
1707
|
+
hsla(265,60%,40%,0.15) 0%,
|
|
1708
|
+
transparent 70%
|
|
1709
|
+
);
|
|
1710
|
+
pointer-events: none; z-index: 0;
|
|
1711
|
+
}
|
|
1712
|
+
.box-black::after {
|
|
1713
|
+
content: '';
|
|
1714
|
+
position: absolute; bottom: -200px; left: 50%;
|
|
1715
|
+
transform: translateX(-50%);
|
|
1716
|
+
width: 800px; height: 400px;
|
|
1717
|
+
background: radial-gradient(ellipse at center,
|
|
1718
|
+
hsla(265,60%,40%,0.1) 0%,
|
|
1719
|
+
transparent 70%
|
|
1720
|
+
);
|
|
1721
|
+
pointer-events: none; z-index: 0;
|
|
1722
|
+
}
|
|
1723
|
+
|
|
1724
|
+
/* Ensure section content is above glow */
|
|
1725
|
+
.box-black > section, .box-white > section { position: relative; z-index: 1; }
|
|
1726
|
+
```
|
|
1727
|
+
|
|
1728
|
+
---
|
|
1729
|
+
|
|
1730
|
+
### 14h. Accordion FAQ (Bootstrap collapse pattern)
|
|
1731
|
+
|
|
1732
|
+
Clean accordion FAQ that works without JS library if you use the native `<details>` element,
|
|
1733
|
+
or with Bootstrap's collapse for richer animations.
|
|
1734
|
+
|
|
1735
|
+
**HTML (native, no-library version):**
|
|
1736
|
+
```html
|
|
1737
|
+
<section class="faq" id="faq" aria-labelledby="faq-title">
|
|
1738
|
+
<div class="container">
|
|
1739
|
+
<div class="section-header">
|
|
1740
|
+
<span class="section-label">FAQ</span>
|
|
1741
|
+
<h2 class="section-title" id="faq-title">Common questions</h2>
|
|
1742
|
+
</div>
|
|
1743
|
+
<div class="faq__list">
|
|
1744
|
+
<details class="faq__item">
|
|
1745
|
+
<summary class="faq__question">
|
|
1746
|
+
How long does setup take?
|
|
1747
|
+
<span class="faq__icon" aria-hidden="true">+</span>
|
|
1748
|
+
</summary>
|
|
1749
|
+
<div class="faq__answer">
|
|
1750
|
+
<p>Setup takes under 5 minutes. Connect your account, configure your first workflow, and you're live.</p>
|
|
1751
|
+
</div>
|
|
1752
|
+
</details>
|
|
1753
|
+
<!-- repeat -->
|
|
1754
|
+
</div>
|
|
1755
|
+
</div>
|
|
1756
|
+
</section>
|
|
1757
|
+
```
|
|
1758
|
+
|
|
1759
|
+
**CSS:**
|
|
1760
|
+
```css
|
|
1761
|
+
.faq__list { max-width: 800px; margin: 0 auto; }
|
|
1762
|
+
|
|
1763
|
+
.faq__item {
|
|
1764
|
+
border-bottom: 1px solid var(--border-faint);
|
|
1765
|
+
padding: var(--space-lg) 0;
|
|
1766
|
+
}
|
|
1767
|
+
.faq__item:first-child { border-top: 1px solid var(--border-faint); }
|
|
1768
|
+
|
|
1769
|
+
.faq__question {
|
|
1770
|
+
display: flex; justify-content: space-between; align-items: center;
|
|
1771
|
+
font-size: var(--text-lg); font-weight: 600;
|
|
1772
|
+
color: var(--text-primary); cursor: pointer;
|
|
1773
|
+
list-style: none; /* hide default marker */
|
|
1774
|
+
}
|
|
1775
|
+
.faq__question::-webkit-details-marker { display: none; }
|
|
1776
|
+
|
|
1777
|
+
.faq__icon {
|
|
1778
|
+
font-size: var(--text-xl); font-weight: 300;
|
|
1779
|
+
color: var(--accent-primary);
|
|
1780
|
+
transition: transform var(--duration-base) var(--ease-out);
|
|
1781
|
+
flex-shrink: 0; margin-left: var(--space-lg);
|
|
1782
|
+
}
|
|
1783
|
+
details[open] .faq__icon { transform: rotate(45deg); }
|
|
1784
|
+
|
|
1785
|
+
.faq__answer {
|
|
1786
|
+
padding-top: var(--space-md);
|
|
1787
|
+
}
|
|
1788
|
+
.faq__answer p {
|
|
1789
|
+
font-size: var(--text-base); color: var(--text-secondary); line-height: 1.7;
|
|
1790
|
+
}
|
|
1791
|
+
```
|
|
1792
|
+
|
|
1793
|
+
---
|
|
1794
|
+
|
|
1795
|
+
### 14i. Footer with Watermark Background Logo
|
|
1796
|
+
|
|
1797
|
+
Premium footer pattern: brand name as a large faded watermark behind footer content,
|
|
1798
|
+
with dense 3-column layout (links / copyright / social).
|
|
1799
|
+
|
|
1800
|
+
**HTML:**
|
|
1801
|
+
```html
|
|
1802
|
+
<footer class="footer" role="contentinfo">
|
|
1803
|
+
<div class="footer__watermark" aria-hidden="true">ProductName</div>
|
|
1804
|
+
<div class="container footer__body">
|
|
1805
|
+
<div class="footer__col footer__col--links">
|
|
1806
|
+
<strong>Product</strong>
|
|
1807
|
+
<a href="#">Features</a>
|
|
1808
|
+
<a href="#">Pricing</a>
|
|
1809
|
+
<a href="#">Docs</a>
|
|
1810
|
+
</div>
|
|
1811
|
+
<div class="footer__col footer__col--center">
|
|
1812
|
+
<a href="/" class="footer__logo" aria-label="Home">
|
|
1813
|
+
<img src="logo.svg" alt="ProductName" height="32">
|
|
1814
|
+
</a>
|
|
1815
|
+
<p class="footer__copy">© 2026 ProductName, Inc. All rights reserved.</p>
|
|
1816
|
+
</div>
|
|
1817
|
+
<div class="footer__col footer__col--social">
|
|
1818
|
+
<strong>Follow us</strong>
|
|
1819
|
+
<div class="footer__social">
|
|
1820
|
+
<a href="#" aria-label="Twitter" class="social-link">
|
|
1821
|
+
<svg width="20" height="20" aria-hidden="true"><!-- twitter icon --></svg>
|
|
1822
|
+
Twitter
|
|
1823
|
+
</a>
|
|
1824
|
+
<a href="#" aria-label="LinkedIn" class="social-link">
|
|
1825
|
+
<svg width="20" height="20" aria-hidden="true"><!-- linkedin icon --></svg>
|
|
1826
|
+
LinkedIn
|
|
1827
|
+
</a>
|
|
1828
|
+
</div>
|
|
1829
|
+
</div>
|
|
1830
|
+
</div>
|
|
1831
|
+
</footer>
|
|
1832
|
+
```
|
|
1833
|
+
|
|
1834
|
+
**CSS:**
|
|
1835
|
+
```css
|
|
1836
|
+
.footer {
|
|
1837
|
+
position: relative; overflow: hidden;
|
|
1838
|
+
background: var(--bg-surface);
|
|
1839
|
+
border-top: 1px solid var(--border-faint);
|
|
1840
|
+
padding: var(--space-3xl) 0 var(--space-xl);
|
|
1841
|
+
}
|
|
1842
|
+
|
|
1843
|
+
/* Faded watermark behind content */
|
|
1844
|
+
.footer__watermark {
|
|
1845
|
+
position: absolute; bottom: -20px; left: 50%;
|
|
1846
|
+
transform: translateX(-50%);
|
|
1847
|
+
font-family: var(--font-display); font-size: clamp(80px, 15vw, 160px);
|
|
1848
|
+
font-weight: 900; line-height: 1;
|
|
1849
|
+
color: var(--text-primary); opacity: 0.04;
|
|
1850
|
+
white-space: nowrap; pointer-events: none; user-select: none;
|
|
1851
|
+
}
|
|
1852
|
+
|
|
1853
|
+
.footer__body {
|
|
1854
|
+
display: grid; grid-template-columns: 1fr auto 1fr;
|
|
1855
|
+
gap: var(--space-xl); align-items: start;
|
|
1856
|
+
position: relative; z-index: 1;
|
|
1857
|
+
}
|
|
1858
|
+
.footer__col { display: flex; flex-direction: column; gap: var(--space-sm); }
|
|
1859
|
+
.footer__col--center { text-align: center; align-items: center; }
|
|
1860
|
+
.footer__col--social { align-items: flex-end; }
|
|
1861
|
+
|
|
1862
|
+
.footer__col strong {
|
|
1863
|
+
font-size: var(--text-sm); font-weight: 700; text-transform: uppercase;
|
|
1864
|
+
letter-spacing: 0.08em; color: var(--text-primary); margin-bottom: var(--space-xs);
|
|
1865
|
+
}
|
|
1866
|
+
.footer__col a {
|
|
1867
|
+
font-size: var(--text-sm); color: var(--text-muted);
|
|
1868
|
+
transition: color var(--duration-fast) ease;
|
|
1869
|
+
}
|
|
1870
|
+
.footer__col a:hover { color: var(--text-primary); }
|
|
1871
|
+
|
|
1872
|
+
.social-link {
|
|
1873
|
+
display: flex; align-items: center; gap: var(--space-sm);
|
|
1874
|
+
}
|
|
1875
|
+
|
|
1876
|
+
.footer__copy {
|
|
1877
|
+
font-size: var(--text-sm); color: var(--text-muted); margin-top: var(--space-md);
|
|
1878
|
+
}
|
|
1879
|
+
|
|
1880
|
+
@media (max-width: 768px) {
|
|
1881
|
+
.footer__body { grid-template-columns: 1fr; text-align: center; }
|
|
1882
|
+
.footer__col--social { align-items: center; }
|
|
1883
|
+
.footer__watermark { display: none; }
|
|
1884
|
+
}
|
|
1885
|
+
```
|
|
1886
|
+
|
|
1887
|
+
---
|
|
1888
|
+
|
|
1889
|
+
### 14j. Canvas Cursor Trail (optional, cinematic touch)
|
|
1890
|
+
|
|
1891
|
+
A subtle canvas trail that follows the cursor — drawn as fading dots.
|
|
1892
|
+
Only activate for Bold & Cinematic direction, and skip on touch devices.
|
|
1893
|
+
|
|
1894
|
+
```js
|
|
1895
|
+
// Add <canvas id="cursorTrail" style="position:fixed;inset:0;pointer-events:none;z-index:9999"></canvas>
|
|
1896
|
+
// to <body> to use this.
|
|
1897
|
+
|
|
1898
|
+
(function initCursorTrail() {
|
|
1899
|
+
if (window.matchMedia('(hover: none)').matches) return; // skip on touch
|
|
1900
|
+
if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) return;
|
|
1901
|
+
|
|
1902
|
+
const canvas = document.getElementById('cursorTrail');
|
|
1903
|
+
if (!canvas) return;
|
|
1904
|
+
const ctx = canvas.getContext('2d');
|
|
1905
|
+
canvas.width = window.innerWidth;
|
|
1906
|
+
canvas.height = window.innerHeight;
|
|
1907
|
+
window.addEventListener('resize', () => {
|
|
1908
|
+
canvas.width = window.innerWidth;
|
|
1909
|
+
canvas.height = window.innerHeight;
|
|
1910
|
+
});
|
|
1911
|
+
|
|
1912
|
+
const dots = [];
|
|
1913
|
+
const MAX_DOTS = 20;
|
|
1914
|
+
const COLOR = 'hsla(265, 80%, 65%,';
|
|
1915
|
+
|
|
1916
|
+
window.addEventListener('mousemove', (e) => {
|
|
1917
|
+
dots.push({ x: e.clientX, y: e.clientY, alpha: 1 });
|
|
1918
|
+
if (dots.length > MAX_DOTS) dots.shift();
|
|
1919
|
+
}, { passive: true });
|
|
1920
|
+
|
|
1921
|
+
function draw() {
|
|
1922
|
+
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
|
1923
|
+
dots.forEach((dot, i) => {
|
|
1924
|
+
const size = (i / dots.length) * 8 + 2;
|
|
1925
|
+
dot.alpha = (i / dots.length) * 0.6;
|
|
1926
|
+
ctx.beginPath();
|
|
1927
|
+
ctx.arc(dot.x, dot.y, size, 0, Math.PI * 2);
|
|
1928
|
+
ctx.fillStyle = `${COLOR}${dot.alpha})`;
|
|
1929
|
+
ctx.fill();
|
|
1930
|
+
});
|
|
1931
|
+
requestAnimationFrame(draw);
|
|
1932
|
+
}
|
|
1933
|
+
draw();
|
|
1934
|
+
})();
|
|
1935
|
+
```
|