/**
 * SyteWide P2 — utility patterns that don't justify their own Elementor templates,
 * plus specificity elevators that beat Blocksy's Customizer-emitted CSS.
 * Loaded site-wide via functions.php AFTER tokens.css.
 */

/* ─────────────────────────────────────────────────────────────────────────
   FLATTEN FLOOR (flattened 2026-05-26, validated to computed-style PARITY by
   scripts/audit/css-regression). Dead rules removed, hardcoded font-family
   decls routed through var(--font-sans/mono), duplicate blocks consolidated,
   non-load-bearing !important dropped.

   What REMAINS is the irreducible floor — DO NOT "clean" it without re-running
   the harness (a delta = a real regression):
   • The ~1,160 surviving !important and the .elementor-page / body.page-id-N
     specificity chains (M-024) beat Elementor's compiled per-element CSS, which
     loads AFTER this file. Each is parity-load-bearing; the harness restores any
     that aren't.
   • The end-of-file override blocks win by SOURCE ORDER (M-021) against the
     locked-rule cascade — keep them at EOF.
   • A few font-family decls are kept as literals (search "FLATTEN-FLOOR") because
     the element renders a glyph (→, chevrons) absent from Inter/JetBrains Mono,
     so the fallback tail is load-bearing.

   To extend safely: `cd scripts/audit/css-regression && node check.mjs
   --candidate ../../../themes/blocksy-child/assets/css/sytewide-overrides.css`
   must report `PASS — total deltas: 0` before committing.
   ───────────────────────────────────────────────────────────────────────── */

/* Specificity elevator: body — wins over Elementor's per-post rule
   body.elementor-page-NNN { background-color: var(--e-global-color-...); }
   at specificity (0,0,1,1). See ExecPlan §9.2 for cascade analysis. */
body.wp-theme-blocksy.wp-child-theme-blocksy-child {
  background: var(--bg);
  color: var(--ink);
  font-family: var(--font-sans);
}

/* Specificity elevator: headings — wins over Blocksy's Customizer-emitted
   heading font-family (Poppins in P1). See ExecPlan §9.5. */
body.wp-theme-blocksy.wp-child-theme-blocksy-child h1,
body.wp-theme-blocksy.wp-child-theme-blocksy-child h2,
body.wp-theme-blocksy.wp-child-theme-blocksy-child h3,
body.wp-theme-blocksy.wp-child-theme-blocksy-child h4,
body.wp-theme-blocksy.wp-child-theme-blocksy-child h5,
body.wp-theme-blocksy.wp-child-theme-blocksy-child h6 {
  font-family: var(--font-sans);
}

/* ── Geist enforcement on Elementor + plugin UI (2026-05-27) ────────────────
   These surfaces carry a per-widget or plugin-default font predating the
   2026-05-26 Geist rollout:
   • Elementor nav items / buttons / icon-lists hard-code font-family:"Inter"
     in _elementor_data, compiled into /uploads/elementor/css/post-*.css at
     ~(0,4-5,0) and loaded AFTER this file — so !important is required (the same
     pattern as this file's other ~1,160 Elementor-beating !important decls).
   • FluentCart (.fct-*), FluentForm (.fluentform/.ff-*) and WPForms
     (.wpforms-*) ship their own -apple-system / Arial UI stack.
   Geist is the locked sitewide UI font (hard rule #9); Inter stays fallback-
   only. Scope is limited to elements that are NEVER mono. Section-label HEADINGS
   are deliberately excluded: the footer-legal line is JetBrains Mono via
   compiled CSS at the SAME specificity as the Inter section labels, so a blanket
   heading rule would flip the copyright too — headings need a per-widget pass
   (see ROADMAP "Geist on Elementor heading widgets"). The .sw-mini-process
   literal Inter (load-bearing glyph width) is preserved. */
.elementor-button,
.elementor-widget-nav-menu .elementor-item,
.elementor-widget-icon-list .elementor-icon-list-text,
.elementor-widget-icon-list .elementor-icon-list-item > a,
[class*="fct-"]:not([class*="icon"]),
.fluentform input, .fluentform textarea, .fluentform select,
.fluentform label, .fluentform .ff-btn, .fluentform button,
.wpforms-container input, .wpforms-container textarea, .wpforms-container select,
.wpforms-container label, .wpforms-field-label, .wpforms-container button,
.ct-breadcrumbs, .ct-breadcrumbs a, .ct-breadcrumbs span {
  font-family: var(--font-sans) !important;
}

/* dashboard-screenshot-frame — framed product UI shots */
.sw-ds-frame {
  border: 1px solid var(--rule);
  border-radius: var(--radius-sm);
  overflow: hidden;
  background: var(--bg);
  box-shadow: 0 2px 16px rgb(11 27 43 / 6%);
}
.sw-dark-twin .sw-ds-frame {
  border-color: var(--rule);
  box-shadow: 0 2px 24px rgb(94 168 227 / 10%);
}
.sw-ds-frame__chrome {
  height: 32px;
  background: var(--bg-soft);
  border-bottom: 1px solid var(--rule);
  display: flex;
  align-items: center;
  padding: 0 14px;
  gap: 6px;
}
/* Chrome dots. Two pseudo-elements cover a bare `<div class="sw-ds-frame__chrome">`;
   a third `> i` element is an optional third dot for templates that need inline
   markup (e.g. Elementor HTML widgets). Template authors MUST add
   `aria-hidden="true"` to the `<i>` — it is purely decorative. */
.sw-ds-frame__chrome::before,
.sw-ds-frame__chrome::after {
  content: "";
}
.sw-ds-frame__chrome::before,
.sw-ds-frame__chrome::after,
.sw-ds-frame__chrome > i {
  width: 10px;
  height: 10px;
  border-radius: 999px;
  background: var(--rule);
  display: inline-block;
}

/* product-code-strip — mono density signal under dark-twin hero */
.sw-code-strip {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-3) var(--space-5);
  background: #132338;
  border-top: 1px solid rgb(245 247 250 / 12%);
  font-family: var(--font-mono);
  font-size: var(--size-meta);
  color: #8B9AAE;
}
.sw-code-strip__glyph { color: #8FC9F7; font-weight: 600; }
.sw-code-strip__sep   { opacity: 0.4; }

/* badge-chip — 999px pill, mono 10-11px, scoped to --blue + --blue-tint */
.sw-chip {
  display: inline-block;
  padding: 4px 10px;
  font-family: var(--font-mono);
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  border-radius: var(--radius-pill);
  background: var(--blue-tint);
  color: var(--blue);
  vertical-align: middle;
}
.sw-chip--solid { background: var(--blue); color: var(--bg); }

/* Dark-twin chip override: --blue-tint isn't remapped in .sw-dark-twin
   (plan gap logged in §9.3), so chips inside .sw-dark-twin would render
   light-sky on dark. Component-level override uses tinted accent. */
.sw-dark-twin .sw-chip {
  background: rgb(94 168 227 / 15%);
  color: var(--blue-bright);
}
/* Dark-twin SOLID chip must beat the tinted dark-twin rule above.
   Specificity (0,3,0) wins over the (0,2,0) base dark-twin rule and
   preserves the --solid modifier's "filled pill" intent on sub-brand surfaces. */
.sw-dark-twin .sw-chip.sw-chip--solid {
  background: var(--blue);
  color: var(--bg);
}

/* video-hero — 16:9 reserved container, poster preload, no-JS fallback.
   Elementor's html widget wrapper collapses to 0px in flex-column section
   contexts even when the inner block child has aspect-ratio set. Fix:
   apply the aspect-ratio reservation to the WIDGET WRAPPER class directly
   (`.sw-video-section-embed` is set on the html widget at M2 close). The
   inner `.sw-video-hero` then absolute-positions inside the reserved box.
   P3 M2 close fix (operator-flagged 2026-04-25 ~23:14 EDT). */
.sw-video-section-embed {
  position: relative;
  width: 100%;
  height: 0;
  padding-bottom: 56.25%;
  aspect-ratio: 16 / 9;
}
.sw-video-section-embed .sw-video-hero {
  position: absolute;
  inset: 0;
  padding-bottom: 0;
  height: 100%;
  background: var(--ink);
  border-radius: var(--radius-sm);
  overflow: hidden;
}
/* Hero / case-card / other sw-video-hero usages (NOT the walkthrough
   section variant). Keep the inline aspect-ratio for these contexts. */
.sw-video-hero {
  position: relative;
  width: 100%;
  aspect-ratio: 16 / 9;
  /* iter-18i (2026-05-20): var(--ink) → var(--ink-fixed). The default
     --ink flips to paper #F5F7FA inside body.sw-dark-twin, which would
     render this video well as a paper-white rectangle. --ink-fixed
     never flips — always the brand dark navy. Today this rule isn't
     rendered inside dark-twin scope anywhere we know of, but the fix
     is preventative. */
  background: var(--ink-fixed);
  border-radius: var(--radius-sm);
  overflow: hidden;
}
.sw-video-hero__poster {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.sw-video-hero__play {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgb(11 27 43 / 20%);
  border: 0;
  cursor: pointer;
  transition: background 150ms ease;
}
.sw-video-hero__play:hover { background: rgb(11 27 43 / 32%); }
.sw-video-hero__play:focus-visible { outline: 3px solid var(--blue); outline-offset: -3px; }
.sw-video-hero__play-glyph {
  width: 72px;
  height: 72px;
  border-radius: 999px;
  background: rgb(255 255 255 / 94%);
  color: var(--blue);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 26px;
}
.sw-video-hero iframe {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  border: 0;
}
.sw-video-hero__fallback-link { display: none; }
@media (prefers-reduced-motion: reduce) {
  .sw-video-hero__play { display: none; }
  .sw-video-hero__fallback-link {
    display: inline-flex;
    align-items: center;
    position: absolute;
    bottom: var(--space-3);
    left: var(--space-3);
    padding: 8px 14px;
    background: rgb(255 255 255 / 94%);
    color: var(--blue);
    font-family: var(--font-sans);
    font-size: var(--size-meta);
    font-weight: 600;
    text-decoration: none;
    border-radius: var(--radius-sm);
    gap: 8px;
  }
}

/* ==========================================================================
   P3 M2 — / (home) layout iteration additions (2026-04-25)
   See .agent/specs/2026-04-25-p3-home-design.md Patterns 2 + 4.
   ========================================================================== */

/* Pattern 2 — Hero inline mono stat row (replaces standalone stat-band on / (home)).
   12px literal is intentional: smaller than --size-meta (13px) per design spec. */
.sw-hero-stat-row {
  font-family: var(--font-mono);
  font-size: 12px;
  font-weight: 500;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ink);
  margin: 0;
  text-align: center;
}
.sw-hero-stat-row strong {
  color: var(--ink);
  font-weight: 600;
}
.sw-hero-stat-row span[aria-hidden="true"] {
  color: rgb(11 27 43 / 70%);
  font-weight: 500;
}
/* Wave 3.A: dark-canvas remap so separator dot stays legible on dark twin */
.sw-dark-twin .sw-hero-stat-row span[aria-hidden="true"] {
  color: rgb(245 247 250 / 60%);
}
/* HTML widget wrapper alignment: Elementor's align=center setting on HTML
   widgets does NOT propagate to .elementor-widget-container the way it does
   on text-editor widgets, so HTML stat-rows render the inline-flex <p> at
   `start`. Force center alignment when the widget contains a stat-row. */
.elementor-widget-html:has(> .elementor-widget-container > .sw-hero-stat-row),
.elementor-widget-html:has(.sw-hero-stat-row) {
  text-align: center;
}
.elementor-widget-html:has(.sw-hero-stat-row) .elementor-widget-container {
  text-align: center;
}
/* Home hero lede — force center alignment across all viewports. The widget
   has align=center in Elementor settings, but with align-items:center on the
   parent flex column, the widget shrinks to content width and text-align
   center within a max-content widget renders as left-flush visually when
   line wraps differ. Pin a comfortable max-width + center via margin-inline. */
body.page-id-700 .elementor-700 [data-sw-section="hero"] .elementor-widget-text-editor p {
  text-align: center !important;
  max-width: 56ch !important;
  margin-inline: auto !important;
}
@media (max-width: 1024px) {
  .sw-hero-stat-row { font-size: 11px; }
}
@media (max-width: 640px) {
  /* 2026-05-21 — stat-row stacks cleanly on mobile. The inline `<strong>`
     segments separated by `·` bullets wrap awkwardly at <640px when one
     segment overflows the line (the `·` lands orphaned on its own line
     between two text runs). Flex-column turns each segment into a clean
     line and hides the now-unnecessary separator spans. */
  .sw-hero-stat-row {
    font-size: 11px;
    letter-spacing: 0.14em;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 4px;
  }
  .sw-hero-stat-row span[aria-hidden="true"] {
    display: none;
  }
}

/* Pattern 4 — CTA band mono micro-eyebrow (single-bg variant on / (home)).
   12px literal is intentional: smaller than --size-meta (13px) per design spec. */
.sw-cta-microeyebrow {
  font-family: var(--font-mono);
  font-size: 12px;
  font-weight: 500;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--blue);
  margin: 0;
}
@media (max-width: 640px) {
  .sw-cta-microeyebrow { font-size: 11px; }
}

/* P3 M8 + M13 page-header band — vertical centering + P2 token alignment + mobile center fix.
   Targets the first Elementor container on each of the 11 chrome-verify surfaces (6 legals
   + 5 FluentCart commerce). Each page's first container is the "dark band" with breadcrumb
   + page title; per-page authoring set asymmetric padding (top:200, bottom:60 on desktop;
   top:0150, bottom:60, left:35, right:35 on mobile — note typo "0150"), bound the bg to
   blocksy_palette_4, and left the heading widget at default left-align. Result: heading
   sits in lower half of band (not centered), mobile heading off-center to the right, color
   is Blocksy slot not P2 token. CSS-only fix below — no MCP writes (chrome-verify scope).
   See ExecPlan §8.8 + overnight-2026-04-25-summary.md M8/M13 sections. */
body.page-id-698 .elementor > .elementor-element.e-flex.e-con-boxed:has(.ct-breadcrumbs),  /* /about-us/ legacy */
  body.page-id-121522 .elementor > .elementor-element.e-flex.e-con-boxed:has(.ct-breadcrumbs),  /* Privacy Policy */
body.page-id-121540 .elementor > .elementor-element.e-flex.e-con-boxed:has(.ct-breadcrumbs),  /* Terms and Conditions */
body.page-id-121546 .elementor > .elementor-element.e-flex.e-con-boxed:has(.ct-breadcrumbs),  /* Acceptable Use Policy */
body.page-id-121554 .elementor > .elementor-element.e-flex.e-con-boxed:has(.ct-breadcrumbs),  /* Refund Policy */
body.page-id-121561 .elementor > .elementor-element.e-flex.e-con-boxed:has(.ct-breadcrumbs),  /* Cookie Policy */
body.page-id-121710 .elementor > .elementor-element.e-flex.e-con-boxed:has(.ct-breadcrumbs),  /* EULA */
body.page-id-118231 .elementor > .elementor-element.e-flex.e-con-boxed:has(.ct-breadcrumbs),  /* Shop */
body.page-id-118232 .elementor > .elementor-element.e-flex.e-con-boxed:has(.ct-breadcrumbs),  /* Account */
body.page-id-118233 .elementor > .elementor-element.e-flex.e-con-boxed:has(.ct-breadcrumbs),  /* Cart */
body.page-id-118234 .elementor > .elementor-element.e-flex.e-con-boxed:has(.ct-breadcrumbs),  /* Receipt */
body.page-id-118235 .elementor > .elementor-element.e-flex.e-con-boxed:has(.ct-breadcrumbs)   /* Checkout */ {
  background-color: var(--ink);       /* P2 token: --ink #0B1B2B (was Blocksy palette_4) */
  min-height: 280px;
  padding: 80px 24px;                 /* symmetric vertical, was 200/0/60/0 */
  display: flex;
  flex-direction: column;
  justify-content: center;            /* vertical center */
  align-items: center;                /* horizontal center for ALL children */
  text-align: center;                 /* breadcrumb + title text center */
  gap: 12px;                                      /* breadcrumb-to-title spacing */
}
/* Children: force their own alignment + center the breadcrumb's flexed inline parts */
body.page-id-698 .elementor > .elementor-element.e-flex.e-con-boxed:has(.ct-breadcrumbs),  /* /about-us/ legacy */
  body.page-id-121522 .elementor > .elementor-element.e-flex.e-con-boxed:has(.ct-breadcrumbs) > .e-con-inner,
body.page-id-121540 .elementor > .elementor-element.e-flex.e-con-boxed:has(.ct-breadcrumbs) > .e-con-inner,
body.page-id-121546 .elementor > .elementor-element.e-flex.e-con-boxed:has(.ct-breadcrumbs) > .e-con-inner,
body.page-id-121554 .elementor > .elementor-element.e-flex.e-con-boxed:has(.ct-breadcrumbs) > .e-con-inner,
body.page-id-121561 .elementor > .elementor-element.e-flex.e-con-boxed:has(.ct-breadcrumbs) > .e-con-inner,
body.page-id-121710 .elementor > .elementor-element.e-flex.e-con-boxed:has(.ct-breadcrumbs) > .e-con-inner,
body.page-id-118231 .elementor > .elementor-element.e-flex.e-con-boxed:has(.ct-breadcrumbs) > .e-con-inner,
body.page-id-118232 .elementor > .elementor-element.e-flex.e-con-boxed:has(.ct-breadcrumbs) > .e-con-inner,
body.page-id-118233 .elementor > .elementor-element.e-flex.e-con-boxed:has(.ct-breadcrumbs) > .e-con-inner,
body.page-id-118234 .elementor > .elementor-element.e-flex.e-con-boxed:has(.ct-breadcrumbs) > .e-con-inner,
body.page-id-118235 .elementor > .elementor-element.e-flex.e-con-boxed:has(.ct-breadcrumbs) > .e-con-inner {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  width: 100%;
  text-align: center;
  padding: 0 !important;          /* override per-page Elementor padding 200px 0 60px on inner */
}
body.page-id-698 .elementor > .elementor-element.e-flex.e-con-boxed:has(.ct-breadcrumbs),  /* /about-us/ legacy */
  body.page-id-121522 .elementor > .elementor-element.e-flex.e-con-boxed:has(.ct-breadcrumbs) .ct-breadcrumbs,
body.page-id-121540 .elementor > .elementor-element.e-flex.e-con-boxed:has(.ct-breadcrumbs) .ct-breadcrumbs,
body.page-id-121546 .elementor > .elementor-element.e-flex.e-con-boxed:has(.ct-breadcrumbs) .ct-breadcrumbs,
body.page-id-121554 .elementor > .elementor-element.e-flex.e-con-boxed:has(.ct-breadcrumbs) .ct-breadcrumbs,
body.page-id-121561 .elementor > .elementor-element.e-flex.e-con-boxed:has(.ct-breadcrumbs) .ct-breadcrumbs,
body.page-id-121710 .elementor > .elementor-element.e-flex.e-con-boxed:has(.ct-breadcrumbs) .ct-breadcrumbs,
body.page-id-118231 .elementor > .elementor-element.e-flex.e-con-boxed:has(.ct-breadcrumbs) .ct-breadcrumbs,
body.page-id-118232 .elementor > .elementor-element.e-flex.e-con-boxed:has(.ct-breadcrumbs) .ct-breadcrumbs,
body.page-id-118233 .elementor > .elementor-element.e-flex.e-con-boxed:has(.ct-breadcrumbs) .ct-breadcrumbs,
body.page-id-118234 .elementor > .elementor-element.e-flex.e-con-boxed:has(.ct-breadcrumbs) .ct-breadcrumbs,
body.page-id-118235 .elementor > .elementor-element.e-flex.e-con-boxed:has(.ct-breadcrumbs) .ct-breadcrumbs {
  display: inline-flex;
  flex-wrap: wrap;
  justify-content: center;
  align-items: center;
  gap: 6px;
}
body.page-id-698 .elementor > .elementor-element.e-flex.e-con-boxed:has(.ct-breadcrumbs),  /* /about-us/ legacy */
  body.page-id-121522 .elementor > .elementor-element.e-flex.e-con-boxed:has(.ct-breadcrumbs) .elementor-widget-heading,
body.page-id-121540 .elementor > .elementor-element.e-flex.e-con-boxed:has(.ct-breadcrumbs) .elementor-widget-heading,
body.page-id-121546 .elementor > .elementor-element.e-flex.e-con-boxed:has(.ct-breadcrumbs) .elementor-widget-heading,
body.page-id-121554 .elementor > .elementor-element.e-flex.e-con-boxed:has(.ct-breadcrumbs) .elementor-widget-heading,
body.page-id-121561 .elementor > .elementor-element.e-flex.e-con-boxed:has(.ct-breadcrumbs) .elementor-widget-heading,
body.page-id-121710 .elementor > .elementor-element.e-flex.e-con-boxed:has(.ct-breadcrumbs) .elementor-widget-heading,
body.page-id-118231 .elementor > .elementor-element.e-flex.e-con-boxed:has(.ct-breadcrumbs) .elementor-widget-heading,
body.page-id-118232 .elementor > .elementor-element.e-flex.e-con-boxed:has(.ct-breadcrumbs) .elementor-widget-heading,
body.page-id-118233 .elementor > .elementor-element.e-flex.e-con-boxed:has(.ct-breadcrumbs) .elementor-widget-heading,
body.page-id-118234 .elementor > .elementor-element.e-flex.e-con-boxed:has(.ct-breadcrumbs) .elementor-widget-heading,
body.page-id-118235 .elementor > .elementor-element.e-flex.e-con-boxed:has(.ct-breadcrumbs) .elementor-widget-heading {
  width: 100%;
  text-align: center;
}
body.page-id-698 .elementor > .elementor-element.e-flex.e-con-boxed:has(.ct-breadcrumbs),  /* /about-us/ legacy */
  body.page-id-121522 .elementor > .elementor-element.e-flex.e-con-boxed:has(.ct-breadcrumbs) .elementor-widget-heading .elementor-heading-title,
body.page-id-121540 .elementor > .elementor-element.e-flex.e-con-boxed:has(.ct-breadcrumbs) .elementor-widget-heading .elementor-heading-title,
body.page-id-121546 .elementor > .elementor-element.e-flex.e-con-boxed:has(.ct-breadcrumbs) .elementor-widget-heading .elementor-heading-title,
body.page-id-121554 .elementor > .elementor-element.e-flex.e-con-boxed:has(.ct-breadcrumbs) .elementor-widget-heading .elementor-heading-title,
body.page-id-121561 .elementor > .elementor-element.e-flex.e-con-boxed:has(.ct-breadcrumbs) .elementor-widget-heading .elementor-heading-title,
body.page-id-121710 .elementor > .elementor-element.e-flex.e-con-boxed:has(.ct-breadcrumbs) .elementor-widget-heading .elementor-heading-title,
body.page-id-118231 .elementor > .elementor-element.e-flex.e-con-boxed:has(.ct-breadcrumbs) .elementor-widget-heading .elementor-heading-title,
body.page-id-118232 .elementor > .elementor-element.e-flex.e-con-boxed:has(.ct-breadcrumbs) .elementor-widget-heading .elementor-heading-title,
body.page-id-118233 .elementor > .elementor-element.e-flex.e-con-boxed:has(.ct-breadcrumbs) .elementor-widget-heading .elementor-heading-title,
body.page-id-118234 .elementor > .elementor-element.e-flex.e-con-boxed:has(.ct-breadcrumbs) .elementor-widget-heading .elementor-heading-title,
body.page-id-118235 .elementor > .elementor-element.e-flex.e-con-boxed:has(.ct-breadcrumbs) .elementor-widget-heading .elementor-heading-title {
  text-align: center;
  color: var(--bg);              /* white-ish on --ink dark band */
  font-family: var(--font-sans);
  font-weight: 600;
}
@media (min-width: 1024px) {
  body.page-id-698 .elementor > .elementor-element.e-flex.e-con-boxed:has(.ct-breadcrumbs),  /* /about-us/ legacy */
  body.page-id-121522 .elementor > .elementor-element.e-flex.e-con-boxed:has(.ct-breadcrumbs),
  body.page-id-121540 .elementor > .elementor-element.e-flex.e-con-boxed:has(.ct-breadcrumbs),
  body.page-id-121546 .elementor > .elementor-element.e-flex.e-con-boxed:has(.ct-breadcrumbs),
  body.page-id-121554 .elementor > .elementor-element.e-flex.e-con-boxed:has(.ct-breadcrumbs),
  body.page-id-121561 .elementor > .elementor-element.e-flex.e-con-boxed:has(.ct-breadcrumbs),
  body.page-id-121710 .elementor > .elementor-element.e-flex.e-con-boxed:has(.ct-breadcrumbs),
  body.page-id-118231 .elementor > .elementor-element.e-flex.e-con-boxed:has(.ct-breadcrumbs),
  body.page-id-118232 .elementor > .elementor-element.e-flex.e-con-boxed:has(.ct-breadcrumbs),
  body.page-id-118233 .elementor > .elementor-element.e-flex.e-con-boxed:has(.ct-breadcrumbs),
  body.page-id-118234 .elementor > .elementor-element.e-flex.e-con-boxed:has(.ct-breadcrumbs),
  body.page-id-118235 .elementor > .elementor-element.e-flex.e-con-boxed:has(.ct-breadcrumbs) {
    min-height: 320px;
    padding: 100px 60px;
  }
}

/* ==========================================================================
   P3 iter-3a — design uplift foundations (2026-04-26)
   See .agent/specs/2026-04-26-p3-iter3-design.md.
   ========================================================================== */

/* iter-3a Bug B — 3-up cards stacking fix.
   Symptom: methodology row on /about-us/ + audit-pillars row + products row
   on /services-hub/ render their 3 child cards as full-width vertical stack
   at desktop instead of 3-column. Diagnosis: each card has saved width=33.33%,
   but Elementor's compiled per-page CSS doesn't emit a `width: 33.33%` rule —
   only `--container-widget-width: calc((1 - flex-grow) * 100%)` which evaluates
   to 100% per child. Setting `_flex_size: "grow"` (flex: 1 0 auto) or "custom"
   (flex: 1 1 with width as basis) didn't move the cards from 1270px width
   because flex-basis: auto inherits the explicit CSS width. Fix: explicit
   `flex: 1 1 calc(...)` + `max-width: calc(...)` on each child via row-id
   selector — the same pattern the M2 / (home) pillar row uses (proven). */
#sw-about-methodology-row > .e-con,
#sw-services-audit-row > .e-con,
#sw-services-products-row > .e-con {
  flex: 1 1 calc((100% - 48px) / 3);
  max-width: calc((100% - 48px) / 3);
}
@media (max-width: 1024px) {
  #sw-about-methodology-row > .e-con,
  #sw-services-audit-row > .e-con,
  #sw-services-products-row > .e-con {
    flex: 1 1 calc((100% - 24px) / 2);
    max-width: calc((100% - 24px) / 2);
  }
}
@media (max-width: 640px) {
  #sw-about-methodology-row > .e-con,
  #sw-services-audit-row > .e-con,
  #sw-services-products-row > .e-con {
    flex: 1 1 100%;
    max-width: 100%;
  }
}

/* iter-3 follow-up L-2 (2026-04-27): blog archive CLS optimization.
   The Elementor posts widget emits `<img width="768" height="432">` with
   correct attributes, but the EWWW lazy-load JS swap from a 1x1 placeholder
   data: URI to the real ExactDN URL races the 2.5s browser-measured CLS
   window under parallel-suite load — visible as 0.05-0.25 CLS spikes on
   /blog/ archive (was relaxed to 0.25 gate at iter-2 close).
   Defensive aspect-ratio + object-fit CSS enforces layout reservation
   regardless of img element attribute timing. The 16:9 ratio (768x432 =
   1.778:1) matches the medium_large image size used by the posts widget.
   With this rule, the gate can tighten back to the Web Vitals "Good"
   threshold of 0.1 (Playwright assertion updated in
   tests/pages/blog.spec.ts at the same time). */
.elementor-widget-posts .elementor-post__thumbnail__link,
.elementor-widget-posts .elementor-post__thumbnail {
  aspect-ratio: 16 / 9;
  display: block;
  overflow: hidden;
}
.elementor-widget-posts .elementor-post__thumbnail img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}
/* `contain: layout` isolates the widget's internal layout reflows so
   they don't cascade to elements below (footer, etc.) during the 2.5s
   CLS measurement window. Without this, a <0.005 internal shift
   amplifies via downstream-element repositioning. */
.elementor-widget-posts {
  contain: layout style;
}
/* iter-3 follow-up M-1 (2026-04-27): the home page (post 700; was skeleton post 122855, since deleted) was queue-for-
   operator at iter-3a kickoff because the title "v2 home (P1 skeleton)" is
   visually unobtrusive next to the dominant Elementor hero. Operator pass v3
   approved the cleanup pass — extending Bug A suppression to the home page
   too so all P3 marketing surfaces have the same Blocksy hero-section gone. */

/* iter-3a S6 editorial card system — design spec §2.4.
   Applies to every container with `data-sw-card` attribute (added via Elementor
   `_attributes` setting). 6px hard offset shadow + 1.5px solid border + 0
   border-radius + zero hover transform = "editorial register" — most still,
   most architectural look. Replaces the iter-2 hairline-rounded card styling
   (8px radius + 1px border + soft shadow) on /about-us/ methodology cards
   + /services-hub/ audit-pillar + product cards. Phase 1.5 adds
   `data-sw-card="pillar"` to / (home) pillar containers so this rule applies
   site-wide. !important is needed to beat Elementor's per-page CSS which loads
   after sytewide-overrides.css. */
[data-sw-card] {
  background: var(--bg) !important;
  border: 1.5px solid var(--ink) !important;
  border-radius: 0 !important;
  /* 2026-05-21 — shadow uses --blue-bright (the sitewide hover/accent cyan)
     at 32% opacity. Replaces the prior paper-white hard offset which
     printed as a stark gray rectangle on dark canvases — off-brand vs
     the cyan-accent treatment used everywhere else. Hover-darker cyan
     keeps the offset readable on light canvases too. */
  box-shadow: 6px 6px 0 rgb(143 201 247 / 32%);
  transition: none;
}
[data-sw-card]:hover {
  transform: none !important;
}
.sw-chapter-dark [data-sw-card],
[data-sw-card][data-sw-mode="dark"] {
  background: #102338 !important;
  color: var(--bg);
  border-color: rgb(255 255 255 / 20%) !important;
  box-shadow: 6px 6px 0 rgb(143 201 247 / 28%);
}

/* iter-3a chapter eyebrow — design spec §2.5.
   Mono micro-eyebrow above each section h2 in the format `01 / SECTION NAME`.
   Smaller than the existing .sw-cta-microeyebrow (which lives on CTA bands) —
   chapter eyebrows have tighter letter-spacing and ink color rather than blue,
   per the editorial register. Two scopes: the class can sit either on the
   text element directly (e.g. a span/p) OR on an Elementor heading widget's
   wrapper div (.sw-chapter-ebrow .elementor-heading-title for the inner
   h-tag — Elementor's heading widget gives the inner title its own font
   stack, so we override there explicitly). */
.sw-chapter-ebrow,
.sw-chapter-ebrow .elementor-heading-title {
  font-family: var(--font-mono) !important;
  font-size: 11px !important;
  font-weight: 500 !important;
  letter-spacing: 0.18em !important;
  text-transform: uppercase !important;
  color: var(--ink) !important;
  margin: 0 0 16px 0;
  display: block;
}
.sw-chapter-ebrow strong,
.sw-chapter-ebrow .elementor-heading-title strong {
  color: var(--ink) !important;
  font-weight: 500 !important;
}
.sw-chapter-dark .sw-chapter-ebrow,
.sw-chapter-dark .sw-chapter-ebrow .elementor-heading-title {
  color: var(--blue-bright) !important;
}

/* iter-3c follow-up (2026-04-27): widen case-study section bands.
   Operator review of /case-studies/placeholder-case-study/ flagged the
   editorial template as "too narrow" — the default `.sw-body-inner` rule
   (max-width 720px from iter-3a §2.6) constrains the entire visual band
   and makes the case-study feel cramped relative to /about-us/ and
   /services-hub/ which have ~1290px section content widths. Compromise:
   widen the band container to 1100px so the 3-up outcome cards + hero
   chips + CTA close visually anchor at editorial scale, while keeping
   article-body prose at the 720px reading column for legibility (the
   drop cap + pull-quote rules stay scoped to that reading column). */
/* iter-3c follow-up v2 (2026-04-27) — operator review pass: case-study
   should match the same 1290px editorial canvas as the other P3 surfaces.
   The earlier 1100/720 split was reverted — body content (article-body)
   now spans the full band width like /about-us/ and /services-hub/.
   The drop cap + pull-quote rules from iter-3a remain scoped to
   [data-sw-role="article-body"] but operate on the wider column. Hero +
   CTA title constraints stay (those are aesthetic typography centering,
   not a reading-column constraint). */
.sw-case-study .sw-body-inner {
  max-width: var(--width-canvas) !important;
}
/* Updated 2026-05-27 (post-#136 cleanup): SOLE width declaration for these
   BEM classes (the line-1106 + line-1255 base rules intentionally OMIT
   max-width — see comments there). Width = wide tier (880px). These classes
   only render inside single-sw_case_study.php's <article class="sw-case-study">,
   so this scoped rule is the only width source actually in play. See
   widths.md (wide tier). */
.sw-case-study .sw-case-hero__title,
.sw-case-study .sw-case-cta__title {
  max-width: var(--width-wide);
}
/* Updated 2026-05-27: 640 → var(--width-narrow) (= 600). −40px on case-study
   pages. The .sw-case-study scoped 640 override deviated from the narrow tier
   without design rationale; folding to canonical narrow. See widths.md. */
.sw-case-study .sw-case-cta__lede {
  max-width: var(--width-narrow);
}

/* iter-3c follow-up — founder portrait vertical breathing room.
   Operator review of /about-us/ flagged the portrait as visually
   tight against the chapter eyebrow above and the narrative below.
   Adding 32px top + 40px bottom margin gives the image proper editorial
   spacing within the operator-origin section. The .sw-portrait-frame
   block-level rule (`max-width:280px;margin:24px auto 32px;` from
   iter-3a §3.2) only handles centering — Elementor's image widget
   wrapper has its own padding that competes; bumping with !important
   wins reliably across both contexts (case-study client logo + about-us
   founder portrait). */
.sw-portrait-frame {
  margin-top: 32px !important;
  margin-bottom: 40px !important;
}
.elementor-widget-image.sw-portrait-frame .elementor-widget-container {
  padding: 0 !important;
}

/* iter-3 follow-up (2026-04-27) — site-wide editorial typography baseline (LOCKED).
   Operator review of /about-us/ flagged body text as "too small" relative
   to the case-study template (which uses 18px/1.65 prose). The case-study
   typography is the operator-approved reference; this rule block lifts those
   values to the 5 P3 surfaces (home 122855, about-us 123218, services-hub
   123253, case-study CPT, blog single posts) so they share the same editorial
   sizing without per-Elementor-widget re-authoring.

   ##### LOCK STATUS — DO NOT CHANGE WITHOUT OPERATOR APPROVAL #####
   Canonical reference: docs/developer/design-system/typography.md §
   "P3 marketing typography baseline (LOCKED 2026-04-27)" + sibling
   spacing.md § "P3 marketing section padding (LOCKED 2026-04-27)" +
   CLAUDE.md § "P3 marketing surfaces — locked design standards".

   Authoring discipline: do NOT author per-widget typography overrides
   for body p, h2, or h3 on these surfaces — let the rule apply. Hero h1
   + CTA h2 may set typography explicitly to override (e.g. M-class pages
   with tighter hero) but commit a comment explaining why.

   `!important` is required because Elementor's per-page CSS sets per-widget
   font-size + line-height at the same (0,1,0) specificity and loads after
   sytewide-overrides.css (priority 20+ vs sw-overrides at 5) — same cascade
   pattern as iter-3a `.sw-chapter-dark` (ExecPlan §8.10). */
body.page-id-700 .elementor-widget-text-editor p,
body.page-id-698 .elementor-widget-text-editor p,
body.page-id-123633 .elementor-widget-text-editor p,
body.page-id-695 .elementor-widget-text-editor p,
body.page-id-122387 .elementor-widget-text-editor p,
body.single-fluent-products.elementor-page .elementor-widget-text-editor p,
body.single-sw_case_study .elementor-widget-text-editor p,
body.single-post [data-sw-role="article-body"] p,
body.single-post [data-sw-role="article-body"] li,
body.page-id-700 .elementor-widget-text-editor li,
body.page-id-698 .elementor-widget-text-editor li,
body.page-id-123633 .elementor-widget-text-editor li,
body.page-id-695 .elementor-widget-text-editor li,
body.page-id-122387 .elementor-widget-text-editor li,
body.single-fluent-products.elementor-page .elementor-widget-text-editor li,
body.single-sw_case_study .elementor-widget-text-editor li {
  font-size: 18px !important;
  line-height: 1.65 !important;
}

body.page-id-700 .elementor-widget-heading h2.elementor-heading-title,
body.page-id-698 .elementor-widget-heading h2.elementor-heading-title,
body.page-id-123633 .elementor-widget-heading h2.elementor-heading-title,
body.page-id-695 .elementor-widget-heading h2.elementor-heading-title,
body.page-id-122387 .elementor-widget-heading h2.elementor-heading-title,
body.single-fluent-products.elementor-page .elementor-widget-heading h2.elementor-heading-title,
body.single-sw_case_study .elementor-widget-heading h2.elementor-heading-title,
body.single-post [data-sw-role="article-body"] h2 {
  font-size: clamp(28px, 3vw, 36px) !important;
  font-weight: 600 !important;
  line-height: 1.15 !important;
}

/* 2026-05-14 (operator request): blog single h2s were sitting flush
   against the preceding paragraph (margin-top: 0 from Blocksy default),
   reading cramped. Add a clamped top margin so each section heading
   gets visible breathing room before it. Clamps to 32px on mobile
   (~10% of 320 viewport) up to 48px on desktop. Scoped to blog single
   body so Elementor heading widgets elsewhere are unaffected. */
body.single-post [data-sw-role="article-body"] h2 {
  margin-top: clamp(32px, 3.5vw, 48px) !important;
}

body.page-id-700 .elementor-widget-heading h3.elementor-heading-title,
body.page-id-698 .elementor-widget-heading h3.elementor-heading-title,
body.page-id-123633 .elementor-widget-heading h3.elementor-heading-title,
body.page-id-695 .elementor-widget-heading h3.elementor-heading-title,
body.page-id-122387 .elementor-widget-heading h3.elementor-heading-title,
body.single-fluent-products.elementor-page .elementor-widget-heading h3.elementor-heading-title,
body.single-sw_case_study .elementor-widget-heading h3.elementor-heading-title,
body.single-post [data-sw-role="article-body"] h3 {
  font-size: 22px !important;
  font-weight: 600 !important;
  line-height: 1.2 !important;
}

/* 2026-06-02 fix — the /services-hub/ hero band (37f61fe) felt cramped: a 16px
   column gap stacked the eyebrow, h2, lede, CTA row, and stat rows too tightly.
   Open the vertical rhythm to 28px so the hero breathes. Page+id+inner
   specificity (0,3,1) wins over Elementor's element-id flex gap. */
body.page-id-123633 .elementor-element-37f61fe > .e-con-inner {
  row-gap: 28px;
}

/* Hero h1 + CTA h2 stay larger — explicit override to keep editorial weight.
   Heros use `data-sw-section="hero"` (sitewide). CTA bands use
   `data-sw-section="cta-band"` or `data-sw-template="cta-band"`.
   Note (intentional unscoping): unlike the typography baseline above, this
   selector has NO body-class prefix. The data-sw-section attribute is a
   P3 marketing convention — any future page adopting it should inherit the
   editorial-weight typography by design.
   2026-05-14 (hero-consolidation Phase 5): retired companion selector
   `[data-sw-template="hero-light"]` — was previously a synonym for hero
   attribution on home only; attribute removed in Phase 3, selector now
   redundant with `[data-sw-section="hero"]`. */
[data-sw-section="hero"] .elementor-widget-heading h1.elementor-heading-title {
  font-size: clamp(22px, 3.5vw, 40px) !important;
  font-weight: 600 !important;
  line-height: 1.05 !important;
  letter-spacing: -0.012em !important;
}
[data-sw-section="cta-band"] .elementor-widget-heading h2.elementor-heading-title,
[data-sw-template="cta-band"] .elementor-widget-heading h2.elementor-heading-title {
  font-size: clamp(24px, 4vw, 44px);
  font-weight: 600;
  line-height: 1.1;
  hyphens: manual;
  overflow-wrap: break-word;
}

/* iter-3 follow-up (2026-04-27) — standardized section padding (LOCKED).
   Operator: "they need to have some padding on the sides. Check out
   Placeholder Case study. I really like this, just add some padding on all
   views to the sides." Then v3.1: "Make that a lock everywhere and update
   all documentation to enforce it!"

   ##### LOCK STATUS — DO NOT CHANGE WITHOUT OPERATOR APPROVAL #####
   Canonical references:
   - docs/developer/design-system/spacing.md § "P3 marketing section padding
     (LOCKED 2026-04-27)" — full clamp table + viewport breakpoints.
   - docs/developer/design-system/typography.md § "P3 marketing typography
     baseline (LOCKED 2026-04-27)" — sibling typography lock.
   - CLAUDE.md § "P3 marketing surfaces — locked design standards" — top-
     level guidance + authoring discipline.

   Final values: clamp(64px, 8vw, 96px) vertical + clamp(20px, 7vw, 120px)
   horizontal across all 5 P3 surfaces. Hero exception below: clamp(96-160)
   vertical for editorial weight. `!important` is required to beat
   Elementor's per-page CSS at same (0,1,0) specificity (ExecPlan §8.10).

   2026-04-30 mobile-audit: horizontal clamp floor 48 → 20. At 375px the
   old 48 floor pinned content to 96px total side padding (~74% content
   width). The new 20px floor lets 7vw scale all the way down (375×0.07
   = 26.25px), giving ~88% content width on iPhone SE. Tablet 640px
   barely changes (45px vs 48px); desktop ≥1024 unchanged (7vw>120px is
   capped). Operator authorized end-to-end execution 2026-04-30. */

   Authoring discipline: do NOT author per-widget padding on widgets that
   live within these P3 surfaces. To extend the lock to a new page ID or
   post type, ADD a body-class selector to the rule block below — do not
   create competing per-element rules. */
/* F1.1 (2026-05-01) — collapsed 14-selector enumeration to 3 via the
   `body.sw-locked-surface` body_class shortcut wired in
   plugins/sytewide-site/includes/body-class.php. Same surfaces, same
   specificity (0,3,1) — slight bump from original (0,2,1) due to
   :not(.single-post) but cascade-equivalent (still loses T/B to
   Elementor per-element, M-021).

   The :not(.single-post) exclusion keeps blog posts on the
   [data-sw-block="blog-single"] selector ONLY — avoids regressing
   blog-post [data-sw-section="newsletter"] + [data-sw-section="end-cta"]
   sections which have intentional per-element L/R padding (28/24px). */
body.sw-locked-surface:not(.single-post) [data-sw-section],
body.sw-locked-surface:not(.single-post) [data-sw-template],
body.single-post [data-sw-block="blog-single"] {
  /* 2026-05-01 review-fix: vertical clamp bumped 64-96 → 80-128 per operator
     feedback ("still need more padding on all the mobile views"). Mobile
     floor 64→80 (+16); desktop ceiling 96→128 (+32). Hero exception below
     unchanged at clamp(96, 12vw, 144) — preserves hero > non-hero
     hierarchy. Mobile hero override at line 4642 (M0.2) still pins hero
     pt/pb to 48 — sytehero baseline (M-022) preserved. F0 inner block
     (line 4900) unchanged. */
  padding-top: clamp(80px, 10vw, 128px) !important;
  padding-bottom: clamp(80px, 10vw, 128px) !important;
  padding-left: var(--section-x) !important;
  padding-right: var(--section-x) !important;
}

/* Hero exception — heroes typically have larger padding for visual
   weight; respect their explicit per-section setting if it's bigger.
   v3 (2026-04-28): MUST match the base rule's body-class specificity
   (0,3,1) — a bare [data-sw-section="hero"] (0,1,0) loses to the
   body-scoped base rule and leaves the hero rendering at the soft-band
   64-96px padding instead of 96-160px. The visible bug ("video bleeds
   into next section on mobile") is the hero pb collapsing to 64px at
   375px because this rule never won. */
/* F1.2 (2026-05-01) — collapsed via body.sw-locked-surface. Same
   :not(.single-post) exclusion + blog [data-sw-block] selector as F1.1. */
body.sw-locked-surface:not(.single-post) [data-sw-section="hero"],
body.single-post [data-sw-block="blog-single"][data-sw-section="hero"] {
  padding-top: clamp(96px, 12vw, 144px) !important;
  padding-bottom: clamp(96px, 12vw, 144px) !important;
}

/* Phase D (2026-05-14): canonical hero inner padding. Locks every P3
   marketing surface's hero `.e-con-inner` to 10px / 10px vertical to
   match the 5 product detail pages (which set this baseline pre-Phase-D
   via Elementor inline settings). Total hero Y = 144 outer (F1.2 above)
   + 10 inner = 308. Replaces the home-only `padding: 0` rule (was
   producing 288 total) and the about-us/services-hub Wave 3.B/3.C 80/64
   outer overrides (were producing 384 total). Specificity (0,3,0) +
   !important beats Elementor per-element padding (0,1,0) reliably. */
body.sw-locked-surface:not(.single-post) [data-sw-section="hero"] > .e-con-inner,
body.single-post [data-sw-block="blog-single"][data-sw-section="hero"] > .e-con-inner {
  padding-top: 10px !important;
  padding-bottom: 10px !important;
  /* 2026-05-22 — vertically center hero content inside the inner
     wrapper. Without this, pages whose Elementor author didn't set
     `flex_justify_content: center` on the inner (e.g., /products/)
     top-align their content while pages that did (home, services-hub,
     about-us) center theirs. Sitewide CSS makes the rendering
     consistent regardless of per-page Elementor authoring. */
  justify-content: center !important;
}

/* mobile-audit 2026-04-30 — Bug A: WP block-image <img> renders at its
   HTML width attribute (e.g., width="600") inside a smaller figure
   container on mobile, creating visual overflow that body{overflow-x:
   hidden} masks but the image is still center-cropped at ~57% of itself.

   The HTML width attribute beats the existing CSS max-width:100% rule.
   !important is required to override an attribute, not just CSS rules.
   Scope is editorial body content only (blog posts + case studies); does
   NOT touch logo/icon images in chrome. */
body.single-post article.post .entry-content img,
body.single-post article.post .wp-block-image img,
body.single-post article.post figure.wp-block-image img,
body.single-sw_case_study article.sw-case-study img,
body.single-sw_case_study article.sw-case-study .wp-block-image img,
body.single-sw_case_study article.sw-case-study figure.wp-block-image img {
  max-width: 100% !important;
  height: auto !important;
  width: auto !important;
}

/* === iter-7 m3 (2026-04-29) — /about-us/ operator-story photo+text
   editorial layout (mockup-approved Variant B).
   Operator: "Variant B — looks great. Make sure after a point the text
   returns to full width."

   Pattern: prose runs primary, the cartoon "stressed-businessman" photo
   becomes a polaroid-style accent on the right (cream frame, 1px rust
   hairline, slight 2deg rotation, italic an italic display companion (retired) caption injected
   via ::after). Body prose wraps next to the photo for the first few
   paragraphs; once it clears the photo bottom, text returns to full
   width naturally via float behavior.

   Implementation:
   1. Override the band's e-flex column to display:block so floats work.
   2. Re-establish vertical rhythm with explicit margins on each widget.
   3. Float the image widget (7ab76f5) right with the polaroid frame
      treatment + ::after caption.
   4. Reset the principles widget to flow inline with the prose (drop
      the centered 720px max-width that the old iter-3 rule applied).
*/
body.page-id-698 #operator-story,
body.page-id-698 #operator-story > .e-con-inner {
  display: block !important;
}
body.page-id-698 #operator-story::after {
  content: "";
  display: block;
  clear: both;
}
body.page-id-698 #operator-story .elementor-element-f64fe40 {
  margin-bottom: 18px !important;
}
body.page-id-698 #operator-story .elementor-element-32d510e {
  margin-bottom: 48px !important;
}
body.page-id-698 #operator-story .elementor-element-f42b4e0,
body.page-id-698 #operator-story .elementor-element-a62d201,
body.page-id-698 #operator-story .elementor-element-ba9e10a {
  margin-bottom: 24px !important;
}

/* Photo as polaroid float-right.
   IMPORTANT: Image widget has NO .elementor-widget-container wrapper
   (Elementor renders <img> as direct child here). Polaroid styles
   apply to the widget directly. Also overrides the sw-portrait-frame
   iter-3a rule (max-width:280 + ink border + auto-center) which
   otherwise wins.
   iter-7 m3-fu3 (2026-04-29): rotation restored per operator pick on
   mockup. Earlier removal was based on misread — the dark strip
   around the polaroid was the methodology band's row container leaking
   through, NOT the rotation. (Updated 2026-05-10: operator-story is
   light again after iter-7 dark-band rule removed.) */
body.page-id-698 #operator-story .elementor-element-7ab76f5 {
  float: right !important;
  width: 320px !important;
  max-width: none !important;
  margin: 8px 0 24px 48px !important;
  shape-outside: margin-box;
  background: #FAFAF7 !important;
  padding: 16px 16px 20px 16px !important;
  border: 1px solid rgba(74, 157, 200, 0.75) !important;
  border-radius: 12px !important;
  box-shadow: 6px 6px 0 rgba(74, 157, 200, 0.22), 0 12px 32px rgba(0, 0, 0, 0.35) !important;
  box-sizing: border-box !important;
  overflow: visible !important;
  transform: rotate(2deg) !important;
}
body.page-id-698 #operator-story .elementor-element-7ab76f5 > img {
  display: block !important;
  width: 100% !important;
  height: auto !important;
  margin: 0 !important;
}
body.page-id-698 #operator-story .elementor-element-7ab76f5::after {
  /* 2026-05-10: was "One operator. Twenty years." — retired per
     CLAUDE.md banned words list + project-sytewide-team-structure
     memory (don't write "one operator" or imply solo). Replaced
     with the locked sitewide stat-row fragment. */
  content: "20+ yrs in service-ops.";
  display: block;
  text-align: center;
  margin-top: 14px;
  font-size: 15px;
  font-weight: 500;
  color: #0A1626;
  line-height: 1.4;
}

/* Principles block: drop the iter-3 centered 720px max-width — let it
   flow inline with the prose so the wrap-around works. */
body.page-id-698 #operator-story .elementor-widget-text-editor:has(> .sw-principles-block) {
  align-self: stretch !important;
  width: auto !important;
  max-width: none !important;
}
body.page-id-698 #operator-story .sw-principles-block {
  max-width: none !important;
  margin-left: 0 !important;
  margin-right: 0 !important;
}

/* Mobile fallback: photo stacks above text, no float. */
@media (max-width: 900px) {
  body.page-id-698 #operator-story .elementor-element-7ab76f5 {
    float: none !important;
    width: 100% !important;
    max-width: 320px !important;
    margin: 0 auto 24px !important;
  }
}

/* iter-7 m3-fu (2026-04-29) — additional /about-us/ polish.
   1) Operator: "The background of the boxes needs to match 'How an
      engagement actually runs' since they are the same div." → drop
      the white-ink border + blue box-shadow on the 3 methodology
      cards (0bd2fc4, 3c61715, 54ac47c) so they read as part of the
      band, not separate floating tiles.
   2) Operator: "The background of the 'Why this exists' area needs
      to be the darker color." → flip operator-story band from soft
      (rgb(19,35,56)) to dark (rgb(10,22,38)). Restores strict
      alternation: hero(D) → methodology(S) → operator-story(D) →
      cta-band(S). */
/* Higher specificity to beat Elementor's compiled per-page CSS
   (post-123218.css). Their selector is
   `.elementor-123218 .elementor-element.elementor-element-X:not(.x)`
   which is 0,4,0; need 0,4,1 or higher. Chain `.e-con` (already on
   the element) to add a class.
   iter-7 m3-fu2 (2026-04-29): operator wants methodology cards to keep
   the "normal box styling" — 1.5px white-ink border + soft blue
   box-shadow per the iter-3a/iter-5 pillar-card chrome. Bg stays
   soft (matching band) so cards float on the band via shadow, not
   bg contrast. Also restore vertical padding (was collapsed to 0). */
body.page-id-698 .elementor-element.elementor-element-0bd2fc4.e-con,
body.page-id-698 .elementor-element.elementor-element-3c61715.e-con,
body.page-id-698 .elementor-element.elementor-element-54ac47c.e-con {
  background-color: rgb(19, 35, 56) !important;
  background-image: none !important;
  border: 1.5px solid rgba(245, 247, 250, 0.22) !important;
  box-shadow: 6px 6px 0 rgba(74, 157, 200, 0.18) !important;
  padding: 32px 28px !important;
}
/* iter-7 m3-fu3 (2026-04-29) — methodology row container 836dbb9
   carries its own dark fill from Elementor compiled CSS, rendering as
   a dark rectangular band behind the 3 cards (visible against the
   soft methodology band). Make it transparent so only the cards float
   on the soft band. */
body.page-id-698 .elementor-element.elementor-element-836dbb9.e-con {
  background-color: transparent !important;
  background-image: none !important;
}
/* Principles block on operator-story — kept transparent so it inherits
   the founder band (now light) with no separate box treatment. */
body.page-id-698 #operator-story .sw-principles-block {
  background-color: transparent !important;
  background-image: none !important;
}
/* iter-7 m4 (2026-04-30) — operator review: principles list rendering
   pinned at body baseline 18px and verified inert vs the page-baseline.
   Bump +2px (20px) at body-class specificity so the italic an italic display companion (retired)
   list reads as a deliberate editorial accent slightly elevated from
   surrounding 18px body prose, while still in scale with it. The base
   rule (21px/1.4) and the widget JSON (cleared 2026-04-30) no longer
   compete; this is the single authority. */
body.page-id-698 #operator-story .sw-principles-block li {
  font-size: 20px !important;
  line-height: 1.65 !important;
}

/* iter-3c follow-up — blog archive post-card date breathing room.
   Operator review of /blog/ flagged the date ("February 10, 2026" etc.)
   as visually squeezed against the post title. Adding 16px margin-top
   on `.elementor-post-date` (the date span emitted by Elementor's posts
   widget on the archive) gives the title room to breathe before the
   meta. The selector is generic across all loop-grid / posts widgets so
   any future post-card surface inherits the same spacing. */
.elementor-post-date {
  margin-top: 16px;
  display: inline-block;
}

/* iter-3c follow-up v2 (2026-04-27) — operator review pass: blog-single
   chrome + body should span the full editorial canvas like other P3
   surfaces. The earlier 720px reading-column constraint was reverted —
   featured image, field-log eyebrow, title, blog-meta-row, theme-post-
   content body all span the block's full 1290px width. Drop cap +
   pull-quote rules from iter-3a remain scoped to [data-sw-role=
   "article-body"] but operate on the wider column. */

/* iter-3c case-study editorial template — design spec §3.4.
   Renders via themes/blocksy-child/single-sw_case_study.php (theme-PHP
   override per ExecPlan §8.8). Each section uses iter-3a building blocks
   (data-sw-band, .sw-chapter-ebrow, .sw-chapter-dark, sw-portrait-frame,
   the inline accent class (retired), [data-sw-card]) plus these case-specific layout rules.
   Note: the original `.sw-case-study [data-sw-section]` padding rule that
   lived here has been superseded by the iter-3 follow-up site-wide padding
   rule above (`body.single-sw_case_study [data-sw-section]` with
   clamp(64-96)/(24-48)). The follow-up rule wins on both specificity and
   source order, so the old rule is dead and has been intentionally removed
   (kept this comment as a forensic breadcrumb). */
.sw-case-hero {
  text-align: center;
}
.sw-case-hero__inner {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 24px;
  text-align: center;
}
.sw-case-hero__logo {
  width: 96px;
  height: 96px;
  margin: 0 0 8px 0;
  max-width: 96px;
}
.sw-case-hero__logo img {
  object-fit: contain;
  padding: 12px;
}
.sw-case-hero__eyebrow {
  display: inline-block;
  margin: 0;
}
.sw-case-hero__client {
  color: var(--ink);
}
.sw-case-hero__title {
  font-family: var(--font-sans);
  font-size: clamp(22px, 3.5vw, 40px);
  font-weight: 600;
  line-height: 1.05;
  letter-spacing: -0.012em;
  color: var(--ink);
  margin: 0;
  /* Updated 2026-05-27 (post-#136 cleanup): max-width intentionally OMITTED.
     This BEM class is only ever rendered inside the single-sw_case_study.php
     template (verified: only emission site is line 88 of that template, always
     inside <article class="sw-case-study">). Width is set by the
     .sw-case-study-scoped rule at line 631 (= var(--width-wide)) which wins
     via higher specificity. If this class is ever reused outside that scope
     (e.g., a new template), the developer should make a conscious width-tier
     choice rather than silently inheriting a wide-tier value. See widths.md
     decision tree. */
}
.sw-case-hero__chips {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 8px;
  margin: 16px 0 0 0;
  padding: 0;
  list-style: none;
}
.sw-case-hero__chips li {
  font-family: var(--font-mono);
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--blue);
  background: var(--blue-tint);
  padding: 4px 12px;
  border-radius: 999px;
}

.sw-case-body {
  background: var(--bg);
}
.sw-case-body__inner {
  font-family: var(--font-sans);
  font-size: 18px;
  line-height: 1.65;
  color: var(--ink);
}
.sw-case-body__inner h2 {
  font-family: var(--font-sans);
  font-size: clamp(28px, 3vw, 36px);
  font-weight: 600;
  line-height: 1.15;
  margin: 48px 0 16px 0;
}
.sw-case-body__inner h3 {
  font-family: var(--font-sans);
  font-size: 22px;
  font-weight: 600;
  margin: 32px 0 12px 0;
}
.sw-case-body__inner p {
  margin: 0 0 16px 0;
}
.sw-case-body__inner ul,
.sw-case-body__inner ol {
  margin: 0 0 24px 1.5em;
}

.sw-case-outcome__inner {
  text-align: left;
}
.sw-case-outcome__title {
  font-family: var(--font-sans);
  font-size: clamp(32px, 4vw, 44px);
  font-weight: 600;
  line-height: 1.1;
  margin: 16px 0 24px 0;
  color: var(--bg);
}
.sw-case-outcome__cards {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 24px;
  list-style: none;
  margin: 32px 0 0 0;
  padding: 0;
}
@media (max-width: 1024px) {
  .sw-case-outcome__cards { grid-template-columns: 1fr; }
}
.sw-case-outcome__card {
  display: flex;
  flex-direction: column;
  gap: 12px;
  padding: 32px 28px;
}
.sw-case-outcome__num {
  font-family: var(--font-mono);
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.18em;
  color: var(--blue-bright);
}
.sw-case-outcome__label {
  font-family: var(--font-sans);
  font-size: 13px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: rgb(245 247 250 / 70%);
}
.sw-case-outcome__value {
  font-family: var(--font-sans);
  font-size: 26px;
  font-weight: 700;
  line-height: 1.1;
  color: var(--bg);
}
.sw-case-outcome__qualifier {
  font-family: var(--font-sans);
  font-size: 14px;
  line-height: 1.4;
  color: rgb(245 247 250 / 70%);
}

.sw-case-sources__composite {
  border: 1px solid var(--ink);
  padding: 16px 20px;
  font-family: var(--font-sans);
  font-size: 14px;
  line-height: 1.55;
  color: var(--ink);
  margin: 0 0 24px 0;
}
.sw-case-sources__composite strong {
  color: var(--ink);
}
.sw-case-sources__list {
  font-family: var(--font-sans);
  font-size: 14px;
  line-height: 1.55;
  color: rgb(11 27 43 / 70%);
}

.sw-case-cta {
  text-align: center;
}
.sw-case-cta__inner {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 16px;
}
.sw-case-cta__title {
  font-family: var(--font-sans);
  font-size: clamp(36px, 4.5vw, 56px);
  font-weight: 600;
  line-height: 1.05;
  margin: 0;
  /* Updated 2026-05-27 (post-#136 cleanup): max-width intentionally OMITTED.
     This BEM class is only ever rendered inside the single-sw_case_study.php
     template (verified: only emission site is line 151 of that template,
     always inside <article class="sw-case-study">). Width is set by the
     .sw-case-study-scoped rule at line 632 (= var(--width-wide)) which wins
     via higher specificity. If this class is ever reused outside that scope,
     the developer should make a conscious width-tier choice rather than
     silently inheriting a wide-tier value. See widths.md decision tree. */
}
.sw-case-cta__lede {
  font-family: var(--font-sans);
  font-size: 18px;
  line-height: 1.55;
  color: rgb(11 27 43 / 72%);
  margin: 0;
  max-width: var(--width-narrow);
}
.sw-case-cta__button {
  display: inline-block;
  padding: 16px 32px;
  background: var(--blue);
  color: var(--bg);
  font-family: var(--font-sans);
  font-size: 16px;
  font-weight: 600;
  letter-spacing: 0.02em;
  border-radius: 6px;
  text-decoration: none;
  margin-top: 12px;
  transition: background 150ms ease;
}
.sw-case-cta__button:hover {
  background: var(--ink);
}

/* iter-3b engagement-timeline — design spec §3.3 chapter 03.
   Horizontal 3-step engagement timeline with a connecting
   hairline rule between the S6-style ordinal nodes. Lives inside the
   engagement section on /services-hub/. Each step has a numbered node
   (18px circle, 1.5px ink border, 4px hard shadow), heading, and body. */
.sw-engagement-timeline {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 40px;
  list-style: none;
  margin: 32px 0 0 0;
  padding: 0;
  position: relative;
}
.sw-engagement-timeline::before {
  content: "";
  position: absolute;
  top: 9px;                            /* center of 18px node */
  left: calc(100% / 6 + 28px);         /* skip past the first node */
  right: calc(100% / 6 + 28px);        /* skip past the last node */
  height: 1px;
  background: rgb(11 27 43 / 12%);
  z-index: 0;
}
.sw-engagement-timeline .sw-engagement-step {
  position: relative;
  z-index: 1;
  margin: 0;
  padding: 0;
}
.sw-engagement-timeline .sw-engagement-step__num {
  display: inline-block;
  width: 28px;
  height: 28px;
  font-family: var(--font-mono);
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.04em;
  line-height: 24px;
  text-align: center;
  color: var(--ink);
  background: var(--bg);
  border: 1.5px solid var(--ink);
  border-radius: 999px;
  box-shadow: 4px 4px 0 var(--ink);
  margin: 0 0 20px 0;
}
.sw-engagement-timeline .sw-engagement-step__title {
  font-family: var(--font-sans);
  font-weight: 600;
  font-size: 22px;
  line-height: 1.2;
  color: var(--ink);
  margin: 0 0 12px 0;
}
.sw-engagement-timeline .sw-engagement-step__body {
  font-family: var(--font-sans);
  font-size: 16px;
  line-height: 1.55;
  color: var(--ink);
  margin: 0;
}
@media (max-width: 1024px) {
  .sw-engagement-timeline { grid-template-columns: repeat(3, 1fr); gap: 24px; }
}
@media (max-width: 640px) {
  .sw-engagement-timeline { grid-template-columns: 1fr; gap: 32px; }
  .sw-engagement-timeline::before { display: none; }
}

/* iter-3b blog meta row — design spec §3.5 R2.
   Hairline-divider meta row above the blog post body: `[Apr 23, 2026] · [6 min
   read] · Written by Chet Bohley`. The shortcode [sw_blog_meta] emits the
   markup; this rule paints it. Spans the centered body column so the row sits
   under the post title symmetrically with the post body. */
.sw-blog-meta-row {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 12px;
  margin: 20px 0 24px 0;
  padding-top: 20px;
  border-top: 1px solid rgb(11 27 43 / 8%);
  font-family: var(--font-mono);
  font-size: 13px;
  font-weight: 500;
  letter-spacing: 0.04em;
  color: var(--ink);
}
.sw-blog-meta-row__sep {
  color: rgb(11 27 43 / 40%);
}
.sw-blog-meta-row__date {
  color: var(--ink);
}
.sw-blog-meta-row__reading {
  color: var(--blue);
}
.sw-blog-meta-row__author {
  color: rgb(11 27 43 / 70%);
}

/* iter-3b field-log eyebrow — design spec §3.5 R1.
   Variant of .sw-chapter-ebrow used on blog-single (above the post title)
   with per-segment coloring: #001 in --rust (handled by the chapter-ebrow
   strong rule above), separators in 0.6-alpha ink, category in --blue, date
   in 0.6-alpha ink. The shortcode emits the markup via
   [sw_field_log_eyebrow] (plugin/sytewide-site/). */
.sw-field-log-eyebrow__sep {
  color: rgb(11 27 43 / 60%);
}
.sw-field-log-eyebrow__cat {
  color: var(--blue);
}
.sw-field-log-eyebrow__date {
  color: rgb(11 27 43 / 60%);
}

/* iter-3a centered body column — design spec §2.6.
   Every flowing-text body column gets max-width 720px centered. Heros stay
   wider (max-width 540px lede). Card grids, metric rows, and timelines can
   span the full section width inside section padding (they're not flowing
   reading text). Targets via `.sw-body-inner` class added to text wrappers
   inside each iter-3 section. */
[data-sw-section] .sw-body-inner {
  max-width: var(--width-prose);
  margin: 0 auto;
}

/* iter-3a pull-quote — design spec §3.5 R4, scoped to the_content body.
   Any <blockquote> in a blog post body becomes an editorial pull-quote with
   --bg-soft background, 4px rust left rule, an italic display companion (retired) italic prose, and
   a decorative close-brace glyph at top-left. ContentPen-safe: scoped to
   [data-sw-role="article-body"] (no structural change to the_content).

   M0.P2.g (P4, 2026-04-30): bring this rule into the iter-7 c2-fu7
   canonical pull-quote pattern (see / (home) testimonial rule at
   #sw-testimonial-band .elementor-element-be89e77). The previous
   absolute-positioned glyph at top:4px left:8px rendered as a small
   pale glyph floating above-left of the prose — operator flagged on
   /case-studies/placeholder-case-study/ ("Check other pages for how
   the " glyph is positioned, this one is off"). Switch to flex-row
   with the glyph as a static ::before flex item, brighter rust at
   75% opacity, and a -6px top margin so the glyph descender aligns
   with the prose baseline. */
[data-sw-role="article-body"] blockquote {
  background: var(--bg-soft);
  border: 0;
  border-left: 4px solid var(--ink);
  margin: 32px 0;
  padding: 20px 28px;
  font-size: 22px;
  line-height: 1.4;
  color: var(--ink);
  display: flex;
  flex-direction: row;
  align-items: flex-start;
  gap: 20px;
}
[data-sw-role="article-body"] blockquote::before {
  content: "\201C"; /* left double quotation mark */
  flex-shrink: 0;
  font-weight: 400;
  font-size: 64px;
  line-height: 0.8;
  color: rgb(74 157 200 / 75%);
  margin: -6px 0 0 0;
  pointer-events: none;
  user-select: none;
}
/* Center the prose vertically within the blockquote's cross-axis without
   moving the glyph (which keeps its iter-7 c2-fu7 flex-start anchoring
   for multi-line quotes where glyph-top should align with line-1-top).
   For single-line quotes this re-centers the prose in the empty space
   below the taller glyph; for multi-line quotes the prose grows past
   the glyph's height and align-self has no visible effect. */
[data-sw-role="article-body"] blockquote > p {
  align-self: center;
}

/* iter-3a three-principles upgraded block — design spec §3.2.
   Applied to the existing principle-quote text-editor on /about-us/
   container 0ef96e6 via a wrapper class (.sw-principles-block) added in
   Phase 2 (iter-3b). For now: scaffold the styles so they're ready when
   the wrapper lands. Replaces the existing italic blockquote treatment with
   a single block: rust left rule, decorative serif close-brace quote glyph, JetBrains
   Mono "01/02/03" numerals beside each principle, an italic display companion (retired) italic in rust. */
.sw-principles-block {
  background: var(--bg-soft);
  border-left: 4px solid var(--ink);
  padding: 32px 36px;
  margin: 32px 0;
  position: relative;
}
.sw-principles-block::before {
  content: "\201C";
  position: absolute;
  top: 8px;
  left: 12px;
  font-size: 72px;
  line-height: 1;
  color: rgb(180 75 31 / 25%);
  pointer-events: none;
  user-select: none;
}
.sw-principles-block ol {
  list-style: none;
  margin: 0;
  padding: 0;
  counter-reset: sw-principle;
}
.sw-principles-block li {
  counter-increment: sw-principle;
  position: relative;
  padding-left: 64px;
  margin: 18px 0;
  font-weight: 500;
  font-size: 21px;
  line-height: 1.4;
  color: var(--ink);
}
.sw-principles-block li::before {
  content: "0" counter(sw-principle);
  position: absolute;
  left: 0;
  top: 4px;
  font-family: var(--font-mono);
  font-style: normal;
  font-size: 14px;
  font-weight: 500;
  letter-spacing: 0.12em;
  color: var(--ink);
}

/* iter-3a founder portrait frame — design spec §3.2.
   S6-style hard-shadow frame for the founder portrait image on /about-us/.
   Wraps an <img> at 280px max-width centered. Asset:
   stressed-businessman.png (banked from M2) for now; operator can swap to a
   real headshot via WP Media Library when available. */
.sw-portrait-frame {
  display: block;
  max-width: 280px;
  margin: 24px auto 32px;
  border: 1.5px solid var(--ink);
  border-radius: 0;
  box-shadow: 6px 6px 0 var(--ink);
  overflow: hidden;
  background: var(--bg);
}
.sw-portrait-frame img {
  display: block;
  width: 100%;
  height: auto;
}

/* iter-3a dark-twin chapter band — design spec §3.3 chapter 02.
   Applied to /services-hub/ products section in Phase 2 (iter-3b). Flips
   the section to --ink background with reverse-twin text colors via the
   existing .sw-dark-twin scope. Adds a 40px gradient transition strip at
   the top of the section so the previous --bg-soft band doesn't hard-cut.
   !important is required: Elementor's per-page CSS sets background-color
   on the same-specificity .elementor-element-XXX selector and loads after
   sytewide-overrides.css, so per-element wins ties without !important. */
.sw-chapter-dark {
  background: var(--ink) !important;
  color: var(--bg);
  position: relative;
}
.sw-chapter-dark::before {
  content: "";
  position: absolute;
  top: -40px;
  left: 0;
  right: 0;
  height: 40px;
  background: linear-gradient(180deg, var(--bg-soft) 0%, var(--ink) 100%);
  pointer-events: none;
  z-index: 1;
}
/* Inherit dark-twin color flips for nested elements without requiring the
   .sw-dark-twin class on every nested container. !important per the same
   per-page-CSS reason as the background above. */
.sw-chapter-dark h1,
.sw-chapter-dark h2,
.sw-chapter-dark h3,
.sw-chapter-dark p,
.sw-chapter-dark .elementor-heading-title {
  color: var(--bg) !important;
}
.sw-chapter-dark .sw-chapter-ebrow strong,
.sw-chapter-dark .sw-chapter-ebrow .elementor-heading-title strong {
  color: var(--blue-bright) !important;
}

/* iter-3a massive ordinal backdrop — design spec §3.3.
   Massive ghost number ("01", "02") in JetBrains Mono at 320px / 4% opacity,
   absolutely positioned in the top-right corner of a chaptered section.
   Decorative only — pointer-events: none, aria-hidden by convention. */
.sw-chapter-num {
  position: absolute;
  top: 12px;
  right: 24px;
  font-family: var(--font-mono);
  font-size: 320px;
  font-weight: 500;
  line-height: 0.85;
  letter-spacing: -0.03em;
  color: rgb(11 27 43 / 4%);
  pointer-events: none;
  user-select: none;
  z-index: 0;
}
.sw-chapter-dark .sw-chapter-num {
  color: rgb(250 250 247 / 6%);
}
@media (max-width: 1024px) {
  .sw-chapter-num { font-size: 220px; right: 16px; }
}
@media (max-width: 640px) {
  .sw-chapter-num { font-size: 140px; top: 8px; right: 12px; }
}

/* iter-3a banded sections — design spec §2.1 alternation.
   Section background alternates between --bg (default canvas) and --bg-soft
   (subtle panel). The `data-sw-band="soft"` attribute on a section container
   triggers the soft variant. iter-2 sections without the attribute keep
   their existing --bg or --bg-soft from per-element settings. Bands are
   positioned containers (so .sw-chapter-num absolute backdrop ghost child
   anchors to the section, not the body) with overflow: hidden to keep the
   backdrop within section bounds. */
[data-sw-band] {
  position: relative;
  overflow: hidden;
}
[data-sw-band="soft"] {
  background: var(--bg-soft) !important;
}
[data-sw-band="default"] {
  background: var(--bg) !important;
}
/* .sw-chapter-dark is also a chapter context (positioning + clipping),
   inheriting the same overflow/positioning behavior as banded sections.
   !important per the same per-page-CSS reason as the background. */
.sw-chapter-dark {
  overflow: hidden !important;
}

/* iter-3b dark-twin product card label scope — when a card lives inside
   .sw-chapter-dark, force per-element title colors that the chapter's
   color cascade can't reach (Elementor heading widget injects its own
   title_color via per-page CSS at higher specificity). The dark-twin
   product cards' "01 / Product / Back-office" label flips from --blue to
   --blue-bright for contrast on the #102338 card background. */
.sw-chapter-dark [data-sw-card] .elementor-heading-title {
  color: var(--bg) !important;
}
.sw-chapter-dark [data-sw-card] [data-sw-role="label"] .elementor-heading-title,
.sw-chapter-dark [data-sw-card] .elementor-element[data-sw-role="label"] .elementor-heading-title {
  color: var(--blue-bright) !important;
}
.sw-chapter-dark [data-sw-card] p,
.sw-chapter-dark [data-sw-card] .elementor-widget-text-editor {
  color: var(--bg) !important;
}

/* iter-4 M5 (2026-04-27) — /contact-us/ (post 695) WPForms aesthetic.
   Brings WPForms #wpforms-439 inline with the iter-3 form aesthetic
   (1.5px ink border, 6px radius, JetBrains Mono uppercase labels, var(--blue)
   focus ring). Scoped to body.page-id-695 so it doesn't bleed to other
   pages running WPForms. Per spec
   .agent/specs/2026-04-27-p3-iter4-contact-us.md § "CSS additions". */
body.page-id-695 .wpforms-form input[type="text"],
body.page-id-695 .wpforms-form input[type="email"],
body.page-id-695 .wpforms-form input[type="tel"],
body.page-id-695 .wpforms-form input[type="url"],
body.page-id-695 .wpforms-form select,
body.page-id-695 .wpforms-form textarea {
  font-family: var(--font-sans) !important;
  font-size: 17px !important;
  padding: 14px 16px !important;
  border: 1.5px solid var(--ink) !important;
  border-radius: var(--radius-sm) !important;
  background: var(--bg) !important;
  color: var(--ink) !important;
  outline: none !important;
}
body.page-id-695 .wpforms-form input:focus,
body.page-id-695 .wpforms-form textarea:focus,
body.page-id-695 .wpforms-form select:focus {
  border-color: var(--blue) !important;
  box-shadow: 0 0 0 3px rgb(31 95 133 / 12%) !important;
}
body.page-id-695 .wpforms-form label.wpforms-field-label,
body.page-id-695 .wpforms-form .wpforms-field-label {
  font-family: var(--font-mono) !important;
  font-size: 11px !important;
  letter-spacing: 0.14em !important;
  text-transform: uppercase !important;
  color: var(--mute) !important;
  font-weight: 500 !important;
}
body.page-id-695 .wpforms-form button.wpforms-submit,
body.page-id-695 .wpforms-form input[type="submit"] {
  background: var(--blue) !important;
  color: var(--bg) !important;
  font-family: var(--font-sans) !important;
  font-size: 15px !important;
  font-weight: 600 !important;
  padding: 14px 24px !important;
  border-radius: var(--radius-sm) !important;
  border: 1.5px solid var(--blue) !important;
  cursor: pointer;
  transition: background 0.18s ease;
}
body.page-id-695 .wpforms-form button.wpforms-submit:hover,
body.page-id-695 .wpforms-form input[type="submit"]:hover {
  background: #194D6B !important;
}
body.page-id-695 .wpforms-form .wpforms-field-description {
  font-size: 13px !important;
  color: var(--mute) !important;
  margin-top: 6px;
}

/* iter-4 M5 (2026-04-27) — /contact-us/ map-overlay address strip.
   Used inside the Band 03 "Find us" Elementor google_maps embed wrapper.
   Sits as a full-width bottom strip flush against the map frame edge,
   covering Google's "Map data ©" attribution row at the bottom of the
   iframe. Single top hairline rule separates it from the map; no S6
   hard shadow (the map frame container already carries the S6 hard
   shadow externally per Band 03 spec). Authored in Elementor as a sub-
   container with class `sw-map-overlay`. */
body.page-id-695 .sw-map-overlay {
  position: absolute;
  bottom: 0;
  left: 0;
  right: 0;
  background: var(--bg);
  border-top: 1.5px solid var(--ink);
  padding: var(--space-3) var(--space-4);
  z-index: 2;
}

/* 2026-05-14 (Phase 5b): iter-4 M6 spillover `body.page-id-700 .sw-video-hero`
   block (~60 lines: page-id-700 video-frame border, ::before grid overlay,
   __play z-index/sizing, iframe z-index, ::after "01 / WALKTHROUGH" caption,
   :has(iframe) hide) RETIRED. The `.sw-video-hero` element no longer exists
   anywhere in the site DOM (live probe 2026-05-14, querySelectorAll across
   all 9 audited surfaces = 0). The home walkthrough band that hosted it was
   removed during the 2026-05-07 revamp; sub-brand product detail pages use a
   different reel pattern (.sw-reel-marquee). The generalized
   `.sw-dark-twin [data-sw-section="hero"] .sw-video-hero` block below also
   matches zero elements but is kept as scaffolding for the post-revamp Backlog
   "real Gumlet videos per product" item (CLAUDE.md M-products-redesign close);
   when operator uploads videos and re-introduces a .sw-video-hero element,
   that block lights up automatically. */
/* Sub-brand product page hero video placeholder — generalized treatment that
   auto-applies whenever a .sw-video-hero appears inside a .sw-dark-twin
   hero band (M6 /syteops/, M7 /products/sytehero-ltd/, and any future
   dark-twin product surface). Mirror of the / (home) walkthrough block
   above (lines 1749–1805) but scoped by class hook instead of page-id so
   new product templates inherit it without per-page CSS work.

   Operator review v6: "on all templates, change the image to a video
   placeholder for gumlet." Replaces the M6.5 + M7.5 image-widget treatment
   (now obsolete since hero video-frames use the .sw-video-hero structure
   directly with click-to-load Gumlet embed). data-embed can be empty for
   placeholder mode (the JS short-circuits on missing URL); when operator
   uploads real Gumlet videos, just set data-embed on each page's
   .sw-video-hero element. */
.sw-dark-twin [data-sw-section="hero"] .sw-video-hero {
  position: relative !important;
  aspect-ratio: 16 / 10 !important;
  width: 100% !important;
  background: var(--bg-soft) !important;
  border: 1.5px solid rgb(255 255 255 / 22%) !important;
  border-radius: 0 !important;
  box-shadow: 12px 12px 0 rgb(74 157 200 / 22%) !important;
  overflow: hidden !important;
}
.sw-dark-twin [data-sw-section="hero"] .sw-video-hero::before {
  content: "";
  position: absolute;
  inset: 0;
  background:
    radial-gradient(1200px 600px at 30% 30%, rgb(94 168 227 / 18%), transparent 60%),
    radial-gradient(800px 400px at 70% 70%, rgb(180 75 31 / 14%), transparent 60%),
    repeating-linear-gradient(0deg, transparent 0 39px, rgb(255 255 255 / 4%) 39px 40px),
    repeating-linear-gradient(90deg, transparent 0 39px, rgb(255 255 255 / 4%) 39px 40px);
  pointer-events: none;
  z-index: 1;
}
.sw-dark-twin [data-sw-section="hero"] .sw-video-hero__play {
  position: absolute;
  inset: 0;
  margin: auto;
  width: 92px;
  height: 92px;
  display: flex;
  align-items: center;
  justify-content: center;
  border: 0;
  border-radius: 50%;
  background: rgb(11 27 43 / 32%);
  color: var(--ink);
  cursor: pointer;
  transition: background 0.18s ease, transform 0.18s ease;
  z-index: 2 !important;
}
.sw-dark-twin [data-sw-section="hero"] .sw-video-hero__play:hover {
  background: rgb(11 27 43 / 56%);
  transform: scale(1.04);
}
.sw-dark-twin [data-sw-section="hero"] .sw-video-hero__play:focus-visible {
  outline: 3px solid var(--blue-bright);
  outline-offset: -3px;
}
.sw-dark-twin [data-sw-section="hero"] .sw-video-hero__play-glyph {
  font-size: 32px;
  line-height: 1;
}
.sw-dark-twin [data-sw-section="hero"] .sw-video-hero iframe {
  position: absolute !important;
  inset: 0 !important;
  width: 100% !important;
  height: 100% !important;
  border: 0 !important;
  z-index: 3 !important;
}
/* Eyebrow caption ("↗ WALKTHROUGH") top-left of the frame — design-system
   convention. Hidden once the Gumlet iframe loads (click-to-play). */
.sw-dark-twin [data-sw-section="hero"] .sw-video-hero::after {
  content: "↗ WALKTHROUGH";
  position: absolute;
  top: 24px;
  left: 24px;
  font-family: var(--font-mono, "JetBrains Mono", monospace);
  font-size: 10px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--blue-bright);
  background: rgb(10 22 38 / 80%);
  padding: 6px 10px;
  border: 1px solid rgb(94 168 227 / 30%);
  z-index: 2;
  pointer-events: none;
}
.sw-dark-twin [data-sw-section="hero"] .sw-video-hero:has(iframe)::before,
.sw-dark-twin [data-sw-section="hero"] .sw-video-hero:has(iframe)::after,
.sw-dark-twin [data-sw-section="hero"] .sw-video-hero.sw-video-hero--active::before,
.sw-dark-twin [data-sw-section="hero"] .sw-video-hero.sw-video-hero--active::after {
  display: none;
}

/* iter-4 M7 (2026-04-27) → iter-5 generalized (2026-04-28) — design rules
   originally authored for /products/sytehero-ltd/ (post 121487), then
   generalized at M7 v3 to body.single-fluent-products.elementor-page so
   any FluentCart product page inherited the dark canvas automatically.
   Iter-5 takes the dark canvas across all 9 P3 surfaces (regular pages
   too, not just fluent-products CPT), so M7.1–M7.9 now scope to the
   `.sw-dark-twin` class wrapper directly. M7.10 stays body-class scoped
   because it suppresses Blocksy chrome OUTSIDE the wrapper that only
   exists on fluent-products CPT (.entry-header, article.fluent-products,
   etc. — regular pages use elementor-template-full-width which handles
   this at template level). FluentCart Path A filter (Plugin::boot())
   continues to suppress the auto product-page chrome so the_content()
   (Elementor build) drives the body. */

/* M7.1 (2026-05-22) → tokenized 2026-05-27.
   IMPORTANT — this rule applies SITEWIDE to every .sw-dark-twin page
   (Home, About-us, Services-hub, /products/ archive, all 5 product
   detail pages, Audit, Newsletter, Contact-us, Privacy, Shop, etc.).
   The original M7.1 comment ("Scoped to /products/sytehero-ltd/") was
   incorrect — the selector .sw-dark-twin .e-con-inner is unscoped.
   This is THE practical sitewide canvas cap. See
   docs/developer/design-system/widths.md (canvas tier). */
.sw-dark-twin .e-con-inner {
  max-width: min(100%, var(--width-canvas)) !important;
}

/* M7.3 — an italic display companion (retired) italic on rust-accent spans. The inline <em> tag in
   hero h1 + cta-h2 carries inline style with font-family an italic display companion (retired), but
   Elementor's heading widget can override font-family on descendants. */
.sw-dark-twin .elementor-widget-heading em {
  font-weight: 400 !important;
  color: #4A9DC8 !important;
}

/* M7.4 — beef up card shadows to match mockup's 6×6 hard blue-tint
   shadow at 22% opacity (was authored at 12% which is barely visible). */
.sw-dark-twin [data-sw-card="pillar"],
.sw-dark-twin [data-sw-card="tier"],
.sw-dark-twin [data-sw-card="tier-featured"] {
  box-shadow: 6px 6px 0 rgb(74 157 200 / 22%);
}
.sw-dark-twin [data-sw-card="tier-featured"] {
  box-shadow: 8px 8px 0 var(--blue);
}

/* M7.6 — fluent-cart product buy section: comprehensive dark-theme +
   centered layout integration. Operator review v3 (orange + center +
   readable) — was previously rendering with unreadable dark-on-dark
   buttons + left-aligned tabs + ungrouped Buy Now. */
.sw-dark-twin .sw-fc-buy-section {
  color: var(--ink) !important;
}
.sw-dark-twin .sw-fc-buy-section .fluentcart-product-buy-section,
.sw-dark-twin .sw-fc-buy-section .fct_buy_section {
  background: transparent !important;
  display: flex !important;
  flex-direction: column !important;
  align-items: center !important;
  width: 100% !important;
}

/* Yearly / Monthly tab nav — center the strip + visible labels */
.sw-dark-twin .sw-fc-buy-section .fct-product-tab {
  width: 100% !important;
  display: flex !important;
  flex-direction: column !important;
  align-items: center !important;
}
.sw-dark-twin .sw-fc-buy-section .fct-product-tab-nav {
  justify-content: center !important;
  border-bottom: 1px solid rgb(245 247 250 / 12%) !important;
  display: flex !important;
  margin: 0 auto 24px !important;
  position: relative !important;
}
.sw-dark-twin .sw-fc-buy-section .fct-product-tab-nav-item {
  color: rgb(245 247 250 / 65%) !important;
  font-family: var(--font-mono) !important;
  font-size: 12px !important;
  letter-spacing: 0.18em !important;
  text-transform: uppercase !important;
  padding: 12px 24px !important;
  cursor: pointer !important;
  transition: color 0.18s ease !important;
}
.sw-dark-twin .sw-fc-buy-section .fct-product-tab-nav-item:hover {
  color: var(--ink) !important;
}
.sw-dark-twin .sw-fc-buy-section .fct-product-tab-nav-item.active {
  color: var(--blue-bright) !important;
}
.sw-dark-twin .sw-fc-buy-section .tab-active-bar {
  background: var(--blue) !important;
  height: 2px !important;
}

/* Tab pane content area — center, full width within section */
.sw-dark-twin .sw-fc-buy-section .fct-product-tab-content,
.sw-dark-twin .sw-fc-buy-section .fct-product-tab-pane {
  width: 100% !important;
}

/* License tier variant buttons — readable + centered + outlined */
.sw-dark-twin .sw-fc-buy-section .fct-product-variants {
  display: flex !important;
  flex-wrap: wrap !important;
  justify-content: center !important;
  gap: 12px !important;
  margin: 0 auto 24px !important;
  width: 100% !important;
}
.sw-dark-twin .sw-fc-buy-section .fct-product-variant-item {
  background: transparent !important;
  border: 1.5px solid rgb(245 247 250 / 28%) !important;
  border-radius: 6px !important;
  padding: 12px 20px !important;
  cursor: pointer !important;
  transition: all 0.18s ease !important;
  min-width: 140px !important;
  /* FluentCart ships justify-content:space-between on these chips which
     shoves the title text to the LEFT edge. Override to center so the
     leading seat-count numbers (1 / 5 / 10 / 25 / 50) read as a clean
     row of centered labels. */
  justify-content: center !important;
  text-align: center !important;
}
.sw-dark-twin .sw-fc-buy-section .fct-product-variant-item:hover {
  border-color: rgb(245 247 250 / 55%) !important;
  background: rgb(245 247 250 / 5%) !important;
}
.sw-dark-twin .sw-fc-buy-section .fct-product-variant-item[aria-checked="true"] {
  border-color: var(--blue) !important;
  background: rgb(94 168 227 / 14%) !important;
}
.sw-dark-twin .sw-fc-buy-section .fct-product-variant-title {
  color: var(--ink) !important;
  font-family: var(--font-sans) !important;
  font-size: 14px !important;
  font-weight: 500 !important;
  letter-spacing: 0 !important;
}

/* Price + payment-type labels */
.sw-dark-twin .sw-fc-buy-section .fct-product-item-price {
  color: var(--ink) !important;
  font-family: var(--font-sans) !important;
  font-size: 18px !important;
  font-weight: 600 !important;
  margin: 16px 0 8px !important;
  text-align: center !important;
  width: 100% !important;
}
.sw-dark-twin .sw-fc-buy-section .fct-product-payment-type {
  color: rgb(245 247 250 / 65%) !important;
  font-family: var(--font-mono) !important;
  font-size: 11px !important;
  letter-spacing: 0.14em !important;
  text-transform: uppercase !important;
  text-align: center !important;
  width: 100% !important;
}

/* Buy Now button — primary CTA styling, centered */
.sw-dark-twin .sw-fc-buy-section .fct-product-buttons-wrap {
  display: flex !important;
  justify-content: center !important;
  margin: 16px 0 0 !important;
  width: 100% !important;
}
/* M7.6 buttons — primary (Buy Now) filled, secondary (Add to Cart) outlined.
   Both buttons share the SAME box model so they render at identical width
   and height regardless of label length. Min-width 180px gives the longer
   "Add To Cart" room to breathe; flex:1 on both fills the row equally on
   wider viewports. v3 fixed white-on-white; v4 (operator review) equalizes
   the visual weight of the two CTAs. */
.sw-dark-twin .sw-fc-buy-section .fluent-cart-direct-checkout-button,
.sw-dark-twin .sw-fc-buy-section .fluent-cart-add-to-cart-button,
.sw-dark-twin .sw-fc-buy-section button[type="button"],
.sw-dark-twin .sw-fc-buy-section input[type="submit"] {
  /* Shared box model — same dimensions for primary + secondary CTAs.
     max-height override needed because FluentCart's own CSS ships
     `max-height: 40px` on .fluent-cart-direct-checkout-button which
     constrains our height: 48px otherwise. */
  display: inline-flex !important;
  align-items: center !important;
  justify-content: center !important;
  box-sizing: border-box !important;
  height: 48px !important;
  max-height: 48px !important;
  min-height: 48px !important;
  min-width: 180px !important;
  padding: 0 28px !important;
  border-radius: 6px !important;
  font-family: var(--font-sans) !important;
  font-weight: 600 !important;
  font-size: 15px !important;
  line-height: 1 !important;
  letter-spacing: 0.005em !important;
  cursor: pointer !important;
  transition: background 0.18s ease, border-color 0.18s ease, color 0.18s ease !important;
  text-shadow: none !important;
  box-shadow: none !important;
  background-image: none !important;
}
/* Primary CTA — filled dark-accent. */
.sw-dark-twin .sw-fc-buy-section .fluent-cart-direct-checkout-button,
.sw-dark-twin .sw-fc-buy-section button[type="button"],
.sw-dark-twin .sw-fc-buy-section input[type="submit"] {
  background: var(--blue) !important;
  color: var(--bg) !important;
  border: 1.5px solid var(--blue) !important;
}
.sw-dark-twin .sw-fc-buy-section .fluent-cart-direct-checkout-button:hover,
.sw-dark-twin .sw-fc-buy-section button[type="button"]:hover,
.sw-dark-twin .sw-fc-buy-section input[type="submit"]:hover {
  background: var(--blue-bright) !important;
  border-color: var(--blue-bright) !important;
}
/* Secondary CTA — Add to Cart, outlined. */
.sw-dark-twin .sw-fc-buy-section .fluent-cart-add-to-cart-button {
  background: transparent !important;
  color: var(--ink) !important;
  border: 1.5px solid var(--blue) !important;
}
.sw-dark-twin .sw-fc-buy-section .fluent-cart-add-to-cart-button:hover {
  background: rgb(94 168 227 / 14%) !important;
  border-color: var(--blue-bright) !important;
  color: var(--ink) !important;
}

/* M7.6b — Quantity label + container styling. FluentCart's default
   .quantity-title label renders at rgb(47,52,72) (dark slate) which is
   invisible on the dark canvas. Restyle as a mono uppercase eyebrow that
   matches the design-system label voice. */
.sw-dark-twin .sw-fc-buy-section .quantity-title,
.sw-dark-twin .sw-fc-buy-section .fct-product-quantity-container > label {
  color: var(--blue-bright) !important;
  font-family: var(--font-mono) !important;
  font-size: 11px !important;
  font-weight: 500 !important;
  letter-spacing: 0.14em !important;
  text-transform: uppercase !important;
  margin-bottom: 8px !important;
}
.sw-dark-twin .sw-fc-buy-section .fct-product-quantity-container {
  display: flex !important;
  flex-direction: column !important;
  align-items: center !important;
  gap: 0 !important;
}

/* M7.8 — use case timeline hairline connector (operator review v8).
   The 3-step row container (.sw-timeline-row) gets a faint blue gradient
   line running across the top of the timeline to visually link the step
   markers. Fades at the left + right edges so it doesn't feel like a
   hard border. z-index:1 sits above the band bg but below the step
   ring markers (which are z-index:2 inside their step containers). */
.sw-dark-twin [data-sw-section="usecase"] .sw-timeline-row {
  position: relative !important;
}
.sw-dark-twin [data-sw-section="usecase"] .sw-timeline-row::before {
  content: "";
  position: absolute;
  top: 56px;
  left: 16.67%;
  right: 16.67%;
  height: 1.5px;
  background: linear-gradient(90deg, transparent 0, #5EA8E3 12%, #5EA8E3 88%, transparent 100%);
  opacity: 0.55;
  z-index: 1;
  pointer-events: none;
}
@media (max-width: 768px) {
  .sw-dark-twin [data-sw-section="usecase"] .sw-timeline-row::before {
    display: none;
  }
}

/* M7.7 — pillar sub-bullets in dark-accent-bright (operator review v4).
   v3 tried rust accent (#4A9DC8) but contrast on dark canvas was
   borderline (~4.4:1) at 12px uppercase. v4 uses --dark-accent-bright
   (#8FC9F7) which matches the eyebrow labels for visual consistency
   and gives much higher contrast on the dark-twin pillars band. Also
   bumps size 12→13px and tightens letter-spacing 1→0.5px so the words
   read as units, not individual letters. */
.sw-dark-twin [data-sw-card="pillar"] .sw-pillar-bullets li {
  color: #8FC9F7 !important;
  font-size: 13px !important;
  letter-spacing: 0.5px !important;
}
.sw-dark-twin [data-sw-card="pillar"] .sw-pillar-bullets li::before {
  color: #8FC9F7 !important;
}

/* M7.9 — global Blocksy semantic-tag border guard (QA review M-2).
   Blocksy applies a default `border-left: 3.75px solid <palette-purple>`
   + `padding-left: 66px` to <blockquote> elements, plus default decoration
   on <ul>/<ol>/<table>. The v6 voice-band fix (commit 774d853) nuked
   this on the specific pull-quote widget via inline style, but any future
   blockquote / list / table on /syteops/ would suffer the same bleed.
   This rule guards them all globally for the page scope. Inline styles
   on individual widgets can still override for surgical control. */
.sw-dark-twin blockquote {
  border: 0 !important;
  padding-left: 0 !important;
}
.sw-dark-twin ul:not(.elementor-icon-list-items):not(.sw-pillar-bullets):not(.fct-product-variants),
.sw-dark-twin ol {
  /* keep list semantics but neutralize Blocksy palette border on rare lists. */
  border-left: 0 !important;
}

/* M7.10 — suppress Blocksy default chrome above + below the Elementor build
   on /products/sytehero-ltd/ (operator review v1).

   FluentCart fluent-products CPT renders Blocksy's default page-title chrome
   (.hero-section with entry-header h1 + entry-meta) ABOVE the_content() —
   that's a light-canvas band breaking the dark-twin product page above the
   hero band. Same problem at the bottom: .entry-content's default padding
   + Blocksy footer area below the Elementor build leaves a light gap before
   the actual footer block.

   Fix: hide Blocksy's auto-rendered chrome on this post AND paint the dark
   canvas color on every ancestor up to <main> so any Blocksy padding shows
   dark instead of cream/white. The site footer (Blocksy content block) keeps
   its own bg — this rule only covers the article container area.

   Mirror of M6 behavior: /syteops/ uses elementor-template-full-width which
   handles this at the template level. M7 (fluent-products CPT) doesn't have
   that template option, so we handle it via CSS scope.

   2026-05-28: scoped the `.entry-content` members to `main.site-main
   .entry-content` (was a bare `.entry-content`). The bare selector also
   matched the header + footer Content Blocks' own `.entry-content` wrappers
   (each Blocksy CB renders article > .entry-content), painting the header
   wrapper #0A1626 ON TOP of #header's #0B1B2B (M4-fix-4) — a two-tone header
   band visible only on product pages (single-fluent-products). The header
   sits in #main-container > header#header, OUTSIDE main#main.site-main, so
   constraining to main.site-main lets #header's solid #0B1B2B show through
   again while the page-body article stays dark. */
body.single-fluent-products.elementor-page .hero-section,
body.single-fluent-products.elementor-page .entry-header {
  display: none !important;
}
body.single-fluent-products.elementor-page article.fluent-products,
body.single-fluent-products.elementor-page main.site-main .entry-content,
body.single-fluent-products.elementor-page main.site-main,
body.single-fluent-products.elementor-page .ct-container-full,
body.single-fluent-products.elementor-page .ct-main-styles {
  background-color: #0A1626 !important;
}
body.single-fluent-products.elementor-page main.site-main .entry-content {
  padding-top: 0 !important;
  padding-bottom: 0 !important;
  margin-top: 0 !important;
  margin-bottom: 0 !important;
}

/* ---- iter-5 Session 1 (2026-04-28) — additions for /products/trustily/ ---- */

/* iter-5.1 — pricing-pair max-width buffer.
   The pricing band on Trustily (and the future template extension for any
   sub-brand product page) hosts a 2-col grid: tier-featured card + "Every
   $99 includes" panel. Mockup at iter5-trustily.html sets max-width 1040px
   centered; the boxed kit content_width is 1290px (M7.1) so the 1040 cap
   gives ~125px buffer on each side. Container's flex_direction=row +
   children at width:50% provides the side-by-side; this rule constrains the
   row's max horizontal extent. */
.sw-dark-twin .pricing-pair {
  max-width: var(--width-pricing-pair) !important;
  margin-left: auto !important;
  margin-right: auto !important;
}

/* iter-5.2 — header layout standardization across BOTH content blocks
   (#sw-header-light renders on light surfaces, #sw-header-sub-brand renders
   on dark-twin surfaces — Trustily 123545, sytehero-ltd 121487, syteops
   122387 — via Blocksy conditions). Both blocks have the same 3-child
   shape: [logo · nav-menu · book-audit-button]. Left-as-flex with
   justify_content:space-between distributes children based on their own
   widths, which means the nav menu drifts off-center if logo width ≠
   button width.

   Fix: promote both header containers to CSS grid with `1fr auto 1fr`
   columns. Logo justify-self:start, nav justify-self:center, button
   justify-self:end. Nav now sits perfectly centered horizontally
   regardless of logo/button widths; button is always flush right.

   Dark mode for the sub-brand header is handled by the sub-brand content
   block's own settings (background_color #0B1B2B + css_classes sw-dark-twin
   set on container 27e7a7b) — no CSS scope override needed. The
   header-light block keeps its light styling for non-converted surfaces. */
#sw-header-light,
#sw-header-sub-brand {
  display: grid !important;
  grid-template-columns: 1fr auto 1fr !important;
  align-items: center !important;
}
#sw-header-light > :nth-child(1),
#sw-header-sub-brand > :nth-child(1) {
  justify-self: start !important;
}
#sw-header-light > :nth-child(2),
#sw-header-sub-brand > :nth-child(2) {
  justify-self: center !important;
}
#sw-header-light > :nth-child(3),
#sw-header-sub-brand > :nth-child(3) {
  justify-self: end !important;
}

/* iter-5.3 — mobile SSoT for sub-brand product pages (template scope: any
   .sw-dark-twin surface with a hero band + usecase timeline). Three rules:

   (a) Hero stack on mobile — desktop has video LEFT / content RIGHT at
       50/50 split via flex-row + flex_wrap=nowrap. The flex-wrap=nowrap
       PERSISTS into mobile because Elementor's --flex-wrap-mobile control
       didn't emit an override for this container at iter-4 authoring time
       (M7 has the same bug — both pages cram into a 50/50 split at 375px,
       leaving the heading clipped to half-viewport). Force the parent
       hero-grid to flex-direction:column + width:100% on children. The
       parent is identified via :has(> .sw-hero-video) so this targets
       BOTH /products/sytehero-ltd/ and /products/trustily/ (and any
       future sub-brand product page that follows the SSoT pattern).

   (b) Hero order swap on mobile — once the children stack column-wise,
       set order:2 on the .sw-hero-video container so the heading + lede
       + CTAs render FIRST and the video frame follows below.

   (c) Timeline step center-alignment on mobile — desktop has 3 steps
       side-by-side, each left-aligned. On mobile the steps stack
       vertically which leaves left-aligned content awkward; center the
       step content so each card reads as a self-contained unit. */
@media (max-width: 767px) {
  /* (a) parent: stack column, allow children to be 100% wide */
  .sw-dark-twin [data-sw-section="hero"] .e-con:has(> .sw-hero-video) {
    flex-direction: column !important;
    flex-wrap: wrap !important;
  }
  .sw-dark-twin [data-sw-section="hero"] .e-con:has(> .sw-hero-video) > .e-con {
    width: 100% !important;
    --width: 100% !important;
  }

  /* (b) order swap: text first, video second */
  .sw-dark-twin [data-sw-section="hero"] .sw-hero-video {
    order: 2 !important;
  }

  /* (c) timeline center alignment */
  .sw-dark-twin [data-sw-section="usecase"] .sw-step {
    text-align: center !important;
  }
  .sw-dark-twin [data-sw-section="usecase"] .sw-step > div[aria-hidden="true"] {
    /* keep the giant ord-bg numeral pinned to its original position so the
       depth illusion still works — only the foreground prose centers. */
    text-align: left !important;
  }
  .sw-dark-twin [data-sw-section="usecase"] .sw-step > div:nth-of-type(2) {
    /* ring-marker + step label row — center the flex children */
    justify-content: center !important;
  }
  .sw-dark-twin [data-sw-section="usecase"] .sw-step h3,
  .sw-dark-twin [data-sw-section="usecase"] .sw-step p {
    margin-left: auto !important;
    margin-right: auto !important;
  }
}

/* iter-5.4 — pull-quote mobile centering. Desktop uses flex-row with the
   curly-quote glyph LEFT of the prose (per §8.15 design-system rule). On
   mobile the row stays flex-row by default which leaves the prose pushed
   right by the glyph + 32px gap, reading off-center. Mobile fix: stack the
   glyph above the prose (flex-column), shrink it slightly, and center
   everything. Applies to ALL .sw-pull-quote instances site-wide — every
   product page, the home page voice band, about-us founder quote, blog
   reader-feedback quotes, etc. — so the SSoT mobile treatment is uniform. */
@media (max-width: 767px) {
  .sw-pull-quote {
    flex-direction: column !important;
    align-items: center !important;
    text-align: center !important;
    gap: 8px !important;
    padding: 24px 16px !important;
  }
  .sw-pull-quote > span[aria-hidden="true"] {
    /* v3 (2026-04-28): inline `align-self:flex-start` on the glyph span
       overrides parent `align-items:center` (per spec, align-self always
       beats align-items). Force align-self:center to recenter the glyph
       above the prose on mobile. */
    align-self: center !important;
    margin-top: 0 !important;
    font-size: 72px !important;
    line-height: 1 !important;
  }
  .sw-pull-quote > div {
    align-self: center !important;
  }
  .sw-pull-quote blockquote {
    text-align: center !important;
  }
  .sw-pull-quote figcaption {
    align-items: center !important;
  }
}

/* iter-5.5 — Blocksy/Elementor mobile hamburger trigger (2026-04-28).
   The Elementor Pro nav-menu widget renders a `.elementor-menu-toggle`
   button with a 22px SVG icon. On the dark sub-brand header the default
   icon color (#33373d slate) reads near-invisible against #0B1B2B; the
   button itself sits at top:16px in a 95px-tall header strip (off-center
   by ~15px) because Elementor's nav-menu widget wraps the toggle in
   default chrome that doesn't honor the parent grid's `align-items:
   center`. Force both: light icon color + vertical center via the
   widget container. Scoped to `.sw-dark-twin` so light-bg headers (e.g.
   header-light at 122841 if it ever shows on a non-dark surface) are
   unaffected. */
.sw-dark-twin .elementor-menu-toggle {
  color: var(--ink-on-dark, #F5F7FA) !important;
  align-self: center !important;
}
.sw-dark-twin .elementor-menu-toggle svg,
.sw-dark-twin .elementor-menu-toggle .elementor-menu-toggle__icon--open,
.sw-dark-twin .elementor-menu-toggle .elementor-menu-toggle__icon--close {
  color: var(--ink-on-dark, #F5F7FA) !important;
  fill: var(--ink-on-dark, #F5F7FA) !important;
}
/* The Elementor nav-menu widget renders the toggle + a hidden inline
   menu UL + a dropdown panel as siblings in a flex-column. The widget
   gets ~63px of grid-cell height and stacks children flex-start, leaving
   the toggle pinned to the top. Auto-margins didn't redistribute (likely
   because Elementor's `flex: 1` on the dropdown panel consumes the free
   space). Use translateY(50%) on the toggle relative to the half-empty
   space below it — equivalently, shift it down by half the difference
   between the widget's height and the toggle's height. ~15px works for
   the standard 95px header. */
.sw-dark-twin .elementor-widget-nav-menu {
  align-self: center !important;
}
/* The widget root is flex-direction:column with hidden NAV + toggle +
   dropdown NAV stacked. Column main-axis is vertical, so
   justify-content:center on the widget root vertically centers all
   children — including the toggle. The widget has a default `gap: 20px`
   between flex children that consumes free space; zero it on mobile so
   the centered group of (effectively just the toggle, since both NAVs
   have height:0) renders centered in the widget cell. */
@media (max-width: 1024px) {
  .sw-dark-twin .elementor-widget.elementor-widget-nav-menu {
    justify-content: center !important;
    gap: 0 !important;
    row-gap: 0 !important;
  }
  .sw-dark-twin .elementor-widget.elementor-widget-nav-menu > * {
    margin-top: 0 !important;
    margin-bottom: 0 !important;
  }
}

/* 2026-05-15 — /checkout/ Country + State dropdown text alignment.
   FluentCart's nice-select trigger has height: 38, line-height: 38, +
   10/10 padding-block — the 20px of padding pushes the line-box past
   the box's content area, so the visible text ("United States (US)" /
   "Select State") renders at the top of the field instead of
   vertically centered. Switch the trigger to flex + align-items:center
   so the inline span centers regardless of line-height math. */
.fct-nice-select.fct-input.fct-input-select {
  display: flex;
  align-items: center;
}

/* 2026-05-14 — /about-us/ founder card LinkedIn icon link.
   Replaced the "LinkedIn ↗" text link with an inline SVG of the
   LinkedIn brand mark. The anchor uses `aria-label` for accessibility.
   Color flips to cyan on hover; small lift for affordance. */
.sw-founder-link a {
  display: inline-flex;
  align-items: center;
  color: var(--paper, #F5F7FA);
  border-bottom: none !important;
  transition: color 0.18s ease, transform 0.18s ease;
}
.sw-founder-link a svg {
  width: 32px;
  height: 32px;
  display: block;
}
.sw-founder-link a:hover {
  color: var(--blue-bright, #6CC2EF);
  transform: translateY(-1px);
}

/* 2026-05-14 — Hide the "Book a Meeting (Bohley - Private Availability)"
   ff-el-section-title h3 that lives inside booking form 12. The form's
   own section break shows this heading right above the form fields,
   creating visual noise above the actual booking inputs. Scoped to form
   12's wrapper so other FluentForms keep their section titles intact.
   To extend to additional booking-only form ids later, append the
   .fluentform_wrapper_<ID> selector to this rule. */
.fluentform_wrapper_12 .ff-el-section-title {
  display: none !important;
}

/* 2026-05-14 — Fix Elementor Pro mobile nav-menu dropdown not opening.
   Symptom: hamburger toggle (.elementor-menu-toggle) click flips
   `aria-expanded` true and adds `.elementor-active`, but the sibling
   `.elementor-nav-menu--dropdown` stays at `display: none`. Elementor's
   own JS sets max-height for the expand animation but does NOT set
   inline `display: block` — it relies on a CSS sibling selector to do
   that. On this site that selector either isn't enqueued (Elementor
   asset-loading optimisation) or is being overridden. Force-show the
   dropdown when the toggle is active so the menu actually opens.

   Also style the dropdown as a full-bleed panel below the header so
   the menu items render legibly on mobile (default Elementor Pro
   styling renders it as a tiny 108px-wide inline box). */
.elementor-menu-toggle.elementor-active + .elementor-nav-menu--dropdown,
.elementor-menu-toggle[aria-expanded="true"] + .elementor-nav-menu--dropdown {
  display: block !important;
  position: absolute;
  top: 100%;
  left: 0;
  right: 0;
  width: 100vw;
  margin-inline: calc(50% - 50vw);
  background: var(--bg, #0A1626);
  border-top: 1px solid var(--blue-bright, #6CC2EF);
  /* 2026-05-28 — zero horizontal panel padding so each item (and its
     selection bar + divider) runs flush to the panel edges; the 24px
     text gutter moves onto a.elementor-item below. */
  padding: 16px 0 24px;
  z-index: 100;
  box-shadow: 0 12px 24px rgb(0 0 0 / 28%);
}
.elementor-menu-toggle.elementor-active + .elementor-nav-menu--dropdown .elementor-nav-menu,
.elementor-menu-toggle[aria-expanded="true"] + .elementor-nav-menu--dropdown .elementor-nav-menu {
  flex-direction: column;
  gap: 4px;
}
.elementor-menu-toggle.elementor-active + .elementor-nav-menu--dropdown .elementor-nav-menu a.elementor-item,
.elementor-menu-toggle[aria-expanded="true"] + .elementor-nav-menu--dropdown .elementor-nav-menu a.elementor-item {
  display: block;
  /* 24px horizontal text gutter (was 8px) now that the panel itself
     carries no horizontal padding — keeps the label inset while the
     item box spans the full panel width edge to edge. */
  padding: 14px 24px;
  font-size: 18px;
  color: var(--paper, #F5F7FA) !important;
  border-bottom: 1px solid rgb(245 247 250 / 8%);
}
.elementor-menu-toggle.elementor-active + .elementor-nav-menu--dropdown .elementor-nav-menu a.elementor-item:last-child,
.elementor-menu-toggle[aria-expanded="true"] + .elementor-nav-menu--dropdown .elementor-nav-menu a.elementor-item:last-child {
  border-bottom: none;
}
/* 2026-05-28 — full-bleed selection bar. The mobile dropdown had no
   real selected/hover state (the gray seen on tap was just the iOS
   tap-highlight); paint an explicit subtle paper-tint bar on hover /
   focus / current-page item. With the panel gutter zeroed above, this
   bar spans the panel edge to edge. */
.elementor-menu-toggle.elementor-active + .elementor-nav-menu--dropdown .elementor-nav-menu a.elementor-item:hover,
.elementor-menu-toggle[aria-expanded="true"] + .elementor-nav-menu--dropdown .elementor-nav-menu a.elementor-item:hover,
.elementor-menu-toggle.elementor-active + .elementor-nav-menu--dropdown .elementor-nav-menu a.elementor-item:focus,
.elementor-menu-toggle[aria-expanded="true"] + .elementor-nav-menu--dropdown .elementor-nav-menu a.elementor-item:focus,
.elementor-menu-toggle.elementor-active + .elementor-nav-menu--dropdown .elementor-nav-menu a.elementor-item.elementor-item-active,
.elementor-menu-toggle[aria-expanded="true"] + .elementor-nav-menu--dropdown .elementor-nav-menu a.elementor-item.elementor-item-active {
  background: rgb(245 247 250 / 10%);
}
/* The nav-menu widget needs `position: relative` so the dropdown's
   `position: absolute; top: 100%` anchors to it (not to the page). */
.elementor-widget-nav-menu:has(> .elementor-menu-toggle) {
  position: relative;
}

/* iter-5.6 — cross-product polish round (2026-04-28).
   Four operator-flagged issues observed on /products/trustily/ but
   designed cross-product (any .sw-dark-twin sub-brand product page that
   adopts the same band template):

   (a) About band h2 looks "too narrow" — current 2-col grid (4fr/8fr)
       compresses h2 into ~33% column on desktop; restructure to a single
       stacked flow so the eyebrow + h2 span the band's full inner width
       and the prose follows below at the same width. This reads more
       editorially and removes the awkward 4-line h2 wrap.

   (b) Voice band on mobile has too much vertical air between the eyebrow
       "06 / VOICE" and the curly-quote glyph. The figure has a 24px
       inline margin-top which compounds with section padding. Zero it
       on mobile so the quote starts right after the eyebrow's natural
       leading.

   (c) End-CTA band on mobile has buttons left-aligned by default (no
       explicit alignment); operator wants text left + buttons centered.
       Center the button-row container on mobile while keeping the
       heading + prose at start (left).

   (d) Hero video frame "bleeds" into the next section on mobile — root
       cause is `.sw-video-hero` carrying a 12px-offset hard box-shadow
       that adds visual weight 12px below + right of the frame. On
       mobile that offset reads as the video extending past its own
       border into the section padding, even though no actual layout
       overflow exists. Soften to a subtle drop-shadow on mobile so the
       frame self-closes visually. */

/* (a) about band — stack vertically across viewports */
.sw-dark-twin [data-sw-section="about"] > .e-con-inner,
.sw-dark-twin [data-sw-section="about"].e-con-full > * {
  flex-direction: column !important;
  gap: clamp(24px, 3vw, 32px) !important;
}
.sw-dark-twin [data-sw-section="about"] .e-con-inner > .e-con,
.sw-dark-twin [data-sw-section="about"] > .e-con-inner > .e-child {
  width: 100% !important;
  --width: 100% !important;
}

/* (b) mobile-only adjustment — voice band gap above quote */
@media (max-width: 767px) {
  .sw-dark-twin [data-sw-section="voice"] .sw-pull-quote {
    margin-top: 0 !important;
    padding-top: 8px !important;
  }
}

/* (c) end-cta band left-align rule RETIRED 2026-05-12 — the M-products-redesign
   v4 pattern uses centered end-CTA content (eyebrow + h2 + body + button-row
   all centered). The `.sw-band-centered .elementor-heading-title` rule at EOF
   handles centering. Button-row width-fit + justify-content rules below kept
   in case future cta-band variants still need them. */
.sw-dark-twin [data-sw-section="cta-band"] .e-con:has(> .elementor-widget-button),
.sw-dark-twin [data-sw-section="end-cta"] .e-con:has(> .elementor-widget-button) {
  justify-content: center !important;
}
.sw-dark-twin [data-sw-section="cta-band"] .elementor-widget-button,
.sw-dark-twin [data-sw-section="end-cta"] .elementor-widget-button {
  width: fit-content !important;
}

/* (d) hero video frame — actual root cause of "bleeds into next section"
   identified at v6 (after v3/v4/v5/v5b band-aids). The .sw-video-hero
   has `aspect-ratio: 16/10; width: 100%` which gives it ~149px
   intrinsic height at mobile widths. But its wrapping
   .elementor-widget-html is a flex item in a flex-column parent
   (.sw-hero-video / c1128405), and `flex-basis: auto` does NOT grow
   to absorb the aspect-ratio'd descendant's intrinsic height — so
   the widget stays at ~2px tall while the frame paints itself 149px
   tall on top. Net: the visual frame overflows its layout box by
   ~140px BELOW the container's actual bottom, ending up in / past
   the next section's territory. That's the literal bleed.

   Fix: hoist the aspect-ratio to the outer `.sw-hero-video` container
   (which IS in the flex layout calculation), and have all wrappers +
   the inner frame fill 100% of that properly-sized container. The
   layout box now matches the visual frame; nothing overflows.

   Also keep v5/v5b cosmetic fixes: bg matches hero (--bg / #0A1626)
   so frame doesn't color-collide with pillars soft band, and on
   mobile the ::before strips the lighter blue+rust radial tints to
   keep the frame visually inside the hero dark territory. */
.sw-dark-twin [data-sw-section="hero"] .sw-hero-video {
  aspect-ratio: 16 / 9 !important;
  align-self: center !important;
}
.sw-dark-twin [data-sw-section="hero"] .sw-hero-video > .elementor-widget-html,
.sw-dark-twin [data-sw-section="hero"] .sw-hero-video > .elementor-widget-html > .elementor-widget-container {
  flex: 1 1 100% !important;
  width: 100% !important;
  height: 100% !important;
  display: block !important;
}
.sw-dark-twin [data-sw-section="hero"] .sw-video-hero {
  background: var(--bg, #0A1626) !important;
  width: 100% !important;
  height: 100% !important;
  aspect-ratio: auto !important;
}
@media (max-width: 767px) {
  .sw-dark-twin [data-sw-section="hero"] .sw-video-hero {
    box-shadow: 0 4px 16px rgb(0 0 0 / 28%) !important;
  }
  /* Replace ::before with grid-only (no lighter color tints) so the
     frame matches the hero's pure dark bg on mobile. */
  .sw-dark-twin [data-sw-section="hero"] .sw-video-hero::before {
    background:
      repeating-linear-gradient(0deg, transparent 0 39px, rgb(255 255 255 / 4%) 39px 40px),
      repeating-linear-gradient(90deg, transparent 0 39px, rgb(255 255 255 / 4%) 39px 40px) !important;
  }
}

/* iter-5.11 — byok-grid 3-up cards fill the row (2026-04-28).
   Each card was authored with explicit width:31% (and width_tablet:100%
   for stacking). Three cards × 31% = 93%, plus two 24px gaps eats
   another ~4% — leaving ~38px of unallocated width on the right side
   of the row at desktop. Visually the row reads as narrower than
   surrounding band content (comparison-strip above, etc.).

   Override the cards' explicit width on desktop only (min-width:1025px
   matches Elementor's tablet breakpoint boundary; below that, the
   width_tablet:100% rule keeps the cards stacked). flex: 1 1 0 makes
   the three cards grow equally to fill the row.

   The byok-grid container 65a90184 carries class `byok-grid` (added
   via update-container) as a stable cross-product hook. */
@media (min-width: 1025px) {
  .sw-dark-twin .byok-grid > .e-con {
    flex: 1 1 0 !important;
    width: auto !important;
    --width: auto !important;
  }
}

/* iter-5.10 — pricing tier-featured card vertical centering (2026-04-28).
   SUPERSEDED 2026-04-29 by iter-7 c4 below — operator flagged
   persistent "wrong size" complaint about this card. Iter-5.10 forced
   center alignment which produced ~114px empty space top + bottom on
   the left tier card (562px tall to match the right inclusions card,
   only ~330px of content). New approach: auto-size to content + match
   sibling padding so the left card reads as a compact pricing tile
   beside the inclusions list, top-aligned. */
.sw-dark-twin [data-sw-card="tier-featured"] {
  align-self: center !important;
  padding: 32px 24px !important;
  justify-content: flex-start !important;
}

/* ============================================================
   ITER-5 v8 /products/ TEMPLATE — 8-band standard (2026-04-28)
   Operator-approved mockup landed live on /products/trustily/.
   New bands replace the standalone voice band:
     06 features (soft) — categorized capability surface
     07 faq      (dark) — 6-question stacked Q&A
     08 cta-band (soft) — testimonial nested as inline closer
                          above heading via .sw-pull-quote-inline + hr.sw-rule
   Cross-product: same structure propagates to all dark-twin product pages.
   ============================================================ */

/* iter-5.12 — features band (sw-band-features) — categorized capability surface
   FluentCart-pattern condensed: ~5 sub-categories, each with a 4-column grid
   of minimalist text-only mini-cards. */
.sw-dark-twin .sw-band-features .sw-features-stack {
  display: flex; flex-direction: column; gap: clamp(48px, 6vw, 72px);
  margin-top: 16px;
}
.sw-dark-twin .sw-band-features .feature-cat { display: block; }
.sw-dark-twin .sw-band-features .feature-cat .cat-label {
  font-family: var(--font-mono) !important;
  font-size: 11px !important; letter-spacing: 0.18em !important;
  text-transform: uppercase !important; color: var(--blue-bright) !important;
  font-weight: 500 !important; display: block !important; margin: 0 0 12px !important;
}
.sw-dark-twin .sw-band-features .feature-cat .cat-title {
  font-size: 22px !important; font-weight: 600 !important; line-height: 1.2 !important;
  color: var(--ink) !important; margin: 0 0 24px !important;
}
.sw-dark-twin .sw-band-features .cat-grid {
  display: grid !important; grid-template-columns: repeat(4, minmax(0, 1fr)) !important;
  gap: 16px !important;
}
@media (max-width: 1024px) { .sw-dark-twin .sw-band-features .cat-grid { grid-template-columns: repeat(2, minmax(0, 1fr)) !important; } }
@media (max-width: 560px)  { .sw-dark-twin .sw-band-features .cat-grid { grid-template-columns: 1fr !important; } }
.sw-dark-twin .sw-band-features .mini-feature {
  background: rgb(255 255 255 / 3%) !important;
  border: 1px solid rgb(255 255 255 / 8%) !important;
  border-radius: 4px !important; padding: 16px !important;
  display: flex !important; flex-direction: column !important; gap: 6px !important;
  transition: border-color 0.18s ease, background 0.18s ease;
}
.sw-dark-twin .sw-band-features .mini-feature:hover {
  border-color: rgb(94 168 227 / 35%) !important; background: rgb(255 255 255 / 5%) !important;
}
.sw-dark-twin .sw-band-features .mini-feature h4 {
  font-size: 14px !important; font-weight: 600 !important; line-height: 1.3 !important;
  color: var(--ink) !important; margin: 0 !important;
  letter-spacing: -0.005em !important;
}
.sw-dark-twin .sw-band-features .mini-feature p {
  font-size: 13px !important; line-height: 1.5 !important;
  color: rgb(245 247 250 / 65%) !important; margin: 0 !important;
}
.sw-dark-twin .sw-band-features .mini-feature .badge {
  display: inline-block; align-self: flex-start;
  font-family: var(--font-mono);
  font-size: 9px; letter-spacing: 0.16em; text-transform: uppercase;
  color: #4A9DC8; /* full opacity for WCAG AA contrast (4.5:1 on dark-bg-soft) */
  border: 1px solid rgb(74 157 200 / 55%);
  padding: 2px 6px; margin-left: 6px; border-radius: 3px;
  vertical-align: middle;
}

/* iter-5.13 — faq band (sw-band-faq) — 6-question stacked Q&A
   Two-column layout per item: 70px Q-number column + flex content. */
.sw-dark-twin .sw-band-faq .sw-faq-list {
  display: flex; flex-direction: column; gap: 0; margin-top: 16px;
}
.sw-dark-twin .sw-band-faq .faq-item {
  display: grid !important;
  grid-template-columns: 70px minmax(0, 1fr);
  gap: 24px;
  padding: 32px 0 !important;
  border-top: 1px solid rgb(245 247 250 / 12%);
}
.sw-dark-twin .sw-band-faq .faq-item:first-child {
  border-top: 0 !important; padding-top: 0 !important;
}
.sw-dark-twin .sw-band-faq .faq-item .q-num {
  font-family: var(--font-mono) !important;
  font-size: 13px !important; letter-spacing: 0.18em !important;
  text-transform: uppercase !important; font-weight: 500 !important;
  color: var(--blue-bright) !important;
  padding-top: 6px;
}
.sw-dark-twin .sw-band-faq .faq-item h3 {
  font-size: 22px !important; font-weight: 600 !important; line-height: 1.25 !important;
  color: var(--ink) !important;
  margin: 0 0 16px !important;
}
.sw-dark-twin .sw-band-faq .faq-item .faq-body p {
  font-size: 17px !important; line-height: 1.6 !important;
  color: rgb(245 247 250 / 80%) !important; margin: 0 !important;
}
.sw-dark-twin .sw-band-faq .faq-item .faq-body p strong {
  color: var(--ink) !important; font-weight: 600 !important;
}
.sw-dark-twin .sw-band-faq .faq-item .faq-body p em {
  color: rgb(245 247 250 / 70%) !important;
}
@media (max-width: 640px) {
  .sw-dark-twin .sw-band-faq .faq-item {
    grid-template-columns: 1fr !important; gap: 8px !important;
  }
  .sw-dark-twin .sw-band-faq .faq-item .q-num { padding-top: 0 !important; }
}

/* iter-5.14 — nested pull-quote + hairline divider in cta-band
   Replaces the standalone voice band. Pull-quote sits between the
   "↗ Last word" eyebrow and the "Ready to ship..." h2, separated
   from the h2 by a full-width hairline rule. Visual order:
     eyebrow → pull-quote → hr → h2 → lede → buttons → hr → meta-row */
.sw-dark-twin [data-sw-section="cta-band"] .sw-pull-quote-inline {
  display: flex !important; align-items: flex-start !important;
  gap: 24px !important;
  margin: 24px 0 !important;
  padding: 24px 32px !important;
  border-left: 2px solid rgb(74 157 200 / 65%) !important;
  text-align: left !important;
}
.sw-dark-twin [data-sw-section="cta-band"] .sw-pull-quote-inline .glyph {
  flex-shrink: 0;
  font-size: 64px !important;
  line-height: 0.8 !important; color: rgb(74 157 200 / 75%) !important;
  margin-top: -6px !important;
}
.sw-dark-twin [data-sw-section="cta-band"] .sw-pull-quote-inline .prose {
  flex: 1 1 0 !important; min-width: 0 !important;
}
.sw-dark-twin [data-sw-section="cta-band"] .sw-pull-quote-inline blockquote {
  margin: 0 !important; padding: 0 !important; border: 0 !important;
  color: var(--ink) !important;
  font-size: clamp(19px, 1.9vw, 24px) !important;
  font-weight: 400 !important; line-height: 1.4 !important;
  letter-spacing: -0.005em !important;
}
.sw-dark-twin [data-sw-section="cta-band"] .sw-pull-quote-inline .attrib {
  display: block; margin-top: 16px;
  font-family: var(--font-mono) !important;
  font-size: 11px !important; color: rgb(245 247 250 / 55%) !important;
  text-transform: uppercase !important; letter-spacing: 0.18em !important;
  font-weight: 500 !important;
}
.sw-dark-twin [data-sw-section="cta-band"] hr.sw-rule {
  border: 0 !important; border-top: 1px solid rgb(245 247 250 / 12%) !important;
  margin: 0 0 24px !important; width: 100% !important;
  background: transparent !important;
}

/* iter-5.15 — override iter-5.6(c) text-align:left for v8 cta-band.
   Earlier iter-5.6(c) (lifted out of mobile-only at user request) sets
   cta-band heading + text-editor text-align:left. v8 mockup shows the
   end-cta heading + lede CENTERED. Use :has() to scope this re-centering
   ONLY to cta-bands that have the v8 nested pull-quote (so older
   cta-bands without the pull-quote retain the left-aligned treatment).
   Selector specificity beats iter-5.6(c) by having more compounds. */
.sw-dark-twin [data-sw-section="cta-band"]:has(.sw-pull-quote-inline) .elementor-widget-heading .elementor-heading-title,
.sw-dark-twin [data-sw-section="cta-band"]:has(.sw-pull-quote-inline) .elementor-widget-text-editor,
.sw-dark-twin [data-sw-section="cta-band"]:has(.sw-pull-quote-inline) .elementor-widget-text-editor p {
  text-align: center !important;
}
/* But the pull-quote itself stays left-aligned (its own widget already
   has text-align:left inline in iter-5.14 — listed here as a sanity
   confirmation in case the cascade reorders). */
.sw-dark-twin [data-sw-section="cta-band"] .sw-pull-quote-inline,
.sw-dark-twin [data-sw-section="cta-band"] .sw-pull-quote-inline blockquote {
  text-align: left !important;
}

/* ============================================================
   ITER-6 — DARK-EVERYWHERE COMPLETION OF NON-/products/ SURFACES
   (revamp/p3-iter6-aesthetic — opens 2026-04-28 after iter-5 PR #5 merge)
   Carries the M6 dark-twin aesthetic onto the 7 non-/products/ surfaces +
   header/footer Blocksy content blocks. Per kickoff
   docs/developer/runbooks/p3-iter6-kickoff.md, these are CHROME conversions
   to the dark canvas; band-order restructure to the queued iter-5 mockups
   (e.g. iter5-v2-home.html) is a separate later phase per surface.
   Standing rules (LOCKED iter-3a section padding + LOCKED P3 typography
   baselines) carry forward unchanged — iter-6 sections only flip canvas
   tone + element-level color tokens.
   ============================================================ */


/* === Blog "Reading is half" CTA band — different bg (Item #10) ===
   Operator: "I'd like 'Reading is half' area to have a different
   background color." This band is on /blog/ archive at the bottom.
   Give it bg-soft to break tonal monotony. */

body.blog [data-sw-section="cta-band"],
body.blog .sw-blog-archive-cta,
body.blog [data-sw-role="archive-cta"],
body.blog main > .elementor > .elementor-element:last-of-type {
  background-color: #132338 !important;
}

/* === Blog single image styling (Item #11b) ===
   Photos in body need centering, smaller default size, framed border.
   Hero/opening image stays large. */

body.single-post [data-sw-role="article-body"] img,
body.single-post .elementor-widget-theme-post-content img {
  display: block;
  margin: 32px auto;
  max-width: 100%;
  height: auto;
  border-radius: 4px;
  border: 1px solid rgba(11, 27, 43, 0.12);
}
/* WordPress block-editor image block alignment helpers */
body.single-post [data-sw-role="article-body"] figure,
body.single-post .elementor-widget-theme-post-content figure {
  margin: 36px auto;
  max-width: 100%;
}
body.single-post [data-sw-role="article-body"] figure.wp-block-image,
body.single-post .elementor-widget-theme-post-content figure.wp-block-image {
  text-align: center;
}
body.single-post [data-sw-role="article-body"] figure.wp-block-image img,
body.single-post .elementor-widget-theme-post-content figure.wp-block-image img {
  max-width: 600px;
  margin: 0 auto;
}
body.single-post [data-sw-role="article-body"] figcaption,
body.single-post .elementor-widget-theme-post-content figcaption {
  font-family: var(--font-mono);
  font-size: 11px;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 1.2px;
  color: #5A6675;
  text-align: center;
  margin-top: 12px;
}
/* Featured image (top of post) — larger treatment, not constrained */
body.single-post .post-thumbnail img,
body.single-post .featured-image img,
body.single-post [data-sw-role="featured-image"] img {
  max-width: 100%;
  margin: 0 auto;
  border: none;
  border-radius: 0;
}

/* === Blog single blockquote — match Trustily / case study style (Item #11c) ===
   Operator: "this quote ... I have said often it's messed up and needs
   refactoring to match the style of other quotes." Apply the iter-3a
   pull-quote pattern uniformly. Clean rust hairline left border,
   an italic display companion (retired) italic, generous padding, attribution caption support. */

body.single-post [data-sw-role="article-body"] blockquote,
body.single-post .elementor-widget-theme-post-content blockquote {
  background-color: #F2F4F7 !important;
  border-left: 4px solid var(--ink);
  border-right: none;
  border-top: none;
  border-bottom: none;
  padding: 32px 40px !important;
  margin: 40px 0 !important;
  font-weight: 500;
  font-size: 22px !important;
  line-height: 1.5;
  color: #0B1B2B;
  position: relative;
  border-radius: 0;
}
body.single-post [data-sw-role="article-body"] blockquote p,
body.single-post .elementor-widget-theme-post-content blockquote p {
  font-family: inherit !important;
  font-style: inherit !important;
  font-size: inherit !important;
  line-height: inherit !important;
  color: inherit !important;
  margin: 0 0 12px !important;
}
body.single-post [data-sw-role="article-body"] blockquote p:last-child,
body.single-post .elementor-widget-theme-post-content blockquote p:last-child {
  margin-bottom: 0 !important;
}
body.single-post [data-sw-role="article-body"] blockquote cite,
body.single-post .elementor-widget-theme-post-content blockquote cite,
body.single-post [data-sw-role="article-body"] blockquote footer,
body.single-post .elementor-widget-theme-post-content blockquote footer {
  display: block;
  font-family: var(--font-mono) !important;
  font-style: normal !important;
  font-size: 11px !important;
  font-weight: 500 !important;
  text-transform: uppercase;
  letter-spacing: 1.4px;
  color: #5A6675 !important;
  margin-top: 16px !important;
}

/* === Blog single body text color fix (Item #11d) ===
   Operator: poor color choice somewhere in body. Most likely a
   light-gray or similar that loses contrast on the cream bg. Bump
   any low-contrast body text up to readable. */

body.single-post [data-sw-role="article-body"] strong,
body.single-post .elementor-widget-theme-post-content strong {
  color: #0B1B2B;
  font-weight: 700;
}
body.single-post [data-sw-role="article-body"] em,
body.single-post .elementor-widget-theme-post-content em {
  color: var(--ink);
}
body.single-post [data-sw-role="article-body"] p,
body.single-post .elementor-widget-theme-post-content p,
body.single-post [data-sw-role="article-body"] li,
body.single-post .elementor-widget-theme-post-content li {
  color: #0B1B2B !important;
  font-family: var(--font-sans) !important;
  font-size: 18px !important;
  line-height: 1.65 !important;
}

/* mobile-audit 2026-04-30 — end-of-file padding override.

   The locked horizontal padding rule at line ~707 has the right selector
   list and !important, but on Elementor-rendered surfaces it loses
   the cascade against Elementor's `.e-con { padding-inline-start:
   var(--padding-inline-start); }` chain.

   M2 INVESTIGATION (2026-05-01) — root cause confirmed as PURE
   source-order:
   - Locked rule selector specificity (0,3,1) matches Elementor's effective
     specificity. Both have !important.
   - Tested: switching the locked rule to padding-inline-start/-end
     (logical, matching Elementor's property axis) does NOT win — bands
     still compute to 10px (Elementor default container padding).
   - Tested: injecting an IDENTICAL selector + specificity + !important
     rule via runtime <style> appended LAST in <head> DOES win → 99px.
   - Conclusion: at equal specificity + equal importance, source order
     decides. sytewide-overrides.css loads at #2; Elementor loads at #11.
     Last-wins.
   - Tested @layer: would put our rule in a layer that loses to
     Elementor's unlayered styles in the same cascade level. Doesn't
     help.
   - Outcome: end-of-file override is the canonical workaround. Cannot
     be eliminated without restructuring the stylesheet load order
     (which would risk many other Blocksy / Elementor interactions).

   This block re-asserts the locked horizontal padding rule at the END of
   the file, which puts it later in source order than ALL Elementor CSS
   for the page. Same selector list as the locked rule (line 697-708);
   same specificity. Source-order wins. */
/* F1.3 (2026-05-01) — collapsed via body.sw-locked-surface. Same
   :not(.single-post) exclusion + blog-specific selector pattern as F1.1. */
body.sw-locked-surface:not(.single-post) [data-sw-section],
body.sw-locked-surface:not(.single-post) [data-sw-template],
body.single-post [data-sw-block="blog-single"] {
  padding-left: var(--section-x) !important;
  padding-right: var(--section-x) !important;
}

/* F1.4 (2026-05-01) — collapsed hero re-assertion. The case-study
   .sw-case-hero[data-sw-band="default"] selector is preserved as-is —
   .sw-case-hero is a <section>, not an Elementor [data-sw-section]
   container, so it doesn't fold into the body.sw-locked-surface chain. */
body.sw-locked-surface:not(.single-post) [data-sw-section="hero"],
body.single-sw_case_study .sw-case-hero[data-sw-band="default"],
body.single-post [data-sw-block="blog-single"][data-sw-section="hero"] {
  padding-top: clamp(96px, 12vw, 144px) !important;
  padding-bottom: clamp(96px, 12vw, 144px) !important;
}

/* Case-study hero L/R override — sw-case-hero is a <section>, not an
   Elementor container, so a higher-specificity selector is needed to
   beat any user-agent / theme defaults. */
body.single-sw_case_study .sw-case-hero[data-sw-band="default"],
body.single-sw_case_study .sw-case-hero[data-sw-section="hero"] {
  padding-left: var(--section-x) !important;
  padding-right: var(--section-x) !important;
}

/* mobile-audit followup M0.2 (2026-05-01): operator feedback after PR #11
   close — locked hero floor of 96px pt/pb felt "way too much top/bottom
   space" on iPhone SE. 96+96 = 192px hero chrome consumed ~29% of 667px
   viewport before content. Reduce hero pt/pb to 48px on mobile only.
   Total vertical chrome at 375px: 192px → 96px (-96px / -50%).

   Tablet 640+ unchanged (12vw at 640 = 76.8px above the 48 floor would
   only kick in below 400px, and we're explicitly capping the override at
   640px). Desktop unchanged from prior locked clamp(96-160) ceiling.

   Same selector list as the hero exception re-assertion above so every
   page-id / post-type that opted into the locked clamp also gets the
   mobile override. */
/* F1.5 (2026-05-01) — collapsed via body.sw-locked-surface. */
@media (max-width: 640px) {
  body.sw-locked-surface:not(.single-post) [data-sw-section="hero"],
  body.single-sw_case_study .sw-case-hero[data-sw-band="default"],
  body.single-post [data-sw-block="blog-single"][data-sw-section="hero"],
  body.single-post .sw-blog-hero[data-sw-section="hero"] {
    padding-top: 48px !important;
    padding-bottom: 48px !important;
  }
}

/* mobile-audit followup M0.4 (2026-05-01): operator wants every hero on
   mobile to match /products/sytehero/'s baseline. Audit revealed that
   sytehero, the products family, contact-us, and case-studies all
   render with hero pt/pb 48 + inner .e-con-inner pt/pb 10 (= 58px total
   chrome above first text). But / (home), /about-us/, /services-hub/,
   /audit/, /newsletter/ rendered with section pt/pb 48 + inner .e-con-
   inner pt/pb **120** (= 168px total chrome) because the inner
   container's padding setting was authored for desktop and never
   reduced on mobile. Blog post hero (.sw-blog-hero) had pt/pb:24
   (per-widget Elementor setting) + inner pt/pb:96 (= 120px chrome).

   Unify: on mobile <640px, set the hero section's direct-child
   .e-con-inner pt/pb to 10px on every locked hero surface — matching
   the sytehero reference. Blog post hero gets the same treatment
   plus M0.2's section pt/pb 48 (already extended above to include
   `body.single-post .sw-blog-hero[data-sw-section="hero"]`). Tablet
   640+ unchanged so desktop visual rhythm is preserved.

   Effect on iPhone SE 375vw:
   - Home, about-us, services-hub, audit, newsletter: 168 → 58 (-110px)
   - Blog post: 120 → 58 (-62px)
   - Sytehero, products, contact-us, case-studies: unchanged (already 58)

   Single revert backs out the unification. */
/* mobile-audit followup M0.5 (2026-05-01): "scan ALL containers" pass.
   M0.4 normalized hero inner wrappers to 10/10 sitewide. The same per-widget
   Elementor padding leak shows up across NON-hero bands too:

   Pattern A — sitewide CTA-band template inner pt:120 pb:132 (252 chrome).
     Affects /about-us/, /contact-us/, /shop/, /blog/ archive (different
     copy on each but same template). Same authoring-for-desktop pattern.
   Pattern B — "scene-break" inners pt:80-96 with section markers
     (testimonial, methodology, operator-story, agency-pillars, products,
     expect, etc.). On mobile, these double the section padding from
     locked clamp(64-96).
   Pattern C — Footer wrapper inner (Blocksy Content Block post-122845
     `.elementor-element-503a593 > .e-con-inner`) at pt:72 pb:32. Sitewide
     because it's the universal footer template.
   Pattern D — Legal pages (privacy, terms, cookies, refund, eula, AUP)
     have an outer .e-con.e-parent with pt:80 pb:80 above breadcrumb +
     page title. 160px chrome on a "Home / Privacy Policy" line is
     excessive on iPhone SE.

   Strategy: ONE rule widens every named-band inner (covers Patterns A+B
   + replaces M0.4's hero-only filter with the unified version), plus
   targeted rules for footer wrapper (Pattern C) and the 6 legal page
   IDs (Pattern D). Tablet+ unchanged across all four. */
@media (max-width: 640px) {
  /* Pattern A + B + replaces M0.4 hero rule above as the canonical
     baseline for every named SyteWide band's inner Elementor wrapper. */
  [data-sw-section] .e-con-inner {
    padding-top: 10px !important;
    padding-bottom: 10px !important;
  }

  /* Pattern C — footer Blocksy Content Block wrapper.
     Tighten 72/32 to a small comfortable gutter above SyteWide brand
     block on mobile. Tablet/desktop padding unchanged. */
  .elementor-element-503a593 > .e-con-inner {
    padding-top: 32px !important;
    padding-bottom: 16px !important;
  }

  /* Pattern D — legal + commerce pages with breadcrumb-style outer
     .e-con.e-parent rendering pt:80 pb:80 on mobile. Per-element
     Elementor padding has specificity ≥ (0, 6, 0) so the rule needs
     `body.elementor-page.page-id-XXX` chained = (0, 6, 1) to win.

     Legal: 121522 privacy, 121540 terms, 121546 acceptable-use,
            121554 refund, 121561 cookie, 121710 eula.
     Commerce: 118231 shop, 118232 account, 118233 cart, 118234 receipt,
               118235 checkout, 121473 ticketing-portal.

     Tighten 80/80 → 32/24 + zero inner-wrapper padding so breadcrumb +
     page title sit ~58px from header. Same baseline as M0.4 hero. */
  body.elementor-page.page-id-121522 .elementor-element.e-con.e-con-boxed.e-parent,
  body.elementor-page.page-id-121540 .elementor-element.e-con.e-con-boxed.e-parent,
  body.elementor-page.page-id-121546 .elementor-element.e-con.e-con-boxed.e-parent,
  body.elementor-page.page-id-121554 .elementor-element.e-con.e-con-boxed.e-parent,
  body.elementor-page.page-id-121561 .elementor-element.e-con.e-con-boxed.e-parent,
  body.elementor-page.page-id-121710 .elementor-element.e-con.e-con-boxed.e-parent,
  body.elementor-page.page-id-118231 .elementor-element.e-con.e-con-boxed.e-parent,
  body.elementor-page.page-id-118232 .elementor-element.e-con.e-con-boxed.e-parent,
  body.elementor-page.page-id-118233 .elementor-element.e-con.e-con-boxed.e-parent,
  body.elementor-page.page-id-118234 .elementor-element.e-con.e-con-boxed.e-parent,
  body.elementor-page.page-id-118235 .elementor-element.e-con.e-con-boxed.e-parent,
  body.elementor-page.page-id-121473 .elementor-element.e-con.e-con-boxed.e-parent {
    padding-top: 32px !important;
    padding-bottom: 24px !important;
  }
  body.elementor-page.page-id-121522 .e-con-inner,
  body.elementor-page.page-id-121540 .e-con-inner,
  body.elementor-page.page-id-121546 .e-con-inner,
  body.elementor-page.page-id-121554 .e-con-inner,
  body.elementor-page.page-id-121561 .e-con-inner,
  body.elementor-page.page-id-121710 .e-con-inner,
  body.elementor-page.page-id-118231 .e-con-inner,
  body.elementor-page.page-id-118232 .e-con-inner,
  body.elementor-page.page-id-118233 .e-con-inner,
  body.elementor-page.page-id-118234 .e-con-inner,
  body.elementor-page.page-id-118235 .e-con-inner,
  body.elementor-page.page-id-121473 .e-con-inner {
    padding-top: 10px !important;
    padding-bottom: 10px !important;
  }
}
/* 2026-05-22 — operator: hide the empty .ct-product-gallery container
   that FluentCart's global widget template injects between the paper
   hero panel and the body content on every legal page. Eats ~101px
   (32 padding + 45 empty inner + 24 padding) of vertical space for no
   visible content. Scoped to the 6 legal page IDs so real commerce
   surfaces (where the gallery IS legitimate) stay intact. All
   viewports — the wasted space is wasted at any size. */
body.elementor-page.page-id-121522 .e-con.e-parent:has(.ct-product-gallery),
body.elementor-page.page-id-121540 .e-con.e-parent:has(.ct-product-gallery),
body.elementor-page.page-id-121546 .e-con.e-parent:has(.ct-product-gallery),
body.elementor-page.page-id-121554 .e-con.e-parent:has(.ct-product-gallery),
body.elementor-page.page-id-121561 .e-con.e-parent:has(.ct-product-gallery),
body.elementor-page.page-id-121710 .e-con.e-parent:has(.ct-product-gallery) {
  display: none !important;
}
/* 2026-05-22 — operator: legal-page body content section carries
   `margin-top: 200px !important` from a per-element Elementor setting.
   On mobile that's a third of the viewport of empty space; on desktop
   it leaves a stark navy gap between the paper hero panel and the
   prose. Zero the outer-container margins sitewide so body content
   sits ~56px below the paper hero (paper pb:24 + body pt:32 = clean
   natural breathing). */
body.elementor-page.page-id-121522 .elementor-element.e-con.e-parent,
body.elementor-page.page-id-121540 .elementor-element.e-con.e-parent,
body.elementor-page.page-id-121546 .elementor-element.e-con.e-parent,
body.elementor-page.page-id-121554 .elementor-element.e-con.e-parent,
body.elementor-page.page-id-121561 .elementor-element.e-con.e-parent,
body.elementor-page.page-id-121710 .elementor-element.e-con.e-parent {
  margin-top: 0 !important;
  margin-bottom: 0 !important;
}

/* mobile-audit followup M0.3 (2026-05-01): two non-hero bands on / (home)
   exceeded the locked non-hero clamp(64px, 8vw, 96px) on mobile because of
   per-widget Elementor padding overrides authored for desktop visual
   rhythm:

   - pillars-intro band (.elementor-element-cf0613b at L4636): pb floor of
     120px on mobile (iter-7 c2-fu intentional desktop breathing room).
     120 ÷ locked 96 ceiling = 25% over spec.
   - end-cta band (#sw-cta-band): pt:80 / pb:88 baked into Elementor widget
     settings. 168 total vs locked 128 = 31% over spec.

   Mobile-only override brings both to the locked clamp floor (64px). Tablet
   640+ unchanged so c2-fu desktop intent is preserved. This sits below the
   M0.2 hero override so a single revert backs out both. */
@media (max-width: 640px) {
  body.page-id-700 [data-sw-section="pillars-intro"],
  body.page-id-700 .sw-band-pillars-intro,
  body.page-id-700 .elementor-element-cf0613b {
    padding-bottom: 64px !important;
  }
  body.page-id-700 [data-sw-section="end-cta"],
  body.page-id-700 .sw-band-end-cta,
  body.page-id-700 #sw-cta-band {
    padding-top: 64px !important;
    padding-bottom: 64px !important;
  }
}

/* mobile-audit followup M-007 (2026-05-01): h2 single-word break on
   narrow mobile when a 36px word like "conversation" exceeds the
   constrained 227px content area inside nested Elementor e-con-boxed
   containers (those add 48px L+R inner padding on top of the locked
   horizontal section padding). Default overflow-wrap: break-word
   produces "conversatio" + "n." (mid-word, last char orphaned).

   Investigation: overflow-wrap: anywhere alone produces the same
   line-breaks as break-word for layout (they differ only in
   min-content sizing). To force a real word-boundary break we add
   hyphens: auto — the html lang="en-US" attribute is set site-wide
   so the browser can hyphenate gracefully ("conversa-" + "tion.").

   Scope: locked surfaces only (same selector list as the locked
   horizontal padding rule), mobile <640px. Tablet+ unchanged because
   content area widens to fit "conversation" at 36px without
   breaking. */
/* F1.6 (2026-05-01) — collapsed via body.sw-locked-surface. */
@media (max-width: 640px) {
  body.sw-locked-surface h2 {
    overflow-wrap: anywhere;
    word-break: normal;
    hyphens: auto;
    -webkit-hyphens: auto;
  }
}

/* mobile-audit followup M3 — body paragraph width on blog posts
   (2026-05-01).

   Problem: blog post body paragraphs render at 254.5px in 375vw
   (~68% width) because of stacked Elementor widget paddings inside
   the post-content widget:
     - locked section padding: 26.25 L+R  (375 → 322.5)  ← keep
     - post-content widget padding: 24 L+R (322.5 → 274.5)
     - inner e-flex e-con-boxed padding: 10 L+R (274.5 → 254.5)
   Industry mobile-reading floor is 16-24px each side. We're at
   60.25 each side currently.

   Fix (CSS-only): zero the post-content-widget's padding AND the
   nested e-con-boxed padding ONLY on mobile. Section padding stays at
   the locked clamp(20, 7vw, 120) → 26.25 each side at 375 — comfortably
   above the 24 reading-margin floor. Recovers ~68px → paragraph width
   ~322.5px (86% of 375).

   Scoped to body.single-post so / (home), products, case-studies,
   p3-marketing surfaces are unaffected. Tablet+ unchanged so the
   24 L+R post-content-widget padding still gives a comfortable
   reading column on iPad+. */
@media (max-width: 640px) {
  body.single-post .elementor-widget-theme-post-content {
    padding-left: 0 !important;
    padding-right: 0 !important;
  }
  body.single-post .elementor-widget-theme-post-content .e-con.e-con-boxed,
  body.single-post .elementor-widget-theme-post-content .e-flex.e-con-boxed {
    padding-left: 0 !important;
    padding-right: 0 !important;
  }
}

/* mobile-audit followup F0 (2026-05-01) — operator-priority: restore
   non-hero band breathing after PR #11 close + mobile-audit landing.

   Problem (operator-flagged 2026-05-01): M0.5's mobile-only
   `[data-sw-section] .e-con-inner { padding: 10 }` covered heros AND
   non-hero bands sitewide. Hero baseline (sytehero 10/10) is
   operator-validated and stays. Non-hero bands feel squished:

   Mobile 375 (M0.5 effect):  case-card inner 10/10  → eyebrow flush
                              testimonial inner 10/10 → flush
                              walkthrough inner 0/0  → no breathing
   Desktop 1440 (Elementor per-element):
                              case-card inner 32/32  → flush per operator
                              testimonial inner 80/80 → comfortable
                              pillars-intro inner 64/64 → comfortable
                              end-cta inner null (outer 120) → comfortable

   Fix (split M0.5 effect, bump only the tight non-hero bands):
   - Mobile: bump direct-child inner of non-hero `[data-sw-section]`
     to 32px. Direct-child only (`>`) so nested `.e-con-inner` (cards
     inside case-card, sub-cards inside testimonial) keep M0.5's 10px.
     Hero bands keep M0.5's 10/10 (sytehero baseline preserved).
   - Desktop: bump case-card direct-child inner to 64px. ONLY case-card
     (the operator-flagged band). testimonial / pillars-intro / end-cta
     unchanged — they're already comfortable. If operator flags more
     bands later, add per-band overrides here.

   Specificity (2026-05-01 review fix — added body.sw-locked-surface
   scope to keep F0 in the same architectural family as F1's locked
   rule. Without the scope, an unrelated future Elementor page using
   `data-sw-section="case-card"` would inherit 64px desktop inner
   unintentionally — anti-pattern):
   - Mobile selector `body.sw-locked-surface [data-sw-section]:not(
     [data-sw-section="hero"]):not([data-sw-template="hero-light"])
     > .e-con-inner` = (0,5,0). Wins M0.5's (0,2,0).
   - Desktop selector `body.sw-locked-surface [data-sw-section=
     "case-card"] > .e-con-inner` = (0,3,0). Confirmed wins Elementor
     per-element rule on case-card inner via runtime test 2026-05-01.

   Block-per-milestone pattern preserved (separate block from M0.5).
   See findings.md M-027 for diagnosis + decision log. */
@media (max-width: 640px) {
  /* F0 baseline reconsidered 2026-05-11: 96 → 72. The 2026-05-01 iteration
     cadence (32→48→64→80→96) was tuned for the retired iter-4+ dark-canvas
     design language. Operator visual review post-2026-05-07 revamp judged
     it too generous. 72 matches the M4-fix-6h values that home + services-
     hub pillars overrides already used. Selector (0,5,0). */
  /* Phase F (2026-05-14): dropped `:not([data-sw-template="hero-light"])`
     exclusion. hero-light attribute fully retired sitewide post-Phase-3
     hero consolidation; the :not() was defensive dead code. */
  body.sw-locked-surface [data-sw-section]:not([data-sw-section="hero"]) > .e-con-inner {
    padding-top: 72px !important;
    padding-bottom: 72px !important;
  }
}

@media (min-width: 641px) {
  /* F0 baseline reconsidered 2026-05-11: 192 → 120. Matches the M4-fix-6h
     values home + services-hub pillars overrides already used. Removing
     the override is folded into this commit (was redundant after the
     baseline drop). Iteration cadence 64→96→120→160→192 was retired
     dark-canvas era; 120 is the new sitewide non-hero inner-chrome floor.
     Phase F (2026-05-14): dropped `:not([data-sw-template="hero-light"])`
     exclusion same as the mobile branch above. */
  body.sw-locked-surface [data-sw-section]:not([data-sw-section="hero"]) > .e-con-inner {
    padding-top: 120px !important;
    padding-bottom: 120px !important;
  }
}

/* =========================================================================
   POST-LAUNCH M1 — /products/ archive (page 123641) — net-new selectors
   =========================================================================
   The curated /products/ archive (post-launch M1, 2026-05-07) introduces
   bands beyond the 8-band /products/<product>/ v8 standard:
     • single-column centered hero with chip-row meta (hero variants)
     • 3-up product cards grid (.products-grid + .product-card)
     • methodology timeline (reuses iter-5.4 .timeline pattern — no new CSS)
     • about + cta-band (reuse existing iter-5.x selectors — no new CSS)

   Selectors scoped to body.page-id-123641 to avoid leaking to other
   surfaces. Pattern matches earlier per-page scoping in this file
   (e.g., body.page-id-123633 services-hub blocks).
   ========================================================================= */

/* Hero: single-column centered (vs. 2-col split-row of /products/<product>/).
   2026-05-22 (normalize-against-services-hub): widened 64ch → 1238px so
   the hero h1 + lede have the same canvas room services-hub gives them
   (services-hub hero widgets shrink-to-content within sw-band-centered;
   /products/ uses HTML widgets so we widen the container instead). */
body.page-id-123641 .hero-content {
  /* Updated 2026-05-27: 1238 → var(--width-canvas) (= 1290). +52px desktop
     gain. See widths.md (canvas tier). */
  max-width: var(--width-canvas);
  margin: 0 auto;
  text-align: center;
}
body.page-id-123641 .hero-content h1.hero-h1,
body.page-id-123641 .hero-content p.lede {
  margin-left: auto;
  margin-right: auto;
}
body.page-id-123641 .hero-content .cta-row {
  justify-content: center;
}
/* 2026-05-14 (hero-consolidation Phase 5): retired .hero-meta-row + .chip-strong
   + .chip-product + .sep blocks. The chip-row widget was removed from /products/
   hero authoring in Phase 2 (operator called it a "weird band"). Classes are
   absent from the rendered DOM as of 2026-05-14; rules were dead-code.
   (The sitewide .chip-product references at lines ~5020/5033/5050 are
   defensive selector-list entries inside broader inline-emphasis rules — kept
   because the rules also target other live classes.) */

/* Products grid — 3-up portfolio cards (band 02) */
body.page-id-123641 .products-grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  /* 8 explicit row tracks shared across all .product-card children via subgrid:
     mark / eyebrow / h3 / tagline / video / features (1fr absorbs slack) / pricing / cta.
     Each row auto-sizes to the tallest child at that position, so any one
     element growing in length pushes the SAME row taller across all 3 cards,
     keeping the cards visually aligned regardless of copy length.
     (Brand mark row added 2026-05-26 — see .sw-card-mark below.) */
  grid-template-rows: auto auto auto auto auto 1fr auto auto;
  gap: 32px;
}
@media (max-width: 980px) {
  body.page-id-123641 .products-grid {
    grid-template-columns: 1fr;
    gap: 24px;
  }
}
body.page-id-123641 .product-card {
  background: #0A1626;
  border: 1.5px solid rgb(255 255 255 / 16%);
  border-radius: 0;
  box-shadow: 8px 8px 0 rgb(74 157 200 / 18%);
  padding: 48px 32px;
  display: grid;
  grid-template-rows: subgrid;
  grid-row: span 8;
  gap: 16px;
  transition: transform 0.18s ease, border-color 0.18s ease, box-shadow 0.18s ease;
}
/* Brand mark (2026-05-26; centered identity-first masthead 2026-05-27).
   mark-on-dark variant (cards render on #0A1626).
   Row reassignment: eyebrow (.num) takes subgrid row 1, mark takes row 2 —
   pure-CSS swap of the first two subgrid rows so the eyebrow orients the
   reader before the brand mark hits. HTML/JSON unchanged. The remaining
   subgrid rows (h3 / tagline / video / features / price / cta) keep their
   default DOM order in rows 3-8.
   Mark bumped 40 -> 96px to match the .sw-hero-logo .mark baseline and
   give real visual weight; centered inside its subgrid cell.
   Top 4 rows (num / mark / h3 / tagline) center-aligned to read as a
   masthead; bottom 4 rows (video / ul / price / cta) keep current alignment
   because the `↳` feature list specifically reads broken when centered. */
body.page-id-123641 .product-card .num       { grid-row: 1; }
body.page-id-123641 .product-card .sw-card-mark { grid-row: 2; }
body.page-id-123641 .product-card .sw-card-mark {
  width: 96px;
  height: 96px;
  display: block;
  margin: 0 auto;
}
body.page-id-123641 .product-card .num,
body.page-id-123641 .product-card h3,
body.page-id-123641 .product-card .tagline {
  text-align: center;
}
body.page-id-123641 .product-card:hover {
  border-color: rgb(94 168 227 / 50%);
  transform: translate(-2px, -2px);
  box-shadow: 10px 10px 0 rgb(94 168 227 / 28%);
}
/* Phase E (2026-05-14): aligned to .sw-tile-num detail-page eyebrow
   (was 11px / 0.18em → now 12px / 0.14em to match). */
body.page-id-123641 .product-card .num {
  font-family: var(--font-mono, "JetBrains Mono", monospace);
  font-size: 12px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: #8FC9F7;
  font-weight: 500;
}
body.page-id-123641 .product-card h3 {
  /* 2026-05-22 — was 28px (too heavy for 3-up grid in a single section);
     dropped to design-system h3 clamp(22-26) so card heads sit a tier
     under the section h2. !important needed to beat Blocksy default
     --size-h3 clamp(26-32) which was tying on specificity and winning
     by source order. */
  font-size: clamp(22px, 2vw, 26px) !important;
  font-weight: 600 !important;
  line-height: 1.15 !important;
  color: #F5F7FA;
  margin: 0;
  letter-spacing: -0.01em;
}
body.page-id-123641 .product-card .tagline {
  /* 2026-05-22 — bumped 16→17 / 1.5→1.6 so the card lede reads as
     body-tier instead of meta-tier. */
  font-weight: 400;
  font-size: 17px;
  color: rgb(245 247 250 / 86%);
  line-height: 1.6;
  margin: 8px 0 20px;
  /* min-height: 3em retired 2026-05-16 in favor of the parent grid's
     subgrid row alignment — the tagline row now sizes to the tallest
     tagline across all cards automatically, with no upper bound to break
     when a future tagline wraps to 3+ lines. */
  /* 2026-05-14 (operator feedback): retired the dashed border-bottom +
     24px padding-bottom — it was sitting too close to the .sw-tile ul
     border-top below, reading as a double-border. The new
     .product-card-video placeholder below sits between tagline and ul
     and provides the visual break. */
}

/* 2026-05-14: video placeholder inside each /products/ archive card.
   "Coming soon" intro video — detail pages will eventually carry a
   real Gumlet video; the archive cards use the same slot as a
   placeholder until video content is available. */
body.page-id-123641 .product-card .product-card-video {
  position: relative;
  aspect-ratio: 16 / 9;
  background: linear-gradient(135deg, rgb(74 157 200 / 10%), rgb(143 201 247 / 4%));
  border: 1px dashed rgb(245 247 250 / 16%);
  border-radius: 4px;
  margin: 0 0 24px;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
}
body.page-id-123641 .product-card .product-card-video .play-btn {
  width: 44px;
  height: 44px;
  border-radius: 50%;
  background: rgb(245 247 250 / 8%);
  border: 1px solid rgb(245 247 250 / 22%);
  position: relative;
  transition: background 0.18s ease, border-color 0.18s ease;
}
body.page-id-123641 .product-card .product-card-video .play-btn::after {
  content: "";
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-30%, -50%);
  border-left: 11px solid rgb(245 247 250 / 70%);
  border-top: 7px solid transparent;
  border-bottom: 7px solid transparent;
}
body.page-id-123641 .product-card .product-card-video .video-tag {
  position: absolute;
  top: 10px;
  left: 10px;
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: rgb(245 247 250 / 55%);
  background: rgb(0 0 0 / 28%);
  padding: 4px 8px;
  border-radius: 2px;
}
body.page-id-123641 .product-card:hover .product-card-video .play-btn {
  background: rgb(245 247 250 / 14%);
  border-color: rgb(143 201 247 / 50%);
}
body.page-id-123641 .product-card ul {
  margin: 0;
  padding-left: 0;
  list-style: none;
  font-family: var(--font-sans, "Inter", system-ui, sans-serif);
  /* 2026-05-22 — bumped 13→15 to clear the body-text floor while
     keeping cards compact (full 16/18 would crowd the card chrome). */
  font-size: 15px;
  line-height: 1.55;
  color: rgb(245 247 250 / 70%);
  display: flex;
  flex-direction: column;
  gap: 12px;
}
body.page-id-123641 .product-card ul li {
  padding-left: 18px;
  position: relative;
}
body.page-id-123641 .product-card ul li::before {
  content: "↳";
  position: absolute;
  left: 0;
  top: 0;
  color: #8FC9F7;
  font-family: var(--font-mono, "JetBrains Mono", monospace);
}
body.page-id-123641 .product-card .price-strip {
  margin-top: auto;
  padding-top: 32px;
  border-top: 1px solid rgb(245 247 250 / 12%);
  display: flex;
  flex-direction: column;
  gap: 8px;
}
body.page-id-123641 .product-card .price-strip .price-label {
  font-family: var(--font-mono, "JetBrains Mono", monospace);
  /* 2026-05-22 — bumped 10→12 to clear the locked caps minimum. */
  font-size: 12px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: #8B9AAE;
  font-weight: 500;
}
body.page-id-123641 .product-card .price-strip .price {
  font-family: var(--font-sans, "Inter", system-ui, sans-serif);
  font-size: 18px;
  font-weight: 600;
  color: #F5F7FA;
  line-height: 1.3;
  letter-spacing: -0.005em;
}
body.page-id-123641 .product-card .price-strip .price em {
  font-weight: 400;
  color: #4A9DC8;
}
body.page-id-123641 .product-card .card-cta-row {
  margin-top: 24px;
  display: flex;
  gap: 16px;
  flex-wrap: wrap;
}
body.page-id-123641 .product-card .cta--small {
  /* 2026-05-22 — bumped 13→14 button label; kept compact padding so
     two-up CTAs still fit comfortably in the card. */
  padding: 12px 18px;
  font-size: 14px;
}

/* Typography + CTAs scoped to page-id-123641 — mirrors approved mockup
   inline CSS. Prefixed-selector pattern avoids leaking to other pages. */
/* 2026-05-22 (normalize-against-services-hub): removed `max-width: 18ch /
   22ch` caps on .hero-h1 / .section-h2 / .cta-h2. The HTML-widget hero +
   section heads were rendering 900px (after the L5121/L5131 Phase 6F
   override) or 522px (.cta-h2 22ch) — visibly narrower than services-hub
   where the matching Elementor heading widgets fill the full 1238px
   canvas. Letting these headlines flow to canvas width matches the
   services-hub standard and reads as one visual family across the
   marketing tier. */
body.page-id-123641 h1.hero-h1 {
  font-size: clamp(22px, 3.5vw, 40px);
  font-weight: 600;
  line-height: 1.05;
  letter-spacing: -0.012em;
  margin: 0 0 32px;
  color: #F5F7FA;
}
body.page-id-123641 h2.section-h2 {
  font-size: clamp(28px, 3vw, 36px);
  font-weight: 600;
  line-height: 1.15;
  margin: 0 0 24px;
  color: #F5F7FA;
}
/* Phase 6F (2026-05-14): tightened from clamp(36,4.5vw,56) → clamp(28,3vw,36)
   to match detail-page h2 scaling exactly (per operator request — /products/
   archive should harmonise with /products/<product>/ pages). !important is
   needed to beat Blocksy's --size-h2 default (clamps to 52 ceiling). */
body.page-id-123641 h2.cta-h2 {
  font-size: clamp(28px, 3vw, 36px) !important;
  font-weight: 600 !important;
  line-height: 1.15 !important;
  margin: 0 0 32px;
  color: #F5F7FA;
}
/* 2026-05-22 (normalize-against-services-hub): widened p.lede 60ch → 727px
   to match the services-hub hero lede width, and pinned line-height with
   !important so it beats the sitewide L4004 baseline. Background: services-
   hub hero lede ships from an Elementor text-editor widget whose typography
   compiles at higher specificity than the sitewide `body.wp-theme-blocksy
   p { line-height: 1.5 }` baseline; /products/ uses HTML widgets so the
   page-scoped rule (0,1,1) loses to that sitewide (0,2,1) without
   !important. */
body.page-id-123641 p.lede {
  font-size: 18px;
  line-height: 1.65 !important;
  color: rgb(245 247 250 / 78%);
  /* Updated 2026-05-27: 727 → var(--width-prose) (= 720). −7px (imperceptible).
     The 727 was a one-off; sitewide convention is 720. See widths.md. */
  max-width: var(--width-prose);
  margin: 0 0 48px;
}
body.page-id-123641 p {
  font-size: 18px;
  line-height: 1.65 !important;
  color: rgb(245 247 250 / 82%);
}
/* 2026-05-22 (operator follow-up): re-align /products/ archive eyebrows
   to the services-hub treatment — mono-caps 11px / 500 / 0.18em tracked
   / paper-on-dark. Matches the sitewide `.sw-chapter-ebrow` baseline
   used on about-us / services-hub / blog-archive so all marketing-tier
   surfaces read as a single family. */
body.page-id-123641 .sw-chapter-ebrow {
  font-family: var(--font-mono) !important;
  font-size: 11px !important;
  font-weight: 500 !important;
  letter-spacing: 0.18em !important;
  text-transform: uppercase !important;
  color: var(--ink) !important;
  margin: 0 0 16px;
}
/* The page-id-123641 p rule above sets 18px Inter on every p — including the
   hero stat-row p. The sitewide .sw-hero-stat-row class wants 13px JetBrains
   Mono. Higher-specificity override here restores the mono treatment so the
   archive's hero stat-row matches detail-page hero stat-row typography. */
body.page-id-123641 p.sw-hero-stat-row,
body.page-id-123641 p.sw-hero-stat-row--lg {
  font-family: var(--font-mono) !important;
  font-size: 13px !important;
  font-weight: 500 !important;
  letter-spacing: 0.04em !important;
  text-transform: uppercase !important;
  line-height: 1.4 !important;
  color: rgb(245 247 250 / 90%) !important;
}
/* 2026-05-22 — match the services-hub Elementor button treatment:
   deep blue (#1F5F85) fill + paper text on primary, transparent +
   paper outline on secondary. Sizing matches the sitewide CTA
   standard (16px / 20x40 / 6px radius). */
body.page-id-123641 .cta {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  padding: 20px 40px;
  font-family: var(--font-sans, "Inter", system-ui, sans-serif);
  font-size: 16px;
  font-weight: 600;
  letter-spacing: 0.02em;
  border-radius: 6px;
  text-decoration: none;
  transition: transform 0.18s ease, background 0.18s ease, border-color 0.18s ease;
  cursor: pointer;
  border: 1.5px solid transparent;
}
body.page-id-123641 .cta--primary {
  background: #1F5F85;
  color: #FAFAF7;
}
body.page-id-123641 .cta--primary:hover {
  background: #4A9DC8;
}
body.page-id-123641 .cta--secondary {
  background: transparent;
  color: #F5F7FA;
  border-color: rgb(245 247 250 / 35%);
}
body.page-id-123641 .cta--secondary:hover {
  background: rgb(245 247 250 / 8%);
  border-color: #F5F7FA;
}
body.page-id-123641 .cta-row {
  display: flex;
  gap: 16px;
  flex-wrap: wrap;
}
/* 2026-05-22 — mobile only: hide BOTH stat-rows on /products/ archive
   hero. The page authored the bullets as inline `&middot;` characters
   (vs the .sw-hero-stat-row span pattern used on home/services-hub),
   so my sitewide flex-column-stack fix can't reach them — they wrap as
   2-line awkward stat-row instead. Combined with the dense h1 + lede +
   2 CTAs already filling the 92dvh budget, the scroll triangle ends up
   overlapping the stat-row text. Drop both stat-rows on phones; the
   tablet/desktop layout (where they have room) keeps both. */
@media (max-width: 640px) {
  body.page-id-123641 [data-sw-section="hero"] .sw-hero-stat-row,
  body.page-id-123641 [data-sw-section="hero"] .sw-hero-stat-row--lg {
    display: none !important;
  }
}

/* Methodology timeline — band 03 */
body.page-id-123641 .methodology-intro,
body.page-id-123641 .products-intro {
  max-width: 60ch;
  margin-bottom: 48px;
}
body.page-id-123641 .methodology-intro .lede,
body.page-id-123641 .products-intro .lede {
  margin-bottom: 0;
}
body.page-id-123641 .timeline {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 0;
  /* 2026-05-14: was margin-top: 48px — too tight against the methodology
     lede paragraph above. Bumped to 80px so the timeline reads as its
     own visual beat below the intro copy. */
  margin-top: 80px;
  position: relative;
}
body.page-id-123641 .timeline::before {
  content: "";
  position: absolute;
  /* 2026-05-14 (v3): after bumping .step padding-top 24 → 40 for
     breathing room above the circles, the circles shifted from y=5
     to ~y=21 inside the timeline (+16). Circle center ≈ y=39. Line
     top adjusted to 38 to stay centered. z-index: 0 stays beneath
     the circle's z-index: 2 so the line is hidden inside the filled
     badge and visible only between circles. */
  top: 38px;
  left: calc(100% / 6);
  right: calc(100% / 6);
  height: 1.5px;
  background: linear-gradient(90deg, transparent 0, #5EA8E3 10%, #5EA8E3 90%, transparent 100%);
  z-index: 0;
}
@media (max-width: 880px) {
  body.page-id-123641 .timeline {
    grid-template-columns: 1fr;
    gap: 48px;
  }
  body.page-id-123641 .timeline::before {
    display: none;
  }
}
body.page-id-123641 .step {
  position: relative;
  padding: 0 24px;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 16px;
}
body.page-id-123641 .step .ord-mark {
  width: 18px;
  height: 18px;
  border-radius: 999px;
  background: #5EA8E3;
  border: 4px solid #0A1626;
  box-shadow: 0 0 0 2px #5EA8E3;
  margin-bottom: 24px;
  position: relative;
  z-index: 2;
}
body.page-id-123641 .step .ord-bg {
  font-family: var(--font-mono, "JetBrains Mono", monospace);
  font-size: 96px;
  font-weight: 500;
  line-height: 1;
  color: rgb(94 168 227 / 14%);
  letter-spacing: -0.04em;
  position: absolute;
  top: -20px;
  left: 24px;
  z-index: 0;
}
body.page-id-123641 .step h3 {
  /* 2026-05-22 — was 20px but Blocksy --size-h3 clamp(26-32) was
     winning at 32 (specificity tie, source order). !important pins it
     to the 22px step-card spec that matches services-hub's
     .sw-mini-method method-step h4. */
  font-size: 22px !important;
  font-weight: 600;
  color: #F5F7FA;
  line-height: 1.2;
  margin: 0;
  max-width: 22ch;
  position: relative;
  z-index: 2;
}
body.page-id-123641 .step p {
  /* 2026-05-22 — bumped 15→16 to clear body floor. */
  font-size: 16px;
  line-height: 1.55;
  color: rgb(245 247 250 / 78%);
  max-width: 32ch;
  position: relative;
  z-index: 2;
  margin: 0;
}
body.page-id-123641 .step .step-tag {
  font-family: var(--font-mono, "JetBrains Mono", monospace);
  /* 2026-05-22 — bumped 10→12 to clear caps floor. */
  font-size: 12px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: #8FC9F7;
  margin-top: 16px;
  position: relative;
  z-index: 2;
}

/* About band — 2-col grid (band 04). !important on display/grid-template
   because something downstream (likely Elementor's compiled per-page CSS)
   was forcing display:block + grid-template-columns:1fr at desktop. */
body.page-id-123641 .about-inner {
  display: grid !important;
  grid-template-columns: minmax(0, 4fr) minmax(0, 8fr) !important;
  gap: 96px !important;
  align-items: start;
}
@media (max-width: 880px) {
  body.page-id-123641 .about-inner {
    grid-template-columns: 1fr !important;
    gap: 32px !important;
  }
}
body.page-id-123641 .about-eyebrow-col h2 {
  /* 2026-05-22 — !important so the design-system h2 clamp wins against
     Blocksy's default `--size-h2` clamp(36, 4.5vw, 52). Without it the
     about-section h2 rendered at 52px (default ceiling) while other
     section h2s land at 36px — visually inconsistent across the page. */
  font-size: clamp(28px, 3vw, 36px) !important;
  font-weight: 600 !important;
  line-height: 1.15 !important;
  color: #F5F7FA;
  margin: 0;
}
body.page-id-123641 .about-eyebrow-col h2 em {
  color: #4A9DC8;
  font-weight: 400;
}
body.page-id-123641 .about-prose p {
  /* 2026-05-22 — bumped 17→18 to match the lede tier used on
     services-hub / home / other marketing surfaces. */
  font-size: 18px;
  line-height: 1.65;
  color: rgb(245 247 250 / 80%);
  margin: 0 0 24px;
}
body.page-id-123641 .about-prose p:last-of-type {
  margin-bottom: 0;
}
body.page-id-123641 .about-prose strong {
  color: #F5F7FA;
  font-weight: 600;
}

/* 2026-05-22 — center the section header (eyebrow + h2 + intro lede)
   on products + methodology bands to match services-hub's
   centered-head pattern. The about band (04) stays left-aligned because
   its content is intentionally 2-col grid. CTA band (05) already
   centered via the block below. Cards / timeline children get reset
   to default text-align inside their own layout. */
body.page-id-123641 [data-sw-section="products"] .canvas > .sw-chapter-ebrow,
body.page-id-123641 [data-sw-section="products"] .canvas > .section-h2,
body.page-id-123641 [data-sw-section="products"] .canvas > .products-intro,
body.page-id-123641 [data-sw-section="methodology"] .canvas > .sw-chapter-ebrow,
body.page-id-123641 [data-sw-section="methodology"] .canvas > .section-h2,
body.page-id-123641 [data-sw-section="methodology"] .canvas > .methodology-intro {
  text-align: center !important;
}
/* 2026-05-22 — section h2s have max-width:900 left-anchored at the
   canvas edge. text-align: center only centers the TEXT inside the
   900px block; the block itself stays anchored left, putting the
   visual center ~170px left of the canvas center. Adding
   `margin-inline: auto` centers the block itself so the entire
   eyebrow → h2 → lede stack reads as truly centered. */
body.page-id-123641 [data-sw-section="products"] .canvas > .section-h2,
body.page-id-123641 [data-sw-section="methodology"] .canvas > .section-h2,
body.page-id-123641 [data-sw-section="products"] .products-intro,
body.page-id-123641 [data-sw-section="methodology"] .methodology-intro {
  margin-left: auto !important;
  margin-right: auto !important;
}

/* End-cta — centered band 05 */
body.page-id-123641 [data-sw-section="cta-band"] .canvas {
  text-align: center;
}
body.page-id-123641 [data-sw-section="cta-band"] .canvas h2,
body.page-id-123641 [data-sw-section="cta-band"] .canvas .lede {
  margin-left: auto;
  margin-right: auto;
}
body.page-id-123641 [data-sw-section="cta-band"] .canvas .cta-row {
  justify-content: center;
}
body.page-id-123641 [data-sw-section="cta-band"] .meta-row {
  display: flex;
  gap: 48px;
  margin-top: 48px;
  flex-wrap: wrap;
  justify-content: center;
  font-family: var(--font-mono, "JetBrains Mono", monospace);
  /* 2026-05-22 — bumped 11→12 to clear locked caps minimum. */
  font-size: 12px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: #8B9AAE;
  padding-top: 32px;
  border-top: 1px solid rgb(245 247 250 / 12%);
}
body.page-id-123641 [data-sw-section="cta-band"] .meta-row strong {
  color: #F5F7FA;
  font-weight: 500;
}

/* Canvas wrapper — constrained content inside full-width bands.
   Updated 2026-05-27: 1238 → var(--width-canvas) (= 1290). +52px desktop
   gain. See widths.md (canvas tier). */
body.page-id-123641 .canvas {
  max-width: var(--width-canvas);
  margin: 0 auto;
  width: 100%;
}

/* About — link affordance below the 2-col prose grid (band 04) */
body.page-id-123641 .about-link {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  margin-top: 32px;
  font-family: var(--font-mono, "JetBrains Mono", monospace);
  font-size: 12px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: #8FC9F7;
  font-weight: 500;
  text-decoration: none;
  padding: 6px 0;
  border-bottom: 1px solid #8FC9F7;
}
body.page-id-123641 .about-link:hover {
  color: #F5F7FA;
  border-color: #F5F7FA;
}

/* =========================================================================
   M0 (2026-05-07 revamp) — typography baseline
   =========================================================================
   Per audit/00-system.md § Typography. Single source of truth for h1-h6 +
   body + .sw-eyebrow so per-widget Elementor overrides become opt-in
   (component-specific only) rather than the default authoring path.

   Display font is Inter for M0 (audit calls for Fraunces Variable; that
   loading is deferred to M5 logo+typography milestone). When Fraunces
   ships, swap var(--font-sans) for var(--font-display) here and add the
   token in tokens.css.

   Sizes stay on the clamp tokens already in tokens.css (--size-h1 etc.).
   Weights, tracking, line-height align with audit's typography.md baseline.
   ========================================================================= */
body.wp-theme-blocksy.wp-child-theme-blocksy-child h1,
body.wp-theme-blocksy.wp-child-theme-blocksy-child h2,
body.wp-theme-blocksy.wp-child-theme-blocksy-child h3 {
  font-family: var(--font-sans);
  font-weight: 600;
  letter-spacing: -0.01em;
  line-height: 1.05;
}
body.wp-theme-blocksy.wp-child-theme-blocksy-child h4,
body.wp-theme-blocksy.wp-child-theme-blocksy-child h5,
body.wp-theme-blocksy.wp-child-theme-blocksy-child h6 {
  font-family: var(--font-sans);
  font-weight: 600;
  line-height: 1.2;
}
body.wp-theme-blocksy.wp-child-theme-blocksy-child h1 { font-size: var(--size-h1); }
body.wp-theme-blocksy.wp-child-theme-blocksy-child h2 { font-size: var(--size-h2); }
body.wp-theme-blocksy.wp-child-theme-blocksy-child h3 { font-size: var(--size-h3); }
body.wp-theme-blocksy.wp-child-theme-blocksy-child p,
body.wp-theme-blocksy.wp-child-theme-blocksy-child li {
  font-family: var(--font-sans);
  font-weight: 400;
  line-height: 1.5;
}
body.wp-theme-blocksy.wp-child-theme-blocksy-child .sw-eyebrow,
body.wp-theme-blocksy.wp-child-theme-blocksy-child .sw-chapter-ebrow {
  font-family: var(--font-mono);
  font-weight: 500;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

/* 2026-05-28 — Blanket eyebrow line-height. Mono/uppercase eyebrows were
   authored at line-height 1.0 on the text element (the Elementor heading
   default), so any that wrapped to 2 lines crammed together (caught first
   on the About + Services Hub hero eyebrows, fixed there per-widget). Give
   every design-system eyebrow component a comfortable 1.5 so a wrapped
   eyebrow always breathes — and so future copy growth can't reintroduce the
   bug. The class sits on the widget wrapper for heading-widget eyebrows
   (target the inner .elementor-heading-title) and on the element itself for
   HTML-widget eyebrows (target it directly). The body prefix outranks
   Elementor's per-widget line-height. NOTE: single-word classless labels
   (e.g. pillar tags MARKETING/OPERATIONS/TOOLS) aren't covered — they have
   no class hook and a single word can't wrap, so line-height is moot. */
body.wp-theme-blocksy.wp-child-theme-blocksy-child .sw-eyebrow .elementor-heading-title,
body.wp-theme-blocksy.wp-child-theme-blocksy-child .sw-chapter-ebrow .elementor-heading-title,
body.wp-theme-blocksy.wp-child-theme-blocksy-child .sw-cta-microeyebrow .elementor-heading-title,
body.wp-theme-blocksy.wp-child-theme-blocksy-child .method-eyebrow .elementor-heading-title,
body.wp-theme-blocksy.wp-child-theme-blocksy-child .sw-eyebrow.elementor-heading-title,
body.wp-theme-blocksy.wp-child-theme-blocksy-child .sw-tile-num,
body.wp-theme-blocksy.wp-child-theme-blocksy-child .method-eyebrow,
body.wp-theme-blocksy.wp-child-theme-blocksy-child .sw-case-hero__eyebrow,
body.wp-theme-blocksy.wp-child-theme-blocksy-child .sw-reel-text .eyebrow,
body.wp-theme-blocksy.wp-child-theme-blocksy-child .sw-field-log-eyebrow__cat,
body.wp-theme-blocksy.wp-child-theme-blocksy-child .sw-field-log-eyebrow__date {
  line-height: 1.5;
}

/* =========================================================================
   M0 (2026-05-07 revamp) — small-text contrast on bg-soft (P0-A11Y-01)
   =========================================================================
   Audit finding: ink-500 (--mute #5A6675) on bg-soft (#F2F4F7) at 14px
   fails AA at small sizes. Lift small-text on bg-soft surfaces from
   --mute to --ink-700.

   Selector chain targets paragraph + small text inside soft bands and
   inside Elementor text-editor widgets. Elementor's per-widget
   typography_text_color overrides at higher specificity will still win
   where intentionally set; this rule is the BASELINE for unset cases.
   ========================================================================= */
[data-sw-band="soft"] p,
[data-sw-band="soft"] li,
[data-sw-band="soft"] .sw-text-small,
[data-sw-section="pillars"] p,
[data-sw-section="pillars"] li,
.sw-band-soft p,
.sw-band-soft li {
  color: var(--ink-700);
}

/* ============================================================
 * M1.1 — Header consolidation (2026-05-09)
 * Single header (post 122841 / SW header-light) renders sitewide.
 * On dark-twin product pages (body.sw-dark-twin via body-class.php
 * filter on fluent-products CPT singulars), the chrome flips to
 * paper-on-ink via these rules. Retires the parallel header-sub-brand
 * part (post 122844). Audit: GH-CONSOLIDATE-01.
 * ============================================================ */

/* Container background flips paper → ink on dark-twin pages.
   Selector matches the M5.1-era data-sw-block attr on the header
   container; falls back to Blocksy's ct-content-block-122841 wrapper
   for redundancy. */
body.sw-dark-twin [data-sw-block="header-light"],
body.sw-dark-twin .ct-content-block-122841 > .elementor > .elementor-section,
body.sw-dark-twin .ct-content-block-122841 > .elementor > .e-con {
  background-color: var(--ink) !important;
  border-bottom-color: rgb(245 247 250 / 14%) !important;
}

/* Logo asset: dark-twin pages already serve a paper variant
   (`sytewide-lettering-white-*.png`); the prior M1.1-era
   `filter: invert(1) brightness(2.2)` rule that lived here was
   dead code after commit 923c2d2 introduced `#header img { filter:
   none !important; }`. Deleted post-M4 cleanup wave. Proper paper
   variants ship via M5 brand/ asset library. */

/* Nav-menu items flip ink → paper on dark canvas; hover stays
   blue-bright (already P2-compatible, but pinned here for clarity). */
body.sw-dark-twin [data-sw-block="header-light"] .elementor-nav-menu a.elementor-item,
body.sw-dark-twin .ct-content-block-122841 .elementor-nav-menu a.elementor-item {
  color: var(--bg) !important;
}
body.sw-dark-twin [data-sw-block="header-light"] .elementor-nav-menu a.elementor-item:hover,
body.sw-dark-twin [data-sw-block="header-light"] .elementor-nav-menu a.elementor-item-active,
body.sw-dark-twin .ct-content-block-122841 .elementor-nav-menu a.elementor-item:hover,
body.sw-dark-twin .ct-content-block-122841 .elementor-nav-menu a.elementor-item-active {
  color: var(--blue-bright) !important;
}

/* Header CTA button — keeps blue background (already paper-coloured
   text via var(--bg) post-M0). On dark canvas the blue still reads
   correctly; no explicit override needed. */

/* ============================================================
   M2.1 PR-CTA-01 — .sw-end-cta partial (2026-05-10)
   --------------------------------------------------------------
   Single source of truth for the end-CTA pattern shared across
   home (post 700, M1.2) + about (698, M1.2) + services-hub (123633,
   M2.1) + 5 product CPT pages (M2.2). Targets both data-sw-section
   values currently in use (cta-band on about/services-hub/products,
   end-cta on home — historical inconsistency, future M2 work
   normalizes to cta-band but the partial handles both for safety).

   Mechanism: inner container max-width clamp + centred prose +
   centred buttons. Tonal awareness inherits via .sw-dark-twin
   cascade (no separate dark rule needed — tokens flip).

   Affected surfaces (verified 2026-05-10):
     - /                          (post 700, ee89f39, end-cta)
     - /about-us/                 (post 698, 5a0a417, cta-band, sw-dark-twin)
     - /services-hub/             (post 123633, M2.1)
     - /products/trustily/        (post 123545, M2.2)
     - /products/syteops/         (post 122385, M2.2)
     - /products/syteops-ltd/     (post 118238, M2.2, sw-dark-twin)
     - /products/sytehero/        (post 122486, M2.2)
     - /products/sytehero-ltd/    (post 121487, M2.2, sw-dark-twin)
   ============================================================ */
[data-sw-section="cta-band"] .e-con-inner,
[data-sw-section="end-cta"] .e-con-inner {
  max-width: var(--width-prose);
  margin-left: auto;
  margin-right: auto;
  text-align: center;
}
[data-sw-section="cta-band"] .e-con-inner .elementor-widget-heading,
[data-sw-section="end-cta"] .e-con-inner .elementor-widget-heading,
[data-sw-section="cta-band"] .e-con-inner .elementor-widget-text-editor,
[data-sw-section="end-cta"] .e-con-inner .elementor-widget-text-editor {
  text-align: center;
}
/* Button row — center both buttons within their flex container */
[data-sw-section="cta-band"] .e-con-inner .e-con:has(> .elementor-widget-button),
[data-sw-section="end-cta"] .e-con-inner .e-con:has(> .elementor-widget-button) {
  justify-content: center;
}
[data-sw-section="cta-band"] .e-con-inner .elementor-widget-button,
[data-sw-section="end-cta"] .e-con-inner .elementor-widget-button {
  width: fit-content;
}

/* ============================================================
   M3.1 BL-TYPE-01 — Inter Variable bound to body (2026-05-10)
   --------------------------------------------------------------
   Audit BL-TYPE-01 (P0): blog body falls back to Blocksy default
   font because Blocksy's theme stylesheet sets body font-family
   later in the cascade. This rule enforces Inter as the body
   font, sourced from the design-token chain. Specificity:
   matches Blocksy's `body, .ct-body` selector by adding a small
   compound selector that wins via specificity AND source order.
   ============================================================ */
body,
body.wp-singular,
body.archive {
  font-family: var(--font-sans, "Inter", system-ui, -apple-system, sans-serif);
}

/* ============================================================
   M3.1 BL-TYPE-02 — long-form 18/1.6 on blog single (2026-05-10)
   --------------------------------------------------------------
   Audit BL-TYPE-02 (P1): marketing baseline is 16/1.5; long-form
   editorial reads better at 18/1.6. Scoped to body.single-post
   so blog singles get the override but archive (and other CPT
   singulars like sw_case_study) inherit the baseline.

   Targets standard WP post content + Elementor blog-single
   block's article-body data-sw-role wrapper.
   ============================================================ */
body.single-post .entry-content p,
body.single-post .entry-content li,
body.single-post [data-sw-role="article-body"] p,
body.single-post [data-sw-role="article-body"] li {
  font-size: 18px;
  line-height: 1.6;
}

/* ============================================================
   M3.1 BL-IMG-01 — featured image 16:9 + 6px radius (2026-05-10)
   --------------------------------------------------------------
   Audit BL-IMG-01 (P1): featured-image aspect-ratio standard for
   blog singles. Object-fit cover prevents distortion for portrait
   originals (operator audits affected posts post-merge if any
   crop unexpectedly).
   ============================================================ */
body.single-post .post-thumbnail img,
body.single-post [data-sw-role="featured-image"] img,
body.single-post .wp-post-image {
  aspect-ratio: 16 / 9;
  border-radius: var(--radius-sm);
  object-fit: cover;
  width: 100%;
  height: auto;
}

/* ============================================================
   M3.1 carryover — M2.2 ghost CTA dark-twin fix (2026-05-10)
   --------------------------------------------------------------
   M2.2 product template generator set ghost CTA buttons with
   widget-level button_text_color: #0B1B2B (deep ink) hex literal.
   On body.sw-dark-twin pages (all 5 product CPT singulars), this
   renders dark-on-dark — invisible. Inline style takes precedence
   over CSS rules, so we use !important on selectors that target
   the rendered inline style attribute.
   ============================================================ */
body.sw-dark-twin .elementor-widget-button .elementor-button[style*="color:#0B1B2B"],
body.sw-dark-twin .elementor-widget-button .elementor-button[style*="color: #0B1B2B"],
body.sw-dark-twin .elementor-widget-button .elementor-button[style*="rgb(11, 27, 43)"],
body.sw-dark-twin .elementor-widget-button .elementor-button[style*="rgb(11,27,43)"] {
  color: var(--bg) !important;
  border-color: var(--bg) !important;
}
body.sw-dark-twin .elementor-widget-button .elementor-button[style*="border-color:#0B1B2B"],
body.sw-dark-twin .elementor-widget-button .elementor-button[style*="border-color: #0B1B2B"] {
  border-color: var(--bg) !important;
}

/* ============================================================
   M3.2 CM-CSS-01 — FluentCart binding layer (2026-05-10)
   --------------------------------------------------------------
   Maps FluentCart-rendered classes to LOCKED 2026-05-07 design
   tokens. Single source of truth for commerce styling across
   /shop/, /cart/, /checkout/, /account/, /receipt/.

   FC's actual class prefix is `fct-` (not `fc-`). This binding
   layer targets the verified production class names + the FC
   Gutenberg block variants `wp-block-fluent-cart-shopapp-*`.

   Maintenance burden: this layer assumes FC plugin's class names
   stay stable. If FC plugin update breaks rendering, re-inspect
   live DOM via curl + DevTools, then update selectors here.

   Extension procedure: when FC adds a new component, audit its
   default classes via DevTools, then add a binding rule here that
   resolves all colors/radii/typography to site tokens
   (var(--bg)/var(--ink)/var(--blue)/var(--radius-sm)/etc.).
   NO hex literals — token-only so dark-twin pages inherit
   correctly via body.sw-dark-twin cascade.

   !important is used only where required to override FC's own
   stylesheet specificity; documented inline why.

   Audit tickets closed: CM-RADIUS-01, CM-COLOR-01, CM-COLOR-02,
   CM-PADDING-01, CM-FORM-01, CM-FORM-02, CM-FORM-03, CM-CSS-01.
   ============================================================ */

/* ----- CM-RADIUS-01 — product cards 6px (replaces 8/12/16 mix) ----- */
.fct-product-card,
.fct-product-card-image,
.fct-product-card-image-wrap {
  border-radius: var(--radius-sm);
}
.fct-product-card {
  background: var(--bg);
  border: 1px solid var(--rule);
  overflow: hidden;
}
.fct-product-card-image,
.fct-product-card-image-wrap {
  overflow: hidden;
}

/* ----- CM-COLOR-02 — sale badges blue-bright (no rust accent) ----- */
.fct-sale-badge,
.fct-product-sale-badge,
.fct-product-card .sale,
.fct-product-card .onsale {
  background: var(--blue-bright) !important;
  color: var(--bg) !important;
  border-radius: var(--radius-btn);
  font-family: var(--font-mono);
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  padding: 4px 10px;
}

/* ----- CM-COLOR-01 — view/add-to-cart paper text via var(--bg) ----- */
.fct-product-view-button,
.fct-single-product-card-view-button,
.fct-add-to-cart,
.wp-block-fluent-cart-shopapp-product-buttons .fct-button-text {
  background: var(--blue);
  color: var(--bg) !important;  /* !important to defeat FC inline + #FFFFFF defaults */
  border-radius: var(--radius-btn);
  font-family: var(--font-sans);
  font-weight: 600;
  font-size: 14px;
  padding: 10px 18px;
  border: none;
  cursor: pointer;
  text-decoration: none;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  transition: background 120ms;
}
.fct-product-view-button:hover,
.fct-single-product-card-view-button:hover,
.fct-add-to-cart:hover {
  background: var(--ink);
  color: var(--bg) !important;
  text-decoration: none;
}

/* Product card title + price typography binding */
.fct-product-card-title,
.wp-block-fluent-cart-shopapp-product-title {
  font-family: var(--font-sans);
  font-size: 17px;
  font-weight: 600;
  color: var(--ink);
  letter-spacing: -0.01em;
  margin: 0 0 8px;
}
.fct-product-card-prices,
.wp-block-fluent-cart-shopapp-product-price,
.fct-item-price {
  font-family: var(--font-mono);
  font-size: 14px;
  color: var(--mute);
}
.fct-product-card-prices strong,
.fct-item-price strong {
  color: var(--ink);
  font-weight: 600;
}

/* ----- CM-PADDING-01 — commerce sections inherit data-sw-section clamp ----- */
/* Real commerce post IDs (verified 2026-05-10 via wp eval url_to_postid):
     /shop/     → 118231
     /cart/     → 118233
     /checkout/ → 118235
     /account/  → 118232
     /receipt/  → 118234
   Apply var(--section-y) clamp(64-96) to replace the fixed 80/80
   per-section padding that FC pages were authored with.
   2026-05-13 fix: scoped to `main` so the rule doesn't cascade into the
   Blocksy footer CB (post-122845) — its e-con-inner containers carry
   data-sw-section attributes too, and the rule was forcing 96/96 footer
   padding on /shop/ vs 72/32 on Wave 1 legal pages that lack this rule. */
body.page-id-118231 main [data-sw-section] .e-con-inner,
body.page-id-118233 main [data-sw-section] .e-con-inner,
body.page-id-118235 main [data-sw-section] .e-con-inner,
body.page-id-118232 main [data-sw-section] .e-con-inner,
body.page-id-118234 main [data-sw-section] .e-con-inner {
  padding-top: var(--section-y);
  padding-bottom: var(--section-y);
}

/* ----- CM-FORM-01 — checkout form labels Inter ----- */
.fct-form-label,
.fct-input-label,
.fct-checkout-label,
label.fct-checkout-form-label,
.fluent-cart-form-label {
  font-family: var(--font-sans);
  font-size: 14px;
  font-weight: 500;
  color: var(--ink);
  letter-spacing: 0.02em;
  margin-bottom: 6px;
  display: block;
}

/* ----- CM-FORM-02 — input radius 4px (button radius, NOT card 6px) ----- */
.fct-input,
.fct-form-input,
.fct-checkout-input,
input.fct-text-input,
input.fct-email-input,
input.fluent-cart-input,
.fluent-cart-form input[type="text"],
.fluent-cart-form input[type="email"],
.fluent-cart-form input[type="tel"] {
  border-radius: var(--radius-btn);  /* 4px, NOT 6px */
  border: 1px solid var(--rule);
  background: var(--bg);
  color: var(--ink);
  font-family: var(--font-sans);
  font-size: 15px;
  padding: 10px 14px;
  transition: border-color 120ms, box-shadow 120ms;
}
.fct-input:focus,
.fct-form-input:focus,
.fct-checkout-input:focus,
input.fct-text-input:focus,
input.fct-email-input:focus,
input.fluent-cart-input:focus,
.fluent-cart-form input:focus {
  outline: none;
  border-color: var(--blue);
  box-shadow: 0 0 0 2px rgb(31 95 133 / 20%);
}

/* ----- CM-FORM-03 — error state ink + underline (rebind from FC red) ----- */
.fct-error,
.fct-form-error,
.fct-input-error,
.fluent-cart-error,
.fluent-cart-form-error {
  color: var(--ink) !important;
  border-bottom: 1px solid currentColor !important;
  background: transparent !important;
  font-family: var(--font-sans);
  font-size: 13px;
  padding: 4px 0;
  display: inline-block;
}
.fct-input.has-error,
.fct-input[aria-invalid="true"],
.fct-form-input.has-error,
input.fluent-cart-input.has-error {
  border-color: var(--ink);
  border-width: 2px;
}

/* ----- M3.2 carryover — /cart/ hand-written CSS audit -----
   Cart page (post 118233) has inline custom_css:
     li.fct-cart-item { padding-left: 0px!important; }
   Folding into binding layer here so the inline rule can be removed
   in a follow-up if cart line-item rendering looks fine. */
.fct-cart-item,
li.fct-cart-item {
  padding-left: 0;
}

/* ----- Receipt-specific styling (CM-RECEIPT-01 supports) -----
   The receipt page hero band (added in this PR via wp eval-file)
   uses .sw-receipt-hero scope; the FC receipt shortcode is wrapped
   in .fct-receipt-* containers. Style FC receipt content for
   readability inside the SyteWide chrome. */
.fct-receipt,
.fct-receipt-content {
  background: var(--bg);
  border-radius: var(--radius-sm);
  padding: 32px;
  border: 1px solid var(--rule);
  box-shadow: var(--shadow-card);
  max-width: var(--width-prose);
  margin: 0 auto;
}
.fct-receipt-line,
.fct-order-summary-row {
  display: flex;
  justify-content: space-between;
  padding: 12px 0;
  border-bottom: 1px solid var(--rule);
  font-family: var(--font-sans);
  font-size: 15px;
}
.fct-receipt-line:last-child,
.fct-order-summary-row.total {
  border-bottom: none;
  font-weight: 600;
}

/* ============================================================
   M4-fix .sw-pillar-deliverables (2026-05-10)
   --------------------------------------------------------------
   M1.2 home + M2.1 services-hub + M2.2 product templates author
   pillar cards with `<ul class="sw-pillar-deliverables"><li>...</li></ul>`
   markup but no CSS rule was ever added. Bullets fell back to
   browser default + theme-inherited color → invisible/low-contrast.
   Style spec matches m2-services-hub.html mockup.
   ============================================================ */
.sw-pillar-deliverables {
  list-style: none;
  padding: 0;
  margin: 16px 0 0;
}
.sw-pillar-deliverables li {
  font-size: 14px;
  line-height: 1.55;
  color: var(--ink-700);
  padding-left: 18px;
  position: relative;
  margin-bottom: 6px;
}
.sw-pillar-deliverables li::before {
  content: "→";
  position: absolute;
  left: 0;
  color: var(--blue);
  font-family: var(--font-mono);
}

/* 2026-06-02 — deliverables-rule alignment across tiles in a .sw-tile-grid row.
   The horizontal rule is the top border of .sw-pillar-deliverables, which sits
   right after a variable-length paragraph, so the rules land at different
   heights per card. Pure CSS can't equalize the paragraph block across separate
   cards at every breakpoint without either fragile per-breakpoint magic-numbers
   or a deep subgrid that fights Elementor's nested container DOM (verified: the
   .e-con-inner/widget nesting blows the subgrid heights up). Instead this is
   handled deterministically by assets/js/tile-align.js, which equalizes the
   pre-rule paragraph heights per VISUAL ROW (so it's correct in the 3-col,
   2-col-wrap, and 1-col-stack states alike) and re-runs on resize + font load.
   No CSS rule needed here; without JS the rules simply fall back to their
   natural (unaligned) positions — graceful, not broken. */

/* ============================================================
   M4-fix-2 (2026-05-10) — followup to PR #28
   --------------------------------------------------------------
   - PR #28 over-corrected: forced ink-on-light text colors on
     home/about/services-hub INCLUDING text inside intentional
     dark-twin sections (M1.2 home methodology) → ink-on-dark
     = invisible. Restored here.
   - .sw-mini-method, .canvas, .lede, .timeline, .step, .ord-bg,
     .step-tag classes from M1.2 home methodology HTML widget +
     M2.1 services-hub mini-method had ZERO CSS rules (the source
     /products/ archive styles were body.page-id-123641-scoped).
   - Pillar cards have excessive iter-6 32×28 padding.
   - Hero info constrained narrow + walkthrough video tiny — flex
     direction column on hero so info + video stack full-width.
   ============================================================ */

/* Methodology HTML widget: canvas + lede + section-h2.
   Updated 2026-05-27: 1238 → var(--width-canvas) (= 1290). +52px desktop
   gain. Affects home, products archive, services-hub. See widths.md. */
[data-sw-section="methodology"] .canvas,
.sw-mini-method {
  max-width: var(--width-canvas);
  margin: 0 auto;
}
[data-sw-section="methodology"] .lede,
[data-sw-section="methodology"] p.lede,
.sw-mini-method .lede,
.sw-mini-method p.lede {
  /* 2026-05-21 (PR 3 round-4 follow-up): matches the marketing-surface
     section-H2 typography exactly per operator request — clamp(28-36)/
     600/1.15 — so the methodology lede reads with the same visual
     weight as section H2s like "Marketing on one side. Operations on
     the other. Tools that hold it together." Max-width bumped 760 →
     880 to match the methodology-intro parent. Round-3 was
     clamp(22-28)/500/1.25 — too subdued. Round-2 was 18/400 body-size
     — way too small. */
  font-family: var(--font-body, "Inter", sans-serif);
  font-size: clamp(28px, 3.5vw, 36px);
  font-weight: 600;
  line-height: 1.15;
  max-width: var(--width-wide);
  margin: 0 auto 32px;
  text-align: center;
  color: #FAFAF7;
}
[data-sw-section="methodology"] .section-h2,
.sw-mini-method .section-h2 {
  font-size: clamp(22px, 3.5vw, 36px) !important;
  font-weight: 600;
  line-height: 1.15;
  margin: 0 0 16px;
  color: #FAFAF7;
}
[data-sw-section="methodology"] .methodology-intro {
  margin: 0 auto 32px;
  max-width: var(--width-wide);
  text-align: center;
}
/* iter-16b (2026-05-20): center the methodology lede so it matches the
   pillars / testimonials / FAQ centered head pattern across the page.
   The shared rule above sets max-width + auto margins + text-align;
   nothing else page-scoped needed. */

/* Methodology timeline / mini-method-grid (3-up).
   2026-05-14: bumped margin-top 32 → 80 sitewide per operator review.
   Gives the 3-card timeline breathing room from the lede paragraph
   above. Applies to home + /products/ (the only two surfaces with
   this pattern). */
[data-sw-section="methodology"] .timeline,
.sw-mini-method .mini-method-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 32px;
  margin-top: 80px;
}
@media (max-width: 900px) {
  [data-sw-section="methodology"] .timeline,
  .sw-mini-method .mini-method-grid {
    grid-template-columns: 1fr;
    gap: 24px;
  }
}
[data-sw-section="methodology"] .step,
.sw-mini-method .method-step {
  position: relative;
  /* 2026-05-14: bumped top 28 → 40 sitewide so the .ord-bg circles
     have visible breathing room above them inside each card. */
  padding: 40px 24px 24px;
  border-radius: var(--radius-sm);
  background: rgb(250 250 247 / 4%);
  border: 1px solid rgb(250 250 247 / 12%);
}
[data-sw-section="methodology"] .step h3,
.sw-mini-method .method-step h4 {
  font-size: 22px;
  font-weight: 600;
  line-height: 1.2;
  margin: 12px 0 8px;
  color: #FAFAF7;
}
[data-sw-section="methodology"] .step p,
.sw-mini-method .method-step p {
  font-size: 15px;
  line-height: 1.55;
  margin: 0 0 12px;
  color: rgb(250 250 247 / 85%);
}
[data-sw-section="methodology"] .step .step-tag,
.sw-mini-method .method-step .method-tag {
  display: inline-block;
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.04em;
  color: #4A9DC8;
  margin-top: 8px;
}
.sw-mini-method .method-step .method-num {
  font-family: var(--font-mono);
  font-size: 14px;
  font-weight: 600;
  width: 36px;
  height: 36px;
  background: #1F5F85;
  color: #FAFAF7;
  border-radius: 50%;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
[data-sw-section="methodology"] .step .ord-bg {
  position: relative !important; /* 2026-05-14: was static — need positioned for z-index to take effect, so the circle covers the timeline::before line where they overlap. */
  font-family: var(--font-mono);
  font-size: 14px !important;
  font-weight: 600;
  width: 36px !important;
  height: 36px !important;
  background: #1F5F85 !important;
  color: #FAFAF7 !important;
  border-radius: 50%;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  margin-bottom: 8px;
  z-index: 2 !important; /* 2026-05-14: was auto — bump above timeline::before (z:0) so the connecting line is hidden behind the filled circle, visible only between circles. */
  opacity: 1 !important;
}
[data-sw-section="methodology"] .step .ord-mark {
  display: none !important;
}
.sw-mini-method .method-cta-link,
.method-cta-link {
  display: block;
  margin-top: 24px;
  text-align: center;
  font-family: var(--font-mono);
  font-size: 13px;
  letter-spacing: 0.04em;
}
.sw-mini-method .method-cta-link a,
.method-cta-link a {
  color: #4A9DC8;
}

/* 2026-05-21 (PR 3 voice-pass follow-up): methodology micro-eyebrow above
   the .lede inside .sw-mini-method (services-hub) and inside
   .methodology-intro (home .canvas). Matches the mono-uppercase-small
   treatment used on other chapter eyebrows across the site so the
   methodology block has its own header anchor instead of relying on the
   outer Elementor widgets alone. */
.method-eyebrow {
  font-family: var(--font-mono) !important;
  font-size: 11px !important;
  font-weight: 500 !important;
  letter-spacing: 0.18em !important;
  text-transform: uppercase !important;
  text-align: center;
  margin: 0 auto 16px;
  color: var(--ink) !important;
  display: block;
}
.method-eyebrow strong {
  color: var(--ink) !important;
  font-weight: 500 !important;
}

/* 2026-05-21 (PR 3 voice pass): optional callout sitting between the 3-phase
   grid and the CTA link in .sw-mini-method / the home methodology band. Styled
   as a bordered callout — distinct from body prose.
   2026-06-02: operator wants this to read as a real, viable option clients
   actually consider (the "we can also host + monitor your site" note on home),
   not a faint footnote. Bumped 14 → 16px and switched var(--ink-700) →
   var(--ink) for full-strength text. var(--ink) is canvas-aware: it resolves
   to paper-white on the dark-twin methodology band yet stays ink on any light
   canvas, so the box is never invisible. Border + fill nudged up a step for a
   touch more presence. */
.sw-mini-method .sw-mini-method-plus,
.sw-mini-method-plus {
  display: block;
  max-width: var(--width-prose);
  margin: 32px auto 0;
  padding: 20px 24px;
  border: 1px solid rgba(74, 157, 200, 0.35);
  border-radius: 6px;
  background: rgba(74, 157, 200, 0.10);
  font-size: 16px;
  line-height: 1.6;
  text-align: center;
  color: var(--ink);
}
@media (max-width: 640px) {
  .sw-mini-method .sw-mini-method-plus,
  .sw-mini-method-plus {
    margin: 24px 12px 0;
    padding: 16px 18px;
    font-size: 15px;
  }
}

/* 2026-05-14 (Phase 5b): home pillar-card 20/18 padding rule, hero stacking +
   walkthrough + .sw-video-hero block, and M4-fix-3 dark-twin.e-con bg
   override (~55 lines combined) RETIRED.

   1. `body.page-id-700 [data-sw-template="pillar-card"] { padding: 20px 18px
      !important }` lost cascade to the later (0,2,2) per-side 28px rule below
      at line ~4365 — live probe shows pillar cards render padding=28px.
   2. `body.page-id-700 [data-sw-section="walkthrough"]` + `.sw-video-hero`
      block targets DOM that no longer exists (walkthrough section + video-
      hero element removed during 2026-05-07 revamp; live querySelectorAll
      across home = 0).
   3. M4-fix-3 `.sw-dark-twin.e-con` bg #0B1B2B for home/about/services-hub:
      the only .sw-dark-twin.e-con on those pages is the Blocksy footer CB
      (503a593), which is fully overridden by the later "Footer always dark
      canvas" rule at line ~5010 (bg #0A1626). Home methodology + services-
      hub end-cta use .sw-band-soft (not .sw-dark-twin) — rule didn't reach
      them.

   Hero column-stacking rules below kept — they configure home hero (the
   only sw-band-centered hero with a .sw-hero-text-col child column wrapper
   between the hero container and its widgets). Other surfaces' heroes
   inherit Elementor's default e-flex column behavior + .sw-band-centered
   sitewide rules. */

/* Home hero column wrapper: when the hero has a .sw-hero-text-col child
   (Elementor inserts this between the hero container and its widgets on
   home), force it full-width inside the hero. The hero itself stays
   flex column via Elementor's e-flex defaults. */
body.page-id-700 .sw-hero-text-col,
body.page-id-700 [data-sw-section="hero"] > .sw-hero-text-col {
  max-width: 100% !important;
  width: 100% !important;
  flex: none !important;
}

/* 2026-06-02 fix — the home hero read as cramped: a 20px column gap stacked the
   72px h1, the lede, and the CTA row too tightly. Open the vertical rhythm to
   34px so the hero has real breathing room. Page+attr+class specificity (0,3,1)
   wins over Elementor's element-id flex `gap`, so no !important needed. */
body.page-id-700 [data-sw-section="hero"] .sw-hero-text-col {
  row-gap: 34px;
}
body.page-id-700 [data-sw-section="hero"] .sw-hero-text-col > .elementor-widget-text-editor {
  margin-top: 4px;
}
body.page-id-700 [data-sw-section="hero"] .sw-hero-text-col > .e-con:last-child {
  margin-top: 6px;
}

/* M4-fix-4 (2026-05-10): operator request — darker header sitewide.
   Applies to the Blocksy <header id="header"> wrapper. Logo + nav + CTA
   inside flip to paper-on-ink for legibility. */
#header,
body #header {
  background: #0B1B2B !important;
  /* 2026-05-27: vertically center the content grid within the header row.
     Blocksy locks #header to height:101px (desktop) but the #sw-header-light
     grid is only ~78px and, with the default display:block, top-pinned —
     leaving a ~23px dead strip below the logo/nav (reads as top-heavy
     padding). Flex-column + justify-center balances it (~12px top/bottom)
     while preserving the 101px row that hero `calc(100vh - 100px)` sizing
     depends on. No-op at ≤768px where the grid already fills the 72px row. */
  display: flex;
  flex-direction: column;
  justify-content: center;
}
#header [data-sw-block="header-light"],
#header .elementor-section,
#header .e-con {
  background-color: transparent !important;  /* let #header bg shine through */
}
#header .elementor-nav-menu a.elementor-item {
  color: #FAFAF7 !important;
}
#header .elementor-nav-menu a.elementor-item:hover,
#header .elementor-nav-menu a.elementor-item-active {
  color: #8FC9F7 !important;
}
/* Logo: operator's asset is multi-coloured (S mark + wordmark);
   inverting it produces weird rainbow artifacts. Render as-authored
   on dark — designer can supply a paper-on-dark variant later. */
#header [data-sw-role="brand"] img,
#header .ct-image-container img {
  filter: none !important;
}
/* Header CTA (Book audit) — keep blue background, ensure paper text */
#header .elementor-button {
  background-color: #1F5F85 !important;
  color: #FAFAF7 !important;
}
#header .elementor-button:hover {
  background-color: #4A9DC8 !important;
  color: #0B1B2B !important;
}

/* Home pillar card padding + content alignment.
   Home is the only surface that uses `.pillar-card` / `.sw-t-pillar-card` /
   `[data-sw-template="pillar-card"]` — product detail pages use `.sw-tile`
   (sitewide 32px padding) instead. Per-side declarations + ancestor selector
   bring specificity to (0,2,2) with !important on each side to beat
   Elementor's per-widget padding clamp (7vw → 102px on 1440 viewport).
   2026-05-14 (Phase 5b): combined two redundant pillar-padding blocks +
   text-align rule into one. */
body.page-id-700 [data-sw-section="pillars-intro"] [data-sw-template="pillar-card"],
body.page-id-700 .sw-band-pillars-intro [data-sw-template="pillar-card"],
body.page-id-700 [data-sw-section="pillars-intro"] .sw-t-pillar-card,
body.page-id-700 .sw-band-pillars-intro .sw-t-pillar-card {
  padding-top: 28px !important;
  padding-right: 28px !important;
  padding-bottom: 28px !important;
  padding-left: 28px !important;
  text-align: left !important;
}
/* Card content: left-align everything (was center-aligned via Elementor
   widget settings; mismatch with left-aligned bullets felt jarring per
   operator). */
body.page-id-700 [data-sw-section="pillars-intro"] [data-sw-template="pillar-card"] .elementor-heading-title,
body.page-id-700 [data-sw-section="pillars-intro"] [data-sw-template="pillar-card"] .elementor-widget-text-editor,
body.page-id-700 [data-sw-section="pillars-intro"] [data-sw-template="pillar-card"] .elementor-widget-text-editor p,
body.page-id-700 [data-sw-section="pillars-intro"] [data-sw-template="pillar-card"] .sw-pillar-deliverables {
  text-align: left !important;
}

/* 2026-05-14 (hero-consolidation Phase 5): retired M4-fix-5 home hero padding
   override (`body.page-id-700 [data-sw-section="hero"]` { 80/64/48/48 }).
   Selector specificity (0,2,0) loses to the locked-surface hero padding rule
   at line ~760 (specificity 0,3,1) which is also `!important`. Live probe
   2026-05-14 confirmed home hero pt/pb=144 (locked clamp ceiling at 1440px),
   not 80/64. Rule was dead-code. Pillar-card padding override below preserved.

   Inner-padding zeroing rule below kept — that one wins (uses .elementor-700
   chain for specificity bump) and produces the observed innerPt/Pb=0px. */
/* Phase D (2026-05-14): split this rule — `.sw-hero-text-col` keeps its
   padding: 0 (home-only home hero text-column primitive), but the hero
   `.e-con-inner` zeroing is RETIRED in favor of the sitewide canonical
   hero inner-padding rule (added near the end of file). Home hero now
   inherits the canonical 10/10 inner, matching every other P3 hero. */
body.page-id-700 .elementor-700 .sw-hero-text-col {
  padding: 0 !important;
}

/* M4-fix-6 (2026-05-10): pillars + footer + end-CTA buttons all
   stacking vertically because parent flex containers default to
   flex-direction: column. Restore intended multi-column layouts. */

/* Pillars: 3 cards in a row, eyebrow + h2 span full width above */
body.page-id-700 [data-sw-section="pillars-intro"] > .e-con-inner {
  display: grid !important;
  grid-template-columns: repeat(3, 1fr) !important;
  gap: 24px !important;
  align-items: stretch !important;
}
body.page-id-700 [data-sw-section="pillars-intro"] > .e-con-inner > .elementor-widget-heading,
body.page-id-700 [data-sw-section="pillars-intro"] > .e-con-inner > .elementor-widget-text-editor {
  grid-column: 1 / -1 !important;
}
@media (max-width: 900px) {
  body.page-id-700 [data-sw-section="pillars-intro"] > .e-con-inner {
    grid-template-columns: 1fr !important;
  }
}

/* M4-fix-6g (2026-05-10): operator clarification — pillars rhythm fix.
   Real diagnosis: there's NO text-editor between h2 and cards (just
   eyebrow + h2 + 3 cards), so the prior text-editor margin-bottom was
   inert. AND the F0 .sw-locked-surface inner pb of 192px (specificity
   0,6,1) was overpowering my section pb override.

   Fix:
   1) Push cards away from h2 by adding margin-bottom on the second
      heading widget (the h2; eyebrow is .sw-chapter-ebrow on widget #1).
   2) Override the F0 inner pb at matching specificity + later source
      order (uses sw-locked-surface chain + same :not() pair so it ties
      then wins on cascade order). Inner pt stays at 192 to keep the
      rhythm above the eyebrow consistent with siblings. */
body.page-id-700 [data-sw-section="pillars-intro"] > .e-con-inner > .elementor-widget-heading:not(.sw-chapter-ebrow) {
  margin-bottom: clamp(48px, 6vw, 80px) !important;
}
/* M4-fix-6h pillars-intro inner pb override REMOVED 2026-05-11: the values
   (120 desktop / 72 mobile) are now provided by the F0 baseline above, so
   this block became redundant. The per-page section padding-bottom: 0 rule
   is kept since it's a structural zeroing on top of the inner padding —
   not part of the chrome adjustment. */
body.page-id-700 [data-sw-section="pillars-intro"],
body.page-id-123633 [data-sw-section="pillars"] {
  padding-bottom: 0 !important;
}

/* Footer: 5-column grid (brand + 4 link cols).
   The 5 col children live inside .e-con-inner (Elementor's nested
   wrapper), not as direct children of footer-grid. Target the inner. */
[data-sw-block="footer"] [data-sw-role="footer-grid"] > .e-con-inner {
  display: grid !important;
  grid-template-columns: 1.4fr 1fr 1fr 1fr 1fr !important;
  gap: 40px !important;
  align-items: start !important;
}
@media (max-width: 900px) {
  [data-sw-block="footer"] [data-sw-role="footer-grid"] > .e-con-inner {
    grid-template-columns: 1fr 1fr !important;
    gap: 32px !important;
  }
}
@media (max-width: 600px) {
  [data-sw-block="footer"] [data-sw-role="footer-grid"] > .e-con-inner {
    grid-template-columns: 1fr !important;
    gap: 28px !important;
  }
}

/* End-CTA layout (generalized 2026-05-10 cleanup wave — item 1):
   flex-row-wrap parent with full-row headings + auto-width buttons.
   Headings + sub each occupy a full row; the 2 buttons sit side-by-
   side on a final row centred.

   Previous M4-fix-6c was scoped to page-ids 700 / 698 / 123633 and
   only worked on e-con-full sections (home). e-con-boxed surfaces
   (about-us / services-hub / 5 product CPT pages / archive 123641)
   nest the widgets inside `.e-con-inner` whose own flex-column
   layout wins, leaving buttons stacked. Generalized selector below
   targets BOTH the section AND its `.e-con-inner` so whichever
   container actually holds the widgets becomes the flex-row-wrap
   parent; widget-level rules cover both nesting depths. */
[data-sw-section="cta-band"],
[data-sw-section="cta-band"] > .e-con-inner,
[data-sw-section="end-cta"],
[data-sw-section="end-cta"] > .e-con-inner {
  text-align: center !important;
  display: flex !important;
  flex-direction: row !important;
  flex-wrap: wrap !important;
  justify-content: center !important;
  align-items: center !important;
  gap: 12px !important;
}
[data-sw-section="cta-band"] > .elementor-widget-heading,
[data-sw-section="cta-band"] > .elementor-widget-text-editor,
[data-sw-section="cta-band"] > .e-con-inner > .elementor-widget-heading,
[data-sw-section="cta-band"] > .e-con-inner > .elementor-widget-text-editor,
[data-sw-section="end-cta"] > .elementor-widget-heading,
[data-sw-section="end-cta"] > .elementor-widget-text-editor,
[data-sw-section="end-cta"] > .e-con-inner > .elementor-widget-heading,
[data-sw-section="end-cta"] > .e-con-inner > .elementor-widget-text-editor {
  flex: 0 0 100% !important;
  text-align: center !important;
  --container-widget-width: 100% !important;
}
[data-sw-section="cta-band"] > .elementor-widget-button,
[data-sw-section="cta-band"] > .e-con-inner > .elementor-widget-button,
[data-sw-section="end-cta"] > .elementor-widget-button,
[data-sw-section="end-cta"] > .e-con-inner > .elementor-widget-button {
  flex: 0 0 auto !important;
  width: auto !important;
  --container-widget-width: auto !important;
  --container-widget-flex-grow: 0 !important;
}
/* 2026-06-03 — give the CTA button row breathing room from the descriptive
   copy above it ("feels quite cramped"). The uniform 12px flex gap sat the
   buttons too close to the lede in the Elementor-button CTA bands (home /
   services-hub / about-us end-cta). Add the space BELOW the lede (the heading
   or text-editor directly preceding the first button) rather than above the
   buttons: that opens room above the button row without (a) breaking button
   alignment or (b) wedging extra space between buttons when they stack on
   mobile.
   The gap uses var(--space-4) — the SAME token the product cta-bands already
   apply via `.sw-cta-row { margin-top: var(--space-4) }` — so the lede→button
   gap is consistent (~44px) across every bottom CTA: the Elementor-button
   bands here and the HTML-widget (.sw-cta-row) product bands. Covers both CTA
   section names and both Elementor nesting depths. */
[data-sw-section="cta-band"] > .elementor-widget-heading:has(+ .elementor-widget-button),
[data-sw-section="cta-band"] > .elementor-widget-text-editor:has(+ .elementor-widget-button),
[data-sw-section="cta-band"] > .e-con-inner > .elementor-widget-heading:has(+ .elementor-widget-button),
[data-sw-section="cta-band"] > .e-con-inner > .elementor-widget-text-editor:has(+ .elementor-widget-button),
[data-sw-section="end-cta"] > .elementor-widget-heading:has(+ .elementor-widget-button),
[data-sw-section="end-cta"] > .elementor-widget-text-editor:has(+ .elementor-widget-button),
[data-sw-section="end-cta"] > .e-con-inner > .elementor-widget-heading:has(+ .elementor-widget-button),
[data-sw-section="end-cta"] > .e-con-inner > .elementor-widget-text-editor:has(+ .elementor-widget-button) {
  margin-bottom: var(--space-4) !important;
}

/* ============================================================
   Targeted dark-bake overrides (2026-05-10, revised)
   --------------------------------------------------------------
   Replaces a broader page-id-wide revert block that over-corrected
   /audit/, /newsletter/, /contact-us/, and /products/ archive
   (it forced their intentional dark heros + cta-bands to light).
   These narrow rules fix the specific elements with stale Elementor
   JSON dark hex backgrounds without disturbing intentional darks.
   ============================================================ */

/* About-us commitments band (3 cards) — Elementor JSON has dark
   #132338 baked in to the .e-con-boxed children. Force light per
   M1.2 design (light cards on soft band). NOTE: scoped to
   :not(.sw-dark-twin) so Wave 3.B dark canvas inverts via the EOF
   block below. */
body.page-id-698:not(.sw-dark-twin) [data-sw-section="commitments"] > .e-con-inner > .e-con.e-con-boxed,
body.page-id-698:not(.sw-dark-twin) [data-sw-section="commitments"] .e-con.e-con-boxed:not([data-sw-section]) {
  background-color: #FAFAF7 !important;
  border-color: rgb(11 27 43 / 10%) !important;
}
body.page-id-698:not(.sw-dark-twin) [data-sw-section="commitments"] .e-con.e-con-boxed .elementor-heading-title {
  color: #0B1B2B !important;
}
body.page-id-698:not(.sw-dark-twin) [data-sw-section="commitments"] .e-con.e-con-boxed .elementor-widget-text-editor p,
body.page-id-698:not(.sw-dark-twin) [data-sw-section="commitments"] .e-con.e-con-boxed .elementor-widget-text-editor li {
  color: #2A2D34 !important;
}

/* /products/ archive (page-id-123641) — tonal alternation
   Phase 6 (2026-05-14): bands restructured to root-level e-con-boxed
   siblings matching the canonical home/about-us shape. New sequence:
   hero(soft) → products(dark) → about(soft) → methodology(dark) → cta(soft).
   methodology stays at position 4 (the dark accent slot, matching home).
   Tokens replace #0B1B2B legacy literal. */
body.page-id-123641 .elementor-element.elementor-element-8231657,  /* hero */
body.page-id-123641 .elementor-element.elementor-element-5a84558,  /* about */
body.page-id-123641 .elementor-element.elementor-element-c9ad4c6 { /* cta-band */
  background-color: var(--bg-soft) !important;
}
body.page-id-123641 .elementor-element.elementor-element-aad818e,  /* products */
body.page-id-123641 .elementor-element.elementor-element-1d0fb80 { /* methodology */
  background-color: var(--bg) !important;
}

/* ============================================================
   sw-darkform-page — FluentForm dark-canvas binding (2026-05-10)
   --------------------------------------------------------------
   /audit/ and /newsletter/ both have FluentForms living inside
   intentional dark heros. PHP body-class.php adds the
   `sw-darkform-page` class to these pages. The CSS rules that
   originally styled the form for the dark canvas (F8 / M-030)
   were lost when PR #30 deleted the iter-6.x dark-canvas block.
   Restoring them here.
   ============================================================ */
body.sw-darkform-page .fluentform label,
body.sw-darkform-page .fluentform .ff-el-section-title,
body.sw-darkform-page .fluentform .ff_form_name,
body.sw-darkform-page .fluentform .ff-el-input--label,
body.sw-darkform-page .fluentform .ff-el-input--content,
body.sw-darkform-page .fluentform .ff-section_break_desk,
body.sw-darkform-page .fluentform .ff-section_break_desk h1,
body.sw-darkform-page .fluentform .ff-section_break_desk h2,
body.sw-darkform-page .fluentform .ff-section_break_desk h3,
body.sw-darkform-page .fluentform .ff-section_break_desk h4,
body.sw-darkform-page .fluentform .ff-section_break_desk p,
body.sw-darkform-page .fluentform .ff-el-tnc {
  color: #F5F7FA !important;
}
body.sw-darkform-page .fluentform .ff-el-form-control,
body.sw-darkform-page .fluentform input[type="text"],
body.sw-darkform-page .fluentform input[type="email"],
body.sw-darkform-page .fluentform input[type="tel"],
body.sw-darkform-page .fluentform input[type="url"],
body.sw-darkform-page .fluentform input[type="number"],
body.sw-darkform-page .fluentform textarea,
body.sw-darkform-page .fluentform select {
  background-color: rgb(245 247 250 / 6%) !important;
  border-color: rgb(245 247 250 / 30%) !important;
  color: #F5F7FA !important;
}
body.sw-darkform-page .fluentform .ff-el-form-control::placeholder,
body.sw-darkform-page .fluentform textarea::placeholder {
  color: rgb(245 247 250 / 55%) !important;
}
body.sw-darkform-page .fluentform .ff-el-form-control:focus {
  border-color: #5EA8E3 !important;
  outline: none !important;
}
/* Required-field asterisk + helper text stays readable */
body.sw-darkform-page .fluentform .ff-el-is-required.asterisk-right label::after,
body.sw-darkform-page .fluentform .ff-el-help-message,
body.sw-darkform-page .fluentform .ff-el-input--label .ff-el-is-required {
  color: rgb(245 247 250 / 65%) !important;
}

/* Blog archive card title (h2.entry-title in Blocksy's default card layout).
   Was rendering at 36px regardless of viewport — too big on narrow widths
   per operator review 2026-05-11. Replaced with a responsive clamp:
   20 mobile / 28 ceiling. */
.entry-card .entry-title,
.entry-card h2.entry-title,
article .entry-title {
  font-size: clamp(20px, 3vw, 28px) !important;
  line-height: 1.2 !important;
}

/* Locked-surface Blocksy chrome suppression (2026-05-11). Regression caught on
   /products/ (page 123641): the WP page-title hero block + 60px container
   padding + entry-content trailing padding were exposing cream gaps above the
   first dark band and below the last band. Other locked pages (about-us,
   services-hub, audit, newsletter, home, contact-us) hide these via Blocksy
   per-page meta; this rule generalizes the same effect to every locked
   surface so future locked pages inherit it. */
body.sw-locked-surface .hero-section.is-width-constrained { display: none !important; }
body.sw-locked-surface .ct-container-full {
  padding-top: 0 !important;
  padding-bottom: 0 !important;
}
body.sw-locked-surface article.page > .entry-content {
  padding-top: 0 !important;
  padding-bottom: 0 !important;
  margin-top: 0 !important;
  margin-bottom: 0 !important;
}

/* Sitewide header + footer hairlines (2026-05-11). Replaces the retired rust
   divider concept (CLAUDE.md hard rule #1). var(--blue-bright) #4A9DC8 is the
   brand accent that replaced rust in PR #41; it stays visible against both
   dark-canvas footer/header and light-canvas content bands, so a single rule
   covers every page regardless of adjacent band color. */
.ct-header,
header[role="banner"],
#header {
  border-bottom: 1px solid var(--blue-bright);
}
/* 2026-05-27: added #header fallback. The .ct-header / header[role="banner"]
   selectors don't match this site's header markup (it renders as plain
   `<header id="header">` with no class or role), so the hairline was a no-op
   on every page — same selector-mismatch bug the footer hairline had (fixed
   2026-05-14 via the #footer fallback below). Without the line, the dark
   header (#0B1B2B) abutted the dark product hero (#0A1626) as a muddy two-tone
   band. The brand-blue rule resolves to #4A9DC8 on light pages, #8FC9F7 under
   body.sw-dark-twin, bracketing the page symmetrically with the footer. */
.ct-footer,
footer[role="contentinfo"],
#footer,
.ct-footer-empty + footer,
body > footer {
  border-top: 1px solid var(--blue-bright);
}
/* 2026-05-14: added #footer + body > footer + .ct-footer-empty fallback
   selectors. The original .ct-footer / footer[role="contentinfo"]
   selectors don't match this site's footer markup (it renders as plain
   `<footer id="footer">` with no class or role attribute), so the
   hairline was a no-op on every page. The expanded selector list now
   catches the actual footer element across the site + leaves the
   original selectors in place for future themes that emit them. */

/* /products/ archive (page 123641) — structural overrides 2026-05-11.
   The 5-band template was authored with bg-soft on the inner bands while the
   outer wrapper kept 120px padding-inline. Result: each "soft" band painted
   1560px wide inside a 1800px dark frame, reading as an inset card rather
   than a full-bleed band. Bands 2-5 were also authored with padding_block: 10
   which overrode the locked [data-sw-section] vertical rhythm via Elementor's
   per-element specificity (M-024 gotcha). And the hero h1 wraps to 4 lines
   because its heading widget is column-constrained. All three are fixed here
   without re-authoring the JSON, so the same page can be regenerated from
   Elementor MCP later without losing this layout. */
body.elementor-page.page-id-123641 [data-sw-section="hero"] h1 {
  margin-inline: auto !important;
}
/* The custom .hero-h1 class bypasses the locked [data-sw-section="hero"] h1
   selector (which expects .elementor-heading-title from Elementor's heading
   widget). Re-apply the same clamp + weight tokens here. */
body.elementor-page.page-id-123641 .hero-h1 {
  font-size: clamp(22px, 3.5vw, 40px) !important;
  font-weight: 600 !important;
  line-height: 1.05 !important;
  letter-spacing: -0.012em !important;
}
/* Band .section-h2 inherits Blocksy's --size-h2 (clamps to 52px ceiling) —
   too heavy for the design system h2 tier. Bring down to match the rest of
   the marketing surfaces. Phase 6F (2026-05-14): tightened from
   clamp(24,3.5vw,40) → clamp(28,3vw,36) to match detail-page h2 scaling
   exactly so /products/ harmonises with /products/<product>/ pages. */
body.elementor-page.page-id-123641 .section-h2 {
  font-size: clamp(28px, 3vw, 36px) !important;
  font-weight: 600 !important;
  line-height: 1.15 !important;
}

/* Methodology dark band step-tag — restore the lighter cyan #8FC9F7
   (M1 default) per operator preference ("↳ ~2 weeks needs the lighter
   blue"). Higher specificity than the .sw-band-cream rules above + the
   sitewide methodology rule, scoped to /products/ page. */
body.elementor-page.page-id-123641 .sw-band-methodology .step-tag,
body.elementor-page.page-id-123641 [data-sw-section="methodology"] .step-tag {
  color: #8FC9F7 !important;
}

/* /shop/ (page 118231) — collapse the orphaned 700px empty section.
   .elementor-element-149bbec3 has min-height: 700px from Elementor JSON
   but its only child (.elementor-widget-icon 12e70987) carries
   elementor-hidden-desktop + elementor-hidden-tablet + elementor-hidden-
   mobile so the widget is display:none on every viewport. Result is a
   700px-tall empty gap between the product grid and the brand-logos band.
   Hide the wrapper section entirely. */
body.elementor-page.page-id-118231 .elementor-element-149bbec3 {
  display: none !important;
}

/* === Dark-twin sub-brand product pages — button WCAG fixes (2026-05-11) ===
   WCAG audit on /products/syteops/ (and sibling sub-brand pages) flagged
   two button patterns failing contrast:

   1. Elementor secondary buttons (widget IDs b0002 + b0004) authored with
      dark ink text + dark border for cream canvas; on dark-twin sub-brand
      pages this renders dark-on-dark = invisible (1.04:1). The template
      pattern is shared across all 5 sub-brand pages (sytehero/-ltd,
      syteops/-ltd, trustily), so target by Elementor widget ID.

   2. Custom .btn-primary / .btn-ghost classes in the M1 pricing-block
      HTML widget render at browser-default link color (rgb(91,56,237)
      purple) on dark — 2.44:1. Add explicit dark-twin styling. */

body.sw-dark-twin .elementor-element-b0002 a.elementor-button,
body.sw-dark-twin .elementor-element-b0004 a.elementor-button {
  color: var(--ink) !important;
  border-color: rgb(245 247 250 / 35%) !important;
}
body.sw-dark-twin .elementor-element-b0002 a.elementor-button:hover,
body.sw-dark-twin .elementor-element-b0004 a.elementor-button:hover {
  background-color: rgb(245 247 250 / 8%) !important;
  border-color: rgb(245 247 250 / 60%) !important;
}

body.sw-dark-twin a.btn,
body.sw-dark-twin .sw-pricing-tier a.btn {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  padding: 14px 24px;
  font-family: var(--font-sans, "Inter", system-ui, sans-serif);
  font-size: 15px;
  font-weight: 600;
  letter-spacing: 0.005em;
  border-radius: 6px;
  text-decoration: none;
  border: 1.5px solid transparent;
  transition: background 0.18s ease;
}
body.sw-dark-twin a.btn-primary {
  /* Deep brand blue + paper text — NOT var(--blue), which flips to the #5EA8E3
     light tint on dark-twin (see memory feedback-blue-token-flips-on-dark-twin).
     Matches the Elementor-widget primaries on home/services-hub + the contact
     CTA primary. Hover goes deep navy (#0B1B2B), not the lighter blue-bright,
     so the paper text keeps AA contrast. */
  background-color: #1F5F85 !important;
  color: #FAFAF7 !important;
  border-color: #1F5F85 !important;
}
body.sw-dark-twin a.btn-primary:hover {
  background-color: #0B1B2B !important;
  color: #FAFAF7 !important;
  border-color: #0B1B2B !important;
}
body.sw-dark-twin a.btn-ghost {
  background-color: transparent !important;
  color: var(--ink) !important;
  border-color: rgb(245 247 250 / 35%) !important;
}
body.sw-dark-twin a.btn-ghost:hover {
  background-color: rgb(245 247 250 / 8%) !important;
  border-color: rgb(245 247 250 / 60%) !important;
}

/* 6. Methodology dark band — re-explicit paper text. The M1 block at L3445-L3471
   sets section-h2 / lede / p to paper colors, but those rules lose cascade to
   the .sw-dark-twin neutralizer above which sets color: #0B1B2B !important on
   the wrapper. Re-state the paper colors here with the page-id specificity
   chain + !important so methodology stays readable on its dark canvas. */
body.elementor-page.page-id-123641 [data-sw-section="methodology"],
body.elementor-page.page-id-123641 [data-sw-section="methodology"] h1,
body.elementor-page.page-id-123641 [data-sw-section="methodology"] h2,
body.elementor-page.page-id-123641 [data-sw-section="methodology"] h3,
body.elementor-page.page-id-123641 [data-sw-section="methodology"] .section-h2,
body.elementor-page.page-id-123641 [data-sw-section="methodology"] .hero-h1 {
  color: #F5F7FA !important;
}
body.elementor-page.page-id-123641 [data-sw-section="methodology"] p,
body.elementor-page.page-id-123641 [data-sw-section="methodology"] p.lede,
body.elementor-page.page-id-123641 [data-sw-section="methodology"] .lede,
body.elementor-page.page-id-123641 [data-sw-section="methodology"] .step p {
  color: rgb(245 247 250 / 82%) !important;
}
body.elementor-page.page-id-123641 [data-sw-section="methodology"] .step {
  background-color: rgb(245 247 250 / 4%) !important;
  border: 1px solid rgb(245 247 250 / 8%);
  /* 2026-05-14: was padding: 24px — circles sat ~5px from card top
     (no breathing room). Bumped top to 40 so the 36px circle sits
     clearly inside the card border with visible space above it. */
  padding: 40px 24px 24px !important;
  border-radius: 6px;
}

/* 8. About band ("Built by SyteWide") was authored as a 2-column grid via
   .about-inner (379px + 759px), pinching the h2 in the narrow left column.
   Collapse to single-column so the h2 uses the full band width. */
body.elementor-page.page-id-123641 [data-sw-section="about"] .about-inner {
  display: block !important;
  grid-template-columns: 1fr !important;
  /* 2026-05-22 — center the whole 1-col stack on the canvas and cap to a
     reading width so eyebrow + h2 + prose + link share a common block.
     Previously each child had a different max-width left-anchored to the
     canvas edge, producing a ragged right edge.
     Updated 2026-05-27: 760 → var(--width-prose) (= 720). −40px reading
     column. The 760 was a one-off; sitewide convention is 720 (~64ch at
     18px body) per widths.md (prose tier). This was the migration that
     triggered the audit. */
  max-width: var(--width-prose) !important;
  margin-left: auto !important;
  margin-right: auto !important;
}
body.elementor-page.page-id-123641 [data-sw-section="about"] .about-eyebrow-col {
  max-width: 100% !important;
  width: 100% !important;
  text-align: center !important;
}
/* Eyebrow + h2 centered above the prose. */
body.elementor-page.page-id-123641 [data-sw-section="about"] .sw-chapter-ebrow,
body.elementor-page.page-id-123641 [data-sw-section="about"] h2 {
  text-align: center !important;
  margin-left: auto !important;
  margin-right: auto !important;
}
body.elementor-page.page-id-123641 [data-sw-section="about"] h2 {
  max-width: 100% !important;
  margin-bottom: 32px !important;
}
/* Body prose: block is centered (inherits from about-inner cap above),
   but text stays left-aligned for body readability. */
body.elementor-page.page-id-123641 [data-sw-section="about"] .about-prose {
  max-width: 100% !important;
  width: 100% !important;
  text-align: left !important;
}
/* Center the "More about SyteWide →" link affordance under the prose. */
body.elementor-page.page-id-123641 [data-sw-section="about"] .about-link {
  display: inline-flex !important;
  margin-left: auto !important;
  margin-right: auto !important;
}
body.elementor-page.page-id-123641 [data-sw-section="about"] .about-prose {
  text-align: left;
}
body.elementor-page.page-id-123641 [data-sw-section="about"] .about-prose .about-link {
  /* Keep the link visually centered relative to the prose column. */
  display: block !important;
  width: max-content !important;
}

/* 9. Remove the empty-space tail below the last band before the footer.
   The page's article > .entry-content > .elementor wrapper had a bottom
   margin that re-appeared after the cream-flip.
   Phase F (2026-05-14): retired the `body.elementor-page.page-id-123641
   .elementor-element-8231657 { margin-bottom: 0 }` rule that used to sit
   here — it targeted the HERO outer container, which was correct when
   bands were nested children of the hero. After Phase 6B promoted bands
   to root-level siblings, the cta-band (c9ad4c6) is now the last
   container; hero margin-bottom no longer affects the footer gap.
   The article + .elementor wrapper rule below handles it correctly. */
body.elementor-page.page-id-123641 article.page > .entry-content,
body.elementor-page.page-id-123641 .elementor-123641 {
  margin-bottom: 0 !important;
  padding-bottom: 0 !important;
}

/* === Sitewide inline-emphasis + blue-on-light consistency (2026-05-11) ===
   M1-era component CSS painted <strong>/<em> tags with paper-white text +
   cyan italic accents — that worked on the original dark canvas but washes
   out on every cream marketing surface (home, about-us, services-hub, audit,
   newsletter, products archive, contact-us, blog single). Apply at body.sw-
   locked-surface scope so every marketing surface inherits the same rules.
   The dark methodology accent band + body.sw-dark-twin sub-brand product
   pages are explicitly excluded — they keep paper-on-dark / cyan-on-dark. */

/* 1. Inline emphasis: <strong>/<b>/<em>/<i> inherit surrounding ink color so
      paper-on-cream washouts disappear. Italic is preserved in body prose
      (semantic emphasis on blog quotes / inline tactics-vs-systems contrasts)
      but killed in HEADINGS where em is almost always a brand wordmark accent
      the operator wants normalized. */
body.sw-locked-surface :where(p, li, td, h1, h2, h3, h4, h5, h6) :is(strong, b) {
  color: inherit !important;
  font-weight: 600 !important;
}
body.sw-locked-surface :where(p, li, td, h1, h2, h3, h4, h5, h6) :is(em, i) {
  color: inherit !important;
}
body.sw-locked-surface :where(h1, h2, h3, h4, h5, h6) :is(em, i) {
  font-style: normal !important;
}

/* 2. Cyan #4A9DC8 (var(--blue-bright)) text accents wash out on cream. Force
      var(--blue) (#1F5F85, deeper navy) for in-prose links + accent chips +
      step tags + about-link callouts on every locked marketing surface. */
body.sw-locked-surface :where(p, li, td) a,
body.sw-locked-surface .about-link,
body.sw-locked-surface .chip-product,
body.sw-locked-surface .step-tag {
  color: var(--blue) !important;
}

/* 3. Dark methodology bands inside locked surfaces — re-apply paper colors so
      strong/em/links stay readable on the dark accent canvas. */
body.sw-locked-surface [data-sw-section="methodology"] :where(p, li, td) :is(strong, b),
body.sw-locked-surface .sw-band-methodology :where(p, li, td) :is(strong, b) {
  color: rgb(245 247 250) !important;
}
body.sw-locked-surface [data-sw-section="methodology"] :where(p, li, td) a,
body.sw-locked-surface [data-sw-section="methodology"] .about-link,
body.sw-locked-surface [data-sw-section="methodology"] .chip-product {
  color: var(--blue-bright) !important;
}
/* Step-tag uses the lighter blue #8FC9F7 (operator preference 2026-05-11)
   for the methodology timeline cards on every locked surface. */
body.sw-locked-surface [data-sw-section="methodology"] .step-tag {
  color: #8FC9F7 !important;
}

/* 4. Dark-twin sub-brand pages (body.sw-dark-twin) — exempt from the cream-canvas
      flip; cyan + paper-on-dark stays correct. */
body.sw-dark-twin :where(p, li, td) :is(strong, b) {
  color: rgb(245 247 250) !important;
  font-weight: 600 !important;
}
body.sw-dark-twin :where(p, li, td) a,
body.sw-dark-twin .about-link,
body.sw-dark-twin .chip-product,
body.sw-dark-twin .step-tag {
  color: var(--blue-bright) !important;
}

/* === Footer always dark canvas (2026-05-11) ===
   The Blocksy footer Content Block (122845) renders a wrapper with class
   .sw-dark-twin AND attribute data-sw-section="hero" — those overlap with
   page-id-123641's cream-flip rules and the footer got painted cream. The
   footer should stay dark on EVERY page regardless of canvas direction
   above it. Use html.elementor-default body prefix to bump specificity
   above the page-id-123641 .sw-dark-twin cream-flip rule (specificity 0,3,1)
   without coupling the footer fix to any specific page. */
body.elementor-page.sw-locked-surface.elementor-default footer .sw-dark-twin,
body.elementor-page.sw-locked-surface.elementor-default footer [data-sw-section],
body.elementor-page.sw-locked-surface.elementor-default .ct-footer .sw-dark-twin,
body.elementor-page.sw-locked-surface.elementor-default .ct-footer [data-sw-section] {
  background-color: #0A1626 !important;
  color: #F5F7FA !important;
  --bg: #0A1626;
  --bg-soft: #132338;
  --ink: #F5F7FA;
  --mute: #8B9AAE;
  --theme-headings-color: #F5F7FA;
  --theme-text-color: #F5F7FA;
}
/* Footer text colour overrides — the cream-flip rule
   `body.elementor-page.page-id-123641 [data-sw-section="hero"] p` (0,3,2)
   was beating my (0,2,3) override on descendants. Adding .elementor-default
   to the chain bumps to (0,3,3) which wins. Also widen the matched elements
   to include div/h1-h6/spans/inline emphasis so the tagline + copyright
   paragraph (which renders inside a heading widget div) gets paper. */
body.elementor-page.sw-locked-surface.elementor-default footer :is(h1, h2, h3, h4, h5, h6, p, li, span, em, i, strong, b, div),
body.elementor-page.sw-locked-surface.elementor-default .ct-footer :is(h1, h2, h3, h4, h5, h6, p, li, span, em, i, strong, b, div) {
  color: #F5F7FA !important;
}
body.elementor-page.sw-locked-surface.elementor-default footer a,
body.elementor-page.sw-locked-surface.elementor-default .ct-footer a {
  color: var(--blue-bright) !important;
}

/* === Products archive (page 123641) — remaining cyan-on-cream accents ===
   .num chips ("A · Operations" / "B · Storefront" / "C · Reputation") and
   the ↳ bullet ::before were authored with cyan #8FC9F7 — same wash-out
   pattern as the rest. Flip to var(--blue) navy for proper cream contrast. */
body.elementor-page.page-id-123641 .product-card .num,
body.elementor-page.page-id-123641 .product-card ul li::before {
  color: var(--blue) !important;
}

/* ───────────────────────────────────────────────────────────────────────────
   M-products-redesign (2026-05-12) — bordered tile primitive.
   Restores the P3-era boxed look retired by the 2026-05-07 audit's PR-FEAT-01.
   Operator preference (2026-05-12 brainstorm Q2) overrides the audit retirement.
   Specificity uses body[class*="postid-"] to win against Elementor per-element
   padding rules; see memory feedback-elementor-flex-width-pct.md.
   ─────────────────────────────────────────────────────────────────────── */
.sw-tile {
  background: var(--bg);
  border: 1px solid var(--rule-strong);
  border-radius: var(--radius-sm);
  padding: var(--space-4);
  text-align: left;
  transition: border-color 0.18s ease, transform 0.18s ease, box-shadow 0.18s ease;
  /* Flex column so an optional bullet `<ul>` can anchor at the bottom
     (margin-top: auto below) and align across cards in a row. Grid already
     stretches cards to equal height; this aligns the rule + bullets too. */
  display: flex;
  flex-direction: column;
}
.sw-tile:hover {
  border-color: var(--ink);
  transform: translateY(-2px);
  box-shadow: var(--shadow-card);
}
body[class*="postid-"] .sw-band-soft .sw-tile { background: var(--bg); }
body[class*="postid-"] .sw-band-dark .sw-tile {
  background: rgb(255 255 255 / 4%);
  border-color: rgb(255 255 255 / 14%);
}
body[class*="postid-"] .sw-band-dark .sw-tile:hover {
  border-color: rgb(255 255 255 / 36%);
}

.sw-tile-num {
  font: 500 var(--size-eyebrow)/1.2 var(--font-mono);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--blue);
  display: block;
  margin-bottom: var(--space-2);
}
body[class*="postid-"] .sw-band-dark .sw-tile-num { color: var(--blue-bright); }

.sw-tile h3 { margin-bottom: var(--space-2); }
.sw-tile p {
  font-size: var(--size-body);
  color: var(--ink-700);
  line-height: 1.6;
}
.sw-tile ul {
  list-style: none;
  /* `auto` pushes the bullet list to the bottom of the flex column so the
     rule + bullets align across cards regardless of body-paragraph length. */
  margin-top: auto;
  padding-top: var(--space-3);
  border-top: 1px solid var(--rule);
  font-size: var(--size-meta);
  color: var(--mute);
}
.sw-tile li {
  padding: 4px 0;
  padding-left: 24px;
  position: relative;
}
.sw-tile li::before {
  content: "→";
  position: absolute;
  left: 0;
  color: var(--blue-bright);
}

.sw-tile-grid {
  display: grid;
  gap: var(--space-3);
  margin-top: var(--space-5);
}
.sw-tile-grid.cols-3 { grid-template-columns: repeat(3, 1fr); }
.sw-tile-grid.cols-2 { grid-template-columns: repeat(2, 1fr); }
@media (max-width: 900px) {
  .sw-tile-grid.cols-3,
  .sw-tile-grid.cols-2 { grid-template-columns: 1fr; }
}

.sw-dashboard-frame {
  background: var(--bg);
  border: 1px solid var(--rule-strong);
  border-radius: var(--radius-sm);
  box-shadow: var(--shadow-card);
  margin-top: var(--space-5);
  overflow: hidden;
  text-align: left;
}
.sw-dashboard-chrome {
  display: flex;
  gap: 6px;
  padding: 12px var(--space-3);
  background: var(--bg-soft);
  border-bottom: 1px solid var(--rule);
  align-items: center;
}
.sw-dashboard-chrome::before {
  content: "";
  width: 12px;
  height: 12px;
  border-radius: 999px;
  background: var(--rule-strong);
  box-shadow: 18px 0 0 var(--rule-strong), 36px 0 0 var(--rule-strong);
}
.sw-dashboard-chrome span {
  margin-left: 60px;
  font: 500 var(--size-meta)/1.2 var(--font-mono);
  color: var(--mute);
  letter-spacing: 0.04em;
}
.sw-dashboard-body {
  padding: var(--space-4);
  display: grid;
  gap: var(--space-3);
  grid-template-columns: 1fr 2fr;
}
.sw-dashboard-side {
  border-right: 1px solid var(--rule);
  padding-right: var(--space-3);
}
.sw-dashboard-side h4 {
  font: 500 var(--size-meta)/1.4 var(--font-mono);
  text-transform: uppercase;
  letter-spacing: 0.12em;
  color: var(--mute);
  margin-bottom: var(--space-2);
}
.sw-dashboard-side ul { list-style: none; font-size: 14px; padding: 0; margin: 0; }
.sw-dashboard-side li {
  padding: 8px 0;
  border-bottom: 1px solid var(--rule);
  color: var(--ink-700);
}
.sw-dashboard-side li:last-child { border-bottom: 0; }
.sw-dashboard-side .active { color: var(--blue); font-weight: 500; }
.sw-dashboard-main h4 {
  font: 600 18px/1.3 var(--font-sans);
  margin-bottom: var(--space-2);
}
.sw-dash-stats {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--space-2);
  margin-top: var(--space-3);
}
.sw-dash-stat {
  background: var(--bg-soft);
  border: 1px solid var(--rule);
  border-radius: var(--radius-sm);
  padding: var(--space-3);
}
.sw-dash-stat .num { font: 600 28px/1 var(--font-sans); color: var(--ink); }
.sw-dash-stat .lbl {
  font: 500 11px/1.4 var(--font-mono);
  text-transform: uppercase;
  letter-spacing: 0.12em;
  color: var(--mute);
  margin-top: 6px;
}
.sw-dash-stat .delta { font-size: 12px; color: #1B8F4A; margin-top: 4px; }
.sw-dash-stat .delta.down { color: var(--blue); }
.sw-dash-reviews { margin-top: var(--space-3); display: grid; gap: var(--space-2); }
.sw-dash-review {
  padding: var(--space-2);
  border: 1px solid var(--rule);
  border-radius: var(--radius-sm);
  background: var(--bg);
  font-size: 14px;
}
.sw-dash-review .src {
  font: 500 11px/1.4 var(--font-mono);
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--mute);
}
.sw-dash-review .stars { color: var(--star-gold); font-size: 13px; margin: 4px 0; letter-spacing: 1px; }
.sw-dash-review p { font-size: 14px; color: var(--ink-700); line-height: 1.55; }
.sw-dashboard-caption {
  padding: var(--space-2) var(--space-4) var(--space-4);
  border-top: 1px solid var(--rule);
  font: 500 var(--size-meta)/1.4 var(--font-mono);
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--mute);
  background: var(--bg);
}
@media (max-width: 760px) {
  .sw-dashboard-body { grid-template-columns: 1fr; }
  .sw-dashboard-side {
    border-right: 0;
    border-bottom: 1px solid var(--rule);
    padding-right: 0;
    padding-bottom: var(--space-3);
  }
  .sw-dash-stats { grid-template-columns: 1fr; }
}

.sw-compare-frame {
  margin-top: var(--space-5);
  background: var(--bg);
  border: 1px solid var(--rule-strong);
  border-radius: var(--radius-sm);
  overflow: hidden;
  text-align: left;
}
.sw-compare-row {
  display: grid;
  grid-template-columns: 2fr 1fr 1fr 1fr;
  gap: var(--space-3);
  padding: var(--space-3) var(--space-4);
  border-bottom: 1px solid var(--rule);
  align-items: center;
}
.sw-compare-row:last-child { border-bottom: 0; }
.sw-compare-row.head {
  background: var(--bg-soft);
  font: 500 var(--size-meta)/1.4 var(--font-mono);
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--mute);
}
.sw-compare-row .col-scenario {
  font-weight: 500;
  color: var(--ink);
  font-size: 15px;
}
.sw-compare-row .col-num {
  font: 500 16px/1.2 var(--font-mono);
  color: var(--ink-700);
}
.sw-compare-row .col-num.trustily {
  color: var(--blue);
  font-weight: 600;
}
@media (max-width: 760px) {
  .sw-compare-row { grid-template-columns: 1fr; gap: 6px; }
  .sw-compare-row.head { display: none; }
  .sw-compare-row .col-num::before {
    content: attr(data-label) "  ";
    font: 500 11px/1 var(--font-mono);
    text-transform: uppercase;
    letter-spacing: 0.12em;
    color: var(--mute);
    margin-right: 6px;
  }
}

.sw-pricing-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: var(--space-3);
  margin-top: var(--space-5);
  text-align: left;
}
.sw-pricing-grid.cols-3 { grid-template-columns: repeat(3, 1fr); }
@media (max-width: 900px) {
  .sw-pricing-grid,
  .sw-pricing-grid.cols-3 { grid-template-columns: 1fr; }
}
.sw-pricing-tier {
  background: var(--bg);
  border: 1px solid var(--rule-strong);
  border-radius: var(--radius-sm);
  padding: var(--space-5);
  display: flex;
  flex-direction: column;
}
.sw-pricing-tier.recommended {
  border: 2px solid var(--product-accent);
  background: var(--bg);
  position: relative;
}
/* "Most Popular" badge. Sits on the accent fill, so text uses --ink-fixed
   (always dark navy) — NOT var(--ink), which flips to paper-white on dark-twin
   product pages and would fail WCAG contrast on the gold/teal/coral accent. */
.sw-pricing-tier.recommended::before {
  content: "Most Popular";
  position: absolute;
  top: 0;
  left: 50%;
  transform: translate(-50%, -50%);
  background: var(--product-accent);
  color: var(--ink-fixed);
  font: 600 12px/1 var(--font-mono);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  padding: 6px 12px;
  border-radius: var(--radius-pill);
  white-space: nowrap;
}
.sw-pricing-tier h3 {
  font-size: 20px;
  font-weight: 600;
  letter-spacing: -0.005em;
}
.sw-price {
  font: 600 48px/1 var(--font-sans);
  margin: var(--space-3) 0;
  letter-spacing: -0.02em;
  color: var(--ink);
}
.sw-price small {
  font: 500 14px/1 var(--font-mono);
  color: var(--mute);
  letter-spacing: 0.04em;
  text-transform: uppercase;
}
.sw-pricing-tier ul {
  list-style: none;
  margin-bottom: var(--space-4);
  flex-grow: 1;
  padding: 0;
}
.sw-pricing-tier li {
  padding: 8px 0;
  font-size: var(--size-body);
  color: var(--ink-700);
  border-bottom: 1px solid var(--rule);
  display: flex;
  gap: 10px;
  align-items: flex-start;
}
.sw-pricing-tier li:last-child { border-bottom: 0; }
.sw-pricing-tier li::before {
  content: "✓";
  color: var(--blue);
  font-weight: 600;
}

.sw-testimonials {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--space-3);
  margin-top: var(--space-5);
  text-align: left;
}
@media (max-width: 900px) {
  .sw-testimonials { grid-template-columns: 1fr; }
}
.sw-testimonial {
  background: var(--bg);
  border: 1px solid var(--rule-strong);
  border-radius: var(--radius-sm);
  padding: var(--space-4);
}
.sw-testimonial blockquote {
  font: 500 17px/1.45 var(--font-sans);
  color: var(--ink);
  margin-bottom: var(--space-3);
  padding-left: var(--space-3);
  border-left: 3px solid var(--blue-bright);
}
.sw-testimonial cite {
  display: block;
  font-style: normal;
  font: 500 12px/1.4 var(--font-mono);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--mute);
}
.sw-rating-row {
  display: inline-flex;
  flex-wrap: wrap;
  gap: var(--space-3);
  justify-content: center;
  margin-top: var(--space-3);
  font: 500 13px/1.4 var(--font-mono);
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--mute);
}
.sw-rating-row span { white-space: nowrap; }
.sw-rating-row strong { color: var(--ink); font-weight: 500; }
.sw-rating-row .stars { color: var(--star-gold); letter-spacing: 1px; }

/* T15 — M-products-redesign Phase 1: hero stat-row + CTA-row + buttons + subtext primitives
   ----------------------------------------------------------------------------------------
   Foundational hero-band primitives for the v4 product-page mockup. Class hooks attached
   on the trustily hero band (post 123545) re-author. Sitewide reusable. */
.sw-hero-stat-row {
  display: inline-flex;
  flex-wrap: wrap;
  gap: var(--space-3);
  justify-content: center;
  margin-top: var(--space-4);
  font: 500 13px/1.4 var(--font-mono);
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--mute);
}
.sw-hero-stat-row span { white-space: nowrap; }
.sw-hero-stat-row span + span::before {
  content: "·";
  margin-right: var(--space-3);
  color: var(--rule-strong);
}
@media (max-width: 640px) {
  /* 2026-05-25: this v4 stat-row stacks vertically on mobile (flex-column),
     where the inline "·" separator renders as an orphaned leading bullet on
     each line — drop it (the line breaks already separate the stats). Also
     relax the nowrap so a long stat fragment wraps within its line instead
     of overflowing the hero (the failure mode behind the Trustily clip). */
  .sw-hero-stat-row span + span::before { content: none; }
  .sw-hero-stat-row span { white-space: normal; }
}
.sw-hero-stat-row strong { color: var(--ink); font-weight: 500; }
/* Operator round 4 (2026-05-12): "/ month" plain text inside stat-row was
   reading too dim against the bright strong values. Bump plain-text alpha
   from 70% to 90% so non-strong fragments stay legible. */
body.sw-dark-twin .sw-hero-stat-row { color: rgb(245 247 250 / 90%); }
body.sw-dark-twin .sw-hero-stat-row strong { color: var(--ink); }

.sw-cta-row {
  display: inline-flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  justify-content: center;
  margin-top: var(--space-4);
}
.btn {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font: 500 16px/1 var(--font-sans);
  padding: 14px 22px;
  border-radius: var(--radius-btn);
  text-decoration: none;
  transition: transform 0.15s ease, background 0.15s ease, border-color 0.15s ease, color 0.15s ease;
  cursor: pointer;
}
.btn-primary {
  background: var(--blue);
  color: var(--bg);
  border: 1px solid var(--blue);
}
.btn-primary:hover {
  background: #174A6B;
  border-color: #174A6B;
  transform: translateY(-1px);
}
.btn-ghost {
  background: transparent;
  color: var(--ink);
  border: 1px solid var(--rule-strong);
}
.btn-ghost:hover {
  border-color: var(--ink);
  transform: translateY(-1px);
}
body.sw-dark-twin .btn-ghost { color: var(--bg); border-color: rgb(255 255 255 / 24%); }
body.sw-dark-twin .btn-ghost:hover { border-color: var(--bg); }
.btn-text {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font: 500 16px/1 var(--font-sans);
  color: var(--blue);
  text-decoration: none;
  border-bottom: 1px solid transparent;
  padding-bottom: 2px;
}
.btn-text:hover { border-bottom-color: var(--blue); }
body.sw-dark-twin .btn-text { color: var(--blue-bright); }
body.sw-dark-twin .btn-text:hover { border-bottom-color: var(--blue-bright); }

.sw-subtext {
  font-size: var(--size-body);
  color: var(--mute);
  max-width: 56ch;
  margin: var(--space-3) auto 0;
}

.sw-section-label {
  font: 500 var(--size-eyebrow)/1.2 var(--font-mono);
  text-transform: uppercase;
  letter-spacing: 0.14em;
  color: var(--mute);
}
body[class*="postid-"] .sw-band-dark .sw-section-label { color: var(--blue-bright); }
/* Brand system 2026-05-26 — accent only the leading number (markup split into
   .num/.dot/.label lands via Elementor in Phase 2). Label text keeps the band's
   dim colour via inheritance; the number pops in the per-product accent. */
.sw-section-label .num { color: var(--product-accent); }
.sw-section-label .dot,
.sw-section-label .label { color: inherit; }

[data-sw-section] .sw-band-centered,
.sw-band-centered { text-align: center; }
.sw-band-centered > * { margin-left: auto; margin-right: auto; }
.sw-band-centered p { max-width: 64ch; }

/* ───────────────────────────────────────────────────────────────────────────
   M-products-redesign (2026-05-12) — band canvas primitives.
   Per-band tonal alternation for base product pages + future surfaces that
   opt into the cream-flip pattern. LTD variants still inherit canvas via
   body.sw-dark-twin (no .sw-band-* class needed on those pages).
   Mockup source: docs/developer/audits/product-pages-2026-05-11/mockups/trustily-mockup-v4.html
   ─────────────────────────────────────────────────────────────────────── */
.sw-band-cream { background: var(--bg); }
.sw-band-soft  { background: var(--bg-soft); }
.sw-band-dark  { background: var(--ink); color: var(--bg); }
.sw-band-dark h1,
.sw-band-dark h2,
.sw-band-dark h3 { color: var(--bg); }
.sw-band-dark p { color: rgb(245 247 250 / 80%); }
.sw-band-dark .sw-mute { color: rgb(245 247 250 / 70%); }
.sw-band-dark .sw-section-label { color: var(--blue-bright); }
.sw-band-dark .sw-tile p { color: rgb(245 247 250 / 78%); }
.sw-band-dark .btn-ghost { color: var(--bg); border-color: rgb(255 255 255 / 24%); }
.sw-band-dark .btn-ghost:hover { border-color: var(--bg); }
.sw-band-dark .btn-text { color: var(--blue-bright); }
.sw-band-dark .btn-text:hover { border-bottom-color: var(--blue-bright); }
.sw-band-dark .sw-hero-stat-row { color: rgb(245 247 250 / 70%); }
.sw-band-dark .sw-hero-stat-row strong { color: var(--bg); }

/* Elementor widget chain inserts wrappers between the .sw-band-centered section
   and the actual text content (.e-con-inner > .elementor-widget > .elementor-widget-container).
   text-align doesn't propagate through these unless re-asserted on the inner
   wrapper. Per memory feedback-elementor-flex-width-pct.md — Elementor's
   per-element settings can stomp text-align inherited from the parent. */
body[class*="postid-"] .sw-band-centered > .e-con-inner { align-items: center; }
body[class*="postid-"] .sw-band-centered .elementor-widget,
body[class*="postid-"] .sw-band-centered .elementor-widget-container,
body[class*="postid-"] .sw-band-centered .elementor-heading-title { text-align: center; }
/* The .elementor-heading-title rule above loses on the end-CTA h2 for unknown
   reasons (CSSOM access blocked by CORS in our probe; likely Elementor's
   `default-kit-*.css` per-element rule wins on source order). !important on
   tagged variants ensures h1/h2/h3 headings inside .sw-band-centered always
   center — operator feedback 2026-05-12. */
body[class*="postid-"] .sw-band-centered h1.elementor-heading-title,
body[class*="postid-"] .sw-band-centered h2.elementor-heading-title,
body[class*="postid-"] .sw-band-centered h3.elementor-heading-title,
body[class*="postid-"] .sw-band-centered h4.elementor-heading-title,
body[class*="postid-"] .sw-band-centered h5.elementor-heading-title,
body[class*="postid-"] .sw-band-centered h6.elementor-heading-title { text-align: center !important; }
body[class*="postid-"] .sw-band-centered .elementor-widget-html .sw-hero-stat-row,
body[class*="postid-"] .sw-band-centered .elementor-widget-html .sw-cta-row { justify-content: center; }
/* Tiles, dashboard, compare table, and pricing grid keep their own left-aligned
   content via the .sw-tile / .sw-dashboard-frame / .sw-compare-frame / .sw-pricing-grid
   inline `text-align: left` declarations authored earlier — those wrap the inner
   content and win over the centered widget container. */

/* Operator feedback 2026-05-12 (post-Phase-1 review):
   1. Body paragraphs inside .sw-band-centered have max-width: 64ch but no
      margin auto, so they left-anchor instead of centering. Most visible on
      the end-CTA band. Fix: add auto-auto horizontal margins to centered
      paragraphs.
   2. .sw-tile ul + li inherit `color: var(--mute)` (mid-tone gray) which is
      illegible on .sw-band-dark canvas. Add a paper-muted color override and
      lighten the ul border-top to match.
   3. Footer Blocksy CB container (post 122845 element 503a593) was authored
      with data-sw-section="hero" attribute and inherits the locked hero
      padding clamp(96-144). Override to a much tighter clamp so the footer
      gets normal top/bottom space — copyright line only needs minimal space
      below it. */
body[class*="postid-"] .sw-band-centered p {
  margin-left: auto;
  margin-right: auto;
}
body[class*="postid-"] .sw-band-dark .sw-tile ul {
  color: rgb(245 247 250 / 70%);
  border-top-color: rgb(255 255 255 / 14%);
}
/* Direct li color override — needed because the legacy rule at line ~3759
   `[data-sw-section="pillars"] li { color: var(--ink-700) }` (from a pre-
   M-products-redesign era when pillars rendered on cream-soft) targets li
   directly and beats inheritance from the ul rule above. */
body[class*="postid-"] .sw-band-dark .sw-tile li { color: rgb(245 247 250 / 70%); }
/* Section labels in dark bands render as h6 heading widgets. The css_classes
   "sw-section-label" applied via Elementor MCP needs a regenerated CSS file
   to render as a class on the widget div; meanwhile, target h6 directly in
   .sw-band-dark scope so the eyebrow is legible. Same fix applies to any
   .sw-band-dark surface (§02 pillars, end-CTA on Trustily; LTD pages too). */
body[class*="postid-"] .sw-band-dark h4,
body[class*="postid-"] .sw-band-dark h5,
body[class*="postid-"] .sw-band-dark h6 { color: var(--blue-bright); }
/* Specificity must match the hero exception rule above (0,3,1) since the
   footer container also carries data-sw-section="hero" attribute. Source
   order wins (this rule is later in file). Three selectors cover all body
   class variants so the override works sitewide. */
body.sw-locked-surface:not(.single-post) [data-sw-block="footer"],
body.single-post [data-sw-block="footer"],
body:not(.sw-locked-surface):not(.single-post) [data-sw-block="footer"] {
  padding-top: clamp(8px, 1vw, 16px) !important;
  padding-bottom: clamp(4px, 0.5vw, 12px) !important;
}

/* ───────────────────────────────────────────────────────────────────────────
   M-products-redesign 2026-05-12 OPERATOR PIVOT — all product surfaces dark.
   All 5 product detail pages (fluent-products singulars) + /products/ archive
   (page 123641) render uniform dark canvas via body.sw-dark-twin. The
   .sw-band-* classes authored via Elementor MCP stay on the bands; their
   canvas resolution is normalized here so every band reads dark.

   Specifically:
   - .sw-band-cream + .sw-band-soft already resolve to dark/dark-soft via the
     token remap (--bg = #0A1626, --bg-soft = #132338). No override needed.
   - .sw-band-dark in the dark-twin scope WOULD paper-flip (bg becomes paper
     since --ink = #F5F7FA on dark-twin). Override below keeps it dark.
   - All component primitives (.sw-tile, .sw-pricing-tier, .sw-compare-frame,
     .sw-dashboard-frame) get a dark-themed treatment with subtle paper-tinted
     bg/border so they read against the dark canvas.
   - Pricing recommended-tier border: --blue-bright 2px (operator-chosen
     2026-05-12).
   ─────────────────────────────────────────────────────────────────────── */

/* Stay-dark on .sw-band-dark within sw-dark-twin scope */
body.sw-dark-twin .sw-band-dark {
  background: var(--bg);
  color: var(--ink);
}
/* Operator pivot round 4 (2026-05-12): use data-sw-section attribute to enforce
   strict D/L alternation across product detail pages (7 bands) + /products/
   archive (5 bands). Class-based mapping (.sw-band-cream / .sw-band-soft /
   .sw-band-dark) doesn't alternate cleanly with the existing MCP-authored
   class assignments — data-sw-section is the durable anchor.
   Detail D/L sequence: hero D / dashboard L / pillars D / deliverables L /
     proof D / pricing L / cta-band D.
   Archive D/L sequence: hero D / products L / methodology D / about L /
     cta-band D. */
body.sw-dark-twin [data-sw-section="hero"],
body.sw-dark-twin [data-sw-section="pillars"],
body.sw-dark-twin [data-sw-section="proof"],
body.sw-dark-twin [data-sw-section="methodology"],
body.sw-dark-twin [data-sw-section="cta-band"] {
  background: var(--bg) !important;
}
body.sw-dark-twin [data-sw-section="dashboard"],
body.sw-dark-twin [data-sw-section="deliverables"],
body.sw-dark-twin [data-sw-section="pricing"],
body.sw-dark-twin [data-sw-section="products"],
body.sw-dark-twin [data-sw-section="about"],
body.sw-dark-twin [data-sw-section="product-reel"] {
  background: var(--bg-soft) !important;
}
body.sw-dark-twin .sw-band-dark h1,
body.sw-dark-twin .sw-band-dark h2,
body.sw-dark-twin .sw-band-dark h3 { color: var(--ink); }
body.sw-dark-twin .sw-band-dark h4,
body.sw-dark-twin .sw-band-dark h5,
body.sw-dark-twin .sw-band-dark h6 { color: var(--blue-bright); }
body.sw-dark-twin .sw-band-dark p { color: rgb(245 247 250 / 80%); }

/* Tile dark theme across all bands on sw-dark-twin scope */
body.sw-dark-twin .sw-tile {
  background: rgb(255 255 255 / 4%);
  border-color: rgb(255 255 255 / 14%);
}
body.sw-dark-twin .sw-tile:hover {
  border-color: rgb(255 255 255 / 36%);
}
body.sw-dark-twin .sw-tile h3 { color: var(--ink); }
body.sw-dark-twin .sw-tile p { color: rgb(245 247 250 / 78%); }
body.sw-dark-twin .sw-tile ul,
body.sw-dark-twin .sw-tile li {
  color: rgb(245 247 250 / 70%);
}
body.sw-dark-twin .sw-tile ul { border-top-color: rgb(255 255 255 / 14%); }
body.sw-dark-twin .sw-tile-num { color: var(--blue-bright); }

/* Pricing grid dark theme + recommended-tier --blue-bright border */
body.sw-dark-twin .sw-pricing-tier {
  background: rgb(255 255 255 / 4%);
  border-color: rgb(255 255 255 / 14%);
}
body.sw-dark-twin .sw-pricing-tier h3 { color: var(--ink); }
body.sw-dark-twin .sw-pricing-tier.recommended {
  border: 2px solid var(--product-accent);
  background: rgb(255 255 255 / 4%);
}
body.sw-dark-twin .sw-price { color: var(--ink); }
body.sw-dark-twin .sw-pricing-tier p,
body.sw-dark-twin .sw-pricing-tier .sw-meta { color: rgb(245 247 250 / 80%); }
body.sw-dark-twin .sw-pricing-tier ul li {
  color: rgb(245 247 250 / 80%);
  border-bottom-color: rgb(255 255 255 / 10%);
}
body.sw-dark-twin .sw-pricing-tier li::before { color: var(--blue-bright); }

/* ────────────────────────────────────────────────────────────
   .sw-pricing-extras — discreet "more options" expander
   Lives below the 3-card .sw-pricing-grid on /products/<slug>/.
   Surfaces off-grid FC variants via the [sw_pricing_extras] shortcode.
   Default state: collapsed. No CSS for the variants themselves —
   styling is driven by the standard <details>/<summary>/<select>/<a> chain.
   ──────────────────────────────────────────────────────────── */

.sw-pricing-extras {
    margin: 24px auto 0;
    max-width: 640px;
    padding: clamp(16px, 2vw, 20px);
    border: 1px solid var(--rule);
    border-radius: 6px;
    background: transparent;
}

.sw-pricing-extras__summary {
    list-style: none;
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    /* FLATTEN-FLOOR: literal kept — .sw-pricing-extras__chevron renders a symbol
       glyph (absent from JetBrains Mono); var(--font-mono)'s fallback shifts it.
       Verified by css-regression harness. */
    font-family: 'JetBrains Mono', ui-monospace, monospace;
    font-size: 13px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    color: var(--blue);
}

.sw-pricing-extras__summary::-webkit-details-marker { display: none; }

.sw-pricing-extras[open] .sw-pricing-extras__chevron {
    transform: rotate(180deg);
}

.sw-pricing-extras__chevron {
    display: inline-block;
    transition: transform 160ms ease;
}

.sw-pricing-extras__row {
    display: grid;
    grid-template-columns: auto 1fr auto auto;
    align-items: center;
    gap: 12px;
    margin-top: 16px;
}

.sw-pricing-extras__label {
    font-family: var(--font-mono);
    font-size: 12px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    color: var(--ink-700);
}

.sw-pricing-extras__select {
    padding: 8px 12px;
    border: 1px solid var(--rule);
    border-radius: 4px;
    background: var(--bg);
    color: var(--ink);
    font-family: inherit;
    font-size: 14px;
    line-height: 1.4;
    min-width: 0;
}

.sw-pricing-extras__price {
    font-size: 18px;
    font-weight: 600;
    color: var(--ink);
    white-space: nowrap;
}

.sw-pricing-extras__price-cycle {
    font-size: 12px;
    font-weight: 400;
    color: var(--ink-700);
}

.sw-pricing-extras__custom {
    margin: 12px 0 0;
    font-family: var(--font-mono);
    font-size: 11px;
    color: var(--ink-700);
}

.sw-pricing-extras__custom a {
    color: var(--blue);
    border-bottom: 1px solid var(--blue);
}

/* Dark-twin surfaces — flip the colors that don't auto-flip via --ink-700 remap. */
body.sw-dark-twin .sw-pricing-extras__summary { color: var(--blue-bright); }
body.sw-dark-twin .sw-pricing-extras__select {
    background: var(--bg);
    color: var(--ink);
    border-color: var(--rule);
}
body.sw-dark-twin .sw-pricing-extras__custom a {
    color: var(--blue-bright);
    border-bottom-color: var(--blue-bright);
}

/* Mobile: stack the row vertically — consistent with M-022. */
@media (max-width: 640px) {
    .sw-pricing-extras__row {
        grid-template-columns: 1fr;
        gap: 10px;
    }
    .sw-pricing-extras__price { text-align: left; }
}

/* Compare frame dark theme */
body.sw-dark-twin .sw-compare-frame {
  background: rgb(255 255 255 / 4%);
  border-color: rgb(255 255 255 / 14%);
}
body.sw-dark-twin .sw-compare-row { border-bottom-color: rgb(255 255 255 / 10%); }
body.sw-dark-twin .sw-compare-row.head { background: rgb(255 255 255 / 6%); }
body.sw-dark-twin .sw-compare-row .col-scenario { color: var(--ink); }
body.sw-dark-twin .sw-compare-row .col-num { color: rgb(245 247 250 / 80%); }
body.sw-dark-twin .sw-compare-row .col-num.trustily { color: var(--blue-bright); }

/* Dashboard frame dark theme */
body.sw-dark-twin .sw-dashboard-frame {
  background: rgb(255 255 255 / 4%);
  border-color: rgb(255 255 255 / 14%);
}
body.sw-dark-twin .sw-dashboard-chrome {
  background: rgb(255 255 255 / 6%);
  border-bottom-color: rgb(255 255 255 / 10%);
}
body.sw-dark-twin .sw-dashboard-side {
  border-right-color: rgb(255 255 255 / 10%);
}
body.sw-dark-twin .sw-dashboard-side li {
  border-bottom-color: rgb(255 255 255 / 10%);
  color: rgb(245 247 250 / 78%);
}
body.sw-dark-twin .sw-dashboard-side .active { color: var(--blue-bright); }
body.sw-dark-twin .sw-dashboard-main h4 { color: var(--ink); }
body.sw-dark-twin .sw-dash-stat {
  background: rgb(255 255 255 / 6%);
  border-color: rgb(255 255 255 / 10%);
}
body.sw-dark-twin .sw-dash-stat .num { color: var(--ink); }
body.sw-dark-twin .sw-dash-stat .lbl { color: rgb(245 247 250 / 60%); }
body.sw-dark-twin .sw-dash-review {
  background: rgb(255 255 255 / 4%);
  border-color: rgb(255 255 255 / 10%);
}
body.sw-dark-twin .sw-dash-review p { color: rgb(245 247 250 / 80%); }
body.sw-dark-twin .sw-dash-review .src { color: rgb(245 247 250 / 60%); }
body.sw-dark-twin .sw-dashboard-caption {
  background: rgb(255 255 255 / 4%);
  border-top-color: rgb(255 255 255 / 10%);
  color: rgb(245 247 250 / 60%);
}

/* ───────────────────────────────────────────────────────────────────────────
   M-products-redesign 2026-05-12 — Product Reel band (V4 mockup).
   Operator-picked variation; replaces §01 dashboard composite on all 5
   product detail pages. 2-col grid (text/logo left · video right with
   --blue-bright corner-bracket accent) above a continuous-scroll
   screenshot marquee that hover-pauses.
   Surface: `<section data-sw-section="product-reel" class="sw-product-reel">`.
   Video: self-host MP4 via Gumlet (operator-content-gated; band ships
   with poster + play-button overlay until Gumlet IDs wired).
   Screenshots: variable count per product (HTML widget contains 2x for
   seamless loop; placeholder gradient thumbs until real screenshots ship).
   ─────────────────────────────────────────────────────────────────────── */
.sw-product-reel {
  position: relative;
  overflow: hidden;
}
.sw-product-reel::before {
  content: "";
  position: absolute;
  inset: 0;
  background: radial-gradient(circle at 50% 30%, rgb(94 168 227 / 6%) 0%, transparent 50%);
  pointer-events: none;
  z-index: 0;
}
.sw-product-reel > .e-con-inner { position: relative; z-index: 1; }

.sw-reel-top {
  display: grid;
  grid-template-columns: 1fr 2fr;
  gap: 64px;
  align-items: center;
  margin-bottom: var(--space-6);
  text-align: left;
}
.sw-reel-text { text-align: left; }
.sw-reel-text .eyebrow {
  display: inline-block;
  padding: 6px 14px;
  border: 1px solid var(--rule-strong);
  border-radius: 999px;
  font: 500 11px/1.4 var(--font-mono);
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--blue-bright);
  margin-bottom: 16px;
}
.sw-reel-text h2 {
  font: 600 clamp(28px, 3.6vw, 40px)/1.1 var(--font-sans) !important;
  letter-spacing: -0.012em;
  color: var(--ink) !important;
  max-width: 14ch;
  margin-bottom: 16px;
  text-align: left !important;
}
.sw-reel-text p {
  font: 500 17px/1.55 var(--font-sans);
  color: rgb(245 247 250 / 80%);
  max-width: 48ch;
  margin: 0;
  text-align: left;
}
.sw-reel-logo {
  display: inline-flex;
  align-items: center;
  gap: 14px;
  margin-top: var(--space-4);
}
/* 2026-05-27: the brand chip renders the REAL white brand mark (was a
   placeholder letter — "SW"/"S"/"H"/"T"). The mark is layered over the blue
   gradient as a background image (centered, 62% of the 56px square) and the
   placeholder letter is hidden via font-size:0/color:transparent so the
   widget HTML can stay untouched. The correct per-brand mark is selected by
   the existing body.sw-product-* class set by body-class.php — SyteWide-brand
   surfaces (home/about/services, no product class) get the SyteWide mark.
   Gradient is the DEEP brand blue as literal hexes (the :root --blue /
   --blue-bright values), NOT var(--blue): on these dark-twin pages var(--blue)
   flips to the light #5EA8E3 tint, which only gave the white marks ~2.1:1 and
   looked washed out (worst on SyteHero's 35%-opacity bars). The deep blue
   yields 4.5:1. Operator-approved 2026-05-27.

   Updated 2026-05-27 (M-images-media-library / site-08 rule): asset URLs
   migrated from theme path (../img/brand/*) to WordPress Media Library
   attachment URLs so EWWW Image Optimizer + ExactDN can manage them and the
   "every image has a Media Library ID" invariant holds. Media Library IDs:
   sytewide-mark-white.svg=124059, syteops-mark-white.svg=124056,
   sytehero-mark-white.svg=124057, trustily-mark-white.svg=124058. */
.sw-reel-logo .mark {
  width: 56px;
  height: 56px;
  border-radius: var(--radius-sm);
  background: url(https://sytewide.com/wp-content/uploads/2026/05/sytewide-mark-white.svg) center / 62% no-repeat,
              linear-gradient(135deg, #1F5F85 0%, #4A9DC8 100%);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 0;            /* hide the placeholder letter */
  color: transparent;
  box-shadow: 0 4px 20px rgb(94 168 227 / 18%);
}
body.sw-product-syteops .sw-reel-logo .mark {
  background-image: url(https://sytewide.com/wp-content/uploads/2026/05/syteops-mark-white.svg),
                    linear-gradient(135deg, #1F5F85 0%, #4A9DC8 100%);
}
body.sw-product-sytehero .sw-reel-logo .mark {
  background-image: url(https://sytewide.com/wp-content/uploads/2026/05/sytehero-mark-white.svg),
                    linear-gradient(135deg, #1F5F85 0%, #4A9DC8 100%);
}
body.sw-product-trustily .sw-reel-logo .mark {
  background-image: url(https://sytewide.com/wp-content/uploads/2026/05/trustily-mark-white.svg),
                    linear-gradient(135deg, #1F5F85 0%, #4A9DC8 100%);
}
.sw-reel-logo .name {
  font: 600 22px/1 var(--font-sans);
  letter-spacing: -0.01em;
  color: var(--ink);
}
.sw-reel-logo .tag {
  display: block;
  margin-top: 10px;
  font: 500 11px/1 var(--font-mono);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--mute);
}

.sw-reel-stage { position: relative; }
.sw-reel-stage::after {
  content: "";
  position: absolute;
  right: -16px;
  bottom: -16px;
  width: 96px;
  height: 96px;
  border-right: 2px solid var(--blue-bright);
  border-bottom: 2px solid var(--blue-bright);
  pointer-events: none;
}
/* 2026-06-02 fix — on Home the caption ("Watch what you'd actually be hiring.")
   lives INSIDE .sw-reel-stage, so anchoring the corner-bracket to the stage's
   bottom dragged it down below the caption text. Wrap the video in
   .sw-reel-frame and anchor the bracket to the FRAME instead: the bracket now
   hugs the video's bottom-right corner and the caption sits cleanly UNDER the
   lines. Product reels have no caption (just the video in the stage) and no
   .sw-reel-frame, so they keep the .sw-reel-stage::after bracket untouched. */
.sw-reel-frame { position: relative; }
.sw-reel-stage:has(.sw-reel-frame)::after { content: none; }
.sw-reel-frame::after {
  content: "";
  position: absolute;
  right: -16px;
  bottom: -16px;
  width: 96px;
  height: 96px;
  border-right: 2px solid var(--blue-bright);
  border-bottom: 2px solid var(--blue-bright);
  pointer-events: none;
}
/* Video caption (copy-rewrite 2026-06-02) — the "Watch what you'd actually be
   hiring" line lifted out of the body paragraph so the paragraph stands on its
   own for visitors who never press play. --ink-700 is canvas-aware. */
.sw-reel-caption {
  margin: 14px 0 0;
  font-family: var(--font-mono);
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-700);
  text-align: center;
}
/* Framed stage (Home): clear the bracket's 16px overhang so the caption sits
   below the lines, not against them. */
.sw-reel-stage:has(.sw-reel-frame) .sw-reel-caption { margin-top: 30px; }
/* Closing-CTA "prefer to talk" phone line on Home (copy-rewrite 2026-06-02).
   2026-06-03: bumped 14 → 16px + weight 500 so it reads at the same confident
   size as the talk lines on other pages (Foundation .sw-talk-now / Contact
   .sw-contact-phone) instead of a timid footnote. */
.sw-cta-phone {
  margin: 18px 0 0;
  text-align: center;
  font-family: var(--font-mono);
  font-size: 16px;
  letter-spacing: 0.02em;
}
.sw-cta-phone a {
  color: var(--ink-700);
  text-decoration: none;
  /* 2026-06-03: weight 500 for the confident treatment used on other pages'
     talk lines. !important overrides the sitewide Geist coverage rule that
     pins link/body weight to 400. */
  font-weight: 500 !important;
  border-bottom: 1px solid transparent;
  transition: color 0.15s ease, border-color 0.15s ease;
}
.sw-cta-phone a:hover,
.sw-cta-phone a:focus-visible {
  color: var(--blue-bright);
  border-bottom-color: currentColor;
}
/* 2026-06-02 fix — the closing-CTA band (.sw-band-end-cta) is a centered
   flex-wrap row, so the two CTA buttons + this phone line all packed onto one
   row and "Prefer to talk?" sat crowded beside the buttons. Force the phone
   widget onto its own line below the button pair, with a little breathing room.
   The :has() selector targets the Elementor widget wrapper holding .sw-cta-phone
   without coupling to a per-page element-id. */
.sw-band-end-cta > .elementor-widget:has(.sw-cta-phone) {
  flex-basis: 100%;
  margin-top: 6px;
}
/* 2026-06-03 — the two closing-CTA buttons sat at different heights because the
   primary ("Book a free Foundation Session") carries a 12px Elementor _margin
   top while the secondary doesn't, so they didn't line up across the row. Zero
   the button widgets' top margin so the pair aligns (align-items:center then
   centers them); the band's flex row-gap handles spacing from the lede above.
   Elementor authors this as a logical `margin-block-start` whose specificity
   outranks a plain class selector, so !important is required to win — same
   cascade battle as the other Elementor-margin overrides in this file. */
.sw-band-end-cta > .elementor-widget-button {
  margin-top: 0 !important;
}
/* Foundation "prefer to talk now" line beside the booking calendar
   (copy-rewrite 2026-06-02). /foundation/ is a sw-darkform-page; canvas-aware
   tokens resolve to paper + light-blue on the dark canvas. */
.sw-talk-now {
  margin: 16px 0 0;
  text-align: center;
  font-family: var(--font-mono);
  font-size: 15px;
  letter-spacing: 0.02em;
  color: var(--ink-700);
}
.sw-talk-now a {
  color: var(--blue-bright);
  font-weight: 600;
  text-decoration: none;
  border-bottom: 1px solid transparent;
  transition: border-color 0.15s ease;
}
.sw-talk-now a:hover,
.sw-talk-now a:focus-visible { border-bottom-color: currentColor; }
/* Contact page phone centrepiece in the hero (copy-rewrite 2026-06-02). The
   number is the primary contact path — large, vanity form, click-to-call. */
.sw-contact-phone {
  margin: 8px 0 0;
  font-family: var(--font-mono);
  font-size: clamp(22px, 3.5vw, 34px);
  font-weight: 600;
  line-height: 1.2;
  letter-spacing: 0.01em;
  color: var(--ink);
}
.sw-contact-phone__label {
  display: block;
  margin-bottom: 6px;
  font-size: 13px;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-700);
}
.sw-contact-phone a {
  color: var(--blue-bright);
  text-decoration: none;
  border-bottom: 2px solid transparent;
  transition: border-color 0.15s ease;
}
.sw-contact-phone a:hover,
.sw-contact-phone a:focus-visible { border-bottom-color: currentColor; }
.sw-reel-video {
  position: relative;
  width: 100%;
  aspect-ratio: 16 / 9;
  background:
    radial-gradient(circle at 30% 25%, rgb(94 168 227 / 10%) 0%, transparent 50%),
    radial-gradient(circle at 75% 70%, rgb(143 201 247 / 6%) 0%, transparent 45%),
    linear-gradient(180deg, #0E1D2F 0%, #08111B 100%);
  border: 1px solid var(--rule-strong);
  border-radius: var(--radius-sm);
  overflow: hidden;
  cursor: pointer;
  transition: border-color 0.25s ease, transform 0.25s ease, box-shadow 0.25s ease;
}
.sw-reel-video:hover {
  border-color: rgb(143 201 247 / 50%);
  box-shadow: 0 16px 48px rgb(94 168 227 / 14%);
  transform: translateY(-2px);
}
.sw-reel-video video,
.sw-reel-video iframe {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  border: 0;
}
.sw-reel-video .play-btn {
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  width: 88px;
  height: 88px;
  border-radius: 999px;
  background: rgb(245 247 250 / 92%);
  display: flex;
  align-items: center;
  justify-content: center;
  box-shadow: 0 12px 40px rgb(0 0 0 / 35%);
  transition: transform 0.18s ease;
  z-index: 2;
}
.sw-reel-video:hover .play-btn { transform: translate(-50%, -50%) scale(1.06); }
.sw-reel-video .play-btn::after {
  content: "";
  border-style: solid;
  border-width: 14px 0 14px 22px;
  border-color: transparent transparent transparent var(--bg);
  margin-left: 6px;
}
.sw-reel-video .video-tag {
  position: absolute;
  top: 12px;
  left: 12px;
  padding: 4px 10px;
  background: rgb(0 0 0 / 65%);
  border: 1px solid rgb(245 247 250 / 18%);
  border-radius: 999px;
  font: 500 10px/1 var(--font-mono);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--blue-bright);
  z-index: 2;
}
.sw-reel-video .duration {
  position: absolute;
  bottom: 12px;
  right: 12px;
  padding: 4px 10px;
  background: rgb(0 0 0 / 65%);
  border: 1px solid rgb(245 247 250 / 18%);
  border-radius: 4px;
  font: 500 11px/1 var(--font-mono);
  letter-spacing: 0.04em;
  color: var(--ink);
  z-index: 2;
}

.sw-reel-marquee-wrap {
  border-top: 1px solid var(--rule);
  border-bottom: 1px solid var(--rule);
  padding: var(--space-4) 0;
  overflow: hidden;
  position: relative;
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 8%, #000 92%, transparent);
          mask-image: linear-gradient(90deg, transparent, #000 8%, #000 92%, transparent);
}
.sw-reel-marquee {
  display: flex;
  gap: 24px;
  animation: sw-reel-scroll 50s linear infinite;
  width: max-content;
}
.sw-reel-marquee:hover { animation-play-state: paused; }
.sw-reel-marquee .shot {
  width: 320px;
  aspect-ratio: 16 / 10;
  flex-shrink: 0;
  position: relative;
  background: linear-gradient(180deg, #132338 0%, #0A1626 100%);
  border: 1px solid var(--rule-strong);
  border-radius: var(--radius-sm);
  overflow: hidden;
  cursor: pointer;
  transition: border-color 0.18s ease, transform 0.18s ease;
}
.sw-reel-marquee .shot:hover {
  border-color: var(--blue-bright);
  transform: translateY(-2px);
}
.sw-reel-marquee .shot img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
.sw-reel-marquee .shot .label {
  position: absolute;
  bottom: 8px;
  left: 12px;
  font: 500 10px/1 var(--font-mono);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--mute);
  background: rgb(7 15 26 / 85%);
  padding: 4px 8px;
  border-radius: 3px;
  z-index: 2;
}
/* Placeholder thumb styling — kicks in when no <img> is present. Real
   screenshots replace placeholder mode by adding an <img> child. */
.sw-reel-marquee .shot.placeholder::before {
  content: "";
  position: absolute;
  top: 0; left: 0; right: 0;
  height: 22px;
  background: linear-gradient(180deg, rgb(245 247 250 / 6%), rgb(245 247 250 / 0%));
  border-bottom: 1px solid rgb(245 247 250 / 6%);
}
.sw-reel-marquee .shot.placeholder::after {
  content: "";
  position: absolute;
  inset: 32px 16px 16px;
  background:
    linear-gradient(180deg,
      rgb(143 201 247 / 22%) 0%, rgb(143 201 247 / 22%) 10%,
      transparent 10%, transparent 18%,
      rgb(245 247 250 / 10%) 18%, rgb(245 247 250 / 10%) 28%,
      transparent 28%, transparent 36%,
      rgb(245 247 250 / 10%) 36%, rgb(245 247 250 / 10%) 46%,
      transparent 46%, transparent 54%,
      rgb(245 247 250 / 6%) 54%, rgb(245 247 250 / 6%) 100%);
  border-radius: 2px;
}

.sw-reel-foot {
  margin-top: var(--space-3);
  text-align: center;
  font: 500 11px/1.4 var(--font-mono);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--mute);
}
.sw-reel-foot strong { color: var(--blue-bright); font-weight: 500; }

@keyframes sw-reel-scroll {
  from { transform: translateX(0); }
  to { transform: translateX(-50%); }
}

@media (max-width: 960px) {
  .sw-reel-top { grid-template-columns: 1fr; gap: 32px; }
  .sw-reel-text h2 { max-width: none; }
  .sw-reel-marquee .shot { width: 240px; }
}

@media (prefers-reduced-motion: reduce) {
  .sw-reel-marquee { animation: none; }
}

/* ───────────────────────────────────────────────────────────────────────────
   M-products-redesign 2026-05-12 — Hero logo mark.
   Operator-requested 2026-05-12: a logo spot above the "Product · NNN"
   eyebrow on every product detail page hero. Matches the gradient-mark
   styling already used in the product-reel band logo lockup for visual
   consistency. Replace `<div class="mark">X</div>` with `<img src="..."
   alt="...">` when real product brand assets land.
   ─────────────────────────────────────────────────────────────────────── */
.sw-hero-logo {
  display: flex;
  justify-content: center;
  /* 2026-05-28 — no extra margin below the mark; the hero's 20px container
     gap is the only space, so the mark + product wordmark read as one tight
     lockup (operator: pull the name closer to the logo on all product heros).
     Was var(--space-3)/24px, which made the mark→wordmark gap (44px) larger
     than the wordmark→h1 gap — backwards for a lockup. */
  margin-bottom: 0;
}
.sw-hero-logo .mark {
  width: 96px;
  height: 96px;
  border-radius: 12px;
  background: linear-gradient(135deg, #1F5F85 0%, #4A9DC8 100%);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font: 700 44px/1 var(--font-sans);
  letter-spacing: -0.02em;
  color: var(--ink);
  box-shadow: 0 8px 32px rgb(94 168 227 / 22%);
}
.sw-hero-logo img {
  display: block;
  max-width: 200px;
  max-height: 200px;
  width: auto;
  height: auto;
}

/* 2026-05-28 — Product detail hero: the product name reads as a large
   wordmark lockup beneath the .sw-hero-logo mark. Operator-requested:
   drop the "Product ·" prefix (removed from each hero eyebrow's heading
   title) and size the name to pair with the mark (80px mobile → 200px
   desktop). Classless target — the eyebrow is the only <h6> in the
   product hero; the headline is a real <h1>. The .sw-wm mixed-weight
   spans (SyteOps / SyteHero) inherit this size; Trustily is single-weight. */
body[class*="sw-product-"] [data-sw-section="hero"] h6.elementor-heading-title {
  font-size: clamp(26px, 6vw, 52px);
  line-height: 1.05;
  margin-bottom: var(--space-2);
}

/* ════════════════════════════════════════════════════════════════════════
   Brand system 2026-05-26 — per-product accent scope + nav/footer lockup +
   mixed-weight wordmark spans. Body classes from plugins/sytewide-site/
   includes/body-class.php; wordmark spans from includes/brand-wordmarks.php.
   ════════════════════════════════════════════════════════════════════════ */

/* Per-product accent — each product page's body carries one sw-product-* class
   (monthly + LTD share a brand). Uses the dark-bg lifted variant (product pages
   are dark canvas). Home/SyteWide keep the --product-accent default. */
body.sw-product-syteops  { --product-accent: var(--so-dark); }
body.sw-product-sytehero { --product-accent: var(--sh-dark); }
body.sw-product-trustily { --product-accent: var(--tr-dark); }

/* Nav + footer brand lockup — the actual brand asset
   (brand/logo/sytewide/sytewide-lockup-horizontal-on-dark.svg) inlined into the
   header (CB 122841) + footer (CB 122845) HTML widgets. Inlined as live SVG,
   NOT an <img>, because the lockup SVG embeds a <text> wordmark that won't load
   the webfont inside an <img> (secure-static blocks it) — inlined in-page the
   <text> renders true Geist (verified 2026-05-27). The mark uses the verbatim
   brand hex #5b7cd9 (= --sw-dark, a fixed brand token, so no cascade dependency
   on link-colour rules); the wordmark uses the site paper hex #F5F7FA — the
   nav/footer are fixed-dark surfaces, so var(--bg)/var(--ink) would invert on
   dark-twin pages. display:flex + fit-content removes the inline line-box gap
   and lets the header grid's align-items:center centre the single SVG (lockup,
   nav links + CTA share centre y); fit-content keeps the click target tight. */
.sw-brand-lockup { display: flex; width: fit-content; align-items: center; text-decoration: none; }
.sw-brand-lockup svg { display: block; width: auto; height: 34px; }

/* Mixed-weight wordmark spans (SyteOps / SyteHero) from brand-wordmarks.php.
   !important is required to beat the sitewide heading font-weight locks; this
   is an intentional new floor rule for an inline brand primitive. */
.sw-wm { font-style: normal; }
.sw-wm .sw-wm-l { font-weight: 400 !important; }
.sw-wm .sw-wm-b { font-weight: 700 !important; }

/* ============================================================================
   Sitewide dark Wave 1 — GetTerms hero polish (operator review 2026-05-13)
   The 5 GetTerms-embedded legal pages render a paper-bg hero panel above
   the dark-canvas body. Two issues surface only after the Wave 1 sw-dark-
   twin flip:
     1. The GetTerms embed renders its own <h1> as the first child of
        .getterms-document-embed, duplicating the Elementor hero title
        immediately above it. Hide GetTerms's inner h1; keep the hero.
     2. The breadcrumb "last-item" segment renders via the dark-twin --ink
        remap (paper) which equals the paper hero bg → invisible. Only
        the cyan "Home" link survives.
   PR: sitewide-dark/wave-1-legal · Spec: .agent/specs/2026-05-12-sitewide-dark-rollout-design.md
   ============================================================================ */
/* Hide GetTerms embed's inner <h1> page-title (duplicates the hero title
   immediately above). Scope by .sw-dark-twin to keep this paired with
   the rollout's body-class flip. */
body.sw-dark-twin .getterms-document-embed h1 {
  display: none !important;
}
/* GetTerms section headings — tame the 52px h2 / 32px h3 defaults that
   GetTerms emits, and add breathing room above each section. Body text
   stays untouched (already paper via dark-twin remap). */
body.sw-dark-twin .getterms-document-embed h2 {
  font-size: clamp(26px, 2.6vw, 32px);
  font-weight: 600;
  line-height: 1.2;
  margin-top: 56px;
}
body.sw-dark-twin .getterms-document-embed h3 {
  font-size: clamp(18px, 1.8vw, 22px);
  font-weight: 600;
  line-height: 1.3;
  margin-top: 32px;
}
body.sw-dark-twin .getterms-document-embed h4 {
  font-size: 18px;
  font-weight: 600;
  line-height: 1.35;
  margin-top: 24px;
}
/* Un-flip breadcrumb text on paper hero panel. dark-twin --ink is paper,
   which renders invisibly on paper bg. Force navy text + keep cyan-blue
   links. Scoped to the 5 GetTerms surfaces. */
body.sw-dark-twin.page-id-121522 .ct-breadcrumbs,
body.sw-dark-twin.page-id-121540 .ct-breadcrumbs,
body.sw-dark-twin.page-id-121554 .ct-breadcrumbs,
body.sw-dark-twin.page-id-121561 .ct-breadcrumbs,
body.sw-dark-twin.page-id-121546 .ct-breadcrumbs {
  color: var(--bg);
}
body.sw-dark-twin.page-id-121522 .ct-breadcrumbs a,
body.sw-dark-twin.page-id-121540 .ct-breadcrumbs a,
body.sw-dark-twin.page-id-121554 .ct-breadcrumbs a,
body.sw-dark-twin.page-id-121561 .ct-breadcrumbs a,
body.sw-dark-twin.page-id-121546 .ct-breadcrumbs a {
  color: var(--blue);
}
/* End Wave 1 — GetTerms hero polish */

/* ============================================================================
   Sitewide dark Wave 2.A — Contact (post 695)
   PR: sitewide-dark/wave-2-contact · Spec: § Wave 2.A
   Hardcoded container backgrounds cleared via Elementor MCP at T4/T5/T6;
   c2y0001/c2y0002 HTML widgets + c2t0001/c2t0002 text-editors rewritten
   with token references at T6b/T6c. This block covers child widgets whose
   color comes from Elementor's per-widget settings — those compile to
   (0,6,0) selectors that beat our (0,5,1) bare rule, so the !important
   below is the M-024 specificity bump documented in site-07.
   ============================================================================ */
body.sw-dark-twin.page-id-695 .elementor-heading-title {
  color: var(--ink) !important;
}
body.sw-dark-twin.page-id-695 .elementor-widget-text-editor,
body.sw-dark-twin.page-id-695 .elementor-widget-text-editor p {
  color: var(--ink);
}
/* CTA-band buttons (c3b0001 primary, c3b0002 ghost "See products →") need
   dark-twin treatment. c3b0001 is a solid primary: use the deep brand blue
   #1F5F85 (NOT var(--blue), which washes out to the #5EA8E3 light tint on
   dark-twin — see memory feedback-blue-token-flips-on-dark-twin) + paper text
   via --ink, matching the Elementor-widget primaries on home/services-hub.
   c3b0002 mirrors the cta-secondary ghost pattern. */
body.sw-dark-twin.page-id-695 .elementor-element-c3b0001 a.elementor-button {
  background-color: #1F5F85 !important;
  color: var(--ink) !important;
  border-color: #1F5F85 !important;
}
body.sw-dark-twin.page-id-695 .elementor-element-c3b0001 a.elementor-button:hover {
  background-color: #0B1B2B !important;
  color: var(--ink) !important;
  border-color: #0B1B2B !important;
}
body.sw-dark-twin.page-id-695 .elementor-element-c3b0002 a.elementor-button {
  background-color: transparent !important;
  color: var(--ink) !important;
  border-color: rgb(245 247 250 / 35%) !important;
}
body.sw-dark-twin.page-id-695 .elementor-element-c3b0002 a.elementor-button:hover {
  background-color: rgb(245 247 250 / 8%) !important;
  border-color: rgb(245 247 250 / 60%) !important;
}
/* FluentForm submit button — sw-darkform-page covers labels + inputs +
   section-title, but the .ff-btn / button[type=submit] selector wasn't
   in the original 2026-05-10 block. FluentForm's default submit bg is
   ~rgb(111,156,255) which is off-token; remap to --blue + dark text. */
body.sw-darkform-page .fluentform .ff-btn,
body.sw-darkform-page .fluentform .ff-btn-submit,
body.sw-darkform-page .fluentform button[type="submit"] {
  background-color: var(--blue) !important;
  color: var(--bg) !important;
  border: 1px solid var(--blue) !important;
  padding: 12px 22px !important;
  border-radius: var(--radius-btn, 4px) !important;
  font: 500 15px/1.2 var(--font-sans) !important;
}
body.sw-darkform-page .fluentform .ff-btn:hover,
body.sw-darkform-page .fluentform .ff-btn-submit:hover,
body.sw-darkform-page .fluentform button[type="submit"]:hover {
  background-color: var(--blue-bright) !important;
  border-color: var(--blue-bright) !important;
}
/* Form first section breathing room — operator note (c2t0001) sits
   immediately above the FluentForm; the first section-break inside the
   form lands flush against the panel below. 32px gives air. */
body.sw-darkform-page .fluentform .ff-el-section-break:first-of-type {
  padding-top: 32px;
}
/* Submit button breathing room — FluentForm Booking renders a bare text
   node "Selected Calendar could not be found" immediately before
   .ff_submit_btn_wrapper. No element wraps the text, so push the submit
   wrapper down to create air around it. Once the calendar is configured
   the bare-text disappears and the margin reads as ordinary submit
   spacing. */
body.sw-darkform-page .fluentform .ff_submit_btn_wrapper {
  margin-top: 32px !important;
}

/* ============================================================
   FluentBooking calendar — dark-twin treatment (2026-06-02)
   --------------------------------------------------------------
   The /foundation/ booking field renders a Svelte calendar app
   (.fcal_* / .day / .fcal_spot / svelte-select). It escaped the
   2026-05-10 sw-darkform-page block above and shipped as a stark
   WHITE widget on the navy canvas — clashing hard with the dark
   form inputs around it. Re-skin to the same dark-twin language as
   those inputs: ~6% white-wash surfaces, hairline borders, --ink
   text, --blue accent (light #5EA8E3) with dark --bg text on fills.

   FluentBooking hard-codes #1B2533 on most leaf text nodes (built
   for a white bg), so rule (0) sets a broad light-text default and
   the muted/accent rules below override it via later source order
   (equal specificity → source order decides). The availability fill
   lives on the date cell's inner <span>, not .day itself. The
   timezone control is a svelte-select; its dropdown list reads CSS
   vars off the control root, so we also set .item colours directly.
   ============================================================ */
/* 0. Broad light-text default */
body.sw-darkform-page .fcal_calendar_inner,
body.sw-darkform-page .fcal_calendar_inner * { color: var(--ink) !important; }

/* 1. Card shell + kill every stock-white panel */
body.sw-darkform-page .fcal_calendar_inner {
  background-color: rgb(245 247 250 / 3%) !important;
  border: 1px solid rgb(245 247 250 / 14%) !important;
  border-radius: var(--radius-sm, 6px) !important;
  box-shadow: 0 1px 2px rgb(0 0 0 / 20%), 0 14px 34px rgb(0 0 0 / 26%) !important;
  overflow: hidden !important;
}
body.sw-darkform-page .fcal_side {
  border-right-color: rgb(245 247 250 / 12%) !important;
  background-color: transparent !important;
}
body.sw-darkform-page .fcal_slot_picker,
body.sw-darkform-page .fcal_slot_picker.is_active,
body.sw-darkform-page .fcal_slot_picker_header,
body.sw-darkform-page .fcal_date_event_details,
body.sw-darkform-page .fcal_date_event_details_header { background-color: transparent !important; }

/* 2. Muted text (weekday heads, description, timezone label, location) */
body.sw-darkform-page .fcal_slot_description,
body.sw-darkform-page .fcal_slot_description *,
body.sw-darkform-page .day-name,
body.sw-darkform-page .fcal_timezone_select > label,
body.sw-darkform-page .fcal_loc_text { color: var(--mute) !important; }

/* 3. Avatar ring */
body.sw-darkform-page .fcal_author_avatar img,
body.sw-darkform-page .fcal_author img { border: 1px solid rgb(245 247 250 / 18%) !important; }

/* 4. Duration pills (15 / 30 / 60 Minutes) */
body.sw-darkform-page .fcal_duration {
  background-color: transparent !important;
  border: 1px solid rgb(245 247 250 / 24%) !important;
  border-radius: 999px !important;
}
body.sw-darkform-page .fcal_duration.is_selected {
  background-color: var(--blue, #5EA8E3) !important;
  border-color: var(--blue, #5EA8E3) !important;
  color: var(--bg, #0A1626) !important;
}

/* 5. Month nav arrows */
body.sw-darkform-page .fcal_nav_active svg,
body.sw-darkform-page .fcal_day_picker .fcal_svg svg { color: var(--ink) !important; stroke: var(--ink) !important; }
body.sw-darkform-page .fcal_btn_disabled svg { color: rgb(245 247 250 / 28%) !important; stroke: rgb(245 247 250 / 28%) !important; }

/* 6. Date cells — the inner <span> carries the availability fill */
body.sw-darkform-page .fcal_day_picker .day { background-color: transparent !important; }
body.sw-darkform-page .fcal_day_picker .day > span { background-color: transparent !important; border-radius: 6px !important; }
body.sw-darkform-page .fcal_day_picker .day.day-disabled > span { color: rgb(245 247 250 / 28%) !important; }
body.sw-darkform-page .fcal_day_picker .day.day-enabled > span {
  background-color: rgb(245 247 250 / 8%) !important;
  color: var(--ink) !important;
}
body.sw-darkform-page .fcal_day_picker .day.day-enabled:hover > span { background-color: rgb(94 168 227 / 26%) !important; }
body.sw-darkform-page .fcal_day_picker .day.day_is_selected > span {
  background-color: var(--blue, #5EA8E3) !important;
  color: var(--bg, #0A1626) !important;
  font-weight: 600 !important;
}
body.sw-darkform-page .fcal_day_picker .day.is-today > span { box-shadow: inset 0 0 0 1px var(--blue, #5EA8E3) !important; }

/* 7. Timezone control (svelte-select) + its dropdown list */
body.sw-darkform-page .svelte-select.fcal_timezone_selector {
  --background: rgb(245 247 250 / 6%);
  --border: 1px solid rgb(245 247 250 / 30%);
  --list-background: var(--bg-soft);
  --item-color: var(--ink);
  --item-hover-bg: rgb(245 247 250 / 10%);
  --selected-item-color: var(--ink);
  --border-radius: 7px;
  background-color: rgb(245 247 250 / 6%) !important;
  border-color: rgb(245 247 250 / 30%) !important;
  /* Match the page form inputs (.ff-el-form-control = 7px). The stock svelte
     rule forces 8px, so set the radius directly to win, not just via the var. */
  border-radius: 7px !important;
}
/* The svelte-select's inner text <input> also matches the sw-darkform-page
   FluentForm input rule (input[type="text"], 6% wash, !important), so it painted
   a SECOND translucent layer on top of this control's own 6% bg — a two-tone
   seam down the field (lighter where the input sits, darker elsewhere). Zero the
   inner input; the control root owns the field background. The leading
   .fluentform raises specificity above that form rule. */
body.sw-darkform-page .fluentform .svelte-select.fcal_timezone_selector input {
  background-color: transparent !important;
  background-image: none !important;
}
body.sw-darkform-page .svelte-select-list {
  background: var(--bg-soft) !important;
  border: 1px solid rgb(245 247 250 / 18%) !important;
}
body.sw-darkform-page .svelte-select-list .item { color: var(--ink) !important; }
body.sw-darkform-page .svelte-select-list .item.hover,
body.sw-darkform-page .svelte-select-list .item.active { background: rgb(245 247 250 / 10%) !important; color: var(--ink) !important; }

/* 8. Time-slot pills. The pill is stock flex:space-between, reserving the right
   half for a Confirm button that only appears on select — so at rest the time sat
   jammed in the top-left with a big empty right side. Make .fcal_spot_name fill
   the pill and center the time so each reads as a deliberate, balanced field; the
   confirm still lands on the right when a slot is selected. Radius 7px matches the
   page form inputs (.ff-el-form-control). */
body.sw-darkform-page .fcal_spot {
  background-color: rgb(245 247 250 / 6%) !important;
  border: 1px solid rgb(245 247 250 / 30%) !important;
  border-radius: 7px !important;
  align-items: stretch !important;
}
body.sw-darkform-page .fcal_spot .fcal_spot_name {
  flex: 1 1 auto !important;
  display: flex !important;
  align-items: center !important;
  justify-content: center !important;
  text-align: center !important;
  padding: 0 !important;
  min-height: 44px !important;
}
body.sw-darkform-page .fcal_spot:hover {
  border-color: var(--blue, #5EA8E3) !important;
  background-color: rgb(94 168 227 / 14%) !important;
}
body.sw-darkform-page .fcal_spot.is_selected,
body.sw-darkform-page .fcal_spot.fcal_active,
body.sw-darkform-page .fcal_spot.active {
  background-color: var(--blue, #5EA8E3) !important;
  border-color: var(--blue, #5EA8E3) !important;
}
body.sw-darkform-page .fcal_spot.is_selected *,
body.sw-darkform-page .fcal_spot.fcal_active *,
body.sw-darkform-page .fcal_spot.active * { color: var(--bg, #0A1626) !important; }

/* 8b. Slot-column spacing + sticky header. The header (date + 12h/24h) is a
   position:sticky child of the scroll area with a stock-transparent background, so
   scrolled slots bled up through it. Give it the card's effective surface
   (navy + 3% wash ≈ #111D2C) + a hairline so it masks cleanly. Pad the list so the
   last slot isn't jammed against the card's bottom edge. */
body.sw-darkform-page .fcal_slot_picker_header {
  background-color: #111D2C !important;
  border-bottom: 1px solid rgb(245 247 250 / 8%) !important;
}
body.sw-darkform-page .fcal_spot_lists { padding-bottom: 16px !important; }
body.sw-darkform-page .fcal_slot_picker.is_active { padding-bottom: 24px !important; }

/* 9. 12h / 24h format toggle. Two .format-hour wrappers (one button each) sit in a
   flex .fcal_slot_picker_header_action that shipped a stock LIGHT border and
   gap:normal — the first pass bordered each wrapper, nesting two boxes with no
   space between. Style the action as ONE segmented shell (dark hairline) with a gap
   between segments; each segment is borderless; the active button fills. flex:0 0
   auto + white-space:nowrap keep the segments from shrinking/wrapping when the
   header row is tight (caught wrapping "12h" to two lines). */
body.sw-darkform-page .fcal_slot_picker_header_action {
  display: inline-flex !important;
  flex: 0 0 auto !important;
  width: auto !important;
  gap: 4px !important;
  padding: 3px !important;
  border: 1px solid rgb(245 247 250 / 22%) !important;
  border-radius: 8px !important;
  background-color: rgb(245 247 250 / 4%) !important;
}
body.sw-darkform-page .format-hour { flex: 0 0 auto !important; border: 0 !important; border-radius: 6px !important; overflow: hidden !important; }
body.sw-darkform-page .format-hour button {
  color: var(--mute) !important;
  background-color: transparent !important;
  border-radius: 6px !important;
  padding: 5px 10px !important;
  white-space: nowrap !important;
  min-width: 0 !important;
  width: auto !important;
  line-height: 1.2 !important;
}
body.sw-darkform-page .format-hour button.active { background-color: rgb(245 247 250 / 12%) !important; color: var(--ink) !important; }

/* 10. Themed scrollbar on the slot list */
body.sw-darkform-page .fcal_spot_lists,
body.sw-darkform-page .fcal_slot_items { scrollbar-width: thin !important; scrollbar-color: rgb(245 247 250 / 22%) transparent !important; }

/* 11. lg-layout width fill — kill the dead space right of the slot column.
   FluentBooking's lg (3-col) layout uses FIXED intrinsic widths: author 360,
   month grid capped at 435, slots 220. They sum to ~1035, but the form column
   here is wider (up to the 1290 canvas tier), so FB left-aligns and leaves a
   growing band of empty card to the RIGHT of the slots (107px at a 1142 card,
   ~255px at 1290). The slot column is position:absolute inside the relative
   .fcal_calendar_slot_wrap, anchored to that wrap's right edge — and that wrap
   doesn't grow because .fcal_date_wrapper is flex:0-grow. So: grow the wrapper
   chain to fill the card, then re-anchor the absolute slot picker left:455
   (= capped grid 435 + 20 gap) / right:0 so it STRETCHES into the reclaimed
   space instead of the dead band.

   Scoped to >=1366px because that's where the grid hits its 435 cap (below it
   the grid scales and the layout already fills the card with no dead space, so
   the fixed 455 anchor would mis-track). Author column is a fixed 360 at lg, so
   calc(100% - 360px) deterministically fills the date side. Non-lg (md/xs
   stacked) layouts are untouched. */
@media (min-width: 1366px) {
  body.sw-darkform-page .fcal_on_lg .fcal_date_wrapper { width: calc(100% - 360px) !important; flex: 1 1 auto !important; }
  body.sw-darkform-page .fcal_on_lg .fcal_day_picker_wrap { width: 100% !important; flex: 1 1 auto !important; }
  body.sw-darkform-page .fcal_on_lg .fcal_day_picker { width: 100% !important; }
  body.sw-darkform-page .fcal_on_lg .fcal_calendar_slot_wrap { width: 100% !important; }
  body.sw-darkform-page .fcal_on_lg .fcal_slot_picker { left: 455px !important; right: 0 !important; width: auto !important; }
  /* The "loading dates" spinner (.fcal_loading_dates) is absolute left:0/right:0
     over .fcal_calendar_slot_wrap, so it centred in that whole region — and
     widening the wrap above pushed it further right (cx 900 vs card centre 720).
     Constrain it to the capped 435px grid so it centres over the month grid it's
     actually loading (cx ~727 ≈ card centre). */
  body.sw-darkform-page .fcal_on_lg .fcal_loading_dates { right: calc(100% - 435px) !important; }
}

/* 12. Booking summary (the "Summary" panel after a time is picked). On the
   desktop overlay layouts (fcal_on_lg/md/sm) FB renders .fcal_date_event_details
   as position:absolute; z-index:10 over the calendar+slots, content-height only —
   so with the transparent neutralizer (§1) the dark slot list bled THROUGH below
   and around it (worst at md/sm, where the panel is only ~263px of a ~495px card).
   Give the overlay the card surface (#111D2C = navy + 3% wash, the §8b value) and
   stretch it bottom:0 to full height so it reads as a clean replacement. fcal_on_xs
   stacks the panel position:relative — excluded; it needs no fill (bottom is inert
   there anyway). Then re-skin the stock light summary table: FB ships the th labels
   on a near-white #f6f6f6 fill with grey hairlines, which here rendered as a white
   box with INVISIBLE white-on-white labels. Transparent cells, muted labels, paper
   values, dark hairlines. */
body.sw-darkform-page .fcal_on_lg .fcal_date_event_details,
body.sw-darkform-page .fcal_on_md .fcal_date_event_details,
body.sw-darkform-page .fcal_on_sm .fcal_date_event_details {
  background-color: #111D2C !important;
  bottom: 0 !important;
}
body.sw-darkform-page .fcal_form_booking_details table {
  border-color: rgb(245 247 250 / 12%) !important;
}
body.sw-darkform-page .fcal_form_booking_details th,
body.sw-darkform-page .fcal_form_booking_details td {
  background-color: transparent !important;
  border-color: rgb(245 247 250 / 8%) !important;
}
body.sw-darkform-page .fcal_form_booking_details th { color: var(--mute) !important; }
body.sw-darkform-page .fcal_form_booking_details td { color: var(--ink) !important; }

/* 12b. Back buttons in the summary + slot-picker headers. FB renders the circular
   control as button.fcal_svg with a grey #90949C border, and the ← as an svg filled
   with FB's hard-coded #1B2533 — both nearly invisible on the dark panel. Lift the
   ring to 65% paper and the arrow to full paper. Re-protect the svg's fill="none"
   bounding-box path (fill is an inherited SVG prop, so the paper fill on the svg
   would otherwise paint that 24×24 rect into a solid white square). */
body.sw-darkform-page .fcal_back button.fcal_svg { border-color: rgb(245 247 250 / 65%) !important; }
body.sw-darkform-page .fcal_back svg { fill: var(--ink) !important; }
body.sw-darkform-page .fcal_back svg path[fill="none"] { fill: none !important; }

/* 12c. FluentForm success confirmation (after a booking, FB hides the form and
   reveals #fluentform_NN_success.ff-message-success). Stock is a plain 1px #CED4DA
   bordered box with no fill. Restyle to the centered "ceremonial" card (operator
   pick, 2026-06-02): --bg-soft surface, hairline, 10px radius, a blue check chip
   via ::before. Blue is the sanctioned success cue — the palette has no green, and
   rule #10 forbids new top-level colours without sign-off. The chip's checkmark is
   an inline SVG data-URI stroked --blue-bright (#8FC9F7). */
body.sw-darkform-page .ff-message-success {
  background-color: var(--bg-soft) !important;
  border: 1px solid rgb(245 247 250 / 12%) !important;
  border-radius: 10px !important;
  padding: 34px 28px !important;
  text-align: center !important;
  color: var(--ink) !important;
}
body.sw-darkform-page .ff-message-success::before {
  content: "";
  display: block;
  width: 52px;
  height: 52px;
  margin: 0 auto 16px;
  border-radius: 999px;
  background-color: rgb(94 168 227 / 14%);
  border: 1px solid rgb(94 168 227 / 55%);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%238FC9F7' stroke-width='2.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M5 13l4 4L19 7'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: center;
  background-size: 24px 24px;
}
body.sw-darkform-page .ff-message-success p {
  margin: 0 auto !important;
  max-width: 48ch;
  font-size: 16px !important;
  line-height: 1.6 !important;
  color: var(--ink) !important;
}
body.sw-darkform-page .ff-message-success p:last-child { margin-bottom: 0 !important; }

/* c2g0003 layout — flex_direction is "row" (so cards lay out
   side-by-side), but c2h0003 ("Or skip the form" eyebrow) needs to
   wrap above the cards as its own line. flex-wrap on the inner +
   flex-basis 100% on the eyebrow + 50% (minus gap) on each card
   gives: eyebrow row → 2-col card row. min-width 280px gracefully
   stacks to 1-col on narrow viewports. */
body.sw-dark-twin.page-id-695 .elementor-element-c2g0003 > .e-con-inner {
  flex-wrap: wrap;
}
body.sw-dark-twin.page-id-695 .elementor-element-c2g0003 .elementor-element-c2h0003 {
  flex-basis: 100%;
}
body.sw-dark-twin.page-id-695 .elementor-element-c2g0003 .elementor-element-c2y0001,
body.sw-dark-twin.page-id-695 .elementor-element-c2g0003 .elementor-element-c2y0002 {
  flex: 1 1 calc(50% - 12px);
  min-width: 280px;
}
/* End Wave 2.A — Contact */

/* ============================================================================
   Sitewide dark Wave 2.B — Shop (post 118231)
   PR: sitewide-dark/wave-2-shop · Spec: § Wave 2.B
   FluentCart owns the /shop/ product-listing template; no Elementor MCP
   edits are made here. Body is painted dark by the sw-dark-twin token
   remap of --bg. Only the FC card whites + filter sidebar chrome need
   explicit token mapping. Selectors enumerated from live DOM probe at T16
   (.fct-product-card / .fct-shop-filter-wrapper-inner / .fct-shop-checkbox /
   .fct-shop-input / .fct-search-icon / h3.item-heading / button.toggle-icon).
   ============================================================================ */
/* Shop layout follows the Wave 1 legal-page pattern: paper hero panel
   (the existing sitewide P3 M8 chrome-verify rule at L260 already paints
   var(--ink) — paper in sw-dark-twin scope), paper cards section, dark
   body around them. The hero override REMOVED — let the chrome-verify
   rule paint paper as it already does for legal pages.
   Operator review iter-2 → iter-3 (2026-05-13). */

/* Hero h1 + breadcrumb on the paper hero panel — same pattern as Wave 1
   GetTerms hero polish (L6092+). */
body.sw-dark-twin.page-id-118231 .elementor-element-1598dceb .elementor-heading-title {
  color: var(--bg) !important;
}
body.sw-dark-twin.page-id-118231 .ct-breadcrumbs {
  color: var(--bg);
}
body.sw-dark-twin.page-id-118231 .ct-breadcrumbs a {
  color: var(--blue);
}

/* FC product cards — operator preference iter-7: cards now sit inside the
   paper FC block, so bump to pure white #FFFFFF for distinction. Stronger
   border keeps the card edges visible against the surrounding paper. */
body.sw-dark-twin.page-id-118231 .fct-product-card {
  background-color: rgb(255 255 255) !important;
  border: 1px solid rgb(11 27 43 / 28%);
  border-radius: var(--radius-sm, 6px);
  color: #0B1B2B;
  padding: 16px;
}
body.sw-dark-twin.page-id-118231 .fct-product-card:hover {
  border-color: #1F5F85;
  box-shadow: 0 4px 16px rgb(11 27 43 / 16%);
}
/* Logo padding inside card — image wrap padding so logos don't fill the
   card edge-to-edge. */
body.sw-dark-twin.page-id-118231 .fct-product-card-image-wrap {
  padding: 24px 24px 8px;
}
body.sw-dark-twin.page-id-118231 .fct-product-card-image {
  max-height: 140px !important;
  object-fit: contain;
}
body.sw-dark-twin.page-id-118231 .fct-product-card-title,
body.sw-dark-twin.page-id-118231 a.fct-product-card-title {
  color: #0B1B2B !important;
}
body.sw-dark-twin.page-id-118231 .fct-product-card-prices,
body.sw-dark-twin.page-id-118231 .fct-item-price,
body.sw-dark-twin.page-id-118231 .fct-item-price span {
  color: #5A6675; /* mute on paper */
}

/* View Options button — iter-3 restyled. Operator review found iter-2's
   blue-on-dark-twin pattern "weird" on paper cards. Switch to
   navy-solid + paper text (matches the sitewide brand button on light
   canvas). */
body.sw-dark-twin.page-id-118231 .fct-product-view-button,
body.sw-dark-twin.page-id-118231 button.fct-product-view-button {
  background-color: #1F5F85 !important; /* brand navy-blue (matches "Book Audit" header CTA) */
  color: #FAFAF7 !important;
  border: 1px solid #1F5F85 !important;
  padding: 10px 20px !important;
  border-radius: 4px !important;
  font-weight: 500 !important;
}
body.sw-dark-twin.page-id-118231 .fct-product-view-button:hover {
  background-color: #2A78A8 !important;
  border-color: #2A78A8 !important;
}
body.sw-dark-twin.page-id-118231 .fct-product-view-button .fct-button-text {
  color: inherit;
}

/* iter-7 — flip the whole FC block area (sidebar + cards) to paper.
   Operator preference Option B (full paper products section, like the
   legal-page paper-hero pattern but extended to the body). The block
   wrapper paints paper; sidebar text/chrome flip to navy; cards keep
   their paper bg + stronger border so they read as distinct panels. */
body.sw-dark-twin.page-id-118231 .wp-block-fluent-cart-products {
  background-color: #F5F7FA;
  padding: 64px 56px;
  border-radius: 8px;
  margin: 48px 32px 80px;
  color: #0B1B2B;
  width: auto !important; /* override FC's compiled 1290px so margins respect parent */
}

/* FC sidebar filter chrome — now paper-canvas */
body.sw-dark-twin.page-id-118231 .fct-shop-filter-wrapper,
body.sw-dark-twin.page-id-118231 .fct-shop-filter-wrapper-inner,
body.sw-dark-twin.page-id-118231 .fct-shop-filter-form {
  color: #0B1B2B;
}
/* Filter section headings (Product Categories / Brands / Price) — eyebrow */
body.sw-dark-twin.page-id-118231 .fct-shop-filter-item .item-heading {
  color: #1F5F85;
  font: 500 12px/1.2 var(--font-mono, ui-monospace, monospace);
  letter-spacing: 0.14em;
  text-transform: uppercase;
}
/* Toggle icon arrows — navy on paper */
body.sw-dark-twin.page-id-118231 .fct-shop-item-collapse-wrap .toggle-icon path {
  stroke: #5A6675 !important;
}
/* Checkbox labels — navy text */
body.sw-dark-twin.page-id-118231 .fct-shop-checkbox,
body.sw-dark-twin.page-id-118231 label.fct-shop-checkbox {
  color: #0B1B2B;
}
body.sw-dark-twin.page-id-118231 .fct-shop-checkbox input[type="checkbox"] {
  accent-color: #1F5F85;
}
/* iter-9 — bump checkmark + slider borders to match card border (28% alpha) */
body.sw-dark-twin.page-id-118231 .fct-shop-checkbox .checkmark {
  border: 1px solid rgb(11 27 43 / 28%) !important;
}
body.sw-dark-twin.page-id-118231 .noUi-handle {
  border: 1px solid rgb(11 27 43 / 28%) !important;
  box-shadow: none !important;
}
body.sw-dark-twin.page-id-118231 .noUi-target {
  border: 1px solid rgb(11 27 43 / 28%) !important;
  background-color: rgb(11 27 43 / 8%) !important;
  box-shadow: none !important;
}
body.sw-dark-twin.page-id-118231 .noUi-connect {
  background: #1F5F85 !important;
}

/* Search input + clear — paper canvas pair */
body.sw-dark-twin.page-id-118231 .fct-shop-input,
body.sw-dark-twin.page-id-118231 input.fct-shop-input,
body.sw-dark-twin.page-id-118231 .fct-shop-product-search input {
  background-color: rgb(255 255 255) !important;
  border: 1px solid rgb(11 27 43 / 28%) !important;
  color: #0B1B2B !important;
}
body.sw-dark-twin.page-id-118231 .fct-shop-input::placeholder {
  color: #5A6675 !important;
}
body.sw-dark-twin.page-id-118231 .fct-search-icon svg,
body.sw-dark-twin.page-id-118231 .fct-search-icon path {
  stroke: #5A6675 !important;
  fill: #5A6675 !important;
}

/* Price range numeric inputs — iter-7: flipped to paper-canvas pair.
   White input bg + navy border + navy text + brand navy focus accent. */
body.sw-dark-twin.page-id-118231 .fct-shop-filter-item input[type="number"],
body.sw-dark-twin.page-id-118231 .fct-shop-filter-item input[type="text"],
body.sw-dark-twin.page-id-118231 .fc_price_range_input {
  background-color: rgb(255 255 255) !important;
  border: 1px solid rgb(11 27 43 / 28%) !important;
  color: #0B1B2B !important;
  padding: 8px 12px !important;
  font-size: 14px;
  border-radius: 4px;
}
body.sw-dark-twin.page-id-118231 .fct-shop-filter-item input[type="number"]:focus,
body.sw-dark-twin.page-id-118231 .fct-shop-filter-item input[type="text"]:focus,
body.sw-dark-twin.page-id-118231 .fc_price_range_input:focus {
  border-color: #1F5F85 !important;
  outline: none !important;
}
body.sw-dark-twin.page-id-118231 .fct-shop-currency-sign {
  color: #0B1B2B !important;
  font-weight: 500;
  font-size: 14px;
  margin-right: 6px;
  padding-left: 2px;
}
body.sw-dark-twin.page-id-118231 .fct-shop-price-range {
  display: flex;
  align-items: center;
  gap: 4px;
}
body.sw-dark-twin.page-id-118231 .fct-shop-price-range-wrap {
  display: flex;
  gap: 12px;
  padding-bottom: 16px;
}

/* View switcher + mobile filter toggle — iter-5: more breathing room
   (gap between + padding under) so the controls don't feel cramped against
   the card area. */
body.sw-dark-twin.page-id-118231 .fct-shop-view-switcher {
  display: flex;
  gap: 8px;
  padding-bottom: 20px;
}
body.sw-dark-twin.page-id-118231 .fct-shop-view-switcher button,
body.sw-dark-twin.page-id-118231 .fct-shop-filter-toggle-button {
  background-color: rgb(255 255 255);
  color: #0B1B2B;
  border: 1px solid rgb(11 27 43 / 28%);
  padding: 8px 10px;
  border-radius: 4px;
}
body.sw-dark-twin.page-id-118231 .fct-shop-view-switcher button:hover,
body.sw-dark-twin.page-id-118231 .fct-shop-filter-toggle-button:hover {
  border-color: #1F5F85;
}
body.sw-dark-twin.page-id-118231 .fct-shop-view-switcher svg path,
body.sw-dark-twin.page-id-118231 .fct-shop-filter-toggle-button svg path {
  stroke: #0B1B2B !important;
  fill: #0B1B2B !important;
}
/* End Wave 2.B — Shop */

/* ============================================================================
   Sitewide dark Wave 2 — Newsletter band sitewide standardization
   PR: sitewide-dark/wave-2-shop · Operator review iter-3 (2026-05-13)
   ----------------------------------------------------------------------------
   The "Need some help? Subscribe to SyteWide for FREE." Elementor section
   renders on 8 dark-twin surfaces (Shop + 7 Wave 1 legal/ticketing). Each
   page has its own copy with a different element ID, but all 8 paint the
   same hardcoded #181C1F (off-token charcoal) via per-page Elementor CSS
   at (0,3,0). Override here at (0,3,1) to flip them to var(--bg) so the
   band merges seamlessly with the surrounding dark canvas.
   ============================================================================ */
body.sw-dark-twin .elementor-element-38d316a7,  /* Shop */
body.sw-dark-twin .elementor-element-2730bd9d,  /* Privacy Policy */
body.sw-dark-twin .elementor-element-4702045c,  /* Terms and Conditions */
body.sw-dark-twin .elementor-element-7e462539,  /* Refund Policy */
body.sw-dark-twin .elementor-element-24e85eb2,  /* Cookie Policy */
body.sw-dark-twin .elementor-element-13cdcfda,  /* Acceptable Use Policy */
body.sw-dark-twin .elementor-element-14715930,  /* EULA */
body.sw-dark-twin .elementor-element-2e3e82be { /* Ticketing Portal */
  background-color: var(--bg) !important;
}

/* Newsletter h2 ("Need some help? Subscribe to SyteWide for FREE.")
   was authored at 52px / Inter 600 — closer to hero h1 size than the
   sitewide P3 h2 standard (clamp(28-36) / 600 / 1.15). Bring it back
   to the design-system h2 size on all 8 dark-twin surfaces with the
   newsletter section. The selector chain ensures the rule only
   matches the heading widget INSIDE one of the known newsletter
   sections, not every heading on the page. */
body.sw-dark-twin .elementor-element-38d316a7 .elementor-heading-title,
body.sw-dark-twin .elementor-element-2730bd9d .elementor-heading-title,
body.sw-dark-twin .elementor-element-4702045c .elementor-heading-title,
body.sw-dark-twin .elementor-element-7e462539 .elementor-heading-title,
body.sw-dark-twin .elementor-element-24e85eb2 .elementor-heading-title,
body.sw-dark-twin .elementor-element-13cdcfda .elementor-heading-title,
body.sw-dark-twin .elementor-element-14715930 .elementor-heading-title,
body.sw-dark-twin .elementor-element-2e3e82be .elementor-heading-title {
  font-size: clamp(28px, 4vw, 36px) !important;
  line-height: 1.18 !important;
  letter-spacing: -0.008em !important;
}

/* WPForms newsletter SUBSCRIBE button — sitewide. WPForms ships with a
   default royal-blue (#2872FA) submit that conflicts with brand --blue
   on dark canvas. Remap to --blue + dark text + --blue-bright on hover.
   Scoped to sw-dark-twin so it only applies on dark-canvas surfaces;
   light pages keep WPForms' default until a sitewide rebrand. */
body.sw-dark-twin .wpforms-submit,
body.sw-dark-twin button.wpforms-submit,
body.sw-dark-twin input[type="submit"].wpforms-submit {
  background-color: var(--blue) !important;
  color: var(--bg) !important;
  border: 1px solid var(--blue) !important;
  font-weight: 500 !important;
}
body.sw-dark-twin .wpforms-submit:hover,
body.sw-dark-twin button.wpforms-submit:hover,
body.sw-dark-twin input[type="submit"].wpforms-submit:hover {
  background-color: var(--blue-bright) !important;
  border-color: var(--blue-bright) !important;
}

/* /audit/ + /newsletter/ "What you get" / "What to expect" sections —
   both authored with `data-sw-section="expect"` and identical structure:
   eyebrow-heading + h2 + 3-col flex row with (eyebrow + body) per column.
   Elementor's per-element flex compiles columns to `flex: 0 1 auto` so
   each column renders at its natural content width (1270px) and the row
   collapses to a stack rather than distributing. The original render
   was 3 numbered headings + paragraphs floating on dark canvas with no
   visual chrome — operator review: "refactor this so it's better."

   Refactor: override the inner flex row with a real 3-col grid + wrap
   each column as a tile (matching .sw-tile sitewide primitive: subtle
   paper-alpha background + 1px hairline + hover lift) + restyle the
   numbered eyebrow with mono + blue-bright + restyle the body
   paragraph at readable 17px size. Mobile drops to single column. */
body.sw-darkform-page [data-sw-section="expect"] > .e-con-boxed > .e-con-inner > .e-con {
  display: grid !important;
  grid-template-columns: repeat(3, 1fr) !important;
  gap: 24px !important;
  margin-top: var(--space-5);
}
body.sw-darkform-page [data-sw-section="expect"] > .e-con-boxed > .e-con-inner > .e-con > .e-con {
  background: rgb(255 255 255 / 4%);
  border: 1px solid rgb(255 255 255 / 14%);
  border-radius: 6px;
  padding: 28px !important;
  display: flex;
  flex-direction: column;
  gap: 8px;
  transition: border-color 0.18s ease, transform 0.18s ease, background-color 0.18s ease;
}
body.sw-darkform-page [data-sw-section="expect"] > .e-con-boxed > .e-con-inner > .e-con > .e-con:hover {
  border-color: rgb(255 255 255 / 32%);
  background: rgb(255 255 255 / 6%);
  transform: translateY(-2px);
}
/* Numbered eyebrow ("01 — Scope", "01 — Cadence") — first heading widget
   inside each tile. Mono + blue-bright + uppercase + tight tracking. */
body.sw-darkform-page [data-sw-section="expect"] > .e-con-boxed > .e-con-inner > .e-con > .e-con > .elementor-widget-heading:first-child .elementor-heading-title {
  font-family: var(--font-mono) !important;
  font-size: var(--size-eyebrow) !important;
  font-weight: 500 !important;
  line-height: 1.2 !important;
  letter-spacing: 0.14em !important;
  text-transform: uppercase !important;
  color: var(--blue-bright) !important;
  margin: 0 0 4px !important;
}
/* Body paragraph — second heading widget. Bring to readable body size
   with the dark-canvas paper-78% color used elsewhere in dark-twin. */
body.sw-darkform-page [data-sw-section="expect"] > .e-con-boxed > .e-con-inner > .e-con > .e-con > .elementor-widget-heading:nth-child(2) .elementor-heading-title {
  font-family: var(--font-body) !important;
  font-size: 17px !important;
  font-weight: 400 !important;
  line-height: 1.6 !important;
  letter-spacing: 0 !important;
  text-transform: none !important;
  color: rgb(245 247 250 / 82%) !important;
  margin: 0 !important;
}
@media (max-width: 900px) {
  body.sw-darkform-page [data-sw-section="expect"] > .e-con-boxed > .e-con-inner > .e-con {
    grid-template-columns: 1fr !important;
  }
}

/* ============================================================================
   Sitewide dark Wave 3.A — Home (post 700)
   PR: sitewide-dark/wave-3-home · Spec: § Wave 3.A
   Actual data-sw-section values on this surface (recon): hero, pillars-intro,
   methodology, end-cta. Plan's "pillars" / "cta" canonical names were not
   present; using existing values to avoid breaking other rules that target
   them.
   ============================================================================ */
/* Band alternation S / D / S / D / S (operator iterate-3 2026-05-13)
   Five bands: hero · product-reel · pillars-intro · methodology · end-cta.
   Hero LIFTS, reel canvas, pillars LIFTS, methodology canvas, end-cta LIFTS.
   "Bookend lift" pattern — top/middle/bottom on soft, gaps on canvas.
   NOTE: sitewide !important rule force-sets methodology + end-cta to
   var(--bg) across all sw-dark-twin pages, so !important required to win
   on lifted bands. */
body.sw-dark-twin.page-id-700 [data-sw-section="hero"] {
  background: var(--bg-soft) !important;
}
body.sw-dark-twin.page-id-700 [data-sw-section="product-reel"] {
  background: var(--bg) !important;
}
body.sw-dark-twin.page-id-700 [data-sw-section="pillars-intro"] {
  background: var(--bg-soft) !important;
}
body.sw-dark-twin.page-id-700 [data-sw-section="methodology"] {
  background: var(--bg) !important;
}
body.sw-dark-twin.page-id-700 [data-sw-section="end-cta"] {
  background: var(--bg-soft) !important;
}
/* Hero h1 — specificity-bumped via .elementor-page chain (M-024) in case
   any per-element rule lingers. */
body.elementor-page.sw-dark-twin.page-id-700 [data-sw-section="hero"] .elementor-heading-title,
body.elementor-page.sw-dark-twin.page-id-700 [data-sw-section="hero"] h1 {
  color: var(--ink);
}
/* Pillar tile chrome — sw-tile rules already paper on dark; failsafe for
   tile p color in the pillars-intro band. */
body.sw-dark-twin.page-id-700 [data-sw-section="pillars-intro"] .sw-tile p {
  color: rgb(245 247 250 / 78%);
}
/* Methodology step description text */
body.sw-dark-twin.page-id-700 [data-sw-section="methodology"] .elementor-widget-text-editor p,
body.sw-dark-twin.page-id-700 [data-sw-section="methodology"] p {
  color: rgb(245 247 250 / 80%);
}
/* End-CTA lede */
body.sw-dark-twin.page-id-700 [data-sw-section="end-cta"] .elementor-widget-text-editor p,
body.sw-dark-twin.page-id-700 [data-sw-section="end-cta"] p,
body.sw-dark-twin.page-id-700 [data-sw-section="end-cta"] .elementor-heading-title {
  color: rgb(245 247 250 / 80%);
}
/* End-CTA h2 — paper, overriding any leftover specificity */
body.elementor-page.sw-dark-twin.page-id-700 [data-sw-section="end-cta"] h2,
body.elementor-page.sw-dark-twin.page-id-700 [data-sw-section="end-cta"] .elementor-widget-heading:nth-child(2) .elementor-heading-title {
  color: var(--ink);
}
/* Secondary / ghost CTA buttons sitewide on dark-twin pages.
   Per-element settings hardcode #0B1B2B (dark ink) text + border which
   renders invisible on dark canvas. Targets data-sw-role markers
   set in Elementor: hero-cta-secondary, cta-secondary. */
.sw-dark-twin [data-sw-role="hero-cta-secondary"] .elementor-button,
.sw-dark-twin [data-sw-role="cta-secondary"] .elementor-button {
  color: var(--ink) !important;
  border-color: var(--rule-strong) !important;
  background-color: transparent !important;
}
.sw-dark-twin [data-sw-role="hero-cta-secondary"] .elementor-button:hover,
.sw-dark-twin [data-sw-role="hero-cta-secondary"] .elementor-button:focus,
.sw-dark-twin [data-sw-role="cta-secondary"] .elementor-button:hover,
.sw-dark-twin [data-sw-role="cta-secondary"] .elementor-button:focus {
  color: var(--bg) !important;
  background-color: var(--ink) !important;
  border-color: var(--ink) !important;
}
/* End Wave 3.A — Home */

/* ============================================================================
   Sitewide dark Wave 3.B — About Us (post 698)
   PR: sitewide-dark/wave-3-about-us · Spec: § Wave 3.B
   5 bands: hero · commitments · founder · network · end-cta.
   Same S/D/S/D/S alternation as Home (operator-locked pattern).
   Operator-led founder band hardcoded #FAFAF7 cleared at source via MCP.
   ============================================================================ */
/* 6 bands now: hero · product-reel · commitments · founder · network · end-cta.
   S/D/S/D/S/D alternation. */
body.sw-dark-twin.page-id-698 [data-sw-section="hero"] {
  background: var(--bg-soft) !important;
}
body.sw-dark-twin.page-id-698 [data-sw-section="product-reel"] {
  background: var(--bg) !important;
}
body.sw-dark-twin.page-id-698 [data-sw-section="commitments"] {
  background: var(--bg-soft) !important;
}
body.sw-dark-twin.page-id-698 [data-sw-section="founder"] {
  background: var(--bg) !important;
}
body.sw-dark-twin.page-id-698 [data-sw-section="network"] {
  background: var(--bg-soft) !important;
}
body.sw-dark-twin.page-id-698 [data-sw-section="end-cta"] {
  background: var(--bg) !important;
}
/* Hero h1 specificity bump for token remap */
body.elementor-page.sw-dark-twin.page-id-698 [data-sw-section="hero"] h1,
body.elementor-page.sw-dark-twin.page-id-698 [data-sw-section="hero"] .elementor-heading-title {
  color: var(--ink);
}
/* Phase D (2026-05-14): retired the previous `padding-top: 80px /
   padding-bottom: 64px` override here. About Us hero now inherits the
   canonical sitewide F1.2 clamp(96,12vw,144) outer + the new sitewide
   `[data-sw-section="hero"] > .e-con-inner { padding-block: 10px }`
   inner rule (added near end of file). Total Y = 144+10 = 308 matching
   detail-page baseline + the rest of the P3 hero family. The mobile
   M-017 extension at line ~6734 still overrides to 48/48 on <640px.
   `min-height: 0` was previously paired but is now redundant — F1.2
   doesn't set min-height. */
/* Commitment cards on canvas (--bg) so they pop against the soft commitments
   band. The legacy line 948 rule sets cards to rgb(19,35,56) which equals
   band bg under new alternation — flat. Force canvas tiles via higher
   specificity (.elementor-page chain). */
body.elementor-page.sw-dark-twin.page-id-698 .elementor-element.elementor-element-0bd2fc4.e-con,
body.elementor-page.sw-dark-twin.page-id-698 .elementor-element.elementor-element-3c61715.e-con,
body.elementor-page.sw-dark-twin.page-id-698 .elementor-element.elementor-element-54ac47c.e-con {
  background-color: var(--bg) !important;
}
/* End Wave 3.B — About Us */

/* ============================================================================
   Sitewide dark Wave 3.C — Services Hub (post 123633) — FINAL PHASE
   PR: sitewide-dark/wave-3-services-hub · Spec: § Wave 3.C
   5 bands: hero · product-reel · pillars · methodology · end-cta.
   S/D/S/D/S alternation matching Home Wave 3.A pattern.
   Closes the sitewide-dark rollout — 14 surfaces total.
   ============================================================================ */
body.sw-dark-twin.page-id-123633 [data-sw-section="hero"] {
  background: var(--bg-soft) !important;
}
body.sw-dark-twin.page-id-123633 [data-sw-section="product-reel"] {
  background: var(--bg) !important;
}
body.sw-dark-twin.page-id-123633 [data-sw-section="pillars"] {
  background: var(--bg-soft) !important;
}
body.sw-dark-twin.page-id-123633 [data-sw-section="methodology"] {
  background: var(--bg) !important;
}
body.sw-dark-twin.page-id-123633 [data-sw-section="end-cta"] {
  background: var(--bg-soft) !important;
}
/* Hero h1 specificity bump */
body.elementor-page.sw-dark-twin.page-id-123633 [data-sw-section="hero"] h1,
body.elementor-page.sw-dark-twin.page-id-123633 [data-sw-section="hero"] .elementor-heading-title {
  color: var(--ink);
}
/* Phase D (2026-05-14): retired — Services Hub hero now inherits the
   canonical sitewide F1.2 + new hero-inner-padding rule, matching every
   other P3 hero (308 total Y desktop). See About Us block above for
   the rationale. Mobile M-017 extension still floors to 48/48. */
/* Pillar tile cards on canvas (--bg) so they pop against soft pillars band.
   Same pattern as About Us iterate-1. */
body.elementor-page.sw-dark-twin.page-id-123633 .elementor-element.elementor-element-e221881.e-con,
body.elementor-page.sw-dark-twin.page-id-123633 .elementor-element.elementor-element-09b25c6.e-con,
body.elementor-page.sw-dark-twin.page-id-123633 .elementor-element.elementor-element-5fbc993.e-con {
  background-color: var(--bg) !important;
  border-color: rgb(245 247 250 / 22%) !important;
}
/* End Wave 3.C — Services Hub */

/* mobile-audit followup M-017 extension (hero-consolidation 2026-05-14, Phase 3):
   About Us (698) and Services Hub (123633) have explicit desktop hero-padding
   rules at the end-of-file Wave 3.A/3.B/3.C blocks (lines ~6730-6735 and
   ~6777-6783) that set padding-top:80px / padding-bottom:64px at ALL viewport
   sizes including mobile. These come AFTER the M-017 @media (max-width:640px)
   block at line ~2978, so the desktop rule wins the source-order cascade
   on mobile even though the intent is 80/64 desktop only.

   Fix: append mobile-only overrides AFTER the end-of-file desktop rules to
   restore the canonical 48px/48px hero padding on mobile for these two pages.
   Same value as M-017 home rule. Same pattern as prior mobile-audit end-of-file
   additions per [[project-locked-rule-cascade-issue]].

   Selector chain: body.sw-locked-surface.page-id-X [data-sw-section="hero"]
   matches the specificity of the desktop rules above (both use
   body.sw-locked-surface.page-id-X chain) so source-order places this AFTER
   and wins. The !important is consistent with the desktop rule. */
@media (max-width: 640px) {
  body.sw-locked-surface.page-id-698 [data-sw-section="hero"],
  body.sw-locked-surface.page-id-123633 [data-sw-section="hero"] {
    padding-top: 48px !important;
    padding-bottom: 48px !important;
  }
}

/* 2026-05-15 — Sitewide header cart button (Blocksy CB 122841).
   Inserted to the right of "Book Audit" CTA. Paper-coloured icon on the
   dark header chrome; cyan-bright count badge that only renders when the
   FluentCart cart has items. Anchor href falls back to /cart/ for
   reliable sitewide navigation; carries data-fluent-cart-cart-toggle-button
   so FC's drawer JS opens the slide-out where its drawer markup is in
   the DOM (commerce surfaces).

   Color uses literal #FAFAF7 (not var(--bg)) because body.sw-dark-twin
   remaps --bg to the dark canvas value, but the header chrome is always
   dark regardless of page-level twin state. Same trick used by the nav
   menu links (Blocksy renders them at paper hardcoded). */
/* The header CB (122841) container is a 3-column CSS grid
   (1fr auto 1fr) authored for {logo, nav, Book Audit}. Adding the 4th item
   auto-flows to row 2. Fix: pin BOTH the Book Audit button and the cart
   wrapper into cell 3 row 1 with justify-self:end. Audit gets margin-right
   52 (40px cart + 12px gap) so the cart sits flush at the cell's right
   edge with proper spacing.

   iter-15w (2026-05-20): SCOPED to ≥641px. Mobile uses a separate flex
   layout (logo left, Book + hamburger paired right) defined further down
   in the mobile @media block; this desktop grid rule was bleeding into
   mobile and pinning Book to col 3 of the unwanted 1fr/auto/1fr grid. */
@media (min-width: 641px) {
  .elementor-element-1674add,
  .elementor-element-5942155 {
    grid-column: 3 !important;
    grid-row: 1 !important;
    justify-self: end !important;
    align-self: center !important;
  }
  .elementor-element-5942155 {
    width: auto !important;
    flex: 0 0 auto !important;
  }
  .elementor-element-1674add {
    margin-right: 52px !important;
  }
}
.sw-header-cart {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 40px;
  height: 40px;
  margin-left: 12px;
  color: #FAFAF7;
  text-decoration: none;
  border-radius: var(--radius-sm);
  transition: color 0.15s ease, background-color 0.15s ease;
}
.sw-header-cart:hover,
.sw-header-cart:focus-visible {
  color: var(--blue-bright);
  background-color: rgb(250 250 247 / 8%);
}
.sw-header-cart__icon {
  width: 22px;
  height: 22px;
  display: block;
  flex: 0 0 auto;
}
.sw-header-cart__count {
  position: absolute;
  top: 4px;
  right: 2px;
  min-width: 16px;
  height: 16px;
  padding: 0 4px;
  border-radius: 999px;
  background-color: var(--blue-bright);
  color: var(--ink);
  font-family: var(--font-mono);
  font-size: 11px;
  font-weight: 600;
  line-height: 16px;
  text-align: center;
  pointer-events: none;
}
/* Hide badge when cart is empty. Empty string (untouched) and "0" both
   hide; FC's frontend JS populates with integer >= 1 when items exist. */
.sw-header-cart__count:empty,
.sw-header-cart[data-cart-empty="true"] .sw-header-cart__count {
  display: none;
}

/* ── Header phone click-to-call (copy-rewrite 2026-06-02) ──────────────────
   Lives in the right-hand header utility cluster beside the cart, inside the
   same HTML widget (5942155) so the #sw-header-light grid keeps its 4-child
   :nth-child mapping intact. Paper-white to match the transparent-over-dark
   header (nav + cart colours). Desktop: icon + number. Mobile handling (icon
   only, cart hidden) lives in the mobile media query further down. */
.sw-header-utils {
  display: inline-flex;
  align-items: center;
  gap: 4px;
}
.sw-header-phone {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  height: 40px;
  padding: 0 10px;
  color: #FAFAF7;
  text-decoration: none;
  white-space: nowrap;
  border-radius: var(--radius-sm);
  font-family: var(--font-mono);
  font-size: 13px;
  font-weight: 600;
  letter-spacing: 0.02em;
  transition: color 0.15s ease, background-color 0.15s ease;
}
.sw-header-phone:hover,
.sw-header-phone:focus-visible {
  color: var(--blue-bright);
  background-color: rgb(250 250 247 / 8%);
}
.sw-header-phone__icon {
  width: 17px;
  height: 17px;
  display: block;
  flex: 0 0 auto;
}
/* The header phone is an ICON-ONLY tap/click target — the readable digits live
   in the footer, Contact, Foundation and the Home closing CTA. The number text
   never renders in the compact header (it can't share grid column 3 with the
   Book button + cart without crowding the centred nav). aria-label + title
   carry the number for AT + hover. */
.sw-header-phone__text { display: none; }
/* Desktop ≥1360px: the right cluster (Book button · phone · cart) clears the
   centred nav only here. Reserve room to the button's right so the phone + cart
   sit beside it instead of stacking in the same grid cell. 2026-06-02: bumped
   100 → 120px so the phone icon has equal breathing room on both sides — the
   icon→cart gap is ~35px (phone padding + utils gap + cart padding), so the
   button→icon gap needs to match instead of crowding the button. */
@media (min-width: 1360px) {
  #sw-header-light > .elementor-element-1674add { margin-right: 120px !important; }
  #sw-header-light > .elementor-element-5942155 { justify-self: end !important; }
}
/* 768–1359px (tablet / small laptop): not enough room for the phone beside the
   button — show the cart only, exactly as before the phone was added. */
@media (min-width: 768px) and (max-width: 1359px) {
  .sw-header-phone { display: none; }
}

/* 2026-05-15 — FluentCart system pages (/cart/ 118233, /checkout/ 118235)
   reconciled to the sitewide marketing typography standard.

   Problem inventoried 2026-05-15: both pages have two Elementor-authored
   sections with per-widget heading typography that escapes the sitewide
   standard (clamp(28-36) / 600 / 1.15 for section h2 per
   docs/developer/design-system/typography.md):
     - "Cart" / "Checkout" page-title hero h2 @ 60px (Blocksy --size-h2
       inherited default)
     - "Need some help? Subscribe to SyteWide for FREE." CTA h2 @ 52px

   Neither page is in body.sw-locked-surface (FC's native layouts on cart
   + checkout would conflict with the locked section-padding rule). Solution:
   scope an h2 clamp override to these specific page IDs, matching the
   /products/ archive (page-id 123641) pattern at line ~3523. */
body.page-id-118233 .elementor h2,
body.page-id-118233 .elementor h2.elementor-heading-title,
body.page-id-118235 .elementor h2,
body.page-id-118235 .elementor h2.elementor-heading-title,
body.page-id-118231 .elementor h2,
body.page-id-118231 .elementor h2.elementor-heading-title {
  font-size: clamp(28px, 3vw, 36px) !important;
  font-weight: 600 !important;
  line-height: 1.15 !important;
}

/* 2026-05-15 — FluentCart empty-cart fragment vertically centered.
   The .fluent-cart-cart-empty-content div renders inside a shortcode
   widget when the cart is empty; FC ships it as display:flex but with
   natural ~280px content height, with the SVG + "Your cart is empty"
   text sitting flush to the top of its parent's 300px section. Operator
   wants the SVG + text visually centered without ballooning the section.

   Approach: give the empty-content a compact-but-comfortable padded
   height (~440px), switch flex direction to column (FC's default is
   row), and center both axes. Scoped to the FC class so it only fires
   on the empty-cart state. */
.fluent-cart-cart-empty-content {
  min-height: clamp(360px, 40vh, 440px);
  flex-direction: column;
  align-items: center !important;
  justify-content: center !important;
}

/* 2026-05-15 — WPForms newsletter form on /cart/ + /checkout/ reskinned
   to match the locked /newsletter/ + /audit/ FluentForm chrome
   (translucent-paper input on dark canvas, cyan-bright Subscribe submit
   in --blue-bright with --ink text). WPForms doesn't get the existing
   sw-darkform-page rules (those target .ff-el-form-control selectors).
   Scoped to the two FC system pages so we don't repaint WPForms forms
   elsewhere.

   Probed reference: /newsletter/ FF input rgba(245,247,250,0.06) bg /
   paper text / 7px radius / 11px-15px padding; submit --blue-bright bg
   / --ink text / 4px radius / 12px-22px padding / 15px weight 500. */
body.page-id-118233 .wpforms-form,
body.page-id-118235 .wpforms-form,
body.page-id-118231 .wpforms-form {
  max-width: var(--width-prose);
  margin-inline: auto;
}
/* WPForms internal wrappers collapse to 1px because they're flex:1 1 0%
   items with no explicit basis. Force the field + field-container to
   take the available row width so the input renders at a usable size
   (~430px instead of the 32px content-size default). */
body.page-id-118233 .wpforms-form .wpforms-field-container,
body.page-id-118233 .wpforms-form .wpforms-field,
body.page-id-118235 .wpforms-form .wpforms-field-container,
body.page-id-118235 .wpforms-form .wpforms-field,
body.page-id-118231 .wpforms-form .wpforms-field-container,
body.page-id-118231 .wpforms-form .wpforms-field {
  width: 100% !important;
  flex: 1 1 auto !important;
  max-width: 100% !important;
}
body.page-id-118233 .wpforms-form .wpforms-field input[type="text"],
body.page-id-118233 .wpforms-form .wpforms-field input[type="email"],
body.page-id-118235 .wpforms-form .wpforms-field input[type="text"],
body.page-id-118235 .wpforms-form .wpforms-field input[type="email"],
body.page-id-118231 .wpforms-form .wpforms-field input[type="text"],
body.page-id-118231 .wpforms-form .wpforms-field input[type="email"] {
  background-color: rgb(245 247 250 / 6%) !important;
  color: #F5F7FA !important;
  border: 1px solid rgb(245 247 250 / 30%) !important;
  border-radius: 7px !important;
  padding: 11px 15px !important;
  font-size: 16px !important;
  font-family: var(--font-sans) !important;
  width: 100% !important;
  height: auto !important;
  min-height: 44px !important;
  box-sizing: border-box !important;
}
body.page-id-118233 .wpforms-form .wpforms-field input::placeholder,
body.page-id-118235 .wpforms-form .wpforms-field input::placeholder,
body.page-id-118231 .wpforms-form .wpforms-field input::placeholder {
  color: rgb(245 247 250 / 50%) !important;
}
body.page-id-118233 .wpforms-form .wpforms-submit,
body.page-id-118235 .wpforms-form .wpforms-submit,
body.page-id-118231 .wpforms-form .wpforms-submit {
  background-color: var(--blue-bright) !important;
  color: var(--ink) !important;
  border: 0 !important;
  border-radius: 4px !important;
  padding: 12px 22px !important;
  font-family: var(--font-sans) !important;
  font-size: 15px !important;
  font-weight: 500 !important;
  letter-spacing: normal !important;
  text-transform: none !important;
  cursor: pointer !important;
  transition: background-color 0.15s ease, color 0.15s ease !important;
}
body.page-id-118233 .wpforms-form .wpforms-submit:hover,
body.page-id-118233 .wpforms-form .wpforms-submit:focus-visible,
body.page-id-118235 .wpforms-form .wpforms-submit:hover,
body.page-id-118235 .wpforms-form .wpforms-submit:focus-visible,
body.page-id-118231 .wpforms-form .wpforms-submit:hover,
body.page-id-118231 .wpforms-form .wpforms-submit:focus-visible {
  background-color: #FAFAF7 !important;
  color: var(--ink) !important;
}

/* 2026-05-15 — Hide placeholder testimonials + stock-photo bands on
   /cart/ + /checkout/ + /shop/. Sections render fabricated client logos
   (ALPHA / COLOR HOUSE / LARAWAY) and a generic beach stock photo —
   violations of the locked voice rule against fabricated clients and
   stock-photo authenticity (see voice-positioning.md). Hide via CSS
   until real testimonial content lands; operator can re-enable by
   removing this block. Section IDs probed live 2026-05-15. */
body.page-id-118233 .elementor-element-47975f0d,
body.page-id-118233 .elementor-element-4067aa52,
body.page-id-118235 .elementor-element-5cdf1c5,
body.page-id-118235 .elementor-element-59e04d88,
body.page-id-118231 .elementor-element-26ac1759 {
  display: none !important;
}

/* 2026-05-15 — Archive title chrome for /blog/ (body.blog). The
   /case-studies/ surface was originally styled here too, but PR 2
   (sculpted-adapt sequence) replaced it with archive-sw_case_study.php
   + a custom .sw-cs-archive-* CSS block at end-of-file. The shared
   archive-title selectors below now only target body.blog.

   The h1 lives in Blocksy's archive title widget (.ct-archive-title or
   .page-title); we wrap it in a hero-style band via padding + canvas
   color + clamp typography matching the locked marketing standard. */
body.blog .ct-archive-title,
body.blog .page-title {
  font-family: var(--font-sans) !important;
  font-size: clamp(28px, 3vw, 36px) !important;
  font-weight: 600 !important;
  line-height: 1.15 !important;
  color: var(--ink) !important;
  letter-spacing: -0.012em !important;
  margin: 0 !important;
  text-align: center;
}
/* Hero-style band container for the archive title. Blocksy renders the
   title inside `.hero-section.is-width-constrained` on archive surfaces;
   give it the marketing hero padding standard so it sits on its own
   canvas band like every other surface. */
body.blog .hero-section {
  background-color: var(--bg-soft) !important;
  padding-block: clamp(64px, 8vw, 96px) !important;
  padding-inline: clamp(20px, 7vw, 120px) !important;
  text-align: center;
}

/* 2026-05-15 — /blog/ card photo alignment. Blocksy's blog archive
   renders cards stacked as: eyebrow tags → title → photo → excerpt →
   date. When titles wrap to a different number of lines across cards
   in the same row (e.g., 3 lines on one card, 2 on the others), the
   photo block sits at a different y-position, breaking the horizontal
   alignment visitors expect. Operator preference 2026-05-15: photos
   should align across the row regardless of title length.

   Fix: reserve exactly 3 lines of title space on every card (clamp
   short titles to wide whitespace, clamp long titles with ellipsis).
   3 lines is the empirical max title length on the current blog and
   gives the photo a stable y-anchor. */
body.blog article.entry-card .entry-title {
  min-height: calc(1.2em * 3);
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
  text-overflow: ellipsis;
}
/* 2026-05-15 — Blog archive cards: add subtle ambient shadow + lift on
   hover. Matches the .sw-reel-video chrome pattern (1px border + radius
   + box-shadow on hover). */
body.blog article.entry-card {
  border-radius: var(--radius-sm, 6px);
  background-color: var(--bg);
  box-shadow:
    0 2px 6px rgb(11 27 43 / 10%),
    0 14px 36px rgb(11 27 43 / 18%);
  transition: box-shadow 0.25s ease, transform 0.25s ease;
}
body.blog article.entry-card:hover {
  box-shadow:
    0 4px 10px rgb(11 27 43 / 14%),
    0 24px 56px rgb(11 27 43 / 28%);
  transform: translateY(-2px);
}
/* 2026-05-15 — Blog post body images: bordered chrome matching the
   .sw-reel-video pattern (1px var(--rule-strong) + 6px radius + soft
   ambient shadow) so editorial photos read as intentional design
   surfaces. Scoped to body.single-post + the article's editorial img
   selector (already scoped to article.post via the cascade override
   above). */
body.single-post article.post .entry-content img,
body.single-post article.post .wp-block-image img,
body.single-post article.post figure.wp-block-image img,
body.single-sw_case_study article.sw-case-study .entry-content img,
body.single-sw_case_study article.sw-case-study .wp-block-image img,
body.single-sw_case_study article.sw-case-study figure.wp-block-image img {
  border: 1px solid var(--rule-strong, rgb(11 27 43 / 18%));
  border-radius: var(--radius-sm, 6px);
  box-shadow:
    0 2px 6px rgb(11 27 43 / 10%),
    0 14px 36px rgb(11 27 43 / 18%);
}
/* Constrain editorial body images on desktop so they read as embedded
   illustrations rather than full-bleed banners. Below 768 they keep
   the existing fluid full-width behavior. */
@media (min-width: 768px) {
  body.single-post article.post .entry-content img,
  body.single-post article.post .wp-block-image img,
  body.single-post article.post figure.wp-block-image img,
  body.single-sw_case_study article.sw-case-study .entry-content img,
  body.single-sw_case_study article.sw-case-study .wp-block-image img,
  body.single-sw_case_study article.sw-case-study figure.wp-block-image img {
    /* Updated 2026-05-27: 720px → var(--width-prose). Post-PR #136 cleanup —
       the lint whitelists 720 as a sanctioned tier value so this literal
       wasn't flagged in the original batch tokenize, but it IS the prose
       tier and should reference it. See widths.md (prose tier). */
    max-width: var(--width-prose) !important;
    margin-inline: auto !important;
    display: block !important;
  }
}

/* 2026-05-15 — Option A hero: asymmetric split (text left, image right).
   The JS shim below (loaded via wpforms-submit-text.php neighbor or
   inline footer) lifts the first body figure/img out of post content
   and into a .sw-hero-media-slot inside the hero. The shim then adds
   .sw-hero--has-media to the hero so this rule kicks in.

   Image stays full-width inside its slot (not capped to 720) because
   the hero column already constrains it to ~50% of the page. */
/* Boxed hero container at ALL viewports: matches body content widget's
   1250 max-width + ~7vw clamped padding-inline. Below 900vw the hero
   stacks single-column (no grid); above 900vw it becomes a 2-col grid
   (rule further below). Keeping the width treatment outside the
   media query ensures the image doesn't extend further left/right
   than the body paragraph on tablet/narrow-desktop viewports. */
body.single-post .sw-blog-hero.sw-hero--has-media,
body.single-sw_case_study .sw-case-hero.sw-hero--has-media {
  /* 2026-05-20 — match body block's content edges at every viewport.
     The body block has padding-inline: var(--section-x) on the outer
     and .e-con-inner { max-width: 1290px; margin: auto } inside, so
     its effective content edge is max(section-x, (vw - 1290)/2). The
     prior formula `section-x + 10` assumed .e-con-inner contributed
     10px of inner padding (it doesn't), and ignored the 1290px cap —
     leaving title + featured image extending 185px past the body p
     at 1920px. Replicating both terms here keeps hero text + image
     flush with article p L/R at every viewport ≥320px. */
  /* Updated 2026-05-27: 1290px → var(--width-canvas). Tokenized post-PR
     #136 cleanup; lint only checks max-width: literals so this slipped past
     the original sweep. See widths.md (canvas tier). */
  padding-inline: max(clamp(20px, 7vw, 120px), calc((100% - var(--width-canvas)) / 2)) !important;
  padding-top: clamp(40px, 5.5vw, 80px) !important;
  padding-bottom: clamp(40px, 5.5vw, 80px) !important;
  margin-top: 0 !important;
  max-width: none !important;
}

/* 2026-05-20 — neutralize Elementor's default 10px container padding on
   the first .e-con inside theme-post-content. When a post is authored in
   Elementor (vs Gutenberg/HTML), `theme-post-content` renders a nested
   .elementor wrapper whose top-level container carries Elementor's
   `--padding-inline-start: 10px` default. That 10px insets body p past
   the hero's section-x edge, breaking the title/image ↔ body p L/R
   alignment. Zeroing it here lets HTML- and Elementor-authored posts
   share the same body p position (= section-x). Scoped to article-body
   so nested containers inside the post content keep their own padding. */
body.single-post [data-sw-role="article-body"] > .elementor > .e-con,
body.single-sw_case_study [data-sw-role="article-body"] > .elementor > .e-con {
  padding-inline: 0 !important;
}
@media (min-width: 900px) {
  body.single-post .sw-blog-hero.sw-hero--has-media,
  body.single-sw_case_study .sw-case-hero.sw-hero--has-media {
    display: grid !important;
    /* Wider text column + narrower image — image reads smaller per
       operator preference 2026-05-16. Was 1.05fr / 1fr. */
    grid-template-columns: minmax(0, 1.35fr) minmax(0, 1fr) !important;
    gap: clamp(40px, 5vw, 72px) !important;
    align-items: center !important;
  }
  /* Kill any margin-top that Blocksy / Elementor inserts on the CB
     wrapper above the hero. Only margin-top — the hero's own
     padding-top rule above now provides intentional breathing space. */
  body.single-post .elementor.elementor-122846,
  body.single-post .elementor.elementor-122846 > .e-con:first-child {
    margin-top: 0 !important;
  }
  /* Blocksy wraps main content in an unnamed div with 60px padding-top
     by default. Zero it on blog single + case study so the hero image
     tucks right up to the header bottom (hero's own padding-top now
     provides the visual breathing room). */
  body.single-post main.site-main > div:first-child,
  body.single-sw_case_study main.site-main > div:first-child {
    padding-top: 0 !important;
  }
  .sw-hero-text-slot {
    grid-column: 1;
    display: flex;
    flex-direction: column;
    gap: 16px;
  }
  .sw-hero-media-slot {
    grid-column: 2;
    grid-row: 1;
    align-self: center;
    /* Cap the image's effective width on very wide viewports so it
       doesn't grow chunky relative to text. ~520 keeps the editorial
       balance at 1600+ viewports. */
    max-width: 520px;
    width: 100%;
    justify-self: end;
  }
}

/* Override the body-image 720px cap inside the hero media slot at ALL
   viewports. Specificity bumped with article.post / article.sw-case-study
   so we win over the body cap rule (0,3,3) via
   body.single-post article.post .entry-content img. Moved out of the
   @media (min-width: 900px) block 2026-05-20 — the 720 cap was leaking
   into mobile/tablet, overflowing the stacked single-column hero by ~60px
   at 768. The 3:2 aspect-ratio + object-fit applies at every viewport;
   the grid column constraint above only narrows it further on desktop. */
body.single-post article.post .sw-blog-hero .sw-hero-media-slot img,
body.single-post article.post .sw-blog-hero .sw-hero-media-slot figure,
body.single-sw_case_study article.sw-case-study .sw-case-hero .sw-hero-media-slot img,
body.single-sw_case_study article.sw-case-study .sw-case-hero .sw-hero-media-slot figure {
  width: 100% !important;
  max-width: 100% !important;
  margin: 0 !important;
  height: auto !important;
  aspect-ratio: 3 / 2;
  object-fit: cover;
  display: block;
}

/* Remove the hard 1px rule above the meta row — operator preference
   2026-05-16. The visual separation between the h1 block and the
   meta line now comes from the .sw-hero-text-slot 16px flex gap. */
body.single-post .sw-blog-hero .sw-blog-meta-row,
body.single-sw_case_study .sw-case-hero .sw-blog-meta-row {
  border-top: 0 !important;
  padding-top: 0 !important;
}
/* 2026-05-21 — operator: reduce padding above the title on mobile.
   Two earlier rules competed for blog hero pt on mobile: M0.2 set 48px
   on `.sw-blog-hero[data-sw-section="hero"]`, and the unscoped
   `.sw-blog-hero.sw-hero--has-media` clamp(40px, 5.5vw, 80px) floor
   pinned 40px (later in the file → wins). On 390vw that 40px reads as
   excess chrome before the breadcrumb. Mobile-only lower bound below
   wins by source order + the `body.single-post` chain bumping
   specificity to (0,0,3,1). */
@media (max-width: 640px) {
  body.single-post .sw-blog-hero[data-sw-section="hero"],
  body.single-post .sw-blog-hero.sw-hero--has-media {
    padding-top: 16px !important;
  }
}
/* On mobile the lifted image just stacks below the title block — no
   grid, but keep margin-top so it doesn't crash into the meta line. */
.sw-hero-media-slot { margin-top: 32px; }
@media (min-width: 900px) {
  .sw-hero-media-slot { margin-top: 0; }
}

/* Hide Blocksy's theme-post-featured-image widget on blog post +
   case-study singulars — the hero JS shim now renders the featured
   image in the hero's right column, so the standalone widget below
   the hero is a duplicate. */
body.single-post .elementor-widget-theme-post-featured-image,
body.single-sw_case_study .elementor-widget-theme-post-featured-image {
  display: none !important;
}

/* 2026-05-15 — [sw_recent_posts] compact 3-up card grid for the home
   page "Recent thinking" section. Image + date eyebrow + title only
   (no excerpt). Shares the shadow + radius treatment with the blog
   archive cards for visual continuity. Hover lift cues clickability.

   Card colors use LITERAL paper #FAFAF7 + ink #0B1B2B (not var(--bg) /
   var(--ink)) because body.sw-dark-twin remaps those tokens to the
   dark-canvas values — and we want cards to stay paper-on-cream
   regardless of the page's twin state. Same trick used by the header
   cart icon. */
.sw-recent-posts {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 24px;
  margin: 32px 0 0;
}
@media (max-width: 768px) {
  .sw-recent-posts { grid-template-columns: 1fr; }
}
.sw-recent-card {
  background-color: #FAFAF7;
  border-radius: var(--radius-sm, 6px);
  overflow: hidden;
  box-shadow:
    0 2px 6px rgb(11 27 43 / 10%),
    0 14px 36px rgb(11 27 43 / 18%);
  transition: box-shadow 0.25s ease, transform 0.25s ease;
}
.sw-recent-card:hover {
  box-shadow:
    0 4px 10px rgb(11 27 43 / 14%),
    0 24px 56px rgb(11 27 43 / 28%);
  transform: translateY(-2px);
}
.sw-recent-card__link {
  display: flex;
  flex-direction: column;
  height: 100%;
  text-decoration: none;
  color: inherit;
}
.sw-recent-card__media {
  aspect-ratio: 16 / 10;
  overflow: hidden;
  background-color: #F2F4F7;
  flex: 0 0 auto;
}
.sw-recent-card__media img {
  width: 100% !important;
  height: 100% !important;
  object-fit: cover;
  display: block;
  border: 0 !important;
  border-radius: 0 !important;
  box-shadow: none !important;
  max-height: none !important;
}
.sw-recent-card__meta {
  display: block;
  flex: 0 0 auto;
  margin: 16px 20px 6px;
  font-family: var(--font-mono);
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: #4A535F;
}
.sw-recent-card__title,
h3.sw-recent-card__title {
  margin: 4px 20px 24px !important;
  font-family: var(--font-sans) !important;
  font-size: 19px !important;
  font-weight: 600 !important;
  line-height: 1.3 !important;
  color: #0B1B2B !important;
}
.sw-recent-card:hover .sw-recent-card__title {
  color: #1F5F85;
}
/* On the dark-twin home page, give the cards deeper shadow so they lift
   off the canvas, and tag both the recent-posts section AND the
   immediately-following end-CTA with a cream-soft canvas — operator
   preference 2026-05-15: this new section should change canvas color,
   carrying the following sections with it for a unified close to the
   page (dark methodology → CREAM-SOFT recent-posts + end-CTA →
   dark footer). */
/* iter-15q (2026-05-19) → iter-19c (2026-05-20): operator pivoted the
   alternation twice. iter-15q made recent-posts DARK + end-cta LIGHT
   (inverting the 2026-05-15 cream-soft canvas-flip). iter-19c re-
   inverts after the walkthrough band landed at position 2: recent-posts
   back to LIGHT (--bg-soft) and end-cta back to DARK (--bg). The
   recent-posts selector here uses #sw-recent-posts (specificity 0,1,1,1)
   so it beats the lower-specificity element-id locks in the iter-19c
   block below at lines 9086+; that's why both blocks must agree. */
body.page-id-700 #sw-recent-posts,
body.page-id-700 [data-sw-section="recent-posts"],
body.page-id-700 .elementor-element.elementor-element-1a87a6e {
  background-color: var(--bg) !important;
}
body.page-id-700 .elementor-element.elementor-element-ee89f39 {
  background-color: var(--bg-soft) !important;
}
body.page-id-700 #sw-recent-posts,
body.page-id-700 [data-sw-section="recent-posts"],
body.page-id-700 .elementor-element.elementor-element-1a87a6e {
  padding-block: clamp(56px, 7vw, 80px) !important;
}
/* Recent-posts section is dark navy — h2 + eyebrow paint paper-on-dark. */
body.page-id-700 #sw-recent-posts h2,
body.page-id-700 [data-sw-section="recent-posts"] h2,
body.page-id-700 .elementor-element.elementor-element-1a87a6e h2 {
  color: #FAFAF7 !important;
}
body.page-id-700 .elementor-element.elementor-element-1a87a6e .sw-chapter-ebrow {
  color: rgb(245 247 250 / 70%) !important;
}
body.page-id-700 .elementor-element.elementor-element-1a87a6e .sw-chapter-ebrow strong {
  color: #FAFAF7 !important;
}
/* 2026-05-21 — field-log cards on home repainted to match the sitewide
   .sw-tile dark-canvas pattern. Prior state was paper-white bottoms on
   the dark navy canvas (off-brand). Now: translucent-white surface,
   hairline border, cyan eyebrow, paper title — same recipe as the
   "What we do" tiles and product-page sw-tile cards. */
body.page-id-700 .sw-recent-card {
  background-color: rgb(255 255 255 / 4%) !important;
  border: 1px solid rgb(255 255 255 / 14%);
  box-shadow: none;
}
body.page-id-700 .sw-recent-card:hover {
  background-color: rgb(255 255 255 / 6%) !important;
  border-color: rgb(255 255 255 / 36%);
  box-shadow: none !important;
}
body.page-id-700 .sw-recent-card__media {
  background-color: rgb(255 255 255 / 6%);
}
body.page-id-700 .sw-recent-card__meta {
  color: var(--blue-bright) !important;
}
body.page-id-700 .sw-recent-card__title,
body.page-id-700 h3.sw-recent-card__title {
  color: var(--ink) !important;
}
body.page-id-700 .sw-recent-card:hover .sw-recent-card__title {
  color: var(--blue-bright) !important;
}
/* 2026-05-21 — disable auto-hyphenation on the field-log h2 specifically.
   The sitewide M-007 rule applies `hyphens: auto` + `overflow-wrap:
   anywhere` to all locked-surface h2s under 640px (prevents mid-word
   overflow on long words like "conversation"). On "Recent thinking on
   running the work." the algorithm splits "running" into "run-/ning"
   which reads worse than a clean word-boundary wrap. None of the words
   in this h2 exceed the available width, so we can safely flip back to
   word-level wrapping for this section. */
@media (max-width: 640px) {
  body.page-id-700 #sw-recent-posts h2,
  body.page-id-700 .elementor-element-1a87a6e h2 {
    overflow-wrap: normal;
    word-break: normal;
    hyphens: manual;
    -webkit-hyphens: manual;
  }
}

/* 2026-05-15 — Case study singular typography parity with blog post.
   body.single-sw_case_study uses a custom .sw-case-* template that
   ships per-section heading sizes far above the locked clamp (h1 72px,
   h2 52px). Blog post (body.single-post) renders at h1 40px / h2 36px
   per the locked typography spec — the user's expectation is that case
   studies match. Override the per-template sizes to mirror blog-single. */
body.single-sw_case_study .sw-case-hero__title,
body.single-sw_case_study h1 {
  font-size: clamp(28px, 4vw, 40px) !important;
  line-height: 1.1 !important;
  font-weight: 600 !important;
  letter-spacing: -0.012em !important;
}
body.single-sw_case_study .sw-case-brief h2,
body.single-sw_case_study .sw-case-challenge h2,
body.single-sw_case_study .sw-case-approach h2,
body.single-sw_case_study .sw-case-outcome h2,
body.single-sw_case_study article h2,
body.single-sw_case_study section h2 {
  font-size: clamp(28px, 3vw, 36px) !important;
  line-height: 1.15 !important;
  font-weight: 600 !important;
}
body.single-sw_case_study h3 {
  font-size: clamp(20px, 2.2vw, 24px) !important;
  line-height: 1.25 !important;
  font-weight: 600 !important;
}
/* The "What changed." outcome stats band uses .sw-chapter-dark on a
   --ink canvas; its h2 should follow the same clamp as the rest. The
   numeric stat values inside also need ink-700-on-dark remapping (they
   render hard-to-read on the dark band without it). */
body.single-sw_case_study .sw-case-outcome.sw-chapter-dark {
  background-color: var(--ink) !important;
  color: var(--bg) !important;
}
body.single-sw_case_study .sw-case-outcome.sw-chapter-dark h2,
body.single-sw_case_study .sw-case-outcome.sw-chapter-dark p,
body.single-sw_case_study .sw-case-outcome.sw-chapter-dark li,
body.single-sw_case_study .sw-case-outcome.sw-chapter-dark strong {
  color: var(--bg) !important;
}
/* Stat cards inside the dark outcome band — restyle from the harsh
   #102338 / white-30% border to a quieter translucent-paper pattern
   matching the sitewide .sw-tile chrome adapted for dark canvas. */
body.single-sw_case_study .sw-case-outcome__card {
  background-color: rgb(245 247 250 / 4%) !important;
  border: 1px solid rgb(245 247 250 / 18%) !important;
  border-radius: 6px !important;
  padding: 24px !important;
}
body.single-sw_case_study .sw-case-outcome__num {
  color: var(--blue-bright) !important;
  font-family: var(--font-mono) !important;
  font-size: 12px !important;
  letter-spacing: 0.06em !important;
  text-transform: uppercase !important;
}
body.single-sw_case_study .sw-case-outcome__label {
  font-family: var(--font-mono) !important;
  font-size: 11px !important;
  text-transform: uppercase !important;
  letter-spacing: 0.06em !important;
  color: rgb(245 247 250 / 70%) !important;
  display: block;
  margin-block: 8px 6px;
}
body.single-sw_case_study .sw-case-outcome__value {
  font-size: clamp(20px, 2.2vw, 24px) !important;
  font-weight: 600 !important;
  line-height: 1.2 !important;
  display: block;
  color: var(--bg) !important;
}
body.single-sw_case_study .sw-case-outcome__qualifier {
  font-size: 13px !important;
  color: rgb(245 247 250 / 65%) !important;
  display: block;
  margin-top: 4px;
}
/* Sources block (composite framing + footnote list) — currently butts
   flush against the bottom of the dark outcome band with a heavy 1px
   ink border on the composite-notice box. Soften: add breathing space,
   demote the notice to a small italic disclosure, render the footnote
   list as muted mono. */
/* Pull the sources section's bg-soft fill out to the section itself so it
   spans full-viewport width like the dark outcome band above and the
   bg-soft CTA below — fixes the "not full width" visual inconsistency.
   See the .sw-case-outcome / .sw-case-sources / .sw-case-cta breakout
   rule below for the negative-margin technique. */
body.single-sw_case_study .sw-case-sources {
  margin-top: 0 !important;
  background-color: var(--ink) !important;
}

/* The article wrapper on case-study singulars has padding-inline =
   clamp(20, 7vw, 120) from the sw-locked-surface rule, which constrains
   every section child to (viewport − 2 × padding) width. The dark
   outcome band + cream sources band + cream CTA all carry bg colors
   that should visually span the full viewport, not sit in a card with
   visible gutters on either side. Standard "breakout" technique: pull
   the section's inline margin outward by the same clamp value so the
   section's box extends to the article's outer edge, then re-add the
   clamp value as inside padding so the section's content stays inside
   the locked horizontal rhythm. Result: full-viewport bg fills with
   correctly indented inner content. */
body.single-sw_case_study .sw-case-outcome,
body.single-sw_case_study .sw-case-sources,
body.single-sw_case_study .sw-case-cta {
  margin-inline: calc(-1 * clamp(20px, 7vw, 120px)) !important;
  padding-inline: clamp(20px, 7vw, 120px) !important;
  padding-block: clamp(56px, 7vw, 80px) !important;
  max-width: none !important;
  margin-top: 0 !important;
  margin-bottom: 0 !important;
}
body.single-sw_case_study .sw-case-sources__composite {
  border: 0 !important;
  background-color: transparent !important;
  border-left: 3px solid var(--blue-bright) !important;
  border-radius: 0 !important;
  padding: 8px 0 8px 20px !important;
  font-size: 14px !important;
  line-height: 1.5 !important;
  color: rgb(245 247 250 / 75%) !important;
}
body.single-sw_case_study .sw-case-sources__composite strong {
  color: var(--bg) !important;
  font-weight: 600;
}
body.single-sw_case_study .sw-case-sources__list,
body.single-sw_case_study .sw-case-sources__list p {
  font-family: var(--font-mono) !important;
  font-size: 12px !important;
  line-height: 1.5 !important;
  letter-spacing: 0.01em !important;
  color: rgb(245 247 250 / 60%) !important;
  margin-top: 16px !important;
}
/* The eyebrow tag list above the title also varies in height (1 line vs
   2 lines when a post has 3+ category tags). Reserve 2 lines so photos
   align across ALL rows of the archive, not just within a single row. */
body.blog article.entry-card > .entry-meta:first-of-type {
  min-height: calc(1.5em * 2);
  display: flex;
  flex-wrap: wrap;
  align-content: flex-start;
}

/* ============================================================================
   2026-05-19 — Sculpted-Adapt PR 1 — Home Trust Stack primitives
   ============================================================================
   See plan: /Users/chetbohley/.claude/plans/for-1-we-have-bubbly-quasar.md
   Mockups: docs/developer/mockups/sculpted-adapt/

   Adds 6 new band primitives + 1 utility for the home page's brand-
   perception lift (sculptedmedia-inspired adaptation):

     .sw-band-logo-wall      — logo marquee under hero
     .sw-band-testimonials   — 3-up testimonial grid (Hodges + Pierce + McAllister)
     .sw-testimonial-quote   — text testimonial card
     .sw-testimonial-video   — video testimonial card (McAllister placeholder)
     .sw-transcript          — <details>/<summary> expander (mirrors .sw-pricing-extras)
     .sw-band-kpi            — 4-up non-hero KPI numbers band (framed-hidden)
     .sw-band-case-teaser    — single case-study teaser tile (framed-hidden)
     .sw-frame-hidden        — utility: display:none, removed when band has data

   Plus Elementor Pro Accordion skin selectors inside .sw-band-faq (D2 lock
   = native accordion widget; skin Elementor's emitted DOM to match the
   editorial restraint of the existing .sw-band-faq aesthetic at lines
   2688-2729).

   Home is body.sw-dark-twin (per body-class.php:106). All primitives use
   semantic tokens which flip via the dark-twin remap, so they should
   render correctly on light canvases too when used in PR 2/3.
   ============================================================================ */

/* ── .sw-band-logo-wall ─────────────────────────────────────────────────────
   Section band wrapping a .sw-reel-marquee logo loop. Eyebrow above.

   Iter-9 (2026-05-19): treats the logo wall as a HERO EXTENSION rather
   than a distinct band. Uses bg-soft (matching the hero above) and
   drops the hairline borders + the wrap's internal padding so the
   marquee reads as one continuous hero zone (hero text + logos =
   single "above the fold" trust complex). The first true band break
   is the product-reel beneath, which restarts the L/D alternation. */
.sw-band-logo-wall {
  background: var(--bg-soft);
}
.sw-band-logo-wall .sw-reel-marquee-wrap {
  border-top: 0;
  border-bottom: 0;
  padding-block: 0;
  /* iter-15g (2026-05-19): the inherited .sw-reel-marquee-wrap mask
     fades 0-8% and 92-100% (~99px each at desktop), which lops off
     half of the leading/trailing logo tile. For a logo wall (vs the
     product-screenshot reel where wide shots take the edge fade well),
     this reads as "logos clipped." Tighten the fade to 3%/97% (~37px
     each) — still softens the edge but doesn't chop a tile in half. */
  -webkit-mask-image: linear-gradient(90deg, transparent 0, #000 3%, #000 97%, transparent 100%);
  mask-image: linear-gradient(90deg, transparent 0, #000 3%, #000 97%, transparent 100%);
}

/* iter-10 (2026-05-19): tighten the hero ↔ logo-wall vertical chrome
   on / (home) specifically. Without this, the locked padding rules at
   line ~733 (clamp 80-128 non-hero) + ~758 (clamp 96-144 hero) sum to
   ~270px between the CTAs and the first logo — defeating the iter-9
   "hero extension" merge. Specificity (0,4,1) via the page-id chain
   matches + outranks the locked-rule base (0,3,1). */
body.sw-locked-surface.page-id-700:not(.single-post) [data-sw-section="hero"] {
  padding-top: 96px !important;
  padding-bottom: 24px !important;
  /* 2026-05-22: re-tuned post-banner-removal so hero + logo wall fill
     exactly 100vh on desktop. Header ~101 + logo wall ~299 = 400 chrome
     below the hero. Previous calc(100vh - 440) left a 37px gap; new
     calc(100vh - 400) closes it. */
  min-height: calc(100vh - 400px) !important;
}
/* 2026-05-22 — desktop + tablet: every other locked-surface marketing
   hero (about-us, services-hub, product pages, audit, newsletter, etc.)
   fills 100vh on the first viewport. Only chrome to subtract is the
   header (~100px) since these pages don't stack a logo wall below the
   hero. Mobile keeps its own 92dvh fit in the @media (max-width: 640)
   block below.

   Also reduces hero pt/pb from the per-element Elementor 144/144 down
   to 80/80 on these heros. The 144 padding was authored when the hero
   sized to natural content height; now that the hero is min-height
   100vh-fit, the heavy padding eats inner space and crowds content-
   heavy pages (e.g. /products/ has 2 stat-rows + CTAs and was filling
   the inner area edge-to-edge, while /services-hub/ has 1 stat-row
   and looked balanced). 80/80 leaves room for centered content to
   breathe regardless of how dense the page is. */
@media (min-width: 641px) {
  body.sw-locked-surface:not(.single-post):not(.page-id-700) [data-sw-section="hero"] {
    min-height: calc(100vh - 100px) !important;
    padding-top: 80px !important;
    padding-bottom: 80px !important;
  }
}
body.sw-locked-surface.page-id-700:not(.single-post) [data-sw-section="logo-wall"] {
  padding-top: 12px !important;
  padding-bottom: 24px !important;
  /* iter-15h (2026-05-19): NO min-height on logo wall — let it size
     to natural content height (~244px) at all viewports. Earlier
     versions had min-height: calc(100vh - X) on BOTH hero and logo
     wall, meaning both grew with viewport → combined zone grew at
     2× the viewport rate. At 1200vh viewport: zone was 1,365px tall
     (165 over fold); at 1440vh: 1,845px (405 over). Logos clipped
     below fold on taller windows. Hero keeps the viewport-grow
     min-height; logo wall stays a constant slim band. Total =
     viewport − 31px across all viewport sizes. */
}
/* iter-15f: tighten logo-wall content margins so the zone fits more
   reliably on shorter viewports. Saves ~36px total across the three
   stacked elements. */
body.page-id-700 .sw-band-logo-wall .sw-logo-wall__eyebrow {
  margin-bottom: 32px !important;
}
body.page-id-700 .sw-band-logo-wall .sw-mini-process {
  margin-top: 64px !important;
}
body.page-id-700 .sw-band-logo-wall .sw-scroll-cue {
  margin-top: 32px !important;
}
/* Elementor's default per-kit .e-con-inner padding for non-hero bands
   is 120px/120px (kit default). Phase D (line ~770) sets it to 10/10
   for hero only. Without this override, the logo wall would carry
   240px of hidden padding chrome, blowing past the 100vh target.

   iter-13 also adds justify-content: center to vertically center the
   eyebrow + marquee + scroll cue as a group inside the band's
   expanded min-height (`calc(100vh - 631px)`). The MCP container
   justify_content setting didn't compile to CSS reliably; targeting
   the .e-con-inner here directly is the dependable path. */
body.sw-locked-surface.page-id-700:not(.single-post) [data-sw-section="logo-wall"] > .e-con-inner {
  padding-top: 10px !important;
  padding-bottom: 10px !important;
  justify-content: center !important;
}
/* iter-15b (2026-05-19) — same justify-content: center treatment on
   the hero's .e-con-inner so H1/lede/CTAs vertically center in the
   538px-tall flex box (hero min-height 610 - 72 padding = 538). The
   outer hero container has justify-content: center via MCP settings,
   but its only child is the .e-con-inner which takes 100% height
   (flex-grow: 1) — so the centering needs to happen on the inner
   level where the actual widgets are flex children. Without this,
   content stays glued to top of the inner = H1 sits high. */
body.sw-locked-surface.page-id-700:not(.single-post) [data-sw-section="hero"] > .e-con-inner {
  justify-content: center !important;
}
@media (max-width: 640px) {
  /* iter-15r (2026-05-19): operator wants hero + logos + scroll cue
     to fit in one viewport on ALL mobile phones, including 375×667
     (iPhone SE). That requires aggressive compaction of hero chrome
     + typography below the desktop rules. */

  /* Hero band — iter-15u (2026-05-19): operator wants hero VERTICALLY
     CENTERED with equal padding above hero text and below logos.
     Strategy: give hero min-height that fills ~half the available
     viewport (after chrome + logo wall). justify-content: center on
     .e-con-inner (from earlier rule) centers content. The split:
       chrome (banner + header) ~ 142
       hero      = calc(100dvh - 142 - 220) ≈ 305 at 667vh
       logo wall = 220
       total     ≈ 667 ✓ */
  /* 2026-05-21 — all locked-surface marketing heros share the same mobile
     treatment: tight padding-top (44), centered content biased upward,
     chunky h1 clamp(24,7vw,30), compact lede (14/1.4), tight 12px row-gap,
     and small CTA buttons (14px / 12x22).
     2026-05-27 — operator wants the first fold to fill the FULL viewport
     and the scroll arrow to sit just above the iOS URL bar. The key is the
     unit: 100dvh (DYNAMIC viewport) equals exactly the currently-visible
     area, so the fold fills precisely to the top of the URL bar and the
     cue near the band bottom lands just above it. (100vh on iOS resolves
     to the LARGE viewport — taller than what's visible — which pushed the
     fold's bottom under the bar; that's reverted here.) padding-bottom (64)
     biases the centered content upward and reserves a clear band for the
     cue. Home subtracts more because its logo-wall stacks immediately
     below the hero; other pages only subtract the in-flow header. */
  body.sw-locked-surface:not(.single-post) [data-sw-section="hero"] {
    padding-top: 44px !important;
    padding-bottom: 64px !important;
    /* 76 = just the in-flow header (~73px). The old 140 dated to a removed
       promo banner and left sparse heros short of the fold. */
    min-height: calc(100dvh - 76px) !important;
  }
  /* Home-only: subtract the logo-wall band (188px) + the in-flow header so
     hero + logo wall fills exactly one dynamic viewport on mobile, with the
     scroll cue just above the URL bar. Reduced top padding biases the
     centered hero content upward (operator: "scrolling area up a bit"). */
  body.sw-locked-surface.page-id-700:not(.single-post) [data-sw-section="hero"] {
    min-height: calc(100dvh - 261px) !important;
    padding-top: 28px !important;
  }
  /* Hero inner: vertically center content (the band padding-bottom biases
     it upward off the URL bar). */
  body.sw-locked-surface:not(.single-post) [data-sw-section="hero"] > .e-con-inner {
    justify-content: center !important;
  }
  /* Hero H1: chunky 24-30 with -0.015em tracking and 1.1 line-height.
     Targets the sitewide locked-clamp h1 directly (no `.sw-hero-h1`
     opt-in required) so every locked hero h1 matches. */
  body.sw-locked-surface:not(.single-post) [data-sw-section="hero"] h1.elementor-heading-title,
  body.sw-locked-surface:not(.single-post) [data-sw-section="hero"] .elementor-widget-heading h1.elementor-heading-title {
    font-size: clamp(24px, 7vw, 30px) !important;
    line-height: 1.1 !important;
    letter-spacing: -0.015em !important;
  }
  body.sw-locked-surface:not(.single-post) [data-sw-section="hero"] .sw-hero-h1 .line + .line {
    margin-top: 0.05em !important;
  }
  /* Hero lede: drop from 18 → 14 with tighter line-height. */
  body.sw-locked-surface:not(.single-post) [data-sw-section="hero"] .elementor-widget-text-editor p {
    font-size: 14px !important;
    line-height: 1.4 !important;
  }
  /* Tighten flex row-gap between H1 / lede / CTA row. Desktop is 32px;
     mobile drops to 12 to save 40+ of vertical chrome. */
  body.sw-locked-surface:not(.single-post) [data-sw-section="hero"] > .e-con-inner {
    row-gap: 12px !important;
    gap: 12px !important;
  }
  body.sw-locked-surface:not(.single-post) [data-sw-section="hero"] > .e-con-inner > .e-con {
    row-gap: 10px !important;
  }
  /* Smaller CTA buttons on mobile so they take less vertical space.
     Home's end-CTA mirrors the same compaction. */
  body.sw-locked-surface:not(.single-post) [data-sw-section="hero"] .elementor-button,
  body.page-id-700 .elementor-element.elementor-element-ee89f39 .elementor-button {
    padding: 12px 22px !important;
    font-size: 14px !important;
  }

  /* Logo wall: tightest possible padding so the 4-logo strip lands as
     a slim band beneath the hero. */
  body.sw-locked-surface.page-id-700:not(.single-post) [data-sw-section="logo-wall"] {
    padding-top: 8px !important;
    padding-bottom: 12px !important;
  }
  /* 2026-05-27 — operator wants the FOUNDATION SESSION → PLAN + BUILD →
     CONVERT mini-process line hidden on mobile. The methodology band below
     the fold carries the same content, and dropping it frees vertical room
     in the now-full-100vh first fold. (Was re-enabled on mobile 2026-05-21;
     that's reverted here by operator request.) */
  body.page-id-700 .sw-band-logo-wall .sw-mini-process {
    display: none !important;
  }
  /* Logo-wall eyebrow margin tighter so the 4-logo strip stays slim. */
  body.page-id-700 .sw-band-logo-wall .sw-logo-wall__eyebrow {
    margin-bottom: 20px !important;
    font-size: 10px !important;
  }

  /* iter-15s (2026-05-19): operator wants scroll cue at the bottom of
     viewport on mobile, with hero+logo zone = 100vh.

     Strategy: logo wall band fills remaining viewport via min-height:
     calc(100dvh - X) where X ≈ chrome (~140) + hero (~300). Then anchor
     the scroll cue absolutely to the band's bottom — the cue is nested
     2 levels deep (HTML widget > anchor), so flex margin-auto doesn't
     reach it. Position absolute is more reliable.

     dvh handles iOS Safari URL bar shuffles better than vh. */
  body.sw-locked-surface.page-id-700:not(.single-post) [data-sw-section="logo-wall"] {
    min-height: 188px !important;
    position: relative !important;
  }
  /* Center eyebrow + marquee vertically in the band, with the cue
     anchored absolute near the bottom. The padding-bottom reserves space
     for the cue so the marquee doesn't ride onto it. */
  body.sw-locked-surface.page-id-700:not(.single-post) [data-sw-section="logo-wall"] > .e-con-inner {
    justify-content: flex-start !important;
    padding-top: 8px !important;
    padding-bottom: 44px !important;
  }
  /* Neutralize the Elementor HTML widget's default position:relative so
     the cue's position:absolute escapes to the band (which has
     position:relative). Without this the cue anchors to the widget,
     not the band. */
  body.sw-locked-surface.page-id-700:not(.single-post) [data-sw-section="logo-wall"] .elementor-widget-html {
    position: static !important;
  }
  /* Anchor cue just above the bottom of the band, centered horizontally.
     2026-05-27 — with the fold sized in 100dvh the band bottom already sits
     just above the iOS URL bar, so the cue only needs a small offset (16) to
     bounce just above the URL field. env() adds the home-indicator inset on
     notched devices in standalone mode. */
  body.sw-locked-surface.page-id-700:not(.single-post) [data-sw-section="logo-wall"] .sw-scroll-cue {
    position: absolute !important;
    bottom: calc(env(safe-area-inset-bottom, 0px) + 16px) !important;
    left: 50% !important;
    transform: translateX(-50%) !important;
    margin: 0 !important;
  }
}

/* ── iter-15s (2026-05-19) — GLOBAL mobile header polish ───────────────
   Operator: "Book a Foundation Session" button wraps to 3 ugly lines on
   mobile. Swap text to just "Book" on mobile only via CSS content trick
   (works without re-authoring the Elementor button widget; preserves
   desktop/tablet text). Also tighten header layout so logo + book +
   cart icons sit in a clean row.

   Sitewide — NOT scoped to page-id-700. */
@media (max-width: 640px) {
  /* Hide original button text, render "Book" via ::after on the
     .elementor-button-text span. The element-id targets the SyteWide
     header CTA specifically (1674add). */
  .elementor-element-1674add .elementor-button .elementor-button-text {
    font-size: 0 !important;
    letter-spacing: 0 !important;
    line-height: 1 !important;
  }
  .elementor-element-1674add .elementor-button .elementor-button-text::after {
    content: "Book";
    font-size: 14px;
    letter-spacing: -0.005em;
    line-height: 1.3;
  }
  /* Tighter button padding on mobile so the compact "Book" fits cleanly. */
  .elementor-element-1674add .elementor-button {
    padding: 10px 18px !important;
    min-width: 0 !important;
  }
  /* iter-15t/u (2026-05-19): operator asks — hamburger to far right,
     cart icon hidden on mobile, BIGGER logo, Book button CENTERED.
     The header parent is a CSS grid (3 columns from Elementor kit
     defaults). Re-template: logo left / book centered / hamburger right. */
  /* iter-15w (2026-05-20): force header to flex via ID selector. The
     header parent has id="sw-header-light" (added in earlier work for
     desktop targeting). Using #sw-header-light gives specificity (1,0,0),
     beats every other rule including the Elementor framework defaults
     that were pinning the layout to grid + 1fr/auto/1fr columns. */
  #sw-header-light {
    --display: flex !important;
    display: flex !important;
    flex-direction: row !important;
    flex-wrap: nowrap !important;
    align-items: center !important;
    gap: 0 !important;
    padding-left: 16px !important;
    padding-right: 16px !important;
  }
  /* Brand lockup on the left — flex order 0 (natural DOM position). Brand
     system 2026-05-26: the old image-widget logo (b2030b1) was replaced with
     an inlined brand-lockup SVG widget (_css_classes sw-nav-logo). Cap the
     lockup SVG height for a compact mobile footprint. */
  #sw-header-light > .sw-nav-logo {
    order: 0 !important;
  }
  #sw-header-light .sw-brand-lockup svg {
    height: 30px !important;
  }
  /* Book button — flex order 2 (between logo and hamburger visually),
     margin-left:auto pushes the Book+hamburger pair right, 12px gap
     to hamburger. ID-prefixed for specificity. */
  #sw-header-light > .elementor-element-1674add {
    order: 2 !important;
    margin-left: auto !important;
    margin-right: 12px !important;
    grid-column: auto !important;
  }
  /* Hamburger — flex order 3, sits at the right edge. */
  #sw-header-light > .elementor-element-9b4f6c4 {
    order: 3 !important;
    margin-left: 0 !important;
    grid-column: auto !important;
  }
  /* Mobile (copy-rewrite 2026-06-02): keep the phone as a tap-to-call icon
     (service-business buyers convert on the phone), hide the cart + the number
     text. The utils widget (5942155) sits between the Book button and the
     hamburger. */
  #sw-header-light > .elementor-element-5942155 {
    order: 2 !important;
    margin-left: 8px !important;
    margin-right: 4px !important;
    grid-column: auto !important;
  }
  .elementor-element-5942155 .sw-header-cart {
    display: none !important;
  }
  .elementor-element-5942155 .sw-header-phone__text {
    display: none !important;
  }
  .elementor-element-5942155 .sw-header-phone {
    padding: 0 6px !important;
  }
}

/* ── iter-15w (2026-05-20) — sitewide mobile horizontal padding bump ──
   Operator: "on the mobile body, add a little more padding / left / right."
   Locked sitewide horizontal floor was clamp(20px, 7vw, 120px) → 26.25 at
   375vw. Bumping the mobile floor to 32px so content sits a little further
   from the edge on phones. Specificity needs `body.elementor-page` to
   beat Elementor's per-element padding-inline-start chain (M-024 cascade
   gotcha — see memory:project-locked-rule-cascade-issue). */
@media (max-width: 640px) {
  :root {
    --section-x: 32px;
  }
}

/* iter-12 (2026-05-19): .sw-scroll-cue — sculpted-style animated
   "scroll for more" link. Sits at the bottom of the logo-wall band
   on / (home); anchors to the product-reel section's element-id
   (#walkthrough). Uses JetBrains Mono + a subtle bounce on the arrow.
   Honors prefers-reduced-motion. */
html {
  scroll-behavior: smooth;
}
.sw-scroll-cue {
  display: block;
  width: fit-content;
  margin: 28px auto 0;
  text-decoration: none;
  opacity: 0.9;
  transition: opacity 0.2s ease, transform 0.2s ease;
}
.sw-scroll-cue:hover,
.sw-scroll-cue:focus-visible {
  opacity: 1;
  transform: translateY(2px);
  outline: 0;
}
/* Filled downward triangle drawn via CSS borders (sani-mani.in reference,
   2026-05-21). No bitmap, scales crisp at any DPR. Width:height ratio
   ~28:22 reads as a stout chevron, not a thin spike. */
.sw-scroll-cue__arrow {
  display: block;
  width: 0;
  height: 0;
  border-left: 14px solid transparent;
  border-right: 14px solid transparent;
  border-top: 18px solid var(--blue-bright);
  animation: sw-scroll-cue-bounce 2.2s ease-in-out infinite;
}
@keyframes sw-scroll-cue-bounce {
  0%, 100% { transform: translateY(0); }
  50%     { transform: translateY(6px); }
}
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  .sw-scroll-cue__arrow { animation: none; }
}

/* 2026-05-22 — sitewide scroll-cue triangle at the bottom of every
   locked-surface hero. Home has its own clickable cue authored inside
   the logo-wall band — exclude it here to avoid a double triangle.
   Drawn purely with CSS borders as a ::after pseudo-element so no
   per-page Elementor edit is needed. Decorative only (pointer-events
   off); operator-validated branding cyan reuses --blue-bright. */
body.sw-locked-surface:not(.single-post):not(.page-id-700) [data-sw-section="hero"]::after {
  content: "";
  position: absolute;
  bottom: 40px;
  /* 2026-05-27 — center via `left: calc(50% - 14px)` (14 = half the 28px
     triangle), NOT `left:50% + translateX(-50%)`. The sw-scroll-cue-bounce
     animation sets `transform: translateY(...)` on this same element, which
     overrode the translateX(-50%) and shifted the arrow ~14px right of center.
     A transform-free centering survives the animation. (Home's cue is immune —
     its centering + bounce live on different elements.) Also raised 28 → 40 so
     it sits a little higher above the URL bar. */
  left: calc(50% - 14px);
  width: 0;
  height: 0;
  border-left: 14px solid transparent;
  border-right: 14px solid transparent;
  border-top: 18px solid var(--blue-bright);
  animation: sw-scroll-cue-bounce 2.2s ease-in-out infinite;
  opacity: 0.9;
  pointer-events: none;
  z-index: 2;
}
@media (prefers-reduced-motion: reduce) {
  body.sw-locked-surface:not(.single-post):not(.page-id-700) [data-sw-section="hero"]::after {
    animation: none;
  }
}
@media (max-width: 640px) {
  /* 2026-05-25: declutter the v4 product hero on mobile — hide the prose lede
     + the closing positioning line (the only two text-editor widgets in the
     hero; eyebrow/H1/stat-row/CTAs are heading/HTML). The stat-row chips
     already carry the lede's key points, and dropping the closing line keeps
     the scroll cue from colliding with hero text. The cue stays visible. */
  body.sw-locked-surface:not(.single-post):not(.page-id-700) [data-sw-section="hero"] .elementor-widget-text-editor {
    display: none;
  }
  /* Reserve a small clear band at the bottom so the scroll cue (::after
     triangle, bottom:28px) doesn't ride onto the last widget. 24 is enough
     on top of the hero band's own 64px padding-bottom; the earlier 56 over-
     reserved and, combined with a long lede (e.g. the /products/ archive
     hero), pushed the band past one dvh so the arrow landed at the URL-bar
     edge. The extra :not(.page-id-700) lifts this above the (0,4,1) canonical
     hero-inner rule so it wins. */
  body.sw-locked-surface:not(.single-post):not(.page-id-700) [data-sw-section="hero"] > .e-con-inner {
    padding-bottom: 24px !important;
  }

  /* 2026-05-27 — product DETAIL heros (the 5 sw-product-* pages) are the only
     content-dense heros: mark + eyebrow + H1 + 3 stat-rows + 3 CTAs run ~700px,
     overflowing one phone viewport so the scroll arrow falls below the fold
     (unlike home + the sparse heros, which already fit). Compact them so the
     whole hero fits one 100dvh with the ::after arrow just above the URL bar:
       • shrink the oversized hero mark 200 → 96 (the img source stays 200×200
         for CDN crispness; only the display box shrinks — even crisper). The
         96px box matches the .sw-hero-logo .mark + .sw-card-mark baseline.
       • tighten the band + inner padding + row-gap.
     Selectors carry .sw-locked-surface + :not chain so they out-specify the
     generic (0,4,1)/(0,5,1) hero + inner rules above. */
  body.sw-locked-surface[class*="sw-product-"]:not(.single-post):not(.page-id-700) .sw-hero-logo img {
    max-width: 80px !important;
    max-height: 80px !important;
  }
  body.sw-locked-surface[class*="sw-product-"]:not(.single-post):not(.page-id-700) [data-sw-section="hero"] {
    padding-top: 20px !important;
    padding-bottom: 36px !important;
  }
  body.sw-locked-surface[class*="sw-product-"]:not(.single-post):not(.page-id-700) [data-sw-section="hero"] > .e-con-inner {
    padding-bottom: 16px !important;
    row-gap: 8px !important;
    gap: 8px !important;
  }
  /* 2026-05-27 — operator: cap the stat lines under the hero title to 2 on
     mobile so the dense product heros stay compact and the CTAs/arrow don't
     sit too low. The .sw-hero-stat-row stacks one <span> per line (3 on
     monthly, 4 on LTD); hide the 3rd onward. The kept two carry price + scope
     (the most decision-relevant); the rest repeat on the pricing band below. */
  body.sw-locked-surface[class*="sw-product-"]:not(.single-post):not(.page-id-700) [data-sw-section="hero"] .sw-hero-stat-row > span:nth-child(n+3) {
    display: none !important;
  }
}

/* iter-14 (2026-05-19): .sw-mini-process — 3-node engagement flow
   indicator sitting between the logo marquee and the scroll cue.
   Foundation Session → Plan + Build → Run. Mono uppercase labels,
   cyan arrows, no chrome — reads as a horizontal text line that
   previews the methodology band below the fold. */
.sw-mini-process {
  display: flex;
  align-items: center;
  justify-content: center;
  flex-wrap: wrap;
  gap: 18px;
  margin: 28px auto 0;
  /* Geist (2026-05-27): the earlier literal-Inter keep is retired — Geist Variable
     DOES contain U+2192 "→" and renders it itself (verified live: → width 32px in
     Geist vs 24px monospace / 40px serif), so var(--font-sans) is actually MORE
     stable than the old "Inter, system-ui" stack, whose → fell through to a
     platform-dependent system glyph. Labels + arrow now both render Geist. */
  font-family: var(--font-sans);
  font-size: 17px;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: rgb(245 247 250 / 60%);
  line-height: 1.1;
}
.sw-mini-process__step {
  white-space: nowrap;
}
.sw-mini-process__step:nth-of-type(2) {
  color: var(--ink);
}
.sw-mini-process__arrow {
  color: var(--blue-bright);
  font-size: 22px;
  font-weight: 500;
  opacity: 0.85;
}
@media (max-width: 540px) {
  .sw-mini-process {
    gap: 6px;
    font-size: 11px;
    letter-spacing: 0.02em;
  }
  .sw-mini-process__arrow {
    font-size: 13px;
  }
  /* Override the desktop margin-top: 96 from the page-id-700 rule
     above — 96px is too much vertical chrome on phones. */
  body.page-id-700 .sw-band-logo-wall .sw-mini-process {
    margin-top: 28px !important;
  }
}
/* iter-15z (2026-05-20): unified with .sw-chapter-ebrow pattern — same
   spec the pre-existing pillars-intro / recent-posts eyebrows use.
   !important wins against the sitewide Inter body rule. */
.sw-band-logo-wall .sw-logo-wall__eyebrow {
  font-family: var(--font-mono) !important;
  font-size: 11px !important;
  font-weight: 500 !important;
  letter-spacing: 0.18em !important;
  text-transform: uppercase !important;
  color: var(--ink) !important;
  text-align: center;
  margin: 0 0 28px !important;
}
/* Logos render as div tiles with background-image (not <img>) to bypass
   WordPress + LiteSpeed image processing — those inject explicit width/
   height attrs + Photon URLs that override CSS sizing. Background-image
   approach gives full control, no naturalWidth scaling, no responsive-
   image attribute interference. */
.sw-band-logo-wall .sw-reel-marquee {
  gap: 64px;
  animation: sw-logo-wall-scroll 30s linear infinite;
  align-items: center;
  /* iter-15x (2026-05-20): one-set-shift via CSS variable so the
     keyframe is viewport-aware. Desktop default: 4 unique tiles × (200
     tile + 64 gap) = 1056. Mobile override below sets it to 720 to
     match the smaller tile + gap. The variable resolves at animation
     start; if the viewport crosses 640 mid-cycle the running animation
     keeps its current value until the next iteration. */
  --sw-logo-set-shift: -1056px;
  /* iter-15y (2026-05-20): GPU compositing hints. The math is exact
     (setWidth measured at 719.9999 ≈ 720px shift) but mobile browsers
     can render a 1-frame paint hitch at the loop reset boundary —
     visible as a "blip." `will-change: transform` promotes the
     marquee to its own GPU layer; translate3d in the keyframes
     (instead of translateX) keeps it composited rather than repainted.
     `backface-visibility: hidden` is a legacy companion that also
     forces 3D context in some engines. */
  will-change: transform;
  backface-visibility: hidden;
}
/* iter-15n/o/x/y: seamless marquee. 12 tiles (4 unique × 3 sets). Shift
   by exactly one set's width so tile N+4 lands where tile N started,
   making the loop indistinguishable from continuous scroll. translate3d
   keeps the marquee GPU-composited so the loop reset is paint-free. */
@keyframes sw-logo-wall-scroll {
  from { transform: translate3d(0, 0, 0); }
  to   { transform: translate3d(var(--sw-logo-set-shift, -1056px), 0, 0); }
}
.sw-band-logo-wall .sw-reel-marquee .sw-logo-tile {
  width: 200px;
  height: 56px;
  flex-shrink: 0;
  background-repeat: no-repeat;
  background-position: center;
  background-size: contain;
  filter: brightness(0) invert(1);
  opacity: 0.7;
  transition: opacity 0.2s ease;
}
.sw-band-logo-wall .sw-reel-marquee .sw-logo-tile:hover { opacity: 1; }
/* Per-logo background-image (inline style="background-image:..." is stripped
   by WordPress KSES on HTML widget save — modifier classes restore control). */
.sw-logo-tile--laraway    { background-image: url("https://sytewide.com/wp-content/uploads/2026/01/Laraway.webp"); }
.sw-logo-tile--alphamaui  { background-image: url("https://sytewide.com/wp-content/uploads/2026/01/Alpha-Maui.webp"); }
.sw-logo-tile--colorhouse { background-image: url("https://sytewide.com/wp-content/uploads/2026/01/Color-House.webp"); }
.sw-logo-tile--hotelloans { background-image: url("https://sytewide.com/wp-content/uploads/2026/05/HotelLoans-Logo.png"); }
@media (max-width: 640px) {
  .sw-band-logo-wall .sw-reel-marquee {
    gap: 40px;
    animation-duration: 24s;
    /* iter-15x (2026-05-20): mobile one-set-shift = 4 × (140 tile + 40
       gap) = 720. Without this override the keyframe used the desktop
       1056 value, overshooting by 336px and causing the visible snap. */
    --sw-logo-set-shift: -720px;
  }
  .sw-band-logo-wall .sw-reel-marquee .sw-logo-tile { width: 140px; height: 40px; }
}

/* ── .sw-band-testimonials ──────────────────────────────────────────────────
   3-up testimonial grid. Mixed cards: 2 text quotes + 1 video.
   bg-soft (iter-8 fix 2026-05-19) lifts this band out of the
   methodology→testimonials→FAQ triple-dark stretch that was making
   the middle of the page feel monotonous. */
.sw-band-testimonials {
  background: var(--bg-soft);
}
.sw-band-testimonials__head {
  max-width: var(--width-wide);
  margin: 0 auto 48px;
  text-align: center;
}
/* iter-15z (2026-05-20): unified with .sw-chapter-ebrow / sitewide h2
   spec so the testimonials band reads at the same scale as pillars-intro
   and recent-posts. Previously 12px Inter cyan / 44px h2 — too large
   and off-palette next to the rest of the page. */
.sw-band-testimonials__eyebrow {
  font-family: var(--font-mono) !important;
  font-size: 11px !important;
  font-weight: 500 !important;
  letter-spacing: 0.18em !important;
  text-transform: uppercase !important;
  color: var(--ink) !important;
  margin: 0 0 16px !important;
}
.sw-band-testimonials__h2 {
  margin-inline: auto !important;
  font-size: clamp(28px, 3.5vw, 36px) !important;
  font-weight: 600 !important;
  line-height: 1.15 !important;
  letter-spacing: -0.012em !important;
  color: var(--ink);
  margin: 0;
  max-width: 28ch;
  text-wrap: balance;
}
.sw-band-testimonials__grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 24px;
  /* Updated 2026-05-27: 1240 → var(--width-canvas) (= 1290). +50px desktop
     gain. Previously was 50px narrower than the .sw-dark-twin .e-con-inner
     sitewide parent cap. See widths.md (canvas tier). */
  max-width: var(--width-canvas);
  margin: 0 auto;
  /* iter-18b (2026-05-20): explicit stretch so shorter text cards
     grow to match the video card's natural height. Combined with
     `margin-top: auto` on each card's logo, this pins all three
     brand logos at the same baseline. */
  align-items: stretch;
}
@media (max-width: 1023px) {
  .sw-band-testimonials__grid {
    grid-template-columns: 1fr;
    /* iter-21 (2026-05-20) tablet audit fix: tightened max-width
       640 → 520. At 640 width on tablet, the 16:9 video frame inside
       David's card grew to ~328px tall while Lee/Sara text cards
       stayed ~135px content height — a ~2x card-height imbalance
       when stacked. 520 max gives a frame height of ~260px and pulls
       the cards into closer visual rhythm without losing readability
       on the text quotes. */
    max-width: 520px;
  }
}

/* .sw-testimonial-quote — text testimonial card.
   iter-17 (2026-05-20): restructured. Person header (photo + name +
   company in a row at top), then blockquote, then a centered brand
   logo at the bottom. Cleaner visual hierarchy than the prior
   photo-upper-left + giant-quote-mark + bottom-cite layout.
   iter-18b (2026-05-20): position: relative + padding-bottom reserved
   so the brand logo can be absolutely positioned at a fixed offset
   from the card bottom (see .sw-testimonial-logo). margin-top: auto
   gave each logo a different push depending on each blockquote's
   natural length — absolute positioning pins all three to the same
   baseline regardless of content. */
.sw-testimonial-quote,
.sw-testimonial-video {
  position: relative;
  padding-bottom: 92px;
}
.sw-testimonial-quote {
  display: flex;
  flex-direction: column;
  gap: 16px;
  padding: 28px 28px 92px;
  background: var(--bg);
  border: 1px solid var(--rule);
  border-radius: 6px;
  /* iter-18e (2026-05-20): overflow: hidden to clip the giant decorative
     close-quote glyph (::after below) that bleeds off the bottom-right
     corner — the editorial flourish that fills the dead space in text
     cards without making the text re-flow or float. */
  overflow: hidden;
  transition: border-color 0.16s ease, transform 0.16s ease;
}
.sw-testimonial-quote:hover {
  border-color: var(--rule-strong);
  transform: translateY(-2px);
}
/* iter-18e (2026-05-20): giant Georgia close-quote glyph bleeds off the
   bottom-right corner of each text card. Sits behind the content (z:1)
   so the blockquote (z:2 via head/logo siblings) reads first. The dead
   space below the natural-length quote — previously fought with
   justify-content/margin tricks — becomes the design.
   Why Georgia not Fraunces: site self-hosts Inter + JetBrains Mono only;
   bringing Fraunces back via Google CDN was reverted at M5. Georgia is
   the next item in the --font-display fallback chain and reads as
   editorial serif at this scale. If Fraunces ever returns sitewide,
   --font-display will pick it up automatically. */
.sw-testimonial-quote::after,
.sw-testimonial-video::after {
  content: "\201D";
  position: absolute;
  bottom: -36px;
  right: -28px;
  font-family: var(--font-display, Georgia, serif);
  font-weight: 700;
  font-size: 360px;
  line-height: 1;
  color: var(--ink);
  opacity: 0.05;
  pointer-events: none;
  z-index: 1;
}
/* iter-18 (2026-05-20): video card now uses the SAME head pattern as the
   text cards — photo + name + company in a row. Selectors below cover
   both `.sw-testimonial-quote__*` and `.sw-testimonial-video__*` so the
   two card types share identical typography + spacing. */
.sw-testimonial-quote__head,
.sw-testimonial-video__head {
  display: flex;
  align-items: center;
  gap: 14px;
  /* iter-18e: lift above the decorative close-quote glyph on text cards. */
  position: relative;
  z-index: 2;
}
.sw-testimonial-quote__photo,
.sw-testimonial-video__photo {
  width: 56px !important;
  height: 56px !important;
  /* iter-17d (2026-05-20): !important on border-radius + box-shadow
     because some image-reset rule in a CORS-blocked Blocksy/Elementor
     sheet was zeroing both. Width/height took, so the override is
     specific to those two properties. */
  border-radius: 999px !important;
  object-fit: cover;
  flex-shrink: 0;
  box-shadow:
    0 0 0 2px rgb(245 247 250 / 10%),
    0 6px 16px rgb(11 27 43 / 45%),
    0 2px 6px rgb(11 27 43 / 28%) !important;
}
.sw-testimonial-quote__person,
.sw-testimonial-video__person {
  display: flex;
  flex-direction: column;
  gap: 4px;
  min-width: 0;
}
.sw-testimonial-quote__person .name,
.sw-testimonial-video__person .name,
.sw-testimonial-video__cite .name {
  display: block;
  font-family: var(--font-sans);
  font-size: 15px;
  font-weight: 600;
  color: var(--ink);
  letter-spacing: -0.005em;
  line-height: 1.2;
}
.sw-testimonial-quote__person .company,
.sw-testimonial-video__person .company,
.sw-testimonial-video__cite .company {
  display: block;
  margin-top: 4px;
  font-family: var(--font-mono);
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--mute);
  line-height: 1.3;
}
/* In the new __person flex column, spans are already block-flow via
   flex; the explicit display: block above only matters for the legacy
   .sw-testimonial-video__cite container (a plain div), so the cite
   name + company stack on separate lines instead of running inline. */
.sw-testimonial-quote blockquote {
  margin: 0;
  padding: 0;
  border: 0;
  font-family: var(--font-sans);
  font-size: 17px;
  line-height: 1.6;
  color: var(--ink);
  font-weight: 400;
  letter-spacing: -0.003em;
  /* iter-18e (2026-05-20): text top-pins under the head (parent gap:16);
     the dead space below is consumed by the decorative close-quote
     glyph on .sw-testimonial-quote::after. Sits above the glyph via
     stacking context — local position+z-index pair. */
  position: relative;
  z-index: 2;
}
/* Cite class kept for any legacy testimonial markup; not used by the
   refactored cards above. */
.sw-testimonial-quote cite {
  font-style: normal;
}

/* .sw-testimonial-video — video card (McAllister live Gumlet embed
   wired 2026-05-20). The inner video well uses the canonical
   .sw-video-hero pattern (declarations at lines 137-200) so the
   shared video-hero.js loader handles click-to-play, reduced-motion
   fallback, and autoplay handoff. */
.sw-testimonial-video {
  display: flex;
  flex-direction: column;
  gap: 16px;
  padding: 28px 28px 92px;
  background: var(--bg);
  border: 1px solid var(--rule);
  border-radius: 6px;
  /* iter-18f (2026-05-20): overflow: hidden so the decorative close-quote
     glyph on ::after (selector grouped with .sw-testimonial-quote::after)
     bleeds off the lower-right corner cleanly. */
  overflow: hidden;
  transition: border-color 0.16s ease, transform 0.16s ease;
}
.sw-testimonial-video:hover {
  border-color: var(--rule-strong);
  transform: translateY(-2px);
}
/* Inline Gumlet iframe (no overlay) on the McAllister testimonial card.
   Operator chose this UX over the .sw-video-hero click-to-load pattern so
   the visitor only clicks once — directly on Gumlet's in-iframe play
   button. That keeps the click inside the iframe's same-origin context,
   which sidesteps Chrome's cross-origin autoplay-with-sound block and
   the "Enable sound" overlay that comes with it. Cost is the iframe's
   JS weight loading lazily when the band scrolls into view (loading=
   "lazy" on the iframe defers it). The wrapper handles 16:9 aspect
   ratio, edge-bleed inside the card (negative margins + expanded width,
   per the earlier .sw-video-hero edge-bleed analysis), and z-index lift
   above the decorative close-quote glyph (.sw-testimonial-video::after
   z:1, pointer-events:none). */
/* 2026-05-21 — restore edge-to-edge bleed for the inline Gumlet iframe.
   The pattern: width: calc(100% + 2*padding) + negative side margins.
   Negative margins alone don't widen a block (they only let following
   content tuck in), so the explicit width expansion is required. Card
   has 28px L/R padding; player widens by 56px and pulls -28px each side
   to align with the card's outer 1px border. Lost between refactors
   when the markup switched from `.sw-video-hero` to `__player` and the
   prior bleed rule's selector stopped matching. */
.sw-testimonial-video__player {
  position: relative;
  z-index: 2;
  aspect-ratio: 16 / 9;
  width: calc(100% + 56px);
  margin-left: -28px;
  margin-right: -28px;
  background: var(--ink-fixed);
  overflow: hidden;
}
.sw-testimonial-video__player iframe {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  border: 0;
  display: block;
}
.sw-testimonial-video__body {
  padding: 20px 28px 28px;
  display: flex;
  flex-direction: column;
  gap: 12px;
  flex: 1;
}
.sw-testimonial-video__cite {
  font-style: normal;
  padding-top: 16px;
  margin-top: auto;
  border-top: 1px solid var(--rule);
}

/* ── .sw-transcript ─────────────────────────────────────────────────────────
   <details>/<summary> expander for video transcript. Mirrors
   .sw-pricing-extras (lines 5908-5959) at smaller scale. */
.sw-transcript {
  margin-top: 4px;
  padding: 0;
  background: transparent;
  /* iter-18d: center the summary link under the video frame. The summary
     is inline-flex so it sizes to content; text-align on the <details>
     centers it horizontally.
     iter-18f: lift above the decorative close-quote glyph. */
  text-align: center;
  position: relative;
  z-index: 2;
}
.sw-transcript__summary {
  list-style: none;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  /* FLATTEN-FLOOR: literal kept — .sw-transcript__chevron renders a symbol glyph
     (absent from JetBrains Mono); var(--font-mono)'s fallback shifts it.
     Verified by css-regression harness. */
  font-family: "JetBrains Mono", ui-monospace, monospace;
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--blue-bright);
  padding: 4px 0;
}
.sw-transcript__summary::-webkit-details-marker { display: none; }
.sw-transcript[open] .sw-transcript__chevron { transform: rotate(180deg); }
.sw-transcript__chevron {
  display: inline-block;
  transition: transform 160ms ease;
  font-size: 10px;
}
.sw-transcript__body {
  margin-top: 12px;
  padding: 16px 0 4px;
  border-top: 1px solid var(--rule);
  font-family: var(--font-sans);
  font-size: 14px;
  line-height: 1.65;
  color: rgb(245 247 250 / 78%);
  /* parent .sw-transcript is text-align: center for the summary; reset
     the open transcript body back to left-aligned reading flow. */
  text-align: left;
}
.sw-transcript__body p { margin: 0 0 12px; }
.sw-transcript__body p:last-child { margin-bottom: 0; }

/* .sw-testimonial-logo — brand logo at the bottom of each testimonial
   card (iter-17, 2026-05-20). Reuses the .sw-logo-tile--<slug> modifier
   classes from the logo wall to set background-image; this rule sizes
   + filters it for in-card context.
   iter-18b (2026-05-20): absolutely positioned at a fixed offset from
   each card's bottom so all three logos sit at the same baseline,
   independent of how long the blockquote happens to be. Parent cards
   reserve 92px padding-bottom for the logo's safe zone. */
.sw-band-testimonials .sw-testimonial-logo {
  position: absolute;
  bottom: 28px;
  left: 50%;
  transform: translateX(-50%);
  width: 140px;
  height: 48px;
  margin: 0;
  background-repeat: no-repeat;
  background-position: center;
  background-size: contain;
  filter: brightness(0) invert(1);
  /* iter-18g: bumped 0.55 → 0.75 so the brand logos read as proof, not
     just decoration. Still soft enough not to compete with the photo +
     name + quote above. */
  opacity: 0.75;
  /* iter-18e: lift above the decorative close-quote glyph on text cards. */
  z-index: 2;
}

/* ── .sw-band-faq · band header (Elementor accordion variant) ───────────────
   Header for Elementor-accordion FAQ bands. Distinct from the dark-twin
   hand-rolled .faq-item layout at lines 2688-2729 (those don't use a head). */
.sw-band-faq__head {
  max-width: var(--width-wide);
  margin: 0 auto 48px;
  text-align: center;
}
/* iter-15z (2026-05-20): unified with sitewide eyebrow + h2 spec. */
.sw-band-faq__eyebrow {
  font-family: var(--font-mono) !important;
  font-size: 11px !important;
  font-weight: 500 !important;
  letter-spacing: 0.18em !important;
  text-transform: uppercase !important;
  color: var(--ink) !important;
  margin: 0 0 16px !important;
}
.sw-band-faq__h2 {
  margin-inline: auto !important;
  font-size: clamp(28px, 3.5vw, 36px) !important;
  font-weight: 600 !important;
  line-height: 1.15 !important;
  letter-spacing: -0.012em !important;
  color: var(--ink);
  margin: 0;
  max-width: 28ch;
  text-wrap: balance;
}
.sw-band-faq .elementor-widget-accordion {
  max-width: var(--width-wide);
  margin: 0 auto;
  width: 100%;
}

/* ── .sw-band-faq · Elementor Pro Accordion skin ────────────────────────────
   D2 (locked 2026-05-19) = Elementor Pro Accordion + CSS skin. Existing
   dark-twin rules at lines 2688-2729 style a hand-rolled .faq-item DOM.
   The Elementor accordion widget emits .elementor-accordion-item with
   .elementor-tab-title + .elementor-tab-content. Skin both shapes so
   either authoring path works.

   iter-20 (2026-05-20) — Option A redesign per faq-redesign-a-b-c.html
   mockup. Three structural fixes from the iter-15a version:

   1. Elementor's default per-item border (1px solid #d5d8dc on ALL four
      sides) was beating our border-top reset, producing the "big
      rectangle" look in the screenshot operator flagged. Explicit
      border: 0 on items + hairline border-top via individual rule.

   2. Native .elementor-accordion-icon (48px wide SVG +/-) defied our
      24px width override and rendered at ~32px. Hidden entirely and
      replaced with a CSS-only +/x toggle on the .elementor-tab-title's
      ::after pseudo so size is fully under our control.

   3. Chapter-style number prefix ("01/02/03/04") injected via
      CSS counter() on the .elementor-accordion-item parent. Ties the
      FAQ band into the home's existing chapter-eyebrow language
      ("01 / What we do", "03 / From the field log").

   Also dropped: the cyan-on-open heading swap. Question keeps its
   native --ink color in both states; only the toggle does the
   visual work. */
.sw-band-faq .elementor-widget-accordion .elementor-accordion {
  display: flex;
  flex-direction: column;
  counter-reset: faq-item;
}
.sw-band-faq .elementor-accordion-item {
  border: 0 !important;
  border-top: 1px solid var(--rule) !important;
  background: transparent !important;
  counter-increment: faq-item;
}
.sw-band-faq .elementor-accordion-item:last-child {
  border-bottom: 1px solid var(--rule) !important;
}
.sw-band-faq .elementor-tab-title {
  display: grid !important;
  grid-template-columns: 56px minmax(0, 1fr) 32px;
  align-items: baseline;
  gap: 20px;
  padding: 28px 4px !important;
  cursor: pointer;
  text-decoration: none;
  color: var(--ink) !important;
  background: transparent !important;
  position: relative;
  transition: background 0.16s ease;
}
/* col 1 = mono number prefix via counter(). Native icon hidden below
   in col 3 via the +/x ::after pseudo. */
.sw-band-faq .elementor-tab-title::before {
  content: counter(faq-item, decimal-leading-zero);
  grid-column: 1;
  align-self: baseline;
  font-family: var(--font-mono);
  font-size: 13px;
  font-weight: 500;
  letter-spacing: 0.10em;
  color: var(--blue-bright);
  line-height: 1.35;
}
/* col 3 = clean CSS +/x toggle on the title itself. Animates from
   plus (closed) → x (open) via rotation of the vertical bar. */
.sw-band-faq .elementor-tab-title::after {
  content: "";
  grid-column: 3;
  align-self: center;
  width: 24px;
  height: 24px;
  background:
    linear-gradient(currentColor, currentColor) center / 16px 1.5px no-repeat,
    linear-gradient(currentColor, currentColor) center / 1.5px 16px no-repeat;
  color: var(--blue-bright);
  transition: transform 200ms ease;
}
.sw-band-faq .elementor-tab-title[aria-expanded="true"]::after {
  transform: rotate(45deg);
}
/* Native Elementor icon: out. We use the ::after pseudo above instead. */
.sw-band-faq .elementor-accordion-icon {
  display: none !important;
}
.sw-band-faq .elementor-tab-title > .elementor-accordion-title {
  grid-column: 2 !important;
}
.sw-band-faq .elementor-tab-title:hover {
  background: rgb(143 201 247 / 4%) !important;
}
.sw-band-faq .elementor-tab-title:focus-visible {
  outline: 2px solid var(--blue-bright);
  outline-offset: -4px;
}
.sw-band-faq .elementor-accordion-title {
  font-family: var(--font-sans) !important;
  font-size: clamp(18px, 1.6vw, 20px) !important;
  font-weight: 600 !important;
  line-height: 1.35 !important;
  letter-spacing: -0.005em !important;
  color: var(--ink) !important;
}
/* Body content. Indent to align under the question text (past the
   56px number column + 20px gap = 76px). Use padding-left so the
   answer sits in the same visual column as the heading. */
.sw-band-faq .elementor-tab-content {
  padding: 0 4px 28px 80px !important;
  font-size: 16px !important;
  line-height: 1.65 !important;
  color: rgb(245 247 250 / 80%) !important;
  max-width: 70ch;
  background: transparent !important;
  border: 0 !important;
}
.sw-band-faq .elementor-tab-content p {
  margin: 0 0 12px;
}
.sw-band-faq .elementor-tab-content p:last-child {
  margin-bottom: 0;
}
.sw-band-faq .elementor-tab-content strong {
  color: var(--ink) !important;
  font-weight: 600;
}
@media (max-width: 640px) {
  .sw-band-faq .elementor-tab-title {
    grid-template-columns: 40px minmax(0, 1fr) 20px;
    gap: 14px;
    padding: 22px 4px !important;
  }
  .sw-band-faq .elementor-tab-title::after {
    width: 20px;
    height: 20px;
    background-size: 14px 1.5px, 1.5px 14px;
  }
  .sw-band-faq .elementor-tab-content {
    padding: 0 4px 22px 54px !important;
  }
}

/* ── .sw-band-kpi ───────────────────────────────────────────────────────────
   4-up non-hero KPI numbers band. Framed-hidden by default (operator
   removes .sw-frame-hidden class when real numbers land). */
.sw-band-kpi {
  background: var(--bg-soft);
  border-block: 1px solid var(--rule);
}
.sw-band-kpi__head {
  max-width: var(--width-wide);
  margin: 0 auto 40px;
  text-align: center;
}
/* iter-15z (2026-05-20): unified with sitewide eyebrow + h2 spec. */
.sw-band-kpi__eyebrow {
  font-family: var(--font-mono) !important;
  font-size: 11px !important;
  font-weight: 500 !important;
  letter-spacing: 0.18em !important;
  text-transform: uppercase !important;
  color: var(--ink) !important;
  margin: 0 0 16px !important;
}
.sw-band-kpi__h2 {
  margin-inline: auto !important;
  font-size: clamp(28px, 3.5vw, 36px) !important;
  font-weight: 600 !important;
  line-height: 1.15 !important;
  letter-spacing: -0.012em !important;
  color: var(--ink);
  margin: 0;
  max-width: 24ch;
}
.sw-band-kpi__grid {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 32px;
  /* Updated 2026-05-27: 1240 → var(--width-canvas) (= 1290). +50px desktop
     gain. See widths.md (canvas tier). */
  max-width: var(--width-canvas);
  margin: 0 auto;
}
@media (max-width: 1023px) {
  .sw-band-kpi__grid { grid-template-columns: repeat(2, 1fr); gap: 24px; }
}
@media (max-width: 540px) {
  .sw-band-kpi__grid { grid-template-columns: 1fr; }
}
.sw-band-kpi__cell { display: flex; flex-direction: column; gap: 8px; }
.sw-band-kpi__number {
  font-family: var(--font-mono);
  font-size: clamp(40px, 5vw, 64px);
  font-weight: 600;
  line-height: 1;
  color: var(--ink);
  letter-spacing: -0.02em;
}
.sw-band-kpi__label {
  font-family: var(--font-sans);
  font-size: 14px;
  line-height: 1.5;
  color: var(--mute);
  max-width: 22ch;
}

/* ── .sw-band-case-teaser ───────────────────────────────────────────────────
   Single case-study teaser tile. Numbered 01 + outcome + 3 inline stats +
   deep-link arrow. Framed-hidden until PR 2 ships the first case study. */
.sw-band-case-teaser__head {
  max-width: var(--width-wide);
  margin: 0 auto 40px;
  text-align: center;
}
/* iter-15z (2026-05-20): unified with sitewide eyebrow + h2 spec. */
.sw-band-case-teaser__eyebrow {
  font-family: var(--font-mono) !important;
  font-size: 11px !important;
  font-weight: 500 !important;
  letter-spacing: 0.18em !important;
  text-transform: uppercase !important;
  color: var(--ink) !important;
  margin: 0 0 16px !important;
}
.sw-band-case-teaser__h2 {
  margin-inline: auto !important;
  font-size: clamp(28px, 3.5vw, 36px) !important;
  font-weight: 600 !important;
  line-height: 1.15 !important;
  letter-spacing: -0.012em !important;
  color: var(--ink);
  margin: 0;
  max-width: 28ch;
}
.sw-band-case-teaser__tile {
  display: grid;
  grid-template-columns: 80px minmax(0, 1fr) auto;
  gap: 32px;
  align-items: start;
  padding: 40px 36px;
  /* Updated 2026-05-27: 1240 → var(--width-canvas) (= 1290). +50px desktop
     gain. See widths.md (canvas tier). */
  max-width: var(--width-canvas);
  margin: 0 auto;
  background: var(--bg-soft);
  border: 1px solid var(--rule);
  border-radius: 6px;
  transition: border-color 0.16s ease, transform 0.16s ease;
  text-decoration: none;
  color: inherit;
}
.sw-band-case-teaser__tile:hover {
  border-color: var(--rule-strong);
  transform: translateY(-2px);
}
@media (max-width: 768px) {
  .sw-band-case-teaser__tile {
    grid-template-columns: 1fr;
    gap: 20px;
    padding: 28px 24px;
  }
}
.sw-band-case-teaser__num {
  font-family: var(--font-mono);
  font-size: clamp(40px, 4.5vw, 56px);
  font-weight: 500;
  line-height: 1;
  color: var(--blue-bright);
  letter-spacing: -0.02em;
}
.sw-band-case-teaser__body {
  display: flex;
  flex-direction: column;
  gap: 16px;
}
.sw-band-case-teaser__title {
  font-family: var(--font-sans);
  font-size: clamp(20px, 2vw, 26px);
  font-weight: 600;
  line-height: 1.25;
  color: var(--ink);
  margin: 0;
  letter-spacing: -0.008em;
}
.sw-band-case-teaser__stats {
  display: flex;
  flex-wrap: wrap;
  gap: 16px 28px;
  margin: 0;
  padding: 0;
  list-style: none;
}
.sw-band-case-teaser__stat {
  display: flex;
  flex-direction: column;
  gap: 2px;
  font-family: var(--font-mono);
}
.sw-band-case-teaser__stat .value {
  font-size: 16px;
  font-weight: 600;
  color: var(--ink);
}
.sw-band-case-teaser__stat .label {
  font-size: 10px;
  font-weight: 500;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--mute);
}
.sw-band-case-teaser__link {
  align-self: center;
  /* FLATTEN-FLOOR: literal kept — the link renders "→" (absent from JetBrains Mono);
     var(--font-mono)'s metric-matched fallback shifts the glyph width.
     Verified by css-regression harness. */
  font-family: "JetBrains Mono", ui-monospace, monospace;
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--blue-bright);
  text-decoration: none;
  white-space: nowrap;
}
.sw-band-case-teaser__link:hover { color: var(--ink); }

/* ── .sw-frame-hidden ───────────────────────────────────────────────────────
   Utility: hide a band until real content lands. Used by .sw-band-kpi (no
   real numbers) and .sw-band-case-teaser (no demo case yet — PR 2 lands it).
   Operator removes this class via Elementor when ready. */
.sw-frame-hidden { display: none !important; }

/* ── iter-19c (2026-05-20) — Home L/D alternation re-flipped after the
   walkthrough band landed at position 2.

   Operator: "How the engagement runs needs the dark, then everything else
   needs to change."

   Visible flow now:
     hero(L) → logo-wall(L) → walkthrough(D) → pillars(L) → methodology(D)
     → testimonials(L) → faq(D) → recent-posts(L) → end-cta(D)

   Hero+logo-wall stays one LIGHT zone (intentional "trust complex").
   The walkthrough band sits as the first DEEP slab to give the page
   a visual punctuation between the trust opening and the offer
   pillars. From there it alternates strictly L/D/L/D/L/D.

   Walkthrough band (ea9c977) is intentionally NOT locked here — it
   has no css_classes background override, so it inherits the body's
   var(--bg) which IS the deep canvas on dark-twin pages. Adding it
   to the lock would be redundant.

   Iter history kept for context:
     - iter-15q (2026-05-19): pillars(D)/methodology(L)/testimonials(D)
       /faq(L)/recent-posts(D)/end-cta(L). Locked the prior alternation.
     - iter-19c (2026-05-20): inverted all 6 locks above. Walkthrough
       was added at position 2 in iter-19/19b, breaking the rhythm.
       Re-flipping the locked bands restores strict alternation from
       walkthrough onward.

   Page-id-700 scoped — doesn't touch the same bands when reused on
   other surfaces (products, blog, etc.). */
/* Specificity matters here: Elementor's auto-generated per-element
   CSS uses .elementor-element-{id} (specificity 0,1,0 per class chain)
   AND there are competing rules elsewhere in this file (e.g., lines
   5841-5854 for sw-dark-twin band alternation, line 7670 for the
   recent-posts/end-cta canvas-flip). Pin each band by Elementor's
   element-id class for guaranteed wins. */
body.page-id-700 .elementor-element.elementor-element-cf0613b {
  background-color: var(--bg-soft) !important;
  background: var(--bg-soft) !important;
}
body.page-id-700 .elementor-element.elementor-element-a3c1c1c {
  background-color: var(--bg) !important;
  background: var(--bg) !important;
}
body.page-id-700 .elementor-element.elementor-element-1d9abaf {
  background-color: var(--bg-soft) !important;
  background: var(--bg-soft) !important;
}
/* 2026-05-21 — re-tuned canvas alternation from FAQ onward. Previously
   case-teaser(D) → faq(D) broke the strict L/D rhythm; faq is now LIGHT,
   field-log DARK, end-cta LIGHT so the page closes with proper
   alternation. */
body.page-id-700 .elementor-element.elementor-element-95b42a1 {
  background-color: var(--bg-soft) !important;
  background: var(--bg-soft) !important;
}
body.page-id-700 .elementor-element.elementor-element-1a87a6e {
  background-color: var(--bg) !important;
  background: var(--bg) !important;
}
body.page-id-700 .elementor-element.elementor-element-ee89f39 {
  background-color: var(--bg-soft) !important;
  background: var(--bg-soft) !important;
}
/* Testimonial cards: inset against the band. iter-19c (2026-05-20)
   flipped the band back to --bg-soft, so cards return to --bg to
   keep the "card lifts off the band" contrast. The base
   .sw-testimonial-quote / .sw-testimonial-video rules already
   declare background: var(--bg) — no override needed at this scope. */

/* ── .sw-hero-h1 · 2-line cadence (iter-3 lock 2026-05-19) ──────────────────
   Two-line H1 with cyan-bright accent on line-2 (the payoff). Each line is
   a <span class="line line-N"> child of the h1; class applied to the
   heading widget via Elementor's _css_classes setting.

   Iter history:
     - iter-1 (B9): 3-line cadence with cyan + rule mark on line-3
     - iter-3 (Cluster 3 anti-volume): 2-line cadence, cyan on line-2, rule
       mark dropped (operator: "remove the hyphen after you").
   When future heros want the rule-mark accent, add a modifier class like
   .sw-hero-h1--rule and re-introduce the ::after; don't restore it as a
   default since it accumulates on simpler 2-line H1s. */
/* iter-4 (2026-05-19): bump hero h1 above the locked clamp(22, 3.5vw, 40)
   that lives at line ~674 (`[data-sw-section="hero"] .elementor-widget-
   heading h1.elementor-heading-title` — the P3 marketing-wide hero
   typography rule). Operator wanted a bigger H1 on / (home) specifically.

   `.sw-hero-h1` is the opt-in: when a page's hero h1 widget carries this
   class, the size bumps to clamp(40, 6.5vw, 88). Heros without the class
   stay on the locked P3 clamp(22, 3.5vw, 40). Pattern matches the locked
   rule's specificity (0,0,3,1) and wins via later source order — both
   carry !important, so cascade-order decides. */
[data-sw-section="hero"] .sw-hero-h1 h1.elementor-heading-title,
[data-sw-section="hero"] .sw-hero-h1 .elementor-heading-title {
  font-size: clamp(40px, 5vw, 72px) !important;
  line-height: 1.05 !important;
  letter-spacing: -0.02em !important;
}
.sw-hero-h1 .line {
  display: block;
  text-wrap: balance;
}
.sw-hero-h1 .line + .line {
  margin-top: 0.2em;
}
.sw-hero-h1 .line-1 {
  color: var(--ink);
}
.sw-hero-h1 .line-2 {
  color: var(--blue-bright);
}
/* iter-1 line-3 styling retained as a safety net in case a future H1 wants
   to re-extend to 3 lines. .line-3 inherits .line display:block; cyan only
   applies when the visible payoff actually sits on line-3. */
.sw-hero-h1 .line-3 {
  color: var(--blue-bright);
}

/* ════════════════════════════════════════════════════════════════════════
   /case-studies/ archive — sculpted-adapt PR 2 (2026-05-20)
   Authored alongside themes/blocksy-child/archive-sw_case_study.php.
   Text-only numbered cards, editorial direction matching PR 1 testimonial
   pattern. Tokens locked: --bg, --ink, --mute, --rule, --rule-strong,
   --blue, --blue-bright, --radius-sm, --space-3/4/5, --font-sans/mono.
   Lives at end-of-file per project-locked-rule-cascade-issue memory.
   ════════════════════════════════════════════════════════════════════════ */
.sw-cs-archive {
  background: var(--bg);
}
/* Padding-block + padding-inline on both .sw-cs-archive-hero and
   .sw-cs-archive-grid-band come from the locked-surface rule above
   (body.sw-locked-surface [data-sw-section]) — the archive opts in via
   the body-class filter at is_post_type_archive('sw_case_study'). This
   inherits the same section rhythm + mobile hero floor (48px) as every
   other P3 marketing surface. Don't author per-band padding here. */
.sw-cs-archive-hero {
  text-align: center;
}
.sw-cs-archive-hero-inner {
  max-width: var(--width-prose);
  margin: 0 auto;
}
.sw-cs-archive-eyebrow {
  display: inline-block;
  font: 500 12px/1 var(--font-mono);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--blue-bright);
  margin-bottom: 16px;
}
/* H1 + H3: !important required to beat Blocksy's body-scoped defaults
   (`body.wp-theme-blocksy.wp-child-theme-blocksy-child h1` at specificity
   0,2,2 — same level as our scoped selector). Locked hero h1 spec from
   `[data-sw-section="hero"] .elementor-widget-heading h1.elementor-heading-title`
   doesn't reach our raw <h1> in the PHP template, so we re-author the
   typography here. */
.sw-cs-archive-h1 {
  font-family: var(--font-sans) !important;
  font-size: clamp(22px, 3.5vw, 40px) !important;
  font-weight: 600 !important;
  line-height: 1.05 !important;
  letter-spacing: -0.012em !important;
  color: var(--ink);
  margin: 0;
}
.sw-cs-archive-lede {
  font: 400 18px/1.55 var(--font-sans);
  color: var(--mute);
  margin: 16px 0 0;
}

.sw-cs-archive-grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--space-3);
  /* Updated 2026-05-27: 1238 → var(--width-canvas) (= 1290). +52px desktop
     gain. See widths.md (canvas tier). */
  max-width: var(--width-canvas);
  margin: 0 auto;
}
.sw-cs-archive-grid--single {
  grid-template-columns: minmax(0, 560px);
  justify-content: center;
}
/* 2-up between 720-1024 — cards at 3-up are too narrow on tablet (204px
   at 768 viewport). Drops to 1-up below 720 (mobile). */
@media (max-width: 1024px) {
  .sw-cs-archive-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}
@media (max-width: 720px) {
  .sw-cs-archive-grid {
    grid-template-columns: 1fr;
  }
}

.sw-cs-archive-card {
  background: var(--bg);
  border: 1px solid var(--rule-strong);
  border-radius: var(--radius-sm);
  padding: var(--space-4);
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  transition: border-color 200ms ease;
}
.sw-cs-archive-card:hover {
  border-color: var(--blue-bright);
}
/* Mobile-only h1 break — the <br class="sw-cs-archive-h1-break"> inside
   the hero h1 acts as a visible line break only below the 1-up grid
   breakpoint, where the 311px content area can't fit the two sentences
   on one line cleanly. Above 720px the <br> is display:none and the
   sentences flow as one line. */
.sw-cs-archive-h1-break {
  display: none;
}
@media (max-width: 720px) {
  .sw-cs-archive-h1-break {
    display: inline;
  }
  /* Tighten card padding on mobile — 32px on a 311px card eats too
     much horizontal room for the counter + headline + framing copy. */
  .sw-cs-archive-card {
    padding: var(--space-3);
  }
}
.sw-cs-archive-counter {
  font: 600 clamp(40px, 5vw, 56px)/1 var(--font-sans);
  letter-spacing: -0.02em;
  color: var(--ink);
}
.sw-cs-archive-headline {
  font-family: var(--font-sans) !important;
  font-size: clamp(20px, 2.5vw, 24px) !important;
  font-weight: 600 !important;
  line-height: 1.2 !important;
  letter-spacing: -0.005em;
  color: var(--ink);
  margin: 0;
}
.sw-cs-archive-headline a {
  color: inherit;
  text-decoration: none;
}
.sw-cs-archive-headline a:hover {
  color: var(--blue);
}
.sw-cs-archive-framing {
  font: 400 15px/1.55 var(--font-sans);
  color: var(--mute);
  margin: 0;
}
.sw-cs-archive-chips {
  list-style: none;
  padding: 0;
  margin: 0;
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}
.sw-cs-archive-chip {
  font: 500 11px/1 var(--font-mono);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--mute);
  border: 1px solid var(--rule);
  border-radius: var(--radius-sm);
  padding: 4px 8px;
}
.sw-cs-archive-cta {
  font: 500 12px/1 var(--font-mono);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--blue);
  text-decoration: none;
  margin-top: auto;
}
.sw-cs-archive-cta:hover {
  text-decoration: underline;
}

.sw-cs-archive-pagination {
  /* Updated 2026-05-27: 1238 → var(--width-canvas) (= 1290). +52px desktop
     gain. See widths.md (canvas tier). */
  max-width: var(--width-canvas);
  margin: var(--space-5) auto 0;
  display: flex;
  justify-content: center;
  gap: var(--space-3);
  font: 500 12px/1 var(--font-mono);
  letter-spacing: 0.1em;
  text-transform: uppercase;
}
.sw-cs-archive-pagination a,
.sw-cs-archive-pagination .current {
  color: var(--blue);
  text-decoration: none;
  padding: 8px 12px;
  border: 1px solid var(--rule);
  border-radius: var(--radius-sm);
}
.sw-cs-archive-pagination .current {
  color: var(--ink);
  border-color: var(--rule-strong);
}

.sw-cs-archive-empty {
  max-width: 560px;
  margin: 0 auto;
  text-align: center;
  color: var(--mute);
  font: 400 18px/1.55 var(--font-sans);
}

/* ============================================================
   Footer legal row — center copyright + legal links (sitewide)
   Added 2026-05-25 per operator request. The legal row carries the
   stable hook data-sw-role="footer-legal-row" (survives Elementor
   element-ID regeneration). The row's .e-con-inner is a left-justified
   flex row holding the © copyright heading + the inline legal-links
   icon-list; centering the inner balances the whole group. The two
   inner rules center content within each widget when the row wraps to
   multiple lines on narrow viewports. Applies on every page since the
   footer is a sitewide Blocksy Content Block.
   ============================================================ */
[data-sw-role="footer-legal-row"] .e-con-inner {
  justify-content: center !important;
  align-items: center !important;
}
[data-sw-role="footer-legal-row"] .elementor-heading-title {
  text-align: center !important;
}
[data-sw-role="footer-legal-row"] .elementor-icon-list-items.elementor-inline-items {
  justify-content: center !important;
}
