Contact
Offices with live local time
City cards that know whether the desk is open
City cards that know whether the desk is open
Build an offices section with Tailwind CSS. A heading, a line inviting people to drop in, then a four-column grid of city cards. Each card carries a data-office attribute and shows the city name with the country under it, a two-line street address, a phone number and an email link, and a footer strip separated by a border holding a live local time in monospaced type on the left and an open/closed pill on the right. Keep the cards light — thin border, rounded-2xl, generous padding — and drop to two columns on sm and one on mobile. Then add JavaScript that stores each office's IANA time zone and opening hours, formats the current time in that zone with Intl.DateTimeFormat, decides whether the desk is open from the zone's own weekday and hour rather than the visitor's, paints the pill green for open and grey for closed with an "Opens 09:00" style hint when shut, and refreshes every thirty seconds so the clocks stay honest. <section class="mx-auto max-w-6xl px-6 py-16">
<div class="max-w-xl">
<h2 class="text-3xl font-semibold text-neutral-900">Come and find us</h2>
<p class="mt-2 text-neutral-500">Four desks, one product. Reception will make you a coffee if you turn up unannounced.</p>
</div>
<div class="mt-10 grid gap-5 sm:grid-cols-2 lg:grid-cols-4">
<div data-office="london" class="rounded-2xl border border-neutral-200 bg-white p-6">
<p class="font-medium text-neutral-900">London</p>
<p class="text-xs text-neutral-400">United Kingdom</p>
<p class="mt-4 text-sm leading-relaxed text-neutral-500">40 Bastwick Street<br />Clerkenwell, EC1V 3PS</p>
<p class="mt-3 text-sm text-neutral-500">+44 20 7946 0112</p>
<a href="#" class="text-sm font-medium text-neutral-900 hover:underline">[email protected]</a>
<div class="mt-5 flex items-center justify-between border-t border-neutral-100 pt-4">
<span data-time class="font-mono text-sm text-neutral-700">—</span>
<span data-status class="rounded-full bg-neutral-100 px-2.5 py-1 text-xs font-medium text-neutral-500">—</span>
</div>
</div>
<div data-office="berlin" class="rounded-2xl border border-neutral-200 bg-white p-6">
<p class="font-medium text-neutral-900">Berlin</p>
<p class="text-xs text-neutral-400">Germany</p>
<p class="mt-4 text-sm leading-relaxed text-neutral-500">Chausseestraße 19<br />Mitte, 10115</p>
<p class="mt-3 text-sm text-neutral-500">+49 30 5679 0044</p>
<a href="#" class="text-sm font-medium text-neutral-900 hover:underline">[email protected]</a>
<div class="mt-5 flex items-center justify-between border-t border-neutral-100 pt-4">
<span data-time class="font-mono text-sm text-neutral-700">—</span>
<span data-status class="rounded-full bg-neutral-100 px-2.5 py-1 text-xs font-medium text-neutral-500">—</span>
</div>
</div>
<div data-office="newyork" class="rounded-2xl border border-neutral-200 bg-white p-6">
<p class="font-medium text-neutral-900">New York</p>
<p class="text-xs text-neutral-400">United States</p>
<p class="mt-4 text-sm leading-relaxed text-neutral-500">115 Broadway, Floor 8<br />New York, NY 10006</p>
<p class="mt-3 text-sm text-neutral-500">+1 212 555 0181</p>
<a href="#" class="text-sm font-medium text-neutral-900 hover:underline">[email protected]</a>
<div class="mt-5 flex items-center justify-between border-t border-neutral-100 pt-4">
<span data-time class="font-mono text-sm text-neutral-700">—</span>
<span data-status class="rounded-full bg-neutral-100 px-2.5 py-1 text-xs font-medium text-neutral-500">—</span>
</div>
</div>
<div data-office="singapore" class="rounded-2xl border border-neutral-200 bg-white p-6">
<p class="font-medium text-neutral-900">Singapore</p>
<p class="text-xs text-neutral-400">Singapore</p>
<p class="mt-4 text-sm leading-relaxed text-neutral-500">9 Straits View<br />Marina One, 018937</p>
<p class="mt-3 text-sm text-neutral-500">+65 6812 0904</p>
<a href="#" class="text-sm font-medium text-neutral-900 hover:underline">[email protected]</a>
<div class="mt-5 flex items-center justify-between border-t border-neutral-100 pt-4">
<span data-time class="font-mono text-sm text-neutral-700">—</span>
<span data-status class="rounded-full bg-neutral-100 px-2.5 py-1 text-xs font-medium text-neutral-500">—</span>
</div>
</div>
</div>
</section> var OFFICES = {
london: { zone: "Europe/London", opens: 9, closes: 18 },
berlin: { zone: "Europe/Berlin", opens: 9, closes: 18 },
newyork: { zone: "America/New_York", opens: 9, closes: 17 },
singapore: { zone: "Asia/Singapore", opens: 10, closes: 19 }
};
var OPEN = "rounded-full bg-green-50 px-2.5 py-1 text-xs font-medium text-green-700";
var CLOSED = "rounded-full bg-neutral-100 px-2.5 py-1 text-xs font-medium text-neutral-500";
function pad(n) {
return n < 10 ? "0" + n : String(n);
}
// Read the wall-clock time in the office's own zone, not the visitor's.
function localParts(zone) {
var parts = new Intl.DateTimeFormat("en-GB", {
timeZone: zone, weekday: "short", hour: "2-digit", minute: "2-digit", hour12: false
}).formatToParts(new Date());
var out = {};
parts.forEach(function (part) { out[part.type] = part.value; });
return { weekday: out.weekday, hour: Number(out.hour) % 24, minute: out.minute };
}
function render() {
Object.keys(OFFICES).forEach(function (id) {
var office = OFFICES[id];
var card = document.querySelector('[data-office="' + id + '"]');
var now = localParts(office.zone);
card.querySelector("[data-time]").textContent = pad(now.hour) + ":" + now.minute;
var weekday = now.weekday !== "Sat" && now.weekday !== "Sun";
var open = weekday && now.hour >= office.opens && now.hour < office.closes;
var pill = card.querySelector("[data-status]");
pill.className = open ? OPEN : CLOSED;
pill.textContent = open ? "Open now" : "Opens " + pad(office.opens) + ":00";
});
}
render();
setInterval(render, 30000);