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:
1 parent
0920370f30
commit
b791b7de9e
94 files changed
+2621
-2889
No files matched your search
+61
-2
@@ -1,6 +1,65 @@
|
||||
import "@testing-library/jest-dom";
|
||||
import "./__mocks__/resizeObserver";
|
||||
|
||||
// Mock IntersectionObserver if not available in JSDOM
|
||||
if (typeof global.IntersectionObserver === "undefined") {
|
||||
class IntersectionObserver {
|
||||
root: Element | Document | null = null;
|
||||
rootMargin: string = "";
|
||||
thresholds: ReadonlyArray<number> = [];
|
||||
private callback: IntersectionObserverCallback;
|
||||
private elements: Set<Element> = new Set();
|
||||
|
||||
constructor(
|
||||
callback: IntersectionObserverCallback,
|
||||
options?: IntersectionObserverInit,
|
||||
) {
|
||||
this.callback = callback;
|
||||
this.root = options?.root || null;
|
||||
this.rootMargin = options?.rootMargin || "";
|
||||
const threshold = options?.threshold ?? 0;
|
||||
this.thresholds = Array.isArray(threshold) ? threshold : [threshold];
|
||||
}
|
||||
|
||||
observe(target: Element) {
|
||||
this.elements.add(target);
|
||||
setTimeout(() => {
|
||||
this.callback(
|
||||
[
|
||||
{
|
||||
target,
|
||||
isIntersecting: true,
|
||||
intersectionRatio: 1,
|
||||
boundingClientRect: target.getBoundingClientRect(),
|
||||
intersectionRect: target.getBoundingClientRect(),
|
||||
rootBounds:
|
||||
this.root instanceof Element
|
||||
? this.root.getBoundingClientRect()
|
||||
: null,
|
||||
time: Date.now(),
|
||||
} as IntersectionObserverEntry,
|
||||
],
|
||||
this,
|
||||
);
|
||||
}, 0);
|
||||
}
|
||||
|
||||
unobserve(target: Element) {
|
||||
this.elements.delete(target);
|
||||
}
|
||||
|
||||
disconnect() {
|
||||
this.elements.clear();
|
||||
}
|
||||
|
||||
takeRecords(): IntersectionObserverEntry[] {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
global.IntersectionObserver =
|
||||
IntersectionObserver as typeof global.IntersectionObserver;
|
||||
}
|
||||
|
||||
// Mock PointerEvent if not available in JSDOM
|
||||
if (typeof global.PointerEvent === "undefined") {
|
||||
class PointerEvent extends MouseEvent {
|
||||
@@ -24,8 +83,8 @@ Object.defineProperty(window, "matchMedia", {
|
||||
matches: false,
|
||||
media: query,
|
||||
onchange: null,
|
||||
addListener: jest.fn(), // Deprecated
|
||||
removeListener: jest.fn(), // Deprecated
|
||||
addListener: jest.fn(),
|
||||
removeListener: jest.fn(),
|
||||
addEventListener: jest.fn(),
|
||||
removeEventListener: jest.fn(),
|
||||
dispatchEvent: jest.fn(),
|
||||
|
||||
Reference in new issue
Block a user