feat: control room hero, SEO/AEO optimization and design system fixes

Hero: replace particle field with cobe globe (Capricorn view, multi-axis drag, reduced-motion safe), telemetry HUD with pointer parallax, particle text build effect, Space Grotesk display font. SEO: short titles with template, PT-BR accents, twitter cards on dynamic cases, escaped JSON-LD via lib/jsonld.ts, CSP report-only, 25 service FAQs answer-first. Design: fix TW4 layered reset killing spacing utilities, literal @theme tokens, mobile hero overflow, header overlap, empty case cards. Remove framer-motion (unified on motion/react), header version badge, phone number from hero CTA. Production build verified (24 static pages).
This commit is contained in:
Evandro Fonseca Junior committed 2026-08-27 10:55:21 -03:00
1 parent 0920370f30
commit b791b7de9e
94 files changed
+2621 -2889

No files matched your search

+51 -27
View File
@@ -1,7 +1,7 @@
"use client";
import { useRef, useState, type ReactNode } from "react";
import { motion } from "framer-motion";
import { motion } from "motion/react";
import { type MouseEvent, type ReactNode, useState } from "react";
interface MagneticButtonProps {
children: ReactNode;
@@ -34,14 +34,10 @@ export default function MagneticButton({
wrapperClassName = "",
"aria-label": ariaLabel,
}: MagneticButtonProps) {
const ref = useRef<HTMLDivElement>(null);
const [position, setPosition] = useState({ x: 0, y: 0 });
const handleMouse = (e: React.MouseEvent) => {
const el = ref.current;
if (!el) return;
const rect = el.getBoundingClientRect();
const handleMouse = (e: MouseEvent<HTMLElement>) => {
const rect = e.currentTarget.getBoundingClientRect();
const centerX = rect.left + rect.width / 2;
const centerY = rect.top + rect.height / 2;
@@ -55,31 +51,59 @@ export default function MagneticButton({
setPosition({ x: 0, y: 0 });
};
const Tag = as === "a" ? motion.a : as === "button" ? motion.button : as === "div" ? motion.div : as === "span" ? motion.span : motion.li;
const Tag =
as === "a"
? motion.a
: as === "button"
? motion.button
: as === "div"
? motion.div
: as === "span"
? motion.span
: motion.li;
const linkProps = as === "a" ? { href, target, rel, download } : {};
const buttonProps = as === "button" ? { type } : {};
return (
<div
ref={ref}
onMouseMove={handleMouse}
onMouseLeave={handleMouseLeave}
className={`inline-block ${wrapperClassName}`.trim()}
>
const motionProps = {
animate: {
x: position.x,
y: position.y,
},
transition: {
type: "spring" as const,
stiffness: 350,
damping: 15,
mass: 0.5,
},
};
if (as === "li") {
return (
<Tag
className={className}
className={`${className} ${wrapperClassName}`.trim()}
onMouseMove={handleMouse}
onMouseLeave={handleMouseLeave}
onClick={onClick}
data-cursor={dataCursor}
animate={{
x: position.x,
y: position.y,
}}
transition={{
type: "spring",
stiffness: 350,
damping: 15,
mass: 0.5,
}}
{...motionProps}
{...linkProps}
{...buttonProps}
aria-label={ariaLabel}
>
{children}
</Tag>
);
}
return (
<div className={`inline-block ${wrapperClassName}`.trim()}>
<Tag
className={className}
onMouseMove={handleMouse}
onMouseLeave={handleMouseLeave}
onClick={onClick}
data-cursor={dataCursor}
{...motionProps}
{...linkProps}
{...buttonProps}
aria-label={ariaLabel}