imitation
← all crawls

AI Marketing Platform for Website Growth | Ploy

https://ploy.ai/

Ploy is an AI marketing platform for website growth. Build on-brand pages, identify visitors, automate SEO and outreach, and improve conversion.

ploy.ai screenshot

Visuals

Colors

Primary
#fffa64
Background
#ffffff
Surface
#212121
Text Primary
#212121
Text Secondary
#ffffff

Typography

Aa
Display
FK Screamer
144px / w700
Aa
Heading
FK Screamer
144px / w700
Aa
Body
Geist
16px / w400
Aa
Label
Geist
14px / w600

Spacing

Xs
6px
Sm
8px
Md
12px
Lg
16px
Xl
24px

Radius

Sm
4px
Md
12px
Lg
16px
Pill
9999px

Shadows

Sm
Md
Lg

Mode

Light mode
Background
Sample text on canvas
Bg#ffffff
Text#212121
Muted#ffffff

Motion

Levelexpressive
Duration200ms
Easingcubic-bezier(0.4, 0, 0.2, 1)

Stack

Frameworktailwind
Iconslucide

Full Palette · 29 tokens

Brand
accent-blue
#e7f1ff
accent-lime
#d1f864
accent-pink
#ffb8fc
primary
#fffa64
primary-active
#f5e650
primary-hover
#fff08a
Surface
canvas
#ffffff
surface-elevated
#f4f4f4
surface-muted
#e3e3e3
surface-soft
#212121
surface-strong
#212121
Text
body
#ffffff
ink
#212121
muted
oklab(0.247756 0.0000112578 0.00000497699 / 0.78)
muted-medium
oklab(0.247756 0.0000112578 0.00000497699 / 0.72)
muted-strong
oklab(0.247756 0.0000112578 0.00000497699 / 0.55)
on-dark
#ffffff
on-primary
#212121
on-surface
#212121
Hairlines & Borders
border-inset
oklab(0.999994 0.0000455677 0.0000200868 / 0.8)
border-light
oklab(0.999994 0.0000455678 0.0000200868 / 0.45)
hairline
rgba(33, 33, 33, 0.08)
hairline-strong
rgba(33, 33, 33, 0.12)
Semantic
error
#ff5252
link
#212121
link-hover
#000000
success
#00c853
warning
#ffd740
Other
body-on-dark
#ffffff

Typography Roles · 10

body-md
Aa
16px / w400
body-sm
Aa
15px / w400
button
Aa
15px / w500
caption
Aa
13px / w400
display-lg
Aa
144px / w700
display-xl
Aa
144px / w700
label-md
Aa
14px / w600
nav-link
Aa
15px / w400
title-md
Aa
144px / w700
title-sm
Aa
18.4px / w600

Components · 26

announcement-card
backgroundColor{colors.canvas}
padding{spacing.lg}
rounded{rounded.md}
textColor{colors.ink}
typography{typography.body-sm}
announcement-label
backgroundColortransparent
textColor{colors.muted-strong}
typography{typography.label-md}
announcement-text
backgroundColortransparent
textColor{colors.ink}
typography{typography.body-sm}
badge
backgroundColor{colors.accent-blue}
padding4px 12px
rounded{rounded.pill}
textColor{colors.ink}
typography{typography.label-md}
button-ghost
backgroundColortransparent
padding10px 20px
rounded{rounded.pill}
textColor{colors.ink}
typography{typography.button}
button-ghost-dark
backgroundColortransparent
padding10px 20px
rounded{rounded.pill}
textColor{colors.body}
typography{typography.button}
button-primary
backgroundColor{colors.primary}
padding12px 24px
rounded{rounded.pill}
textColor{colors.on-primary}
typography{typography.button}
button-primary-active
backgroundColor{colors.primary-active}
rounded{rounded.pill}
textColor{colors.on-primary}
button-primary-hover
backgroundColor{colors.primary-hover}
rounded{rounded.pill}
textColor{colors.on-primary}
button-secondary
backgroundColor{colors.surface-soft}
padding12px 24px
rounded{rounded.pill}
textColor{colors.body}
typography{typography.button}
button-secondary-hover
backgroundColor#2a2a2a
rounded{rounded.pill}
textColor{colors.body}
card-dark
backgroundColor{colors.surface-soft}
padding{spacing.xl}
rounded{rounded.lg}
textColor{colors.body}
typography{typography.body-md}
card-surface
backgroundColor{colors.canvas}
padding{spacing.xl}
rounded{rounded.lg}
textColor{colors.ink}
typography{typography.body-md}
divider
backgroundColor{colors.hairline}
height1px
hero-band
backgroundColor{colors.surface-soft}
padding{spacing.hero} {spacing.container-x}
textColor{colors.body}
typography{typography.display-xl}
hero-cta-group
backgroundColortransparent
padding{spacing.xl} 0 0 0
textColor{colors.body}
hero-headline
backgroundColortransparent
textColor{colors.body}
typography{typography.display-xl}
hero-subhead
backgroundColortransparent
textColor{colors.body}
typography{typography.body-md}
icon-button
backgroundColortransparent
size40px
textColor{colors.ink}
nav-link
backgroundColortransparent
padding8px 12px
textColor{colors.ink}
typography{typography.nav-link}
nav-link-active
backgroundColortransparent
textColor{colors.ink}
typography{typography.nav-link}
nav-popover-category
backgroundColortransparent
padding12px
rounded{rounded.lg}
textColor{colors.muted}
typography{typography.title-sm}
nav-popover-category-hover
backgroundColor{colors.surface-elevated}
rounded{rounded.lg}
textColor{colors.ink}
typography{typography.title-sm}
text-input
backgroundColortransparent
height40px
padding8px 16px
rounded{rounded.DEFAULT}
textColor{colors.ink}
typography{typography.body-sm}
text-input-focus
backgroundColortransparent
rounded{rounded.DEFAULT}
textColor{colors.ink}
top-nav
backgroundColor{colors.canvas}
height64px
textColor{colors.ink}
typography{typography.nav-link}

design.md

Ploy Design System

Overview

Ploy's visual identity is built on deliberate extremity: a near-black hero surface ({colors.surface-soft}) that swallows the viewport, overlaid with 144px white display type in the proprietary FK Screamer face — tight, heavy, and deliberately aggressive. The brand voltage lives almost exclusively in {colors.primary}, an electric yellow (#fffa64) that appears on the primary CTA and nowhere else on the fold, creating a single focal point of impossible brightness against the darkness.

The system operates in a dual-mode dialect: the navigation and announcement card live in a clean white ({canvas}) universe with {ink} text, while the hero band and any signature content areas flip to dark-mode semantics despite the overall page being classified as light-theme. This isn't a toggle — it's a compositional strategy where dark bands are used editorially to create visual punctuation.

The shape language is aggressively rounded: every interactive element from buttons to cards to popover categories uses either {rounded.pill} (9999px) or {rounded.lg} (16px). Sharp corners are reserved for structural containers only. Typography hierarchy is achieved through grotesque scale differences rather than weight modulation — body copy stays at a restrained 400 weight while headlines scream at 700 weight and 144px size.

The overall mood is confident bordering on confrontational: "Your website should be working harder than you are" isn't a suggestion, it's a manifesto delivered in type so large it forces line-breaking mid-word. This is a system that trusts scale, contrast, and whitespace (or its absence) over decoration.

Key Characteristics

  • Hero uses {colors.surface-soft} background with {colors.body} white text at {typography.display-xl} (144px FK Screamer)
  • Single brand color {colors.primary} (#fffa64) reserved for primary CTAs only — no gradients, no tints in UI chrome
  • All buttons use {rounded.pill} geometry; cards and popovers use {rounded.lg}
  • Body text in Geist at 16px/400 weight with -0.32px letter-spacing for optical tightening
  • Navigation lives on {colors.canvas} with {colors.ink} text; dark sections are compositional, not thematic
  • Announcement card ({component.announcement-card}) floats as a white bubble overlapping the hero boundary
  • Shadow system is minimal: {extensions.shadows.sm} for elevation, inset borders for focus states
  • Motion is expressive with 700ms transform durations using spring-like easing

Colors

Brand & Accent

  • Primary ({colors.primary} — #fffa64): Electric yellow used exclusively on {component.button-primary} and brand moments. Never used as background for large areas or text highlights.
  • Primary Hover ({colors.primary-hover} — #fff08a): Lightened yellow state for hover interactions on primary buttons.
  • Primary Active ({colors.primary-active} — #f5e650): Deepened yellow for pressed/active states.
  • Accent Pink ({colors.accent-pink} — #ffb8fc): Secondary brand accent observed in decorative contexts and illustration tints.
  • Accent Blue ({colors.accent-blue} — #e7f1ff): Pale blue used for badge backgrounds ({component.badge}) and subtle surface differentiation.
  • Accent Lime ({colors.accent-lime} — #d1f864): Yellow-green variant appearing in illustration accents and secondary CTAs.

Surface

  • Canvas ({colors.canvas} — #ffffff): Pure white baseline for navigation, announcement card, and light-section backgrounds.
  • Surface Soft ({colors.surface-soft} — #212121): Near-black (not pure #000) used for hero band and dark content sections. This is the dominant surface on the fold.
  • Surface Strong ({colors.surface-strong} — #212121): Identical to soft in current implementation; reserved for future elevated-dark contexts.
  • Surface Elevated ({colors.surface-elevated} — #f4f4f4): Light gray used for section alternating backgrounds and subtle card differentiation.
  • Surface Muted ({colors.surface-muted} — #e3e3e3): Medium gray for dividers, disabled surface areas, and border-like backgrounds.

Text & Ink

  • Ink ({colors.ink} — #212121): Near-black text color for all light-surface content. Matches surface-soft exactly.
  • Body ({colors.body} — #ffffff): White text for dark-surface content (hero, dark cards).
  • Body on Dark ({colors.body-on-dark} — #ffffff): Explicit alias for white-on-dark contexts.
  • Muted ({colors.muted} — oklab 78% opacity): Reduced-opacity ink for secondary nav items and popover categories.
  • Muted Medium ({colors.muted-medium} — oklab 72% opacity): Tertiary text, captions, metadata.
  • Muted Strong ({colors.muted-strong} — oklab 55% opacity): Labels, announcements category headers, timestamp text.
  • On Primary ({colors.on-primary} — #212121): Dark text for yellow button backgrounds. Satisfies AA contrast against #fffa64.
  • On Dark ({colors.on-dark} — #ffffff): White text on any dark surface.
  • On Surface ({colors.on-surface} — #212121): Default text color for light surfaces.

Hairlines & Borders

  • Hairline ({colors.hairline} — rgba(33,33,33,0.08)): Subtle dividers, very light borders on inputs and cards.
  • Hairline Strong ({colors.hairline-strong} — rgba(33,33,33,0.12)): Visible but quiet borders for card edges and separators.
  • Border Light ({colors.border-light} — oklab 45% opacity white): Inset border treatment for dark-surface inputs and focused states.
  • Border Inset ({colors.border-inset} — oklab 80% opacity white): Stronger inset border for elevated dark components.

Semantic

  • Link ({colors.link} — #212121): Default link color matches ink on light surfaces.
  • Link Hover ({colors.link-hover} — #000000): Darkened state for link hover.
  • Success ({colors.success} — #00c853): Green for confirmation states (observed in system, not on fold).
  • Error ({colors.error} — #ff5252): Red for validation errors and destructive actions.
  • Warning ({colors.warning} — #ffd740): Amber for caution states.

Typography

Font Family

The system employs two typefaces in a strict role division. FK Screamer is a proprietary display face (served from Ploy's CDN as a variable font supporting weights 100–900) reserved exclusively for headline-level text. It is a bold, condensed sans-serif with maximal presence — the kind of typeface that demands 144px because anything smaller would feel like a compromise. Geist (variable font, weights 100–900) handles everything else: navigation, body copy, buttons, labels, and UI chrome. Geist is Vercel's system font — clean, neutral, and intentionally unremarkable so that FK Screamer can dominate without competition.

A third face, ui-monospace, appears in caption-level technical contexts (file sizes, version numbers, timestamps) at 13px.

For the FK Screamer font-face declaration, the source URL is https://cdn.ploy.ai/d21bf4ad-2458-43ee-9561-54c28ab0e85f/user/4b73ec13-fkscreamer.woff2. Geist is self-hosted at /fonts/geist-variable-latin-ext-1.woff2 and /fonts/geist-variable-latin-1.woff2 with unicode-range splitting.

Hierarchy

| Token | Size | Weight | Line Height | Letter Spacing | Use | |---|---|---|---|---|---| | {typography.display-xl} | 144px | 700 | 123.84px (0.86) | normal | Hero headline (dark background) | | {typography.display-lg} | 144px | 700 | 123.84px (0.86) | normal | Section headlines (light background) | | {typography.title-md} | 144px | 700 | 123.84px (0.86) | normal | Alias for display on dark | | {typography.title-sm} | 18.4px | 600 | 1 (18.4px) | -0.32px | Nav popover categories | | {typography.label-md} | 14px | 600 | 20px (1.43) | normal | Section labels, badges, announcements header | | {typography.body-md} | 16px | 400 | 24px (1.5) | -0.32px | Paragraph text, descriptions | | {typography.body-sm} | 15px | 400 | 1.5 | 0 | Input text, secondary body, card text | | {typography.caption} | 13px | 400 | 1.4 (18.2px) | normal | Monospace technical metadata | | {typography.button} | 15px | 500 | 1 (15px) | 0 | Button labels, CTAs | | {typography.nav-link} | 15px | 400 | 1.5 (22.5px) | 0 | Navigation menu items |

Principles

The typographic system's most striking decision is its refusal to modulate weight for emphasis within body copy. Body text locks at 400 weight regardless of context — if something needs to feel more important, it gets bigger or changes color, never bolder. This creates a calm "reading voice" that makes the FK Screamer headlines feel even more explosive by contrast.

Letter-spacing carries subtle work: body text at 16px gets -0.32px tracking to optically tighten the Geist lowercase for screen readability (a common technique for geometric sans-serifs at larger sizes). Display type uses normal (0) tracking because FK Screamer's letterforms already have tight fitting built into the font data.

The hierarchy relies almost entirely on size deltas. There is no "heading-sm" or "heading-xs" — you go from 144px display directly to 18.4px titles, a 7.8x ratio that ensures headlines cannot be ignored. This isn't a system for nuanced typographic gradation; it's a system for manifestos.

Line heights are tight across the board: 0.86 for display (123.84px on 144px — headlines are meant to touch or overlap), 1.5 for body (standard readable), and 1.0 for buttons and titles where vertical centering in fixed-height containers matters more than readability.

Note on Font Substitutes

FK Screamer is proprietary to Ploy and has no public Google Fonts equivalent. For development without access to the licensed font file, substitute with Bebas Neue (Google Fonts) for a similar bold-condensed display effect, or Oswald for a slightly wider but equally impactful alternative. Define the substitution via CSS variable:

--font-display: 'Bebas Neue', 'FK Screamer', sans-serif;

Geist is available as open-source from Vercel (github.com/vercel/geist-font). If unable to self-host, substitute with Inter (Google Fonts) which shares similar metrics and variable font support:

--font-body: 'Inter', 'Geist', system-ui, sans-serif;

Layout

Spacing System

  • Base unit: 4px (with 2px sub-steps at xxs level).

  • Tokens:

    • {spacing.xxs} (4px) — icon internal padding, tight gaps
    • {spacing.xs} (6px) — between icon and text in buttons, small internal gaps
    • {spacing.sm} (8px) — stack spacing for small elements, input padding vertical
    • {spacing.md} (12px) — card internal padding compact, nav item padding
    • {spacing.lg} (16px) — standard card padding, grid gaps, form field margins
    • {spacing.xl} (24px) — major element spacing, section-internal padding, hero subhead margin-top
    • {spacing.xx l} (32px) — large component spacing, section gaps
    • {spacing.section} (48px) — vertical rhythm between major page sections
    • {spacing.hero} (86px) — hero band vertical padding (asymmetric: 86px top, 64px bottom per container data)
    • {spacing.container-x} (32px) — horizontal gutter for content edge-to-viewport
  • Section padding (vertical): {spacing.hero} (86px) on hero; {spacing.section} (48px) on standard sections.

  • Card internal padding: {spacing.xl} (24px) for {component.announcement-card} and {component.card-surface}.

  • Gutters: Content max-width is 1440px centered with {spacing.container-x} (32px) horizontal padding on each side at desktop.

Grid & Container

Max content width is 1440px. The layout uses a single-column centered container approach for the hero (text block centered or left-aligned within the 1440px constraint). The hero itself is full-viewport-width with the dark background bleeding edge-to-edge, while text content respects the 1440px inner bound plus 32px gutters.

No multi-column grid is visible on the fold — this is a hero-dominated landing page where the headline occupies the full content width. The CTA group stacks horizontally (primary + secondary button side by side) with gap inferred from spacing tokens.

Whitespace Philosophy

Whitespace is weaponized asymmetrically. The hero band gives 86px of breathing room above the headline but the headline itself is set with 0.86 line-height — letters nearly touching. This tension between generous container padding and tight typesetting creates a "compressed explosion" effect. The announcement card floats with significant clear space around it, positioned as a discrete element overlapping the hero-to-content transition zone.

Header Architecture

|<- 1440px max ->|
+------------------+------------------------------------------+--------------------+
| [ploy]           | Product  Resources  Solutions  Company   | [Log in] [Start Free]|
|  (logo)          | Pricing  The Ploy Effect                 |  (ghost) (primary)  |
+------------------+------------------------------------------+--------------------+
|<-- 32px -->|                                          |-- 32px -->|

Navigation sits on {colors.canvas} with 64px height. Logo flush-left, navigation items center-spaced, utility actions flush-right. The "Start Free" button in nav uses {component.button-secondary} styling (dark background, white text, pill shape).

Hero Section

|<- full viewport width ->|
+----------------------------------------------------------+
|                                                          |
|              [Photographic/Illustration Background]       |
|              (3D rendered objects, dark moody)            |
|                                                          |
|                    YOUR WEBSITE SHOULD BE                 |
|                  WORKING HARDER THAN YOU ARE              |
|                                                          |
|         Ploy is the marketing platform that turns        |
|          your website into your company's growth         |
|                        engine.                           |
|                                                          |
|              [Start Free]  [Get Demo]                     |
|                                                          |
|  +--------------------------------------------+          |
|  | [img] ANNOUNCEMENT                        |          |
|  | Ploy raises $27M seed to build the  [->]  |          |
|  | first website platform that grows your    |          |
|  | business.                                 |          |
|  +--------------------------------------------+          |
|                                                          |
+----------------------------------------------------------+
|<-------- 32px ------>|

Hero uses {component.hero-band} with {colors.surface-soft} background. Headline in {typography.display-xl} (FK Screamer, 144px, white). Subhead in {typography.body-md} (Geist, 16px, white) with {spacing.xl} margin-top. CTA group contains {component.button-primary} ("Start Free") and {component.button-ghost-dark} ("Get Demo") side by side.

The {component.announcement-card} is absolutely positioned or negatively margined to overlap the bottom-left area of the hero, breaking out of the centered content column to create visual interest at the fold boundary.

Elevation & Depth

| Level | Treatment | Use | |---|---|---| | 0 (Flat) | No shadow, no border | Base surfaces: canvas, hero band, nav background | | 1 (Hairline) | {colors.hairline} 1px border or {extensions.shadows.sm} | Cards on light surfaces, input fields, announcement card | | 2 (Soft drop) | {extensions.shadows.sm} — rgba(33,33,33,0.08) 0 2px 8px | Dropdowns, floating elements, popover containers | | 3 (Elevated) | {extensions.shadows.md} — deep shadow with inset border | Modals, mega-menu popovers, demo forms | | 4 (Decorative) | N/A (photographic depth) | Hero background imagery provides depth layer |

The elevation philosophy is flat-first with targeted depth. Most elements sit flat against their background — the hero has no shadow, the nav has no border. Elevation is reserved for elements that need to visually "lift" from the surface: the announcement card has a subtle shadow to separate it from the hero background, and any dropdown/popover would use the stronger shadow stack.

Inset borders ({extensions.shadows.inset-border} and {extensions.shadows.inset-white}) replace outset borders for focus rings and pressed states, creating a "cut into the surface" feeling rather than "floating above." This is particularly evident in the extracted shadow data where multiple variants use inset box-shadows with varying opacities of white or near-white.

Decorative Depth

The hero background provides the primary decorative depth: a photographic/3D-rendered scene featuring glossy objects (appears to be balloon-like forms in green, yellow, and metallic blue-gray) with dramatic lighting and shallow depth of field. This imagery sits behind the white headline text, with the dark background ensuring readability without overlays. The imagery style is product-photography-meets-contemporary-3D: clean, slightly surreal, high-gloss surfaces with studio lighting.

Shapes

Border Radius Scale

| Token | Value | Use | |---|---|---| | {rounded.sm} | 4px | Small internal elements, tags, minor corners | | {rounded.DEFAULT} | 8px | Input fields, default card radius fallback | | {rounded.md} | 12px | Announcement card, medium cards | | {rounded.lg} | 16px | Nav popover categories, feature cards, major containers | | {rounded.xl} | 44px | Extra-rounded cards, special containers | | {rounded.pill} | 9999px | All buttons (primary, secondary, ghost), badges |

The shape vocabulary is dominated by extreme rounding. The 9999px value (appearing as 3.35544e+07px in extracted CSS due to browser precision) is used for every button variant, creating a consistent pill language across all CTAs. The 16px radius for cards and popovers feels friendly without being cartoonish. Sharp corners (0px or 4px) are structurally necessary only for the main layout container and icon buttons.

Photography Geometry

The hero imagery appears to be full-bleed behind the text content, with no border-radius applied (it fills the hero band edge-to-edge). The announcement card contains a small square/circular avatar image (founder or executive photo) with {rounded.sm} or slight rounding. No other photography is visible on the fold.

Components

No hover states documented. Hover behavior is unreliable to extract from a single screenshot. State variants live as separate entries in the components: frontmatter (e.g. {component.button-primary-hover}, {component.text-input-focus}); add hover styling at implementation time per your stack's conventions.

Buttons

{component.button-primary} — The principal call-to-action with {colors.primary} yellow background and {colors.on-primary} dark text. Uses {typography.button} (15px/500), {rounded.pill} geometry, and {padding: "12px 24px"}. Appears in the hero CTA group as "Start Free" and in the navigation bar as the rightmost action. Hover state is {component.button-primary-hover} (lighter yellow); active/pressed is {component.button-primary-active} (deeper gold). No visible border or shadow — the color itself provides affordance.

{component.button-secondary} — Dark-filled button with {colors.surface-soft} background and {colors.body} white text. Same geometry and typography as primary. Used for "Log in" ghost-adjacent action in nav (rendered as dark pill) and as the primary inverse action in dark contexts. Hover variant {component.button-secondary-hover} lightens the background slightly to #2a2a2a.

{component.button-ghost} — Transparent-background button with {colors.ink} text for light-surface contexts. Used for navigation links that function as actions. Has {rounded.pill} shape to maintain button language even without fill.

{component.button-ghost-dark} — Transparent-background button with {colors.body} white text for dark-surface contexts. Used for "Get Demo" in the hero — the secondary CTA that declines to compete with the primary yellow button. May have a subtle border in practice (not fully extractable); reference {colors.hairline-strong} for a 12% opacity border if needed.

{component.icon-button} — Square 40×40px transparent button for icon-only actions (hamburger menu, close, settings trigger). Uses {colors.ink} icon color. No visible rounding in extracted data (0px), which is unusual for this system — may inherit {rounded.DEFAULT} or remain sharp for icon clarity.

Cards & Containers

{component.announcement-card} — A white ({colors.canvas}) floating card positioned at the hero bottom edge overlap zone. Contains an avatar image (left), "ANNOUNCEMENT" label in {typography.label-md} with {colors.muted-strong}, body text in {typography.body-sm} describing the $27M seed round, and a right-arrow link indicator. Rounded {rounded.md} (12px), padded {spacing.lg} (24px), with {extensions.shadows.sm} elevation to lift it from the hero background. This is one of the few components using a shadow rather than flat positioning.

{component.card-surface} — Standard light-surface card with white background, {colors.ink} text, {typography.body-md}, {rounded.lg} (16px), and {spacing.xl} (24px) internal padding. Template for feature cards, testimonial cards, and content blocks below the fold.

{component.card-dark} — Dark-surface variant matching {colors.surface-soft} background with {colors.body} white text. Same geometry as card-surface. Used for content that lives within or adjacent to hero/dark bands.

{component.badge} — Small inline label with {colors.accent-blue} pale blue background, {colors.ink} text, {typography.label-md}, and {rounded.pill} shape. Padding approximately 4px 12px. Used for categorization tags ("ANNOUNCEMENT", "NEW", etc.).

{component.divider} — 1px horizontal rule with {colors.hairline} color. Full-width within parent container. Used between nav and content, or between sections.

Inputs & Forms

{component.text-input} — Standard text/email input with transparent background, {colors.ink} text at {typography.body-sm} (15px), {rounded.DEFAULT} (8px), and padding 8px 16px. Height is 40px. Border is 0px in default state — the field relies on container background or underline for visibility. Focus state {component.text-input-focus} maintains transparency and would gain an inset border or ring per the shadow system ({extensions.shadows.inset-border-light} or similar).

Navigation

{component.top-nav} — Fixed or sticky top navigation bar, 64px height, {colors.canvas} background spanning full viewport width. Contains logo (left), navigation links (center), and utility actions (right). Text uses {typography.nav-link} at 15px/400 weight in {colors.ink}. No visible border-bottom (or extremely subtle {colors.hairline}).

{component.nav-link} — Individual navigation menu item with transparent background, {colors.ink} text, and 8px 12px padding. Typography is {typography.nav-link}. On the visible fold: Product, Resources, Solutions, Company, Pricing, The Ploy Effect.

{component.nav-link-active} — Active or hover state for navigation links. Would typically show a background highlight or underline; exact treatment not visible on static fold but likely uses {colors.surface-elevated} background or bottom-border indication.

{component.nav-popover-category} — Category header within a navigation dropdown/mega-menu. Uses {typography.title-sm} (18.4px/600) in {colors.muted} (78% opacity), with {rounded.lg} (16px) and 12px padding. These are the "Product", "Resources", etc. headers inside an expanded nav panel. Hover state {component.nav-popover-category-hover} adds {colors.surface-elevated} background and full {colors.ink} text color.

Hero & Signature Components

{component.hero-band} — The dominant above-fold section: full-viewport-width, {colors.surface-soft} background, {spacing.hero} (86px) top padding, {spacing.container-x} (32px) horizontal padding. Contains headline, subhead, CTA group, and overlapping announcement card. Typography defaults to {typography.display-xl} for the band's primary text role.

{component.hero-headline} — The "YOUR WEBSITE SHOULD BE WORKING HARDER THAN YOU ARE" text. {typography.display-xl} (FK Screamer, 144px, 700 weight, white color, 123.84px line-height). This is the signature moment of the entire design system — no other element competes at this scale.

{component.hero-subhead} — Supporting paragraph: "Ploy is the marketing platform that turns your website into your company's growth engine." Uses {typography.body-md} (Geist, 16px/400, white, -0.32px tracking). Positioned with {spacing.xl} (24px) margin-top from headline baseline.

{component.hero-cta-group} — Horizontal container holding the primary and secondary CTAs. Padding-top of {spacing.xl} (24px) creates separation from subhead. Contains {component.button-primary} and {component.button-ghost-dark} with inferred gap of {spacing.md} (12px) or {spacing.lg} (16px).

{component.announcement-label} — "ANNOUNCEMENT" text inside the announcement card. {typography.label-md} (14px/600) in {colors.muted-strong} (55% opacity). Uppercase by content convention, not text-transform.

{component.announcement-text} — Body text inside announcement card describing funding news. {typography.body-sm} (15px/400) in {colors.ink}. Contains an inline arrow icon/link indicator on the right.

Do's and Don'ts

Do

  • Do reserve {colors.primary} (#fffa64) exclusively for the primary CTA button — it loses impact if used for icons, borders, or secondary elements.
  • Do use {typography.display-xl} (144px FK Screamer) for hero headlines only — this scale is not intended for section headings below the fold.
  • Do apply {rounded.pill} to every button variant — the pill shape is non-negotiable in this system's interactive language.
  • Do use {colors.surface-soft} (#212121) as the hero background with white text overlay — this dark-band technique is the signature compositional move.
  • Do keep body text at 400 weight even for emphasized passages — use size or color changes instead of bolding.
  • Do apply -0.32px letter-spacing to {typography.body-md} for optical tightening of Geist at 16px.
  • Do use inset borders ({extensions.shadows.inset-border}) for focus states on dark surfaces rather than glow rings.
  • Do position {component.announcement-card} to overlap the hero-to-content transition — this break-out placement is intentional, not a layout error.
  • Do maintain 1440px max-width for text content while allowing hero backgrounds to bleed full-viewport.
  • Do use {typography.caption} (ui-mono, 13px) for technical metadata like labels and timestamps.

Don't

  • Don't apply FK Screamer to anything smaller than 48px — the face is designed for display scale and becomes illegible at body sizes.
  • Don't use {colors.primary} as a text color on any background — it fails contrast unless on near-black, and even then it's reserved for button fills.
  • Don't add gradient overlays to the hero imagery — the design trusts the photograph's native contrast with the dark background.
  • Don't round the announcement card beyond {rounded.md} (12px) — it needs to feel distinct from the softer rounded buttons.
  • Don't add shadows to the hero headline or subhead — type sits flat against the dark surface with no elevation.
  • Don't use {colors.muted} (78% opacity) for body copy — reserve reduced opacity for labels and secondary navigation only.
  • Don't expand the radius vocabulary beyond the seven defined tokens — especially avoid introducing intermediate values like 6px or 10px.
  • Don't center-align the hero headline if the original is left-aligned — the ragged-right edge of "WORKING HARDER THAN YOU ARE" is intentional visual rhythm.
  • Don't apply {typography.title-sm} (18.4px/600) outside of navigation popover categories — it's specifically scoped to that context.
  • Don't use {colors.accent-pink} or {colors.accent-lime} for UI chrome — these are illustration/decorative accents only.

Motion & Animation

Transition Tokens

--transition-base: color 0.2s cubic-bezier(0.4, 0, 0.2, 1), background-color 0.2s cubic-bezier(0.4, 0, 0.2, 1), border-color 0.2s cubic-bezier(0.4, 0, 0.2, 1), outline-color 0.2s cubic-bezier(0.4, 0, 0.2, 1), text-decoration-color 0.2s cubic-bezier(0.4, 0, 0.2, 1), fill 0.2s cubic-bezier(0.4, 0, 0.2, 1), stroke 0.2s cubic-bezier(0.4, 0, 0.2, 1);
--transition-opacity: opacity 0.5s cubic-bezier(0, 0, 0.2, 1);
--transform-expressive: transform 0.7s cubic-bezier(0.34, 1.56, 0.64, 1), color 0.7s;
--svg-fill: fill 0.4s;
--svg-fill-delayed: fill 0.4s 0.15s;
--svg-fill-short-delay: fill 0.4s 0.1s;
--svg-fill-minimal-delay: fill 0.4s 0.05s;

Keyframe Animations

  • homeHeroIntroIn — Hero entrance sequence (likely headline word-by-word or line-by-line reveal with transform/opacity).
  • proof-marquee-scroll — Continuous horizontal scrolling ticker for logos/testimonials (infinite loop, probably CSS-driven).
  • lpVisualBreathe — Subtle scale/opacity pulse on hero illustration elements (the 3D objects appear to "breathe").
  • lpRise — Upward fade-in translation for content blocks entering viewport.
  • navDesktopPopoverIn / navDesktopPopoverOut — Mega-menu reveal/dismiss with scale and opacity.
  • chatConciergeIn / chatConciergeTyping — Chat widget entrance and typing indicator animation.
  • nightShiftDot / nightShiftProgress — Animated progress indicator (possibly a loading or onboarding state).
  • surveyBalloonRise / surveyPop / surveyEmotionIn — Survey/form micro-interactions with spring physics.
  • blogPostProgress — Reading progress bar animation for article pages.
  • lpBar / lpPulseDot / lpDraw / lpCycleStep / lpCycleDot — Illustration-specific orchestrated animations for the hero visual (sequential drawing, pulsing indicators, cycling states).
  • demoFormStepIn — Multi-step form transition between fields/pages.

Interaction Patterns

  • Hero intro choreography: On page load, the hero doesn't appear statically — homeHeroIntroIn animates the headline in, likely with staggered word reveals or a wipe-up effect. The lpVisualBreathe animation runs continuously on the background imagery, giving subtle life to the 3D objects.
  • Scroll-triggered reveals: Content below the fold (features, testimonials, pricing) likely uses lpRise — elements fade up and into view as they enter the viewport, driven by IntersectionObserver.
  • Nav popover expansion: Clicking/hovering "Product" or "Solutions" triggers navDesktopPopoverIn, revealing a categorized mega-menu with nav-popover-category items that highlight on hover.
  • Marquee proof section: A scrolling logo bar or testimonial ticker using proof-marquee-scroll with continuous CSS animation (duplicated content for seamless loop).
  • Button interactions: All buttons use --transition-base (200ms ease-standard) for color/background transitions. The primary button may also have a subtle scale transform on press using --transform-expressive timing.
  • SVG icon animations: Icon fills transition over 400ms with staggered delays (0ms, 50ms, 100ms, 150ms) for icon groups, creating a sequential "color-flow" effect.
  • Chat widget: A concierge/chat bubble system with typing indicator animation (chatConciergeTyping) and smooth entrance (chatConciergeIn).
  • Focus rings: Inputs and interactive elements transition to an inset-border focus state using the base 200ms timing.

Imagery Style

  • Primary technique: 3D rendering / CGI product photography with glossy, reflective surfaces and studio lighting.
  • Subject matter: Abstract/surreal object compositions — the hero shows inflated balloon-like forms in saturated colors (lime green, yellow, metallic blue-gray) arranged in a cluster with shallow depth of field.
  • Color treatment: Objects use brand-adjacent palette (yellows, greens) against a dark moody environment, creating natural contrast with white overlay text.
  • Lighting: Dramatic directional lighting with soft shadows and specular highlights on glossy surfaces — suggests high-end commercial 3D work (Blender/Cinema 4D/Renderman quality).
  • Role in layout: Imagery is background to the typography, not equal partner — the 144px headline sits on top with no gradient scrim, trusting the photo's inherent darkness for readability.
  • Mood: Playful but sophisticated — the objects feel tactile and slightly absurd (balloons? gloves?), reinforcing the "work harder than you are" message with visual wit.
  • Additional imagery types expected below fold: Product screenshots (likely with {rounded.lg} corners and {extensions.shadows.md}), founder/headshot photography (professional, warm lighting), and possibly illustrated diagrams for feature explanations.
  • No illustration style detected on fold — the hero is photographic/3D, not flat illustration or line art.
  • Avatar imagery: Small circular/square founder photo in announcement card, professional headshot style, warm tones.

Icon System

  • Library: Lucide (detected from framework analysis and class naming conventions).
  • Specific icons observed: Arrow-right (external link arrow in announcement card), possible menu/hamburger (inferred for mobile nav), user/account icon (for "Log in" context).
  • Treatment: Stroke-based icons (Lucide's default), colored with currentColor to inherit parent text color ({colors.ink} on light surfaces, {colors.body} on dark).
  • Size: Inferred 16px–20px for inline icons (arrow in announcement), 20px–24px for standalone icon buttons. Exact sizes should align with the 4px spacing grid.
  • Alignment: Icons sit on the same baseline as adjacent text with optical adjustment (typically -1px or -2px nudge for cap-height alignment with Geist's x-height).
  • Stroke width: Likely 1.5px or 2px (Lucide default) — consistent with the system's preference for refined over heavy detail.

Recommended Frontend Stack

- Framework:      Next.js (Astro or React-based, given the Astro-island custom elements in DOM)
- Styling:        Tailwind CSS v3/v4 (confirmed by tailwindLikely: true, utility class patterns)
- Fonts:          Self-hosted FK Screamer (CDN) + Geist (local woff2 variable fonts)
- Animation:      CSS keyframes + Framer Motion (for scroll-triggered reveals and complex orchestration)
- Icons:          Lucide React (@lucide/react)
- Component lib:  Custom (no shadcn/Radix detection — components appear hand-built)
- Build tool:     Vite or Next.js compiler (Astro framework detected via astro-island)

Responsive Behavior

Breakpoints

| Name | Width | Key Changes | |---|---|---| | Mobile | < 640px | Single-column hero, stacked CTAs, hamburger nav, announcement card full-width | | Tablet | 640px – 1024px | Two-column potential, nav collapses to menu, hero headline scales down (72–96px?) | | Desktop | 1024px – 1440px | Full layout as shown: 144px headline, horizontal CTA pair, full nav | | Wide | > 1440px | Content centers in 1440px container; hero background bleeds further |

Note: Exact breakpoint values are inferred from standard Tailwind defaults and the 1440px max-container. The actual site may use custom breakpoints.

Touch Targets

  • {component.button-primary}: Height ~48px (12px padding × 2 + 15px text + buffer) — exceeds WCAG AAA 44×44px minimum ✓
  • {component.button-secondary}: Same dimensions as primary — passes ✓
  • {component.button-ghost-dark}: Same dimensions — passes ✓
  • {component.icon-button}: 40×40px — meets WCAG AA (44×44px recommended, 40×40px acceptable) ⚠
  • {component.text-input}: 40px height — acceptable for touch; width depends on context
  • {component.nav-link}: Touch area expanded via padding (8px 12px) — likely hits 44px minimum with padding inclusion ✓

Collapsing Strategy

  • Navigation: At mobile breakpoint, the horizontal nav links (Product, Resources, Solutions, Company, Pricing, The Ploy Effect) collapse into a hamburger menu triggering {component.nav-popover-category} styled drawer or fullscreen overlay. "Log in" and "Start Free" buttons may remain visible or move into the menu.
  • Hero headline: {typography.display-xl} (144px) scales down dramatically — likely to 48–64px on mobile, potentially 72–96px on tablet. FK Screamer remains the fontface; only the fontSize changes. Line-height may increase proportionally (to 1.0–1.1 ratio) for smaller screens to prevent cramped leading.
  • CTA group: Horizontal pair ({component.button-primary} + {component.button-ghost-dark}) stacks vertically on mobile with {spacing.md} (12px) gap between them. Primary button typically moves to top position.
  • Announcement card: Full-width within mobile container (minus {spacing.container-x} gutters), maintaining {rounded.md} and internal padding. Avatar and text may reflow (avatar above text instead of inline).
  • Container padding: {spacing.container-x} (32px) may reduce to 16–20px on mobile to maximize content area.
  • Hero imagery: Background image may crop to focal point (center or left-aligned depending on composition), or scale down maintaining aspect ratio. The 3D objects cluster should remain visible behind the (now smaller) headline.

Image Behavior

  • Hero illustration: Background-size: cover; background-position: center (or slightly left-of-center to balance the left-weighted text block). On mobile, the image may shift to ensure the most visually interesting objects remain visible behind the shortened headline.
  • Avatar in announcement: Fixed size (likely 40×48px) regardless of viewport, with {rounded.sm} or circular clip. Does not scale proportionally with text.
  • Product screenshots (below fold): Likely responsive images with srcset, max-width 100%, {rounded.lg}, and {extensions.shadows.md}. Would stack vertically on mobile.

Iteration Guide

  1. Set up the font stack first. Define @font-face for FK Screamer (from the CDN URL in the CSS) and Geist (self-hosted or from Vercel's package). Create CSS variables --font-display: 'FK Screamer' and --font-body: 'Geist, sans-serif'. Without these, the entire typographic system is invisible.

  2. Implement the dual-surface theme. Ploy isn't simply "light mode" — it uses {colors.canvas} for nav/announcements AND {colors.surface-soft} for the hero. Set up CSS (or Tailwind config) custom properties for both surface modes and apply them via section-level classes, not a global theme toggle.

  3. Build the hero as a full-viewport section. Use min-height: 100vh (or min-height: 80vh depending on content), {colors.surface-soft} background, and the {typography.display-xl} headline at 144px. Add the homeHeroIntroIn animation (even a simple opacity + translateY fade) — static 144px type looks broken without entrance motion.

  4. Reserve {colors.primary} (#fffa64) religiously. Create a lint rule or team convention that this hex appears ONLY in {component.button-primary} and its hover/active variants. No borders, no text, no icons, no backgrounds except that one button fill.

  5. Apply {rounded.pill} to every <button> and <a> that acts as a CTA. This is non-negotiable in the design language. If you're tempted to use {rounded.lg} or {rounded.DEFAULT} for a button, you're fighting the system.

  6. Use the extracted transition timing for all interactive state changes. The 200ms cubic-bezier(0.4, 0, 0.2, 1) base transition and 700ms cubic-bezier(0.34, 1.56, 0.64, 1) for transforms are brand-consistent timings. Don't default to ease-in-out or 300ms.

  7. Build the announcement card as an overlapping element. It breaks out of the content flow — use negative margin-top or absolute positioning relative to the hero section bottom edge. Give it {extensions.shadows.sm} so it visibly floats above the hero background.

  8. Scale typography responsively using clamp(). For the headline, consider clamp(48px, 10vw, 144px) to gracefully reduce from 144px desktop to ~48px mobile without media query sprawl. Maintain the 0.86 line-height ratio at every size.

  9. Reference tokens, never hardcode values. Every color, spacing, radius, and font-size in your implementation should resolve to a token path from the YAML frontmatter. If you find yourself writing #fffa64 outside the primary button definition, pause and check if {colors.primary} should be used instead.

  10. Run the linter after implementation. Execute npx @google/design.md lint DESIGN.md to catch broken token references ({colors.nonexistent}), orphaned tokens (defined but never referenced), and contrast ratio violations (especially {colors.primary} on {colors.surface-soft} — that combination must never appear as text).

Font Setup for Development:

If you cannot access the licensed FK Screamer WOFF2 from Ploy's CDN, substitute with Bebas Neue from Google Fonts:

@import url('https://fonts.googleapis.com/css2?family=Bebas+Neuw&display=swap');

:root {
  --font-display: 'Bebas Neue', 'FK Screamer', sans-serif;
  --font-body: 'Inter', 'Geist', system-ui, sans-serif;
  --font-mono: 'UI Mono', ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
}

For production, replace with the actual font-face declarations pointing to the self-hosted or CDN-sourced WOFF2 files shown in the extracted CSS.

Known Gaps

  • Hover state styling: Per the no-hover policy, hover appearance for buttons, links, and nav items is documented as separate frontmatter entries but visual specifics (background color shifts, subtle lifts, border appearances) are inferred rather than directly extracted.
  • Loading skeletons / pending states: No skeleton loaders, spinners, or empty states were visible on the landing page fold. These would need to be designed following the {colors.surface-elevated} + pulse animation patterns suggested by lpPulseDot.
  • Form validation states: Only default and focus states for {component.text-input} are defined. Error ({colors.error} border/text), success ({success} checkmark), and disabled (opacity + reduced cursor) states are not represented in the extracted data.
  • Dashboard / authenticated surfaces: The landing page shows only marketing/content surfaces. Any app dashboard, settings panels, or user-profile pages would require extending the design system with table styles, data visualization colors, and denser layouts.
  • Dark mode toggle behavior: While {colors.surface-soft} is used for the hero, this is a compositional dark-band within a light page, not a true dark mode. A system-wide dark mode (inverting canvas to #212121 and ink to #ffffff globally) is not represented and would require new token mappings.
  • Mobile navigation drawer: The collapsed mobile nav structure, animation (navDesktopPopoverIn adapted for slide-over), and touch gesture handling are not visible in the desktop screenshot.
  • Exact pixel values for oklab colors: Several muted text colors use oklab() notation with opacity parameters (e.g., oklab(0.247756 0.0000112578 0.00000497699 / 0.78)). These have been preserved as-is but their sRGB equivalents for older browsers were not computed.
  • Footer component: No footer is visible in the above-fold screenshot. A typical Ploy footer would need to be designed following {colors.surface-elevated} or {colors.canvas} with muted link styling.
  • Illustration animation specifics: The lpVisualBreathe, lpDraw, lpCycleStep, and lpCycleDot keyframes suggest complex orchestrated animation on the hero 3D scene, but the exact property transformations (which objects move, scale, or rotate) cannot be determined from CSS alone — requires inspection of the animation keyframe definitions or JavaScript orchestrator.
  • Sub-brand or enterprise tier theming: No evidence of palette variations for different pricing tiers (Starter vs. Enterprise), geographic regions, or white-label deployments. The {colors.primary} yellow appears universal.