This commit is contained in:
parent
06936e7a4f
commit
4ca1a4d497
|
|
@ -209,6 +209,9 @@
|
||||||
.marquee-winners:hover {
|
.marquee-winners:hover {
|
||||||
animation-play-state: paused;
|
animation-play-state: paused;
|
||||||
}
|
}
|
||||||
|
.marquee-winners-paused {
|
||||||
|
animation-play-state: paused !important;
|
||||||
|
}
|
||||||
|
|
||||||
@keyframes winner-impact-pulse {
|
@keyframes winner-impact-pulse {
|
||||||
0%,
|
0%,
|
||||||
|
|
@ -233,6 +236,9 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.winner-impact-value {
|
.winner-impact-value {
|
||||||
|
display: block;
|
||||||
|
max-width: 50%;
|
||||||
|
margin-inline: auto;
|
||||||
animation:
|
animation:
|
||||||
winner-impact-pulse 2.4s ease-in-out infinite,
|
winner-impact-pulse 2.4s ease-in-out infinite,
|
||||||
winner-impact-glow 2.4s ease-in-out infinite;
|
winner-impact-glow 2.4s ease-in-out infinite;
|
||||||
|
|
|
||||||
|
|
@ -12,9 +12,11 @@ const GRV_LOGO = "/branding/logo-icon.png";
|
||||||
type Props = {
|
type Props = {
|
||||||
company: LastYearWinner;
|
company: LastYearWinner;
|
||||||
tipKey: string;
|
tipKey: string;
|
||||||
isTipOpen: boolean;
|
isOpen: boolean;
|
||||||
onTipOpen: (key: string) => void;
|
isPinned: boolean;
|
||||||
onTipClose: () => void;
|
onPin: () => void;
|
||||||
|
onUnpin: () => void;
|
||||||
|
onHover: (key: string | null) => void;
|
||||||
variant?: "on-green" | "on-light";
|
variant?: "on-green" | "on-light";
|
||||||
className?: string;
|
className?: string;
|
||||||
};
|
};
|
||||||
|
|
@ -22,58 +24,71 @@ type Props = {
|
||||||
export function LastYearWinnerMark({
|
export function LastYearWinnerMark({
|
||||||
company,
|
company,
|
||||||
tipKey,
|
tipKey,
|
||||||
isTipOpen,
|
isOpen,
|
||||||
onTipOpen,
|
isPinned,
|
||||||
onTipClose,
|
onPin,
|
||||||
|
onUnpin,
|
||||||
|
onHover,
|
||||||
variant = "on-light",
|
variant = "on-light",
|
||||||
className,
|
className,
|
||||||
}: Props) {
|
}: Props) {
|
||||||
const closeTimer = useRef<ReturnType<typeof setTimeout> | null>(null);
|
const leaveTimer = useRef<ReturnType<typeof setTimeout> | null>(null);
|
||||||
const [failed, setFailed] = useState(false);
|
const [failed, setFailed] = useState(false);
|
||||||
const src = company.logoSrc ?? GRV_LOGO;
|
const src = company.logoSrc ?? GRV_LOGO;
|
||||||
const showImage = !failed && src;
|
const showImage = !failed && src;
|
||||||
const logoOnly = !company.name;
|
const logoOnly = !company.name;
|
||||||
const onLight = variant === "on-light";
|
const onLight = variant === "on-light";
|
||||||
|
|
||||||
const clearCloseTimer = useCallback(() => {
|
const clearLeaveTimer = useCallback(() => {
|
||||||
if (closeTimer.current) {
|
if (leaveTimer.current) {
|
||||||
clearTimeout(closeTimer.current);
|
clearTimeout(leaveTimer.current);
|
||||||
closeTimer.current = null;
|
leaveTimer.current = null;
|
||||||
}
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const handleEnter = useCallback(() => {
|
const handleEnter = useCallback(() => {
|
||||||
clearCloseTimer();
|
clearLeaveTimer();
|
||||||
onTipOpen(tipKey);
|
onHover(tipKey);
|
||||||
}, [clearCloseTimer, onTipOpen, tipKey]);
|
}, [clearLeaveTimer, onHover, tipKey]);
|
||||||
|
|
||||||
const handleLeave = useCallback(() => {
|
const handleLeave = useCallback(() => {
|
||||||
clearCloseTimer();
|
if (isPinned) return;
|
||||||
closeTimer.current = setTimeout(() => onTipClose(), 120);
|
clearLeaveTimer();
|
||||||
}, [clearCloseTimer, onTipClose]);
|
leaveTimer.current = setTimeout(() => onHover(null), 120);
|
||||||
|
}, [clearLeaveTimer, isPinned, onHover]);
|
||||||
|
|
||||||
|
const handleClick = useCallback(
|
||||||
|
(e: React.MouseEvent) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
onPin();
|
||||||
|
},
|
||||||
|
[onPin]
|
||||||
|
);
|
||||||
|
|
||||||
const handleOpenChange = useCallback(
|
const handleOpenChange = useCallback(
|
||||||
(open: boolean) => {
|
(open: boolean) => {
|
||||||
if (open) onTipOpen(tipKey);
|
if (!open && !isPinned) onHover(null);
|
||||||
else onTipClose();
|
|
||||||
},
|
},
|
||||||
[onTipOpen, onTipClose, tipKey]
|
[isPinned, onHover]
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Popover open={isTipOpen} onOpenChange={handleOpenChange}>
|
<Popover open={isOpen} onOpenChange={handleOpenChange}>
|
||||||
<PopoverTrigger asChild>
|
<PopoverTrigger asChild>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
|
data-winner-interactive
|
||||||
className={cn(
|
className={cn(
|
||||||
"shrink-0 rounded-lg outline-none focus-visible:ring-2 focus-visible:ring-[#1a5c38]/40",
|
"shrink-0 rounded-lg outline-none focus-visible:ring-2 focus-visible:ring-[#1a5c38]/40",
|
||||||
isTipOpen && "relative z-30"
|
isOpen && "relative z-30"
|
||||||
)}
|
)}
|
||||||
aria-label={
|
aria-label={
|
||||||
company.name
|
company.name
|
||||||
? `${company.name} — hover for impact details`
|
? `${company.name} — tap to read impact details`
|
||||||
: "GRV alumni — hover for impact details"
|
: "GRV alumni — tap to read impact details"
|
||||||
}
|
}
|
||||||
|
aria-expanded={isOpen}
|
||||||
|
onClick={handleClick}
|
||||||
onMouseEnter={handleEnter}
|
onMouseEnter={handleEnter}
|
||||||
onMouseLeave={handleLeave}
|
onMouseLeave={handleLeave}
|
||||||
onFocus={handleEnter}
|
onFocus={handleEnter}
|
||||||
|
|
@ -86,10 +101,11 @@ export function LastYearWinnerMark({
|
||||||
? "border-[#1a5c38]/15 bg-white/95 hover:border-[#1a5c38]/30 hover:bg-white"
|
? "border-[#1a5c38]/15 bg-white/95 hover:border-[#1a5c38]/30 hover:bg-white"
|
||||||
: "border-white/25 bg-white/90 hover:border-white/50 hover:bg-white",
|
: "border-white/25 bg-white/90 hover:border-white/50 hover:bg-white",
|
||||||
logoOnly && "px-2",
|
logoOnly && "px-2",
|
||||||
isTipOpen &&
|
isOpen &&
|
||||||
(onLight
|
(onLight
|
||||||
? "border-[#1a5c38]/40 ring-2 ring-[#1a5c38]/15"
|
? "border-[#1a5c38]/40 ring-2 ring-[#1a5c38]/15"
|
||||||
: "border-white/60 ring-2 ring-white/25"),
|
: "border-white/60 ring-2 ring-white/25"),
|
||||||
|
isPinned && "border-[#ffb300]/70 ring-2 ring-[#ffb300]/25",
|
||||||
className
|
className
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
|
|
@ -125,17 +141,25 @@ export function LastYearWinnerMark({
|
||||||
</div>
|
</div>
|
||||||
</button>
|
</button>
|
||||||
</PopoverTrigger>
|
</PopoverTrigger>
|
||||||
{isTipOpen ? (
|
{isOpen ? (
|
||||||
<PopoverContent
|
<PopoverContent
|
||||||
side="top"
|
side="top"
|
||||||
align="center"
|
align="center"
|
||||||
sideOffset={8}
|
sideOffset={8}
|
||||||
className="z-50 border-[#1a5c38]/15 bg-white p-0 shadow-lg"
|
collisionPadding={12}
|
||||||
|
data-winner-interactive
|
||||||
|
className="z-50 w-72 max-w-[calc(100vw-2rem)] shrink-0 overflow-hidden border-[#1a5c38]/15 bg-white p-0 shadow-lg"
|
||||||
onMouseEnter={handleEnter}
|
onMouseEnter={handleEnter}
|
||||||
onMouseLeave={handleLeave}
|
onMouseLeave={handleLeave}
|
||||||
onOpenAutoFocus={(e) => e.preventDefault()}
|
onOpenAutoFocus={(e) => e.preventDefault()}
|
||||||
|
onPointerDownOutside={(e) => {
|
||||||
|
if (isPinned) e.preventDefault();
|
||||||
|
}}
|
||||||
|
onInteractOutside={(e) => {
|
||||||
|
if (isPinned) e.preventDefault();
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<LastYearWinnerTip company={company} />
|
<LastYearWinnerTip company={company} onClose={onUnpin} showClose={isPinned} />
|
||||||
</PopoverContent>
|
</PopoverContent>
|
||||||
) : null}
|
) : null}
|
||||||
</Popover>
|
</Popover>
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { ExternalLink, HandHeart } from "lucide-react";
|
import { ExternalLink, HandHeart, X } from "lucide-react";
|
||||||
import { getWinnerImpact, type LastYearWinner } from "@/content/last-year-winners";
|
import { getWinnerImpact, type LastYearWinner } from "@/content/last-year-winners";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
|
|
@ -11,6 +11,8 @@ import { cn } from "@/lib/utils";
|
||||||
type Props = {
|
type Props = {
|
||||||
company: LastYearWinner;
|
company: LastYearWinner;
|
||||||
className?: string;
|
className?: string;
|
||||||
|
showClose?: boolean;
|
||||||
|
onClose?: () => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
function externalLinkProps(href: string) {
|
function externalLinkProps(href: string) {
|
||||||
|
|
@ -55,7 +57,7 @@ function FounderPhoto({ company }: { company: LastYearWinner }) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function LastYearWinnerTip({ company, className }: Props) {
|
export function LastYearWinnerTip({ company, className, showClose, onClose }: Props) {
|
||||||
const impact = getWinnerImpact(company);
|
const impact = getWinnerImpact(company);
|
||||||
const headline = impact.metrics.find((m) => m.highlight) ?? impact.metrics[0];
|
const headline = impact.metrics.find((m) => m.highlight) ?? impact.metrics[0];
|
||||||
const supporting = impact.metrics.filter((m) => m !== headline).slice(0, 2);
|
const supporting = impact.metrics.filter((m) => m !== headline).slice(0, 2);
|
||||||
|
|
@ -63,9 +65,11 @@ export function LastYearWinnerTip({ company, className }: Props) {
|
||||||
const viewHref = impact.links.view ?? impact.links.website;
|
const viewHref = impact.links.view ?? impact.links.website;
|
||||||
const donateHref = impact.links.donate;
|
const donateHref = impact.links.donate;
|
||||||
|
|
||||||
|
const showFooter = showClose || viewHref || donateHref;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={cn("w-[min(calc(100vw-2rem),16.5rem)]", className)}>
|
<div className={cn("box-border w-72 max-w-full shrink-0 overflow-hidden", className)}>
|
||||||
<div className="flex gap-3 px-3 pt-3 pb-2">
|
<div className="flex min-w-0 gap-2.5 px-3 pt-3 pb-2">
|
||||||
<FounderPhoto company={company} />
|
<FounderPhoto company={company} />
|
||||||
<div className="min-w-0 flex-1">
|
<div className="min-w-0 flex-1">
|
||||||
<p className="truncate text-[11px] font-semibold uppercase tracking-wider text-[#1a5c38]">
|
<p className="truncate text-[11px] font-semibold uppercase tracking-wider text-[#1a5c38]">
|
||||||
|
|
@ -81,56 +85,74 @@ export function LastYearWinnerTip({ company, className }: Props) {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{headline ? (
|
{headline ? (
|
||||||
<div className="mx-3 mb-2 rounded-lg border border-[#1a5c38]/12 bg-[#f0f5f2] px-3 py-2.5 text-center">
|
<div className="mx-3 mb-2 overflow-hidden rounded-lg border border-[#1a5c38]/12 bg-[#f0f5f2] px-2.5 py-2 text-center">
|
||||||
<p className="text-[10px] font-bold uppercase tracking-wider text-[#1a5c38]/75">
|
<p className="truncate text-[10px] font-bold uppercase tracking-wider text-[#1a5c38]/75">
|
||||||
{headline.label}
|
{headline.label}
|
||||||
</p>
|
</p>
|
||||||
<p className="winner-impact-value mt-0.5 font-display text-2xl font-extrabold tracking-tight text-[#0d3d26]">
|
<p className="winner-impact-value mt-0.5 truncate font-display text-xl font-extrabold tracking-tight text-[#0d3d26]">
|
||||||
{headline.value}
|
{headline.value}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
|
|
||||||
{supporting.length > 0 ? (
|
{supporting.length > 0 ? (
|
||||||
<dl className="grid grid-cols-2 gap-1.5 px-3 pb-2">
|
<dl className="grid min-w-0 grid-cols-2 gap-1.5 px-3 pb-2">
|
||||||
{supporting.map((m) => (
|
{supporting.map((m) => (
|
||||||
<div key={m.label} className="rounded-md bg-[#f7faf8] px-2 py-1.5 text-center">
|
<div key={m.label} className="min-w-0 rounded-md bg-[#f7faf8] px-1.5 py-1.5 text-center">
|
||||||
<dt className="text-[9px] font-semibold uppercase tracking-wide text-[#1a5c38]/65">
|
<dt className="truncate text-[9px] font-semibold uppercase tracking-wide text-[#1a5c38]/65">
|
||||||
{m.label}
|
{m.label}
|
||||||
</dt>
|
</dt>
|
||||||
<dd className="text-xs font-bold text-[#0d3d26]">{m.value}</dd>
|
<dd className="truncate text-xs font-bold text-[#0d3d26]">{m.value}</dd>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</dl>
|
</dl>
|
||||||
) : null}
|
) : null}
|
||||||
|
|
||||||
{(viewHref || donateHref) && (
|
{showFooter ? (
|
||||||
<div className="flex items-center justify-end gap-2 border-t border-[#1a5c38]/10 bg-[#f7faf8]/80 px-3 py-2">
|
<div
|
||||||
{viewHref ? (
|
className={cn(
|
||||||
<Button
|
"flex w-full min-w-0 items-center gap-1.5 border-t border-[#1a5c38]/10 bg-[#f7faf8]/80 px-2.5 py-2",
|
||||||
variant="outline"
|
showClose && onClose ? "justify-between" : "justify-end"
|
||||||
size="icon-sm"
|
)}
|
||||||
className="rounded-full border-[#1a5c38]/20 text-[#1a5c38] hover:bg-[#f0f5f2]"
|
>
|
||||||
asChild
|
{showClose && onClose ? (
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
data-winner-interactive
|
||||||
|
onClick={onClose}
|
||||||
|
className="inline-flex size-7 shrink-0 items-center justify-center rounded-full border border-[#1a5c38]/15 bg-white text-[#1a5c38] shadow-sm transition-colors hover:bg-[#f0f5f2]"
|
||||||
|
aria-label="Close impact details"
|
||||||
>
|
>
|
||||||
<Link href={viewHref} aria-label="View startup" {...externalLinkProps(viewHref)}>
|
<X className="size-3.5" aria-hidden />
|
||||||
<ExternalLink className="size-4" />
|
</button>
|
||||||
</Link>
|
|
||||||
</Button>
|
|
||||||
) : null}
|
|
||||||
{donateHref ? (
|
|
||||||
<Button
|
|
||||||
size="icon-sm"
|
|
||||||
className="rounded-full bg-[#1a5c38] text-white hover:bg-[#0d3d26]"
|
|
||||||
asChild
|
|
||||||
>
|
|
||||||
<Link href={donateHref} aria-label="Donate" {...externalLinkProps(donateHref)}>
|
|
||||||
<HandHeart className="size-4" />
|
|
||||||
</Link>
|
|
||||||
</Button>
|
|
||||||
) : null}
|
) : null}
|
||||||
|
<div className="flex shrink-0 items-center gap-1.5">
|
||||||
|
{viewHref ? (
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
size="icon-sm"
|
||||||
|
className="rounded-full border-[#1a5c38]/20 text-[#1a5c38] hover:bg-[#f0f5f2]"
|
||||||
|
asChild
|
||||||
|
>
|
||||||
|
<Link href={viewHref} aria-label="View startup" {...externalLinkProps(viewHref)}>
|
||||||
|
<ExternalLink className="size-4" />
|
||||||
|
</Link>
|
||||||
|
</Button>
|
||||||
|
) : null}
|
||||||
|
{donateHref ? (
|
||||||
|
<Button
|
||||||
|
size="icon-sm"
|
||||||
|
className="rounded-full bg-[#1a5c38] text-white hover:bg-[#0d3d26]"
|
||||||
|
asChild
|
||||||
|
>
|
||||||
|
<Link href={donateHref} aria-label="Donate" {...externalLinkProps(donateHref)}>
|
||||||
|
<HandHeart className="size-4" />
|
||||||
|
</Link>
|
||||||
|
</Button>
|
||||||
|
) : null}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useCallback, useState } from "react";
|
import { useCallback, useEffect, useState } from "react";
|
||||||
import { lastYearWinners, lastYearWinnersCopy } from "@/content/last-year-winners";
|
import { lastYearWinners, lastYearWinnersCopy } from "@/content/last-year-winners";
|
||||||
import { LastYearWinnerMark } from "@/components/home/LastYearWinnerMark";
|
import { LastYearWinnerMark } from "@/components/home/LastYearWinnerMark";
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
|
|
@ -13,16 +13,50 @@ type Props = {
|
||||||
export function LastYearWinnersScroll({ variant = "on-green", className }: Props) {
|
export function LastYearWinnersScroll({ variant = "on-green", className }: Props) {
|
||||||
const items = [...lastYearWinners, ...lastYearWinners];
|
const items = [...lastYearWinners, ...lastYearWinners];
|
||||||
const onLight = variant === "on-light";
|
const onLight = variant === "on-light";
|
||||||
const [activeTipKey, setActiveTipKey] = useState<string | null>(null);
|
const [hoverKey, setHoverKey] = useState<string | null>(null);
|
||||||
|
const [pinnedKey, setPinnedKey] = useState<string | null>(null);
|
||||||
|
|
||||||
const openTip = useCallback((key: string) => {
|
const isPaused = pinnedKey !== null;
|
||||||
setActiveTipKey(key);
|
|
||||||
|
const pinTip = useCallback((key: string) => {
|
||||||
|
setPinnedKey(key);
|
||||||
|
setHoverKey(null);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const closeTip = useCallback(() => {
|
const unpinTip = useCallback(() => {
|
||||||
setActiveTipKey(null);
|
setPinnedKey(null);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
const setHover = useCallback(
|
||||||
|
(key: string | null) => {
|
||||||
|
if (pinnedKey) return;
|
||||||
|
setHoverKey(key);
|
||||||
|
},
|
||||||
|
[pinnedKey]
|
||||||
|
);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!pinnedKey) return;
|
||||||
|
|
||||||
|
const dismiss = () => setPinnedKey(null);
|
||||||
|
|
||||||
|
const onPointerDown = (e: PointerEvent) => {
|
||||||
|
const target = e.target as HTMLElement;
|
||||||
|
if (target.closest("[data-winner-interactive]")) return;
|
||||||
|
dismiss();
|
||||||
|
};
|
||||||
|
|
||||||
|
const onScroll = () => dismiss();
|
||||||
|
|
||||||
|
document.addEventListener("pointerdown", onPointerDown, true);
|
||||||
|
window.addEventListener("scroll", onScroll, true);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
document.removeEventListener("pointerdown", onPointerDown, true);
|
||||||
|
window.removeEventListener("scroll", onScroll, true);
|
||||||
|
};
|
||||||
|
}, [pinnedKey]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
|
|
@ -67,18 +101,28 @@ export function LastYearWinnersScroll({ variant = "on-green", className }: Props
|
||||||
onLight ? "from-white" : "from-[#1a5c38]"
|
onLight ? "from-white" : "from-[#1a5c38]"
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
<div className="marquee-winners pointer-events-auto flex w-max shrink-0 items-center gap-3 py-1">
|
<div
|
||||||
|
className={cn(
|
||||||
|
"marquee-winners pointer-events-auto flex w-max shrink-0 items-center gap-3 py-1",
|
||||||
|
isPaused && "marquee-winners-paused"
|
||||||
|
)}
|
||||||
|
>
|
||||||
{items.map((company, i) => {
|
{items.map((company, i) => {
|
||||||
const tipKey = `${company.id}-${i}`;
|
const tipKey = `${company.id}-${i}`;
|
||||||
|
const isPinned = pinnedKey === tipKey;
|
||||||
|
const isOpen = isPinned || (!pinnedKey && hoverKey === tipKey);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<LastYearWinnerMark
|
<LastYearWinnerMark
|
||||||
key={tipKey}
|
key={tipKey}
|
||||||
tipKey={tipKey}
|
tipKey={tipKey}
|
||||||
company={company}
|
company={company}
|
||||||
variant={variant}
|
variant={variant}
|
||||||
isTipOpen={activeTipKey === tipKey}
|
isOpen={isOpen}
|
||||||
onTipOpen={openTip}
|
isPinned={isPinned}
|
||||||
onTipClose={closeTip}
|
onPin={() => pinTip(tipKey)}
|
||||||
|
onUnpin={unpinTip}
|
||||||
|
onHover={setHover}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,7 @@ export type LastYearWinner = {
|
||||||
export const lastYearWinnersCopy = {
|
export const lastYearWinnersCopy = {
|
||||||
eyebrow: "Last year's summit",
|
eyebrow: "Last year's summit",
|
||||||
headline: "18+ companies supported",
|
headline: "18+ companies supported",
|
||||||
hoverHint: "Hover a company to see summit impact",
|
hoverHint: "Tap a company to read impact details",
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
const GRV_LOGO = "/branding/logo-icon.png";
|
const GRV_LOGO = "/branding/logo-icon.png";
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user