/*
Theme Name: Nehemiah
Theme URI: https://github.com/TheDragonBeyond/MySite
Author: Nehemiah Newell
Author URI: https://github.com/TheDragonBeyond
Description: Hand-built classic theme for Nehemiah's personal site — a bold, expressive portfolio for shipped software and published writing. Name is a placeholder pending GATE-CONTENT.
Version: 0.11.0
Requires at least: 6.4
Tested up to: 6.6
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: nehemiah
Tags: portfolio, blog, custom-theme
*/

/* ==========================================================================
   §1 DESIGN TOKENS — the single source of truth (GATE-VISUAL: Direction C,
   "Night Kiln", resolved 2026-07-19; values recorded in docs/DECISIONS.md).

   Editing rules (the future-proofing contract):
   - Change VALUES here; never introduce raw colors below §1 or in templates.
     Templates and components reference Tier-2/3 tokens only (spec 03 §C).
   - After any Tier-1/Tier-2 color edit, run:  python scripts/design/check-contrast.py
     It re-proves every committed WCAG pair (7:1 long-form, 4.5:1 text, 3:1 large/focus).
   - To regenerate a whole ramp from OKLCH: scripts/design/generate-ramp.py.
   - Fonts: swap WOFF2 + the @font-face block + the two stack tokens
     (see assets/fonts/FONTS.md), then update the preloads in functions.php.
   ========================================================================== */

/* ---- §1a Tier 1 — primitives ------------------------------------------- */

:root {
	/* Ochre ramp — OKLCH hue 46 "ember" (do not eyeball-edit; regenerate) */
	--ochre-50: #fdf3ee;
	--ochre-100: #fee3d7;
	--ochre-200: #fccbb5;
	--ochre-300: #f6ad8b;
	--ochre-400: #ea8a5c;
	--ochre-500: #d66628;
	--ochre-600: #b74d02;
	--ochre-700: #8c3900;
	--ochre-800: #662700;
	--ochre-900: #391705;

	/* Tinted neutrals — same hue family, never default warm cream */
	--ink-900: #221a15;
	--ink-700: #403733;
	--ink-500: #605753;
	--ground-50: #faf4f2;
	--ground-100: #f1e9e6;
	--ground-200: #e6dbd6;
	--night-900: #1c110d; /* dark-first chrome ground */
	--night-800: #2a1c16;

	/* Font stacks (self-hosted, assets/fonts/) */
	--font-display: "Source Serif 4", Georgia, "Times New Roman", serif;
	--font-body: "Archivo", system-ui, -apple-system, "Segoe UI", sans-serif;

	/* Type scale — base 1.125rem × 1.414; step-5 is the fluid display step */
	--step--1: 0.7955rem;
	--step-0: 1.125rem;
	--step-1: 1.591rem;
	--step-2: 2.25rem;
	--step-3: 3.182rem;
	--step-4: 4.5rem;
	--step-5: clamp(2.6rem, 2rem + 4.5vw, 4.5rem);

	/* Spacing (spec 03 §C, decided) */
	--space-1: 0.25rem;
	--space-2: 0.5rem;
	--space-3: 0.75rem;
	--space-4: 1rem;
	--space-5: 1.5rem;
	--space-6: 2rem;
	--space-7: 3rem;
	--space-8: 4rem;
	--space-9: 6rem;

	/* Radius / motion (spec 03 §C, decided) */
	--radius-1: 2px;
	--radius-2: 6px;
	--dur-1: 150ms;
	--dur-2: 250ms;
	--ease-out: cubic-bezier(0.2, 0, 0, 1);

	/* Reading */
	--measure-prose: 70ch; /* 65–75ch hard range (W-6) */
	--prose-size: 1.1875rem;
	--prose-leading: 1.75;

	/* Chrome */
	--nav-height: 56px;
}

/* ---- §1b Tier 2 — semantic (what templates use) -------------------------
   Default scheme is NIGHT (the identity). Light remaps THIS TIER ONLY.
   The "lit page" tokens carry long-form reading surfaces (chapters, posts,
   résumé): paper + ink at ≥7:1 in both schemes (The Reading Rule).       */

:root {
	--color-bg: var(--night-900);
	--color-text: var(--ground-50);
	--color-text-muted: var(--ochre-200);
	--color-surface: var(--ochre-700); /* committed ember field */
	--color-on-surface: var(--ground-50);
	--color-surface-strong: var(--ochre-500);
	--color-on-surface-strong: var(--night-900);
	--color-link: var(--ochre-300);
	--color-focus-ring: var(--ochre-400);
	--color-border: var(--night-800);
	--color-page: var(--ground-50); /* lit reading surface */
	--color-page-text: var(--ink-900);
	--color-page-muted: var(--ink-700);
	--color-page-link: var(--ochre-700);
	--color-selection-bg: var(--ochre-300);
	--color-selection-text: var(--night-900);
	/* Overlay scrim behind the mobile drawer — decorative, scheme-neutral
	   (a dark wash reads correctly over either ground); not a text pair. */
	--color-scrim: rgba(18, 11, 8, 0.62);
	/* Content-chrome hairline that stays VISIBLE on the scheme ground:
	   --color-border is tuned for the bar edge and computes 1.12:1 on the
	   night ground, which erased table grids / image frames in write-ups on
	   chrome grounds (S7 review). Decorative, not a text pair. */
	--color-border-strong: var(--ink-500);
	color-scheme: dark;
}

/* GATE-VISUAL amendment (2026-07-28): night is the default for EVERY visitor —
   prefers-color-scheme no longer auto-flips (UAs report `light` for
   no-preference users, which made the daylight scheme the de-facto default).
   The daylight scheme remains first-class but is reached only through the
   manual toggle below (html[data-theme="light"]). */

/* Manual-toggle tiers (W-7 toggle shipped 2026-07-28; the pre-paint script
   sets data-theme from localStorage before first paint). */

html[data-theme="dark"] {
	--color-bg: var(--night-900);
	--color-text: var(--ground-50);
	--color-text-muted: var(--ochre-200);
	--color-link: var(--ochre-300);
	--color-focus-ring: var(--ochre-400);
	--color-border: var(--night-800);
	--color-border-strong: var(--ink-500);
	--color-page: var(--ground-50);
	--color-page-text: var(--ink-900);
	--color-page-muted: var(--ink-700);
	--color-page-link: var(--ochre-700);
	--color-selection-bg: var(--ochre-300);
	--color-selection-text: var(--night-900);
	color-scheme: dark;
}

html[data-theme="light"] {
	--color-bg: var(--ground-50);
	--color-text: var(--ink-900);
	--color-text-muted: var(--ink-700);
	--color-link: var(--ochre-700);
	--color-focus-ring: var(--ochre-600);
	--color-border: var(--ground-200);
	--color-border-strong: var(--ground-200);
	--color-page: var(--ground-50);
	--color-page-text: var(--ink-900);
	--color-page-muted: var(--ink-700);
	--color-page-link: var(--ochre-700);
	--color-selection-bg: var(--ochre-200);
	--color-selection-text: var(--ink-900);
	color-scheme: light;
}

/* Section accents (IA-2, "one maker, two rooms") — registers remap only the
   surface pairing. Professional = banked ember; community = open flame.
   Templates opt in via <body data-register="pro|community"> (S6/S7).      */

[data-register="pro"] {
	--color-surface: var(--ochre-700);
	--color-on-surface: var(--ground-50);
}

[data-register="community"] {
	--color-surface: var(--ochre-500);
	--color-on-surface: var(--night-900);
}

/* ---- §1c Fonts ----------------------------------------------------------
   Latin-subset variable WOFF2, self-hosted (PF-2). Preloaded in functions.php. */

@font-face {
	font-family: "Source Serif 4";
	src: url(assets/fonts/source-serif-4-latin.woff2) format("woff2");
	font-weight: 200 900;
	font-style: normal;
	font-display: swap;
	unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
	font-family: "Archivo";
	src: url(assets/fonts/archivo-latin.woff2) format("woff2");
	font-weight: 100 900;
	font-style: normal;
	font-display: swap;
	unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
	font-family: "Archivo";
	src: url(assets/fonts/archivo-italic-latin.woff2) format("woff2");
	font-weight: 100 900;
	font-style: italic;
	font-display: swap;
	unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* ==========================================================================
   §2 BASE — reset, grounds, focus, motion. Tokens only from here on.
   ========================================================================== */

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

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

body {
	margin: 0;
	background: var(--color-bg);
	color: var(--color-text);
	font-family: var(--font-body);
	font-size: var(--step-0);
	line-height: 1.6;
}

::selection {
	background: var(--color-selection-bg);
	color: var(--color-selection-text);
}

img,
svg,
video {
	max-width: 100%;
	height: auto;
}

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

/* Fragment targets (footnotes, backrefs, in-page anchors) land with a little
   air on top of the sticky-bar scroll-padding — additive, both sticky states
   (S5-R carry-in #4). */
:target {
	scroll-margin-block-start: var(--space-3);
}

@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;
	}
}

/* ==========================================================================
   §3 TYPOGRAPHY
   ========================================================================== */

h1,
h2,
h3,
h4,
h5,
h6 {
	font-family: var(--font-display);
	font-weight: 700;
	line-height: 1.1;
	margin-block: 0 var(--space-4);
	text-wrap: balance;
}

h1 {
	font-size: var(--step-3);
}

h2 {
	font-size: var(--step-2);
}

h3 {
	font-size: var(--step-1);
}

h4,
h5,
h6 {
	font-size: var(--step-0);
}

p {
	margin-block: 0 var(--space-4);
}

a {
	color: var(--color-link);
	text-underline-offset: 0.14em;
	transition: color var(--dur-1) var(--ease-out), text-decoration-color var(--dur-1) var(--ease-out);
}

a:hover {
	text-decoration-thickness: 2px;
}

small {
	font-size: var(--step--1);
}

hr {
	border: 0;
	border-top: 1px solid var(--color-border);
	margin-block: var(--space-6);
}

code,
kbd,
samp {
	font-family: ui-monospace, Consolas, "Cascadia Mono", monospace;
	font-size: 0.9em;
}

/* Footnote markers must not open up the line box they sit in (W-5/W-6). */
sup,
sub {
	line-height: 0;
}

/* Long unbreakable tokens (URLs, namespaced identifiers) appear in real prose
   and excerpts (S7/S8 lesson — synthetic fixtures hide this): break inside
   the box rather than push a 320px viewport sideways (AA-4). break-word, not
   anywhere: it engages only at actual overflow, so intrinsic sizing and the
   reading measure stay untouched. */
.entry-content,
.post-card-excerpt,
.search-entry-summary,
.front-hero-intro,
.front-exhibit-line,
.post-meta,
.work-card-blurb,
.project-card-summary {
	overflow-wrap: break-word;
}

/* ==========================================================================
   §4 SITE FRAME & NAVIGATION (S5 — spec 03 §B; N-1..N-9).
   Layout: full-width sticky header with a centered .nav-bar; a .site-body
   grid holds <main> and the optional contextual sidebar; full-width footer
   with a centered .footer-inner. All controls degrade to a no-JS baseline.
   ========================================================================== */

/* ---- §4a Accessibility scaffolding ------------------------------------- */

.screen-reader-text {
	clip-path: inset(50%);
	height: 1px;
	width: 1px;
	overflow: hidden;
	position: absolute;
	word-wrap: normal;
}

.skip-link:focus {
	clip-path: none;
	height: auto;
	width: auto;
	position: fixed;
	inset-block-start: var(--space-2);
	inset-inline-start: var(--space-2);
	z-index: 300;
	background: var(--color-surface-strong);
	color: var(--color-on-surface-strong);
	padding: var(--space-2) var(--space-4);
	font-weight: 700;
	text-decoration: none;
	border-radius: var(--radius-1);
}

/* <main> is a focus target for the skip link but not a tab stop. */
.site-main:focus {
	outline: none;
}

/* ---- §4b Top bar (N-1) + sticky (N-2) + compact (N-3) ------------------ */

.site-header {
	background: var(--color-bg);
	border-block-end: 1px solid var(--color-border);
	z-index: 50;
}

/* Sticky when data-nav-sticky="1". The server emits the owner default in the
   attribute; the pre-paint snippet overwrites it with any stored reader pref,
   so one attribute is always the effective state (no flash, no JS needed). */
html[data-nav-sticky="1"] .site-header {
	position: sticky;
	inset-block-start: 0;
}

/* Logged-in: the fixed admin bar sits above all site chrome, so the pinned
   header (and the focused skip link) must clear it. Core exposes the height
   as a custom property; below 600px core makes the bar absolute (it scrolls
   away with the page), so no offset there. */
body.admin-bar .site-header {
	inset-block-start: var(--wp-admin--admin-bar--height, 32px);
}

body.admin-bar .skip-link:focus {
	inset-block-start: calc(var(--wp-admin--admin-bar--height, 32px) + var(--space-2));
}

@media (max-width: 600px) {

	body.admin-bar .site-header {
		inset-block-start: 0;
	}

	body.admin-bar .skip-link:focus {
		inset-block-start: var(--space-2);
	}
}

/* Fragment navigation (skip link, footnote/backref anchors, in-page links)
   must land clear of the pinned bar, never underneath it. */
html[data-nav-sticky="1"] {
	scroll-padding-block-start: calc(var(--nav-height) + var(--space-2));
}

.nav-bar {
	display: flex;
	align-items: center;
	gap: var(--space-3) var(--space-5);
	max-width: 72rem;
	margin-inline: auto;
	padding-inline: var(--space-4);
	min-block-size: var(--nav-height);
	/* No height transition: min-block-size is a layout property, and animating
	   it relayouts the whole document every frame mid-scroll. The 8px compact
	   change snaps instead (imperceptible, and reduced-motion parity). */
}

/* N-3: compact shrinks, never hides (content stays reachable). JS only sets
   [data-compact] while sticky and not under reduced motion. */
.site-header[data-compact] {
	--nav-height: 48px;
}

/* The brand is owner content (GATE-CONTENT can make it any length): it must
   shrink and ellipsize before it can push the bar past a 320px viewport
   (AA-4 / WCAG 1.4.10 — found S8: an 12-char dev title overflowed). Block +
   44px line-height keeps the tap target; nowrap guarantees one line so the
   ellipsis engages. */
.site-name {
	display: block;
	min-inline-size: 0;
	min-block-size: 44px;
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
	font-family: var(--font-display);
	font-weight: 700;
	font-size: var(--step-1);
	line-height: 44px;
	color: var(--color-text);
	text-decoration: none;
}

@media (max-width: 479.98px) {

	.site-name {
		font-size: var(--step-0);
	}
}

.site-name:hover {
	text-decoration: underline;
	text-underline-offset: 0.14em;
}

.nav-primary {
	flex: 1 1 auto;
	min-inline-size: 0;
}

.nav-menu {
	list-style: none;
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: var(--space-2) var(--space-4);
	padding: 0;
	margin: 0;
}

.nav-menu a {
	display: inline-flex;
	align-items: center;
	min-block-size: 44px;
	color: var(--color-text);
	text-decoration: none;
	border-block-end: 3px solid transparent;
}

.nav-menu a:hover {
	border-block-end-color: var(--color-text-muted);
}

/* Current section: weight + underline marker — never color alone (AA-3).
   [aria-current] covers both the exact page ('page') and section ancestors
   ('true', e.g. Writing while reading a chapter). */
.nav-menu a[aria-current] {
	font-weight: 700;
	border-block-end-color: var(--color-focus-ring);
}

/* Quiet cross-link (D-SPLIT): the seeded door to the other half — muted,
   regular weight, never aria-current (nehemiah_primary_menu() docblock). */
.nav-menu .nav-crosslink > a,
.drawer-menu-list .nav-crosslink > a {
	color: var(--color-text-muted);
	font-weight: 400;
}

.nav-actions {
	display: flex;
	align-items: center;
	gap: var(--space-2);
	margin-inline-start: auto;
}

.nav-account {
	display: inline-flex;
	align-items: center;
	min-block-size: 44px;
	padding-inline: var(--space-2);
	color: var(--color-text);
	text-decoration: none;
	white-space: nowrap;
}

.nav-account:hover {
	text-decoration: underline;
	text-underline-offset: 0.14em;
}

.nav-icon-btn {
	display: none; /* JS-only controls; revealed by the data-nav-ready guards below. */
	align-items: center;
	justify-content: center;
	min-inline-size: 44px;
	min-block-size: 44px;
	padding: 0;
	background: transparent;
	color: var(--color-text);
	border: 1px solid transparent;
	border-radius: var(--radius-1);
	cursor: pointer;
}

.nav-icon-btn:hover {
	/* --color-border is a hairline tuned for the bar edge — invisible as a
	   hover cue on the night ground (1.06:1). Muted text reads on both. */
	border-color: var(--color-text-muted);
}

.nav-sticky-toggle .nav-icon {
	transition: transform var(--dur-1) var(--ease-out);
}

/* Pinned = upright ember pin; unpinned tilts the pin 45° ("pulled out") so the
   state is never conveyed by color alone (AA-3 / WCAG 1.4.1). */
.nav-sticky-toggle[aria-pressed="true"] {
	color: var(--color-surface-strong);
}

.nav-sticky-toggle[aria-pressed="false"] .nav-icon {
	transform: rotate(45deg);
}

/* W-7 theme toggle: the glyph mirrors the CURRENT scheme — moon while the
   night identity is active, sun once daylight is engaged — so state is never
   conveyed by color alone (AA-3 / WCAG 1.4.1). Icons key on the root
   data-theme attribute (absent = night), not aria-pressed, so any control
   that flips the theme (e.g. the style-guide switcher) keeps them honest. */
.nav-theme-toggle .icon-sun {
	display: none;
}

html[data-theme="light"] .nav-theme-toggle .icon-sun {
	display: block;
}

html[data-theme="light"] .nav-theme-toggle .icon-moon {
	display: none;
}

.nav-theme-toggle[aria-pressed="true"] {
	color: var(--color-link);
}

.nav-icon {
	display: block;
}

/* ≤360px the bar's fixed controls (account + theme + pin + hamburger) plus the
   site name overflow the viewport at the default gaps (found by reading-check
   at 320px — predates S6). Tighten chrome spacing only; every touch target
   stays ≥44px. */
@media (max-width: 359.98px) {

	.nav-bar {
		padding-inline: var(--space-3);
		gap: var(--space-2) var(--space-3);
	}

	.nav-actions {
		gap: var(--space-1);
	}

	.nav-account {
		padding-inline: var(--space-1);
	}

	/* A fourth 44px control re-breaks AA-4 at 320px (~20px overflow). The bar's
	   theme toggle yields; the drawer-head instance keeps W-7 reachable. */
	html[data-nav-ready] .nav-actions .nav-theme-toggle {
		display: none;
	}
}

/* N-2 pin + W-7 theme toggles appear wherever nav.js runs; hamburger only on
   small screens. JS-only controls key on [data-nav-ready] — set by nav.js
   itself at startup, not the pre-paint `js` class — so a failed nav.js load
   degrades to the no-JS baseline instead of dead controls (S5-R carry-in #2). */
html[data-nav-ready] .nav-sticky-toggle,
html[data-nav-ready] .nav-theme-toggle {
	display: inline-flex;
}

/* ---- §4c No-JS baseline & responsive swap (M-3) ------------------------ */

/* Drawer/sidebar boundary is 1120px, not 1024px: with the sidebar's fixed
   15rem column, the chapter measure would fall to ~57ch at 1024px — below
   W-6's 65ch floor. §E's "sidebar collapses first" wins (S6 review). Keep in
   sync with nav.js's desktop matchMedia. */
@media (max-width: 1119.98px) {

	html[data-nav-ready] .nav-hamburger {
		display: inline-flex;
	}

	/* With nav.js running, the drawer owns navigation on small screens: hide
	   the inline primary nav and the desktop contextual sidebar (their content
	   is in the drawer). Without it both stay visible and usable. */
	html[data-nav-ready] .nav-primary,
	html[data-nav-ready] .ctx-sidebar {
		display: none;
	}
}

/* ---- §4d Site body grid + contextual sidebar (N-4) --------------------- */

.site-body {
	max-width: 72rem;
	margin-inline: auto;
	padding-inline: var(--space-4);
	padding-block: var(--space-6) var(--space-8);
}

.site-main {
	min-inline-size: 0;
}

@media (min-width: 1120px) {

	.site-body.has-sidebar {
		display: grid;
		grid-template-columns: minmax(0, max-content) minmax(0, 1fr);
		column-gap: var(--space-7);
		align-items: start;
	}

	.site-body.has-sidebar .site-main {
		grid-column: 2;
		grid-row: 1;
	}

	.ctx-sidebar {
		grid-column: 1;
		grid-row: 1;
		position: sticky;
		inset-block-start: var(--space-4);
	}

	/* Only reserve headroom for the bar when it is actually pinned. */
	html[data-nav-sticky="1"] .ctx-sidebar {
		inset-block-start: calc(var(--nav-height) + var(--space-4));
	}
}

.ctx-sidebar {
	inline-size: 15rem;
	max-inline-size: 15rem;
	font-size: var(--step--1);
}

/* Collapsed visuals key on the ROOT attribute, which two writers keep in
   sync: the byte-stable pre-paint sidebar snippet sets it before first paint
   (no post-paint reading-column shift for collapsed-pref readers — S5-R
   carry-in #1) and nav.js mirrors it on every toggle. The element-level
   [data-collapsed] stays for state/ARIA and the interaction harness. */
html[data-ctx-collapsed] .ctx-sidebar {
	inline-size: auto;
	max-inline-size: none;
	/* Reserve the reopen tab's footprint from first paint: the toggle itself
	   appears only when nav.js signals ready, and without this floor its
	   arrival would shift the centered reading column (carry-in #1). */
	min-inline-size: 2.75rem;
}

/* Until nav.js applies panel.hidden, CSS alone must hold the pre-paint
   collapsed state (the pre-nav.js window). */
html[data-ctx-collapsed] .ctx-panel {
	display: none;
}

.ctx-toggle {
	align-items: center;
	gap: var(--space-2);
	min-block-size: 44px;
	inline-size: 100%;
	padding: var(--space-2) var(--space-3);
	background: transparent;
	color: var(--color-text);
	font-family: var(--font-body);
	font-weight: 700;
	border: 1px solid var(--color-border);
	border-radius: var(--radius-1);
	cursor: pointer;
	text-align: start;
}

/* No-JS: the toggle is hidden and the panel is simply open (M-3). */
.ctx-toggle {
	display: none;
}

html[data-nav-ready] .ctx-toggle {
	display: inline-flex;
}

html[data-ctx-collapsed] .ctx-toggle {
	inline-size: auto;
}

html[data-ctx-collapsed] .ctx-toggle-text {
	display: none;
}

.ctx-chevron {
	flex: none;
	transition: transform var(--dur-1) var(--ease-out);
}

html:not([data-ctx-collapsed]) .ctx-sidebar .ctx-chevron {
	transform: rotate(90deg);
}

.ctx-panel {
	margin-block-start: var(--space-3);
	max-block-size: calc(100vh - var(--nav-height) - var(--space-8));
	overflow-y: auto;
	overscroll-behavior: contain;
}

.ctx-list {
	list-style: none;
	margin: 0;
	padding: 0;
}

.ctx-list li {
	margin-block-end: var(--space-1);
}

.ctx-list a {
	display: block;
	padding: var(--space-2) var(--space-3);
	color: var(--color-link);
	text-decoration: none;
	border-radius: var(--radius-1);
}

.ctx-list a:hover {
	background: var(--color-border);
}

/* Current entry = a filled ember chip (the committed field), not a thin edge
   marker: bold weight + a solid ground carry the state without color alone. */
.ctx-list a[aria-current="page"] {
	font-weight: 700;
	background: var(--color-surface);
	color: var(--color-on-surface);
}

.ctx-group-title {
	margin-block: var(--space-4) var(--space-2);
	font-weight: 700;
	color: var(--color-text-muted);
	text-transform: uppercase;
	letter-spacing: 0.06em;
	font-size: 0.8em;
}

.ctx-group-title:first-child {
	margin-block-start: 0;
}

/* ---- §4e Breadcrumbs (N-6) --------------------------------------------- */

.breadcrumbs {
	margin-block-end: var(--space-5);
	font-size: var(--step--1);
}

.breadcrumbs-list {
	list-style: none;
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: var(--space-2);
	margin: 0;
	padding: 0;
}

.breadcrumbs-list .crumb-item + .crumb-item::before {
	content: "\203A"; /* › */
	margin-inline-end: var(--space-2);
	color: var(--color-text-muted);
}

.crumb-link {
	color: var(--color-link);
}

.crumb-current {
	color: var(--color-text-muted);
}

@media (max-width: 479.98px) {

	.crumb-link,
	.crumb-current {
		display: inline-block;
		max-inline-size: 9rem;
		overflow: hidden;
		text-overflow: ellipsis;
		white-space: nowrap;
		vertical-align: bottom;
	}
}

/* ---- §4f Mobile drawer (N-5/N-9) --------------------------------------- */

.drawer {
	position: fixed;
	inset: 0;
	z-index: 200;
	display: flex;
	justify-content: flex-end;
	background: transparent;
	transition: background var(--dur-2) var(--ease-out);
}

.drawer[hidden] {
	display: none;
}

/* During the close animation the overlay is still displayed (hidden lands on
   transitionend) but the drawer is logically closed — make it visual-only so
   taps reach the page underneath (e.g. the hamburger, to reopen mid-close). */
html:not([data-drawer-active]) .drawer {
	pointer-events: none;
}

html[data-drawer-active] .drawer {
	background: var(--color-scrim);
}

/* Scroll lock while the modal drawer is open: inert stops focus and clicks
   but not wheel/touch scroll chaining to the document. */
html[data-drawer-active] {
	overflow: hidden;
}

.drawer-panel {
	inline-size: min(20rem, 85vw);
	block-size: 100%;
	overflow-y: auto;
	overscroll-behavior: contain;
	background: var(--color-bg);
	border-inline-start: 1px solid var(--color-border);
	padding: var(--space-4) var(--space-5) var(--space-7);
	transform: translateX(100%);
	transition: transform var(--dur-2) var(--ease-out);
}

html[data-drawer-active] .drawer-panel {
	transform: translateX(0);
}

.drawer-head {
	display: flex;
	align-items: center;
	gap: var(--space-3);
	min-block-size: var(--nav-height);
}

.drawer-title {
	font-family: var(--font-display);
	font-weight: 700;
	font-size: var(--step-1);
	margin-inline-end: auto; /* pushes the theme toggle + close to the end */
}

.drawer-close {
	display: inline-flex; /* always shown inside the (JS-only) drawer */
}

.drawer-nav {
	margin-block: var(--space-4);
}

.drawer-menu-list {
	list-style: none;
	margin: 0;
	padding: 0;
}

.drawer-menu-list a {
	display: flex;
	align-items: center;
	min-block-size: 44px;
	padding: var(--space-2) var(--space-2);
	color: var(--color-text);
	text-decoration: none;
	border-block-end: 1px solid var(--color-border);
}

.drawer-menu-list a[aria-current] {
	font-weight: 700;
	background: var(--color-surface);
	color: var(--color-on-surface);
	border-radius: var(--radius-1);
	padding-inline: var(--space-3);
}

.drawer-section {
	margin-block-start: var(--space-5);
	border-block-start: 1px solid var(--color-border);
	padding-block-start: var(--space-4);
}

.drawer-section-title {
	font-size: var(--step-0);
	margin-block: 0 var(--space-3);
}

/* ---- §4g Footer (N-8) -------------------------------------------------- */

.site-footer {
	border-block-start: 1px solid var(--color-border);
	color: var(--color-text-muted);
	font-size: var(--step--1);
}

.footer-inner {
	max-width: 72rem;
	margin-inline: auto;
	padding: var(--space-6) var(--space-4) var(--space-7);
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: var(--space-4) var(--space-6);
}

.footer-nav .footer-menu-list,
.footer-links {
	list-style: none;
	display: flex;
	flex-wrap: wrap;
	gap: var(--space-3) var(--space-5);
	margin: 0;
	padding: 0;
}

.footer-nav a,
.footer-links a {
	color: var(--color-link);
	min-block-size: 44px;
	display: inline-flex;
	align-items: center;
}

.footer-copy {
	margin: 0;
	margin-inline-start: auto;
}

/* ==========================================================================
   §5 THE LIT PAGE — long-form reading surface (W-6, The Reading Rule).
   Chapters, posts, and the résumé render inside .lit-page; the chrome
   around it stays in the scheme's ground. ≥7:1 in both schemes, proven.
   ========================================================================== */

.lit-page {
	background: var(--color-page);
	color: var(--color-page-text);
	border-radius: var(--radius-2);
	padding: clamp(var(--space-5), 4vw, var(--space-7)) clamp(var(--space-4), 4vw, var(--space-7));
	/* The paper is ground-50 in BOTH schemes, so the scheme's ring (ochre-400
	   in night) can fall under 3:1 here — the page carries its own ring
	   (ochre-700 on paper: 7.14:1, proven; WCAG 1.4.11 / S6 review). */
	--color-focus-ring: var(--color-page-link);
	/* UA widgets (table-wrap scrollbars, future native form controls) must
	   follow the paper, not the inherited night scheme — color-scheme is
	   inherited, and since the amendment the root is dark for everyone. */
	color-scheme: light;
}

.lit-page a {
	color: var(--color-page-link);
}

/* One centered column for everything on the page — header and prose share
   --prose-size, so the 70ch cap computes identically for both and their
   edges align like a set book page (W-6: 65–75ch hard range). */
.reading-col {
	max-inline-size: var(--measure-prose);
	margin-inline: auto;
	font-size: var(--prose-size);
}

/* ---- Chapter opening (single-chapter.php) ------------------------------ */

.chapter-header {
	margin-block-end: var(--space-7);
}

/* The work's running header: display italic, a quiet path back to the TOC. */
.chapter-kicker {
	font-family: var(--font-display);
	font-style: italic;
	font-size: var(--step-0);
	margin-block-end: var(--space-5);
}

/* Book grammar, not web-eyebrow scaffolding: real small caps, one instance,
   carrying the chapter's actual number. */
.chapter-no {
	font-size: var(--step-0);
	font-variant-caps: all-small-caps;
	letter-spacing: 0.05em;
	color: var(--color-page-muted);
	margin-block-end: var(--space-2);
}

.chapter-title {
	font-size: clamp(var(--step-2), 1.7rem + 2.6vw, var(--step-3));
	margin-block-end: var(--space-3);
}

.chapter-date {
	font-size: var(--step--1);
	color: var(--color-page-muted);
	margin-block-end: 0;
}

.entry-content {
	max-inline-size: var(--measure-prose);
	font-size: var(--prose-size);
	line-height: var(--prose-leading);
	text-wrap: pretty;
	/* Muted ink for captions/cites follows the ground: scheme muted on the
	   dark/light chrome grounds, page muted on the lit page (S6 review — the
	   lit-page token alone was 1.6:1 on the night ground). */
	--entry-muted: var(--color-text-muted);
	/* Same ground-following pattern for content-chrome hairlines (table
	   grids, image frames): the bar-edge border token vanishes on the night
	   chrome ground (S7 review — first hit by project write-ups). */
	--entry-border: var(--color-border-strong);
}

.lit-page .entry-content {
	--entry-muted: var(--color-page-muted);
	--entry-border: var(--color-border);
}

.entry-content > * {
	margin-block: 0 var(--space-5);
}

.entry-content :is(h2, h3, h4) {
	margin-block: var(--space-6) var(--space-4);
}

/* ==========================================================================
   §6 CORE BLOCK STYLES (03 §F contract: the theme dequeues wp-block-library
   and takes responsibility for paragraph, heading, list, quote, image,
   code, table — blog posts are block content, so this is mandatory).
   ========================================================================== */

.entry-content :is(ul, ol) {
	padding-inline-start: 1.4em;
}

.entry-content li {
	margin-block-end: var(--space-2);
}

.entry-content li > :is(ul, ol) {
	margin-block-start: var(--space-2);
}

.wp-block-quote,
.entry-content blockquote {
	margin-inline: 0;
	padding-inline: var(--space-5);
	font-family: var(--font-display);
	font-style: italic;
	font-size: 1.08em;
}

.wp-block-quote cite,
.entry-content blockquote cite {
	display: block;
	margin-block-start: var(--space-2);
	font-family: var(--font-body);
	font-style: normal;
	font-size: var(--step--1);
	color: var(--entry-muted, var(--color-text-muted));
}

.wp-block-image,
.entry-content figure {
	margin-inline: 0;
}

.entry-content img {
	border: 1px solid var(--entry-border, var(--color-border));
	border-radius: var(--radius-1);
}

.wp-block-image figcaption,
.entry-content figcaption {
	font-size: var(--step--1);
	color: var(--entry-muted, var(--color-text-muted));
	margin-block-start: var(--space-2);
}

.entry-content code {
	background: var(--ground-100);
	color: var(--ink-900);
	padding: 0.1em 0.35em;
	border-radius: var(--radius-1);
}

.wp-block-code,
.entry-content pre {
	background: var(--night-800);
	color: var(--ground-100);
	padding: var(--space-4) var(--space-5);
	border-radius: var(--radius-2);
	overflow-x: auto;
	/* Always a night panel (even on the lit page, which forces color-scheme:
	   light) — keep its own scrollbar dark to match. */
	color-scheme: dark;
	font-size: 0.85em;
	line-height: 1.55;
}

.wp-block-code code,
.entry-content pre code {
	background: none;
	color: inherit;
	padding: 0;
}

.wp-block-table table,
.entry-content table {
	border-collapse: collapse;
	width: 100%;
	font-size: 0.92em;
	font-variant-numeric: tabular-nums;
}

.wp-block-table :is(th, td),
.entry-content :is(th, td) {
	border: 1px solid var(--entry-border, var(--color-border));
	padding: var(--space-2) var(--space-3);
	text-align: left;
}

.wp-block-table th,
.entry-content th {
	background: var(--ground-100);
	color: var(--ink-900);
}

.wp-block-table,
.entry-content .table-wrap {
	overflow-x: auto;
}

/* Inside the lit page, code/table chrome keeps page-side neutrals so the
   panels read as printed matter, not chrome. (Same tokens both schemes.) */

/* Footnotes (W-5): league/commonmark's FootnoteExtension output. Page-side
   neutrals — the block is printed matter, and it draws its own rule, so the
   extension's <hr> is redundant. */
.entry-content .footnotes {
	margin-block-start: var(--space-7);
	border-block-start: 1px solid var(--ground-200);
	padding-block-start: var(--space-4);
	font-size: 0.85em;
}

.entry-content .footnotes hr {
	display: none;
}

.entry-content .footnotes :is(ul, ol) {
	margin-block: 0;
}

.entry-content .footnotes li {
	margin-block-end: var(--space-3);
}

.entry-content .footnotes li > p {
	display: inline;
	margin: 0;
}

/* Backref keeps its underline: a bare ↩ distinguished by color alone fails
   WCAG 1.4.1 (axe link-in-text-block, S6 verify). */
.entry-content a.footnote-backref {
	margin-inline-start: var(--space-1);
}

/* ==========================================================================
   §6b WRITING TEMPLATES (Session 6 — 03 §A: works index, work page, chapter
   chrome). The ember panels here ARE the Committed Rule on index surfaces;
   the reading interiors above stay exempt, their frame counts.
   ========================================================================== */

/* ---- Works index (archive-work.php) ------------------------------------ */

.archive-header {
	margin-block: var(--space-6) var(--space-7);
}

.archive-title,
.work-title,
.project-title {
	font-size: var(--step-5);
	margin-block-end: var(--space-3);
	/* Term archives put author-entered single tokens (OpenTelemetry) in this
	   display step — wrap rather than overflow at 320px (AA-4, S7 review). */
	overflow-wrap: anywhere;
}

.archive-description {
	max-inline-size: 60ch;
	color: var(--color-text-muted);
}

/* .work-list rides the shared flex wall — grouped onto .project-list (§6c),
   whose flex-not-grid comment is the load-bearing rationale (GATE-VISUAL-C
   Option A: the works index joined the wall 2026-08-09). */

/* One work = one wall panel (GATE-VISUAL-C: project-card scale). Body and
   cover wrap as a flex pair; the title is the single link (no card-wrapping
   link soup) — a cover is never a second link. The panel base is shared
   with .project-card (§6c) — 60KB budget. */
.work-card,
.project-card {
	flex: 1 1 24rem;
	min-inline-size: 0;
	display: flex;
	padding: clamp(var(--space-5), 3vw, var(--space-6));
	border-radius: var(--radius-2);
}

.work-card {
	flex-wrap: wrap;
	gap: var(--space-4) var(--space-5);
	align-items: flex-start;
}

.work-card-body {
	flex: 1 1 14rem;
	min-inline-size: 0;
}

/* Fixed width, NATURAL aspect (GATE-VISUAL-C row 2: the 2:3 card crop was
   declined — height follows each cover's own ratio; CLS stays closed by the
   img's intrinsic width/height). */
.work-card-cover {
	flex: 0 0 8rem;
}

.work-card-cover img,
.work-cover img {
	display: block;
	max-inline-size: 100%;
	block-size: auto;
	border-radius: var(--radius-1);
}

/* Index covers FILL the fixed 8rem slot; the single-work cover (.work-cover,
   14rem column) caps at natural size — a narrow cover never upscales (OR-3
   review). */
.work-card-cover img {
	inline-size: 100%;
}

/* S11 count-aware wall: <3 works → full-width rows (2-up wall measured 27%
   ember at 1280 — under the floor, OR-3c); the tighter padding floor holds
   ≤60% at 375 while the intro region ships empty. */
.work-list--sparse .work-card {
	flex-basis: 100%;
	padding: clamp(var(--space-4), 3vw, var(--space-6));
}

/* Card families (§6b/§6c/§6d) share the grouped rules below — 60KB budget. */
.work-card-status,
.work-status,
.project-card-role,
.post-card-date {
	font-weight: 700;
	font-size: var(--step--1);
	letter-spacing: 0.02em;
	margin-block-end: var(--space-2);
}

/* .work-card-title rides the §6c card-title group (step-2 — GATE-VISUAL-C
   row 3: wall panels, not full-width rows). */

:is(.work-card-title, .project-card-title, .post-card-title, .front-exhibit-title) a {
	color: inherit;
	text-decoration: none;
}

:is(.work-card-title, .project-card-title, .post-card-title, .front-exhibit-title) a:is(:hover, :focus-visible) {
	text-decoration: underline;
	text-underline-offset: 0.14em;
}

.work-card-blurb,
.project-card-summary,
.post-card-excerpt {
	max-inline-size: 60ch;
}

.work-card-blurb p:last-child,
.project-card-summary p:last-child,
.post-card-excerpt p:last-child {
	margin-block-end: 0;
}

.work-card-meta {
	font-size: var(--step--1);
	margin-block: var(--space-3) 0;
}

.archive-empty,
.toc-empty {
	color: var(--color-text-muted);
}

/* ---- Work page (single-work.php) --------------------------------------- */

.work-header,
.project-header {
	display: flex;
	flex-wrap: wrap;
	gap: var(--space-5) var(--space-7);
	align-items: flex-start;
	padding: clamp(var(--space-5), 4vw, var(--space-8));
	border-radius: var(--radius-2);
	margin-block-end: var(--space-7);
}

.work-header-text,
.project-header-text {
	flex: 1 1 24rem;
	min-inline-size: 0;
}

.work-cover {
	flex: 0 1 14rem;
}

.work-updated {
	margin-block-end: 0;
}

.work-start {
	margin-block: var(--space-5) 0;
}

/* Ink button for use ON the ember field (the ordinary .btn would vanish
   there — surface-strong and the community surface share a ramp slot). Both
   pairs proven by check-contrast.py. */
.btn-ink {
	display: inline-block;
	background: var(--night-900);
	color: var(--ground-50);
	font-weight: 700;
	text-decoration: none;
	padding: var(--space-3) var(--space-5);
	border-radius: var(--radius-1);
	transition: background-color var(--dur-1) var(--ease-out);
}

.btn-ink:hover {
	background: var(--night-800);
}

.work-blurb {
	margin-block-end: var(--space-7);
}

/* The in-page chapter TOC (W-2) — the gallery list. Rows are whole-link
   targets; the current-reading state lives in the N-4 sidebar, not here. */
.work-toc {
	margin-block-end: var(--space-7);
}

.toc-list {
	list-style: none;
	margin: 0;
	padding: 0;
	max-inline-size: 52rem;
}

.toc-item + .toc-item {
	border-block-start: 1px solid var(--color-border);
}

.toc-link {
	display: grid;
	grid-template-columns: 3ch minmax(0, 1fr) auto;
	column-gap: var(--space-4);
	align-items: baseline;
	padding: var(--space-3);
	min-block-size: 44px;
	color: var(--color-text);
	text-decoration: none;
	border-radius: var(--radius-1);
}

.toc-link:hover {
	background: var(--color-border);
}

.toc-no {
	font-variant-numeric: tabular-nums;
	font-weight: 700;
	color: var(--color-text-muted);
	text-align: end;
}

.toc-title {
	font-family: var(--font-display);
	font-size: var(--step-1);
	line-height: 1.25;
}

.toc-date {
	font-size: var(--step--1);
	color: var(--color-text-muted);
	white-space: nowrap;
}

@media (max-width: 479.98px) {

	.toc-link {
		grid-template-columns: 3ch minmax(0, 1fr);
	}

	.toc-date {
		grid-column: 2;
	}
}

/* ---- Chapter prev/contents/next (parts/chapter-nav.php) ---------------- */

.chapter-nav {
	display: grid;
	grid-template-columns: 1fr auto 1fr;
	gap: var(--space-3);
	align-items: stretch;
	margin-block: var(--space-6);
}

.chapter-nav-link {
	display: flex;
	flex-direction: column;
	justify-content: center;
	gap: var(--space-1);
	min-block-size: 44px;
	min-inline-size: 0;
	padding: var(--space-3) var(--space-4);
	border: 1px solid var(--color-border);
	border-radius: var(--radius-2);
	color: var(--color-text);
	text-decoration: none;
}

.chapter-nav-link:hover {
	background: var(--color-border);
}

.chapter-nav-next {
	text-align: end;
}

.chapter-nav-contents {
	align-self: center;
	color: var(--color-link);
}

.chapter-nav-dir {
	font-size: var(--step--1);
	color: var(--color-text-muted);
}

.chapter-nav-name {
	font-family: var(--font-display);
	line-height: 1.25;
	/* Real chapter/post titles carry long unbreakable tokens (S8 review found
	   a live 320px overflow in post-nav) — break inside the panel, never push
	   the page sideways (AA-4). */
	overflow-wrap: anywhere;
}

@media (max-width: 639.98px) {

	.chapter-nav {
		grid-template-columns: 1fr;
	}

	.chapter-nav-next {
		text-align: start;
	}

	.chapter-nav-spacer {
		display: none;
	}
}

/* ---- Download box (parts/download-box.php) ----------------------------- */

.download-box {
	margin-block: var(--space-6);
}

.download-box-link {
	display: inline-flex;
	/* The meta names the real filename (Nehemiah_Newell_Resume_2026.pdf) —
	   one unbreakable token that must stack/wrap at 320px, never push the
	   page sideways (AA-4, S7 review). */
	flex-wrap: wrap;
	align-items: center;
	gap: var(--space-3);
	min-block-size: 44px;
	padding: var(--space-3) var(--space-5);
	background: var(--color-surface);
	color: var(--color-on-surface);
	font-weight: 700;
	text-decoration: none;
	border-radius: var(--radius-1);
}

.download-box-link:hover .download-box-label {
	text-decoration: underline;
	text-underline-offset: 0.14em;
}

/* On the lit page (the résumé slot) the page-link repaint would set this
   link ochre-on-ochre — same cascade family as the .surface a.btn-ink bug
   (GV-A, owner-found). The box carries its own proven on-surface pair. */
.lit-page a.download-box-link {
	color: var(--color-on-surface);
}

.download-box-icon {
	flex: none;
}

.download-box-meta {
	font-weight: 400;
	font-size: var(--step--1);
	overflow-wrap: anywhere;
}

/* ---- Archive pagination + in-post page links --------------------------- */

.navigation.pagination {
	margin-block: var(--space-7);
}

.nav-links {
	display: flex;
	flex-wrap: wrap;
	gap: var(--space-2);
}

.page-numbers,
.post-page-numbers {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	min-inline-size: 44px;
	min-block-size: 44px;
	padding-inline: var(--space-3);
	border: 1px solid var(--color-border);
	border-radius: var(--radius-1);
	color: var(--color-link);
	text-decoration: none;
}

.page-numbers.current,
.post-page-numbers.current {
	background: var(--color-surface);
	color: var(--color-on-surface);
	border-color: transparent;
	font-weight: 700;
}

.page-numbers.dots {
	border: 0;
}

.page-links {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: var(--space-2);
	margin-block-start: var(--space-5);
}

.page-links-title {
	/* Ground-following muted (S8 review): wp_link_pages renders inside
	   .entry-content on lit pages too, where the scheme muted (ochre-200)
	   computes 1.34:1 on the paper — --entry-muted follows the actual ground. */
	color: var(--entry-muted, var(--color-text-muted));
	font-size: var(--step--1);
	margin-inline-end: var(--space-2);
}

/* ==========================================================================
   §6c PROFESSIONAL TEMPLATES (Session 7 — 03 §A: projects index, project
   page, résumé, generic pages). The pro register maps .surface to the
   banked ember (ochre-700 + paper text); these panels are the Committed
   Rule on the professional index, same as §6b's community walls.
   ========================================================================== */

/* ---- Projects index (archive-project.php) ------------------------------- */

.archive-action {
	margin-block: var(--space-4) 0;
}

/* The gallery wall: wrapping flex, not a fixed grid — a partial row's panels
   GROW to fill it, so the ember field never leaves empty night slots and the
   Committed Rule's 30–60% share holds at any project count (a 3-up seed
   index measured 25% on the grid version). Order is source order (owner
   menu_order); a featured project takes a full row via flex-basis (P-1). */
.work-list,
.project-list {
	display: flex;
	flex-wrap: wrap;
	gap: var(--space-5);
	margin-block-end: var(--space-7);
}

/* The panel base (flex/size/padding/radius) rides the §6b .work-card group. */
.project-card {
	flex-direction: column;
	gap: var(--space-4);
}

.project-card-body {
	min-inline-size: 0;
}

/* Screenshot sits at the panel foot so mixed-height rows keep a level base. */
.project-card-shot {
	margin-block-start: auto;
}

.project-card-shot img,
.project-cover img {
	display: block;
	inline-size: 100%;
	block-size: auto;
	border: 1px solid var(--color-border);
	border-radius: var(--radius-1);
}

.project-card.is-featured {
	flex-basis: 100%;
	flex-direction: row;
	flex-wrap: wrap;
	align-items: flex-start;
	gap: var(--space-5) var(--space-6);
	padding: clamp(var(--space-5), 4vw, var(--space-7));
}

.project-card.is-featured .project-card-body {
	flex: 1 1 24rem;
}

.project-card.is-featured .project-card-shot {
	flex: 0 1 20rem;
	margin-block-start: 0;
}

/* Text marker, never color alone (AA-3): book small caps, like .chapter-no.
   .post-card-flag is the blog twin (a pinned/sticky post, S8). */
.project-card-flag,
.post-card-flag,
.project-role,
.front-exhibit-kicker {
	font-variant-caps: all-small-caps;
	letter-spacing: 0.05em;
	font-size: var(--step-0);
	margin-block-end: var(--space-2);
}

/* Project names are often one long unbreakable token (WriterAssistant);
   overflow-wrap keeps them inside the panel at 320px (AA-4) instead of
   forcing page scroll. Applies only when a word exceeds the line box. */
.work-card-title,
.project-card-title,
.post-card-title {
	font-size: var(--step-2);
	margin-block: 0 var(--space-2);
	overflow-wrap: anywhere;
}

.project-card.is-featured .project-card-title {
	font-size: clamp(var(--step-2), 1.7rem + 2.6vw, var(--step-3));
}

/* Kicker-group override: the role keeps its larger gap (P-1 rhythm). */
.project-card-role {
	margin-block-end: var(--space-3);
}

/* ---- Tech tags (P-1) ----------------------------------------------------
   Chips inherit their ground's proven text pair; on the ember panels that is
   the on-surface paper, on the chrome ground the scheme text. Links only on
   the project page (cards stay single-link). */
.tech-list,
.tag-list {
	list-style: none;
	display: flex;
	flex-wrap: wrap;
	gap: var(--space-2);
	margin: var(--space-4) 0 0;
	padding: 0;
}

.project-card .tech-list {
	margin-block-start: var(--space-4);
}

.tech-chip,
.tag-chip {
	border: 1px solid currentColor;
	border-radius: var(--radius-1);
	padding: var(--space-1) var(--space-3);
	font-size: var(--step--1);
	font-weight: 700;
	letter-spacing: 0.02em;
	/* Real tech names are long unbreakable tokens
	   (Microsoft.Extensions.DependencyInjection) — wrap inside the chip at
	   320px instead of dragging the page sideways (AA-4, S7 review). */
	overflow-wrap: anywhere;
}

.tech-chip a {
	display: inline-block;
	padding-block: var(--space-1);
	margin-block: calc(-1 * var(--space-1));
}

/* ---- Project page (single-project.php) ---------------------------------- */

.project-cover {
	flex: 1 1 18rem;
	max-inline-size: 26rem;
}

/* .project-role (the recruiter's first-read kicker, P-1) rides the §6c
   kicker group; .project-title rides the §6b display-title group. */

.project-summary {
	max-inline-size: 60ch;
}

.work-links,
.project-links {
	list-style: none;
	display: flex;
	flex-wrap: wrap;
	gap: var(--space-3);
	margin: var(--space-5) 0 0;
	padding: 0;
}

.work-link,
.project-link {
	display: inline-flex;
	align-items: center;
	gap: var(--space-2);
	min-block-size: 44px;
}

.external-glyph {
	flex: none;
}

.project-writeup {
	margin-block-end: var(--space-7);
}

/* ---- Generic page + résumé (page.php, page-resume.php) ------------------ */

.page-header {
	margin-block-end: var(--space-6);
}

.page-title {
	font-size: clamp(var(--step-2), 1.7rem + 2.6vw, var(--step-3));
	margin-block-end: 0;
	overflow-wrap: anywhere;
}

/* ==========================================================================
   §6d HOME, BLOG & UTILITY TEMPLATES (S8; reworked OR-2c, GATE-VISUAL-P).
   `/` = Option B: compact solo hero, ember wall in the first viewport,
   What's-New rows; /personal/ re-hosts the open-flame exhibit. The blog is
   professional now (D-SPLIT flip) — banked-ember cards that the notes
   surfaces reuse open-flame.
   ========================================================================== */

/* ---- Landings (front-page.php, page-personal.php) ------------------------ */

.front-hero {
	display: grid;
	gap: var(--space-6) var(--space-7);
	align-items: center;
	margin-block: var(--space-6) var(--space-8);
}

/* Option B (GATE-VISUAL-P): tighter solo hero — the Exhibits wall (the
   Committed Rule holder on `/`) must reach the first viewport. */
.front-hero--solo {
	margin-block: var(--space-5) var(--space-6);
}

/* Two columns only while the exhibit exists (page-personal.php sets the
   class since OR-2c); before the first published chapter the hero holds one
   full-width column instead of a silent empty grid track (S8 review). */
@media (min-width: 960px) {

	.front-hero.has-exhibit {
		grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
	}
}

/* Stacked hero (mobile): the ember must still reach into the first viewport
   — the Committed Rule binds both landings at 375px too (S8). */
@media (max-width: 959.98px) {

	.front-hero {
		margin-block-start: 0;
	}
}

.front-hero-title {
	font-size: var(--step-5);
	margin-block-end: var(--space-4);
	/* The hero h1 is owner-edited page content (H-1) — wrap long tokens. */
	overflow-wrap: anywhere;
}

@media (max-width: 479.98px) {

	.front-hero-title {
		/* Same drop the chapter/work display titles take at phone widths:
		   presence without pushing the exhibit below the fold. */
		font-size: var(--step-2);
	}

	.front-cta .btn,
	.front-cta .btn-ghost {
		/* Both doors on one row at 375px (the wrap cost a full button row). */
		padding-inline: var(--space-4);
	}

	/* S11: the real hero intro runs ~8 lines at 375 and pushed the wall below
	   the fold (20% ember). Tighter lede + halved gaps restore Option B's
	   first-viewport wall (measured back to 31%). */
	.front-hero-intro {
		font-size: 1rem;
		line-height: 1.55;
	}

	.front-cta {
		margin-block-end: var(--space-6);
	}

	.front-section-title {
		margin-block-end: var(--space-4);
	}
}

.front-hero-intro {
	color: var(--color-text-muted);
	max-inline-size: 36rem;
}

.front-hero-intro :last-child {
	margin-block-end: 0;
}

.front-cta {
	display: flex;
	flex-wrap: wrap;
	gap: var(--space-3);
	margin-block: var(--space-5) 0;
}

/* The open-flame exhibit (parts/front-exhibit.php) rides .surface-strong. */
.front-exhibit {
	padding: clamp(var(--space-5), 3vw, var(--space-7));
	border-radius: var(--radius-2);
}

/* .front-exhibit-kicker rides the §6c kicker group; the title's link rides
   the §6b title-link group. */
.front-exhibit-title {
	font-size: clamp(var(--step-2), 1.7rem + 2.6vw, var(--step-3));
	margin-block-end: var(--space-2);
	overflow-wrap: anywhere;
}

.front-exhibit-line {
	font-family: var(--font-display);
	font-style: italic;
	max-inline-size: 48ch;
}

.front-toc {
	list-style: none;
	margin: var(--space-4) 0 0;
	padding: 0;
}

.front-toc li {
	display: flex;
	flex-wrap: wrap;
	align-items: baseline;
	justify-content: space-between;
	gap: var(--space-1) var(--space-4);
}

.front-toc a {
	display: inline-flex;
	align-items: center;
	min-block-size: 44px;
	font-weight: 700;
	/* Real chapter titles can carry long unbreakable tokens (S7 lesson). */
	overflow-wrap: anywhere;
	min-inline-size: 0;
}

.front-toc time {
	font-size: var(--step--1);
	white-space: nowrap;
}

.front-exhibit-cta {
	margin-block: var(--space-4) 0;
}

.front-projects,
.front-blog {
	margin-block-end: var(--space-8);
}

.front-section-title {
	font-size: var(--step-2);
	margin-block-end: var(--space-5);
}

/* What's-New rows: quiet on the night ground — the ember stays on the
   walls above (Committed Rule). */
.front-posts {
	list-style: none;
	margin: 0;
	padding: 0;
	max-inline-size: 52rem;
}

.front-posts li {
	display: flex;
	flex-wrap: wrap;
	align-items: baseline;
	justify-content: space-between;
	gap: var(--space-1) var(--space-4);
	border-block-end: 1px solid var(--color-border-strong);
}

.front-posts a {
	display: inline-flex;
	align-items: center;
	min-block-size: 44px;
	/* Real post titles can carry long unbreakable tokens (S7 lesson). */
	overflow-wrap: anywhere;
	min-inline-size: 0;
}

.front-posts time {
	font-size: var(--step--1);
	color: var(--color-text-muted);
	white-space: nowrap;
}

.front-blog-all {
	margin-block: var(--space-4) 0;
	font-weight: 700;
}

/* Personal What's-New type kicker ("Chapter · {work}" / "Note"), inline;
   the title link absorbs free space so the date stays at the row end. */
.whats-new-kicker {
	font-variant-caps: all-small-caps;
	letter-spacing: 0.05em;
	color: var(--color-text-muted);
	/* Work titles can carry long unbreakable tokens (S7 lesson). */
	overflow-wrap: anywhere;
	min-inline-size: 0;
}

.whats-new .front-posts a {
	margin-inline-end: auto;
}

/* ---- Blog index + archives (home.php, archive.php, parts/card-post.php) - */

.post-list {
	display: flex;
	flex-direction: column;
	gap: var(--space-5);
	margin-block-end: var(--space-7);
}

.post-card {
	padding: clamp(var(--space-5), 3vw, var(--space-6));
	border-radius: var(--radius-2);
}

/* .post-card date/title/excerpt/flag ride the §6b/§6c card-family groups. */

/* ---- Blog post (single.php): lit-page header ----------------------------- */

.post-header {
	margin-block-end: var(--space-7);
}

.post-title {
	font-size: clamp(var(--step-2), 1.7rem + 2.6vw, var(--step-3));
	margin-block-end: var(--space-3);
	overflow-wrap: anywhere;
}

.post-meta {
	font-size: var(--step--1);
	color: var(--color-page-muted);
	margin-block-end: 0;
}

/* ---- Search (search.php) + 404 (404.php) --------------------------------- */

.search-form {
	display: flex;
	flex-wrap: wrap;
	gap: var(--space-2);
	max-inline-size: 36rem;
	margin-block: var(--space-4) 0;
}

.search-form label {
	flex: 1 1 14rem;
}

.search-field {
	inline-size: 100%;
	min-block-size: 44px;
	padding: var(--space-2) var(--space-3);
	font: inherit;
	color: var(--color-text);
	background: transparent;
	/* An input's boundary is a UI-component indicator (WCAG 1.4.11 ≥3:1):
	   the decorative hairline tokens compute ~2:1 night / 1.25:1 light here
	   (S8 review) — the muted text token clears 3:1 on both grounds. */
	border: 1px solid var(--color-text-muted);
	border-radius: var(--radius-1);
}

.search-field::placeholder {
	color: var(--color-text-muted);
	opacity: 1;
}

.search-list {
	display: flex;
	flex-direction: column;
	gap: var(--space-6);
	margin-block: 0 var(--space-7);
	max-inline-size: 52rem;
}

.search-entry-type {
	font-variant-caps: all-small-caps;
	letter-spacing: 0.05em;
	color: var(--color-text-muted);
	margin-block-end: var(--space-1);
}

.search-entry-title {
	font-size: var(--step-1);
	margin-block-end: var(--space-2);
	overflow-wrap: anywhere;
}

.search-entry-summary {
	max-inline-size: 60ch;
}

.search-entry-summary p:last-child {
	margin-block-end: 0;
}

.error-copy {
	max-inline-size: 60ch;
}

/* Division label over each exit group (parts/section-links.php, v1.2). */
.section-links-title {
	margin-block: 0 var(--space-1);
	font-weight: 700;
	color: var(--color-text-muted);
	text-transform: uppercase;
	letter-spacing: 0.06em;
	font-size: 0.8em;
}

.section-links {
	list-style: none;
	display: flex;
	flex-wrap: wrap;
	gap: var(--space-2) var(--space-5);
	margin: 0 0 var(--space-5);
	padding: 0;
}

.section-links a {
	display: inline-flex;
	align-items: center;
	min-block-size: 44px;
	font-weight: 700;
}

.error-search-title {
	font-size: var(--step-1);
}

/* ---- Work-page feed link (W-13 affordance, single-work.php) -------------- */

.work-feed {
	margin-block: 0 var(--space-5);
	font-size: var(--step--1);
}

/* ==========================================================================
   §7 SURFACE UTILITIES — the committed ember fields (The Committed Rule:
   these are walls, not garnish; templates apply them to section grounds).
   ========================================================================== */

.surface {
	background: var(--color-surface);
	color: var(--color-on-surface);
	/* Focus ring must clear 3:1 against the ember, not the scheme ground —
	   the on-surface pairs are proven ≥4.5:1 in both registers (WCAG 1.4.11,
	   S6 review). Scoped to the surfaces; the chrome keeps the scheme ring. */
	--color-focus-ring: var(--color-on-surface);
}

.surface a {
	color: var(--color-on-surface);
}

.surface-strong {
	background: var(--color-surface-strong);
	color: var(--color-on-surface-strong);
	--color-focus-ring: var(--color-on-surface-strong);
}

.surface-strong a {
	color: var(--color-on-surface-strong);
}

/* Buttons carry their OWN proven pairs — the surface link remaps above must
   not repaint them: community on-surface is night-ink, which vanished on the
   ink button (night-900 on night-900; owner-found 2026-07-28 — axe missed it
   in both schemes, and check-contrast.py proves pairs, not the cascade). */
.surface a.btn,
.surface-strong a.btn {
	color: var(--color-on-surface-strong);
}

.surface a.btn-ink,
.surface-strong a.btn-ink {
	color: var(--ground-50);
}

.btn,
.search-submit {
	display: inline-block;
	background: var(--color-surface-strong);
	color: var(--color-on-surface-strong);
	/* .btn also lands on <button>/<input>, which don't inherit fonts. */
	font: inherit;
	font-weight: 700;
	text-decoration: none;
	padding: var(--space-3) var(--space-5);
	border-radius: var(--radius-1);
	border: 0;
	cursor: pointer;
	transition: background-color var(--dur-1) var(--ease-out);
}

.btn:hover,
.search-submit:hover {
	background: var(--ochre-400);
	color: var(--night-900);
}

/* Ghost sibling of .btn for a second door beside a filled CTA (front hero):
   outline + the ground's own proven text pair — currentColor keeps the border
   honest in both schemes, and the ember stays on the primary. */
.btn-ghost {
	display: inline-block;
	background: transparent;
	color: var(--color-text);
	font-weight: 700;
	text-decoration: none;
	padding: calc(var(--space-3) - 2px) var(--space-5);
	border: 2px solid currentColor;
	border-radius: var(--radius-1);
	transition: color var(--dur-1) var(--ease-out);
}

.btn-ghost:hover {
	color: var(--color-link);
}

/* ==========================================================================
   §8 STYLE GUIDE (page-style-guide.php — a LIVE route since S11 kept the
   page). TRIMMED at OR-2c: only the token-swatch rules survive; the page
   otherwise styles itself inline (its own idiom — S13 label fix included).
   ========================================================================== */

.sg-grid {
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr));
	gap: var(--space-2);
	padding: 0;
	list-style: none;
}

/* Swatch only — labels sit on the page ground (4.5:1 small-text limit). */
.sg-chip-swatch {
	display: block;
	block-size: 2.5rem;
	border-radius: var(--radius-1);
	border: 1px solid var(--color-border);
	margin-block-end: var(--space-2);
}
