News — Two-page Spread
A fictional long-read magazine feature laid out as a true two-page spread: an oversized Playfair Display headline crosses the center gutter, a duotone full-bleed press photo anchors one page while a multi-column body carries a red drop cap and an oversized serif pull quote on the other. Running folios, a kicker, byline with dateline and read-time, and a newsprint fact box complete the art direction. Flip between spreads and toggle a column-grid overlay, all in vanilla JS.
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;
}
* {
box-sizing: border-box;
}
html {
-webkit-text-size-adjust: 100%;
}
body {
margin: 0;
padding: clamp(16px, 4vw, 48px) clamp(12px, 4vw, 40px) 64px;
font-family: var(--sans);
font-size: 16px;
line-height: 1.5;
color: var(--ink);
background-color: var(--cream);
background-image:
radial-gradient(circle at 20% 10%, rgba(180, 41, 31, 0.04), transparent 40%),
radial-gradient(circle at 90% 80%, rgba(22, 19, 15, 0.05), transparent 45%);
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
/* ============ TOOLBAR ============ */
.toolbar {
max-width: 1180px;
margin: 0 auto 22px;
display: flex;
align-items: center;
gap: 12px;
padding: 10px 14px;
background: var(--paper);
border: 1px solid var(--rule);
border-radius: var(--r-md);
flex-wrap: wrap;
}
.toolbar__brand {
font-family: var(--serif);
font-weight: 800;
font-size: 18px;
letter-spacing: 0.01em;
}
.toolbar__sep {
width: 1px;
height: 20px;
background: var(--rule);
}
.toolbar__issue {
font-size: 12px;
letter-spacing: 0.14em;
text-transform: uppercase;
color: var(--muted);
font-weight: 600;
}
.toolbar__actions {
margin-left: auto;
display: flex;
gap: 8px;
flex-wrap: wrap;
}
.btn {
font-family: var(--sans);
font-size: 12px;
font-weight: 600;
letter-spacing: 0.08em;
text-transform: uppercase;
color: var(--ink-2);
background: var(--white);
border: 1px solid var(--rule-2);
border-radius: var(--r-sm);
padding: 8px 14px;
cursor: pointer;
transition: background 0.15s ease, color 0.15s ease, border-color 0.15s ease,
transform 0.05s ease;
}
.btn:hover {
background: var(--newsprint);
border-color: var(--ink-3);
}
.btn:active {
transform: translateY(1px);
}
.btn:focus-visible {
outline: 2px solid var(--red);
outline-offset: 2px;
}
.btn--ghost {
background: transparent;
}
.btn--accent {
background: var(--red);
color: var(--white);
border-color: var(--red-d);
}
.btn--accent:hover {
background: var(--red-d);
color: var(--white);
}
.btn[aria-pressed="true"] {
background: var(--ink);
color: var(--paper);
border-color: var(--ink);
}
/* ============ MAGAZINE / SPREAD ============ */
.magazine {
max-width: 1180px;
margin: 0 auto;
}
.spread {
position: relative;
}
.leaf {
position: relative;
display: grid;
grid-template-columns: 1fr 1fr;
background: var(--paper);
border: 1px solid var(--rule);
border-radius: var(--r-md);
box-shadow: 0 1px 0 rgba(22, 19, 15, 0.06);
overflow: hidden;
animation: leafIn 0.35s ease both;
}
.leaf[hidden] {
display: none;
}
@keyframes leafIn {
from {
opacity: 0;
transform: translateY(8px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
/* center gutter */
.gutter {
position: absolute;
top: 0;
bottom: 0;
left: 50%;
width: 36px;
transform: translateX(-50%);
pointer-events: none;
background: linear-gradient(
to right,
rgba(22, 19, 15, 0) 0%,
rgba(22, 19, 15, 0.12) 42%,
rgba(22, 19, 15, 0.2) 50%,
rgba(22, 19, 15, 0.12) 58%,
rgba(22, 19, 15, 0) 100%
);
z-index: 4;
}
.page {
position: relative;
padding: clamp(20px, 3vw, 40px);
min-width: 0;
}
.page--left {
border-right: 1px solid var(--rule-hair);
background:
linear-gradient(to right, rgba(22, 19, 15, 0) 92%, rgba(22, 19, 15, 0.04));
}
.page--right {
background:
linear-gradient(to left, rgba(22, 19, 15, 0) 92%, rgba(22, 19, 15, 0.04));
}
/* ============ FOLIOS ============ */
.folio {
display: flex;
align-items: baseline;
gap: 12px;
font-size: 11px;
letter-spacing: 0.16em;
text-transform: uppercase;
color: var(--muted);
font-weight: 600;
}
.folio--top {
padding-bottom: 8px;
margin-bottom: 18px;
border-bottom: 1px solid var(--rule);
}
.folio--top-right {
justify-content: flex-end;
}
.folio__num {
font-family: var(--serif);
font-size: 15px;
font-weight: 700;
letter-spacing: 0;
color: var(--ink-2);
}
.folio__section {
flex: 1;
}
.folio--top-right .folio__section {
flex: 0;
text-align: right;
}
.folio--bottom {
margin-top: 22px;
padding-top: 10px;
border-top: 1px solid var(--rule);
justify-content: space-between;
}
.folio__continue,
.folio__end {
font-style: normal;
color: var(--ink-3);
letter-spacing: 0.1em;
}
.folio__end {
font-size: 10px;
text-transform: none;
letter-spacing: 0.02em;
font-style: italic;
}
/* ============ MASTHEAD / HEADLINE BLOCK ============ */
.kicker {
margin: 0 0 14px;
font-family: var(--sans);
font-size: 12px;
font-weight: 700;
letter-spacing: 0.24em;
text-transform: uppercase;
color: var(--red);
}
.kicker::before {
content: "";
display: inline-block;
width: 26px;
height: 2px;
margin-right: 10px;
vertical-align: middle;
background: var(--red);
}
.headline {
margin: 0 0 20px;
font-family: var(--serif);
font-weight: 900;
font-size: clamp(40px, 7vw, 74px);
line-height: 0.94;
letter-spacing: -0.015em;
color: var(--ink);
}
.headline em {
font-style: italic;
font-weight: 600;
color: var(--red);
}
.standfirst {
margin: 0 0 20px;
font-family: var(--serif);
font-style: italic;
font-weight: 500;
font-size: clamp(17px, 2.2vw, 21px);
line-height: 1.42;
color: var(--ink-2);
max-width: 42ch;
}
.byline {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 8px;
padding: 10px 0;
border-top: 1px solid var(--rule);
border-bottom: 1px solid var(--rule);
font-size: 11px;
letter-spacing: 0.1em;
text-transform: uppercase;
color: var(--ink-3);
margin-bottom: 22px;
}
.byline__name {
font-weight: 700;
color: var(--ink);
}
.byline__dot {
color: var(--rule-2);
}
.byline__read {
color: var(--muted);
}
/* ============ FIGURES / PRESS PHOTOS ============ */
.figure {
margin: 0;
}
.figure--bleed {
margin: 4px 0 0;
}
.press-photo {
width: 100%;
aspect-ratio: 4 / 3;
border-radius: var(--r-sm);
position: relative;
overflow: hidden;
filter: contrast(1.04) saturate(0.92);
}
.figure--tall .press-photo {
aspect-ratio: 3 / 4;
}
/* duotone "shoreline at dusk" */
.press-photo--shore {
background:
linear-gradient(
180deg,
rgba(180, 41, 31, 0.22) 0%,
rgba(74, 68, 59, 0.55) 30%,
rgba(43, 38, 32, 0.85) 58%,
rgba(122, 113, 100, 0.4) 64%,
rgba(244, 239, 228, 0.85) 70%,
rgba(207, 199, 183, 0.95) 100%
),
radial-gradient(
120% 80% at 72% 22%,
rgba(244, 220, 217, 0.7) 0%,
rgba(180, 41, 31, 0.18) 26%,
transparent 55%
),
linear-gradient(90deg, rgba(22, 19, 15, 0.18), rgba(22, 19, 15, 0));
}
/* duotone "stilt house over water" */
.press-photo--house {
background:
linear-gradient(
180deg,
rgba(122, 113, 100, 0.45) 0%,
rgba(207, 199, 183, 0.7) 26%,
rgba(74, 68, 59, 0.7) 46%,
rgba(43, 38, 32, 0.9) 60%,
rgba(74, 68, 59, 0.6) 74%,
rgba(122, 113, 100, 0.5) 100%
),
radial-gradient(
90% 60% at 50% 44%,
rgba(22, 19, 15, 0.55) 0%,
transparent 60%
),
repeating-linear-gradient(
90deg,
rgba(22, 19, 15, 0.06) 0 2px,
transparent 2px 26px
),
linear-gradient(0deg, rgba(180, 41, 31, 0.1), transparent 40%);
}
.figure__cap {
margin-top: 8px;
padding-top: 7px;
border-top: 1px solid var(--rule-hair);
display: flex;
flex-direction: column;
gap: 3px;
}
.figure__cap-text {
font-family: var(--serif);
font-style: italic;
font-size: 13.5px;
line-height: 1.4;
color: var(--ink-3);
}
.figure__credit {
font-size: 10px;
letter-spacing: 0.12em;
text-transform: uppercase;
color: var(--muted);
}
/* ============ COLUMNS / BODY ============ */
.columns {
column-count: 2;
column-gap: 26px;
column-rule: 1px solid var(--rule-hair);
font-size: 14.5px;
line-height: 1.62;
text-align: justify;
hyphens: auto;
color: var(--ink-2);
}
.columns--short {
column-count: 2;
margin-top: 16px;
}
.columns p {
margin: 0 0 0.85em;
text-indent: 1.1em;
}
.columns > p:first-child,
.columns .lead {
text-indent: 0;
}
.lead::first-letter {
float: left;
font-family: var(--serif);
font-weight: 800;
font-size: 4.1em;
line-height: 0.78;
padding: 6px 10px 0 0;
margin-top: 4px;
color: var(--red);
}
.lead--cont::first-letter {
color: var(--ink);
}
.lead {
font-size: 15px;
}
/* ============ PULL QUOTE ============ */
.pullquote {
margin: 4px 0 6px;
padding: 14px 0;
break-inside: avoid;
border-top: 2px solid var(--ink);
border-bottom: 1px solid var(--rule);
}
.pullquote blockquote {
margin: 0;
font-family: var(--serif);
font-weight: 600;
font-style: italic;
font-size: clamp(20px, 2.4vw, 27px);
line-height: 1.18;
letter-spacing: -0.01em;
color: var(--ink);
}
.pullquote blockquote::before {
content: "\201C";
color: var(--red);
font-weight: 800;
}
.pullquote figcaption {
margin-top: 8px;
font-family: var(--sans);
font-size: 11px;
letter-spacing: 0.1em;
text-transform: uppercase;
color: var(--muted);
font-weight: 600;
}
/* ============ FACT BOX ============ */
.factbox {
break-inside: avoid;
margin: 6px 0 10px;
padding: 14px 16px 12px;
background: var(--newsprint);
border: 1px solid var(--rule);
border-top: 3px solid var(--red);
border-radius: var(--r-sm);
}
.factbox__title {
margin: 0 0 10px;
font-family: var(--sans);
font-size: 11px;
font-weight: 700;
letter-spacing: 0.18em;
text-transform: uppercase;
color: var(--red-d);
}
.factbox__list {
margin: 0;
}
.factbox__row {
display: flex;
justify-content: space-between;
align-items: baseline;
gap: 10px;
padding: 6px 0;
border-bottom: 1px solid var(--rule-hair);
}
.factbox__row:last-child {
border-bottom: 0;
}
.factbox__row dt {
font-size: 12.5px;
color: var(--ink-3);
text-indent: 0;
}
.factbox__row dd {
margin: 0;
font-family: var(--serif);
font-weight: 700;
font-size: 16px;
color: var(--ink);
white-space: nowrap;
}
.factbox__src {
margin: 10px 0 0;
font-size: 9.5px;
letter-spacing: 0.1em;
text-transform: uppercase;
color: var(--muted);
text-indent: 0;
}
/* ============ GRID / GUIDES OVERLAY ============ */
.grid-overlay {
position: absolute;
inset: 0;
z-index: 6;
pointer-events: none;
opacity: 0;
transition: opacity 0.2s ease;
background-image: repeating-linear-gradient(
to right,
rgba(180, 41, 31, 0.16) 0,
rgba(180, 41, 31, 0.16) 1px,
transparent 1px,
transparent calc(100% / 12)
);
background-size: 100% 100%;
}
.leaf.show-grid .grid-overlay {
opacity: 1;
}
.leaf.show-grid .grid-overlay::after {
content: "";
position: absolute;
inset: clamp(20px, 3vw, 40px);
border: 1px dashed rgba(180, 41, 31, 0.45);
}
.leaf.show-grid .gutter {
background: linear-gradient(
to right,
rgba(180, 41, 31, 0) 0%,
rgba(180, 41, 31, 0.3) 50%,
rgba(180, 41, 31, 0) 100%
);
}
/* ============ TOAST ============ */
.toast {
position: fixed;
left: 50%;
bottom: 26px;
transform: translate(-50%, 16px);
background: var(--ink);
color: var(--paper);
font-size: 13px;
font-weight: 500;
letter-spacing: 0.02em;
padding: 11px 18px;
border-radius: var(--r-sm);
box-shadow: 0 6px 24px rgba(22, 19, 15, 0.25);
opacity: 0;
pointer-events: none;
transition: opacity 0.25s ease, transform 0.25s ease;
z-index: 50;
max-width: 90vw;
}
.toast.show {
opacity: 1;
transform: translate(-50%, 0);
}
.toast__accent {
color: var(--red-50);
font-weight: 700;
}
/* ============ RESPONSIVE ============ */
@media (max-width: 920px) {
.columns {
column-count: 1;
}
.columns--short {
column-count: 1;
}
.headline {
font-size: clamp(38px, 9vw, 60px);
}
}
@media (max-width: 720px) {
.leaf {
grid-template-columns: 1fr;
}
.gutter {
display: none;
}
.page--left {
border-right: 0;
border-bottom: 2px solid var(--ink);
background: none;
}
.page--right {
background: none;
}
.leaf.show-grid .grid-overlay {
background-image: repeating-linear-gradient(
to right,
rgba(180, 41, 31, 0.16) 0,
rgba(180, 41, 31, 0.16) 1px,
transparent 1px,
transparent calc(100% / 6)
);
}
}
@media (max-width: 480px) {
body {
padding: 14px 10px 56px;
}
.toolbar {
gap: 8px;
}
.toolbar__brand {
font-size: 16px;
}
.toolbar__sep,
.toolbar__issue {
display: none;
}
.toolbar__actions {
margin-left: 0;
width: 100%;
}
.btn {
flex: 1;
padding: 9px 8px;
}
.headline {
font-size: clamp(34px, 12vw, 48px);
}
}
@media (prefers-reduced-motion: reduce) {
.leaf,
.toast,
.grid-overlay {
animation: none;
transition: none;
}
}(function () {
"use strict";
var body = document.body;
var leaves = Array.prototype.slice.call(
document.querySelectorAll(".leaf")
);
var total = leaves.length;
var nextBtn = document.getElementById("nextSpread");
var prevBtn = document.getElementById("prevSpread");
var gridBtn = document.getElementById("gridToggle");
var issueLabel = document.getElementById("issueLabel");
var toastEl = document.getElementById("toast");
var current = 0; // index into leaves
var gridOn = false;
var spreadNames = ["The Long Read", "The Vanishing Coast, cont."];
/* ---- toast helper ---- */
var toastTimer = null;
function toast(msg, accent) {
if (!toastEl) return;
toastEl.innerHTML = accent
? '<span class="toast__accent">' + accent + "</span> " + msg
: msg;
toastEl.classList.add("show");
if (toastTimer) clearTimeout(toastTimer);
toastTimer = setTimeout(function () {
toastEl.classList.remove("show");
}, 2200);
}
/* ---- show a given spread ---- */
function showSpread(index) {
if (index < 0) index = total - 1;
if (index >= total) index = 0;
current = index;
leaves.forEach(function (leaf, i) {
var active = i === current;
leaf.hidden = !active;
leaf.classList.toggle("leaf--active", active);
if (active) {
// restart entrance animation
leaf.style.animation = "none";
// force reflow
void leaf.offsetWidth;
leaf.style.animation = "";
applyGrid(leaf);
}
});
body.setAttribute("data-spread", String(current + 1));
if (issueLabel) {
issueLabel.textContent =
"Issue 214 — " + (spreadNames[current] || "The Long Read");
}
updateNav();
}
function updateNav() {
if (prevBtn) prevBtn.disabled = false;
if (nextBtn) {
nextBtn.firstChild &&
(nextBtn.childNodes[0].nodeValue =
current === total - 1 ? "Back to start " : "Flip spread ");
}
}
/* ---- grid overlay ---- */
function applyGrid(leaf) {
if (!leaf) return;
leaf.classList.toggle("show-grid", gridOn);
}
function toggleGrid() {
gridOn = !gridOn;
gridBtn.setAttribute("aria-pressed", String(gridOn));
leaves.forEach(applyGrid);
toast(gridOn ? "showing column grid & margins" : "guides hidden", "Layout");
}
/* ---- next / prev ---- */
function goNext() {
var atEnd = current === total - 1;
showSpread(current + 1);
toast(
atEnd
? "returned to the opening spread"
: "pages " + (current * 2 + 38) + "–" + (current * 2 + 39),
atEnd ? "Wrapped" : "Spread " + (current + 1)
);
}
function goPrev() {
showSpread(current - 1);
toast(
"pages " + (current * 2 + 38) + "–" + (current * 2 + 39),
"Spread " + (current + 1)
);
}
/* ---- wire up ---- */
if (nextBtn) nextBtn.addEventListener("click", goNext);
if (prevBtn) prevBtn.addEventListener("click", goPrev);
if (gridBtn) gridBtn.addEventListener("click", toggleGrid);
/* ---- keyboard navigation ---- */
document.addEventListener("keydown", function (e) {
if (e.target && /^(INPUT|TEXTAREA|SELECT)$/.test(e.target.tagName)) return;
if (e.key === "ArrowRight" || e.key === "PageDown") {
e.preventDefault();
goNext();
} else if (e.key === "ArrowLeft" || e.key === "PageUp") {
e.preventDefault();
goPrev();
} else if (e.key === "g" || e.key === "G") {
toggleGrid();
}
});
/* ---- init ---- */
showSpread(0);
setTimeout(function () {
toast("use ‹ › or the arrow keys · press G for guides", "The Meridian Review");
}, 600);
})();<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>The Meridian Review — Two-page Spread</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 data-spread="1">
<!-- Reading-room toolbar -->
<div class="toolbar" role="toolbar" aria-label="Spread controls">
<span class="toolbar__brand">The Meridian Review</span>
<span class="toolbar__sep" aria-hidden="true"></span>
<span class="toolbar__issue" id="issueLabel">Issue 214 — The Long Read</span>
<div class="toolbar__actions">
<button class="btn" id="gridToggle" type="button" aria-pressed="false">
Layout guides
</button>
<button class="btn btn--ghost" id="prevSpread" type="button">
<span aria-hidden="true">‹</span> Prev
</button>
<button class="btn btn--accent" id="nextSpread" type="button">
Flip spread <span aria-hidden="true">›</span>
</button>
</div>
</div>
<!-- The magazine -->
<main class="magazine">
<div class="spread" id="spread" aria-live="polite">
<!-- ============ SPREAD 1 ============ -->
<section class="leaf leaf--active" data-leaf="1" aria-label="Spread one">
<div class="grid-overlay" aria-hidden="true"></div>
<div class="gutter" aria-hidden="true"></div>
<!-- LEFT PAGE -->
<article class="page page--left">
<header class="folio folio--top">
<span class="folio__num">38</span>
<span class="folio__section">Feature · Environment</span>
</header>
<p class="kicker">The Vanishing Coast</p>
<h1 class="headline">
Where the<br />Tide Keeps<br /><em>Its Ledger</em>
</h1>
<p class="standfirst">
On a thinning sandbar off the Caulder Strait, a community of
three hundred is learning to read the sea the way their
grandparents once read the sky — and to bargain with a
shoreline that no longer keeps its promises.
</p>
<div class="byline">
<span class="byline__name">By Marguerite Halloway</span>
<span class="byline__dot" aria-hidden="true">•</span>
<span class="byline__place">CAULDER STRAIT</span>
<span class="byline__dot" aria-hidden="true">•</span>
<span class="byline__read">14 min read</span>
</div>
<figure class="figure figure--bleed">
<div class="press-photo press-photo--shore" role="img"
aria-label="A low grey shoreline at dusk, water meeting a thin band of pale sand."></div>
<figcaption class="figure__cap">
<span class="figure__cap-text">Dawn over the eastern flats, where
the high-water mark has advanced forty metres in a decade.</span>
<span class="figure__credit">Photograph · J. Okonkwo for The Meridian Review</span>
</figcaption>
</figure>
</article>
<!-- RIGHT PAGE -->
<article class="page page--right">
<header class="folio folio--top folio--top-right">
<span class="folio__section">The Meridian Review</span>
<span class="folio__num">39</span>
</header>
<div class="columns">
<p class="lead">
The boat leaves before light. By the time the engine settles
into its long flat drone, the village behind us has dissolved
into a single yellow window and the smell of woodsmoke, and
there is nothing ahead but the grey seam where the water
meets a sky the same colour as the water. Idris Vale has made
this crossing eleven thousand times, give or take, and he no
longer needs the markers his father planted. He reads the
current, the chop, the particular hush before a swell, the
way a city driver reads a yellow light.
</p>
<p>
“The sea was never our enemy,” he says, not turning
from the tiller. “An enemy you can fight. The sea is more
like a landlord. It raises the rent and you find a way to pay,
or you go.” In the last twelve years the families of
Saltmere have paid in pasture, in two cemeteries, in the old
schoolhouse whose foundation now serves as a mooring at high
tide. What they have not done, against every actuarial
prediction, is go.
</p>
<figure class="pullquote">
<blockquote>
It raises the rent and you find a way to pay, or you go.
</blockquote>
<figcaption>— Idris Vale, harbour pilot, age 61</figcaption>
</figure>
<p>
The arithmetic of staying is brutal and intimate. A managed
retreat would relocate everyone to the mainland within three
seasons; the regional authority has offered grants, a cul-de-sac
of new houses, jobs at the cannery forty minutes inland. Most
villages along the strait took the deal a decade ago. Saltmere
held a vote in the long room above the chandlery and the count
was not close.
</p>
<p>
Instead they have improvised an architecture of refusal. Houses
raised on stilts of reclaimed keel timber. A floating allotment
of salt-tolerant kale and sea beet that rises with the spring
tides. A tide-gauge built by a retired engineer named Solveig
Marr, who reads it each dawn and chalks the figure on the harbour
wall the way another town might post the weather.
</p>
<aside class="factbox" aria-label="By the numbers">
<h2 class="factbox__title">By the numbers</h2>
<dl class="factbox__list">
<div class="factbox__row">
<dt>Residents remaining</dt><dd>301</dd>
</div>
<div class="factbox__row">
<dt>High-water advance, 2014–24</dt><dd>40 m</dd>
</div>
<div class="factbox__row">
<dt>Homes raised on stilts</dt><dd>62</dd>
</div>
<div class="factbox__row">
<dt>Relocation grants declined</dt><dd>118</dd>
</div>
<div class="factbox__row">
<dt>Average tide reading, June</dt><dd>2.41 m</dd>
</div>
</dl>
<p class="factbox__src">Source · Caulder Strait Hydrographic Log</p>
</aside>
<p>
By noon the fog has burned off and the flats reveal themselves —
a shining country of channels and bars that exists for six hours
and then is repossessed. Children walk out onto it to dig for
clams, watched by mothers who know the tide tables the way other
parents know bus timetables. There is a margin of error here,
and everyone has memorised it.
</p>
</div>
<footer class="folio folio--bottom">
<span class="folio__continue">Continued overleaf →</span>
</footer>
</article>
</section>
<!-- ============ SPREAD 2 ============ -->
<section class="leaf" data-leaf="2" aria-label="Spread two" hidden>
<div class="grid-overlay" aria-hidden="true"></div>
<div class="gutter" aria-hidden="true"></div>
<!-- LEFT PAGE -->
<article class="page page--left">
<header class="folio folio--top">
<span class="folio__num">40</span>
<span class="folio__section">Feature · Environment</span>
</header>
<div class="columns">
<p class="lead lead--cont">
Solveig Marr keeps her records in a ledger bound in oilcloth,
one line per dawn, and she will tell you without sentiment that
the trend is a curve, not a line. “People want a number
they can argue with,” she says. “The sea doesn’t
argue. It accumulates.” Her tide-gauge is the village’s
quiet oracle, and its readings have begun to draw scientists who
once would not have found Saltmere on a chart.
</p>
<p>
Among them is Dr. Petra Anand, an oceanographer who spent two
winters here measuring how the raised houses change the way water
moves through the village at flood. Her unexpected finding — that
a settlement can be engineered to let the sea pass through rather
than break against it — has begun to circulate in journals under
a phrase the villagers find amusing: <em>porous habitation</em>.
</p>
<figure class="pullquote pullquote--left">
<blockquote>
You cannot hold the line. You can teach the line to move.
</blockquote>
<figcaption>— Dr. Petra Anand, oceanographer</figcaption>
</figure>
<p>
The word has travelled further than anyone expected. A delegation
from a sinking delta city visited in spring, photographing the
stilts and the floating allotment, asking Solveig to explain her
chalk marks twice. Saltmere, which spent a decade being told it
was a cautionary tale, has begun to suspect it might be a
prototype.
</p>
</div>
<footer class="folio folio--bottom">
<span class="folio__continue">The Vanishing Coast</span>
</footer>
</article>
<!-- RIGHT PAGE -->
<article class="page page--right">
<header class="folio folio--top folio--top-right">
<span class="folio__section">The Meridian Review</span>
<span class="folio__num">41</span>
</header>
<figure class="figure figure--bleed figure--tall">
<div class="press-photo press-photo--house" role="img"
aria-label="A small house raised on weathered timber stilts above shallow tidal water."></div>
<figcaption class="figure__cap">
<span class="figure__cap-text">The Vale house at high tide;
the family eats supper while the strait moves beneath the floor.</span>
<span class="figure__credit">Photograph · J. Okonkwo for The Meridian Review</span>
</figcaption>
</figure>
<div class="columns columns--short">
<p>
On the last evening the tide comes in fast and silver, and from
the Vale kitchen you can watch it claim the road, the wall, the
chalked figure of the morning. Idris sets a lamp in the window —
not for himself, he says, but so the channel keeps a shape after
dark. Below the floorboards the water knocks once, twice, and is
still.
</p>
<p>
“They keep asking when we’ll leave,” he says.
“Wrong question. Ask the water. It’s the one with
somewhere to be.” Outside, the lamp doubles itself on the
flood, and the village holds its narrow ground for one more night,
keeping its own ledger against the tide.
</p>
</div>
<footer class="folio folio--bottom">
<span class="folio__end">∎ Marguerite Halloway is a contributing
editor at The Meridian Review.</span>
</footer>
</article>
</section>
</div>
</main>
<div class="toast" id="toast" role="status" aria-live="polite"></div>
<script src="script.js"></script>
</body>
</html>Two-page Spread
A print-style feature spread for the fictional Meridian Review, built as two facing pages divided by a soft center gutter. The opening lands like a real magazine: an uppercase red kicker, a towering Playfair Display headline that breaks across lines and leans into an italic accent, an italic standfirst, and a byline carrying dateline and read-time meta over hairline rules. A duotone, gradient-built “press photo” sits full-bleed on the left page with an italic caption and a credit line, while the facing page runs a justified two-column body with a hyphenated lead, a red drop cap, an oversized serif pull quote, and a newsprint By the numbers fact box.
Two controls drive the page. Flip spread (and the ‹ / › buttons, or the arrow keys) advances through the feature’s facing-page layouts, updating the running folios, section labels, and issue strap; the second spread reverses the photo and column arrangement so the rhythm stays varied. Layout guides overlays a twelve-column grid, page margins, and a highlighted gutter so the underlying structure is visible — handy for seeing how the headline and figures snap to the grid.
Everything is self-contained: no images load over the network — the photography is simulated with layered linear and radial gradients in a duotone ink-and-red treatment. The layout collapses from facing pages to a single stacked column under ~720px and stays legible down to ~360px, with a small toast helper narrating each interaction.
Illustrative UI only — masthead, headlines, bylines, and articles are fictional; not a real news publication.