/* ═══════════════════════════════════════════════════════════════════════════
   Plastic blog — reusable long-form article template.
   Static, no build step, no dependencies, light theme only.

   SCOPING: every rule below is nested under `.blog`. Nothing here touches a
   bare element or a site class, so this file can be linked next to
   the fingerprinted home stylesheet (see uikit.html) without restyling anything outside a
   `.blog` wrapper. Tokens are declared on `.blog`, not on :root, for the same
   reason — a :root redeclaration of --bg / --ink would leak.

   TYPOGRAPHY: Archivo only, self-hosted. The @font-face blocks below are
   copied verbatim from the fingerprinted home stylesheet (same two woff2
   files, same unicode-range values), so this page renders standalone without
   loading the home stylesheet. No Google Fonts, no second family.

   ── DERIVED TYPE VALUES ──────────────────────────────────────────────────
   The home contract scale was built for a hero, not for a 720px reading
   measure. Sizes below are derived from it; WEIGHTS ARE UNCHANGED from the
   contract in every case. What moved, and why:

   --blog-body-size   19px  (contract --body1-size tops out at 17px)
        17px over a 720px measure is ~83 characters per line of very small
        type. 19px brings the measure to ~74ch, inside the 65–75ch band, and
        is the size the reference layout was measured at. Line-height drops
        1.5 → 1.45 and letter-spacing goes to -0.018em because both the
        contract 1.5 and Archivo's default tracking are tuned for 15–17px;
        at 19px they read loose. Weight 400, unchanged.

   --blog-h1-size     46px  (contract --h1-size tops out at 60px)
        60px is a landing-hero size. 46px is the article-title size from the
        reference — and it is not a new number: it is exactly the contract's
        own --h2-size ceiling, so the article title lands on a step the scale
        already contains. Weight 600 (contract H1 weight, unchanged).
        Line-height 1.0 → 1.1 because an article title wraps to two or three
        lines where a hero H1 wraps to one, and 1.0 collides descenders with
        the next line's caps. Letter-spacing -0.022em.

   --blog-h2-size     27px  (contract --h2-size tops out at 46px)
        46px inside an article is a section poster, not a section heading —
        it would out-shout the 46px H1. 27px sits between the contract's H3
        ceiling (24px) and its H2, keeping the article's own hierarchy
        readable. Weight 500 (contract H2 weight, unchanged). Line-height
        1.0 → 1.2, the contract's own H3 value, for the same wrapping reason
        as H1.

   --blog-h3-size     22px  (contract --h3-size is 21–24px)
        Inside the contract's existing H3 range, nudged to 22px so the steps
        distribute evenly against the larger body (22/19 = 1.16, 27/22 =
        1.23). Weight 600 (contract H3 weight, unchanged), line-height 1.3.
        Note H3 is heavier than H2 (600 vs 500) — that is the contract's own
        relationship, preserved deliberately, not an error.

   --blog-lead-size   21px  standfirst. New step; no contract equivalent
        exists between body and H3. Weight 400, colour --muted.

   --blog-small-size  15px  captions, meta, footnotes, table cells.
        Not a new value — it is the contract's --body1-size floor, reused as
        the article's secondary size. (Contract --body2-size is 14px, which
        is too small next to 19px body.)

   Everything fluid via clamp() so 375px gets the small end and ~1100px the
   measured value. .label is used verbatim from the contract: 12px, uppercase,
   0.04em — once, on the article eyebrow, not above every section.
   ═══════════════════════════════════════════════════════════════════════ */

/* Self-hosted Archivo (variable, wght 400-700). Copied verbatim from the
   fingerprinted home stylesheet — same two files, same unicode-range values. */
@font-face {
  font-family: "Archivo";
  src: url("/assets/fonts/archivo-latin.woff2") format("woff2");
  font-weight: 400 700;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Archivo";
  src: url("/assets/fonts/archivo-latin-ext.woff2") format("woff2");
  font-weight: 400 700;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/* ── Tokens ──────────────────────────────────────────────────────────────
   Colour tokens are the contract values, verbatim. Declared on .blog rather
   than :root so this file cannot restyle a page it is merely linked into. */
.blog {
  --bg: #f5f5f5;
  --ink: #090d0e;
  --accent: #ce3300;
  --muted: #4d4d4d;
  --dark-bg: #090d0e;
  --on-dark: #f5f5f5;
  --blog-link-on-dark: #ff875f; /* 8.26:1 on --dark-bg, WCAG AAA */
  --blog-link-on-dark-hover: #ffad8a;

  --font: "Archivo", ui-sans-serif, system-ui, -apple-system, "Helvetica Neue", sans-serif;

  /* Derived article scale — see the header block for the reasoning. */
  --blog-h1-size: clamp(32px, 1.9vw + 25px, 46px);
  --blog-h2-size: clamp(23px, 0.55vw + 21px, 27px);
  --blog-h3-size: clamp(20px, 0.28vw + 19px, 22px);
  --blog-lead-size: clamp(19px, 0.28vw + 18px, 21px);
  --blog-body-size: clamp(17px, 0.28vw + 16px, 19px);
  --blog-small-size: 15px;

  /* Layout */
  --blog-measure: 720px;   /* the reading column */
  --blog-canvas: 1120px;   /* the outer canvas hero images break out to */
  --blog-gutter: clamp(20px, 5vw, 40px);

  /* Derived surfaces. Mixed from the colour tokens above so nothing here is a
     hard-coded hex — the palette stays single-sourced. */
  --blog-rule: color-mix(in srgb, var(--ink) 14%, transparent);
  --blog-rule-soft: color-mix(in srgb, var(--ink) 9%, transparent);
  --blog-surface: color-mix(in srgb, var(--ink) 4%, var(--bg));
  --blog-surface-deep: color-mix(in srgb, var(--ink) 7%, var(--bg));

  /* Semantic z-index scale. Nothing here stacks yet beyond the placeholder
     tag over its field, but the scale exists so future additions to the
     template don't reach for 999. */
  --z-base: 0;
  --z-raised: 10;
  --z-sticky: 20;

  --ease-out-quart: cubic-bezier(0.25, 1, 0.5, 1);

  background: var(--bg);
  color: var(--ink);
  font-family: var(--font);
  font-size: var(--blog-body-size);
  font-weight: 400;
  line-height: 1.45;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  /* clip, not hidden: `overflow-x: hidden` would compute overflow-y to auto
     and make this a scroll container. Same reasoning as the home stylesheet. */
  overflow-x: clip;
}

/* The reset is wrapped in :where() so it carries ZERO specificity. A plain
   `.blog p { margin: 0 }` scores (0,1,1) and silently beats every single-class
   component rule below it — .blog-lead, .blog-meta, .blog-quote, .blog-figure
   and .blog-pre all lost their margins to it before this was zeroed.
   :where() changes specificity only, never matching, so the .blog scope holds. */
:where(.blog) *,
:where(.blog) *::before,
:where(.blog) *::after { box-sizing: border-box; }

:where(.blog) :where(h1, h2, h3, h4, h5, h6, p, ul, ol, figure,
                     blockquote, pre, table, dl, dd) { margin: 0; padding: 0; }

:where(.blog) img { display: block; max-width: 100%; height: auto; }
.blog ::selection { background: var(--accent); color: #fff; }

/* Focus is visible everywhere, on the accent, with an offset so it never
   sits on top of the thing it is marking. */
.blog :focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
  border-radius: 1px;
}

.blog-visually-hidden {
  position: absolute; width: 1px; height: 1px; margin: -1px;
  clip-path: inset(50%); overflow: hidden; white-space: nowrap;
}

/* Skip link: off-screen until focused, then a real visible control. */
.blog-skip {
  position: absolute;
  left: 12px;
  top: -60px;
  z-index: var(--z-sticky);
  padding: 10px 16px;
  background: var(--dark-bg);
  color: var(--on-dark);
  text-decoration: none;
  transition: top 200ms var(--ease-out-quart);
}
.blog-skip:focus-visible { top: 12px; }

/* Contract utility, verbatim: 12px uppercase 0.04em. */
.blog .label {
  font-size: 12px;
  font-weight: 400;
  line-height: 1.4;
  text-transform: uppercase;
  letter-spacing: 0.04em;
}

/* ── Shell ───────────────────────────────────────────────────────────────
   One padded wrapper caps the canvas; children opt into the reading measure.
   The gutter stays outside the 1120 canvas, so at 1280 the hero is 1120 wide
   with room to spare and the page never scrolls sideways. */
.blog-shell {
  width: 100%;
  max-width: calc(var(--blog-canvas) + var(--blog-gutter) * 2);
  margin-inline: auto;
  padding-inline: var(--blog-gutter);
}

/* Every direct child of the article sits at the reading measure unless it
   explicitly breaks out. */
.blog-article > * {
  width: 100%;
  max-width: var(--blog-measure);
  margin-inline: auto;
}
.blog-breakout { max-width: var(--blog-canvas) !important; }

/* ── Masthead ────────────────────────────────────────────────────────── */
.blog-masthead {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 20px;
  flex-wrap: wrap;
  padding-block: 32px 0;
}
.blog-wordmark {
  display: inline-flex;
  flex-direction: column;
  gap: 1px;
  color: inherit;
  text-decoration: none;
}
.blog-wordmark-name {
  font-size: 18px;
  font-weight: 700;
  line-height: 1.1;
  text-transform: uppercase;
  letter-spacing: 0;
  opacity: 0.8;
}
.blog-wordmark-tag { color: var(--accent); font-size: 11px; line-height: 1.2; }
.blog-masthead-link {
  color: var(--muted);
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 3px;
  transition: color 180ms var(--ease-out-quart);
}
.blog-masthead-link:hover { color: var(--accent); }

/* ── Article header ──────────────────────────────────────────────────── */
.blog-header { padding-block: clamp(48px, 7vw, 88px) 0; }

.blog-eyebrow {
  color: var(--accent);
  margin-bottom: 20px;
}

.blog-title {
  font-size: var(--blog-h1-size);
  font-weight: 600;              /* contract H1 weight */
  line-height: 1.1;
  letter-spacing: -0.022em;
  text-wrap: balance;
}

.blog-lead {
  margin-top: 24px;
  font-size: var(--blog-lead-size);
  font-weight: 400;
  line-height: 1.45;
  letter-spacing: -0.014em;
  color: var(--muted);           /* 7.7:1 on --bg */
  text-wrap: pretty;
}

.blog-meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 10px 14px;
  margin-top: 28px;
  padding-top: 20px;
  border-top: 1px solid var(--blog-rule);
  color: var(--muted);
}
.blog-meta-sep { color: var(--blog-rule); }

/* ── Body rhythm ─────────────────────────────────────────────────────── */
.blog-body { padding-bottom: clamp(56px, 7vw, 88px); }

.blog-body p {
  font-size: var(--blog-body-size);
  line-height: 1.45;
  letter-spacing: -0.018em;
  text-wrap: pretty;
}
.blog-body p + p { margin-top: 22px; }

.blog-body h2 {
  font-size: var(--blog-h2-size);
  font-weight: 500;              /* contract H2 weight */
  line-height: 1.2;
  letter-spacing: -0.018em;
  margin-top: 56px;
  margin-bottom: 18px;
  text-wrap: balance;
}
.blog-body h3 {
  font-size: var(--blog-h3-size);
  font-weight: 600;              /* contract H3 weight */
  line-height: 1.3;
  letter-spacing: -0.012em;
  margin-top: 36px;
  margin-bottom: 12px;
  text-wrap: balance;
}
/* A heading straight after a breakout figure has already had its air. */
.blog-body > figure + h2 { margin-top: 44px; }

.blog-body strong { font-weight: 600; }
.blog-body em { font-style: italic; }

/* ── Links ───────────────────────────────────────────────────────────── */
/* Muted colour + a visible underline. The underline is the non-colour cue,
   so the link is identifiable without relying on hue. --muted is 7.7:1 on
   --bg, well past the 4.5:1 floor. */
.blog-body a,
.blog-link {
  color: var(--muted);
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 3px;
  /* Full-strength --muted, not a tint of it. The underline is the only cue
     that does not depend on colour perception, so it has to actually read:
     at 45% opacity it measured 2.16:1 against --bg, which is not "visible". */
  text-decoration-color: var(--muted);
  transition: color 180ms var(--ease-out-quart),
              text-decoration-color 180ms var(--ease-out-quart);
}
.blog-body a:hover,
.blog-link:hover {
  color: var(--accent);
  text-decoration-color: var(--accent);
}

/* Standalone link — sits on its own line as a destination, not inside prose. */
.blog-link-standalone {
  display: inline-flex;
  align-items: baseline;
  gap: 8px;
  margin-top: 24px;
  font-size: var(--blog-body-size);
  letter-spacing: -0.018em;
  color: var(--ink);
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 4px;
  text-decoration-color: var(--blog-rule);
  transition: color 180ms var(--ease-out-quart),
              text-decoration-color 180ms var(--ease-out-quart);
}
.blog-link-standalone::after {
  content: "→";
  text-decoration: none;
  transition: transform 260ms var(--ease-out-quart);
}
.blog-link-standalone:hover {
  color: var(--accent);
  text-decoration-color: var(--accent);
}
.blog-link-standalone:hover::after { transform: translateX(3px); }

/* ── Lists ───────────────────────────────────────────────────────────── */
.blog-body ul,
.blog-body ol {
  /* Bottom margin matches the p+p rhythm: the vertical rhythm is driven by
     adjacent-sibling rules, so a list with no bottom margin collides with the
     paragraph after it. Adjacent margins collapse, so this never doubles up
     against an h2's 56px or a figure's 40px. */
  margin-top: 20px;
  margin-bottom: 22px;
  padding-left: 0;
  list-style: none;
  font-size: var(--blog-body-size);
  letter-spacing: -0.018em;
}
.blog-body li { position: relative; padding-left: 30px; line-height: 1.45; }
.blog-body li + li { margin-top: 12px; }

.blog-body ul > li::before {
  content: "";
  position: absolute;
  left: 4px;
  top: 0.6em;
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--accent);
}
.blog-body ol { counter-reset: blog-ol; }
.blog-body ol > li { counter-increment: blog-ol; }
.blog-body ol > li::before {
  content: counter(blog-ol) ".";
  position: absolute;
  left: 0;
  top: 0;
  width: 24px;
  font-variant-numeric: tabular-nums;
  font-weight: 600;
  color: var(--accent);
}
/* Nested paragraphs inside a list item keep the item's rhythm. */
.blog-body li > p + p { margin-top: 10px; }

/* ── Pull quote ──────────────────────────────────────────────────────── */
/* No side stripe. The quote is set larger and lighter than body, bounded by
   full-width hairlines above and below — the rhythm break does the work. */
.blog-quote {
  margin-block: 44px;
  padding-block: 30px;
  border-top: 1px solid var(--blog-rule);
  border-bottom: 1px solid var(--blog-rule);
}
.blog-quote p {
  font-size: clamp(21px, 0.7vw + 18px, 26px) !important;
  font-weight: 500;
  line-height: 1.3 !important;
  letter-spacing: -0.02em !important;
  text-wrap: balance;
}
.blog-quote cite {
  display: block;
  margin-top: 14px;
  font-size: var(--blog-small-size);
  font-style: normal;
  color: var(--muted);
}

/* ── Callout / note ──────────────────────────────────────────────────── */
/* Background tint plus a full hairline border. No side stripe. */
.blog-note {
  margin-block: 32px;
  padding: 22px 24px;
  background: var(--blog-surface);
  border: 1px solid var(--blog-rule-soft);
}
.blog-note-title {
  display: block;
  margin-bottom: 8px;
  color: var(--accent);
}
.blog-note p {
  font-size: var(--blog-small-size) !important;
  line-height: 1.5 !important;
  letter-spacing: -0.01em !important;
}

/* ── Table ───────────────────────────────────────────────────────────── */
/* The wrapper scrolls, not the page. It is focusable (tabindex="0" in the
   markup) so a keyboard user can reach the overflow without a pointer. */
.blog-tablewrap {
  margin-block: 32px;
  overflow-x: auto;
  border: 1px solid var(--blog-rule-soft);
  overscroll-behavior-x: contain;
}
.blog-tablewrap:focus-visible { outline-offset: 0; }

.blog-table {
  width: 100%;
  min-width: 560px;   /* forces the wrapper to scroll below ~600px */
  border-collapse: collapse;
  font-size: var(--blog-small-size);
  line-height: 1.45;
  text-align: left;
}
.blog-table caption {
  padding: 16px 20px 0;
  font-size: var(--blog-small-size);
  color: var(--muted);
  text-align: left;
}
.blog-table th,
.blog-table td {
  padding: 14px 20px;
  vertical-align: top;
  border-bottom: 1px solid var(--blog-rule-soft);
}
.blog-table thead th {
  font-weight: 600;
  font-size: 12px;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--muted);
  background: var(--blog-surface);
  border-bottom-color: var(--blog-rule);
}
.blog-table tbody th { font-weight: 600; color: var(--ink); }
.blog-table tbody tr:last-child th,
.blog-table tbody tr:last-child td { border-bottom: 0; }

/* ── Figures & image placeholders ────────────────────────────────────── */
.blog-figure { margin-block: 40px; }
.blog-figure figcaption,
.blog-figcaption {
  margin-top: 12px;
  font-size: var(--blog-small-size);
  line-height: 1.45;
  color: var(--muted);
  text-wrap: pretty;
}

.blog-image {
  display: block;
  width: 100%;
  height: auto;
  border: 1px solid var(--blog-rule-soft);
  background: var(--blog-surface);
}

.blog-video {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 16 / 9;
  border: 1px solid var(--blog-rule-soft);
  background: var(--dark-bg);
}

/* Square corners, per the reference. */
.blog-ph {
  position: relative;
  width: 100%;
  aspect-ratio: var(--ph-ratio, 3 / 2);
  border-radius: 0;
  border: 1px solid var(--blog-rule-soft);
  background:
    /* Run-out grooves: concentric hairlines around the label position.
       Period is 16px, not the 6px first tried — at 6px the ring spacing beat
       against the pixel grid across a 1120px hero and the field read as moiré
       noise rather than grooves. 16px stays legible at every width. */
    repeating-radial-gradient(
      circle at var(--ph-cx, 50%) var(--ph-cy, 50%),
      transparent 0 14.5px,
      var(--blog-rule-soft) 14.5px 16px
    ),
    var(--blog-surface-deep);
  overflow: hidden;
}
/* The hero carries the label disc; the in-body placeholders are grooves only,
   so five placeholders on one page don't read as five identical tiles. */
.blog-ph--label::before {
  content: "";
  position: absolute;
  left: var(--ph-cx, 50%);
  top: var(--ph-cy, 50%);
  width: clamp(76px, 11vw, 132px);
  aspect-ratio: 1;
  transform: translate(-50%, -50%);
  border-radius: 50%;
  background: var(--accent);
}
/* Flat-lit variant: no groove layer at all. Used for the "flat-lit and
   unreadable" half of the 2-up pair, so the two placeholders show the
   difference their captions describe instead of being identical tiles. */
.blog-ph--flat { background: var(--blog-surface-deep); }

.blog-ph-tag {
  position: absolute;
  z-index: var(--z-raised);
  left: 12px;
  bottom: 12px;
  padding: 5px 9px;
  background: var(--bg);
  border: 1px solid var(--blog-rule-soft);
  color: var(--muted);
}

/* The phone screenshot is portrait — cap it so it doesn't run 1500px tall
   inside a 720px measure. */
.blog-figure--device .blog-ph {
  max-width: 320px;
  margin-inline: auto;
  --ph-ratio: 9 / 19.5;
}
.blog-figure--device .blog-image {
  max-width: 420px;
  margin-inline: auto;
}

/* 2-up figure grid. auto-fit keeps it breakpoint-free: two columns while both
   fit at 300px, one column below that. */
.blog-grid-2 {
  margin-block: 40px;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: 24px;
}
.blog-grid-2 figure { margin: 0; }
.blog-grid-2 .blog-ph { --ph-ratio: 1 / 1; }

/* ── Code ────────────────────────────────────────────────────────────── */
/* Archivo has no monospace cut, and the contract forbids a second web font.
   Inline and block code therefore use the system mono stack — a local UI
   font, not a downloaded family, so the one-web-font rule holds. Character
   alignment is the whole point of a code block; faking it in a proportional
   face would be worse. */
.blog-body code,
.blog-code {
  font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
  font-size: 0.86em;
  letter-spacing: 0;
}
.blog-body :not(pre) > code {
  padding: 2px 6px;
  background: var(--blog-surface-deep);
  border: 1px solid var(--blog-rule-soft);
  white-space: nowrap;
}
.blog-pre {
  margin-block: 32px;
  padding: 20px 22px;
  background: var(--dark-bg);
  color: var(--on-dark);
  border: 1px solid var(--dark-bg);
  overflow-x: auto;
  overscroll-behavior-x: contain;
}
.blog-pre code {
  display: block;
  font-size: 14px;
  line-height: 1.7;
  color: inherit;
  background: none;
  border: 0;
  padding: 0;
  white-space: pre;
}
.blog-pre .blog-code-note { color: color-mix(in srgb, var(--on-dark) 55%, transparent); }

/* ── Horizontal rule ─────────────────────────────────────────────────── */
.blog-hr {
  height: 1px;
  margin-block: 52px;
  border: 0;
  background: var(--blog-rule);
}

/* ── Footnotes ───────────────────────────────────────────────────────── */
.blog-footnotes {
  margin-top: 52px;
  padding-top: 28px;
  border-top: 1px solid var(--blog-rule);
}
.blog-footnotes h2 {
  font-size: var(--blog-h3-size) !important;
  font-weight: 600 !important;
  margin-top: 0 !important;
  margin-bottom: 18px !important;
}
.blog-footnotes ol { margin-top: 0; font-size: var(--blog-small-size); }
.blog-footnotes li { padding-left: 30px; line-height: 1.5; color: var(--muted); }
.blog-footnotes li + li { margin-top: 14px; }
.blog-footnotes li strong { color: var(--ink); }
/* Landing target gets a moment of accent so the jump is legible. */
.blog-footnotes li:target { color: var(--ink); }
.blog-footnotes li:target::before { color: var(--accent); }

/* The marker sits inside a semantic <sup>, which already supplies the raise
   and the size — this only handles colour and kills the prose underline. */
.blog-fnref {
  font-weight: 600;
  text-decoration: none !important;
  color: var(--accent) !important;
}
.blog-fnref:hover { color: var(--ink) !important; }
.blog-fnback {
  margin-left: 6px;
  text-decoration: none;
  color: var(--accent);
}

/* ── End CTA ─────────────────────────────────────────────────────────── */
.blog-cta {
  margin-top: 56px;
  padding: clamp(32px, 4vw, 44px);
  background: var(--dark-bg);
  color: var(--on-dark);
}
.blog-cta p {
  font-size: clamp(21px, 0.7vw + 18px, 26px) !important;
  font-weight: 500;
  /* Light type on dark reads lighter; +0.05 line-height per the register. */
  line-height: 1.35 !important;
  letter-spacing: -0.02em !important;
  max-width: 22ch;
  text-wrap: balance;
}
.blog-cta--stores {
  display: flex;
  align-items: center;
  gap: clamp(20px, 4vw, 36px);
}
.blog-cta--stores .blog-cta-icon {
  flex: 0 0 auto;
  width: clamp(72px, 12vw, 104px);
  height: auto;
  border-radius: 22%;
}
.blog-cta--stores p { max-width: 24ch; }
.blog-cta--stores a {
  color: var(--blog-link-on-dark);
  text-decoration-color: currentColor;
  text-decoration-thickness: 1px;
  text-underline-offset: 4px;
}
.blog-cta--stores a:hover { color: var(--blog-link-on-dark-hover); }
/* Award credit sits under the CTA line: quieter and wider than .blog-cta p. */
.blog-cta p.blog-cta-award {
  font-size: var(--blog-small-size) !important;
  font-weight: 400;
  line-height: 1.5 !important;
  letter-spacing: 0 !important;
  max-width: 46ch;
  margin-top: 18px;
  opacity: 0.72;
}
.blog-cta-badge {
  display: inline-block;
  margin-top: 26px;
  transition: opacity 180ms var(--ease-out-quart);
}
.blog-cta-badge:hover { opacity: 0.8; }
.blog-cta-badge img { width: 168px; height: auto; }
.blog-cta-badges {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  margin-top: 26px;
}
.blog-cta-badges .blog-cta-badge { margin-top: 0; }

/* ── Footer ──────────────────────────────────────────────────────────── */
.blog-footer {
  display: flex;
  flex-wrap: wrap;
  gap: 10px 20px;
  justify-content: space-between;
  padding-block: 32px 48px;
  margin-top: 40px;
  border-top: 1px solid var(--blog-rule);
  color: var(--muted);
}

/* ── Reduced motion ──────────────────────────────────────────────────── */
/* The template has no entrance or scroll motion by design — long-form reading
   is the job. What motion exists is hover feedback on links and the CTA;
   under reduce, the colour still changes, the movement does not. */
@media (prefers-reduced-motion: reduce) {
  .blog *,
  .blog *::before,
  .blog *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
  .blog .blog-link-standalone:hover::after { transform: none; }
}

/* ── Print ───────────────────────────────────────────────────────────── */
@media print {
  .blog-masthead, .blog-cta, .blog-footer { display: none; }
  .blog-article > * { max-width: 100%; }
}

/* ── Index grid ──────────────────────────────────────────────────────────
   Appended for /blog/. Every selector below is scoped under .blog like the
   rest of the file, so the UI kit page and the home stylesheet stay untouched.
   Nothing above this line was modified. */

/* The index header reuses .blog-header for its top padding; it only needs the
   reading measure, which .blog-article normally supplies and there is no
   article element here. */
.blog .blog-index-header {
  max-width: var(--blog-measure);
  margin-bottom: clamp(36px, 5vw, 56px);
}

/* 1 column below 640, 2 from 640, 3 from 1024. Explicit breakpoints rather
   than auto-fit because the column count is part of the brief. */
.blog .blog-cards {
  display: grid;
  grid-template-columns: 1fr;
  gap: clamp(20px, 2.4vw, 32px);
  margin: 0;
  padding: 0;
  list-style: none;
}
@media (min-width: 640px) {
  .blog .blog-cards { grid-template-columns: repeat(2, 1fr); }
}
@media (min-width: 1024px) {
  .blog .blog-cards { grid-template-columns: repeat(3, 1fr); }
}

/* The whole card is the link, so there is no nested interactive element and
   nothing to tab past. Stretch + flex column is what makes the row equal
   height with the reading time pinned to the bottom edge. */
.blog .blog-cards > li { display: flex; }
.blog .blog-card {
  display: flex;
  flex-direction: column;
  width: 100%;
  background: var(--bg);
  border: 1px solid var(--blog-rule-soft);
  color: var(--ink);
  text-decoration: none;
  transition: border-color 180ms var(--ease-out-quart),
              background-color 180ms var(--ease-out-quart);
}
.blog .blog-card:hover { border-color: var(--blog-rule); background: var(--blog-surface); }
/* Explicit, not inherited: the card must never lose its ring to a later reset. */
.blog .blog-card:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
}

/* Same drawing recipe as .blog-ph: concentric groove hairlines over the deep
   surface. --ring and the centre come off the card index in the markup, so
   nine tiles read as a texture rather than one tile repeated nine times. */
.blog .blog-card-thumb {
  position: relative;
  display: block;
  width: 100%;
  aspect-ratio: 16 / 9;
  border-bottom: 1px solid var(--blog-rule-soft);
  overflow: hidden;
  background:
    repeating-radial-gradient(
      circle at var(--ph-cx, 50%) var(--ph-cy, 50%),
      transparent 0 calc(var(--ring, 16px) - 1.5px),
      var(--blog-rule-soft) calc(var(--ring, 16px) - 1.5px) var(--ring, 16px)
    ),
    var(--blog-surface-deep);
}
/* The spindle dot. Small on purpose: texture, not illustration. */
.blog .blog-card-thumb::after {
  content: "";
  position: absolute;
  left: var(--ph-cx, 50%);
  top: var(--ph-cy, 50%);
  width: 14px;
  aspect-ratio: 1;
  transform: translate(-50%, -50%);
  border-radius: 50%;
  background: var(--accent);
}
.blog .blog-card-thumb--image {
  border-bottom: 0;
  background: #fff;
}
.blog .blog-card-thumb--image::after { content: none; }
.blog .blog-card-thumb--image img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.blog .blog-card-text {
  display: flex;
  flex: 1;
  flex-direction: column;
  padding: clamp(20px, 2vw, 26px);
}

.blog .blog-card-eyebrow {
  display: block;
  margin-bottom: 12px;
  color: var(--accent);        /* 4.7:1 on --bg */
}

/* No clamp and no ellipsis here by instruction: a card title always shows in
   full, and the flex column absorbs the height difference. */
.blog .blog-card-title {
  display: block;
  font-size: clamp(19px, 0.28vw + 18px, 21px);
  font-weight: 600;
  line-height: 1.25;
  letter-spacing: -0.014em;
  text-wrap: balance;
}

.blog .blog-card:hover .blog-card-title { color: var(--accent); }

/* The excerpt is the part allowed to clamp, at three lines. */
.blog .blog-card-excerpt {
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 3;
  line-clamp: 3;
  overflow: hidden;
  margin-top: 12px;
  font-size: var(--blog-small-size);
  line-height: 1.5;
  letter-spacing: -0.01em;
  color: var(--muted);         /* 7.7:1 on --bg */
}

/* margin-top: auto is what pins the reading time to the bottom of every card
   regardless of how many lines the title above it took. */
.blog .blog-card-meta {
  display: block;
  margin-top: auto;
  padding-top: 20px;
  font-size: var(--blog-small-size);
  color: var(--muted);
}

/* The index CTA sits on the canvas rather than inside a reading measure,
   because there is no article column on this page to align to. */
.blog .blog-index-cta { margin-bottom: 8px; }

/* Shared consent prompt for the blog analytics helper. */
.blog .blog-cookie-banner {
  position: fixed;
  inset: auto 16px 16px;
  z-index: 1000;
  display: flex;
  justify-content: center;
  pointer-events: none;
}
.blog .blog-cookie-card {
  display: flex;
  width: min(620px, 100%);
  align-items: center;
  justify-content: space-between;
  gap: 20px;
  padding: 18px 20px;
  border: 1px solid #383838;
  border-radius: 14px;
  background: var(--dark-bg);
  color: var(--on-dark);
  box-shadow: 0 16px 48px rgb(0 0 0 / 24%);
  pointer-events: auto;
}
.blog .blog-cookie-card p {
  margin: 0;
  font-size: 14px;
  line-height: 1.45;
}
.blog .blog-cookie-card a {
  color: var(--blog-link-on-dark);
  text-underline-offset: 3px;
}
.blog .blog-cookie-actions {
  display: flex;
  flex: 0 0 auto;
  align-items: center;
  gap: 8px;
}
.blog .blog-cookie-actions button {
  min-height: 44px;
  padding: 0 16px;
  border: 0;
  border-radius: 999px;
  font: inherit;
  font-size: 14px;
  font-weight: 600;
  cursor: pointer;
}
.blog .blog-cookie-accept {
  background: #ff5a1f;
  color: #111;
}
.blog .blog-cookie-deny {
  background: transparent;
  color: #d6d6d6;
}
.blog .blog-cookie-actions button:focus-visible {
  outline: 3px solid #fff;
  outline-offset: 2px;
}
@media (max-width: 560px) {
  .blog .blog-cookie-card { align-items: stretch; flex-direction: column; }
  .blog .blog-cookie-actions { justify-content: flex-end; }
}
