Upload 6 files
Browse files- .gitignore +31 -0
- LICENSE +21 -0
- README.md +40 -0
- image.png +0 -0
- prompt.md +45 -0
- sample.jsx +501 -0
.gitignore
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Dependencies
|
| 2 |
+
node_modules/
|
| 3 |
+
.npm
|
| 4 |
+
/.pnp
|
| 5 |
+
.pnp.js
|
| 6 |
+
|
| 7 |
+
# Testing
|
| 8 |
+
/coverage
|
| 9 |
+
|
| 10 |
+
# Production
|
| 11 |
+
/dist
|
| 12 |
+
/build
|
| 13 |
+
|
| 14 |
+
# Misc
|
| 15 |
+
.DS_Store
|
| 16 |
+
.env.local
|
| 17 |
+
.env.development.local
|
| 18 |
+
.env.test.local
|
| 19 |
+
.env.production.local
|
| 20 |
+
|
| 21 |
+
npm-debug.log*
|
| 22 |
+
yarn-debug.log*
|
| 23 |
+
yarn-error.log*
|
| 24 |
+
|
| 25 |
+
# IDEs
|
| 26 |
+
.vscode/
|
| 27 |
+
.idea/
|
| 28 |
+
|
| 29 |
+
# Repository specific
|
| 30 |
+
prompt.md
|
| 31 |
+
reponame_savelog.txt
|
LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
MIT License
|
| 2 |
+
|
| 3 |
+
Copyright (c) 2026 Rembrant Oyangoren Albeos
|
| 4 |
+
|
| 5 |
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
| 6 |
+
of this software and associated documentation files (the "Software"), to deal
|
| 7 |
+
in the Software without restriction, including without limitation the rights
|
| 8 |
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
| 9 |
+
copies of the Software, and to permit persons to whom the Software is
|
| 10 |
+
furnished to do so, subject to the following conditions:
|
| 11 |
+
|
| 12 |
+
The above copyright notice and this permission notice shall be included in all
|
| 13 |
+
copies or substantial portions of the Software.
|
| 14 |
+
|
| 15 |
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
| 16 |
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
| 17 |
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
| 18 |
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
| 19 |
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
| 20 |
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
| 21 |
+
SOFTWARE.
|
README.md
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
license: mit
|
| 3 |
+
sdk: react
|
| 4 |
+
tags:
|
| 5 |
+
- cyberpunk
|
| 6 |
+
- ui
|
| 7 |
+
- react
|
| 8 |
+
- canvas
|
| 9 |
+
- glassmorphism
|
| 10 |
+
title: Cyberpunk Nexus Protocol Ticket UI
|
| 11 |
+
pinned: true
|
| 12 |
+
---
|
| 13 |
+
|
| 14 |
+
# Cyberpunk Nexus Protocol Ticket UI
|
| 15 |
+
|
| 16 |
+
A premium React-based cyberpunk interface component designed with a "Nexus Protocol" aesthetic. This project demonstrates advanced front-end techniques including dynamic noise/grain rendering on canvas, SVG geometric overlays, and real-time terminal-style code cascades.
|
| 17 |
+
|
| 18 |
+

|
| 19 |
+
|
| 20 |
+
## Key Features
|
| 21 |
+
|
| 22 |
+
- **Dynamic Grain Canvas**: Real-time noise and vignette effects for a gritty, hardware-like feel.
|
| 23 |
+
- **Terminal Cascade**: Animated code-stream mask with periodic glitch effects.
|
| 24 |
+
- **Geometric Overlays**: Crisp, low-alpha SVG patterns and crosshairs providing depth.
|
| 25 |
+
- **Responsive "Ticket" Physics**: Perspective-warped card layout with inner glassmorphism.
|
| 26 |
+
- **Premium Typography**: Uses [IBM Plex Mono](https://fonts.google.com/specimen/IBM+Plex+Mono) for a high-clearance, technical look.
|
| 27 |
+
|
| 28 |
+
## Technical Stack
|
| 29 |
+
|
| 30 |
+
- **React**: Functional components and advanced hooks (`useRef`, `useEffect`).
|
| 31 |
+
- **Canvas API**: Custom low-level grain and noise generation.
|
| 32 |
+
- **SVG**: Complex vector overlays for geometric precision.
|
| 33 |
+
- **CSS-in-JS**: Dynamic styling for animations and layouts.
|
| 34 |
+
|
| 35 |
+
## Getting Started
|
| 36 |
+
|
| 37 |
+
1. **Dependencies**: This component is standalone but requires `ibm-plex-mono` font (loaded dynamically).
|
| 38 |
+
2. **Integration**: Simply import `CyberTicket` into your React application.
|
| 39 |
+
|
| 40 |
+
|
image.png
ADDED
|
prompt.md
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<command>
|
| 2 |
+
You are a world-class senior UI/UX designer specializing in premium dark cyber-analog ticket aesthetics.
|
| 3 |
+
|
| 4 |
+
Strictly follow and apply this exact design system to create or redesign any interface. Do not deviate, add colors, change fonts, or soften rules.
|
| 5 |
+
|
| 6 |
+
MANDATORY DESIGN SYSTEM:
|
| 7 |
+
|
| 8 |
+
- Background: Pure deep black #0A0A0A
|
| 9 |
+
|
| 10 |
+
- Surface texture (applied everywhere): Heavy elegant film grain + high-frequency noise texture. Include subtle noise gradient — stronger near edges/corners, softer toward center — to mimic scanned printed ticket with analog imperfections. Add very faint overall vignette darkening toward edges (optional but recommended for depth).
|
| 11 |
+
|
| 12 |
+
- Colors: Only #FFFFFF (crisp white) for primary text/icons + #B0B0B0 (light gray) for secondary/dimmed text. No other colors, no tints, no glows.
|
| 13 |
+
|
| 14 |
+
- Typography — IBM Plex Mono ONLY for all text:
|
| 15 |
+
• Headlines / main branding / large titles: IBM Plex Mono Bold (or SemiBold if too heavy), generous tracking (+10–20% letter-spacing), slightly condensed optical feel at large sizes, uppercase preferred.
|
| 16 |
+
• Body / paragraphs / dense info / labels: IBM Plex Mono Regular or Medium, standard monospaced rhythm, line-height 1.4–1.6, size/weight contrast for hierarchy, uppercase for short labels/headers when it improves punch and scannability.
|
| 17 |
+
• Exploit monospaced neo-grotesque terminal/data/code character throughout.
|
| 18 |
+
|
| 19 |
+
- Card style: Tall vertical ticket-like panel (or horizontal variant), soft rounded corners (12–20 px radius), subtle 3D perspective tilt + gentle drop shadow to give lifted physical printed-ticket depth. Or horizontal version.
|
| 20 |
+
|
| 21 |
+
- Geometric overlays (subtle, thin white lines #FFFFFF): Perspective triangles, faint grid lines, radiating technical accents — blueprint/schematic technical feeling without clutter.
|
| 22 |
+
|
| 23 |
+
- Strict layout order (top to bottom):
|
| 24 |
+
1. Strong top header bar — slightly darker raised panel (#111111–#1A1A1A range) containing left-aligned branding text + right-aligned small symbol/copyright
|
| 25 |
+
2. Branding block — centered or left-aligned main title with wave/neural icon integrated
|
| 26 |
+
3. Large visual centerpiece — occupies ~40–50% of height
|
| 27 |
+
4. Information block — generous vertical spacing, breathing room, left-aligned text
|
| 28 |
+
5. Minimal ticket-style footer strip — small text, icons (star, copyright, seal/triangle), barcode or similar element, aligned bottom
|
| 29 |
+
|
| 30 |
+
- Mood & polish: Mysterious, exclusive, high-tech classified artifact. Extreme black-white contrast, Apple-grade micro-details (perfect alignment, kerning, shadow subtlety), instantly readable in dark mode. Must feel like a premium physical ticket that was printed, handled, and scanned — analog grain + digital precision.
|
| 31 |
+
|
| 32 |
+
VISUAL CENTERPIECE — always include one (use both side-by-side when creating pairs/tickets; otherwise user specifies or default to A for technical interfaces):
|
| 33 |
+
|
| 34 |
+
A) Technical version: Large perfect circle (or sharp rectangle) mask filled with dense monospaced terminal/code/data cascade — white IBM Plex Mono text blocks, slight glitch/offset row shifts, layered or pseudo-scrolling effect. Apply same heavy film grain + noise gradient inside the mask.
|
| 35 |
+
|
| 36 |
+
B) Atmospheric version: Full-bleed cinematic moody photograph/scene (desaturated dark tones, low-key lighting, retro-futuristic or abandoned tech vibe). Overlay same heavy film grain + noise gradient + faint vignette.
|
| 37 |
+
|
| 38 |
+
Now strictly apply this system to create or redesign:
|
| 39 |
+
|
| 40 |
+
[INSERT YOUR DESCRIPTION / IDEA / REFERENCE HERE]
|
| 41 |
+
|
| 42 |
+
Optional generation parameters (append if needed): --ar 9:16 --stylize 180 --v 6 --style raw
|
| 43 |
+
|
| 44 |
+
|
| 45 |
+
</command>
|
sample.jsx
ADDED
|
@@ -0,0 +1,501 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { useState, useEffect, useRef } from "react";
|
| 2 |
+
|
| 3 |
+
const FONT_URL = "https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@400;500;600;700&display=swap";
|
| 4 |
+
|
| 5 |
+
// ── Terminal data cascade lines ───────────────────────────────────────────────
|
| 6 |
+
const TERMINAL_LINES = [
|
| 7 |
+
"INIT SEQUENCE 0x4F2A...",
|
| 8 |
+
"AUTH TOKEN: ██████████████",
|
| 9 |
+
"SCAN FREQ 847.221 MHz",
|
| 10 |
+
"SECTOR [CLASSIFIED]",
|
| 11 |
+
"NODE_ID NX-9912-DELTA",
|
| 12 |
+
"TIMESTAMP 2025.03.06",
|
| 13 |
+
"CLEARANCE LEVEL-OMEGA",
|
| 14 |
+
"STATUS AUTHORIZED",
|
| 15 |
+
"PAYLOAD ENCRYPTED",
|
| 16 |
+
"UPLINK ACTIVE",
|
| 17 |
+
"SIGNAL ████░░░░ 61%",
|
| 18 |
+
"VERIFY SHA256:c0d3f",
|
| 19 |
+
"MATRIX 7×7 LATTICE",
|
| 20 |
+
"HASH 0xDEADBEEF",
|
| 21 |
+
"PING 12.4ms OK",
|
| 22 |
+
"TRACE DISABLED",
|
| 23 |
+
"VAULT SEALED",
|
| 24 |
+
"PROTOCOL OMEGA-9",
|
| 25 |
+
"ENTROPY HIGH",
|
| 26 |
+
"FRAME #00441",
|
| 27 |
+
"BUFFER FLUSHED",
|
| 28 |
+
"CIPHER AES-256",
|
| 29 |
+
"RELAY PROXIED",
|
| 30 |
+
"ECHO SILENT",
|
| 31 |
+
"LOCK ENGAGED",
|
| 32 |
+
"GRID REF 44.0N 76.2W",
|
| 33 |
+
"KERNEL 3.14.159",
|
| 34 |
+
"DAEMON RUNNING",
|
| 35 |
+
"WATCHDOG ARMED",
|
| 36 |
+
"ROUTE OBFUSCATED",
|
| 37 |
+
];
|
| 38 |
+
|
| 39 |
+
function useAnimFrame(cb) {
|
| 40 |
+
const ref = useRef();
|
| 41 |
+
useEffect(() => {
|
| 42 |
+
let id;
|
| 43 |
+
const loop = (t) => { cb(t); id = requestAnimationFrame(loop); };
|
| 44 |
+
id = requestAnimationFrame(loop);
|
| 45 |
+
return () => cancelAnimationFrame(id);
|
| 46 |
+
}, [cb]);
|
| 47 |
+
}
|
| 48 |
+
|
| 49 |
+
// ── Noise / grain canvas ──────────────────────────────────────────────────────
|
| 50 |
+
function GrainOverlay({ strength = 0.18 }) {
|
| 51 |
+
const canvasRef = useRef();
|
| 52 |
+
const tick = useRef(0);
|
| 53 |
+
|
| 54 |
+
useEffect(() => {
|
| 55 |
+
const canvas = canvasRef.current;
|
| 56 |
+
if (!canvas) return;
|
| 57 |
+
const ctx = canvas.getContext("2d");
|
| 58 |
+
let raf;
|
| 59 |
+
|
| 60 |
+
const draw = () => {
|
| 61 |
+
tick.current++;
|
| 62 |
+
if (tick.current % 2 !== 0) { raf = requestAnimationFrame(draw); return; }
|
| 63 |
+
const { width, height } = canvas;
|
| 64 |
+
const img = ctx.createImageData(width, height);
|
| 65 |
+
const d = img.data;
|
| 66 |
+
for (let i = 0; i < d.length; i += 4) {
|
| 67 |
+
const px = i / 4;
|
| 68 |
+
const x = px % width;
|
| 69 |
+
const y = Math.floor(px / width);
|
| 70 |
+
// Vignette factor — stronger at edges
|
| 71 |
+
const cx = x / width - 0.5, cy = y / height - 0.5;
|
| 72 |
+
const dist = Math.sqrt(cx * cx + cy * cy);
|
| 73 |
+
const vignette = Math.min(1, dist * 2.2);
|
| 74 |
+
const noise = (Math.random() - 0.5) * 255 * (strength + vignette * 0.18);
|
| 75 |
+
d[i] = d[i + 1] = d[i + 2] = 128 + noise;
|
| 76 |
+
d[i + 3] = Math.floor(18 + vignette * 30);
|
| 77 |
+
}
|
| 78 |
+
ctx.putImageData(img, 0, 0);
|
| 79 |
+
raf = requestAnimationFrame(draw);
|
| 80 |
+
};
|
| 81 |
+
|
| 82 |
+
const resize = () => {
|
| 83 |
+
canvas.width = canvas.offsetWidth;
|
| 84 |
+
canvas.height = canvas.offsetHeight;
|
| 85 |
+
};
|
| 86 |
+
resize();
|
| 87 |
+
draw();
|
| 88 |
+
window.addEventListener("resize", resize);
|
| 89 |
+
return () => { cancelAnimationFrame(raf); window.removeEventListener("resize", resize); };
|
| 90 |
+
}, [strength]);
|
| 91 |
+
|
| 92 |
+
return (
|
| 93 |
+
<canvas
|
| 94 |
+
ref={canvasRef}
|
| 95 |
+
style={{
|
| 96 |
+
position: "absolute", inset: 0, width: "100%", height: "100%",
|
| 97 |
+
pointerEvents: "none", mixBlendMode: "overlay", zIndex: 10,
|
| 98 |
+
}}
|
| 99 |
+
/>
|
| 100 |
+
);
|
| 101 |
+
}
|
| 102 |
+
|
| 103 |
+
// ── SVG geometric overlay ─────────────────────────────────────────────────────
|
| 104 |
+
function GeometricOverlay() {
|
| 105 |
+
return (
|
| 106 |
+
<svg
|
| 107 |
+
viewBox="0 0 480 860"
|
| 108 |
+
style={{
|
| 109 |
+
position: "absolute", inset: 0, width: "100%", height: "100%",
|
| 110 |
+
pointerEvents: "none", zIndex: 5, opacity: 0.12,
|
| 111 |
+
}}
|
| 112 |
+
preserveAspectRatio="none"
|
| 113 |
+
>
|
| 114 |
+
{/* Corner triangles */}
|
| 115 |
+
<polygon points="0,0 72,0 0,72" fill="none" stroke="white" strokeWidth="0.8" />
|
| 116 |
+
<polygon points="480,0 408,0 480,72" fill="none" stroke="white" strokeWidth="0.8" />
|
| 117 |
+
<polygon points="0,860 72,860 0,788" fill="none" stroke="white" strokeWidth="0.8" />
|
| 118 |
+
<polygon points="480,860 408,860 480,788" fill="none" stroke="white" strokeWidth="0.8" />
|
| 119 |
+
{/* Subtle grid lines */}
|
| 120 |
+
{[0.2, 0.4, 0.6, 0.8].map((t) => (
|
| 121 |
+
<line key={t} x1={480 * t} y1="0" x2={480 * t} y2="860" stroke="white" strokeWidth="0.4" />
|
| 122 |
+
))}
|
| 123 |
+
{[0.25, 0.5, 0.75].map((t) => (
|
| 124 |
+
<line key={t} x1="0" y1={860 * t} x2="480" y2={860 * t} stroke="white" strokeWidth="0.4" />
|
| 125 |
+
))}
|
| 126 |
+
{/* Radiating accent lines from center */}
|
| 127 |
+
<line x1="240" y1="430" x2="0" y2="0" stroke="white" strokeWidth="0.3" />
|
| 128 |
+
<line x1="240" y1="430" x2="480" y2="0" stroke="white" strokeWidth="0.3" />
|
| 129 |
+
<line x1="240" y1="430" x2="0" y2="860" stroke="white" strokeWidth="0.3" />
|
| 130 |
+
<line x1="240" y1="430" x2="480" y2="860" stroke="white" strokeWidth="0.3" />
|
| 131 |
+
{/* Center circle accent */}
|
| 132 |
+
<circle cx="240" cy="430" r="200" fill="none" stroke="white" strokeWidth="0.5" />
|
| 133 |
+
<circle cx="240" cy="430" r="198" fill="none" stroke="white" strokeWidth="0.2" />
|
| 134 |
+
</svg>
|
| 135 |
+
);
|
| 136 |
+
}
|
| 137 |
+
|
| 138 |
+
// ── Terminal code cascade ─────────────────────────────────────────────────────
|
| 139 |
+
function TerminalMask() {
|
| 140 |
+
const [offset, setOffset] = useState(0);
|
| 141 |
+
const [glitch, setGlitch] = useState({ row: -1, px: 0 });
|
| 142 |
+
|
| 143 |
+
useEffect(() => {
|
| 144 |
+
const iv = setInterval(() => {
|
| 145 |
+
setOffset((o) => (o + 1) % TERMINAL_LINES.length);
|
| 146 |
+
}, 120);
|
| 147 |
+
const gv = setInterval(() => {
|
| 148 |
+
setGlitch({ row: Math.floor(Math.random() * 18), px: (Math.random() - 0.5) * 8 });
|
| 149 |
+
setTimeout(() => setGlitch({ row: -1, px: 0 }), 80);
|
| 150 |
+
}, 900);
|
| 151 |
+
return () => { clearInterval(iv); clearInterval(gv); };
|
| 152 |
+
}, []);
|
| 153 |
+
|
| 154 |
+
const visible = Array.from({ length: 20 }, (_, i) =>
|
| 155 |
+
TERMINAL_LINES[(offset + i) % TERMINAL_LINES.length]
|
| 156 |
+
);
|
| 157 |
+
|
| 158 |
+
return (
|
| 159 |
+
<div
|
| 160 |
+
style={{
|
| 161 |
+
width: "100%", height: "100%",
|
| 162 |
+
background: "#000",
|
| 163 |
+
borderRadius: "50%",
|
| 164 |
+
overflow: "hidden",
|
| 165 |
+
display: "flex",
|
| 166 |
+
flexDirection: "column",
|
| 167 |
+
justifyContent: "center",
|
| 168 |
+
padding: "18px 14px",
|
| 169 |
+
gap: 0,
|
| 170 |
+
position: "relative",
|
| 171 |
+
}}
|
| 172 |
+
>
|
| 173 |
+
{/* inner vignette */}
|
| 174 |
+
<div style={{
|
| 175 |
+
position: "absolute", inset: 0, borderRadius: "50%",
|
| 176 |
+
background: "radial-gradient(ellipse at center, transparent 30%, rgba(0,0,0,0.72) 100%)",
|
| 177 |
+
zIndex: 2, pointerEvents: "none",
|
| 178 |
+
}} />
|
| 179 |
+
{visible.map((line, i) => (
|
| 180 |
+
<div
|
| 181 |
+
key={i}
|
| 182 |
+
style={{
|
| 183 |
+
fontFamily: "'IBM Plex Mono', monospace",
|
| 184 |
+
fontSize: "9.5px",
|
| 185 |
+
fontWeight: i === 0 ? 700 : 400,
|
| 186 |
+
letterSpacing: "0.08em",
|
| 187 |
+
color: i === 0 ? "#FFFFFF" : i < 3 ? "#D8D8D8" : "#888",
|
| 188 |
+
lineHeight: "1.55",
|
| 189 |
+
opacity: i === 0 ? 1 : Math.max(0.18, 1 - i * 0.045),
|
| 190 |
+
transform: glitch.row === i ? `translateX(${glitch.px}px)` : "none",
|
| 191 |
+
whiteSpace: "nowrap",
|
| 192 |
+
overflow: "hidden",
|
| 193 |
+
textOverflow: "ellipsis",
|
| 194 |
+
transition: "transform 0.04s",
|
| 195 |
+
position: "relative",
|
| 196 |
+
zIndex: 3,
|
| 197 |
+
}}
|
| 198 |
+
>
|
| 199 |
+
{i === 0 ? "▶ " : " "}{line}
|
| 200 |
+
</div>
|
| 201 |
+
))}
|
| 202 |
+
</div>
|
| 203 |
+
);
|
| 204 |
+
}
|
| 205 |
+
|
| 206 |
+
// ── Barcode SVG ───────────────────────────────────────────────────────────────
|
| 207 |
+
function Barcode() {
|
| 208 |
+
const bars = Array.from({ length: 60 }, (_, i) => {
|
| 209 |
+
const w = [1, 1, 2, 1, 3, 1, 2, 1, 1, 2][i % 10];
|
| 210 |
+
return { w, gap: [2, 1, 2, 3, 1, 2, 1, 2, 3, 1][i % 10] };
|
| 211 |
+
});
|
| 212 |
+
return (
|
| 213 |
+
<svg width="160" height="36" viewBox="0 0 160 36">
|
| 214 |
+
{bars.reduce((acc, bar, i) => {
|
| 215 |
+
const x = acc.x;
|
| 216 |
+
acc.elements.push(
|
| 217 |
+
<rect key={i} x={x} y={2} width={bar.w} height={28} fill="white" />
|
| 218 |
+
);
|
| 219 |
+
acc.x += bar.w + bar.gap;
|
| 220 |
+
return acc;
|
| 221 |
+
}, { x: 2, elements: [] }).elements}
|
| 222 |
+
</svg>
|
| 223 |
+
);
|
| 224 |
+
}
|
| 225 |
+
|
| 226 |
+
// ── Tear line ─────────────────────────────────────────────────────────────────
|
| 227 |
+
function TearLine() {
|
| 228 |
+
return (
|
| 229 |
+
<div style={{ display: "flex", alignItems: "center", gap: 0, margin: "0 -32px" }}>
|
| 230 |
+
<div style={{
|
| 231 |
+
width: 22, height: 22, borderRadius: "50%",
|
| 232 |
+
background: "#0A0A0A", border: "1px solid #2a2a2a", flexShrink: 0,
|
| 233 |
+
}} />
|
| 234 |
+
<div style={{
|
| 235 |
+
flex: 1, height: 1,
|
| 236 |
+
backgroundImage: "repeating-linear-gradient(90deg, #333 0, #333 6px, transparent 6px, transparent 12px)",
|
| 237 |
+
}} />
|
| 238 |
+
<div style={{
|
| 239 |
+
width: 22, height: 22, borderRadius: "50%",
|
| 240 |
+
background: "#0A0A0A", border: "1px solid #2a2a2a", flexShrink: 0,
|
| 241 |
+
}} />
|
| 242 |
+
</div>
|
| 243 |
+
);
|
| 244 |
+
}
|
| 245 |
+
|
| 246 |
+
// ── Main component ────────────────────────────────────────────────────────────
|
| 247 |
+
export default function CyberTicket() {
|
| 248 |
+
const [pulse, setPulse] = useState(false);
|
| 249 |
+
|
| 250 |
+
useEffect(() => {
|
| 251 |
+
// inject font
|
| 252 |
+
if (!document.getElementById("ibm-plex-mono")) {
|
| 253 |
+
const link = document.createElement("link");
|
| 254 |
+
link.id = "ibm-plex-mono";
|
| 255 |
+
link.rel = "stylesheet";
|
| 256 |
+
link.href = FONT_URL;
|
| 257 |
+
document.head.appendChild(link);
|
| 258 |
+
}
|
| 259 |
+
const iv = setInterval(() => setPulse((p) => !p), 1400);
|
| 260 |
+
return () => clearInterval(iv);
|
| 261 |
+
}, []);
|
| 262 |
+
|
| 263 |
+
return (
|
| 264 |
+
<div
|
| 265 |
+
style={{
|
| 266 |
+
minHeight: "100vh",
|
| 267 |
+
background: "#0A0A0A",
|
| 268 |
+
display: "flex",
|
| 269 |
+
alignItems: "center",
|
| 270 |
+
justifyContent: "center",
|
| 271 |
+
padding: "40px 16px",
|
| 272 |
+
fontFamily: "'IBM Plex Mono', monospace",
|
| 273 |
+
position: "relative",
|
| 274 |
+
overflow: "hidden",
|
| 275 |
+
}}
|
| 276 |
+
>
|
| 277 |
+
{/* full-page grain */}
|
| 278 |
+
<GrainOverlay strength={0.14} />
|
| 279 |
+
|
| 280 |
+
{/* Ticket card */}
|
| 281 |
+
<div
|
| 282 |
+
style={{
|
| 283 |
+
width: "100%",
|
| 284 |
+
maxWidth: 420,
|
| 285 |
+
background: "#0A0A0A",
|
| 286 |
+
border: "1px solid #222",
|
| 287 |
+
borderRadius: 18,
|
| 288 |
+
overflow: "hidden",
|
| 289 |
+
position: "relative",
|
| 290 |
+
transform: "perspective(900px) rotateX(2.5deg) rotateY(-1.5deg)",
|
| 291 |
+
boxShadow: "0 32px 80px rgba(0,0,0,0.92), 0 2px 0 #333 inset",
|
| 292 |
+
}}
|
| 293 |
+
>
|
| 294 |
+
<GrainOverlay strength={0.22} />
|
| 295 |
+
<GeometricOverlay />
|
| 296 |
+
|
| 297 |
+
{/* 1 ── TOP HEADER BAR */}
|
| 298 |
+
<div
|
| 299 |
+
style={{
|
| 300 |
+
background: "#111",
|
| 301 |
+
borderBottom: "1px solid #222",
|
| 302 |
+
padding: "12px 20px",
|
| 303 |
+
display: "flex",
|
| 304 |
+
alignItems: "center",
|
| 305 |
+
justifyContent: "space-between",
|
| 306 |
+
position: "relative",
|
| 307 |
+
zIndex: 20,
|
| 308 |
+
}}
|
| 309 |
+
>
|
| 310 |
+
<div>
|
| 311 |
+
<div style={{
|
| 312 |
+
fontSize: 9, fontWeight: 700, letterSpacing: "0.32em",
|
| 313 |
+
color: "#FFF", textTransform: "uppercase",
|
| 314 |
+
}}>NEXUS PROTOCOL</div>
|
| 315 |
+
<div style={{ fontSize: 7.5, color: "#666", letterSpacing: "0.18em", marginTop: 1 }}>
|
| 316 |
+
CLASSIFIED ACCESS DOCUMENT
|
| 317 |
+
</div>
|
| 318 |
+
</div>
|
| 319 |
+
<div style={{ textAlign: "right" }}>
|
| 320 |
+
<div style={{ fontSize: 9, color: "#555", letterSpacing: "0.1em" }}>Ω-09</div>
|
| 321 |
+
<div style={{ fontSize: 7, color: "#3a3a3a", marginTop: 1 }}>© 2025</div>
|
| 322 |
+
</div>
|
| 323 |
+
</div>
|
| 324 |
+
|
| 325 |
+
{/* 2 ── BRANDING BLOCK */}
|
| 326 |
+
<div style={{ padding: "24px 28px 16px", position: "relative", zIndex: 20 }}>
|
| 327 |
+
<div style={{
|
| 328 |
+
display: "flex", alignItems: "center", gap: 12, marginBottom: 6,
|
| 329 |
+
}}>
|
| 330 |
+
{/* Wave/neural icon */}
|
| 331 |
+
<svg width="28" height="28" viewBox="0 0 28 28" fill="none">
|
| 332 |
+
<circle cx="14" cy="14" r="13" stroke="white" strokeWidth="1" />
|
| 333 |
+
<path d="M4 14 Q7 8 10 14 Q13 20 16 14 Q19 8 22 14 Q24 18 24 14"
|
| 334 |
+
stroke="white" strokeWidth="1.2" fill="none" strokeLinecap="round" />
|
| 335 |
+
<circle cx="14" cy="14" r="2" fill="white" />
|
| 336 |
+
</svg>
|
| 337 |
+
<div>
|
| 338 |
+
<div style={{
|
| 339 |
+
fontSize: 22, fontWeight: 700, letterSpacing: "0.18em",
|
| 340 |
+
color: "#FFF", textTransform: "uppercase", lineHeight: 1,
|
| 341 |
+
}}>NEURAL</div>
|
| 342 |
+
<div style={{
|
| 343 |
+
fontSize: 22, fontWeight: 400, letterSpacing: "0.34em",
|
| 344 |
+
color: "#888", textTransform: "uppercase", lineHeight: 1, marginTop: 2,
|
| 345 |
+
}}>ACCESS</div>
|
| 346 |
+
</div>
|
| 347 |
+
</div>
|
| 348 |
+
<div style={{
|
| 349 |
+
fontSize: 8, color: "#444", letterSpacing: "0.22em", textTransform: "uppercase",
|
| 350 |
+
}}>
|
| 351 |
+
HIGH-CLEARANCE ADMISSION DOCUMENT — SERIES 9 — OMEGA TIER
|
| 352 |
+
</div>
|
| 353 |
+
</div>
|
| 354 |
+
|
| 355 |
+
{/* 3 ── VISUAL CENTERPIECE — Terminal circle */}
|
| 356 |
+
<div style={{
|
| 357 |
+
padding: "0 28px", position: "relative", zIndex: 20,
|
| 358 |
+
display: "flex", justifyContent: "center",
|
| 359 |
+
}}>
|
| 360 |
+
<div style={{
|
| 361 |
+
width: 260, height: 260,
|
| 362 |
+
borderRadius: "50%",
|
| 363 |
+
border: "1px solid #333",
|
| 364 |
+
overflow: "hidden",
|
| 365 |
+
position: "relative",
|
| 366 |
+
boxShadow: "0 0 0 6px #111, 0 0 0 7px #222",
|
| 367 |
+
}}>
|
| 368 |
+
<TerminalMask />
|
| 369 |
+
<GrainOverlay strength={0.26} />
|
| 370 |
+
</div>
|
| 371 |
+
</div>
|
| 372 |
+
|
| 373 |
+
{/* scan status pill */}
|
| 374 |
+
<div style={{
|
| 375 |
+
display: "flex", justifyContent: "center", marginTop: 12,
|
| 376 |
+
position: "relative", zIndex: 20,
|
| 377 |
+
}}>
|
| 378 |
+
<div style={{
|
| 379 |
+
display: "flex", alignItems: "center", gap: 6,
|
| 380 |
+
border: "1px solid #2a2a2a", borderRadius: 999,
|
| 381 |
+
padding: "4px 14px",
|
| 382 |
+
background: "#0f0f0f",
|
| 383 |
+
}}>
|
| 384 |
+
<div style={{
|
| 385 |
+
width: 5, height: 5, borderRadius: "50%",
|
| 386 |
+
background: pulse ? "#FFF" : "#444",
|
| 387 |
+
transition: "background 0.4s",
|
| 388 |
+
}} />
|
| 389 |
+
<span style={{ fontSize: 8, letterSpacing: "0.2em", color: "#888", textTransform: "uppercase" }}>
|
| 390 |
+
SIGNAL {pulse ? "ACTIVE" : "PINGING"}
|
| 391 |
+
</span>
|
| 392 |
+
</div>
|
| 393 |
+
</div>
|
| 394 |
+
|
| 395 |
+
{/* 4 ── INFORMATION BLOCK */}
|
| 396 |
+
<div style={{
|
| 397 |
+
padding: "22px 28px 0",
|
| 398 |
+
position: "relative", zIndex: 20,
|
| 399 |
+
display: "grid", gridTemplateColumns: "1fr 1fr", gap: "16px 12px",
|
| 400 |
+
}}>
|
| 401 |
+
{[
|
| 402 |
+
{ label: "BEARER", value: "AGENT_NX-44" },
|
| 403 |
+
{ label: "ISSUED", value: "2025.03.06" },
|
| 404 |
+
{ label: "CLEARANCE", value: "OMEGA / Ω" },
|
| 405 |
+
{ label: "EXPIRES", value: "2025.12.31" },
|
| 406 |
+
{ label: "SECTOR", value: "DELTA-9" },
|
| 407 |
+
{ label: "NODE", value: "0xDEAD·BF9" },
|
| 408 |
+
].map(({ label, value }) => (
|
| 409 |
+
<div key={label}>
|
| 410 |
+
<div style={{
|
| 411 |
+
fontSize: 7.5, color: "#444", letterSpacing: "0.24em",
|
| 412 |
+
textTransform: "uppercase", marginBottom: 3,
|
| 413 |
+
}}>{label}</div>
|
| 414 |
+
<div style={{
|
| 415 |
+
fontSize: 11, color: "#DEDEDE", letterSpacing: "0.1em",
|
| 416 |
+
fontWeight: 600,
|
| 417 |
+
}}>{value}</div>
|
| 418 |
+
</div>
|
| 419 |
+
))}
|
| 420 |
+
</div>
|
| 421 |
+
|
| 422 |
+
{/* divider row */}
|
| 423 |
+
<div style={{
|
| 424 |
+
padding: "20px 28px 0", position: "relative", zIndex: 20,
|
| 425 |
+
}}>
|
| 426 |
+
<div style={{ display: "flex", gap: 6, alignItems: "center" }}>
|
| 427 |
+
{Array.from({ length: 3 }).map((_, i) => (
|
| 428 |
+
<div key={i} style={{
|
| 429 |
+
flex: 1, height: 1,
|
| 430 |
+
background: i === 1 ? "#2a2a2a" : "#1a1a1a",
|
| 431 |
+
}} />
|
| 432 |
+
))}
|
| 433 |
+
<svg width="10" height="10" viewBox="0 0 10 10" fill="none" style={{ opacity: 0.4 }}>
|
| 434 |
+
<polygon points="5,0 10,10 0,10" stroke="white" strokeWidth="1" fill="none" />
|
| 435 |
+
</svg>
|
| 436 |
+
{Array.from({ length: 3 }).map((_, i) => (
|
| 437 |
+
<div key={i} style={{
|
| 438 |
+
flex: 1, height: 1,
|
| 439 |
+
background: i === 1 ? "#2a2a2a" : "#1a1a1a",
|
| 440 |
+
}} />
|
| 441 |
+
))}
|
| 442 |
+
</div>
|
| 443 |
+
</div>
|
| 444 |
+
|
| 445 |
+
{/* 5 ── TEAR LINE */}
|
| 446 |
+
<div style={{ padding: "18px 10px 14px", position: "relative", zIndex: 20 }}>
|
| 447 |
+
<TearLine />
|
| 448 |
+
</div>
|
| 449 |
+
|
| 450 |
+
{/* 5 ── FOOTER STRIP */}
|
| 451 |
+
<div style={{
|
| 452 |
+
padding: "8px 28px 22px",
|
| 453 |
+
position: "relative", zIndex: 20,
|
| 454 |
+
display: "flex", alignItems: "center", justifyContent: "space-between",
|
| 455 |
+
}}>
|
| 456 |
+
{/* Barcode */}
|
| 457 |
+
<div>
|
| 458 |
+
<Barcode />
|
| 459 |
+
<div style={{ fontSize: 7, color: "#333", letterSpacing: "0.16em", marginTop: 3 }}>
|
| 460 |
+
NX-9912-DELTA-44-0x2F
|
| 461 |
+
</div>
|
| 462 |
+
</div>
|
| 463 |
+
{/* Footer icons & stamp */}
|
| 464 |
+
<div style={{ textAlign: "right" }}>
|
| 465 |
+
<div style={{ display: "flex", gap: 6, justifyContent: "flex-end", marginBottom: 6 }}>
|
| 466 |
+
{["★", "◈", "▲"].map((icon, i) => (
|
| 467 |
+
<span key={i} style={{
|
| 468 |
+
fontSize: i === 0 ? 11 : 9, color: "#444",
|
| 469 |
+
fontFamily: "monospace",
|
| 470 |
+
}}>{icon}</span>
|
| 471 |
+
))}
|
| 472 |
+
</div>
|
| 473 |
+
<div style={{
|
| 474 |
+
fontSize: 7, color: "#3a3a3a", letterSpacing: "0.2em",
|
| 475 |
+
textTransform: "uppercase", lineHeight: 1.6,
|
| 476 |
+
}}>
|
| 477 |
+
ADMIT<br />ONE
|
| 478 |
+
</div>
|
| 479 |
+
</div>
|
| 480 |
+
</div>
|
| 481 |
+
|
| 482 |
+
{/* subtle bottom vignette */}
|
| 483 |
+
<div style={{
|
| 484 |
+
position: "absolute", bottom: 0, left: 0, right: 0, height: 80,
|
| 485 |
+
background: "linear-gradient(to top, rgba(0,0,0,0.55), transparent)",
|
| 486 |
+
pointerEvents: "none", zIndex: 8,
|
| 487 |
+
}} />
|
| 488 |
+
</div>
|
| 489 |
+
|
| 490 |
+
{/* ambient page label */}
|
| 491 |
+
<div style={{
|
| 492 |
+
position: "absolute", bottom: 18, left: "50%", transform: "translateX(-50%)",
|
| 493 |
+
fontSize: 8, letterSpacing: "0.3em", color: "#2a2a2a",
|
| 494 |
+
fontFamily: "'IBM Plex Mono', monospace", textTransform: "uppercase",
|
| 495 |
+
zIndex: 20,
|
| 496 |
+
}}>
|
| 497 |
+
NEXUS PROTOCOL — DOCUMENT RENDER v9.1 — CLASSIFIED
|
| 498 |
+
</div>
|
| 499 |
+
</div>
|
| 500 |
+
);
|
| 501 |
+
}
|