Dental — Emergency Info
A reassuring dental emergency card with a one-tap urgent call button, live open-now status, weekly hours, an expandable what-to-do first-aid accordion for knocked-out teeth, swelling and lost fillings, plus a gradient map placeholder with directions — trust-first clinical styling built in vanilla HTML, CSS and JavaScript.
MCP
Code
:root {
--blue: #2563eb;
--blue-d: #1d4ed8;
--blue-50: #eff5ff;
--mint: #34d399;
--mint-50: #e8fbf3;
--ink: #0f2740;
--ink-2: #3a5169;
--muted: #6b7c90;
--bg: #f3f8ff;
--white: #fff;
--line: rgba(15, 39, 64, 0.1);
--line-2: rgba(15, 39, 64, 0.18);
--ok: #22b07d;
--warn: #d98a2b;
--danger: #e05252;
--r-sm: 8px;
--r-md: 14px;
--r-lg: 22px;
--sh-sm: 0 1px 2px rgba(15, 39, 64, 0.06);
--sh-md: 0 10px 30px rgba(15, 39, 64, 0.08);
--sh-lg: 0 24px 60px rgba(15, 39, 64, 0.12);
}
* { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }
body {
margin: 0;
font-family: "Inter", system-ui, -apple-system, sans-serif;
line-height: 1.5;
color: var(--ink);
background:
radial-gradient(1100px 520px at 90% -10%, #e6f0ff 0%, transparent 60%),
radial-gradient(900px 500px at -10% 110%, var(--mint-50) 0%, transparent 55%),
var(--bg);
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
min-height: 100vh;
display: grid;
place-items: center;
padding: 28px 16px;
}
.wrap { width: 100%; max-width: 560px; }
/* ---------- card ---------- */
.card {
background: var(--white);
border: 1px solid var(--line);
border-radius: var(--r-lg);
box-shadow: var(--sh-lg);
overflow: hidden;
}
/* ---------- banner ---------- */
.banner {
display: flex;
align-items: center;
gap: 14px;
padding: 20px 22px;
color: var(--white);
background:
linear-gradient(135deg, #e8534f 0%, var(--danger) 45%, #c53d3d 100%);
position: relative;
}
.banner::after {
content: "";
position: absolute;
inset: 0;
background: radial-gradient(360px 140px at 82% 0%, rgba(255, 255, 255, 0.22), transparent 70%);
pointer-events: none;
}
.banner__glyph, .banner__glyph svg { color: #fff; }
.banner__glyph {
flex: 0 0 auto;
width: 46px;
height: 46px;
display: grid;
place-items: center;
border-radius: 14px;
background: rgba(255, 255, 255, 0.16);
border: 1px solid rgba(255, 255, 255, 0.28);
}
.banner__text { flex: 1 1 auto; min-width: 0; }
.banner__kicker {
margin: 0 0 2px;
font-size: 11px;
font-weight: 700;
letter-spacing: 0.08em;
text-transform: uppercase;
opacity: 0.92;
}
.banner__title {
margin: 0;
font-size: 19px;
font-weight: 800;
letter-spacing: -0.01em;
line-height: 1.2;
}
.banner__call {
flex: 0 0 auto;
display: inline-flex;
align-items: center;
gap: 8px;
padding: 11px 16px;
border-radius: 999px;
background: var(--white);
color: var(--danger);
font-weight: 700;
font-size: 14px;
text-decoration: none;
box-shadow: 0 6px 16px rgba(0, 0, 0, 0.18);
transition: transform 0.15s ease, box-shadow 0.15s ease;
position: relative;
z-index: 1;
animation: pulse 2.4s ease-in-out infinite;
}
.banner__call:hover { transform: translateY(-1px); box-shadow: 0 10px 22px rgba(0, 0, 0, 0.22); }
.banner__call:active { transform: translateY(0) scale(0.98); }
@keyframes pulse {
0%, 100% { box-shadow: 0 6px 16px rgba(0, 0, 0, 0.18), 0 0 0 0 rgba(255, 255, 255, 0.5); }
50% { box-shadow: 0 6px 16px rgba(0, 0, 0, 0.18), 0 0 0 8px rgba(255, 255, 255, 0); }
}
/* ---------- facts ---------- */
.facts { padding: 18px 22px 4px; }
.status {
display: flex;
align-items: center;
gap: 12px;
flex-wrap: wrap;
margin-bottom: 16px;
}
.pill {
display: inline-flex;
align-items: center;
gap: 8px;
padding: 6px 12px 6px 10px;
border-radius: 999px;
font-size: 13px;
font-weight: 700;
border: 1px solid var(--line);
background: var(--blue-50);
color: var(--ink-2);
}
.pill .dot {
width: 9px;
height: 9px;
border-radius: 50%;
background: var(--muted);
box-shadow: 0 0 0 3px rgba(107, 124, 144, 0.18);
}
.pill.is-open { background: var(--mint-50); color: #0d7a55; border-color: rgba(34, 176, 125, 0.35); }
.pill.is-open .dot { background: var(--ok); box-shadow: 0 0 0 3px rgba(34, 176, 125, 0.2); animation: blink 1.6s ease-in-out infinite; }
.pill.is-closed { background: #fdecec; color: #b93a3a; border-color: rgba(224, 82, 82, 0.3); }
.pill.is-closed .dot { background: var(--danger); box-shadow: 0 0 0 3px rgba(224, 82, 82, 0.18); }
@keyframes blink { 50% { opacity: 0.45; } }
.status__sub { font-size: 13px; color: var(--muted); }
.facts__grid {
list-style: none;
margin: 0;
padding: 0;
display: grid;
gap: 10px;
}
.fact {
display: flex;
align-items: center;
gap: 12px;
padding: 12px 14px;
border: 1px solid var(--line);
border-radius: var(--r-md);
background: linear-gradient(180deg, #fbfdff, var(--white));
transition: border-color 0.15s ease, transform 0.15s ease, box-shadow 0.15s ease;
}
.fact:hover { border-color: var(--line-2); transform: translateY(-1px); box-shadow: var(--sh-sm); }
.fact__ico {
flex: 0 0 auto;
width: 34px;
height: 34px;
display: grid;
place-items: center;
border-radius: 10px;
color: var(--blue);
background: var(--blue-50);
}
.fact__body { flex: 1 1 auto; min-width: 0; }
.fact__label {
margin: 0;
font-size: 11px;
font-weight: 600;
letter-spacing: 0.04em;
text-transform: uppercase;
color: var(--muted);
}
.fact__value {
margin: 1px 0 0;
font-size: 15px;
font-weight: 700;
color: var(--ink);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.fact__copy {
flex: 0 0 auto;
font: inherit;
font-size: 12px;
font-weight: 700;
color: var(--blue);
background: var(--white);
border: 1px solid var(--line-2);
border-radius: 999px;
padding: 6px 12px;
cursor: pointer;
transition: background 0.15s ease, color 0.15s ease, transform 0.1s ease;
}
.fact__copy:hover { background: var(--blue); color: #fff; border-color: var(--blue); }
.fact__copy:active { transform: scale(0.95); }
.fact__tag {
flex: 0 0 auto;
font-size: 11px;
font-weight: 700;
color: var(--ink-2);
background: #eef3f9;
border-radius: 999px;
padding: 4px 10px;
}
.fact__tag--ok { color: #0d7a55; background: var(--mint-50); }
/* ---------- weekly hours ---------- */
.hours {
display: grid;
grid-template-columns: repeat(7, 1fr);
gap: 6px;
padding: 16px 22px 4px;
}
.hcell {
text-align: center;
border: 1px solid var(--line);
border-radius: 10px;
padding: 8px 4px;
background: #fbfdff;
}
.hcell__day {
font-size: 11px;
font-weight: 700;
letter-spacing: 0.03em;
text-transform: uppercase;
color: var(--muted);
}
.hcell__val { font-size: 11px; font-weight: 600; color: var(--ink-2); margin-top: 2px; }
.hcell.is-closed .hcell__val { color: var(--danger); }
.hcell.is-today {
border-color: var(--blue);
background: var(--blue-50);
box-shadow: inset 0 0 0 1px var(--blue);
}
.hcell.is-today .hcell__day { color: var(--blue-d); }
/* ---------- accordion ---------- */
.steps { padding: 18px 22px 6px; }
.steps__title {
display: flex;
align-items: center;
gap: 8px;
margin: 0 0 12px;
font-size: 15px;
font-weight: 800;
color: var(--ink);
}
.steps__title svg { color: var(--blue); }
.accordion { display: grid; gap: 8px; }
.acc {
border: 1px solid var(--line);
border-radius: var(--r-md);
background: var(--white);
overflow: hidden;
transition: border-color 0.15s ease, box-shadow 0.15s ease;
}
.acc.is-open { border-color: var(--line-2); box-shadow: var(--sh-sm); }
.acc__btn {
width: 100%;
display: flex;
align-items: center;
gap: 12px;
padding: 13px 14px;
font: inherit;
text-align: left;
background: transparent;
border: 0;
cursor: pointer;
color: var(--ink);
}
.acc__emoji {
flex: 0 0 auto;
width: 32px;
height: 32px;
display: grid;
place-items: center;
border-radius: 9px;
background: #fdecec;
font-size: 16px;
}
.acc__label { flex: 1 1 auto; font-weight: 700; font-size: 14px; }
.acc__chev {
flex: 0 0 auto;
color: var(--muted);
transition: transform 0.25s ease;
}
.acc.is-open .acc__chev { transform: rotate(180deg); color: var(--blue); }
.acc__panel {
max-height: 0;
overflow: hidden;
transition: max-height 0.3s ease;
}
.acc__inner { padding: 0 14px 14px 58px; }
.acc__lead { margin: 0 0 8px; font-size: 13px; color: var(--ink-2); }
.acc ol {
margin: 0;
padding-left: 18px;
display: grid;
gap: 6px;
}
.acc li { font-size: 13px; color: var(--ink-2); }
.acc li b { color: var(--ink); font-weight: 700; }
/* ---------- locate ---------- */
.locate { padding: 16px 22px 22px; }
.map {
position: relative;
height: 150px;
border-radius: var(--r-md);
overflow: hidden;
border: 1px solid var(--line);
background:
linear-gradient(135deg, #dbeafe 0%, #c9e9df 55%, #e0f2ff 100%);
}
.map__grid {
position: absolute;
inset: 0;
background-image:
linear-gradient(rgba(37, 99, 235, 0.1) 1px, transparent 1px),
linear-gradient(90deg, rgba(37, 99, 235, 0.1) 1px, transparent 1px);
background-size: 26px 26px;
}
.map::before {
content: "";
position: absolute;
left: -10%;
top: 62%;
width: 130%;
height: 16px;
background: rgba(255, 255, 255, 0.7);
transform: rotate(-8deg);
border-radius: 4px;
}
.map::after {
content: "";
position: absolute;
left: 26%;
top: -10%;
width: 14px;
height: 130%;
background: rgba(255, 255, 255, 0.55);
transform: rotate(6deg);
}
.map__pin {
position: absolute;
left: 50%;
top: 46%;
transform: translate(-50%, -100%);
color: var(--danger);
filter: drop-shadow(0 6px 8px rgba(15, 39, 64, 0.3));
z-index: 2;
animation: bob 2.2s ease-in-out infinite;
}
@keyframes bob {
50% { transform: translate(-50%, -110%); }
}
.map__badge {
position: absolute;
left: 12px;
bottom: 12px;
z-index: 2;
font-size: 12px;
font-weight: 700;
color: var(--ink);
background: rgba(255, 255, 255, 0.92);
border: 1px solid var(--line);
padding: 5px 10px;
border-radius: 999px;
box-shadow: var(--sh-sm);
}
.addr { padding-top: 14px; }
.addr__name { margin: 0; font-size: 15px; font-weight: 800; color: var(--ink); }
.addr__line { margin: 3px 0 12px; font-size: 13px; color: var(--muted); }
.addr__actions { display: flex; gap: 10px; flex-wrap: wrap; }
.btn {
display: inline-flex;
align-items: center;
gap: 8px;
font: inherit;
font-size: 14px;
font-weight: 700;
padding: 10px 16px;
border-radius: 999px;
cursor: pointer;
border: 1px solid transparent;
text-decoration: none;
transition: transform 0.12s ease, background 0.15s ease, box-shadow 0.15s ease, color 0.15s ease;
}
.btn--primary { background: var(--blue); color: #fff; box-shadow: 0 6px 16px rgba(37, 99, 235, 0.28); }
.btn--primary:hover { background: var(--blue-d); transform: translateY(-1px); }
.btn--primary:active { transform: translateY(0) scale(0.98); }
.btn--ghost { background: var(--white); color: var(--ink-2); border-color: var(--line-2); }
.btn--ghost:hover { border-color: var(--blue); color: var(--blue); background: var(--blue-50); }
.btn--ghost:active { transform: scale(0.98); }
/* ---------- focus ---------- */
:focus-visible {
outline: 3px solid rgba(37, 99, 235, 0.45);
outline-offset: 2px;
border-radius: 6px;
}
.banner__call:focus-visible { outline-color: rgba(255, 255, 255, 0.9); }
/* ---------- toast ---------- */
.toast {
position: fixed;
left: 50%;
bottom: 26px;
transform: translate(-50%, 20px);
background: var(--ink);
color: #fff;
font-size: 13px;
font-weight: 600;
padding: 11px 18px;
border-radius: 999px;
box-shadow: var(--sh-md);
opacity: 0;
pointer-events: none;
transition: opacity 0.25s ease, transform 0.25s ease;
z-index: 50;
}
.toast.is-show { opacity: 1; transform: translate(-50%, 0); }
/* ---------- responsive ---------- */
@media (max-width: 520px) {
body { padding: 16px 10px; }
.banner { flex-wrap: wrap; gap: 12px; padding: 18px; }
.banner__call { order: 3; width: 100%; justify-content: center; }
.banner__title { font-size: 17px; }
.facts, .steps, .locate, .hours { padding-left: 16px; padding-right: 16px; }
.hours { grid-template-columns: repeat(4, 1fr); }
.fact__value { font-size: 14px; }
.acc__inner { padding-left: 14px; }
.addr__actions .btn { flex: 1 1 auto; justify-content: center; }
}
@media (prefers-reduced-motion: reduce) {
* { animation: none !important; transition: none !important; }
}(function () {
"use strict";
/* ---------- data ---------- */
// openMin / closeMin in minutes from midnight; null = closed
var SCHEDULE = [
{ day: "Sun", open: 9 * 60, close: 15 * 60 }, // 0
{ day: "Mon", open: 8 * 60, close: 20 * 60 }, // 1
{ day: "Tue", open: 8 * 60, close: 20 * 60 }, // 2
{ day: "Wed", open: 8 * 60, close: 20 * 60 }, // 3
{ day: "Thu", open: 8 * 60, close: 20 * 60 }, // 4
{ day: "Fri", open: 8 * 60, close: 18 * 60 }, // 5
{ day: "Sat", open: 10 * 60, close: 16 * 60 } // 6
];
var TIPS = [
{
emoji: "🦷",
label: "Knocked-out tooth",
lead: "Fast action in the first 30 minutes greatly improves the chance of saving it.",
steps: [
"Pick the tooth up by the <b>crown</b> — never touch the root.",
"Gently rinse with milk or saline; do <b>not</b> scrub or dry it.",
"Try to reinsert it into the socket and bite on gauze to hold it.",
"If it won't go back, keep it in <b>milk</b> and call us immediately."
]
},
{
emoji: "😣",
label: "Severe swelling or abscess",
lead: "Swelling with fever can signal a spreading infection that needs urgent care.",
steps: [
"Rinse with warm salt water (½ tsp salt in a cup of water).",
"Apply a <b>cold compress</b> to the outside of the cheek, 15 min on/off.",
"Do not place aspirin directly on the gum — it burns tissue.",
"Call the emergency line; seek the ER if breathing is affected."
]
},
{
emoji: "🩸",
label: "Bleeding that won't stop",
lead: "Most oral bleeding slows with steady, direct pressure.",
steps: [
"Fold clean gauze and bite down firmly for <b>15 minutes</b> without peeking.",
"Keep your head elevated and stay calm.",
"Avoid rinsing, spitting or hot drinks for a few hours.",
"If bleeding continues past 30 minutes, call us right away."
]
},
{
emoji: "🔧",
label: "Lost filling or crown",
lead: "Usually uncomfortable rather than dangerous — protect the tooth until you're seen.",
steps: [
"Save the crown if you have it and keep it clean.",
"Cover the gap with dental wax or sugar-free gum to shield it.",
"Avoid chewing on that side and skip very hot or cold foods.",
"Book a same-day walk-in to have it re-cemented."
]
}
];
var DAY_LABELS = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
/* ---------- helpers ---------- */
function fmt(min) {
var h = Math.floor(min / 60);
var m = min % 60;
var ap = h >= 12 ? "pm" : "am";
var hh = h % 12;
if (hh === 0) hh = 12;
return hh + (m ? ":" + String(m).padStart(2, "0") : "") + ap;
}
var toastEl = document.getElementById("toast");
var toastTimer;
function toast(msg) {
if (!toastEl) return;
toastEl.textContent = msg;
toastEl.classList.add("is-show");
clearTimeout(toastTimer);
toastTimer = setTimeout(function () {
toastEl.classList.remove("is-show");
}, 2200);
}
function copy(text) {
if (navigator.clipboard && navigator.clipboard.writeText) {
navigator.clipboard.writeText(text).then(
function () { toast("Copied: " + text); },
function () { fallbackCopy(text); }
);
} else {
fallbackCopy(text);
}
}
function fallbackCopy(text) {
var ta = document.createElement("textarea");
ta.value = text;
ta.setAttribute("readonly", "");
ta.style.position = "absolute";
ta.style.left = "-9999px";
document.body.appendChild(ta);
ta.select();
try { document.execCommand("copy"); toast("Copied: " + text); }
catch (e) { toast("Press Ctrl/Cmd+C to copy"); }
document.body.removeChild(ta);
}
/* ---------- open-now status ---------- */
function renderStatus() {
var now = new Date();
var dow = now.getDay();
var mins = now.getHours() * 60 + now.getMinutes();
var today = SCHEDULE[dow];
var pill = document.getElementById("statusPill");
var label = document.getElementById("statusLabel");
var sub = document.getElementById("statusSub");
var todayHours = document.getElementById("todayHours");
var isOpen = today && today.open != null && mins >= today.open && mins < today.close;
pill.classList.remove("is-open", "is-closed");
if (isOpen) {
pill.classList.add("is-open");
label.textContent = "Open now";
sub.textContent = "Walk-ins accepted until " + fmt(today.close);
} else {
pill.classList.add("is-closed");
label.textContent = "Closed now";
// find next opening
var nxt = nextOpen(dow, mins);
sub.textContent = nxt
? "Call the 24/7 line · opens " + nxt
: "Call the 24/7 emergency line";
}
todayHours.textContent = today && today.open != null
? fmt(today.open) + " – " + fmt(today.close)
: "Closed";
}
function nextOpen(dow, mins) {
for (var i = 0; i < 7; i++) {
var d = (dow + i) % 7;
var s = SCHEDULE[d];
if (s.open == null) continue;
if (i === 0 && mins < s.open) return "today " + fmt(s.open);
if (i === 1) return "tomorrow " + fmt(s.open);
if (i > 1) return DAY_LABELS[d] + " " + fmt(s.open);
}
return null;
}
/* ---------- weekly hours grid ---------- */
function renderHours() {
var grid = document.getElementById("hoursGrid");
var todayIdx = new Date().getDay();
var frag = document.createDocumentFragment();
SCHEDULE.forEach(function (s, i) {
var cell = document.createElement("div");
cell.className = "hcell";
if (i === todayIdx) cell.classList.add("is-today");
if (s.open == null) cell.classList.add("is-closed");
var val = s.open == null ? "Closed" : fmt(s.open).replace("am", "").replace("pm", "") + "–" + fmt(s.close);
cell.innerHTML =
'<div class="hcell__day">' + s.day + "</div>" +
'<div class="hcell__val">' + val + "</div>";
frag.appendChild(cell);
});
grid.appendChild(frag);
}
/* ---------- accordion ---------- */
function renderAccordion() {
var acc = document.getElementById("accordion");
TIPS.forEach(function (tip, i) {
var item = document.createElement("div");
item.className = "acc";
var panelId = "acc-panel-" + i;
var btnId = "acc-btn-" + i;
var ol = tip.steps.map(function (s) { return "<li>" + s + "</li>"; }).join("");
item.innerHTML =
'<button class="acc__btn" type="button" id="' + btnId + '" aria-expanded="false" aria-controls="' + panelId + '">' +
'<span class="acc__emoji" aria-hidden="true">' + tip.emoji + "</span>" +
'<span class="acc__label">' + tip.label + "</span>" +
'<svg class="acc__chev" viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M6 9l6 6 6-6"/></svg>' +
"</button>" +
'<div class="acc__panel" id="' + panelId + '" role="region" aria-labelledby="' + btnId + '">' +
'<div class="acc__inner">' +
'<p class="acc__lead">' + tip.lead + "</p>" +
"<ol>" + ol + "</ol>" +
"</div>" +
"</div>";
acc.appendChild(item);
});
acc.addEventListener("click", function (e) {
var btn = e.target.closest(".acc__btn");
if (!btn) return;
var item = btn.closest(".acc");
var panel = item.querySelector(".acc__panel");
var isOpen = item.classList.contains("is-open");
// close all
acc.querySelectorAll(".acc").forEach(function (el) {
el.classList.remove("is-open");
el.querySelector(".acc__btn").setAttribute("aria-expanded", "false");
el.querySelector(".acc__panel").style.maxHeight = null;
});
if (!isOpen) {
item.classList.add("is-open");
btn.setAttribute("aria-expanded", "true");
panel.style.maxHeight = panel.scrollHeight + "px";
}
});
}
/* ---------- copy wiring ---------- */
function wireCopy() {
document.querySelectorAll("[data-copy]").forEach(function (el) {
// buttons/ghost/copy trigger copy; the tel link should still dial,
// so only intercept non-anchor elements plus ghost/copy buttons.
var isDial = el.tagName === "A" && el.getAttribute("href") &&
el.getAttribute("href").indexOf("tel:") === 0;
var isMapLink = el.tagName === "A" && el.getAttribute("target") === "_blank";
if (isDial || isMapLink) return;
el.addEventListener("click", function (e) {
e.preventDefault();
copy(el.getAttribute("data-copy"));
});
});
}
/* ---------- init ---------- */
renderStatus();
renderHours();
renderAccordion();
wireCopy();
// keep the open/closed pill fresh
setInterval(renderStatus, 60 * 1000);
})();<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Dental — Emergency Info</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=Inter:wght@400;500;600;700;800&display=swap"
rel="stylesheet"
/>
<link rel="stylesheet" href="style.css" />
</head>
<body>
<main class="wrap">
<section class="card" aria-labelledby="card-title">
<!-- Urgent banner -->
<header class="banner">
<div class="banner__glyph" aria-hidden="true">
<svg viewBox="0 0 24 24" width="26" height="26" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M12 2 3 7v6c0 5 3.8 8.3 9 9 5.2-.7 9-4 9-9V7z" />
<path d="M12 8v5" /><path d="M12 16h.01" />
</svg>
</div>
<div class="banner__text">
<p class="banner__kicker">24/7 Dental Emergency Line</p>
<h1 id="card-title" class="banner__title">In pain? We can see you today.</h1>
</div>
<a class="banner__call" href="tel:+18005550142" data-copy="+1 (800) 555-0142" aria-label="Call the emergency dental line, 1 800 555 0142">
<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
<path d="M22 16.9v3a2 2 0 0 1-2.2 2 19.8 19.8 0 0 1-8.6-3 19.5 19.5 0 0 1-6-6 19.8 19.8 0 0 1-3-8.6A2 2 0 0 1 4.1 2h3a2 2 0 0 1 2 1.7c.1.9.3 1.8.6 2.7a2 2 0 0 1-.5 2.1L8 9.6a16 16 0 0 0 6 6l1.1-1.1a2 2 0 0 1 2.1-.5c.9.3 1.8.5 2.7.6a2 2 0 0 1 1.7 2z" />
</svg>
<span>Call now</span>
</a>
</header>
<!-- Status + quick facts -->
<div class="facts">
<div class="status" id="statusRow">
<span class="pill" id="statusPill" aria-live="polite">
<span class="dot" aria-hidden="true"></span>
<span id="statusLabel">Checking…</span>
</span>
<span class="status__sub" id="statusSub">Loading today's hours</span>
</div>
<ul class="facts__grid">
<li class="fact">
<span class="fact__ico" aria-hidden="true">
<svg viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M22 16.9v3a2 2 0 0 1-2.2 2 19.8 19.8 0 0 1-8.6-3 19.5 19.5 0 0 1-6-6 19.8 19.8 0 0 1-3-8.6A2 2 0 0 1 4.1 2h3a2 2 0 0 1 2 1.7c.1.9.3 1.8.6 2.7a2 2 0 0 1-.5 2.1L8 9.6a16 16 0 0 0 6 6l1.1-1.1a2 2 0 0 1 2.1-.5c.9.3 1.8.5 2.7.6a2 2 0 0 1 1.7 2z"/></svg>
</span>
<div class="fact__body">
<p class="fact__label">Hotline</p>
<p class="fact__value" data-copy="+1 (800) 555-0142">+1 (800) 555-0142</p>
</div>
<button class="fact__copy" type="button" data-copy="+1 (800) 555-0142" aria-label="Copy hotline number">Copy</button>
</li>
<li class="fact">
<span class="fact__ico" aria-hidden="true">
<svg viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="9"/><path d="M12 7v5l3 2"/></svg>
</span>
<div class="fact__body">
<p class="fact__label">Walk-in today</p>
<p class="fact__value" id="todayHours">—</p>
</div>
<span class="fact__tag">No appt.</span>
</li>
<li class="fact">
<span class="fact__ico" aria-hidden="true">
<svg viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 12h4l3 8 4-16 3 8h4"/></svg>
</span>
<div class="fact__body">
<p class="fact__label">Avg. wait</p>
<p class="fact__value">~14 min</p>
</div>
<span class="fact__tag fact__tag--ok">Low</span>
</li>
</ul>
</div>
<!-- Weekly hours -->
<div class="hours" id="hoursGrid" aria-label="Weekly opening hours"></div>
<!-- What to do accordion -->
<div class="steps">
<h2 class="steps__title">
<svg viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="9"/><path d="M12 16v-4"/><path d="M12 8h.01"/></svg>
What to do right now
</h2>
<div class="accordion" id="accordion"></div>
</div>
<!-- Map placeholder + address -->
<div class="locate">
<div class="map" role="img" aria-label="Map showing Brightsmile Dental Emergency Clinic location">
<div class="map__grid" aria-hidden="true"></div>
<span class="map__pin" aria-hidden="true">
<svg viewBox="0 0 24 24" width="24" height="24" fill="currentColor"><path d="M12 2a7 7 0 0 0-7 7c0 5 7 13 7 13s7-8 7-13a7 7 0 0 0-7-7zm0 9.5A2.5 2.5 0 1 1 12 6a2.5 2.5 0 0 1 0 5.5z"/></svg>
</span>
<span class="map__badge">Brightsmile Clinic</span>
</div>
<div class="addr">
<p class="addr__name">Brightsmile Dental Emergency Clinic</p>
<p class="addr__line">248 Rivermill Avenue, Suite 3<br />Northgate, CA 90210</p>
<div class="addr__actions">
<a class="btn btn--primary" href="https://maps.google.com/?q=248+Rivermill+Avenue" target="_blank" rel="noopener">
<svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 11l19-9-9 19-2-8-8-2z"/></svg>
Get directions
</a>
<button class="btn btn--ghost" type="button" data-copy="248 Rivermill Avenue, Suite 3, Northgate, CA 90210" aria-label="Copy clinic address">Copy address</button>
</div>
</div>
</div>
</section>
</main>
<div class="toast" id="toast" role="status" aria-live="polite"></div>
<script src="script.js"></script>
</body>
</html>The Dental Emergency Info component is a calm, high-clarity card designed to help an anxious patient act fast. A bold red urgent banner pairs a large one-tap call button with a live open now / closed pill that is calculated from the current time against the clinic’s weekly schedule, so visitors instantly know whether to phone the after-hours line. A compact grid surfaces the emergency hotline, walk-in hours and average wait time, each with a copy-to-clipboard shortcut and a friendly toast confirmation.
Below the summary, a keyboard-accessible “What to do right now” accordion expands step-by-step first-aid guidance for the most common dental emergencies — a knocked-out tooth, severe swelling, uncontrolled bleeding and a lost filling or crown — with only one panel open at a time for focus. A soft gradient map placeholder anchors the clinic location with an address block and a “Get directions” action, while every control exposes clear focus rings and hover states so the card stays usable under stress, on a phone, and for screen-reader users.
Interactions are entirely vanilla JavaScript: the open-now logic, the animated accordion with
aria-expanded syncing, clipboard copy helpers and the toast notifications all run without any
framework or build step, and the layout collapses gracefully to a single column under 520px.
Illustrative UI only — not intended for real medical use.