/* The IDX widget listings in the v2 layout (spec 2026-09-25). EVERY rule is
   under body.rt-sx, which the server sets only when our parts rendered and
   rt-sx.js toggles on SPA nav, so the /homes/ search view and outage renders
   are untouched. Layout is a grid on the EXISTING .rt-citypage__prose so no
   node inside or around the IDX widget's React root is moved. */

/* max-width/margin only: NO horizontal padding on .rt-citypage__prose itself
   (code review 2026-09-25). .rt-citypage__body (rt-header.css) already
   supplies a 24px ambient gutter at every width -- the exact rule the
   first-party template's own .rt-mls-wrap relies on too (it declares none of
   its own either) -- so an additional padding here just stacked a SECOND
   24px on top of it: measured live, 48px total from 1024px up, where
   the IDX widget never strips anything (see the mobile-gutter block below for
   the width range where it does).

   1320px, MATCHING v2, NOT v1's 1200 (code review 2026-09-26). v2 caps its
   own page at 1320px (`.rt-mls--v2 .rt-mls-wrap` / `.rt-mls-body >
   .rt-citypage__prose`, rt-mls-listing-v2.css), and this file's own
   grid-template-columns below already use v2's exact rail widths (380px from
   1024px, 452px from 1280px) with the same 28px gap -- only this max-width
   had been left at v1's number. At 1200px every SX listing above ~1248px was
   narrower than first-party, and RT_MLS_GALLERY_SIZES_V2's 840px `sizes`
   bucket (inc/mls/lib-redesign.php) described a slot this page never
   actually rendered (1200 - 452 - 28 = 720). At 1320 the arithmetic is
   1320 - 452 - 28 = 840, matching the constant exactly (see rt_sx_gallery_html()'s
   docblock in inc/sx-parts-lib.php). */
body.rt-sx .rt-citypage__prose { max-width: 1320px; margin: 0 auto; }
/* min-height is intentionally left alone here (code review 2026-09-25): the
   listing-detail polish printer (functions.php) sets `min-height: 100vh` on
   this same element as a load-flash / CLS guard (the widget server-renders this
   container at ~zero height, so anything appended after it would jump into
   place once React mounts). That rule has no !important, so a `min-height: 0
   !important` here defeated it silently on every rt-sx render. Do not add it
   back without re-measuring layout-shift sources on a direct listing load. */
body.rt-sx .rt-citypage__prose > .sidx-container { max-width: none !important; padding-top: 0 !important; }

/* The duplicates our parts replace. .property-address-banner-placeholder is
   included because address-bar.js still wires the (now display:none, see
   the banner block in functions.php) banner on desktop, and once the visitor
   scrolls past its zero-height natural position it flips the placeholder to
   .is-active, a real 34px flow element (address-bar.css) -- measured live,
   that wiring runs even against a hidden banner (code review 2026-09-25).

   :is(body.rt-sx, body.rt-sx-wait) (code review 2026-09-25, CLS fix): the
   SAME hides also apply under body.rt-sx-wait, a class the gallery-order
   observer (functions.php) sets the moment it knows a v2-match hero is
   coming for the current listing (window.rtSxSync loaded) but before the
   fetch that carries .rt-sx-top/.rt-sx-rail has resolved. Without this, the
   vendor header/carousel/price/actions stayed fully visible and painted for
   the whole 0.5-1.2s round trip, then vanished in the same tick the hero was
   inserted above them: one large layout-shift entry, well outside the
   click's hadRecentInput window. Hiding them as soon as rt-sx-wait is set
   moves most of that same hide inside the window the click opened.

   THIS IS A PARTIAL FIX, MEASURED HONESTLY, NOT A COMPLETE ONE. Hiding early
   barely moves the COUNTED score by itself (fixture-measured: 0.4654 ->
   ~0.45), because nothing holds the vendor content's old space open -- the
   LATER swap-in still has to add the hero's full height back on its own. A
   reserved placeholder swapped in for the real content was tried in the
   same fixture and made the counted score WORSE (0.51), because sizing it
   without the SAME max-width/grid constraints that only apply once body.rt-sx
   (not body.rt-sx-wait) is set over-reserves at desktop widths -- not
   shipped for that reason. See functions.php's syncSxWait() and Task 6 Step
   6 of docs/superpowers/plans/2026-09-25-sidx-listing-v2-match.md for the
   live measurement this session added, which is what actually decides
   whether a properly-scoped reservation is worth building. */
:is(body.rt-sx, body.rt-sx-wait) .property-address-banner,
:is(body.rt-sx, body.rt-sx-wait) .property-address-banner-placeholder,
:is(body.rt-sx, body.rt-sx-wait) .sidx-main-header,
:is(body.rt-sx, body.rt-sx-wait) .sidx-menu,
:is(body.rt-sx, body.rt-sx-wait) .sidx-carousel,
:is(body.rt-sx, body.rt-sx-wait) .sidx-photo-array,
:is(body.rt-sx, body.rt-sx-wait) .sidx-price,
:is(body.rt-sx, body.rt-sx-wait) .sidx-sub-address,
:is(body.rt-sx, body.rt-sx-wait) .sidx-primary-action,
:is(body.rt-sx, body.rt-sx-wait) .sidx-actions,
:is(body.rt-sx, body.rt-sx-wait) .sidx-call-to-actions,
:is(body.rt-sx, body.rt-sx-wait) .rt-noacct,
:is(body.rt-sx, body.rt-sx-wait) .sidx-prev-nav-container #rt-viewall,
/* .sidx-price-area is the LEFT-COLUMN box holding both .sidx-price (already
   hidden above) and .sidx-days-online, a navy "N Days On Site" chip. Hiding
   only .sidx-price left that chip as an orphan, the first element of the
   main column, directly under the tabs (code review 2026-09-26). First-party
   shows the same fact inside the fact card's price line instead
   (rt-mls-listing-v2.php's .rt-mls-days); hiding the whole box here is the
   in-scope fix (adding that div would need the caller in functions.php,
   which builds $d, to pass a first-seen date -- out of this lane). */
:is(body.rt-sx, body.rt-sx-wait) .sidx-price-area,
/* Three legacy vendor/spot blocks that used to open the main column under
   the tabs, none of which exist on first-party (code review 2026-09-26):
   the elevated stats card (repeats Beds/Baths/Sq Ft/Built the fact card
   already shows), the centred "{street}, {city}" H2 (repeats the fact
   card's own aria-hidden headline), and the "Call Clark Smith..." box
   (repeats the fact card's + rail's own call line). Hidden rather than
   restyled: their content is a duplicate, not a distinct section, so there
   is nothing here worth keeping visible. */
:is(body.rt-sx, body.rt-sx-wait) .sidx-primary-info,
:is(body.rt-sx, body.rt-sx-wait) .property-address-h2,
:is(body.rt-sx, body.rt-sx-wait) .property-contact-text-below-description,
body.rt-sx .rt-citypage__prose > .rt-listing-crumbs:not(.rt-sx-crumbs) { display: none !important; }

/*
 * MOBILE GUTTER PARITY (code review 2026-09-25). Below 1024px the IDX widget's
 * own small-screen mode strips the horizontal padding from EVERY non-sidx
 * ancestor of .sidx-container with an inline !important override -- the
 * documented full-bleed behaviour; page 9863 is deliberately excluded from
 * the theme's fix that reverts it (realhomes_child_sidx_fullbleed_fix() in
 * functions.php), because on the vendor-served /homes/ search view that
 * full-bleed IS the intended layout. .rt-citypage__body and
 * .rt-citypage__prose are both such ancestors, so whatever gutter they
 * supply disappears the instant the IDX widget hydrates: measured live at
 * 390px (both an iPhone-13 emulation and a plain 390 viewport),
 * .rt-mls-facts, .rt-mls-h1 (inside it), .rt-mls-ctas (inside it) and
 * .rt-sx-crumbs all sat flush at the viewport edges post-hydration -- and,
 * before hydration, at x=48 (a doubled 24px+24px gutter, from
 * .rt-citypage__body's ambient plus .rt-citypage__prose's own padding,
 * which is why that padding was removed from the base rule above rather
 * than kept as a fallback). Either way a real, visible shift on hydration.
 *
 * A rule that only ADDS its own 24px on top of whatever ambient happens to
 * be present would still jump on hydration -- just 48px pre / 24px post
 * instead of 24 pre / 0 post. The fix instead makes each of these three
 * elements independent of the ambient ENTIRELY, using the SAME
 * viewport-relative bleed rt-mls-listing.css's `.rt-mls-gallery__stage`
 * already relies on: `width:100vw` plus `margin-left:calc(50% - 50vw)`
 * makes the box span exactly 0 to 100vw regardless of what padding its
 * ancestors currently carry (the calc resolves against whatever the
 * element's containing block happens to measure at that moment, so it is
 * correct whether the ambient is present, stripped, or anything in
 * between), and the element's OWN `padding-left/padding-right: 24px` then
 * re-adds a FIXED 24px inset on top of that (padding-top/bottom are left
 * exactly as each element's own base rule already sets them) --
 * `box-sizing:border-box` keeps the padding
 * inside the explicit 100vw instead of pushing the box past it.
 * `margin-right` is deliberately left undeclared: once `width` and
 * `margin-left` are both explicit, CSS's own over-constrained-box rule
 * (2.1 10.3.3) recalculates it to make the box fit, which is also why the
 * gallery's own rule never sets it either.
 *
 * The extra bleed step (beyond a plain padding declaration, which is all
 * .rt-listing-gallery / .rt-mls-map / .rt-listing-area need for the same
 * underlying reason) is needed here because those three sit as TOP-LEVEL
 * siblings of .sidx-container with nothing else between them and
 * .rt-citypage__prose, while .rt-mls-facts and .rt-sx-crumbs sit inside
 * .rt-mls-hero (a real box, not display:contents) -- a plain padding
 * declaration on them would ADD to whatever inset .rt-mls-hero's own
 * containing chain is currently supplying, not replace it.
 *
 * The gallery photo itself is deliberately left untouched: spec 4.2 has it
 * edge to edge below 761px on purpose (today's Redfin-style mobile hero),
 * and its existing bleed rule already resolves to that edge-to-edge result
 * regardless of ambient state, so there was nothing to fix there.
 *
 * Scoped to max-width:1023px because from 1024px .rt-mls-facts becomes its
 * own card with 26px/28px padding (rt-mls-listing-v2.css) and the tab row's
 * own @media(min-width:1024px) override (`margin:0;padding:0`) takes over --
 * neither of these rules is needed or wanted there, and the IDX widget never
 * strips anything at that width in the first place.
 */
@media (max-width: 1023px) {
	body.rt-sx .rt-mls-facts,
	body.rt-sx .rt-mls-hero > .rt-sx-crumbs,
	body.rt-sx .rt-mls-tabs__row {
		width: 100vw; max-width: 100vw; margin-left: calc(50% - 50vw);
		padding-left: 24px; padding-right: 24px; box-sizing: border-box;
	}
	/* MAIN-COLUMN LEFT EDGES, mobile half (code review 2026-09-26). Below
	   1024px the IDX widget's own small-screen mode strips .sidx-main-app-body's
	   padding to 0 (the same full-bleed behaviour the big comment above this
	   block describes for our own injected elements), so re-add the same
	   24px inset the facts/crumbs/tabs above already carry, with !important
	   to beat the vendor's own inline override the same way the rest of this
	   file counters vendor !important rules. */
	body.rt-sx .sidx-main-app-body { padding-left: 24px !important; padding-right: 24px !important; }
}

/* The IDX widget's address <h1> stays the document's one h1, visible to
   crawlers and screen readers, not on screen. Never display:none.

   SCOPE FIX (code review 2026-09-25). The old `body.rt-sx .sidx-listing-heading`
   selector is (0,2,1), which LOSES `width` and `margin-left` to the polish
   printer's `body.rt-listing-detail .sidx-container .sidx-listing-heading`
   (functions.php) at (0,3,1) -- both !important, so specificity (not source
   order) decides, and the printer's is higher. Measured live: the element
   kept `position:absolute` and `height:1px` from here (nothing else set
   those), but `width` resolved to the printer's `100%` and `margin-left` to
   its `0`. With no positioned ancestor its containing block is the ICB
   (viewport), so `width:100%` is 100% of the VIEWPORT, not of any local
   column -- computed 1440px wide at a 1440px viewport, 390px at 390 -- and
   with the element's static (in-flow) position offset from the viewport's
   left edge by the page's own gutter, that full-viewport-width box pushed
   past the right edge: documentElement.scrollWidth measured 1575 at 1440
   (innerWidth 1440) and 429 at 390 (innerWidth 390), and scrollTo(300,0) /
   scrollTo(x,0) actually scrolled. `.sidx-container`'s `max-width:none` (this
   same file) does not clip it either -- clip only crops a box's own
   overflowing DESCENDANTS, and this box is itself the one overflowing its
   ancestors. Fixed by adding `.sidx-container` to the selector for a fourth
   class -- `body.rt-listing-detail.rt-sx .sidx-container .sidx-listing-heading`
   is (0,4,1), which beats the printer's (0,3,1) outright regardless of
   source order, so this rule's `width:1px` and `margin:-1px` (both
   !important) win instead. Re-verified: documentElement.scrollWidth equals
   innerWidth at both 1440 and 390, and the h1 is still present and reachable
   in the accessibility tree (position:absolute + clip does not remove it). */
body.rt-listing-detail.rt-sx .sidx-container .sidx-listing-heading {
	position: absolute !important; width: 1px !important; height: 1px !important; padding: 0 !important;
	margin: -1px !important; overflow: hidden !important; clip: rect(0 0 0 0) !important; white-space: nowrap !important; border: 0 !important;
}

/*
 * STICKY TAB BAR FIX (code review 2026-09-25, verified in Chromium AND WebKit
 * with a static HTML/CSS fixture; both engines agree byte-for-byte).
 *
 * .rt-mls-tabs reuses rt-mls-listing-v2.css's unconditional
 * `.rt-mls--v2 .rt-mls-tabs { position: sticky; top: var(--rt-v2-hdr) }`
 * (it applies at every width, not just desktop). A position:sticky element's
 * containing block is its parent box. rt_sx_top_html() (inc/sx-parts-lib.php)
 * puts .rt-mls-tabs inside .rt-mls-lower, and on this page .rt-mls-lower
 * wraps ONLY the <nav> -- unlike the v2 first-party template, where
 * .rt-mls-lower also wraps .rt-mls-cols (main content + rail), which is what
 * gives the nav's containing block enough height to actually pin. Left as a
 * plain nested box here, .rt-mls-lower's height equals the nav's own height,
 * so there is ~0px of room to stick in: verified in headless Chromium, the
 * nav's computed top went 200 (in flow) -> -480 -> -1280 -> -2146 across a
 * scroll instead of settling at the header offset.
 *
 * FIX: make .rt-sx-top and .rt-mls-lower transparent to layout everywhere
 * (not just >=1024px -- the sticky rule is unconditional, so mobile needs
 * this too), so .rt-mls-hero and .rt-mls-tabs become direct children of
 * .rt-citypage__prose, whose box spans the whole listing body. `display:
 * contents` removes only the BOX; the DOM node (and its
 * data-rt-listing-key attribute, which ensureProse/apply read) stays fully
 * queryable. Their own margin-top becomes a no-op once they generate no
 * box, so it has to move onto .rt-mls-hero -- but only inside the
 * `@media (min-width: 1024px)` block below, see the HEADER-GAP COLLAPSE
 * comment there for why an unconditional version of that rule broke
 * everything under 1024px.
 */
body.rt-sx .rt-sx-top, body.rt-sx .rt-sx-top > .rt-mls-lower { display: contents; }
/*
 * TABS GAP FIX (code review 2026-09-26). `display: contents` on
 * .rt-sx-top > .rt-mls-lower (above) makes v2's own
 * `.rt-mls--v2 .rt-mls-lower { margin-top: 20px }` (rt-mls-listing-v2.css) a
 * no-op here for the SAME reason the hero's margin-top had to move onto
 * .rt-mls-hero itself (see the HEADER-GAP COLLAPSE comment below): a
 * display:contents parent generates no box, so nothing is left to carry that
 * margin. Without this the white tab band sat flush against the photo's
 * bottom edge at every width, where first-party leaves a 20px/28px ivory
 * gap. Moved onto .rt-mls-tabs, same as the hero's fix, and unconditional
 * (v2's own rule is not media-gated either) since the hero always precedes
 * it in the grid, so there is nothing here for this margin to collapse
 * through. 28px override lives in the >=1024px block below, matching v2's
 * own desktop bump (rt-mls-listing-v2.css:399's block). The rail's
 * grid-row is unaffected: this only adds space, it does not add a row.
 */
body.rt-sx .rt-citypage__prose > .rt-sx-top .rt-mls-tabs { margin-top: 20px; }
body.rt-sx #rt-sx-overview, body.rt-sx #rt-sx-details, body.rt-sx #rt-mls-map-sec, body.rt-sx #rt-nearby-section {
	scroll-margin-top: 140px;
}

/* Phone: one column; the rail is a v2 desktop element (display:none below
   1024 in rt-mls-listing-v2.css). */
@media (min-width: 1024px) {
	body.rt-sx .rt-citypage__prose {
		display: grid; grid-template-columns: minmax(0, 1fr) 380px; column-gap: 28px; align-items: start;
	}
	body.rt-sx .rt-citypage__prose > * { grid-column: 1; min-width: 0; }
	/*
	 * HEADER-GAP COLLAPSE FIX (code review 2026-09-25, reproduced in a
	 * headless-Chromium fixture built from a live listing -- 123 Gauguin,
	 * Aliso Viejo -- with the IDX widget actually hydrating, all three real
	 * stylesheets and rt_sx_top_html() / rt_sx_rail_html() output).
	 *
	 * This `.rt-mls-hero` margin-top used to be unconditional (declared right
	 * after the `display: contents` rule above this media query, at every
	 * width). Below this breakpoint `.rt-citypage__prose` is plain block
	 * flow, not a grid, and .rt-sx-top / .rt-mls-lower generate no box
	 * (display:contents), so .rt-mls-hero is the first IN-FLOW BOX child of
	 * .rt-citypage__prose (the address banner ahead of it is display:none,
	 * so it contributes no box either). A block's own top margin collapses
	 * into its parent's when nothing -- no border, no padding, no BFC --
	 * separates them, and nothing does here: .rt-citypage__prose carries no
	 * padding-top of its own, and the hidden address banner's embedded
	 * <style> zeroes .rt-citypage__body's ambient padding-top too (that
	 * <style> tag's rules apply regardless of the element's own display).
	 * So the 16px kept propagating straight up through .rt-citypage__prose,
	 * .rt-citypage__body and <main> to <body>, showing as a 16px strip of
	 * the site's navy body background between the fixed header and the
	 * hero -- reproduced before this fix: header bottom at y=58, everything
	 * from .rt-citypage__body down to the hero starting at y=74, both at an
	 * iPhone-13 emulation width and at a plain 900px viewport.
	 *
	 * FIX: gate the rule to this same `min-width: 1024px` block, matching
	 * the first-party v2 template, where the equivalent gap comes from
	 * `.rt-mls--v2 .rt-mls-wrap { padding-top: 16px }` (rt-mls-listing-v2.css)
	 * -- also desktop-only, and a padding rather than a margin, so it can
	 * never collapse in the first place. At >=1024px .rt-citypage__prose is
	 * a grid container, and CSS Grid never collapses a grid item's margin
	 * into the container's own margin box, so the same declaration is safe
	 * there: it only adds space inside the hero's own auto-sized grid row,
	 * which is exactly the 16px gap this rule exists to produce. Below
	 * 1024px the hero is now flush against the header with no rule setting
	 * its margin-top at all, matching the first-party template's own mobile
	 * layout (--rt-v2-hdr steps 58px -> 68px at this same breakpoint, with
	 * no 16px gap below it either). Re-verified after: hero top equals the
	 * header bottom at both the emulated and the plain 900px viewport, and
	 * the >=1024px case (hero at 84, matching v2) is unchanged.
	 *
	 * ACTIVE LISTINGS ONLY (code review 2026-09-26). The re-verify above
	 * never ran against a Sold/Closed listing, where the off-market notice
	 * (functions.php, the_content prio 47) is what actually sits FIRST below
	 * 1024px, not the hero -- this rule only ever collapses a PRECEDING
	 * margin into <body>, and it has none to collapse when the notice's own
	 * box comes first. That case has the identical collapse (the notice's
	 * own margin-top propagates up instead) and its own fix, below this
	 * media query: "CLOSED-LISTING HERO GAP + NAVY STRIP".
	 */
	body.rt-sx .rt-citypage__prose > .rt-sx-top .rt-mls-hero { margin-top: 16px; }
	/* v2's own desktop bump for the identical gap (rt-mls-listing-v2.css:399,
	   `.rt-mls--v2 .rt-mls-lower { margin-top: 28px }`), moved onto
	   .rt-mls-tabs for the same display:contents reason as the base rule
	   above. */
	body.rt-sx .rt-citypage__prose > .rt-sx-top .rt-mls-tabs { margin-top: 28px; }
	/*
	 * AUTO-MARGIN GRID-STRETCH FIX (code review 2026-09-25, verified in a
	 * headless-Chromium fixture built from the real rt_sx_top_html() /
	 * rt_sx_rail_html() markup and all three real stylesheets, at 1440px).
	 *
	 * `.rt-mls-map`, `.rt-listing-closednote`, `.rt-listing-gallery`,
	 * `.rt-listing-prose` and `.rt-listing-area` are appended as direct
	 * children of `.rt-citypage__prose` by various `the_content` filters
	 * (functions.php), and every one of them carries `margin: <top> auto
	 * <bottom>` from the listing-detail polish printer (e.g.
	 * `.rt-mls-map{max-width:1200px;margin:30px auto 0}` at (0,3,2)), a
	 * pattern written for plain block flow: width defaults to "fill the
	 * available space, then center via auto margins if max-width caps it."
	 *
	 * Once `.rt-citypage__prose` becomes a grid container at this width, that
	 * pattern breaks. Per the CSS Grid auto-margin rule, an item whose
	 * inline-axis margins are auto has those margins absorb the extra space
	 * FIRST, exactly like block-level auto-margin centering -- so the item
	 * shrink-to-fits its content instead of stretching to the grid area's
	 * width (the item never reaches the `stretch` `justify-self` this
	 * container otherwise defaults every item to, since none of them set
	 * `justify-items`). `max-width:1200px` never engages as the limiting
	 * factor either, since the grid column itself is already narrower than
	 * that. Measured live: `.rt-mls-map` rendered 348px wide (a 300px iframe)
	 * centered in its ~720px column; `.rt-listing-closednote` measured 640px
	 * wide despite already carrying `grid-column: 1 / -1` two rules above (a
	 * spanning grid AREA does not itself stretch a shrink-to-fit item to fill
	 * it); `.rt-listing-gallery` measured 451px wide before its lazy images
	 * loaded and 720px after, changing width as its intrinsic content
	 * (unset-until-loaded image dimensions) changed.
	 *
	 * FIX: zero the inline-axis (left/right) margins at higher specificity
	 * than the printer's highest (0,3,2), leaving each element's own
	 * top/bottom margin (30px, 18px, 0, 8px, 36px, 48px -- all set via the
	 * shorthand's OTHER positions) completely untouched. With the auto
	 * absorption gone, the grid's default `justify-self: stretch` takes over
	 * and each item fills its actual column width instead of shrink-fitting.
	 * `main:not(.rt-mls)` reprises the plain-the widget-vs-first-party scope used
	 * throughout the polish printer (the first-party template's <main>
	 * carries .rt-mls; the widget page's never does), so this can never reach the
	 * first-party v2 template, which already lays these out correctly inside
	 * .rt-mls-wrap. Specificity (0,4,2) beats every printer rule this fixes
	 * ((0,3,2) for .rt-mls-map, (0,2,1) for the rest) outright, and none of
	 * those declarations carry !important, so none is needed here either.
	 * Re-measured after: the map iframe, closed note and gallery each fill
	 * their column/row width identically before and after image load.
	 *
	 * NOT .sidx-container, though -- correction below. This selector's
	 * "none of those declarations carry !important" is true only of the five
	 * elements it was written for (.rt-mls-map / .rt-listing-closednote /
	 * .rt-listing-gallery / .rt-listing-prose / .rt-listing-area); it does
	 * NOT hold for .sidx-container, which is ALSO a direct child of
	 * .rt-citypage__prose and so is ALSO matched by the bare `> *` below.
	 * .sidx-container's own margin-left/margin-right ARE !important (the
	 * listing-detail polish printer, functions.php: `body.rt-listing-detail
	 * .rt-citypage__prose > .sidx-container{margin-left:auto !important;
	 * margin-right:auto !important;...}`, specificity (0,3,1)), and
	 * !important always wins over a non-important declaration regardless of
	 * specificity, so this rule's margin-left:0/margin-right:0 never reaches
	 * it: .sidx-container keeps its auto margins and shrink-to-fits its
	 * content instead of stretching to the grid column (code review
	 * 2026-09-25).
	 */
	body.rt-listing-detail.rt-sx main:not(.rt-mls) .rt-citypage__prose > * { margin-left: 0; margin-right: 0; }
	/*
	 * .the widget-CONTAINER AUTO-MARGIN / CLS FIX (code review 2026-09-25, verified
	 * in a headless-Chromium fixture built from the real the IDX widget-served
	 * listing /homes/listing/CRMLS/NP26208573/1174-Winslow-Newport-Beach,
	 * hydrating, with the Task 2 parts, all three real stylesheets and the
	 * real body classes).
	 *
	 * The general `> *` rule above cannot reach .sidx-container for the
	 * reason explained in its own comment: its printer rule
	 * (functions.php:1335) carries !important, and this file's `> *` rule
	 * does not, so the printer wins the property regardless of the (0,4,2)
	 * specificity edge. During React mount the container was measured
	 * collapsing from 720px to 30px wide (centered at x=465 in its 720px
	 * column), then returning to 720px roughly 150ms later, producing two
	 * layout shifts -- desktop CLS 0.0658 on every run, on the page type that
	 * earns most organic clicks, regressing the 2026-07-16 listing CLS fix
	 * (desktop 0.000 there). The plain the widget page and this same page with the
	 * grid removed both measure 0.
	 *
	 * FIX: a dedicated rule targeting .sidx-container directly, so its own
	 * !important wins the specificity fight outright ((0,5,2) here vs the
	 * printer's (0,3,1), both !important). Re-verified in the same fixture:
	 * CLS drops to 0 and the container stays at 720px throughout hydration.
	 */
	body.rt-listing-detail.rt-sx main:not(.rt-mls) .rt-citypage__prose > .sidx-container {
		margin-left: 0 !important; margin-right: 0 !important;
	}
	/*
	 * MAIN-COLUMN LEFT EDGES (code review 2026-09-26). Four different insets
	 * used to line up under the tabs: the hero/tabs/rail sit flush with the
	 * column edge, the IDX widget's own `.sidx-main-app-body` keeps a 15px
	 * padding-left from its own vendor stylesheet, the polish-printer blocks
	 * (.rt-listing-prose / .rt-listing-gallery / .rt-mls-map / .rt-listing-area)
	 * are inset 24px (functions.php), and the legacy stats card was inset
	 * 56px. First-party's `.rt-mls-wrap` context gives every block the same
	 * flush edge; this makes the SX column do the same.
	 *
	 * `.sidx-main-app-body` is the vendor's OWN wrapper, not ours, so its
	 * padding is beaten with !important the same way every other vendor
	 * override in this file is (its source stylesheet is not in this repo to
	 * measure a specificity against).
	 */
	body.rt-sx .sidx-main-app-body { padding-left: 0 !important; padding-right: 0 !important; }
	/*
	 * The four the_content blocks are direct children of .rt-citypage__prose
	 * (same fact the auto-margin fix above relies on), each carrying its own
	 * `padding: ... 24px 0` from the listing-detail polish printer at (0,2,1)
	 * (.rt-listing-prose / .rt-listing-gallery / .rt-listing-area) or (0,3,2)
	 * (.rt-mls-map, `main:not(.rt-mls) .rt-mls-map`), none !important.
	 * Naming each class individually pushes every selector in this list to
	 * (0,5,2) (this rule's own compound selector plus the target class),
	 * comfortably clearing the (0,4,2) the map rule alone would need, so no
	 * !important is required here either -- matching the auto-margin fix's
	 * own reasoning immediately above.
	 */
	body.rt-listing-detail.rt-sx main:not(.rt-mls) .rt-citypage__prose > .rt-listing-prose,
	body.rt-listing-detail.rt-sx main:not(.rt-mls) .rt-citypage__prose > .rt-listing-gallery,
	body.rt-listing-detail.rt-sx main:not(.rt-mls) .rt-citypage__prose > .rt-mls-map,
	body.rt-listing-detail.rt-sx main:not(.rt-mls) .rt-citypage__prose > .rt-listing-area {
		padding-left: 0; padding-right: 0;
	}
	/* .rt-sx-top no longer generates a box (display:contents above), so it can
	   no longer carry its own grid-column: the hero and the nav are now two
	   SEPARATE grid items and each needs the span. Verified: hero lands in
	   row 1, nav in row 2, so the widget content that follows (matched by the
	   `> *` rule above) lands in row 3 -- one row later than in the original
	   (unfixed) draft of this rule, where hero+nav were still one combined
	   box occupying row 1 alone. The rail's row-start moves with it. */
	body.rt-sx .rt-citypage__prose > .rt-sx-top .rt-mls-hero,
	body.rt-sx .rt-citypage__prose > .rt-sx-top .rt-mls-tabs { grid-column: 1 / -1; }
	/* The off-market notice (functions.php, the_content prio 47) prepends
	   AHEAD of the hero on closed listings, so it is a real grid item at this
	   width too -- give it the full row or it would sit squeezed into the
	   main column alone (code review 2026-09-25). */
	body.rt-sx .rt-citypage__prose > .rt-listing-closednote { grid-column: 1 / -1; }
	body.rt-sx .rt-citypage__prose > .rt-sx-rail { grid-column: 2; grid-row: 3 / span 20; align-self: stretch; padding-top: 28px; }
	/* CLOSED-LISTING VARIANT (code review 2026-09-25, verified in a headless
	   Chromium fixture built from the real rt_sx_top_html() / rt_sx_rail_html()
	   markup): on a Sold/Closed listing .rt-listing-closednote is a THIRD
	   sibling ahead of .rt-mls-hero (row 1 = note, row 2 = hero, row 3 = tabs,
	   row 4 = the widget container), so the base rule above -- written for the
	   no-note case where the widget lands in row 3 -- pins the rail one row too
	   early, overlapping .rt-mls-hero instead of aligning with the widget
	   content beside it. :has() gives this selector one more class than the
	   base rule (4 vs 3), so it always wins the cascade regardless of source
	   order. */
	body.rt-sx .rt-citypage__prose:has(> .rt-listing-closednote) > .rt-sx-rail { grid-row: 4 / span 20; }
}
@media (min-width: 1280px) {
	body.rt-sx .rt-citypage__prose { grid-template-columns: minmax(0, 1fr) 452px; }
}

/*
 * CLOSED-LISTING HERO GAP + NAVY STRIP (code review 2026-09-26). Two related
 * defects on a Sold/Closed listing, both below the >=1024px breakpoint (the
 * HEADER-GAP COLLAPSE fix above already re-verified the hero flush with the
 * header at 900px, but only for ACTIVE listings -- that check never ran
 * against a closed one, where the off-market note is what actually sits
 * first, not the hero).
 *
 * (1) v2's own `.rt-mls--v2 .rt-listing-closednote + .rt-mls-hero { margin-top:
 * 14px }` (rt-mls-listing-v2.css) never matches here: the note's next DOM
 * sibling is .rt-sx-top (rt_sx_top_html()'s own wrapper), not .rt-mls-hero
 * directly, and `display: contents` on .rt-sx-top does not change what a
 * sibling combinator sees through it. So below 1024px the note sat flush on
 * the hero photo with a 0px gap, the "one navy slab" look v2's rule exists
 * to prevent.
 *
 * (2) Separately, the note's OWN top margin (14px on phones, 18px from
 * 761-1023px, functions.php) was collapsing straight through
 * .rt-citypage__prose and .rt-citypage__body up to <body>, since nothing --
 * no border, no padding, no BFC -- sits between them below 1024px (the same
 * margin-collapse mechanism the HEADER-GAP COLLAPSE fix already documents
 * for .rt-mls-hero, just triggered by a different element). That painted a
 * strip of the site's navy body background between the fixed header and the
 * note.
 *
 * FIX: (1) the equivalent selector routed through .rt-sx-top, unconditional
 * (not media-gated, matching v2's own rule) so a closed listing gets the
 * same 14px at every width -- placed here, AFTER the >=1024px block's own
 * `.rt-mls-hero { margin-top: 16px }`, so equal specificity (0,4,1) resolves
 * by source order and this rule wins the tie at >=1024px too, same as v2's
 * unconditional 14px. (2) `display: flow-root` on .rt-citypage__prose below
 * 1024px establishes a new block-formatting context, which stops a child's
 * margin from collapsing through it -- the standard fix for this exact
 * problem, and safe here because .rt-citypage__prose has no margin of its
 * own that would need to collapse with anything either.
 *
 * Re-verified: gap is 14px at 390/900/1440 with the navy strip gone (the
 * probe now hits .rt-citypage__prose, not <body>); active listings are
 * unchanged (hero top 58/58/84 at the same three widths, since neither rule
 * here matches without a preceding .rt-listing-closednote); no horizontal
 * scroll; page-bottom spacing (area block to body bottom) is identical
 * before and after, since flow-root changes nothing about height, only
 * where a margin stops collapsing.
 */
body.rt-sx .rt-listing-closednote + .rt-sx-top .rt-mls-hero { margin-top: 14px; }
@media (max-width: 1023px) {
	body.rt-sx .rt-citypage__prose { display: flow-root; }
}

/*
 * "the IDX widget's remaining blocks in v2 type" -- REMOVED (code review
 * 2026-09-25). Two rules used to sit here:
 *
 *   body.rt-sx .sidx-listing-description { font: 400 17px/1.7 var(--rt-f-sans), system-ui, sans-serif; color: #1a1a1a; }
 *   body.rt-sx .sidx-details-table .sidx-heading { font: 400 22px/1.3 var(--rt-f-serif), Georgia, serif !important; color: #0e2c4a !important; }
 *
 * Both lost almost every declaration to higher-specificity !important rules
 * already in the listing-detail polish printer (functions.php), and what
 * survived moved the page AWAY from v2, not toward it. Verified with a
 * headless-Chromium fixture built from the printer's exact CSS text (the
 * `.sidx-details-table .sidx-heading` rule at (0,4,1)!important, the
 * `.sidx-heading.rt-acc-hd.rt-acc-hd` accordion-header rule at
 * (0,5,1)!important, and the `.sidx-listing-description`/`.sidx-details-table
 * .sidx-heading` font-family rule at (0,4,1)!important, all beating this
 * file's (0,3,1)/(0,2,1) rules on every property except font-weight, which
 * none of the printer rules touch):
 *
 *   - Heading rule: font-size (20px General Features / 17px accordion
 *     headers) and font-family (var(--rt-f-sans)) both stayed exactly as the
 *     printer already sets them, with or without this rule present -- so the
 *     serif/22px this rule asked for never rendered. The ONE property that
 *     DID land was font-weight, because the shorthand `font:` resets every
 *     sub-property the declaration does not spell out, including
 *     font-weight, to 400 -- and nothing else in the cascade sets
 *     font-weight for these headings at a competing specificity, so this
 *     rule's 400 won outright. Measured: every the widget section heading
 *     (General Features AND the Interior/Exterior/Community/Schools
 *     accordion headers) dropped from 700 to 400, undoing the 17px/700 the
 *     owner matched to the IDX widget's own rendering on 2026-09-12 (see
 *     CLAUDE.md, "LISTING FEATURE + SNAPSHOT TYPE MATCHED TO THE IDX WIDGET").
 *   - Description rule: shrank the description from 20px/32px (line-height
 *     1.6, color #202122 -- matching the first-party v2 template's
 *     `.rt-mls-desc__body`, rt-mls-listing.css:253) to 17px/28.9px, color
 *     #1a1a1a, since nothing else in the printer competes for font-size,
 *     line-height or color on `.sidx-listing-description`.
 *
 * Deleting both restores 700-weight headings and the 20px/32px/#202122
 * description exactly, with zero effect on anything else the printer
 * already governs (font-size, font-family, margin) -- reproduced in the
 * fixture: identical output with or without the rules present, apart from
 * font-weight and the description's size/line-height/color reverting to the
 * printer's / vendor default's own values.
 */

/* .rt-mls-tourlink (a virtual-tour link, rendered inside the map section by
   realhomes_child_lseo_build_map() / inc/listing-seo-lib.php for every
   the IDX widget listing that has one) is display:none above 760px in the
   listing-detail polish printer (functions.php), on the documented
   assumption that the IDX widget's own .sidx-menu strip is still visible there
   with its own Virtual Tour link -- true on the plain the widget path, but the
   hide list above removes .sidx-menu at every width, so on an rt-sx render
   there was no tour link left at all past 760px (code review 2026-09-25).
   Re-show ours: three classes (rt-listing-detail, rt-sx, rt-mls-tourlink)
   plus two elements (body, main) is (0,4,2), which beats the printer's
   (0,3,2) outright with no !important needed, so its own pinned test
   strings (tests/listing-seo.test.php) stay untouched. */
@media (min-width: 761px) {
	body.rt-listing-detail.rt-sx main:not(.rt-mls) .rt-mls-tourlink { display: block; }
}

/* Listing Snapshot table: the widget pads its .sidx-details-table wrapper 10px,
   which set the snapshot 10px in from the description and the section
   headings (measured live: rows at 70px against a 60px column edge). The
   first-party v2 rows sit on the column edge with their own 5px cell
   padding, so zero the wrapper's horizontal padding here. Scoped to the
   snapshot: the feature accordions already zero theirs via [data-rt-acc]. */
body.rt-sx .sidx-container .sidx-listing-snapshot > .sidx-details-table {
	padding-left: 0 !important;
	padding-right: 0 !important;
}

/* Property History (2026-09-26). The v2 section (section.rt-mls-hist,
   rendered by rt_sx_history_html()) sits INSIDE the Property Details prose,
   right after its facts paragraph, so it does not get the first-party
   template's `.rt-mls-main > *` section rhythm (rt-mls-listing-v2.css: 36px,
   44px from 761px). Without this the h2 sat 4px under the paragraph (the
   paragraph's own bottom margin, collapsed). Same numbers as v2 so the two
   page types space the section identically; the table, headings and phone
   stacking already come from rt-mls-listing-v2.css, which is scoped to
   `.rt-mls--v2`, a class body carries on every rt-sx render. */
body.rt-sx .rt-listing-prose > .rt-mls-hist { margin: 36px 0 0; }
@media (min-width: 761px) {
	body.rt-sx .rt-listing-prose > .rt-mls-hist { margin-top: 44px; }
}

/*
 * THE MAP BREAK (spec docs/superpowers/specs/2026-09-26-listing-full-width-lower-design.md;
 * option rt_listing_full_lower -> body.rt-lower-full, or ?rtfull=1). The tour
 * card stops pinning at the map, and the map, the photo grid, Similar Homes
 * and Explore span both columns.
 *
 * HOW IT STOPS THE CARD. .rt-citypage__prose is the grid above, and the rail
 * is placed explicitly at `grid-column: 2; grid-row: 3 / span 20` (4 on a
 * closed listing). An auto-placed item that spans both columns can only go in
 * the first row where both are free, which is the first row after the rail's
 * twenty, and every later item flows after it. The rows in between hold
 * nothing and the grid has no row gap, so they are zero high: the rail's box,
 * and the sticky card inside it, end exactly at the top of the map.
 *
 * WHICH ITEMS. After moveProse() the order is prose, map, gallery,
 * #rt-nearby-section, rail, crumbs (display:none), area; moveProse() seats the
 * prose above the map even before Similar Homes lands (it anchors on the
 * Explore block until then), so the prose never falls below the break. The
 * four blocks are named, and so are the attribution and CRMLS disclaimer
 * copies cleanupNearbyExtras() places after the Explore block (a closed
 * listing has no map, so the sibling rule would not reach them);
 * `.rt-mls-map ~ :not(.rt-sx-rail)` catches anything else that lands after
 * the map, so no stray item sits half width below it. The rail is placed
 * explicitly and never moves.
 *
 * THE ONE !important. The printer pulls Similar Homes up 26px
 * (`#rt-nearby-section.rt-nl{margin-top:-26px !important}`) to close the gap
 * under the photo grid. When Similar Homes is the first block below the rail
 * (a closed listing: no map, one photo, so no grid), that pull put its top
 * 26px under the card's end. Where it follows the prose directly it gets the
 * map's own 30px instead, which only !important can do.
 *
 * WIDTHS. Each block carries a 1200px cap from its own printer: functions.php's
 * `.rt-listing-gallery` / `.rt-listing-area` (0,2,1) and
 * `main:not(.rt-mls) .rt-mls-map` (0,3,2), and rt-sidx-nearby.js's `.rt-nl`
 * (0,1,0), which also pads Similar Homes 24px in from the column. Lifted here
 * without !important so every block lines up with the hero row's edges. The
 * map grows to 440px with its width; Similar Homes is 4 across from 1024 (the
 * shared grid drops to 2 at 1024 and below); the Explore paragraph keeps a
 * reading measure.
 */
@media (min-width: 1024px) {
	body.rt-lower-full.rt-sx .rt-citypage__prose > .rt-mls-map,
	body.rt-lower-full.rt-sx .rt-citypage__prose > .rt-listing-gallery,
	body.rt-lower-full.rt-sx .rt-citypage__prose > #rt-nearby-section,
	body.rt-lower-full.rt-sx .rt-citypage__prose > .rt-listing-area,
	body.rt-lower-full.rt-sx .rt-citypage__prose > .rt-mls-moved,
	body.rt-lower-full.rt-sx .rt-citypage__prose > .rt-agent-moved,
	body.rt-lower-full.rt-sx .rt-citypage__prose > .rt-mls-map ~ :not(.rt-sx-rail) { grid-column: 1 / -1; }
	body.rt-lower-full.rt-listing-detail.rt-sx main:not(.rt-mls) .rt-citypage__prose > .rt-mls-map,
	body.rt-lower-full.rt-listing-detail.rt-sx main:not(.rt-mls) .rt-citypage__prose > .rt-listing-gallery,
	body.rt-lower-full.rt-listing-detail.rt-sx main:not(.rt-mls) .rt-citypage__prose > .rt-listing-area { max-width: none; }
	body.rt-lower-full.rt-listing-detail.rt-sx main:not(.rt-mls) .rt-citypage__prose > #rt-nearby-section { max-width: none; padding-left: 0; padding-right: 0; }
	body.rt-lower-full.rt-listing-detail.rt-sx main:not(.rt-mls) .rt-citypage__prose > .rt-listing-prose + #rt-nearby-section.rt-nl { margin-top: 30px !important; }
	body.rt-lower-full.rt-listing-detail.rt-sx main:not(.rt-mls) .rt-mls-map__frame,
	body.rt-lower-full.rt-listing-detail.rt-sx main:not(.rt-mls) #rt-mls-map { height: 440px; }
	body.rt-lower-full.rt-sx #rt-nearby-section .rt-ww__grid { grid-template-columns: repeat(4, minmax(0, 1fr)); }
	body.rt-lower-full.rt-sx .rt-listing-area .rt-listing-area__copy { max-width: 760px; }
}
