From 06bb9df500c9d54099f5f598e9d9ff797cd72f13 Mon Sep 17 00:00:00 2001 From: AdamBtech <60339324+AdamBtech@users.noreply.github.com> Date: Tue, 27 May 2025 12:03:10 +0200 Subject: [PATCH] Added optimization / patched memory leaks for several animation and Threejs component --- src/app/components/button/nav-button.css | 144 +-- .../container-decorator.component.css | 0 .../container-decorator.component.html | 1 - .../container-decorator.component.ts | 11 - .../header-contact-links.component.css | 275 ++---- .../header-switch-theme-button.component.css | 54 +- .../header-logo/header-logo.component.css | 240 +++-- .../header-text-animate-section.component.ts | 234 ++--- .../holo-video-container.component.css | 17 +- .../holo-video-container.component.ts | 144 ++- .../infocard/infocard.component.css | 0 .../infocard/infocard.component.html | 1 - .../components/infocard/infocard.component.ts | 11 - .../long-button/long-button.component.css | 0 .../long-button/long-button.component.html | 1 - .../long-button/long-button.component.ts | 11 - src/app/pages/about/about.component.css | 635 ++++--------- .../neural-profile-tree.component.css | 191 ++-- .../contact-content.component.css | 248 +++++ .../contact-content.component.html | 99 ++ .../contact-content.component.ts | 11 + src/app/pages/contact/contact.component.html | 10 +- src/app/pages/contact/contact.component.ts | 3 +- src/app/pages/projects/projects.component.css | 884 +++--------------- src/styles.css | 192 ++-- 25 files changed, 1326 insertions(+), 2091 deletions(-) delete mode 100644 src/app/components/container-decorator/container-decorator.component.css delete mode 100644 src/app/components/container-decorator/container-decorator.component.html delete mode 100644 src/app/components/container-decorator/container-decorator.component.ts delete mode 100644 src/app/components/infocard/infocard.component.css delete mode 100644 src/app/components/infocard/infocard.component.html delete mode 100644 src/app/components/infocard/infocard.component.ts delete mode 100644 src/app/components/long-button/long-button.component.css delete mode 100644 src/app/components/long-button/long-button.component.html delete mode 100644 src/app/components/long-button/long-button.component.ts create mode 100644 src/app/pages/contact/contact-content/contact-content.component.css create mode 100644 src/app/pages/contact/contact-content/contact-content.component.html create mode 100644 src/app/pages/contact/contact-content/contact-content.component.ts diff --git a/src/app/components/button/nav-button.css b/src/app/components/button/nav-button.css index 1e74e0d..b5dbdbe 100644 --- a/src/app/components/button/nav-button.css +++ b/src/app/components/button/nav-button.css @@ -1,3 +1,24 @@ +/* ===================== OPTIMIZED NIER BUTTON ===================== */ + +/* Core keyframes - simplified and optimized */ +@keyframes glitchEffect { + 0%, 100% { opacity: 0; transform: translateX(0); } + 10% { opacity: 0.2; transform: translateX(-2px); clip-path: inset(10% 0 80% 0); } + 20% { opacity: 0.2; transform: translateX(2px); clip-path: inset(30% 0 50% 0); } + 30% { opacity: 0.1; transform: translateX(-1px); clip-path: inset(50% 0 20% 0); } +} + +@keyframes scanLine { + from { opacity: 0.5; left: -100%; } + to { opacity: 0; left: 100%; } +} + +@keyframes flash { + from { opacity: 0.2; } + to { opacity: 0; } +} + +/* Button base styles */ .button-custom { cursor: pointer; background-color: var(--color-nier-mid); @@ -7,43 +28,25 @@ border-left: none; border-right: none; overflow: hidden; - transition: color 0.2s ease, background-color 0.2s ease, border-color 0.2s ease; + transition: all 0.2s ease; } +/* Fill effect */ .button-custom::before { content: ""; position: absolute; - left: 0; - top: 3px; /* Space from top border */ - bottom: 3px; /* Space from bottom border */ + inset: 3px 0; width: 0; - height: auto; /* This makes it respect top and bottom values */ background-color: var(--color-nier-dark); transition: width 0.2s ease; z-index: 0; } -/* Normal hover/focus state - keep your original behavior */ -.button-custom:hover, -.button-custom:focus { - color: var(--color-nier-text-light); - background-color: transparent; - border-color: var(--color-nier-dark); -} - -.button-custom:hover::before, -.button-custom:focus::before { - width: 100%; -} - -/* Add NieR-style glitch effects on hover */ +/* Glitch text overlay */ .button-custom::after { - content: attr(data-label); /* Use the button's text from data-label attribute */ + content: attr(data-label); position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; + inset: 0; display: flex; align-items: center; justify-content: center; @@ -53,11 +56,7 @@ pointer-events: none; } -.button-custom:hover::after { - animation: nier-button-glitch 0.6s ease forwards; -} - -/* Add subtle scan line on hover */ +/* Scan line element */ .button-custom .scan-line { position: absolute; top: 0; @@ -70,80 +69,35 @@ pointer-events: none; } +/* ===================== HOVER STATES ===================== */ + +.button-custom:hover, +.button-custom:focus { + color: var(--color-nier-text-light); + background-color: transparent; + border-color: var(--color-nier-dark); +} + +.button-custom:hover::before, +.button-custom:focus::before { + width: 100%; +} + +.button-custom:hover::after { + animation: glitchEffect 0.6s ease forwards; +} + .button-custom:hover .scan-line { - animation: nier-button-scan 0.3s ease forwards; + animation: scanLine 0.3s ease forwards; } -/* The glitch effect animation */ -@keyframes nier-button-glitch { - 0%, 100% { - opacity: 0; - transform: translateX(0); - clip-path: inset(0 0 0 0); - } - 10% { - opacity: 0.2; - transform: translateX(-2px); - clip-path: inset(10% 0 80% 0); - } - 12% { - opacity: 0; - transform: translateX(0); - } - 20% { - opacity: 0.2; - transform: translateX(2px); - clip-path: inset(30% 0 50% 0); - } - 22% { - opacity: 0; - transform: translateX(0); - } - 30% { - opacity: 0.1; - transform: translateX(-1px); - clip-path: inset(50% 0 20% 0); - } - 32% { - opacity: 0; - transform: translateX(0); - } -} +/* ===================== ACTIVE STATES ===================== */ -/* The scan line animation */ -@keyframes nier-button-scan { - 0% { - opacity: 0.5; - left: -100%; - } - 100% { - opacity: 0; - left: 100%; - } -} - -/* Add a brief effect on button click */ .button-custom:active { transform: scale(0.98); } .button-custom:active::after { - content: ""; - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; background-color: rgba(255, 255, 255, 0.1); - opacity: 0; - animation: nier-button-flash 0.2s ease; -} - -@keyframes nier-button-flash { - 0% { - opacity: 0.2; - } - 100% { - opacity: 0; - } + animation: flash 0.2s ease; } diff --git a/src/app/components/container-decorator/container-decorator.component.css b/src/app/components/container-decorator/container-decorator.component.css deleted file mode 100644 index e69de29..0000000 diff --git a/src/app/components/container-decorator/container-decorator.component.html b/src/app/components/container-decorator/container-decorator.component.html deleted file mode 100644 index 61a89b6..0000000 --- a/src/app/components/container-decorator/container-decorator.component.html +++ /dev/null @@ -1 +0,0 @@ -
container-decorator works!
diff --git a/src/app/components/container-decorator/container-decorator.component.ts b/src/app/components/container-decorator/container-decorator.component.ts deleted file mode 100644 index fdfe1f3..0000000 --- a/src/app/components/container-decorator/container-decorator.component.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { Component } from '@angular/core'; - -@Component({ - selector: 'app-container-decorator', - imports: [], - templateUrl: './container-decorator.component.html', - styleUrl: './container-decorator.component.css' -}) -export class ContainerDecoratorComponent { - -} diff --git a/src/app/components/header/header-contact-links/header-contact-links.component.css b/src/app/components/header/header-contact-links/header-contact-links.component.css index 74750da..c61042e 100644 --- a/src/app/components/header/header-contact-links/header-contact-links.component.css +++ b/src/app/components/header/header-contact-links/header-contact-links.component.css @@ -1,3 +1,25 @@ +/* ===================== OPTIMIZED SVG BUTTONS & LINKS ===================== */ + +/* Core keyframes - reused across components */ +@keyframes glitchEffect { + 0%, 100% { opacity: 0; transform: translateX(0); clip-path: inset(0); } + 10% { opacity: 0.2; transform: translateX(-2px); clip-path: inset(10% 0 80% 0); } + 20% { opacity: 0.2; transform: translateX(2px); clip-path: inset(30% 0 50% 0); } + 30% { opacity: 0.1; transform: translateX(-1px); clip-path: inset(50% 0 20% 0); } +} + +@keyframes scanLine { + from { opacity: 0.5; left: -100%; } + to { opacity: 0; left: 100%; } +} + +@keyframes flash { + from { opacity: 0.2; } + to { opacity: 0; } +} + +/* ===================== SVG BUTTON CONTAINER ===================== */ + .svg-button-container { position: relative; cursor: pointer; @@ -5,14 +27,11 @@ transition: transform 0.2s ease; -webkit-tap-highlight-color: transparent; user-select: none; - - /* Add these properties to match button-custom */ border: 1px solid transparent; border-left: none; border-right: none; } -/* SVG icon styling */ .svg-icon { fill: var(--color-nier-dark, #5a5a50); position: relative; @@ -20,45 +39,22 @@ transition: fill 0.3s ease; } -/* Background fill effect - matching button-custom */ +/* Fill effect */ .svg-button-container::before { content: ""; position: absolute; - left: 0; - top: 3px; /* Space from top border */ - bottom: 3px; /* Space from bottom border */ + inset: 3px 0; width: 0; - height: auto; /* This makes it respect top and bottom values */ background-color: var(--color-nier-dark, rgba(30, 30, 30, 0.8)); transition: width 0.2s ease; z-index: 0; } -/* Hover state */ -.svg-button-container:hover, -.svg-button-container:focus { - border-color: var(--color-nier-dark); -} - -.svg-button-container:hover::before, -.svg-button-container:focus::before { - width: 100%; -} - -/* Change SVG fill on hover */ -.svg-button-container:hover .svg-icon, -.svg-button-container:active .svg-icon { - fill: var(--color-nier-text-light, #dcd8c0); -} - -/* Glitch effect on hover */ +/* Glitch text overlay */ .svg-button-container::after { content: attr(data-label); position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; + inset: 0; display: flex; align-items: center; justify-content: center; @@ -71,15 +67,10 @@ font-family: "Noto Sans JP", monospace; } -.svg-button-container:hover::after { - animation: nier-button-glitch 0.6s ease forwards; -} - -/* Scan line effect */ +/* Scan line */ .svg-button-container .scan-line { position: absolute; - top: 0; - left: -100%; + inset: 0 0 auto -100%; width: 100%; height: 1px; background-color: var(--color-nier-text-light, rgba(230, 230, 230, 0.9)); @@ -88,100 +79,42 @@ pointer-events: none; } +/* Hover states */ +.svg-button-container:hover, +.svg-button-container:focus { + border-color: var(--color-nier-dark); +} + +.svg-button-container:hover::before, +.svg-button-container:focus::before { + width: 100%; +} + +.svg-button-container:hover .svg-icon, +.svg-button-container:active .svg-icon { + fill: var(--color-nier-text-light, #dcd8c0); +} + +.svg-button-container:hover::after { + animation: glitchEffect 0.6s ease forwards; +} + .svg-button-container:hover .scan-line { - animation: nier-button-scan 0.3s ease forwards; + animation: scanLine 0.3s ease forwards; } -/* The glitch effect animation */ -@keyframes nier-button-glitch { - 0%, 100% { - opacity: 0; - transform: translateX(0); - clip-path: inset(0 0 0 0); - } - 10% { - opacity: 0.2; - transform: translateX(-2px); - clip-path: inset(10% 0 80% 0); - } - 12% { - opacity: 0; - transform: translateX(0); - } - 20% { - opacity: 0.2; - transform: translateX(2px); - clip-path: inset(30% 0 50% 0); - } - 22% { - opacity: 0; - transform: translateX(0); - } - 30% { - opacity: 0.1; - transform: translateX(-1px); - clip-path: inset(50% 0 20% 0); - } - 32% { - opacity: 0; - transform: translateX(0); - } -} - -/* The scan line animation */ -@keyframes nier-button-scan { - 0% { - opacity: 0.5; - left: -100%; - } - 100% { - opacity: 0; - left: 100%; - } -} - -/* Button click effect */ +/* Active state */ .svg-button-container:active { transform: scale(0.98); } .svg-button-container:active::after { - content: ""; - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; background-color: rgba(255, 255, 255, 0.1); - opacity: 0; - animation: nier-button-flash 0.2s ease; + animation: flash 0.2s ease; } -@keyframes nier-button-flash { - 0% { - opacity: 0.2; - } - 100% { - opacity: 0; - } -} +/* ===================== TEXT LINKS ===================== */ -/* Touch device optimization */ -@media (hover: none) { - .svg-button-container:active::before { - width: 100%; - } - - .svg-button-container:active .scan-line { - animation: nier-button-scan 0.3s ease forwards; - } - - .svg-button-container:active .svg-icon { - fill: var(--color-nier-dark, #dcd8c0); - } -} - -/* Styling for text links with NieR aesthetic */ a[data-label] { position: relative; cursor: pointer; @@ -194,7 +127,6 @@ a[data-label] { border-bottom: 1px solid transparent; } -/* Underline effect */ a[data-label]::before { content: ""; position: absolute; @@ -207,27 +139,9 @@ a[data-label]::before { z-index: 0; } -/* Text color change on hover */ -a[data-label]:hover { - color: var(--color-nier-dark, #dcd8c0); - border-bottom-color: var(--color-nier-dark); -} - -/* Underline animation on hover */ -a[data-label]:hover::before { - width: 100%; -} - -/* Glitch effect on hover (similar to buttons) */ -a[data-label]:hover span:first-child { - animation: nier-text-glitch 0.6s ease; -} - -/* Scan line for links */ a[data-label] .scan-line { position: absolute; - top: 0; - left: -100%; + inset: 0 0 auto -100%; width: 100%; height: 1px; background-color: var(--color-nier-dark, rgba(230, 230, 230, 0.9)); @@ -236,65 +150,62 @@ a[data-label] .scan-line { pointer-events: none; } +/* Link hover states */ +a[data-label]:hover { + color: var(--color-nier-dark, #dcd8c0); + border-bottom-color: var(--color-nier-dark); +} + +a[data-label]:hover::before { + width: 100%; +} + +a[data-label]:hover span:first-child { + animation: glitchEffect 0.6s ease; +} + a[data-label]:hover .scan-line { - animation: nier-button-scan 0.3s ease forwards; + animation: scanLine 0.3s ease forwards; } -/* Text glitch animation for links */ -@keyframes nier-text-glitch { - 0%, 100% { - transform: translateX(0); - clip-path: inset(0 0 0 0); - } - 10% { - transform: translateX(-2px); - clip-path: inset(0 0 40% 0); - } - 15% { - transform: translateX(0); - } - 20% { - transform: translateX(1px); - clip-path: inset(40% 0 0 0); - } - 25% { - transform: translateX(0); - } - 30% { - transform: translateX(-1px); - clip-path: inset(20% 0 20% 0); - } - 35% { - transform: translateX(0); - } -} - -/* Active/click state for links */ a[data-label]:active { transform: scale(0.98); } -/* Touch device optimization */ +/* ===================== ICON STYLES ===================== */ + +.icon-email { + width: 150px; + height: 150px; + background-color: var(--color-nier-text-dark); + -webkit-mask: url('/email_qr_black.svg') no-repeat center / contain; + mask: url('/email_qr_black.svg') no-repeat center / contain; +} + +/* ===================== TOUCH DEVICES ===================== */ + @media (hover: none) { + .svg-button-container:active::before { + width: 100%; + } + + .svg-button-container:active .scan-line { + animation: scanLine 0.3s ease forwards; + } + + .svg-button-container:active .svg-icon { + fill: var(--color-nier-dark, #dcd8c0); + } + a[data-label]:active::before { width: 100%; } a[data-label]:active .scan-line { - animation: nier-button-scan 0.3s ease forwards; + animation: scanLine 0.3s ease forwards; } a[data-label]:active { color: var(--color-nier-dark, #dcd8c0); } } - -.icon-email { - width: 150px; - height: 150px; - background-color: var(--color-nier-text-dark); - -webkit-mask: url('/email_qr_black.svg') no-repeat center; - mask: url('/email_qr_black.svg') no-repeat center; - -webkit-mask-size: contain; - mask-size: contain; -} diff --git a/src/app/components/header/header-contact-links/header-switch-theme-button/header-switch-theme-button.component.css b/src/app/components/header/header-contact-links/header-switch-theme-button/header-switch-theme-button.component.css index b3576dc..c5acc52 100644 --- a/src/app/components/header/header-contact-links/header-switch-theme-button/header-switch-theme-button.component.css +++ b/src/app/components/header/header-contact-links/header-switch-theme-button/header-switch-theme-button.component.css @@ -1,62 +1,52 @@ -/* header-switch-theme-button.component.scss */ +/* ===================== OPTIMIZED THEME SWITCH BUTTON ===================== */ -/* Base styling for SVG elements */ +/* Base SVG styling */ .svg-icon { - transition: fill 0.3s ease, color 0.3s ease, transform 0.3s ease; + transition: all 0.3s ease; } -/* Moon cutout styling - hidden by default in light mode */ -.moon-cutout { - transition: fill 0.3s ease, transform 0.3s ease, opacity 0.3s ease; - fill: #1a1a18; /* Default darker color */ - opacity: 0; /* Hidden in light mode */ -} - -/* LIGHT MODE (default) - show sun */ -/* Base circle acts as the sun */ .svg-icon circle[cx="12"][cy="12"][r="5"] { fill: currentColor; } -/* DARK MODE - show moon */ -/* Show the moon cutout in dark mode */ +/* Moon cutout - hidden by default */ +.moon-cutout { + fill: #1a1a18; + opacity: 0; + transition: all 0.3s ease; +} + +/* ===================== THEME STATES ===================== */ + +/* Dark mode - show moon */ :host-context(.dark) .moon-cutout, body[data-theme="dark"] .moon-cutout { fill: var(--color-nier-dark-bg, #292925); - opacity: 1; /* Visible in dark mode */ -} - -/* HOVER STATE - always show moon with rotation */ -/* Rotation animation on hover */ -:host-context(.svg-button-container:hover) .svg-icon, -.svg-button-container:hover .svg-icon { - transform: rotate(45deg); - color: var(--color-nier-text-light, #dcd8c0); - fill: var(--color-nier-text-light, #dcd8c0); -} - -/* Always show moon cutout on hover */ -:host-context(.svg-button-container:hover) .moon-cutout, -.svg-button-container:hover .moon-cutout { opacity: 1; - fill: #3a3a34; /* Dark color for cutout on hover */ } -/* ACTIVE STATE - same as hover */ +/* ===================== INTERACTIVE STATES ===================== */ + +/* Hover and active states */ +:host-context(.svg-button-container:hover) .svg-icon, :host-context(.svg-button-container:active) .svg-icon, +.svg-button-container:hover .svg-icon, .svg-button-container:active .svg-icon { transform: rotate(45deg); color: var(--color-nier-text-light, #dcd8c0); fill: var(--color-nier-text-light, #dcd8c0); } +:host-context(.svg-button-container:hover) .moon-cutout, :host-context(.svg-button-container:active) .moon-cutout, +.svg-button-container:hover .moon-cutout, .svg-button-container:active .moon-cutout { opacity: 1; fill: #3a3a34; } -/* Touch device support */ +/* ===================== TOUCH DEVICES ===================== */ + @media (hover: none) { :host-context(.svg-button-container:active) .svg-icon, .svg-button-container:active .svg-icon { diff --git a/src/app/components/header/header-logo/header-logo.component.css b/src/app/components/header/header-logo/header-logo.component.css index 9992e41..8e677a5 100644 --- a/src/app/components/header/header-logo/header-logo.component.css +++ b/src/app/components/header/header-logo/header-logo.component.css @@ -1,4 +1,35 @@ -/* This simpler approach uses your existing global variables directly */ +/* ===================== OPTIMIZED NIER LOGO ===================== */ + +/* Core keyframes - simplified and optimized */ +@keyframes fadeInReveal { + 0% { opacity: 0; clip-path: inset(0 100% 0 0); } + 100% { opacity: 1; clip-path: inset(0 0 0 0); } +} + +@keyframes pulse { + 0%, 100% { opacity: 1; } + 50% { opacity: 0.9; } +} + +@keyframes glitch { + 0%, 100% { opacity: 0; transform: translateX(0); } + 10.5% { opacity: 0.5; transform: translateX(3px); } + 30% { opacity: 0.4; transform: translateX(-3px); } + 80.5% { opacity: 0.6; transform: translateX(5px); } +} + +@keyframes scanLine { + from { transform: translateY(-100%); } + to { transform: translateY(100%); } +} + +@keyframes floatingBlocks { + 0% { opacity: 0; transform: translate(0, 0); } + 10% { opacity: 0.8; } + 100% { opacity: 0; transform: translate(40px, 100px); } +} + +/* ===================== LOGO CONTAINER ===================== */ .nier-logo-container { position: relative; @@ -8,107 +39,15 @@ transition: all 0.3s ease; } -.nier-logo { - position: relative; - color: var(--color-nier-text-dark); /* Use your global variable */ - text-shadow: 0 0 2px var(--color-nier-text-dark); - animation: nier-fade-in 2.5s ease-out forwards, nier-subtle-pulse 4s 2.5s infinite; - transition: color 0.3s ease, text-shadow 0.3s ease; -} - -.nier-logo::before { - content: attr(data-text); - position: absolute; - left: 0; - top: 0; - width: 100%; - height: 100%; - text-shadow: 0 0 3px var(--color-nier-text-dark); - opacity: 0; - animation: nier-glitch 6s 3s infinite; - transition: text-shadow 0.3s ease; -} - -.nier-logo::after { - content: attr(data-text); - position: absolute; - left: -2px; - top: 0; - width: 100%; - height: 100%; - text-shadow: -1px 0 1px var(--color-nier-text-dark); - opacity: 0; - animation: nier-glitch-2 5s 3s infinite; - transition: text-shadow 0.3s ease; -} - -.nier-scan-line { - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; - background: linear-gradient( - to bottom, - transparent 0%, - color-mix(in srgb, var(--color-nier-text-dark) 5%, transparent) 50%, - transparent 100% - ); - animation: nier-scan 3s linear infinite; - z-index: 2; - pointer-events: none; - transition: background 0.3s ease; -} - -.nier-blocks { - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; - z-index: 1; - pointer-events: none; -} - -.nier-blocks::before, -.nier-blocks::after { - content: ""; - position: absolute; - width: 10px; - height: 6px; - background: color-mix(in srgb, var(--color-nier-text-dark) 10%, transparent); - animation: nier-blocks 10s linear infinite; - transition: background 0.3s ease; -} - -.nier-blocks::before { - top: 20%; - left: 10%; - animation-delay: 1s; -} - -.nier-blocks::after { - bottom: 40%; - right: 10%; - width: 15px; - height: 4px; - animation-delay: 3s; -} - -/* Border and interface details */ .nier-logo-container::before { content: ""; position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; + inset: 0; border: 1px solid var(--color-nier-border); pointer-events: none; transition: border-color 0.3s ease; } -/* Interface dots in the corner */ .nier-logo-container::after { content: ""; position: absolute; @@ -124,57 +63,88 @@ -6px 6px 0 var(--color-nier-text-dark), -12px 6px 0 var(--color-nier-text-dark); pointer-events: none; - transition: background-color 0.3s ease, box-shadow 0.3s ease; + transition: all 0.3s ease; } -/* Animations unchanged */ -@keyframes nier-fade-in { - 0% { opacity: 0; clip-path: inset(0 100% 0 0); } - 20% { opacity: 0.3; clip-path: inset(0 80% 0 0); } - 40% { opacity: 0.5; clip-path: inset(0 60% 0 0); } - 60% { opacity: 0.7; clip-path: inset(0 40% 0 0); } - 80% { opacity: 0.9; clip-path: inset(0 20% 0 0); } - 95% { opacity: 1; clip-path: inset(0 5% 0 0); } - 100% { opacity: 1; clip-path: inset(0 0 0 0); } +/* ===================== LOGO TEXT ===================== */ + +.nier-logo { + position: relative; + color: var(--color-nier-text-dark); + text-shadow: 0 0 2px var(--color-nier-text-dark); + animation: fadeInReveal 2.5s ease-out forwards, pulse 4s 2.5s infinite; + transition: all 0.3s ease; } -@keyframes nier-subtle-pulse { - 0%, 100% { opacity: 1; } - 50% { opacity: 0.9; } +.nier-logo::before, +.nier-logo::after { + content: attr(data-text); + position: absolute; + top: 0; + width: 100%; + height: 100%; + opacity: 0; + animation: glitch 6s 3s infinite; + transition: text-shadow 0.3s ease; } -@keyframes nier-glitch { - 0%, 100% { opacity: 0; transform: translateX(0); } - 10.5% { opacity: 0.5; transform: translateX(3px); } - 11% { opacity: 0; transform: translateX(0); } - 29.5% { opacity: 0; transform: translateX(0); } - 30% { opacity: 0.4; transform: translateX(-3px); } - 30.5% { opacity: 0; transform: translateX(0); } - 80% { opacity: 0; transform: translateX(0); } - 80.5% { opacity: 0.6; transform: translateX(5px); } - 81% { opacity: 0; transform: translateX(0); } +.nier-logo::before { + left: 0; + text-shadow: 0 0 3px var(--color-nier-text-dark); } -@keyframes nier-glitch-2 { - 0%, 100% { opacity: 0; transform: translateX(0); } - 10.5% { opacity: 0; transform: translateX(0); } - 11% { opacity: 0.4; transform: translateX(-2px); } - 11.5% { opacity: 0; transform: translateX(0); } - 50% { opacity: 0; transform: translateX(0); } - 50.5% { opacity: 0.4; transform: translateX(2px); } - 51% { opacity: 0; transform: translateX(0); } +.nier-logo::after { + left: -2px; + text-shadow: -1px 0 1px var(--color-nier-text-dark); + animation-delay: 3s; + animation-duration: 5s; } -@keyframes nier-scan { - 0% { transform: translateY(-100%); } - 100% { transform: translateY(100%); } +/* ===================== EFFECTS ===================== */ + +.nier-scan-line { + position: absolute; + inset: 0; + background: linear-gradient( + to bottom, + transparent 0%, + color-mix(in srgb, var(--color-nier-text-dark) 5%, transparent) 50%, + transparent 100% + ); + animation: scanLine 3s linear infinite; + z-index: 2; + pointer-events: none; + transition: background 0.3s ease; } -@keyframes nier-blocks { - 0% { opacity: 0; transform: translateY(0) translateX(0); } - 10% { opacity: 0.8; } - 30% { opacity: 0.6; transform: translateY(20px) translateX(10px); } - 50% { opacity: 0.4; transform: translateY(40px) translateX(20px); } - 70% { opacity: 0.2; transform: translateY(60px) translateX(30px); } - 100% { opacity: 0; transform: translateY(100px) translateX(40px); } +.nier-blocks { + position: absolute; + inset: 0; + z-index: 1; + pointer-events: none; +} + +.nier-blocks::before, +.nier-blocks::after { + content: ""; + position: absolute; + background: color-mix(in srgb, var(--color-nier-text-dark) 10%, transparent); + animation: floatingBlocks 10s linear infinite; + transition: background 0.3s ease; +} + +.nier-blocks::before { + top: 20%; + left: 10%; + width: 10px; + height: 6px; + animation-delay: 1s; +} + +.nier-blocks::after { + bottom: 40%; + right: 10%; + width: 15px; + height: 4px; + animation-delay: 3s; } diff --git a/src/app/components/header/header-text-animate-section/header-text-animate-section.component.ts b/src/app/components/header/header-text-animate-section/header-text-animate-section.component.ts index 334660f..9dd088a 100644 --- a/src/app/components/header/header-text-animate-section/header-text-animate-section.component.ts +++ b/src/app/components/header/header-text-animate-section/header-text-animate-section.component.ts @@ -8,6 +8,8 @@ import { signal, computed, ChangeDetectionStrategy, + DestroyRef, + inject, } from '@angular/core'; import { trigger, @@ -16,14 +18,15 @@ import { transition, animate, } from '@angular/animations'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; +import { timer, Subject } from 'rxjs'; import { type TextItem } from '../../../shared/models/header.model'; -// Constants for better maintainability -const TYPING_DELAYS = { - GLITCH: { min: 150, max: 200, probability: 0.03 }, - RAPID: { min: 5, max: 15, probability: 0.5 }, - SLOW: { min: 50, max: 80, probability: 0.15 }, - STUTTER: { delay: 200, probability: 0.02 }, +// Simplified constants +const TYPING_PATTERNS = { + GLITCH: { delay: 150, probability: 0.03 }, + RAPID: { delay: 10, probability: 0.3 }, + SLOW: { delay: 60, probability: 0.1 }, } as const; @Component({ @@ -35,24 +38,11 @@ const TYPING_DELAYS = { changeDetection: ChangeDetectionStrategy.OnPush, animations: [ trigger('textChange', [ - state( - 'visible', - style({ - opacity: 1, - transform: 'translateY(0)', - }), - ), - state( - 'hidden', - style({ - opacity: 0, - transform: 'translateY(20px)', - }), - ), + state('visible', style({ opacity: 1, transform: 'translateY(0)' })), + state('hidden', style({ opacity: 0, transform: 'translateY(20px)' })), transition('visible => hidden', animate('300ms ease-out')), transition('hidden => visible', animate('300ms ease-in')), ]), - // Add fade-in animation for new text items trigger('slideIn', [ transition(':enter', [ style({ opacity: 0, transform: 'translateY(10px)' }), @@ -65,7 +55,9 @@ const TYPING_DELAYS = { ], }) export class HeaderTextAnimateSectionComponent implements OnInit, OnDestroy { - // Input signals with better defaults + private readonly destroyRef = inject(DestroyRef); + + // Input signals phrases = inputinfocard works!
diff --git a/src/app/components/infocard/infocard.component.ts b/src/app/components/infocard/infocard.component.ts deleted file mode 100644 index c01012b..0000000 --- a/src/app/components/infocard/infocard.component.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { Component } from '@angular/core'; - -@Component({ - selector: 'app-infocard', - imports: [], - templateUrl: './infocard.component.html', - styleUrl: './infocard.component.css' -}) -export class InfocardComponent { - -} diff --git a/src/app/components/long-button/long-button.component.css b/src/app/components/long-button/long-button.component.css deleted file mode 100644 index e69de29..0000000 diff --git a/src/app/components/long-button/long-button.component.html b/src/app/components/long-button/long-button.component.html deleted file mode 100644 index 891b15d..0000000 --- a/src/app/components/long-button/long-button.component.html +++ /dev/null @@ -1 +0,0 @@ -long-button works!
diff --git a/src/app/components/long-button/long-button.component.ts b/src/app/components/long-button/long-button.component.ts deleted file mode 100644 index 9579960..0000000 --- a/src/app/components/long-button/long-button.component.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { Component } from '@angular/core'; - -@Component({ - selector: 'app-long-button', - imports: [], - templateUrl: './long-button.component.html', - styleUrl: './long-button.component.css' -}) -export class LongButtonComponent { - -} diff --git a/src/app/pages/about/about.component.css b/src/app/pages/about/about.component.css index 4bf9d26..1ca6353 100644 --- a/src/app/pages/about/about.component.css +++ b/src/app/pages/about/about.component.css @@ -1,551 +1,248 @@ -/* ===================== NEURAL PROFILE SECTION ANIMATIONS ===================== */ +/* ===================== NEURAL PROFILE ANIMATIONS ===================== */ -/* Section Container Animation */ +/* Core keyframes - reused across components */ +@keyframes slideInLeft { + from { + opacity: 0; + transform: translate3d(-30px, 0, 0); + } + to { + opacity: 1; + transform: translate3d(0, 0, 0); + } +} + +@keyframes slideInRight { + from { + opacity: 0; + transform: translate3d(30px, 0, 0); + } + to { + opacity: 1; + transform: translate3d(0, 0, 0); + } +} + +@keyframes slideInUp { + from { + opacity: 0; + transform: translate3d(0, 30px, 0); + } + to { + opacity: 1; + transform: translate3d(0, 0, 0); + } +} + +@keyframes scaleIn { + from { + opacity: 0; + transform: scale(0.95); + } + to { + opacity: 1; + transform: scale(1); + } +} + +@keyframes fadeIn { + from { opacity: 0; } + to { opacity: 1; } +} + +/* ===================== NEURAL SECTION ELEMENTS ===================== */ + +/* Section container - no animation */ .neural-section { - opacity: 0; - transform: translateY(30px); - animation: neuralSectionMaterialize 1.2s ease-out 0.1s forwards; + opacity: 1; } -@keyframes neuralSectionMaterialize { - 0% { - opacity: 0; - transform: translateY(30px); - filter: blur(2px); - } - 60% { - opacity: 0.8; - transform: translateY(-5px); - filter: blur(0.5px); - } - 100% { - opacity: 1; - transform: translateY(0); - filter: blur(0px); - } -} - -/* Header Section Animation */ +/* Header - no animation */ .neural-header { - opacity: 0; - transform: translateY(-20px); - animation: neuralHeaderSlideDown 1s ease-out 0.3s forwards; + opacity: 1; } -@keyframes neuralHeaderSlideDown { - 0% { - opacity: 0; - transform: translateY(-20px); - filter: blur(1px); - } - 70% { - opacity: 0.9; - transform: translateY(3px); - filter: blur(0.3px); - } - 100% { - opacity: 1; - transform: translateY(0); - filter: blur(0px); - } -} - -/* Divider Line Animation */ +/* Divider - static */ .neural-divider { - opacity: 0; - transform: scaleX(0); - transform-origin: left; - animation: neuralDividerExpand 0.8s ease-out 0.8s forwards; + opacity: 0.5; } -@keyframes neuralDividerExpand { - 0% { - opacity: 0; - transform: scaleX(0); - } - 50% { - opacity: 0.3; - transform: scaleX(0.7); - } - 100% { - opacity: 0.5; - transform: scaleX(1); - } -} - -/* Main Content Grid Animation */ +/* Main content grid */ .neural-content-grid { - opacity: 0; - transform: translateY(40px); - animation: neuralContentReveal 1.2s ease-out 0.5s forwards; + opacity: 0; + animation: slideInUp 0.8s ease-out 0.3s forwards; } -@keyframes neuralContentReveal { - 0% { - opacity: 0; - transform: translateY(40px); - } - 100% { - opacity: 1; - transform: translateY(0); - } -} - -/* Left Panel - Neural Profile Tree Animation */ +/* Left panel */ .neural-left-panel { - opacity: 0; - transform: translateX(-40px) scale(0.95); - animation: neuralLeftPanelSlideIn 1.2s ease-out 0.7s forwards; + opacity: 0; + animation: slideInLeft 0.8s ease-out 0.5s forwards; } -@keyframes neuralLeftPanelSlideIn { - 0% { - opacity: 0; - transform: translateX(-40px) scale(0.95); - border-color: transparent; - background-color: rgba(41, 41, 37, 0); - } - 40% { - opacity: 0.6; - transform: translateX(-10px) scale(0.98); - border-color: var(--color-nier-border); - } - 80% { - opacity: 0.9; - transform: translateX(2px) scale(1.01); - background-color: var(--color-nier-bg); - } - 100% { - opacity: 1; - transform: translateX(0) scale(1); - border-color: var(--color-nier-border); - background-color: var(--color-nier-bg); - } -} - -/* Neural Profile Tree Content */ +/* Tree content */ .neural-tree-container { - opacity: 0; - transform: translateY(20px); - animation: neuralTreeContentFadeUp 0.8s ease-out 1.2s forwards; + opacity: 0; + animation: slideInUp 0.6s ease-out 0.8s forwards; } -@keyframes neuralTreeContentFadeUp { - 0% { - opacity: 0; - transform: translateY(20px); - } - 100% { - opacity: 1; - transform: translateY(0); - } -} - -/* Right Panel Container Animation */ +/* Right panel */ .neural-right-panel { - opacity: 0; - transform: translateX(40px); - animation: neuralRightPanelSlideIn 1s ease-out 0.9s forwards; + opacity: 0; + animation: slideInRight 0.8s ease-out 0.6s forwards; } -@keyframes neuralRightPanelSlideIn { - 0% { - opacity: 0; - transform: translateX(40px); - } - 100% { - opacity: 1; - transform: translateX(0); - } -} - -/* Video Display Panel Animation */ +/* Video panel */ .neural-video-panel { - opacity: 0; - transform: scale(0.95) translateY(20px); - animation: neuralVideoPanelMaterialize 1.2s ease-out 1.1s forwards; + opacity: 0; + animation: scaleIn 0.8s ease-out 0.7s forwards; } -@keyframes neuralVideoPanelMaterialize { - 0% { - opacity: 0; - transform: scale(0.95) translateY(20px); - border-color: transparent; - background-color: rgba(41, 41, 37, 0); - } - 30% { - opacity: 0.4; - transform: scale(0.98) translateY(10px); - border-color: var(--color-nier-accent); - } - 70% { - opacity: 0.8; - transform: scale(1.01) translateY(-2px); - background-color: var(--color-nier-dark); - } - 100% { - opacity: 1; - transform: scale(1) translateY(0); - border-color: var(--color-nier-accent); - background-color: var(--color-nier-dark); - } -} - -/* Video Container Inner Animation */ +/* Video inner container */ .neural-video-inner { - opacity: 0; - transform: scale(0.9); - animation: neuralVideoInnerExpand 0.8s ease-out 1.5s forwards; + opacity: 0; + animation: scaleIn 0.6s ease-out 1.0s forwards; } -@keyframes neuralVideoInnerExpand { - 0% { - opacity: 0; - transform: scale(0.9); - border-color: transparent; - background-color: rgba(0, 0, 0, 0); - } - 60% { - opacity: 0.7; - transform: scale(1.02); - border-color: rgba(255, 201, 102, 0.3); - } - 100% { - opacity: 1; - transform: scale(1); - border-color: rgba(255, 201, 102, 0.5); - background-color: rgba(41, 41, 37, 0.1); - } -} - -/* Holo Video Component Animation */ +/* Holo video */ .neural-holo-video { - opacity: 0; - animation: neuralHoloVideoFadeIn 1s ease-out 1.8s forwards; + opacity: 0; + animation: fadeIn 0.8s ease-out 1.2s forwards; } -@keyframes neuralHoloVideoFadeIn { - 0% { - opacity: 0; - filter: blur(2px) brightness(0.5); - } - 50% { - opacity: 0.7; - filter: blur(1px) brightness(0.8); - } - 100% { - opacity: 1; - filter: blur(0px) brightness(1); - } -} - -/* Status Panel Animation */ +/* Status panel */ .neural-status-panel { - opacity: 0; - transform: translateY(30px) scale(0.95); - animation: neuralStatusPanelRise 1s ease-out 1.3s forwards; + opacity: 0; + animation: slideInUp 0.8s ease-out 0.9s forwards; } -@keyframes neuralStatusPanelRise { - 0% { - opacity: 0; - transform: translateY(30px) scale(0.95); - border-color: transparent; - background-color: rgba(41, 41, 37, 0); - } - 40% { - opacity: 0.6; - transform: translateY(10px) scale(0.98); - border-color: var(--color-nier-border); - } - 80% { - opacity: 0.9; - transform: translateY(-2px) scale(1.01); - background-color: var(--color-nier-mid); - } - 100% { - opacity: 1; - transform: translateY(0) scale(1); - border-color: var(--color-nier-border); - background-color: var(--color-nier-mid); - } -} - -/* Status Grid Content Animation */ +/* Status grid */ .neural-status-grid { - opacity: 0; - transform: translateY(15px); - animation: neuralStatusGridFadeUp 0.6s ease-out 1.7s forwards; + opacity: 0; + animation: slideInUp 0.5s ease-out 1.2s forwards; } -@keyframes neuralStatusGridFadeUp { - 0% { - opacity: 0; - transform: translateY(15px); - } - 100% { - opacity: 1; - transform: translateY(0); - } -} - -/* Status Items Staggered Animation */ +/* Status items */ .neural-status-item { - opacity: 0; - transform: translateX(-15px); - animation: neuralStatusItemSlideIn 0.5s ease-out forwards; + opacity: 0; + animation: slideInLeft 0.4s ease-out forwards; } -.neural-status-item:nth-child(1) { animation-delay: 1.9s; } -.neural-status-item:nth-child(2) { animation-delay: 2.1s; } +.neural-status-item:nth-child(1) { animation-delay: 1.4s; } +.neural-status-item:nth-child(2) { animation-delay: 1.5s; } -@keyframes neuralStatusItemSlideIn { - 0% { - opacity: 0; - transform: translateX(-15px); - } - 100% { - opacity: 1; - transform: translateX(0); - } -} - -/* Status Labels Animation */ +/* Status labels */ .neural-status-label { - opacity: 0; - transform: translateY(-5px); - animation: neuralStatusLabelFadeDown 0.4s ease-out forwards; + opacity: 0; + animation: fadeIn 0.3s ease-out forwards; } -.neural-status-item:nth-child(1) .neural-status-label { animation-delay: 2.0s; } -.neural-status-item:nth-child(2) .neural-status-label { animation-delay: 2.2s; } +.neural-status-item:nth-child(1) .neural-status-label { animation-delay: 1.5s; } +.neural-status-item:nth-child(2) .neural-status-label { animation-delay: 1.6s; } -@keyframes neuralStatusLabelFadeDown { - 0% { - opacity: 0; - transform: translateY(-5px); - } - 100% { - opacity: 0.6; - transform: translateY(0); - } -} - -/* Status Values Animation */ +/* Status values */ .neural-status-value { - opacity: 0; - transform: scale(0.9); - animation: neuralStatusValuePop 0.4s ease-out forwards; + opacity: 0; + animation: scaleIn 0.3s ease-out forwards; } -.neural-status-item:nth-child(1) .neural-status-value { animation-delay: 2.1s; } -.neural-status-item:nth-child(2) .neural-status-value { animation-delay: 2.3s; } +.neural-status-item:nth-child(1) .neural-status-value { animation-delay: 1.6s; } +.neural-status-item:nth-child(2) .neural-status-value { animation-delay: 1.7s; } -@keyframes neuralStatusValuePop { - 0% { - opacity: 0; - transform: scale(0.9); - } - 50% { - opacity: 0.8; - transform: scale(1.05); - } - 100% { - opacity: 1; - transform: scale(1); - } -} - -/* Pulse Animation for Status Indicator */ +/* Pulse dot */ .neural-pulse-dot { - opacity: 0; - animation: neuralPulseDotAppear 0.6s ease-out forwards; + opacity: 0; + animation: scaleIn 0.4s ease-out forwards; } -.neural-status-item:nth-child(1) .neural-pulse-dot { animation-delay: 2.2s; } +.neural-status-item:nth-child(1) .neural-pulse-dot { animation-delay: 1.7s; } -@keyframes neuralPulseDotAppear { - 0% { - opacity: 0; - transform: scale(0.5); - } - 50% { - opacity: 0.8; - transform: scale(1.2); - } - 100% { - opacity: 1; - transform: scale(1); - } -} +/* ===================== BACKGROUND ELEMENTS ===================== */ -/* Background Checkered Pattern Enhancement */ +/* Checkered background - static */ .neural-checkered-bg { - position: relative; + position: relative; } .neural-checkered-bg::before { - content: ''; - position: absolute; - top: 0; - left: 0; - right: 0; - bottom: 0; - opacity: 0; - background-image: - linear-gradient(45deg, rgba(255, 201, 102, 0.01) 25%, transparent 25%), - linear-gradient(-45deg, rgba(255, 201, 102, 0.01) 25%, transparent 25%), - linear-gradient(45deg, transparent 75%, rgba(255, 201, 102, 0.01) 75%), - linear-gradient(-45deg, transparent 75%, rgba(255, 201, 102, 0.01) 75%); - background-size: 30px 30px; - background-position: 0 0, 0 15px, 15px -15px, -15px 0px; - animation: neuralBgPatternFadeIn 3s ease-out 0.2s forwards; - pointer-events: none; - z-index: 0; + content: ''; + position: absolute; + inset: 0; + opacity: 1; + background-image: + linear-gradient(45deg, rgba(255, 201, 102, 0.01) 25%, transparent 25%), + linear-gradient(-45deg, rgba(255, 201, 102, 0.01) 25%, transparent 25%), + linear-gradient(45deg, transparent 75%, rgba(255, 201, 102, 0.01) 75%), + linear-gradient(-45deg, transparent 75%, rgba(255, 201, 102, 0.01) 75%); + background-size: 30px 30px; + background-position: 0 0, 0 15px, 15px -15px, -15px 0px; + pointer-events: none; + z-index: 0; } -@keyframes neuralBgPatternFadeIn { - 0% { - opacity: 0; - } - 100% { - opacity: 1; - } -} - -/* Scanning Line Effect for Neural Section */ +/* Scan line - hidden */ .neural-scan-line { - position: absolute; - top: 0; - left: -100%; - width: 100%; - height: 2px; - background: linear-gradient(90deg, - transparent, - rgba(255, 201, 102, 0.6), - transparent - ); - animation: neuralScanLine 4s ease-in-out 1s forwards; - z-index: 1; -} - -@keyframes neuralScanLine { - 0% { - left: -100%; - opacity: 0; - } - 20% { - opacity: 1; - } - 80% { - opacity: 1; - } - 100% { - left: 100%; - opacity: 0; - } + display: none; } /* ===================== RESPONSIVE DESIGN ===================== */ @media (max-width: 1024px) { - /* Faster animations on tablets */ - .neural-section { - animation-duration: 1s; - } - - .neural-left-panel, - .neural-video-panel { - animation-duration: 1s; - } - - .neural-right-panel { - animation-duration: 0.8s; - animation-delay: 0.7s; - } + .neural-content-grid, + .neural-left-panel, + .neural-video-panel, + .neural-right-panel { + animation-duration: 0.6s; + } } @media (max-width: 768px) { - /* Much faster animations on mobile */ - .neural-section { - animation-duration: 0.8s; - animation-delay: 0.05s; - } + .neural-content-grid { animation-delay: 0.2s; } + .neural-left-panel { animation-delay: 0.3s; } + .neural-right-panel { animation-delay: 0.4s; } + .neural-video-panel { animation-delay: 0.5s; } + .neural-status-panel { animation-delay: 0.6s; } - .neural-header { - animation-duration: 0.6s; - animation-delay: 0.2s; - } + .neural-status-item:nth-child(1) { animation-delay: 0.8s; } + .neural-status-item:nth-child(2) { animation-delay: 0.9s; } - .neural-divider { - animation-duration: 0.5s; - animation-delay: 0.4s; - } + .neural-status-item:nth-child(1) .neural-status-label { animation-delay: 0.9s; } + .neural-status-item:nth-child(2) .neural-status-label { animation-delay: 1.0s; } - .neural-content-grid { - animation-duration: 0.8s; - animation-delay: 0.3s; - } + .neural-status-item:nth-child(1) .neural-status-value { animation-delay: 1.0s; } + .neural-status-item:nth-child(2) .neural-status-value { animation-delay: 1.1s; } - .neural-left-panel { - animation-duration: 0.8s; - animation-delay: 0.4s; - } + .neural-status-item:nth-child(1) .neural-pulse-dot { animation-delay: 1.1s; } - .neural-right-panel { - animation-duration: 0.6s; - animation-delay: 0.5s; - } + /* Faster animations on mobile */ + .neural-content-grid, + .neural-left-panel, + .neural-right-panel, + .neural-video-panel, + .neural-status-panel { + animation-duration: 0.5s; + } - .neural-video-panel { - animation-duration: 0.8s; - animation-delay: 0.6s; - } + .neural-tree-container, + .neural-video-inner, + .neural-holo-video, + .neural-status-grid { + animation-duration: 0.4s; + } - .neural-status-panel { - animation-duration: 0.6s; - animation-delay: 0.7s; - } - - .neural-status-item:nth-child(1) { animation-delay: 0.9s; } - .neural-status-item:nth-child(2) { animation-delay: 1.0s; } - - .neural-status-item:nth-child(1) .neural-status-label { animation-delay: 1.0s; } - .neural-status-item:nth-child(2) .neural-status-label { animation-delay: 1.1s; } - - .neural-status-item:nth-child(1) .neural-status-value { animation-delay: 1.1s; } - .neural-status-item:nth-child(2) .neural-status-value { animation-delay: 1.2s; } - - .neural-status-item:nth-child(1) .neural-pulse-dot { animation-delay: 1.2s; } + .neural-status-item, + .neural-status-label, + .neural-status-value, + .neural-pulse-dot { + animation-duration: 0.3s; + } } /* ===================== ACCESSIBILITY ===================== */ -/* Respect reduced motion preferences */ @media (prefers-reduced-motion: reduce) { - .neural-section, - .neural-header, - .neural-divider, - .neural-content-grid, - .neural-left-panel, - .neural-tree-container, - .neural-right-panel, - .neural-video-panel, - .neural-video-inner, - .neural-holo-video, - .neural-status-panel, - .neural-status-grid, - .neural-status-item, - .neural-status-label, - .neural-status-value, - .neural-pulse-dot { - animation: none !important; - opacity: 1 !important; - transform: none !important; - } - - .neural-checkered-bg::before, - .neural-scan-line { - display: none !important; - } + *[class*="neural-"] { + animation: none !important; + opacity: 1 !important; + transform: none !important; + } } diff --git a/src/app/pages/about/neural-profile-tree/neural-profile-tree.component.css b/src/app/pages/about/neural-profile-tree/neural-profile-tree.component.css index 52f961f..b110685 100644 --- a/src/app/pages/about/neural-profile-tree/neural-profile-tree.component.css +++ b/src/app/pages/about/neural-profile-tree/neural-profile-tree.component.css @@ -1,4 +1,20 @@ -/* NieR-style hover effects for tree nodes */ +/* ===================== OPTIMIZED TREE & CHIP SYSTEM ===================== */ + +/* Core keyframes */ +@keyframes textGlitch { + 0%, 100% { transform: translateX(0); clip-path: inset(0); } + 10% { transform: translateX(-2px); clip-path: inset(0 0 40% 0); } + 20% { transform: translateX(1px); clip-path: inset(40% 0 0 0); } + 30% { transform: translateX(-1px); clip-path: inset(20% 0 20% 0); } +} + +@keyframes scanLine { + from { opacity: 0.5; left: -100%; } + to { opacity: 0; left: 100%; } +} + +/* ===================== TREE NODES ===================== */ + .tree-node-item { position: relative; overflow: hidden; @@ -6,7 +22,6 @@ border-bottom: 1px solid transparent; } -/* Underline effect */ .tree-node-item::before { content: ""; position: absolute; @@ -16,35 +31,11 @@ height: 1px; background-color: var(--color-nier-text-dark); transition: width 0.3s ease; - z-index: 0; } -/* Text color change on hover */ -.tree-node-item:hover { - color: var(--color-nier-text-dark); - border-bottom-color: var(--color-nier-text-dark); -} - -/* Underline animation on hover */ -.tree-node-item:hover::before { - width: 100%; -} - -/* Glitch effect on hover - target the text span directly */ -.tree-node-item:hover span.text-nier-dark { - animation: nier-text-glitch 0.6s ease; -} - -/* Alternative - target any span with text */ -.tree-node-item:hover span:last-of-type { - animation: nier-text-glitch 0.6s ease; -} - -/* Scan line for tree nodes */ .tree-node-item .scan-line { position: absolute; - top: 0; - left: -100%; + inset: 0 0 auto -100%; width: 100%; height: 1px; background-color: var(--color-nier-text-dark); @@ -53,16 +44,30 @@ pointer-events: none; } -.tree-node-item:hover .scan-line { - animation: nier-button-scan 0.3s ease forwards; +/* Tree node hover effects */ +.tree-node-item:hover { + color: var(--color-nier-text-dark); + border-bottom-color: var(--color-nier-text-dark); +} + +.tree-node-item:hover::before { + width: 100%; +} + +.tree-node-item:hover span:last-of-type { + animation: textGlitch 0.6s ease; +} + +.tree-node-item:hover .scan-line { + animation: scanLine 0.3s ease forwards; } -/* Active/click state for tree nodes */ .tree-node-item:active { transform: scale(0.98); } -/* NieR-style Chip Container System - NO INTERACTIONS */ +/* ===================== CHIP SYSTEM ===================== */ + .chip-container { position: relative; } @@ -70,33 +75,19 @@ .chip-parent { position: relative; padding: 16px; - border-radius: 0; - border: 1px solid rgba(0, 0, 0, 0.15); /* Very thin border */ - box-shadow: none; + border: 1px solid rgba(0, 0, 0, 0.15); overflow: hidden; - transition: transform 0.2s ease; /* Allow parent scaling */ + transition: transform 0.2s ease; } -/* Parent Chip Colors - Complete NieR Earth Palette */ -.tools-parent { - background: linear-gradient(135deg, #a67c63 0%, #8b6b52 100%); -} +/* Parent chip gradients */ +.tools-parent { background: linear-gradient(135deg, #a67c63, #8b6b52); } +.frontend-parent { background: linear-gradient(135deg, #b8916f, #a67c63); } +.backend-parent { background: linear-gradient(135deg, #c9b896, #b8916f); } +.database-parent { background: linear-gradient(135deg, #e6d7b8, #d4c4a0); } -.frontend-parent { - background: linear-gradient(135deg, #b8916f 0%, #a67c63 100%); -} - -.backend-parent { - background: linear-gradient(135deg, #c9b896 0%, #b8916f 100%); -} - -.database-parent { - background: linear-gradient(135deg, #e6d7b8 0%, #d4c4a0 100%); -} - -/* Locked Parent Category */ .locked-parent { - background: linear-gradient(135deg, #606060 0%, #4a4a4a 100%); + background: linear-gradient(135deg, #606060, #4a4a4a); border: none; cursor: not-allowed; height: 100%; @@ -111,14 +102,12 @@ min-height: 0; } -/* Chip Cells Column Layout */ .chip-cells-column { display: flex; flex-direction: column; gap: 4px; } -/* Locked Content Styling */ .locked-content { display: flex; align-items: center; @@ -136,58 +125,46 @@ filter: drop-shadow(0 1px 2px rgba(0, 0, 0, 0.3)); } -/* Individual Chip Cells - Keep original grayish overlay */ +/* Chip cells */ .chip-cell { position: relative; width: 100%; height: 16px; - border-radius: 0; - border: 1px solid rgba(0, 0, 0, 0.2); /* Keep original border */ - background: rgba(0, 0, 0, 0.1); /* Keep original grayish overlay */ + border: 1px solid rgba(0, 0, 0, 0.2); + background: rgba(0, 0, 0, 0.1); cursor: pointer; overflow: hidden; - box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.2); /* Keep original shadow */ - transition: transform 0.2s ease; /* Allow scale animation */ + box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.2); + transition: transform 0.2s ease; } .chip-cell::before { content: ''; position: absolute; - top: 1px; - left: 1px; - right: 1px; + inset: 1px 1px auto 1px; height: 4px; - background: rgba(255, 255, 255, 0.1); /* Keep original highlight */ - border-radius: 0; + background: rgba(255, 255, 255, 0.1); } -/* Scale animation when tree node is hovered OR selected */ +/* Active states */ .chip-cell.cell-hovered, .chip-cell.cell-selected { transform: scale(1.05) !important; - background: rgba(0, 0, 0, 0.1) !important; /* Keep original grayish color */ - border: 1px solid rgba(0, 0, 0, 0.2) !important; /* Keep original border */ - box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.2) !important; /* Keep original shadow */ } -/* Parent categories scale when children are hovered OR selected */ .chip-container.chip-hovered .chip-parent, .chip-container.chip-selected .chip-parent { transform: scale(1.02) !important; } -/* NO direct hover effects on cells */ +/* Prevent direct chip interactions */ .chip-cell:hover, .chip-cell:focus, .chip-cell:active { - background: rgba(0, 0, 0, 0.1) !important; /* Keep original grayish */ - border: 1px solid rgba(0, 0, 0, 0.2) !important; /* Keep original border */ - box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.2) !important; /* Keep original shadow */ + background: rgba(0, 0, 0, 0.1) !important; + border: 1px solid rgba(0, 0, 0, 0.2) !important; + box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.2) !important; transform: none !important; - filter: none !important; - opacity: 1 !important; - animation: none !important; - transition: none !important; } .chip-cell:hover::before, @@ -195,60 +172,12 @@ .chip-cell:active::before, .chip-cell.cell-hovered::before, .chip-cell.cell-selected::before { - background: rgba(255, 255, 255, 0.1) !important; /* Keep original highlight */ + background: rgba(255, 255, 255, 0.1) !important; height: 4px !important; - box-shadow: none !important; } -/* NO CONTAINER EFFECTS */ -.chip-normal, -.chip-selected, -.chip-hovered { - /* No effects */ -} +/* ===================== RESPONSIVE DESIGN ===================== */ -/* Text glitch effect for tree nodes only */ -@keyframes nier-text-glitch { - 0%, 100% { - transform: translateX(0); - clip-path: inset(0 0 0 0); - } - 10% { - transform: translateX(-2px); - clip-path: inset(0 0 40% 0); - } - 15% { - transform: translateX(0); - } - 20% { - transform: translateX(1px); - clip-path: inset(40% 0 0 0); - } - 25% { - transform: translateX(0); - } - 30% { - transform: translateX(-1px); - clip-path: inset(20% 0 20% 0); - } - 35% { - transform: translateX(0); - } -} - -/* Scan line animation for tree nodes only */ -@keyframes nier-button-scan { - 0% { - opacity: 0.5; - left: -100%; - } - 100% { - opacity: 0; - left: 100%; - } -} - -/* Responsive Adjustments */ @media (max-width: 768px) { .chip-cell { height: 12px; @@ -263,14 +192,14 @@ } } -/* Touch device optimization for tree nodes only */ +/* Touch devices */ @media (hover: none) { .tree-node-item:active::before { width: 100%; } .tree-node-item:active .scan-line { - animation: nier-button-scan 0.3s ease forwards; + animation: scanLine 0.3s ease forwards; } .tree-node-item:active { diff --git a/src/app/pages/contact/contact-content/contact-content.component.css b/src/app/pages/contact/contact-content/contact-content.component.css new file mode 100644 index 0000000..86c5833 --- /dev/null +++ b/src/app/pages/contact/contact-content/contact-content.component.css @@ -0,0 +1,248 @@ +/* ===================== CONTACT PAGE ANIMATIONS ===================== */ + +/* Core keyframes - reused across components */ +@keyframes slideInLeft { + from { + opacity: 0; + transform: translate3d(-30px, 0, 0); + } + to { + opacity: 1; + transform: translate3d(0, 0, 0); + } +} + +@keyframes slideInUp { + from { + opacity: 0; + transform: translate3d(0, 30px, 0); + } + to { + opacity: 1; + transform: translate3d(0, 0, 0); + } +} + +@keyframes scaleIn { + from { + opacity: 0; + transform: scale(0.95); + } + to { + opacity: 1; + transform: scale(1); + } +} + +@keyframes fadeIn { + from { opacity: 0; } + to { opacity: 1; } +} + +@keyframes scanLine { + from { transform: translate3d(-100%, 0, 0); opacity: 0; } + 50% { opacity: 1; } + to { transform: translate3d(100%, 0, 0); opacity: 0; } +} + +/* ===================== CONTACT ELEMENTS ===================== */ + +/* Content container */ +.contact-content-container { + opacity: 0; + animation: slideInUp 0.8s ease-out 0.4s forwards; +} + +/* Availability status */ +.availability-status { + opacity: 0; + animation: slideInLeft 0.6s ease-out 0.6s forwards; +} + +/* Status dot */ +.status-dot { + opacity: 0; + animation: scaleIn 0.4s ease-out 0.8s forwards; +} + +/* Contact grid */ +.contact-grid { + opacity: 0; + animation: slideInUp 0.8s ease-out 0.8s forwards; +} + +/* Contact items */ +.contact-item { + opacity: 0; + animation: scaleIn 0.8s ease-out forwards; + position: relative; +} + +.contact-item:nth-child(1) { animation-delay: 1.0s; } +.contact-item:nth-child(2) { animation-delay: 1.1s; } +.contact-item:nth-child(3) { animation-delay: 1.2s; } + +/* Scan line effect */ +.contact-item::before { + content: ''; + position: absolute; + top: 0; + left: -100%; + width: 100%; + height: 2px; + background: linear-gradient(90deg, transparent, var(--color-nier-accent), transparent); + animation: scanLine 1.5s ease-out forwards; + z-index: 20; +} + +.contact-item:nth-child(1)::before { animation-delay: 1.2s; } +.contact-item:nth-child(2)::before { animation-delay: 1.3s; } +.contact-item:nth-child(3)::before { animation-delay: 1.4s; } + +/* Contact icons */ +.contact-icon { + opacity: 0; + animation: scaleIn 0.5s ease-out forwards; +} + +.contact-item:nth-child(1) .contact-icon { animation-delay: 1.3s; } +.contact-item:nth-child(2) .contact-icon { animation-delay: 1.4s; } +.contact-item:nth-child(3) .contact-icon { animation-delay: 1.5s; } + +/* Contact labels and values */ +.contact-label, +.contact-value { + opacity: 0; + animation: slideInLeft 0.4s ease-out forwards; +} + +.contact-item:nth-child(1) .contact-label { animation-delay: 1.4s; } +.contact-item:nth-child(2) .contact-label { animation-delay: 1.5s; } +.contact-item:nth-child(3) .contact-label { animation-delay: 1.6s; } + +.contact-item:nth-child(1) .contact-value { animation-delay: 1.5s; } +.contact-item:nth-child(2) .contact-value { animation-delay: 1.6s; } +.contact-item:nth-child(3) .contact-value { animation-delay: 1.7s; } + +/* Info panels container */ +.info-panels-container { + opacity: 0; + animation: slideInUp 0.8s ease-out 1.4s forwards; +} + +/* Info panels */ +.info-panel { + opacity: 0; + animation: scaleIn 0.6s ease-out forwards; +} + +.info-panel:nth-child(1) { animation-delay: 1.6s; } +.info-panel:nth-child(2) { animation-delay: 1.7s; } + +/* Info panel titles and content */ +.info-panel-title { + opacity: 0; + animation: slideInLeft 0.4s ease-out forwards; +} + +.info-panel-content { + opacity: 0; + animation: fadeIn 0.4s ease-out forwards; +} + +.info-panel:nth-child(1) .info-panel-title { animation-delay: 1.8s; } +.info-panel:nth-child(2) .info-panel-title { animation-delay: 1.9s; } + +.info-panel:nth-child(1) .info-panel-content { animation-delay: 1.9s; } +.info-panel:nth-child(2) .info-panel-content { animation-delay: 2.0s; } + +/* ===================== HOVER EFFECTS ===================== */ + +.contact-item:hover { + transform: translateY(-3px); + box-shadow: + 0 8px 25px rgba(0, 0, 0, 0.2), + 0 0 20px rgba(90, 90, 80, 0.1); + transition: all 0.3s ease; +} + +.contact-item:hover .contact-icon { + transform: scale(1.05); + box-shadow: 0 0 15px rgba(90, 90, 80, 0.3); + transition: all 0.3s ease; +} + +/* ===================== BACKGROUND PATTERN ===================== */ + +.contact-content-container::before { + content: ''; + position: absolute; + inset: 0; + opacity: 1; + background-image: + linear-gradient(45deg, rgba(90, 90, 80, 0.02) 25%, transparent 25%), + linear-gradient(-45deg, rgba(90, 90, 80, 0.02) 25%, transparent 25%), + linear-gradient(45deg, transparent 75%, rgba(90, 90, 80, 0.02) 75%), + linear-gradient(-45deg, transparent 75%, rgba(90, 90, 80, 0.02) 75%); + background-size: 20px 20px; + background-position: 0 0, 0 10px, 10px -10px, -10px 0px; + pointer-events: none; + z-index: -1; +} + +/* ===================== RESPONSIVE DESIGN ===================== */ + +@media (max-width: 768px) { + .contact-content-container { animation-delay: 0.3s; } + .availability-status { animation-delay: 0.4s; } + .contact-grid { animation-delay: 0.6s; } + + .contact-item:nth-child(1) { animation-delay: 0.8s; } + .contact-item:nth-child(2) { animation-delay: 0.9s; } + \contact-item:nth-child(3) { animation-delay: 1.0s; } + + .info-panels-container { animation-delay: 1.2s; } + .info-panel:nth-child(1) { animation-delay: 1.3s; } + .info-panel:nth-child(2) { animation-delay: 1.4s; } + + /* Faster animations on mobile */ + .contact-content-container, + .availability-status, + .contact-grid, + .contact-item, + .info-panels-container, + .info-panel { + animation-duration: 0.6s; + } + + .status-dot, + .contact-icon, + .contact-label, + .contact-value, + .info-panel-title, + .info-panel-content { + animation-duration: 0.3s; + } +} + +/* ===================== ACCESSIBILITY ===================== */ + +@media (prefers-reduced-motion: reduce) { + *[class*="contact-"], + *[class*="info-"], + .availability-status, + .status-dot { + animation: none !important; + opacity: 1 !important; + transform: none !important; + } + + .contact-item::before, + .contact-content-container::before { + display: none !important; + } + + .contact-item:hover { + transform: none; + } +} diff --git a/src/app/pages/contact/contact-content/contact-content.component.html b/src/app/pages/contact/contact-content/contact-content.component.html new file mode 100644 index 0000000..0f91e4a --- /dev/null +++ b/src/app/pages/contact/contact-content/contact-content.component.html @@ -0,0 +1,99 @@ + +