Tons of fixes to every page

This commit is contained in:
2026-05-02 20:18:48 +03:00
parent e2c3ec5b8a
commit 0368d64bcc
15 changed files with 490 additions and 286 deletions

View File

@@ -1,66 +0,0 @@
"use client";
import { useEffect, useState } from "react";
interface CS2SidebarProps {
sections: { id: string; label: string }[];
}
export default function CS2Sidebar({ sections }: CS2SidebarProps) {
const [activeSection, setActiveSection] = useState<string>(sections[0]?.id || "");
useEffect(() => {
const observer = new IntersectionObserver(
(entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
setActiveSection(entry.target.id);
}
});
},
{
rootMargin: "-20% 0px -70% 0px",
threshold: 0,
}
);
sections.forEach((section) => {
const element = document.getElementById(section.id);
if (element) {
observer.observe(element);
}
});
return () => {
sections.forEach((section) => {
const element = document.getElementById(section.id);
if (element) {
observer.unobserve(element);
}
});
};
}, [sections]);
return (
<aside className="hidden lg:block">
<nav className="sticky top-24 border-l border-[#1F5673] pl-6">
<ul className="flex flex-col gap-2">
{sections.map((section) => (
<li key={section.id}>
<a
href={`#${section.id}`}
className={`transition ${
activeSection === section.id
? "text-[#EEE5E5] font-bold"
: "text-[#00A3E0] hover:text-[#EEE5E5]"
}`}
>
{section.label}
</a>
</li>
))}
</ul>
</nav>
</aside>
);
}

View File

@@ -15,11 +15,9 @@ const Footer = () => {
>
{t("footer.contact")}
</h2>
<div className="flex flex-row justify-between gap-8 items-start">
<div>
<h3 className="text-xl font-bold">
MTÜ Lapikud
</h3>
<div className="grid grid-cols-1 sm:grid-cols-3 gap-8 items-start w-full">
<div className="text-center sm:text-left sm:justify-self-start">
<h3 className="text-xl font-bold">MTÜ Lapikud</h3>
<div className="flex flex-col gap-2 mt-2">
<p>
{t("footer.registrationCode")}:{" "}
@@ -30,12 +28,12 @@ const Footer = () => {
<p className="">Swedbank EE842200221094704780</p>
</div>
</div>
<div className="flex flex-col gap-2 items-center">
<div className="flex flex-col gap-2 items-center text-center sm:justify-self-center">
<div className="flex flex-row gap-2">
<span className="material-symbols-outlined !font-bold text-[#007CAB] dark:text-[#00A3E0]">
mail
</span>
<a href="mailto:tipilaninfo@gmail.com" className="underline">
<a href="mailto:tipilaninfogmail.com" className="underline">
tipilaninfo@gmail.com
</a>
</div>
@@ -49,7 +47,7 @@ const Footer = () => {
</div>
</div>
{/* Social media */}
<div className="flex flex-row gap-4">
<div className="flex flex-row gap-4 justify-center sm:justify-self-end sm:justify-end">
<a
href="https://discord.gg/pPhhatZAfA"
target="_blank"

View File

@@ -1,7 +1,7 @@
"use client";
import Image from "next/image";
import { Link } from "@/i18n/routing";
import { Link, usePathname } from "@/i18n/routing";
import { vipnagorgialla } from "@/components/Vipnagorgialla";
// Icons
@@ -36,15 +36,16 @@ interface HeaderProps {
}
const Header = ({ navItems }: HeaderProps) => {
const pathname = usePathname();
// Filter nav items for the horizontal bar (exclude kodukord)
const mainNavItems = navItems.filter(
(item) => item.href !== "/kodukord"
);
const mainNavItems = navItems.filter((item) => item.href !== "/kodukord");
const disabledNavHrefs = new Set<NavItem["href"]>(["/messiala", "/ajakava"]);
return (
<header className="px-4 py-2 md:px-8 flex items-center bg-[#0E0F19] border-b-3 border-[#1F5673] justify-between">
{/* Logo */}
<Link href="/" className="flex-shrink-0">
<Link href="/" className="shrink-0">
<Image
src="/tipilan-icon-white.svg"
alt="TipiLAN"
@@ -57,16 +58,30 @@ const Header = ({ navItems }: HeaderProps) => {
{/* Right side - Navigation + controls */}
<div className="flex items-center gap-3">
{/* Desktop Navigation */}
<nav className="hidden md:flex items-center gap-3">
{mainNavItems.map((item) => (
<Link
key={item.href}
href={item.href}
className={`${vipnagorgialla.className} font-bold italic text-lg uppercase px-4 py-1.5 border-2 border-[#00A3E0] text-[#EEE5E5] hover:bg-[#00A3E0]/20 transition`}
>
{item.label}
</Link>
))}
<nav className="hidden lg:flex items-center gap-3">
{mainNavItems.map((item) => {
const isActive = pathname === item.href;
const isDisabled = disabledNavHrefs.has(item.href);
return (
<Link
key={item.href}
href={item.href}
aria-current={isActive ? "page" : undefined}
aria-disabled={isActive || isDisabled ? true : undefined}
tabIndex={isActive || isDisabled ? -1 : undefined}
className={`${vipnagorgialla.className} font-bold italic text-lg uppercase px-4 py-1.5 border-2 border-[#00A3E0] text-[#EEE5E5] transition ${
isActive
? "bg-[#00A3E0] text-black cursor-default pointer-events-none"
: isDisabled
? "opacity-50 cursor-not-allowed pointer-events-none"
: "hover:bg-[#00A3E0]/20"
}`}
>
{item.label}
</Link>
);
})}
</nav>
<LanguageSwitcher />
@@ -76,23 +91,46 @@ const Header = ({ navItems }: HeaderProps) => {
<Button
variant="ghost"
size="icon"
className="md:hidden size-10 cursor-pointer"
className="lg:hidden size-10 cursor-pointer"
>
<MdMenu className="h-8 w-8 text-[#EEE5E5]" />
<span className="sr-only">Menu</span>
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end" className="w-48 translate-y-4">
{navItems.map((item) => (
<DropdownMenuItem key={item.href} asChild>
<Link
href={item.href}
className={`${vipnagorgialla.className} font-bold italic uppercase text-lg`}
{navItems.map((item) => {
const isActive = pathname === item.href;
const isDisabled = disabledNavHrefs.has(item.href);
return (
<DropdownMenuItem
key={item.href}
disabled={isActive || isDisabled}
asChild={!isActive && !isDisabled}
>
{item.label}
</Link>
</DropdownMenuItem>
))}
{isActive ? (
<span
className={`${vipnagorgialla.className} font-bold italic uppercase text-lg text-[#00A3E0] cursor-default`}
>
{item.label}
</span>
) : isDisabled ? (
<span
className={`${vipnagorgialla.className} font-bold italic uppercase text-lg opacity-50 cursor-not-allowed`}
>
{item.label}
</span>
) : (
<Link
href={item.href}
className={`${vipnagorgialla.className} font-bold italic uppercase text-lg`}
>
{item.label}
</Link>
)}
</DropdownMenuItem>
);
})}
</DropdownMenuContent>
</DropdownMenu>
</div>

View File

@@ -0,0 +1,84 @@
"use client";
import { useEffect, useState } from "react";
interface RuleNavProps {
sections: { id: string; label: string }[];
}
export default function RuleNav({ sections }: RuleNavProps) {
const [activeSection, setActiveSection] = useState<string>(
sections[0]?.id || "",
);
useEffect(() => {
if (sections.length === 0) {
return;
}
const getScrollOffset = () => {
const firstSection = document.getElementById(sections[0].id);
if (!firstSection) {
return 0;
}
const scrollMarginTop =
window.getComputedStyle(firstSection).scrollMarginTop;
const parsed = Number.parseFloat(scrollMarginTop);
return Number.isNaN(parsed) ? 0 : parsed;
};
const updateActiveSection = () => {
const offset = getScrollOffset();
const scrollPosition = window.scrollY + offset + 1;
let currentId = sections[0].id;
for (const section of sections) {
const element = document.getElementById(section.id);
if (!element) {
continue;
}
if (element.offsetTop <= scrollPosition) {
currentId = section.id;
} else {
break;
}
}
setActiveSection(currentId);
};
updateActiveSection();
window.addEventListener("scroll", updateActiveSection, { passive: true });
window.addEventListener("resize", updateActiveSection);
window.addEventListener("hashchange", updateActiveSection);
return () => {
window.removeEventListener("scroll", updateActiveSection);
window.removeEventListener("resize", updateActiveSection);
window.removeEventListener("hashchange", updateActiveSection);
};
}, [sections]);
return (
<aside className="hidden lg:block">
<nav className="sticky top-24 border-l border-[#1F5673] pl-6">
<ul className="flex flex-col gap-2">
{sections.map((section) => (
<li key={section.id}>
<a
href={`#${section.id}`}
className={`transition ${
activeSection === section.id
? "text-[#EEE5E5] font-bold"
: "text-[#00A3E0] hover:text-[#EEE5E5]"
}`}
>
{section.label}
</a>
</li>
))}
</ul>
</nav>
</aside>
);
}

View File

@@ -4,14 +4,25 @@ import { vipnagorgialla } from "@/components/Vipnagorgialla";
interface RuleSection {
title: string;
rules: (string | { main: string; sub: (string | { main: string; sub: string[] })[] })[];
rules: (
| string
| { main: string; sub: (string | { main: string; sub: string[] })[] }
)[];
}
interface CS2RulesProps {
interface RulesContentProps {
sections: RuleSection[];
}
function RuleItem({ rule, index }: { rule: string | { main: string; sub: (string | { main: string; sub: string[] })[] }; index: number }) {
function RuleItem({
rule,
index,
}: {
rule:
| string
| { main: string; sub: (string | { main: string; sub: string[] })[] };
index: number;
}) {
if (typeof rule === "string") {
return (
<li className="text-[#EEE5E5]/80 mb-2">
@@ -31,20 +42,26 @@ function RuleItem({ rule, index }: { rule: string | { main: string; sub: (string
if (typeof subRule === "string") {
return (
<li key={subIndex} className="text-[#EEE5E5]/70 mb-1">
<span className="text-[#00A3E0]/70 mr-2">{index}.{subIndex + 1}.</span>
<span className="text-[#00A3E0]/70 mr-2">
{index}.{subIndex + 1}.
</span>
{subRule}
</li>
);
}
return (
<li key={subIndex} className="text-[#EEE5E5]/70 mb-2">
<span className="text-[#00A3E0]/70 mr-2">{index}.{subIndex + 1}.</span>
<span className="text-[#00A3E0]/70 mr-2">
{index}.{subIndex + 1}.
</span>
{subRule.main}
{subRule.sub && subRule.sub.length > 0 && (
<ol className="ml-6 mt-1">
{subRule.sub.map((subSubRule, subSubIndex) => (
<li key={subSubIndex} className="text-[#EEE5E5]/60 mb-1">
<span className="text-[#00A3E0]/50 mr-2">{index}.{subIndex + 1}.{subSubIndex + 1}.</span>
<span className="text-[#00A3E0]/50 mr-2">
{index}.{subIndex + 1}.{subSubIndex + 1}.
</span>
{subSubRule}
</li>
))}
@@ -59,12 +76,14 @@ function RuleItem({ rule, index }: { rule: string | { main: string; sub: (string
);
}
export default function CS2Rules({ sections }: CS2RulesProps) {
export default function RulesContent({ sections }: RulesContentProps) {
return (
<div>
{sections.map((section, sectionIndex) => (
<div key={sectionIndex} className="mb-8">
<h3 className={`${vipnagorgialla.className} font-bold italic text-xl text-[#00A3E0] uppercase mb-4`}>
<h3
className={`${vipnagorgialla.className} font-bold italic text-xl text-[#00A3E0] uppercase mb-4`}
>
{sectionIndex + 1}) {section.title}
</h3>
<ol className="list-none">

View File

@@ -16,15 +16,71 @@ interface Sponsor {
}
const sponsors: Sponsor[] = [
{ href: "https://taltech.ee", src: "/sponsors/taltech-color.png", alt: "Taltech (Tallinna Tehnikaülikool)", width: 192, height: 192 },
{ href: "https://www.redbull.com/ee-et/", src: "/sponsors/redbull.png", alt: "Redbull", width: 80, height: 80 },
{ href: "https://www.simracing.ee/", src: "/sponsors/EVAL.png", alt: "EVAL", width: 200, height: 200 },
{ href: "https://www.facebook.com/bfglOfficial", src: "/sponsors/BFGL.png", alt: "BFGL", width: 192, height: 192 },
{ href: "https://www.tomorrow.ee/", src: "/sponsors/nt.png", alt: "Network Tomorrow", width: 300, height: 200 },
{ href: "https://k-space.ee/", src: "/sponsors/k-space_ee-white.png", alt: "K-Space", width: 200, height: 200, className: "not-dark:invert" },
{ href: "https://globalproductions.ee/", src: "/sponsors/Global-productions.png", alt: "Global Productions", width: 200, height: 200 },
{ href: "https://www.linkedin.com/company/gamedev-guild/", src: "/sponsors/estonian_gamedev_guild.png", alt: "Estonian Gamedev Guild", width: 200, height: 200, className: "not-dark:invert" },
{ href: "https://alzgamer.ee/", src: "/sponsors/alzgamer.png", alt: "AlzGamer", width: 200, height: 200 },
{
href: "https://taltech.ee",
src: "/sponsors/taltech-color.png",
alt: "Taltech (Tallinna Tehnikaülikool)",
width: 192,
height: 192,
},
{
href: "https://www.redbull.com/ee-et/",
src: "/sponsors/redbull.png",
alt: "Redbull",
width: 80,
height: 80,
},
{
href: "https://www.simracing.ee/",
src: "/sponsors/EVAL.png",
alt: "EVAL",
width: 200,
height: 200,
},
{
href: "https://www.facebook.com/bfglOfficial",
src: "/sponsors/BFGL.png",
alt: "BFGL",
width: 192,
height: 192,
},
{
href: "https://www.tomorrow.ee/",
src: "/sponsors/nt.png",
alt: "Network Tomorrow",
width: 300,
height: 200,
},
{
href: "https://k-space.ee/",
src: "/sponsors/k-space_ee-white.png",
alt: "K-Space",
width: 200,
height: 200,
className: "not-dark:invert",
},
{
href: "https://globalproductions.ee/",
src: "/sponsors/Global-productions.png",
alt: "Global Productions",
width: 200,
height: 200,
},
{
href: "https://www.linkedin.com/company/gamedev-guild/",
src: "/sponsors/estonian_gamedev_guild.png",
alt: "Estonian Gamedev Guild",
width: 200,
height: 200,
className: "not-dark:invert",
},
{
href: "https://alzgamer.ee/",
src: "/sponsors/alzgamer.png",
alt: "AlzGamer",
width: 200,
height: 200,
},
];
// Split sponsors into slides (6 per slide)
@@ -46,7 +102,10 @@ export default function Sponsors({
const t = useTranslations();
const [current, setCurrent] = useState(0);
const next = useCallback(() => setCurrent((c) => (c + 1) % slides.length), []);
const next = useCallback(
() => setCurrent((c) => (c + 1) % slides.length),
[],
);
useEffect(() => {
const id = setInterval(next, 5000);
@@ -55,7 +114,7 @@ export default function Sponsors({
return (
<div
className={`flex flex-col max-w-[1920px] h-[414px] mx-auto ${vipnagorgialla.className} font-bold italic border-b-3 border-[#1F5673] ${className}`}
className={`flex flex-col max-w-[1920px] xl:h-[414px] mx-auto ${vipnagorgialla.className} font-bold italic border-[#1F5673] ${className}`}
>
{showTitle && (
<h3 className="text-4xl md:text-5xl dark:text-[#EEE5E5] text-[#2A2C3F] px-12 pt-8 pb-4">
@@ -64,7 +123,7 @@ export default function Sponsors({
)}
{/* Carousel container */}
<div className="relative flex-1 overflow-hidden">
<div className="relative xl:flex-1 overflow-x-hidden overflow-y-visible">
<div
className="flex h-full transition-transform duration-500 ease-in-out"
style={{ transform: `translateX(-${current * 100}%)` }}
@@ -72,16 +131,21 @@ export default function Sponsors({
{slides.map((slideSponsors, slideIndex) => (
<div
key={slideIndex}
className="flex-none w-full h-full flex items-center justify-center gap-8 md:gap-12 px-12"
className="flex-none w-full xl:h-full grid grid-cols-2 sm:grid-cols-3 place-items-center gap-6 px-6 py-6 xl:flex xl:items-center xl:justify-center xl:gap-12 xl:px-12 xl:py-0"
>
{slideSponsors.map((sponsor, i) => (
<NextLink key={i} href={sponsor.href} target="_blank">
<NextLink
key={i}
href={sponsor.href}
target="_blank"
className="flex items-center justify-center w-full"
>
<Image
src={sponsor.src}
alt={sponsor.alt}
width={sponsor.width}
height={sponsor.height}
className={`object-contain max-h-[180px] ${sponsor.className || ""}`}
className={`object-contain max-h-[80px] max-w-[120px] sm:max-h-[110px] sm:max-w-[150px] md:max-h-[130px] md:max-w-[180px] lg:max-h-[140px] lg:max-w-[200px] xl:max-h-[180px] xl:max-w-[240px] ${sponsor.className || ""}`}
/>
</NextLink>
))}
@@ -97,7 +161,9 @@ export default function Sponsors({
key={i}
onClick={() => setCurrent(i)}
className={`w-3 h-3 rounded-full transition ${
i === current ? "bg-[#007CAB]" : "bg-[#1F5673] hover:bg-[#007CAB]/60"
i === current
? "bg-[#00A3E0]"
: "bg-[#1F5673] hover:bg-[#007CAB]/60"
}`}
aria-label={`Slide ${i + 1}`}
/>

View File

@@ -25,7 +25,8 @@ function highlightLAN(text: string) {
const suffix = part.slice(7); // Everything after "TIPILAN"
return (
<span key={i}>
TIPI<span className="text-[#00A3E0]">LAN</span>{suffix.toUpperCase()}
TIPI<span className="text-[#00A3E0]">LAN</span>
{suffix.toUpperCase()}
</span>
);
}
@@ -34,16 +35,40 @@ function highlightLAN(text: string) {
}
const slides: Slide[] = [
{ key: "compete", image: "/images/landing/compete_teaser.jpg", imageAlt: "Võistle", hero: "/images/landing/compete_hero.png", href: "/turniirid" },
{ key: "play", image: "/images/landing/play_teaser.png", imageAlt: "Mängi", hero: "/images/landing/play_hero.png", href: "/piletid", flip: true, fullBrightness: true },
{ key: "explore", image: "/images/landing/explore_teaser.png", imageAlt: "Avasta", hero: "/images/landing/explore_hero.png", href: "/messiala", fullBrightness: true },
{
key: "compete",
image: "/images/landing/compete_teaser.jpg",
imageAlt: "Võistle",
hero: "/images/landing/compete_hero.png",
href: "/turniirid",
},
{
key: "play",
image: "/images/landing/play_teaser.png",
imageAlt: "Mängi",
hero: "/images/landing/play_hero.png",
href: "/piletid",
flip: true,
fullBrightness: true,
},
{
key: "explore",
image: "/images/landing/explore_teaser.png",
imageAlt: "Avasta",
hero: "/images/landing/explore_hero.png",
href: "/messiala",
fullBrightness: true,
},
];
export default function TeaserCarousel() {
const t = useTranslations("home.teaser");
const [current, setCurrent] = useState(0);
const next = useCallback(() => setCurrent((c) => (c + 1) % slides.length), []);
const next = useCallback(
() => setCurrent((c) => (c + 1) % slides.length),
[],
);
const prev = () => setCurrent((c) => (c - 1 + slides.length) % slides.length);
useEffect(() => {
@@ -60,7 +85,7 @@ export default function TeaserCarousel() {
style={{ transform: `translateX(-${current * 100}%)` }}
>
{slides.map((slide) => {
const title = t(`${slide.key}.title`);
const title = t(`${slide.key}.title`);
const description = t(`${slide.key}.description`);
return (
<div key={slide.key} className="relative flex-none w-full h-full">
@@ -72,19 +97,29 @@ export default function TeaserCarousel() {
className="object-cover object-center"
/>
{/* Overlay */}
<div className={`absolute inset-0 ${slide.fullBrightness ? "" : "bg-gradient-to-r from-[#0E0F19]/90 via-[#0E0F19]/60 to-[#0E0F19]/20"}`} />
<div
className={`absolute inset-0 ${slide.fullBrightness ? "" : "bg-gradient-to-r from-[#0E0F19]/90 via-[#0E0F19]/60 to-[#0E0F19]/20"}`}
/>
{/* Content */}
<div className={`relative grid grid-cols-1 md:grid-cols-2 h-full ${slide.flip ? "md:[direction:rtl]" : ""}`}>
<div className={`flex flex-col justify-between px-8 py-8 md:px-12 md:py-10 ${slide.flip ? "md:[direction:ltr]" : ""}`}>
<div
className={`relative grid grid-cols-1 md:grid-cols-2 h-full ${slide.flip ? "md:[direction:rtl]" : ""}`}
>
<div
className={`flex flex-col justify-between px-8 py-8 md:px-12 md:py-10 ${slide.flip ? "md:[direction:ltr]" : ""}`}
>
{/* Heading at top */}
<h2 className={`${vipnagorgialla.className} font-bold italic text-[64px] leading-none tracking-normal uppercase text-[#EEE5E5]`}>
<h2
className={`${vipnagorgialla.className} font-bold italic text-[clamp(2.5rem,1.5rem+4.5vw,4rem)] md:text-[64px] leading-none tracking-normal uppercase text-[#EEE5E5]`}
>
{highlightLAN(t("heading"))}
</h2>
{/* Title + description at bottom */}
<div className="flex flex-col gap-3 pb-16">
<Link href={slide.href}>
<h3 className={`${vipnagorgialla.className} font-bold italic text-[clamp(2.5rem,2rem+2.5vw,5rem)] leading-none text-[#EEE5E5] hover:text-[#00A3E0] transition`}>
<h3
className={`${vipnagorgialla.className} font-bold italic text-[clamp(2.5rem,2rem+2.5vw,5rem)] leading-none text-[#EEE5E5] hover:text-[#00A3E0] transition`}
>
{title}
</h3>
</Link>
@@ -94,7 +129,9 @@ export default function TeaserCarousel() {
</div>
</div>
{/* Hero image */}
<div className={`hidden md:block relative ${slide.flip ? "md:[direction:ltr]" : ""}`}>
<div
className={`hidden md:block relative ${slide.flip ? "md:[direction:ltr]" : ""}`}
>
<Image
src={slide.hero}
alt={slide.imageAlt}
@@ -111,32 +148,34 @@ export default function TeaserCarousel() {
{/* Arrow buttons */}
<button
onClick={prev}
className="absolute left-4 top-1/2 -translate-y-1/2 w-10 h-10 flex items-center justify-center bg-[#0E0F19]/50 hover:bg-[#007CAB] text-[#EEE5E5] transition"
className="absolute left-4 top-1/2 -translate-y-1/2 w-10 h-10 flex items-center justify-center bg-[#0E0F19]/50 hover:bg-[#007CAB] text-[#EEE5E5] transition z-20"
aria-label="Previous slide"
>
<span className="material-symbols-outlined">chevron_left</span>
</button>
<button
onClick={next}
className="absolute right-4 top-1/2 -translate-y-1/2 w-10 h-10 flex items-center justify-center bg-[#0E0F19]/50 hover:bg-[#007CAB] text-[#EEE5E5] transition"
className="absolute right-4 top-1/2 -translate-y-1/2 w-10 h-10 flex items-center justify-center bg-[#0E0F19]/50 hover:bg-[#007CAB] text-[#EEE5E5] transition z-20"
aria-label="Next slide"
>
<span className="material-symbols-outlined">chevron_right</span>
</button>
</div>
{/* Navigation dots */}
<div className="flex justify-center gap-3 py-5">
{slides.map((_, i) => (
<button
key={i}
onClick={() => setCurrent(i)}
className={`w-3 h-3 rounded-full transition ${
i === current ? "bg-[#007CAB]" : "bg-[#1F5673] hover:bg-[#007CAB]/60"
}`}
aria-label={`Slide ${i + 1}`}
/>
))}
{/* Navigation dots */}
<div className="absolute bottom-5 left-1/2 -translate-x-1/2 flex justify-center gap-3 z-20 px-3 py-2 bg-[#0E0F19]/50 backdrop-blur-md">
{slides.map((_, i) => (
<button
key={i}
onClick={() => setCurrent(i)}
className={`w-3 h-3 rounded-full transition ${
i === current
? "bg-[#00A3E0]"
: "bg-[#1F5673] hover:bg-[#007CAB]/60"
}`}
aria-label={`Slide ${i + 1}`}
/>
))}
</div>
</div>
</div>
);