Skip to content

polish(marketing): hero font, pixel-dithered demos, testimonials, CTA#3563

Merged
Kitenite merged 27 commits intomainfrom
muddy-quartz
Apr 18, 2026
Merged

polish(marketing): hero font, pixel-dithered demos, testimonials, CTA#3563
Kitenite merged 27 commits intomainfrom
muddy-quartz

Conversation

@Kitenite
Copy link
Copy Markdown
Collaborator

@Kitenite Kitenite commented Apr 18, 2026

Summary

  • Hero: bigger "Orchestrate 100+ coding agents in parallel" subtitle copy; pills back directly under the mockup (no scroll transform); grid lines removed. AI Agents. styling restored to match main (var(--font-geist-pixel-grid)).
  • Feature demo backgrounds: replaced MeshGradient with @paper-design/shaders-react' <Dithering> shader (shape="warp", type="4x4"), lazy-loaded. Rendered at opacity: 0.3 with mix-blend-screen over each card's palette. Rounded corners removed on the outer cards.
  • Video section removed.
  • Testimonials: added role field (e.g. "Co-founder & CTO at Mastra", "Engineer at Paraflow"); swapped kihonushi for Vlad Arbatov; removed rounded-xl on cards.
  • CTA: "Get Superset Today" → "Try Superset now.", heading now uses the same font/size as the FAQ heading.
  • TypewriterText: optional render(visibleText) per segment so individual segments can render custom glyphs while still typing char-by-char.

Notes / follow-ups

  • Iven's role ("Engineer at Paraflow") is inferred from his GitHub profile (@paraflow-hq, updated 2026-04-02). Swap to Founding Engineer / other title if you know better.

Test plan

  • Hero renders with the same pixel-grid "AI Agents." treatment as main.
  • Demo pills sit under the mockup, no scroll animation.
  • FeatureDemo cards show animated warp-dither backgrounds at each card's brand color; sharp corners; no console errors from lazy load.
  • Testimonials show role/company as subtitle when present, else @handle; circular avatars unchanged.
  • Final CTA reads "Try Superset now." with FAQ-matching typography.
  • No grid lines on the page; no regressions to other sections.

Summary by CodeRabbit

Release Notes

  • New Features

    • Added dithered background visual effects to feature demonstrations.
    • Enabled display of testimonial roles and positions in user testimonials.
  • Bug Fixes

    • Removed scroll-dependent animation from selector pills for improved layout stability.
  • Style

    • Updated CTA messaging and typography ("Get Superset Today" → "Try Superset now").
    • Refined hero section description with clearer agent language.
    • Enhanced button styling with new brand color scheme.
    • Improved hover effects and transitions on interactive tiles and cards.
    • Added visual refinements to testimonial and trusted partner sections.
  • Removed Features

    • Removed video section from home page.
    • Removed OS/platform detection from header CTA.

- Hero title: both segments at weight 500; "AI Agents." uses lo-res-21-ot-serif with Pixelify Sans fallback.
- Subtitle: "Orchestrate 100+ coding agents in parallel" (keep rest).
- Remove vertical grid lines from <main>.
- Move ProductDemo pills back under the mockup (no scroll transform).
- CTA heading matches FAQ styling; copy now "Try Superset now.".
- Add `role` field to Iven's testimonial (Engineer at Paraflow).
- TypewriterText: optional per-segment `render` for custom glyph rendering.
- Simplify DownloadButton classes (unify with buttonClasses).
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 18, 2026

Warning

Rate limit exceeded

@Kitenite has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 2 minutes and 17 seconds before requesting another review.

Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 2 minutes and 17 seconds.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: e701fd04-6ae7-455e-8b85-55cf2e6d28f1

📥 Commits

Reviewing files that changed from the base of the PR and between ed2fa45 and f76e7af.

⛔ Files ignored due to path filters (1)
  • bun.lock is excluded by !**/*.lock
📒 Files selected for processing (10)
  • apps/marketing/package.json
  • apps/marketing/src/app/components/CTAButtons/HeaderCTA.tsx
  • apps/marketing/src/app/components/DownloadButton/DownloadButton.tsx
  • apps/marketing/src/app/components/FeaturesSection/components/FeatureDemo/components/DitheredBackground/DitheredBackground.tsx
  • apps/marketing/src/app/components/HeroSection/HeroSection.tsx
  • apps/marketing/src/app/components/HeroSection/components/ProductDemo/ProductDemo.tsx
  • apps/marketing/src/app/components/TrustedBySection/TrustedBySection.tsx
  • apps/marketing/src/app/components/WallOfLoveSection/constants.ts
  • apps/marketing/src/app/globals.css
  • apps/marketing/src/app/layout.tsx
📝 Walkthrough

Walkthrough

Removed platform-specific download gating and the VideoSection; added a lazily-loaded DitheredBackground shader component, typography and styling adjustments across CTA/Hero/TrustedBy/WallOfLove, extended TypewriterText rendering, added brand CSS variables and Pixelify_Sans font, and updated testimonial data with optional roles.

Changes

Cohort / File(s) Summary
CTA / Waitlist
apps/marketing/src/app/components/CTAButtons/HeaderCTA.tsx, apps/marketing/src/app/components/DownloadButton/DownloadButton.tsx, apps/marketing/src/app/components/CTASection/CTASection.tsx
Removed OS/mobile gating in HeaderCTA (always renders DownloadButton and the waitlist modal flow). Updated DownloadButton visual styling. Adjusted CTA heading text and responsive typography classes.
FeatureDemo — Background
apps/marketing/src/app/components/FeaturesSection/components/FeatureDemo/FeatureDemo.tsx, apps/marketing/src/app/components/FeaturesSection/components/FeatureDemo/components/DitheredBackground/DitheredBackground.tsx, apps/marketing/src/app/components/FeaturesSection/components/FeatureDemo/components/DitheredBackground/index.ts
Replaced MeshGradient with new client DitheredBackground component that lazy-loads Dithering from @paper-design/shaders-react inside Suspense. Removed some rounded classes and added an index re-export.
Hero / Product Demo / Typewriter
apps/marketing/src/app/components/HeroSection/HeroSection.tsx, apps/marketing/src/app/components/HeroSection/components/ProductDemo/ProductDemo.tsx, apps/marketing/src/app/components/HeroSection/components/TypewriterText/TypewriterText.tsx
Added monospace fallback to hero font-family and updated hero copy. Removed scroll-linked vertical motion for selector pills (now static). Added optional render callback to TextSegment for custom segment rendering.
Video section removal
apps/marketing/src/app/components/VideoSection/VideoSection.tsx, apps/marketing/src/app/components/VideoSection/index.ts, apps/marketing/src/app/page.tsx
Deleted VideoSection component, removed its barrel export, and removed its dynamic import/render from the home page.
Wall of Love / Testimonials
apps/marketing/src/app/components/WallOfLoveSection/WallOfLoveSection.tsx, apps/marketing/src/app/components/WallOfLoveSection/constants.ts
Added optional role?: string to Testimonial interface and populated many entries. TestimonialCard now shows role when present (fallback to handle). Removed rounded-xl from card anchor styling; replaced one testimonial entry (id "11").
TrustedBy & Visual Styling
apps/marketing/src/app/components/TrustedBySection/TrustedBySection.tsx, apps/marketing/src/app/globals.css
Adjusted TrustedBySection heading weight and logo hover transitions (switched to transition-all, added border/bg hover). Added :root brand variables, Tailwind theme token mappings, and a default cursor: pointer rule for enabled buttons and role="button" elements.
Layout / Fonts / Dependencies
apps/marketing/src/app/layout.tsx, apps/marketing/package.json
Added Pixelify_Sans via next/font/google and included its variable in root html class. Added dependency @paper-design/shaders-react.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Poem

🐰
I hop through pixels, dithers bright and fair,
Where meshes bowed, now tiny patterns flare.
Pills sit still, the video took flight,
Roles appear in the stories of delight.
Fonts and brand hues twinkle — a rabbit's night!

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title 'polish(marketing): hero font, pixel-dithered demos, testimonials, CTA' clearly summarizes the main changes: hero typography updates, new dithered backgrounds for feature demos, testimonial enhancements, and CTA styling improvements.
Description check ✅ Passed PR description includes all required template sections: summary, type of change context, test plan, and additional notes about implementation details and known follow-ups.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch muddy-quartz

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 18, 2026

🚀 Preview Deployment

🔗 Preview Links

Service Status Link
Neon Database (Neon) View Branch
Fly.io Electric (Fly.io) View App
Vercel API (Vercel) Open Preview
Vercel Web (Vercel) Open Preview
Vercel Marketing (Vercel) Open Preview
Vercel Admin (Vercel) Open Preview
Vercel Docs (Vercel) Open Preview

Preview updates automatically with new commits

Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 1 file (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="apps/marketing/src/app/components/HeroSection/HeroSection.tsx">

<violation number="1">
P2: `AI Agents.` now uses Geist Pixel Grid instead of the Lo-Res/Pixelify font stack, so the Adobe kit follow-up won’t change the hero font as intended.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

Comment thread apps/marketing/src/app/components/HeroSection/HeroSection.tsx
@greptile-apps
Copy link
Copy Markdown

greptile-apps Bot commented Apr 18, 2026

Greptile Summary

This is a marketing-site polish PR covering five areas: hero typography upgrades, a new canvas-based DitheredBackground replacing MeshGradient, removal of the video section, testimonial enrichment with role/company subtitles, and a CTA copy refresh.

Key changes:

  • DitheredBackground: a new \"use client\" canvas component that renders a Bayer-ordered dither across four sorted palette colours at 96×72 px (upscaled with imageRendering: pixelated). Per-card angle and phase variation is derived from an FNV-1a hash of the colour strings.
  • Hero font stack: h1 now includes lo-res-22 (Adobe Fonts, conditionally loaded via ADOBE_FONTS_KIT_ID) ahead of IBM Plex Mono; the "AI Agents." segment targets lo-res-21-ot-serif with Pixelify Sans as the live fallback.
  • TypewriterText: gains an optional render(visibleText) per-segment callback for custom glyph rendering while preserving the character-by-character reveal.
  • Testimonials: role field added to all entries; displayed in preference to the Twitter @handle when present.
  • Product demo pills: scroll-driven useTransform removed; pills now sit in a static div directly below the mockup.
  • Brand tokens: --brand, --brand-light, --brand-dark added to globals.css and surfaced as Tailwind @theme inline vars; used for the re-styled download button in HeaderCTA.

Confidence Score: 5/5

Safe to merge — purely visual/marketing changes with no data-path or auth impact; the two nits in DitheredBackground are non-blocking.

All changes are confined to the marketing site's UI layer. The new DitheredBackground component works correctly for its intended use case (6-digit hex colours from hardcoded constants). The two flagged issues — array-reference instability in the useEffect dependency and the silent NaN in parseHex — are edge cases that don't affect current callers. The Adobe Fonts placeholder is intentional and guarded. No logic regressions, no data exposure, no auth changes.

apps/marketing/src/app/components/FeaturesSection/components/FeatureDemo/components/DitheredBackground/DitheredBackground.tsx — minor useEffect dependency and parseHex robustness nits.

Important Files Changed

Filename Overview
apps/marketing/src/app/components/FeaturesSection/components/FeatureDemo/components/DitheredBackground/DitheredBackground.tsx New canvas-based Bayer-dither background component; works correctly but has two minor nits: array-reference instability in the useEffect dependency and the parseHex helper silently returns NaN for non-6-digit inputs.
apps/marketing/src/app/components/HeroSection/components/TypewriterText/TypewriterText.tsx Adds optional per-segment render prop so individual segments can render custom glyphs while the typewriter reveals characters; implementation is clean and backward-compatible.
apps/marketing/src/app/components/WallOfLoveSection/constants.ts Adds optional role field to all testimonials and swaps one testimonial (kihonushi → Vlad Arbatov); straightforward data update.
apps/marketing/src/app/layout.tsx Adds Pixelify Sans as a font variable and conditionally loads Adobe Fonts via a kit ID constant; kit ID is a placeholder (acknowledged in PR) and the conditional guard prevents any real network request until it is wired up.
apps/marketing/src/app/components/HeroSection/components/ProductDemo/ProductDemo.tsx Removes the scroll-driven pill transform (useTransform + motion.div) and replaces with a static div; simpler and eliminates the framer-motion dependency for this element.
apps/marketing/src/app/globals.css Adds brand color tokens (--brand, --brand-light, --brand-dark) exposed as Tailwind @theme inline vars for the updated CTA button styling.
apps/marketing/src/app/components/VideoSection/VideoSection.tsx File deleted; YouTube embed section removed from the marketing page.
apps/marketing/src/app/components/CTAButtons/HeaderCTA.tsx Download button switches from foreground/background token colours to the new brand colour tokens (bg-brand, border-brand-light).

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[FeatureDemo\ncolors prop] --> B[DitheredBackground]
    B --> C{useEffect\non colors ref}
    C --> D[parseHex x4\nSort by luminance]
    D --> E[FNV-1a hash\nangle + phase]
    E --> F[96x72 pixel loop\nBayer-8 ordered dither]
    F --> G[ctx.putImageData\ncanvas element\nimageRendering: pixelated]

    H[layout.tsx] --> I{ADOBE_FONTS_KIT_ID\nnot YOUR_KIT_ID?}
    I -- Yes --> J[Load typekit CSS\nlo-res-22 / lo-res-21-ot-serif]
    I -- No --> K[Skip Adobe Fonts\nPixelify Sans fallback]
    J & K --> L[HeroSection h1\nlo-res-22 to IBM Plex Mono]
    L --> M[TypewriterText segments\nsegment.render optional]
Loading
Prompt To Fix All With AI
This is a comment left during a code review.
Path: apps/marketing/src/app/components/FeaturesSection/components/FeatureDemo/components/DitheredBackground/DitheredBackground.tsx
Line: 54-98

Comment:
**Array reference causes unnecessary canvas redraws**

`colors` is an array, and React compares `useEffect` dependencies by reference. If any ancestor renders and passes a new inline array literal, the effect re-runs every render — redrawing all 6,912 pixels each time.

The computation is cheap, but a stable comparison is simple to achieve by joining the values into a string:

```suggestion
	useEffect(() => {
		const canvas = ref.current;
		if (!canvas) return;
		const ctx = canvas.getContext("2d");
		if (!ctx) return;

		const palette: Rgb[] = colors
			.map(parseHex)
			.sort((a, b) => luminance(a) - luminance(b));
		const n = palette.length - 1;

		const seed = hashStr(colors.join("|"));
		const angle = ((seed % 360) * Math.PI) / 180;
		const dx = Math.cos(angle);
		const dy = Math.sin(angle);
		const phase = ((seed >>> 8) % 1000) / 100;

		const img = ctx.createImageData(W, H);

		for (let y = 0; y < H; y++) {
			for (let x = 0; x < W; x++) {
				const nx = x / W - 0.5;
				const ny = y / H - 0.5;

				let v = 0.5 + (nx * dx + ny * dy) * 0.45;
				v += Math.sin(nx * 6 + ny * 4 + phase) * 0.07;
				v += Math.sin(nx * 11 - ny * 9 + phase * 2) * 0.04;
				v = Math.max(0, Math.min(1, v));

				const t = (BAYER_8[y % 8]?.[x % 8] ?? 0) / 64;
				const level = Math.max(0, Math.min(n, Math.floor(v * n + t)));
				const pick = palette[level] ?? palette[0]!;

				const i = (y * W + x) * 4;
				img.data[i] = pick[0];
				img.data[i + 1] = pick[1];
				img.data[i + 2] = pick[2];
				img.data[i + 3] = 255;
			}
		}
		ctx.putImageData(img, 0, 0);
		// eslint-disable-next-line react-hooks/exhaustive-deps
	}, [colors.join("|")]);
```

How can I resolve this? If you propose a fix, please make it concise.

---

This is a comment left during a code review.
Path: apps/marketing/src/app/components/FeaturesSection/components/FeatureDemo/components/DitheredBackground/DitheredBackground.tsx
Line: 21-28

Comment:
**`parseHex` silently produces `NaN` for non-6-digit strings**

`slice(4, 6)` returns `""` for a 3-character hex shorthand, and `Number.parseInt("", 16)` returns `NaN`. Canvas `ImageData` coerces `NaN` to `0`, so affected pixels render fully black with no runtime error.

The prop type constrains inputs to 4 strings but does not enforce 6-digit hex. A clarifying comment on `parseHex` would prevent silent breakage if a caller later passes a CSS shorthand or a Tailwind-resolved token string.

How can I resolve this? If you propose a fix, please make it concise.

Reviews (1): Last reviewed commit: "revert hero 'AI Agents.' styling to main..." | Re-trigger Greptile

Comment on lines +54 to +98
useEffect(() => {
const canvas = ref.current;
if (!canvas) return;
const ctx = canvas.getContext("2d");
if (!ctx) return;

const palette: Rgb[] = colors
.map(parseHex)
.sort((a, b) => luminance(a) - luminance(b));
const n = palette.length - 1;

// Per-card variation so each card looks distinct
const seed = hashStr(colors.join("|"));
const angle = ((seed % 360) * Math.PI) / 180;
const dx = Math.cos(angle);
const dy = Math.sin(angle);
const phase = ((seed >>> 8) % 1000) / 100;

const img = ctx.createImageData(W, H);

for (let y = 0; y < H; y++) {
for (let x = 0; x < W; x++) {
const nx = x / W - 0.5;
const ny = y / H - 0.5;

// Gentle directional ramp + low-freq wobble for variety
let v = 0.5 + (nx * dx + ny * dy) * 0.45;
v += Math.sin(nx * 6 + ny * 4 + phase) * 0.07;
v += Math.sin(nx * 11 - ny * 9 + phase * 2) * 0.04;
v = Math.max(0, Math.min(1, v));

// Ordered dither across the full palette (4 levels)
const t = (BAYER_8[y % 8]?.[x % 8] ?? 0) / 64;
const level = Math.max(0, Math.min(n, Math.floor(v * n + t)));
const pick = palette[level] ?? palette[0]!;

const i = (y * W + x) * 4;
img.data[i] = pick[0];
img.data[i + 1] = pick[1];
img.data[i + 2] = pick[2];
img.data[i + 3] = 255;
}
}
ctx.putImageData(img, 0, 0);
}, [colors]);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Array reference causes unnecessary canvas redraws

colors is an array, and React compares useEffect dependencies by reference. If any ancestor renders and passes a new inline array literal, the effect re-runs every render — redrawing all 6,912 pixels each time.

The computation is cheap, but a stable comparison is simple to achieve by joining the values into a string:

Suggested change
useEffect(() => {
const canvas = ref.current;
if (!canvas) return;
const ctx = canvas.getContext("2d");
if (!ctx) return;
const palette: Rgb[] = colors
.map(parseHex)
.sort((a, b) => luminance(a) - luminance(b));
const n = palette.length - 1;
// Per-card variation so each card looks distinct
const seed = hashStr(colors.join("|"));
const angle = ((seed % 360) * Math.PI) / 180;
const dx = Math.cos(angle);
const dy = Math.sin(angle);
const phase = ((seed >>> 8) % 1000) / 100;
const img = ctx.createImageData(W, H);
for (let y = 0; y < H; y++) {
for (let x = 0; x < W; x++) {
const nx = x / W - 0.5;
const ny = y / H - 0.5;
// Gentle directional ramp + low-freq wobble for variety
let v = 0.5 + (nx * dx + ny * dy) * 0.45;
v += Math.sin(nx * 6 + ny * 4 + phase) * 0.07;
v += Math.sin(nx * 11 - ny * 9 + phase * 2) * 0.04;
v = Math.max(0, Math.min(1, v));
// Ordered dither across the full palette (4 levels)
const t = (BAYER_8[y % 8]?.[x % 8] ?? 0) / 64;
const level = Math.max(0, Math.min(n, Math.floor(v * n + t)));
const pick = palette[level] ?? palette[0]!;
const i = (y * W + x) * 4;
img.data[i] = pick[0];
img.data[i + 1] = pick[1];
img.data[i + 2] = pick[2];
img.data[i + 3] = 255;
}
}
ctx.putImageData(img, 0, 0);
}, [colors]);
useEffect(() => {
const canvas = ref.current;
if (!canvas) return;
const ctx = canvas.getContext("2d");
if (!ctx) return;
const palette: Rgb[] = colors
.map(parseHex)
.sort((a, b) => luminance(a) - luminance(b));
const n = palette.length - 1;
const seed = hashStr(colors.join("|"));
const angle = ((seed % 360) * Math.PI) / 180;
const dx = Math.cos(angle);
const dy = Math.sin(angle);
const phase = ((seed >>> 8) % 1000) / 100;
const img = ctx.createImageData(W, H);
for (let y = 0; y < H; y++) {
for (let x = 0; x < W; x++) {
const nx = x / W - 0.5;
const ny = y / H - 0.5;
let v = 0.5 + (nx * dx + ny * dy) * 0.45;
v += Math.sin(nx * 6 + ny * 4 + phase) * 0.07;
v += Math.sin(nx * 11 - ny * 9 + phase * 2) * 0.04;
v = Math.max(0, Math.min(1, v));
const t = (BAYER_8[y % 8]?.[x % 8] ?? 0) / 64;
const level = Math.max(0, Math.min(n, Math.floor(v * n + t)));
const pick = palette[level] ?? palette[0]!;
const i = (y * W + x) * 4;
img.data[i] = pick[0];
img.data[i + 1] = pick[1];
img.data[i + 2] = pick[2];
img.data[i + 3] = 255;
}
}
ctx.putImageData(img, 0, 0);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [colors.join("|")]);
Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/marketing/src/app/components/FeaturesSection/components/FeatureDemo/components/DitheredBackground/DitheredBackground.tsx
Line: 54-98

Comment:
**Array reference causes unnecessary canvas redraws**

`colors` is an array, and React compares `useEffect` dependencies by reference. If any ancestor renders and passes a new inline array literal, the effect re-runs every render — redrawing all 6,912 pixels each time.

The computation is cheap, but a stable comparison is simple to achieve by joining the values into a string:

```suggestion
	useEffect(() => {
		const canvas = ref.current;
		if (!canvas) return;
		const ctx = canvas.getContext("2d");
		if (!ctx) return;

		const palette: Rgb[] = colors
			.map(parseHex)
			.sort((a, b) => luminance(a) - luminance(b));
		const n = palette.length - 1;

		const seed = hashStr(colors.join("|"));
		const angle = ((seed % 360) * Math.PI) / 180;
		const dx = Math.cos(angle);
		const dy = Math.sin(angle);
		const phase = ((seed >>> 8) % 1000) / 100;

		const img = ctx.createImageData(W, H);

		for (let y = 0; y < H; y++) {
			for (let x = 0; x < W; x++) {
				const nx = x / W - 0.5;
				const ny = y / H - 0.5;

				let v = 0.5 + (nx * dx + ny * dy) * 0.45;
				v += Math.sin(nx * 6 + ny * 4 + phase) * 0.07;
				v += Math.sin(nx * 11 - ny * 9 + phase * 2) * 0.04;
				v = Math.max(0, Math.min(1, v));

				const t = (BAYER_8[y % 8]?.[x % 8] ?? 0) / 64;
				const level = Math.max(0, Math.min(n, Math.floor(v * n + t)));
				const pick = palette[level] ?? palette[0]!;

				const i = (y * W + x) * 4;
				img.data[i] = pick[0];
				img.data[i + 1] = pick[1];
				img.data[i + 2] = pick[2];
				img.data[i + 3] = 255;
			}
		}
		ctx.putImageData(img, 0, 0);
		// eslint-disable-next-line react-hooks/exhaustive-deps
	}, [colors.join("|")]);
```

How can I resolve this? If you propose a fix, please make it concise.

Comment on lines +21 to +28
function parseHex(hex: string): Rgb {
const s = hex.replace("#", "");
return [
Number.parseInt(s.slice(0, 2), 16),
Number.parseInt(s.slice(2, 4), 16),
Number.parseInt(s.slice(4, 6), 16),
];
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 parseHex silently produces NaN for non-6-digit strings

slice(4, 6) returns "" for a 3-character hex shorthand, and Number.parseInt("", 16) returns NaN. Canvas ImageData coerces NaN to 0, so affected pixels render fully black with no runtime error.

The prop type constrains inputs to 4 strings but does not enforce 6-digit hex. A clarifying comment on parseHex would prevent silent breakage if a caller later passes a CSS shorthand or a Tailwind-resolved token string.

Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/marketing/src/app/components/FeaturesSection/components/FeatureDemo/components/DitheredBackground/DitheredBackground.tsx
Line: 21-28

Comment:
**`parseHex` silently produces `NaN` for non-6-digit strings**

`slice(4, 6)` returns `""` for a 3-character hex shorthand, and `Number.parseInt("", 16)` returns `NaN`. Canvas `ImageData` coerces `NaN` to `0`, so affected pixels render fully black with no runtime error.

The prop type constrains inputs to 4 strings but does not enforce 6-digit hex. A clarifying comment on `parseHex` would prevent silent breakage if a caller later passes a CSS shorthand or a Tailwind-resolved token string.

How can I resolve this? If you propose a fix, please make it concise.

Replaces the canvas-based Bayer dither with @paper-design/shaders-react's
<Dithering> shader (shape="warp", type="4x4"), lazy-loaded via React.lazy.
Rendered at opacity 30% with mix-blend-screen over each card's palette.
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 16 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="apps/marketing/src/app/components/HeroSection/components/ProductDemo/ProductDemo.tsx">

<violation number="1" location="apps/marketing/src/app/components/HeroSection/components/ProductDemo/ProductDemo.tsx:80">
P2: The pills row removed horizontal overflow handling, which can overflow on mobile because pills are `whitespace-nowrap` + `shrink-0` and include long labels.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
apps/marketing/src/app/components/HeroSection/HeroSection.tsx (1)

29-51: ⚠️ Potential issue | 🟡 Minor

"AI Agents." segment uses wrong font fallback; reconcile version number with layout.tsx documentation.

The "AI Agents." segment on line 44 uses var(--font-geist-pixel-grid) instead of var(--font-pixel) (Pixelify Sans). However, there is a conflict in your suggested fix: the comment in layout.tsx line 53 explicitly states "LoRes 22 OT", yet you suggested changing the family slug to "lo-res-21". This contradicts the documented version. Verify whether the intended version is Lo-Res 21 or 22, then align both the outer <h1> style and the "AI Agents." segment fallback accordingly—either:

  • Keep "lo-res-22" with var(--font-pixel) as the fallback, or
  • Change to "lo-res-21" with var(--font-pixel) as the fallback (if 21 is correct)

Until ADOBE_FONTS_KIT_ID is configured, the segment will render in Geist Pixel Grid instead of the intended fallback.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/marketing/src/app/components/HeroSection/HeroSection.tsx` around lines
29 - 51, The "AI Agents." TypewriterText segment is using the wrong CSS fallback
font and the Lo-Res version is inconsistent with layout.tsx; decide whether the
intended Lo-Res version is "lo-res-22" (as documented) or "lo-res-21" and make
both the outer h1 style and the TypewriterText segment use the same family and
Pixelify fallback: update the h1 fontFamily entry (currently '"lo-res-22",
var(--font-ibm-plex-mono), monospace' or change to '"lo-res-21", ...' per your
decision) and change the TypewriterText segment style from
var(--font-geist-pixel-grid) to var(--font-pixel) so both the outer element and
the "AI Agents." segment reference the same Lo-Res slug and the Pixelify
fallback; ensure layout.tsx documentation and the chosen Lo-Res slug are
reconciled.
🧹 Nitpick comments (2)
apps/marketing/src/app/components/WallOfLoveSection/WallOfLoveSection.tsx (1)

41-57: Consider preserving the handle when a role is shown.

With testimonial.role ?? testimonial.handle, the Twitter/X handle disappears whenever a role is set (which is now nearly every testimonial). Since the card already links to X, losing the handle is probably fine, but some readers use the handle to recognize the author. If intentional per design, ignore; otherwise consider rendering both, e.g. role · @handle`` or showing the handle as a smaller secondary line.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/marketing/src/app/components/WallOfLoveSection/WallOfLoveSection.tsx`
around lines 41 - 57, Replace the single-field fallback expression
testimonial.role ?? testimonial.handle in WallOfLoveSection so the handle isn't
dropped when a role exists; update the render logic in the WallOfLoveSection
component (where Avatar and author are rendered) to display both values (for
example: show testimonial.role followed by a separator and testimonial.handle or
render the handle as a smaller secondary line) while preserving existing link
and styling classes; ensure you reference the same JSX block that currently
contains <span className="text-muted-foreground text-sm">{testimonial.role ??
testimonial.handle}</span> and keep accessibility and layout intact.
apps/marketing/src/app/components/CTAButtons/HeaderCTA.tsx (1)

64-72: Consider unifying download CTA styling with DownloadButton.

DownloadButton.tsx still uses bg-foreground text-background for the same "Download" action, while this macOS link now uses bg-brand text-white border border-brand-light. Users will see two visually distinct primary download CTAs on the marketing site depending on context. If the brand treatment is intentional for the header, consider propagating it to DownloadButton (or vice-versa) so the visual hierarchy stays consistent.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/marketing/src/app/components/CTAButtons/HeaderCTA.tsx` around lines 64 -
72, HeaderCTA renders a macOS download link styled with "bg-brand text-white
border border-brand-light" while DownloadButton.tsx uses "bg-foreground
text-background", causing inconsistent primary CTA visuals; decide on the
correct brand treatment and apply it consistently: update either HeaderCTA's
anchor or DownloadButton component to use the chosen class set (referencing
HeaderCTA, DOWNLOAD_URL_MAC_ARM64, and DownloadButton) so both download CTAs
share the same utility classes and visual hierarchy, and ensure any onClick
tracking (track("download_clicked")) remains wired in the unified component.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@apps/marketing/src/app/components/FeaturesSection/components/FeatureDemo/components/DitheredBackground/DitheredBackground.tsx`:
- Around line 101-108: The canvas in DitheredBackground is currently using
aria-hidden="true" which Biome flags; remove the aria-hidden attribute and mark
the decorative canvas with a presentational role instead (e.g.,
role="presentation" or role="none") on the <canvas> element that uses ref,
width={W}, height={H}, className and style so screen readers ignore it
correctly; after the change run biome check --write --unsafe to format and
ensure lint rules pass.
- Around line 21-27: The parseHex function should validate and normalize
incoming hex strings: accept and normalize 3-digit (`#abc`) to 6-digit (`#aabbcc`),
accept 6-digit (`#aabbcc`), strip a leading "#" and ensure the remaining
characters match /^[0-9a-fA-F]{6}$/, and if not, throw or return a clear
error/default instead of producing NaNs; update parseHex (and reference the Rgb
return type) to perform these checks and normalization before calling
Number.parseInt so malformed inputs (CSS vars, rgb() strings, short hex, or
other strings) are rejected or handled safely.

In `@apps/marketing/src/app/components/WallOfLoveSection/constants.ts`:
- Around line 99-104: The testimonial entry for author "Iven" (handle "@ivenvd")
in the WallOfLoveSection constants has an inferred role "Engineer at Paraflow"
that must be verified; contact Iven or check his current public bio and then
update the role field in the object for author "Iven" (or temporarily
remove/clear the role value and add a TODO comment) so the public-facing
attribution is accurate—locate the object with author: "Iven" / handle:
"@ivenvd" in constants.ts and change the role property accordingly once
confirmed.

In `@apps/marketing/src/app/layout.tsx`:
- Around line 53-55: Replace the hardcoded ADOBE_FONTS_KIT_ID constant with
reading process.env.NEXT_PUBLIC_ADOBE_FONTS_KIT_ID (keep a sensible
no-op/fallback when unset) and update any conditional checks that rely on
ADOBE_FONTS_KIT_ID to treat an empty/unset value as false (e.g., change
ADOBE_FONTS_KIT_ID && (...) logic). Also reconcile the font-family name between
this file and HeroSection.tsx by confirming the intended Adobe Fonts family slug
(Lo-Res 21 OT Serif vs lo-res-22), then update the comment, the kit/env usage,
and the fontFamily string in HeroSection.tsx so they all match the confirmed
font slug.

---

Outside diff comments:
In `@apps/marketing/src/app/components/HeroSection/HeroSection.tsx`:
- Around line 29-51: The "AI Agents." TypewriterText segment is using the wrong
CSS fallback font and the Lo-Res version is inconsistent with layout.tsx; decide
whether the intended Lo-Res version is "lo-res-22" (as documented) or
"lo-res-21" and make both the outer h1 style and the TypewriterText segment use
the same family and Pixelify fallback: update the h1 fontFamily entry (currently
'"lo-res-22", var(--font-ibm-plex-mono), monospace' or change to '"lo-res-21",
...' per your decision) and change the TypewriterText segment style from
var(--font-geist-pixel-grid) to var(--font-pixel) so both the outer element and
the "AI Agents." segment reference the same Lo-Res slug and the Pixelify
fallback; ensure layout.tsx documentation and the chosen Lo-Res slug are
reconciled.

---

Nitpick comments:
In `@apps/marketing/src/app/components/CTAButtons/HeaderCTA.tsx`:
- Around line 64-72: HeaderCTA renders a macOS download link styled with
"bg-brand text-white border border-brand-light" while DownloadButton.tsx uses
"bg-foreground text-background", causing inconsistent primary CTA visuals;
decide on the correct brand treatment and apply it consistently: update either
HeaderCTA's anchor or DownloadButton component to use the chosen class set
(referencing HeaderCTA, DOWNLOAD_URL_MAC_ARM64, and DownloadButton) so both
download CTAs share the same utility classes and visual hierarchy, and ensure
any onClick tracking (track("download_clicked")) remains wired in the unified
component.

In `@apps/marketing/src/app/components/WallOfLoveSection/WallOfLoveSection.tsx`:
- Around line 41-57: Replace the single-field fallback expression
testimonial.role ?? testimonial.handle in WallOfLoveSection so the handle isn't
dropped when a role exists; update the render logic in the WallOfLoveSection
component (where Avatar and author are rendered) to display both values (for
example: show testimonial.role followed by a separator and testimonial.handle or
render the handle as a smaller secondary line) while preserving existing link
and styling classes; ensure you reference the same JSX block that currently
contains <span className="text-muted-foreground text-sm">{testimonial.role ??
testimonial.handle}</span> and keep accessibility and layout intact.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 581baff2-cd0b-481c-a399-fd6eec496c6b

📥 Commits

Reviewing files that changed from the base of the PR and between 37161eb and ed2fa45.

⛔ Files ignored due to path filters (1)
  • bun.lock is excluded by !**/*.lock
📒 Files selected for processing (15)
  • apps/marketing/src/app/components/CTAButtons/HeaderCTA.tsx
  • apps/marketing/src/app/components/CTASection/CTASection.tsx
  • apps/marketing/src/app/components/FeaturesSection/components/FeatureDemo/FeatureDemo.tsx
  • apps/marketing/src/app/components/FeaturesSection/components/FeatureDemo/components/DitheredBackground/DitheredBackground.tsx
  • apps/marketing/src/app/components/FeaturesSection/components/FeatureDemo/components/DitheredBackground/index.ts
  • apps/marketing/src/app/components/HeroSection/HeroSection.tsx
  • apps/marketing/src/app/components/HeroSection/components/ProductDemo/ProductDemo.tsx
  • apps/marketing/src/app/components/HeroSection/components/TypewriterText/TypewriterText.tsx
  • apps/marketing/src/app/components/VideoSection/VideoSection.tsx
  • apps/marketing/src/app/components/VideoSection/index.ts
  • apps/marketing/src/app/components/WallOfLoveSection/WallOfLoveSection.tsx
  • apps/marketing/src/app/components/WallOfLoveSection/constants.ts
  • apps/marketing/src/app/globals.css
  • apps/marketing/src/app/layout.tsx
  • apps/marketing/src/app/page.tsx
💤 Files with no reviewable changes (3)
  • apps/marketing/src/app/components/VideoSection/index.ts
  • apps/marketing/src/app/page.tsx
  • apps/marketing/src/app/components/VideoSection/VideoSection.tsx

Comment on lines 99 to 104
author: "Iven",
handle: "@ivenvd",
role: "Engineer at Paraflow",
avatar: "https://unavatar.io/twitter/ivenvd",
url: "https://x.com/ivenvd/status/2011738469610242559",
},
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Verify Iven's role accuracy before publishing.

The PR description notes that "Engineer at Paraflow" for @ivenvd was inferred from his GitHub profile and may be incorrect. Since this is a public-facing testimonial attribution, please confirm directly with Iven (or via his current bio) before shipping to avoid misrepresenting his affiliation.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/marketing/src/app/components/WallOfLoveSection/constants.ts` around
lines 99 - 104, The testimonial entry for author "Iven" (handle "@ivenvd") in
the WallOfLoveSection constants has an inferred role "Engineer at Paraflow" that
must be verified; contact Iven or check his current public bio and then update
the role field in the object for author "Iven" (or temporarily remove/clear the
role value and add a TODO comment) so the public-facing attribution is
accurate—locate the object with author: "Iven" / handle: "@ivenvd" in
constants.ts and change the role property accordingly once confirmed.

Comment thread apps/marketing/src/app/layout.tsx Outdated
Comment on lines +53 to +55
// LoRes 22 OT ships via Adobe Fonts — replace `YOUR_KIT_ID` with the Typekit kit
// ID (the slug in https://use.typekit.net/<kit>.css) once the project is created.
const ADOBE_FONTS_KIT_ID = "YOUR_KIT_ID";
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Prefer an env var over a hardcoded sentinel, and fix the font-name typo.

Two issues here:

  1. ADOBE_FONTS_KIT_ID is a module-level constant set to "YOUR_KIT_ID". The PR description describes this as a value to be "provided" later — using process.env.NEXT_PUBLIC_ADOBE_FONTS_KIT_ID (with a sensible fallback/no-op when unset) would avoid a code change + redeploy each time the kit ID rotates, and keeps the placeholder out of source control.
  2. The comment says "LoRes 22 OT", but the PR objective specifies Lo-Res 21 OT Serif, while HeroSection.tsx references "lo-res-22". One of these is wrong; please confirm the intended Adobe Fonts family slug and align the comment, the kit, and the fontFamily string.
Suggested refactor
-// LoRes 22 OT ships via Adobe Fonts — replace `YOUR_KIT_ID` with the Typekit kit
-// ID (the slug in https://use.typekit.net/<kit>.css) once the project is created.
-const ADOBE_FONTS_KIT_ID = "YOUR_KIT_ID";
+// Lo-Res 21 OT Serif ships via Adobe Fonts. Set
+// NEXT_PUBLIC_ADOBE_FONTS_KIT_ID to the Typekit kit slug
+// (from https://use.typekit.net/<kit>.css) to enable it.
+const ADOBE_FONTS_KIT_ID = process.env.NEXT_PUBLIC_ADOBE_FONTS_KIT_ID;

And update the conditional on line 138 to ADOBE_FONTS_KIT_ID && (...).

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/marketing/src/app/layout.tsx` around lines 53 - 55, Replace the
hardcoded ADOBE_FONTS_KIT_ID constant with reading
process.env.NEXT_PUBLIC_ADOBE_FONTS_KIT_ID (keep a sensible no-op/fallback when
unset) and update any conditional checks that rely on ADOBE_FONTS_KIT_ID to
treat an empty/unset value as false (e.g., change ADOBE_FONTS_KIT_ID && (...)
logic). Also reconcile the font-family name between this file and
HeroSection.tsx by confirming the intended Adobe Fonts family slug (Lo-Res 21 OT
Serif vs lo-res-22), then update the comment, the kit/env usage, and the
fontFamily string in HeroSection.tsx so they all match the confirmed font slug.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant