/*
Theme Name: SocialFollow
Theme URI: https://socialfollow.co/
Author: SocialGrowth
Description: Block theme for socialfollow.co. Replaces hello-elementor + Elementor. Design tokens live in theme.json and are the same values the Astro homepage uses, so the two halves of the site cannot drift.
Version: 1.0.0
Requires at least: 6.6
Tested up to: 7.1
Requires PHP: 8.0
License: GPL-2.0-or-later
Text Domain: socialfollow
*/

/* ═══════════════════════════════════════════════════════════════════════════
   Typefaces

   ⚠️ These URLs point into the WORDPRESS UPLOADS DIRECTORY, not into this
   theme, and that is deliberate. A block theme conventionally ships fonts at
   themes/<theme>/assets/fonts/ — doing that here would break the Astro
   homepage's three @font-face rules and both logo references SILENTLY (the
   font falls back to sans-serif, the logo 404s), because src/styles/brand.css
   hard-codes these same six paths. Same origin is also why `/` and every
   WordPress page share one font cache: a visitor crossing between them
   re-downloads nothing.

   These six URLs are duplicated in src/styles/brand.css:25-48. They must move
   together or not at all.

   Three FAMILIES each at weight normal, not one family at three weights —
   again matching brand.css, where six rules select them by name.
   ═══════════════════════════════════════════════════════════════════════════ */
@font-face {
  font-family: 'VisbyCF-Medium';
  font-style: normal;
  font-weight: normal;
  font-display: swap;
  src: url('/wp-content/uploads/2022/11/VisbyCF-Medium.woff2') format('woff2'),
       url('/wp-content/uploads/2022/11/VisbyCF-Medium.woff') format('woff');
}
@font-face {
  font-family: 'VisbyCF-Bold';
  font-style: normal;
  font-weight: normal;
  font-display: swap;
  src: url('/wp-content/uploads/2022/11/VisbyCF-Bold.woff2') format('woff2'),
       url('/wp-content/uploads/2022/11/VisbyCF-Bold.woff') format('woff');
}
@font-face {
  font-family: 'VisbyCF-ExtraBold';
  font-style: normal;
  font-weight: normal;
  font-display: swap;
  src: url('/wp-content/uploads/2022/11/VisbyCF-ExtraBold.woff2') format('woff2'),
       url('/wp-content/uploads/2022/11/VisbyCF-ExtraBold.woff') format('woff');
}

/* ═══════════════════════════════════════════════════════════════════════════
   The responsive type ramp

   theme.json cannot express this without fluid typography, and fluid type is
   off on purpose: every value on both sides of the site is a fixed px, because
   the Astro side's site.css drops the root font-size to 15px below 1200px and
   14px below 768px. Anything in rem therefore renders ~6% then ~12% smaller
   there than here. These steps mirror src/styles/brand.css:95-108 exactly.
   ═══════════════════════════════════════════════════════════════════════════ */
@media (max-width: 1024px) {
  :root :where(h1), :root :where(.wp-block-post-title) { font-size: 36px; line-height: 48px; }
}
@media (max-width: 768px) {
  :root :where(h1), :root :where(.wp-block-post-title) { font-size: 28px; line-height: 32px; }
  :root :where(h3) { font-size: 18px; line-height: 28px; }
  :root :where(small), :root :where(.has-small-font-size) { font-size: 12px; line-height: 20px; }
}

/* ═══════════════════════════════════════════════════════════════════════════
   Chrome
   ═══════════════════════════════════════════════════════════════════════════ */
/* ⛔ THE TWO HALVES OF THE SITE HAD TWO DIFFERENT HEADERS, AND A VISITOR CROSSING BETWEEN THEM
   SAW IT. Igor, 2026-09-15: "homepage and other pages header design is different". Measured in
   the deployed DOM at 1470px, WordPress vs the Astro homepage:
       bar height    101px  vs   81px        (20px padding vs 16px)
       background    transparent vs #fff
       hairline      #EEEEEE vs #E6E1EE
       Sign up       103x60, radius 8px  vs  103x48, radius 999px
       content left  135px  vs  147px       (block gutter vs Bootstrap's .container)
   The homepage is the reference — it is the designed one — so the theme moves to it, not the
   other way round. ⚠️ The nav's font, size, colour and 28px gap already matched; only the box
   metrics did not, which is exactly why this survived so many reviews.
   ⚠️ src/styles/brand.css owns the other half of this pairing. Change one, check the other. */
/* ⚠️ AT `:root`, NOT ON `.sf-page` — the header and footer are template-part siblings of
   `main`, so a token scoped to the page would not reach them. This is the one deliberately
   global declaration in this file; everything else stays scoped. It mirrors src/styles/brand.css
   exactly (see the ⛔ note there for why it is derived rather than a hex). */
/* ⛔⛔ `--sf-hairline` IS HERE BECAUSE THE HEADER COULD NOT SEE IT, AND ITS BORDER HAS NEVER
   DRAWN. The token is declared on `.sf-page`; the header is a template-part SIBLING of
   `main`, so `var(--sf-hairline)` resolved to nothing there and took the whole `border-bottom`
   declaration down with it — invalid at computed-value time, measured as `0px none` on the
   deployed page. The homepage's navbar has had its 1px hairline all along, so the two headers
   differed by a line nobody could see in the CSS: the rule was right there, it just never
   applied. (Igor, 2026-09-15: "a bit different on home/how".)
   ⚠️ THE `var(--x, var(--x))` FALLBACK WAS THE TELL. A fallback that names the same undefined
   token cannot rescue anything — someone had already met this and patched the symptom.
   ⚠️ Declared at `:root`, like `--sf-chrome` below, because chrome lives outside the page. */
:root {
  --sf-hairline: #E6E1EE;
  --sf-chrome: color-mix(in oklab, var(--wp--preset--color--pink-light) 45%, #fff);
}
/* ⛔ THE LITERAL IS A FALLBACK AND IT IS NOT REDUNDANT. `--sf-chrome` is a `color-mix()`,
   and a custom property holding an unsupported function is still syntactically VALID — the
   failure happens at substitution, which makes the whole `background` declaration
   invalid-at-computed-value-time and resolves it to `unset`. On a `position: sticky` header
   that means a TRANSPARENT bar with the page scrolling through it, unreadable. The first
   declaration is what a browser without color-mix keeps. It is #F4EFFF at 45% over white —
   if the lavender ever changes, recompute it. */
/* ⛔⛔ THE HEADER IS WHITE, AND THE ONE TIME IT WAS NOT IS WHY THIS NOTE EXISTS.
   Tinting it with `--sf-chrome` on 2026-09-15 — to answer "header and footer should be more
   subtly included" — did the opposite at this end of the page: Igor, an hour later, "weird
   header/hero section cut and a bit different on home/how". The hero's ground is WHITE with a
   wash fading in from its own top edge, so a tinted bar above it meets white at a hard
   horizontal line, and the tint reads as a slab rather than as chrome.
   A white header has no edge to cut on: it IS the hero's ground, and the wash fades up into it
   from ~3/255 at the seam. That is as subtly included as this element gets.
   ⚠️ THE FOOTER KEEPS `--sf-chrome`, and the asymmetry is correct — it lands under the CORAL
   closing band, where full-strength lavender read as one more content band. Different problem,
   different end of the page. Do not "restore symmetry" by re-tinting this. */
.sf-header { background: #fff; border-bottom: 1px solid var(--sf-hairline); }
/* ⛔ THE HOMEPAGE'S CONTAINER IS STEPPED, NOT FLUID, AND THE HEADER HAS TO STEP WITH IT.
   Bootstrap's `.container` jumps between fixed widths as the viewport crosses each breakpoint;
   the block theme's group is fluid up to `contentSize`. At 1440 both are 1200 and the two
   headers were identical, but at 1100 the homepage column is 960 and at 991 it is 720 — so the
   logo sat at 70/136 on the homepage and hard against the gutter on every WordPress page.
   ⚠️ MEASURED FROM THE DEPLOYED HOMEPAGE, NOT FROM BOOTSTRAP'S DOCS — this site overrides the
   xl step to 1200 (the stock value is 1140) and its gutter to 12px (stock is 15px):
       >=1200 -> 1200 + 12px gutter
       >=992  -> 960  + 12px
       >=768  -> 720  + 0
       >=576  -> 540  + 0
       below  -> fluid, 21px gutter
   ⚠️ This mirrors a value owned by another stylesheet. If the homepage's container ever
   changes, these steps have to be re-measured — they cannot inherit it. */
.sf-header > .wp-block-group { width: 100%; max-width: 1200px; margin-inline: auto; padding-inline: 12px; }
@media (max-width: 1199.98px) { .sf-header > .wp-block-group { max-width: 960px; } }
@media (max-width: 991.98px)  { .sf-header > .wp-block-group { max-width: 720px; padding-inline: 0; } }
@media (max-width: 767.98px)  { .sf-header > .wp-block-group { max-width: 540px; } }
@media (max-width: 575.98px)  { .sf-header > .wp-block-group { max-width: none; padding-inline: 21px; } }
/* ⛔ A PILL, NOT AN 8px RECTANGLE. theme.json's global button radius is 8px, which is right for
   buttons in page content and wrong for this one — the homepage's nav CTA is a pill, and it is
   the most-looked-at element on every page. Scoped to the header so page buttons keep the 8px. */
.sf-header .wp-element-button { padding: 12px 24px; border-radius: 999px; line-height: 24px; }
/* The nav's text-to-text gap: 24px on the homepage (Bootstrap's 12px link padding, doubled),
   so the header part's blockGap is 24px rather than the 28px it shipped with. */
.sf-logo img { width: 165px; height: auto; display: block; }

/* Nav links are VisbyCF-Bold, black, purple on hover — the WordPress nav
   treatment the Astro header already copies (brand.css:203-211). */
.sf-header .wp-block-navigation {
  font-family: var(--wp--preset--font-family--bold);
  font-weight: 700;
  font-size: 16px;
}
/* :not(.wp-element-button) — the Sign up CTA is a button nested inside the
   navigation block, and without this exclusion the nav's link colour wins on
   specificity and paints the button's label black on pink. */
.sf-header .wp-block-navigation a:not(.wp-element-button) { color: var(--wp--preset--color--text); text-decoration: none; }
.sf-header .wp-block-navigation a:not(.wp-element-button):hover { color: var(--wp--preset--color--accent); }

/* ⛔ THE TWO HEADERS COLLAPSED AT DIFFERENT WIDTHS, WHICH IS THE OTHER HALF OF "the header
   design is different". WordPress's block navigation switches to its overlay at 600px; the
   homepage is a Bootstrap `navbar-expand-lg` and switches at 992px. Measured at 991px: the
   homepage showed a hamburger and the WordPress pages still showed the full inline nav plus
   the Sign up button. Crossing between them at tablet width changed the header outright.
   ⛔ RE-APPLY CORE'S CLOSED-STATE RULES IN THE GAP, do not try to move core's breakpoint —
   its `@media (min-width: 600px)` block is what reveals the inline nav, so the fix is to undo
   that between 600 and 991 and let the same interactivity JS drive the overlay it already
   drives below 600.
   ⚠️ `:not(.is-menu-open)` is load-bearing: the open overlay is positioned by rules outside
   core's media query, and hiding the container unconditionally would make the hamburger open
   nothing. Scoped to `.sf-header` so page content with its own nav block is untouched. */
@media (max-width: 991.98px) {
  .sf-header .wp-block-navigation__responsive-container-open:not(.always-shown) { display: flex; }
  .sf-header .wp-block-navigation__responsive-container:not(.hidden-by-default):not(.is-menu-open) { display: none; }
}

.sf-footer { background: #FAF8FF; background: var(--sf-chrome); }
/* ⛔⛔ THE 24px WHITE STRIPE BETWEEN THE LAST BAND AND THE FOOTER, AND IT IS THE SAME TRAP
   THIS FILE ALREADY RECORDS TWICE. WordPress's flow layout gives every block child
   `margin-block-start: var(--wp--style--block-gap)`. `main.sf-page` and the children of
   `main` and `.entry-content` were all zeroed when the bands first shipped — but the FOOTER is
   a template part sitting beside `main` at the root, which none of those selectors reach.
   Measured on the deployed page: the coral band ended at 7783 and the footer began at 7807, so
   the body's white showed through in between. The homepage, which is not built from template
   parts, never had it — which is why this survived: the two halves genuinely differed.
   ⚠️ The header needs the same zeroing for the same reason, even though it is first and
   currently has nothing above it to gap from. */
.wp-site-blocks > header, .wp-site-blocks > footer,
body > header.wp-block-template-part, body > footer.wp-block-template-part {
  margin-block-start: 0 !important;
}
/* ⛔ THE HEADER STICKS HERE TOO, AND IT IS THE LAST OF THE FOUR WAYS THESE TWO DIVERGED.
   brand.css has made the homepage navbar `position: sticky; top: 0; z-index: 30` since the
   restructure — "it scrolled away, on a 15,000px page whose entire purpose is that one CTA".
   Every WordPress page is long for the same reason and its header just scrolled off. The
   header/nav pairing was matched across the two halves on four metrics, a breakpoint and the
   stepped container; `position` was never one of them (Igor, 2026-09-15: "a bit different on
   home/how"). Same value, same z-index — a `<dialog>` is in the top layer and does not compete.
   ⚠️ IT IS THE TEMPLATE PART THAT STICKS, NOT `.sf-header` INSIDE IT. The inner div is the
   full height of its parent, so sticking it gives it no room to travel in its containing
   block and it simply does not move. The `<header>` element's containing block is
   `.wp-site-blocks`, which is the whole page.
   ⚠️ Verified no ancestor sets `overflow` — `header`, `.wp-site-blocks`, `body` and `html` are
   all `visible`. A single `overflow: hidden` anywhere above this kills sticky silently. */
.wp-site-blocks > header.wp-block-template-part {
  position: sticky;
  top: 0;
  z-index: 30;
}
.sf-footer a { color: var(--wp--preset--color--text); text-decoration: none; }
.sf-footer a:hover { color: var(--wp--preset--color--primary); }
.sf-footer h2, .sf-footer h3 { font-size: 14px; line-height: 22px; }
.sf-footer ul { list-style: none; padding-left: 0; margin: 0; }
.sf-footer li { margin-bottom: 8px; }

/* `.c-badge` is a brand class, not a WordPress one. Values from the Customizer
   CSS this theme replaces (custom_css post 31), mirrored in brand.css:245-259
   so the badge renders identically on the Astro homepage. */
.c-badge {
  display: inline-block;
  padding: 4px 8px;
  margin-left: 8px;
  font-family: var(--wp--preset--font-family--bold);
  font-size: 12px;
  line-height: 18px;
  font-weight: 600;
  color: var(--wp--preset--color--base);
  background-color: var(--wp--preset--color--badge);
  border-radius: 8px;
  text-align: center;
  vertical-align: middle;
}

/* ═══════════════════════════════════════════════════════════════════════════
   Long-form pages (Terms, Privacy) — one text blob each, so give the prose a
   readable measure rather than letting it run to 1200px.
   ═══════════════════════════════════════════════════════════════════════════ */
/* margin-inline-start:0 matters as much as the max-width. A constrained block
   layout centres its children, so capping the measure alone leaves the prose
   indented ~150px from the heading and the featured image above it — it reads
   as a broken container rather than a reading measure. Cap the width, keep the
   left edge aligned with everything else. */
/* Left edge aligned with the heading, width capped for readability. This only
   works because the templates use FLOW layout for post-content — a constrained
   layout sets `margin-left: auto !important` and would centre these regardless
   of what is written here. */
.sf-prose :where(p, ul, ol),
.wp-block-post-content :where(p, ul, ol) {
  max-width: 68ch;
  margin-inline-start: 0;
}
.sf-prose :where(h2) { margin-top: 50px; }
.sf-prose :where(h3) { margin-top: 30px; }
.sf-prose :where(li) { margin-bottom: 8px; }

/* ═══════════════════════════════════════════════════════════════════════════
   Blog
   ═══════════════════════════════════════════════════════════════════════════ */
.sf-post-card { border-bottom: 1px solid var(--wp--preset--color--grey); padding-bottom: 30px; }
.sf-post-card:last-child { border-bottom: 0; }
/* 1 of the 4 posts has no featured image; the template must not leave a gap. */
.sf-post-card .wp-block-post-featured-image img { border-radius: 8px; }

/* Blog cards: the post title is a heading that happens to be a link, so it must
   not inherit link colour or underline. Sized as an H3 — an H2 at 32px in a
   three-across grid wraps to four lines. */
.sf-post-card .wp-block-post-title { font-size: 20px; line-height: 30px; margin-top: 20px; }
.sf-post-card .wp-block-post-title a { color: var(--wp--preset--color--text); text-decoration: none; }
.sf-post-card .wp-block-post-title a:hover { color: var(--wp--preset--color--primary); }
.sf-post-card .wp-block-post-date { color: #6b6b6b; }
.sf-post-card .wp-block-post-excerpt__more-link { color: var(--wp--preset--color--primary); }

/* ═══════════════════════════════════════════════════════════════════════════
   /how/ — the page, not the document

   ⛔ WHY THIS EXISTS. /how/ was 8,189px of prose on ONE background with two CTAs
   in the whole scroll (Igor, 2026-09-14: "the page still looks bad… needs to be
   more styled as homepage but with its own components"). The content was right;
   the page had no design. The homepage argues in coloured bands with a rhythm —
   white, lavender, yellow, coral — and this gives /how/ the same grammar with
   its own components, referencing the 2019 page's shape (hero → visual steps →
   the offer restated → trust) and carrying the current copy.

   ⚠️ SCOPED TO `.sf-page`, WHICH ONLY THE BANDED PAGE TEMPLATES EMIT —
   templates/page-how.html and templates/page-reviews.html. Every rule below is
   inert on /terms/, /contact/, the blog and the homepage. Keep it that way: an
   unscoped rule here reaches every WordPress page on the site.

   ⚠️ IT WAS CALLED `.sf-howpage` UNTIL 2026-09-17, AND THE RENAME IS THE POINT.
   /reviews/ is the second page built out of these bands, and the choice was
   between a name that lies on one of them or ~100 lines of duplicated tokens,
   band variants and button rules kept in sync by hand — this file already
   carries one necessary duplication (brand.css) and did not need a second.
   Nothing about the rules changed: verified as a pure token rename, 1479 lines
   before and after, and /how/ re-rendered against its own screenshots.
   ⛔ A rule that is genuinely /how/-only goes under `.sf-howpage`, which that
   template still emits alongside `.sf-page`. Most of what is here is not — it
   is inert on /reviews/ because /reviews/ has no `.sf-net` or `.sf-draw` in it,
   which is a better guarantee than a scope class anyway.

   ⚠️ COLOURS ARE THE theme.json PRESETS, which are the same hex values
   src/styles/brand.css uses. Do not hard-code a seventh pink.
   ═══════════════════════════════════════════════════════════════════════════ */
/* ⚠️ `--sf-text` AND `--sf-radius` ARE NEW HERE AND WERE ALREADY BEING REFERENCED.
   Two rules below used `var(--sf-text)` against a token this block never declared. An
   undefined custom property is invalid at computed-value time, so `stroke: var(--sf-text)`
   resolves to `none` — an SVG that draws nothing, at HTTP 200 with no error anywhere. Same
   six values brand.css declares. */
/* ⛔ THE FULL TOKEN SET, NOT A SUBSET. This declared six of the homepage's thirteen, and every
   rule needing one of the other seven hardcoded a hex instead — which is how nine colours ended
   up on this page that appear nowhere on the homepage. A partial token set does not fail; it
   quietly licenses invention.
   ⚠️ THESE ARE src/styles/brand.css:51-63 AND MUST STAY IDENTICAL. brand.css never loads on a
   WordPress page and this file never loads on an Astro one, so the site's palette lives in two
   files by necessity. Change one, change the other. ⛔ Do not add a colour here that brand.css
   does not have — if /how/ needs a new one, it goes in brand.css first and the homepage gets it
   too, because a colour only this page knows about is exactly the drift this block prevents. */
.sf-page {
  --sf-primary: #FF3366;
  --sf-accent: #945FFD;
  --sf-badge: #8C61F4;
  --sf-link: #D11444;
  --sf-pink-light: #F4EFFF;
  --sf-yellow: #FFDF7E;
  --sf-yellow-light: #FFFBEF;
  --sf-ig-blue: #0095F6;
  --sf-hairline: #E6E1EE;
  --sf-muted: #4A4453;
  --sf-text: #000000;
  --sf-radius: 8px;
  --sf-container: 1200px;
  /* The two grounds the homepage uses for its dark sections, named so this page cannot invent
     a third: #0D0D11 is the band, #15151C a card sitting on it. */
  --sf-ink: #0D0D11;
  --sf-ink-card: #15151C;
  /* ⚠️ THE CURRENT BAND'S OWN GROUND, RE-DECLARED BY EVERY BAND BELOW. A ring, a knockout or a
     notch that has to disappear into the band behind it cannot name a colour — the step
     numbers wore a `--sf-pink-light` halo, invisible while the stepper sat on lavender and four
     pale discs the moment the band went dark. Read the ground, never restate it. */
  --sf-band-bg: #fff;
}

/* The bands. Each is full-bleed; `.sf-in` is the 1200px column inside it —
   the same two-element pattern every homepage section uses. */
/* ⚠️ THE GUTTER LIVES ON THE BAND, NOT ON `.sf-in`, SO EVERY COLUMN LINES UP.
   With the padding on `.sf-in`, band content started 24px right of the hero's — the hero is a
   `constrained` group (children centred in the padded box) and `.sf-in` is a plain box with its
   own inset, so the two resolved to different x. Measured 135 vs 159 before this moved. */
/* ⛔ THE CONTENT WRAPPER HAS TO BE RELEASED OR NO BAND CAN BLEED. Measured on the rendered
   page: `.entry-content` computed to max-width 1200px with margin-left 111px — so every band
   below the hero was a 1,200px box, not a full-width stripe, and their content sat 24px right
   of the hero's. WordPress constrains post-content from theme.json's contentSize even when the
   template's `main` declares no layout; `!important` because the rule it beats carries one.
   ⚠️ This is why the hero looked right and nothing else did: the hero is a template sibling of
   `.entry-content`, not a child of it. */
.sf-page .entry-content { max-width: none !important; margin-inline: 0 !important; }

/* ⛔⛔ WORDPRESS'S 24px BLOCK GAP WAS DRAWING A WHITE STRIPE BETWEEN EVERY BAND.
   Igor, 2026-09-15: "hero background has some odd white stripe beneath header, the same is
   here". Measured on the deployed page: `main.sf-page` sat at margin-top 24px under the
   header, `.entry-content` another 24px under the hero, and every band after that another 24
   from its neighbour. On a page whose sections are full-bleed COLOURED bands, each of those
   gaps is a 24px band of page background showing through — a white stripe under the header,
   and another between the lavender and cream bands further down.
   ⚠️ IT IS FLOW LAYOUT, NOT A MARGIN OF OURS. WordPress emits
   `:where(.is-layout-flow) > * { margin-block-start: var(--wp--style--block-gap) }` for every
   block child, which is right for prose and wrong for stacked bands. ⛔ Zeroed only at the two
   levels that stack bands — `main`'s children and `.entry-content`'s children — never deeper,
   or paragraphs inside a band lose their spacing too. Each band carries its own padding, so
   there is nothing left for the gap to do. */
main.sf-page { margin-block-start: 0 !important; }
.sf-page > *,
.sf-page > .entry-content > * { margin-block-start: 0 !important; }
.sf-page .sf-band { padding: 76px 24px; }
.sf-page .sf-in { max-width: 1200px; margin-inline: auto; }
/* ⛔⛔ THE BAND RHYTHM IS THE HOMEPAGE'S, AND THE HOMEPAGE IS THE REFERENCE.
   Igor, 2026-09-15: "color patterns are different on /how and home, but the home should be the
   leading one". Measured both rendered pages side by side, and the difference was structural
   rather than a wrong hex — the palette values already matched (`.sf-band-tint` IS the
   homepage's `.bg-primary-alt`, `.sf-band-cream` IS its `.sf-band-yellow`). What this page had
   inverted were the two rules brand.css states:
     · THE PRODUCT DEMO GOES DARK. On the homepage the three steps sit on ink; here the four
       steps sat on lavender and the CLOSE sat on ink — dark spent on the ending instead of on
       the demo, which is the opposite of "dark reads as *here is the thing*".
     · THE CLOSE GOES CORAL. The homepage's last band is `--sf-primary`, its loudest moment.
       This page had no coral band at all.
   Fixed by re-classing the bands, not by inventing anything: the steps take `sf-band-dark`,
   the close takes `sf-band-coral`.
   ⛔ AND TINT GROUPS, IT DOES NOT ALTERNATE. brand.css: "Five alternating tinted bands made
   every section read as equally important. Tint now GROUPS." This page alternated
   tint/white/tint/white down its whole length. The three sections of the honesty case — what
   this does not do, free vs organic, how to spot a fake — are one cream group now, and the
   bands either side are plain. Do not re-tint a band here to "break up" a run; a run IS the
   grouping. */
.sf-page .sf-band-tint { --sf-band-bg: var(--sf-pink-light); background: var(--sf-band-bg); }
.sf-page .sf-band-cream { --sf-band-bg: var(--sf-yellow-light); background: var(--sf-band-bg); }
/* ⚠️ Two cream bands in a row must not draw a seam between themselves — they are one group. */
.sf-page .sf-band-cream + .sf-band-cream { padding-top: 0; }

.sf-page .sf-band-dark { --sf-band-bg: var(--sf-ink); background: var(--sf-band-bg); color: #fff;
  --sf-muted: #B9B2C4; }
/* ⚠️ EVERY HEADING LEVEL, NOT JUST h2/h3 — brand.css records shipping black-on-black because
   h1 and h4-h6 were missing from its equivalent list. Same list, same reason. */
.sf-page .sf-band-dark h1, .sf-page .sf-band-dark h2, .sf-page .sf-band-dark h3,
.sf-page .sf-band-dark h4, .sf-page .sf-band-dark h5, .sf-page .sf-band-dark h6,
.sf-page .sf-band-dark strong, .sf-page .sf-band-dark b { color: #fff; }
/* ⚠️ 88%, WHERE brand.css's `.sf-dark` USES 66% — the one deliberate divergence in this file.
   Igor rejected a dark band on this page as "cool idea but unreadable" at 78%, and 88% is the
   value that answered it. Matching the homepage here would re-introduce what he rejected.
   (The step bodies take `--sf-muted`, which IS the homepage's #B9B2C4.) */
.sf-page .sf-band-dark p { color: rgba(255,255,255,0.88); }

/* The closing band: `--sf-primary`, exactly as the homepage's last section.
   ⛔ DO NOT ADD `--sf-muted` HERE. The dark band can rebind it because everything on it sits
   directly on the ink; this band carries WHITE testimonial cards, and `.sf-tm-who` reads
   `--sf-muted` — a white value there would be white-on-white. */
.sf-page .sf-band-coral { --sf-band-bg: var(--sf-primary); background: var(--sf-band-bg); color: #fff; }
.sf-page .sf-band-coral h1, .sf-page .sf-band-coral h2, .sf-page .sf-band-coral h3,
.sf-page .sf-band-coral h4, .sf-page .sf-band-coral h5, .sf-page .sf-band-coral h6,
.sf-page .sf-band-coral strong { color: #fff; }
/* ⛔⛔ WHITE, NOT A TINT OF WHITE, AND NOTHING ON THIS BAND MAY GO BELOW IT.
   `--sf-primary` is a LIGHT ground: pure white on it is 3.55:1, which carries headings and
   buttons and is exactly what the homepage's closing band ships. Every step down from white
   falls through the floor — the 88% this band inherited measures 3.02:1, and the 70-82% the
   press kicker and the CTA note carried are worse still. Those were written for an INK ground,
   where dimming white is free, and they came across with the section.
   On this band hierarchy is size and weight. It cannot be opacity. */
.sf-page .sf-band-coral p { color: #fff; }

/* The hero. The 2019 page opened with a heading and a promise and nothing else;
   this keeps that and adds the eyebrow the rest of the site uses. */
/* ⛔ THE HERO IS TALL, NOT WIDE. Igor, 2026-09-14: "needs to be more vertical in design than
   squished like right now". It was a short letterbox: a wide landscape photo forced the whole
   band into one flat strip, so the headline, the chips and the CTA had no room to breathe. The
   art is a PORTRAIT Instagram post card now, which sets the band's height rather than fighting
   it, and the vertical padding went up with it. */
.sf-page .sf-hero-k { margin: 0 0 12px; font-family: 'VisbyCF-Bold', sans-serif; font-size: 13px;
  line-height: 20px; letter-spacing: 0.09em; text-transform: uppercase; color: var(--sf-muted); }
/* ⛔ `!important` IS REQUIRED HERE AND IT IS NOT LAZINESS. The hero is a constrained group, and
   WordPress emits `.is-layout-constrained > :where(...) { margin-left: auto !important; }` — so
   a capped title centres itself against the left-aligned eyebrow above it no matter what
   specificity we bring. Measured at margin-left: 326px before this rule. The same trap is
   documented in theme/templates/page.html, which avoids it by not constraining post-content. */
/* ⛔ DO NOT STYLE THE TITLE'S WIDTH OR MARGINS. THREE ATTEMPTS, TWO BROKEN LAYOUTS.
   WordPress's constrained layout emits, for every child of the hero group:
       max-width: <content-size>;  margin-left: auto !important;  margin-right: auto !important
   Cap the width and it centres the CAPPED box — the title sat 326px right of the eyebrow.
   Override the margin and it goes flush to the viewport edge — 111px the other way. Remove the
   cap and there is nothing left to centre, so it goes flush again.
   Left alone, WP's own rule produces a 1200px box centred in the band, whose left edge IS the
   column every other element on this page uses. The correct amount of CSS here is none. */

/* Prose inside a band keeps a readable measure without the theme's global rule,
   which does not reach here — `.sf-prose` is on the OTHER template. */
.sf-page .entry-content :where(p, ul, ol) { max-width: 68ch; }
.sf-page .entry-content > h2 { margin-top: 0; }
/* ⚠️ NOT `.sf-hero-band + .sf-band` — THEY ARE NOT SIBLINGS. The hero is a direct child of
   `main`; the content bands are inside `.entry-content`, which is a different child. The
   adjacent-sibling version of this rule matched nothing and the double gap stayed. */
.sf-page .entry-content > .sf-band:first-child { padding-top: 54px; }
/* ⚠️ `.sf-lead` was removed 2026-09-15 — no markup on any template used it. */

/* ⛔ THE 68ch PROSE CAP HITS CARD GRIDS TOO, BECAUSE THEY ARE `ul`/`ol`. The rule above says
   `:where(p, ul, ol)` so it can give real prose lists a measure — and it silently squeezed the
   testimonial grid and the press band to 68ch inside a 1200px column. Any list that is a LAYOUT
   rather than prose has to opt out here. Measured: the testimonial grid rendered 900px wide in a
   1355px band before this. */
.sf-page .sf-tm,
.sf-page .sf-press,
.sf-page .sf-cal ul { max-width: none; }

/* ⛔⛔ `.sf-sp-wp` IS IN THESE SELECTORS AND IT IS THE WHOLE REASON THEY WORK.
   The press strip's own rules ship in the LIFTED block near the end of this file as
   `.sf-page .sf-sp-wp .sf-press…` — same specificity as a plain `.sf-band-coral .sf-press…`
   and LATER in the source, so the band's overrides lost every time and lost silently: measured
   on the deployed page as `filter: grayscale(1); opacity: .55` (the light-ground treatment) and
   `color: #4A4453` on a coral ground — a 2.64:1 kicker. Naming the wrapper here takes the
   selectors to (0,4,0) and the band wins.
   ⚠️ THE SAME TRAP IS ALREADY RECORDED IN THIS FILE for `.sf-tm`'s column count. Any rule that
   overrides the lifted block has to out-specify it; equal-specificity-and-earlier is a no-op. */
/* ⛔⛔ THE PRESS LOGOS GO ON A WHITE PANEL, BECAUSE A KNOCKOUT CANNOT RENDER A LOGO.
   Igor, 2026-09-15: "this is very bad and not visible enough". The previous treatment flattened
   each mark to pure white (`brightness(0) invert(1)`) at 92% on coral — which throws away every
   internal edge a logo is made of. The New York Post's blackletter became a white smear and
   Product Hunt's roundel became a plain white disc; the two wordmark-only logos survived because
   they have no internal detail to lose. No opacity or size change fixes that: the information
   is destroyed by the filter, not dimmed by it.
   So the strip gets what the logos were drawn for — a light ground — and keeps the homepage's
   own treatment on it (`grayscale(1)` at low opacity, brand.css `.sf-press img`). It also makes
   the band coherent: the testimonials beside it are already white cards on coral, and the press
   strip was the one element trying to live directly on the ground.
   ⚠️ The kicker follows the panel, not the band: it is dark-on-white again, not white. */
/* ⚠️ THE KICKER SITS BESIDE THE LOGOS, NOT ABOVE THEM. Stacked, the four marks used the left
   45% of a 1200px panel and the rest was dead white. In one row the label earns its place as a
   lead-in and the logos spread across the track, which is what makes the strip read as a strip.
   ⚠️ `flex-wrap` and the `320px` basis are what let it fall back to two lines on a phone. */
.sf-page .sf-band-coral .sf-sp-wp .sf-sp-k { margin: 0; flex: 0 0 auto; text-align: left;
  color: var(--sf-muted); }
.sf-page .sf-band-coral .sf-sp-wp .sf-press { flex: 1 1 320px; justify-content: space-between;
  gap: 18px 36px; margin: 0; }
.sf-page .sf-band-coral .sf-sp-wp .sf-press img { filter: grayscale(1); opacity: 0.88; height: 38px; }
/* The cards stay light on the coral band — that contrast is the point — so their text must not
   inherit the band's white. */
.sf-page .sf-band-coral .sf-sp-wp .sf-tm li p,
.sf-page .sf-band-coral .sf-sp-wp .sf-tm li q { color: #000; }

/* ── The closing band's layout ───────────────────────────────────────────────
   ⛔ IT WAS THREE THINGS IN A FLAT PILE. Igor, 2026-09-14: "fix laytout". The press strip, the
   four quote cards and the page's last CTA sat on one flat ground with nothing between them but
   a heading, so the ask read as the tail of the testimonials rather than as the thing the page
   had been building to. Three fixes, and all three keep the page's single left axis — mixing a
   centred panel in here is the asymmetry Igor already flagged once on the network section.

   1. The press strip is a strip: its own row, closed by a hairline, so it reads as the band's
      masthead instead of as the first item in a list.
   2. Four quotes go FOUR ACROSS, not 2x2. Two columns of two made a 700px-tall block of text
      that the CTA then had to survive; one row of short cards reads as a proof bar.
   3. The CTA is a raised panel — its own surface, its own edge, copy and button on one line. */
/* ⚠️ `.sf-presswrap`, NOT `:first-child`. The press band's wrapper is NOT the first `.sf-sp-wp`
   in the closing band — an inline `<style>` block ships ahead of it in its own div, so the
   positional selector matched nothing and the hairline silently never drew. */
.sf-page .sf-band-coral .sf-presswrap { display: flex; flex-wrap: wrap; align-items: center;
  gap: 16px 40px; padding: 24px 32px; margin-bottom: 22px;
  background: #fff; border-radius: 16px; }

/* ⚠️ FOUR EQUAL TRACKS, AND THE CARDS MUST BE FLEX COLUMNS OR THEY DO NOT LINE UP. The quotes
   are four different lengths; in a plain grid cell the attribution row sits at the top and the
   quote hangs below it at four different depths. `1fr` on the quote pushes nothing around but
   lets every card fill its track, so the four bottoms land together. */
.sf-page .sf-band-coral .sf-tm { grid-template-columns: repeat(4, minmax(0,1fr)); gap: 16px; }
.sf-page .sf-band-coral .sf-tm li { display: flex; flex-direction: column; }
.sf-page .sf-band-coral .sf-tm li q { flex: 1 1 auto; font-size: 15px; line-height: 24px; }
.sf-page .sf-band-coral .sf-tm-who { min-width: 0; }
.sf-page .sf-band-coral .sf-tm-who b { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.sf-page .sf-close { margin-top: 44px; padding: 34px 36px; border-radius: 20px;
  /* ⚠️ 12%/32%, NOT 6%/14%. Those were measured against ink, where a 6% white wash is a
     visible lift; on coral the panel and its edge both disappeared into the band. */
  background: rgba(255,255,255,0.12); border: 1px solid rgba(255,255,255,0.32);
  display: grid; grid-template-columns: minmax(0,1fr) minmax(0,300px); gap: 10px 48px;
  align-items: center; }
.sf-page .sf-close h2 { grid-column: 1; margin: 0 0 10px; }
.sf-page .sf-close p { grid-column: 1; margin: 0; max-width: 60ch; }
/* ⚠️ The button column spans both rows of the copy column, which is what puts it on the
   heading's optical centre rather than level with the last line of the paragraph. */
.sf-page .sf-close-go { grid-column: 2; grid-row: 1 / span 2; }
.sf-page .sf-close-go .sf-btn { display: block; text-align: center; }
.sf-page .sf-close-go .sf-cta-note { margin: 12px 0 0; font-size: 13px; line-height: 20px; }

@media (max-width: 1100px) {
  .sf-page .sf-band-coral .sf-tm { grid-template-columns: repeat(2, minmax(0,1fr)); }
}
@media (max-width: 860px) {
  .sf-page .sf-close { grid-template-columns: 1fr; padding: 28px 24px; }
  .sf-page .sf-close h2, .sf-page .sf-close p { grid-column: 1; }
  .sf-page .sf-close-go { grid-column: 1; grid-row: auto; margin-top: 22px; }
}
@media (max-width: 600px) {
  .sf-page .sf-band-coral .sf-tm { grid-template-columns: 1fr; }
}

/* ── The stepper: FOUR ACROSS, LEFT TO RIGHT ────────────────────────────────
   ⛔ HORIZONTAL. Igor, 2026-09-14: "HORIZONTAL LAYOUT". It was a vertical track with the art
   stranded ~400px from the words it illustrated, and four of those stacked turned a four-beat
   sequence into 1,100px of scrolling.

   ⛔ AND THE FOUR ILLUSTRATIONS ARE GONE (Igor, 2026-09-15: "remove these 4 images fully").
   They were the last 3D stock on the page — the family Igor had already thrown out of the
   zig-zag — and at 124px in a 279px column they were decoration standing between the number
   and the sentence that matters. Without them each step is a number, a heading and the copy,
   which is the whole content of a step. ⛔ Do not put an icon back in the gap: the row is the
   sequence, and the numbers are what carry it.

   ⛔ THE RULE CARRIES THE SEQUENCE NOW, so it earns some detail (Igor: "make the 1-4 line a bit
   more interesting"). Each segment is a gradient that hands off to the next — pink at step 1
   through to lavender at step 4, the page's two ends — with a dot at its midpoint and an
   arrowhead landing at each number. It is the same three segments; what changed is that they
   read as travel rather than as a hairline.
   ⚠️ THE ARITHMETIC IS UNCHANGED AND IS THE PART THAT MUST NOT DRIFT: a badge centre is 22px
   from its column's left edge, so a segment starting there must be `100% + gap` wide to reach
   the next one. Change the 28px gap and change it here. */
/* ⛔⛔ THE FOUR STEP COLOURS ARE THE HOMEPAGE'S THREE, WITH ONE POINT INTERPOLATED.
   The homepage's dark steps band tints its three cards `--sf-primary`, `--sf-accent`,
   `--sf-badge` (brand.css `.sf-card:nth-child(n)`) — a real ramp of three real tokens. This
   stepper has four segments, and I had invented two mid-tones (#F06AC0, #C07AEE) to fill them:
   the numbers looked like the homepage's ramp and were not on it.
   Four slots, the same three ends, and the one gap between them mixed rather than guessed.
   ⚠️ `color-mix` is the point — a hex here would be the same invention with a nicer comment. */
.sf-page .sf-steps { --sf-step-1: var(--sf-primary);
  --sf-step-2: color-mix(in oklab, var(--sf-primary), var(--sf-accent));
  --sf-step-3: var(--sf-accent);
  --sf-step-4: var(--sf-badge);
  position: relative; display: grid;
  grid-template-columns: repeat(4, minmax(0,1fr)); gap: 0 28px;
  margin: 44px 0 0; padding: 0; list-style: none; max-width: none; }
.sf-page .sf-steps li { position: relative; z-index: 1; margin: 0; padding: 0;
  display: flex; flex-direction: column; align-items: flex-start; }

.sf-page .sf-steps li:not(:last-child)::after { content: ""; position: absolute; z-index: 0;
  top: 20px; left: 22px; width: calc(100% + 28px); height: 4px; border-radius: 3px; }
.sf-page .sf-steps li:nth-child(1)::after { background: linear-gradient(90deg, var(--sf-step-1), var(--sf-step-2)); }
.sf-page .sf-steps li:nth-child(2)::after { background: linear-gradient(90deg, var(--sf-step-2), var(--sf-step-3)); }
.sf-page .sf-steps li:nth-child(3)::after { background: linear-gradient(90deg, var(--sf-step-3), var(--sf-step-4)); }
/* The arrowhead: a rotated corner sitting where the segment meets the next number, so the line
   reads as going somewhere. ⚠️ `currentColor` would inherit the copy's ink — these take the
   segment's end colour explicitly. */
.sf-page .sf-steps li:not(:last-child)::before { content: ""; position: absolute; z-index: 2;
  top: 16px; left: calc(100% + 42px); width: 8px; height: 8px;
  border-right: 3px solid; border-top: 3px solid; transform: rotate(45deg); }
.sf-page .sf-steps li:nth-child(1)::before { color: var(--sf-step-2); }
.sf-page .sf-steps li:nth-child(2)::before { color: var(--sf-step-3); }
.sf-page .sf-steps li:nth-child(3)::before { color: var(--sf-step-4); }

.sf-page .sf-steps .n { position: relative; z-index: 3; display: grid; place-items: center;
  width: 44px; height: 44px; border-radius: 50%; background: var(--sf-step-1); color: #fff;
  /* ⚠️ 19px, AND THE VALUE IS LOAD-BEARING. White on the four ramp colours measures
     3.55 / 4.4 / 3.95 / 4.09:1 — under the 4.5:1 body-text floor, over the 3:1 LARGE-text one,
     and WCAG's large threshold for bold type is 14pt = 18.66px. At 17px all four discs were a
     fail; at 19px all four pass. Do not shrink it back to fit a tighter disc. */
  font-family: 'VisbyCF-ExtraBold', sans-serif; font-size: 19px; line-height: 1;
  box-shadow: 0 0 0 6px var(--sf-band-bg); }
.sf-page .sf-steps li:nth-child(2) .n { background: var(--sf-step-2); }
.sf-page .sf-steps li:nth-child(3) .n { background: var(--sf-step-3); }
.sf-page .sf-steps li:nth-child(4) .n { background: var(--sf-step-4); }
/* ⚠️ Two lines reserved so the four paragraphs start together — steps 2 and 4 wrap and 1 and 3
   do not, which put four bodies on three baselines and made the row read as scattered. */
.sf-page .sf-steps h3 { margin: 22px 0 8px; min-height: 54px; font-size: 19px; line-height: 27px; }
.sf-page .sf-steps p { margin: 0; font-size: 15px; line-height: 25px; color: var(--sf-muted);
  max-width: none; }

/* ── The repeat-cycle CTA ────────────────────────────────────────────────────
   Igor, 2026-09-15: "add this type of section which should serve as a CTA section right after
   the 1-2-3-4 steps section", with the v1 "50 Free Followers, Every Day" block as the reference.
   What was taken from it is the SHAPE — eyebrow, one big headline, one paragraph, one button, a
   visual holding the right half — because that shape is the page's first ask and it has to be
   able to stop a reader who has just finished the steps.
   ⛔ ITS NUMBERS AND ITS PROMISE DID NOT COME WITH IT. See the note in deploy/pages/how.html.
   ⚠️ THE ART IS THE PAGE'S OWN `.sf-draw` PARTS, NOT A NEW COMPONENT — same card, same rows,
   same pills as the password and survey mocks. A fifth visual idiom on one page is how a page
   stops looking designed, and these cost no bytes and cannot contradict the copy beside them. */
.sf-page .sf-cyc { display: grid; grid-template-columns: minmax(0,1fr) minmax(0,420px);
  gap: 34px 56px; align-items: center; }
.sf-page .sf-cyc-k { margin: 0 0 14px; font-family: 'VisbyCF-Bold', sans-serif; font-size: 13px;
  line-height: 20px; letter-spacing: 0.09em; text-transform: uppercase; color: var(--sf-primary); }
.sf-page .sf-cyc-copy h2 { margin: 0 0 16px; }
.sf-page .sf-cyc-copy p { margin: 0; max-width: 54ch; }
/* ⚠️ The button row's own 34px top margin is the rhythm the hero uses; it is not repeated here. */
.sf-page .sf-cyc-art { display: grid; place-items: center; }
/* ⚠️ `.sf-draw` CAPS ITSELF AT 300px AND THAT CAP IS WRONG HERE. It was set for the zig-zag,
   where the mock is an aside beside a wide argument; in this CTA the card IS the right half of
   the section, and at 300px in a 420px column it left the band visibly lopsided — measured a
   724px copy column against a 300px card. Released to fill its track. */
.sf-page .sf-cyc-art .sf-draw { max-width: none; }
.sf-page .sf-cyc-art .sf-draw-nav { padding: 15px 16px; }
/* ⚠️ THE NUMBER AND THE CADENCE, AT A SIZE THAT SURVIVES A SKIM. `baseline` alignment, not
   `center`: a 46px numeral centred against two small lines floats above their text. */
.sf-page .sf-cyc-top { display: flex; align-items: baseline; gap: 12px; }
.sf-page .sf-cyc-n { flex: 0 0 auto; font-family: 'VisbyCF-ExtraBold', sans-serif;
  font-size: 46px; line-height: 1; color: var(--sf-primary); }
.sf-page .sf-cyc-lab { min-width: 0; }
.sf-page .sf-cyc-lab b { display: block; font-family: 'VisbyCF-Bold', sans-serif;
  font-size: 17px; line-height: 24px; }
.sf-page .sf-cyc-lab em { display: block; font-style: normal;
  font-family: 'VisbyCF-Bold', sans-serif; font-size: 14px; line-height: 21px;
  color: var(--sf-badge); }
/* ⚠️ The separator's default 18px margins are set for the zig-zag mocks; under a 46px numeral
   the top one reads as a gap and the bottom one as a second gap. */
.sf-page .sf-cyc-art .sf-draw-sep { margin: 16px 0 14px; }
/* ⚠️ THE ROWS ARE A LIST, SO THE FIRST ONE'S TOP MARGIN IS THE CARD'S PADDING. `.sf-draw-nav`
   carries `margin-top: 12px` for use as a single trailing element; stacked, that reads as an
   inconsistent first gap under the label. The markup zeroes it inline on the first row. */
.sf-page .sf-cyc-art .sf-draw-pill { flex: 0 0 auto; }

/* ── The zig-zag ─────────────────────────────────────────────────────────────
   The homepage's device, rebuilt here: art one side, argument the other, alternating. */
.sf-page .sf-zag { display: grid; grid-template-columns: minmax(0,1fr) minmax(0,1fr);
  gap: 34px 56px; align-items: center; margin: 0 0 66px; }
.sf-page .sf-zag:last-child { margin-bottom: 0; }
.sf-page .sf-zag.is-flip .sf-zag-art { order: 2; }
.sf-page .sf-zag-art { display: grid; place-items: center; padding: 30px;
  background: #fff; border: 1px solid var(--sf-hairline); border-radius: 16px; }
.sf-page .sf-band-tint .sf-zag-art { background: #fff; }
.sf-page .sf-zag-art img { width: 100%; max-width: 250px; height: auto; }
.sf-page .sf-zag h2 { margin: 0 0 14px; }
.sf-page .sf-zag p { margin: 0 0 14px; max-width: none; }
.sf-page .sf-zag p:last-child { margin-bottom: 0; }

/* ── The FAQ: THE HOMEPAGE'S ACCORDION, RULE FOR RULE ───────────────────────
   ⛔ IT ONLY LOOKED LIKE THE HOMEPAGE'S. Igor, 2026-09-15: "faq design is different from
   homepage's". Same `<details>` structure and the same `.sf-faq` class, but every value under
   it had drifted: the mark was a CSS-drawn plus that collapsed to a MINUS on open where the
   homepage rotates a plus into a cross, summaries were top-aligned against the homepage's
   centred, and the answer text was `--sf-muted` grey where the homepage leaves it ink. Three
   small differences that add up to a different component on the same site.
   ⚠️ These values are src/styles/brand.css:979-996. That file never loads on a WordPress page,
   so this is a hand-kept copy — the same pairing the header and the modal carry. Change one,
   check the other. */
.sf-page .sf-faq { margin: 30px 0 0; border-top: 1px solid var(--sf-hairline); }
.sf-page .sf-faq details { border-bottom: 1px solid var(--sf-hairline); }
.sf-page .sf-faq summary { display: flex; align-items: center; justify-content: space-between;
  gap: 16px; padding: 18px 0; cursor: pointer; list-style: none; }
.sf-page .sf-faq summary::-webkit-details-marker { display: none; }
.sf-page .sf-faq summary h3 { margin: 0; font-size: 18px; line-height: 26px; }
.sf-page .sf-faq summary:focus-visible { outline: 2px solid var(--sf-primary); outline-offset: 4px; }
/* ⚠️ A PLUS THAT ROTATES INTO A CROSS, which is what the homepage does — not a plus that loses
   its vertical bar to become a minus. 45° on an equal-armed plus is a cross. */
.sf-page .sf-faq .sf-faq-mark { flex-shrink: 0; width: 20px; height: 20px; fill: none;
  stroke: var(--sf-primary); stroke-width: 2; stroke-linecap: round;
  transition: transform 0.15s ease; }
.sf-page .sf-faq details[open] .sf-faq-mark { transform: rotate(45deg); }
.sf-page .sf-faq details > p { margin: 0 0 20px; padding-right: 40px; max-width: 72ch; }
@media (prefers-reduced-motion: reduce) {
  .sf-page .sf-faq .sf-faq-mark { transition: none; }
}

/* ── The hero ────────────────────────────────────────────────────────────────
   ⛔⛔ TWO COLUMNS, WITH THE TITLE INSIDE THE LEFT ONE. Rebuilt 2026-09-15 (Igor: "hero section
   is very poorly structured now, and there's a lot of empty/random space and poor layout").
   Measured before: a 239px copy column against a 524px art column left 345px of dead lavender
   under the CTA — and it could not be centred away, because the title was a full-width sibling
   ABOVE the row, so centring opened a void between the H1 and the lead instead. With the title
   in the column the two halves are one block and the leftover splits evenly, which is exactly
   what the homepage hero does.
   ⛔ `align-items: center` IS NOW THE RIGHT ANSWER and was the wrong one before. The difference
   is entirely that the H1 moved; if it ever moves back out, this goes back to `start`.
   ⛔ AND THE TITLE NEEDS NO RULES AT ALL. Four attempts at width and margins on it are recorded
   below; the reason none were needed is that the hero groups are FLOW layout (see the template),
   so WordPress emits no auto-margins to fight. The correct amount of CSS on the title is none. */
/* ⛔⛔ THE HOMEPAGE'S HERO IS NOT A FLAT LAVENDER BAND, AND THIS ONE WAS.
   Igor, 2026-09-15: "color patterns are different on /how and home, but the home should be the
   leading one". This is the first thing a visitor compares, and it was the clearest difference:
   the homepage opens on WHITE with one soft radial wash over it — lavender at the centre fading
   through cream to nothing by 70% — while this page opened on a solid slab of `--sf-pink-light`
   that stopped at a hard horizontal edge. Same token, opposite impression: one reads as light
   falling on the page, the other as a coloured box the header sits on.
   Copied from brand.css `.sf-hero::before` rather than approximated — the ellipse, the three
   stops and the offsets are the homepage's numbers.
   ⚠️ `overflow: hidden` IS REQUIRED. The wash is deliberately wider and taller than the band;
   without it it adds ~700px of horizontal scroll on a phone.
   ⚠️ AND THE BAND BELOW IT IS WHITE TOO, WHICH IS THE POINT — the wash has to run out into the
   page rather than end on a line. Do not re-tint the first content band. */
.sf-page .sf-hero-band { position: relative; overflow: hidden; --sf-band-bg: #fff;
  background: var(--sf-band-bg); padding: 64px 24px 64px; }
.sf-page .sf-hero-band::before { content: ''; position: absolute; z-index: 0;
  top: -220px; left: 50%; width: 1100px; height: 700px; transform: translateX(-50%);
  background: radial-gradient(ellipse at center, rgba(244,239,255,0.9) 0%,
    rgba(255,251,239,0.55) 42%, rgba(255,255,255,0) 70%);
  pointer-events: none; }
.sf-page .sf-hero-rest { position: relative; z-index: 1;
  display: grid; grid-template-columns: minmax(0,1fr) minmax(0,420px);
  gap: 40px 56px; align-items: center; max-width: 1200px; margin-inline: auto; }
/* ⚠️ Flow layout gives every child a 24px block-gap. The copy column wants its own rhythm, so
   the gap is zeroed here and each element carries its own margin. */
.sf-page .sf-hero-copy > * { margin-block: 0; }
.sf-page .sf-hero-k { margin: 0 0 12px; font-family: 'VisbyCF-Bold', sans-serif; font-size: 13px;
  line-height: 20px; letter-spacing: 0.09em; text-transform: uppercase; color: var(--sf-muted); }
.sf-page .sf-hero-copy .wp-block-post-title { margin: 0; }
.sf-page .sf-hero-lead { margin: 20px 0 0; font-size: 19px; line-height: 31px; max-width: 46ch; }
/* ⚠️ The hero keeps only what is ITS OWN: a tighter top margin. The stacking and the note's
   measure moved to `.sf-cta-row` above, where every CTA row on the page gets them. */
.sf-page .sf-hero-band .sf-cta-row { margin-top: 28px; }

/* ⛔ DO NOT ADD A WIDTH OR MARGIN TO THE TITLE. FOUR ATTEMPTS, THREE BROKEN LAYOUTS — recorded
   because the failure mode is invisible in the source. While the hero was a `constrained` group,
   WordPress emitted for every direct child:
       max-width: <content-size>;  margin-left: auto !important;  margin-right: auto !important
   Cap the width and it centres the CAPPED box (title sat 326px right of the eyebrow). Override
   the margin and it goes flush to the viewport edge (111px the other way). Remove the cap and
   there is nothing left to centre, so it goes flush again. The hero is FLOW layout now and emits
   none of those, which is why the rule above is a plain `margin: 0`. */

/* ── The hero's Instagram post card ──────────────────────────────────────────
   ⛔ AN INSTAGRAM POST, DRAWN, NOT A PHOTO IN A ROUNDED BOX. Igor: "fix image to be vertical
   and instagram post style like on homepage". The homepage's hero art is IgCard.astro — header,
   square photo, action row, likes, caption — and this is the same object in static CSS, because
   /how/ is WordPress and cannot import an Astro component.
   ⛔ INVENTED PERSONA ONLY, SAME RULE AS IgCard. The tick and the counts describe an account
   that does not exist. Never point this at a real handle; on a real one the badge becomes a
   claim about Instagram's actions and the likes become a results claim about a third party. */
/* ⚠️ THE ART IS A STACK, NOT ONE CARD. `.sf-hero-art` is the positioning context; the back card
   is absolutely placed so it cannot add height, and the front card alone sets the column's size.
   That is what stops the band growing every time the composition gains a layer. */
.sf-page .sf-hero-art { position: relative; padding-top: 34px; }
.sf-page .sf-igp { width: 100%; max-width: 320px; background: #fff; border-radius: 16px;
  overflow: hidden; box-shadow: 0 22px 50px rgba(20,8,24,0.16); }
.sf-page .sf-igp.is-front { position: relative; z-index: 1; }
/* Offset up and to the right, rotated, about 40% of it showing — the homepage's exact
   treatment. ⛔ Its media box keeps the landscape ratio; forcing it square would crop the
   photograph to a different subject than the one the file holds. */
/* ⛔ `right: 0`, NOT A NEGATIVE OFFSET. It was `right: -26px` to push the card past the column
   edge, and at 1100px that put it 2px beyond the viewport and gave the whole page a horizontal
   scrollbar — measured, not theorised. The column is 420px and the front card 320, so there is
   already 100px of lateral room inside it: the overlap reads the same and nothing can escape.
   ⚠️ A negative offset on an absolutely-positioned child of a max-width column is only safe
   while the page gutter happens to be wider than the offset. Do not reintroduce one. */
.sf-page .sf-igp.is-back { position: absolute; top: 0; right: 0; width: 250px;
  transform: rotate(6deg); box-shadow: 0 16px 38px rgba(20,8,24,0.13); }
.sf-page .sf-igp.is-back .sf-igp-media { aspect-ratio: 3 / 2; }
.sf-page .sf-igp.is-back .sf-igp-top { padding: 10px 12px; }
.sf-page .sf-igp.is-back .sf-igp-av { width: 28px; height: 28px; }
.sf-page .sf-igp-top { display: flex; align-items: center; gap: 10px; padding: 13px 14px; }
.sf-page .sf-igp-av { flex: 0 0 auto; width: 34px; height: 34px; border-radius: 50%;
  background: linear-gradient(135deg, var(--sf-primary), var(--sf-badge)); }
.sf-page .sf-igp-who b { display: flex; align-items: center; gap: 5px;
  font-family: 'VisbyCF-Bold', sans-serif; font-size: 14px; line-height: 20px; }
.sf-page .sf-igp-who em { font-style: normal; display: block; font-size: 12px; line-height: 17px;
  color: var(--sf-muted); }
.sf-page .sf-igp-tick { width: 13px; height: 13px; fill: var(--sf-ig-blue); }
.sf-page .sf-igp-media { position: relative; aspect-ratio: 1 / 1; background: var(--sf-pink-light); }
.sf-page .sf-igp.is-back .sf-igp-media { margin-bottom: 0; }
.sf-page .sf-igp-media img { width: 100%; height: 100%; object-fit: cover; display: block; }
.sf-page .sf-igp-bub { position: absolute; right: 12px; bottom: 12px; display: inline-flex;
  align-items: center; gap: 7px; padding: 8px 14px; border-radius: 16px 16px 16px 5px;
  background: var(--sf-primary); color: #fff;
  font-family: 'VisbyCF-ExtraBold', sans-serif; font-size: 19px; line-height: 1;
  box-shadow: 0 12px 26px rgba(255,51,102,0.34); }
.sf-page .sf-igp-bub svg { width: 15px; height: 15px; fill: none; stroke: #fff; stroke-width: 2; }
/* ⛔ THE QUIETER OF THE TWO, AND IT MUST STAY QUIETER. The follower bubble is the offer and
   stays solid coral; this is a white pill with a coral heart, exactly as `.sf-bub.is-likes`
   does on the homepage. Two solid coral badges on one photo and neither reads as the point. */
.sf-page .sf-igp-likes { position: absolute; left: 12px; top: 12px; display: inline-flex;
  align-items: center; gap: 6px; padding: 6px 12px; border-radius: 16px 16px 16px 5px;
  background: rgba(255,255,255,0.94); color: var(--sf-text); backdrop-filter: blur(6px);
  box-shadow: 0 10px 24px rgba(20,8,24,0.16);
  font-family: 'VisbyCF-ExtraBold', sans-serif; font-size: 15px; line-height: 1;
  font-variant-numeric: tabular-nums; }
.sf-page .sf-igp-likes svg { width: 13px; height: 13px; fill: var(--sf-primary); stroke: none; }
.sf-page .sf-igp-bub.is-sm { padding: 6px 11px; border-radius: 12px 12px 12px 4px; font-size: 15px; }
.sf-page .sf-igp-bub.is-sm svg { width: 13px; height: 13px; }
.sf-page .sf-igp-acts { display: flex; align-items: center; gap: 14px; padding: 12px 14px 0; }
.sf-page .sf-igp-acts svg { width: 21px; height: 21px; fill: none; stroke: var(--sf-text);
  stroke-width: 1.9; stroke-linecap: round; stroke-linejoin: round; }
.sf-page .sf-igp-acts .heart svg { fill: var(--sf-primary); stroke: var(--sf-primary); }
.sf-page .sf-igp-acts .sp { margin-left: auto; }
.sf-page .sf-igp-foot { padding: 9px 14px 16px; }
.sf-page .sf-igp-foot p { margin: 0; max-width: none; font-size: 13px; line-height: 20px; }
.sf-page .sf-igp-foot b { font-family: 'VisbyCF-Bold', sans-serif; }
.sf-page .sf-igp-foot .cap { margin-top: 3px; color: var(--sf-muted);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.sf-page .sf-igp figure, .sf-page figure.sf-igp { margin: 0; }

@media (max-width: 900px) {
  .sf-page .sf-hero-rest { grid-template-columns: 1fr; }
  .sf-page .sf-hero-band { padding: 48px 20px 52px; }
  .sf-page .sf-hero-art { padding-top: 0; }
  .sf-page .sf-igp { max-width: 320px; }
  /* ⛔ The back card goes on a phone. At 320px it either overlaps the front card's content or
     hangs off the screen, and it is decoration — nothing is lost by dropping it. */
  .sf-page .sf-igp.is-back { display: none; }
}
.sf-page .sf-chips { display: flex; flex-wrap: wrap; gap: 10px; margin: 22px 0 0; padding: 0; list-style: none; max-width: none; }
.sf-page .sf-chips li { margin: 0; display: inline-flex; align-items: center; gap: 8px;
  padding: 9px 15px; background: #fff; border: 1px solid var(--sf-hairline); border-radius: 999px;
  font-family: 'VisbyCF-Bold', sans-serif; font-size: 14px; line-height: 21px; }
.sf-page .sf-chips svg { width: 16px; height: 16px; fill: none; stroke: var(--sf-primary); stroke-width: 2.2; }

/* ── Secondary descriptive facts (was six linked cards) ──────────────────────
   ⛔ NON-INTERACTIVE ON PURPOSE. Igor: "fully repurposed into secondary non-interactive
   descriptive content". The hero owns the call to action now; a second grid of clickable
   cards under it competed with it and sent readers sideways before they had read anything. */
.sf-page .sf-facts { display: grid; grid-template-columns: repeat(2, 1fr); gap: 4px 44px;
  margin: 26px 0 0; padding: 0; list-style: none; max-width: none; }
.sf-page .sf-facts li { display: flex; gap: 13px; align-items: flex-start; margin: 0; padding: 13px 0; }
.sf-page .sf-facts li + li + li { border-top: 1px solid var(--sf-hairline); }
.sf-page .sf-facts li:nth-child(2) { border-top: 0; }
.sf-page .sf-facts svg { flex: 0 0 auto; width: 20px; height: 20px; margin-top: 3px;
  fill: none; stroke: var(--sf-primary); stroke-width: 2.1; }
.sf-page .sf-facts b { display: block; font-family: 'VisbyCF-Bold', sans-serif; font-size: 16px; line-height: 24px; }
.sf-page .sf-facts span em { font-style: normal; display: block; font-size: 15px; line-height: 23px; color: var(--sf-muted); }

/* The band CTA. Reuses the theme's button colours rather than inventing a third. */
/* ⛔ THE NOTE SITS UNDER THE BUTTON. Beside it, a 14px grey sentence sits on the same baseline
   as the primary action and reads as a second, competing label; under it, it reads as the
   footnote it is. Igor has now asked for this on all three of this page's CTA rows — the hero
   (2026-09-14), the repeat-cycle CTA and the network row (2026-09-15). It is the component's
   behaviour now, so a fourth CTA row cannot be built without it. */
.sf-page .sf-cta-row { display: block; margin-top: 34px; }
.sf-page .sf-cta-row .sf-btn { display: inline-block; }
.sf-page .sf-cta-row .sf-cta-note { margin: 14px 0 0; max-width: 46ch; }
.sf-page .sf-btn { display: inline-block; padding: 17px 34px; border-radius: 999px;
  background: var(--sf-primary); color: #fff; text-decoration: none;
  font-family: 'VisbyCF-Bold', sans-serif; font-size: 16px; line-height: 24px; }
.sf-page .sf-btn:hover { background: #e62e5c; color: #fff; }
/* ⛔⛔ THE BUTTON INVERTS ON THE CORAL BAND, OR IT DISAPPEARS INTO IT.
   `.sf-btn` is `background: var(--sf-primary)` — the strongest thing on the page while the
   closing band was ink, and the band's own ground now that the close is coral. This is the
   homepage's `.btn-white` recipe verbatim (brand.css:182): white fill, coral ink, and a hover
   that moves the FILL rather than the text, because a coral-on-coral hover is the same bug one
   state later. */
.sf-page .sf-band-coral .sf-btn { background: #fff; color: var(--sf-primary); }
.sf-page .sf-band-coral .sf-btn:hover,
.sf-page .sf-band-coral .sf-btn:focus-visible { background: #FFF3F6; color: #d11f4d; }
/* ⚠️ `.sf-btn-ghost` IS GONE (2026-09-14) — the closing CTA was its only caller and it is a
   solid button on its own panel now. An outline button on a dark ground was also the weakest
   thing on the page in the position that most needed weight. */
.sf-page .sf-cta-note { margin: 0; font-size: 14px; line-height: 22px; color: var(--sf-muted); }

/* ── The /how/ handoff modal — THE HOMEPAGE'S MODAL, NOT A SECOND ONE ────────
   Igor, 2026-09-14: "also use the same modal as we use on homepage, why not?". He was right,
   and the bespoke `.sf-hm` dialog this replaces was the wrong answer to a real constraint:
   /how/ is WordPress, so it cannot import HandoffModal.astro, and I built a smaller dialog
   instead. That gave one site two different answers to "click the CTA" — a plain white card
   with a heading here, a dark countdown header and a deal strip on the homepage — and the
   difference was visible the moment anyone crossed between the two pages.

   ⛔ SO THIS IS A MIRROR OF brand.css, CLASS FOR CLASS, and the class names are deliberately
   the homepage's: `.sf-modal-card`, `.sf-cd-line`, `.sf-modal-deal`, `.sf-handoff-row`. A
   diff between the two files is then a real diff. ⚠️ THEY ARE TWO FILES AND THEY CAN DRIFT —
   brand.css never loads on a WordPress page, so nothing here inherits from it. When the
   homepage's modal changes, this block has to change with it.
   ⚠️ Everything stays scoped under `.sf-page`, so none of it reaches /terms/ or /contact/.

   ⚠️ THE NEGATIVE MARGIN ON `.sf-cd-line` TRACKS THE CARD'S SIDE PADDING — 32px, and 20px in
   the narrow block. Two halves of one measurement, same as brand.css:1697. Change one, change
   both, or the dark header over- or under-bleeds by the difference. That full bleed is also
   what keeps the close button legible: it is 14px from the card's top edge and white, so with
   any top padding on the card, half of it would sit on white. */
/* ⛔⛔ border-box, AND THE WHOLE MIRROR IS WRONG WITHOUT IT. brand.css can write
   `width: min(560px, calc(100% - 32px))` beside `padding: 0 32px` because site.css (Bootstrap)
   ships a global `*{box-sizing:border-box}` on the Astro page. THIS STYLESHEET HAS NO SUCH RULE
   — the block theme sets box-sizing nowhere — so every width here resolved as a CONTENT width
   and the card came out its own padding wider than intended: 624px instead of 560 on desktop,
   and 398px inside a 390px viewport on a phone, which overflows the screen.
   ⚠️ SCOPED TO THE MODAL SUBTREE, NOT TO `.sf-page *`. The rest of this page was measured
   and tuned under content-box; flipping it globally would silently resize every card, step and
   band on a page that is already signed off. */
.sf-page .sf-modal, .sf-page .sf-modal * { box-sizing: border-box; }
.sf-page .sf-modal { width: 100%; height: 100%; max-width: none; max-height: none;
  margin: 0; padding: 0; border: 0; background: none; overflow-y: auto;
  overscroll-behavior: contain; }
.sf-page .sf-modal::backdrop { background: rgba(20,8,24,0.55); backdrop-filter: blur(3px); }
.sf-page .sf-modal-card { position: relative; width: min(560px, calc(100% - 32px));
  margin: max(24px, 8vh) auto; padding: 0 32px 30px; background: #fff; border-radius: 20px;
  box-shadow: 0 24px 60px rgba(20,8,24,0.28); }
.sf-page .sf-modal-card:focus { outline: none; }
.sf-page .sf-modal-x { position: absolute; top: 14px; right: 14px; z-index: 1;
  display: grid; place-items: center; width: 38px; height: 38px; padding: 0; border: 0;
  border-radius: 50%; background: none; color: #fff; opacity: 0.7; cursor: pointer;
  transition: opacity .15s ease, background-color .15s ease; }
.sf-page .sf-modal-x:hover { opacity: 1; background: rgba(255,255,255,0.14); }
.sf-page .sf-modal-x:focus-visible { outline: 2px solid var(--sf-primary); outline-offset: 2px; opacity: 1; }
.sf-page .sf-modal-x svg { width: 20px; height: 20px; fill: none; stroke: currentColor;
  stroke-width: 2; stroke-linecap: round; }

/* The dark countdown header. ⚠️ A FIXED DURATION THAT RESTARTS ON EVERY LOAD — signups do not
   close, and the full note is in src/components/Countdown.astro. Igor's decision, taken with
   that in front of him. ⛔ Nothing may be added that turns it into a stated consequence. */
.sf-page .sf-cd-line { display: flex; flex-wrap: wrap; align-items: center;
  justify-content: space-between; gap: 12px 18px; margin: 0 -32px 22px;
  padding: 18px 60px 18px 32px; background: var(--sf-ink); border-radius: 20px 20px 0 0; }
.sf-page .sf-cd-line-h { display: flex; align-items: center; gap: 9px; margin: 0;
  font-family: 'VisbyCF-ExtraBold', sans-serif; font-size: 19px; line-height: 26px; color: #fff; }
.sf-page .sf-cd-line-h svg { flex: 0 0 auto; width: 19px; height: 19px; fill: none;
  stroke: var(--sf-primary); stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
.sf-page .sf-cd-clock { display: flex; align-items: flex-start; gap: 4px; }
.sf-page .sf-cd-cell { display: flex; flex-direction: column; align-items: center;
  min-width: 54px; padding: 5px 4px 4px; border-radius: 9px; background: rgba(255,255,255,0.07); }
.sf-page .sf-cd-cell b { font-family: 'VisbyCF-ExtraBold', sans-serif; font-size: 26px;
  line-height: 30px; color: #fff; font-variant-numeric: tabular-nums; }
/* ⛔ NOT `--sf-muted` — it is a grey for white grounds and measures 2.07:1 on #0D0D11, which
   is how the unit labels went invisible on the homepage once already. 62% white is 7.4:1. */
.sf-page .sf-cd-cell em { font-style: normal; font-size: 9px; line-height: 14px;
  letter-spacing: 0.07em; text-transform: uppercase; color: rgba(255,255,255,0.62); }
.sf-page .sf-cd-sep { font-family: 'VisbyCF-ExtraBold', sans-serif; font-size: 22px;
  line-height: 42px; color: var(--sf-primary); }

.sf-page .sf-modal-deal { display: flex; flex-wrap: wrap; align-items: baseline;
  gap: 0 10px; padding: 0 0 14px; margin: 0 0 18px; border-bottom: 1px solid var(--sf-hairline); }
.sf-page .sf-modal-qty { font-family: 'VisbyCF-ExtraBold', sans-serif; font-size: 40px;
  line-height: 44px; color: var(--sf-primary); font-variant-numeric: tabular-nums; }
.sf-page .sf-modal-unit { font-size: 18px; line-height: 28px; }
.sf-page .sf-modal-meta { margin-left: auto; font-size: 14px; line-height: 22px; opacity: 0.7; }
.sf-page .sf-modal-title { margin: 0; font-size: 26px; line-height: 34px; }
.sf-page .sf-modal-lead { margin: 10px 0 22px; font-size: 16px; line-height: 26px;
  opacity: 0.8; max-width: none; }

/* The fused pill — one control you type into and press, not a field above a button. */
.sf-page .sf-handoff { margin: 0; }
.sf-page .sf-handoff-label { display: block; margin: 0 0 8px; font-size: 14px;
  line-height: 22px; opacity: 0.75; }
.sf-page .sf-handoff-row { display: flex; align-items: center; gap: 10px;
  padding: 8px 8px 8px 22px; background: #fff; border: 2px solid var(--sf-hairline);
  border-radius: 999px; transition: border-color .18s ease, box-shadow .18s ease; }
.sf-page .sf-handoff-row:hover { border-color: #cfc7dd; }
.sf-page .sf-handoff:focus-within .sf-handoff-row { border-color: var(--sf-primary);
  box-shadow: 0 0 0 6px rgba(255,51,102,0.13); }
.sf-page .sf-handoff-field { flex: 1 1 auto; min-width: 0; display: flex; align-items: center; gap: 2px; }
.sf-page .sf-handoff-at { flex: 0 0 auto; font-size: 20px; line-height: 1; opacity: 0.35;
  pointer-events: none; }
.sf-page .sf-handoff-input { width: 100%; height: 56px; padding: 0 8px; border: 0;
  border-radius: 0; background: transparent; -webkit-appearance: none; appearance: none;
  font-family: 'VisbyCF-Medium', sans-serif; font-size: 20px; line-height: 24px; color: var(--sf-text); }
.sf-page .sf-handoff-input::placeholder { color: var(--sf-text); opacity: 0.32; }
.sf-page .sf-handoff-input:focus { outline: none; box-shadow: none; }
.sf-page .sf-handoff-go { flex: 0 0 auto; height: 56px; padding-inline: 30px; border: 0;
  border-radius: 999px; background: var(--sf-primary); color: #fff; cursor: pointer;
  white-space: nowrap; font-family: 'VisbyCF-Bold', sans-serif; font-size: 16px; }
.sf-page .sf-offer-note { margin: 16px 0 0; font-size: 14px; line-height: 22px;
  opacity: 0.75; max-width: none; }
.sf-page .sf-modal-card .sf-chips { margin-top: 16px; }

@media (max-width: 575px) {
  /* Side padding is 20px here, so the header bleeds by 20. See the note above. */
  .sf-page .sf-modal-card { padding: 0 20px 24px; margin-top: 16px; }
  .sf-page .sf-cd-line { margin: 0 -20px 20px; padding: 16px 56px 16px 20px; }
  /* The pill unfuses: a 56px button beside a shrinking field leaves neither usable. */
  .sf-page .sf-handoff-row { flex-wrap: wrap; border-radius: 28px; padding: 8px; }
  .sf-page .sf-handoff-field { padding-left: 14px; }
  .sf-page .sf-handoff-go { width: 100%; }
}

/* ── The network section ─────────────────────────────────────────────────────
   ⛔ WAS CENTRED UNDER LEFT-ALIGNED PROSE. Igor: "layout is very bad and asymmetric". The
   figure centred itself in a 1200px band while the paragraphs above it stopped at 68ch, and the
   key list underneath centred to a third width — three different axes in one section. Copy and
   diagram are now one two-column row on a single baseline. */
/* ⛔ `start`, NOT `center` — AND THAT WAS THE WHOLE COMPLAINT. Igor, 2026-09-14: "still very
   shitty layout". Centring three paragraphs against a taller right-hand column pushed the copy
   ~220px DOWN, so the H2 was followed by a band of empty lilac and the section looked like it
   had failed to render. Same defect as the hero had, same fix.
   ⛔ AND THE DIAGRAM AND ITS KEY ARE ONE OBJECT. They were two loose things stacked in the right
   column — a floating circle, then three rows of text with hairlines, reading as an unrelated
   list rather than as the diagram's legend. On a white card they are one figure with a key. */
.sf-page .sf-netrow { display: grid; grid-template-columns: minmax(0,1fr) minmax(0,400px);
  gap: 34px 56px; align-items: start; }
.sf-page .sf-netrow p { max-width: none; }
.sf-page .sf-netrow .sf-net-wp { margin: 0; padding: 26px 26px 10px; background: #fff;
  border: 1px solid var(--sf-hairline); border-radius: 16px; }
.sf-page .sf-netrow .sf-net-wrap { margin: 0 !important; max-width: none !important; }
.sf-page .sf-netrow .sf-net-svg { max-width: 260px !important; }
/* The trust row under the network argument. ⛔ Three FACTS the page already argues — no rating,
   no customer count, no delivery time, which are the three things this category fabricates and
   the three Gate 2 bars. Do not put a number in here. */
.sf-page .sf-trust { display: grid; grid-template-columns: repeat(3, minmax(0,1fr)); gap: 14px;
  margin: 28px 0 0; padding: 0; list-style: none; max-width: none; }
.sf-page .sf-trust li { display: flex; align-items: flex-start; gap: 11px; margin: 0;
  padding: 14px 16px; background: #fff; border: 1px solid var(--sf-hairline);
  border-radius: 12px; }
.sf-page .sf-trust svg { flex: 0 0 auto; width: 18px; height: 18px; margin-top: 2px; fill: none;
  stroke: var(--sf-primary); stroke-width: 2.2; stroke-linecap: round; stroke-linejoin: round; }
.sf-page .sf-trust b { display: block; font-family: 'VisbyCF-Bold', sans-serif;
  font-size: 14px; line-height: 21px; }
.sf-page .sf-trust span { font-size: 13px; line-height: 20px; color: var(--sf-muted); }
.sf-page .sf-netrow .sf-cta-row { margin-top: 26px; }

/* ⚠️ `.sf-vs-art` (the badge frame) WENT WITH ITS SECTION on 2026-09-15 — Igor removed "Free
   followers vs. organic growth" outright, and four rules drawing a frame for a photograph no
   markup references are dead CSS that reads as live layout. `.sf-side`, its two-column wrapper,
   went with it for the same reason. The photograph itself is still in WP media if the section
   is ever wanted back: free-followers-vs-organic-instagram-growth.webp. */

@media (max-width: 900px) { .sf-page .sf-netrow { grid-template-columns: 1fr; } }

/* ── Spot-a-fake: descriptive icons, not a numbered list ─────────────────────
   ⛔ NOT 1-2-3-4-5. Igor: "how to spot shouldn't be based on 1-2-3 but on descriptive icons".
   The five tells are not a sequence — you do not check them in order, and numbering them implied
   one. Each gets the icon that describes it instead.

   ⛔⛔ AND EVERY LAYOUT RULE BELOW WAS DEAD ON THE PAGE. Igor, 2026-09-14: "very bad...". The
   grid, the cards and `list-style: none` lived in an inline `<style>` written as
   `.sf-pr-wp .sf-tells{…}` — and the `<ul class="sf-tells">` is in a DIFFERENT wp:html block,
   outside any `.sf-pr-wp`. Not one of those seven rules ever matched. The section rendered as a
   bare bulleted list with visible browser bullets: no cards, no columns, no card padding. Only
   the two rules that happened to live HERE applied, which is why the icons looked right and
   nothing else did. It has been shipping that way since the descriptive-icons change.
   ⛔ SO THE STYLES LIVE IN THE THEME NOW, like everything else on this page. A style block that
   only matches what is nested inside it is one wp:html boundary away from matching nothing, and
   nothing goes red when it stops. */
/* ⛔ THREE ACROSS, TWO DOWN (Igor, 2026-09-15: "add 1 more card here to have 3x2 grid"). Five
   cards in two columns left a single card alone on the last row — the ragged shape that made
   the section look unfinished. Six in three columns fills both rows exactly.
   ⚠️ SO THE COUNT AND THE COLUMNS ARE ONE DECISION: drop a tell and the orphan comes back. The
   lead above says "Five things" — it says six now, and it has to move with this. */
.sf-page .sf-tells { display: grid; grid-template-columns: repeat(3, minmax(0,1fr));
  gap: 16px; margin: 26px 0 0; padding: 0; list-style: none; max-width: none; }
.sf-page .sf-tells li { margin: 0; padding: 22px; background: #fff;
  border: 1px solid var(--sf-hairline); border-radius: 12px; }
.sf-page .sf-tells li b { display: flex; align-items: center; gap: 12px; margin-bottom: 10px;
  font-family: 'VisbyCF-Bold', sans-serif; font-size: 17px; line-height: 25px; }
.sf-page .sf-tells li p { margin: 0; font-size: 15px; line-height: 24px;
  color: var(--sf-muted); max-width: none; }
.sf-page .sf-tells li a { color: var(--sf-link); }
.sf-page .sf-tells li i { flex: 0 0 auto; display: grid; place-items: center; width: 34px;
  height: 34px; border-radius: 10px; background: var(--sf-pink-light); }
.sf-page .sf-tells li i svg { width: 19px; height: 19px; fill: none; stroke: var(--sf-primary);
  stroke-width: 2.1; stroke-linecap: round; stroke-linejoin: round; }

/* ── The "what this does not do" callout, lifted out of the same dead inline block ── */
.sf-page .sf-cal { display: block; margin: 26px 0 0; padding: 22px 24px;
  background: var(--sf-pink-light); border-left: 4px solid var(--sf-primary);
  border-radius: var(--sf-radius); }
.sf-page .sf-cal-k { margin: 0 0 14px; font-family: 'VisbyCF-Bold', sans-serif; font-size: 13px;
  line-height: 20px; letter-spacing: .09em; text-transform: uppercase; color: var(--sf-muted); }
.sf-page .sf-cal ul { display: grid; gap: 10px; margin: 0; padding: 0; list-style: none; max-width: none; }
.sf-page .sf-cal li { display: flex; gap: 11px; align-items: flex-start; margin: 0;
  font-family: 'VisbyCF-Bold', sans-serif; font-size: 16px; line-height: 24px; }
.sf-page .sf-cal svg { flex: 0 0 auto; width: 19px; height: 19px; margin-top: 3px; fill: none;
  stroke: var(--sf-primary); stroke-width: 2.4; stroke-linecap: round; }


/* ── Zig-zag art, DRAWN not stocked ──────────────────────────────────────────
   ⛔ NO MORE 3D RENDERS. Igor, 2026-09-14: "doesn't make sense, create better images and more
   aligned with homepage style ones". The glossy purple lock, clipboard and molecule were the
   same stock family the homepage threw out in September — a lock is a metaphor for a password
   field, not a picture of one. These are drawn in CSS in Instagram's own grammar and our
   palette, which is exactly what IgMock.astro does on the homepage. They also cost no bytes,
   scale crisply, and cannot contradict the copy beside them. */
.sf-page .sf-draw { width: 100%; max-width: 300px; font-size: 14px; line-height: 21px; }
.sf-page .sf-draw-card { padding: 18px; background: #fff; border: 1px solid var(--sf-hairline);
  border-radius: 14px; box-shadow: 0 10px 30px rgba(20,8,24,0.07); }
.sf-page .sf-draw-row { display: flex; align-items: center; gap: 11px; padding: 11px 0; }
.sf-page .sf-draw-row + .sf-draw-row { border-top: 1px solid var(--sf-hairline); }
.sf-page .sf-draw-av { flex: 0 0 auto; width: 34px; height: 34px; border-radius: 50%;
  background: linear-gradient(135deg, var(--sf-primary), var(--sf-badge)); }
.sf-page .sf-draw-bar { height: 9px; border-radius: 5px; background: var(--sf-hairline); }
.sf-page .sf-draw-t { flex: 1 1 auto; min-width: 0; display: grid; gap: 6px; }
/* ⚠️ A TIME CUE ON A CARD IN THE SECTION ABOUT FREQUENCY (Igor, 2026-09-17: "add a clock/time
   reference here in the design"). The heading beside it asks "How often can I get 100
   followers?" and the drawing answered with a static list — nothing in it said the list comes
   back. It rides on the existing label row rather than adding a component, so there is one less
   thing to keep in sync with brand.css.
   ⛔ "Resets in 24h" IS THE CYCLE, NOT A DELIVERY TIME. Gate 2 bars stating when followers land;
   this states when the NEXT CYCLE OPENS, which the page already says in words twice. Do not let
   a future edit turn it into a countdown to an arrival. */
.sf-page .sf-draw-when { float: right; font-family: 'VisbyCF-Medium', sans-serif;
  font-size: 11px; letter-spacing: 0.02em; color: var(--sf-primary); text-transform: none; }
.sf-page .sf-draw-lab { font-family: 'VisbyCF-Bold', sans-serif; font-size: 12px;
  letter-spacing: .07em; text-transform: uppercase; color: var(--sf-muted); margin: 0 0 12px; }
.sf-page .sf-draw-field { display: flex; align-items: center; gap: 8px; padding: 12px 14px;
  border: 1px solid var(--sf-hairline); border-radius: 10px;
  /* ⚠️ THE HOMEPAGE'S PLACEHOLDER RECIPE, NOT A NEW GREY. brand.css writes every placeholder as
     `--sf-text` at low alpha (`.sf-handoff-input::placeholder`, `.sf-fcta-in::placeholder`);
     this drew the same thing with an invented #9A93A6. Same ink, same role, one fewer colour. */
  color: rgba(0,0,0,0.36); }
.sf-page .sf-draw-field.is-ok { border-color: var(--sf-primary); color: var(--sf-text);
  font-family: 'VisbyCF-Bold', sans-serif; }
.sf-page .sf-draw-field + .sf-draw-field { margin-top: 10px; }
.sf-page .sf-draw-field.is-off { position: relative; opacity: .55; }
.sf-page .sf-draw-field.is-off::after { content: ""; position: absolute; left: 10px; right: 10px;
  top: 50%; height: 2px; background: var(--sf-primary); }
.sf-page .sf-draw-btn { margin-top: 12px; padding: 12px; border-radius: 999px; text-align: center;
  background: var(--sf-primary); color: #fff; font-family: 'VisbyCF-Bold', sans-serif; font-size: 14px; }
.sf-page .sf-draw-pill { display: inline-flex; align-items: center; gap: 7px; padding: 6px 12px;
  border-radius: 999px; background: var(--sf-pink-light); font-family: 'VisbyCF-Bold', sans-serif;
  font-size: 12px; }
/* ⚠️ THE "FOLLOWED" PILL IS THE ONE TINT THIS PAGE CANNOT COPY — the homepage has a success
   INK (#12784A, its tick marks and its valid-input hint) and no success SURFACE, so there was
   nothing to match and the pill had been given an invented mint. Mixed from that ink instead,
   and carrying it as the label colour too, so the pill is the homepage's green in two roles
   rather than a fifth green. */
.sf-page .sf-draw-pill.is-green { background: color-mix(in oklab, #12784A 11%, #fff); color: #12784A; }

/* ⚠️ THE ACTION-LIMITS METER WAS DELETED HERE ON 2026-09-15, WITH THE SECTION IT DREW.
   That row asked "Is it against Instagram's rules?" and the meter showed ten follows sitting low
   on an unlabelled scale; the row is now "How often can I get 100 followers?" and draws today's
   ten instead, which the existing `.sf-draw-row` / `.sf-draw-pill` rules already cover. Five
   rules for an element no longer in any markup is dead CSS that reads as live layout — the same
   trap as the step-art responsive rules removed in the same pass.
   ⚠️ `.sf-draw-sep` and `.sf-draw-nav` below SURVIVED: the new art still uses both. */
.sf-page .sf-draw-sep { height: 1px; margin: 18px 0; background: var(--sf-hairline); }
.sf-page .sf-draw-nav { display: flex; align-items: center; gap: 12px; margin-top: 12px;
  padding: 13px 14px; border: 1px solid var(--sf-hairline); border-radius: 10px; }
.sf-page .sf-draw-nav-t { flex: 1 1 auto; min-width: 0; }
.sf-page .sf-draw-nav-t b { display: block; font-family: 'VisbyCF-Bold', sans-serif;
  font-size: 15px; line-height: 22px; }
.sf-page .sf-draw-nav-t em { font-style: normal; display: block; font-size: 12px;
  line-height: 18px; color: var(--sf-muted); }
/* ⚠️ A TICK, DRAWN — AND THE NOTE HERE USED TO ARGUE FOR THE CHEVRON IT REPLACED.
   It said: "it points at a place to go and look. ⛔ Not a tick and not a cross: this panel
   reports no verdict about anyone's account." The reasoning was about what the panel MEANS and
   ignored what the shape PROMISES. Igor, 2026-09-17: "change the arrow icon into something
   else, misleading" — a right-pointing chevron at the end of a row is the universal "tap me,
   there is more", and this row is a drawing inside an illustration with nothing behind it.
   A tick is not a verdict about the reader either: the row says "Checked before we send", and
   the tick marks that check, not the account. ⛔ Do not put the chevron back without giving the
   row somewhere to go. */
.sf-page .sf-draw-nav-c { flex: 0 0 auto; width: 9px; height: 9px; margin-right: 3px;
  border-right: 2px solid rgba(0,0,0,0.36); border-top: 2px solid rgba(0,0,0,0.36);
  transform: rotate(45deg); }
.sf-page .sf-draw-nav-tick { flex: 0 0 auto; width: 11px; height: 6px; margin-right: 4px;
  border-left: 2px solid rgba(45,138,86,0.85); border-bottom: 2px solid rgba(45,138,86,0.85);
  transform: rotate(-45deg); }
/* ⚠️ `.sf-draw-x` WENT WITH THE ART THAT USED IT (2026-09-15) — four rules drawing a cross over
   a struck-through row, in no markup on the page. `.sf-draw-field.is-off` still draws the
   password row as refused; this was a second, unused way of doing the same thing. */

/* ── The closing band, made readable ─────────────────────────────────────────
   ⛔ Igor: "cool idea but unreadable". Three separate contrast failures on one band: the body
   copy at 78% white, the press logos knocked out and then dropped to 72% opacity, and an inline
   link in brand coral, which on a coral ground is not a link at all.
   ⚠️ THESE FIXES FOLLOWED THE SECTION, NOT THE COLOUR. They were written when the closing band
   was the DARK one; on 2026-09-15 the close became coral and the steps took the ink, so every
   rule here moved with the section it was written for. */
.sf-page .sf-band-coral a:not(.sf-btn) { color: #fff; text-decoration: underline;
  text-decoration-thickness: 2px; text-underline-offset: 3px;
  text-decoration-color: rgba(255,255,255,0.55); }
.sf-page .sf-band-coral a:not(.sf-btn):hover { text-decoration-color: #fff; }
.sf-page .sf-band-coral .sf-cta-note { color: #fff; }


/* ═══════════════════════════════════════════════════════════════════════════
   The network diagram and the social-proof band

   ⛔ LIFTED OUT OF THE PAGE BODY, 2026-09-14, AND THAT FIXED TWO LIVE BUGS.
   These rules used to ship as inline `<style>` blocks inside wp:html, which the template's own
   header already said not to do. Two things were wrong with them, both silent:

   1. ⛔ `font-family:\'VisbyCF-Bold\'` — SIX declarations written with BACKSLASH-ESCAPED
      quotes. `\'` is not a quote in CSS; it makes an escaped identifier that matches no font
      family, so every one of those six fell back to the system sans-serif. The press eyebrow,
      the testimonial names, the quotes and the network key have been rendering in the wrong
      typeface. Fixed in the move.
   2. A block scoped to its own wrapper only reaches what is nested inside it. The
      `.sf-tells` grid was written that way and matched NOTHING (see the spot-a-fake note
      above); these two happened to sit inside their wrappers, so they worked — by luck, not by
      design, and one wp:html boundary away from the same failure.

   ⚠️ EVERY SELECTOR IS RE-SCOPED UNDER `.sf-page`. In the page body the wrapper class was
   the only scope; here the file is global to every WordPress page on the site, so the page
   scope has to be explicit or these reach /terms/ and /contact/.
   ═══════════════════════════════════════════════════════════════════════════ */
.sf-page .sf-net-wp .sf-net { margin: 0; }
.sf-page .sf-net-wp .sf-net-wrap { display: grid; gap: 20px; }
.sf-page .sf-net-wp .sf-net-svg { display: block; width: 100%; max-width: 360px; height: auto; margin-inline: auto; }
.sf-page .sf-net-wp .sf-net-ring { fill: none; stroke: #E2D8F6; stroke-width: 1.5; }
.sf-page .sf-net-wp .sf-net-link { stroke: #DCD2F2; stroke-width: 1.5; }
.sf-page .sf-net-wp .sf-net-link.is-far { stroke: #EDE7F9; stroke-dasharray: 2 6; }
.sf-page .sf-net-wp .sf-net-node { fill: var(--sf-badge); }
.sf-page .sf-net-wp .sf-net-node.is-far { fill: #D8CBF7; }
.sf-page .sf-net-wp .sf-net-you { fill: var(--sf-primary); }
.sf-page .sf-net-wp .sf-net-you-i { fill: #fff; }
.sf-page .sf-net-wp .sf-net-grow { fill: none; stroke: var(--sf-primary); stroke-width: 2.5; stroke-dasharray: 5 6; }
.sf-page .sf-net-wp .sf-net-spark { fill: var(--sf-primary); opacity: 0.55; }
.sf-page .sf-net-wp .sf-net-keys {
  margin: 0;
  padding: 0;
  list-style: none;
  
  text-align: left;
}
.sf-page .sf-net-wp .sf-net-keys li { display: flex; gap: 12px; padding: 11px 0; }
.sf-page .sf-net-wp .sf-net-keys li + li { border-top: 1px solid var(--sf-hairline); }
.sf-page .sf-net-wp .sf-net-dot { flex: 0 0 auto; width: 13px; height: 13px; margin-top: 5px; border-radius: 50%; }
.sf-page .sf-net-wp .sf-net-dot.you { background: var(--sf-primary); }
.sf-page .sf-net-wp .sf-net-dot.near { background: var(--sf-badge); }
.sf-page .sf-net-wp .sf-net-dot.far { background: #D8CBF7; }
.sf-page .sf-net-wp .sf-net-key-t { display: block; }
.sf-page .sf-net-wp .sf-net-key-t strong { display: block; font-family: 'VisbyCF-Bold', sans-serif; font-size: 16px; line-height: 24px; }
.sf-page .sf-net-wp .sf-net-key-t em { display: block; font-style: normal; font-size: 14px; line-height: 22px; color: var(--sf-muted); }
.sf-page .sf-net-wp .sf-net-wrap {display:flex;flex-direction:column;align-items:center;gap:22px;max-width:620px;margin:34px auto 0}
.sf-page .sf-net-wp .sf-net-svg {width:100%;max-width:360px;height:auto}
.sf-page .sf-net-wp .sf-net-keys {width:100%}
.sf-page .sf-sp-wp .sf-sp-k {margin:0 0 18px;font-family:'VisbyCF-Bold',sans-serif;font-size:13px;line-height:20px;letter-spacing:.09em;text-transform:uppercase;color:var(--sf-muted);text-align:center}
.sf-page .sf-sp-wp .sf-press {display:flex;flex-wrap:wrap;align-items:center;justify-content:center;gap:22px 44px;margin:0 0 6px;padding:0;list-style:none}
.sf-page .sf-sp-wp .sf-press img {display:block;height:30px;width:auto;opacity:.55;filter:grayscale(1)}
/* ⛔ TWO ACROSS ON A PHONE. The press row is a centred flex-wrap with a 44px COLUMN gap, and
   these logos are 150-190px wide at 30px tall — so below about 420px exactly one fits per line
   and four logos become a 4-high column with a lot of white around it. Igor, 2026-09-17:
   "fucked up structure on mobile".
   A 2x2 grid is the right shape for four logos and does not depend on their individual widths,
   which is what made the flex version fragile: swap one logo for a wider one and the desktop
   row reflows too.
   ⛔ SCOPED TO `.sf-presswrap`, WHICH IS /how/'s WRAPPER, NOT `.sf-sp-wp`. Both press strips
   sit inside `.sf-sp-wp`, so the obvious selector also hit /reviews/ — whose strip Igor
   reviewed the same day and passed ("structure okay, size not"), and whose own rules set 44px
   logos at equal specificity and LATER in the file. That combination would have given /reviews/
   my two-column grid with its 44px logos still applying: a ~180px column holding a ~280px
   image. Checked the markup rather than assuming the wrapper was shared — it is, and that is
   exactly why the narrower hook was needed.
   ⚠️ PLACED AFTER the lifted `.sf-sp-wp` block on purpose. The note a few rules down records
   that this file has already had an equal-specificity rule silently beaten because the lifted
   block lands LATER — same specificity, later wins. Verified after this edit that no later rule
   sets `display` or `gap` on `.sf-page .sf-presswrap .sf-press`. */
@media (max-width: 575px) {
  .sf-page .sf-presswrap .sf-press {
    display: grid; grid-template-columns: 1fr 1fr;
    gap: 22px 14px; justify-items: center; align-items: center;
  }
  .sf-page .sf-presswrap .sf-press img { height: 26px; max-width: 100%; }
}
/* ⛔ THE COLUMN COUNT IS NOT SET HERE, AND THAT IS DELIBERATE. This rule shipped with
   `grid-template-columns:repeat(2,1fr)` and, because the lifted block lands LATER in the file
   at equal specificity (both are `.sf-page .<wrapper> .sf-tm`), it silently beat the
   four-across rule in the closing-band block above — measured 2 columns at 1440px where 4
   were intended, with no warning of any kind. One owner for the column count: the closing-band
   block, which also owns the breakpoints that change it. */
.sf-page .sf-sp-wp .sf-tm {display:grid;gap:16px;margin:0;padding:0;list-style:none}
.sf-page .sf-sp-wp .sf-tm li {padding:20px;background:#fff;border:1px solid var(--sf-hairline);border-radius:8px}
.sf-page .sf-sp-wp .sf-tm-by {display:flex;align-items:center;gap:12px;margin-bottom:12px}
.sf-page .sf-sp-wp .sf-tm-pfp {position:relative;flex:0 0 auto;display:inline-flex}
.sf-page .sf-sp-wp .sf-tm-av {width:46px;height:46px;padding:3px;border-radius:50%;object-fit:cover;background:linear-gradient(135deg,var(--sf-primary),var(--sf-badge))}
.sf-page .sf-sp-wp .sf-tm-tick {position:absolute;right:-1px;bottom:-1px;width:18px;height:18px;padding:1px;background:#fff;border-radius:50%;fill:var(--sf-ig-blue)}
.sf-page .sf-sp-wp .sf-tm-tick-k {fill:none;stroke:#fff;stroke-width:2.2;stroke-linecap:round;stroke-linejoin:round}
.sf-page .sf-sp-wp .sf-tm-who {display:flex;flex-direction:column;min-width:0;font-size:13px;line-height:19px;color:var(--sf-muted)}
.sf-page .sf-sp-wp .sf-tm-who b {font-family:'VisbyCF-Bold',sans-serif;font-size:15px;color:#000}
.sf-page .sf-sp-wp .sf-tm q {display:block;quotes:none;font-family:'VisbyCF-Bold',sans-serif;font-size:16px;line-height:25px;color:#000}
/* (The narrow-screen column counts live with the base rule, in the closing-band block.) */


/* ═══════════════════════════════════════════════════════════════════════════
   ⛔⛔ EVERY RESPONSIVE OVERRIDE FOR /how/ LIVES HERE, AT THE END OF THE FILE,
   AND MOVING IT HERE IS THE FIX FOR A REAL BUG — NOT TIDYING.

   A `@media` block carries NO extra specificity. These overrides used to sit in the middle of
   the page's styles, so any base rule written BELOW them at equal specificity beat them at every
   width. Measured on the deployed page: `.sf-tells` is declared `repeat(2, …)` about 130 lines
   after the 900px rule that collapses it to one column — so the spot-a-fake cards stayed TWO
   columns all the way down and rendered as two 167px slivers on a 390px phone. Nothing warned:
   the media query matched, applied, and lost on source order.

   ⚠️ SO: a new component's base rules go ABOVE this block, never below it. If you add a
   breakpoint, add it here — and if you ever reach for `!important` to make a media query stick
   on this page, the rule is in the wrong place, not under-specified.
   ═══════════════════════════════════════════════════════════════════════════ */

/* ── Narrow screens ──────────────────────────────────────────────────────────
   ⛔ THE TWO STEP RULES THAT USED TO LIVE HERE ARE GONE, NOT MISLAID. They were
   `grid-template-columns: 44px minmax(0,1fr)` and `.sf-steps img { display: none }` — written
   for the VERTICAL track. Against the horizontal grid the first fights the column count and the
   second deletes the art from every phone. A responsive override outlives the layout it was
   written for and goes on applying to its replacement; check this block whenever a grid above
   it changes.
   ⚠️ And at 1100/760 the STEP COUNT changes, so the connector has to move with it — a rule
   drawn through four numbers is wrong the moment there are two per row. */
@media (max-width: 1100px) {
  .sf-page .sf-tells { grid-template-columns: repeat(2, minmax(0,1fr)); }
  .sf-page .sf-steps { grid-template-columns: repeat(2, minmax(0,1fr)); gap: 40px 28px; }
  /* ⚠️ Two per row: a segment from step 2 would run off the end of the row into nothing, and
     there is no line to draw from 2 down to 3. The numbers carry the order on their own —
     and they are colour-coded, so the progression survives losing the track. */
  .sf-page .sf-steps li::after,
  .sf-page .sf-steps li::before { display: none; }
}
@media (max-width: 900px) {
  /* ⚠️ `.sf-hero-grid` left this list on 2026-09-15 — it wrapped the eyebrow while the hero
     was three stacked siblings, and the restructure removed it. `.sf-hero-rest` collapses in
     its own block below. */
  .sf-page .sf-zag { grid-template-columns: 1fr; }
  /* ⚠️ THE ART GOES ABOVE THE COPY ON A PHONE, not below it. A CTA whose button is the last
     thing before the next band wants the visual to have already made its point. */
  .sf-page .sf-cyc { grid-template-columns: 1fr; gap: 28px; }
  .sf-page .sf-cyc-art { order: -1; justify-items: stretch; }
  .sf-page .sf-cyc-art .sf-draw { max-width: none; width: 100%; }
  .sf-page .sf-cyc-copy p { max-width: none; }
  .sf-page .sf-zag.is-flip .sf-zag-art { order: 0; }
  .sf-page .sf-facts { grid-template-columns: 1fr; }
  .sf-page .sf-facts li + li { border-top: 1px solid var(--sf-hairline); }
  .sf-page .sf-tells { grid-template-columns: 1fr; }
  .sf-page .sf-trust { grid-template-columns: 1fr; }
}
@media (max-width: 781px) {
  .sf-page .sf-band { padding: 52px 0; }
  .sf-page .sf-hero-band { padding: 40px 20px 44px; }
  .sf-page .sf-in { padding-inline: 20px; }
}
@media (max-width: 760px) {
  .sf-page .sf-steps { grid-template-columns: 1fr; gap: 34px; }
  /* ⚠️ THE `sf-step-art` RULE THAT USED TO BE HERE IS GONE WITH THE IMAGES IT POSITIONED
     (2026-09-15). A responsive override for an element that no longer exists is dead weight
     that reads as live layout to whoever edits this next — the same trap as the two vertical
     step rules that were removed from the 1100px block above.
     Stacked, a step is the number beside its heading with the copy underneath. */
  .sf-page .sf-steps li { display: grid; grid-template-columns: 44px minmax(0,1fr);
    grid-template-areas: "n h" ". p"; gap: 0 16px; align-items: center; }
  .sf-page .sf-steps .n { grid-area: n; }
  .sf-page .sf-steps h3 { grid-area: h; margin: 0; min-height: 0; }
  .sf-page .sf-steps p { grid-area: p; margin-top: 14px; }
}

/* ── The mobile type ramp ────────────────────────────────────────────────────
   ⛔ EVERY SIZE ON THIS PAGE IS A FIXED px, SO NOTHING SHRINKS ON ITS OWN. The file's own header
   explains why (the Astro side drops the root size and rem would then render ~12% smaller there
   than here) — the cost is that a 20px hero lead is still 20px on a 390px screen, where it runs
   to five words a line. Igor, 2026-09-14: "downscale the font a bit".
   ⚠️ SIZES ONLY. No colours, no layout — those are decided above and a media block that changes
   both is where a phone-only bug hides. */
@media (max-width: 781px) {
  .sf-page { font-size: 15px; }
  .sf-page h2, .sf-page .wp-block-heading { font-size: 25px; line-height: 33px; }
  .sf-page h3 { font-size: 17px; line-height: 26px; }
  .sf-page .entry-content p, .sf-page .sf-zag p { font-size: 15px; line-height: 25px; }
  .sf-page .sf-hero-lead { font-size: 17px; line-height: 28px; }
  .sf-page .sf-hero-k { font-size: 12px; }
  .sf-page .sf-steps h3 { font-size: 17px; line-height: 26px; }
  .sf-page .sf-steps p { font-size: 14px; line-height: 23px; }
  .sf-page .sf-facts b { font-size: 15px; line-height: 23px; }
  .sf-page .sf-facts span em { font-size: 14px; line-height: 22px; }
  .sf-page .sf-tells li b { font-size: 16px; line-height: 24px; }
  .sf-page .sf-tells li p { font-size: 14px; line-height: 23px; }
  .sf-page .sf-cal li { font-size: 15px; line-height: 23px; }
  .sf-page .sf-faq summary h3 { font-size: 16px; line-height: 25px; }
  .sf-page .sf-faq details > p { font-size: 14px; line-height: 23px; }
  .sf-page .sf-btn { padding: 15px 28px; font-size: 15px; }
  .sf-page .sf-cta-note { font-size: 13px; line-height: 21px; }
  .sf-page .sf-close h2 { font-size: 23px; line-height: 31px; }
  .sf-page .sf-band-coral .sf-tm li q { font-size: 15px; line-height: 24px; }
  .sf-page .sf-modal-title { font-size: 22px; line-height: 30px; }
  .sf-page .sf-modal-lead { font-size: 15px; line-height: 24px; }
  .sf-page .sf-modal-qty { font-size: 34px; line-height: 38px; }
  .sf-page .sf-modal-unit { font-size: 16px; line-height: 25px; }
  .sf-page .sf-cd-line-h { font-size: 16px; line-height: 23px; }
  .sf-page .sf-handoff-input { font-size: 17px; }
}


/* ═══════════════════════════════════════════════════════════════════════════
   The footer's handle field

   ⚠️ A NEWSLETTER-SHAPED BOX FOR AN INSTAGRAM HANDLE (Igor, 2026-09-15). One line, one field,
   one arrow. ⛔ It must stay smaller than the offer card — that is the brief, not a detail:
   "it obviously cannot be a full blown component as the interactive CTA one". No deal strip,
   no countdown, no chips, no live validation.
   ⚠️ THESE RULES ARE DUPLICATED IN src/styles/brand.css. The Astro pages never load this
   stylesheet and the WordPress pages never load that one, so the footer has to be styled twice
   to look the same on both halves of the site — the same pairing the header carries. Change
   one, change the other.
   ═══════════════════════════════════════════════════════════════════════════ */
.sf-fcta { margin-top: 24px; max-width: 300px; }
.sf-fcta-k { margin: 0 0 6px; font-family: 'VisbyCF-Bold', sans-serif; font-size: 16px; line-height: 24px; }
/* The value-proposition line. ⛔ It restates the offer, it never extends it — same numbers as
   the hero lead and the deal strip. */
.sf-fcta-vp { margin: 0 0 14px; font-size: 14px; line-height: 22px; color: var(--sf-muted); }

/* Visually hidden, not removed: the field needs a programmatic label and the placeholder is
   not one. */
.sf-fcta-lab { position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; }
.sf-fcta-form { position: relative; display: flex; align-items: center; gap: 2px;
  padding: 4px 4px 4px 14px; background: #fff; border: 1px solid var(--sf-hairline); border-radius: 999px; }
.sf-fcta-form:focus-within { border-color: var(--sf-primary); box-shadow: 0 0 0 4px rgba(255,51,102,0.12); }
.sf-fcta-at { flex: 0 0 auto; font-size: 16px; line-height: 1; opacity: 0.4; }
.sf-fcta-in { flex: 1 1 auto; min-width: 0; height: 38px; padding: 0 6px; border: 0; outline: 0;
  background: transparent; font-family: 'VisbyCF-Medium', sans-serif; font-size: 15px; color: #000; }
.sf-fcta-in::placeholder { color: #000; opacity: 0.34; }
.sf-fcta-go { flex: 0 0 auto; display: grid; place-items: center; width: 38px; height: 38px;
  padding: 0; border: 0; border-radius: 50%; background: var(--sf-primary); cursor: pointer; }
.sf-fcta-go:hover { background: #e62e5c; }
.sf-fcta-go svg { width: 17px; height: 17px; fill: none; stroke: #fff; stroke-width: 2.2;
  stroke-linecap: round; stroke-linejoin: round; }
.sf-fcta-note { margin: 9px 0 0; font-size: 13px; line-height: 20px; color: var(--sf-muted); }


/* ── Quantity table ──────────────────────────────────────────────────────
   ⛔ PORTED FROM src/styles/brand.css 2026-09-16, NOT SHARED WITH IT. /how/ is a
   WordPress page and cannot load brand.css, which is why this file already carries
   namespaced copies of other homepage components. The table itself was cut from the
   homepage on 2026-09-13 ("cut the homepage to category shape") and its CSS was left
   behind as dead rules; it now lives on /how/ instead, where "how many can I get?"
   is a question the page is already answering.
   ⚠️ THE TWO COPIES CAN DRIFT. They are the same rules today. If you restyle one,
   restyle the other or delete the one you are not using — brand.css has carried these
   as dead rules since 2026-09-13 and nothing noticed.
   ⚠️ The mobile half matters: under 575px the table stops being a table and becomes
   stacked blocks with the colour moved to a left border. Headless Chrome clamps the
   viewport at ~500px here, so that breakpoint cannot be verified with this tooling. */
/* ── Quantity table (the "how many" section) ─────────────────────────────
   ⚠️ A TABLE, NOT A CARD GRID, because the content is a lookup: the reader arrives having
   searched a specific number and wants the row for that number. It also lets the word orders
   people actually type sit as row headings instead of being crammed into a sentence.
   ⛔ `site.css` styles bare `table`; everything here is class-scoped so it cannot inherit it,
   and the wrapper scrolls rather than letting the page body scroll sideways on a phone. */
.sf-qty-wrap { overflow-x: auto; border: 1px solid var(--sf-hairline); border-radius: 8px; }
.sf-qty { width: 100%; margin: 0; border-collapse: collapse; font-size: 15px; }
.sf-qty th, .sf-qty td { padding: 13px 16px; text-align: left; vertical-align: top; }
.sf-qty thead th {
  font-size: 12px; letter-spacing: 0.06em; text-transform: uppercase;
  color: var(--sf-muted); font-weight: 600; border-bottom: 1px solid var(--sf-hairline);
  white-space: nowrap;
}
.sf-qty tbody th {
  font-weight: 600; white-space: nowrap; padding-right: 24px;
}
.sf-qty tbody td { color: var(--sf-muted); line-height: 25px; }
/* ⚠️ THE TIME COLUMN IS THE ANSWER, SO IT READS LIKE ONE (Igor, 2026-09-17: the table should
   "do a time estimate how much free followers they will get if done daily"). It is the only
   cell a skimmer actually looks up, so it is set in the bold face, in body colour rather than
   muted, and never wraps — "About 7 weeks" breaking over two lines would make the column
   ragged and unscannable. `width: 1%` plus nowrap is the standard trick for a column that
   should be exactly as wide as its widest value and no wider. */
.sf-qty tbody .sf-qty-t {
  font-family: 'VisbyCF-Bold', sans-serif;
  color: var(--sf-ink, #101012);
  white-space: nowrap;
  width: 1%;
  padding-right: 24px;
}
/* The conditional frame under the table. Smaller and muted: it qualifies the numbers above
   without competing with them. ⛔ It is not decoration — Gate 2 bars a stated delivery window,
   and this is the sentence that keeps the table arithmetic rather than a schedule. */
.sf-qty-note { font-size: 14px; line-height: 24px; color: var(--sf-muted); margin-top: 14px; }
.sf-qty tbody tr + tr th, .sf-qty tbody tr + tr td { border-top: 1px solid var(--sf-hairline); }

/* ⚠️ THE COLOUR ENCODES THE ANSWER, IT IS NOT DECORATION (Igor: "more interesting/colorful,
   dont overdo it"). Three kinds of row and they are genuinely different answers: one IS our
   offer, one gives you more than you searched for, and four are warnings about what that
   number means elsewhere. A rainbow would say nothing; three states say the whole table.
   The left edge carries it so the row still reads as a row. */
.sf-qty tbody th { border-left: 3px solid transparent; padding-left: 16px; }
.sf-qty tbody .is-ours { background: rgba(255, 51, 102, 0.05); }
.sf-qty tbody .is-ours th { border-left-color: var(--sf-primary); color: var(--sf-primary); }
.sf-qty tbody .is-more th { border-left-color: #12784A; }
.sf-qty tbody .is-warn th { border-left-color: rgba(154, 100, 7, 0.5); }
/* The row that is our offer gets a marker, so the table answers "which one is this site?"
   without the reader matching numbers. */
.sf-qty tbody .is-ours th::after {
  content: 'this page';
  display: inline-block;
  margin-left: 9px;
  padding: 2px 8px;
  font-family: 'VisbyCF-Bold', sans-serif;
  font-size: 11px; letter-spacing: 0.04em; text-transform: uppercase;
  color: #fff; background: var(--sf-primary);
  border-radius: 999px;
  vertical-align: 2px;
}
.sf-split-i-warn { color: #9A6407; background: rgba(255, 223, 126, 0.22); }
/* ⚠️ ON A PHONE THE LOOKUP STACKS — TWO COLUMNS OF PROSE DO NOT FIT IN ~390px.
   The previous mobile rule kept two columns and only turned `white-space: nowrap` off, so
   the row heading wrapped to THREE lines ("Free 10 / Instagram / followers") in a column
   about 150px wide, and every row was as tall as its narrowest column. Stacking makes each
   row one block: the searched phrase on its own line, the answer under it, full width.
   ⛔ The table is still a table — see the lookup note above. Only the PRESENTATION stacks,
   and the searched phrase stays a heading so the reader can still scan for their number.
   ⚠️ `display: block` on table elements strips native table semantics in WebKit/VoiceOver,
   which is why the markup carries explicit role="table"/row/rowheader/cell attributes. Do not
   remove them: they are inert on desktop and load-bearing here.
   ⚠️ THE COLOUR EDGE MOVES TO THE ROW. Stacked, the `th` is a full-width block, so a
   `border-left` on it would draw beside the heading only and stop above the answer. On the
   `tr` it runs the height of the whole row, which is what makes the three states still read.
   ⚠️ Mobile CANNOT be verified with this repo's headless tooling (it clamps at ~500px) — 500px
   does exercise this breakpoint, but 390px needs a real device. */
@media (max-width: 575px) {
  .sf-qty { font-size: 15px; }
  .sf-qty thead { display: none; }
  .sf-qty, .sf-qty tbody, .sf-qty tbody tr, .sf-qty tbody th, .sf-qty tbody td {
    display: block;
    width: 100%;
  }
  .sf-qty tbody tr { border-left: 3px solid transparent; }
  .sf-qty tbody tr + tr { border-top: 1px solid var(--sf-hairline); }
  .sf-qty tbody th, .sf-qty tbody td { border: 0; white-space: normal; }
  .sf-qty tbody th { padding: 13px 15px 3px; }
  .sf-qty tbody td { padding: 0 15px 13px; line-height: 24px; }
  /* ⛔ THE STACK PUTS THE TIME BETWEEN THE SEARCH AND THE EXPLANATION, WHICH IS THE READING
     ORDER THE TABLE IS FOR. `width: 1%` and nowrap are desktop-column tricks and have to be
     undone here or the cell keeps a table-cell width inside a block layout. */
  .sf-qty tbody .sf-qty-t { width: auto; white-space: normal; padding: 0 15px 4px; }
  .sf-qty tbody .is-ours { border-left-color: var(--sf-primary); }
  .sf-qty tbody .is-more { border-left-color: #12784A; }
  .sf-qty tbody .is-warn { border-left-color: rgba(154, 100, 7, 0.5); }
}


/* ═══════════════════════════════════════════════════════════════════════════
   /reviews/   ·   added 2026-09-17

   ⚠️ EVERYTHING HERE IS SCOPED TO `.sf-revpage`, which only templates/page-reviews.html
   emits. The shared banded-page scope is `.sf-page`, above: tokens, bands, `.sf-in`, the
   hero, `.sf-btn`, `.sf-faq`, `.sf-press` and `.sf-tm` all come from there and are not
   restated. This block carries only what /reviews/ adds.

   ⛔ THIS IS THE END OF THE FILE AND THAT IS LOAD-BEARING — for THREE declarations, no more.
   Each is a longhand losing to an earlier shorthand at EQUAL specificity (0,3,0), where only
   source order decides:
     · `.sf-revpage .sf-revpress .sf-sp-k { margin-bottom }`  vs  `:1260 { margin: 0 0 18px }`
     · `.sf-revpage .sf-revquotes .sf-tm { gap }`             vs  `:1269 { gap: 16px }`
     · `.sf-revpage .sf-revquotes .sf-tm { margin-top }`      vs  `:1269 { margin: 0 }`
   ⛔ If you move this block up, those three revert and nothing will warn you. Everything else
   here wins on specificity and does not care where it sits.

   ⚠️ THIS NOTE HAS NAMED THE WRONG RULES TWICE, SO CHECK BEFORE YOU TRUST THE NEXT VERSION.
   It first said the press kicker's ALIGNMENT was order-dependent: it was not — the centring
   comes from `:1260` at (0,3,0) and the `.sf-revpage .sf-revpress { text-align: center }`
   written to override it was (0,2,0) and had never done anything. The correction then named
   that line's `max-width` instead, which is also wrong: its competitor is `:462`
   `.sf-page .entry-content :where(p, ul, ol)`, and `:where()` contributes ZERO, so that is
   (0,2,0) and the longhand wins on specificity from anywhere in the file. The same correction
   named the quote grid's COLUMN COUNT, which cannot collide either — the rule that sets four
   is `.sf-page .sf-band-coral .sf-tm` (:537) and the reviews quote band is a plain `.sf-band`.
   A false reason standing beside a true one is worse than no reason: it invites someone to test
   the claim, find it hollow, and move the block.
   ⛔ THE REAL FIX IS TO STOP DEPENDING ON ORDER AT ALL — give those three selectors one extra
   step (`main.sf-revpage …`, or `.sf-revpage.sf-revpage …`) and this constraint disappears.
   It is left as-is only because it is a live page late in a session; do it before page three,
   because "must be last in the file" is a constraint that can only be satisfied once.

   ⛔ NOTHING IN HERE DRAWS A RATING. `.sf-stars` is a drawn design element on a quote a reader
   can weigh. No `aggregateRating` or `Review` markup ships with it — that half of Gate 2 did
   not move. See deploy/pages/reviews.html and CLAUDE.md.
   ═══════════════════════════════════════════════════════════════════════════ */

/* ── Star rows ─────────────────────────────────────────────────────────────
   ⚠️ PORTED FROM brand.css, NOT SHARED. brand.css never loads on a WordPress page, so the
   two halves of this site hold one design in two files by necessity — the same arrangement
   the token block at the top of this file records. Change one, change the other. */
.sf-revpage .sf-stars { display: inline-flex; gap: 2px; color: var(--sf-yellow); }
.sf-revpage .sf-stars svg { width: 15px; height: 15px; fill: currentColor; stroke: none; }

/* ── The hero proof card ───────────────────────────────────────────────────
   The 2022 page's "Join 9,000+ Clients" avatar row, rebuilt as the hero's art so the page
   opens on faces rather than on a paragraph.
   ⚠️ THE FIGURE IS 10,000+ AND THE MARKUP SAYS SO — see the ⛔ block in page-reviews.html.
   ⚠️ The faces are stock, are `alt=""` and `aria-hidden`, and are not named anywhere. They
   illustrate a membership; they do not assert seven specific customers. */
.sf-revpage .sf-revproof {
  max-width: 460px;
  margin-inline: auto;
  padding: 34px 32px 30px;
  background: #fff;
  border: 1px solid var(--sf-hairline);
  border-radius: 20px;
  box-shadow: 0 22px 54px rgba(13, 13, 17, 0.10);
  text-align: center;
}
/* The overlapping row. `direction: rtl` on the list with `ltr` on each item is what makes the
   LEFT-most face sit on top — a plain negative margin stacks them the other way and the row
   reads as falling backwards. */
.sf-revpage .sf-revfaces {
  display: flex;
  direction: rtl;
  justify-content: center;
  margin: 0 0 20px;
  padding: 0;
  list-style: none;
}
.sf-revpage .sf-revfaces li { direction: ltr; margin-right: -14px; }
.sf-revpage .sf-revfaces li:first-child { margin-right: 0; }
.sf-revpage .sf-revfaces img {
  display: block;
  width: 56px;
  height: 56px;
  border-radius: 50%;
  object-fit: cover;
  border: 3px solid #fff;
}
.sf-revpage .sf-revproof-n {
  margin: 0 0 8px;
  max-width: none;
  font-family: 'VisbyCF-ExtraBold', sans-serif;
  font-size: 26px;
  line-height: 34px;
}
.sf-revpage .sf-revproof-s {
  margin: 0 0 16px;
  max-width: none;
  font-size: 15px;
  line-height: 23px;
  color: var(--sf-muted);
}

/* ── The press strip on the lavender band ──────────────────────────────────
   ⛔ THIS WAS A BESPOKE LEFT-ALIGNED ROW WITH THE LOGOS SPREAD EDGE TO EDGE, AND IT WAS WRONG.
   Igor, 2026-09-17: "fix this asap". Two faults, and the second is the one that made it look
   broken: `justify-content: space-between` pushed four small greyscale marks across the full
   1200px, so they read as four unrelated images rather than as a strip — and the band kept the
   standard 76px padding around a 38px row, which is most of a screen of empty lavender.
   ⚠️ THE SITE ALREADY HAD THE ANSWER AND I DID NOT USE IT. `PressRow.astro` on the homepage is
   a CENTRED kicker over a CENTRED row (brand.css `.sf-press-band { text-align: center }`, and
   `.sf-press { justify-content: center; gap: 22px 44px }`), and the lifted block in this file
   is the same treatment. So this now overrides nothing about alignment — it only makes the band
   compact, because a press strip is a trust marker, not a section. */
/* ⛔ `max-width: none` ON THE KICKER, OR IT IS NOT ACTUALLY CENTRED. `.sf-page .entry-content
   :where(p, ul, ol)` caps every paragraph at 68ch for prose. The lifted block already releases
   `.sf-press` from that, but not the `<p>` above it — so "AS FEATURED ON" was centring inside a
   68ch box pinned to the LEFT edge, landing its midpoint at x=396 against the band's 640.
   It reads as "roughly centred", which is why it survived a look and needed a measurement. */
.sf-revpage .sf-revpress .sf-sp-k { max-width: none; margin-bottom: 26px; }
/* ⚠️ 44px, NOT THE LIFTED BLOCK'S 30px. Igor, 2026-09-17: "structure okay, size not". The base
   rule is sized for /how/'s closing band, where the strip is a masthead inside a white panel
   among other content. Here it is a band of its own with nothing else in it, and four 30px marks
   at 55% opacity across 1200px read as a footnote rather than as a credential. */
.sf-revpage .sf-revpress .sf-press img { height: 44px; opacity: 0.62; }
.sf-revpage .sf-revpress .sf-press { gap: 26px 56px; }
/* ⚠️ A CLASS ON THE BAND, NOT `:has()`. The first version selected the band by its contents,
   which works in current browsers and fails silently in anything older — and a press strip
   reverting to 76px of padding is exactly the kind of regression nobody files. The band names
   itself instead. */
.sf-revpage .sf-revpress-band { padding-top: 34px; padding-bottom: 34px; }

/* ── The solo video section ────────────────────────────────────────────────
   ⛔ CENTRED, AT EVERY WIDTH. Igor, 2026-09-17: "bad structure, bad design, reference home/how
   for better reference, needs to be centered" — and the reference settles it: the homepage's own
   video section is centred (`.sf-vids .row.justify-content-center.text-center` in
   VideoWall.astro). I had ranged this left an hour earlier on a mis-read of "centered mobile
   only", citing /how/'s single left axis. That axis is right for ARGUMENT bands; a section whose
   whole content is one media object centred under its own header is the site's other pattern,
   and it is the one this section is. ⛔ Both video sections use it — do not split them again. */
.sf-revpage .sf-revsolo { text-align: center; }
/* ⛔ THE KICKER TREATMENT IS SCOPED TO `.sf-sp-wp` AND TWO OF THIS PAGE'S KICKERS ARE NOT IN ONE.
   `.sf-page .sf-sp-wp .sf-sp-k` (line ~1260) is the only rule that makes a `.sf-sp-k` look like a
   kicker — 13px, uppercase, letter-spaced, muted. "As featured on" sits inside `.sf-sp-wp` and
   got it; "Watch first" and "In their own words" do not, so they rendered as ordinary body
   paragraphs while claiming the same class. Three kickers on one page, one of them styled.
   ⚠️ SCOPED HERE RATHER THAN WIDENING THE BASE SELECTOR. Dropping `.sf-sp-wp` from line 1260
   would reach every WordPress page at once, including /how/, where any unstyled `.sf-sp-k` would
   silently change. This page states what it needs; the wider fix is a conversation. */
.sf-revpage .sf-sp-k {
  margin: 0 0 14px;
  font-family: 'VisbyCF-Bold', sans-serif;
  font-size: 13px;
  line-height: 20px;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--sf-muted);
}
.sf-revpage .sf-revsolo .sf-sp-k { text-align: center; }
.sf-revpage .sf-revsolo h2 { max-width: 26ch; margin-inline: auto; }
.sf-revpage .sf-revsolo p { max-width: 62ch; margin-inline: auto; }
.sf-revpage .sf-vsolo { max-width: 760px; margin: 34px auto 0; }
.sf-revpage .sf-vsolo .sf-cta-row { margin-top: 30px; text-align: center; }
.sf-revpage .sf-vsolo .sf-cta-row .sf-btn { display: inline-block; }
.sf-revpage .sf-vsolo .sf-cta-row .sf-cta-note { margin-inline: auto; }

/* ── The video card, and the carousel around three of them ─────────────────
   ⚠️ PORTED FROM src/components/VideoWall.astro AND brand.css. Same necessity as the stars:
   a WordPress page cannot load either.
   ⛔ THE DARK OVERLAY ON THE PLAY BUTTON IS NOT DECORATION. The poster PNGs are screenshots
   of the 2022 player with a play triangle painted into the image. Without a wash to sink it,
   two play buttons appear and only one of them does anything — which is exactly why these
   videos were reported as broken. */
.sf-revpage .sf-vslide {
  background: #fff;
  border: 1px solid var(--sf-hairline);
  border-radius: var(--sf-radius);
  overflow: hidden;
  box-shadow: 0 18px 44px rgba(13, 13, 17, 0.10);
  /* ⛔ RE-BIND `--sf-muted` ON THE CARD, NOT ON THE BAND. `.sf-band-dark` rebinds it to
     #B9B2C4 so body copy reads on ink — but this card is WHITE, and `.sf-vid-t` reads the
     same token. Without this line the handle and follower count render light grey on white.
     The coral band's block records the identical trap for `.sf-tm-who`. */
  --sf-muted: #4A4453;
  color: var(--sf-text);
}
.sf-revpage .sf-vid-media { position: relative; aspect-ratio: 4 / 3; background: var(--sf-ink); }
.sf-revpage .sf-vid-el { display: block; width: 100%; height: 100%; object-fit: cover; background: var(--sf-ink); }
.sf-revpage .sf-vplay {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  width: 100%;
  height: 100%;
  border: 0;
  background: rgba(13, 13, 17, 0.34);
  cursor: pointer;
}
.sf-revpage .sf-vplay svg {
  width: 34px;
  height: 34px;
  padding: 18px;
  fill: #fff;
  border-radius: 50%;
  background: var(--sf-primary);
  box-sizing: content-box;
  box-shadow: 0 10px 28px rgba(255, 51, 102, 0.45);
}
.sf-revpage .sf-vid-by { display: flex; align-items: center; gap: 12px; padding: 14px 16px; }
.sf-revpage .sf-vid-av {
  position: relative;
  flex: 0 0 auto;
  width: 38px;
  height: 38px;
  border-radius: 50%;
  background: linear-gradient(135deg, var(--sf-primary), var(--sf-accent));
}
.sf-revpage .sf-vid-av::after { content: ''; position: absolute; inset: 2px; border-radius: 50%; background: #fff; }
.sf-revpage .sf-vid-av::before { content: ''; position: absolute; inset: 4px; border-radius: 50%; background: var(--sf-pink-light); z-index: 1; }
.sf-revpage .sf-vid-t { display: flex; flex-direction: column; min-width: 0; font-size: 13px; line-height: 19px; }
.sf-revpage .sf-vid-t b { font-family: 'VisbyCF-Bold', sans-serif; font-size: 15px; }
.sf-revpage .sf-vid-t b em { font-style: normal; font-weight: normal; margin-left: 6px; font-size: 13px; color: var(--sf-muted); }
.sf-revpage .sf-vid-t > span { color: var(--sf-muted); }
/* The stars sit at the end of the by-line, not beside the name. */
.sf-revpage .sf-vid-by .sf-stars { margin-left: auto; }

/* ⛔⛔ THE PRESENTER'S NAME WAS WHITE ON A WHITE CARD, AND EVERY OTHER CHECK WAS GREEN.
   `.sf-page .sf-band-dark b, .sf-page .sf-band-dark strong { color: #fff }` exists so bold text
   reads on ink — and it reaches INSIDE this card, which is white. `<b>Jenny</b>` rendered
   invisible; the role and handle below it were fine, so the card looked like it simply had no
   name rather than like a colour bug. Found by rendering the deployed page, 2026-09-17.
   ⚠️ I ALREADY WROTE THE NOTE FOR THIS TRAP ONE RULE UP AND STILL SHIPPED IT. The `.sf-vslide`
   block rebinds `--sf-muted` for exactly this reason, citing the coral band's identical
   problem with `.sf-tm-who` — and `--sf-muted` was only half of it. The band sets a token AND
   a direct colour on two element selectors; rebinding the token fixes the first half only.
   ⛔ Specificity, not order: the band's rule is (0,2,1), so this has to name the band too.
   A `.sf-revpage .sf-vslide b` at (0,2,1) would tie and lose on source order. */
.sf-revpage .sf-band-dark .sf-vslide b,
.sf-revpage .sf-band-dark .sf-vslide strong { color: var(--sf-text); }

/* ⚠️ The arrows sit OUTSIDE the stage, which is why this keeps an auto/1fr/auto grid rather than
   being a plain block. Centred with its header — see the solo section above for why. */
.sf-revpage .sf-vcar {
  position: relative;
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  align-items: center;
  gap: 18px;
  max-width: 800px;
  margin: 34px auto 0;
}
/* The carousel band's header centres with it — same pattern, same reason as the solo section. */
.sf-revpage .sf-band-dark .sf-in { text-align: center; }
.sf-revpage .sf-band-dark .sf-in p { margin-inline: auto; max-width: 62ch; }
.sf-revpage .sf-band-dark .sf-in h2 { margin-inline: auto; }
/* ⚠️ The by-line INSIDE the card stays ranged left — it is a person's name, not a section header. */
.sf-revpage .sf-vslide { text-align: left; }
.sf-revpage .sf-vcar-stage { min-width: 0; }
.sf-revpage .sf-vcar-arw {
  display: grid;
  place-items: center;
  width: 44px;
  height: 44px;
  border: 1px solid rgba(255, 255, 255, 0.28);
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.10);
  cursor: pointer;
}
/* ⚠️ THE ARROWS SIT ON INK HERE, NOT ON WHITE. brand.css draws them as white discs with a
   dark chevron because the homepage runs this carousel on a light ground; this page runs it
   on `.sf-band-dark`, where a white disc is the loudest object in the band and the chevron
   would be black on it either way. Ghosted, with a white stroke, they read as controls
   without competing with the video. */
.sf-revpage .sf-vcar-arw svg { width: 20px; height: 20px; fill: none; stroke: #fff; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
.sf-revpage .sf-vcar-arw:hover { background: rgba(255, 255, 255, 0.18); }
.sf-revpage .sf-vcar-dots { grid-column: 1 / -1; display: flex; justify-content: center; gap: 8px; margin: 4px 0 0; padding: 0; list-style: none; }
.sf-revpage .sf-vcar-dots button { width: 9px; height: 9px; padding: 0; border: 0; border-radius: 50%; background: rgba(255, 255, 255, 0.34); cursor: pointer; }
.sf-revpage .sf-vcar-dots button[aria-current='true'] { width: 26px; border-radius: 999px; background: var(--sf-primary); }
@media (prefers-reduced-motion: no-preference) {
  .sf-revpage .sf-vcar-dots button { transition: width 0.2s ease, background 0.2s ease; }
}

/* ── The written quotes ────────────────────────────────────────────────────
   ⚠️ TWO ACROSS, NOT FOUR. /how/ runs four because they sit in its closing band beside a CTA
   panel; here they are the section, and brand.css measured the difference — four over a
   1200px container gives each quote ~230px of measure, which reads as a column of fragments.
   Two gives ~520px and the quotes read as somebody talking.
   ⛔ THE COLUMN COUNT IS SET HERE BECAUSE NOTHING ELSE SETS IT FOR THIS WRAPPER. The lifted
   `.sf-page .sf-sp-wp .sf-tm` rule declares no template on purpose, and the four-across rule
   is scoped to `.sf-band-coral`. This band is white. */
.sf-revpage .sf-revquotes .sf-tm { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 20px; margin-top: 34px; }
.sf-revpage .sf-revquotes .sf-tm li { display: flex; flex-direction: column; padding: 22px; }
.sf-revpage .sf-revquotes .sf-tm li q { flex: 1 1 auto; }
.sf-revpage .sf-revquotes .sf-tm-by { margin-bottom: 14px; }
.sf-revpage .sf-revquotes .sf-tm-by .sf-stars { margin-left: auto; }
.sf-revpage .sf-revquotes .sf-tm-who b { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* ── "Four things to check" ────────────────────────────────────────────────
   ⚠️ `max-width: none` IS REQUIRED. `.sf-page .entry-content :where(p, ul, ol)` caps every
   list at 68ch for prose; a two-column card grid inside that cap renders as two slivers down
   the left half of the band. */
.sf-revpage .sf-revtells {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 18px;
  max-width: none;
  margin: 34px 0 0;
  padding: 0;
  list-style: none;
}
.sf-revpage .sf-revtells li {
  margin: 0;
  padding: 22px 24px;
  background: #fff;
  border: 1px solid var(--sf-hairline);
  border-radius: var(--sf-radius);
}
/* ── The trust cards' icons ───────────────────────────────────────────────
   Igor, 2026-09-17: "icons are needed here". Stroke icons in a tinted square, which is the
   site's own idiom — /how/'s `.sf-facts` draws inline SVG at 20px and `.sf-pros` sets them in a
   `--sf-primary` disc. ⚠️ `fill: none` + `stroke: currentColor` is load-bearing: these paths are
   outlines, and the theme has no global SVG rule, so without it they render as filled blobs. */
/* ⛔⛔ `.sf-revtells .sf-revtells-i`, NOT `.sf-revtells-i` — THE ICON IS A `<span>` AND THE CARD
   BODY RULE IS ALSO A `<span>`. `.sf-revpage .sf-revtells span` is (0,2,1) and beat the plain
   class at (0,2,0), so the icon inherited `color: var(--sf-muted)` and `display: block`: the
   glyphs rendered dark grey instead of pink and the 44px square collapsed. Third class takes it
   to (0,3,0). ⚠️ SAME SHAPE AS THE WHITE-ON-WHITE NAME IN THE DARK BAND — a broad rule written
   for one element reaching into a new one added later. Check what already selects your tag. */
.sf-revpage .sf-revtells .sf-revtells-i {
  display: grid;
  place-items: center;
  width: 44px;
  height: 44px;
  margin-bottom: 16px;
  border-radius: var(--sf-radius);
  background: var(--sf-pink-light);
  color: var(--sf-primary);
}
.sf-revpage .sf-revtells .sf-revtells-i svg {
  width: 24px;
  height: 22px;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.9;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.sf-revpage .sf-revtells b { display: block; margin-bottom: 8px; font-family: 'VisbyCF-Bold', sans-serif; font-size: 18px; line-height: 26px; }
.sf-revpage .sf-revtells span { display: block; font-size: 15px; line-height: 24px; color: var(--sf-muted); }

/* ── Responsive ────────────────────────────────────────────────────────────
   ⚠️ AT THE END OF THIS BLOCK, FOR THE REASON THE /how/ OVERRIDES ARE AT THE END OF THEIRS:
   a `@media` block carries NO extra specificity, so any base rule written below it at equal
   specificity beats it at every width. This file has already shipped that defect once — the
   spot-a-fake cards stayed two columns down to 390px. Keep new base rules ABOVE this line.
   ⚠️ Mobile CANNOT be verified with this repo's headless tooling (it clamps at ~500px). */
@media (max-width: 991px) {
  .sf-revpage .sf-revquotes .sf-tm { grid-template-columns: 1fr; }
  .sf-revpage .sf-revtells { grid-template-columns: 1fr; }
  .sf-revpage .sf-revproof { max-width: 420px; }
}
/* ⚠️ CENTRED ON A PHONE, RANGED LEFT EVERYWHERE ELSE (Igor, 2026-09-17). This is the site's
   existing pattern, not a new one — brand.css centres `.sf-cta-line` and `.sf-cd-bar` at exactly
   this breakpoint and nowhere above it. Once a band stacks to one column, ragged-left copy under
   a heading that fills the width reads as misalignment; above it, the left axis is the design.
   ⛔ 767, matching brand.css. The zig-zag note there records why the two breakpoints differ:
   between 768 and 991 a row has stacked but a button is still auto-width and sits correctly.
   ⚠️ Mobile CANNOT be verified with this repo's tooling — headless Chrome clamps at ~500px, so
   500 exercises this rule but 390 needs a real device. */
@media (max-width: 767px) {
  .sf-revpage .sf-revpress .sf-press { gap: 18px 30px; }
  .sf-revpage .sf-revproof { padding: 26px 22px 24px; }
  .sf-revpage .sf-revfaces img { width: 46px; height: 46px; }
  .sf-revpage .sf-revproof-n { font-size: 22px; line-height: 30px; }

  /* ⚠️ THE CENTRING MOVED OUT OF HERE — both video sections are centred at every width now, so
     restating it at 767 would be a rule that cannot fail. What is left is phone-only: a button
     that fills its column rather than sitting as a narrow pill. */
  .sf-revpage .sf-revsolo .sf-cta-row .sf-btn { display: block; margin-inline: auto; }
}
@media (max-width: 575px) {
  .sf-revpage .sf-vcar { grid-template-columns: 1fr; gap: 12px; }
  .sf-revpage .sf-vcar-arw { display: none; }
  .sf-revpage .sf-vid-by { flex-wrap: wrap; }
  .sf-revpage .sf-vid-by .sf-stars { margin-left: 50px; }
}
