Fix brackets on homepage.
This commit is contained in:
parent
924ad5caee
commit
886f9f1847
1 changed files with 132 additions and 114 deletions
|
|
@ -31,7 +31,7 @@ function getInterval(tick: number): number {
|
||||||
}
|
}
|
||||||
|
|
||||||
function SlotMachineHeadline() {
|
function SlotMachineHeadline() {
|
||||||
const [text, setText] = useState("\u00A0");
|
const [text, setText] = useState(SPORTS[0]);
|
||||||
const [landed, setLanded] = useState(false);
|
const [landed, setLanded] = useState(false);
|
||||||
const [animKey, setAnimKey] = useState(0);
|
const [animKey, setAnimKey] = useState(0);
|
||||||
const tickRef = useRef(0);
|
const tickRef = useRef(0);
|
||||||
|
|
@ -82,129 +82,130 @@ function SlotMachineHeadline() {
|
||||||
|
|
||||||
// ─── Bracket Decoration ──────────────────────────────────────────────────────
|
// ─── Bracket Decoration ──────────────────────────────────────────────────────
|
||||||
|
|
||||||
const LIME = "#adf661";
|
function BracketDecor({ flip = false, mobileTall = false }: { flip?: boolean; mobileTall?: boolean }) {
|
||||||
const LIME_DIM = "rgba(173,246,97,0.07)";
|
const base = flip ? "r" : "l";
|
||||||
const LIME_DIM_BORDER = "rgba(173,246,97,0.25)";
|
const id = mobileTall ? `${base}-m` : base;
|
||||||
const LIME_CHAMP = "rgba(173,246,97,0.12)";
|
const W = 399;
|
||||||
const LIME_CHAMP_BORDER = "rgba(173,246,97,0.8)";
|
const H = mobileTall ? 2000 : 560;
|
||||||
const SLOT_BG = "rgba(32,33,36,0.95)";
|
|
||||||
const SLOT_BORDER = "rgba(255,255,255,0.10)";
|
|
||||||
const LINE_COLOR_R1 = "rgba(255,255,255,0.15)";
|
|
||||||
const LINE_COLOR_R2 = "rgba(255,255,255,0.28)";
|
|
||||||
|
|
||||||
// SVG coordinate-space layout constants
|
const pad = 36;
|
||||||
const SW = 80;
|
const spacing = (H - 2 * pad) / 7;
|
||||||
const SH = 20;
|
const r1Ys = Array.from({ length: 8 }, (_, i) => pad + i * spacing);
|
||||||
const GAP = 10;
|
const r2Ys = [0, 1, 2, 3].map((i) => (r1Ys[i * 2] + r1Ys[i * 2 + 1]) / 2);
|
||||||
const PAIR_H = SH * 2 + GAP;
|
const r3Ys = [0, 1].map((i) => (r2Ys[i * 2] + r2Ys[i * 2 + 1]) / 2);
|
||||||
const R1_X = 10;
|
const champY = (r3Ys[0] + r3Ys[1]) / 2;
|
||||||
const R2_X = 120;
|
|
||||||
const R3_X = 210;
|
|
||||||
const BRACKET_W = 300;
|
|
||||||
const BRACKET_H = 360;
|
|
||||||
const R1_TOP = 50;
|
|
||||||
const PAIR_GAP = 20;
|
|
||||||
|
|
||||||
type SlotDef = {
|
const x0 = 4;
|
||||||
x: number; y: number; w: number; h: number;
|
const x1 = 77;
|
||||||
winner?: boolean; champ?: boolean;
|
const x2 = 161;
|
||||||
};
|
const x3 = 252;
|
||||||
|
|
||||||
function BracketSlot({ x, y, w, h, winner, champ }: SlotDef) {
|
const gradId = `grad-${id}`;
|
||||||
const bg = champ ? LIME_CHAMP : winner ? LIME_DIM : SLOT_BG;
|
const glowId = `glow-${id}`;
|
||||||
const border = champ ? LIME_CHAMP_BORDER : winner ? LIME_DIM_BORDER : SLOT_BORDER;
|
const finalsGlowId = `finals-glow-${id}`;
|
||||||
const bw = champ ? 1.5 : 1;
|
const finalsHaloId = `finals-halo-${id}`;
|
||||||
return (
|
const finalsFadeMaskGradId = `finals-fade-mask-grad-${id}`;
|
||||||
<g>
|
const finalsFadeMaskId = `finals-fade-mask-${id}`;
|
||||||
<rect x={x} y={y} width={w} height={h} rx={2} fill={bg} stroke={border} strokeWidth={bw} />
|
const finalsLineGlowId = `finals-line-glow-${id}`;
|
||||||
{(winner || champ) && (
|
|
||||||
<rect x={x} y={y} width={3} height={h} rx={1} fill={LIME} />
|
|
||||||
)}
|
|
||||||
{champ && (
|
|
||||||
<text
|
|
||||||
x={x + w / 2} y={y + h + 10}
|
|
||||||
textAnchor="middle"
|
|
||||||
fontSize={7} fontWeight={700} fill={LIME}
|
|
||||||
style={{ fontFamily: "Barlow, sans-serif", letterSpacing: "0.08em" }}
|
|
||||||
>
|
|
||||||
CHAMP
|
|
||||||
</text>
|
|
||||||
)}
|
|
||||||
</g>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function hLine(x1: number, y1: number, x2: number, color: string) {
|
|
||||||
return <line x1={x1} y1={y1} x2={x2} y2={y1} stroke={color} strokeWidth={1} />;
|
|
||||||
}
|
|
||||||
|
|
||||||
function connector(ax: number, ay: number, bx: number, by: number, cx: number, color: string) {
|
|
||||||
const midY = (ay + by) / 2;
|
|
||||||
return (
|
|
||||||
<path d={`M${ax} ${ay} H${cx} V${midY} H${bx}`} fill="none" stroke={color} strokeWidth={1} />
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function BracketDecor({ flip = false }: { flip?: boolean }) {
|
|
||||||
const r1Pairs: [SlotDef, SlotDef][] = Array.from({ length: 4 }, (_, i) => {
|
|
||||||
const pairY = R1_TOP + i * (PAIR_H + PAIR_GAP);
|
|
||||||
const winner = i % 3 !== 1;
|
|
||||||
return [
|
|
||||||
{ x: R1_X, y: pairY, w: SW, h: SH, winner: winner && i % 2 === 0 },
|
|
||||||
{ x: R1_X, y: pairY + SH + GAP, w: SW, h: SH, winner: winner && i % 2 !== 0 },
|
|
||||||
];
|
|
||||||
});
|
|
||||||
|
|
||||||
const r2Slots: SlotDef[] = [0, 1].map((i) => {
|
|
||||||
const top1Y = R1_TOP + (i * 2) * (PAIR_H + PAIR_GAP);
|
|
||||||
const bot2Y = R1_TOP + (i * 2 + 1) * (PAIR_H + PAIR_GAP) + SH + GAP;
|
|
||||||
const midY = (top1Y + bot2Y) / 2 - SH / 2;
|
|
||||||
return { x: R2_X, y: midY, w: SW, h: SH, winner: i === 0 };
|
|
||||||
});
|
|
||||||
|
|
||||||
const r3Y = (r2Slots[0].y + r2Slots[1].y + SH) / 2 - SH / 2;
|
|
||||||
const r3Slot: SlotDef = { x: R3_X, y: r3Y, w: SW, h: SH + 4, champ: true };
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<svg
|
<svg
|
||||||
width={BRACKET_W} height={BRACKET_H}
|
viewBox={`0 0 ${W} ${H}`}
|
||||||
style={{
|
style={{
|
||||||
position: "absolute",
|
position: "absolute",
|
||||||
top: "50%",
|
top: "50%",
|
||||||
transform: `translateY(-52%)${flip ? " scaleX(-1)" : ""}`,
|
transform: `translateY(-52%)${flip ? " scaleX(-1)" : ""}`,
|
||||||
[flip ? "right" : "left"]: 0,
|
[flip ? "right" : "left"]: 0,
|
||||||
opacity: 0.75,
|
|
||||||
pointerEvents: "none",
|
pointerEvents: "none",
|
||||||
|
height: mobileTall ? "auto" : "75vh",
|
||||||
|
width: mobileTall ? "32vw" : "auto",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{r1Pairs.map(([a, b], i) => (
|
<defs>
|
||||||
<g key={`r1-${a.y}`}>
|
<linearGradient id={gradId} x1="0" y1="0" x2="0" y2={H} gradientUnits="userSpaceOnUse">
|
||||||
<BracketSlot {...a} />
|
<stop offset="0%" stopColor="#adf661" />
|
||||||
<BracketSlot {...b} />
|
<stop offset="100%" stopColor="#2ce1c1" />
|
||||||
{connector(
|
</linearGradient>
|
||||||
R1_X + SW, a.y + SH / 2,
|
<filter id={glowId} x="-50%" y="-50%" width="200%" height="200%">
|
||||||
r2Slots[Math.floor(i / 2)].x, r2Slots[Math.floor(i / 2)].y + SH / 2,
|
<feGaussianBlur stdDeviation="3" result="blur" />
|
||||||
R1_X + SW + 14,
|
<feMerge>
|
||||||
LINE_COLOR_R1
|
<feMergeNode in="blur" />
|
||||||
)}
|
<feMergeNode in="SourceGraphic" />
|
||||||
{hLine(R1_X + SW + 14, (a.y + SH / 2 + b.y + SH / 2) / 2, R2_X, LINE_COLOR_R1)}
|
</feMerge>
|
||||||
</g>
|
</filter>
|
||||||
))}
|
<filter id={finalsGlowId} filterUnits="userSpaceOnUse"
|
||||||
|
x={x2 - 15} y={r3Ys[0] - 15} width={x3 - x2 + 30} height={r3Ys[1] - r3Ys[0] + 30}>
|
||||||
{r2Slots.map((s, ri) => (
|
<feGaussianBlur stdDeviation="7" result="blur" />
|
||||||
<g key={`r2-${s.y}`}>
|
<feMerge>
|
||||||
<BracketSlot {...s} />
|
<feMergeNode in="blur" />
|
||||||
{connector(
|
<feMergeNode in="SourceGraphic" />
|
||||||
R2_X + SW, r2Slots[0].y + SH / 2,
|
</feMerge>
|
||||||
r3Slot.x, r3Slot.y + (r3Slot.h ?? SH) / 2,
|
</filter>
|
||||||
R2_X + SW + 14,
|
<filter id={finalsHaloId} filterUnits="userSpaceOnUse"
|
||||||
LINE_COLOR_R2
|
x={x2 - 40} y={r3Ys[0] - 40} width={x3 - x2 + 80} height={r3Ys[1] - r3Ys[0] + 80}>
|
||||||
)}
|
<feGaussianBlur stdDeviation="16" />
|
||||||
{ri === 1 && hLine(R2_X + SW + 14, (r2Slots[0].y + SH / 2 + r2Slots[1].y + SH / 2) / 2, R3_X, LINE_COLOR_R2)}
|
</filter>
|
||||||
</g>
|
<linearGradient id={finalsFadeMaskGradId} x1={x3} y1="0" x2={W} y2="0" gradientUnits="userSpaceOnUse">
|
||||||
))}
|
<stop offset="0%" stopColor="white" />
|
||||||
|
<stop offset="50%" stopColor="white" />
|
||||||
<BracketSlot {...r3Slot} />
|
<stop offset="100%" stopColor="white" stopOpacity="0" />
|
||||||
|
</linearGradient>
|
||||||
|
<mask id={finalsFadeMaskId} maskUnits="userSpaceOnUse" x={x3} y={champY - 20} width={W - x3} height={40}>
|
||||||
|
<rect x="0" y="0" width={W} height={H} fill={`url(#${finalsFadeMaskGradId})`} />
|
||||||
|
</mask>
|
||||||
|
<filter id={finalsLineGlowId} filterUnits="userSpaceOnUse" x={x3 - 10} y={champY - 15} width={W - x3 + 20} height={30}>
|
||||||
|
<feGaussianBlur stdDeviation="3" result="blur" />
|
||||||
|
<feMerge>
|
||||||
|
<feMergeNode in="blur" />
|
||||||
|
<feMergeNode in="SourceGraphic" />
|
||||||
|
</feMerge>
|
||||||
|
</filter>
|
||||||
|
</defs>
|
||||||
|
<g filter={`url(#${finalsHaloId})`} stroke={`url(#${gradId})`} strokeWidth={2} fill="none" opacity={0.75}>
|
||||||
|
{r3Ys.map((midY) => (
|
||||||
|
<line key={midY} x1={x2} y1={midY} x2={x3} y2={midY} />
|
||||||
|
))}
|
||||||
|
<line x1={x3} y1={r3Ys[0]} x2={x3} y2={r3Ys[1]} />
|
||||||
|
</g>
|
||||||
|
<g filter={`url(#${finalsGlowId})`} stroke={`url(#${gradId})`} strokeWidth={1.5} fill="none">
|
||||||
|
{r3Ys.map((midY) => (
|
||||||
|
<line key={midY} x1={x2} y1={midY} x2={x3} y2={midY} />
|
||||||
|
))}
|
||||||
|
<line x1={x3} y1={r3Ys[0]} x2={x3} y2={r3Ys[1]} />
|
||||||
|
</g>
|
||||||
|
<g filter={`url(#${glowId})`} stroke={`url(#${gradId})`} strokeWidth={1.5} fill="none">
|
||||||
|
{r1Ys.map((y) => (
|
||||||
|
<g key={y}>
|
||||||
|
<circle cx={x0} cy={y} r={3} fill={`url(#${gradId})`} stroke="none" />
|
||||||
|
<line x1={x0} y1={y} x2={x1} y2={y} />
|
||||||
|
</g>
|
||||||
|
))}
|
||||||
|
{r2Ys.map((midY, i) => (
|
||||||
|
<g key={midY}>
|
||||||
|
<line x1={x1} y1={r1Ys[i * 2]} x2={x1} y2={r1Ys[i * 2 + 1]} />
|
||||||
|
<circle cx={x1} cy={midY} r={3} fill={`url(#${gradId})`} stroke="none" />
|
||||||
|
<line x1={x1} y1={midY} x2={x2} y2={midY} />
|
||||||
|
</g>
|
||||||
|
))}
|
||||||
|
{r3Ys.map((midY, i) => (
|
||||||
|
<g key={midY}>
|
||||||
|
<line x1={x2} y1={r2Ys[i * 2]} x2={x2} y2={r2Ys[i * 2 + 1]} />
|
||||||
|
<circle cx={x2} cy={midY} r={3} fill={`url(#${gradId})`} stroke="none" />
|
||||||
|
<line x1={x2} y1={midY} x2={x3} y2={midY} />
|
||||||
|
</g>
|
||||||
|
))}
|
||||||
|
<line x1={x3} y1={r3Ys[0]} x2={x3} y2={r3Ys[1]} />
|
||||||
|
<circle cx={x3} cy={champY} r={3} fill={`url(#${gradId})`} stroke="none" />
|
||||||
|
</g>
|
||||||
|
<line
|
||||||
|
x1={x3} y1={champY} x2={W} y2={champY}
|
||||||
|
stroke={`url(#${gradId})`}
|
||||||
|
strokeWidth={1.5}
|
||||||
|
fill="none"
|
||||||
|
filter={`url(#${finalsLineGlowId})`}
|
||||||
|
mask={`url(#${finalsFadeMaskId})`}
|
||||||
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -303,6 +304,7 @@ const FOOTER_LINKS = [
|
||||||
{ label: "Home", to: "/" },
|
{ label: "Home", to: "/" },
|
||||||
{ label: "Rules", to: "/rules" },
|
{ label: "Rules", to: "/rules" },
|
||||||
{ label: "How to Play", to: "/how-to-play" },
|
{ label: "How to Play", to: "/how-to-play" },
|
||||||
|
{ label: "Support", to: "/support" },
|
||||||
{ label: "Privacy Policy", to: "/privacy-policy" },
|
{ label: "Privacy Policy", to: "/privacy-policy" },
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
@ -345,7 +347,23 @@ export function LandingPage() {
|
||||||
className="relative flex flex-col items-center justify-center overflow-hidden px-8"
|
className="relative flex flex-col items-center justify-center overflow-hidden px-8"
|
||||||
style={{ minHeight: "calc(100vh - var(--navbar-height))" }}
|
style={{ minHeight: "calc(100vh - var(--navbar-height))" }}
|
||||||
>
|
>
|
||||||
{/* Bracket decorations — lg+ only */}
|
{/* Dot-grid texture */}
|
||||||
|
<div
|
||||||
|
aria-hidden
|
||||||
|
className="pointer-events-none absolute inset-0"
|
||||||
|
style={{
|
||||||
|
backgroundImage: "radial-gradient(circle, rgba(255,255,255,0.07) 1px, transparent 1px)",
|
||||||
|
backgroundSize: "32px 32px",
|
||||||
|
maskImage: "radial-gradient(ellipse 85% 85% at 50% 50%, black 30%, transparent 100%)",
|
||||||
|
WebkitMaskImage: "radial-gradient(ellipse 85% 85% at 50% 50%, black 30%, transparent 100%)",
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* Bracket decorations */}
|
||||||
|
<div className="lg:hidden">
|
||||||
|
<BracketDecor mobileTall />
|
||||||
|
<BracketDecor flip mobileTall />
|
||||||
|
</div>
|
||||||
<div className="hidden lg:block">
|
<div className="hidden lg:block">
|
||||||
<BracketDecor />
|
<BracketDecor />
|
||||||
<BracketDecor flip />
|
<BracketDecor flip />
|
||||||
|
|
@ -354,7 +372,7 @@ export function LandingPage() {
|
||||||
<ScrollHint />
|
<ScrollHint />
|
||||||
|
|
||||||
{/* Center content */}
|
{/* Center content */}
|
||||||
<div className="relative z-10 w-full max-w-[680px] text-center">
|
<div className="relative z-10 w-[80%] lg:w-full max-w-[680px] text-center px-6 py-4 lg:px-0 lg:py-0">
|
||||||
<h1
|
<h1
|
||||||
className="mb-0 leading-[0.95] tracking-[-0.04em]"
|
className="mb-0 leading-[0.95] tracking-[-0.04em]"
|
||||||
style={{ fontSize: "clamp(38px, 8vw, 88px)", fontWeight: 900 }}
|
style={{ fontSize: "clamp(38px, 8vw, 88px)", fontWeight: 900 }}
|
||||||
|
|
@ -365,10 +383,10 @@ export function LandingPage() {
|
||||||
|
|
||||||
<p
|
<p
|
||||||
className="mx-auto mt-8 text-muted-foreground"
|
className="mx-auto mt-8 text-muted-foreground"
|
||||||
style={{ fontSize: "16px", fontWeight: 400, lineHeight: 1.65, maxWidth: "460px" }}
|
style={{ fontSize: "16px", fontWeight: 400, lineHeight: 1.65, maxWidth: "414px" }}
|
||||||
>
|
>
|
||||||
Draft teams across every sport — NHL, NBA, NFL, EPL, World Cup, and more.
|
Draft teams across every league and sport, like the NFL, NHL, EPL, F1, and more.
|
||||||
Compete with your friends in a league all season long.
|
Compete against your friends in a season-long league.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<div className="mt-8 flex flex-col items-center justify-center gap-2.5 sm:flex-row">
|
<div className="mt-8 flex flex-col items-center justify-center gap-2.5 sm:flex-row">
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue