/* ======================================================================================================
 * Relationship Popup Engine — shared styles (cross-pillar lego block)
 * Filename: assets/css/partials/relationship-popup.css
 * Version:  v1.10.0
 * Purpose:  Styles the relationship-popup content (relationship-popup-engine.js) rendered inside an
 *           SDXStage panel slot ([data-slot="relationship"]). Shared across Eco Guilds, Viridians and
 *           MamaDEX — pillar identity is carried ONLY as a narrow accent via each relationship block's
 *           [data-relationship] attribute, never as a large saturated fill. Component surface/text
 *           tokens (--rpe-surface / --rpe-text / --rpe-muted / --rpe-border / --rpe-story-surface /
 *           --rpe-shadow) are ISOLATED, fixed-light values — this component does NOT consume the
 *           site's page-theme tokens (--card / --fg / --muted / --border / --surface-alt /
 *           --card-shadow) and does NOT change with [data-theme] on <html>. See v1.3.0 changelog.
 * License:  © Buktika, 2026. All rights reserved.
 *
 * Changelog:
 *   v1.10.0 — 2026-07-17 — LOWER-PANEL ACCENT KEY CORRECTION (caught live in the Viridians
 *     fixture, not by the v1.9.0 static contract tests). The v1.9.0 rules below matched
 *     .rpe-panel[data-relationship-panel="<pillar>"] — but that attribute holds the
 *     relationship's own KEY, not its accent, and Viridians' bound guild relationship has
 *     key:"eco_guild" while accent:"eco". "eco_guild" never matched "eco", so --rpe-accent stayed
 *     unset for that panel and .rpe-story__chev fell through to the generic violet default — the
 *     exact reported defect, reproducible again through a different door. relationship-popup-
 *     engine.js v2.3.0's relationshipPanelHtml() now also emits data-accent-panel="<accent>"
 *     (data-relationship-panel is unchanged and still key-based, since
 *     activateTopLevelBlock()'s show/hide toggle must keep matching by relationship identity).
 *     The four rules below now key off data-accent-panel instead, plus a fifth ("viridians")
 *     added for the same reason the block-level mapping above already has one — a story panel
 *     could just as plausibly present a bound Viridians relationship from inside another
 *     pillar's popup.
 *   v1.9.0 — 2026-07-17 — ACTION ICONS AND HYPERLINK COLOUR STATES (owner instruction, "Correct
 *     relationship-popup action icons and hyperlink colour states"). The lower story panel's own
 *     chevron (.rpe-story__chev) previously hardcoded `background: var(--rpe-focus, #8b5cf6)` —
 *     --rpe-focus is never actually declared as a custom property anywhere in this file (it only
 *     ever resolves through its fallback), so every story chevron rendered the SAME violet
 *     regardless of which pillar's story was showing, which is exactly why an ECO Conservation
 *     story's action circle read as MamaDEX violet in the reported screenshot — a coincidence of
 *     --sdx-color-mama-violet also being #8b5cf6, not an intentional MamaDEX association. FIXED:
 *     new `.rpe-panel[data-relationship-panel="…"]` mapping (mirrors the existing
 *     `.rpe-block[data-relationship="…"]` one above it exactly, same four keys) sets --rpe-accent
 *     on the panel wrapper relationshipPanelHtml() already emits data-relationship-panel on, so a
 *     routed block's own route-story panels (nested inside it) and every plain relationship's
 *     Storybook-shaped panel inherit the correct pillar accent by cascade. `.rpe-story__chev` now
 *     reads `var(--rpe-accent, var(--rpe-focus, #8b5cf6))` — the same two-tier fallback pattern
 *     `.rpe-block` itself already uses — instead of jumping straight to the generic default.
 *     Also added `:link/:visited/:hover/:focus-visible/:active` states for `.rpe-secondary__link`
 *     (the one genuine inline text hyperlink this component renders) using two new isolated
 *     tokens, --rpe-link/--rpe-link-visited, declared alongside the existing --rpe-* block on both
 *     `.sdx-stage[data-stage-variant]` and `.rpe-root` (same placement rule as every other --rpe-*
 *     token, per POINT 2's original placement note). Previously the link's only colour was
 *     --rpe-muted with no distinct :visited treatment — visually closer to plain de-emphasised
 *     text than a hyperlink. The action-circle icon itself never uses `:visited` styling (it isn't
 *     real link text, and relationship-popup-engine.js's CHEVRON_SVG carries no colour of its own
 *     beyond `currentColor`), so it can never pick up a browser-default visited-purple regardless
 *     of the surrounding element's visited state.
 *   v1.8.0 — 2026-07-17 — NO-MEDIA RENDERING CORRECTION (owner instruction, "Final pre-migration
 *     correction — panel lifecycle, focus containment and no-media rendering", point 3 —
 *     CSS half; see relationship-popup-engine.js v2.1.0 for the markup half). A published story
 *     with no image (relationship-popup-engine.js's `hasMedia` check) no longer emits any
 *     `.rpe-story__media`-family element at all — the dead `.rpe-story__media--empty` rule
 *     (previously the empty-box placeholder this correction removes) is deleted. New
 *     `.rpe-story.is-text-only { grid-template-columns: 1fr; }` collapses the two-column
 *     media|prose grid to one column for exactly this case, so no empty gap remains where the
 *     thumbnail column used to be — the same technique the mobile breakpoint below already uses
 *     for a different reason (narrow viewport), now available above that breakpoint too, scoped
 *     only to the text-only case.
 *   v1.7.0 — 2026-07-16 — COMPLETE RELATIONSHIP-POPUP PARITY FOR VIRIDIANS AND MAMADEX (owner
 *     instruction). Generalised the v1.6.0 "unified tablist" styling so it is no longer named after
 *     ECO specifically (relationship-popup-engine.js v1.8.0 companion change): renamed
 *     .rpe-block--eco -> .rpe-block--routed everywhere (the class now applies to ANY relationship
 *     block that carries its own internal routes, not only Eco Guild's). Removed the now-dead
 *     .rpe-block.is-actionable/.is-unavailable whole-card-link states and their hover/chevron-
 *     transform sub-rules (legacyBlockHtml(), their only source, was deleted in engine v1.8.0 — every
 *     relationship block is a selector now, styled via .rpe-tab/.rpe-tab.is-active/
 *     .rpe-tab[data-status] instead). Kept .rpe-block__chev (still used by a routed block's own
 *     explore control) and .rpe-block__media/--icon (kept, and now also reachable from a PLAIN
 *     block's own imagery, not only a routed one — see genericBlockInnerHtml() in the engine).
 *     No new selectors needed for Viridians/MamaDEX specifically — their blocks reuse the exact same
 *     .rpe-tab/.rpe-panel rules Eco Guild's MamaDEX/Reporters/EduTower blocks already used.
 *   v1.6.0 — 2026-07-16 — CORRECT THE LAB INTERACTION AND VISITOR TERMINOLOGY (owner instruction).
 *     relationship-popup-engine.js v1.7.0 turns the four top-level blocks into one ARIA tablist for
 *     ecoRoutes-shaped contracts (Eco Guild) — added .rpe-tab (shared cursor/hover/focus-visible/
 *     .is-active treatment for all four blocks, including ECO) and
 *     .rpe-tab[data-status="unavailable"] (honest muted-text state for a block with no published
 *     relationship yet, WITHOUT disabling pointer/keyboard selection — distinct from the legacy
 *     .is-unavailable rule below, which also sets cursor:default and is only ever used by
 *     legacyBlockHtml()'s non-ecoRoutes whole-card links). Removed .rpe-block--eco's own
 *     cursor:default — the ECO card is now itself a selectable tab like the other three, so it
 *     needs the same pointer cursor .rpe-tab gives them. No new section, no layout change — the
 *     2×2 grid and the single shared lower panel are visually unchanged from v1.5.0; only which
 *     elements are clickable and how the active one is indicated changed.
 *   v1.5.0 — 2026-07-16 — RES/SGR MUST STAY INSIDE THE ECO BLOCK (owner correction, superseding
 *     v1.4.0 below, rejected: "It breaks the approved four-block structure"). Removed
 *     .rpe-eco-routes/__eyebrow/__selector/__tab/__panel entirely — no section after the grid.
 *     Added .rpe-block--eco (the ECO card itself, always a plain div — cursor:default, not a
 *     click target), .rpe-block__routes (tab row), .rpe-block__route-tab (pill button, reuses
 *     --rpe-accent so it matches ECO's own green), .rpe-block__route-summary (compact in-card
 *     summary line). The shared story pane below the grid keeps its original .rpe-story styling
 *     completely unchanged — only relationship-popup-engine.js's new ecoBlockHtml()/
 *     ecoRouteStoryHtml() changed where the tab CONTROL lives, not how the story content looks.
 *   v1.4.0 — 2026-07-16 — APPROVED FOUR-BLOCK ARCHITECTURE CORRECTION (owner instruction, later
 *     superseded above). Added .rpe-eco-routes/__eyebrow/__selector/__tab/__panel — a separate
 *     section after the grid. Rules retained in change history only; removed in v1.5.0.
 *   v1.3.2 — 2026-07-15 — LIVE VERIFICATION FIX, part 2 (POINT 7): the v1.3.1 corner-inset fix was
 *     not the whole story — a debug outline test proved the button's box was correctly sized and
 *     positioned (not clipped by overflow at all); the real cause was .rpe-head's z-index:2 (needed
 *     for its sticky positioning) painting its own opaque background straight over the close
 *     button, which had no z-index of its own. Added z-index: 5 to the close button. Verified live:
 *     full circle and X icon now render correctly.
 *   v1.3.1 — 2026-07-15 — LIVE VERIFICATION FIX (POINT 7, caught in the real-browser screenshot
 *     pass against the actual Eco Guild popup): the close button at top/right: 0.6rem sat closer
 *     to the panel's corner than the panel's own border-radius (1.1rem), so the panel's
 *     `overflow: hidden` clipped roughly half the circular button — confirmed live via a zoomed
 *     screenshot. Repositioned to top/right: 1.1rem (matching the panel's own corner radius, which
 *     fully clears the curved clip region) and widened .rpe-head's right padding from 2.5rem to
 *     4rem to keep clearing the relocated button.
 *   v1.3.0 — 2026-07-15 — SHARED POPUP VISUAL PARITY — REAL DEFECT PASS (owner rejection, same-named
 *     doc, 2026-07-15: "The map repair is accepted. The popup UI is not accepted.").
 *
 *     POINT 1 — Explicit SDXStage variant. All width/position/surface/scrolling/close-control CSS
 *     that used to be scoped via the fragile `.sdx-stage.is-anchored:has([data-slot="relationship"])`
 *     / `.sdx-stage:has([data-slot="relationship"])` selectors is now scoped ONLY to the stable
 *     `[data-stage-variant="relationship-popup"]` selector, set by relationship-popup-engine.js
 *     passing `variant: 'relationship-popup'` to `SDXStage.open()` (sdx-stage.js now applies it as
 *     `data-stage-variant` on the active panel/modal element and clears it on close — see sdx-stage.js
 *     changelog). :has() is a live DOM query the browser must re-evaluate; a plain attribute selector
 *     set explicitly by the caller is the "governed shared variant" the rejection asked for. No pillar
 *     page (mamadex-locations.html, viridians-location-invitation.html, eco-guilds-farms.html) applies
 *     its own stage skin — this file is the only place stage geometry for this variant is defined.
 *
 *     POINT 2 — Isolated component tokens, root-cause audit. LIVE COMPUTED-STYLE AUDIT (via
 *     document.querySelector + getComputedStyle against the actual MamaDEX popup, 2026-07-15) found
 *     the exact inherited collision: this stylesheet's rules previously read var(--card) / var(--fg) /
 *     var(--muted) / var(--border) / var(--surface-alt) / var(--card-shadow) directly — the SAME
 *     global tokens themes.css flips between a light and a dark block on `[data-theme]` on <html> for
 *     ordinary page content. Meanwhile sdx-stage.css's OWN panel background
 *     (var(--sdx-color-ui-popup-surface) = #123543, HARD RULE "always dark by design", never
 *     theme-responsive — see sdx-stage.css header) sat directly behind this content with NO relation
 *     to the page theme at all. Two consequences, both confirmed live: (a) when the page's stored/
 *     system theme resolved to dark, --card/--fg resolved to near-black-on-dark-card values rendered
 *     on top of the panel's own permanently-dark #123543 shell — text and card boundaries lost nearly
 *     all contrast against each other, reported as an "illegible dark shell"; (b) even in light theme,
 *     the light --card content sat inset inside the panel's own dark padding margin (owned by
 *     sdx-stage.css's shared .sdx-stage-inner padding, not by this file), leaving a visible dark seam
 *     around the light card. NEITHER of these is a page-theme bug to fix with more theme-awareness —
 *     the popup's surface was never supposed to be theme-coupled at all; it is a fixed-identity
 *     component, like the panel shell itself, just light instead of dark. FIX: every rule below now
 *     reads the new isolated --rpe-* custom properties (fixed light hex values, declared once, see the
 *     token block just below this note) instead of the global --card/--fg/--muted/--border/
 *     --surface-alt/--card-shadow chain. No [data-theme="dark"] override was added anywhere in this
 *     file — the component simply never varies. No `!important` was used anywhere in this fix (the
 *     rejection explicitly forbids "random !important declarations" as a patch) — the collision is
 *     resolved by NARROWING scope (the new [data-stage-variant] selector owns the panel-shell
 *     background/border/box-shadow for this variant only, at the same specificity the base
 *     sdx-stage.css panel rule already has, so it wins on cascade order + equal specificity without
 *     needing !important) rather than by brute-forcing a higher-specificity/!important override on top
 *     of the old selectors.
 *     Token placement: the --rpe-* custom properties are declared on BOTH
 *     `.sdx-stage[data-stage-variant="relationship-popup"]` (the true DOM boundary of this component —
 *     SDXStage's own close-button lives as a sibling of .rpe-root inside the same stage element, not a
 *     descendant of it, and CSS custom properties only inherit downward, so anchoring the tokens at
 *     the shared stage-level ancestor is what lets the close-control read the identical isolated
 *     values) AND on `.rpe-root` itself (redundant given the above, but keeps .rpe-root a
 *     self-contained isolation boundary in its own right, matching the rejection's literal token
 *     placement and guarding against a future reuse of this engine inside a different SDXStage
 *     variant/mode).
 *
 *     POINT 4 — Pillar colour independent of actionability. `.rpe-block.is-unavailable` previously set
 *     `--rpe-accent: var(--border)`, which neutralised the pillar accent to grey whenever a
 *     relationship was unavailable — directly contradicts "colour treatment must NOT depend on
 *     actionability." Removed. The accent (now driven by the new `[data-relationship]` attribute —
 *     relationship-popup-engine.js always emits this, `[data-accent]` kept as a compatibility alias
 *     pointing at the same value) is now identical in the available and unavailable states; only
 *     opacity/muted text treatment communicates the unavailable state, never a colour swap.
 *
 *   v1.2.0 — 2026-07-15 — RUNTIME REGRESSION REPAIR. Width restored, scroll containment fixed,
 *     unpublished Storybook media collapse fixed. (Full detail preserved from prior version — see
 *     git history / prior file revision; superseded in structure, not intent, by v1.3.0 above.)
 * ====================================================================================================== */

/* ── Stage shell (relationship-popup variant only) ──
   Everything in this block replaces the former `.sdx-stage.is-anchored:has([data-slot="relationship"])`
   hack (POINT 1). Scoped purely to the explicit variant attribute SDXStage sets from
   relationship-popup-engine.js's `SDXStage.open({ variant: 'relationship-popup', ... })` call — no
   :has() re-evaluation, no page-specific stage skin anywhere else in the codebase. */
.sdx-stage[data-stage-variant="relationship-popup"] {
  /* Isolated component tokens (POINT 2) — declared here too so the panel CHROME (this rule, plus the
     close-control rules below, which are outside .rpe-root) reads the exact same fixed values as the
     engine's own content. Fixed hex, not a var() reference to --card/--fg/etc — this is the whole
     point: the popup never changes with [data-theme] on <html>. Values match the site's existing
     LIGHT-theme card tokens (global.css primitives) exactly, so the popup keeps the site's established
     light-card language, it just never SWITCHES away from it. */
  --rpe-surface:       #f8f5f2;
  --rpe-text:          #111827;
  --rpe-muted:         #374151;
  --rpe-border:        rgba(211, 196, 170, 0.55);
  --rpe-story-surface: hsl(60 20% 98%);
  --rpe-shadow:        0 14px 30px rgba(15, 23, 42, 0.08), 0 1px 2px rgba(15, 23, 42, 0.06);
  /* Hyperlink colour states (v1.9.0) — isolated tokens, same fixed-light-value rule as every
     other --rpe-* token: never sourced from the page's own [data-theme] link colours. Unvisited
     is a clear "this is a link" blue (distinct from body text and from any pillar accent);
     visited is a muted warm brown — deliberately NOT a purple, so it can never read as an
     unrelated MamaDEX association or the browser's own default visited-purple. */
  --rpe-link:          #2a4d8f;
  --rpe-link-visited:  #6b4226;

  width: min(44rem, calc(100vw - 2rem));
  max-width: calc(100vw - 2rem);
  max-height: calc(100dvh - var(--header-height, 4.8125rem) - 3rem);
  display: flex;
  flex-direction: column;
  /* The base .sdx-stage rule (sdx-stage.css) sets overflow-y:auto directly on it, which would scroll
     the close button and header along with the body on any popup taller than max-height. Overriding to
     hidden here and moving the actual scroll to .sdx-stage-slot below keeps the header/close
     permanently in view and lets only the relationship grid + Storybook scroll. */
  overflow: hidden;
  background: var(--rpe-surface);
  color: var(--rpe-text);
  border: 1px solid var(--rpe-border);
  border-radius: 1.1rem;
  box-shadow: var(--rpe-shadow);
}

/* .sdx-stage-inner wraps the close button + (hidden) title + .sdx-stage-slot. It must not add its own
   scrollbar — the slot below owns scrolling — but it does need to shrink correctly inside the now-
   bounded, flex-column panel above. */
.sdx-stage[data-stage-variant="relationship-popup"] .sdx-stage-inner {
  display: flex;
  flex-direction: column;
  flex: 1 1 auto;
  min-height: 0;   /* required for a flex child to actually respect its parent's height clamp */
  overflow: hidden;
}

/* The relationship grid + Storybook now live in the ONLY scrolling region. The engine's own .rpe-head
   is the first element inside .rpe-root, itself the first (only) element inside this slot, so making
   it position:sticky (below) keeps it visually pinned to the top of this scrollport. */
.sdx-stage[data-stage-variant="relationship-popup"] .sdx-stage-slot {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
}

/* The popup renders its own header (title + location), so hide the engine's generic title bar. */
.sdx-stage[data-stage-variant="relationship-popup"] .sdx-stage-title { display: none; }

/* Close control — scoped to the relationship-popup variant only (does not affect any other SDXStage
   consumer). >=44x44px hit area per the accessibility requirement. Positioned absolute against
   .sdx-stage-inner (which no longer scrolls as a whole — see above), so it stays fixed in the panel's
   top-right corner regardless of how far the grid/Storybook below has scrolled. Reads the same isolated
   --rpe-* tokens as the rest of the component (POINT 2) — never the page's --fg/--border/--surface-alt. */
.sdx-stage[data-stage-variant="relationship-popup"] .sdx-stage-close {
  /* POINT 7 FIX (caught live, real-browser screenshot pass): the panel this button sits inside
     has border-radius: 1.1rem AND overflow: hidden (needed to contain scrolling to the body only
     — see the stage-shell rule above). Any child positioned closer to a corner than the corner's
     OWN radius gets partially clipped by that curve regardless of the child's own shape — verified
     live: at top/right: 0.6rem (well inside the 1.1rem curve) roughly half the circular close
     button was invisible, clipped by the panel's own rounded corner. Insetting by the SAME amount
     as the panel's border-radius (1.1rem) fully clears the curved region — the button's bounding
     box then starts exactly where the straight edges resume. */
  top: 1.1rem;
  right: 1.1rem;
  width: 2.75rem;
  height: 2.75rem;
  background: var(--rpe-story-surface);
  border: 1px solid var(--rpe-border);
  /* POINT 7 FIX, part 2 (caught live — the corner-inset fix alone was not enough): .rpe-head is
     position:sticky with z-index:2 (needed so it stays above the scrolling grid/Storybook content
     beneath it). This close button has position:absolute with no z-index of its own (stacks at the
     default "auto" level), which is BELOW any positive-z-index box in the same stacking context —
     .rpe-head's own opaque background was painting straight over the button, since .rpe-head's
     border-box (no horizontal inset at the container level, only internal padding) spans the full
     panel width and overlaps the button's position entirely. Verified live: forcing z-index above
     2 made the previously half-invisible button (and its X icon) render fully and correctly.
     z-index: 5 keeps a clear margin above .rpe-head's 2 without being a "magic number" collision
     risk against any other z-index in this component (nothing else here sets one above 2). */
  z-index: 5;
}
.sdx-stage[data-stage-variant="relationship-popup"] .sdx-stage-close::before,
.sdx-stage[data-stage-variant="relationship-popup"] .sdx-stage-close::after {
  background: var(--rpe-text);
}
.sdx-stage[data-stage-variant="relationship-popup"] .sdx-stage-close:hover {
  background: var(--rpe-border);
}
.sdx-stage[data-stage-variant="relationship-popup"] .sdx-stage-close:focus-visible {
  outline: 2px solid var(--rpe-focus, #8b5cf6);
  outline-offset: 2px;
}

.sdx-stage[data-stage-variant="relationship-popup"] [data-slot="relationship"] { padding-bottom: 0.5rem; }

/* ── .rpe-root — component isolation boundary (POINT 2) ──
   Every element the engine renders lives inside this wrapper (relationship-popup-engine.js v1.4.0
   wraps buildHtml()'s output in <div class="rpe-root">). Tokens redeclared here (same fixed values as
   the stage-level declaration above) so .rpe-root is a self-contained isolation boundary in its own
   right, not solely reliant on the ancestor stage rule. */
.rpe-root {
  --rpe-surface:       #f8f5f2;
  --rpe-text:          #111827;
  --rpe-muted:         #374151;
  --rpe-border:        rgba(211, 196, 170, 0.55);
  --rpe-story-surface: hsl(60 20% 98%);
  --rpe-shadow:        0 14px 30px rgba(15, 23, 42, 0.08), 0 1px 2px rgba(15, 23, 42, 0.06);
  /* Hyperlink colour states (v1.9.0) — isolated tokens, same fixed-light-value rule as every
     other --rpe-* token: never sourced from the page's own [data-theme] link colours. Unvisited
     is a clear "this is a link" blue (distinct from body text and from any pillar accent);
     visited is a muted warm brown — deliberately NOT a purple, so it can never read as an
     unrelated MamaDEX association or the browser's own default visited-purple. */
  --rpe-link:          #2a4d8f;
  --rpe-link-visited:  #6b4226;
  background: var(--rpe-surface);
  color: var(--rpe-text);
}

/* ── Header: mark + eyebrow/title/location, thin shared pillar-accent divider ──
   position:sticky — this is the FIRST element inside the slot's own scrollport, so sticky top:0 pins
   it there as the grid/Storybook below it scroll underneath; background must be opaque so that
   scrolling content doesn't show through. */
.sdx-stage [data-slot="relationship"] .rpe-head {
  position: sticky;
  top: 0;
  z-index: 2;
  display: flex;
  flex-direction: row;
  align-items: flex-start;
  gap: 0.75rem;
  margin: 0 0 1rem;
  padding: 0 4rem 0.85rem 0;   /* right padding clears the corner close button — widened alongside
    the POINT 7 close-button reposition above (1.1rem inset + 2.75rem button = 3.85rem minimum) */
  background: var(--rpe-surface);
  border-bottom: 2px solid var(--rpe-focus, #8b5cf6);
}

/* Entity-pillar divider colour (§E-equivalent) — a SEPARATE mapping from the per-relationship-block
   [data-relationship] one below: this colours the header's OWN divider from the entity's own home
   pillar (relationship-popup-engine.js sets [data-pillar] from entity.pillar), never from a
   sidecar-supplied colour. Same 5 shared tokens the relationship blocks use, plus 'viridians' (a
   Viridians location IS an entity that can own a popup, even though 'viridians' is never one of the
   four universal relationship-block keys). Unset/unrecognised pillar keeps the engine's neutral
   default (--rpe-focus) set above. Scoped identically to the base .rpe-head rule (same specificity) —
   see the v1.2.0 changelog note preserved in git history for why this scoping matters.
   VISUAL PARITY (2026-07-15): verified this mapping renders identically on eco / mamadex / reporters /
   edutower in BOTH the available and unavailable relationship states — this rule only ever colours the
   HEADER divider (the entity's own pillar), which never had an actionability-dependent code path. */
.sdx-stage [data-slot="relationship"] .rpe-head[data-pillar="eco"]        { border-bottom-color: var(--sdx-color-green-deep, #2e5d3a); }
.sdx-stage [data-slot="relationship"] .rpe-head[data-pillar="mamadex"]    { border-bottom-color: var(--sdx-color-mama-violet, #8b5cf6); }
.sdx-stage [data-slot="relationship"] .rpe-head[data-pillar="reporters"] { border-bottom-color: var(--sdx-color-mama-amber, #f5b014); }
.sdx-stage [data-slot="relationship"] .rpe-head[data-pillar="edutower"]  { border-bottom-color: var(--sdx-color-edutower-blue, #56747b); }
.sdx-stage [data-slot="relationship"] .rpe-head[data-pillar="viridians"] { border-bottom-color: var(--sdx-color-viridians-card-mid, #4b4261); }

/* Entity-mark slot — fixed ~4.25rem (68px, inside the accepted 64-72px range) tile, never collapses.
   Sits flush with the top of the header, left of the eyebrow/title column, identical position across
   every pillar. */
.rpe-head__mark {
  flex: 0 0 auto;
  width: 4.25rem;
  height: 4.25rem;
  border-radius: 0.65rem;
  overflow: hidden;
  background: var(--rpe-story-surface);
  border: 1px solid var(--rpe-border);
  display: flex;
  align-items: center;
  justify-content: center;
}
.rpe-head__mark[data-kind="portrait"] { border-radius: 50%; }
.rpe-head__mark img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
.rpe-head__mark[data-kind="logo"] img,
.rpe-head__mark[data-kind="emblem"] img { object-fit: contain; padding: 0.4rem; }
/* No real asset resolved yet — engine-owned generic fallback (never an empty box, never a broken
   image): a monogram tile tinted with whichever pillar colour the header divider above is using. */
.rpe-head__mark--fallback {
  background: color-mix(in srgb, var(--rpe-focus, #8b5cf6) 14%, var(--rpe-story-surface));
}
.rpe-head[data-pillar="eco"] .rpe-head__mark--fallback       { background: color-mix(in srgb, var(--sdx-color-green-deep, #2e5d3a) 16%, var(--rpe-story-surface)); }
.rpe-head[data-pillar="mamadex"] .rpe-head__mark--fallback   { background: color-mix(in srgb, var(--sdx-color-mama-violet, #8b5cf6) 16%, var(--rpe-story-surface)); }
.rpe-head[data-pillar="reporters"] .rpe-head__mark--fallback { background: color-mix(in srgb, var(--sdx-color-mama-amber, #f5b014) 16%, var(--rpe-story-surface)); }
.rpe-head[data-pillar="edutower"] .rpe-head__mark--fallback  { background: color-mix(in srgb, var(--sdx-color-edutower-blue, #56747b) 16%, var(--rpe-story-surface)); }
.rpe-head[data-pillar="viridians"] .rpe-head__mark--fallback { background: color-mix(in srgb, var(--sdx-color-viridians-card-mid, #4b4261) 16%, var(--rpe-story-surface)); }
.rpe-head__mark-initial {
  font-size: 1.5rem;
  font-weight: 700;
  color: var(--rpe-text);
  line-height: 1;
}
/* Canonical pillar-mark fallback tier (POINT 3) — an intermediate tier between a real
   sidecar-resolved asset and the last-resort monogram: a fixed, always-available pillar glyph, used
   when the sidecar has no entity-specific asset AND when a resolved <img> fails to load at runtime
   (relationship-popup-engine.js wires the img's onerror to swap data-kind to "pillar-fallback" and
   re-render this tier rather than leaving a broken image icon). */
.rpe-head__mark[data-kind="pillar-fallback"] {
  display: flex;
  align-items: center;
  justify-content: center;
}
.rpe-head__mark[data-kind="pillar-fallback"] .rpe-head__mark-glyph {
  width: 60%;
  height: 60%;
  border-radius: 0.35rem;
  background: currentColor;
  opacity: 0.85;
}
/* Pillar-fallback glyph colour — same 5 shared tokens as the header divider / monogram tint above.
   Default (unset/unrecognised pillar) inherits .rpe-root's --rpe-text so the glyph is always visible
   even without a matched pillar. */
.rpe-head__mark[data-kind="pillar-fallback"] { color: var(--rpe-text); }
.rpe-head[data-pillar="eco"] .rpe-head__mark[data-kind="pillar-fallback"]        { color: var(--sdx-color-green-deep, #2e5d3a); }
.rpe-head[data-pillar="mamadex"] .rpe-head__mark[data-kind="pillar-fallback"]    { color: var(--sdx-color-mama-violet, #8b5cf6); }
.rpe-head[data-pillar="reporters"] .rpe-head__mark[data-kind="pillar-fallback"] { color: var(--sdx-color-mama-amber, #f5b014); }
.rpe-head[data-pillar="edutower"] .rpe-head__mark[data-kind="pillar-fallback"]  { color: var(--sdx-color-edutower-blue, #56747b); }
.rpe-head[data-pillar="viridians"] .rpe-head__mark[data-kind="pillar-fallback"] { color: var(--sdx-color-viridians-card-mid, #4b4261); }

.rpe-head__text {
  display: flex;
  flex-direction: column;
  gap: 0.2rem;
  min-width: 0;
  flex: 1 1 auto;
}
.rpe-head__eyebrow {
  font-size: 0.72rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--rpe-muted);
  font-weight: 700;
}
.rpe-head__row {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 0.25rem 0.9rem;
}
.rpe-head__title {
  margin: 0;
  flex: 1 1 auto;
  min-width: 0;
  color: var(--rpe-text);
  font-weight: 700;
  letter-spacing: -0.01em;
  font-size: 1.35rem;
  line-height: 1.16;
}
.rpe-head__loc {
  flex: 0 1 auto;
  color: var(--rpe-muted);
  font-size: 0.86rem;
}

/* Mobile (one shared breakpoint, not per-pillar): the mark shrinks slightly rather than disappearing,
   so it still reads as a stable slot at any width. */
@media (max-width: 26rem) {
  .rpe-head__mark { width: 3.25rem; height: 3.25rem; }
  .rpe-head__mark-initial { font-size: 1.15rem; }
}

/* ── 2x2 relationship grid ──
   Interactive blocks are real <a> elements (free keyboard support); unavailable blocks are plain
   <div>s — no pointer cursor, no hover lift, no chevron. */
.rpe-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 0.65rem;
}
.rpe-block {
  --rpe-accent: var(--rpe-focus, #8b5cf6);
  --rpe-tint: color-mix(in srgb, var(--rpe-accent) 8%, transparent);
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  overflow: hidden;
  text-decoration: none;
  color: inherit;
  background: var(--rpe-tint);
  border: 1px solid var(--rpe-border);
  border-left: 0.28rem solid var(--rpe-accent);
  border-radius: 0.65rem;
  padding: 0.7rem 0.85rem 0.75rem 0.85rem;
  min-height: 44px;
  /* Reset for the .rpe-block[type="button"] case (same-page action blocks, e.g. Eco Guild's map
     fly-to) so a <button> reads identically to the <a>/<div> variants. */
  width: 100%;
  font: inherit;
  text-align: left;
  appearance: none;
  -webkit-appearance: none;
  margin: 0;
}

/* Pillar accent mapping (POINT 4) — narrow accents only, applied via [data-relationship], the
   engine's always-emitted canonical attribute. [data-accent] targets the exact same values as a
   compatibility alias (relationship-popup-engine.js still emits both attributes on each block). This
   mapping is now the ONLY thing that determines --rpe-accent — .is-unavailable no longer overrides it
   (see the removed neutralisation rule below this block; POINT 4 fix). */
.rpe-block[data-relationship="eco"],
.rpe-block[data-accent="eco"]        { --rpe-accent: var(--sdx-color-green-deep, #2e5d3a); }
.rpe-block[data-relationship="mamadex"],
.rpe-block[data-accent="mamadex"]    { --rpe-accent: var(--sdx-color-mama-violet, #8b5cf6); }
.rpe-block[data-relationship="reporters"],
.rpe-block[data-accent="reporters"]  { --rpe-accent: var(--sdx-color-mama-amber, #f5b014); }
.rpe-block[data-relationship="edutower"],
.rpe-block[data-accent="edutower"]   { --rpe-accent: var(--sdx-color-edutower-blue, #56747b); }
.rpe-block[data-relationship="viridians"],
.rpe-block[data-accent="viridians"]  { --rpe-accent: var(--sdx-color-viridians-card-mid, #4b4261); }

/* Lower-panel accent mapping (v1.9.0, corrected v1.10.0) — mirrors the
   .rpe-block[data-relationship] mapping above. v1.9.0 originally keyed this off
   data-relationship-panel (the panel's raw relationship KEY), which matches for every fixture
   whose key happens to equal its own pillar name ("eco", "mamadex"...) but silently fails for a
   BOUND/aliased relationship — e.g. Viridians' own eco_guild block has key:"eco_guild" but
   accent:"eco" (Viridians references the Eco Guild as a relationship, so its key names the
   relationship, not the pillar). That mismatch is exactly why the reported "ECO Conservation
   story chevron reads as MamaDEX violet" defect was still reproducible live in the Viridians
   fixture even after v1.9.0 shipped: data-relationship-panel="eco_guild" never matched
   [data-relationship-panel="eco"], so --rpe-accent stayed unset and .rpe-story__chev fell through
   to the generic violet default. Corrected by keying off data-accent-panel instead — a new
   attribute relationship-popup-engine.js v2.3.0's relationshipPanelHtml() emits alongside the
   unchanged data-relationship-panel (still needed as-is for activateTopLevelBlock()'s show/hide
   toggle, which must keep matching by the relationship's own key, not its pillar). A routed
   block's own route-story panels (e.g. Eco Guild's Restoration/Conservation) are nested inside
   this same wrapper, so they inherit the correct accent by ordinary cascade with no extra
   selector needed. The fifth entry (viridians) mirrors the block-level mapping above for the
   same reason: a story panel could just as plausibly present a bound Viridians relationship from
   inside another pillar's popup. */
.rpe-panel[data-accent-panel="eco"]        { --rpe-accent: var(--sdx-color-green-deep, #2e5d3a); }
.rpe-panel[data-accent-panel="mamadex"]    { --rpe-accent: var(--sdx-color-mama-violet, #8b5cf6); }
.rpe-panel[data-accent-panel="reporters"]  { --rpe-accent: var(--sdx-color-mama-amber, #f5b014); }
.rpe-panel[data-accent-panel="edutower"]   { --rpe-accent: var(--sdx-color-edutower-blue, #56747b); }
.rpe-panel[data-accent-panel="viridians"]  { --rpe-accent: var(--sdx-color-viridians-card-mid, #4b4261); }

.rpe-block__media {
  display: block;
  width: 100%;
  aspect-ratio: 4 / 3;
  border-radius: 0.5rem;
  overflow: hidden;
  background: var(--rpe-story-surface);
}
.rpe-block__media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
.rpe-block__media--icon {
  aspect-ratio: auto;
  width: 1.9rem;
  height: 1.9rem;
  border-radius: 0.4rem;
  background: transparent;
}
.rpe-block__media--icon img { object-fit: contain; }

.rpe-block__body { display: flex; flex-direction: column; gap: 0.15rem; min-width: 0; }
.rpe-block__label {
  font-size: 0.74rem;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  font-weight: 700;
  color: var(--rpe-text);
  padding-right: 1.6rem;   /* clears a routed block's own explore chevron, when it has one */
}
.rpe-block__title {
  font-size: 0.92rem;
  font-weight: 600;
  color: var(--rpe-text);
  line-height: 1.28;
}
.rpe-block__meta {
  font-size: 0.78rem;
  color: var(--rpe-muted);
}
.rpe-block__summary {
  font-size: 0.82rem;
  color: var(--rpe-text);
  line-height: 1.4;
}

/* .rpe-block__chev — a routed block's own small explore/action control (exploreControlHtml() in
   the engine), reused unchanged since v1.6.0. The old .rpe-block.is-actionable/.is-unavailable
   whole-card link states these hover rules used to key off (legacyBlockHtml(), pre-v1.8.0) are
   gone — every block is a selector now (.rpe-tab / .rpe-tab.is-active / .rpe-tab[data-status]
   above own that treatment). */
.rpe-block__chev {
  position: absolute;
  top: 0.6rem;
  right: 0.65rem;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 1.4rem;
  height: 1.4rem;
  border-radius: 50%;
  background: var(--rpe-accent);
  color: #fff;
  transition: transform 0.2s ease;
}
/* v1.9.0: sized for the canonical CHEVRON_SVG (relationship-popup-engine.js) — this control no
   longer renders a text glyph, so the old font-size/font-weight/line-height rules (sized for a
   character, not an icon) are removed. */
.rpe-block__chev svg { width: 0.75rem; height: 0.75rem; }

/* ── Storybook: whole panel clickable, image left / copy right, serif title ── */
.rpe-story {
  margin-top: 0.85rem;
  border-top: 1px solid var(--rpe-border);
  padding-top: 0.85rem;
  display: grid;
  grid-template-columns: minmax(0, 7.5rem) minmax(0, 1fr);
  gap: 0.9rem;
  align-items: center;
  text-decoration: none;
  color: inherit;
  position: relative;
}
.rpe-story__media {
  margin: 0;
  aspect-ratio: 4 / 3;
  border-radius: 0.6rem;
  overflow: hidden;
  border: 1px solid var(--rpe-border);
  background: var(--rpe-story-surface);
}
.rpe-story__media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  transition: transform 0.3s ease;
}
/* Neutral Storybook mark — the unpublished state's own media-column filler: a calm glyph tile,
   deliberately NOT shaped like a photograph placeholder, so it never implies a Storybook exists when
   it doesn't, while still occupying the same structural column the published state's thumbnail sits
   in. */
.rpe-story__mark--unpublished {
  width: 100%;
  aspect-ratio: 4 / 3;
  border-radius: 0.6rem;
  border: 1px dashed var(--rpe-border);
  background-color: var(--rpe-story-surface);
  /* LABORATORY CORRECTION (owner instruction, 2026-07-15, POINT 4): "Replace the unpublished
     empty-square placeholder with one shared neutral Storybook icon." The previous ::before
     hollow-square (a plain border-only box) is replaced with ONE real, shared SVG glyph — an
     open-book shape, identical for every pillar's unpublished state, never a per-pillar variant.
     background-image (not mask-image) — the icon's colour is baked into the SVG file itself as a
     fixed hex matching --rpe-muted exactly, since this component's tokens are deliberately
     non-theme-reactive; a plain background-image avoids any mask-image browser-support risk for
     no benefit, since the colour was never going to vary anyway. */
  background-image: url('/assets/icons/partials/icon-storybook-unpublished.svg');
  background-repeat: no-repeat;
  background-position: center;
  background-size: 2.1rem 2.1rem;
}
.rpe-story__body { display: flex; flex-direction: column; gap: 0.3rem; min-width: 0; }
.rpe-story__eyebrow {
  font-size: 0.72rem;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--rpe-muted);
  font-weight: 700;
}
.rpe-story__title {
  font-family: Georgia, 'Iowan Old Style', 'Palatino Linotype', serif;
  font-size: 1.08rem;
  font-weight: 600;
  color: var(--rpe-text);
  line-height: 1.24;
  padding-right: 1.6rem;
}
.rpe-story__summary {
  font-size: 0.88rem;
  color: var(--rpe-text);
  line-height: 1.45;
}
.rpe-story__chev {
  position: absolute;
  top: 0.85rem;
  right: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 1.5rem;
  height: 1.5rem;
  border-radius: 50%;
  /* v1.9.0: read the cascaded per-pillar accent (see .rpe-panel[data-relationship-panel] above)
     first, falling back to the generic default only when no pillar context is present (e.g. the
     defensive contract.storybook-only path in buildHtml(), which has no relationship key at
     all) — same two-tier fallback pattern .rpe-block itself already uses. Previously hardcoded
     straight to --rpe-focus, which is why every story chevron rendered identically regardless
     of which pillar's story was open. */
  background: var(--rpe-accent, var(--rpe-focus, #8b5cf6));
  color: #fff;
  transition: transform 0.2s ease;
}
.rpe-story__chev svg { width: 0.8rem; height: 0.8rem; }
.rpe-story.is-actionable {
  cursor: pointer;
  transition: transform 0.2s ease;
}
.rpe-story.is-actionable:hover,
.rpe-story.is-actionable:focus-visible {
  transform: translateY(-1px);
}
.rpe-story.is-actionable:hover .rpe-story__media img,
.rpe-story.is-actionable:focus-visible .rpe-story__media img {
  transform: scale(1.05);
}
.rpe-story.is-actionable:hover .rpe-story__chev,
.rpe-story.is-actionable:focus-visible .rpe-story__chev {
  transform: translateX(2px);
}
.rpe-story.is-actionable:focus-visible {
  outline: 2px solid var(--rpe-focus, #8b5cf6);
  outline-offset: 2px;
}

/* Unpublished: retain the section (never omitted, never collapsed), calm inert state, no interactive
   styling. The grid stays the SAME 2-column media|text layout the published state uses. */
.rpe-story.is-unpublished {
  color: var(--rpe-muted);
}
.rpe-story.is-unpublished .rpe-story__title,
.rpe-story.is-unpublished .rpe-story__eyebrow { color: var(--rpe-muted); }

/* Text-only (v1.9.0, no-media rendering correction): a PUBLISHED story with no image (e.g. the
   canonical Sielsgrond/Conservation route's deliberate `image: null`) renders no media element
   at all (relationship-popup-engine.js's storybookHtml()) — collapse the two-column media|prose
   grid to a single prose column so no empty gap remains where the thumbnail column used to sit.
   Does not apply to the unpublished state, which intentionally keeps its own neutral mark tile
   (and therefore the two-column layout) — see relationship-popup-engine.js's own note. */
.rpe-story.is-text-only { grid-template-columns: 1fr; }

/* ── Unified relationship selector (engine v1.7.0, generalised in v1.8.0) ──
   Every relationship block in a contract is ONE ARIA tablist ([role="tablist"] on .rpe-grid,
   [role="tab"] on each .rpe-block) — selecting any of them drives the same shared lower panel.
   Applies uniformly whether a block is a native <button role="tab"> or a plain <div role="tab">
   (a ROUTED block — one that carries its own rel.routes, e.g. Eco Guild's Restoration/
   Conservation pair; relationship-popup-engine.js's tabBlockHtml() picks the tag generically off
   rel.routes, never off a hardcoded pillar key), since both carry .rpe-tab. No pillar name is
   referenced by this rule or the ones below it — v1.8.0 removed the legacy non-unified whole-
   card-link path entirely, so every contract's relationships now render this way. */
.rpe-tab {
  cursor: pointer;
}
.rpe-tab:hover {
  border-color: color-mix(in srgb, var(--rpe-accent) 45%, var(--rpe-border));
}
.rpe-tab:focus-visible {
  outline: 2px solid var(--rpe-accent);
  outline-offset: 2px;
}
/* Active-tab treatment — a filled inset ring plus the block's own accent border colour,
   restrained rather than a full redesign, matching the originally-approved Eco Guild pattern. */
.rpe-tab.is-active {
  border-color: var(--rpe-accent);
  background: color-mix(in srgb, var(--rpe-accent) 16%, transparent);
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--rpe-accent) 55%, transparent);
}
/* Honest "nothing published yet" muting for a top-level tab — text only, NEVER cursor:default or
   removal from the tab order: every block stays keyboard- and pointer-selectable regardless of
   whether it currently has a published relationship, so a visitor can always open it and read
   the honest empty-state explanation in the shared panel below. */
.rpe-tab[data-status="unavailable"] .rpe-block__label,
.rpe-tab[data-status="unavailable"] .rpe-block__title,
.rpe-tab[data-status="unavailable"] .rpe-block__summary {
  color: var(--rpe-muted);
}

/* ── The shared lower panel's pre-rendered children ──
   relationshipPanelHtml() renders one panel per relationship together; only the active one lacks
   [hidden]. No layout rules needed beyond that — each panel's own content (.rpe-story, reused
   unchanged via storybookHtml()/routeStoryHtml()) already carries its own spacing/border-top, so
   the ONE visible panel looks identical to the single shared story pane this replaced. */
.rpe-panels { display: block; }

/* ── A routed block's own internal routes (e.g. Eco Guild's Restoration / Conservation) ──
   Live INSIDE the relationship block itself, not as a section after the grid (owner correction,
   2026-07-16, superseding the prior .rpe-eco-routes section-after-grid attempt). .rpe-block--routed
   is always a plain <div> (relationship-popup-engine.js's tabBlockHtml()/routedBlockInnerHtml(),
   applied to ANY block whose data supplies rel.routes — never a hardcoded pillar key) — never the
   native <button role="tab"> a plain block uses — because a route's own tab <button>s are real
   interactive children and nesting interactive elements inside a <button> is invalid HTML. It
   keeps the SAME [data-relationship] accent-colour hook as every other block (mapping already
   declared above), so its left accent border/tint match unchanged. */
.rpe-block--routed .rpe-block__body { margin-bottom: 0.15rem; }
.rpe-block__routes {
  display: flex;
  flex-wrap: wrap;
  gap: 0.35rem;
  margin: 0.3rem 0 0.4rem;
}
.rpe-block__route-tab {
  font: inherit;
  font-size: 0.72rem;
  font-weight: 700;
  cursor: pointer;
  padding: 0.3rem 0.65rem;
  border: 1px solid var(--rpe-border);
  border-radius: 999px;
  background: var(--rpe-story-surface);
  color: var(--rpe-muted);
  appearance: none;
  -webkit-appearance: none;
  margin: 0;
  transition: background-color 0.18s ease, color 0.18s ease, border-color 0.18s ease;
}
.rpe-block__route-tab:hover { border-color: var(--rpe-accent); }
.rpe-block__route-tab:focus-visible {
  outline: 2px solid var(--rpe-accent);
  outline-offset: 2px;
}
.rpe-block__route-tab.is-active {
  background: var(--rpe-accent);
  border-color: var(--rpe-accent);
  color: #fff;
}
.rpe-block__route-summary {
  display: block;
  font-size: 0.8rem;
  color: var(--rpe-text);
  line-height: 1.4;
}
/* exploreControlHtml() reuses the existing .rpe-block__chev badge/position exactly — no new
   rule needed beyond making it focusable-visible, since it is now a real <a>/<button> rather than
   a decorative aria-hidden span. */
.rpe-block--routed .rpe-block__chev:focus-visible {
  outline: 2px solid var(--rpe-accent);
  outline-offset: 2px;
}

/* ── Secondary actions row — outside the four blocks, visually restrained ── */
.rpe-secondary {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem 1rem;
  margin-top: 0.9rem;
  padding-top: 0.7rem;
  border-top: 1px solid var(--rpe-border);
}
.rpe-secondary__link {
  font-size: 0.85rem;
  text-decoration: underline;
  text-underline-offset: 2px;
}
/* Deliberate link/visited/hover/focus-visible/active states (v1.9.0) — previously this was a flat
   --rpe-muted colour with no distinct :visited treatment (read as de-emphasised text rather than
   a hyperlink). Underline is always present regardless of state, so hover is never the ONLY
   interactivity signal. :visited uses the governed --rpe-link-visited token (a muted brown) —
   never suppressed or forced to match :link, and never the browser's default visited-purple. */
.rpe-secondary__link:link    { color: var(--rpe-link); }
.rpe-secondary__link:visited { color: var(--rpe-link-visited); }
.rpe-secondary__link:hover {
  color: var(--rpe-text);
  text-decoration-thickness: 2px;
}
.rpe-secondary__link:focus-visible {
  color: var(--rpe-text);
  outline: 2px solid var(--rpe-accent, var(--rpe-focus, #8b5cf6));
  outline-offset: 2px;
}
.rpe-secondary__link:active { color: var(--rpe-text); }

.rpe-empty { margin: 0; color: var(--rpe-muted); }

/* ── Reduced motion: restrained movement only; remove it entirely when requested ── */
@media (prefers-reduced-motion: reduce) {
  .rpe-block__chev,
  .rpe-block__route-tab,
  .rpe-story.is-actionable,
  .rpe-story__media img,
  .rpe-story__chev {
    transition: none !important;
  }
  .rpe-story.is-actionable:hover,
  .rpe-story.is-actionable:focus-visible {
    transform: none !important;
  }
  .rpe-story.is-actionable:hover .rpe-story__media img,
  .rpe-story.is-actionable:focus-visible .rpe-story__media img {
    transform: none !important;
  }
}

/* ── Responsive (engine-owned, not per-sidecar) ──
   Desktop/tablet: two-column grid, image-left Storybook (rules above).
   Mobile: SDXStage's own breakpoint turns the panel into the governed bottom-sheet; the 4-block grid
   collapses to one column and the Storybook stacks image-above-text so no interactive surface drops
   below the 44px target width on a narrow sheet. */
@media (max-width: 40rem) {
  .rpe-grid { grid-template-columns: 1fr; }
  .rpe-story { grid-template-columns: 1fr; }
  .rpe-story__media,
  .rpe-story__mark--unpublished { max-width: 10rem; }
}
