@layer bx-modules {
/* blog-cards@1.2.1 */
/* Lifted from src/styles/tool.css:410-427 (the pill grid, shared with `.sec-related` and split here),
   :415 (the wide row) and :475-499 (cover cards and Group-C article cards).

   Two lift shapes, and the difference matters. A source selector that already starts at the section
   (`.sec-blogcards > .blogcards-grid`) has that class REPLACED by `.m-blog-cards`, so the tail is
   unchanged. A source selector that starts BELOW the section (`.blogcards-grid > .card`, written at top
   level in tool.css because `.sec-related` shares it) is prefixed as a DESCENDANT —
   `.m-blog-cards .blogcards-grid > .card` — never as `.m-blog-cards > .blogcards-grid > .card`. The
   descendant form keeps the source's own combinators byte-for-byte, so the rule matches the same elements
   at the same depths; the child form invents a combinator the source never had and would stop matching a
   card the markup ever nests one level deeper. seed-parity.test.ts pins the tail against src/styles.

   Left in site CSS: `main[data-kind] .sec-cta + .sec-blogcards` and the phone `main[data-kind]
   .sec-blogcards > h2` rule (tool.css:436, 440-442) — both carry the `main[data-kind]` prefix module CSS
   may not use, and the first is positional (R9 turns it into a layout attribute). */
.m-blog-cards { background: var(--c-bg); }
.m-blog-cards > .sec-sub { font-size: 20px; line-height: 1.3; font-weight: 500; color: var(--c-text-3); margin-top: -12px; }
.m-blog-cards > .blogcards-grid { display: flex; flex-wrap: wrap; justify-content: center; gap: 16px; }
/* production keeps the three blog links on one row even when long locale titles (ja) outgrow the 1200 column: the row may use the wider gallery width */
@media (min-width: 810px) { .m-blog-cards > .blogcards-grid { grid-column: 1 / -1; width: min(100% - 2 * var(--gutter), 1392px); justify-self: center; } }
/* production pills are flat on the grey band (text + thin arrow), white only on hover */
.m-blog-cards .blogcards-grid > .card { border: 0; box-shadow: none; background: transparent; border-radius: 12px; max-width: 100%; transition: background-color .15s ease; }
.m-blog-cards .blogcards-grid > .card .card-arrow { font-weight: 400; }
.m-blog-cards .blogcards-grid > .card > a { display: flex; align-items: center; justify-content: center; gap: 8px; padding: 14px 22px; font-size: 16px; line-height: 1.3; font-weight: 600; color: var(--c-text); text-decoration: none; text-wrap: balance; }
.m-blog-cards .blogcards-grid > .card:hover { background: var(--c-white); }
.m-blog-cards .blogcards-grid > .card > a:hover { color: var(--c-brand); }
@media (max-width: 809px) {
  .m-blog-cards > * { padding-inline: 4px; }
  .m-blog-cards .blogcards-grid > .card { flex: 0 1 auto; width: fit-content; }
  .m-blog-cards .blogcards-grid > .card > a { justify-content: space-between; text-align: left; text-wrap: pretty; padding: 12px 16px; }
  .m-blog-cards .blogcards-grid > .card > a > .card-label { flex: 1 1 auto; }
  .m-blog-cards .blogcards-grid > .card > a > .card-arrow { flex: 0 0 auto; }
}

/* ---- blog cards WITH a cover (production 2026-08-19: "Discover more blogs" = 3 image cards 278px wide, 12px apart, left-aligned;
   cover 16:9 with 8px top corners over a white body: title 18px/600 + "Read More →" 12px/600). Cards without a cover keep the pill. */
.m-blog-cards > .blogcards-grid:has(> .card-has-cover) { display: grid; grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); gap: 12px; }
@media (min-width: 810px) { .m-blog-cards > .blogcards-grid:has(> .card-has-cover) { width: 100%; } }
.m-blog-cards .blogcards-grid > .card-has-cover { background: var(--c-white); border-radius: 8px; overflow: hidden; box-shadow: var(--shadow-card, 0 1px 3px rgba(0,0,0,.08)); max-width: none; width: auto; border: 1px solid var(--c-line); }
.m-blog-cards .blogcards-grid > .card-has-cover > a { display: flex; flex-direction: column; align-items: flex-start; justify-content: flex-start; gap: 0; padding: 0; text-align: left; text-wrap: normal; height: 100%; }
.m-blog-cards .blogcards-grid > .card-has-cover .card-cover { display: block; width: 100%; aspect-ratio: 16 / 9; overflow: hidden; }
.m-blog-cards .blogcards-grid > .card-has-cover .card-cover img, .m-blog-cards .blogcards-grid > .card-has-cover .card-cover picture { display: block; width: 100%; height: 100%; object-fit: cover; }
.m-blog-cards .blogcards-grid > .card-has-cover .card-label { display: block; padding: 10px 8px 4px; font-size: 18px; line-height: 1.3; font-weight: 600; color: var(--c-text); }
.m-blog-cards .blogcards-grid > .card-has-cover .card-more { display: block; padding: 0 8px 12px; font-size: 12px; line-height: 1.2; font-weight: 600; color: var(--c-text); }
.m-blog-cards .blogcards-grid > .card-has-cover .card-arrow { font-weight: 600; }
.m-blog-cards .blogcards-grid > .card-has-cover:hover { background: var(--c-white); }
.m-blog-cards .blogcards-grid > .card-has-cover > a:hover .card-label { color: var(--c-brand); }
/* ---- article cards (Group-C canvas pages: the card carries the whole article, production clamps it to 3 lines under the
   title; the article headings render at 19px there). Only cards with tools.blogCards[].body take this shape. */
.m-blog-cards .blogcards-grid > .card-article { display: flex; flex-direction: column; align-items: flex-start; text-align: left; }
.m-blog-cards .blogcards-grid > .card-article .card-title { margin: 0; padding: 10px 8px 4px; font-size: 18px; line-height: 1.3; font-weight: 600; }
.m-blog-cards .blogcards-grid > .card-article .card-title a { color: var(--c-text); }
.m-blog-cards .blogcards-grid > .card-article .card-excerpt { display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden; padding: 0 8px; font-size: 14px; line-height: 1.5; color: var(--c-text); }
.m-blog-cards .blogcards-grid > .card-article .card-excerpt h2, .m-blog-cards .blogcards-grid > .card-article .card-excerpt h3, .m-blog-cards .blogcards-grid > .card-article .card-excerpt h4 { font-size: 19px; line-height: 1.3; margin: 0 0 4px; }
.m-blog-cards .blogcards-grid > .card-article .card-excerpt p { margin: 0 0 6px; }
.m-blog-cards .blogcards-grid > .card-article .card-more { display: block; padding: 6px 8px 12px; font-size: 12px; line-height: 1.2; font-weight: 600; color: var(--c-text); }
@media (max-width: 809px) {
  .m-blog-cards > .blogcards-grid:has(> .card-has-cover) { grid-template-columns: 1fr; gap: 12px; padding-inline: 0; }
  .m-blog-cards .blogcards-grid > .card-has-cover { width: 100%; }
  .m-blog-cards .blogcards-grid > .card-has-cover .card-label { font-size: 20px; }
}

/* Explicit source-grid variant: retained RelatedToolCards mode=cards, c4def564d. Defaults stay unchanged. */
.m-blog-cards.sec-blogcards.source-grid[data-variant="source-grid"] { display: flex; flex-direction: column; align-items: center; gap: 24px; padding: 20px 0 40px; background: #f5f6f7; letter-spacing: normal; }
.m-blog-cards.sec-blogcards.source-grid[data-variant="source-grid"] > h2 { width: 100%; margin: 0; padding: 0; font-size: 36px; line-height: 1.2em; font-weight: 700; text-align: center; text-wrap: wrap; white-space: pre-wrap; word-break: break-word; overflow-wrap: break-word; }
.m-blog-cards.sec-blogcards.source-grid[data-variant="source-grid"] .source-blog-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); gap: 12px; width: 100%; max-width: none; min-width: 0; margin: 0; padding: 0; align-self: stretch; }
.m-blog-cards.sec-blogcards.source-grid[data-variant="source-grid"] .source-blog-card { display: flex; flex-direction: column; gap: 12px; background: #fff; border: 0; box-shadow: none; border-radius: 8px; overflow: hidden; text-decoration: none; color: rgb(51, 51, 51); font-family: Inter, sans-serif; line-height: normal; }
.m-blog-cards.sec-blogcards.source-grid[data-variant="source-grid"] .source-blog-image { width: 100%; aspect-ratio: 291 / 160; overflow: hidden; }
.m-blog-cards.sec-blogcards.source-grid[data-variant="source-grid"] .source-blog-image :is(picture, img) { display: block; width: 100%; height: 100%; object-fit: cover; }
.m-blog-cards.sec-blogcards.source-grid[data-variant="source-grid"] .source-blog-copy { display: flex; flex-direction: column; gap: 8px; padding: 0 4px 12px; }
.m-blog-cards.sec-blogcards.source-grid[data-variant="source-grid"] .source-blog-label { font-size: 20px; font-weight: 600; line-height: 26px; }
.m-blog-cards.sec-blogcards.source-grid[data-variant="source-grid"] .source-blog-description { font-size: 14px; font-weight: 400; line-height: 20px; color: rgb(102, 102, 102); }
.m-blog-cards.sec-blogcards.source-grid[data-variant="source-grid"] .source-blog-description p { margin: 0; }
.m-blog-cards.sec-blogcards.source-grid[data-variant="source-grid"] .source-blog-more { font-size: 14px; font-weight: 600; color: rgb(51, 51, 51); }
@media (min-width: 768px) and (max-width: 1279.98px) { .m-blog-cards.sec-blogcards.source-grid[data-variant="source-grid"] { padding: 20px; } }
@media (max-width: 767.98px) {
  .m-blog-cards.sec-blogcards.source-grid[data-variant="source-grid"] { align-items: flex-start; gap: 12px; padding: 0 16px 20px; }
  .m-blog-cards.sec-blogcards.source-grid[data-variant="source-grid"] > h2 { font-size: 20px; line-height: 1.3em; }
}

/* blog-hero@1.1.0 */
/* lifted verbatim from src/styles/blog.css:19-69 (the hero band and the collage) and the collage half of
   :233 (prefers-reduced-motion).
   Rewrite forced by module-language.md §9 rule 1: `.hero-blog` is the module root, so every `.hero-blog`
   selector becomes `.m-blog-hero` (which still carries the `hero hero-blog` classes the site CSS uses).
   `.hero-collage` and `.hero-text` are descendants and keep their selectors. Nothing else changes. */
/* ───────── hero band (index + tag) ───────── */
.m-blog-hero { position: relative; overflow: hidden; background: var(--c-bg); display: grid; grid-template-columns: minmax(var(--gutter), 1fr) minmax(0, var(--page-max)) minmax(var(--gutter), 1fr); align-items: center; }
.m-blog-hero > * { grid-column: 2; grid-row: 1; }
/* phone: Framer keeps a 260px hero and pins the copy to its bottom (44px under the CTA), so a longer locale lede rises instead of growing the band;
   min-height (not height) so a de/fr lede that outgrows 260 still fits */
.m-blog-hero .hero-text { position: relative; z-index: 1; display: flex; flex-direction: column; align-items: center; text-align: center; padding-block: 24px 44px; }
@media (max-width: 809px) {
  .m-blog-hero:has(.hero-collage) { min-height: 260px; align-items: end; }
}
.m-blog-hero h1 { font-family: var(--font-display); font-size: 28px; font-weight: 400; line-height: 1.2; letter-spacing: normal; }
.m-blog-hero .hero-sub { color: var(--c-text); font-size: 16px; line-height: 1.3; font-weight: 400; margin-top: 20px; max-width: 40ch; }
.m-blog-hero .cta-row { margin-top: 12px; }
.m-blog-hero .cta-row .btn { padding: 12px 24px; font-size: 14px; font-weight: 700; line-height: 1.15; border-radius: var(--blog-r-card); }
/* the collage: Framer = a 561×412 box (right of the copy) holding a 3-column sheet of 189×252 r12 tiles (gap 12, middle column shifted up half a pitch),
   the sheet rotated -18°, the box soft-masked with an ellipse (opaque to 24%, gone at 95%). Each tile is placed on the rotated grid by its own transform. */
.m-blog-hero .hero-collage { grid-column: 1 / -1; grid-row: 1; position: relative; z-index: 0; align-self: stretch; justify-self: stretch; margin: 0; padding: 0; list-style: none; pointer-events: none;
  -webkit-mask-image: radial-gradient(50% 50% at 50% 50%, rgba(0, 0, 0, 0.24) 0%, transparent 100%); mask-image: radial-gradient(50% 50% at 50% 50%, rgba(0, 0, 0, 0.24) 0%, transparent 100%); -webkit-mask-repeat: no-repeat; mask-repeat: no-repeat; }
.m-blog-hero .hero-collage li { position: absolute; left: 50%; top: 50%; width: 189px; height: 252px; margin: -126px 0 0 -94.5px; transform: rotate(-18deg) translate(var(--tx, 0px), var(--ty, 0px)); }
.m-blog-hero .hero-collage li:nth-child(1) { --tx: -201px; --ty: -264px; }
.m-blog-hero .hero-collage li:nth-child(2) { --tx: -201px; --ty: 0px; }
.m-blog-hero .hero-collage li:nth-child(3) { --tx: -201px; --ty: 264px; }
.m-blog-hero .hero-collage li:nth-child(4) { --tx: 0px; --ty: -396px; }
.m-blog-hero .hero-collage li:nth-child(5) { --tx: 0px; --ty: -132px; }
.m-blog-hero .hero-collage li:nth-child(6) { --tx: 0px; --ty: 132px; }
.m-blog-hero .hero-collage li:nth-child(7) { --tx: 0px; --ty: 396px; }
.m-blog-hero .hero-collage li:nth-child(8) { --tx: 201px; --ty: -264px; }
.m-blog-hero .hero-collage li:nth-child(9) { --tx: 201px; --ty: 0px; }
.m-blog-hero .hero-collage li:nth-child(10) { --tx: 201px; --ty: 264px; }
.m-blog-hero .hero-collage li:nth-child(n+11) { display: none; }
.m-blog-hero .hero-collage picture, .m-blog-hero .hero-collage img { width: 100%; height: 100%; }
.m-blog-hero .hero-collage img { object-fit: cover; border-radius: 12px; }
@media (min-width: 810px) {
  .m-blog-hero { min-height: 380px; }
  .m-blog-hero .hero-text { align-items: flex-start; text-align: left; padding-block: 48px; max-width: 392px; }
  .m-blog-hero h1 { font-size: 40px; }
  .m-blog-hero .hero-sub { color: var(--c-text-2); font-size: 20px; line-height: 1.3; font-weight: 500; margin-top: 32px; }
  .m-blog-hero .cta-row { margin-top: 16px; }
  .m-blog-hero .cta-row .btn { font-size: 16px; line-height: 1.2; }
  .m-blog-hero .hero-collage { grid-column: 2; justify-self: end; width: min(561px, 52%); pointer-events: auto;
    -webkit-mask-image: radial-gradient(50% 50% at 50% 50%, #000 24%, transparent 95%); mask-image: radial-gradient(50% 50% at 50% 50%, #000 24%, transparent 95%); }
  .m-blog-hero .hero-collage li { transition: transform .2s ease; }
  .m-blog-hero .hero-collage li:hover { transform: rotate(-18deg) translate(var(--tx, 0px), var(--ty, 0px)) scale(1.03); }
}
@media (min-width: 1280px) {
  .m-blog-hero { min-height: 412px; }
  .m-blog-hero .hero-text { padding-inline-start: 40px; max-width: 432px; }   /* copy at x=160 on the 1440 ground */
  .m-blog-hero .hero-collage { width: 561px; margin-right: 40px; }
}
/* tag archive has no collage: keep the copy centred */
.m-blog-hero:not(:has(.hero-collage)) { min-height: 0; }
.m-blog-hero:not(:has(.hero-collage)) .hero-text { align-items: center; text-align: center; margin-inline: auto; max-width: 60ch; padding-block: 48px; padding-inline-start: 0; }
@media (prefers-reduced-motion: reduce) { .m-blog-hero .hero-collage li { transition: none; } }

/* explainer@1.7.2 */
/* explainer — lifted from tool.css:101-149,172-208 under the .m-explainer prefix
   (module-language.md §9 rule 1). Every DECLARATION is byte-identical to src/styles; only the selector
   prefix differs, and src/lib/bx/seed-parity.test.ts fails when one drifts.

   The zigzag row: the text column's rhythm, the in-row CTA rectangle, the text-only variant and the
   media column. Every selector here is anchored in markup this template emits (.sec-explainer, .sec-text,
   .sec-media). The band itself (.sec) is the Section primitive's CSS and the button is the Cta primitive's.

   STAYS IN SITE CSS — a page-kind, positional or document-order scope that no module selector can
   carry. R9 converts these to the layout attributes the renderer stamps (data-surface, data-next,
   data-variant); until then src/styles/*.css is still the live stylesheet and owns them:
     tool.css:185 main[data-kind="tool"] .sec-explainer.has-media > .sec-text
     tool.css:186 main[data-kind="tool"] .sec-explainer.has-media > .sec-media
     tool.css:187 main[data-kind="tool"] .sec-explainer.has-media.alt > .sec-text
     tool.css:188 main[data-kind="tool"] .sec-explainer.has-media.alt > .sec-media
     tool.css:193 main[data-kind="tool"] .sec-explainer.has-media > .sec-media
     tool.css:194 main[data-kind="tool"] .sec-explainer.has-media:not(.alt) > .sec-media
     tool.css:195 main[data-kind="tool"] .sec-explainer.has-media:not(.alt) > .sec-text
     tool.css:197 main[data-kind="tool"] .sec-explainer.sec-row.has-media > .sec-media
     tool.css:198 main[data-kind="tool"] .sec-explainer.sec-row.has-media > .sec-text
     tool.css:207 main[data-kind="tool"] .sec-explainer.sec-row.has-media > .sec-media > picture + picture
 */
/* ---------- the FAQ shape: an FAQ-ish heading with headings in its body and no picture renders the
     accordion band instead of the zigzag (template.bx arm 1), so the module owes that shape its rules too.
     Lifted from tool.css:445-447 and :450-454, declaration for declaration. The root of THIS arm is the
     <section class="sec sec-faq sec-faq-home"> itself, so the stamped .m-explainer lands on the same
     element and the selector is compound (.m-explainer.sec-faq), not descendant.
     `.sec-faq > .sec-sub` is not lifted: this arm renders no subtitle. `.faq-list` / `.faq-item` ship with
     the Accordion primitive. tool.css:455 (`main[data-kind] .sec-faq` band padding) and the whole home
     override (home.css:449-490, `main[data-page="home"] .sec-faq`, which reads the home-only
     --home-faq-pad-b / --home-faq-left / --home-faq-list) stay in site CSS: both are page-kind scoped, and
     the home block out-specifies these at 0-2-1 exactly as it out-specifies tool.css today. ---------- */
.m-explainer.sec-faq { background: var(--c-bg); row-gap: 0; }
.m-explainer.sec-faq > h2 { text-align: center; line-height: 1.3; margin-bottom: 20px; }
@media (min-width: 810px) {
  .m-explainer.sec-faq { grid-template-columns: minmax(var(--gutter), 1fr) minmax(0, 380px) 48px minmax(0, 772px) minmax(var(--gutter), 1fr); grid-template-rows: auto auto 1fr; align-items: start; }
  .m-explainer.sec-faq > h2 { grid-column: 2; grid-row: 1; text-align: left; margin: 0; }
  .m-explainer.sec-faq > .faq-list { grid-column: 4; grid-row: 1 / span 3; }
}

/* ---------- explainer rows: text column + media column, zigzag ---------- */
.m-explainer .sec-explainer .sec-text > h2 { font-size: var(--t-row-h2); line-height: 1.3; font-weight: 600; text-align: left; margin: 0 0 24px; }
.m-explainer .sec-explainer .sec-text > * + * { margin-top: 24px; }
.m-explainer .sec-explainer .sec-text > :first-child { margin-top: 0; }
.m-explainer .sec-explainer .sec-text > h3 { margin-top: 36px; }
.m-explainer .sec-explainer .sec-text > h4 { margin-top: 32px; }
.m-explainer .sec-explainer .sec-text > :is(h3, h4) + * { margin-top: 24px; }
/* an <h2> that is NOT the row's first child is the in-row sub-heading the rich text carries ("Step 5: …" h3 → "More Than Image
   to Video" h2 → copy): production 24/31 600 left with 42px above and 24px below — never the centred section title */
.m-explainer .sec-explainer .sec-text > * + h2 { font-size: var(--t-row-h2); line-height: 1.3; font-weight: 600; text-align: left; margin: 40px 0 24px; }
.m-explainer .sec-explainer .sec-text > * + h2 + * { margin-top: 0; }
/* phone rhythm (production 390: p→p 21px, h3 27px above, body→CREATE NOW 16px, button→picture 20px) */
@media (max-width: 809px) {
  .m-explainer .sec-explainer .sec-text > * + * { margin-top: 21px; }
  .m-explainer .sec-explainer .sec-text > h3 { margin-top: 27px; }
  .m-explainer .sec-explainer .sec-text > :is(h3, h4) + * { margin-top: 21px; }
  .m-explainer .sec-explainer .sec-text > * + h2 { margin-top: 32px; }
  .m-explainer .sec-explainer .sec-text > :first-child { margin-top: 0; }
}
.m-explainer .sec-explainer .sec-text > ul, .m-explainer .sec-explainer .sec-text > ol { padding-left: 1.4em; }
.m-explainer .sec-explainer .sec-text > ul { list-style: disc; }
.m-explainer .sec-explainer .sec-text > ol { list-style: decimal; }
.m-explainer .sec-explainer .sec-text li + li { margin-top: 8px; }
/* in-section CTA = the black CREATE NOW rectangle (both the rich-text link-paragraph and the block's own ctaLabel button), at Framer's 160px minimum (tool pages en + ja 1440/390, 2026-09-25: CREATE NOW 169, 今すぐ作成 160) */
/* the link-paragraph is an app link alone in its paragraph: one carrying ?cta= (every seeded CTA), or any app.apob.ai link that
   opens in the same tab — a page whose CTAs production links without attribution (ai-kpop-idol-generator's five CREATE NOW,
   /model/create?usage=portraitSim) keeps the button. An app link that opens a new tab is a reading link, as production draws it
   (kpop "Build Your First Idol Reference", heygen-alternative's two; apob.ai 2026-09-26, en/ja/ko at 1440 and 390). Each ?cta=
   selector below has an app-link twin of the same specificity (its :not([target]) sits in :where()); the ?cta= selectors stay
   verbatim, as the guide and value-section modules lift them. */
.m-explainer .sec-explainer .sec-text > p:has(> a[href*="?cta="]:only-child), .m-explainer .sec-explainer .sec-text > p:has(> a[href^="https://app.apob.ai/"]:where(:not([target])):only-child) { margin-top: 32px; }
.m-explainer .sec-explainer .sec-text > p > a[href*="?cta="]:only-child,
.m-explainer .sec-explainer .sec-text > p > a[href^="https://app.apob.ai/"]:where(:not([target])):only-child,
.m-explainer .sec-fold > p > a[href*="?cta="]:only-child,
.m-explainer .sec-fold > p > a[href^="https://app.apob.ai/"]:where(:not([target])):only-child,
.m-explainer .sec-explainer .sec-text .cta-row .btn {
  display: inline-flex; align-items: center; justify-content: center;
  padding: 12px 24px; border-radius: 4px; min-width: 160px;
  background: var(--c-text); color: var(--c-white);
  font-size: 18px; line-height: 1.2; font-weight: 700;
  transition: background-color .15s ease, transform .15s ease;
}
.m-explainer .sec-explainer .sec-text > p > a[href*="?cta="]:only-child:hover,
.m-explainer .sec-explainer .sec-text > p > a[href^="https://app.apob.ai/"]:where(:not([target])):only-child:hover,
.m-explainer .sec-fold > p > a[href*="?cta="]:only-child:hover,
.m-explainer .sec-fold > p > a[href^="https://app.apob.ai/"]:where(:not([target])):only-child:hover,
.m-explainer .sec-explainer .sec-text .cta-row .btn:hover { background: var(--c-black); transform: translateY(-1px); }
.m-explainer .sec-explainer .sec-text > .cta-row { justify-content: flex-start; margin-top: 32px; }
.m-explainer .sec-explainer .sec-text > .cta-row + * { margin-top: 32px; }
/* a CTA paragraph is followed by the next step's heading: production leaves 40px above such a heading */
.m-explainer .sec-explainer .sec-text > p:has(> a[href*="?cta="]:only-child) + :is(h3, h4), .m-explainer .sec-explainer .sec-text > p:has(> a[href^="https://app.apob.ai/"]:where(:not([target])):only-child) + :is(h3, h4) { margin-top: 40px; }
@media (max-width: 809px) {
  .m-explainer .sec-explainer .sec-text > p:has(> a[href*="?cta="]:only-child), .m-explainer .sec-explainer .sec-text > p:has(> a[href^="https://app.apob.ai/"]:where(:not([target])):only-child), .m-explainer .sec-explainer .sec-text > .cta-row { margin-top: 16px; }
  .m-explainer .sec-explainer .sec-text > p:has(> a[href*="?cta="]:only-child) + :is(h3, h4), .m-explainer .sec-explainer .sec-text > p:has(> a[href^="https://app.apob.ai/"]:where(:not([target])):only-child) + :is(h3, h4) { margin-top: 40px; }
}

/* text-only explainer: centred title, left copy, inset like production (1120 within 1200) — only the block's own h2 (always the first child) */
.m-explainer .sec-explainer:not(.has-media) > .sec-text { padding-inline: var(--t-inset); }
.m-explainer .sec-explainer:not(.has-media) .sec-text > h2:first-child { font-size: var(--t-sec-h2); font-weight: 700; line-height: 1.2; text-align: center; margin-bottom: 32px; }
@media (max-width: 809px) { .m-explainer .sec-explainer:not(.has-media) .sec-text > h2:first-child { margin-bottom: 24px; } }   /* production phone: title → first paragraph ~24px */

/* media row: 580 | 40 | 580 on desktop, stacked on phone. Both children sit in ROW 1 (explicit — the auto-placement
   cursor otherwise drops the media into a second row whenever the text is placed in the later column). */
@media (min-width: 810px) {
  .m-explainer .sec-explainer.has-media {
    grid-template-columns: minmax(var(--gutter), 1fr) minmax(0, 580px) var(--t-col-gap) minmax(0, 580px) minmax(var(--gutter), 1fr);
    align-items: center;
  }
  .m-explainer .sec-explainer.has-media > .sec-text { grid-column: 2; grid-row: 1; }
  .m-explainer .sec-explainer.has-media > .sec-media { grid-column: 4; grid-row: 1; }
  .m-explainer .sec-explainer.has-media.alt > .sec-text { grid-column: 4; }
  .m-explainer .sec-explainer.has-media.alt > .sec-media { grid-column: 2; }
  /* tool pages: production starts the zigzag with the copy on the LEFT (row 1 = the first odd index = .alt), so the sides flip here */
}
@media (max-width: 809px) {
  .m-explainer .sec-explainer.has-media > .sec-media { margin-top: 32px; }
  /* tool pages stack a row in its DESKTOP reading order: picture-left rows (not .alt) put the picture first, so the phone page alternates image-first / text-first like production */   /* production phone: CREATE NOW → picture ~20px */
  /* … but the h3 rows of a guide ("Step 2", "Beyond Video") stay copy-first on production's phone regardless of their desktop side */
}
.m-explainer .sec-media { min-width: 0; display: grid; grid-template-columns: minmax(0, 1fr); gap: 16px; align-content: center; }
.m-explainer .sec-media > * { min-width: 0; }
/* every row image is full column width, 16px radius, natural height; a hairline ring outlines white UI screenshots and disappears on photos */
.m-explainer .sec-media picture, .m-explainer .sec-media img, .m-explainer .sec-media video { display: block; width: 100%; height: auto; }
.m-explainer .sec-media img, .m-explainer .sec-media video { border-radius: var(--r-md); }
.m-explainer .sec-media img { box-shadow: 0 0 0 1px var(--c-line); }   /* production: a visible 1px #dedede ring keeps white app screenshots off the page */
/* tool explainer rows carry ONE picture each (the data maps media[i] → row i); a stray second picture never renders as a thumbnail stack */
.m-explainer .sec-media video { object-fit: cover; background: var(--c-black); }
/* a row clip's own aspect: [autoplay] from its metadata (1.5.0), a tool-page poster clip from the poster's width / height the
   template stamps (1.6.1); every other row video keeps the 16:9 box (tool.css says why) */
.m-explainer .sec-media video:where(:not([width][height])) { aspect-ratio: 16 / 9; }
.m-explainer .sec-media video[autoplay] { aspect-ratio: auto; }

/* ---------- the prompts / comparison-table slot: variant `prompts-scroll` (template.bx arm 2). Lifted from tool.css, declaration
     for declaration, and — unlike the rest of this file — keyed on .sec-prompts alone, a class only that arm emits, so the
     variant cannot restyle any other explainer band. ---------- */
.m-explainer .sec-explainer.sec-prompts:not(.has-media) > .sec-text { padding-inline: 0; }
.m-explainer .sec-explainer.sec-prompts:not(.has-media) .sec-text > h2:first-child { margin-bottom: 32px; }
.m-explainer .sec-prompts .sec-scroll { position: relative; box-sizing: border-box; height: 320px; padding: 20px; background: var(--c-white); border-radius: 4px; }
.m-explainer .sec-prompts .sec-scroll::after { content: ""; position: absolute; inset: 0; border: 1px solid var(--c-line); border-radius: inherit; pointer-events: none; }
.m-explainer .sec-prompts .sec-scroll-inner { height: 100%; overflow: auto; }
.m-explainer .sec-prompts .sec-scroll-inner:focus-visible { outline: 2px solid var(--c-text); outline-offset: 2px; }
.m-explainer .sec-prompts .sec-scroll-inner > * + * { margin-top: 16px; }
.m-explainer .sec-prompts .sec-scroll-inner > h3 { margin-top: 36px; }
.m-explainer .sec-prompts .sec-scroll-inner > h4 { margin-top: 28px; }
.m-explainer .sec-prompts .sec-scroll-inner > :first-child { margin-top: 0; }
.m-explainer .sec-prompts .sec-scroll-inner > :is(h3, h4) + :is(ul, ol) { margin-top: 4px; }
.m-explainer .sec-prompts .sec-scroll-inner > :is(ul, ol) { padding-left: 1.4em; }
.m-explainer .sec-prompts .sec-scroll-inner > ul { list-style: disc; }
.m-explainer .sec-prompts .sec-scroll-inner > ol { list-style: decimal; }
.m-explainer .sec-explainer.sec-prompts .sec-scroll-inner li + li { margin-top: 0; }
/* a frame that OPENS with a table (bullet-time-effect's comparison, measured on apob.ai 2026-09-25): production's frame is a
   flex column whose table wrapper shrinks and scrolls on its own (232 of 316px), so the note under the table stays in view
   inside the 280px box; the frame's other contents keep their size. */
.m-explainer .sec-prompts .sec-scroll-inner:has(> .rich-table-wrap[data-table-preset]:first-child) { display: flex; flex-direction: column; }
.m-explainer .sec-prompts .sec-scroll-inner:has(> .rich-table-wrap[data-table-preset]:first-child) > * { flex: 0 0 auto; }
.m-explainer .sec-prompts .sec-scroll-inner:has(> .rich-table-wrap[data-table-preset]:first-child) > .rich-table-wrap[data-table-preset]:first-child { flex: 0 1 auto; min-height: 0; overflow: auto; }
.m-explainer .sec-prompts > .sec-media { margin-top: 32px; }
@media (max-width: 809px) { .m-explainer .sec-prompts .sec-scroll { margin-inline: calc(-1 * var(--gutter)); } }   /* production phone: the frame is full-bleed (390 wide, 350 inside) */
/* variants prompts-scroll-inset / -short / -phone (1.6.0), lifted declaration for declaration into tool.css — production's other scroll frames, each the prompts-scroll arm with the variant's name as a modifier class
   (measured 2026-09-25 at 1440 and 390; the heading is outside the frame in every one):
   prompts-scroll-inset  the Selling Point frame (navigate-sora-shutdown): 1200×320 like prompts-scroll, but on the phone an
                         inset 358×320 card at x 16 with 16px padding (326×288 inside) instead of the full-bleed frame
   prompts-scroll-short  the Use Case frame (ai-ad-video-generator): 1200×296 (256 inside); phone inset 358×200, 16px padding (168)
   prompts-scroll-phone  the prompt library that scrolls on the phone only (ai-video-extender, ai-baby-generator, ai-boyfriend-
                         generator): an open 1200-wide card with 20px padding at any height from 810px; a 326×168 scroll card at
                         x 32 with no padding below */
@media (min-width: 810px) {
  .m-explainer .sec-prompts.prompts-scroll-short .sec-scroll { height: 296px; }
  .m-explainer .sec-prompts.prompts-scroll-phone .sec-scroll { height: auto; }
  .m-explainer .sec-prompts.prompts-scroll-phone .sec-scroll-inner { height: auto; overflow: visible; }
}
@media (max-width: 809px) {
  .m-explainer .sec-prompts.prompts-scroll-inset .sec-scroll, .m-explainer .sec-prompts.prompts-scroll-short .sec-scroll { margin-inline: 0; padding: 16px; }
  .m-explainer .sec-prompts.prompts-scroll-short .sec-scroll { height: 200px; }
  .m-explainer .sec-prompts.prompts-scroll-phone .sec-scroll { margin-inline: 16px; padding: 0; height: 168px; }
}
/* explainer 1.6.0 `foldRows`: production's collapsible "Expand" fold (Framer's 折叠/展开 component; measured 2026-09-25 on home
   sections 2–5 and navigate-sora-shutdown at 1440, 810 and 390, all 7 locales). The copy is clamped to four lines (Chrome draws the
   ellipsis), a 24px gap (8px on the phone) above a 26px toggle: a 32px #999 rule, the label 20/26 bold #999 (14px on the phone) and
   a 20px chevron that turns over when open. The label is always shown, overflow or not. CSS-only: the Disclosure checkbox + label,
   the checkbox kept focusable (the footer's pattern). The copy is 16/24 #333 (14/21 on the phone), paragraphs 30px apart. */
.m-explainer .sec-fold { position: relative; display: flex; flex-direction: column; align-items: flex-start; gap: 24px; }
.m-explainer .sec-fold > .fold-text { align-self: stretch; display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 4; overflow: hidden; font-size: 16px; line-height: 24px; color: var(--c-text); }
.m-explainer .sec-fold > .fold-text > * + * { margin-top: 30px; }
.m-explainer .sec-fold:has(> .fold-state:checked) > .fold-text { display: block; -webkit-line-clamp: none; overflow: visible; }
.m-explainer .sec-fold > .fold-state[hidden] { display: block; position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%); border: 0; opacity: 0; }
.m-explainer .sec-fold > .fold-toggle { display: flex; align-items: center; gap: 8px; height: 26px; font-size: 20px; line-height: 26px; font-weight: 700; color: var(--c-text-3); cursor: pointer; }
.m-explainer .sec-fold > .fold-state:focus-visible + .fold-toggle { outline: 2px solid var(--c-text); outline-offset: 2px; }
.m-explainer .sec-fold > .fold-toggle > .fold-rule { flex: 0 0 34px; height: 4px; background: linear-gradient(currentColor, currentColor) 0 0 / 32px 1px no-repeat; }
.m-explainer .sec-fold > .fold-toggle > .fold-chevron { flex: 0 0 20px; width: 20px; height: 20px; stroke-width: 1.2; }
.m-explainer .sec-fold:has(> .fold-state:checked) > .fold-toggle > .fold-chevron { transform: rotate(180deg); }
/* the link-only tail (the row's CREATE NOW) sits under the toggle as the in-section black button (the CTA rule above), never
   clamped: production leaves 32px between toggle and button, 16 on the phone — the fold's 24 / 8px gap plus this 8 (measured
   2026-09-25 on home s2–s5 en + ko and navigate-sora-shutdown en + ja at 1440 and 390) */
.m-explainer .sec-fold > p:has(> a[href*="?cta="]:only-child), .m-explainer .sec-fold > p:has(> a[href^="https://app.apob.ai/"]:where(:not([target])):only-child) { margin-top: 8px; }
@media (max-width: 809px) { .m-explainer .sec-fold { gap: 8px; } .m-explainer .sec-fold > .fold-text { font-size: 14px; line-height: 21px; } .m-explainer .sec-fold > .fold-toggle { font-size: 14px; } }
/* the label, production's strings per locale (collapsed / open) */
.m-explainer .sec-fold > .fold-toggle > .fold-label::before { content: "Expand"; }
.m-explainer .sec-fold:has(> .fold-state:checked) > .fold-toggle > .fold-label::before { content: "Collapse"; }
.m-explainer .sec-fold > .fold-toggle > .fold-label:lang(es)::before { content: "Expandir"; }
.m-explainer .sec-fold:has(> .fold-state:checked) > .fold-toggle > .fold-label:lang(es)::before { content: "Colapsar"; }
.m-explainer .sec-fold > .fold-toggle > .fold-label:lang(fr)::before { content: "Développer"; }
.m-explainer .sec-fold:has(> .fold-state:checked) > .fold-toggle > .fold-label:lang(fr)::before { content: "Réduire"; }
.m-explainer .sec-fold > .fold-toggle > .fold-label:lang(pt)::before { content: "Expandir"; }
.m-explainer .sec-fold:has(> .fold-state:checked) > .fold-toggle > .fold-label:lang(pt)::before { content: "Recolher"; }
.m-explainer .sec-fold > .fold-toggle > .fold-label:lang(ko)::before { content: "확장"; }
.m-explainer .sec-fold:has(> .fold-state:checked) > .fold-toggle > .fold-label:lang(ko)::before { content: "접기"; }
.m-explainer .sec-fold > .fold-toggle > .fold-label:lang(ja)::before { content: "展開"; }
.m-explainer .sec-fold:has(> .fold-state:checked) > .fold-toggle > .fold-label:lang(ja)::before { content: "折りたたむ"; }
.m-explainer .sec-fold > .fold-toggle > .fold-label:lang(de)::before { content: "Erweitern"; }
.m-explainer .sec-fold:has(> .fold-state:checked) > .fold-toggle > .fold-label:lang(de)::before { content: "Zuklappen"; }
/* explainer 1.6.0 variant `thumb-strip`: production's "Same Face Collage" (ai-influencer-generator, measured 2026-09-25 at 1440, 810
   and 390): the text full width and left-aligned (h2 36/48.6 bold, 24/38 below 1200px; copy 18/24.3 #505050), the row's pictures a
   strip of 3:4 thumbnails 12px apart under it (six 190px columns in the 1200 column, two columns below 1200px), 8px corners with a
   1px #dedede ring, then the caption 14/18.9 semi-bold #666 centred; 18px between the blocks (16 below 1200px), 56px band padding
   (40 below 1200px) and a 20px gutter below 1200px. */
.m-explainer .sec-explainer.sec-thumbs { padding-block: 40px; }
.m-explainer .sec-explainer.sec-thumbs > .sec-text { padding-inline: 0; }
.m-explainer .sec-explainer.sec-thumbs > .sec-text > h2:first-child { font-size: 24px; line-height: 38px; font-weight: 700; text-align: left; margin: 0 0 16px; }
.m-explainer .sec-explainer.sec-thumbs > .sec-text > p { font-size: 18px; line-height: 1.35; color: rgb(80, 80, 80); }
.m-explainer .sec-explainer.sec-thumbs > .sec-text > p + p { margin-top: 16px; }
.m-explainer .sec-explainer.sec-thumbs > .sec-strip { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; margin-top: 16px; }
.m-explainer .sec-explainer.sec-thumbs > .sec-strip > .strip-cell { position: relative; aspect-ratio: 3 / 4; border-radius: 8px; overflow: hidden; }
.m-explainer .sec-explainer.sec-thumbs > .sec-strip > .strip-cell::after { content: ""; position: absolute; inset: 0; border: 1px solid rgb(222, 222, 222); border-radius: inherit; pointer-events: none; }
.m-explainer .sec-explainer.sec-thumbs > .sec-strip > .strip-cell :is(picture, img) { display: block; width: 100%; height: 100%; }
.m-explainer .sec-explainer.sec-thumbs > .sec-strip > .strip-cell img { object-fit: cover; object-position: 50% 50%; }
.m-explainer .sec-explainer.sec-thumbs > .sec-strip-caption { margin: 16px 0 0; font-size: 14px; line-height: 1.35; font-weight: 600; color: var(--c-text-2); text-align: center; }
@media (min-width: 1200px) { .m-explainer .sec-explainer.sec-thumbs { padding-block: 56px; } .m-explainer .sec-explainer.sec-thumbs > .sec-text > h2:first-child { font-size: 36px; line-height: 1.35; margin-bottom: 18px; } .m-explainer .sec-explainer.sec-thumbs > .sec-text > p + p, .m-explainer .sec-explainer.sec-thumbs > .sec-strip, .m-explainer .sec-explainer.sec-thumbs > .sec-strip-caption { margin-top: 18px; } .m-explainer .sec-explainer.sec-thumbs > .sec-strip { grid-template-columns: repeat(6, minmax(0, 1fr)); } }
@media (max-width: 1199px) { .m-explainer .sec-explainer.sec-thumbs { --gutter: 20px; } }
/* explainer 1.6.0 `mediaWidth`: a band whose media production draws narrower than the 580 column (ai-transition-video-generator:
   520 wide, centred in the column at 1440; full width on the phone). The module stamps data-media-width and --row-media-w. */
@media (min-width: 810px) { .m-explainer .sec-explainer.has-media > .sec-media[data-media-width] { width: 100%; max-width: var(--row-media-w); justify-self: center; } }
/* explainer 1.7.0 `scrollRows` (lifted declaration for declaration into tool.css): production's per-row text frames — the row's copy
   scrolls in a box at most 500px tall beside the media (580×500 at 1440), the scrollbar hidden, the CREATE NOW under it; below 810px
   the copy is unframed unless the band keeps a 300px frame (scrollRowsPhone). The frame is a nested .sec-text. */
.m-explainer .sec-explainer .sec-text > .sec-rowscroll:focus-visible { outline: 2px solid var(--c-text); outline-offset: 2px; }
@media (min-width: 810px) { .m-explainer .sec-explainer .sec-text > .sec-rowscroll { max-height: 500px; overflow-y: auto; scrollbar-width: none; } }
@media (max-width: 809px) { .m-explainer .sec-explainer .sec-text > .sec-rowscroll[data-phone="frame"] { max-height: 300px; overflow-y: auto; scrollbar-width: none; } }
/* explainer 1.7.0 `quoteStyle: 'bar'` (lifted declaration for declaration from tool.css, where the rules carry main[data-kind]):
   production's Framer rich-text quote — 20px under the node before it, 22px in, a 2px #ddd bar, #999 italic 16/28.8 paragraphs
   20px apart. The band root is this module's root, so the stamp is on .m-explainer itself. */
.m-explainer[data-quote="bar"] > .sec div > * + blockquote { margin-top: 20px; }
.m-explainer[data-quote="bar"] > .sec blockquote { position: relative; padding-left: 22px; }
.m-explainer[data-quote="bar"] > .sec blockquote::before { content: " "; position: absolute; top: 0; left: 0; width: 2px; height: 100%; border-radius: 1px; background: #ddd; }
.m-explainer[data-quote="bar"] > .sec blockquote :is(p, li) { font-size: 16px; line-height: 1.8em; font-style: italic; font-weight: 400; color: #999; }
.m-explainer[data-quote="bar"] > .sec blockquote > * + * { margin-top: 20px; }
/* explainer variant `feature-groups-untitled`: production keeps those feature explainers' h2 in the DOM and never shows it (any width) */
.m-explainer .sec-explainer .sec-text > h2.sec-title-hidden { position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap; border: 0; }
.m-explainer .sec-explainer .sec-text > h2.sec-title-hidden + *, .m-explainer .sec-explainer .sec-text > h2.sec-title-hidden + p:has(> a[href*="?cta="]:only-child), .m-explainer .sec-explainer .sec-text > h2.sec-title-hidden + p:has(> a[href^="https://app.apob.ai/"]:where(:not([target])):only-child) { margin-top: 0; }   /* the first visible node spaces like a :first-child */
/* explainer 1.7.2 variant `card` (lifted declaration for declaration from tool.css, where the rules carry main[data-kind]):
   production's bordered text card and the model grid under it (ai-real-estate-photo-editing; apob.ai 2026-09-27, 1440 / 1000 /
   390). The card: 92px under the band's top (48 on the phone), 32 above its end, 20px padding (16), 4px corners, a 1px #dedede
   ring, 60px of empty card above the first title; titles 24/31.2 semi-bold (18/23.4), the h2 copy 16/24 (14/21), 20px under its
   title, the next title 10px under the copy; open from 810px, 358×200 with a 326×168 scrolling body on the phone. The grid band:
   #f5f6f7, white 16px-corner cards with a 2px 4px 10px rgba(0,0,0,.16) shadow, 30/36 bold links (20/24), three columns inset 12px
   from 810px (one below), 168px from the first row to the next (80 on the phone), the caption 20/26 medium #999 centred. The
   band root is this module's root, so the band-level rules sit on .m-explainer itself. */
.m-explainer.sec-card-group { padding-block: 0; }
.m-explainer.sec-card-group > .sec.sec-card { padding-block: 92px 32px; }
.m-explainer .sec-explainer.sec-card > .sec-text { padding-inline: 0; }
.m-explainer .sec-card .sec-card-frame { position: relative; box-sizing: border-box; padding: 20px; background: var(--c-white); border-radius: 4px; }
.m-explainer .sec-card .sec-card-frame::after { content: ""; position: absolute; inset: 0; border: 1px solid rgb(222, 222, 222); border-radius: inherit; pointer-events: none; }
.m-explainer .sec-card .sec-card-body { padding-top: 60px; }
.m-explainer .sec-card .sec-card-body:focus-visible { outline: 2px solid var(--c-text); outline-offset: 2px; }
.m-explainer .sec-card .sec-card-body > * { margin: 20px 0 0; }
.m-explainer .sec-card .sec-card-body > h3 { margin-top: 10px; font-size: 24px; line-height: 1.3; font-weight: 600; color: var(--c-text); }
.m-explainer .sec-card .sec-card-body > h2 { font-size: 16px; line-height: 24px; font-weight: 400; text-align: left; color: var(--c-text); }
.m-explainer .sec-card .sec-card-body > :first-child { margin-top: 0; }
.m-explainer .sec-card .sec-card-body > :is(ul, ol) { padding-left: 1.4em; }
.m-explainer .sec-card .sec-card-body > ul { list-style: disc; }
.m-explainer .sec-card .sec-card-body > ol { list-style: decimal; }
.m-explainer.sec-card-group > .sec.sec-card-links { padding-block: 72px 88px; background: var(--c-bg); }
.m-explainer .sec-explainer.sec-card-links > .sec-text { padding-inline: 12px; }
.m-explainer .sec-card-links .sec-card-grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 12px; align-items: start; }
.m-explainer .sec-card-links .sec-card-grid > p { margin: 0; padding: 20px; background: var(--c-white); border-radius: 16px; box-shadow: 2px 4px 10px 0 rgba(0, 0, 0, 0.16); white-space: pre-wrap; overflow-wrap: break-word; }
.m-explainer > .sec.sec-card-links .sec-card-grid > p { font-size: 30px; line-height: 36px; font-weight: 700; }
.m-explainer .sec-card-links .sec-card-grid:has(> p:nth-child(4)) > p:nth-child(-n+3) { margin-bottom: 156px; }
.m-explainer > .sec.sec-card-links .sec-card-grid > p > a[href] { color: rgb(11, 24, 43); }
.m-explainer > .sec.sec-card-links > .sec-text > .sec-card-grid + p { margin: 156px 0 0; font-size: 20px; line-height: 26px; font-weight: 500; color: var(--c-text-3); text-align: center; }
@media (max-width: 809px) {
  .m-explainer.sec-card-group > .sec.sec-card { padding-block: 48px 32px; }
  .m-explainer .sec-card .sec-card-frame { height: 200px; padding: 16px; }
  .m-explainer .sec-card .sec-card-body { height: 100%; overflow: auto; }
  .m-explainer .sec-card .sec-card-body > h3 { font-size: 18px; }
  .m-explainer .sec-card .sec-card-body > h2 { font-size: 14px; line-height: 21px; }
  .m-explainer.sec-card-group > .sec.sec-card-links { padding-block: 64px 80px; }
  .m-explainer .sec-explainer.sec-card-links > .sec-text { padding-inline: 0; }
  .m-explainer .sec-card-links .sec-card-grid { grid-template-columns: minmax(0, 1fr); }
  .m-explainer > .sec.sec-card-links .sec-card-grid > p { font-size: 20px; line-height: 24px; }
  .m-explainer .sec-card-links .sec-card-grid:has(> p:nth-child(4)) > p:nth-child(-n+3) { margin-bottom: 0; }
  .m-explainer .sec-card-links .sec-card-grid:has(> p:nth-child(4)) > p:nth-child(3) { margin-bottom: 68px; }
  .m-explainer > .sec.sec-card-links > .sec-text > .sec-card-grid + p { margin-top: 100px; }
}

/* extra-prose@1.0.0 */
/* extra-prose — lifted from tool.css:227-232 under the .m-extra-prose prefix
   (module-language.md §9 rule 1). Every DECLARATION is byte-identical to src/styles; only the selector
   prefix differs, and src/lib/bx/seed-parity.test.ts fails when one drifts.

   The boxed prose card. tool.css writes these declarations once for .sec-prose and .sec-usecases
   together; module CSS is per module, so each seed carries its own copy of the selector list it needs.

 */
/* ---------- boxed prose bands (extraProse / useCases): the .sec-body IS the card, h2 (+ grey subtitle) outside ---------- */
.m-extra-prose > .sec-body { padding: 20px var(--t-card-x) 24px; background: var(--c-white); border: 1px solid var(--c-line); border-radius: var(--t-card-r); }
.m-extra-prose > .sec-body > * + * { margin-top: 24px; }
.m-extra-prose > .sec-body > h3 { margin-top: 36px; }
.m-extra-prose > .sec-body > :first-child { margin-top: 0; }
.m-extra-prose > .sec-body > ul, .m-extra-prose > .sec-body > ol { list-style: disc; padding-left: 1.4em; }

/* gallery@1.1.0 */
/* gallery — lifted from tool.css:210-225 under the .m-gallery prefix
   (module-language.md §9 rule 1). Every DECLARATION is byte-identical to src/styles; only the selector
   prefix differs, and src/lib/bx/seed-parity.test.ts fails when one drifts.

   The 5 / 3 / 2 portrait grid, wider than the text column.

   STAYS IN SITE CSS — a page-kind, positional or document-order scope that no module selector can
   carry. R9 converts these to the layout attributes the renderer stamps (data-surface, data-next,
   data-variant); until then src/styles/*.css is still the live stylesheet and owns them:
     tool.css:212 main[data-kind="tool"] .sec-gallery
 */
/* ---------- gallery: 5 / 3 / 2 portrait crops, 4px gaps, wider than the text column ---------- */
/* production breathes more around the gallery: title ~20px lower than a plain band, ~50px more air before the first explainer group */
.m-gallery > h2 { line-height: 1.3; }
.m-gallery > .gallery-grid {
  grid-column: 1 / -1;
  width: min(100% - 2 * var(--gutter), 1392px);
  margin-inline: auto;
  display: grid; gap: 4px;
  grid-template-columns: repeat(2, minmax(0, 1fr));
}
@media (min-width: 810px) { .m-gallery > .gallery-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); width: min(100% - 48px, 1392px); } }
@media (min-width: 1280px) { .m-gallery > .gallery-grid { grid-template-columns: repeat(5, minmax(0, 1fr)); } }
.m-gallery .gallery-grid li { min-width: 0; }
.m-gallery .gallery-grid picture, .m-gallery .gallery-grid img { width: 100%; height: 100%; }
.m-gallery .gallery-grid img { aspect-ratio: 3 / 4; object-fit: cover; }

/* guide@1.6.0 */
/* guide — lifted from tool.css:101-149,172-208 under the .m-guide prefix
   (module-language.md §9 rule 1). Every DECLARATION is byte-identical to src/styles; only the selector
   prefix differs, and src/lib/bx/seed-parity.test.ts fails when one drifts.

   A guide row IS an explainer row — the same .sec-explainer / .sec-text / .sec-media markup — so the
   same rules ride along under this module's own prefix. Module CSS is per module and never shared, so the
   duplication with seeds/modules/explainer/styles.css is deliberate.

   STAYS IN SITE CSS — a page-kind, positional or document-order scope that no module selector can
   carry. R9 converts these to the layout attributes the renderer stamps (data-surface, data-next,
   data-variant); until then src/styles/*.css is still the live stylesheet and owns them:
     tool.css:185 main[data-kind="tool"] .sec-explainer.has-media > .sec-text
     tool.css:186 main[data-kind="tool"] .sec-explainer.has-media > .sec-media
     tool.css:187 main[data-kind="tool"] .sec-explainer.has-media.alt > .sec-text
     tool.css:188 main[data-kind="tool"] .sec-explainer.has-media.alt > .sec-media
     tool.css:193 main[data-kind="tool"] .sec-explainer.has-media > .sec-media
     tool.css:194 main[data-kind="tool"] .sec-explainer.has-media:not(.alt) > .sec-media
     tool.css:195 main[data-kind="tool"] .sec-explainer.has-media:not(.alt) > .sec-text
     tool.css:197 main[data-kind="tool"] .sec-explainer.sec-row.has-media > .sec-media
     tool.css:198 main[data-kind="tool"] .sec-explainer.sec-row.has-media > .sec-text
     tool.css:207 main[data-kind="tool"] .sec-explainer.sec-row.has-media > .sec-media > picture + picture
 */
/* ---------- explainer rows: text column + media column, zigzag ---------- */
.m-guide .sec-explainer .sec-text > h2 { font-size: var(--t-row-h2); line-height: 1.3; font-weight: 600; text-align: left; margin: 0 0 24px; }
.m-guide .sec-explainer .sec-text > * + * { margin-top: 24px; }
.m-guide .sec-explainer .sec-text > :first-child { margin-top: 0; }
.m-guide .sec-explainer .sec-text > h3 { margin-top: 36px; }
.m-guide .sec-explainer .sec-text > h4 { margin-top: 32px; }
.m-guide .sec-explainer .sec-text > :is(h3, h4) + * { margin-top: 24px; }
/* an <h2> that is NOT the row's first child is the in-row sub-heading the rich text carries ("Step 5: …" h3 → "More Than Image
   to Video" h2 → copy): production 24/31 600 left with 42px above and 24px below — never the centred section title */
.m-guide .sec-explainer .sec-text > * + h2 { font-size: var(--t-row-h2); line-height: 1.3; font-weight: 600; text-align: left; margin: 40px 0 24px; }
.m-guide .sec-explainer .sec-text > * + h2 + * { margin-top: 0; }
/* phone rhythm (production 390: p→p 21px, h3 27px above, body→CREATE NOW 16px, button→picture 20px) */
@media (max-width: 809px) {
  .m-guide .sec-explainer .sec-text > * + * { margin-top: 21px; }
  .m-guide .sec-explainer .sec-text > h3 { margin-top: 27px; }
  .m-guide .sec-explainer .sec-text > :is(h3, h4) + * { margin-top: 21px; }
  .m-guide .sec-explainer .sec-text > * + h2 { margin-top: 32px; }
  .m-guide .sec-explainer .sec-text > :first-child { margin-top: 0; }
}
.m-guide .sec-explainer .sec-text > ul, .m-guide .sec-explainer .sec-text > ol { padding-left: 1.4em; }
.m-guide .sec-explainer .sec-text > ul { list-style: disc; }
.m-guide .sec-explainer .sec-text > ol { list-style: decimal; }
.m-guide .sec-explainer .sec-text li + li { margin-top: 8px; }
/* in-section CTA = the black CREATE NOW rectangle (both the rich-text link-paragraph and the block's own ctaLabel button) */
.m-guide .sec-explainer .sec-text > p:has(> a[href*="?cta="]:only-child) { margin-top: 32px; }
.m-guide .sec-explainer .sec-text > p > a[href*="?cta="]:only-child,
.m-guide .sec-explainer .sec-text .cta-row .btn {
  display: inline-flex; align-items: center; justify-content: center;
  padding: 12px 24px; border-radius: 4px;
  background: var(--c-text); color: var(--c-white);
  font-size: 18px; line-height: 1.2; font-weight: 700;
  transition: background-color .15s ease, transform .15s ease;
}
.m-guide .sec-explainer .sec-text > p > a[href*="?cta="]:only-child:hover,
.m-guide .sec-explainer .sec-text .cta-row .btn:hover { background: var(--c-black); transform: translateY(-1px); }
.m-guide .sec-explainer .sec-text > .cta-row { justify-content: flex-start; margin-top: 32px; }
.m-guide .sec-explainer .sec-text > .cta-row + * { margin-top: 32px; }
/* a CTA paragraph is followed by the next step's heading: production leaves 40px above such a heading */
.m-guide .sec-explainer .sec-text > p:has(> a[href*="?cta="]:only-child) + :is(h3, h4) { margin-top: 40px; }
@media (max-width: 809px) {
  .m-guide .sec-explainer .sec-text > p:has(> a[href*="?cta="]:only-child), .m-guide .sec-explainer .sec-text > .cta-row { margin-top: 16px; }
  .m-guide .sec-explainer .sec-text > p:has(> a[href*="?cta="]:only-child) + :is(h3, h4) { margin-top: 40px; }
}

/* text-only explainer: centred title, left copy, inset like production (1120 within 1200) — only the block's own h2 (always the first child) */
.m-guide .sec-explainer:not(.has-media) > .sec-text { padding-inline: var(--t-inset); }
.m-guide .sec-explainer:not(.has-media) .sec-text > h2:first-child { font-size: var(--t-sec-h2); font-weight: 700; line-height: 1.2; text-align: center; margin-bottom: 32px; }
@media (max-width: 809px) { .m-guide .sec-explainer:not(.has-media) .sec-text > h2:first-child { margin-bottom: 24px; } }   /* production phone: title → first paragraph ~24px */

/* media row: 580 | 40 | 580 on desktop, stacked on phone. Both children sit in ROW 1 (explicit — the auto-placement
   cursor otherwise drops the media into a second row whenever the text is placed in the later column). */
@media (min-width: 810px) {
  .m-guide .sec-explainer.has-media {
    grid-template-columns: minmax(var(--gutter), 1fr) minmax(0, 580px) var(--t-col-gap) minmax(0, 580px) minmax(var(--gutter), 1fr);
    align-items: center;
  }
  .m-guide .sec-explainer.has-media > .sec-text { grid-column: 2; grid-row: 1; }
  .m-guide .sec-explainer.has-media > .sec-media { grid-column: 4; grid-row: 1; }
  .m-guide .sec-explainer.has-media.alt > .sec-text { grid-column: 4; }
  .m-guide .sec-explainer.has-media.alt > .sec-media { grid-column: 2; }
  /* tool pages: production starts the zigzag with the copy on the LEFT (row 1 = the first odd index = .alt), so the sides flip here */
}
@media (max-width: 809px) {
  .m-guide .sec-explainer.has-media > .sec-media { margin-top: 32px; }
  /* tool pages stack a row in its DESKTOP reading order: picture-left rows (not .alt) put the picture first, so the phone page alternates image-first / text-first like production */   /* production phone: CREATE NOW → picture ~20px */
  /* … but the h3 rows of a guide ("Step 2", "Beyond Video") stay copy-first on production's phone regardless of their desktop side */
}
.m-guide .sec-media { min-width: 0; display: grid; grid-template-columns: minmax(0, 1fr); gap: 16px; align-content: center; }
.m-guide .sec-media > * { min-width: 0; }
/* every row image is full column width, 16px radius, natural height; a hairline ring outlines white UI screenshots and disappears on photos */
.m-guide .sec-media picture, .m-guide .sec-media img, .m-guide .sec-media video { display: block; width: 100%; height: auto; }
.m-guide .sec-media img, .m-guide .sec-media video { border-radius: var(--r-md); }
.m-guide .sec-media img { box-shadow: 0 0 0 1px var(--c-line); }   /* production: a visible 1px #dedede ring keeps white app screenshots off the page */
/* tool explainer rows carry ONE picture each (the data maps media[i] → row i); a stray second picture never renders as a thumbnail stack */
.m-guide .sec-media video { aspect-ratio: 16 / 9; object-fit: cover; background: var(--c-black); }

/* Optional measured guide-row placement. Absent/null controls retain the existing index policy. */
@media (min-width: 810px) {
  .m-guide.sec-group[data-block="guide"] > .sec-explainer.has-media[data-guide-picture-side="right"] > .sec-text { grid-column: 2; }
  .m-guide.sec-group[data-block="guide"] > .sec-explainer.has-media[data-guide-picture-side="right"] > .sec-media { grid-column: 4; }
  .m-guide.sec-group[data-block="guide"] > .sec-explainer.has-media[data-guide-picture-side="left"] > .sec-text { grid-column: 4; }
  .m-guide.sec-group[data-block="guide"] > .sec-explainer.has-media[data-guide-picture-side="left"] > .sec-media { grid-column: 2; }
}
@media (max-width: 809px) {
  .m-guide.sec-group[data-block="guide"] > .sec-explainer.has-media[data-guide-picture-mobile-first="true"] > .sec-media { grid-row: 1; margin: 0 0 20px; }
  .m-guide.sec-group[data-block="guide"] > .sec-explainer.has-media[data-guide-picture-mobile-first="true"] > .sec-text { grid-row: 2; }
  .m-guide.sec-group[data-block="guide"] > .sec-explainer.has-media[data-guide-picture-mobile-first="false"] > .sec-text { grid-row: 1; }
  .m-guide.sec-group[data-block="guide"] > .sec-explainer.has-media[data-guide-picture-mobile-first="false"] > .sec-media { grid-row: 2; margin: 20px 0 0; }
}

/* Optional full-width Guide header, measured on the current five source tools. */
.m-guide.sec-group[data-block="guide"].guide-heading-full-width { padding-top: 0; }
.m-guide.sec-group[data-block="guide"] > .guide-full-width-heading { display: flex; flex-direction: column; align-items: center; width: 100%; padding: 40px 0 0; margin: 0 0 24px; }
.m-guide.sec-group[data-block="guide"] > .guide-full-width-heading > h2 { width: 100%; max-width: 1280px; margin: 0; padding: 0; color: var(--c-text); font-family: Inter, sans-serif; font-size: 24px; line-height: 1.3em; font-weight: 700; text-align: center; white-space: pre-wrap; text-wrap: wrap; word-break: break-word; overflow-wrap: break-word; }
@media (min-width: 768px) { .m-guide.sec-group[data-block="guide"] > .guide-full-width-heading { margin-bottom: 48px; } .m-guide.sec-group[data-block="guide"] > .guide-full-width-heading > h2 { font-size: 28px; } }
@media (min-width: 1280px) { .m-guide.sec-group[data-block="guide"] > .guide-full-width-heading > h2 { font-size: 32px; } }

/* Guide 1.6 explicit source-layout. Measurements and exact source declarations are retained in recovery/full-guide-fidelity. */
.m-guide.guide-source-layout.sec-group[data-block="guide"] { display: flex; flex-direction: column; align-items: center; gap: 0; width: 100%; padding: 0; }
.m-guide.guide-source-layout.sec-group[data-block="guide"] > .guide-full-width-heading { display: flex; flex-direction: column; align-items: center; width: 100%; padding: 40px 0 0; margin: 0; }
.m-guide.guide-source-layout.sec-group[data-block="guide"] > .guide-full-width-heading > h2 { width: 100%; max-width: 1280px; margin: 0; padding: 0; color: #333; font: 700 24px/1.3em "Guide Source Inter", "Guide Source Inter Placeholder", sans-serif; text-align: center; white-space: pre-wrap; word-break: break-word; overflow-wrap: break-word; }
.m-guide.guide-source-layout .guide-source-row { display: flex; justify-content: center; flex: none; width: 100%; padding: 24px 16px; }
.m-guide.guide-source-layout .guide-source-columns { display: flex; flex-direction: column; flex: none; align-items: center; gap: 20px; width: 100%; }
.m-guide.guide-source-layout .guide-source-text, .m-guide.guide-source-layout .guide-source-media { flex: 1 0 0; min-width: 0; width: 100%; }
.m-guide.guide-source-layout .guide-source-text { color: #333; font: 400 14px/1.5em "Guide Source Inter", "Guide Source Inter Placeholder", sans-serif; letter-spacing: 0; white-space: pre-wrap; word-break: break-word; overflow-wrap: break-word; font-feature-settings: "blwf" on, "cv09" on, "cv03" on, "cv04" on, "cv11" on; }
.m-guide.guide-source-layout .guide-source-text > * { margin: 0; padding: 0; }
.m-guide.guide-source-layout .guide-source-text > * + * { margin-top: 21px; }
.m-guide.guide-source-layout .guide-source-text h3 { color: #333; font: 700 20px/1.3em "Guide Source Inter", "Guide Source Inter Placeholder", sans-serif; letter-spacing: 0; }
.m-guide.guide-source-layout .guide-source-text > h3 { margin-top: 27px; }
.m-guide.guide-source-layout .guide-source-text > :first-child { margin-top: 0; }
.m-guide.guide-source-layout .guide-source-text p, .m-guide.guide-source-layout .guide-source-text li, .m-guide.guide-source-layout .guide-source-text blockquote { font-size: inherit; line-height: inherit; }
.m-guide.guide-source-layout .guide-source-text ul, .m-guide.guide-source-layout .guide-source-text ol { padding-left: 3ch; }
.m-guide.guide-source-layout .guide-source-text ul { list-style: disc; }
.m-guide.guide-source-layout .guide-source-text ol { list-style: decimal; }
.m-guide.guide-source-layout .guide-source-text code { font: 400 1em/1.5em "Guide Source Mono", monospace; background: rgba(0,0,0,.1); padding: .1em .2em; border-radius: 6px; }
.m-guide.guide-source-layout .guide-source-text a:not(.btn) { color: #1a73e8; text-decoration: none; }
.m-guide.guide-source-layout .guide-source-text > .cta-row { display: flex; justify-content: flex-start; margin: 16px 0 0; padding: 0; white-space: normal; }
.m-guide.guide-source-layout .guide-source-text .cta-row .btn { min-width: 160px; display: inline-flex; align-items: center; justify-content: center; padding: 12px 24px; border: 0; border-radius: 4px; background: #333; color: #fff; font: 700 18px/1.2em "Guide Source Inter", "Guide Source Inter Placeholder", sans-serif; letter-spacing: 0; }
.m-guide.guide-source-layout .guide-source-media { aspect-ratio: var(--guide-picture-ratio, auto); position: relative; border-radius: 16px; overflow: hidden; }
.m-guide.guide-source-layout .guide-source-media::after { content: ""; position: absolute; inset: 0; pointer-events: none; border: 1px solid #dedede; border-radius: inherit; }
.m-guide.guide-source-layout .guide-source-media picture, .m-guide.guide-source-layout .guide-source-media img { display: block; width: 100%; height: 100%; object-fit: cover; margin: 0; border-radius: 0; box-shadow: none; }
.m-guide.guide-source-layout .guide-source-row[data-text-inset="true"] .guide-source-text { padding-top: 16px; }
@media (max-width: 767.98px) {
 .m-guide.guide-source-layout .guide-source-media[data-mobile-radius="8"] { border-radius: 8px; }
 .m-guide.guide-source-layout .guide-source-text, .m-guide.guide-source-layout .guide-source-media { flex: none; }
 .m-guide.guide-source-layout .guide-source-row[data-guide-picture-mobile-first="true"] .guide-source-media { order: -1; }
}
@media (min-width: 768px) {
 .m-guide.guide-source-layout.sec-group[data-block="guide"] > .guide-full-width-heading > h2 { font-size: 28px; }
 .m-guide.guide-source-layout .guide-source-row { padding: 48px 20px; }
 .m-guide.guide-source-layout .guide-source-columns { flex-direction: row; }
 .m-guide.guide-source-layout .guide-source-row[data-picture-align="start"] .guide-source-columns { align-items: flex-start; }
 .m-guide.guide-source-layout .guide-source-row[data-guide-picture-side="left"] .guide-source-media { order: -1; }
 .m-guide.guide-source-layout .guide-source-row[data-text-inset="true"] .guide-source-text { padding-top: 32px; }
 .m-guide.guide-source-layout .guide-source-text > .cta-row { margin-top: 32px; }
 .m-guide.guide-source-layout .guide-source-row[data-cta-gap="0"] .guide-source-text > .cta-row { margin-top: 0; }
 .m-guide.guide-source-layout .guide-source-row[data-bottom-padding="60"] { padding-bottom: 60px; }
}
@media (min-width: 810px) {
 .m-guide.guide-source-layout .guide-source-text { font-size: 16px; }
 .m-guide.guide-source-layout .guide-source-text h3 { font-size: 24px; }
 .m-guide.guide-source-layout .guide-source-text > * + * { margin-top: 24px; }
 .m-guide.guide-source-layout .guide-source-text > h3 { margin-top: 36px; }
 .m-guide.guide-source-layout .guide-source-text > :first-child { margin-top: 0; }
}
@media (min-width: 1280px) {
 .m-guide.guide-source-layout.sec-group[data-block="guide"] > .guide-full-width-heading > h2 { font-size: 32px; }
 .m-guide.guide-source-layout .guide-source-row { padding-inline: 120px; }
 .m-guide.guide-source-layout .guide-source-columns { width: 1200px; gap: 40px; }
 .m-guide.guide-source-layout .guide-source-text h3 { font-size: 22px; }
}
@media (min-width: 1100px) {
 .m-guide.guide-source-layout.guide-source-wide-media .guide-source-row { justify-content: flex-start; }
 .m-guide.guide-source-layout.guide-source-wide-media .guide-source-columns { align-items: flex-start; gap: 40px; width: min(1280px, calc(100vw - 80px)); max-width: min(1280px, calc(100vw - 80px)); margin-left: auto; margin-right: auto; overflow: visible; }
 .m-guide.guide-source-layout.guide-source-wide-media .guide-source-text { box-sizing: border-box; flex: 0 0 calc(48% - 20px); width: calc(48% - 20px); max-width: calc(48% - 20px); height: auto; min-height: 0; max-height: none; overflow: visible; }
 .m-guide.guide-source-layout.guide-source-wide-media .guide-source-media { aspect-ratio: auto; box-sizing: border-box; flex: 0 0 calc(52% - 20px); width: calc(52% - 20px); max-width: calc(52% - 20px); align-self: flex-start; display: block; height: auto; min-height: 0; max-height: none; }
 .m-guide.guide-source-layout.guide-source-wide-media .guide-source-media picture, .m-guide.guide-source-layout.guide-source-wide-media .guide-source-media img { height: auto; object-fit: contain; image-rendering: auto; margin-left: auto; margin-right: auto; }
}
/* Source rich text presets, scoped to this variant: tables, quotations and list markers. */
.m-guide.guide-source-layout .guide-source-text > .rich-table-wrap { margin-top: 0; border: 0; border-radius: 0; background: transparent; font: 400 12px/normal sans-serif; font-feature-settings: normal; }
.m-guide.guide-source-layout .rich-table-wrap table { width: 100%; min-width: 0; border-collapse: separate; border-spacing: 0; table-layout: auto; word-break: normal; border: 1px solid #99999940; border-radius: 8px; overflow: hidden; }
.m-guide.guide-source-layout .rich-table-wrap :is(th, td) { min-width: 16ch; padding: 10px; border: 0; vertical-align: top; overflow-wrap: anywhere; }
.m-guide.guide-source-layout .rich-table-wrap th { background: #9999991a; }
.m-guide.guide-source-layout .rich-table-wrap tr + tr :is(th, td) { border-top: 1px solid #99999940; }
.m-guide.guide-source-layout .rich-table-wrap tr > :is(th, td) + :is(th, td) { border-left: 1px solid #99999940; }
.m-guide.guide-source-layout .rich-table-wrap p { margin: 0; font: 400 14px/1.5em "Guide Source Inter", "Guide Source Inter Placeholder", sans-serif; font-feature-settings: "blwf" on, "cv09" on, "cv03" on, "cv04" on, "cv11" on; }
.m-guide.guide-source-layout .guide-source-text > blockquote { position: relative; margin-top: 20px; padding: 0 0 0 22px; color: #999; font: italic 400 16px/1.8em "Guide Source Inter", "Guide Source Inter Placeholder", sans-serif; font-feature-settings: normal; }
.m-guide.guide-source-layout .guide-source-text > blockquote::before { content: " "; background: #ddd; border-radius: 1px; width: 2px; height: 100%; display: block; position: absolute; top: 0; left: 0; }
.m-guide.guide-source-layout .guide-source-text :is(ul, ol) { margin-top: 0; padding-left: 0; list-style: none; }
.m-guide.guide-source-layout .guide-source-text li { position: relative; counter-increment: list-item; padding-inline-start: 2ch; list-style: none; }
.m-guide.guide-source-layout .guide-source-text ul > li::before { content: "•"; position: absolute; inset-inline-start: 0; }
.m-guide.guide-source-layout .guide-source-text ol > li::before { content: counter(list-item) "."; font-variant-numeric: tabular-nums; position: absolute; inset-inline-start: 0; }
@media (min-width: 810px) { .m-guide.guide-source-layout .rich-table-wrap p { font-size: 16px; } }
/* The source quote preset gives bold text its normal700 face; explicit emphasis keeps bold-italic. */
.m-guide.guide-source-layout .guide-source-text blockquote strong { font-style: normal; font-weight: 700; }
.m-guide.guide-source-layout .guide-source-text blockquote em > strong { font-style: italic; }

/* Source quote paragraph preset reapplies the source Inter OpenType features. */
.m-guide.guide-source-layout .guide-source-text blockquote p { font-feature-settings: "blwf" on, "cv09" on, "cv03" on, "cv04" on, "cv11" on; }

/* Source wide-media controller: original-ratio frame remains while the intrinsic image may overflow it. */
@media (min-width: 1100px) {
 .m-guide.guide-source-layout.guide-source-wide-media .guide-source-media { aspect-ratio: var(--guide-picture-ratio, auto); overflow: visible; }
 .m-guide.guide-source-layout.guide-source-wide-media .guide-source-media > picture { position: absolute; inset: 0; height: 100%; }
}

/* hero-stat@0.1.0 */
.m-hero-stat{padding:2rem 0}
.m-hero-stat .stat{font-size:2.5rem;font-weight:600}

/* influencer-wall@1.0.0 */
/* influencer-wall — lifted from tool.css:338-363,372-373 under the .m-influencer-wall prefix
   (module-language.md §9 rule 1). Every DECLARATION is byte-identical to src/styles; only the selector
   prefix differs, and src/lib/bx/seed-parity.test.ts fails when one drifts.

   The grey band, the two sliding rows and the pill cards. ONE rename: keyframe names are global, so
   @keyframes wall-slide is m-influencer-wall-slide here (module-language.md §9 rule 2) and the animation
   shorthand names the new identifier. The animation itself is unchanged.

   STAYS IN SITE CSS — a page-kind, positional or document-order scope that no module selector can
   carry. R9 converts these to the layout attributes the renderer stamps (data-surface, data-next,
   data-variant); until then src/styles/*.css is still the live stylesheet and owns them:
     tool.css:341 main[data-kind] .sec-wall
 */
/* ---------- influencer wall: grey band, two rows of pill cards sliding (CSS only, pauses on hover) ----------
   markup: .wall > ul.wall-row[data-row] > li.wall-card (avatar link, name, followers) — production: 320×120 white cards, radius 16, on every breakpoint */
.m-influencer-wall { background: var(--c-bg); overflow: hidden; }
.m-influencer-wall > h2 { margin-bottom: 40px; }
@media (min-width: 1280px) { .m-influencer-wall > h2 { line-height: 1.3; } }
.m-influencer-wall > .wall {
  grid-column: 1 / -1; width: max-content; max-width: none; justify-self: start;
  display: grid; grid-auto-flow: row; gap: 12px;
  animation: m-influencer-wall-slide 60s linear infinite alternate;
  will-change: transform;
}
.m-influencer-wall > .wall:hover, .m-influencer-wall > .wall:focus-within { animation-play-state: paused; }
@keyframes m-influencer-wall-slide { from { transform: translateX(0); } to { transform: translateX(min(0px, calc(100vw - 100%))); } }
.m-influencer-wall .wall-row { display: flex; gap: 12px; width: max-content; }
.m-influencer-wall .wall-row[data-row="1"] { margin-left: -160px; }   /* the second row starts a half-card earlier, like production's staggered rows */
.m-influencer-wall .wall-card {
  display: grid; grid-template-columns: 80px 1fr; grid-template-rows: auto auto; column-gap: 16px; align-items: center;
  width: 320px; padding: 20px;
  background: var(--c-white); border-radius: var(--r-md); box-shadow: 0 0 2px 0 rgba(0, 0, 0, .1);
}
.m-influencer-wall .wall-card > a { grid-row: 1 / span 2; width: 80px; height: 80px; border-radius: 50%; overflow: hidden; display: block; }
.m-influencer-wall .wall-card > a:focus-visible { outline-offset: 0; }
.m-influencer-wall .wall-card img { width: 80px; height: 80px; object-fit: cover; }
.m-influencer-wall .wall-card :is(.wall-name, .wall-followers) { margin: 0; }   /* the name and the follower count are their own <p> blocks, as on production */
.m-influencer-wall .wall-card .wall-name { font-size: 20px; line-height: 1.2; color: var(--c-text-2); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; align-self: end; }
.m-influencer-wall .wall-card .wall-followers { font-size: 24px; line-height: 1.2; font-weight: 500; color: var(--c-text); white-space: nowrap; align-self: start; margin-top: 8px; }
/* the follower stat's two halves: the template writes <b class="wall-count"> + <span class="wall-unit"> (the
   split the module does itself, pinned by seed-parity.test.ts), so the module owes them their rules — count
   dark and larger, unit grey and smaller, an empty unit hidden. Lifted from src/styles/components.css:29-32. */
.m-influencer-wall .wall-followers .wall-count { font-weight: 500; color: var(--c-text); }
.m-influencer-wall .wall-followers .wall-unit { font-weight: 400; color: var(--c-text-3); font-size: 0.66em; }
.m-influencer-wall .wall-followers .wall-unit:empty { display: none; }
@media (prefers-reduced-motion: reduce) { .m-influencer-wall > .wall { animation: none; } }

/* latest-posts@1.0.0 */
/* lifted from src/styles/blog.css:72, :76, :79-80, :98 (second selector), :101, :193-202, :208, :214, :218,
   :227-229 — the sidebar list beside a post.
   Rewrites forced by module-language.md §9 rule 1 (`main` may not appear); the page-kind qualifier is carried
   by the module's identity, because this module only renders on a post page:
     main[data-kind] .sec.sec-posts                     → .m-latest-posts
     .sec-posts > h3                                    → .m-latest-posts > h3
     main[data-kind="post"] .post-grid                  → .m-latest-posts .post-grid
     main[data-kind="post"] > #also …                   → .m-latest-posts …
   The generic .post-grid declaration (blog.css:79) is included first so :80's 260px override still lands
   after it, and :194's own grid-template-columns after that — source order is preserved. */
.m-latest-posts { display: block; padding-block: 0; }   /* beats tool.css `main[data-kind] .sec` rhythm */
.m-latest-posts > h3 { font-size: 22px; font-weight: 700; line-height: 1.3; margin-bottom: 16px; }
.m-latest-posts .post-grid { display: grid; gap: 16px; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); margin: 0; padding: 0; list-style: none; }
.m-latest-posts .post-grid { grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); }
/* "You may also be interested in": horizontal cards (date · thumb · title · chevron) — same look in the desktop sidebar and stacked on phone */
.m-latest-posts .post-grid { grid-template-columns: minmax(0, 1fr); gap: 20px; }
.m-latest-posts .post-card { position: relative; display: grid; grid-template-columns: 120px minmax(0, 1fr); grid-template-areas: "date date" "cover title"; column-gap: 16px; row-gap: 8px; align-items: start; padding: 16px; border-radius: var(--blog-r-card); box-shadow: none; overflow: visible; }
.m-latest-posts .post-card:hover { transform: none; box-shadow: var(--blog-shadow-card); }
.m-latest-posts .post-cover { grid-area: cover; height: 77px; border-radius: 2px; overflow: hidden; }
.m-latest-posts .post-body { display: contents; }
.m-latest-posts .post-date { grid-area: date; }
.m-latest-posts .post-title { grid-area: title; min-height: 0; }
.m-latest-posts .post-card::after { content: ""; width: 8px; height: 8px; border-right: 1.5px solid currentColor; border-bottom: 1.5px solid currentColor; transform: rotate(-45deg); flex: none; margin-right: 3px; position: absolute; right: 22px; bottom: 18px; }
.m-latest-posts > h3 { text-align: center; }
.m-latest-posts { padding-block: 20px 0; }   /* Framer phone: ~20px between the article card and "You may also…" (32 on desktop) */
@media (min-width: 810px) {
  .m-latest-posts { padding-block: 48px 0; }
  .m-latest-posts { padding-block: 32px 0; }
  .m-latest-posts .post-grid { grid-template-columns: repeat(auto-fill, minmax(320px, 1fr)); }
}
@media (min-width: 1280px) {
  .m-latest-posts { grid-column: 4; grid-row: 1 / span 2; width: auto; padding: 0; margin: 0; }
  .m-latest-posts > h3 { text-align: left; }
  .m-latest-posts .post-grid { grid-template-columns: minmax(0, 1fr); }
}

/* latest-posts-paged@1.0.0 */
/* lifted from src/styles/blog.css:72-75, :77, :79, :88, :101-103 — the posts band, the gradient h2, the card
   grid, the index's own vertical rhythm and the 57px card-body foot.
   Rewrites forced by module-language.md §9 rule 1 (`main` may not appear); the page-kind qualifier is carried
   by the module's identity, because this module only renders on the blog index:
     main[data-kind] .sec.sec-posts                       → .m-latest-posts-paged
     main[data-kind="blogIndex"] .sec.sec-posts           → .m-latest-posts-paged
     main[data-kind="blogIndex"] > #latest                → .m-latest-posts-paged
     .sec-posts > h2 (+ the gradient rule)                → .m-latest-posts-paged > h2
     .post-grid                                           → .m-latest-posts-paged .post-grid
     main[data-kind="blogIndex"] > #latest .post-body      → .m-latest-posts-paged .post-body
   blog.css:106-116 (Load More + the pager) is carried by the Pagination primitive and is not duplicated. */
.m-latest-posts-paged { display: block; padding-block: 0; }   /* beats tool.css `main[data-kind] .sec` rhythm */
.m-latest-posts-paged { width: min(100% - 2 * var(--gutter), 1368px); }
.m-latest-posts-paged > h2 { font-size: 24px; line-height: 1.2; font-weight: 700; text-align: center; margin: 0 auto 20px; width: fit-content; }
.m-latest-posts-paged > h2 { background: linear-gradient(90deg, var(--c-brand) 0%, var(--c-text) 100%); -webkit-background-clip: text; background-clip: text; color: transparent; }
.m-latest-posts-paged { padding-block: 72px 32px; }
.m-latest-posts-paged .post-grid { display: grid; gap: 16px; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); margin: 0; padding: 0; list-style: none; }
.m-latest-posts-paged .post-body { padding-bottom: 57px; }
@media (min-width: 810px) {
  .m-latest-posts-paged { padding-block: 48px 0; }
  .m-latest-posts-paged > h2 { font-size: 34px; margin-bottom: 32px; }
  .m-latest-posts-paged { padding-block: 72px 32px; }
}

/* more-posts@1.0.0 */
/* lifted from src/styles/blog.css:72, :74, :79-80, :101-102, :203-207, :209-211, :219-220, :230 —
   "Discover more blogs" under a post.
   Rewrites forced by module-language.md §9 rule 1 (`main` may not appear); the page-kind qualifier is carried
   by the module's identity, because this module only renders on a post page:
     main[data-kind] .sec.sec-posts        → .m-more-posts
     .sec-posts > h2                       → .m-more-posts > h2
     main[data-kind="post"] .post-grid     → .m-more-posts .post-grid
     main[data-kind="post"] > #more …      → .m-more-posts …
   The h2 keeps the plain dark treatment (:205 undoes the index/tag gradient, which never applied here). */
.m-more-posts { display: block; padding-block: 0; }   /* beats tool.css `main[data-kind] .sec` rhythm */
.m-more-posts > h2 { font-size: 24px; line-height: 1.2; font-weight: 700; text-align: center; margin: 0 auto 20px; width: fit-content; }
.m-more-posts .post-grid { display: grid; gap: 16px; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); margin: 0; padding: 0; list-style: none; }
.m-more-posts .post-grid { grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); }
/* "Discover more blogs" (post) — plain dark heading, 4-up cards with a 4:3 cover and 42px above + below the copy */
.m-more-posts { padding-block: 32px 40px; }
.m-more-posts > h2 { color: var(--c-text); background: none; -webkit-text-fill-color: currentColor; margin-bottom: 24px; }
.m-more-posts .post-cover { height: auto; min-height: 0; aspect-ratio: 4 / 3; overflow: hidden; }   /* min-height 0: the aspect-ratio box must not grow to a taller image */
.m-more-posts .post-body { padding: 42px 16px; }
@media (max-width: 809px) {
  .m-more-posts > h2 { font-size: 20px; line-height: 26px; margin-bottom: 12px; }   /* Framer phone: same 20/26 as "You may also be interested in", 12px above the stack */
}
@media (min-width: 810px) {
  .m-more-posts { padding-block: 48px 0; }
  .m-more-posts > h2 { font-size: 34px; margin-bottom: 32px; }
  .m-more-posts > h2 { font-size: 36px; }
  .m-more-posts { padding-block: 40px 40px; }
}
@media (min-width: 1280px) { .m-more-posts { grid-column: 1 / -1; grid-row: 3; } }

/* popular-posts@1.0.0 */
/* lifted from src/styles/blog.css:72-75, :79, :101-102 — the posts band, its gradient h2 and the card grid.
   Rewrites forced by module-language.md §9 rule 1 (`main` may not appear): the page-kind qualifier is carried
   by the module's identity instead, because this module only ever renders on the blog index.
     main[data-kind] .sec.sec-posts                    → .m-popular-posts
     main[data-kind="blogIndex"] .sec.sec-posts        → .m-popular-posts
     .sec-posts > h2                                   → .m-popular-posts > h2
     main[data-kind="blogIndex"] .sec-posts > h2       → .m-popular-posts > h2
     .post-grid                                        → .m-popular-posts .post-grid
   Declaration order is source order, so blog.css:73's width still lands after :72. */
.m-popular-posts { display: block; padding-block: 0; }   /* beats tool.css `main[data-kind] .sec` rhythm */
.m-popular-posts { width: min(100% - 2 * var(--gutter), 1368px); }
.m-popular-posts > h2 { font-size: 24px; line-height: 1.2; font-weight: 700; text-align: center; margin: 0 auto 20px; width: fit-content; }
.m-popular-posts > h2 { background: linear-gradient(90deg, var(--c-brand) 0%, var(--c-text) 100%); -webkit-background-clip: text; background-clip: text; color: transparent; }
.m-popular-posts .post-grid { display: grid; gap: 16px; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); margin: 0; padding: 0; list-style: none; }
@media (min-width: 810px) {
  .m-popular-posts { padding-block: 48px 0; }
  .m-popular-posts > h2 { font-size: 34px; margin-bottom: 32px; }
}

/* post-article@1.0.0 */
/* lifted from src/styles/blog.css:139-182 (the article card, the cover and the prose rhythm), :213
   (the 810px stack gap) and :225 (the 1280px column placement).
   Rewrites forced by module-language.md §9 rule 1 (`main` may not appear in module CSS):
     article.post                            → .m-post-article                (the module root IS article.post)
     main[data-kind="post"] > article.post   → .m-post-article                (:225, the grid placement)
   Rules that STAY in src/styles/blog.css because they style the surface, not this module:
     :137-138  main[data-kind="post"] { display:flex; padding-top }  and  > .blog-cta { order:10 }
     :171-172  the phone padding-top on main
     :222-232  the 1280px grid declaration on main and the placement of the OTHER blog modules
   blog.css:165-167 (the in-article youtubeEmbed frame) is already carried by the RichText primitive
   (src/primitives/RichText/styles.css) and is not duplicated here. */
.m-post-article { width: min(100% - 2 * var(--gutter), 700px); margin-inline: auto; background: var(--c-white); border-radius: var(--blog-r-card) var(--blog-r-card) 0 0; padding: 44px 24px 34px; display: flex; flex-direction: column; gap: 28px; }
.m-post-article h1 { font-size: var(--fs-h1); font-weight: 700; line-height: 1.3; text-wrap: pretty; }
.m-post-article .post-hero { border-radius: var(--blog-r-card); overflow: hidden; aspect-ratio: 16 / 9; background: var(--c-line); }
.m-post-article .post-hero picture, .m-post-article .post-hero img { width: 100%; height: 100%; }
.m-post-article .post-hero img { object-fit: cover; }
.m-post-article .prose { max-width: none; font-size: 16px; line-height: 1.5; }
.m-post-article .prose > :first-child { margin-top: 0; }
.m-post-article .prose p { margin: 24px 0 0; }
.m-post-article .prose h2 { font-size: 24px; font-weight: 600; line-height: 1.3; margin: 42px 0 0; text-wrap: auto; }   /* Framer wraps article headings plainly (base.css balances h2) */
.m-post-article .prose h3 { font-size: 22px; font-weight: 700; line-height: 1.3; margin: 36px 0 0; }
.m-post-article .prose h4 { font-size: 20px; font-weight: 600; line-height: 1.3; margin: 32px 0 0; }
.m-post-article .prose h2 + p, .m-post-article .prose h3 + p, .m-post-article .prose h4 + p { margin-top: 24px; }
/* Framer lists: 32px above (both breakpoints); ol = a left-aligned tabular marker column sized to the digit count (2ch for 1–9 items, 3ch from 10) */
.m-post-article .prose ul, .m-post-article .prose ol { margin: 32px 0 0; padding-left: 20px; }
.m-post-article .prose ol { list-style: none; padding-left: 0; counter-reset: blog-item; }
.m-post-article .prose ol > li { position: relative; counter-increment: blog-item; padding-inline-start: 2ch; }
.m-post-article .prose ol > li::before { content: counter(blog-item) "."; position: absolute; inset-inline-start: 0; top: 0; font-variant-numeric: tabular-nums; }
.m-post-article .prose ol:has(> li:nth-child(10)) > li { padding-inline-start: 3ch; }
.m-post-article .prose li { margin: 0; }
.m-post-article .prose li + li { margin-top: 0; }
.m-post-article .prose li > :is(ul, ol) { margin-top: 0; }
.m-post-article .prose a { color: var(--blog-link); text-decoration: none; }
.m-post-article .prose a:hover { text-decoration: underline; text-underline-offset: 3px; }
.m-post-article .prose h2 a, .m-post-article .prose h3 a { color: var(--blog-link); }
.m-post-article .prose img, .m-post-article .prose picture { border-radius: var(--blog-r-card); margin: 24px 0 0; width: 100%; height: auto; }
.m-post-article .prose picture img { margin: 0; }
.m-post-article .prose blockquote { margin: 24px 0 0; padding-left: 16px; border-left: 3px solid var(--c-line); color: var(--c-text-2); }
.m-post-article .prose strong { font-weight: 600; }
/* Framer scales the article down on phone: h1 24/31.2, h2+h3 20/26, body 14/21 — and tightens the rhythm (stack gap 24, p 21, h2 30, h3 27; lists stay 32) */
@media (max-width: 809px) {
  .m-post-article { padding: 32px 24px 24px; gap: 24px; }
  .m-post-article h1 { font-size: 24px; line-height: 1.3; }
  .m-post-article .prose { font-size: 14px; line-height: 1.5; }
  .m-post-article .prose p { margin-top: 21px; }
  .m-post-article .prose h2, .m-post-article .prose h3 { font-size: 20px; line-height: 1.3; }
  .m-post-article .prose h2 { margin-top: 30px; }
  .m-post-article .prose h3 { margin-top: 27px; }
  .m-post-article .prose h4 { font-size: 18px; margin-top: 27px; }
  .m-post-article .prose h2 + p, .m-post-article .prose h3 + p, .m-post-article .prose h4 + p { margin-top: 21px; }
}
@media (min-width: 810px) { .m-post-article { gap: 34px; } }
@media (min-width: 1280px) { .m-post-article { grid-column: 2; grid-row: 1; width: auto; margin-inline: 0; } }

/* post-card@1.0.0 */
/* lifted from src/styles/blog.css:81-99 (the card itself) and :233 (the reduced-motion opt-out).
   The module is included, so its root carries data-module="post-card" but no .m- class — the prefix is
   [data-module="post-card"], which module-language.md §9 rule 1 allows.
   Rewrites forced by §9 rule 1 (`main` may not appear):
     blog.css:91-92  main[data-kind] .sec.sec-posts .post-card .post-title a  →  [data-module="post-card"] .post-title a
       Declarations unchanged. NOTE for R9: neither form wins against tool.css:46
       (main[data-kind] .sec :is(…, li:not(.card,…), …) a:not([class]):not([href*="?cta="]), specificity
       (0,5,3)) — blog.css:91 is (0,5,2) and this rewrite is (0,2,1). The rule is inert today and stays
       inert here; making it effective would CHANGE the rendered colour, so it is not "fixed" in this pass.
     blog.css:98 second selector (main[data-kind="post"] > #also .post-card::after) is the `also` chevron and
       belongs to latest-posts, not here.
   The grid that holds the cards (blog.css:79-80) belongs to the list modules, not here. */
[data-module="post-card"] { display: flex; flex-direction: column; background: var(--c-white); border-radius: var(--r-md); box-shadow: var(--blog-shadow-card); overflow: hidden; transition: transform .2s ease, box-shadow .2s ease; }
[data-module="post-card"]:hover { transform: translateY(-2px); box-shadow: 0 8px 24px 0 rgba(0, 0, 0, 0.12); }
[data-module="post-card"] .post-cover { display: block; height: var(--blog-cover-h); background: var(--c-line); }
[data-module="post-card"] .post-cover picture, [data-module="post-card"] .post-cover img { width: 100%; height: 100%; }
[data-module="post-card"] .post-cover img { object-fit: cover; }
/* Framer card body: 12 · title (always two 23.4px lines) · 8 · Read More · 30 (popular) / 57 (latest, tag) / 42 (post "discover more") */
[data-module="post-card"] .post-body { flex: 1; display: flex; flex-direction: column; gap: 8px; padding: 12px 16px 30px; }
[data-module="post-card"] .post-date { font-size: var(--fs-body-3); color: var(--c-text-2); line-height: 1.5; }
[data-module="post-card"] .post-title { font-size: 18px; font-weight: 500; line-height: 1.3; min-height: 2.6em; color: var(--c-text); display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
[data-module="post-card"] .post-title a { color: inherit; }
[data-module="post-card"] .post-title a:hover { color: var(--c-brand); }
/* tag-archive card extras (oracle-measured): the meta description under the title, clamped to 2 lines like production
   (.framer-rgmhe7 -webkit-line-clamp:2); chips sit in the body's 8px gap, so the post-page 1.25rem margin would double it */
[data-module="post-card"] .post-excerpt { font-size: var(--fs-body-3); line-height: 1.5; color: var(--c-text-2); display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
[data-module="post-card"] .post-body .post-tags { margin: 0; }
[data-module="post-card"] .post-more { align-self: flex-end; display: inline-flex; align-items: center; gap: 8px; font-size: 16px; font-weight: 500; line-height: 1.3; color: var(--c-text); }
[data-module="post-card"] .post-more::after { content: ""; width: 8px; height: 8px; border-right: 1.5px solid currentColor; border-bottom: 1.5px solid currentColor; transform: rotate(-45deg); flex: none; margin-right: 3px; }
[data-module="post-card"] .post-more:hover { color: var(--c-brand); }
@media (prefers-reduced-motion: reduce) { [data-module="post-card"] { transition: none; } }

/* post-cta@1.1.0 */
/* lifted from src/styles/blog.css:118-134 (the photo band) and :184-192, :215-217, :226 (the after-article pair).
   One module renders two shapes, so both roots keep the class they carry today (.blog-cta / .after-article)
   and every rule is anchored on `.m-post-cta` PLUS that class — no declaration is invented and neither shape
   can bleed into the other.
   Prefix swaps (module-language.md §9 rule 1 — `main` may not appear in module CSS):
     .blog-cta       → .m-post-cta.blog-cta
     .after-article  → .m-post-cta.after-article
   Rules that STAY in src/styles/blog.css because they are scoped to the page kind, not to this module:
     :138  main[data-kind="post"] > .blog-cta { order: 10 }      (places the module in the page's flex flow)
     :231  main[data-kind="post"] > .blog-cta { grid-column/row } (the 1280px page grid)
     :191  main[data-kind="post"]:lang(en) .after-article::after { content: "Be the first…" }
     :192  main[data-kind="post"]:lang(es) .after-article::after
       The like-box copy was previously lifted as `.m-post-cta.after-article:lang(en)::after`, which drops
       the `main[data-kind="post"]` ancestor and takes the selector from 0-3-2 to 0-3-1. That is a rewrite,
       not a lift: it widens the rule to this module on ANY page kind and re-orders it against every other
       ::after content rule at 0-3-2. The module root keeps its `after-article` class, so blog.css:191-192
       still match it exactly as they do today — the rule needs no module copy at all. */
/* ───────── CTA banner (photo band, copy pinned to the bottom; Framer: 360 tall on phone, 560 on desktop, image top-aligned) ───────── */
.m-post-cta.blog-cta { position: relative; overflow: hidden; isolation: isolate; display: flex; flex-direction: column; justify-content: flex-end; align-items: center; min-height: 360px; padding: 48px var(--gutter) 65px; color: var(--c-white); text-align: center; }
.m-post-cta .blog-cta-art { position: absolute; inset: 0; z-index: -1; background: var(--c-text-3); }
.m-post-cta .blog-cta-art picture, .m-post-cta .blog-cta-art img { position: absolute; inset: 0; width: 100%; height: 100%; }
.m-post-cta .blog-cta-art img { object-fit: cover; object-position: 50% 0%; }
/* Framer paints only the wide banner (the last of the three CMS images); the two portrait extras never render on the live template */
.m-post-cta .blog-cta-art picture:not(:last-child) { display: none; }
.m-post-cta .blog-cta-art::after { content: ""; position: absolute; inset: 0; background: rgba(0, 0, 0, 0.12); }
.m-post-cta .blog-cta-text { display: flex; flex-direction: column; align-items: center; gap: 24px; max-width: 560px; }
.m-post-cta .blog-cta-text h2 { font-family: var(--font-display); font-size: 24px; line-height: 1.2; font-weight: 400; letter-spacing: normal; color: var(--c-white); text-shadow: 0 1px 8px rgba(0, 0, 0, 0.25); white-space: pre-line; }   /* pre-line: Framer keeps the CMS hard break (es "…ensueño\ncon APOB") */
.m-post-cta .blog-cta-text > p:not(.cta-row) { order: 3; margin-top: -16px; font-size: var(--fs-body-3); line-height: 1.2; color: var(--c-bg); }
.m-post-cta .blog-cta-text .cta-row { margin-top: 0; }
.m-post-cta .blog-cta-text .cta-row .btn { background: var(--c-white); color: var(--c-text); border-radius: var(--blog-r-card); padding: 12px 32px; font-size: 20px; font-weight: 700; line-height: 1.4; }
.m-post-cta .blog-cta-text .cta-row .btn:hover { background: var(--c-bg); }
.m-post-cta .blog-cta-text .cta-row .btn:focus-visible { outline-color: var(--c-white); }
@media (min-width: 810px) { .m-post-cta.blog-cta { min-height: 480px; padding-bottom: 80px; } .m-post-cta .blog-cta-text h2 { font-size: 30px; } }
@media (min-width: 1280px) { .m-post-cta.blog-cta { min-height: 560px; padding-bottom: 100px; } .m-post-cta .blog-cta-text h2 { font-size: 36px; } }

/* the two links after the article sit inside the white card (Framer: grey "back" + brand "try", 43px tall, r4), then the like-box placeholder */
.m-post-cta.after-article { position: relative; width: min(100% - 2 * var(--gutter), 700px); margin-inline: auto; background: var(--c-white); border-radius: 0 0 var(--blog-r-card) var(--blog-r-card); padding: 0 24px 44px; display: flex; flex-wrap: wrap; justify-content: center; gap: 12px; }
.m-post-cta.after-article .btn { padding: 12px 24px; font-size: 16px; border-radius: var(--blog-r-card); flex: 1 1 100%; }
.m-post-cta.after-article .btn-ghost { background: rgba(153, 153, 153, 0.65); color: var(--c-white); border: 0; font-weight: 500; opacity: 0.85; }
.m-post-cta.after-article .btn-ghost:hover { opacity: 1; }
.m-post-cta.after-article .btn-primary { font-weight: 700; }
/* Framer's like widget ("Be the first to like this." under a greyscale 😀 in a 44px white disc; box 118 tall on phone / 124 on desktop, 24 / 34px under the buttons) — static, no JS; copy only where we know Framer's string */
.m-post-cta.after-article::after { content: ""; order: 3; flex: 1 1 100%; box-sizing: border-box; height: 118px; margin-top: 12px; display: flex; flex-direction: column; justify-content: flex-end; align-items: center; padding: 18px 20px 17px; border: 1px solid var(--blog-like-line); border-radius: var(--blog-r-card); background: var(--blog-like-bg) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 44 44'%3E%3Ccircle cx='22' cy='22' r='21.5' fill='%23fff' stroke='%23d4d4d4'/%3E%3Ccircle cx='22' cy='22' r='13.5' fill='%23c4c4c4'/%3E%3Ccircle cx='22' cy='22' r='13.5' fill='none' stroke='%23a9a9a9' stroke-width='.8'/%3E%3Cellipse cx='17.4' cy='18.2' rx='1.7' ry='2.9' fill='%23fff'/%3E%3Cellipse cx='26.6' cy='18.2' rx='1.7' ry='2.9' fill='%23fff'/%3E%3Cpath d='M14.6 24.6h14.8a7.4 7.4 0 0 1-14.8 0z' fill='%23fff'/%3E%3Cpath d='M16.4 27h11.2a5.6 5.6 0 0 1-11.2 0z' fill='%23888'/%3E%3C/svg%3E") no-repeat 50% 18px / 44px 44px; font-size: 16px; line-height: 24px; color: var(--c-text-3); text-align: center; }
@media (min-width: 810px) {
  .m-post-cta.after-article { padding-bottom: 44px; gap: 16px; }
  .m-post-cta.after-article .btn { flex: 0 1 auto; min-width: 200px; }
  .m-post-cta.after-article::after { margin-top: 18px; height: 124px; padding: 22px 20px 24px; background-position: 50% 22px; }
}
@media (min-width: 1280px) { .m-post-cta.after-article { width: auto; margin: 0 0 32px; } }

/* post-tags@1.0.0 */
/* lifted verbatim from src/styles/blog.css:235-242 — the `Blog Tag Chip` row.
   The module is included (by post-article and post-card), so its root carries data-module but no .m- class:
   the prefix is [data-module="post-tags"], which module-language.md §9 rule 1 allows. */
/* tag chips under a post title — production's `Blog Tag Chip` row */
[data-module="post-tags"] { display: flex; flex-wrap: wrap; gap: .5rem; margin: 0 0 1.25rem; }
[data-module="post-tags"] .tag-chip {
  display: inline-block; padding: .25rem .7rem; border: 1px solid var(--line, #e3e3e3);
  border-radius: 999px; font-size: .8125rem; line-height: 1.5; text-decoration: none;
  color: var(--ink-2, #555); background: var(--surface-2, transparent);
}
[data-module="post-tags"] .tag-chip:hover { border-color: var(--ink-2, #555); color: var(--ink, #111); }

/* prompts-table@1.0.0 */
/* prompts-table — lifted from tool.css:379 under the .m-prompts-table prefix
   (module-language.md §9 rule 1). Every DECLARATION is byte-identical to src/styles; only the selector
   prefix differs, and src/lib/bx/seed-parity.test.ts fails when one drifts.

   Only the band's own grey ground. The table markup comes from the RichText primitive and
   .rich-table-wrap is that primitive's CSS (src/primitives/RichText/styles.css).

 */
.m-prompts-table { background: var(--c-bg); }

/* pros-cons@1.0.0 */
/* pros-cons — lifted from tool.css:375-378,388-408 under the .m-pros-cons prefix
   (module-language.md §9 rule 1). Every DECLARATION is byte-identical to src/styles; only the selector
   prefix differs, and src/lib/bx/seed-parity.test.ts fails when one drifts.

   The grey band and the two white columns, including the icon-titled first h3 of each column.

 */
/* ---------- pros & cons: two white boxes; the column label ("Pros" / 長所) is the FIRST h3 of each column's rich text,
   rendered as production's centred 24/500 icon title over a rule; every later h3 is an item title (22/700), p 16/24 with
   700 bold runs and blue links. The rich text may arrive bare (.pros > h3) or wrapped (.pros > div > h3) — both hooks are covered. ---------- */
.m-pros-cons { background: var(--c-bg); }
.m-pros-cons > .proscons-grid { display: grid; gap: 12px; grid-template-columns: minmax(0, 1fr); }
@media (min-width: 810px) { .m-pros-cons > .proscons-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); align-items: start; } }
.m-pros-cons .proscons-grid > div { background: var(--c-white); border: 1px solid var(--c-line); border-radius: var(--r-sm); padding: 20px 20px 16px; }
.m-pros-cons .proscons-grid > div > div:only-child { display: contents; }   /* a wrapper div around the rich text is transparent to the rules below */
.m-pros-cons .proscons-grid > div > h3:first-child, .m-pros-cons .proscons-grid > div > div:only-child > h3:first-child {
  display: flex; align-items: center; justify-content: center; gap: 8px; margin: 0;
  font-size: 18px; line-height: 1.2; font-weight: 500; text-align: center; padding-bottom: 12px; border-bottom: 1px solid var(--c-line);
}
@media (min-width: 810px) { .m-pros-cons .proscons-grid > div > h3:first-child, .m-pros-cons .proscons-grid > div > div:only-child > h3:first-child { font-size: 24px; } }
.m-pros-cons .proscons-grid > div > h3:first-child::before, .m-pros-cons .proscons-grid > div > div:only-child > h3:first-child::before { content: ""; width: 20px; height: 20px; flex: 0 0 20px; background: currentColor; -webkit-mask: var(--t-face) center / contain no-repeat; mask: var(--t-face) center / contain no-repeat; }
.m-pros-cons .proscons-grid > .pros { --t-face: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round'%3E%3Ccircle cx='12' cy='12' r='10'/%3E%3Cpath d='M8 14s1.5 2 4 2 4-2 4-2'/%3E%3Cline x1='9' y1='9' x2='9.01' y2='9'/%3E%3Cline x1='15' y1='9' x2='15.01' y2='9'/%3E%3C/svg%3E"); }
.m-pros-cons .proscons-grid > .cons { --t-face: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round'%3E%3Ccircle cx='12' cy='12' r='10'/%3E%3Cpath d='M16 16s-1.5-2-4-2-4 2-4 2'/%3E%3Cline x1='9' y1='9' x2='9.01' y2='9'/%3E%3Cline x1='15' y1='9' x2='15.01' y2='9'/%3E%3C/svg%3E"); }
/* item rhythm: 36px above an item title, 24px under it, 24px between paragraphs; the first item sits 24px under the label rule */
.m-pros-cons .proscons-grid > div > * + *, .m-pros-cons .proscons-grid > div > div:only-child > * + * { margin-top: 24px; }
.m-pros-cons .proscons-grid > div > h3, .m-pros-cons .proscons-grid > div > div:only-child > h3 { margin-top: 36px; font-size: var(--t-h3); line-height: 1.3; font-weight: 700; text-align: left; }
.m-pros-cons .proscons-grid > div > h3 + p, .m-pros-cons .proscons-grid > div > div:only-child > h3 + p { margin-top: 24px; }
.m-pros-cons .proscons-grid > div > h3:first-child + *, .m-pros-cons .proscons-grid > div > div:only-child > h3:first-child + * { margin-top: 24px; }
.m-pros-cons .proscons-grid > div > ul, .m-pros-cons .proscons-grid > div > ol, .m-pros-cons .proscons-grid > div > div:only-child > ul, .m-pros-cons .proscons-grid > div > div:only-child > ol { list-style: disc; padding-left: 1.4em; }
.m-pros-cons .proscons-grid p strong { font-weight: 700; }
.m-pros-cons .proscons-grid p a:not([class]) { color: var(--c-accent-video); }
.m-pros-cons .proscons-grid p a:not([class]):hover { text-decoration: underline; text-underline-offset: 2px; }

/* related-tools@1.1.4 */
/* Lifted from src/styles/tool.css:410-427, which writes the pill grid once for `.sec-related` and
   `.sec-blogcards`. A module ships only its own half, so every grouped selector is split here.

   A source selector that already starts at the section (`.sec-related > .related-grid`) has that class
   REPLACED by `.m-related-tools`. A source selector written at top level because the two sections share it
   (`.related-grid > .card`) is prefixed as a DESCENDANT — `.m-related-tools .related-grid > .card` — so its
   own combinators survive byte-for-byte and it matches the same elements at the same depths. Writing
   `.m-related-tools > .related-grid > .card` instead would invent a child combinator the source never had.
   Specificity goes 0-2-0 → 0-3-0 against the same elements either way, so the rendering is unchanged. */
.m-related-tools { background: var(--c-bg); }
.m-related-tools > .sec-sub { font-size: 20px; line-height: 1.3; font-weight: 500; color: var(--c-text-3); margin-top: -12px; }
.m-related-tools > .related-grid { display: flex; flex-wrap: wrap; justify-content: center; gap: 16px; }
/* production pills are flat on the grey band (text + thin arrow), white only on hover */
.m-related-tools .related-grid > .card { border: 0; box-shadow: none; background: transparent; border-radius: 12px; max-width: 100%; transition: background-color .15s ease; }
.m-related-tools .related-grid > .card .card-arrow { font-weight: 400; }
.m-related-tools .related-grid > .card > a { display: flex; align-items: center; justify-content: center; gap: 8px; padding: 14px 22px; font-size: 16px; line-height: 1.3; font-weight: 600; color: var(--c-text); text-decoration: none; text-wrap: balance; }
.m-related-tools .related-grid > .card:hover { background: var(--c-white); }
.m-related-tools .related-grid > .card > a:hover { color: var(--c-brand); }
/* phone: short chips keep their width and centre; long chips fill the row with the arrow pushed to the right edge (production keeps 20px gutters here) */
@media (max-width: 809px) {
  .m-related-tools > * { padding-inline: 4px; }
  .m-related-tools .related-grid > .card { flex: 0 1 auto; width: fit-content; }
  .m-related-tools .related-grid > .card > a { justify-content: space-between; text-align: left; text-wrap: pretty; padding: 12px 16px; }
  .m-related-tools .related-grid > .card > a > .card-label { flex: 1 1 auto; }
  .m-related-tools .related-grid > .card > a > .card-arrow { flex: 0 0 auto; }
}

/* reviews@1.3.1 */
/* reviews — lifted from tool.css:294-296,306-316,330,334-335 under the .m-reviews prefix
   (module-language.md §9 rule 1). Every DECLARATION is byte-identical to src/styles; only the selector
   prefix differs, and src/lib/bx/seed-parity.test.ts fails when one drifts.

   The grey band and the cards. The snap strip and the arrow controls are the Carousel primitive's
   CSS (tool.css:297-305, 318-329, 333), so they are not repeated here.

   Two rules deliberately NOT ported (module-doc.md §2.1 reasons through both):
     tool.css:317  main[data-kind] .sec .review-card .review-quote { line-height: 1.3 }
                   exists only to out-specify the generic main[data-kind] .sec p rule (0-2-2). The module's
                   own .m-reviews .review-card .review-quote is 0-3-0 and already carries line-height: 1.3.
     home.css:130-134  main[data-page="home"] .sec-reviews .reviews-strip … (the phone card strip)
                   is home's page scope; R9 converts it to .m-reviews[data-surface="home"], which is a
                   conversion rather than a lift, so it stays in home.css for now.

 */
/* ---------- reviews: grey band, 3-up cards in pages of 3×3, snap strip with arrow links ---------- */
.m-reviews { background: var(--c-bg); position: relative; }
@media (min-width: 1280px) { .m-reviews > h2 { font-size: 40px; line-height: 48px; } }
.m-reviews .review-card { scroll-snap-align: start; min-width: 0; }
/* the card content is top-aligned (align-content) — a shorter quote never pushes its avatar down */
.m-reviews .review-card blockquote {
  height: 100%; display: flex; flex-wrap: wrap; align-items: center; align-content: flex-start; gap: 12px 16px;
  padding: 16px; background: var(--c-white); border-radius: var(--r-md); box-shadow: var(--shadow-card);
}
.m-reviews .review-card picture { order: 0; width: 40px; height: 40px; flex: 0 0 40px; border-radius: 50%; overflow: hidden; }
.m-reviews .review-card picture img { width: 40px; height: 40px; object-fit: cover; }
.m-reviews .review-card .review-by { order: 1; flex: 1 1 0; min-width: 0; font-size: 18px; font-weight: 500; line-height: 1.2; color: var(--c-text); }
.m-reviews .review-card .review-by a:hover { text-decoration: underline; text-underline-offset: 2px; }
.m-reviews .review-card .review-quote { order: 2; flex: 1 1 100%; margin: 0; padding-top: 12px; border-top: 1px solid var(--c-line); font-size: 16px; line-height: 1.3; color: var(--c-text-2); }
@media (max-width: 809px) {
  .m-reviews > h2 { margin-bottom: 16px; }   /* production phone: title → first card row 16px */
  .m-reviews > .reviews-strip .review-quote { font-size: 16px; line-height: 1.3; }
}

/* R9 opt-in strip, copied from the existing home showcase treatment; the default never emits this node. */
.m-reviews .review-showcase { display: grid; grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)); gap: 8px; list-style: none; margin: 24px 0 0; padding: 0; }
.m-reviews .review-showcase img { width: 100%; height: 100%; object-fit: cover; border-radius: 10px; display: block; }

/* variant `paged` (1.2.0), lifted declaration for declaration into tool.css: production's tool-page slideshow where it pages (ai-video-generator 27 reviews,
   ai-video-editor 22; 10 tools page on production, the other 15 show every card), measured 2026-09-25 in a real browser. 1440:
   one 3×3 page in the 1200 content column — 389px cards, 16px apart both ways, row-major (FranzyMa · Javi Galué · Thomasbssh);
   the round 40px arrows 20px inside the column, centred on the cards; Next/Previous swap the whole page in place (no page
   scroll, no URL change) and wrap. A short last page keeps row-major order (22 = 9 + 9 + 3 over 1). Here: a column-flow strip
   three rows high whose columns are a third of the column; `order` sorts each run of nine column-major and `grid-row` pins
   its row, so the auto-placement cursor rebuilds row-major pages (also for a short last page); the carousel controller then
   pages it (the strip is one page wide). The arrows sit in the strip's grid row, so `top: 50%` centres them on the cards.
   A last page of one or two cards is padded to three columns by an empty ::after item, so the strip ends on a page.
   Phones (1.3.1): production keeps the same 3×3 page below 810px and lets it overflow (measured on its 10 paged tools at 390,
   2026-09-26): seven centre a 690px viewport of 390px cards, so a third of column 1 and column 2 show and both arrows sit off
   screen; ai-video-generator, ai-podcast-generator and navigate-sora-shutdown show exactly one column, three cards (1 · 4 · 7
   or 2 · 5 · 8), with at most one arrow on screen. Here the page arithmetic and the arrows' row hold at every width, and below
   810px each column is the full content width, so one column of the page (cards 1 · 4 · 7) is in view, nothing overflows the
   viewport, and the arrows (centred on the cards) step one column and wrap; the ::after padding and the page-wide no-JS
   anchors stay desktop-only. The order arithmetic covers 12 pages (108 cards, four times production's largest
   strip); a longer strip keeps the ordinary open grid rather than interleaving. Every selector needs .reviews-paged,
   which only that variant emits. */
.m-reviews.reviews-paged:not(:has(> .reviews-strip > .review-card:nth-child(109))) .review-page-anchor { display: block; }   /* the no-JS arrows' targets need a box to scroll to */
.m-reviews.reviews-paged:not(:has(> .reviews-strip > .review-card:nth-child(109))) > .reviews-strip { grid-auto-flow: column; grid-template-columns: none; grid-template-rows: repeat(3, auto); gap: 16px; overflow-x: auto; }
.m-reviews.reviews-paged:not(:has(> .reviews-strip > .review-card:nth-child(109))) .review-card { --rv-p: 0; --rv-k: 0; order: calc(var(--rv-p) * 9 + var(--rv-k)); }
.m-reviews.reviews-paged:not(:has(> .reviews-strip > .review-card:nth-child(109))) .review-card:nth-child(n + 10) { --rv-p: 1; }
.m-reviews.reviews-paged:not(:has(> .reviews-strip > .review-card:nth-child(109))) .review-card:nth-child(n + 19) { --rv-p: 2; }
.m-reviews.reviews-paged:not(:has(> .reviews-strip > .review-card:nth-child(109))) .review-card:nth-child(n + 28) { --rv-p: 3; }
.m-reviews.reviews-paged:not(:has(> .reviews-strip > .review-card:nth-child(109))) .review-card:nth-child(n + 37) { --rv-p: 4; }
.m-reviews.reviews-paged:not(:has(> .reviews-strip > .review-card:nth-child(109))) .review-card:nth-child(n + 46) { --rv-p: 5; }
.m-reviews.reviews-paged:not(:has(> .reviews-strip > .review-card:nth-child(109))) .review-card:nth-child(n + 55) { --rv-p: 6; }
.m-reviews.reviews-paged:not(:has(> .reviews-strip > .review-card:nth-child(109))) .review-card:nth-child(n + 64) { --rv-p: 7; }
.m-reviews.reviews-paged:not(:has(> .reviews-strip > .review-card:nth-child(109))) .review-card:nth-child(n + 73) { --rv-p: 8; }
.m-reviews.reviews-paged:not(:has(> .reviews-strip > .review-card:nth-child(109))) .review-card:nth-child(n + 82) { --rv-p: 9; }
.m-reviews.reviews-paged:not(:has(> .reviews-strip > .review-card:nth-child(109))) .review-card:nth-child(n + 91) { --rv-p: 10; }
.m-reviews.reviews-paged:not(:has(> .reviews-strip > .review-card:nth-child(109))) .review-card:nth-child(n + 100) { --rv-p: 11; }
.m-reviews.reviews-paged:not(:has(> .reviews-strip > .review-card:nth-child(109))) .review-card:nth-child(9n + 2) { --rv-k: 3; }
.m-reviews.reviews-paged:not(:has(> .reviews-strip > .review-card:nth-child(109))) .review-card:nth-child(9n + 3) { --rv-k: 6; }
.m-reviews.reviews-paged:not(:has(> .reviews-strip > .review-card:nth-child(109))) .review-card:nth-child(9n + 4) { --rv-k: 1; }
.m-reviews.reviews-paged:not(:has(> .reviews-strip > .review-card:nth-child(109))) .review-card:nth-child(9n + 5) { --rv-k: 4; }
.m-reviews.reviews-paged:not(:has(> .reviews-strip > .review-card:nth-child(109))) .review-card:nth-child(9n + 6) { --rv-k: 7; }
.m-reviews.reviews-paged:not(:has(> .reviews-strip > .review-card:nth-child(109))) .review-card:nth-child(9n + 7) { --rv-k: 2; }
.m-reviews.reviews-paged:not(:has(> .reviews-strip > .review-card:nth-child(109))) .review-card:nth-child(9n + 8) { --rv-k: 5; }
.m-reviews.reviews-paged:not(:has(> .reviews-strip > .review-card:nth-child(109))) .review-card:nth-child(9n + 9) { --rv-k: 8; }
.m-reviews.reviews-paged:not(:has(> .reviews-strip > .review-card:nth-child(109))) .review-card:is(:nth-child(9n + 1), :nth-child(9n + 2), :nth-child(9n + 3)) { grid-row: 1; }
.m-reviews.reviews-paged:not(:has(> .reviews-strip > .review-card:nth-child(109))) .review-card:is(:nth-child(9n + 4), :nth-child(9n + 5), :nth-child(9n + 6)) { grid-row: 2; }
.m-reviews.reviews-paged:not(:has(> .reviews-strip > .review-card:nth-child(109))) .review-card:is(:nth-child(9n + 7), :nth-child(9n + 8), :nth-child(9n + 9)) { grid-row: 3; }
.m-reviews.reviews-paged:not(:has(> .reviews-strip > .review-card:nth-child(109))) > .carousel-controls a { grid-row: 2 / 3; grid-column: 1 / -1; }
@media (min-width: 810px) {
  .m-reviews.reviews-paged:not(:has(> .reviews-strip > .review-card:nth-child(109))) > .reviews-strip { grid-auto-columns: calc((100% - 32px) / 3); }
  .m-reviews.reviews-paged:not(:has(> .reviews-strip > .review-card:nth-child(109))) > .reviews-strip:has(> .review-card:last-child:nth-child(9n + 1))::after, .m-reviews.reviews-paged:not(:has(> .reviews-strip > .review-card:nth-child(109))) > .reviews-strip:has(> .review-card:last-child:nth-child(9n + 2))::after { content: ""; order: 999999; grid-row: 1; }
  .m-reviews.reviews-paged:not(:has(> .reviews-strip > .review-card:nth-child(109))) > .reviews-strip:has(> .review-card:last-child:nth-child(9n + 1))::after { grid-column-end: span 2; }
  .m-reviews.reviews-paged:not(:has(> .reviews-strip > .review-card:nth-child(109))) .review-card { position: relative; }
  .m-reviews.reviews-paged:not(:has(> .reviews-strip > .review-card:nth-child(109))) .review-page-anchor { position: absolute; top: 0; left: 0; width: calc(300% + 32px); height: 1px; pointer-events: none; }   /* a whole page wide: the no-JS arrows scroll the strip to that page exactly */
}
@media (max-width: 809px) {
  .m-reviews.reviews-paged:not(:has(> .reviews-strip > .review-card:nth-child(109))) > .reviews-strip { grid-column: 1 / -1; grid-auto-columns: 100%; padding-inline: var(--gutter); scroll-padding-inline: var(--gutter); }
}

/* variant `strip` (1.3.0): production's other tool-page reviews layout (44 of the 93 tool pages with reviews, measured
   2026-09-25 in a real browser at 390, 600, 809, 810, 1000 and 1440). Below 1200px ONE row of cards that scrolls sideways from the
   viewport edge — 343px cards 8px apart below 810 (one card and a 39px peek at 390), 360px from 810 to 1199 — snapping card by
   card; the two 40px arrows are a centred pair 16px apart, 16px under the strip, on no background (8px radius, 24px icon). From
   1200px every card is open in the 3-column grid and the pair sits centred under it. Every selector needs .reviews-scroller,
   which only that variant emits. */
@media (max-width: 1199px) {
  .m-reviews.reviews-scroller > .reviews-strip { grid-column: 1 / -1; display: flex; flex-direction: row; gap: 8px; overflow-x: auto; scroll-snap-type: x mandatory; padding: 0 0 4px; scroll-padding-inline: 0; }
  .m-reviews.reviews-scroller > .reviews-strip > .review-card { flex: 0 0 343px; }
}
@media (min-width: 810px) and (max-width: 1199px) { .m-reviews.reviews-scroller > .reviews-strip > .review-card { flex-basis: 360px; } }
.m-reviews.reviews-scroller > .carousel-controls { grid-column: 1 / -1; display: flex; justify-content: center; gap: 16px; margin: 16px 0 0; }
.m-reviews.reviews-scroller > .carousel-controls a, .m-reviews.reviews-scroller > .carousel-controls a:hover { position: static; margin: 0; width: 40px; height: 40px; border-radius: 8px; background: transparent; }

/* selling-point@1.1.0 */
/* Lifted from src/styles/tool.css:243-252; `.sec-selling` -> `.m-selling-point` (same element, 0-1-0).
   The phone body-copy rule (tool.css:50-52) keeps its `main[data-kind]` prefix and stays site CSS. */

/* ---------- selling point: plain, inset copy; production caps its centred title at 720px ---------- */
.m-selling-point > h2 { max-width: 700px; margin-inline: auto; }
.m-selling-point > :not(h2) { padding-inline: var(--t-inset); }
.m-selling-point > * + * { margin-top: 24px; }
.m-selling-point > h2 + * { margin-top: 0; }   /* the band is a grid (no margin collapsing): the title's own 32px is the gap to the first paragraph */
@media (max-width: 809px) { .m-selling-point > h2 { margin-bottom: 24px; } }
.m-selling-point > h3 { margin-top: 36px; }
.m-selling-point > h3 + p { margin-top: 24px; }
.m-selling-point > ul, .m-selling-point > ol { list-style: disc; padding-left: calc(var(--t-inset) + 1.4em); }

/* 1.1.0 variant `scroll-inset` — the root carries the explainer prompts-scroll arm's classes (.sec-prompts.prompts-scroll-inset),
   so tool.css's frame rules (tool.css .sec-prompts …) draw the card; lifted here under the root, declaration for declaration: the
   1200×320 card with 20px inside (the phone's inset 358×320, 16px inside), the title across the column, and the Selling Point's
   own rhythm inside the frame (tool.css .sec-selling.sec-prompts …). */
.m-selling-point.sec-prompts > h2 { max-width: none; margin-inline: 0; line-height: 1.3; }
.m-selling-point.sec-prompts .sec-scroll { position: relative; box-sizing: border-box; height: 320px; padding: 20px; background: var(--c-white); border-radius: 4px; }
.m-selling-point.sec-prompts .sec-scroll::after { content: ""; position: absolute; inset: 0; border: 1px solid var(--c-line); border-radius: inherit; pointer-events: none; }
.m-selling-point.sec-prompts .sec-scroll-inner { height: 100%; overflow: auto; }
.m-selling-point.sec-prompts .sec-scroll-inner:focus-visible { outline: 2px solid var(--c-text); outline-offset: 2px; }
.m-selling-point.sec-prompts .sec-scroll-inner > * + * { margin-top: 24px; }
.m-selling-point.sec-prompts .sec-scroll-inner > h3 { margin-top: 36px; }
.m-selling-point.sec-prompts .sec-scroll-inner > :first-child { margin-top: 0; }
.m-selling-point.sec-prompts .sec-scroll-inner > :is(h3, h4) + :is(ul, ol) { margin-top: 4px; }
.m-selling-point.sec-prompts .sec-scroll-inner > :is(ul, ol) { padding-left: 1.4em; }
.m-selling-point.sec-prompts .sec-scroll-inner > ul { list-style: disc; }
.m-selling-point.sec-prompts .sec-scroll-inner > ol { list-style: decimal; }
@media (max-width: 809px) {
  .m-selling-point.sec-prompts.prompts-scroll-inset .sec-scroll { margin-inline: 0; padding: 16px; }
  .m-selling-point.sec-prompts > h2 { margin-bottom: 16px; }
  .m-selling-point.sec-prompts .sec-scroll-inner > * + * { margin-top: 21px; }
  .m-selling-point.sec-prompts .sec-scroll-inner > h3 { margin-top: 27px; }
  .m-selling-point.sec-prompts .sec-scroll-inner > :first-child { margin-top: 0; }
}

/* tab-showcase@1.0.0 */
/* tab-showcase — lifted from home.css:330-331,347-350,352,368-369,373-375,377-378,381-382 under the .m-tab-showcase prefix
   (module-language.md §9 rule 1). Every DECLARATION is byte-identical to src/styles; only the selector
   prefix differs, and src/lib/bx/seed-parity.test.ts fails when one drifts.

   The band, its centred title, the panel body and the media column. The bars, the radio machine and
   the failed-video swap belong to the Tabs and Video primitives.

   Home's page scope (main[data-page="home"]) is dropped from the rules kept here, which is safe BECAUSE
   every one of them resolves against global tokens (--c-white, --c-text, --gutter): the module carries the
   same look wherever it is placed, which is the point of module CSS.

   ONE rule could not come with them and stays in src/styles/home.css:375 —
     main[data-page="home"] .sec-tabshow .ts-panels > .ts-panel {
       grid-template-columns: minmax(0, 1fr) minmax(0, var(--home-media-col)); column-gap: var(--home-media-gap); }
   Both custom properties are declared ONLY on main[data-page="home"] (home.css:25-26, :38). Lifted here as
   `.m-tab-showcase .ts-panels > .ts-panel` the qualifier is stripped and the rule fires on the `page` kind
   this module also declares, where neither property resolves — grid-template-columns and column-gap then fall
   back at computed-value time and the desktop two-column panel collapses. The module root keeps its
   `sec-tabshow` class, so home.css:375 still matches it on home; nothing is lost.
 */
.m-tab-showcase { --ts-accent: var(--c-accent-video); background: var(--c-white); padding: 48px var(--gutter) 0; }
.m-tab-showcase > h2 { font-size: 24px; line-height: 1.2; font-weight: 700; color: var(--c-text); text-align: center; margin: 0 0 24px; }
.m-tab-showcase .ts-panel .ts-copy :is(h3, .ts-title) { display: flex; align-items: center; gap: 10px; margin: 0; font-size: 20px; line-height: 1.3; font-weight: 700; color: var(--c-text); }
.m-tab-showcase .ts-stage[data-si="0"] .ts-panel .ts-copy :is(h3, .ts-title)::before { content: ""; flex: 0 0 auto; width: 1em; height: 1em; background: currentColor; -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M12 2a10 10 0 1 0 0 20 10 10 0 0 0 0-20zm-2 14V8l6 4-6 4z'/%3E%3C/svg%3E") center / contain no-repeat; mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M12 2a10 10 0 1 0 0 20 10 10 0 0 0 0-20zm-2 14V8l6 4-6 4z'/%3E%3C/svg%3E") center / contain no-repeat; }
.m-tab-showcase .ts-panel .ts-copy p { margin: 16px 0 0; font-size: 16px; line-height: 1.6; color: var(--c-text-2); max-width: 62ch; }
.m-tab-showcase .ts-media video { width: 100%; height: auto; aspect-ratio: 1 / 1; object-fit: cover; border-radius: 12px; background: var(--c-line); }
.m-tab-showcase .ts-media picture img, .m-tab-showcase .ts-media > img { width: 100%; height: auto; border-radius: 12px; }   /* the Image-stage stills are 588px intrinsically; unbounded they widened the phone layout viewport to 1,139px (staged build, 01:50Z) */
@media (min-width: 810px) {
  .m-tab-showcase > h2 { font-size: 32px; margin-bottom: 32px; }
  .m-tab-showcase .ts-panel .ts-copy :is(h3, .ts-title) { font-size: 24px; }
  .m-tab-showcase .ts-panel .ts-copy p { font-size: 18px; line-height: 1.4; }
}
@media (min-width: 1280px) {
  .m-tab-showcase > h2 { font-size: 40px; }
  .m-tab-showcase .ts-panel .ts-copy :is(h3, .ts-title) { font-size: 30px; }
  .m-tab-showcase .ts-panel .ts-copy p { margin-top: 28px; font-size: 20px; line-height: 1.3; }
}

/* tagged-posts@1.0.0 */
/* lifted from src/styles/blog.css:72-75, :78, :79, :88, :101-102, :104 — the posts band, the gradient h2,
   the card grid, the archive's vertical rhythm and the 57px card-body foot.
   Rewrites forced by module-language.md §9 rule 1 (`main` may not appear); the page-kind qualifier is carried
   by the module's identity, because this module only renders on a tag archive:
     main[data-kind] .sec.sec-posts                 → .m-tagged-posts
     main[data-kind="tag"] .sec.sec-posts           → .m-tagged-posts
     main[data-kind="tag"] > .sec-posts             → .m-tagged-posts
     .sec-posts > h2 (+ the gradient rule)          → .m-tagged-posts > h2   (the h2 is the tag-heading include)
     .post-grid                                     → .m-tagged-posts .post-grid
     main[data-kind="tag"] .post-body               → .m-tagged-posts .post-body
   blog.css:106-116 (the pager) is carried by the Pagination primitive and is not duplicated. */
.m-tagged-posts { display: block; padding-block: 0; }   /* beats tool.css `main[data-kind] .sec` rhythm */
.m-tagged-posts { width: min(100% - 2 * var(--gutter), 1368px); }
.m-tagged-posts > h2 { font-size: 24px; line-height: 1.2; font-weight: 700; text-align: center; margin: 0 auto 20px; width: fit-content; }
.m-tagged-posts > h2 { background: linear-gradient(90deg, var(--c-brand) 0%, var(--c-text) 100%); -webkit-background-clip: text; background-clip: text; color: transparent; }
.m-tagged-posts { padding-block: 32px; }
.m-tagged-posts .post-grid { display: grid; gap: 16px; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); margin: 0; padding: 0; list-style: none; }
.m-tagged-posts .post-body { padding-bottom: 57px; }
@media (min-width: 810px) {
  .m-tagged-posts { padding-block: 48px 0; }
  .m-tagged-posts > h2 { font-size: 34px; margin-bottom: 32px; }
  .m-tagged-posts { padding-block: 48px 32px; }
}

/* tool-cta@1.2.0 */
/* Lifted from src/styles/tool.css:411 and 430-435; `.sec-cta` -> `.m-tool-cta` (same element, 0-1-0), and
   the grouped background rule of :411 is split so the module ships only its own half.
   Left in site CSS on purpose: `main[data-kind] .sec-cta { padding: 40px 0 48px }` and its phone overrides
   (tool.css:431, 437-441) out-specify the shared `.sec` band padding through the `main[data-kind]` prefix
   that module CSS may not use (module-language.md §9 rule 1), and `main[data-kind] .sec-cta + .sec-blogcards`
   is a positional rule R9 converts to a layout attribute. */
.m-tool-cta { background: var(--c-bg); }
.m-tool-cta > h2 { grid-row: 1; margin-bottom: 20px; font-family: var(--font-display); font-weight: 400; font-size: var(--t-sec-h2); line-height: 1.2; }
.m-tool-cta > .microcopy { grid-row: 2; margin: 0; font-size: 14px; line-height: 1.2; color: var(--c-text-3); }
.m-tool-cta > .cta-row { grid-row: 3; margin-top: 8px; }
.m-tool-cta > .cta-row .btn { padding: 12px 24px; border-radius: 4px; font-size: 16px; font-weight: 700; }

/* Opt-in source CTA order and measured box metrics; the default stays unchanged. */
.m-tool-cta.sec-cta.source-order[data-variant="source-order"] { display: flex; flex-direction: column; align-items: center; padding: 40px 0 60px; gap: 0; }
.m-tool-cta.sec-cta.source-order[data-variant="source-order"] > h2 { width: 100%; max-width: none; padding: 0; margin: 0 0 20px; font-family: "Krona One", sans-serif; font-size: 36px; line-height: 1.2em; font-weight: 400; white-space: pre-wrap; text-wrap: wrap; word-break: break-word; overflow-wrap: break-word; }
.m-tool-cta.sec-cta.source-order[data-variant="source-order"] > .microcopy { width: 191px; padding: 0; margin: 0; font-family: Inter, sans-serif; font-size: 14px; line-height: 1.2em; white-space: pre-wrap; word-break: break-word; overflow-wrap: break-word; }
.m-tool-cta.sec-cta.source-order[data-variant="source-order"] > .cta-row { width: auto; padding: 0; margin: 8px 0 0; }
.m-tool-cta.sec-cta.source-order[data-variant="source-order"] > .cta-row > .btn { padding: 12px 24px; font-family: Inter, sans-serif; font-size: 16px; line-height: 1.2em; font-weight: 700; letter-spacing: 0; border-radius: 4px; }
@media (min-width: 768px) and (max-width: 1279.98px) { .m-tool-cta.sec-cta.source-order[data-variant="source-order"] { padding: 40px 60px 60px; } .m-tool-cta.sec-cta.source-order[data-variant="source-order"] > h2 { font-size: 34px; } }
@media (max-width: 767.98px) { .m-tool-cta.sec-cta.source-order[data-variant="source-order"] { padding: 40px 0 48px; } .m-tool-cta.sec-cta.source-order[data-variant="source-order"] > h2 { font-size: 24px; } .m-tool-cta.sec-cta.source-order[data-variant="source-order"] > .cta-row > .btn { height: 40px; font-size: 14px; } }

/* tool-faq@1.0.0 */
/* Lifted from src/styles/tool.css:445-454; `.sec-faq` -> `.m-tool-faq` (same element, 0-1-0). The list and
   item rules (tool.css:456-469) already ship with the Accordion primitive, and the 1280px band padding
   (tool.css:455) keeps its `main[data-kind]` prefix and stays site CSS. */
/* ---------- FAQ: grey band, title + subtitle on the left, questions and answers on the right ---------- */
.m-tool-faq { background: var(--c-bg); row-gap: 0; }
.m-tool-faq > h2 { text-align: center; line-height: 1.3; margin-bottom: 20px; }
.m-tool-faq > .sec-sub { margin: 0 0 32px; max-width: none; text-align: left; font-size: 18px; line-height: 1.2; color: rgb(140, 150, 160); }
@media (min-width: 810px) {
  .m-tool-faq { grid-template-columns: minmax(var(--gutter), 1fr) minmax(0, 380px) 48px minmax(0, 772px) minmax(var(--gutter), 1fr); grid-template-rows: auto auto 1fr; align-items: start; }
  .m-tool-faq > h2 { grid-column: 2; grid-row: 1; text-align: left; margin: 0; }
  .m-tool-faq > .sec-sub { grid-column: 2; grid-row: 2; margin: 20px 0 0; }
  .m-tool-faq > .faq-list { grid-column: 4; grid-row: 1 / span 3; }
}

/* tool-hero@1.3.0 */
/* Lifted from src/styles/tool.css:54-86, declaration for declaration; the only rewrite is the module's own
   root selector `.hero-tool` -> `.m-tool-hero` (same element, same 0-1-0 specificity). The `.btn`, `.cta-row`
   and `.microcopy` bases stay with the Cta primitive (src/primitives/Cta/styles.css); what is here is the
   hero's own override of them. */
/* ---------- hero: soft tint band, centred, brand pill with the microcopy inside the button ---------- */
.m-tool-hero {
  display: grid;
  grid-template-columns: minmax(var(--gutter), 1fr) minmax(0, var(--page-max)) minmax(var(--gutter), 1fr);
  padding: 48px 0 24px;
  background: linear-gradient(var(--t-hero-tint) 49.5%, var(--c-white) 100%);
}
@media (min-width: 810px) { .m-tool-hero { padding: 64px 0 40px; } }
@media (min-width: 1280px) { .m-tool-hero { padding: 80px 0; } }
.m-tool-hero > * { grid-column: 2; }
.m-tool-hero .hero-text { max-width: 900px; margin-inline: auto; text-align: center; width: 100%; }
.m-tool-hero .hero-media { order: 2; margin-top: 32px; max-width: 900px; margin-inline: auto; }
.m-tool-hero .hero-media img { border-radius: var(--r-md); }
.m-tool-hero h1 { font-size: var(--t-hero-h1); line-height: var(--t-hero-lh); font-weight: 600; color: var(--c-text); max-width: var(--t-hero-h1-w, none); margin-inline: auto; }
.m-tool-hero .hero-sub { margin-top: 16px; font-size: var(--fs-body); line-height: 1.2; color: var(--c-black); }   /* production: 16/19.2 pure black (the h1 stays #333) */
@media (min-width: 810px) { .m-tool-hero .hero-sub { font-size: 16px; } }
.m-tool-hero .cta-row { margin-top: 32px; }
/* the button: 22px label + arrow over the microcopy line, BOTH inside the anchor as on production
   (<a><p>label</p><div><p>microcopy</p></div></a> — the extractor reads the anchor as one block plus its paragraphs) */
.m-tool-hero .cta-row .btn {
  display: inline-flex; flex-direction: column; align-items: center; justify-content: center; gap: 2px;
  padding: 10px 44px 8px;
  border-radius: var(--r-pill);
  font-size: 22px; line-height: 1.2; font-weight: 700;
  box-shadow: 2px 4px 10px 0 rgba(0, 0, 0, .16);
  transition: background-color .15s ease;
}
.m-tool-hero .cta-row .btn:hover { transform: none; }
.m-tool-hero .cta-row .btn p { margin: 0; }
.m-tool-hero .cta-row .btn .btn-label::after { content: "→"; font-weight: 600; font-size: 24px; line-height: 1; margin-left: 2px; }
.m-tool-hero .cta-row .btn .microcopy { font-size: 14px; line-height: 1.2; font-weight: 500; color: var(--c-bg); opacity: .92; }
@media (min-width: 810px) { .m-tool-hero .cta-row .btn { border-radius: var(--r-sm); } }
@media (max-width: 809px) { .m-tool-hero .cta-row .btn { width: 100%; max-width: 360px; } }

/* tool.css hero background video block, lifted declaration for declaration. ---------- hero background video (lx 2026-09-23 "Real video"). prod's "Hero Background Video" layer is
   `position:absolute; inset:0; z-index:0; opacity:.58` inside the band with the clip `object-fit:cover`, and the copy
   sits above it. The .58 is laid here as the band's own gradient at .42 OVER the opaque clip (.58*clip + .42*band, the
   same composite), so the keyboard pause control inside the layer is not faded with it. The layer is auto-placed:
   an absolutely positioned grid child with an explicit grid-column would be sized by that column, not the band. ---------- */
.m-tool-hero.has-bg { position: relative; isolation: isolate; }
.m-tool-hero.has-bg > :not(.hero-bg) { position: relative; z-index: 1; }
.m-tool-hero .hero-bg { grid-column: auto; position: absolute; inset: 0; z-index: 0; overflow: hidden; background: inherit; }
.m-tool-hero .hero-bg::after { content: ""; position: absolute; inset: 0; background: inherit; opacity: .42; pointer-events: none; }
.m-tool-hero .hero-bg .hero-backdrop, .m-tool-hero .hero-bg .hero-videos { position: absolute; inset: 0; pointer-events: none; }
/* the poster is <picture><img>, and base.css gives picture `height: auto`, which would turn the img's 100% into auto (the still
   at its own aspect ratio, top-aligned, the lower band bare gradient); the picture spans the layer so the img covers it */
.m-tool-hero .hero-bg .hero-backdrop picture { width: 100%; height: 100%; }
.m-tool-hero .hero-bg .hero-backdrop img, .m-tool-hero .hero-bg .hero-videos video { width: 100%; height: 100%; object-fit: cover; object-position: 50% 50%; display: block; }
.m-tool-hero .hero-bg .hero-videos video:not(:first-child) { display: none; }
/* prod adds an empty spacer under the copy on video pages only (framer-k1hd1e: 280 / 80 / 260 px at desktop / tablet /
   phone, plus the band's 10px flex gap), so the band shows more of the clip: 706px at 1440 on /aesthetic-generator,
   397px on a tool without a video. One video template has no spacer (framer-HkyKH, /ai-ad-video-generator: the band holds
   only the layer and the copy); its hero slot variant `video-no-spacer` adds no-spacer. The spacer switches at the hero's
   own 810px tablet boundary like every other hero rule here, not at prod's 768px: between 768 and 809 the rest of this
   hero is still its phone layout, and prod's 80px there would leave /aesthetic-generator's band 242px short of prod at
   790px instead of 62px (measured 2026-09-23; the tablet band is short even without a video, a separate hero issue) */
.m-tool-hero.has-bg:not(.no-spacer)::after { content: ""; display: block; grid-column: 2; order: 3; height: 270px; }
@media (min-width: 810px) { .m-tool-hero.has-bg:not(.no-spacer)::after { height: 90px; } }
@media (min-width: 1280px) { .m-tool-hero.has-bg:not(.no-spacer)::after { height: 290px; } }
/* WCAG 2.2.2: the decorative motion stays pausable, but the control is invisible until it has keyboard focus (prod has none) */
.m-tool-hero .hero-bg .hero-motion-toggle { position: absolute; top: 12px; right: 16px; z-index: 1; display: inline-flex; align-items: center; gap: 7px; min-height: 40px; margin: 0; padding: 8px 12px; border: 1px solid rgba(255, 255, 255, 0.55); border-radius: 999px; background: rgba(20, 20, 24, 0.58); color: var(--c-white); font: inherit; font-size: 13px; line-height: 1; cursor: pointer; touch-action: manipulation; -webkit-tap-highlight-color: transparent; }
.m-tool-hero .hero-bg .hero-motion-toggle:not(:focus-visible) { opacity: 0; pointer-events: none; }
.m-tool-hero .hero-bg .hero-motion-toggle[hidden] { display: none; }
.m-tool-hero .hero-bg .hero-motion-toggle:focus-visible { outline: 3px solid var(--c-white); outline-offset: 3px; }
.m-tool-hero .hero-bg .hero-motion-icon { display: inline-flex; align-items: center; justify-content: center; width: 14px; min-height: 14px; font-size: 12px; font-weight: 700; }
@media (prefers-reduced-motion: reduce) {
  .m-tool-hero .hero-bg .hero-videos, .m-tool-hero .hero-bg .hero-motion-toggle { display: none; }
}

/* tool.css per-page hero h1 block, lifted declaration for declaration (tool-hero 1.3.0). ---------- per-page hero h1 (tools.heroTitleSize, lx 2026-09-25) ----------
   Production sets its own h1 on a few tool pages: /ai-girlfriend-generator 48px (English only), /minimax-h3-vs-seedance-2-5
   56 / 44 / 32px at line heights 1.14 / 1.16 / 1.18, /ai-ad-video-generator 58 / 58 / 36px, /creative-birthday-video-ideas
   64 / 52 / 36px at 1.1 / 1.12 / 1.15, each wrapping at its own width (1200, 640, 1040px at 1440; measured in Chromium at
   1440, 1000 and 390 on 2026-09-25). The field's numbers arrive as custom properties on the hero (ToolPage.tsx, tool-hero
   1.3.0: --hero-h1-{d,t,p} size, --hero-h1-lh-{d,t,p} line height, --hero-h1-w-{d,t,p} wrap width) and the hero's own three
   widths pick one tier. A tier or key the page leaves out resolves to an undefined variable, so its fallback, the standard
   (80 / 1.2 / 900px, 80 / 1.2 / 640px at 810-1279, 40 / 1.2 / full width on the phone), wins. The copy column stays 900px for the subtitle and the button; a wider title widens only itself. */
.m-tool-hero.sized-title { --hero-h1-size: var(--hero-h1-p); --hero-h1-lh: var(--hero-h1-lh-p); --hero-h1-w: var(--hero-h1-w-p); }
@media (min-width: 810px) { .m-tool-hero.sized-title { --hero-h1-size: var(--hero-h1-t); --hero-h1-lh: var(--hero-h1-lh-t); --hero-h1-w: var(--hero-h1-w-t); } }
@media (min-width: 1280px) { .m-tool-hero.sized-title { --hero-h1-size: var(--hero-h1-d); --hero-h1-lh: var(--hero-h1-lh-d); --hero-h1-w: var(--hero-h1-w-d); } }
.m-tool-hero.sized-title h1 { font-size: var(--hero-h1-size, var(--t-hero-h1)); line-height: var(--hero-h1-lh, var(--t-hero-lh)); max-width: var(--hero-h1-w, var(--t-hero-h1-w, none)); margin-inline: auto; }
.m-tool-hero.sized-title .hero-text { max-width: max(900px, var(--hero-h1-w, 0px)); }
.m-tool-hero.sized-title .hero-text > :not(h1) { max-width: 900px; margin-inline: auto; }

/* use-cases@1.0.0 */
/* use-cases — lifted from tool.css:227-234 under the .m-use-cases prefix
   (module-language.md §9 rule 1). Every DECLARATION is byte-identical to src/styles; only the selector
   prefix differs, and src/lib/bx/seed-parity.test.ts fails when one drifts.

   The boxed prose card plus the grey subtitle line.

 */
/* ---------- boxed prose bands (extraProse / useCases): the .sec-body IS the card, h2 (+ grey subtitle) outside ---------- */
.m-use-cases > .sec-body { padding: 20px var(--t-card-x) 24px; background: var(--c-white); border: 1px solid var(--c-line); border-radius: var(--t-card-r); }
.m-use-cases > .sec-body > * + * { margin-top: 24px; }
.m-use-cases > .sec-body > h3 { margin-top: 36px; }
.m-use-cases > .sec-body > :first-child { margin-top: 0; }
.m-use-cases > .sec-body > ul, .m-use-cases > .sec-body > ol { list-style: disc; padding-left: 1.4em; }
.m-use-cases > .sec-sub { font-size: 18px; line-height: 1.2; color: var(--c-text-3); }
@media (min-width: 1280px) { .m-use-cases > .sec-sub { font-size: 20px; } }

/* value-section@1.0.0 */
/* value-section — lifted from tool.css:101-149 under the .m-value-section prefix
   (module-language.md §9 rule 1). Every DECLARATION is byte-identical to src/styles; only the selector
   prefix differs, and src/lib/bx/seed-parity.test.ts fails when one drifts.

   The value band renders one text-only .sec-explainer, so it carries the text-column rhythm and the
   text-only treatment. It never has media, so the .has-media / .sec-media rules (tool.css:172-208) are not
   lifted.

 */
/* ---------- explainer rows: text column + media column, zigzag ---------- */
.m-value-section .sec-explainer .sec-text > h2 { font-size: var(--t-row-h2); line-height: 1.3; font-weight: 600; text-align: left; margin: 0 0 24px; }
.m-value-section .sec-explainer .sec-text > * + * { margin-top: 24px; }
.m-value-section .sec-explainer .sec-text > :first-child { margin-top: 0; }
.m-value-section .sec-explainer .sec-text > h3 { margin-top: 36px; }
.m-value-section .sec-explainer .sec-text > h4 { margin-top: 32px; }
.m-value-section .sec-explainer .sec-text > :is(h3, h4) + * { margin-top: 24px; }
/* an <h2> that is NOT the row's first child is the in-row sub-heading the rich text carries ("Step 5: …" h3 → "More Than Image
   to Video" h2 → copy): production 24/31 600 left with 42px above and 24px below — never the centred section title */
.m-value-section .sec-explainer .sec-text > * + h2 { font-size: var(--t-row-h2); line-height: 1.3; font-weight: 600; text-align: left; margin: 40px 0 24px; }
.m-value-section .sec-explainer .sec-text > * + h2 + * { margin-top: 0; }
/* phone rhythm (production 390: p→p 21px, h3 27px above, body→CREATE NOW 16px, button→picture 20px) */
@media (max-width: 809px) {
  .m-value-section .sec-explainer .sec-text > * + * { margin-top: 21px; }
  .m-value-section .sec-explainer .sec-text > h3 { margin-top: 27px; }
  .m-value-section .sec-explainer .sec-text > :is(h3, h4) + * { margin-top: 21px; }
  .m-value-section .sec-explainer .sec-text > * + h2 { margin-top: 32px; }
  .m-value-section .sec-explainer .sec-text > :first-child { margin-top: 0; }
}
.m-value-section .sec-explainer .sec-text > ul, .m-value-section .sec-explainer .sec-text > ol { padding-left: 1.4em; }
.m-value-section .sec-explainer .sec-text > ul { list-style: disc; }
.m-value-section .sec-explainer .sec-text > ol { list-style: decimal; }
.m-value-section .sec-explainer .sec-text li + li { margin-top: 8px; }
/* in-section CTA = the black CREATE NOW rectangle (both the rich-text link-paragraph and the block's own ctaLabel button) */
.m-value-section .sec-explainer .sec-text > p:has(> a[href*="?cta="]:only-child) { margin-top: 32px; }
.m-value-section .sec-explainer .sec-text > p > a[href*="?cta="]:only-child,
.m-value-section .sec-explainer .sec-text .cta-row .btn {
  display: inline-flex; align-items: center; justify-content: center;
  padding: 12px 24px; border-radius: 4px;
  background: var(--c-text); color: var(--c-white);
  font-size: 18px; line-height: 1.2; font-weight: 700;
  transition: background-color .15s ease, transform .15s ease;
}
.m-value-section .sec-explainer .sec-text > p > a[href*="?cta="]:only-child:hover,
.m-value-section .sec-explainer .sec-text .cta-row .btn:hover { background: var(--c-black); transform: translateY(-1px); }
.m-value-section .sec-explainer .sec-text > .cta-row { justify-content: flex-start; margin-top: 32px; }
.m-value-section .sec-explainer .sec-text > .cta-row + * { margin-top: 32px; }
/* a CTA paragraph is followed by the next step's heading: production leaves 40px above such a heading */
.m-value-section .sec-explainer .sec-text > p:has(> a[href*="?cta="]:only-child) + :is(h3, h4) { margin-top: 40px; }
@media (max-width: 809px) {
  .m-value-section .sec-explainer .sec-text > p:has(> a[href*="?cta="]:only-child), .m-value-section .sec-explainer .sec-text > .cta-row { margin-top: 16px; }
  .m-value-section .sec-explainer .sec-text > p:has(> a[href*="?cta="]:only-child) + :is(h3, h4) { margin-top: 40px; }
}

/* text-only explainer: centred title, left copy, inset like production (1120 within 1200) — only the block's own h2 (always the first child) */
.m-value-section .sec-explainer:not(.has-media) > .sec-text { padding-inline: var(--t-inset); }
.m-value-section .sec-explainer:not(.has-media) .sec-text > h2:first-child { font-size: var(--t-sec-h2); font-weight: 700; line-height: 1.2; text-align: center; margin-bottom: 32px; }
@media (max-width: 809px) { .m-value-section .sec-explainer:not(.has-media) .sec-text > h2:first-child { margin-bottom: 24px; } }   /* production phone: title → first paragraph ~24px */

/* youtube-tutorial@1.0.0 */
/* youtube-tutorial — lifted from tool.css:254-259,278-279 under the .m-youtube-tutorial prefix
   (module-language.md §9 rule 1). Every DECLARATION is byte-identical to src/styles; only the selector
   prefix differs, and src/lib/bx/seed-parity.test.ts fails when one drifts.

   The white card the title, subtitle and body share. The facade itself (.yt-facade, .yt-play,
   .yt-label and the hidden <noscript>) is the YouTube primitive's CSS.

 */
/* ---------- YouTube tutorial: ONE white card = centred title + grey subtitle + 16:9 poster (the .sec-body holds the facade) ---------- */
.m-youtube-tutorial > h2, .m-youtube-tutorial > .sec-sub, .m-youtube-tutorial > .sec-body { margin: 0; max-width: none; background: var(--c-white); border-inline: 1px solid var(--c-line); padding-inline: 20px; }
.m-youtube-tutorial > h2 { border-top: 1px solid var(--c-line); border-radius: 12px 12px 0 0; padding-top: 20px; padding-bottom: 20px; text-align: center; font-size: 36px; line-height: 1.2; }   /* production keeps 36px on every breakpoint */
.m-youtube-tutorial > .sec-sub { font-size: 20px; line-height: 1.2; color: var(--c-text-2); text-align: center; padding-bottom: 20px; }
.m-youtube-tutorial > .sec-body { padding-bottom: 20px; border-bottom: 1px solid var(--c-line); border-radius: 0 0 12px 12px; }
.m-youtube-tutorial > .sec-body:first-child { border-top: 1px solid var(--c-line); border-radius: 12px; padding-top: 20px; }
.m-youtube-tutorial .sec-body iframe { display: block; width: 100%; aspect-ratio: 16 / 9; height: auto; border: 0; }
.m-youtube-tutorial figcaption { display: block; font-size: 13px; line-height: 1.3; color: var(--c-text); text-align: left; margin-top: 6px; }
}
