diff --git a/_static_server.js b/_static_server.js new file mode 100644 index 0000000..2a924cb --- /dev/null +++ b/_static_server.js @@ -0,0 +1,61 @@ +const http = require('http'); +const fs = require('fs'); +const path = require('path'); + +const root = __dirname; +const port = process.argv[2] ? parseInt(process.argv[2], 10) : 8181; + +const mime = { + '.html': 'text/html; charset=utf-8', + '.js': 'text/javascript; charset=utf-8', + '.css': 'text/css; charset=utf-8', + '.json': 'application/json; charset=utf-8', + '.png': 'image/png', + '.jpg': 'image/jpeg', + '.jpeg': 'image/jpeg', + '.gif': 'image/gif', + '.svg': 'image/svg+xml', + '.webp': 'image/webp', + '.mp4': 'video/mp4', + '.woff': 'font/woff', + '.woff2': 'font/woff2', + '.ttf': 'font/ttf', + '.ico': 'image/x-icon', +}; + +const server = http.createServer((req, res) => { + let reqPath = decodeURIComponent(req.url.split('?')[0]); + if (reqPath === '/') reqPath = '/index.html'; + let filePath = path.join(root, reqPath); + + if (!filePath.startsWith(root)) { + res.writeHead(403); + res.end('Forbidden'); + return; + } + + fs.stat(filePath, (err, stats) => { + if (err) { + res.writeHead(404); + res.end('Not found: ' + reqPath); + return; + } + if (stats.isDirectory()) { + filePath = path.join(filePath, 'index.html'); + } + fs.readFile(filePath, (err2, data) => { + if (err2) { + res.writeHead(404); + res.end('Not found: ' + reqPath); + return; + } + const ext = path.extname(filePath).toLowerCase(); + res.writeHead(200, { 'Content-Type': mime[ext] || 'application/octet-stream' }); + res.end(data); + }); + }); +}); + +server.listen(port, () => { + console.log(`Static server running at http://localhost:${port}/`); +}); diff --git a/assets/black-loafer.png b/assets/black-loafer.png new file mode 100644 index 0000000..8e18125 Binary files /dev/null and b/assets/black-loafer.png differ diff --git a/assets/car.svg b/assets/car.svg new file mode 100644 index 0000000..82c091b --- /dev/null +++ b/assets/car.svg @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/connected-systems/Shoe_trans.png b/assets/connected-systems/Shoe_trans.png new file mode 100644 index 0000000..e88b395 Binary files /dev/null and b/assets/connected-systems/Shoe_trans.png differ diff --git a/assets/connected-systems/banner.png b/assets/connected-systems/banner.png new file mode 100644 index 0000000..9b793bb Binary files /dev/null and b/assets/connected-systems/banner.png differ diff --git a/assets/connected-systems/connected-branch-new.svg b/assets/connected-systems/connected-branch-new.svg new file mode 100644 index 0000000..269ecf5 --- /dev/null +++ b/assets/connected-systems/connected-branch-new.svg @@ -0,0 +1,239 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/connected-systems/connected-branch.svg b/assets/connected-systems/connected-branch.svg new file mode 100644 index 0000000..30c08c2 --- /dev/null +++ b/assets/connected-systems/connected-branch.svg @@ -0,0 +1,172 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/connected-systems/icon-donation.svg b/assets/connected-systems/icon-donation.svg new file mode 100644 index 0000000..496a24d --- /dev/null +++ b/assets/connected-systems/icon-donation.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/assets/connected-systems/icon-dot.svg b/assets/connected-systems/icon-dot.svg new file mode 100644 index 0000000..3723c52 --- /dev/null +++ b/assets/connected-systems/icon-dot.svg @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/connected-systems/icon-marketplace.svg b/assets/connected-systems/icon-marketplace.svg new file mode 100644 index 0000000..80a37e1 --- /dev/null +++ b/assets/connected-systems/icon-marketplace.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/assets/connected-systems/icon-recondition.svg b/assets/connected-systems/icon-recondition.svg new file mode 100644 index 0000000..7b87291 --- /dev/null +++ b/assets/connected-systems/icon-recondition.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/assets/connected-systems/icon-recycling.svg b/assets/connected-systems/icon-recycling.svg new file mode 100644 index 0000000..2622fb5 --- /dev/null +++ b/assets/connected-systems/icon-recycling.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/assets/connected-systems/icon-resale.svg b/assets/connected-systems/icon-resale.svg new file mode 100644 index 0000000..19f2389 --- /dev/null +++ b/assets/connected-systems/icon-resale.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/assets/connected-systems/label-icon-condition.svg b/assets/connected-systems/label-icon-condition.svg new file mode 100644 index 0000000..9488d8f --- /dev/null +++ b/assets/connected-systems/label-icon-condition.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/assets/connected-systems/label-icon-market.svg b/assets/connected-systems/label-icon-market.svg new file mode 100644 index 0000000..b738c73 --- /dev/null +++ b/assets/connected-systems/label-icon-market.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/assets/connected-systems/label-icon-resale.svg b/assets/connected-systems/label-icon-resale.svg new file mode 100644 index 0000000..d7dcaf3 --- /dev/null +++ b/assets/connected-systems/label-icon-resale.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/assets/connected-systems/label-icon-value.svg b/assets/connected-systems/label-icon-value.svg new file mode 100644 index 0000000..d43af05 --- /dev/null +++ b/assets/connected-systems/label-icon-value.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/assets/connected-systems/shoe-clean.png b/assets/connected-systems/shoe-clean.png new file mode 100644 index 0000000..b804094 Binary files /dev/null and b/assets/connected-systems/shoe-clean.png differ diff --git a/assets/connected-systems/shoe-new.png b/assets/connected-systems/shoe-new.png new file mode 100644 index 0000000..df39dba Binary files /dev/null and b/assets/connected-systems/shoe-new.png differ diff --git a/assets/connected-systems/skiply-logo.png b/assets/connected-systems/skiply-logo.png new file mode 100644 index 0000000..1151245 Binary files /dev/null and b/assets/connected-systems/skiply-logo.png differ diff --git a/assets/red-loafer.png b/assets/red-loafer.png new file mode 100644 index 0000000..08445be Binary files /dev/null and b/assets/red-loafer.png differ diff --git a/assets/road-path-1.svg b/assets/road-path-1.svg new file mode 100644 index 0000000..1112b93 --- /dev/null +++ b/assets/road-path-1.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/road-path-2.svg b/assets/road-path-2.svg new file mode 100644 index 0000000..498cc82 --- /dev/null +++ b/assets/road-path-2.svg @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/road-path-3.svg b/assets/road-path-3.svg new file mode 100644 index 0000000..b28bd41 --- /dev/null +++ b/assets/road-path-3.svg @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/main/index.html b/main/index.html new file mode 100644 index 0000000..4e6bebe --- /dev/null +++ b/main/index.html @@ -0,0 +1,172 @@ + + + + + + + Shoe Scanner – Product Showcase + + + + + + +
+ + +
+
+
+
+ + +
+ + +
+

Red Statement Loafer

+
+
+
MSRP
+
$119
+
+
+
SIZE
+
USED
+
+
+
CONDITION
+
13
+
+
+
COLOR
+
Red
+
+
+
VALUE
+
$34
+
+
+
DEMAND
+
Seasonal/niche
+
+
+
CHANNEL
+
Donation
+
+
+
+ + +
+ +
+
+
+ + + + + + + +
+
+
+ + + + +
+ Red Statement Loafer +
+

Top image of both shoes

+
+ Scanning process +
+
+
+ 1 of 2 Captured +
+ + +
+ + +
+
+
+ + + + + + + +
+
+
+ + + + +
+ Black Classic Loafer +
+

Top image of both shoes

+
+ Scanning process +
+
+
+ 1 of 2 Captured +
+ + +
+
+ + +
+

Black Classic Loafer

+
+
+
MSRP
+
$119
+
+
+
SIZE
+
USED
+
+
+
CONDITION
+
10
+
+
+
COLOR
+
Black
+
+
+
VALUE
+
$91
+
+
+
DEMAND
+
Year-round
+
+
+
CHANNEL
+
Premium Resale
+
+
+
+ +
+
+ + + + + + + diff --git a/main/shoe-animation-gsap.js b/main/shoe-animation-gsap.js new file mode 100644 index 0000000..cdc4eb8 --- /dev/null +++ b/main/shoe-animation-gsap.js @@ -0,0 +1,83 @@ +/* ================================================ + Shoe Showcase – Interactive GSAP Animation + ================================================ + + - Uses GSAP for smooth easing and interpolation + ================================================ */ + +document.addEventListener('DOMContentLoaded', () => { + const showcase = document.getElementById('showcase'); + const bgRed = document.getElementById('bg-red'); + const bgBlack = document.getElementById('bg-black'); + const phoneRed = document.getElementById('phone-red'); + const phoneBlack = document.getElementById('phone-black'); + const contentLeft = document.getElementById('content-left'); + const contentRight = document.getElementById('content-right'); + + function clamp(v, lo, hi) { + return Math.min(hi, Math.max(lo, v)); + } + + function lerp(a, b, t) { + return a + (b - a) * t; + } + + function animateToRatio(ratio) { + // ratio is 0 (far left) to 1 (far right). Center is 0.5. + + // 1. Calculate flex values for background panels + const redFlex = lerp(3.5, 0.3, ratio); + const blackFlex = lerp(0.3, 3.5, ratio); + + // 2. Calculate bias for phones (+1 when red dominates, -1 when black dominates) + const bias = (0.5 - ratio) * 2; + + const redScale = 1 + clamp(bias, 0, 1) * 0.3; + const redOpacity = clamp(1 + bias * 3, 0, 1); + const redShiftX = clamp(bias, 0, 1) * 60; + + const blackScale = 1 + clamp(-bias, 0, 1) * 0.3; + const blackOpacity = clamp(1 - bias * 3, 0, 1); + const blackShiftX = clamp(-bias, 0, 1) * -60; + + // 3. Animate using GSAP + // Using overwrite: "auto" ensures that new mouse movements smoothly override old animations + const animConfig = { duration: 0.5, ease: "power2.out", overwrite: "auto" }; + + gsap.to(bgRed, { flexGrow: redFlex, ...animConfig }); + gsap.to(bgBlack, { flexGrow: blackFlex, ...animConfig }); + + gsap.to(phoneRed, { + scale: redScale, + x: redShiftX, + opacity: redOpacity, + ...animConfig + }); + + gsap.to(phoneBlack, { + scale: blackScale, + x: blackShiftX, + opacity: blackOpacity, + ...animConfig + }); + + gsap.to(contentLeft, { opacity: redOpacity, ...animConfig }); + gsap.to(contentRight, { opacity: blackOpacity, ...animConfig }); + } + + // Set initial center state + gsap.set(bgRed, { flexGrow: 1 }); + gsap.set(bgBlack, { flexGrow: 1 }); + + /* ---- Mouse tracking ---- */ + showcase.addEventListener('mousemove', (e) => { + const rect = showcase.getBoundingClientRect(); + const ratio = clamp((e.clientX - rect.left) / rect.width, 0, 1); + animateToRatio(ratio); + }); + + // Spring back to center on leave + showcase.addEventListener('mouseleave', () => { + animateToRatio(0.5); + }); +}); diff --git a/main/shoe-animation-vanilla.js b/main/shoe-animation-vanilla.js new file mode 100644 index 0000000..32550cd --- /dev/null +++ b/main/shoe-animation-vanilla.js @@ -0,0 +1,74 @@ +/* ================================================ + Shoe Showcase – Interactive Mouse-Driven Animation + ================================================ + - Background panels expand/contract based on mouse X + - Phone mockups fade in/out (only phones, text stays) + - On mouse leave → smoothly resets to 50/50 + ================================================ */ + +document.addEventListener('DOMContentLoaded', () => { + const showcase = document.getElementById('showcase'); + const bgRed = document.getElementById('bg-red'); + const bgBlack = document.getElementById('bg-black'); + const phoneRed = document.getElementById('phone-red'); + const phoneBlack = document.getElementById('phone-black'); + + let targetRatio = 0.5; + let currentRatio = 0.5; + const LERP_SPEED = 0.1; + + function lerp(a, b, t) { + return a + (b - a) * t; + } + + function clamp(v, lo, hi) { + return Math.min(hi, Math.max(lo, v)); + } + + /* ---- mouse tracking ---- */ + showcase.addEventListener('mousemove', (e) => { + const rect = showcase.getBoundingClientRect(); + targetRatio = clamp((e.clientX - rect.left) / rect.width, 0, 1); + }); + + showcase.addEventListener('mouseleave', () => { + targetRatio = 0.5; + }); + + /* ---- animation loop ---- */ + function animate() { + currentRatio = lerp(currentRatio, targetRatio, LERP_SPEED); + + const r = currentRatio; + + /* 1. Background panel flex (full-width decorative) */ + const redFlex = lerp(3.5, 0.3, r); + const blackFlex = lerp(0.3, 3.5, r); + + bgRed.style.flex = redFlex; + bgBlack.style.flex = blackFlex; + + /* 2. Phone mockup scale / opacity / translate */ + const bias = (0.5 - r) * 2; // +1 red, -1 black + + const redScale = 1 + clamp(bias, 0, 1) * 0.3; + const redOpacity = clamp(1 + bias * 3, 0, 1); + const redShiftX = clamp(bias, 0, 1) * 60; + + const blackScale = 1 + clamp(-bias, 0, 1) * 0.3; + const blackOpacity = clamp(1 - bias * 3, 0, 1); + const blackShiftX = clamp(-bias, 0, 1) * -60; + + phoneRed.style.transform = `scale(${redScale}) translateX(${redShiftX}px)`; + phoneRed.style.opacity = redOpacity; + + phoneBlack.style.transform = `scale(${blackScale}) translateX(${blackShiftX}px)`; + phoneBlack.style.opacity = blackOpacity; + + /* 3. Text always stays visible — no opacity changes */ + + requestAnimationFrame(animate); + } + + animate(); +}); diff --git a/main/styles.css b/main/styles.css new file mode 100644 index 0000000..bcca018 --- /dev/null +++ b/main/styles.css @@ -0,0 +1,426 @@ +/* ======================================== + CSS Custom Properties & Reset + ======================================== */ + +:root { + --color-red-bg: #6b0f1a; + --color-red-dark: #3d0a10; + --color-black-bg: #0e0e0e; + --color-white: #ffffff; + --color-white-dim: rgba(255, 255, 255, 0.35); + --color-capture-btn: #1a1f3d; + --color-progress-fill: #d4a017; + + --font-display: 'Playfair Display', Georgia, serif; + --font-body: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif; + + --phone-width: 190px; + --phone-height: 380px; + --phone-radius: 26px; +} + +*, +*::before, +*::after { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +html { + font-size: 16px; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +body { + font-family: var(--font-body); + background: #000; + color: var(--color-white); + overflow-x: hidden; + margin: 0; +} + +/* ======================================== + Showcase Section – Full-width wrapper + ======================================== */ + +.showcase { + position: relative; + width: 100%; + min-height: 100vh; + display: flex; + align-items: center; + justify-content: center; + overflow: hidden; +} + +/* ======================================== + Background Panels – Decorative, full-width + ======================================== */ + +.bg-panels { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + display: flex; + z-index: 0; +} + +.bg-panel { + flex: 1; + will-change: flex; +} + +.bg-panel--red { + background: linear-gradient(135deg, var(--color-red-bg) 0%, var(--color-red-dark) 100%); +} + +.bg-panel--black { + background: linear-gradient(135deg, #1a1a1a 0%, var(--color-black-bg) 100%); +} + +/* ======================================== + Container – 1320px centered content + ======================================== */ + +.container { + position: relative; + z-index: 1; + width: 100%; + max-width: 1320px; + margin: 0 auto; + padding: 60px 40px; + display: grid; + grid-template-columns: 1fr auto 1fr; + align-items: center; + gap: 30px; +} + +/* ======================================== + Content Columns (Left / Right text) + ======================================== */ + +.content-col { + width: 100%; +} + +.content-col--left { + text-align: left; +} + +.content-col--right { + text-align: left; + justify-self: end; +} + +.content-col__title { + font-family: var(--font-display); + font-weight: 500; + font-style: italic; + font-size: 1.85rem; + line-height: 1.2; + margin-bottom: 36px; + letter-spacing: -0.02em; + color: var(--color-white); +} + +/* ======================================== + Spec List + ======================================== */ + +.spec-list { + display: flex; + flex-direction: column; +} + +.spec-list__row { + display: flex; + align-items: baseline; + padding: 9px 0; + border-top: 1px solid rgba(255, 255, 255, 0.08); +} + +.spec-list__row:last-child { + border-bottom: 1px solid rgba(255, 255, 255, 0.08); +} + +.spec-list__label { + font-size: 0.65rem; + font-weight: 500; + letter-spacing: 0.12em; + text-transform: uppercase; + color: var(--color-white-dim); + width: 90px; + flex-shrink: 0; +} + +.spec-list__value { + font-size: 1rem; + font-weight: 500; + color: var(--color-white); + letter-spacing: -0.01em; +} + +/* ======================================== + Phone Mockups – Center column + ======================================== */ + +.phones { + display: flex; + gap: 14px; + flex-shrink: 0; +} + +.phone { + width: var(--phone-width); + height: var(--phone-height); + background: #ffffff; + border-radius: var(--phone-radius); + padding: 12px; + display: flex; + flex-direction: column; + position: relative; + box-shadow: + 0 25px 60px rgba(0, 0, 0, 0.4), + 0 8px 20px rgba(0, 0, 0, 0.25), + inset 0 1px 0 rgba(255, 255, 255, 0.1); + overflow: hidden; + will-change: transform, opacity; + transform-origin: center center; +} + +/* Phone Notch */ +.phone__notch { + width: 80px; + height: 5px; + background: #1a1a1a; + border-radius: 10px; + margin: 0 auto 8px; +} + +/* Phone Header */ +.phone__header { + display: flex; + justify-content: space-between; + align-items: center; + padding: 0 4px 6px; +} + +.phone__icon { + width: 16px; + height: 16px; + color: #1a1a1a; + cursor: pointer; +} + +/* Viewfinder */ +.phone__viewfinder { + position: relative; + width: 100%; + aspect-ratio: 1 / 0.85; + background: #f0f0f0; + border-radius: 8px; + display: flex; + align-items: center; + justify-content: center; + overflow: hidden; +} + +.phone__corners { + position: absolute; + inset: 12px; + pointer-events: none; +} + +.corner { + position: absolute; + width: 20px; + height: 20px; +} + +.corner::before, +.corner::after { + content: ''; + position: absolute; + background: #555; +} + +.corner--tl { top: 0; left: 0; } +.corner--tl::before { width: 20px; height: 2px; top: 0; left: 0; } +.corner--tl::after { width: 2px; height: 20px; top: 0; left: 0; } + +.corner--tr { top: 0; right: 0; } +.corner--tr::before { width: 20px; height: 2px; top: 0; right: 0; } +.corner--tr::after { width: 2px; height: 20px; top: 0; right: 0; } + +.corner--bl { bottom: 0; left: 0; } +.corner--bl::before { width: 20px; height: 2px; bottom: 0; left: 0; } +.corner--bl::after { width: 2px; height: 20px; bottom: 0; left: 0; } + +.corner--br { bottom: 0; right: 0; } +.corner--br::before { width: 20px; height: 2px; bottom: 0; right: 0; } +.corner--br::after { width: 2px; height: 20px; bottom: 0; right: 0; } + +.phone__shoe-img { + width: 85%; + height: 85%; + object-fit: contain; + position: relative; + z-index: 1; +} + +/* Caption */ +.phone__caption { + font-size: 0.65rem; + color: #444; + text-align: center; + margin-top: 6px; + font-weight: 400; +} + +/* Progress */ +.phone__progress { + display: flex; + align-items: center; + gap: 6px; + margin-top: 4px; + padding: 0 2px; +} + +.phone__progress-text { + font-size: 0.5rem; + color: #888; + white-space: nowrap; +} + +.phone__progress-bar { + flex: 1; + height: 3px; + background: #e0e0e0; + border-radius: 3px; + overflow: hidden; +} + +.phone__progress-fill { + height: 100%; + background: var(--color-progress-fill); + border-radius: 3px; +} + +.phone__progress-count { + font-size: 0.5rem; + color: #888; + white-space: nowrap; +} + +/* Phone Buttons */ +.phone__btn { + display: block; + width: 100%; + border: none; + cursor: pointer; + font-family: var(--font-body); + font-weight: 500; + transition: transform 0.15s ease, opacity 0.15s ease; +} + +.phone__btn:active { + transform: scale(0.97); +} + +.phone__btn--capture { + background: var(--color-capture-btn); + color: #fff; + font-size: 0.72rem; + padding: 10px 0; + border-radius: 20px; + margin-top: 10px; + letter-spacing: 0.02em; +} + +.phone__btn--capture:hover { + background: #252b55; +} + +.phone__btn--skip { + background: transparent; + color: #555; + font-size: 0.68rem; + padding: 6px 0; + margin-top: 2px; +} + +.phone__btn--skip:hover { + color: #222; +} + +/* ======================================== + Responsive + ======================================== */ + +@media (max-width: 1100px) { + :root { + --phone-width: 190px; + --phone-height: 380px; + } + + .content-col__title { + font-size: 1.5rem; + } + + .container { + padding: 0 30px; + } + + .content-col { + width: 220px; + } +} + +@media (max-width: 900px) { + .container { + flex-direction: column; + padding: 60px 30px; + gap: 40px; + } + + .content-col { + width: 100%; + max-width: 400px; + text-align: center; + } + + .spec-list__row { + justify-content: center; + } + + .phones { + justify-content: center; + } + + .channel-icons { + align-items: center; + } +} + +@media (max-width: 520px) { + :root { + --phone-width: 160px; + --phone-height: 340px; + } + + .phones { + gap: 10px; + } + + .content-col__title { + font-size: 1.3rem; + } + + .spec-list__label { + width: 80px; + } +} diff --git a/skiply/ambient-float-check.png b/skiply/ambient-float-check.png new file mode 100644 index 0000000..e5c5b67 Binary files /dev/null and b/skiply/ambient-float-check.png differ diff --git a/skiply/ambient-t1.png b/skiply/ambient-t1.png new file mode 100644 index 0000000..e8a0d58 Binary files /dev/null and b/skiply/ambient-t1.png differ diff --git a/skiply/ambient-t2.png b/skiply/ambient-t2.png new file mode 100644 index 0000000..59bf4b5 Binary files /dev/null and b/skiply/ambient-t2.png differ diff --git a/skiply/beam-final.png b/skiply/beam-final.png new file mode 100644 index 0000000..808bd20 Binary files /dev/null and b/skiply/beam-final.png differ diff --git a/skiply/beam-t1.png b/skiply/beam-t1.png new file mode 100644 index 0000000..3f75a09 Binary files /dev/null and b/skiply/beam-t1.png differ diff --git a/skiply/beam-t2.png b/skiply/beam-t2.png new file mode 100644 index 0000000..122a03d Binary files /dev/null and b/skiply/beam-t2.png differ diff --git a/skiply/beam-t3.png b/skiply/beam-t3.png new file mode 100644 index 0000000..25b84df Binary files /dev/null and b/skiply/beam-t3.png differ diff --git a/skiply/beam-v2-rise-full.png b/skiply/beam-v2-rise-full.png new file mode 100644 index 0000000..1b79c30 Binary files /dev/null and b/skiply/beam-v2-rise-full.png differ diff --git a/skiply/beam-v2-rise.png b/skiply/beam-v2-rise.png new file mode 100644 index 0000000..d41b610 Binary files /dev/null and b/skiply/beam-v2-rise.png differ diff --git a/skiply/beam-v2-sweep-full.png b/skiply/beam-v2-sweep-full.png new file mode 100644 index 0000000..83b7aab Binary files /dev/null and b/skiply/beam-v2-sweep-full.png differ diff --git a/skiply/beam-v2-sweep.png b/skiply/beam-v2-sweep.png new file mode 100644 index 0000000..ad08d11 Binary files /dev/null and b/skiply/beam-v2-sweep.png differ diff --git a/skiply/beam-v2-zoom-rise-base.png b/skiply/beam-v2-zoom-rise-base.png new file mode 100644 index 0000000..c0708ec Binary files /dev/null and b/skiply/beam-v2-zoom-rise-base.png differ diff --git a/skiply/beam-v2-zoom-sweep-mid.png b/skiply/beam-v2-zoom-sweep-mid.png new file mode 100644 index 0000000..55d61c4 Binary files /dev/null and b/skiply/beam-v2-zoom-sweep-mid.png differ diff --git a/skiply/beam-v2-zoombase-rise-t028.png b/skiply/beam-v2-zoombase-rise-t028.png new file mode 100644 index 0000000..e8c1bd1 Binary files /dev/null and b/skiply/beam-v2-zoombase-rise-t028.png differ diff --git a/skiply/beam-v2-zoombase-rise-t045.png b/skiply/beam-v2-zoombase-rise-t045.png new file mode 100644 index 0000000..7756479 Binary files /dev/null and b/skiply/beam-v2-zoombase-rise-t045.png differ diff --git a/skiply/connector-final.png b/skiply/connector-final.png new file mode 100644 index 0000000..181aa8a Binary files /dev/null and b/skiply/connector-final.png differ diff --git a/skiply/connector-t1.png b/skiply/connector-t1.png new file mode 100644 index 0000000..84bf2a7 Binary files /dev/null and b/skiply/connector-t1.png differ diff --git a/skiply/connector-t2.png b/skiply/connector-t2.png new file mode 100644 index 0000000..6999812 Binary files /dev/null and b/skiply/connector-t2.png differ diff --git a/skiply/final-check-full.png b/skiply/final-check-full.png new file mode 100644 index 0000000..5b74a78 Binary files /dev/null and b/skiply/final-check-full.png differ diff --git a/skiply/final-check-shoe.png b/skiply/final-check-shoe.png new file mode 100644 index 0000000..b719f9e Binary files /dev/null and b/skiply/final-check-shoe.png differ diff --git a/skiply/h2-t1.png b/skiply/h2-t1.png new file mode 100644 index 0000000..83117e4 Binary files /dev/null and b/skiply/h2-t1.png differ diff --git a/skiply/h2-t2.png b/skiply/h2-t2.png new file mode 100644 index 0000000..47d581f Binary files /dev/null and b/skiply/h2-t2.png differ diff --git a/skiply/h2-t3.png b/skiply/h2-t3.png new file mode 100644 index 0000000..98adb79 Binary files /dev/null and b/skiply/h2-t3.png differ diff --git a/skiply/h2-t4.png b/skiply/h2-t4.png new file mode 100644 index 0000000..20c3cfe Binary files /dev/null and b/skiply/h2-t4.png differ diff --git a/skiply/horizon-final.png b/skiply/horizon-final.png new file mode 100644 index 0000000..04b18c7 Binary files /dev/null and b/skiply/horizon-final.png differ diff --git a/skiply/horizon-t1.png b/skiply/horizon-t1.png new file mode 100644 index 0000000..f8b70f7 Binary files /dev/null and b/skiply/horizon-t1.png differ diff --git a/skiply/horizon-t2.png b/skiply/horizon-t2.png new file mode 100644 index 0000000..87ade26 Binary files /dev/null and b/skiply/horizon-t2.png differ diff --git a/skiply/labels-t1.png b/skiply/labels-t1.png new file mode 100644 index 0000000..15f17d2 Binary files /dev/null and b/skiply/labels-t1.png differ diff --git a/skiply/labels-t2.png b/skiply/labels-t2.png new file mode 100644 index 0000000..118a79d Binary files /dev/null and b/skiply/labels-t2.png differ diff --git a/skiply/labels-t3.png b/skiply/labels-t3.png new file mode 100644 index 0000000..3f85b8d Binary files /dev/null and b/skiply/labels-t3.png differ diff --git a/skiply/labels-t4.png b/skiply/labels-t4.png new file mode 100644 index 0000000..f39f532 Binary files /dev/null and b/skiply/labels-t4.png differ diff --git a/skiply/no-logo-final.png b/skiply/no-logo-final.png new file mode 100644 index 0000000..8ed46aa Binary files /dev/null and b/skiply/no-logo-final.png differ diff --git a/skiply/node-fix-final.png b/skiply/node-fix-final.png new file mode 100644 index 0000000..e030c3f Binary files /dev/null and b/skiply/node-fix-final.png differ diff --git a/skiply/node-fix-mid1.png b/skiply/node-fix-mid1.png new file mode 100644 index 0000000..6cfe450 Binary files /dev/null and b/skiply/node-fix-mid1.png differ diff --git a/skiply/node-fix-mid2.png b/skiply/node-fix-mid2.png new file mode 100644 index 0000000..46846d6 Binary files /dev/null and b/skiply/node-fix-mid2.png differ diff --git a/skiply/path-animation-fade.js b/skiply/path-animation-fade.js new file mode 100644 index 0000000..ec56701 --- /dev/null +++ b/skiply/path-animation-fade.js @@ -0,0 +1,221 @@ +/* ============================================= + Skiply.ai — JavaScript + - Car travels along the exact road-path-1.svg path + - Back-and-forth loop using requestAnimationFrame + - Step icons pinned to real path coordinates + - Entry reveal animations via IntersectionObserver + ============================================= */ + +'use strict'; + +document.addEventListener('DOMContentLoaded', () => { + + /* ═══════════════════════════════════════════ + SCREEN 1 — Car motion along road path + ═══════════════════════════════════════════ */ + + /* ═══════════════════════════════════════════ + Reusable Car Animation Setup + ═══════════════════════════════════════════ */ + function setupRoadAnimation(svgId, pathId, carId, mainAreaSelector, screenId, speed = 0.00016) { + const svgEl = document.getElementById(svgId); + const pathEl = document.getElementById(pathId); + const car = document.getElementById(carId); + const mainArea = document.querySelector(mainAreaSelector); + const screen = document.getElementById(screenId); + + if (!pathEl || !car || !svgEl || !mainArea || !screen) { + console.warn(`Missing elements for ${screenId}:`, {svgEl, pathEl, car, mainArea, screen}); + return; + } + + const TOTAL_LEN = pathEl.getTotalLength(); + + function getSVGScale() { + const rect = svgEl.getBoundingClientRect(); + const vbW = svgEl.viewBox.baseVal.width || 1005; + const vbH = svgEl.viewBox.baseVal.height || 650; + + const scaleX = rect.width / vbW; + const scaleY = rect.height / vbH; + const scale = Math.min(scaleX, scaleY); + + const offsetX = (rect.width - vbW * scale) / 2; + const offsetY = (rect.height - vbH * scale) / 2; + return { scale, offsetX, offsetY, rect }; + } + + let progress = 0; + const SPEED = speed; + let lastTime = null; + let rafId = null; + let running = false; + let looping = false; + + function getAngle(t, delta = 0.002) { + const t1 = Math.max(0, t - delta); + const t2 = Math.min(1, t + delta); + const p1 = pathEl.getPointAtLength(t1 * TOTAL_LEN); + const p2 = pathEl.getPointAtLength(t2 * TOTAL_LEN); + return Math.atan2(p2.y - p1.y, p2.x - p1.x) * (180 / Math.PI); + } + + function placeCar(t) { + const { scale, offsetX, offsetY, rect } = getSVGScale(); + const containerRect = mainArea.getBoundingClientRect(); + const pt = pathEl.getPointAtLength(t * TOTAL_LEN); + const angle = getAngle(t); + const relX = (rect.left + offsetX + pt.x * scale) - containerRect.left; + const relY = (rect.top + offsetY + pt.y * scale) - containerRect.top; + car.style.left = `${relX}px`; + car.style.top = `${relY}px`; + car.style.transform = `translate(-50%, -50%) rotate(${angle}deg)`; + } + + function doLoop() { + looping = true; + car.style.transition = 'opacity 0.25s ease'; + car.style.opacity = '0'; + + setTimeout(() => { + progress = 0; + placeCar(0); + setTimeout(() => { + car.style.transition = 'opacity 0.3s ease'; + car.style.opacity = '1'; + setTimeout(() => { + car.style.transition = ''; + looping = false; + lastTime = null; + rafId = requestAnimationFrame(animateCar); + }, 320); + }, 60); + }, 280); + } + + function animateCar(timestamp) { + if (looping) return; + if (!lastTime) lastTime = timestamp; + const dt = Math.min(timestamp - lastTime, 50); + lastTime = timestamp; + + progress += SPEED * dt; + + if (progress >= 1) { + progress = 1; + placeCar(1); + cancelAnimationFrame(rafId); + doLoop(); + return; + } + + placeCar(progress); + rafId = requestAnimationFrame(animateCar); + } + + function startCar() { + if (running) return; + running = true; + lastTime = null; + progress = 0; + looping = false; + placeCar(0); + car.style.transition = 'opacity 0.4s ease'; + car.style.opacity = '1'; + setTimeout(() => { + car.style.transition = ''; + rafId = requestAnimationFrame(animateCar); + }, 420); + } + + const ro = new ResizeObserver(() => {}); + ro.observe(mainArea); + + + + // Use GSAP ScrollTrigger for Fade In / Fade Out effect + if (typeof gsap !== 'undefined' && typeof ScrollTrigger !== 'undefined') { + gsap.registerPlugin(ScrollTrigger); + + // 1. Fade in the left side panel text + gsap.fromTo(document.querySelectorAll('#' + screenId + ' .side-panel > *'), + { opacity: 0, y: 20 }, + { + opacity: 1, + y: 0, + duration: 0.6, + stagger: 0.15, + ease: "power2.out", + scrollTrigger: { + trigger: screen, + start: "top 60%", + end: "bottom 40%", + toggleActions: "play reverse play reverse" + } + } + ); + + // 2. Stagger fade-in for SVG pointers (navy circles and white icons) + const pointers = Array.from(document.querySelectorAll('#' + screenId + ' svg circle, #' + screenId + ' svg path')).filter(el => { + const fill = el.getAttribute('fill'); + if (!fill) return false; + const upper = fill.toUpperCase(); + return upper === '#0A1A37' || upper === 'WHITE' || upper === '#FFFFFF'; + }); + + gsap.fromTo(pointers, + { opacity: 0, scale: 0, transformOrigin: "50% 50%" }, + { + opacity: 1, + scale: 1, + duration: 0.4, + stagger: 0.15, + ease: "back.out(1.5)", + scrollTrigger: { + trigger: screen, + start: "top 50%", + end: "bottom 20%", + toggleActions: "play reverse play reverse" + } + } + ); + + // 3. Start car animation when screen is in view + ScrollTrigger.create({ + trigger: screen, + start: "top 60%", + onEnter: startCar, + once: true + }); + } + } + + // Initialize Screen 1 + setupRoadAnimation('road-svg', 'road-path', 'road-car', '.main-area--prob1', 'screen-prob1', 0.00008); + + // Initialize Screen 2 + setupRoadAnimation('road-svg-2', 'road-path-2', 'road-car-2', '.main-area--prob2', 'screen-prob2', 0.00016); + + + + + + /* ═══════════════════════════════════════════ + Screen 2 Fade In Effect (Original Smooth Version) + ═══════════════════════════════════════════ */ + if (typeof gsap !== 'undefined' && typeof ScrollTrigger !== 'undefined') { + gsap.fromTo('.screen--prob2', + { opacity: 0 }, + { + opacity: 1, + ease: "none", + scrollTrigger: { + trigger: '.screen--prob2', + start: "top 75%", + end: "top 0%", + scrub: true + } + } + ); + } +}); diff --git a/skiply/path-animation-fullpage.js b/skiply/path-animation-fullpage.js new file mode 100644 index 0000000..7b85313 --- /dev/null +++ b/skiply/path-animation-fullpage.js @@ -0,0 +1,215 @@ +/* ============================================= + Skiply.ai — JavaScript + - Car travels along the exact road-path-1.svg path + - Back-and-forth loop using requestAnimationFrame + - Step icons pinned to real path coordinates + - Entry reveal animations via IntersectionObserver + ============================================= */ + +'use strict'; + +document.addEventListener('DOMContentLoaded', () => { + + /* ═══════════════════════════════════════════ + SCREEN 1 — Car motion along road path + ═══════════════════════════════════════════ */ + + /* ═══════════════════════════════════════════ + Reusable Car Animation Setup + ═══════════════════════════════════════════ */ + function setupRoadAnimation(svgId, pathId, carId, mainAreaSelector, screenId, speed = 0.00016) { + const svgEl = document.getElementById(svgId); + const pathEl = document.getElementById(pathId); + const car = document.getElementById(carId); + const mainArea = document.querySelector(mainAreaSelector); + const screen = document.getElementById(screenId); + + if (!pathEl || !car || !svgEl || !mainArea || !screen) { + console.warn(`Missing elements for ${screenId}:`, {svgEl, pathEl, car, mainArea, screen}); + return; + } + + const TOTAL_LEN = pathEl.getTotalLength(); + + function getSVGScale() { + const rect = svgEl.getBoundingClientRect(); + const vbW = svgEl.viewBox.baseVal.width || 1005; + const vbH = svgEl.viewBox.baseVal.height || 650; + + const scaleX = rect.width / vbW; + const scaleY = rect.height / vbH; + const scale = Math.min(scaleX, scaleY); + + const offsetX = (rect.width - vbW * scale) / 2; + const offsetY = (rect.height - vbH * scale) / 2; + return { scale, offsetX, offsetY, rect }; + } + + let progress = 0; + const SPEED = speed; + let lastTime = null; + let rafId = null; + let running = false; + let looping = false; + + function getAngle(t, delta = 0.002) { + const t1 = Math.max(0, t - delta); + const t2 = Math.min(1, t + delta); + const p1 = pathEl.getPointAtLength(t1 * TOTAL_LEN); + const p2 = pathEl.getPointAtLength(t2 * TOTAL_LEN); + return Math.atan2(p2.y - p1.y, p2.x - p1.x) * (180 / Math.PI); + } + + function placeCar(t) { + const { scale, offsetX, offsetY, rect } = getSVGScale(); + const containerRect = mainArea.getBoundingClientRect(); + const pt = pathEl.getPointAtLength(t * TOTAL_LEN); + const angle = getAngle(t); + const relX = (rect.left + offsetX + pt.x * scale) - containerRect.left; + const relY = (rect.top + offsetY + pt.y * scale) - containerRect.top; + car.style.left = `${relX}px`; + car.style.top = `${relY}px`; + car.style.transform = `translate(-50%, -50%) rotate(${angle}deg)`; + } + + function doLoop() { + looping = true; + car.style.transition = 'opacity 0.25s ease'; + car.style.opacity = '0'; + + setTimeout(() => { + progress = 0; + placeCar(0); + setTimeout(() => { + car.style.transition = 'opacity 0.3s ease'; + car.style.opacity = '1'; + setTimeout(() => { + car.style.transition = ''; + looping = false; + lastTime = null; + rafId = requestAnimationFrame(animateCar); + }, 320); + }, 60); + }, 280); + } + + function animateCar(timestamp) { + if (looping) return; + if (!lastTime) lastTime = timestamp; + const dt = Math.min(timestamp - lastTime, 50); + lastTime = timestamp; + + progress += SPEED * dt; + + if (progress >= 1) { + progress = 1; + placeCar(1); + cancelAnimationFrame(rafId); + doLoop(); + return; + } + + placeCar(progress); + rafId = requestAnimationFrame(animateCar); + } + + function startCar() { + if (running) return; + running = true; + lastTime = null; + progress = 0; + looping = false; + placeCar(0); + car.style.transition = 'opacity 0.4s ease'; + car.style.opacity = '1'; + setTimeout(() => { + car.style.transition = ''; + rafId = requestAnimationFrame(animateCar); + }, 420); + } + + const ro = new ResizeObserver(() => {}); + ro.observe(mainArea); + + + + // Use GSAP ScrollTrigger for Fade In / Fade Out effect + if (typeof gsap !== 'undefined' && typeof ScrollTrigger !== 'undefined') { + gsap.registerPlugin(ScrollTrigger); + + // 1. Fade in the left side panel text + gsap.fromTo(document.querySelectorAll('#' + screenId + ' .side-panel > *'), + { opacity: 0, y: 20 }, + { + opacity: 1, + y: 0, + duration: 0.6, + stagger: 0.15, + ease: "power2.out", + scrollTrigger: { + trigger: screen, + start: "top 60%", + end: "bottom 40%", + toggleActions: "play reverse play reverse" + } + } + ); + + // 2. Stagger fade-in for SVG pointers (navy circles and white icons) + const pointers = Array.from(document.querySelectorAll('#' + screenId + ' svg circle, #' + screenId + ' svg path')).filter(el => { + const fill = el.getAttribute('fill'); + if (!fill) return false; + const upper = fill.toUpperCase(); + return upper === '#0A1A37' || upper === 'WHITE' || upper === '#FFFFFF'; + }); + + gsap.fromTo(pointers, + { opacity: 0, scale: 0, transformOrigin: "50% 50%" }, + { + opacity: 1, + scale: 1, + duration: 0.4, + stagger: 0.15, + ease: "back.out(1.5)", + scrollTrigger: { + trigger: screen, + start: "top 50%", + end: "bottom 20%", + toggleActions: "play reverse play reverse" + } + } + ); + + // 3. Start car animation when screen is in view + ScrollTrigger.create({ + trigger: screen, + start: "top 60%", + onEnter: startCar, + once: true + }); + } + } + + // Initialize Screen 1 + setupRoadAnimation('road-svg', 'road-path', 'road-car', '.main-area--prob1', 'screen-prob1', 0.00008); + + // Initialize Screen 2 + setupRoadAnimation('road-svg-2', 'road-path-2', 'road-car-2', '.main-area--prob2', 'screen-prob2', 0.00016); + + + + + + /* ═══════════════════════════════════════════ + FullPage.js Initialization + ═══════════════════════════════════════════ */ + if (typeof fullpage !== 'undefined') { + new fullpage('#fullpage', { + autoScrolling: true, + scrollBar: true, // IMPORTANT: keeps native scroll so GSAP ScrollTrigger still works! + scrollingSpeed: 700, + fitToSection: true, + fitToSectionDelay: 1000 + }); + } +}); diff --git a/skiply/path-animation.js b/skiply/path-animation.js new file mode 100644 index 0000000..993f178 --- /dev/null +++ b/skiply/path-animation.js @@ -0,0 +1,202 @@ +/* ============================================= + Skiply.ai — JavaScript + - Car travels along the exact road-path-1.svg path + - Back-and-forth loop using requestAnimationFrame + - Step icons pinned to real path coordinates + - Entry reveal animations via IntersectionObserver + ============================================= */ + +'use strict'; + +document.addEventListener('DOMContentLoaded', () => { + + /* ═══════════════════════════════════════════ + SCREEN 1 — Car motion along road path + ═══════════════════════════════════════════ */ + + /* ═══════════════════════════════════════════ + Reusable Car Animation Setup + ═══════════════════════════════════════════ */ + function setupRoadAnimation(svgId, pathId, carId, mainAreaSelector, screenId, speed = 0.00016) { + const svgEl = document.getElementById(svgId); + const pathEl = document.getElementById(pathId); + const car = document.getElementById(carId); + const mainArea = document.querySelector(mainAreaSelector); + const screen = document.getElementById(screenId); + + if (!pathEl || !car || !svgEl || !mainArea || !screen) { + console.warn(`Missing elements for ${screenId}:`, {svgEl, pathEl, car, mainArea, screen}); + return; + } + + const TOTAL_LEN = pathEl.getTotalLength(); + + function getSVGScale() { + const rect = svgEl.getBoundingClientRect(); + const vbW = svgEl.viewBox.baseVal.width || 1005; + const vbH = svgEl.viewBox.baseVal.height || 650; + + const scaleX = rect.width / vbW; + const scaleY = rect.height / vbH; + const scale = Math.min(scaleX, scaleY); + + const offsetX = (rect.width - vbW * scale) / 2; + const offsetY = (rect.height - vbH * scale) / 2; + return { scale, offsetX, offsetY, rect }; + } + + let progress = 0; + const SPEED = speed; + let lastTime = null; + let rafId = null; + let running = false; + let looping = false; + + function getAngle(t, delta = 0.002) { + const t1 = Math.max(0, t - delta); + const t2 = Math.min(1, t + delta); + const p1 = pathEl.getPointAtLength(t1 * TOTAL_LEN); + const p2 = pathEl.getPointAtLength(t2 * TOTAL_LEN); + return Math.atan2(p2.y - p1.y, p2.x - p1.x) * (180 / Math.PI); + } + + function placeCar(t) { + const { scale, offsetX, offsetY, rect } = getSVGScale(); + const containerRect = mainArea.getBoundingClientRect(); + const pt = pathEl.getPointAtLength(t * TOTAL_LEN); + const angle = getAngle(t); + const relX = (rect.left + offsetX + pt.x * scale) - containerRect.left; + const relY = (rect.top + offsetY + pt.y * scale) - containerRect.top; + car.style.left = `${relX}px`; + car.style.top = `${relY}px`; + car.style.transform = `translate(-50%, -50%) rotate(${angle}deg)`; + } + + function doLoop() { + looping = true; + car.style.transition = 'opacity 0.25s ease'; + car.style.opacity = '0'; + + setTimeout(() => { + progress = 0; + placeCar(0); + setTimeout(() => { + car.style.transition = 'opacity 0.3s ease'; + car.style.opacity = '1'; + setTimeout(() => { + car.style.transition = ''; + looping = false; + lastTime = null; + rafId = requestAnimationFrame(animateCar); + }, 320); + }, 60); + }, 280); + } + + function animateCar(timestamp) { + if (looping) return; + if (!lastTime) lastTime = timestamp; + const dt = Math.min(timestamp - lastTime, 50); + lastTime = timestamp; + + progress += SPEED * dt; + + if (progress >= 1) { + progress = 1; + placeCar(1); + cancelAnimationFrame(rafId); + doLoop(); + return; + } + + placeCar(progress); + rafId = requestAnimationFrame(animateCar); + } + + function startCar() { + if (running) return; + running = true; + lastTime = null; + progress = 0; + looping = false; + placeCar(0); + car.style.transition = 'opacity 0.4s ease'; + car.style.opacity = '1'; + setTimeout(() => { + car.style.transition = ''; + rafId = requestAnimationFrame(animateCar); + }, 420); + } + + const ro = new ResizeObserver(() => {}); + ro.observe(mainArea); + + + + // Use GSAP ScrollTrigger for Fade In / Fade Out effect + if (typeof gsap !== 'undefined' && typeof ScrollTrigger !== 'undefined') { + gsap.registerPlugin(ScrollTrigger); + + // 1. Fade in the left side panel text + gsap.fromTo(document.querySelectorAll('#' + screenId + ' .side-panel > *'), + { opacity: 0, y: 20 }, + { + opacity: 1, + y: 0, + duration: 0.6, + stagger: 0.15, + ease: "power2.out", + scrollTrigger: { + trigger: screen, + start: "top 60%", + end: "bottom 40%", + toggleActions: "play reverse play reverse" + } + } + ); + + // 2. Stagger fade-in for SVG pointers (navy circles and white icons) + const pointers = Array.from(document.querySelectorAll('#' + screenId + ' svg circle, #' + screenId + ' svg path')).filter(el => { + const fill = el.getAttribute('fill'); + if (!fill) return false; + const upper = fill.toUpperCase(); + return upper === '#0A1A37' || upper === 'WHITE' || upper === '#FFFFFF'; + }); + + gsap.fromTo(pointers, + { opacity: 0, scale: 0, transformOrigin: "50% 50%" }, + { + opacity: 1, + scale: 1, + duration: 0.4, + stagger: 0.15, + ease: "back.out(1.5)", + scrollTrigger: { + trigger: screen, + start: "top 50%", + end: "bottom 20%", + toggleActions: "play reverse play reverse" + } + } + ); + + // 3. Start car animation when screen is in view + ScrollTrigger.create({ + trigger: screen, + start: "top 60%", + onEnter: startCar, + once: true + }); + } + } + + // Initialize Screen 1 + setupRoadAnimation('road-svg', 'road-path', 'road-car', '.main-area--prob1', 'screen-prob1', 0.00008); + + // Initialize Screen 2 + setupRoadAnimation('road-svg-2', 'road-path-2', 'road-car-2', '.main-area--prob2', 'screen-prob2', 0.00016); + + + + +}); diff --git a/skiply/routes-card-1440.png b/skiply/routes-card-1440.png new file mode 100644 index 0000000..a607b03 Binary files /dev/null and b/skiply/routes-card-1440.png differ diff --git a/skiply/routes-card-500.png b/skiply/routes-card-500.png new file mode 100644 index 0000000..eadfdc3 Binary files /dev/null and b/skiply/routes-card-500.png differ diff --git a/skiply/routes-card-800.png b/skiply/routes-card-800.png new file mode 100644 index 0000000..5e5fb8e Binary files /dev/null and b/skiply/routes-card-800.png differ diff --git a/skiply/scan-anim-end.png b/skiply/scan-anim-end.png new file mode 100644 index 0000000..d0b6c6e Binary files /dev/null and b/skiply/scan-anim-end.png differ diff --git a/skiply/scan-anim-fixed-crop.png b/skiply/scan-anim-fixed-crop.png new file mode 100644 index 0000000..070c53c Binary files /dev/null and b/skiply/scan-anim-fixed-crop.png differ diff --git a/skiply/scan-anim-fixed.png b/skiply/scan-anim-fixed.png new file mode 100644 index 0000000..a0521d2 Binary files /dev/null and b/skiply/scan-anim-fixed.png differ diff --git a/skiply/scan-anim-mid.png b/skiply/scan-anim-mid.png new file mode 100644 index 0000000..d0b6c6e Binary files /dev/null and b/skiply/scan-anim-mid.png differ diff --git a/skiply/screenshot-banner-crop.png b/skiply/screenshot-banner-crop.png new file mode 100644 index 0000000..07b4430 Binary files /dev/null and b/skiply/screenshot-banner-crop.png differ diff --git a/skiply/screenshot-connected-systems-1440-v2.png b/skiply/screenshot-connected-systems-1440-v2.png new file mode 100644 index 0000000..4450099 Binary files /dev/null and b/skiply/screenshot-connected-systems-1440-v2.png differ diff --git a/skiply/screenshot-connected-systems-1440.png b/skiply/screenshot-connected-systems-1440.png new file mode 100644 index 0000000..361095c Binary files /dev/null and b/skiply/screenshot-connected-systems-1440.png differ diff --git a/skiply/skiply-connected-systems.css b/skiply/skiply-connected-systems.css new file mode 100644 index 0000000..0ca3973 --- /dev/null +++ b/skiply/skiply-connected-systems.css @@ -0,0 +1,487 @@ +/* ============================================= + Skiply.ai — Connected Systems section + Figma: SG-DEV + ============================================= */ + +*, +*::before, +*::after { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +:root { + --navy-deep: #06152c; + --blue: #0060ef; + --text-white: #ffffff; + --text-dim: rgba(255, 255, 255, 0.65); + --line: rgba(255, 255, 255, 0.5); + + --font-main: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif; + --font-display: 'Fraunces', Georgia, 'Times New Roman', serif; + + --content-w: 1320px; +} + +html { + font-family: var(--font-main); + -webkit-font-smoothing: antialiased; +} + +body { + background: var(--navy-deep); +} + +/* ── Section shell ──────────────────────────── */ +.connected { + background: var(--navy-deep); + padding: 96px 32px 80px; +} + +.connected__inner { + max-width: var(--content-w); + margin: 0 auto; +} + +/* ── Heading ────────────────────────────────── */ +.connected__header { + text-align: center; + margin-bottom: 32px; +} + +.connected__eyebrow { + font-size: 0.95rem; + font-weight: 400; + color: var(--text-dim); + margin-bottom: 12px; +} + +.connected__title { + font-family: var(--font-display); + font-size: 2.6rem; + font-weight: 400; + line-height: 1.2; + color: var(--text-white); + max-width: 780px; + margin: 0 auto; +} + +/* ── Banner / scan stage ─────────────────────── */ +.connected__banner { + max-width: 720px; + margin: 0 auto; +} + +.shoe-stage { + position: relative; + width: 100%; + aspect-ratio: 631/527; +} + +.shoe-stage__glow { + position: absolute; + left: 50%; + top: 50%; + width: 85%; + height: 85%; + transform: translate(-50%, -50%); + background: radial-gradient(ellipse, rgba(0, 96, 239, 0.35) 0%, rgba(0, 96, 239, 0) 70%); + filter: blur(20px); + animation: shoeGlowPulse 4s ease-in-out infinite; + pointer-events: none; +} + +@keyframes shoeGlowPulse { + + 0%, + 100% { + opacity: 0.7; + } + + 50% { + opacity: 1; + } +} + +.shoe-stage__img { + position: absolute; + z-index: 2; + inset: 0; + width: 100%; + height: 100%; + object-fit: contain; + filter: drop-shadow(0 0 14px rgba(110, 165, 255, 0.8)) drop-shadow(0 0 36px rgba(70, 120, 255, 0.4)); +} + +/* Outer layer: a plain rectangular clip that reveals left-to-right in + step with the scan beam via --reveal (kept separate from the alpha + mask below — combining both in one multi-layer mask is unreliable + across engines). */ +.shoe-stage__mesh-clip { + --reveal: 0%; + position: absolute; + inset: 0; + clip-path: inset(0 calc(100% - var(--reveal)) 0 0); + pointer-events: none; +} + +/* Inner layer: the wireframe grid, clipped to the shoe's own alpha + silhouette so it only ever paints on the shoe itself. */ +.shoe-stage__mesh { + position: absolute; + inset: 0; + opacity: 0; + background-image: + repeating-linear-gradient(0deg, rgba(140, 190, 255, 0.4) 0 1px, transparent 1px 22px), + repeating-linear-gradient(90deg, rgba(140, 190, 255, 0.4) 0 1px, transparent 1px 22px); + mix-blend-mode: screen; + filter: drop-shadow(0 0 5px rgba(96, 160, 255, 0.6)); + -webkit-mask-image: url(../assets/connected-systems/shoe-clean.png); + -webkit-mask-size: 100% 100%; + -webkit-mask-repeat: no-repeat; + mask-image: url(../assets/connected-systems/shoe-clean.png); + mask-size: 100% 100%; + mask-repeat: no-repeat; + mask-mode: alpha; + pointer-events: none; +} + +/* Horizon beam: a persistent glowing line that appears to pass through + the shoe once the scan finishes, extending past its silhouette on + both sides. Grows outward from center. */ + +.shoe-stage__horizon { + position: absolute; + left: -18%; + right: -18%; + top: 45%; + height: 3px; + margin-top: -1.5px; + opacity: 0; + transform: scaleX(0); + transform-origin: center; + + pointer-events: none; + + border-top: 2px solid; + border-image-slice: 2; + border-image-source: linear-gradient(90deg, + rgba(255, 255, 255, 0) 0%, + #fff 15.38%, + #fff 85.58%, + rgba(255, 255, 255, 0) 100%); + + filter: + drop-shadow(0 0 4px rgba(255, 255, 255, 0.9)) drop-shadow(0 0 12px rgba(110, 165, 255, 0.8)) drop-shadow(0 0 30px rgba(70, 120, 255, 0.5)); +} + +.shoe-stage__particles { + position: absolute; + inset: 0; + pointer-events: none; +} + +.scan-particle { + position: absolute; + width: 3px; + height: 3px; + border-radius: 50%; + background: #cfe2ff; + box-shadow: 0 0 6px 1px rgba(150, 195, 255, 0.9); + opacity: 0; +} + +/* ── Ambient dust: a persistent field of dots, independent of the + scroll-triggered scan sequence, that keeps the banner feeling alive. + Color is a light cyan-white chosen for contrast against the dark + navy banner backdrop. ── */ +.shoe-stage__ambient { + position: absolute; + inset: 0; + pointer-events: none; + z-index: 1; +} + +.ambient-particle { + position: absolute; + border-radius: 50%; + background: #e4f0ff; + box-shadow: 0 0 5px 1.5px rgba(150, 195, 255, 0.85); + animation: ambientFloat var(--dur, 3.5s) ease-in-out infinite; + animation-delay: var(--delay, 0s); + animation-fill-mode: backwards; +} + +@keyframes ambientFloat { + + 0%, + 100% { + transform: translate(0, 0) scale(0.7); + opacity: 0.12; + } + + 50% { + transform: translate(var(--dx, 8px), var(--dy, -14px)) scale(1.2); + opacity: 0.85; + } +} + +@media (prefers-reduced-motion: reduce) { + .ambient-particle { + animation: none; + opacity: 0.4; + } +} + +/* ── Scan labels: icon + two-line chip, matching Figma node 328:820 ── */ +.scan-label { + position: absolute; + z-index: 4; + display: inline-flex; + align-items: center; + gap: 12px; + font-size: 0.85rem; + font-weight: 400; + color: rgba(255, 255, 255, 0.85); + border: 1px solid rgba(255, 255, 255, 0.35); + text-shadow: 0 1px 4px rgba(0, 0, 0, 0.9), 0 0 10px rgba(0, 0, 0, 0.7); + opacity: 0; + white-space: nowrap; + border-radius: 7px; + background: rgba(255, 255, 255, 0.10); + padding: 10px 20px; + backdrop-filter: blur(3px); + -webkit-backdrop-filter: blur(3px); +} + +.scan-label__icon { + width: 26px; + height: 26px; + flex-shrink: 0; + object-fit: contain; +} + +.scan-label__text { + display: flex; + flex-direction: column; + line-height: 1.3; +} + +.scan-label__key { + font-size: 0.8rem; + font-weight: 400; + color: rgba(255, 255, 255, 0.75); +} + +.scan-label__value { + font-size: 1rem; + font-weight: 700; + color: var(--text-white); +} + +.scan-label--condition { + left: -1%; + top: 0; +} + +.scan-label--resale { + right: 2%; + top: 18%; +} + +.scan-label--market { + left: 10%; + top: 66%; +} + +.scan-label--value { + right: -4%; + top: 65%; +} + +/* ── Light connector: carries the horizon beam into the tree ──── */ +.connected__flow { + position: relative; + width: 3px; + height: 56px; + margin: 0 auto; + overflow: visible; +} + +.connected__flow-line { + position: absolute; + inset: 0; + background: linear-gradient(to bottom, + transparent, + rgba(150, 195, 255, 0.95) 25%, + rgba(150, 195, 255, 0.95) 75%, + transparent); + box-shadow: 0 0 12px 3px rgba(110, 165, 255, 0.7); + transform: scaleY(0); + transform-origin: top center; + opacity: 0; +} + +.connected__flow-dot { + position: absolute; + left: 50%; + top: 0; + width: 8px; + height: 8px; + margin: -4px 0 0 -4px; + border-radius: 50%; + background: #eaf2ff; + box-shadow: 0 0 10px 3px #6ba8ff, 0 0 20px 8px #3f7bff; + opacity: 0; +} + +/* ── Distribution tree ──────────────────────── */ +.connected__tree { + position: relative; + margin-top: -250px; +} + +.connected__branches { + display: block; + width: 100%; + height: auto; + opacity: 0; +} + +/* Each branch reveals top-to-bottom independently, staggered in JS, + so the five lines visibly draw in one after another. */ +.connected__branch-group { + clip-path: inset(0 0 100% 0); +} + +.connected__node { + opacity: 0; + transform-box: fill-box; + transform-origin: center; +} + +/* ── Route pills ─────────────────────────────── */ +.connected__routes { + display: flex; + gap: 15px; + list-style: none; +} + +.route { + flex: 1 1 0; + display: flex; + flex-direction: column; + justify-content: space-between; + align-items: center; + gap: 24px; + min-height: 150px; + padding: 40px 20px; + background: var(--blue); + border-radius: 16px; + transition: transform 0.4s ease; +} + +.route.is-lit { + transform: translateY(-4px); +} + +.route__badge { + flex-shrink: 0; + width: 52px; + height: 52px; + border-radius: 7px; + background: #ffffff; + border: 1px solid #e2dede; + box-shadow: 0 4px 4px rgba(0, 0, 0, 0.15), inset 1px -1px 4px rgba(0, 0, 0, 0.15); + transform: rotate(45deg); + display: flex; + align-items: center; + justify-content: center; +} + +.route__badge img { + width: 24px; + height: 24px; + transform: rotate(-45deg); +} + +.route__label { + font-size: 1.1rem; + font-weight: 400; + color: var(--text-white); +} + +/* ═══════════════════════════════════════════════ + Responsive + ═══════════════════════════════════════════════ */ +@media (max-width: 1024px) { + .connected__title { + font-size: 2.1rem; + } + + .connected__routes { + flex-wrap: wrap; + } + + .route { + flex: 1 1 calc(33.333% - 10px); + } +} + +@media (max-width: 640px) { + .connected { + padding: 64px 20px 56px; + } + + .connected__title { + font-size: 1.6rem; + } + + .connected__eyebrow { + font-size: 0.8rem; + } + + .connected__branches { + display: none; + } + + .scan-label { + font-size: 0.7rem; + gap: 8px; + padding: 6px 12px; + } + + .scan-label__icon { + width: 18px; + height: 18px; + } + + .scan-label__key { + font-size: 0.65rem; + } + + .scan-label__value { + font-size: 0.8rem; + } + + .connected__routes { + flex-direction: column; + gap: 12px; + } + + .route { + flex: 1 1 auto; + width: 100%; + flex-direction: row; + align-items: center; + justify-content: flex-start; + min-height: auto; + gap: 16px; + padding: 14px 20px; + } + + .route__label { + font-size: 1rem; + } +} \ No newline at end of file diff --git a/skiply/skiply-connected-systems.html b/skiply/skiply-connected-systems.html new file mode 100644 index 0000000..bd09f3f --- /dev/null +++ b/skiply/skiply-connected-systems.html @@ -0,0 +1,216 @@ + + + + + + + + Skiply.ai – Connected Systems + + + + + + + + + + + +
+
+ + +
+

Connected systems

+

Designed to connect a product to its second life

+
+ + +
+
+ + + Sneaker being scanned by Skiply's AI + + + + + + + + + + Condition + New + + + + + + Resale + In-Store + + + + + + Market + High Demand + + + + + + Value + $110 + + +
+
+ + + + + +
+ + +
    +
  • + + Resale +
  • +
  • + + Marketplace +
  • +
  • + + Recondition +
  • +
  • + + Donation +
  • +
  • + + Recycling +
  • +
+
+ +
+
+ + + + + diff --git a/skiply/skiply-connected-systems.js b/skiply/skiply-connected-systems.js new file mode 100644 index 0000000..012316c --- /dev/null +++ b/skiply/skiply-connected-systems.js @@ -0,0 +1,155 @@ +/* ============================================= + Skiply.ai — Connected Systems section + Scan-the-shoe → distribute-to-tree motion sequence + ============================================= */ + +'use strict'; + +document.addEventListener('DOMContentLoaded', () => { + const section = document.getElementById('connected-systems'); + const shoeStage = document.getElementById('shoeStage'); + const horizon = document.getElementById('scanHorizon'); + const flowLine = document.querySelector('.connected__flow-line'); + const flowDot = document.querySelector('.connected__flow-dot'); + const mesh = document.getElementById('shoeMesh'); + const meshClip = document.getElementById('meshClip'); + const particlesHost = document.getElementById('scanParticles'); + const labels = Array.from(document.querySelectorAll('.scan-label')); + const treeSvg = document.getElementById('treeSvg'); + const branchGroups = Array.from(document.querySelectorAll('.connected__branch-group')); + const nodes = Array.from(document.querySelectorAll('.connected__node')); + const routes = Array.from(document.querySelectorAll('.route')); + + if (!section || !shoeStage || typeof gsap === 'undefined') return; + + const reduceMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches; + + /* ── Build scan particles ───────────────────── */ + const PARTICLE_COUNT = 14; + const particles = []; + for (let i = 0; i < PARTICLE_COUNT; i++) { + const p = document.createElement('span'); + p.className = 'scan-particle'; + const left = 8 + Math.random() * 84; + const top = 6 + Math.random() * 88; + const size = 2 + Math.random() * 3; + p.style.left = left + '%'; + p.style.top = top + '%'; + p.style.width = size + 'px'; + p.style.height = size + 'px'; + particlesHost.appendChild(p); + particles.push({ el: p, left }); + } + + /* ── Ambient dust: persistent, independent of the scroll timeline ── */ + const ambientHost = document.getElementById('ambientParticles'); + const AMBIENT_COUNT = 22; + for (let i = 0; i < AMBIENT_COUNT; i++) { + const p = document.createElement('span'); + p.className = 'ambient-particle'; + const size = 1.5 + Math.random() * 2; + p.style.left = Math.random() * 100 + '%'; + p.style.top = Math.random() * 100 + '%'; + p.style.width = size + 'px'; + p.style.height = size + 'px'; + p.style.setProperty('--dur', (2.5 + Math.random() * 3).toFixed(2) + 's'); + p.style.setProperty('--delay', (Math.random() * 4).toFixed(2) + 's'); + p.style.setProperty('--dx', (Math.random() * 20 - 10).toFixed(1) + 'px'); + p.style.setProperty('--dy', (-6 - Math.random() * 16).toFixed(1) + 'px'); + ambientHost.appendChild(p); + } + + if (reduceMotion) { + gsap.set(shoeStage, { opacity: 1 }); + gsap.set(mesh, { opacity: 0.75 }); + gsap.set(meshClip, { '--reveal': '115%' }); + gsap.set(labels, { opacity: 1, y: 0, scale: 1 }); + gsap.set(particles.map((p) => p.el), { opacity: 0.6 }); + gsap.set(horizon, { opacity: 0.85, scaleX: 1 }); + gsap.set(flowLine, { opacity: 0.7, scaleY: 1 }); + gsap.set(treeSvg, { opacity: 1 }); + gsap.set(branchGroups, { clipPath: 'inset(0% 0% 0% 0%)' }); + gsap.set(nodes, { opacity: 1, scale: 1 }); + routes.forEach((el) => el.classList.add('is-lit')); + return; + } + + gsap.set(shoeStage, { opacity: 0 }); + gsap.set(labels, { opacity: 0, y: 10, scale: 0.9, transformOrigin: '50% 50%' }); + + function playIntro() { + const tl = gsap.timeline({ defaults: { ease: 'power2.out' } }); + + // 1. Stage fades up, ambient glow already pulsing via CSS + tl.to(shoeStage, { opacity: 1, duration: 0.5 }, 0); + + // 2. The horizon line is the whole scan effect: it lights up and + // grows outward from center, passing through the shoe and + // extending past its silhouette on both sides. The wireframe + // mesh reveals in step with it. + tl.to(horizon, { opacity: 0.85, duration: 0.2 }, 0.3) + .to(horizon, { scaleX: 1, duration: 0.6, ease: 'power2.out' }, 0.3) + .to(mesh, { opacity: 0.75, duration: 0.2 }, 0.3) + .to(meshClip, { '--reveal': '115%', duration: 0.6, ease: 'power2.out' }, 0.3); + + // 3. Particles twinkle in across the same window the horizon grows + particles.forEach(({ el, left }) => { + const t = 0.3 + (left / 100) * 0.6; + tl.to(el, { opacity: 1, duration: 0.25 }, t) + .to(el, { opacity: 0.45, y: -4, duration: 0.5 }, t + 0.25); + }); + + // 4. Annotation labels appear one at a time, each a distinct beat + labels.forEach((label, i) => { + tl.to(label, { opacity: 1, y: 0, scale: 1, duration: 0.4, ease: 'back.out(1.7)' }, 0.45 + i * 0.32); + }); + + // 5. The horizon line hands off into a connector that carries the + // light down from the shoe stage into the tree's attachment point. + tl.to(flowLine, { opacity: 0.7, scaleY: 1, duration: 0.4, ease: 'power1.out' }, 1.1) + .fromTo(flowDot, { opacity: 0, top: '0%' }, { opacity: 1, top: '100%', duration: 0.4, ease: 'power1.in' }, 1.1) + .to(flowDot, { opacity: 0, duration: 0.15 }, 1.5); + + // 6. The tree becomes visible, then each branch draws itself in + // center-out: the middle line first, then the two adjacent, then + // the two outer ones — each followed by its node flashing and + // its pill lighting up as the light "arrives". + const centerIndex = Math.floor(branchGroups.length / 2); + const STEP = 0.22; + const BRANCH_DUR = 0.9; + + tl.addLabel('branches', 1.5) + .to(treeSvg, { opacity: 1, duration: 0.3 }, 'branches'); + + branchGroups.forEach((group, i) => { + const pos = `branches+=${(Math.abs(i - centerIndex) * STEP).toFixed(2)}`; + tl.fromTo(group, + { clipPath: 'inset(0% 0% 100% 0%)' }, + { clipPath: 'inset(0% 0% 0% 0%)', duration: BRANCH_DUR, ease: 'power2.out' }, + pos + ); + // Opacity reveal is permanent; the scale punch alone yoyos back down. + tl.to(nodes[i], { opacity: 1, duration: 0.15 }, `${pos}+=${BRANCH_DUR - 0.15}`); + tl.fromTo(nodes[i], + { scale: 1 }, + { scale: 1.5, duration: 0.18, yoyo: true, repeat: 1, ease: 'power2.out' }, + `${pos}+=${BRANCH_DUR - 0.15}` + ); + tl.call(() => routes[i].classList.add('is-lit'), null, `${pos}+=${(BRANCH_DUR - 0.05).toFixed(2)}`); + }); + + return tl; + } + + if (typeof ScrollTrigger !== 'undefined') { + gsap.registerPlugin(ScrollTrigger); + ScrollTrigger.create({ + trigger: section, + start: 'top 70%', + once: true, + onEnter: playIntro, + }); + } else { + playIntro(); + } +}); diff --git a/skiply/skiply-fade.css b/skiply/skiply-fade.css new file mode 100644 index 0000000..58a6511 --- /dev/null +++ b/skiply/skiply-fade.css @@ -0,0 +1,653 @@ +/* ============================================= + Skiply.ai — Styles + Figma: Skiply.ai / node 172-173 + ============================================= */ + +/* ── Keyframes ──────────────────────────────── */ +@keyframes truckBounce { + from { + transform: translate(-50%, -50%) translateY(0px); + } + + to { + transform: translate(-50%, -50%) translateY(-8px); + } +} + +@keyframes dashFlow { + to { + stroke-dashoffset: -48; + } +} + +@keyframes pulseGlow { + + 0%, + 100% { + filter: drop-shadow(0 4px 10px rgba(0, 0, 0, 0.2)); + } + + 50% { + filter: drop-shadow(0 4px 20px rgba(100, 160, 240, 0.55)); + } +} + +/* ── Reset & Tokens ─────────────────────────── */ +*, +*::before, +*::after { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +:root { + --navy: #0a1a37; + --navy-mid: #0d2247; + --light-bg: #f6f6f6; + --road-blue: #b8c8e8; + --road-blue-dark: #8fa8d0; + --text-dark: #0a1a37; + --text-white: #ffffff; + + --font-main: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif; + + /* Typography scale matching Figma */ + --fs-intro: 2.2rem; + /* "From" / "We take only" */ + --fs-number: 11rem; + /* "30" */ + --fs-number-lg: 12.5rem; + /* "15" */ + --fs-unit: 3.1rem; + /* "Days" / "Seconds" */ + --fs-desc: 1.5rem; + /* sub-description */ + --fs-step-title: 1.5rem; + /* "Receive", "Inspect" … */ + --fs-step-desc: 1.05rem; + /* descriptor text */ + + --panel-w: 312px; + --screen-h: 769px; +} + +html { + font-family: var(--font-main); + -webkit-font-smoothing: antialiased; +} + +body { + background: var(--light-bg); + color: var(--text-white); + overflow-x: hidden; +} + +/* ── Screen wrapper ─────────────────────────── */ +.screen { + position: sticky; + top: 0; + width: 1320px; + height: var(--screen-h); + margin: 0 auto; + display: flex; + overflow: hidden; +} + +/* ── Side Panel ─────────────────────────────── */ +.side-panel { + flex-shrink: 0; + width: var(--panel-w); + height: 100%; + background: var(--navy); + display: flex; + flex-direction: column; + justify-content: center; + padding: 48px 40px 48px 36px; + position: relative; + z-index: 2; +} + +.side-panel__intro { + font-size: var(--fs-intro); + font-weight: 400; + line-height: 1; + margin-bottom: 4px; + opacity: 0; + transform: translateY(20px); +} + +.side-panel__number { + font-size: var(--fs-number); + font-weight: 300; + line-height: 0.85; + letter-spacing: -0.04em; + margin-bottom: 2px; + opacity: 0; + transform: translateY(20px); +} + +.side-panel__number--lg { + font-size: var(--fs-number-lg); +} + +.side-panel__unit { + font-size: var(--fs-unit); + font-weight: 400; + line-height: 1; + margin-bottom: 24px; + opacity: 0; + transform: translateY(20px); +} + +.side-panel__desc { + font-size: var(--fs-desc); + font-weight: 400; + line-height: 1.45; + color: rgba(255, 255, 255, 0.65); + opacity: 0; + transform: translateY(20px); +} + +/* ── Main Content Area ──────────────────────── */ +.main-area { + flex: 1; + height: 100%; + background: var(--light-bg); + position: relative; + overflow: hidden; +} + +/* ── Background Decorative Blobs ────────────── */ +.bg-blobs { + position: absolute; + inset: 0; + pointer-events: none; + overflow: hidden; +} + +.blob { + position: absolute; + border-radius: 50%; + opacity: 0.35; + filter: blur(60px); +} + +.screen--prob1 .blob--1 { + width: 600px; + height: 500px; + background: radial-gradient(ellipse, #a8bfe8 0%, transparent 70%); + right: -100px; + top: -80px; +} + +.screen--prob1 .blob--2 { + width: 400px; + height: 350px; + background: radial-gradient(ellipse, #c0d4f0 0%, transparent 70%); + left: 100px; + bottom: -60px; +} + +.screen--prob1 .blob--3 { + width: 350px; + height: 300px; + background: radial-gradient(ellipse, #d4e4f8 0%, transparent 70%); + right: 200px; + bottom: 100px; +} + +.screen--prob2 .blob--1 { + width: 550px; + height: 450px; + background: radial-gradient(ellipse, #b0c8ec 0%, transparent 70%); + right: -80px; + top: -60px; +} + +.screen--prob2 .blob--2 { + width: 380px; + height: 300px; + background: radial-gradient(ellipse, #c8daf4 0%, transparent 70%); + left: 60px; + bottom: -40px; +} + +.screen--prob2 .blob--3 { + width: 300px; + height: 260px; + background: radial-gradient(ellipse, #e0eaf8 0%, transparent 70%); + left: 250px; + top: 120px; +} + +/* ── Background shoe image (Prob 2) ─────────── */ +.shoe-bg { + position: absolute; + inset: 0; + pointer-events: none; + overflow: hidden; +} + +.shoe-bg__img { + position: absolute; + width: 180%; + max-width: none; + left: -10%; + top: -10%; + opacity: 0.18; + filter: blur(3px); + object-fit: cover; +} + +/* ════════════════════════════════════════════ + PROB 1 — Road / Journey Layout + ════════════════════════════════════════════ */ +.road-container { + position: absolute; + inset: 0; +} + +/* SVG Road */ +.road-svg { + position: absolute; + inset: 0; + width: 100%; + height: 100%; +} + +.road-fill { + filter: drop-shadow(0 4px 12px rgba(100, 140, 200, 0.3)); +} + +.road-dashes { + animation: dashFlow 2s linear infinite; +} + +.flow-dashes { + animation: dashFlow 2s linear infinite; +} + +/* Removed SVG connector class */ + +/* ── Road SVG fills the container vertically centred ── */ +.road-svg { + position: absolute; + inset: 0; + width: 100%; + height: 100%; +} + +/* ── Moving Car (truck) ────────────────────── */ +.road-car { + position: absolute; + width: 88px; + height: 88px; + z-index: 10; + /* JS will set left/top via transform */ + transform: translate(-50%, -50%); + pointer-events: none; + opacity: 0; + will-change: transform; + filter: drop-shadow(0 6px 14px rgba(0, 0, 0, 0.25)); +} + +.road-car__img { + width: 100%; + height: 100%; + object-fit: contain; +} + +/* ── Step Icons ─────────────────────────────── */ +.step-icon { + position: absolute; + width: 71px; + height: 71px; + z-index: 5; + transform: translate(-50%, -50%); + opacity: 0; + transition: transform 0.2s ease; +} + +.step-icon img { + width: 100%; + height: 100%; + object-fit: contain; +} + +.step-icon:hover { + transform: translate(-50%, -50%) scale(1.12); +} + +/* + Icon positions anchored to the real path geometry. + Road SVG viewBox = 983 × 473, rendered inside 983×769 area. + xMidYMid meet => rendered SVG height = 473px, top offset = (769-473)/2 = 148px. + + screen_top% = (svgY + 148) / 769 * 100 + Receive : (384+148)/769 = 69.2% + Sort : (449+148)/769 = 77.6% + Inspect : (100+148)/769 = 32.3% + Hold : ( 24+148)/769 = 22.4% + Route : (178+148)/769 = 42.4% + + JS will override these with precise pixel positions after render. + These are fallback values only. +*/ +.step-icon--receive { + left: 11.3%; + top: 69.2%; +} + +.step-icon--sort { + left: 36.7%; + top: 77.6%; +} + +.step-icon--inspect { + left: 49.5%; + top: 32.3%; +} + +.step-icon--hold { + left: 75.2%; + top: 22.4%; +} + +.step-icon--route { + left: 87.7%; + top: 42.4%; +} + +/* ── Step Labels ─────────────────────────────── */ +.step-label { + position: absolute; + z-index: 6; + opacity: 0; + transform: translateY(8px); +} + +.step-label--inspect, +.step-label--sort, +.step-label--route { + padding-left: 12px; +} + +.step-label::before { + content: ""; + position: absolute; + opacity: 0.5; +} + +.step-label--receive::before { + right: -60px; + bottom: 100%; + width: calc(100% + 60px); + height: 190px; + border-bottom: 1.5px solid var(--navy); + border-right: 1.5px solid var(--navy); + background-color: transparent; +} + +.step-label--inspect::before { + left: 0; + top: -10px; + width: 1.5px; + height: 360px; + background-color: var(--navy); + border: none; +} + +.step-label--sort::before { + left: 0; + bottom: 100%; + width: 1.5px; + height: 400px; + background-color: var(--navy); + border: none; +} + +.step-label--hold::before { + right: -60px; + top: -10px; + width: calc(100% + 60px); + height: 230px; + border-top: 1.5px solid var(--navy); + border-right: 1.5px solid var(--navy); + background-color: transparent; +} + +.step-label--route::before { + left: 0; + bottom: 100%; + width: 1.5px; + height: 300px; + background-color: var(--navy); + border: none; +} + +.step-label__title { + display: block; + font-size: var(--fs-step-title); + font-weight: 600; + color: var(--text-dark); + line-height: 1; + margin-bottom: 6px; +} + +.step-label__desc { + display: block; + font-size: var(--fs-step-desc); + font-weight: 400; + color: var(--text-dark); + line-height: 1.45; + max-width: 185px; +} + +/* + Label positions (pre-computed, same logic as icons). + Bottom labels (receive, sort, route): icon top% + ~5.7% (44px/769) + Top labels (inspect, hold): icon top% - ~11.7% (90px/769) +*/ +.step-label--receive { + left: 7%; + top: 74.9%; +} + +/* 69.2 + 5.7 */ +.step-label--sort { + left: 33%; + top: 83.3%; +} + +/* 77.6 + 5.7 */ +.step-label--inspect { + left: 46%; + top: 20.6%; +} + +/* 32.3 - 11.7 */ +.step-label--hold { + left: 71%; + top: 10.7%; +} + +/* 22.4 - 11.7 */ +.step-label--route { + left: 84%; + top: 48.1%; +} + +/* 42.4 + 5.7 */ + +/* ════════════════════════════════════════════ + PROB 2 — Scan → Decision Flow Layout + ════════════════════════════════════════════ */ +.flow-container { + position: absolute; + inset: 0; +} + +/* SVG Flow Path */ +.flow-svg { + position: absolute; + inset: 0; + width: 100%; + height: 100%; +} + +/* ── Shoe icon in center ────────────────────── */ +.flow-shoe-icon { + position: absolute; + width: 96px; + height: 96px; + left: 50%; + top: 50%; + transform: translate(-50%, -50%) rotate(-2.69deg); + z-index: 8; + opacity: 0; + filter: drop-shadow(0 6px 14px rgba(0, 0, 0, 0.15)); +} + +.flow-shoe-icon img { + width: 100%; + height: 100%; + object-fit: contain; +} + +/* ── Flow Nodes ─────────────────────────────── */ +.flow-node { + position: absolute; + width: 71px; + height: 71px; + z-index: 7; + transform: translate(-50%, -50%); + opacity: 0; + transition: transform 0.2s ease; +} + +.flow-node img { + width: 100%; + height: 100%; + object-fit: contain; +} + +.flow-node:hover { + transform: translate(-50%, -50%) scale(1.12); +} + +/* Positions (% within 983px × 769px main area) */ +.flow-node--scan { + left: 29%; + top: 61%; +} + +.flow-node--decision { + left: 67%; + top: 37%; +} + +/* ── Flow Labels ─────────────────────────────── */ +.flow-label { + position: absolute; + z-index: 8; + opacity: 0; + transform: translateY(8px); + padding-left: 12px; +} + +.flow-label::before { + content: ""; + position: absolute; + left: 0; + width: 1.5px; + background-color: var(--navy); + opacity: 0.5; +} + +.flow-label--scan::before { + top: -45px; + height: 45px; +} + +.flow-label--decision::before { + bottom: -45px; + height: 45px; +} + +.flow-label--scan { + left: 22%; + top: 70%; +} + +.flow-label--decision { + left: 60%; + top: 20%; +} + +/* ── Spacing between two screens ─────────────── */ +.screen+.screen { + margin-top: 0; + border-top: 2px solid rgba(255, 255, 255, 0.06); +} + +/* ═══════════════════════════════════════════════ + Responsive – scale for smaller viewports + ═══════════════════════════════════════════════ */ +@media (max-width: 1340px) { + .screen { + width: 100%; + height: auto; + aspect-ratio: 1320 / 769; + } + + :root { + --fs-number: 8vw; + --fs-number-lg: 9vw; + --fs-unit: 2.2vw; + --fs-intro: 1.6vw; + --fs-desc: 1.1vw; + --fs-step-title: 1.1vw; + --fs-step-desc: 0.8vw; + --panel-w: 23.6%; + } +} + +@media (max-width: 768px) { + .screen { + flex-direction: column; + height: auto; + aspect-ratio: unset; + width: 100%; + } + + .side-panel { + width: 100%; + height: auto; + padding: 40px 32px; + flex-direction: row; + flex-wrap: wrap; + align-items: center; + gap: 8px; + } + + .main-area { + min-height: 420px; + width: 100%; + } + + :root { + --fs-number: 20vw; + --fs-number-lg: 22vw; + --fs-unit: 8vw; + --fs-intro: 5vw; + --fs-desc: 3.5vw; + --fs-step-title: 3vw; + --fs-step-desc: 2.2vw; + } +} + + + + + diff --git a/skiply/skiply-fade.html b/skiply/skiply-fade.html new file mode 100644 index 0000000..a274507 --- /dev/null +++ b/skiply/skiply-fade.html @@ -0,0 +1,208 @@ + + + + + + + Skiply.ai – Smarter Shoe Processing + + + + + + + + + + +
+ + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Car +
+ + +
+ + + + +
+
+ + + + +
+
+ + + + + +
+
+ + + + + +
+
+ + + + + + + + +
+ + +
+ Receive + Our team will get back to you promptly +
+
+ Sort + Quickly route products to the right resale channel. +
+
+ Inspect + Assess condition quickly and consistently. +
+
+ Hold + Pause uncertain items for a closer review. +
+
+ Route + Direct every pair to the right next step. +
+ +
+
+
+ + + +
+ + + + + +
+ + + + +
+ + + + + + + + + + + + + + + + + + + + + + + +
+ Car +
+
+
+
+ + + + + + + + diff --git a/skiply/skiply-fullpage.html b/skiply/skiply-fullpage.html new file mode 100644 index 0000000..c8d2ff4 --- /dev/null +++ b/skiply/skiply-fullpage.html @@ -0,0 +1,293 @@ + + + + + + + + Skiply.ai – Smarter Shoe Processing + + + + + + + + + + + + + + + +
+ +
+
+ + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Car +
+ + +
+ + + + +
+
+ + + + +
+
+ + + + + +
+
+ + + + + +
+
+ + + + + + + + +
+ + +
+ Receive + Our team will get back to you promptly +
+
+ Sort + Quickly route products to the right resale channel. +
+
+ Inspect + Assess condition quickly and consistently. +
+
+ Hold + Pause uncertain items for a closer review. +
+
+ Route + Direct every pair to the right next step. +
+ +
+
+
+
+ +
+
+ + + + + +
+ + + + +
+ + + + + + + + + + + + + + + + + + + + + + + +
+ Car +
+
+
+
+
+
+ + + + + + \ No newline at end of file diff --git a/skiply/skiply.css b/skiply/skiply.css new file mode 100644 index 0000000..58a6511 --- /dev/null +++ b/skiply/skiply.css @@ -0,0 +1,653 @@ +/* ============================================= + Skiply.ai — Styles + Figma: Skiply.ai / node 172-173 + ============================================= */ + +/* ── Keyframes ──────────────────────────────── */ +@keyframes truckBounce { + from { + transform: translate(-50%, -50%) translateY(0px); + } + + to { + transform: translate(-50%, -50%) translateY(-8px); + } +} + +@keyframes dashFlow { + to { + stroke-dashoffset: -48; + } +} + +@keyframes pulseGlow { + + 0%, + 100% { + filter: drop-shadow(0 4px 10px rgba(0, 0, 0, 0.2)); + } + + 50% { + filter: drop-shadow(0 4px 20px rgba(100, 160, 240, 0.55)); + } +} + +/* ── Reset & Tokens ─────────────────────────── */ +*, +*::before, +*::after { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +:root { + --navy: #0a1a37; + --navy-mid: #0d2247; + --light-bg: #f6f6f6; + --road-blue: #b8c8e8; + --road-blue-dark: #8fa8d0; + --text-dark: #0a1a37; + --text-white: #ffffff; + + --font-main: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif; + + /* Typography scale matching Figma */ + --fs-intro: 2.2rem; + /* "From" / "We take only" */ + --fs-number: 11rem; + /* "30" */ + --fs-number-lg: 12.5rem; + /* "15" */ + --fs-unit: 3.1rem; + /* "Days" / "Seconds" */ + --fs-desc: 1.5rem; + /* sub-description */ + --fs-step-title: 1.5rem; + /* "Receive", "Inspect" … */ + --fs-step-desc: 1.05rem; + /* descriptor text */ + + --panel-w: 312px; + --screen-h: 769px; +} + +html { + font-family: var(--font-main); + -webkit-font-smoothing: antialiased; +} + +body { + background: var(--light-bg); + color: var(--text-white); + overflow-x: hidden; +} + +/* ── Screen wrapper ─────────────────────────── */ +.screen { + position: sticky; + top: 0; + width: 1320px; + height: var(--screen-h); + margin: 0 auto; + display: flex; + overflow: hidden; +} + +/* ── Side Panel ─────────────────────────────── */ +.side-panel { + flex-shrink: 0; + width: var(--panel-w); + height: 100%; + background: var(--navy); + display: flex; + flex-direction: column; + justify-content: center; + padding: 48px 40px 48px 36px; + position: relative; + z-index: 2; +} + +.side-panel__intro { + font-size: var(--fs-intro); + font-weight: 400; + line-height: 1; + margin-bottom: 4px; + opacity: 0; + transform: translateY(20px); +} + +.side-panel__number { + font-size: var(--fs-number); + font-weight: 300; + line-height: 0.85; + letter-spacing: -0.04em; + margin-bottom: 2px; + opacity: 0; + transform: translateY(20px); +} + +.side-panel__number--lg { + font-size: var(--fs-number-lg); +} + +.side-panel__unit { + font-size: var(--fs-unit); + font-weight: 400; + line-height: 1; + margin-bottom: 24px; + opacity: 0; + transform: translateY(20px); +} + +.side-panel__desc { + font-size: var(--fs-desc); + font-weight: 400; + line-height: 1.45; + color: rgba(255, 255, 255, 0.65); + opacity: 0; + transform: translateY(20px); +} + +/* ── Main Content Area ──────────────────────── */ +.main-area { + flex: 1; + height: 100%; + background: var(--light-bg); + position: relative; + overflow: hidden; +} + +/* ── Background Decorative Blobs ────────────── */ +.bg-blobs { + position: absolute; + inset: 0; + pointer-events: none; + overflow: hidden; +} + +.blob { + position: absolute; + border-radius: 50%; + opacity: 0.35; + filter: blur(60px); +} + +.screen--prob1 .blob--1 { + width: 600px; + height: 500px; + background: radial-gradient(ellipse, #a8bfe8 0%, transparent 70%); + right: -100px; + top: -80px; +} + +.screen--prob1 .blob--2 { + width: 400px; + height: 350px; + background: radial-gradient(ellipse, #c0d4f0 0%, transparent 70%); + left: 100px; + bottom: -60px; +} + +.screen--prob1 .blob--3 { + width: 350px; + height: 300px; + background: radial-gradient(ellipse, #d4e4f8 0%, transparent 70%); + right: 200px; + bottom: 100px; +} + +.screen--prob2 .blob--1 { + width: 550px; + height: 450px; + background: radial-gradient(ellipse, #b0c8ec 0%, transparent 70%); + right: -80px; + top: -60px; +} + +.screen--prob2 .blob--2 { + width: 380px; + height: 300px; + background: radial-gradient(ellipse, #c8daf4 0%, transparent 70%); + left: 60px; + bottom: -40px; +} + +.screen--prob2 .blob--3 { + width: 300px; + height: 260px; + background: radial-gradient(ellipse, #e0eaf8 0%, transparent 70%); + left: 250px; + top: 120px; +} + +/* ── Background shoe image (Prob 2) ─────────── */ +.shoe-bg { + position: absolute; + inset: 0; + pointer-events: none; + overflow: hidden; +} + +.shoe-bg__img { + position: absolute; + width: 180%; + max-width: none; + left: -10%; + top: -10%; + opacity: 0.18; + filter: blur(3px); + object-fit: cover; +} + +/* ════════════════════════════════════════════ + PROB 1 — Road / Journey Layout + ════════════════════════════════════════════ */ +.road-container { + position: absolute; + inset: 0; +} + +/* SVG Road */ +.road-svg { + position: absolute; + inset: 0; + width: 100%; + height: 100%; +} + +.road-fill { + filter: drop-shadow(0 4px 12px rgba(100, 140, 200, 0.3)); +} + +.road-dashes { + animation: dashFlow 2s linear infinite; +} + +.flow-dashes { + animation: dashFlow 2s linear infinite; +} + +/* Removed SVG connector class */ + +/* ── Road SVG fills the container vertically centred ── */ +.road-svg { + position: absolute; + inset: 0; + width: 100%; + height: 100%; +} + +/* ── Moving Car (truck) ────────────────────── */ +.road-car { + position: absolute; + width: 88px; + height: 88px; + z-index: 10; + /* JS will set left/top via transform */ + transform: translate(-50%, -50%); + pointer-events: none; + opacity: 0; + will-change: transform; + filter: drop-shadow(0 6px 14px rgba(0, 0, 0, 0.25)); +} + +.road-car__img { + width: 100%; + height: 100%; + object-fit: contain; +} + +/* ── Step Icons ─────────────────────────────── */ +.step-icon { + position: absolute; + width: 71px; + height: 71px; + z-index: 5; + transform: translate(-50%, -50%); + opacity: 0; + transition: transform 0.2s ease; +} + +.step-icon img { + width: 100%; + height: 100%; + object-fit: contain; +} + +.step-icon:hover { + transform: translate(-50%, -50%) scale(1.12); +} + +/* + Icon positions anchored to the real path geometry. + Road SVG viewBox = 983 × 473, rendered inside 983×769 area. + xMidYMid meet => rendered SVG height = 473px, top offset = (769-473)/2 = 148px. + + screen_top% = (svgY + 148) / 769 * 100 + Receive : (384+148)/769 = 69.2% + Sort : (449+148)/769 = 77.6% + Inspect : (100+148)/769 = 32.3% + Hold : ( 24+148)/769 = 22.4% + Route : (178+148)/769 = 42.4% + + JS will override these with precise pixel positions after render. + These are fallback values only. +*/ +.step-icon--receive { + left: 11.3%; + top: 69.2%; +} + +.step-icon--sort { + left: 36.7%; + top: 77.6%; +} + +.step-icon--inspect { + left: 49.5%; + top: 32.3%; +} + +.step-icon--hold { + left: 75.2%; + top: 22.4%; +} + +.step-icon--route { + left: 87.7%; + top: 42.4%; +} + +/* ── Step Labels ─────────────────────────────── */ +.step-label { + position: absolute; + z-index: 6; + opacity: 0; + transform: translateY(8px); +} + +.step-label--inspect, +.step-label--sort, +.step-label--route { + padding-left: 12px; +} + +.step-label::before { + content: ""; + position: absolute; + opacity: 0.5; +} + +.step-label--receive::before { + right: -60px; + bottom: 100%; + width: calc(100% + 60px); + height: 190px; + border-bottom: 1.5px solid var(--navy); + border-right: 1.5px solid var(--navy); + background-color: transparent; +} + +.step-label--inspect::before { + left: 0; + top: -10px; + width: 1.5px; + height: 360px; + background-color: var(--navy); + border: none; +} + +.step-label--sort::before { + left: 0; + bottom: 100%; + width: 1.5px; + height: 400px; + background-color: var(--navy); + border: none; +} + +.step-label--hold::before { + right: -60px; + top: -10px; + width: calc(100% + 60px); + height: 230px; + border-top: 1.5px solid var(--navy); + border-right: 1.5px solid var(--navy); + background-color: transparent; +} + +.step-label--route::before { + left: 0; + bottom: 100%; + width: 1.5px; + height: 300px; + background-color: var(--navy); + border: none; +} + +.step-label__title { + display: block; + font-size: var(--fs-step-title); + font-weight: 600; + color: var(--text-dark); + line-height: 1; + margin-bottom: 6px; +} + +.step-label__desc { + display: block; + font-size: var(--fs-step-desc); + font-weight: 400; + color: var(--text-dark); + line-height: 1.45; + max-width: 185px; +} + +/* + Label positions (pre-computed, same logic as icons). + Bottom labels (receive, sort, route): icon top% + ~5.7% (44px/769) + Top labels (inspect, hold): icon top% - ~11.7% (90px/769) +*/ +.step-label--receive { + left: 7%; + top: 74.9%; +} + +/* 69.2 + 5.7 */ +.step-label--sort { + left: 33%; + top: 83.3%; +} + +/* 77.6 + 5.7 */ +.step-label--inspect { + left: 46%; + top: 20.6%; +} + +/* 32.3 - 11.7 */ +.step-label--hold { + left: 71%; + top: 10.7%; +} + +/* 22.4 - 11.7 */ +.step-label--route { + left: 84%; + top: 48.1%; +} + +/* 42.4 + 5.7 */ + +/* ════════════════════════════════════════════ + PROB 2 — Scan → Decision Flow Layout + ════════════════════════════════════════════ */ +.flow-container { + position: absolute; + inset: 0; +} + +/* SVG Flow Path */ +.flow-svg { + position: absolute; + inset: 0; + width: 100%; + height: 100%; +} + +/* ── Shoe icon in center ────────────────────── */ +.flow-shoe-icon { + position: absolute; + width: 96px; + height: 96px; + left: 50%; + top: 50%; + transform: translate(-50%, -50%) rotate(-2.69deg); + z-index: 8; + opacity: 0; + filter: drop-shadow(0 6px 14px rgba(0, 0, 0, 0.15)); +} + +.flow-shoe-icon img { + width: 100%; + height: 100%; + object-fit: contain; +} + +/* ── Flow Nodes ─────────────────────────────── */ +.flow-node { + position: absolute; + width: 71px; + height: 71px; + z-index: 7; + transform: translate(-50%, -50%); + opacity: 0; + transition: transform 0.2s ease; +} + +.flow-node img { + width: 100%; + height: 100%; + object-fit: contain; +} + +.flow-node:hover { + transform: translate(-50%, -50%) scale(1.12); +} + +/* Positions (% within 983px × 769px main area) */ +.flow-node--scan { + left: 29%; + top: 61%; +} + +.flow-node--decision { + left: 67%; + top: 37%; +} + +/* ── Flow Labels ─────────────────────────────── */ +.flow-label { + position: absolute; + z-index: 8; + opacity: 0; + transform: translateY(8px); + padding-left: 12px; +} + +.flow-label::before { + content: ""; + position: absolute; + left: 0; + width: 1.5px; + background-color: var(--navy); + opacity: 0.5; +} + +.flow-label--scan::before { + top: -45px; + height: 45px; +} + +.flow-label--decision::before { + bottom: -45px; + height: 45px; +} + +.flow-label--scan { + left: 22%; + top: 70%; +} + +.flow-label--decision { + left: 60%; + top: 20%; +} + +/* ── Spacing between two screens ─────────────── */ +.screen+.screen { + margin-top: 0; + border-top: 2px solid rgba(255, 255, 255, 0.06); +} + +/* ═══════════════════════════════════════════════ + Responsive – scale for smaller viewports + ═══════════════════════════════════════════════ */ +@media (max-width: 1340px) { + .screen { + width: 100%; + height: auto; + aspect-ratio: 1320 / 769; + } + + :root { + --fs-number: 8vw; + --fs-number-lg: 9vw; + --fs-unit: 2.2vw; + --fs-intro: 1.6vw; + --fs-desc: 1.1vw; + --fs-step-title: 1.1vw; + --fs-step-desc: 0.8vw; + --panel-w: 23.6%; + } +} + +@media (max-width: 768px) { + .screen { + flex-direction: column; + height: auto; + aspect-ratio: unset; + width: 100%; + } + + .side-panel { + width: 100%; + height: auto; + padding: 40px 32px; + flex-direction: row; + flex-wrap: wrap; + align-items: center; + gap: 8px; + } + + .main-area { + min-height: 420px; + width: 100%; + } + + :root { + --fs-number: 20vw; + --fs-number-lg: 22vw; + --fs-unit: 8vw; + --fs-intro: 5vw; + --fs-desc: 3.5vw; + --fs-step-title: 3vw; + --fs-step-desc: 2.2vw; + } +} + + + + + diff --git a/skiply/skiply.html b/skiply/skiply.html new file mode 100644 index 0000000..6e3c00b --- /dev/null +++ b/skiply/skiply.html @@ -0,0 +1,208 @@ + + + + + + + Skiply.ai – Smarter Shoe Processing + + + + + + + + + + +
+ + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Car +
+ + +
+ + + + +
+
+ + + + +
+
+ + + + + +
+
+ + + + + +
+
+ + + + + + + + +
+ + +
+ Receive + Our team will get back to you promptly +
+
+ Sort + Quickly route products to the right resale channel. +
+
+ Inspect + Assess condition quickly and consistently. +
+
+ Hold + Pause uncertain items for a closer review. +
+
+ Route + Direct every pair to the right next step. +
+ +
+
+
+ + + +
+ + + + + +
+ + + + +
+ + + + + + + + + + + + + + + + + + + + + + + +
+ Car +
+
+
+
+ + + + + + + + diff --git a/skiply/tree-final-crop.png b/skiply/tree-final-crop.png new file mode 100644 index 0000000..fdcaa6b Binary files /dev/null and b/skiply/tree-final-crop.png differ diff --git a/skiply/tree-final.png b/skiply/tree-final.png new file mode 100644 index 0000000..58aa8aa Binary files /dev/null and b/skiply/tree-final.png differ diff --git a/skiply/tree-progressive-t1.png b/skiply/tree-progressive-t1.png new file mode 100644 index 0000000..57d4f19 Binary files /dev/null and b/skiply/tree-progressive-t1.png differ diff --git a/skiply/tree-progressive-t2.png b/skiply/tree-progressive-t2.png new file mode 100644 index 0000000..c8e198d Binary files /dev/null and b/skiply/tree-progressive-t2.png differ diff --git a/skiply/tree-progressive-t3.png b/skiply/tree-progressive-t3.png new file mode 100644 index 0000000..eb1f155 Binary files /dev/null and b/skiply/tree-progressive-t3.png differ diff --git a/skiply/tree-progressive-t4.png b/skiply/tree-progressive-t4.png new file mode 100644 index 0000000..3f34280 Binary files /dev/null and b/skiply/tree-progressive-t4.png differ