News — Inline Newsletter Signup
An editorial inline newsletter signup module styled like a real broadsheet, set inside a fictional harbor-town front page. A red-ruled brief block carries a let-spaced kicker, a serif headline, a one-line value prop, an email field with live validation, Daily and Weekly frequency chips, a privacy note and a live subscriber count. A dark compact sidebar variant mirrors it. Vanilla JS handles validation, frequency selection, a success state, a toast and an incrementing reader tally.
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;
}
.skip-link {
position: absolute;
left: -999px;
top: 0;
background: var(--ink);
color: var(--paper);
padding: 8px 14px;
z-index: 50;
font: 600 13px/1 var(--sans);
}
.skip-link:focus { left: 12px; top: 12px; }
:focus-visible {
outline: 2px solid var(--red);
outline-offset: 2px;
}
/* ============ MASTHEAD ============ */
.masthead {
max-width: 1080px;
margin: 0 auto;
padding: 18px 24px 0;
text-align: center;
}
.masthead__top {
display: flex;
justify-content: space-between;
align-items: center;
font: 600 11px/1 var(--sans);
letter-spacing: 0.14em;
text-transform: uppercase;
color: var(--muted);
border-bottom: 1px solid var(--rule-hair);
padding-bottom: 10px;
}
.masthead__title {
display: block;
font-family: var(--serif);
font-weight: 900;
font-size: clamp(2.6rem, 8vw, 5rem);
line-height: 0.96;
letter-spacing: -0.01em;
color: var(--ink);
text-decoration: none;
margin: 14px 0 6px;
}
.masthead__title:hover { color: var(--ink-2); }
.masthead__tag {
font: italic 500 14px/1.4 var(--serif);
color: var(--ink-3);
margin: 0 0 14px;
}
.masthead__nav {
display: flex;
justify-content: center;
flex-wrap: wrap;
gap: 26px;
border-top: 3px double var(--rule-2);
border-bottom: 1px solid var(--rule-2);
padding: 10px 0;
}
.masthead__nav a {
font: 600 12px/1 var(--sans);
letter-spacing: 0.14em;
text-transform: uppercase;
color: var(--ink-2);
text-decoration: none;
}
.masthead__nav a:hover { color: var(--red); }
/* ============ LAYOUT ============ */
.layout {
max-width: 1080px;
margin: 0 auto;
padding: 34px 24px 10px;
display: grid;
grid-template-columns: minmax(0, 1fr) 300px;
gap: 0 44px;
align-items: start;
}
/* ============ ARTICLE ============ */
.kicker {
font: 700 12px/1 var(--sans);
letter-spacing: 0.2em;
text-transform: uppercase;
color: var(--red);
margin: 0 0 12px;
}
.article__head {
font-family: var(--serif);
font-weight: 800;
font-size: clamp(2rem, 5vw, 3.2rem);
line-height: 1.04;
letter-spacing: -0.01em;
margin: 0 0 14px;
color: var(--ink);
}
.article__deck {
font: italic 500 clamp(1.05rem, 2.4vw, 1.35rem)/1.4 var(--serif);
color: var(--ink-3);
margin: 0 0 18px;
max-width: 36ch;
}
.byline {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 9px;
font: 500 12px/1 var(--sans);
letter-spacing: 0.04em;
color: var(--muted);
text-transform: uppercase;
padding: 12px 0;
border-top: 1px solid var(--rule);
border-bottom: 1px solid var(--rule);
}
.byline__name { color: var(--ink); font-weight: 700; }
.byline__dot { color: var(--rule-2); }
/* figure */
.figure { margin: 22px 0; }
.figure__img {
aspect-ratio: 16 / 9;
border-radius: var(--r-sm);
border: 1px solid var(--rule);
}
.figure__img--harbor {
background:
radial-gradient(120% 90% at 78% 18%, rgba(244, 227, 184, 0.55), transparent 48%),
radial-gradient(90% 120% at 12% 90%, rgba(143, 31, 23, 0.20), transparent 55%),
linear-gradient(155deg, #2b2620 0%, #4a443b 36%, #7a6f5a 60%, #2b2620 100%);
position: relative;
}
.figure__img--harbor::after {
content: "";
position: absolute;
inset: 0;
border-radius: inherit;
background:
repeating-linear-gradient(90deg, rgba(255,255,255,0.04) 0 1px, transparent 1px 4px),
radial-gradient(60% 50% at 70% 28%, rgba(244,239,228,0.30), transparent 60%);
mix-blend-mode: screen;
}
.figure__cap {
font: italic 500 13px/1.5 var(--serif);
color: var(--ink-3);
margin-top: 8px;
border-left: 2px solid var(--red);
padding-left: 10px;
}
.figure__credit {
display: block;
font: 600 10px/1.4 var(--sans);
font-style: normal;
letter-spacing: 0.1em;
text-transform: uppercase;
color: var(--muted);
margin-top: 3px;
}
/* body copy */
.article__body {
column-count: 2;
column-gap: 36px;
column-rule: 1px solid var(--rule-hair);
font-size: 1.02rem;
color: var(--ink-2);
}
.article__body p {
margin: 0 0 1rem;
text-align: justify;
hyphens: auto;
}
.article__body .lead {
column-span: all;
font-size: 1.12rem;
text-align: left;
hyphens: none;
}
.article__body .lead::first-letter {
font-family: var(--serif);
font-weight: 800;
float: left;
font-size: 3.4em;
line-height: 0.74;
padding: 6px 10px 0 0;
color: var(--red);
}
/* pull quote */
.pullquote {
column-span: all;
margin: 14px 0 18px;
padding: 18px 0;
border-top: 3px double var(--rule-2);
border-bottom: 3px double var(--rule-2);
font-family: var(--serif);
font-weight: 600;
font-size: clamp(1.3rem, 3vw, 1.75rem);
line-height: 1.18;
color: var(--ink);
text-align: center;
}
.pullquote cite {
display: block;
margin-top: 10px;
font: 600 12px/1 var(--sans);
font-style: normal;
letter-spacing: 0.12em;
text-transform: uppercase;
color: var(--muted);
}
/* ============ INLINE SIGNUP (variant A) ============ */
.ns {
column-span: all;
background: var(--paper);
border: 1px solid var(--rule-2);
border-radius: var(--r-md);
padding: 26px 28px 22px;
margin: 8px 0 22px;
position: relative;
}
.ns__rule {
position: absolute;
top: 0; left: 0; right: 0;
height: 4px;
background: var(--red);
border-radius: var(--r-md) var(--r-md) 0 0;
}
.ns__kicker {
font: 700 11px/1 var(--sans);
letter-spacing: 0.24em;
text-transform: uppercase;
color: var(--red);
margin: 6px 0 10px;
}
.ns__head {
font-family: var(--serif);
font-weight: 700;
font-size: clamp(1.3rem, 3vw, 1.7rem);
line-height: 1.1;
margin: 0 0 8px;
color: var(--ink);
}
.ns__prop {
font-size: 0.98rem;
color: var(--ink-3);
margin: 0 0 18px;
max-width: 48ch;
}
.ns__form { margin: 0; }
.ns__field { margin: 0 0 16px; }
.ns__label {
display: block;
font: 700 10px/1 var(--sans);
letter-spacing: 0.16em;
text-transform: uppercase;
color: var(--muted);
margin: 0 0 7px;
}
.ns__inputrow {
display: flex;
gap: 10px;
}
.ns__input {
flex: 1;
min-width: 0;
font: 500 1rem/1.2 var(--sans);
color: var(--ink);
background: var(--white);
border: 1px solid var(--rule-2);
border-radius: var(--r-sm);
padding: 12px 14px;
}
.ns__input::placeholder { color: var(--muted); }
.ns__input:focus-visible { outline: 2px solid var(--red); outline-offset: 1px; }
.ns__input.is-invalid { border-color: var(--danger); background: #fbf1f0; }
.ns__btn {
font: 700 0.92rem/1 var(--sans);
letter-spacing: 0.04em;
color: var(--paper);
background: var(--ink);
border: 1px solid var(--ink);
border-radius: var(--r-sm);
padding: 0 22px;
cursor: pointer;
white-space: nowrap;
transition: background 0.15s ease, transform 0.05s ease;
}
.ns__btn:hover { background: var(--red); border-color: var(--red); }
.ns__btn:active { transform: translateY(1px); }
.ns__btn:disabled { opacity: 0.6; cursor: progress; }
.ns__error {
font: 600 12px/1.3 var(--sans);
color: var(--danger);
margin: 8px 0 0;
}
.ns__freq { border: 0; margin: 0 0 14px; padding: 0; }
.ns__freq-legend {
font: 700 10px/1 var(--sans);
letter-spacing: 0.16em;
text-transform: uppercase;
color: var(--muted);
margin: 0 0 8px;
padding: 0;
}
.ns__chips { display: flex; gap: 8px; }
.ns__chip {
font: 600 0.85rem/1 var(--sans);
color: var(--ink-2);
background: var(--white);
border: 1px solid var(--rule-2);
border-radius: 999px;
padding: 8px 18px;
cursor: pointer;
transition: background 0.12s ease, color 0.12s ease, border-color 0.12s ease;
}
.ns__chip:hover { border-color: var(--ink-3); }
.ns__chip.is-active {
background: var(--ink);
color: var(--paper);
border-color: var(--ink);
}
.ns__privacy {
font: 500 12px/1.4 var(--sans);
color: var(--muted);
margin: 0;
}
.ns__count {
font: 500 13px/1.4 var(--sans);
color: var(--ink-3);
margin: 16px 0 0;
padding-top: 14px;
border-top: 1px solid var(--rule-hair);
}
.ns__count [data-ns-count] {
font-weight: 700;
color: var(--ink);
font-variant-numeric: tabular-nums;
}
/* success collapse */
[data-newsletter].is-done .ns__form,
[data-newsletter].is-done .nsc__form { display: none; }
.ns__done,
.nsc__done {
font-family: var(--serif);
font-weight: 600;
color: var(--ok);
margin: 4px 0 0;
}
.ns__done { font-size: 1.15rem; }
.nsc__done { font-size: 1rem; }
/* ============ SIDEBAR ============ */
.rail { display: flex; flex-direction: column; gap: 26px; }
.rail__block { }
.rail__title {
font: 700 12px/1 var(--sans);
letter-spacing: 0.18em;
text-transform: uppercase;
color: var(--ink);
margin: 0 0 12px;
padding-bottom: 8px;
border-bottom: 2px solid var(--ink);
}
.rail__list { list-style: none; margin: 0; padding: 0; }
.rail__list li {
display: flex;
gap: 12px;
font-family: var(--serif);
font-weight: 600;
font-size: 1.02rem;
line-height: 1.22;
color: var(--ink-2);
padding: 12px 0;
border-bottom: 1px solid var(--rule-hair);
}
.rail__num {
font-family: var(--serif);
font-weight: 800;
font-size: 1.5rem;
line-height: 1;
color: var(--red);
flex: none;
}
/* ===== COMPACT SIGNUP (variant B) ===== */
.nsc {
background: var(--ink);
color: var(--newsprint);
border-radius: var(--r-md);
padding: 22px 20px 18px;
}
.nsc__kicker {
font: 700 10px/1 var(--sans);
letter-spacing: 0.22em;
text-transform: uppercase;
color: #e6a39d;
margin: 0 0 8px;
}
.nsc__head {
font-family: var(--serif);
font-weight: 700;
font-size: 1.4rem;
line-height: 1.08;
margin: 0 0 6px;
color: var(--white);
}
.nsc__prop {
font-size: 0.86rem;
color: rgba(244, 239, 228, 0.78);
margin: 0 0 14px;
}
.nsc__label { color: rgba(244, 239, 228, 0.6); }
.nsc__input {
width: 100%;
font: 500 0.95rem/1.2 var(--sans);
color: var(--ink);
background: var(--paper);
border: 1px solid transparent;
border-radius: var(--r-sm);
padding: 11px 13px;
}
.nsc__input:focus-visible { outline: 2px solid #e6a39d; outline-offset: 1px; }
.nsc__input.is-invalid { border-color: #e6a39d; background: #fbf1f0; }
.nsc__error { color: #f0b7b2; margin: 7px 0 0; }
.nsc__chips { display: flex; gap: 7px; margin: 13px 0; }
.nsc .ns__chip {
background: transparent;
color: var(--newsprint);
border-color: rgba(244, 239, 228, 0.32);
padding: 7px 15px;
font-size: 0.8rem;
}
.nsc .ns__chip:hover { border-color: rgba(244, 239, 228, 0.6); }
.nsc .ns__chip.is-active {
background: var(--red);
color: var(--white);
border-color: var(--red);
}
.nsc__btn {
width: 100%;
font: 700 0.92rem/1 var(--sans);
letter-spacing: 0.04em;
color: var(--white);
background: var(--red);
border: 1px solid var(--red);
border-radius: var(--r-sm);
padding: 12px;
cursor: pointer;
transition: background 0.15s ease, transform 0.05s ease;
}
.nsc__btn:hover { background: var(--red-d); }
.nsc__btn:active { transform: translateY(1px); }
.nsc__btn:disabled { opacity: 0.6; cursor: progress; }
.nsc__privacy {
font: 500 11px/1.4 var(--sans);
color: rgba(244, 239, 228, 0.6);
margin: 10px 0 0;
text-align: center;
}
.nsc__count {
font: 600 11px/1 var(--sans);
letter-spacing: 0.1em;
text-transform: uppercase;
color: rgba(244, 239, 228, 0.7);
margin: 14px 0 0;
padding-top: 12px;
border-top: 1px solid rgba(244, 239, 228, 0.16);
text-align: center;
}
.nsc__count [data-ns-count] {
color: #e6a39d;
font-variant-numeric: tabular-nums;
}
/* weather block */
.rail__weather-line {
font: 500 0.95rem/1.4 var(--sans);
color: var(--ink-2);
margin: 0 0 4px;
}
.rail__weather-line strong { color: var(--red); }
.rail__weather-note {
font: italic 500 13px/1.4 var(--serif);
color: var(--muted);
margin: 8px 0 0;
}
/* ============ FOOTER ============ */
.footer {
max-width: 1080px;
margin: 30px auto 0;
padding: 18px 24px 40px;
border-top: 3px double var(--rule-2);
text-align: center;
font: 500 12px/1.5 var(--sans);
color: var(--muted);
}
/* ============ TOAST ============ */
.toast {
position: fixed;
left: 50%;
bottom: 26px;
transform: translate(-50%, 130%);
background: var(--ink);
color: var(--paper);
font: 600 14px/1.3 var(--sans);
padding: 13px 20px;
border-radius: var(--r-sm);
border-left: 4px solid var(--red);
box-shadow: 0 8px 24px rgba(22, 19, 15, 0.28);
z-index: 60;
max-width: min(420px, 90vw);
opacity: 0;
transition: transform 0.32s cubic-bezier(0.2, 0.8, 0.2, 1), opacity 0.32s ease;
}
.toast.is-on { transform: translate(-50%, 0); opacity: 1; }
/* ============ RESPONSIVE ============ */
@media (max-width: 900px) {
.layout { grid-template-columns: 1fr; gap: 36px; }
.rail { max-width: 560px; }
}
@media (max-width: 720px) {
.article__body { column-count: 1; }
.article__body .lead { font-size: 1.06rem; }
}
@media (max-width: 480px) {
.masthead__top { font-size: 10px; }
.masthead__price { display: none; }
.masthead__nav { gap: 16px; }
.ns { padding: 22px 18px 18px; }
.ns__inputrow { flex-direction: column; }
.ns__btn { padding: 12px; }
}
@media (max-width: 360px) {
.layout { padding: 24px 14px 8px; }
.masthead { padding: 14px 14px 0; }
}
@media (prefers-reduced-motion: reduce) {
* { transition-duration: 0.01ms !important; }
}(function () {
"use strict";
/* ---------- toast helper ---------- */
var toastEl = document.querySelector("[data-toast]");
var toastTimer = null;
function toast(msg) {
if (!toastEl) return;
toastEl.textContent = msg;
toastEl.classList.add("is-on");
window.clearTimeout(toastTimer);
toastTimer = window.setTimeout(function () {
toastEl.classList.remove("is-on");
}, 3400);
}
/* ---------- shared subscriber count (keeps both widgets in sync) ---------- */
var EMAIL_RE = /^[^\s@]+@[^\s@]+\.[^\s@]{2,}$/;
var counters = Array.prototype.slice.call(document.querySelectorAll("[data-ns-count]"));
var subscribers = 18402;
function renderCount() {
var label = subscribers.toLocaleString("en-US");
counters.forEach(function (el) {
// compact widget shows only the number, no "readers" suffix here
el.textContent = label;
});
}
renderCount();
function bumpCount() {
subscribers += 1;
renderCount();
// small tick animation
counters.forEach(function (el) {
el.style.transition = "color .2s ease";
el.style.color = "var(--ok)";
window.setTimeout(function () {
el.style.color = "";
}, 600);
});
}
/* ---------- wire up each newsletter widget ---------- */
var widgets = document.querySelectorAll("[data-newsletter]");
widgets.forEach(function (widget) {
var form = widget.querySelector("[data-ns-form]");
if (!form) return;
var input = form.querySelector('input[type="email"]');
var error = form.querySelector('.ns__error, .nsc__error');
var submitBtn = form.querySelector('button[type="submit"]');
var chips = Array.prototype.slice.call(form.querySelectorAll(".ns__chip"));
var freqValue = form.querySelector("[data-freq-value]");
/* frequency chips behave as a radio group */
chips.forEach(function (chip) {
chip.addEventListener("click", function () {
chips.forEach(function (c) {
c.classList.remove("is-active");
c.setAttribute("aria-checked", "false");
});
chip.classList.add("is-active");
chip.setAttribute("aria-checked", "true");
if (freqValue) freqValue.value = chip.getAttribute("data-freq");
});
/* keyboard: arrow keys move between chips */
chip.addEventListener("keydown", function (e) {
var idx = chips.indexOf(chip);
var next = null;
if (e.key === "ArrowRight" || e.key === "ArrowDown") next = chips[(idx + 1) % chips.length];
if (e.key === "ArrowLeft" || e.key === "ArrowUp") next = chips[(idx - 1 + chips.length) % chips.length];
if (next) {
e.preventDefault();
next.focus();
next.click();
}
});
});
function clearError() {
if (!error) return;
error.hidden = true;
error.textContent = "";
if (input) input.classList.remove("is-invalid");
}
function showError(msg) {
if (error) {
error.hidden = false;
error.textContent = msg;
}
if (input) {
input.classList.add("is-invalid");
input.focus();
}
}
if (input) input.addEventListener("input", clearError);
form.addEventListener("submit", function (e) {
e.preventDefault();
var value = input ? input.value.trim() : "";
if (!value) {
showError("Please enter your email address.");
return;
}
if (!EMAIL_RE.test(value)) {
showError("That doesn’t look like a valid email — check for typos.");
return;
}
clearError();
var freq = freqValue ? freqValue.value : "daily";
var freqLabel = freq === "weekly" ? "weekly" : "daily";
/* simulate request */
if (submitBtn) {
submitBtn.disabled = true;
submitBtn.dataset.label = submitBtn.textContent;
submitBtn.textContent = "Subscribing…";
}
window.setTimeout(function () {
widget.classList.add("is-done");
/* inject a success line in place of the form */
var isCompact = widget.classList.contains("nsc");
var done = document.createElement("p");
done.className = isCompact ? "nsc__done" : "ns__done";
done.setAttribute("role", "status");
done.textContent = "✓ You’re in — the " + freqLabel + " brief is on its way.";
var anchor = form;
anchor.parentNode.insertBefore(done, anchor);
bumpCount();
toast("Subscribed to The Morning Brief (" + freqLabel + "). Check your inbox to confirm.");
}, 650);
});
});
})();<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>The Tidewater Chronicle — The Morning Brief</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="#main">Skip to content</a>
<!-- ============ MASTHEAD ============ -->
<header class="masthead" role="banner">
<div class="masthead__top">
<span class="masthead__edition">Coastal Edition</span>
<span class="masthead__date">Monday, June 8, 2026</span>
<span class="masthead__price">$3.00</span>
</div>
<a class="masthead__title" href="#main" aria-label="The Tidewater Chronicle home">
The Tidewater Chronicle
</a>
<p class="masthead__tag">Reporting the harbor since 1911 — fiercely, fairly, daily.</p>
<nav class="masthead__nav" aria-label="Sections">
<a href="#main">Front Page</a>
<a href="#main">Politics</a>
<a href="#main">Harbor</a>
<a href="#main">Culture</a>
<a href="#main">Opinion</a>
</nav>
</header>
<main id="main" class="layout">
<!-- ============ ARTICLE COLUMN ============ -->
<article class="article" aria-labelledby="story-head">
<p class="kicker">Harbor & Tides</p>
<h1 class="article__head" id="story-head">
After Forty Years, the Tide Clock on Pier Nine Keeps Time Again
</h1>
<p class="article__deck">
A retired machinist, a box of brass fittings, and a stubborn refusal to let the
old harbor lose its heartbeat.
</p>
<div class="byline">
<span class="byline__name">By Marisol Trevino</span>
<span class="byline__dot" aria-hidden="true">•</span>
<span class="byline__date">Pier Nine, Tidewater</span>
<span class="byline__dot" aria-hidden="true">•</span>
<span class="byline__read">7 min read</span>
</div>
<figure class="figure">
<div class="figure__img figure__img--harbor" role="img"
aria-label="The brass face of an antique tide clock catching low morning light against weathered dock pilings."></div>
<figcaption class="figure__cap">
<em>The restored tide clock at dawn, its brass face freshly turned.</em>
<span class="figure__credit">Photograph — Dell Marrow / The Chronicle</span>
</figcaption>
</figure>
<div class="article__body">
<p class="lead">
For most of the last four decades, the tide clock bolted to the head of Pier Nine
read the same wrong hour — frozen at a quarter to six, a small civic embarrassment
that nobody quite knew how to fix. The harbormaster called it folklore. The gulls
called it a perch. On the first Monday of June, at precisely high water, the
brass hands began to move.
</p>
<p>
The repair is the work of Esther Quill, seventy-three, a machinist who spent her
career fitting marine gearboxes two streets over and who, by her own account, had
"walked past that dead clock one too many times." She arrived in March with a
canvas roll of tools and a coffee can of salvaged fittings, set up a folding stool,
and went to work where the whole town could watch.
</p>
<!-- ===== INLINE NEWSLETTER SIGNUP (variant A) ===== -->
<aside class="ns" data-newsletter aria-labelledby="ns-head">
<div class="ns__rule" aria-hidden="true"></div>
<p class="ns__kicker">The Morning Brief</p>
<h2 class="ns__head" id="ns-head">The harbor, in your inbox before the coffee’s cool.</h2>
<p class="ns__prop">
One tight read — the stories shaping Tidewater, hand-picked by our newsroom.
</p>
<form class="ns__form" novalidate data-ns-form>
<div class="ns__field">
<label class="ns__label" for="ns-email">Email address</label>
<div class="ns__inputrow">
<input
class="ns__input"
id="ns-email"
name="email"
type="email"
inputmode="email"
autocomplete="email"
placeholder="[email protected]"
aria-describedby="ns-error ns-privacy"
required
/>
<button class="ns__btn" type="submit">Subscribe</button>
</div>
<p class="ns__error" id="ns-error" role="alert" hidden></p>
</div>
<fieldset class="ns__freq">
<legend class="ns__freq-legend">How often?</legend>
<div class="ns__chips" role="radiogroup" aria-label="Delivery frequency">
<button type="button" class="ns__chip is-active" role="radio"
aria-checked="true" data-freq="daily">Daily</button>
<button type="button" class="ns__chip" role="radio"
aria-checked="false" data-freq="weekly">Weekly</button>
</div>
<input type="hidden" name="frequency" value="daily" data-freq-value />
</fieldset>
<p class="ns__privacy" id="ns-privacy">
No spam, ever. Unsubscribe in one click. We never sell your address.
</p>
</form>
<p class="ns__count">
Joined by <span data-ns-count>18,402</span> Tidewater readers.
</p>
</aside>
<!-- ===== /inline signup ===== -->
<p>
Restoring the mechanism meant machining three replacement gears by hand, each no
larger than a coat button, and coaxing the float linkage — the part that actually
reads the rising and falling water — back into true. "The tide never stopped,"
Quill said, wiping brass polish from her thumb. "Only the clock did. My job was
just to introduce them again."
</p>
<blockquote class="pullquote">
“A town that can’t tell its own tide has forgotten how to listen to the water.”
<cite>— Esther Quill, machinist</cite>
</blockquote>
<p>
City records suggest the clock was installed in 1948 and last serviced sometime
before the harbor's commercial fleet thinned in the eighties. Funding for the
fix amounted to exactly nothing; Quill paid for the brass herself and waved off
a collection the dockworkers tried to take up. What she accepted instead was a
standing invitation to the Tuesday fish fry and, she noted dryly, "first dibs on
the next thing around here that quits."
</p>
<p>
By midmorning a small crowd had formed at the pier head, watching the hands ease
toward the afternoon ebb. A deckhand checked the clock against his phone, grinned,
and put the phone away. For the first time in forty years, the harbor was keeping
its own time again — and, for once, keeping it right.
</p>
</div>
</article>
<!-- ============ SIDEBAR ============ -->
<aside class="rail" aria-label="Sidebar">
<section class="rail__block" aria-labelledby="most-read">
<h2 class="rail__title" id="most-read">Most Read</h2>
<ol class="rail__list">
<li><span class="rail__num">1</span> Council votes to repave the boardwalk before autumn storms</li>
<li><span class="rail__num">2</span> A ferry captain’s last crossing, and the route she leaves behind</li>
<li><span class="rail__num">3</span> The oyster beds are recovering — slowly, and against the odds</li>
<li><span class="rail__num">4</span> Why the lighthouse keeper still climbs the stairs by hand</li>
</ol>
</section>
<!-- ===== COMPACT NEWSLETTER SIGNUP (variant B) ===== -->
<aside class="nsc" data-newsletter aria-labelledby="nsc-head">
<p class="nsc__kicker">The Morning Brief</p>
<h2 class="nsc__head" id="nsc-head">Get the harbor daily.</h2>
<p class="nsc__prop">The newsroom’s essential read, weekday mornings.</p>
<form class="nsc__form" novalidate data-ns-form>
<label class="ns__label nsc__label" for="nsc-email">Email address</label>
<input
class="nsc__input"
id="nsc-email"
name="email"
type="email"
inputmode="email"
autocomplete="email"
placeholder="[email protected]"
aria-describedby="nsc-error"
required
/>
<p class="ns__error nsc__error" id="nsc-error" role="alert" hidden></p>
<div class="nsc__chips" role="radiogroup" aria-label="Delivery frequency">
<button type="button" class="ns__chip is-active" role="radio"
aria-checked="true" data-freq="daily">Daily</button>
<button type="button" class="ns__chip" role="radio"
aria-checked="false" data-freq="weekly">Weekly</button>
</div>
<input type="hidden" name="frequency" value="daily" data-freq-value />
<button class="nsc__btn" type="submit">Subscribe</button>
<p class="nsc__privacy">One-click unsubscribe. No spam.</p>
</form>
<p class="nsc__count"><span data-ns-count>18,402</span> readers</p>
</aside>
<!-- ===== /compact signup ===== -->
<section class="rail__block rail__weather" aria-labelledby="tide-head">
<h2 class="rail__title" id="tide-head">Today’s Tides</h2>
<p class="rail__weather-line"><strong>High</strong> 6:14 a.m. · 9:02 p.m.</p>
<p class="rail__weather-line"><strong>Low</strong> 12:48 p.m.</p>
<p class="rail__weather-note">Light fog clearing by ten. Winds easterly, calm seas.</p>
</section>
</aside>
</main>
<footer class="footer">
<p>The Tidewater Chronicle — a wholly fictional publication. © 2026 Tidewater Press.</p>
</footer>
<!-- toast region -->
<div class="toast" data-toast role="status" aria-live="polite"></div>
<script src="script.js"></script>
</body>
</html>Inline Newsletter Signup
Two newsletter signup modules dropped into a fully art-directed front page for The Tidewater Chronicle, a wholly fictional harbor daily. The lead variant is an inline-in-article block: a thin red rule across the top, a let-spaced The Morning Brief kicker, a Playfair Display headline, a one-line value prop, an email field paired with a Subscribe button, Daily and Weekly frequency chips, a tiny privacy reassurance and a live subscriber-count line. It sits between the columns of a real-feeling news story, complete with a drop cap, a pull quote and a captioned duotone press photo simulated entirely in CSS. A second compact, ink-dark variant lives in the sidebar for tighter rails.
Both forms share the same vanilla JavaScript. Submitting validates the email inline (empty and
malformed addresses get an accessible error and a focus return), the frequency chips act as a
keyboard-navigable radio group, and a successful subscribe collapses the form into a serif success
line, fires a toast, and ticks the subscriber count up by one across every widget at once. There
are no frameworks, no network calls and no images — just rules, type and a small toast() helper.
Illustrative UI only — masthead, headlines, bylines, and articles are fictional; not a real news publication.