Add Neon Mirai conference example
|
After Width: | Height: | Size: 199 KiB |
|
After Width: | Height: | Size: 261 KiB |
|
After Width: | Height: | Size: 5.8 KiB |
|
After Width: | Height: | Size: 5.0 KiB |
|
After Width: | Height: | Size: 7.1 KiB |
|
After Width: | Height: | Size: 20 KiB |
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 947 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 33 KiB |
|
After Width: | Height: | Size: 27 KiB |
|
After Width: | Height: | Size: 23 KiB |
|
After Width: | Height: | Size: 23 KiB |
|
After Width: | Height: | Size: 31 KiB |
|
After Width: | Height: | Size: 24 KiB |
|
After Width: | Height: | Size: 28 KiB |
|
After Width: | Height: | Size: 24 KiB |
|
After Width: | Height: | Size: 21 KiB |
|
After Width: | Height: | Size: 84 KiB |
|
After Width: | Height: | Size: 90 KiB |
|
After Width: | Height: | Size: 79 KiB |
|
After Width: | Height: | Size: 75 KiB |
|
After Width: | Height: | Size: 24 KiB |
|
After Width: | Height: | Size: 24 KiB |
|
After Width: | Height: | Size: 261 KiB |
|
After Width: | Height: | Size: 27 KiB |
@@ -0,0 +1,22 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
|
||||
<defs>
|
||||
<filter id="rough" x="-20%" y="-20%" width="140%" height="140%">
|
||||
<feTurbulence type="fractalNoise" baseFrequency="0.9" numOctaves="3" seed="42"/>
|
||||
<feDisplacementMap in="SourceGraphic" scale="1.6"/>
|
||||
</filter>
|
||||
<mask id="ink">
|
||||
<rect width="64" height="64" fill="white"/>
|
||||
<g fill="black" opacity="0.22">
|
||||
<circle cx="11" cy="18" r="1.4"/>
|
||||
<circle cx="49" cy="13" r="1"/>
|
||||
<circle cx="19" cy="48" r="1.2"/>
|
||||
<circle cx="43" cy="44" r="1.5"/>
|
||||
<path d="M8 55h18v2H8zM37 8h15v2H37z"/>
|
||||
</g>
|
||||
</mask>
|
||||
</defs>
|
||||
<g filter="url(#rough)" mask="url(#ink)" fill="none" stroke="#d95a35" stroke-width="3.2" stroke-linecap="square" stroke-linejoin="miter">
|
||||
<path d="M10 10h44v44H10z"/>
|
||||
<path d="M23 18v28M41 18v28M23 31h18M17 24h12M35 40h12"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 902 B |
@@ -0,0 +1,344 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
|
||||
<meta name="description" content="NEO MIRAI AI Design Conference, Tokyo 2042. A retro-futurist conference experience for illustration, interfaces, machines, and optimism.">
|
||||
<title>NEO MIRAI AI Design Conference</title>
|
||||
<link rel="icon" href="../favicon.svg" type="image/svg+xml">
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Azeret+Mono:wght@400;500;600;700&family=Chakra+Petch:wght@300;400;500;600&family=Zen+Old+Mincho:wght@400;500;600&display=swap" rel="stylesheet">
|
||||
<link rel="stylesheet" href="./styles.css?v=20260424-speaker-dots-left">
|
||||
</head>
|
||||
<body>
|
||||
<a class="skip-link" href="#main">Skip to content</a>
|
||||
|
||||
<header class="site-header" data-reveal>
|
||||
<a class="brand-lockup" href="#" aria-label="NEO MIRAI home">
|
||||
<span class="brand-mark" aria-hidden="true">
|
||||
<span></span>
|
||||
</span>
|
||||
<span class="brand-word">
|
||||
<span>NEO</span>
|
||||
<span>MIRAI</span>
|
||||
</span>
|
||||
</a>
|
||||
|
||||
<button class="nav-toggle" type="button" aria-expanded="false" aria-controls="site-nav">
|
||||
<span>Menu</span>
|
||||
<i aria-hidden="true"></i>
|
||||
</button>
|
||||
|
||||
<nav class="site-nav" id="site-nav" aria-label="Primary navigation">
|
||||
<a href="#agenda" aria-current="page">Home</a>
|
||||
<a href="#agenda">Agenda</a>
|
||||
<a href="#speakers">Speakers</a>
|
||||
<a href="#installations">Installations</a>
|
||||
<a href="#experience">Experience</a>
|
||||
<a href="#tickets">Practicals</a>
|
||||
</nav>
|
||||
|
||||
<a class="ticket-pill" href="#tickets">
|
||||
<span>Get tickets</span>
|
||||
<svg viewBox="0 0 24 24" aria-hidden="true">
|
||||
<path d="M5 12h12m-5-5 5 5-5 5"/>
|
||||
</svg>
|
||||
</a>
|
||||
</header>
|
||||
|
||||
<main id="main">
|
||||
<section class="hero" aria-labelledby="hero-title">
|
||||
<div class="vertical-poem" aria-hidden="true">
|
||||
<span>未来を描き、共に創る。</span>
|
||||
<i></i>
|
||||
<img src="./assets/stamp-seal.svg" alt="">
|
||||
</div>
|
||||
|
||||
<div class="hero-copy" data-reveal>
|
||||
<p class="kicker">新しい未来デザインカンファレンス</p>
|
||||
<h1 id="hero-title">
|
||||
<span>NEO MIRAI</span>
|
||||
<span>AI DESIGN</span>
|
||||
<span>CONFERENCE</span>
|
||||
</h1>
|
||||
<p class="hero-place">Tokyo 2042</p>
|
||||
<p class="hero-summary">Illustration, interfaces, machines, optimism</p>
|
||||
<div class="hero-meta" aria-label="Conference date and venue">
|
||||
<span>May 20–22, 2042</span>
|
||||
<span>Tokyo International Forum</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<figure class="hero-art" data-reveal>
|
||||
<img src="./assets/hero-city.webp" alt="An illustrated golden future Tokyo skyline with towers, terraces, and visitors walking toward the conference venue.">
|
||||
<figcaption>Systems with warmth. Machines with a human horizon.</figcaption>
|
||||
</figure>
|
||||
</section>
|
||||
|
||||
<section class="agenda-block" id="agenda" aria-labelledby="agenda-title">
|
||||
<div class="agenda-panel" data-reveal>
|
||||
<p class="section-label">Agenda</p>
|
||||
<h2 id="agenda-title">Three days of ideas and inspiration</h2>
|
||||
|
||||
<ol class="agenda-days" aria-label="Conference agenda highlights">
|
||||
<li>
|
||||
<time datetime="2042-05-20">Day 01 <span>05.20</span></time>
|
||||
<div>
|
||||
<h3>New Frontiers</h3>
|
||||
<p>Keynotes, visions, and emerging paradigms.</p>
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<time datetime="2042-05-21">Day 02 <span>05.21</span></time>
|
||||
<div>
|
||||
<h3>Design x Intelligence</h3>
|
||||
<p>Crafting experiences with AI.</p>
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<time datetime="2042-05-22">Day 03 <span>05.22</span></time>
|
||||
<div>
|
||||
<h3>Society & Imagination</h3>
|
||||
<p>Systems, ethics, and collective futures.</p>
|
||||
</div>
|
||||
</li>
|
||||
</ol>
|
||||
|
||||
<a class="text-action" href="#tickets">View full agenda <span aria-hidden="true">→</span></a>
|
||||
</div>
|
||||
|
||||
<figure class="agenda-art" data-reveal>
|
||||
<img src="./assets/agenda-architecture.webp" alt="A quiet illustrated architectural platform with amber circles, slim towers, and a lone visitor.">
|
||||
<figcaption>創造は、未来をつくる最初の一歩。</figcaption>
|
||||
</figure>
|
||||
</section>
|
||||
|
||||
<section class="speakers" id="speakers" aria-labelledby="speakers-title">
|
||||
<div class="section-intro" data-reveal>
|
||||
<p class="section-label">Speakers</p>
|
||||
<h2 id="speakers-title">Pioneers shaping tomorrow</h2>
|
||||
<a class="text-action" href="#tickets">View full speakers <span aria-hidden="true">→</span></a>
|
||||
</div>
|
||||
|
||||
<div class="speaker-carousel" data-reveal aria-roledescription="carousel" aria-label="Featured speaker carousel">
|
||||
<div class="speaker-track" id="speaker-track">
|
||||
<article class="speaker-slide is-active" aria-label="Featured speakers set 1">
|
||||
<ul class="speaker-grid" aria-label="Featured speakers, set 1">
|
||||
<li class="speaker-person" style="--speaker-focus-x: 68%; --speaker-focus-y: 48%;" data-kanji="中村由美">
|
||||
<img src="./assets/speaker-yumi-nakamura.webp" alt="Yumi Nakamura illustrated in profile before a golden sun and distant future city.">
|
||||
<div class="speaker-meta">
|
||||
<span>Yumi Nakamura</span>
|
||||
<small>AI Ethicist & Researcher</small>
|
||||
</div>
|
||||
</li>
|
||||
<li class="speaker-person" style="--speaker-focus-x: 52%; --speaker-focus-y: 47%;" data-kanji="田中啓介">
|
||||
<img src="./assets/speaker-keisuke-tanaka.webp" alt="Keisuke Tanaka illustrated before an amber sun and speculative city towers.">
|
||||
<div class="speaker-meta">
|
||||
<span>Keisuke Tanaka</span>
|
||||
<small>Interaction Designer & Creative Director</small>
|
||||
</div>
|
||||
</li>
|
||||
<li class="speaker-person" style="--speaker-focus-x: 42%; --speaker-focus-y: 48%;" data-kanji="李ソフィア">
|
||||
<img src="./assets/speaker-sophia-lee.webp" alt="Sophia Lee illustrated in profile near circular geometry and a temple skyline.">
|
||||
<div class="speaker-meta">
|
||||
<span>Sophia Lee</span>
|
||||
<small>Machine Learning Artist</small>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</article>
|
||||
|
||||
<article class="speaker-slide" aria-label="Featured speakers set 2" aria-hidden="true">
|
||||
<ul class="speaker-grid" aria-label="Featured speakers, set 2">
|
||||
<li class="speaker-person" style="--speaker-focus-x: 44%; --speaker-focus-y: 48%;" data-kanji="渡辺藍子">
|
||||
<img src="./assets/speaker-aiko-watanabe.webp" alt="Aiko Watanabe illustrated in profile against a burnt-orange sun and pine silhouette.">
|
||||
<div class="speaker-meta">
|
||||
<span>Aiko Watanabe</span>
|
||||
<small>Synthetic Media Director</small>
|
||||
</div>
|
||||
</li>
|
||||
<li class="speaker-person" style="--speaker-focus-x: 52%; --speaker-focus-y: 48%;" data-kanji="佐藤蓮">
|
||||
<img src="./assets/speaker-ren-sato.webp" alt="Ren Sato illustrated before Mount Fuji and flowing translucent forms.">
|
||||
<div class="speaker-meta">
|
||||
<span>Ren Sato</span>
|
||||
<small>Robotics Experience Lead</small>
|
||||
</div>
|
||||
</li>
|
||||
<li class="speaker-person" style="--speaker-focus-x: 46%; --speaker-focus-y: 48%;" data-kanji="黒田美奈">
|
||||
<img src="./assets/speaker-mina-kuroda.webp" alt="Mina Kuroda illustrated before a red sun and vertical future towers.">
|
||||
<div class="speaker-meta">
|
||||
<span>Mina Kuroda</span>
|
||||
<small>Computational Typographer</small>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</article>
|
||||
|
||||
<article class="speaker-slide" aria-label="Featured speakers set 3" aria-hidden="true">
|
||||
<ul class="speaker-grid" aria-label="Featured speakers, set 3">
|
||||
<li class="speaker-person" style="--speaker-focus-x: 43%; --speaker-focus-y: 48%;" data-kanji="森花">
|
||||
<img src="./assets/speaker-hana-mori.webp" alt="Hana Mori illustrated against a black eclipse circle and delicate future skyline.">
|
||||
<div class="speaker-meta">
|
||||
<span>Hana Mori</span>
|
||||
<small>Speculative Systems Artist</small>
|
||||
</div>
|
||||
</li>
|
||||
<li class="speaker-person" style="--speaker-focus-x: 52%; --speaker-focus-y: 47%;" data-kanji="伊藤大地">
|
||||
<img src="./assets/speaker-daichi-ito.webp" alt="Daichi Ito illustrated before a red sun and dense machine city.">
|
||||
<div class="speaker-meta">
|
||||
<span>Daichi Ito</span>
|
||||
<small>Neural Interface Researcher</small>
|
||||
</div>
|
||||
</li>
|
||||
<li class="speaker-person" style="--speaker-focus-x: 42%; --speaker-focus-y: 48%;" data-kanji="小林恵美">
|
||||
<img src="./assets/speaker-emi-kobayashi.webp" alt="Emi Kobayashi illustrated near a gold disk and Tokyo tower forms.">
|
||||
<div class="speaker-meta">
|
||||
<span>Emi Kobayashi</span>
|
||||
<small>Civic AI Designer</small>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</article>
|
||||
|
||||
<article class="speaker-slide" aria-label="Featured speakers set 4" aria-hidden="true">
|
||||
<ul class="speaker-grid" aria-label="Featured speakers, set 4">
|
||||
<li class="speaker-person" style="--speaker-focus-x: 70%; --speaker-focus-y: 48%;" data-kanji="荒田健司">
|
||||
<img src="./assets/speaker-kenji-arata.webp" alt="Kenji Arata illustrated before a red sun and distant speculative towers.">
|
||||
<div class="speaker-meta">
|
||||
<span>Kenji Arata</span>
|
||||
<small>Machine Imagination Lab</small>
|
||||
</div>
|
||||
</li>
|
||||
<li class="speaker-person" style="--speaker-focus-x: 52%; --speaker-focus-y: 48%;" data-kanji="古賀直美">
|
||||
<img src="./assets/speaker-naomi-koga.webp" alt="Naomi Koga illustrated below layered amber circles and a small temple skyline.">
|
||||
<div class="speaker-meta">
|
||||
<span>Naomi Koga</span>
|
||||
<small>Data Rituals Curator</small>
|
||||
</div>
|
||||
</li>
|
||||
<li class="speaker-person" style="--speaker-focus-x: 58%; --speaker-focus-y: 48%;" data-kanji="藤本亮">
|
||||
<img src="./assets/speaker-ryo-fujimoto.webp" alt="Ryo Fujimoto illustrated near dark circular geometry and warm technical markings.">
|
||||
<div class="speaker-meta">
|
||||
<span>Ryo Fujimoto</span>
|
||||
<small>Embodied Intelligence Architect</small>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</article>
|
||||
</div>
|
||||
|
||||
<div class="speaker-controls" aria-label="Speaker carousel controls">
|
||||
<button class="speaker-dot is-active" type="button" data-slide="0" aria-label="Show speaker set 1" aria-current="true"></button>
|
||||
<button class="speaker-dot" type="button" data-slide="1" aria-label="Show speaker set 2"></button>
|
||||
<button class="speaker-dot" type="button" data-slide="2" aria-label="Show speaker set 3"></button>
|
||||
<button class="speaker-dot" type="button" data-slide="3" aria-label="Show speaker set 4"></button>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="installations" id="installations" aria-labelledby="installations-title">
|
||||
<div class="section-intro" data-reveal>
|
||||
<p class="section-label">Installations</p>
|
||||
<h2 id="installations-title">Experiences beyond the screen</h2>
|
||||
<a class="text-action" href="#tickets">Explore installations <span aria-hidden="true">→</span></a>
|
||||
</div>
|
||||
|
||||
<div class="installation-rhythm" data-reveal>
|
||||
<article class="installation-item item-flux">
|
||||
<img src="./assets/install-flux.webp" alt="A tiny visitor standing before a glowing orange spherical installation.">
|
||||
<h3>Harmonic Flux</h3>
|
||||
<p>Immersive environment</p>
|
||||
</article>
|
||||
<article class="installation-item item-dream">
|
||||
<img src="./assets/install-dream.webp" alt="An illustrated generative ribbon floating through an amber exhibition space.">
|
||||
<h3>Dream Compiler</h3>
|
||||
<p>Generative sculpture</p>
|
||||
</article>
|
||||
<article class="installation-item item-echoes">
|
||||
<img src="./assets/install-echoes.webp" alt="A lone visitor in a circular sound installation with a dark center.">
|
||||
<h3>Echoes of Tomorrow</h3>
|
||||
<p>AI soundscape</p>
|
||||
</article>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="manifesto" id="experience" aria-labelledby="manifesto-title">
|
||||
<div class="manifesto-copy" data-reveal>
|
||||
<p class="section-label">Manifesto</p>
|
||||
<h2 id="manifesto-title">We believe the future is something we design together.</h2>
|
||||
<a class="text-action text-action-light" href="#tickets">The NEO MIRAI manifesto <span aria-hidden="true">→</span></a>
|
||||
</div>
|
||||
<figure class="manifesto-art" data-reveal>
|
||||
<img src="./assets/manifesto-fuji-clean.webp" alt="Mount Fuji and a golden future city beneath a red sun.">
|
||||
</figure>
|
||||
</section>
|
||||
|
||||
<section class="tickets" id="tickets" aria-labelledby="tickets-title">
|
||||
<img class="ticket-pine" src="./assets/pine-ticket.png" alt="" aria-hidden="true">
|
||||
<div class="tickets-heading" data-reveal>
|
||||
<p class="section-label">Tickets</p>
|
||||
<h2 id="tickets-title">Choose your experience</h2>
|
||||
</div>
|
||||
|
||||
<div class="ticket-options" data-reveal>
|
||||
<article>
|
||||
<h3>Attendee</h3>
|
||||
<p>Full access to all talks and installations.</p>
|
||||
<strong>¥ 49,000</strong>
|
||||
<a href="mailto:tickets@neomirai.example">Select</a>
|
||||
</article>
|
||||
<article>
|
||||
<h3>Student</h3>
|
||||
<p>Access for students and educators.</p>
|
||||
<strong>¥ 19,000</strong>
|
||||
<a href="mailto:tickets@neomirai.example">Select</a>
|
||||
</article>
|
||||
<article>
|
||||
<h3>Supporter</h3>
|
||||
<p>Support the community and get benefits.</p>
|
||||
<strong>¥ 99,000</strong>
|
||||
<a href="mailto:tickets@neomirai.example">Select</a>
|
||||
</article>
|
||||
<article>
|
||||
<h3>Group</h3>
|
||||
<p>For teams of five or more.</p>
|
||||
<strong>Contact us</strong>
|
||||
<a href="mailto:tickets@neomirai.example">Inquire</a>
|
||||
</article>
|
||||
</div>
|
||||
|
||||
<aside class="ticket-seal" data-reveal>
|
||||
<span class="ticket-seal-label">
|
||||
<span>Get</span>
|
||||
<span>Your</span>
|
||||
<span>Ticket</span>
|
||||
</span>
|
||||
<a href="mailto:tickets@neomirai.example" aria-label="Email ticket office">→</a>
|
||||
</aside>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<footer class="site-footer">
|
||||
<a class="brand-lockup brand-lockup-small" href="#" aria-label="NEO MIRAI home">
|
||||
<span class="brand-mark" aria-hidden="true"><span></span></span>
|
||||
<span class="brand-word"><span>NEO</span><span>MIRAI</span></span>
|
||||
</a>
|
||||
<p>未来を描き、共に創る。</p>
|
||||
<nav aria-label="Footer navigation">
|
||||
<a href="#agenda">About</a>
|
||||
<a href="#speakers">Partners</a>
|
||||
<a href="#installations">News</a>
|
||||
<a href="#tickets">FAQ</a>
|
||||
<a href="mailto:hello@neomirai.example">Contact</a>
|
||||
</nav>
|
||||
<div class="language-switch" aria-label="Language options">
|
||||
<a href="#" aria-current="true">EN</a>
|
||||
<span>/</span>
|
||||
<a href="#">日本語</a>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
<script src="./script.js?v=20260424-speaker-dots-left"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,155 @@
|
||||
const header = document.querySelector(".site-header");
|
||||
const nav = document.querySelector("#site-nav");
|
||||
const navToggle = document.querySelector(".nav-toggle");
|
||||
const revealTargets = document.querySelectorAll("[data-reveal]");
|
||||
const navLinks = document.querySelectorAll(".site-nav a");
|
||||
const reducedMotion = window.matchMedia("(prefers-reduced-motion: reduce)");
|
||||
|
||||
const setHeaderState = () => {
|
||||
header.classList.toggle("is-scrolled", window.scrollY > 12);
|
||||
};
|
||||
|
||||
setHeaderState();
|
||||
window.addEventListener("scroll", setHeaderState, { passive: true });
|
||||
|
||||
if (navToggle && nav) {
|
||||
navToggle.addEventListener("click", () => {
|
||||
const isOpen = navToggle.getAttribute("aria-expanded") === "true";
|
||||
navToggle.setAttribute("aria-expanded", String(!isOpen));
|
||||
nav.classList.toggle("is-open", !isOpen);
|
||||
});
|
||||
|
||||
nav.addEventListener("click", (event) => {
|
||||
if (event.target instanceof HTMLAnchorElement) {
|
||||
navToggle.setAttribute("aria-expanded", "false");
|
||||
nav.classList.remove("is-open");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const revealObserver = new IntersectionObserver(
|
||||
(entries, observer) => {
|
||||
entries.forEach((entry) => {
|
||||
if (!entry.isIntersecting) return;
|
||||
entry.target.classList.add("is-visible");
|
||||
observer.unobserve(entry.target);
|
||||
});
|
||||
},
|
||||
{ threshold: 0.16, rootMargin: "0px 0px -8% 0px" }
|
||||
);
|
||||
|
||||
revealTargets.forEach((target) => {
|
||||
const section = target.closest("section");
|
||||
const localTargets = section
|
||||
? Array.from(section.querySelectorAll("[data-reveal]"))
|
||||
: Array.from(revealTargets);
|
||||
const localIndex = Math.max(localTargets.indexOf(target), 0);
|
||||
const delay = Math.min(localIndex, 4) * 110;
|
||||
|
||||
target.style.setProperty("--reveal-delay", `${delay}ms`);
|
||||
revealObserver.observe(target);
|
||||
});
|
||||
|
||||
window.setTimeout(() => {
|
||||
revealTargets.forEach((target) => target.classList.add("is-visible"));
|
||||
}, 1600);
|
||||
|
||||
const sections = Array.from(document.querySelectorAll("main section[id]"));
|
||||
|
||||
const activeObserver = new IntersectionObserver(
|
||||
(entries) => {
|
||||
const visible = entries
|
||||
.filter((entry) => entry.isIntersecting)
|
||||
.sort((a, b) => b.intersectionRatio - a.intersectionRatio)[0];
|
||||
|
||||
if (!visible) return;
|
||||
|
||||
navLinks.forEach((link) => {
|
||||
const isActive = link.getAttribute("href") === `#${visible.target.id}`;
|
||||
if (isActive) {
|
||||
link.setAttribute("aria-current", "page");
|
||||
} else {
|
||||
link.removeAttribute("aria-current");
|
||||
}
|
||||
});
|
||||
},
|
||||
{ threshold: [0.24, 0.42, 0.64], rootMargin: "-18% 0px -52% 0px" }
|
||||
);
|
||||
|
||||
sections.forEach((section) => activeObserver.observe(section));
|
||||
|
||||
const speakerCarousel = document.querySelector(".speaker-carousel");
|
||||
|
||||
if (speakerCarousel) {
|
||||
const speakerTrack = speakerCarousel.querySelector(".speaker-track");
|
||||
const speakerSlides = Array.from(speakerCarousel.querySelectorAll(".speaker-slide"));
|
||||
const speakerDots = Array.from(speakerCarousel.querySelectorAll(".speaker-dot"));
|
||||
let activeSpeakerIndex = 0;
|
||||
let speakerTimer = null;
|
||||
|
||||
const setSpeakerSlide = (index) => {
|
||||
activeSpeakerIndex = (index + speakerSlides.length) % speakerSlides.length;
|
||||
speakerCarousel.style.setProperty("--speaker-index", activeSpeakerIndex);
|
||||
speakerCarousel.style.setProperty("--speaker-offset", `${activeSpeakerIndex * -100}%`);
|
||||
|
||||
speakerSlides.forEach((slide, slideIndex) => {
|
||||
const isActive = slideIndex === activeSpeakerIndex;
|
||||
slide.classList.toggle("is-active", isActive);
|
||||
slide.toggleAttribute("aria-hidden", !isActive);
|
||||
});
|
||||
|
||||
speakerDots.forEach((dot, dotIndex) => {
|
||||
const isActive = dotIndex === activeSpeakerIndex;
|
||||
dot.classList.toggle("is-active", isActive);
|
||||
if (isActive) {
|
||||
dot.setAttribute("aria-current", "true");
|
||||
} else {
|
||||
dot.removeAttribute("aria-current");
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const stopSpeakerTimer = () => {
|
||||
if (!speakerTimer) return;
|
||||
window.clearInterval(speakerTimer);
|
||||
speakerTimer = null;
|
||||
};
|
||||
|
||||
const startSpeakerTimer = () => {
|
||||
if (reducedMotion.matches || speakerSlides.length < 2 || speakerTimer) return;
|
||||
speakerTimer = window.setInterval(() => {
|
||||
setSpeakerSlide(activeSpeakerIndex + 1);
|
||||
}, 5600);
|
||||
};
|
||||
|
||||
speakerDots.forEach((dot) => {
|
||||
dot.addEventListener("click", () => {
|
||||
const slideIndex = Number(dot.dataset.slide);
|
||||
if (Number.isNaN(slideIndex)) return;
|
||||
setSpeakerSlide(slideIndex);
|
||||
stopSpeakerTimer();
|
||||
startSpeakerTimer();
|
||||
});
|
||||
});
|
||||
|
||||
speakerCarousel.addEventListener("pointerenter", stopSpeakerTimer);
|
||||
speakerCarousel.addEventListener("pointerleave", startSpeakerTimer);
|
||||
speakerCarousel.addEventListener("focusin", stopSpeakerTimer);
|
||||
speakerCarousel.addEventListener("focusout", startSpeakerTimer);
|
||||
speakerCarousel.addEventListener("keydown", (event) => {
|
||||
if (event.key === "ArrowDown" || event.key === "ArrowRight") {
|
||||
event.preventDefault();
|
||||
setSpeakerSlide(activeSpeakerIndex + 1);
|
||||
}
|
||||
|
||||
if (event.key === "ArrowUp" || event.key === "ArrowLeft") {
|
||||
event.preventDefault();
|
||||
setSpeakerSlide(activeSpeakerIndex - 1);
|
||||
}
|
||||
});
|
||||
|
||||
if (speakerTrack) {
|
||||
setSpeakerSlide(0);
|
||||
startSpeakerTimer();
|
||||
}
|
||||
}
|
||||