/*
AUTHORSHIP NOTE
Purpose: all authored UI effects beyond standard layout — split background, WebGL portal frame, circular loader, pointer annotation, segment nav arrows, audio toggle, Explore overlay, contact form states, focus rings, scrollbars, main-scene pause state, and mobile layout fixes.
Craft signal: a deliberate z-index map (documented inline) prevents compositing conflicts between the 3D background, WebGL canvas, fixed UI layers, and modal stack. Overlay shells own their own scroll columns so scrollbars sit on the popup edge rather than the viewport, while a shared body-level pause state freezes only the home-scene motion and leaves overlay transitions running. Fluid and cursor effects are CSS-only at paint time — JS writes only custom properties, never layout attributes. Every animated region has a reduced-motion fallback. The pointer and nav arrow systems share the same fixed-layer strategy: placed outside all transformed ancestors so they are never subject to perspective, rotateY, or scale inherited from the portal tilt chain.
*/

/*
 * ui-elements.css
 * Non-case UI: split background, portal, audio toggle, explore overlay,
 * contact form, accessibility, scrollbars, mobile fixes.
 * Case overlay styles — including popup-owned themed scrollbars — live in cases.css.
 *
 * Z-index layer map (main page):
 *   1   — .split-background          decorative background environment
 *   5   — (reserved)
 *   7   — #portal-canvas             WebGL canvas (glow + particles + video)
 *   8   — .portal-ring               portal shell
 *   9   — .portal-pointer            screen-space pointer
 *  10   — .mainwrap                  nav + page content
 *  14   — .portal-audio-toggle-wrap  audio button above portal
 * 900   — .explore-overlay           explore panel
 * 10050 — .modal-wrap                contact form (above case overlay)
 * 9999  — body::before               cursor spotlight
 */


/* =============================================================================
   1. GLOBAL BASE RESETS
   Overflows and scroll behaviour applied at root level.
   ============================================================================= */

html {
  overflow-x: hidden;
  overflow-x: clip;
  overscroll-behavior-x: none;
}

body {
  margin: 0;
  padding: 0;
  overflow-x: hidden;
  overflow-x: clip;
  overscroll-behavior-x: none;
  /* Compositor layer on Android Chrome — eliminates scroll jitter */
  -webkit-overflow-scrolling: touch;
}

/* Keep the back-link hover treatment opacity-only; no rotational affordance on this control. */
.header .link-back:hover,
.header .link-back:active {
  transform: none;
}


/* =============================================================================
   2. SPLIT BACKGROUND
   Full-viewport fixed layer: light right half + dark left half divided by a
   diagonal clip-path polygon. The division line is expressed in em units where
   font-size equals the portal diameter, so the diagonal always passes through
   the portal center regardless of viewport size or zoom state. Switches to
   absolute positioning on mobile so content below the portal scrolls naturally.
   ============================================================================= */

/* Body inherits the light noise texture when the split background is present */

/* AUTHORSHIP: the entire studio environment — background halves, glow blobs, WebGL canvas, and
   portal ring — is consolidated into one fixed composited layer at z-index 1. All interactive
   content, navigation, and modal UI sits above it in a separate stacking context, which means
   the background can be GPU-promoted once and never triggers repaints when UI above it changes. */

/* ── Mobile overrides ──────────────────────────────────────────────────────── */


/* Mobile body background continuity when page content grows taller than the split layer */


/* Disable will-change on explore map elements — not needed on mobile */
@media only screen and (max-width: 900px) {
  #bubble-interactive,
  .explore-map-base,
  .explore-map-reveal,
  .explore-map-base::before,
  .explore-map-base::after,
  .explore-map-reveal::before,
  .explore-map-reveal::after {
    will-change: auto;
  }
}


/* =============================================================================
   3. PORTAL
   Circular WebGL viewport centered over the split background.
   Layer order: canvas (WebGL glow/particles/video shader) → ring shell →
   pointer annotation → segment nav arrows → loader overlay (shown during init).
   The ring's 2px padding gap acts as the gradient border painted by the WebGL
   ring shader — no CSS border-image or pseudo-element is needed, and the border
   updates continuously with the animated gradient in the same GPU draw call.
   ============================================================================= */

/*
 * Portal ring — a circular div whose 2px padding gap acts as the gradient border.
 * The actual border gradient is painted by JS onto the canvas behind it.
 */

/* Inner circle — clips the WebGL canvas and video texture to a circle */

/* Video is a Three.js texture source only — never directly visible */

/*
 * WebGL canvas — covers the full split-background layer.
 * Renders: atmospheric glow, particle field, portal video shader.
 * z-index 7: below the portal shell (z:8) and pointer (z:9), below mainwrap (z:10).
 */

/* Shown while Three.js + video are initialising; fades out on first render */
/* AUTHORSHIP: the loader arc is driven by a weighted composite of two independent progress signals —
   Three.js module download bytes (60% weight, tracked via streaming fetch) and video texture readiness
   (40% weight, tracked via canplay). The percentage therefore reflects actual asset readiness rather than
   a fake timer, and the loader never reaches 100% before the portal has rendered its first real frame. */


/* SVG progress ring — rotated so 0% starts at 12 o'clock */


/* Percentage counter styles are kept in the critical inline CSS of index.html. */

/* ── Mobile portal overrides ─────────────────────────────────────────────── */


/* =============================================================================
   4. CURSOR SPOTLIGHT
   Full-page radial glow tracking the mouse via --mx / --my custom properties
   written by cursor-spotlight.js. Lives on body::before so it composites above
   everything without adding a DOM node or triggering layout. The gradient uses
   a sentinel value (−999px) as default so it renders off-screen when no cursor
   position has been reported — safe on first paint and after cursor leaves.
   Suppressed entirely on touch devices via media query so the selector and
   gradient exist in CSS but the RAF loop never starts in JS.
   ============================================================================= */

body::before {
  content: '';
  position: fixed;
  inset: 0;
  pointer-events: none;
  z-index: 9999;
  background: radial-gradient(
    circle 280px at var(--mx, -999px) var(--my, -999px),
    rgba(120, 160, 255, 0.10) 0%,
    transparent 100%
  );
}

@media only screen and (max-width: 900px) {
  body::before {
    display: none;
  }
}


/* =============================================================================
   5. HERO INTRO TEXT
   Left-column copy block on the main page: tagline, description, proof line.
   ============================================================================= */

.intro-copy {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
}

/* Social proof line beneath the CTA — client logos / stats */
.intro-proof {
  margin-top: 18px;
  font-size: 12px;
  line-height: 1.55;
  color: rgba(255, 255, 255, 0.72);
  max-width: 560px;
}

.intro-proof strong {
  color: rgba(255, 255, 255, 0.92);
  font-weight: 600;
}

/* Subtitle shown below the CTA button on mobile only */
.intro-subtitle {
  display: none;
}

@media only screen and (max-width: 900px) {
  .intro-proof {
    max-width: 100%;
    font-size: 11px;
  }

  .intro-subtitle {
    display: block;
    margin-top: 0.85rem;
    font-size: clamp(12px, 3.2vw, 14px);
    line-height: 1.5;
    text-align: center;
    opacity: 0.65;
    color: inherit;
  }
}


/* =============================================================================
   6. CLUTCH BADGE
   Third-party trust badge with star rating shown in the hero area.
   ============================================================================= */

.clutch-badge__meta {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 1px;
}

.clutch-badge__rating {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 11px;
  line-height: 1.2;
  color: rgba(23, 49, 59, 0.78);
  white-space: nowrap;
}

.clutch-badge__stars {
  letter-spacing: 0.08em;
  color: #f2b94b;
  font-size: 12px;
}


/* =============================================================================
   7. AUDIO TOGGLE
   Floating button on the portal that enables background music.
   ============================================================================= */

/* Wrapper — absolutely positioned relative to split-background on desktop */
.portal-audio-toggle-wrap {
  position: absolute;
  left: calc(50% - (var(--mainpage-circle) * 0.54));
  top: calc(50% - (var(--mainpage-circle) * 0.18));
  transform: translate(-50%, -50%);
  z-index: 14;
  pointer-events: auto;
}

/* Button */
.audio-toggle--link {
  --audio-accent: #8fd7ff;
  display: inline-flex;
  align-items: center;
  gap: 0.72rem;
  text-decoration: none;
  color: var(--white);
  font-size: 12px;
  line-height: 1;
  font-weight: 600;
  letter-spacing: -0.01em;
  padding: 0.16rem 0;
  transition: color 0.28s ease, opacity 0.28s ease, transform 0.28s ease;
}

.audio-toggle--link:hover {
  color: rgba(255, 255, 255, 0.98);
  transform: translateY(-1px);
}

.audio-toggle--link:focus-visible {
  outline: 2px solid rgba(255, 255, 255, 0.92);
  outline-offset: 4px;
  border-radius: 999px;
}

/* Circular icon container with frosted-glass border */
.audio-toggle__icon-wrap {
  width: 32px;
  height: 32px;
  border-radius: 999px;
  display: inline-grid;
  place-items: center;
  border: 1px solid var(--grey600);
  backdrop-filter: blur(10px) saturate(120%);
  -webkit-backdrop-filter: blur(10px) saturate(120%);
  flex: 0 0 auto;
}

.audio-toggle__icon {
  position: relative;
  display: inline-grid;
  place-items: center;
  width: 16px;
  height: 16px;
}

/* Music note icon via background-image */
.audio-toggle__note {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translateX(-50%) translateY(-50%);
  width: 32px;
  height: 32px;
  z-index: 2;
  color: transparent;
  background: url(../img/icons/music.svg) no-repeat 50% 50% / 16px auto;
}

/* Expanding ring — pulses when audio is off to invite interaction */
.audio-toggle__pulse {
  position: absolute;
  inset: -0.18rem;
  border-radius: 999px;
  border: 1px solid rgba(143, 215, 255, 0.22);
  opacity: 0;
  transform: scale(0.74);
}

.audio-toggle:not(.is-enabled) .audio-toggle__pulse {
  animation: audioPulseRing 2.6s ease-out infinite;
}

.audio-toggle:not(.is-enabled) .audio-toggle__pulse--2 {
  animation-delay: 1.3s;
}

/* Label with subtle animated underline */
.audio-toggle__label {
  position: relative;
  white-space: nowrap;
}

.audio-toggle__label::after {
  content: '';
  position: absolute;
  left: 0;
  right: 0;
  bottom: -0.22rem;
  height: 1px;
  background: currentColor;
  opacity: 0.22;
  transform-origin: left center;
  transition: opacity 0.28s ease, transform 0.28s ease;
}

.audio-toggle--link:hover .audio-toggle__label::after,
.audio-toggle.is-enabled .audio-toggle__label::after {
  opacity: 0.48;
  transform: scaleX(1);
}

/* Enabled state — rings become static halos */
.audio-toggle.is-enabled {
  --audio-accent: #c4f0ff;
  color: rgba(255, 255, 255, 0.98);
}

.audio-toggle.is-enabled .audio-toggle__pulse {
  opacity: 1;
  animation: none;
}

.audio-toggle.is-enabled .audio-toggle__pulse--1 {
  transform: scale(1.03);
  border-color: rgba(143, 215, 255, 0.14);
}

.audio-toggle.is-enabled .audio-toggle__pulse--2 {
  transform: scale(1.32);
  border-color: rgba(143, 215, 255, 0.08);
}

@keyframes audioPulseRing {
  0%   { opacity: 0;    transform: scale(0.72); }
  20%  { opacity: 0.66; }
  100% { opacity: 0;    transform: scale(1.34); }
}

/* ── Mobile audio toggle ─────────────────────────────────────────────────── */

@media only screen and (max-width: 900px) {
  .portal-audio-toggle-wrap {
    position: absolute;
    top: calc(var(--cp-mobile-portal-top) + 90px);
    left: auto;
    right: var(--side);
    margin-top: -20px;
    z-index: 5;
    transform: none;
  }

  .audio-toggle--link {
    color: rgba(255, 255, 255, 0.92);
  }

  .audio-toggle__label {
    font-size: 0.8rem;
  }
}

@media only screen and (min-width: 901px) {
  .portal-audio-toggle-wrap {
    top: calc(50% - (var(--mainpage-circle) * 0.5));
    left: calc(50% - (var(--mainpage-circle) * 0.5));
    transform: none;
  }
}


/* =============================================================================
   8. PAGE ROOTS & TRANSITIONS
   Per-page body overrides and the full-screen transition veil.
   ============================================================================= */

#page-main,
#page-explore {
  position: relative;
  min-height: 100vh;
  min-height: 100dvh;
}

html[data-cp-page="main"] body {
  background-color: #0f1020;
}

html[data-cp-page="explore"] body {
  background-color: #f3f4f8;
}

html[data-cp-page="explore"] #page-main .split-background,
html[data-cp-page="main"] #page-main .split-background {
  display: block !important;
}

/* Desktop body flow: main page remains scrollable while the background stays decorative */
@media only screen and (min-width: 901px) {
  html[data-cp-page="main"] body {
    overflow: auto;
    height: auto;
    min-height: 100%;
  }

  html[data-cp-page="main"] #page-main {
    height: auto;
    min-height: auto;
  }
}

/* Page transition: disable pointer events while the veil animates */
html[data-page-transition="1"] {
  pointer-events: none;
}

/* Full-screen colour fade between pages */
#page-transition-veil {
  position: fixed;
  inset: 0;
  z-index: 2147483000;
  opacity: 0;
  pointer-events: none;
  background:
    radial-gradient(1200px 700px at 50% 50%, rgba(255, 255, 255, 0.045), rgba(255, 255, 255, 0) 65%),
    linear-gradient(180deg, rgba(255, 255, 255, 0.02), rgba(255, 255, 255, 0.01)),
    var(--page-transition-veil-color, #10111a);
  transition: opacity 560ms cubic-bezier(0.22, 1, 0.36, 1),
              background-color 0s linear 560ms;
  will-change: opacity;
}

#page-transition-veil.is-active {
  opacity: 1;
}

/* AUTHORSHIP: prefers-reduced-motion is applied as a single global override rather than per-component
   so no individual animation can slip through. Duration is collapsed to 0.01ms rather than 0 to preserve
   animation events that downstream JS may listen to for sequencing — zero duration suppresses the events
   entirely on some engines, which can break completion-dependent logic. */
@media (prefers-reduced-motion: reduce) {
  #page-transition-veil {
    transition: none;
  }
}

/* Explore page: content layers sit above the fixed map background */
html[data-cp-page="explore"] body > *:not(.explore-map-base):not(.explore-map-reveal):not(.skip-link) {
  position: relative;
  z-index: 1;
}

/* Content wrapper — fills remaining vertical space between header and footer */
.content {
  flex: 1 0 auto;
  position: relative;
  width: 100%;
}


/* =============================================================================
   9. EXPLORE MAP BACKGROUND
   Treasure-map illustration revealed by a cursor-following radial mask.
   Two layers: .explore-map-base (always faintly visible) +
               .explore-map-reveal (bright, shown under cursor).
   ============================================================================= */

.explore-map-base,
.explore-map-reveal {
  position: fixed;
  inset: 0;
  pointer-events: none;
  z-index: 0;
  background-image:
    radial-gradient(circle at 18% 22%, rgba(222, 211, 170, 0.18), transparent 24%),
    radial-gradient(circle at 74% 34%, rgba(145, 127, 93, 0.10), transparent 20%),
    radial-gradient(circle at 52% 68%, rgba(168, 146, 108, 0.10), transparent 22%),
    linear-gradient(180deg, rgba(232, 226, 214, 0.28), rgba(215, 207, 191, 0.18)),
    linear-gradient(135deg, rgba(250, 248, 242, 0.80), rgba(236, 230, 217, 0.72)),
    image-set(
      url("../img/explore/treasure-map-reference-1280.webp") 1x,
      url("../img/explore/treasure-map-reference-1920.webp") 2x
    );
  background-repeat: no-repeat;
  background-position: center, center, center, center, center, center 46%;
  background-size: cover, cover, cover, cover, cover, min(92vw, 1680px) auto;
}

/* Always-visible faint base layer */
.explore-map-base {
  opacity: 0.12;
  mix-blend-mode: multiply;
  filter: contrast(1.02);
}

/* Bright layer revealed by a radial mask that follows --map-x / --map-y */
.explore-map-reveal {
  opacity: 0.72;
  mix-blend-mode: multiply;
  filter: contrast(1.08);
  mask-image: radial-gradient(
    circle 340px at var(--map-x) var(--map-y),
    rgba(0,0,0,0.98) 0%, rgba(0,0,0,0.86) 20%,
    rgba(0,0,0,0.72) 32%, rgba(0,0,0,0.28) 48%,
    rgba(0,0,0,0.10) 60%, transparent 76%
  );
  -webkit-mask-image: radial-gradient(
    circle 340px at var(--map-x) var(--map-y),
    rgba(0,0,0,0.98) 0%, rgba(0,0,0,0.86) 20%,
    rgba(0,0,0,0.72) 32%, rgba(0,0,0,0.28) 48%,
    rgba(0,0,0,0.10) 60%, transparent 76%
  );
}

@media only screen and (max-width: 900px) {
  .explore-map-base,
  .explore-map-reveal {
    background-position: center, center, center, center, center, center 38%;
    background-size: cover, cover, cover, cover, cover, min(148vw, 1080px) auto;
  }

  .explore-map-base {
    position: absolute;
    inset: 0;
    min-height: 100%;
    mix-blend-mode: normal;
    filter: contrast(1.01);
    opacity: 0.10;
  }

  .explore-map-reveal {
    position: fixed;
    mix-blend-mode: normal;
    opacity: 0.58;
    filter: contrast(1.05);
    mask-image: radial-gradient(
      circle 200px at var(--map-x) var(--map-y),
      rgba(0,0,0,0.92) 0%, rgba(0,0,0,0.70) 28%,
      rgba(0,0,0,0.36) 52%, rgba(0,0,0,0.10) 70%, transparent 88%
    );
    -webkit-mask-image: radial-gradient(
      circle 200px at var(--map-x) var(--map-y),
      rgba(0,0,0,0.92) 0%, rgba(0,0,0,0.70) 28%,
      rgba(0,0,0,0.36) 52%, rgba(0,0,0,0.10) 70%, transparent 88%
    );
  }
}


/* =============================================================================
   10. EXPLORE FLUID BACKGROUND  (.explore-fluid)
   Three organic blob shapes merged via an SVG goo filter, animated with
   slow orbital motion. Used as the background of the explore overlay shell.
   ============================================================================= */

/* Static gradient behind the blobs */
#explore-fluid-bg {
  position: fixed;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  overflow: hidden;
  background: linear-gradient(135deg, #f2f2f5 0%, #e9edf3 40%, #eef1f7 70%, #f3f3f6 100%);
  isolation: isolate;
}

#explore-fluid-bg::after {
  content: "";
  position: absolute;
  inset: 0;
  background: radial-gradient(circle at 22% 18%, rgba(255, 255, 255, 0.45), transparent 60%);
}

/* Container — clips blobs at shell's rounded corners */
/* AUTHORSHIP: the fluid background blobs use an SVG feTurbulence + feDisplacementMap filter chain applied
   via CSS filter on a plain div, producing organic motion without canvas, WebGL, or a JS animation loop.
   The displacement field animates via CSS keyframes on the turbulence baseFrequency, which the browser
   computes on the GPU compositor thread — zero main-thread cost at runtime and zero additional JS weight. */
.explore-fluid {
  position: absolute;
  inset: 0;
  pointer-events: none;
  overflow: hidden;
  border-radius: inherit;
  isolation: isolate;
  z-index: 0;
}

.explore-fluid::after {
  content: "";
  position: absolute;
  inset: 0;
  background: radial-gradient(circle at 22% 18%, rgba(255, 255, 255, 0.45), transparent 60%);
}

/* Blob layer — SVG goo filter merges children into liquid shapes */
.explore-fluid__blobs {
  position: absolute;
  inset: 0;
  filter: url(#explore-fluid-merge) blur(55px);
}

/* Blob 1 — large cyan mass, drifts vertically (the "current") */
.explore-fluid__blob--current {
  position: absolute;
  border-radius: 50%;
  width: 80%;
  height: 80%;
  top: 10%;
  left: 10%;
  will-change: transform;
  transform: translateZ(0);
  background: radial-gradient(circle,
    rgba(85, 240, 255, 0.28) 0%,
    rgba(85, 240, 255, 0.14) 28%,
    rgba(85, 240, 255, 0.06) 55%,
    transparent 78%
  );
  animation: fluidDriftY 30s ease-in-out infinite;
}

/* Blob 2 wrapper — orbits slowly around an off-centre pivot */
.explore-fluid__blob-orbit-a-wrap {
  position: absolute;
  inset: 0;
  transform-origin: calc(50% - 400px);
  animation: fluidOrbit 20s linear infinite;
}

.explore-fluid__blob-orbit-a {
  width: 80%;
  height: 80%;
  border-radius: 50%;
  background: radial-gradient(circle,
    rgba(60, 80, 255, 0.22) 0%,
    rgba(0, 0, 255, 0.18) 28%,
    rgba(0, 0, 255, 0.06) 55%,
    transparent 78%
  );
  mix-blend-mode: soft-light;
}

/* Blob 3 wrapper — counter-orbits at half speed */
.explore-fluid__blob-orbit-b-wrap {
  position: absolute;
  inset: 0;
  transform-origin: calc(50% + 400px);
  animation: fluidOrbit 40s linear infinite;
}

.explore-fluid__blob-orbit-b {
  position: absolute;
  width: 80%;
  height: 80%;
  border-radius: 50%;
  background: radial-gradient(circle,
    rgba(255, 255, 255, 1) 0%,
    rgba(255, 255, 255, 0.85) 18%,
    rgba(255, 255, 255, 0.45) 40%,
    rgba(255, 255, 255, 0.14) 62%,
    transparent 82%
  );
  top: calc(50% + 200px);
  left: calc(50% - 500px);
}

/* Interactive blob that tracks the cursor inside the explore shell */
.explore-fluid__cursor-blob {
  position: absolute;
  width: 100%;
  height: 100%;
  border-radius: 50%;
  background: radial-gradient(circle,
    rgba(255, 255, 255, 0.8) 0%,
    rgba(200, 220, 255, 0.4) 18%,
    rgba(120, 150, 255, 0.15) 40%,
    transparent 70%
  );
  opacity: 0.9;
  transform: translate(0, 0) translateZ(0);
  will-change: transform;
}

/*
 * Cursor glow — oversized (200%×200%) so its edges never clip visibly
 * when the cursor is near the container boundary.
 */
.explore-fluid__glow {
  position: absolute;
  width: 200%;
  height: 200%;
  top: -50%;
  left: -50%;
  will-change: transform;
  background: radial-gradient(circle at center,
    rgba(255, 255, 255, 0.92) 0%,
    rgba(210, 228, 255, 0.55) 12%,
    rgba(140, 170, 255, 0.22) 28%,
    transparent 50%
  );
  opacity: 0.95;
}

@keyframes fluidDriftY {
  0%, 100% { transform: translateY(-50px); }
  50%       { transform: translateY(50px);  }
}

@keyframes fluidOrbit {
  from { transform: rotate(0);      }
  to   { transform: rotate(360deg); }
}

/* AUTHORSHIP: CSS-side fluid motion is explicitly paused until Explore's visual transition is done;
   this keeps the hyperspace reveal and the blob field off the same hot frames on lower-end GPUs. */
.explore-overlay:not(.is-runtime-active) .explore-fluid__blob--current,
.explore-overlay:not(.is-runtime-active) .explore-fluid__blob-orbit-a-wrap,
.explore-overlay:not(.is-runtime-active) .explore-fluid__blob-orbit-b-wrap {
  animation-play-state: paused;
}

.explore-fluid__glow {
  transition: opacity 0.36s ease;
}

.explore-overlay:not(.is-runtime-active) .explore-fluid__glow {
  opacity: 0;
}

@media only screen and (max-width: 900px) {
  #explore-fluid-bg {
    position: absolute;
    min-height: 100%;
  }

  /* Disable animations on mobile — GPU cost not worth it */
  .explore-fluid__blob--current,
  .explore-fluid__blob-orbit-a-wrap,
  .explore-fluid__blob-orbit-b-wrap {
    animation: none;
  }

  .explore-fluid__cursor-blob {
    display: none;
  }
}


/* =============================================================================
   11. EXPLORE OVERLAY  (.explore-overlay)
   Full-screen panel that opens when the user clicks "Explore".
   ============================================================================= */

.explore-overlay {
  position: fixed;
  inset: 0;
  z-index: 900;
  display: none;
  background: rgba(2, 5, 12, 0.84);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
  opacity: 0;
  transition: opacity 0.55s cubic-bezier(0.4, 0, 0.2, 1);
}

.explore-overlay.is-open   { display: block; opacity: 1; }
.explore-overlay.is-closing { display: block; opacity: 0; pointer-events: none; }

/* AUTHORSHIP: Explore reveal is staged in three layers — overlay fade, shell handoff, then content/runtime —
   so the hyperspace canvas never competes with freshly-mounted copy or the fluid background on the same frames. */
.explore-overlay__shell {
  opacity: 0;
  transform: translate3d(0, 18px, 0) scale(0.985);
  transition:
    opacity 0.42s cubic-bezier(0.22, 0.61, 0.36, 1),
    transform 0.62s cubic-bezier(0.22, 0.61, 0.36, 1);
}

.explore-overlay.is-open .explore-overlay__shell {
  opacity: 1;
  transform: translate3d(0, 0, 0) scale(1);
}

/* Hyperspace layer — canvas sits above everything */
.explore-flash {
  position: fixed;
  inset: 0;
  z-index: 1100;
  pointer-events: none;
  opacity: 0;
  transition: opacity 0.08s ease;
}

.explore-flash canvas {
  width: 100%;
  height: 100%;
  display: block;
}

/* Clickable backdrop — closes the overlay when clicked outside the shell */
.explore-overlay__backdrop {
  position: absolute;
  inset: 0;
  cursor: default;
}

/* Viewport inset frame. The content column itself owns vertical scrolling,
   so the scrollbar sits on the popup edge instead of the browser edge while
   the paused main scene remains visually separate behind the shell. */
.explore-overlay__panel {
  position: absolute;
  inset: 20px;
  display: flex;
  align-items: flex-start;
  justify-content: center;
  overflow: hidden;
  padding: 18px;
  box-sizing: border-box;
  cursor: default;
}

/* Centred content column capped at 1360px; this is the Explore scroll root so the rail stays visually attached to the shell.
   AUTHORSHIP: Explore uses the same scrollbar construction standard as case popups — tokenised track/thumb/hover, padded thumb, and soft chrome — but in a light silver-blue palette that matches the shell gradient. */
.explore-overlay__content {
  --explore-scroll-hint-edge: rgba(225, 232, 242, 0.34);
  --explore-scroll-cue-bg: rgba(255, 255, 255, 0.56);
  --explore-scroll-cue-border: rgba(120, 137, 165, 0.16);
  --explore-scroll-cue-fg: rgba(71, 84, 110, 0.82);
  --explore-scrollbar-track: rgba(182, 194, 214, 0.34);
  --explore-scrollbar-thumb: #bcc8dc;
  --explore-scrollbar-thumb-hover: #d8e0ec;
  position: relative;
  width: min(1360px, 100%);
  max-height: calc(100vh - 76px);
  max-height: calc(100dvh - 76px);
  margin: 0 auto;
  overflow-x: hidden;
  overflow-y: auto;
  border-radius: 34px;
  scrollbar-gutter: stable;
  box-sizing: border-box;
}

html.explore-overlay-open,
body.explore-overlay-open {
  overflow: hidden;
}

/* Rounded card that clips the fluid background */
.explore-overlay__shell {
  overflow: hidden;
  border-radius: 34px;
  background: linear-gradient(135deg, #f2f2f5 0%, #e9edf3 40%, #eef1f7 70%, #f3f3f6 100%);
  box-shadow: 0 30px 90px rgba(0, 0, 0, 0.45);
}

/* Close button — top-right corner of the shell */
.explore-overlay__close {
  position: absolute;
  top: 14px;
  right: 14px;
  z-index: 10;
  width: 34px;
  height: 34px;
  border: 0;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.96);
  color: transparent;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  box-shadow: 0 10px 28px rgba(15, 23, 42, 0.10);
  transition: transform 0.16s ease, background-color 0.16s ease, opacity 0.16s ease;
  opacity: 0.88;
}

.explore-overlay__close::before,
.explore-overlay__close::after {
  content: '';
  position: absolute;
  width: 11px;
  height: 1.5px;
  border-radius: 999px;
  background: #111827;
}

.explore-overlay__close::before { transform: rotate(45deg);  }
.explore-overlay__close::after  { transform: rotate(-45deg); }

.explore-overlay__close:hover {
  transform: scale(1.04);
  background: rgba(255, 255, 255, 1);
  opacity: 1;
}

/* Scroll wrapper sits above the fluid background (z-index:0)
   and stays visually hidden until the hyperspace handoff has completed. */
.explore-overlay__scroll {
  position: relative;
  z-index: 1;
  opacity: 0;
  transform: translate3d(0, 14px, 0);
  transition:
    opacity 0.30s ease,
    transform 0.46s cubic-bezier(0.22, 0.61, 0.36, 1);
}

.explore-overlay.is-live .explore-overlay__scroll {
  opacity: 1;
  transform: translate3d(0, 0, 0);
}

/* AUTHORSHIP: Explore now uses the same DOM/scroll-affordance pattern as the case popups — content column owns scroll, the cue sits as a sticky sibling after the shell, visibility can be driven either by overflow state or by the same explicit .is-visible class, and the cue itself is a real button that advances Explore by one viewport. */
.explore-overlay__scroll-affordance {
  position: sticky;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 18;
  height: 92px;
  margin-top: -92px;
  display: flex;
  align-items: flex-end;
  justify-content: center;
  pointer-events: none;
  visibility: hidden;
  opacity: 0;
  transform: translateY(6px);
  transition: opacity .22s ease, transform .22s ease, visibility 0s linear .22s;
}

.explore-overlay.is-live .explore-overlay__content.has-overflow:not(.is-at-end) .explore-overlay__scroll-affordance,
.explore-overlay.is-live .explore-overlay__scroll-affordance.is-visible {
  visibility: visible;
  opacity: 1;
  transform: translateY(0);
  transition-delay: 0s;
}

.explore-overlay__scroll-edge {
  position: absolute;
  left: 16px;
  right: 16px;
  bottom: 0;
  height: 82px;
  border-radius: 0 0 34px 34px;
  background: linear-gradient(180deg, rgba(255, 255, 255, 0) 0%, var(--explore-scroll-hint-edge) 56%, color-mix(in srgb, var(--explore-scroll-hint-edge) 135%, transparent) 100%);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  mask-image: linear-gradient(180deg, transparent 0%, rgba(0, 0, 0, 0.75) 44%, rgba(0, 0, 0, 1) 100%);
  -webkit-mask-image: linear-gradient(180deg, transparent 0%, rgba(0, 0, 0, 0.75) 44%, rgba(0, 0, 0, 1) 100%);
}

.explore-overlay__scroll-cue {
  position: relative;
  z-index: 1;
  margin-bottom: 12px;
  display: inline-flex;
  align-items: center;
  gap: 9px;
  min-height: 34px;
  padding: 0 14px;
  border: 1px solid var(--explore-scroll-cue-border);
  border-radius: 999px;
  background: var(--explore-scroll-cue-bg);
  color: var(--explore-scroll-cue-fg);
  box-shadow: 0 8px 24px rgba(20, 28, 44, 0.10), inset 0 1px 0 rgba(255, 255, 255, 0.24);
  font-size: 10px;
  font-weight: 600;
  letter-spacing: .12em;
  line-height: 1;
  text-transform: uppercase;
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
  pointer-events: auto;
  cursor: pointer;
  appearance: none;
  -webkit-appearance: none;
  border-width: 1px;
  border-style: solid;
  text-decoration: none;
  transition: transform .16s ease, background-color .16s ease, border-color .16s ease, box-shadow .16s ease;
}

.explore-overlay__scroll-cue-icon {
  width: 8px;
  height: 8px;
  display: inline-block;
  border-right: 1.5px solid currentColor;
  border-bottom: 1.5px solid currentColor;
  transform: rotate(45deg) translateY(-1px);
  opacity: .82;
}


.explore-overlay__scroll-cue:hover {
  transform: translateY(-1px);
  background: color-mix(in srgb, var(--explore-scroll-cue-bg) 82%, rgba(255, 255, 255, 0.22));
  border-color: color-mix(in srgb, var(--explore-scroll-cue-border) 72%, rgba(255, 255, 255, 0.22));
}

.explore-overlay__scroll-cue:active {
  transform: translateY(0);
}

/* Extra top padding when explore content is rendered inside the overlay */
#explore-overlay .explore {
  padding-top: clamp(48px, 5rem, 80px);
}

/* ── Mobile explore overlay ──────────────────────────────────────────────── */

@media (max-width: 900px) {
  .explore-overlay__panel {
    inset: 0;
    overflow: hidden;
    padding: 0;
  }

  .explore-overlay__content {
    width: 100%;
    max-width: 100%;
    max-height: 100vh;
    max-height: 100dvh;
    margin: 0;
    border-radius: 0;
  }

  .explore-overlay__scroll-affordance {
    height: 82px;
    margin-top: -82px;
  }

  .explore-overlay__scroll-edge {
    left: 0;
    right: 0;
    border-radius: 0;
  }

  .explore-overlay__scroll-cue {
    margin-bottom: 10px;
  }

  .explore-overlay__shell {
    border-radius: 0;
  }

  .explore-overlay__close {
    position: fixed;
    top: calc(12px + env(safe-area-inset-top));
    right: calc(12px + env(safe-area-inset-right));
    width: 44px;
    height: 44px;
    z-index: 1000;
  }
}


/* =============================================================================
   12. CONTACT FORM
   Envelope modal — mobile layout overrides and file upload widget.
   ============================================================================= */

@media only screen and (max-width: 900px) {
  .contact-us .title {
    font-size: clamp(17px, 5vw, 22px);
    line-height: 1.3;
    margin-bottom: 1.2rem;
    text-wrap: balance;
  }

  .contact-us .form-grid {
    display: grid;
    grid-template-columns: 1fr;
    gap: 10px;
    width: 100%;
  }

  .form-el,
  .form-el.wid100 {
    width: 100%;
    min-width: 0;
  }

  .contact-us .form-grid input,
  .contact-us .form-grid textarea {
    width: 100%;
    box-sizing: border-box;
    min-height: 48px;
    font-size: 16px; /* Prevents iOS zoom-on-focus */
  }

  .contact-us .form-grid textarea {
    min-height: 110px;
  }

  .contact-us .form-grid select {
    font-size: 1em;
  }

  .form-submit {
    margin-top: 14px;
  }

  .form-submit .btn {
    width: 100%;
    min-height: 52px;
    display: flex;
    align-items: center;
    justify-content: center;
    box-sizing: border-box;
  }

  .contact-us .direct-contacts {
    gap: 10px;
  }

  .contact-us .direct-contacts li {
    width: 100%;
  }

  .contact-us .direct-contacts a {
    width: 100%;
    min-height: 48px;
    display: flex;
    align-items: center;
    gap: 10px;
    box-sizing: border-box;
    padding: 10px 12px;
  }
}

/* File upload drop area */
.file-upload-label {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 12px 14px;
  border: 1.5px dashed rgba(0, 0, 90, 0.22);
  border-radius: 10px;
  cursor: pointer;
  transition: border-color 0.2s ease, background 0.2s ease;
  background: rgba(0, 0, 90, 0.03);
  font-size: 14px;
  color: rgba(0, 0, 90, 0.55);
  width: 100%;
  box-sizing: border-box;
  line-height: 1.4;
}

.file-upload-label:hover {
  border-color: rgba(0, 0, 255, 0.4);
  background: rgba(0, 0, 255, 0.04);
}

.file-upload-icon {
  font-size: 18px;
  flex-shrink: 0;
  line-height: 1;
}

.file-upload-label input[type="file"] {
  position: absolute;
  width: 0;
  height: 0;
  opacity: 0;
  pointer-events: none;
}

/* Truncated filename shown after a file is selected */
.file-upload-filename {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Form field validation states */
.wrap-form input.valid,
.wrap-form textarea.valid   { border-color: #28c76f; }
.wrap-form input.invalid,
.wrap-form textarea.invalid { border-color: #ff4d4d; }
.wrap-form input,
.wrap-form textarea          { transition: border-color 0.25s ease; }


/* =============================================================================
   13. EXPLORE CAPABILITY ICONS  (.explore-icon)
   Animated SVG icons displayed in a grid on the explore page.
   JS repositions and expands them on interaction.
   ============================================================================= */

.explore-icon {
  position: absolute;
  width: 33.333%;
  height: 33.333%;
  display: flex;
  align-items: center;
  justify-content: center;
  pointer-events: none;
  opacity: 1;
  transform: translateY(0) scale(1) translateZ(0);
  transition:
    top     0.8s cubic-bezier(0.22, 1, 0.36, 1),
    left    0.8s cubic-bezier(0.22, 1, 0.36, 1),
    width   0.8s cubic-bezier(0.22, 1, 0.36, 1),
    height  0.8s cubic-bezier(0.22, 1, 0.36, 1),
    opacity 0.9s ease;
  will-change: transform, opacity, top, left;
}

.explore-icon svg {
  width: 60%;
  height: 60%;
  opacity: 1;
}

.explore-icon--pink svg path { fill: #ED00EC; }
.explore-icon--blue svg path { fill: #0000FF; }

/* Expanded state — icon fills the entire grid area */
.explore-icon.is-expanded {
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  z-index: 10;
}

/* Hide all non-expanded icons when one is expanded */
.icons-animated-grid.expanded-mode .explore-icon:not(.is-expanded) {
  opacity: 0;
  visibility: hidden;
}

.icons-animated-grid:not(.expanded-mode) .explore-icon {
  visibility: visible;
}

@media only screen and (max-width: 900px) {
  .explore-proofline {
    font-size: 11px;
  }
}


/* =============================================================================
   14. SCROLLBARS
   Base document keeps the simple site rail, while Explore now follows the
   same popup-scrollbar standard as case overlays: 10px gutter, softened
   track, padded thumb, and a gentle hover step rather than a raw browser rail.
   ============================================================================= */

/* Firefox */
* {
  scrollbar-width: thin;
  scrollbar-color: #d5d5e5 transparent;
}

.explore-overlay__content {
  scrollbar-width: thin;
  scrollbar-color: var(--explore-scrollbar-thumb) var(--explore-scrollbar-track);
}

/* Chrome / Safari / Edge */
html::-webkit-scrollbar {
  width: 10px;
  height: 10px;
}

.explore-overlay__content::-webkit-scrollbar,
.explore-overlay__scroll::-webkit-scrollbar {
  width: 10px;
  height: 10px;
}

html::-webkit-scrollbar-track {
  background: transparent;
}

.explore-overlay__content::-webkit-scrollbar-track,
.explore-overlay__scroll::-webkit-scrollbar-track {
  background: var(--explore-scrollbar-track);
  border-radius: 999px;
}

html::-webkit-scrollbar-thumb {
  background: #d5d5e5;
  border-radius: 999px;
}

.explore-overlay__content::-webkit-scrollbar-thumb,
.explore-overlay__scroll::-webkit-scrollbar-thumb {
  background: linear-gradient(180deg, var(--explore-scrollbar-thumb), var(--explore-scrollbar-thumb));
  border-radius: 999px;
  border: 2px solid transparent;
  background-clip: padding-box;
  box-shadow:
    inset 0 0 0 1px rgba(255, 255, 255, 0.22),
    0 0 0 1px rgba(75, 91, 120, 0.08);
}

html::-webkit-scrollbar-thumb:hover {
  background: #7a7a9c;
}

.explore-overlay__content::-webkit-scrollbar-thumb:hover,
.explore-overlay__scroll::-webkit-scrollbar-thumb:hover {
  background: linear-gradient(180deg, var(--explore-scrollbar-thumb-hover), var(--explore-scrollbar-thumb-hover));
  background-clip: padding-box;
}

/* =============================================================================
   15. ACCESSIBILITY
   Focus rings, touch targets, and reduced-motion overrides.
   ============================================================================= */

/* Keyboard focus ring — cyan contrasts on both dark portal and light content */
:focus-visible {
  outline: 2px solid rgba(85, 240, 255, 0.85);
  outline-offset: 3px;
}

/* Links: underline instead of box outline */
a:focus-visible {
  outline: none;
  text-decoration-color: rgba(85, 240, 255, 0.9);
  text-decoration-thickness: 2px;
}

/* Inputs already show a border change on focus */
input:focus-visible,
textarea:focus-visible,
select:focus-visible {
  outline: 2px solid rgba(85, 240, 255, 0.7);
  outline-offset: 0;
}

/* Touch target minimum — 44×44px per WCAG 2.5.5 */
@media only screen and (max-width: 900px) {
  a:not([class]),
  button:not([class]) {
    min-height: 44px;
    display: inline-flex;
    align-items: center;
  }
}

/* Collapse authored motion without relying on declaration priority. */
@media (prefers-reduced-motion: reduce) {
  html:focus-within {
    scroll-behavior: auto;
  }

  html[data-cp-page][data-cp-page] body,
  html[data-cp-page][data-cp-page] body *,
  html[data-cp-page][data-cp-page] body *::before,
  html[data-cp-page][data-cp-page] body *::after {
    animation-duration: 0.01ms;
    animation-iteration-count: 1;
    transition-duration: 0.01ms;
    scroll-behavior: auto;
  }

  #portal-loader,
  #portal-loader .progress,
  #portal-loader .pct,
  #page-transition-veil,
  .audio-toggle--link,
  .audio-toggle__pulse,
  .audio-toggle__label::after,
  .explore-overlay,
  .explore-flash,
  .explore-overlay__close,
  .file-upload-label,
  .wrap-form input,
  .wrap-form textarea,
  .explore-icon,
  .cp-detected {
    animation-duration: 0.01ms;
    animation-iteration-count: 1;
    transition-duration: 0.01ms;
  }

  #portal-loader .pct,
  .audio-toggle:not(.is-enabled) .audio-toggle__pulse,
  .explore-fluid__blob--current,
  .explore-fluid__blob-orbit-a-wrap,
  .explore-fluid__blob-orbit-b-wrap {
    animation: none;
  }
}


/* =============================================================================
   16. CASE TILE FOIL ANIMATIONS
   Holographic satin shimmer on featured-cases tiles.
   Keyframes only — the rules that reference them live in style.css.
   ============================================================================= */

@keyframes satinDrift {
  0%   { background-position: 0% 50%;   }
  50%  { background-position: 100% 50%; }
  100% { background-position: 0% 50%;   }
}

@keyframes satinHue {
  from { filter: hue-rotate(0deg);   }
  to   { filter: hue-rotate(360deg); }
}

/* ── Scroll reveal — explore (port of js_animateText) ───────────────────── */
.cp-detected {
  backface-visibility: hidden;
  transition-property: transform, opacity;
  transition-duration: 1.4s, 1.0s;
  transition-timing-function: ease;
  transform: translate3d(0, 0, 0);
}

.cp-detected:not(.cp-visible) {
  transform: translate3d(0, 2rem, 0);
  opacity: 0;
  pointer-events: none;
}

.cp-detected.cp-visible {
  transform: translate3d(0, 0, 0);
  opacity: 1;
  pointer-events: auto;
}


/* =============================================================================
   PORTAL POINTER
   Clean 2D model:
   1) .portal-pointer-anchor-plane is a tiny, invisible geometry layer that
      mirrors the portal surface's layout and zoom, but intentionally does not
      receive the portal's hover tilt.
   2) .portal-pointer-stage is outside .split-background in the DOM and is fixed
      to the viewport. JS copies the anchor's viewport coordinates to the
      pointer's left/top. The pointer graphic never inherits the portal/background
      3D transform, so tilt cannot re-rasterize or skew the SVG/text.
   ============================================================================= */

.portal-pointer-anchor-plane {
  position: absolute;
  top: 50%;
  left: 50%;
  width: min(calc(33.333vmax - 4px), 636px);
  aspect-ratio: 1 / 1;
  border-radius: 50%;
  overflow: visible;
  z-index: 9;
  pointer-events: none;
  user-select: none;
  transform: translate(-50%, -50%) scale(var(--pp-anchor-scale, 1));
  transform-origin: 50% 50%;
}

.portal-pointer-anchor {
  position: absolute;
  left: 58.5%;
  top: 9.2%;
  width: 1px;
  height: 1px;
  margin-left: -0.5px;
  margin-top: -0.5px;
  opacity: 0;
  pointer-events: none;
  user-select: none;
}

.portal-pointer-stage {
  position: fixed;
  inset: 0;
  overflow: visible;
  z-index: 12;
  pointer-events: none;
  user-select: none;
  transform: none;
  perspective: none;
}

.portal-pointer {
  position: absolute;
  left: var(--pp-anchor-x, -9999px);
  top: var(--pp-anchor-y, -9999px);
  z-index: 1;
  pointer-events: none;
  user-select: none;
  overflow: visible;

  /* SVG viewBox is 392x62; the visible tip circle center is at (18, 46). */
  --pp-tip-x: 4.591837%;
  --pp-tip-y: 74.193548%;
  --portal-pointer-diameter: var(--mainpage-circle);
  --pp-width: clamp(186px, calc(var(--portal-pointer-diameter) * 0.61), 308px);
  width: var(--pp-width);

  /* Constant 2D offset only: left/top is the tip point. */
  transform: translate(calc(-1 * var(--pp-tip-x)), calc(-1 * var(--pp-tip-y)));
  transform-origin: var(--pp-tip-x) var(--pp-tip-y);

  opacity: 0;
  transition: opacity 0.4s ease;
}

.portal-pointer__svg {
  display: block;
  width: 100%;
  height: auto;
  opacity: 0.96;
  filter: drop-shadow(0 0 1.2px rgba(255, 255, 255, 0.88))
          drop-shadow(0 0 4px rgba(255, 255, 255, 0.30));
}

.portal-pointer__label {
  position: absolute;
  right: 0;
  bottom: 100%;
  margin-bottom: 5px;
  font-family: var(--font, 'Poppins', sans-serif);
  font-size: clamp(9px, calc(var(--portal-pointer-diameter, var(--mainpage-circle)) * 0.026), 13px);
  font-weight: 500;
  letter-spacing: 0.038em;
  white-space: nowrap;
  color: #000000;
  text-shadow:
    0 0 1px rgba(255,255,255,0.95),
    0 0 6px rgba(255,255,255,0.92),
    0 1px 2px rgba(255,255,255,0.68);
  transition: opacity 0.3s ease;
}

.portal-pointer__label.is-updating {
  opacity: 0;
}

.portal-pointer__label.has-link {
  pointer-events: auto;
  cursor: pointer;
  text-decoration: underline;
  text-underline-offset: 2px;
  text-decoration-thickness: 1px;
}

.portal-pointer__label.has-link:hover,
.portal-pointer__label.has-link:focus-visible {
  color: var(--violet);
  text-shadow:
    0 0 1px rgba(255,255,255,0.9),
    0 0 8px rgba(237,0,236,0.4);
  outline: none;
}

.portal-pointer__label.has-link {
  cursor: pointer;
  text-decoration: none;
}

.portal-pointer__label.has-link:hover,
.portal-pointer__label.has-link:focus-visible {
  text-decoration: underline;
  text-underline-offset: 2px;
  outline: none;
}

/* AUTHORSHIP: is-segment-change drives the full pointer reveal — stroke-dashoffset
   animates the SVG path from tip outward so the line appears to grow from its
   anchor point toward the label, grounding the annotation in the portal surface
   rather than fading in as a disconnected graphic element. */
/* -- Segment-change: line draws from tip to label, then label fades in -- */

.portal-pointer.is-segment-change .portal-pointer__svg path {
  stroke-dasharray: 368.5;
  stroke-dashoffset: 368.5;
  animation: ppLineDraw 1.92s cubic-bezier(0.08, 0.5, 0.25, 1) forwards;
}

.portal-pointer.is-segment-change .portal-pointer__svg circle {
  stroke-dasharray: 35.2;
  stroke-dashoffset: 35.2;
  animation: ppCircleDraw 0.64s cubic-bezier(0.08, 0.5, 0.25, 1) 1.6s forwards;
}

.portal-pointer.is-segment-change .portal-pointer__label {
  opacity: 0;
  animation: ppLabelAppear 0.56s ease 1.7s forwards;
}

@keyframes ppLineDraw {
  0%   { stroke-dashoffset: 368.5; }
  100% { stroke-dashoffset: 0; }
}

@keyframes ppCircleDraw {
  0%   { stroke-dashoffset: 35.2; }
  100% { stroke-dashoffset: 0; }
}

@keyframes ppLabelAppear {
  0%   { opacity: 0; }
  100% { opacity: 1; }
}

/* =============================================================================
   PORTAL NAV ARROWS
   AUTHORSHIP: arrows live in a fixed layer identical in structure to the pointer
   stage — outside any 3D-transformed ancestor so they never inherit perspective
   distortion from portal tilt. left/top are written as inline style by JS using
   getBoundingClientRect on the portal surface, making position always match the
   live rendered circle regardless of zoom, resize, or orientation change.
   ============================================================================= */

.portal-nav-arrows {
  position: fixed;
  inset: 0;
  pointer-events: none;
  z-index: 12;
  user-select: none;
}

.portal-nav-arrows__btn {
  position: fixed;
  pointer-events: auto;
  cursor: pointer;
  background: none;
  border: none;
  padding: 14px;
  color: #000;
  opacity: 0;
  transform: translate(-50%, -50%);
  transition: opacity 0.35s ease, color 0.2s ease, transform 0.15s ease;
  -webkit-tap-highlight-color: transparent;
  /* start offscreen */
  left: -9999px;
  top: -9999px;
}

.portal-nav-arrows__btn.is-visible {
  opacity: 0.5;
}

.portal-nav-arrows__btn:hover,
.portal-nav-arrows__btn:focus-visible,
.portal-nav-arrows__btn.is-key-active {
  opacity: 1;
  color: var(--lightblue);
}

.portal-nav-arrows__btn:active {
  transform: translate(-50%, -50%) scale(0.85);
}

.portal-nav-arrows__btn svg {
  display: block;
  filter: drop-shadow(0 0 4px rgba(255,255,255,0.8));
}


@media only screen and (max-width: 900px) {
  .portal-nav-arrows {
    display: none;
  }

  .portal-pointer-anchor-plane {
    top: calc(var(--cp-mobile-portal-top) + 2px);
    left: 50%;
    width: calc(var(--cp-mobile-portal-size) - 4px);
    transform: translate(-50%, 0) scale(var(--pp-anchor-scale, 1));
  }

  .portal-pointer {
    --portal-pointer-diameter: var(--cp-mobile-portal-size, 320px);
    --pp-width: clamp(132px, calc(var(--portal-pointer-diameter) * 0.50), 206px);
  }
}

@media only screen and (max-width: 560px) {
  .portal-pointer {
    --pp-width: clamp(116px, calc(var(--portal-pointer-diameter) * 0.47), 176px);
  }
}

/* Pause the main-page motion layer whenever the shared overlay contract marks Case or Explore as active.
   The selector is scoped away from overlay shells so popup intro/close choreography keeps running. */
body.main-animations-paused .mainwrap,
body.main-animations-paused .mainwrap *,
body.main-animations-paused .mainwrap *::before,
body.main-animations-paused .mainwrap *::after,
body.main-animations-paused .split-background,
body.main-animations-paused .split-background *,
body.main-animations-paused .split-background *::before,
body.main-animations-paused .split-background *::after,
body.main-animations-paused .portal-pointer-stage,
body.main-animations-paused .portal-pointer-stage *,
body.main-animations-paused .portal-pointer-stage *::before,
body.main-animations-paused .portal-pointer-stage *::after,
body.main-animations-paused .portal-nav-arrows,
body.main-animations-paused .portal-nav-arrows *,
body.main-animations-paused .portal-nav-arrows *::before,
body.main-animations-paused .portal-nav-arrows *::after {
  animation-play-state: paused !important;
}
