front-end-sample / CyberTicket.jsx
algorembrant's picture
Upload 2 files
d2ac159 verified
import { useState, useEffect, useRef } from "react";
const FONT_URL = "https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@400;500;600;700&display=swap";
const TERMINAL_LINES = [
"INIT SEQUENCE 0x4F2A...",
"AUTH TOKEN: β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ",
"SCAN FREQ 847.221 MHz",
"SECTOR [CLASSIFIED]",
"NODE_ID NX-9912-DELTA",
"TIMESTAMP 2025.03.06",
"CLEARANCE LEVEL-OMEGA",
"STATUS AUTHORIZED",
"PAYLOAD ENCRYPTED",
"UPLINK ACTIVE",
"SIGNAL β–ˆβ–ˆβ–ˆβ–ˆβ–‘β–‘β–‘β–‘ 61%",
"VERIFY SHA256:c0d3f",
"MATRIX 7Γ—7 LATTICE",
"HASH 0xDEADBEEF",
"PING 12.4ms OK",
"TRACE DISABLED",
"VAULT SEALED",
"PROTOCOL OMEGA-9",
"ENTROPY HIGH",
"FRAME #00441",
"BUFFER FLUSHED",
"CIPHER AES-256",
"RELAY PROXIED",
"ECHO SILENT",
"LOCK ENGAGED",
"GRID REF 44.0N 76.2W",
"KERNEL 3.14.159",
"DAEMON RUNNING",
"WATCHDOG ARMED",
"ROUTE OBFUSCATED",
];
// ── Noise / grain canvas ──────────────────────────────────────────────────────
function GrainOverlay({ strength = 0.18 }) {
const canvasRef = useRef();
const tick = useRef(0);
useEffect(() => {
const canvas = canvasRef.current;
if (!canvas) return;
const ctx = canvas.getContext("2d");
let raf;
const draw = () => {
tick.current++;
if (tick.current % 2 !== 0) { raf = requestAnimationFrame(draw); return; }
const { width, height } = canvas;
const img = ctx.createImageData(width, height);
const d = img.data;
for (let i = 0; i < d.length; i += 4) {
const px = i / 4;
const x = px % width;
const y = Math.floor(px / width);
const cx = x / width - 0.5, cy = y / height - 0.5;
const dist = Math.sqrt(cx * cx + cy * cy);
const vignette = Math.min(1, dist * 2.2);
const noise = (Math.random() - 0.5) * 255 * (strength + vignette * 0.18);
d[i] = d[i + 1] = d[i + 2] = 128 + noise;
d[i + 3] = Math.floor(18 + vignette * 30);
}
ctx.putImageData(img, 0, 0);
raf = requestAnimationFrame(draw);
};
const resize = () => {
canvas.width = canvas.offsetWidth;
canvas.height = canvas.offsetHeight;
};
resize();
draw();
window.addEventListener("resize", resize);
return () => { cancelAnimationFrame(raf); window.removeEventListener("resize", resize); };
}, [strength]);
return (
<canvas
ref={canvasRef}
style={{
position: "absolute", inset: 0, width: "100%", height: "100%",
pointerEvents: "none", mixBlendMode: "overlay", zIndex: 10,
}}
/>
);
}
// ── SVG geometric overlay ─────────────────────────────────────────────────────
function GeometricOverlay() {
return (
<svg
viewBox="0 0 480 860"
style={{
position: "absolute", inset: 0, width: "100%", height: "100%",
pointerEvents: "none", zIndex: 5, opacity: 0.08,
}}
preserveAspectRatio="none"
>
<polygon points="0,0 72,0 0,72" fill="none" stroke="white" strokeWidth="0.8" />
<polygon points="480,0 408,0 480,72" fill="none" stroke="white" strokeWidth="0.8" />
<polygon points="0,860 72,860 0,788" fill="none" stroke="white" strokeWidth="0.8" />
<polygon points="480,860 408,860 480,788" fill="none" stroke="white" strokeWidth="0.8" />
{[0.2, 0.4, 0.6, 0.8].map((t) => (
<line key={t} x1={480 * t} y1="0" x2={480 * t} y2="860" stroke="white" strokeWidth="0.4" />
))}
{[0.25, 0.5, 0.75].map((t) => (
<line key={t} x1="0" y1={860 * t} x2="480" y2={860 * t} stroke="white" strokeWidth="0.4" />
))}
<line x1="240" y1="430" x2="0" y2="0" stroke="white" strokeWidth="0.3" />
<line x1="240" y1="430" x2="480" y2="0" stroke="white" strokeWidth="0.3" />
<line x1="240" y1="430" x2="0" y2="860" stroke="white" strokeWidth="0.3" />
<line x1="240" y1="430" x2="480" y2="860" stroke="white" strokeWidth="0.3" />
<circle cx="240" cy="430" r="200" fill="none" stroke="white" strokeWidth="0.5" />
</svg>
);
}
// ── Terminal code cascade ─────────────────────────────────────────────────────
function TerminalMask() {
const [offset, setOffset] = useState(0);
const [glitch, setGlitch] = useState({ row: -1, px: 0 });
useEffect(() => {
const iv = setInterval(() => {
setOffset((o) => (o + 1) % TERMINAL_LINES.length);
}, 120);
const gv = setInterval(() => {
setGlitch({ row: Math.floor(Math.random() * 18), px: (Math.random() - 0.5) * 8 });
setTimeout(() => setGlitch({ row: -1, px: 0 }), 80);
}, 900);
return () => { clearInterval(iv); clearInterval(gv); };
}, []);
const visible = Array.from({ length: 20 }, (_, i) =>
TERMINAL_LINES[(offset + i) % TERMINAL_LINES.length]
);
return (
<div style={{
width: "100%", height: "100%",
background: "#000",
borderRadius: "50%",
overflow: "hidden",
display: "flex",
flexDirection: "column",
justifyContent: "center",
padding: "18px 14px",
gap: 0,
position: "relative",
}}>
<div style={{
position: "absolute", inset: 0, borderRadius: "50%",
background: "radial-gradient(ellipse at center, transparent 30%, rgba(0,0,0,0.72) 100%)",
zIndex: 2, pointerEvents: "none",
}} />
{visible.map((line, i) => (
<div key={i} style={{
fontFamily: "'IBM Plex Mono', monospace",
fontSize: "9.5px",
fontWeight: i === 0 ? 700 : 400,
letterSpacing: "0.08em",
color: i === 0 ? "#FFFFFF" : i < 3 ? "#D8D8D8" : "#888",
lineHeight: "1.55",
opacity: i === 0 ? 1 : Math.max(0.18, 1 - i * 0.045),
transform: glitch.row === i ? `translateX(${glitch.px}px)` : "none",
whiteSpace: "nowrap",
overflow: "hidden",
textOverflow: "ellipsis",
transition: "transform 0.04s",
position: "relative",
zIndex: 3,
}}>
{i === 0 ? "β–Ά " : " "}{line}
</div>
))}
</div>
);
}
// ── Barcode SVG ───────────────────────────────────────────────────────────────
function Barcode() {
const bars = Array.from({ length: 60 }, (_, i) => {
const w = [1, 1, 2, 1, 3, 1, 2, 1, 1, 2][i % 10];
return { w, gap: [2, 1, 2, 3, 1, 2, 1, 2, 3, 1][i % 10] };
});
return (
<svg width="160" height="36" viewBox="0 0 160 36">
{bars.reduce((acc, bar, i) => {
const x = acc.x;
acc.elements.push(
<rect key={i} x={x} y={2} width={bar.w} height={28} fill="white" />
);
acc.x += bar.w + bar.gap;
return acc;
}, { x: 2, elements: [] }).elements}
</svg>
);
}
// ── TICKET SHELL with authentic physical edges ────────────────────────────────
// Renders the outer ticket shape as a single SVG clip-path with:
// β€’ Scalloped top & bottom edges (zigzag perforations)
// β€’ Semicircle punch-outs on both sides at the tear line
// β€’ Punched corner rounds
// β€’ A perforated dashed-border inner stroke
function TicketShell({ children, stubHeight = 120 }) {
const W = 460; // SVG viewBox width
const H = 820; // SVG viewBox height
const R = 14; // corner radius
const PR = 9; // top/bottom diamond tooth size
const SR = 11; // side diamond notch size (tear notch)
const tearY = H - stubHeight; // Y where tear line sits
// Diamond zigzag teeth left→right: dir=-1 teeth UP, dir=+1 teeth DOWN
const zigzagLR = (x0, x1, y, dir) => {
const count = Math.floor((x1 - x0) / (PR * 2.2));
const step = (x1 - x0) / count;
let d = "";
for (let i = 0; i < count; i++) {
const mx = x0 + i * step + step / 2;
const ex = x0 + (i + 1) * step;
d += ` L ${mx} ${y + dir * PR} L ${ex} ${y}`;
}
return d;
};
// Diamond zigzag teeth right→left (bottom edge return)
const zigzagRL = (x0, x1, y, dir) => {
const count = Math.floor((x1 - x0) / (PR * 2.2));
const step = (x1 - x0) / count;
let d = "";
for (let i = count - 1; i >= 0; i--) {
const mx = x0 + i * step + step / 2;
const ex = x0 + i * step;
d += ` L ${mx} ${y + dir * PR} L ${ex} ${y}`;
}
return d;
};
const pad = 2;
const x0 = pad + R, x1 = W - pad - R;
const y0 = pad, y1 = H - pad;
// Right side semicircle at tearY (bites into card from right)
// Left side semicircle at tearY (bites into card from left)
const rightX = W - pad;
const leftX = pad;
// Build SVG path for clip
const path = [
`M ${x0} ${y0}`,
// top diamond zigzag edge β†’
zigzagLR(x0, x1, y0, -1),
// top-right corner
`L ${W - pad - R} ${y0}`,
`Q ${W - pad} ${y0} ${W - pad} ${y0 + R}`,
// right side down to tear notch
`L ${rightX} ${tearY - SR}`,
// semi-diamond bite: two lines forming a V-point inward
`L ${rightX - SR} ${tearY}`,
`L ${rightX} ${tearY + SR}`,
// right side continues down
`L ${rightX} ${y1 - R}`,
// bottom-right corner
`Q ${rightX} ${y1} ${x1} ${y1}`,
// bottom diamond zigzag edge ←
`L ${x1} ${y1}`,
zigzagRL(x0, x1, y1, 1),
// bottom-left corner
`L ${x0} ${y1}`,
`Q ${leftX} ${y1} ${leftX} ${y1 - R}`,
// left side up from bottom to tear notch
`L ${leftX} ${tearY + SR}`,
// semi-diamond bite: two lines forming a V-point inward from left
`L ${leftX + SR} ${tearY}`,
`L ${leftX} ${tearY - SR}`,
// left side up to top
`L ${leftX} ${y0 + R}`,
// top-left corner
`Q ${leftX} ${y0} ${x0} ${y0}`,
`Z`,
].join(" ");
const clipId = "ticket-clip";
const innerId = "ticket-inner";
return (
<div style={{ position: "relative", width: "100%", maxWidth: 460 }}>
{/* SVG defs for clip path */}
<svg width="0" height="0" style={{ position: "absolute" }}>
<defs>
<clipPath id={clipId} clipPathUnits="userSpaceOnUse">
<path d={path} />
</clipPath>
</defs>
</svg>
{/* Outer shadow/glow layer */}
<div style={{
position: "absolute",
inset: -2,
borderRadius: 18,
boxShadow: "0 40px 100px rgba(0,0,0,0.95), 0 0 0 1px #1a1a1a",
pointerEvents: "none",
zIndex: 0,
}} />
{/* The actual ticket content clipped to shape */}
<div
style={{
position: "relative",
width: "100%",
clipPath: `url(#${clipId})`,
// Fallback border for browsers without clipPath
}}
>
{/* SVG border drawn on top of content */}
<svg
viewBox={`0 0 ${W} ${H}`}
width="100%"
style={{
position: "absolute", inset: 0, width: "100%", height: "100%",
pointerEvents: "none", zIndex: 50,
}}
preserveAspectRatio="none"
>
{/* Outer border */}
<path
d={path}
fill="none"
stroke="#2e2e2e"
strokeWidth="1.2"
/>
{/* Inner inset border */}
<path
d={path}
fill="none"
stroke="#1e1e1e"
strokeWidth="3"
strokeDasharray="4 3"
opacity="0.5"
/>
{/* Tear-line dashed rule */}
<line
x1={pad + SR + 4} y1={tearY}
x2={W - pad - SR - 4} y2={tearY}
stroke="#2a2a2a"
strokeWidth="1"
strokeDasharray="5 4"
/>
{/* Tear scissors icon hint */}
<text x={W / 2 - 10} y={tearY - 5} fill="#333" fontSize="9" fontFamily="monospace" textAnchor="middle">βœ‚ TEAR</text>
{/* Corner punch holes β€” diamond arrowhead shapes */}
{[
[pad + 28, pad + 28],
[W - pad - 28, pad + 28],
[pad + 28, H - pad - 28],
[W - pad - 28, H - pad - 28],
].map(([cx, cy], i) => {
const ro = 9;
const ri = 4.5;
return (
<g key={i}>
<polygon
points={`${cx},${cy - ro} ${cx + ro},${cy} ${cx},${cy + ro} ${cx - ro},${cy}`}
fill="#0A0A0A" stroke="#2e2e2e" strokeWidth="1"
/>
<polygon
points={`${cx},${cy - ri} ${cx + ri},${cy} ${cx},${cy + ri} ${cx - ri},${cy}`}
fill="none" stroke="#252525" strokeWidth="0.8"
/>
</g>
);
})}
{/* Perforation diamond dots along tear line */}
{Array.from({ length: 18 }, (_, i) => {
const spacing = (W - pad * 2 - SR * 2 - 20) / 17;
const x = pad + SR + 10 + i * spacing;
const r = 2.2;
return (
<polygon
key={i}
points={`${x},${tearY - r} ${x + r},${tearY} ${x},${tearY + r} ${x - r},${tearY}`}
fill="#252525"
/>
);
})}
</svg>
{children}
</div>
</div>
);
}
// ── Main component ────────────────────────────────────────────────────────────
export default function CyberTicket() {
const [pulse, setPulse] = useState(false);
useEffect(() => {
if (!document.getElementById("ibm-plex-mono")) {
const link = document.createElement("link");
link.id = "ibm-plex-mono";
link.rel = "stylesheet";
link.href = FONT_URL;
document.head.appendChild(link);
}
const iv = setInterval(() => setPulse((p) => !p), 1400);
return () => clearInterval(iv);
}, []);
const STUB_H = 130; // px β€” must roughly match SVG tearY portion
return (
<div
style={{
minHeight: "100vh",
background: "#060606",
display: "flex",
alignItems: "center",
justifyContent: "center",
padding: "60px 16px",
fontFamily: "'IBM Plex Mono', monospace",
position: "relative",
overflow: "hidden",
}}
>
{/* ambient radial glow */}
<div style={{
position: "absolute",
width: 600, height: 600,
background: "radial-gradient(circle, rgba(255,255,255,0.015) 0%, transparent 70%)",
left: "50%", top: "50%",
transform: "translate(-50%,-50%)",
pointerEvents: "none",
}} />
<GrainOverlay strength={0.13} />
<TicketShell stubHeight={130}>
{/* ── background fill */}
<div style={{
background: "#0A0A0A",
minHeight: 820,
position: "relative",
overflow: "hidden",
}}>
<GrainOverlay strength={0.2} />
<GeometricOverlay />
{/* 1 ── TOP HEADER BAR */}
<div style={{
background: "#0f0f0f",
borderBottom: "1px solid #1e1e1e",
padding: "16px 28px",
display: "flex",
alignItems: "center",
justifyContent: "space-between",
position: "relative",
zIndex: 20,
marginTop: 14, // clear top scallop
}}>
<div>
<div style={{
fontSize: 9, fontWeight: 700, letterSpacing: "0.32em",
color: "#FFF", textTransform: "uppercase",
}}>NEXUS PROTOCOL</div>
<div style={{ fontSize: 7.5, color: "#555", letterSpacing: "0.18em", marginTop: 1 }}>
CLASSIFIED ACCESS DOCUMENT
</div>
</div>
<div style={{ textAlign: "right" }}>
<div style={{ fontSize: 9, color: "#555", letterSpacing: "0.1em" }}>Ξ©-09</div>
<div style={{ fontSize: 7, color: "#333", marginTop: 1 }}>Β© 2025</div>
</div>
</div>
{/* 2 ── BRANDING BLOCK */}
<div style={{ padding: "22px 32px 14px", position: "relative", zIndex: 20 }}>
<div style={{ display: "flex", alignItems: "center", gap: 12, marginBottom: 6 }}>
<svg width="28" height="28" viewBox="0 0 28 28" fill="none">
<circle cx="14" cy="14" r="13" stroke="white" strokeWidth="1" />
<path d="M4 14 Q7 8 10 14 Q13 20 16 14 Q19 8 22 14 Q24 18 24 14"
stroke="white" strokeWidth="1.2" fill="none" strokeLinecap="round" />
<circle cx="14" cy="14" r="2" fill="white" />
</svg>
<div>
<div style={{
fontSize: 22, fontWeight: 700, letterSpacing: "0.18em",
color: "#FFF", textTransform: "uppercase", lineHeight: 1,
}}>NEURAL</div>
<div style={{
fontSize: 22, fontWeight: 400, letterSpacing: "0.34em",
color: "#666", textTransform: "uppercase", lineHeight: 1, marginTop: 2,
}}>ACCESS</div>
</div>
</div>
<div style={{
fontSize: 7.5, color: "#383838", letterSpacing: "0.22em", textTransform: "uppercase",
}}>
HIGH-CLEARANCE ADMISSION β€” SERIES 9 β€” OMEGA TIER
</div>
</div>
{/* 3 ── TERMINAL CIRCLE */}
<div style={{
padding: "0 28px", position: "relative", zIndex: 20,
display: "flex", justifyContent: "center",
}}>
<div style={{
width: 250, height: 250,
borderRadius: "50%",
border: "1px solid #2a2a2a",
overflow: "hidden",
position: "relative",
boxShadow: "0 0 0 6px #0e0e0e, 0 0 0 7px #1e1e1e",
}}>
<TerminalMask />
<GrainOverlay strength={0.26} />
</div>
</div>
{/* scan status pill */}
<div style={{
display: "flex", justifyContent: "center", marginTop: 12,
position: "relative", zIndex: 20,
}}>
<div style={{
display: "flex", alignItems: "center", gap: 6,
border: "1px solid #222", borderRadius: 999,
padding: "4px 14px",
background: "#0d0d0d",
}}>
<div style={{
width: 5, height: 5, borderRadius: "50%",
background: pulse ? "#FFF" : "#333",
transition: "background 0.4s",
}} />
<span style={{ fontSize: 7.5, letterSpacing: "0.2em", color: "#666", textTransform: "uppercase" }}>
SIGNAL {pulse ? "ACTIVE" : "PINGING"}
</span>
</div>
</div>
{/* 4 ── INFO GRID */}
<div style={{
padding: "24px 32px 0",
position: "relative", zIndex: 20,
display: "grid", gridTemplateColumns: "1fr 1fr", gap: "16px 12px",
}}>
{[
{ label: "BEARER", value: "AGENT_NX-44" },
{ label: "ISSUED", value: "2025.03.06" },
{ label: "CLEARANCE", value: "OMEGA / Ξ©" },
{ label: "EXPIRES", value: "2025.12.31" },
{ label: "SECTOR", value: "DELTA-9" },
{ label: "NODE", value: "0xDEADΒ·BF9" },
].map(({ label, value }) => (
<div key={label}>
<div style={{
fontSize: 7, color: "#3a3a3a", letterSpacing: "0.28em",
textTransform: "uppercase", marginBottom: 3,
}}>{label}</div>
<div style={{
fontSize: 11, color: "#CDCDCD", letterSpacing: "0.1em",
fontWeight: 600,
}}>{value}</div>
</div>
))}
</div>
{/* subtle divider */}
<div style={{ padding: "22px 32px 0", position: "relative", zIndex: 20 }}>
<div style={{ display: "flex", gap: 6, alignItems: "center" }}>
{Array.from({ length: 3 }).map((_, i) => (
<div key={i} style={{ flex: 1, height: "1px", background: i === 1 ? "#222" : "#161616" }} />
))}
<svg width="10" height="10" viewBox="0 0 10 10" fill="none" style={{ opacity: 0.3 }}>
<polygon points="5,0 10,10 0,10" stroke="white" strokeWidth="1" fill="none" />
</svg>
{Array.from({ length: 3 }).map((_, i) => (
<div key={i} style={{ flex: 1, height: "1px", background: i === 1 ? "#222" : "#161616" }} />
))}
</div>
</div>
{/* ── STUB SECTION (below tear line) ── */}
{/* Spacer to push stub below the scalloped tear cutout */}
<div style={{ height: 36 }} />
<div style={{
padding: "4px 32px 32px",
position: "relative", zIndex: 20,
display: "flex", alignItems: "center", justifyContent: "space-between",
}}>
{/* Barcode */}
<div>
<Barcode />
<div style={{ fontSize: 7, color: "#2e2e2e", letterSpacing: "0.16em", marginTop: 3 }}>
NX-9912-DELTA-44-0x2F
</div>
</div>
{/* Stamp area */}
<div style={{ textAlign: "right" }}>
<div style={{ display: "flex", gap: 6, justifyContent: "flex-end", marginBottom: 6 }}>
{["β˜…", "β—ˆ", "β–²"].map((icon, i) => (
<span key={i} style={{
fontSize: i === 0 ? 11 : 9, color: "#3a3a3a", fontFamily: "monospace",
}}>{icon}</span>
))}
</div>
<div style={{
fontSize: 7, color: "#333", letterSpacing: "0.22em",
textTransform: "uppercase", lineHeight: 1.7,
}}>
ADMIT<br />ONE
</div>
</div>
</div>
{/* bottom vignette */}
<div style={{
position: "absolute", bottom: 0, left: 0, right: 0, height: 80,
background: "linear-gradient(to top, rgba(0,0,0,0.6), transparent)",
pointerEvents: "none", zIndex: 8,
}} />
</div>
</TicketShell>
{/* ambient page label */}
<div style={{
position: "absolute", bottom: 18, left: "50%", transform: "translateX(-50%)",
fontSize: 7.5, letterSpacing: "0.3em", color: "#222",
fontFamily: "'IBM Plex Mono', monospace", textTransform: "uppercase",
zIndex: 20, whiteSpace: "nowrap",
}}>
NEXUS PROTOCOL β€” DOCUMENT RENDER v9.1 β€” CLASSIFIED
</div>
</div>
);
}