/* =====================================================================
 * blocks.css — AUTHORITATIVE source for all .sg-block-* styling.
 *
 * As of 2026-04-18 (Option B migration, Phase B1), this file is canonical.
 * Edit directly. The Next.js preview's app/globals.css is a MIRROR that
 * gets overwritten when you run `npm run sync-blocks-css` in the Style
 * Guide repo (sync direction is now WP → Next.js).
 *
 * Enqueued by inc/enqueue.php on both frontend and editor. Palette-driven
 * rules (pair helpers, inverse-icon chips, per-color button hovers) are
 * NOT in this file — they're emitted dynamically by
 * ccc_build_color_pair_helpers_css() in inc/enqueue.php so they track the
 * active child theme's palette.
 * ===================================================================== */

/* Document-level horizontal overflow guard. Clips any descendant content
 * that visually extends past the viewport width — lets carousels, hero
 * bleeds, and any other block use `overflow: visible` without stretching
 * the page and triggering a horizontal scrollbar.
 *
 * Uses `overflow-x: clip` rather than `hidden` on purpose: `hidden` on
 * <html>/<body> creates a scroll-containing block and breaks
 * `position: sticky` on descendants (e.g. our sticky header). `clip`
 * does the visual clipping without the side effects. */
html {
  overflow-x: clip;
}

/* Edge-to-edge horizontal scrollers (mobile). A block that switches to a
 * horizontal scroll-snap carousel below 768px is otherwise trapped inside the
 * page's root padding and clips its cards. Pull it out by the root padding on
 * each side and re-add that gutter inside via padding so the first/last card
 * still rests at the margin while the track reaches the page edges.
 * NOTE: must be physical `margin-left`/`margin-right` + `!important` — the
 * logical `margin-inline` does NOT override WP's physical margin-left/right
 * centering on constrained-layout children. No `width:100vw` (that caused
 * body sidescroll on iOS). Add `.sg-hscroll` to any future scroller. */
@media (max-width: 768px) {
  .is-mobile-scroll,
  .sg-hscroll {
    margin-left: calc(-1 * var(--wp--style--root--padding-left)) !important;
    margin-right: calc(-1 * var(--wp--style--root--padding-left)) !important;
    padding-left: var(--wp--style--root--padding-left);
    padding-right: var(--wp--style--root--padding-right);
  }
}

/* System (chrome) styles for the style guide page itself.
 * These intentionally use fixed values, NOT client tokens, so the chrome
 * never shifts when client tokens change. Client tokens live in a scoped
 * <style> on the preview area only. */

:root {
  --sg-font: 'Inter', system-ui, -apple-system, sans-serif;
  --sg-bg: #FFFFFF;
  --sg-surface: #F8FAFC;
  --sg-border: #E2E8F0;
  --sg-text: #0F172A;
  --sg-text-muted: #64748B;
  --sg-accent: #0F172A;
  --sg-radius: 12px;
  --sg-max-width: 1200px;
  --sg-pod-shadow: 0 3px 11px -5px rgba(0, 0, 0, 0.1), 0 8px 16px -6px rgba(0, 0, 0, 0.1);
}

/* Reusable pod hover effect: zoom to 110% + rotate 3deg on hover,
 * single overshoot bounce on return. Apply by adding .sg-pod-hover.
 * Add .sg-pod-hover-shadow alongside it to also apply --sg-pod-shadow on hover. */
.sg-pod-hover {
  transition: transform 0.5s cubic-bezier(0.34, 1.56, 0.64, 1),
              box-shadow 0.5s cubic-bezier(0.34, 1.56, 0.64, 1);
  will-change: transform;
}
.sg-pod-hover:hover {
  transform: scale(1.1) rotate(3deg);
  transition: transform 0.25s ease-out, box-shadow 0.25s ease-out;
}
.sg-pod-hover:nth-child(even):hover {
  transform: scale(1.1) rotate(-3deg);
}
.sg-pod-hover-shadow:hover {
  box-shadow: var(--sg-pod-shadow);
}

/* ==================================================================
 * Modern CSS reset (Josh Comeau-style). Pairs with blocks.css in both
 * the Next.js preview and the WP parent theme. Handles default margins,
 * box-sizing, media sizing, form control inheritance, and text wrapping
 * so individual block rules don't have to keep resetting these basics.
 * ================================================================== */

/* 1. Use a more-intuitive box-sizing model */
*, *::before, *::after {
  box-sizing: border-box;
}
/* 2. Remove default margin */
*:not(dialog) {
  margin: 0;
}
/* 3. Enable keyword animations */
@media (prefers-reduced-motion: no-preference) {
  html {
    interpolate-size: allow-keywords;
  }
}
body {
  /* 4. Increase line-height */
  line-height: 1.5;
  /* 5. Improve text rendering */
  -webkit-font-smoothing: antialiased;
}
/* 6. Improve media defaults */
img, picture, video, canvas, svg {
  display: block;
  max-width: 100%;
}
/* 7. Inherit fonts for form controls */
input, button, textarea, select {
  font: inherit;
}
/* 8. Avoid text overflows */
p, h1, h2, h3, h4, h5, h6 {
  overflow-wrap: break-word;
}
/* 9. Improve line wrapping */
p {
  text-wrap: pretty;
}
h1, h2, h3, h4, h5, h6 {
  text-wrap: balance;
}
/* 10. Create a root stacking context */
#root, #__next {
  isolation: isolate;
}

html, body {
  margin: 0;
  padding: 0;
  -webkit-font-smoothing: antialiased;
}
/* Body background / text color / font come from theme.json (styles.color +
 * typography), so they track each child theme's tokens (canvas / ink / body
 * font). They are intentionally NOT set here — the old `var(--sg-*)` values
 * were the /style-guide page's chrome tokens and leaked onto every client
 * site's <body>. The --sg-* tokens below are scoped in use to the .sg-*
 * chrome elements, which only exist on the style-guide page. */

a { color: inherit; }

/* ── Page-level rhythm + section panels ──────────────────────────────
 * Automatic vertical spacing between top-level page sections, so page
 * markup doesn't hand-code margins between blocks. This is larger than
 * the in-block blockGap (spacing--30). Tune per child theme by overriding
 * --ccc-section-gap. */
:root { --ccc-section-gap: var(--wp--preset--spacing--70); }
.wp-block-post-content > * + *,
.entry-content > * + * {
  margin-block-start: var(--ccc-section-gap);
}

/* Section-gap utilities — registered as the "Flush top" + "Overlap up" block
 * styles (inc/block-styles.php) on core/group + columns + cover + every CCC
 * block. Root-level only: they override the section-gap on a top-level block.
 *   flush-top  : zero the gap so two sections sit back-to-back.
 *   overlap-up : pull the section up over the one above it (z-index:2). The
 *                section ABOVE must have padding-bottom >= the overlap amount
 *                or its content gets covered. Overlap distance is a token
 *                (--wp--custom--overlap--*) so children can retune it; it
 *                collapses to the mobile token under 768px.
 * Scoped to direct children of the content wrapper so (0,2,0) beats the
 * section-gap rule (0,1,0) without !important. */
.entry-content > .is-style-flush-top,
.wp-block-post-content > .is-style-flush-top {
  margin-block-start: 0;
}
.entry-content > .is-style-overlap-up,
.wp-block-post-content > .is-style-overlap-up {
  margin-block-start: calc(var(--wp--custom--overlap--desktop) * -1);
  position: relative;
  z-index: 2;
}
@media (max-width: 768px) {
  .entry-content > .is-style-overlap-up,
  .wp-block-post-content > .is-style-overlap-up {
    margin-block-start: calc(var(--wp--custom--overlap--mobile) * -1);
  }
}

/* "Section Panel" block style (className is-style-section) — gives a
 * full-width wp:group the rounded + shadowed panel frame. Set the panel
 * color with the group's normal Background control (has-{slug}-background-
 * color, which also pairs the text color); set padding with the group's
 * Dimensions control. The radius + shadow live here (not inline) so they
 * survive editing and stay consistent site-wide. */
.wp-block-group.is-style-section {
  border-radius: var(--wp--custom--radius--default);
  box-shadow: var(--wp--preset--shadow--sm);
}

/* Chrome classes explicitly use --sg-font. Without this, any chrome element
 * that doesn't set its own font-family would inherit the client body font
 * (since .sg-preview is the <main> itself and the generated client CSS sets
 * font-family on it). Block samples use their own element tags (h1-h6, p)
 * which get client fonts via the generated CSS rules. */
.sg-header, .sg-header p,
.sg-section-head, .sg-eyebrow,
.sg-type-cell, .sg-type-cell-name, .sg-type-spec-muted,
.sg-swatch-name, .sg-swatch-hex-label,
.sg-block-label,
.sg-spacing-label,
.sg-icon-tile-name,
.sg-shadow-tile,
.sg-btn-export,
.sg-landing-card, .sg-landing-card h3, .sg-landing-card p,
.sg-field > label {
  font-family: var(--sg-font);
}

/* Page wrapper — 100% wide so .alignfull children can truly span the viewport.
 * Every direct child auto-constrains to --wp--style--global--content-size and centers itself.
 * Children that opt into .alignwide go to --wp--style--global--wide-size; .alignfull breaks out
 * to 100% of the viewport. */
.sg-shell {
  padding: 48px 0 96px;
}
.sg-shell > * {
  max-width: var(--wp--style--global--content-size);
  margin-left: auto !important;
  margin-right: auto !important;
}
.sg-shell > .alignwide {
  max-width: var(--wp--style--global--wide-size) !important;
}
.sg-shell > .alignfull {
  max-width: none !important;
  margin-left: 0 !important;
  margin-right: 0 !important;
}

.sg-header {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 24px;
  padding: 0 32px 32px;
  border-bottom: 1px solid var(--sg-border);
  margin-bottom: 56px;
}

.sg-header h1 {
  font-family: var(--sg-font);
  font-size: 40px;
  font-weight: 700;
  margin: 0 0 4px;
  text-transform: none;
  letter-spacing: -0.01em;
}

.sg-header p { color: var(--sg-text-muted); margin: 0; }

.sg-section {
  margin-bottom: 80px;
}

.sg-section-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 16px;
  margin-bottom: 24px;
  padding-bottom: 12px;
  border-bottom: 1px solid var(--sg-border);
}

.sg-section-head h2 {
  font-family: var(--sg-font);
  font-size: 24px;
  font-weight: 600;
  margin: 0;
  letter-spacing: -0.01em;
  text-transform: none;
}

.sg-eyebrow {
  font-size: 11px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--sg-text-muted);
}

.sg-btn-export {
  font-family: var(--sg-font);
  font-size: 13px;
  font-weight: 500;
  padding: 8px 14px;
  border-radius: 8px;
  background: var(--sg-accent);
  color: #FFFFFF;
  border: none;
  text-decoration: none;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  gap: 6px;
}

.sg-btn-export:hover { opacity: 0.9; }

/* Colors grid */
.sg-color-group { margin-bottom: 24px; }
.sg-color-group:last-child { margin-bottom: 0; }
.sg-color-group-title {
  font-family: var(--sg-font);
  font-size: 14px;
  font-weight: 600;
  color: var(--sg-text-muted);
  margin: 0 0 12px;
  text-transform: uppercase;
  letter-spacing: 0.06em;
}
.sg-color-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 20px;
}

.sg-swatch {
  border-radius: var(--sg-radius);
  border: 1px solid var(--sg-border);
  overflow: hidden;
  background: #FFF;
  box-shadow: var(--sg-pod-shadow);
}

.sg-swatch-color {
  position: relative;
  height: 140px;
  display: flex;
}

.sg-swatch-half {
  position: relative;
  flex: 1;
  min-width: 0;
}

.sg-swatch-hex-label {
  position: absolute;
  bottom: 12px;
  font-size: 12px;
  font-weight: 500;
  letter-spacing: 0.02em;
}

.sg-swatch-meta {
  padding: 14px 16px;
}

.sg-swatch-name {
  font-size: 14px;
  font-weight: 600;
  margin: 0;
  text-transform: capitalize;
}

/* Type table — inline columns: Style | Family | Weight | Size | Sample */
.sg-type-row {
  display: grid;
  grid-template-columns: 1fr 1.6fr 3fr;
  gap: 32px;
  align-items: center;
  padding: 24px 0;
  border-bottom: 1px solid var(--sg-border);
}

.sg-type-cell {
  font-size: 13px;
  color: var(--sg-text);
  min-width: 0;
}

.sg-type-cell-name {
  font-weight: 600;
  font-size: 14px;
}

.sg-type-cell-sample {
  text-align: right;
  overflow: hidden;
}

.sg-type-spec-muted {
  color: var(--sg-text-muted);
}

/* Buttons section */
.sg-button-row { display: flex; flex-wrap: wrap; gap: 16px; align-items: center; margin-bottom: 24px; }
.sg-button-row + .sg-button-row { margin-top: 8px; }

/* Rich text */
.sg-richtext {
  background: var(--sg-surface);
  border: 1px solid var(--sg-border);
  border-radius: var(--sg-radius);
  padding: 32px;
}

/* Block samples */
.sg-block-label {
  font-size: 12px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--sg-text-muted);
  margin-bottom: 16px;
}
.sg-block-label:not(:first-child) {
  margin-top: var(--wp--preset--spacing--100);
}

/* ==================================================================
 * Block samples — layout only.
 * Typography comes from element defaults (body font + h1–h6 rules).
 * Use Gutenberg-style helper classes (.has-large-font-size, .has-*-color)
 * on elements that need a size or color override. All spacing uses --space-*.
 * ================================================================== */

/* Editor-parity reset: these blocks apply the admin-picked bg color to an
 * INNER element (card/body/inner panel) rather than the block root.
 *
 * On the frontend, `ccc_strip_bg_from_wrapper()` in inc/enqueue.php removes
 * WP's auto-injected `has-{slug}-background-color` + `has-background` classes
 * (and the inline bg style) from the block wrapper in render.php output.
 *
 * In the block editor iframe, Gutenberg's `useBlockProps` re-applies those
 * same classes to the block wrapper via JavaScript, independent of what
 * render.php returns. That makes the editor preview look wrong — the whole
 * block is colored when the frontend only has the inner element colored.
 *
 * This rule nulls the wrapper-level bg for these blocks ONLY when
 * `has-background` is present on the wrapper. On the frontend, `has-background`
 * is stripped from the wrapper so this rule no-ops. In the editor, it fires
 * and restores parity with the frontend. Inner elements are untouched (the
 * selector requires the block-wrapper class). */
:is(
  .wp-block-classic-city-core-feature-grid,
  .wp-block-classic-city-core-icon-feature-row,
  .wp-block-classic-city-core-image-columns,
  .wp-block-classic-city-core-image-hero-50-50,
  .wp-block-classic-city-core-image-overlay,
  .wp-block-classic-city-core-process-steps
).has-background {
  background: none !important;
  background-color: transparent !important;
  color: inherit !important;
}

/* ACF Pro wraps frontend InnerBlocks in <div class="acf-innerblocks-container">.
 * Flatten it for layout purposes so flex/grid/gap on the parent container applies
 * to the real content blocks inside. No-op in the Next.js preview (no such
 * element is emitted there). */
.sg-block-hero-content > .acf-innerblocks-container,
.sg-block-hero-full-card > .acf-innerblocks-container,
.sg-block-hero-bg-content > .acf-innerblocks-container,
.sg-block-hero-gradient__content > .acf-innerblocks-container,
.sg-block-split-body > .acf-innerblocks-container,
.sg-block-overlay-content > .acf-innerblocks-container,
.sg-block-center-content-body > .acf-innerblocks-container,
.sg-block-cta-inner > .acf-innerblocks-container,
.sg-block-stackable > .acf-innerblocks-container,
.sg-block-framed-callout__content > .acf-innerblocks-container {
  display: contents;
}

/* Eyebrow — small caps lead-in above a heading.
 * - `.sg-block-eyebrow` is used by the React preview (BlockSamplesSection.tsx).
 * - `.is-style-eyebrow` is what WP applies when the core/paragraph "Eyebrow"
 *   block style is picked in the editor (registered in inc/block-styles.php).
 * Both selectors get the same treatment so the preview and WP output match. */
.sg-block-eyebrow,
.is-style-eyebrow {
  font-family: var(--wp--preset--font-family--body);
  font-size: var(--wp--preset--font-size--small);
  text-transform: uppercase;
  letter-spacing: var(--wp--custom--eyebrow--letter-spacing);
  margin: 0;
}

/* Gap-collapse rule: eyebrow + heading should read as one unit (10px
 * spacing) regardless of whether they're inside a flex-gap container (ACF
 * blocks) or a core flow/constrained layout (wp:group, etc).
 *
 * Two layouts behave differently and need different math:
 *
 *   1. Flex/grid with `gap` (our ACF block shells) — CSS spec says gap
 *      and child margins ACCUMULATE. A negative margin subtracts from the
 *      parent's gap to leave the 10px we want.
 *
 *   2. Core flow/constrained layout (no real gap — WP simulates it by
 *      setting `margin-block-start: block-gap` on each sibling via a
 *      layout rule). Our rule lives on the same physical margin edge at
 *      higher specificity, so it REPLACES WP's margin rather than
 *      accumulating. A negative value would pull the heading into the
 *      eyebrow — we need a positive 10px to replace WP's gap with 10px.
 *
 * So we fork on the parent's layout class. `.is-layout-flow` and
 * `.is-layout-constrained` are the core-layout markers WP attaches to
 * wp:group and similar. Our custom ACF containers don't carry them, so
 * the default rule catches those.
 */
.sg-block-eyebrow + :is(h1, h2, h3, h4, h5, h6),
.is-style-eyebrow + :is(h1, h2, h3, h4, h5, h6) {
  margin-block-start: calc(var(--wp--preset--spacing--10) - var(--wp--style--block-gap));
}
:is(.is-layout-flow, .is-layout-constrained) > .sg-block-eyebrow + :is(h1, h2, h3, h4, h5, h6),
:is(.is-layout-flow, .is-layout-constrained) > .is-style-eyebrow + :is(h1, h2, h3, h4, h5, h6) {
  margin-block-start: var(--wp--preset--spacing--10);
}

/* ==================================================================
 * Shared block head — the canonical "eyebrow + h2" intro section that
 * sits at the top of blocks like image-link-cards and
 * product-feature-toggles. Markup emitted by `ccc_render_block_head()`
 * in inc/block-head.php.
 *
 * The wrapper carries `is-layout-flow` so the parent eyebrow→heading
 * collapse rule above lands cleanly (10px gap, no flex-gap math).
 * `has-text-align-center` on the eyebrow and h2 themselves wires up
 * centering the WP-idiomatic way — same class WP applies when an
 * author drops a centered block via Gutenberg's alignment toolbar.
 * ================================================================== */
.sg-block-head {
  text-align: center;
  max-width: var(--wp--custom--layout--narrow-size, 800px);
  margin: 0 auto;
}

.sg-block-head__heading {
  text-transform: uppercase;
  /* margin intentionally NOT set — the parent's eyebrow + h2 rule
     governs top margin (10px); h2 typography (size/family/weight)
     inherits from theme.json's elements.h2. */
}

/* Hero */
.sg-block-hero {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--wp--preset--spacing--50);
  align-items: center;
}
/* Side-toggle swap: DOM is content → image. Default matches field 'right'.
 * `.image-left` flips column placement so the image moves to col 1.
 * `grid-row: 1` on both children is required — grid auto-placement moves the
 * cursor forward after placing an item, so without an explicit row, the
 * second child (which wants the "earlier" column) drops to row 2. */
.sg-block-hero.image-left .sg-block-hero-content {
  grid-column: 2;
  grid-row: 1;
}
.sg-block-hero.image-left .sg-block-hero-image {
  grid-column: 1;
  grid-row: 1;
}
.sg-block-hero-content {
  display: flex;
  flex-direction: column;
  gap: var(--wp--style--block-gap);
}
.sg-block-hero-image {
  aspect-ratio: 4 / 3;
  background-size: cover;
  background-position: center;
  background-color: var(--wp--custom--gray--40);
  border-radius: var(--wp--custom--radius--default);
  box-shadow: var(--wp--custom--elevation--card, var(--wp--preset--shadow--md));
}

/* Stats — block in markup carries .has-secondary-gradient-background, which
 * applies the secondary→secondary-alt gradient AND secondary's opposite text
 * color via the generated gradient helper (see css-generator.ts). */
.sg-block-stats {
  display: grid;
  grid-template-columns: repeat(var(--block-columns, 4), 1fr);
  gap: var(--wp--style--block-gap);
  padding: var(--wp--preset--spacing--60) var(--wp--preset--spacing--50);
  border-radius: var(--wp--custom--radius--default);
  box-shadow: var(--wp--custom--elevation--card, var(--wp--preset--shadow--md));
}
.sg-block-stat {
  text-align: center;
  display: flex;
  flex-direction: column;
  gap: var(--wp--preset--spacing--10);
}

/* Feature grid with icons */
.sg-block-features {
  display: grid;
  grid-template-columns: repeat(var(--block-columns, 3), 1fr);
  gap: var(--wp--style--block-gap);
}
/* Default bg wrapped in :where() (0-specificity) so any .has-*-background-color
 * helper from the admin's per-card picker wins without needing !important. */
:where(.sg-block-feature) {
  background: var(--wp--preset--color--panel, #ffffff);
}
.sg-block-feature {
  border-radius: var(--wp--custom--radius--default);
  padding: var(--wp--preset--spacing--30);
  box-shadow: var(--wp--custom--elevation--card, var(--wp--preset--shadow--md));
  display: flex;
  flex-direction: column;
  gap: var(--wp--style--block-gap);
}
.sg-block-feature-content {
  display: flex;
  flex-direction: column;
  gap: var(--wp--preset--spacing--10);
}
/* Default icon bg = primary (same :where() dance so inverse-icon rules emitted
 * by css-generator.ts can override when the parent card has a bg picked). */
:where(.sg-block-feature-icon) {
  background: var(--wp--preset--color--primary);
  color: var(--wp--custom--color--primary-opposite);
}
.sg-block-feature-icon {
  width: var(--wp--preset--spacing--40);
  height: var(--wp--preset--spacing--40);
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: var(--wp--custom--radius--default);
}
/* Icon glyph inside the brand-colored tile. FA <i> element; inherits `color`
 * from the icon wrapper so inverse-icon rules emitted per palette slug take
 * effect without re-targeting the glyph. */
.sg-block-feature-icon-glyph {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--wp--preset--spacing--20);
  height: var(--wp--preset--spacing--20);
  color: inherit;
  font-size: var(--wp--preset--spacing--20);
  line-height: 1;
}

/* ==================================================================
 * Logo strip — two layouts: static grid + marquee scroller.
 *
 * Child themes can tweak any of these without forking the block, via
 * the CSS variables declared on .sg-block-logos:
 *   --sg-logo-max-height   image cap (default unset = native size)
 *   --sg-logo-gap          space between logos (default spacing--50)
 *   --sg-logo-scroll-dur   marquee cycle duration (default 40s; the
 *                          speed modifier classes set this, but child
 *                          themes can pin it explicitly too)
 * ================================================================== */
.sg-block-logos {
  --sg-logo-gap: var(--wp--preset--spacing--50);
  --sg-logo-scroll-dur: 40s;
  text-align: center;
  padding: var(--wp--preset--spacing--30) 0;
  display: flex;
  flex-direction: column;
  gap: var(--wp--style--block-gap);
}
.sg-block-logos img {
  /* max-height only applies when child theme sets the var (or
   * scroller mode below pins it). Grid mode otherwise behaves as
   * before — native logo sizes. */
  max-height: var(--sg-logo-max-height, none);
  width: auto;
  object-fit: contain;
  flex-shrink: 0;
}
.sg-block-logos-row {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  align-items: center;
  gap: var(--sg-logo-gap);
  filter: grayscale(1);
  /* Opacity dimming is a per-site decision — not applied by default. */
}

/* Scroller layout — single nowrap row, CSS marquee. The .row contains
 * the logo set rendered twice (the duplicate is aria-hidden); the
 * animation translates -50% so the second set lands exactly where the
 * first started, producing an infinite loop. */
.sg-block-logos--scroller {
  --sg-logo-max-height: 60px;
  overflow: hidden;
  width: 100%;
}
.sg-block-logos--scroller .sg-block-logos-row {
  flex-wrap: nowrap;
  justify-content: flex-start;
  width: max-content;
  animation: sg-logo-scroll var(--sg-logo-scroll-dur) linear infinite;
}
.sg-block-logos--speed-slow   { --sg-logo-scroll-dur: 60s; }
.sg-block-logos--speed-medium { --sg-logo-scroll-dur: 40s; }
.sg-block-logos--speed-fast   { --sg-logo-scroll-dur: 25s; }
.sg-block-logos--dir-right .sg-block-logos-row {
  animation-direction: reverse;
}
.sg-block-logos--pause-on-hover:hover .sg-block-logos-row {
  animation-play-state: paused;
}
@keyframes sg-logo-scroll {
  from { transform: translateX(0); }
  to   { transform: translateX(-50%); }
}

/* Accessibility: respect prefers-reduced-motion by halting the
 * animation entirely and falling back to wrapped layout. */
@media (prefers-reduced-motion: reduce) {
  .sg-block-logos--scroller {
    overflow: visible;
  }
  .sg-block-logos--scroller .sg-block-logos-row {
    animation: none;
    flex-wrap: wrap;
    justify-content: center;
    width: auto;
  }
}

/* Block-quote (matches WP core .wp-block-quote markup).
 *   <blockquote class="wp-block-quote [is-style-quote]">
 *     <p>{quote}</p>
 *     <cite><strong>{name}</strong><br>{title}</cite>
 *   </blockquote>
 * Default styling: centered, large body text on the <p>, muted cite.
 * The .is-style-quote modifier prepends a brand-colored quote glyph via ::before. */
.wp-block-quote {
  max-width: 760px;
  margin: 0 auto;
  text-align: center;
  padding: var(--wp--preset--spacing--50) var(--wp--preset--spacing--20);
  display: flex;
  flex-direction: column;
  gap: var(--wp--style--block-gap);
  align-items: center;
}
.wp-block-quote p {
  font-size: var(--wp--preset--font-size--large);
  margin: 0;
}
.wp-block-quote cite {
  font-style: normal;
  font-size: var(--wp--preset--font-size--small);
  display: block;
}
.wp-block-quote cite strong {
  display: inline-block;
  font-size: var(--wp--preset--font-size--medium);
  margin-bottom: 2px;
}
.wp-block-quote.is-style-quote::before {
  content: '';
  display: block;
  width: 36px;
  height: 36px;
  background-color: var(--wp--preset--color--primary);
  -webkit-mask-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 448 512'><path d='M0 216C0 149.7 53.7 96 120 96l8 0c17.7 0 32 14.3 32 32s-14.3 32-32 32l-8 0c-30.9 0-56 25.1-56 56l0 8 64 0c35.3 0 64 28.7 64 64l0 64c0 35.3-28.7 64-64 64l-64 0c-35.3 0-64-28.7-64-64L0 216zm256 0c0-66.3 53.7-120 120-120l8 0c17.7 0 32 14.3 32 32s-14.3 32-32 32l-8 0c-30.9 0-56 25.1-56 56l0 8 64 0c35.3 0 64 28.7 64 64l0 64c0 35.3-28.7 64-64 64l-64 0c-35.3 0-64-28.7-64-64l0-136z'/></svg>");
          mask-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 448 512'><path d='M0 216C0 149.7 53.7 96 120 96l8 0c17.7 0 32 14.3 32 32s-14.3 32-32 32l-8 0c-30.9 0-56 25.1-56 56l0 8 64 0c35.3 0 64 28.7 64 64l0 64c0 35.3-28.7 64-64 64l-64 0c-35.3 0-64-28.7-64-64L0 216zm256 0c0-66.3 53.7-120 120-120l8 0c17.7 0 32 14.3 32 32s-14.3 32-32 32l-8 0c-30.9 0-56 25.1-56 56l0 8 64 0c35.3 0 64 28.7 64 64l0 64c0 35.3-28.7 64-64 64l-64 0c-35.3 0-64-28.7-64-64l0-136z'/></svg>");
  -webkit-mask-repeat: no-repeat;
          mask-repeat: no-repeat;
  -webkit-mask-position: center;
          mask-position: center;
  -webkit-mask-size: contain;
          mask-size: contain;
}

/* core/image — Drop Shadow style.
 * Registered in inc/block-styles.php. Apply via the editor sidebar's
 * Styles panel; WP injects `.is-style-shadow` on the wrapping <figure>.
 * Shadow lands on the <img> itself so it tracks the image rectangle
 * (not the caption, if one's set). Uses --wp--preset--shadow--md so
 * every client theme picks up its own shadow scale. */
.wp-block-image.is-style-shadow img {
  box-shadow: var(--wp--custom--elevation--card, var(--wp--preset--shadow--md));
}

/* Testimonial cards */
.sg-block-testimonial-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--wp--style--block-gap);
}
.sg-block-testimonial-card {
  background: var(--wp--preset--color--panel, #ffffff);
  border-radius: var(--wp--custom--radius--default);
  box-shadow: var(--wp--custom--elevation--card, var(--wp--preset--shadow--md));
  padding: var(--wp--preset--spacing--30);
  display: flex;
  flex-direction: column;
  gap: var(--wp--style--block-gap);
}
.sg-block-testimonial-card-icon { color: var(--wp--preset--color--primary); }
.sg-block-testimonial-card-icon svg { width: var(--wp--preset--spacing--30); height: var(--wp--preset--spacing--30); }
.sg-block-testimonial-card-quote { flex: 1; }

/* FAQ accordion */
.sg-block-faq {
  display: flex;
  flex-direction: column;
  gap: var(--wp--preset--spacing--10);
}
.sg-block-faq-item {
  background: var(--wp--preset--color--panel, #ffffff);
  border: 1px solid var(--sg-border);
  border-left: 4px solid transparent;
  border-radius: var(--wp--custom--radius--default);
  padding: 0 var(--wp--preset--spacing--20);
  transition: border-left-color 0.15s ease, box-shadow 0.15s ease;
}
.sg-block-faq-item[open] {
  border-left-color: var(--wp--preset--color--secondary);
  box-shadow: var(--wp--preset--shadow--sm);
}
.sg-block-faq-question {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--wp--style--block-gap);
  font-weight: 600;
  padding: var(--wp--preset--spacing--20) 0;
  cursor: pointer;
  list-style: none;
}
.sg-block-faq-question::-webkit-details-marker { display: none; }
.sg-block-faq-icon {
  color: var(--wp--preset--color--primary);
  transition: transform 0.2s ease;
  display: inline-block;
}
.sg-block-faq-item[open] .sg-block-faq-icon { transform: rotate(45deg); }
.sg-block-faq-answer { padding: 0 0 var(--wp--preset--spacing--20); }

.sg-block-columns {
  display: grid;
  grid-template-columns: repeat(var(--block-columns, 3), 1fr);
  gap: var(--wp--style--block-gap);
}

.sg-block-card {
  background: var(--wp--preset--color--panel, #ffffff);
  border-radius: var(--wp--custom--radius--default);
  overflow: hidden;
  display: flex;
  flex-direction: column;
  box-shadow: var(--wp--custom--elevation--card, var(--wp--preset--shadow--md));
}
.sg-block-card-image {
  background-size: cover;
  background-position: center;
  background-color: var(--wp--custom--gray--40);
}
.sg-block-card-image.aspect-horizontal { aspect-ratio: 16 / 9; }
.sg-block-card-image.aspect-square     { aspect-ratio: 1 / 1; }
.sg-block-card-image.aspect-vertical   { aspect-ratio: 3 / 4; }
/* Card body default bg wrapped in :where() so per-card .has-*-background-color
 * helpers from the admin picker override without needing !important. */
:where(.sg-block-card-body) {
  background: var(--wp--preset--color--panel, #ffffff);
}
.sg-block-card-body {
  padding: var(--wp--preset--spacing--30);
  display: flex;
  flex-direction: column;
  gap: var(--wp--preset--spacing--10);
  flex: 1;
}
.sg-block-card-copy { flex: 1; }
/* Tight 10px gap rhythm inside the card body is right for headline+body, but
 * the button needs a bit more air above it to feel like its own element. */
.sg-block-card-body > .sg-block-card-btn {
  margin-top: var(--wp--preset--spacing--20);
}
.sg-block-card-btn,
.sg-block-card-btn .wp-block-button__link { width: 100%; }

/* Split 50/50 block */
.sg-block-split {
  display: grid;
  grid-template-columns: 1fr 1fr;
  background: var(--wp--preset--color--panel, #ffffff);
  border-radius: var(--wp--custom--radius--default);
  overflow: hidden;
  box-shadow: var(--wp--custom--elevation--card, var(--wp--preset--shadow--md));
  align-items: stretch;
}
/* Side-toggle swap: DOM is image → body. Default matches field 'left'.
 * `.content-left` flips column placement so the body moves to col 1.
 * `grid-row: 1` on both prevents auto-placement from dropping the second
 * child to row 2 when it wants an "earlier" column than the first. */
.sg-block-split.content-left .sg-block-split-image {
  grid-column: 2;
  grid-row: 1;
}
.sg-block-split.content-left .sg-block-split-body {
  grid-column: 1;
  grid-row: 1;
}
.sg-block-split-image {
  position: relative;
  background-size: cover;
  background-position: center;
  background-color: var(--wp--custom--gray--40);
  min-height: 360px;
}
.sg-block-split-video {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
.sg-block-split-body {
  padding: var(--wp--preset--spacing--50);
  display: flex;
  flex-direction: column;
  gap: var(--wp--style--block-gap);
  justify-content: center;
}
.sg-block-split-list {
  margin: 0;
  padding-left: var(--wp--preset--spacing--20);
}
.sg-block-split-list li + li { margin-top: var(--wp--preset--spacing--10); }

/* Large CTA block — layered background model:
 *
 *  z-axis (bottom → top):
 *    wrapper bg color  (this rule's `background` OR WP helper class)
 *    wrapper texture   (.has-bg-texture, if set — see textures.php)
 *    .sg-block-cta-bg  (absolute child, image at chosen opacity)
 *    .sg-block-cta-inner (positioned, z-index:1 — content always on top)
 *
 * `overflow: hidden` keeps the absolute image layer clipped to the
 * wrapper's border-radius. The `background:` shorthand here is the
 * default that prints when no native bg color is chosen — WP's
 * `.has-{slug}-background-color { !important }` utility wins when an
 * author picks a different palette color from the native picker.
 *
 * See blocks/cta-large/render.php header for the full rationale and
 * backward-compat notes.
 */
/* Full-width CTAs read as edge-to-edge bands, so the rounded corners look
 * wrong at that scale — square them when the block is alignfull. */
.sg-block-cta.alignfull,
.sg-block-cta-thin.alignfull {
  border-radius: 0;
}
.sg-block-cta {
  position: relative;
  overflow: hidden;
  background: var(--wp--preset--color--cta);
  color: var(--wp--custom--color--cta-opposite);
  border-radius: var(--wp--custom--radius--default);
  box-shadow: var(--wp--custom--elevation--card, var(--wp--preset--shadow--md));
  padding: var(--wp--preset--spacing--60) var(--wp--preset--spacing--30);
  text-align: center;
}
.sg-block-cta-bg {
  /* Absolute child filling the wrapper's padding box (inset:0 lands at
     the padding edge of the nearest positioned ancestor). Image is
     cover-fit and center-positioned by default — overrides via
     block-instance custom CSS if a specific CTA needs `contain` or a
     non-center focal point. Opacity comes from render.php inline. */
  position: absolute;
  inset: 0;
  background-size: cover;
  background-position: center;
  background-repeat: no-repeat;
  z-index: 0;
  pointer-events: none;
}
.sg-block-cta-inner {
  position: relative;
  z-index: 1;
  max-width: var(--wp--custom--layout--narrow-size);
  margin: 0 auto;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--wp--style--block-gap);
}
/* Button sitting on top of --wp--preset--color--cta: invert to opposite background + cta text.
 * Class sits on the .wp-block-button wrapper; visual styling targets the inner anchor. */
.sg-block-cta-button .wp-block-button__link {
  background-color: var(--wp--custom--color--cta-opposite);
  color: var(--wp--preset--color--cta);
  border-color: var(--wp--custom--color--cta-opposite);
}
.sg-block-cta-button .wp-block-button__link:hover {
  background-color: transparent;
  color: var(--wp--custom--color--cta-opposite);
}

/* Outline button style — WP core registers `.is-style-outline` as a block
 * style on core/button; we own the visual: transparent bg + currentColor
 * border. Hover state is emitted per-color by ccc_build_color_pair_helpers_css()
 * so text swaps from {slug} to {slug}-alt (border follows via currentColor).
 * Apply a textColor to outline buttons to get the hover — e.g.
 * `<!-- wp:button {"className":"is-style-outline","textColor":"primary"} -->`.
 *
 * Padding override: WP core ships a rule
 *   .wp-block-button.is-style-outline>:where(.wp-block-button__link):not(.has-background) { padding: 0.667em 1.333em; }
 * that hijacks outline buttons (they never have `.has-background`) with its
 * own hardcoded padding, making them render smaller than solid siblings.
 * Re-apply our theme.json-driven padding here to restore parity. Specificity
 * beats core's `:where()` (0 specificity). */
.wp-block-button.is-style-outline .wp-block-button__link {
  background-color: transparent;
  border-color: currentColor;
  padding: var(--wp--custom--btn--padding-y) var(--wp--custom--btn--padding-x);
}

/* Thin CTA bar — full-width, content-width inner row justified L/R.
 * Left: headline + subtext. Right: CTA-opposite button. Stacks centered on mobile.
 * Same layered-bg model as cta-large — see that block's comment above for
 * the rationale. No border-radius here so no `overflow: hidden` needed. */
.sg-block-cta-thin {
  position: relative;
  background: var(--wp--preset--color--cta);
  color: var(--wp--custom--color--cta-opposite);
  padding: var(--wp--preset--spacing--30) var(--wp--preset--spacing--30);
}
.sg-block-cta-thin-bg {
  position: absolute;
  inset: 0;
  background-size: cover;
  background-position: center;
  background-repeat: no-repeat;
  z-index: 0;
  pointer-events: none;
}
.sg-block-cta-thin-inner {
  position: relative;
  z-index: 1;
  max-width: var(--wp--style--global--content-size);
  margin: 0 auto;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--wp--style--block-gap);
}
.sg-block-cta-thin-copy {
  display: flex;
  flex-direction: column;
  gap: 0;
}
.sg-block-cta-thin-copy p { margin: 0; }
@media (max-width: 768px) {
  .sg-block-cta-thin-inner {
    flex-direction: column;
    text-align: center;
  }
}

/* Flat icon feature row — four inline benefits with FA icon + heading + copy.
 * Block sits on --wp--preset--color--canvas with a large drop shadow; columns are separated
 * by a 1px divider in --wp--custom--gray--40. */
.sg-block-feature-row {
  display: grid;
  grid-template-columns: repeat(var(--block-columns, 4), 1fr);
  gap: 0;
  /* Card-esque default: a panel (white) surface so the row reads as a card
   * on the page canvas. (Was canvas — which now equals the body bg, leaving
   * the panel invisible.) Set a per-item Background to override. */
  background: var(--wp--preset--color--panel);
  color: var(--wp--custom--color--panel-opposite, var(--wp--preset--color--ink));
  border-radius: var(--wp--custom--radius--default);
  box-shadow: var(--wp--preset--shadow--lg);
  /* Clip children so per-item backgrounds respect the block's rounded corners. */
  overflow: hidden;
}
.sg-block-feature-row-item {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: var(--wp--preset--spacing--20);
  padding: var(--wp--preset--spacing--30);
}
/* Icon + h5 stack inside this wrapper at the spacing--30 gap; the wrapper
 * is then a sibling to the body paragraph (spacing--20 set on the item
 * above), creating a clear header/body rhythm. */
.sg-block-feature-row-head {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--wp--preset--spacing--30);
}
/* Divider only between items that share the default (no per-item bg) look.
 * When an item has its own .has-*-background-color, the contrasting bg is
 * the divider — the 1px line would look grubby against it. */
.sg-block-feature-row-item:not(:first-child):not([class*="has-"][class*="-background-color"]) {
  border-left: 1px solid var(--wp--custom--gray--40);
}
/* Default icon look — plain glyph, primary-colored. Wrapped in :where() so
 * the per-slug chip rules emitted by css-generator.ts (higher specificity)
 * take over when an item has a bg color. */
:where(.sg-block-feature-row-icon) {
  color: var(--wp--preset--color--primary);
}
/* Feature row icon — 40x40 chip sized for both the bare-glyph default and the
 * per-slug chip treatment (bg + rounded corners applied by inverse-icon rules
 * from css-generator.ts). Default color is set above via :where(). */
.sg-block-feature-row-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--wp--preset--spacing--40);
  height: var(--wp--preset--spacing--40);
  font-size: var(--wp--preset--spacing--40);
  line-height: 1;
}

/* Process steps — cards with numbered badges hanging half-above the card top.
 * Desktop: 4-up grid. Mobile: horizontal scroll with snap. */
.sg-block-process {
  display: grid;
  grid-template-columns: repeat(var(--block-columns, 5), 1fr);
  gap: var(--wp--style--block-gap);
  counter-reset: sg-step;
  /* Make room for the number badges that overhang the card tops by half their height. */
  padding-top: calc(var(--wp--preset--spacing--60) / 2);
}
/* Card bg default wrapped in :where() so per-step .has-*-background-color wins.
 * Pair the text color with the panel bg too — without this, a process-steps
 * block placed inside a dark-bg section inherits that section's light text and
 * the step copy renders invisibly (light on the light panel). */
:where(.sg-block-process-step) {
  background: var(--wp--preset--color--panel, #ffffff);
  color: var(--wp--custom--color--panel-opposite, var(--wp--preset--color--ink));
}
.sg-block-process-step {
  position: relative;
  border-radius: var(--wp--custom--radius--default);
  box-shadow: var(--wp--custom--elevation--card, var(--wp--preset--shadow--md));
  padding: calc(var(--wp--preset--spacing--60) / 2 + var(--wp--preset--spacing--30)) var(--wp--preset--spacing--20) var(--wp--preset--spacing--30);
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: var(--wp--preset--spacing--10);
  counter-increment: sg-step;
}
.sg-block-process-step::before {
  content: counter(sg-step);
  position: absolute;
  top: calc(var(--wp--preset--spacing--60) / -2);
  left: 50%;
  transform: translateX(-50%);
  display: flex;
  align-items: center;
  justify-content: center;
  width: var(--wp--preset--spacing--60);
  height: var(--wp--preset--spacing--60);
  border-radius: 50%;
  /* Number badge colors come from custom props so render.php can override
   * per-step. Defaults to primary / primary-opposite. */
  background: var(--step-number-bg, var(--wp--preset--color--primary));
  color: var(--step-number-color, var(--wp--custom--color--primary-opposite));
  font-family: var(--wp--preset--font-family--heading);
  font-size: var(--wp--preset--font-size--large);
  font-weight: 700;
  line-height: 1;
  box-shadow: var(--wp--preset--shadow--sm);
}
@media (max-width: 768px) {
  .sg-block-process {
    grid-template-columns: none;
    grid-auto-flow: column;
    grid-auto-columns: 80%;
    overflow-x: auto;
    scroll-snap-type: x mandatory;
    padding-bottom: var(--wp--preset--spacing--20);
    -webkit-overflow-scrolling: touch;
  }
  .sg-block-process-step {
    scroll-snap-align: center;
  }
}

/* Document downloads — card grid where each doc shows a type badge, title,
 * description, and a Download link in --wp--preset--color--primary. ACF block output
 * drops into the same markup: title + description + detected type. */
.sg-block-docs {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--wp--style--block-gap);
}
.sg-block-doc {
  background: var(--wp--preset--color--panel, #ffffff);
  border-radius: var(--wp--custom--radius--default);
  padding: var(--wp--preset--spacing--30);
  box-shadow: var(--wp--custom--elevation--card, var(--wp--preset--shadow--md));
  display: flex;
  flex-direction: column;
  gap: var(--wp--style--block-gap);
  align-items: flex-start;
  transition: box-shadow 0.15s ease, transform 0.15s ease;
}
.sg-block-doc:hover {
  box-shadow: var(--wp--preset--shadow--lg);
  transform: translateY(-2px);
}
.sg-block-doc-type {
  display: inline-flex;
  align-items: center;
  gap: 0.5em;
  padding-bottom: var(--wp--preset--spacing--20);
  border-bottom: 1px solid var(--wp--custom--gray--40);
  color: var(--wp--preset--color--ink-soft);
  font-family: var(--wp--preset--font-family--body);
  font-size: var(--wp--preset--font-size--x-small);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  line-height: 1;
}
.sg-block-doc-type svg {
  width: 1.4em;
  height: 1.4em;
  flex-shrink: 0;
  fill: var(--wp--preset--color--primary);
}
.sg-block-doc-body {
  display: flex;
  flex-direction: column;
  gap: var(--wp--preset--spacing--10);
  flex: 1;
}
.sg-block-doc-link {
  color: var(--wp--preset--color--cta);
  font-weight: 700;
  text-decoration: none;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  font-size: var(--wp--preset--font-size--x-small);
  display: inline-flex;
  align-items: center;
  gap: var(--wp--preset--spacing--10);
}
.sg-block-doc-link:hover { text-decoration: underline; }
.sg-block-doc-link-arrow {
  display: inline-block;
  transition: transform 0.15s ease;
}
.sg-block-doc-link:hover .sg-block-doc-link-arrow {
  transform: translateY(2px);
}
@media (max-width: 900px) {
  .sg-block-docs { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 560px) {
  .sg-block-docs { grid-template-columns: 1fr; }
}

/* Hero: Full Image — alignfull, 70vh background image with soft gradient
 * fade to the body background. Centered H1 with a bold emphasis phrase,
 * and a content card that hangs over the bottom edge to invite scrolling. */
.sg-block-hero-full {
  position: relative;
  height: 70vh;
  min-height: 480px;
  overflow: visible;
  /* Reserve vertical space below the hero for the translated card. The card
   * inside uses `transform: translateY(spacing--50)` to float over the next
   * section — transforms don't affect layout, so without this margin the
   * next block would sit flush against the hero's natural bottom and the
   * card would overlap it. spacing--80 covers the 50px translate plus
   * breathing room for typical card content (headline + button). */
  margin-bottom: var(--wp--preset--spacing--80);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
}
.sg-block-hero-full-bg {
  position: absolute;
  inset: 0;
  background-size: cover;
  background-position: center;
  z-index: 0;
  overflow: hidden;
}
.sg-block-hero-full-video {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
.sg-block-hero-full-gradient {
  position: absolute;
  inset: 0;
  z-index: 1;
  /* Default gradient target = page background. Override via .sg-hero-grad-*
   * classes below, which are applied to the block root by render.php when
   * the "Bottom Gradient Color" ACF field is set to a palette slug. Using
   * a CSS variable keeps the 4-stop easing formula untouched. */
  background: linear-gradient(
    to bottom,
    transparent 30%,
    color-mix(in srgb, var(--hero-grad, var(--wp--preset--color--canvas)) 40%, transparent) 55%,
    color-mix(in srgb, var(--hero-grad, var(--wp--preset--color--canvas)) 80%, transparent) 75%,
    var(--hero-grad, var(--wp--preset--color--canvas)) 100%
  );
}
/* Palette-slug gradient overrides. Each class also sets --hero-grad-opposite
 * so descendant text (the headline) can auto-flip to a readable color on
 * dark gradient choices. */
.sg-hero-grad-cta           { --hero-grad: var(--wp--preset--color--cta);           --hero-grad-opposite: var(--wp--custom--color--cta-opposite); }
.sg-hero-grad-cta-alt       { --hero-grad: var(--wp--preset--color--cta-alt);       --hero-grad-opposite: var(--wp--custom--color--cta-alt-opposite); }
.sg-hero-grad-primary       { --hero-grad: var(--wp--preset--color--primary);       --hero-grad-opposite: var(--wp--custom--color--primary-opposite); }
.sg-hero-grad-primary-alt   { --hero-grad: var(--wp--preset--color--primary-alt);   --hero-grad-opposite: var(--wp--custom--color--primary-alt-opposite); }
.sg-hero-grad-secondary     { --hero-grad: var(--wp--preset--color--secondary);     --hero-grad-opposite: var(--wp--custom--color--secondary-opposite); }
.sg-hero-grad-secondary-alt { --hero-grad: var(--wp--preset--color--secondary-alt); --hero-grad-opposite: var(--wp--custom--color--secondary-alt-opposite); }
.sg-hero-grad-tertiary      { --hero-grad: var(--wp--preset--color--tertiary);      --hero-grad-opposite: var(--wp--custom--color--tertiary-opposite); }
.sg-hero-grad-tertiary-alt  { --hero-grad: var(--wp--preset--color--tertiary-alt);  --hero-grad-opposite: var(--wp--custom--color--tertiary-alt-opposite); }
.sg-hero-grad-canvas        { --hero-grad: var(--wp--preset--color--canvas);        --hero-grad-opposite: var(--wp--custom--color--canvas-opposite); }
.sg-hero-grad-panel         { --hero-grad: var(--wp--preset--color--panel);         --hero-grad-opposite: var(--wp--custom--color--panel-opposite); }
.sg-hero-grad-ink           { --hero-grad: var(--wp--preset--color--ink);           --hero-grad-opposite: var(--wp--custom--color--ink-opposite); }
.sg-hero-grad-ink-soft      { --hero-grad: var(--wp--preset--color--ink-soft);      --hero-grad-opposite: var(--wp--custom--color--ink-soft-opposite); }
/* When a gradient color is explicitly chosen, recolor the headline to the
 * gradient's paired opposite. The headline sits in the bottom half of the
 * hero (margin-top: auto) where the gradient is strongest, so a dark
 * gradient + default dark headline becomes unreadable without this. */
[class*="sg-hero-grad-"] .sg-block-hero-full-headline h1 {
  color: var(--hero-grad-opposite, var(--wp--preset--color--ink));
}
.sg-block-hero-full-headline {
  position: relative;
  z-index: 2;
  max-width: 900px;
  text-align: center;
  padding: 0 var(--wp--preset--spacing--30);
  margin-top: auto;
  margin-bottom: var(--wp--preset--spacing--30);
}
.sg-block-hero-full-headline h1 {
  color: var(--wp--preset--color--ink);
  margin: 0;
}
.sg-block-hero-full-headline h1 strong {
  font-weight: inherit;
  color: var(--wp--preset--color--cta);
}
.sg-block-hero-full-card {
  position: relative;
  z-index: 2;
  /* Default max-width is set by the card-width variant class below; this
   * keeps the hardcoded fallback in case neither class is present. */
  max-width: var(--wp--custom--layout--narrow-size, 800px);
  width: 100%;
  background: var(--wp--preset--color--panel, #ffffff);
  border-radius: var(--wp--custom--radius--default);
  box-shadow: var(--wp--preset--shadow--xl);
  padding: var(--wp--preset--spacing--40);
  transform: translateY(var(--wp--preset--spacing--50));
  display: flex;
  flex-direction: column;
  gap: var(--wp--style--block-gap);
  text-align: center;
}
.sg-hero-card-narrow  { max-width: var(--wp--custom--layout--narrow-size, 800px); }
.sg-hero-card-content { max-width: var(--wp--style--global--content-size, 1200px); }
@media (max-width: 768px) {
  .sg-block-hero-full {
    height: 60vh;
    min-height: 400px;
  }
  .sg-block-hero-full-headline {
    padding: 0 var(--wp--preset--spacing--20);
  }
  .sg-block-hero-full-card {
    margin-left: var(--wp--preset--spacing--20);
    margin-right: var(--wp--preset--spacing--20);
    padding: var(--wp--preset--spacing--30);
  }
}

/* Full-width hero — alignfull, 50/50 image (left) and content (right).
 * Image side has a brand backdrop so transparent product PNGs read clearly. */
.sg-block-hero-bg {
  display: grid;
  grid-template-columns: 1fr 1fr;
  align-items: stretch;
  min-height: 100vh;
}
/* Side-toggle swap: DOM is media → content. Default matches field 'left'.
 * `.image-right` flips column placement so the image moves to col 2.
 * `grid-row: 1` on both prevents auto-placement from dropping the second
 * child to row 2 when it wants an "earlier" column than the first. */
.sg-block-hero-bg.image-right .sg-block-hero-bg-media {
  grid-column: 2;
  grid-row: 1;
}
.sg-block-hero-bg.image-right .sg-block-hero-bg-content {
  grid-column: 1;
  grid-row: 1;
}
/* Block in markup carries .has-secondary-gradient-background for the brand
 * gradient behind the hero image (secondary → secondary-alt). */
.sg-block-hero-bg-media {
  width: 100%;
  height: 100%;
  padding: var(--wp--preset--spacing--30);
  display: flex;
}
.sg-block-hero-bg-image,
.sg-block-hero-bg-video {
  width: 100%;
  height: 100%;
  object-fit: cover;
  box-shadow: var(--wp--preset--shadow--lg);
  display: block;
}
/* Media-spacing OFF — media fills its half edge-to-edge, no drop shadow. */
.sg-block-hero-bg.no-media-spacing .sg-block-hero-bg-media {
  padding: 0;
}
.sg-block-hero-bg.no-media-spacing .sg-block-hero-bg-image,
.sg-block-hero-bg.no-media-spacing .sg-block-hero-bg-video {
  box-shadow: none;
}
/* Dynamic height — block sizes to its content. Drop the 100vh floor and give
 * the content section top/bottom padding so it doesn't feel cramped. Horizontal
 * padding stays at spacing--60 from the base rule. */
.sg-block-hero-bg.dynamic-height {
  min-height: 0;
}
.sg-block-hero-bg.dynamic-height .sg-block-hero-bg-content {
  padding-top: var(--wp--preset--spacing--40);
  padding-bottom: var(--wp--preset--spacing--40);
}
/* Without this, the image/video's intrinsic size would set the grid row's
 * min-height and push it taller than the content side. `min-height: 0` lets the
 * media cell shrink to 0 min-content so the content cell drives the row; the
 * grid's default align-items: stretch then stretches the media back to match,
 * and object-fit: cover on the <img>/<video> crops cleanly. */
.sg-block-hero-bg.dynamic-height .sg-block-hero-bg-media {
  min-height: 0;
  overflow: hidden;
}
.sg-block-hero-bg-content {
  padding: var(--wp--preset--spacing--60);
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  justify-content: center;
  gap: var(--wp--style--block-gap);
}
@media (max-width: 768px) {
  .sg-block-hero-bg {
    grid-template-columns: 1fr;
    min-height: 0;
  }
  .sg-block-hero-bg-media {
    aspect-ratio: 16 / 9;
    height: auto;
  }
  .sg-block-hero-bg-content {
    padding: var(--wp--preset--spacing--40) var(--wp--preset--spacing--20);
  }
  /* Reset the desktop .image-right swap — on a 1fr grid the explicit
   * column 2 would become an implicit track and break stacking. Also reset
   * grid-row so mobile stacks in natural DOM order. */
  .sg-block-hero-bg.image-right .sg-block-hero-bg-media,
  .sg-block-hero-bg.image-right .sg-block-hero-bg-content {
    grid-column: auto;
    grid-row: auto;
  }
}

/* Image with content overlay — full-bleed block with a 65vw image and a content
 * card that overlaps onto it. Toggle with .content-left to swap sides. The
 * content card sits 30px inside the block's top/bottom edges so the image is
 * visible behind it, showcasing the overlay effect. */
.sg-block-overlay {
  display: grid;
  grid-template-columns: 65vw 1fr;
  align-items: center;
}
.sg-block-overlay-image {
  grid-column: 1;
  grid-row: 1;
  width: 100%;
  aspect-ratio: 16 / 9;
  object-fit: cover;
  display: block;
}
/* Default white bg is wrapped in :where() (0-specificity) so WP's .has-*-background-color
 * / .has-*-gradient-background helpers (specificity 0,1,0) can override when the admin
 * picks a bg color or gradient from the native picker. */
:where(.sg-block-overlay-content) {
  background: var(--wp--preset--color--panel, #ffffff);
}
.sg-block-overlay-content {
  grid-column: 2;
  grid-row: 1;
  border-radius: var(--wp--custom--radius--default);
  box-shadow: var(--wp--preset--shadow--xl);
  padding: var(--wp--preset--spacing--40);
  margin: var(--wp--preset--spacing--50) var(--wp--preset--spacing--50) var(--wp--preset--spacing--50) calc(var(--wp--preset--spacing--60) * -1);
  align-self: center;
  display: flex;
  flex-direction: column;
  gap: var(--wp--style--block-gap);
  z-index: 1;
}

/* Toggle: content on the left, image on the right */
.sg-block-overlay.content-left {
  grid-template-columns: 1fr 65vw;
}
.sg-block-overlay.content-left .sg-block-overlay-image {
  grid-column: 2;
}
.sg-block-overlay.content-left .sg-block-overlay-content {
  grid-column: 1;
  margin: var(--wp--preset--spacing--50) calc(var(--wp--preset--spacing--60) * -1) var(--wp--preset--spacing--50) var(--wp--preset--spacing--50);
}

/* Mobile: stack the image on top, content below (no overlap) */
@media (max-width: 768px) {
  .sg-block-overlay,
  .sg-block-overlay.content-left {
    grid-template-columns: 1fr;
  }
  .sg-block-overlay-image,
  .sg-block-overlay.content-left .sg-block-overlay-image {
    grid-column: 1;
    grid-row: 1;
  }
  .sg-block-overlay-content,
  .sg-block-overlay.content-left .sg-block-overlay-content {
    grid-column: 1;
    grid-row: 2;
    margin: calc(var(--wp--preset--spacing--30) * -1) var(--wp--preset--spacing--20) var(--wp--preset--spacing--30);
    padding: var(--wp--preset--spacing--30);
  }
}

/* Center content — headline + copy + CTA in a colored block, with a full-width
 * image hanging over the top edge. Designed to be used with .alignwide so the
 * block breaks out to wide-size while its inner content stays at content-size. */
.sg-block-center-content {
  position: relative;
  background: var(--wp--preset--color--secondary);
  color: var(--wp--custom--color--secondary-opposite);
  border-radius: var(--wp--custom--radius--default);
  padding: var(--wp--preset--spacing--30) var(--wp--preset--spacing--50) var(--wp--preset--spacing--80);
  margin-top: var(--wp--preset--spacing--80);
  text-align: center;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--wp--preset--spacing--50);
}
/* Texture surface — absolutely fills the panel so the texture's required
 * overflow:hidden lives here, not on the parent. The parent stays
 * overflow:visible so .sg-block-center-content-image can overhang above. */
/* Higher specificity than .has-bg-texture so the absolute positioning
 * isn't overridden by the helper's `position: relative`. */
.sg-block-center-content > .sg-block-center-content-bg {
  position: absolute;
  inset: 0;
  border-radius: inherit;
  pointer-events: none;
}
/* Lift content above the texture layer (which sits at the bottom of the
 * parent's stacking context). */
.sg-block-center-content > .sg-block-center-content-image,
.sg-block-center-content > .sg-block-center-content-body {
  position: relative;
  z-index: 1;
}
.sg-block-center-content-body {
  max-width: var(--wp--custom--layout--narrow-size);
  margin-left: auto;
  margin-right: auto;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--wp--style--block-gap);
}
.sg-block-center-content-image {
  width: 100%;
  max-width: var(--wp--style--global--content-size);
  aspect-ratio: 16 / 9;
  background-size: cover;
  background-position: center;
  background-color: var(--wp--custom--gray--40);
  border-radius: calc(var(--wp--custom--radius--default) * 0.5);
  margin-top: calc((var(--wp--preset--spacing--80) + var(--wp--preset--spacing--30)) * -1);
  box-shadow: var(--wp--preset--shadow--lg);
}
.sg-block-center-content-body > p {
  max-width: 52ch;
}

/* Alignment demo rows. These are direct children of .is-layout-constrained
 * (the <main>) so the alignment classes actually break out of the chrome. */
.sg-align-row {
  background: var(--wp--preset--color--secondary);
  color: var(--wp--custom--color--secondary-opposite);
  padding: var(--wp--preset--spacing--20);
  border-radius: var(--wp--custom--radius--default);
  text-align: center;
  font-weight: 600;
  margin-top: var(--wp--preset--spacing--10);
}
/* alignfull rows go to the viewport edge, so square off the corners */
.sg-align-row.alignfull {
  border-radius: 0;
}
.sg-align-caption {
  margin-top: var(--wp--preset--spacing--30);
  color: var(--wp--preset--color--ink);
  text-align: center;
}
.sg-align-caption code {
  font-family: ui-monospace, monospace;
  background: rgba(0, 0, 0, 0.06);
  padding: 2px 6px;
  border-radius: 4px;
  margin: 0 2px;
}

/* Icons */
.sg-icon-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(96px, 1fr));
  gap: 12px;
}

.sg-icon-tile {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 8px;
  padding: 18px 8px;
  border: 1px solid var(--sg-border);
  border-radius: 10px;
  background: #FFF;
  text-align: center;
}

.sg-icon-tile-glyph {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 28px;
  height: 28px;
  font-size: 28px;
  line-height: 1;
  color: var(--sg-text);
}

.sg-icon-tile-name {
  font-size: 10px;
  color: var(--sg-text-muted);
  word-break: break-all;
  line-height: 1.3;
}

/* Forms */
.sg-form-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 24px;
}

.sg-field { display: flex; flex-direction: column; gap: 6px; }
.sg-field label { font-size: 13px; font-weight: 500; }

/* Spacing */
.sg-spacing-row {
  display: flex;
  align-items: center;
  gap: 16px;
  padding: 8px 0;
}
.sg-spacing-bar { background: var(--sg-accent); height: 14px; border-radius: 4px; }
.sg-spacing-label { font-size: 12px; color: var(--sg-text-muted); min-width: 80px; }

/* Shadows */
.sg-shadow-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)); gap: 24px; }
.sg-shadow-tile {
  height: 110px;
  background: #FFF;
  border-radius: var(--sg-radius);
  display: flex;
  align-items: flex-end;
  justify-content: center;
  padding: 12px;
  font-size: 12px;
  color: var(--sg-text-muted);
}

/* The preview area where client tokens are scoped. */
.sg-preview {
  /* Client CSS variables get injected here via inline <style> on .sg-preview */
}

/* Landing page (client list) */
.sg-landing-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
  gap: 16px;
  margin-top: 32px;
}
.sg-landing-card {
  display: block;
  padding: 24px;
  border: 1px solid var(--sg-border);
  border-radius: var(--sg-radius);
  text-decoration: none;
  color: inherit;
  transition: border-color 0.15s ease, transform 0.15s ease;
}
.sg-landing-card:hover { border-color: var(--sg-accent); transform: translateY(-1px); }
.sg-landing-card h3 { margin: 0 0 4px; font-size: 18px; }
.sg-landing-card p { margin: 0; color: var(--sg-text-muted); font-size: 13px; }

/* ==================================================================
 * CSS Inspector — inline editor activated by CMD+OPTION+click.
 * ================================================================== */
.inspector-active { cursor: crosshair !important; }
.inspector-active *:hover {
  outline: 2px dashed rgba(59, 130, 246, 0.6) !important;
  outline-offset: 2px;
}
.inspector-selected {
  outline: 2px solid #3B82F6 !important;
  outline-offset: 2px;
}
.sg-inspector-panel {
  position: fixed;
  top: 0;
  right: 0;
  width: 380px;
  height: 100vh;
  z-index: 10000;
  background: var(--sg-bg);
  border-left: 1px solid var(--sg-border);
  box-shadow: -8px 0 24px rgba(0, 0, 0, 0.08);
  display: flex;
  flex-direction: column;
  font-family: var(--sg-font);
  font-size: 13px;
  color: var(--sg-text);
  animation: sg-inspector-slide-in 0.2s ease-out;
}
@keyframes sg-inspector-slide-in {
  from { transform: translateX(100%); }
  to   { transform: translateX(0); }
}
.sg-inspector-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 16px 20px;
  border-bottom: 1px solid var(--sg-border);
  flex-shrink: 0;
}
.sg-inspector-title { font-size: 15px; font-weight: 600; margin-bottom: 4px; }
.sg-inspector-element {
  font-size: 11px;
  color: var(--sg-text-muted);
  background: var(--sg-surface);
  padding: 2px 6px;
  border-radius: 4px;
}
.sg-inspector-close {
  background: none;
  border: none;
  font-size: 24px;
  cursor: pointer;
  color: var(--sg-text-muted);
  padding: 0 4px;
  line-height: 1;
}
.sg-inspector-close:hover { color: var(--sg-text); }
.sg-inspector-tabs {
  display: flex;
  border-bottom: 1px solid var(--sg-border);
  flex-shrink: 0;
}
.sg-inspector-tab {
  flex: 1;
  padding: 10px 16px;
  font-family: var(--sg-font);
  font-size: 12px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  background: none;
  border: none;
  border-bottom: 2px solid transparent;
  color: var(--sg-text-muted);
  cursor: pointer;
  transition: color 0.15s, border-color 0.15s;
}
.sg-inspector-tab:hover { color: var(--sg-text); }
.sg-inspector-tab.active {
  color: var(--sg-text);
  border-bottom-color: #3B82F6;
}
.sg-inspector-tab-count {
  font-size: 10px;
  font-weight: 400;
  opacity: 0.6;
  margin-left: 4px;
}
.sg-inspector-body { flex: 1; overflow-y: auto; padding: 16px 20px; }
.sg-inspector-empty { color: var(--sg-text-muted); text-align: center; padding: 40px 0; }
.sg-inspector-group { margin-bottom: 20px; }
.sg-inspector-group-label {
  font-size: 10px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--sg-text-muted);
  margin-bottom: 8px;
  padding-bottom: 4px;
  border-bottom: 1px solid var(--sg-border);
}
.sg-inspector-row { margin-bottom: 10px; }
.sg-inspector-row-label {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  margin-bottom: 4px;
}
.sg-inspector-row-name { font-weight: 500; font-size: 12px; }
.sg-inspector-row-var {
  font-size: 10px;
  color: var(--sg-text-muted);
  font-family: ui-monospace, monospace;
}
.sg-inspector-row-input { display: flex; }
.sg-inspector-text {
  width: 100%;
  font-family: ui-monospace, monospace;
  font-size: 12px;
  padding: 5px 8px;
  border: 1px solid var(--sg-border);
  border-radius: 4px;
  background: var(--sg-surface);
  color: var(--sg-text);
}
.sg-inspector-text:focus {
  outline: none;
  border-color: #3B82F6;
  box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.15);
}
.sg-inspector-readonly { opacity: 0.55; cursor: default; }
.sg-inspector-color-wrap { display: flex; gap: 6px; width: 100%; }
.sg-inspector-color-picker {
  width: 32px;
  height: 32px;
  padding: 0;
  border: 1px solid var(--sg-border);
  border-radius: 4px;
  cursor: pointer;
  flex-shrink: 0;
}
.sg-inspector-color-wrap .sg-inspector-text { flex: 1; }
.sg-inspector-footer {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 12px;
  padding: 12px 20px;
  border-top: 1px solid var(--sg-border);
  flex-shrink: 0;
}
.sg-inspector-save-btn {
  font-family: var(--sg-font);
  font-size: 13px;
  font-weight: 500;
  padding: 6px 16px;
  border: none;
  border-radius: 6px;
  background: var(--sg-accent);
  color: #fff;
  cursor: pointer;
}
.sg-inspector-save-btn:disabled { opacity: 0.4; cursor: default; }
.sg-inspector-save-btn:not(:disabled):hover { opacity: 0.85; }
.sg-inspector-saved { color: #16a34a; font-weight: 500; }
.sg-inspector-saving { color: var(--sg-text-muted); }
.sg-inspector-error { color: #dc2626; font-size: 12px; }

/* ==================================================================
 * Image wall — infinite-scroll gallery of alternating squares and
 * wide rectangles. Two rows scroll right-to-left on a loop. Hover
 * pauses the animation and lifts the hovered image.
 * ================================================================== */
@keyframes sg-wall-scroll {
  from { transform: translateX(0); }
  to { transform: translateX(-50%); }
}
.sg-block-image-wall {
  display: flex;
  flex-direction: column;
  gap: var(--wp--style--block-gap);
  overflow: hidden;
  /* Extra vertical padding so the 10% hover zoom + shadow aren't clipped
   * by the outer overflow:hidden (which must stay for the scroll illusion). */
  padding: 40px 0;
}
/* Speed scales with image count. The ACF block sets --wall-count-top and
 * --wall-count-bottom on .sg-block-image-wall based on how many images
 * were uploaded. Per-item rates (7s top, 6s bottom) are calibrated so
 * 12 images ≈ 85s / 72s respectively. */
.sg-block-image-wall-strip {
  display: flex;
  gap: var(--wp--style--block-gap);
  width: max-content;
  animation: sg-wall-scroll var(--wall-speed) linear infinite;
}
.sg-block-image-wall-strip:nth-child(1) {
  --wall-speed: calc(var(--wall-count-top, 12) * 7s);
}
.sg-block-image-wall-strip:nth-child(2) {
  --wall-speed: calc(var(--wall-count-bottom, 12) * 6s);
  animation-direction: reverse;
}
.sg-block-image-wall-strip:hover {
  animation-play-state: paused;
}
.sg-block-image-wall-item {
  --wall-size: 280px;
  width: var(--wall-size);
  height: var(--wall-size);
  flex-shrink: 0;
  background-size: cover;
  background-position: center;
  background-color: var(--wp--custom--gray--40);
  border-radius: calc(var(--wp--custom--radius--default) * 0.5);
  transition: transform 0.3s ease, box-shadow 0.3s ease, opacity 0.3s ease;
}
.sg-block-image-wall-item.wide {
  width: calc(var(--wall-size) * 2 + var(--wp--preset--spacing--20));
}
/* Dim siblings, not the hovered item — uses :has() so the strip only
 * dims when an item inside it is hovered. */
.sg-block-image-wall-strip:has(.sg-block-image-wall-item:hover) .sg-block-image-wall-item:not(:hover) {
  opacity: 0.65;
}
.sg-block-image-wall-item:hover {
  opacity: 1;
  transform: scale(1.1) rotate(2deg);
  box-shadow: var(--wp--preset--shadow--xl);
  z-index: 1;
}
@media (max-width: 768px) {
  .sg-block-image-wall-item { --wall-size: 180px; }
}

/* ==================================================================
 * Background-texture overlays.
 * Add one of these classes to any element that already has a background
 * color or gradient. A large ::before sits BELOW the element's content
 * (z-index -1 inside a fresh stacking context) but ABOVE its background
 * paint, so the texture layers on top of the color.
 *
 * Variants (each reads its own CSS variable so a client can have several
 * textures live at once):
 *   .has-bg-texture        — uses --bg-texture, rotated by --bg-texture-angle
 *                            (default 45deg)
 *   .has-bg-texture-sand   — uses --bg-texture-sand, no rotation
 *
 * Common knobs (apply to both variants):
 *   --bg-texture-size  background-size (default: 50% auto)
 *
 * Set the URL variables on a client's .sg-preview in customCSS, e.g.
 *   .sg-preview {
 *     --bg-texture: url('/clients/foo/wood.png');
 *     --bg-texture-sand: url('/clients/foo/sand.png');
 *   }
 * Without the matching variable the pseudo renders empty and is invisible.
 * ================================================================== */
.has-bg-texture,
.has-bg-texture-sand {
  position: relative;
  isolation: isolate;
  overflow: hidden;
}
.has-bg-texture::before,
.has-bg-texture-sand::before {
  content: '';
  position: absolute;
  top: 50%;
  left: 50%;
  /* Square sized off the larger viewport axis so it always fully covers
   * the parent after rotation, regardless of the parent's aspect ratio.
   * (A simple width/height in % can leave gaps when a wide-and-short box
   * is rotated 45°.) */
  width: 200vmax;
  height: 200vmax;
  background-size: var(--bg-texture-size, 50% auto);
  background-repeat: repeat;
  pointer-events: none;
  z-index: -1;
}
.has-bg-texture::before {
  background-image: var(--bg-texture, none);
  transform: translate(-50%, -50%) rotate(var(--bg-texture-angle, 45deg));
}
.has-bg-texture-sand::before {
  background-image: var(--bg-texture-sand, none);
  transform: translate(-50%, -50%);
}

/* ==================================================================
 * Team block.
 * Column count is driven by the local --team-columns CSS variable set
 * inline on .sg-team so an ACF field can override it on a per-block basis.
 * The card's inner DOM:
 *   <div class="sg-team-content">
 *     <p class="has-large-font-size">{Name}<br><span class="has-medium-font-size">{Title}</span></p>
 *     <p>{Bio}</p>
 *   </div>
 * ================================================================== */
.sg-team {
  --team-columns: 4;
  display: flex;
  flex-direction: column;
  gap: var(--wp--preset--spacing--50);
}
.sg-team-group {
  display: flex;
  flex-direction: column;
  gap: var(--wp--style--block-gap);
}
.sg-team-grid {
  display: grid;
  grid-template-columns: repeat(var(--team-columns), minmax(0, 1fr));
  gap: var(--wp--style--block-gap);
}
.sg-team-member {
  display: flex;
  flex-direction: column;
  background: var(--wp--preset--color--canvas);
  border-radius: var(--wp--custom--radius--default);
  box-shadow: var(--wp--custom--elevation--card, var(--wp--preset--shadow--md));
  overflow: hidden;
}
.sg-team-photo {
  aspect-ratio: 1 / 1;
  width: 100%;
  background-size: cover;
  background-position: center;
  background-color: var(--wp--custom--gray--40);
}
.sg-team-content {
  display: flex;
  flex-direction: column;
  gap: var(--wp--preset--spacing--10);
  padding: var(--wp--preset--spacing--20);
}
.sg-team-content p { margin: 0; }
.sg-team-content .has-large-font-size {
  font-weight: 700;
  line-height: 1.3;
}
.sg-team-content .has-medium-font-size {
  font-weight: 400;
  opacity: 0.7;
}

@media (max-width: 900px) {
  .sg-team-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 540px) {
  .sg-team-grid { grid-template-columns: 1fr; }
}

/* ==================================================================
 * Image portfolio gallery — 4-col CSS grid of 16:9 images with a title
 * in the bottom-right corner. Clicking an item opens a fixed lightbox
 * (rendered by components/style-guide/ImagePortfolioGallery.tsx). The
 * hover effect mirrors the image wall: scale(1.1) + slight tilt +
 * elevated shadow. Neighbors dim slightly so the hovered item reads as
 * the focal point.
 * ================================================================== */
.sg-block-portfolio {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: var(--wp--style--block-gap);
  /* Vertical breathing room for the hover scale + shadow. No horizontal
   * padding — sides align with the rest of the content column. */
  padding: calc(var(--wp--preset--spacing--20) + 20px) 0;
}
.sg-block-portfolio-item {
  position: relative;
  width: 100%;
  aspect-ratio: 16 / 9;
  background: var(--wp--custom--gray--40);
  border-radius: var(--wp--custom--radius--default);
  overflow: hidden;
  transition: transform 0.3s ease, box-shadow 0.3s ease, opacity 0.3s ease;
}
/* The button is the full-bleed click target; it carries no visual styling
 * of its own so the container's border-radius, hover scale, and shadow
 * govern the look. */
.sg-block-portfolio-item-btn {
  position: absolute;
  inset: 0;
  display: block;
  width: 100%;
  height: 100%;
  border: 0;
  padding: 0;
  margin: 0;
  background: transparent;
  cursor: pointer;
  font: inherit;
  color: inherit;
}
.sg-block-portfolio-item-image {
  position: absolute;
  inset: 0;
  background-size: cover;
  background-position: center;
  /* The image div follows the <button> in source order, so without this it
   * paints on top and swallows clicks — the lightbox would never open. */
  pointer-events: none;
}
/* Dim siblings of the hovered item for focus, same pattern as image wall. */
.sg-block-portfolio:has(.sg-block-portfolio-item:hover) .sg-block-portfolio-item:not(:hover) {
  opacity: 0.65;
}
.sg-block-portfolio-item:hover {
  opacity: 1;
  transform: scale(1.05) rotate(2deg);
  box-shadow: var(--wp--preset--shadow--xl);
  z-index: 1;
}
/* Alternate tilt direction on even items for a more natural look. */
.sg-block-portfolio-item:nth-child(even):hover {
  transform: scale(1.05) rotate(-2deg);
}
/* Title chip sits bottom-right, overlaying the image. A semi-transparent
 * --wp--preset--color--ink pill (65% opacity) keeps it readable on any photo. All
 * typography — font size, weight, line-height, letter-spacing — comes
 * from the paragraph's default styles (.has-medium-font-size on the
 * element sets the size). */
.sg-block-portfolio-item-title {
  position: absolute;
  right: var(--wp--preset--spacing--20);
  bottom: var(--wp--preset--spacing--20);
  margin: 0;
  max-width: calc(100% - var(--wp--preset--spacing--20) * 2);
  padding: var(--wp--preset--spacing--10) var(--wp--preset--spacing--20);
  background: color-mix(in srgb, var(--wp--preset--color--ink) 65%, transparent);
  color: var(--wp--preset--color--canvas);
  border-radius: var(--wp--custom--radius--default);
  pointer-events: none;
  backdrop-filter: blur(var(--wp--custom--bg--blur));
}
@media (max-width: 900px) {
  .sg-block-portfolio { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
/* On phones, landscape 16:9 tiles get too squat when two sit side-by-side.
 * Flip them to 3:4 portrait so each image gets meaningful height. */
@media (max-width: 600px) {
  .sg-block-portfolio-item { aspect-ratio: 3 / 4; }
}
/* Lightbox CSS lives further down under `.sg-portfolio-lightbox` — that class
 * is the one the JS-injected overlay uses. */

/* ==================================================================
 * Image tiles — 4-col grid of portrait 3:4 image cards, each a full-area
 * link. A vertical dark gradient (transparent → --wp--preset--color--ink) over the
 * image keeps the bottom-left blurb readable. Hover zooms the image
 * layer and upgrades the shadow from --wp--preset--shadow--md to --wp--preset--shadow--lg.
 * ================================================================== */
.sg-block-image-tiles {
  display: grid;
  grid-template-columns: repeat(var(--block-columns, 4), minmax(0, 1fr));
  gap: var(--wp--style--block-gap);
}
.sg-block-image-tile {
  position: relative;
  display: block;
  aspect-ratio: 3 / 4;
  border-radius: var(--wp--custom--radius--default);
  overflow: hidden;
  text-decoration: none;
  /* No default box-shadow — tile sits flat. Shadow is a hover affordance only;
   * see .sg-block-image-tile:hover below. Transition stays so the shadow eases in. */
  transition: box-shadow 0.3s ease;
}
/* Aspect variants chosen via ACF. Default is vertical (3:4); horizontal
 * flips to 4:3 for landscape-leaning imagery. */
.sg-block-image-tiles.is-aspect-horizontal .sg-block-image-tile {
  aspect-ratio: 4 / 3;
}
/* Desktop carousel — same scroll-snap pattern the mobile breakpoint uses,
 * but opt-in at any width via the ACF toggle. Scoped to >=601px so the
 * existing mobile-first carousel (70vw tiles) still wins below that; this
 * also avoids a specificity battle with the mobile rule. Each tile fills
 * 1 / columns of the container (minus gap), so N tiles fit exactly in
 * view and the rest scroll in. */
@media (min-width: 601px) {
  /* Desktop carousel breaks out to viewport width so tiles bleed past the
   * content column's right edge into the outer margin area. The negative
   * left margin pulls the flex container to the viewport's left edge; the
   * matching left padding keeps the FIRST tile aligned with the content
   * column's start (where the column would have begun naturally). The
   * `html { overflow-x: clip }` rule at the top of this file catches any
   * tile that extends past the viewport right edge — no body stretch. */
  .sg-block-image-tiles.is-desktop-carousel {
    display: flex;
    overflow-x: auto;
    scroll-snap-type: x mandatory;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
    width: 100vw;
    margin-left: calc((100% - 100vw) / 2);
    margin-right: calc((100% - 100vw) / 2);
    padding-left: calc((100vw - 100%) / 2);
    padding-right: 0;
  }
  .sg-block-image-tiles.is-desktop-carousel::-webkit-scrollbar { display: none; }
  /* Peek layout: tile width is sized so N full tiles + a 25% sliver of
   * the Nth+1 tile fit across the CONTENT COLUMN (not the full viewport).
   * Additional tiles after the peek remain visible in the right bleed
   * area, visually extending past the content column edge.
   *
   * Math: T*(N + 0.25) + G*N = content_width
   *   →   T = (content_width - G*N) / (N + 0.25)
   *
   * `min(100%, content-size)` handles viewport narrower than content-size:
   * fall back to the carousel's own width (100% = 100vw here). */
  .sg-block-image-tiles.is-desktop-carousel .sg-block-image-tile {
    flex: 0 0 calc(
      (min(var(--wp--style--global--content-size, 1200px), 100%) - var(--wp--style--block-gap) * var(--block-columns, 4))
      / (var(--block-columns, 4) + 0.25)
    );
    scroll-snap-align: start;
  }
}
.sg-block-image-tile-image {
  position: absolute;
  inset: 0;
  background-size: cover;
  background-position: center;
  transition: transform 0.5s ease;
}
.sg-block-image-tile-gradient {
  position: absolute;
  inset: 0;
  background: linear-gradient(
    to bottom,
    transparent 0%,
    transparent 40%,
    var(--wp--preset--color--ink) 100%
  );
  pointer-events: none;
}
.sg-block-image-tile-blurb {
  position: absolute;
  left: var(--wp--preset--spacing--20);
  bottom: var(--wp--preset--spacing--20);
  right: var(--wp--preset--spacing--20);
  margin: 0;
  color: var(--wp--preset--color--canvas);
  font-weight: 700;
}
.sg-block-image-tile:hover {
  box-shadow: var(--wp--preset--shadow--lg);
}
.sg-block-image-tile:hover .sg-block-image-tile-image {
  transform: scale(1.06);
}
@media (max-width: 900px) {
  .sg-block-image-tiles { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
/* On phones the grid collapses into a horizontal scroll-snap carousel.
 * Each tile is ~70vw so the next one peeks at the right edge, hinting
 * that the list scrolls. Scrollbar is hidden to keep it looking tactile. */
@media (max-width: 600px) {
  .sg-block-image-tiles {
    display: flex;
    overflow-x: auto;
    scroll-snap-type: x mandatory;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
  }
  .sg-block-image-tiles::-webkit-scrollbar { display: none; }
  .sg-block-image-tile {
    flex: 0 0 70vw;
    scroll-snap-align: center;
  }
}

/* ==================================================================
 * Mobile adjustments for multi-column block samples.
 * One consolidated block so phone-specific layout shifts are easy to
 * find and tune together.
 * ================================================================== */
@media (max-width: 600px) {
  /* Image columns — stack. */
  .sg-block-columns { grid-template-columns: 1fr; }

  /* Stats — 2-col grid with tighter padding. */
  .sg-block-stats {
    grid-template-columns: repeat(2, 1fr);
    padding: var(--wp--preset--spacing--30);
  }

  /* Feature grid — stack. */
  .sg-block-features { grid-template-columns: 1fr; }

  /* 50/50 split — image on top of the content section in a 16:9 frame.
   * Reset the desktop min-height so aspect-ratio governs the height. */
  .sg-block-split { grid-template-columns: 1fr; }
  .sg-block-split-image {
    aspect-ratio: 16 / 9;
    /* Explicit floor: when stacked, this is an empty grid item with only a
       background-image, and iOS Safari computes aspect-ratio height to 0 on
       it. The min-height guarantees the image is visible; aspect-ratio still
       governs where it's honored. */
    min-height: 220px;
  }
  .sg-block-split-body { padding: var(--wp--preset--spacing--30); }
  /* Reset the desktop .content-left swap — with grid-template-columns: 1fr
   * there's only one explicit column, so grid-column: 2 creates an implicit
   * track. Also reset grid-row so mobile stacks cleanly. */
  .sg-block-split.content-left .sg-block-split-image,
  .sg-block-split.content-left .sg-block-split-body {
    grid-column: auto;
    grid-row: auto;
  }

  /* Hero — stack with image on top, content below. DOM order is
   * content → image; use grid-row to put image first visually. Also reset
   * the .image-left desktop swap's grid-column/grid-row. */
  .sg-block-hero { grid-template-columns: 1fr; gap: var(--wp--preset--spacing--30); }
  .sg-block-hero-image { grid-row: 1; aspect-ratio: 16 / 9; }
  .sg-block-hero-content { grid-row: 2; }
  .sg-block-hero.image-left .sg-block-hero-content {
    grid-column: auto;
    grid-row: 2;
  }
  .sg-block-hero.image-left .sg-block-hero-image {
    grid-column: auto;
    grid-row: 1;
  }

  /* Logos row (grid mode) — collapses into a 2-column grid with
   * standard block gap. Scroller mode keeps its nowrap marquee on
   * mobile — a continuous strip is the whole point of the variant. */
  .sg-block-logos:not(.sg-block-logos--scroller) .sg-block-logos-row {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    gap: var(--wp--style--block-gap);
  }

  /* Testimonial cards — horizontal scroll-snap carousel. Each card ~70vw
   * so the next one peeks at the edge (mirrors Image tiles). */
  .sg-block-testimonial-grid {
    display: flex;
    overflow-x: auto;
    scroll-snap-type: x mandatory;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
  }
  .sg-block-testimonial-grid::-webkit-scrollbar { display: none; }
  .sg-block-testimonial-card {
    flex: 0 0 70vw;
    scroll-snap-align: center;
  }

  /* Icon feature row — 2×2 grid. Reset the desktop left-divider rule
   * and paint a grid: right border on the left column, bottom border
   * on the top row, no trailing borders on the right/bottom edges. */
  .sg-block-feature-row { grid-template-columns: repeat(2, 1fr); }
  .sg-block-feature-row-item:not(:first-child) { border-left: 0; }
  .sg-block-feature-row-item:nth-child(odd) {
    border-right: 1px solid var(--wp--custom--gray--40);
  }
  .sg-block-feature-row-item:nth-child(-n+2) {
    border-bottom: 1px solid var(--wp--custom--gray--40);
  }
  /* If the item count is odd, the last item would leave an empty
   * quadrant in the bottom-right. Span it across both columns so the
   * grid stays balanced. `:last-child:nth-child(odd)` fires only when
   * the last item sits in an odd position (total count is odd). */
  .sg-block-feature-row-item:last-child:nth-child(odd) {
    grid-column: 1 / -1;
    border-right: 0;
  }
}

/* ────────────────────────────────────────────────────────────────── */
/* Portfolio gallery lightbox — injected by assets/portfolio-lightbox.js.
 * Hidden until JS adds `.is-open` on the overlay root. Uses a pure-black
 * translucent backdrop; image is bounded to 90vw/90vh; keyboard-focusable
 * nav controls. Only applies on frontend pages that contain the block. */
.sg-portfolio-lightbox {
  position: fixed;
  inset: 0;
  z-index: 9999;
  display: none;
  align-items: center;
  justify-content: center;
}
.sg-portfolio-lightbox.is-open {
  display: flex;
}
.sg-portfolio-lightbox-backdrop {
  position: absolute;
  inset: 0;
  background: rgba(0, 0, 0, 0.88);
  cursor: zoom-out;
}
.sg-portfolio-lightbox-figure {
  position: relative;
  z-index: 1;
  margin: 0;
  max-width: 90vw;
  max-height: 90vh;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--wp--preset--spacing--20);
}
.sg-portfolio-lightbox-image {
  max-width: 100%;
  max-height: 80vh;
  width: auto;
  height: auto;
  object-fit: contain;
  box-shadow: 0 20px 60px rgba(0, 0, 0, 0.5);
  border-radius: var(--wp--custom--radius--default);
}
.sg-portfolio-lightbox-caption {
  color: #fff;
  font-size: var(--wp--preset--font-size--small);
  display: flex;
  gap: 1em;
  align-items: baseline;
}
.sg-portfolio-lightbox-counter {
  opacity: 0.6;
}
.sg-portfolio-lightbox-close,
.sg-portfolio-lightbox-prev,
.sg-portfolio-lightbox-next {
  position: absolute;
  z-index: 2;
  background: rgba(255, 255, 255, 0.08);
  border: 1px solid rgba(255, 255, 255, 0.2);
  color: #fff;
  cursor: pointer;
  font-size: 28px;
  line-height: 1;
  padding: 0;
  transition: background 120ms ease, border-color 120ms ease;
}
.sg-portfolio-lightbox-close:hover,
.sg-portfolio-lightbox-prev:hover,
.sg-portfolio-lightbox-next:hover {
  background: rgba(255, 255, 255, 0.2);
  border-color: rgba(255, 255, 255, 0.5);
}
.sg-portfolio-lightbox-close {
  top: 20px;
  right: 20px;
  width: 44px;
  height: 44px;
  border-radius: 50%;
  font-size: 22px;
}
.sg-portfolio-lightbox-prev,
.sg-portfolio-lightbox-next {
  top: 50%;
  transform: translateY(-50%);
  width: 56px;
  height: 56px;
  border-radius: 50%;
  font-size: 32px;
}
.sg-portfolio-lightbox-prev { left: 20px; }
.sg-portfolio-lightbox-next { right: 20px; }
@media (max-width: 640px) {
  .sg-portfolio-lightbox-prev,
  .sg-portfolio-lightbox-next {
    width: 40px;
    height: 40px;
    font-size: 24px;
  }
  .sg-portfolio-lightbox-close {
    top: 12px;
    right: 12px;
  }
}

/* ==================================================================
 * Header template part — sticky to the top of the viewport so the nav
 * stays reachable while scrolling. Targets the outer <header> wrapper
 * that WP emits when a template-part has tagName: header. z-index keeps
 * it above block content (hero images, etc.) as they scroll past.
 * ================================================================== */
header.wp-block-template-part {
  position: sticky;
  top: 0;
  z-index: 100;
}
/* Submenu dropdowns anchor to their <li>, which sits vertically centered
 * in the header row — so the default `top: 100%` opens the dropdown flush
 * against the link text, roughly at the header's bottom edge (because the
 * header's bottom padding = spacing--20 and the <li> ends 20px before the
 * header ends). We need two things:
 *   1. Clear daylight between the header and the dropdown.
 *   2. Predictable horizontal alignment under the link.
 *
 * Stretching the <li> to fill the nav's full height makes its bottom land
 * exactly at the nav's bottom (= header's bottom padding top). Then the
 * top offset on the submenu produces a visible gap below the header.
 *
 * The `>` combinator limits this to first-level submenus so any nested
 * flyouts keep their default offset. */
header.wp-block-template-part .wp-block-navigation__container {
  align-items: stretch;
}
header.wp-block-template-part .wp-block-navigation-item {
  display: flex;
  align-items: center;
}
header.wp-block-template-part
  .wp-block-navigation-item.has-child
  > .wp-block-navigation__submenu-container {
  top: calc(100% + var(--wp--preset--spacing--30));
}
/* Invisible hover bridge. The gap above creates a dead zone between the
 * link and the dropdown: once the cursor leaves the <li> on the way down,
 * :hover on the <li> drops and the submenu closes. A transparent pseudo
 * inside the <li> that spans the gap keeps the cursor "over" the <li> the
 * whole way across, so :hover stays live. Height matches the top offset
 * above so the bridge ends exactly where the submenu begins. */
header.wp-block-template-part
  .wp-block-navigation-item.has-child::after {
  content: "";
  position: absolute;
  top: 100%;
  left: 0;
  right: 0;
  height: calc(var(--wp--preset--spacing--20) + var(--wp--preset--spacing--30));
}

/* ==================================================================
 * Core Navigation block — mobile overlay.
 * WP's default open-menu overlay inherits the desktop nav's layout
 * (right-aligned in our header) and is cramped. This normalizes the
 * overlay so any child theme gets a usable mobile menu without per-site
 * CSS: equal padding, top-right close button, left-aligned link list with
 * divider borders, and a full-width CTA button below.
 * ================================================================== */
.wp-block-navigation__responsive-container.is-menu-open {
  padding: var(--wp--preset--spacing--30);
  /* Let the overlay scroll when the menu is taller than the viewport.
   * Combined with flex-column + margin-top:auto on the CTA below, this
   * pins the CTA to the viewport bottom on short menus and lets the
   * content scroll on long ones (CTA scrolls into view naturally). */
  overflow-y: auto;
  /* Don't set `position` here — WP defaults it to `fixed` (what makes it a
   * full-screen overlay). A fixed element is its own containing block for
   * absolutely-positioned descendants, so the close button still anchors
   * inside it correctly. */
}
/* Force the close button into the top-right corner. WP's default placement
 * depends on dialog dimensions and drifts when we change padding — pin it. */
.wp-block-navigation__responsive-container-close {
  position: absolute;
  top: var(--wp--preset--spacing--20);
  right: var(--wp--preset--spacing--20);
  padding: var(--wp--preset--spacing--10);
  z-index: 1;
}
.wp-block-navigation__responsive-container-open,
.wp-block-navigation__responsive-container-close {
  padding: var(--wp--preset--spacing--10);
}
.wp-block-navigation__responsive-container-open svg,
.wp-block-navigation__responsive-container-close svg {
  width: 28px;
  height: 28px;
}
/* Overlay content column — full width, left-aligned, with top space so the
 * first link doesn't collide with the close button. */
.wp-block-navigation__responsive-container.is-menu-open
  .wp-block-navigation__responsive-container-content {
  justify-content: flex-start;
  align-items: stretch;
  width: 100%;
  padding-top: var(--wp--preset--spacing--60);
}
/* Override is-content-justification-right coming from the desktop layout.
 * `!important` is regrettable but unavoidable — WP ships the justification
 * class with high specificity and a preset value. `flex-grow: 1` lets this
 * ul fill the overlay's available height so `margin-top: auto` on the CTA
 * below can push it to the bottom. */
.wp-block-navigation__responsive-container.is-menu-open
  .wp-block-navigation__container {
  display: flex;
  flex-direction: column;
  gap: 0;
  align-items: stretch;
  justify-content: flex-start !important;
  width: 100%;
  text-align: left;
  flex-grow: 1;
}
/* Every item in the overlay is a full-width row, left-aligned. */
.wp-block-navigation__responsive-container.is-menu-open .wp-block-navigation-item {
  width: 100%;
  text-align: left;
}
/* The divider border sits on the TOP-LEVEL label's content box (not on the
 * <li>), so it lands right under the label text rather than at the bottom
 * of the whole item (which on a submenu parent would be after all the sub
 * items). `>` combinators restrict this to direct children of the outer
 * <ul> so submenu items don't get divider lines. spacing--10 top/bottom
 * padding gives the 10px breathing room on each side of the line. */
.wp-block-navigation__responsive-container.is-menu-open
  .wp-block-navigation__container
  > .wp-block-navigation-item
  > .wp-block-navigation-item__content {
  padding-top: var(--wp--preset--spacing--10);
  padding-bottom: var(--wp--preset--spacing--10);
  border-bottom: var(--wp--custom--border--default-width) solid
    var(--wp--custom--gray--40);
}
.wp-block-navigation__responsive-container.is-menu-open
  .wp-block-navigation-item__content {
  display: block;
  width: 100%;
  padding-top: var(--wp--preset--spacing--10);
  padding-bottom: var(--wp--preset--spacing--10);
  font-size: var(--wp--preset--font-size--x-large);
}
/* Pin the CTA to the bottom of the overlay with `margin-top: auto`. When
 * the menu fits in the viewport, the CTA sits at the bottom with empty
 * space above it. When the menu overflows, `auto` resolves to 0 and the
 * CTA sits naturally after the last item — the overlay's overflow-y: auto
 * above takes over and the whole thing scrolls. */
.wp-block-navigation__responsive-container.is-menu-open .wp-block-buttons {
  margin-top: auto;
  padding-top: var(--wp--preset--spacing--30);
  width: 100%;
}
.wp-block-navigation__responsive-container.is-menu-open .wp-block-buttons .wp-block-button {
  width: 100%;
}
.wp-block-navigation__responsive-container.is-menu-open .wp-block-button__link {
  display: block;
  width: 100%;
  text-align: center;
}
/* Submenu items in the mobile overlay. The desktop header rules set the <li>
 * to `display: flex; align-items: center` so the stretched <li> vertically
 * centers its text — but in the overlay that same rule arranges the parent
 * label and its nested submenu <ul> sideways. Reset to block layout here,
 * then give the submenu <ul> its own vertical stack and strip the desktop
 * dropdown chrome (absolute positioning, shadow, white bg). Sub items get
 * padding-left so the nesting is visually obvious. */
.wp-block-navigation__responsive-container.is-menu-open .wp-block-navigation-item {
  display: block;
}
.wp-block-navigation__responsive-container.is-menu-open
  .wp-block-navigation__submenu-container {
  /* Force out of the desktop dropdown layout (absolute + right-anchored
   * via the inherited `justifyContent: right` from the parent nav) into
   * a plain full-width stacked list inside the overlay.
   *
   * !important is unavoidable here: our own header-scoped rule up in the
   * sticky-dropdown section sets `top: calc(100% + spacing--30)` at
   * higher specificity (header tag selector), which would otherwise keep
   * the submenu absolutely positioned and offscreen inside the overlay.
   * This is a mode switch, not a layered style, so `!important` is OK. */
  position: static !important;
  display: flex !important;
  flex-direction: column !important;
  align-items: stretch !important;
  justify-content: flex-start !important;
  width: 100% !important;
  min-width: 0 !important;
  padding: 0 !important;
  margin: 0 !important;
  top: auto !important;
  left: auto !important;
  right: auto !important;
  transform: none !important;
  background: transparent !important;
  box-shadow: none !important;
}
.wp-block-navigation__responsive-container.is-menu-open
  .wp-block-navigation__submenu-container
  .wp-block-navigation-item__content {
  padding-left: var(--wp--preset--spacing--30);
}

/* ==================================================================
 * Link Pods (.sg-block-link-pods)
 * Grid of clickable cards. Each pod is an <a>; the entire card is
 * clickable. Background color via standard `has-{slug}-background-color`
 * helper so opposite text colors flip automatically. Background image
 * (if set) sits behind the content.
 * ================================================================== */
.sg-block-link-pods {
  display: grid;
  grid-template-columns: repeat(var(--block-columns, 3), minmax(0, 1fr));
  gap: var(--wp--style--block-gap);
}
.sg-block-link-pod {
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  gap: var(--wp--preset--spacing--40);
  padding: var(--wp--preset--spacing--40);
  border-radius: var(--wp--custom--radius--default);
  text-decoration: none;
  color: inherit;
  background-size: cover;
  background-position: center;
  position: relative;
  overflow: hidden;
  transition: transform 0.15s ease, box-shadow 0.15s ease;
}
.sg-block-link-pod:hover,
.sg-block-link-pod:focus-visible {
  transform: translateY(-2px);
  box-shadow: var(--wp--preset--shadow--md);
}
/* When a pod has a background image, its solid bg color (if set) becomes a
 * darken/lighten overlay so text stays legible. Done with a pseudo overlay
 * so the underlying `has-{slug}-background-color` helper still wins for the
 * `has-{slug}-color` opposite-text-color pairing. */
.sg-block-link-pod.has-bg-image::before {
  content: "";
  position: absolute;
  inset: 0;
  background-color: inherit;
  opacity: 0.7;
  z-index: 0;
}
.sg-block-link-pod.has-bg-image:not(.has-background)::before {
  background-color: rgba(0, 0, 0, 0.4);
  opacity: 1;
}
.sg-block-link-pod-content,
.sg-block-link-pod-link {
  position: relative;
  z-index: 1;
}
.sg-block-link-pod-content {
  display: flex;
  flex-direction: column;
  gap: var(--wp--preset--spacing--10);
}
.sg-block-link-pod-title {
  margin: 0;
}
.sg-block-link-pod-description {
  font-size: var(--wp--preset--font-size--small, 0.95em);
}
.sg-block-link-pod-description :where(p):last-child {
  margin-bottom: 0;
}
.sg-block-link-pod-link {
  display: inline-flex;
  align-items: center;
  gap: 0.4em;
  font-weight: 600;
  letter-spacing: 0.02em;
}
.sg-block-link-pod:hover .sg-block-link-pod-link i,
.sg-block-link-pod:focus-visible .sg-block-link-pod-link i {
  transform: translateX(3px);
}
.sg-block-link-pod-link i {
  transition: transform 0.15s ease;
}

@media (max-width: 900px) {
  .sg-block-link-pods { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 600px) {
  .sg-block-link-pods { grid-template-columns: 1fr; }
}

/* ==================================================================
 * Highlighted Image Gallery (classic-city-core/highlighted-image-gallery).
 * Featured image above a thumbnail row. Click a thumbnail to make it
 * primary. Column count comes from the inline `--ccc-gallery-cols`
 * custom prop set on the block root. Border radius + block gap pull
 * from the site defaults so this looks native to whatever palette
 * + radius the active child theme ships.
 * ================================================================== */
.sg-block-highlighted-image-gallery {
  display: flex;
  flex-direction: column;
  gap: var(--wp--style--block-gap, 20px);
}
.sg-block-highlighted-image-gallery__primary {
  width: 100%;
  height: auto;
  display: block;
  border-radius: var(--wp--custom--radius--default, 0);
  object-fit: cover;
}
.sg-block-highlighted-image-gallery__thumbs {
  display: grid;
  grid-template-columns: repeat(var(--ccc-gallery-cols, 5), minmax(0, 1fr));
  gap: var(--wp--style--block-gap, 20px);
}
.sg-block-highlighted-image-gallery__thumb {
  appearance: none;
  background: none;
  border: 0;
  padding: 0;
  margin: 0;
  cursor: pointer;
  display: block;
  border-radius: var(--wp--custom--radius--default, 0);
  overflow: hidden;
  outline: 0;
  outline-offset: 2px;
}
.sg-block-highlighted-image-gallery__thumb img {
  width: 100%;
  height: 100%;
  aspect-ratio: 1 / 1;
  object-fit: cover;
  display: block;
  border-radius: var(--wp--custom--radius--default, 0);
}
.sg-block-highlighted-image-gallery__thumb.is-active,
.sg-block-highlighted-image-gallery__thumb:focus-visible {
  outline: 2px solid var(--wp--preset--color--primary, currentColor);
}

@media (max-width: 900px) {
  /* Step down to 3 columns on tablets so thumbnails stay tappable even
     when the admin configured a higher column count for desktop. */
  .sg-block-highlighted-image-gallery__thumbs {
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }
}
@media (max-width: 600px) {
  /* Two columns on phones. */
  .sg-block-highlighted-image-gallery__thumbs {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

/* ==================================================================
 * Image Links with Icons (classic-city-core/image-link-cards).
 *
 * Grid of clickable image cards. Each card: aspect-locked image on top,
 * colored footer bar (palette slug → has-{slug}-background-color → text
 * + icon auto-flip via the parent's palette pair-helpers) with
 * FontAwesome icon + title + right-arrow affordance.
 *
 * Column count comes from the block's `columns` field via the inline
 * `--block-columns` CSS var. Gap uses the WP core block-gap token so
 * spacing inherits theme.json defaults and tracks any per-child
 * override.
 *
 * Image aspect-ratio is locked at 1:1 so user-uploaded images of any
 * dimension fit consistently. `object-fit: cover` does the cropping.
 * ================================================================== */
.sg-block-image-link-cards {
  /* Block-wide vertical spacing. Head → grid gap inherits from the
     normal block-gap so it tracks theme.json. */
  display: flex;
  flex-direction: column;
  gap: var(--wp--preset--spacing--40, 2rem);
}

/* Head section uses the shared `.sg-block-head` rule (above) — emitted
   by the shared `ccc_render_block_head()` helper. Previously had
   per-block __head + __heading overrides here; both deduplicated. */

.sg-block-image-link-cards__grid {
  display: grid;
  grid-template-columns: repeat(var(--block-columns, 4), minmax(0, 1fr));
  gap: var(--wp--style--block-gap, 1.5rem);
}

/* Each card is the entire <a>. `color: inherit` lets the inner footer
   bar's `has-background` + palette pair-helpers control text color
   instead of the user-agent's default link color. `overflow: hidden`
   clips the image + bar to the corner radius. */
.sg-block-image-link-cards__card {
  display: flex;
  flex-direction: column;
  color: inherit;
  text-decoration: none;
  border-radius: var(--wp--custom--radius--default, 0);
  overflow: hidden;
  background-color: transparent;
  transition: transform 0.2s ease, box-shadow 0.2s ease;
}
.sg-block-image-link-cards__card:hover,
.sg-block-image-link-cards__card:focus-visible {
  /* Subtle lift on hover/focus — readable affordance without overdoing it. */
  transform: translateY(-2px);
  box-shadow: var(--wp--preset--shadow--md, 0 4px 10px rgba(0,0,0,0.12));
  outline: 0;
}

/* Square aspect image frame. `aspect-ratio` keeps cards uniform
   regardless of uploaded image dimensions. */
.sg-block-image-link-cards__image {
  aspect-ratio: 1 / 1;
  overflow: hidden;
  background-color: var(--wp--custom--gray--40, #e5e5e5);
}
.sg-block-image-link-cards__img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

/* Footer bar. `has-{slug}-background-color has-background` (added in
   render.php) applies the palette bg; the parent's palette pair-helper
   CSS in inc/enqueue.php sets text + icon color to the slug's opposite
   automatically. */
.sg-block-image-link-cards__bar {
  display: flex;
  align-items: center;
  gap: var(--wp--preset--spacing--20, 0.75rem);
  padding: var(--wp--preset--spacing--20, 0.75rem) var(--wp--preset--spacing--30, 1.25rem);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  line-height: 1.2;
  /* font-size scales with viewport; pin to a medium scale for the bar. */
  font-size: var(--wp--preset--font-size--medium, 1rem);
  min-height: 3.25rem;
}

.sg-block-image-link-cards__icon {
  flex: 0 0 auto;
  font-size: 1.25em;
  line-height: 1;
}

.sg-block-image-link-cards__title {
  /* Center the title between the leading icon and trailing arrow. */
  flex: 1 1 auto;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.sg-block-image-link-cards__arrow {
  flex: 0 0 auto;
  font-size: 1.1em;
  line-height: 1;
  /* Slide the arrow forward slightly on hover for a clearer "go" cue. */
  transition: transform 0.2s ease;
}
.sg-block-image-link-cards__card:hover .sg-block-image-link-cards__arrow,
.sg-block-image-link-cards__card:focus-visible .sg-block-image-link-cards__arrow {
  transform: translateX(3px);
}

/* Responsive column collapse. The inline `--block-columns` is the
   *desktop* target; smaller viewports step down to a reasonable count
   even when the admin asks for many columns. */
@media (max-width: 1024px) {
  .sg-block-image-link-cards__grid {
    /* Cap at 3 cols on tablets. min() picks the smaller of the
       admin choice and 3, so 1- and 2-col layouts stay as-is. */
    grid-template-columns: repeat(min(var(--block-columns, 4), 3), minmax(0, 1fr));
  }
}
@media (max-width: 700px) {
  .sg-block-image-link-cards__grid {
    grid-template-columns: repeat(min(var(--block-columns, 4), 2), minmax(0, 1fr));
  }
}
@media (max-width: 480px) {
  .sg-block-image-link-cards__grid {
    grid-template-columns: 1fr;
  }
}

/* ==================================================================
 * Image Card Grid.
 *
 * Grid of `.sg-image-card` partials. Block-level CSS handles layout
 * only — card markup + styling lives in the shared partial above.
 *
 * Mobile behavior is class-driven:
 *   - .is-mobile-stack — collapses to one column under the breakpoint.
 *   - .is-mobile-scroll — flex horizontal scroller with scroll-snap;
 *     each card flexes to ~85% viewport width so the next one peeks
 *     at the right edge.
 * ================================================================== */
.sg-block-image-card-grid {
  display: grid;
  grid-template-columns: repeat(var(--block-columns, 3), minmax(0, 1fr));
  gap: var(--wp--style--block-gap, var(--wp--preset--spacing--30, 1.5rem));
  align-items: stretch;
}

@media (max-width: 768px) {
  .sg-block-image-card-grid.is-mobile-stack {
    grid-template-columns: 1fr;
  }

  .sg-block-image-card-grid.is-mobile-scroll {
    /* Switch from grid to flex to enable horizontal scrolling. Each card
       gets a fixed flex-basis so the carousel behaves consistently across
       card counts. (No scroll-snap — free-scroll feels better here.) */
    display: flex;
    grid-template-columns: none;
    overflow-x: auto;
    /* Pull the scroll past the page edge a touch so the bottom-right
       scrollbar gutter sits below content rather than clipping it. */
    padding-bottom: var(--wp--preset--spacing--20, 1rem);
    /* Smooth, modern scrollbar — visible enough to signal scrollability
       without being heavy. */
    scrollbar-width: thin;
  }
  .sg-block-image-card-grid.is-mobile-scroll > * {
    flex: 0 0 85%;
  }
}

/* ==================================================================
 * Image Card (shared partial).
 *
 * Rendered by `ccc_render_image_card()` in inc/image-card.php. Used by
 * image-card-grid and any future block that needs the canonical
 * "image-on-top, body in the middle, optional footer at the bottom"
 * card style.
 *
 * Color treatment is HARDCODED via WP palette helper classes in
 * markup (has-canvas-background-color (was has-light-background-color)
 * on the card, has-secondary-color
 * on the tag, has-cta-color on the footer link). CSS handles layout,
 * spacing, dividers, and the link-arrow detail only.
 *
 * Footer-anchored-to-bottom contract: the card needs `height: 100%`
 * from its grid cell context (which image-card-grid's CSS provides
 * via `align-items: stretch` on the grid). `__body` then carries
 * `flex: 1` so it expands to push the footer block to the card's
 * bottom edge regardless of body length.
 * ================================================================== */
.sg-image-card {
  display: flex;
  flex-direction: column;
  height: 100%;
  overflow: hidden;
  border-radius: var(--wp--custom--radius--default, 0);
  box-shadow: var(--wp--custom--elevation--card);
  /* `--card-aspect-ratio` is set inline on the article by render.php
     so authors can vary the image aspect per-card or per-block. */
}

.sg-image-card__media {
  position: relative;
  aspect-ratio: var(--card-aspect-ratio, 16 / 9);
  overflow: hidden;
}

.sg-image-card__image {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

.sg-image-card__body {
  flex: 1 1 auto;
  display: flex;
  flex-direction: column;
  gap: var(--wp--preset--spacing--20, 1rem);
  padding: var(--wp--preset--spacing--30, 1.5rem);
}

/* Tag + title group: a flex column with a tight 10px gap. The title
   is now a <p> (not a heading), so the parent's eyebrow→heading gap-
   collapse rule no longer applies — we own the spacing here directly. */
.sg-image-card__heading-group {
  display: flex;
  flex-direction: column;
  gap: var(--wp--preset--spacing--10, 10px);
}

.sg-image-card__tag {
  /* is-style-eyebrow handles typography (uppercase, letter-spacing,
     small font-size); this rule owns the hue. Set as a semantic class
     here rather than a hardcoded `has-secondary-color` utility on the
     markup, so a child theme can recolor the tag with a normal
     selector — no !important war. A direct color on the tag also wins
     over the color the card's bg pair-helper would otherwise inherit
     down to it. */
  margin: 0;
  color: var(--wp--preset--color--secondary);
}
.sg-image-card__tag a {
  color: inherit;
  text-decoration: none;
}
.sg-image-card__tag a:hover {
  text-decoration: underline;
}

.sg-image-card__title {
  margin: 0;
  font-size: var(--wp--preset--font-size--large);
  font-weight: 700;
  line-height: 1.25;
}
.sg-image-card__title a {
  color: inherit;
  text-decoration: none;
}
.sg-image-card__title a:hover {
  text-decoration: underline;
}

.sg-image-card__description {
  margin: 0;
  opacity: 0.85;
}

.sg-image-card__divider {
  height: 1px;
  background-color: var(--wp--preset--color--canvas, rgba(0, 0, 0, 0.1));
  margin: 0 var(--wp--preset--spacing--30, 1.5rem);
}

.sg-image-card__footer {
  display: flex;
  flex-direction: column;
  gap: var(--wp--preset--spacing--20, 1rem);
  padding: var(--wp--preset--spacing--30, 1.5rem);
}

.sg-image-card__footer-meta {
  display: flex;
  flex-direction: column;
  gap: 0;
}

.sg-image-card__footer-title {
  margin: 0;
  font-weight: 700;
}

.sg-image-card__footer-subtitle {
  margin: 0;
  opacity: 0.7;
}

.sg-image-card__footer-link {
  margin: 0;
  text-transform: uppercase;
  font-weight: 700;
  font-size: var(--wp--preset--font-size--small, 0.875rem);
}
.sg-image-card__footer-link a {
  /* Hue owned here as a semantic class (cta), not a hardcoded
     `has-cta-color` utility on the markup — so children recolor with a
     normal selector instead of fighting WP's !important on the utility. */
  color: var(--wp--preset--color--cta);
  text-decoration: none;
  display: inline-flex;
  align-items: center;
  gap: 0.4em;
  transition: gap 0.15s ease;
}
.sg-image-card__footer-link a:hover {
  gap: 0.65em;
}
.sg-image-card__footer-link-arrow {
  display: inline-block;
  line-height: 1;
}

/* ==================================================================
 * Stackable.
 *
 * Position-sticky wrapper. Multiple Stackables placed back-to-back as
 * siblings in the same scroll container stick at the same top offset
 * and visually cover each other in DOM order as the user scrolls —
 * the later ones rise up and stack on top of the earlier ones.
 *
 * Offset is per-instance via the inline `--stackable-offset` CSS var
 * (set by render.php from the sticky_offset ACF field). Defaults to
 * spacing--40 below the viewport top. Authors bump it under taller
 * navbars by picking a larger spacing preset.
 *
 * NOTE: position: sticky only sticks within the bounds of its
 * scrolling ancestor (typically <html>/<body>). Stackables nested
 * inside a container with `overflow: hidden`, `overflow: auto`, or
 * `transform` won't stick to the viewport — they'll stick to that
 * container instead. If you find Stackables not sticking as expected,
 * check the containing chain for any such overflow / transform
 * ancestor.
 * ================================================================== */
/* Stackable supports two authoring patterns:
 *
 *  1. Multi-item (canonical / intuitive): ONE Stackable block with
 *     N children inside it. Each child sticks at the top offset and
 *     the next child covers it as the user scrolls. The wrapper
 *     itself does NOT stick — it just bounds the sticky range so all
 *     children release together when the wrapper scrolls past.
 *
 *  2. Single-item (legacy): N Stackable blocks back-to-back, each
 *     with one child inside. Each wrapper sticks at the offset and
 *     subsequent wrappers cover it in DOM order. This matches the
 *     pre-:has() rendering and is preserved so existing client sites
 *     keep working.
 *
 * We detect with :has(). The default rule (wrapper sticky) covers
 * legacy + browsers without :has() support — degrades to "no stacking
 * inside a multi-item stackable," which is benign. The @supports
 * block then overrides for modern browsers: if the wrapper has >1
 * authored child, sticky shifts off the wrapper and onto the children.
 *
 * Selector path: `> .acf-innerblocks-container > *` because ACF Pro
 * wraps InnerBlocks in `.acf-innerblocks-container` on the front end.
 * The container is `display: contents` (see the list near line 400),
 * so layout-wise the children appear as direct children of the
 * stackable, but selector-wise we still have to descend through the
 * actual DOM. */
.sg-block-stackable {
  position: sticky;
  top: var(--stackable-offset, var(--wp--preset--spacing--40));
  /* Flex column + block-gap so children inside a multi-item Stackable
     get the theme's default sibling spacing (same rhythm authors are
     used to from constrained-layout containers — see the "canonical
     flex-gap pattern" in this theme's CLAUDE.md). The
     `.acf-innerblocks-container` is `display: contents` (line ~400),
     so the authored children become real flex children of the wrapper
     and the gap rule applies to them directly. Single-item Stackables
     trivially have 0 internal gaps (no siblings). */
  display: flex;
  flex-direction: column;
  gap: var(--wp--style--block-gap);
}
@supports selector(:has(*)) {
  .sg-block-stackable:has(> .acf-innerblocks-container > :nth-child(2)) {
    position: static;
  }
  .sg-block-stackable:has(> .acf-innerblocks-container > :nth-child(2)) > .acf-innerblocks-container > * {
    position: sticky;
    top: var(--stackable-offset, var(--wp--preset--spacing--40));
  }
}

/* ==================================================================
 * Background Texture.
 *
 * Drop-in decorative block that projects a registered texture image
 * up, down, or both directions from its drop point. The block itself
 * has effectively no height (1px for editor selectability); a
 * `::before` pseudo carries the texture and is absolutely positioned.
 *
 * Stacking: pseudo lands at `z-index: -1` so the texture sits behind
 * sibling content in its stacking context.
 *
 * CRITICAL: the pseudo intentionally does NOT use `mask-image` or
 * `mix-blend-mode`. Both properties create implicit stacking contexts
 * (per the CSS spec). Combining either with `z-index: -1` broke the
 * negative stacking — the pseudo's local context floated up and the
 * texture disappeared entirely instead of just sitting behind
 * siblings. The fade-out at the far edge of the reach is the
 * responsibility of the texture image itself (PNG alpha channel),
 * not a CSS gradient mask.
 *
 * Reach: 50vh — viewport-relative so the texture scales naturally
 * across screen sizes without per-breakpoint overrides.
 *
 * Visibility model: the texture is visible where adjacent siblings
 * have no opaque background. Where a sibling has its own bg color,
 * the sibling paints over the texture in that overlap. That's the
 * direct consequence of "behind sibling content" — `z-index: -1`
 * means exactly that. If you want a texture that's visible through
 * opaque section bgs, design the bgs to be transparent on that
 * section or use the texture as a `background-image` on the section
 * itself instead of as a standalone block.
 *
 * CSS vars driven by inline style on the wrapper (render.php):
 *   --texture-url      the texture image URL
 *   --texture-opacity  respect from theme.json texture entry
 * (blend-mode intentionally NOT honored — see above.)
 * ================================================================== */
.sg-block-background-texture {
  position: relative;
  height: 1px;
  /* Allow the pseudo to spill above/below the 1px footprint. */
  overflow: visible;
}

.sg-block-background-texture::before {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  width: 100%;
  height: 50vh;
  background-image: var(--texture-url);
  background-size: cover;
  background-repeat: no-repeat;
  background-position: center;
  opacity: var(--texture-opacity, 1);
  pointer-events: none;
  z-index: -1;
}

/* Bottom: pseudo's bottom edge aligns with the block's drop point.
   Texture extends UPWARD by 50vh. Far-edge fade is the PNG's alpha. */
.sg-block-background-texture--bottom::before {
  bottom: 0;
}

/* Top: pseudo's top edge aligns with the block's drop point. Texture
   extends DOWNWARD by 50vh. */
.sg-block-background-texture--top::before {
  top: 0;
}

/* Middle: pseudo's vertical center aligns with the block's drop
   point. Texture extends both ways, 25vh in each direction.
   `top: -25vh` (not `top: 50%; transform: translateY(-50%)`) because
   transform ALSO creates a stacking context — same trap as the mask. */
.sg-block-background-texture--middle::before {
  top: -25vh;
}

/* ==================================================================
 * Swatch Explorer.
 *
 * Full-width swatch showcase. A row of scenario tabs drives panels
 * that each contain a featured "big image" + a horizontally
 * scrollable swatch row. Click a swatch → swaps the big image.
 * Tab + swatch state both track via class + aria.
 *
 * Layout pattern:
 *  - Container is full-width with global side padding (same
 *    technique as hero-gradient).
 *  - Tab row mirrors product-feature-toggles' look (centered,
 *    icon + label, active gets bordered box + cta-color icon).
 *    If a third tab-row block joins, extract to a shared partial.
 *  - Swatch track is a flex row with overflow-x: auto +
 *    scroll-snap so swipe / drag / arrow-button all work. Active
 *    swatch gets a CTA-color border. Swatch width is a CSS custom
 *    prop that shrinks at the tablet + mobile breakpoints so
 *    fewer swatches are visible per scroll-port (the count adapts
 *    naturally — no explicit per-breakpoint count rules).
 * ================================================================== */
.sg-block-swatch-explorer {
  --swatch-width: 8.75rem; /* 140px desktop default */
  display: flex;
  flex-direction: column;
  gap: var(--wp--preset--spacing--40, 2rem);
  /* Global side padding (same pattern as hero-gradient) so on mobile
     the content area gets the theme.json layout padding naturally. */
  padding-inline: var(--wp--style--root--padding-left, var(--wp--preset--spacing--40, 2rem));
  padding-block: var(--wp--preset--spacing--60, 3rem);
}

/* Tab row — centered, wraps gracefully when a long list of tabs
   exceeds the viewport. Active state mirrors product-feature-
   toggles (bordered pill + cta-color icon) but scoped here so the
   two blocks can diverge visually later without coupling. */
.sg-block-swatch-explorer__tabs {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: var(--wp--preset--spacing--20, 1rem);
}

.sg-block-swatch-explorer__tab {
  appearance: none;
  background: transparent;
  border: 1px solid transparent;
  border-radius: var(--wp--custom--radius--default, 0);
  padding: var(--wp--preset--spacing--10, 0.625rem);
  display: inline-flex;
  align-items: center;
  gap: var(--wp--preset--spacing--10, 0.625rem);
  cursor: pointer;
  font-family: var(--wp--preset--font-family--body, inherit);
  font-size: var(--wp--preset--font-size--small, 0.875rem);
  font-weight: 700;
  text-transform: uppercase;
  color: var(--wp--preset--color--ink, currentColor);
  transition: border-color 0.15s ease;
}
.sg-block-swatch-explorer__tab:hover,
.sg-block-swatch-explorer__tab:focus-visible {
  border-color: var(--wp--custom--gray--40, rgba(0, 0, 0, 0.1));
  outline: 0;
}
.sg-block-swatch-explorer__tab.is-active {
  border-color: var(--wp--preset--color--primary, currentColor);
}
.sg-block-swatch-explorer__tab-icon {
  font-size: 1.1em;
  color: var(--wp--preset--color--secondary, currentColor);
  line-height: 1;
  transition: color 0.15s ease;
}
.sg-block-swatch-explorer__tab.is-active .sg-block-swatch-explorer__tab-icon {
  color: var(--wp--preset--color--cta, currentColor);
}

/* Panels — only one visible at a time. hidden attr is removed by
   view.js when activated. */
.sg-block-swatch-explorer__panel {
  display: flex;
  flex-direction: column;
  gap: var(--wp--preset--spacing--40, 2rem);
}
.sg-block-swatch-explorer__panel[hidden] {
  display: none;
}

/* Big featured image. Aspect-locked so swapping images doesn't make
   the layout jump. */
.sg-block-swatch-explorer__big-image {
  width: 100%;
  aspect-ratio: 16 / 9;
  overflow: hidden;
  border-radius: var(--wp--custom--radius--default, 0);
}
.sg-block-swatch-explorer__big-image-el {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

/* Swatch row layout: arrow / track / arrow. Track is flex with
   horizontal overflow + scroll-snap. */
.sg-block-swatch-explorer__swatches {
  display: grid;
  grid-template-columns: auto 1fr auto;
  align-items: center;
  gap: var(--wp--preset--spacing--20, 1rem);
}

.sg-block-swatch-explorer__scroll-prev,
.sg-block-swatch-explorer__scroll-next {
  appearance: none;
  background: transparent;
  border: 0;
  padding: var(--wp--preset--spacing--10, 0.625rem);
  cursor: pointer;
  font-size: 1.1rem;
  color: var(--wp--preset--color--ink, currentColor);
  transition: opacity 0.15s ease;
}
.sg-block-swatch-explorer__scroll-prev:disabled,
.sg-block-swatch-explorer__scroll-next:disabled {
  opacity: 0.25;
  cursor: default;
}

.sg-block-swatch-explorer__swatch-track {
  display: flex;
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  scroll-behavior: smooth;
  gap: var(--wp--preset--spacing--20, 1rem);
  padding-bottom: var(--wp--preset--spacing--10, 0.625rem);
  scrollbar-width: thin;
}

.sg-block-swatch-explorer__swatch {
  appearance: none;
  background: transparent;
  border: 0;
  padding: 0;
  flex: 0 0 var(--swatch-width);
  width: var(--swatch-width);
  scroll-snap-align: start;
  cursor: pointer;
  display: flex;
  flex-direction: column;
  gap: var(--wp--preset--spacing--10, 0.625rem);
  text-align: center;
}

.sg-block-swatch-explorer__swatch-image {
  display: block;
  width: 100%;
  aspect-ratio: 1 / 1;
  background-color: var(--wp--custom--gray--40, #e6e6e6);
  background-size: cover;
  background-position: center;
  background-repeat: no-repeat;
  border-radius: var(--wp--custom--radius--default, 0);
  /* Border carries the active-state cta indicator. Inset slightly
     so the inactive border doesn't shift the layout when toggled. */
  border: 3px solid transparent;
  transition: border-color 0.15s ease;
}
.sg-block-swatch-explorer__swatch.is-active .sg-block-swatch-explorer__swatch-image {
  border-color: var(--wp--preset--color--cta, currentColor);
}
.sg-block-swatch-explorer__swatch:hover .sg-block-swatch-explorer__swatch-image {
  border-color: var(--wp--custom--gray--40, rgba(0, 0, 0, 0.1));
}

.sg-block-swatch-explorer__swatch-name {
  font-size: var(--wp--preset--font-size--x-small, 0.75rem);
  font-weight: 700;
  text-transform: uppercase;
  color: var(--wp--preset--color--ink, currentColor);
}

/* Responsive: shrink the per-swatch width at breakpoints so fewer
   swatches fit per visible scroll-port. Counts adapt naturally. */
@media (max-width: 900px) {
  .sg-block-swatch-explorer { --swatch-width: 7rem; }
}
@media (max-width: 600px) {
  .sg-block-swatch-explorer { --swatch-width: 6rem; }
  .sg-block-swatch-explorer__big-image { aspect-ratio: 4 / 3; }
}

/* ==================================================================
 * Hero — Gradient.
 *
 * Full-width image hero. Layer stack (bottom→top):
 *   - Parent `has-{slug}-background-color` paints the entire hero in
 *     the selected palette slug (the base layer).
 *   - .__image — bg-image set inline by render.php, opacity 0.7 so the
 *     palette color shows through ~30% everywhere. object-fit-style
 *     via background-size: cover; position-bias toward center.
 *   - .__gradient — directional `linear-gradient(to bottom left,
 *     transparent, --hero-gradient-color)`. The CSS var is set inline
 *     on the wrapper by render.php and tracks whichever palette slug
 *     the parent's bg-color uses. The fade goes transparent (top-
 *     right) → opaque palette color (bottom-left) so text in the
 *     bottom-left reads cleanly against the image.
 *   - .__content — relative-positioned wrapper that constrains the
 *     InnerBlocks to the global content-size and centers them.
 *
 * Text-color cascade for InnerBlocks (h1, paragraph, eyebrow) is
 * handled by the parent's per-palette pair-helpers — they read the
 * has-{slug}-background-color on the parent and color descendant
 * text to the slug's opposite automatically. No per-element color
 * picking required by authors.
 * ================================================================== */
.sg-block-hero-gradient {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  /* Global side padding lives on the container, not the inner
     content. On mobile this guarantees the content area gets natural
     side spacing without per-breakpoint overrides. Bg image and
     gradient overlay still extend full-width because they're
     absolutely positioned and `inset: 0` resolves to the parent's
     padding-box edges (which include the padding). */
  padding-inline: var(--wp--style--root--padding-left, var(--wp--preset--spacing--40, 2rem));
  padding-block: var(--wp--preset--spacing--80, 4rem);
}

.sg-block-hero-gradient__image {
  position: absolute;
  inset: 0;
  z-index: 0;
  background-size: cover;
  background-position: center;
  background-repeat: no-repeat;
  opacity: 0.7;
  pointer-events: none;
}

.sg-block-hero-gradient__gradient {
  position: absolute;
  inset: 0;
  z-index: 1;
  /* `to bottom left` reads as "fade starts top-right (transparent)
     and ends bottom-left (full color)" — exactly what the spec asks
     for. Color is whatever palette slug the parent's bg-color uses,
     wired via the --hero-gradient-color inline var set in render.php. */
  background: linear-gradient(
    to bottom left,
    transparent 0%,
    var(--hero-gradient-color, var(--wp--preset--color--primary, currentColor)) 100%
  );
  pointer-events: none;
}

.sg-block-hero-gradient__content {
  position: relative;
  z-index: 2;
  /* Constrain to global content-size and center. Padding-inline
     intentionally NOT set here — it lives on the container so the
     bg image + gradient extend past the padding edges naturally. */
  max-width: var(--wp--style--global--content-size, 1200px);
  margin-inline: auto;
  /* Canonical InnerBlocks layout: flex column + gap=block-gap on
     the InnerBlocks wrapper, paired with `display: contents` on the
     acf-innerblocks-container (added to the list above) so the
     authored eyebrow / h1 / paragraph / buttons become flex children
     for layout purposes. Together with the parent theme's eyebrow→h1
     collapse rule this lands a clean 10px eyebrow→h1 gap and
     block-gap (30px) between everything else. Same pattern as
     hero-content, hero-bg-content, split-body, cta-inner, etc. */
  display: flex;
  flex-direction: column;
  gap: var(--wp--style--block-gap);
}

/* Modifier: narrow content stack.
 *
 * Wrapper stays at content-size so its left edge keeps aligning with
 * the rest of the page's 1200px-centered content; the inner children
 * are capped at narrow-size for shorter line lengths. Children sit
 * flush-left in LTR (flex default with explicit max-width on each).
 * Pair with .content-align-center to center them within content-size. */
.sg-block-hero-gradient__content.content-width-narrow > .acf-innerblocks-container > * {
  max-width: var(--wp--custom--layout--narrow-size);
}

/* Modifier: center alignment.
 *
 * Centers the flex children horizontally (works whether content is
 * full content-size or narrow-size). text-align: center centers
 * inline content (eyebrow + headline + paragraphs); the explicit
 * wp-block-buttons rule centers the buttons row (which is its own
 * flex container — text-align doesn't propagate through that). */
.sg-block-hero-gradient__content.content-align-center {
  align-items: center;
  text-align: center;
}
.sg-block-hero-gradient__content.content-align-center .wp-block-buttons {
  justify-content: center;
}

/* ==================================================================
 * Product Feature Toggles.
 *
 * Tabbed feature switcher. A horizontal row of icon toggles drives a
 * two-column panel: image + floating caption (left), headline + body +
 * detail rows + CTA button (right). Each toggle is its own product
 * variant or category; only one panel is visible at a time.
 *
 * Per-toggle detail row accent color is plumbed via `--detail-color`
 * (set inline on the panel by render.php), so authors can pick any
 * palette slug per toggle without per-toggle CSS overrides.
 * ================================================================== */
.sg-block-product-feature-toggles {
  display: flex;
  flex-direction: column;
  gap: var(--wp--preset--spacing--40, 2rem);
}

/* Tab row — horizontal flex, centered. Wraps gracefully on narrow
   viewports so a long list of tabs doesn't blow the layout. */
.sg-block-product-feature-toggles__tabs {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  align-items: stretch;
  gap: var(--wp--preset--spacing--20, 1rem);
}

.sg-block-product-feature-toggles__tab {
  /* Reset button defaults; we render this as a styled icon + label
     button-but-not-quite. */
  appearance: none;
  background: transparent;
  border: 1px solid transparent;
  border-radius: var(--wp--custom--radius--default, 0);
  padding: var(--wp--preset--spacing--10, 0.625rem);
  display: inline-flex;
  align-items: center;
  gap: var(--wp--preset--spacing--10, 0.625rem);
  cursor: pointer;
  font-family: var(--wp--preset--font-family--body, inherit);
  font-size: var(--wp--preset--font-size--small, 0.875rem);
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--wp--preset--color--ink, currentColor);
  transition: border-color 0.15s ease, background-color 0.15s ease;
}

.sg-block-product-feature-toggles__tab:hover,
.sg-block-product-feature-toggles__tab:focus-visible {
  border-color: var(--wp--custom--gray--40, rgba(0, 0, 0, 0.1));
  outline: 0;
}

.sg-block-product-feature-toggles__tab.is-active {
  /* Active state: bordered box with primary color. Icon flips to CTA
     to draw the eye to the live selection. */
  border-color: var(--wp--preset--color--primary, currentColor);
}

.sg-block-product-feature-toggles__tab-icon {
  font-size: 1.1em;
  color: var(--wp--preset--color--secondary, currentColor);
  line-height: 1;
  transition: color 0.15s ease;
}

.sg-block-product-feature-toggles__tab.is-active .sg-block-product-feature-toggles__tab-icon {
  color: var(--wp--preset--color--cta, currentColor);
}

.sg-block-product-feature-toggles__divider {
  height: 1px;
  background-color: var(--wp--custom--gray--40, rgba(0, 0, 0, 0.1));
  width: 100%;
  margin: 0;
}

/* PFT tabpanel: only one visible at a time. Server-rendered with
   `hidden` on inactive ones; view.js toggles `is-active` + the hidden
   attribute. The 2-col grid layout lives on .sg-block-feature-detail
   (the partial's root, shared by both PFT and the feature-detail
   standalone block) — see that rule below. */
.sg-block-product-feature-toggles__tabpanel[hidden] {
  display: none;
}

/* Shared 2-col layout for the feature-detail panel. Lives on the
   partial's root so it applies whether the partial is rendered inside
   a PFT tabpanel OR by the standalone feature-detail block. */
.sg-block-feature-detail {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--wp--preset--spacing--50, 2.5rem);
  align-items: start;
}

/* Left column: image with floating caption card overlay. */
.sg-block-feature-detail__image {
  position: relative;
  width: 100%;
  aspect-ratio: 1 / 1;
  overflow: hidden;
  border-radius: var(--wp--custom--radius--default, 0);
}

.sg-block-feature-detail__img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

/* Caption card overhangs the image's bottom-left corner. The orange
   accent bar on the left edge is a `::before` pseudo-element so we
   don't need extra markup. */
.sg-block-feature-detail__caption {
  position: absolute;
  left: var(--wp--preset--spacing--30, 1.5rem);
  bottom: var(--wp--preset--spacing--30, 1.5rem);
  background-color: var(--wp--preset--color--canvas, #fff);
  padding: var(--wp--preset--spacing--20, 1rem) var(--wp--preset--spacing--30, 1.25rem);
  max-width: 75%;
  box-shadow: var(--wp--custom--elevation--card, var(--wp--preset--shadow--md));
  border-radius: var(--wp--custom--radius--default, 0);
}

.sg-block-feature-detail__caption::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0;
  bottom: 0;
  width: 4px;
  background-color: var(--wp--preset--color--cta, currentColor);
}

.sg-block-feature-detail__caption-subtitle {
  margin: 0;
  font-size: var(--wp--preset--font-size--x-small, 0.75rem);
  font-weight: 700;
  text-transform: uppercase;
  color: var(--wp--preset--color--ink-soft, currentColor);
}

.sg-block-feature-detail__caption-title {
  margin: 0;
  font-size: var(--wp--preset--font-size--large, 1.1rem);
  font-weight: 700;
  line-height: 1.2;
  color: var(--wp--preset--color--ink, currentColor);
}

/* Right column: text + detail list + CTA. */
.sg-block-feature-detail__right {
  display: flex;
  flex-direction: column;
  gap: var(--wp--preset--spacing--30, 1.5rem);
}

/* Headline inherits all typography from theme.json's elements.h3 via
   the .wp-block-heading family. No overrides here — each child theme
   gets to decide how this scale looks. */
.sg-block-feature-detail__headline {
  margin: 0;
}

.sg-block-feature-detail__description {
  margin: 0;
  font-size: var(--wp--preset--font-size--medium, 1rem);
}

/* Detail row list — vertical stack, gap between rows. */
.sg-block-feature-detail__details {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--wp--preset--spacing--20, 1rem);
}

/* One detail row: panel-tinted card with a colored left bar driven by
   the panel's --detail-color CSS prop (set inline by render.php from
   the per-toggle right_detail_color palette slug). */
.sg-block-feature-detail__detail {
  display: flex;
  align-items: flex-start;
  gap: var(--wp--preset--spacing--20, 1rem);
  background-color: var(--wp--preset--color--panel, #ffffff);
  border-left: 4px solid var(--detail-color, var(--wp--preset--color--secondary, currentColor));
  border-radius: var(--wp--custom--radius--default, 0);
  padding: var(--wp--preset--spacing--20, 1rem);
}

.sg-block-feature-detail__detail-icon {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2.5rem;
  height: 2.5rem;
  background-color: var(--wp--preset--color--canvas, #f8f9fa);
  border-radius: var(--wp--custom--radius--default, 0);
  color: var(--detail-color, var(--wp--preset--color--secondary, currentColor));
  font-size: 1.1rem;
  line-height: 1;
}

.sg-block-feature-detail__detail-body {
  flex: 1 1 auto;
  min-width: 0;
}

.sg-block-feature-detail__detail-label {
  margin: 0;
  font-size: var(--wp--preset--font-size--medium, 1rem);
  font-weight: 700;
  line-height: 1.2;
  color: var(--wp--preset--color--ink, currentColor);
}

.sg-block-feature-detail__detail-desc {
  margin: 0;
  font-size: var(--wp--preset--font-size--small, 0.9rem);
}

/* CTA button — outlined by default to match the mockup. The trailing
   arrow is a sibling span so it inherits color from the button text. */
.sg-block-feature-detail__cta {
  margin-top: var(--wp--preset--spacing--10, 0.5rem);
}

.sg-block-feature-detail__cta-arrow {
  display: inline-block;
  margin-left: 0.4em;
  transform: translateY(-1px);
}

/* Mobile: collapse the panel to single column, image first, content below. */
@media (max-width: 900px) {
  .sg-block-feature-detail {
    grid-template-columns: 1fr;
  }
  .sg-block-product-feature-toggles__tab {
    /* Tighter horizontal padding so 4+ tabs still fit on phones. */
    padding-inline: var(--wp--preset--spacing--20, 0.75rem);
  }
}

/* ==================================================================
 * Feature Showcase (.sg-block-feature-showcase)
 *
 * Two stacked sections: a colored "top panel" with centered title + WYSIWYG
 * + figure-wrapped image, and a row of icon-feature pods underneath.
 *
 * Top panel padding is spacing--30 on three sides; bottom is flush so the
 * image can bleed to the lower edge of the panel (Tailscale-style hero).
 *
 * The native bg picker is stripped off the block root by render.php and
 * re-applied to `.sg-block-feature-showcase__top` only — so the icon pods
 * stay transparent against the page bg.
 * ================================================================== */
/* Parent-most owns the corner radius + clips inner sections to it.
   No gap between the colored top panel and the pod row — they butt
   directly against each other so a future border on `.__pod` can sit
   flush with the panel edge. */
.sg-block-feature-showcase {
  display: flex;
  flex-direction: column;
  border-radius: var(--wp--custom--radius--default);
  overflow: hidden;
}

.sg-block-feature-showcase__top {
  padding: var(--wp--preset--spacing--60) var(--wp--preset--spacing--30) 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--wp--preset--spacing--30);
  text-align: center;
}

.sg-block-feature-showcase__top-content {
  max-width: var(--wp--custom--layout--narrow-size, 800px);
  display: flex;
  flex-direction: column;
  gap: var(--wp--preset--spacing--20);
}

.sg-block-feature-showcase__title {
  margin: 0;
}

.sg-block-feature-showcase__description > :first-child { margin-top: 0; }
.sg-block-feature-showcase__description > :last-child { margin-bottom: 0; }

/* Figure is flush with the panel's bottom edge — the panel has zero
   bottom padding so the image clips cleanly against the colored bg. */
.sg-block-feature-showcase__figure {
  margin: 0;
  width: 100%;
  max-width: var(--wp--style--global--wide-size, 1200px);
  display: block;
}
.sg-block-feature-showcase__figure img {
  display: block;
  width: 100%;
  height: auto;
}

/* Bottom: icon pod grid. No gap between pods — they sit edge-to-edge so
   a future border treatment can share edges cleanly. Padding lives on
   each `.__pod` instead, not the grid. Paints the card-bg so the row
   reads as the bottom half of the same rounded shell as the top panel. */
.sg-block-feature-showcase__pods {
  display: grid;
  grid-template-columns: repeat(var(--block-columns, 3), 1fr);
  background: var(--wp--preset--color--panel, #ffffff);
}

.sg-block-feature-showcase__pod {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--wp--preset--spacing--10);
  padding: var(--wp--preset--spacing--30);
}

.sg-block-feature-showcase__pod-icon {
  color: var(--wp--preset--color--primary);
  font-size: var(--wp--preset--spacing--30);
  line-height: 1;
  margin-bottom: var(--wp--preset--spacing--10);
}

.sg-block-feature-showcase__pod-title {
  margin: 0;
}

.sg-block-feature-showcase__pod-body {
  margin: 0;
}

/* Pin the button to the pod's bottom edge so every pod's button aligns
   on the same baseline regardless of how tall the body copy runs. The
   grid's default `align-items: stretch` makes pods equal-height; the
   `margin-top: auto` then shoves the button to the bottom. */
.sg-block-feature-showcase__pod-btn {
  margin-top: auto;
  padding-top: var(--wp--preset--spacing--10);
}

/* Mobile: collapse pods. Top panel can keep its layout — title + image
   already stack vertically. */
@media (max-width: 900px) {
  .sg-block-feature-showcase__pods {
    grid-template-columns: repeat(2, 1fr);
  }
}
@media (max-width: 600px) {
  .sg-block-feature-showcase__pods {
    grid-template-columns: 1fr;
  }
  .sg-block-feature-showcase__top {
    padding: var(--wp--preset--spacing--40) var(--wp--preset--spacing--20) 0;
  }
}

/* ==================================================================
 * Style-guide block descriptors.
 *
 * Rendered above every block on /style-guide by the
 * `ccc_sg_block_descriptor()` helper in patterns/style-guide.php. Carries
 * the block name, a "kind" pill (Custom / Hybrid / Gutenberg), the
 * block.json description, and per-block usage guidance.
 *
 * Visually: a tinted card with an accent-color left bar. The accent bar
 * + kind pill share the same hue per kind so authors can scan the page
 * and immediately tell which blocks are pure-ACF (Custom), ACF wrapping
 * InnerBlocks (Hybrid), or core WP blocks (Gutenberg).
 *
 * All hues pull from theme.json palette presets so this works across
 * every child theme without per-child overrides.
 * ================================================================== */
.sg-block-descriptor {
  /* Slightly tinted card. color-mix darkens the palette's `light` slot
     by 8% toward `dark` — keeps the card distinct from the page bg on
     light palettes and avoids being invisible. */
  background-color: color-mix(in srgb, var(--wp--preset--color--canvas, #f5f5f5) 92%, var(--wp--preset--color--ink, #000) 8%);
  border-left: 4px solid var(--wp--preset--color--primary, currentColor);
  border-radius: var(--wp--custom--radius--default, 0);
  padding: var(--wp--preset--spacing--30, 1.5rem) var(--wp--preset--spacing--40, 2rem);
  /* Lots of top-margin separation from prior block, tight bottom margin
     to visually pair the descriptor with the block it's introducing. */
  margin-top: var(--wp--preset--spacing--80, 4rem);
  margin-bottom: var(--wp--preset--spacing--20, 0.75rem);
}

.sg-block-descriptor__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--wp--preset--spacing--30, 1rem);
  flex-wrap: wrap;
  margin-bottom: var(--wp--preset--spacing--10, 0.5rem);
}

.sg-block-descriptor__name {
  margin: 0;
  font-size: var(--wp--custom--fs--h-4, 1.5rem);
  line-height: 1.15;
  color: var(--wp--preset--color--ink, currentColor);
}

/* Kind pill — small, all-caps, padded, rounded (always round even when
   the theme uses 0 radius — the pill should feel pill-shaped).
   Per-kind background colors below; default text color is the palette's
   opposite of the kind's bg so contrast is correct regardless of palette. */
.sg-block-descriptor__kind {
  display: inline-block;
  flex: 0 0 auto;
  font-size: 0.7rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  padding: 0.35em 0.85em;
  border-radius: 999px;
  white-space: nowrap;
}

/* Custom: primary palette (cool / brand). */
.sg-block-descriptor__kind--custom {
  background-color: var(--wp--preset--color--primary, #000);
  color: var(--wp--custom--color--primary-opposite, #fff);
}
.sg-block-descriptor--custom {
  border-left-color: var(--wp--preset--color--primary, currentColor);
}

/* Hybrid: cta palette (warm / accent) — signals "more setup than Custom". */
.sg-block-descriptor__kind--hybrid {
  background-color: var(--wp--preset--color--cta, #ff8000);
  color: var(--wp--custom--color--cta-opposite, #fff);
}
.sg-block-descriptor--hybrid {
  border-left-color: var(--wp--preset--color--cta, currentColor);
}

/* Gutenberg: secondary palette (neutral / "core WP"). */
.sg-block-descriptor__kind--gutenberg {
  background-color: var(--wp--preset--color--secondary, #555);
  color: var(--wp--custom--color--secondary-opposite, #fff);
}
.sg-block-descriptor--gutenberg {
  border-left-color: var(--wp--preset--color--secondary, currentColor);
}

/* block.json description — the canonical short "what is this?" line. */
.sg-block-descriptor__what {
  margin: 0 0 var(--wp--preset--spacing--20, 0.75rem) 0;
  font-size: var(--wp--preset--font-size--medium, 1rem);
  font-style: italic;
  color: color-mix(in srgb, var(--wp--preset--color--ink, #000) 70%, transparent);
}

/* Usage guidance — slightly muted body. Contains <code> spans for
   field/setting names; style those distinctly so they read as
   identifiers, not body copy. */
.sg-block-descriptor__how {
  margin: 0;
  font-size: var(--wp--preset--font-size--small, 0.95rem);
  line-height: 1.55;
  color: var(--wp--preset--color--ink, currentColor);
}
.sg-block-descriptor__how p {
  margin: 0;
}
.sg-block-descriptor__how code {
  background-color: color-mix(in srgb, var(--wp--preset--color--ink, #000) 8%, transparent);
  padding: 0.1em 0.4em;
  border-radius: 3px;
  font-size: 0.9em;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  color: var(--wp--preset--color--primary, currentColor);
}

/* ==================================================================
 * Hero 3 Up (classic-city-core/hero-3-up).
 *
 * Layout: 2/3 + 1/3 intro row (title-col + desc-col), full-width hero
 * image below. Field-driven; no Inner Blocks.
 *
 * Padding-top is the theme's block-gap (default of any Hero block;
 * variant-specific value can be tuned here per design). Horizontal
 * padding comes from theme.json's root padding because
 * `useRootPaddingAwareAlignments` is on — we restore it inside the
 * alignfull wrapper via `--wp--style--root--padding-left/right`.
 *
 * Mobile (≤ 768px): the 2-col intro stacks to single column; image
 * stays full-width below.
 *
 * `.sg-hero` is on the wrapper as the shared "page hero" marker —
 * no CSS attached to that class. The variant-specific layout lives
 * here under `.sg-block-hero-3-up`.
 * ================================================================== */
.sg-block-hero-3-up {
  padding-top: var(--wp--style--block-gap, var(--wp--preset--spacing--30));
}
.sg-block-hero-3-up__inner {
  padding-left: var(--wp--style--root--padding-left, var(--wp--preset--spacing--30));
  padding-right: var(--wp--style--root--padding-right, var(--wp--preset--spacing--30));
  max-width: var(--wp--style--global--wide-size);
  margin-left: auto;
  margin-right: auto;
  display: flex;
  flex-direction: column;
  gap: var(--wp--style--block-gap, var(--wp--preset--spacing--30));
}
.sg-block-hero-3-up__intro {
  display: grid;
  grid-template-columns: 2fr 1fr;
  gap: var(--wp--style--block-gap, var(--wp--preset--spacing--30));
  align-items: end;
}
.sg-block-hero-3-up__title-col,
.sg-block-hero-3-up__desc-col {
  display: flex;
  flex-direction: column;
  gap: var(--wp--style--block-gap, var(--wp--preset--spacing--30));
}
/* The image cell respects an authored aspect-ratio (set inline from
   the field). When `none` is chosen, no inline aspect-ratio attribute
   is emitted and the image uses its natural ratio. */
.sg-block-hero-3-up__image {
  width: 100%;
  overflow: hidden;
}
.sg-block-hero-3-up__image > img {
  width: 100%;
  height: auto;
  display: block;
}
.sg-block-hero-3-up__image[style*="aspect-ratio"] {
  /* Container has the ratio; image fills + crops. */
}
.sg-block-hero-3-up__image[style*="aspect-ratio"] > img {
  height: 100%;
  object-fit: cover;
}

@media (max-width: 768px) {
  .sg-block-hero-3-up__intro {
    grid-template-columns: 1fr;
    align-items: stretch;
  }
}

/* ==================================================================
 * Detail Cards.
 *
 * Image-backgrounded data cards (stat top-left, label top-right, hairline
 * divider, description bottom). Two layout modes:
 *   - .is-grid       — grid of `--block-columns` columns; mobile collapses
 *                      to stack or horizontal scroll per .is-mobile-*.
 *   - .is-autoscroll — single nowrap row, fixed ~33vw cards, CSS marquee
 *                      (same pattern as .sg-block-logos--scroller). The
 *                      duplicate card set in markup is aria-hidden; the
 *                      animation translates -50% for a seamless loop.
 *
 * Card surface: the Gutenberg bg-color picker is applied per-card
 * (render.php strips it off the wrapper and emits has-{slug}-background-
 * color on each .__card). That triggers the parent's pair-helper text
 * color automatically. The same slug is exposed as `--card-bg-color` on
 * the wrapper so the top/bottom scrim gradients fade into the card's
 * own color instead of an arbitrary black tint.
 * ================================================================== */
.sg-block-detail-cards {
  --detail-cards-gap: var(--wp--style--block-gap, var(--wp--preset--spacing--30, 1.5rem));
  --detail-cards-pad: var(--wp--preset--spacing--30, 1.5rem);
  --detail-cards-card-bg-fallback: var(--wp--preset--color--panel, transparent);
}

.sg-block-detail-cards__row {
  display: grid;
  grid-template-columns: repeat(var(--block-columns, 3), minmax(0, 1fr));
  gap: var(--detail-cards-gap);
  align-items: stretch;
}

/* Autoscroll — single nowrap row of fixed 33vw cards, marquee loop.
   The row contains every card twice (duplicate set is aria-hidden in
   render.php); translateX(-50%) lands the duplicate exactly where the
   original started. */
.sg-block-detail-cards.is-autoscroll {
  overflow: hidden;
  width: 100%;
}
.sg-block-detail-cards.is-autoscroll .sg-block-detail-cards__row {
  display: flex;
  flex-wrap: nowrap;
  width: max-content;
  animation: sg-detail-cards-scroll 60s linear infinite;
}
.sg-block-detail-cards.is-autoscroll .sg-block-detail-cards__card {
  flex: 0 0 33vw;
}
.sg-block-detail-cards.is-autoscroll:hover .sg-block-detail-cards__row {
  animation-play-state: paused;
}
@keyframes sg-detail-cards-scroll {
  from { transform: translateX(0); }
  to   { transform: translateX(calc(-50% - (var(--detail-cards-gap) / 2))); }
}

/* Card. */
.sg-block-detail-cards__card {
  position: relative;
  aspect-ratio: var(--card-aspect-ratio, 3 / 4);
  border-radius: var(--wp--custom--radius--default, 0);
  overflow: hidden;
  background-color: var(--detail-cards-card-bg-fallback);
  /* `has-{slug}-background-color` from the Gutenberg picker overrides
     the fallback above and (via the parent's pair-helpers) sets text
     color to the slug's opposite. */
}

/* Background image fills the card; opacity is block-level. */
.sg-block-detail-cards__bg {
  position: absolute;
  inset: 0;
  margin: 0;
  width: 100%;
  height: 100%;
  opacity: var(--card-bg-opacity, 1);
  pointer-events: none;
}
.sg-block-detail-cards__bg img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

/* Scrim — fades into the card's own bg color at top and bottom so the
   stat row and description stay legible over arbitrary photos. When
   no bg color is picked, --card-bg-color is unset and the gradient
   falls back to transparent (no scrim). */
.sg-block-detail-cards__card::after {
  content: '';
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: linear-gradient(
    to bottom,
    var(--card-bg-color, transparent) 0%,
    transparent 22%,
    transparent 70%,
    var(--card-bg-color, transparent) 100%
  );
}

.sg-block-detail-cards__inner {
  position: relative;
  z-index: 1;
  display: flex;
  flex-direction: column;
  height: 100%;
  padding: var(--detail-cards-pad);
  gap: var(--wp--style--block-gap, var(--wp--preset--spacing--30, 1.5rem));
}

/* Top: stat (left, 50%) + stat title (right, 50%) with hairline
   divider below in currentColor — inherits the auto-paired text color
   from has-{slug}-background-color. Items are bottom-aligned so the
   stat sits on the baseline of the wrapped label (matches the
   reference card). */
.sg-block-detail-cards__head {
  display: grid;
  grid-template-columns: 1fr 1fr;
  align-items: end;
  gap: var(--wp--preset--spacing--20, 1rem);
  padding-bottom: var(--wp--preset--spacing--20, 1rem);
  border-bottom: var(--wp--custom--border--default-width, 1px) solid currentColor;
}
.sg-block-detail-cards__stat {
  margin: 0;
  text-align: left;
  line-height: 1.1;
  font-size: var(--wp--preset--font-size--large);
}
.sg-block-detail-cards__stat-title {
  margin: 0;
  text-align: right;
  line-height: 1.2;
  font-size: var(--wp--preset--font-size--small);
}

/* Description anchored at the bottom. */
.sg-block-detail-cards__desc {
  margin: auto 0 0 0;
  font-size: var(--wp--preset--font-size--small);
}

/* Respect prefers-reduced-motion: halt the marquee and fall back to a
   wrapped layout so the cards still flow. */
@media (prefers-reduced-motion: reduce) {
  .sg-block-detail-cards.is-autoscroll {
    overflow: visible;
  }
  .sg-block-detail-cards.is-autoscroll .sg-block-detail-cards__row {
    animation: none;
    flex-wrap: wrap;
    width: auto;
  }
}

/* Mobile — autoscroll always disables itself, then .is-mobile-* takes
   over (stack collapses to 1 col; scroll becomes a swipeable snap row
   matching the image-card-grid pattern). */
@media (max-width: 768px) {
  .sg-block-detail-cards.is-autoscroll {
    overflow: visible;
  }
  .sg-block-detail-cards.is-autoscroll .sg-block-detail-cards__row {
    animation: none;
    width: auto;
  }
  /* Autoscroll on mobile falls back to whatever mobile_layout is set —
     stack by default, scroll if the author opted in. */
  .sg-block-detail-cards.is-mobile-stack .sg-block-detail-cards__row {
    display: grid;
    grid-template-columns: 1fr;
    flex-wrap: initial;
  }
  .sg-block-detail-cards.is-mobile-stack .sg-block-detail-cards__card {
    flex: initial;
  }
  .sg-block-detail-cards.is-mobile-scroll .sg-block-detail-cards__row {
    display: flex;
    flex-wrap: nowrap;
    grid-template-columns: none;
    overflow-x: auto;
    padding-bottom: var(--wp--preset--spacing--20, 1rem);
    scrollbar-width: thin;
  }
  .sg-block-detail-cards.is-mobile-scroll .sg-block-detail-cards__card {
    flex: 0 0 85%;
  }
}

/* ==================================================================
 * Highlight Tiles.
 *
 * Marketing tile grid — 2×2 (.is-4-tiles) or 3×2 (.is-6-tiles) on
 * desktop, single-column stack on mobile. Each tile is a headline-led
 * card with a per-card palette bg color (via has-{slug}-background-
 * color so the parent's pair-helper auto-sets text color) and an
 * optional image with one of two treatments:
 *
 *   - .is-treatment-background — full-bleed `<figure>` behind the
 *     headline, cover-cropped center/center. A top scrim driven by
 *     --card-bg-color keeps the headline legible.
 *   - .is-treatment-inline — image sits centered in the lower portion
 *     at constrained max-width (does NOT bleed edge-to-edge).
 *
 * Aspect ratio is hardcoded here (no editor knob). 5/3 reads as a
 * proper marketing tile across both desktop and tablet widths; on
 * mobile we drop the ratio so a stacked tile can grow as tall as its
 * content needs.
 * ================================================================== */
.sg-block-highlight-tiles {
  --tiles-gap: var(--wp--style--block-gap, var(--wp--preset--spacing--30, 1.5rem));
  --tiles-pad: var(--wp--preset--spacing--30, 1.5rem);
  --tiles-inline-image-max-width: 60%;
  display: grid;
  gap: var(--tiles-gap);
  align-items: stretch;
}

/* 6-tile: 3-column × 5-row bento (3fr / 5fr / 4fr widths). The 5-row
   track lets tall tiles span 3 rows and short tiles span 2 — a 1.5×
   height ratio that reads more uniform than the old 2:1 (2+1) split.
     col 1 (3fr, narrow): #1 tall (rows 1-3), #4 short (rows 4-5)
     col 2 (5fr, wide):   #2 short (rows 1-2), #5 tall (rows 3-5)
     col 3 (4fr, medium): #3 tall (rows 1-3), #6 short (rows 4-5)
   `aspect-ratio: 2/1` on the grid anchors the row heights; each cell
   inherits its dimensions from grid track geometry, so different col
   widths × different row spans produces visibly distinct aspect
   shapes per tile. */
.sg-block-highlight-tiles.is-6-tiles {
  grid-template-columns: 3fr 5fr 4fr;
  grid-template-rows: repeat(5, 1fr);
  aspect-ratio: 2 / 1;
}
.sg-block-highlight-tiles.is-6-tiles .sg-block-highlight-tiles__tile:nth-child(1) {
  grid-column: 1;
  grid-row: 1 / span 3;
}
.sg-block-highlight-tiles.is-6-tiles .sg-block-highlight-tiles__tile:nth-child(2) {
  grid-column: 2;
  grid-row: 1 / span 2;
}
.sg-block-highlight-tiles.is-6-tiles .sg-block-highlight-tiles__tile:nth-child(3) {
  grid-column: 3;
  grid-row: 1 / span 3;
}
.sg-block-highlight-tiles.is-6-tiles .sg-block-highlight-tiles__tile:nth-child(4) {
  grid-column: 1;
  grid-row: 4 / span 2;
}
.sg-block-highlight-tiles.is-6-tiles .sg-block-highlight-tiles__tile:nth-child(5) {
  grid-column: 2;
  grid-row: 3 / span 3;
}
.sg-block-highlight-tiles.is-6-tiles .sg-block-highlight-tiles__tile:nth-child(6) {
  grid-column: 3;
  grid-row: 4 / span 2;
}

/* 4-tile: drop col 3 (and its #3/#6 occupants). Cols are 3fr/5fr,
   5 rows. Bumping the grid aspect to 4/3 keeps the leftover four
   tiles at the same aspect shapes they have in the 6-tile layout —
   they just get more horizontal room each because col 3 is gone. */
.sg-block-highlight-tiles.is-4-tiles {
  grid-template-columns: 3fr 5fr;
  grid-template-rows: repeat(5, 1fr);
  aspect-ratio: 4 / 3;
}
.sg-block-highlight-tiles.is-4-tiles .sg-block-highlight-tiles__tile:nth-child(1) {
  grid-column: 1;
  grid-row: 1 / span 3;
}
.sg-block-highlight-tiles.is-4-tiles .sg-block-highlight-tiles__tile:nth-child(2) {
  grid-column: 2;
  grid-row: 1 / span 2;
}
.sg-block-highlight-tiles.is-4-tiles .sg-block-highlight-tiles__tile:nth-child(3) {
  grid-column: 1;
  grid-row: 4 / span 2;
}
.sg-block-highlight-tiles.is-4-tiles .sg-block-highlight-tiles__tile:nth-child(4) {
  grid-column: 2;
  grid-row: 3 / span 3;
}

.sg-block-highlight-tiles__tile {
  position: relative;
  display: flex;
  flex-direction: column;
  border-radius: var(--wp--custom--radius--default, 0);
  overflow: hidden;
  background-color: var(--wp--preset--color--panel, transparent);
  /* No global aspect-ratio — 4-tile sets one on the tile, 6-tile lets
     the grid cell determine size. `has-{slug}-background-color` from
     markup auto-pairs text color via the parent's pair-helpers. */
}

/* Background-treatment image — fills the tile under the content. */
.sg-block-highlight-tiles__bg {
  position: absolute;
  inset: 0;
  margin: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
}
.sg-block-highlight-tiles__bg img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center center;
  display: block;
}

/* Scrim — only for background treatment. Fades the picked card color
   into transparency so the headline reads on any photo. When no bg
   color is picked, --card-bg-color is unset and the gradient is
   transparent (no scrim drawn). */
.sg-block-highlight-tiles__tile.is-treatment-background::after {
  content: '';
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: linear-gradient(
    to bottom,
    var(--card-bg-color, transparent) 0%,
    transparent 55%
  );
}

.sg-block-highlight-tiles__inner {
  position: relative;
  z-index: 1;
  display: flex;
  flex-direction: column;
  height: 100%;
  padding: var(--tiles-pad);
  gap: var(--wp--preset--spacing--20, 1rem);
}

.sg-block-highlight-tiles__headline {
  margin: 0;
  width: 60%;
  text-align: left;
  font-size: var(--wp--preset--font-size--large);
}

/* Inline image — fills the remaining vertical space below the
   headline so every tile keeps its aspect ratio regardless of the
   image's intrinsic shape. `flex: 1 + min-height: 0` lets the figure
   shrink-or-grow to whatever space is left; `object-fit: contain` +
   `object-position: center bottom` anchors the image so portrait or
   landscape sources both feel deliberate. Width is capped so the
   image never bleeds edge-to-edge. */
.sg-block-highlight-tiles__inline {
  margin: 0 auto;
  width: 100%;
  max-width: var(--tiles-inline-image-max-width);
  flex: 1;
  min-height: 0;
  display: flex;
  align-items: flex-end;
  justify-content: center;
  overflow: hidden;
}
.sg-block-highlight-tiles__inline img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: contain;
  object-position: center bottom;
}

/* Mobile: stack to a single column. Drop bento placement (column +
   row tracks), the grid aspect-ratio, and any tile aspect-ratio so
   each tile grows to fit its content. */
@media (max-width: 768px) {
  .sg-block-highlight-tiles,
  .sg-block-highlight-tiles.is-4-tiles,
  .sg-block-highlight-tiles.is-6-tiles {
    grid-template-columns: 1fr;
    grid-template-rows: none;
    aspect-ratio: auto;
  }
  .sg-block-highlight-tiles.is-4-tiles .sg-block-highlight-tiles__tile,
  .sg-block-highlight-tiles.is-6-tiles .sg-block-highlight-tiles__tile {
    grid-column: auto;
    grid-row: auto;
  }
  .sg-block-highlight-tiles__tile {
    aspect-ratio: auto;
    min-height: 320px;
  }
}

/* ==================================================================
 * Framed Callout — full-width hero / CTA section.
 *
 * Container = `.sg-block-framed-callout.alignfull`. Carries uniform
 * padding (the page's global L/R padding token) on all four sides
 * so the inner panel is visually "framed" against the page bg.
 *
 * Inner = `.sg-block-framed-callout__inner`. Paints the bg color /
 * gradient (from the Gutenberg native picker, propagated off the
 * wrapper in render.php), holds the optional bg image figure, and
 * flex-centers the content wrapper vertically.
 *
 * Content = `.sg-block-framed-callout__content`. Constrained to the
 * page's content-size (wide) and centered. 40px top/bottom padding
 * always present so the InnerBlocks never touch the inner panel's
 * top/bottom edges. Children are width-capped at narrow-size via the
 * canonical InnerBlocks pattern. Alignment modifier (left | center)
 * decides which edge they pin to.
 *
 * Heights: `.is-height-{50..100}` sets `min-height` on the Container
 * in vh. Because Container has `box-sizing: border-box`, the chosen
 * vh INCLUDES the frame padding — so 100vh means the entire framed
 * section is exactly one viewport tall (frame + inner panel together).
 * `.is-height-auto` lets the section size to its content.
 * ================================================================== */
.sg-block-framed-callout {
  /* Frame inset matches the site's root padding so the panel lines up with
     the page's content gutters (was a fixed spacing--30). */
  --framed-callout-frame-padding: var(--wp--style--root--padding-left, var(--wp--preset--spacing--30));
  --framed-callout-inner-pad-block: var(--wp--preset--spacing--40);
  display: flex;
  flex-direction: column;
  /* Frame insets top + sides; flush at the bottom (no bottom gap). */
  padding: var(--framed-callout-frame-padding) var(--framed-callout-frame-padding) 0;
  box-sizing: border-box;
  /* No `width: 100%` here — WP's alignfull cascade sets `width: unset`
     so the negative-margin breakout can reach the viewport edges. A
     fixed width % would re-clamp it to the parent's content-box. We
     do still need `margin-inline: 0` defensively because the page
     template's `is-layout-constrained` rule otherwise auto-centers
     each child. */
  margin-inline: 0;
  max-width: none;
}

.sg-block-framed-callout.is-height-50  { min-height: 50vh;  }
.sg-block-framed-callout.is-height-60  { min-height: 60vh;  }
.sg-block-framed-callout.is-height-70  { min-height: 70vh;  }
.sg-block-framed-callout.is-height-80  { min-height: 80vh;  }
.sg-block-framed-callout.is-height-90  { min-height: 90vh;  }
.sg-block-framed-callout.is-height-100 { min-height: 100vh; }
/* .is-height-auto intentionally has no rule — falls back to content. */

.sg-block-framed-callout__inner {
  flex: 1;
  position: relative;
  display: flex;
  flex-direction: column;
  justify-content: center;
  border-radius: var(--wp--custom--radius--default, 0);
  box-shadow: var(--wp--custom--elevation--card);
  overflow: hidden;
  /* `has-{slug}-background-color` / `has-{slug}-gradient-background`
     from render.php paint this panel; without them it inherits the
     page bg through transparency. */
}

.sg-block-framed-callout__bg {
  position: absolute;
  inset: 0;
  margin: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
}
.sg-block-framed-callout__bg img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center center;
  display: block;
}

.sg-block-framed-callout__content {
  position: relative;
  z-index: 1;
  width: 100%;
  max-width: var(--wp--style--global--content-size, 1200px);
  margin-inline: auto;
  padding-block: var(--framed-callout-inner-pad-block);
  padding-inline: var(--wp--preset--spacing--30);
  /* Canonical InnerBlocks layout: flex column + gap=block-gap with
     `display: contents` on the acf-innerblocks-container (added to
     the list near line 400) so authored eyebrow / h1 / paragraph /
     buttons become flex children of __content. Same pattern as
     hero-content, hero-bg-content, hero-gradient__content, etc. */
  display: flex;
  flex-direction: column;
  gap: var(--wp--style--block-gap);
}

/* Narrow constraint on direct children (the authored blocks) so each
   row is capped at narrow-size while the wrapper itself stays at
   content-size — gives left-aligned content a natural left edge that
   matches the page's content column. */
.sg-block-framed-callout__content > .acf-innerblocks-container > * {
  max-width: var(--wp--custom--layout--narrow-size);
  width: 100%;
}

/* Alignment modifiers — same shape as hero-gradient's content-align. */
.sg-block-framed-callout__content.content-align-center {
  align-items: center;
  text-align: center;
}
.sg-block-framed-callout__content.content-align-center .wp-block-buttons {
  justify-content: center;
}
.sg-block-framed-callout__content.content-align-left {
  align-items: flex-start;
  text-align: left;
}
.sg-block-framed-callout__content.content-align-left .wp-block-buttons {
  justify-content: flex-start;
}
