Skip to content

Instantly share code, notes, and snippets.

@0xbageltoes
Created April 26, 2025 01:50
Show Gist options
  • Select an option

  • Save 0xbageltoes/59af82ca9b55cd3773c3e083e2418b6c to your computer and use it in GitHub Desktop.

Select an option

Save 0xbageltoes/59af82ca9b55cd3773c3e083e2418b6c to your computer and use it in GitHub Desktop.
dkl-first-look-v1
<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>
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);
});
});
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