Created
April 26, 2025 01:50
-
-
Save 0xbageltoes/59af82ca9b55cd3773c3e083e2418b6c to your computer and use it in GitHub Desktop.
dkl-first-look-v1
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
| <div class="container" id="flowchart"> | |
| <h1 class="title">FIRST LOOK</h1> | |
| <div class="columns"> | |
| <div class="column" id="private-column"> | |
| <h2 class="section-title">PRIVATE</h2> | |
| <div class="divider"></div> | |
| <p class="content"> | |
| For those who want their property to stay 100% private, our confidential program allows your property to market exclusively within the TTR Sotheby's International Realty brokerage network, without ever hitting the eyes of the public. | |
| </p> | |
| </div> | |
| <div class="right-column"> | |
| <div id="preview-column"> | |
| <h2 class="section-title">COMING SOON PREVIEW</h2> | |
| <div class="divider"></div> | |
| <p class="content"> | |
| Immediately upon signing your listing agreement, we launch your property within our First Look platform - which presents this exclusive preview to the best-in-class TTR Sotheby's International Realty Associates, before hitting the public marketplace. | |
| </p> | |
| </div> | |
| <div class="public-launch" id="public-launch"> | |
| <h2 class="section-title">PUBLIC LAUNCH</h2> | |
| <div class="divider"></div> | |
| <p class="content"> | |
| After it's First Look preview launch, your listing is released to the public marketplace via SothebysRealty.com, and cascading to real estate websites across the globe. | |
| </p> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- SVG container for paths --> | |
| <div class="svg-container" id="svg-container"></div> | |
| <!-- Glowing dot for animation --> | |
| <div class="dot" id="dot"></div> | |
| </div> |
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
| document.addEventListener("DOMContentLoaded", function () { | |
| // Get elements | |
| const flowchart = document.getElementById("flowchart"); | |
| const svgContainer = document.getElementById("svg-container"); | |
| const dot = document.getElementById("dot"); | |
| // Create SVG element | |
| const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg"); | |
| svg.setAttribute("width", "100%"); | |
| svg.setAttribute("height", "100%"); | |
| svgContainer.appendChild(svg); | |
| // Create paths and arrows | |
| const path1 = document.createElementNS("http://www.w3.org/2000/svg", "path"); | |
| path1.id = "path1"; | |
| path1.setAttribute("stroke", "#ccc"); | |
| path1.setAttribute("stroke-width", "1"); | |
| path1.setAttribute("fill", "none"); | |
| const path2 = document.createElementNS("http://www.w3.org/2000/svg", "path"); | |
| path2.id = "path2"; | |
| path2.setAttribute("stroke", "#ccc"); | |
| path2.setAttribute("stroke-width", "1"); | |
| path2.setAttribute("fill", "none"); | |
| const path3 = document.createElementNS("http://www.w3.org/2000/svg", "path"); | |
| path3.id = "path3"; | |
| path3.setAttribute("stroke", "#ccc"); | |
| path3.setAttribute("stroke-width", "1"); | |
| path3.setAttribute("fill", "none"); | |
| const arrow1 = document.createElementNS( | |
| "http://www.w3.org/2000/svg", | |
| "polygon" | |
| ); | |
| arrow1.id = "arrow1"; | |
| arrow1.setAttribute("fill", "#ccc"); | |
| const arrow2 = document.createElementNS( | |
| "http://www.w3.org/2000/svg", | |
| "polygon" | |
| ); | |
| arrow2.id = "arrow2"; | |
| arrow2.setAttribute("fill", "#ccc"); | |
| const arrow3 = document.createElementNS( | |
| "http://www.w3.org/2000/svg", | |
| "polygon" | |
| ); | |
| arrow3.id = "arrow3"; | |
| arrow3.setAttribute("fill", "#ccc"); | |
| svg.appendChild(path1); | |
| svg.appendChild(path2); | |
| svg.appendChild(path3); | |
| svg.appendChild(arrow1); | |
| svg.appendChild(arrow2); | |
| svg.appendChild(arrow3); | |
| // Function to update paths based on current layout | |
| function updatePaths() { | |
| const title = document.querySelector(".title"); | |
| const privateColumn = document.getElementById("private-column"); | |
| const previewColumn = document.getElementById("preview-column"); | |
| const publicLaunch = document.getElementById("public-launch"); | |
| const titleRect = title.getBoundingClientRect(); | |
| const privateRect = privateColumn.getBoundingClientRect(); | |
| const previewRect = previewColumn.getBoundingClientRect(); | |
| const publicRect = publicLaunch.getBoundingClientRect(); | |
| const flowchartRect = flowchart.getBoundingClientRect(); | |
| // Calculate positions relative to the flowchart container | |
| const titleX = titleRect.left + titleRect.width / 2 - flowchartRect.left; | |
| const titleY = titleRect.bottom - flowchartRect.top; | |
| const privateX = | |
| privateRect.left + privateRect.width / 2 - flowchartRect.left; | |
| const privateY = privateRect.top - flowchartRect.top; | |
| const previewX = | |
| previewRect.left + previewRect.width / 2 - flowchartRect.left; | |
| const previewY = previewRect.top - flowchartRect.top; | |
| const publicX = publicRect.left + publicRect.width / 2 - flowchartRect.left; | |
| const publicY = publicRect.top - flowchartRect.top; | |
| // Create paths | |
| const midY = titleY + 30; | |
| path1.setAttribute( | |
| "d", | |
| `M${titleX},${titleY} V${midY} H${privateX} V${privateY - 0}` | |
| ); | |
| path2.setAttribute( | |
| "d", | |
| `M${titleX},${titleY} V${midY} H${previewX} V${previewY - 0}` | |
| ); | |
| path3.setAttribute( | |
| "d", | |
| `M${previewX},${previewRect.bottom - flowchartRect.top} V${publicY - 0}` | |
| ); | |
| // Update arrows | |
| const arrowSize = 8; | |
| arrow1.setAttribute( | |
| "points", | |
| `${privateX - arrowSize},${ | |
| privateY - arrowSize | |
| } ${privateX},${privateY} ${privateX + arrowSize},${privateY - arrowSize}` | |
| ); | |
| arrow2.setAttribute( | |
| "points", | |
| `${previewX - arrowSize},${ | |
| previewY - arrowSize | |
| } ${previewX},${previewY} ${previewX + arrowSize},${previewY - arrowSize}` | |
| ); | |
| arrow3.setAttribute( | |
| "points", | |
| `${publicX - arrowSize},${publicY - arrowSize} ${publicX},${publicY} ${ | |
| publicX + arrowSize | |
| },${publicY - arrowSize}` | |
| ); | |
| } | |
| // Function to animate dot along a path | |
| function animateDotAlongPath(path, onComplete) { | |
| const pathLength = path.getTotalLength(); | |
| let start = null; | |
| const duration = 1500; // Animation duration in ms | |
| dot.style.opacity = "1"; | |
| function animate(timestamp) { | |
| if (!start) start = timestamp; | |
| const progress = (timestamp - start) / duration; | |
| if (progress < 1) { | |
| const point = path.getPointAtLength(progress * pathLength); | |
| dot.style.left = point.x + "px"; | |
| dot.style.top = point.y + "px"; | |
| requestAnimationFrame(animate); | |
| } else { | |
| if (onComplete) onComplete(); | |
| } | |
| } | |
| requestAnimationFrame(animate); | |
| } | |
| // Chain animations | |
| function startAnimation() { | |
| // First path animation | |
| animateDotAlongPath(path1, function () { | |
| // Wait a bit before starting the second path | |
| setTimeout(function () { | |
| // Reset dot position to start of first path | |
| const startPoint = path1.getPointAtLength(0); | |
| dot.style.left = startPoint.x + "px"; | |
| dot.style.top = startPoint.y + "px"; | |
| // Animate along second path | |
| animateDotAlongPath(path2, function () { | |
| // Animate along third path | |
| animateDotAlongPath(path3, function () { | |
| // Animation complete, restart after delay | |
| setTimeout(startAnimation, 2000); | |
| }); | |
| }); | |
| }, 500); | |
| }); | |
| } | |
| // Initial update and start animation | |
| // Wait for layout to stabilize | |
| setTimeout(function () { | |
| updatePaths(); | |
| startAnimation(); | |
| }, 100); | |
| // Update paths on window resize | |
| let resizeTimeout; | |
| window.addEventListener("resize", function () { | |
| // Debounce resize event | |
| clearTimeout(resizeTimeout); | |
| resizeTimeout = setTimeout(function () { | |
| updatePaths(); | |
| }, 250); | |
| }); | |
| }); |
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
| body { | |
| font-family: "Times New Roman", Times, serif; | |
| max-width: 100%; | |
| margin: 0 auto; | |
| padding: 0px; | |
| color: #333; | |
| font-family: "Geist", "Inter"; | |
| box-sizing: border-box; | |
| } | |
| .container { | |
| position: relative; | |
| padding: 0px 50px; | |
| padding-top: 40px; | |
| max-width: 800px; | |
| margin: 0 auto; | |
| overflow: hidden; | |
| } | |
| .title { | |
| text-align: center; | |
| color: #8b2332; | |
| font-size: 24px; | |
| font-weight: normal; | |
| letter-spacing: 1px; | |
| margin-bottom: 30px; | |
| } | |
| .columns { | |
| display: flex; | |
| justify-content: space-between; | |
| margin-top: 80px; | |
| } | |
| .column { | |
| width: 40%; | |
| position: relative; | |
| } | |
| .section-title { | |
| color: #b8860b; | |
| text-align: center; | |
| font-weight: normal; | |
| letter-spacing: 0.5px; | |
| margin-bottom: 10px; | |
| font-size: 18px; | |
| } | |
| .divider { | |
| border-top: 1px dashed #999; | |
| margin: 5px 0 15px; | |
| } | |
| .content { | |
| font-size: 14px; | |
| line-height: 1.5; | |
| color: #555; | |
| } | |
| .right-column { | |
| width: 40%; | |
| display: flex; | |
| flex-direction: column; | |
| } | |
| .public-launch { | |
| margin-top: 60px; | |
| position: relative; | |
| } | |
| .public-launch .section-title { | |
| margin-top: 20px; | |
| } | |
| /* SVG container */ | |
| .svg-container { | |
| position: absolute; | |
| top: 0; | |
| left: 0; | |
| width: 100%; | |
| height: 100%; | |
| pointer-events: none; | |
| } | |
| /* Glowing dot */ | |
| .dot { | |
| position: absolute; | |
| width: 10px; | |
| height: 10px; | |
| background: radial-gradient( | |
| circle, | |
| rgba(255, 215, 0, 0.8) 0%, | |
| rgba(255, 215, 0, 0) 70% | |
| ); | |
| border-radius: 50%; | |
| transform: translate(-50%, -50%); | |
| filter: blur(2px); | |
| box-shadow: 0 0 5px 2px rgba(255, 215, 0, 0.5); | |
| z-index: 10; | |
| opacity: 0; | |
| } | |
| /* Responsive adjustments */ | |
| @media (max-width: 768px) { | |
| .container { | |
| padding: 20px 10px; | |
| } | |
| .title { | |
| font-size: 20px; | |
| margin-bottom: 20px; | |
| } | |
| .columns { | |
| margin-top: 40px; | |
| } | |
| .section-title { | |
| font-size: 16px; | |
| } | |
| .content { | |
| font-size: 12px; | |
| line-height: 1.4; | |
| } | |
| .public-launch { | |
| margin-top: 40px; | |
| } | |
| } | |
| @media (max-width: 576px) { | |
| .title { | |
| font-size: 18px; | |
| } | |
| .section-title { | |
| font-size: 14px; | |
| } | |
| .content { | |
| font-size: 11px; | |
| line-height: 1.3; | |
| } | |
| .public-launch { | |
| margin-top: 30px; | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment