@pugi/cli 1.0.0-alpha.26 → 1.0.0-alpha.261
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/README.md +19 -8
- package/THIRD_PARTY_NOTICES.md +39 -0
- package/bin/pugi +95 -8
- package/models.json +1 -0
- package/package.json +30 -10
- package/postinstall.mjs +158 -23
- package/release.json +8 -0
- package/skills/a11y/SKILL.md +27 -0
- package/skills/ab-test-analysis/SKILL.md +15 -0
- package/skills/ai-sdk/SKILL.md +27 -0
- package/skills/api-integration/SKILL.md +27 -0
- package/skills/architecture-review/SKILL.md +19 -0
- package/skills/brand-voice/SKILL.md +27 -0
- package/skills/ci-cd-pipeline/SKILL.md +15 -0
- package/skills/code-review/SKILL.md +27 -0
- package/skills/competitor-analysis/SKILL.md +27 -0
- package/skills/content-design/SKILL.md +35 -0
- package/skills/css-tailwind/SKILL.md +39 -0
- package/skills/css-tailwind/quirks.md +86 -0
- package/skills/db-vector/SKILL.md +27 -0
- package/skills/design-radix/SKILL.md +35 -0
- package/skills/design-system/SKILL.md +35 -0
- package/skills/detecting-data-anomalies/SKILL.md +15 -0
- package/skills/docker-deploy/SKILL.md +15 -0
- package/skills/edge-case-finder/SKILL.md +27 -0
- package/skills/fonts/SKILL.md +34 -0
- package/skills/framework-grammy/SKILL.md +27 -0
- package/skills/framework-mastra/SKILL.md +27 -0
- package/skills/framework-nestjs/SKILL.md +27 -0
- package/skills/framework-prisma/SKILL.md +27 -0
- package/skills/funnel-optimization/SKILL.md +19 -0
- package/skills/ha-prototype-web/LICENSE-APACHE-2.0 +201 -0
- package/skills/ha-prototype-web/NOTICE.md +126 -0
- package/skills/ha-prototype-web/SKILL.md +25 -0
- package/skills/icons/SKILL.md +34 -0
- package/skills/landing-templates/SKILL.md +35 -0
- package/skills/market-research/SKILL.md +27 -0
- package/skills/metrics-dashboard/SKILL.md +19 -0
- package/skills/monitoring-setup/SKILL.md +15 -0
- package/skills/motion/SKILL.md +27 -0
- package/skills/nestjs-api/SKILL.md +38 -0
- package/skills/nestjs-api/quirks.md +36 -0
- package/skills/nextjs-page/SKILL.md +27 -0
- package/skills/od-blog-post/LICENSE-APACHE-2.0 +201 -0
- package/skills/od-blog-post/NOTICE.md +126 -0
- package/skills/od-blog-post/SKILL.md +81 -0
- package/skills/od-contact-widget/LICENSE-APACHE-2.0 +201 -0
- package/skills/od-contact-widget/NOTICE.md +126 -0
- package/skills/od-contact-widget/SKILL.md +132 -0
- package/skills/od-craft/LICENSE-APACHE-2.0 +201 -0
- package/skills/od-craft/NOTICE.md +126 -0
- package/skills/od-craft/SKILL.md +58 -0
- package/skills/od-craft/references/README.md +108 -0
- package/skills/od-craft/references/accessibility-baseline.md +203 -0
- package/skills/od-craft/references/animation-discipline.md +156 -0
- package/skills/od-craft/references/anti-ai-slop.md +86 -0
- package/skills/od-craft/references/color.md +90 -0
- package/skills/od-craft/references/form-validation.md +223 -0
- package/skills/od-craft/references/laws-of-ux.md +298 -0
- package/skills/od-craft/references/rtl-and-bidi.md +188 -0
- package/skills/od-craft/references/state-coverage.md +136 -0
- package/skills/od-craft/references/typography-hierarchy-editorial.md +184 -0
- package/skills/od-craft/references/typography-hierarchy.md +167 -0
- package/skills/od-craft/references/typography.md +124 -0
- package/skills/od-critique/LICENSE-APACHE-2.0 +201 -0
- package/skills/od-critique/NOTICE.md +126 -0
- package/skills/od-critique/SKILL.md +259 -0
- package/skills/od-dashboard/LICENSE-APACHE-2.0 +201 -0
- package/skills/od-dashboard/NOTICE.md +126 -0
- package/skills/od-dashboard/SKILL.md +77 -0
- package/skills/od-design-brief/LICENSE-APACHE-2.0 +201 -0
- package/skills/od-design-brief/NOTICE.md +126 -0
- package/skills/od-design-brief/SKILL.md +260 -0
- package/skills/od-docs-page/LICENSE-APACHE-2.0 +201 -0
- package/skills/od-docs-page/NOTICE.md +126 -0
- package/skills/od-docs-page/SKILL.md +81 -0
- package/skills/od-email-marketing/LICENSE-APACHE-2.0 +201 -0
- package/skills/od-email-marketing/NOTICE.md +126 -0
- package/skills/od-email-marketing/SKILL.md +85 -0
- package/skills/od-eng-runbook/LICENSE-APACHE-2.0 +201 -0
- package/skills/od-eng-runbook/NOTICE.md +126 -0
- package/skills/od-eng-runbook/SKILL.md +52 -0
- package/skills/od-faq-page/LICENSE-APACHE-2.0 +201 -0
- package/skills/od-faq-page/NOTICE.md +126 -0
- package/skills/od-faq-page/SKILL.md +113 -0
- package/skills/od-finance-report/LICENSE-APACHE-2.0 +201 -0
- package/skills/od-finance-report/NOTICE.md +126 -0
- package/skills/od-finance-report/SKILL.md +62 -0
- package/skills/od-frontend-design/LICENSE-APACHE-2.0 +201 -0
- package/skills/od-frontend-design/LICENSE.txt +177 -0
- package/skills/od-frontend-design/NOTICE.md +126 -0
- package/skills/od-frontend-design/SKILL.md +86 -0
- package/skills/od-hr-onboarding/LICENSE-APACHE-2.0 +201 -0
- package/skills/od-hr-onboarding/NOTICE.md +126 -0
- package/skills/od-hr-onboarding/SKILL.md +53 -0
- package/skills/od-invoice/LICENSE-APACHE-2.0 +201 -0
- package/skills/od-invoice/NOTICE.md +126 -0
- package/skills/od-invoice/SKILL.md +49 -0
- package/skills/od-kanban-board/LICENSE-APACHE-2.0 +201 -0
- package/skills/od-kanban-board/NOTICE.md +126 -0
- package/skills/od-kanban-board/SKILL.md +51 -0
- package/skills/od-login-flow/LICENSE-APACHE-2.0 +201 -0
- package/skills/od-login-flow/NOTICE.md +126 -0
- package/skills/od-login-flow/SKILL.md +49 -0
- package/skills/od-login-flow/references/checklist.md +19 -0
- package/skills/od-magazine-poster/LICENSE-APACHE-2.0 +201 -0
- package/skills/od-magazine-poster/NOTICE.md +126 -0
- package/skills/od-magazine-poster/SKILL.md +90 -0
- package/skills/od-meeting-notes/LICENSE-APACHE-2.0 +201 -0
- package/skills/od-meeting-notes/NOTICE.md +126 -0
- package/skills/od-meeting-notes/SKILL.md +48 -0
- package/skills/od-mobile-app/LICENSE-APACHE-2.0 +201 -0
- package/skills/od-mobile-app/NOTICE.md +126 -0
- package/skills/od-mobile-app/SKILL.md +103 -0
- package/skills/od-mobile-app/assets/template.html +442 -0
- package/skills/od-mobile-app/references/checklist.md +46 -0
- package/skills/od-mobile-app/references/layouts.md +312 -0
- package/skills/od-mobile-onboarding/LICENSE-APACHE-2.0 +201 -0
- package/skills/od-mobile-onboarding/NOTICE.md +126 -0
- package/skills/od-mobile-onboarding/SKILL.md +54 -0
- package/skills/od-pm-spec/LICENSE-APACHE-2.0 +201 -0
- package/skills/od-pm-spec/NOTICE.md +126 -0
- package/skills/od-pm-spec/SKILL.md +53 -0
- package/skills/od-pricing-page/LICENSE-APACHE-2.0 +201 -0
- package/skills/od-pricing-page/NOTICE.md +126 -0
- package/skills/od-pricing-page/SKILL.md +80 -0
- package/skills/od-redesign-skill/LICENSE +21 -0
- package/skills/od-redesign-skill/LICENSE-APACHE-2.0 +201 -0
- package/skills/od-redesign-skill/NOTICE.md +126 -0
- package/skills/od-redesign-skill/SKILL.md +205 -0
- package/skills/od-reference-design-contract/LICENSE-APACHE-2.0 +201 -0
- package/skills/od-reference-design-contract/NOTICE.md +126 -0
- package/skills/od-reference-design-contract/SKILL.md +131 -0
- package/skills/od-reference-design-contract/references/checklist.md +26 -0
- package/skills/od-saas-landing/LICENSE-APACHE-2.0 +201 -0
- package/skills/od-saas-landing/NOTICE.md +126 -0
- package/skills/od-saas-landing/SKILL.md +125 -0
- package/skills/od-social-carousel/LICENSE-APACHE-2.0 +201 -0
- package/skills/od-social-carousel/NOTICE.md +126 -0
- package/skills/od-social-carousel/SKILL.md +92 -0
- package/skills/od-team-okrs/LICENSE-APACHE-2.0 +201 -0
- package/skills/od-team-okrs/NOTICE.md +126 -0
- package/skills/od-team-okrs/SKILL.md +48 -0
- package/skills/od-waitlist-page/LICENSE-APACHE-2.0 +201 -0
- package/skills/od-waitlist-page/NOTICE.md +126 -0
- package/skills/od-waitlist-page/SKILL.md +229 -0
- package/skills/od-waitlist-page/assets/template.html +457 -0
- package/skills/od-waitlist-page/references/checklist.md +42 -0
- package/skills/od-web-design-guidelines/LICENSE +21 -0
- package/skills/od-web-design-guidelines/LICENSE-APACHE-2.0 +201 -0
- package/skills/od-web-design-guidelines/NOTICE.md +126 -0
- package/skills/od-web-design-guidelines/SKILL.md +66 -0
- package/skills/od-web-design-guidelines/references/guidelines.md +133 -0
- package/skills/od-web-prototype/LICENSE-APACHE-2.0 +201 -0
- package/skills/od-web-prototype/NOTICE.md +126 -0
- package/skills/od-web-prototype/SKILL.md +102 -0
- package/skills/od-web-prototype/assets/template.html +348 -0
- package/skills/od-web-prototype/references/checklist.md +47 -0
- package/skills/od-web-prototype/references/layouts.md +247 -0
- package/skills/od-web-prototype-taste-brutalist/LICENSE-APACHE-2.0 +201 -0
- package/skills/od-web-prototype-taste-brutalist/LICENSE-MIT-Leonxlnx +21 -0
- package/skills/od-web-prototype-taste-brutalist/NOTICE.md +126 -0
- package/skills/od-web-prototype-taste-brutalist/SKILL.md +64 -0
- package/skills/od-web-prototype-taste-editorial/LICENSE-APACHE-2.0 +201 -0
- package/skills/od-web-prototype-taste-editorial/LICENSE-MIT-Leonxlnx +21 -0
- package/skills/od-web-prototype-taste-editorial/NOTICE.md +126 -0
- package/skills/od-web-prototype-taste-editorial/SKILL.md +61 -0
- package/skills/od-web-prototype-taste-soft/LICENSE-APACHE-2.0 +201 -0
- package/skills/od-web-prototype-taste-soft/LICENSE-MIT-Leonxlnx +21 -0
- package/skills/od-web-prototype-taste-soft/NOTICE.md +126 -0
- package/skills/od-web-prototype-taste-soft/SKILL.md +64 -0
- package/skills/od-webgl-experience/LICENSE-APACHE-2.0 +201 -0
- package/skills/od-webgl-experience/NOTICE.md +126 -0
- package/skills/od-webgl-experience/SKILL.md +81 -0
- package/skills/od-wireframe-annotated/LICENSE-APACHE-2.0 +201 -0
- package/skills/od-wireframe-annotated/NOTICE.md +126 -0
- package/skills/od-wireframe-annotated/SKILL.md +90 -0
- package/skills/od-wireframe-greybox/LICENSE-APACHE-2.0 +201 -0
- package/skills/od-wireframe-greybox/NOTICE.md +126 -0
- package/skills/od-wireframe-greybox/SKILL.md +90 -0
- package/skills/od-wireframe-mobile-flow/LICENSE-APACHE-2.0 +201 -0
- package/skills/od-wireframe-mobile-flow/NOTICE.md +126 -0
- package/skills/od-wireframe-mobile-flow/SKILL.md +96 -0
- package/skills/od-wireframe-sketch/LICENSE-APACHE-2.0 +201 -0
- package/skills/od-wireframe-sketch/NOTICE.md +126 -0
- package/skills/od-wireframe-sketch/SKILL.md +98 -0
- package/skills/orchestrating-multi-agent-systems/SKILL.md +19 -0
- package/skills/preprocessing-data/SKILL.md +15 -0
- package/skills/project-memory-keeper/SKILL.md +19 -0
- package/skills/public-apis/SKILL.md +27 -0
- package/skills/pugi-patterns/SKILL.md +19 -0
- package/skills/react-component/SKILL.md +27 -0
- package/skills/sprint-planning/SKILL.md +19 -0
- package/skills/stack-selection/SKILL.md +15 -0
- package/skills/standup-summary/SKILL.md +15 -0
- package/skills/task-decomposition/SKILL.md +19 -0
- package/skills/team-routing/SKILL.md +15 -0
- package/skills/tech-survey/SKILL.md +15 -0
- package/skills/test-plan/SKILL.md +15 -0
- package/skills/typescript-refactor/SKILL.md +15 -0
- package/skills/ui-components/SKILL.md +35 -0
- package/skills/ux-patterns/SKILL.md +35 -0
- package/skills/web-platform/SKILL.md +27 -0
- package/skills/web-scrape/SKILL.md +27 -0
- package/skills/web-search/SKILL.md +15 -0
- package/skills/webapp-testing/SKILL.md +37 -0
- package/skills/webapp-testing/quirks.md +26 -0
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: od-waitlist-page
|
|
3
|
+
description: |
|
|
4
|
+
Minimal pre-launch landing with email capture, brand logo, and optional decorative layer.
|
|
5
|
+
Reads DESIGN.md for colors, typography, and layout rules.
|
|
6
|
+
Best for: product launches, beta signups, early access programs, indie projects.
|
|
7
|
+
triggers:
|
|
8
|
+
- "waitlist page"
|
|
9
|
+
- "coming soon page"
|
|
10
|
+
- "pre-launch landing page"
|
|
11
|
+
- "email capture page"
|
|
12
|
+
- "launch page"
|
|
13
|
+
od:
|
|
14
|
+
mode: prototype
|
|
15
|
+
platform: desktop
|
|
16
|
+
scenario: marketing
|
|
17
|
+
preview:
|
|
18
|
+
type: html
|
|
19
|
+
entry: example.html
|
|
20
|
+
design_system:
|
|
21
|
+
requires: true
|
|
22
|
+
sections: [color, typography, layout, components]
|
|
23
|
+
inputs:
|
|
24
|
+
- name: product_name
|
|
25
|
+
type: string
|
|
26
|
+
required: true
|
|
27
|
+
- name: tagline
|
|
28
|
+
type: string
|
|
29
|
+
required: true
|
|
30
|
+
- name: bg_expression
|
|
31
|
+
type: string
|
|
32
|
+
description: "CSS color expression for background (e.g., '#FDE8DF' or 'rgba(...)')."
|
|
33
|
+
required: true
|
|
34
|
+
- name: fg_expression
|
|
35
|
+
type: string
|
|
36
|
+
description: "CSS color expression for text and primary shapes (e.g., '#1A1410' or 'rgba(...)')."
|
|
37
|
+
required: true
|
|
38
|
+
- name: accent_expression
|
|
39
|
+
type: string
|
|
40
|
+
description: "CSS color expression for brand badge and highlights."
|
|
41
|
+
required: true
|
|
42
|
+
- name: deco_expression
|
|
43
|
+
type: string
|
|
44
|
+
description: "CSS color expression for decoration primary."
|
|
45
|
+
required: true
|
|
46
|
+
- name: stripe_expression
|
|
47
|
+
type: string
|
|
48
|
+
description: "CSS color expression for accent stripe."
|
|
49
|
+
required: true
|
|
50
|
+
- name: border_expression
|
|
51
|
+
type: string
|
|
52
|
+
description: "CSS color expression for input borders (e.g., 'rgba(100,50,30,0.38)' or 'color-mix(in srgb, var(--fg) 38%, transparent)'). Must be valid CSS."
|
|
53
|
+
required: true
|
|
54
|
+
- name: success_expression
|
|
55
|
+
type: string
|
|
56
|
+
description: "CSS color expression for success message. Defaults to #2D6A4F if not in DESIGN.md."
|
|
57
|
+
required: true
|
|
58
|
+
- name: btn_label_expression
|
|
59
|
+
type: string
|
|
60
|
+
description: "CSS color expression for button label text (e.g., 'rgba(255,255,255,1)' or '#fff'). Ensure WCAG AA contrast."
|
|
61
|
+
required: true
|
|
62
|
+
- name: ticker_bg_expression
|
|
63
|
+
type: string
|
|
64
|
+
description: "CSS color expression for ticker background (e.g., 'rgba(0,0,0,0.9)'). Must be valid CSS."
|
|
65
|
+
required: true
|
|
66
|
+
- name: ticker_fg_expression
|
|
67
|
+
type: string
|
|
68
|
+
description: "CSS color expression for ticker text (e.g., 'rgba(255,255,255,0.9)'). Ensure contrast."
|
|
69
|
+
required: true
|
|
70
|
+
- name: deco_stroke_expression
|
|
71
|
+
type: string
|
|
72
|
+
description: "CSS color expression for SVG strokes (e.g., 'rgba(0,0,0,0.12)'). Typically a muted foreground or neutral."
|
|
73
|
+
required: true
|
|
74
|
+
- name: logo_shadow_expression
|
|
75
|
+
type: string
|
|
76
|
+
description: "CSS color expression for logo container shadow (e.g., 'rgba(0,0,0,0.08)'). Typically a subtle foreground shade."
|
|
77
|
+
required: true
|
|
78
|
+
- name: logo_fg_expression
|
|
79
|
+
type: string
|
|
80
|
+
description: "CSS color expression for logo text/initials (e.g., 'rgba(255,255,255,1)' or '#fff'). Ensure contrast against accent."
|
|
81
|
+
required: true
|
|
82
|
+
- name: logo_mark
|
|
83
|
+
type: string
|
|
84
|
+
description: "Text initials are HTML-escaped by default. Inline SVG is allowed only after strict allowlist-based sanitization (removing scripts, event handlers, foreignObject, and external references); unsafe input falls back to escaped text."
|
|
85
|
+
required: false
|
|
86
|
+
- name: display_font_url
|
|
87
|
+
type: string
|
|
88
|
+
description: "Display font name with spaces encoded as '+' (e.g., 'Syne', 'DM+Sans'). Used in Google Fonts URL."
|
|
89
|
+
required: true
|
|
90
|
+
- name: display_font_css
|
|
91
|
+
type: string
|
|
92
|
+
description: "Display font name as it appears in CSS (e.g., 'Syne', 'DM Sans'). Already quoted if needed; no extra quotes in template."
|
|
93
|
+
required: true
|
|
94
|
+
- name: body_font_url
|
|
95
|
+
type: string
|
|
96
|
+
description: "Body font name with spaces encoded as '+' (e.g., 'DM+Sans', 'IBM+Plex+Serif'). Used in Google Fonts URL."
|
|
97
|
+
required: true
|
|
98
|
+
- name: body_font_css
|
|
99
|
+
type: string
|
|
100
|
+
description: "Body font name as it appears in CSS (e.g., 'DM Sans', 'IBM Plex Serif'). Already quoted if needed; no extra quotes in template."
|
|
101
|
+
required: true
|
|
102
|
+
outputs:
|
|
103
|
+
primary: index.html
|
|
104
|
+
capabilities_required:
|
|
105
|
+
- file_write
|
|
106
|
+
example_prompt: "Make a waitlist page for a design tool — clean, minimal, with a custom logo and one call-to-action."
|
|
107
|
+
---
|
|
108
|
+
<!-- Vendored from nexu-io/open-design@3b0df90f305c (design-templates/waitlist-page), Apache-2.0. Modified by Pugi: frontmatter `name` changed from `waitlist-page` to `od-waitlist-page`; example files omitted. See NOTICE.md in this directory. Where this skill conflicts with the Pugi plan-gate prototype contract in the designer persona prompt, the Pugi contract wins. -->
|
|
109
|
+
|
|
110
|
+
# Waitlist Page Skill
|
|
111
|
+
|
|
112
|
+
Pre-launch pages are your first handshake with future users. This skill builds a focused, honest entrance: your brand identity, what you're making, one clear path to join the early list. No artificial scarcity, no fake countdown, no inflation tactics—just a clean, mobile-first vessel for genuine interest.
|
|
113
|
+
|
|
114
|
+
## Workflow
|
|
115
|
+
|
|
116
|
+
### Preflight: Load hardened template and brand foundation
|
|
117
|
+
|
|
118
|
+
0. **Load the brand identity** — Read `DESIGN.md` for the color system, font pairing, and spatial rules. This is your foundation. A waitlist page lives or dies by consistency with the brand it represents. If `DESIGN.md` is missing, ask the user to provide one before you proceed.
|
|
119
|
+
1. **Read and copy the reusable template** — Read `assets/template.html`. This template is the hardened seed for all outputs. Copy it to `index.html` as your base. Do not write HTML from scratch or deviate from this structure. The template has all required layout, form structure, decorations, focus styles, and accessibility scaffolding baked in.
|
|
120
|
+
|
|
121
|
+
### Steps: Token replacement with validation and escaping
|
|
122
|
+
|
|
123
|
+
2. **Map tokens from inputs** — For each placeholder in the template (e.g., `{{PRODUCT_NAME}}`, `{{BG_EXPRESSION}}`, `{{BORDER_EXPRESSION}}`, `{{LOGO_MARK}}`), follow the replacement rules below:
|
|
124
|
+
- **Text tokens** (`{{PRODUCT_NAME}}`, `{{TAGLINE}}`): HTML-escape `<`, `>`, `&`, `"`, `'` before insertion into HTML text nodes or attribute values.
|
|
125
|
+
- **HTML tokens** (`{{LOGO_MARK}}`): If using text initials, HTML-escape them by default. If using inline SVG, you must strictly sanitize it using an allowlist: strip `<script>` tags, event handlers (`on*`), `<foreignObject>`, external refs (`href`, `xlink:href`, `url()`), and any disallowed attributes/elements before insertion. If the SVG cannot be safely sanitized, fallback to escaped text initials. Never emit raw, unsanitized arbitrary HTML. Ensure any SVG scales cleanly within its container.
|
|
126
|
+
- **Color expression tokens** (`{{BG_EXPRESSION}}`, `{{FG_EXPRESSION}}`, `{{ACCENT_EXPRESSION}}`, `{{DECO_EXPRESSION}}`, `{{STRIPE_EXPRESSION}}`, `{{SUCCESS_EXPRESSION}}`, `{{BORDER_EXPRESSION}}`, `{{BTN_LABEL_EXPRESSION}}`, `{{TICKER_BG_EXPRESSION}}`, `{{TICKER_FG_EXPRESSION}}`, `{{DECO_STROKE_EXPRESSION}}`, `{{LOGO_SHADOW_EXPRESSION}}`, `{{LOGO_FG_EXPRESSION}}`): Must strictly adhere to an explicit color grammar (`#hex`, `rgb`/`rgba`, `hsl`/`hsla`, `oklch`, or `color-mix()` using only local variables). Hard reject any input containing `;`, `{}`, `<`, `>`, comments (`/*`), `@`, `url(`, or external refs to prevent CSS injection. Do not wrap in `#` or add extra quotes. Examples: `rgba(196, 169, 154, 0.38)`, `color-mix(in srgb, var(--fg) 38%, transparent)`, `#FDE8DF`. Insert as-is into `:root` CSS variables. Derive `--success` from DESIGN.md if present; otherwise use the allowed fallback `#2D6A4F` only.
|
|
127
|
+
- **Font name tokens** (`{{DISPLAY_FONT_CSS}}`, `{{BODY_FONT_CSS}}`): These are CSS font-family values, already quoted if they contain spaces (e.g., `'DM Sans'`, `Syne`). Insert as-is into `--font-display` and `--font-body` declarations; do NOT add extra quotes.
|
|
128
|
+
- **Font URL tokens** (`{{DISPLAY_FONT_URL}}`, `{{BODY_FONT_URL}}`): Spaces must be encoded as `+` for the Google Fonts URL (e.g., `DM+Sans`, `IBM+Plex+Serif`). Validate the URL is well-formed before insertion.
|
|
129
|
+
3. **Verify token mapping rules** — All color tokens are now in CSS variables:
|
|
130
|
+
- `--bg` = `{{BG_EXPRESSION}}` (e.g., `#FDE8DF`)
|
|
131
|
+
- `--fg` = `{{FG_EXPRESSION}}` (e.g., `#1A1410`)
|
|
132
|
+
- `--accent` = `{{ACCENT_EXPRESSION}}` (brand badge)
|
|
133
|
+
- `--deco` = `{{DECO_EXPRESSION}}` (decoration primary)
|
|
134
|
+
- `--deco-stripe` = `{{STRIPE_EXPRESSION}}` (accent stripe)
|
|
135
|
+
- `--input-border` = `{{BORDER_EXPRESSION}}` (full CSS expression with opacity)
|
|
136
|
+
- `--success` = `{{SUCCESS_EXPRESSION}}` or `#2D6A4F` fallback
|
|
137
|
+
- `--btn-label` = `{{BTN_LABEL_EXPRESSION}}` (button text color for contrast)
|
|
138
|
+
- `--ticker-bg` = `{{TICKER_BG_EXPRESSION}}` (animated ticker background)
|
|
139
|
+
- `--ticker-fg` = `{{TICKER_FG_EXPRESSION}}` (animated ticker text)
|
|
140
|
+
- `--deco-stroke` = `{{DECO_STROKE_EXPRESSION}}` (SVG strokes, typically muted with 12–15% opacity)
|
|
141
|
+
- `--logo-shadow` = `{{LOGO_SHADOW_EXPRESSION}}` (logo container shadow, subtle foreground shade)
|
|
142
|
+
- `--logo-fg` = `{{LOGO_FG_EXPRESSION}}` (contrasting text color for logo initials)
|
|
143
|
+
4. **Responsive layout** — The template includes mobile-first scaling:
|
|
144
|
+
- 375px: form stacks to single column, logo shrinks to 40px, decoration compresses, no horizontal scroll.
|
|
145
|
+
- 768px: comfortable two-column breathing room.
|
|
146
|
+
- 1440px+: centered layout with generous whitespace.
|
|
147
|
+
- Verify all text remains readable and the email input + button are fully visible (no clipping) at 375×667 and 390×844.
|
|
148
|
+
|
|
149
|
+
### Validation: Run hardened quality gates before emitting
|
|
150
|
+
|
|
151
|
+
5. **Run the quality checklist** — Read `references/checklist.md`. Validate every **P0 gate** before emitting:
|
|
152
|
+
- Exactly one CTA (email form)
|
|
153
|
+
- No countdown timer, no fake social proof, no emoji
|
|
154
|
+
- No horizontal scroll at 375px
|
|
155
|
+
- Email input has `type="email"` and `required`; first name has no `required`
|
|
156
|
+
- Form does NOT use `novalidate`; JS uses `checkValidity()` guard
|
|
157
|
+
- Success message has `role="status"` or `aria-live="polite"`
|
|
158
|
+
- All colors from DESIGN.md or allowed fallback; **no invented hex values**
|
|
159
|
+
- All user text is HTML-escaped; color tokens adhere to strict grammar; fonts are URL-encoded
|
|
160
|
+
- **Pass P0 or do not emit.** If any P0 gate fails, ask the user or fix the token mappings and try again.
|
|
161
|
+
6. **Verify P1 gates** for quality submission (recommended):
|
|
162
|
+
- Hover and active states on submit button
|
|
163
|
+
- Form validation with clear feedback
|
|
164
|
+
- Ticker animation respects `prefers-reduced-motion: reduce`
|
|
165
|
+
- All interactive elements have visible focus styles (input:focus includes `outline`)
|
|
166
|
+
- Tab/Enter keyboard support
|
|
167
|
+
- WCAG AA contrast for body text (≥4.5:1) and button label (≥4.5:1)
|
|
168
|
+
- Logo alt/aria-label present; ticker has `aria-hidden="true"`
|
|
169
|
+
- `<html lang="">` is set
|
|
170
|
+
|
|
171
|
+
### Output
|
|
172
|
+
|
|
173
|
+
7. **Emit clean HTML** — Single file, CSS inlined, SVG for graphics. Mark interactive elements with `data-od-id` (headline, form, logo, ticker, grid, etc.) so agents can customize without parsing.
|
|
174
|
+
|
|
175
|
+
## Quality gates
|
|
176
|
+
|
|
177
|
+
This skill enforces a hardened, template-based workflow to ensure compliance. **You must follow this execution path; deviating from the template disqualifies the output.**
|
|
178
|
+
|
|
179
|
+
**Mandatory template-based execution:**
|
|
180
|
+
- Read `assets/template.html` as your base; do not write HTML from scratch.
|
|
181
|
+
- Copy it to `index.html` and replace only the `{{PLACEHOLDER}}` tokens with validated/escaped values.
|
|
182
|
+
- Run every P0 gate in `references/checklist.md` before emitting; if any fail, fix and re-validate.
|
|
183
|
+
|
|
184
|
+
**P0 gates (must pass):**
|
|
185
|
+
- Single CTA: Email form is the only interactive element. No nav, no secondary buttons, no social links.
|
|
186
|
+
- Logo placement: Fixed top-left, matches DESIGN.md accent color, scales down on mobile (50px → 40px).
|
|
187
|
+
- Color consistency: Every color from DESIGN.md palette. Only allowed hardcoded exception: `#2D6A4F` for `--success`.
|
|
188
|
+
- No anti-patterns: No countdown timer, no fake social proof, no emoji icons, no lorem ipsum.
|
|
189
|
+
- Content integrity: Headline and copy tie directly to `product_name` and `tagline` inputs—no filler copy.
|
|
190
|
+
- Mobile fit: No horizontal scroll at 375px. Email input and submit button are fully visible (no clipping) at 375×667 and 390×844. Vertical overflow is scrollable, not hidden.
|
|
191
|
+
- Typographic discipline: Display + body fonts only (2-font rule). Consistent sizing across sections.
|
|
192
|
+
- Form structure: Two fields (first name optional, email required), `checkValidity()` guard, success message with `role="status"`.
|
|
193
|
+
- Token escaping: All user-supplied text HTML-escaped; color tokens adhere to strict grammar and contain no unsafe characters (`;`, `{}`, `<`, `>`, `/*`, `@`, `url(`); font names are URL-encoded; `{{LOGO_MARK}}` is escaped text initials or strictly sanitized inline SVG (no scripts, no event handlers, no foreignObject, no external hrefs).
|
|
194
|
+
|
|
195
|
+
**P1 gates (should pass for quality submission):**
|
|
196
|
+
- Hero section visually distinct and above-the-fold.
|
|
197
|
+
- Email submit button has hover and active states.
|
|
198
|
+
- Form validation provides clear feedback.
|
|
199
|
+
- Page scrollable (not clipped) at all mobile viewports; CTA visible without scroll.
|
|
200
|
+
- Ticker animation paused/removed under `prefers-reduced-motion: reduce`.
|
|
201
|
+
- All interactive elements have visible focus styles (outline, not just outline:none).
|
|
202
|
+
- Keyboard: Tab reaches each form field; Enter submits.
|
|
203
|
+
- WCAG AA contrast: Body text ≥4.5:1, button label ≥4.5:1.
|
|
204
|
+
- Logo alt/aria-label present; ticker has `aria-hidden="true"`.
|
|
205
|
+
- `<html lang="">` set to correct language code.
|
|
206
|
+
|
|
207
|
+
**Decoration restraint:**
|
|
208
|
+
- Lower zone enhances without distraction. Opacity, subtle strokes, muted animation.
|
|
209
|
+
- No gradient that spans more than 20% of viewport height.
|
|
210
|
+
- Coil, stripe, grid, or ticker all use colors from DESIGN.md only.
|
|
211
|
+
|
|
212
|
+
## Output
|
|
213
|
+
|
|
214
|
+
**Only emit after all P0 gates in `references/checklist.md` pass.**
|
|
215
|
+
|
|
216
|
+
Emit the artifact between tags:
|
|
217
|
+
|
|
218
|
+
```
|
|
219
|
+
<artifact identifier="waitlist-id" type="text/html" title="Coming Soon — {{PRODUCT_NAME}}">
|
|
220
|
+
<!doctype html>
|
|
221
|
+
<html lang="en">
|
|
222
|
+
...
|
|
223
|
+
</html>
|
|
224
|
+
</artifact>
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
One line of description above the artifact; nothing below.
|
|
228
|
+
|
|
229
|
+
**Post-emission:** If the user asks for changes, update the index.html in-place and re-run the P0 checklist gates before emitting the next version. Do not skip validation on iterations.
|
|
@@ -0,0 +1,457 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>{{PRODUCT_NAME}} — Coming Soon</title>
|
|
7
|
+
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
8
|
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
9
|
+
<link href="https://fonts.googleapis.com/css2?family={{DISPLAY_FONT_URL}}:wght@700;800&family={{BODY_FONT_URL}}:wght@400;500;600&display=swap" rel="stylesheet">
|
|
10
|
+
<style>
|
|
11
|
+
:root {
|
|
12
|
+
--bg: {{BG_EXPRESSION}};
|
|
13
|
+
--fg: {{FG_EXPRESSION}};
|
|
14
|
+
--accent: {{ACCENT_EXPRESSION}};
|
|
15
|
+
--deco: {{DECO_EXPRESSION}};
|
|
16
|
+
--deco-stripe: {{STRIPE_EXPRESSION}};
|
|
17
|
+
--input-border: {{BORDER_EXPRESSION}};
|
|
18
|
+
--success: {{SUCCESS_EXPRESSION}};
|
|
19
|
+
--btn-label: {{BTN_LABEL_EXPRESSION}};
|
|
20
|
+
--ticker-bg: {{TICKER_BG_EXPRESSION}};
|
|
21
|
+
--ticker-fg: {{TICKER_FG_EXPRESSION}};
|
|
22
|
+
--deco-stroke: {{DECO_STROKE_EXPRESSION}};
|
|
23
|
+
--logo-shadow: {{LOGO_SHADOW_EXPRESSION}};
|
|
24
|
+
--logo-fg: {{LOGO_FG_EXPRESSION}};
|
|
25
|
+
--font-body: {{BODY_FONT_CSS}}, sans-serif;
|
|
26
|
+
--font-display: {{DISPLAY_FONT_CSS}}, serif;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
*, *::before, *::after {
|
|
30
|
+
box-sizing: border-box;
|
|
31
|
+
margin: 0;
|
|
32
|
+
padding: 0;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
html, body {
|
|
36
|
+
height: 100%;
|
|
37
|
+
overflow-x: hidden;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
body {
|
|
41
|
+
font-family: var(--font-body);
|
|
42
|
+
background-color: var(--bg);
|
|
43
|
+
color: var(--fg);
|
|
44
|
+
display: flex;
|
|
45
|
+
flex-direction: column;
|
|
46
|
+
min-height: 100svh;
|
|
47
|
+
overflow-x: hidden;
|
|
48
|
+
overflow-y: auto;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/* Logo — reusable component */
|
|
52
|
+
.logo {
|
|
53
|
+
position: absolute;
|
|
54
|
+
top: 20px;
|
|
55
|
+
left: 24px;
|
|
56
|
+
display: flex;
|
|
57
|
+
align-items: center;
|
|
58
|
+
gap: 12px;
|
|
59
|
+
z-index: 10;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
.logo-container {
|
|
63
|
+
width: 48px;
|
|
64
|
+
height: 48px;
|
|
65
|
+
flex-shrink: 0;
|
|
66
|
+
border-radius: 50%;
|
|
67
|
+
background: var(--accent);
|
|
68
|
+
display: flex;
|
|
69
|
+
align-items: center;
|
|
70
|
+
justify-content: center;
|
|
71
|
+
overflow: hidden;
|
|
72
|
+
color: var(--logo-fg);
|
|
73
|
+
font-family: var(--font-display);
|
|
74
|
+
font-size: 20px;
|
|
75
|
+
font-weight: 700;
|
|
76
|
+
letter-spacing: -0.02em;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
.logo-container svg {
|
|
80
|
+
width: 100%;
|
|
81
|
+
height: 100%;
|
|
82
|
+
object-fit: contain;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
.logo-text {
|
|
86
|
+
display: flex;
|
|
87
|
+
align-items: center;
|
|
88
|
+
gap: 8px;
|
|
89
|
+
flex-shrink: 0;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
.logo-name {
|
|
93
|
+
font-family: var(--font-display);
|
|
94
|
+
font-weight: 700;
|
|
95
|
+
font-size: 16px;
|
|
96
|
+
color: var(--fg);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
.logo-dot {
|
|
100
|
+
font-size: 8px;
|
|
101
|
+
color: var(--accent);
|
|
102
|
+
opacity: 0.6;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/* Upper section — hero */
|
|
106
|
+
.upper {
|
|
107
|
+
flex: 0 0 auto;
|
|
108
|
+
display: flex;
|
|
109
|
+
flex-direction: column;
|
|
110
|
+
align-items: center;
|
|
111
|
+
justify-content: center;
|
|
112
|
+
text-align: center;
|
|
113
|
+
padding: 60px 24px 40px;
|
|
114
|
+
position: relative;
|
|
115
|
+
z-index: 2;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
.headline {
|
|
119
|
+
font-family: var(--font-display);
|
|
120
|
+
font-weight: 800;
|
|
121
|
+
font-size: clamp(48px, 7vw, 80px);
|
|
122
|
+
letter-spacing: -0.02em;
|
|
123
|
+
line-height: 1.1;
|
|
124
|
+
color: var(--fg);
|
|
125
|
+
max-width: 900px;
|
|
126
|
+
margin: 0 auto 18px;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
.subtext {
|
|
130
|
+
font-size: clamp(14px, 1.3vw, 16px);
|
|
131
|
+
font-weight: 400;
|
|
132
|
+
line-height: 1.5;
|
|
133
|
+
color: var(--fg);
|
|
134
|
+
opacity: 0.75;
|
|
135
|
+
max-width: 480px;
|
|
136
|
+
margin-bottom: 40px;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/* Form */
|
|
140
|
+
.form-row {
|
|
141
|
+
display: flex;
|
|
142
|
+
gap: 8px;
|
|
143
|
+
width: 100%;
|
|
144
|
+
max-width: 560px;
|
|
145
|
+
justify-content: center;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
.form-row input {
|
|
149
|
+
flex: 1;
|
|
150
|
+
min-width: 0;
|
|
151
|
+
padding: 14px 16px;
|
|
152
|
+
font-family: var(--font-body);
|
|
153
|
+
font-size: 14px;
|
|
154
|
+
color: var(--fg);
|
|
155
|
+
background: transparent;
|
|
156
|
+
border: 1.5px solid var(--input-border);
|
|
157
|
+
border-radius: 3px;
|
|
158
|
+
outline: none;
|
|
159
|
+
transition: border-color 0.2s;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
.form-row input::placeholder {
|
|
163
|
+
color: var(--input-border);
|
|
164
|
+
opacity: 0.7;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
.form-row input:focus {
|
|
168
|
+
border-color: var(--fg);
|
|
169
|
+
outline: 2px solid var(--fg);
|
|
170
|
+
outline-offset: 2px;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
.form-row button {
|
|
174
|
+
flex-shrink: 0;
|
|
175
|
+
padding: 14px 24px;
|
|
176
|
+
font-family: var(--font-body);
|
|
177
|
+
font-size: 14px;
|
|
178
|
+
font-weight: 600;
|
|
179
|
+
color: var(--btn-label);
|
|
180
|
+
background: var(--fg);
|
|
181
|
+
border: none;
|
|
182
|
+
border-radius: 2px;
|
|
183
|
+
cursor: pointer;
|
|
184
|
+
white-space: nowrap;
|
|
185
|
+
transition: opacity 0.15s;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
.form-row button:hover {
|
|
189
|
+
opacity: 0.85;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
.success-msg {
|
|
193
|
+
display: none;
|
|
194
|
+
margin-top: 16px;
|
|
195
|
+
font-size: 14px;
|
|
196
|
+
font-weight: 500;
|
|
197
|
+
color: var(--success);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
.success-msg.visible {
|
|
201
|
+
display: block;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/* Decoration section */
|
|
205
|
+
.deco-section {
|
|
206
|
+
flex: 1;
|
|
207
|
+
position: relative;
|
|
208
|
+
overflow: hidden;
|
|
209
|
+
min-height: 25vh;
|
|
210
|
+
display: flex;
|
|
211
|
+
flex-direction: column;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/* Coil pattern */
|
|
215
|
+
.coil-row {
|
|
216
|
+
display: block;
|
|
217
|
+
width: 100%;
|
|
218
|
+
height: 60px;
|
|
219
|
+
flex-shrink: 0;
|
|
220
|
+
background: var(--bg);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/* Accent stripe */
|
|
224
|
+
.stripe {
|
|
225
|
+
width: 100%;
|
|
226
|
+
height: 12px;
|
|
227
|
+
background: var(--deco-stripe);
|
|
228
|
+
flex-shrink: 0;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/* Floor with grid */
|
|
232
|
+
.grid-floor {
|
|
233
|
+
flex: 1;
|
|
234
|
+
position: relative;
|
|
235
|
+
background-color: var(--deco);
|
|
236
|
+
min-height: 80px;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
.grid-svg {
|
|
240
|
+
position: absolute;
|
|
241
|
+
inset: 0;
|
|
242
|
+
width: 100%;
|
|
243
|
+
height: 100%;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/* Ticker ribbon */
|
|
247
|
+
.ticker-wrap {
|
|
248
|
+
width: 100%;
|
|
249
|
+
overflow: hidden;
|
|
250
|
+
background: var(--ticker-bg);
|
|
251
|
+
padding: 10px 0;
|
|
252
|
+
flex-shrink: 0;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
.ticker-track {
|
|
256
|
+
display: flex;
|
|
257
|
+
gap: 24px;
|
|
258
|
+
white-space: nowrap;
|
|
259
|
+
animation: ticker-scroll 20s linear infinite;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
.ticker-unit {
|
|
263
|
+
font-family: var(--font-body);
|
|
264
|
+
font-size: 11px;
|
|
265
|
+
font-weight: 600;
|
|
266
|
+
letter-spacing: 0.12em;
|
|
267
|
+
text-transform: uppercase;
|
|
268
|
+
color: var(--ticker-fg);
|
|
269
|
+
flex-shrink: 0;
|
|
270
|
+
display: flex;
|
|
271
|
+
align-items: center;
|
|
272
|
+
gap: 12px;
|
|
273
|
+
margin-right: -12px;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
.ticker-star {
|
|
277
|
+
color: var(--deco-stripe);
|
|
278
|
+
margin-right: 4px;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
@keyframes ticker-scroll {
|
|
282
|
+
from { transform: translateX(0); }
|
|
283
|
+
to { transform: translateX(-50%); }
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
@media (prefers-reduced-motion: reduce) {
|
|
287
|
+
.ticker-track {
|
|
288
|
+
animation: none;
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
/* Responsive */
|
|
293
|
+
@media (max-width: 600px) {
|
|
294
|
+
.logo {
|
|
295
|
+
left: 16px;
|
|
296
|
+
top: 16px;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
.logo-container {
|
|
300
|
+
width: 40px;
|
|
301
|
+
height: 40px;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
.logo-name {
|
|
305
|
+
font-size: 13px;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
.upper {
|
|
309
|
+
padding: 50px 20px 30px;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
.headline {
|
|
313
|
+
margin: 0 auto 12px;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
.subtext {
|
|
317
|
+
margin-bottom: 30px;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
.form-row {
|
|
321
|
+
flex-direction: column;
|
|
322
|
+
align-items: stretch;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
.form-row input,
|
|
326
|
+
.form-row button {
|
|
327
|
+
width: 100%;
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
</style>
|
|
331
|
+
</head>
|
|
332
|
+
<body>
|
|
333
|
+
|
|
334
|
+
<!-- Logo placeholder — replace or customize -->
|
|
335
|
+
<div class="logo">
|
|
336
|
+
<div class="logo-container" data-od-id="logo-mark">
|
|
337
|
+
{{LOGO_MARK}}
|
|
338
|
+
</div>
|
|
339
|
+
<div class="logo-text">
|
|
340
|
+
<span class="logo-name" data-od-id="logo-name">{{PRODUCT_NAME}}</span>
|
|
341
|
+
<span class="logo-dot">·</span>
|
|
342
|
+
</div>
|
|
343
|
+
</div>
|
|
344
|
+
|
|
345
|
+
<!-- Hero section -->
|
|
346
|
+
<section class="upper">
|
|
347
|
+
<h1 class="headline" data-od-id="headline">{{PRODUCT_NAME}}<br>is launching</h1>
|
|
348
|
+
<p class="subtext" data-od-id="tagline">{{TAGLINE}}</p>
|
|
349
|
+
|
|
350
|
+
<form class="form-row" id="waitlist-form" data-od-id="form">
|
|
351
|
+
<input type="text" name="firstname" placeholder="First name">
|
|
352
|
+
<input type="email" name="email" placeholder="Work email" required>
|
|
353
|
+
<button type="submit">Join Waitlist</button>
|
|
354
|
+
</form>
|
|
355
|
+
<p class="success-msg" id="success-msg" role="status">You're on the list. We'll be in touch.</p>
|
|
356
|
+
</section>
|
|
357
|
+
|
|
358
|
+
<!-- Decorative section -->
|
|
359
|
+
<div class="deco-section">
|
|
360
|
+
|
|
361
|
+
<!-- Coil pattern (optional) -->
|
|
362
|
+
<svg class="coil-row" viewBox="0 0 1440 60" preserveAspectRatio="none" xmlns="http://www.w3.org/2000/svg" data-od-id="coil">
|
|
363
|
+
<g fill="none" style="stroke: var(--deco-stroke);" stroke-width="1.5">
|
|
364
|
+
<path d="M 9,60 A 41,30 0 0 1 91,60"/>
|
|
365
|
+
<path d="M 94,60 A 41,30 0 0 1 176,60"/>
|
|
366
|
+
<path d="M 179,60 A 41,30 0 0 1 261,60"/>
|
|
367
|
+
<path d="M 264,60 A 41,30 0 0 1 346,60"/>
|
|
368
|
+
<path d="M 349,60 A 41,30 0 0 1 431,60"/>
|
|
369
|
+
<path d="M 434,60 A 41,30 0 0 1 516,60"/>
|
|
370
|
+
<path d="M 519,60 A 41,30 0 0 1 601,60"/>
|
|
371
|
+
<path d="M 604,60 A 41,30 0 0 1 686,60"/>
|
|
372
|
+
<path d="M 689,60 A 41,30 0 0 1 771,60"/>
|
|
373
|
+
<path d="M 774,60 A 41,30 0 0 1 856,60"/>
|
|
374
|
+
<path d="M 859,60 A 41,30 0 0 1 941,60"/>
|
|
375
|
+
<path d="M 944,60 A 41,30 0 0 1 1026,60"/>
|
|
376
|
+
<path d="M 1029,60 A 41,30 0 0 1 1111,60"/>
|
|
377
|
+
<path d="M 1114,60 A 41,30 0 0 1 1196,60"/>
|
|
378
|
+
<path d="M 1199,60 A 41,30 0 0 1 1281,60"/>
|
|
379
|
+
<path d="M 1284,60 A 41,30 0 0 1 1366,60"/>
|
|
380
|
+
<path d="M 1369,60 A 41,30 0 0 1 1451,60"/>
|
|
381
|
+
</g>
|
|
382
|
+
</svg>
|
|
383
|
+
|
|
384
|
+
<!-- Accent stripe -->
|
|
385
|
+
<div class="stripe" data-od-id="stripe"></div>
|
|
386
|
+
|
|
387
|
+
<!-- Floor with perspective grid -->
|
|
388
|
+
<div class="grid-floor">
|
|
389
|
+
<svg class="grid-svg" viewBox="0 0 1280 400" preserveAspectRatio="none" xmlns="http://www.w3.org/2000/svg" data-od-id="grid">
|
|
390
|
+
<g style="stroke: var(--deco-stroke);" stroke-width="1" fill="none">
|
|
391
|
+
<!-- Radiating lines from center top -->
|
|
392
|
+
<line x1="640" y1="0" x2="0" y2="400"/>
|
|
393
|
+
<line x1="640" y1="0" x2="128" y2="400"/>
|
|
394
|
+
<line x1="640" y1="0" x2="256" y2="400"/>
|
|
395
|
+
<line x1="640" y1="0" x2="384" y2="400"/>
|
|
396
|
+
<line x1="640" y1="0" x2="512" y2="400"/>
|
|
397
|
+
<line x1="640" y1="0" x2="640" y2="400"/>
|
|
398
|
+
<line x1="640" y1="0" x2="768" y2="400"/>
|
|
399
|
+
<line x1="640" y1="0" x2="896" y2="400"/>
|
|
400
|
+
<line x1="640" y1="0" x2="1024" y2="400"/>
|
|
401
|
+
<line x1="640" y1="0" x2="1152" y2="400"/>
|
|
402
|
+
<line x1="640" y1="0" x2="1280" y2="400"/>
|
|
403
|
+
<!-- Horizontal grid lines -->
|
|
404
|
+
<line x1="0" y1="80" x2="1280" y2="80"/>
|
|
405
|
+
<line x1="0" y1="160" x2="1280" y2="160"/>
|
|
406
|
+
<line x1="0" y1="240" x2="1280" y2="240"/>
|
|
407
|
+
<line x1="0" y1="320" x2="1280" y2="320"/>
|
|
408
|
+
</g>
|
|
409
|
+
</svg>
|
|
410
|
+
</div>
|
|
411
|
+
</div>
|
|
412
|
+
|
|
413
|
+
<!-- Ticker ribbon -->
|
|
414
|
+
<div class="ticker-wrap" aria-hidden="true" data-od-id="ticker">
|
|
415
|
+
<div class="ticker-track">
|
|
416
|
+
<span class="ticker-unit">COMING SOON <span class="ticker-star">✦</span></span>
|
|
417
|
+
<span class="ticker-unit">COMING SOON <span class="ticker-star">✦</span></span>
|
|
418
|
+
<span class="ticker-unit">COMING SOON <span class="ticker-star">✦</span></span>
|
|
419
|
+
<span class="ticker-unit">COMING SOON <span class="ticker-star">✦</span></span>
|
|
420
|
+
<span class="ticker-unit">COMING SOON <span class="ticker-star">✦</span></span>
|
|
421
|
+
<span class="ticker-unit">COMING SOON <span class="ticker-star">✦</span></span>
|
|
422
|
+
<span class="ticker-unit">COMING SOON <span class="ticker-star">✦</span></span>
|
|
423
|
+
<span class="ticker-unit">COMING SOON <span class="ticker-star">✦</span></span>
|
|
424
|
+
<span class="ticker-unit">COMING SOON <span class="ticker-star">✦</span></span>
|
|
425
|
+
<span class="ticker-unit">COMING SOON <span class="ticker-star">✦</span></span>
|
|
426
|
+
<span class="ticker-unit">COMING SOON <span class="ticker-star">✦</span></span>
|
|
427
|
+
<span class="ticker-unit">COMING SOON <span class="ticker-star">✦</span></span>
|
|
428
|
+
<!-- Duplicate for seamless loop -->
|
|
429
|
+
<span class="ticker-unit">COMING SOON <span class="ticker-star">✦</span></span>
|
|
430
|
+
<span class="ticker-unit">COMING SOON <span class="ticker-star">✦</span></span>
|
|
431
|
+
<span class="ticker-unit">COMING SOON <span class="ticker-star">✦</span></span>
|
|
432
|
+
<span class="ticker-unit">COMING SOON <span class="ticker-star">✦</span></span>
|
|
433
|
+
<span class="ticker-unit">COMING SOON <span class="ticker-star">✦</span></span>
|
|
434
|
+
<span class="ticker-unit">COMING SOON <span class="ticker-star">✦</span></span>
|
|
435
|
+
<span class="ticker-unit">COMING SOON <span class="ticker-star">✦</span></span>
|
|
436
|
+
<span class="ticker-unit">COMING SOON <span class="ticker-star">✦</span></span>
|
|
437
|
+
<span class="ticker-unit">COMING SOON <span class="ticker-star">✦</span></span>
|
|
438
|
+
<span class="ticker-unit">COMING SOON <span class="ticker-star">✦</span></span>
|
|
439
|
+
<span class="ticker-unit">COMING SOON <span class="ticker-star">✦</span></span>
|
|
440
|
+
<span class="ticker-unit">COMING SOON <span class="ticker-star">✦</span></span>
|
|
441
|
+
</div>
|
|
442
|
+
</div>
|
|
443
|
+
|
|
444
|
+
<script>
|
|
445
|
+
document.getElementById('waitlist-form').addEventListener('submit', function(e) {
|
|
446
|
+
e.preventDefault();
|
|
447
|
+
if (!this.checkValidity()) {
|
|
448
|
+
this.reportValidity();
|
|
449
|
+
return;
|
|
450
|
+
}
|
|
451
|
+
this.style.display = 'none';
|
|
452
|
+
document.getElementById('success-msg').classList.add('visible');
|
|
453
|
+
});
|
|
454
|
+
</script>
|
|
455
|
+
|
|
456
|
+
</body>
|
|
457
|
+
</html>
|