/*
Theme Name: Bunting
Theme URI: https://joebunting.com
Author: Joe Bunting
Description: Hand-built classic theme for joebunting.com. Serif-led, no horizontal rules, light and dark themes, no page builder. Replaces Divi.
Version: 1.0.0
Requires at least: 6.0
Requires PHP: 8.1
License: GNU General Public License v2 or later
License URI: http://www.gnu.org/licenses/gpl-2.0.html
Text Domain: bunting
*/

/* ==========================================================================
   1. Tokens
   Light palette lives on bare :root. Dark is redefined in the media query
   AND under [data-theme="dark"] so an explicit choice wins either way.
   Never declare a color only inside a media query.
   ========================================================================== */

:root {
	/* Ground & surface
	   Cream is The Write Practice's #FAF5EF, so the two properties read as
	   one family. White is the alternating band, as it is on TWP. */
	--ground: #faf5ef;
	--surface: #ffffff;
	--surface-sunk: #f1ebe2;

	/* Ink. Tuned for the cream ground, which is darker than plain white —
	   the earlier values sat just under AA once the background warmed up. */
	--ink: #0f172a;
	--ink-soft: #475569;
	--ink-muted: #646f82;

	/* Brand
	   --brand is the fill colour. --on-brand is white, matching the orange
	   buttons on thewritepractice.com. Note this is a deliberate brand choice
	   over a strict accessibility one: white on #ff6633 is 2.92:1, below the
	   4.5:1 AA needs for normal text. See the note on .btn--primary.
	   --accent-text is small accent-coloured text only (AA on --ground). */
	--brand: #ff6633;
	--brand-hover: #f2551f;
	--on-brand: #ffffff;
	--accent-text: #c44813;

	/* Hairlines. Used inside data tables only — never as a section divider. */
	--rule: #e8e0d5;

	/* Focus */
	--focus: #ff6633;

	/* Type */
	--font-serif: "Crimson Pro", "Iowan Old Style", Charter, Georgia, serif;
	--font-sans: Inter, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;

	/* Type scale */
	--t-xs: 0.75rem;
	--t-sm: 0.875rem;
	--t-base: 1.125rem;
	--t-lg: 1.3125rem;
	--t-xl: clamp(1.5rem, 2.4vw, 1.875rem);
	--t-2xl: clamp(1.875rem, 3.4vw, 2.5rem);
	--t-3xl: clamp(2.5rem, 6vw, 4rem);

	/* Spacing. Deliberately tighter than the mocks — 8rem section padding
	   read as empty. Sections breathe at 4–5rem, not 8. */
	--s-1: 0.375rem;
	--s-2: 0.75rem;
	--s-3: 1.25rem;
	--s-4: 2rem;
	--s-5: 3rem;
	--s-6: 4.5rem;

	/* Layout */
	--measure: 34rem;
	--wrap: 68rem;
	--wrap-narrow: 46rem;
	--gutter: clamp(1.25rem, 4vw, 2.5rem);
}

@media (prefers-color-scheme: dark) {
	:root:not([data-theme="light"]) {
		--ground: #0f1520;
		--surface: #161d2b;
		--surface-sunk: #1d2537;
		--ink: #e8ecf3;
		--ink-soft: #b3bdcd;
		--ink-muted: #8b97ac;
		--brand: #ff6633;
		--brand-hover: #ff7d52;
		--on-brand: #0f172a;
		--accent-text: #ff6633;
		--rule: #2a3346;
		--focus: #ff8a5c;
	}
}

:root[data-theme="dark"] {
	--ground: #0f1520;
	--surface: #161d2b;
	--surface-sunk: #1d2537;
	--ink: #e8ecf3;
	--ink-soft: #b3bdcd;
	--ink-muted: #8b97ac;
	--brand: #ff6633;
	--brand-hover: #ff7d52;
	--on-brand: #0f172a;
	--accent-text: #ff6633;
	--rule: #2a3346;
	--focus: #ff8a5c;
}

/* ==========================================================================
   2. Reset & base
   ========================================================================== */

*,
*::before,
*::after {
	box-sizing: border-box;
}

html {
	-webkit-text-size-adjust: 100%;
}

/*
 * overflow-wrap on the body so a long unbroken token (a pasted URL in a post
 * excerpt, a long filename) breaks instead of pushing the page wider than the
 * viewport and creating horizontal scroll on phones.
 */
body {
	overflow-wrap: break-word;
	margin: 0;
	background: var(--ground);
	color: var(--ink);
	font-family: var(--font-serif);
	font-size: var(--t-base);
	line-height: 1.6;
	-webkit-font-smoothing: antialiased;
	text-rendering: optimizeLegibility;
}

img,
picture,
video,
canvas,
svg {
	display: block;
	max-width: 100%;
	height: auto;
}

h1,
h2,
h3,
h4 {
	margin: 0;
	font-weight: 600;
	line-height: 1.12;
	letter-spacing: -0.018em;
	text-wrap: balance;
}

h1 { font-size: var(--t-3xl); }
h2 { font-size: var(--t-2xl); }
h3 { font-size: var(--t-xl); letter-spacing: -0.012em; }
h4 { font-size: var(--t-lg); }

p { margin: 0; }

a {
	color: var(--accent-text);
	text-decoration-thickness: 1px;
	text-underline-offset: 0.18em;
}

a:hover { color: var(--brand-hover); }

:focus-visible {
	outline: 2px solid var(--focus);
	outline-offset: 3px;
	border-radius: 2px;
}

ul, ol {
	margin: 0;
	padding-left: 1.25rem;
}

/* Deliberately no <hr>. If content injects one, make it space, not a line. */
hr {
	border: 0;
	height: 0;
	margin: var(--s-5) 0;
}

.screen-reader-text {
	position: absolute !important;
	width: 1px;
	height: 1px;
	padding: 0;
	margin: -1px;
	overflow: hidden;
	clip: rect(0 0 0 0);
	white-space: nowrap;
}

.skip-link:focus {
	position: fixed;
	top: 1rem;
	left: 1rem;
	z-index: 999;
	width: auto;
	height: auto;
	clip: auto;
	padding: 0.75rem 1rem;
	background: var(--brand);
	color: var(--on-brand);
	font-family: var(--font-sans);
	font-size: var(--t-sm);
	font-weight: 600;
	text-decoration: none;
}

/* ==========================================================================
   3. Layout primitives
   ========================================================================== */

.wrap {
	width: 100%;
	max-width: var(--wrap);
	margin-inline: auto;
	padding-inline: var(--gutter);
}

.wrap--narrow { max-width: var(--wrap-narrow); }

.section {
	padding-block: var(--s-6);
}

.section--tight { padding-block: var(--s-5); }

.section--surface {
	background: var(--surface);
}

.stack {
	display: flex;
	flex-direction: column;
	gap: var(--s-3);
}

.stack--wide { gap: var(--s-4); }

.measure { max-width: var(--measure); }

.scroller {
	overflow-x: auto;
	-webkit-overflow-scrolling: touch;
}

/* ==========================================================================
   4. Utility type
   ========================================================================== */

.eyebrow {
	font-family: var(--font-sans);
	font-size: var(--t-xs);
	font-weight: 600;
	letter-spacing: 0.12em;
	text-transform: uppercase;
	color: var(--accent-text);
}

.meta {
	font-family: var(--font-sans);
	font-size: var(--t-sm);
	color: var(--ink-muted);
}

.lede {
	font-size: var(--t-lg);
	line-height: 1.5;
	color: var(--ink-soft);
	max-width: var(--measure);
}

.num {
	font-variant-numeric: tabular-nums;
}

/* ==========================================================================
   5. Header — no rule beneath it. Separation is space and weight only.
   ========================================================================== */

.site-header {
	background: var(--ground);
	padding-block: var(--s-3);
}

.site-header__inner {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: var(--s-3);
	flex-wrap: wrap;
}

.site-title {
	font-family: var(--font-serif);
	font-size: var(--t-lg);
	font-weight: 600;
	letter-spacing: -0.02em;
	margin: 0;
}

.site-title a {
	color: var(--ink);
	text-decoration: none;
}

.site-nav ul {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: var(--s-3);
	list-style: none;
	margin: 0;
	padding: 0;
}

.site-nav a {
	font-family: var(--font-sans);
	font-size: var(--t-sm);
	color: var(--ink-soft);
	text-decoration: none;
}

.site-nav a:hover,
.site-nav .current-menu-item > a {
	color: var(--ink);
}

/* The single emphasised nav item. Add the CSS class "nav-cta" to the menu
   item in Appearance → Menus. */
.site-nav .nav-cta > a {
	color: var(--accent-text);
	font-weight: 600;
}

/* ==========================================================================
   6. Buttons — brand fill always carries --on-brand text, never white.
   ========================================================================== */

/*
 * The hidden attribute is only display:none in the UA sheet, so any component
 * that sets its own display (.btn is inline-flex) silently overrides it. That
 * left the no-JS fallback link visible next to the button that replaced it.
 */
[hidden] {
	display: none !important;
}

.btn {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	gap: 0.5rem;
	padding: 0.7rem 1.3rem;
	font-family: var(--font-sans);
	font-size: var(--t-sm);
	font-weight: 600;
	line-height: 1;
	text-decoration: none;
	border: 1px solid transparent;
	border-radius: 2px;
	cursor: pointer;
	transition: background-color 0.15s ease, color 0.15s ease;
}

/* White on #ff6633 measures 2.92:1, under the 4.5:1 AA bar for normal text and
   under the 3:1 bar for large text. This matches the buttons on
   thewritepractice.com, so it is brand consistency chosen knowingly.
   To reach AA without losing the orange, darken the fill to #c44813 (4.90:1
   against white) by setting --brand-strong on .btn--primary only. */
.btn--primary {
	background: var(--brand);
	color: var(--on-brand);
	font-weight: 700;
}

.btn--primary:hover {
	background: var(--brand-hover);
	color: var(--on-brand);
}

.btn--secondary {
	background: transparent;
	color: var(--ink);
	border-color: var(--ink-muted);
}

.btn--secondary:hover {
	background: var(--surface-sunk);
	color: var(--ink);
}

/* ==========================================================================
   6a. Hero
   ========================================================================== */

.hero {
	display: grid;
	gap: var(--s-4);
	align-items: center;
}

@media (min-width: 48rem) {
	.hero {
		grid-template-columns: minmax(0, 1fr) minmax(0, 22rem);
		gap: var(--s-5);
	}
}

.hero__text {
	min-width: 0;
}

.hero__portrait {
	margin: 0;
	min-width: 0;
}

.hero__portrait img {
	width: 100%;
	border-radius: 3px;
	background: var(--surface-sunk);
}

/* Figures set inside running prose. Weight, not colour — an accent-coloured
   number in a sentence reads as a link. */
.stat {
	font-weight: 600;
	font-variant-numeric: tabular-nums;
	color: var(--ink);
}

.measure-wide {
	max-width: 46rem;
}

/* ==========================================================================
   7. Proof figures
   Retained for the press kit, where a scannable table of figures is the right
   form. The homepage says the same thing in prose instead.
   ========================================================================== */

.figures {
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr));
	gap: var(--s-4) var(--s-3);
}

.figure {
	display: flex;
	flex-direction: column;
	gap: 0.2rem;
}

.figure__value {
	font-family: var(--font-serif);
	font-size: var(--t-xl);
	font-weight: 600;
	line-height: 1;
	font-variant-numeric: tabular-nums;
	color: var(--ink);
}

.figure__label {
	font-family: var(--font-sans);
	font-size: var(--t-sm);
	line-height: 1.35;
	color: var(--ink-muted);
}

/* ==========================================================================
   8. Entry list — used by /built/ and /books/.
   Varied weight, no dividers, no status badges.
   ========================================================================== */

.entries {
	display: flex;
	flex-direction: column;
	gap: var(--s-5);
}

.entry {
	display: grid;
	gap: var(--s-3);
	align-items: start;
}

/* Flagship entries carry a large screenshot. */
.entry--major {
	grid-template-columns: 1fr;
}

@media (min-width: 48rem) {
	.entry--major {
		grid-template-columns: minmax(0, 1fr) minmax(0, 1.15fr);
		gap: var(--s-4);
	}
	.entry--major:nth-child(even) .entry__media {
		order: -1;
	}
}

/* Curiosities carry a small thumbnail. */
.entry--minor {
	grid-template-columns: 6rem minmax(0, 1fr);
	gap: var(--s-3);
	align-items: center;
}

/* Small tools: text only, grouped. */
.entry--plain {
	grid-template-columns: 1fr;
	gap: var(--s-1);
}

.entry__title {
	font-size: var(--t-lg);
	font-weight: 600;
}

.entry__title a {
	color: var(--ink);
	text-decoration: none;
}

.entry__title a:hover {
	color: var(--accent-text);
	text-decoration: underline;
}

.entry__body {
	color: var(--ink-soft);
	max-width: var(--measure);
}

.entry__media img {
	width: 100%;
	border-radius: 3px;
	background: var(--surface-sunk);
}

.entry__link {
	font-family: var(--font-sans);
	font-size: var(--t-sm);
	font-weight: 600;
	text-decoration: none;
}

.entry__link:hover { text-decoration: underline; }

/* ==========================================================================
   8a. Press kit
   ========================================================================== */

.bio {
	display: flex;
	flex-direction: column;
	gap: var(--s-2);
	background: var(--surface);
	padding: var(--s-3);
	border-radius: 3px;
	max-width: 46rem;
}

.bio__head {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: var(--s-2);
}

.bio__head h3 {
	font-family: var(--font-sans);
	font-size: var(--t-xs);
	font-weight: 600;
	letter-spacing: 0.12em;
	text-transform: uppercase;
	color: var(--ink-muted);
}

.bio__text {
	display: flex;
	flex-direction: column;
	gap: var(--s-2);
}

.btn--small {
	padding: 0.4rem 0.75rem;
	font-size: var(--t-xs);
}

.photo-grid {
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(14rem, 20rem));
	gap: var(--s-4);
}

.photo {
	margin: 0;
	display: flex;
	flex-direction: column;
	gap: var(--s-2);
}

.photo img {
	width: 100%;
	border-radius: 3px;
	background: var(--surface-sunk);
}

.photo__links {
	display: flex;
	gap: var(--s-3);
	font-family: var(--font-sans);
	font-size: var(--t-sm);
	font-weight: 600;
}

/* ==========================================================================
   9. Tables — the only place hairlines are permitted.
   ========================================================================== */

.table {
	width: 100%;
	border-collapse: collapse;
	font-size: var(--t-sm);
	font-family: var(--font-sans);
}

.table th,
.table td {
	text-align: left;
	padding: 0.6rem 0.8rem;
	border-bottom: 1px solid var(--rule);
	vertical-align: top;
}

.table thead th {
	position: sticky;
	top: 0;
	background: var(--ground);
	font-size: var(--t-xs);
	letter-spacing: 0.08em;
	text-transform: uppercase;
	color: var(--ink-muted);
}

.table tbody tr:last-child td { border-bottom: 0; }

.table .num { font-variant-numeric: tabular-nums; }

/* ==========================================================================
   10. Forms
   ========================================================================== */

input[type="text"],
input[type="email"],
input[type="url"],
input[type="date"],
input[type="search"],
textarea,
select {
	width: 100%;
	padding: 0.65rem 0.8rem;
	font-family: var(--font-sans);
	font-size: var(--t-sm);
	color: var(--ink);
	background: var(--ground);
	border: 1px solid var(--ink-muted);
	border-radius: 2px;
}

label {
	font-family: var(--font-sans);
	font-size: var(--t-sm);
	font-weight: 600;
	color: var(--ink);
}

/* ==========================================================================
   11. Prose — post and page content
   ========================================================================== */

.prose {
	max-width: var(--measure);
	display: flex;
	flex-direction: column;
	gap: var(--s-3);
}

.prose h2,
.prose h3 {
	margin-top: var(--s-3);
}

.prose blockquote {
	margin: 0;
	padding-left: var(--s-3);
	border-left: 2px solid var(--brand);
	font-style: italic;
	color: var(--ink-soft);
}

.prose img { border-radius: 3px; }

.prose code {
	font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
	font-size: 0.85em;
	background: var(--surface-sunk);
	padding: 0.1em 0.35em;
	border-radius: 2px;
}

/* ==========================================================================
   12. Footer
   ========================================================================== */

.site-footer {
	background: var(--surface);
	padding-block: var(--s-5);
	margin-top: var(--s-6);
}

.site-footer__inner {
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(12rem, 1fr));
	gap: var(--s-4);
}

.site-footer h2 {
	font-family: var(--font-sans);
	font-size: var(--t-xs);
	font-weight: 600;
	letter-spacing: 0.12em;
	text-transform: uppercase;
	color: var(--ink-muted);
	margin-bottom: var(--s-2);
}

.site-footer ul {
	list-style: none;
	margin: 0;
	padding: 0;
	display: flex;
	flex-direction: column;
	gap: var(--s-1);
}

.site-footer a {
	color: var(--ink-soft);
	text-decoration: none;
	font-size: var(--t-sm);
	font-family: var(--font-sans);
}

.site-footer a:hover {
	color: var(--ink);
	text-decoration: underline;
}

.site-footer__legal {
	margin-top: var(--s-4);
	font-family: var(--font-sans);
	font-size: var(--t-xs);
	color: var(--ink-muted);
}

/* ==========================================================================
   13. Tool template — minimal chrome, the widget is the hero
   ========================================================================== */

.tool-header {
	padding-block: var(--s-3);
}

.tool-signature {
	background: var(--surface);
	padding-block: var(--s-4);
	margin-top: var(--s-5);
}

.tool-signature p {
	max-width: var(--measure);
	color: var(--ink-soft);
	font-size: var(--t-sm);
}

/* ==========================================================================
   14. Motion
   ========================================================================== */

@media (prefers-reduced-motion: reduce) {
	*,
	*::before,
	*::after {
		animation-duration: 0.01ms !important;
		animation-iteration-count: 1 !important;
		transition-duration: 0.01ms !important;
		scroll-behavior: auto !important;
	}
}

/* ==========================================================================
   8b. Offer template
   ========================================================================== */

.video {
	display: flex;
	flex-direction: column;
	gap: var(--s-2);
}

.video__frame {
	position: relative;
	width: 100%;
	max-width: 52rem;
	aspect-ratio: 16 / 9;
	background: var(--surface-sunk);
	border-radius: 3px;
	overflow: hidden;
}

.video__frame iframe,
.video__frame video {
	position: absolute;
	inset: 0;
	width: 100%;
	height: 100%;
	border: 0;
}

.video__frame--empty {
	display: flex;
	align-items: center;
	justify-content: center;
}

.offer-cta {
	display: flex;
	flex-direction: column;
	gap: var(--s-3);
	padding: var(--s-4);
	border-radius: 3px;
	max-width: 46rem;
}



/* ==========================================================================
   8c. Services template — the two-column /work/ page
   ========================================================================== */

/*
 * One column on phones, two from 48rem. Ghostwriting sits first in the source,
 * so on a phone it is what you meet first. That is the right order: it is the
 * higher-value service and the one people arrive searching for.
 *
 * align-items: start lets each card end where its copy ends. An earlier version
 * stretched them to equal height and pushed each button to the foot so the two
 * lined up. With copy this uneven that stranded the coaching button a long way
 * below its own last paragraph. A button belongs at the end of the argument it
 * closes, not at the bottom of a box.
 */
.services {
	display: grid;
	grid-template-columns: 1fr;
	gap: var(--s-4);
	align-items: start;
}

@media (min-width: 48rem) {
	.services {
		grid-template-columns: repeat(2, minmax(0, 1fr));
	}
}

/*
 * Each column is a white card on the cream ground. That is what separates the
 * two services from one another, so no rule or divider is needed between them.
 */
.services__column {
	background: var(--surface);
	padding: var(--s-4);
	border-radius: 3px;
}

/*
 * Inside a column the column *is* the measure. Leaving the global --measure cap
 * on would leave text hugging the left edge of an already-narrow column.
 */
.services__column > *,
.services__column .measure {
	max-width: none;
}

/*
 * The ghostwriting call to action was marked out by a white panel, which reads
 * as nothing once the column behind it is also white. It drops to the sunk tone
 * instead: still quiet, still clearly a panel.
 */
.services__cta--panel {
	background: var(--surface-sunk);
	padding: var(--s-4);
	border-radius: 3px;
}

/*
 * Headings step down one level from their global sizes. A --t-2xl h2 is set for
 * a full-width page and looks shouted in a half-width column, and the levels
 * below it have to move too or the hierarchy collapses.
 */
.services__title {
	font-size: var(--t-xl);
}

.services__column h3 {
	font-size: var(--t-lg);
	letter-spacing: normal;
}

.services__column .entry__title {
	font-size: var(--t-base);
	font-weight: 600;
}

/*
 * The deck under each column heading is the line that tells a visitor whether
 * this column is the one they want. It stays above body copy in weight without
 * competing with the page lede above it.
 */
.services__column .lede {
	font-size: var(--t-lg);
}

/* ==========================================================================
   8d. Book entries — /books/
   ========================================================================== */

/*
 * A jacket beside the copy. Stacked on phones, side by side from 40rem.
 *
 * align-items: start rather than center, because jacket copy runs several
 * paragraphs and a vertically centred cover drifts away from the title it
 * belongs to.
 *
 * The cover column is fixed rather than fractional so all five jackets align
 * down the page even though their aspect ratios differ. Hands is a 3:4 flyer,
 * the three Amazon jackets are roughly 5:8.
 */
.entry--book {
	grid-template-columns: 1fr;
	gap: var(--s-3);
	align-items: start;
}

@media (min-width: 40rem) {
	.entry--book {
		grid-template-columns: 7.5rem minmax(0, 1fr);
		gap: var(--s-4);
	}
}

/*
 * Cap the cover on phones so a stacked jacket does not fill the viewport
 * before the reader reaches the description.
 */
/*
 * Deliberately small. These are reference thumbnails beside several paragraphs
 * of jacket copy, not a storefront, and at full width on a phone a cover pushed
 * the description below the fold.
 */
.entry--book .entry__media {
	max-width: 7.5rem;
}

.entry--book .entry__media img {
	width: 100%;
	height: auto;
	border-radius: 2px;
	/* Pale jackets need an edge or they bleed into the cream ground. */
	box-shadow: 0 1px 3px rgb(15 23 42 / 0.18);
}

/*
 * Any entry whose image is missing collapses to a single column.
 *
 * Without this the content div lands in the media column and is squeezed to
 * its width: 6rem for a minor entry, which crushes the text to two or three
 * characters per line. The templates omit .entry__media entirely when a file is
 * absent, and assets/shots/ is currently empty, so this governs every major and
 * minor entry on /built/ today, not just a theoretical case.
 *
 * Placed after the weight rules and their media queries so it wins on order
 * rather than needing !important.
 */
.entry--major:not(:has(.entry__media)),
.entry--minor:not(:has(.entry__media)),
.entry--book:not(:has(.entry__media)) {
	grid-template-columns: 1fr;
}

@media (min-width: 40rem) {
	.entry--major:not(:has(.entry__media)),
	.entry--minor:not(:has(.entry__media)),
	.entry--book:not(:has(.entry__media)) {
		grid-template-columns: 1fr;
	}
}

@media (min-width: 48rem) {
	.entry--major:not(:has(.entry__media)),
	.entry--minor:not(:has(.entry__media)) {
		grid-template-columns: 1fr;
	}
}

/*
 * Phones: the jacket floats and the copy wraps around it, magazine style.
 *
 * Stacking the cover above the title left a small image marooned over a gap,
 * and pushed the description most of a screen down. Floating it puts the cover
 * beside the title where it belongs and costs no vertical space at all.
 *
 * .entry__content carries .stack (flex column), and a flex container will not
 * wrap around a float, so it reverts to block here and the gap is rebuilt with
 * margins on the children.
 */
@media (max-width: 39.999rem) {
	.entry--book {
		display: block;
	}

	.entry--book .entry__media {
		float: left;
		width: 5.5rem;
		max-width: 5.5rem;
		margin: 0.3rem var(--s-3) var(--s-2) 0;
	}

	.entry--book .entry__content {
		display: block;
	}

	.entry--book .entry__content > * + * {
		margin-block-start: var(--s-3);
	}

	/* Contain the float so the next book does not ride up beside this jacket. */
	.entry--book::after {
		content: "";
		display: table;
		clear: both;
	}
}

/* ==========================================================================
   8e. Book updates modal
   ========================================================================== */

/*
 * A native <dialog>. The browser gives us focus trapping, Escape, inertness of
 * the page behind, and the backdrop, so there is no overlay library here.
 *
 * The dialog element itself is only a positioning box: all visible styling
 * lives on the panel inside, which keeps the backdrop and the card independent.
 */
.book-modal {
	border: 0;
	padding: 0;
	background: transparent;
	color: inherit;

	/*
	 * Sized and centred explicitly rather than leaning on the UA's dialog
	 * defaults, and deliberately free of vw/vh units.
	 *
	 * The earlier rule used min(30rem, calc(100vw - 2rem)) and 100vh. On iOS
	 * Safari 100vw counts the area under the scrollbar gutter and 100vh counts
	 * the area behind the browser chrome, so both can resolve wider or taller
	 * than the visual viewport. Percentages against a position:fixed box cannot.
	 */
	position: fixed;
	inset: 0;
	margin: auto;
	width: calc(100% - 2rem);
	max-width: 30rem;
	max-height: calc(100% - 2rem);
	overflow: auto;
}

/*
 * A closed dialog must not influence layout. The UA sheet already says
 * display:none, but this is the element that was leaving horizontal scroll
 * behind after being opened and closed on iOS, so it is stated outright.
 */
dialog:not([open]) {
	display: none !important;
}

.book-modal::backdrop {
	background: rgb(15 23 42 / 0.6);
	backdrop-filter: blur(2px);
}

/*
 * The cream ground rather than white. The page behind is cream, and a white
 * card floating on a darkened cream page reads as a system alert. Cream on
 * cream reads as part of the site.
 */
.book-modal__panel {
	position: relative;
	background: var(--ground);
	border-radius: 4px;
	padding: var(--s-5) var(--s-4) var(--s-4);
	box-shadow: 0 18px 50px rgb(15 23 42 / 0.28);
}

/*
 * A hairline of brand across the top. One small piece of colour so the panel
 * belongs to this site rather than looking like a default dialog, without
 * putting orange anywhere it competes with the submit button.
 */
.book-modal__panel::before {
	content: "";
	position: absolute;
	inset: 0 0 auto 0;
	height: 3px;
	background: var(--brand);
	border-radius: 4px 4px 0 0;
}

.book-modal__close {
	position: absolute;
	top: var(--s-3);
	right: var(--s-3);
	display: flex;
	background: none;
	border: 0;
	padding: var(--s-1);
	cursor: pointer;
	color: var(--ink-muted);
	line-height: 0;
}

.book-modal__close:hover {
	color: var(--ink);
}

/* Small caps label above the title, the same device the tool pages use. */
.book-modal__eyebrow {
	font-family: var(--font-sans);
	font-size: var(--t-xs);
	letter-spacing: 0.08em;
	text-transform: uppercase;
	color: var(--accent-text);
	margin: 0 0 var(--s-2);
}

.book-modal__title {
	font-size: var(--t-xl);
	line-height: 1.15;
	margin: 0 0 var(--s-2);
}

.book-modal__text {
	color: var(--ink-soft);
	margin: 0 0 var(--s-4);
}

/*
 * Stacked on phones, input and button side by side once there is room. The
 * button does not shrink, so "Send me updates" never wraps to two lines.
 */








/*
 * The front-page trigger is a <button> so it is keyboard-operable and announced
 * as a control, but it sits inline among text links, so it sheds every default
 * button style and inherits the link's.
 */
.entry__link--button {
	background: none;
	border: 0;
	padding: 0;
	font: inherit;
	color: inherit;
	cursor: pointer;
	text-align: left;
}

.entry__link--button:hover {
	text-decoration: underline;
}

/* ==========================================================================
   8f. Subscribe form — inline on the front page and inside the modal
   ========================================================================== */

.subscribe {
	margin-block-start: var(--s-2);
}

/*
 * Stacked on narrow screens, side by side once there is room. The button does
 * not shrink, so "Send me updates" never wraps to two lines.
 */
.subscribe__form {
	display: flex;
	flex-direction: column;
	gap: var(--s-2);
	max-width: 30rem;
}

@media (min-width: 26rem) {
	.subscribe__form {
		flex-direction: row;
		flex-wrap: wrap;
	}
	.subscribe__input { flex: 1 1 12rem; }
	.subscribe__submit { flex: 0 0 auto; }
}

.subscribe__input {
	font: inherit;
	font-size: var(--t-base);
	padding: 0.7rem 0.85rem;
	border: 1px solid var(--rule);
	border-radius: 3px;
	background: var(--surface);
	color: var(--ink);
	min-width: 0;
}

.subscribe__input::placeholder { color: var(--ink-muted); }

.subscribe__input:focus-visible {
	outline: 2px solid var(--focus);
	outline-offset: 1px;
	border-color: var(--focus);
}

.subscribe__submit:disabled {
	opacity: 0.65;
	cursor: default;
}

.subscribe__error {
	flex: 1 0 100%;
	margin: 0;
	font-size: var(--t-sm);
	color: var(--accent-text);
}

.subscribe__done {
	margin: 0;
	max-width: var(--measure);
	color: var(--ink-soft);
}

/* ==========================================================================
   8g. Venues — "Where I've spoken"
   ========================================================================== */

/*
 * A grid of credits, venue first and the place and year beneath it as a quiet
 * caption. The same value-then-label shape as .figure on the press page, at a
 * smaller size because there are eleven of these rather than four.
 *
 * The previous version stacked one venue per full-width row with a hairline
 * under each, which is the shape of a table of data. These are credits, and
 * they read better as a set than as a ledger.
 */
.venues {
	list-style: none;
	margin: 0;
	padding: 0;
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr));
	gap: var(--s-3) var(--s-4);
}

.venue {
	display: flex;
	flex-direction: column;
	gap: 0.15rem;
}

.venue__name {
	font-weight: 600;
	line-height: 1.25;
	text-wrap: balance;
}

.venue__meta {
	font-family: var(--font-sans);
	font-size: var(--t-sm);
	color: var(--ink-muted);
	font-variant-numeric: tabular-nums;
}

/* ==========================================================================
   8h. Press facts — the short list under the figures
   ========================================================================== */

/*
 * Deliberately not a table and not a rule-per-row list. The label sits inline
 * ahead of the value at a quiet weight, so the eye reads the values down the
 * left and the labels recede. Two items, no chrome needed.
 */
.facts {
	margin: 0;
	display: flex;
	flex-direction: column;
	gap: var(--s-2);
	max-width: var(--measure);
}

.facts__row {
	display: flex;
	flex-wrap: wrap;
	gap: 0 var(--s-2);
	align-items: baseline;
}

.facts dt {
	font-family: var(--font-sans);
	font-size: var(--t-sm);
	color: var(--ink-muted);
	flex: 0 0 8.5rem;
}

.facts dd {
	margin: 0;
	flex: 1 1 12rem;
}

/*
 * The figures sit above the list, so they need air beneath them rather than
 * the stack's default gap, which reads as the same relationship.
 */
.figures + .facts {
	margin-block-start: var(--s-2);
}
