Museum — Plan Your Visit
A calm, curatorial plan-your-visit page for a fictional art museum. It shows today's hours with a live open or closed badge computed in JavaScript, a full weekly hours table, an admission price table with free Thursday evenings, getting-here details for transit, parking and bikes, accessibility notes, an inline SVG site map marking the entrance and metro, and an interactive before-you-go checklist with progress tracking and toast feedback.
MCP
Code
:root {
--paper: #f6f4ef;
--wall: #ffffff;
--charcoal: #1c1b19;
--ink: #2a2825;
--ink-2: #4a4640;
--muted: #8c857a;
--gold: #a98140;
--gold-d: #876631;
--gold-50: #f3ecdd;
--line: rgba(28, 27, 25, 0.12);
--line-2: rgba(28, 27, 25, 0.2);
--ok: #3f7d56;
--warn: #b8842c;
--danger: #b4493a;
--r-sm: 6px;
--r-md: 12px;
--r-lg: 18px;
--serif: "Cormorant Garamond", Georgia, serif;
--sans: "Inter", system-ui, -apple-system, sans-serif;
--shadow: 0 1px 2px rgba(28, 27, 25, 0.04), 0 12px 30px rgba(28, 27, 25, 0.06);
}
* { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }
body {
margin: 0;
font-family: var(--sans);
font-size: 16px;
line-height: 1.55;
color: var(--ink);
background: var(--paper);
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.wrap { width: min(1080px, 92vw); margin-inline: auto; }
.sr-only {
position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}
.skip-link {
position: absolute; left: -999px; top: 0; z-index: 50;
background: var(--charcoal); color: var(--paper);
padding: 10px 16px; border-radius: 0 0 var(--r-sm) 0;
}
.skip-link:focus { left: 0; }
a { color: var(--gold-d); }
:focus-visible {
outline: 2px solid var(--gold-d);
outline-offset: 2px;
border-radius: var(--r-sm);
}
/* ---------- header ---------- */
.site-header {
position: sticky; top: 0; z-index: 30;
background: rgba(246, 244, 239, 0.86);
backdrop-filter: saturate(1.2) blur(8px);
border-bottom: 1px solid var(--line);
}
.header-inner {
display: flex; align-items: center; justify-content: space-between;
gap: 16px; padding: 14px 0;
}
.brand { display: flex; align-items: center; gap: 12px; }
.brand-mark {
display: grid; place-items: center;
width: 36px; height: 36px; border-radius: var(--r-sm);
background: var(--charcoal); color: var(--paper);
font-family: var(--serif); font-weight: 700; font-size: 22px;
}
.brand-name { font-family: var(--serif); font-weight: 600; font-size: 20px; letter-spacing: 0.2px; }
.site-nav { display: flex; gap: 22px; }
.site-nav a {
color: var(--ink-2); text-decoration: none; font-size: 14px; font-weight: 500;
padding: 4px 0; border-bottom: 2px solid transparent; transition: color .15s, border-color .15s;
}
.site-nav a:hover { color: var(--charcoal); border-color: var(--gold); }
/* ---------- hero ---------- */
.hero { padding: 56px 0 28px; max-width: 720px; }
.eyebrow {
margin: 0 0 10px; text-transform: uppercase; letter-spacing: 0.22em;
font-size: 12px; font-weight: 600; color: var(--gold-d);
}
.hero h1 {
font-family: var(--serif); font-weight: 600; font-size: clamp(34px, 6vw, 56px);
line-height: 1.05; margin: 0 0 16px; color: var(--charcoal); letter-spacing: -0.01em;
}
.lede { font-size: 18px; color: var(--ink-2); margin: 0 0 24px; }
.status-banner {
display: inline-flex; align-items: center; gap: 12px; flex-wrap: wrap;
background: var(--wall); border: 1px solid var(--line);
border-radius: var(--r-md); padding: 12px 18px; box-shadow: var(--shadow);
}
.status-dot {
width: 10px; height: 10px; border-radius: 50%; background: var(--muted);
box-shadow: 0 0 0 4px rgba(140, 133, 122, 0.15);
}
.status-banner.open .status-dot { background: var(--ok); box-shadow: 0 0 0 4px rgba(63, 125, 86, 0.18); }
.status-banner.closed .status-dot { background: var(--danger); box-shadow: 0 0 0 4px rgba(180, 73, 58, 0.18); }
.status-text { font-weight: 600; color: var(--charcoal); }
.status-sub { color: var(--muted); font-size: 14px; }
/* ---------- grid ---------- */
.grid {
display: grid; grid-template-columns: repeat(12, 1fr);
gap: 22px; padding: 16px 0 64px;
}
.span-7 { grid-column: span 7; }
.span-5 { grid-column: span 5; }
.card {
background: var(--wall); border: 1px solid var(--line);
border-radius: var(--r-lg); padding: 24px 26px; box-shadow: var(--shadow);
}
.card-head {
display: flex; align-items: center; justify-content: space-between;
gap: 12px; margin-bottom: 16px;
}
.card-head h2 {
font-family: var(--serif); font-weight: 600; font-size: 26px;
margin: 0; color: var(--charcoal);
}
.badge {
display: inline-flex; align-items: center; gap: 6px;
font-size: 12px; font-weight: 600; letter-spacing: 0.02em;
padding: 5px 11px; border-radius: 999px;
background: var(--gold-50); color: var(--gold-d); border: 1px solid rgba(169, 129, 64, 0.25);
}
.badge.gold { background: var(--charcoal); color: var(--gold-50); border-color: transparent; }
.badge.ok { background: rgba(63, 125, 86, 0.12); color: var(--ok); border-color: rgba(63, 125, 86, 0.25); }
/* ---------- tables ---------- */
.table { width: 100%; border-collapse: collapse; font-size: 15px; }
.table th, .table td { text-align: left; padding: 11px 8px; border-bottom: 1px solid var(--line); }
.table thead th {
font-size: 12px; text-transform: uppercase; letter-spacing: 0.08em;
color: var(--muted); font-weight: 600; border-bottom-color: var(--line-2);
}
.table tbody th { font-weight: 600; color: var(--ink); }
.table .num { text-align: right; }
.table tbody tr:last-child th, .table tbody tr:last-child td { border-bottom: none; }
.table tr.today { background: var(--gold-50); }
.table tr.today th, .table tr.today td { color: var(--charcoal); }
.table tr.hl { background: rgba(63, 125, 86, 0.06); }
.day-status {
font-size: 12px; font-weight: 600; padding: 3px 9px; border-radius: 999px;
background: rgba(63, 125, 86, 0.12); color: var(--ok);
}
.day-status.closed { background: rgba(180, 73, 58, 0.1); color: var(--danger); }
.price { font-variant-numeric: tabular-nums; font-weight: 600; color: var(--charcoal); }
.price.free { color: var(--ok); }
.note { font-size: 13px; color: var(--muted); margin: 14px 0 0; }
/* ---------- checklist ---------- */
.checklist { list-style: none; margin: 0 0 16px; padding: 0; display: grid; gap: 6px; }
.checklist label {
display: flex; align-items: flex-start; gap: 12px;
padding: 11px 12px; border: 1px solid var(--line); border-radius: var(--r-md);
cursor: pointer; transition: background .15s, border-color .15s;
font-size: 15px;
}
.checklist label:hover { background: var(--paper); border-color: var(--line-2); }
.checklist input { margin-top: 3px; width: 18px; height: 18px; accent-color: var(--gold-d); flex: none; }
.checklist input:checked + span { color: var(--muted); text-decoration: line-through; }
/* ---------- info lists ---------- */
.info-list { list-style: none; margin: 0; padding: 0; display: grid; gap: 16px; }
.info-list.tight { gap: 10px; }
.info-list li { display: flex; gap: 14px; }
.info-list .ico {
flex: none; width: 30px; height: 30px; border-radius: var(--r-sm);
display: grid; place-items: center; font-size: 11px;
background: var(--gold-50); color: var(--gold-d);
}
.info-list.tight .ico { width: 22px; height: 22px; font-size: 8px; margin-top: 3px; }
.info-list strong { display: block; color: var(--charcoal); font-size: 15px; }
.info-list p { margin: 2px 0 0; font-size: 14px; color: var(--ink-2); }
/* ---------- map ---------- */
.map-figure { margin: 0; }
.site-map {
width: 100%; height: auto; display: block;
border: 1px solid var(--line-2); border-radius: var(--r-md);
background: #efe9dd;
}
.map-label { font-family: var(--sans); font-size: 13px; fill: var(--ink-2); font-weight: 500; }
.map-bld { font-family: var(--serif); font-size: 17px; fill: var(--paper); font-weight: 600; }
.street-label { font-family: var(--sans); font-size: 11px; fill: var(--muted); letter-spacing: 0.06em; }
.street-dash { stroke: var(--line-2); stroke-width: 2; stroke-dasharray: 8 8; }
.pin-label { font-family: var(--sans); font-size: 11px; fill: var(--charcoal); font-weight: 600; }
.map-t { font-family: var(--serif); font-size: 16px; fill: var(--paper); font-weight: 700; }
/* ---------- buttons ---------- */
.btn {
appearance: none; border: 1px solid var(--charcoal); background: var(--charcoal);
color: var(--paper); font-family: var(--sans); font-size: 14px; font-weight: 600;
padding: 11px 18px; border-radius: var(--r-md); cursor: pointer;
transition: transform .12s, background .15s, box-shadow .15s;
}
.btn:hover { background: #000; box-shadow: var(--shadow); }
.btn:active { transform: translateY(1px); }
.btn.ghost { background: transparent; color: var(--ink); border-color: var(--line-2); }
.btn.ghost:hover { background: var(--paper); box-shadow: none; }
/* ---------- footer ---------- */
.site-footer { border-top: 1px solid var(--line); background: var(--wall); }
.footer-inner { padding: 28px 0; display: flex; justify-content: space-between; flex-wrap: wrap; gap: 8px; }
.footer-inner p { margin: 0; font-size: 14px; color: var(--ink-2); }
.muted { color: var(--muted); }
/* ---------- toast ---------- */
.toast-host {
position: fixed; right: 20px; bottom: 20px; z-index: 60;
display: grid; gap: 10px; max-width: 320px;
}
.toast {
background: var(--charcoal); color: var(--paper);
padding: 13px 16px; border-radius: var(--r-md); font-size: 14px;
box-shadow: 0 12px 30px rgba(28, 27, 25, 0.25);
border-left: 3px solid var(--gold);
opacity: 0; transform: translateY(8px); transition: opacity .25s, transform .25s;
}
.toast.show { opacity: 1; transform: translateY(0); }
/* ---------- responsive ---------- */
@media (max-width: 860px) {
.span-7, .span-5 { grid-column: span 12; }
.site-nav { display: none; }
}
@media (max-width: 520px) {
body { font-size: 15px; }
.hero { padding: 40px 0 20px; }
.lede { font-size: 16px; }
.card { padding: 20px 18px; }
.card-head h2 { font-size: 22px; }
.grid { gap: 16px; }
.header-inner { padding: 12px 0; }
.brand-name { font-size: 17px; }
.status-banner { width: 100%; }
.table { font-size: 14px; }
.table th, .table td { padding: 9px 5px; }
.toast-host { left: 16px; right: 16px; max-width: none; }
}(function () {
"use strict";
// Weekly schedule. open/close in 24h decimal hours; null = closed.
// Order matches getDay() (0 = Sunday).
var SCHEDULE = [
{ day: "Sunday", open: 10, close: 17 },
{ day: "Monday", open: null, close: null },
{ day: "Tuesday", open: 10, close: 17 },
{ day: "Wednesday", open: 10, close: 17 },
{ day: "Thursday", open: 10, close: 20 },
{ day: "Friday", open: 10, close: 17 },
{ day: "Saturday", open: 10, close: 18 }
];
function fmt(h) {
var hour = Math.floor(h);
var min = Math.round((h - hour) * 60);
var ampm = hour >= 12 ? "pm" : "am";
var h12 = hour % 12; if (h12 === 0) h12 = 12;
var mm = min === 0 ? "" : ":" + (min < 10 ? "0" + min : min);
return h12 + mm + " " + ampm;
}
function rangeText(entry) {
if (entry.open === null) return "Closed";
return fmt(entry.open) + " – " + fmt(entry.close);
}
// ----- compute current status -----
function getStatus(now) {
var d = now.getDay();
var nowH = now.getHours() + now.getMinutes() / 60;
var today = SCHEDULE[d];
if (today.open !== null && nowH >= today.open && nowH < today.close) {
return { open: true, today: today, msg: "Open now", sub: "Today " + rangeText(today) + " · closes " + fmt(today.close) };
}
// Opening later today?
if (today.open !== null && nowH < today.open) {
return { open: false, today: today, msg: "Closed now", sub: "Opens today at " + fmt(today.open) };
}
// Find next open day (search up to 7 days ahead)
for (var i = 1; i <= 7; i++) {
var next = SCHEDULE[(d + i) % 7];
if (next.open !== null) {
var label = i === 1 ? "tomorrow" : next.day;
return { open: false, today: today, msg: "Closed now", sub: "Opens " + label + " at " + fmt(next.open) };
}
}
return { open: false, today: today, msg: "Closed", sub: "" };
}
// ----- render hours table -----
function renderHours(now) {
var body = document.getElementById("hoursBody");
if (!body) return;
var todayIdx = now.getDay();
var status = getStatus(now);
body.innerHTML = "";
SCHEDULE.forEach(function (entry, idx) {
var tr = document.createElement("tr");
if (idx === todayIdx) tr.className = "today";
var th = document.createElement("th");
th.scope = "row";
th.textContent = entry.day + (idx === todayIdx ? " (today)" : "");
tr.appendChild(th);
var td = document.createElement("td");
td.textContent = rangeText(entry);
tr.appendChild(td);
var tdStatus = document.createElement("td");
tdStatus.className = "num";
var span = document.createElement("span");
var isClosed = entry.open === null;
// for today, reflect live open/closed; otherwise just "Open"/"Closed" schedule
var openLabel = "Open";
var closedLabel = "Closed";
if (idx === todayIdx && !isClosed) {
span.textContent = status.open ? "Open now" : "Closed now";
span.className = "day-status" + (status.open ? "" : " closed");
} else {
span.textContent = isClosed ? closedLabel : openLabel;
span.className = "day-status" + (isClosed ? " closed" : "");
}
tdStatus.appendChild(span);
tr.appendChild(tdStatus);
body.appendChild(tr);
});
}
// ----- banner + badge -----
function renderBanner(now) {
var banner = document.getElementById("statusBanner");
var text = document.getElementById("statusText");
var sub = document.getElementById("statusSub");
var badge = document.getElementById("hoursBadge");
if (!banner) return;
var status = getStatus(now);
banner.classList.remove("open", "closed");
banner.classList.add(status.open ? "open" : "closed");
text.textContent = status.msg;
sub.textContent = status.sub;
if (badge) badge.textContent = SCHEDULE[now.getDay()].day;
}
// ----- checklist -----
function setupChecklist() {
var boxes = Array.prototype.slice.call(document.querySelectorAll("[data-check]"));
var badge = document.getElementById("checkBadge");
var reset = document.getElementById("resetCheck");
if (!boxes.length) return;
function update() {
var done = boxes.filter(function (b) { return b.checked; }).length;
if (badge) badge.textContent = done + " / " + boxes.length;
if (badge) badge.classList.toggle("ok", done === boxes.length);
if (done === boxes.length) toast("All set — enjoy your visit!");
}
boxes.forEach(function (b) { b.addEventListener("change", update); });
if (reset) {
reset.addEventListener("click", function () {
boxes.forEach(function (b) { b.checked = false; });
update();
toast("Checklist reset");
});
}
update();
}
// ----- toast helper -----
var toastTimer = null;
function toast(msg) {
var host = document.getElementById("toastHost");
if (!host) return;
var el = document.createElement("div");
el.className = "toast";
el.textContent = msg;
host.appendChild(el);
requestAnimationFrame(function () { el.classList.add("show"); });
setTimeout(function () {
el.classList.remove("show");
setTimeout(function () { if (el.parentNode) el.parentNode.removeChild(el); }, 280);
}, 2600);
}
// ----- access button -----
function setupAccess() {
var btn = document.getElementById("accessBtn");
if (btn) {
btn.addEventListener("click", function () {
toast("Visit assistant requested — our team will confirm by email.");
});
}
}
// ----- smooth scroll for in-page nav -----
function setupNav() {
document.querySelectorAll('.site-nav a[href^="#"]').forEach(function (a) {
a.addEventListener("click", function (e) {
var target = document.querySelector(a.getAttribute("href"));
if (target) {
e.preventDefault();
target.scrollIntoView({ behavior: "smooth", block: "start" });
}
});
});
}
function init() {
var now = new Date();
renderHours(now);
renderBanner(now);
setupChecklist();
setupAccess();
setupNav();
// Refresh status every minute so it stays accurate during a long session.
setInterval(function () {
var n = new Date();
renderHours(n);
renderBanner(n);
}, 60000);
}
if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", init);
} else {
init();
}
})();<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Meridian Museum of Art — Plan Your Visit</title>
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link href="https://fonts.googleapis.com/css2?family=Cormorant+Garamond:wght@500;600;700&family=Inter:wght@400;500;600&display=swap" rel="stylesheet" />
<link rel="stylesheet" href="style.css" />
</head>
<body>
<a class="skip-link" href="#main">Skip to content</a>
<header class="site-header">
<div class="wrap header-inner">
<div class="brand">
<span class="brand-mark" aria-hidden="true">M</span>
<span class="brand-name">Meridian Museum of Art</span>
</div>
<nav class="site-nav" aria-label="Primary">
<a href="#hours">Hours</a>
<a href="#admission">Admission</a>
<a href="#getting-here">Getting Here</a>
<a href="#map">Map</a>
</nav>
</div>
</header>
<main id="main" class="wrap">
<section class="hero">
<p class="eyebrow">Plan Your Visit</p>
<h1>A calm afternoon among the collection.</h1>
<p class="lede">
Two hundred years of painting, sculpture, and works on paper across four naturally lit
galleries on the Eastside Cultural Mile. Free for members and children under twelve.
</p>
<div class="status-banner" id="statusBanner" role="status" aria-live="polite">
<span class="status-dot" id="statusDot" aria-hidden="true"></span>
<span class="status-text" id="statusText">Checking today’s hours…</span>
<span class="status-sub" id="statusSub"></span>
</div>
</section>
<div class="grid">
<!-- HOURS -->
<section id="hours" class="card span-7" aria-labelledby="hours-h">
<header class="card-head">
<h2 id="hours-h">Opening Hours</h2>
<span class="badge" id="hoursBadge">Today</span>
</header>
<table class="table hours-table">
<caption class="sr-only">Weekly opening hours</caption>
<thead>
<tr><th scope="col">Day</th><th scope="col">Hours</th><th scope="col" class="num">Status</th></tr>
</thead>
<tbody id="hoursBody"><!-- rows injected by script.js --></tbody>
</table>
<p class="note">Last admission 30 minutes before closing. Galleries closed Jan 1 and Dec 25.</p>
</section>
<!-- BEFORE YOU GO CHECKLIST -->
<section class="card span-5" aria-labelledby="checklist-h">
<header class="card-head">
<h2 id="checklist-h">Before You Go</h2>
<span class="badge" id="checkBadge">0 / 5</span>
</header>
<ul class="checklist" id="checklist">
<li><label><input type="checkbox" data-check /> <span>Book a timed entry for special exhibitions</span></label></li>
<li><label><input type="checkbox" data-check /> <span>Bring a valid member card or ID</span></label></li>
<li><label><input type="checkbox" data-check /> <span>Coats & large bags go to the cloakroom</span></label></li>
<li><label><input type="checkbox" data-check /> <span>Download the audio-guide app on Wi-Fi</span></label></li>
<li><label><input type="checkbox" data-check /> <span>Check accessibility services if needed</span></label></li>
</ul>
<button type="button" class="btn ghost" id="resetCheck">Reset checklist</button>
</section>
<!-- ADMISSION -->
<section id="admission" class="card span-7" aria-labelledby="admission-h">
<header class="card-head">
<h2 id="admission-h">Admission</h2>
<span class="badge gold">Members free</span>
</header>
<table class="table price-table">
<caption class="sr-only">Admission prices</caption>
<thead>
<tr><th scope="col">Ticket</th><th scope="col">Includes</th><th scope="col" class="num">Price</th></tr>
</thead>
<tbody>
<tr><th scope="row">General</th><td>Permanent collection</td><td class="num price">$18</td></tr>
<tr><th scope="row">Senior (65+)</th><td>Permanent collection</td><td class="num price">$14</td></tr>
<tr><th scope="row">Student</th><td>With valid ID</td><td class="num price">$10</td></tr>
<tr><th scope="row">Child (under 12)</th><td>Accompanied by adult</td><td class="num price free">Free</td></tr>
<tr><th scope="row">Special exhibition</th><td>Timed entry add-on</td><td class="num price">+$8</td></tr>
<tr class="hl"><th scope="row">Thursday evenings</th><td>5–8 pm, all galleries</td><td class="num price free">Free</td></tr>
</tbody>
</table>
<p class="note">Members enjoy unlimited free entry and 10% off in the shop & café.</p>
</section>
<!-- GETTING HERE -->
<section id="getting-here" class="card span-5" aria-labelledby="getting-h">
<header class="card-head">
<h2 id="getting-h">Getting Here</h2>
</header>
<ul class="info-list">
<li>
<span class="ico" aria-hidden="true">▲</span>
<div><strong>By transit</strong><p>Metro Blue & Green lines to <em>Cultural Mile</em>, then a 3-minute walk north. Bus 12, 40, 88 stop at the door.</p></div>
</li>
<li>
<span class="ico" aria-hidden="true">■</span>
<div><strong>Parking</strong><p>Underground garage on Linden St., $6 flat with validation. 18 accessible bays on level P1.</p></div>
</li>
<li>
<span class="ico" aria-hidden="true">●</span>
<div><strong>By bike</strong><p>Sheltered racks at the West entrance; city bike-share dock across the plaza.</p></div>
</li>
</ul>
</section>
<!-- MAP -->
<section id="map" class="card span-7" aria-labelledby="map-h">
<header class="card-head">
<h2 id="map-h">Site Map</h2>
<span class="badge">Eastside Cultural Mile</span>
</header>
<figure class="map-figure">
<svg viewBox="0 0 600 360" class="site-map" role="img" aria-label="Map of the museum and surrounding streets. The museum sits on Linden Street between the plaza and the transit stop.">
<rect x="0" y="0" width="600" height="360" fill="#efe9dd"/>
<!-- park -->
<rect x="20" y="30" width="150" height="120" rx="8" fill="#dfe7da"/>
<text x="95" y="95" class="map-label" text-anchor="middle">Sculpture Park</text>
<!-- streets -->
<rect x="0" y="170" width="600" height="34" fill="#f6f4ef"/>
<rect x="240" y="0" width="34" height="360" fill="#f6f4ef"/>
<line x1="0" y1="187" x2="600" y2="187" class="street-dash"/>
<line x1="257" y1="0" x2="257" y2="360" class="street-dash"/>
<text x="40" y="160" class="street-label">Cedar Ave</text>
<text x="285" y="350" class="street-label" transform="rotate(0 285 350)">Linden St</text>
<!-- plaza -->
<rect x="300" y="220" width="130" height="110" rx="8" fill="#eee6d4"/>
<text x="365" y="278" class="map-label" text-anchor="middle">Plaza</text>
<!-- museum building -->
<rect x="300" y="40" width="260" height="110" rx="10" fill="#a98140"/>
<rect x="312" y="52" width="236" height="86" rx="6" fill="#bf9a5c"/>
<text x="430" y="100" class="map-bld" text-anchor="middle">Meridian Museum</text>
<!-- entrance marker -->
<circle cx="345" cy="170" r="11" fill="#1c1b19"/>
<circle cx="345" cy="170" r="5" fill="#f6f4ef"/>
<text x="345" y="155" class="pin-label" text-anchor="middle">Main entrance</text>
<!-- transit marker -->
<rect x="455" y="172" width="30" height="30" rx="5" fill="#3f7d56"/>
<text x="470" y="192" class="map-t" text-anchor="middle">M</text>
<text x="470" y="228" class="pin-label" text-anchor="middle">Metro</text>
</svg>
<figcaption class="note">Main entrance on Linden St. Metro and bus stops are a short walk east across the plaza.</figcaption>
</figure>
</section>
<!-- ACCESSIBILITY -->
<section class="card span-5" aria-labelledby="access-h">
<header class="card-head">
<h2 id="access-h">Accessibility</h2>
<span class="badge ok">Step-free</span>
</header>
<ul class="info-list tight">
<li><span class="ico" aria-hidden="true">●</span><div><p>Step-free access throughout; lifts to every gallery level.</p></div></li>
<li><span class="ico" aria-hidden="true">●</span><div><p>Wheelchairs & folding stools available free at coat check.</p></div></li>
<li><span class="ico" aria-hidden="true">●</span><div><p>Large-print & Braille labels; audio guide with described tours.</p></div></li>
<li><span class="ico" aria-hidden="true">●</span><div><p>Quiet hour first Sunday of each month, 9–10 am.</p></div></li>
<li><span class="ico" aria-hidden="true">●</span><div><p>Assistance animals welcome. Accessible restrooms on every floor.</p></div></li>
</ul>
<button type="button" class="btn" id="accessBtn">Request a visit assistant</button>
</section>
</div>
</main>
<footer class="site-footer">
<div class="wrap footer-inner">
<p>Meridian Museum of Art · 47 Linden Street, Eastside Cultural Mile</p>
<p class="muted">Illustrative demo — not a real institution.</p>
</div>
</footer>
<div class="toast-host" id="toastHost" aria-live="polite" aria-atomic="true"></div>
<script src="script.js"></script>
</body>
</html>Plan Your Visit
A self-contained plan-your-visit page for the fictional Meridian Museum of Art. The layout favors generous wall space and a refined serif-and-sans pairing to evoke a real cultural institution. A status banner at the top reads the current day and time and tells visitors whether the galleries are Open now or Closed, with a follow-up line such as “closes 5 pm” or “opens tomorrow at 10 am”.
The weekly hours table highlights today’s row and mirrors the live status, while the admission table lays out general, senior, student, and child pricing alongside the free Thursday-evening window. Getting-here, accessibility, and an inline SVG site map (marking the main entrance, plaza, and metro stop) round out the informational content — no external images or scripts are used.
The page is interactive: the before-you-go checklist tracks progress in a badge, fires a celebratory toast when everything is ticked, and can be reset; the “request a visit assistant” button and in-page navigation also give immediate feedback. Everything is keyboard-usable with visible focus, meets AA contrast, and reflows cleanly down to 360px.
Illustrative UI only — demo data; not a real museum system.