/* ============================================================================
   blog.css — the reading experience: single posts and the archive.

   Loads last, so it overrides the shared .entry rules in sections.css without
   specificity games. Those rules stay put: page.php and the legal pages still
   depend on them. Everything here is scoped to .single / .blog / .archive.

   Three things this file fixes, recorded because they are easy to reintroduce:

   1. h5 and h6 were styled NOWHERE in the theme (base.css selects h1,h2,h3
      only), so they fell to UA defaults — 0.83em and 0.67em of the body font,
      i.e. SMALLER than body copy, in the wrong typeface.
   2. The type scale halved from h2 (44px) to h3 (22px), then h3/h4/body sat
      within 5px of each other. No usable hierarchy in a long post.
   3. Paragraph gaps collapsed to 16px against a 28px line-height, and headings
      used that same 16px above and below. The body read as one grey slab and
      headings floated between paragraphs instead of binding to their section.
   ========================================================================= */

/* ---------- 1. shell ---------- */

/* The archive's shell — wide, for the three-up post grid. */
.container--article { max-width: var(--container-article); }

/* Single post: one centred column, two shells.

   --container-post holds the hero text and the footer; --container-post-wide
   holds the article body and the featured image. Both are centred by
   .container, and .single .entry centres a --measure-article track inside the
   wide one. Equal widths around a shared centre, so the hero title, the prose
   and the author card land on the same left AND right edge at every viewport
   with no breakpoint. Media grows symmetrically into the two side tracks —
   cleaner than negative margins, and every child stays in normal flow.

   The measure itself is documented on --measure-article in tokens.css; no
   character count is repeated here, so the two can never drift apart. */
.container--post      { max-width: var(--container-post); }
.container--post-wide { max-width: var(--container-post-wide); }

.single .entry {
	display: grid;
	grid-template-columns:
		[full-start] minmax(0, 1fr)
		[content-start] min(100%, var(--measure-article)) [content-end]
		minmax(0, 1fr) [full-end];
}
/* min-width: 0 so a wide <pre> or table honours its own overflow-x instead of
   stretching the track — grid items default to min-width: auto. */
.single .entry > * { grid-column: content; margin-bottom: 0; min-width: 0; }
.single .entry > :is(figure, pre, table, .wp-block-table, .wp-block-image, .wp-block-gallery, .wp-block-embed, .alignwide, .alignfull) {
	grid-column: full;
}
/* The first block sets its own top edge. sections.css still gives a bare
   .entry h2/h3/h4 a top margin at (0,1,1), and the flow rules in section 3
   only reach children that HAVE a previous sibling. Until the contents
   disclosure was removed it always occupied this slot, so this never showed. */
.single .entry > :first-child { margin-top: 0; }

/* ---------- 2. type scale ---------- */

/* .hero.product-hero already left-aligns and clears margin-inline; this only
   drops the marketing size (68px) to something a post title can wear. The blog
   index and category archives share it so the whole section reads as one thing. */
.single .hero h1,
.blog .hero h1,
.archive .hero h1,
.changelog-hero h1 {
	font-size: var(--text-article-h1);
	line-height: 1.1;
	letter-spacing: -0.02em;
	max-width: 24ch;
}

.single .entry :is(h1, h2, h3, h4, h5, h6) {
	font-family: var(--font-display);
	font-weight: 600;
	letter-spacing: -0.01em;
	text-wrap: balance;
}
/* The post title is already the page h1. Content carrying another one (imported
   posts do) otherwise falls to base.css at --text-h1 — up to 68px, LOUDER than
   the title above it — in the wrong face, with no scroll-margin and none of the
   flow overrides below. */
.single .entry h1 { font-size: var(--text-article-h1); line-height: 1.1; }
.single .entry h2 { font-size: var(--text-article-h2); line-height: 1.15; }
.single .entry h3 { font-size: var(--text-article-h3); line-height: 1.2; }
.single .entry h4 { font-size: var(--text-article-h4); line-height: 1.25; }
.single .entry h5 { font-size: var(--text-article-h5); line-height: 1.3; }
.single .entry h6 {
	font-size: var(--text-article-h6);
	line-height: 1.3;
	text-transform: uppercase;
	letter-spacing: 0.06em;
	color: var(--ink-soft);
}

/* Anchors are injected server-side. .admin-bar-pp is sticky at 57px, so a
   jumped-to heading needs to clear it plus a little air, or it lands underneath
   the bar and looks like the link went to the wrong place. */
.single .entry :is(h1, h2, h3, h4, h5, h6) {
	scroll-margin-top: calc(var(--space-7) + var(--space-6));
}

/* ---------- 3. rhythm ---------- */

/* Article body runs a step above the site's 17px. A post is read continuously,
   not scanned like a landing page, and the larger size is what lets the measure
   stay near 80 characters while the column still looks generous. */
.single .entry {
	font-size: var(--text-lg);
	line-height: 1.7;
}

/* One flow gap, then headings override it: generous above, tight below, so a
   heading belongs to the text it introduces rather than floating between. */
.single .entry > * + * { margin-top: var(--space-6); }
.single .entry > * + :is(h1, h2) { margin-top: var(--space-8); }
.single .entry > * + h3 { margin-top: var(--space-7); }
.single .entry > * + :is(h4, h5, h6) { margin-top: var(--space-6); }
.single .entry > :is(h1, h2, h3, h4, h5, h6) + * { margin-top: var(--space-3); }

/* The opening paragraph carries the reader in: one rung above body, softer ink.
   first-child, not first-of-type — after a lead image the paragraph that
   follows is body copy, not a lede. (first-of-type was there because the
   contents disclosure used to occupy the first-child slot.) */
.single .entry > p:first-child {
	font-size: var(--text-article-lede);
	line-height: 1.55;
	color: var(--ink-soft);
}

/* ---------- 4. prose details ---------- */

/* Gutenberg wraps images, galleries, TABLES, embeds and pullquotes in <figure>,
   which takes the UA's `margin: 1em 40px`. Only the 40px side indent is wrong.
   Do NOT reset the block axis here: a figure-targeting selector scores (0,3,0)
   — :is() inherits .wp-block-image's (0,1,0) — and beats every `> * + *` rule
   in section 3 at (0,2,0). That is what used to glue tables and images flush
   against the paragraph above them. Top-level figures take their top margin
   from the flow rules instead; an author rule beats the UA regardless. */
.single .entry :is(figure, .wp-block-image) { margin-inline: 0; }
/* Nested figures (gallery items, figures inside columns) sit outside the flow
   rules, so those do need their block margins cleared. */
.single .entry :is(figure, .wp-block-image) figure { margin-block: 0; }
.single .entry :is(figure, .wp-block-image) img {
	display: block;
	width: 100%;
	height: auto;
	border: 1px solid var(--line);
	border-radius: var(--r-card);
}
.single .entry figcaption {
	margin-top: var(--space-3);
	font-size: var(--text-small);
	line-height: 1.5;
	color: var(--ink-soft);
	text-align: center;
}

/* An inline image (a logo, a badge) is not a figure and should not be boxed;
   sections.css borders every .entry img, so undo it here. */
.single .entry p > img { border: 0; border-radius: 0; width: auto; display: inline; }

/* .entry pre sets 13px and .entry code sets 0.9em, so <pre><code> — exactly
   what the code block emits — compounded to 11.7px. */
.single .entry pre { font-size: var(--text-small); line-height: 1.6; }
.single .entry pre code { font-size: inherit; }
.single .entry :not(pre) > code {
	background: var(--surface-alt);
	border: 1px solid var(--line);
	border-radius: var(--r-inner);
	padding: 0.1em 0.35em;
}

/* A table wider than the column has to scroll rather than break the layout. */
.single .entry :is(.wp-block-table, .table-scroll) { overflow-x: auto; }
.single .entry :is(.wp-block-table, .table-scroll) table { margin: 0; }

.single .entry blockquote {
	padding-left: var(--space-5);
	border-left: 3px solid var(--accent, var(--line));
	font-size: var(--text-lg);
	line-height: 1.55;
}
.single .entry blockquote cite {
	display: block;
	margin-top: var(--space-3);
	font-size: var(--text-small);
	font-style: normal;
	color: var(--ink-soft);
}

.single .entry .wp-block-pullquote {
	padding: var(--space-6) 0;
	border-top: 1px solid var(--line);
	border-bottom: 1px solid var(--line);
	text-align: center;
}
.single .entry .wp-block-pullquote blockquote { border: 0; padding: 0; }

.single .entry .wp-block-separator {
	border: 0;
	border-top: 1px solid var(--line);
	margin-block: var(--space-8);
	width: 100%;
}

.single .entry .wp-block-details {
	border: 1px solid var(--line);
	border-radius: var(--r-card);
	padding: var(--space-4) var(--space-5);
}
.single .entry .wp-block-details summary { cursor: pointer; font-weight: 600; }

.single .entry .wp-block-embed figcaption { text-align: left; }
.single .entry .wp-block-embed__wrapper { position: relative; }
.single .entry .wp-block-embed iframe { width: 100%; border-radius: var(--r-card); }

.single .entry .wp-block-buttons { display: flex; flex-wrap: wrap; gap: var(--space-3); }

.single .entry .wp-block-columns { display: flex; flex-wrap: wrap; gap: var(--space-6); }
.single .entry .wp-block-column { flex: 1 1 14rem; min-width: 0; }

/* ---------- 5. post meta ---------- */

.post-meta {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: var(--space-2) var(--space-3);
	margin-top: var(--space-5);
}
.post-meta__term {
	font-family: var(--font-mono);
	font-size: var(--text-label);
	text-transform: uppercase;
	letter-spacing: 0.06em;
	color: var(--ink-soft);
	text-decoration: none;
	border: 1px solid var(--line);
	border-radius: var(--r-inner);
	padding: 0.25em 0.6em;
}
.post-meta__term:hover { color: var(--ink); }

.post-cover {
	margin-top: var(--space-6);
	border: 1px solid var(--line);
	border-radius: var(--r-card);
	overflow: hidden;
}
.post-cover img { display: block; width: 100%; height: auto; }

/* ---------- 6. archive ---------- */

/* The listing opens closer to the filter row than a full --space-section: the
   hero already pays --space-6 below the pills. Overriding padding-top alone
   leaves the padding-block shorthand's bottom value intact. */
.blog .section,
.archive .section { padding-top: var(--space-5); }

/* -- category filter row -- */

.post-filter {
	display: flex;
	flex-wrap: wrap;
	gap: var(--space-2);
	margin-top: var(--space-5);
}
/* .chip's shape in the human voice. Not literally .chip: that is a static
   badge in the mono artifact voice, shared with the 404 shelf and the animated
   scene chips, so bolting hover/focus/current states onto it would leak into
   them. Its 2px 10px padding also lands well under the WCAG 2.2 (2.5.8) 24px
   target minimum, where this is a real control. */
.post-filter__pill {
	display: inline-block;
	padding: var(--space-2) var(--space-4);
	border: 1px solid var(--line);
	border-radius: 999px;
	background: var(--surface-alt);
	font-size: var(--text-small);
	line-height: 1.2;
	color: var(--ink-soft);
	text-decoration: none;
	transition: color 0.15s var(--ease-out), border-color 0.15s var(--ease-out), background-color 0.15s var(--ease-out);
}
.post-filter__pill:hover,
.post-filter__pill:focus-visible {
	color: var(--ink);
	border-color: var(--line-strong);
	text-decoration: none;
}
/* "you are here" is the ink fill the pagination already uses for the current
   page number — one meaning, one look. The fill is also the non-colour cue. */
.post-filter__pill[aria-current] {
	background: var(--ink);
	border-color: var(--ink);
	color: var(--surface);
}

/* -- the grid -- */

/* Explicit column counts, never auto-fit: the theme has none, and minmax(0,1fr)
   is what stops a long unbreakable title blowing out a column.
   1100 was the article layout's hinge before the contents rail went, so it is
   reused rather than inventing a third breakpoint; 820 is the site's own
   card-grid collapse. Without the 1100 step, three-up at a 900px viewport
   gives 262px cards and a 24px title wrapping at about nine characters. */
.post-grid { display: grid; }
.post-grid--lead {
	grid-template-columns: repeat(3, minmax(0, 1fr));
	gap: var(--space-5);
	align-items: stretch;
}
/* Borderless rows need more air than bordered ones: nothing but the gap says
   where one entry ends and the next begins. Space separates the two tiers,
   not a rule — dotted hairlines are component-internal in this theme. */
.post-grid--log {
	grid-template-columns: repeat(3, minmax(0, 1fr));
	column-gap: var(--space-6);
	row-gap: var(--space-7);
	margin-top: var(--space-7);
}
@media (max-width: 1099px) {
	.post-grid--lead,
	.post-grid--log { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 820px) {
	.post-grid--lead,
	.post-grid--log { grid-template-columns: minmax(0, 1fr); }
}

/* -- the card -- */

.post-card {
	position: relative;   /* anchors the stretched title link */
	display: flex;
	flex-direction: column;
	min-width: 0;
}
.post-card__meta {
	display: flex;
	flex-wrap: wrap;
	align-items: baseline;
	gap: var(--space-3);
	margin: 0;
}
/* a date is real data — the one mono item on the card */
.post-card__date {
	font-family: var(--font-mono);
	font-size: var(--text-label);
	color: var(--ink);
	white-space: nowrap;
}
/* a category NAME is a human label, not an artifact: sans, muted. The old
   .post-list__term set it in mono, against the theme's own rule. */
.post-card__term {
	font-size: var(--text-label);
	color: var(--ink-soft);
}
/* not clamped: hiding words of a heading from sighted readers trades a real
   loss for a cosmetic one. Rows stretch to the tallest card and the other
   cards' spacers simply open wider, which is the layout working. This handles
   the real failure mode, an unbreakable token. */
.post-card__title {
	margin: 0;
	overflow-wrap: anywhere;
}
.post-card__link { color: inherit; text-decoration: none; }
.post-card__link:hover { color: var(--blue); text-decoration: underline; text-underline-offset: 3px; }
/* The whole card is the click target, but the link's accessible name stays the
   title alone (see parts/chrome/post-card.php). Two costs worth knowing: this
   blocks mouse text-selection inside the card, and any link added inside a card
   later needs position: relative; z-index: 1 to sit above it. */
.post-card__link::after {
	content: '';
	position: absolute;
	inset: 0;
}

/* lead — bordered, excerpt pinned to the card floor */
.post-card--lead {
	gap: var(--space-3);
	/* the two tiers must differ by more than a border, and the spacer needs
	   room to open */
	min-height: 260px;
	padding: var(--space-5);
	background: var(--surface);
	border: 1px solid var(--line);
	border-radius: var(--r-card);
}
.post-card--lead .post-card__title {
	font-size: var(--text-article-h3);
	line-height: 1.2;
}
.post-card--lead .post-card__excerpt {
	margin: auto 0 0;   /* the spacer — .feature-card__tail's pattern */
	color: var(--ink-soft);
	font-size: var(--text-small);
	display: -webkit-box;
	-webkit-box-orient: vertical;
	-webkit-line-clamp: 2;
	line-clamp: 2;
	overflow: hidden;
}

/* log — type on the page, no card chrome. The hierarchy comes from shrinking
   this tier, not from inflating the one above: --text-article-h3 is exactly
   what archive titles already used at >=900px. */
.post-card--log { gap: var(--space-2); }
.post-card--log .post-card__title {
	font-size: var(--text-lg);
	line-height: 1.3;
}

/* ---------- 7. changelog ---------- */

/* One centred stream, newest first. The shell is wider than a post because a
   release carries a meta rail beside its prose; what is left after the rail and
   the gutter lands near --measure-article, so an entry still reads like a post. */
.container--changelog { max-width: var(--container-changelog); }

/* A shorter lead-in than a marketing hero. .hero pays --space-section top and
   bottom, which is right above a headline that has to carry a page on its own;
   here the h1 is one word over a dated list, so ~100px of nothing above it read
   as a gap rather than as room. --space-7 keeps it on the scale. */
.changelog-hero {
	text-align: center;
	padding-top: var(--space-7);
	padding-bottom: var(--space-6);
}
.changelog-hero .hero__sub { margin-inline: auto; }
.changelog-hero .post-filter { justify-content: center; }
/* The hero already pays --space-6 below the pills, so the stream opens closer
   than a full --space-section. padding-top alone leaves the shorthand's bottom
   value intact, same as the archive. */
.container--changelog.section { padding-top: var(--space-6); }

/* -- the stream -- */

/* Entries are divided by a hairline rather than boxed: a release is a document,
   not a card, and cards here would fight the rail's sticky behaviour. */
.release + .release {
	margin-top: var(--space-8);
	padding-top: var(--space-8);
	border-top: 1px solid var(--line);
}

@media (min-width: 821px) {
	.release {
		display: grid;
		grid-template-columns: var(--changelog-rail) minmax(0, 1fr);
		gap: var(--space-7);
		align-items: start;
	}
	/* The rail holds its position while its own entry scrolls past, so you can
	   always see which release you are reading. Clears the 57px sticky header. */
	.release__meta {
		position: sticky;
		top: calc(var(--space-7) + var(--space-6));
	}
}

/* -- the meta rail -- */

/* Metadata, not chrome: the product and version are the only things saying which
   plugin an entry belongs to, so they are real text and never aria-hidden. */
.release__meta {
	display: flex;
	flex-wrap: wrap;
	align-items: baseline;
	gap: var(--space-2) var(--space-3);
}
@media (min-width: 821px) {
	.release__meta { flex-direction: column; gap: var(--space-2); }
}

/* a product name is a human label — sans, and a link to the product page */
.release__product {
	display: inline-flex;
	align-items: center;
	gap: var(--space-2);
	font-weight: 600;
	font-size: var(--text-small);
	color: var(--ink);
	text-decoration: none;
}
/* Subtle on purpose: the mark identifies the product at a glance, it does not
   announce it. Slightly dimmed at rest so the name stays the thing you read,
   full strength when you go to click. Decorative, so alt is empty. */
.release__icon {
	width: 18px;
	height: 18px;
	object-fit: contain;
	flex: none;
	opacity: 0.75;
	transition: opacity 0.15s var(--ease-out);
}
.release__product:hover .release__icon { opacity: 1; }
.release__product:hover { color: var(--accent, var(--blue)); }

/* a version IS real data, so mono. The pill wears the product's accent at a
   wash, which is why .release carries the inline --accent. */
.release__version {
	display: inline-block;
	font-family: var(--font-mono);
	font-size: var(--text-mono);
	line-height: 1.4;
	color: var(--accent, var(--ink));
	background: color-mix(in srgb, var(--accent, var(--blue)) 10%, transparent);
	border-radius: 999px;
	padding: 0.15em 0.6em;
}

.release__date {
	font-size: var(--text-small);
	color: var(--ink-soft);
}

/* -- the entry body -- */

.release__body { max-width: var(--measure-article); }
.release__title {
	margin: 0;
	font-size: var(--text-article-h3);
	line-height: 1.25;
	letter-spacing: -0.01em;
	text-wrap: balance;
}
.release__title a { color: inherit; text-decoration: none; }
.release__title a:hover { color: var(--accent, var(--blue)); }
.release__body > * + * { margin-top: var(--space-4); }

/* Section labels (New, Fixed, Improved) are real h3s so a parser and a screen
   reader both see the grouping. They only LOOK like small labels — the same
   trick section 2 plays on an article h6. The vocabulary is open: whatever the
   release note is grouped by is what shows. */
.release__body h3 {
	margin-top: var(--space-6);
	font-family: var(--font-sans);
	font-size: var(--text-label);
	font-weight: 600;
	line-height: 1.4;
	text-transform: uppercase;
	letter-spacing: 0.08em;
	color: var(--ink-soft);
}
.release__body h3 + * { margin-top: var(--space-3); }
.release__body ul { padding-left: 1.15em; }
.release__body li + li { margin-top: var(--space-3); }
.release__body li strong { color: var(--ink); }
.release__body :is(figure, .wp-block-image) { margin-inline: 0; }
.release__body :is(figure, .wp-block-image) img {
	display: block;
	width: 100%;
	height: auto;
	border: 1px solid var(--line);
	border-radius: var(--r-card);
}

@media (max-width: 820px) {
	.release__body { margin-top: var(--space-4); }
}

/* -- older releases -- */

/* A real link, upgraded by site.js into a load-more that fires on scroll. It
   stays a link so the no-JS path and the crawler path both work. */
.release-more {
	margin-top: var(--space-8);
	padding-top: var(--space-7);
	border-top: 1px solid var(--line);
	text-align: center;
}
.release-more__link {
	display: inline-block;
	padding: var(--space-3) var(--space-5);
	border: 1px solid var(--line);
	border-radius: 999px;
	font-size: var(--text-small);
	color: var(--ink-soft);
	text-decoration: none;
	transition: color 0.15s var(--ease-out), border-color 0.15s var(--ease-out);
}
.release-more__link:hover { color: var(--ink); border-color: var(--line-strong); text-decoration: none; }
.release-more[data-loading] .release-more__link { color: var(--ink-soft); pointer-events: none; }
.release-more[data-loading] .release-more__link::after { content: '…'; }

/* The anchor beside a title: there when you want to copy a link to one release,
   invisible until then. Always reachable by keyboard. */
.release__anchor {
	margin-left: var(--space-2);
	color: var(--ink-soft);
	text-decoration: none;
	opacity: 0;
	transition: opacity 0.15s var(--ease-out);
}
.release__title:hover .release__anchor,
.release__anchor:focus-visible { opacity: 1; }
@media (hover: none) { .release__anchor { display: none; } }

/* The same meta line on a release's own page. Entries have no entrance from
   the changelog, so everyone here arrived directly or from an outside link —
   the product and version have to be on the page, not only in the stream. */
.release__meta--single {
	justify-content: flex-start;
	margin-bottom: var(--space-4);
}
.release__back {
	margin: var(--space-5) 0 0;
	font-size: var(--text-small);
}
.release__back a { color: var(--ink-soft); text-decoration: none; }
.release__back a:hover { color: var(--ink); }

/* Free / Pro on the two products that ship both. Quiet by design: the tier is a
   qualifier on the version, not a sales badge, so it sits at label size beside
   it rather than competing with the product name. Pro wears the product accent
   at a wash; free stays a plain outline. */
.release__tier {
	display: inline-block;
	font-size: var(--text-label);
	line-height: 1.4;
	letter-spacing: 0.02em;
	padding: 0.15em 0.5em;
	border-radius: 999px;
	border: 1px solid var(--line);
	color: var(--ink-soft);
}
.release__tier--pro {
	border-color: transparent;
	color: var(--accent, var(--ink));
	background: color-mix(in srgb, var(--accent, var(--blue)) 12%, transparent);
	font-weight: 600;
}
