Created
March 27, 2026 12:01
-
-
Save vdavid/0f7e7ee63e382ffe55a7a59419219b7e to your computer and use it in GitHub Desktop.
Cmdr 3D shape gallery — 30 interactive SVG shapes with hover animations
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <!DOCTYPE html> | |
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Cmdr 3D shape gallery</title> | |
| <style> | |
| * { margin: 0; padding: 0; box-sizing: border-box; } | |
| body { | |
| background: #14130f; | |
| color: #fafafa; | |
| font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif; | |
| min-height: 100vh; | |
| padding: 40px 20px; | |
| } | |
| h1 { | |
| text-align: center; | |
| font-size: 28px; | |
| font-weight: 600; | |
| margin-bottom: 8px; | |
| color: #ffc206; | |
| } | |
| .subtitle { | |
| text-align: center; | |
| color: #a1a1aa; | |
| font-size: 14px; | |
| margin-bottom: 48px; | |
| } | |
| /* ── Feature section ── */ | |
| .feature-group { | |
| max-width: 1400px; | |
| margin: 0 auto 48px; | |
| } | |
| .feature-header { | |
| margin-bottom: 20px; | |
| padding-left: 4px; | |
| } | |
| .feature-title { | |
| font-size: 20px; | |
| font-weight: 600; | |
| color: #ffc206; | |
| margin-bottom: 4px; | |
| } | |
| .feature-desc { | |
| font-size: 14px; | |
| color: #a1a1aa; | |
| } | |
| .variant-grid { | |
| display: grid; | |
| grid-template-columns: repeat(3, 1fr); | |
| gap: 24px; | |
| } | |
| @media (max-width: 900px) { | |
| .variant-grid { grid-template-columns: 1fr 1fr; } | |
| } | |
| @media (max-width: 600px) { | |
| .variant-grid { grid-template-columns: 1fr; } | |
| } | |
| /* ── Gallery section ── */ | |
| .divider { | |
| max-width: 1400px; | |
| margin: 64px auto 48px; | |
| text-align: center; | |
| position: relative; | |
| } | |
| .divider::before { | |
| content: ''; | |
| position: absolute; | |
| top: 50%; | |
| left: 0; | |
| right: 0; | |
| height: 1px; | |
| background: #2e2d2a; | |
| } | |
| .divider span { | |
| position: relative; | |
| background: #14130f; | |
| padding: 0 16px; | |
| color: #7a5210; | |
| font-size: 12px; | |
| font-weight: 600; | |
| letter-spacing: 0.1em; | |
| text-transform: uppercase; | |
| } | |
| .gallery-grid { | |
| display: grid; | |
| grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); | |
| gap: 32px; | |
| max-width: 1400px; | |
| margin: 0 auto; | |
| } | |
| /* ── Shared card styles ── */ | |
| .card { | |
| background: #1a1917; | |
| border: 1px solid #2e2d2a; | |
| border-radius: 16px; | |
| padding: 24px; | |
| display: flex; | |
| flex-direction: column; | |
| align-items: center; | |
| transition: border-color 0.3s ease, box-shadow 0.3s ease; | |
| } | |
| .card:hover { | |
| border-color: #d49422; | |
| box-shadow: 0 0 30px rgba(255, 194, 6, 0.3); | |
| } | |
| .card svg { | |
| width: 100%; | |
| max-width: 280px; | |
| height: auto; | |
| } | |
| .card-info { | |
| margin-top: 16px; | |
| text-align: center; | |
| } | |
| .card-name { | |
| font-size: 15px; | |
| font-weight: 600; | |
| color: #ffc206; | |
| margin-bottom: 4px; | |
| } | |
| .card-desc { | |
| font-size: 13px; | |
| color: #a1a1aa; | |
| line-height: 1.5; | |
| } | |
| .card-anim { | |
| display: inline-block; | |
| margin-top: 8px; | |
| font-size: 11px; | |
| color: #7a5210; | |
| background: rgba(255, 194, 6, 0.08); | |
| border: 1px solid rgba(255, 194, 6, 0.15); | |
| border-radius: 100px; | |
| padding: 2px 10px; | |
| font-family: 'SF Mono', 'Fira Code', monospace; | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <h1>Cmdr 3D shape gallery</h1> | |
| <p class="subtitle">Hover each shape to see its animation. 30 shapes generated with scripts/3d-shapes/.</p> | |
| <!-- Feature illustrations: 3 variants per feature --> | |
| <div class="feature-group"> | |
| <div class="feature-header"> | |
| <h2 class="feature-title">Live full-disk index</h2> | |
| <p class="feature-desc">Indexes your entire drive once in about 4 minutes. Then stays current forever.</p> | |
| </div> | |
| <div class="variant-grid"> | |
| <div class="card"> | |
| <svg id="shape-index-—-stacked-discs" viewBox="0 0 400 400" width="400" height="400" class="w-full max-w-[400px] h-auto overflow-visible" style="cursor: pointer;"> | |
| <g class="shape-group" style="transform-origin: 50% 50%;"> | |
| <polygon data-face="0" points="251.86,165.26 191.99,177.39 191.99,190.56 251.86,178.42" fill="#6b480c" stroke="#b67e1c" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="1" points="191.99,177.39 140.13,158.60 140.13,171.77 191.99,190.56" fill="#8a5e14" stroke="#d49422" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="2" points="249.26,184.50 192.39,196.03 192.39,209.19 249.26,197.67" fill="#6b480c" stroke="#b67e1c" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="3" points="192.39,196.03 143.12,178.18 143.12,191.34 192.39,209.19" fill="#8a5e14" stroke="#d49422" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="4" points="246.67,203.75 192.79,214.67 192.79,227.83 246.67,216.91" fill="#6b480c" stroke="#b67e1c" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="5" points="192.79,214.67 146.12,197.76 146.12,210.92 192.79,227.83" fill="#8a5e14" stroke="#d49422" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="6" points="259.87,134.33 251.86,165.26 251.86,178.42 259.87,147.50" fill="#4e3608" stroke="#986816" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="7" points="244.08,222.99 193.19,233.31 193.19,246.47 244.08,236.16" fill="#6b480c" stroke="#b67e1c" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="8" points="259.87,134.33 251.86,165.26 191.99,177.39 140.13,158.60 148.14,127.68 208.01,115.54" fill="#f0b400" stroke="#ffc206" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="9" points="193.19,233.31 149.11,217.34 149.11,230.50 193.19,246.47" fill="#8a5e14" stroke="#d49422" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="10" points="208.01,128.71 148.14,140.84 140.13,171.77 191.99,190.56 251.86,178.42 259.87,147.50" fill="#5c3d0a" stroke="#986816" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="11" points="256.88,155.12 249.26,184.50 249.26,197.67 256.88,168.29" fill="#4e3608" stroke="#986816" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="12" points="140.13,158.60 148.14,127.68 148.14,140.84 140.13,171.77" fill="#a87218" stroke="#f0b400" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="13" points="256.88,155.12 249.26,184.50 192.39,196.03 143.12,178.18 150.74,148.80 207.61,137.27" fill="#f0b400" stroke="#ffc206" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="14" points="241.48,242.24 193.59,251.95 193.59,265.11 241.48,255.40" fill="#6b480c" stroke="#b67e1c" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="15" points="193.59,251.95 152.10,236.91 152.10,250.08 193.59,265.11" fill="#8a5e14" stroke="#d49422" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="16" points="207.61,150.44 150.74,161.97 143.12,191.34 192.39,209.19 249.26,197.67 256.88,168.29" fill="#5c3d0a" stroke="#986816" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="17" points="253.88,175.91 246.67,203.75 246.67,216.91 253.88,189.08" fill="#4e3608" stroke="#986816" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="18" points="143.12,178.18 150.74,148.80 150.74,161.97 143.12,191.34" fill="#a87218" stroke="#f0b400" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="19" points="253.88,175.91 246.67,203.75 192.79,214.67 146.12,197.76 153.33,169.93 207.21,159.00" fill="#f0b400" stroke="#ffc206" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="20" points="207.21,172.17 153.33,183.09 146.12,210.92 192.79,227.83 246.67,216.91 253.88,189.08" fill="#5c3d0a" stroke="#986816" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="21" points="146.12,197.76 153.33,169.93 153.33,183.09 146.12,210.92" fill="#a87218" stroke="#f0b400" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="22" points="250.89,196.71 244.08,222.99 244.08,236.16 250.89,209.87" fill="#4e3608" stroke="#986816" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="23" points="250.89,196.71 244.08,222.99 193.19,233.31 149.11,217.34 155.92,191.05 206.81,180.73" fill="#f0b400" stroke="#ffc206" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="24" points="206.81,193.90 155.92,204.21 149.11,230.50 193.19,246.47 244.08,236.16 250.89,209.87" fill="#5c3d0a" stroke="#986816" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="25" points="149.11,217.34 155.92,191.05 155.92,204.21 149.11,230.50" fill="#a87218" stroke="#f0b400" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="26" points="247.90,217.50 241.48,242.24 241.48,255.40 247.90,230.66" fill="#4e3608" stroke="#986816" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="27" points="208.01,115.54 259.87,134.33 259.87,147.50 208.01,128.71" fill="#e4a024" stroke="#ffd23f" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="28" points="247.90,217.50 241.48,242.24 193.59,251.95 152.10,236.91 158.52,212.17 206.41,202.47" fill="#f0b400" stroke="#ffc206" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="29" points="148.14,127.68 208.01,115.54 208.01,128.71 148.14,140.84" fill="#c48a1e" stroke="#ffc206" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="30" points="206.41,215.63 158.52,225.34 152.10,250.08 193.59,265.11 241.48,255.40 247.90,230.66" fill="#5c3d0a" stroke="#986816" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="31" points="207.61,137.27 256.88,155.12 256.88,168.29 207.61,150.44" fill="#e4a024" stroke="#ffd23f" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="32" points="152.10,236.91 158.52,212.17 158.52,225.34 152.10,250.08" fill="#a87218" stroke="#f0b400" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="33" points="150.74,148.80 207.61,137.27 207.61,150.44 150.74,161.97" fill="#c48a1e" stroke="#ffc206" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="34" points="207.21,159.00 253.88,175.91 253.88,189.08 207.21,172.17" fill="#e4a024" stroke="#ffd23f" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="35" points="153.33,169.93 207.21,159.00 207.21,172.17 153.33,183.09" fill="#c48a1e" stroke="#ffc206" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="36" points="206.81,180.73 250.89,196.71 250.89,209.87 206.81,193.90" fill="#e4a024" stroke="#ffd23f" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="37" points="155.92,191.05 206.81,180.73 206.81,193.90 155.92,204.21" fill="#c48a1e" stroke="#ffc206" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="38" points="206.41,202.47 247.90,217.50 247.90,230.66 206.41,215.63" fill="#e4a024" stroke="#ffd23f" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="39" points="158.52,212.17 206.41,202.47 206.41,215.63 158.52,225.34" fill="#c48a1e" stroke="#ffc206" stroke-width="1.5" stroke-linejoin="round"/> | |
| </g> | |
| </svg> | |
| <script> | |
| function radialData(el, size) { | |
| const faces = el.querySelectorAll('[data-face]'); | |
| const data = []; | |
| faces.forEach(function(f) { | |
| var pts = f.getAttribute('points').split(' ').map(function(p) { return p.split(',').map(Number); }); | |
| var cx = pts.reduce(function(s, p) { return s + p[0]; }, 0) / pts.length; | |
| var cy = pts.reduce(function(s, p) { return s + p[1]; }, 0) / pts.length; | |
| var rx = cx - size / 2; | |
| var ry = cy - size / 2; | |
| var dist = Math.sqrt(rx * rx + ry * ry) || 0.001; | |
| var angle = Math.atan2(ry, rx); | |
| // Unit radial direction | |
| var ux = rx / dist; | |
| var uy = ry / dist; | |
| f.style.transformOrigin = cx + 'px ' + cy + 'px'; | |
| data.push({ el: f, cx: cx, cy: cy, rx: rx, ry: ry, dist: dist, angle: angle, ux: ux, uy: uy }); | |
| }); | |
| return data; | |
| } | |
| (function() { | |
| var el = document.getElementById('shape-index-—-stacked-discs'); | |
| var rd = radialData(el, 400); | |
| var t = 0; | |
| (function tick() { | |
| t += 0.02; | |
| var hovering = el.matches(':hover'); | |
| rd.forEach(function(d, i) { | |
| var phase = d.dist * 0.02 + i * 0.1; | |
| var wave = Math.sin(t * 0.2 + phase); | |
| if (hovering) { | |
| // Radial push/pull | |
| var mag = wave * 6; | |
| d.el.style.transition = 'transform 0.3s ease'; | |
| d.el.style.transform = 'translate(' + (d.ux * mag) + 'px, ' + (d.uy * mag) + 'px)'; | |
| } else { | |
| // Gentle scale breathing | |
| d.el.style.transition = 'none'; | |
| d.el.style.transform = 'scale(' + (1 + wave * 0.02) + ')'; | |
| } | |
| }); | |
| requestAnimationFrame(tick); | |
| })(); | |
| })();</script> | |
| <div class="card-info"> | |
| <div class="card-name">Index — stacked discs</div> | |
| <div class="card-desc">Database-like stacked discs that gently breathe, evoking layered storage.</div> | |
| <span class="card-anim">breathe</span> | |
| </div> | |
| </div> | |
| <div class="card"> | |
| <svg id="shape-index-—-hex-rings" viewBox="0 0 400 400" width="400" height="400" class="w-full max-w-[400px] h-auto overflow-visible" style="cursor: pointer;"> | |
| <g class="shape-group" style="transform-origin: 50% 50%;"> | |
| <polygon data-face="0" points="275.79,214.30 188.29,232.03 188.29,258.36 275.79,240.63" fill="#6b480c" stroke="#b67e1c" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="1" points="266.46,212.37 185.03,226.62 185.03,251.30 266.46,237.05" fill="#7a5210" stroke="#d49422" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="2" points="257.50,210.07 182.62,221.17 182.62,244.21 257.50,233.10" fill="#8a5e14" stroke="#d49422" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="3" points="188.29,232.03 112.50,204.57 112.50,230.90 188.29,258.36" fill="#8a5e14" stroke="#d49422" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="4" points="248.95,207.41 181.07,215.72 181.07,237.11 248.95,228.80" fill="#986816" stroke="#f0b400" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="5" points="185.03,226.62 118.57,199.94 118.57,224.62 185.03,251.30" fill="#986816" stroke="#f0b400" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="6" points="182.62,221.17 125.13,195.64 125.13,218.67 182.62,244.21" fill="#a87218" stroke="#f0b400" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="7" points="181.07,215.72 132.12,191.69 132.12,213.08 181.07,237.11" fill="#b67e1c" stroke="#f0b400" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="8" points="267.88,175.07 248.95,207.41 248.95,228.80 267.88,196.46" fill="#8a5e14" stroke="#d49422" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="9" points="274.87,173.43 257.50,210.07 257.50,233.10 274.87,196.46" fill="#7a5210" stroke="#b67e1c" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="10" points="281.43,171.43 266.46,212.37 266.46,237.05 281.43,196.12" fill="#5c3d0a" stroke="#b67e1c" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="11" points="267.88,175.07 248.95,207.41 181.07,215.72 132.12,191.69 151.05,159.35 218.93,151.04" fill="#f0b400" stroke="#ffc206" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="12" points="287.50,169.10 275.79,214.30 275.79,240.63 287.50,195.43" fill="#4e3608" stroke="#986816" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="13" points="274.87,173.43 257.50,210.07 182.62,221.17 125.13,195.64 142.50,159.00 217.38,147.89" fill="#f0b400" stroke="#ffc206" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="14" points="281.43,171.43 266.46,212.37 185.03,226.62 118.57,199.94 133.54,159.00 214.97,144.75" fill="#f0b400" stroke="#ffc206" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="15" points="287.50,169.10 275.79,214.30 188.29,232.03 112.50,204.57 124.21,159.37 211.71,141.64" fill="#f0b400" stroke="#ffc206" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="16" points="218.93,172.43 151.05,180.74 132.12,213.08 181.07,237.11 248.95,228.80 267.88,196.46" fill="#5c3d0a" stroke="#986816" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="17" points="217.38,170.93 142.50,182.03 125.13,218.67 182.62,244.21 257.50,233.10 274.87,196.46" fill="#5c3d0a" stroke="#986816" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="18" points="214.97,169.43 133.54,183.68 118.57,224.62 185.03,251.30 266.46,237.05 281.43,196.12" fill="#5c3d0a" stroke="#986816" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="19" points="211.71,167.97 124.21,185.70 112.50,230.90 188.29,258.36 275.79,240.63 287.50,195.43" fill="#5c3d0a" stroke="#986816" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="20" points="112.50,204.57 124.21,159.37 124.21,185.70 112.50,230.90" fill="#a87218" stroke="#f0b400" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="21" points="118.57,199.94 133.54,159.00 133.54,183.68 118.57,224.62" fill="#b67e1c" stroke="#ffc206" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="22" points="125.13,195.64 142.50,159.00 142.50,182.03 125.13,218.67" fill="#c48a1e" stroke="#ffc206" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="23" points="132.12,191.69 151.05,159.35 151.05,180.74 132.12,213.08" fill="#c48a1e" stroke="#ffc206" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="24" points="218.93,151.04 267.88,175.07 267.88,196.46 218.93,172.43" fill="#f0b400" stroke="#ffe066" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="25" points="217.38,147.89 274.87,173.43 274.87,196.46 217.38,170.93" fill="#f0b400" stroke="#ffe066" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="26" points="151.05,159.35 218.93,151.04 218.93,172.43 151.05,180.74" fill="#e4a024" stroke="#ffd23f" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="27" points="214.97,144.75 281.43,171.43 281.43,196.12 214.97,169.43" fill="#f0b400" stroke="#ffe066" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="28" points="142.50,159.00 217.38,147.89 217.38,170.93 142.50,182.03" fill="#d49422" stroke="#ffd23f" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="29" points="211.71,141.64 287.50,169.10 287.50,195.43 211.71,167.97" fill="#e4a024" stroke="#ffd23f" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="30" points="133.54,159.00 214.97,144.75 214.97,169.43 133.54,183.68" fill="#d49422" stroke="#ffd23f" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="31" points="124.21,159.37 211.71,141.64 211.71,167.97 124.21,185.70" fill="#c48a1e" stroke="#ffc206" stroke-width="1.5" stroke-linejoin="round"/> | |
| </g> | |
| </svg> | |
| <script> | |
| function radialData(el, size) { | |
| const faces = el.querySelectorAll('[data-face]'); | |
| const data = []; | |
| faces.forEach(function(f) { | |
| var pts = f.getAttribute('points').split(' ').map(function(p) { return p.split(',').map(Number); }); | |
| var cx = pts.reduce(function(s, p) { return s + p[0]; }, 0) / pts.length; | |
| var cy = pts.reduce(function(s, p) { return s + p[1]; }, 0) / pts.length; | |
| var rx = cx - size / 2; | |
| var ry = cy - size / 2; | |
| var dist = Math.sqrt(rx * rx + ry * ry) || 0.001; | |
| var angle = Math.atan2(ry, rx); | |
| // Unit radial direction | |
| var ux = rx / dist; | |
| var uy = ry / dist; | |
| f.style.transformOrigin = cx + 'px ' + cy + 'px'; | |
| data.push({ el: f, cx: cx, cy: cy, rx: rx, ry: ry, dist: dist, angle: angle, ux: ux, uy: uy }); | |
| }); | |
| return data; | |
| } | |
| (function() { | |
| var el = document.getElementById('shape-index-—-hex-rings'); | |
| var rd = radialData(el, 400); | |
| rd.forEach(function(d) { | |
| var mag = 35 + d.dist * 0.35; | |
| d.el.style.transition = 'transform 0.8s cubic-bezier(0.34, 1.56, 0.64, 1)'; | |
| d.el.style.transform = 'translate(' + (d.ux * mag) + 'px, ' + (d.uy * mag) + 'px)'; | |
| }); | |
| el.addEventListener('mouseenter', function() { | |
| rd.forEach(function(d) { | |
| d.el.style.transform = 'translate(0, 0)'; | |
| }); | |
| }); | |
| el.addEventListener('mouseleave', function() { | |
| rd.forEach(function(d) { | |
| var mag = 35 + d.dist * 0.35; | |
| d.el.style.transform = 'translate(' + (d.ux * mag) + 'px, ' + (d.uy * mag) + 'px)'; | |
| }); | |
| }); | |
| })();</script> | |
| <div class="card-info"> | |
| <div class="card-name">Index — hex rings</div> | |
| <div class="card-desc">Concentric hexagonal rings that assemble inward, like an index building itself.</div> | |
| <span class="card-anim">assemble</span> | |
| </div> | |
| </div> | |
| <div class="card"> | |
| <svg id="shape-index-—-disc-tower" viewBox="0 0 400 400" width="400" height="400" class="w-full max-w-[400px] h-auto overflow-visible" style="cursor: pointer;"> | |
| <g class="shape-group" style="transform-origin: 50% 50%;"> | |
| <polygon data-face="0" points="243.88,165.00 193.22,175.27 193.22,184.04 243.88,173.77" fill="#6b480c" stroke="#b67e1c" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="1" points="193.22,175.27 149.34,159.37 149.34,168.14 193.22,184.04" fill="#8a5e14" stroke="#d49422" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="2" points="241.68,178.25 193.56,188.00 193.56,196.78 241.68,187.02" fill="#6b480c" stroke="#b67e1c" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="3" points="193.56,188.00 151.87,172.90 151.87,181.67 193.56,196.78" fill="#8a5e14" stroke="#d49422" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="4" points="239.49,191.49 193.90,200.73 193.90,209.51 239.49,200.27" fill="#6b480c" stroke="#b67e1c" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="5" points="193.90,200.73 154.41,186.42 154.41,195.20 193.90,209.51" fill="#8a5e14" stroke="#d49422" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="6" points="237.30,204.74 194.24,213.47 194.24,222.24 237.30,213.51" fill="#6b480c" stroke="#b67e1c" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="7" points="194.24,213.47 156.94,199.95 156.94,208.73 194.24,222.24" fill="#8a5e14" stroke="#d49422" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="8" points="250.66,138.83 243.88,165.00 243.88,173.77 250.66,147.61" fill="#4e3608" stroke="#986816" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="9" points="250.66,138.83 243.88,165.00 193.22,175.27 149.34,159.37 156.12,133.20 206.78,122.93" fill="#f0b400" stroke="#ffc206" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="10" points="235.10,217.98 194.58,226.20 194.58,234.98 235.10,226.76" fill="#6b480c" stroke="#b67e1c" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="11" points="206.78,131.71 156.12,141.98 149.34,168.14 193.22,184.04 243.88,173.77 250.66,147.61" fill="#5c3d0a" stroke="#986816" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="12" points="248.13,153.39 241.68,178.25 241.68,187.02 248.13,162.16" fill="#4e3608" stroke="#986816" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="13" points="194.58,226.20 159.47,213.48 159.47,222.26 194.58,234.98" fill="#8a5e14" stroke="#d49422" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="14" points="149.34,159.37 156.12,133.20 156.12,141.98 149.34,168.14" fill="#a87218" stroke="#f0b400" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="15" points="248.13,153.39 241.68,178.25 193.56,188.00 151.87,172.90 158.32,148.04 206.44,138.28" fill="#f0b400" stroke="#ffc206" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="16" points="232.91,231.23 194.91,238.93 194.91,247.71 232.91,240.01" fill="#6b480c" stroke="#b67e1c" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="17" points="206.44,147.06 158.32,156.81 151.87,181.67 193.56,196.78 241.68,187.02 248.13,162.16" fill="#5c3d0a" stroke="#986816" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="18" points="245.59,167.94 239.49,191.49 239.49,200.27 245.59,176.72" fill="#4e3608" stroke="#986816" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="19" points="151.87,172.90 158.32,148.04 158.32,156.81 151.87,181.67" fill="#a87218" stroke="#f0b400" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="20" points="245.59,167.94 239.49,191.49 193.90,200.73 154.41,186.42 160.51,162.87 206.10,153.63" fill="#f0b400" stroke="#ffc206" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="21" points="194.91,238.93 162.01,227.01 162.01,235.78 194.91,247.71" fill="#8a5e14" stroke="#d49422" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="22" points="206.10,162.41 160.51,171.65 154.41,195.20 193.90,209.51 239.49,200.27 245.59,176.72" fill="#5c3d0a" stroke="#986816" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="23" points="243.06,182.50 237.30,204.74 237.30,213.51 243.06,191.27" fill="#4e3608" stroke="#986816" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="24" points="230.71,244.48 195.25,251.67 195.25,260.44 230.71,253.25" fill="#6b480c" stroke="#b67e1c" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="25" points="154.41,186.42 160.51,162.87 160.51,171.65 154.41,195.20" fill="#a87218" stroke="#f0b400" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="26" points="243.06,182.50 237.30,204.74 194.24,213.47 156.94,199.95 162.70,177.71 205.76,168.98" fill="#f0b400" stroke="#ffc206" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="27" points="195.25,251.67 164.54,240.54 164.54,249.31 195.25,260.44" fill="#8a5e14" stroke="#d49422" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="28" points="205.76,177.76 162.70,186.49 156.94,208.73 194.24,222.24 237.30,213.51 243.06,191.27" fill="#5c3d0a" stroke="#986816" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="29" points="240.53,197.05 235.10,217.98 235.10,226.76 240.53,205.83" fill="#4e3608" stroke="#986816" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="30" points="156.94,199.95 162.70,177.71 162.70,186.49 156.94,208.73" fill="#a87218" stroke="#f0b400" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="31" points="240.53,197.05 235.10,217.98 194.58,226.20 159.47,213.48 164.90,192.55 205.42,184.33" fill="#f0b400" stroke="#ffc206" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="32" points="205.42,193.11 164.90,201.32 159.47,222.26 194.58,234.98 235.10,226.76 240.53,205.83" fill="#5c3d0a" stroke="#986816" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="33" points="206.78,122.93 250.66,138.83 250.66,147.61 206.78,131.71" fill="#e4a024" stroke="#ffd23f" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="34" points="237.99,211.61 232.91,231.23 232.91,240.01 237.99,220.38" fill="#4e3608" stroke="#986816" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="35" points="159.47,213.48 164.90,192.55 164.90,201.32 159.47,222.26" fill="#a87218" stroke="#f0b400" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="36" points="237.99,211.61 232.91,231.23 194.91,238.93 162.01,227.01 167.09,207.38 205.09,199.68" fill="#f0b400" stroke="#ffc206" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="37" points="156.12,133.20 206.78,122.93 206.78,131.71 156.12,141.98" fill="#c48a1e" stroke="#ffc206" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="38" points="206.44,138.28 248.13,153.39 248.13,162.16 206.44,147.06" fill="#e4a024" stroke="#ffd23f" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="39" points="205.09,208.46 167.09,216.16 162.01,235.78 194.91,247.71 232.91,240.01 237.99,220.38" fill="#5c3d0a" stroke="#986816" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="40" points="162.01,227.01 167.09,207.38 167.09,216.16 162.01,235.78" fill="#a87218" stroke="#f0b400" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="41" points="235.46,226.16 230.71,244.48 230.71,253.25 235.46,234.94" fill="#4e3608" stroke="#986816" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="42" points="235.46,226.16 230.71,244.48 195.25,251.67 164.54,240.54 169.29,222.22 204.75,215.03" fill="#f0b400" stroke="#ffc206" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="43" points="158.32,148.04 206.44,138.28 206.44,147.06 158.32,156.81" fill="#c48a1e" stroke="#ffc206" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="44" points="206.10,153.63 245.59,167.94 245.59,176.72 206.10,162.41" fill="#e4a024" stroke="#ffd23f" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="45" points="204.75,223.81 169.29,230.99 164.54,249.31 195.25,260.44 230.71,253.25 235.46,234.94" fill="#5c3d0a" stroke="#986816" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="46" points="164.54,240.54 169.29,222.22 169.29,230.99 164.54,249.31" fill="#a87218" stroke="#f0b400" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="47" points="160.51,162.87 206.10,153.63 206.10,162.41 160.51,171.65" fill="#c48a1e" stroke="#ffc206" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="48" points="205.76,168.98 243.06,182.50 243.06,191.27 205.76,177.76" fill="#e4a024" stroke="#ffd23f" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="49" points="162.70,177.71 205.76,168.98 205.76,177.76 162.70,186.49" fill="#c48a1e" stroke="#ffc206" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="50" points="205.42,184.33 240.53,197.05 240.53,205.83 205.42,193.11" fill="#e4a024" stroke="#ffd23f" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="51" points="164.90,192.55 205.42,184.33 205.42,193.11 164.90,201.32" fill="#c48a1e" stroke="#ffc206" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="52" points="205.09,199.68 237.99,211.61 237.99,220.38 205.09,208.46" fill="#e4a024" stroke="#ffd23f" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="53" points="167.09,207.38 205.09,199.68 205.09,208.46 167.09,216.16" fill="#c48a1e" stroke="#ffc206" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="54" points="204.75,215.03 235.46,226.16 235.46,234.94 204.75,223.81" fill="#e4a024" stroke="#ffd23f" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="55" points="169.29,222.22 204.75,215.03 204.75,223.81 169.29,230.99" fill="#c48a1e" stroke="#ffc206" stroke-width="1.5" stroke-linejoin="round"/> | |
| </g> | |
| </svg> | |
| <script> | |
| (function() { | |
| var el = document.getElementById('shape-index-—-disc-tower'); | |
| var g = el.querySelector('.shape-group'); | |
| g.style.transformOrigin = '50% 50%'; | |
| var t = 0, mouseX = 0, mouseY = 0, hovering = false; | |
| el.addEventListener('mouseenter', function() { hovering = true; }); | |
| el.addEventListener('mouseleave', function() { hovering = false; }); | |
| el.addEventListener('mousemove', function(e) { | |
| var rect = el.getBoundingClientRect(); | |
| mouseX = (e.clientX - rect.left) / rect.width - 0.5; | |
| mouseY = (e.clientY - rect.top) / rect.height - 0.5; | |
| }); | |
| (function tick() { | |
| t += 0.03; | |
| var floatY = Math.sin(t * 0.14285714285714285) * 6; | |
| var floatR = Math.sin(t * 0.09999999999999999) * 2; | |
| if (hovering) { | |
| g.style.transition = 'transform 0.2s ease-out'; | |
| g.style.transform = 'translateY(' + floatY + 'px) perspective(400px) rotateX(' + (mouseY * -15) + 'deg) rotateY(' + (mouseX * 15) + 'deg)'; | |
| } else { | |
| g.style.transition = 'transform 0.6s ease-out'; | |
| g.style.transform = 'translateY(' + floatY + 'px) rotate(' + floatR + 'deg)'; | |
| } | |
| requestAnimationFrame(tick); | |
| })(); | |
| })();</script> | |
| <div class="card-info"> | |
| <div class="card-name">Index — disc tower</div> | |
| <div class="card-desc">A tall disc tower floating gently, tilting toward the cursor on hover.</div> | |
| <span class="card-anim">float</span> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="feature-group"> | |
| <div class="feature-header"> | |
| <h2 class="feature-title">Blazing fast</h2> | |
| <p class="feature-desc">Built in Rust. Opens a 100k-file folder in 4 seconds with icons, sizes, and dates.</p> | |
| </div> | |
| <div class="variant-grid"> | |
| <div class="card"> | |
| <svg id="shape-speed-—-arrow-burst" viewBox="0 0 400 400" width="400" height="400" class="w-full max-w-[400px] h-auto overflow-visible" style="cursor: pointer;"> | |
| <g class="shape-group" style="transform-origin: 50% 50%;"> | |
| <polygon data-face="0" points="131.37,189.04 191.24,124.25 208.76,144.12 148.89,208.91" fill="#f0b400" stroke="#ffe066" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="1" points="170.28,194.31 131.37,189.04 148.89,208.91 187.81,214.18" fill="#d49422" stroke="#ffd23f" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="2" points="148.89,208.91 187.81,214.18 187.81,280.00 229.72,271.51 229.72,205.69 268.63,184.64 208.76,144.12" fill="#5c3d0a" stroke="#986816" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="3" points="191.24,124.25 251.11,164.77 268.63,184.64 208.76,144.12" fill="#4e3608" stroke="#986816" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="4" points="170.28,260.13 170.28,194.31 187.81,214.18 187.81,280.00" fill="#b67e1c" stroke="#ffc206" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="5" points="251.11,164.77 212.19,185.82 229.72,205.69 268.63,184.64" fill="#5c3d0a" stroke="#b67e1c" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="6" points="191.24,124.25 251.11,164.77 212.19,185.82 212.19,251.64 170.28,260.13 170.28,194.31 131.37,189.04" fill="#f0b400" stroke="#ffc206" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="7" points="212.19,185.82 212.19,251.64 229.72,271.51 229.72,205.69" fill="#7a5210" stroke="#d49422" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="8" points="212.19,251.64 170.28,260.13 187.81,280.00 229.72,271.51" fill="#986816" stroke="#f0b400" stroke-width="1.5" stroke-linejoin="round"/> | |
| </g> | |
| </svg> | |
| <script> | |
| function radialData(el, size) { | |
| const faces = el.querySelectorAll('[data-face]'); | |
| const data = []; | |
| faces.forEach(function(f) { | |
| var pts = f.getAttribute('points').split(' ').map(function(p) { return p.split(',').map(Number); }); | |
| var cx = pts.reduce(function(s, p) { return s + p[0]; }, 0) / pts.length; | |
| var cy = pts.reduce(function(s, p) { return s + p[1]; }, 0) / pts.length; | |
| var rx = cx - size / 2; | |
| var ry = cy - size / 2; | |
| var dist = Math.sqrt(rx * rx + ry * ry) || 0.001; | |
| var angle = Math.atan2(ry, rx); | |
| // Unit radial direction | |
| var ux = rx / dist; | |
| var uy = ry / dist; | |
| f.style.transformOrigin = cx + 'px ' + cy + 'px'; | |
| data.push({ el: f, cx: cx, cy: cy, rx: rx, ry: ry, dist: dist, angle: angle, ux: ux, uy: uy }); | |
| }); | |
| return data; | |
| } | |
| (function() { | |
| var el = document.getElementById('shape-speed-—-arrow-burst'); | |
| var rd = radialData(el, 400); | |
| var g = el.querySelector('.shape-group'); | |
| var t = 0; | |
| rd.forEach(function(d) { | |
| d.el.style.transition = 'transform 0.5s cubic-bezier(0.34, 1.56, 0.64, 1)'; | |
| }); | |
| (function idleTick() { | |
| t += 0.15; | |
| if (!el.matches(':hover')) { | |
| g.style.transform = 'rotate(' + (Math.sin(t * 0.02) * 2) + 'deg)'; | |
| } | |
| requestAnimationFrame(idleTick); | |
| })(); | |
| el.addEventListener('mouseenter', function() { | |
| rd.forEach(function(d) { | |
| // Strictly radial: magnitude = base + proportional to distance from center | |
| var mag = 30 + d.dist * 0.4; | |
| d.el.style.transform = 'translate(' + (d.ux * mag) + 'px, ' + (d.uy * mag) + 'px)'; | |
| }); | |
| }); | |
| el.addEventListener('mouseleave', function() { | |
| rd.forEach(function(d) { d.el.style.transform = 'translate(0, 0)'; }); | |
| }); | |
| })();</script> | |
| <div class="card-info"> | |
| <div class="card-name">Speed — arrow burst</div> | |
| <div class="card-desc">A sharp arrow prism that explodes apart, evoking bursts of speed.</div> | |
| <span class="card-anim">explode</span> | |
| </div> | |
| </div> | |
| <div class="card"> | |
| <svg id="shape-speed-—-pyramid-streak" viewBox="0 0 400 400" width="400" height="400" class="w-full max-w-[400px] h-auto overflow-visible" style="cursor: pointer;"> | |
| <g class="shape-group" style="transform-origin: 50% 50%;"> | |
| <polygon data-face="0" points="200.00,134.18 263.82,288.94 190.14,303.88" fill="#8a5e14" stroke="#b67e1c" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="1" points="200.00,134.18 190.14,303.88 126.32,280.75" fill="#986816" stroke="#d49422" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="2" points="200.00,134.18 273.68,250.88 263.82,288.94" fill="#7a5210" stroke="#b67e1c" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="3" points="200.00,134.18 126.32,280.75 136.18,242.69" fill="#b67e1c" stroke="#f0b400" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="4" points="209.86,227.76 136.18,242.69 126.32,280.75 190.14,303.88 263.82,288.94 273.68,250.88" fill="#5c3d0a" stroke="#986816" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="5" points="200.00,134.18 209.86,227.76 273.68,250.88" fill="#d49422" stroke="#ffd23f" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="6" points="200.00,134.18 136.18,242.69 209.86,227.76" fill="#c48a1e" stroke="#ffc206" stroke-width="1.5" stroke-linejoin="round"/> | |
| </g> | |
| </svg> | |
| <script> | |
| function radialData(el, size) { | |
| const faces = el.querySelectorAll('[data-face]'); | |
| const data = []; | |
| faces.forEach(function(f) { | |
| var pts = f.getAttribute('points').split(' ').map(function(p) { return p.split(',').map(Number); }); | |
| var cx = pts.reduce(function(s, p) { return s + p[0]; }, 0) / pts.length; | |
| var cy = pts.reduce(function(s, p) { return s + p[1]; }, 0) / pts.length; | |
| var rx = cx - size / 2; | |
| var ry = cy - size / 2; | |
| var dist = Math.sqrt(rx * rx + ry * ry) || 0.001; | |
| var angle = Math.atan2(ry, rx); | |
| // Unit radial direction | |
| var ux = rx / dist; | |
| var uy = ry / dist; | |
| f.style.transformOrigin = cx + 'px ' + cy + 'px'; | |
| data.push({ el: f, cx: cx, cy: cy, rx: rx, ry: ry, dist: dist, angle: angle, ux: ux, uy: uy }); | |
| }); | |
| return data; | |
| } | |
| (function() { | |
| var el = document.getElementById('shape-speed-—-pyramid-streak'); | |
| var rd = radialData(el, 400); | |
| rd.forEach(function(d) { | |
| d.el.style.transition = 'transform 0.6s cubic-bezier(0.34, 1.56, 0.64, 1)'; | |
| }); | |
| el.addEventListener('mouseenter', function() { | |
| rd.forEach(function(d, i) { | |
| var mag = 25 + d.dist * 0.5; | |
| d.el.style.transform = 'translate(' + (d.ux * mag) + 'px, ' + (d.uy * mag) + 'px)'; | |
| // Staggered delay creates the spiral wave feel | |
| d.el.style.transitionDelay = (i * 0.03) + 's'; | |
| }); | |
| }); | |
| el.addEventListener('mouseleave', function() { | |
| rd.forEach(function(d, i) { | |
| d.el.style.transform = 'translate(0, 0)'; | |
| d.el.style.transitionDelay = (i * 0.015) + 's'; | |
| }); | |
| }); | |
| })();</script> | |
| <div class="card-info"> | |
| <div class="card-name">Speed — pyramid streak</div> | |
| <div class="card-desc">A tall pointed pyramid that spirals outward on hover — forward momentum frozen in 3D.</div> | |
| <span class="card-anim">spiral</span> | |
| </div> | |
| </div> | |
| <div class="card"> | |
| <svg id="shape-speed-—-gem-flash" viewBox="0 0 400 400" width="400" height="400" class="w-full max-w-[400px] h-auto overflow-visible" style="cursor: pointer;"> | |
| <g class="shape-group" style="transform-origin: 50% 50%;"> | |
| <polygon data-face="0" points="211.68,150.06 188.72,150.14 177.44,212.61 223.37,212.45" fill="#c48a1e" stroke="#ffc206" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="1" points="200.00,129.79 211.68,150.06 188.72,150.14" fill="#ffd23f" stroke="#ffc206" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="2" points="200.00,129.79 188.72,150.14 172.37,142.41" fill="#f0b400" stroke="#ffc206" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="3" points="200.00,129.79 227.80,142.22 211.68,150.06" fill="#ffc206" stroke="#ffc206" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="4" points="188.72,150.14 172.37,142.41 144.74,197.16 177.44,212.61" fill="#c48a1e" stroke="#ffd23f" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="5" points="227.80,142.22 211.68,150.06 223.37,212.45 255.60,196.77" fill="#b67e1c" stroke="#ffc206" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="6" points="200.00,129.79 172.37,142.41 172.20,131.41" fill="#ffc206" stroke="#ffc206" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="7" points="200.00,129.79 227.63,131.21 227.80,142.22" fill="#f0b400" stroke="#ffc206" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="8" points="223.37,212.45 177.44,212.61 200.00,270.21" fill="#7a5210" stroke="#b67e1c" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="9" points="200.00,129.79 172.20,131.41 188.32,123.57" fill="#ffd23f" stroke="#ffc206" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="10" points="200.00,129.79 211.28,123.49 227.63,131.21" fill="#ffc206" stroke="#ffc206" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="11" points="172.37,142.41 172.20,131.41 144.40,175.14 144.74,197.16" fill="#d49422" stroke="#ffd23f" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="12" points="227.63,131.21 227.80,142.22 255.60,196.77 255.26,174.76" fill="#b67e1c" stroke="#ffc206" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="13" points="200.00,129.79 188.32,123.57 211.28,123.49" fill="#f0b400" stroke="#ffc206" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="14" points="177.44,212.61 144.74,197.16 200.00,270.21" fill="#7a5210" stroke="#b67e1c" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="15" points="255.60,196.77 223.37,212.45 200.00,270.21" fill="#6b480c" stroke="#986816" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="16" points="172.20,131.41 188.32,123.57 176.63,159.46 144.40,175.14" fill="#d49422" stroke="#ffd23f" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="17" points="211.28,123.49 227.63,131.21 255.26,174.76 222.56,159.30" fill="#e4a024" stroke="#ffe066" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="18" points="144.74,197.16 144.40,175.14 200.00,270.21" fill="#8a5e14" stroke="#d49422" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="19" points="255.26,174.76 255.60,196.77 200.00,270.21" fill="#6b480c" stroke="#986816" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="20" points="188.32,123.57 211.28,123.49 222.56,159.30 176.63,159.46" fill="#e4a024" stroke="#ffe066" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="21" points="144.40,175.14 176.63,159.46 200.00,270.21" fill="#986816" stroke="#d49422" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="22" points="222.56,159.30 255.26,174.76 200.00,270.21" fill="#a87218" stroke="#f0b400" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="23" points="176.63,159.46 222.56,159.30 200.00,270.21" fill="#986816" stroke="#f0b400" stroke-width="1.5" stroke-linejoin="round"/> | |
| </g> | |
| </svg> | |
| <script> | |
| (function() { | |
| var el = document.getElementById('shape-speed-—-gem-flash'); | |
| var g = el.querySelector('.shape-group'); | |
| g.style.transformOrigin = '50% 50%'; | |
| el.addEventListener('mouseenter', function() { | |
| g.style.transition = 'transform 0.15s ease'; | |
| g.style.transform = 'rotate(8deg) scale(1.05)'; | |
| setTimeout(function() { | |
| g.style.transition = 'transform 0.3s cubic-bezier(0.34, 1.56, 0.64, 1)'; | |
| g.style.transform = 'rotate(-5deg) scale(1.05)'; | |
| setTimeout(function() { g.style.transform = 'rotate(0deg) scale(1.05)'; }, 300); | |
| }, 150); | |
| }); | |
| el.addEventListener('mouseleave', function() { | |
| g.style.transition = 'transform 0.4s cubic-bezier(0.34, 1.56, 0.64, 1)'; | |
| g.style.transform = 'rotate(0deg) scale(1)'; | |
| }); | |
| })();</script> | |
| <div class="card-info"> | |
| <div class="card-name">Speed — gem flash</div> | |
| <div class="card-desc">An elongated diamond that wobbles like it was hit at high speed.</div> | |
| <span class="card-anim">wobble</span> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="feature-group"> | |
| <div class="feature-header"> | |
| <h2 class="feature-title">Keyboard-first</h2> | |
| <p class="feature-desc">Navigate, select, copy, move. All without touching your mouse.</p> | |
| </div> | |
| <div class="variant-grid"> | |
| <div class="card"> | |
| <svg id="shape-keys-—-cube-grid" viewBox="0 0 400 400" width="400" height="400" class="w-full max-w-[400px] h-auto overflow-visible" style="cursor: pointer;"> | |
| <g class="shape-group" style="transform-origin: 50% 50%;"> | |
| <polygon data-face="0" points="176.02,218.90 167.89,220.55 167.89,228.30 176.02,226.65" fill="#7a5210" stroke="#b67e1c" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="1" points="167.89,220.55 176.02,218.90 172.58,215.00 164.45,216.65" fill="#d49422" stroke="#ffc206" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="2" points="167.89,220.55 164.45,216.65 164.45,224.40 167.89,228.30" fill="#986816" stroke="#d49422" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="3" points="172.58,215.00 176.02,218.90 176.02,226.65 172.58,222.75" fill="#b67e1c" stroke="#f0b400" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="4" points="164.45,224.40 172.58,222.75 176.02,226.65 167.89,228.30" fill="#f0b400" stroke="#ffd23f" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="5" points="164.45,216.65 172.58,215.00 172.58,222.75 164.45,224.40" fill="#5c3d0a" stroke="#986816" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="6" points="206.56,211.92 194.34,214.39 194.34,226.04 206.56,223.56" fill="#7a5210" stroke="#b67e1c" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="7" points="194.34,214.39 206.56,211.92 201.40,206.05 189.17,208.53" fill="#d49422" stroke="#ffc206" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="8" points="194.34,214.39 189.17,208.53 189.17,220.18 194.34,226.04" fill="#986816" stroke="#d49422" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="9" points="201.40,206.05 206.56,211.92 206.56,223.56 201.40,217.70" fill="#b67e1c" stroke="#f0b400" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="10" points="237.00,204.98 220.83,208.26 220.83,223.66 237.00,220.39" fill="#7a5210" stroke="#b67e1c" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="11" points="189.17,220.18 201.40,217.70 206.56,223.56 194.34,226.04" fill="#f0b400" stroke="#ffd23f" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="12" points="189.17,208.53 201.40,206.05 201.40,217.70 189.17,220.18" fill="#5c3d0a" stroke="#986816" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="13" points="220.83,208.26 237.00,204.98 230.16,197.23 214.00,200.51" fill="#d49422" stroke="#ffc206" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="14" points="220.83,208.26 214.00,200.51 214.00,215.91 220.83,223.66" fill="#986816" stroke="#d49422" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="15" points="169.35,203.27 154.17,206.35 154.17,220.81 169.35,217.73" fill="#7a5210" stroke="#b67e1c" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="16" points="154.17,206.35 169.35,203.27 162.93,195.99 147.75,199.07" fill="#d49422" stroke="#ffc206" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="17" points="154.17,206.35 147.75,199.07 147.75,213.53 154.17,220.81" fill="#986816" stroke="#d49422" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="18" points="230.16,197.23 237.00,204.98 237.00,220.39 230.16,212.63" fill="#b67e1c" stroke="#f0b400" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="19" points="214.00,215.91 230.16,212.63 237.00,220.39 220.83,223.66" fill="#f0b400" stroke="#ffd23f" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="20" points="261.00,201.11 249.94,203.35 249.94,213.88 261.00,211.64" fill="#7a5210" stroke="#b67e1c" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="21" points="162.93,195.99 169.35,203.27 169.35,217.73 162.93,210.46" fill="#b67e1c" stroke="#f0b400" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="22" points="214.00,200.51 230.16,197.23 230.16,212.63 214.00,215.91" fill="#5c3d0a" stroke="#986816" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="23" points="249.94,203.35 261.00,201.11 256.32,195.81 245.26,198.05" fill="#d49422" stroke="#ffc206" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="24" points="147.75,213.53 162.93,210.46 169.35,217.73 154.17,220.81" fill="#f0b400" stroke="#ffd23f" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="25" points="249.94,203.35 245.26,198.05 245.26,208.58 249.94,213.88" fill="#986816" stroke="#d49422" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="26" points="192.65,199.73 183.56,201.57 183.56,210.23 192.65,208.39" fill="#7a5210" stroke="#b67e1c" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="27" points="147.75,199.07 162.93,195.99 162.93,210.46 147.75,213.53" fill="#5c3d0a" stroke="#986816" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="28" points="183.56,201.57 192.65,199.73 188.81,195.37 179.72,197.21" fill="#d49422" stroke="#ffc206" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="29" points="256.32,195.81 261.00,201.11 261.00,211.64 256.32,206.34" fill="#b67e1c" stroke="#f0b400" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="30" points="183.56,201.57 179.72,197.21 179.72,205.87 183.56,210.23" fill="#986816" stroke="#d49422" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="31" points="245.26,208.58 256.32,206.34 261.00,211.64 249.94,213.88" fill="#f0b400" stroke="#ffd23f" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="32" points="245.26,198.05 256.32,195.81 256.32,206.34 245.26,208.58" fill="#5c3d0a" stroke="#986816" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="33" points="188.81,195.37 192.65,199.73 192.65,208.39 188.81,204.03" fill="#b67e1c" stroke="#f0b400" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="34" points="179.72,205.87 188.81,204.03 192.65,208.39 183.56,210.23" fill="#f0b400" stroke="#ffd23f" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="35" points="179.72,197.21 188.81,195.37 188.81,204.03 179.72,205.87" fill="#5c3d0a" stroke="#986816" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="36" points="220.88,193.84 210.95,195.86 210.95,205.31 220.88,203.30" fill="#7a5210" stroke="#b67e1c" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="37" points="210.95,195.86 220.88,193.84 216.68,189.09 206.75,191.10" fill="#d49422" stroke="#ffc206" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="38" points="210.95,195.86 206.75,191.10 206.75,200.55 210.95,205.31" fill="#986816" stroke="#d49422" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="39" points="155.52,191.04 143.36,193.51 143.36,205.09 155.52,202.63" fill="#7a5210" stroke="#b67e1c" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="40" points="143.36,193.51 155.52,191.04 150.38,185.22 138.22,187.68" fill="#d49422" stroke="#ffc206" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="41" points="143.36,193.51 138.22,187.68 138.22,199.26 143.36,205.09" fill="#986816" stroke="#d49422" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="42" points="216.68,189.09 220.88,193.84 220.88,203.30 216.68,198.54" fill="#b67e1c" stroke="#f0b400" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="43" points="206.75,200.55 216.68,198.54 220.88,203.30 210.95,205.31" fill="#f0b400" stroke="#ffd23f" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="44" points="252.69,186.26 236.89,189.46 236.89,204.51 252.69,201.31" fill="#7a5210" stroke="#b67e1c" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="45" points="206.75,191.10 216.68,189.09 216.68,198.54 206.75,200.55" fill="#5c3d0a" stroke="#986816" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="46" points="150.38,185.22 155.52,191.04 155.52,202.63 150.38,196.80" fill="#b67e1c" stroke="#f0b400" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="47" points="236.89,189.46 252.69,186.26 246.01,178.68 230.21,181.89" fill="#d49422" stroke="#ffc206" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="48" points="186.01,184.08 169.83,187.36 169.83,202.78 186.01,199.50" fill="#7a5210" stroke="#b67e1c" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="49" points="138.22,199.26 150.38,196.80 155.52,202.63 143.36,205.09" fill="#f0b400" stroke="#ffd23f" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="50" points="236.89,189.46 230.21,181.89 230.21,196.94 236.89,204.51" fill="#986816" stroke="#d49422" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="51" points="138.22,187.68 150.38,185.22 150.38,196.80 138.22,199.26" fill="#5c3d0a" stroke="#986816" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="52" points="169.83,187.36 186.01,184.08 179.17,176.33 162.99,179.61" fill="#d49422" stroke="#ffc206" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="53" points="169.83,187.36 162.99,179.61 162.99,195.02 169.83,202.78" fill="#986816" stroke="#d49422" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="54" points="246.01,178.68 252.69,186.26 252.69,201.31 246.01,193.74" fill="#b67e1c" stroke="#f0b400" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="55" points="230.21,196.94 246.01,193.74 252.69,201.31 236.89,204.51" fill="#f0b400" stroke="#ffd23f" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="56" points="179.17,176.33 186.01,184.08 186.01,199.50 179.17,191.74" fill="#b67e1c" stroke="#f0b400" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="57" points="230.21,181.89 246.01,178.68 246.01,193.74 230.21,196.94" fill="#5c3d0a" stroke="#986816" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="58" points="162.99,195.02 179.17,191.74 186.01,199.50 169.83,202.78" fill="#f0b400" stroke="#ffd23f" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="59" points="210.05,180.19 198.92,182.45 198.92,193.04 210.05,190.79" fill="#7a5210" stroke="#b67e1c" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="60" points="162.99,179.61 179.17,176.33 179.17,191.74 162.99,195.02" fill="#5c3d0a" stroke="#986816" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="61" points="198.92,182.45 210.05,180.19 205.34,174.86 194.22,177.11" fill="#d49422" stroke="#ffc206" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="62" points="198.92,182.45 194.22,177.11 194.22,187.71 198.92,193.04" fill="#986816" stroke="#d49422" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="63" points="205.34,174.86 210.05,180.19 210.05,190.79 205.34,185.46" fill="#b67e1c" stroke="#f0b400" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="64" points="194.22,187.71 205.34,185.46 210.05,190.79 198.92,193.04" fill="#f0b400" stroke="#ffd23f" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="65" points="194.22,177.11 205.34,174.86 205.34,185.46 194.22,187.71" fill="#5c3d0a" stroke="#986816" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="66" points="235.75,175.51 227.34,177.21 227.34,185.22 235.75,183.52" fill="#7a5210" stroke="#b67e1c" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="67" points="227.34,177.21 235.75,175.51 232.19,171.48 223.79,173.18" fill="#d49422" stroke="#ffc206" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="68" points="227.34,177.21 223.79,173.18 223.79,181.19 227.34,185.22" fill="#986816" stroke="#d49422" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="69" points="232.19,171.48 235.75,175.51 235.75,183.52 232.19,179.49" fill="#b67e1c" stroke="#f0b400" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="70" points="223.79,181.19 232.19,179.49 235.75,183.52 227.34,185.22" fill="#f0b400" stroke="#ffd23f" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="71" points="223.79,173.18 232.19,171.48 232.19,179.49 223.79,181.19" fill="#5c3d0a" stroke="#986816" stroke-width="1.5" stroke-linejoin="round"/> | |
| </g> | |
| </svg> | |
| <script> | |
| function radialData(el, size) { | |
| const faces = el.querySelectorAll('[data-face]'); | |
| const data = []; | |
| faces.forEach(function(f) { | |
| var pts = f.getAttribute('points').split(' ').map(function(p) { return p.split(',').map(Number); }); | |
| var cx = pts.reduce(function(s, p) { return s + p[0]; }, 0) / pts.length; | |
| var cy = pts.reduce(function(s, p) { return s + p[1]; }, 0) / pts.length; | |
| var rx = cx - size / 2; | |
| var ry = cy - size / 2; | |
| var dist = Math.sqrt(rx * rx + ry * ry) || 0.001; | |
| var angle = Math.atan2(ry, rx); | |
| // Unit radial direction | |
| var ux = rx / dist; | |
| var uy = ry / dist; | |
| f.style.transformOrigin = cx + 'px ' + cy + 'px'; | |
| data.push({ el: f, cx: cx, cy: cy, rx: rx, ry: ry, dist: dist, angle: angle, ux: ux, uy: uy }); | |
| }); | |
| return data; | |
| } | |
| (function() { | |
| var el = document.getElementById('shape-keys-—-cube-grid'); | |
| var rd = radialData(el, 400); | |
| var rand = function(seed) { var x = Math.sin(seed * 127.1 + 311.7) * 43758.5453; return x - Math.floor(x); }; | |
| rd.forEach(function(d) { | |
| d.el.style.transition = 'transform 0.6s cubic-bezier(0.34, 1.56, 0.64, 1)'; | |
| }); | |
| el.addEventListener('mouseenter', function() { | |
| rd.forEach(function(d, i) { | |
| // Radial direction, random magnitude (20–80px range) | |
| var mag = 20 + rand(i * 7) * 60; | |
| d.el.style.transform = 'translate(' + (d.ux * mag) + 'px, ' + (d.uy * mag) + 'px)'; | |
| d.el.style.transitionDelay = (rand(i * 5) * 0.15) + 's'; | |
| }); | |
| }); | |
| el.addEventListener('mouseleave', function() { | |
| rd.forEach(function(d) { | |
| d.el.style.transform = 'translate(0, 0)'; | |
| d.el.style.transitionDelay = '0s'; | |
| }); | |
| }); | |
| })();</script> | |
| <div class="card-info"> | |
| <div class="card-name">Keys — cube grid</div> | |
| <div class="card-desc">A grid of small cubes like keyboard keys that scatter apart on hover.</div> | |
| <span class="card-anim">scatter</span> | |
| </div> | |
| </div> | |
| <div class="card"> | |
| <svg id="shape-keys-—-stepped-platform" viewBox="0 0 400 400" width="400" height="400" class="w-full max-w-[400px] h-auto overflow-visible" style="cursor: pointer;"> | |
| <g class="shape-group" style="transform-origin: 50% 50%;"> | |
| <polygon data-face="0" points="189.37,151.13 226.21,143.67 226.21,178.77 189.37,186.24" fill="#8a5e14" stroke="#b67e1c" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="1" points="173.79,133.47 210.63,126.00 226.21,143.67 189.37,151.13" fill="#6b480c" stroke="#b67e1c" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="2" points="173.79,168.57 173.79,133.47 189.37,151.13 189.37,186.24" fill="#8a5e14" stroke="#b67e1c" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="3" points="193.40,173.70 227.50,163.67 227.50,198.77 193.40,208.80" fill="#a87218" stroke="#d49422" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="4" points="172.50,157.35 206.60,147.32 227.50,163.67 193.40,173.70" fill="#8a5e14" stroke="#d49422" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="5" points="172.50,192.45 172.50,157.35 193.40,173.70 193.40,208.80" fill="#a87218" stroke="#d49422" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="6" points="210.63,126.00 210.63,161.11 226.21,178.77 226.21,143.67" fill="#6b480c" stroke="#b67e1c" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="7" points="210.63,161.11 173.79,168.57 189.37,186.24 226.21,178.77" fill="#7a5210" stroke="#b67e1c" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="8" points="197.59,195.96 228.18,183.60 228.18,218.71 197.59,231.06" fill="#c48a1e" stroke="#f0b400" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="9" points="171.82,216.40 171.82,181.29 197.59,195.96 197.59,231.06" fill="#c48a1e" stroke="#f0b400" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="10" points="171.82,181.29 202.41,168.94 228.18,183.60 197.59,195.96" fill="#a87218" stroke="#f0b400" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="11" points="173.79,168.57 210.63,161.11 210.63,126.00 173.79,133.47" fill="#7a5210" stroke="#b67e1c" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="12" points="206.60,147.32 206.60,182.43 227.50,198.77 227.50,163.67" fill="#8a5e14" stroke="#d49422" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="13" points="206.60,182.43 172.50,192.45 193.40,208.80 227.50,198.77" fill="#986816" stroke="#d49422" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="14" points="171.77,240.37 171.77,205.26 201.83,217.92 201.83,253.02" fill="#e4a024" stroke="#ffc206" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="15" points="201.83,217.92 228.23,203.51 228.23,238.62 201.83,253.02" fill="#e4a024" stroke="#ffc206" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="16" points="171.77,205.26 198.17,190.86 228.23,203.51 201.83,217.92" fill="#c48a1e" stroke="#ffc206" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="17" points="172.50,192.45 206.60,182.43 206.60,147.32 172.50,157.35" fill="#986816" stroke="#d49422" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="18" points="202.41,204.04 171.82,216.40 197.59,231.06 228.18,218.71" fill="#b67e1c" stroke="#f0b400" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="19" points="202.41,168.94 202.41,204.04 228.18,218.71 228.18,183.60" fill="#a87218" stroke="#f0b400" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="20" points="171.82,216.40 202.41,204.04 202.41,168.94 171.82,181.29" fill="#b67e1c" stroke="#f0b400" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="21" points="198.17,225.96 171.77,240.37 201.83,253.02 228.23,238.62" fill="#d49422" stroke="#ffc206" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="22" points="171.77,240.37 198.17,225.96 198.17,190.86 171.77,205.26" fill="#d49422" stroke="#ffc206" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="23" points="198.17,190.86 198.17,225.96 228.23,238.62 228.23,203.51" fill="#c48a1e" stroke="#ffc206" stroke-width="1.2" stroke-linejoin="round"/> | |
| </g> | |
| </svg> | |
| <script> | |
| function radialData(el, size) { | |
| const faces = el.querySelectorAll('[data-face]'); | |
| const data = []; | |
| faces.forEach(function(f) { | |
| var pts = f.getAttribute('points').split(' ').map(function(p) { return p.split(',').map(Number); }); | |
| var cx = pts.reduce(function(s, p) { return s + p[0]; }, 0) / pts.length; | |
| var cy = pts.reduce(function(s, p) { return s + p[1]; }, 0) / pts.length; | |
| var rx = cx - size / 2; | |
| var ry = cy - size / 2; | |
| var dist = Math.sqrt(rx * rx + ry * ry) || 0.001; | |
| var angle = Math.atan2(ry, rx); | |
| // Unit radial direction | |
| var ux = rx / dist; | |
| var uy = ry / dist; | |
| f.style.transformOrigin = cx + 'px ' + cy + 'px'; | |
| data.push({ el: f, cx: cx, cy: cy, rx: rx, ry: ry, dist: dist, angle: angle, ux: ux, uy: uy }); | |
| }); | |
| return data; | |
| } | |
| (function() { | |
| var el = document.getElementById('shape-keys-—-stepped-platform'); | |
| var rd = radialData(el, 400); | |
| rd.forEach(function(d) { | |
| d.el.style.transition = 'transform 0.7s cubic-bezier(0.34, 1.56, 0.64, 1)'; | |
| }); | |
| el.addEventListener('mouseenter', function() { | |
| rd.forEach(function(d, i) { | |
| var mag = 20 + d.dist * 0.4; | |
| d.el.style.transform = 'translate(' + (d.ux * mag) + 'px, ' + (d.uy * mag) + 'px) scale(0.92)'; | |
| d.el.style.transitionDelay = (i * 0.02) + 's'; | |
| }); | |
| }); | |
| el.addEventListener('mouseleave', function() { | |
| rd.forEach(function(d, i) { | |
| d.el.style.transform = 'translate(0, 0) scale(1)'; | |
| d.el.style.transitionDelay = (i * 0.01) + 's'; | |
| }); | |
| }); | |
| })();</script> | |
| <div class="card-info"> | |
| <div class="card-name">Keys — stepped platform</div> | |
| <div class="card-desc">Stacked twisted cubes like keyboard tiers that bloom outward.</div> | |
| <span class="card-anim">bloom</span> | |
| </div> | |
| </div> | |
| <div class="card"> | |
| <svg id="shape-keys-—-dual-pane" viewBox="0 0 400 400" width="400" height="400" class="w-full max-w-[400px] h-auto overflow-visible" style="cursor: pointer;"> | |
| <g class="shape-group" style="transform-origin: 50% 50%;"> | |
| <polygon data-face="0" points="166.57,218.88 159.18,220.38 159.18,227.42 166.57,225.92" fill="#7a5210" stroke="#b67e1c" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="1" points="159.18,220.38 166.57,218.88 163.44,215.33 156.05,216.83" fill="#d49422" stroke="#ffc206" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="2" points="159.18,220.38 156.05,216.83 156.05,223.87 159.18,227.42" fill="#986816" stroke="#d49422" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="3" points="163.44,215.33 166.57,218.88 166.57,225.92 163.44,222.38" fill="#b67e1c" stroke="#f0b400" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="4" points="156.05,223.87 163.44,222.38 166.57,225.92 159.18,227.42" fill="#f0b400" stroke="#ffd23f" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="5" points="156.05,216.83 163.44,215.33 163.44,222.38 156.05,223.87" fill="#5c3d0a" stroke="#986816" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="6" points="193.16,212.76 182.05,215.02 182.05,225.61 193.16,223.35" fill="#7a5210" stroke="#b67e1c" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="7" points="182.05,215.02 193.16,212.76 188.46,207.44 177.35,209.69" fill="#d49422" stroke="#ffc206" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="8" points="182.05,215.02 177.35,209.69 177.35,220.28 182.05,225.61" fill="#986816" stroke="#d49422" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="9" points="188.46,207.44 193.16,212.76 193.16,223.35 188.46,218.03" fill="#b67e1c" stroke="#f0b400" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="10" points="177.35,220.28 188.46,218.03 193.16,223.35 182.05,225.61" fill="#f0b400" stroke="#ffd23f" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="11" points="177.35,209.69 188.46,207.44 188.46,218.03 177.35,220.28" fill="#5c3d0a" stroke="#986816" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="12" points="161.00,205.23 147.20,208.03 147.20,221.18 161.00,218.38" fill="#7a5210" stroke="#b67e1c" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="13" points="147.20,208.03 161.00,205.23 155.17,198.61 141.37,201.41" fill="#d49422" stroke="#ffc206" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="14" points="147.20,208.03 141.37,201.41 141.37,214.56 147.20,221.18" fill="#986816" stroke="#d49422" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="15" points="155.17,198.61 161.00,205.23 161.00,218.38 155.17,211.76" fill="#b67e1c" stroke="#f0b400" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="16" points="240.25,203.94 232.86,205.44 232.86,212.48 240.25,210.98" fill="#7a5210" stroke="#b67e1c" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="17" points="141.37,214.56 155.17,211.76 161.00,218.38 147.20,221.18" fill="#f0b400" stroke="#ffd23f" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="18" points="232.86,205.44 240.25,203.94 237.13,200.40 229.74,201.90" fill="#d49422" stroke="#ffc206" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="19" points="232.86,205.44 229.74,201.90 229.74,208.94 232.86,212.48" fill="#986816" stroke="#d49422" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="20" points="181.01,202.25 172.75,203.92 172.75,211.80 181.01,210.12" fill="#7a5210" stroke="#b67e1c" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="21" points="141.37,201.41 155.17,198.61 155.17,211.76 141.37,214.56" fill="#5c3d0a" stroke="#986816" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="22" points="172.75,203.92 181.01,202.25 177.52,198.28 169.25,199.96" fill="#d49422" stroke="#ffc206" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="23" points="172.75,203.92 169.25,199.96 169.25,207.83 172.75,211.80" fill="#986816" stroke="#d49422" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="24" points="237.13,200.40 240.25,203.94 240.25,210.98 237.13,207.44" fill="#b67e1c" stroke="#f0b400" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="25" points="229.74,208.94 237.13,207.44 240.25,210.98 232.86,212.48" fill="#f0b400" stroke="#ffd23f" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="26" points="229.74,201.90 237.13,200.40 237.13,207.44 229.74,208.94" fill="#5c3d0a" stroke="#986816" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="27" points="266.85,197.83 255.73,200.08 255.73,210.67 266.85,208.42" fill="#7a5210" stroke="#b67e1c" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="28" points="177.52,198.28 181.01,202.25 181.01,210.12 177.52,206.16" fill="#b67e1c" stroke="#f0b400" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="29" points="169.25,207.83 177.52,206.16 181.01,210.12 172.75,211.80" fill="#f0b400" stroke="#ffd23f" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="30" points="255.73,200.08 266.85,197.83 262.15,192.50 251.03,194.75" fill="#d49422" stroke="#ffc206" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="31" points="169.25,199.96 177.52,198.28 177.52,206.16 169.25,207.83" fill="#5c3d0a" stroke="#986816" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="32" points="255.73,200.08 251.03,194.75 251.03,205.34 255.73,210.67" fill="#986816" stroke="#d49422" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="33" points="148.92,194.68 137.87,196.92 137.87,207.45 148.92,205.21" fill="#7a5210" stroke="#b67e1c" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="34" points="262.15,192.50 266.85,197.83 266.85,208.42 262.15,203.09" fill="#b67e1c" stroke="#f0b400" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="35" points="251.03,205.34 262.15,203.09 266.85,208.42 255.73,210.67" fill="#f0b400" stroke="#ffd23f" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="36" points="137.87,196.92 148.92,194.68 144.25,189.38 133.20,191.62" fill="#d49422" stroke="#ffc206" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="37" points="137.87,196.92 133.20,191.62 133.20,202.15 137.87,207.45" fill="#986816" stroke="#d49422" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="38" points="251.03,194.75 262.15,192.50 262.15,203.09 251.03,205.34" fill="#5c3d0a" stroke="#986816" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="39" points="234.69,190.29 220.89,193.09 220.89,206.24 234.69,203.44" fill="#7a5210" stroke="#b67e1c" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="40" points="144.25,189.38 148.92,194.68 148.92,205.21 144.25,199.91" fill="#b67e1c" stroke="#f0b400" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="41" points="175.47,188.59 160.76,191.57 160.76,205.58 175.47,202.60" fill="#7a5210" stroke="#b67e1c" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="42" points="220.89,193.09 234.69,190.29 228.85,183.68 215.05,186.48" fill="#d49422" stroke="#ffc206" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="43" points="133.20,202.15 144.25,199.91 148.92,205.21 137.87,207.45" fill="#f0b400" stroke="#ffd23f" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="44" points="220.89,193.09 215.05,186.48 215.05,199.62 220.89,206.24" fill="#986816" stroke="#d49422" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="45" points="133.20,191.62 144.25,189.38 144.25,199.91 133.20,202.15" fill="#5c3d0a" stroke="#986816" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="46" points="160.76,191.57 175.47,188.59 169.25,181.54 154.54,184.52" fill="#d49422" stroke="#ffc206" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="47" points="160.76,191.57 154.54,184.52 154.54,198.53 160.76,205.58" fill="#986816" stroke="#d49422" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="48" points="228.85,183.68 234.69,190.29 234.69,203.44 228.85,196.83" fill="#b67e1c" stroke="#f0b400" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="49" points="215.05,199.62 228.85,196.83 234.69,203.44 220.89,206.24" fill="#f0b400" stroke="#ffd23f" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="50" points="254.70,187.31 246.43,188.99 246.43,196.86 254.70,195.19" fill="#7a5210" stroke="#b67e1c" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="51" points="169.25,181.54 175.47,188.59 175.47,202.60 169.25,195.55" fill="#b67e1c" stroke="#f0b400" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="52" points="215.05,186.48 228.85,183.68 228.85,196.83 215.05,199.62" fill="#5c3d0a" stroke="#986816" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="53" points="154.54,198.53 169.25,195.55 175.47,202.60 160.76,205.58" fill="#f0b400" stroke="#ffd23f" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="54" points="246.43,188.99 254.70,187.31 251.20,183.35 242.94,185.02" fill="#d49422" stroke="#ffc206" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="55" points="246.43,188.99 242.94,185.02 242.94,192.90 246.43,196.86" fill="#986816" stroke="#d49422" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="56" points="154.54,184.52 169.25,181.54 169.25,195.55 154.54,198.53" fill="#5c3d0a" stroke="#986816" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="57" points="251.20,183.35 254.70,187.31 254.70,195.19 251.20,191.22" fill="#b67e1c" stroke="#f0b400" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="58" points="242.94,192.90 251.20,191.22 254.70,195.19 246.43,196.86" fill="#f0b400" stroke="#ffd23f" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="59" points="242.94,185.02 251.20,183.35 251.20,191.22 242.94,192.90" fill="#5c3d0a" stroke="#986816" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="60" points="222.61,179.74 211.55,181.98 211.55,192.51 222.61,190.27" fill="#7a5210" stroke="#b67e1c" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="61" points="211.55,181.98 222.61,179.74 217.93,174.44 206.88,176.68" fill="#d49422" stroke="#ffc206" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="62" points="211.55,181.98 206.88,176.68 206.88,187.21 211.55,192.51" fill="#986816" stroke="#d49422" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="63" points="217.93,174.44 222.61,179.74 222.61,190.27 217.93,184.97" fill="#b67e1c" stroke="#f0b400" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="64" points="249.15,173.65 234.45,176.63 234.45,190.65 249.15,187.66" fill="#7a5210" stroke="#b67e1c" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="65" points="206.88,187.21 217.93,184.97 222.61,190.27 211.55,192.51" fill="#f0b400" stroke="#ffd23f" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="66" points="206.88,176.68 217.93,174.44 217.93,184.97 206.88,187.21" fill="#5c3d0a" stroke="#986816" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="67" points="234.45,176.63 249.15,173.65 242.94,166.60 228.23,169.58" fill="#d49422" stroke="#ffc206" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="68" points="234.45,176.63 228.23,169.58 228.23,183.60 234.45,190.65" fill="#986816" stroke="#d49422" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="69" points="242.94,166.60 249.15,173.65 249.15,187.66 242.94,180.61" fill="#b67e1c" stroke="#f0b400" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="70" points="228.23,183.60 242.94,180.61 249.15,187.66 234.45,190.65" fill="#f0b400" stroke="#ffd23f" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="71" points="228.23,169.58 242.94,166.60 242.94,180.61 228.23,183.60" fill="#5c3d0a" stroke="#986816" stroke-width="1.5" stroke-linejoin="round"/> | |
| </g> | |
| </svg> | |
| <script> | |
| function radialData(el, size) { | |
| const faces = el.querySelectorAll('[data-face]'); | |
| const data = []; | |
| faces.forEach(function(f) { | |
| var pts = f.getAttribute('points').split(' ').map(function(p) { return p.split(',').map(Number); }); | |
| var cx = pts.reduce(function(s, p) { return s + p[0]; }, 0) / pts.length; | |
| var cy = pts.reduce(function(s, p) { return s + p[1]; }, 0) / pts.length; | |
| var rx = cx - size / 2; | |
| var ry = cy - size / 2; | |
| var dist = Math.sqrt(rx * rx + ry * ry) || 0.001; | |
| var angle = Math.atan2(ry, rx); | |
| // Unit radial direction | |
| var ux = rx / dist; | |
| var uy = ry / dist; | |
| f.style.transformOrigin = cx + 'px ' + cy + 'px'; | |
| data.push({ el: f, cx: cx, cy: cy, rx: rx, ry: ry, dist: dist, angle: angle, ux: ux, uy: uy }); | |
| }); | |
| return data; | |
| } | |
| (function() { | |
| var el = document.getElementById('shape-keys-—-dual-pane'); | |
| var rd = radialData(el, 400); | |
| var g = el.querySelector('.shape-group'); | |
| var t = 0; | |
| rd.forEach(function(d) { | |
| d.el.style.transition = 'transform 0.6s cubic-bezier(0.34, 1.56, 0.64, 1)'; | |
| }); | |
| (function idleTick() { | |
| t += 0.15; | |
| if (!el.matches(':hover')) { | |
| g.style.transform = 'rotate(' + (Math.sin(t * 0.02) * 2) + 'deg)'; | |
| } | |
| requestAnimationFrame(idleTick); | |
| })(); | |
| el.addEventListener('mouseenter', function() { | |
| rd.forEach(function(d) { | |
| // Strictly radial: magnitude = base + proportional to distance from center | |
| var mag = 30 + d.dist * 0.4; | |
| d.el.style.transform = 'translate(' + (d.ux * mag) + 'px, ' + (d.uy * mag) + 'px)'; | |
| }); | |
| }); | |
| el.addEventListener('mouseleave', function() { | |
| rd.forEach(function(d) { d.el.style.transform = 'translate(0, 0)'; }); | |
| }); | |
| })();</script> | |
| <div class="card-info"> | |
| <div class="card-name">Keys — dual pane</div> | |
| <div class="card-desc">Two side-by-side cube clusters (dual pane!) that explode apart on hover.</div> | |
| <span class="card-anim">explode</span> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="feature-group"> | |
| <div class="feature-header"> | |
| <h2 class="feature-title">Smart search</h2> | |
| <p class="feature-desc">Find files by describing them: "that PDF contract from last month."</p> | |
| </div> | |
| <div class="variant-grid"> | |
| <div class="card"> | |
| <svg id="shape-search-—-radar-rings" viewBox="0 0 400 400" width="400" height="400" class="w-full max-w-[400px] h-auto overflow-visible" style="cursor: pointer;"> | |
| <g class="shape-group" style="transform-origin: 50% 50%;"> | |
| <polygon data-face="0" points="200.24,210.74 187.41,209.56 186.09,216.44 200.27,217.74" fill="#b67e1c" stroke="#d49422" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="1" points="213.05,209.47 200.24,210.74 200.27,217.74 214.41,216.34" fill="#b67e1c" stroke="#d49422" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="2" points="200.27,217.74 186.09,216.44 187.41,220.20 200.24,221.38" fill="#986816" stroke="#d49422" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="3" points="200.19,207.38 190.04,206.45 187.41,209.56 200.24,210.74" fill="#a87218" stroke="#d49422" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="4" points="214.41,216.34 200.27,217.74 200.24,221.38 213.05,220.11" fill="#986816" stroke="#d49422" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="5" points="210.32,206.38 200.19,207.38 200.24,210.74 213.05,209.47" fill="#a87218" stroke="#d49422" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="6" points="187.41,209.56 176.49,206.12 174.03,212.63 186.09,216.44" fill="#c48a1e" stroke="#d49422" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="7" points="223.86,205.95 213.05,209.47 214.41,216.34 226.35,212.45" fill="#a87218" stroke="#b67e1c" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="8" points="209.83,204.69 196.89,205.55 196.36,214.04 211.49,213.03" fill="#b67e1c" stroke="#d49422" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="9" points="190.04,206.45 181.40,203.73 176.49,206.12 187.41,209.56" fill="#b67e1c" stroke="#d49422" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="10" points="218.87,203.60 210.32,206.38 213.05,209.47 223.86,205.95" fill="#986816" stroke="#b67e1c" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="11" points="186.09,216.44 174.03,212.63 176.49,216.76 187.41,220.20" fill="#a87218" stroke="#d49422" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="12" points="226.35,212.45 214.41,216.34 213.05,220.11 223.86,216.59" fill="#8a5e14" stroke="#b67e1c" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="13" points="196.89,205.55 184.78,203.20 182.21,211.28 196.36,214.04" fill="#b67e1c" stroke="#d49422" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="14" points="200.24,221.38 187.41,220.20 190.04,217.09 200.19,218.02" fill="#a87218" stroke="#d49422" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="15" points="213.05,220.11 200.24,221.38 200.19,218.02 210.32,217.02" fill="#a87218" stroke="#d49422" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="16" points="200.17,211.03 191.35,210.22 190.04,206.45 200.19,207.38" fill="#986816" stroke="#d49422" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="17" points="208.96,210.16 200.17,211.03 200.19,207.38 210.32,206.38" fill="#986816" stroke="#d49422" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="18" points="211.49,213.03 196.36,214.04 196.89,218.47 209.83,217.61" fill="#986816" stroke="#d49422" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="19" points="206.52,200.94 197.93,201.51 196.89,205.55 209.83,204.69" fill="#a87218" stroke="#d49422" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="20" points="220.14,200.84 209.83,204.69 211.49,213.03 223.53,208.53" fill="#a87218" stroke="#b67e1c" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="21" points="197.93,201.51 189.90,199.95 184.78,203.20 196.89,205.55" fill="#a87218" stroke="#d49422" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="22" points="196.36,214.04 182.21,211.28 184.78,216.12 196.89,218.47" fill="#986816" stroke="#d49422" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="23" points="200.19,218.02 190.04,217.09 191.35,210.22 200.17,211.03" fill="#b67e1c" stroke="#d49422" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="24" points="210.32,217.02 200.19,218.02 200.17,211.03 208.96,210.16" fill="#b67e1c" stroke="#d49422" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="25" points="191.35,210.22 183.86,207.85 181.40,203.73 190.04,206.45" fill="#a87218" stroke="#d49422" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="26" points="216.38,207.74 208.96,210.16 210.32,206.38 218.87,203.60" fill="#8a5e14" stroke="#b67e1c" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="27" points="187.41,220.20 176.49,216.76 181.40,214.37 190.04,217.09" fill="#b67e1c" stroke="#d49422" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="28" points="176.49,206.12 169.14,200.93 165.92,206.91 174.03,212.63" fill="#c48a1e" stroke="#f0b400" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="29" points="223.86,216.59 213.05,220.11 210.32,217.02 218.87,214.24" fill="#986816" stroke="#b67e1c" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="30" points="231.04,200.72 223.86,205.95 226.35,212.45 234.29,206.67" fill="#a87218" stroke="#b67e1c" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="31" points="206.62,199.91 193.61,199.95 191.73,209.77 208.57,209.71" fill="#b67e1c" stroke="#d49422" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="32" points="213.36,198.38 206.52,200.94 209.83,204.69 220.14,200.84" fill="#986816" stroke="#b67e1c" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="33" points="181.40,203.73 175.59,199.63 169.14,200.93 176.49,206.12" fill="#b67e1c" stroke="#f0b400" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="34" points="224.56,199.46 218.87,203.60 223.86,205.95 231.04,200.72" fill="#986816" stroke="#b67e1c" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="35" points="223.53,208.53 211.49,213.03 209.83,217.61 220.14,213.76" fill="#8a5e14" stroke="#b67e1c" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="36" points="190.04,217.09 181.40,214.37 183.86,207.85 191.35,210.22" fill="#c48a1e" stroke="#d49422" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="37" points="218.87,214.24 210.32,217.02 208.96,210.16 216.38,207.74" fill="#a87218" stroke="#b67e1c" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="38" points="184.78,203.20 176.74,198.25 172.83,205.51 182.21,211.28" fill="#c48a1e" stroke="#f0b400" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="39" points="174.03,212.63 165.92,206.91 169.14,211.57 176.49,216.76" fill="#a87218" stroke="#f0b400" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="40" points="234.29,206.67 226.35,212.45 223.86,216.59 231.04,211.36" fill="#8a5e14" stroke="#b67e1c" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="41" points="189.90,199.95 184.57,196.67 176.74,198.25 184.78,203.20" fill="#b67e1c" stroke="#f0b400" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="42" points="209.83,217.61 196.89,218.47 197.93,214.43 206.52,213.86" fill="#a87218" stroke="#d49422" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="43" points="204.87,205.52 198.46,205.95 197.93,201.51 206.52,200.94" fill="#986816" stroke="#d49422" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="44" points="202.73,195.49 197.37,195.51 193.61,199.95 206.62,199.91" fill="#a87218" stroke="#d49422" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="45" points="208.57,209.71 191.73,209.77 193.61,215.15 206.62,215.11" fill="#986816" stroke="#d49422" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="46" points="183.86,207.85 178.82,204.29 175.59,199.63 181.40,203.73" fill="#a87218" stroke="#f0b400" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="47" points="193.61,199.95 184.34,195.57 179.74,204.11 191.73,209.77" fill="#c48a1e" stroke="#d49422" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="48" points="198.46,205.95 192.46,204.78 189.90,199.95 197.93,201.51" fill="#986816" stroke="#d49422" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="49" points="215.75,195.46 206.62,199.91 208.57,209.71 220.39,203.97" fill="#a87218" stroke="#b67e1c" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="50" points="221.31,204.15 216.38,207.74 218.87,203.60 224.56,199.46" fill="#8a5e14" stroke="#b67e1c" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="51" points="196.89,218.47 184.78,216.12 189.90,212.87 197.93,214.43" fill="#a87218" stroke="#d49422" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="52" points="182.21,211.28 172.83,205.51 176.74,211.17 184.78,216.12" fill="#a87218" stroke="#f0b400" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="53" points="176.49,216.76 169.14,211.57 175.59,210.27 181.40,214.37" fill="#b67e1c" stroke="#f0b400" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="54" points="209.97,203.61 204.87,205.52 206.52,200.94 213.36,198.38" fill="#8a5e14" stroke="#b67e1c" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="55" points="231.04,211.36 223.86,216.59 218.87,214.24 224.56,210.10" fill="#986816" stroke="#b67e1c" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="56" points="197.37,195.51 193.55,193.71 184.34,195.57 193.61,199.95" fill="#b67e1c" stroke="#d49422" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="57" points="206.49,193.66 202.73,195.49 206.62,199.91 215.75,195.46" fill="#986816" stroke="#b67e1c" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="58" points="206.52,213.86 197.93,214.43 198.46,205.95 204.87,205.52" fill="#b67e1c" stroke="#d49422" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="59" points="220.14,213.76 209.83,217.61 206.52,213.86 213.36,211.30" fill="#986816" stroke="#b67e1c" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="60" points="181.40,214.37 175.59,210.27 178.82,204.29 183.86,207.85" fill="#c48a1e" stroke="#f0b400" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="61" points="225.06,195.03 220.14,200.84 223.53,208.53 229.27,201.74" fill="#986816" stroke="#b67e1c" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="62" points="224.56,210.10 218.87,214.24 216.38,207.74 221.31,204.15" fill="#a87218" stroke="#b67e1c" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="63" points="216.62,194.53 213.36,198.38 220.14,200.84 225.06,195.03" fill="#8a5e14" stroke="#b67e1c" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="64" points="197.93,214.43 189.90,212.87 192.46,204.78 198.46,205.95" fill="#b67e1c" stroke="#d49422" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="65" points="192.46,204.78 188.49,202.33 184.57,196.67 189.90,199.95" fill="#a87218" stroke="#f0b400" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="66" points="191.73,209.77 179.74,204.11 184.34,210.77 193.61,215.15" fill="#a87218" stroke="#d49422" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="67" points="220.39,203.97 208.57,209.71 206.62,215.11 215.75,210.66" fill="#8a5e14" stroke="#b67e1c" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="68" points="175.59,199.63 173.50,194.77 166.50,194.80 169.14,200.93" fill="#c48a1e" stroke="#f0b400" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="69" points="213.36,211.30 206.52,213.86 204.87,205.52 209.97,203.61" fill="#a87218" stroke="#b67e1c" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="70" points="226.50,194.59 224.56,199.46 231.04,200.72 233.50,194.56" fill="#8a5e14" stroke="#b67e1c" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="71" points="169.14,200.93 166.50,194.80 163.00,200.13 165.92,206.91" fill="#d49422" stroke="#f0b400" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="72" points="184.78,216.12 176.74,211.17 184.57,209.59 189.90,212.87" fill="#b67e1c" stroke="#f0b400" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="73" points="233.50,194.56 231.04,200.72 234.29,206.67 237.00,199.87" fill="#986816" stroke="#b67e1c" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="74" points="229.27,201.74 223.53,208.53 220.14,213.76 225.06,207.95" fill="#7a5210" stroke="#b67e1c" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="75" points="212.40,200.74 209.97,203.61 213.36,198.38 216.62,194.53" fill="#7a5210" stroke="#b67e1c" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="76" points="184.57,196.67 183.38,192.55 174.94,192.05 176.74,198.25" fill="#c48a1e" stroke="#f0b400" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="77" points="189.90,212.87 184.57,209.59 188.49,202.33 192.46,204.78" fill="#c48a1e" stroke="#f0b400" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="78" points="178.82,204.29 177.00,200.08 173.50,194.77 175.59,199.63" fill="#b67e1c" stroke="#f0b400" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="79" points="200.78,200.88 199.25,200.89 197.37,195.51 202.73,195.49" fill="#986816" stroke="#d49422" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="80" points="206.62,215.11 193.61,215.15 197.37,210.71 202.73,210.69" fill="#a87218" stroke="#d49422" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="81" points="223.00,199.92 221.31,204.15 224.56,199.46 226.50,194.59" fill="#7a5210" stroke="#b67e1c" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="82" points="176.74,198.25 174.94,192.05 170.73,198.26 172.83,205.51" fill="#d49422" stroke="#f0b400" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="83" points="165.92,206.91 163.00,200.13 166.50,205.44 169.14,211.57" fill="#b67e1c" stroke="#f0b400" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="84" points="199.25,200.89 198.16,200.37 193.55,193.71 197.37,195.51" fill="#a87218" stroke="#d49422" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="85" points="201.85,200.36 200.78,200.88 202.73,195.49 206.49,193.66" fill="#8a5e14" stroke="#b67e1c" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="86" points="237.00,199.87 234.29,206.67 231.04,211.36 233.50,205.20" fill="#7a5210" stroke="#b67e1c" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="87" points="193.55,193.71 193.51,191.14 184.25,189.34 184.34,195.57" fill="#c48a1e" stroke="#f0b400" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="88" points="206.45,191.09 206.49,193.66 215.75,195.46 215.66,189.23" fill="#8a5e14" stroke="#b67e1c" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="89" points="188.49,202.33 187.60,199.26 183.38,192.55 184.57,196.67" fill="#b67e1c" stroke="#f0b400" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="90" points="225.06,207.95 220.14,213.76 213.36,211.30 216.62,207.45" fill="#8a5e14" stroke="#b67e1c" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="91" points="216.62,207.45 213.36,211.30 209.97,203.61 212.40,200.74" fill="#986816" stroke="#b67e1c" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="92" points="175.59,210.27 173.50,205.41 177.00,200.08 178.82,204.29" fill="#d49422" stroke="#f0b400" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="93" points="193.61,215.15 184.34,210.77 193.55,208.91 197.37,210.71" fill="#b67e1c" stroke="#d49422" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="94" points="215.75,210.66 206.62,215.11 202.73,210.69 206.49,208.86" fill="#986816" stroke="#b67e1c" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="95" points="226.50,205.23 224.56,210.10 221.31,204.15 223.00,199.92" fill="#986816" stroke="#b67e1c" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="96" points="169.14,211.57 166.50,205.44 173.50,205.41 175.59,210.27" fill="#c48a1e" stroke="#f0b400" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="97" points="184.34,195.57 184.25,189.34 179.61,196.03 179.74,204.11" fill="#d49422" stroke="#f0b400" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="98" points="198.16,200.37 198.15,199.64 193.51,191.14 193.55,193.71" fill="#b67e1c" stroke="#f0b400" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="99" points="201.84,199.63 201.85,200.36 206.49,193.66 206.45,191.09" fill="#7a5210" stroke="#b67e1c" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="100" points="215.66,189.23 215.75,195.46 220.39,203.97 220.26,195.89" fill="#986816" stroke="#b67e1c" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="101" points="233.50,205.20 231.04,211.36 224.56,210.10 226.50,205.23" fill="#8a5e14" stroke="#b67e1c" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="102" points="202.73,210.69 197.37,210.71 199.25,200.89 200.78,200.88" fill="#b67e1c" stroke="#d49422" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="103" points="172.83,205.51 170.73,198.26 174.94,204.97 176.74,211.17" fill="#b67e1c" stroke="#f0b400" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="104" points="215.43,190.41 216.62,194.53 225.06,195.03 223.26,188.83" fill="#8a5e14" stroke="#b67e1c" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="105" points="197.37,210.71 193.55,208.91 198.16,200.37 199.25,200.89" fill="#c48a1e" stroke="#d49422" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="106" points="206.49,208.86 202.73,210.69 200.78,200.88 201.85,200.36" fill="#a87218" stroke="#b67e1c" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="107" points="211.51,197.67 212.40,200.74 216.62,194.53 215.43,190.41" fill="#7a5210" stroke="#b67e1c" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="108" points="184.57,209.59 183.38,205.47 187.60,199.26 188.49,202.33" fill="#d49422" stroke="#f0b400" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="109" points="198.15,199.64 199.22,199.12 197.27,189.31 193.51,191.14" fill="#c48a1e" stroke="#ffc206" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="110" points="200.75,199.11 201.84,199.63 206.45,191.09 202.63,189.29" fill="#e4a024" stroke="#ffd23f" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="111" points="176.74,211.17 174.94,204.97 183.38,205.47 184.57,209.59" fill="#c48a1e" stroke="#f0b400" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="112" points="223.26,188.83 225.06,195.03 229.27,201.74 227.17,194.49" fill="#986816" stroke="#b67e1c" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="113" points="199.22,199.12 200.75,199.11 202.63,189.29 197.27,189.31" fill="#d49422" stroke="#ffc206" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="114" points="173.50,194.77 175.44,189.90 168.96,188.64 166.50,194.80" fill="#c48a1e" stroke="#f0b400" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="115" points="179.74,204.11 179.61,196.03 184.25,204.54 184.34,210.77" fill="#b67e1c" stroke="#f0b400" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="116" points="193.55,208.91 193.51,206.34 198.15,199.64 198.16,200.37" fill="#d49422" stroke="#f0b400" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="117" points="206.45,206.29 206.49,208.86 201.85,200.36 201.84,199.63" fill="#986816" stroke="#b67e1c" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="118" points="220.26,195.89 220.39,203.97 215.75,210.66 215.66,204.43" fill="#7a5210" stroke="#b67e1c" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="119" points="224.41,189.73 226.50,194.59 233.50,194.56 230.86,188.43" fill="#8a5e14" stroke="#b67e1c" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="120" points="177.00,200.08 178.69,195.85 175.44,189.90 173.50,194.77" fill="#b67e1c" stroke="#f0b400" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="121" points="193.51,191.14 197.27,189.31 193.38,184.89 184.25,189.34" fill="#d49422" stroke="#ffc206" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="122" points="202.63,189.29 206.45,191.09 215.66,189.23 206.39,184.85" fill="#f0b400" stroke="#ffd23f" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="123" points="221.18,195.71 223.00,199.92 226.50,194.59 224.41,189.73" fill="#7a5210" stroke="#b67e1c" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="124" points="187.60,199.26 190.03,196.39 186.64,188.70 183.38,192.55" fill="#b67e1c" stroke="#f0b400" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="125" points="183.38,192.55 186.64,188.70 179.86,186.24 174.94,192.05" fill="#c48a1e" stroke="#f0b400" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="126" points="215.43,203.33 216.62,207.45 212.40,200.74 211.51,197.67" fill="#986816" stroke="#b67e1c" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="127" points="184.34,210.77 184.25,204.54 193.51,206.34 193.55,208.91" fill="#c48a1e" stroke="#f0b400" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="128" points="215.66,204.43 215.75,210.66 206.49,208.86 206.45,206.29" fill="#8a5e14" stroke="#b67e1c" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="129" points="166.50,194.80 168.96,188.64 165.71,193.33 163.00,200.13" fill="#d49422" stroke="#f0b400" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="130" points="193.51,206.34 197.27,204.51 199.22,199.12 198.15,199.64" fill="#e4a024" stroke="#ffc206" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="131" points="202.63,204.49 206.45,206.29 201.84,199.63 200.75,199.11" fill="#ffc206" stroke="#ffd23f" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="132" points="230.86,188.43 233.50,194.56 237.00,199.87 234.08,193.09" fill="#986816" stroke="#b67e1c" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="133" points="227.17,194.49 229.27,201.74 225.06,207.95 223.26,201.75" fill="#7a5210" stroke="#b67e1c" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="134" points="173.50,205.41 175.44,200.54 178.69,195.85 177.00,200.08" fill="#d49422" stroke="#f0b400" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="135" points="197.27,189.31 202.63,189.29 206.39,184.85 193.38,184.89" fill="#e4a024" stroke="#ffc206" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="136" points="197.27,204.51 202.63,204.49 200.75,199.11 199.22,199.12" fill="#f0b400" stroke="#ffc206" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="137" points="224.41,200.37 226.50,205.23 223.00,199.92 221.18,195.71" fill="#986816" stroke="#b67e1c" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="138" points="207.54,195.22 211.51,197.67 215.43,190.41 210.10,187.13" fill="#e4a024" stroke="#ffd23f" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="139" points="223.26,201.75 225.06,207.95 216.62,207.45 215.43,203.33" fill="#8a5e14" stroke="#b67e1c" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="140" points="183.38,205.47 186.64,201.62 190.03,196.39 187.60,199.26" fill="#d49422" stroke="#f0b400" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="141" points="174.94,192.05 179.86,186.24 176.47,191.47 170.73,198.26" fill="#d49422" stroke="#f0b400" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="142" points="163.00,200.13 165.71,193.33 168.96,199.28 166.50,205.44" fill="#b67e1c" stroke="#f0b400" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="143" points="210.10,187.13 215.43,190.41 223.26,188.83 215.22,183.88" fill="#f0b400" stroke="#ffd23f" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="144" points="234.08,193.09 237.00,199.87 233.50,205.20 230.86,199.07" fill="#7a5210" stroke="#b67e1c" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="145" points="166.50,205.44 168.96,199.28 175.44,200.54 173.50,205.41" fill="#c48a1e" stroke="#f0b400" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="146" points="190.03,196.39 195.13,194.48 193.48,186.14 186.64,188.70" fill="#c48a1e" stroke="#ffc206" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="147" points="230.86,199.07 233.50,205.20 226.50,205.23 224.41,200.37" fill="#8a5e14" stroke="#b67e1c" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="148" points="184.25,189.34 193.38,184.89 191.43,190.29 179.61,196.03" fill="#e4a024" stroke="#ffc206" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="149" points="206.39,184.85 215.66,189.23 220.26,195.89 208.27,190.23" fill="#ffc206" stroke="#ffd23f" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="150" points="210.10,200.05 215.43,203.33 211.51,197.67 207.54,195.22" fill="#ffc206" stroke="#ffd23f" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="151" points="201.54,194.05 207.54,195.22 210.10,187.13 202.07,185.57" fill="#d49422" stroke="#ffd23f" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="152" points="174.94,204.97 179.86,199.16 186.64,201.62 183.38,205.47" fill="#c48a1e" stroke="#f0b400" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="153" points="178.69,195.85 183.62,192.26 181.13,185.76 175.44,189.90" fill="#c48a1e" stroke="#ffc206" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="154" points="170.73,198.26 176.47,191.47 179.86,199.16 174.94,204.97" fill="#b67e1c" stroke="#f0b400" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="155" points="216.14,192.15 221.18,195.71 224.41,189.73 218.60,185.63" fill="#e4a024" stroke="#ffd23f" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="156" points="186.64,188.70 193.48,186.14 190.17,182.39 179.86,186.24" fill="#d49422" stroke="#ffc206" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="157" points="195.13,194.48 201.54,194.05 202.07,185.57 193.48,186.14" fill="#d49422" stroke="#ffc206" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="158" points="184.25,204.54 193.38,200.09 197.27,204.51 193.51,206.34" fill="#d49422" stroke="#ffc206" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="159" points="206.39,200.05 215.66,204.43 206.45,206.29 202.63,204.49" fill="#f0b400" stroke="#ffd23f" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="160" points="175.44,189.90 181.13,185.76 176.14,183.41 168.96,188.64" fill="#d49422" stroke="#ffc206" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="161" points="186.64,201.62 193.48,199.06 195.13,194.48 190.03,196.39" fill="#e4a024" stroke="#ffc206" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="162" points="218.60,185.63 224.41,189.73 230.86,188.43 223.51,183.24" fill="#f0b400" stroke="#ffd23f" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="163" points="215.22,183.88 223.26,188.83 227.17,194.49 217.79,188.72" fill="#ffc206" stroke="#ffd23f" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="164" points="202.07,185.57 210.10,187.13 215.22,183.88 203.11,181.53" fill="#e4a024" stroke="#ffd23f" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="165" points="175.44,200.54 181.13,196.40 183.62,192.26 178.69,195.85" fill="#e4a024" stroke="#ffc206" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="166" points="179.61,196.03 191.43,190.29 193.38,200.09 184.25,204.54" fill="#c48a1e" stroke="#ffc206" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="167" points="202.07,198.49 210.10,200.05 207.54,195.22 201.54,194.05" fill="#f0b400" stroke="#ffd23f" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="168" points="208.27,190.23 220.26,195.89 215.66,204.43 206.39,200.05" fill="#e4a024" stroke="#ffd23f" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="169" points="218.60,196.27 224.41,200.37 221.18,195.71 216.14,192.15" fill="#ffc206" stroke="#ffd23f" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="170" points="193.38,184.89 206.39,184.85 208.27,190.23 191.43,190.29" fill="#f0b400" stroke="#ffc206" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="171" points="193.38,200.09 206.39,200.05 202.63,204.49 197.27,204.51" fill="#e4a024" stroke="#ffc206" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="172" points="193.48,199.06 202.07,198.49 201.54,194.05 195.13,194.48" fill="#f0b400" stroke="#ffc206" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="173" points="193.48,186.14 202.07,185.57 203.11,181.53 190.17,182.39" fill="#e4a024" stroke="#ffc206" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="174" points="215.22,196.80 223.26,201.75 215.43,203.33 210.10,200.05" fill="#f0b400" stroke="#ffd23f" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="175" points="168.96,188.64 176.14,183.41 173.65,187.55 165.71,193.33" fill="#e4a024" stroke="#ffc206" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="176" points="223.51,183.24 230.86,188.43 234.08,193.09 225.97,187.37" fill="#ffc206" stroke="#ffd23f" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="177" points="217.79,188.72 227.17,194.49 223.26,201.75 215.22,196.80" fill="#e4a024" stroke="#ffd23f" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="178" points="183.62,192.26 191.04,189.84 189.68,182.98 181.13,185.76" fill="#c48a1e" stroke="#ffc206" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="179" points="208.65,189.78 216.14,192.15 218.60,185.63 209.96,182.91" fill="#e4a024" stroke="#ffd23f" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="180" points="179.86,186.24 190.17,182.39 188.51,186.97 176.47,191.47" fill="#e4a024" stroke="#ffc206" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="181" points="168.96,199.28 176.14,194.05 181.13,196.40 175.44,200.54" fill="#d49422" stroke="#ffc206" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="182" points="223.51,193.88 230.86,199.07 224.41,200.37 218.60,196.27" fill="#f0b400" stroke="#ffd23f" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="183" points="179.86,199.16 190.17,195.31 193.48,199.06 186.64,201.62" fill="#d49422" stroke="#ffc206" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="184" points="191.43,190.29 208.27,190.23 206.39,200.05 193.38,200.09" fill="#d49422" stroke="#ffc206" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="185" points="165.71,193.33 173.65,187.55 176.14,194.05 168.96,199.28" fill="#c48a1e" stroke="#ffc206" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="186" points="181.13,185.76 189.68,182.98 186.95,179.89 176.14,183.41" fill="#d49422" stroke="#ffc206" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="187" points="225.97,187.37 234.08,193.09 230.86,199.07 223.51,193.88" fill="#e4a024" stroke="#ffd23f" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="188" points="209.96,182.91 218.60,185.63 223.51,183.24 212.59,179.80" fill="#f0b400" stroke="#ffd23f" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="189" points="181.13,196.40 189.68,193.62 191.04,189.84 183.62,192.26" fill="#e4a024" stroke="#ffc206" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="190" points="209.96,193.55 218.60,196.27 216.14,192.15 208.65,189.78" fill="#ffc206" stroke="#ffd23f" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="191" points="191.04,189.84 199.83,188.97 199.81,181.98 189.68,182.98" fill="#d49422" stroke="#ffc206" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="192" points="199.83,188.97 208.65,189.78 209.96,182.91 199.81,181.98" fill="#d49422" stroke="#ffd23f" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="193" points="203.11,181.53 215.22,183.88 217.79,188.72 203.64,185.96" fill="#f0b400" stroke="#ffd23f" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="194" points="203.11,194.45 215.22,196.80 210.10,200.05 202.07,198.49" fill="#e4a024" stroke="#ffd23f" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="195" points="176.47,191.47 188.51,186.97 190.17,195.31 179.86,199.16" fill="#c48a1e" stroke="#ffc206" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="196" points="190.17,195.31 203.11,194.45 202.07,198.49 193.48,199.06" fill="#e4a024" stroke="#ffc206" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="197" points="190.17,182.39 203.11,181.53 203.64,185.96 188.51,186.97" fill="#f0b400" stroke="#ffc206" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="198" points="189.68,193.62 199.81,192.62 199.83,188.97 191.04,189.84" fill="#f0b400" stroke="#ffc206" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="199" points="199.81,192.62 209.96,193.55 208.65,189.78 199.83,188.97" fill="#f0b400" stroke="#ffd23f" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="200" points="189.68,182.98 199.81,181.98 199.76,178.62 186.95,179.89" fill="#e4a024" stroke="#ffc206" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="201" points="199.81,181.98 209.96,182.91 212.59,179.80 199.76,178.62" fill="#e4a024" stroke="#ffd23f" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="202" points="203.64,185.96 217.79,188.72 215.22,196.80 203.11,194.45" fill="#d49422" stroke="#ffd23f" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="203" points="176.14,183.41 186.95,179.89 185.59,183.66 173.65,187.55" fill="#e4a024" stroke="#ffc206" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="204" points="212.59,179.80 223.51,183.24 225.97,187.37 213.91,183.56" fill="#ffc206" stroke="#ffd23f" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="205" points="176.14,194.05 186.95,190.53 189.68,193.62 181.13,196.40" fill="#d49422" stroke="#ffc206" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="206" points="212.59,190.44 223.51,193.88 218.60,196.27 209.96,193.55" fill="#f0b400" stroke="#ffd23f" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="207" points="188.51,186.97 203.64,185.96 203.11,194.45 190.17,195.31" fill="#d49422" stroke="#ffc206" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="208" points="173.65,187.55 185.59,183.66 186.95,190.53 176.14,194.05" fill="#c48a1e" stroke="#ffc206" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="209" points="213.91,183.56 225.97,187.37 223.51,193.88 212.59,190.44" fill="#e4a024" stroke="#ffd23f" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="210" points="186.95,190.53 199.76,189.26 199.81,192.62 189.68,193.62" fill="#e4a024" stroke="#ffc206" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="211" points="186.95,179.89 199.76,178.62 199.73,182.26 185.59,183.66" fill="#f0b400" stroke="#ffc206" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="212" points="199.76,189.26 212.59,190.44 209.96,193.55 199.81,192.62" fill="#e4a024" stroke="#ffd23f" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="213" points="199.76,178.62 212.59,179.80 213.91,183.56 199.73,182.26" fill="#f0b400" stroke="#ffd23f" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="214" points="185.59,183.66 199.73,182.26 199.76,189.26 186.95,190.53" fill="#d49422" stroke="#ffc206" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="215" points="199.73,182.26 213.91,183.56 212.59,190.44 199.76,189.26" fill="#d49422" stroke="#ffd23f" stroke-width="1" stroke-linejoin="round"/> | |
| </g> | |
| </svg> | |
| <script> | |
| function radialData(el, size) { | |
| const faces = el.querySelectorAll('[data-face]'); | |
| const data = []; | |
| faces.forEach(function(f) { | |
| var pts = f.getAttribute('points').split(' ').map(function(p) { return p.split(',').map(Number); }); | |
| var cx = pts.reduce(function(s, p) { return s + p[0]; }, 0) / pts.length; | |
| var cy = pts.reduce(function(s, p) { return s + p[1]; }, 0) / pts.length; | |
| var rx = cx - size / 2; | |
| var ry = cy - size / 2; | |
| var dist = Math.sqrt(rx * rx + ry * ry) || 0.001; | |
| var angle = Math.atan2(ry, rx); | |
| // Unit radial direction | |
| var ux = rx / dist; | |
| var uy = ry / dist; | |
| f.style.transformOrigin = cx + 'px ' + cy + 'px'; | |
| data.push({ el: f, cx: cx, cy: cy, rx: rx, ry: ry, dist: dist, angle: angle, ux: ux, uy: uy }); | |
| }); | |
| return data; | |
| } | |
| (function() { | |
| var el = document.getElementById('shape-search-—-radar-rings'); | |
| var rd = radialData(el, 400); | |
| var t = 0; | |
| (function tick() { | |
| t += 0.02; | |
| var hovering = el.matches(':hover'); | |
| rd.forEach(function(d, i) { | |
| var phase = d.dist * 0.02 + i * 0.1; | |
| var wave = Math.sin(t * 0.3333333333333333 + phase); | |
| if (hovering) { | |
| // Radial push/pull | |
| var mag = wave * 6; | |
| d.el.style.transition = 'transform 0.3s ease'; | |
| d.el.style.transform = 'translate(' + (d.ux * mag) + 'px, ' + (d.uy * mag) + 'px)'; | |
| } else { | |
| // Gentle scale breathing | |
| d.el.style.transition = 'none'; | |
| d.el.style.transform = 'scale(' + (1 + wave * 0.02) + ')'; | |
| } | |
| }); | |
| requestAnimationFrame(tick); | |
| })(); | |
| })();</script> | |
| <div class="card-info"> | |
| <div class="card-name">Search — radar rings</div> | |
| <div class="card-desc">Concentric rings that pulse like search radar, breathing in and out.</div> | |
| <span class="card-anim">breathe</span> | |
| </div> | |
| </div> | |
| <div class="card"> | |
| <svg id="shape-search-—-octahedron-focus" viewBox="0 0 400 400" width="400" height="400" class="w-full max-w-[400px] h-auto overflow-visible" style="cursor: pointer;"> | |
| <g class="shape-group" style="transform-origin: 50% 50%;"> | |
| <polygon data-face="0" points="200.00,125.41 233.10,237.53 121.71,215.87" fill="#c48a1e" stroke="#ffd23f" stroke-width="1.8" stroke-linejoin="round"/> | |
| <polygon data-face="1" points="200.00,125.41 278.29,184.13 233.10,237.53" fill="#b67e1c" stroke="#ffc206" stroke-width="1.8" stroke-linejoin="round"/> | |
| <polygon data-face="2" points="200.00,274.59 121.71,215.87 233.10,237.53" fill="#8a5e14" stroke="#d49422" stroke-width="1.8" stroke-linejoin="round"/> | |
| <polygon data-face="3" points="200.00,125.41 121.71,215.87 166.90,162.47" fill="#d49422" stroke="#ffe066" stroke-width="1.8" stroke-linejoin="round"/> | |
| <polygon data-face="4" points="200.00,274.59 233.10,237.53 278.29,184.13" fill="#7a5210" stroke="#b67e1c" stroke-width="1.8" stroke-linejoin="round"/> | |
| <polygon data-face="5" points="200.00,125.41 166.90,162.47 278.29,184.13" fill="#e4a024" stroke="#ffeb99" stroke-width="1.8" stroke-linejoin="round"/> | |
| <polygon data-face="6" points="200.00,274.59 166.90,162.47 121.71,215.87" fill="#986816" stroke="#f0b400" stroke-width="1.8" stroke-linejoin="round"/> | |
| <polygon data-face="7" points="200.00,274.59 278.29,184.13 166.90,162.47" fill="#a87218" stroke="#ffc206" stroke-width="1.8" stroke-linejoin="round"/> | |
| </g> | |
| </svg> | |
| <script> | |
| function radialData(el, size) { | |
| const faces = el.querySelectorAll('[data-face]'); | |
| const data = []; | |
| faces.forEach(function(f) { | |
| var pts = f.getAttribute('points').split(' ').map(function(p) { return p.split(',').map(Number); }); | |
| var cx = pts.reduce(function(s, p) { return s + p[0]; }, 0) / pts.length; | |
| var cy = pts.reduce(function(s, p) { return s + p[1]; }, 0) / pts.length; | |
| var rx = cx - size / 2; | |
| var ry = cy - size / 2; | |
| var dist = Math.sqrt(rx * rx + ry * ry) || 0.001; | |
| var angle = Math.atan2(ry, rx); | |
| // Unit radial direction | |
| var ux = rx / dist; | |
| var uy = ry / dist; | |
| f.style.transformOrigin = cx + 'px ' + cy + 'px'; | |
| data.push({ el: f, cx: cx, cy: cy, rx: rx, ry: ry, dist: dist, angle: angle, ux: ux, uy: uy }); | |
| }); | |
| return data; | |
| } | |
| (function() { | |
| var el = document.getElementById('shape-search-—-octahedron-focus'); | |
| var rd = radialData(el, 400); | |
| rd.forEach(function(d) { | |
| d.el.style.transition = 'transform 0.7s cubic-bezier(0.34, 1.56, 0.64, 1)'; | |
| }); | |
| el.addEventListener('mouseenter', function() { | |
| rd.forEach(function(d, i) { | |
| var mag = 20 + d.dist * 0.4; | |
| d.el.style.transform = 'translate(' + (d.ux * mag) + 'px, ' + (d.uy * mag) + 'px) scale(0.92)'; | |
| d.el.style.transitionDelay = (i * 0.02) + 's'; | |
| }); | |
| }); | |
| el.addEventListener('mouseleave', function() { | |
| rd.forEach(function(d, i) { | |
| d.el.style.transform = 'translate(0, 0) scale(1)'; | |
| d.el.style.transitionDelay = (i * 0.01) + 's'; | |
| }); | |
| }); | |
| })();</script> | |
| <div class="card-info"> | |
| <div class="card-name">Search — octahedron focus</div> | |
| <div class="card-desc">An octahedron that blooms open, revealing its inner structure — like finding a result.</div> | |
| <span class="card-anim">bloom</span> | |
| </div> | |
| </div> | |
| <div class="card"> | |
| <svg id="shape-search-—-lens-sphere" viewBox="0 0 400 400" width="400" height="400" class="w-full max-w-[400px] h-auto overflow-visible" style="cursor: pointer;"> | |
| <g class="shape-group" style="transform-origin: 50% 50%;"> | |
| <polygon data-face="0" points="161.26,148.13 226.50,193.14 153.70,231.28" fill="#e4a024" stroke="#ffd23f" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="1" points="226.50,266.96 153.70,231.28 226.50,193.14" fill="#986816" stroke="#d49422" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="2" points="238.74,132.43 226.50,193.14 161.26,148.13" fill="#b67e1c" stroke="#ffc206" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="3" points="226.50,193.14 279.06,205.87 226.50,266.96" fill="#d49422" stroke="#ffd23f" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="4" points="238.74,132.43 279.06,205.87 226.50,193.14" fill="#b67e1c" stroke="#ffc206" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="5" points="153.70,231.28 120.94,194.13 161.26,148.13" fill="#986816" stroke="#f0b400" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="6" points="161.26,267.57 153.70,231.28 226.50,266.96" fill="#6b480c" stroke="#b67e1c" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="7" points="238.74,132.43 161.26,148.13 173.50,133.04" fill="#c48a1e" stroke="#ffc206" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="8" points="161.26,267.57 120.94,194.13 153.70,231.28" fill="#8a5e14" stroke="#d49422" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="9" points="173.50,133.04 161.26,148.13 120.94,194.13" fill="#e4a024" stroke="#ffe066" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="10" points="238.74,251.87 226.50,266.96 279.06,205.87" fill="#8a5e14" stroke="#d49422" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="11" points="238.74,132.43 246.30,168.72 279.06,205.87" fill="#d49422" stroke="#ffd23f" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="12" points="161.26,267.57 226.50,266.96 238.74,251.87" fill="#6b480c" stroke="#b67e1c" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="13" points="238.74,132.43 173.50,133.04 246.30,168.72" fill="#c48a1e" stroke="#ffc206" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="14" points="279.06,205.87 246.30,168.72 238.74,251.87" fill="#f0b400" stroke="#ffe066" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="15" points="161.26,267.57 173.50,206.86 120.94,194.13" fill="#7a5210" stroke="#b67e1c" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="16" points="120.94,194.13 173.50,206.86 173.50,133.04" fill="#a87218" stroke="#f0b400" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="17" points="161.26,267.57 238.74,251.87 173.50,206.86" fill="#7a5210" stroke="#b67e1c" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="18" points="246.30,168.72 173.50,133.04 173.50,206.86" fill="#f0b400" stroke="#ffe066" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="19" points="173.50,206.86 238.74,251.87 246.30,168.72" fill="#a87218" stroke="#f0b400" stroke-width="1.5" stroke-linejoin="round"/> | |
| </g> | |
| </svg> | |
| <script> | |
| (function() { | |
| var el = document.getElementById('shape-search-—-lens-sphere'); | |
| var g = el.querySelector('.shape-group'); | |
| g.style.transformOrigin = '50% 50%'; | |
| var t = 0; | |
| el.addEventListener('mouseenter', function() { | |
| g.style.transition = 'transform 0.6s cubic-bezier(0.34, 1.56, 0.64, 1)'; | |
| g.style.transform = 'scale(1.15)'; | |
| }); | |
| el.addEventListener('mouseleave', function() { | |
| g.style.transition = 'transform 0.6s cubic-bezier(0.34, 1.56, 0.64, 1)'; | |
| g.style.transform = 'scale(1)'; | |
| }); | |
| (function idle() { | |
| t += 0.02; | |
| if (!el.matches(':hover')) { | |
| var s = 1 + Math.sin(t * 0.25) * 0.03; | |
| g.style.transition = 'none'; | |
| g.style.transform = 'scale(' + s + ')'; | |
| } | |
| requestAnimationFrame(idle); | |
| })(); | |
| })();</script> | |
| <div class="card-info"> | |
| <div class="card-name">Search — lens sphere</div> | |
| <div class="card-desc">Icosahedron faces that pulse gently like a scanning lens.</div> | |
| <span class="card-anim">pulse</span> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="feature-group"> | |
| <div class="feature-header"> | |
| <h2 class="feature-title">Natural language rename</h2> | |
| <p class="feature-desc">"Make these lowercase and add date prefix" — no regex, no scripts, just words.</p> | |
| </div> | |
| <div class="variant-grid"> | |
| <div class="card"> | |
| <svg id="shape-rename-—-dual-arrows" viewBox="0 0 400 400" width="400" height="400" class="w-full max-w-[400px] h-auto overflow-visible" style="cursor: pointer;"> | |
| <g class="shape-group" style="transform-origin: 50% 50%;"> | |
| <polygon data-face="0" points="132.84,188.95 171.52,146.71 182.43,159.07 143.74,201.31" fill="#f0b400" stroke="#ffe066" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="1" points="157.98,192.45 132.84,188.95 143.74,201.31 168.89,204.82" fill="#d49422" stroke="#ffd23f" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="2" points="143.74,201.31 168.89,204.82 168.89,247.82 195.96,242.33 195.96,199.33 221.11,185.63 182.43,159.07" fill="#5c3d0a" stroke="#986816" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="3" points="171.52,146.71 210.21,173.27 221.11,185.63 182.43,159.07" fill="#4e3608" stroke="#986816" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="4" points="242.02,164.54 214.94,170.03 204.04,157.67 231.11,152.18" fill="#986816" stroke="#f0b400" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="5" points="157.98,235.46 157.98,192.45 168.89,204.82 168.89,247.82" fill="#b67e1c" stroke="#ffc206" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="6" points="210.21,173.27 185.06,186.97 195.96,199.33 221.11,185.63" fill="#5c3d0a" stroke="#b67e1c" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="7" points="214.94,170.03 214.94,213.03 204.04,200.67 204.04,157.67" fill="#b67e1c" stroke="#ffc206" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="8" points="171.52,146.71 210.21,173.27 185.06,186.97 185.06,229.97 157.98,235.46 157.98,192.45 132.84,188.95" fill="#f0b400" stroke="#ffc206" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="9" points="228.48,253.29 267.16,211.05 242.02,207.55 242.02,164.54 214.94,170.03 214.94,213.03 189.79,226.73" fill="#f0b400" stroke="#ffc206" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="10" points="185.06,186.97 185.06,229.97 195.96,242.33 195.96,199.33" fill="#7a5210" stroke="#d49422" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="11" points="214.94,213.03 189.79,226.73 178.89,214.37 204.04,200.67" fill="#d49422" stroke="#ffd23f" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="12" points="242.02,207.55 242.02,164.54 231.11,152.18 231.11,195.18" fill="#7a5210" stroke="#d49422" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="13" points="185.06,229.97 157.98,235.46 168.89,247.82 195.96,242.33" fill="#986816" stroke="#f0b400" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="14" points="189.79,226.73 228.48,253.29 217.57,240.93 178.89,214.37" fill="#f0b400" stroke="#ffe066" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="15" points="178.89,214.37 204.04,200.67 204.04,157.67 231.11,152.18 231.11,195.18 256.26,198.69 217.57,240.93" fill="#5c3d0a" stroke="#986816" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="16" points="267.16,211.05 242.02,207.55 231.11,195.18 256.26,198.69" fill="#5c3d0a" stroke="#b67e1c" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="17" points="228.48,253.29 267.16,211.05 256.26,198.69 217.57,240.93" fill="#4e3608" stroke="#986816" stroke-width="1.5" stroke-linejoin="round"/> | |
| </g> | |
| </svg> | |
| <script> | |
| (function() { | |
| var el = document.getElementById('shape-rename-—-dual-arrows'); | |
| var g = el.querySelector('.shape-group'); | |
| g.style.transformOrigin = '50% 50%'; | |
| var t = 0; | |
| el.addEventListener('mouseenter', function() { | |
| g.style.transition = 'transform 0.8s cubic-bezier(0.68, -0.55, 0.27, 1.55)'; | |
| g.style.transform = 'perspective(600px) rotateY(180deg) scale(0.9)'; | |
| }); | |
| el.addEventListener('mouseleave', function() { | |
| g.style.transition = 'transform 0.8s cubic-bezier(0.68, -0.55, 0.27, 1.55)'; | |
| g.style.transform = 'perspective(600px) rotateY(0deg) scale(1)'; | |
| }); | |
| (function idle() { | |
| t += 0.015; | |
| if (!el.matches(':hover')) { | |
| g.style.transition = 'none'; | |
| g.style.transform = 'perspective(600px) rotateY(' + (Math.sin(t) * 3) + 'deg)'; | |
| } | |
| requestAnimationFrame(idle); | |
| })(); | |
| })();</script> | |
| <div class="card-info"> | |
| <div class="card-name">Rename — dual arrows</div> | |
| <div class="card-desc">Two opposing arrows — input transforms to output. Flips on hover.</div> | |
| <span class="card-anim">flip</span> | |
| </div> | |
| </div> | |
| <div class="card"> | |
| <svg id="shape-rename-—-crystal-morph" viewBox="0 0 400 400" width="400" height="400" class="w-full max-w-[400px] h-auto overflow-visible" style="cursor: pointer;"> | |
| <g class="shape-group" style="transform-origin: 50% 50%;"> | |
| <polygon data-face="0" points="186.80,165.96 201.71,201.22 185.75,202.62" fill="#d49422" stroke="#ffc206" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="1" points="186.80,165.96 185.75,202.62 171.67,201.45" fill="#d49422" stroke="#ffc206" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="2" points="185.09,172.80 202.24,205.50 181.90,211.81" fill="#f0b400" stroke="#ffe066" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="3" points="188.45,234.13 185.75,202.62 201.71,201.22" fill="#8a5e14" stroke="#b67e1c" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="4" points="185.09,172.80 181.90,211.81 163.98,201.81" fill="#f0b400" stroke="#ffe066" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="5" points="188.45,234.13 171.67,201.45 185.75,202.62" fill="#8a5e14" stroke="#b67e1c" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="6" points="186.80,165.96 203.58,198.64 201.71,201.22" fill="#d49422" stroke="#ffc206" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="7" points="202.47,166.02 219.10,202.49 198.24,208.41" fill="#e4a024" stroke="#ffd23f" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="8" points="202.47,166.02 198.24,208.41 180.04,198.03" fill="#e4a024" stroke="#ffd23f" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="9" points="185.09,172.80 204.68,189.20 202.24,205.50" fill="#f0b400" stroke="#ffe066" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="10" points="186.80,165.96 171.67,201.45 173.55,198.88" fill="#d49422" stroke="#ffc206" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="11" points="188.45,234.13 201.71,201.22 203.58,198.64" fill="#8a5e14" stroke="#b67e1c" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="12" points="185.09,172.80 163.98,201.81 166.41,185.50" fill="#f0b400" stroke="#ffe066" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="13" points="188.45,234.13 173.55,198.88 171.67,201.45" fill="#8a5e14" stroke="#b67e1c" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="14" points="200.70,174.15 215.16,195.33 197.08,194.50" fill="#c48a1e" stroke="#ffc206" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="15" points="202.47,166.02 221.75,186.18 219.10,202.49" fill="#e4a024" stroke="#ffd23f" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="16" points="200.70,174.15 197.08,194.50 180.39,190.56" fill="#c48a1e" stroke="#ffc206" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="17" points="202.47,166.02 180.04,198.03 182.69,181.72" fill="#e4a024" stroke="#ffd23f" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="18" points="186.80,165.96 189.50,197.47 203.58,198.64" fill="#d49422" stroke="#ffc206" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="19" points="196.23,208.63 197.08,194.50 215.16,195.33" fill="#7a5210" stroke="#b67e1c" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="20" points="196.23,208.63 180.39,190.56 197.08,194.50" fill="#7a5210" stroke="#b67e1c" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="21" points="185.09,172.80 186.76,179.19 204.68,189.20" fill="#f0b400" stroke="#ffe066" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="22" points="186.80,165.96 173.55,198.88 189.50,197.47" fill="#d49422" stroke="#ffc206" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="23" points="185.09,172.80 166.41,185.50 186.76,179.19" fill="#f0b400" stroke="#ffe066" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="24" points="183.57,218.20 181.90,211.81 202.24,205.50" fill="#a87218" stroke="#f0b400" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="25" points="183.57,218.20 163.98,201.81 181.90,211.81" fill="#a87218" stroke="#f0b400" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="26" points="188.45,234.13 203.58,198.64 189.50,197.47" fill="#8a5e14" stroke="#b67e1c" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="27" points="188.45,234.13 189.50,197.47 173.55,198.88" fill="#8a5e14" stroke="#b67e1c" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="28" points="202.47,166.02 203.55,175.80 221.75,186.18" fill="#e4a024" stroke="#ffd23f" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="29" points="202.47,166.02 182.69,181.72 203.55,175.80" fill="#e4a024" stroke="#ffd23f" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="30" points="200.70,174.15 216.55,192.22 215.16,195.33" fill="#c48a1e" stroke="#ffc206" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="31" points="200.70,174.15 180.39,190.56 181.77,187.44" fill="#c48a1e" stroke="#ffc206" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="32" points="199.32,218.19 198.24,208.41 219.10,202.49" fill="#986816" stroke="#d49422" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="33" points="183.57,218.20 202.24,205.50 204.68,189.20" fill="#a87218" stroke="#f0b400" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="34" points="196.23,208.63 215.16,195.33 216.55,192.22" fill="#7a5210" stroke="#b67e1c" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="35" points="199.32,218.19 180.04,198.03 198.24,208.41" fill="#986816" stroke="#d49422" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="36" points="196.23,208.63 181.77,187.44 180.39,190.56" fill="#7a5210" stroke="#b67e1c" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="37" points="183.57,218.20 166.41,185.50 163.98,201.81" fill="#a87218" stroke="#f0b400" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="38" points="199.32,218.19 219.10,202.49 221.75,186.18" fill="#986816" stroke="#d49422" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="39" points="183.57,218.20 204.68,189.20 186.76,179.19" fill="#a87218" stroke="#f0b400" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="40" points="199.32,218.19 182.69,181.72 180.04,198.03" fill="#986816" stroke="#d49422" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="41" points="183.57,218.20 186.76,179.19 166.41,185.50" fill="#a87218" stroke="#f0b400" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="42" points="200.70,174.15 199.85,188.28 216.55,192.22" fill="#c48a1e" stroke="#ffc206" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="43" points="200.70,174.15 181.77,187.44 199.85,188.28" fill="#c48a1e" stroke="#ffc206" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="44" points="196.23,208.63 216.55,192.22 199.85,188.28" fill="#7a5210" stroke="#b67e1c" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="45" points="196.23,208.63 199.85,188.28 181.77,187.44" fill="#7a5210" stroke="#b67e1c" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="46" points="199.32,218.19 221.75,186.18 203.55,175.80" fill="#986816" stroke="#d49422" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="47" points="199.32,218.19 203.55,175.80 182.69,181.72" fill="#986816" stroke="#d49422" stroke-width="1.2" stroke-linejoin="round"/> | |
| </g> | |
| </svg> | |
| <script> | |
| (function() { | |
| var el = document.getElementById('shape-rename-—-crystal-morph'); | |
| var g = el.querySelector('.shape-group'); | |
| g.style.transformOrigin = '50% 0%'; | |
| var t = 0, hovering = false; | |
| el.addEventListener('mouseenter', function() { hovering = true; }); | |
| el.addEventListener('mouseleave', function() { hovering = false; }); | |
| (function tick() { | |
| t += 0.04; | |
| if (hovering) { | |
| g.style.transition = 'none'; | |
| g.style.transform = 'rotate(' + (Math.sin(t * 3) * 12 * Math.exp(-(t * 0.3) % 1)) + 'deg)'; | |
| } else { | |
| g.style.transition = 'transform 0.5s ease'; | |
| g.style.transform = 'rotate(' + (Math.sin(t * 0.1) * 3) + 'deg)'; | |
| } | |
| requestAnimationFrame(tick); | |
| })(); | |
| })();</script> | |
| <div class="card-info"> | |
| <div class="card-name">Rename — crystal morph</div> | |
| <div class="card-desc">A crystal cluster that swings — raw text morphing into structured names.</div> | |
| <span class="card-anim">swing</span> | |
| </div> | |
| </div> | |
| <div class="card"> | |
| <svg id="shape-rename-—-star-transform" viewBox="0 0 400 400" width="400" height="400" class="w-full max-w-[400px] h-auto overflow-visible" style="cursor: pointer;"> | |
| <g class="shape-group" style="transform-origin: 50% 50%;"> | |
| <polygon data-face="0" points="233.10,222.18 195.32,202.72 195.32,233.44 233.10,252.89" fill="#a87218" stroke="#f0b400" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="1" points="195.32,202.72 148.75,217.15 148.75,247.87 195.32,233.44" fill="#b67e1c" stroke="#ffc206" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="2" points="230.32,195.63 233.10,222.18 233.10,252.89 230.32,226.34" fill="#986816" stroke="#d49422" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="3" points="148.75,217.15 165.00,191.74 165.00,222.45 148.75,247.87" fill="#c48a1e" stroke="#ffc206" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="4" points="284.35,189.67 230.32,195.63 230.32,226.34 284.35,220.38" fill="#8a5e14" stroke="#d49422" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="5" points="166.90,147.11 204.68,166.56 251.25,152.13 235.00,177.55 284.35,189.67 230.32,195.63 233.10,222.18 195.32,202.72 148.75,217.15 165.00,191.74 115.65,179.62 169.68,173.66" fill="#f0b400" stroke="#ffc206" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="6" points="165.00,191.74 115.65,179.62 115.65,210.33 165.00,222.45" fill="#d49422" stroke="#ffd23f" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="7" points="235.00,177.55 284.35,189.67 284.35,220.38 235.00,208.26" fill="#7a5210" stroke="#b67e1c" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="8" points="169.68,204.37 115.65,210.33 165.00,222.45 148.75,247.87 195.32,233.44 233.10,252.89 230.32,226.34 284.35,220.38 235.00,208.26 251.25,182.85 204.68,197.28 166.90,177.82" fill="#5c3d0a" stroke="#986816" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="9" points="115.65,179.62 169.68,173.66 169.68,204.37 115.65,210.33" fill="#e4a024" stroke="#ffd23f" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="10" points="251.25,152.13 235.00,177.55 235.00,208.26 251.25,182.85" fill="#6b480c" stroke="#b67e1c" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="11" points="169.68,173.66 166.90,147.11 166.90,177.82 169.68,204.37" fill="#f0b400" stroke="#ffe066" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="12" points="204.68,166.56 251.25,152.13 251.25,182.85 204.68,197.28" fill="#5c3d0a" stroke="#986816" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="13" points="166.90,147.11 204.68,166.56 204.68,197.28 166.90,177.82" fill="#4e3608" stroke="#986816" stroke-width="1.5" stroke-linejoin="round"/> | |
| </g> | |
| </svg> | |
| <script> | |
| function radialData(el, size) { | |
| const faces = el.querySelectorAll('[data-face]'); | |
| const data = []; | |
| faces.forEach(function(f) { | |
| var pts = f.getAttribute('points').split(' ').map(function(p) { return p.split(',').map(Number); }); | |
| var cx = pts.reduce(function(s, p) { return s + p[0]; }, 0) / pts.length; | |
| var cy = pts.reduce(function(s, p) { return s + p[1]; }, 0) / pts.length; | |
| var rx = cx - size / 2; | |
| var ry = cy - size / 2; | |
| var dist = Math.sqrt(rx * rx + ry * ry) || 0.001; | |
| var angle = Math.atan2(ry, rx); | |
| // Unit radial direction | |
| var ux = rx / dist; | |
| var uy = ry / dist; | |
| f.style.transformOrigin = cx + 'px ' + cy + 'px'; | |
| data.push({ el: f, cx: cx, cy: cy, rx: rx, ry: ry, dist: dist, angle: angle, ux: ux, uy: uy }); | |
| }); | |
| return data; | |
| } | |
| (function() { | |
| var el = document.getElementById('shape-rename-—-star-transform'); | |
| var rd = radialData(el, 400); | |
| rd.forEach(function(d) { | |
| d.el.style.transition = 'transform 0.6s cubic-bezier(0.34, 1.56, 0.64, 1)'; | |
| }); | |
| el.addEventListener('mouseenter', function() { | |
| rd.forEach(function(d, i) { | |
| var mag = 25 + d.dist * 0.5; | |
| d.el.style.transform = 'translate(' + (d.ux * mag) + 'px, ' + (d.uy * mag) + 'px)'; | |
| // Staggered delay creates the spiral wave feel | |
| d.el.style.transitionDelay = (i * 0.03) + 's'; | |
| }); | |
| }); | |
| el.addEventListener('mouseleave', function() { | |
| rd.forEach(function(d, i) { | |
| d.el.style.transform = 'translate(0, 0)'; | |
| d.el.style.transitionDelay = (i * 0.015) + 's'; | |
| }); | |
| }); | |
| })();</script> | |
| <div class="card-info"> | |
| <div class="card-name">Rename — star transform</div> | |
| <div class="card-desc">A star prism that spirals outward — transformation in motion.</div> | |
| <span class="card-anim">spiral</span> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="feature-group"> | |
| <div class="feature-header"> | |
| <h2 class="feature-title">AI batch operations</h2> | |
| <p class="feature-desc">Organize hundreds of files with a single command: "Sort these into folders by project."</p> | |
| </div> | |
| <div class="variant-grid"> | |
| <div class="card"> | |
| <svg id="shape-batch-—-scattered-cubes" viewBox="0 0 400 400" width="400" height="400" class="w-full max-w-[400px] h-auto overflow-visible" style="cursor: pointer;"> | |
| <g class="shape-group" style="transform-origin: 50% 50%;"> | |
| <polygon data-face="0" points="191.31,211.58 171.83,215.52 171.83,234.08 191.31,230.14" fill="#7a5210" stroke="#b67e1c" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="1" points="171.83,215.52 191.31,211.58 183.07,202.24 163.59,206.19" fill="#d49422" stroke="#ffc206" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="2" points="171.83,215.52 163.59,206.19 163.59,224.74 171.83,234.08" fill="#986816" stroke="#d49422" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="3" points="183.07,202.24 191.31,211.58 191.31,230.14 183.07,220.80" fill="#b67e1c" stroke="#f0b400" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="4" points="163.59,224.74 183.07,220.80 191.31,230.14 171.83,234.08" fill="#f0b400" stroke="#ffd23f" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="5" points="163.59,206.19 183.07,202.24 183.07,220.80 163.59,224.74" fill="#5c3d0a" stroke="#986816" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="6" points="256.83,182.47 234.71,186.95 234.71,208.03 256.83,203.54" fill="#7a5210" stroke="#b67e1c" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="7" points="234.71,186.95 256.83,182.47 247.48,171.86 225.36,176.35" fill="#d49422" stroke="#ffc206" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="8" points="234.71,186.95 225.36,176.35 225.36,197.42 234.71,208.03" fill="#986816" stroke="#d49422" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="9" points="187.43,205.81 174.42,208.45 174.42,220.85 187.43,218.21" fill="#7a5210" stroke="#b67e1c" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="10" points="247.48,171.86 256.83,182.47 256.83,203.54 247.48,192.94" fill="#b67e1c" stroke="#f0b400" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="11" points="174.42,208.45 187.43,205.81 181.93,199.57 168.91,202.21" fill="#d49422" stroke="#ffc206" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="12" points="174.42,208.45 168.91,202.21 168.91,214.61 174.42,220.85" fill="#986816" stroke="#d49422" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="13" points="261.06,183.25 245.36,186.44 245.36,201.39 261.06,198.21" fill="#7a5210" stroke="#b67e1c" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="14" points="225.36,197.42 247.48,192.94 256.83,203.54 234.71,208.03" fill="#f0b400" stroke="#ffd23f" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="15" points="245.36,186.44 261.06,183.25 254.42,175.73 238.73,178.91" fill="#d49422" stroke="#ffc206" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="16" points="245.36,186.44 238.73,178.91 238.73,193.87 245.36,201.39" fill="#986816" stroke="#d49422" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="17" points="225.36,176.35 247.48,171.86 247.48,192.94 225.36,197.42" fill="#5c3d0a" stroke="#986816" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="18" points="181.93,199.57 187.43,205.81 187.43,218.21 181.93,211.97" fill="#b67e1c" stroke="#f0b400" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="19" points="168.91,214.61 181.93,211.97 187.43,218.21 174.42,220.85" fill="#f0b400" stroke="#ffd23f" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="20" points="168.91,202.21 181.93,199.57 181.93,211.97 168.91,214.61" fill="#5c3d0a" stroke="#986816" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="21" points="254.42,175.73 261.06,183.25 261.06,198.21 254.42,190.68" fill="#b67e1c" stroke="#f0b400" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="22" points="238.73,193.87 254.42,190.68 261.06,198.21 245.36,201.39" fill="#f0b400" stroke="#ffd23f" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="23" points="266.63,218.96 253.01,221.73 253.01,234.71 266.63,231.95" fill="#7a5210" stroke="#b67e1c" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="24" points="230.96,204.67 207.65,209.40 207.65,231.61 230.96,226.89" fill="#7a5210" stroke="#b67e1c" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="25" points="238.73,178.91 254.42,175.73 254.42,190.68 238.73,193.87" fill="#5c3d0a" stroke="#986816" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="26" points="253.01,221.73 266.63,218.96 260.87,212.43 247.25,215.19" fill="#d49422" stroke="#ffc206" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="27" points="253.01,221.73 247.25,215.19 247.25,228.18 253.01,234.71" fill="#986816" stroke="#d49422" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="28" points="207.65,209.40 230.96,204.67 221.11,193.49 197.79,198.22" fill="#d49422" stroke="#ffc206" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="29" points="207.65,209.40 197.79,198.22 197.79,220.44 207.65,231.61" fill="#986816" stroke="#d49422" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="30" points="260.87,212.43 266.63,218.96 266.63,231.95 260.87,225.41" fill="#b67e1c" stroke="#f0b400" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="31" points="256.73,204.63 243.27,207.36 243.27,220.18 256.73,217.45" fill="#7a5210" stroke="#b67e1c" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="32" points="247.25,228.18 260.87,225.41 266.63,231.95 253.01,234.71" fill="#f0b400" stroke="#ffd23f" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="33" points="243.27,207.36 256.73,204.63 251.04,198.18 237.58,200.91" fill="#d49422" stroke="#ffc206" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="34" points="247.25,215.19 260.87,212.43 260.87,225.41 247.25,228.18" fill="#5c3d0a" stroke="#986816" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="35" points="243.27,207.36 237.58,200.91 237.58,213.73 243.27,220.18" fill="#986816" stroke="#d49422" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="36" points="221.11,193.49 230.96,204.67 230.96,226.89 221.11,215.71" fill="#b67e1c" stroke="#f0b400" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="37" points="251.04,198.18 256.73,204.63 256.73,217.45 251.04,211.00" fill="#b67e1c" stroke="#f0b400" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="38" points="197.79,220.44 221.11,215.71 230.96,226.89 207.65,231.61" fill="#f0b400" stroke="#ffd23f" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="39" points="237.58,213.73 251.04,211.00 256.73,217.45 243.27,220.18" fill="#f0b400" stroke="#ffd23f" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="40" points="237.58,200.91 251.04,198.18 251.04,211.00 237.58,213.73" fill="#5c3d0a" stroke="#986816" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="41" points="197.79,198.22 221.11,193.49 221.11,215.71 197.79,220.44" fill="#5c3d0a" stroke="#986816" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="42" points="167.24,203.53 154.42,206.13 154.42,218.34 167.24,215.74" fill="#7a5210" stroke="#b67e1c" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="43" points="154.42,206.13 167.24,203.53 161.82,197.39 149.01,199.99" fill="#d49422" stroke="#ffc206" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="44" points="154.42,206.13 149.01,199.99 149.01,212.20 154.42,218.34" fill="#986816" stroke="#d49422" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="45" points="222.56,156.12 209.93,158.68 209.93,170.71 222.56,168.15" fill="#7a5210" stroke="#b67e1c" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="46" points="161.82,197.39 167.24,203.53 167.24,215.74 161.82,209.60" fill="#b67e1c" stroke="#f0b400" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="47" points="149.01,212.20 161.82,209.60 167.24,215.74 154.42,218.34" fill="#f0b400" stroke="#ffd23f" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="48" points="209.93,158.68 222.56,156.12 217.22,150.07 204.60,152.63" fill="#d49422" stroke="#ffc206" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="49" points="209.93,158.68 204.60,152.63 204.60,164.66 209.93,170.71" fill="#986816" stroke="#d49422" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="50" points="149.01,199.99 161.82,197.39 161.82,209.60 149.01,212.20" fill="#5c3d0a" stroke="#986816" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="51" points="217.22,150.07 222.56,156.12 222.56,168.15 217.22,162.10" fill="#b67e1c" stroke="#f0b400" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="52" points="204.60,164.66 217.22,162.10 222.56,168.15 209.93,170.71" fill="#f0b400" stroke="#ffd23f" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="53" points="204.60,152.63 217.22,150.07 217.22,162.10 204.60,164.66" fill="#5c3d0a" stroke="#986816" stroke-width="1.5" stroke-linejoin="round"/> | |
| </g> | |
| </svg> | |
| <script> | |
| function radialData(el, size) { | |
| const faces = el.querySelectorAll('[data-face]'); | |
| const data = []; | |
| faces.forEach(function(f) { | |
| var pts = f.getAttribute('points').split(' ').map(function(p) { return p.split(',').map(Number); }); | |
| var cx = pts.reduce(function(s, p) { return s + p[0]; }, 0) / pts.length; | |
| var cy = pts.reduce(function(s, p) { return s + p[1]; }, 0) / pts.length; | |
| var rx = cx - size / 2; | |
| var ry = cy - size / 2; | |
| var dist = Math.sqrt(rx * rx + ry * ry) || 0.001; | |
| var angle = Math.atan2(ry, rx); | |
| // Unit radial direction | |
| var ux = rx / dist; | |
| var uy = ry / dist; | |
| f.style.transformOrigin = cx + 'px ' + cy + 'px'; | |
| data.push({ el: f, cx: cx, cy: cy, rx: rx, ry: ry, dist: dist, angle: angle, ux: ux, uy: uy }); | |
| }); | |
| return data; | |
| } | |
| (function() { | |
| var el = document.getElementById('shape-batch-—-scattered-cubes'); | |
| var rd = radialData(el, 400); | |
| rd.forEach(function(d) { | |
| var mag = 35 + d.dist * 0.35; | |
| d.el.style.transition = 'transform 0.8s cubic-bezier(0.34, 1.56, 0.64, 1)'; | |
| d.el.style.transform = 'translate(' + (d.ux * mag) + 'px, ' + (d.uy * mag) + 'px)'; | |
| }); | |
| el.addEventListener('mouseenter', function() { | |
| rd.forEach(function(d) { | |
| d.el.style.transform = 'translate(0, 0)'; | |
| }); | |
| }); | |
| el.addEventListener('mouseleave', function() { | |
| rd.forEach(function(d) { | |
| var mag = 35 + d.dist * 0.35; | |
| d.el.style.transform = 'translate(' + (d.ux * mag) + 'px, ' + (d.uy * mag) + 'px)'; | |
| }); | |
| }); | |
| })();</script> | |
| <div class="card-info"> | |
| <div class="card-name">Batch — scattered cubes</div> | |
| <div class="card-desc">Scattered cubes that snap together into formation on hover — chaos to order.</div> | |
| <span class="card-anim">assemble</span> | |
| </div> | |
| </div> | |
| <div class="card"> | |
| <svg id="shape-batch-—-radial-sort" viewBox="0 0 400 400" width="400" height="400" class="w-full max-w-[400px] h-auto overflow-visible" style="cursor: pointer;"> | |
| <g class="shape-group" style="transform-origin: 50% 50%;"> | |
| <polygon data-face="0" points="206.86,225.05 158.70,219.68 158.70,246.01 206.86,251.38" fill="#986816" stroke="#f0b400" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="1" points="252.40,215.82 206.86,225.05 206.86,251.38 252.40,242.15" fill="#986816" stroke="#d49422" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="2" points="200.00,186.84 206.86,225.05 158.70,219.68" fill="#c48a1e" stroke="#ffc206" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="3" points="200.00,186.84 252.40,215.82 206.86,225.05" fill="#c48a1e" stroke="#f0b400" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="4" points="158.70,219.68 126.32,201.77 126.32,228.10 158.70,246.01" fill="#a87218" stroke="#f0b400" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="5" points="200.00,186.84 158.70,219.68 126.32,201.77" fill="#d49422" stroke="#ffc206" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="6" points="200.00,213.16 158.70,246.01 206.86,251.38" fill="#6b480c" stroke="#986816" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="7" points="277.92,195.52 252.40,215.82 252.40,242.15 277.92,221.84" fill="#8a5e14" stroke="#d49422" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="8" points="200.00,213.16 206.86,251.38 252.40,242.15" fill="#5c3d0a" stroke="#986816" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="9" points="200.00,186.84 277.92,195.52 252.40,215.82" fill="#b67e1c" stroke="#f0b400" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="10" points="200.00,213.16 126.32,228.10 158.70,246.01" fill="#6b480c" stroke="#b67e1c" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="11" points="200.00,213.16 252.40,242.15 277.92,221.84" fill="#5c3d0a" stroke="#986816" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="12" points="200.00,186.84 126.32,201.77 122.08,178.16" fill="#e4a024" stroke="#ffd23f" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="13" points="126.32,201.77 122.08,178.16 122.08,204.48 126.32,228.10" fill="#b67e1c" stroke="#ffc206" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="14" points="200.00,186.84 273.68,171.90 277.92,195.52" fill="#b67e1c" stroke="#f0b400" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="15" points="200.00,213.16 122.08,204.48 126.32,228.10" fill="#7a5210" stroke="#b67e1c" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="16" points="273.68,171.90 277.92,195.52 277.92,221.84 273.68,198.23" fill="#8a5e14" stroke="#d49422" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="17" points="200.00,213.16 277.92,221.84 273.68,198.23" fill="#5c3d0a" stroke="#986816" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="18" points="200.00,186.84 122.08,178.16 147.60,157.85" fill="#e4a024" stroke="#ffd23f" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="19" points="200.00,186.84 241.30,153.99 273.68,171.90" fill="#ffc206" stroke="#ffe066" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="20" points="200.00,213.16 147.60,184.18 122.08,204.48" fill="#7a5210" stroke="#b67e1c" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="21" points="200.00,186.84 147.60,157.85 193.14,148.62" fill="#f0b400" stroke="#ffd23f" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="22" points="122.08,178.16 147.60,157.85 147.60,184.18 122.08,204.48" fill="#b67e1c" stroke="#ffc206" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="23" points="200.00,186.84 193.14,148.62 241.30,153.99" fill="#f0b400" stroke="#ffe066" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="24" points="200.00,213.16 273.68,198.23 241.30,180.32" fill="#8a5e14" stroke="#d49422" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="25" points="241.30,153.99 273.68,171.90 273.68,198.23 241.30,180.32" fill="#d49422" stroke="#ffd23f" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="26" points="200.00,213.16 193.14,174.95 147.60,184.18" fill="#7a5210" stroke="#d49422" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="27" points="200.00,213.16 241.30,180.32 193.14,174.95" fill="#8a5e14" stroke="#d49422" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="28" points="147.60,157.85 193.14,148.62 193.14,174.95 147.60,184.18" fill="#c48a1e" stroke="#ffc206" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="29" points="193.14,148.62 241.30,153.99 241.30,180.32 193.14,174.95" fill="#c48a1e" stroke="#ffd23f" stroke-width="1.5" stroke-linejoin="round"/> | |
| </g> | |
| </svg> | |
| <script> | |
| function radialData(el, size) { | |
| const faces = el.querySelectorAll('[data-face]'); | |
| const data = []; | |
| faces.forEach(function(f) { | |
| var pts = f.getAttribute('points').split(' ').map(function(p) { return p.split(',').map(Number); }); | |
| var cx = pts.reduce(function(s, p) { return s + p[0]; }, 0) / pts.length; | |
| var cy = pts.reduce(function(s, p) { return s + p[1]; }, 0) / pts.length; | |
| var rx = cx - size / 2; | |
| var ry = cy - size / 2; | |
| var dist = Math.sqrt(rx * rx + ry * ry) || 0.001; | |
| var angle = Math.atan2(ry, rx); | |
| // Unit radial direction | |
| var ux = rx / dist; | |
| var uy = ry / dist; | |
| f.style.transformOrigin = cx + 'px ' + cy + 'px'; | |
| data.push({ el: f, cx: cx, cy: cy, rx: rx, ry: ry, dist: dist, angle: angle, ux: ux, uy: uy }); | |
| }); | |
| return data; | |
| } | |
| (function() { | |
| var el = document.getElementById('shape-batch-—-radial-sort'); | |
| var rd = radialData(el, 400); | |
| rd.forEach(function(d) { | |
| d.el.style.transition = 'transform 0.7s cubic-bezier(0.34, 1.56, 0.64, 1)'; | |
| }); | |
| el.addEventListener('mouseenter', function() { | |
| rd.forEach(function(d, i) { | |
| var mag = 20 + d.dist * 0.4; | |
| d.el.style.transform = 'translate(' + (d.ux * mag) + 'px, ' + (d.uy * mag) + 'px) scale(0.92)'; | |
| d.el.style.transitionDelay = (i * 0.02) + 's'; | |
| }); | |
| }); | |
| el.addEventListener('mouseleave', function() { | |
| rd.forEach(function(d, i) { | |
| d.el.style.transform = 'translate(0, 0) scale(1)'; | |
| d.el.style.transitionDelay = (i * 0.01) + 's'; | |
| }); | |
| }); | |
| })();</script> | |
| <div class="card-info"> | |
| <div class="card-name">Batch — radial sort</div> | |
| <div class="card-desc">Triangular wedges blooming outward — files fanning into organized groups.</div> | |
| <span class="card-anim">bloom</span> | |
| </div> | |
| </div> | |
| <div class="card"> | |
| <svg id="shape-batch-—-hex-organize" viewBox="0 0 400 400" width="400" height="400" class="w-full max-w-[400px] h-auto overflow-visible" style="cursor: pointer;"> | |
| <g class="shape-group" style="transform-origin: 50% 50%;"> | |
| <polygon data-face="0" points="271.80,210.66 188.90,227.46 188.90,258.18 271.80,241.37" fill="#6b480c" stroke="#b67e1c" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="1" points="260.09,207.81 184.96,220.28 184.96,248.43 260.09,235.96" fill="#7a5210" stroke="#d49422" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="2" points="188.90,227.46 117.10,201.45 117.10,232.16 188.90,258.18" fill="#8a5e14" stroke="#d49422" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="3" points="249.03,204.34 182.47,213.05 182.47,238.65 249.03,229.93" fill="#8a5e14" stroke="#d49422" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="4" points="184.96,220.28 124.88,195.32 124.88,223.48 184.96,248.43" fill="#986816" stroke="#f0b400" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="5" points="182.47,213.05 133.44,189.78 133.44,215.37 182.47,238.65" fill="#a87218" stroke="#f0b400" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="6" points="266.56,172.34 249.03,204.34 249.03,229.93 266.56,197.94" fill="#7a5210" stroke="#b67e1c" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="7" points="275.12,170.38 260.09,207.81 260.09,235.96 275.12,198.54" fill="#5c3d0a" stroke="#b67e1c" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="8" points="266.56,172.34 249.03,204.34 182.47,213.05 133.44,189.78 150.97,157.78 217.53,149.06" fill="#f0b400" stroke="#ffc206" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="9" points="275.12,170.38 260.09,207.81 184.96,220.28 124.88,195.32 139.91,157.89 215.04,145.42" fill="#f0b400" stroke="#ffc206" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="10" points="282.90,167.84 271.80,210.66 271.80,241.37 282.90,198.55" fill="#4e3608" stroke="#986816" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="11" points="282.90,167.84 271.80,210.66 188.90,227.46 117.10,201.45 128.20,158.63 211.10,141.82" fill="#f0b400" stroke="#ffc206" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="12" points="217.53,174.66 150.97,183.38 133.44,215.37 182.47,238.65 249.03,229.93 266.56,197.94" fill="#5c3d0a" stroke="#986816" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="13" points="215.04,173.58 139.91,186.05 124.88,223.48 184.96,248.43 260.09,235.96 275.12,198.54" fill="#5c3d0a" stroke="#986816" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="14" points="211.10,172.54 128.20,189.34 117.10,232.16 188.90,258.18 271.80,241.37 282.90,198.55" fill="#5c3d0a" stroke="#986816" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="15" points="117.10,201.45 128.20,158.63 128.20,189.34 117.10,232.16" fill="#a87218" stroke="#f0b400" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="16" points="124.88,195.32 139.91,157.89 139.91,186.05 124.88,223.48" fill="#b67e1c" stroke="#ffc206" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="17" points="133.44,189.78 150.97,157.78 150.97,183.38 133.44,215.37" fill="#c48a1e" stroke="#ffc206" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="18" points="217.53,149.06 266.56,172.34 266.56,197.94 217.53,174.66" fill="#f0b400" stroke="#ffe066" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="19" points="215.04,145.42 275.12,170.38 275.12,198.54 215.04,173.58" fill="#f0b400" stroke="#ffe066" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="20" points="150.97,157.78 217.53,149.06 217.53,174.66 150.97,183.38" fill="#d49422" stroke="#ffd23f" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="21" points="211.10,141.82 282.90,167.84 282.90,198.55 211.10,172.54" fill="#e4a024" stroke="#ffd23f" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="22" points="139.91,157.89 215.04,145.42 215.04,173.58 139.91,186.05" fill="#d49422" stroke="#ffd23f" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="23" points="128.20,158.63 211.10,141.82 211.10,172.54 128.20,189.34" fill="#c48a1e" stroke="#ffc206" stroke-width="1.5" stroke-linejoin="round"/> | |
| </g> | |
| </svg> | |
| <script> | |
| function radialData(el, size) { | |
| const faces = el.querySelectorAll('[data-face]'); | |
| const data = []; | |
| faces.forEach(function(f) { | |
| var pts = f.getAttribute('points').split(' ').map(function(p) { return p.split(',').map(Number); }); | |
| var cx = pts.reduce(function(s, p) { return s + p[0]; }, 0) / pts.length; | |
| var cy = pts.reduce(function(s, p) { return s + p[1]; }, 0) / pts.length; | |
| var rx = cx - size / 2; | |
| var ry = cy - size / 2; | |
| var dist = Math.sqrt(rx * rx + ry * ry) || 0.001; | |
| var angle = Math.atan2(ry, rx); | |
| // Unit radial direction | |
| var ux = rx / dist; | |
| var uy = ry / dist; | |
| f.style.transformOrigin = cx + 'px ' + cy + 'px'; | |
| data.push({ el: f, cx: cx, cy: cy, rx: rx, ry: ry, dist: dist, angle: angle, ux: ux, uy: uy }); | |
| }); | |
| return data; | |
| } | |
| (function() { | |
| var el = document.getElementById('shape-batch-—-hex-organize'); | |
| var rd = radialData(el, 400); | |
| var g = el.querySelector('.shape-group'); | |
| var t = 0; | |
| rd.forEach(function(d) { | |
| d.el.style.transition = 'transform 0.6s cubic-bezier(0.34, 1.56, 0.64, 1)'; | |
| }); | |
| (function idleTick() { | |
| t += 0.15; | |
| if (!el.matches(':hover')) { | |
| g.style.transform = 'rotate(' + (Math.sin(t * 0.02) * 2) + 'deg)'; | |
| } | |
| requestAnimationFrame(idleTick); | |
| })(); | |
| el.addEventListener('mouseenter', function() { | |
| rd.forEach(function(d) { | |
| // Strictly radial: magnitude = base + proportional to distance from center | |
| var mag = 30 + d.dist * 0.4; | |
| d.el.style.transform = 'translate(' + (d.ux * mag) + 'px, ' + (d.uy * mag) + 'px)'; | |
| }); | |
| }); | |
| el.addEventListener('mouseleave', function() { | |
| rd.forEach(function(d) { d.el.style.transform = 'translate(0, 0)'; }); | |
| }); | |
| })();</script> | |
| <div class="card-info"> | |
| <div class="card-name">Batch — hex organize</div> | |
| <div class="card-desc">Nested hexagons that explode apart — visualizing batch file processing.</div> | |
| <span class="card-anim">explode</span> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Divider --> | |
| <div class="divider"><span>Abstract gallery</span></div> | |
| <!-- Original gallery shapes --> | |
| <div class="gallery-grid"> | |
| <div class="card"> | |
| <svg id="shape-nested-hexagons" viewBox="0 0 400 400" width="400" height="400" class="w-full max-w-[400px] h-auto overflow-visible" style="cursor: pointer;"> | |
| <g class="shape-group" style="transform-origin: 50% 50%;"> | |
| <polygon data-face="0" points="279.78,206.97 187.67,225.64 187.67,269.52 279.78,250.85" fill="#6b480c" stroke="#b67e1c" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="1" points="266.77,203.23 183.29,217.09 183.29,257.31 266.77,243.45" fill="#7a5210" stroke="#d49422" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="2" points="254.47,198.80 180.52,208.49 180.52,245.06 254.47,235.37" fill="#8a5e14" stroke="#d49422" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="3" points="187.67,225.64 107.89,196.73 107.89,240.61 187.67,269.52" fill="#8a5e14" stroke="#d49422" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="4" points="183.29,217.09 116.53,189.36 116.53,229.58 183.29,257.31" fill="#986816" stroke="#f0b400" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="5" points="180.52,208.49 126.04,182.63 126.04,219.19 180.52,245.06" fill="#a87218" stroke="#f0b400" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="6" points="273.96,163.26 254.47,198.80 254.47,235.37 273.96,199.82" fill="#7a5210" stroke="#b67e1c" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="7" points="283.47,161.64 266.77,203.23 266.77,243.45 283.47,201.87" fill="#5c3d0a" stroke="#b67e1c" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="8" points="273.96,163.26 254.47,198.80 180.52,208.49 126.04,182.63 145.53,147.08 219.48,137.39" fill="#f0b400" stroke="#ffc206" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="9" points="283.47,161.64 266.77,203.23 183.29,217.09 116.53,189.36 133.23,147.77 216.71,133.92" fill="#f0b400" stroke="#ffc206" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="10" points="292.11,159.39 279.78,206.97 187.67,225.64 107.89,196.73 120.22,149.15 212.33,130.48" fill="#f0b400" stroke="#ffc206" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="11" points="292.11,159.39 279.78,206.97 279.78,250.85 292.11,203.27" fill="#4e3608" stroke="#986816" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="12" points="219.48,173.96 145.53,183.64 126.04,219.19 180.52,245.06 254.47,235.37 273.96,199.82" fill="#5c3d0a" stroke="#986816" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="13" points="216.71,174.14 133.23,187.99 116.53,229.58 183.29,257.31 266.77,243.45 283.47,201.87" fill="#5c3d0a" stroke="#986816" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="14" points="107.89,196.73 120.22,149.15 120.22,193.03 107.89,240.61" fill="#a87218" stroke="#f0b400" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="15" points="126.04,182.63 145.53,147.08 145.53,183.64 126.04,219.19" fill="#c48a1e" stroke="#ffc206" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="16" points="116.53,189.36 133.23,147.77 133.23,187.99 116.53,229.58" fill="#b67e1c" stroke="#ffc206" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="17" points="212.33,174.36 120.22,193.03 107.89,240.61 187.67,269.52 279.78,250.85 292.11,203.27" fill="#5c3d0a" stroke="#986816" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="18" points="219.48,137.39 273.96,163.26 273.96,199.82 219.48,173.96" fill="#f0b400" stroke="#ffe066" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="19" points="216.71,133.92 283.47,161.64 283.47,201.87 216.71,174.14" fill="#f0b400" stroke="#ffe066" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="20" points="145.53,147.08 219.48,137.39 219.48,173.96 145.53,183.64" fill="#d49422" stroke="#ffd23f" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="21" points="212.33,130.48 292.11,159.39 292.11,203.27 212.33,174.36" fill="#e4a024" stroke="#ffd23f" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="22" points="133.23,147.77 216.71,133.92 216.71,174.14 133.23,187.99" fill="#d49422" stroke="#ffd23f" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="23" points="120.22,149.15 212.33,130.48 212.33,174.36 120.22,193.03" fill="#c48a1e" stroke="#ffc206" stroke-width="1.5" stroke-linejoin="round"/> | |
| </g> | |
| </svg> | |
| <script> | |
| (function() { | |
| var el = document.getElementById('shape-nested-hexagons'); | |
| var g = el.querySelector('.shape-group'); | |
| g.style.transformOrigin = '50% 50%'; | |
| var t = 0, mouseX = 0, mouseY = 0, hovering = false; | |
| el.addEventListener('mouseenter', function() { hovering = true; }); | |
| el.addEventListener('mouseleave', function() { hovering = false; }); | |
| el.addEventListener('mousemove', function(e) { | |
| var rect = el.getBoundingClientRect(); | |
| mouseX = (e.clientX - rect.left) / rect.width - 0.5; | |
| mouseY = (e.clientY - rect.top) / rect.height - 0.5; | |
| }); | |
| (function tick() { | |
| t += 0.03; | |
| var floatY = Math.sin(t * 0.16666666666666666) * 6; | |
| var floatR = Math.sin(t * 0.11666666666666665) * 2; | |
| if (hovering) { | |
| g.style.transition = 'transform 0.2s ease-out'; | |
| g.style.transform = 'translateY(' + floatY + 'px) perspective(400px) rotateX(' + (mouseY * -15) + 'deg) rotateY(' + (mouseX * 15) + 'deg)'; | |
| } else { | |
| g.style.transition = 'transform 0.6s ease-out'; | |
| g.style.transform = 'translateY(' + floatY + 'px) rotate(' + floatR + 'deg)'; | |
| } | |
| requestAnimationFrame(tick); | |
| })(); | |
| })();</script> | |
| <div class="card-info"> | |
| <div class="card-name">Nested hexagons</div> | |
| <div class="card-desc">Three concentric hexagonal prisms, gently floating. Tilts toward cursor on hover.</div> | |
| <span class="card-anim">float</span> | |
| </div> | |
| </div> | |
| <div class="card"> | |
| <svg id="shape-exploding-cube" viewBox="0 0 400 400" width="400" height="400" class="w-full max-w-[400px] h-auto overflow-visible" style="cursor: pointer;"> | |
| <g class="shape-group" style="transform-origin: 50% 50%;"> | |
| <polygon data-face="0" points="272.08,165.75 170.76,186.29 170.76,282.82 272.08,262.29" fill="#7a5210" stroke="#b67e1c" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="1" points="170.76,186.29 272.08,165.75 229.24,117.18 127.92,137.71" fill="#d49422" stroke="#ffc206" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="2" points="170.76,186.29 127.92,137.71 127.92,234.25 170.76,282.82" fill="#986816" stroke="#d49422" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="3" points="229.24,117.18 272.08,165.75 272.08,262.29 229.24,213.71" fill="#b67e1c" stroke="#f0b400" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="4" points="127.92,234.25 229.24,213.71 272.08,262.29 170.76,282.82" fill="#f0b400" stroke="#ffd23f" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="5" points="127.92,137.71 229.24,117.18 229.24,213.71 127.92,234.25" fill="#5c3d0a" stroke="#986816" stroke-width="1.5" stroke-linejoin="round"/> | |
| </g> | |
| </svg> | |
| <script> | |
| function radialData(el, size) { | |
| const faces = el.querySelectorAll('[data-face]'); | |
| const data = []; | |
| faces.forEach(function(f) { | |
| var pts = f.getAttribute('points').split(' ').map(function(p) { return p.split(',').map(Number); }); | |
| var cx = pts.reduce(function(s, p) { return s + p[0]; }, 0) / pts.length; | |
| var cy = pts.reduce(function(s, p) { return s + p[1]; }, 0) / pts.length; | |
| var rx = cx - size / 2; | |
| var ry = cy - size / 2; | |
| var dist = Math.sqrt(rx * rx + ry * ry) || 0.001; | |
| var angle = Math.atan2(ry, rx); | |
| // Unit radial direction | |
| var ux = rx / dist; | |
| var uy = ry / dist; | |
| f.style.transformOrigin = cx + 'px ' + cy + 'px'; | |
| data.push({ el: f, cx: cx, cy: cy, rx: rx, ry: ry, dist: dist, angle: angle, ux: ux, uy: uy }); | |
| }); | |
| return data; | |
| } | |
| (function() { | |
| var el = document.getElementById('shape-exploding-cube'); | |
| var rd = radialData(el, 400); | |
| var g = el.querySelector('.shape-group'); | |
| var t = 0; | |
| rd.forEach(function(d) { | |
| d.el.style.transition = 'transform 0.7s cubic-bezier(0.34, 1.56, 0.64, 1)'; | |
| }); | |
| (function idleTick() { | |
| t += 0.15; | |
| if (!el.matches(':hover')) { | |
| g.style.transform = 'rotate(' + (Math.sin(t * 0.02) * 2) + 'deg)'; | |
| } | |
| requestAnimationFrame(idleTick); | |
| })(); | |
| el.addEventListener('mouseenter', function() { | |
| rd.forEach(function(d) { | |
| // Strictly radial: magnitude = base + proportional to distance from center | |
| var mag = 30 + d.dist * 0.4; | |
| d.el.style.transform = 'translate(' + (d.ux * mag) + 'px, ' + (d.uy * mag) + 'px)'; | |
| }); | |
| }); | |
| el.addEventListener('mouseleave', function() { | |
| rd.forEach(function(d) { d.el.style.transform = 'translate(0, 0)'; }); | |
| }); | |
| })();</script> | |
| <div class="card-info"> | |
| <div class="card-name">Exploding cube</div> | |
| <div class="card-desc">A golden cube that blows apart into its six faces when you hover, then reassembles.</div> | |
| <span class="card-anim">explode</span> | |
| </div> | |
| </div> | |
| <div class="card"> | |
| <svg id="shape-pyramid-pulse" viewBox="0 0 400 400" width="400" height="400" class="w-full max-w-[400px] h-auto overflow-visible" style="cursor: pointer;"> | |
| <g class="shape-group" style="transform-origin: 50% 50%;"> | |
| <polygon data-face="0" points="200.00,147.35 258.95,285.26 153.54,289.61" fill="#8a5e14" stroke="#d49422" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="1" points="200.00,147.35 153.54,289.61 112.34,242.89" fill="#a87218" stroke="#f0b400" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="2" points="200.00,147.35 282.90,235.85 258.95,285.26" fill="#7a5210" stroke="#b67e1c" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="3" points="192.28,209.67 112.34,242.89 153.54,289.61 258.95,285.26 282.90,235.85" fill="#5c3d0a" stroke="#986816" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="4" points="200.00,147.35 112.34,242.89 192.28,209.67" fill="#b67e1c" stroke="#ffc206" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="5" points="200.00,147.35 192.28,209.67 282.90,235.85" fill="#d49422" stroke="#ffd23f" stroke-width="1.5" stroke-linejoin="round"/> | |
| </g> | |
| </svg> | |
| <script> | |
| (function() { | |
| var el = document.getElementById('shape-pyramid-pulse'); | |
| var g = el.querySelector('.shape-group'); | |
| g.style.transformOrigin = '50% 50%'; | |
| var t = 0; | |
| el.addEventListener('mouseenter', function() { | |
| g.style.transition = 'transform 0.6s cubic-bezier(0.34, 1.56, 0.64, 1)'; | |
| g.style.transform = 'scale(1.15)'; | |
| }); | |
| el.addEventListener('mouseleave', function() { | |
| g.style.transition = 'transform 0.6s cubic-bezier(0.34, 1.56, 0.64, 1)'; | |
| g.style.transform = 'scale(1)'; | |
| }); | |
| (function idle() { | |
| t += 0.02; | |
| if (!el.matches(':hover')) { | |
| var s = 1 + Math.sin(t * 0.2) * 0.03; | |
| g.style.transition = 'none'; | |
| g.style.transform = 'scale(' + s + ')'; | |
| } | |
| requestAnimationFrame(idle); | |
| })(); | |
| })();</script> | |
| <div class="card-info"> | |
| <div class="card-name">Pyramid pulse</div> | |
| <div class="card-desc">A pentagonal pyramid with a subtle breathing pulse. Swells on hover.</div> | |
| <span class="card-anim">pulse</span> | |
| </div> | |
| </div> | |
| <div class="card"> | |
| <svg id="shape-octahedron-bloom" viewBox="0 0 400 400" width="400" height="400" class="w-full max-w-[400px] h-auto overflow-visible" style="cursor: pointer;"> | |
| <g class="shape-group" style="transform-origin: 50% 50%;"> | |
| <polygon data-face="0" points="200.00,121.02 235.05,239.74 117.10,216.80" fill="#c48a1e" stroke="#ffd23f" stroke-width="1.8" stroke-linejoin="round"/> | |
| <polygon data-face="1" points="200.00,121.02 282.90,183.20 235.05,239.74" fill="#b67e1c" stroke="#ffc206" stroke-width="1.8" stroke-linejoin="round"/> | |
| <polygon data-face="2" points="200.00,278.98 117.10,216.80 235.05,239.74" fill="#8a5e14" stroke="#d49422" stroke-width="1.8" stroke-linejoin="round"/> | |
| <polygon data-face="3" points="200.00,121.02 117.10,216.80 164.95,160.26" fill="#d49422" stroke="#ffe066" stroke-width="1.8" stroke-linejoin="round"/> | |
| <polygon data-face="4" points="200.00,278.98 235.05,239.74 282.90,183.20" fill="#7a5210" stroke="#b67e1c" stroke-width="1.8" stroke-linejoin="round"/> | |
| <polygon data-face="5" points="200.00,121.02 164.95,160.26 282.90,183.20" fill="#e4a024" stroke="#ffeb99" stroke-width="1.8" stroke-linejoin="round"/> | |
| <polygon data-face="6" points="200.00,278.98 164.95,160.26 117.10,216.80" fill="#986816" stroke="#f0b400" stroke-width="1.8" stroke-linejoin="round"/> | |
| <polygon data-face="7" points="200.00,278.98 282.90,183.20 164.95,160.26" fill="#a87218" stroke="#ffc206" stroke-width="1.8" stroke-linejoin="round"/> | |
| </g> | |
| </svg> | |
| <script> | |
| function radialData(el, size) { | |
| const faces = el.querySelectorAll('[data-face]'); | |
| const data = []; | |
| faces.forEach(function(f) { | |
| var pts = f.getAttribute('points').split(' ').map(function(p) { return p.split(',').map(Number); }); | |
| var cx = pts.reduce(function(s, p) { return s + p[0]; }, 0) / pts.length; | |
| var cy = pts.reduce(function(s, p) { return s + p[1]; }, 0) / pts.length; | |
| var rx = cx - size / 2; | |
| var ry = cy - size / 2; | |
| var dist = Math.sqrt(rx * rx + ry * ry) || 0.001; | |
| var angle = Math.atan2(ry, rx); | |
| // Unit radial direction | |
| var ux = rx / dist; | |
| var uy = ry / dist; | |
| f.style.transformOrigin = cx + 'px ' + cy + 'px'; | |
| data.push({ el: f, cx: cx, cy: cy, rx: rx, ry: ry, dist: dist, angle: angle, ux: ux, uy: uy }); | |
| }); | |
| return data; | |
| } | |
| (function() { | |
| var el = document.getElementById('shape-octahedron-bloom'); | |
| var rd = radialData(el, 400); | |
| rd.forEach(function(d) { | |
| d.el.style.transition = 'transform 0.8s cubic-bezier(0.34, 1.56, 0.64, 1)'; | |
| }); | |
| el.addEventListener('mouseenter', function() { | |
| rd.forEach(function(d, i) { | |
| var mag = 20 + d.dist * 0.4; | |
| d.el.style.transform = 'translate(' + (d.ux * mag) + 'px, ' + (d.uy * mag) + 'px) scale(0.92)'; | |
| d.el.style.transitionDelay = (i * 0.02) + 's'; | |
| }); | |
| }); | |
| el.addEventListener('mouseleave', function() { | |
| rd.forEach(function(d, i) { | |
| d.el.style.transform = 'translate(0, 0) scale(1)'; | |
| d.el.style.transitionDelay = (i * 0.01) + 's'; | |
| }); | |
| }); | |
| })();</script> | |
| <div class="card-info"> | |
| <div class="card-name">Octahedron bloom</div> | |
| <div class="card-desc">An octahedron whose eight faces peel open like a flower when hovered.</div> | |
| <span class="card-anim">bloom</span> | |
| </div> | |
| </div> | |
| <div class="card"> | |
| <svg id="shape-gem-wobble" viewBox="0 0 400 400" width="400" height="400" class="w-full max-w-[400px] h-auto overflow-visible" style="cursor: pointer;"> | |
| <g class="shape-group" style="transform-origin: 50% 50%;"> | |
| <polygon data-face="0" points="215.58,162.38 184.96,162.48 169.93,223.25 231.15,223.04" fill="#c48a1e" stroke="#ffc206" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="1" points="184.96,162.48 163.16,152.18 126.32,202.65 169.93,223.25" fill="#c48a1e" stroke="#ffd23f" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="2" points="200.00,138.57 215.58,162.38 184.96,162.48" fill="#ffd23f" stroke="#ffc206" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="3" points="237.07,151.92 215.58,162.38 231.15,223.04 274.13,202.13" fill="#b67e1c" stroke="#ffc206" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="4" points="200.00,138.57 184.96,162.48 163.16,152.18" fill="#f0b400" stroke="#ffc206" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="5" points="200.00,138.57 237.07,151.92 215.58,162.38" fill="#ffc206" stroke="#ffc206" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="6" points="231.15,223.04 169.93,223.25 200.00,261.43" fill="#7a5210" stroke="#b67e1c" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="7" points="200.00,138.57 163.16,152.18 162.93,137.50" fill="#ffc206" stroke="#ffc206" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="8" points="200.00,138.57 236.84,137.24 237.07,151.92" fill="#f0b400" stroke="#ffc206" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="9" points="169.93,223.25 126.32,202.65 200.00,261.43" fill="#7a5210" stroke="#b67e1c" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="10" points="274.13,202.13 231.15,223.04 200.00,261.43" fill="#6b480c" stroke="#986816" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="11" points="163.16,152.18 162.93,137.50 125.87,173.30 126.32,202.65" fill="#d49422" stroke="#ffd23f" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="12" points="236.84,137.24 237.07,151.92 274.13,202.13 273.68,172.78" fill="#b67e1c" stroke="#ffc206" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="13" points="200.00,138.57 162.93,137.50 184.42,127.05" fill="#ffd23f" stroke="#ffc206" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="14" points="200.00,138.57 215.04,126.94 236.84,137.24" fill="#ffc206" stroke="#ffc206" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="15" points="200.00,138.57 184.42,127.05 215.04,126.94" fill="#f0b400" stroke="#ffc206" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="16" points="126.32,202.65 125.87,173.30 200.00,261.43" fill="#8a5e14" stroke="#d49422" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="17" points="273.68,172.78 274.13,202.13 200.00,261.43" fill="#6b480c" stroke="#986816" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="18" points="162.93,137.50 184.42,127.05 168.85,152.39 125.87,173.30" fill="#d49422" stroke="#ffd23f" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="19" points="215.04,126.94 236.84,137.24 273.68,172.78 230.07,152.17" fill="#e4a024" stroke="#ffe066" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="20" points="184.42,127.05 215.04,126.94 230.07,152.17 168.85,152.39" fill="#e4a024" stroke="#ffe066" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="21" points="125.87,173.30 168.85,152.39 200.00,261.43" fill="#986816" stroke="#d49422" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="22" points="230.07,152.17 273.68,172.78 200.00,261.43" fill="#a87218" stroke="#f0b400" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="23" points="168.85,152.39 230.07,152.17 200.00,261.43" fill="#986816" stroke="#f0b400" stroke-width="1.5" stroke-linejoin="round"/> | |
| </g> | |
| </svg> | |
| <script> | |
| (function() { | |
| var el = document.getElementById('shape-gem-wobble'); | |
| var g = el.querySelector('.shape-group'); | |
| g.style.transformOrigin = '50% 50%'; | |
| el.addEventListener('mouseenter', function() { | |
| g.style.transition = 'transform 0.15s ease'; | |
| g.style.transform = 'rotate(8deg) scale(1.05)'; | |
| setTimeout(function() { | |
| g.style.transition = 'transform 0.3s cubic-bezier(0.34, 1.56, 0.64, 1)'; | |
| g.style.transform = 'rotate(-5deg) scale(1.05)'; | |
| setTimeout(function() { g.style.transform = 'rotate(0deg) scale(1.05)'; }, 300); | |
| }, 150); | |
| }); | |
| el.addEventListener('mouseleave', function() { | |
| g.style.transition = 'transform 0.5s cubic-bezier(0.34, 1.56, 0.64, 1)'; | |
| g.style.transform = 'rotate(0deg) scale(1)'; | |
| }); | |
| })();</script> | |
| <div class="card-info"> | |
| <div class="card-name">Gem wobble</div> | |
| <div class="card-desc">A faceted gemstone that wobbles playfully when touched.</div> | |
| <span class="card-anim">wobble</span> | |
| </div> | |
| </div> | |
| <div class="card"> | |
| <svg id="shape-star-spiral" viewBox="0 0 400 400" width="400" height="400" class="w-full max-w-[400px] h-auto overflow-visible" style="cursor: pointer;"> | |
| <g class="shape-group" style="transform-origin: 50% 50%;"> | |
| <polygon data-face="0" points="217.52,202.32 177.37,229.15 177.37,264.25 217.52,237.42" fill="#a87218" stroke="#f0b400" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="1" points="177.37,229.15 166.00,196.58 166.00,231.68 177.37,264.25" fill="#b67e1c" stroke="#ffc206" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="2" points="285.64,207.20 217.52,202.32 217.52,237.42 285.64,242.30" fill="#986816" stroke="#d49422" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="3" points="244.83,180.60 285.64,207.20 285.64,242.30 244.83,215.70" fill="#7a5210" stroke="#d49422" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="4" points="166.00,196.58 100.37,186.56 100.37,221.66 166.00,231.68" fill="#d49422" stroke="#ffc206" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="5" points="161.06,138.29 210.19,161.43 275.56,151.05 244.83,180.60 285.64,207.20 217.52,202.32 177.37,229.15 166.00,196.58 100.37,186.56 161.46,171.31" fill="#f0b400" stroke="#ffc206" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="6" points="100.37,186.56 161.46,171.31 161.46,206.41 100.37,221.66" fill="#e4a024" stroke="#ffd23f" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="7" points="161.46,206.41 100.37,221.66 166.00,231.68 177.37,264.25 217.52,237.42 285.64,242.30 244.83,215.70 275.56,186.15 210.19,196.54 161.06,173.39" fill="#5c3d0a" stroke="#986816" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="8" points="275.56,151.05 244.83,180.60 244.83,215.70 275.56,186.15" fill="#6b480c" stroke="#b67e1c" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="9" points="210.19,161.43 275.56,151.05 275.56,186.15 210.19,196.54" fill="#5c3d0a" stroke="#986816" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="10" points="161.46,171.31 161.06,138.29 161.06,173.39 161.46,206.41" fill="#f0b400" stroke="#ffe066" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="11" points="161.06,138.29 210.19,161.43 210.19,196.54 161.06,173.39" fill="#4e3608" stroke="#986816" stroke-width="1.5" stroke-linejoin="round"/> | |
| </g> | |
| </svg> | |
| <script> | |
| function radialData(el, size) { | |
| const faces = el.querySelectorAll('[data-face]'); | |
| const data = []; | |
| faces.forEach(function(f) { | |
| var pts = f.getAttribute('points').split(' ').map(function(p) { return p.split(',').map(Number); }); | |
| var cx = pts.reduce(function(s, p) { return s + p[0]; }, 0) / pts.length; | |
| var cy = pts.reduce(function(s, p) { return s + p[1]; }, 0) / pts.length; | |
| var rx = cx - size / 2; | |
| var ry = cy - size / 2; | |
| var dist = Math.sqrt(rx * rx + ry * ry) || 0.001; | |
| var angle = Math.atan2(ry, rx); | |
| // Unit radial direction | |
| var ux = rx / dist; | |
| var uy = ry / dist; | |
| f.style.transformOrigin = cx + 'px ' + cy + 'px'; | |
| data.push({ el: f, cx: cx, cy: cy, rx: rx, ry: ry, dist: dist, angle: angle, ux: ux, uy: uy }); | |
| }); | |
| return data; | |
| } | |
| (function() { | |
| var el = document.getElementById('shape-star-spiral'); | |
| var rd = radialData(el, 400); | |
| rd.forEach(function(d) { | |
| d.el.style.transition = 'transform 0.6s cubic-bezier(0.34, 1.56, 0.64, 1)'; | |
| }); | |
| el.addEventListener('mouseenter', function() { | |
| rd.forEach(function(d, i) { | |
| var mag = 25 + d.dist * 0.5; | |
| d.el.style.transform = 'translate(' + (d.ux * mag) + 'px, ' + (d.uy * mag) + 'px)'; | |
| // Staggered delay creates the spiral wave feel | |
| d.el.style.transitionDelay = (i * 0.03) + 's'; | |
| }); | |
| }); | |
| el.addEventListener('mouseleave', function() { | |
| rd.forEach(function(d, i) { | |
| d.el.style.transform = 'translate(0, 0)'; | |
| d.el.style.transitionDelay = (i * 0.015) + 's'; | |
| }); | |
| }); | |
| })();</script> | |
| <div class="card-info"> | |
| <div class="card-name">Star spiral</div> | |
| <div class="card-desc">A five-pointed star prism whose faces spiral outward on hover with staggered timing.</div> | |
| <span class="card-anim">spiral</span> | |
| </div> | |
| </div> | |
| <div class="card"> | |
| <svg id="shape-radial-assembly" viewBox="0 0 400 400" width="400" height="400" class="w-full max-w-[400px] h-auto overflow-visible" style="cursor: pointer;"> | |
| <g class="shape-group" style="transform-origin: 50% 50%;"> | |
| <polygon data-face="0" points="235.05,224.38 166.17,224.63 166.17,255.34 235.05,255.10" fill="#986816" stroke="#f0b400" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="1" points="200.00,184.64 235.05,224.38 166.17,224.63" fill="#c48a1e" stroke="#ffc206" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="2" points="166.17,224.63 117.10,201.45 117.10,232.16 166.17,255.34" fill="#a87218" stroke="#f0b400" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="3" points="283.40,200.86 235.05,224.38 235.05,255.10 283.40,231.58" fill="#8a5e14" stroke="#d49422" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="4" points="200.00,184.64 166.17,224.63 117.10,201.45" fill="#d49422" stroke="#ffc206" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="5" points="200.00,184.64 283.40,200.86 235.05,224.38" fill="#b67e1c" stroke="#f0b400" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="6" points="200.00,215.36 166.17,255.34 235.05,255.10" fill="#6b480c" stroke="#986816" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="7" points="200.00,215.36 117.10,232.16 166.17,255.34" fill="#6b480c" stroke="#b67e1c" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="8" points="200.00,215.36 235.05,255.10 283.40,231.58" fill="#5c3d0a" stroke="#986816" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="9" points="200.00,184.64 117.10,201.45 116.60,168.42" fill="#e4a024" stroke="#ffd23f" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="10" points="200.00,184.64 282.90,167.84 283.40,200.86" fill="#b67e1c" stroke="#f0b400" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="11" points="117.10,201.45 116.60,168.42 116.60,199.14 117.10,232.16" fill="#b67e1c" stroke="#ffc206" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="12" points="282.90,167.84 283.40,200.86 283.40,231.58 282.90,198.55" fill="#8a5e14" stroke="#d49422" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="13" points="200.00,215.36 116.60,199.14 117.10,232.16" fill="#7a5210" stroke="#b67e1c" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="14" points="200.00,215.36 283.40,231.58 282.90,198.55" fill="#5c3d0a" stroke="#986816" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="15" points="200.00,184.64 116.60,168.42 164.95,144.90" fill="#e4a024" stroke="#ffd23f" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="16" points="200.00,184.64 233.83,144.66 282.90,167.84" fill="#ffc206" stroke="#ffe066" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="17" points="200.00,184.64 164.95,144.90 233.83,144.66" fill="#f0b400" stroke="#ffe066" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="18" points="200.00,215.36 164.95,175.62 116.60,199.14" fill="#7a5210" stroke="#b67e1c" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="19" points="200.00,215.36 282.90,198.55 233.83,175.37" fill="#8a5e14" stroke="#d49422" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="20" points="116.60,168.42 164.95,144.90 164.95,175.62 116.60,199.14" fill="#b67e1c" stroke="#ffc206" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="21" points="233.83,144.66 282.90,167.84 282.90,198.55 233.83,175.37" fill="#d49422" stroke="#ffd23f" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="22" points="200.00,215.36 233.83,175.37 164.95,175.62" fill="#8a5e14" stroke="#d49422" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="23" points="164.95,144.90 233.83,144.66 233.83,175.37 164.95,175.62" fill="#c48a1e" stroke="#ffd23f" stroke-width="1.5" stroke-linejoin="round"/> | |
| </g> | |
| </svg> | |
| <script> | |
| function radialData(el, size) { | |
| const faces = el.querySelectorAll('[data-face]'); | |
| const data = []; | |
| faces.forEach(function(f) { | |
| var pts = f.getAttribute('points').split(' ').map(function(p) { return p.split(',').map(Number); }); | |
| var cx = pts.reduce(function(s, p) { return s + p[0]; }, 0) / pts.length; | |
| var cy = pts.reduce(function(s, p) { return s + p[1]; }, 0) / pts.length; | |
| var rx = cx - size / 2; | |
| var ry = cy - size / 2; | |
| var dist = Math.sqrt(rx * rx + ry * ry) || 0.001; | |
| var angle = Math.atan2(ry, rx); | |
| // Unit radial direction | |
| var ux = rx / dist; | |
| var uy = ry / dist; | |
| f.style.transformOrigin = cx + 'px ' + cy + 'px'; | |
| data.push({ el: f, cx: cx, cy: cy, rx: rx, ry: ry, dist: dist, angle: angle, ux: ux, uy: uy }); | |
| }); | |
| return data; | |
| } | |
| (function() { | |
| var el = document.getElementById('shape-radial-assembly'); | |
| var rd = radialData(el, 400); | |
| rd.forEach(function(d) { | |
| var mag = 35 + d.dist * 0.35; | |
| d.el.style.transition = 'transform 0.7s cubic-bezier(0.34, 1.56, 0.64, 1)'; | |
| d.el.style.transform = 'translate(' + (d.ux * mag) + 'px, ' + (d.uy * mag) + 'px)'; | |
| }); | |
| el.addEventListener('mouseenter', function() { | |
| rd.forEach(function(d) { | |
| d.el.style.transform = 'translate(0, 0)'; | |
| }); | |
| }); | |
| el.addEventListener('mouseleave', function() { | |
| rd.forEach(function(d) { | |
| var mag = 35 + d.dist * 0.35; | |
| d.el.style.transform = 'translate(' + (d.ux * mag) + 'px, ' + (d.uy * mag) + 'px)'; | |
| }); | |
| }); | |
| })();</script> | |
| <div class="card-info"> | |
| <div class="card-name">Radial assembly</div> | |
| <div class="card-desc">Eight triangular wedges scattered apart, snapping together into a solid ring on hover.</div> | |
| <span class="card-anim">assemble</span> | |
| </div> | |
| </div> | |
| <div class="card"> | |
| <svg id="shape-icosahedron-scatter" viewBox="0 0 400 400" width="400" height="400" class="w-full max-w-[400px] h-auto overflow-visible" style="cursor: pointer;"> | |
| <g class="shape-group" style="transform-origin: 50% 50%;"> | |
| <polygon data-face="0" points="158.84,144.89 228.16,192.71 150.80,233.23" fill="#e4a024" stroke="#ffd23f" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="1" points="228.16,271.15 150.80,233.23 228.16,192.71" fill="#986816" stroke="#d49422" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="2" points="241.16,128.20 228.16,192.71 158.84,144.89" fill="#b67e1c" stroke="#ffc206" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="3" points="228.16,192.71 284.00,206.23 228.16,271.15" fill="#d49422" stroke="#ffd23f" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="4" points="241.16,128.20 284.00,206.23 228.16,192.71" fill="#b67e1c" stroke="#ffc206" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="5" points="150.80,233.23 116.00,193.77 158.84,144.89" fill="#986816" stroke="#f0b400" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="6" points="158.84,271.80 150.80,233.23 228.16,271.15" fill="#6b480c" stroke="#b67e1c" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="7" points="241.16,128.20 158.84,144.89 171.84,128.85" fill="#c48a1e" stroke="#ffc206" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="8" points="158.84,271.80 116.00,193.77 150.80,233.23" fill="#8a5e14" stroke="#d49422" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="9" points="171.84,128.85 158.84,144.89 116.00,193.77" fill="#e4a024" stroke="#ffe066" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="10" points="241.16,255.11 228.16,271.15 284.00,206.23" fill="#8a5e14" stroke="#d49422" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="11" points="241.16,128.20 249.20,166.77 284.00,206.23" fill="#d49422" stroke="#ffd23f" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="12" points="158.84,271.80 228.16,271.15 241.16,255.11" fill="#6b480c" stroke="#b67e1c" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="13" points="241.16,128.20 171.84,128.85 249.20,166.77" fill="#c48a1e" stroke="#ffc206" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="14" points="284.00,206.23 249.20,166.77 241.16,255.11" fill="#f0b400" stroke="#ffe066" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="15" points="158.84,271.80 171.84,207.29 116.00,193.77" fill="#7a5210" stroke="#b67e1c" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="16" points="116.00,193.77 171.84,207.29 171.84,128.85" fill="#a87218" stroke="#f0b400" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="17" points="158.84,271.80 241.16,255.11 171.84,207.29" fill="#7a5210" stroke="#b67e1c" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="18" points="249.20,166.77 171.84,128.85 171.84,207.29" fill="#f0b400" stroke="#ffe066" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="19" points="171.84,207.29 241.16,255.11 249.20,166.77" fill="#a87218" stroke="#f0b400" stroke-width="1.5" stroke-linejoin="round"/> | |
| </g> | |
| </svg> | |
| <script> | |
| function radialData(el, size) { | |
| const faces = el.querySelectorAll('[data-face]'); | |
| const data = []; | |
| faces.forEach(function(f) { | |
| var pts = f.getAttribute('points').split(' ').map(function(p) { return p.split(',').map(Number); }); | |
| var cx = pts.reduce(function(s, p) { return s + p[0]; }, 0) / pts.length; | |
| var cy = pts.reduce(function(s, p) { return s + p[1]; }, 0) / pts.length; | |
| var rx = cx - size / 2; | |
| var ry = cy - size / 2; | |
| var dist = Math.sqrt(rx * rx + ry * ry) || 0.001; | |
| var angle = Math.atan2(ry, rx); | |
| // Unit radial direction | |
| var ux = rx / dist; | |
| var uy = ry / dist; | |
| f.style.transformOrigin = cx + 'px ' + cy + 'px'; | |
| data.push({ el: f, cx: cx, cy: cy, rx: rx, ry: ry, dist: dist, angle: angle, ux: ux, uy: uy }); | |
| }); | |
| return data; | |
| } | |
| (function() { | |
| var el = document.getElementById('shape-icosahedron-scatter'); | |
| var rd = radialData(el, 400); | |
| var rand = function(seed) { var x = Math.sin(seed * 127.1 + 311.7) * 43758.5453; return x - Math.floor(x); }; | |
| rd.forEach(function(d) { | |
| d.el.style.transition = 'transform 0.5s cubic-bezier(0.34, 1.56, 0.64, 1)'; | |
| }); | |
| el.addEventListener('mouseenter', function() { | |
| rd.forEach(function(d, i) { | |
| // Radial direction, random magnitude (20–80px range) | |
| var mag = 20 + rand(i * 7) * 60; | |
| d.el.style.transform = 'translate(' + (d.ux * mag) + 'px, ' + (d.uy * mag) + 'px)'; | |
| d.el.style.transitionDelay = (rand(i * 5) * 0.15) + 's'; | |
| }); | |
| }); | |
| el.addEventListener('mouseleave', function() { | |
| rd.forEach(function(d) { | |
| d.el.style.transform = 'translate(0, 0)'; | |
| d.el.style.transitionDelay = '0s'; | |
| }); | |
| }); | |
| })();</script> | |
| <div class="card-info"> | |
| <div class="card-name">Icosahedron scatter</div> | |
| <div class="card-desc">Twenty triangular faces that fly apart in random directions when you hover.</div> | |
| <span class="card-anim">scatter</span> | |
| </div> | |
| </div> | |
| <div class="card"> | |
| <svg id="shape-torus-breathe" viewBox="0 0 400 400" width="400" height="400" class="w-full max-w-[400px] h-auto overflow-visible" style="cursor: pointer;"> | |
| <g class="shape-group" style="transform-origin: 50% 50%;"> | |
| <polygon data-face="0" points="229.60,216.84 190.63,219.44 189.27,241.39 233.88,238.42" fill="#b67e1c" stroke="#d49422" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="1" points="190.63,219.44 154.18,212.35 147.54,233.28 189.27,241.39" fill="#b67e1c" stroke="#d49422" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="2" points="233.88,238.42 189.27,241.39 190.63,252.88 229.60,250.28" fill="#986816" stroke="#d49422" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="3" points="221.03,207.13 193.34,208.97 190.63,219.44 229.60,216.84" fill="#a87218" stroke="#d49422" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="4" points="193.34,208.97 167.44,203.93 154.18,212.35 190.63,219.44" fill="#a87218" stroke="#d49422" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="5" points="189.27,241.39 147.54,233.28 154.18,245.79 190.63,252.88" fill="#986816" stroke="#d49422" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="6" points="260.63,205.25 229.60,216.84 233.88,238.42 269.41,225.15" fill="#a87218" stroke="#b67e1c" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="7" points="243.08,198.89 221.03,207.13 229.60,216.84 260.63,205.25" fill="#986816" stroke="#b67e1c" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="8" points="269.41,225.15 233.88,238.42 229.60,250.28 260.63,238.69" fill="#8a5e14" stroke="#b67e1c" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="9" points="154.18,212.35 130.00,197.47 119.87,216.24 147.54,233.28" fill="#c48a1e" stroke="#f0b400" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="10" points="229.60,250.28 190.63,252.88 193.34,242.41 221.03,240.57" fill="#a87218" stroke="#d49422" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="11" points="216.74,218.99 194.70,220.46 193.34,208.97 221.03,207.13" fill="#986816" stroke="#d49422" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="12" points="167.44,203.93 150.26,193.36 130.00,197.47 154.18,212.35" fill="#b67e1c" stroke="#f0b400" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="13" points="194.70,220.46 174.07,216.45 167.44,203.93 193.34,208.97" fill="#986816" stroke="#d49422" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="14" points="190.63,252.88 154.18,245.79 167.44,237.37 193.34,242.41" fill="#a87218" stroke="#d49422" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="15" points="147.54,233.28 119.87,216.24 130.00,230.91 154.18,245.79" fill="#a87218" stroke="#f0b400" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="16" points="234.30,212.43 216.74,218.99 221.03,207.13 243.08,198.89" fill="#8a5e14" stroke="#b67e1c" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="17" points="221.03,240.57 193.34,242.41 194.70,220.46 216.74,218.99" fill="#b67e1c" stroke="#d49422" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="18" points="260.63,238.69 229.60,250.28 221.03,240.57 243.08,232.33" fill="#986816" stroke="#b67e1c" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="19" points="193.34,242.41 167.44,237.37 174.07,216.45 194.70,220.46" fill="#b67e1c" stroke="#d49422" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="20" points="275.42,187.77 260.63,205.25 269.41,225.15 286.34,205.14" fill="#986816" stroke="#b67e1c" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="21" points="253.59,186.47 243.08,198.89 260.63,205.25 275.42,187.77" fill="#8a5e14" stroke="#b67e1c" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="22" points="174.07,216.45 160.39,208.03 150.26,193.36 167.44,203.93" fill="#a87218" stroke="#f0b400" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="23" points="243.08,232.33 221.03,240.57 216.74,218.99 234.30,212.43" fill="#a87218" stroke="#b67e1c" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="24" points="154.18,245.79 130.00,230.91 150.26,226.80 167.44,237.37" fill="#b67e1c" stroke="#f0b400" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="25" points="286.34,205.14 269.41,225.15 260.63,238.69 275.42,221.21" fill="#7a5210" stroke="#b67e1c" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="26" points="167.44,237.37 150.26,226.80 160.39,208.03 174.07,216.45" fill="#c48a1e" stroke="#f0b400" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="27" points="242.67,202.54 234.30,212.43 243.08,198.89 253.59,186.47" fill="#7a5210" stroke="#b67e1c" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="28" points="150.26,193.36 146.41,180.09 124.58,178.79 130.00,197.47" fill="#c48a1e" stroke="#f0b400" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="29" points="130.00,197.47 124.58,178.79 113.66,194.86 119.87,216.24" fill="#d49422" stroke="#f0b400" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="30" points="275.42,221.21 260.63,238.69 243.08,232.33 253.59,219.91" fill="#8a5e14" stroke="#b67e1c" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="31" points="253.59,219.91 243.08,232.33 234.30,212.43 242.67,202.54" fill="#986816" stroke="#b67e1c" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="32" points="160.39,208.03 157.33,197.46 146.41,180.09 150.26,193.36" fill="#b67e1c" stroke="#f0b400" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="33" points="119.87,216.24 113.66,194.86 124.58,212.23 130.00,230.91" fill="#b67e1c" stroke="#f0b400" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="34" points="249.74,173.20 253.59,186.47 275.42,187.77 270.00,169.09" fill="#8a5e14" stroke="#b67e1c" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="35" points="150.26,226.80 146.41,213.53 157.33,197.46 160.39,208.03" fill="#d49422" stroke="#f0b400" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="36" points="239.61,191.97 242.67,202.54 253.59,186.47 249.74,173.20" fill="#7a5210" stroke="#b67e1c" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="37" points="130.00,230.91 124.58,212.23 146.41,213.53 150.26,226.80" fill="#c48a1e" stroke="#f0b400" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="38" points="270.00,169.09 275.42,187.77 286.34,205.14 280.13,183.76" fill="#986816" stroke="#b67e1c" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="39" points="249.74,206.64 253.59,219.91 242.67,202.54 239.61,191.97" fill="#986816" stroke="#b67e1c" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="40" points="157.33,197.46 165.70,187.57 156.92,167.67 146.41,180.09" fill="#b67e1c" stroke="#f0b400" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="41" points="146.41,180.09 156.92,167.67 139.37,161.31 124.58,178.79" fill="#c48a1e" stroke="#f0b400" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="42" points="280.13,183.76 286.34,205.14 275.42,221.21 270.00,202.53" fill="#7a5210" stroke="#b67e1c" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="43" points="270.00,202.53 275.42,221.21 253.59,219.91 249.74,206.64" fill="#8a5e14" stroke="#b67e1c" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="44" points="146.41,213.53 156.92,201.11 165.70,187.57 157.33,197.46" fill="#d49422" stroke="#f0b400" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="45" points="225.93,183.55 239.61,191.97 249.74,173.20 232.56,162.63" fill="#e4a024" stroke="#ffd23f" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="46" points="124.58,178.79 139.37,161.31 130.59,174.85 113.66,194.86" fill="#d49422" stroke="#f0b400" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="47" points="232.56,162.63 249.74,173.20 270.00,169.09 245.82,154.21" fill="#f0b400" stroke="#ffd23f" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="48" points="165.70,187.57 183.26,181.01 178.97,159.43 156.92,167.67" fill="#c48a1e" stroke="#ffc206" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="49" points="232.56,196.07 249.74,206.64 239.61,191.97 225.93,183.55" fill="#ffc206" stroke="#ffd23f" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="50" points="124.58,212.23 139.37,194.75 156.92,201.11 146.41,213.53" fill="#c48a1e" stroke="#f0b400" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="51" points="113.66,194.86 130.59,174.85 139.37,194.75 124.58,212.23" fill="#b67e1c" stroke="#f0b400" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="52" points="205.30,179.54 225.93,183.55 232.56,162.63 206.66,157.59" fill="#d49422" stroke="#ffd23f" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="53" points="156.92,167.67 178.97,159.43 170.40,149.72 139.37,161.31" fill="#d49422" stroke="#ffc206" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="54" points="183.26,181.01 205.30,179.54 206.66,157.59 178.97,159.43" fill="#d49422" stroke="#ffc206" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="55" points="156.92,201.11 178.97,192.87 183.26,181.01 165.70,187.57" fill="#e4a024" stroke="#ffc206" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="56" points="245.82,154.21 270.00,169.09 280.13,183.76 252.46,166.72" fill="#ffc206" stroke="#ffd23f" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="57" points="206.66,157.59 232.56,162.63 245.82,154.21 209.37,147.12" fill="#e4a024" stroke="#ffd23f" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="58" points="206.66,191.03 232.56,196.07 225.93,183.55 205.30,179.54" fill="#f0b400" stroke="#ffd23f" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="59" points="245.82,187.65 270.00,202.53 249.74,206.64 232.56,196.07" fill="#f0b400" stroke="#ffd23f" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="60" points="178.97,192.87 206.66,191.03 205.30,179.54 183.26,181.01" fill="#f0b400" stroke="#ffc206" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="61" points="178.97,159.43 206.66,157.59 209.37,147.12 170.40,149.72" fill="#e4a024" stroke="#ffc206" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="62" points="252.46,166.72 280.13,183.76 270.00,202.53 245.82,187.65" fill="#e4a024" stroke="#ffd23f" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="63" points="139.37,161.31 170.40,149.72 166.12,161.58 130.59,174.85" fill="#e4a024" stroke="#ffc206" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="64" points="139.37,194.75 170.40,183.16 178.97,192.87 156.92,201.11" fill="#d49422" stroke="#ffc206" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="65" points="130.59,174.85 166.12,161.58 170.40,183.16 139.37,194.75" fill="#c48a1e" stroke="#ffc206" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="66" points="209.37,147.12 245.82,154.21 252.46,166.72 210.73,158.61" fill="#f0b400" stroke="#ffd23f" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="67" points="209.37,180.56 245.82,187.65 232.56,196.07 206.66,191.03" fill="#e4a024" stroke="#ffd23f" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="68" points="170.40,183.16 209.37,180.56 206.66,191.03 178.97,192.87" fill="#e4a024" stroke="#ffc206" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="69" points="170.40,149.72 209.37,147.12 210.73,158.61 166.12,161.58" fill="#f0b400" stroke="#ffc206" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="70" points="210.73,158.61 252.46,166.72 245.82,187.65 209.37,180.56" fill="#d49422" stroke="#ffd23f" stroke-width="1" stroke-linejoin="round"/> | |
| <polygon data-face="71" points="166.12,161.58 210.73,158.61 209.37,180.56 170.40,183.16" fill="#d49422" stroke="#ffc206" stroke-width="1" stroke-linejoin="round"/> | |
| </g> | |
| </svg> | |
| <script> | |
| function radialData(el, size) { | |
| const faces = el.querySelectorAll('[data-face]'); | |
| const data = []; | |
| faces.forEach(function(f) { | |
| var pts = f.getAttribute('points').split(' ').map(function(p) { return p.split(',').map(Number); }); | |
| var cx = pts.reduce(function(s, p) { return s + p[0]; }, 0) / pts.length; | |
| var cy = pts.reduce(function(s, p) { return s + p[1]; }, 0) / pts.length; | |
| var rx = cx - size / 2; | |
| var ry = cy - size / 2; | |
| var dist = Math.sqrt(rx * rx + ry * ry) || 0.001; | |
| var angle = Math.atan2(ry, rx); | |
| // Unit radial direction | |
| var ux = rx / dist; | |
| var uy = ry / dist; | |
| f.style.transformOrigin = cx + 'px ' + cy + 'px'; | |
| data.push({ el: f, cx: cx, cy: cy, rx: rx, ry: ry, dist: dist, angle: angle, ux: ux, uy: uy }); | |
| }); | |
| return data; | |
| } | |
| (function() { | |
| var el = document.getElementById('shape-torus-breathe'); | |
| var rd = radialData(el, 400); | |
| var t = 0; | |
| (function tick() { | |
| t += 0.02; | |
| var hovering = el.matches(':hover'); | |
| rd.forEach(function(d, i) { | |
| var phase = d.dist * 0.02 + i * 0.1; | |
| var wave = Math.sin(t * 0.25 + phase); | |
| if (hovering) { | |
| // Radial push/pull | |
| var mag = wave * 6; | |
| d.el.style.transition = 'transform 0.3s ease'; | |
| d.el.style.transform = 'translate(' + (d.ux * mag) + 'px, ' + (d.uy * mag) + 'px)'; | |
| } else { | |
| // Gentle scale breathing | |
| d.el.style.transition = 'none'; | |
| d.el.style.transform = 'scale(' + (1 + wave * 0.02) + ')'; | |
| } | |
| }); | |
| requestAnimationFrame(tick); | |
| })(); | |
| })();</script> | |
| <div class="card-info"> | |
| <div class="card-name">Torus breathe</div> | |
| <div class="card-desc">A hexagonal torus that gently breathes, with faces pulsing outward on hover.</div> | |
| <span class="card-anim">breathe</span> | |
| </div> | |
| </div> | |
| <div class="card"> | |
| <svg id="shape-spiral-tower" viewBox="0 0 400 400" width="400" height="400" class="w-full max-w-[400px] h-auto overflow-visible" style="cursor: pointer;"> | |
| <g class="shape-group" style="transform-origin: 50% 50%;"> | |
| <polygon data-face="0" points="190.70,116.65 222.93,110.12 222.93,140.84 190.70,147.37" fill="#8a5e14" stroke="#b67e1c" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="1" points="177.07,101.20 209.30,94.67 222.93,110.12 190.70,116.65" fill="#6b480c" stroke="#b67e1c" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="2" points="177.07,131.91 177.07,101.20 190.70,116.65 190.70,147.37" fill="#8a5e14" stroke="#b67e1c" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="3" points="196.66,143.74 224.52,133.59 224.52,164.30 196.66,174.46" fill="#986816" stroke="#b67e1c" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="4" points="175.48,161.10 175.48,130.39 196.66,143.74 196.66,174.46" fill="#986816" stroke="#b67e1c" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="5" points="209.30,94.67 209.30,125.38 222.93,140.84 222.93,110.12" fill="#6b480c" stroke="#b67e1c" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="6" points="175.48,130.39 203.34,120.23 224.52,133.59 196.66,143.74" fill="#7a5210" stroke="#b67e1c" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="7" points="209.30,125.38 177.07,131.91 190.70,147.37 222.93,140.84" fill="#7a5210" stroke="#b67e1c" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="8" points="177.07,131.91 209.30,125.38 209.30,94.67 177.07,101.20" fill="#7a5210" stroke="#b67e1c" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="9" points="175.41,190.39 175.41,159.67 202.83,170.10 202.83,200.82" fill="#a87218" stroke="#d49422" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="10" points="202.83,170.10 224.59,156.96 224.59,187.67 202.83,200.82" fill="#a87218" stroke="#d49422" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="11" points="175.41,159.67 197.17,146.53 224.59,156.96 202.83,170.10" fill="#8a5e14" stroke="#d49422" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="12" points="203.34,150.95 175.48,161.10 196.66,174.46 224.52,164.30" fill="#8a5e14" stroke="#b67e1c" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="13" points="203.34,120.23 203.34,150.95 224.52,164.30 224.52,133.59" fill="#7a5210" stroke="#b67e1c" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="14" points="175.48,161.10 203.34,150.95 203.34,120.23 175.48,130.39" fill="#8a5e14" stroke="#b67e1c" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="15" points="176.88,219.59 176.88,188.87 208.82,195.73 208.82,226.44" fill="#c48a1e" stroke="#f0b400" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="16" points="176.88,188.87 191.18,173.56 223.12,180.41 208.82,195.73" fill="#a87218" stroke="#f0b400" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="17" points="208.82,195.73 223.12,180.41 223.12,211.13 208.82,226.44" fill="#c48a1e" stroke="#f0b400" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="18" points="197.17,177.24 175.41,190.39 202.83,200.82 224.59,187.67" fill="#986816" stroke="#d49422" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="19" points="175.41,190.39 197.17,177.24 197.17,146.53 175.41,159.67" fill="#986816" stroke="#d49422" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="20" points="197.17,146.53 197.17,177.24 224.59,187.67 224.59,156.96" fill="#8a5e14" stroke="#d49422" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="21" points="179.78,248.53 179.78,217.81 214.27,220.66 214.27,251.38" fill="#d49422" stroke="#ffc206" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="22" points="179.78,217.81 185.73,201.28 220.22,204.13 214.27,220.66" fill="#b67e1c" stroke="#ffc206" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="23" points="176.88,219.59 191.18,204.27 191.18,173.56 176.88,188.87" fill="#b67e1c" stroke="#f0b400" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="24" points="191.18,204.27 176.88,219.59 208.82,226.44 223.12,211.13" fill="#b67e1c" stroke="#f0b400" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="25" points="214.27,220.66 220.22,204.13 220.22,234.84 214.27,251.38" fill="#d49422" stroke="#ffc206" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="26" points="183.94,277.04 183.94,246.32 218.83,245.00 218.83,275.71" fill="#e4a024" stroke="#ffd23f" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="27" points="191.18,173.56 191.18,204.27 223.12,211.13 223.12,180.41" fill="#a87218" stroke="#f0b400" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="28" points="179.78,248.53 185.73,231.99 185.73,201.28 179.78,217.81" fill="#c48a1e" stroke="#ffc206" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="29" points="183.94,246.32 181.17,229.60 216.06,228.27 218.83,245.00" fill="#c48a1e" stroke="#ffd23f" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="30" points="185.73,231.99 179.78,248.53 214.27,251.38 220.22,234.84" fill="#c48a1e" stroke="#ffc206" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="31" points="183.94,277.04 181.17,260.31 181.17,229.60 183.94,246.32" fill="#d49422" stroke="#ffd23f" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="32" points="185.73,201.28 185.73,231.99 220.22,234.84 220.22,204.13" fill="#b67e1c" stroke="#ffc206" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="33" points="218.83,245.00 216.06,228.27 216.06,258.99 218.83,275.71" fill="#e4a024" stroke="#ffd23f" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="34" points="181.17,260.31 183.94,277.04 218.83,275.71 216.06,258.99" fill="#d49422" stroke="#ffd23f" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="35" points="181.17,229.60 181.17,260.31 216.06,258.99 216.06,228.27" fill="#c48a1e" stroke="#ffd23f" stroke-width="1.2" stroke-linejoin="round"/> | |
| </g> | |
| </svg> | |
| <script> | |
| (function() { | |
| var el = document.getElementById('shape-spiral-tower'); | |
| var g = el.querySelector('.shape-group'); | |
| g.style.transformOrigin = '50% 50%'; | |
| var t = 0; | |
| el.addEventListener('mouseenter', function() { | |
| g.style.transition = 'transform 0.8s cubic-bezier(0.68, -0.55, 0.27, 1.55)'; | |
| g.style.transform = 'perspective(600px) rotateY(180deg) scale(0.9)'; | |
| }); | |
| el.addEventListener('mouseleave', function() { | |
| g.style.transition = 'transform 0.8s cubic-bezier(0.68, -0.55, 0.27, 1.55)'; | |
| g.style.transform = 'perspective(600px) rotateY(0deg) scale(1)'; | |
| }); | |
| (function idle() { | |
| t += 0.015; | |
| if (!el.matches(':hover')) { | |
| g.style.transition = 'none'; | |
| g.style.transform = 'perspective(600px) rotateY(' + (Math.sin(t) * 3) + 'deg)'; | |
| } | |
| requestAnimationFrame(idle); | |
| })(); | |
| })();</script> | |
| <div class="card-info"> | |
| <div class="card-name">Spiral tower</div> | |
| <div class="card-desc">Stacked twisted cubes forming a tower. Flips 180 degrees on hover.</div> | |
| <span class="card-anim">flip</span> | |
| </div> | |
| </div> | |
| <div class="card"> | |
| <svg id="shape-crystal-cluster" viewBox="0 0 400 400" width="400" height="400" class="w-full max-w-[400px] h-auto overflow-visible" style="cursor: pointer;"> | |
| <g class="shape-group" style="transform-origin: 50% 50%;"> | |
| <polygon data-face="0" points="220.90,177.43 225.04,208.70 214.83,210.97" fill="#ffc206" stroke="#ffe066" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="1" points="220.90,177.43 214.83,210.97 206.16,205.21" fill="#ffc206" stroke="#ffe066" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="2" points="220.90,177.43 226.58,200.67 225.04,208.70" fill="#ffc206" stroke="#ffe066" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="3" points="220.90,177.43 206.16,205.21 207.70,197.17" fill="#ffc206" stroke="#ffe066" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="4" points="220.90,177.43 217.90,194.90 226.58,200.67" fill="#ffc206" stroke="#ffe066" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="5" points="220.90,177.43 207.70,197.17 217.90,194.90" fill="#ffc206" stroke="#ffe066" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="6" points="185.68,165.97 200.58,201.22 184.63,202.63" fill="#d49422" stroke="#ffc206" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="7" points="185.68,165.97 184.63,202.63 170.55,201.46" fill="#d49422" stroke="#ffc206" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="8" points="187.33,234.14 184.63,202.63 200.58,201.22" fill="#8a5e14" stroke="#b67e1c" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="9" points="183.66,172.39 200.82,205.09 180.47,211.40" fill="#f0b400" stroke="#ffd23f" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="10" points="187.33,234.14 170.55,201.46 184.63,202.63" fill="#8a5e14" stroke="#b67e1c" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="11" points="183.66,172.39 180.47,211.40 162.55,201.40" fill="#f0b400" stroke="#ffd23f" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="12" points="185.68,165.97 202.46,198.65 200.58,201.22" fill="#d49422" stroke="#ffc206" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="13" points="202.55,165.31 219.18,201.77 198.32,207.69" fill="#e4a024" stroke="#ffd23f" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="14" points="202.55,165.31 198.32,207.69 180.12,197.31" fill="#e4a024" stroke="#ffd23f" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="15" points="185.68,165.97 170.55,201.46 172.42,198.88" fill="#d49422" stroke="#ffc206" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="16" points="183.66,172.39 203.25,188.79 200.82,205.09" fill="#f0b400" stroke="#ffd23f" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="17" points="211.83,228.44 214.83,210.97 225.04,208.70" fill="#b67e1c" stroke="#f0b400" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="18" points="211.83,228.44 206.16,205.21 214.83,210.97" fill="#b67e1c" stroke="#f0b400" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="19" points="187.33,234.14 200.58,201.22 202.46,198.65" fill="#8a5e14" stroke="#b67e1c" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="20" points="183.66,172.39 162.55,201.40 164.99,185.09" fill="#f0b400" stroke="#ffd23f" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="21" points="187.33,234.14 172.42,198.88 170.55,201.46" fill="#8a5e14" stroke="#b67e1c" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="22" points="211.83,228.44 225.04,208.70 226.58,200.67" fill="#b67e1c" stroke="#f0b400" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="23" points="211.83,228.44 207.70,197.17 206.16,205.21" fill="#b67e1c" stroke="#f0b400" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="24" points="202.55,165.31 221.84,185.46 219.18,201.77" fill="#e4a024" stroke="#ffd23f" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="25" points="200.56,173.36 215.02,194.55 196.94,193.72" fill="#c48a1e" stroke="#ffc206" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="26" points="185.68,165.97 188.38,197.48 202.46,198.65" fill="#d49422" stroke="#ffc206" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="27" points="200.56,173.36 196.94,193.72 180.25,189.77" fill="#c48a1e" stroke="#ffc206" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="28" points="202.55,165.31 180.12,197.31 182.77,181.00" fill="#e4a024" stroke="#ffd23f" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="29" points="185.68,165.97 172.42,198.88 188.38,197.48" fill="#d49422" stroke="#ffc206" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="30" points="183.66,172.39 185.33,178.78 203.25,188.79" fill="#f0b400" stroke="#ffd23f" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="31" points="211.83,228.44 226.58,200.67 217.90,194.90" fill="#b67e1c" stroke="#f0b400" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="32" points="211.83,228.44 217.90,194.90 207.70,197.17" fill="#b67e1c" stroke="#f0b400" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="33" points="196.09,207.85 196.94,193.72 215.02,194.55" fill="#7a5210" stroke="#b67e1c" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="34" points="196.09,207.85 180.25,189.77 196.94,193.72" fill="#7a5210" stroke="#b67e1c" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="35" points="183.66,172.39 164.99,185.09 185.33,178.78" fill="#f0b400" stroke="#ffd23f" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="36" points="182.14,217.79 180.47,211.40 200.82,205.09" fill="#a87218" stroke="#d49422" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="37" points="187.33,234.14 202.46,198.65 188.38,197.48" fill="#8a5e14" stroke="#b67e1c" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="38" points="182.14,217.79 162.55,201.40 180.47,211.40" fill="#a87218" stroke="#d49422" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="39" points="187.33,234.14 188.38,197.48 172.42,198.88" fill="#8a5e14" stroke="#b67e1c" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="40" points="202.55,165.31 203.63,175.08 221.84,185.46" fill="#e4a024" stroke="#ffd23f" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="41" points="202.55,165.31 182.77,181.00 203.63,175.08" fill="#e4a024" stroke="#ffd23f" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="42" points="200.56,173.36 216.41,191.44 215.02,194.55" fill="#c48a1e" stroke="#ffc206" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="43" points="182.14,217.79 200.82,205.09 203.25,188.79" fill="#a87218" stroke="#d49422" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="44" points="199.40,217.47 198.32,207.69 219.18,201.77" fill="#986816" stroke="#d49422" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="45" points="200.56,173.36 180.25,189.77 181.63,186.66" fill="#c48a1e" stroke="#ffc206" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="46" points="199.40,217.47 180.12,197.31 198.32,207.69" fill="#986816" stroke="#d49422" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="47" points="196.09,207.85 215.02,194.55 216.41,191.44" fill="#7a5210" stroke="#b67e1c" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="48" points="182.14,217.79 164.99,185.09 162.55,201.40" fill="#a87218" stroke="#d49422" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="49" points="196.09,207.85 181.63,186.66 180.25,189.77" fill="#7a5210" stroke="#b67e1c" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="50" points="199.40,217.47 219.18,201.77 221.84,185.46" fill="#986816" stroke="#d49422" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="51" points="182.14,217.79 203.25,188.79 185.33,178.78" fill="#a87218" stroke="#d49422" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="52" points="182.14,217.79 185.33,178.78 164.99,185.09" fill="#a87218" stroke="#d49422" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="53" points="199.40,217.47 182.77,181.00 180.12,197.31" fill="#986816" stroke="#d49422" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="54" points="200.56,173.36 199.71,187.50 216.41,191.44" fill="#c48a1e" stroke="#ffc206" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="55" points="200.56,173.36 181.63,186.66 199.71,187.50" fill="#c48a1e" stroke="#ffc206" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="56" points="196.09,207.85 216.41,191.44 199.71,187.50" fill="#7a5210" stroke="#b67e1c" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="57" points="196.09,207.85 199.71,187.50 181.63,186.66" fill="#7a5210" stroke="#b67e1c" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="58" points="199.40,217.47 221.84,185.46 203.63,175.08" fill="#986816" stroke="#d49422" stroke-width="1.2" stroke-linejoin="round"/> | |
| <polygon data-face="59" points="199.40,217.47 203.63,175.08 182.77,181.00" fill="#986816" stroke="#d49422" stroke-width="1.2" stroke-linejoin="round"/> | |
| </g> | |
| </svg> | |
| <script> | |
| (function() { | |
| var el = document.getElementById('shape-crystal-cluster'); | |
| var g = el.querySelector('.shape-group'); | |
| g.style.transformOrigin = '50% 0%'; | |
| var t = 0, hovering = false; | |
| el.addEventListener('mouseenter', function() { hovering = true; }); | |
| el.addEventListener('mouseleave', function() { hovering = false; }); | |
| (function tick() { | |
| t += 0.04; | |
| if (hovering) { | |
| g.style.transition = 'none'; | |
| g.style.transform = 'rotate(' + (Math.sin(t * 3) * 12 * Math.exp(-(t * 0.3) % 1)) + 'deg)'; | |
| } else { | |
| g.style.transition = 'transform 0.5s ease'; | |
| g.style.transform = 'rotate(' + (Math.sin(t * 0.08333333333333333) * 3) + 'deg)'; | |
| } | |
| requestAnimationFrame(tick); | |
| })(); | |
| })();</script> | |
| <div class="card-info"> | |
| <div class="card-name">Crystal cluster</div> | |
| <div class="card-desc">Elongated crystals growing at odd angles. Swings like a pendulum on hover.</div> | |
| <span class="card-anim">swing</span> | |
| </div> | |
| </div> | |
| <div class="card"> | |
| <svg id="shape-arrow-rotate" viewBox="0 0 400 400" width="400" height="400" class="w-full max-w-[400px] h-auto overflow-visible" style="cursor: pointer;"> | |
| <g class="shape-group" style="transform-origin: 50% 50%;"> | |
| <polygon data-face="0" points="125.79,187.99 190.26,118.75 209.74,140.83 145.26,210.07" fill="#f0b400" stroke="#ffe066" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="1" points="167.70,193.53 125.79,187.99 145.26,210.07 187.17,215.61" fill="#d49422" stroke="#ffd23f" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="2" points="145.26,210.07 187.17,215.61 187.17,285.82 232.30,276.67 232.30,206.47 274.21,183.93 209.74,140.83" fill="#5c3d0a" stroke="#986816" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="3" points="190.26,118.75 254.74,161.85 274.21,183.93 209.74,140.83" fill="#4e3608" stroke="#986816" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="4" points="167.70,263.74 167.70,193.53 187.17,215.61 187.17,285.82" fill="#b67e1c" stroke="#ffc206" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="5" points="254.74,161.85 212.83,184.39 232.30,206.47 274.21,183.93" fill="#5c3d0a" stroke="#b67e1c" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="6" points="190.26,118.75 254.74,161.85 212.83,184.39 212.83,254.59 167.70,263.74 167.70,193.53 125.79,187.99" fill="#f0b400" stroke="#ffc206" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="7" points="212.83,184.39 212.83,254.59 232.30,276.67 232.30,206.47" fill="#7a5210" stroke="#d49422" stroke-width="1.5" stroke-linejoin="round"/> | |
| <polygon data-face="8" points="212.83,254.59 167.70,263.74 187.17,285.82 232.30,276.67" fill="#986816" stroke="#f0b400" stroke-width="1.5" stroke-linejoin="round"/> | |
| </g> | |
| </svg> | |
| <script> | |
| (function() { | |
| var el = document.getElementById('shape-arrow-rotate'); | |
| var g = el.querySelector('.shape-group'); | |
| var angle = 0, hovering = false, hoverAngle = 0; | |
| el.addEventListener('mouseenter', function() { hovering = true; }); | |
| el.addEventListener('mouseleave', function() { hovering = false; }); | |
| el.addEventListener('mousemove', function(e) { | |
| var rect = el.getBoundingClientRect(); | |
| hoverAngle = ((e.clientX - rect.left) / rect.width - 0.5) * 25; | |
| }); | |
| (function tick() { | |
| angle += 0.3; | |
| var tilt = hovering ? hoverAngle : 0; | |
| g.style.transform = 'rotateY(' + (angle + tilt) + 'deg)'; | |
| g.style.transition = hovering ? 'none' : 'transform 0.6s ease-out'; | |
| requestAnimationFrame(tick); | |
| })(); | |
| })();</script> | |
| <div class="card-info"> | |
| <div class="card-name">Arrow rotate</div> | |
| <div class="card-desc">A bold arrow/chevron prism, slowly spinning. Tracks the cursor when hovered.</div> | |
| <span class="card-anim">rotate</span> | |
| </div> | |
| </div> | |
| </div> | |
| </body> | |
| </html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment