News — Live Blog / Breaking News
A breaking-news live blog for the fictional Meridian Dispatch, art-directed in a warm newsprint palette with Playfair Display mastheads and Inter meta. A pulsing red LIVE badge tops a serif headline, summary and byline, while a reverse-chronological timeline carries timestamped updates with rail dots, key-update flags, duotone captioned press photos and an oversized pull quote. A sticky What you need to know box and a helpline card sit alongside. Vanilla JS prepends fresh entries with a live timestamp and flash, refreshes relative times, and fires toasts.
MCP
Код
:root {
--cream: #f4efe4;
--paper: #faf7f0;
--white: #ffffff;
--newsprint: #efe9da;
--ink: #16130f;
--ink-2: #2b2620;
--ink-3: #4a443b;
--muted: #7a7164;
--red: #b4291f;
--red-d: #8f1f17;
--red-50: #f3dcd9;
--rule: rgba(22, 19, 15, 0.16);
--rule-2: rgba(22, 19, 15, 0.30);
--rule-hair: rgba(22, 19, 15, 0.10);
--ok: #2f7d4f;
--warn: #b67a18;
--danger: #b4291f;
--r-sm: 4px;
--r-md: 8px;
--r-lg: 12px;
--serif: "Playfair Display", "Times New Roman", Georgia, serif;
--sans: "Inter", system-ui, -apple-system, sans-serif;
}
* { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }
body {
margin: 0;
background: var(--cream);
color: var(--ink);
font-family: var(--sans);
line-height: 1.5;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
font-size: 16px;
}
.skip-link {
position: absolute;
left: -999px;
top: 0;
background: var(--ink);
color: var(--paper);
padding: 10px 16px;
z-index: 50;
font-size: 13px;
font-weight: 600;
}
.skip-link:focus { left: 8px; top: 8px; }
:focus-visible {
outline: 2px solid var(--red);
outline-offset: 2px;
}
/* ---------- Masthead ---------- */
.masthead {
background: var(--paper);
border-bottom: 3px double var(--rule-2);
}
.masthead__bar {
max-width: 1180px;
margin: 0 auto;
padding: 7px 24px;
display: flex;
justify-content: space-between;
align-items: center;
border-bottom: 1px solid var(--rule-hair);
font-size: 11px;
letter-spacing: 0.08em;
text-transform: uppercase;
color: var(--muted);
font-weight: 500;
}
.masthead__name {
text-align: center;
padding: 14px 24px 8px;
}
.masthead__title {
font-family: var(--serif);
font-weight: 900;
font-size: clamp(2.4rem, 7vw, 4.2rem);
letter-spacing: -0.01em;
margin: 0;
line-height: 1;
color: var(--ink);
}
.masthead__nav {
max-width: 1180px;
margin: 0 auto;
padding: 0 24px;
display: flex;
gap: 26px;
justify-content: center;
flex-wrap: wrap;
border-top: 1px solid var(--rule);
border-bottom: 1px solid var(--rule);
}
.masthead__nav a {
display: inline-block;
padding: 9px 0;
font-size: 12px;
font-weight: 600;
letter-spacing: 0.1em;
text-transform: uppercase;
color: var(--ink-2);
text-decoration: none;
}
.masthead__nav a:hover { color: var(--red); }
.masthead__nav a[aria-current="page"] {
color: var(--red);
border-bottom: 2px solid var(--red);
margin-bottom: -1px;
}
/* ---------- Layout shell ---------- */
.wrap {
max-width: 1180px;
margin: 0 auto;
padding: 30px 24px 60px;
}
/* ---------- Live blog head ---------- */
.kicker {
display: flex;
align-items: center;
gap: 14px;
margin: 0 0 12px;
flex-wrap: wrap;
}
.kicker__topic {
font-size: 12px;
font-weight: 700;
letter-spacing: 0.16em;
text-transform: uppercase;
color: var(--ink-3);
}
.live-badge {
display: inline-flex;
align-items: center;
gap: 7px;
background: var(--red);
color: var(--white);
padding: 4px 10px 4px 9px;
border-radius: var(--r-sm);
font-size: 12px;
font-weight: 700;
letter-spacing: 0.14em;
text-transform: uppercase;
}
.live-badge__dot {
width: 8px;
height: 8px;
border-radius: 50%;
background: var(--white);
box-shadow: 0 0 0 0 rgba(255, 255, 255, 0.7);
animation: pulse 1.6s ease-out infinite;
}
@keyframes pulse {
0% { box-shadow: 0 0 0 0 rgba(255, 255, 255, 0.55); }
70% { box-shadow: 0 0 0 7px rgba(255, 255, 255, 0); }
100% { box-shadow: 0 0 0 0 rgba(255, 255, 255, 0); }
}
.liveblog__headline {
font-family: var(--serif);
font-weight: 800;
font-size: clamp(1.9rem, 4.6vw, 3.15rem);
line-height: 1.04;
letter-spacing: -0.01em;
margin: 0 0 16px;
color: var(--ink);
max-width: 17ch;
}
.liveblog__summary {
font-size: 1.12rem;
line-height: 1.55;
color: var(--ink-2);
margin: 0 0 18px;
max-width: 62ch;
}
.liveblog__summary::first-letter {
initial-letter: 3;
-webkit-initial-letter: 3;
font-family: var(--serif);
font-weight: 800;
color: var(--red);
margin-right: 8px;
}
.liveblog__byline {
display: flex;
flex-wrap: wrap;
align-items: baseline;
justify-content: space-between;
gap: 8px 18px;
padding: 14px 0;
border-top: 1px solid var(--rule);
border-bottom: 1px solid var(--rule);
font-size: 13px;
color: var(--ink-3);
}
.byline__authors strong { color: var(--ink); font-weight: 600; }
.byline__meta {
color: var(--muted);
letter-spacing: 0.02em;
}
.byline__sep { margin: 0 4px; }
/* ---------- Two-column layout ---------- */
.liveblog__layout {
display: grid;
grid-template-columns: minmax(0, 1fr) 320px;
gap: 48px;
margin-top: 28px;
}
.liveblog__main { min-width: 0; }
/* ---------- Stream toolbar ---------- */
.stream-toolbar {
display: flex;
align-items: center;
justify-content: space-between;
gap: 16px;
padding-bottom: 14px;
margin-bottom: 4px;
border-bottom: 2px solid var(--ink);
flex-wrap: wrap;
}
.stream-toolbar__left {
display: flex;
align-items: center;
gap: 9px;
font-size: 12px;
letter-spacing: 0.1em;
text-transform: uppercase;
font-weight: 600;
color: var(--ink-3);
}
.stream-count { color: var(--ink); }
.stream-toolbar__hint { color: var(--muted); font-weight: 500; }
.stream-toolbar__sep { color: var(--rule-2); }
.btn {
font-family: var(--sans);
font-size: 13px;
font-weight: 600;
letter-spacing: 0.04em;
border: 1px solid var(--ink);
background: var(--paper);
color: var(--ink);
padding: 9px 15px;
border-radius: var(--r-sm);
cursor: pointer;
display: inline-flex;
align-items: center;
gap: 8px;
transition: background 0.15s ease, color 0.15s ease, transform 0.05s ease;
}
.btn:hover { background: var(--newsprint); }
.btn:active { transform: translateY(1px); }
.btn--accent {
background: var(--red);
border-color: var(--red);
color: var(--white);
}
.btn--accent:hover { background: var(--red-d); border-color: var(--red-d); }
.btn__dot {
width: 7px;
height: 7px;
border-radius: 50%;
background: var(--white);
animation: pulse 1.6s ease-out infinite;
}
/* ---------- Stream / entries ---------- */
.stream {
list-style: none;
margin: 0;
padding: 0;
}
.entry {
display: grid;
grid-template-columns: 96px minmax(0, 1fr);
gap: 20px;
padding: 24px 0;
border-bottom: 1px solid var(--rule);
position: relative;
}
.entry::before {
content: "";
position: absolute;
left: 96px;
top: 0;
bottom: 0;
width: 1px;
background: var(--rule-hair);
transform: translateX(-10px);
}
.entry__rail {
display: flex;
flex-direction: column;
gap: 3px;
position: relative;
z-index: 1;
}
.entry__rail::after {
content: "";
position: absolute;
right: -10px;
top: 7px;
width: 9px;
height: 9px;
border-radius: 50%;
background: var(--paper);
border: 2px solid var(--ink-3);
transform: translateX(50%);
}
.entry--key-row .entry__rail::after,
.entry:first-child .entry__rail::after {
border-color: var(--red);
background: var(--red);
}
.entry__time {
font-family: var(--sans);
font-weight: 700;
font-size: 14px;
color: var(--ink);
letter-spacing: 0.01em;
}
.entry__rel {
font-size: 11px;
color: var(--muted);
letter-spacing: 0.04em;
}
.entry__body { min-width: 0; }
.entry__kicker {
font-size: 11px;
font-weight: 700;
letter-spacing: 0.14em;
text-transform: uppercase;
color: var(--red);
margin: 0 0 6px;
}
.entry--key {
display: flex;
align-items: center;
gap: 8px;
flex-wrap: wrap;
color: var(--ink-3);
}
.key-flag {
background: var(--ink);
color: var(--paper);
padding: 2px 7px;
border-radius: var(--r-sm);
font-size: 10px;
letter-spacing: 0.12em;
}
.entry__headline {
font-family: var(--serif);
font-weight: 700;
font-size: 1.32rem;
line-height: 1.18;
margin: 0 0 10px;
color: var(--ink);
}
.entry__body p {
margin: 0 0 11px;
font-size: 1rem;
line-height: 1.62;
color: var(--ink-2);
text-align: justify;
hyphens: auto;
}
.entry__body p:last-child { margin-bottom: 0; }
.entry__lede::first-letter {
initial-letter: 2;
-webkit-initial-letter: 2;
font-family: var(--serif);
font-weight: 700;
color: var(--ink);
margin-right: 6px;
}
.entry__author {
font-style: italic;
font-size: 0.9rem !important;
color: var(--muted) !important;
text-align: left !important;
}
/* New entry flash */
.entry--flash {
animation: flashIn 1.6s ease-out;
}
@keyframes flashIn {
0% { background: var(--red-50); }
100% { background: transparent; }
}
.entry--new .entry__time {
color: var(--red);
}
/* ---------- Press photos (CSS-only) ---------- */
.entry__fig { margin: 4px 0 14px; }
.press-photo {
width: 100%;
aspect-ratio: 16 / 9;
border-radius: var(--r-sm);
border: 1px solid var(--rule);
position: relative;
overflow: hidden;
}
.press-photo::after {
content: "";
position: absolute;
inset: 0;
background:
repeating-linear-gradient(115deg,
rgba(255, 255, 255, 0.04) 0 2px,
rgba(0, 0, 0, 0.04) 2px 4px);
mix-blend-mode: overlay;
pointer-events: none;
}
.press-photo--surge {
background:
radial-gradient(120% 80% at 20% 0%, rgba(200, 214, 214, 0.55), transparent 60%),
linear-gradient(180deg, #5d6a6b 0%, #44514f 32%, #2f3b39 60%, #1d2625 100%),
linear-gradient(90deg, #3a4746, #4c5957);
}
.press-photo--crew {
background:
radial-gradient(90% 120% at 78% 20%, rgba(214, 168, 60, 0.45), transparent 55%),
linear-gradient(180deg, #2c2620 0%, #3a3024 40%, #241e18 100%),
linear-gradient(120deg, #4a3c26, #2a221a);
}
.press-photo--live {
background:
radial-gradient(110% 90% at 30% 10%, rgba(180, 41, 31, 0.4), transparent 55%),
linear-gradient(180deg, #3a2622 0%, #2a1c1a 45%, #161311 100%);
}
figcaption {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
gap: 4px 12px;
margin-top: 7px;
font-size: 12.5px;
line-height: 1.4;
}
.figcap__text { font-style: italic; color: var(--ink-3); }
.figcap__credit {
text-transform: uppercase;
letter-spacing: 0.08em;
font-size: 10.5px;
color: var(--muted);
font-weight: 600;
white-space: nowrap;
}
/* ---------- Pull quote ---------- */
.pull {
margin: 16px 0;
padding: 4px 0 4px 20px;
border-left: 3px solid var(--red);
}
.pull p {
font-family: var(--serif) !important;
font-weight: 600;
font-size: 1.34rem !important;
line-height: 1.32 !important;
color: var(--ink) !important;
font-style: italic;
margin: 0 0 8px !important;
text-align: left !important;
hyphens: none !important;
}
.pull cite {
display: block;
font-family: var(--sans);
font-style: normal;
font-size: 12px;
font-weight: 600;
letter-spacing: 0.08em;
text-transform: uppercase;
color: var(--muted);
}
/* ---------- Aside ---------- */
.liveblog__aside {
position: sticky;
top: 20px;
align-self: start;
display: flex;
flex-direction: column;
gap: 22px;
}
.keybox {
border: 1px solid var(--ink);
background: var(--white);
padding: 18px 20px 16px;
border-radius: var(--r-md);
}
.keybox__title {
font-family: var(--sans);
font-size: 12px;
font-weight: 700;
letter-spacing: 0.16em;
text-transform: uppercase;
margin: 0 0 12px;
padding-bottom: 10px;
border-bottom: 2px solid var(--ink);
color: var(--ink);
}
.keybox__list {
margin: 0;
padding: 0;
list-style: none;
}
.keybox__list li {
position: relative;
padding: 8px 0 8px 18px;
font-size: 14px;
line-height: 1.45;
color: var(--ink-2);
border-bottom: 1px solid var(--rule-hair);
}
.keybox__list li:last-child { border-bottom: 0; }
.keybox__list li::before {
content: "";
position: absolute;
left: 0;
top: 14px;
width: 6px;
height: 6px;
background: var(--red);
border-radius: 1px;
}
.keybox__list strong { color: var(--ink); font-weight: 600; }
.keybox__foot {
margin: 12px 0 0;
padding-top: 10px;
border-top: 1px solid var(--rule-hair);
font-size: 11px;
color: var(--muted);
letter-spacing: 0.03em;
}
.aside-card {
border: 1px solid var(--rule);
background: var(--newsprint);
padding: 16px 20px;
border-radius: var(--r-md);
}
.aside-card__kicker {
margin: 0 0 2px;
font-size: 11px;
font-weight: 700;
letter-spacing: 0.16em;
text-transform: uppercase;
color: var(--red);
}
.aside-card__big {
font-family: var(--serif);
font-weight: 800;
font-size: 2.6rem;
line-height: 1;
margin: 0 0 6px;
color: var(--ink);
}
.aside-card__sub {
margin: 0 0 6px;
font-size: 13px;
line-height: 1.45;
color: var(--ink-3);
}
.aside-card__sub--alt { margin-bottom: 0; color: var(--red-d); }
.aside-card__sub strong { color: var(--red-d); }
/* ---------- Footer ---------- */
.foot {
border-top: 3px double var(--rule-2);
background: var(--paper);
padding: 24px;
text-align: center;
}
.foot__name {
font-family: var(--serif);
font-weight: 700;
font-size: 1.3rem;
margin: 0 0 4px;
color: var(--ink);
}
.foot__note {
margin: 0;
font-size: 12px;
color: var(--muted);
letter-spacing: 0.02em;
}
/* ---------- Toast ---------- */
.toast {
position: fixed;
left: 50%;
bottom: 24px;
transform: translate(-50%, 20px);
background: var(--ink);
color: var(--paper);
padding: 11px 18px;
border-radius: var(--r-sm);
font-size: 13px;
font-weight: 500;
letter-spacing: 0.02em;
box-shadow: 0 6px 20px rgba(22, 19, 15, 0.22);
opacity: 0;
pointer-events: none;
transition: opacity 0.22s ease, transform 0.22s ease;
z-index: 60;
max-width: calc(100vw - 32px);
}
.toast--show {
opacity: 1;
transform: translate(-50%, 0);
}
.toast__dot {
display: inline-block;
width: 7px;
height: 7px;
border-radius: 50%;
background: var(--red);
margin-right: 8px;
vertical-align: middle;
}
/* ---------- Responsive ---------- */
@media (max-width: 880px) {
.liveblog__layout {
grid-template-columns: 1fr;
gap: 36px;
}
.liveblog__aside {
position: static;
flex-direction: column;
}
}
@media (max-width: 720px) {
.wrap { padding: 22px 18px 48px; }
.liveblog__summary { font-size: 1.05rem; }
.entry {
grid-template-columns: 1fr;
gap: 8px;
padding: 20px 0;
}
.entry::before { display: none; }
.entry__rail {
flex-direction: row;
align-items: baseline;
gap: 10px;
}
.entry__rail::after { display: none; }
.entry__body p { text-align: left; hyphens: none; }
}
@media (max-width: 400px) {
body { font-size: 15px; }
.masthead__bar { font-size: 10px; padding: 6px 16px; }
.masthead__name { padding: 12px 16px 6px; }
.masthead__nav { gap: 16px; padding: 0 16px; }
.stream-toolbar { flex-direction: column; align-items: flex-start; }
.btn--accent { width: 100%; justify-content: center; }
}
@media (prefers-reduced-motion: reduce) {
.live-badge__dot, .btn__dot { animation: none; }
.entry--flash { animation: none; }
.toast { transition: opacity 0.01s; }
}(function () {
"use strict";
var stream = document.getElementById("stream");
var newBtn = document.getElementById("newUpdateBtn");
var countEl = document.getElementById("streamCount");
var toastEl = document.getElementById("toast");
var keyboxTime = document.getElementById("keyboxTime");
/* ---------- Toast helper ---------- */
var toastTimer = null;
function toast(msg) {
if (!toastEl) return;
toastEl.innerHTML = '<span class="toast__dot" aria-hidden="true"></span>' + msg;
toastEl.classList.add("toast--show");
clearTimeout(toastTimer);
toastTimer = setTimeout(function () {
toastEl.classList.remove("toast--show");
}, 2600);
}
/* ---------- Relative time ---------- */
function relTime(date) {
var diff = Math.round((Date.now() - date.getTime()) / 1000);
if (diff < 5) return "just now";
if (diff < 60) return diff + " sec ago";
var mins = Math.round(diff / 60);
if (mins < 60) return mins + " min ago";
var hours = Math.round(mins / 60);
if (hours < 24) return hours + " hr ago";
var days = Math.round(hours / 24);
return days + (days === 1 ? " day ago" : " days ago");
}
function fmtClock(date) {
var h = date.getHours();
var m = date.getMinutes();
var ampm = h >= 12 ? "PM" : "AM";
h = h % 12;
if (h === 0) h = 12;
return h + ":" + (m < 10 ? "0" + m : m) + " " + ampm;
}
/* ---------- Refresh all relative timestamps ---------- */
function refreshRel() {
var entries = stream.querySelectorAll(".entry");
for (var i = 0; i < entries.length; i++) {
var iso = entries[i].getAttribute("data-time");
var relEl = entries[i].querySelector("[data-rel]");
if (!iso || !relEl) continue;
var d = new Date(iso);
if (isNaN(d.getTime())) continue;
relEl.textContent = relTime(d);
}
if (keyboxTime) {
keyboxTime.textContent = "moments ago";
}
}
/* ---------- Update count label ---------- */
function updateCount() {
var n = stream.querySelectorAll(".entry").length;
countEl.textContent = n + (n === 1 ? " update" : " updates");
}
/* ---------- Pool of fictional new updates ---------- */
var pool = [
{
kicker: "Bay County",
headline: "Water reaches the first floor of the Wharf Row market",
key: false,
body: "Reporters at the scene say the historic market has taken on roughly a foot of water, with vendors moving stock to upper shelves. Police have closed the surrounding blocks to all but emergency vehicles.",
author: "— Marcus Vellán, at the Old Wharf"
},
{
kicker: "Schools",
headline: "All coastal schools to remain closed Tuesday",
key: false,
body: "The Meridian district announced that every school east of the Pinewright ridge will stay closed tomorrow, with remote instruction suspended in areas expecting prolonged power outages."
},
{
kicker: "Surge",
headline: "Tide gauge at Cole Point hits a new daily record",
key: true,
body: "The Cole Point gauge has recorded its highest reading since the station opened, surpassing the mark set during last autumn's nor'easter. Forecasters warn the peak is still two hours away.",
author: "— Dispatch Weather Desk"
},
{
kicker: "Rescues",
headline: "Swift-water teams pull six from stranded vehicles",
key: false,
body: "Fire officials confirm that swift-water rescue crews have helped six people from cars caught in flooded underpasses along Calder Street. No injuries have been reported.",
author: "— Dana Okwu"
},
{
kicker: "Power",
headline: "Outage total climbs past 18,000 customers",
key: false,
body: "Meridian Power & Light now reports more than 18,000 homes and businesses without electricity, with the heaviest losses on the Cole County peninsula. Restoration crews remain grounded until winds ease."
},
{
kicker: "Shelters",
headline: "Fairgrounds shelter nears capacity; new site opening",
key: false,
body: "Officials say the Meridian Fairgrounds shelter is approaching its limit and that a fourth site will open within the hour at the Tasker County armory to absorb arriving families.",
author: "— Dana Okwu"
}
];
var poolIdx = 0;
/* ---------- Build a new entry element ---------- */
function buildEntry(item, date) {
var li = document.createElement("li");
li.className = "entry entry--flash entry--new";
li.setAttribute("data-time", date.toISOString());
var rail = document.createElement("div");
rail.className = "entry__rail";
var time = document.createElement("time");
time.className = "entry__time";
time.setAttribute("datetime", date.toISOString());
time.textContent = fmtClock(date);
var rel = document.createElement("span");
rel.className = "entry__rel";
rel.setAttribute("data-rel", "");
rel.textContent = "just now";
rail.appendChild(time);
rail.appendChild(rel);
var body = document.createElement("div");
body.className = "entry__body";
var kick = document.createElement("p");
if (item.key) {
kick.className = "entry__kicker entry--key";
kick.innerHTML =
'<span class="key-flag">Key update</span> ' + escapeHtml(item.kicker);
} else {
kick.className = "entry__kicker";
kick.textContent = item.kicker;
}
var h = document.createElement("h3");
h.className = "entry__headline";
h.textContent = item.headline;
var p = document.createElement("p");
p.textContent = item.body;
body.appendChild(kick);
body.appendChild(h);
body.appendChild(p);
if (item.author) {
var au = document.createElement("p");
au.className = "entry__author";
au.textContent = item.author;
body.appendChild(au);
}
li.appendChild(rail);
li.appendChild(body);
return li;
}
function escapeHtml(s) {
return String(s)
.replace(/&/g, "&")
.replace(/</g, "<")
.replace(/>/g, ">");
}
/* ---------- Prepend a fresh update ---------- */
function postUpdate() {
var item = pool[poolIdx % pool.length];
poolIdx++;
var now = new Date();
var el = buildEntry(item, now);
// Clear the "new" red accent from the previously-newest entry
var prevNew = stream.querySelector(".entry--new");
if (prevNew) prevNew.classList.remove("entry--new");
stream.insertBefore(el, stream.firstElementChild);
updateCount();
refreshRel();
// Remove the one-shot flash class after the animation settles
setTimeout(function () {
el.classList.remove("entry--flash");
}, 1700);
toast(item.key ? "Key update posted" : "New update posted");
// Move focus to the new headline for keyboard/AT users
var headline = el.querySelector(".entry__headline");
if (headline) {
headline.setAttribute("tabindex", "-1");
headline.focus({ preventScroll: false });
}
}
if (newBtn) newBtn.addEventListener("click", postUpdate);
/* ---------- Init ---------- */
refreshRel();
updateCount();
setInterval(refreshRel, 30000);
})();<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>The Meridian Dispatch — Live: Coastal Storm Surge</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=Playfair+Display:ital,wght@0,500;0,600;0,700;0,800;0,900;1,500;1,600&family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet" />
<link rel="stylesheet" href="style.css" />
</head>
<body>
<a class="skip-link" href="#stream">Skip to live updates</a>
<header class="masthead" role="banner">
<div class="masthead__bar">
<span class="masthead__edition">Late City Edition · Vol. CXVII · No. 214</span>
<span class="masthead__price">Free with subscription</span>
</div>
<div class="masthead__name">
<h1 class="masthead__title">The Meridian Dispatch</h1>
</div>
<nav class="masthead__nav" aria-label="Sections">
<a href="#">Front</a>
<a href="#">Politics</a>
<a href="#" aria-current="page">Weather</a>
<a href="#">Business</a>
<a href="#">Culture</a>
<a href="#">Opinion</a>
</nav>
</header>
<main class="wrap" id="main">
<article class="liveblog" aria-labelledby="lede-headline">
<header class="liveblog__head">
<p class="kicker">
<span class="live-badge" id="liveBadge" aria-live="off">
<span class="live-badge__dot" aria-hidden="true"></span>Live
</span>
<span class="kicker__topic">Severe Weather · East Coast</span>
</p>
<h2 class="liveblog__headline" id="lede-headline">
Storm Surge Forces Evacuations Along the Meridian Coast as Atlas Bears Down
</h2>
<p class="liveblog__summary">
Tropical Storm Atlas strengthened overnight, pushing record tides into low-lying
neighborhoods from Harbor Point to the Old Wharf district. Emergency crews have begun
door-to-door evacuations, and the governor has declared a state of emergency for six
counties. Follow this page for verified, time-stamped updates from our reporters in
the field.
</p>
<div class="liveblog__byline">
<span class="byline__authors">By <strong>Dana Okwu</strong>, <strong>Marcus Vellán</strong> and the Dispatch Weather Desk</span>
<span class="byline__meta">
<span id="metaStarted">Updated continuously</span>
<span class="byline__sep" aria-hidden="true">·</span>
<span>5 min read</span>
</span>
</div>
</header>
<div class="liveblog__layout">
<div class="liveblog__main">
<div class="stream-toolbar">
<div class="stream-toolbar__left">
<span class="stream-count" id="streamCount">9 updates</span>
<span class="stream-toolbar__sep" aria-hidden="true">·</span>
<span class="stream-toolbar__hint">Newest first</span>
</div>
<button type="button" class="btn btn--accent" id="newUpdateBtn">
<span class="btn__dot" aria-hidden="true"></span>
Post new update
</button>
</div>
<ol class="stream" id="stream" aria-label="Live updates, newest first">
<li class="entry" data-time="2026-06-08T14:02:00">
<div class="entry__rail">
<time class="entry__time" datetime="2026-06-08T14:02:00">2:02 PM</time>
<span class="entry__rel" data-rel>just now</span>
</div>
<div class="entry__body">
<p class="entry__kicker entry--key">
<span class="key-flag">Key update</span> Governor declares state of emergency
</p>
<h3 class="entry__headline">Six counties placed under emergency order; National Guard mobilized</h3>
<p>
Governor Lena Castellano signed an emergency declaration covering Meridian,
Harbor, Cole, Pinewright, Tasker and Bay counties, freeing state funds for
shelters and authorizing the deployment of roughly 1,200 National Guard
personnel. "We are asking residents in flood zones to leave now, not later,"
she told reporters at the capitol.
</p>
<p class="entry__author">— Dana Okwu, reporting from the State House</p>
</div>
</li>
<li class="entry" data-time="2026-06-08T13:41:00">
<div class="entry__rail">
<time class="entry__time" datetime="2026-06-08T13:41:00">1:41 PM</time>
<span class="entry__rel" data-rel></span>
</div>
<div class="entry__body">
<p class="entry__kicker">Harbor Point</p>
<h3 class="entry__headline">Floodwater overtops the Harbor Point seawall</h3>
<figure class="entry__fig">
<div class="press-photo press-photo--surge" role="img"
aria-label="Gray-green floodwater surging over a concrete seawall under a heavy storm sky"></div>
<figcaption>
<span class="figcap__text">Waves breach the seawall near Harbor Point just after 1:30 p.m.</span>
<span class="figcap__credit">Meridian Dispatch / Pool</span>
</figcaption>
</figure>
<p>
Our crew watched the first waves clear the seawall around 1:30 p.m., sending
ankle-deep water down Calder Street within minutes. Storm drains, already at
capacity from the morning's rain, offered little relief. Two parked cars were
seen floating against a chain-link fence.
</p>
<p class="entry__author">— Marcus Vellán, at Harbor Point</p>
</div>
</li>
<li class="entry" data-time="2026-06-08T13:18:00">
<div class="entry__rail">
<time class="entry__time" datetime="2026-06-08T13:18:00">1:18 PM</time>
<span class="entry__rel" data-rel></span>
</div>
<div class="entry__body">
<p class="entry__kicker">Transit</p>
<h3 class="entry__headline">Coastal rail line suspended; bridges to close at 3 p.m.</h3>
<p>
The Meridian Transit Authority has suspended all service on the Coastal Line
and warns that the Whitman and Old Wharf bridges will close to traffic at
3 p.m. as winds approach the 45-mph threshold. Commuters are urged to move
inland before the closures take effect.
</p>
</div>
</li>
<li class="entry" data-time="2026-06-08T12:55:00">
<div class="entry__rail">
<time class="entry__time" datetime="2026-06-08T12:55:00">12:55 PM</time>
<span class="entry__rel" data-rel></span>
</div>
<div class="entry__body">
<p class="entry__kicker">Analysis</p>
<h3 class="entry__headline">Why Atlas intensified so quickly overnight</h3>
<p>
Unusually warm shelf waters off the Meridian coast gave Atlas the fuel to
jump two categories of intensity in under twelve hours, a process forecasters
call rapid intensification.
</p>
<blockquote class="pull">
<p>"The ocean has been running three to four degrees above normal all month.
Storms don't slow down over water like that — they feed."</p>
<cite>Dr. Priya Anand, Meridian Coastal Institute</cite>
</blockquote>
<p>
Models now bring the center ashore near the Old Wharf district between 6 and
8 p.m., with the most dangerous surge expected on the storm's eastern flank.
</p>
</div>
</li>
<li class="entry" data-time="2026-06-08T12:30:00">
<div class="entry__rail">
<time class="entry__time" datetime="2026-06-08T12:30:00">12:30 PM</time>
<span class="entry__rel" data-rel></span>
</div>
<div class="entry__body">
<p class="entry__kicker">Shelters</p>
<h3 class="entry__headline">Three emergency shelters now open inland</h3>
<p>
Red Cross volunteers have opened shelters at Pinewright High School, the Tasker
County Civic Center and the Meridian Fairgrounds. Officials say each can take
several hundred people and accepts pets in carriers.
</p>
</div>
</li>
<li class="entry" data-time="2026-06-08T12:04:00">
<div class="entry__rail">
<time class="entry__time" datetime="2026-06-08T12:04:00">12:04 PM</time>
<span class="entry__rel" data-rel></span>
</div>
<div class="entry__body">
<p class="entry__kicker">Old Wharf</p>
<h3 class="entry__headline">Sandbagging crews race the tide downtown</h3>
<figure class="entry__fig">
<div class="press-photo press-photo--crew" role="img"
aria-label="Workers in yellow rain gear stacking sandbags against a storefront in driving rain"></div>
<figcaption>
<span class="figcap__text">City crews shore up storefronts along Wharf Row before the afternoon high tide.</span>
<span class="figcap__credit">A. Brent / Meridian Dispatch</span>
</figcaption>
</figure>
<p>
With the next high tide due at 4:12 p.m., public-works teams worked a chain of
sandbags down Wharf Row, focusing on the century-old storefronts that flooded
during last autumn's nor'easter.
</p>
</div>
</li>
<li class="entry" data-time="2026-06-08T11:40:00">
<div class="entry__rail">
<time class="entry__time" datetime="2026-06-08T11:40:00">11:40 AM</time>
<span class="entry__rel" data-rel></span>
</div>
<div class="entry__body">
<p class="entry__kicker">Power</p>
<h3 class="entry__headline">First outages reported as gusts top 40 mph</h3>
<p>
Meridian Power & Light reports about 6,400 customers without electricity,
most in the exposed Cole County peninsula. Crews are staged but will not climb
poles once sustained winds pass 45 mph.
</p>
</div>
</li>
<li class="entry" data-time="2026-06-08T11:12:00">
<div class="entry__rail">
<time class="entry__time" datetime="2026-06-08T11:12:00">11:12 AM</time>
<span class="entry__rel" data-rel></span>
</div>
<div class="entry__body">
<p class="entry__kicker entry--key">
<span class="key-flag">Key update</span> Evacuations begin
</p>
<h3 class="entry__headline">Mandatory evacuation ordered for Zones A and B</h3>
<p>
Emergency management has ordered residents of coastal Zones A and B to leave
immediately. Door-to-door notifications began in Harbor Point and the Old Wharf
district shortly after 11 a.m.
</p>
</div>
</li>
<li class="entry" data-time="2026-06-08T10:30:00">
<div class="entry__rail">
<time class="entry__time" datetime="2026-06-08T10:30:00">10:30 AM</time>
<span class="entry__rel" data-rel></span>
</div>
<div class="entry__body">
<p class="entry__kicker">Forecast</p>
<h3 class="entry__headline">Atlas upgraded; surge warning posted for the coast</h3>
<p class="entry__lede">
The National Weather Center upgraded Atlas to a strong tropical storm at
10:30 a.m. and posted a storm-surge warning for the entire Meridian shoreline,
cautioning that water could rise four to seven feet above ground in the most
vulnerable neighborhoods. Forecasters described the threat as "life-threatening"
and urged residents not to wait for water to appear before leaving.
</p>
</div>
</li>
</ol>
</div>
<aside class="liveblog__aside" aria-label="At a glance">
<section class="keybox">
<h3 class="keybox__title">What you need to know</h3>
<ul class="keybox__list">
<li>Mandatory evacuation in coastal <strong>Zones A and B</strong> — leave now.</li>
<li>State of emergency declared for <strong>six counties</strong>.</li>
<li>Whitman & Old Wharf bridges close at <strong>3 p.m.</strong></li>
<li>Coastal rail line <strong>suspended</strong> until further notice.</li>
<li>Three inland shelters open; pets accepted in carriers.</li>
</ul>
<p class="keybox__foot">Last reviewed by the Weather Desk · <span id="keyboxTime">moments ago</span></p>
</section>
<section class="aside-card">
<p class="aside-card__kicker">Helpline</p>
<p class="aside-card__big">311</p>
<p class="aside-card__sub">Non-emergency storm information, 24 hours.</p>
<p class="aside-card__sub aside-card__sub--alt">Call <strong>911</strong> only for life-threatening emergencies.</p>
</section>
</aside>
</div>
</article>
</main>
<footer class="foot" role="contentinfo">
<p class="foot__name">The Meridian Dispatch</p>
<p class="foot__note">A fictional newspaper. All headlines, bylines and reporting on this page are invented for demonstration.</p>
</footer>
<div class="toast" id="toast" role="status" aria-live="polite"></div>
<script src="script.js"></script>
</body>
</html>Live Blog / Breaking News
A breaking-news live blog for The Meridian Dispatch, a fictional broadsheet, set in a warm newsprint palette with hairline rules and a single sparing red accent. A pulsing red LIVE badge, uppercase topic kicker and oversized Playfair Display headline anchor the page, followed by a summary paragraph with a red drop cap and a byline row carrying the reporters, an updated-continuously note and a read-time. Below, a reverse-chronological timeline threads timestamped updates down a ruled rail, each with a clock time, a relative “min ago” stamp, a section kicker and — where it matters — a black Key update flag.
Updates mix straight reporting with duotone CSS press photos (each with an italic caption and credit line), a lead drop cap and an oversized serif pull quote, all inside a strict two-column grid with a sticky What you need to know summary box and a 311 helpline card in the rail. The layout collapses to a single column under 720px and stays readable down to 360px.
The vanilla JS drives a Post new update button that prepends a fresh entry from a pool of real-feeling dispatches — stamped with the live clock time, flashed in red, and announced with a toast — then keeps every relative timestamp (“just now”, “3 min ago”) current on a 30-second interval. Focus moves to each new headline for keyboard and screen-reader users.
Illustrative UI only — masthead, headlines, bylines, and articles are fictional; not a real news publication.