/* RedSprout — design system
   Brand: red #C40808, charcoal #1A1A1C, off-white paper.
   Signature: the sourced figure. Every number carries its source, so the
   design makes an unsourced claim physically impossible to render.
   No framework. No utility classes. ~1 request. */

/* ---------------------------------------------------------- tokens */
:root {
  --rsd-red:        #C40808;
  --rsd-red-ink:    #980606;
  --rsd-charcoal:   #1A1A1C;
  --rsd-paper:      #F7F6F4;
  --rsd-white:      #FFFFFF;
  --rsd-line:       #E3E0DA;
  --rsd-muted:      #63636A;

  --rsd-display: Archivo, "Helvetica Neue", Arial, sans-serif;
  --rsd-body:    "Plex Sans", ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, sans-serif;
  /* D-24's third family (figures, eyebrows, source lines, table numerals).
     No .woff2 file exists yet, same as the other two — falls back to the
     system monospace stack until one is added, same pattern already used
     for Archivo/Plex Sans in inc/enqueue.php. */
  --rsd-mono:    "IBM Plex Mono", ui-monospace, "SF Mono", Consolas, monospace;

  /* Type scale — widened for clearer hierarchy. Each step now clamps
     between a mobile-floor and a desktop-ceiling computed with the same
     fluid formula (minVP 375px → maxVP 1280px), so the ratio between
     levels holds at every width instead of compressing toward ~1.26 at
     the narrow end the way the old scale did. Ratio to the step below:
     lg~1.13, h3/base~1.3-1.65, h2/h3~1.3-1.43, h1/h2~1.25-1.6 — was as
     tight as ~1.26 across the board at mobile widths before. --rsd-t-base
     stays exactly where it was: body copy wasn't the thing reading flat. */
  --rsd-t-xs:  0.78rem;
  --rsd-t-sm:  0.875rem;
  --rsd-t-base: 1.0625rem;
  --rsd-t-lg:  clamp(1.2rem,  0.27vw + 1.14rem, 1.35rem);
  --rsd-t-h3:  clamp(1.4rem,  0.62vw + 1.25rem, 1.75rem);
  --rsd-t-h2:  clamp(1.85rem, 1.15vw + 1.58rem, 2.5rem);
  --rsd-t-h1:  clamp(2.4rem,  2.83vw + 1.74rem, 4rem);

  --rsd-shell: 1180px;
  --rsd-read:  68ch;

  /* Vertical rhythm — one scale, three tiers, all fluid.
     --rsd-gap-sm  (small)  — between related items inside one group: cards
                              in a row, points/steps siblings, a badge list.
     --rsd-gap     (medium) — between a heading and the content under it,
                              and between the two sides of a two-column split.
     --rsd-block   (large)  — between one major section and the next.
     Applied consistently below via .rsd-shell + .rsd-shell (large) and the
     existing/standardized heading-adjacent and item-grid rules (medium/
     small) — not per pattern, so every pattern inherits it automatically. */
  --rsd-gap-sm: clamp(.75rem, 1.5vw, 1.1rem);
  --rsd-gap:   clamp(1.25rem, 3vw, 2.5rem);
  --rsd-block: clamp(3rem, 6vw, 5.5rem);
  --rsd-radius: 4px;
}

/* ---------------------------------------------------------- reset */
*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }
body {
  margin: 0;
  background: var(--rsd-paper);
  color: var(--rsd-charcoal);
  font-family: var(--rsd-body);
  font-size: var(--rsd-t-base);
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
}
img, svg, video { max-width: 100%; height: auto; }
/* line-height 1.12 was already inside the requested 1.1-1.2 band; margin
   nudged 0.5em→0.6em for a touch more room under every heading (the
   "medium" gap before whatever follows — an intro paragraph, a card grid
   — reads a little pinched at 0.5em once the type scale above got wider). */
h1, h2, h3, h4 { font-family: var(--rsd-display); font-weight: 700; line-height: 1.12; letter-spacing: -0.02em; margin: 0 0 0.6em; }
p { margin: 0 0 1.1em; }
a { color: inherit; text-decoration-thickness: 1px; text-underline-offset: 3px; }
a:hover { text-decoration-color: var(--rsd-red); }
/* Applies to every interactive element by default. Four components used
   to override this with a tighter 1px offset (form/capture inputs, the
   modal close button) or a different colour entirely (.rsd-cta-btn, which
   used charcoal — presumably to avoid a red-on-red clash, but
   outline-offset:3px places the ring on the page background outside the
   button, not over its red fill, so that concern doesn't actually apply).
   All four removed rather than reconciled individually, so this one rule
   is genuinely the only definition of what focus looks like. */
:focus-visible { outline: 2px solid var(--rsd-red); outline-offset: 3px; }

.rsd-skip {
  position: absolute; left: -9999px; top: 0; z-index: 999;
  background: var(--rsd-charcoal); color: #fff; padding: .75rem 1rem;
}
.rsd-skip:focus { left: 0; }

/* ---------------------------------------------------------- layout */
.rsd-shell { width: min(100% - 2.5rem, var(--rsd-shell)); margin-inline: auto; }
.rsd-shell--narrow { width: min(100% - 2.5rem, 820px); }
/* overflow-x:clip guards against the one full-bleed element in this theme
   (.rsd-hero-service, further down this file) — its 100vw breakout can run
   ~15-17px past the true viewport edge on Windows/Chrome, where a classic
   (non-overlay) scrollbar's own width counts inside 100vw. clip (not
   hidden) so it doesn't create a new scroll container or block any
   position:sticky element inside main — there isn't one today, but hidden
   would silently break one if added later. */
.rsd-main { padding-block: clamp(1.5rem, 4vw, 2.75rem) var(--rsd-block); overflow-x: clip; }

/* Large-tier section rhythm — every pattern wraps each of its major
   sections in its own top-level .rsd-shell (hero, cards, cta, faq, all of
   it — see inc/patterns.php, inc/patterns-services-hub.php,
   inc/patterns-page-types.php), so one adjacent-sibling rule here gives
   every one of the 31 patterns consistent large spacing between sections
   without touching a single pattern file. Only fires between siblings —
   the first section in a page never gets extra top space it doesn't need.
   Where an inner element (e.g. .rsd-home-section) already carries its own
   var(--rsd-block) top margin, this collapses with it rather than adding
   to it (same value, adjoining margins collapse to the larger/equal one,
   not the sum) — so sections that already had rhythm keep exactly the
   same spacing; the ones that had none (.rsd-cta, .rsd-strip, a bare
   .rsd-shell wrapping a shortcode) are the ones this actually fixes. */
.rsd-shell + .rsd-shell { margin-top: var(--rsd-block); }

/* ---------------------------------------------------------- eyebrow + rule
   The red hairline under an eyebrow is the recurring structural device.
   It marks a measured claim — used only on sections that carry evidence.
   width:fit-content shrinks the box (and so the border-bottom hairline
   riding its bottom edge) to the text's own width, rather than the full
   block-level width the container would otherwise give it — every one of
   .rsd-eyebrow's host tags (p, h2, h3) is block-level by default, so
   without this the hairline always ran edge-to-edge regardless of how
   short the label was. The red tick (::after) is unaffected either way —
   it's already a fixed 2.5rem anchored to the box's own left edge, not
   sized relative to the box's width. */
/* .comment-reply-title (comments.php's own "Leave a response" heading,
   WordPress's own class — comment_form() has no 'class_title_reply' arg
   to set className directly) is added to THIS selector, not given a
   duplicate copy of these declarations elsewhere: the ask was "same
   style," and the only way that's genuinely guaranteed rather than a
   copy that can silently drift is one rule both share. */
.rsd-eyebrow,
.comment-reply-title {
  font-family: var(--rsd-display);
  font-weight: 600;
  font-size: var(--rsd-t-xs);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--rsd-muted);
  width: fit-content;
  margin: 0 0 0.85rem;
  padding-bottom: 0.5rem;
  border-bottom: 1px solid var(--rsd-line);
  position: relative;
}
.rsd-eyebrow::after,
.comment-reply-title::after {
  content: ""; position: absolute; left: 0; bottom: -1px;
  width: 2.5rem; height: 2px; background: var(--rsd-red);
}

/* ---------------------------------------------------------- header */
.rsd-header {
  position: sticky; top: 0; z-index: 60;
  background: color-mix(in srgb, var(--rsd-paper) 88%, transparent);
  border-bottom: 1px solid var(--rsd-line);
}
.rsd-header__inner {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--rsd-gap); min-height: 68px;
}
.rsd-logo { display: inline-flex; align-items: center; gap: .55rem; text-decoration: none; font-family: var(--rsd-display); font-weight: 700; letter-spacing: -0.02em; }
.rsd-logo__mark { width: 12px; height: 12px; background: var(--rsd-red); border-radius: 2px; transform: rotate(45deg); flex: none; }
.rsd-logo__text { font-size: 1.05rem; }

/* the_custom_logo() output — width driven by the Customizer's "Logo width"
   control (inc/logo-crop.php), not a fixed value, so it can be adjusted
   without re-uploading or re-cropping. */
.custom-logo { width: var(--rsd-logo-width, 180px); height: auto; }

.rsd-nav { display: flex; align-items: center; gap: var(--rsd-gap); }
.rsd-nav__list { display: flex; align-items: center; gap: 1.4rem; list-style: none; margin: 0; padding: 0; }
.rsd-nav__list a { text-decoration: none; font-size: var(--rsd-t-sm); font-weight: 500; padding-block: .4rem; display: inline-block; }
.rsd-nav__list > li { position: relative; }
.rsd-nav__list > li > a::after {
  content: ""; display: block; height: 2px; width: 0; background: var(--rsd-red);
  transition: width .18s ease;
}
.rsd-nav__list > li:hover > a::after,
.rsd-nav__list > li.current-menu-item > a::after { width: 100%; }

/* one level of dropdown, CSS only */
.rsd-nav__list .sub-menu {
  position: absolute; top: 100%; left: 0; min-width: 230px;
  background: var(--rsd-white); border: 1px solid var(--rsd-line);
  padding: .5rem; margin: 0; list-style: none; border-radius: var(--rsd-radius);
  display: none; box-shadow: 0 12px 28px rgba(26,26,28,.09);
}
.rsd-nav__list li:hover > .sub-menu,
.rsd-nav__list li:focus-within > .sub-menu { display: block; }
.rsd-nav__list .sub-menu a { display: block; padding: .45rem .6rem; }

/* min-height + inline-flex centering makes "consistent height" an explicit
   guarantee rather than an implicit consequence of padding/line-height
   math happening to land the same for every variant — solid and ghost
   already shared padding/radius/border-width via this one base rule, so
   there was no real divergence between them specifically, but nothing
   previously pinned the result to a real number either. 44px doubles as
   the accessible tap-target floor used elsewhere in this file
   (.rsd-hamburger, .rsd-call, the drawer's own button rule). */
.rsd-btn {
  display: inline-flex; align-items: center; justify-content: center;
  min-height: 44px; font-family: var(--rsd-display); font-weight: 600;
  font-size: var(--rsd-t-sm); padding: .6rem 1.1rem; border-radius: var(--rsd-radius);
  text-decoration: none; border: 1px solid transparent; cursor: pointer;
  transition: background .16s ease, color .16s ease;
}
.rsd-btn--solid { background: var(--rsd-red); color: #fff; }
.rsd-btn--solid:hover { background: var(--rsd-red-ink); }
.rsd-btn--ghost { border-color: var(--rsd-charcoal); color: var(--rsd-charcoal); }
.rsd-btn--ghost:hover { background: var(--rsd-charcoal); color: #fff; }

/* Client Login header button only — a quieter outline than .rsd-btn--ghost
   so it doesn't compete with the solid "Get a proposal" CTA. Scoped to this
   one button; .rsd-btn--ghost itself is unchanged everywhere else it's used. */
.rsd-btn--ghost.rsd-btn--muted {
  border-color: #6B6B6B; color: #4A4A4A;
  transition: border-color .16s ease, color .16s ease;
}
.rsd-btn--ghost.rsd-btn--muted:hover { background: transparent; border-color: #2F2F2F; color: #2F2F2F; }

/* Compact size — overrides padding/font-size/min-height only, inherits
   everything else from .rsd-btn. Opts out of the 44px floor above on
   purpose: this variant exists specifically for dense contexts (the
   footer's own proposal button) where the full tap-target minimum isn't
   the point — deliberately smaller, not an oversight. */
.rsd-btn--sm { padding: .45rem .85rem; font-size: var(--rsd-t-xs); min-height: 0; }

/* Ghost variant for dark surfaces (footer brand column) — same rgba(247,246,244,…)
   opacity convention already used by .rsd-footer__line/__copy/__markets borders. */
.rsd-btn--ghost-invert { border-color: rgba(247, 246, 244, .35); color: var(--rsd-paper); }
.rsd-btn--ghost-invert:hover { background: var(--rsd-paper); color: var(--rsd-charcoal); }

.rsd-burger { display: none; background: none; border: 0; padding: .6rem; cursor: pointer; }
.rsd-burger span { display: block; width: 22px; height: 2px; background: var(--rsd-charcoal); }
.rsd-burger span + span { margin-top: 6px; }

/* ---------------------------------------------------------- page head */
.rsd-crumbs { margin: 1.25rem 0 1.75rem; font-size: var(--rsd-t-xs); color: var(--rsd-muted); }
.rsd-crumbs ol { display: flex; flex-wrap: wrap; gap: .4rem; list-style: none; margin: 0; padding: 0; }
.rsd-crumbs li + li::before { content: "/"; margin-right: .4rem; color: var(--rsd-line); }
.rsd-crumbs a { text-decoration: none; }

.rsd-pagehead { margin-bottom: var(--rsd-gap); }
/* max-width is in ch, which is relative to the element's OWN font-size —
   16ch of the H1's own (large, fluid) type size, so the column stays
   proportionate to the type at every viewport rather than being capped by
   a value computed at some other, smaller font-size (see the removed
   .rsd-hero__body cap below, which was exactly that bug: 34ch measured at
   the body's ~1.0625rem paragraph size constrained a heading rendered up
   to 3.6rem, capping it to a column far narrower than the type needed). */
/* -0.03em, tighter than the -0.02em every other heading level inherits —
   large display type reads loose at the base tracking; the biggest size
   on the page is the one that needs it pulled in further. */
.rsd-h1 { font-size: var(--rsd-t-h1); max-width: 16ch; letter-spacing: -0.03em; }
.rsd-h2 { font-size: var(--rsd-t-h2); }
.rsd-h3 { font-size: var(--rsd-t-h3); }
.rsd-lede { font-size: var(--rsd-t-lg); color: var(--rsd-muted); max-width: 58ch; }
.rsd-meta { font-size: var(--rsd-t-sm); color: var(--rsd-muted); display: flex; gap: .5rem; flex-wrap: wrap; }

/* ---------------------------------------------------------- hero (asymmetric)
   align-items:start keeps the body column reading from the top naturally;
   align-self:center on the aside (below) only re-centers the shorter right
   panel within that same row, rather than leaving it pinned to the top of
   a much taller row with all the slack dumped below it as visible empty
   space. Row height itself now comes only from real content height (the
   H1 fix above is what actually shrinks that height — a 2-3 line headline
   instead of 5 — this alignment change is the remaining polish once the
   row is a sane height, not a substitute for that fix). */
.rsd-hero { display: grid; grid-template-columns: 7fr 5fr; gap: var(--rsd-gap); align-items: start; padding-block: var(--rsd-gap) var(--rsd-block); }
.rsd-hero__body { min-width: 0; }
.rsd-hero__aside {
  align-self: center;
  background: var(--rsd-white); border: 1px solid var(--rsd-line);
  border-radius: var(--rsd-radius); padding: var(--rsd-gap);
}
.rsd-hero__aside h2 { font-size: var(--rsd-t-base); }

/* ---------------------------------------------------------- hero (service, centred, full-bleed)
   redsprout-digital/section-hero-service (inc/patterns-hero-service.php).
   Deliberately centred — unlike the asymmetric .rsd-hero above, so a
   service page reads as its own page type rather than a copy of the
   homepage/interior hero.

   Full-bleed: this element carries the pattern's outer className directly
   (no wrapping .rsd-shell around it — see the pattern file), and is the
   one place in this theme that intentionally spans the true viewport
   width even though it's nested inside page.php's own width-capped
   .rsd-shell wrapper. width:100vw + the negative-margin/left:50% pair is
   the standard breakout technique — it resolves to the real viewport
   edges regardless of how many constrained ancestors sit above it (the
   maths cancels the ancestor's own centring offset out; verified by hand,
   not just copied). The one known side effect of vw-based breakout on
   Windows/Chrome, where the classic (non-overlay) scrollbar's own width is
   counted inside 100vw, is neutralised by .rsd-main's overflow-x:clip
   (this file, layout section, near .rsd-shell) rather than left to cause
   a horizontal scrollbar.

   A nested .rsd-shell (in the pattern markup) puts the actual container
   width/centring back for the content, exactly as it works everywhere
   else in this theme — only the background reaches edge to edge.

   Background: a radial wash, --rsd-paper at centre to an 8% color-mix()
   of --rsd-paper/--rsd-red at the edges — still the one scoped D-26
   gradient exception .rsd-cta-capture below already established, not a
   second one; only --rsd-paper/--rsd-red are mixed, D-23's six-value
   palette gains no seventh colour. `ellipse closest-side`, not the
   farthest-corner default: at 100vw wide but only ~400px tall, the
   farthest corner is enormous, so a farthest-corner ellipse would barely
   register within the visible band. closest-side sizes the ellipse's two
   axes independently — vertically tight (reaches full 8% at the actual
   top/bottom edge of this short band), horizontally very wide (changes
   gently, which is correct: the left/right "edges" here are the viewport
   edge itself, not a boundary against other content, so there's nothing
   for a strong horizontal vignette to contrast against anyway). */
.rsd-hero-service {
  width: 100vw; position: relative; left: 50%; right: 50%;
  margin-left: -50vw; margin-right: -50vw;
  padding-block: clamp(1.75rem, 3vw, 2.25rem);
  background: radial-gradient(ellipse closest-side at center, var(--rsd-paper) 0%, color-mix(in srgb, var(--rsd-paper), var(--rsd-red) 8%) 100%);
}
.rsd-hero-service > .rsd-shell {
  display: flex; flex-direction: column; align-items: center; text-align: center;
}

/* Breadcrumb-as-eyebrow (rsd_breadcrumbs_inline(), inc/breadcrumbs.php) —
   sits where the eyebrow normally would, styled the same size/role but
   with the mono family D-24 specifies for eyebrows (scoped to this one
   component; the sitewide .rsd-eyebrow above intentionally keeps Archivo
   and is out of scope here) and its own centred red calibration tick
   (D-25) — ::after can't reuse .rsd-eyebrow's left:0 version since this
   box is centred, not left-aligned, so the tick is centred under it too. */
.rsd-hero-crumbs {
  font-family: var(--rsd-mono); font-size: var(--rsd-t-xs); letter-spacing: .04em;
  color: var(--rsd-muted); width: fit-content; margin: 0 auto .6rem;
  padding-bottom: .5rem; border-bottom: 1px solid var(--rsd-line); position: relative;
}
.rsd-hero-crumbs ol {
  display: flex; flex-wrap: wrap; justify-content: center; gap: .4rem;
  list-style: none; margin: 0; padding: 0;
}
.rsd-hero-crumbs li + li::before { content: "/"; margin-right: .4rem; color: var(--rsd-line); }
.rsd-hero-crumbs a { text-decoration: none; color: inherit; }
.rsd-hero-crumbs a:hover { color: var(--rsd-red); }
.rsd-hero-crumbs::after {
  content: ""; position: absolute; left: 50%; bottom: -1px; transform: translateX(-50%);
  width: 2.5rem; height: 2px; background: var(--rsd-red);
}

/* Single-statement H1 — one line at 1440, wraps to at most two on mobile
   through natural reflow rather than a forced break. Smaller than the
   sitewide .rsd-h1 (clamp tops out at 4rem) on purpose: "confident, not
   enormous." 22ch keeps it from running the full container width even
   though the current copy is short enough not to need it, so a longer
   swapped-in headline still behaves. */
.rsd-hero-service .rsd-h1 {
  font-size: clamp(2.25rem, 3vw + 1.1rem, 3.25rem);
  max-width: 22ch; margin-inline: auto; margin-bottom: .5rem;
}
/* Description — dropped to body size (was --rsd-t-lg, reading at nearly
   the same weight as the headline) and capped at 55ch so it wraps to two
   lines rather than one long line or three short ones. Colour stays
   --rsd-muted via the shared .rsd-lede class. */
.rsd-hero-service .rsd-lede {
  font-size: var(--rsd-t-base); max-width: 55ch; margin-inline: auto; margin-bottom: 0;
}

/* Capture card — reuses the exact warm gradient + hairline border
   .rsd-cta-capture already established below (same computed colours, same
   D-26 exception the band background above also uses), as a single
   centred card rather than that section's two-column layout. Widened to
   820px so it's clearly the widest element and anchors the composition.
   Shadow value copied from .rsd-cta-btn's own resting shadow further down
   this file rather than inventing a new one. Padding/margin are tightened
   fixed values, not the fluid var(--rsd-gap) tokens used elsewhere — this
   band reads as one tight unit, not a card with independent breathing
   room. */
.rsd-capture-card {
  width: 100%; max-width: 820px; margin-top: 1.25rem;
  background: linear-gradient(to bottom, var(--rsd-paper), color-mix(in srgb, var(--rsd-paper), var(--rsd-red) 5%));
  border: 1px solid var(--rsd-line); border-radius: var(--rsd-radius);
  padding: 1rem 1.25rem;
  box-shadow: 0 8px 30px rgba(26, 26, 28, .06);
}
/* rsd_url_capture()'s own markup/CSS (.rsd-capture) is unchanged; this only
   removes its default margin/max-width, which don't belong inside a card
   that already has its own padding, and pins the row to a single line at
   rest — the field grows, the button stays its own width and a consistent
   52px height, same as any other input+button pair on one row. */
.rsd-capture-card .rsd-capture {
  flex-wrap: nowrap; max-width: none; width: 100%; margin-top: 0;
}
.rsd-capture-card .rsd-capture__input,
.rsd-capture-card .rsd-capture .rsd-btn { height: 52px; }
/* Uppercase label ("GET MY FREE PROPOSAL") needs its own letter-spacing —
   caps read cramped at .rsd-btn's normal-case tracking. Scoped to this
   card; every other .rsd-btn on the site keeps sentence case and is
   unaffected. */
.rsd-capture-card .rsd-capture .rsd-btn { flex: none; letter-spacing: .05em; }
/* Below 640px the field and button stack full-width rather than wrapping
   at whatever width the flex row happens to break — deliberate, not a
   side effect of .rsd-capture's own min-width. */
@media (max-width: 640px) {
  .rsd-capture-card .rsd-capture { flex-direction: column; }
  .rsd-capture-card .rsd-capture__input,
  .rsd-capture-card .rsd-capture .rsd-btn { width: 100%; }
}

/* This hero's own outer element carries "rsd-hero-service", not
   "rsd-shell" (full-bleed requires it not be one — see above), so the
   generic ".rsd-shell + .rsd-shell" large-tier rule can't see this
   transition. Restated here for the one relationship that rule was
   missing: spacing from this hero to whatever plain .rsd-shell section
   follows it. */
.rsd-hero-service + .rsd-shell { margin-top: var(--rsd-block); }

/* ---------------------------------------------------------- signature: sourced figure */
.rsd-figure {
  margin: 0 0 1.25rem; padding-left: 1rem;
  border-left: 2px solid var(--rsd-red);
}
.rsd-figure__value {
  font-family: var(--rsd-display); font-weight: 700;
  font-size: clamp(2rem, 3.5vw, 3rem); line-height: 1; letter-spacing: -0.03em;
  font-variant-numeric: tabular-nums;
}
.rsd-figure figcaption { display: block; margin-top: .5rem; }
.rsd-figure__label { display: block; font-size: var(--rsd-t-sm); }
.rsd-figure__source {
  display: block; margin-top: .25rem; font-style: normal;
  font-size: var(--rsd-t-xs); letter-spacing: .04em; text-transform: uppercase;
  color: var(--rsd-muted);
}

/* ---------------------------------------------------------- qualifier
   align-items:baseline lines up the two columns' own first line of text
   (each starts with an .rsd-eyebrow h2) exactly, so the pair reads as one
   deliberate grid rather than two independently-floating text blocks that
   happen to sit side by side. */
.rsd-qualifier { display: grid; width: 100%; box-sizing: border-box; gap: var(--rsd-gap); grid-template-columns: 1fr 1fr; align-items: baseline; margin-block: var(--rsd-block); }
.rsd-qualifier ul { margin: 0; padding-left: 1.1rem; }
.rsd-qualifier li { margin-bottom: .5rem; }
.rsd-qualifier--no .rsd-eyebrow::after { background: var(--rsd-charcoal); }

/* Generic two-column primitive for interior-page patterns — plain 1fr/1fr,
   no semantics of its own (unlike .rsd-qualifier/.rsd-points, which carry
   specific copy roles). Collapses in the same 900px breakpoint below.
   align-items:baseline — same reasoning as .rsd-qualifier above: both
   columns open with an h3, baseline-aligning them is what makes the pair
   read as a grid instead of two independently-placed text blocks. */
.rsd-cols-2 { display: grid; width: 100%; box-sizing: border-box; grid-template-columns: 1fr 1fr; align-items: baseline; gap: var(--rsd-gap); }

/* ---------------------------------------------------------- prose */
.rsd-prose { max-width: var(--rsd-read); }
/* scroll-margin-top: single-post.php's TOC links jump to these via #id —
   without this, .rsd-header's own position:sticky floats over the top of
   whatever the browser scrolls to, hiding the heading text underneath it.
   Same 96px offset as .rsd-article__aside's sticky top, for one consistent
   "space reserved for the floating header" value rather than two. */
.rsd-prose h2 { font-size: var(--rsd-t-h2); margin-top: 2.25em; scroll-margin-top: 96px; }
.rsd-prose h3 { font-size: var(--rsd-t-h3); margin-top: 1.75em; scroll-margin-top: 96px; }
.rsd-prose ul, .rsd-prose ol { padding-left: 1.25rem; }
.rsd-prose li { margin-bottom: .4rem; }
.rsd-prose blockquote {
  margin: 1.75rem 0; padding: .25rem 0 .25rem 1.25rem;
  border-left: 2px solid var(--rsd-red); font-size: var(--rsd-t-lg);
}
.rsd-prose table { width: 100%; border-collapse: collapse; margin: 1.75rem 0; font-size: var(--rsd-t-sm); }
.rsd-prose th, .rsd-prose td { text-align: left; padding: .7rem .8rem; border-bottom: 1px solid var(--rsd-line); vertical-align: top; }
.rsd-prose thead th { font-family: var(--rsd-display); font-size: var(--rsd-t-xs); letter-spacing: .08em; text-transform: uppercase; color: var(--rsd-muted); }
.rsd-prose code { font-size: .9em; background: var(--rsd-white); border: 1px solid var(--rsd-line); padding: .1em .35em; border-radius: 3px; }
.rsd-prose figure { margin: 1.75rem 0; }

/* ---------------------------------------------------------- lists / cards */
.rsd-postlist, .rsd-linklist { list-style: none; margin: 0; padding: 0; }
.rsd-postlist__item { padding-block: 1.5rem; border-top: 1px solid var(--rsd-line); }
.rsd-postlist__item h2 { margin: .35rem 0 .5rem; }
.rsd-postlist__item a { text-decoration: none; }
.rsd-postlist__item a:hover { text-decoration: underline; text-decoration-color: var(--rsd-red); }
.rsd-excerpt { color: var(--rsd-muted); margin: 0; max-width: 62ch; }

/* width:100% + box-sizing guard the grid against ever reading narrower
   than its container — belt-and-braces alongside auto-fit's own correct
   default behaviour (the actual number of tracks is already computed from
   the container's real width, so this is a safety net, not the primary
   mechanism). margin-top:var(--rsd-gap) is the medium heading→content
   tier. gap is the small tier — real spacing between cards now that each
   card is its own bordered box (see below), not the 1px shared-hairline
   mesh this used to be (grid gap:1px + a background colour showing
   through as dividers, every card sat flush against its neighbours with
   no room to lift independently on hover). overflow is no longer hidden
   here — a shared-mesh grid needed it to clip corners to one rounded
   boundary; individual cards carry their own radius now, and a hidden
   overflow on the parent would clip the hover-lift transform below. */
.rsd-cards {
  display: grid; width: 100%; box-sizing: border-box;
  gap: var(--rsd-gap-sm);
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  margin-top: var(--rsd-gap);
}
/* Same padding value as .rsd-hero__aside — one padding for every card-like
   surface (was 1.4rem here vs 1.5rem there: two numbers for the same kind
   of surface, tightened to one, var(--rsd-gap), which also makes both
   fluid instead of fixed). The full-bleed .rsd-cta band keeps its own,
   deliberately more generous padding below — a section-width band reads
   differently from a grid card, not "the same kind of surface" this rule
   is about.
   border+border-radius replace the old shared-mesh trick with a real,
   individual hairline per card — needed so hover (below) can act on one
   card without disturbing its neighbours. Hover is restrained on purpose:
   the border tone deepens (color-mix, not a new token — the same
   "derived, not invented" approach .rsd-header already uses for its own
   translucency) and the card lifts 2px; no shadow, no scale, gated by
   prefers-reduced-motion like every other transition in this file. */
.rsd-cards > * {
  background: var(--rsd-white); border: 1px solid var(--rsd-line);
  border-radius: var(--rsd-radius); padding: var(--rsd-gap);
}
@media (prefers-reduced-motion: no-preference) {
  .rsd-cards > * { transition: border-color .18s ease, transform .18s ease; }
}
.rsd-cards > *:hover {
  border-color: color-mix(in srgb, var(--rsd-line), var(--rsd-charcoal) 35%);
  transform: translateY(-2px);
}
.rsd-card__title { font-size: var(--rsd-t-base); margin-bottom: .4rem; }
.rsd-card__title a { text-decoration: none; }
.rsd-card p { color: var(--rsd-muted); font-size: var(--rsd-t-sm); margin: 0; }

.rsd-linklist li { border-top: 1px solid var(--rsd-line); }
.rsd-linklist a { display: block; padding: .9rem 0; text-decoration: none; font-family: var(--rsd-display); font-weight: 600; }
.rsd-linklist a:hover { color: var(--rsd-red); }

/* ---------------------------------------------------------- FAQ */
.rsd-faq { margin-block: var(--rsd-block); max-width: var(--rsd-read); }
.rsd-faq details { border-top: 1px solid var(--rsd-line); padding: 1rem 0; }
.rsd-faq summary { cursor: pointer; font-family: var(--rsd-display); font-weight: 600; list-style: none; display: flex; justify-content: space-between; gap: 1rem; }
.rsd-faq summary::-webkit-details-marker { display: none; }
.rsd-faq summary::after { content: "+"; color: var(--rsd-red); font-size: 1.25rem; line-height: 1; }
.rsd-faq details[open] summary::after { content: "–"; }
.rsd-faq details > p { margin: .75rem 0 0; color: var(--rsd-muted); }

/* ---------------------------------------------------------- market switcher */
.rsd-markets { margin-block: var(--rsd-block) 0; padding-top: 1.25rem; border-top: 1px solid var(--rsd-line); font-size: var(--rsd-t-sm); }
.rsd-markets__label { color: var(--rsd-muted); margin-right: .75rem; }
.rsd-markets ul { display: inline-flex; flex-wrap: wrap; gap: 1rem; list-style: none; margin: 0; padding: 0; }

/* ---------------------------------------------------------- CTA band */
.rsd-cta {
  margin-block: var(--rsd-block) 0; padding: clamp(1.75rem, 4vw, 3rem);
  background: var(--rsd-charcoal); color: var(--rsd-paper); border-radius: var(--rsd-radius);
}
.rsd-cta h2 { color: #fff; max-width: 22ch; }
.rsd-cta p { color: rgba(247,246,244,.72); max-width: 52ch; }
.rsd-cta .rsd-eyebrow { color: rgba(247,246,244,.6); border-color: rgba(247,246,244,.18); }

/* ---------------------------------------------------------- CTA capture band
   Distinct from .rsd-cta above: a light warm-tinted surface + hairline
   border rather than the dark charcoal band, and a real asymmetric
   two-column layout (~55/45, 11fr/9fr) rather than single-column centred
   content — copy left, the existing URL-capture field right. No
   margin-block of its own on purpose: .rsd-shell + .rsd-shell (this file,
   near the top) already gives the wrapping .rsd-shell correct large-tier
   spacing from whatever precedes it, and because this element has its own
   border, a margin set directly on it would NOT collapse through that
   border the way an unbordered child's margin does elsewhere in this file
   — adding one here would double the gap rather than share it.

   Background is a vertical gradient, plain paper → a color-mix() of
   paper and brand red, not a new hex value: the top stop is --rsd-paper
   outright (a definite colour, not "transparent" inheriting whatever sits
   behind it); the bottom stop is 5% --rsd-red mixed into --rsd-paper —
   deliberately red now, not the neutral --rsd-line this used before,
   because this band holds the form and the solid-red button: a neutral
   tint doesn't distinguish an action surface from any other section, a
   faint warm-red one says "something happens here."

   Computed before choosing, same discipline as the neutral version this
   replaced: paper is #F7F6F4 (lum≈246). At 5% red (#C40808 mixed in),
   the result is #F4EAE8 (lum≈236, an ≈10-point/≈4% delta) — R barely
   moves (247→244) while G and B both drop by roughly equal amounts
   (246→234, 244→232), which is what reads as WARM rather than pink: true
   pink needs R and B both elevated relative to G; here G actually sits
   slightly above B, if anything a warm-yellow lean, not a blue/magenta
   one. 5% sits in the middle of the requested 4-6% range — enough to
   read as intentional warmth, nowhere near "obviously red."

   The button stays the strongest red element on the band by a wide
   margin regardless: --rsd-red itself has a luminance of ≈48 (very dark,
   fully saturated) against this tint's ≈236 (very light, barely
   saturated) — a ≈188-point gap, before even accounting for the
   saturation difference between "5% mixed into near-white" and "100%
   pure." The background cannot credibly compete with the button at
   these values.

   This is the one deliberate, scoped exception to D-26's "no gradients"
   — everywhere else on the site still has none. Contrast is unaffected:
   even at the deepest point (≈236 luminance, still a near-white),
   --rsd-charcoal (heading) and --rsd-muted (lede) both keep effectively
   the same contrast they already have against plain --rsd-paper
   elsewhere in this file, and the input sits on its own explicit
   --rsd-white, independent of the band's gradient beneath it either way.

   Padding reuses .rsd-cta's own value (clamp(1.75rem,4vw,3rem)) rather
   than the card padding (var(--rsd-gap)) this used before — the same
   "a full-width band reads differently from a grid card" reasoning
   .rsd-cards' own comment already states, applied consistently here too.

   align-items:start, not baseline: the right column's first child is a
   <form> (label, input, button) rather than plain text, and true CSS
   baseline alignment against a form control is inconsistent across
   browsers/UA styles. start gives both columns a shared, deterministic
   top edge — the heading and the field visibly begin at the same height,
   which is the actual goal ("not floating at different heights"). */
.rsd-cta-capture {
  display: grid; grid-template-columns: 11fr 9fr; gap: var(--rsd-gap);
  align-items: start;
  background: linear-gradient(to bottom, var(--rsd-paper), color-mix(in srgb, var(--rsd-paper), var(--rsd-red) 5%));
  border: 1px solid var(--rsd-line);
  border-radius: var(--rsd-radius); padding: clamp(1.75rem, 4vw, 3rem);
}
/* h2 scale explicitly — a bare <h2> carries no font-size of its own in
   this theme (only the .rsd-h2 class does; h1-h4's shared element rule
   sets weight/line-height/tracking but never size), so without this the
   heading rendered at the browser's plain UA default, which read as body
   text rather than "the second-biggest thing on the page." 18ch keeps it
   to two confident lines instead of one long one; -0.03em is the same
   tighter large-size tracking .rsd-h1 already uses, applied here because
   at --rsd-t-h2's now-widened size (previous pass) this counts as "large"
   too. */
.rsd-cta-capture h2 {
  font-size: var(--rsd-t-h2); max-width: 18ch; letter-spacing: -0.03em;
  margin-bottom: .5rem;
}
/* .rsd-lede already sets font-size:var(--rsd-t-lg) (restated explicitly
   below anyway, so this requirement is visibly satisfied here rather than
   only cross-referenced) — what it doesn't set is a line-height of its
   own (it inherits the sitewide 1.6, tuned for body copy, not a larger
   lede-scale line) or a width narrow enough to read as two lines rather
   than run the column's full ~58ch default. 1.5 is tighter than body's
   1.6 — larger type needs proportionally less line-height to still read
   as comfortable, the same reasoning .rsd-h1's own tighter tracking is
   built on. 42ch, not .rsd-lede's default 58ch: at this column's real
   width (~55% of the shell), 58ch was closer to the column's own edge
   than to a genuine two-line wrap. */
.rsd-cta-capture .rsd-lede {
  font-size: var(--rsd-t-lg); line-height: 1.5; max-width: 42ch;
}
/* rsd_url_capture()'s own markup/CSS (.rsd-capture) is an unchanged,
   reused field — everything below only overrides how it LAYS OUT and
   SIZES in this one context, scoped under .rsd-cta-capture so the
   default behaviour it keeps everywhere else (hero aside, services-hub
   closing CTA) is untouched.

   flex-direction:column flips the container's main axis from horizontal
   to vertical — .rsd-capture__input's own base rule sets
   flex: 1 1 14rem, written for the ROW layout it normally sits in, where
   14rem is a WIDTH. In a column-direction container that same flex-basis
   is read against the main axis, which is now HEIGHT — a 14rem-tall
   (224px) input was the actual bug, not a rendering fluke: this file's
   own scoped column override broke an assumption the shared rule never
   had to account for before. flex:none here removes that entirely, and
   height is set explicitly instead. */
.rsd-cta-capture .rsd-capture {
  flex-direction: column; max-width: none; gap: var(--rsd-gap-sm);
}
.rsd-cta-capture .rsd-capture__input {
  flex: none; width: 100%; height: 52px;
  padding-block: 0; padding-inline: 1rem;
}
/* height matches the input exactly, not just the sitewide 44px button
   floor — "so they read as a pair," per spec — and centres its own label
   the same way .rsd-btn already centres text everywhere else (inline-flex
   + align-items:center, inherited from the base rule; only height is
   added here). */
.rsd-cta-capture .rsd-capture .rsd-btn { width: 100%; height: 52px; }

/* ---------------------------------------------------------- footer */
.rsd-footer { background: var(--rsd-charcoal); color: var(--rsd-paper); padding-block: var(--rsd-block) 2rem; }
.rsd-footer a { color: inherit; text-decoration: none; }
.rsd-footer .rsd-eyebrow { color: rgba(247,246,244,.55); border-color: rgba(247,246,244,.15); }
.rsd-footer__top { display: grid; grid-template-columns: 2fr 1fr 1fr 1fr; gap: var(--rsd-gap); }
.rsd-footer__line { color: rgba(247,246,244,.66); max-width: 30ch; font-size: var(--rsd-t-sm); }
.rsd-footer__list { list-style: none; margin: 0; padding: 0; font-size: var(--rsd-t-sm); }
.rsd-footer__list li { margin-bottom: .5rem; }
.rsd-footer__markets { margin-top: var(--rsd-gap); padding-top: 1.5rem; border-top: 1px solid rgba(247,246,244,.14); }
.rsd-footer__markets ul { display: flex; flex-wrap: wrap; gap: 1.5rem; list-style: none; margin: 0; padding: 0; font-size: var(--rsd-t-sm); }
.rsd-footer__ccy { color: rgba(247,246,244,.45); font-size: var(--rsd-t-xs); margin-left: .35rem; }
/* Copyright only now — no legal menu in this row (removed; a legal/
   policy link belongs in a footer column like any other link). Kept as
   flex/space-between rather than simplified to a plain block: the row
   still needs the top border + spacing treatment consistent with
   .rsd-footer__markets above it, and space-between with a single child
   is harmless (it just left-aligns). */
.rsd-footer__base { display: flex; flex-wrap: wrap; justify-content: space-between; gap: 1rem; margin-top: var(--rsd-gap); padding-top: 1.25rem; border-top: 1px solid rgba(247,246,244,.14); font-size: var(--rsd-t-xs); }
.rsd-footer__copy { margin: 0; color: rgba(247,246,244,.6); max-width: 60ch; }

/* ---------------------------------------------------------- search / misc */
.rsd-search { margin-block: var(--rsd-gap); max-width: 480px; }
.rsd-search__row { display: flex; gap: .5rem; }
.rsd-search input {
  flex: 1; padding: .6rem .75rem; font: inherit; font-size: var(--rsd-t-sm);
  border: 1px solid var(--rsd-line); border-radius: var(--rsd-radius); background: var(--rsd-white);
}
.rsd-empty { color: var(--rsd-muted); }
.rsd-related { margin-block: var(--rsd-block) 0; padding-top: 1.5rem; border-top: 1px solid var(--rsd-line); }
.rsd-related ul { list-style: none; margin: 0; padding: 0; }
.rsd-related li { border-bottom: 1px solid var(--rsd-line); }
.rsd-related a { display: block; padding: .75rem 0; text-decoration: none; font-family: var(--rsd-display); font-weight: 600; }
.rsd-hero-img { margin: 0 0 var(--rsd-gap); }

/* ---------------------------------------------------------- article (blog post)
   single-post.php. Two-column: prose body + a sidebar carrying the table
   of contents and author credibility. Narrower split than .rsd-hero's 7/5
   (2fr/1fr ≈ 8/4) since this sidebar only ever holds two short cards, not
   a full hero panel — both cards reuse .rsd-hero__aside's existing surface
   (white, hairline border, no shadow) rather than a new component; D-26
   allows a shadow/gradient only on the two patterns that already carry
   one (the CTA capture band's gradient, nothing here), so this stays flat
   on purpose. */
.rsd-article { display: grid; grid-template-columns: 2fr 1fr; gap: var(--rsd-gap); align-items: start; }
.rsd-article__body { min-width: 0; }
/* position:sticky, not JS scroll-spy — the sidebar staying in view while
   the reader scrolls is the entire value; highlighting the active section
   would need an IntersectionObserver for a marginal gain, which is exactly
   the "nothing beyond what's strictly necessary" this was told to avoid.
   top is an approximate offset under the floating sticky pill header
   (.rsd-pill, ~68px min-height plus its own breathing room) — a few px of
   slack here is harmless, unlike overlap. Disabled below the 900px
   collapse (next rule block) since a stacked, full-width sidebar has
   nothing meaningful to stay pinned against. */
.rsd-article__aside { display: flex; flex-direction: column; gap: var(--rsd-gap-sm); position: sticky; top: 96px; }
/* .rsd-hero__aside's own align-self:center assumes a grid context sized
   for a full hero row; here it would shrink the card to its content width
   instead of filling this flex column, which is the opposite of what a
   sidebar card should do. */
.rsd-article__aside .rsd-hero__aside { align-self: stretch; }

/* Table of contents — one flat <ul>, no icon (nothing here needs one). No
   active-section highlighting — see the sticky comment above for why.
   H3 sub-items get an indent/rule via a modifier class on the <li> itself
   rather than an actual nested <ul> — a <ul> can only validly appear
   inside an <li> of its parent list, and building that correctly from a
   single linear heading sequence needs real tree logic for one cosmetic
   indent; flat li's with a class do the same visual job without it. */
.rsd-toc { margin: 0; padding: 0; list-style: none; }
.rsd-toc li + li { margin-top: .5rem; }
.rsd-toc a { display: block; font-size: var(--rsd-t-sm); text-decoration: none; }
.rsd-toc a:hover { text-decoration: underline; text-decoration-color: var(--rsd-red); }
.rsd-toc__sub { padding-left: .85rem; border-left: 1px solid var(--rsd-line); }
.rsd-toc__sub a { font-size: var(--rsd-t-xs); color: var(--rsd-muted); }

/* Author credibility — real WP author data (name, avatar, bio), never
   invented (D-21). */
.rsd-author { display: flex; gap: .85rem; align-items: flex-start; }
/* get_avatar() is requested at 88px (template) for a crisp image on 2x
   displays; constrained to 44px here so it actually displays at that
   size rather than the native 88px the <img> tag's own attributes carry. */
.rsd-author img { border-radius: 50%; flex: none; width: 44px; height: 44px; }
.rsd-author__name { font-family: var(--rsd-display); font-weight: 600; font-size: var(--rsd-t-base); margin: 0 0 .25rem; }
.rsd-author__bio { font-size: var(--rsd-t-sm); color: var(--rsd-muted); margin: 0 0 .5rem; }
.rsd-author__link { font-size: var(--rsd-t-xs); }

/* Read Next — redsprout-digital/read-next pattern
   (inc/patterns-read-next.php) and single-post.php's automatic placement;
   one render function (rsd_read_next_content()) backs both, so this is
   the only markup shape to style. Reuses .rsd-cards/.rsd-cards > * exactly
   as every other card grid in this theme does — including that rule's own
   "no shadow, no scale" hover (border-color deepens, translateY(-2px)
   only); nothing here adds a second hover treatment. Same rhythm/divider
   convention as .rsd-related above (margin-block + border-top). */
.rsd-read-next { margin-block: var(--rsd-block) 0; padding-top: 1.5rem; border-top: 1px solid var(--rsd-line); }
/* Explicit 3/2/1 columns, not .rsd-cards' own auto-fit default — scoped to
   this context only. max-width:1024px/768px (inclusive of both numbers
   themselves), not .rsd-pill's own 1023px/767px elsewhere in this file —
   those read as "below 1024/768," one pixel short of the actual required
   values, "two columns AT 1024px, one column AT 768px." Caught and fixed
   here and on .rsd-blog-grid below, which had the identical bug from the
   identical requirement wording. */
.rsd-read-next .rsd-cards { grid-template-columns: repeat(3, 1fr); }
@media (max-width: 1024px) { .rsd-read-next .rsd-cards { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 768px)  { .rsd-read-next .rsd-cards { grid-template-columns: 1fr; } }
/* overflow:hidden scoped to these cards only (not the shared .rsd-cards > *
   rule, which deliberately stays visible so the hover-lift on OTHER card
   grids isn't clipped by their own grid container) — here it's what makes
   the bled thumbnail respect the card's own rounded top corners. */
.rsd-read-next .rsd-cards > * { overflow: hidden; }
.rsd-read-next .rsd-card__thumb { display: block; margin: calc(var(--rsd-gap) * -1) calc(var(--rsd-gap) * -1) var(--rsd-gap-sm); }
.rsd-read-next .rsd-card__thumb img { display: block; width: 100%; height: auto; }
.rsd-read-next .rsd-badge { margin-bottom: .6rem; }
.rsd-read-next .rsd-card__meta { color: var(--rsd-muted); font-size: var(--rsd-t-xs); margin: .4rem 0 0; }

/* ---------------------------------------------------------- comments
   comments.php. WordPress's default comment/form markup carries no theme
   styling at all until this block — 'comment-list'/'comment-form' html5
   support (inc/setup.php) puts it on the modern, predictable markup shape
   these selectors target (Walker_Comment's html5_comment(), and
   comment_form()'s own html5-mode field wrappers). */
.rsd-comments { margin-block: var(--rsd-block) 0; padding-top: 1.5rem; border-top: 1px solid var(--rsd-line); }
.rsd-comments__list, .rsd-comments__list .children { list-style: none; margin: 0; padding: 0; }
/* Hairline separator between each comment, not a bordered box — border-top
   only, no side/bottom borders, no background, no radius. First comment
   gets none (nothing above it to divide from); replies (.children) get a
   left rule + indent instead of their own top hairline, so a threaded
   reply reads as "part of" the comment above it, not a new hairline block. */
.rsd-comments__list > li { padding-top: var(--rsd-gap-sm); margin-top: var(--rsd-gap-sm); border-top: 1px solid var(--rsd-line); }
.rsd-comments__list > li:first-child { padding-top: 0; margin-top: 0; border-top: 0; }
.rsd-comments__list .children { margin-top: var(--rsd-gap-sm); padding-left: 1rem; border-left: 1px solid var(--rsd-line); }
.rsd-comments__list .children > li { border-top: 0; padding-top: var(--rsd-gap-sm); margin-top: var(--rsd-gap-sm); }
.rsd-comments__list .children > li:first-child { padding-top: 0; margin-top: 0; }
.comment-author { display: flex; align-items: center; gap: .6rem; margin-bottom: .3rem; }
.comment-author .avatar { border-radius: 50%; display: block; }
/* Commenter name in Archivo — the same "structural label" family every
   heading/eyebrow/card-title in this theme already uses, not the body
   font WP's default markup inherits with no styling at all. */
.comment-author .fn { font-family: var(--rsd-display); font-weight: 600; font-style: normal; }
.comment-author .says { display: none; } /* "says:" — decorative WP boilerplate, no information */
/* Date in Plex Mono at small size, per D-24 (figures/eyebrows/source
   lines/table numerals are the mono family's job sitewide — a timestamp
   is exactly that kind of small, precise metadata). */
.comment-metadata { font-family: var(--rsd-mono); font-size: var(--rsd-t-xs); color: var(--rsd-muted); margin-bottom: .6rem; }
.comment-metadata a { text-decoration: none; }
.comment-content p:last-child { margin-bottom: 0; }
.comment-awaiting-moderation { display: block; margin-top: .5rem; font-size: var(--rsd-t-xs); color: var(--rsd-muted); }
.comment-reply-link {
  display: inline-block; margin-top: .5rem; font-family: var(--rsd-display); font-weight: 600;
  font-size: var(--rsd-t-xs); text-decoration: none; color: var(--rsd-muted);
}
.comment-reply-link:hover { color: var(--rsd-red); }

/* Comment form — same field treatment as rsd_url_capture()'s own
   .rsd-capture__input (hairline border, --rsd-radius, --rsd-white
   background); the red focus ring comes free from the sitewide
   ::focus-visible rule (top of this file), same as that field — no
   second focus style invented here. */
#commentform p { margin-bottom: 1rem; }
#commentform label { display: block; font-family: var(--rsd-display); font-weight: 600; font-size: var(--rsd-t-sm); margin-bottom: .4rem; }
#commentform textarea,
#commentform input[type="text"],
#commentform input[type="email"],
#commentform input[type="url"] {
  width: 100%; box-sizing: border-box; font: inherit; font-size: var(--rsd-t-sm);
  padding: .65rem .85rem; border: 1px solid var(--rsd-line); border-radius: var(--rsd-radius);
  background: var(--rsd-white); color: var(--rsd-charcoal);
}
/* Same native-:invalid neutralisation as .rsd-capture__input, and for the
   same reason — type="email"/"url" fields (html5 comment-form support
   turns these on) can carry the browser's own unrequested invalid-state
   decoration before the visitor has typed anything. */
#commentform input:invalid { border-color: var(--rsd-line); box-shadow: none; outline: none; }
#commentform textarea { min-height: 9rem; resize: vertical; }
.comment-notes, .comment-form-legend, .logged-in-as { color: var(--rsd-muted); font-size: var(--rsd-t-sm); }
.form-submit { margin-top: 1.25rem; margin-bottom: 0; }

.pagination { margin-top: var(--rsd-block); font-size: var(--rsd-t-sm); }
.pagination .nav-links { display: flex; gap: .75rem; flex-wrap: wrap; }
.pagination .page-numbers { padding: .35rem .7rem; border: 1px solid var(--rsd-line); border-radius: var(--rsd-radius); text-decoration: none; }
.pagination .current { background: var(--rsd-charcoal); color: #fff; border-color: var(--rsd-charcoal); }

/* ---------------------------------------------------------- blog index (home.php)
   Featured post — full width, larger than the grid cards below it: a
   bigger image size (rsd-wide, requested at asset-registration time in
   home.php, not scaled up here) and a bigger title (--rsd-t-h3, the grid
   cards use .rsd-card__title's --rsd-t-base). Asymmetric split, not a
   plain two-column 1fr/1fr — the image carries more visual weight than
   the text column here, same asymmetric instinct as .rsd-hero. */
.rsd-blog-featured {
  display: grid; grid-template-columns: 3fr 2fr; gap: var(--rsd-gap); align-items: center;
  margin-top: var(--rsd-block); padding-bottom: var(--rsd-block); border-bottom: 1px solid var(--rsd-line);
}
.rsd-blog-featured__thumb { display: block; overflow: hidden; border-radius: var(--rsd-radius); }
.rsd-blog-featured__thumb img { display: block; width: 100%; height: auto; }
.rsd-blog-featured__title { font-size: var(--rsd-t-h3); margin: .6rem 0 .5rem; }
.rsd-blog-featured__title a { text-decoration: none; }
/* One-line excerpt via actual truncation (overflow/ellipsis), not just a
   short wp_trim_words() word count — guarantees a single visual line
   regardless of how long an individual excerpt's words happen to run. */
.rsd-blog-featured__excerpt {
  color: var(--rsd-muted); font-size: var(--rsd-t-base); margin: 0 0 .75rem;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.rsd-blog-featured__meta { margin: 0 0 .85rem; }
.rsd-blog-featured__more { display: inline-block; }

/* Featured-image placeholder (rsd_blog_thumb(), inc/blog-index.php) — a
   post with no featured image gets this instead of a broken <img> or
   empty space, per spec: plain paper surface, hairline border, and a
   small tick-on-a-hairline mark centred in it, the same red-tick-on-a-
   hairline motif .rsd-eyebrow::after already uses elsewhere (D-25's
   calibration language), not a new visual element invented for this one
   case. aspect-ratio reserves the same shape a real image would occupy —
   default matches rsd-card's own 640:400 (8:5); --wide matches rsd-wide's
   1200:675 (16:9) for the featured slot. Sits inside the same
   .rsd-card__thumb/.rsd-blog-featured__thumb wrapper a real <img> would,
   so it gets the identical bled, corner-clipped treatment automatically —
   nothing about the placeholder's own layout is a special case. */
.rsd-thumb-placeholder {
  display: flex; align-items: center; justify-content: center;
  width: 100%; aspect-ratio: 8 / 5; background: var(--rsd-paper); border: 1px solid var(--rsd-line);
}
.rsd-thumb-placeholder--wide { aspect-ratio: 16 / 9; }
.rsd-thumb-placeholder__mark { position: relative; width: 3rem; height: 1px; background: var(--rsd-line); }
.rsd-thumb-placeholder__mark::after {
  content: ""; position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%);
  width: 2px; height: 14px; background: var(--rsd-red);
}

/* Category filter pills — reuses .rsd-badge exactly (design system §12),
   no new pill component. flex-wrap alone is what gives "wraps cleanly on
   mobile" — a row of inline-sized pills needs no breakpoint-specific
   rule to reflow correctly. */
.rsd-blog-filters { display: flex; flex-wrap: wrap; gap: .6rem; margin-top: var(--rsd-gap); }

/* Post grid — same shape as redsprout-digital/read-next's own card grid
   (inc/patterns-read-next.php): explicit 3/2/1 columns scoped to this
   context rather than .rsd-cards' own auto-fit default, the same
   max-width:1024px/768px breakpoints (see the note on .rsd-read-next's
   own rule above — not .rsd-pill's 1023px/767px, which are one pixel
   short of the actual required values), the same bled-thumbnail
   treatment. Kept as its own scoped rule set rather than sharing one
   class with .rsd-read-next — similar shape, different content (this
   grid also carries an excerpt and a full author/date/read-time line),
   so a forced shared abstraction would need as many overrides as it
   saved. */
.rsd-blog-grid { margin-top: var(--rsd-block); }
.rsd-blog-grid .rsd-cards { grid-template-columns: repeat(3, 1fr); }
@media (max-width: 1024px) { .rsd-blog-grid .rsd-cards { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 768px)  { .rsd-blog-grid .rsd-cards { grid-template-columns: 1fr; } }
.rsd-blog-grid .rsd-cards > * { overflow: hidden; }
/* Card surface restated directly on .rsd-card here, not left solely to
   the .rsd-cards > * inheritance path above (design system, this file) —
   a belt-and-braces fix: if a card was ever rendering as unstyled loose
   text rather than a bordered/padded box, the likely cause is that
   inheritance path not applying for some reason, and this more specific
   selector (two classes, beats a universal-child selector on specificity
   regardless of source order) guarantees the same result either way. */
.rsd-blog-grid .rsd-card {
  background: var(--rsd-white); border: 1px solid var(--rsd-line);
  border-radius: var(--rsd-radius); padding: var(--rsd-gap);
}
.rsd-blog-grid .rsd-card__thumb { display: block; margin: calc(var(--rsd-gap) * -1) calc(var(--rsd-gap) * -1) var(--rsd-gap-sm); }
.rsd-blog-grid .rsd-card__thumb img { display: block; width: 100%; height: auto; }
.rsd-blog-grid .rsd-badge { display: inline-block; margin-bottom: .6rem; }
.rsd-blog-grid__excerpt {
  color: var(--rsd-muted); font-size: var(--rsd-t-sm); margin: 0 0 .6rem;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* Author + date + read time in Plex Mono, per D-24 (figures/eyebrows/
   source lines/table numerals are the mono family's job sitewide) —
   scoped here only; redsprout-digital/read-next's own .rsd-card__meta
   (just a reading-time figure, no byline) is untouched. */
.rsd-blog-grid .rsd-card__meta { font-family: var(--rsd-mono); font-size: var(--rsd-t-xs); color: var(--rsd-muted); margin: 0 0 .85rem; }
.rsd-blog-grid .rsd-card__more { display: inline-block; }

/* Pagination — scoped override, not a change to the shared .pagination
   rule above (which archive.php's and index.php's own pagination still
   use unchanged). Base hairline border on .page-numbers already comes
   from that shared rule; this only adds the mono numerals and swaps
   current-page from charcoal to brand red. No arrow icons: home.php
   passes explicit prev_text/next_text strings (same values index.php/
   archive.php already use), so there's no default WordPress glyph to
   suppress here in the first place. */
.rsd-blog-pagination .page-numbers { font-family: var(--rsd-mono); }
.rsd-blog-pagination .page-numbers.current { background: var(--rsd-red); border-color: var(--rsd-red); color: #fff; }

/* ---------------------------------------------------------- responsive */
@media (max-width: 900px) {
  .rsd-hero, .rsd-qualifier, .rsd-cols-2, .rsd-cta-capture, .rsd-article, .rsd-blog-featured { grid-template-columns: 1fr; }
}
/* .rsd-article__aside: stacked full-width has nothing to stay pinned
   against (position:static), and order:-1 puts the TOC ahead of the full
   article body in the stacked reading order — useful for jumping to a
   section on a long article read on a phone; on desktop it's a sidebar,
   so source order there is irrelevant to what the reader sees first. */
@media (max-width: 900px) {
  .rsd-article__aside { position: static; order: -1; }
}
@media (max-width: 720px) {
  .rsd-burger { display: block; }
  .rsd-nav {
    display: none; position: absolute; inset: 68px 0 auto; flex-direction: column;
    align-items: stretch; gap: 0; background: var(--rsd-paper);
    border-bottom: 1px solid var(--rsd-line); padding: .75rem 1.25rem 1.25rem;
  }
  .rsd-nav.is-open { display: flex; }
  .rsd-nav__list { flex-direction: column; align-items: stretch; gap: 0; }
  .rsd-nav__list > li { border-bottom: 1px solid var(--rsd-line); }
  .rsd-nav__list a { padding-block: .7rem; }
  .rsd-nav__list .sub-menu { position: static; display: block; border: 0; box-shadow: none; background: transparent; padding: 0 0 .5rem .75rem; }
  .rsd-nav__cta { margin-top: .9rem; }
}

/* ---------------------------------------------------------- footer
   responsive — four columns desktop (unconditional base rule, above),
   two on tablet, one on mobile. Own dedicated queries (not reusing the
   900/720 breakpoints above, which are unrelated legacy nav rules) at
   1199/767, chosen so the four widths this was tested at each land in
   the intended bucket: 1440 → base (4 cols), 1024 → ≤1199 (2 cols),
   768 → ≤1199 (2 cols, upper edge of "tablet"), 375 → ≤767 (1 col). */
@media (max-width: 1199px) {
  .rsd-footer__top { grid-template-columns: 1fr 1fr; }
}
@media (max-width: 767px) {
  .rsd-footer__top { grid-template-columns: 1fr; }
  .rsd-footer { padding-block: var(--rsd-block) 1.5rem; }
  .rsd-footer__markets ul { gap: 1rem; }
  /* Copyright + legal menu: side-by-side (space-between) above this
     width relies on flex-wrap alone, which can look like an accidental
     line-break rather than a deliberate stack. Explicit column stack
     below 767px instead. */
  .rsd-footer__base { flex-direction: column; align-items: flex-start; gap: .75rem; }
  /* One step down the existing type scale — footer text doesn't need
     body-copy size on a narrow screen. */
  .rsd-footer__line { font-size: var(--rsd-t-xs); }
  .rsd-footer__col,
  .rsd-footer__col a,
  .rsd-footer__col li,
  .rsd-footer__col p,
  .rsd-footer__col .wp-block-navigation,
  .rsd-footer__col .wp-block-navigation-item,
  .rsd-footer__col .wp-block-navigation-item__content,
  .rsd-footer__col .wp-block-navigation__container {
    font-size: var(--rsd-t-xs) !important;
  }
}

/* ---------------------------------------------------------- motion */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation-duration: .01ms !important; transition-duration: .01ms !important; scroll-behavior: auto !important; }
}

/* ============================================================
   Floating pill header — hand-written, no utility framework.
   ============================================================ */
.rsd-sr {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

.rsd-header { position: relative; z-index: 60; }

.rsd-pill {
  /* --rsd-st: 0 (top, normal) → 1 (fully compact/sticky). Driven by
     assets/js/main.js mapping scroll progress 3%→30% onto this one
     property; every property below is a pure calc() of it, so the
     transition is continuous and reverses for free on scroll-up. */
  --rsd-st: 0;
  position: fixed; top: calc(20px - 10px * var(--rsd-st)); left: 50%;
  transform: translateX(-50%) scale(calc(1 - 0.015 * var(--rsd-st)));
  transform-origin: top center;
  z-index: 60; width: calc(100% - 2.5rem); max-width: 1290px;
  display: flex; align-items: center; justify-content: space-between; gap: 1rem;
  padding: calc(0.5rem - 0.15rem * var(--rsd-st)) calc(0.6rem - 0.1rem * var(--rsd-st))
           calc(0.5rem - 0.15rem * var(--rsd-st)) calc(1.25rem - 0.25rem * var(--rsd-st));
  background: color-mix(in srgb, var(--rsd-white) calc(92% + 6% * var(--rsd-st)), transparent);
  backdrop-filter: blur(calc(14px + 4px * var(--rsd-st)));
  border: 1px solid var(--rsd-line);
  border-radius: 999px;
  box-shadow: 0 calc(8px + 4px * var(--rsd-st)) calc(30px + 10px * var(--rsd-st)) rgba(26, 26, 28, calc(0.06 + 0.06 * var(--rsd-st)));
}
@media (prefers-reduced-motion: no-preference) {
  .rsd-pill {
    transition: top .22s cubic-bezier(.2,.8,.2,1), transform .22s cubic-bezier(.2,.8,.2,1),
                padding .22s cubic-bezier(.2,.8,.2,1), background-color .22s ease,
                backdrop-filter .22s ease, box-shadow .22s ease;
  }
}
/* Offset the fixed bar so content never sits underneath it. */
.rsd-main { padding-top: calc(68px + clamp(1.5rem, 4vw, 2.75rem)); }

@media (min-width: 1280px) { .rsd-pill { max-width: 1290px; } }
@media (max-width: 1279px) { .rsd-pill { max-width: 1140px; } }
@media (max-width: 1023px) { .rsd-pill { max-width: 960px; } }
@media (max-width: 767px)  { .rsd-pill { max-width: 720px; } }

.rsd-pill__nav, .rsd-pill__cta { display: none; }
@media (min-width: 1024px) {
  .rsd-pill__nav, .rsd-pill__cta { display: flex; align-items: center; }
  .rsd-pill__cta { gap: .6rem; }
}

.rsd-menu { display: flex; align-items: center; gap: 1.5rem; list-style: none; margin: 0; padding: 0; }
.rsd-menu a { text-decoration: none; font-size: var(--rsd-t-sm); font-weight: 500; display: inline-block; padding-block: .35rem; }
.rsd-menu > li { position: relative; }

/* Top-level trigger — desktop pill nav only (scoped via .rsd-pill__nav so
   this never competes with the drawer's own .rsd-menu--stacked padding/
   layout rules, which live in a completely separate .rsd-drawer__nav
   container). Same rounded pale-red hover language as the submenu links
   below (reusing the same --rsd-submenu-hover-bg token, not a second
   color), no underline. Padding grows slightly on hover/focus so the pill
   has room without the rest of the nav shifting — offset by a matching
   negative margin so neighbouring items don't move. */
.rsd-pill__nav .rsd-menu > li > a {
  border-radius: 9px; padding: .4rem .65rem; margin: 0 -.65rem;
  display: inline-flex; align-items: center; gap: .35rem;
}
@media (prefers-reduced-motion: no-preference) {
  .rsd-pill__nav .rsd-menu > li > a { transition: background-color .13s ease; }
}
.rsd-pill__nav .rsd-menu > li:hover > a,
.rsd-pill__nav .rsd-menu > li:focus-within > a,
.rsd-pill__nav .rsd-menu > li.current-menu-item > a {
  background: var(--rsd-submenu-hover-bg);
}

/* Chevron — desktop pill nav only, same reasoning. Only on items that
   actually open something (RSD_Nav_Walker adds aria-haspopup="true" to
   exactly those), so it's never confused with the separate internal
   submenu-item icons. Pure CSS, no image/font; flips to point up while
   its mega-menu is open. */
.rsd-pill__nav .rsd-menu > li > a[aria-haspopup="true"]::after {
  content: ""; width: 7px; height: 7px; flex: none;
  border-right: 1.5px solid currentColor; border-bottom: 1.5px solid currentColor;
  transform: rotate(45deg); margin-top: -3px;
}
@media (prefers-reduced-motion: no-preference) {
  .rsd-pill__nav .rsd-menu > li > a[aria-haspopup="true"]::after { transition: transform .16s ease, margin .16s ease; }
}
.rsd-pill__nav .rsd-menu > li:hover > a[aria-haspopup="true"]::after,
.rsd-pill__nav .rsd-menu > li:focus-within > a[aria-haspopup="true"]::after {
  transform: rotate(225deg); margin-top: 3px;
}

/* ---- mega-menu text + icon (shared by the desktop panel and the mobile
   accordion — same markup, rendered twice by RSD_Nav_Walker) ----------
   All colors below are CSS custom properties generated from Customizer
   theme_mods (inc/customizer.php) — nothing here is a repeated literal.
   Defaults reuse the theme's own existing --rsd-charcoal/--rsd-muted
   tokens, so this looks exactly like it did before the Customizer
   controls existed until someone actually changes a setting. */
.rsd-menu__heading {
  display: block; font-family: var(--rsd-display); font-weight: 600;
  font-size: var(--rsd-t-xs); letter-spacing: .06em; text-transform: uppercase;
  color: var(--rsd-submenu-heading); padding: .5rem .6rem .6rem;
}
/* Icon + text sit side by side; icon aligned to the flex-start (the label
   line), not centred on the whole two-line label+description block — see
   .rsd-menu__icon's own top nudge below for the fine adjustment. */
.rsd-menu__row { display: flex; align-items: flex-start; gap: .6rem; }
.rsd-menu__text { display: flex; flex-direction: column; min-width: 0; }
.rsd-menu__label {
  display: block; font-family: var(--rsd-display); font-weight: 600; font-size: var(--rsd-t-sm);
  color: var(--rsd-submenu-text);
}
.rsd-menu__desc { display: block; margin-top: .15rem; font-size: var(--rsd-t-xs); color: var(--rsd-submenu-desc); }

/* Icon — one small, consistent, hand-drawn inline SVG (no icon font or
   library exists in this project; this is the lightweight fallback),
   applied to every real link below the top level via RSD_Nav_Walker.
   Rendered on both desktop and mobile — only the hover-rectangle treatment
   below is desktop-only. Fixed 18px box regardless of an individual SVG's
   own viewBox, so every icon (the shared default arrow, or any per-service
   icon from inc/services.php's rsd_service_icons()) renders at the same
   size — sizing lives here, once, not on each icon's own markup. The
   margin-top nudge optically aligns the icon's visual centre with the
   label's cap-height, since align-items on .rsd-menu__row is flex-start
   (needed so the icon sits at the top of a two-line label+description
   block, not vertically centred across both lines). */
.rsd-menu__icon {
  display: inline-flex; align-items: center; justify-content: center;
  flex: none; width: 18px; height: 18px; margin-top: .1rem;
  color: var(--rsd-submenu-icon);
}
.rsd-menu__icon svg { display: block; width: 18px; height: 18px; }
@media (prefers-reduced-motion: no-preference) {
  .rsd-menu__icon { transition: color .14s ease; }
}

/* ---- desktop mega-menu (≥1024px only — the same width the pill nav
   itself appears at) -----------------------------------------------------
   One panel shape, two layouts, chosen purely by structure (not by which
   item it is): a flat list of links (Services, Company) becomes a compact
   2-column grid; a panel whose children are themselves group headings
   with their own children (Resources) becomes a 3-column grouped panel.
   RSD_Nav_Walker (inc/setup.php) is what turns a childless-URL parent
   into the plain-text .rsd-menu__heading these :has() selectors key off.
   Scoping the whole panel mechanism to this breakpoint — rather than
   fighting :has()'s specificity with mobile overrides — is what keeps it
   from ever reaching the drawer's .rsd-menu--stacked rendering, since the
   drawer itself only ever shows below 1024px. */
@media (min-width: 1024px) {
  /* Zero positional gap between the trigger and the panel — on purpose.
     CSS applies pointer-events:none the instant :hover/:focus-within is
     lost, with no transition on that property, so ANY real gap (even a
     couple of px, even with a bridge pseudo-element sized to cover it)
     is a hard lock-out risk: one discrete mousemove event landing in the
     dead pixel row kills pointer-events on the panel before the pointer
     ever reaches it, and a non-interactive element can't be re-entered
     except by going back through the trigger. Removing the gap removes
     the dead zone entirely — no geometry to get wrong, no JS needed. The
     lost external gap is replaced by extra padding-top, so the visible
     panel still reads as "floating" with breathing room, it just has no
     literal unhoverable space between it and the trigger. */
  /* Scoped to ">li>.sub-menu" — the outer panel only — on purpose. A plain
     ".rsd-menu .sub-menu" descendant selector here would also match the
     nested (level-2) link list under a group heading, setting its
     opacity/visibility/pointer-events directly (not inherited — a rule
     that matches an element always wins over inheriting from a parent),
     and nothing un-hides it afterward since the reveal rule below is also
     ">li:hover>.sub-menu" and can't reach two levels deep. That was
     exactly why a grouped panel's real links (Services' pillars; this
     would equally have broken Resources, just never live-tested) never
     appeared: correct markup, permanently hidden third level. Leaving
     level 2 untouched here is what lets it inherit visibility/pointer-
     events from level 1's current state, per the comment further down. */
  .rsd-menu > li > .sub-menu {
    position: absolute; top: 100%; left: 50%;
    transform: translateX(-50%) translateY(-6px);
    list-style: none; margin: 0; padding: 1.5rem 1.5rem 1.25rem;
    background: var(--rsd-submenu-bg); border: 1px solid var(--rsd-submenu-border);
    border-radius: 14px; box-shadow: 0 12px 32px rgba(26,26,28,.10);
    width: max-content; max-width: min(620px, calc(100vw - 3rem));
    opacity: 0; visibility: hidden; pointer-events: none;
  }
  @media (prefers-reduced-motion: no-preference) {
    .rsd-menu > li > .sub-menu {
      transition: opacity .16s ease, transform .16s ease, visibility 0s linear .16s;
    }
  }
  .rsd-menu > li:hover > .sub-menu,
  .rsd-menu > li:focus-within > .sub-menu {
    opacity: 1; visibility: visible; pointer-events: auto;
    transform: translateX(-50%) translateY(0);
    transition-delay: 0s;
  }

  /* Flat panel (Services, Company): direct child links only — compact
     2-column grid, generous but controlled, not an oversized card. */
  .rsd-menu > li > .sub-menu:not(:has(> li.menu-item-has-children)) {
    display: grid; grid-template-columns: repeat(2, minmax(220px, 1fr)); gap: .1rem 1.5rem;
  }
  /* Grouped panel (Resources, and Services since it became dynamic):
     children are group headings with their own lists. Width ceiling
     reuses the sitewide shell token rather than a one-off number, and
     never forces the panel wider than the content actually needs. Base
     rule here is the tablet-and-up default (2 columns, 1024-1279px);
     1280px+ ("true" desktop, the same tier this design already treats as
     the wide breakpoint — see .rsd-pill's own min-width:1280px rule
     earlier in this file) is where column count follows however many
     group-heading children actually exist, capped at 4 — see
     rsd_service_pillars_with_leftovers() in inc/services.php for where
     that count comes from for Services. A 5th+ group wraps to a second
     row inside the same 4-column template rather than forcing a 5th
     column. Below 1024px this selector doesn't apply at all — the mobile
     accordion (.rsd-menu--stacked) takes over instead. */
  .rsd-menu > li > .sub-menu:has(> li.menu-item-has-children) {
    display: grid; grid-template-columns: repeat(2, minmax(180px, 1fr)); gap: 0 1.5rem;
    width: max-content; max-width: min(var(--rsd-shell), calc(100vw - 3rem));
  }
  @media (min-width: 1280px) {
    .rsd-menu > li > .sub-menu:has(> li.menu-item-has-children) {
      grid-template-columns: repeat(3, minmax(180px, 1fr)); gap: 0 2rem;
    }
    .rsd-menu > li > .sub-menu:has(> li.menu-item-has-children):not(:has(> li.menu-item-has-children:nth-child(2))) {
      grid-template-columns: minmax(180px, 1fr);
    }
    .rsd-menu > li > .sub-menu:has(> li.menu-item-has-children:nth-child(2)):not(:has(> li.menu-item-has-children:nth-child(3))) {
      grid-template-columns: repeat(2, minmax(180px, 1fr));
    }
    .rsd-menu > li > .sub-menu:has(> li.menu-item-has-children:nth-child(4)) {
      grid-template-columns: repeat(4, minmax(180px, 1fr));
    }
  }

  /* Nested (group) link list — just a plain static column, positioning-wise.
     Deliberately NOT setting opacity/visibility/pointer-events here: those
     must inherit from the outer panel's own current open/closed state.
     An earlier version forced pointer-events:auto (and visibility:visible)
     unconditionally, which — because a descendant CAN override an
     ancestor's pointer-events:none/visibility:hidden — left every real
     link in this nested list permanently interactive, invisible but
     hoverable, everywhere the panel's laid-out footprint existed, whether
     the panel was actually open or not. That was the exact cause of the
     mega-menu appearing to open from empty space. */
  .rsd-menu .sub-menu .sub-menu {
    position: static; display: block; list-style: none;
    background: none; border: 0; box-shadow: none; padding: 0; margin: 0;
    transform: none; width: auto; max-width: none;
  }
  /* Right-side featured group — Trust & Policies ONLY. Previously
     ":last-child" of any grouped panel, which was wrong: once the Services
     panel became dynamic and grouped too, its last column ("Intelligence")
     matched the same rule and got boxed for no reason. Scoped to an
     explicit class instead — add "rsd-menu__group--boxed" to the Trust &
     Policies item itself via Appearance → Menus → (edit item) → CSS
     Classes (a native WordPress field). Nothing else should carry this
     class; individual links stay clean/lightweight everywhere else. */
  .rsd-menu > li > .sub-menu:has(> li.menu-item-has-children) > li.menu-item-has-children.rsd-menu__group--boxed {
    background: var(--rsd-paper); border: 1px solid var(--rsd-submenu-border);
    border-radius: 10px; padding: .5rem .75rem .75rem;
  }

  /* Hover/focus — a soft red-tinted rounded rectangle, not an underline.
     Same treatment for every link in every panel, including inside the
     tinted Trust & Policies box. */
  .rsd-menu .sub-menu a {
    display: block; padding: .4rem .6rem; border-radius: 9px;
  }
  @media (prefers-reduced-motion: no-preference) {
    .rsd-menu .sub-menu a { transition: background-color .13s ease; }
  }
  .rsd-menu .sub-menu a:hover,
  .rsd-menu .sub-menu a:focus-visible {
    background: var(--rsd-submenu-hover-bg);
  }
  .rsd-menu .sub-menu a:hover .rsd-menu__label,
  .rsd-menu .sub-menu a:focus-visible .rsd-menu__label { color: var(--rsd-submenu-hover-text); }
  .rsd-menu .sub-menu a:hover .rsd-menu__icon,
  .rsd-menu .sub-menu a:focus-visible .rsd-menu__icon { color: var(--rsd-submenu-hover-icon); }
}

.rsd-btn--block { display: block; text-align: center; margin-top: 1.25rem; }
.rsd-pill .rsd-btn--solid, .rsd-pill .rsd-btn--ghost { border-radius: 999px; }

/* Mobile-only cluster: call icon + hamburger, grouped together so the two
   sit at a fixed small gap from each other regardless of how much space
   .rsd-pill's own space-between gives the row — the call icon otherwise
   would land mid-pill instead of hugging the hamburger. Hidden ≥1024px,
   same breakpoint .rsd-pill__nav/.rsd-pill__cta swap in at. */
.rsd-pill__mobile { display: flex; align-items: center; gap: .5rem; }
@media (min-width: 1024px) { .rsd-pill__mobile { display: none; } }

/* Icon only — no button chrome, no visible number. 44px hit area via
   padding around the fixed 18px SVG so the tap target still clears D-39-
   style accessibility minimums without looking like a filled button next
   to the circular hamburger. */
.rsd-call {
  display: flex; align-items: center; justify-content: center;
  width: 44px; height: 44px; flex: none;
  color: var(--rsd-charcoal); text-decoration: none;
}
.rsd-call svg { display: block; width: 18px; height: 18px; }
@media (prefers-reduced-motion: no-preference) { .rsd-call { transition: color .16s ease; } }
.rsd-call:hover, .rsd-call:focus-visible { color: var(--rsd-red); }

/* hamburger — plain lines only, no circle/background/border. 44px hit area
   (matches .rsd-call's own tap-target treatment) even though the visible
   mark (three 22px lines) is smaller than that box. */
.rsd-hamburger {
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 5px;
  width: 44px; height: 44px; flex: none; padding: 0; cursor: pointer;
  background: none; border: 0;
}
.rsd-hamburger span[aria-hidden] { display: block; width: 22px; height: 2px; background: var(--rsd-charcoal); }
@media (min-width: 1024px) { .rsd-hamburger { display: none; } }

/* left-side drawer — anchored to the left edge (right/left auto swapped vs.
   a right drawer), off-screen at translateX(-100%) when closed. 320ms,
   cubic-bezier(0.22, 1, 0.36, 1) is the same curve the link-underline
   transition uses further down this file — one motion signature, reused. */
.rsd-drawer {
  position: fixed; inset: 0 auto 0 0; width: min(100%, 420px); height: 100dvh;
  background: var(--rsd-white); z-index: 70; padding: 1.25rem;
  transform: translateX(-100%);
  overflow-y: auto; overscroll-behavior: contain;
}
@media (prefers-reduced-motion: no-preference) {
  .rsd-drawer { transition: transform .32s cubic-bezier(0.22, 1, 0.36, 1); }
}
.rsd-drawer.is-open { transform: translateX(0); }
.rsd-drawer__head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 1.5rem; }
/* Plain X, no circle/background/border — same treatment as .rsd-hamburger.
   44px hit area though the visible mark (two 16px lines) is smaller. */
.rsd-drawer__close {
  position: relative; width: 44px; height: 44px; flex: none; padding: 0; cursor: pointer;
  background: none; border: 0;
}
.rsd-drawer__close span[aria-hidden] {
  position: absolute; top: 50%; left: 50%; width: 16px; height: 2px; background: var(--rsd-charcoal);
}
.rsd-drawer__close span[aria-hidden]:first-of-type { transform: translate(-50%, -50%) rotate(45deg); }
.rsd-drawer__close span[aria-hidden]:last-of-type  { transform: translate(-50%, -50%) rotate(-45deg); }

.rsd-menu--stacked { flex-direction: column; align-items: stretch; gap: 0; }
.rsd-menu--stacked > li { border-bottom: 1px solid var(--rsd-line); }
.rsd-menu--stacked a, .rsd-menu--stacked .rsd-menu__heading { padding-block: .8rem; }
.rsd-menu--stacked > li > a::after { display: none; }
.rsd-menu--stacked .sub-menu {
  position: static; display: block; list-style: none; border: 0; box-shadow: none; padding: 0 0 .6rem .9rem; background: none;
  opacity: 1; visibility: visible; pointer-events: auto; transform: none; width: auto; max-width: none;
}
.rsd-menu--stacked .rsd-menu__label { font-weight: 500; }
.rsd-menu--stacked .rsd-menu__desc { display: none; } /* keep the drawer list scannable; the tagline is a desktop-panel nicety */

/* Mobile accordion — main.js inserts .rsd-menu__toggle only inside the
   drawer, only for items with children. Without JS these submenus simply
   stay visible (today's behaviour) — a safe no-JS fallback, never a dead
   link: the real <a>/<span> is always server-rendered regardless. */
.rsd-menu--stacked li.menu-item-has-children { position: relative; }
.rsd-menu--stacked li.menu-item-has-children > a,
.rsd-menu--stacked li.menu-item-has-children > .rsd-menu__heading { padding-right: 2.75rem; }
.rsd-menu__toggle {
  position: absolute; top: 0; right: 0; width: 44px; height: 44px;
  display: flex; align-items: center; justify-content: center;
  background: none; border: 0; padding: 0; margin: 0; cursor: pointer; color: var(--rsd-charcoal);
}
.rsd-menu__toggle span[aria-hidden] { position: relative; width: 12px; height: 12px; display: block; }
.rsd-menu__toggle span[aria-hidden]::before,
.rsd-menu__toggle span[aria-hidden]::after {
  content: ""; position: absolute; top: 50%; left: 50%; background: currentColor;
  transform: translate(-50%, -50%);
}
.rsd-menu__toggle span[aria-hidden]::before { width: 12px; height: 2px; }
.rsd-menu__toggle span[aria-hidden]::after { width: 2px; height: 12px; }
@media (prefers-reduced-motion: no-preference) {
  .rsd-menu__toggle span[aria-hidden]::after { transition: transform .16s ease; }
}
.rsd-menu__toggle[aria-expanded="true"] span[aria-hidden]::after { transform: translate(-50%, -50%) scaleY(0); }

/* ARIA state is the single source of truth for both semantics and visibility. */
.rsd-menu__toggle + .sub-menu { display: none; }
.rsd-menu__toggle[aria-expanded="true"] + .sub-menu { display: block; }

/* Blurred, dimmed backdrop — fades in step with the drawer via the same
   .32s cubic-bezier(0.22, 1, 0.36, 1) curve. opacity/visibility (not the
   `hidden` attribute) so it can transition instead of snapping; the
   visibility delay keeps it out of the tab order and unclickable while
   closed without a hard display:none cutting the fade short. */
.rsd-scrim {
  position: fixed; inset: 0; z-index: 65;
  background: rgba(26, 26, 28, .55);
  backdrop-filter: blur(8px); -webkit-backdrop-filter: blur(8px);
  opacity: 0; visibility: hidden; pointer-events: none;
}
.rsd-scrim.is-open { opacity: 1; visibility: visible; pointer-events: auto; }
@media (prefers-reduced-motion: no-preference) {
  .rsd-scrim { transition: opacity .32s cubic-bezier(0.22, 1, 0.36, 1), visibility 0s linear .32s; }
  .rsd-scrim.is-open { transition: opacity .32s cubic-bezier(0.22, 1, 0.36, 1); }
}
@media (min-width: 1024px) { .rsd-drawer, .rsd-scrim { display: none; } }

/* Bottom of the drawer: Client Login (outline) then the proposal button
   (solid), matching the desktop pill's own two-button treatment. Separated
   from the nav by margin alone (the last stacked menu item already ends on
   its own hairline — see .rsd-menu--stacked > li — so a second border here
   would double up the rule right above these buttons). min-height rather
   than trusting .rsd-btn's own padding: at this font-size .rsd-btn's
   padding alone lands a hair under the 44px tap-target floor. */
.rsd-drawer__actions { display: flex; flex-direction: column; gap: .65rem; margin-top: 1.75rem; }
.rsd-drawer__actions .rsd-btn--block {
  margin-top: 0; min-height: 44px;
  display: flex; align-items: center; justify-content: center;
}

/* ============================================================
   Homepage components — reuse existing tokens only, per
   REDSPROUT_DESIGN_SYSTEM.md §12 (badges) and the homepage build.
   ============================================================ */

/* Shared vertical rhythm between homepage sections. */
.rsd-home-section { margin-block: var(--rsd-block) 0; }
.rsd-home-section:first-child { margin-top: 0; }

/* [rsd_reach] and [rsd_services] (inc/shortcodes.php) both echo their own
   <section class="rsd-home-section"> directly — the homepage pattern
   places them as bare top-level shortcode blocks, never wrapped in
   .rsd-shell the way every other section on the page is (verified against
   the live rendered homepage: this section is a direct child of
   .rsd-main, siblings with .rsd-shell, not nested inside one). Un-shelled,
   it inherits no width constraint at all and runs edge to edge while
   .rsd-shell content on the same page sits in a centred 1180px column —
   the pill rows' left edge then can't line up with any heading above them
   either, since there's no shared reference box to align to.
   Scoped to ".rsd-main >" specifically so this only ever engages for that
   genuinely un-shelled case: a .rsd-home-section already nested inside a
   real .rsd-shell (the normal case everywhere else this class is used,
   including the rest of this same homepage pattern) doesn't match this
   selector at all, so it isn't touched or re-constrained a second time.
   The width formula is copied verbatim from .rsd-shell itself, not
   re-derived, so a self-constrained instance computes the exact same left
   offset as a real .rsd-shell and lines up with it pixel for pixel. */
.rsd-main > .rsd-home-section {
  width: min(100% - 2.5rem, var(--rsd-shell));
  margin-inline: auto;
}

/* Badges (§12 of the design system) — neutral by default; the red variant
   is reserved for genuinely notable flags, not used on this page. */
.rsd-badge {
  display: inline-flex; align-items: center; text-decoration: none;
  font-family: var(--rsd-display); font-weight: 600;
  font-size: var(--rsd-t-xs); letter-spacing: .04em; text-transform: uppercase;
  padding: .3rem .75rem; border-radius: 999px;
  border: 1px solid var(--rsd-line); background: var(--rsd-white); color: var(--rsd-muted);
  transition: border-color .16s ease, color .16s ease;
}
.rsd-badge:hover { border-color: var(--rsd-red); color: var(--rsd-red); }
.rsd-badge--red { background: var(--rsd-red); color: #fff; border-color: transparent; }
/* Row-gap AND column-gap explicitly, not a single value relying on the
   shorthand's "applies to both" behaviour reading as an accident — small
   tier (related items in one row), same token .rsd-points/.rsd-steps use
   for the same relationship. Was a flat .6rem before: smaller than the
   established small tier and, being a single unlabelled value, easy to
   misread as governing only the horizontal axis. */
.rsd-badge-list { display: flex; flex-wrap: wrap; row-gap: var(--rsd-gap-sm); column-gap: var(--rsd-gap-sm); list-style: none; margin: 0 0 1rem; padding: 0; }
.rsd-badge-list:last-child { margin-bottom: 0; }

/* Markets/Industries pill-row rhythm ([rsd_reach], inc/shortcodes.php) —
   scoped to the two adjacent-sibling relationships that actually exist in
   that shortcode's own markup: an .rsd-eyebrow immediately followed by the
   pill row it labels (medium tier — heading to its content), and a pill
   row immediately followed by the NEXT block's own .rsd-eyebrow (large
   tier — Markets to Industries is a section boundary, not a related-item
   gap, so it gets the same rhythm as any other major section transition,
   not the medium heading-to-content spacing). .rsd-eyebrow's own
   margin-bottom (0.85rem, unchanged, used everywhere else eyebrows appear)
   is what read as "the rule sits directly on the pills" — too tight for
   this specific pairing specifically, not for eyebrows in general, hence
   scoping the fix to these two adjacent-sibling combinators rather than
   changing .rsd-eyebrow itself. */
.rsd-eyebrow + .rsd-badge-list { margin-top: var(--rsd-gap); }
.rsd-badge-list + .rsd-eyebrow { margin-top: var(--rsd-block); }

/* Positioning strip — three sourced statements, not a client-logo bar.
   Reuses the same red-left-border "evidenced claim" motif as .rsd-figure. */
.rsd-strip {
  list-style: none; margin: var(--rsd-block) 0 0; padding: 1.5rem 0;
  border-top: 1px solid var(--rsd-line); border-bottom: 1px solid var(--rsd-line);
  display: flex; flex-wrap: wrap; gap: 1.5rem 2.5rem;
  font-size: var(--rsd-t-sm); color: var(--rsd-muted);
}
.rsd-strip li { flex: 1 1 220px; padding-left: 1rem; border-left: 2px solid var(--rsd-red); }

/* Outcome cards — reuse .rsd-cards/.rsd-card, add a problem/outcome pairing. */
.rsd-card--outcome { display: flex; flex-direction: column; gap: var(--rsd-gap-sm); }
.rsd-card__problem { color: var(--rsd-charcoal); font-weight: 600; margin: 0; }
.rsd-card__outcome { color: var(--rsd-muted); font-size: var(--rsd-t-sm); margin: 0; padding-left: 1rem; border-left: 2px solid var(--rsd-red); }

/* "How we operate" — a two-column point grid of four peer items. gap
   (both axes — every item here is a peer of every other, not "sections
   vs. items within a section") is the small tier (related items);
   margin-top stays the medium tier (this group as a whole, under its own
   heading). Previously gap used var(--rsd-gap), conflating the two tiers
   the spacing scale now separates. */
.rsd-points { display: grid; width: 100%; box-sizing: border-box; grid-template-columns: 1fr 1fr; align-items: baseline; gap: var(--rsd-gap-sm); margin-top: var(--rsd-gap); }
.rsd-points__item h3 { font-size: var(--rsd-t-base); margin-bottom: .35rem; }
.rsd-points__item p { color: var(--rsd-muted); font-size: var(--rsd-t-sm); margin: 0; }

/* Process steps — numbered with CSS counters, tabular-nums in the brand red,
   echoing the sourced-figure numeral treatment for a related but distinct use.
   Children are plain Group-block divs, not <li> — Gutenberg's core blocks
   can't cleanly nest a heading + paragraph inside a single List Item, so
   this trades strict list semantics for a heading-per-step structure that's
   just as navigable by assistive tech via the heading outline itself. */
.rsd-steps {
  margin: var(--rsd-gap) 0 0; counter-reset: rsd-step;
  display: grid; width: 100%; box-sizing: border-box;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: var(--rsd-gap-sm);
}
/* Each step keeps its own top hairline as the default/mobile-stacked
   state (a horizontal connecting line makes no sense once steps are in
   one vertical column) — overridden below at ≥1024px, where the steps
   are realistically all in one row and a single line spanning the whole
   .rsd-steps container reads as one continuous sequence instead of a
   dashed one (one border-top per grid cell is visually broken by the
   gap between cells; that's what made it read as separate cards rather
   than a sequence). */
.rsd-steps > div { counter-increment: rsd-step; padding-top: 1.25rem; border-top: 2px solid var(--rsd-line); }
@media (min-width: 1024px) {
  .rsd-steps { position: relative; }
  .rsd-steps::before {
    content: ""; position: absolute; top: 0; left: 0; right: 0;
    height: 1px; background: var(--rsd-line);
  }
  .rsd-steps > div { border-top: 0; }
}
/* mono + brand red + reusing .rsd-figure__value's own large-numeral scale
   (the theme's other "big red number" component, D-25) is what actually
   separates the step number from its title visually — the number was
   already the first block-level child (structurally above the title, not
   inline with it), but at the old display-font/lede-sized treatment it
   read close enough in weight to the title beneath it that the two
   blurred into what looked like one line. Mono is also what visually
   marks it as a measured/counted value elsewhere on the site (figures,
   table numerals, D-24) — the same signal, reused. */
.rsd-steps > div::before {
  content: counter(rsd-step, decimal-leading-zero); display: block;
  font-family: var(--rsd-mono); font-weight: 700; font-size: clamp(1.75rem, 2.5vw, 2.25rem);
  color: var(--rsd-red); font-variant-numeric: tabular-nums; line-height: 1; margin-bottom: .6rem;
}
.rsd-steps h3 { font-size: var(--rsd-t-base); margin-bottom: .35rem; }
.rsd-steps p { color: var(--rsd-muted); font-size: var(--rsd-t-sm); margin: 0; }

/* Hero button row — the core Buttons block ships no layout CSS of its own
   once wp-block-library is dequeued, so this supplies the flex/gap. */
.rsd-hero__actions { display: flex; flex-wrap: wrap; gap: .75rem; margin-top: 1.5rem; }

/* Bridge for core Button blocks carrying an .rsd-btn* className.
   The block's real DOM is <div class="wp-block-button rsd-btn rsd-btn--solid">
   <a class="wp-block-button__link">…</a></div> — className lands on the
   outer div, not the link .rsd-btn itself was written for, so the button
   look is re-anchored onto .wp-block-button__link here. This is the one
   deliberate exception to "apply existing classes with no new CSS": the
   classes are reused as-is, only the selector that paints them differs,
   because Gutenberg's Button block has a wrapper .rsd-btn never assumed. */
.wp-block-button.rsd-btn { background: none; padding: 0; border: 0; border-radius: 0; }
/* Same min-height/centering guarantee as the plain .rsd-btn rule above —
   a Button-block-wrapped .rsd-btn and a plain <a class="rsd-btn"> should
   never be the one place height quietly diverges between the two ways
   this theme renders "the same button." */
.wp-block-button.rsd-btn .wp-block-button__link {
  display: inline-flex; align-items: center; justify-content: center;
  min-height: 44px; font-family: var(--rsd-display); font-weight: 600;
  font-size: var(--rsd-t-sm); padding: .6rem 1.1rem; border-radius: var(--rsd-radius);
  text-decoration: none; border: 1px solid transparent; cursor: pointer;
  transition: background .16s ease, color .16s ease;
}
.wp-block-button.rsd-btn--solid .wp-block-button__link { background: var(--rsd-red); color: #fff; }
.wp-block-button.rsd-btn--solid .wp-block-button__link:hover { background: var(--rsd-red-ink); }
/* theme.json's styles.elements.button sets background:red + color:white
   globally on every .wp-element-button (WordPress's own "global styles"
   inline <style>, printed in wp_footer — see wp-includes/block-supports/
   elements.php). That rule targets the SAME .wp-block-button__link element
   this bridge does, and its selector is :root :where(.wp-element-button),
   which is a real selector — a `:root` pseudo-class carries specificity —
   not the zero-specificity most devs assume from seeing :where(). This
   bridge's higher specificity (two classes + a class, vs :root's one)
   already beat it for color, which is why the ghost button's text was
   never actually red-on-red — but nothing here ever set its OWN
   background, so WordPress's background:red went uncontested and painted
   straight through underneath the (correctly) charcoal text: a charcoal-
   on-red button that was supposed to be an outline, unreadable at a
   glance. background:transparent below is the fix — same header/Client
   Login language: transparent fill, charcoal border and text, inverting
   solid-charcoal-with-white-text on hover. */
.wp-block-button.rsd-btn--ghost .wp-block-button__link { background: transparent; border-color: var(--rsd-charcoal); color: var(--rsd-charcoal); }
.wp-block-button.rsd-btn--ghost .wp-block-button__link:hover { background: var(--rsd-charcoal); color: #fff; }

/* .rsd-sr (visually-hidden) already defined above — reused as-is for the
   accessible <section> heading in [rsd_reach]'s combined output. */

@media (max-width: 720px) {
  .rsd-points { grid-template-columns: 1fr; }
}

/* ---------------------------------------------------------- services hub
   Added for the /services/ commercial hub (inc/patterns-services-hub.php,
   inc/services.php). None of this touches the nav/mega-menu rules above —
   .rsd-pillars is the services-grid block's own layout, unrelated to
   .rsd-menu's pillar-grouped submenu, which reuses that existing markup/CSS
   unchanged. */

.rsd-pillars {
  display: grid;
  grid-template-columns: repeat(var(--rsd-pillar-cols, 3), 1fr);
  gap: var(--rsd-gap);
  margin-top: var(--rsd-gap);
}
.rsd-pillar__title { font-size: var(--rsd-t-base); margin-bottom: .25rem; }
.rsd-pillar__blurb { color: var(--rsd-muted); font-size: var(--rsd-t-sm); margin: 0 0 1rem; }
.rsd-pillar .rsd-cards { grid-template-columns: 1fr; }

/* URL capture (D-40) — one input, one button, zero JS. */
.rsd-capture {
  display: flex; flex-wrap: wrap; gap: .6rem; margin-top: 1.25rem; max-width: 32rem;
}
.rsd-capture__input {
  flex: 1 1 14rem; min-width: 0; font: inherit; font-size: var(--rsd-t-sm);
  padding: .65rem .85rem; border: 1px solid var(--rsd-line); border-radius: var(--rsd-radius);
  background: var(--rsd-white); color: var(--rsd-charcoal);
}
/* type="url" — browsers apply their own native :invalid styling (often a
   red ring or border) to email/url inputs on their own UA stylesheet,
   independent of anything authored here, and it can show before a visitor
   has typed anything at all: there's no `required` or `pattern` on this
   field, an empty value is not actually invalid, but several engines
   apply the styling anyway. The theme's own border is already the
   standard hairline at rest (above) and the standard 2px red 3px-offset
   ring on focus (the global :focus-visible rule, top of this file) — this
   only neutralizes the browser's own extra, unrequested invalid-state
   decoration so red means "focused," never "empty." */
.rsd-capture__input:invalid { border-color: var(--rsd-line); box-shadow: none; outline: none; }
.rsd-capture__input::placeholder { color: var(--rsd-muted); }

/* Data tables (core/table, "RedSprout — Data table" style) — the system,
   engagement-variables and measurement tables. Numerals in mono per D-24. */
.is-style-rsd-data table { border-collapse: collapse; width: 100%; margin: 1.5rem 0; }
.is-style-rsd-data th,
.is-style-rsd-data td {
  border-bottom: 1px solid var(--rsd-line); padding: .65rem .9rem .65rem 0;
  text-align: left; vertical-align: top; font-size: var(--rsd-t-sm);
}
.is-style-rsd-data th {
  font-family: var(--rsd-display); font-weight: 700; font-size: var(--rsd-t-xs);
  text-transform: uppercase; letter-spacing: .04em; color: var(--rsd-muted);
  border-bottom: 1px solid var(--rsd-charcoal);
}
.is-style-rsd-data td:first-child,
.is-style-rsd-data th:first-child { font-family: var(--rsd-mono); }

/* Proof — section-level spacing only; .rsd-figure/.rsd-excerpt (both
   already styled above) cover a real unit's own contents once one exists.
   Deliberately empty today (D-21) — inc/block-guards.php suppresses the
   whole block while it has zero .rsd-proof-unit children, so none of this
   paints anything yet. */
.rsd-proof { margin-block: var(--rsd-block); }
.rsd-proof-unit { margin-bottom: var(--rsd-gap); }
.rsd-proof-unit__limitation { color: var(--rsd-muted); max-width: 62ch; }

@media (max-width: 900px) {
  .rsd-pillars { grid-template-columns: 1fr 1fr; }
}
@media (max-width: 720px) {
  .rsd-pillars { grid-template-columns: 1fr; }
}

/* ---------------------------------------------------------- text-link
   hover: a thin underline that draws in left-to-right on hover/focus and
   retracts back to the left on mouse-out — not the instant native
   text-decoration:underline toggle this replaces (which is what read as
   abrupt), and not a fade (opacity is never touched here).
   Scope is deliberately narrow: breadcrumbs, footer links, and general
   in-content prose links (a plain <a> inside a <p> or <li> somewhere in
   .rsd-shell — how post/page content and most lists render in this
   theme). Explicitly excludes .rsd-btn (buttons keep their own solid/
   ghost hover) and never touches .rsd-menu (the primary/mega nav keeps
   its separate pale-red rounded hover, per D-38) — neither selector is
   part of the :is() list below, so this rule never reaches them. */
:is(.rsd-crumbs, .rsd-footer, .rsd-shell p, .rsd-shell li) a:not(.rsd-btn) {
  position: relative;
  text-decoration: none;
}
:is(.rsd-crumbs, .rsd-footer, .rsd-shell p, .rsd-shell li) a:not(.rsd-btn)::after {
  content: "";
  position: absolute;
  left: 0; right: 0; bottom: -2px;
  height: 1px;
  background: var(--rsd-red);
  transform: scaleX(0);
  transform-origin: left;
}
@media (prefers-reduced-motion: no-preference) {
  /* Duration/easing only exist under no-preference — see the reduced-
     motion block below for what happens instead. */
  :is(.rsd-crumbs, .rsd-footer, .rsd-shell p, .rsd-shell li) a:not(.rsd-btn)::after {
    transition: transform .32s cubic-bezier(0.22, 1, 0.36, 1);
  }
}
:is(.rsd-crumbs, .rsd-footer, .rsd-shell p, .rsd-shell li) a:not(.rsd-btn):hover::after,
:is(.rsd-crumbs, .rsd-footer, .rsd-shell p, .rsd-shell li) a:not(.rsd-btn):focus-visible::after {
  transform: scaleX(1);
}
/* prefers-reduced-motion: reduce — no draw-in, no retraction. The
   transition above simply doesn't exist in this mode, so the scaleX(0)
   to scaleX(1) flip on :hover/:focus-visible happens instantly: the
   underline is just present while hovering/focused, gone otherwise. */
@media (prefers-reduced-motion: reduce) {
  :is(.rsd-crumbs, .rsd-footer, .rsd-shell p, .rsd-shell li) a:not(.rsd-btn)::after {
    transition: none;
  }
}

/* ---------------------------------------------------------- footer
   widget areas — all four columns (inc/widgets.php), uniformly. Column
   1's structural logo and Column-1-only CTA button (footer.php) are the
   only things in a column that aren't widget content; both are direct
   children of .rsd-footer__col alongside the widget wrapper, so they get
   their own spacing rules below rather than through .rsd-footer__widget.
   Links/headings inside reuse .rsd-footer a (this file, and the
   text-link underline treatment near the end of this file) and
   .rsd-eyebrow (register_sidebar()'s before_title/after_title) as-is —
   nothing new is defined for either here. */
.rsd-footer__widget { margin-bottom: 1.25rem; }
.rsd-footer__widget:last-child { margin-bottom: 0; }
.rsd-footer__widget p {
  margin: 0 0 .75rem; font-size: var(--rsd-t-sm); color: rgba(247,246,244,.85);
}
.rsd-footer__widget p:last-child { margin-bottom: 0; }
.rsd-footer__widget img { max-width: 100%; height: auto; display: block; }

/* Column-1-only "Get a proposal" button (footer.php, gated by
   rsd_footer_cta_enabled) — reuses .rsd-btn--ghost-invert (already the
   dark-surface ghost variant, defined above) rather than a new visual
   style. inline-flex so an optional avatar image sits beside the label
   instead of stacking under it. */
.rsd-footer__cta { display: inline-flex; align-items: center; gap: .5rem; margin-top: .9rem; }
.rsd-footer__cta-avatar { width: 24px; height: 24px; border-radius: 50%; object-fit: cover; flex: none; }

/* Footer widget-area normalization — whatever block gets dropped into a
   footer column (Navigation, List, Latest Posts, Page List, the live
   Services Grid, anything) renders with the same footer-density look, not
   that block's own default styling. Scoped to .rsd-footer__col only —
   .rsd-footer__markets and .rsd-footer__base (copyright) are separate
   classes and untouched by any rule here, and nothing here reaches
   outside .rsd-footer at all, so the mega menu and every other list on
   the site are equally untouched. */

/* No bullets, no default WP list spacing, at any depth — a plain
   descendant selector (no ">") reaches a Navigation block's own nested
   submenu <ul>s just as much as a top-level list. */
.rsd-footer__col ul,
.rsd-footer__col ol {
  list-style: none; margin: 0; padding: 0;
}
.rsd-footer__col li { margin: 0 0 .5rem; }
.rsd-footer__col li:last-child { margin-bottom: 0; }

/* Footer size everywhere in a footer column — the same size the Markets
   row and legal line use (--rsd-t-sm, see .rsd-footer__list/.rsd-footer__
   markets ul above). !important is deliberate and narrow here: WordPress
   core blocks (core/navigation in particular) can apply their own font
   size as an INLINE style attribute when a size is picked in the editor's
   typography controls, and an inline style beats any class-based rule
   regardless of specificity — normal specificity has no way to win that.
   Setting it on the container too (not just a, li, p) means anything not
   explicitly listed here still inherits the right size by default. */
.rsd-footer__col,
.rsd-footer__col a,
.rsd-footer__col li,
.rsd-footer__col p,
.rsd-footer__col .wp-block-navigation,
.rsd-footer__col .wp-block-navigation-item,
.rsd-footer__col .wp-block-navigation-item__content,
.rsd-footer__col .wp-block-navigation__container {
  font-size: var(--rsd-t-sm) !important;
}

/* Any heading dropped in — a bare Heading block, or a widget's own title
   (register_sidebar()'s before_title/after_title in inc/widgets.php
   already wraps that one in .rsd-eyebrow) — gets the same eyebrow +
   calibration-rule treatment as every other section heading on the site.
   Targets the element, not a class, so it applies whether or not
   rsd-eyebrow is actually present on it. Colors match the existing
   ".rsd-footer .rsd-eyebrow" override for this dark background exactly. */
.rsd-footer__col h1,
.rsd-footer__col h2,
.rsd-footer__col h3,
.rsd-footer__col h4 {
  font-family: var(--rsd-display); font-weight: 600; font-size: var(--rsd-t-xs);
  letter-spacing: .14em; text-transform: uppercase;
  color: rgba(247,246,244,.55);
  width: fit-content; /* same fix as .rsd-eyebrow above — this is that exact pattern's dark-surface reimplementation, not a shared rule, so it needed the same edit separately. */
  margin: 0 0 .85rem; padding-bottom: .5rem;
  border-bottom: 1px solid rgba(247,246,244,.15);
  position: relative;
}
.rsd-footer__col h1::after,
.rsd-footer__col h2::after,
.rsd-footer__col h3::after,
.rsd-footer__col h4::after {
  content: ""; position: absolute; left: 0; bottom: -1px;
  width: 2.5rem; height: 2px; background: var(--rsd-red);
}

/* Tight, even vertical rhythm — footer density, not article density. */
.rsd-footer__col p { margin: 0 0 .5rem; font-size: var(--rsd-t-sm); }
.rsd-footer__col p:last-child { margin-bottom: 0; }

/* Force vertical stacking + full column width regardless of a block's
   own default layout — core/navigation in particular defaults to a
   horizontal flex row, which on its own would break column width/
   alignment consistency with the other columns. */
.rsd-footer__col .wp-block-navigation,
.rsd-footer__col .wp-block-navigation__container,
.rsd-footer__col ul.wp-block-latest-posts,
.rsd-footer__col ul.wp-block-page-list {
  display: flex; flex-direction: column; align-items: flex-start;
  width: 100%; gap: 0;
}
.rsd-footer__col .wp-block-navigation-item,
.rsd-footer__col .wp-block-latest-posts__post {
  width: 100%; margin: 0 0 .5rem;
}
.rsd-footer__col .wp-block-navigation-item:last-child,
.rsd-footer__col .wp-block-latest-posts__post:last-child { margin-bottom: 0; }
.rsd-footer__col .wp-block-navigation-item__content { padding: 0; font-size: var(--rsd-t-sm); }

/* Links: color and the left-to-right underline hover already come from
   .rsd-footer a (this file, above) and the shared text-link underline
   rule (:is(.rsd-crumbs, .rsd-footer, ...) a:not(.rsd-btn), further down
   this file) — .rsd-footer is in both selector lists already, so
   anything inside .rsd-footer__col inherits both with no new rule
   needed here. */

/* ---------------------------------------------------------- proposal CTA
   Deliberately no entrance/exit animation on the <dialog> itself: doing
   that correctly needs @starting-style + transition-behavior:allow-
   discrete (dialog toggles display:none -> block, which a plain
   transition can't animate across without them), and this was built
   without the ability to visually verify it renders correctly in a real
   browser — an instant, fully-functional, on-brand modal beats a
   fancier one that might silently fail to show at all. The button's own
   hover still transitions, gated by prefers-reduced-motion as required. */
.rsd-cta-btn {
  position: fixed; right: 1.25rem; bottom: 1.25rem; z-index: 70;
  display: inline-flex; align-items: center; gap: .55rem;
  background: var(--rsd-red); color: #fff; border: 0; cursor: pointer;
  padding: .75rem 1.15rem; border-radius: 999px; /* pill — explicitly allowed for this button, like the header CTA */
  font-family: var(--rsd-display); font-weight: 600; font-size: var(--rsd-t-sm);
  box-shadow: 0 4px 14px rgba(26,26,28,.18); /* small, not the "large shadow" this is meant to avoid */
}
.rsd-cta-btn:hover, .rsd-cta-btn:focus-visible { background: var(--rsd-red-ink); transform: translateY(-2px); }
.rsd-cta-btn__avatar { width: 28px; height: 28px; border-radius: 50%; object-fit: cover; flex: none; }
@media (prefers-reduced-motion: no-preference) {
  .rsd-cta-btn { transition: background-color .15s ease, transform .18s ease; }
}

/* Attention pulse — a single thin ring, ::after, pure CSS. inset:0 +
   border-radius:inherit starts the ring flush with the button's own edge
   (circle on desktop, pill on mobile — inherit means it's never a second
   shape to keep in sync); transform:scale() carries it outward to 1.6×
   while opacity fades to 0, so nothing here touches layout or paints a
   scrollbar (transformed overflow is paint-only, not layout). z-index:-1
   keeps it behind the avatar/label within the button's own stacking
   context (position:fixed + z-index:70 above already establishes that
   context, so -1 here can't drop below anything on the page outside it).
   One ::after, one animation instance — structurally there is only ever
   one ring, never a stack. Base state is opacity:0 outside the
   no-preference block below, so prefers-reduced-motion: reduce shows no
   ring at all, exactly D-28. */
.rsd-cta-btn::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  border: 1px solid var(--rsd-red);
  pointer-events: none;
  opacity: 0;
  z-index: -1;
}
@media (prefers-reduced-motion: no-preference) {
  /* 6.5s total = 2.5s visible pulse + 4s pause, so it reads as an
     occasional draw of the eye rather than constant motion. 38% is
     2.5s/6.5s — the ring finishes expanding/fading there and simply
     holds at scale(1.6)/opacity:0 (no keyframe stop needed between 38%
     and 100%) until the next cycle snaps it back to 0%. */
  .rsd-cta-btn::after { animation: rsd-cta-pulse 6.5s ease-out infinite; }
}
/* The pulse exists to attract attention; once attention has actually
   arrived (hover/focus) it stops immediately rather than continuing
   to compete with itself. */
.rsd-cta-btn:hover::after, .rsd-cta-btn:focus-visible::after {
  animation: none;
  opacity: 0;
}
@keyframes rsd-cta-pulse {
  0%  { transform: scale(1);   opacity: .6; }
  38% { transform: scale(1.6); opacity: 0; }
}

/* Mobile: same breakpoint the header switches pill-nav/hamburger at
   (main.css, .rsd-pill__nav / .rsd-hamburger, min-width:1024px) — "mobile"
   means the same range everywhere in this theme, not a second definition.
   Desktop (≥1024px, the rules above) is untouched. Avatar moves to the
   right via row-reverse (visual order only — DOM order, and so the a11y
   reading order, stays avatar-then-label; the avatar's alt is empty/
   decorative regardless). max-width:70vw + the label's own truncation
   rule below is what "roughly 70% of the viewport" becomes in practice:
   past that width the label ellipses instead of wrapping to a second
   line. Radius is already 999px above — matches the header pill's own
   radius already, nothing to change. Shadow below is the header pill's
   own resting (unscrolled, --rsd-st:0) shadow value, copied literally
   rather than re-derived, so the two are the same shadow, not merely a
   similar one. */
@media (max-width: 1023px) {
  .rsd-cta-btn {
    flex-direction: row-reverse;
    min-height: 44px; /* the floor itself — not relied on padding math to reach it */
    max-width: 70vw;
    padding: .55rem .9rem;
    font-size: var(--rsd-t-xs);
    letter-spacing: -0.01em;
    box-shadow: 0 8px 30px rgba(26, 26, 28, .06);
  }
  .rsd-cta-btn__label {
    min-width: 0; /* flex children default to min-width:auto, which blocks shrink-to-ellipsis entirely */
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
}

.rsd-cta-modal {
  position: fixed; margin: auto; border: 0; border-radius: 3px; padding: 2rem;
  max-width: min(480px, calc(100vw - 2rem)); width: 100%; max-height: calc(100vh - 2rem);
  overflow-y: auto;
  background: var(--rsd-white); color: var(--rsd-charcoal);
  box-shadow: 0 12px 32px rgba(26,26,28,.16);
}
.rsd-cta-modal::backdrop { background: rgba(26,26,28,.55); }
.rsd-cta-modal__close {
  position: absolute; top: .75rem; right: .75rem;
  width: 32px; height: 32px; border-radius: 3px; border: 1px solid var(--rsd-line);
  background: var(--rsd-paper); color: var(--rsd-charcoal); cursor: pointer;
  font-size: 1.25rem; line-height: 1; display: flex; align-items: center; justify-content: center;
}
.rsd-cta-modal__head { margin-bottom: 1.25rem; }
.rsd-cta-modal__avatar { width: 48px; height: 48px; border-radius: 50%; object-fit: cover; margin-bottom: .75rem; }
/* Same eyebrow-adjacent calibration-rule language as every other section
   heading on the site (.rsd-eyebrow, near the top of this file), sized
   up for a modal heading rather than a small-caps label. */
.rsd-cta-modal__heading {
  font-family: var(--rsd-display); font-size: var(--rsd-t-h3); font-weight: 700;
  margin: 0 0 .6rem; padding-bottom: .5rem; border-bottom: 1px solid var(--rsd-line);
  position: relative;
}
.rsd-cta-modal__heading::after {
  content: ""; position: absolute; left: 0; bottom: -1px; width: 2.5rem; height: 2px; background: var(--rsd-red);
}
.rsd-cta-modal__subheading { color: var(--rsd-muted); font-size: var(--rsd-t-sm); margin: 0; }

.rsd-cta-form__row { margin: 0 0 .85rem; }
.rsd-cta-form__row label { display: block; font-size: var(--rsd-t-xs); font-weight: 600; margin-bottom: .3rem; color: var(--rsd-muted); }
.rsd-cta-form__row input, .rsd-cta-form__row textarea {
  width: 100%; font: inherit; font-size: var(--rsd-t-sm); padding: .55rem .7rem;
  border: 1px solid var(--rsd-line); border-radius: 3px; background: var(--rsd-white); color: var(--rsd-charcoal);
  box-sizing: border-box;
}
/* Honeypot — off-screen, not display:none (some bots specifically check
   for and skip display:none fields), aria-hidden on the wrapper and
   tabindex="-1" on the field itself (inc/proposal-cta.php) so it's
   invisible and unreachable for real/keyboard visitors either way. */
.rsd-cta-form__hp { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }
.rsd-cta-form__submit { width: 100%; text-align: center; margin-top: .25rem; }
.rsd-cta-form__submit:disabled { opacity: .6; cursor: default; }
.rsd-cta-form__status { font-size: var(--rsd-t-xs); color: var(--rsd-muted); margin: .6rem 0 0; min-height: 1em; }

.rsd-cta-modal__phone { margin: 1.25rem 0 0; font-size: var(--rsd-t-sm); }
.rsd-cta-modal__phone a { color: var(--rsd-red); font-weight: 600; }
.rsd-cta-modal__consent { margin: .5rem 0 0; font-size: var(--rsd-t-xs); color: var(--rsd-muted); }

@media (max-width: 480px) {
  .rsd-cta-modal { padding: 1.5rem; }
}
