Updates, inlining, fix for container restarts
This commit is contained in:
BIN
src/assets/android-chrome-192x192.png
Normal file
BIN
src/assets/android-chrome-192x192.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 58 KiB |
BIN
src/assets/android-chrome-512x512.png
Normal file
BIN
src/assets/android-chrome-512x512.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 353 KiB |
BIN
src/assets/apple-touch-icon.png
Normal file
BIN
src/assets/apple-touch-icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 52 KiB |
@@ -1,129 +1,145 @@
|
||||
:root {
|
||||
--bg: #fff;
|
||||
--fg: #222;
|
||||
--accent: #007acc;
|
||||
--bg: #fff;
|
||||
--fg: #222;
|
||||
--accent: #007acc;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
:root {
|
||||
:root {
|
||||
--bg: #111;
|
||||
--fg: #eee;
|
||||
--accent: #46c;
|
||||
}
|
||||
}
|
||||
|
||||
/* Explicit theme toggle overrides (win over media query when set) */
|
||||
html.dark {
|
||||
--bg: #111;
|
||||
--fg: #eee;
|
||||
--accent: #46c;
|
||||
}
|
||||
}
|
||||
/* Explicit theme toggle overrides (win over media query when set) */
|
||||
html.dark {
|
||||
--bg: #111;
|
||||
--fg: #eee;
|
||||
--accent: #46c;
|
||||
}
|
||||
|
||||
html.light {
|
||||
--bg: #fff;
|
||||
--fg: #222;
|
||||
--accent: #007acc;
|
||||
--bg: #fff;
|
||||
--fg: #222;
|
||||
--accent: #007acc;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: sans-serif;
|
||||
background-color: var(--bg);
|
||||
color: var(--fg);
|
||||
margin: 0;
|
||||
font-family: sans-serif;
|
||||
background-color: var(--bg);
|
||||
color: var(--fg);
|
||||
}
|
||||
|
||||
.lightbox-open {
|
||||
overflow: hidden;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.site-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 1rem;
|
||||
background: var(--bg);
|
||||
border-bottom: 1px solid #ccc;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 1rem;
|
||||
background: var(--bg);
|
||||
border-bottom: 1px solid #ccc;
|
||||
}
|
||||
|
||||
.emoji-button {
|
||||
background: none;
|
||||
border: none;
|
||||
font-size: 1.5rem;
|
||||
cursor: pointer;
|
||||
color: var(--accent);
|
||||
background: none;
|
||||
border: none;
|
||||
font-size: 1.5rem;
|
||||
cursor: pointer;
|
||||
color: var(--accent);
|
||||
}
|
||||
|
||||
.gallery-grid {
|
||||
column-count: 1;
|
||||
column-gap: 1rem;
|
||||
padding: 1rem;
|
||||
column-count: 1;
|
||||
column-gap: 1rem;
|
||||
padding: 1rem;
|
||||
}
|
||||
@media (min-width: 768px) {
|
||||
.gallery-grid {
|
||||
column-count: 2;
|
||||
}
|
||||
|
||||
@media (width >= 768px) {
|
||||
.gallery-grid {
|
||||
column-count: 2;
|
||||
}
|
||||
}
|
||||
@media (min-width: 1024px) {
|
||||
.gallery-grid {
|
||||
column-count: 3;
|
||||
}
|
||||
|
||||
@media (width >= 1024px) {
|
||||
.gallery-grid {
|
||||
column-count: 3;
|
||||
}
|
||||
}
|
||||
|
||||
.gallery-item {
|
||||
margin: 0 0 1rem;
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
margin: 0 0 1rem;
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.gallery-item img {
|
||||
width: 100%;
|
||||
display: block;
|
||||
border-radius: 8px;
|
||||
width: 100%;
|
||||
display: block;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.gallery-item figcaption {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
padding: 0.5rem;
|
||||
background: rgba(0, 0, 0, 0.6);
|
||||
color: #fff;
|
||||
font-size: 0.9rem;
|
||||
opacity: 0;
|
||||
transition: opacity 0.2s;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
padding: 0.5rem;
|
||||
background: rgb(0 0 0 / 60%);
|
||||
color: #fff;
|
||||
font-size: 0.9rem;
|
||||
opacity: 0;
|
||||
transition: opacity 0.2s;
|
||||
}
|
||||
|
||||
.gallery-item:focus figcaption,
|
||||
.gallery-item:hover figcaption {
|
||||
opacity: 1;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
#lightbox {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: rgba(0, 0, 0, 0.9);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
visibility: hidden;
|
||||
opacity: 0;
|
||||
transition: opacity 0.3s;
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
background: rgb(0 0 0 / 90%);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
visibility: hidden;
|
||||
opacity: 0;
|
||||
transition: opacity 0.3s;
|
||||
}
|
||||
|
||||
#lightbox[aria-hidden='false'] {
|
||||
visibility: visible;
|
||||
opacity: 1;
|
||||
visibility: visible;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
#lb-content img,
|
||||
#lb-content video {
|
||||
max-width: 90vw;
|
||||
max-height: 80vh;
|
||||
border-radius: 8px;
|
||||
max-width: 90vw;
|
||||
max-height: 80vh;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
#lb-caption {
|
||||
color: #fff;
|
||||
margin-top: 0.5rem;
|
||||
text-align: center;
|
||||
max-width: 90vw;
|
||||
color: #fff;
|
||||
margin-top: 0.5rem;
|
||||
text-align: center;
|
||||
max-width: 90vw;
|
||||
}
|
||||
|
||||
#lb-close {
|
||||
position: absolute;
|
||||
top: 1rem;
|
||||
right: 1rem;
|
||||
background: none;
|
||||
border: none;
|
||||
font-size: 2rem;
|
||||
color: #fff;
|
||||
cursor: pointer;
|
||||
position: absolute;
|
||||
top: 1rem;
|
||||
right: 1rem;
|
||||
background: none;
|
||||
border: none;
|
||||
font-size: 2rem;
|
||||
color: #fff;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
BIN
src/assets/favicon-16x16.png
Normal file
BIN
src/assets/favicon-16x16.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 896 B |
BIN
src/assets/favicon-32x32.png
Normal file
BIN
src/assets/favicon-32x32.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.4 KiB |
BIN
src/assets/favicon.ico
Normal file
BIN
src/assets/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 15 KiB |
11
src/assets/js/ga-init.js
Normal file
11
src/assets/js/ga-init.js
Normal file
@@ -0,0 +1,11 @@
|
||||
;(function () {
|
||||
var script = document.currentScript
|
||||
var id = script && script.getAttribute('data-ga-id')
|
||||
if (!id) return
|
||||
window.dataLayer = window.dataLayer || []
|
||||
function gtag() {
|
||||
window.dataLayer.push(arguments)
|
||||
}
|
||||
gtag('js', new Date())
|
||||
gtag('config', id, { anonymize_ip: true })
|
||||
})()
|
||||
@@ -1,115 +1,115 @@
|
||||
// --- theme toggle ---
|
||||
const toggle = document.getElementById('theme-toggle');
|
||||
const root = document.documentElement;
|
||||
const saved = localStorage.getItem('dark-mode');
|
||||
const sysDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
|
||||
const toggle = document.getElementById('theme-toggle')
|
||||
const root = document.documentElement
|
||||
const saved = localStorage.getItem('dark-mode')
|
||||
const sysDark = window.matchMedia('(prefers-color-scheme: dark)').matches
|
||||
if (saved === 'true') {
|
||||
root.classList.add('dark');
|
||||
root.classList.remove('light');
|
||||
root.classList.add('dark')
|
||||
root.classList.remove('light')
|
||||
} else if (saved === 'false') {
|
||||
root.classList.add('light');
|
||||
root.classList.remove('dark');
|
||||
root.classList.add('light')
|
||||
root.classList.remove('dark')
|
||||
} else {
|
||||
if (sysDark) {
|
||||
root.classList.add('dark');
|
||||
root.classList.remove('light');
|
||||
} else {
|
||||
root.classList.add('light');
|
||||
root.classList.remove('dark');
|
||||
}
|
||||
if (sysDark) {
|
||||
root.classList.add('dark')
|
||||
root.classList.remove('light')
|
||||
} else {
|
||||
root.classList.add('light')
|
||||
root.classList.remove('dark')
|
||||
}
|
||||
}
|
||||
toggle.addEventListener('click', () => {
|
||||
const isDark = root.classList.contains('dark');
|
||||
root.classList.toggle('dark', !isDark);
|
||||
root.classList.toggle('light', isDark);
|
||||
localStorage.setItem('dark-mode', !isDark);
|
||||
});
|
||||
const isDark = root.classList.contains('dark')
|
||||
root.classList.toggle('dark', !isDark)
|
||||
root.classList.toggle('light', isDark)
|
||||
localStorage.setItem('dark-mode', !isDark)
|
||||
})
|
||||
|
||||
const body = document.body;
|
||||
const body = document.body
|
||||
|
||||
// --- lightbox base ---
|
||||
const lb = document.getElementById('lightbox');
|
||||
const lbCnt = document.getElementById('lb-content');
|
||||
const lbCap = document.getElementById('lb-caption');
|
||||
const lb = document.getElementById('lightbox')
|
||||
const lbCnt = document.getElementById('lb-content')
|
||||
const lbCap = document.getElementById('lb-caption')
|
||||
document.getElementById('lb-close').addEventListener('click', () => {
|
||||
lb.setAttribute('aria-hidden', 'true');
|
||||
body.classList.remove('lightbox-open');
|
||||
lbCnt.innerHTML = '';
|
||||
});
|
||||
lb.setAttribute('aria-hidden', 'true')
|
||||
body.classList.remove('lightbox-open')
|
||||
lbCnt.innerHTML = ''
|
||||
})
|
||||
|
||||
// --- build gallery ---
|
||||
const gallery = document.getElementById('gallery');
|
||||
const mediaData = JSON.parse(document.getElementById('media-data').textContent);
|
||||
const gallery = document.getElementById('gallery')
|
||||
const mediaData = JSON.parse(document.getElementById('media-data').textContent)
|
||||
|
||||
const createPicture = (item) => {
|
||||
const pic = document.createElement('picture');
|
||||
['desktop', 'tablet', 'mobile'].forEach((bp) => {
|
||||
const src = document.createElement('source');
|
||||
const widthQuery = bp === 'desktop' ? 1024 : bp === 'tablet' ? 768 : 0;
|
||||
src.media = `(min-width:${widthQuery}px)`;
|
||||
if (item.type === 'image') {
|
||||
src.srcset =
|
||||
`assets/media/${bp}/${item.name}@1x.webp 1x, ` +
|
||||
`assets/media/${bp}/${item.name}.webp 2x`;
|
||||
} else {
|
||||
// video poster still
|
||||
src.srcset =
|
||||
`assets/media/${bp}/${item.name}_still@1x.webp 1x, ` +
|
||||
`assets/media/${bp}/${item.name}_still.webp 2x`;
|
||||
}
|
||||
pic.appendChild(src);
|
||||
});
|
||||
const pic = document.createElement('picture')
|
||||
;['desktop', 'tablet', 'mobile'].forEach((bp) => {
|
||||
const src = document.createElement('source')
|
||||
const widthQuery = bp === 'desktop' ? 1024 : bp === 'tablet' ? 768 : 0
|
||||
src.media = `(min-width:${widthQuery}px)`
|
||||
if (item.type === 'image') {
|
||||
src.srcset =
|
||||
`assets/media/${bp}/${item.name}@1x.webp 1x, ` +
|
||||
`assets/media/${bp}/${item.name}.webp 2x`
|
||||
} else {
|
||||
// video poster still
|
||||
src.srcset =
|
||||
`assets/media/${bp}/${item.name}_still@1x.webp 1x, ` +
|
||||
`assets/media/${bp}/${item.name}_still.webp 2x`
|
||||
}
|
||||
pic.appendChild(src)
|
||||
})
|
||||
|
||||
// thumbnail fallback (always 300px/2×)
|
||||
const img = document.createElement('img');
|
||||
img.src = `assets/media/thumbnail/${item.name}.webp`;
|
||||
img.alt = item.alt.replace(/[‘’]/g, '');
|
||||
pic.appendChild(img);
|
||||
return pic;
|
||||
};
|
||||
// thumbnail fallback (always 300px/2×)
|
||||
const img = document.createElement('img')
|
||||
img.src = `assets/media/thumbnail/${item.name}.webp`
|
||||
img.alt = item.alt.replace(/[‘’]/g, '')
|
||||
pic.appendChild(img)
|
||||
return pic
|
||||
}
|
||||
|
||||
mediaData.forEach((item) => {
|
||||
const fig = document.createElement('figure');
|
||||
fig.className = `gallery-item${item.type === 'video' ? ' video' : ''}`;
|
||||
fig.tabIndex = 0;
|
||||
fig.dataset.name = item.name;
|
||||
fig.dataset.type = item.type;
|
||||
fig.dataset.caption = item.caption;
|
||||
fig.appendChild(createPicture(item));
|
||||
const fig = document.createElement('figure')
|
||||
fig.className = `gallery-item${item.type === 'video' ? ' video' : ''}`
|
||||
fig.tabIndex = 0
|
||||
fig.dataset.name = item.name
|
||||
fig.dataset.type = item.type
|
||||
fig.dataset.caption = item.caption
|
||||
fig.appendChild(createPicture(item))
|
||||
|
||||
// overlay caption
|
||||
const cap = document.createElement('figcaption');
|
||||
cap.textContent = item.caption;
|
||||
fig.appendChild(cap);
|
||||
// overlay caption
|
||||
const cap = document.createElement('figcaption')
|
||||
cap.textContent = item.caption
|
||||
fig.appendChild(cap)
|
||||
|
||||
// events
|
||||
fig.addEventListener('click', () => openLightbox(item));
|
||||
fig.addEventListener(
|
||||
'keypress',
|
||||
(e) => e.key === 'Enter' && openLightbox(item)
|
||||
);
|
||||
// events
|
||||
fig.addEventListener('click', () => openLightbox(item))
|
||||
fig.addEventListener(
|
||||
'keypress',
|
||||
(e) => e.key === 'Enter' && openLightbox(item)
|
||||
)
|
||||
|
||||
gallery.appendChild(fig);
|
||||
});
|
||||
gallery.appendChild(fig)
|
||||
})
|
||||
|
||||
// --- video toggle ---
|
||||
const videoTgl = document.getElementById('show_video');
|
||||
const videoTgl = document.getElementById('show_video')
|
||||
videoTgl.addEventListener('click', () => {
|
||||
openLightbox(mediaData.find((i) => i.type === 'video'));
|
||||
});
|
||||
openLightbox(mediaData.find((i) => i.type === 'video'))
|
||||
})
|
||||
|
||||
function openLightbox(item) {
|
||||
lbCnt.innerHTML = '';
|
||||
if (item.type === 'video') {
|
||||
const v = document.createElement('video');
|
||||
v.src = `assets/media/videos/${item.name}.mp4`;
|
||||
v.controls = true;
|
||||
v.autoplay = true;
|
||||
lbCnt.appendChild(v);
|
||||
} else {
|
||||
lbCnt.appendChild(createPicture(item));
|
||||
}
|
||||
lbCap.textContent = item.caption;
|
||||
body.classList.add('lightbox-open');
|
||||
lb.setAttribute('aria-hidden', 'false');
|
||||
lbCnt.innerHTML = ''
|
||||
if (item.type === 'video') {
|
||||
const v = document.createElement('video')
|
||||
v.src = `assets/media/videos/${item.name}.mp4`
|
||||
v.controls = true
|
||||
v.autoplay = true
|
||||
lbCnt.appendChild(v)
|
||||
} else {
|
||||
lbCnt.appendChild(createPicture(item))
|
||||
}
|
||||
lbCap.textContent = item.caption
|
||||
body.classList.add('lightbox-open')
|
||||
lb.setAttribute('aria-hidden', 'false')
|
||||
}
|
||||
|
||||
365
src/index.html
365
src/index.html
@@ -1,184 +1,195 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
||||
<title>64 Armandine St #3 Boston, Massachusetts</title>
|
||||
<link
|
||||
rel="icon"
|
||||
type="image/png"
|
||||
sizes="32x32"
|
||||
href="assets/favicon-32x32.png"
|
||||
/>
|
||||
<link
|
||||
rel="icon"
|
||||
type="image/png"
|
||||
sizes="16x16"
|
||||
href="assets/favicon-16x16.png"
|
||||
/>
|
||||
<link rel="icon" type="image/x-icon" href="assets/favicon.ico" />
|
||||
<link rel="stylesheet" href="assets/css/style.css" />
|
||||
</head>
|
||||
<body>
|
||||
<header class="site-header">
|
||||
<h1>64 Armandine St #3 Boston, Massachusetts</h1>
|
||||
<div class="buttons">
|
||||
<button
|
||||
id="show_video"
|
||||
class="emoji-button"
|
||||
aria-label="Show video tour"
|
||||
>
|
||||
🎥
|
||||
</button>
|
||||
<button
|
||||
id="theme-toggle"
|
||||
class="emoji-button"
|
||||
aria-label="Toggle light/dark theme"
|
||||
>
|
||||
🌓
|
||||
</button>
|
||||
</div>
|
||||
</header>
|
||||
<head>
|
||||
<!-- Google tag (gtag.js) -->
|
||||
<script
|
||||
async
|
||||
src="https://www.googletagmanager.com/gtag/js?id=G-QZGFK4MDT4"
|
||||
></script>
|
||||
<script
|
||||
defer
|
||||
src="/assets/js/ga-init.js"
|
||||
data-ga-id="G-QZGFK4MDT4"
|
||||
></script>
|
||||
|
||||
<main>
|
||||
<section id="gallery" class="gallery-grid">
|
||||
<!-- gallery items are injected here by script.js -->
|
||||
</section>
|
||||
</main>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
||||
<title>64 Armandine St #3 Boston, Massachusetts</title>
|
||||
<link
|
||||
rel="icon"
|
||||
type="image/png"
|
||||
sizes="32x32"
|
||||
href="/assets/favicon-32x32.png"
|
||||
/>
|
||||
<link
|
||||
rel="icon"
|
||||
type="image/png"
|
||||
sizes="16x16"
|
||||
href="/assets/favicon-16x16.png"
|
||||
/>
|
||||
<link rel="icon" type="image/x-icon" href="/assets/favicon.ico" />
|
||||
<link rel="stylesheet" href="/assets/css/style.css" />
|
||||
</head>
|
||||
<body>
|
||||
<header class="site-header">
|
||||
<h1>64 Armandine St #3 Boston, Massachusetts</h1>
|
||||
<div class="buttons">
|
||||
<button
|
||||
id="show_video"
|
||||
class="emoji-button"
|
||||
aria-label="Show video tour"
|
||||
>
|
||||
🎥
|
||||
</button>
|
||||
<button
|
||||
id="theme-toggle"
|
||||
class="emoji-button"
|
||||
aria-label="Toggle light/dark theme"
|
||||
>
|
||||
🌓
|
||||
</button>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<!-- Lightbox -->
|
||||
<div id="lightbox" aria-hidden="true">
|
||||
<button id="lb-close" aria-label="Close">×</button>
|
||||
<div id="lb-content"></div>
|
||||
<p id="lb-caption"></p>
|
||||
</div>
|
||||
<main>
|
||||
<section id="gallery" class="gallery-grid">
|
||||
<!-- gallery items are injected here by script.js -->
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<!-- Your media manifest: list each file (no extension), type, and caption -->
|
||||
<script id="media-data" type="application/json">
|
||||
[
|
||||
{
|
||||
"type": "image",
|
||||
"name": "living_room_1",
|
||||
"caption": "An inviting blend of comfort and curated art—relaxation guaranteed.",
|
||||
"alt": "Sunny living room with stylish seating and vibrant artwork."
|
||||
},
|
||||
{
|
||||
"type": "image",
|
||||
"name": "living_room_2",
|
||||
"caption": "Relaxation elevated—your stylish living space awaits.",
|
||||
"alt": "Spacious living area featuring elegant furniture and tasteful decor."
|
||||
},
|
||||
{
|
||||
"type": "image",
|
||||
"name": "kitchen",
|
||||
"caption": "The culinary stage is set—snacking encouraged, style required.",
|
||||
"alt": "Modern kitchen showcasing sleek appliances and contemporary design."
|
||||
},
|
||||
{
|
||||
"type": "image",
|
||||
"name": "bedroom_suite_1",
|
||||
"caption": "A bedroom suite designed to make snoozing irresistible.",
|
||||
"alt": "Inviting bedroom suite with cozy bedding and warm lighting."
|
||||
},
|
||||
{
|
||||
"type": "image",
|
||||
"name": "bedroom_suite_2",
|
||||
"caption": "Style meets comfort—sleeping in has never been easier.",
|
||||
"alt": "Comfortable bedroom suite with elegant decor and soft tones."
|
||||
},
|
||||
{
|
||||
"type": "image",
|
||||
"name": "bedroom_suite_3",
|
||||
"caption": "Where dreams get stylish—a bedroom that feels like home.",
|
||||
"alt": "Welcoming bedroom with soothing colors and inviting ambiance."
|
||||
},
|
||||
{
|
||||
"type": "image",
|
||||
"name": "guest_bath",
|
||||
"caption": "Your personal spa experience—right down the hall.",
|
||||
"alt": "Sophisticated guest bathroom with modern fixtures and clean lines."
|
||||
},
|
||||
{
|
||||
"type": "image",
|
||||
"name": "onsuite_1",
|
||||
"caption": "Luxury meets practicality—your private ensuite awaits.",
|
||||
"alt": "Private ensuite bathroom featuring contemporary design and premium finishes."
|
||||
},
|
||||
{
|
||||
"type": "image",
|
||||
"name": "onsuite_2",
|
||||
"caption": "Everyday luxury, right at home—your ensuite oasis.",
|
||||
"alt": "Elegant ensuite with sleek fixtures and stylish decor."
|
||||
},
|
||||
{
|
||||
"type": "image",
|
||||
"name": "laundry",
|
||||
"caption": "Laundry day reimagined—functional never looked so good.",
|
||||
"alt": "Modern laundry room with washer, dryer, and organized storage."
|
||||
},
|
||||
{
|
||||
"type": "image",
|
||||
"name": "coat_closet",
|
||||
"caption": "Organized and chic—your entryway's best friend.",
|
||||
"alt": "Convenient coat closet with tidy storage solutions."
|
||||
},
|
||||
{
|
||||
"type": "image",
|
||||
"name": "deck_1",
|
||||
"caption": "Outdoor comfort, just steps away—morning coffee optional.",
|
||||
"alt": "Sunny deck with cozy seating and pleasant outdoor views."
|
||||
},
|
||||
{
|
||||
"type": "image",
|
||||
"name": "deck_2",
|
||||
"caption": "Your fresh-air escape—ideal for relaxing evenings.",
|
||||
"alt": "Comfortable deck area perfect for unwinding or entertaining."
|
||||
},
|
||||
{
|
||||
"type": "image",
|
||||
"name": "exterior",
|
||||
"caption": "Curb appeal perfected—your new favorite place starts here.",
|
||||
"alt": "Attractive home exterior with inviting architecture."
|
||||
},
|
||||
{
|
||||
"type": "image",
|
||||
"name": "backyard_parking",
|
||||
"caption": "Convenience meets privacy—your personal backyard parking spot.",
|
||||
"alt": "Private backyard parking area offering secure convenience."
|
||||
},
|
||||
{
|
||||
"type": "image",
|
||||
"name": "office_fitness_guest_1",
|
||||
"caption": "Productivity zone meets fitness corner—multitasking done right.",
|
||||
"alt": "Dual-purpose room featuring office setup and fitness equipment."
|
||||
},
|
||||
{
|
||||
"type": "image",
|
||||
"name": "office_fitness_guest_2",
|
||||
"caption": "Work, workout, or unwind—the room of endless possibilities.",
|
||||
"alt": "Versatile office and fitness area with modern amenities."
|
||||
},
|
||||
{
|
||||
"type": "image",
|
||||
"name": "office_fitness_guest_3",
|
||||
"caption": "Stay focused or get fit—you decide.",
|
||||
"alt": "Functional space combining a workspace and home fitness area."
|
||||
},
|
||||
{
|
||||
"type": "image",
|
||||
"name": "office_fitness_guest_4",
|
||||
"caption": "Room for every routine—your workspace meets wellness.",
|
||||
"alt": "Stylish office area seamlessly integrated with fitness features."
|
||||
},
|
||||
{
|
||||
"type": "video",
|
||||
"name": "tour",
|
||||
"caption": "Take the scenic route—explore your the home's highlights with a virtual walkthrough.",
|
||||
"alt": "Video tour showcasing the property."
|
||||
}
|
||||
]
|
||||
</script>
|
||||
<!-- Lightbox -->
|
||||
<div id="lightbox" aria-hidden="true">
|
||||
<button id="lb-close" aria-label="Close">×</button>
|
||||
<div id="lb-content"></div>
|
||||
<p id="lb-caption"></p>
|
||||
</div>
|
||||
|
||||
<script src="assets/js/script.js"></script>
|
||||
</body>
|
||||
<!-- Your media manifest: list each file (no extension), type, and caption -->
|
||||
<script id="media-data" type="application/json">
|
||||
[
|
||||
{
|
||||
"type": "image",
|
||||
"name": "living_room_1",
|
||||
"caption": "An inviting blend of comfort and curated art—relaxation guaranteed.",
|
||||
"alt": "Sunny living room with stylish seating and vibrant artwork."
|
||||
},
|
||||
{
|
||||
"type": "image",
|
||||
"name": "living_room_2",
|
||||
"caption": "Relaxation elevated—your stylish living space awaits.",
|
||||
"alt": "Spacious living area featuring elegant furniture and tasteful decor."
|
||||
},
|
||||
{
|
||||
"type": "image",
|
||||
"name": "kitchen",
|
||||
"caption": "The culinary stage is set—snacking encouraged, style required.",
|
||||
"alt": "Modern kitchen showcasing sleek appliances and contemporary design."
|
||||
},
|
||||
{
|
||||
"type": "image",
|
||||
"name": "bedroom_suite_1",
|
||||
"caption": "A bedroom suite designed to make snoozing irresistible.",
|
||||
"alt": "Inviting bedroom suite with cozy bedding and warm lighting."
|
||||
},
|
||||
{
|
||||
"type": "image",
|
||||
"name": "bedroom_suite_2",
|
||||
"caption": "Style meets comfort—sleeping in has never been easier.",
|
||||
"alt": "Comfortable bedroom suite with elegant decor and soft tones."
|
||||
},
|
||||
{
|
||||
"type": "image",
|
||||
"name": "bedroom_suite_3",
|
||||
"caption": "Where dreams get stylish—a bedroom that feels like home.",
|
||||
"alt": "Welcoming bedroom with soothing colors and inviting ambiance."
|
||||
},
|
||||
{
|
||||
"type": "image",
|
||||
"name": "guest_bath",
|
||||
"caption": "Your personal spa experience—right down the hall.",
|
||||
"alt": "Sophisticated guest bathroom with modern fixtures and clean lines."
|
||||
},
|
||||
{
|
||||
"type": "image",
|
||||
"name": "onsuite_1",
|
||||
"caption": "Luxury meets practicality—your private ensuite awaits.",
|
||||
"alt": "Private ensuite bathroom featuring contemporary design and premium finishes."
|
||||
},
|
||||
{
|
||||
"type": "image",
|
||||
"name": "onsuite_2",
|
||||
"caption": "Everyday luxury, right at home—your ensuite oasis.",
|
||||
"alt": "Elegant ensuite with sleek fixtures and stylish decor."
|
||||
},
|
||||
{
|
||||
"type": "image",
|
||||
"name": "laundry",
|
||||
"caption": "Laundry day reimagined—functional never looked so good.",
|
||||
"alt": "Modern laundry room with washer, dryer, and organized storage."
|
||||
},
|
||||
{
|
||||
"type": "image",
|
||||
"name": "coat_closet",
|
||||
"caption": "Organized and chic—your entryway's best friend.",
|
||||
"alt": "Convenient coat closet with tidy storage solutions."
|
||||
},
|
||||
{
|
||||
"type": "image",
|
||||
"name": "deck_1",
|
||||
"caption": "Outdoor comfort, just steps away—morning coffee optional.",
|
||||
"alt": "Sunny deck with cozy seating and pleasant outdoor views."
|
||||
},
|
||||
{
|
||||
"type": "image",
|
||||
"name": "deck_2",
|
||||
"caption": "Your fresh-air escape—ideal for relaxing evenings.",
|
||||
"alt": "Comfortable deck area perfect for unwinding or entertaining."
|
||||
},
|
||||
{
|
||||
"type": "image",
|
||||
"name": "exterior",
|
||||
"caption": "Curb appeal perfected—your new favorite place starts here.",
|
||||
"alt": "Attractive home exterior with inviting architecture."
|
||||
},
|
||||
{
|
||||
"type": "image",
|
||||
"name": "backyard_parking",
|
||||
"caption": "Convenience meets privacy—your personal backyard parking spot.",
|
||||
"alt": "Private backyard parking area offering secure convenience."
|
||||
},
|
||||
{
|
||||
"type": "image",
|
||||
"name": "office_fitness_guest_1",
|
||||
"caption": "Productivity zone meets fitness corner—multitasking done right.",
|
||||
"alt": "Dual-purpose room featuring office setup and fitness equipment."
|
||||
},
|
||||
{
|
||||
"type": "image",
|
||||
"name": "office_fitness_guest_2",
|
||||
"caption": "Work, workout, or unwind—the room of endless possibilities.",
|
||||
"alt": "Versatile office and fitness area with modern amenities."
|
||||
},
|
||||
{
|
||||
"type": "image",
|
||||
"name": "office_fitness_guest_3",
|
||||
"caption": "Stay focused or get fit—you decide.",
|
||||
"alt": "Functional space combining a workspace and home fitness area."
|
||||
},
|
||||
{
|
||||
"type": "image",
|
||||
"name": "office_fitness_guest_4",
|
||||
"caption": "Room for every routine—your workspace meets wellness.",
|
||||
"alt": "Stylish office area seamlessly integrated with fitness features."
|
||||
},
|
||||
{
|
||||
"type": "video",
|
||||
"name": "tour",
|
||||
"caption": "Take the scenic route—explore your the home's highlights with a virtual walkthrough.",
|
||||
"alt": "Video tour showcasing the property."
|
||||
}
|
||||
]
|
||||
</script>
|
||||
|
||||
<script src="assets/js/script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Reference in New Issue
Block a user