diff --git a/.gitignore b/.gitignore index 5ef6a52..e5b2fc4 100644 --- a/.gitignore +++ b/.gitignore @@ -39,3 +39,16 @@ yarn-error.log* # typescript *.tsbuildinfo next-env.d.ts + +# local agent/editor state +/.augment/ +.opencode/ +.claude/settings.local.json +.superpowers/ + +# generated analysis artifacts +/graphify-out/ +*.graphify.json +*.analysis.json +*.tmp.json +*.debug.json diff --git a/.graphifyignore b/.graphifyignore new file mode 100644 index 0000000..d58fa88 --- /dev/null +++ b/.graphifyignore @@ -0,0 +1,21 @@ +node_modules/ +.next/ +dist/ +build/ +out/ +coverage/ +.git/ +.pnpm-store/ +.cache/ +*.log +.env +.env.* +__pycache__/ +*.pyc +.venv/ +venv/ +target/ +*.exe +*.dll +*.so +*.dylib diff --git a/README.md b/README.md index e215bc4..8ae8c4f 100644 --- a/README.md +++ b/README.md @@ -1,36 +1,88 @@ -This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app). +
-## Getting Started +# Evandro Dev — Portfolio -First, run the development server: +**A cinematic, interactive developer portfolio built with Next.js, React, TypeScript, and motion-first UI engineering.** -```bash -npm run dev -# or -yarn dev -# or -pnpm dev -# or -bun dev +![Next.js](https://img.shields.io/badge/Next.js-15-000?logo=nextdotjs) +![React](https://img.shields.io/badge/React-19-61DAFB?logo=react&logoColor=111) +![TypeScript](https://img.shields.io/badge/TypeScript-5-3178C6?logo=typescript&logoColor=fff) +![Biome](https://img.shields.io/badge/Code%20Style-Biome-60A5FA) +![Jest](https://img.shields.io/badge/Tests-Jest-C21325?logo=jest&logoColor=fff) + +
+ +## Overview + +This is the portfolio site for **Evandro Dev**. It presents professional work, case studies, technical positioning, and interactive visual storytelling through a high-polish Next.js interface. + +The project combines a modern App Router codebase with motion design, 3D/visual effects, and tested UI components. + +## Highlights + +| Area | Details | +| --- | --- | +| Framework | Next.js App Router with React and TypeScript. | +| Motion | GSAP, Framer Motion, Lenis, and custom animation components. | +| Visuals | Three.js / React Three Fiber / Drei for interactive and cinematic surfaces. | +| Styling | Tailwind CSS, utility composition, and reusable component primitives. | +| Quality | Jest, Testing Library, property-based tests, and Biome checks. | +| Content | Portfolio sections, case pages, company logos, hero composition, and supporting docs. | + +## Project structure + +```txt +app/ + page.tsx Main portfolio route + layout.tsx Root layout and metadata + components/ Portfolio sections and cinematic UI modules + cases/ Case-study routes/content + api/ Server/API routes +components/ Shared UI primitives +lib/ Utilities and shared logic +docs/ Supporting documentation +graphify-out/ Project analysis output +public/ Static assets ``` -Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. +## Getting started -You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file. +```bash +pnpm install +pnpm dev +``` -This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel. +The development server uses Turbopack through the project script. -## Learn More +## Scripts -To learn more about Next.js, take a look at the following resources: +| Command | Purpose | +| --- | --- | +| `pnpm dev` | Start the Next.js development server. | +| `pnpm build` | Build the production application. | +| `pnpm start` | Start the production server after building. | +| `pnpm lint` | Run Biome checks. | +| `pnpm format` | Format the codebase with Biome. | +| `pnpm test` | Run Jest tests. | +| `pnpm test:watch` | Run Jest in watch mode. | -- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. -- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. +## Engineering standards -You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome! +- Keep visual sections modular and testable. +- Respect reduced-motion and accessibility expectations for animated sections. +- Keep portfolio copy and case-study content easy to update without touching low-level animation code. +- Validate interactive components with tests when changing behavior, layout assumptions, or accessibility states. -## Deploy on Vercel +## Validation -The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js. +Before publishing changes: -Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details. +```bash +pnpm lint +pnpm test +pnpm build +``` + +## License + +Personal portfolio project unless a repository license says otherwise. diff --git a/app/components/About.tsx b/app/components/About.tsx index bf87ee6..3ac9328 100644 --- a/app/components/About.tsx +++ b/app/components/About.tsx @@ -1,7 +1,7 @@ -"use client"; +"use client"; import { motion, type Variants, useReducedMotion } from "framer-motion"; -import profilePhoto from "@/public/edev.jpeg"; +import profilePhoto from "@/public/edev.jpg"; import { Section } from "./Section"; import { DrawLine } from "./animations/DrawLine"; import { LazyRevealImage } from "./animations/LazyRevealImage"; @@ -19,6 +19,12 @@ const STORY_DATA = [ content: "Trabalho bem em contextos com legado, integracoes complexas e pouca margem para erro, priorizando estabilidade, leitura tecnica e evolucao sustentavel.", }, + { + label: "Production mindset", + title: "Do codigo ao sistema no ar", + content: + "Penso alem da feature: deploy, observabilidade, manutencao e riscos de operacao entram na decisao tecnica desde o primeiro corte.", + }, ]; const PROFILE_POINTS = [ @@ -91,7 +97,7 @@ export default function About() { } >
Technical profile
@@ -158,5 +164,3 @@ export default function About() { ); } - - diff --git a/app/components/Contact.tsx b/app/components/Contact.tsx index 2f80154..ac67d68 100644 --- a/app/components/Contact.tsx +++ b/app/components/Contact.tsx @@ -1,16 +1,22 @@ "use client"; import type React from "react"; -import { useState } from "react"; +import { useEffect, useState } from "react"; +import { buildWhatsAppUrl, getWhatsAppDisplayNumber } from "@/lib/whatsapp"; import { motion, type Variants, useReducedMotion } from "framer-motion"; import { Section } from "./Section"; import { DrawLine } from "./animations/DrawLine"; import MagneticButton from "./cinematic/MagneticButton"; -const WHATSAPP_NUMBER = "5547992742365"; export default function Contact() { const shouldReduceMotion = useReducedMotion(); + const [whatsAppDisplay, setWhatsAppDisplay] = useState("WhatsApp"); + + useEffect(() => { + setWhatsAppDisplay(getWhatsAppDisplayNumber()); + }, []); + const fieldClassName = "mt-sp-2 w-full border border-[rgba(255,255,255,.12)] bg-[rgba(255,255,255,.06)] px-sp-4 py-[14px] text-[13px] leading-6 text-white outline-none transition-[border-color,box-shadow] focus-visible:border-[var(--slash)] focus-visible:ring-2 focus-visible:ring-[rgba(204,255,0,0.34)] focus-visible:ring-offset-2 focus-visible:ring-offset-[var(--ink)]"; const [formData, setFormData] = useState({ @@ -25,8 +31,7 @@ export default function Contact() { const messageTemplate = `*NOVA SOLICITACAO DE PROJETO*\n\n*Origem:* Portfolio Profissional\n*Prioridade:* Alta\n\n---\n*CLIENTE:* ${formData.name}\n*EMAIL:* ${formData.email}\n*AREA:* ${formData.subject.toUpperCase()}\n\n*DESCRICAO:*\n"${formData.message}"\n---\n_Mensagem gerada via Evandro.dev | Garuva, SC_`; - const encodedMessage = encodeURIComponent(messageTemplate); - const whatsappUrl = `https://wa.me/${WHATSAPP_NUMBER}?text=${encodedMessage}`; + const whatsappUrl = buildWhatsAppUrl(messageTemplate); window.open(whatsappUrl, "_blank", "noopener,noreferrer"); }; @@ -93,7 +98,7 @@ export default function Contact() { projeto, performance ou construcao de produto com ownership real.

-
WhatsApp: +55 47 99274-2365
+
WhatsApp: {whatsAppDisplay}
E-mail: evandrofonsecajunior@gmail.com
Base: Garuva, Santa Catarina | atuacao remota
diff --git a/app/components/ContactForm.tsx b/app/components/ContactForm.tsx index 5027462..df44114 100644 --- a/app/components/ContactForm.tsx +++ b/app/components/ContactForm.tsx @@ -2,6 +2,7 @@ import { useState, ChangeEvent, FormEvent, useEffect } from "react"; import { usePortfolioData } from "@/lib/usePortfolioData"; +import { buildWhatsAppUrl } from "@/lib/whatsapp"; import { motion } from "motion/react"; interface ContactFormProps { @@ -173,15 +174,12 @@ ${formData.userName}`; const handleEmailFocus = () => setEmailFocused(true); const handlePhoneFocus = () => setPhoneFocused(true); - const generateWhatsAppUrl = () => { - const message = encodeURIComponent( + const generateWhatsAppUrl = () => + buildWhatsAppUrl( formType === "advanced" ? generateAdvancedMessage() : formData.customMessage || getGreetingMessageWithUserName(), ); - const phone = data.contact.whatsapp.replace(/\D/g, ""); // Remove caracteres não numéricos - return `https://wa.me/${phone}?text=${message}`; - }; const generateEmailUrl = () => { const subject = encodeURIComponent("Proposta de Projeto - Portfólio"); diff --git a/app/components/ContactInfo.tsx b/app/components/ContactInfo.tsx index 017fd6b..2f8913c 100644 --- a/app/components/ContactInfo.tsx +++ b/app/components/ContactInfo.tsx @@ -1,8 +1,10 @@ "use client"; +import { useEffect, useState } from "react"; import { motion } from "motion/react"; import { Mail, MessageCircle } from "lucide-react"; import { usePortfolioData } from "@/lib/usePortfolioData"; +import { getWhatsAppDisplayNumber } from "@/lib/whatsapp"; import MagneticButton from "./cinematic/MagneticButton"; interface ContactInfoProps { @@ -11,6 +13,11 @@ interface ContactInfoProps { export function ContactInfo({ onWhatsAppClick }: ContactInfoProps) { const { data } = usePortfolioData(); + const [whatsAppDisplay, setWhatsAppDisplay] = useState("WhatsApp"); + + useEffect(() => { + setWhatsAppDisplay(getWhatsAppDisplayNumber()); + }, []); if (!data) { return null; // Ou um componente de carregamento/erro @@ -58,7 +65,7 @@ export function ContactInfo({ onWhatsAppClick }: ContactInfoProps) {

WhatsApp

-

{data.contact.whatsapp}

+

{whatsAppDisplay}

diff --git a/app/components/Hero.reducedMotion.test.tsx b/app/components/Hero.reducedMotion.test.tsx index e4eeb2c..5d7e4cd 100644 --- a/app/components/Hero.reducedMotion.test.tsx +++ b/app/components/Hero.reducedMotion.test.tsx @@ -1,9 +1,15 @@ -import { render, screen } from "@testing-library/react"; +import { render, screen, waitFor } from "@testing-library/react"; import type { ReactNode } from "react"; import Hero from "./Hero"; -let mockReducedMotion = false; +jest.mock("next/dynamic", () => () => { + const DynamicComponent = ({ variant }: { variant?: string }) => ( +
+ ); + DynamicComponent.displayName = "MockHeroScene"; + return DynamicComponent; +}); jest.mock("framer-motion", () => { const React = require("react"); @@ -16,15 +22,15 @@ jest.mock("framer-motion", () => { children, initial, animate, - whileHover, - whileTap, + whileHover: _whileHover, + whileTap: _whileTap, + variants: _variants, + transition: _transition, ...props }: { children?: ReactNode; initial?: unknown; animate?: unknown; - whileHover?: unknown; - whileTap?: unknown; } & Record) => { const dataProps = { ...(initial !== undefined @@ -33,12 +39,6 @@ jest.mock("framer-motion", () => { ...(animate !== undefined ? { "data-animate": JSON.stringify(animate) } : {}), - ...(whileHover !== undefined - ? { "data-while-hover": JSON.stringify(whileHover) } - : {}), - ...(whileTap !== undefined - ? { "data-while-tap": JSON.stringify(whileTap) } - : {}), }; return React.createElement(tag, { ...props, ...dataProps }, children); @@ -53,49 +53,67 @@ jest.mock("framer-motion", () => { AnimatePresence: ({ children }: { children: React.ReactNode }) => children, motion, useInView: () => true, - useReducedMotion: () => mockReducedMotion, + useReducedMotion: () => true, }; }); +function mockMatchMedia(matches: boolean) { + Object.defineProperty(window, "matchMedia", { + writable: true, + value: jest.fn().mockImplementation((query) => ({ + matches, + media: query, + onchange: null, + addListener: jest.fn(), + removeListener: jest.fn(), + addEventListener: jest.fn(), + removeEventListener: jest.fn(), + dispatchEvent: jest.fn(), + })), + }); +} + describe("Hero reduced motion", () => { - afterEach(() => { - mockReducedMotion = false; + beforeEach(() => { + mockMatchMedia(true); + jest.spyOn(Math, "random").mockReturnValue(0); }); - it("removes hover/tap scale animations when reduced motion is enabled", () => { - mockReducedMotion = true; + afterEach(() => { + jest.restoreAllMocks(); + }); + it("keeps essential hero content visible with reduced motion enabled", async () => { + render(); + + await waitFor(() => + expect(screen.queryByTestId("hero-scene")).not.toBeInTheDocument(), + ); + + expect(screen.getByText(/Command center online/i)).toBeInTheDocument(); + expect( + screen.getByRole("heading", { + level: 1, + name: /coloque seu sistema no ar/i, + }), + ).toBeInTheDocument(); + expect( + screen.getByRole("button", { name: /botar meu sistema no ar/i }), + ).toBeInTheDocument(); + }); + + it("keeps motion blocks declarative instead of requiring 3D", async () => { const { container } = render(); - const primaryAction = screen.getByText(/Iniciar projeto/i).closest("a"); - const secondaryAction = screen.getByText(/Ver projetos/i).closest("a"); - - expect(primaryAction).not.toHaveAttribute("data-while-hover"); - expect(primaryAction).not.toHaveAttribute("data-while-tap"); - expect(secondaryAction).not.toHaveAttribute("data-while-hover"); - expect(secondaryAction).not.toHaveAttribute("data-while-tap"); + await waitFor(() => + expect(screen.queryByTestId("hero-scene")).not.toBeInTheDocument(), + ); const rootMotionBlock = container.querySelector( "div[data-initial][data-animate]", ); expect(rootMotionBlock).toBeInTheDocument(); - expect(rootMotionBlock?.getAttribute("data-initial")).toContain( - '"opacity":0', - ); - expect(rootMotionBlock?.getAttribute("data-animate")).toContain( - '"opacity":1', - ); - }); - - it("keeps essential hero content visible with reduced motion enabled", () => { - mockReducedMotion = true; - - render(); - - expect(screen.getByLabelText("evandro.dev.br")).toBeInTheDocument(); - expect( - screen.getByText(/Available for selected builds/i), - ).toBeInTheDocument(); - expect(screen.getByText(/Execution Brief/i)).toBeInTheDocument(); + expect(rootMotionBlock?.getAttribute("data-initial")).toContain("hidden"); + expect(rootMotionBlock?.getAttribute("data-animate")).toContain("visible"); }); }); diff --git a/app/components/Hero.test.tsx b/app/components/Hero.test.tsx index 7f36b0a..f8ac736 100644 --- a/app/components/Hero.test.tsx +++ b/app/components/Hero.test.tsx @@ -1,8 +1,16 @@ -import { render, screen } from "@testing-library/react"; +import { fireEvent, render, screen, waitFor } from "@testing-library/react"; import type { ReactNode } from "react"; import Hero from "./Hero"; +jest.mock("next/dynamic", () => () => { + const DynamicComponent = ({ variant }: { variant?: string }) => ( +
+ ); + DynamicComponent.displayName = "MockHeroScene"; + return DynamicComponent; +}); + jest.mock("framer-motion", () => { const React = require("react"); @@ -12,6 +20,12 @@ jest.mock("framer-motion", () => { get: (_, tag: string) => { const Component = ({ children, + initial: _initial, + animate: _animate, + whileHover: _whileHover, + whileTap: _whileTap, + variants: _variants, + transition: _transition, ...props }: { children?: ReactNode } & Record) => React.createElement(tag, props, children); @@ -29,34 +43,84 @@ jest.mock("framer-motion", () => { }; }); +function mockMatchMedia(matches = false) { + Object.defineProperty(window, "matchMedia", { + writable: true, + value: jest.fn().mockImplementation((query) => ({ + matches, + media: query, + onchange: null, + addListener: jest.fn(), + removeListener: jest.fn(), + addEventListener: jest.fn(), + removeEventListener: jest.fn(), + dispatchEvent: jest.fn(), + })), + }); +} + describe("Hero", () => { - it("renders the upgraded hero content and primary actions", () => { - render(); - - expect(screen.getByLabelText("evandro.dev.br")).toBeInTheDocument(); - expect( - screen.getByText( - /Engenharia de software para produtos conectados, interfaces de alta performance e sistemas criticos/i, - ), - ).toBeInTheDocument(); - - expect(screen.getByText(/Iniciar projeto/i)).toBeInTheDocument(); - expect(screen.getByText(/Ver projetos/i)).toBeInTheDocument(); - expect(screen.getByText(/Download CV/i)).toBeInTheDocument(); - expect(screen.getByText(/Execution Brief/i)).toBeInTheDocument(); + beforeEach(() => { + mockMatchMedia(false); + jest.spyOn(Math, "random").mockReturnValue(0); + jest.spyOn(window, "open").mockImplementation(() => null); }); - it("renders social links and proof content", () => { + afterEach(() => { + jest.restoreAllMocks(); + }); + + it("renders the command center hero and primary actions", async () => { + const { container } = render(); + + await waitFor(() => + expect(container.querySelector("[data-hero-variant]")).toHaveAttribute( + "data-hero-variant", + "command-center", + ), + ); + + expect( + screen.getByRole("heading", { + level: 1, + name: /coloque seu sistema no ar/i, + }), + ).toBeInTheDocument(); + expect( + screen.getByRole("button", { name: /botar meu sistema no ar/i }), + ).toBeInTheDocument(); + expect( + screen.getByRole("link", { name: /ver casos selecionados/i }), + ).toHaveAttribute("href", "/cases"); + expect(screen.getByText(/Download CV/i)).toBeInTheDocument(); + expect(screen.getByTestId("hero-scene")).toHaveAttribute( + "data-variant", + "command", + ); + }); + + it("keeps social links, proof content and WhatsApp CTA", async () => { render(); - expect(screen.getAllByLabelText("GitHub").length).toBeGreaterThan(0); - expect(screen.getAllByLabelText("LinkedIn").length).toBeGreaterThan(0); - expect(screen.getAllByLabelText("Email").length).toBeGreaterThan(0); + await waitFor(() => + expect(screen.getByText(/Command center online/i)).toBeInTheDocument(), + ); + expect(screen.getByLabelText("GitHub")).toBeInTheDocument(); + expect(screen.getByLabelText("LinkedIn")).toBeInTheDocument(); + expect(screen.getByLabelText("Email")).toBeInTheDocument(); expect(screen.getByText(/Garuva, SC \/ Brasil/i)).toBeInTheDocument(); - expect(screen.getByText(/Especialidade/i)).toBeInTheDocument(); - expect(screen.getByText(/Escopo/i)).toBeInTheDocument(); - expect(screen.getByText(/Formato/i)).toBeInTheDocument(); - expect(screen.getByText(/Scroll/i)).toBeInTheDocument(); + expect(screen.getByText(/Operacao/i)).toBeInTheDocument(); + expect(screen.getByText(/Clareza/i)).toBeInTheDocument(); + expect(screen.getAllByText(/Resultado/i).length).toBeGreaterThan(0); + + fireEvent.click( + screen.getByRole("button", { name: /botar meu sistema no ar/i }), + ); + expect(window.open).toHaveBeenCalledWith( + expect.stringContaining("https://wa.me/"), + "_blank", + "noopener,noreferrer", + ); }); }); diff --git a/app/components/Hero.tsx b/app/components/Hero.tsx index 04b2271..797e260 100644 --- a/app/components/Hero.tsx +++ b/app/components/Hero.tsx @@ -1,13 +1,42 @@ "use client"; +import { motion, type Variants } from "framer-motion"; import dynamic from "next/dynamic"; -import { motion, useReducedMotion, type Variants } from "framer-motion"; +import { useEffect, useState } from "react"; +import { buildWhatsAppUrl, getWhatsAppDisplayNumber } from "@/lib/whatsapp"; import MagneticButton from "./cinematic/MagneticButton"; const HeroScene = dynamic(() => import("./cinematic/HeroScene"), { ssr: false, }); +type HeroSceneVariant = + | "command" + | "launch" + | "rescue" + | "signal" + | "diagnostic" + | "portrait"; + +type HeroVariant = { + id: string; + weight: number; + scene: HeroSceneVariant; + layout: "default" | "visual-first" | "split" | "report" | "portrait"; + status: string; + chip: string; + eyebrow: string; + headline: string; + subtitle: string; + cta: string; + whatsappMessage: string; + sideLabel: string; + sideTitle: string; + sideText: string; + points: string[]; + proof: Array<{ label: string; value: string }>; +}; + const SOCIAL_LINKS = [ { href: "https://github.com/evandrodevbr", @@ -36,50 +65,402 @@ const SOCIAL_LINKS = [ }, ]; -const HERO_POINTS = [ - "Produtos conectados, integracoes complexas e operacoes criticas.", - "Arquitetura mais clara para evoluir sem aumentar o caos tecnico.", - "Execucao ponta a ponta, do discovery a entrega em producao.", +const HERO_VARIANTS: HeroVariant[] = [ + { + id: "command-center", + weight: 22, + scene: "command", + layout: "default", + status: "Command center online", + chip: "Sistemas em producao, sem improviso", + eyebrow: + "Evandro Fonseca Junior | Engenharia para produtos que precisam operar", + headline: "Coloque seu sistema no ar com engenharia que nao improvisa.", + subtitle: + "Se voce tem um projeto travado, um MVP para lancar ou um sistema critico precisando operar melhor, agende uma conversa tecnica e saia com o proximo passo claro para producao.", + cta: "Botar meu sistema no ar", + whatsappMessage: "Ola! Quero colocar meu sistema no ar. Podemos conversar?", + sideLabel: "Conversa tecnica pelo WhatsApp", + sideTitle: "Seu projeto precisa virar sistema em producao?", + sideText: + "Me conte objetivo, prazo e contexto. Eu respondo com uma conversa direta para destravar arquitetura, entrega e deploy.", + points: [ + "Conversa tecnica para tirar o projeto do papel sem improviso.", + "Arquitetura, codigo e deploy conectados ao resultado do negocio.", + "Plano claro para colocar o sistema no ar com seguranca.", + ], + proof: [ + { + label: "Operacao", + value: "Fluxo pensado para discovery, build, deploy e sustentacao.", + }, + { + label: "Clareza", + value: "Menos ruído entre produto, engenharia e decisao tecnica.", + }, + { + label: "Resultado", + value: "Base pronta para operar, evoluir e receber proximo ciclo.", + }, + ], + }, + { + id: "launch-sequence", + weight: 18, + scene: "launch", + layout: "visual-first", + status: "Launch sequence ready", + chip: "Discovery → build → deploy", + eyebrow: "MVP, produto e entrega em producao", + headline: + "Tire seu MVP do plano e coloque em producao com base tecnica real.", + subtitle: + "Transformo escopo solto em sequencia de lancamento: o que validar, o que construir primeiro e como publicar sem criar uma divida tecnica logo no inicio.", + cta: "Lancar meu MVP", + whatsappMessage: + "Ola! Quero lancar um MVP com uma base tecnica segura. Podemos conversar?", + sideLabel: "Sequencia de lancamento", + sideTitle: "Do primeiro corte ao deploy.", + sideText: + "A conversa define prioridade, risco e o menor caminho confiavel para colocar a primeira versao no ar.", + points: [ + "Recorte de MVP sem inflar escopo.", + "Entrega incremental com criterio de producao.", + "Deploy e continuidade pensados desde o inicio.", + ], + proof: [ + { label: "01", value: "Diagnostico do objetivo e gargalo principal." }, + { label: "02", value: "Plano de construcao com risco visivel." }, + { label: "03", value: "Publicacao e proximo ciclo sem perder contexto." }, + ], + }, + { + id: "rescue-split", + weight: 16, + scene: "rescue", + layout: "split", + status: "Rescue mode available", + chip: "Legado, instabilidade e projeto travado", + eyebrow: "Rescue tecnico para sistemas que precisam voltar a andar", + headline: + "Codigo confuso, entrega travada ou sistema instavel? Vamos organizar.", + subtitle: + "Entro no ponto onde o projeto parou: leio o fluxo real, separo sintoma de causa e transformo caos tecnico em plano executavel.", + cta: "Destravar meu projeto", + whatsappMessage: + "Ola! Tenho um projeto travado ou instavel e quero destravar. Podemos conversar?", + sideLabel: "Antes / depois tecnico", + sideTitle: "Do caos operacional para uma rota clara.", + sideText: + "O primeiro objetivo nao e reescrever tudo. E encontrar o menor corte seguro que devolve controle e previsibilidade.", + points: [ + "Leitura rapida de fluxo, risco e acoplamento.", + "Priorizacao do que destrava sem reescrever tudo.", + "Correcoes com criterio para nao trocar um bug por outro.", + ], + proof: [ + { + label: "Antes", + value: "Backlog confuso, bugs recorrentes e pouca previsibilidade.", + }, + { + label: "Intervencao", + value: "Mapa de risco, cortes pequenos e decisao tecnica clara.", + }, + { label: "Depois", value: "Entrega volta a andar com menos improviso." }, + ], + }, + { + id: "iot-signal-map", + weight: 15, + scene: "signal", + layout: "default", + status: "Signal map active", + chip: "Hardware + software + operacao", + eyebrow: "Produtos conectados, telemetria e integracoes criticas", + headline: + "Conecte dispositivo, backend e interface em uma operacao confiavel.", + subtitle: + "Atuo na fronteira que costuma quebrar primeiro: sinal fisico, API, painel, dados e rotina operacional funcionando como um unico produto.", + cta: "Conectar meu produto", + whatsappMessage: + "Ola! Quero conectar hardware, software e operacao em um produto confiavel. Podemos conversar?", + sideLabel: "Mapa de sinais", + sideTitle: "Dados, comandos e painel no mesmo fluxo.", + sideText: + "A conversa identifica fontes de dados, contratos, pontos de falha e o caminho para transformar integracao em produto.", + points: [ + "Contratos claros entre dispositivo, API e interface.", + "Telemetria e dashboards com contexto operacional.", + "Base pronta para automacao, IA ou analytics.", + ], + proof: [ + { + label: "Entrada", + value: "Sensores, dispositivos, eventos ou sistemas externos.", + }, + { + label: "Meio", + value: "APIs, WebSockets, filas, banco e observabilidade.", + }, + { + label: "Saida", + value: "Painel, operacao, alerta e decisao de produto.", + }, + ], + }, + { + id: "diagnostic-report", + weight: 15, + scene: "diagnostic", + layout: "report", + status: "Diagnostic report open", + chip: "Arquitetura antes de mais codigo", + eyebrow: "Diagnostico tecnico para decidir melhor", + headline: "Antes de escrever mais codigo, descubra o proximo passo certo.", + subtitle: + "Quando a base cresce sem clareza, mais features podem piorar o problema. Eu ajudo a mapear risco, arquitetura e prioridades antes da proxima decisao cara.", + cta: "Agendar diagnostico", + whatsappMessage: + "Ola! Quero um diagnostico tecnico para decidir o proximo passo do meu sistema. Podemos conversar?", + sideLabel: "Relatorio tecnico", + sideTitle: "Risco, arquitetura e rota de acao.", + sideText: + "Ideal para produto que precisa escalar, migrar, estabilizar ou parar de acumular retrabalho tecnico.", + points: [ + "Mapa claro dos gargalos e dependencias.", + "Trade-offs traduzidos para decisao de negocio.", + "Plano de transicao sem promessa magica.", + ], + proof: [ + { label: "Risco", value: "O que pode quebrar, atrasar ou custar caro." }, + { + label: "Arquitetura", + value: "Onde simplificar, separar ou estabilizar primeiro.", + }, + { label: "Acao", value: "Proximo corte de implementacao com criterio." }, + ], + }, + { + id: "cinematic-portrait", + weight: 14, + scene: "portrait", + layout: "portrait", + status: "Direct technical partner", + chip: "Conversa direta com quem implementa", + eyebrow: "Engenharia senior, ownership e execucao", + headline: "Um parceiro tecnico para transformar complexidade em entrega.", + subtitle: + "Voce nao precisa apenas de tela bonita ou codigo isolado. Precisa de alguem que entenda contexto, escolha o corte certo e acompanhe ate o sistema operar.", + cta: "Falar com Evandro", + whatsappMessage: + "Ola Evandro! Quero conversar sobre um projeto e entender como podemos colocar esse sistema no ar.", + sideLabel: "Founder-led engineering", + sideTitle: "Menos repasse, mais ownership.", + sideText: + "A conversa e comigo: contexto tecnico, prioridade, risco e caminho de entrega sem perder informacao entre camadas.", + points: [ + "Atendimento consultivo com leitura tecnica real.", + "Do produto ao backend sem perder contexto.", + "Execucao com criterio, nao so codigo por demanda.", + ], + proof: [ + { + label: "Perfil", + value: "Engenharia full-stack com base em sistemas e integracoes.", + }, + { + label: "Postura", + value: "Clareza, autonomia e decisao tecnica pragmatica.", + }, + { + label: "Acesso", + value: "WhatsApp direto para conversa tecnica inicial.", + }, + ], + }, ]; -const PROOF_ITEMS = [ - { - label: "Quem eu ajudo", - value: - "Empresas que dependem de integracoes, hardware ou fluxos criticos para operar com seguranca e previsibilidade.", - }, - { - label: "Como entrego", - value: - "Traduzo complexidade tecnica em backlog claro, arquitetura legivel e decisao mais rapida para o time.", - }, - { - label: "Por que conversar", - value: - "Voce ganha um parceiro tecnico capaz de conectar produto, backend, interface e operacao sem perder contexto.", - }, -]; +const LAYOUT_CLASSES: Record = { + default: + "grid items-start gap-sp-8 lg:grid-cols-[minmax(0,1.1fr)_minmax(340px,0.9fr)] lg:gap-sp-10", + "visual-first": + "grid items-start gap-sp-8 lg:grid-cols-[minmax(340px,0.9fr)_minmax(0,1.1fr)] lg:gap-sp-10", + split: "grid items-stretch gap-sp-8 lg:grid-cols-2 lg:gap-sp-10", + report: + "grid items-start gap-sp-8 lg:grid-cols-[minmax(360px,0.85fr)_minmax(0,1.15fr)] lg:gap-sp-10", + portrait: + "grid items-center gap-sp-8 lg:grid-cols-[minmax(0,1fr)_minmax(320px,0.72fr)] lg:gap-sp-10", +}; + +function pickWeightedHeroVariant() { + const total = HERO_VARIANTS.reduce((sum, variant) => sum + variant.weight, 0); + let ticket = Math.random() * total; + + for (const variant of HERO_VARIANTS) { + ticket -= variant.weight; + if (ticket <= 0) return variant; + } + + return HERO_VARIANTS[0]; +} + +function getGreeting(message: string) { + const hour = new Date().getHours(); + if (hour >= 8 && hour < 12) return `Bom dia! ${message}`; + if (hour >= 13 && hour < 18) return `Boa tarde! ${message}`; + return message; +} + +function SideVisual({ variant }: { variant: HeroVariant }) { + if (variant.scene === "launch") { + return ( +
+ {variant.proof.map((item, index) => ( +
+ + 0{index + 1} + +
+
+ {item.label} +
+

+ {item.value} +

+
+
+ ))} +
+ ); + } + + if (variant.scene === "rescue") { + return ( +
+
+
+ Antes +
+

+ Bugs recorrentes, escopo nebuloso e medo de mexer. +

+
+
+
+ Depois +
+

+ Rota clara, cortes menores e entrega voltando a andar. +

+
+
+ ); + } + + if (variant.scene === "signal") { + return ( +
+ + + {[28, 150, 284, 400].map((x, index) => ( + + ))} + +
+ ); + } + + if (variant.scene === "diagnostic") { + return ( +
+
+ Risk report + Actionable +
+
+ {variant.proof.map((item) => ( +
+
+ {item.label} + mapped +
+
+
+ ))} +
+
+ ); + } + + if (variant.scene === "portrait") { + return ( +
+
+ EFJ +
+
+ Engenharia com contato direto +
+
+ ); + } + + return ( +
+ {["deploy pipeline", "api health", "production ready"].map((item) => ( +
+ {item} + ok +
+ ))} +
+ ); +} export default function Hero() { - const shouldReduceMotion = useReducedMotion(); + const [variant, setVariant] = useState(HERO_VARIANTS[0]); + const [shouldReduceMotion, setShouldReduceMotion] = useState(false); + const [whatsAppDisplay, setWhatsAppDisplay] = useState("WhatsApp"); - const hour = new Date().getHours(); - const greeting = - hour >= 8 && hour < 12 - ? "Ola! Vi seu portfolio. Podemos conversar?" - : hour >= 13 && hour < 18 - ? "Boa tarde! Vi seu portfolio. Podemos conversar?" - : "Ola! Vi seu portfolio. Podemos conversar quando estiver disponivel?"; + useEffect(() => { + setVariant(pickWeightedHeroVariant()); + setShouldReduceMotion( + window.matchMedia("(prefers-reduced-motion: reduce)").matches, + ); + setWhatsAppDisplay(getWhatsAppDisplayNumber()); + }, []); + + const handleWhatsAppClick = () => { + window.open( + buildWhatsAppUrl(getGreeting(variant.whatsappMessage)), + "_blank", + "noopener,noreferrer", + ); + }; - // Cinematic stagger variants const heroContainer: Variants = { hidden: { opacity: 0 }, visible: { opacity: 1, - transition: { - staggerChildren: 0.1, - delayChildren: 0.3, - }, + transition: { staggerChildren: 0.1, delayChildren: 0.3 }, }, }; @@ -93,10 +474,7 @@ export default function Hero() { opacity: 1, y: 0, filter: "blur(0px)", - transition: { - duration: 1, - ease: easeOut, - }, + transition: { duration: 1, ease: easeOut }, }, }; @@ -108,26 +486,29 @@ export default function Hero() { opacity: 1, y: 0, filter: "blur(0px)", - transition: { - duration: 1.2, - ease: easeOut, - }, + transition: { duration: 1.2, ease: easeOut }, }, }; + const contentOrder = + variant.layout === "visual-first" || variant.layout === "report" + ? "lg:order-2" + : ""; + const panelOrder = + variant.layout === "visual-first" || variant.layout === "report" + ? "lg:order-1" + : ""; + return (
- {/* 3D Scene Background */} - {!shouldReduceMotion && } + {!shouldReduceMotion && } - {/* Cinematic overlays */}
- - {/* Scanlines effect */}
- - {/* Vignette */}
@@ -144,10 +523,9 @@ export default function Hero() { initial="hidden" animate="visible" variants={heroContainer} - className="grid items-start gap-sp-8 lg:grid-cols-[minmax(0,1.1fr)_minmax(340px,0.9fr)] lg:gap-sp-10" + className={LAYOUT_CLASSES[variant.layout]} > -
- {/* Status badge with glow */} +
- Available for selected builds + {variant.status}
- Produtos conectados, integracoes criticas e entrega ponta a ponta + {variant.chip}
- {/* Eyebrow */} - Evandro Fonseca Junior | Engenheiro de software full-stack + {variant.eyebrow} - {/* Main heading with cinematic reveal */} - Engenharia de software para sistemas que nao podem falhar. + {variant.headline} - {/* Subtitle */} - Ajudo empresas com produtos conectados, integracoes complexas e - operacoes criticas a transformar arquitetura confusa em software - estavel, mais rapido de evoluir e muito mais facil de operar. + {variant.subtitle} - {/* Key points with staggered reveal */} - {HERO_POINTS.map((point, i) => ( + {variant.points.map((point, i) => ( - {/* CTAs with magnetic effect */} - Agendar conversa tecnica + {variant.cta} - {/* Footer info with reveal */}
- {/* Aside card with cinematic entrance */} - {/* Top glow line */} + +
- Why this positioning works + {variant.sideLabel}

- Menos atrito entre produto, engenharia e operacao. + {variant.sideTitle}

- Entro onde a complexidade cresce e ajudo a devolver clareza, - ritmo de entrega e confiabilidade para o sistema e para o time. + {variant.sideText}

+
- {PROOF_ITEMS.map((item, index) => ( + {variant.proof.map((item, index) => (
- {/* Scroll indicator */} (0); @@ -29,7 +22,7 @@ export default function CustomCursor() { if (!isVisible) setIsVisible(true); }); }, - [cursorX, cursorY, isVisible] + [cursorX, cursorY, isVisible], ); useEffect(() => { @@ -57,7 +50,7 @@ export default function CustomCursor() { function attachHoverListeners() { const hoverables = document.querySelectorAll( - 'a, button, [role="button"], input, textarea, select, [data-cursor]' + 'a, button, [role="button"], input, textarea, select, [data-cursor]', ); for (const el of hoverables) { el.addEventListener("mouseenter", handleElementEnter as EventListener); @@ -89,7 +82,10 @@ export default function CustomCursor() { }, [handleMouseMove]); // Don't render on touch devices - if (typeof window !== "undefined" && !window.matchMedia("(pointer: fine)").matches) { + if ( + typeof window !== "undefined" && + !window.matchMedia("(pointer: fine)").matches + ) { return null; } @@ -105,8 +101,8 @@ export default function CustomCursor() {
{hoverText && ( (null); const mouseRef = useRef({ x: 0, y: 0 }); @@ -32,10 +40,13 @@ function ParticleField({ count = 2000 }: { count?: number }) { return s; }, [count]); - const handlePointerMove = useCallback((e: { clientX: number; clientY: number }) => { - mouseRef.current.x = (e.clientX / window.innerWidth) * 2 - 1; - mouseRef.current.y = -(e.clientY / window.innerHeight) * 2 + 1; - }, []); + const handlePointerMove = useCallback( + (e: { clientX: number; clientY: number }) => { + mouseRef.current.x = (e.clientX / window.innerWidth) * 2 - 1; + mouseRef.current.y = -(e.clientY / window.innerHeight) * 2 + 1; + }, + [], + ); useFrame((state) => { if (!mesh.current) return; @@ -107,22 +118,61 @@ function ParticleField({ count = 2000 }: { count?: number }) { ); } -function FloatingGeometry() { +function FloatingGeometry({ variant }: { variant: HeroSceneVariant }) { const meshRef = useRef(null); const mesh2Ref = useRef(null); const mesh3Ref = useRef(null); + const layout = { + command: { + a: [4, 1.5, -3] as [number, number, number], + b: [-5, -2, -4] as [number, number, number], + c: [2, -3, -5] as [number, number, number], + scale: [1.2, 0.8, 1.5] as [number, number, number], + }, + launch: { + a: [3.8, 2.1, -3] as [number, number, number], + b: [0, -1.4, -4] as [number, number, number], + c: [-3.8, -2.6, -5] as [number, number, number], + scale: [0.9, 0.9, 1.8] as [number, number, number], + }, + rescue: { + a: [-3.6, 1.2, -3] as [number, number, number], + b: [3.6, 1.2, -4] as [number, number, number], + c: [0, -2.8, -5] as [number, number, number], + scale: [1, 1, 1.4] as [number, number, number], + }, + signal: { + a: [-4.2, -1, -3] as [number, number, number], + b: [0.2, 1.6, -4] as [number, number, number], + c: [4.2, -0.6, -5] as [number, number, number], + scale: [0.7, 0.7, 1.2] as [number, number, number], + }, + diagnostic: { + a: [4.6, 0.6, -3] as [number, number, number], + b: [-3.5, 1.8, -4] as [number, number, number], + c: [-1.2, -2.8, -5] as [number, number, number], + scale: [1.4, 0.7, 1.1] as [number, number, number], + }, + portrait: { + a: [3.2, 0.4, -3] as [number, number, number], + b: [4.8, 2.1, -4] as [number, number, number], + c: [-3.6, -2.4, -5] as [number, number, number], + scale: [1.8, 0.5, 1.1] as [number, number, number], + }, + }[variant]; + useFrame((state) => { const t = state.clock.elapsedTime; if (meshRef.current) { meshRef.current.rotation.x = t * 0.15; meshRef.current.rotation.y = t * 0.1; - meshRef.current.position.y = Math.sin(t * 0.4) * 0.3; + meshRef.current.position.y = layout.a[1] + Math.sin(t * 0.4) * 0.3; } if (mesh2Ref.current) { mesh2Ref.current.rotation.x = t * 0.1; mesh2Ref.current.rotation.z = t * 0.12; - mesh2Ref.current.position.y = Math.cos(t * 0.3) * 0.5; + mesh2Ref.current.position.y = layout.b[1] + Math.cos(t * 0.3) * 0.5; } if (mesh3Ref.current) { mesh3Ref.current.rotation.y = t * 0.08; @@ -133,13 +183,19 @@ function FloatingGeometry() { return ( <> - - + + {variant === "diagnostic" ? ( + + ) : variant === "portrait" ? ( + + ) : ( + + )} @@ -147,8 +203,14 @@ function FloatingGeometry() { - - + + {variant === "launch" ? ( + + ) : variant === "signal" ? ( + + ) : ( + + )} - - + + {variant === "rescue" ? ( + + ) : variant === "signal" ? ( + + ) : ( + + )} - + - +
diff --git a/app/globals.css b/app/globals.css index cfd122e..f3c01c6 100644 --- a/app/globals.css +++ b/app/globals.css @@ -62,17 +62,39 @@ padding: 0; } +html, +body { + width: 100%; + max-width: 100%; + overflow-x: hidden; + overscroll-behavior-x: none; +} + html { scroll-behavior: smooth; } body { + position: relative; background: var(--paper); color: var(--ink); font-family: var(--font-mono); font-size: 14px; line-height: 1.68; - overflow-x: hidden; +} + +img, +video, +canvas, +svg { + max-width: 100%; +} + +@supports (overflow: clip) { + html, + body { + overflow-x: clip; + } } a { diff --git a/app/opengraph-image.tsx b/app/opengraph-image.tsx index c53d052..5bf4c30 100644 --- a/app/opengraph-image.tsx +++ b/app/opengraph-image.tsx @@ -1,6 +1,5 @@ import { ImageResponse } from "next/og"; -export const runtime = "edge"; export const alt = "Evandro Fonseca Junior - Engenheiro de Software"; export const size = { width: 1200, diff --git a/docs/superpowers/plans/2026-07-08-random-hero-variations.md b/docs/superpowers/plans/2026-07-08-random-hero-variations.md new file mode 100644 index 0000000..74ab219 --- /dev/null +++ b/docs/superpowers/plans/2026-07-08-random-hero-variations.md @@ -0,0 +1,61 @@ +# Random Hero Variations Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** Build six weighted, randomly selected Hero Section layouts with matching 3D scene modes. + +**Architecture:** Add data-driven hero variant configuration in `Hero.tsx`, choose a variant once on client mount, and render layout-specific panel/accent components from that config. Update `HeroScene.tsx` to accept a scene variant prop and switch geometry composition without adding dependencies. + +**Tech Stack:** Next.js, React 19, TypeScript, Framer Motion, @react-three/fiber, @react-three/drei, Three.js. + +--- + +### Task 1: Add variant data and weighted picker + +**Files:** +- Modify: `app/components/Hero.tsx` + +- [ ] Define `HeroVariantId`, `HeroSceneVariant`, and `HeroVariant` types. +- [ ] Add six approved variant objects with copy, points, proof items, side panel mode, layout mode, CTA text, and weights. +- [ ] Add `pickWeightedHeroVariant()` using `Math.random()` and relative weights. +- [ ] Keep `HERO_VARIANTS[0]` as deterministic SSR/default fallback. + +### Task 2: Render six hero layouts from shared primitives + +**Files:** +- Modify: `app/components/Hero.tsx` + +- [ ] Replace fixed hero text with selected variant data. +- [ ] Add layout-specific class maps for Command Center, Launch Sequence, Rescue Split, IoT Signal Map, Diagnostic Report, and Cinematic Portrait. +- [ ] Add side panel renderer that draws distinct visual panels for each mode using existing CSS/Tailwind utilities. +- [ ] Keep primary WhatsApp CTA and secondary cases/CV actions. + +### Task 3: Parameterize the 3D scene + +**Files:** +- Modify: `app/components/cinematic/HeroScene.tsx` + +- [ ] Export/accept a `HeroSceneVariant`-compatible prop locally as string union. +- [ ] Pass the selected scene mode from `Hero.tsx` into `HeroScene`. +- [ ] Change floating geometry positions/geometries per scene mode. +- [ ] Keep reduced motion behavior unchanged: no `HeroScene` render when reduced motion is enabled. + +### Task 4: Update tests + +**Files:** +- Modify: `app/components/Hero.test.tsx` +- Modify: `app/components/Hero.reducedMotion.test.tsx` + +- [ ] Update assertions to current commercial CTA/copy. +- [ ] Assert at least one approved hero layout renders. +- [ ] Assert CTA remains a button and cases/CV remain available. +- [ ] Assert reduced-motion content still renders without animation-specific failures. + +### Task 5: Verify + +**Commands:** +- `pnpm exec tsc --noEmit --pretty false` +- `pnpm test -- Hero.test.tsx Hero.reducedMotion.test.tsx --runInBand` +- `pnpm build` + +**Expected:** all commands exit 0. diff --git a/docs/superpowers/specs/2026-07-08-random-hero-variations-design.md b/docs/superpowers/specs/2026-07-08-random-hero-variations-design.md new file mode 100644 index 0000000..ef85343 --- /dev/null +++ b/docs/superpowers/specs/2026-07-08-random-hero-variations-design.md @@ -0,0 +1,37 @@ +# Random Hero Variations Design + +## Goal + +Create six structurally different Hero Section variations for the portfolio home page. Each visit should pick one variation randomly using weighted probability, while preserving the existing visual identity: dark cinematic background, slash green accent, glass panels, mono labels, strong display typography, technical lines, and WhatsApp conversion CTA. + +## Approved hero variants + +1. **Command Center** — Main/default hero. Two-column mission-control layout with production status, deploy/operation language, and strong CTA. +2. **Launch Sequence** — Timeline/checklist layout showing discovery → build → deploy, focused on launching MVPs and putting systems in production. +3. **Before / After Rescue** — Split layout comparing technical chaos vs organized system, focused on legacy, instability, and rescue projects. +4. **IoT Signal Map** — Signal/network layout with nodes and telemetry language, focused on hardware + software, APIs, dashboards, and connected products. +5. **Diagnostic Report** — Report/audit layout with risk, architecture, and next-step panels, focused on technical diagnosis before more code. +6. **Cinematic Portrait** — More personal consultant-led layout with trust, direct conversation, and premium technical partner positioning. + +## Weighted distribution + +- Command Center: 22 +- Launch Sequence: 18 +- Before / After Rescue: 16 +- IoT Signal Map: 15 +- Diagnostic Report: 15 +- Cinematic Portrait: 14 + +Weights are relative. Command Center appears most often because it is the clearest commercial positioning. + +## Implementation approach + +Keep the implementation lazy and maintainable: one data array defines copy, layout, side panel, points, CTA, and scene mode. `Hero.tsx` renders the same primitives with layout-specific branches instead of six separate full components. `HeroScene.tsx` accepts a `variant` prop and changes the 3D object composition per selected hero. + +## Constraints + +- Do not expose the WhatsApp number as a complete literal. +- Do not add dependencies. +- Keep accessibility basics: real buttons/links, visible text, keyboard focus. +- Avoid horizontal scroll. +- Reduced motion must continue to hide the 3D scene. diff --git a/lib/site-content.ts b/lib/site-content.ts index fbf90fa..043d8b4 100644 --- a/lib/site-content.ts +++ b/lib/site-content.ts @@ -61,8 +61,7 @@ export const servicePages: ServicePageContent[] = [ ], stack: ["Next.js", "TypeScript", "Node.js", "PostgreSQL", "APIs REST"], ctaLabel: "Agendar conversa sobre engenharia", - ctaHref: - "https://wa.me/5547992742365?text=Quero%20conversar%20sobre%20engenharia%20de%20software.", + ctaHref: "/#contact", relatedCaseIds: ["nutriscan", "reddit-explorer", "sc-plus"], }, { @@ -96,7 +95,7 @@ export const servicePages: ServicePageContent[] = [ ], stack: ["Next.js", "React", "TypeScript", "Tailwind CSS", "SEO tecnico"], ctaLabel: "Solicitar consultoria Next.js", - ctaHref: "https://wa.me/5547992742365?text=Quero%20consultoria%20em%20Next.js.", + ctaHref: "/#contact", relatedCaseIds: ["nutriscan", "twins-lab", "sc-plus"], }, { @@ -136,8 +135,7 @@ export const servicePages: ServicePageContent[] = [ "System integration", ], ctaLabel: "Conversar sobre Tauri e Rust", - ctaHref: - "https://wa.me/5547992742365?text=Quero%20conversar%20sobre%20Tauri%20e%20Rust.", + ctaHref: "/#contact", relatedCaseIds: ["gcc-installer"], }, { @@ -170,8 +168,7 @@ export const servicePages: ServicePageContent[] = [ ], stack: ["IoT", "WebSockets", "APIs", "Dashboards", "Integracao de sistemas"], ctaLabel: "Falar sobre integracao e IoT", - ctaHref: - "https://wa.me/5547992742365?text=Quero%20falar%20sobre%20integracao%20hardware%20%2B%20software%20e%20IoT.", + ctaHref: "/#contact", relatedCaseIds: ["twins-lab"], }, { @@ -211,8 +208,7 @@ export const servicePages: ServicePageContent[] = [ "Sistemas criticos", ], ctaLabel: "Solicitar diagnostico arquitetural", - ctaHref: - "https://wa.me/5547992742365?text=Quero%20um%20diagnostico%20de%20arquitetura%20de%20software.", + ctaHref: "/#contact", relatedCaseIds: ["nutriscan", "twins-lab", "reddit-explorer"], }, ]; diff --git a/lib/whatsapp.ts b/lib/whatsapp.ts new file mode 100644 index 0000000..7dc3ce6 --- /dev/null +++ b/lib/whatsapp.ts @@ -0,0 +1,17 @@ +const COUNTRY = ["5", "5"]; +const AREA = ["4", "7"]; +const NINE = ["9"]; +const FIRST = ["9", "2", "7", "4"]; +const LAST = ["2", "3", "6", "5"]; + +export function getWhatsAppNumber() { + return [...COUNTRY, ...AREA, ...NINE, ...FIRST, ...LAST].join(""); +} + +export function getWhatsAppDisplayNumber() { + return `+${COUNTRY.join("")} ${AREA.join("")} ${NINE.join("")}${FIRST.join("")}-${LAST.join("")}`; +} + +export function buildWhatsAppUrl(message: string) { + return `https://wa.me/${getWhatsAppNumber()}?text=${encodeURIComponent(message)}`; +} diff --git a/next.config.ts b/next.config.ts index be63155..e54bcad 100644 --- a/next.config.ts +++ b/next.config.ts @@ -9,11 +9,11 @@ const nextConfig: NextConfig = { minimumCacheTTL: 60, }, - // Otimizações de performance - experimental: { - scrollRestoration: true, + turbopack: { + root: process.cwd(), }, + // Headers de cache e segurança async headers(): Promise< Array<{ source: string; headers: Array<{ key: string; value: string }> }> @@ -59,15 +59,6 @@ const nextConfig: NextConfig = { }, ], }, - { - source: "/_next/image(.*)", - headers: [ - { - key: "Cache-Control", - value: "public, max-age=31536000, immutable", - }, - ], - }, { source: "/fonts/(.*)", headers: [ diff --git a/package.json b/package.json index 5cb4a75..e6a0666 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,7 @@ "@gsap/react": "^2.1.2", "@react-three/drei": "^10.7.7", "@react-three/fiber": "^9.5.0", - "@types/three": "^0.183.1", + "@types/three": "0.182.0", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "cobe": "^0.6.4", @@ -30,7 +30,7 @@ "react-dom": "19.1.0", "tailwind-merge": "^3.3.1", "tailwindcss-animate": "^1.0.7", - "three": "^0.183.2" + "three": "0.182.0" }, "devDependencies": { "@biomejs/biome": "2.2.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1d2ed1c..2d449ef 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -5,13 +5,32 @@ settings: excludeLinksFromLockfile: false overrides: + '@babel/core@<=7.29.0': '>=7.29.6' + brace-expansion@<1.1.13: '>=1.1.13' + brace-expansion@>=2.0.0 <2.0.3: '>=2.0.3' + handlebars@>=4.0.0 <4.7.9: '>=4.7.9' + handlebars@>=4.0.0 <=4.7.8: '>=4.7.9' + handlebars@>=4.6.0 <=4.7.8: '>=4.7.9' + js-yaml@<=4.1.1: '>=4.2.0' next@>=10.0.0 <15.5.10: '>=15.5.10' next@>=15.5.1-canary.0 <15.5.10: '>=15.5.10' + next@>=16.0.0 <16.2.5: '>=16.2.5' + next@>=16.0.0 <16.2.6: '>=16.2.6' + next@>=16.0.0-beta.0 <16.1.7: '>=16.1.7' + next@>=16.0.0-beta.0 <16.2.3: '>=16.2.3' + next@>=16.0.1 <16.1.7: '>=16.1.7' + picomatch@<2.3.2: '>=2.3.2' + picomatch@>=4.0.0 <4.0.4: '>=4.0.4' + postcss@<8.5.10: '>=8.5.10' tar@<7.5.7: '>=7.5.7' tar@<7.5.8: '>=7.5.8' + tar@<=7.5.10: '>=7.5.11' + tar@<=7.5.15: '>=7.5.16' tar@<=7.5.2: '>=7.5.3' tar@<=7.5.3: '>=7.5.4' tar@<=7.5.9: '>=7.5.10' + ws@>=8.0.0 <8.20.1: '>=8.20.1' + ws@>=8.0.0 <8.21.0: '>=8.21.0' importers: @@ -22,13 +41,13 @@ importers: version: 2.1.2(gsap@3.13.0)(react@19.1.0) '@react-three/drei': specifier: ^10.7.7 - version: 10.7.7(@react-three/fiber@9.5.0(@types/react@19.1.12)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(three@0.183.2))(@types/react@19.1.12)(@types/three@0.183.1)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(three@0.183.2) + version: 10.7.7(@react-three/fiber@9.5.0(@types/react@19.1.12)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(three@0.182.0))(@types/react@19.1.12)(@types/three@0.182.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(three@0.182.0) '@react-three/fiber': specifier: ^9.5.0 - version: 9.5.0(@types/react@19.1.12)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(three@0.183.2) + version: 9.5.0(@types/react@19.1.12)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(three@0.182.0) '@types/three': - specifier: ^0.183.1 - version: 0.183.1 + specifier: 0.182.0 + version: 0.182.0 class-variance-authority: specifier: ^0.7.1 version: 0.7.1 @@ -54,8 +73,8 @@ importers: specifier: ^12.23.12 version: 12.23.12(react-dom@19.1.0(react@19.1.0))(react@19.1.0) next: - specifier: '>=15.5.10' - version: 16.1.6(@babel/core@7.29.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + specifier: '>=16.1.7' + version: 16.2.9(@babel/core@8.0.1)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) ogl: specifier: ^1.0.11 version: 1.0.11 @@ -72,8 +91,8 @@ importers: specifier: ^1.0.7 version: 1.0.7(tailwindcss@4.1.12) three: - specifier: ^0.183.2 - version: 0.183.2 + specifier: 0.182.0 + version: 0.182.0 devDependencies: '@biomejs/biome': specifier: 2.2.0 @@ -125,7 +144,7 @@ importers: version: 4.1.12 ts-jest: specifier: ^29.4.6 - version: 29.4.6(@babel/core@7.29.0)(@jest/transform@30.3.0)(@jest/types@30.3.0)(babel-jest@30.3.0(@babel/core@7.29.0))(jest-util@30.3.0)(jest@30.3.0(@types/node@20.19.11)(ts-node@10.9.2(@types/node@20.19.11)(typescript@5.9.2)))(typescript@5.9.2) + version: 29.4.6(@babel/core@8.0.1)(@jest/transform@30.3.0)(@jest/types@30.3.0)(babel-jest@30.3.0(@babel/core@8.0.1))(jest-util@30.3.0)(jest@30.3.0(@types/node@20.19.11)(ts-node@10.9.2(@types/node@20.19.11)(typescript@5.9.2)))(typescript@5.9.2) ts-node: specifier: ^10.9.2 version: 10.9.2(@types/node@20.19.11)(typescript@5.9.2) @@ -152,35 +171,33 @@ packages: resolution: {integrity: sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==} engines: {node: '>=6.9.0'} - '@babel/compat-data@7.29.0': - resolution: {integrity: sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg==} - engines: {node: '>=6.9.0'} + '@babel/code-frame@8.0.0': + resolution: {integrity: sha512-dYYg153EyN2Ekbqw2zAsbd6/JR+9N2SEoC7YV2GyyqMM7x9bLDTjBD6XBhSMLH0wtIVyJj03jWNriQhaN+eoCw==} + engines: {node: ^22.18.0 || >=24.11.0} - '@babel/core@7.29.0': - resolution: {integrity: sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==} - engines: {node: '>=6.9.0'} + '@babel/compat-data@8.0.0': + resolution: {integrity: sha512-DOjnob/cXOUgDOozCDeq/aK2p5y8dUIVdf6tNhEV1HQRd6I8aQ4f4fbtHRVEvb6lP3BGomrKHiS8ICAASSVQSw==} + engines: {node: ^22.18.0 || >=24.11.0} + + '@babel/core@8.0.1': + resolution: {integrity: sha512-5FgxM4dLQpMJHSiVATk8foW263dVHQHBVpXYiimNECVWG01f4nFyEbQixeT6Mwvg7TayREJ2gpKl3o2RoMdnqw==} + engines: {node: ^22.18.0 || >=24.11.0} '@babel/generator@7.29.1': resolution: {integrity: sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==} engines: {node: '>=6.9.0'} - '@babel/helper-compilation-targets@7.28.6': - resolution: {integrity: sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==} - engines: {node: '>=6.9.0'} + '@babel/generator@8.0.0': + resolution: {integrity: sha512-NT9NrVwJsbSV6Y2FSstWa71EETOnzrjkL5/wX3D2mYHtKM+qvqB1DvR4D0Setb/gDBsHzRICifwEWMO8CnTF6g==} + engines: {node: ^22.18.0 || >=24.11.0} - '@babel/helper-globals@7.28.0': - resolution: {integrity: sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==} - engines: {node: '>=6.9.0'} + '@babel/helper-compilation-targets@8.0.0': + resolution: {integrity: sha512-JwculLABZvyPvyLBpwU/E/IbH2uM3mnxNtIJpxnIfb24y1PrdVxK5Dqjle4DpgqpGRnwgC7G8IkzPdSXZrO1Ew==} + engines: {node: ^22.18.0 || >=24.11.0} - '@babel/helper-module-imports@7.28.6': - resolution: {integrity: sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==} - engines: {node: '>=6.9.0'} - - '@babel/helper-module-transforms@7.28.6': - resolution: {integrity: sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0 + '@babel/helper-globals@8.0.0': + resolution: {integrity: sha512-lLozHOM6sWWlxNo8CYqHy4MBZeTvHXNgVPBfPOGsjPKUzHC2Az9QwB6gxdQmpwHl6GlQtbGgS+lj5887guDiLw==} + engines: {node: ^22.18.0 || >=24.11.0} '@babel/helper-plugin-utils@7.28.6': resolution: {integrity: sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==} @@ -190,130 +207,147 @@ packages: resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==} engines: {node: '>=6.9.0'} + '@babel/helper-string-parser@8.0.0': + resolution: {integrity: sha512-6mJgmFFFIIO82vvoLt9XtRC7/TkzXfts1t/SpRX4IHSzMgqoPYCWesVu1udUPUWioAE/2fcG6WuI8zrkE1gwrg==} + engines: {node: ^22.18.0 || >=24.11.0} + '@babel/helper-validator-identifier@7.28.5': resolution: {integrity: sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==} engines: {node: '>=6.9.0'} - '@babel/helper-validator-option@7.27.1': - resolution: {integrity: sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==} - engines: {node: '>=6.9.0'} + '@babel/helper-validator-identifier@8.0.2': + resolution: {integrity: sha512-9Fr9QeyCAyi1BR1jKZ6uYQ24EIhQUx5ReHfQU7drOE+TPOb+w11/dsqLkMOT2U29OdCT71XajrOT8xDc1C7orA==} + engines: {node: ^22.18.0 || >=24.11.0} - '@babel/helpers@7.28.6': - resolution: {integrity: sha512-xOBvwq86HHdB7WUDTfKfT/Vuxh7gElQ+Sfti2Cy6yIWNW05P8iUslOVcZ4/sKbE+/jQaukQAdz/gf3724kYdqw==} - engines: {node: '>=6.9.0'} + '@babel/helper-validator-option@8.0.0': + resolution: {integrity: sha512-U4Dybxh4WESWHt5XhBeExi4DrY0/DNK1aHpQbsrQXCUbFHuMweT0TpLEWKvaraV2Y6fS+ZXunsZ8zIuZIgvF2Q==} + engines: {node: ^22.18.0 || >=24.11.0} + + '@babel/helpers@8.0.0': + resolution: {integrity: sha512-wfbi91pM3py96oIiJEz7qIpyXDytgr9zQC1HEWwlGNVRAEmItuU/0a41ZUKu1sJGyhhOIpc4t5vk4PYzt8wpsg==} + engines: {node: ^22.18.0 || >=24.11.0} '@babel/parser@7.29.0': resolution: {integrity: sha512-IyDgFV5GeDUVX4YdF/3CPULtVGSXXMLh1xVIgdCgxApktqnQV0r7/8Nqthg+8YLGaAtdyIlo2qIdZrbCv4+7ww==} engines: {node: '>=6.0.0'} hasBin: true + '@babel/parser@8.0.0': + resolution: {integrity: sha512-aLxAE+imI9bCcyaPrUDjBv3uSkWieifjLe0kuFOZF0zli0L6GCsTmsePnTr55adbIAgYz2zhN1vnFimCBUYcRQ==} + engines: {node: ^22.18.0 || >=24.11.0} + hasBin: true + '@babel/plugin-syntax-async-generators@7.8.4': resolution: {integrity: sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==} peerDependencies: - '@babel/core': ^7.0.0-0 + '@babel/core': '>=7.29.6' '@babel/plugin-syntax-bigint@7.8.3': resolution: {integrity: sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==} peerDependencies: - '@babel/core': ^7.0.0-0 + '@babel/core': '>=7.29.6' '@babel/plugin-syntax-class-properties@7.12.13': resolution: {integrity: sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==} peerDependencies: - '@babel/core': ^7.0.0-0 + '@babel/core': '>=7.29.6' '@babel/plugin-syntax-class-static-block@7.14.5': resolution: {integrity: sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==} engines: {node: '>=6.9.0'} peerDependencies: - '@babel/core': ^7.0.0-0 + '@babel/core': '>=7.29.6' '@babel/plugin-syntax-import-attributes@7.28.6': resolution: {integrity: sha512-jiLC0ma9XkQT3TKJ9uYvlakm66Pamywo+qwL+oL8HJOvc6TWdZXVfhqJr8CCzbSGUAbDOzlGHJC1U+vRfLQDvw==} engines: {node: '>=6.9.0'} peerDependencies: - '@babel/core': ^7.0.0-0 + '@babel/core': '>=7.29.6' '@babel/plugin-syntax-import-meta@7.10.4': resolution: {integrity: sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==} peerDependencies: - '@babel/core': ^7.0.0-0 + '@babel/core': '>=7.29.6' '@babel/plugin-syntax-json-strings@7.8.3': resolution: {integrity: sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==} peerDependencies: - '@babel/core': ^7.0.0-0 + '@babel/core': '>=7.29.6' '@babel/plugin-syntax-jsx@7.28.6': resolution: {integrity: sha512-wgEmr06G6sIpqr8YDwA2dSRTE3bJ+V0IfpzfSY3Lfgd7YWOaAdlykvJi13ZKBt8cZHfgH1IXN+CL656W3uUa4w==} engines: {node: '>=6.9.0'} peerDependencies: - '@babel/core': ^7.0.0-0 + '@babel/core': '>=7.29.6' '@babel/plugin-syntax-logical-assignment-operators@7.10.4': resolution: {integrity: sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==} peerDependencies: - '@babel/core': ^7.0.0-0 + '@babel/core': '>=7.29.6' '@babel/plugin-syntax-nullish-coalescing-operator@7.8.3': resolution: {integrity: sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==} peerDependencies: - '@babel/core': ^7.0.0-0 + '@babel/core': '>=7.29.6' '@babel/plugin-syntax-numeric-separator@7.10.4': resolution: {integrity: sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==} peerDependencies: - '@babel/core': ^7.0.0-0 + '@babel/core': '>=7.29.6' '@babel/plugin-syntax-object-rest-spread@7.8.3': resolution: {integrity: sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==} peerDependencies: - '@babel/core': ^7.0.0-0 + '@babel/core': '>=7.29.6' '@babel/plugin-syntax-optional-catch-binding@7.8.3': resolution: {integrity: sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==} peerDependencies: - '@babel/core': ^7.0.0-0 + '@babel/core': '>=7.29.6' '@babel/plugin-syntax-optional-chaining@7.8.3': resolution: {integrity: sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==} peerDependencies: - '@babel/core': ^7.0.0-0 + '@babel/core': '>=7.29.6' '@babel/plugin-syntax-private-property-in-object@7.14.5': resolution: {integrity: sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==} engines: {node: '>=6.9.0'} peerDependencies: - '@babel/core': ^7.0.0-0 + '@babel/core': '>=7.29.6' '@babel/plugin-syntax-top-level-await@7.14.5': resolution: {integrity: sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==} engines: {node: '>=6.9.0'} peerDependencies: - '@babel/core': ^7.0.0-0 + '@babel/core': '>=7.29.6' '@babel/plugin-syntax-typescript@7.28.6': resolution: {integrity: sha512-+nDNmQye7nlnuuHDboPbGm00Vqg3oO8niRRL27/4LYHUsHYh0zJ1xWOz0uRwNFmM1Avzk8wZbc6rdiYhomzv/A==} engines: {node: '>=6.9.0'} peerDependencies: - '@babel/core': ^7.0.0-0 + '@babel/core': '>=7.29.6' '@babel/runtime@7.28.6': resolution: {integrity: sha512-05WQkdpL9COIMz4LjTxGpPNCdlpyimKppYNoJ5Di5EUObifl8t4tuLuUBBZEpoLYOmfvIWrsp9fCl0HoPRVTdA==} engines: {node: '>=6.9.0'} - '@babel/template@7.28.6': - resolution: {integrity: sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==} - engines: {node: '>=6.9.0'} + '@babel/template@8.0.0': + resolution: {integrity: sha512-eAD0QW/AlbamBbw0FeGiwasbCVPq5ncW0HNVyLP3B9czqLyh4gvw+5JTSNt6le9+ziAU7mqDZsKTHf3jTb4chQ==} + engines: {node: ^22.18.0 || >=24.11.0} - '@babel/traverse@7.29.0': - resolution: {integrity: sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==} - engines: {node: '>=6.9.0'} + '@babel/traverse@8.0.0': + resolution: {integrity: sha512-bxTj/W2VclGE6CctlfQOpxg8MPDzXArRqkOBePw8EHfebcjF7fETWSS3BriEECo+UiU/Yblq+xUtSImFu7cTbw==} + engines: {node: ^22.18.0 || >=24.11.0} '@babel/types@7.29.0': resolution: {integrity: sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==} engines: {node: '>=6.9.0'} + '@babel/types@8.0.0': + resolution: {integrity: sha512-K8ponJDxBwDHigkeFqaqT5wLGl4bTlwMafR8k7b5CPxr6Ww+UG9ls8Yx6Tcpboxu97eeGVEEyKcHmEyOwN1vSw==} + engines: {node: ^22.18.0 || >=24.11.0} + '@bcoe/v8-coverage@0.2.3': resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==} @@ -715,57 +749,57 @@ packages: '@napi-rs/wasm-runtime@0.2.12': resolution: {integrity: sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ==} - '@next/env@16.1.6': - resolution: {integrity: sha512-N1ySLuZjnAtN3kFnwhAwPvZah8RJxKasD7x1f8shFqhncnWZn4JMfg37diLNuoHsLAlrDfM3g4mawVdtAG8XLQ==} + '@next/env@16.2.9': + resolution: {integrity: sha512-ki5VxxXfzD/9TDe13wyeTKIjQTAwBVpnr8KhRDUr8ltMUq1/NBpWNT5tiPoxiGl+PHM4X2ahSOiPk6iAimIzPg==} - '@next/swc-darwin-arm64@16.1.6': - resolution: {integrity: sha512-wTzYulosJr/6nFnqGW7FrG3jfUUlEf8UjGA0/pyypJl42ExdVgC6xJgcXQ+V8QFn6niSG2Pb8+MIG1mZr2vczw==} + '@next/swc-darwin-arm64@16.2.9': + resolution: {integrity: sha512-HkfxNYUCmcct0Xsqib5KxqMSHV4AHJq857BNRchyBDs4YS19aHzVfn1kDuBYKqLLQBjXgnkIsjV2Kd4d2wzYhw==} engines: {node: '>= 10'} cpu: [arm64] os: [darwin] - '@next/swc-darwin-x64@16.1.6': - resolution: {integrity: sha512-BLFPYPDO+MNJsiDWbeVzqvYd4NyuRrEYVB5k2N3JfWncuHAy2IVwMAOlVQDFjj+krkWzhY2apvmekMkfQR0CUQ==} + '@next/swc-darwin-x64@16.2.9': + resolution: {integrity: sha512-7IAtK4MeybpqRV9GRABWEhJ62mOS+rzWOzOTFie4cSEtm12xsoOMJRcECoZx3FHPzFAqN/IJtHqWAFOLfl152w==} engines: {node: '>= 10'} cpu: [x64] os: [darwin] - '@next/swc-linux-arm64-gnu@16.1.6': - resolution: {integrity: sha512-OJYkCd5pj/QloBvoEcJ2XiMnlJkRv9idWA/j0ugSuA34gMT6f5b7vOiCQHVRpvStoZUknhl6/UxOXL4OwtdaBw==} + '@next/swc-linux-arm64-gnu@16.2.9': + resolution: {integrity: sha512-hBD75iWpUtkL9SmQmcRhmLomn9jgkPzCEkbOcLgHymPEKzv+6ONy13RRiIEz/iEObjkS2Jlb5gYS2XGoS3X4rw==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] libc: [glibc] - '@next/swc-linux-arm64-musl@16.1.6': - resolution: {integrity: sha512-S4J2v+8tT3NIO9u2q+S0G5KdvNDjXfAv06OhfOzNDaBn5rw84DGXWndOEB7d5/x852A20sW1M56vhC/tRVbccQ==} + '@next/swc-linux-arm64-musl@16.2.9': + resolution: {integrity: sha512-qZTI3pf9SGc/obr8NkQAekBxmp1QK+kVm+VAf3BALLfFAj+1kUhkTxmrWpVos9R/UYIA8AWX2p6cGI5WdwzVUA==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] libc: [musl] - '@next/swc-linux-x64-gnu@16.1.6': - resolution: {integrity: sha512-2eEBDkFlMMNQnkTyPBhQOAyn2qMxyG2eE7GPH2WIDGEpEILcBPI/jdSv4t6xupSP+ot/jkfrCShLAa7+ZUPcJQ==} + '@next/swc-linux-x64-gnu@16.2.9': + resolution: {integrity: sha512-xm0HfRNX+UkH4R3c18ynswjj5o5uEj/7iI9p9omdtTSIsRCzQqkGMA+10nzJ4EHnYC3as65IMhbbl5fWRUWHYg==} engines: {node: '>= 10'} cpu: [x64] os: [linux] libc: [glibc] - '@next/swc-linux-x64-musl@16.1.6': - resolution: {integrity: sha512-oicJwRlyOoZXVlxmIMaTq7f8pN9QNbdes0q2FXfRsPhfCi8n8JmOZJm5oo1pwDaFbnnD421rVU409M3evFbIqg==} + '@next/swc-linux-x64-musl@16.2.9': + resolution: {integrity: sha512-QumimHkGEG6vM3PfEDWKyKen03NcqLOkeKB1EfcPe7VxzmEiCa4jNnMyBn/US5zcd/VE1CI+O8Ovb3lfjVHfGw==} engines: {node: '>= 10'} cpu: [x64] os: [linux] libc: [musl] - '@next/swc-win32-arm64-msvc@16.1.6': - resolution: {integrity: sha512-gQmm8izDTPgs+DCWH22kcDmuUp7NyiJgEl18bcr8irXA5N2m2O+JQIr6f3ct42GOs9c0h8QF3L5SzIxcYAAXXw==} + '@next/swc-win32-arm64-msvc@16.2.9': + resolution: {integrity: sha512-hzQpKZvw8rAwI6A2uQh6SacCSvNAXaIkPNsWwzqqfRiIMiXMfH936skDhz1OO6KpvdKkJrgHHtqQOq5PIXOvdQ==} engines: {node: '>= 10'} cpu: [arm64] os: [win32] - '@next/swc-win32-x64-msvc@16.1.6': - resolution: {integrity: sha512-NRfO39AIrzBnixKbjuo2YiYhB6o9d8v/ymU9m/Xk8cyVk+k7XylniXkHwjs4s70wedVffc6bQNbufk5v0xEm0A==} + '@next/swc-win32-x64-msvc@16.2.9': + resolution: {integrity: sha512-qr2VL3Ce5QrwgO2yh1ujSBawrimjVKX8FGF/cOynmdYKJY0BdHpGVNIRK1tqONB10Vkm25Ub1BD2bkjWs4+96w==} engines: {node: '>= 10'} cpu: [x64] os: [win32] @@ -983,6 +1017,9 @@ packages: '@types/draco3d@1.4.10': resolution: {integrity: sha512-AX22jp8Y7wwaBgAixaSvkoG4M/+PlAcm3Qs4OW8yT9DM4xUpWKeFhLueTAyZF39pviAdcDdeJoACapiAceqNcw==} + '@types/gensync@1.0.5': + resolution: {integrity: sha512-MbsRCT7mTikHwKZ0X+LVUTLRrZZRLipTuXEO9qOYO+zmjMVk81axyClMROf6uoPD9MRVu46bx8zoR0Ad9q3NAg==} + '@types/istanbul-lib-coverage@2.0.6': resolution: {integrity: sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==} @@ -998,6 +1035,9 @@ packages: '@types/jsdom@21.1.7': resolution: {integrity: sha512-yOriVnggzrnQ3a9OKOCxaVuSug3w3/SbOj5i7VwXWZEyUNl3bLF9V3MfxGbZKuwqJOQyRfqXyROBB1CoZLFWzA==} + '@types/jsesc@2.5.1': + resolution: {integrity: sha512-9VN+6yxLOPLOav+7PwjZbxiID2bVaeq0ED4qSQmdQTdjnXJSaCVKTR58t15oqH1H5t8Ng2ZX1SabJVoN9Q34bw==} + '@types/node@20.19.11': resolution: {integrity: sha512-uug3FEEGv0r+jrecvUUpbY8lLisvIjg6AAic6a2bSP5OEOLeJsDSnvhCDov7ipFFMXS3orMpzlmi0ZcuGkBbow==} @@ -1026,8 +1066,8 @@ packages: '@types/stats.js@0.17.4': resolution: {integrity: sha512-jIBvWWShCvlBqBNIZt0KAshWpvSjhkwkEu4ZUcASoAvhmrgAUI2t1dXrjSL4xXVLB4FznPrIsX3nKXFl/Dt4vA==} - '@types/three@0.183.1': - resolution: {integrity: sha512-f2Pu5Hrepfgavttdye3PsH5RWyY/AvdZQwIVhrc4uNtvF7nOWJacQKcoVJn0S4f0yYbmAE6AR+ve7xDcuYtMGw==} + '@types/three@0.182.0': + resolution: {integrity: sha512-WByN9V3Sbwbe2OkWuSGyoqQO8Du6yhYaXtXLoA5FkKTUJorZ+yOHBZ35zUUPQXlAKABZmbYp5oAqpA4RBjtJ/Q==} '@types/tough-cookie@4.0.5': resolution: {integrity: sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==} @@ -1043,6 +1083,7 @@ packages: '@ungap/structured-clone@1.3.0': resolution: {integrity: sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==} + deprecated: Potential CWE-502 - Update to 1.3.1 or higher '@unrs/resolver-binding-android-arm-eabi@1.11.1': resolution: {integrity: sha512-ppLRUgHVaGRWUx0R0Ut06Mjo9gBaBkg3v/8AxusGLhsIotbBLuRk51rAzqLC8gq6NyyAojEXglNjzf6R948DNw==} @@ -1202,8 +1243,8 @@ packages: arg@4.1.3: resolution: {integrity: sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==} - argparse@1.0.10: - resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} + argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} aria-query@5.3.0: resolution: {integrity: sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==} @@ -1216,7 +1257,7 @@ packages: resolution: {integrity: sha512-gRpauEU2KRrCox5Z296aeVHR4jQ98BCnu0IO332D/xpHNOsIH/bgSRk9k6GbKIbBw8vFeN6ctuu6tV8WOyVfYQ==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} peerDependencies: - '@babel/core': ^7.11.0 || ^8.0.0-0 + '@babel/core': '>=7.29.6' babel-plugin-istanbul@7.0.1: resolution: {integrity: sha512-D8Z6Qm8jCvVXtIRkBnqNHX0zJ37rQcFJ9u8WOS6tkYOsRdHBzypCstaxWiu5ZIlqQtviRYbgnRLSoCEvjqcqbA==} @@ -1229,17 +1270,21 @@ packages: babel-preset-current-node-syntax@1.2.0: resolution: {integrity: sha512-E/VlAEzRrsLEb2+dv8yp3bo4scof3l9nR4lrld+Iy5NyVqgVYUJnDAmunkhPMisRI32Qc4iRiz425d8vM++2fg==} peerDependencies: - '@babel/core': ^7.0.0 || ^8.0.0-0 + '@babel/core': '>=7.29.6' babel-preset-jest@30.3.0: resolution: {integrity: sha512-6ZcUbWHC+dMz2vfzdNwi87Z1gQsLNK2uLuK1Q89R11xdvejcivlYYwDlEv0FHX3VwEXpbBQ9uufB/MUNpZGfhQ==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} peerDependencies: - '@babel/core': ^7.11.0 || ^8.0.0-beta.1 + '@babel/core': '>=7.29.6' balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + balanced-match@4.0.4: + resolution: {integrity: sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==} + engines: {node: 18 || 20 || >=22} + base64-js@1.5.1: resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} @@ -1251,12 +1296,13 @@ packages: bidi-js@1.0.3: resolution: {integrity: sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw==} - brace-expansion@1.1.12: - resolution: {integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==} - brace-expansion@2.0.2: resolution: {integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==} + brace-expansion@5.0.6: + resolution: {integrity: sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==} + engines: {node: 18 || 20 || >=22} + browserslist@4.28.1: resolution: {integrity: sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==} engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} @@ -1293,9 +1339,6 @@ packages: peerDependencies: three: '>=0.126.1' - caniuse-lite@1.0.30001739: - resolution: {integrity: sha512-y+j60d6ulelrNSwpPyrHdl+9mJnQzHBr08xm48Qno0nSk4h3Qojh+ziv2qE6rXf4k3tadF4o1J/1tAbVm1NtnA==} - caniuse-lite@1.0.30001777: resolution: {integrity: sha512-tmN+fJxroPndC74efCdp12j+0rk0RHwV5Jwa1zWaFVyw2ZxAuPeG8ZgWC3Wz7uSjT3qMRQ5XHZ4COgQmsCMJAQ==} @@ -1349,9 +1392,6 @@ packages: color-name@1.1.4: resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} - concat-map@0.0.1: - resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} - convert-source-map@2.0.0: resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} @@ -1453,6 +1493,10 @@ packages: emoji-regex@9.2.2: resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} + empathic@2.0.1: + resolution: {integrity: sha512-YGRs8knHhKHVShLkFET/rWAU8kmHbOV5LwN938RHI0pljAJ1Gf6SzXsSmRaEzcXTtOOmVqJ5+WtQPL5uigY50Q==} + engines: {node: '>=14'} + enhanced-resolve@5.18.3: resolution: {integrity: sha512-d4lC8xfavMeBjzGr2vECC3fsGXziXZQyJxD868h2M/mBI3PwAuODxAkLkq5HYuvrPYcUtiLzsTo8U3PgX3Ocww==} engines: {node: '>=10.13.0'} @@ -1472,11 +1516,6 @@ packages: resolution: {integrity: sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==} engines: {node: '>=8'} - esprima@4.0.1: - resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} - engines: {node: '>=4'} - hasBin: true - execa@5.1.1: resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==} engines: {node: '>=10'} @@ -1569,8 +1608,8 @@ packages: gsap@3.13.0: resolution: {integrity: sha512-QL7MJ2WMjm1PHWsoFrAQH/J8wUeqZvMtHO58qdekHpCfhvhSL4gSiz6vJf5EeMP0LOn3ZCprL2ki/gjED8ghVw==} - handlebars@4.7.8: - resolution: {integrity: sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==} + handlebars@4.7.9: + resolution: {integrity: sha512-4E71E0rpOaQuJR2A3xDZ+GM1HyWYv1clR58tC8emQNeQe3RH7MAzSbat+V0wG78LQBo6m6bzSG/L4pBuCsgnUQ==} engines: {node: '>=0.4.7'} hasBin: true @@ -1622,6 +1661,9 @@ packages: engines: {node: '>=8'} hasBin: true + import-meta-resolve@4.2.0: + resolution: {integrity: sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg==} + imurmurhash@0.1.4: resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} engines: {node: '>=0.8.19'} @@ -1830,11 +1872,14 @@ packages: resolution: {integrity: sha512-twQoecYPiVA5K/h6SxtORw/Bs3ar+mLUtoPSc7iMXzQzK8d7eJ/R09wmTwAjiamETn1cXYPGfNnu7DMoHgu12w==} hasBin: true + js-tokens@10.0.0: + resolution: {integrity: sha512-lM/UBzQmfJRo9ABXbPWemivdCW8V2G8FHaHdypQaIy523snUjog0W71ayWXTjiR+ixeMyVHN2XcpnTd/liPg/Q==} + js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} - js-yaml@3.14.2: - resolution: {integrity: sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==} + js-yaml@5.1.0: + resolution: {integrity: sha512-s8VA5jkR8f22S3NAXmhKPFqGUduqZGlsufabVOgN14iTdw/RXcym7bKkbwjxLK9Yw2lEvvmJjFp119+KPeo8Kg==} hasBin: true jsdom@26.1.0: @@ -1961,8 +2006,9 @@ packages: lru-cache@10.4.3: resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} - lru-cache@5.1.1: - resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} + lru-cache@11.5.1: + resolution: {integrity: sha512-RPimw/7aMdv2oqRrxKwvZXcPfwBrn/JZ2xYcY9Hus/6LaS3VOAKVWKWgNLCFSiOm1ESXinjsDlidVU7JlnCN2A==} + engines: {node: 20 || >=22} lucide-react@0.542.0: resolution: {integrity: sha512-w3hD8/SQB7+lzU2r4VdFyzzOzKnUjTZIF/MQJGSSvni7Llewni4vuViRppfRAa2guOsY5k4jZyxw/i9DQHv+dw==} @@ -2000,8 +2046,8 @@ packages: peerDependencies: three: '>=0.137' - meshoptimizer@1.0.1: - resolution: {integrity: sha512-Vix+QlA1YYT3FwmBBZ+49cE5y/b+pRrcXKqGpS5ouh33d3lSp2PoTpCw19E0cKDFWalembrHnIaZetf27a+W2g==} + meshoptimizer@0.22.0: + resolution: {integrity: sha512-IebiK79sqIy+E4EgOr+CAw+Ke8hAspXKzBd0JdgEmPHiAwmvEj2S4h1rfvo+o/BnfEYd/jAOg5IeeIjzlzSnDg==} mimic-fn@2.1.0: resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} @@ -2052,8 +2098,8 @@ packages: ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} - nanoid@3.3.11: - resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} + nanoid@3.3.15: + resolution: {integrity: sha512-y7Wygv/7mEOvxTuEQDB8StXdMRBWf1kR/tlhAzBRUFkB2jfcLOAxO/SHmOO2zgz1pVgK29/kyupn059/bCHdjA==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true @@ -2068,8 +2114,8 @@ packages: neo-async@2.6.2: resolution: {integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==} - next@16.1.6: - resolution: {integrity: sha512-hkyRkcu5x/41KoqnROkfTm2pZVbKxvbZRuNvKXLRXxs3VfyO0WhY50TQS40EuKO9SW3rBj/sF3WbVwDACeMZyw==} + next@16.2.9: + resolution: {integrity: sha512-MEOJiq/UvuezAdqVSceHbqDgZt1kDw2tpGVOlsdIoJsQdbN2JY2hpVG4xnXGkbdJUOEWhnRfiu/O4Hpc9Juwww==} engines: {node: '>=20.9.0'} hasBin: true peerDependencies: @@ -2106,6 +2152,10 @@ packages: nwsapi@2.2.23: resolution: {integrity: sha512-7wfH4sLbt4M0gCDzGE6vzQBo0bfTKjU7Sfpqy/7gs1qBfYz2vEJH6vXcBKpO3+6Yu1telwd0t9HpyOoLEQQbIQ==} + obug@2.1.3: + resolution: {integrity: sha512-9miFgM2OFba7hB+pRgvtV84pYTBaoTHohvmIgiRt6dRIzbwEOIaNaP+dIlGs2fNFoB0SeISs0Jz5WFVRid6Xyg==} + engines: {node: '>=12.20.0'} + ogl@1.0.11: resolution: {integrity: sha512-kUpC154AFfxi16pmZUK4jk3J+8zxwTWGPo03EoYA8QPbzikHoaC82n6pNTbd+oEaJonaE8aPWBlX7ad9zrqLsA==} @@ -2164,12 +2214,8 @@ packages: picocolors@1.1.1: resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} - picomatch@2.3.1: - resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} - engines: {node: '>=8.6'} - - picomatch@4.0.3: - resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==} + picomatch@4.0.4: + resolution: {integrity: sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==} engines: {node: '>=12'} pirates@4.0.7: @@ -2180,12 +2226,8 @@ packages: resolution: {integrity: sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==} engines: {node: '>=8'} - postcss@8.4.31: - resolution: {integrity: sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==} - engines: {node: ^10 || ^12 || >=14} - - postcss@8.5.6: - resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==} + postcss@8.5.15: + resolution: {integrity: sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==} engines: {node: ^10 || ^12 || >=14} potpack@1.0.2: @@ -2275,10 +2317,6 @@ packages: scheduler@0.27.0: resolution: {integrity: sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==} - semver@6.3.1: - resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} - hasBin: true - semver@7.7.4: resolution: {integrity: sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==} engines: {node: '>=10'} @@ -2318,9 +2356,6 @@ packages: resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} engines: {node: '>=0.10.0'} - sprintf-js@1.0.3: - resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} - stack-utils@2.0.6: resolution: {integrity: sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==} engines: {node: '>=10'} @@ -2436,8 +2471,8 @@ packages: peerDependencies: three: '>=0.128.0' - three@0.183.2: - resolution: {integrity: sha512-di3BsL2FEQ1PA7Hcvn4fyJOlxRRgFYBpMTcyOgkwJIaDOdJMebEFPA+t98EvjuljDx4hNulAGwF6KIjtwI5jgQ==} + three@0.182.0: + resolution: {integrity: sha512-GbHabT+Irv+ihI1/f5kIIsZ+Ef9Sl5A1Y7imvS5RQjWgtTPfPnZ43JmlYI7NtCRDK9zir20lQpfg8/9Yd02OvQ==} tldts-core@6.1.86: resolution: {integrity: sha512-Je6p7pkk+KMzMv2XXKmAE3McmolOQFdxkKw0R8EYNr7sELW46JqnNeTX8ybPiQgvg1ymCoF8LXs5fzFaZvJPTA==} @@ -2475,7 +2510,7 @@ packages: engines: {node: ^14.15.0 || ^16.10.0 || ^18.0.0 || >=20.0.0} hasBin: true peerDependencies: - '@babel/core': '>=7.0.0-beta.0 <8' + '@babel/core': '>=7.29.6' '@jest/transform': ^29.0.0 || ^30.0.0 '@jest/types': ^29.0.0 || ^30.0.0 babel-jest: ^29.0.0 || ^30.0.0 @@ -2623,8 +2658,8 @@ packages: resolution: {integrity: sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - ws@8.19.0: - resolution: {integrity: sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg==} + ws@8.21.0: + resolution: {integrity: sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g==} engines: {node: '>=10.0.0'} peerDependencies: bufferutil: ^4.0.1 @@ -2646,9 +2681,6 @@ packages: resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} engines: {node: '>=10'} - yallist@3.1.1: - resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} - yallist@5.0.0: resolution: {integrity: sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==} engines: {node: '>=18'} @@ -2722,27 +2754,31 @@ snapshots: js-tokens: 4.0.0 picocolors: 1.1.1 - '@babel/compat-data@7.29.0': {} - - '@babel/core@7.29.0': + '@babel/code-frame@8.0.0': dependencies: - '@babel/code-frame': 7.29.0 - '@babel/generator': 7.29.1 - '@babel/helper-compilation-targets': 7.28.6 - '@babel/helper-module-transforms': 7.28.6(@babel/core@7.29.0) - '@babel/helpers': 7.28.6 - '@babel/parser': 7.29.0 - '@babel/template': 7.28.6 - '@babel/traverse': 7.29.0 - '@babel/types': 7.29.0 - '@jridgewell/remapping': 2.3.5 + '@babel/helper-validator-identifier': 8.0.2 + js-tokens: 10.0.0 + + '@babel/compat-data@8.0.0': {} + + '@babel/core@8.0.1': + dependencies: + '@babel/code-frame': 8.0.0 + '@babel/generator': 8.0.0 + '@babel/helper-compilation-targets': 8.0.0 + '@babel/helpers': 8.0.0 + '@babel/parser': 8.0.0 + '@babel/template': 8.0.0 + '@babel/traverse': 8.0.0 + '@babel/types': 8.0.0 + '@types/gensync': 1.0.5 convert-source-map: 2.0.0 - debug: 4.4.3 + empathic: 2.0.1 gensync: 1.0.0-beta.2 + import-meta-resolve: 4.2.0 json5: 2.2.3 - semver: 6.3.1 - transitivePeerDependencies: - - supports-color + obug: 2.1.3 + semver: 7.7.4 '@babel/generator@7.29.1': dependencies: @@ -2752,159 +2788,163 @@ snapshots: '@jridgewell/trace-mapping': 0.3.30 jsesc: 3.1.0 - '@babel/helper-compilation-targets@7.28.6': + '@babel/generator@8.0.0': dependencies: - '@babel/compat-data': 7.29.0 - '@babel/helper-validator-option': 7.27.1 + '@babel/parser': 8.0.0 + '@babel/types': 8.0.0 + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.30 + '@types/jsesc': 2.5.1 + jsesc: 3.1.0 + + '@babel/helper-compilation-targets@8.0.0': + dependencies: + '@babel/compat-data': 8.0.0 + '@babel/helper-validator-option': 8.0.0 browserslist: 4.28.1 - lru-cache: 5.1.1 - semver: 6.3.1 + lru-cache: 11.5.1 + semver: 7.7.4 - '@babel/helper-globals@7.28.0': {} - - '@babel/helper-module-imports@7.28.6': - dependencies: - '@babel/traverse': 7.29.0 - '@babel/types': 7.29.0 - transitivePeerDependencies: - - supports-color - - '@babel/helper-module-transforms@7.28.6(@babel/core@7.29.0)': - dependencies: - '@babel/core': 7.29.0 - '@babel/helper-module-imports': 7.28.6 - '@babel/helper-validator-identifier': 7.28.5 - '@babel/traverse': 7.29.0 - transitivePeerDependencies: - - supports-color + '@babel/helper-globals@8.0.0': {} '@babel/helper-plugin-utils@7.28.6': {} '@babel/helper-string-parser@7.27.1': {} + '@babel/helper-string-parser@8.0.0': {} + '@babel/helper-validator-identifier@7.28.5': {} - '@babel/helper-validator-option@7.27.1': {} + '@babel/helper-validator-identifier@8.0.2': {} - '@babel/helpers@7.28.6': + '@babel/helper-validator-option@8.0.0': {} + + '@babel/helpers@8.0.0': dependencies: - '@babel/template': 7.28.6 - '@babel/types': 7.29.0 + '@babel/template': 8.0.0 + '@babel/types': 8.0.0 '@babel/parser@7.29.0': dependencies: '@babel/types': 7.29.0 - '@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.29.0)': + '@babel/parser@8.0.0': dependencies: - '@babel/core': 7.29.0 + '@babel/types': 8.0.0 + + '@babel/plugin-syntax-async-generators@7.8.4(@babel/core@8.0.1)': + dependencies: + '@babel/core': 8.0.1 '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-syntax-bigint@7.8.3(@babel/core@7.29.0)': + '@babel/plugin-syntax-bigint@7.8.3(@babel/core@8.0.1)': dependencies: - '@babel/core': 7.29.0 + '@babel/core': 8.0.1 '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.29.0)': + '@babel/plugin-syntax-class-properties@7.12.13(@babel/core@8.0.1)': dependencies: - '@babel/core': 7.29.0 + '@babel/core': 8.0.1 '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-syntax-class-static-block@7.14.5(@babel/core@7.29.0)': + '@babel/plugin-syntax-class-static-block@7.14.5(@babel/core@8.0.1)': dependencies: - '@babel/core': 7.29.0 + '@babel/core': 8.0.1 '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-syntax-import-attributes@7.28.6(@babel/core@7.29.0)': + '@babel/plugin-syntax-import-attributes@7.28.6(@babel/core@8.0.1)': dependencies: - '@babel/core': 7.29.0 + '@babel/core': 8.0.1 '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.29.0)': + '@babel/plugin-syntax-import-meta@7.10.4(@babel/core@8.0.1)': dependencies: - '@babel/core': 7.29.0 + '@babel/core': 8.0.1 '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.29.0)': + '@babel/plugin-syntax-json-strings@7.8.3(@babel/core@8.0.1)': dependencies: - '@babel/core': 7.29.0 + '@babel/core': 8.0.1 '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-syntax-jsx@7.28.6(@babel/core@7.29.0)': + '@babel/plugin-syntax-jsx@7.28.6(@babel/core@8.0.1)': dependencies: - '@babel/core': 7.29.0 + '@babel/core': 8.0.1 '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.29.0)': + '@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@8.0.1)': dependencies: - '@babel/core': 7.29.0 + '@babel/core': 8.0.1 '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.29.0)': + '@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@8.0.1)': dependencies: - '@babel/core': 7.29.0 + '@babel/core': 8.0.1 '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.29.0)': + '@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@8.0.1)': dependencies: - '@babel/core': 7.29.0 + '@babel/core': 8.0.1 '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.29.0)': + '@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@8.0.1)': dependencies: - '@babel/core': 7.29.0 + '@babel/core': 8.0.1 '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.29.0)': + '@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@8.0.1)': dependencies: - '@babel/core': 7.29.0 + '@babel/core': 8.0.1 '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.29.0)': + '@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@8.0.1)': dependencies: - '@babel/core': 7.29.0 + '@babel/core': 8.0.1 '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-syntax-private-property-in-object@7.14.5(@babel/core@7.29.0)': + '@babel/plugin-syntax-private-property-in-object@7.14.5(@babel/core@8.0.1)': dependencies: - '@babel/core': 7.29.0 + '@babel/core': 8.0.1 '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.29.0)': + '@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@8.0.1)': dependencies: - '@babel/core': 7.29.0 + '@babel/core': 8.0.1 '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-syntax-typescript@7.28.6(@babel/core@7.29.0)': + '@babel/plugin-syntax-typescript@7.28.6(@babel/core@8.0.1)': dependencies: - '@babel/core': 7.29.0 + '@babel/core': 8.0.1 '@babel/helper-plugin-utils': 7.28.6 '@babel/runtime@7.28.6': {} - '@babel/template@7.28.6': + '@babel/template@8.0.0': dependencies: - '@babel/code-frame': 7.29.0 - '@babel/parser': 7.29.0 - '@babel/types': 7.29.0 + '@babel/code-frame': 8.0.0 + '@babel/parser': 8.0.0 + '@babel/types': 8.0.0 - '@babel/traverse@7.29.0': + '@babel/traverse@8.0.0': dependencies: - '@babel/code-frame': 7.29.0 - '@babel/generator': 7.29.1 - '@babel/helper-globals': 7.28.0 - '@babel/parser': 7.29.0 - '@babel/template': 7.28.6 - '@babel/types': 7.29.0 - debug: 4.4.3 - transitivePeerDependencies: - - supports-color + '@babel/code-frame': 8.0.0 + '@babel/generator': 8.0.0 + '@babel/helper-globals': 8.0.0 + '@babel/parser': 8.0.0 + '@babel/template': 8.0.0 + '@babel/types': 8.0.0 + obug: 2.1.3 '@babel/types@7.29.0': dependencies: '@babel/helper-string-parser': 7.27.1 '@babel/helper-validator-identifier': 7.28.5 + '@babel/types@8.0.0': + dependencies: + '@babel/helper-string-parser': 8.0.0 + '@babel/helper-validator-identifier': 8.0.2 + '@bcoe/v8-coverage@0.2.3': {} '@biomejs/biome@2.2.0': @@ -3104,7 +3144,7 @@ snapshots: camelcase: 5.3.1 find-up: 4.1.0 get-package-type: 0.1.0 - js-yaml: 3.14.2 + js-yaml: 5.1.0 resolve-from: 5.0.0 '@istanbuljs/schema@0.1.3': {} @@ -3181,8 +3221,6 @@ snapshots: dependencies: expect: 30.3.0 jest-snapshot: 30.3.0 - transitivePeerDependencies: - - supports-color '@jest/fake-timers@30.3.0': dependencies: @@ -3201,8 +3239,6 @@ snapshots: '@jest/expect': 30.3.0 '@jest/types': 30.3.0 jest-mock: 30.3.0 - transitivePeerDependencies: - - supports-color '@jest/pattern@30.0.1': dependencies: @@ -3270,7 +3306,7 @@ snapshots: '@jest/transform@30.3.0': dependencies: - '@babel/core': 7.29.0 + '@babel/core': 8.0.1 '@jest/types': 30.3.0 '@jridgewell/trace-mapping': 0.3.30 babel-plugin-istanbul: 7.0.1 @@ -3284,8 +3320,6 @@ snapshots: pirates: 4.0.7 slash: 3.0.0 write-file-atomic: 5.0.1 - transitivePeerDependencies: - - supports-color '@jest/types@30.3.0': dependencies: @@ -3323,10 +3357,10 @@ snapshots: '@mediapipe/tasks-vision@0.10.17': {} - '@monogrid/gainmap-js@3.4.0(three@0.183.2)': + '@monogrid/gainmap-js@3.4.0(three@0.182.0)': dependencies: promise-worker-transferable: 1.0.4 - three: 0.183.2 + three: 0.182.0 '@napi-rs/wasm-runtime@0.2.12': dependencies: @@ -3335,30 +3369,30 @@ snapshots: '@tybys/wasm-util': 0.10.1 optional: true - '@next/env@16.1.6': {} + '@next/env@16.2.9': {} - '@next/swc-darwin-arm64@16.1.6': + '@next/swc-darwin-arm64@16.2.9': optional: true - '@next/swc-darwin-x64@16.1.6': + '@next/swc-darwin-x64@16.2.9': optional: true - '@next/swc-linux-arm64-gnu@16.1.6': + '@next/swc-linux-arm64-gnu@16.2.9': optional: true - '@next/swc-linux-arm64-musl@16.1.6': + '@next/swc-linux-arm64-musl@16.2.9': optional: true - '@next/swc-linux-x64-gnu@16.1.6': + '@next/swc-linux-x64-gnu@16.2.9': optional: true - '@next/swc-linux-x64-musl@16.1.6': + '@next/swc-linux-x64-musl@16.2.9': optional: true - '@next/swc-win32-arm64-msvc@16.1.6': + '@next/swc-win32-arm64-msvc@16.2.9': optional: true - '@next/swc-win32-x64-msvc@16.1.6': + '@next/swc-win32-x64-msvc@16.2.9': optional: true '@pkgjs/parseargs@0.11.0': @@ -3366,28 +3400,28 @@ snapshots: '@pkgr/core@0.2.9': {} - '@react-three/drei@10.7.7(@react-three/fiber@9.5.0(@types/react@19.1.12)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(three@0.183.2))(@types/react@19.1.12)(@types/three@0.183.1)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(three@0.183.2)': + '@react-three/drei@10.7.7(@react-three/fiber@9.5.0(@types/react@19.1.12)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(three@0.182.0))(@types/react@19.1.12)(@types/three@0.182.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(three@0.182.0)': dependencies: '@babel/runtime': 7.28.6 '@mediapipe/tasks-vision': 0.10.17 - '@monogrid/gainmap-js': 3.4.0(three@0.183.2) - '@react-three/fiber': 9.5.0(@types/react@19.1.12)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(three@0.183.2) + '@monogrid/gainmap-js': 3.4.0(three@0.182.0) + '@react-three/fiber': 9.5.0(@types/react@19.1.12)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(three@0.182.0) '@use-gesture/react': 10.3.1(react@19.1.0) - camera-controls: 3.1.2(three@0.183.2) + camera-controls: 3.1.2(three@0.182.0) cross-env: 7.0.3 detect-gpu: 5.0.70 glsl-noise: 0.0.0 hls.js: 1.6.15 - maath: 0.10.8(@types/three@0.183.1)(three@0.183.2) - meshline: 3.3.1(three@0.183.2) + maath: 0.10.8(@types/three@0.182.0)(three@0.182.0) + meshline: 3.3.1(three@0.182.0) react: 19.1.0 - stats-gl: 2.4.2(@types/three@0.183.1)(three@0.183.2) + stats-gl: 2.4.2(@types/three@0.182.0)(three@0.182.0) stats.js: 0.17.0 suspend-react: 0.1.3(react@19.1.0) - three: 0.183.2 - three-mesh-bvh: 0.8.3(three@0.183.2) - three-stdlib: 2.36.1(three@0.183.2) - troika-three-text: 0.52.4(three@0.183.2) + three: 0.182.0 + three-mesh-bvh: 0.8.3(three@0.182.0) + three-stdlib: 2.36.1(three@0.182.0) + troika-three-text: 0.52.4(three@0.182.0) tunnel-rat: 0.1.2(@types/react@19.1.12)(react@19.1.0) use-sync-external-store: 1.6.0(react@19.1.0) utility-types: 3.11.0 @@ -3399,7 +3433,7 @@ snapshots: - '@types/three' - immer - '@react-three/fiber@9.5.0(@types/react@19.1.12)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(three@0.183.2)': + '@react-three/fiber@9.5.0(@types/react@19.1.12)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(three@0.182.0)': dependencies: '@babel/runtime': 7.28.6 '@types/webxr': 0.5.24 @@ -3410,7 +3444,7 @@ snapshots: react-use-measure: 2.1.7(react-dom@19.1.0(react@19.1.0))(react@19.1.0) scheduler: 0.27.0 suspend-react: 0.1.3(react@19.1.0) - three: 0.183.2 + three: 0.182.0 use-sync-external-store: 1.6.0(react@19.1.0) zustand: 5.0.11(@types/react@19.1.12)(react@19.1.0)(use-sync-external-store@1.6.0(react@19.1.0)) optionalDependencies: @@ -3502,7 +3536,7 @@ snapshots: '@alloc/quick-lru': 5.2.0 '@tailwindcss/node': 4.1.12 '@tailwindcss/oxide': 4.1.12 - postcss: 8.5.6 + postcss: 8.5.15 tailwindcss: 4.1.12 '@testing-library/dom@10.4.1': @@ -3579,6 +3613,8 @@ snapshots: '@types/draco3d@1.4.10': {} + '@types/gensync@1.0.5': {} + '@types/istanbul-lib-coverage@2.0.6': {} '@types/istanbul-lib-report@3.0.3': @@ -3600,6 +3636,8 @@ snapshots: '@types/tough-cookie': 4.0.5 parse5: 7.3.0 + '@types/jsesc@2.5.1': {} + '@types/node@20.19.11': dependencies: undici-types: 6.21.0 @@ -3624,7 +3662,7 @@ snapshots: '@types/stats.js@0.17.4': {} - '@types/three@0.183.1': + '@types/three@0.182.0': dependencies: '@dimforge/rapier3d-compat': 0.12.0 '@tweenjs/tween.js': 23.1.3 @@ -3632,7 +3670,7 @@ snapshots: '@types/webxr': 0.5.24 '@webgpu/types': 0.1.69 fflate: 0.8.2 - meshoptimizer: 1.0.1 + meshoptimizer: 0.22.0 '@types/tough-cookie@4.0.5': {} @@ -3741,13 +3779,11 @@ snapshots: anymatch@3.1.3: dependencies: normalize-path: 3.0.0 - picomatch: 2.3.1 + picomatch: 4.0.4 arg@4.1.3: {} - argparse@1.0.10: - dependencies: - sprintf-js: 1.0.3 + argparse@2.0.1: {} aria-query@5.3.0: dependencies: @@ -3755,18 +3791,16 @@ snapshots: aria-query@5.3.2: {} - babel-jest@30.3.0(@babel/core@7.29.0): + babel-jest@30.3.0(@babel/core@8.0.1): dependencies: - '@babel/core': 7.29.0 + '@babel/core': 8.0.1 '@jest/transform': 30.3.0 '@types/babel__core': 7.20.5 babel-plugin-istanbul: 7.0.1 - babel-preset-jest: 30.3.0(@babel/core@7.29.0) + babel-preset-jest: 30.3.0(@babel/core@8.0.1) chalk: 4.1.2 graceful-fs: 4.2.11 slash: 3.0.0 - transitivePeerDependencies: - - supports-color babel-plugin-istanbul@7.0.1: dependencies: @@ -3775,40 +3809,40 @@ snapshots: '@istanbuljs/schema': 0.1.3 istanbul-lib-instrument: 6.0.3 test-exclude: 6.0.0 - transitivePeerDependencies: - - supports-color babel-plugin-jest-hoist@30.3.0: dependencies: '@types/babel__core': 7.20.5 - babel-preset-current-node-syntax@1.2.0(@babel/core@7.29.0): + babel-preset-current-node-syntax@1.2.0(@babel/core@8.0.1): dependencies: - '@babel/core': 7.29.0 - '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.29.0) - '@babel/plugin-syntax-bigint': 7.8.3(@babel/core@7.29.0) - '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.29.0) - '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.29.0) - '@babel/plugin-syntax-import-attributes': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.29.0) - '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.29.0) - '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.29.0) - '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.29.0) - '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.29.0) - '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.29.0) - '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.29.0) - '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.29.0) - '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.29.0) - '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.29.0) + '@babel/core': 8.0.1 + '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@8.0.1) + '@babel/plugin-syntax-bigint': 7.8.3(@babel/core@8.0.1) + '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@8.0.1) + '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@8.0.1) + '@babel/plugin-syntax-import-attributes': 7.28.6(@babel/core@8.0.1) + '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@8.0.1) + '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@8.0.1) + '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@8.0.1) + '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@8.0.1) + '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@8.0.1) + '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@8.0.1) + '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@8.0.1) + '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@8.0.1) + '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@8.0.1) + '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@8.0.1) - babel-preset-jest@30.3.0(@babel/core@7.29.0): + babel-preset-jest@30.3.0(@babel/core@8.0.1): dependencies: - '@babel/core': 7.29.0 + '@babel/core': 8.0.1 babel-plugin-jest-hoist: 30.3.0 - babel-preset-current-node-syntax: 1.2.0(@babel/core@7.29.0) + babel-preset-current-node-syntax: 1.2.0(@babel/core@8.0.1) balanced-match@1.0.2: {} + balanced-match@4.0.4: {} + base64-js@1.5.1: {} baseline-browser-mapping@2.10.0: {} @@ -3817,15 +3851,14 @@ snapshots: dependencies: require-from-string: 2.0.2 - brace-expansion@1.1.12: - dependencies: - balanced-match: 1.0.2 - concat-map: 0.0.1 - brace-expansion@2.0.2: dependencies: balanced-match: 1.0.2 + brace-expansion@5.0.6: + dependencies: + balanced-match: 4.0.4 + browserslist@4.28.1: dependencies: baseline-browser-mapping: 2.10.0 @@ -3855,11 +3888,9 @@ snapshots: camelcase@6.3.0: {} - camera-controls@3.1.2(three@0.183.2): + camera-controls@3.1.2(three@0.182.0): dependencies: - three: 0.183.2 - - caniuse-lite@1.0.30001739: {} + three: 0.182.0 caniuse-lite@1.0.30001777: {} @@ -3904,8 +3935,6 @@ snapshots: color-name@1.1.4: {} - concat-map@0.0.1: {} - convert-source-map@2.0.0: {} create-require@1.1.1: {} @@ -3975,6 +4004,8 @@ snapshots: emoji-regex@9.2.2: {} + empathic@2.0.1: {} + enhanced-resolve@5.18.3: dependencies: graceful-fs: 4.2.11 @@ -3990,8 +4021,6 @@ snapshots: escape-string-regexp@2.0.0: {} - esprima@4.0.1: {} - execa@5.1.1: dependencies: cross-spawn: 7.0.6 @@ -4085,7 +4114,7 @@ snapshots: gsap@3.13.0: {} - handlebars@4.7.8: + handlebars@4.7.9: dependencies: minimist: 1.2.8 neo-async: 2.6.2 @@ -4139,6 +4168,8 @@ snapshots: pkg-dir: 4.2.0 resolve-cwd: 3.0.0 + import-meta-resolve@4.2.0: {} + imurmurhash@0.1.4: {} indent-string@4.0.0: {} @@ -4168,13 +4199,11 @@ snapshots: istanbul-lib-instrument@6.0.3: dependencies: - '@babel/core': 7.29.0 + '@babel/core': 8.0.1 '@babel/parser': 7.29.0 '@istanbuljs/schema': 0.1.3 istanbul-lib-coverage: 3.2.2 semver: 7.7.4 - transitivePeerDependencies: - - supports-color istanbul-lib-report@3.0.1: dependencies: @@ -4238,7 +4267,6 @@ snapshots: stack-utils: 2.0.6 transitivePeerDependencies: - babel-plugin-macros - - supports-color jest-cli@30.3.0(@types/node@20.19.11)(ts-node@10.9.2(@types/node@20.19.11)(typescript@5.9.2)): dependencies: @@ -4261,12 +4289,12 @@ snapshots: jest-config@30.3.0(@types/node@20.19.11)(ts-node@10.9.2(@types/node@20.19.11)(typescript@5.9.2)): dependencies: - '@babel/core': 7.29.0 + '@babel/core': 8.0.1 '@jest/get-type': 30.1.0 '@jest/pattern': 30.0.1 '@jest/test-sequencer': 30.3.0 '@jest/types': 30.3.0 - babel-jest: 30.3.0(@babel/core@7.29.0) + babel-jest: 30.3.0(@babel/core@8.0.1) chalk: 4.1.2 ci-info: 4.4.0 deepmerge: 4.3.1 @@ -4289,7 +4317,6 @@ snapshots: ts-node: 10.9.2(@types/node@20.19.11)(typescript@5.9.2) transitivePeerDependencies: - babel-plugin-macros - - supports-color jest-diff@30.3.0: dependencies: @@ -4340,7 +4367,7 @@ snapshots: jest-regex-util: 30.0.1 jest-util: 30.3.0 jest-worker: 30.3.0 - picomatch: 4.0.3 + picomatch: 4.0.4 walker: 1.0.8 optionalDependencies: fsevents: 2.3.3 @@ -4364,7 +4391,7 @@ snapshots: '@types/stack-utils': 2.0.3 chalk: 4.1.2 graceful-fs: 4.2.11 - picomatch: 4.0.3 + picomatch: 4.0.4 pretty-format: 30.3.0 slash: 3.0.0 stack-utils: 2.0.6 @@ -4385,8 +4412,6 @@ snapshots: dependencies: jest-regex-util: 30.0.1 jest-snapshot: 30.3.0 - transitivePeerDependencies: - - supports-color jest-resolve@30.3.0: dependencies: @@ -4423,8 +4448,6 @@ snapshots: jest-worker: 30.3.0 p-limit: 3.1.0 source-map-support: 0.5.13 - transitivePeerDependencies: - - supports-color jest-runtime@30.3.0: dependencies: @@ -4450,22 +4473,20 @@ snapshots: jest-util: 30.3.0 slash: 3.0.0 strip-bom: 4.0.0 - transitivePeerDependencies: - - supports-color jest-snapshot@30.3.0: dependencies: - '@babel/core': 7.29.0 + '@babel/core': 8.0.1 '@babel/generator': 7.29.1 - '@babel/plugin-syntax-jsx': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-syntax-typescript': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-syntax-jsx': 7.28.6(@babel/core@8.0.1) + '@babel/plugin-syntax-typescript': 7.28.6(@babel/core@8.0.1) '@babel/types': 7.29.0 '@jest/expect-utils': 30.3.0 '@jest/get-type': 30.1.0 '@jest/snapshot-utils': 30.3.0 '@jest/transform': 30.3.0 '@jest/types': 30.3.0 - babel-preset-current-node-syntax: 1.2.0(@babel/core@7.29.0) + babel-preset-current-node-syntax: 1.2.0(@babel/core@8.0.1) chalk: 4.1.2 expect: 30.3.0 graceful-fs: 4.2.11 @@ -4476,8 +4497,6 @@ snapshots: pretty-format: 30.3.0 semver: 7.7.4 synckit: 0.11.12 - transitivePeerDependencies: - - supports-color jest-util@30.3.0: dependencies: @@ -4486,7 +4505,7 @@ snapshots: chalk: 4.1.2 ci-info: 4.4.0 graceful-fs: 4.2.11 - picomatch: 4.0.3 + picomatch: 4.0.4 jest-validate@30.3.0: dependencies: @@ -4531,12 +4550,13 @@ snapshots: jiti@2.5.1: {} + js-tokens@10.0.0: {} + js-tokens@4.0.0: {} - js-yaml@3.14.2: + js-yaml@5.1.0: dependencies: - argparse: 1.0.10 - esprima: 4.0.1 + argparse: 2.0.1 jsdom@26.1.0: dependencies: @@ -4558,7 +4578,7 @@ snapshots: whatwg-encoding: 3.1.1 whatwg-mimetype: 4.0.0 whatwg-url: 14.2.0 - ws: 8.19.0 + ws: 8.21.0 xml-name-validator: 5.0.0 transitivePeerDependencies: - bufferutil @@ -4636,9 +4656,7 @@ snapshots: lru-cache@10.4.3: {} - lru-cache@5.1.1: - dependencies: - yallist: 3.1.1 + lru-cache@11.5.1: {} lucide-react@0.542.0(react@19.1.0): dependencies: @@ -4646,10 +4664,10 @@ snapshots: lz-string@1.5.0: {} - maath@0.10.8(@types/three@0.183.1)(three@0.183.2): + maath@0.10.8(@types/three@0.182.0)(three@0.182.0): dependencies: - '@types/three': 0.183.1 - three: 0.183.2 + '@types/three': 0.182.0 + three: 0.182.0 magic-string@0.30.18: dependencies: @@ -4667,11 +4685,11 @@ snapshots: merge-stream@2.0.0: {} - meshline@3.3.1(three@0.183.2): + meshline@3.3.1(three@0.182.0): dependencies: - three: 0.183.2 + three: 0.182.0 - meshoptimizer@1.0.1: {} + meshoptimizer@0.22.0: {} mimic-fn@2.1.0: {} @@ -4679,11 +4697,11 @@ snapshots: minimatch@3.1.5: dependencies: - brace-expansion: 1.1.12 + brace-expansion: 2.0.2 minimatch@9.0.9: dependencies: - brace-expansion: 2.0.2 + brace-expansion: 5.0.6 minimist@1.2.8: {} @@ -4709,7 +4727,7 @@ snapshots: ms@2.1.3: {} - nanoid@3.3.11: {} + nanoid@3.3.15: {} napi-postinstall@0.3.4: {} @@ -4717,25 +4735,25 @@ snapshots: neo-async@2.6.2: {} - next@16.1.6(@babel/core@7.29.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0): + next@16.2.9(@babel/core@8.0.1)(react-dom@19.1.0(react@19.1.0))(react@19.1.0): dependencies: - '@next/env': 16.1.6 + '@next/env': 16.2.9 '@swc/helpers': 0.5.15 baseline-browser-mapping: 2.10.0 - caniuse-lite: 1.0.30001739 - postcss: 8.4.31 + caniuse-lite: 1.0.30001777 + postcss: 8.5.15 react: 19.1.0 react-dom: 19.1.0(react@19.1.0) - styled-jsx: 5.1.6(@babel/core@7.29.0)(react@19.1.0) + styled-jsx: 5.1.6(@babel/core@8.0.1)(react@19.1.0) optionalDependencies: - '@next/swc-darwin-arm64': 16.1.6 - '@next/swc-darwin-x64': 16.1.6 - '@next/swc-linux-arm64-gnu': 16.1.6 - '@next/swc-linux-arm64-musl': 16.1.6 - '@next/swc-linux-x64-gnu': 16.1.6 - '@next/swc-linux-x64-musl': 16.1.6 - '@next/swc-win32-arm64-msvc': 16.1.6 - '@next/swc-win32-x64-msvc': 16.1.6 + '@next/swc-darwin-arm64': 16.2.9 + '@next/swc-darwin-x64': 16.2.9 + '@next/swc-linux-arm64-gnu': 16.2.9 + '@next/swc-linux-arm64-musl': 16.2.9 + '@next/swc-linux-x64-gnu': 16.2.9 + '@next/swc-linux-x64-musl': 16.2.9 + '@next/swc-win32-arm64-msvc': 16.2.9 + '@next/swc-win32-x64-msvc': 16.2.9 sharp: 0.34.5 transitivePeerDependencies: - '@babel/core' @@ -4753,6 +4771,8 @@ snapshots: nwsapi@2.2.23: {} + obug@2.1.3: {} + ogl@1.0.11: {} once@1.4.0: @@ -4805,9 +4825,7 @@ snapshots: picocolors@1.1.1: {} - picomatch@2.3.1: {} - - picomatch@4.0.3: {} + picomatch@4.0.4: {} pirates@4.0.7: {} @@ -4815,15 +4833,9 @@ snapshots: dependencies: find-up: 4.1.0 - postcss@8.4.31: + postcss@8.5.15: dependencies: - nanoid: 3.3.11 - picocolors: 1.1.1 - source-map-js: 1.2.1 - - postcss@8.5.6: - dependencies: - nanoid: 3.3.11 + nanoid: 3.3.15 picocolors: 1.1.1 source-map-js: 1.2.1 @@ -4898,8 +4910,6 @@ snapshots: scheduler@0.27.0: {} - semver@6.3.1: {} - semver@7.7.4: {} sharp@0.34.5: @@ -4955,16 +4965,14 @@ snapshots: source-map@0.6.1: {} - sprintf-js@1.0.3: {} - stack-utils@2.0.6: dependencies: escape-string-regexp: 2.0.0 - stats-gl@2.4.2(@types/three@0.183.1)(three@0.183.2): + stats-gl@2.4.2(@types/three@0.182.0)(three@0.182.0): dependencies: - '@types/three': 0.183.1 - three: 0.183.2 + '@types/three': 0.182.0 + three: 0.182.0 stats.js@0.17.0: {} @@ -5003,12 +5011,12 @@ snapshots: strip-json-comments@3.1.1: {} - styled-jsx@5.1.6(@babel/core@7.29.0)(react@19.1.0): + styled-jsx@5.1.6(@babel/core@8.0.1)(react@19.1.0): dependencies: client-only: 0.0.1 react: 19.1.0 optionalDependencies: - '@babel/core': 7.29.0 + '@babel/core': 8.0.1 supports-color@7.2.0: dependencies: @@ -5052,11 +5060,11 @@ snapshots: glob: 7.2.3 minimatch: 3.1.5 - three-mesh-bvh@0.8.3(three@0.183.2): + three-mesh-bvh@0.8.3(three@0.182.0): dependencies: - three: 0.183.2 + three: 0.182.0 - three-stdlib@2.36.1(three@0.183.2): + three-stdlib@2.36.1(three@0.182.0): dependencies: '@types/draco3d': 1.4.10 '@types/offscreencanvas': 2019.7.3 @@ -5064,9 +5072,9 @@ snapshots: draco3d: 1.5.7 fflate: 0.6.10 potpack: 1.0.2 - three: 0.183.2 + three: 0.182.0 - three@0.183.2: {} + three@0.182.0: {} tldts-core@6.1.86: {} @@ -5084,25 +5092,25 @@ snapshots: dependencies: punycode: 2.3.1 - troika-three-text@0.52.4(three@0.183.2): + troika-three-text@0.52.4(three@0.182.0): dependencies: bidi-js: 1.0.3 - three: 0.183.2 - troika-three-utils: 0.52.4(three@0.183.2) + three: 0.182.0 + troika-three-utils: 0.52.4(three@0.182.0) troika-worker-utils: 0.52.0 webgl-sdf-generator: 1.1.1 - troika-three-utils@0.52.4(three@0.183.2): + troika-three-utils@0.52.4(three@0.182.0): dependencies: - three: 0.183.2 + three: 0.182.0 troika-worker-utils@0.52.0: {} - ts-jest@29.4.6(@babel/core@7.29.0)(@jest/transform@30.3.0)(@jest/types@30.3.0)(babel-jest@30.3.0(@babel/core@7.29.0))(jest-util@30.3.0)(jest@30.3.0(@types/node@20.19.11)(ts-node@10.9.2(@types/node@20.19.11)(typescript@5.9.2)))(typescript@5.9.2): + ts-jest@29.4.6(@babel/core@8.0.1)(@jest/transform@30.3.0)(@jest/types@30.3.0)(babel-jest@30.3.0(@babel/core@8.0.1))(jest-util@30.3.0)(jest@30.3.0(@types/node@20.19.11)(ts-node@10.9.2(@types/node@20.19.11)(typescript@5.9.2)))(typescript@5.9.2): dependencies: bs-logger: 0.2.6 fast-json-stable-stringify: 2.1.0 - handlebars: 4.7.8 + handlebars: 4.7.9 jest: 30.3.0(@types/node@20.19.11)(ts-node@10.9.2(@types/node@20.19.11)(typescript@5.9.2)) json5: 2.2.3 lodash.memoize: 4.1.2 @@ -5112,10 +5120,10 @@ snapshots: typescript: 5.9.2 yargs-parser: 21.1.1 optionalDependencies: - '@babel/core': 7.29.0 + '@babel/core': 8.0.1 '@jest/transform': 30.3.0 '@jest/types': 30.3.0 - babel-jest: 30.3.0(@babel/core@7.29.0) + babel-jest: 30.3.0(@babel/core@8.0.1) jest-util: 30.3.0 ts-node@10.9.2(@types/node@20.19.11)(typescript@5.9.2): @@ -5255,7 +5263,7 @@ snapshots: imurmurhash: 0.1.4 signal-exit: 4.1.0 - ws@8.19.0: {} + ws@8.21.0: {} xml-name-validator@5.0.0: {} @@ -5263,8 +5271,6 @@ snapshots: y18n@5.0.8: {} - yallist@3.1.1: {} - yallist@5.0.0: {} yargs-parser@21.1.1: {} diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 087be12..2cf77b1 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -1,15 +1,39 @@ packages: - . +allowBuilds: + '@tailwindcss/oxide': true + sharp: true + unrs-resolver: true + onlyBuiltDependencies: - '@tailwindcss/oxide' - sharp overrides: + '@babel/core@<=7.29.0': '>=7.29.6' + brace-expansion@<1.1.13: '>=1.1.13' + brace-expansion@>=2.0.0 <2.0.3: '>=2.0.3' + handlebars@>=4.0.0 <4.7.9: '>=4.7.9' + handlebars@>=4.0.0 <=4.7.8: '>=4.7.9' + handlebars@>=4.6.0 <=4.7.8: '>=4.7.9' + js-yaml@<=4.1.1: '>=4.2.0' next@>=10.0.0 <15.5.10: '>=15.5.10' next@>=15.5.1-canary.0 <15.5.10: '>=15.5.10' + next@>=16.0.0 <16.2.5: '>=16.2.5' + next@>=16.0.0 <16.2.6: '>=16.2.6' + next@>=16.0.0-beta.0 <16.1.7: '>=16.1.7' + next@>=16.0.0-beta.0 <16.2.3: '>=16.2.3' + next@>=16.0.1 <16.1.7: '>=16.1.7' + picomatch@<2.3.2: '>=2.3.2' + picomatch@>=4.0.0 <4.0.4: '>=4.0.4' + postcss@<8.5.10: '>=8.5.10' tar@<7.5.7: '>=7.5.7' tar@<7.5.8: '>=7.5.8' + tar@<=7.5.10: '>=7.5.11' + tar@<=7.5.15: '>=7.5.16' tar@<=7.5.2: '>=7.5.3' tar@<=7.5.3: '>=7.5.4' tar@<=7.5.9: '>=7.5.10' + ws@>=8.0.0 <8.20.1: '>=8.20.1' + ws@>=8.0.0 <8.21.0: '>=8.21.0' diff --git a/public/edev.jpeg b/public/edev.jpeg deleted file mode 100644 index cdb3109..0000000 Binary files a/public/edev.jpeg and /dev/null differ diff --git a/public/edev.jpg b/public/edev.jpg new file mode 100644 index 0000000..9679467 Binary files /dev/null and b/public/edev.jpg differ diff --git a/public/portfolio-data.json b/public/portfolio-data.json index f12c065..c4ff923 100644 --- a/public/portfolio-data.json +++ b/public/portfolio-data.json @@ -4,7 +4,7 @@ "title": "Desenvolvedor Full-Stack", "location": "Garuva, Santa Catarina, Brasil", "email": "evandrofonsecajunior@gmail.com", - "phone": "+55 47 99274-2365", + "phone": "Disponivel pelo botao de WhatsApp", "linkedin": "https://www.linkedin.com/in/evandro-fonseca-junior-0969aa1b0/", "github": "https://github.com/evandrodevbr", "summary": "Desenvolvedor Full-Stack com 3+ anos de experiência, especializado em soluções web modernas e inovadoras. Formado em Engenharia da Computação, com conhecimentos sólidos em computação em nuvem e inteligência artificial. Busco constantemente novos desafios onde possa aplicar minha paixão por tecnologia e resolver problemas de forma criativa." @@ -367,8 +367,8 @@ }, "contact": { "email": "evandrofonsecajunior@gmail.com", - "phone": "+55 47 99274-2365", - "whatsapp": "+55 47 99274-2365", + "phone": "Disponivel pelo botao de WhatsApp", + "whatsapp": "Disponivel pelo botao de WhatsApp", "linkedin": "https://www.linkedin.com/in/evandro-fonseca-junior-0969aa1b0/", "github": "https://github.com/evandrodevbr", "location": "Garuva, Santa Catarina, Brasil"