From 4d44e470cc746ec404e8b4ffe8494568d98e2790 Mon Sep 17 00:00:00 2001 From: rail Date: Sat, 25 Oct 2025 23:33:24 +0300 Subject: [PATCH] Add modern animations and remove admin services UI/UX improvements: - Removed CI/CD and Container Management from public view (admin-only) - Added scroll-triggered fade-up animations for cards and tech items - Implemented parallax floating effect on hero background - Added animated gradient shift on title text - Created micro-interactions for all interactive elements Animation features: - Service cards: Subtle bounce on hover - Tech items: Scale + pulsing glow effect - Footer links: Animated underline slide-in - Hero badge: Floating + glowing pulse - Status badges: Subtle pulse animation - Section titles: Slide-in from top entrance Technical implementation: - Built IntersectionObserver in Rust for scroll animations - Added js-sys and DomTokenList web-sys features - CSS animations following 2024-2025 trends - Accessibility: Respects prefers-reduced-motion - Performance: GPU-accelerated transforms - Staggered delays for cascade effect Bundle size: ~25KB WASM + 10KB JS (still optimized) --- Cargo.toml | 7 + dist/railwayka_landing.d.ts | 4 + dist/railwayka_landing.js | 243 +++++++++++++++++++++++++--- dist/railwayka_landing_bg.wasm | Bin 19425 -> 26308 bytes dist/railwayka_landing_bg.wasm.d.ts | 4 + dist/style.css | 144 +++++++++++++++++ src/lib.rs | 62 ++++++- style.css | 144 +++++++++++++++++ 8 files changed, 579 insertions(+), 29 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 66cdbb1..4075c89 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,6 +9,7 @@ crate-type = ["cdylib"] [dependencies] wasm-bindgen = "0.2.104" wasm-bindgen-futures = "0.4.54" +js-sys = "0.3.81" [dependencies.web-sys] version = "0.3.81" @@ -20,6 +21,12 @@ features = [ "Node", "Window", "Location", + "IntersectionObserver", + "IntersectionObserverEntry", + "IntersectionObserverInit", + "DomRect", + "NodeList", + "DomTokenList", ] [profile.release] diff --git a/dist/railwayka_landing.d.ts b/dist/railwayka_landing.d.ts index 66b17eb..7accea5 100644 --- a/dist/railwayka_landing.d.ts +++ b/dist/railwayka_landing.d.ts @@ -8,6 +8,10 @@ export interface InitOutput { readonly memory: WebAssembly.Memory; readonly main: () => void; readonly __wbindgen_export_0: (a: number) => void; + readonly __wbindgen_export_1: (a: number, b: number) => number; + readonly __wbindgen_export_2: (a: number, b: number, c: number, d: number) => number; + readonly __wbindgen_export_3: WebAssembly.Table; + readonly __wbindgen_export_4: (a: number, b: number, c: number, d: number) => void; readonly __wbindgen_start: () => void; } diff --git a/dist/railwayka_landing.js b/dist/railwayka_landing.js index 9c1168b..12d6721 100644 --- a/dist/railwayka_landing.js +++ b/dist/railwayka_landing.js @@ -6,29 +6,6 @@ heap.push(undefined, null, true, false); function getObject(idx) { return heap[idx]; } -let heap_next = heap.length; - -function addHeapObject(obj) { - if (heap_next === heap.length) heap.push(heap.length + 1); - const idx = heap_next; - heap_next = heap[idx]; - - heap[idx] = obj; - return idx; -} - -function handleError(f, args) { - try { - return f.apply(this, args); - } catch (e) { - wasm.__wbindgen_export_0(addHeapObject(e)); - } -} - -function isLikeNone(x) { - return x === undefined || x === null; -} - let cachedUint8ArrayMemory0 = null; function getUint8ArrayMemory0() { @@ -59,6 +36,92 @@ function getStringFromWasm0(ptr, len) { return decodeText(ptr, len); } +let heap_next = heap.length; + +function addHeapObject(obj) { + if (heap_next === heap.length) heap.push(heap.length + 1); + const idx = heap_next; + heap_next = heap[idx]; + + heap[idx] = obj; + return idx; +} + +function handleError(f, args) { + try { + return f.apply(this, args); + } catch (e) { + wasm.__wbindgen_export_0(addHeapObject(e)); + } +} + +function isLikeNone(x) { + return x === undefined || x === null; +} + +let WASM_VECTOR_LEN = 0; + +const cachedTextEncoder = new TextEncoder(); + +if (!('encodeInto' in cachedTextEncoder)) { + cachedTextEncoder.encodeInto = function (arg, view) { + const buf = cachedTextEncoder.encode(arg); + view.set(buf); + return { + read: arg.length, + written: buf.length + }; + } +} + +function passStringToWasm0(arg, malloc, realloc) { + + if (realloc === undefined) { + const buf = cachedTextEncoder.encode(arg); + const ptr = malloc(buf.length, 1) >>> 0; + getUint8ArrayMemory0().subarray(ptr, ptr + buf.length).set(buf); + WASM_VECTOR_LEN = buf.length; + return ptr; + } + + let len = arg.length; + let ptr = malloc(len, 1) >>> 0; + + const mem = getUint8ArrayMemory0(); + + let offset = 0; + + for (; offset < len; offset++) { + const code = arg.charCodeAt(offset); + if (code > 0x7F) break; + mem[ptr + offset] = code; + } + + if (offset !== len) { + if (offset !== 0) { + arg = arg.slice(offset); + } + ptr = realloc(ptr, len, len = offset + arg.length * 3, 1) >>> 0; + const view = getUint8ArrayMemory0().subarray(ptr + offset, ptr + len); + const ret = cachedTextEncoder.encodeInto(arg, view); + + offset += ret.written; + ptr = realloc(ptr, len, offset, 1) >>> 0; + } + + WASM_VECTOR_LEN = offset; + return ptr; +} + +let cachedDataViewMemory0 = null; + +function getDataViewMemory0() { + if (cachedDataViewMemory0 === null || cachedDataViewMemory0.buffer.detached === true || (cachedDataViewMemory0.buffer.detached === undefined && cachedDataViewMemory0.buffer !== wasm.memory.buffer)) { + cachedDataViewMemory0 = new DataView(wasm.memory.buffer); + } + return cachedDataViewMemory0; +} + function dropObject(idx) { if (idx < 132) return; heap[idx] = heap_next; @@ -71,10 +134,48 @@ function takeObject(idx) { return ret; } +const CLOSURE_DTORS = (typeof FinalizationRegistry === 'undefined') + ? { register: () => {}, unregister: () => {} } + : new FinalizationRegistry( +state => { + wasm.__wbindgen_export_3.get(state.dtor)(state.a, state.b); +} +); + +function makeMutClosure(arg0, arg1, dtor, f) { + const state = { a: arg0, b: arg1, cnt: 1, dtor }; + const real = (...args) => { + + // First up with a closure we increment the internal reference + // count. This ensures that the Rust closure environment won't + // be deallocated while we're invoking it. + state.cnt++; + const a = state.a; + state.a = 0; + try { + return f(a, state.b, ...args); + } finally { + if (--state.cnt === 0) { + wasm.__wbindgen_export_3.get(state.dtor)(a, state.b); + CLOSURE_DTORS.unregister(state); + } else { + state.a = a; + } + } + }; + real.original = state; + CLOSURE_DTORS.register(real, state, state); + return real; +} + export function main() { wasm.main(); } +function __wbg_adapter_4(arg0, arg1, arg2, arg3) { + wasm.__wbindgen_export_4(arg0, arg1, addHeapObject(arg2), addHeapObject(arg3)); +} + const EXPECTED_RESPONSE_TYPES = new Set(['basic', 'cors', 'default']); async function __wbg_load(module, imports) { @@ -113,6 +214,9 @@ async function __wbg_load(module, imports) { function __wbg_get_imports() { const imports = {}; imports.wbg = {}; + imports.wbg.__wbg_add_4e0283c00f7ecabe = function() { return handleError(function (arg0, arg1, arg2) { + getObject(arg0).add(getStringFromWasm0(arg1, arg2)); + }, arguments) }; imports.wbg.__wbg_appendChild_87a6cc0aeb132c06 = function() { return handleError(function (arg0, arg1) { const ret = getObject(arg0).appendChild(getObject(arg1)); return addHeapObject(ret); @@ -125,6 +229,10 @@ function __wbg_get_imports() { const ret = getObject(arg0).call(getObject(arg1)); return addHeapObject(ret); }, arguments) }; + imports.wbg.__wbg_classList_61149e0de7c668c5 = function(arg0) { + const ret = getObject(arg0).classList; + return addHeapObject(ret); + }; imports.wbg.__wbg_createElement_4909dfa2011f2abe = function() { return handleError(function (arg0, arg1, arg2) { const ret = getObject(arg0).createElement(getStringFromWasm0(arg1, arg2)); return addHeapObject(ret); @@ -133,6 +241,37 @@ function __wbg_get_imports() { const ret = getObject(arg0).document; return isLikeNone(ret) ? 0 : addHeapObject(ret); }; + imports.wbg.__wbg_getAttribute_8bfaf67e99ed2ee3 = function(arg0, arg1, arg2, arg3) { + const ret = getObject(arg1).getAttribute(getStringFromWasm0(arg2, arg3)); + var ptr1 = isLikeNone(ret) ? 0 : passStringToWasm0(ret, wasm.__wbindgen_export_1, wasm.__wbindgen_export_2); + var len1 = WASM_VECTOR_LEN; + getDataViewMemory0().setInt32(arg0 + 4 * 1, len1, true); + getDataViewMemory0().setInt32(arg0 + 4 * 0, ptr1, true); + }; + imports.wbg.__wbg_get_0da715ceaecea5c8 = function(arg0, arg1) { + const ret = getObject(arg0)[arg1 >>> 0]; + return addHeapObject(ret); + }; + imports.wbg.__wbg_instanceof_Element_162e4334c7d6f450 = function(arg0) { + let result; + try { + result = getObject(arg0) instanceof Element; + } catch (_) { + result = false; + } + const ret = result; + return ret; + }; + imports.wbg.__wbg_instanceof_IntersectionObserverEntry_819e56422a481344 = function(arg0) { + let result; + try { + result = getObject(arg0) instanceof IntersectionObserverEntry; + } catch (_) { + result = false; + } + const ret = result; + return ret; + }; imports.wbg.__wbg_instanceof_Window_12d20d558ef92592 = function(arg0) { let result; try { @@ -143,13 +282,44 @@ function __wbg_get_imports() { const ret = result; return ret; }; + imports.wbg.__wbg_isIntersecting_31dfa252ee048a6f = function(arg0) { + const ret = getObject(arg0).isIntersecting; + return ret; + }; + imports.wbg.__wbg_item_e5c3452334bca83f = function(arg0, arg1) { + const ret = getObject(arg0).item(arg1 >>> 0); + return isLikeNone(ret) ? 0 : addHeapObject(ret); + }; + imports.wbg.__wbg_length_186546c51cd61acd = function(arg0) { + const ret = getObject(arg0).length; + return ret; + }; + imports.wbg.__wbg_length_e7f4a6e30ea139e7 = function(arg0) { + const ret = getObject(arg0).length; + return ret; + }; imports.wbg.__wbg_log_6c7b5f4f00b8ce3f = function(arg0) { console.log(getObject(arg0)); }; + imports.wbg.__wbg_new_19c25a3f2fa63a02 = function() { + const ret = new Object(); + return addHeapObject(ret); + }; imports.wbg.__wbg_newnoargs_254190557c45b4ec = function(arg0, arg1) { const ret = new Function(getStringFromWasm0(arg0, arg1)); return addHeapObject(ret); }; + imports.wbg.__wbg_newwithoptions_f6e0820321465a5c = function() { return handleError(function (arg0, arg1) { + const ret = new IntersectionObserver(getObject(arg0), getObject(arg1)); + return addHeapObject(ret); + }, arguments) }; + imports.wbg.__wbg_observe_d5620e0d99e20a09 = function(arg0, arg1) { + getObject(arg0).observe(getObject(arg1)); + }; + imports.wbg.__wbg_querySelectorAll_71b924f0e83d096b = function() { return handleError(function (arg0, arg1, arg2) { + const ret = getObject(arg0).querySelectorAll(getStringFromWasm0(arg1, arg2)); + return addHeapObject(ret); + }, arguments) }; imports.wbg.__wbg_setAttribute_d1baf9023ad5696f = function() { return handleError(function (arg0, arg1, arg2, arg3, arg4) { getObject(arg0).setAttribute(getStringFromWasm0(arg1, arg2), getStringFromWasm0(arg3, arg4)); }, arguments) }; @@ -162,6 +332,9 @@ function __wbg_get_imports() { imports.wbg.__wbg_settextContent_b55fe2f5f1399466 = function(arg0, arg1, arg2) { getObject(arg0).textContent = arg1 === 0 ? undefined : getStringFromWasm0(arg1, arg2); }; + imports.wbg.__wbg_setthreshold_7daca4126268ea47 = function(arg0, arg1) { + getObject(arg0).threshold = getObject(arg1); + }; imports.wbg.__wbg_static_accessor_GLOBAL_8921f820c2ce3f12 = function() { const ret = typeof global === 'undefined' ? null : global; return isLikeNone(ret) ? 0 : addHeapObject(ret); @@ -178,6 +351,19 @@ function __wbg_get_imports() { const ret = typeof window === 'undefined' ? null : window; return isLikeNone(ret) ? 0 : addHeapObject(ret); }; + imports.wbg.__wbg_target_161cb00cc3daf872 = function(arg0) { + const ret = getObject(arg0).target; + return addHeapObject(ret); + }; + imports.wbg.__wbg_wbindgencbdrop_eb10308566512b88 = function(arg0) { + const obj = getObject(arg0).original; + if (obj.cnt-- == 1) { + obj.a = 0; + return true; + } + const ret = false; + return ret; + }; imports.wbg.__wbg_wbindgenisundefined_c4b71d073b92f3c5 = function(arg0) { const ret = getObject(arg0) === undefined; return ret; @@ -193,6 +379,16 @@ function __wbg_get_imports() { const ret = getStringFromWasm0(arg0, arg1); return addHeapObject(ret); }; + imports.wbg.__wbindgen_cast_d6cd19b81560fd6e = function(arg0) { + // Cast intrinsic for `F64 -> Externref`. + const ret = arg0; + return addHeapObject(ret); + }; + imports.wbg.__wbindgen_cast_fe9164a03cdb0b6b = function(arg0, arg1) { + // Cast intrinsic for `Closure(Closure { dtor_idx: 1, function: Function { arguments: [NamedExternref("Array"), NamedExternref("IntersectionObserver")], shim_idx: 2, ret: Unit, inner_ret: Some(Unit) }, mutable: true }) -> Externref`. + const ret = makeMutClosure(arg0, arg1, 1, __wbg_adapter_4); + return addHeapObject(ret); + }; imports.wbg.__wbindgen_object_clone_ref = function(arg0) { const ret = getObject(arg0); return addHeapObject(ret); @@ -211,6 +407,7 @@ function __wbg_init_memory(imports, memory) { function __wbg_finalize_init(instance, module) { wasm = instance.exports; __wbg_init.__wbindgen_wasm_module = module; + cachedDataViewMemory0 = null; cachedUint8ArrayMemory0 = null; diff --git a/dist/railwayka_landing_bg.wasm b/dist/railwayka_landing_bg.wasm index 37de7d2d725d2cf182fa17334c62a0b67fa5d503..238df8f180319235ed4e95bec8c501369c9bd2d1 100644 GIT binary patch literal 26308 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 void; export const __wbindgen_export_0: (a: number) => void; +export const __wbindgen_export_1: (a: number, b: number) => number; +export const __wbindgen_export_2: (a: number, b: number, c: number, d: number) => number; +export const __wbindgen_export_3: WebAssembly.Table; +export const __wbindgen_export_4: (a: number, b: number, c: number, d: number) => void; export const __wbindgen_start: () => void; diff --git a/dist/style.css b/dist/style.css index 0f8af89..3ac623f 100644 --- a/dist/style.css +++ b/dist/style.css @@ -159,6 +159,8 @@ section { text-decoration: none; color: inherit; display: block; + overflow: hidden; + word-wrap: break-word; } .service-card:hover { @@ -173,12 +175,15 @@ section { justify-content: space-between; align-items: flex-start; margin-bottom: 0.75rem; + gap: 0.75rem; } .service-card h3 { font-size: 1.25rem; color: var(--text-primary); flex: 1; + word-break: break-word; + min-width: 0; } .status-badge { @@ -191,6 +196,8 @@ section { color: var(--text-secondary); font-size: 0.95rem; line-height: 1.6; + word-break: break-word; + overflow-wrap: break-word; } /* Tech Section */ @@ -297,3 +304,140 @@ html { background: var(--accent); color: white; } + +/* Scroll-triggered animations */ +[data-animate] { + opacity: 0; + transform: translateY(30px); + transition: opacity 0.6s ease-out, transform 0.6s ease-out; +} + +[data-animate].animate-in { + opacity: 1; + transform: translateY(0); +} + +/* Parallax effect on hero */ +.hero::before { + animation: parallax-float 20s ease-in-out infinite; +} + +@keyframes parallax-float { + 0%, 100% { transform: translateY(0) scale(1); } + 50% { transform: translateY(-20px) scale(1.05); } +} + +/* Animated gradient text */ +.hero-title { + background-size: 200% auto; + animation: gradient-shift 3s ease-in-out infinite; +} + +@keyframes gradient-shift { + 0%, 100% { + background-position: 0% 50%; + } + 50% { + background-position: 100% 50%; + } +} + +/* Hover micro-interactions */ +.service-card { + transform-origin: center; + will-change: transform; +} + +.service-card:hover { + animation: subtle-bounce 0.6s ease; +} + +@keyframes subtle-bounce { + 0%, 100% { transform: translateY(-4px); } + 50% { transform: translateY(-8px); } +} + +.tech-item { + transition: all 0.3s ease; +} + +.tech-item:hover { + transform: scale(1.05); +} + +.tech-item:hover h3 { + animation: pulse-glow 1.5s ease-in-out infinite; +} + +@keyframes pulse-glow { + 0%, 100% { opacity: 1; } + 50% { opacity: 0.8; text-shadow: 0 0 20px rgba(59, 130, 246, 0.5); } +} + +/* Footer links animation */ +.footer-links a { + position: relative; + overflow: hidden; +} + +.footer-links a::before { + content: ''; + position: absolute; + bottom: 0; + left: -100%; + width: 100%; + height: 2px; + background: var(--accent); + transition: left 0.3s ease; +} + +.footer-links a:hover::before { + left: 0; +} + +/* Hero badge pulse */ +.hero-badge { + animation: float 3s ease-in-out infinite, glow-pulse 2s ease-in-out infinite; +} + +@keyframes glow-pulse { + 0%, 100% { box-shadow: 0 0 10px rgba(59, 130, 246, 0.2); } + 50% { box-shadow: 0 0 20px rgba(59, 130, 246, 0.4); } +} + +/* Status badge animations */ +.status-badge { + animation: badge-pulse 2s ease-in-out infinite; +} + +@keyframes badge-pulse { + 0%, 100% { transform: scale(1); } + 50% { transform: scale(1.1); } +} + +/* Section title entrance */ +.section-title { + animation: slide-in-top 0.8s ease-out; +} + +@keyframes slide-in-top { + from { + opacity: 0; + transform: translateY(-30px); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +/* Reduce motion for accessibility */ +@media (prefers-reduced-motion: reduce) { + *, + *::before, + *::after { + animation-duration: 0.01ms !important; + animation-iteration-count: 1 !important; + transition-duration: 0.01ms !important; + } +} diff --git a/src/lib.rs b/src/lib.rs index 9658e58..fceac10 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,5 +1,6 @@ use wasm_bindgen::prelude::*; -use web_sys::{console, Document, Element, HtmlElement, Window}; +use wasm_bindgen::JsCast; +use web_sys::{console, Document, Element, HtmlElement, Window, IntersectionObserver, IntersectionObserverEntry, IntersectionObserverInit}; #[wasm_bindgen(start)] pub fn main() -> Result<(), JsValue> { @@ -12,6 +13,7 @@ pub fn main() -> Result<(), JsValue> { if let Some(body) = document.body() { body.set_inner_html(""); build_page(&document, &body)?; + setup_scroll_animations(&document)?; } console::log_1(&"✅ Landing page ready!".into()); @@ -83,12 +85,12 @@ fn create_services_section(document: &Document) -> Result { // Live services let services_live = vec![ ("Git Repository", "Gitea - Self-hosted Git server with web UI", "🔴", "https://git.dc.railwayka.ru"), - ("CI/CD", "Woodpecker CI - Automated builds and deployments", "🔴", "https://cicd.dc.railwayka.ru"), - ("Container Management", "Portainer - Docker management interface", "🔴", "https://port.dc.railwayka.ru"), ]; - for (name, desc, status, url) in services_live { + for (i, (name, desc, status, url)) in services_live.iter().enumerate() { let card = create_service_card(document, name, desc, status, Some(url))?; + card.set_attribute("data-animate", "fade-up")?; + card.set_attribute("data-delay", &format!("{}", i * 100))?; grid.append_child(&card)?; } @@ -101,8 +103,10 @@ fn create_services_section(document: &Document) -> Result { ("Monitoring", "Grafana + Prometheus - Infrastructure monitoring", "🔵"), ]; - for (name, desc, status) in services_planned { + for (i, (name, desc, status)) in services_planned.iter().enumerate() { let card = create_service_card(document, name, desc, status, None)?; + card.set_attribute("data-animate", "fade-up")?; + card.set_attribute("data-delay", &format!("{}", (i + services_live.len()) * 100))?; grid.append_child(&card)?; } @@ -177,8 +181,10 @@ fn create_tech_section(document: &Document) -> Result { ("Rust + WASM", "High-performance web applications"), ]; - for (name, desc) in technologies { + for (i, (name, desc)) in technologies.iter().enumerate() { let item = create_tech_item(document, name, desc)?; + item.set_attribute("data-animate", "fade-up")?; + item.set_attribute("data-delay", &format!("{}", i * 80))?; grid.append_child(&item)?; } @@ -238,3 +244,47 @@ fn create_footer(document: &Document) -> Result { Ok(footer) } + +fn setup_scroll_animations(document: &Document) -> Result<(), JsValue> { + // Create intersection observer callback + let callback = Closure::wrap(Box::new(move |entries: js_sys::Array, _observer: IntersectionObserver| { + for entry in entries.iter() { + if let Ok(entry) = entry.dyn_into::() { + if entry.is_intersecting() { + if let Some(target) = entry.target().dyn_ref::() { + let delay = target.get_attribute("data-delay") + .and_then(|d| d.parse::().ok()) + .unwrap_or(0); + + target.class_list().add_1("animate-in").ok(); + target.set_attribute("style", &format!("animation-delay: {}ms", delay)).ok(); + } + } + } + } + }) as Box); + + // Create observer with options + let mut options = IntersectionObserverInit::new(); + options.set_threshold(&JsValue::from_f64(0.1)); + + let observer = IntersectionObserver::new_with_options( + callback.as_ref().unchecked_ref(), + &options, + )?; + + // Observe all elements with data-animate attribute + let elements = document.query_selector_all("[data-animate]")?; + for i in 0..elements.length() { + if let Some(node) = elements.item(i) { + if let Some(element) = node.dyn_ref::() { + observer.observe(element); + } + } + } + + // Prevent callback from being dropped + callback.forget(); + + Ok(()) +} diff --git a/style.css b/style.css index 0f8af89..3ac623f 100644 --- a/style.css +++ b/style.css @@ -159,6 +159,8 @@ section { text-decoration: none; color: inherit; display: block; + overflow: hidden; + word-wrap: break-word; } .service-card:hover { @@ -173,12 +175,15 @@ section { justify-content: space-between; align-items: flex-start; margin-bottom: 0.75rem; + gap: 0.75rem; } .service-card h3 { font-size: 1.25rem; color: var(--text-primary); flex: 1; + word-break: break-word; + min-width: 0; } .status-badge { @@ -191,6 +196,8 @@ section { color: var(--text-secondary); font-size: 0.95rem; line-height: 1.6; + word-break: break-word; + overflow-wrap: break-word; } /* Tech Section */ @@ -297,3 +304,140 @@ html { background: var(--accent); color: white; } + +/* Scroll-triggered animations */ +[data-animate] { + opacity: 0; + transform: translateY(30px); + transition: opacity 0.6s ease-out, transform 0.6s ease-out; +} + +[data-animate].animate-in { + opacity: 1; + transform: translateY(0); +} + +/* Parallax effect on hero */ +.hero::before { + animation: parallax-float 20s ease-in-out infinite; +} + +@keyframes parallax-float { + 0%, 100% { transform: translateY(0) scale(1); } + 50% { transform: translateY(-20px) scale(1.05); } +} + +/* Animated gradient text */ +.hero-title { + background-size: 200% auto; + animation: gradient-shift 3s ease-in-out infinite; +} + +@keyframes gradient-shift { + 0%, 100% { + background-position: 0% 50%; + } + 50% { + background-position: 100% 50%; + } +} + +/* Hover micro-interactions */ +.service-card { + transform-origin: center; + will-change: transform; +} + +.service-card:hover { + animation: subtle-bounce 0.6s ease; +} + +@keyframes subtle-bounce { + 0%, 100% { transform: translateY(-4px); } + 50% { transform: translateY(-8px); } +} + +.tech-item { + transition: all 0.3s ease; +} + +.tech-item:hover { + transform: scale(1.05); +} + +.tech-item:hover h3 { + animation: pulse-glow 1.5s ease-in-out infinite; +} + +@keyframes pulse-glow { + 0%, 100% { opacity: 1; } + 50% { opacity: 0.8; text-shadow: 0 0 20px rgba(59, 130, 246, 0.5); } +} + +/* Footer links animation */ +.footer-links a { + position: relative; + overflow: hidden; +} + +.footer-links a::before { + content: ''; + position: absolute; + bottom: 0; + left: -100%; + width: 100%; + height: 2px; + background: var(--accent); + transition: left 0.3s ease; +} + +.footer-links a:hover::before { + left: 0; +} + +/* Hero badge pulse */ +.hero-badge { + animation: float 3s ease-in-out infinite, glow-pulse 2s ease-in-out infinite; +} + +@keyframes glow-pulse { + 0%, 100% { box-shadow: 0 0 10px rgba(59, 130, 246, 0.2); } + 50% { box-shadow: 0 0 20px rgba(59, 130, 246, 0.4); } +} + +/* Status badge animations */ +.status-badge { + animation: badge-pulse 2s ease-in-out infinite; +} + +@keyframes badge-pulse { + 0%, 100% { transform: scale(1); } + 50% { transform: scale(1.1); } +} + +/* Section title entrance */ +.section-title { + animation: slide-in-top 0.8s ease-out; +} + +@keyframes slide-in-top { + from { + opacity: 0; + transform: translateY(-30px); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +/* Reduce motion for accessibility */ +@media (prefers-reduced-motion: reduce) { + *, + *::before, + *::after { + animation-duration: 0.01ms !important; + animation-iteration-count: 1 !important; + transition-duration: 0.01ms !important; + } +}