News — Front Page
A full newspaper front page with a Playfair Display nameplate, dateline, edition and price strip framed by hairline rules, then a strict multi-column lead grid. One dominant lead story carries a deck, drop-cap lede, captioned duotone hero figure and an oversized pull quote, flanked by secondary stories with column rules, a More-on-the-front sidebar, a five-day outlook, and a four-up briefs strip. Vanilla JS drives a live dateline clock, an A-/A+ body-type control, and a scroll-spy section-jump nav.
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.3);
--rule-hair: rgba(22, 19, 15, 0.1);
--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;
/* JS-controlled body type scale */
--type-scale: 1;
--body-size: calc(1rem * var(--type-scale));
}
* {
box-sizing: border-box;
}
html {
-webkit-text-size-adjust: 100%;
}
body {
margin: 0;
background:
radial-gradient(120% 80% at 50% -10%, rgba(255, 255, 255, 0.5), transparent 60%),
var(--cream);
color: var(--ink);
font-family: var(--sans);
line-height: 1.5;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.skip-link {
position: absolute;
left: -999px;
top: 0;
background: var(--ink);
color: var(--paper);
padding: 10px 16px;
z-index: 50;
font: 600 13px/1 var(--sans);
}
.skip-link:focus {
left: 12px;
top: 12px;
}
.sheet {
max-width: 1180px;
margin: 0 auto;
padding: 28px clamp(14px, 3.5vw, 40px) 48px;
background:
linear-gradient(180deg, var(--paper), var(--paper)) padding-box;
}
/* ========================= MASTHEAD ========================= */
.masthead {
text-align: center;
}
.masthead__strip {
display: flex;
flex-wrap: wrap;
align-items: center;
justify-content: center;
gap: 6px 22px;
font: 600 11px/1.3 var(--sans);
letter-spacing: 0.14em;
text-transform: uppercase;
color: var(--ink-3);
padding: 9px 0;
}
.masthead__strip--top {
border-bottom: 1px solid var(--rule-2);
justify-content: space-between;
}
.masthead__strip--bottom {
border-top: 2px solid var(--ink);
border-bottom: 1px solid var(--rule-2);
justify-content: space-between;
gap: 6px 16px;
}
.strip__item--motto {
font-style: italic;
font-family: var(--serif);
font-weight: 500;
letter-spacing: 0.02em;
text-transform: none;
color: var(--muted);
}
.strip__label {
color: var(--muted);
margin-right: 5px;
}
.strip__item--clock time {
font-variant-numeric: tabular-nums;
letter-spacing: 0.08em;
}
.strip__item--price,
.strip__item--edition {
color: var(--red-d);
}
.strip__day {
font-weight: 700;
}
.nameplate {
padding: clamp(8px, 2vw, 18px) 0 4px;
}
.nameplate__title {
margin: 0;
font-family: var(--serif);
font-weight: 900;
font-size: clamp(2.6rem, 9vw, 6.2rem);
line-height: 0.95;
letter-spacing: -0.01em;
color: var(--ink);
text-shadow: 0 1px 0 rgba(255, 255, 255, 0.4);
}
/* ========================= SECTION NAV ========================= */
.sectnav {
display: flex;
flex-wrap: wrap;
align-items: center;
justify-content: space-between;
gap: 10px;
margin-top: 14px;
padding: 8px 0;
border-top: 1px solid var(--rule);
border-bottom: 3px double var(--rule-2);
position: sticky;
top: 0;
background: var(--paper);
z-index: 20;
}
.sectnav__list {
display: flex;
flex-wrap: wrap;
gap: 4px 4px;
list-style: none;
margin: 0;
padding: 0;
}
.sectnav__list a {
display: inline-block;
padding: 5px 11px;
font: 700 11.5px/1 var(--sans);
letter-spacing: 0.13em;
text-transform: uppercase;
color: var(--ink-2);
text-decoration: none;
border-radius: var(--r-sm);
transition: background 0.15s ease, color 0.15s ease;
}
.sectnav__list a:hover,
.sectnav__list a:focus-visible {
background: var(--ink);
color: var(--paper);
outline: none;
}
.sectnav__list a.is-active {
color: var(--red-d);
background: var(--red-50);
}
.textsize {
display: flex;
align-items: center;
gap: 5px;
}
.textsize__label {
font: 600 10px/1 var(--sans);
letter-spacing: 0.16em;
text-transform: uppercase;
color: var(--muted);
margin-right: 2px;
}
.textsize__btn {
font: 700 13px/1 var(--serif);
min-width: 30px;
height: 28px;
padding: 0 8px;
border: 1px solid var(--rule-2);
background: var(--white);
color: var(--ink);
border-radius: var(--r-sm);
cursor: pointer;
transition: background 0.15s ease, transform 0.05s ease;
}
.textsize__btn:hover {
background: var(--newsprint);
}
.textsize__btn:active {
transform: translateY(1px);
}
.textsize__btn:focus-visible {
outline: 2px solid var(--red);
outline-offset: 1px;
}
/* ========================= KICKERS / SHARED ========================= */
.kicker {
margin: 0 0 6px;
font: 700 10.5px/1.2 var(--sans);
letter-spacing: 0.18em;
text-transform: uppercase;
color: var(--ink-3);
}
.kicker--red {
color: var(--red);
}
.kicker--red::before {
content: "";
display: inline-block;
width: 7px;
height: 7px;
margin-right: 7px;
background: var(--red);
border-radius: 50%;
vertical-align: 1px;
}
.byline {
margin: 0 0 10px;
font: 600 12px/1.4 var(--sans);
color: var(--ink-2);
}
.byline .dateline {
color: var(--muted);
font-weight: 500;
text-transform: uppercase;
letter-spacing: 0.08em;
font-size: 11px;
}
/* ========================= MAIN GRID ========================= */
.grid {
display: grid;
grid-template-columns: minmax(0, 2.1fr) minmax(0, 1.35fr) minmax(0, 1fr);
gap: 0;
margin-top: 20px;
}
.grid > .lead {
padding-right: clamp(16px, 2.5vw, 30px);
border-right: 1px solid var(--rule);
}
.grid > .secondary {
padding: 0 clamp(16px, 2.5vw, 30px);
border-right: 1px solid var(--rule);
}
.grid > .sidebar {
padding-left: clamp(16px, 2.5vw, 28px);
}
/* ========================= LEAD STORY ========================= */
.lead__headline {
margin: 4px 0 10px;
font-family: var(--serif);
font-weight: 800;
font-size: clamp(1.9rem, 4.2vw, 3.2rem);
line-height: 1.02;
letter-spacing: -0.01em;
color: var(--ink);
}
.lead__deck {
margin: 0 0 16px;
font-family: var(--serif);
font-style: italic;
font-weight: 500;
font-size: clamp(1.05rem, 1.7vw, 1.3rem);
line-height: 1.35;
color: var(--ink-3);
padding-bottom: 14px;
border-bottom: 1px solid var(--rule);
}
.lead__body {
font-size: var(--body-size);
color: var(--ink-2);
text-align: justify;
hyphens: auto;
}
.lead__body p {
margin: 0 0 0.85em;
}
.lead__lede {
margin-top: 14px;
}
.lead__lede::first-letter {
float: left;
font-family: var(--serif);
font-weight: 800;
font-size: 3.6em;
line-height: 0.74;
padding: 6px 10px 0 0;
color: var(--red-d);
}
@media (min-width: 880px) {
.lead__body {
column-count: 2;
column-gap: 28px;
column-rule: 1px solid var(--rule-hair);
}
.lead__body .pullquote {
column-span: all;
}
}
.jump-cont {
font-style: italic;
font-family: var(--serif);
color: var(--red-d);
white-space: nowrap;
}
/* ========================= PULL QUOTE ========================= */
.pullquote {
margin: 18px 0;
padding: 14px 0;
border-top: 2px solid var(--ink);
border-bottom: 2px solid var(--ink);
font-family: var(--serif);
font-weight: 600;
font-size: clamp(1.3rem, 2.4vw, 1.85rem);
line-height: 1.18;
color: var(--ink);
text-align: left;
hyphens: none;
}
.pullquote cite {
display: block;
margin-top: 10px;
font-family: var(--sans);
font-style: normal;
font-weight: 600;
font-size: 0.66em;
letter-spacing: 0.04em;
color: var(--red-d);
}
/* ========================= FIGURES / PRESS PHOTOS ========================= */
.figure {
margin: 0 0 16px;
}
.press-photo {
width: 100%;
border: 1px solid var(--rule-2);
border-radius: var(--r-sm);
filter: contrast(1.04) saturate(0.9);
}
.figure--hero .press-photo {
aspect-ratio: 16 / 9;
}
.figure--inline .press-photo {
aspect-ratio: 4 / 3;
}
.figure--side .press-photo {
aspect-ratio: 3 / 4;
}
/* Duotone-ink / red "press" imagery via layered gradients */
.press-photo--harbor {
background:
linear-gradient(180deg, rgba(244, 239, 228, 0.35), rgba(22, 19, 15, 0.55)),
radial-gradient(80% 60% at 22% 22%, rgba(180, 41, 31, 0.42), transparent 55%),
radial-gradient(120% 90% at 78% 90%, rgba(43, 38, 32, 0.9), transparent 60%),
repeating-linear-gradient(96deg, rgba(22, 19, 15, 0.16) 0 2px, transparent 2px 7px),
linear-gradient(120deg, #6f5a4a, #2b2620 70%);
}
.press-photo--river {
background:
linear-gradient(180deg, rgba(244, 239, 228, 0.4), rgba(74, 68, 59, 0.5)),
radial-gradient(90% 70% at 30% 10%, rgba(212, 196, 160, 0.6), transparent 60%),
linear-gradient(160deg, #b8ab87 0%, #7a7164 45%, #3b352d 100%),
repeating-linear-gradient(-12deg, rgba(255, 255, 255, 0.06) 0 1px, transparent 1px 9px);
}
.press-photo--portrait {
background:
linear-gradient(180deg, rgba(244, 239, 228, 0.25), rgba(22, 19, 15, 0.6)),
radial-gradient(60% 45% at 50% 32%, rgba(216, 198, 178, 0.85), transparent 60%),
radial-gradient(120% 80% at 50% 120%, rgba(143, 31, 23, 0.5), transparent 55%),
linear-gradient(135deg, #5a4a3e, #211d18);
}
.figure__caption {
margin-top: 7px;
font: italic 500 12.5px/1.4 var(--serif);
color: var(--ink-3);
border-left: 2px solid var(--red);
padding-left: 9px;
}
.figure__credit {
display: inline;
font: 600 10.5px/1.4 var(--sans);
font-style: normal;
letter-spacing: 0.06em;
text-transform: uppercase;
color: var(--muted);
}
/* ========================= SECONDARY COLUMN ========================= */
.secondary {
font-size: calc(0.95rem * var(--type-scale));
}
.story {
padding-bottom: 16px;
margin-bottom: 16px;
border-bottom: 1px solid var(--rule);
}
.story:last-child {
border-bottom: 0;
margin-bottom: 0;
}
.story__headline {
margin: 0 0 8px;
font-family: var(--serif);
font-weight: 700;
font-size: clamp(1.15rem, 1.9vw, 1.45rem);
line-height: 1.12;
color: var(--ink);
}
.story__headline:hover {
color: var(--ink-2);
}
.story p {
margin: 0 0 0.7em;
color: var(--ink-2);
text-align: justify;
hyphens: auto;
}
.story p:first-of-type::first-letter {
font-weight: 600;
}
/* ========================= SIDEBAR ========================= */
.sidebar__title,
.briefs__title {
margin: 0 0 12px;
padding-bottom: 7px;
font: 700 12px/1 var(--sans);
letter-spacing: 0.2em;
text-transform: uppercase;
color: var(--ink);
border-bottom: 2px solid var(--ink);
}
.morelist {
list-style: none;
margin: 0 0 18px;
padding: 0;
}
.morelist__item {
padding: 11px 0;
border-bottom: 1px solid var(--rule-hair);
}
.morelist__item:first-child {
padding-top: 0;
}
.morelist__link {
display: block;
font-family: var(--serif);
font-weight: 600;
font-size: 1.05rem;
line-height: 1.16;
color: var(--ink);
text-decoration: none;
transition: color 0.15s ease;
}
.morelist__link:hover,
.morelist__link:focus-visible {
color: var(--red-d);
text-decoration: underline;
text-decoration-thickness: 1px;
text-underline-offset: 3px;
outline: none;
}
.morelist__meta {
margin: 5px 0 0;
font: 600 10.5px/1 var(--sans);
letter-spacing: 0.1em;
text-transform: uppercase;
color: var(--muted);
}
.weatherbox {
margin-top: 16px;
padding: 12px 14px;
background: var(--newsprint);
border: 1px solid var(--rule);
border-radius: var(--r-sm);
}
.weatherbox__title {
margin: 0 0 8px;
font: 700 10.5px/1 var(--sans);
letter-spacing: 0.16em;
text-transform: uppercase;
color: var(--ink-2);
}
.weatherbox__list {
list-style: none;
margin: 0;
padding: 0;
}
.weatherbox__list li {
display: grid;
grid-template-columns: 1fr 1.4fr auto;
align-items: center;
gap: 8px;
padding: 5px 0;
font: 600 12px/1 var(--sans);
color: var(--ink-2);
border-top: 1px solid var(--rule-hair);
}
.weatherbox__list li:first-child {
border-top: 0;
}
.wx {
font-weight: 500;
font-size: 11px;
letter-spacing: 0.04em;
}
.wx--sun {
color: var(--warn);
}
.wx--cloud {
color: var(--muted);
}
.wx--rain {
color: #3a6ea5;
}
/* ========================= BRIEFS ========================= */
.briefs {
margin-top: 26px;
padding-top: 18px;
border-top: 3px double var(--rule-2);
}
.briefs__grid {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 0;
}
.brief {
padding: 0 18px;
border-left: 1px solid var(--rule);
font-size: calc(0.9rem * var(--type-scale));
}
.brief:first-child {
padding-left: 0;
border-left: 0;
}
.brief:last-child {
padding-right: 0;
}
.brief__text {
margin: 0;
color: var(--ink-2);
text-align: justify;
hyphens: auto;
}
/* ========================= COLOPHON ========================= */
.colophon {
margin-top: 28px;
padding-top: 14px;
border-top: 1px solid var(--rule);
text-align: center;
font: italic 500 12px/1.5 var(--serif);
color: var(--muted);
}
/* ========================= TOAST ========================= */
.toast {
position: fixed;
left: 50%;
bottom: 26px;
transform: translate(-50%, 18px);
background: var(--ink);
color: var(--paper);
padding: 11px 18px;
border-radius: var(--r-md);
font: 600 13px/1.2 var(--sans);
letter-spacing: 0.01em;
box-shadow: 0 6px 22px rgba(22, 19, 15, 0.28);
opacity: 0;
pointer-events: none;
transition: opacity 0.22s ease, transform 0.22s ease;
z-index: 60;
max-width: 88vw;
}
.toast.is-visible {
opacity: 1;
transform: translate(-50%, 0);
}
.toast__accent {
color: var(--red-50);
font-weight: 700;
}
/* ========================= RESPONSIVE ========================= */
@media (max-width: 980px) {
.grid {
grid-template-columns: minmax(0, 1.6fr) minmax(0, 1fr);
}
.grid > .sidebar {
grid-column: 1 / -1;
margin-top: 22px;
padding-left: 0;
padding-top: 18px;
border-top: 3px double var(--rule-2);
}
.grid > .secondary {
border-right: 0;
padding-right: 0;
}
.sidebar {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 18px 28px;
align-items: start;
}
.sidebar__title {
grid-column: 1 / -1;
}
}
@media (max-width: 720px) {
.grid {
grid-template-columns: 1fr;
}
.grid > .lead,
.grid > .secondary {
border-right: 0;
padding: 0;
}
.grid > .secondary {
margin-top: 22px;
padding-top: 18px;
border-top: 3px double var(--rule-2);
}
.lead__body {
column-count: 1 !important;
}
.briefs__grid {
grid-template-columns: repeat(2, 1fr);
}
.brief {
padding: 12px 0;
border-left: 0;
border-top: 1px solid var(--rule-hair);
}
.brief:nth-child(odd) {
padding-right: 14px;
}
.brief:nth-child(even) {
padding-left: 14px;
border-left: 1px solid var(--rule);
}
.sidebar {
grid-template-columns: 1fr;
}
}
@media (max-width: 460px) {
.masthead__strip {
letter-spacing: 0.08em;
gap: 4px 12px;
}
.strip__item--motto {
display: none;
}
.sectnav {
position: static;
}
.briefs__grid {
grid-template-columns: 1fr;
}
.brief {
padding: 12px 0 !important;
border-left: 0 !important;
}
}
@media (prefers-reduced-motion: reduce) {
* {
transition-duration: 0.01ms !important;
}
}/* The Meridian Courier — front page interactions (vanilla JS) */
(function () {
"use strict";
var doc = document;
/* ----------------------------- toast helper ----------------------------- */
var toastEl = doc.getElementById("toast");
var toastTimer = null;
function toast(msg, accent) {
if (!toastEl) return;
toastEl.innerHTML = accent
? '<span class="toast__accent">' + accent + "</span> " + msg
: msg;
toastEl.classList.add("is-visible");
window.clearTimeout(toastTimer);
toastTimer = window.setTimeout(function () {
toastEl.classList.remove("is-visible");
}, 2200);
}
/* --------------------------- live dateline clock ------------------------ */
var clockEl = doc.getElementById("press-clock");
var dayEl = doc.getElementById("dateline-day");
var dateEl = doc.getElementById("dateline-date");
var DAYS = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];
var MONTHS = ["January", "February", "March", "April", "May", "June",
"July", "August", "September", "October", "November", "December"];
function pad(n) { return n < 10 ? "0" + n : "" + n; }
function tick() {
var now = new Date();
if (clockEl) {
clockEl.textContent = pad(now.getHours()) + ":" + pad(now.getMinutes()) + ":" + pad(now.getSeconds());
clockEl.setAttribute("datetime", now.toISOString());
}
if (dayEl) dayEl.textContent = DAYS[now.getDay()];
if (dateEl) dateEl.textContent = MONTHS[now.getMonth()] + " " + now.getDate() + ", " + now.getFullYear();
}
tick();
window.setInterval(tick, 1000);
/* ------------------------------ text size ------------------------------- */
var root = doc.documentElement;
var STORE_KEY = "courier-type-scale";
var MIN = 0.85, MAX = 1.4, STEP = 0.075;
var scale = 1;
try {
var saved = parseFloat(window.localStorage.getItem(STORE_KEY));
if (!isNaN(saved) && saved >= MIN && saved <= MAX) scale = saved;
} catch (e) { /* storage unavailable */ }
function applyScale(announce) {
scale = Math.min(MAX, Math.max(MIN, Math.round(scale * 1000) / 1000));
root.style.setProperty("--type-scale", String(scale));
try { window.localStorage.setItem(STORE_KEY, String(scale)); } catch (e) {}
if (announce) {
var pct = Math.round(scale * 100);
toast("Body type set to " + pct + "%", "Aa");
}
}
applyScale(false);
function bind(id, fn) {
var el = doc.getElementById(id);
if (el) el.addEventListener("click", fn);
}
bind("text-inc", function () { scale += STEP; applyScale(true); });
bind("text-dec", function () { scale -= STEP; applyScale(true); });
bind("text-reset", function () { scale = 1; applyScale(true); });
/* --------------------------- section-jump nav --------------------------- */
var jumpLinks = Array.prototype.slice.call(doc.querySelectorAll("[data-jump]"));
var sections = [];
jumpLinks.forEach(function (link) {
var id = (link.getAttribute("href") || "").slice(1);
var target = id ? doc.getElementById(id) : null;
if (target) sections.push({ link: link, target: target });
link.addEventListener("click", function (ev) {
if (!target) return;
ev.preventDefault();
var reduce = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
target.scrollIntoView({ behavior: reduce ? "auto" : "smooth", block: "start" });
try { history.replaceState(null, "", "#" + id); } catch (e) {}
// move focus for keyboard/AT users without re-scrolling
target.setAttribute("tabindex", "-1");
target.focus({ preventScroll: true });
var label = link.textContent.trim();
toast("Jumped to " + label, "§");
});
});
/* ----------------- scroll-spy: highlight active section ----------------- */
function setActive(id) {
jumpLinks.forEach(function (link) {
link.classList.toggle("is-active", link.getAttribute("href") === "#" + id);
});
}
if ("IntersectionObserver" in window && sections.length) {
var visible = {};
var io = new IntersectionObserver(function (entries) {
entries.forEach(function (en) {
visible[en.target.id] = en.isIntersecting ? en.intersectionRatio : 0;
});
var bestId = null, best = 0;
sections.forEach(function (s) {
var r = visible[s.target.id] || 0;
if (r > best) { best = r; bestId = s.target.id; }
});
if (bestId) setActive(bestId);
}, { rootMargin: "-30% 0px -55% 0px", threshold: [0, 0.25, 0.5, 1] });
sections.forEach(function (s) { io.observe(s.target); });
}
/* ------------------- "continued / more" link feedback ------------------- */
doc.querySelectorAll(".morelist__link, .jump-cont").forEach(function (el) {
el.addEventListener("click", function (ev) {
if (el.classList.contains("jump-cont")) {
ev.preventDefault();
toast("Story continues on an inside page", "☞");
return;
}
ev.preventDefault();
var headline = el.textContent.trim();
toast("“" + headline + "” — inside section", "Read");
});
});
/* a small welcome note once the masthead is set */
window.setTimeout(function () {
toast("Front page set. Use A− / A+ to resize the type.", "Extra");
}, 600);
})();<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>The Meridian Courier — Front Page</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&family=Playfair+Display:ital,wght@0,500;0,600;0,700;0,800;0,900;1,500;1,600&display=swap"
rel="stylesheet"
/>
<link rel="stylesheet" href="style.css" />
</head>
<body>
<a class="skip-link" href="#lead">Skip to lead story</a>
<div class="sheet">
<!-- ============ MASTHEAD / NAMEPLATE ============ -->
<header class="masthead" role="banner">
<div class="masthead__strip masthead__strip--top">
<span class="strip__item strip__item--est">Est. 1887</span>
<span class="strip__item strip__item--vol">Vol. CXXXIX — No. 214</span>
<span class="strip__item strip__item--motto">Light for the Common Reader</span>
</div>
<div class="nameplate">
<h1 class="nameplate__title">The Meridian Courier</h1>
</div>
<div class="masthead__strip masthead__strip--bottom">
<span class="strip__item strip__item--date" id="dateline">
<span class="strip__day" id="dateline-day">Monday</span>,
<span id="dateline-date">June 8, 2026</span>
</span>
<span class="strip__item strip__item--clock" aria-live="polite">
<span class="strip__label">Last set</span>
<time id="press-clock">--:--:--</time>
</span>
<span class="strip__item strip__item--weather">
Clear · 71°F · Wind W 6mph
</span>
<span class="strip__item strip__item--edition">City & Late Final</span>
<span class="strip__item strip__item--price">$2.50</span>
</div>
</header>
<!-- ============ SECTION NAV ============ -->
<nav class="sectnav" aria-label="Front page sections">
<ul class="sectnav__list" id="sectnav">
<li><a href="#lead" data-jump>Top Story</a></li>
<li><a href="#metro" data-jump>Metro</a></li>
<li><a href="#world" data-jump>World</a></li>
<li><a href="#more" data-jump>More News</a></li>
<li><a href="#briefs" data-jump>In Brief</a></li>
</ul>
<div class="textsize" role="group" aria-label="Adjust text size">
<span class="textsize__label">Text</span>
<button class="textsize__btn" id="text-dec" type="button" aria-label="Decrease text size">A−</button>
<button class="textsize__btn" id="text-reset" type="button" aria-label="Reset text size">A</button>
<button class="textsize__btn" id="text-inc" type="button" aria-label="Increase text size">A+</button>
</div>
</nav>
<!-- ============ MAIN GRID ============ -->
<main class="grid" id="frontpage">
<!-- ===== LEAD STORY ===== -->
<article class="lead" id="lead" aria-labelledby="lead-hl">
<p class="kicker kicker--red">Breaking · Harbor District</p>
<h2 class="lead__headline" id="lead-hl">
Tidewater Tunnel Reopens After Decade, Splicing City to Its Forgotten East Bank
</h2>
<p class="lead__deck">
Engineers cut the ribbon on the rebuilt crossing at dawn, ending the longest
public-works closure in the Courier's records and rerouting a generation of commuters.
</p>
<figure class="figure figure--hero">
<div class="press-photo press-photo--harbor" role="img"
aria-label="Crowds gather at the mouth of the reopened Tidewater Tunnel at first light"></div>
<figcaption class="figure__caption">
First light over the Tidewater portal as the gates lifted on Sunday.
<span class="figure__credit">— Photograph by R. Vance / Courier Staff</span>
</figcaption>
</figure>
<div class="lead__body">
<p class="lead__lede">
The first car rolled through the Tidewater Tunnel at 5:41 a.m., headlamps
sweeping a corridor that had stood dark and dripping since the spring floods of
2016. By the time the morning rush thinned, more than nine thousand vehicles had
made the crossing — a quiet thunder of tires that, to the engineers gathered at
the east portal, sounded a great deal like vindication.
</p>
<p>
For ten years the closure had been a fact of municipal life, an absence so
permanent that maps quietly stopped drawing the route. Bus lines were rewritten.
Two ferry companies were born of the gap and one of them died of it. Along the
East Bank, storefronts that once fed the tunnel traffic boarded their windows one
by one, until the neighborhood the city had named for its industry came to be
known, with affection and a little grief, as the Forgotten Quarter.
</p>
<blockquote class="pullquote">
“We did not rebuild a tunnel. We rebuilt the argument that this side of the
river is worth reaching.”
<cite>— Mayor Iris Delacroix, at the east portal</cite>
</blockquote>
<p>
Sunday's reopening arrived four months ahead of the revised schedule and, the
transit authority insists, within its third and final budget. The new bore is
wider by a lane, lined with sensors that read water pressure in real time, and
crowned by a pumping system designed to clear in minutes what once took the old
tunnel days to shed. Whether it holds against the next great tide is a question
the harbor will answer in its own time.
</p>
<p>
What is certain is the morning itself: the smell of wet concrete and coffee, the
brass band the East Bank merchants' league hired without telling anyone, the
commuters who slowed at the midpoint to photograph a ceiling they had spent a
decade routing around. <span class="jump-cont">Continued on A6, col. 1</span>
</p>
</div>
</article>
<!-- ===== SECONDARY COLUMN ===== -->
<div class="secondary">
<article class="story" id="metro" aria-labelledby="metro-hl">
<p class="kicker">Metro</p>
<h3 class="story__headline" id="metro-hl">
School Board Backs Four-Day Week in Narrow Vote
</h3>
<p class="byline">By Naomi Hart · <span class="dateline">City Hall</span></p>
<p>
After a hearing that ran past midnight, trustees approved a pilot four-day
calendar for the district's eleven elementary schools, betting that shorter weeks
and longer days will steady a teaching corps thinned by two years of departures.
</p>
<p>
Parents filled the gallery on both sides of a debate that turned less on test
scores than on the cost of a fifth day of childcare. The measure passed five votes
to four, with the swing trustee citing “a duty to try the unfamiliar before
we lose the familiar.”
</p>
</article>
<article class="story" id="world" aria-labelledby="world-hl">
<p class="kicker kicker--red">World</p>
<h3 class="story__headline" id="world-hl">
Drought Pact Signed as Three Nations Share a Shrinking River
</h3>
<p class="byline">By Tomás Réy · <span class="dateline">Vallport</span></p>
<figure class="figure figure--inline">
<div class="press-photo press-photo--river" role="img"
aria-label="A low river winding between dry banks under a pale sky"></div>
<figcaption class="figure__caption">
The Saren at its lowest reading in forty years.
<span class="figure__credit">— Courier Wire</span>
</figcaption>
</figure>
<p>
Negotiators emerged with a ledger rather than a treaty: a season-by-season
accounting of who draws what from the Saren as its headwaters retreat up the
mountain. The agreement holds for five years and, all sides concede, only as long
as the snow does.
</p>
</article>
<article class="story" aria-labelledby="arts-hl">
<p class="kicker">Arts</p>
<h3 class="story__headline" id="arts-hl">
A Painter's Lost Notebooks Surface in a Flea-Market Crate
</h3>
<p class="byline">By Greta Olund</p>
<p>
Three sketchbooks attributed to the muralist Edda Wren, missing since her studio
fire, turned up beneath a stack of postcards at the Sunday market — annotated,
water-stained, and, a curator says, “unmistakably hers.”
</p>
</article>
</div>
<!-- ===== SIDEBAR ===== -->
<aside class="sidebar" id="more" aria-labelledby="more-hl">
<h3 class="sidebar__title" id="more-hl">More on the Front</h3>
<ul class="morelist">
<li class="morelist__item">
<p class="kicker">Business</p>
<a class="morelist__link" href="#more">Port Tolls to Rise 3% as Tunnel Traffic Returns</a>
<p class="morelist__meta">4 min read</p>
</li>
<li class="morelist__item">
<p class="kicker">Science</p>
<a class="morelist__link" href="#more">Observatory Catches a Comet No One Predicted</a>
<p class="morelist__meta">6 min read</p>
</li>
<li class="morelist__item">
<p class="kicker kicker--red">Sports</p>
<a class="morelist__link" href="#more">Harriers Clinch the Pennant in Eleven Innings</a>
<p class="morelist__meta">3 min read</p>
</li>
<li class="morelist__item">
<p class="kicker">Opinion</p>
<a class="morelist__link" href="#more">The City We Routed Around — and Owe</a>
<p class="morelist__meta">5 min read</p>
</li>
</ul>
<figure class="figure figure--side">
<div class="press-photo press-photo--portrait" role="img"
aria-label="Portrait of a woman at a podium addressing a crowd"></div>
<figcaption class="figure__caption">
Mayor Delacroix at the ribbon.
<span class="figure__credit">— A. Brandt / Courier</span>
</figcaption>
</figure>
<div class="weatherbox" aria-label="Five day outlook">
<p class="weatherbox__title">Five-Day Outlook</p>
<ul class="weatherbox__list">
<li><span>Mon</span><span class="wx wx--sun">Clear</span><span>71°</span></li>
<li><span>Tue</span><span class="wx wx--cloud">Cloud</span><span>68°</span></li>
<li><span>Wed</span><span class="wx wx--rain">Rain</span><span>61°</span></li>
<li><span>Thu</span><span class="wx wx--rain">Rain</span><span>59°</span></li>
<li><span>Fri</span><span class="wx wx--sun">Clear</span><span>66°</span></li>
</ul>
</div>
</aside>
</main>
<!-- ============ BRIEFS STRIP ============ -->
<section class="briefs" id="briefs" aria-labelledby="briefs-hl">
<h3 class="briefs__title" id="briefs-hl">In Brief</h3>
<div class="briefs__grid">
<article class="brief">
<p class="kicker">Transit</p>
<p class="brief__text">
Night bus service returns to the East Bank for the first time since 2017, with two
lines running until 1 a.m. beginning Wednesday.
</p>
</article>
<article class="brief">
<p class="kicker">Courts</p>
<p class="brief__text">
A judge dismissed the final suit over the harbor expansion, clearing the way for
dredging to resume after the autumn fishing season.
</p>
</article>
<article class="brief">
<p class="kicker">Obituary</p>
<p class="brief__text">
Walden Pyle, the ferryman who carried commuters across the gap for nine years, has
died at 74. He is survived by his crew and his cat.
</p>
</article>
<article class="brief">
<p class="kicker kicker--red">Late</p>
<p class="brief__text">
Power restored to the Lantern Hill district at 11:40 p.m. after a transformer
fault left four blocks dark for under an hour.
</p>
</article>
</div>
</section>
<footer class="colophon">
<p>The Meridian Courier · A Fictional Daily · Printed on recycled imagination.</p>
</footer>
</div>
<div class="toast" id="toast" role="status" aria-live="polite"></div>
<script src="script.js"></script>
</body>
</html>Front Page
A complete broadsheet front page for The Meridian Courier, a fictional daily. The nameplate sets the paper’s name in heavy Playfair Display between two hairline strips — an “Est. 1887 / Vol. CXXXIX” masthead line above and a dateline-and-edition strip below carrying the day, a live clock, weather, the edition mark, and the cover price. Beneath a sticky section nav, the page resolves into a strict three-column grid: a dominant lead story with a deck, drop-cap lede, a captioned duotone “press photo” hero and an oversized serif pull quote; a secondary column of stories separated by thin rules; and a sidebar of “More on the Front” links, a portrait figure, and a five-day outlook box. A four-up “In Brief” strip closes the sheet.
The layout leans on rules rather than shadows — hairlines between columns, a double rule above the briefs, a red rule on every caption — with a single accent red reserved for kickers, breaking dots, and links. Figures are simulated entirely in CSS with layered linear and radial gradients to read like muted, duotone-ink press imagery, each with an italic caption and a credit line. Body text is justified with hyphenation, and the lead column splits into two CSS columns on wider screens, collapsing cleanly to a single column under ~720px and holding together down to ~360px.
Three vanilla-JS interactions bring it to life: a live clock that ticks in the dateline (and keeps the day and date in sync), an A-/A+ / reset control that scales the body type via a --type-scale custom property and remembers the choice in localStorage, and a section-jump nav with smooth scrolling, focus management, and an IntersectionObserver scroll-spy that highlights the current section. A small toast() helper surfaces feedback for each action.
Illustrative UI only — masthead, headlines, bylines, and articles are fictional; not a real news publication.