Update hero alumni strip, 2027 dates, and Antebas font loading.
Some checks failed
Deploy to Cloudflare Workers (OpenNext) / deploy (push) Has been cancelled
Some checks failed
Deploy to Cloudflare Workers (OpenNext) / deploy (push) Has been cancelled
Add a bottom cut-out panel with larger winner cards (four visible), move summit dates to Feb 21–22 2027, and limit demo Antebas to letters so symbols render via DM Sans. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
f950545ae0
commit
03d439e97b
53
app/antebas.css
Normal file
53
app/antebas.css
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
/**
|
||||
* Antebas (Fontspring DEMO) — letters only.
|
||||
* Demo builds replace numerals/punctuation with watermark glyphs; DM Sans covers the rest.
|
||||
*/
|
||||
@font-face {
|
||||
font-family: Antebas;
|
||||
src: url("/fonts/Fontspring-DEMO-antebas-light.otf") format("opentype");
|
||||
font-weight: 300;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
unicode-range:
|
||||
U+0041-005A, U+0061-007A, U+00C0-00D6, U+00D8-00F6, U+00F8-00FF, U+0100-017F;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: Antebas;
|
||||
src: url("/fonts/Fontspring-DEMO-antebas-regular.otf") format("opentype");
|
||||
font-weight: 400;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
unicode-range:
|
||||
U+0041-005A, U+0061-007A, U+00C0-00D6, U+00D8-00F6, U+00F8-00FF, U+0100-017F;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: Antebas;
|
||||
src: url("/fonts/Fontspring-DEMO-antebas-medium.otf") format("opentype");
|
||||
font-weight: 500;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
unicode-range:
|
||||
U+0041-005A, U+0061-007A, U+00C0-00D6, U+00D8-00F6, U+00F8-00FF, U+0100-017F;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: Antebas;
|
||||
src: url("/fonts/Fontspring-DEMO-antebas-bold.otf") format("opentype");
|
||||
font-weight: 700;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
unicode-range:
|
||||
U+0041-005A, U+0061-007A, U+00C0-00D6, U+00D8-00F6, U+00F8-00FF, U+0100-017F;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: Antebas;
|
||||
src: url("/fonts/Fontspring-DEMO-antebas-black.otf") format("opentype");
|
||||
font-weight: 900;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
unicode-range:
|
||||
U+0041-005A, U+0061-007A, U+00C0-00D6, U+00D8-00F6, U+00F8-00FF, U+0100-017F;
|
||||
}
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
@import "tailwindcss";
|
||||
@import "./antebas.css";
|
||||
@import "@fontsource-variable/google-sans-flex/wght.css";
|
||||
|
||||
@custom-variant dark (&:is(.dark *));
|
||||
|
|
@ -26,8 +27,8 @@
|
|||
--radius-md: calc(var(--radius) - 2px);
|
||||
--radius-lg: var(--radius);
|
||||
--radius-xl: calc(var(--radius) + 4px);
|
||||
--font-sans: var(--font-antebas);
|
||||
--font-display: var(--font-antebas);
|
||||
--font-sans: var(--font-site);
|
||||
--font-display: var(--font-site);
|
||||
--font-wordmark: "Google Sans Flex Variable", system-ui, sans-serif;
|
||||
--font-hero-serif: var(--font-hero-serif);
|
||||
--color-brand-primary: #37a47a;
|
||||
|
|
@ -77,6 +78,7 @@
|
|||
--hero: var(--brand-tertiary);
|
||||
--section-muted: var(--brand-surface-muted);
|
||||
--section-inverse: var(--brand-primary);
|
||||
--font-site: Antebas, var(--font-hero-body), system-ui, sans-serif;
|
||||
}
|
||||
|
||||
@layer base {
|
||||
|
|
@ -85,13 +87,13 @@
|
|||
}
|
||||
body {
|
||||
@apply bg-background text-foreground antialiased;
|
||||
font-family: var(--font-antebas), system-ui, sans-serif;
|
||||
font-family: var(--font-site);
|
||||
}
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4 {
|
||||
font-family: var(--font-antebas), system-ui, sans-serif;
|
||||
font-family: var(--font-site);
|
||||
letter-spacing: -0.02em;
|
||||
}
|
||||
|
||||
|
|
@ -238,6 +240,18 @@
|
|||
.marquee-winners {
|
||||
animation: marquee 55s linear infinite;
|
||||
}
|
||||
|
||||
/* Hero bottom cut-out — fits four large winner marks in the visible strip */
|
||||
.hero-winners-viewport {
|
||||
width: 100%;
|
||||
max-width: min(100%, calc(var(--winners-visible, 4) * 13.25rem + (var(--winners-visible, 4) - 1) * 1rem));
|
||||
}
|
||||
|
||||
@media (max-width: 639px) {
|
||||
.hero-winners-viewport {
|
||||
max-width: min(100%, calc(var(--winners-visible, 4) * 10.5rem + (var(--winners-visible, 4) - 1) * 0.75rem));
|
||||
}
|
||||
}
|
||||
@keyframes marquee {
|
||||
from {
|
||||
transform: translateX(0);
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
import { antebas } from "@/lib/fonts/antebas";
|
||||
import { heroFontVariables } from "@/lib/fonts/hero";
|
||||
import { RiftPageFlow } from "@/components/brand/RiftPageFlow";
|
||||
import { JsonLd } from "@/components/seo/JsonLd";
|
||||
|
|
@ -16,11 +15,8 @@ export default function RootLayout({
|
|||
children: React.ReactNode;
|
||||
}>) {
|
||||
return (
|
||||
<html
|
||||
lang="en"
|
||||
className={`${antebas.variable} ${heroFontVariables}`}
|
||||
>
|
||||
<body className={`${antebas.className} min-h-screen flex flex-col`}>
|
||||
<html lang="en" className={heroFontVariables}>
|
||||
<body className="min-h-screen flex flex-col">
|
||||
<JsonLd />
|
||||
<SiteHeader />
|
||||
<main className="relative flex-1">
|
||||
|
|
|
|||
|
|
@ -74,6 +74,10 @@ export function Hero() {
|
|||
hoverY={hoverPoint.y}
|
||||
className="absolute inset-0"
|
||||
/>
|
||||
<div
|
||||
className="pointer-events-none absolute inset-x-0 bottom-0 z-[4] h-28 bg-gradient-to-t from-[#e8f2ec]/95 via-[#e8f2ec]/40 to-transparent md:h-36"
|
||||
aria-hidden
|
||||
/>
|
||||
<TopoCurvyExtend />
|
||||
<HeroRiftParticles
|
||||
active={!reduceMotion}
|
||||
|
|
@ -81,7 +85,8 @@ export function Hero() {
|
|||
/>
|
||||
|
||||
<TopoSectionProvider tone="light">
|
||||
<div className="topo-content-layer topo-content-readable relative z-20 mx-auto flex min-h-[min(100svh,900px)] w-full max-w-6xl flex-col items-center justify-center px-4 py-24 text-center md:px-6">
|
||||
<div className="topo-content-layer topo-content-readable relative z-20 mx-auto flex min-h-[min(100svh,900px)] w-full max-w-6xl flex-col px-4 pt-24 pb-0 text-center md:px-6">
|
||||
<div className="flex flex-1 flex-col items-center justify-center pb-8">
|
||||
<TopoProseSurface className="mx-auto flex w-full max-w-4xl flex-col items-center text-center">
|
||||
<ScrollReveal immediate variant="fade-up" delay={0}>
|
||||
<p className="text-xs font-semibold uppercase tracking-[0.2em] text-[#37a47a]/80 md:text-sm">
|
||||
|
|
@ -131,8 +136,9 @@ export function Hero() {
|
|||
</p>
|
||||
</ScrollReveal>
|
||||
</TopoProseSurface>
|
||||
</div>
|
||||
|
||||
<LastYearWinnersScroll variant="on-light" className="w-full max-w-5xl" />
|
||||
<LastYearWinnersScroll variant="on-light" size="large" visibleCount={4} className="w-full" />
|
||||
</div>
|
||||
</TopoSectionProvider>
|
||||
</section>
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ type Props = {
|
|||
onUnpin: () => void;
|
||||
onHover: (key: string | null) => void;
|
||||
variant?: "on-green" | "on-light";
|
||||
size?: "default" | "large";
|
||||
className?: string;
|
||||
};
|
||||
|
||||
|
|
@ -30,6 +31,7 @@ export function LastYearWinnerMark({
|
|||
onUnpin,
|
||||
onHover,
|
||||
variant = "on-light",
|
||||
size = "default",
|
||||
className,
|
||||
}: Props) {
|
||||
const leaveTimer = useRef<ReturnType<typeof setTimeout> | null>(null);
|
||||
|
|
@ -38,6 +40,8 @@ export function LastYearWinnerMark({
|
|||
const showImage = !failed && src;
|
||||
const logoOnly = !company.name;
|
||||
const onLight = variant === "on-light";
|
||||
const isLarge = size === "large";
|
||||
const logoPx = isLarge ? 48 : 28;
|
||||
|
||||
const clearLeaveTimer = useCallback(() => {
|
||||
if (leaveTimer.current) {
|
||||
|
|
@ -96,11 +100,14 @@ export function LastYearWinnerMark({
|
|||
>
|
||||
<div
|
||||
className={cn(
|
||||
"flex h-10 shrink-0 items-center gap-2 rounded-lg border px-2.5 shadow-sm transition-colors",
|
||||
"flex shrink-0 items-center gap-2.5 rounded-xl border shadow-sm transition-colors",
|
||||
isLarge
|
||||
? "h-14 min-w-[10.5rem] gap-2.5 px-3 sm:h-16 sm:min-w-[12.75rem] sm:gap-3 sm:px-3.5 md:h-[4.5rem] md:min-w-[13.25rem] md:px-4"
|
||||
: "h-10 gap-2 rounded-lg px-2.5",
|
||||
onLight
|
||||
? "border-[#37a47a]/15 bg-white/95 hover:border-[#37a47a]/30 hover:bg-white"
|
||||
: "border-white/25 bg-white/90 hover:border-white/50 hover:bg-white",
|
||||
logoOnly && "px-2",
|
||||
logoOnly && (isLarge ? "px-3" : "px-2"),
|
||||
isOpen &&
|
||||
(onLight
|
||||
? "border-[#37a47a]/40 ring-2 ring-[#37a47a]/15"
|
||||
|
|
@ -109,32 +116,49 @@ export function LastYearWinnerMark({
|
|||
className
|
||||
)}
|
||||
>
|
||||
<div className="relative flex size-7 shrink-0 items-center justify-center overflow-hidden rounded-md bg-white">
|
||||
<div
|
||||
className={cn(
|
||||
"relative flex shrink-0 items-center justify-center overflow-hidden rounded-lg bg-white",
|
||||
isLarge ? "size-11 md:size-12" : "size-7 rounded-md"
|
||||
)}
|
||||
>
|
||||
{showImage ? (
|
||||
<Image
|
||||
src={src}
|
||||
alt=""
|
||||
width={28}
|
||||
height={28}
|
||||
className="size-7 object-contain p-0.5"
|
||||
width={logoPx}
|
||||
height={logoPx}
|
||||
className={cn("object-contain p-0.5", isLarge ? "size-11 md:size-12" : "size-7")}
|
||||
onError={() => setFailed(true)}
|
||||
/>
|
||||
) : company.initials ? (
|
||||
<span className="text-[10px] font-bold leading-none text-[#37a47a]">
|
||||
<span
|
||||
className={cn(
|
||||
"font-bold leading-none text-[#37a47a]",
|
||||
isLarge ? "text-sm md:text-base" : "text-[10px]"
|
||||
)}
|
||||
>
|
||||
{company.initials}
|
||||
</span>
|
||||
) : (
|
||||
<Image
|
||||
src={GRV_LOGO}
|
||||
alt=""
|
||||
width={28}
|
||||
height={28}
|
||||
className="size-7 object-contain p-0.5"
|
||||
width={logoPx}
|
||||
height={logoPx}
|
||||
className={cn("object-contain p-0.5", isLarge ? "size-11 md:size-12" : "size-7")}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
{company.name ? (
|
||||
<span className="max-w-[7.5rem] truncate text-[11px] font-medium text-[#37a47a]">
|
||||
<span
|
||||
className={cn(
|
||||
"truncate font-medium text-[#37a47a]",
|
||||
isLarge
|
||||
? "max-w-[8.5rem] text-sm md:max-w-[9.5rem] md:text-base"
|
||||
: "max-w-[7.5rem] text-[11px]"
|
||||
)}
|
||||
>
|
||||
{company.name}
|
||||
</span>
|
||||
) : null}
|
||||
|
|
@ -148,7 +172,10 @@ export function LastYearWinnerMark({
|
|||
sideOffset={8}
|
||||
collisionPadding={12}
|
||||
data-winner-interactive
|
||||
className="z-50 w-72 max-w-[calc(100vw-2rem)] shrink-0 overflow-hidden border-[#37a47a]/15 bg-white p-0 shadow-lg"
|
||||
className={cn(
|
||||
"z-50 max-w-[calc(100vw-2rem)] shrink-0 overflow-hidden border-[#37a47a]/15 bg-white p-0 shadow-lg",
|
||||
isLarge ? "w-[22rem] md:w-96" : "w-72"
|
||||
)}
|
||||
onMouseEnter={handleEnter}
|
||||
onMouseLeave={handleLeave}
|
||||
onOpenAutoFocus={(e) => e.preventDefault()}
|
||||
|
|
@ -159,7 +186,12 @@ export function LastYearWinnerMark({
|
|||
if (isPinned) e.preventDefault();
|
||||
}}
|
||||
>
|
||||
<LastYearWinnerTip company={company} onClose={onUnpin} showClose={isPinned} />
|
||||
<LastYearWinnerTip
|
||||
company={company}
|
||||
onClose={onUnpin}
|
||||
showClose={isPinned}
|
||||
size={size}
|
||||
/>
|
||||
</PopoverContent>
|
||||
) : null}
|
||||
</Popover>
|
||||
|
|
|
|||
|
|
@ -13,13 +13,20 @@ type Props = {
|
|||
className?: string;
|
||||
showClose?: boolean;
|
||||
onClose?: () => void;
|
||||
size?: "default" | "large";
|
||||
};
|
||||
|
||||
function externalLinkProps(href: string) {
|
||||
return href.startsWith("http") ? { target: "_blank" as const, rel: "noopener noreferrer" } : {};
|
||||
}
|
||||
|
||||
function FounderPhoto({ company }: { company: LastYearWinner }) {
|
||||
function FounderPhoto({
|
||||
company,
|
||||
large,
|
||||
}: {
|
||||
company: LastYearWinner;
|
||||
large?: boolean;
|
||||
}) {
|
||||
const [failed, setFailed] = useState(false);
|
||||
const src = company.founderImageSrc;
|
||||
|
||||
|
|
@ -28,9 +35,12 @@ function FounderPhoto({ company }: { company: LastYearWinner }) {
|
|||
<Image
|
||||
src={src}
|
||||
alt=""
|
||||
width={48}
|
||||
height={48}
|
||||
className="size-12 shrink-0 rounded-full border-2 border-[#37a47a]/15 object-cover"
|
||||
width={large ? 56 : 48}
|
||||
height={large ? 56 : 48}
|
||||
className={cn(
|
||||
"shrink-0 rounded-full border-2 border-[#37a47a]/15 object-cover",
|
||||
large ? "size-14" : "size-12"
|
||||
)}
|
||||
onError={() => setFailed(true)}
|
||||
/>
|
||||
);
|
||||
|
|
@ -49,7 +59,10 @@ function FounderPhoto({ company }: { company: LastYearWinner }) {
|
|||
|
||||
return (
|
||||
<div
|
||||
className="flex size-12 shrink-0 items-center justify-center rounded-full border-2 border-[#37a47a]/15 bg-[#e8f2ec] text-sm font-bold text-[#37a47a]"
|
||||
className={cn(
|
||||
"flex shrink-0 items-center justify-center rounded-full border-2 border-[#37a47a]/15 bg-[#e8f2ec] font-bold text-[#37a47a]",
|
||||
large ? "size-14 text-base" : "size-12 text-sm"
|
||||
)}
|
||||
aria-hidden
|
||||
>
|
||||
{initials}
|
||||
|
|
@ -57,7 +70,14 @@ function FounderPhoto({ company }: { company: LastYearWinner }) {
|
|||
);
|
||||
}
|
||||
|
||||
export function LastYearWinnerTip({ company, className, showClose, onClose }: Props) {
|
||||
export function LastYearWinnerTip({
|
||||
company,
|
||||
className,
|
||||
showClose,
|
||||
onClose,
|
||||
size = "default",
|
||||
}: Props) {
|
||||
const isLarge = size === "large";
|
||||
const impact = getWinnerImpact(company);
|
||||
const headline = impact.metrics.find((m) => m.highlight) ?? impact.metrics[0];
|
||||
const supporting = impact.metrics.filter((m) => m !== headline).slice(0, 2);
|
||||
|
|
@ -68,28 +88,66 @@ export function LastYearWinnerTip({ company, className, showClose, onClose }: Pr
|
|||
const showFooter = showClose || viewHref || donateHref;
|
||||
|
||||
return (
|
||||
<div className={cn("box-border w-72 max-w-full shrink-0 overflow-hidden", className)}>
|
||||
<div className="flex min-w-0 gap-2.5 px-3 pt-3 pb-2">
|
||||
<FounderPhoto company={company} />
|
||||
<div
|
||||
className={cn(
|
||||
"box-border max-w-full shrink-0 overflow-hidden",
|
||||
isLarge ? "w-full" : "w-72",
|
||||
className
|
||||
)}
|
||||
>
|
||||
<div className={cn("flex min-w-0 px-3 pt-3 pb-2", isLarge ? "gap-3.5" : "gap-2.5")}>
|
||||
<FounderPhoto company={company} large={isLarge} />
|
||||
<div className="min-w-0 flex-1">
|
||||
<p className="truncate text-[11px] font-semibold uppercase tracking-wider text-[#37a47a]">
|
||||
<p
|
||||
className={cn(
|
||||
"truncate font-semibold uppercase tracking-wider text-[#37a47a]",
|
||||
isLarge ? "text-xs md:text-sm" : "text-[11px]"
|
||||
)}
|
||||
>
|
||||
{name}
|
||||
</p>
|
||||
{company.founderName ? (
|
||||
<p className="truncate text-[10px] text-[#5b5b5b]/80">{company.founderName}</p>
|
||||
<p
|
||||
className={cn(
|
||||
"truncate text-[#5b5b5b]/80",
|
||||
isLarge ? "text-xs" : "text-[10px]"
|
||||
)}
|
||||
>
|
||||
{company.founderName}
|
||||
</p>
|
||||
) : null}
|
||||
<p className="mt-1 line-clamp-2 text-[11px] leading-snug text-[#5b5b5b]">
|
||||
<p
|
||||
className={cn(
|
||||
"mt-1 line-clamp-2 leading-snug text-[#5b5b5b]",
|
||||
isLarge ? "text-xs md:text-sm" : "text-[11px]"
|
||||
)}
|
||||
>
|
||||
{impact.summary}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{headline ? (
|
||||
<div className="mx-3 mb-2 overflow-hidden rounded-lg border border-[#37a47a]/12 bg-[#e8f2ec] px-2.5 py-2 text-center">
|
||||
<p className="truncate text-[10px] font-bold uppercase tracking-wider text-[#37a47a]/75">
|
||||
<div
|
||||
className={cn(
|
||||
"mx-3 mb-2 overflow-hidden rounded-lg border border-[#37a47a]/12 bg-[#e8f2ec] text-center",
|
||||
isLarge ? "px-3 py-3" : "px-2.5 py-2"
|
||||
)}
|
||||
>
|
||||
<p
|
||||
className={cn(
|
||||
"truncate font-bold uppercase tracking-wider text-[#37a47a]/75",
|
||||
isLarge ? "text-xs" : "text-[10px]"
|
||||
)}
|
||||
>
|
||||
{headline.label}
|
||||
</p>
|
||||
<p className="winner-impact-value mt-0.5 truncate font-display text-xl font-extrabold tracking-tight text-[#30614c]">
|
||||
<p
|
||||
className={cn(
|
||||
"winner-impact-value mt-0.5 truncate font-display font-extrabold tracking-tight text-[#30614c]",
|
||||
isLarge ? "text-2xl md:text-3xl" : "text-xl"
|
||||
)}
|
||||
>
|
||||
{headline.value}
|
||||
</p>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,18 +1,27 @@
|
|||
"use client";
|
||||
|
||||
import { useCallback, useEffect, useState } from "react";
|
||||
import { useCallback, useEffect, useState, type CSSProperties } from "react";
|
||||
import { lastYearWinners, lastYearWinnersCopy } from "@/content/last-year-winners";
|
||||
import { LastYearWinnerMark } from "@/components/home/LastYearWinnerMark";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
type Props = {
|
||||
variant?: "on-green" | "on-light";
|
||||
size?: "default" | "large";
|
||||
/** How many company marks fit in the visible strip (marquee viewport). */
|
||||
visibleCount?: number;
|
||||
className?: string;
|
||||
};
|
||||
|
||||
export function LastYearWinnersScroll({ variant = "on-green", className }: Props) {
|
||||
export function LastYearWinnersScroll({
|
||||
variant = "on-green",
|
||||
size = "default",
|
||||
visibleCount = 4,
|
||||
className,
|
||||
}: Props) {
|
||||
const items = [...lastYearWinners, ...lastYearWinners];
|
||||
const onLight = variant === "on-light";
|
||||
const isLarge = size === "large";
|
||||
const [hoverKey, setHoverKey] = useState<string | null>(null);
|
||||
const [pinnedKey, setPinnedKey] = useState<string | null>(null);
|
||||
|
||||
|
|
@ -57,53 +66,65 @@ export function LastYearWinnersScroll({ variant = "on-green", className }: Props
|
|||
};
|
||||
}, [pinnedKey]);
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
"pt-8",
|
||||
onLight ? "mt-8 border-t border-[#37a47a]/12" : "mt-10 border-t border-white/15",
|
||||
className
|
||||
)}
|
||||
>
|
||||
const content = (
|
||||
<>
|
||||
<p
|
||||
className={cn(
|
||||
"text-center text-[10px] font-semibold uppercase tracking-[0.2em] text-[#37a47a]"
|
||||
"text-center font-semibold uppercase tracking-[0.2em] text-[#37a47a]",
|
||||
isLarge ? "text-xs md:text-sm" : "text-[10px]"
|
||||
)}
|
||||
>
|
||||
{lastYearWinnersCopy.eyebrow}
|
||||
</p>
|
||||
<p
|
||||
className={cn(
|
||||
"mt-2 text-center text-lg font-bold tracking-tight md:text-xl",
|
||||
"mt-2 text-center font-bold tracking-tight",
|
||||
isLarge ? "text-xl md:text-2xl" : "text-lg md:text-xl",
|
||||
onLight ? "text-[#30614c]" : "text-white"
|
||||
)}
|
||||
>
|
||||
{lastYearWinnersCopy.headline}
|
||||
</p>
|
||||
<p className="mt-2 text-center">
|
||||
<span className="inline-block rounded-full bg-white px-3 py-1 text-[11px] font-medium text-[#37a47a] shadow-sm">
|
||||
<span
|
||||
className={cn(
|
||||
"inline-block rounded-full bg-white font-medium text-[#37a47a] shadow-sm",
|
||||
isLarge ? "px-4 py-1.5 text-xs md:text-sm" : "px-3 py-1 text-[11px]"
|
||||
)}
|
||||
>
|
||||
{lastYearWinnersCopy.hoverHint}
|
||||
</span>
|
||||
</p>
|
||||
<div
|
||||
className="relative mt-5 overflow-hidden"
|
||||
className={cn(
|
||||
"relative mt-5 overflow-hidden",
|
||||
isLarge && "hero-winners-viewport mx-auto"
|
||||
)}
|
||||
style={
|
||||
isLarge
|
||||
? ({
|
||||
"--winners-visible": visibleCount,
|
||||
} as CSSProperties)
|
||||
: undefined
|
||||
}
|
||||
aria-label="Companies supported at last year's summit"
|
||||
>
|
||||
<div
|
||||
className={cn(
|
||||
"pointer-events-none absolute inset-y-0 left-0 z-10 w-10 bg-gradient-to-r to-transparent",
|
||||
onLight ? "from-white" : "from-[#37a47a]"
|
||||
"pointer-events-none absolute inset-y-0 left-0 z-10 w-12 bg-gradient-to-r to-transparent md:w-16",
|
||||
onLight ? "from-[#e8f2ec]" : "from-[#37a47a]"
|
||||
)}
|
||||
/>
|
||||
<div
|
||||
className={cn(
|
||||
"pointer-events-none absolute inset-y-0 right-0 z-10 w-10 bg-gradient-to-l to-transparent",
|
||||
onLight ? "from-white" : "from-[#37a47a]"
|
||||
"pointer-events-none absolute inset-y-0 right-0 z-10 w-12 bg-gradient-to-l to-transparent md:w-16",
|
||||
onLight ? "from-[#e8f2ec]" : "from-[#37a47a]"
|
||||
)}
|
||||
/>
|
||||
<div
|
||||
className={cn(
|
||||
"marquee-winners pointer-events-auto flex w-max shrink-0 items-center gap-3 py-1",
|
||||
"marquee-winners pointer-events-auto flex w-max shrink-0 items-center py-1",
|
||||
isLarge ? "gap-3 md:gap-4" : "gap-3",
|
||||
isPaused && "marquee-winners-paused"
|
||||
)}
|
||||
>
|
||||
|
|
@ -118,6 +139,7 @@ export function LastYearWinnersScroll({ variant = "on-green", className }: Props
|
|||
tipKey={tipKey}
|
||||
company={company}
|
||||
variant={variant}
|
||||
size={size}
|
||||
isOpen={isOpen}
|
||||
isPinned={isPinned}
|
||||
onPin={() => pinTip(tipKey)}
|
||||
|
|
@ -128,6 +150,36 @@ export function LastYearWinnersScroll({ variant = "on-green", className }: Props
|
|||
})}
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
|
||||
if (onLight && isLarge) {
|
||||
return (
|
||||
<div className={cn("relative mt-auto w-full", className)}>
|
||||
<div className="hero-winners-cutout relative mx-auto w-full max-w-6xl">
|
||||
<div
|
||||
className={cn(
|
||||
"rounded-t-[1.75rem] border border-[#37a47a]/12 border-b-0",
|
||||
"bg-[#e8f2ec]/95 px-4 pt-7 pb-6 shadow-[0_-10px_40px_rgba(48,97,76,0.08)]",
|
||||
"md:rounded-t-[2.25rem] md:px-8 md:pt-8 md:pb-7"
|
||||
)}
|
||||
>
|
||||
{content}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
"pt-8",
|
||||
onLight ? "mt-8 border-t border-[#37a47a]/12" : "mt-10 border-t border-white/15",
|
||||
className
|
||||
)}
|
||||
>
|
||||
{content}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ export const faqs: FaqItem[] = [
|
|||
id: "when",
|
||||
question: "When and where does the summit take place?",
|
||||
answer:
|
||||
"The inaugural summit was held 31 January – 1 February 2025 at Skylight Hotel, Bole, Addis Ababa, Ethiopia. Future edition dates will be announced on this site.",
|
||||
"The summit takes place 21–22 February 2027 at Skylight Hotel, Bole, Addis Ababa, Ethiopia.",
|
||||
},
|
||||
{
|
||||
id: "who",
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ export const pageSeo = {
|
|||
home: {
|
||||
title: "Great Rift Valley Innovation Summit",
|
||||
description:
|
||||
"Ethiopia's premier innovation summit for agriculture, healthcare, and education. 31 Jan – 01 Feb 2025 at Skylight Hotel, Addis Ababa. Tickets, pitch grants, and partnerships.",
|
||||
"Ethiopia's premier innovation summit for agriculture, healthcare, and education. 21 Feb – 22 Feb 2027 at Skylight Hotel, Addis Ababa. Tickets, pitch grants, and partnerships.",
|
||||
path: "/",
|
||||
},
|
||||
program: {
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ const placeholderRow = (count: number) =>
|
|||
Array.from({ length: count }, (_, i) => placeholder(i + 1));
|
||||
|
||||
export const partnersIntro = {
|
||||
eyebrow: "Partners 2025",
|
||||
eyebrow: "Partners 2027",
|
||||
headline: "Meet the organizations that make GRV Summit possible",
|
||||
subheadline:
|
||||
"Partner logos and profiles below are placeholders — your brand could be featured here. Get in touch to secure a slot.",
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ export type ProgramDay = {
|
|||
export const programDays: ProgramDay[] = [
|
||||
{
|
||||
id: "day-1",
|
||||
date: "31 Jan 2025",
|
||||
date: "21 Feb 2027",
|
||||
title: "Workshops & Panel Discussions",
|
||||
description:
|
||||
"Curated sessions offering valuable insights for innovators and professionals at every career stage—from newcomers to seasoned executives.",
|
||||
|
|
@ -23,7 +23,7 @@ export const programDays: ProgramDay[] = [
|
|||
},
|
||||
{
|
||||
id: "day-2",
|
||||
date: "01 Feb 2025",
|
||||
date: "22 Feb 2027",
|
||||
title: "Exhibition & Pitch Finals",
|
||||
description:
|
||||
"Connect with investors, companies, and startups in the exhibitor hall. Watch finalists compete for Africa's largest non-dilutive grant pool.",
|
||||
|
|
|
|||
|
|
@ -5,9 +5,9 @@ export const site = {
|
|||
"Ethiopia's premier gathering for tech-enabled innovation in agriculture, healthcare, and education.",
|
||||
presentedBy: "Ethiopian Diaspora Trust Fund (EDTF)",
|
||||
dates: {
|
||||
label: "31 Jan – 01 Feb 2025",
|
||||
start: "2025-01-31",
|
||||
end: "2025-02-01",
|
||||
label: "21 Feb – 22 Feb 2027",
|
||||
start: "2027-02-21",
|
||||
end: "2027-02-22",
|
||||
},
|
||||
venue: {
|
||||
name: "Skylight Hotel",
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ export type TicketTier = {
|
|||
description: string;
|
||||
priceUsd: number;
|
||||
priceLabel?: string;
|
||||
/** e.g. "Day 2 — 01 Feb" for single-day passes */
|
||||
/** e.g. "Day 2 — 22 Feb" for single-day passes */
|
||||
scheduleLabel?: string;
|
||||
features: string[];
|
||||
soldOut?: boolean;
|
||||
|
|
@ -38,7 +38,7 @@ export const ticketTiers: TicketTier[] = [
|
|||
{
|
||||
id: "cocktail-pass",
|
||||
name: "Cocktail Pass",
|
||||
scheduleLabel: "Day 2 — 01 Feb 2025",
|
||||
scheduleLabel: "Day 2 — 22 Feb 2027",
|
||||
description:
|
||||
"Join the summit cocktail reception and evening networking on the second day at Skylight Hotel.",
|
||||
priceUsd: 75,
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
import { site } from "@/content/site";
|
||||
|
||||
/** Event times in Addis Ababa (EAT, UTC+3) */
|
||||
const EVENT_START = "20250131T080000";
|
||||
const EVENT_END = "20250201T180000";
|
||||
const EVENT_START = "20270221T080000";
|
||||
const EVENT_END = "20270222T180000";
|
||||
|
||||
function formatIcsDate(iso: string) {
|
||||
return iso.replace(/[-:]/g, "").split(".")[0] + "Z";
|
||||
|
|
@ -25,8 +25,8 @@ export function buildOutlookCalendarUrl() {
|
|||
path: "/calendar/action/compose",
|
||||
rru: "addevent",
|
||||
subject: site.name,
|
||||
startdt: "2025-01-31T08:00:00",
|
||||
enddt: "2025-02-01T18:00:00",
|
||||
startdt: "2027-02-21T08:00:00",
|
||||
enddt: "2027-02-22T18:00:00",
|
||||
body: site.tagline,
|
||||
location: `${site.venue.name}, ${site.venue.address}`,
|
||||
});
|
||||
|
|
@ -46,8 +46,8 @@ export function buildIcsFileContent() {
|
|||
"BEGIN:VEVENT",
|
||||
`UID:${uid}`,
|
||||
`DTSTAMP:${now}`,
|
||||
`DTSTART;TZID=Africa/Addis_Ababa:20250131T080000`,
|
||||
`DTEND;TZID=Africa/Addis_Ababa:20250201T180000`,
|
||||
`DTSTART;TZID=Africa/Addis_Ababa:20270221T080000`,
|
||||
`DTEND;TZID=Africa/Addis_Ababa:20270222T180000`,
|
||||
`SUMMARY:${site.name}`,
|
||||
`DESCRIPTION:${site.tagline.replace(/\n/g, "\\n")}`,
|
||||
`LOCATION:${site.venue.name}, ${site.venue.address}`,
|
||||
|
|
|
|||
|
|
@ -1,39 +1,8 @@
|
|||
import localFont from "next/font/local";
|
||||
|
||||
/** Antebas — primary site typeface (self-hosted from public/fonts). */
|
||||
export const antebas = localFont({
|
||||
src: [
|
||||
{
|
||||
path: "../../public/fonts/Fontspring-DEMO-antebas-thin.otf",
|
||||
weight: "100",
|
||||
style: "normal",
|
||||
},
|
||||
{
|
||||
path: "../../public/fonts/Fontspring-DEMO-antebas-light.otf",
|
||||
weight: "300",
|
||||
style: "normal",
|
||||
},
|
||||
{
|
||||
path: "../../public/fonts/Fontspring-DEMO-antebas-regular.otf",
|
||||
weight: "400",
|
||||
style: "normal",
|
||||
},
|
||||
{
|
||||
path: "../../public/fonts/Fontspring-DEMO-antebas-medium.otf",
|
||||
weight: "500",
|
||||
style: "normal",
|
||||
},
|
||||
{
|
||||
path: "../../public/fonts/Fontspring-DEMO-antebas-bold.otf",
|
||||
weight: "700",
|
||||
style: "normal",
|
||||
},
|
||||
{
|
||||
path: "../../public/fonts/Fontspring-DEMO-antebas-black.otf",
|
||||
weight: "900",
|
||||
style: "normal",
|
||||
},
|
||||
],
|
||||
variable: "--font-antebas",
|
||||
display: "swap",
|
||||
});
|
||||
/**
|
||||
* Antebas letterforms are declared in app/antebas.css (unicode-range limited).
|
||||
* DM Sans (--font-hero-body) renders numerals, punctuation, and symbols.
|
||||
* Replace DEMO .otf files with licensed Antebas to use the full glyph set.
|
||||
*/
|
||||
/** Site font stack — Antebas letters + DM Sans for numerals/symbols. */
|
||||
export const ANTEBAS_FONT_STACK =
|
||||
"Antebas, var(--font-hero-body), system-ui, sans-serif";
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user