/* ============================================================
   KOBA — blog.css
   The article body, the article page around it, the post card, and the
   blog's list pages (/blog, topics, authors).
   ============================================================
   Two audiences in one file, and the split matters:

   1. .post-body — the article exactly as renderDoc() emits it
      (public/js/blog-render.js, SPEC §3.5). The admin editor reproduces the
      same markup in its canvas and loads THIS file with variables.css (and
      fonts.css) only: no style.css, and admin.css's global resets — `*`
      margins, `thead th`, `tbody td` — in play. So every .post-body rule is
      self-contained: its own reset, list markers, links, table cells and
      spacing, with enough specificity to beat those globals, and tokens from
      variables.css only. Block typography uses the child combinator so it
      never leaks into a product card that sits inside the article.

   2. Page furniture — the article page, cards, list pages, the author box.
      These may lean on style.css (.container, .breadcrumb, .chip-row,
      .sec-*, .btn, .product-card).

   Loaded by the Worker's blog pages, by the product page when it lists
   articles, and by the homepage's «З блогу» slot (a body <link>, present only
   when that section is).

   The house rules (CLAUDE.md):
   - No literal colours: tokens from variables.css. Local custom properties
     (--pb-*, --post-*) carry only tokens and lengths.
   - rem for type, px for borders, %/fr for layout; breakpoints 600/768/1024,
     media queries at the bottom of each component.
   - Gold is an accent: never body text. The one gold text colour is --c-link,
     always underlined. Small marks (numbers, a topic label) use --c-accent-ink.
   - Only transform and opacity move. Scroll-driven reveals sit behind THREE
     gates and attach to components (cards), never to page sections; nothing
     rests at opacity 0 without a running animation.
   ============================================================ */


/* ══ 1 · THE ARTICLE BODY (.post-body) ═══════════════════════════
   A grid with named lines, so a block chooses its width by name and nothing
   needs 100vw (which overflows wherever a scrollbar takes width):
     content — the reading measure: ~68–72 characters of Ukrainian text a line
     wide    — up to 60rem, for figures, galleries, products, video
     full    — edge to edge of the grid
   Standalone (the editor canvas, a browser without subgrid) the body draws
   these lines itself. On the article page it takes them from .post through
   subgrid, so the body lines up with the title above it and can make room
   for the table of contents.

   Why 56ch and not 68ch: `ch` is the width of «0», and Manrope's zero is wide
   (0.61em) next to an average Cyrillic letter (0.48em). Measured on the stand
   at 18px: 68ch came out at 86 characters a line; 56ch gives ~70, the measure
   the spec actually asks for. Change --pb-measure and --post-measure together. */

.post-body {
  --pb-gutter: var(--sp-4);
  --pb-measure: 56ch;
  --pb-wide: 60rem;
  --pb-flow: 1.15em;
  --pb-block: 2rem;
  display: grid;
  grid-template-columns:
    [full-start] minmax(var(--pb-gutter), 1fr)
    [wide-start] minmax(0, calc((var(--pb-wide) - var(--pb-measure)) / 2))
    [content-start] min(var(--pb-measure), 100% - 2 * var(--pb-gutter))
    [content-end] minmax(0, calc((var(--pb-wide) - var(--pb-measure)) / 2))
    [wide-end] minmax(var(--pb-gutter), 1fr)
    [full-end];
  align-content: start;
  counter-reset: pb-h2;
  font-family: var(--ff-body);
  font-size: 1.0625rem;
  font-weight: 400;
  font-style: normal;
  line-height: 1.75;
  letter-spacing: 0;
  color: var(--c-text-soft);
  text-align: start;
  overflow-wrap: break-word;
}

/* Own reset — zero specificity, so a component that sets its own margin (a
   product card's .card-sub) still wins over it. */
.post-body :where(p, h2, h3, ul, ol, li, blockquote, figure, figcaption, table, details, summary, cite, hr) {
  margin: 0;
  padding: 0;
}

/* Every block sits in the measure unless it asks for more. Grid items do not
   collapse margins, so the rhythm is top margins only. */
.post-body > * {
  grid-column: content;
  min-width: 0;
  margin: 0;
}
.post-body > * + * { margin-top: var(--pb-flow); }

/* Objects get more air than a paragraph — above and below. :where() keeps
   these at the specificity of the rule above, so the heading rules below
   (declared later, one element more specific) still decide around headings. */
.post-body > :where(figure, aside, blockquote, .pb-table, .pb-products, .pb-faq, .pb-cta, .pb-hr),
.post-body > :where(figure, aside, blockquote, .pb-table, .pb-products, .pb-faq, .pb-cta, .pb-hr) + * {
  margin-top: var(--pb-block);
}

.post-body > :is(.pb-figure--wide, .pb-gallery, .pb-products, .pb-yt) { grid-column: wide; }
.post-body > .pb-figure--full { grid-column: full; }

/* ── Text ───────────────────────────────────────────────── */

.post-body strong { font-weight: 700; color: var(--c-text); }
.post-body em { font-style: italic; }

/* Links in running text: the one gold text colour, always underlined —
   colour alone is not a link (WCAG 1.4.1), and on this page gold is also the
   action fill. Scoped to text containers, so a product card's own links and
   the CTA button keep their styles. */
.post-body :is(p, li, td, th, figcaption, .pb-faq-a) a:not(.pb-cta-link) {
  color: var(--c-link);
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: .2em;
}
.post-body :is(p, li, td, th, figcaption, .pb-faq-a) a:not(.pb-cta-link):hover {
  color: var(--c-link-hover);
  text-decoration-thickness: 2px;
}

/* Section headings carry a mono number — the homepage's «01 · Для чого»
   gesture, and the same numbers the table of contents shows. The number is
   decoration: the `/ ""` alt text keeps it out of the accessible name where
   supported, and the first declaration is the fallback. Empty headings (the
   editor's placeholder state) are not numbered and not counted, because
   renderDoc drops them from the page. */
.post-body > h2 {
  margin-top: 2.75rem;
  font-family: var(--ff-head);
  font-size: clamp(1.375rem, 1.2rem + .8vw, 1.75rem);
  font-weight: 800;
  line-height: 1.2;
  letter-spacing: -.015em;
  color: var(--c-text);
  text-wrap: balance;
}
.post-body > h2:not(.is-empty) { counter-increment: pb-h2; }
.post-body > h2:not(.is-empty)::before {
  content: counter(pb-h2, decimal-leading-zero);
  content: counter(pb-h2, decimal-leading-zero) / "";
  display: block;
  margin-bottom: .6rem;
  font-family: var(--ff-num);
  font-size: .75rem;
  font-weight: 500;
  letter-spacing: .14em;
  line-height: 1;
  color: var(--c-accent-ink);
}
.post-body > h3 {
  margin-top: 2rem;
  font-family: var(--ff-head);
  font-size: 1.1875rem;
  font-weight: 700;
  line-height: 1.3;
  letter-spacing: -.01em;
  color: var(--c-text);
}
.post-body > :where(h2, h3) + * { margin-top: .75rem; }
.post-body > :first-child { margin-top: 0; }

/* ── Lists ──────────────────────────────────────────────── */
/* ::marker rather than a positioned ::before: the editor shows its
   placeholders in ::before, and a bullet drawn there would swallow them. */
.post-body > :is(ul, ol) { padding-left: 1.5em; }
.post-body > ul { list-style: disc outside; }
.post-body > ol { list-style: decimal-leading-zero outside; padding-left: 2.25em; }
.post-body > :is(ul, ol) > li { padding-left: .35em; }
.post-body > :is(ul, ol) > li + li { margin-top: .5em; }
.post-body > :is(ul, ol) > li::marker { color: var(--c-accent-ink); }
.post-body > ol > li::marker {
  font-family: var(--ff-num);
  font-size: .8em;
  font-variant-numeric: tabular-nums;
}

/* ── Quote ──────────────────────────────────────────────── */
.post-body > .pb-quote {
  padding: var(--sp-1) 0 var(--sp-1) var(--sp-6);
  border-left: 2px solid var(--c-accent);
}
.post-body .pb-quote > p {
  font-family: var(--ff-head);
  font-size: 1.25rem;
  font-weight: 600;
  line-height: 1.5;
  letter-spacing: -.005em;
  color: var(--c-text);
}
.post-body .pb-quote > cite {
  display: block;
  margin-top: var(--sp-3);
  font-family: var(--ff-num);
  font-size: .8125rem;
  font-style: normal;
  line-height: 1.4;
  color: var(--c-muted);
}
.post-body .pb-quote > cite::before { content: '— '; }

/* ── Callouts: info · warn · tip ────────────────────────── */
/* The panel is only TINTED (variables.css): body text keeps its colour, and
   the tone is carried by the icon and the edge, never by the words. The text
   column is minmax(0, 1fr) so long words and the editor's controls inside it
   cannot widen the panel. */
.post-body > .pb-callout {
  --pb-tone: var(--c-info);
  --pb-tone-bg: var(--c-info-bg);
  --pb-tone-bdr: var(--c-info-bdr);
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  column-gap: var(--sp-3);
  padding: var(--sp-4) var(--sp-5) var(--sp-4) var(--sp-4);
  background: var(--pb-tone-bg);
  border: 1px solid var(--pb-tone-bdr);
  border-left: 3px solid var(--pb-tone);
  border-radius: var(--r-lg);
  font-size: .9375em;
  line-height: 1.65;
}
.post-body > .pb-callout--warn {
  --pb-tone: var(--c-warning);
  --pb-tone-bg: var(--c-warn-bg);
  --pb-tone-bdr: var(--c-warn-bdr);
}
.post-body > .pb-callout--tip {
  --pb-tone: var(--c-tip-ink);
  --pb-tone-bg: var(--c-tip-bg);
  --pb-tone-bdr: var(--c-tip-bdr);
}
.post-body .pb-callout-icon {
  display: block;
  width: 1.25rem;
  height: 1.25rem;
  margin-top: .2em;
  color: var(--pb-tone);
}
.post-body .pb-callout > div { min-width: 0; }
.post-body .pb-callout p + p { margin-top: .4em; }
.post-body .pb-callout .pb-callout-title {
  font-weight: 700;
  color: var(--c-text);
}

/* ── Figures and galleries ──────────────────────────────── */
.post-body :is(.pb-figure, .pb-gallery) img {
  display: block;
  width: 100%;
  max-width: 100%;
  height: auto;
  background-color: var(--c-plate);
  border: 1px solid var(--c-img-bdr);
  border-radius: var(--r-lg);
}
.post-body figcaption {
  margin-top: var(--sp-3);
  font-size: .875rem;
  line-height: 1.55;
  color: var(--c-muted);
}
/* Edge to edge: no radius, no side borders — and above the table of contents
   when it shares the row, with the page colour behind the caption. */
.post-body > .pb-figure--full {
  position: relative;
  z-index: 1;
  background-color: var(--c-bg);
}
.post-body > .pb-figure--full img {
  border-radius: 0;
  border-left: 0;
  border-right: 0;
}
.post-body > .pb-figure--full > figcaption { padding-inline: var(--pb-gutter); }

.pb-gallery-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--sp-2);
}
/* Uniform tiles: the alt text describes the photo, the crop keeps the grid
   calm. A single image (a gallery still being built in a draft) keeps its
   own proportions. */
.post-body .pb-gallery-grid > img {
  aspect-ratio: 4 / 3;
  object-fit: cover;
}
.pb-gallery--1 .pb-gallery-grid { grid-template-columns: minmax(0, 1fr); }
.post-body .pb-gallery--1 .pb-gallery-grid > img { aspect-ratio: auto; object-fit: fill; }
.pb-gallery--3 .pb-gallery-grid > :first-child,
.pb-gallery--5 .pb-gallery-grid > :first-child { grid-column: 1 / -1; }

/* ── Video facade ───────────────────────────────────────── */
/* A thumbnail and a button; the iframe exists only after a click
   (blog-page.js → lightbox.js), so a post with a video costs one image. */
.post-body .pb-yt-facade {
  position: relative;
  display: block;
  width: 100%;
  aspect-ratio: 16 / 9;
  margin: 0;
  padding: 0;
  overflow: hidden;
  font: inherit;
  color: var(--c-text);
  background-color: var(--c-plate);
  border: 1px solid var(--c-img-bdr);
  border-radius: var(--r-lg);
  cursor: pointer;
  appearance: none;
}
.post-body .pb-yt-facade > img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  opacity: .78;
  transition: opacity var(--t-md), transform var(--t-md);
}
.post-body .pb-yt-facade::after {
  content: '';
  position: absolute;
  inset: 0;
  background-image: var(--grad-img-fade);
  opacity: .55;
  pointer-events: none;
}
.post-body .pb-yt-play {
  position: absolute;
  z-index: 1;
  left: 50%;
  top: 50%;
  width: 4.5rem;
  height: 4.5rem;
  margin: -2.25rem 0 0 -2.25rem;
  color: var(--c-text);
  filter: drop-shadow(var(--sh-text));
  transition: transform var(--t-md);
}
.post-body .pb-yt-facade:hover > img { opacity: 1; transform: scale(1.02); }
.post-body .pb-yt-facade:hover .pb-yt-play { transform: scale(1.08); }

/* ── Products ───────────────────────────────────────────── */
/* The shared product card (style.css) in a grid of its own: the article sets
   the type, the card keeps the catalogue's. */
.post-body > .pb-products {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--sp-3);
  font-size: 1rem;
  line-height: 1.65;
  color: var(--c-text);
}
.post-body > .pb-products--1 { grid-template-columns: minmax(0, 1fr); }
/* A shorter plate than the catalogue's square: in an article the card is a
   reference, not the shop window. `contain` (style.css) keeps the whole jar. */
.post-body .pb-products .card-img-wrap { aspect-ratio: 4 / 3; }
/* One product lies down: a 600px-tall square of packshot would be the
   loudest thing in the article. */
.pb-products--1 .product-card { flex-direction: row; }
/* min-width: 0, or the plate's own size becomes the flex minimum and the
   text column is pushed out of the card. */
.pb-products--1 .card-img-link { flex: 0 0 38%; max-width: 15rem; min-width: 0; }
.post-body .pb-products--1 .card-img-wrap {
  aspect-ratio: auto;
  height: 100%;
  min-height: 8rem;
  border-bottom: 0;
  border-right: 1px solid var(--c-img-bdr);
}
.pb-products--1 .card-body { justify-content: center; }
/* Three or four abreast, a card is ~200px: the price beside a nowrap button
   does not fit, so the footer stacks (the shelf does the same at 768–1023). */
.post-body > :is(.pb-products--3, .pb-products--4) .card-footer {
  flex-direction: column;
  align-items: stretch;
  gap: var(--sp-2);
}
.post-body > :is(.pb-products--3, .pb-products--4) .card-footer .btn { width: 100%; }

/* ── Button ─────────────────────────────────────────────── */
/* Styled by its class, not by a[href]: the editor's copy has no href. */
.post-body .pb-cta-link {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  min-height: 48px;
  padding: .75rem 1.5rem;
  font-family: var(--ff-ui);
  font-size: 1rem;
  font-weight: 700;
  line-height: 1.2;
  color: var(--c-on-cta);
  text-decoration: none;
  background: var(--c-cta);
  border-radius: var(--r);
  transition: transform var(--t);
}
.post-body .pb-cta-link::after {
  content: '→';
  content: '→' / '';
  transition: transform var(--t);
}
.post-body .pb-cta-link:hover { background: var(--c-cta-hover); }
.post-body .pb-cta-link:hover::after { transform: translateX(3px); }
.post-body .pb-cta-link:active { transform: translateY(1px) scale(.985); }

/* ── Divider ────────────────────────────────────────────── */
.post-body > .pb-hr {
  justify-self: center;
  width: min(12rem, 50%);
  height: 1px;
  border: 0;
  background-image: var(--grad-accent-line);
}

/* ── Table ──────────────────────────────────────────────── */
/* The wrapper scrolls sideways; the page never does. Cells have a floor, so
   a five-column table scrolls at 320px instead of folding into a word per
   line. Selectors carry two classes to outrank admin.css's global
   `tbody tr:hover td`. */
.post-body > .pb-table {
  overflow-x: auto;
  overscroll-behavior-x: contain;
  background: var(--c-card);
  border: 1px solid var(--c-border);
  border-radius: var(--r-lg);
}
.post-body .pb-table table {
  width: 100%;
  border-collapse: collapse;
  font-size: .9375rem;
  line-height: 1.5;
  font-variant-numeric: tabular-nums;
}
.post-body .pb-table :is(th, td) {
  min-width: 6.5rem;
  padding: .75rem 1rem;
  text-align: left;
  vertical-align: top;
  white-space: normal;
  text-transform: none;
  letter-spacing: 0;
  font-size: inherit;
  font-weight: 400;
  color: var(--c-text-soft);
  background: transparent;
  border: 0;
  border-bottom: 1px solid var(--c-border-light);
}
.post-body .pb-table th {
  font-size: .8125rem;
  font-weight: 700;
  color: var(--c-text);
  background: var(--c-surface);
  border-bottom-color: var(--c-border);
}
.post-body .pb-table tr:last-child > td { border-bottom: 0; }

/* ── FAQ ────────────────────────────────────────────────── */
/* Plain <details> — visible content, no FAQPage markup (SPEC D11). */
.post-body > .pb-faq { border-top: 1px solid var(--c-border); }
.post-body .pb-faq-item { border-bottom: 1px solid var(--c-border); }
.post-body .pb-faq-item > summary {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--sp-4);
  min-height: 44px;
  padding: var(--sp-4) 0;
  font-family: var(--ff-head);
  font-size: 1.0625rem;
  font-weight: 700;
  line-height: 1.4;
  color: var(--c-text);
  list-style: none;
  cursor: pointer;
}
.post-body .pb-faq-item > summary::-webkit-details-marker { display: none; }
.post-body .pb-faq-item > summary::after {
  content: '+';
  content: '+' / '';
  flex: none;
  font-family: var(--ff-num);
  font-size: 1.25rem;
  font-weight: 500;
  line-height: 1.1;
  color: var(--c-accent-ink);
  transition: transform var(--t-md);
}
.post-body .pb-faq-item[open] > summary::after { transform: rotate(45deg); }
.post-body .pb-faq-a {
  padding: 0 var(--sp-8) var(--sp-5) 0;
  color: var(--c-text-soft);
}

@keyframes pb-rise {
  from { opacity: 0; transform: translateY(12px); }
  to   { opacity: 1; transform: none; }
}
@media (prefers-reduced-motion: no-preference) {
  .post-body .pb-faq-item[open] > .pb-faq-a { animation: pb-rise var(--dur-md) var(--ease-out) both; }
}

@media (min-width: 600px) {
  .pb-gallery--3 .pb-gallery-grid,
  .pb-gallery--6 .pb-gallery-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .pb-gallery--3 .pb-gallery-grid > :first-child { grid-column: auto; }
  .pb-gallery--5 .pb-gallery-grid { grid-template-columns: repeat(6, minmax(0, 1fr)); }
  .pb-gallery--5 .pb-gallery-grid > * { grid-column: span 2; }
  .pb-gallery--5 .pb-gallery-grid > :nth-child(-n + 2) { grid-column: span 3; }
  .post-body > .pb-products { gap: var(--sp-4); }
}
@media (min-width: 768px) {
  .post-body { font-size: 1.125rem; }
  .post-body > h3 { font-size: 1.25rem; }
  .post-body .pb-quote > p { font-size: 1.375rem; }
  .post-body > .pb-callout { padding: var(--sp-5) var(--sp-6) var(--sp-5) var(--sp-5); column-gap: var(--sp-4); }
  .post-body > .pb-products--3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}
@media (min-width: 1024px) {
  .post-body > .pb-products--4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }
  .pb-gallery--4 .pb-gallery-grid { grid-template-columns: repeat(4, minmax(0, 1fr)); }
}
@media (prefers-reduced-motion: reduce) {
  .post-body .pb-yt-facade > img,
  .post-body .pb-yt-play,
  .post-body .pb-cta-link,
  .post-body .pb-cta-link::after,
  .post-body .pb-faq-item > summary::after { transition: none; }
}


/* ══ 2 · THE ARTICLE PAGE (.post) ═══════════════════════════════
   The article is one grid with the body's named lines, so the breadcrumbs,
   the title, the cover and the body share one measure. The first two rows sit
   on a band of the homepage hero's light. */

.post {
  --post-gutter: var(--sp-4);
  --post-measure: 56ch;  /* = --pb-measure, see section 1 */
  --post-wide: 60rem;
  position: relative;
  display: grid;
  grid-template-columns:
    [full-start] minmax(var(--post-gutter), 1fr)
    [wide-start] minmax(0, calc((var(--post-wide) - var(--post-measure)) / 2))
    [content-start] min(var(--post-measure), 100% - 2 * var(--post-gutter))
    [content-end] minmax(0, calc((var(--post-wide) - var(--post-measure)) / 2))
    [wide-end] minmax(var(--post-gutter), 1fr)
    [full-end];
  /* `ch` follows the font size, and the measure is counted in the BODY's
     characters — so the article sets the body's size here. */
  font-size: 1.0625rem;
  padding-bottom: var(--sp-section);
}
.post > * { grid-column: content; min-width: 0; }

/* The band: a grid item of its own behind the first two rows. Separate colour
   and image properties, never the shorthand (pastka 18). */
.post::before {
  content: '';
  grid-column: full;
  grid-row: 1 / 3;
  background-color: var(--c-hero-fallback);
  background-image: var(--grad-hero);
  border-bottom: 1px solid var(--c-border);
}
.post > .breadcrumb { grid-row: 1; }
/* A long title in the trail stays on one line — the h1 right below says it in full. */
.post .breadcrumb-current {
  min-width: 0;
  max-width: 100%;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}
.post > .post-head { grid-row: 2; }

.post-head { padding: var(--sp-2) 0 var(--sp-8); }

/* The topic as a pill: a way back to its siblings. A hidden topic is a label
   (span) with the same shape and no hover. */
.post-topic {
  display: inline-flex;
  align-items: center;
  min-height: 32px;
  padding: 4px 12px;
  font-family: var(--ff-num);
  font-size: .75rem;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--c-accent-ink);
  background: var(--c-card);
  border: 1px solid var(--c-border-strong);
  border-radius: var(--r-pill);
}
a.post-topic:hover { border-color: var(--c-primary); }

.post-title {
  margin-top: var(--sp-4);
  font-family: var(--ff-head);
  font-size: clamp(1.875rem, 1.35rem + 2.4vw, 3.25rem);
  font-weight: 800;
  line-height: 1.08;
  letter-spacing: -.02em;
  color: var(--c-text);
  text-wrap: balance;
}
.post-lead {
  margin-top: var(--sp-4);
  font-size: 1.125rem;
  line-height: 1.55;
  color: var(--c-text-soft);
  text-wrap: pretty;
}

/* Byline: the portrait, the name, and a line of facts under it. */
.post-meta {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  margin-top: var(--sp-6);
  padding-top: var(--sp-5);
  font-size: .875rem;
  line-height: 1.45;
  color: var(--c-muted);
  border-top: 1px solid var(--c-border-light);
}
.post-meta-text { min-width: 0; }
.post-meta-by { font-size: .9375rem; font-weight: 600; color: var(--c-text); }
/* Facts separated by dots that never start a line: every item carries its dot
   in front, the list is pulled left by one dot's width, and the wrapper clips
   it — so the first item of each line has its dot cut off. */
.post-meta-facts { overflow: hidden; }
.post-meta-facts > ul {
  display: flex;
  flex-wrap: wrap;
  margin: 0 0 0 -1.25em;
  padding: 0;
  list-style: none;
}
.post-meta-facts li::before {
  content: '·';
  content: '·' / '';
  display: inline-block;
  width: 1.25em;
  text-align: center;
  color: var(--c-placeholder);
}
.post-meta time { color: var(--c-text-soft); font-variant-numeric: tabular-nums; }
.post-meta-by > a {
  color: var(--c-link);
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: .2em;
}
.post-meta-by > a:hover { color: var(--c-link-hover); text-decoration-thickness: 2px; }

/* Portraits: a photo, a monogram, or the shop's own mark for «Редакція Koba». */
.post-avatar,
.post-author-photo {
  flex: none;
  display: grid;
  place-items: center;
  width: 2.75rem;
  height: 2.75rem;
  overflow: hidden;
  object-fit: cover;
  color: var(--c-logo);
  background-color: var(--c-plate);
  background-image: var(--grad-tile-glow);
  border: 1px solid var(--c-border-strong);
  border-radius: 50%;
}
.post-avatar--mono,
.post-author-photo--mono {
  font-family: var(--ff-head);
  font-size: 1.125rem;
  font-weight: 800;
  color: var(--c-accent-ink);
}

/* The cover is the LCP image: explicit size, no lazy, never animated. Without
   one the article gets a slim plate — the empty frame made deliberate. */
.post-cover {
  grid-column: wide;
  margin: var(--sp-6) 0 0;
}
.post-cover > img {
  display: block;
  width: 100%;
  height: auto;
  background-color: var(--c-plate);
  border: 1px solid var(--c-img-bdr);
  border-radius: var(--r-xl);
}
.post-cover--plate {
  grid-column: content;
  position: relative;
  display: grid;
  place-items: center;
  aspect-ratio: 3 / 1;
  overflow: hidden;
  color: var(--c-art-line);
  background-color: var(--c-plate);
  background-image: var(--grad-tile-glow), var(--grad-plate);
  border: 1px solid var(--c-border);
  border-radius: var(--r-xl);
}
.post-cover--plate::before {
  content: '';
  position: absolute;
  inset: var(--sp-3);
  border: 1px solid var(--c-border);
  border-radius: var(--r-lg);
  pointer-events: none;
}
.post-cover-glyph { width: 2.75rem; height: 2.75rem; }

/* ── Body and table of contents ─────────────────────────── */
.post-main {
  grid-column: full;
  margin-top: var(--sp-10);
}
/* Without subgrid the TOC sits in the measure above the body. */
.post-rail {
  width: min(var(--post-measure), 100% - 2 * var(--post-gutter));
  margin: 0 auto var(--sp-8);
}

.pb-toc {
  padding: var(--sp-4) var(--sp-5);
  background: var(--c-surface);
  border: 1px solid var(--c-border);
  border-radius: var(--r-lg);
}
.pb-toc-title {
  margin: 0 0 var(--sp-2);
  font-family: var(--ff-num);
  font-size: .75rem;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--c-muted);
}
.pb-toc ol {
  margin: 0;
  padding: 0;
  list-style: none;
  counter-reset: pb-toc;
}
.pb-toc li { counter-increment: pb-toc; }
.pb-toc a {
  position: relative;
  display: grid;
  grid-template-columns: 2rem minmax(0, 1fr);
  align-items: baseline;
  min-height: 44px;
  padding: .55rem 0;
  font-size: .9375rem;
  line-height: 1.4;
  color: var(--c-text-soft);
  text-decoration: none;
}
.pb-toc a::before {
  content: counter(pb-toc, decimal-leading-zero);
  content: counter(pb-toc, decimal-leading-zero) / "";
  font-family: var(--ff-num);
  font-size: .75rem;
  color: var(--c-accent-ink);
}
.pb-toc a:hover { color: var(--c-text); }
/* The section being read (blog-page.js). */
.pb-toc a[aria-current="true"] { color: var(--c-text); font-weight: 600; }

/* ── After the article ──────────────────────────────────── */
.post-foot {
  display: grid;
  gap: var(--sp-5);
  margin-top: var(--sp-12);
}
.post-foot-title {
  margin: 0 0 var(--sp-3);
  font-family: var(--ff-num);
  font-size: .75rem;
  font-weight: 500;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--c-muted);
}

.post-sources {
  padding-top: var(--sp-5);
  border-top: 1px solid var(--c-border);
}
.post-sources-list {
  margin: 0;
  padding: 0;
  list-style: none;
  counter-reset: pb-src;
  font-size: .9375rem;
  line-height: 1.5;
}
.post-sources-list > li {
  counter-increment: pb-src;
  display: grid;
  grid-template-columns: 2rem minmax(0, 1fr);
  padding: var(--sp-2) 0;
  overflow-wrap: anywhere;
}
.post-sources-list > li::before {
  content: counter(pb-src, decimal-leading-zero);
  content: counter(pb-src, decimal-leading-zero) / "";
  grid-row: span 2;
  font-family: var(--ff-num);
  font-size: .75rem;
  line-height: 2;
  color: var(--c-accent-ink);
}
.post-sources-list a {
  color: var(--c-link);
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: .2em;
}
.post-sources-list a:hover { color: var(--c-link-hover); text-decoration-thickness: 2px; }
.post-source-host {
  font-family: var(--ff-num);
  font-size: .75rem;
  color: var(--c-muted);
}

.post-disclaimer {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  gap: var(--sp-3);
  padding: var(--sp-5);
  font-size: .875rem;
  line-height: 1.6;
  color: var(--c-text-soft);
  background: var(--c-surface);
  border: 1px solid var(--c-border);
  border-radius: var(--r-lg);
}
.post-disclaimer-icon { margin-top: 1px; color: var(--c-muted); }
.post-disclaimer p + p { margin-top: var(--sp-2); }
.post-disclaimer .post-foot-title { margin-bottom: var(--sp-2); }

.post-author {
  position: relative;
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  grid-template-areas: "photo head" "bio bio" "more more";
  column-gap: var(--sp-4);
  align-items: center;
  padding: var(--sp-5);
  background: var(--c-card);
  border: 1px solid var(--c-border);
  border-radius: var(--r-lg);
}
/* A gold hairline at the top edge — the plate's signature, kept short. */
.post-author::before {
  content: '';
  position: absolute;
  left: var(--sp-5);
  top: -1px;
  width: 2.5rem;
  height: 2px;
  background: var(--c-accent);
}
.post-author .post-author-photo { grid-area: photo; width: 4rem; height: 4rem; }
.post-author-head { grid-area: head; min-width: 0; }
.post-author-photo--mono { font-size: 1.75rem; }
.post-author-kicker {
  font-family: var(--ff-num);
  font-size: .6875rem;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--c-muted);
}
.post-author-name {
  margin-top: var(--sp-1);
  font-family: var(--ff-head);
  font-size: 1.125rem;
  font-weight: 700;
  line-height: 1.3;
  color: var(--c-text);
}
.post-author-name > a { color: inherit; text-decoration: underline; text-decoration-color: var(--c-border-strong); text-underline-offset: .2em; }
.post-author-name > a:hover { text-decoration-color: var(--c-primary); }
.post-author-role { margin-top: 2px; font-size: .875rem; font-weight: 600; color: var(--c-text-soft); }
.post-author-bio { grid-area: bio; margin-top: var(--sp-4); font-size: .9375rem; line-height: 1.6; color: var(--c-text-soft); }
.post-author-more { grid-area: more; justify-self: start; margin-top: var(--sp-1); }

.post-related {
  padding-block: var(--sp-section);
  border-top: 1px solid var(--c-border);
}

/* Admin preview only: a strip that says what this is. */
.post-preview-bar {
  position: sticky;
  top: var(--header-h);
  z-index: 50;
  padding: var(--sp-2) var(--sp-4);
  font-size: .875rem;
  line-height: 1.45;
  text-align: center;
  color: var(--c-text);
  background: var(--c-warn-bg);
  border-bottom: 1px solid var(--c-warn-bdr);
}
.post-preview-bar strong { color: var(--c-warning); }

/* Subgrid: the body (and the full-bleed figure) take the article's lines, so
   the body column IS the title's column, and the table of contents can have a
   column of its own. Without subgrid the standalone grid of .post-body draws
   the same lines over the same width. */
@supports (grid-template-columns: subgrid) {
  .post-main {
    display: grid;
    grid-template-columns: subgrid;
  }
  .post-main > .post-rail {
    grid-column: content;
    grid-row: 1;
    width: auto;
    margin: 0 0 var(--sp-8);
  }
  .post-main > .post-body {
    grid-column: full;
    grid-row: 2;
    grid-template-columns: subgrid;
  }
  .post-body > .pb-figure--full {
    display: grid;
    grid-template-columns: subgrid;
  }
  .post-body > .pb-figure--full > img { grid-column: full; }
  .post-body > .pb-figure--full > figcaption { grid-column: content; padding-inline: 0; }
}

@media (min-width: 600px) {
  .post { --post-gutter: var(--sp-6); }
  .post-body { --pb-gutter: var(--sp-6); }
  .post-author .post-author-photo { width: 5rem; height: 5rem; }
  .post-author {
    grid-template-areas: "photo head" "photo bio" "photo more";
    grid-template-rows: auto auto 1fr;
    align-items: start;
    column-gap: var(--sp-5);
    padding: var(--sp-6);
  }
  .post-author-bio { margin-top: var(--sp-2); }
  .post-author::before { left: var(--sp-6); }
}
@media (min-width: 768px) {
  .post { font-size: 1.125rem; }
  .post-head { padding-bottom: var(--sp-10); }
  .post-lead { font-size: 1.25rem; }
  .post-cover { margin-top: var(--sp-8); }
  .post-cover--plate { aspect-ratio: 4 / 1; }
  .post-cover-glyph { width: 3.25rem; height: 3.25rem; }
}
/* From 1024, a post with a table of contents gets a rail beside the body: the
   measure moves left, figures widen leftward only, and the contents follow
   the reader (sticky inside the body's own row, so they stop where the
   article does). A full-bleed figure passes OVER the rail. */
@media (min-width: 1024px) {
  @supports (grid-template-columns: subgrid) {
    .post--toc {
      grid-template-columns:
        [full-start] minmax(var(--post-gutter), 1fr)
        [wide-start] minmax(0, 8rem)
        [content-start] min(var(--post-measure), 100% - 20rem)
        [content-end wide-end] 3rem
        [rail-start] 14rem
        [rail-end] minmax(var(--post-gutter), 1fr)
        [full-end];
    }
    .post--toc .post-main > .post-rail {
      grid-column: rail;
      grid-row: 1;
      margin: 0;
    }
    .post--toc .post-main > .post-body { grid-row: 1; }
    .post--toc .pb-toc {
      position: sticky;
      top: calc(var(--sticky-top) + var(--sp-4));
      max-height: calc(100vh - var(--sticky-top) - var(--sp-8));
      overflow-y: auto;
      /* Room inside the scroll box for the focus ring (2px + offset), which
         overflow:auto would otherwise clip on the right and at the ends. */
      padding: var(--sp-1) var(--sp-1) var(--sp-1) var(--sp-4);
      background: none;
      border: 0;
      border-left: 1px solid var(--c-border);
      border-radius: 0;
    }
    .post--toc .pb-toc a { font-size: .875rem; min-height: 0; padding: .4rem 0; }
    .post--toc .pb-toc a::after {
      content: '';
      position: absolute;
      left: calc(-1 * var(--sp-4) - 1px);
      top: .35rem;
      bottom: .35rem;
      width: 2px;
      background: var(--c-accent);
      opacity: 0;
      transform: scaleY(.4);
      transition: opacity var(--t-md), transform var(--t-md);
    }
    .post--toc .pb-toc a[aria-current="true"]::after { opacity: 1; transform: none; }
  }
}
@media (prefers-reduced-motion: reduce) {
  .post--toc .pb-toc a::after { transition: none; }
}


/* ══ 3 · THE POST CARD ══════════════════════════════════════════
   One card for every list on the site. The title link is stretched over the
   card, so the whole card is one link and one tab stop; its focus ring is
   drawn on the stretched layer, around the card.
   Phones: a row — thumbnail and title — so a list reads as a list. From
   600px: a column with the 16:9 picture on top and the excerpt. */

.post-grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--sp-3);
  margin: 0;
  padding: 0;
  list-style: none;
}
.post-grid > li { display: flex; min-width: 0; }

.post-card {
  position: relative;
  display: grid;
  grid-template-columns: 6.5rem minmax(0, 1fr);
  width: 100%;
  overflow: hidden;
  color: var(--c-text);
  background: var(--c-card);
  border: 1px solid var(--c-border);
  border-radius: var(--r-lg);
}
/* Colour changes are instant; only transform and opacity animate. */
.post-card:hover { border-color: var(--c-primary); }

.post-card-media {
  position: relative;
  overflow: hidden;
  min-height: 6.5rem;
  color: var(--c-art-line);
  background-color: var(--c-plate);
  border-right: 1px solid var(--c-img-bdr);
}
.post-card-media > img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform var(--t-md);
}
.post-card-media--plate {
  display: grid;
  place-items: center;
  background-image: var(--grad-tile-glow), var(--grad-plate);
}
.post-card-glyph {
  width: 2rem;
  height: 2rem;
  transition: transform var(--t-md);
}
.post-card:hover .post-card-media > img { transform: scale(1.03); }
.post-card:hover .post-card-glyph { transform: translateY(-3px); }

.post-card-body {
  display: flex;
  flex-direction: column;
  gap: var(--sp-1);
  min-width: 0;
  padding: var(--sp-3) var(--sp-4);
}
.post-card-kicker {
  font-family: var(--ff-num);
  font-size: .6875rem;
  line-height: 1.5;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--c-accent-ink);
}
.post-card-dot { color: var(--c-placeholder); }
.post-card-title {
  font-family: var(--ff-head);
  font-size: 1rem;
  font-weight: 700;
  line-height: 1.3;
  letter-spacing: -.01em;
  color: var(--c-text);
  text-wrap: pretty;
}
.post-card-title > a { color: inherit; text-decoration: none; }
.post-card-title > a::after {
  content: '';
  position: absolute;
  inset: 0;
  z-index: 1;
  border-radius: inherit;
}
.post-card-title > a:focus-visible { outline: none; box-shadow: none; }
.post-card-title > a:focus-visible::after {
  /* Inside the edge: the card clips its corners (overflow: hidden), and a ring
     drawn outside would be clipped with them. */
  outline: 2px solid var(--c-focus);
  outline-offset: -2px;
  border-radius: var(--r-lg);
}
.post-card-excerpt {
  display: none;
  font-size: .875rem;
  line-height: 1.55;
  color: var(--c-text-soft);
}
.post-card-date {
  margin-top: auto;
  padding-top: var(--sp-1);
  font-size: .8125rem;
  line-height: 1.45;
  color: var(--c-muted);
  font-variant-numeric: tabular-nums;
}
.post-card-date > time,
.post-card-read { white-space: nowrap; }

/* The featured post: a column on phones, a spread from 768. */
.post-card--featured { grid-template-columns: minmax(0, 1fr); }
.post-card--featured .post-card-media {
  aspect-ratio: 16 / 9;
  min-height: 0;
  border-right: 0;
  border-bottom: 1px solid var(--c-img-bdr);
}
.post-card--featured .post-card-glyph { width: 3rem; height: 3rem; }
.post-card--featured .post-card-body { gap: var(--sp-2); padding: var(--sp-5); }
.post-card--featured .post-card-title { font-size: clamp(1.375rem, 1.1rem + 1.2vw, 2rem); line-height: 1.15; letter-spacing: -.015em; }
.post-card--featured .post-card-excerpt { display: block; font-size: .9375rem; }

/* The product page's list: always rows, never pictures on top. */
.post-grid--compact .post-card-title { font-size: .9375rem; }

@media (min-width: 600px) {
  .post-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--sp-4); }
  .post-grid:not(.post-grid--compact) .post-card { display: flex; flex-direction: column; }
  .post-grid:not(.post-grid--compact) .post-card-media {
    aspect-ratio: 16 / 9;
    min-height: 0;
    border-right: 0;
    border-bottom: 1px solid var(--c-img-bdr);
  }
  .post-grid:not(.post-grid--compact) .post-card-body { flex: 1; gap: var(--sp-2); padding: var(--sp-4) var(--sp-5) var(--sp-5); }
  .post-grid:not(.post-grid--compact) .post-card-title { font-size: 1.125rem; }
  .post-grid:not(.post-grid--compact) .post-card-excerpt {
    display: -webkit-box;
    -webkit-line-clamp: 3;
    -webkit-box-orient: vertical;
    overflow: hidden;
  }
  .post-card--featured .post-card-body { padding: var(--sp-6); }
}
/* Two columns, an odd card out: the last one spans both and lies down, so no
   cell is ever left empty. */
@media (min-width: 600px) and (max-width: 1023px) {
  .post-grid:not(.post-grid--compact) > li:last-child:nth-child(odd) { grid-column: 1 / -1; }
  .post-grid:not(.post-grid--compact) > li:last-child:nth-child(odd) .post-card {
    display: grid;
    grid-template-columns: minmax(0, 2fr) minmax(0, 3fr);
  }
  .post-grid:not(.post-grid--compact) > li:last-child:nth-child(odd) .post-card-media {
    aspect-ratio: auto;
    min-height: 11rem;
    border-bottom: 0;
    border-right: 1px solid var(--c-img-bdr);
  }
}
@media (min-width: 768px) {
  .post-card--featured { display: grid; grid-template-columns: minmax(0, 7fr) minmax(0, 5fr); }
  .post-card--featured .post-card-media {
    aspect-ratio: auto;
    min-height: 20rem;
    border-bottom: 0;
    border-right: 1px solid var(--c-img-bdr);
  }
  .post-card--featured .post-card-body { justify-content: center; padding: var(--sp-10) var(--sp-8); }
  .post-card--featured .post-card-glyph { width: 4rem; height: 4rem; }
}
@media (min-width: 1024px) {
  .post-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .post-card--featured .post-card-media { min-height: 24rem; }
}
@media (prefers-reduced-motion: reduce) {
  .post-card-media > img,
  .post-card-glyph { transition: none; }
  .post-card:hover .post-card-media > img,
  .post-card:hover .post-card-glyph { transform: none; }
}


/* ══ 4 · LIST PAGES — /blog, topics, authors ═══════════════════ */

.blog-page { padding-bottom: var(--sp-section); }

/* The opening band, lit like the homepage hero. */
.blog-band {
  background-color: var(--c-hero-fallback);
  background-image: var(--grad-hero);
  border-bottom: 1px solid var(--c-border);
}
.blog-hero { padding: var(--sp-2) 0 var(--sp-8); }
/* /blog has no visible breadcrumb above the hero, so the hero brings its own
   top space; topic and author pages keep the breadcrumb and the 8px. */
.blog-band .container > .blog-hero:first-child { padding-top: var(--sp-8); }
.blog-title {
  font-family: var(--ff-head);
  font-size: clamp(2rem, 1.45rem + 2.6vw, 3.5rem);
  font-weight: 800;
  line-height: 1.06;
  letter-spacing: -.02em;
  color: var(--c-text);
  text-wrap: balance;
}
.blog-intro {
  max-width: 42rem;
  margin-top: var(--sp-4);
  font-size: 1rem;
  line-height: 1.65;
  color: var(--c-text-soft);
}
.blog-intro p + p,
.blog-intro p + ul,
.blog-intro ul + p { margin-top: var(--sp-3); }
.blog-intro ul { padding-left: 1.25em; list-style: disc outside; }
.blog-intro li::marker { color: var(--c-accent-ink); }
.blog-topics-nav { margin-top: var(--sp-6); }

.blog-featured { margin-top: var(--sp-8); }

.blog-section { margin-top: var(--sp-12); }

/* «Про блог» — a quiet note at the foot of /blog: an icon, a label and one
   sentence, smaller and dimmer than anything above it on purpose. */
.blog-about {
  display: flex;
  align-items: flex-start;
  gap: var(--sp-3);
  margin-top: var(--sp-12);
  padding-top: var(--sp-6);
  border-top: 1px solid var(--c-border);
}
.blog-about-icon {
  flex-shrink: 0;
  width: 1.25rem;
  height: 1.25rem;
  color: var(--c-placeholder);
}
.blog-about-body {
  max-width: 40rem;
  font-size: 0.875rem;
  line-height: 1.6;
  color: var(--c-muted);
}
.blog-about-title {
  margin-bottom: var(--sp-1);
  font-weight: 600;
  color: var(--c-text-soft);
}
.blog-section-head {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  justify-content: space-between;
  gap: var(--sp-2) var(--sp-6);
  margin-bottom: var(--sp-6);
}

/* Topic cards — text only, with the plate's gold hairline. */
.topic-grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--sp-3);
  margin: 0;
  padding: 0;
  list-style: none;
}
.topic-grid > li { display: flex; }
.topic-card {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: var(--sp-2);
  width: 100%;
  padding: var(--sp-5) calc(var(--sp-5) + 1.25rem) var(--sp-5) var(--sp-5);
  color: var(--c-text);
  background-color: var(--c-card);
  background-image: var(--grad-tile-glow);
  border: 1px solid var(--c-border);
  border-radius: var(--r-lg);
}
.topic-card::before {
  content: '';
  position: absolute;
  left: var(--sp-5);
  top: -1px;
  width: 2.5rem;
  height: 2px;
  background: var(--c-accent);
}
.topic-card:hover { border-color: var(--c-primary); }
.topic-card-name { font-family: var(--ff-head); font-size: 1.0625rem; font-weight: 700; line-height: 1.3; }
.topic-card-desc { font-size: .875rem; line-height: 1.55; color: var(--c-text-soft); }
.topic-card-count {
  margin-top: auto;
  padding-top: var(--sp-1);
  font-family: var(--ff-num);
  font-size: .75rem;
  color: var(--c-muted);
}
.topic-card-arrow {
  position: absolute;
  right: var(--sp-4);
  bottom: var(--sp-5);
  color: var(--c-accent-ink);
  transition: transform var(--t-md);
}
.topic-card:hover .topic-card-arrow { transform: translateX(3px); }

/* Pagination: sequential links, 44px targets. */
.pager {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-3);
  margin-top: var(--sp-8);
  padding-top: var(--sp-6);
  border-top: 1px solid var(--c-border-light);
}
.pager-list {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: var(--sp-1);
  order: -1;
  width: 100%;
  margin: 0;
  padding: 0;
  list-style: none;
}
.pager-num,
.pager-gap {
  display: inline-grid;
  place-items: center;
  min-width: 44px;
  height: 44px;
  padding: 0 var(--sp-2);
  font-family: var(--ff-num);
  font-size: .875rem;
  color: var(--c-text-soft);
  border: 1px solid transparent;
  border-radius: var(--r);
}
.pager-num:hover { color: var(--c-text); border-color: var(--c-border-strong); }
.pager-num[aria-current="page"] {
  color: var(--c-primary);
  background: var(--c-primary-wash);
  border-color: var(--c-primary);
}
.pager-gap { color: var(--c-placeholder); }
.pager-step {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  min-height: 44px;
  font-weight: 600;
  font-size: .9375rem;
  color: var(--c-text);
}
a.pager-step:hover { color: var(--c-primary); }

/* An honest empty blog: what is missing and where to go instead. */
.blog-empty {
  display: grid;
  justify-items: center;
  gap: var(--sp-3);
  margin-top: var(--sp-8);
  padding: var(--sp-12) var(--sp-5);
  text-align: center;
  color: var(--c-text-soft);
  background-color: var(--c-plate);
  background-image: var(--grad-plate);
  border: 1px solid var(--c-border-light);
  border-radius: var(--r-lg);
}
.blog-empty > svg { width: 3rem; height: 3rem; color: var(--c-plate-ink); }
.blog-empty > h2 { font-size: clamp(1.375rem, 1.2rem + .9vw, 1.75rem); color: var(--c-text); }
.blog-empty > p { max-width: 36rem; }
.blog-empty > .btn { margin-top: var(--sp-2); }

/* Author page. */
.author-hero {
  display: grid;
  gap: var(--sp-5);
  padding: var(--sp-2) 0 var(--sp-8);
}
.author-photo {
  display: grid;
  place-items: center;
  width: 7.5rem;
  height: 7.5rem;
  object-fit: cover;
  font-family: var(--ff-head);
  font-size: 3rem;
  font-weight: 800;
  color: var(--c-accent-ink);
  background-color: var(--c-plate);
  background-image: var(--grad-tile-glow);
  border: 1px solid var(--c-border-strong);
  border-radius: 50%;
}
.author-role {
  margin-top: var(--sp-2);
  font-size: 1rem;
  font-weight: 600;
  color: var(--c-text-soft);
}
.author-links {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-2);
  margin: var(--sp-5) 0 0;
  padding: 0;
  list-style: none;
}
.author-links a {
  display: inline-flex;
  align-items: center;
  min-height: 36px;
  padding: 6px 14px;
  font-family: var(--ff-num);
  font-size: .8125rem;
  color: var(--c-text-soft);
  text-decoration: underline;
  text-decoration-color: var(--c-border-strong);
  text-underline-offset: .2em;
  background: var(--c-card);
  border: 1px solid var(--c-border-strong);
  border-radius: var(--r-pill);
}
.author-links a:hover { color: var(--c-text); border-color: var(--c-primary); }

@media (pointer: coarse) {
  .author-links a,
  .post-topic,
  .post--toc .pb-toc a { min-height: 44px; }
}
@media (min-width: 600px) {
  .topic-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--sp-4); }
  .pager { flex-wrap: nowrap; }
  .pager-list { order: 0; width: auto; }
  .pager-step--next { justify-content: flex-end; }
  .pager-step { min-width: 7.5rem; }
}
@media (min-width: 768px) {
  .blog-hero { padding-bottom: var(--sp-12); }
  .blog-band .container > .blog-hero:first-child { padding-top: var(--sp-12); }
  .blog-intro { font-size: 1.125rem; }
  .author-hero {
    grid-template-columns: 10rem minmax(0, 1fr);
    gap: var(--sp-8);
    align-items: start;
    padding-bottom: var(--sp-12);
  }
  .author-photo { width: 10rem; height: 10rem; font-size: 4rem; }
}
/* Up to three abreast; two topics share the row instead of leaving a hole. */
@media (min-width: 1024px) {
  .topic-grid { grid-template-columns: repeat(auto-fit, minmax(18rem, 1fr)); }
}
@media (prefers-reduced-motion: reduce) {
  .topic-card-arrow { transition: none; }
  .topic-card:hover .topic-card-arrow { transform: none; }
}


/* ══ 5 · MOTION ═════════════════════════════════════════════════
   Cards rise into place as they enter — tied to their own view timeline, no
   IntersectionObserver, no scroll listener. Three gates: screen (print has no
   timeline and would print the 0% frame), the feature itself, and the
   reader's motion preference. The featured card is the first screen of /blog
   and never animates. Focus inside a card cancels the animation: the browser
   scrolls focus to `nearest`, which can stop mid-range. */
@media screen {
  @supports (animation-timeline: view()) {
    @media (prefers-reduced-motion: no-preference) {
      .post-card:not(.post-card--featured),
      .topic-card {
        animation: pb-rise linear both;
        animation-timeline: view();
        animation-range: entry 0% entry 55%;
      }
      .post-card:focus-within,
      .topic-card:focus-visible {
        animation: none;
        opacity: 1;
        transform: none;
      }
    }
  }
}
