Fix
15
.codex/hooks.json
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"hooks": {
|
||||||
|
"PreToolUse": [
|
||||||
|
{
|
||||||
|
"matcher": "Skill",
|
||||||
|
"hooks": [
|
||||||
|
{
|
||||||
|
"type": "command",
|
||||||
|
"command": "'C:\\Users\\timo\\Documents\\qrmaster\\QR-master\\.codex\\hooks\\check-gstack.sh'"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
19
.codex/hooks/check-gstack.sh
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Block skill usage when gstack is not installed globally.
|
||||||
|
if [ ! -d "$HOME/.claude/skills/gstack/bin" ]; then
|
||||||
|
cat >&2 <<'MSG'
|
||||||
|
BLOCKED: gstack is not installed globally.
|
||||||
|
gstack is required for AI-assisted work in this repo.
|
||||||
|
|
||||||
|
Install it:
|
||||||
|
git clone --depth 1 https://github.com/garrytan/gstack.git ~/.claude/skills/gstack
|
||||||
|
cd ~/.claude/skills/gstack && ./setup --team
|
||||||
|
|
||||||
|
Then restart your AI coding tool.
|
||||||
|
MSG
|
||||||
|
echo '{"permissionDecision":"deny","message":"gstack is required but not installed. See stderr for install instructions."}'
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo '{}'
|
||||||
2
.gitignore
vendored
@@ -50,7 +50,7 @@ logs
|
|||||||
|
|
||||||
# project-specific
|
# project-specific
|
||||||
Leads/
|
Leads/
|
||||||
marketing/
|
/marketing/
|
||||||
output/
|
output/
|
||||||
remotion/
|
remotion/
|
||||||
|
|
||||||
|
|||||||
BIN
public/marketing/use-cases/business-card-qr-codes.png
Normal file
|
After Width: | Height: | Size: 2.0 MiB |
BIN
public/marketing/use-cases/coupon-qr-codes.png
Normal file
|
After Width: | Height: | Size: 1.5 MiB |
BIN
public/marketing/use-cases/event-qr-codes.png
Normal file
|
After Width: | Height: | Size: 1.6 MiB |
BIN
public/marketing/use-cases/feedback-qr-codes.png
Normal file
|
After Width: | Height: | Size: 1.8 MiB |
BIN
public/marketing/use-cases/flyer-qr-codes.png
Normal file
|
After Width: | Height: | Size: 2.0 MiB |
BIN
public/marketing/use-cases/hotel-welcome-qr-codes.png
Normal file
|
After Width: | Height: | Size: 2.0 MiB |
BIN
public/marketing/use-cases/packaging-qr-codes.png
Normal file
|
After Width: | Height: | Size: 2.4 MiB |
BIN
public/marketing/use-cases/payment-qr-codes.png
Normal file
|
After Width: | Height: | Size: 2.0 MiB |
BIN
public/marketing/use-cases/qr-codes-for-barbershops.png
Normal file
|
After Width: | Height: | Size: 1.7 MiB |
BIN
public/marketing/use-cases/qr-codes-for-hotel.png
Normal file
|
After Width: | Height: | Size: 2.0 MiB |
BIN
public/marketing/use-cases/qr-codes-for-review-collection.png
Normal file
|
After Width: | Height: | Size: 1.8 MiB |
BIN
public/marketing/use-cases/real-estate-sign-qr-codes.png
Normal file
|
After Width: | Height: | Size: 2.2 MiB |
BIN
public/marketing/use-cases/restaurant-menu-qr-codes.png
Normal file
|
After Width: | Height: | Size: 2.6 MiB |
BIN
public/marketing/use-cases/salon-barbershop-qr-codes.png
Normal file
|
After Width: | Height: | Size: 1.6 MiB |
50
src/components/marketing/HeroSpotlight.tsx
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import React, { useState } from "react";
|
||||||
|
|
||||||
|
export function HeroSpotlight({ children }: { children: React.ReactNode }) {
|
||||||
|
const [coords, setCoords] = useState({ x: 0, y: 0 });
|
||||||
|
const [isHovered, setIsHovered] = useState(false);
|
||||||
|
|
||||||
|
const handleMouseMove = (e: React.MouseEvent<HTMLDivElement>) => {
|
||||||
|
const { left, top, width, height } = e.currentTarget.getBoundingClientRect();
|
||||||
|
const x = ((e.clientX - left) / width) * 100;
|
||||||
|
const y = ((e.clientY - top) / height) * 100;
|
||||||
|
setCoords({ x, y });
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<section
|
||||||
|
className="relative overflow-hidden bg-white border-b border-slate-150 pb-20 pt-16"
|
||||||
|
onMouseMove={handleMouseMove}
|
||||||
|
onMouseEnter={() => setIsHovered(true)}
|
||||||
|
onMouseLeave={() => setIsHovered(false)}
|
||||||
|
>
|
||||||
|
{/* Grid Container - Handles the fade-out mask at the bottom */}
|
||||||
|
<div
|
||||||
|
className="absolute inset-0 pointer-events-none"
|
||||||
|
style={{
|
||||||
|
maskImage: 'radial-gradient(ellipse 60% 50% at 50% 0%, black 70%, transparent 100%)',
|
||||||
|
WebkitMaskImage: 'radial-gradient(ellipse 60% 50% at 50% 0%, black 70%, transparent 100%)',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{/* Base Grid - light gray lines */}
|
||||||
|
<div className="absolute inset-0 bg-[linear-gradient(to_right,#cbd5e1_1px,transparent_1px),linear-gradient(to_bottom,#cbd5e1_1px,transparent_1px)] bg-[size:3.5rem_3.5rem] opacity-[0.35]" />
|
||||||
|
|
||||||
|
{/* Active Grid - blue lines, only visible within 120px circle around cursor */}
|
||||||
|
<div
|
||||||
|
className="absolute inset-0 bg-[linear-gradient(to_right,#3b82f6_1.5px,transparent_1.5px),linear-gradient(to_bottom,#3b82f6_1.5px,transparent_1.5px)] bg-[size:3.5rem_3.5rem] transition-opacity duration-300"
|
||||||
|
style={{
|
||||||
|
opacity: isHovered ? 1 : 0,
|
||||||
|
maskImage: `radial-gradient(120px circle at ${coords.x}% ${coords.y}%, black, transparent)`,
|
||||||
|
WebkitMaskImage: `radial-gradient(120px circle at ${coords.x}% ${coords.y}%, black, transparent)`,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="relative z-10">
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
}
|
||||||