/* ============================================================ Cyberpunk — Neon-on-black BlockNinja theme Scanlines / glitch / RGB-split / caret-blink / neon-edge utilities. All colours consume the host shadcn HSL tokens via hsl(var(--token)). No hardcoded hex / rgb / named colours. ============================================================ */ /* --- Typography fallbacks --------------------------------- */ .cyberpunk-body { font-family: var(--font-body, "Inter", system-ui, -apple-system, "Segoe UI", sans-serif); } .cyberpunk-mono, .cyberpunk-mono *, .cyberpunk-mono code, pre.cyberpunk-mono, code.cyberpunk-mono { font-family: var(--font-mono, "JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, Monaco, monospace); } .cyberpunk-display { font-family: var(--font-heading, "Space Grotesk", system-ui, -apple-system, "Segoe UI", sans-serif); letter-spacing: -0.01em; } .cyberpunk-prose, .cyberpunk-prose p, .cyberpunk-prose li { font-family: var(--font-body, "Inter", system-ui, sans-serif); line-height: 1.75; } .cyberpunk-prose code { color: hsl(var(--accent)); background-color: hsl(var(--muted)); padding: 0.125rem 0.375rem; border-radius: 0.25rem; font-family: var(--font-mono, "JetBrains Mono", ui-monospace, monospace); font-size: 0.875em; } /* --- Scanlines -------------------------------------------- */ .scanlines { opacity: 0.04; background-image: linear-gradient( to bottom, hsl(var(--foreground)) 0px, hsl(var(--foreground)) 1px, transparent 1px, transparent 3px ); background-size: 100% 3px; animation: scanline-drift 6s linear infinite; } @keyframes scanline-drift { 0% { background-position: 0 0; } 100% { background-position: 0 3px; } } /* --- Glitch (RGB-split text-shadow) on H1/H2 -------------- */ .glitch { position: relative; text-shadow: -2px 0 hsl(var(--primary)), 2px 0 hsl(var(--accent)); animation: glitch-x 3.6s steps(1, end) infinite; } @keyframes glitch-x { 0%, 92%, 100% { text-shadow: -2px 0 hsl(var(--primary)), 2px 0 hsl(var(--accent)); } 93% { text-shadow: -3px 0 hsl(var(--primary)), 3px 0 hsl(var(--accent)), 0 1px hsl(var(--accent)); } 95% { text-shadow: -1px 0 hsl(var(--accent)), 4px 0 hsl(var(--primary)); } 97% { text-shadow: -2px 0 hsl(var(--primary)), 2px 0 hsl(var(--accent)); } } /* --- RGB-split hover/focus/active on primary buttons ------ */ .rgb-split { transition: transform 120ms ease, box-shadow 120ms ease; } .rgb-split:hover, .rgb-split:focus-visible { box-shadow: -2px 0 0 hsl(var(--primary)), 2px 0 0 hsl(var(--accent)); transform: translateY(-1px); } /* Touch tap-flash fallback (iOS strips :hover after touchend) */ .cyberpunk-btn:active, .rgb-split:active, button.cyberpunk-btn:active { box-shadow: -2px 0 0 hsl(var(--primary)), 2px 0 0 hsl(var(--accent)); } /* :focus-visible ring uses the ring token, not browser default */ .cyberpunk-btn:focus-visible, .cyberpunk-btn-ghost:focus-visible, button:focus-visible, a:focus-visible { outline: 2px solid hsl(var(--ring)); outline-offset: 2px; } /* --- Caret blink ------------------------------------------ */ .caret-blink { display: inline-block; animation: caret 1.05s steps(2, end) infinite; } @keyframes caret { 0%, 50% { opacity: 1; } 51%, 100% { opacity: 0; } } /* --- Neon edge utilities (magenta / cyan / lime) ---------- */ .neon-edge-magenta { box-shadow: 0 0 0 1px hsl(var(--primary) / 0.4), 0 0 18px hsl(var(--primary) / 0.25); transition: box-shadow 180ms ease, transform 180ms ease; } .neon-edge-magenta:hover, .neon-edge-magenta:focus-within { box-shadow: 0 0 0 1px hsl(var(--primary) / 0.85), 0 0 28px hsl(var(--primary) / 0.45); transform: translateY(-1px); } .neon-edge-cyan { box-shadow: 0 0 0 1px hsl(var(--accent) / 0.4), 0 0 18px hsl(var(--accent) / 0.25); transition: box-shadow 180ms ease, transform 180ms ease; } .neon-edge-cyan:hover, .neon-edge-cyan:focus-within { box-shadow: 0 0 0 1px hsl(var(--accent) / 0.85), 0 0 28px hsl(var(--accent) / 0.45); transform: translateY(-1px); } .neon-edge-lime { /* Lime accent is the toxic-bloom primary; magenta-noir uses the primary token. We reuse --ring as a third neon channel so this utility renders visibly across all three presets. */ box-shadow: 0 0 0 1px hsl(var(--ring) / 0.4), 0 0 18px hsl(var(--ring) / 0.25); transition: box-shadow 180ms ease, transform 180ms ease; } .neon-edge-lime:hover, .neon-edge-lime:focus-within { box-shadow: 0 0 0 1px hsl(var(--ring) / 0.85), 0 0 28px hsl(var(--ring) / 0.45); transform: translateY(-1px); } /* --- Card override dashed border --------------------------- */ .cyberpunk-card { position: relative; } /* --- Status pill / dot animation -------------------------- */ .cyberpunk-status-dot { animation: cyberpunk-pulse 2.2s ease-in-out infinite; } @keyframes cyberpunk-pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.4; } } /* --- Reduced motion: disable glitch, caret, scanline ------ */ @media (prefers-reduced-motion: reduce) { .glitch, .caret-blink, .scanlines, .cyberpunk-status-dot { animation: none !important; animation-duration: 0.01ms !important; } .glitch { text-shadow: none; } .scanlines { opacity: 0; } .rgb-split:hover, .rgb-split:focus-visible { transform: none; } } /* --- High-contrast: kill scanlines ------------------------ */ @media (prefers-contrast: more) { .scanlines { display: none; } }