-
{team}
- {/* Team member nodes will go here */}
-
+
+
+
+ {TEAMS_BACKGROUND_STARS.map((star) => {
+ const starStyle: CSSProperties = {
+ top: `${star.top}%`,
+ left: `${star.left}%`,
+ width: `${star.size}px`,
+ height: `${star.size}px`,
+ opacity: star.opacity,
+ };
+
+ return (
+
+ );
+ })}
+
+
+
+
+
+ {TEAMS_COPY.eyebrow}
+
+
+ {TEAMS_COPY.heading[0]}
+
+ {TEAMS_COPY.heading[1]}
+
+
+ {ORDERED_OFFICER_TEAMS[displayedTeamIndex]?.description ?? ""}
+
+
+
+ {ORDERED_OFFICER_TEAMS[displayedTeamIndex]?.groupPhotoUrl ? (
+
+ ) : (
+
+
+ Group photo coming soon
+
+
+ )}
+
+
+
+
+
+
+ {desktopLayouts.map((layout, index) => (
+
+
+
+ ))}
+
- ))}
+
);
diff --git a/app/components/teams/constellationLayout.ts b/app/components/teams/constellationLayout.ts
new file mode 100644
index 0000000..fe5b410
--- /dev/null
+++ b/app/components/teams/constellationLayout.ts
@@ -0,0 +1,248 @@
+import constellationTemplatesData from "@/app/data/constellation-templates.json";
+import officerTeamsData from "@/app/data/officer-teams.json";
+
+export type OfficerMember = {
+ id: string;
+ name: string;
+ role: string;
+ linkedinUrl: string;
+ quote?: string;
+ imageUrl?: string;
+};
+
+export type OfficerTeam = {
+ id: string;
+ label: string;
+ order: number;
+ templateId?: string;
+ description?: string;
+ groupPhotoUrl?: string;
+ lead: OfficerMember;
+ members: OfficerMember[];
+};
+
+export type ConstellationNode = {
+ id: string;
+ x: number;
+ y: number;
+};
+
+export type ConstellationEdge = {
+ fromId: string;
+ toId: string;
+};
+
+export type ConstellationAppendRule = {
+ fromNodeId: string;
+ toNodeId: string;
+ stepX: number;
+ stepY: number;
+};
+
+export type ConstellationTemplate = {
+ id: string;
+ name: string;
+ starCount: number;
+ leadNodeId: string;
+ nodes: ConstellationNode[];
+ edges: ConstellationEdge[];
+ append: ConstellationAppendRule;
+};
+
+export type ResolvedOfficerNode = ConstellationNode & {
+ person: OfficerMember;
+ isLead: boolean;
+ isOverflow: boolean;
+};
+
+export type RenderedConstellationEdge = {
+ fromId: string;
+ toId: string;
+ from: { x: number; y: number };
+ to: { x: number; y: number };
+};
+
+export type RenderedOfficerNode = ResolvedOfficerNode & {
+ renderX: number;
+ renderY: number;
+};
+
+export type ResolvedConstellationLayout = {
+ team: OfficerTeam;
+ template: ConstellationTemplate;
+ nodes: RenderedOfficerNode[];
+ edges: RenderedConstellationEdge[];
+};
+
+const officerTeams = officerTeamsData as OfficerTeam[];
+const constellationTemplates = constellationTemplatesData as ConstellationTemplate[];
+
+export const ORDERED_OFFICER_TEAMS = officerTeams
+ .slice()
+ .sort((left, right) => left.order - right.order);
+
+function hashString(input: string) {
+ let hash = 0;
+
+ for (const character of input) {
+ hash = (hash * 31 + character.charCodeAt(0)) >>> 0;
+ }
+
+ return hash;
+}
+
+function pickTemplate(teamId: string, peopleCount: number, templateId?: string) {
+ if (templateId) {
+ const explicit = constellationTemplates.find((t) => t.id === templateId);
+ if (explicit) return explicit;
+ }
+
+ const availableCounts = Array.from(
+ new Set(constellationTemplates.map((template) => template.starCount)),
+ ).sort((left, right) => left - right);
+
+ const chosenCount =
+ availableCounts.filter((count) => count <= peopleCount).at(-1) ??
+ availableCounts[0];
+ const candidates = constellationTemplates.filter(
+ (template) => template.starCount === chosenCount,
+ );
+
+ return candidates[hashString(teamId) % candidates.length];
+}
+
+function getAssignmentOrder(template: ConstellationTemplate) {
+ return [
+ template.leadNodeId,
+ ...template.nodes
+ .map((node) => node.id)
+ .filter((nodeId) => nodeId !== template.leadNodeId),
+ ];
+}
+
+function fitNodesToBox(
+ nodes: ResolvedOfficerNode[],
+ width: number,
+ height: number,
+ padding: number,
+ verticalBias: number,
+) {
+ const minX = Math.min(...nodes.map((node) => node.x));
+ const maxX = Math.max(...nodes.map((node) => node.x));
+ const minY = Math.min(...nodes.map((node) => node.y));
+ const maxY = Math.max(...nodes.map((node) => node.y));
+ const rawWidth = Math.max(maxX - minX, 0.01);
+ const rawHeight = Math.max(maxY - minY, 0.01);
+ const scale = Math.min(
+ (width - padding * 2) / rawWidth,
+ (height - padding * 2) / rawHeight,
+ );
+ const contentWidth = rawWidth * scale;
+ const contentHeight = rawHeight * scale;
+ const offsetX = (width - contentWidth) / 2;
+ const centeredOffsetY = (height - contentHeight) / 2;
+ const offsetY = Math.min(
+ centeredOffsetY + verticalBias,
+ height - contentHeight - padding * 0.4,
+ );
+
+ return nodes.map((node) => ({
+ ...node,
+ renderX: (node.x - minX) * scale + offsetX,
+ renderY: (node.y - minY) * scale + offsetY,
+ }));
+}
+
+export function resolveConstellationLayout(
+ team: OfficerTeam,
+ width: number,
+ height: number,
+ padding: number,
+ verticalBias = 0,
+): ResolvedConstellationLayout {
+ const people = [team.lead, ...team.members];
+ const template = pickTemplate(team.id, people.length, team.templateId);
+ const assignmentOrder = getAssignmentOrder(template);
+ const selectedNodeIds = assignmentOrder.slice(
+ 0,
+ Math.min(people.length, template.nodes.length),
+ );
+ const selectedNodeIdSet = new Set(selectedNodeIds);
+ const resolvedNodes: ResolvedOfficerNode[] = template.nodes
+ .filter((node) => selectedNodeIdSet.has(node.id))
+ .map((node) => ({
+ ...node,
+ person:
+ people[
+ selectedNodeIds.findIndex((selectedNodeId) => selectedNodeId === node.id)
+ ],
+ isLead: node.id === template.leadNodeId,
+ isOverflow: false,
+ }));
+ const resolvedEdges: ConstellationEdge[] = template.edges.filter(
+ (edge) =>
+ selectedNodeIdSet.has(edge.fromId) && selectedNodeIdSet.has(edge.toId),
+ );
+
+ if (people.length > template.nodes.length) {
+ const anchorNode = template.nodes.find(
+ (node) => node.id === template.append.toNodeId,
+ );
+
+ if (anchorNode) {
+ let previousNodeId = anchorNode.id;
+
+ for (
+ let overflowIndex = 0;
+ overflowIndex < people.length - template.nodes.length;
+ overflowIndex += 1
+ ) {
+ const person = people[template.nodes.length + overflowIndex];
+ const overflowNodeId = `overflow-${overflowIndex + 1}`;
+ resolvedNodes.push({
+ id: overflowNodeId,
+ x: anchorNode.x + template.append.stepX * (overflowIndex + 1),
+ y: anchorNode.y + template.append.stepY * (overflowIndex + 1),
+ person,
+ isLead: false,
+ isOverflow: true,
+ });
+ resolvedEdges.push({
+ fromId: previousNodeId,
+ toId: overflowNodeId,
+ });
+ previousNodeId = overflowNodeId;
+ }
+ }
+ }
+
+ const renderedNodes = fitNodesToBox(
+ resolvedNodes,
+ width,
+ height,
+ padding,
+ verticalBias,
+ );
+ const nodeMap = new Map(
+ renderedNodes.map((node) => [node.id, { x: node.renderX, y: node.renderY }]),
+ );
+ const renderedEdges = resolvedEdges
+ .map((edge) => {
+ const from = nodeMap.get(edge.fromId);
+ const to = nodeMap.get(edge.toId);
+
+ if (!from || !to) {
+ return null;
+ }
+
+ return { fromId: edge.fromId, toId: edge.toId, from, to };
+ })
+ .filter((edge): edge is RenderedConstellationEdge => edge !== null);
+
+ return {
+ team,
+ template,
+ nodes: renderedNodes,
+ edges: renderedEdges,
+ };
+}
diff --git a/app/components/teams/sceneConfig.ts b/app/components/teams/sceneConfig.ts
new file mode 100644
index 0000000..fab8775
--- /dev/null
+++ b/app/components/teams/sceneConfig.ts
@@ -0,0 +1,102 @@
+export type AmbientStar = {
+ id: number;
+ top: number;
+ left: number;
+ size: number;
+ opacity: number;
+};
+
+export type ConstellationBox = {
+ width: number;
+ height: number;
+ padding: number;
+ verticalBias: number;
+ leadNodeSize: number;
+ nodeSize: number;
+};
+
+export const TEAMS_COPY = {
+ eyebrow: "HACKUTD",
+ heading: ["The", "Constellation"],
+} as const;
+
+export const TEAMS_LAYOUT = {
+ desktopSectionMinHeight: "min-h-[560vh]",
+ mobileSectionMinHeight: "min-h-[420vh]",
+ mobileSectionMinHeightAndroid: "min-h-[480vh]",
+ mobileSectionPadding: "px-5 py-24 sm:px-6",
+ mobileViewportHeight: "h-[100svh]",
+ mobileViewportHeightAndroid: "h-[100dvh]",
+ desktopViewportHeight: "h-[100svh] md:h-screen",
+ desktopContainer: "mx-auto flex h-full w-full max-w-[1800px] items-start pt-28 gap-8 px-5 md:px-8 lg:gap-10 lg:px-12",
+ introWidth: "w-[320px] shrink-0 lg:w-[400px]",
+ desktopHeading: "mt-6 text-5xl font-semibold leading-none text-foreground lg:text-6xl",
+ mobileHeading: "mt-5 text-4xl font-semibold leading-none text-foreground sm:text-5xl",
+ desktopTrackViewport: "relative min-w-0 flex-1 overflow-x-hidden overflow-y-visible",
+} as const;
+
+export const TEAMS_SCROLL = {
+ smoothing: 0.22,
+ desktopGap: 10,
+ desktopTrailingSpace: 180,
+ desktopPeekWidth: 150,
+ separatorWidth: 380,
+ firstConstellationOffset: 160,
+ tooltipCloseDelayMs: 140,
+} as const;
+
+export const TEAM_CLUSTER_BOX = {
+ desktop: {
+ width: 980,
+ height: 380,
+ padding: 42,
+ verticalBias: 22,
+ leadNodeSize: 72,
+ nodeSize: 50,
+ },
+ mobile: {
+ width: 310,
+ height: 260,
+ padding: 30,
+ verticalBias: 14,
+ leadNodeSize: 62,
+ nodeSize: 46,
+ },
+} as const;
+
+function clamp(value: number, min: number, max: number) {
+ return Math.min(Math.max(value, min), max);
+}
+
+export function getDesktopConstellationBox(
+ trackViewportWidth: number,
+ viewportHeight: number,
+): ConstellationBox {
+ return {
+ width: clamp(trackViewportWidth * 0.95, 820, 1380),
+ height: clamp(viewportHeight * 0.66, 420, 600),
+ padding: clamp(trackViewportWidth * 0.028, 60, 80),
+ verticalBias: 50,
+ leadNodeSize: 74,
+ nodeSize: 56,
+ };
+}
+
+function createAmbientStars(count: number): AmbientStar[] {
+ let seed = 71;
+
+ const next = () => {
+ seed = (seed * 48271) % 2147483647;
+ return seed / 2147483647;
+ };
+
+ return Array.from({ length: count }, (_, index) => ({
+ id: index,
+ top: 8 + next() * 82,
+ left: 2 + next() * 96,
+ size: 1 + next() * 2.8,
+ opacity: 0.16 + next() * 0.4,
+ }));
+}
+
+export const TEAMS_BACKGROUND_STARS = createAmbientStars(34);
diff --git a/app/data/constellation-templates.json b/app/data/constellation-templates.json
new file mode 100644
index 0000000..e8830d4
--- /dev/null
+++ b/app/data/constellation-templates.json
@@ -0,0 +1,414 @@
+[
+ {
+ "id": "cassiopeia",
+ "name": "Cassiopeia",
+ "starCount": 5,
+ "leadNodeId": "n0",
+ "nodes": [
+ { "id": "n0", "x": 0.48, "y": 0.12 },
+ { "id": "n1", "x": 0.16, "y": 0.3 },
+ { "id": "n2", "x": 0.04, "y": 0.66 },
+ { "id": "n3", "x": 0.7, "y": 0.44 },
+ { "id": "n4", "x": 0.96, "y": 0.72 }
+ ],
+ "edges": [
+ { "fromId": "n1", "toId": "n0" },
+ { "fromId": "n0", "toId": "n2" },
+ { "fromId": "n2", "toId": "n3" },
+ { "fromId": "n3", "toId": "n4" }
+ ],
+ "append": {
+ "fromNodeId": "n3",
+ "toNodeId": "n4",
+ "stepX": 0.16,
+ "stepY": 0.08
+ }
+ },
+ {
+ "id": "lyra",
+ "name": "Lyra",
+ "starCount": 5,
+ "leadNodeId": "n0",
+ "nodes": [
+ { "id": "n0", "x": 0.64, "y": 0.1 },
+ { "id": "n1", "x": 0.28, "y": 0.32 },
+ { "id": "n2", "x": 0.68, "y": 0.46 },
+ { "id": "n3", "x": 0.3, "y": 0.76 },
+ { "id": "n4", "x": 0.78, "y": 0.88 }
+ ],
+ "edges": [
+ { "fromId": "n0", "toId": "n1" },
+ { "fromId": "n1", "toId": "n2" },
+ { "fromId": "n2", "toId": "n3" },
+ { "fromId": "n2", "toId": "n4" }
+ ],
+ "append": {
+ "fromNodeId": "n2",
+ "toNodeId": "n4",
+ "stepX": 0.12,
+ "stepY": 0.1
+ }
+ },
+ {
+ "id": "delphinus",
+ "name": "Delphinus",
+ "starCount": 5,
+ "leadNodeId": "n0",
+ "nodes": [
+ { "id": "n0", "x": 0.52, "y": 0.1 },
+ { "id": "n1", "x": 0.18, "y": 0.4 },
+ { "id": "n2", "x": 0.5, "y": 0.5 },
+ { "id": "n3", "x": 0.82, "y": 0.42 },
+ { "id": "n4", "x": 0.64, "y": 0.86 }
+ ],
+ "edges": [
+ { "fromId": "n0", "toId": "n1" },
+ { "fromId": "n1", "toId": "n2" },
+ { "fromId": "n2", "toId": "n3" },
+ { "fromId": "n3", "toId": "n0" },
+ { "fromId": "n2", "toId": "n4" }
+ ],
+ "append": {
+ "fromNodeId": "n2",
+ "toNodeId": "n4",
+ "stepX": 0.08,
+ "stepY": 0.16
+ }
+ },
+ {
+ "id": "orion",
+ "name": "Orion",
+ "starCount": 7,
+ "leadNodeId": "n0",
+ "nodes": [
+ { "id": "n0", "x": 0.24, "y": 0.1 },
+ { "id": "n1", "x": 0.76, "y": 0.18 },
+ { "id": "n2", "x": 0.4, "y": 0.36 },
+ { "id": "n3", "x": 0.5, "y": 0.48 },
+ { "id": "n4", "x": 0.6, "y": 0.58 },
+ { "id": "n5", "x": 0.28, "y": 0.82 },
+ { "id": "n6", "x": 0.78, "y": 0.9 }
+ ],
+ "edges": [
+ { "fromId": "n0", "toId": "n2" },
+ { "fromId": "n1", "toId": "n4" },
+ { "fromId": "n2", "toId": "n3" },
+ { "fromId": "n3", "toId": "n4" },
+ { "fromId": "n2", "toId": "n5" },
+ { "fromId": "n4", "toId": "n6" }
+ ],
+ "append": {
+ "fromNodeId": "n4",
+ "toNodeId": "n6",
+ "stepX": 0.12,
+ "stepY": 0.12
+ }
+ },
+ {
+ "id": "aquila",
+ "name": "Aquila",
+ "starCount": 8,
+ "leadNodeId": "n0",
+ "nodes": [
+ { "id": "n0", "x": 0.5, "y": 0.08 },
+ { "id": "n1", "x": 0.16, "y": 0.24 },
+ { "id": "n2", "x": 0.34, "y": 0.34 },
+ { "id": "n3", "x": 0.66, "y": 0.38 },
+ { "id": "n4", "x": 0.86, "y": 0.24 },
+ { "id": "n5", "x": 0.42, "y": 0.58 },
+ { "id": "n6", "x": 0.56, "y": 0.72 },
+ { "id": "n7", "x": 0.68, "y": 0.92 }
+ ],
+ "edges": [
+ { "fromId": "n1", "toId": "n2" },
+ { "fromId": "n2", "toId": "n0" },
+ { "fromId": "n0", "toId": "n3" },
+ { "fromId": "n3", "toId": "n4" },
+ { "fromId": "n2", "toId": "n5" },
+ { "fromId": "n5", "toId": "n6" },
+ { "fromId": "n6", "toId": "n7" },
+ { "fromId": "n3", "toId": "n6" }
+ ],
+ "append": {
+ "fromNodeId": "n6",
+ "toNodeId": "n7",
+ "stepX": 0.1,
+ "stepY": 0.14
+ }
+ },
+ {
+ "id": "cepheus",
+ "name": "Cepheus",
+ "starCount": 8,
+ "leadNodeId": "n0",
+ "nodes": [
+ { "id": "n0", "x": 0.56, "y": 0.08 },
+ { "id": "n1", "x": 0.28, "y": 0.28 },
+ { "id": "n2", "x": 0.76, "y": 0.28 },
+ { "id": "n3", "x": 0.12, "y": 0.54 },
+ { "id": "n4", "x": 0.68, "y": 0.56 },
+ { "id": "n5", "x": 0.3, "y": 0.82 },
+ { "id": "n6", "x": 0.58, "y": 0.84 },
+ { "id": "n7", "x": 0.94, "y": 0.58 }
+ ],
+ "edges": [
+ { "fromId": "n1", "toId": "n0" },
+ { "fromId": "n0", "toId": "n2" },
+ { "fromId": "n1", "toId": "n3" },
+ { "fromId": "n3", "toId": "n5" },
+ { "fromId": "n5", "toId": "n6" },
+ { "fromId": "n6", "toId": "n4" },
+ { "fromId": "n4", "toId": "n2" },
+ { "fromId": "n4", "toId": "n7" }
+ ],
+ "append": {
+ "fromNodeId": "n4",
+ "toNodeId": "n7",
+ "stepX": 0.14,
+ "stepY": 0.04
+ }
+ },
+ {
+ "id": "cygnus",
+ "name": "Cygnus",
+ "starCount": 9,
+ "leadNodeId": "n0",
+ "nodes": [
+ { "id": "n0", "x": 0.5, "y": 0.06 },
+ { "id": "n1", "x": 0.5, "y": 0.24 },
+ { "id": "n2", "x": 0.5, "y": 0.44 },
+ { "id": "n3", "x": 0.5, "y": 0.68 },
+ { "id": "n4", "x": 0.5, "y": 0.92 },
+ { "id": "n5", "x": 0.18, "y": 0.42 },
+ { "id": "n6", "x": 0.32, "y": 0.5 },
+ { "id": "n7", "x": 0.68, "y": 0.5 },
+ { "id": "n8", "x": 0.82, "y": 0.42 }
+ ],
+ "edges": [
+ { "fromId": "n0", "toId": "n1" },
+ { "fromId": "n1", "toId": "n2" },
+ { "fromId": "n2", "toId": "n3" },
+ { "fromId": "n3", "toId": "n4" },
+ { "fromId": "n5", "toId": "n6" },
+ { "fromId": "n6", "toId": "n2" },
+ { "fromId": "n2", "toId": "n7" },
+ { "fromId": "n7", "toId": "n8" }
+ ],
+ "append": {
+ "fromNodeId": "n3",
+ "toNodeId": "n4",
+ "stepX": 0,
+ "stepY": 0.14
+ }
+ },
+ {
+ "id": "andromeda",
+ "name": "Andromeda",
+ "starCount": 9,
+ "leadNodeId": "n0",
+ "nodes": [
+ { "id": "n0", "x": 0.18, "y": 0.18 },
+ { "id": "n1", "x": 0.34, "y": 0.3 },
+ { "id": "n2", "x": 0.5, "y": 0.42 },
+ { "id": "n3", "x": 0.68, "y": 0.54 },
+ { "id": "n4", "x": 0.88, "y": 0.68 },
+ { "id": "n5", "x": 0.32, "y": 0.56 },
+ { "id": "n6", "x": 0.44, "y": 0.72 },
+ { "id": "n7", "x": 0.62, "y": 0.84 },
+ { "id": "n8", "x": 0.82, "y": 0.92 }
+ ],
+ "edges": [
+ { "fromId": "n0", "toId": "n1" },
+ { "fromId": "n1", "toId": "n2" },
+ { "fromId": "n2", "toId": "n3" },
+ { "fromId": "n3", "toId": "n4" },
+ { "fromId": "n1", "toId": "n5" },
+ { "fromId": "n5", "toId": "n6" },
+ { "fromId": "n6", "toId": "n7" },
+ { "fromId": "n7", "toId": "n8" }
+ ],
+ "append": {
+ "fromNodeId": "n7",
+ "toNodeId": "n8",
+ "stepX": 0.12,
+ "stepY": 0.08
+ }
+ },
+ {
+ "id": "perseus",
+ "name": "Perseus",
+ "starCount": 7,
+ "leadNodeId": "n0",
+ "nodes": [
+ { "id": "n0", "x": 0.46, "y": 0.46 },
+ { "id": "n1", "x": 0.10, "y": 0.14 },
+ { "id": "n2", "x": 0.28, "y": 0.20 },
+ { "id": "n3", "x": 0.50, "y": 0.14 },
+ { "id": "n4", "x": 0.70, "y": 0.22 },
+ { "id": "n5", "x": 0.78, "y": 0.54 },
+ { "id": "n6", "x": 0.64, "y": 0.80 }
+ ],
+ "edges": [
+ { "fromId": "n1", "toId": "n2" },
+ { "fromId": "n2", "toId": "n3" },
+ { "fromId": "n3", "toId": "n4" },
+ { "fromId": "n2", "toId": "n0" },
+ { "fromId": "n4", "toId": "n0" },
+ { "fromId": "n0", "toId": "n5" },
+ { "fromId": "n5", "toId": "n6" }
+ ],
+ "append": {
+ "fromNodeId": "n5",
+ "toNodeId": "n6",
+ "stepX": -0.10,
+ "stepY": 0.14
+ }
+ },
+ {
+ "id": "bootes",
+ "name": "Boötes",
+ "starCount": 7,
+ "leadNodeId": "n0",
+ "nodes": [
+ { "id": "n0", "x": 0.50, "y": 0.90 },
+ { "id": "n1", "x": 0.50, "y": 0.60 },
+ { "id": "n2", "x": 0.30, "y": 0.76 },
+ { "id": "n3", "x": 0.28, "y": 0.28 },
+ { "id": "n4", "x": 0.50, "y": 0.08 },
+ { "id": "n5", "x": 0.72, "y": 0.28 },
+ { "id": "n6", "x": 0.12, "y": 0.48 }
+ ],
+ "edges": [
+ { "fromId": "n0", "toId": "n2" },
+ { "fromId": "n2", "toId": "n1" },
+ { "fromId": "n1", "toId": "n0" },
+ { "fromId": "n1", "toId": "n3" },
+ { "fromId": "n3", "toId": "n4" },
+ { "fromId": "n4", "toId": "n5" },
+ { "fromId": "n5", "toId": "n1" },
+ { "fromId": "n3", "toId": "n6" }
+ ],
+ "append": {
+ "fromNodeId": "n3",
+ "toNodeId": "n6",
+ "stepX": -0.12,
+ "stepY": 0.10
+ }
+ },
+ {
+ "id": "castor",
+ "name": "Castor System",
+ "starCount": 6,
+ "leadNodeId": "n0",
+ "nodes": [
+ { "id": "n0", "x": 0.38, "y": 0.14 },
+ { "id": "n1", "x": 0.58, "y": 0.22 },
+ { "id": "n2", "x": 0.14, "y": 0.50 },
+ { "id": "n3", "x": 0.34, "y": 0.62 },
+ { "id": "n4", "x": 0.66, "y": 0.72 },
+ { "id": "n5", "x": 0.84, "y": 0.58 }
+ ],
+ "edges": [
+ { "fromId": "n0", "toId": "n1" },
+ { "fromId": "n2", "toId": "n3" },
+ { "fromId": "n4", "toId": "n5" },
+ { "fromId": "n0", "toId": "n2" },
+ { "fromId": "n1", "toId": "n4" },
+ { "fromId": "n3", "toId": "n4" }
+ ],
+ "append": {
+ "fromNodeId": "n4",
+ "toNodeId": "n5",
+ "stepX": 0.12,
+ "stepY": -0.10
+ }
+ },
+ {
+ "id": "cetus",
+ "name": "Cetus",
+ "starCount": 14,
+ "leadNodeId": "n0",
+ "nodes": [
+ { "id": "n0", "x": 0.18, "y": 0.10 },
+ { "id": "n1", "x": 0.06, "y": 0.26 },
+ { "id": "n2", "x": 0.24, "y": 0.36 },
+ { "id": "n3", "x": 0.38, "y": 0.22 },
+ { "id": "n4", "x": 0.44, "y": 0.36 },
+ { "id": "n5", "x": 0.54, "y": 0.46 },
+ { "id": "n6", "x": 0.62, "y": 0.28 },
+ { "id": "n7", "x": 0.76, "y": 0.38 },
+ { "id": "n8", "x": 0.88, "y": 0.52 },
+ { "id": "n9", "x": 0.78, "y": 0.66 },
+ { "id": "n10", "x": 0.60, "y": 0.72 },
+ { "id": "n11", "x": 0.48, "y": 0.80 },
+ { "id": "n12", "x": 0.36, "y": 0.90 },
+ { "id": "n13", "x": 0.52, "y": 0.94 }
+ ],
+ "edges": [
+ { "fromId": "n1", "toId": "n0" },
+ { "fromId": "n0", "toId": "n3" },
+ { "fromId": "n3", "toId": "n2" },
+ { "fromId": "n2", "toId": "n1" },
+ { "fromId": "n3", "toId": "n4" },
+ { "fromId": "n4", "toId": "n5" },
+ { "fromId": "n5", "toId": "n6" },
+ { "fromId": "n6", "toId": "n7" },
+ { "fromId": "n7", "toId": "n8" },
+ { "fromId": "n8", "toId": "n9" },
+ { "fromId": "n9", "toId": "n10" },
+ { "fromId": "n5", "toId": "n10" },
+ { "fromId": "n10", "toId": "n11" },
+ { "fromId": "n11", "toId": "n12" },
+ { "fromId": "n12", "toId": "n13" }
+ ],
+ "append": {
+ "fromNodeId": "n12",
+ "toNodeId": "n13",
+ "stepX": 0.10,
+ "stepY": 0.08
+ }
+ },
+ {
+ "id": "ophiuchus",
+ "name": "Ophiuchus",
+ "starCount": 13,
+ "leadNodeId": "n0",
+ "nodes": [
+ { "id": "n0", "x": 0.50, "y": 0.06 },
+ { "id": "n1", "x": 0.72, "y": 0.20 },
+ { "id": "n2", "x": 0.28, "y": 0.18 },
+ { "id": "n3", "x": 0.68, "y": 0.40 },
+ { "id": "n4", "x": 0.32, "y": 0.38 },
+ { "id": "n5", "x": 0.50, "y": 0.50 },
+ { "id": "n6", "x": 0.64, "y": 0.62 },
+ { "id": "n7", "x": 0.36, "y": 0.60 },
+ { "id": "n8", "x": 0.74, "y": 0.74 },
+ { "id": "n9", "x": 0.24, "y": 0.74 },
+ { "id": "n10", "x": 0.78, "y": 0.88 },
+ { "id": "n11", "x": 0.18, "y": 0.88 },
+ { "id": "n12", "x": 0.50, "y": 0.94 }
+ ],
+ "edges": [
+ { "fromId": "n0", "toId": "n1" },
+ { "fromId": "n0", "toId": "n2" },
+ { "fromId": "n1", "toId": "n3" },
+ { "fromId": "n2", "toId": "n4" },
+ { "fromId": "n3", "toId": "n5" },
+ { "fromId": "n4", "toId": "n5" },
+ { "fromId": "n5", "toId": "n6" },
+ { "fromId": "n5", "toId": "n7" },
+ { "fromId": "n6", "toId": "n8" },
+ { "fromId": "n7", "toId": "n9" },
+ { "fromId": "n8", "toId": "n10" },
+ { "fromId": "n9", "toId": "n11" },
+ { "fromId": "n10", "toId": "n12" },
+ { "fromId": "n11", "toId": "n12" }
+ ],
+ "append": {
+ "fromNodeId": "n10",
+ "toNodeId": "n12",
+ "stepX": 0.0,
+ "stepY": 0.10
+ }
+ }
+]
diff --git a/app/data/officer-teams.json b/app/data/officer-teams.json
new file mode 100644
index 0000000..b13fc9b
--- /dev/null
+++ b/app/data/officer-teams.json
@@ -0,0 +1,407 @@
+[
+ {
+ "id": "marketing",
+ "label": "Marketing",
+ "order": 0,
+ "templateId": "cetus",
+ "lead": {
+ "id": "marketing-lead",
+ "name": "Dhivyesh Prithiviraj",
+ "role": "Team Lead",
+ "linkedinUrl": "https://www.linkedin.com/in/dhivyeshprithiviraj/",
+ "quote": "Building the story behind HackUTD one campaign at a time.",
+ "imageUrl": ""
+ },
+ "members": [
+ {
+ "id": "marketing-1",
+ "name": "Maya Patel",
+ "role": "Content Strategy",
+ "linkedinUrl": "https://www.linkedin.com/in/maya-patel-hackutd",
+ "quote": "Good content makes people feel something.",
+ "imageUrl": ""
+ },
+ {
+ "id": "marketing-2",
+ "name": "Ethan Nguyen",
+ "role": "Brand Design",
+ "linkedinUrl": "https://www.linkedin.com/in/ethan-nguyen-hackutd",
+ "quote": "Design is the silent ambassador of your brand.",
+ "imageUrl": ""
+ },
+ {
+ "id": "marketing-3",
+ "name": "Sofia Ramirez",
+ "role": "Social Media",
+ "linkedinUrl": "https://www.linkedin.com/in/sofia-ramirez-hackutd",
+ "quote": "Every post is a chance to spark curiosity.",
+ "imageUrl": ""
+ },
+ {
+ "id": "marketing-4",
+ "name": "Jordan Kim",
+ "role": "Photo + Video",
+ "linkedinUrl": "https://www.linkedin.com/in/jordan-kim-hackutd",
+ "quote": "A frame can capture a feeling words can't.",
+ "imageUrl": ""
+ },
+ {
+ "id": "marketing-5",
+ "name": "Avery Brooks",
+ "role": "Copywriting",
+ "linkedinUrl": "https://www.linkedin.com/in/avery-brooks-hackutd",
+ "quote": "Words are the architecture of ideas.",
+ "imageUrl": ""
+ },
+ {
+ "id": "marketing-6",
+ "name": "Maya Patel",
+ "role": "Content Strategy",
+ "linkedinUrl": "https://www.linkedin.com/in/maya-patel-hackutd",
+ "quote": "Good content makes people feel something.",
+ "imageUrl": ""
+ },
+ {
+ "id": "marketing-7",
+ "name": "Maya Patel",
+ "role": "Content Strategy",
+ "linkedinUrl": "https://www.linkedin.com/in/maya-patel-hackutd",
+ "quote": "Good content makes people feel something.",
+ "imageUrl": ""
+ },
+ {
+ "id": "marketing-8",
+ "name": "Maya Patel",
+ "role": "Content Strategy",
+ "linkedinUrl": "https://www.linkedin.com/in/maya-patel-hackutd",
+ "quote": "Good content makes people feel something.",
+ "imageUrl": ""
+ },
+ {
+ "id": "marketing-9",
+ "name": "Maya Patel",
+ "role": "Content Strategy",
+ "linkedinUrl": "https://www.linkedin.com/in/maya-patel-hackutd",
+ "quote": "Good content makes people feel something.",
+ "imageUrl": ""
+ },
+ {
+ "id": "marketing-10",
+ "name": "Maya Patel",
+ "role": "Content Strategy",
+ "linkedinUrl": "https://www.linkedin.com/in/maya-patel-hackutd",
+ "quote": "Good content makes people feel something.",
+ "imageUrl": ""
+ },
+ {
+ "id": "marketing-11",
+ "name": "Maya Patel",
+ "role": "Content Strategy",
+ "linkedinUrl": "https://www.linkedin.com/in/maya-patel-hackutd",
+ "quote": "Good content makes people feel something.",
+ "imageUrl": ""
+ },
+ {
+ "id": "marketing-12",
+ "name": "Maya Patel",
+ "role": "Content Strategy",
+ "linkedinUrl": "https://www.linkedin.com/in/maya-patel-hackutd",
+ "quote": "Good content makes people feel something.",
+ "imageUrl": ""
+ },
+ {
+ "id": "marketing-13",
+ "name": "Maya Patel",
+ "role": "Content Strategy",
+ "linkedinUrl": "https://www.linkedin.com/in/maya-patel-hackutd",
+ "quote": "Good content makes people feel something.",
+ "imageUrl": ""
+ }
+ ],
+ "description": "Crafting the visual identity and story of HackUTD — from social campaigns to brand design, they make sure the world knows we exist."
+ },
+ {
+ "id": "tech",
+ "label": "Tech",
+ "order": 1,
+ "templateId": "orion",
+ "lead": {
+ "id": "tech-lead",
+ "name": "Caleb Bae",
+ "role": "Team Lead",
+ "linkedinUrl": "https://www.linkedin.com/in/baecal000/",
+ "quote": "The best infrastructure is the kind nobody notices.",
+ "imageUrl": ""
+ },
+ "members": [
+ {
+ "id": "tech-1",
+ "name": "Aiden Williams",
+ "role": "Platform Engineering",
+ "linkedinUrl": "https://www.linkedin.com/in/aiden-williams-hackutd",
+ "quote": "Scalability isn't an afterthought — it's a mindset.",
+ "imageUrl": ""
+ },
+ {
+ "id": "tech-2",
+ "name": "Priya Shah",
+ "role": "Infra + DevOps",
+ "linkedinUrl": "https://www.linkedin.com/in/priya-shah-hackutd",
+ "quote": "Automate the boring so humans can do the interesting.",
+ "imageUrl": ""
+ },
+ {
+ "id": "tech-3",
+ "name": "Lucas Martin",
+ "role": "Registration Systems",
+ "linkedinUrl": "https://www.linkedin.com/in/lucas-martin-hackutd",
+ "quote": "A smooth check-in sets the tone for everything after.",
+ "imageUrl": ""
+ },
+ {
+ "id": "tech-4",
+ "name": "Grace Lee",
+ "role": "Website Experience",
+ "linkedinUrl": "https://www.linkedin.com/in/grace-lee-hackutd",
+ "quote": "The website is every hacker's first impression of us.",
+ "imageUrl": ""
+ },
+ {
+ "id": "tech-5",
+ "name": "Mateo Garcia",
+ "role": "Internal Tooling",
+ "linkedinUrl": "https://www.linkedin.com/in/mateo-garcia-hackutd",
+ "quote": "Tools that work quietly keep teams moving fast.",
+ "imageUrl": ""
+ },
+ {
+ "id": "tech-6",
+ "name": "Chloe Nguyen",
+ "role": "Security + Reliability",
+ "linkedinUrl": "https://www.linkedin.com/in/chloe-nguyen-hackutd",
+ "quote": "Security is a feature, not a fix.",
+ "imageUrl": ""
+ }
+ ],
+ "description": "Building the platform that powers HackUTD — registration, infrastructure, and every line of code that keeps 1,000+ hackers online."
+ },
+ {
+ "id": "industry",
+ "label": "Industry",
+ "order": 2,
+ "templateId": "perseus",
+ "lead": {
+ "id": "industry-lead",
+ "name": "Sachi Hansalia",
+ "role": "Team Lead",
+ "linkedinUrl": "https://www.linkedin.com/in/sachi-hansalia/",
+ "quote": "Great partnerships start with a genuine conversation.",
+ "imageUrl": ""
+ },
+ "members": [
+ {
+ "id": "industry-1",
+ "name": "Ryan Park",
+ "role": "Sponsor Outreach",
+ "linkedinUrl": "https://www.linkedin.com/in/ryan-park-hackutd",
+ "quote": "Every cold email is a door waiting to open.",
+ "imageUrl": ""
+ },
+ {
+ "id": "industry-2",
+ "name": "Anika Singh",
+ "role": "Partner Success",
+ "linkedinUrl": "https://www.linkedin.com/in/anika-singh-hackutd",
+ "quote": "Success is when sponsors come back year after year.",
+ "imageUrl": ""
+ },
+ {
+ "id": "industry-3",
+ "name": "Daniel Scott",
+ "role": "Relationships",
+ "linkedinUrl": "https://www.linkedin.com/in/daniel-scott-hackutd",
+ "quote": "Long-term relationships outlast any single event.",
+ "imageUrl": ""
+ },
+ {
+ "id": "industry-4",
+ "name": "Harper Wilson",
+ "role": "Prospecting",
+ "linkedinUrl": "https://www.linkedin.com/in/harper-wilson-hackutd",
+ "quote": "Find the companies that believe in what we build.",
+ "imageUrl": ""
+ },
+ {
+ "id": "industry-5",
+ "name": "Kevin Flores",
+ "role": "Deliverables",
+ "linkedinUrl": "https://www.linkedin.com/in/kevin-flores-hackutd",
+ "quote": "Under-promise, over-deliver — every single time.",
+ "imageUrl": ""
+ },
+ {
+ "id": "industry-6",
+ "name": "Isabella Moore",
+ "role": "Stewardship",
+ "linkedinUrl": "https://www.linkedin.com/in/isabella-moore-hackutd",
+ "quote": "Gratitude is how you turn a sponsor into a partner.",
+ "imageUrl": ""
+ }
+ ],
+ "description": "The bridge between HackUTD and the companies that believe in what we build. They turn cold outreach into lasting partnerships."
+ },
+ {
+ "id": "experience",
+ "label": "Experience",
+ "order": 3,
+ "templateId": "bootes",
+ "lead": {
+ "id": "experience-lead",
+ "name": "Liana Forster",
+ "role": "Team Lead",
+ "linkedinUrl": "https://www.linkedin.com/in/liana-forster/",
+ "quote": "The best hackathon is the one people can't stop talking about.",
+ "imageUrl": ""
+ },
+ "members": [
+ {
+ "id": "experience-1",
+ "name": "Zoe Hernandez",
+ "role": "Operations",
+ "linkedinUrl": "https://www.linkedin.com/in/zoe-hernandez-hackutd",
+ "quote": "Chaos is just a plan that hasn't been written yet.",
+ "imageUrl": ""
+ },
+ {
+ "id": "experience-2",
+ "name": "Adam Walker",
+ "role": "Volunteer Coordination",
+ "linkedinUrl": "https://www.linkedin.com/in/adam-walker-hackutd",
+ "quote": "Volunteers are the heartbeat of every event.",
+ "imageUrl": ""
+ },
+ {
+ "id": "experience-3",
+ "name": "Nina Robinson",
+ "role": "Check-In Flow",
+ "linkedinUrl": "https://www.linkedin.com/in/nina-robinson-hackutd",
+ "quote": "First impressions are made at the door.",
+ "imageUrl": ""
+ },
+ {
+ "id": "experience-4",
+ "name": "Tyler Hall",
+ "role": "Venue Logistics",
+ "linkedinUrl": "https://www.linkedin.com/in/tyler-hall-hackutd",
+ "quote": "The space shapes the experience.",
+ "imageUrl": ""
+ },
+ {
+ "id": "experience-5",
+ "name": "Claire Lewis",
+ "role": "Swag + Merch",
+ "linkedinUrl": "https://www.linkedin.com/in/claire-lewis-hackutd",
+ "quote": "Good swag is something you actually want to keep.",
+ "imageUrl": ""
+ },
+ {
+ "id": "experience-6",
+ "name": "Isaac Young",
+ "role": "Food + Hospitality",
+ "linkedinUrl": "https://www.linkedin.com/in/isaac-young-hackutd",
+ "quote": "Feed the hackers, fuel the ideas.",
+ "imageUrl": ""
+ }
+ ],
+ "description": "Turning 24 hours into an unforgettable experience — from the moment hackers walk in to the moment they leave inspired."
+ },
+ {
+ "id": "logistics",
+ "label": "Logistics",
+ "order": 4,
+ "templateId": "castor",
+ "lead": {
+ "id": "logistics-lead",
+ "name": "Sofia Thomas",
+ "role": "Team Lead",
+ "linkedinUrl": "https://www.linkedin.com/in/sofia-thomas-82055832b/",
+ "quote": "Coming soon.",
+ "imageUrl": ""
+ },
+ "members": [
+ {
+ "id": "logistics-1",
+ "name": "TBD",
+ "role": "TBD",
+ "linkedinUrl": "https://www.linkedin.com/in/",
+ "quote": "Coming soon.",
+ "imageUrl": ""
+ },
+ {
+ "id": "logistics-2",
+ "name": "TBD",
+ "role": "TBD",
+ "linkedinUrl": "https://www.linkedin.com/in/",
+ "quote": "Coming soon.",
+ "imageUrl": ""
+ },
+ {
+ "id": "logistics-3",
+ "name": "TBD",
+ "role": "TBD",
+ "linkedinUrl": "https://www.linkedin.com/in/",
+ "quote": "Coming soon.",
+ "imageUrl": ""
+ },
+ {
+ "id": "logistics-4",
+ "name": "TBD",
+ "role": "TBD",
+ "linkedinUrl": "https://www.linkedin.com/in/",
+ "quote": "Coming soon.",
+ "imageUrl": ""
+ },
+ {
+ "id": "logistics-5",
+ "name": "TBD",
+ "role": "TBD",
+ "linkedinUrl": "https://www.linkedin.com/in/",
+ "quote": "Coming soon.",
+ "imageUrl": ""
+ }
+ ],
+ "description": "The backbone of the event. They handle the details nobody sees so that everything everyone does see runs flawlessly."
+ },
+ {
+ "id": "finance",
+ "label": "Finance",
+ "order": 5,
+ "lead": {
+ "id": "finance-lead",
+ "name": "Aatish Bommisetty",
+ "role": "Team Lead",
+ "linkedinUrl": "https://www.linkedin.com/in/aatishbommisetty/",
+ "quote": "Every dollar we spend is a decision about our priorities.",
+ "imageUrl": ""
+ },
+ "members": [
+ {
+ "id": "finance-1",
+ "name": "Caleb Adams",
+ "role": "Budgeting",
+ "linkedinUrl": "https://www.linkedin.com/in/caleb-adams-hackutd",
+ "quote": "A budget is your vision translated into numbers.",
+ "imageUrl": ""
+ },
+ {
+ "id": "finance-2",
+ "name": "Sarah Murphy",
+ "role": "Procurement",
+ "linkedinUrl": "https://www.linkedin.com/in/sarah-murphy-hackutd",
+ "quote": "Getting the right things at the right price is an art.",
+ "imageUrl": ""
+ }
+ ],
+ "description": "Keeping HackUTD financially sound — from budget planning to vendor payments, they make sure every dollar serves a purpose."
+ }
+]
diff --git a/app/data/teams.ts b/app/data/teams.ts
deleted file mode 100644
index 3e62e81..0000000
--- a/app/data/teams.ts
+++ /dev/null
@@ -1 +0,0 @@
-export const teams = ["Marketing", "Tech"];
diff --git a/app/globals.css b/app/globals.css
index 8b2cfb1..e7cb4cf 100644
--- a/app/globals.css
+++ b/app/globals.css
@@ -40,6 +40,23 @@ body {
}
}
+@keyframes constellation-lead-pulse {
+ 0%,
+ 100% {
+ transform: scale(1);
+ box-shadow:
+ 0 0 0 4px rgba(243, 22, 103, 0.16),
+ 0 0 16px rgba(243, 22, 103, 0.42);
+ }
+
+ 50% {
+ transform: scale(1.12);
+ box-shadow:
+ 0 0 0 8px rgba(243, 22, 103, 0.08),
+ 0 0 30px rgba(243, 22, 103, 0.68);
+ }
+}
+
.hero-star {
box-shadow: 0 0 8px rgba(255, 162, 31, 0.45);
clip-path: polygon(
@@ -55,10 +72,18 @@ body {
animation: hero-star-twinkle 4s ease-in-out infinite;
}
+.constellation-lead {
+ animation: constellation-lead-pulse 4.8s ease-in-out infinite;
+}
+
@media (prefers-reduced-motion: reduce) {
.hero-star {
animation: none;
}
+
+ .constellation-lead {
+ animation: none;
+ }
}
@media (max-width: 767px) {
diff --git a/app/hooks/useIsAndroid.ts b/app/hooks/useIsAndroid.ts
new file mode 100644
index 0000000..40b8be3
--- /dev/null
+++ b/app/hooks/useIsAndroid.ts
@@ -0,0 +1,20 @@
+"use client";
+
+import { useSyncExternalStore } from "react";
+
+function getSnapshot() {
+ return /android/i.test(navigator.userAgent);
+}
+
+function getServerSnapshot() {
+ return false;
+}
+
+// No-op subscribe: UA string never changes after mount.
+function subscribe(callback: () => void) {
+ return () => {};
+}
+
+export function useIsAndroid() {
+ return useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);
+}
diff --git a/package-lock.json b/package-lock.json
index 8f84a5d..4b315b3 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -69,7 +69,6 @@
"integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==",
"dev": true,
"license": "MIT",
- "peer": true,
"dependencies": {
"@babel/code-frame": "^7.29.0",
"@babel/generator": "^7.29.0",
@@ -1574,7 +1573,6 @@
"integrity": "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==",
"dev": true,
"license": "MIT",
- "peer": true,
"dependencies": {
"csstype": "^3.2.2"
}
@@ -1634,7 +1632,6 @@
"integrity": "sha512-rLoGZIf9afaRBYsPUMtvkDWykwXwUPL60HebR4JgTI8mxfFe2cQTu3AGitANp4b9B2QlVru6WzjgB2IzJKiCSA==",
"dev": true,
"license": "MIT",
- "peer": true,
"dependencies": {
"@typescript-eslint/scope-manager": "8.58.0",
"@typescript-eslint/types": "8.58.0",
@@ -2160,7 +2157,6 @@
"integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==",
"dev": true,
"license": "MIT",
- "peer": true,
"bin": {
"acorn": "bin/acorn"
},
@@ -2504,7 +2500,6 @@
}
],
"license": "MIT",
- "peer": true,
"dependencies": {
"baseline-browser-mapping": "^2.10.12",
"caniuse-lite": "^1.0.30001782",
@@ -3073,7 +3068,6 @@
"integrity": "sha512-XoMjdBOwe/esVgEvLmNsD3IRHkm7fbKIUGvrleloJXUZgDHig2IPWNniv+GwjyJXzuNqVjlr5+4yVUZjycJwfQ==",
"dev": true,
"license": "MIT",
- "peer": true,
"dependencies": {
"@eslint-community/eslint-utils": "^4.8.0",
"@eslint-community/regexpp": "^4.12.1",
@@ -3259,7 +3253,6 @@
"integrity": "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==",
"dev": true,
"license": "MIT",
- "peer": true,
"dependencies": {
"@rtsao/scc": "^1.1.0",
"array-includes": "^3.1.9",
@@ -3838,8 +3831,7 @@
"version": "3.14.2",
"resolved": "https://registry.npmjs.org/gsap/-/gsap-3.14.2.tgz",
"integrity": "sha512-P8/mMxVLU7o4+55+1TCnQrPmgjPKnwkzkXOK1asnR9Jg2lna4tEY5qBJjMmAaOBDDZWtlRjBXjLa0w53G/uBLA==",
- "license": "Standard 'no charge' license: https://gsap.com/standard-license.",
- "peer": true
+ "license": "Standard 'no charge' license: https://gsap.com/standard-license."
},
"node_modules/has-bigints": {
"version": "1.1.0",
@@ -5464,7 +5456,6 @@
"resolved": "https://registry.npmjs.org/react/-/react-19.2.4.tgz",
"integrity": "sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==",
"license": "MIT",
- "peer": true,
"engines": {
"node": ">=0.10.0"
}
@@ -5474,7 +5465,6 @@
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.4.tgz",
"integrity": "sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ==",
"license": "MIT",
- "peer": true,
"dependencies": {
"scheduler": "^0.27.0"
},
@@ -6163,7 +6153,6 @@
"integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==",
"dev": true,
"license": "MIT",
- "peer": true,
"engines": {
"node": ">=12"
},
@@ -6326,7 +6315,6 @@
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
"dev": true,
"license": "Apache-2.0",
- "peer": true,
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
@@ -6602,7 +6590,6 @@
"integrity": "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==",
"dev": true,
"license": "MIT",
- "peer": true,
"funding": {
"url": "https://github.com/sponsors/colinhacks"
}