/* ==========================================================================
   SOLUTIONS EXPERIENCE — styles (prefix: .sxe-)
   Restrained / architectural / white-glass — matches the host design tokens
   (--white,--soft,--glass-w,--titanium,--lgrey,--graphite,--red,--ink-*,
   --hair,--spring,--springy,--r-*). The renders remain the visual hero;
   the red accent is used only for the primary CTA and live indicators.
   ========================================================================== */
.sxe-head{display:flex;justify-content:space-between;align-items:flex-end;gap:24px;flex-wrap:wrap;margin-bottom:16px}
.sxe-headline .lede{margin-top:8px}

/* Sized so the headline + map + hint text all fit inside one ordinary
   browser window at 100% zoom without scrolling — a flat vh fraction alone
   over- or under-shoots depending on window height, since the page header
   above this section is a near-fixed number of pixels regardless of
   viewport size; subtracting that out and clamping keeps it fitting from
   short laptop screens up to large desktop monitors. */
.sxe-frame{position:relative;overflow:hidden;padding:0;
  height:clamp(400px, calc(100vh - 392px), 760px);background:#0f1013}
.sxe-frame::before,.sxe-frame::after{z-index:5}

.sxe-viewport{position:absolute;inset:0;z-index:1;overflow:hidden;cursor:grab;touch-action:none;outline:none}
.sxe-viewport:active{cursor:grabbing}
.sxe-viewport:focus-visible{outline:2px solid var(--red);outline-offset:-2px}
.sxe-viewport.is-dim::after{content:"";position:absolute;inset:0;z-index:4;pointer-events:none;
  background:rgba(10,10,14,0.30);transition:opacity .45s var(--spring)}

.sxe-scenes{position:absolute;inset:0}
.sxe-scene{position:absolute;inset:0;opacity:0;pointer-events:none;
  transition:transform .55s var(--spring)}
.sxe-scene.is-on{opacity:1;pointer-events:auto}
/* Momentary "settling into focus" treatment applied for one frame the
   instant a scene activates (see setActiveScene()), then immediately
   released so the transition rule above eases it back to rest. Every scene
   swap that changes which render is showing now happens fully behind the
   opaque .sxe-veil (see below), so this is purely a polish flourish on the
   reveal — never a way of hiding a cut between two different photos, which
   is what a blur+crossfade of this element used to do and read as
   ghosting/glitching. */
.sxe-scene.is-entering{transform:scale(1.03)}
.sxe-layer{position:absolute;left:0;top:0;transform-origin:0 0;will-change:transform}
.sxe-img{position:absolute;left:0;top:0;width:100%;height:100%;display:block;user-select:none;pointer-events:none}

/* ---------- transition veil --------------------------------------------
   Covers the viewport while a scene swap (master <-> solution, solution
   <-> a different solution) happens underneath it, so the two different
   renders are never simultaneously visible — see veilTransition() in the
   engine. Sits above every scene (all scene-internal z-indexes top out at
   3) and below the always-reachable nav (fixed, body-level, z-index 560). */
.sxe-veil{position:absolute;inset:0;z-index:5;pointer-events:none;
  background:radial-gradient(circle at 50% 50%, #171a20 0%, #0b0c10 78%);
  opacity:0;transition:opacity .42s ease}
.sxe-veil.is-on{opacity:1;transition:opacity .22s ease}

/* ---------- ambient "brightening sky" highlight, every scene ---------- */
.sxe-sun{position:absolute;border-radius:50%;pointer-events:none;z-index:1;
  background:radial-gradient(circle, rgba(255,246,220,0.95) 0%, rgba(255,214,140,0.5) 32%, rgba(255,190,110,0) 72%);
  mix-blend-mode:screen;will-change:transform,opacity;
  animation:sxeSunBreathe 5.5s ease-in-out infinite}
@keyframes sxeSunBreathe{
  0%,100%{transform:translate(-50%,-50%) scale(0.9);opacity:.5}
  50%{transform:translate(-50%,-50%) scale(1.12);opacity:1}
}

/* ---------- master-only: subtle sky drift + water shimmer ---------------
   Two thin, full-width live-overlay bands (positioned from SXE_DATA.images.
   master.sky/water) — the render pixels are never touched, this only adds
   a soft moving gradient on top so the sky and water read as gently alive
   rather than a frozen photo. Kept restrained and slow deliberately. */
.sxe-sky{position:absolute;left:-15%;width:130%;pointer-events:none;z-index:1;
  background:linear-gradient(90deg, rgba(255,255,255,0) 0%, rgba(255,250,235,0.30) 20%, rgba(255,255,255,0) 42%, rgba(255,255,255,0) 60%, rgba(214,231,255,0.22) 82%, rgba(255,255,255,0) 100%);
  background-size:220% 100%;mix-blend-mode:soft-light;
  animation:sxeSkyDrift 90s linear infinite}
@keyframes sxeSkyDrift{0%{background-position:0% 0}100%{background-position:-220% 0}}
.sxe-water{position:absolute;left:0;width:100%;pointer-events:none;z-index:2;
  background:linear-gradient(100deg, rgba(255,255,255,0) 0%, rgba(255,244,214,0) 34%, rgba(255,244,214,0.16) 47%, rgba(255,255,255,0.22) 50%, rgba(255,244,214,0.16) 53%, rgba(255,244,214,0) 66%, rgba(255,255,255,0) 100%);
  background-size:260% 100%;mix-blend-mode:screen;
  animation:sxeWaterShimmer 22s ease-in-out infinite}
@keyframes sxeWaterShimmer{0%,100%{background-position:120% 0}50%{background-position:-20% 0}}
/* ---------- master-only: real water ripple ------------------------------
   A cropped, feTurbulence-displaced copy of the same water band (see
   buildWaterRipple() in the engine) — genuine pixel-level ripple motion,
   not just a color sweep. Sits under the .sxe-water sheen above so the
   glint still reads as light glancing off the now-moving water. */
.sxe-water-fx{position:absolute;left:0;width:100%;pointer-events:none;z-index:1;display:block}

.sxe-paths{position:absolute;left:0;top:0;pointer-events:none;overflow:visible;z-index:2}
/* connectivity lines are always live now, in every scene — a soft base
   flow at rest, brightening to the hot state on hover/focus rather than
   switching from fully static, so the map always "feels alive" */
.sxe-path{fill:none;stroke:rgba(206,27,40,0.30);stroke-width:5;stroke-linecap:round;stroke-linejoin:round;
  stroke-dasharray:2 17;animation:sxeFlow 2.6s linear infinite;
  transition:stroke .35s var(--spring),stroke-width .35s var(--spring)}
.sxe-path.is-hot{stroke:rgba(206,27,40,0.92);stroke-width:7;stroke-dasharray:2 16;animation:sxeFlow 1.1s linear infinite}
/* master-level network lines: always live, never gated on hover — the
   "wide preview" connections the whole landscape into one interactive map */
.sxe-path-net{stroke:rgba(206,27,40,0.62);stroke-width:6;stroke-dasharray:2 17;
  animation:sxeFlow 1.15s linear infinite;filter:drop-shadow(0 0 6px rgba(206,27,40,0.35))}
@keyframes sxeFlow{to{stroke-dashoffset:-38}}
/* bright amber "data flow" mark travelling on top of every connectivity
   line (master network lines and every solution's own routes) — layered
   exactly over the steady red line's own "d" so the animation itself is
   never in question: a small, high-contrast, continuously moving dash,
   distinct from the red line underneath it rather than a subtle
   dash-offset crawl on the red alone. */
.sxe-path-flow{fill:none;stroke:#FFC53D;stroke-width:3.2;stroke-linecap:round;
  stroke-dasharray:2 38;opacity:.92;
  filter:drop-shadow(0 0 4px rgba(255,197,61,0.9)) drop-shadow(0 0 9px rgba(255,197,61,0.45));
  animation:sxeFlowFast 1.7s linear infinite}
.sxe-path-flow-net{stroke-width:4;stroke-dasharray:2 34;animation-duration:1.35s}
@keyframes sxeFlowFast{to{stroke-dashoffset:-80}}
/* master-only "always something moving" — a small bright pulse travelling
   the network lines via SMIL <animateMotion> (see buildMasterPaths()) */
.sxe-pulse-dot{fill:#fff;filter:drop-shadow(0 0 3px rgba(255,255,255,0.9)) drop-shadow(0 0 7px rgba(206,27,40,0.75))}
/* solution-level: one restrained pulse per named route (buildSolutionContent()) —
   dimmer than the master's so it reads as ambient flow, not a HUD, even when
   the camera is zoomed in close on a single product */
.sxe-pulse-dot-sol{opacity:.8;filter:drop-shadow(0 0 2px rgba(255,255,255,0.85)) drop-shadow(0 0 5px rgba(206,27,40,0.6))}
/* master-only faint twinkle overlay — distant-light ambience, restrained
   (low-opacity, staggered, no color/glow beyond a soft white halo) so the
   landscape reads as quietly alive rather than a HUD */
.sxe-twinkle{position:absolute;width:4px;height:4px;border-radius:50%;background:rgba(255,255,255,0.85);
  box-shadow:0 0 5px 1px rgba(255,255,255,0.45);pointer-events:none;opacity:0;
  animation:sxeTwinkle 4s ease-in-out infinite}
@keyframes sxeTwinkle{0%,100%{opacity:0}45%,55%{opacity:.8}}
/* master-only ambient birds: a slow horizontal drift across the sky band
   (--sxe-bird-dist, set per-bird in buildMasterBirds()) plus a gentle
   vertical bob; the wing flap itself is a fast SMIL path-morph on the
   glyph inside (see the engine). Restrained/monochrome — reads as
   distant birds, not an icon set. */
.sxe-bird{position:absolute;pointer-events:none;will-change:transform;
  animation:sxeBirdFly linear infinite}
.sxe-bird svg{display:block;overflow:visible}
.sxe-bird path{fill:none;stroke:rgba(26,28,34,0.68);stroke-width:1.8;stroke-linecap:round}
@keyframes sxeBirdFly{
  0%{transform:translate(0,0);opacity:0}
  8%,88%{opacity:1}
  25%{transform:translate(calc(var(--sxe-bird-dist) * 0.25),-26px)}
  50%{transform:translate(calc(var(--sxe-bird-dist) * 0.5),6px)}
  75%{transform:translate(calc(var(--sxe-bird-dist) * 0.75),-21px)}
  100%{transform:translate(var(--sxe-bird-dist),0);opacity:0}
}
/* zone hover-highlight box — confirms exactly which building a hotspot targets */
.sxe-zone-hit{fill:rgba(206,27,40,0.08);stroke:rgba(206,27,40,0.85);stroke-width:5;stroke-dasharray:10 8;
  opacity:0;transition:opacity .3s var(--spring);vector-effect:non-scaling-stroke}
.sxe-zone-hit.is-on{opacity:1}
.sxe-zone-hit{display:none!important}
.sxe-markers{position:absolute;left:0;top:0;z-index:3}

/* ---------- master zones ---------- */
.sxe-zone{position:absolute;left:0;top:0;transform:translate(-50%,-50%) scale(var(--iv,1));
  display:flex;align-items:center;min-width:44px;min-height:44px;justify-content:center;
  background:none;border:0;padding:0;cursor:pointer}
.sxe-zone-ring{width:16px;height:16px;border-radius:50%;background:var(--red);
  box-shadow:0 0 0 4px rgba(206,27,40,0.22),0 2px 10px rgba(0,0,0,0.35);position:relative;
  animation:sxeZoneBreathe 1.9s ease-in-out infinite}
.sxe-zone-ring::before{content:"";position:absolute;inset:3px;border-radius:50%;background:#fff}
.sxe-zone-ring::after{content:"";position:absolute;inset:-10px;border-radius:50%;border:2px solid rgba(206,27,40,0.75);
  opacity:0;pointer-events:none;animation:sxePulse 1.8s ease-out infinite}
@keyframes sxeZoneBreathe{0%,100%{transform:scale(1);box-shadow:0 0 0 4px rgba(206,27,40,0.22),0 2px 10px rgba(0,0,0,.35)}50%{transform:scale(1.16);box-shadow:0 0 0 8px rgba(206,27,40,0.32),0 2px 10px rgba(0,0,0,.35)}}
@keyframes sxePulse{0%{transform:scale(0.6);opacity:.95}100%{transform:scale(2.6);opacity:0}}
.sxe-zone-card{position:absolute;left:26px;top:50%;transform:translateY(-50%);white-space:nowrap;
  display:flex;flex-direction:column;gap:2px;padding:10px 16px;border-radius:var(--r-lg);
  background:rgba(255,255,255,0.94);box-shadow:0 10px 28px rgba(0,0,0,0.18);
  opacity:0;transform:translateY(-50%) translateX(-6px);transition:opacity .3s var(--spring),transform .3s var(--spring);
  pointer-events:none}
.sxe-zone:hover .sxe-zone-card,.sxe-zone:focus-visible .sxe-zone-card{opacity:1;transform:translateY(-50%) translateX(0)}
.sxe-zone-card b{font-size:13px;color:var(--graphite)}
.sxe-zone-card small{font-size:10.5px;letter-spacing:.03em;color:var(--ink-3)}
.sxe-zone-card .go{display:flex;align-items:center;gap:5px;font-size:11px;font-weight:600;color:var(--red);margin-top:4px}
.sxe-zone-card .go svg{width:13px;height:13px}
[dir="rtl"] .sxe-zone-card{left:auto;right:26px;transform:translateY(-50%) translateX(6px)}
[dir="rtl"] .sxe-zone:hover .sxe-zone-card,[dir="rtl"] .sxe-zone:focus-visible .sxe-zone-card{transform:translateY(-50%) translateX(0)}

/* ---------- clusters ---------- */
/* Every category marker carries its number AND its name at all times (not
   just on hover) — hover-only text is invisible on touch/mobile, which has
   no hover state at all.
   LAYOUT CONTRACT (shared with .sxe-pin below): the button is a fixed 44px
   square centred on the marker's own image point, and the name pill is
   absolutely positioned BESIDE that square. It is not a centred flex row:
   that put the anchor point in the middle of dot+label together, which
   pushed the numbered dot ~60px off the very thing it points at, and made
   the label impossible to keep inside the frame (flipping a centred row
   moves nothing). The pill still sits inside the <button>, so clicking the
   name works exactly like clicking the dot. .is-flip swaps the pill to the
   other side; the engine sets it per frame when the frame edge is close
   (see updateLabelSides()). */
.sxe-cluster{position:absolute;left:0;top:0;transform:translate(-50%,-50%) scale(var(--iv,1));
  width:44px;height:44px;display:flex;align-items:center;justify-content:center;
  background:none;border:0;padding:0;cursor:pointer}
.sxe-cluster-dot{position:relative;flex:none;display:flex;align-items:center;justify-content:center;
  width:44px;height:44px;border-radius:50%;
  background:linear-gradient(135deg,#E22633,#B5121F);border:1.5px solid rgba(255,255,255,0.7);
  box-shadow:0 6px 18px rgba(206,27,40,0.35);transition:transform .3s var(--spring);
  animation:sxeZoneBreathe 2.1s ease-in-out infinite}
.sxe-cluster-badge{font-size:12px;font-weight:800;color:#fff;letter-spacing:.01em}
.sxe-cluster-count{position:absolute;top:-6px;right:-6px;min-width:18px;height:18px;padding:0 4px;
  border-radius:999px;background:#fff;color:var(--red);font-size:10px;font-weight:800;
  display:flex;align-items:center;justify-content:center;box-shadow:0 2px 6px rgba(0,0,0,.25)}
/* pointer-events:none: the pulse ring is decoration that extends 9px past
   the dot on every side. Left hit-testable it enlarges this marker's
   effective hit area and starts stealing taps aimed at the marker next to
   it. */
.sxe-cluster-dot::before{content:"";position:absolute;inset:-9px;border-radius:50%;border:2px solid rgba(206,27,40,0.55);
  opacity:0;pointer-events:none;animation:sxePulse 2s ease-out infinite}
.sxe-cluster-label{position:absolute;left:calc(100% + 6px);top:50%;transform:translateY(-50%);
  font-size:11.5px;font-weight:700;color:#fff;background:rgba(20,20,24,0.72);
  padding:5px 11px;border-radius:var(--r-pill);white-space:nowrap;backdrop-filter:blur(6px);
  transition:background .22s var(--spring)}
.sxe-cluster.is-left .sxe-cluster-label{left:auto;right:calc(100% + 6px)}
/* is-crowded: the engine found no room for this pill without it landing on
   another marker (see updateLabelSides). The numbered dot stays; only the
   name steps aside, and returns as soon as zooming in makes space.
   visibility (not just opacity) so a hidden pill cannot absorb a tap. */
.sxe-pin.is-crowded .sxe-pin-label,
.sxe-cluster.is-crowded .sxe-cluster-label{opacity:0;visibility:hidden;transition:opacity .15s linear}
/* is-offstage: the marker currently sits where it could not be tapped —
   under the floating nav bar, or outside the frame. Hidden rather than left
   as a dead control; it returns as soon as the camera gives it room. */
.sxe-pin.is-offstage,.sxe-cluster.is-offstage{opacity:0;visibility:hidden;pointer-events:none;
  transition:opacity .18s linear}
.sxe-cluster:hover .sxe-cluster-dot,.sxe-cluster:focus-visible .sxe-cluster-dot{transform:scale(1.14)}
.sxe-cluster:hover .sxe-cluster-label,.sxe-cluster:focus-visible .sxe-cluster-label{background:rgba(206,27,40,0.88)}
.sxe-cluster.is-hidden{opacity:0;transform:translate(-50%,-50%) scale(calc(var(--iv,1)*0.5));pointer-events:none}

/* ---------- product pins ---------- */
/* Same rule: the number + name label is always visible, never hover-gated,
   so it reads identically on desktop and on a touch screen. */
.sxe-pin{position:absolute;left:0;top:0;transform:translate(-50%,-50%) scale(var(--iv,1));
  width:44px;height:44px;display:flex;align-items:center;justify-content:center;
  background:none;border:0;padding:0;cursor:pointer}
.sxe-pin-dot{width:26px;height:26px;border-radius:50%;background:#fff;box-shadow:0 4px 14px rgba(0,0,0,0.28);
  display:flex;align-items:center;justify-content:center;flex:none;position:relative;
  transition:transform .25s var(--spring),box-shadow .25s var(--spring);
  animation:sxePinTease 2.4s ease-in-out infinite}
@keyframes sxePinTease{0%,100%{box-shadow:0 4px 14px rgba(0,0,0,0.28),0 0 0 0 rgba(206,27,40,0.45)}50%{box-shadow:0 4px 14px rgba(0,0,0,0.28),0 0 0 7px rgba(206,27,40,0.0)}}
.sxe-pin-dot i{font-style:normal;font-size:10px;font-weight:800;color:var(--red)}
.sxe-pin-label{position:absolute;left:calc(100% - 3px);top:50%;transform:translateY(-50%);
  font-size:11.5px;font-weight:600;color:#fff;background:rgba(20,20,24,0.72);
  padding:5px 10px;border-radius:var(--r-pill);white-space:nowrap;
  transition:background .22s var(--spring);backdrop-filter:blur(6px)}
/* is-left is the single source of truth for which side the pill sits on.
   The engine sets it from the authored preference in SXE_DATA (side:'l'),
   overriding that side whenever it would push the pill out of the frame. */
.sxe-pin.is-left .sxe-pin-label{left:auto;right:calc(100% - 3px)}
.sxe-pin:hover .sxe-pin-dot,.sxe-pin:focus-visible .sxe-pin-dot{transform:scale(1.14);box-shadow:0 6px 20px rgba(0,0,0,0.34)}
.sxe-pin:hover .sxe-pin-label,.sxe-pin:focus-visible .sxe-pin-label{background:rgba(206,27,40,0.88)}
.sxe-pin[hidden]{display:none}

/* ---------- top nav: Back · solution switcher · Home ------------------ */
/* Lives at document.body level (see buildShell()/syncNavRect() in the
   engine), position:fixed and glued to the frame's own box in JS, with a
   z-index above the product drawer's (500) — .glass on the frame below
   sets `isolation:isolate` (and, in dark mode, adds a `filter`), either of
   which would otherwise trap any z-index nested inside the frame in its
   own stacking context, making it impossible for the nav to ever paint
   above — never mind stay clickable above — the drawer's body-level
   sheet. Moving it out of that subtree sidesteps the trap entirely, the
   same fix already used for the drawer itself just below. */
.sxe-nav{position:fixed;z-index:560;pointer-events:none;transition:opacity .25s ease,visibility .25s}
.sxe-nav>*{pointer-events:auto}
/* syncNavRect() (solutions-engine.js) toggles this whenever a site-level
   sheet is open (auth, cart, search, download center, menu) — the nav bar
   steps out of the way instead of floating over the modal. */
.sxe-nav.is-covered{opacity:0;visibility:hidden;pointer-events:none}
.sxe-nav.is-covered>*{pointer-events:none}
.sxe-nav-btn{position:absolute;top:20px;display:flex;align-items:center;gap:.7em;height:44px;padding:0 18px 0 6px;
  border:0;border-radius:var(--r-pill);cursor:pointer;font-size:13px;font-weight:600;color:var(--graphite);
  background:rgba(255,255,255,0.94);box-shadow:0 10px 26px rgba(0,0,0,0.16),inset 0 1px 0 #fff;
  transition:transform .2s var(--spring),box-shadow .3s var(--spring),opacity .25s ease,visibility .25s}
.sxe-nav-btn svg{width:16px;height:16px}
.sxe-nav-btn:hover{box-shadow:0 14px 32px rgba(0,0,0,0.2),inset 0 0 0 1px rgba(206,27,40,0.2)}
.sxe-nav-btn:active{transform:scale(.96)}
.sxe-nav-btn.is-hidden{opacity:0;visibility:hidden;pointer-events:none;transform:translateY(-6px)}
.sxe-nav-back{left:20px}
.sxe-nav-back .ci{width:30px;height:30px;border-radius:50%;background:var(--titanium);display:flex;align-items:center;justify-content:center}
.sxe-nav-back .ci svg{width:14px;height:14px}
.sxe-nav-home{right:20px;padding:0 18px 0 14px}
/* RTL: Back leads from the right edge and points right; Home sits left */
[dir="rtl"] .sxe-nav-btn{padding:0 6px 0 18px}
[dir="rtl"] .sxe-nav-back{left:auto;right:20px}
[dir="rtl"] .sxe-nav-home{right:auto;left:20px;padding:0 14px 0 18px}
.sxe-nav-tabs{position:absolute;left:50%;top:20px;transform:translateX(-50%);display:flex;padding:5px;
  border-radius:var(--r-pill);background:rgba(255,255,255,0.9);box-shadow:0 10px 26px rgba(0,0,0,0.16);
  transition:opacity .25s ease,visibility .25s}
.sxe-nav-tabs.is-hidden{opacity:0;visibility:hidden;pointer-events:none}
/* On the master landscape the tabs are live but nothing is selected yet, so
   the sliding indicator has nowhere to sit — hide just the indicator. */
.sxe-nav-tabs.is-idle .sxe-nav-ind{opacity:0}
.sxe-nav-tab{position:relative;z-index:1;border:0;background:none;cursor:pointer;font-size:12.5px;font-weight:700;
  color:var(--ink-3);padding:9px 18px;border-radius:var(--r-pill);transition:color .25s ease}
.sxe-nav-tab[aria-selected="true"]{color:#fff}
.sxe-nav-ind{position:absolute;top:5px;bottom:5px;left:0;border-radius:var(--r-pill);
  background:linear-gradient(135deg,#E22633,#B5121F);box-shadow:0 6px 16px rgba(206,27,40,0.35);
  transition:transform .4s var(--spring),width .4s var(--spring)}

/* Lives in .sxe-underbar below the map. As a pill floating over the frame it
   swallowed clicks meant for markers beneath it, and forced their names to
   hide to avoid stacking text on text. */
.sxe-hint{margin:0;font-size:11px;font-weight:500;color:var(--ink-3);white-space:nowrap;
  overflow:hidden;text-overflow:ellipsis}

/* ---------- solution bundle CTA ---------- */
/* Sits in .sxe-underbar beneath the map now, not over it. */
.sxe-underbar{display:flex;align-items:center;justify-content:space-between;gap:16px;
  min-height:46px;margin-top:12px}
.sxe-bundle{display:flex;align-items:center;gap:9px;flex:none;
  height:46px;padding:0 20px 0 16px;border:0;border-radius:var(--r-pill);cursor:pointer;
  font-size:12.5px;font-weight:700;color:#fff;background:linear-gradient(135deg,#E22633,#B5121F);
  box-shadow:0 10px 24px rgba(206,27,40,0.28);opacity:0;visibility:hidden;transform:translateY(6px);
  transition:opacity .3s var(--spring),transform .3s var(--spring),visibility .3s,box-shadow .3s var(--spring)}
.sxe-bundle svg{width:17px;height:17px}
.sxe-bundle.is-visible{opacity:1;visibility:visible;transform:translateY(0)}
.sxe-bundle.is-panel-open{opacity:0;visibility:hidden;pointer-events:none;transform:translateY(8px)}
.sxe-bundle:hover{box-shadow:0 16px 34px rgba(206,27,40,0.45)}
.sxe-bundle:active{transform:translateY(0) scale(.97)}


/* ---------- product panel (desktop drawer / mobile bottom sheet) ------- */
/* click-outside-to-close is handled by a document-level pointerdown check
   in JS (wirePanel()) so it works regardless of stacking context — this
   scrim is purely visual/legacy and must never intercept pointer events
   (that would block the always-visible nav bar sitting "under" it). */
.sxe-panel-scrim{position:fixed;inset:0;z-index:499;background:transparent;pointer-events:none}
.sxe-panel[hidden]{display:none}
.sxe-panel-sheet{position:fixed;top:0;bottom:0;right:0;width:min(440px,94vw);z-index:500;
  background:var(--white);box-shadow:-18px 0 48px rgba(0,0,0,0.16);
  padding:28px 26px 26px;overflow-y:auto;transform:translateX(100%);
  transition:transform .42s var(--spring);z-index:9999}
.sxe-panel.is-on .sxe-panel-sheet{transform:translateX(0)}
[dir="rtl"] .sxe-panel-sheet{right:auto;left:0;box-shadow:18px 0 48px rgba(0,0,0,0.16);transform:translateX(-100%)}
[dir="rtl"] .sxe-panel.is-on .sxe-panel-sheet{transform:translateX(0)}
.sxe-panel-close{position:absolute;right:20px;top:20px;width:36px;height:36px;border-radius:50%;
  background:var(--glass-w);border:0;display:flex;align-items:center;justify-content:center;cursor:pointer;color:var(--graphite)}
.sxe-panel-close:hover{background:var(--titanium)}
.sxe-panel-close svg{width:16px;height:16px}

.sxe-pd-fam{font-size:10.5px;font-weight:600;letter-spacing:.1em;text-transform:uppercase;color:var(--ink-3);margin-bottom:10px}
.sxe-pd-ref{display:inline-flex;font-size:11px;font-weight:800;color:var(--red);letter-spacing:.05em;margin-bottom:6px}
.sxe-pd-title{font-size:22px;font-weight:700;letter-spacing:-0.02em;line-height:1.15;margin-bottom:10px;padding-right:34px}
.sxe-pd-role,.sxe-pd-summary{font-size:13.5px;line-height:1.65;color:var(--ink-2);margin-bottom:14px}
.sxe-pd-block{margin-top:18px;padding-top:16px;border-top:1px solid var(--hair)}
.sxe-pd-block h4{font-size:11px;font-weight:700;letter-spacing:.08em;text-transform:uppercase;color:var(--ink-3);margin-bottom:10px}
.sxe-pd-list{list-style:none;display:flex;flex-direction:column;gap:7px}
.sxe-pd-list li{position:relative;padding-left:16px;font-size:13px;line-height:1.5;color:var(--graphite)}
.sxe-pd-list li::before{content:"";position:absolute;left:0;top:7px;width:5px;height:5px;border-radius:50%;background:var(--red)}
[dir="rtl"] .sxe-pd-list li{padding-left:0;padding-right:16px}
[dir="rtl"] .sxe-pd-list li::before{left:auto;right:0}
.sxe-chip-row{display:flex;flex-wrap:wrap;gap:8px}
.sxe-refs{width:100%;border-collapse:collapse;font-size:12px}
.sxe-refs tr{border-top:1px solid var(--hair)}
.sxe-refs td{padding:8px 0;color:var(--ink-2);vertical-align:top}
.sxe-refs td.pn{text-align:right;font-weight:700;color:var(--graphite);white-space:nowrap;padding-left:10px}
[dir="rtl"] .sxe-refs td.pn{text-align:left;padding-right:10px;padding-left:0}
.sxe-related{margin-top:18px;padding-top:16px;border-top:1px solid var(--hair)}
.sxe-related small{display:block;font-size:10.5px;letter-spacing:.06em;text-transform:uppercase;color:var(--ink-3);margin-bottom:10px}
.sxe-chip{background:var(--glass-w);border:1px solid var(--hair);border-radius:var(--r-pill);padding:7px 12px;
  font-size:12px;font-weight:600;color:var(--graphite);cursor:pointer}
.sxe-chip:hover{background:var(--titanium)}
/* Add to Cart / Download Datasheet — no price shown anywhere here */
.sxe-pd-actions{display:flex;gap:10px;margin-top:22px}
.sxe-pd-actions .btn{flex:1;width:auto}
.sxe-pd-cta{display:flex;flex-direction:column;gap:10px;margin-top:12px}
.sxe-pd-cta .btn{width:100%}

@media (max-width:860px){
  /* Height is tied to WIDTH here, not just to vh. The renders are panoramic
     (~1.95:1) and the map always fills its frame edge to edge, so a tall
     narrow frame on a phone crops the sides away: at 62vh on a 390px phone
     only the middle ~36% of the landscape was visible, which put two of the
     three solution hotspots outside the frame with nothing on screen to
     suggest panning. Keeping the frame nearer the render's own proportion
     shows the whole district instead. */
  .sxe-frame{height:clamp(190px, min(50vh, 68vw), 640px);border-radius:var(--r-lg)}
  /* Slightly smaller markers on a phone: the dots keep a constant on-screen
     size while the map is far smaller, so full-size ones crowd each other. */
  .sxe-pin,.sxe-cluster{width:38px;height:38px}
  .sxe-cluster-dot{width:38px;height:38px}
  .sxe-pin-label,.sxe-cluster-label{font-size:10.5px;padding:4px 9px}
  .sxe-panel-sheet{top:auto;left:0;right:0;bottom:0;width:auto;max-height:82vh;border-radius:var(--r-xl) var(--r-xl) 0 0;
    transform:translateY(100%);box-shadow:0 -18px 48px rgba(0,0,0,0.18)}
  .sxe-panel.is-on .sxe-panel-sheet{transform:translateY(0)}
  [dir="rtl"] .sxe-panel-sheet{transform:translateY(100%)}
  .sxe-hint{display:none}
  .sxe-zone-card{display:none}
  .sxe-bundle-txt{display:none}
  .sxe-bundle{padding:0 15px}
}
@media (max-width:520px){
  /* Three tabs, a Back and a Home button have to share ~350px. Without
     nowrap "Data Center" broke onto a second line and pushed the whole bar
     taller, eating the top of the map. */
  .sxe-nav-tab{padding:8px 10px;font-size:11.5px;white-space:nowrap}
  .sxe-nav-tabs{padding:4px}
  .sxe-nav-back,.sxe-nav-home{top:12px;padding-right:12px}
  .sxe-nav-back{left:12px}
  .sxe-nav-home{right:12px;padding-left:12px}
  [dir="rtl"] .sxe-nav-back{left:auto;right:12px;padding:0 6px 0 12px}
  [dir="rtl"] .sxe-nav-home{right:auto;left:12px;padding:0 12px}
  /* Arabic tab names are full words, not acronyms — tighten them so the
     row still clears Back and Home on a phone */
  [dir="rtl"] .sxe-nav-tab{padding:8px 5px;font-size:10px}
  [dir="rtl"] .sxe-nav-back{padding:0 6px}
  [dir="rtl"] .sxe-nav-home{padding:0 10px}
  .sxe-nav-back span:last-child,.sxe-nav-home span:last-child{display:none}
  .sxe-nav-tabs{top:12px}
  .sxe-bundle{height:42px}
}@media (max-width:380px){
  [dir="rtl"] .sxe-nav-tabs{padding:3px}
  [dir="rtl"] .sxe-nav-tab{padding:7px 4px;font-size:9.5px}
  [dir="rtl"] .sxe-nav-back{padding:0 4px}
  [dir="rtl"] .sxe-nav-home{padding:0 8px}
}

/* Short viewport regardless of width — this is what actually distinguishes
   a landscape phone from a portrait one (a portrait phone's own 62vh
   already clears the 420px floor comfortably; a landscape phone's much
   shorter height does not, and the floor was winning by a wide margin).
   Trims this module's own head block so the map still gets most of the
   screen — the site's own top bar/nav above it is untouched. */
@media (max-width:860px) and (max-height:500px){
  .sxe-head{margin-bottom:8px}
  .sxe-headline .lede{display:none}
  .sxe-head>.btn{display:none}
  /* A landscape phone gives ~390px of height and the site's own fixed top
     bar permanently owns ~150px of it. Sizing the map at 68vh made it taller
     than the space that actually exists, so part of it always sat under that
     bar with markers stranded beneath. Size it to the room that is left
     instead — smaller, but all of it usable. */
  .sxe-frame{height:clamp(150px, calc(100vh - 208px), 420px)}
  /* keep the drag/zoom caption here: a landscape frame is short enough that
     some markers really are a drag away, so saying so matters most */
  .sxe-hint{display:block;font-size:10px}
  .sxe-underbar{min-height:38px;margin-top:8px}
  .sxe-bundle{height:38px;font-size:11.5px}
}

@media (prefers-reduced-motion: reduce){
  .sxe-scene{transition:none}
  .sxe-veil{transition:none}
  .sxe-zone-ring,.sxe-zone-ring::after,.sxe-cluster-dot,.sxe-cluster-dot::before,.sxe-pin-dot,.sxe-sun{animation:none}
  .sxe-path,.sxe-path.is-hot,.sxe-path-net{animation:none}
  /* Pure-motion-only elements (nothing to show once paused) are hidden
     outright. The sky/water/ripple are NOT in this group — they are real
     ambient scenery that should still be visible, just held still, rather
     than vanishing (opacity:0 here was a bug: it deleted the sky and
     water entirely for anyone with this setting on, rather than simply
     freezing them). */
  .sxe-pulse-dot,.sxe-twinkle,.sxe-path-flow{animation:none;opacity:0}
  .sxe-sky,.sxe-water{animation:none}
  .sxe-bird{animation:none}
  .sxe-panel-sheet{transition:transform .18s linear}
  .sxe-nav-ind{transition:none}
}

/* ==========================================================================
   CPQ CONFIGURATOR — polish pass
   --------------------------------------------------------------------------
   Purely additive presentation-layer upgrades on top of the existing
   .cpq-* configurator (selection logic, part-number derivation and pricing
   are untouched — this only makes the existing interaction feel more
   alive: a moving shimmer on the progress fill, a red "ping" burst on
   selection, and a gentle invitation-glow on the live part number while a
   configuration is still incomplete).
   ========================================================================== */
.cpq-progress .bar i{
  background-image:linear-gradient(90deg,#E22633,#B5121F),
    linear-gradient(90deg,rgba(255,255,255,0) 0%,rgba(255,255,255,0.65) 50%,rgba(255,255,255,0) 100%);
  background-size:100% 100%, 55% 100%;
  background-repeat:no-repeat, no-repeat;
  background-position:0 0, -60% 0;
  animation:cpqShimmer 1.7s linear infinite;
}
@keyframes cpqShimmer{to{background-position:0 0, 180% 0}}

.cpq-seg button,.cpq-chip{position:relative}
.cpq-seg button.on,.cpq-chip.on{transform:scale(1.045)}
.cpq-seg button.on::after,.cpq-chip.on::after{
  content:"";position:absolute;inset:-3px;border-radius:inherit;pointer-events:none;
  border:2px solid rgba(206,27,40,0.55);opacity:.9;animation:cpqRing .55s ease-out;
}
@keyframes cpqRing{0%{opacity:.85;transform:scale(1)}100%{opacity:0;transform:scale(1.32)}}

.cpq-pn-live:not(.locked){animation:cpqPnGlow 2.6s ease-in-out infinite}
@keyframes cpqPnGlow{
  0%,100%{box-shadow:inset 0 0 0 1px var(--hair),0 0 0 0 rgba(206,27,40,0)}
  50%{box-shadow:inset 0 0 0 1px var(--hair),0 0 0 6px rgba(206,27,40,0.12)}
}

.cpq-validity.no .vdot{animation:cpqDotPulse 1.6s ease-in-out infinite}
@keyframes cpqDotPulse{0%,100%{opacity:1}50%{opacity:.45}}

@media (prefers-reduced-motion: reduce){
  .cpq-progress .bar i,.cpq-seg button.on::after,.cpq-chip.on::after,.cpq-pn-live:not(.locked),.cpq-validity.no .vdot{
    animation:none
  }
}

