Add Spanish and Arabic versions, a contact form captcha and a multilingual sitemap
- English, Spanish (/es) and Arabic (/ar, right to left) versions of every page, with a language switch - Self-hosted "I'm not a robot" check on the contact form; the message field is optional - Google Tag Manager, GA4 and Search Console verification in the shared layout - Sitemap, robots.txt and canonical links point at aigrowthtechnologies.com
This commit is contained in:
@@ -0,0 +1,377 @@
|
||||
import { Fragment, type CSSProperties, type ReactNode } from "react";
|
||||
import GlobeCanvas from "@/components/globe-canvas";
|
||||
import StatCounter from "@/components/stat-counter";
|
||||
import RevealObserver from "@/components/reveal-observer";
|
||||
import Capabilities from "@/components/capabilities";
|
||||
import QueryTicker from "@/components/query-ticker";
|
||||
import ProductVisual from "@/components/product-visuals";
|
||||
import Link from "next/link";
|
||||
import { home } from "@/content/home";
|
||||
import { localize } from "@/i18n/config";
|
||||
import { getLocale } from "@/i18n/server";
|
||||
|
||||
type Vars = CSSProperties & Record<`--${string}`, string | number>;
|
||||
const delay = (ms: number) => ({ "--d": `${ms}ms` }) as Vars;
|
||||
|
||||
const Arrow = () => <span className="arrow">→</span>;
|
||||
|
||||
function Eyebrow({ index, children }: { index?: string; children: ReactNode }) {
|
||||
return (
|
||||
<p className="eyebrow">
|
||||
{index && <span className="idx">{index}</span>}
|
||||
{children}
|
||||
</p>
|
||||
);
|
||||
}
|
||||
|
||||
export default async function Home() {
|
||||
const locale = await getLocale();
|
||||
const c = home[locale];
|
||||
const to = (path: string) => localize(path, locale);
|
||||
const { hero, numbers, capabilities, technology, discovery, journey, difference, cta } = c;
|
||||
|
||||
return (
|
||||
<>
|
||||
{/* ───────────────────────── Hero ───────────────────────── */}
|
||||
<section className="hero" id="top" aria-labelledby="hero-title">
|
||||
<GlobeCanvas />
|
||||
<div className="wrap hero-inner">
|
||||
<div data-reveal>
|
||||
<Eyebrow>{hero.eyebrow}</Eyebrow>
|
||||
</div>
|
||||
<h1 id="hero-title" className="display h1" data-reveal style={delay(120)}>
|
||||
{hero.title}
|
||||
</h1>
|
||||
|
||||
<div className="hero-grid">
|
||||
<div className="hero-lead lead" data-reveal style={delay(260)}>
|
||||
<p>{hero.lead}</p>
|
||||
<p className="muted">{hero.leadMuted}</p>
|
||||
</div>
|
||||
<div className="hero-body" data-reveal style={delay(380)}>
|
||||
<div className="copy">
|
||||
<p>{hero.body}</p>
|
||||
<p className="hl">{hero.highlight}</p>
|
||||
</div>
|
||||
<div className="hero-actions">
|
||||
<Link href={to("/contact")} className="btn btn-solid">
|
||||
{hero.primary} <Arrow />
|
||||
</Link>
|
||||
<Link href={to("/services")} className="btn btn-ghost">
|
||||
{hero.secondary} <Arrow />
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="hero-foot" data-reveal style={delay(520)}>
|
||||
<div className="hero-channels" aria-hidden="true">
|
||||
{hero.channels.map((ch, i) => (
|
||||
<Fragment key={ch}>
|
||||
{i > 0 && <i />}
|
||||
<span>{ch}</span>
|
||||
</Fragment>
|
||||
))}
|
||||
</div>
|
||||
<a href="#evidence" className="scroll-cue">
|
||||
{hero.scroll} <i />
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* ───────────────────────── Numbers ───────────────────────── */}
|
||||
<section className="section" id="evidence" aria-labelledby="evidence-title">
|
||||
<div className="wrap">
|
||||
<div className="section-head">
|
||||
<div className="title" data-reveal>
|
||||
<Eyebrow index="01">{numbers.eyebrow}</Eyebrow>
|
||||
<h2 id="evidence-title" className="display h2">
|
||||
{numbers.title}
|
||||
</h2>
|
||||
</div>
|
||||
</div>
|
||||
<div className="stats">
|
||||
{numbers.stats.map((s, i) => (
|
||||
<div key={s.label} className="stat" data-reveal style={delay(i * 120)}>
|
||||
<StatCounter value={s.value} suffix={s.suffix} />
|
||||
<div className="stat-label">{s.label}</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* ───────────────────────── Capabilities ───────────────────────── */}
|
||||
<section className="section section-night" id="capabilities" aria-labelledby="cap-title">
|
||||
<div className="wrap">
|
||||
<div className="section-head">
|
||||
<div className="title" data-reveal>
|
||||
<Eyebrow index="02">{capabilities.eyebrow}</Eyebrow>
|
||||
<h2 id="cap-title" className="display h2">
|
||||
{capabilities.title[0]}
|
||||
<br />
|
||||
{capabilities.title[1]}
|
||||
</h2>
|
||||
</div>
|
||||
</div>
|
||||
<div className="split">
|
||||
<div className="lead" data-reveal>
|
||||
<p>{capabilities.lead}</p>
|
||||
</div>
|
||||
<div className="copy" data-reveal style={delay(140)}>
|
||||
<p>{capabilities.copy}</p>
|
||||
<p className="hl">{capabilities.highlight}</p>
|
||||
<p>{capabilities.after}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div data-reveal>
|
||||
<Capabilities
|
||||
items={capabilities.items.map((it) => ({ ...it, href: to(it.href) }))}
|
||||
core={capabilities.core}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* ───────────────────────── Proprietary technology ───────────────────────── */}
|
||||
<section className="section" id="technology" aria-labelledby="tech-title">
|
||||
<div className="wrap">
|
||||
<div className="section-head">
|
||||
<div className="title" data-reveal>
|
||||
<Eyebrow index="03">{technology.eyebrow}</Eyebrow>
|
||||
<h2 id="tech-title" className="display h2">
|
||||
{technology.title}
|
||||
</h2>
|
||||
</div>
|
||||
</div>
|
||||
<div className="split">
|
||||
<div className="lead" data-reveal>
|
||||
<p>{technology.lead}</p>
|
||||
<p className="muted">{technology.leadMuted}</p>
|
||||
</div>
|
||||
<div className="copy" data-reveal style={delay(140)}>
|
||||
<p>{technology.copy}</p>
|
||||
<p>
|
||||
{technology.closing} <span className="hl">{technology.closingHighlight}</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="products">
|
||||
{technology.products.map((p, i) => (
|
||||
<article key={p.name} className="product" aria-labelledby={`product-${i}`}>
|
||||
<div className="product-visual" data-reveal>
|
||||
<ProductVisual kind={p.visual} labels={technology.visuals} />
|
||||
</div>
|
||||
<div className="product-body" data-reveal style={delay(160)}>
|
||||
<div className="product-kind">
|
||||
<span dir="ltr">{String(i + 1).padStart(2, "0")} / 03</span>
|
||||
<span className="pill">{p.kind}</span>
|
||||
</div>
|
||||
<h3 id={`product-${i}`} className="product-name">
|
||||
{p.name}
|
||||
</h3>
|
||||
<h4 className="display h3">{p.headline}</h4>
|
||||
<div className="copy">
|
||||
{p.body.map((b) => (
|
||||
<p key={b}>{b}</p>
|
||||
))}
|
||||
</div>
|
||||
<Link href={to(p.href)} className="arrow-link">
|
||||
{p.cta} <Arrow />
|
||||
</Link>
|
||||
</div>
|
||||
</article>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* ───────────────────────── Discovery ───────────────────────── */}
|
||||
<section className="section section-night discovery" id="discovery" aria-labelledby="disc-title">
|
||||
<div className="wrap">
|
||||
<div className="section-head">
|
||||
<div className="title" data-reveal>
|
||||
<Eyebrow index="04">{discovery.eyebrow}</Eyebrow>
|
||||
<h2 id="disc-title" className="display h2">
|
||||
{discovery.title}
|
||||
</h2>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="discovery-stage" data-reveal>
|
||||
<div className="query-label">{discovery.queryLabel}</div>
|
||||
<QueryTicker queries={discovery.queries} />
|
||||
<div className="surfaces">
|
||||
<div className="surface left">
|
||||
{discovery.searchPills.map((p) => (
|
||||
<span key={p} className="pill">
|
||||
{p}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
<div className="surface-core">{discovery.yourBrand}</div>
|
||||
<div className="surface">
|
||||
<span className="pill">ChatGPT</span>
|
||||
<span className="pill">Gemini</span>
|
||||
<span className="pill">AI Overviews</span>
|
||||
<span className="pill">Perplexity</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="split">
|
||||
<div className="lead" data-reveal>
|
||||
<p>{discovery.lead}</p>
|
||||
<p className="muted">{discovery.leadMuted}</p>
|
||||
</div>
|
||||
<div data-reveal style={delay(140)}>
|
||||
<div className="copy">
|
||||
<p className="hl">{discovery.highlight}</p>
|
||||
{discovery.copy.map((p) => (
|
||||
<p key={p}>{p}</p>
|
||||
))}
|
||||
</div>
|
||||
<Link href={to("/services/llmo")} className="arrow-link" style={{ marginTop: 28 }}>
|
||||
{discovery.cta} <Arrow />
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* ───────────────────────── Attention to action ───────────────────────── */}
|
||||
<section className="section" id="approach" aria-labelledby="journey-title">
|
||||
<div className="wrap">
|
||||
<div className="section-head">
|
||||
<div className="title" data-reveal>
|
||||
<Eyebrow index="05">{journey.eyebrow}</Eyebrow>
|
||||
<h2 id="journey-title" className="display h2">
|
||||
{journey.title}
|
||||
</h2>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="journey" data-reveal-group>
|
||||
<ol>
|
||||
{journey.steps.map((s, i) => (
|
||||
<li key={s.label} style={{ "--i": i } as Vars}>
|
||||
<span className="dot" />
|
||||
<span className="channel">
|
||||
{String(i + 1).padStart(2, "0")} — {s.channel}
|
||||
</span>
|
||||
<span className="step">{s.label}</span>
|
||||
</li>
|
||||
))}
|
||||
</ol>
|
||||
</div>
|
||||
|
||||
<div className="credit split">
|
||||
<h3 className="display question" data-reveal>
|
||||
{journey.question}
|
||||
</h3>
|
||||
<div className="copy" data-reveal style={delay(140)}>
|
||||
{journey.copy.map((p) => (
|
||||
<p key={p}>{p}</p>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<figure className="pullquote" data-reveal>
|
||||
<Eyebrow>{journey.quoteEyebrow}</Eyebrow>
|
||||
<blockquote>{journey.quote}</blockquote>
|
||||
<figcaption className="copy">
|
||||
<p>{journey.caption}</p>
|
||||
<p className="hl">{journey.captionHighlight}</p>
|
||||
</figcaption>
|
||||
<Link href={to("/about")} className="arrow-link">
|
||||
{journey.cta} <Arrow />
|
||||
</Link>
|
||||
</figure>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* ───────────────────────── Connections ───────────────────────── */}
|
||||
<section className="section section-night" id="difference" aria-labelledby="diff-title">
|
||||
<div className="wrap">
|
||||
<div className="section-head">
|
||||
<div className="title" data-reveal>
|
||||
<Eyebrow index="06">{difference.eyebrow}</Eyebrow>
|
||||
<h2 id="diff-title" className="display h2">
|
||||
{difference.title}
|
||||
</h2>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ul className="gaps">
|
||||
{difference.gaps.map((g, i) => (
|
||||
<li key={g} data-reveal style={delay(i * 80)}>
|
||||
<span>{g}</span>
|
||||
<svg className="break" viewBox="0 0 120 14" aria-hidden="true">
|
||||
<line x1="0" y1="7" x2="50" y2="7" />
|
||||
<line x1="70" y1="7" x2="120" y2="7" />
|
||||
<line className="cut" x1="55" y1="1" x2="65" y2="13" />
|
||||
</svg>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
|
||||
<div className="split" style={{ marginTop: "clamp(80px, 10vw, 144px)" }}>
|
||||
<h3 className="display h2" data-reveal>
|
||||
{difference.gapTitle}
|
||||
</h3>
|
||||
<div className="copy" data-reveal style={delay(140)}>
|
||||
<p>{difference.copy}</p>
|
||||
<p className="hl">{difference.highlight}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="nos">
|
||||
{difference.nos.map((n, i) => (
|
||||
<div key={n} data-reveal style={i ? delay(i * 120) : undefined}>
|
||||
<span>
|
||||
{difference.noLabel} {String(i + 1).padStart(2, "0")}
|
||||
</span>
|
||||
{n}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<p className="closing" data-reveal>
|
||||
{difference.closing}
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* ───────────────────────── CTA ───────────────────────── */}
|
||||
<section className="cta" id="contact" aria-labelledby="cta-title">
|
||||
<GlobeCanvas layout="center" />
|
||||
<div className="wrap">
|
||||
<div data-reveal>
|
||||
<Eyebrow>{cta.eyebrow}</Eyebrow>
|
||||
</div>
|
||||
<h2 id="cta-title" className="display h2" data-reveal style={delay(120)}>
|
||||
{cta.title}
|
||||
</h2>
|
||||
<div className="copy" data-reveal style={delay(240)}>
|
||||
{cta.copy.map((p) => (
|
||||
<p key={p}>{p}</p>
|
||||
))}
|
||||
</div>
|
||||
<div className="lead" data-reveal style={delay(320)}>
|
||||
<p>{cta.lead}</p>
|
||||
<p className="muted">{cta.leadMuted}</p>
|
||||
</div>
|
||||
<div data-reveal style={delay(400)}>
|
||||
<Link href={to("/contact")} className="btn btn-solid">
|
||||
{cta.button} <Arrow />
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<RevealObserver />
|
||||
</>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user