feat: compass/crosshair SVG decorations across landing page, fix email from noreply→hello
- CompassBg: 8 compass marks scattered across full page height with brand amber accent on inner ring, faded crosshair lines, cardinal ticks — z-index 0 (behind content) - Index.tsx: CompassBg rendered as absolute layer inside relative wrapper - email_service: EMAIL_FROM default hello@ (noreply triggers spam filters) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
969198adde
commit
f4dc3074c0
3 changed files with 133 additions and 2 deletions
|
|
@ -10,7 +10,7 @@ import httpx
|
|||
logger = logging.getLogger(__name__)
|
||||
|
||||
RESEND_API_KEY = os.environ.get("RESEND_API_KEY", "")
|
||||
FROM_EMAIL = os.environ.get("EMAIL_FROM", "Cohorta <noreply@ai-impress.com>")
|
||||
FROM_EMAIL = os.environ.get("EMAIL_FROM", "Cohorta <hello@ai-impress.com>") # ai-impress.com verified in Resend; avoid noreply (spam filter)
|
||||
APP_URL = os.environ.get("APP_URL", "http://localhost:5173")
|
||||
|
||||
|
||||
|
|
|
|||
129
src/components/landing/CompassBg.tsx
Normal file
129
src/components/landing/CompassBg.tsx
Normal file
|
|
@ -0,0 +1,129 @@
|
|||
import React from 'react';
|
||||
|
||||
interface CompassMarkProps {
|
||||
uid: string;
|
||||
size: number;
|
||||
x: string; // CSS left value
|
||||
y: number; // px from top of container
|
||||
opacity: number;
|
||||
rotate?: number;
|
||||
}
|
||||
|
||||
function CompassMark({ uid, size, x, y, opacity, rotate = 0 }: CompassMarkProps) {
|
||||
const cx = size / 2;
|
||||
const r1 = size * 0.46; // outer ring
|
||||
const r2 = size * 0.31; // middle dashed ring
|
||||
const r3 = size * 0.15; // inner amber ring
|
||||
const pad = size * 0.04;
|
||||
|
||||
return (
|
||||
<div
|
||||
aria-hidden="true"
|
||||
style={{
|
||||
position: 'absolute',
|
||||
left: x,
|
||||
top: y,
|
||||
width: size,
|
||||
height: size,
|
||||
opacity,
|
||||
transform: `translate(-50%, -50%) rotate(${rotate}deg)`,
|
||||
pointerEvents: 'none',
|
||||
}}
|
||||
>
|
||||
<svg viewBox={`0 0 ${size} ${size}`} fill="none" xmlns="http://www.w3.org/2000/svg" width={size} height={size}>
|
||||
<defs>
|
||||
<linearGradient id={`${uid}-h`} x1="0" y1="0.5" x2="1" y2="0.5" gradientUnits="objectBoundingBox">
|
||||
<stop offset="0%" stopColor="white" stopOpacity="0" />
|
||||
<stop offset="20%" stopColor="white" stopOpacity="0.8" />
|
||||
<stop offset="50%" stopColor="white" stopOpacity="1" />
|
||||
<stop offset="80%" stopColor="white" stopOpacity="0.8" />
|
||||
<stop offset="100%" stopColor="white" stopOpacity="0" />
|
||||
</linearGradient>
|
||||
<linearGradient id={`${uid}-v`} x1="0.5" y1="0" x2="0.5" y2="1" gradientUnits="objectBoundingBox">
|
||||
<stop offset="0%" stopColor="white" stopOpacity="0" />
|
||||
<stop offset="20%" stopColor="white" stopOpacity="0.8" />
|
||||
<stop offset="50%" stopColor="white" stopOpacity="1" />
|
||||
<stop offset="80%" stopColor="white" stopOpacity="0.8" />
|
||||
<stop offset="100%" stopColor="white" stopOpacity="0" />
|
||||
</linearGradient>
|
||||
<linearGradient id={`${uid}-d1`} x1="0" y1="0" x2="1" y2="1" gradientUnits="objectBoundingBox">
|
||||
<stop offset="0%" stopColor="white" stopOpacity="0" />
|
||||
<stop offset="25%" stopColor="white" stopOpacity="0.6" />
|
||||
<stop offset="75%" stopColor="white" stopOpacity="0.6" />
|
||||
<stop offset="100%" stopColor="white" stopOpacity="0" />
|
||||
</linearGradient>
|
||||
<linearGradient id={`${uid}-d2`} x1="1" y1="0" x2="0" y2="1" gradientUnits="objectBoundingBox">
|
||||
<stop offset="0%" stopColor="white" stopOpacity="0" />
|
||||
<stop offset="25%" stopColor="white" stopOpacity="0.6" />
|
||||
<stop offset="75%" stopColor="white" stopOpacity="0.6" />
|
||||
<stop offset="100%" stopColor="white" stopOpacity="0" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
|
||||
{/* Outer ring */}
|
||||
<circle cx={cx} cy={cx} r={r1} stroke="white" strokeWidth={size * 0.003} strokeOpacity="0.18" />
|
||||
|
||||
{/* Middle dashed ring */}
|
||||
<circle cx={cx} cy={cx} r={r2} stroke="white" strokeWidth={size * 0.002}
|
||||
strokeOpacity="0.1" strokeDasharray={`${size * 0.008} ${size * 0.014}`} />
|
||||
|
||||
{/* Inner amber ring */}
|
||||
<circle cx={cx} cy={cx} r={r3} stroke="#E07A2F" strokeWidth={size * 0.003} strokeOpacity="0.55" />
|
||||
|
||||
{/* Center dot */}
|
||||
<circle cx={cx} cy={cx} r={size * 0.007} fill="#E07A2F" fillOpacity="0.6" />
|
||||
|
||||
{/* Crosshair lines */}
|
||||
<line x1={pad} y1={cx} x2={size - pad} y2={cx}
|
||||
stroke={`url(#${uid}-h)`} strokeWidth={size * 0.002} />
|
||||
<line x1={cx} y1={pad} x2={cx} y2={size - pad}
|
||||
stroke={`url(#${uid}-v)`} strokeWidth={size * 0.002} />
|
||||
|
||||
{/* Diagonal lines */}
|
||||
<line x1={pad * 3} y1={pad * 3} x2={size - pad * 3} y2={size - pad * 3}
|
||||
stroke={`url(#${uid}-d1)`} strokeWidth={size * 0.0015} />
|
||||
<line x1={size - pad * 3} y1={pad * 3} x2={pad * 3} y2={size - pad * 3}
|
||||
stroke={`url(#${uid}-d2)`} strokeWidth={size * 0.0015} />
|
||||
|
||||
{/* Cardinal ticks on outer ring — N S E W */}
|
||||
{[0, 90, 180, 270].map(deg => {
|
||||
const rad = (deg - 90) * Math.PI / 180;
|
||||
const x1 = cx + (r1 - size * 0.015) * Math.cos(rad);
|
||||
const y1 = cx + (r1 - size * 0.015) * Math.sin(rad);
|
||||
const x2 = cx + (r1 + size * 0.025) * Math.cos(rad);
|
||||
const y2 = cx + (r1 + size * 0.025) * Math.sin(rad);
|
||||
return (
|
||||
<line key={deg} x1={x1} y1={y1} x2={x2} y2={y2}
|
||||
stroke="white" strokeWidth={size * 0.004} strokeOpacity="0.3" />
|
||||
);
|
||||
})}
|
||||
</svg>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// Instances scattered across the full landing page height
|
||||
const MARKS = [
|
||||
{ uid: 'c0', size: 580, x: '88%', y: 340, opacity: 0.08, rotate: 12 },
|
||||
{ uid: 'c1', size: 300, x: '-2%', y: 720, opacity: 0.055, rotate: -7 },
|
||||
{ uid: 'c2', size: 440, x: '82%', y: 1200, opacity: 0.07, rotate: 20 },
|
||||
{ uid: 'c3', size: 260, x: '22%', y: 1700, opacity: 0.05, rotate: 5 },
|
||||
{ uid: 'c4', size: 500, x: '10%', y: 2200, opacity: 0.065, rotate: -15 },
|
||||
{ uid: 'c5', size: 340, x: '75%', y: 2700, opacity: 0.06, rotate: 8 },
|
||||
{ uid: 'c6', size: 220, x: '40%', y: 3100, opacity: 0.045, rotate: -20 },
|
||||
{ uid: 'c7', size: 390, x: '92%', y: 3500, opacity: 0.055, rotate: 30 },
|
||||
] as const;
|
||||
|
||||
export default function CompassBg() {
|
||||
return (
|
||||
<div
|
||||
className="absolute inset-0 pointer-events-none overflow-hidden"
|
||||
style={{ zIndex: 0 }}
|
||||
aria-hidden="true"
|
||||
>
|
||||
{MARKS.map(m => (
|
||||
<CompassMark key={m.uid} {...m} />
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
import Hero from '@/components/landing/Hero';
|
||||
import CompassBg from '@/components/landing/CompassBg';
|
||||
import StatsBand from '@/components/landing/StatsBand';
|
||||
import FeatureGrid from '@/components/landing/FeatureGrid';
|
||||
import HowItWorks from '@/components/landing/HowItWorks';
|
||||
|
|
@ -12,7 +13,8 @@ import FinalCTA from '@/components/landing/FinalCTA';
|
|||
|
||||
export default function Index() {
|
||||
return (
|
||||
<div className="bg-background overflow-hidden">
|
||||
<div className="bg-background overflow-hidden relative">
|
||||
<CompassBg />
|
||||
<Hero />
|
||||
<StatsBand />
|
||||
<FeatureGrid />
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue