diff --git a/apps/info/public/card_icon_1.svg b/apps/info/public/card_icon_1.svg
new file mode 100644
index 00000000..6beba271
--- /dev/null
+++ b/apps/info/public/card_icon_1.svg
@@ -0,0 +1,5 @@
+
diff --git a/apps/info/public/card_icon_2.svg b/apps/info/public/card_icon_2.svg
new file mode 100644
index 00000000..5f6a2135
--- /dev/null
+++ b/apps/info/public/card_icon_2.svg
@@ -0,0 +1,3 @@
+
diff --git a/apps/info/public/card_icon_3.svg b/apps/info/public/card_icon_3.svg
new file mode 100644
index 00000000..9be2fa6a
--- /dev/null
+++ b/apps/info/public/card_icon_3.svg
@@ -0,0 +1,3 @@
+
diff --git a/apps/info/public/exhibit_background.png b/apps/info/public/exhibit_background.png
new file mode 100644
index 00000000..c677316c
Binary files /dev/null and b/apps/info/public/exhibit_background.png differ
diff --git a/apps/info/public/exhibit_background.svg b/apps/info/public/exhibit_background.svg
new file mode 100644
index 00000000..d72c6c70
--- /dev/null
+++ b/apps/info/public/exhibit_background.svg
@@ -0,0 +1,9 @@
+
diff --git a/apps/info/public/icon_building.svg b/apps/info/public/icon_building.svg
new file mode 100644
index 00000000..126b7dfa
--- /dev/null
+++ b/apps/info/public/icon_building.svg
@@ -0,0 +1,278 @@
+
diff --git a/apps/info/public/icon_calendar.svg b/apps/info/public/icon_calendar.svg
new file mode 100644
index 00000000..332883d4
--- /dev/null
+++ b/apps/info/public/icon_calendar.svg
@@ -0,0 +1,182 @@
+
diff --git a/apps/info/public/icon_location.svg b/apps/info/public/icon_location.svg
new file mode 100644
index 00000000..5496c990
--- /dev/null
+++ b/apps/info/public/icon_location.svg
@@ -0,0 +1,185 @@
+
diff --git a/apps/info/public/icon_person.svg b/apps/info/public/icon_person.svg
new file mode 100644
index 00000000..6d3695b9
--- /dev/null
+++ b/apps/info/public/icon_person.svg
@@ -0,0 +1,164 @@
+
diff --git a/apps/info/public/icon_speaker.svg b/apps/info/public/icon_speaker.svg
new file mode 100644
index 00000000..3b0bb966
--- /dev/null
+++ b/apps/info/public/icon_speaker.svg
@@ -0,0 +1,298 @@
+
diff --git a/apps/info/public/instagram_logo.png b/apps/info/public/instagram_logo.png
new file mode 100644
index 00000000..1ba06941
Binary files /dev/null and b/apps/info/public/instagram_logo.png differ
diff --git a/apps/info/public/logo_2018.svg b/apps/info/public/logo_2018.svg
new file mode 100644
index 00000000..5f4e7179
--- /dev/null
+++ b/apps/info/public/logo_2018.svg
@@ -0,0 +1,63 @@
+
+
+
diff --git a/apps/info/public/logo_2019.svg b/apps/info/public/logo_2019.svg
new file mode 100644
index 00000000..18019b26
--- /dev/null
+++ b/apps/info/public/logo_2019.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/apps/info/public/logo_2020.svg b/apps/info/public/logo_2020.svg
new file mode 100644
index 00000000..659fa97c
--- /dev/null
+++ b/apps/info/public/logo_2020.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/apps/info/public/logo_2021.svg b/apps/info/public/logo_2021.svg
new file mode 100644
index 00000000..d7d5f090
--- /dev/null
+++ b/apps/info/public/logo_2021.svg
@@ -0,0 +1,12 @@
+
+
\ No newline at end of file
diff --git a/apps/info/public/logo_2022.svg b/apps/info/public/logo_2022.svg
new file mode 100644
index 00000000..d54cd07e
--- /dev/null
+++ b/apps/info/public/logo_2022.svg
@@ -0,0 +1,12 @@
+
+
\ No newline at end of file
diff --git a/apps/info/public/logo_2023.svg b/apps/info/public/logo_2023.svg
new file mode 100644
index 00000000..5fd6df68
--- /dev/null
+++ b/apps/info/public/logo_2023.svg
@@ -0,0 +1,12 @@
+
+
diff --git a/apps/info/public/logo_2024.svg b/apps/info/public/logo_2024.svg
new file mode 100644
index 00000000..2236be55
--- /dev/null
+++ b/apps/info/public/logo_2024.svg
@@ -0,0 +1,22 @@
+
diff --git a/apps/info/public/logo_2025.svg b/apps/info/public/logo_2025.svg
new file mode 100644
index 00000000..c9ebca2f
--- /dev/null
+++ b/apps/info/public/logo_2025.svg
@@ -0,0 +1,12 @@
+
+
\ No newline at end of file
diff --git a/apps/info/public/logo_info.svg b/apps/info/public/logo_info.svg
new file mode 100644
index 00000000..37eb429f
--- /dev/null
+++ b/apps/info/public/logo_info.svg
@@ -0,0 +1,34 @@
+
diff --git a/apps/info/public/rp2024shine_logo.png b/apps/info/public/rp2024shine_logo.png
new file mode 100644
index 00000000..5287b43a
Binary files /dev/null and b/apps/info/public/rp2024shine_logo.png differ
diff --git a/apps/info/public/spotlight.svg b/apps/info/public/spotlight.svg
new file mode 100644
index 00000000..83e62ca2
--- /dev/null
+++ b/apps/info/public/spotlight.svg
@@ -0,0 +1,12 @@
+
diff --git a/apps/info/public/vite.svg b/apps/info/public/vite.svg
deleted file mode 100644
index e7b8dfb1..00000000
--- a/apps/info/public/vite.svg
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/apps/info/src/components/AnimatedLogo.tsx b/apps/info/src/components/AnimatedLogo.tsx
new file mode 100644
index 00000000..0106991d
--- /dev/null
+++ b/apps/info/src/components/AnimatedLogo.tsx
@@ -0,0 +1,182 @@
+import { motion } from "framer-motion";
+import { useCallback, useEffect, useRef, useState } from "react";
+
+export const AnimatedLogoConstant = () => {
+ const changeImageFnRef = useRef<() => void>(() => {});
+ const [rotation, setRotation] = useState(0);
+ const [topOpacity, setTopOpacity] = useState(1);
+ const [topCurrentImage, setTopCurrentImage] = useState(0);
+ const [bottomCurrentImage, setBottomCurrentImage] = useState(-1);
+ const intervalRef = useRef(null);
+
+ const images = [
+ "logo_2025.svg",
+ "logo_2024.svg",
+ "logo_2023.svg",
+ "logo_2022.svg",
+ "logo_2021.svg",
+ "logo_2020.svg",
+ "logo_2019.svg",
+ "logo_2018.svg"
+ ];
+
+ // this updates every render so the ref always has a fresh function
+ changeImageFnRef.current = () => {
+ // switch to bottom
+ if (topOpacity) {
+ setRotation((prev) => prev + 360);
+ setBottomCurrentImage((prev) => (prev + 2) % images.length); // bottom switches by 2
+ setTopOpacity((prev) => (prev + 1) % 2); // top fades out
+ }
+ // switch to top
+ else {
+ setRotation((prev) => prev + 360);
+ setTopCurrentImage((prev) => (prev + 2) % images.length); // top switches by 2
+ setTopOpacity((prev) => (prev + 1) % 2); // top fades in
+ }
+ };
+
+ const stopRotation = () => {
+ if (intervalRef.current) {
+ clearInterval(intervalRef.current);
+ }
+ };
+
+ const startRotation = useCallback(() => {
+ // if the browser 'focus' event fires twice, just
+ // stop the extra trigger here
+ stopRotation();
+ intervalRef.current = window.setInterval(() => {
+ changeImageFnRef.current();
+ }, 6000);
+ }, []);
+
+ useEffect(() => {
+ console.log("setting window listeners...");
+ startRotation();
+ window.addEventListener("blur", stopRotation);
+ window.addEventListener("focus", startRotation);
+
+ return () => {
+ stopRotation();
+ window.removeEventListener("blur", stopRotation);
+ window.removeEventListener("focus", startRotation);
+ };
+ }, [startRotation]); // happy linter... (and just in case)
+
+ return (
+ <>
+
+
+ >
+ );
+};
+
+export const AnimatedLogoHover = () => {
+ const [isHovered, setIsHovered] = useState(false);
+ const changeImageFnRef = useRef<() => void>(() => {});
+ const [rotation, setRotation] = useState(0);
+ const [topOpacity, setTopOpacity] = useState(1);
+ const [topCurrentImage, setTopCurrentImage] = useState(0);
+ const [bottomCurrentImage, setBottomCurrentImage] = useState(-1);
+ const intervalRef = useRef(null);
+
+ const images = [
+ "logo_info.svg",
+ "logo_2025.svg",
+ "logo_2024.svg",
+ "logo_2023.svg",
+ "logo_2022.svg",
+ "logo_2021.svg",
+ "logo_2020.svg",
+ "logo_2019.svg",
+ "logo_2018.svg"
+ ];
+
+ // this updates every render so the ref always has a fresh function
+ changeImageFnRef.current = () => {
+ // switch to bottom
+ if (topOpacity) {
+ setRotation((prev) => prev + 360);
+ setBottomCurrentImage((prev) => (prev + 2) % images.length); // bottom switches by 2
+ setTopOpacity((prev) => (prev + 1) % 2); // top fades out
+ }
+ // switch to top
+ else {
+ setRotation((prev) => prev + 360);
+ setTopCurrentImage((prev) => (prev + 2) % images.length); // top switches by 2
+ setTopOpacity((prev) => (prev + 1) % 2); // top fades in
+ }
+ };
+
+ // runs on hover on/off - either enabling or disabling the rotation interval
+ useEffect(() => {
+ if (isHovered) {
+ changeImageFnRef.current();
+ intervalRef.current = window.setInterval(() => {
+ changeImageFnRef.current();
+ }, 2000);
+ } else {
+ if (intervalRef.current) {
+ clearInterval(intervalRef.current);
+ }
+ }
+ // every time isHovered becomes false, useEffect runs here,
+ // hitting the else statement and killing the active interval.
+
+ return () => {
+ if (intervalRef.current) {
+ clearInterval(intervalRef.current);
+ }
+ };
+ }, [isHovered]);
+
+ return (
+ <>
+ setIsHovered(true)}
+ onMouseLeave={() => setIsHovered(false)}
+ animate={{ rotate: rotation, opacity: topOpacity }}
+ transition={{ duration: 0.5, ease: "easeInOut" }}
+ width="100%"
+ height="100%"
+ style={{
+ zIndex: 2,
+ position: "absolute"
+ }}
+ src={images[topCurrentImage]}
+ />
+
+ >
+ );
+};
diff --git a/apps/info/src/components/AnimatedPillar.tsx b/apps/info/src/components/AnimatedPillar.tsx
new file mode 100644
index 00000000..9b8b1010
--- /dev/null
+++ b/apps/info/src/components/AnimatedPillar.tsx
@@ -0,0 +1,88 @@
+import { Box } from "@chakra-ui/react";
+import { motion, useInView } from "framer-motion";
+import { useRef } from "react";
+
+const MotionBox = motion(Box);
+
+type AnimatedCounterProps = {
+ // icon?: string;
+ baseHeight: number;
+ heightDelta: number;
+ time: number;
+ children: React.ReactNode;
+};
+
+const AnimatedPillar: React.FC = ({
+ baseHeight,
+ heightDelta,
+ time,
+ children
+}) => {
+ const viewMarkerRef = useRef(null);
+ const isInView = useInView(viewMarkerRef);
+
+ return (
+
+
+ {/* pillar */}
+
+ {children}
+
+
+
+
+
+
+
+
+ );
+};
+
+export default AnimatedPillar;
diff --git a/apps/info/src/components/AnimatedPillarsSection.tsx b/apps/info/src/components/AnimatedPillarsSection.tsx
new file mode 100644
index 00000000..8d866c8b
--- /dev/null
+++ b/apps/info/src/components/AnimatedPillarsSection.tsx
@@ -0,0 +1,198 @@
+import { Box, VStack, Image, Flex, Container } from "@chakra-ui/react";
+import { motion, useScroll, useTransform } from "framer-motion";
+import { createPortal } from "react-dom";
+import AnimatedPillar from "./AnimatedPillar";
+
+interface AnimatedPillarsSectionProps {
+ icons: string[];
+}
+
+const MotionBox = motion(Box);
+const MotionContainer = motion(Container);
+const spotlightHeight = "975px";
+
+export const AnimatedPillarsSection: React.FC = ({
+ icons
+}) => {
+ const { scrollY } = useScroll();
+ const offset = 950;
+ const shadowOpacity = useTransform(
+ scrollY,
+ [-350 + offset, -60 + offset, 350 + offset, 650 + offset],
+ [0, 0.35, 0.35, 0]
+ );
+ const heightDiff = useTransform(
+ scrollY,
+ [-250 + offset, 700 + offset],
+ [200, -750]
+ );
+ const translateY = useTransform(heightDiff, (h) => `translateY(${h}px)`);
+
+ return (
+
+ {/* portal */}
+ {createPortal(
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ,
+ document.body
+ )}{" "}
+ {/* portal */}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+};
diff --git a/apps/info/src/components/Carousel.tsx b/apps/info/src/components/Carousel.tsx
index 7ae950ad..42993616 100644
--- a/apps/info/src/components/Carousel.tsx
+++ b/apps/info/src/components/Carousel.tsx
@@ -1,5 +1,5 @@
import React, { useState } from "react";
-import { Box, Heading, Flex, Image, Text, Button } from "@chakra-ui/react";
+import { Box, Flex, Image, Text, Button } from "@chakra-ui/react";
interface CarouselItem {
title: string;
@@ -102,9 +102,9 @@ const CircularCarousel: React.FC = ({ items }) => {
h="700px"
pt={8}
>
-
+ {/*
Meet the Team
-
+ */}
= ({ items }) => {