/*
 * Single blog post — one of 149 URLs.
 *
 * Anything the index also renders is in p-blog.css, which this file loads
 * after: the meta line, the post card, the pagination. What is left is the
 * article, and it is mostly p-shared's .prose-layout, .toc, .prose, .cap,
 * .note and .faq. This file is the four things those cannot fix on their own —
 * a compact article header, a jumped-to heading landing under the sticky
 * topbar, a contents rail longer than the viewport, and a .cap table losing
 * its frame once it sits inside .prose.
 *
 * The second half of the file is a layer the prototype never needed. p-post.html
 * hand-writes its article, so `.prose` only ever has to style clean semantic
 * markup. The 149 real posts were migrated byte-identically from live and their
 * markup cannot be edited, so what actually arrives is core block output
 * (`.wp-block-table`, `.wp-block-image`, `.wp-block-embed`), Spectra/UAGB blocks
 * on 88-90 posts each, and Elementor widget trees on fourteen. Everything from
 * "migrated block markup" down styles what is there rather than what we would
 * have written, and is scoped under `.post-body` so it cannot reach the legal
 * and docs pages that share `.prose`.
 *
 * Two cascade facts decide most of the selectors below, and both are worth
 * knowing before editing:
 *
 *   - Core's per-block CSS is inlined AFTER the theme's stylesheets. At equal
 *     specificity core wins, which is why `.prose td` lost to
 *     `.wp-block-table td { border: 1px solid; padding: .5em }` and every
 *     migrated table rendered as a default block table. Rules here carry one
 *     class more than core's so they win on specificity, not on order.
 *   - UAGB's generated per-page CSS is enqueued BEFORE the theme, so an equal
 *     tie goes to us. Its per-block rules run to six classes though, so the
 *     handful that have to be beaten are matched class-for-class.
 *
 * Nothing below is specific to the Shopify/Etsy post that instantiates the
 * template. Swapping in another of the 149 is a head-and-body change with no
 * CSS change.
 *
 * Load order is shared.css -> fx.css -> p-shared.css -> p-blog.css ->
 * p-post.css. Same constraint as the rest of the lab: no literal colour,
 * radius or font size.
 */

/* ---------- article header ----------
   A post is read, not browsed, so the header states what this is and how long
   it will take, then gets out of the way. The .subhero the pair and platform
   templates use would put a 460px glow above a 3,000-word article. */

.post-head { padding-bottom: var(--s-6); }
.post-head__inner { margin-top: var(--s-5); }
.post-head h1 { max-width: 20em; }

/* Post titles are long — the shortest of the 149 is 42 characters and the
   median is 68. On a phone the trail's own last item wraps to three lines of
   the same words the h1 is about to say at four times the size. The link back
   up is the useful part of a two-level trail; the leaf is not. */
@media (max-width: 720px) {
  .post-head .crumb li:last-child { display: none; }
}
.post-head .pmeta {
  margin-top: var(--s-5);
  padding-bottom: var(--s-5);
  border-bottom: 1px solid var(--line);
  font-size: var(--t-sm);
}

.post-body { padding-bottom: var(--s-8); }

/* ---------- sticky-header clearance ----------
   .topbar is sticky and about 77px tall, so p-shared's --s-8 scroll margin
   parks a jumped-to heading just underneath it. The contents rail is the main
   way through a post this long, which makes a jump link that hides its own
   target the most likely bug on this template. */

.prose h2,
.prose h3 { scroll-margin-top: calc(var(--s-8) + var(--s-5)); }

/* ---------- the reading column ----------
   The rail is what makes this page a spread rather than a column of text with
   half the window empty beside it, so the two tracks are sized together and
   centred as a pair. Without `justify-content` the 1fr track takes all the
   slack and `.prose`'s own max-width leaves it as 200px of blank page on the
   right — which is what the template looked like before the rail existed.

   `minmax(0, ...)` rather than a bare max-width: a grid item defaults to
   min-width auto, so a wide migrated table would otherwise push the track past
   its own maximum and drag the rail off-screen.

   A post with two sections gets no rail (see QS_TOC_MIN_SECTIONS), and the
   column then centres on its own. */

.post-body .prose-layout {
  grid-template-columns: 220px minmax(0, 42em);
  justify-content: center;
}
.post-body .prose-layout--solo { grid-template-columns: minmax(0, 42em); }

@media (max-width: 900px) {
  .post-body .prose-layout,
  .post-body .prose-layout--solo { grid-template-columns: minmax(0, 1fr); }
}

/* ---------- the contents rail ----------
   Two corrections to the shared rail, both only above 900px where it is
   sticky. p-shared parks it 80px down and the sticky topbar measures 77px, so
   the two read as touching. And a post with a dozen sections gives a rail
   taller than a laptop viewport, which then cannot be scrolled to its own last
   item unless it scrolls itself. */

@media (min-width: 901px) {
  .toc {
    top: calc(var(--s-8) + var(--s-6));
    max-height: calc(100vh - var(--s-9) - var(--s-5));
    overflow-y: auto;
  }

}

/* The rail is a <details> so that on a phone, where p-shared drops it out of
   the sticky column and into a card above the article, a fifteen-item list can
   be folded away instead of costing a screen and a half before the first
   sentence. It ships open: a contents list nobody can see is not a contents
   list, and the reader closes it once.

   Native disclosure rather than script — the rail has to work on the first
   frame, and this is the one piece of the article that is pure navigation.

   The marker is drawn at every width, not just on the phone card. A summary is
   a disclosure control wherever it renders — a click, or Enter on it while
   focused, folds the list away — so a desktop rail with the native triangle
   suppressed and no replacement is a 220px column that can be emptied with
   nothing left in it to click. Only the size of the marker changes with the
   breakpoint. */

.toc > summary {
  display: flex; align-items: center; justify-content: space-between; gap: var(--s-3);
  cursor: pointer; list-style: none;
}
.toc > summary::-webkit-details-marker { display: none; }
.toc > summary::after {
  content: '+';
  font-family: var(--mono); font-size: var(--t-lg); line-height: 1;
  color: var(--muted);
}
.toc[open] > summary::after { content: '\2212'; }
.toc > summary:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

@media (max-width: 900px) {
  .toc > summary { margin-bottom: 0; }
  .toc > summary::after { font-size: var(--t-xl); }
  .toc[open] > summary { margin-bottom: var(--s-3); }
}

@media (prefers-reduced-motion: no-preference) {
  .toc a { transition: color .2s var(--fx-ease), border-left-color .2s var(--fx-ease); }
}

/* ---------- capability tables inside the reading column ----------
   p-shared defines `.prose table` after `.cap` at the same specificity, so a
   .cap dropped into the prose column loses its rounded frame to
   `border-collapse: collapse` and gains a bottom rule under every cell. These
   put it back rather than changing either component, because .cap is shared
   with the pair and platform templates and .prose with the legal pages.

   The phone block repeats the fix inside .cap's own stacking breakpoint, where
   the cells become blocks and take a different padding. */

.prose .cap { border-collapse: separate; border-spacing: 0; }
.prose .cap th,
.prose .cap td { padding: var(--s-3) var(--s-4); border-bottom: 0; }
.prose .cap thead th { border-bottom: 1px solid var(--line); }
.prose .cap tbody tr + tr th,
.prose .cap tbody tr + tr td { border-top: 1px solid var(--line); }

@media (max-width: 720px) {
  .prose .cap tbody th { padding: var(--s-3) var(--s-4) var(--s-1); }
  .prose .cap td { padding: var(--s-1) var(--s-4); }
  .prose .cap tbody tr + tr th,
  .prose .cap tbody tr + tr td { border-top: 0; }
}

/* ---------- prose additions ----------
   Body copy leans on bold for the term being defined at the start of a list
   item, which has to read as a step down from --ink rather than as shouting. */

.prose b,
.prose strong { color: var(--ink); font-weight: 600; }

.prose .cap,
.prose .note,
.prose .card,
.prose .faq { margin-top: var(--s-5); }

/* ======================================================================
   migrated block markup
   ======================================================================
   Everything below is scoped under .post-body, which only single.php emits.
   The legal and docs templates load p-prose.css against the same `.prose`
   class and must not inherit any of it. */

/* ---------- measure and rhythm ----------
   1.75 rather than the site's 1.6. The article is the one surface where a
   reader is in continuous text for several minutes, and at 16px across a 672px
   measure 1.6 is the "cramped" the redesign was asked to fix.

   The gaps match a reading rhythm rather than a uniform stack: 16px between
   paragraphs, 48px before a section, 32px before a subsection, and 24px around
   anything that is an object rather than a sentence. */

.post-body .prose { line-height: var(--lh-prose); }
.post-body .prose > :first-child { margin-top: 0; }

/* p-shared sets `.prose ul, .prose ol { margin: 0 }` after `.prose > * + *`
   and at higher specificity, so every list on every post opened flush against
   the paragraph introducing it. */
.post-body .prose > ul,
.post-body .prose > ol { margin-top: var(--s-4); }

.post-body .prose li + li { margin-top: var(--s-1); }
.post-body .prose li > ul,
.post-body .prose li > ol { margin-top: var(--s-1); }

/* 18px left only 2px over body text, and body copy here sets its key term in
   bold at 16px — so a subsection heading and an emphasised phrase read as the
   same rung. 20px puts the step back. */
.post-body .prose h3 { font-size: var(--t-xl); margin-top: var(--s-6); }

/* Direct children only. A Spectra container holds its own blocks, and giving
   those the article's between-object gap stacked the container's padding on top
   of the child's margin — 73px of dead space above the first line of a callout
   that is 24px tall. Inside a container the container owns the spacing.

   Pictures get 32px and everything else 24px. A framed image is a break in the
   reading, and at 24px its border sits close enough to the last line to read as
   attached to that sentence rather than to the section. A table or a panel is
   still text, so it stays on the tighter gap. */
.post-body .prose > figure.wp-block-image,
.post-body .prose > .wp-block-embed { margin-top: var(--s-6); }
/* Matched below as well as above, so the picture sits in its own space rather
   than being pushed up against the copy that follows it. The successors are
   named rather than using `+ *`: a heading after a picture keeps its own
   larger gap. */
.post-body .prose > figure.wp-block-image + :is(p, ul, ol),
.post-body .prose > .wp-block-embed + :is(p, ul, ol) { margin-top: var(--s-6); }

.post-body .prose > figure,
.post-body .prose > .wp-block-uagb-container,
.post-body .prose > .wp-block-uagb-faq,
.post-body .prose > .wp-block-uagb-call-to-action { margin-top: var(--s-5); }

/* ---------- links in dense copy ----------
   A solid full-strength underline on every third phrase turns a paragraph into
   stripes, and these posts link heavily. Dropping the underline to a tint of
   the link colour and pushing it clear of the descenders keeps the affordance
   without the texture; hover restores it at full strength. */

.post-body .prose a {
  text-decoration-color: color-mix(in srgb, var(--accent) 45%, transparent);
  text-decoration-thickness: 1px;
  text-underline-offset: .18em;
  display: inline-block;
  min-height: 24px;
  padding-block: 4px;
}
.post-body .prose a:hover { text-decoration-color: currentColor; }
.post-body .toc a {
  display: inline-block;
  min-height: 24px;
  padding-block: 4px;
}

/* ---------- images ----------
   A screenshot on a white page needs an edge, or it bleeds into the column and
   its own white chrome reads as part of the article. */

.post-body .prose figure.wp-block-image { margin-inline: 0; }
.post-body .prose figure.wp-block-image img {
  display: block;
  border: 1px solid var(--line);
  border-radius: var(--r-md);
}
.post-body .prose figcaption {
  margin-top: var(--s-3);
  font-size: var(--t-sm);
  line-height: var(--lh-body);
  color: var(--muted);
  text-align: left;
}

/* ---------- embeds ----------
   57 posts carry a YouTube embed. aspect-ratio rather than the padding-top box
   core uses: it needs no wrapper, and it survives the iframe arriving with
   width and height attributes from oEmbed. */

.post-body .prose .wp-block-embed iframe {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 16 / 9;
  border: 0;
  border-radius: var(--r-md);
}
.post-body .prose .wp-block-embed figcaption { margin-top: var(--s-3); }

/* ---------- tables ----------
   Core inlines `.wp-block-table td, th { border: 1px solid; padding: .5em }`
   after the theme, and that unqualified `1px solid` resolves to currentColor —
   so every migrated table drew a grid of body-grey hairlines around half-em
   cells. These carry one class more than core's so they win on specificity.

   The frame is `.cap`'s: one outer hairline, a tinted header row, rules
   between rows and none between columns. Column rules on a table this narrow
   read as a spreadsheet. */

.post-body .prose figure.wp-block-table { overflow-x: auto; }
.post-body .prose .wp-block-table table {
  width: 100%;
  border-collapse: separate;
  border-spacing: 0;
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  overflow: hidden;
  font-size: var(--t-sm);
  line-height: var(--lh-body);
}
.post-body .prose .wp-block-table th,
.post-body .prose .wp-block-table td {
  padding: var(--s-3) var(--s-4);
  border: 0;
  border-top: 1px solid var(--line);
  text-align: left;
  vertical-align: top;
}
.post-body .prose .wp-block-table thead th {
  background: var(--surface);
  color: var(--ink);
  font-weight: 700;
}
.post-body .prose .wp-block-table thead:first-child tr:first-child > *,
.post-body .prose .wp-block-table tbody:first-child tr:first-child > * { border-top: 0; }
/* Migrated tables often lead with a bold first column instead of a thead. */
.post-body .prose .wp-block-table tbody th { color: var(--ink); font-weight: 600; }

/* Only 11 of the 150 migrated tables have a <thead>. The other 139 open with a
   header row the editor typed as an ordinary row and emboldened by hand, so the
   tint above had nothing to attach to and the comparison tables — which is most
   of them — read as a grid of equal cells with no column labels.
   Surveyed across all 149 posts: every table's first row is a header row.
   `:not(:last-child)` keeps this off the 20 one-row tables, which are callouts
   rather than data and are styled as panels below. */
.post-body .prose .wp-block-table table:not(:has(thead)) tbody > tr:first-child:not(:last-child) > td {
  background: var(--surface);
  color: var(--ink);
  font-weight: 700;
}

/* ---------- the one-cell table, which is a callout ----------
   Twenty posts open with "Key Takeaways" built as a table containing a single
   cell: one <td> holding six lines separated by <br>, each led by a literal "→"
   typed into the copy. It is the first thing a reader sees on those pages and
   it was rendering as a bare hairline rectangle with half-em padding — the
   exact screenshot this redesign started from.

   The markup cannot change, so the shape has to be read out of it instead:
   a table whose whole body is one cell is a panel, not data. It gets the same
   treatment as the Spectra callout that opens the other 88 posts, so a reader
   moving between posts sees one component rather than two.

   <br> is given a block box so the six lines separate into six entries. That is
   the only lever CSS has over runs of text divided by line breaks, and it is
   what turns the arrows from stray glyphs into list markers.

   The panel goes on the figure rather than the table because some of them
   carry `style="border-style:none;border-width:0px"` on the table
   itself, from a border attribute set in the editor. Inline styles cannot be
   beaten from a stylesheet, so the accent rule painted on those four resolved
   to 0px wide and the callout came out as an untinted rectangle. The figure
   carries at most a font-size. */

.post-body .prose figure.wp-block-table:has(tbody > tr:only-child > td:only-child) {
  overflow-x: visible;
  padding: var(--s-5);
  border: 1px solid var(--line);
  border-left: 3px solid var(--accent);
  border-radius: var(--r-md);
  background: var(--surface);
}
.post-body .prose figure.wp-block-table:has(tbody > tr:only-child > td:only-child) table {
  border: 0;
  border-radius: 0;
  background: none;
  font-size: var(--t-base);
  line-height: var(--lh-prose);
}
.post-body .prose figure.wp-block-table:has(tbody > tr:only-child > td:only-child) td {
  padding: 0;
  border: 0;
}
.post-body .prose figure.wp-block-table:has(tbody > tr:only-child > td:only-child) td br {
  display: block;
  content: '';
  margin-top: var(--s-3);
}

@media (max-width: 720px) {
  .post-body .prose figure.wp-block-table:has(tbody > tr:only-child > td:only-child) {
    padding: var(--s-4);
  }
}

/* ---------- quotes ----------
   Some posts carry a wp-block-quote holding one empty paragraph, left behind
   where a pull quote used to be. It renders as a gap the reader reads as a
   layout fault. Collapsing it is presentation; the markup stays as migrated. */

.post-body .prose blockquote:has(> p:only-child:empty) { display: none; }

.post-body .prose blockquote {
  margin: 0;
  padding: var(--s-1) 0 var(--s-1) var(--s-5);
  border-left: 3px solid var(--accent);
  color: var(--muted);
  font-style: italic;
}
.post-body .prose blockquote p + p { margin-top: var(--s-3); }

/* ---------- Spectra blockquote ----------
   Five posts. Its own stylesheet sets the attribution to #888888, which is
   3.54:1 on white, and paints the share link in Twitter blue with white text at
   2.83:1 — the only two contrast failures left on the blog once the --muted
   token was fixed, and both from colours that are not in the palette. */

.post-body .prose .uagb-blockquote { font-style: normal; }
.post-body .prose .uagb-blockquote__author,
.post-body .prose .uagb-blockquote cite { color: var(--muted); font-style: normal; }
.post-body .prose a.uagb-blockquote__tweet-button {
  background-color: var(--accent);
  color: var(--white);
  border-radius: var(--r-sm);
  text-decoration: none;
}
.post-body .prose a.uagb-blockquote__tweet-button:hover { background-color: var(--accent-hover); }

/* ---------- Spectra container ----------
   88 posts. The generated CSS paints it with a repeating wave PNG fetched from
   the production origin and then washes 65% of an off-palette grey over the top
   with a ::before, which is two requests and two colours to arrive at a tinted
   panel the design system already has. This is that panel. */

.post-body .prose .wp-block-uagb-container {
  padding: var(--s-5);
  border: 1px solid var(--line);
  border-left: 3px solid var(--accent);
  border-radius: var(--r-md);
  background-color: var(--surface);
  background-image: none;
}
.post-body .prose .wp-block-uagb-container::before { display: none; }
.post-body .prose .wp-block-uagb-container .wp-block-uagb-container {
  padding: 0;
  border: 0;
  border-radius: 0;
  background: none;
}

/* ---------- Spectra call to action ----------
   89 posts, and the worst-rendering block on the site: the button inherited the
   article's link underline, and the flex row let the copy take the whole width
   so the label wrapped inside a box with no padding to wrap in. Restated as the
   design system's own button.

   `.wp-block-button` is on the block wrapper as well as the link, so the
   selectors have to name both to clear the generated rules, which run to six
   classes. */

.post-body .prose .wp-block-uagb-call-to-action {
  display: flex;
  flex-direction: row;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-4);
}
/* Generated CSS pads the CTA by 25px. Where it sits inside a container — which
   is every one of the 89 — that padding is inside the container's own, so the
   panel gets 49px of inset on one side of the text and 24px on the other. */
.post-body .prose .uagb-container-inner-blocks-wrap > .wp-block-uagb-call-to-action { padding: 0; }
.post-body .prose .uagb-cta__wrap { flex: 1 1 20em; min-width: 0; }
/* Generated CSS centres the copy below 976px. Body text in this column is left
   aligned everywhere else, and the description runs to six lines on a phone. */
.post-body .prose .uagb-cta__wrap,
.post-body .prose .uagb-cta__title,
.post-body .prose .uagb-cta__desc { text-align: left; }
.post-body .prose .uagb-cta__title {
  font-size: var(--t-lg);
  font-weight: 700;
  color: var(--ink);
  line-height: var(--lh-snug);
}
.post-body .prose .uagb-cta__desc {
  margin-top: var(--s-2);
  font-size: var(--t-sm);
  line-height: var(--lh-body);
  color: var(--body);
}
.post-body .prose .uagb-cta__buttons { flex: none; }
.post-body .prose .wp-block-uagb-call-to-action.wp-block-button .uagb-cta__buttons a.uagb-cta__button-link-wrapper.wp-block-button__link {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: auto;
  min-height: 44px;
  padding: 0 var(--s-5);
  border-radius: var(--r-sm);
  background-color: var(--accent);
  color: var(--white);
  font-size: var(--t-sm);
  font-weight: 600;
  line-height: var(--lh-snug);
  white-space: nowrap;
  text-decoration: none;
}
.post-body .prose .wp-block-uagb-call-to-action.wp-block-button .uagb-cta__buttons a.uagb-cta__button-link-wrapper.wp-block-button__link:hover {
  background-color: var(--accent-hover);
}

@media (max-width: 560px) {
  /* Generated CSS gives the copy a max-width below 976px, sized for the centred
     column it also switches on there. Left aligned in a 275px panel that leaves
     the text ragged down one third of the width. */
  .post-body .prose .uagb-cta__wrap { flex: 1 1 100%; max-width: none; }

  /* A nowrap label plus a 20em minimum would push the row wider than the
     column, so on a phone the button takes the full width instead. */
  .post-body .prose .uagb-cta__buttons { width: 100%; }
  .post-body .prose .wp-block-uagb-call-to-action.wp-block-button .uagb-cta__buttons a.uagb-cta__button-link-wrapper.wp-block-button__link {
    width: 100%;
    white-space: normal;
  }
}

/* ---------- Spectra FAQ ----------
   90 posts, and the block that closes most of them. The generated CSS gives it
   a 2px radius and a #D2D2D2 border, neither of which is in the palette, and
   10px of padding on a 44px-tall control. Retuned to the .faq component the
   pricing and feature pages already use. */

.post-body .prose .uagb-faq-child__outer-wrap { margin-bottom: var(--s-2); }
.post-body .prose .uagb-faq-item {
  border-color: var(--line);
  border-radius: var(--r-sm);
  background: var(--white);
  transition: border-color .2s var(--fx-ease);
}
.post-body .prose .uagb-faq-item:hover { border-color: var(--line-strong); }
.post-body .prose .uagb-faq-questions-button {
  padding: var(--s-3) var(--s-4);
  color: var(--ink);
  font-weight: 600;
  line-height: var(--lh-snug);
}
.post-body .prose .uagb-faq-content {
  padding: 0 var(--s-4) var(--s-4);
  font-size: var(--t-sm);
  line-height: var(--lh-body);
}
.post-body .prose .uagb-faq-content span { margin: 0; }
.post-body .prose .uagb-faq-item .uagb-icon svg { fill: var(--muted); }

/* ---------- Elementor bodies ----------
   Fourteen posts are Elementor documents rather than blocks. The bridge blanks
   every container custom property so that built pages can carry `.wrap` and
   `.section` themselves — but these posts predate that convention and carry no
   design classes at all, so they inherited a zero-padding, 100%-width container
   and ran edge to edge across the viewport with no spacing between widgets.

   Wrapping the document in `.prose` gives back the measure; the rest is the
   rhythm the widget wrappers no longer supply. */

.prose--elementor .elementor-widget + .elementor-widget { margin-top: var(--s-4); }
.prose--elementor .elementor-widget + .elementor-widget-heading:has(h2) { margin-top: var(--s-7); }
.prose--elementor .elementor-widget + .elementor-widget-heading:has(h3) { margin-top: var(--s-6); }
.prose--elementor .elementor-widget-image { margin-top: var(--s-5); }
.prose--elementor .elementor-widget-image img {
  border: 1px solid var(--line);
  border-radius: var(--r-md);
}
