/* ==========================================================================
   MyCabin Blog — Page Pack CSS
   Scoped entirely under .pp-blog (Page Pack Spec §5). Trimmed from the
   Claude Design export's shared styles.css: only rules this page's own
   sections actually use. STATIC content only (hero, newsletter band,
   dark CTA) — every selector below that targets a class the dynamic
   mycabin/blog-listing block itself emits (blog-filter, tab, blog-tag,
   blog-date, blog-featured*, blog-card*, blog-grid, blog-more, read-time,
   eyebrow) is present because the Page Pack owns VISUAL styling for that
   markup while the plugin owns its DATA/behavior — the established
   Page-Pack-owns-visuals / plugin-owns-functional split already used on
   MyCabin Demo Days' own Page Pack (see that pack's own frontend.css for
   the identical pattern, e.g. `.pp-demo-days .mdd-cta`).

   01  Tokens (scoped custom properties, not :root)
   02  Base resets (scoped)
   03  Flow-layout margin neutralization
   04  Hero
   05  Blog Listing area (styles mycabin/blog-listing's own markup)
   06  Newsletter
   07  Dark Demo Days CTA
   08  Reduced motion
   ========================================================================== */


/* ==========================================================================
   01  TOKENS — scoped to .pp-blog, never :root (Page Pack Spec §5, layer 2).
   Identical palette to every other MyCabin Page Pack — source-design's own
   README: "This page uses the same lavender/ink/beige palette as the rest
   of the site — nothing page-specific here."
   ========================================================================== */

.pp-blog {
  --pp-ink:          #27272C;
  --pp-bone:         #FFFFFF;
  --pp-surface:      #FBF6F3;
  --pp-accent:       #917EBC;
  --pp-accent-soft:  #CDCBFF;
  --pp-tint:         #E6E6FF;

  --pp-stone:            color-mix(in oklab, var(--pp-ink) 11%, var(--pp-bone));
  --pp-ink-hair:         color-mix(in oklab, var(--pp-ink) 7%,  var(--pp-bone));
  --pp-ink-line:         color-mix(in oklab, var(--pp-ink) 13%, var(--pp-bone));
  --pp-tint-line-strong: color-mix(in oklab, var(--pp-accent) 34%, var(--pp-tint));

  --pp-ink-72: color-mix(in oklab, var(--pp-ink) 72%, transparent);
  /* 65%, not 56% — matches the site-wide WCAG AA fix already applied on
     every other MyCabin Page Pack for this identical anthracite/beige
     pairing (source's own --ink-56 comment records the same reasoning). */
  --pp-ink-56: color-mix(in oklab, var(--pp-ink) 65%, transparent);

  --pp-maxw:   1280px;
  --pp-gutter: clamp(18px, 4vw, 40px);
  --pp-hdr-h:  clamp(58px, 7vw, 74px);
  --pp-sec-y-p: clamp(48px, 7vw, 104px);

  --pp-r-pill: 999px;
  --pp-r-card: 11px;
  --pp-r-xs:   7px;

  --pp-ease:  cubic-bezier(.22, 1, .36, 1);
  --pp-t-med: .25s;

  position: relative;
  background: var(--pp-bone);
  color: var(--pp-ink);
  overflow-x: clip;
}


/* ==========================================================================
   02  BASE RESETS (scoped)
   ========================================================================== */

.pp-blog,
.pp-blog *,
.pp-blog *::before,
.pp-blog *::after {
  box-sizing: border-box;
}
.pp-blog img { max-width: 100%; display: block; }
.pp-blog a { color: inherit; text-decoration: none; }
.pp-blog a:hover { color: var(--pp-accent); }

.pp-blog .wrap {
  max-width: var(--pp-maxw);
  margin: 0 auto;
  padding-inline: var(--pp-gutter);
}

.pp-blog h1,
.pp-blog h2,
.pp-blog h3 { margin: 0; }
.pp-blog p { margin: 0; }

/* The native [hidden] attribute is used by the plugin's own Load More
   button and "Newest" eyebrow. Scoped defensively so a flex/grid display
   value declared anywhere below can never silently beat the browser's
   own default [hidden]{display:none} — the exact bug source's own README
   documents and fixes for .blog-card/.blog-more's button. */
.pp-blog [hidden] { display: none !important; }

/* Correction round (v2.0.4): fine paper-grain dot pattern, missing from
   this Page Pack entirely until now. Source (blog-original.zip,
   styles.css line 145-156) applies this SITE-WIDE via `body::after` —
   not a Blog-specific treatment — so it is reproduced here scoped to
   `.pp-blog::after` (this page only) rather than added to the Design
   System globally, which is out of this round's scope. Exact source
   technique and values, unchanged: a `position:fixed; inset:0` overlay
   (covers the whole viewport, so it is visible behind every section —
   hero, filters, listing, newsletter, dark CTA — exactly as it is
   site-wide in source, not something invented from a screenshot),
   opacity .05, a radial-gradient dot (not a raster image asset) sized
   .5px with `color-mix(in oklab, var(--ink) 55%, transparent)`, tiled
   at a 3x3px background-size. `pointer-events:none` and a z-index of
   200 (matching source exactly) keep it non-interactive and above
   section backgrounds but below the WordPress admin bar (#wpadminbar is
   z-index 99999) and this page's own "Agent" floating control. */
.pp-blog::after {
  content: "";
  position: fixed;
  inset: 0;
  z-index: 200;
  pointer-events: none;
  opacity: .05;
  background-image: radial-gradient(color-mix(in oklab, var(--pp-ink) 55%, transparent) .5px, transparent .5px);
  background-size: 3px 3px;
}


/* ==========================================================================
   03  FLOW-LAYOUT MARGIN NEUTRALIZATION
   core/group with no explicit layout attribute injects margin-block-start
   on every direct child but the first, sized from the active theme's own
   blockGap — a real bug for a horizontal flex row (see
   docs/MYCABIN-GUTENBERG-REFERENCE-IMPLEMENTATION.md §5). Both .dark-cta
   and .blog-newsletter-row are horizontal flex rows with two direct
   Gutenberg-block children.
   ========================================================================== */

.pp-blog .dark-cta > *,
.pp-blog .blog-newsletter-row > * {
  margin-block-start: 0 !important;
  margin-block-end: 0 !important;
}

/* Correction round (v2.0.2): the same Flow-injected margin-block-start
   also lands on .dark-cta-section itself, as a direct child of the root
   .pp-blog group — invisible between .blog-hero/.blog-list-section (both
   white-on-white) but a visible 40px white strip before the dark CTA's
   opaque ink background. Confirmed via computed style on staging
   (margin-top: 40px, sourced from the theme's blockGap, no inline style).
   Scoped to this one section only — .blog-list-section and
   .blog-newsletter-section keep their existing (already-approved,
   invisible) flow gap untouched, per this round's scope lock. */
.pp-blog .dark-cta-section {
  margin-top: 0 !important;
}


/* ==========================================================================
   04  HERO
   ========================================================================== */

/* Correction round (v2.0.4): bottom padding removed from the section
   itself. Source (styles.css line 3476) applies clamp(32px,5vw,48px) as
   .blog-hero's own bottom padding because .blog-filter is source's own
   LAST child of the hero — that gap is now supplied as margin-top on
   .mcb-blog-listing__body instead (see section 05), so it lands
   correctly between the filter and the white listing area rather than
   after the entire (now hero-nested) dynamic block. */
.pp-blog .blog-hero {
  padding: calc(var(--pp-hdr-h) + clamp(28px, 6vw, 48px)) 0 0;
  background: var(--pp-surface);
}
/* Hero content runs full gutter-aligned width, matching every other
   section on the site — only the title/lead text itself is capped. */
.pp-blog .blog-hero__text { max-width: 640px; }
.pp-blog .blog-hero .h1 {
  margin-top: 10px !important;
  font-size: clamp(34px, 8.6vw, 46px);
  line-height: 1.06;
  letter-spacing: -.026em;
  font-weight: 500;
}
.pp-blog .blog-hero .lead {
  margin-top: 14px !important;
  font-size: clamp(16px, 2vw, 20px);
  line-height: 1.55;
  color: var(--pp-ink-72);
  font-weight: 300;
}


/* ==========================================================================
   05  BLOG LISTING AREA — styles mycabin/blog-listing's own rendered
   markup (Phase 2/3 plugin output; the wrapping .mcb-blog-listing__body
   element was added in plugin v0.3.1, Phase 4 correction round). This
   Page Pack owns every property below; the plugin never sets its own
   visual design, only functional/accessibility CSS (hidden-state,
   loading-state, no-image fallback).

   Correction round (v2.0.4): the block is now embedded directly inside
   .blog-hero (content.html), right after .blog-hero__inner, matching
   source's own literal structure (blog.html line 136-158: .blog-filter
   is a sibling of .blog-hero__inner inside <section class="blog-hero">,
   sharing its beige background — a prior, DELIBERATE deviation from
   this, recorded in earlier manifest.json notes, is now corrected).
   The plugin's own root .mcb-blog-listing div is not itself wrapped in
   a .wrap here (unlike v2.0.0-2.0.3) — instead .blog-filter and
   .mcb-blog-listing__body each get .wrap's own box model directly
   (max-width/margin-inline/padding-inline below), because a single
   .wrap ancestor around the whole block would also cap
   .mcb-blog-listing__body's own BACKGROUND at 1280px, when source's
   white listing background runs full-bleed edge to edge (only its
   CONTENT is 1280px-capped, exactly like .wrap does at the section
   level everywhere else on this page). */

/* Gutenberg's Flow layout injects margin-block-start (the theme's
   blockGap) on every child of .blog-hero but the first — the same bug
   documented in section 03 for .dark-cta-section, landing here because
   .mcb-blog-listing (the plugin's own root output) is now the SECOND
   child of the hero group, after .blog-hero__inner. Neutralized with
   the same scoped, smallest-selector pattern; confirmed via computed
   style before committing (see manifest.json notes). */
.pp-blog .blog-hero > .mcb-blog-listing {
  margin-top: 0 !important;
}

/* Category filter pills — still source's own margin-top (the
   "paragraph → filter gap"), now correct again since .blog-filter is
   back to being a normal sibling of the hero text, matching source,
   instead of the first child of a separate white section. */
.pp-blog .blog-filter {
  margin-top: clamp(20px, 3vw, 28px);
  max-width: var(--pp-maxw);
  margin-inline: auto;
  padding-inline: var(--pp-gutter);
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}
.pp-blog .blog-filter .tab {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  height: 30px;
  padding: 0 14px;
  border-radius: var(--pp-r-pill);
  border: 1px solid var(--pp-ink-line);
  background: transparent;
  color: var(--pp-ink);
  font-family: "Poppins", "Helvetica Neue", Helvetica, sans-serif; /* Correction
    round (v2.0.4): .tab renders as a real <button>, which browsers do
    not give the page's own font-family by default — confirmed via
    computed style on staging (resolved to the browser's UI font, not
    Poppins). Source (styles.css line 138) sets this stack on body,
    inherited everywhere except native form controls like buttons;
    reproduced explicitly here rather than adding a page-wide
    `button { font-family: inherit }` reset, which is out of this
    round's narrowly-scoped fix. Already loaded by the MyCabin
    Gutenberg Design System — no font is loaded again here. */
  font-size: 14px;
  font-weight: 500;
  white-space: nowrap;
  cursor: pointer;
  transition: background var(--pp-t-med), color var(--pp-t-med), border-color var(--pp-t-med);
}
.pp-blog .blog-filter .tab.is-active {
  background: var(--pp-ink);
  border-color: var(--pp-ink);
  color: var(--pp-bone);
}
.pp-blog .blog-filter .tab:hover:not(.is-active) { background: var(--pp-surface); }
.pp-blog .blog-filter .tab:focus-visible { outline: 2px solid var(--pp-accent-soft); outline-offset: 2px; }

/* Loading-state / status region (plugin-owned functional behavior;
   this pack only styles it not to disrupt layout when empty). */
.pp-blog .mcb-blog-status:empty { display: none; }
.pp-blog .mcb-blog-listing[data-mcb-loading] { opacity: .6; }

/* Correction round (v2.0.4): .mcb-blog-listing__body (plugin v0.3.1) is
   the article-list "section" — everything the block renders except the
   filter. Its own background runs full-bleed (it is not inside a
   .wrap), replicating source's .blog-list-section as a real section
   with white/bone background. margin-top reproduces source's OWN
   .blog-hero bottom padding (clamp(32px,5vw,48px), styles.css line
   3476) — the beige gap between the filter and where white begins,
   now living here instead of on .blog-hero itself (see section 04).
   The padding below reproduces source's .blog-list-section padding
   exactly (styles.css §"POSTS"/blog-list-section rule): top
   clamp(28px,5vw,44px), bottom var(--pp-sec-y-p) — unchanged from the
   v2.0.0-2.0.3 rule this replaces. */
.pp-blog .mcb-blog-listing__body {
  margin-top: clamp(32px, 5vw, 48px);
  background: var(--pp-bone);
  padding: clamp(28px, 5vw, 44px) 0 var(--pp-sec-y-p);
}
/* Each of .mcb-blog-listing__body's direct children (status region,
   eyebrow, featured slot, grid, Load More) gets .wrap's own box model
   directly, since there is no literal .wrap div around them in the
   plugin's markup (see the section-05 note above for why). */
.pp-blog .mcb-blog-listing__body > * {
  max-width: var(--pp-maxw);
  margin-inline: auto;
  padding-inline: var(--pp-gutter);
}

/* "Newest" eyebrow — already emitted by the plugin's own render.php.
   Correction round (v2.0.5): removed an erroneous margin-top that was
   never in source. Source (styles.css line 274-280 for the base
   .eyebrow rule, line 3492 for `.blog-list-section .eyebrow`) sets only
   `margin-bottom: 14px` — no margin-top at all; the section's own
   top padding is the ONLY gap above it. This pack's own margin-top
   (clamp(28px,5vw,44px)) duplicated .mcb-blog-listing__body's own
   padding-top of the identical value, doubling the hero-to-"Newest"
   gap. Confirmed live on staging before removing it: gap measured
   88px -> 60px at a 500px viewport, exactly matching the sum of
   .mcb-blog-listing__body's own margin-top (32px at this width) +
   padding-top (28px at this width) once the duplicate was gone. */
.pp-blog .eyebrow {
  margin-bottom: 14px !important;
  font-size: 11px;
  font-weight: 400;
  letter-spacing: .18em;
  text-transform: uppercase;
  color: var(--pp-ink-56);
}

/* Featured article — media left, copy right on desktop; stacked mobile. */
.pp-blog .blog-featured {
  display: flex;
  flex-direction: column;
  background: var(--pp-bone);
  border: 1px solid var(--pp-stone);
  border-radius: var(--pp-r-card);
  overflow: hidden;
}
.pp-blog .blog-featured__media { position: relative; aspect-ratio: 4 / 3; }
.pp-blog .blog-featured__media img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.pp-blog .blog-featured__body {
  padding: clamp(22px, 3vw, 32px);
  display: flex;
  flex-direction: column;
  gap: 14px;
}
.pp-blog .blog-featured__body h2 {
  font-size: clamp(22px, 3.2vw, 30px);
  line-height: 1.15;
  letter-spacing: -.02em;
  font-weight: 500;
  color: var(--pp-ink);
}
.pp-blog .blog-featured__body p {
  font-size: 14px;
  line-height: 1.6;
  font-weight: 300;
  color: var(--pp-ink-72);
}

/* Category tag pill — shared by the featured card and every grid card. */
.pp-blog .blog-tag {
  display: inline-flex;
  align-items: center;
  align-self: flex-start;
  padding: 4px 11px;
  border-radius: var(--pp-r-pill);
  background: var(--pp-tint);
  border: 1px solid var(--pp-tint-line-strong);
  color: var(--pp-accent);
  font-size: 11.5px;
  font-weight: 500;
  white-space: nowrap;
}
.pp-blog .blog-date { font-size: 12.5px; color: var(--pp-ink-56); }
.pp-blog .blog-byline .link-arrow { font-size: 13px; font-weight: 500; color: var(--pp-ink); }
.pp-blog .blog-byline .link-arrow:hover { color: var(--pp-accent); }

/* Article grid */
.pp-blog .blog-grid {
  margin-top: clamp(32px, 5vw, 48px) !important;
  display: grid;
  grid-template-columns: 1fr;
  gap: clamp(16px, 2.2vw, 22px);
}
.pp-blog .blog-card {
  display: flex;
  flex-direction: column;
  background: var(--pp-bone);
  border: 1px solid var(--pp-stone);
  border-radius: var(--pp-r-card);
  overflow: hidden;
}
.pp-blog .blog-card__media { position: relative; aspect-ratio: 4 / 3; }
.pp-blog .blog-card__media img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
/* Clean no-image fallback (plugin adds this modifier class when a post
   has no Featured Image — instruction: "use a clean no-image/fallback
   state"). */
.pp-blog .blog-card__media--no-image,
.pp-blog .blog-featured__media--no-image {
  background: var(--pp-surface);
}
.pp-blog .blog-card__body {
  padding: clamp(18px, 2.2vw, 22px);
  display: flex;
  flex-direction: column;
  gap: 10px;
  flex: 1;
}
.pp-blog .blog-card__body .blog-tag { margin-bottom: 2px; }
.pp-blog .blog-card__body h3 {
  font-size: 17px;
  line-height: 1.25;
  letter-spacing: -.015em;
  font-weight: 500;
  color: var(--pp-ink);
}
.pp-blog .blog-card__body p {
  font-size: 13px;
  line-height: 1.55;
  font-weight: 300;
  color: var(--pp-ink-72);
  flex: 1;
}
.pp-blog .blog-card__foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  margin-top: 4px;
}
.pp-blog .blog-card__foot .read-time { font-size: 12px; color: var(--pp-ink-56); }

/* Empty-listing state (plugin-rendered — a specific category with no
   posts, reachable via a direct/shared URL even once its own filter
   button is hidden per v0.3.2's category-visibility logic, or the whole
   Blog having zero published posts). Correction round: margin-top was
   clamp(32px,5vw,48px) — the same generous value used to open a whole
   new visual block elsewhere on this page — which read as a large,
   heavy empty section for what is meant to be a small, modest fallback
   line. Reduced to a lighter clamp; width/alignment already come from
   .mcb-blog-listing__body > * (this element's own .blog-grid parent),
   so no change was needed there. */
.pp-blog .blog-empty {
  margin-top: clamp(16px, 3vw, 24px);
  font-size: 14px;
  color: var(--pp-ink-56);
}

/* Load more */
.pp-blog .blog-more {
  margin-top: clamp(28px, 4vw, 40px);
  display: flex;
  justify-content: center;
}
.pp-blog .blog-more .btn {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  height: 38px;
  padding-inline: 19px;
  border-radius: var(--pp-r-pill);
  font-size: 15px;
  font-weight: 500;
  white-space: nowrap;
  cursor: pointer;
  transition: background var(--pp-t-med), color var(--pp-t-med), border-color var(--pp-t-med);
  background: transparent;
  color: var(--pp-ink);
  border: 1px solid var(--pp-ink);
}
.pp-blog .blog-more .btn:hover:not(:disabled) { background: var(--pp-surface); }
.pp-blog .blog-more .btn:focus-visible { outline: 2px solid var(--pp-accent-soft); outline-offset: 2px; }
.pp-blog .blog-more .btn:disabled { cursor: default; opacity: .6; }

@media (min-width: 900px) {
  .pp-blog .blog-featured { flex-direction: row; }
  .pp-blog .blog-featured__media { flex: 0 0 46%; aspect-ratio: auto; }
  .pp-blog .blog-featured__body { flex: 1; justify-content: center; }
  .pp-blog .blog-grid { grid-template-columns: repeat(3, 1fr); }
}


/* ==========================================================================
   06  NEWSLETTER
   ========================================================================== */

.pp-blog .blog-newsletter-section {
  padding: clamp(36px, 5vw, 56px) 0;
  background: var(--pp-tint);
  border-block: 1px solid var(--pp-tint-line-strong);
}
.pp-blog .blog-newsletter-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: clamp(20px, 3vw, 32px);
}
.pp-blog .blog-newsletter-row h2 {
  font-size: clamp(22px, 3vw, 28px);
  letter-spacing: -.02em;
  font-weight: 500;
}
.pp-blog .blog-newsletter-row p {
  margin-top: 6px !important;
  font-size: 13.5px;
  font-weight: 300;
  color: var(--pp-ink-72);
  max-width: 44ch;
}
/* Correction round (v2.0.3): switched from flex to grid for the input/
   button row. A second report described the Subscribe button being
   pulled left while the input shrank from its right edge on focus — not
   reproducible in this session's own Chrome (real click, keyboard Tab,
   typing, blur all measured 0px change: getBoundingClientRect() on
   .blog-newsletter-form input and .btn was byte-identical unfocused vs
   focused, and no :focus-within/global rule was found matching this
   form's ancestors). Since the exact trigger could not be reproduced
   here (possibly a caching or browser/extension difference on the
   reporting device), the flex implementation — whose input track relied
   on flex-grow/flex-shrink/flex-basis:auto content-based sizing, an
   inherently more ambiguous calculation than a fixed grid track — was
   replaced with an explicit two-column grid per this round's own
   stated preference. minmax(0,1fr) gives the input a track that can
   never be recalculated by its own content/state, and auto gives the
   button a track sized only by its own intrinsic content, fully
   decoupled from the input. This is strictly more deterministic than
   the flex version regardless of the exact original trigger. */
.pp-blog .blog-newsletter-form {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: center; /* Correction round (v2.0.2), user-approved deviation
    from source — see manifest.json notes. Source's own input/button are
    not vertically centered on each other (input 47px tall vs button's
    fixed 38px); confirmed via computed center-Y on staging (4.5px
    offset) and fixed by centering both on the same axis. Preserved
    here under the v2.0.3 grid layout. */
  column-gap: 10px;
  flex: 0 1 420px;
  min-width: 260px;
}
.pp-blog .blog-newsletter-form .visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}
.pp-blog .blog-newsletter-form input {
  /* Grid item in the minmax(0,1fr) track (v2.0.3) — min-width:0 lets it
     shrink below its content's intrinsic width instead of overflowing;
     the track itself, not this property, is what makes the width
     deterministic on focus. */
  min-width: 0;
  width: 100%;
  font: 400 14px "Poppins", sans-serif;
  color: var(--pp-ink);
  background: var(--pp-bone);
  border: 1px solid var(--pp-ink-hair);
  border-radius: var(--pp-r-xs);
  padding: 12px 14px;
  outline: none;
}
/* Correction round (v2.0.2): reproduced/investigated on staging (real
   mouse click, keyboard Tab, and programmatic focus, at the available
   Chrome viewport) — no pixel shift was actually measurable from the
   prior outline-based indicator; outline/outline-offset never affect
   box layout per spec. Kept as a hardening measure anyway, matching the
   requested principle of an inset, non-layout-affecting focus style
   (box-shadow instead of outline) so no future browser/theme quirk can
   reintroduce a shift: the input's own border-box size, and therefore
   the Subscribe button's position via flex, can never change on focus.
   Accessibility is preserved — the ring is still 2px, high-contrast,
   and shown only for :focus-visible (keyboard/programmatic focus). */
.pp-blog .blog-newsletter-form input:focus-visible {
  outline: none;
  box-shadow: inset 0 0 0 2px var(--pp-accent-soft);
}
.pp-blog .blog-newsletter-form input::placeholder { color: var(--pp-ink-56); }
.pp-blog .blog-newsletter-form .btn {
  /* Grid item in the auto track (v2.0.3) — sized only by its own
     intrinsic content, never by the input's state. justify-self:start
     stops it from stretching to fill the track (grid items default to
     stretch), keeping its width exactly its own content width, as
     before. */
  justify-self: start;
  width: max-content;
}


/* ==========================================================================
   07  DARK DEMO DAYS CTA
   ========================================================================== */

.pp-blog .dark-cta-section { background: var(--pp-ink); }
.pp-blog .dark-cta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 28px;
}
.pp-blog .dark-cta__copy { display: flex; flex-direction: column; gap: 12px; flex: 1 1 300px; min-width: 0; }

/* Correction round (v2.0.2): the CTA heading had no explicit font-size
   rule anywhere in this pack, so it fell back to the theme/Gutenberg
   default core/heading H2 style (confirmed via computed style on
   staging: 24px / weight 700) instead of source's own larger scale.
   Source (source-design/blog-original.zip, styles.css line 2750):
   `.dark-cta h2 { font-size: clamp(25px, 7vw, 42px); line-height: 1.16;
   letter-spacing: -.03em; font-weight: 500; color: var(--paper); }` —
   color is already set inline on the block (#FFFFFF), so only the type
   scale is added here. */
.pp-blog .dark-cta h2 {
  font-size: clamp(25px, 7vw, 42px);
  line-height: 1.16;
  letter-spacing: -.03em;
  font-weight: 500;
  /* Correction round (v2.0.5): the MyCabin Gutenberg Design System
     applies `text-wrap: balance` to headings sitewide (confirmed via
     computed style on staging), which redistributes this heading's
     text for visual evenness across lines rather than filling each
     line greedily — at mobile widths it wrapped "See these ideas" /
     "come to life." even though "come" measurably fits on line 1
     (confirmed: ~368px of text in a ~460px available box at a 500px
     viewport). Source (styles.css line 141) sets `text-wrap: pretty`
     on body — a widow-prevention heuristic, not full balancing —
     which produces source's actual wrap, "See these ideas come" /
     "to life.". Overridden here, scoped to this one heading only (not
     a global Design System change, out of this round's scope);
     verified live before committing. */
  text-wrap: pretty;
}
.pp-blog .dark-cta__btns {
  display: flex;
  flex-direction: column;
  /* Correction round (v2.0.5): WordPress core's own `.wp-block-buttons`
     default CSS sets `align-items: center` on this element (it also
     carries that core class) — confirmed via computed style on
     staging. Combined with flex-direction:column above, that made the
     BUTTON'S OWN WRAPPER (.wp-block-button, the actual flex item) size
     to its own content and center horizontally, instead of stretching
     to fill the row — so the existing width:100% on the inner <a>
     below was a no-op, since its containing block never grew past the
     button's own text width. Source's own mobile-first rule (
     styles.css line 2753-2754) is `.dark-cta__btns { ...; width: 100%; }
     .dark-cta__btns .btn { width: 100%; }` — full width by default.
     align-items:stretch overrides core's centering so the wrapper (and
     therefore the <a>) actually fills the row; verified live before
     committing (button wrapper measured 221px -> 557px at a fixed test
     width). Desktop (900px+) restores align-items:center below,
     matching this rule's own pre-existing, already-correct desktop
     behavior — the stretch fix is mobile-scoped only. */
  align-items: stretch;
  gap: 10px;
  width: 100%;
}
.pp-blog .dark-cta__btns .wp-block-button__link { width: 100%; }

/* Correction round (v2.0.4): the button previously had its ink-on-bone
   colors hardcoded as inline style + a core/button "style":{"color":...}
   JSON attribute, which meant a :hover rule could never win over it
   without !important, and no hover was implemented at all — confirmed
   on staging (no transition on pointer-enter). Source
   (blog-original.zip, blog.html line 347) marks this exact button
   `class="btn btn--light btn--48"`; source CSS (styles.css line
   337-338): `.btn--light { background: var(--bone); color: var(--ink); }
   .btn--light:hover { background: var(--accent-soft); color: var(--ink); }`
   — background-only change, text color unchanged, no transform, no
   border, no icon movement (this button has no arrow glyph). The
   button's own inline style/JSON color attribute was removed from
   content.html so these rules can apply cleanly; base background/color
   and the existing shared `.btn, .wp-block-button__link` transition
   (background var(--pp-t-med), color var(--pp-t-med) — .25s, matching
   source's --t-med) now drives the same smooth cross-fade source uses. */
.pp-blog .dark-cta__btns .wp-block-button__link {
  background: var(--pp-bone);
  color: var(--pp-ink);
}
.pp-blog .dark-cta__btns .wp-block-button__link:hover {
  background: var(--pp-accent-soft);
  color: var(--pp-ink);
}
/* No custom :focus-visible existed for this button before (it relied on
   Gutenberg/browser defaults). Source's own global :focus-visible rule
   (styles.css line 166-170) is `outline: 2px solid var(--accent);
   outline-offset: 3px;` — reproduced here, scoped to this button, rather
   than added globally to .pp-blog (out of scope for this round). Border-
   radius is intentionally left off: Chrome already shapes an outline to
   match the element's own border-radius (this button is a full pill),
   so a fixed 4px radius here would fight that and look wrong on a pill
   shape — source's global rule is a fallback for elements with no
   border-radius of their own, not a literal value to copy onto every
   focusable element regardless of shape. */
.pp-blog .dark-cta__btns .wp-block-button__link:focus-visible {
  outline: 2px solid var(--pp-accent);
  outline-offset: 3px;
}

/* Shared button base (btn / btn--ink / btn--light / btn--48) — reused by
   the newsletter Subscribe button (a plain <button class="btn btn--ink">,
   core/html) and the dark CTA's native core/button link. */
.pp-blog .btn,
.pp-blog .wp-block-button__link {
  height: 38px;
  padding: 0 19px;
  border-radius: var(--pp-r-pill);
  font: 500 15px "Poppins", sans-serif;
  white-space: nowrap;
  cursor: pointer;
  border: 0;
  transition: background var(--pp-t-med), color var(--pp-t-med);
}
.pp-blog .btn--ink { background: var(--pp-ink); color: var(--pp-bone); }
.pp-blog .btn--ink:hover { background: var(--pp-accent); color: var(--pp-bone); }

@media (min-width: 900px) {
  .pp-blog .dark-cta__btns {
    width: auto;
    /* Restores core's own default (center) at desktop, since the base
       rule now explicitly sets align-items:stretch for the mobile
       full-width fix above — desktop's already-correct, previously
       accepted layout (button right-aligned, sized to its own content,
       not stretched) is unchanged. */
    align-items: center;
  }
  .pp-blog .dark-cta__btns .wp-block-button__link { width: auto; }
}


/* Scroll reveal — fade up once, the first time a block is scrolled into
   view. Toggled by the IntersectionObserver in frontend.js; .pp-in-view is
   the only state it ever adds, never removed, so a reveal never re-triggers.
   editor.css settles this to its final visible state inside Gutenberg,
   since frontend.js never runs there. */
.pp-blog .pp-io-fade {
  opacity: 0;
  transform: translateY(24px);
  transition: opacity .8s var(--pp-ease), transform .8s var(--pp-ease);
}
.pp-blog .pp-io-fade.pp-in-view { opacity: 1; transform: none; }


/* ==========================================================================
   08  REDUCED MOTION
   ========================================================================== */

@media (prefers-reduced-motion: reduce) {
  .pp-blog .pp-io-fade { transition: none !important; }
}
