/* ==========================================================================
   Income Outsource — Single Post
   Reading experience: header, featured image, long-form typography,
   in-content images, author bio, related posts, comments, progress bar, TOC.
   All values from variables.css — nothing hardcoded here.
   ========================================================================== */

.single-post-main {
  display: block;
}

/* Astra puts single posts in its own "Narrow Container" layout by default
   (body class `ast-narrow-container`), which wraps everything in a *second*,
   Astra-owned wrapper — `.site-content > .ast-container` — capped at 750px
   via its dynamic Customizer CSS. That wrapper sits ABOVE our own <main>/
   .container elements, so widening our .container (below) was previously a
   no-op: it was still nested inside Astra's narrower 750px box regardless
   of what max-width we gave it. Same "conflict lives in the rendered
   inline <style>, not the compiled CSS" pattern as the .ast-plain-container
   background conflict in base.css — check CLAUDE.md before re-deriving this.
   Override it to the same width our own widened .container uses below, or
   the two just fight each other. */
.ast-narrow-container .site-content > .ast-container {
  max-width: var(--container-max-wide) !important;
}

/* The sitewide .container caps out at 1200px (--container-max), which read
   as a narrow, over-centered column once the post gained a sidebar — a
   2-column layout needs more room than a single reading column does.
   Widened only here (single.css only loads on is_single()), not globally:
   other pages keep the narrower, more editorial 1200px measure on purpose. */
.single-post-main .container {
  max-width: var(--container-max-wide);
}

/* ---------------------------------------------------------------------
   Reading progress bar
   --------------------------------------------------------------------- */
.reading-progress {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 3px;
  z-index: calc(var(--z-sticky-header) + 1);
  background: transparent;
  pointer-events: none;
}

.reading-progress__bar {
  display: block;
  height: 100%;
  width: 0%;
  background: var(--color-accent);
  transition: width var(--duration-fast) var(--ease-out);
}

/* ---------------------------------------------------------------------
   Post header
   --------------------------------------------------------------------- */
.single-post__header {
  /* Top padding trimmed a step (space-8 → space-6) — the gap under the
     sticky header read as too much across the site; bottom kept as-is. */
  padding-block: var(--space-6);
}

/* A separate nested div from .container in post-header.php on purpose:
   combining a narrower max-width directly on the same element as
   .container's margin-inline:auto causes a second, unwanted centering pass
   (box shrinks below 1200px, then auto-centers in the space centering
   already gave it) — pushed the title inward instead of flush left. Same
   fix applied to .author-bio__inner and .single-post__comments below, and
   to pages.css's .page-hero__inner/.about-founder__inner (plan 09).
   No max-width here (unlike those) — the title/meta intentionally span the
   full wide container above the grid, like a magazine hero, rather than
   being capped to reading measure; only the article body below is capped
   (.single-post__main), since that's the part that's actually read. */
.single-post__header-inner {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--space-4);
}

.single-post__chip {
  cursor: pointer;
}

.single-post__title {
  margin: 0;
  font-size: var(--fs-h1);
}

/* ---------------------------------------------------------------------
   Featured image — nested inside .single-post__main (single.php), same as
   the article body below it, so it renders at the same width as the post
   content (capped at --content-measure there) instead of stretching to
   the full page width above the grid. Also puts it in the same row as the
   sidebar, so "Recent Posts" starts alongside the image, not below it.
   --------------------------------------------------------------------- */
.single-post__media {
  margin-bottom: var(--space-8);
}

.single-post__media img {
  width: 100%;
  aspect-ratio: 16 / 9;
  object-fit: cover;
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-md);
}

/* ---------------------------------------------------------------------
   Sidebar toggle — hides/shows the Recent Posts sidebar (single.js).
   Icon-only, matching the site's other icon-only chrome controls exactly
   (.theme-toggle in header.css: same 40x40 box, transparent default,
   surface-3 + accent on hover/focus, radius-sm) rather than the .btn
   component built for labeled CTAs — that's what "match the site" means
   for a control like this. Accessible name comes from aria-label (kept in
   sync by single.js) since there's no visible text; the tooltip below
   reads that same aria-label via attr(), so there's one source of truth
   instead of a second hardcoded string. Right-aligned above the grid so
   it stays visible (and clickable to bring the sidebar back) regardless
   of state. Only shown at the same width the sidebar becomes optional at
   (≥1024px) — see the mobile override further down.
   --------------------------------------------------------------------- */
.single-post__toolbar {
  display: none;
}

.sidebar-toggle {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 40px;
  height: 40px;
  padding: 0;
  color: var(--color-primary);
  background: transparent;
  border: 1px solid transparent;
  border-radius: var(--radius-sm);
  transition:
    color var(--duration-fast) var(--ease-standard),
    background-color var(--duration-fast) var(--ease-standard),
    border-color var(--duration-fast) var(--ease-standard);
}

/* Astra's button:hover rule paints its own accent-blue background (see
   base.css's .btn--ghost comment for the full explanation) — redeclaring
   background here, not just color/border, is what actually neutralizes it. */
.sidebar-toggle:hover,
.sidebar-toggle:focus-visible,
.sidebar-toggle:focus,
.sidebar-toggle:active {
  color: var(--color-accent);
  background: var(--color-surface-3);
  border-color: var(--color-border-strong);
}

.sidebar-toggle__icon {
  width: 18px;
  height: 18px;
}

/* Custom tooltip (not the native title="" tooltip) — ledger-ink surface,
   same pairing as ::selection/back-to-top rather than a generic OS-styled
   box. content: attr(aria-label) so the tooltip text and the accessible
   name are always the same string, updated in exactly one place by JS. */
.sidebar-toggle::after {
  content: attr(aria-label);
  position: absolute;
  top: calc(100% + var(--space-2));
  right: 0;
  padding: var(--space-2) var(--space-3);
  font-family: var(--font-body);
  font-size: var(--fs-caption);
  font-weight: var(--fw-medium);
  color: var(--color-on-ink);
  background: var(--color-ink);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-md);
  white-space: nowrap;
  opacity: 0;
  transform: translateY(-4px);
  pointer-events: none;
  transition:
    opacity var(--duration-fast) var(--ease-standard),
    transform var(--duration-fast) var(--ease-standard);
  z-index: var(--z-dropdown);
}

.sidebar-toggle:hover::after,
.sidebar-toggle:focus-visible::after {
  opacity: 1;
  transform: translateY(0);
}

/* ---------------------------------------------------------------------
   Body layout — 8/4-column split (article + author bio in the main
   column, a "Recent Posts" widget in the sidebar) at desktop widths,
   single column with the sidebar always shown (no toggle) on smaller
   screens. .single-post__grid carries .container itself, so
   .single-post__main and .single-post__sidebar are plain flex children —
   no nested .container needed (see author-bio.php's comment for why
   nesting one would double up the horizontal padding and misalign it
   against the article text).

   Flexbox rather than CSS Grid on purpose: animating a *width* is
   reliably, smoothly interpolable in every browser, but animating
   grid-template-columns is not consistently supported — a first pass
   using grid-template-columns just snapped instantly for the collapse/
   expand instead of transitioning, even though the sidebar's own opacity
   fade worked. Giving the sidebar a fixed pixel width and transitioning
   *that* (plus opacity) is what actually animates everywhere.
   --------------------------------------------------------------------- */
.single-post__grid {
  display: flex;
  flex-direction: column;
  gap: var(--space-8);
}

/* No max-width here in the default (sidebar-visible) state on purpose —
   capping this at --content-measure (as a first pass did) left a wide
   dead zone between the article text and the sidebar, since flex-grow
   fills the actual remaining space next to the sidebar's fixed width
   (~900px at the 1440px container) while content-measure is only ~750px.
   That empty gap read as "too much space next to the sidebar," and it
   wasn't the flex `gap` property — it was this unused leftover space.
   Letting the column fill its real available width removes the dead
   zone; the resulting ~900px line length is intentionally a bit wider
   than the sitewide --content-measure, matching this page's wider
   "magazine + sidebar" layout. */
.single-post__main {
  min-width: 0; /* stop wide content (tables, code blocks) blowing out the flex item */
}

.single-post__sidebar {
  display: flex;
  flex-direction: column;
  gap: var(--space-6);
}

@media (min-width: 1024px) {
  /* Toggle only makes sense once there's a sidebar layout to collapse —
     on narrower screens the sidebar already always shows underneath the
     article, so hiding it isn't offered there at all. */
  .single-post__toolbar {
    display: flex;
    justify-content: flex-end;
    margin-bottom: var(--space-5);
  }

  .single-post__grid {
    flex-direction: row;
    /* Narrower column gap than the space-8 used for the stacked-mobile
       row gap above — that vertical gap reads fine between two full-width
       stacked blocks, but the same value felt like a wide gutter once it
       became a horizontal gap next to a narrow sidebar. */
    gap: var(--space-6);
    align-items: flex-start;
  }

  .single-post__main {
    flex: 1 1 auto;
  }

  .single-post__sidebar {
    flex: 0 0 434px;
    width: 434px;
    position: sticky;
    top: calc(var(--header-height) + var(--space-6));
    opacity: 1;
    overflow: hidden;
    transition:
      flex-basis var(--duration-slow) var(--ease-standard),
      width var(--duration-slow) var(--ease-standard),
      opacity var(--duration-slow) var(--ease-standard),
      transform var(--duration-slow) var(--ease-standard);
  }

  /* Sidebar hidden (single.js toggles this class on .single-post__grid,
     then sets the [hidden] attribute once the fade+shrink below
     finishes). .single-post__main isn't flex-grow'd to fill the entire
     freed width — it's capped at roughly the same ~906px it had
     alongside the sidebar and centered via margin-inline: auto (the
     flexbox equivalent of the same trick used elsewhere in this theme),
     per the "similar width, just centered" brief rather than a plain
     full-bleed expand. */
  .single-post__grid.is-sidebar-hidden .single-post__main {
    flex: 0 1 906px;
    margin-inline: auto;
  }

  .single-post__grid.is-sidebar-hidden .single-post__sidebar {
    flex-basis: 0;
    width: 0;
    opacity: 0;
    transform: translateX(16px);
    pointer-events: none;
  }

  /* Only once the fade+shrink above has actually finished does single.js
     set the [hidden] attribute, which is what actually removes the
     sidebar from the flex row (rather than leaving a collapsed-but-
     still-participating item and its gap). Needs an explicit author
     rule: the plain `.single-post__sidebar { display: flex }` rule above
     would otherwise beat the [hidden] attribute's default UA style,
     since author rules always win over user-agent ones regardless of
     specificity. */
  .single-post__sidebar[hidden] {
    display: none;
  }
}

/* ---------------------------------------------------------------------
   Table of contents — only populated (and shown) by single.js when a
   post has 3+ h2/h3 headings. Empty/absent otherwise, no layout cost.
   --------------------------------------------------------------------- */
.post-toc {
  display: none;
  margin-bottom: var(--space-7);
  padding: var(--space-5);
  background: var(--color-card-bg);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
}

.post-toc.has-toc {
  display: block;
}

.post-toc__title {
  margin: 0 0 var(--space-3);
  font-family: var(--font-mono);
  font-size: var(--fs-caption);
  font-weight: var(--fw-medium);
  letter-spacing: var(--ls-caps);
  text-transform: uppercase;
  color: var(--color-text-muted);
}

.post-toc__list {
  margin: 0;
  padding: 0;
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}

.post-toc__list a {
  font-size: var(--fs-small);
  color: var(--color-text);
}

.post-toc__list a:hover {
  /* accent-hover, not accent — plain --color-accent text on this card's
     background falls just under WCAG AA's 4.5:1 for normal text
     (measured ~3.65:1); accent-hover clears it (~5.18:1) in both themes. */
  color: var(--color-accent-hover);
}

.post-toc__sub {
  margin-top: var(--space-2);
  margin-left: var(--space-5);
  padding: 0;
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}

/* ---------------------------------------------------------------------
   Long-form typography — every element the block editor can output.
   --------------------------------------------------------------------- */
.entry-content {
  font-size: var(--fs-body);
  line-height: var(--lh-body);
  color: var(--color-text);
}

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

/*
 * Astra's parent stylesheet ships its own `.entry-content h2, h2 { color:
 * #808285; ... }` rule. That selector's specificity (class + element) beats
 * our plain `h2` rule in base.css, so headings inside post content were
 * silently rendering Astra's hardcoded gray in both themes — just legible
 * enough on the light paper background to go unnoticed, unreadable against
 * the dark background. Re-declaring color at the same `.entry-content h2`
 * specificity here (loaded after Astra's stylesheet) wins the cascade.
 */
.entry-content h2 {
  font-size: var(--fs-h2);
  margin-top: var(--space-9);
  color: var(--color-primary);
}

.entry-content h3 {
  font-size: var(--fs-h3);
  margin-top: var(--space-8);
  color: var(--color-primary);
}

.entry-content h4 {
  font-size: var(--fs-h4);
  margin-top: var(--space-7);
  color: var(--color-primary);
}

.entry-content h2,
.entry-content h3,
.entry-content h4 {
  scroll-margin-top: calc(var(--header-height) + var(--space-4));
}

.entry-content p {
  margin: 0;
}

.entry-content ul,
.entry-content ol {
  padding-left: var(--space-6);
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}

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

.entry-content a {
  color: var(--color-accent-hover);
  text-decoration: underline;
  text-decoration-color: var(--color-accent-soft);
  text-underline-offset: 3px;
  transition:
    color var(--duration-fast) var(--ease-standard),
    text-decoration-color var(--duration-fast) var(--ease-standard);
}

.entry-content a:hover,
.entry-content a:focus-visible {
  /* accent-hover, not accent — see .post-toc__list a:hover's comment above;
     same AA contrast shortfall on the paper background here. */
  color: var(--color-accent-hover);
  text-decoration-color: currentColor;
}

.entry-content strong {
  font-weight: var(--fw-semibold);
  color: var(--color-primary);
}

.entry-content em {
  font-style: italic;
}

/* Blockquotes — the editorial pull-quote treatment */
.entry-content blockquote {
  margin: 0;
  padding: var(--space-5) var(--space-6);
  border-left: 3px solid var(--color-accent);
  background: var(--color-card-bg);
  border-radius: 0 var(--radius-md) var(--radius-md) 0;
  font-family: var(--font-heading);
  font-size: var(--fs-lead);
  font-style: italic;
  color: var(--color-primary);
}

.entry-content blockquote p:last-child {
  margin-bottom: 0;
}

.entry-content blockquote cite {
  display: block;
  margin-top: var(--space-3);
  font-family: var(--font-mono);
  font-size: var(--fs-caption);
  font-style: normal;
  letter-spacing: var(--ls-caps);
  color: var(--color-text-muted);
}

/* Code */
.entry-content code {
  font-family: var(--font-mono);
  font-size: 0.9em;
  padding: 0.15em 0.4em;
  background: var(--color-surface-2);
  border-radius: var(--radius-sm);
}

.entry-content pre {
  margin: 0;
  padding: var(--space-5);
  overflow-x: auto;
  background: var(--color-ink);
  border-radius: var(--radius-md);
}

.entry-content pre code {
  padding: 0;
  background: none;
  color: var(--color-on-ink);
  font-size: var(--fs-small);
  line-height: var(--lh-body);
}

/* Tables */
.entry-content table {
  width: 100%;
  border-collapse: collapse;
  font-size: var(--fs-small);
}

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

.entry-content th {
  font-family: var(--font-mono);
  font-size: var(--fs-caption);
  letter-spacing: var(--ls-caps);
  text-transform: uppercase;
  color: var(--color-text-muted);
  background: var(--color-surface-2);
}

/* Horizontal rule — a ledger-style section break */
.entry-content hr {
  border: none;
  height: 1px;
  background: var(--color-border);
  margin: var(--space-8) 0;
}

/* ---------------------------------------------------------------------
   In-content images — figure/figcaption, works with 0, 1, 2, or 3 images.
   --------------------------------------------------------------------- */
.entry-content figure {
  margin: 0;
}

.entry-content img {
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-sm);
}

.entry-content figcaption {
  margin-top: var(--space-2);
  font-family: var(--font-mono);
  font-size: var(--fs-caption);
  color: var(--color-text-muted);
  text-align: center;
}

/* WP alignment classes escape the constrained measure intentionally */
.entry-content .alignwide {
  width: 100%;
  max-width: var(--content-align-wide);
  margin-left: 50%;
  transform: translateX(-50%);
}

.entry-content .alignfull {
  width: 100vw;
  max-width: var(--content-align-full);
  margin-left: 50%;
  transform: translateX(-50%);
}

.entry-content .alignleft {
  float: left;
  margin: var(--space-2) var(--space-5) var(--space-2) 0;
  max-width: 45%;
}

.entry-content .alignright {
  float: right;
  margin: var(--space-2) 0 var(--space-2) var(--space-5);
  max-width: 45%;
}

.entry-content .aligncenter {
  margin-left: auto;
  margin-right: auto;
}

/* ---------------------------------------------------------------------
   Tags — a quieter sibling of .post-chip (homepage.css): an outlined,
   muted-text pill instead of the chip's solid filled badge. Sits directly
   on the page background (--color-bg), not a card surface —
   --color-text-muted on --color-surface-2/--color-card-bg measures
   ~4.45:1, just under AA's 4.5:1 for this text size; on the plain page
   background it clears ~4.9:1 in light and ~6.7:1 in dark.
   --------------------------------------------------------------------- */
.post-tags {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-3);
  max-width: var(--content-measure);
  margin-top: var(--space-6);
}

.post-tags__label {
  font-family: var(--font-mono);
  font-size: var(--fs-caption);
  letter-spacing: var(--ls-caps);
  text-transform: uppercase;
  color: var(--color-text-muted);
}

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

.post-tags__tag {
  display: inline-block;
  padding: var(--space-1) var(--space-3);
  font-family: var(--font-mono);
  font-size: var(--fs-caption);
  letter-spacing: var(--ls-caps);
  color: var(--color-text-muted);
  background: transparent;
  border: 1px solid var(--color-border-strong);
  border-radius: var(--radius-pill);
  transition:
    color var(--duration-fast) var(--ease-standard),
    border-color var(--duration-fast) var(--ease-standard),
    background-color var(--duration-fast) var(--ease-standard);
}

.post-tags__tag:hover,
.post-tags__tag:focus-visible {
  color: var(--color-accent-hover);
  background: var(--color-surface-3);
  border-color: var(--color-accent);
}

/* ---------------------------------------------------------------------
   Social share — icon-button convention matching .theme-toggle/
   .sidebar-toggle exactly (40x40, transparent default, surface-3 +
   accent on hover/focus, radius-sm), so it reads as the same family of
   chrome controls rather than a bespoke widget. All four interaction
   states (:hover/:focus-visible/:focus/:active) redeclare color,
   background, AND border-color — see CLAUDE.md's Astra button-conflict
   notes for why omitting any one of those leaves Astra's accent-blue
   showing on a real click/tap.
   --------------------------------------------------------------------- */
.post-share {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-3);
  max-width: var(--content-measure);
  margin-top: var(--space-6);
  padding-top: var(--space-6);
  border-top: 1px solid var(--color-border);
}

.post-share__label {
  font-family: var(--font-mono);
  font-size: var(--fs-caption);
  letter-spacing: var(--ls-caps);
  text-transform: uppercase;
  color: var(--color-text-muted);
}

.post-share__buttons {
  display: flex;
  align-items: center;
  gap: var(--space-2);
}

.post-share__button {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 40px;
  height: 40px;
  padding: 0;
  color: var(--color-primary);
  background: transparent;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
  cursor: pointer;
  transition:
    color var(--duration-fast) var(--ease-standard),
    background-color var(--duration-fast) var(--ease-standard),
    border-color var(--duration-fast) var(--ease-standard);
}

.post-share__button:hover,
.post-share__button:focus-visible,
.post-share__button:focus,
.post-share__button:active {
  color: var(--color-accent);
  background: var(--color-surface-3);
  border-color: var(--color-border-strong);
}

.post-share__icon {
  width: 18px;
  height: 18px;
}

/* Custom tooltip (not native title="") on every share button — same
   ledger-ink pattern as .sidebar-toggle::after, using ::before so it can
   coexist with .post-share__copy's own ::after "Copied!" confirmation
   below. content: attr(aria-label) keeps the tooltip text and accessible
   name as a single source of truth (single.js already updates
   aria-label to "Link copied" on the copy button, so the tooltip follows
   automatically). Suppressed while .is-copied so it doesn't stack with the
   "Copied!" confirmation in the same spot. */
.post-share__button::before {
  content: attr(aria-label);
  position: absolute;
  top: calc(100% + var(--space-2));
  left: 50%;
  padding: var(--space-2) var(--space-3);
  font-family: var(--font-body);
  font-size: var(--fs-caption);
  font-weight: var(--fw-medium);
  color: var(--color-on-ink);
  background: var(--color-ink);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-md);
  white-space: nowrap;
  opacity: 0;
  transform: translateX(-50%) translateY(-4px);
  pointer-events: none;
  transition:
    opacity var(--duration-fast) var(--ease-standard),
    transform var(--duration-fast) var(--ease-standard);
  z-index: var(--z-dropdown);
}

.post-share__button:hover::before,
.post-share__button:focus-visible::before {
  opacity: 1;
  transform: translateX(-50%) translateY(0);
}

/* Instant, not transitioned — otherwise this crossfades at the same rate
   and over the same spot as .post-share__copy::after's "Copied!" fade-in
   below, and the two tooltips visibly overlap mid-transition. */
.post-share__copy.is-copied::before {
  opacity: 0;
  transition: none;
}

/* Link/checkmark crossfade on copy — driven by .is-copied (single.js), not
   the [hidden] attribute: Chrome's UA stylesheet only wires [hidden] up to
   display:none for HTML-namespaced elements, not SVG ones, so an
   svg[hidden] silently keeps rendering (both icons stacked/overlapping in
   testing) despite the attribute being present in the DOM. Toggling
   display via a class sidesteps that entirely. */
.post-share__icon--check {
  display: none;
  color: var(--color-success);
}

.post-share__copy.is-copied .post-share__icon--link {
  display: none;
}

.post-share__copy.is-copied .post-share__icon--check {
  display: block;
}

/* "Copied!" confirmation — same custom-tooltip pattern as
   .sidebar-toggle::after (single.css above), but triggered by a JS-added
   class instead of hover/focus, since the confirmation needs to persist
   briefly after the pointer/focus has already moved on. */
.post-share__copy::after {
  content: "Copied!";
  position: absolute;
  top: calc(100% + var(--space-2));
  left: 50%;
  padding: var(--space-2) var(--space-3);
  font-family: var(--font-body);
  font-size: var(--fs-caption);
  font-weight: var(--fw-medium);
  color: var(--color-on-ink);
  background: var(--color-ink);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-md);
  white-space: nowrap;
  opacity: 0;
  transform: translateX(-50%) translateY(-4px);
  pointer-events: none;
  transition:
    opacity var(--duration-fast) var(--ease-standard),
    transform var(--duration-fast) var(--ease-standard);
  z-index: var(--z-dropdown);
}

.post-share__copy.is-copied::after {
  opacity: 1;
  transform: translateX(-50%) translateY(0);
}

/* ---------------------------------------------------------------------
   Author bio
   --------------------------------------------------------------------- */
.author-bio {
  margin-top: var(--space-10);
  padding-block: var(--space-7);
  border-top: 1px solid var(--color-border);
}

.author-bio__inner {
  max-width: var(--content-measure);
  display: flex;
  align-items: flex-start;
  gap: var(--space-4);
}

.author-bio__avatar img {
  border-radius: 50%;
  box-shadow: var(--shadow-sm);
}

.author-bio__body {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--space-1);
}

.author-bio__label {
  font-family: var(--font-mono);
  font-size: var(--fs-caption);
  letter-spacing: var(--ls-caps);
  text-transform: uppercase;
  color: var(--color-text-muted);
}

.author-bio__name {
  font-family: var(--font-heading);
  font-size: var(--fs-h5);
  font-weight: var(--fw-semibold);
  color: var(--color-primary);
  text-transform: uppercase;
}

.author-bio__text {
  margin: var(--space-1) 0 0;
  font-size: var(--fs-small);
  color: var(--color-text-muted);
}

/* ---------------------------------------------------------------------
   Recent Posts — sidebar widget, .single-post__sidebar (see above).
   Compact list treatment (small thumbnail + title + date) rather than the
   full post-card grid, since it only has ~4/12 columns to work with.
   --------------------------------------------------------------------- */
.recent-posts {
  padding: var(--space-5);
  background: var(--color-card-bg);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
}

.recent-posts__title {
  margin: 0 0 var(--space-4);
  font-family: var(--font-mono);
  font-size: var(--fs-caption);
  font-weight: var(--fw-medium);
  letter-spacing: var(--ls-caps);
  text-transform: uppercase;
  color: var(--color-text-muted);
}

.recent-posts__list {
  margin: 0;
  padding: 0;
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
}

.recent-posts__link {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  color: inherit;
}

.recent-posts__thumb {
  flex: 0 0 auto;
  display: block;
  width: 64px;
  height: 64px;
  overflow: hidden;
  border-radius: var(--radius-md);
  background: var(--color-surface-2);
}

.recent-posts__thumb img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform var(--duration-slow) var(--ease-out);
}

.recent-posts__link:hover .recent-posts__thumb img,
.recent-posts__link:focus-visible .recent-posts__thumb img {
  transform: scale(1.05);
}

.recent-posts__thumb-placeholder {
  display: block;
  width: 100%;
  height: 100%;
  background-color: var(--color-ink);
  background-image: repeating-linear-gradient(
    -45deg,
    var(--color-ink-light) 0,
    var(--color-ink-light) 1px,
    transparent 1px,
    transparent 8px
  );
}

.recent-posts__body {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  min-width: 0;
}

.recent-posts__item-title {
  font-family: var(--font-heading);
  font-size: var(--fs-small);
  font-weight: var(--fw-semibold);
  color: var(--color-primary);
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  transition: color var(--duration-fast) var(--ease-standard);
}

.recent-posts__link:hover .recent-posts__item-title,
.recent-posts__link:focus-visible .recent-posts__item-title {
  color: var(--color-accent-hover);
}

.recent-posts__item-meta {
  font-family: var(--font-mono);
  font-size: var(--fs-caption);
  color: var(--color-text-muted);
}

/* Deliberately not accent-colored — in this compact, thumbnail-heavy list
   a colored category label competed with the bold title right above it
   and the accent color already used elsewhere on the page (nav underline,
   category chip). Same muted tone as the date keeps it one calm meta
   line instead of two competing elements. */
.recent-posts__item-category {
  color: var(--color-text-muted);
}

/* ---------------------------------------------------------------------
   Previous/Next post navigation — sits between the article (author bio)
   and related posts: finish article -> immediate next post -> broader
   related reading -> comments. Single column on mobile; two columns from
   700px. When only one side exists (first/latest post, or no adjacent
   post in the same category), post-navigation.php adds
   .adjacent-posts__grid--single and the lone card spans the full row
   instead of leaving a lopsided empty box next to it.

   Named .adjacent-posts, not .post-navigation, on purpose — Astra ships
   its own built-in prev/next feature under the exact class name
   `.post-navigation` (`.nav-previous`/`.nav-next`/`.nav-links` children),
   styled via dynamic Customizer CSS printed inline in <head> (same
   "invisible in the compiled CSS file" pattern as the other Customizer
   conflicts in CLAUDE.md). A first pass using `.post-navigation` collided
   with it directly: Astra's `.single .post-navigation a {max-width:80%;
   width:100%}` (class+class+element, (0,2,1)) silently beat our
   `.post-navigation__grid--single .post-navigation__card {max-width:560px}`
   ((0,2,0)) on pure specificity regardless of load order, so the "lone
   card" edge case rendered at ~80% of the full grid width instead of the
   intended compact size — and Astra's rule set also touches `a:hover`
   color, `svg` positioning, and more, any of which could resurface here
   later even if this one symptom were patched around. Renaming the
   component sidesteps the whole collision instead of fighting it
   property-by-property. **Don't reuse `.post-navigation` as a class name
   anywhere in this theme.**
   --------------------------------------------------------------------- */
.adjacent-posts {
  padding-block: var(--space-8);
  border-top: 1px solid var(--color-border);
}

.adjacent-posts__grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-4);
}

/* Compact "chapter navigator" card — a small thumbnail beside the title
   (same scale/pattern as .recent-posts__link above), not a full-bleed
   hero image. A first pass used a 16:9 image filling the whole card, with
   only a thin direction label and the title squeezed at the top/bottom
   edges — the photo ended up doing all the visual work while the actual
   navigation text read as an afterthought stuck to the image's border.
   This version leads with typography (as the rest of the site does) and
   uses the thumbnail as a supporting cue, not the headline. */
.adjacent-posts__card {
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
  padding: var(--space-5) var(--space-6);
  color: inherit;
  background: var(--color-card-bg);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  transition:
    border-color var(--duration-fast) var(--ease-standard),
    box-shadow var(--duration-fast) var(--ease-standard);
}

.adjacent-posts__card:hover {
  border-color: var(--color-border-strong);
  box-shadow: var(--shadow-sm);
}

/* Separate from :hover on purpose — base.css's global `:focus-visible`
   rule gives every interactive element an accent-colored focus ring
   (--shadow-focus); reusing the hover rule's plain elevation shadow here
   would have silently downgraded that ring to a much fainter effect for
   keyboard users specifically (a real regression caught in testing).
   Matches the same border-color + --shadow-focus pairing already used by
   .search-form-custom__input:focus-visible in base.css. */
.adjacent-posts__card:focus-visible {
  border-color: var(--color-accent);
  box-shadow: var(--shadow-focus);
}

.adjacent-posts__card--next {
  align-items: flex-end;
}

.adjacent-posts__direction {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  font-family: var(--font-mono);
  font-size: var(--fs-caption);
  font-weight: var(--fw-medium);
  letter-spacing: var(--ls-caps);
  text-transform: uppercase;
  color: var(--color-text-muted);
}

.adjacent-posts__direction svg {
  width: 14px;
  height: 14px;
  color: var(--color-accent);
}

.adjacent-posts__row {
  display: flex;
  align-items: center;
  gap: var(--space-4);
  width: 100%;
}

.adjacent-posts__card--next .adjacent-posts__row {
  flex-direction: row-reverse;
}

.adjacent-posts__thumb {
  flex: 0 0 72px;
  display: block;
  width: 72px;
  height: 72px;
  overflow: hidden;
  border-radius: var(--radius-md);
  background: var(--color-surface-2);
}

.adjacent-posts__thumb img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform var(--duration-slow) var(--ease-out);
}

.adjacent-posts__card:hover .adjacent-posts__thumb img,
.adjacent-posts__card:focus-visible .adjacent-posts__thumb img {
  transform: scale(1.05);
}

.adjacent-posts__title {
  flex: 1 1 auto;
  min-width: 0;
  font-family: var(--font-heading);
  font-size: var(--fs-h5);
  font-weight: var(--fw-semibold);
  color: var(--color-primary);
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  transition: color var(--duration-fast) var(--ease-standard);
}

.adjacent-posts__card--next .adjacent-posts__title {
  text-align: right;
}

.adjacent-posts__card:hover .adjacent-posts__title,
.adjacent-posts__card:focus-visible .adjacent-posts__title {
  color: var(--color-accent-hover);
}

@media (min-width: 700px) {
  .adjacent-posts__grid {
    grid-template-columns: 1fr 1fr;
  }

  .adjacent-posts__grid--single .adjacent-posts__card {
    grid-column: 1 / -1;
    max-width: 560px;
  }

  .adjacent-posts__grid--single .adjacent-posts__card--next {
    margin-left: auto;
  }
}

/* ---------------------------------------------------------------------
   Related posts — reuses .post-grid / .post-card from homepage.css
   --------------------------------------------------------------------- */
.related-posts {
  padding-block: var(--space-9);
  background: var(--color-bg-alt);
}

.related-posts__title {
  margin: 0 0 var(--space-6);
  font-size: var(--fs-h3);
}

/* ---------------------------------------------------------------------
   Comments
   --------------------------------------------------------------------- */
/* Widened from --content-measure (68ch, matched to .entry-content's
   reading-width) to --content-align-wide (900px, roughly the article
   column's own rendered width) — the comments block sits outside
   .single-post__grid, in the full wide .container (single-post-main's
   1440px override above), not inside the narrower article column itself,
   so the tighter reading-width cap left a large empty gap on the right
   instead of reading as intentionally narrow. margin-inline: auto centers
   it in that wide container rather than sitting flush left. */
.single-post__comments {
  max-width: var(--content-align-wide);
  margin-inline: auto;
  padding-block: var(--space-9);
}

.single-post__comments .comment-reply-title,
.single-post__comments .comments-title {
  font-size: var(--fs-h3);
  margin-bottom: var(--space-5);
}

.single-post__comments .ast-comment-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-5);
}

.single-post__comments .ast-comment-list .children {
  list-style: none;
  margin: var(--space-5) 0 0;
  padding-left: var(--space-6);
  border-left: 1px solid var(--color-border);
}

.single-post__comments .ast-comment {
  padding: var(--space-5);
  background: var(--color-card-bg);
  border: none;
  border-radius: var(--radius-md);
}

.single-post__comments .ast-comment-meta {
  font-family: var(--font-mono);
  font-size: var(--fs-caption);
  color: var(--color-text-muted);
  margin-bottom: var(--space-2);
}

.single-post__comments .ast-comment-cite-wrap,
.single-post__comments .ast-comment-cite-wrap a {
  color: var(--color-primary);
}

.single-post__comments .ast-comment-content {
  color: var(--color-text);
}

/* Astra styles this as a small pill (radius + padding) with no background
   at rest, but paints its own blue background + white text/border on
   hover/focus/active — same "redeclare every property, not just the one
   you noticed" Astra button conflict as .btn--ghost/.sidebar-toggle
   elsewhere in this project (see CLAUDE.md). Our original rule only set
   `color`, so Astra's hover background/border still won for those
   specific properties regardless of our color override. */
.single-post__comments .comment-reply-link {
  color: var(--color-accent-hover);
}

.single-post__comments .comment-reply-link:hover,
.single-post__comments .comment-reply-link:focus,
.single-post__comments .comment-reply-link:active {
  color: var(--color-accent-hover);
  background: var(--color-accent-soft);
  border-color: transparent;
}

.single-post__comments .comment-form input[type="text"],
.single-post__comments .comment-form input[type="email"],
.single-post__comments .comment-form input[type="url"],
.single-post__comments .comment-form textarea {
  width: 100%;
  padding: var(--space-3) var(--space-4);
  font-family: var(--font-body);
  font-size: var(--fs-body);
  color: var(--color-text);
  background: var(--color-surface-1);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
}

.single-post__comments .comment-form input:focus-visible,
.single-post__comments .comment-form textarea:focus-visible {
  outline: none;
  box-shadow: var(--shadow-focus);
}

/*
 * Neither WP core nor Astra style these — they render with whatever
 * ambient body color wins the cascade, which reads fine on the light
 * paper background but goes low-contrast on dark. Set explicitly rather
 * than rely on inheritance.
 */
.single-post__comments .comment-notes,
.single-post__comments .comment-form-cookies-consent,
.single-post__comments .comment-form-cookies-consent label {
  color: var(--color-text);
}

/*
 * Astra's own stylesheet targets the submit button via `#comments .submit`
 * — an ID selector, which beats any chain of classes we write here no
 * matter how specific. WP core's comment_form() also renders it with
 * `id="submit"`, so we match Astra's specificity by anchoring on that same
 * ID rather than fighting it with more classes.
 *
 * Uses the accent color (not --color-ink) on purpose: --color-ink is a
 * deliberately non-flipping dark surface, but the comments card itself sits
 * on --color-card-bg, which in dark mode is nearly the same tone as
 * --color-ink — the button all but disappeared into its own container.
 * --color-accent flips brightness per theme and reliably contrasts against
 * any surface in either theme, same pairing already used for ::selection
 * in base.css.
 */
.single-post__comments .form-submit #submit[type="submit"] {
  padding: var(--space-3) var(--space-6);
  font-family: var(--font-heading);
  font-weight: var(--fw-semibold);
  color: var(--color-text-inverse);
  background: var(--color-accent);
  border: none;
  border-radius: var(--radius-md);
  transition: background-color var(--duration-fast) var(--ease-standard);
}

.single-post__comments .form-submit #submit[type="submit"]:hover,
.single-post__comments .form-submit #submit[type="submit"]:focus {
  color: var(--color-text-inverse);
  background: var(--color-accent-hover);
  border-color: var(--color-accent-hover);
}

/* ---------------------------------------------------------------------
   Breakpoints
   --------------------------------------------------------------------- */
@media (min-width: 700px) {
  .author-bio__avatar img {
    width: 72px;
    height: 72px;
  }
}

@media (min-width: 1024px) {
  .single-post__title {
    font-size: var(--fs-h1);
  }
}
