:root {
  color-scheme: light;
  /* Site accent — the shared header (CONTACT pill, nav hovers, dropdowns,
     toggle) drinks from these, so a page can re-theme the whole chrome to
     match its own world (solution pages set red/green/blue). Defaults = the
     classic blue, so every page that doesn't override renders unchanged. */
  --site-accent: #007aff;
  --site-accent-bright: #9ecbff; /* dark-mode text accent */
  --site-accent-strong: #49aaff; /* dark-mode borders / hover bg */
  --ink: #111411;
  --muted: rgba(17, 20, 17, 0.68);
  --line: rgba(17, 20, 17, 0.16);
  --dark: #f7f8f3;
  --orange: #007aff;
  --red: #007aff;
  --teal: #b9bdb2;
  --green: #007aff;
  --shadow: 0 24px 80px rgba(46, 61, 42, 0.14);
  --radius: 18px;
}

* {
  box-sizing: border-box;
}

html {
  background: #fff;
  overflow-x: clip;
  scroll-behavior: smooth;
  /* No rubber band past the ends. Overscrolling the bottom used to bounce
     the page off the footer and expose the fixed background behind it. */
  overscroll-behavior-y: none;
}

body {
  margin: 0;
  min-width: 320px;
  overflow-x: clip;
  color: var(--ink);
  background: #fff;
  font-family: Inter, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
}

.flow-background {
  position: fixed;
  inset: 0;
  z-index: 0;
  display: block;
  width: 100%;
  height: 100%;
  pointer-events: none;
}

a {
  color: inherit;
  text-decoration: none;
}

.site-shell {
  --hero-exit: 0;
  --scroll-cue-opacity: 1;
  position: relative;
  z-index: 1;
  min-height: 100vh;
  overflow: visible;
}

.site-shell.init-failed .orbit-showcase {
  --orbit-copy-progress: 1;
  --offer-enter: 1;
  --offer-absorb: 0;
  --offer-shrink: 0.22;
  --offer-presence: 1;
  --offer-depth-front: 0;
  --carousel-presence: 1;
  --orbit-label-pop: 1;
  --orbit-label-scale: 1;
  --orbit-label-wide: 1;
  --orbit-label-text: 1;
  --orbit-label-warp-x: 1;
  --orbit-label-warp-y: 1;
  --orbit-controls-events: auto;
}

.site-shell.init-failed .orbit-copy,
.site-shell.init-failed .offer-bridge-copy,
.site-shell.init-failed .orbit-controls-panel,
.site-shell.init-failed .orbit-section-label,
.site-shell.init-failed .orbit-canvas,
.site-shell.init-failed .scroll-cue {
  opacity: 1 !important;
}

.site-shell.init-failed .offer-bridge-copy-front {
  opacity: 0 !important;
}

.site-shell.init-failed .ecosystem-section {
  --ecosystem-copy-opacity: 1;
  --ecosystem-intro-opacity: 1;
  --ecosystem-divider-opacity: 0.76;
  --ecosystem-stage-lift: 0px;
}

/* ---------- Site header ----------
   The bar is the same glass bubble as the word wheel (clear middle, bright
   top hairline, liquid distortion layer, cursor glow ring). On scroll-down it
   "splits": the pill collapses while a hamburger bubble flies to the left
   edge and a logo bubble flies to the right. Scroll up reassembles it. */
.topbar {
  position: fixed;
  z-index: 100;
  top: 0.9rem;
  left: 50%;
  width: min(1160px, calc(100vw - 1.6rem));
  transform: translateX(-50%);
}

/* Shared bubble material (the wheel pills' permanent "hover state"). */
.glass-bubble {
  --glow-strength: 0;
  --glow-local-x: -9999px;
  --glow-local-y: -9999px;
  --glow-ring: 0.6px;
  --glow-size: 90px;
  position: relative;
  border: none;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.03);
  box-shadow:
    inset 0 -0.09em 0.1em rgba(0, 0, 0, 0.05),
    inset 0 0.09em 0.1em rgba(255, 255, 255, 0.5),
    inset 0 0 0.04em 0.045em rgba(255, 255, 255, 0.4);
}

.glass-bubble .bubble-liquid-base {
  position: absolute;
  inset: 0;
  z-index: 0;
  display: block;
  overflow: hidden;
  border-radius: inherit;
  -webkit-backdrop-filter: blur(3px);
  backdrop-filter: blur(3px);
  filter: url(#glass-distortion);
  pointer-events: none;
}

/* Cursor-following hairline glow — identical ring to the wheel bubbles. */
.glass-bubble::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 3;
  padding: var(--glow-ring, 0.6px);
  border-radius: inherit;
  background: radial-gradient(
    circle var(--glow-size, 90px) at var(--glow-local-x, -9999px) var(--glow-local-y, -9999px),
    rgba(255, 255, 255, 0.98) 0%,
    rgba(255, 255, 255, 0.85) 20%,
    rgba(255, 255, 255, 0.45) 36%,
    rgba(255, 255, 255, 0) 54%
  );
  opacity: var(--glow-strength, 0);
  pointer-events: none;
  -webkit-mask:
    linear-gradient(#000 0 0) content-box,
    linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
  mask:
    linear-gradient(#000 0 0) content-box,
    linear-gradient(#000 0 0);
  mask-composite: exclude;
  transition: opacity 120ms ease;
}

/* Soft drop — same as the wheel bubbles' shadow layer. */
.glass-bubble::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  border-radius: inherit;
  box-shadow: 0 0.42em 0.9em -0.3em rgba(17, 20, 17, 0.32);
  pointer-events: none;
}

/* Visible unless the split state hides it - stated, because that rule now
   sets visibility and the two have to agree. */
.header-main {
  visibility: visible;
  display: flex;
  align-items: center;
  gap: clamp(0.8rem, 2vw, 1.6rem);
  padding: 0.5rem 0.55rem 0.5rem 1.05rem;
  transform-origin: center;
  /* Arriving: hold until the bubbles have gone. */
  transition: transform 0.5s cubic-bezier(0.5, 0, 0.2, 1), opacity 0.24s ease 0.15s;
}

.header-main > :not(.bubble-liquid-base) {
  position: relative;
  z-index: 1;
}

html.header-split .header-main {
  opacity: 0;
  /* And out of the tab order. opacity:0 with pointer-events:none hides it from
     the eye and the mouse but NOT from the keyboard - Tab walked into five to
     seven invisible stops on every page and the browser scrolled the page to
     chase focus it could not show. visibility does what opacity cannot. */
  visibility: hidden;
  /* Leaving: straight out, no delay, so nothing overlaps the bubbles. */
  transition: transform 0.5s cubic-bezier(0.5, 0, 0.2, 1), opacity 0.15s ease;
  transform: scaleX(0.42) scaleY(0.72);
  pointer-events: none;
}

/* The two split bubbles: parked at the pill's center while assembled, they
   fly to the corners when the header splits. */
/* Inspo bubble — admin only, pinned to the header's top-left so it never
   sits in the visitor tab row (see placeInspo in site-header.js).
   Selector matches `.header-main > :not(.bubble-liquid-base)`, which
   forces position: relative on every child. */
.header-main > .header-inspo-bubble {
  position: absolute;
  top: 0.5rem;
  left: 0.7rem;
  z-index: 2;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0.32rem 0.72rem;
  border-radius: 999px;
  color: inherit;
  font-size: 0.72rem;
  font-weight: 700;
  text-decoration: none;
  opacity: 0.85;
  transition: opacity 160ms ease;
}

.header-main > .header-inspo-bubble:hover {
  opacity: 1;
}

.header-inspo-label {
  position: relative;
  z-index: 1;
}

.header-bubble {
  position: absolute;
  top: 0;
  display: inline-flex;
  width: 3.3rem;
  height: 3.3rem;
  align-items: center;
  justify-content: center;
  padding: 0;
  color: var(--ink);
  font-family: inherit;
  cursor: pointer;
  opacity: 0;
  pointer-events: none;
  /* Not focusable while hidden - see .header-main. */
  visibility: hidden;
  /* Leaving: straight out, matching the pill's exit. */
  transition: transform 0.5s cubic-bezier(0.5, 0, 0.2, 1), opacity 0.15s ease;
}

.header-menu-bubble {
  left: 0;
  transform: translateX(calc(min(1160px, 100vw - 1.6rem) / 2 - 50%)) scale(0.4);
}

.header-logo-bubble {
  right: 0;
  transform: translateX(calc((min(1160px, 100vw - 1.6rem) / 2 - 50%) * -1)) scale(0.4);
}

html.header-split .header-bubble {
  opacity: 1;
  visibility: visible;
  /* Arriving: hold until the pill has gone. */
  transition: transform 0.5s cubic-bezier(0.5, 0, 0.2, 1), opacity 0.24s ease 0.15s;
  pointer-events: auto;
  transform: translateX(0) scale(1);
}

/* A filter panel open under the split header: the two corner bubbles slide
   off to the sides so the panel can use the full width. Without this the
   panel is pinched to roughly 220px on a phone and the tiers stack into an
   unusable column. They slide straight back when it closes. */
html.header-split.filters-expanded .header-menu-bubble {
  opacity: 0;
  transform: translateX(-150%) scale(0.8);
  pointer-events: none;
}

html.header-split.filters-expanded .header-logo-bubble {
  opacity: 0;
  transform: translateX(150%) scale(0.8);
  pointer-events: none;
}

html.header-split .topbar {
  pointer-events: none;
}

html.header-split .header-bubble,
html.header-split .header-menu-drop {
  pointer-events: auto;
}

.header-bubble .burger {
  display: grid;
  gap: 4px;
  z-index: 1;
}

.header-bubble .burger i {
  display: block;
  width: 1.05rem;
  height: 2px;
  border-radius: 2px;
  background: currentColor;
}

.header-logo-bubble img {
  position: relative;
  z-index: 1;
  display: block;
  width: 1.7rem;
  height: auto;
  filter: invert(1);
}

/* Hamburger dropdown */
.header-menu-drop {
  position: absolute;
  top: calc(100% + 0.6rem);
  left: 0;
  z-index: 110;
  display: grid;
  gap: 0.05rem;
  width: max-content;
  min-width: 14.5rem;
  max-width: 20rem;
  min-width: 15.5rem;
  padding: 0.5rem 0.5rem 0.45rem;
  border: 1px solid rgba(255, 255, 255, 0.6);
  border-radius: 22px;
  background:
    linear-gradient(180deg, rgba(255, 255, 255, 0.5), rgba(255, 255, 255, 0) 42%),
    rgba(246, 249, 252, 0.76);
  box-shadow:
    0 36px 80px rgba(17, 20, 17, 0.26),
    0 4px 14px rgba(17, 20, 17, 0.08),
    inset 0 1px 0.5px rgba(255, 255, 255, 0.95),
    inset 0 -1px 0.5px rgba(17, 20, 17, 0.05);
  -webkit-backdrop-filter: blur(26px) saturate(1.8);
  backdrop-filter: blur(26px) saturate(1.8);
  transform-origin: top left;
  animation: menu-drop-in 260ms cubic-bezier(0.22, 1, 0.36, 1) both;
}

@keyframes menu-drop-in {
  from { opacity: 0; transform: translateY(-6px) scale(0.97); }
  to { opacity: 1; transform: none; }
}

/* Rows arrive just behind the panel, in order. */
.header-menu-drop > * {
  animation: menu-row-in 300ms cubic-bezier(0.22, 1, 0.36, 1) both;
}
.header-menu-drop > :nth-child(1) { animation-delay: 40ms; }
.header-menu-drop > :nth-child(2) { animation-delay: 66ms; }
.header-menu-drop > :nth-child(3) { animation-delay: 92ms; }
.header-menu-drop > :nth-child(4) { animation-delay: 118ms; }
.header-menu-drop > :nth-child(5) { animation-delay: 144ms; }
.header-menu-drop > :nth-child(6) { animation-delay: 170ms; }
.header-menu-drop > :nth-child(7) { animation-delay: 196ms; }
.header-menu-drop > :nth-child(8) { animation-delay: 222ms; }
.header-menu-drop > :nth-child(n + 9) { animation-delay: 248ms; }

@keyframes menu-row-in {
  from { opacity: 0; transform: translateX(-7px); }
  to { opacity: 1; transform: none; }
}

@media (prefers-reduced-motion: reduce) {
  .header-menu-drop,
  .header-menu-drop > * {
    animation: none;
  }
}

.header-menu-drop[hidden] {
  display: none;
}

.header-menu-drop a {
  position: relative;
  display: flex;
  align-items: center;
  gap: 0.72rem;
  padding: 0.62rem 1.5rem 0.62rem 0.72rem;
  border-radius: 13px;
  color: var(--ink);
  font-size: 0.94rem;
  font-weight: 600;
  letter-spacing: -0.01em;
  transition: background 190ms ease, color 190ms ease, transform 190ms ease;
}

/* The category marker is a RAIL, not a dot: it stretches on hover and fills
   solid on the current page, so the row reads as a tab rather than a
   bulleted list item. --row-accent falls back to a neutral track for the
   entries that carry no category. */
.header-menu-drop a::before {
  content: "";
  flex: none;
  width: 3px;
  height: 0.95rem;
  border-radius: 999px;
  background: var(--row-accent, rgba(17, 20, 17, 0.16));
  transition: height 220ms cubic-bezier(0.22, 1, 0.36, 1), opacity 190ms ease;
}

/* Chevron slides in from the right edge on hover. */
.header-menu-drop a::after {
  position: absolute;
  right: 0.68rem;
  color: currentColor;
  content: "›";
  font-size: 1.02rem;
  font-weight: 700;
  line-height: 1;
  opacity: 0;
  transform: translateX(-4px);
  transition: opacity 190ms ease, transform 190ms ease;
}

.header-menu-drop a[data-cat="media"] { --row-accent: #ff3b30; --row-accent-bright: #ff6b61; }
.header-menu-drop a[data-cat="marketing"] { --row-accent: #16a34a; --row-accent-bright: #3ecf72; }
.header-menu-drop a[data-cat="workflow"] { --row-accent: #007aff; --row-accent-bright: #4aa8ff; }

/* The row owns its colour: a media row tints red, marketing green, workflow
   blue, and anything without a category falls back to the page accent.
   Tinting every row with --site-accent put a red rail next to blue text. */
.header-menu-drop a[aria-current="page"] {
  background: color-mix(in srgb, var(--row-accent, var(--site-accent)) 15%, transparent);
  color: var(--row-accent, var(--site-accent));
  font-weight: 700;
}

.header-menu-drop a[aria-current="page"]::before {
  height: 1.35rem;
  background: var(--row-accent, var(--site-accent));
}

/* Contact reads as the action, not another destination: filled accent
   button, no rail and no chevron (both belong to the list rows). */
.header-menu-drop a.menu-cta {
  justify-content: center;
  margin-top: 0.55rem;
  padding: 0.82rem 1rem;
  border-radius: 14px;
  background: linear-gradient(180deg, #2b93ff, #007aff);
  color: #fff;
  font-size: 0.95rem;
  font-weight: 800;
  letter-spacing: 0.01em;
  box-shadow:
    0 10px 22px rgba(0, 122, 255, 0.34),
    inset 0 1px 0 rgba(255, 255, 255, 0.38);
}

.header-menu-drop a.menu-cta::before,
.header-menu-drop a.menu-cta::after {
  display: none;
}

/* Beats both the generic hover and the current-page tint, which would
   otherwise wash the button back to a pale row. */
.header-menu-drop a.menu-cta,
.header-menu-drop a.menu-cta:visited,
.header-menu-drop a.menu-cta[aria-current="page"],
html.theme-dark .header-menu-drop a.menu-cta,
html.theme-dark .header-menu-drop a.menu-cta[aria-current="page"],
html.landing-text-dark .header-menu-drop a.menu-cta,
html.landing-text-dark .header-menu-drop a.menu-cta[aria-current="page"] {
  background: linear-gradient(180deg, #2b93ff, #007aff);
  color: #fff;
}

@media (hover: hover) {
  .header-menu-drop a.menu-cta:hover,
  html.theme-dark .header-menu-drop a.menu-cta:hover,
  html.landing-text-dark .header-menu-drop a.menu-cta:hover {
    background: linear-gradient(180deg, #46a0ff, #0b84ff);
    color: #fff;
    transform: translateY(-1px);
    box-shadow:
      0 14px 28px rgba(0, 122, 255, 0.42),
      inset 0 1px 0 rgba(255, 255, 255, 0.45);
  }
}

.header-menu-drop a:visited,
.header-menu-drop a:active {
  color: var(--ink);
}

/* Pointer devices only: iOS latches :hover after a tap and the last item
   touched would stay lit behind the closed menu. */
@media (hover: hover) {
  .header-menu-drop a:hover {
    background: color-mix(in srgb, var(--row-accent, var(--site-accent)) 10%, transparent);
    color: var(--row-accent, var(--site-accent));
    transform: translateX(2px);
  }

  .header-menu-drop a:hover::before {
    height: 1.35rem;
    background: var(--row-accent, var(--site-accent));
  }

  .header-menu-drop a:hover::after {
    opacity: 0.55;
    transform: none;
  }
}

.header-menu-drop a:active {
  transform: scale(0.985);
}

.brand {
  display: inline-flex;
  align-items: center;
  gap: 0.55rem;
  color: var(--ink);
  font-size: 1rem;
  font-weight: 800;
  letter-spacing: 0;
  line-height: 1;
  white-space: nowrap;
}

.brand:visited,
.brand:active {
  color: var(--ink);
}

.brand img {
  display: block;
  width: 2.1rem;
  height: auto;
  filter: invert(1);
  object-fit: contain;
}

.nav-links {
  display: flex;
  align-items: center;
  gap: clamp(0.9rem, 2.4vw, 1.9rem);
  margin: 0 auto;
}

/* Top-nav items are white with a soft dark halo so they stay legible over the
   glass bar on any page, and pick up a blue glow on hover. */
.nav-links a,
.nav-links .nav-current {
  padding: 0.35rem 0.1rem;
  color: #ffffff;
  font-size: 0.9rem;
  font-weight: 700;
  white-space: nowrap;
  text-shadow:
    0 1px 1px rgba(4, 8, 16, 0.72),
    0 0 3px rgba(4, 8, 16, 0.6),
    0 0 11px rgba(4, 8, 16, 0.34);
  transition: color 140ms ease, text-shadow 200ms ease;
}

.nav-links a:visited,
.nav-links a:active {
  color: #ffffff;
}

.nav-links a:hover {
  color: #ffffff;
  text-shadow:
    0 0 8px rgba(120, 180, 255, 0.95),
    0 0 20px rgba(90, 160, 255, 0.7),
    0 0 34px rgba(90, 160, 255, 0.45),
    0 1px 2px rgba(8, 11, 20, 0.4);
}

.nav-links .nav-current {
  border-bottom: 2px solid #4aa3ff;
  padding-bottom: 0.18rem;
  text-shadow:
    0 0 8px rgba(120, 180, 255, 0.85),
    0 0 18px rgba(90, 160, 255, 0.55),
    0 1px 2px rgba(8, 11, 20, 0.4);
}

/* ---- Solutions dropdowns (Media / Marketing / AI), built by site-header.js
   from solutions-data.js. Same frosted panel language as .header-menu-drop. */
.nav-drop {
  position: relative;
}

.nav-drop-btn {
  appearance: none;
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  padding: 0.35rem 0.1rem;
  border: none;
  background: none;
  color: #ffffff;
  font-family: inherit;
  font-size: 0.9rem;
  font-weight: 700;
  white-space: nowrap;
  cursor: pointer;
  text-shadow:
    0 1px 1px rgba(4, 8, 16, 0.72),
    0 0 3px rgba(4, 8, 16, 0.6),
    0 0 11px rgba(4, 8, 16, 0.34);
  transition: color 140ms ease, text-shadow 200ms ease;
}

.nav-drop-btn .nav-caret {
  font-size: 0.62em;
  opacity: 0.75;
  transition: transform 160ms ease;
}

.nav-drop:hover .nav-drop-btn,
.nav-drop.open .nav-drop-btn {
  color: #ffffff;
  text-shadow:
    0 0 8px rgba(120, 180, 255, 0.95),
    0 0 20px rgba(90, 160, 255, 0.7),
    0 0 34px rgba(90, 160, 255, 0.45),
    0 1px 2px rgba(8, 11, 20, 0.4);
}

.nav-drop.open .nav-caret {
  transform: rotate(180deg);
}

.nav-drop-panel {
  position: absolute;
  top: calc(100% + 0.55rem);
  left: 50%;
  z-index: 120;
  display: flex;
  flex-direction: column;
  min-width: 11.5rem;
  padding: 0.5rem;
  border-radius: 14px;
  /* Frosted "liquid glass" (matches the footer's material). Safari renders the
     menu text fine as long as its color is pinned (it is, below + in the
     landing overrides) — the old white-on-white was a text-color inheritance
     bug, not the blur itself. */
  background: rgba(246, 249, 252, 0.7);
  border: 1px solid rgba(255, 255, 255, 0.55);
  box-shadow:
    0 24px 60px rgba(17, 20, 17, 0.2),
    inset 0 1px 0.5px rgba(255, 255, 255, 0.85),
    inset 0 -1px 0.5px rgba(17, 20, 17, 0.04);
  -webkit-backdrop-filter: blur(22px) saturate(1.7);
  backdrop-filter: blur(22px) saturate(1.7);
  opacity: 0;
  transform: translateX(-50%) translateY(-6px);
  visibility: hidden;
  pointer-events: none;
  transition: opacity 160ms ease, transform 180ms ease, visibility 0s linear 180ms;
}

/* Invisible hover bridge so the pointer can travel button -> panel. */
.nav-drop-panel::before {
  content: "";
  position: absolute;
  top: -0.6rem;
  left: 0;
  right: 0;
  height: 0.6rem;
}

.nav-drop.open .nav-drop-panel {
  opacity: 1;
  transform: translateX(-50%) translateY(0);
  visibility: visible;
  pointer-events: auto;
  transition: opacity 160ms ease, transform 180ms ease, visibility 0s;
}

.nav-drop-panel a {
  padding: 0.5rem 0.75rem;
  border-radius: 9px;
  color: var(--ink);
  /* Safari: the visible glyph fill = -webkit-text-fill-color, which otherwise
     wins over `color`. Pin it to currentColor so the fill always follows the
     text color (defeats any inherited white fill from the hero-nav treatment). */
  -webkit-text-fill-color: currentColor;
  font-size: 0.88rem;
  font-weight: 600;
  white-space: nowrap;
  transition: background 120ms ease, color 120ms ease;
}

.nav-drop-panel a:hover {
  background: color-mix(in srgb, var(--site-accent) 10%, transparent);
  color: var(--site-accent);
}

/* Dropdown items sit on their own frosted panel — plain ink, never a glow. */
.nav-drop-panel a {
  text-shadow: none;
}

/* The white-with-halo nav treatment is for the landing page's scenery only.
   Every other page has a flat background: plain ink, zero glow. */
body:not(.landing-page) .nav-links a,
body:not(.landing-page) .nav-links a:visited,
body:not(.landing-page) .nav-links a:active,
body:not(.landing-page) .nav-drop-btn {
  color: var(--ink);
  text-shadow: none;
}

body:not(.landing-page) .nav-links a:hover,
body:not(.landing-page) .nav-drop:hover .nav-drop-btn,
body:not(.landing-page) .nav-drop.open .nav-drop-btn {
  color: var(--site-accent);
  text-shadow: none;
}

/* Group labels inside the hamburger dropdown. */
.menu-group-label {
  display: flex;
  align-items: center;
  margin: 0.75rem 0 0.3rem;
  padding: 0 0.75rem;
  color: var(--muted);
  font-size: 0.58rem;
  font-weight: 800;
  letter-spacing: 0.19em;
  text-transform: uppercase;
  gap: 0.6rem;
}

/* Hairline running out from each label, so the groups read as sections
   rather than as stray small text. */
.menu-group-label::after {
  height: 1px;
  flex: 1;
  background: linear-gradient(90deg, rgba(17, 20, 17, 0.13), transparent);
  content: "";
}

.menu-group-label:first-child {
  margin-top: 0.1rem;
}

/* The hero word bubbles link to their solution pages. */
.word-carousel span {
  cursor: pointer;
}

.top-contact {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0.55rem 1.15rem;
  border: 1px solid color-mix(in srgb, var(--site-accent) 35%, transparent);
  border-radius: 999px;
  background: color-mix(in srgb, var(--site-accent) 10%, transparent);
  color: var(--site-accent);
  font-size: 0.78rem;
  font-weight: 800;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  white-space: nowrap;
  transition: background 160ms ease, color 160ms ease, border-color 160ms ease;
}

.top-contact:visited,
.top-contact:active {
  color: var(--site-accent);
}

.top-contact:hover {
  border-color: var(--site-accent);
  background: var(--site-accent);
  color: #fff;
}

.theme-toggle {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  border: 1px solid rgba(17, 20, 17, 0.14);
  border-radius: 999px;
  background: rgba(17, 20, 17, 0.045);
  color: var(--muted);
  font-family: inherit;
  cursor: pointer;
  transition:
    border-color 160ms ease,
    background 160ms ease,
    color 160ms ease,
    transform 160ms ease;
}

.theme-toggle:hover {
  border-color: color-mix(in srgb, var(--site-accent) 42%, transparent);
  background: color-mix(in srgb, var(--site-accent) 10%, transparent);
  color: var(--site-accent);
}

.theme-toggle:focus-visible {
  outline: 2px solid color-mix(in srgb, var(--site-accent) 62%, transparent);
  outline-offset: 2px;
}

.theme-toggle-inline {
  padding: 0.42rem 0.72rem 0.42rem 0.48rem;
}

.theme-toggle-track {
  position: relative;
  display: inline-flex;
  width: 2.12rem;
  height: 1.16rem;
  flex: 0 0 auto;
  border: 1px solid rgba(17, 20, 17, 0.18);
  border-radius: 999px;
  background: rgba(17, 20, 17, 0.11);
  box-shadow: inset 0 1px 2px rgba(17, 20, 17, 0.12);
  transition: border-color 180ms ease, background 180ms ease;
}

.theme-toggle-knob {
  position: absolute;
  top: 50%;
  left: 0.17rem;
  width: 0.78rem;
  height: 0.78rem;
  border-radius: 999px;
  background: currentColor;
  box-shadow: 0 1px 4px rgba(17, 20, 17, 0.24);
  transform: translateY(-50%);
  transition: transform 220ms cubic-bezier(0.2, 0.9, 0.24, 1), background 180ms ease;
}

.theme-toggle-label {
  font-size: 0.68rem;
  font-weight: 800;
  letter-spacing: 0.06em;
  line-height: 1;
  text-transform: uppercase;
  white-space: nowrap;
}

.header-menu-drop .theme-toggle-menu {
  width: 100%;
  justify-content: space-between;
  margin-top: 0.45rem;
  padding: 0.7rem 0.7rem 0.55rem;
  border: 0;
  border-top: 1px solid var(--line);
  border-radius: 11px;
  background: transparent;
  color: var(--ink);
  font-size: 0.85rem;
  font-weight: 600;
  letter-spacing: 0.02em;
}

.header-menu-drop .theme-toggle-menu:hover {
  background: color-mix(in srgb, var(--site-accent) 10%, transparent);
  color: var(--site-accent);
}

.hero {
  min-height: 100svh;
  padding: 6.8rem clamp(1rem, 4vw, 3.5rem) 3.5rem;
}

.slogan-hero {
  position: relative;
  z-index: 5;
  display: grid;
  align-items: center;
  min-height: 100svh;
  padding-top: 7.6rem;
  padding-bottom: 2rem;
  pointer-events: none;
}

/* Shader background inside the offer bridge: everything paints above it,
   and the bubble glass can sample it (same backdrop root). The bridge
   scroll-scales/translates its contents — this transform is the exact
   inverse, so the shader reads as a fixed, non-scaling background. */
.hero-shader {
  position: absolute;
  top: 50%;
  left: 50%;
  z-index: -1;
  display: block;
  /* Sized to the viewport (not the narrower bridge box) with overscan, and
     centered on the bridge so the inverse transform still cancels cleanly. */
  width: 120vw;
  height: 120vh;
  margin-top: -60vh;
  margin-left: -60vw;
  transform:
    translate3d(
      0,
      calc(
        (((1 - var(--offer-enter, 1)) * 40vh) + (var(--offer-absorb, 0) * 28vh))
        / (1.06 - (var(--offer-shrink, 0) * 0.62)) * -1
      ),
      0
    )
    scale(calc(1 / (1.06 - (var(--offer-shrink, 0) * 0.62))));
  transform-origin: center center;
  pointer-events: none;
}

.hero-intro {
  position: relative;
  z-index: 2;
  max-width: 1180px;
  margin: 0 auto;
  text-align: center;
  opacity: calc(1 - var(--hero-exit));
  transform:
    translateY(calc(var(--hero-exit) * -8vh))
    scale(calc(1 - (var(--hero-exit) * 0.08)));
  transform-origin: center;
  will-change: opacity, transform, filter;
  filter: blur(calc(var(--hero-exit) * 9px));
}

.scroll-cue {
  position: absolute;
  right: 0;
  bottom: clamp(1rem, 3vh, 2rem);
  left: 0;
  z-index: 4;
  display: grid;
  justify-items: center;
  gap: 0.48rem;
  color: rgba(17, 20, 17, 0.54);
  font-size: 0.72rem;
  font-weight: 800;
  letter-spacing: 0.16em;
  opacity: var(--scroll-cue-opacity);
  text-transform: uppercase;
  transform: translateY(calc((1 - var(--scroll-cue-opacity)) * 0.7rem));
  transition: opacity 120ms linear, transform 120ms linear;
}

.scroll-cue i {
  display: block;
  width: 0.62rem;
  height: 0.62rem;
  border-right: 2px solid currentColor;
  border-bottom: 2px solid currentColor;
  transform: rotate(45deg);
}

.eyebrow {
  margin: 0 0 0.7rem;
  color: var(--green);
  font-size: 0.76rem;
  font-weight: 800;
  letter-spacing: 0.14em;
  text-transform: uppercase;
}

h1,
h2,
p {
  margin-top: 0;
}

.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

h1 {
  max-width: 780px;
  margin-bottom: 1rem;
  font-size: clamp(3rem, 7vw, 7.4rem);
  line-height: 0.92;
  letter-spacing: 0;
}

.slogan-hero h1 {
  max-width: 1120px;
  margin-right: auto;
  margin-left: auto;
  font-size: clamp(4.2rem, 10.5vw, 12rem);
  line-height: 0.82;
}

.hero-solution-lockup {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: clamp(0.75rem, 1.8vw, 1.8rem);
  max-width: calc(100vw - clamp(2rem, 6vw, 7rem));
  margin: clamp(1rem, 3vw, 2rem) auto 1.1rem;
  color: var(--ink);
  font-size: clamp(3rem, 7.6vw, 8.4rem);
  font-weight: 800;
  line-height: 0.92;
}

.hero-solution-lockup-original {
  opacity: 0;
  visibility: hidden;
}

.word-carousel,
.offer-word-carousel {
  --word-count: 10;
  position: relative;
  flex: 0 1 clamp(20rem, 52vw, 64rem);
  width: clamp(20rem, 52vw, 64rem);
  height: clamp(15rem, 29vw, 28rem);
  overflow: visible;
  perspective: 1050px;
  cursor: grab;
  touch-action: none;
  user-select: none;
}

.offer-word-carousel {
  cursor: default;
  pointer-events: none;
}

.word-carousel.is-dragging {
  cursor: grabbing;
}

.word-carousel span,
.offer-word-carousel span {
  --wheel-front: 0;
  --wheel-presence: 0;
  --word-text-scale: 1;
  --bubble-extra-current: 0px;
  --glow-strength: 0;
  --glow-local-x: -9999px;
  --glow-local-y: -9999px;
  --glow-ring: 0.5px;
  --glow-size: 60px;
  --bubble-radius: 999rem;
  position: absolute;
  top: 50%;
  left: 50%;
  display: inline-flex;
  box-sizing: border-box;
  width: calc(min(calc(100% - 3.5rem), clamp(22rem, 52vw, 56rem)) + var(--bubble-extra-current));
  min-height: clamp(4.6rem, 9vw, 9.8rem);
  align-items: center;
  justify-content: center;
  padding: 0.08em 0.24em 0.14em;
  border-radius: var(--bubble-radius);
  border: none;
  /* Permanent "hover state" of the CTA buttons: near-transparent middle with
     the bright hairline sitting on the TOP lip. */
  background: rgba(255, 255, 255, 0.03);
  box-shadow:
    inset 0 -0.09em 0.1em rgba(0, 0, 0, 0.05),
    inset 0 0.09em 0.1em rgba(255, 255, 255, 0.5),
    inset 0 0 0.04em 0.045em rgba(255, 255, 255, 0.4);
  color: rgba(17, 20, 17, 0.92);
  /* Same face as "Solutions": inherit the lockup's weight/tracking. */
  font-size: calc(1em * var(--word-text-scale));
  line-height: 1;
  white-space: nowrap;
  opacity: 0;
  text-shadow: none;
  transform: translate(-50%, -50%);
  transition: box-shadow 0.3s ease, background 0.35s ease;
  /* The liquid layer sits inside this stacking context at z:0, matching the
     header bubbles. Keeping it above the element background is what lets the
     backdrop sample and refract the shader passing behind the pill. */
  will-change: transform;
}

/* Cursor-following hairline glow — identical to the CTA buttons' ring. */
.word-carousel span::before,
.offer-word-carousel span::before {
  position: absolute;
  inset: 0;
  z-index: 4;
  padding: var(--glow-ring, 0.5px);
  border-radius: var(--bubble-radius);
  background: radial-gradient(
    circle var(--glow-size, 60px) at var(--glow-local-x, -9999px) var(--glow-local-y, -9999px),
    rgba(255, 255, 255, 0.98) 0%,
    rgba(255, 255, 255, 0.85) 20%,
    rgba(255, 255, 255, 0.45) 36%,
    rgba(255, 255, 255, 0) 54%
  );
  content: "";
  opacity: var(--glow-strength, 0);
  pointer-events: none;
  -webkit-mask:
    linear-gradient(#000 0 0) content-box,
    linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
  mask:
    linear-gradient(#000 0 0) content-box,
    linear-gradient(#000 0 0);
  mask-composite: exclude;
  transition: opacity 120ms ease;
}

/* Soft drop — the .glass-button-shadow equivalent. */
.word-carousel span::after,
.offer-word-carousel span::after {
  position: absolute;
  inset: 0;
  z-index: -1;
  border-radius: var(--bubble-radius);
  box-shadow: 0 0.42em 0.9em -0.3em rgba(17, 20, 17, 0.32);
  content: "";
  opacity: calc(0.35 + var(--wheel-presence) * 0.65);
  pointer-events: none;
}

/* Word sitting ON the glass: solid white (same as "Solutions" in dark) with
   a grayish 3D shadow dropping into the glass. Plain color fill — a clipped
   gradient would let text-shadow paint OVER the glyphs and darken them. */
.word-carousel span .bubble-text,
.offer-word-carousel span .bubble-text {
  position: relative;
  z-index: 2;
  display: inline-block;
  color: #ffffff;
  -webkit-text-fill-color: #ffffff;
  font-weight: inherit;
  text-shadow:
    0 0.018em 0.02em rgba(72, 80, 90, 0.55),
    0 0.05em 0.07em rgba(44, 52, 62, 0.3);
}

/* Liquid glass layer — same as the site header's: blur(3px) + distortion,
   so the shader lines frost and refract when they pass behind the pill. */
.word-carousel span .bubble-liquid-base,
.offer-word-carousel span .bubble-liquid-base {
  position: absolute;
  inset: 0;
  z-index: 0;
  display: block;
  overflow: hidden;
  border-radius: inherit;
  -webkit-backdrop-filter: blur(3px);
  backdrop-filter: blur(3px);
  filter: url(#glass-distortion);
  pointer-events: none;
}

/* Diagonal shine streak — the GlassButton hover sweep, driven here by the
   wheel: it slides across the bubble as the word rotates into focus. */
.word-carousel span .bubble-liquid-base::after,
.offer-word-carousel span .bubble-liquid-base::after {
  position: absolute;
  inset: 0;
  border-radius: inherit;
  background: linear-gradient(
    -50deg,
    transparent 0% 42%,
    rgba(255, 255, 255, 0.8) 50%,
    transparent 58% 100%
  );
  background-position: calc(88% - var(--wheel-front, 0) * 74%) 50%;
  background-size: 250% 250%;
  content: "";
  filter: blur(clamp(2px, 0.125em, 12px));
  mix-blend-mode: screen;
  opacity: calc(0.3 + var(--wheel-front, 0) * 0.32);
  pointer-events: none;
}

.solution-static {
  text-align: left;
}

.hero-intro p:last-child {
  max-width: 830px;
  margin: 0 auto;
  color: var(--muted);
  font-size: clamp(1rem, 1.6vw, 1.2rem);
  line-height: 1.65;
}

.orbit-showcase {
  position: relative;
  z-index: 2;
  min-height: 440svh;
  margin-top: -100svh;
  padding: 0;
}

.orbit-pin {
  position: sticky;
  top: 0;
  min-height: 100svh;
}

.orbit-scene {
  --orbit-reveal: 0;
  --orbit-copy-progress: 0;
  --offer-enter: 1;
  --offer-absorb: 0;
  --offer-shrink: 0;
  --offer-presence: 1;
  --offer-depth-front: 0;
  --carousel-presence: 0;
  --orbit-label-pop: 0;
  --orbit-label-scale: 0.74;
  --orbit-label-wide: 0;
  --orbit-label-text: 0;
  --orbit-label-warp-x: 1;
  --orbit-label-warp-y: 1;
  --orbit-controls-events: none;
  --orbit-label-x: -28px;
  --orbit-label-y: -8px;
  --label-float-x: 0px;
  --label-float-y: 0px;
  --label-drift-x: 0px;
  --label-drift-y: 0px;
  --orbit-bg-shift: 0px;
  --orbit-mid-shift: 0px;
  position: relative;
  min-height: 100svh;
  overflow: hidden;
  background: transparent;
  cursor: grab;
  isolation: isolate;
  touch-action: pan-y;
}

.orbit-scene:active {
  cursor: grabbing;
}

.orbit-canvas {
  position: absolute;
  inset: 0;
  z-index: 2;
  display: block;
  width: 100%;
  height: 100%;
  opacity: var(--carousel-presence);
  /* Entry: rises from the bottom and un-blurs as it fades in (presence 0→1).
     Both settle to identity once fully present, so nothing changes mid-use. */
  filter: blur(calc((1 - var(--carousel-presence)) * 14px));
  transform: translateY(calc((1 - var(--carousel-presence)) * 8vh));
  transition: opacity 120ms linear;
  /* No pointer listeners live on the canvas (drag is on .orbit-scene), so it
     must not intercept events meant for the CTA buttons beneath it. */
  pointer-events: none;
}

.orbit-dom-layer {
  position: absolute;
  inset: 0;
  z-index: 2;
  opacity: var(--carousel-presence);
  perspective: 920px;
  perspective-origin: 50% 50%;
  pointer-events: none;
  transform-style: preserve-3d;
  /* Ride the same entry rise as the canvas (NO filter here — a filter would
     flatten this layer's preserve-3d card transforms). */
  transform: translateY(calc((1 - var(--carousel-presence)) * 8vh));
  transition: opacity 120ms linear;
}

.orbit-dom-card {
  --card-radius: 48px;
  --media-inset: 24px;
  --glow-ring: 1.25px;
  --glow-size: 150px;
  position: absolute;
  left: 50%;
  top: 50%;
  width: 240px;
  height: 320px;
  overflow: hidden;
  border-radius: var(--card-radius);
  background:
    linear-gradient(
      90deg,
      rgba(255, 255, 255, 0.18) 0%,
      rgba(255, 255, 255, 0.045) 13%,
      rgba(255, 255, 255, 0.012) 50%,
      rgba(0, 0, 0, 0.13) 100%
    ),
    rgba(255, 255, 255, 0.03);
  backface-visibility: visible;
  box-shadow:
    inset 1.2em 0 1.8em -1.75em rgba(255, 255, 255, 0.74),
    inset -1.3em 0 1.9em -1.75em rgba(0, 0, 0, 0.42),
    inset 0 -0.09em 0.1em rgba(0, 0, 0, 0.05),
    inset 0 0.09em 0.1em rgba(255, 255, 255, 0.5),
    inset 0 0 0.04em 0.045em rgba(255, 255, 255, 0.4);
  opacity: 0;
  transform-style: preserve-3d;
  will-change: transform, opacity, width, height;
}

.orbit-dom-card > .bubble-liquid-base {
  border-radius: inherit;
  opacity: 1;
}

.orbit-dom-card-media {
  position: absolute;
  inset: var(--media-inset);
  z-index: 1;
  overflow: hidden;
  border-radius: max(18px, calc(var(--card-radius) - var(--media-inset) * 0.62));
  background: transparent;
  box-shadow:
    0 0 0 1px rgba(255, 255, 255, 0.22),
    inset 0 0 0 1px rgba(17, 20, 17, 0.1);
}

/* The collection as a control. It sits with the tag pills because that is
   where the panel already talks about the card at the front. */

/* The way out of the carousel and into the work itself. */
.orbit-seemore .glass-button-text { font-weight: 700; letter-spacing: 0.02em; }

.orbit-disc-pop {
  position: fixed;
  inset: 0;
  z-index: 400;
  display: grid;
  place-items: center;
  padding: 1.2rem;
  background: rgba(6, 8, 12, 0.55);
  -webkit-backdrop-filter: blur(8px);
  backdrop-filter: blur(8px);
}

.orbit-disc-pop[hidden] { display: none; }

.orbit-disc-card {
  max-width: 32rem;
  padding: 1.6rem 1.7rem;
  border: 1px solid rgba(255, 255, 255, 0.16);
  border-radius: 16px;
  background: rgba(18, 22, 30, 0.96);
  color: #f2f5fb;
  box-shadow: 0 24px 60px rgba(0, 0, 0, 0.45);
}

.orbit-disc-card h3 {
  margin: 0 0 0.6rem;
  font-size: 0.72rem;
  font-weight: 800;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: rgba(242, 245, 251, 0.62);
}

.orbit-disc-card p { margin: 0 0 1.2rem; font-size: 1rem; line-height: 1.6; }

.orbit-disc-card button {
  padding: 0.55rem 1.2rem;
  border: 1px solid rgba(255, 255, 255, 0.22);
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.1);
  color: #f2f5fb;
  font: inherit;
  font-weight: 650;
  cursor: pointer;
}

.orbit-disc-card button:hover { background: rgba(255, 255, 255, 0.18); }

/* The tapped card, opened. Sits on the same scrim as the disclaimer, one
   layer under it, so the collection chip inside can hand off without either
   panel fighting for the top. */
.orbit-card-pop {
  position: fixed;
  inset: 0;
  z-index: 390;
  display: grid;
  place-items: center;
  padding: 1.2rem;
  background: rgba(6, 8, 12, 0.62);
  -webkit-backdrop-filter: blur(8px);
  backdrop-filter: blur(8px);
}

.orbit-card-pop[hidden] { display: none; }

.orbit-card-panel {
  position: relative;
  width: min(52rem, 100%);
  max-height: min(86vh, 46rem);
  overflow: auto;
  border: 1px solid rgba(255, 255, 255, 0.16);
  border-radius: 18px;
  /* Opaque, not translucent: Safari repaints a backdrop-filter panel badly
     when a video is playing inside it. */
  background: #12161e;
  color: #f2f5fb;
  box-shadow: 0 28px 70px rgba(0, 0, 0, 0.55);
}

.orbit-card-close {
  position: absolute;
  top: 0.7rem;
  right: 0.8rem;
  z-index: 2;
  width: 2rem;
  height: 2rem;
  border: 0;
  border-radius: 999px;
  background: rgba(8, 10, 14, 0.62);
  color: #fff;
  font-size: 1.3rem;
  line-height: 1;
  cursor: pointer;
}

.orbit-card-close:hover { background: rgba(8, 10, 14, 0.9); }

.orbit-card-media {
  position: relative;
  aspect-ratio: 16 / 10;
  background: #06080c;
}

/* Whose work it is: a thin bar across the foot of the picture. On the picture
   rather than under it, because detached it stops reading as being about the
   picture - and full width rather than a chip, so it is a caption on the work
   instead of one more tag. */
.orbit-card-credit {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 3;
  padding: 0.42rem 0.9rem;
  border: 0;
  background: rgba(8, 10, 14, 0.72);
  -webkit-backdrop-filter: blur(4px);
  backdrop-filter: blur(4px);
  color: rgba(255, 255, 255, 0.92);
  font: inherit;
  font-size: 0.72rem;
  font-weight: 650;
  letter-spacing: 0.02em;
  text-align: left;
  cursor: pointer;
}

.orbit-card-credit:hover { background: rgba(0, 122, 255, 0.92); color: #fff; }

/* contain, not cover: the box is already the shape of the file, so there is
   nothing to crop away, and a file that reports an odd shape letterboxes
   instead of losing its edges. */
.orbit-card-media img,
.orbit-card-media video {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: contain;
}

/* A tall picture goes BESIDE its copy, not above it - the same shape the work
   page lightbox uses. Stacked, a reel pushed the title and blurb off the
   bottom of the screen and left the panel a narrow ribbon. */
.orbit-card-panel.is-portrait {
  display: flex;
  flex-direction: row;
  align-items: stretch;
  width: min(52rem, 100%);
  max-width: 100%;
}

.orbit-card-panel.is-portrait .orbit-card-media {
  flex: none;
  width: var(--card-media-w, 52%);
  aspect-ratio: auto;
}

.orbit-card-panel.is-portrait .orbit-card-media img,
.orbit-card-panel.is-portrait .orbit-card-media video {
  position: absolute;
  inset: 0;
}

.orbit-card-panel.is-portrait .orbit-card-body {
  flex: 1;
  min-width: 0;
  overflow-y: auto;
}

/* Side by side stops working long before a phone, so below that it stacks and
   the picture is capped by height instead. */
@media (max-width: 40rem) {
  .orbit-card-panel.is-portrait { flex-direction: column; }
  .orbit-card-panel.is-portrait .orbit-card-media {
    width: 100%;
    max-height: 58vh;
    aspect-ratio: var(--card-ar, 9 / 16);
  }
  .orbit-card-panel.is-portrait .orbit-card-media img,
  .orbit-card-panel.is-portrait .orbit-card-media video { position: static; }
}

/* The before/after wipe inside the card popup. */
.orbit-card-media.is-pair { cursor: ew-resize; }

/* pan-y, not none: a horizontal drag is the wipe and reaches the handler
   either way, but leaving vertical to the browser means the popup can still be
   scrolled from the picture instead of it being a dead zone. */
.orbit-pair { position: relative; width: 100%; height: 100%; overflow: hidden; touch-action: pan-y; }

.orbit-pair img { display: block; width: 100%; height: 100%; object-fit: cover; user-select: none; -webkit-user-drag: none; }

/* The raw half sits on top, clipped to the seam. */
/* Clipped, not resized. Narrowing the layer itself would squash the picture
   inside it as the seam moved; clip-path leaves it full size and simply hides
   the part past the seam, so both halves stay in register. */
.orbit-pair-before {
  position: absolute;
  inset: 0;
  clip-path: inset(0 calc(100% - var(--wipe, 50%)) 0 0);
}

/* A 2px line is not a touch target. */
.orbit-pair-line::before {
  content: "";
  position: absolute;
  top: 0;
  bottom: 0;
  left: 50%;
  width: 44px;
  transform: translateX(-50%);
  pointer-events: auto;
  touch-action: none;
}

.orbit-pair-line {
  position: absolute;
  top: 0;
  bottom: 0;
  left: var(--wipe, 50%);
  width: 2px;
  margin-left: -1px;
  background: rgba(255, 255, 255, 0.9);
  pointer-events: none;
}

.orbit-pair-grip {
  /* The knob's whole job is a drag, so it keeps the gesture outright. */
  touch-action: none;
  position: absolute;
  top: 50%;
  left: 50%;
  width: 2.1rem;
  height: 2.1rem;
  transform: translate(-50%, -50%);
  border: 2px solid rgba(255, 255, 255, 0.9);
  border-radius: 999px;
  background: rgba(8, 10, 14, 0.55);
  -webkit-backdrop-filter: blur(2px);
  backdrop-filter: blur(2px);
}

.orbit-pair-tag {
  position: absolute;
  /* Clear of the disclaimer bar, which owns the very bottom. */
  bottom: 2.1rem;
  padding: 0.22rem 0.6rem;
  border-radius: 999px;
  background: rgba(8, 10, 14, 0.62);
  font-size: 0.66rem;
  font-weight: 800;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: #fff;
  pointer-events: none;
}

.orbit-pair-tag.is-b { left: 0.6rem; }
.orbit-pair-tag.is-a { right: 0.6rem; }

.orbit-card-body { padding: 1.3rem 1.5rem 1.6rem; }

.orbit-card-kicker {
  margin: 0 0 0.35rem;
  font-size: 0.7rem;
  font-weight: 800;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: rgba(242, 245, 251, 0.6);
}

.orbit-card-title { margin: 0 0 0.6rem; font-size: 1.85rem; line-height: 1.1; }
.orbit-card-desc { margin: 0 0 1rem; font-size: 1rem; line-height: 1.6; color: rgba(242, 245, 251, 0.82); }

.orbit-card-tags { display: flex; flex-wrap: wrap; gap: 0.45rem; margin-bottom: 1.2rem; }

.orbit-card-tag {
  padding: 0.32rem 0.7rem;
  border: 1px solid rgba(255, 255, 255, 0.16);
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.07);
  font-size: 0.78rem;
  font-weight: 600;
  color: rgba(242, 245, 251, 0.9);
}

/* The collection chip is the way into the disclaimer, so it is a button and
   lights blue like every other one on the site. */
button.orbit-card-tag.is-collection { font: inherit; font-size: 0.78rem; font-weight: 650; cursor: pointer; }
button.orbit-card-tag.is-collection:hover { border-color: rgba(0, 122, 255, 0.9); background: rgba(0, 122, 255, 0.92); color: #fff; }

.orbit-card-more {
  display: inline-block;
  padding: 0.6rem 1.3rem;
  border: 1px solid rgba(255, 255, 255, 0.22);
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.1);
  color: #f2f5fb;
  font-weight: 650;
  text-decoration: none;
}

.orbit-card-more:hover { border-color: rgba(0, 122, 255, 0.9); background: rgba(0, 122, 255, 0.92); color: #fff; }

/* Whose work the picture is. The WebGL path paints this into the texture;
   this is the DOM fallback wearing the same label. */
.orbit-dom-card-collection {
  position: absolute;
  left: 0.6rem;
  bottom: 0.5rem;
  z-index: 2;
  color: rgba(255, 245, 244, 0.9);
  /* Matches the canvas label exactly - see drawOrbitMediaCollection. The
     page loads only Inter, so a webfont named here would never arrive. */
  font-family: ui-monospace, "SF Mono", Menlo, monospace;
  font-size: 0.58rem;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  text-shadow: 0 1px 6px rgba(0, 0, 0, 0.8);
  pointer-events: none;
}

.orbit-dom-card:not(.has-media) .orbit-dom-card-media {
  display: none;
}

.orbit-dom-card-media img,
.orbit-dom-card-media video {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* ---- Falling-pattern background (ported from a 21st.dev React/framer-motion
   component to pure CSS). SITE-WIDE: site-header.js prepends this fixed layer
   to <body> on every page; z-index -1 keeps all content above it. 36 stacked
   radial-gradients — thin 100px streaks plus a small dot per tile — drift
   downward forever; the layer paints its OWN opaque background and the
   .falling-pattern-veil backdrop-blurs the streaks through a fine dot-grid,
   which melts them into the soft halftone BLOBS of the reference (not crisp
   lines). Monochrome: white-on-near-black in dark mode (the reference photo),
   inverted soft-black-on-white in light mode so text stays readable. ---- */
/* The body's own opaque background paints OVER negative-z fixed children
   (body isn't a stacking context, so its background lands in a later paint
   step than the root's negative-z layer). The pattern carries the page
   background itself (--fall-bg), so body must go transparent wherever the
   pattern is injected — !important beats each sub-page's inline body rule. */
body:has(> .falling-pattern) {
  background: transparent !important;
}

.falling-pattern {
  /* Light mode = the reference photo inverted: soft near-black blobs on
     white. The veil grid must be SEMI-transparent here — an opaque white
     mask erases dark-on-white streaks entirely (no glow to punch through,
     unlike white-on-black in dark mode). */
  --fall-color: rgba(10, 12, 10, 1);
  --fall-bg: #ffffff;
  --fall-veil: rgba(255, 255, 255, 0.52);
  --fall-hole: 2.2px; /* dot-grid hole radius — bigger = more blob shows */
  position: fixed;
  inset: 0;
  z-index: -1;
  overflow: hidden;
  pointer-events: none;
  background-color: var(--fall-bg);
  background-image:
    radial-gradient(4px 100px at 0px 235px, var(--fall-color), transparent),
    radial-gradient(4px 100px at 300px 235px, var(--fall-color), transparent),
    radial-gradient(1.5px 1.5px at 150px 117.5px, var(--fall-color) 100%, transparent 150%),
    radial-gradient(4px 100px at 0px 252px, var(--fall-color), transparent),
    radial-gradient(4px 100px at 300px 252px, var(--fall-color), transparent),
    radial-gradient(1.5px 1.5px at 150px 126px, var(--fall-color) 100%, transparent 150%),
    radial-gradient(4px 100px at 0px 150px, var(--fall-color), transparent),
    radial-gradient(4px 100px at 300px 150px, var(--fall-color), transparent),
    radial-gradient(1.5px 1.5px at 150px 75px, var(--fall-color) 100%, transparent 150%),
    radial-gradient(4px 100px at 0px 253px, var(--fall-color), transparent),
    radial-gradient(4px 100px at 300px 253px, var(--fall-color), transparent),
    radial-gradient(1.5px 1.5px at 150px 126.5px, var(--fall-color) 100%, transparent 150%),
    radial-gradient(4px 100px at 0px 204px, var(--fall-color), transparent),
    radial-gradient(4px 100px at 300px 204px, var(--fall-color), transparent),
    radial-gradient(1.5px 1.5px at 150px 102px, var(--fall-color) 100%, transparent 150%),
    radial-gradient(4px 100px at 0px 134px, var(--fall-color), transparent),
    radial-gradient(4px 100px at 300px 134px, var(--fall-color), transparent),
    radial-gradient(1.5px 1.5px at 150px 67px, var(--fall-color) 100%, transparent 150%),
    radial-gradient(4px 100px at 0px 179px, var(--fall-color), transparent),
    radial-gradient(4px 100px at 300px 179px, var(--fall-color), transparent),
    radial-gradient(1.5px 1.5px at 150px 89.5px, var(--fall-color) 100%, transparent 150%),
    radial-gradient(4px 100px at 0px 299px, var(--fall-color), transparent),
    radial-gradient(4px 100px at 300px 299px, var(--fall-color), transparent),
    radial-gradient(1.5px 1.5px at 150px 149.5px, var(--fall-color) 100%, transparent 150%),
    radial-gradient(4px 100px at 0px 215px, var(--fall-color), transparent),
    radial-gradient(4px 100px at 300px 215px, var(--fall-color), transparent),
    radial-gradient(1.5px 1.5px at 150px 107.5px, var(--fall-color) 100%, transparent 150%),
    radial-gradient(4px 100px at 0px 281px, var(--fall-color), transparent),
    radial-gradient(4px 100px at 300px 281px, var(--fall-color), transparent),
    radial-gradient(1.5px 1.5px at 150px 140.5px, var(--fall-color) 100%, transparent 150%),
    radial-gradient(4px 100px at 0px 158px, var(--fall-color), transparent),
    radial-gradient(4px 100px at 300px 158px, var(--fall-color), transparent),
    radial-gradient(1.5px 1.5px at 150px 79px, var(--fall-color) 100%, transparent 150%),
    radial-gradient(4px 100px at 0px 210px, var(--fall-color), transparent),
    radial-gradient(4px 100px at 300px 210px, var(--fall-color), transparent),
    radial-gradient(1.5px 1.5px at 150px 105px, var(--fall-color) 100%, transparent 150%);
  background-size:
    300px 235px, 300px 235px, 300px 235px,
    300px 252px, 300px 252px, 300px 252px,
    300px 150px, 300px 150px, 300px 150px,
    300px 253px, 300px 253px, 300px 253px,
    300px 204px, 300px 204px, 300px 204px,
    300px 134px, 300px 134px, 300px 134px,
    300px 179px, 300px 179px, 300px 179px,
    300px 299px, 300px 299px, 300px 299px,
    300px 215px, 300px 215px, 300px 215px,
    300px 281px, 300px 281px, 300px 281px,
    300px 158px, 300px 158px, 300px 158px,
    300px 210px, 300px 210px, 300px 210px;
  background-position:
    0px 220px, 3px 220px, 151.5px 337.5px, 25px 24px, 28px 24px, 176.5px 150px,
    50px 16px, 53px 16px, 201.5px 91px, 75px 224px, 78px 224px, 226.5px 230.5px,
    100px 19px, 103px 19px, 251.5px 121px, 125px 120px, 128px 120px, 276.5px 187px,
    150px 31px, 153px 31px, 301.5px 120.5px, 175px 235px, 178px 235px, 326.5px 384.5px,
    200px 121px, 203px 121px, 351.5px 228.5px, 225px 224px, 228px 224px, 376.5px 364.5px,
    250px 26px, 253px 26px, 401.5px 105px, 275px 75px, 278px 75px, 426.5px 180px;
  animation: falling-drift 150s linear infinite;
}

html.theme-dark .falling-pattern {
  --fall-color: rgba(255, 255, 255, 1);
  --fall-bg: #050607;
  --fall-veil: #050607; /* opaque, exactly like the reference photo */
  --fall-hole: 3px; /* wider glow holes = brighter, more prominent blobs */
}

@keyframes falling-drift {
  to {
    background-position:
      0px 6800px, 3px 6800px, 151.5px 6917.5px, 25px 13632px, 28px 13632px, 176.5px 13758px,
      50px 5416px, 53px 5416px, 201.5px 5491px, 75px 17175px, 78px 17175px, 226.5px 17301.5px,
      100px 5119px, 103px 5119px, 251.5px 5221px, 125px 8428px, 128px 8428px, 276.5px 8495px,
      150px 9876px, 153px 9876px, 301.5px 9965.5px, 175px 13391px, 178px 13391px, 326.5px 13540.5px,
      200px 14741px, 203px 14741px, 351.5px 14848.5px, 225px 18770px, 228px 18770px, 376.5px 18910.5px,
      250px 5082px, 253px 5082px, 401.5px 5161px, 275px 6375px, 278px 6375px, 426.5px 6480px;
  }
}

/* The blurred dot-grid veil that turns the raw streaks into soft glow. */
.falling-pattern-veil {
  position: absolute;
  inset: 0;
  -webkit-backdrop-filter: blur(1em);
  backdrop-filter: blur(1em);
  background-image: radial-gradient(circle at 50% 50%, transparent 0, transparent var(--fall-hole, 2px), var(--fall-veil) var(--fall-hole, 2px));
  background-size: 8px 8px;
}

@media (prefers-reduced-motion: reduce) {
  .falling-pattern { animation: none; }
}

/* ---- Landing-page layered zone background (built by zone-bg.js) ----
   Fixed stack behind all content: sky (base) → sea → underwater → lava,
   each upper layer's opacity scroll-driven for a true crossfade. The
   body:has rule mirrors the falling-pattern fix: body's own opaque
   background otherwise paints OVER negative-z fixed children. */
body:has(> .zone-bg) {
  background: transparent !important;
}

.zone-bg {
  position: fixed;
  inset: 0;
  z-index: -1;
  overflow: hidden;
  pointer-events: none;
}

.zone-layer {
  position: absolute;
  inset: 0;
  contain: layout paint style;
}

/* Every zone below the first rises up from the bottom; its top edge is
   feathered so it dissolves into the zone it's replacing while both travel
   (--zone-seam is set by zone-bg.js to match its parked-overlap math). */
.zone-layer.zone-below {
  -webkit-mask-image: linear-gradient(to bottom, transparent 0, #000 var(--zone-seam, 14%));
  mask-image: linear-gradient(to bottom, transparent 0, #000 var(--zone-seam, 14%));
}

/* The art oversized slightly so drift/shimmer never expose edges. */
.zone-art {
  position: absolute;
  inset: -4%;
  background-position: center;
  background-size: cover;
}

.zone-art video {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* During scroll-scrubbed scenery, favor motion clarity over live refraction.
   The glass treatment returns automatically in every resting band — and it
   EASES back in (the resting-state transition below) instead of snapping.
   Dropping the refraction when a scrub starts stays instant (transition:none
   in the scrubbing rule) so fast scrolls never pay for a fade-out. The genie
   lockup is excluded from the ease: its filter is recomputed every scroll
   frame, and a transition there would fight the scrub itself. */
.word-carousel span .bubble-liquid-base,
.offer-word-carousel span .bubble-liquid-base,
.orbit-copy-panel .bubble-liquid-base {
  transition:
    backdrop-filter 520ms ease,
    -webkit-backdrop-filter 520ms ease,
    filter 520ms ease;
}

/* (.ecosystem-dom-liquid-base no longer carries filters at all — see its
   base rule — so it needs no scrub exemption or eased return.) */
html.zone-is-scrubbing .offer-solution-lockup.is-genie-warp,
html.zone-is-scrubbing .word-carousel span .bubble-liquid-base,
html.zone-is-scrubbing .offer-word-carousel span .bubble-liquid-base,
html.zone-is-scrubbing .orbit-copy-panel .bubble-liquid-base {
  -webkit-backdrop-filter: none;
  backdrop-filter: none;
  filter: none;
  transition: none;
}

/* Idle motion hooks (cheap transforms only). */
.zone-motion-drift {
  animation: zone-drift 90s ease-in-out infinite alternate;
}

.zone-motion-shimmer {
  animation: zone-shimmer 7s ease-in-out infinite;
}

@keyframes zone-drift {
  from { transform: translateX(-1.6%); }
  to { transform: translateX(1.6%); }
}

@keyframes zone-shimmer {
  0%, 100% { transform: scale(1); filter: brightness(1); }
  50% { transform: scale(1.015); filter: brightness(1.12); }
}

/* Scenery reads too bright against dark-mode UI — dim it slightly. */
html.theme-dark .zone-bg {
  filter: brightness(0.72) saturate(0.9);
}

@media (prefers-reduced-motion: reduce) {
  .zone-motion-drift,
  .zone-motion-shimmer {
    animation: none;
  }
}

.orbit-depth {
  position: absolute;
  inset: -18vh -10vw;
  pointer-events: none;
  transition: opacity 160ms linear;
  will-change: transform;
}

.orbit-depth-back {
  z-index: 0;
  opacity: 0;
  background: none;
  transform: translate3d(0, calc(var(--orbit-bg-shift) * -0.35), 0) scale(1.08);
}

.orbit-depth-mid {
  z-index: 0;
  opacity: 0;
  background: none;
  transform: translate3d(0, calc(var(--orbit-mid-shift) * -0.7), 0) scale(1.14);
}

.orbit-vignette {
  position: absolute;
  inset: 0;
  z-index: 3;
  pointer-events: none;
  background: none;
}

.offer-bridge-copy {
  position: absolute;
  z-index: 1;
  inset: 0;
  display: grid;
  place-items: center;
  pointer-events: none;
  opacity: var(--offer-presence);
  filter: blur(calc(var(--offer-absorb) * 18px));
  transform:
    translate3d(
      0,
      calc(((1 - var(--offer-enter)) * 40vh) + (var(--offer-absorb) * 28vh)),
      0
    )
    scale(calc(1.06 - (var(--offer-shrink) * 0.62)));
  transform-origin: center;
  will-change: opacity, filter, transform;
}

.offer-bridge-copy-back {
  z-index: 1;
}

.offer-bridge-copy-front {
  z-index: 3;
  opacity: calc(var(--offer-presence) * var(--offer-depth-front) * 0.82);
  filter: blur(calc(var(--offer-absorb) * 10px));
  -webkit-mask-image:
    radial-gradient(
      ellipse 42% 28% at 50% 50%,
      transparent 0 45%,
      rgba(0, 0, 0, 0.22) 58%,
      #000 76%
    );
  mask-image:
    radial-gradient(
      ellipse 42% 28% at 50% 50%,
      transparent 0 45%,
      rgba(0, 0, 0, 0.22) 58%,
      #000 76%
    );
}

.offer-solution-lockup {
  display: flex;
  width: min(calc(100vw - clamp(2rem, 6vw, 7rem)), 1180px);
  align-items: center;
  justify-content: center;
  gap: clamp(0.75rem, 1.8vw, 1.8rem);
  color: rgba(17, 20, 17, 0.9);
  font-size: clamp(2.8rem, 7.5vw, 7.5rem);
  font-weight: 800;
  line-height: 0.92;
  /* Genie collapse into a BOTTOM squeeze point that recedes into depth (back
     toward the ring behind), so the whole lockup gets sucked back + down into
     a point rather than tilting forward. transform-origin at the bottom pins
     the squeeze point there; the top collapses DOWN into it (scaleY), it necks
     horizontally (scaleX), and translateZ pushes it back into the ring while
     translateY drops it toward the dock. --g is a quartic ease-out: necks hard
     early, then settles. (Applies ONLY to the words, never the shader bg.) */
  --a: var(--offer-absorb, 0);
  --g: calc(1 - (1 - var(--a)) * (1 - var(--a)) * (1 - var(--a)) * (1 - var(--a)));
  transform:
    perspective(1000px)
    translateY(calc(var(--g) * 13vh))
    translateZ(calc(var(--g) * -460px))
    scaleX(calc(1 - (var(--g) * 0.6)))
    scaleY(calc(1 - (var(--g) * 0.42)));
  transform-origin: 50% 100%;
  filter:
    drop-shadow(0 calc(var(--g) * 8px) calc(var(--g) * 34px)
      rgba(120, 175, 255, calc(var(--g) * 0.9)))
    brightness(calc(1 + (var(--g) * 0.5)));
  will-change: transform, filter;
}

/* While it's compressing, script.js adds .is-genie-warp and ramps the
   #genie-warp displacement scale with the scroll, so the lockup ripples like
   liquid being pulled down instead of scaling rigidly. The class is only on
   mid-transition so the turbulence filter isn't rasterizing at the hero top. */
.offer-solution-lockup.is-genie-warp {
  filter:
    url(#genie-warp)
    drop-shadow(0 calc(var(--g) * 8px) calc(var(--g) * 34px)
      rgba(120, 175, 255, calc(var(--g) * 0.9)))
    brightness(calc(1 + (var(--g) * 0.5)));
}

.offer-solution-lockup .offer-word-carousel {
  flex-basis: clamp(20rem, 52vw, 58rem);
  width: clamp(20rem, 52vw, 58rem);
  height: clamp(15rem, 29vw, 27rem);
}

.offer-solution-lockup .offer-word-carousel span {
  width: calc(min(calc(100% - 3.5rem), clamp(22rem, 52vw, 54rem)) + var(--bubble-extra-current));
}

.offer-solution-static {
  color: #111411;
  text-align: left;
}

.solution-stack {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 0;
}

/* ---- "Solutions" × peak intersection ----
   Two stacked copies of the word: the FILL is clipped to the sky, the
   OUTLINE (transparent letters, hairline stroke) is clipped to the rock —
   so wherever the summit rises behind the word, only a border of each
   letter remains and the mountain reads as standing in front of the type.
   script.js maps the hand-traced ridge through the live video's cover box
   and drives both clip-paths. Without JS, the fill simply covers the
   outline and the word looks like a normal solid. */
.offer-solution-static {
  position: relative;
  display: inline-block;
}

.offer-solution-static .solution-fill {
  position: relative;
  z-index: 1;
  display: inline-block;
}

.offer-solution-static .solution-outline {
  /* MOBILE-ONLY effect — hidden here; the 840px block turns it on and
     script.js only drives the clips at compact widths. */
  position: absolute;
  inset: 0;
  z-index: 0;
  display: none;
  color: transparent;
  -webkit-text-stroke: 0.022em rgba(255, 255, 255, 0.95);
  text-shadow: none;
  pointer-events: none;
}

/* Glass CTA buttons under "Solutions". They live inside the bridge lockup so
   they track the "Solutions" text (position + scroll scale) exactly, and the
   glass samples the shader behind them just like the word bubbles. */
.hero-cta {
  /* Absolute so the buttons hang below "Solutions" without pushing it up —
     keeps Solutions centered on the focused-bubble line. */
  position: absolute;
  top: 100%;
  left: 0;
  display: flex;
  gap: clamp(0.55rem, 0.9vw, 1rem);
  margin-top: clamp(0.7rem, 1.3vw, 1.4rem);
  /* Reset the giant lockup font so the buttons are a sane size (they still
     scale with the bridge transform, staying proportional to Solutions). */
  font-size: clamp(1rem, 1.55vw, 1.45rem);
  pointer-events: none;
}

.glass-button-wrap {
  position: relative;
  display: inline-flex;
  border-radius: 999px;
  pointer-events: auto;
}

/* Glass buttons can also be links (e.g. the media carousel tag pills). */
a.glass-button {
  display: inline-block;
  color: rgba(17, 20, 17, 0.92);
  text-decoration: none;
}

/* Tag pills under the media carousel copy — same CTA glass buttons, smaller. */
.orbit-tags {
  position: relative;
  z-index: 1;
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  margin-top: 0.4rem;
  font-size: clamp(0.78rem, 0.95vw, 0.95rem);
  opacity: var(--orbit-copy-progress, 1);
  pointer-events: var(--orbit-controls-events, auto);
  transition:
    opacity 180ms ease,
    filter 180ms ease;
}

.orbit-tags .glass-button-wrap {
  pointer-events: auto;
}

/* Undo the .orbit-copy span kicker styling that would leak onto the pills. */
.orbit-tags .glass-button-text,
.orbit-tags .glass-button-glass {
  width: auto;
  color: rgba(17, 20, 17, 0.92);
  font-size: 1em;
  font-weight: 600;
  letter-spacing: -0.012em;
  text-transform: none;
}

html.theme-dark .orbit-tags .glass-button-text {
  color: rgba(247, 249, 255, 0.95);
}

.glass-button {
  --glow-strength: 0;
  --glow-local-x: -9999px;
  --glow-local-y: -9999px;
  --glow-ring: 0.5px;
  --glow-size: 90px;
  position: relative;
  margin: 0;
  padding: 0;
  border: none;
  border-radius: 999px;
  /* Light tint over the .glass-button-glass distortion layer. Goes transparent
     on hover so the sharp background shows through. */
  background: linear-gradient(
    -75deg,
    rgba(255, 255, 255, 0.07),
    rgba(255, 255, 255, 0.22),
    rgba(255, 255, 255, 0.07)
  );
  box-shadow:
    inset 0 0.09em 0.1em rgba(0, 0, 0, 0.05),
    inset 0 -0.09em 0.1em rgba(255, 255, 255, 0.5),
    inset 0 0 0.04em 0.045em rgba(255, 255, 255, 0.4);
  color: rgba(17, 20, 17, 0.92);
  font: inherit;
  cursor: pointer;
  transition:
    transform 0.28s cubic-bezier(0.2, 0.9, 0.3, 1.25),
    box-shadow 0.28s ease,
    background 0.28s ease,
    -webkit-backdrop-filter 0.35s ease,
    backdrop-filter 0.35s ease;
}

/* Cursor-following hairline glow — same masked white ring as the ecosystem
   cards, driven by --glow-* vars from updateButtonGlow(). */
.glass-button::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 2;
  padding: var(--glow-ring, 1px);
  border-radius: inherit;
  background: radial-gradient(
    circle var(--glow-size, 90px) at var(--glow-local-x, -9999px) var(--glow-local-y, -9999px),
    rgba(255, 255, 255, 0.98) 0%,
    rgba(255, 255, 255, 0.85) 20%,
    rgba(255, 255, 255, 0.45) 36%,
    rgba(255, 255, 255, 0) 54%
  );
  opacity: var(--glow-strength, 0);
  pointer-events: none;
  -webkit-mask:
    linear-gradient(#000 0 0) content-box,
    linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
  mask:
    linear-gradient(#000 0 0) content-box,
    linear-gradient(#000 0 0);
  mask-composite: exclude;
  transition: opacity 120ms ease;
}

/* Liquid-glass layer (21st.dev recipe): blurs + refracts the shader behind the
   button at rest; clears on hover so the background sharpens. */
.glass-button-glass {
  position: absolute;
  inset: 0;
  z-index: 0;
  border-radius: inherit;
  overflow: hidden;
  -webkit-backdrop-filter: blur(3px);
  backdrop-filter: blur(3px);
  filter: url(#glass-distortion);
  opacity: 1;
  pointer-events: none;
  /* Fade the whole frosted+refracted layer out on hover (rather than animating
     the blur while the distortion lingers, which caused the end-state snap). */
  transition: opacity 0.4s ease;
}

.glass-button:hover .glass-button-glass {
  opacity: 0;
}

.glass-button-text {
  position: relative;
  z-index: 1;
  display: block;
  padding: 0.62em 1.4em 0.7em;
  font-weight: 600;
  font-size: 1em;
  letter-spacing: -0.012em;
  line-height: 1;
  white-space: nowrap;
  transition: transform 0.32s cubic-bezier(0.4, 0, 0.2, 1);
}

.glass-button-shadow {
  position: absolute;
  inset: 0;
  z-index: -1;
  border-radius: 999px;
  box-shadow: 0 0.42em 0.9em -0.3em rgba(17, 20, 17, 0.32);
  transition: box-shadow 0.28s ease;
}

.glass-button:hover {
  /* Tint clears so the sharp (un-blurred) background shows through. */
  background: rgba(255, 255, 255, 0.03);
  /* Same rim thickness — the bright edge just slides from bottom to top. */
  box-shadow:
    inset 0 -0.09em 0.1em rgba(0, 0, 0, 0.05),
    inset 0 0.09em 0.1em rgba(255, 255, 255, 0.5),
    inset 0 0 0.04em 0.045em rgba(255, 255, 255, 0.4);
}

.glass-button:hover .glass-button-text {
  transform: scale(0.94) translateY(0.07em);
}

.glass-button:active {
  transform: translateY(0) scale(0.975);
}

.glass-button:focus-visible {
  outline: 2px solid rgba(0, 122, 255, 0.7);
  outline-offset: 3px;
}

.orbit-section-label {
  position: absolute;
  /* Vertically centered on the header bubbles' axis: topbar sits at 0.9rem,
     the hamburger/logo bubbles are 3.3rem tall (center = 2.55rem), and the
     pill is 1.08em tall — so its top is that center minus half its height. */
  top: calc(2.55rem - 0.54em);
  /* Centered at the top of the page. */
  left: 50%;
  z-index: 4;
  display: inline-grid;
  /* Sized for "Media Solutions" — collapsed circle → wide pill. */
  width: calc(1.08em + (7.6em * var(--orbit-label-wide)));
  height: 1.08em;
  margin-left: calc((1.08em + (7.6em * var(--orbit-label-wide))) / -2);
  place-items: center;
  overflow: hidden;
  padding: 0;
  border: none;
  border-radius: 999px;
  /* Same pill as the wheel bubbles: clear glass, bright hairline on top. */
  background: rgba(255, 255, 255, 0.03);
  box-shadow:
    inset 0 -0.09em 0.1em rgba(0, 0, 0, 0.05),
    inset 0 0.09em 0.1em rgba(255, 255, 255, 0.5),
    inset 0 0 0.04em 0.045em rgba(255, 255, 255, 0.4);
  color: rgba(255, 255, 255, var(--orbit-label-text));
  -webkit-text-fill-color: rgba(255, 255, 255, var(--orbit-label-text));
  text-shadow:
    0 0.018em 0.02em rgba(72, 80, 90, calc(0.55 * var(--orbit-label-text))),
    0 0.05em 0.07em rgba(44, 52, 62, calc(0.3 * var(--orbit-label-text)));
  font-size: clamp(1.9rem, 4.2vw, 4.4rem);
  font-weight: 800;
  letter-spacing: 0;
  line-height: 0.86;
  opacity: var(--orbit-label-pop);
  pointer-events: var(--orbit-label-events, auto);
  text-transform: none;
  cursor: pointer;
  transform:
    translate3d(
      calc(var(--orbit-label-x) + var(--label-float-x) + var(--label-drift-x)),
      calc(var(--orbit-label-y) + var(--label-float-y) + var(--label-drift-y)),
      0
    )
    scale(var(--orbit-label-scale))
    scaleX(var(--orbit-label-warp-x))
    scaleY(var(--orbit-label-warp-y));
  transform-origin: center center;
  transition:
    border-color 180ms ease,
    background 180ms ease,
    box-shadow 180ms ease,
    color 180ms ease,
    -webkit-text-fill-color 180ms ease,
    opacity 160ms ease,
    transform 260ms cubic-bezier(0.2, 1.35, 0.28, 1);
  will-change: opacity, transform;
}

.orbit-section-label span {
  display: block;
  pointer-events: none;
  white-space: nowrap;
  transform: scale(0.82);
  transform-origin: center;
}

/* Subtle hover — it links to the Our Work page. */
.orbit-section-label:hover {
  background: rgba(255, 255, 255, 0.07);
  box-shadow:
    inset 0 -0.09em 0.1em rgba(0, 0, 0, 0.05),
    inset 0 0.09em 0.1em rgba(255, 255, 255, 0.68),
    inset 0 0 0.04em 0.045em rgba(255, 255, 255, 0.56);
}

/* Soft drop — same as the wheel bubbles' shadow layer. */
.orbit-section-label::after {
  position: absolute;
  inset: 0;
  z-index: -1;
  border-radius: inherit;
  box-shadow: 0 0.42em 0.9em -0.3em rgba(17, 20, 17, 0.32);
  content: "";
  opacity: calc(0.6 * var(--orbit-label-pop, 0));
  pointer-events: none;
}

.orbit-copy {
  position: absolute;
  /* Above the arrows so long titles are never covered by the controls. */
  z-index: 6;
  display: grid;
  gap: 0.75rem;
  opacity: var(--orbit-copy-progress);
  pointer-events: none;
  transition:
    opacity 240ms ease,
    transform 240ms ease;
}

.orbit-copy-content {
  position: relative;
  z-index: 2;
  display: grid;
  width: 100%;
  min-width: 0;
  max-width: 100%;
  gap: 0.75rem;
  transition:
    opacity 180ms ease,
    filter 180ms ease;
}

/* One continuous, untinted liquid-glass panel. The layout box stays stable so
   copy never clips; JS only contours the two material layers, keeping the left
   edge fixed while the right edge flows around the rendered text lines. */
.orbit-copy-panel {
  --orbit-copy-radius: 18px;
  position: relative;
  z-index: 0;
  display: inline-grid;
  width: 100%;
  max-width: 100%;
  padding: 0.72rem 1rem 0.92rem;
  isolation: isolate;
  overflow: hidden; /* clips cleanly while the height morphs between copies */
  border-radius: var(--orbit-copy-radius);
  background: transparent;
  box-shadow: none;
  pointer-events: none;
}

/* The milk tint lives on the CLIPPED surface layer (not the panel itself),
   so the frost follows the staircase contour instead of painting the full
   rectangle behind it. The panel element stays transparent — its box also
   holds the (unclipped) text, so it must never carry visible material. */
body.landing-page .orbit-copy-panel.glass-bubble {
  background: transparent;
  box-shadow: none;
}

body.landing-page .orbit-copy-panel__surface {
  background: var(--landing-glass-tint);
}

/* The shared glass-bubble ring is rectangular. The SVG path below replaces it
   with the exact same smooth contour as the clipped refractive layers. */
body.landing-page .orbit-copy-panel.glass-bubble::before,
body.landing-page .orbit-copy-panel.glass-bubble::after {
  display: none;
}

/* Clip wrapper for the material layers. Backdrop-filter is only clipped when
   the blur is a CHILD and the clip-path lives on the PARENT (a same-element
   clip is ignored by Chrome), so this node — which carries NO filter of its
   own — is what the staircase clip is applied to. Both the blurred liquid base
   and the tint surface ride inside it and inherit the stepped silhouette. */
.orbit-copy-panel__clip {
  position: absolute;
  inset: 0;
  z-index: 0;
  border-radius: inherit;
  pointer-events: none;
}

.orbit-copy-panel .bubble-liquid-base {
  z-index: 0;
  border-radius: inherit;
}

.orbit-copy-panel__surface {
  position: absolute;
  inset: 0;
  z-index: 1;
  display: block;
  border-radius: inherit;
  background: transparent;
  box-shadow:
    inset 0 -0.09em 0.1em rgba(0, 0, 0, 0.05),
    inset 0 0.09em 0.1em rgba(255, 255, 255, 0.5),
    inset 0 0 0.04em 0.045em rgba(255, 255, 255, 0.4);
  pointer-events: none;
}

.orbit-copy-panel__rim {
  position: absolute;
  inset: 0;
  z-index: 3;
  display: block;
  width: 100%;
  height: 100%;
  overflow: visible;
  opacity: 0;
  pointer-events: none;
}

.orbit-copy-panel__rim path {
  fill: none;
  stroke: rgba(255, 255, 255, 0.5);
  stroke-width: 1;
  vector-effect: non-scaling-stroke;
  filter:
    drop-shadow(0 1px 1px rgba(255, 255, 255, 0.2))
    drop-shadow(0 7px 10px rgba(2, 7, 12, 0.2));
  shape-rendering: geometricPrecision;
}

/* The staircase is now cut per material layer with an SVG mask (set in JS),
   not a clip-path on this wrapper — a parent clip-path isolates the child's
   backdrop-filter and the glass goes invisible. The wrapper is just a passive
   container now (no clip, so it never isolates the blur). */
.orbit-copy-panel.is-contour-ready .bubble-liquid-base,
.orbit-copy-panel.is-contour-ready .orbit-copy-panel__surface {
  border-radius: 0;
}

/* The distortion filter STAYS on the liquid base — it's what makes the frost
   read as refractive glass. It no longer breaks the clip because the staircase
   clip now lives on the parent wrapper (.orbit-copy-panel__clip), which clips
   the child's whole composited output (blur + distortion) to the steps. */

.orbit-copy-panel.is-contour-ready .orbit-copy-panel__rim {
  opacity: 1;
  transition: opacity 160ms ease;
}

.orbit-copy-panel.is-contour-ready .orbit-copy-panel__rim path {
  transition: d 320ms cubic-bezier(0.22, 1, 0.36, 1);
}

.orbit-copy-primary {
  left: clamp(1rem, 5vw, 5rem);
  /* Hugs the bottom edge so the copy blocks as little scenery as possible;
     the tag buttons ride ABOVE the panel (order flip below). */
  bottom: clamp(1.1rem, 2.6vh, 2.2rem);
  width: min(38rem, 84vw);
  max-width: min(38rem, 84vw);
  transform:
    translateX(calc((1 - var(--orbit-copy-progress)) * -3rem))
    translateY(calc((1 - var(--orbit-copy-progress)) * 0.8rem));
}

/* Related-work pills sit on top of the panel instead of dangling below it. */
.orbit-copy-primary > .orbit-tags {
  order: -1;
  margin-top: 0;
}

/* ---- settle gating: section text/headers only show when the viewer is
   actually parked IN that segment. zone-bg.js drives --settle-media /
   --settle-eco / --settle-detail (1 at a rest point → 0 while traveling),
   and each text layer multiplies its own engine-driven opacity by it. So
   copy melts away as soon as you scroll off a rest, and headers no longer
   pop in ahead of the guide. Defaults of 1 keep non-landing pages intact. */
body.landing-page .orbit-copy-primary {
  opacity: calc(var(--orbit-copy-progress, 0) * var(--settle-media, 1));
}

body.landing-page .orbit-controls-panel {
  /* settle² — the arrows spend almost no time half-faded (a translucent
     arrow over the bright scenery read as a stuck hover state); they
     materialize decisively right as the section lands. */
  opacity: calc(
    var(--orbit-copy-progress, 0)
    * var(--settle-media, 1) * var(--settle-media, 1)
  );
}

body.landing-page .orbit-section-label {
  opacity: var(--settle-media, 1);
}

body.landing-page .ecosystem-intro {
  opacity: calc(var(--ecosystem-intro-opacity, 1) * var(--settle-eco, 1));
}

/* The suite console is NOT settle-gated (unlike the intros above): its
   reveal is a tight scroll band riding the card descent, and the Motion
   cascade in script.js staggers each element in. The settle veil on top
   turned that whole choreography into one flat container fade. */

.orbit-controls-panel {
  position: absolute;
  inset: 0;
  z-index: 5;
  display: block;
  opacity: var(--orbit-copy-progress);
  pointer-events: none;
  transform: none;
  transition:
    opacity 220ms ease;
}

/* Same glass material as the Media/Marketing CTA buttons. Sits above the
   vertical center so it clears tall two-line card titles at bottom-left. */
.orbit-arrow {
  position: absolute;
  top: 40%;
  display: inline-grid;
  width: clamp(3.1rem, 4.8vw, 4.35rem);
  height: clamp(3.1rem, 4.8vw, 4.35rem);
  place-items: center;
  border: none;
  border-radius: 999px;
  background: linear-gradient(
    -75deg,
    rgba(255, 255, 255, 0.07),
    rgba(255, 255, 255, 0.22),
    rgba(255, 255, 255, 0.07)
  );
  box-shadow:
    inset 0 0.09em 0.09em rgba(0, 0, 0, 0.05),
    inset 0 -0.09em 0.09em rgba(255, 255, 255, 0.55),
    inset 0 0 0.04em 0.09em rgba(255, 255, 255, 0.48),
    0 0.42em 0.9em -0.3em rgba(17, 20, 17, 0.32);
  color: rgba(17, 20, 17, 0.92);
  cursor: pointer;
  font-size: clamp(2rem, 3.2vw, 3rem);
  font-weight: 800;
  line-height: 1;
  pointer-events: var(--orbit-controls-events);
  transform: translateY(-50%);
  transition:
    background 0.35s ease,
    box-shadow 0.3s ease;
}

/* Refraction glass layer — same as .glass-button-glass; fades on hover. */
.orbit-arrow::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 0;
  border-radius: inherit;
  overflow: hidden;
  -webkit-backdrop-filter: blur(3px);
  backdrop-filter: blur(3px);
  filter: url(#glass-distortion);
  opacity: 1;
  pointer-events: none;
  transition: opacity 0.4s ease;
}

.orbit-arrow span {
  position: relative;
  z-index: 1;
  display: grid;
  width: 100%;
  height: 100%;
  place-items: center;
  line-height: 0.72;
  transform: translateY(-0.055em);
  transition: transform 0.32s cubic-bezier(0.4, 0, 0.2, 1);
}

.orbit-arrow-prev {
  left: clamp(1rem, 4vw, 4.5rem);
}

.orbit-arrow-next {
  right: clamp(1rem, 4vw, 4.5rem);
}

.orbit-thumb:hover {
  border-color: rgba(38, 202, 255, 0.86);
  background:
    radial-gradient(circle at 32% 12%, rgba(255, 255, 255, 0.74), transparent 32%),
    linear-gradient(135deg, rgba(38, 202, 255, 0.86), rgba(73, 170, 255, 0.58));
  box-shadow:
    inset 0 1px 1px rgba(255, 255, 255, 0.82),
    0 0 24px rgba(38, 202, 255, 0.28),
    0 16px 36px rgba(38, 202, 255, 0.2);
  color: #fff;
}

/* Button-style hover: middle clears, bright lip slides to the top, icon eases
   smaller — no scale-up of the whole control. */
.orbit-arrow:hover {
  background: rgba(255, 255, 255, 0.03);
  box-shadow:
    inset 0 -0.09em 0.09em rgba(0, 0, 0, 0.05),
    inset 0 0.09em 0.09em rgba(255, 255, 255, 0.55),
    inset 0 0 0.04em 0.09em rgba(255, 255, 255, 0.48),
    0 0.42em 0.9em -0.3em rgba(17, 20, 17, 0.28);
}

.orbit-arrow:hover::before {
  opacity: 0;
}

.orbit-arrow:hover span {
  transform: translateY(-0.055em) scale(0.9);
}

.orbit-thumbnails {
  position: absolute;
  top: 50%;
  right: clamp(0.75rem, 2.8vw, 2.8rem);
  width: clamp(4.5rem, 6vw, 5.9rem);
  height: min(64vh, 34rem);
  overflow: visible;
  padding: 0;
  pointer-events: var(--orbit-controls-events);
  transform: translateY(-50%);
}

.orbit-thumbnails[hidden] {
  display: none;
}

.orbit-thumbnails::-webkit-scrollbar {
  display: none;
}

.orbit-thumb {
  display: grid;
  position: absolute;
  top: 50%;
  left: 0;
  width: 100%;
  height: clamp(3.05rem, 4.7vw, 4.05rem);
  align-items: end;
  overflow: hidden;
  padding: 0.38rem;
  border: 1px solid rgba(0, 122, 255, 0.2);
  border-radius: 14px;
  background:
    linear-gradient(135deg, rgba(255, 255, 255, 0.72), rgba(255, 255, 255, 0.26));
  box-shadow:
    inset 0 1px 1px rgba(255, 255, 255, 0.82),
    0 12px 30px rgba(17, 20, 17, 0.08);
  color: rgba(17, 20, 17, 0.8);
  cursor: pointer;
  font: inherit;
  opacity: var(--thumb-opacity, 0.8);
  transform:
    translate3d(0, calc(-50% + var(--thumb-y, 0px)), 0)
    scale(var(--thumb-scale, 0.86));
  transform-origin: center;
  z-index: var(--thumb-layer, 1);
  transition:
    opacity 240ms ease,
    transform 260ms cubic-bezier(0.2, 0.8, 0.2, 1),
    border-color 180ms ease,
    background 180ms ease,
    box-shadow 180ms ease;
}

.orbit-thumb:hover {
  transform:
    translate3d(-0.45rem, calc(-50% + var(--thumb-y, 0px)), 0)
    scale(var(--thumb-hover-scale, 1.02));
}

.orbit-thumb.is-active {
  border-color: rgba(0, 122, 255, 0.62);
  box-shadow:
    inset 0 1px 1px rgba(255, 255, 255, 0.88),
    0 0 0 1px rgba(0, 122, 255, 0.22),
    0 0 28px rgba(0, 122, 255, 0.22),
    0 16px 38px rgba(17, 20, 17, 0.1);
}

.orbit-thumb img,
.orbit-thumb video {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.orbit-thumb::after {
  position: absolute;
  inset: 0;
  background: linear-gradient(180deg, transparent 32%, rgba(255, 255, 255, 0.78));
  content: "";
}

.orbit-thumb span {
  position: relative;
  z-index: 1;
  color: rgba(17, 20, 17, 0.82);
  font-size: 0.64rem;
  font-weight: 800;
  letter-spacing: 0;
  line-height: 1;
}

.orbit-thumb:hover span {
  color: #fff;
  text-shadow: 0 0 12px rgba(255, 255, 255, 0.58);
}

.orbit-copy.is-switching .orbit-copy-content,
.orbit-copy.is-switching > .orbit-tags {
  opacity: 0;
  filter: blur(4px);
}

.orbit-copy [data-orbit-kicker] {
  /* Retired 2026-07-10: the little eyebrow line ("IMMERSIVE PERSPECTIVE" etc.)
     is hidden to keep the copy panel as compact as possible. */
  display: none;
  width: fit-content;
  color: var(--green);
  font-size: 0.74rem;
  font-weight: 800;
  letter-spacing: 0.16em;
  text-transform: uppercase;
}

.orbit-copy h2 {
  max-width: 820px;
  margin: 0;
  /* ~80% of the old size, per design pass. line-height must stay ≥1: the
     panel clips overflow, and a sub-1 line box slices descenders ("y"). */
  font-size: clamp(2.2rem, 5.6vw, 6.4rem);
  line-height: 1.04;
}

.orbit-copy p {
  max-width: 34rem;
  margin: 0;
  color: rgba(17, 20, 17, 0.66);
  font-size: clamp(1rem, 1.45vw, 1.18rem);
  line-height: 1.55;
}

.contact-button {
  display: inline-flex;
  min-height: 2.75rem;
  align-items: center;
  justify-content: center;
  padding: 0.75rem 1rem;
  font-size: 0.92rem;
  font-weight: 800;
}

.contact-button {
  color: #f8f8f2;
  background: var(--ink);
}

.contact-band {
  padding: clamp(3rem, 8vw, 6rem) clamp(1rem, 4vw, 3.5rem);
}

.ecosystem-section {
  --ecosystem-copy-opacity: 0;
  --ecosystem-intro-opacity: 1;
  --ecosystem-divider-opacity: 0.72;
  --ecosystem-stage-lift: 0px;
  --ecosystem-mobile-stage-preroll: 0px;
  --ecosystem-pointer-events: none;
  position: relative;
  z-index: 3;
  min-height: 330svh;
  margin-top: -220svh;
  background: transparent;
  pointer-events: none;
}

.ecosystem-pin {
  position: sticky;
  top: 0;
  display: grid;
  grid-template-rows: auto auto minmax(0, 1fr);
  min-height: 100svh;
  align-content: stretch;
  gap: clamp(0.7rem, 1.6vh, 1.25rem);
  overflow: hidden;
  padding: clamp(4.2rem, 6.2vh, 5rem) clamp(1rem, 4vw, 3.5rem) clamp(1rem, 2.4vh, 1.8rem);
  pointer-events: none;
}

.ecosystem-intro {
  position: relative;
  z-index: 3;
  display: grid;
  max-width: min(70rem, 90vw);
  justify-items: center;
  gap: clamp(0.55rem, 1.1vh, 0.85rem);
  margin: 0 auto;
  opacity: var(--ecosystem-intro-opacity, 1);
  text-align: center;
  transform: translateY(calc((1 - var(--ecosystem-intro-opacity, 1)) * -1.5rem));
}

.ecosystem-intro h2 {
  max-width: 900px;
  margin: 0;
  font-family: inherit;
  font-size: clamp(2.25rem, 4.35vw, 5.05rem);
  font-weight: 800;
  letter-spacing: -0.032em;
  line-height: 0.94;
  text-wrap: balance;
}

.ecosystem-intro p:last-child {
  max-width: 50rem;
  margin: 0 auto;
  color: rgba(17, 20, 17, 0.74);
  font-size: clamp(1rem, 1.2vw, 1.18rem);
  line-height: 1.6;
  text-wrap: pretty;
}

.ecosystem-divider {
  position: relative;
  z-index: 3;
  width: min(42rem, 76vw);
  height: 1px;
  margin-inline: auto;
  border-radius: 999px;
  background:
    linear-gradient(90deg, transparent, rgba(17, 20, 17, 0.16), rgba(0, 122, 255, 0.42), rgba(17, 20, 17, 0.16), transparent);
  opacity: var(--ecosystem-divider-opacity, 0.76);
}

.ecosystem-divider::after {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 0.38rem;
  height: 0.38rem;
  border-radius: 999px;
  background: rgba(0, 122, 255, 0.68);
  box-shadow: 0 0 0 5px rgba(0, 122, 255, 0.08);
  content: "";
  transform: translate(-50%, -50%);
}

@media (min-width: 841px) {
  .ecosystem-divider {
    margin-top: -0.35rem;
  }
}

.ecosystem-stage {
  position: relative;
  z-index: 4;
  display: grid;
  grid-template-columns: minmax(17rem, 0.72fr) minmax(26rem, 1.28fr);
  align-items: start;
  min-height: 0;
  gap: clamp(1.2rem, 3.2vw, 4rem);
  transform: translateY(var(--ecosystem-stage-lift, 0px));
}

.ecosystem-copy {
  display: grid;
  align-content: start;
  min-height: 0;
  gap: clamp(0.8rem, 1.9vh, 1.15rem);
  padding-top: clamp(0.9rem, 2.1vh, 1.45rem);
  opacity: var(--ecosystem-copy-opacity, 1);
  filter: blur(calc((1 - var(--ecosystem-copy-opacity, 1)) * 5px));
  transform: translateY(calc((1 - var(--ecosystem-copy-opacity, 1)) * 1.1rem));
  transition:
    opacity 240ms ease,
    filter 240ms ease,
    transform 240ms ease;
}

/* ============================================================
   Workflow suite console (the detail phase's left column).
   Sits over the abyss zone, so ink is the fixed high-contrast
   set; the frost surfaces use tint + hairline shine only, NO
   backdrop-filter (retired site-wide here for scroll perf).
   ============================================================ */

.suite-head {
  display: grid;
  gap: clamp(0.5rem, 1.2vh, 0.8rem);
}

.suite-eyebrow {
  display: flex;
  align-items: center;
  gap: 0.65rem;
  font-size: 0.76rem;
  font-weight: 800;
  letter-spacing: 0.22em;
  text-transform: uppercase;
}

.suite-dot {
  flex: none;
  width: 0.44rem;
  height: 0.44rem;
  border-radius: 999px;
  background: #3ea6ff;
  box-shadow:
    0 0 0 4px rgba(62, 166, 255, 0.16),
    0 0 12px rgba(62, 166, 255, 0.8);
}

.suite-rule {
  flex: 1;
  height: 1px;
  background: linear-gradient(90deg, rgba(158, 203, 255, 0.55), rgba(158, 203, 255, 0));
}

.suite-head h3 {
  max-width: 640px;
  margin: 0;
  font-size: clamp(2.05rem, 3.15vw, 3.35rem);
  font-weight: 800;
  letter-spacing: -0.028em;
  line-height: 1.02;
  text-wrap: balance;
}

.suite-head p {
  max-width: 40ch;
  margin: 0;
  font-size: clamp(0.88rem, 1.14vw, 1.02rem);
  line-height: 1.55;
}

/* The live readout is an ANNOTATION, not another card: the carousel card
   broadcasts the tool's name, so over here it shrinks to a mono caption and
   the description does the talking, hung off a thin blueprint rule. */
.suite-readout {
  display: grid;
  gap: 0.55rem;
  padding-left: clamp(0.85rem, 1.4vw, 1.1rem);
  border-left: 2px solid rgba(62, 166, 255, 0.55);
}

.suite-now {
  display: flex;
  align-items: baseline;
  gap: 0.3rem;
  font-size: 0.78rem;
  font-weight: 800;
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.18em;
  text-transform: uppercase;
}

.suite-now-label {
  color: rgba(247, 249, 255, 0.88);
  font-style: normal;
  text-shadow: 0 1px 6px rgba(2, 7, 12, 0.8);
}

.suite-readout p {
  max-width: 36ch;
  margin: 0;
  font-size: clamp(0.95rem, 1.22vw, 1.08rem);
  line-height: 1.6;
}

/* The suite map: all ten tools at once, so the console reads as a system
   rather than a single caption. The scroll lights the active chip; passed
   chips stay warm so the walk has a visible trail. */
.suite-map {
  display: grid;
  grid-template-columns: repeat(5, minmax(0, 1fr));
  gap: 0.4rem;
  max-width: 34rem;
  margin: 0.25rem 0 0;
  padding: 0;
  list-style: none;
}

.suite-map-step {
  -webkit-appearance: none;
  appearance: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0.42rem 0.5rem;
  border: 1px solid rgba(158, 203, 255, 0.16);
  border-radius: 999px;
  background: rgba(10, 18, 30, 0.42);
  color: rgba(247, 249, 255, 0.5);
  font-family: inherit;
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  line-height: 1;
  white-space: nowrap;
  cursor: pointer;
  /* Same gate the CTAs use: only clickable once the console is actually
     on screen, so there is never an invisible hit target. */
  pointer-events: var(--suite-cta-pointer, none);
  -webkit-tap-highlight-color: transparent;
  transition: color 0.32s ease, border-color 0.32s ease,
    background-color 0.32s ease, box-shadow 0.32s ease,
    transform 0.18s ease;
}

/* Pointer devices only: on iOS :hover latches after a tap and the chip
   would stay lit on whatever you last touched. */
@media (hover: hover) {
  .suite-map-step:hover {
    border-color: rgba(62, 166, 255, 0.5);
    color: rgba(247, 249, 255, 0.92);
  }
}

.suite-map-step:active {
  transform: scale(0.96);
}

.suite-map-step:focus-visible {
  outline: 2px solid rgba(62, 166, 255, 0.9);
  outline-offset: 2px;
}


.suite-map-step.is-done {
  border-color: rgba(158, 203, 255, 0.26);
  color: rgba(247, 249, 255, 0.72);
}


.suite-map-step.is-active {
  border-color: rgba(62, 166, 255, 0.75);
  background: rgba(62, 166, 255, 0.16);
  color: #f7f9ff;
  box-shadow: 0 0 18px rgba(62, 166, 255, 0.28);
}


.suite-ctas {
  display: flex;
  gap: 0.75rem;
  margin-top: 0.15rem;
}

.suite-cta {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: clamp(10rem, 13.5vw, 12rem);
  padding: 0.7rem 1.3rem;
  border-radius: 999px;
  font-size: 0.84rem;
  font-weight: 800;
  letter-spacing: 0.02em;
  text-decoration: none;
  pointer-events: var(--suite-cta-pointer, none);
  transition: filter 160ms ease, border-color 160ms ease, background 160ms ease;
}

.suite-cta-primary {
  color: #04121f;
  background: linear-gradient(180deg, #eaf4ff, #bfdfff);
  box-shadow:
    0 10px 26px rgba(2, 10, 18, 0.45),
    inset 0 0 0 0.6px rgba(255, 255, 255, 0.7);
}

.suite-cta-primary:hover {
  filter: brightness(1.06);
}

/* Ghost CTA: same size as the primary, clearly outlined. */
.suite-cta-ghost {
  color: #f7f9ff;
  border: 1.5px solid rgba(247, 249, 255, 0.55);
  background: rgba(233, 242, 255, 0.07);
  text-shadow: 0 1px 6px rgba(2, 7, 12, 0.8);
}

.suite-cta-ghost:hover {
  border-color: rgba(247, 249, 255, 0.85);
  background: rgba(233, 242, 255, 0.14);
}

/* ============================================================
   Console motion. The "coming online" cascade and tool-swap are
   driven by Motion springs in script.js (vendor/motion.min.js);
   CSS keeps only the ambient live-dot pulse and hover polish,
   all inert under reduced motion.
   ============================================================ */
.suite-rule {
  transform-origin: left center;
}

@media (prefers-reduced-motion: no-preference) {
  @keyframes suite-dot-pulse {
    0%,
    100% {
      box-shadow:
        0 0 0 4px rgba(62, 166, 255, 0.16),
        0 0 12px rgba(62, 166, 255, 0.8);
    }

    50% {
      box-shadow:
        0 0 0 6px rgba(62, 166, 255, 0.07),
        0 0 18px rgba(62, 166, 255, 0.45);
    }
  }

  .suite-dot {
    animation: suite-dot-pulse 2.6s ease-in-out infinite;
  }

  .suite-cta {
    transition:
      filter 160ms ease,
      border-color 160ms ease,
      background 160ms ease,
      transform 160ms ease;
  }

  .suite-cta:hover {
    transform: translateY(-1.5px);
  }
}

@media (min-width: 841px) {
  .ecosystem-stage {
    align-items: center;
  }

  .ecosystem-copy {
    align-self: start;
    padding-top: 0;
    transform: translateY(calc(clamp(-10rem, -15vh, -6.5rem) + ((1 - var(--ecosystem-copy-opacity, 1)) * 1.1rem)));
  }
}

/* Short desktop viewports: drop the support line so the console
   never clips against the pinned stage. */
@media (min-width: 841px) and (max-height: 700px) {
  .ecosystem-copy {
    gap: 0.7rem;
  }

  .suite-head p {
    display: none;
  }
}

.ecosystem-media {
  position: relative;
  /* Above the suite console: cards passing over the left column mid-flight
     paint OVER its text, never behind it (they're pointer-events: none, so
     the console's CTAs stay clickable regardless). */
  z-index: 5;
  align-self: stretch;
  min-height: min(37rem, 60vh);
  isolation: isolate;
  transform: translateY(clamp(-3.2rem, -5vh, -1.8rem));
}

.ecosystem-canvas {
  position: absolute;
  top: -14vh;
  right: -8vw;
  bottom: -14vh;
  left: -48vw;
  z-index: 1;
  display: block;
  width: calc(100% + 56vw);
  height: calc(100% + 28vh);
  cursor: default;
  pointer-events: var(--ecosystem-pointer-events);
}

.ecosystem-glass {
  position: absolute;
  top: -14vh;
  right: -8vw;
  bottom: -14vh;
  left: -48vw;
  z-index: 2;
  overflow: visible;
  opacity: 1;
  pointer-events: none;
}

.ecosystem-dom-card {
  --card-alpha: 0;
  --card-content-alpha: 0;
  --glow-local-x: -9999px;
  --glow-local-y: -9999px;
  --glow-strength: 0;
  --glow-ring: 1.35px;
  --glow-size: 176px;
  --glass-radius: 24px;
  --glass-inner-radius: 18px;
  --glass-inset: 14px;
  --glass-rim: 14px;
  position: absolute;
  top: 0;
  left: 0;
  box-sizing: border-box;
  overflow: hidden;
  border-radius: var(--glass-radius);
  background: transparent;
  color: #111411;
  font-family: Inter, Arial, sans-serif;
  pointer-events: none;
  transform-origin: center;
  transition-timing-function: cubic-bezier(0.175, 0.885, 0.32, 2.2);
  visibility: hidden;
  will-change: transform, width, height;
}

.ecosystem-dom-card.is-visible {
  visibility: visible;
}

.ecosystem-dom-liquid-base,
.ecosystem-dom-liquid-wash,
.ecosystem-dom-liquid-shine {
  position: absolute;
  inset: 0;
  border-radius: inherit;
  pointer-events: none;
}

.ecosystem-dom-liquid-base {
  z-index: 0;
  overflow: hidden;
  opacity: var(--card-alpha);
  /* Refraction retired 2026-07-10 (perf): live backdrop-blur + SVG distortion
     on a dozen cards was the ecosystem scroll lag. The glass EDGE stays —
     hairline highlights live on .ecosystem-dom-liquid-shine, the milk tint on
     .ecosystem-dom-liquid-wash. Restore the two filter lines to bring the
     refraction back. */
  isolation: isolate;
}

.ecosystem-dom-liquid-wash {
  z-index: 1;
  background: rgba(255, 255, 255, 0.25);
  opacity: var(--card-alpha);
}

.ecosystem-dom-liquid-shine {
  z-index: 4;
  opacity: var(--card-alpha);
  box-shadow:
    inset 0.75px 0.75px 0.7px rgba(255, 255, 255, 0.46),
    inset -0.55px -0.55px 0.7px rgba(255, 255, 255, 0.34),
    inset 0 0 0 0.55px rgba(255, 255, 255, 0.34);
}

.ecosystem-dom-liquid-shine::before {
  position: absolute;
  inset: -4px;
  padding: var(--glow-ring, 0.6px);
  border-radius: calc(var(--glass-radius) + 4px);
  background:
    radial-gradient(
      circle var(--glow-size, 90px) at var(--glow-local-x, -9999px) var(--glow-local-y, -9999px),
      rgba(255, 255, 255, 0.98) 0%,
      rgba(255, 255, 255, 0.86) 18%,
      rgba(255, 255, 255, 0.5) 32%,
      rgba(255, 255, 255, 0) 48%
    );
  content: "";
  /* Fallback is mandatory: an unset --glow-strength makes this calc invalid,
     and an invalid opacity computes to its initial value of 1 - a sheen
     pinned fully lit rather than switched off. */
  opacity: calc(var(--card-alpha) * var(--glow-strength, 0));
  pointer-events: none;
  -webkit-mask:
    linear-gradient(#000 0 0) content-box,
    linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
  mask:
    linear-gradient(#000 0 0) content-box,
    linear-gradient(#000 0 0);
  mask-composite: exclude;
  mix-blend-mode: normal;
  transition: opacity 140ms ease;
}

.ecosystem-dom-paper {
  position: absolute;
  inset: var(--glass-inset);
  z-index: 2;
  isolation: isolate;
  overflow: hidden;
  border-radius: var(--glass-inner-radius);
  background: linear-gradient(135deg, rgb(253, 252, 247), rgb(242, 243, 236), rgb(255, 255, 255));
  /* Crossfades against the stacked workflow face as the card files down into
     the detail carousel (--swap-mix driven per-frame from stack progress). */
  opacity: calc(var(--card-content-alpha, var(--card-alpha)) * (1 - var(--swap-mix, 0)));
  box-shadow: none;
}

/* The card's second face: the workflow-suite tool it becomes in the vertical
   carousel. Same anatomy, same glass chrome; fades IN as the ecosystem face
   fades out, so the swap reads as the card's content morphing mid-flight. */
.ecosystem-dom-paper--workflow {
  opacity: calc(var(--card-content-alpha, var(--card-alpha)) * var(--swap-mix, 0));
}

/* Placeholder media for the workflow face until real tool shots land:
   blueprint grid + a soft blue glow, echoing the workflow pages' Control
   Room look (swap for real screenshots later). */
.ecosystem-dom-visual--workflow {
  background:
    linear-gradient(rgba(0, 90, 200, 0.07) 1px, transparent 1px),
    linear-gradient(90deg, rgba(0, 90, 200, 0.07) 1px, transparent 1px),
    radial-gradient(120% 92% at 82% -10%, rgba(0, 122, 255, 0.2), transparent 58%),
    radial-gradient(100% 80% at -8% 108%, rgba(0, 122, 255, 0.12), transparent 55%),
    linear-gradient(160deg, #eef4fd, #dbe7f8);
  background-size: 7.4% 12%, 7.4% 12%, auto, auto, auto;
}

.ecosystem-dom-topbar {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  z-index: 3;
  display: flex;
  align-items: center;
  justify-content: space-between;
  height: 15%;
  padding: 0 6.3%;
  background: rgba(255, 255, 255, 0.7);
  font-size: 6.2%;
  font-weight: 800;
  letter-spacing: 0.02em;
  line-height: 1;
  text-transform: uppercase;
}

.ecosystem-dom-visual {
  position: absolute;
  left: 1.2%;
  right: 1.2%;
  bottom: 2%;
  /* Eco faces are full-bleed photo: no topbar strip above (the workflow face
     keeps its topbar, see the --workflow visual rule). */
  top: 2%;
  overflow: hidden;
  border-radius: calc(var(--glass-inner-radius) * 0.8);
  background-color: #111411;
  background-position: center;
  background-size: cover;
}

.ecosystem-dom-visual::before {
  position: absolute;
  inset: 0;
  background:
    linear-gradient(180deg, rgba(255, 255, 255, 0.22), rgba(255, 255, 255, 0.05)),
    linear-gradient(90deg, rgba(255, 255, 255, 0.28), transparent 42% 58%, rgba(255, 255, 255, 0.18));
  content: "";
}

/* One compact solid chip in the lower-left corner, both faces. The old big
   frosted mid-card banner blurred half the artwork; the photo or mockup now
   owns the face and the name stays legible without a backdrop filter. */
.ecosystem-dom-title-panel {
  position: absolute;
  left: 4.6%;
  right: auto;
  bottom: 5.4%;
  z-index: 4;
  max-width: 76%;
  padding: 2.4% 4.4% 2.2%;
  border: 1px solid rgba(28, 34, 48, 0.12);
  border-radius: calc(var(--glass-inner-radius) * 0.42);
  background: rgba(255, 255, 255, 0.93);
  box-shadow: 0 2px 9px rgba(28, 34, 48, 0.12);
}

.ecosystem-dom-title-panel strong {
  display: block;
  font-size: 6.8%;
  font-weight: 800;
  line-height: 1.05;
}

/* Sub lines were boilerplate placeholders on both faces; the chip is
   title-only. (The workflow face still renders its span in the DOM.) */
.ecosystem-dom-title-panel span {
  display: none;
}

.ecosystem-liquid-filter {
  position: absolute;
  width: 0;
  height: 0;
}

.contact-band {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1.5rem;
  border-top: 1px solid var(--line);
}

.contact-band h2 {
  max-width: 760px;
  margin-bottom: 0;
  font-size: clamp(1.8rem, 4vw, 4.1rem);
  line-height: 1;
}

html.theme-dark {
  color-scheme: dark;
  --ink: #f7f9ff;
  --muted: rgba(247, 249, 255, 0.68);
  --line: rgba(247, 249, 255, 0.16);
  --dark: #07090d;
  --shadow: 0 24px 80px rgba(0, 0, 0, 0.42);
  background: #07090d;
}

html.theme-dark body {
  color: var(--ink);
  background:
    radial-gradient(circle at 18% 10%, rgba(0, 122, 255, 0.16), transparent 32rem),
    radial-gradient(circle at 82% 18%, rgba(247, 249, 255, 0.08), transparent 30rem),
    linear-gradient(145deg, #07090d 0%, #0e1320 48%, #07090d 100%);
}

html.theme-dark .glass-bubble {
  background: rgba(255, 255, 255, 0.03);
  box-shadow:
    inset 0 -0.09em 0.1em rgba(0, 0, 0, 0.2),
    inset 0 0.09em 0.1em rgba(255, 255, 255, 0.28),
    inset 0 0 0.04em 0.045em rgba(255, 255, 255, 0.22);
}

html.theme-dark .glass-bubble::after {
  box-shadow: 0 0.42em 1em -0.3em rgba(0, 0, 0, 0.6);
}

html.theme-dark .header-bubble {
  color: #f7f9ff;
}

html.theme-dark .header-logo-bubble img {
  filter: none;
}

html.theme-dark .header-menu-drop,
html.landing-text-dark .header-menu-drop {
  border-color: rgba(247, 249, 255, 0.14);
  background: rgba(12, 16, 26, 0.58);
  box-shadow:
    0 30px 72px rgba(0, 0, 0, 0.6),
    inset 0 1px 0.5px rgba(255, 255, 255, 0.16),
    inset 0 -1px 0.5px rgba(0, 0, 0, 0.22);
}
/* Dark: the panel goes to smoked glass, and the neutral rail track and the
   group hairline both have to flip or they vanish into the surface. */
html.theme-dark .header-menu-drop,
html.landing-text-dark .header-menu-drop {
  background:
    linear-gradient(180deg, rgba(255, 255, 255, 0.09), rgba(255, 255, 255, 0) 42%),
    rgba(16, 20, 27, 0.82);
  border-color: rgba(247, 249, 255, 0.14);
  box-shadow:
    0 36px 80px rgba(0, 0, 0, 0.5),
    inset 0 1px 0.5px rgba(255, 255, 255, 0.16);
}

html.theme-dark .header-menu-drop a::before,
html.landing-text-dark .header-menu-drop a::before {
  background: var(--row-accent, rgba(247, 249, 255, 0.22));
}

html.theme-dark .menu-group-label::after,
html.landing-text-dark .menu-group-label::after {
  background: linear-gradient(90deg, rgba(247, 249, 255, 0.16), transparent);
}

html.theme-dark .header-menu-drop .theme-toggle-menu { border-top-color: rgba(247, 249, 255, 0.12); }
html.theme-dark .header-menu-drop a[aria-current="page"] {
  background: color-mix(in srgb, var(--row-accent, var(--site-accent)) 24%, transparent);
  color: var(--row-accent-bright, var(--site-accent-bright));
}

html.theme-dark .header-menu-drop a {
  color: rgba(247, 249, 255, 0.85);
}

html.theme-dark .header-menu-drop a:visited,
html.theme-dark .header-menu-drop a:active {
  color: rgba(247, 249, 255, 0.85);
}

html.theme-dark .header-menu-drop a:hover {
  background: color-mix(in srgb, var(--site-accent) 18%, transparent);
  color: var(--site-accent-bright);
}

/* The landing page flips to dark via .landing-text-dark (not .theme-dark), so
   the dropdown TEXT needs matching dark-mode colors there too. */
html.landing-text-dark .header-menu-drop a,
html.landing-text-dark .header-menu-drop a:visited,
html.landing-text-dark .header-menu-drop a:active {
  color: rgba(247, 249, 255, 0.85);
}
html.landing-text-dark .header-menu-drop a:hover,
html.landing-text-dark .header-menu-drop a[aria-current="page"] {
  background: color-mix(in srgb, var(--row-accent, var(--site-accent)) 22%, transparent);
  color: var(--row-accent-bright, var(--site-accent-bright));
}
html.landing-text-dark .header-menu-drop .menu-group-label { color: rgba(247, 249, 255, 0.5); }
html.landing-text-dark .header-menu-drop .theme-toggle-menu { border-top-color: rgba(247, 249, 255, 0.12); }
/* nav-drop-panel links are pinned #111411 for light mode at (0,2,2); beat that
   with (0,3,3) so they go light on the landing page's dark glass. */
html.landing-text-dark body.landing-page .nav-drop-panel a {
  color: rgba(247, 249, 255, 0.9);
  -webkit-text-fill-color: rgba(247, 249, 255, 0.9);
}
html.landing-text-dark body.landing-page .nav-drop-panel a:hover {
  color: var(--site-accent-bright);
  -webkit-text-fill-color: var(--site-accent-bright);
}

html.theme-dark .nav-drop-panel,
html.landing-text-dark .nav-drop-panel {
  background: rgba(12, 16, 26, 0.58);
  border-color: rgba(247, 249, 255, 0.14);
  box-shadow:
    0 24px 60px rgba(0, 0, 0, 0.6),
    inset 0 1px 0.5px rgba(255, 255, 255, 0.16),
    inset 0 -1px 0.5px rgba(0, 0, 0, 0.22);
}

html.theme-dark .nav-drop-panel a {
  color: rgba(247, 249, 255, 0.88);
}

html.theme-dark .nav-drop-panel a:hover {
  background: color-mix(in srgb, var(--site-accent) 18%, transparent);
  color: var(--site-accent-bright);
}

html.theme-dark .brand {
  color: #f7f9ff;
}

html.theme-dark .brand:visited,
html.theme-dark .brand:active {
  color: #f7f9ff;
}

html.theme-dark .brand img {
  filter: none;
}

html.theme-dark .contact-button {
  background: rgba(247, 249, 255, 0.92);
  color: #07090d;
}

html.theme-dark .nav-links a,
html.theme-dark .nav-links .nav-current {
  color: rgba(247, 249, 255, 0.62);
}

html.theme-dark .nav-links a:visited,
html.theme-dark .nav-links a:active {
  color: rgba(247, 249, 255, 0.62);
}

html.theme-dark .nav-links a:hover,
html.theme-dark .nav-links .nav-current {
  color: #f7f9ff;
}

html.theme-dark .top-contact {
  border-color: color-mix(in srgb, var(--site-accent-strong) 50%, transparent);
  background: color-mix(in srgb, var(--site-accent) 18%, transparent);
  color: var(--site-accent-bright);
}

html.theme-dark .top-contact:visited,
html.theme-dark .top-contact:active {
  color: var(--site-accent-bright);
}

html.theme-dark .top-contact:hover {
  border-color: var(--site-accent-strong);
  background: var(--site-accent);
  color: #fff;
}

html.theme-dark .theme-toggle {
  border-color: color-mix(in srgb, var(--site-accent-strong) 42%, transparent);
  background: color-mix(in srgb, var(--site-accent) 16%, transparent);
  color: var(--site-accent-bright);
}

html.theme-dark .theme-toggle:hover {
  border-color: color-mix(in srgb, var(--site-accent-bright) 75%, transparent);
  background: color-mix(in srgb, var(--site-accent-strong) 22%, transparent);
  color: #f7f9ff;
}

html.theme-dark .theme-toggle-track {
  border-color: rgba(158, 203, 255, 0.42);
  background: rgba(73, 170, 255, 0.24);
  box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.32);
}

html.theme-dark .theme-toggle-knob {
  background: #f7f9ff;
  transform: translate(0.94rem, -50%);
}

html.theme-dark .header-menu-drop .theme-toggle-menu {
  color: rgba(247, 249, 255, 0.85);
}

html.theme-dark .header-menu-drop .theme-toggle-menu:hover {
  background: rgba(0, 122, 255, 0.18);
  color: #9ecbff;
}

html.theme-dark .word-carousel span,
html.theme-dark .offer-word-carousel span {
  background: rgba(255, 255, 255, 0.03);
  box-shadow:
    inset 0 -0.09em 0.1em rgba(0, 0, 0, 0.2),
    inset 0 0.09em 0.1em rgba(255, 255, 255, 0.28),
    inset 0 0 0.04em 0.045em rgba(255, 255, 255, 0.22);
  color: rgba(247, 249, 255, 0.95);
}

html.theme-dark .word-carousel span::after,
html.theme-dark .offer-word-carousel span::after {
  box-shadow: 0 0.42em 1em -0.3em rgba(0, 0, 0, 0.6);
}

/* Media carousel arrows — dark glass, matching the CTA buttons. */
html.theme-dark .orbit-arrow {
  background: linear-gradient(
    -75deg,
    rgba(255, 255, 255, 0.05),
    rgba(255, 255, 255, 0.14),
    rgba(255, 255, 255, 0.05)
  );
  box-shadow:
    inset 0 0.09em 0.09em rgba(0, 0, 0, 0.2),
    inset 0 -0.09em 0.09em rgba(255, 255, 255, 0.3),
    inset 0 0 0.04em 0.09em rgba(255, 255, 255, 0.26),
    0 0.42em 1em -0.3em rgba(0, 0, 0, 0.6);
  color: rgba(247, 249, 255, 0.95);
}

html.theme-dark .orbit-arrow:hover {
  background: rgba(255, 255, 255, 0.03);
  box-shadow:
    inset 0 -0.09em 0.09em rgba(0, 0, 0, 0.2),
    inset 0 0.09em 0.09em rgba(255, 255, 255, 0.3),
    inset 0 0 0.04em 0.09em rgba(255, 255, 255, 0.26),
    0 0.42em 1em -0.3em rgba(0, 0, 0, 0.6);
}

/* White word on the dark glass — deeper gray/black 3D drop. */
html.theme-dark .word-carousel span .bubble-text,
html.theme-dark .offer-word-carousel span .bubble-text {
  text-shadow:
    0 0.018em 0.02em rgba(0, 0, 0, 0.62),
    0 0.05em 0.08em rgba(0, 0, 0, 0.38);
}

html.theme-dark .word-carousel span .bubble-liquid-base::after,
html.theme-dark .offer-word-carousel span .bubble-liquid-base::after {
  opacity: calc(0.16 + var(--wheel-front, 0) * 0.2);
}

html.theme-dark .orbit-section-label {
  background: rgba(255, 255, 255, 0.03);
  box-shadow:
    inset 0 -0.09em 0.1em rgba(0, 0, 0, 0.2),
    inset 0 0.09em 0.1em rgba(255, 255, 255, 0.28),
    inset 0 0 0.04em 0.045em rgba(255, 255, 255, 0.22);
  text-shadow:
    0 0.018em 0.02em rgba(0, 0, 0, calc(0.62 * var(--orbit-label-text))),
    0 0.05em 0.08em rgba(0, 0, 0, calc(0.38 * var(--orbit-label-text)));
}

html.theme-dark .orbit-section-label::after {
  box-shadow: 0 0.42em 1em -0.3em rgba(0, 0, 0, 0.6);
}

html.theme-dark .orbit-section-label:hover {
  background: rgba(255, 255, 255, 0.06);
  box-shadow:
    inset 0 -0.09em 0.1em rgba(0, 0, 0, 0.2),
    inset 0 0.09em 0.1em rgba(255, 255, 255, 0.42),
    inset 0 0 0.04em 0.045em rgba(255, 255, 255, 0.34);
}

html.theme-dark .scroll-cue {
  color: rgba(247, 249, 255, 0.55);
}

html.theme-dark .offer-solution-lockup,
html.theme-dark .offer-solution-static {
  color: rgba(247, 249, 255, 0.88);
}

html.theme-dark .offer-solution-static {
  color: #f7f9ff;
}

html.theme-dark .glass-button {
  background: linear-gradient(
    -75deg,
    rgba(255, 255, 255, 0.05),
    rgba(255, 255, 255, 0.14),
    rgba(255, 255, 255, 0.05)
  );
  box-shadow:
    inset 0 0.09em 0.1em rgba(0, 0, 0, 0.2),
    inset 0 -0.09em 0.1em rgba(255, 255, 255, 0.28),
    inset 0 0 0.04em 0.045em rgba(255, 255, 255, 0.22);
  color: rgba(247, 249, 255, 0.95);
}

html.theme-dark .glass-button-shadow {
  box-shadow: 0 0.42em 1em -0.3em rgba(0, 0, 0, 0.6);
}

html.theme-dark .glass-button:hover {
  background: rgba(255, 255, 255, 0.03);
  box-shadow:
    inset 0 -0.09em 0.1em rgba(0, 0, 0, 0.2),
    inset 0 0.09em 0.1em rgba(255, 255, 255, 0.28),
    inset 0 0 0.04em 0.045em rgba(255, 255, 255, 0.22);
}

html.theme-dark .orbit-copy p {
  color: rgba(247, 249, 255, 0.68);
}

html.theme-dark .ecosystem-copy p {
  color: rgba(247, 249, 255, 0.72);
}

html.theme-dark .ecosystem-intro h2 {
  color: #f7f9ff;
}

html.theme-dark .ecosystem-intro p:last-child {
  color: rgba(247, 249, 255, 0.8);
}

html.theme-dark .ecosystem-divider {
  background:
    linear-gradient(90deg, transparent, rgba(247, 249, 255, 0.12), rgba(73, 170, 255, 0.36), rgba(247, 249, 255, 0.12), transparent);
}

html.theme-dark .ecosystem-divider::after {
  background: rgba(158, 203, 255, 0.68);
  box-shadow: 0 0 0 5px rgba(73, 170, 255, 0.09);
}

/* ---------- Landing-page look (single consistent state) ----------
   The landing has NO separate dark/light treatment — the toggle class resolves
   to the SAME values, so flipping it changes nothing here. It is always:
     • WHITE text
     • the original CLEAR frosted glass (no tint plate)
     • a single directional DROP shadow (a soft cast below the glyphs) for
       legibility on bright scenery — NOT the old halo/glow that blurred out
       around the letters in every direction. */
body.landing-page,
html.landing-text-dark body.landing-page {
  --landing-hero-ink: #ffffff;
  --landing-hero-muted: rgba(255, 255, 255, 0.85);
  --landing-hero-text-shadow: 0 2px 5px rgba(2, 7, 12, 0.55);
  --landing-seg-ink: #ffffff;                       /* media-segment headings */
  --landing-seg-muted: rgba(255, 255, 255, 0.88);   /* media-segment body copy */
  --landing-seg-rgb: 255, 255, 255;                 /* label ink + fade opacity */
  --landing-glass-tint: rgba(255, 255, 255, 0.03);  /* original clear frost, no tint */
  --landing-glass-panel: rgba(255, 255, 255, 0.94); /* original light dropdown */
}

/* The ONLY thing the dark toggle changes on the landing: the top-header text
   goes flat black (to match the logo, which also darkens). Nothing else. */
html.landing-text-dark body.landing-page .brand,
html.landing-text-dark body.landing-page .brand:visited,
html.landing-text-dark body.landing-page .brand:active,
html.landing-text-dark body.landing-page .nav-links a,
html.landing-text-dark body.landing-page .nav-links a:visited,
html.landing-text-dark body.landing-page .nav-links a:active,
html.landing-text-dark body.landing-page .nav-links a:hover,
html.landing-text-dark body.landing-page .nav-links .nav-current,
html.landing-text-dark body.landing-page .nav-drop-btn,
html.landing-text-dark body.landing-page .nav-drop:hover .nav-drop-btn,
html.landing-text-dark body.landing-page .nav-drop.open .nav-drop-btn,
html.landing-text-dark body.landing-page .top-contact,
html.landing-text-dark body.landing-page .top-contact:visited {
  color: #111411;
  text-shadow: none;
}

body.landing-page .brand,
body.landing-page .brand:visited,
body.landing-page .brand:active,
body.landing-page .nav-links a,
body.landing-page .nav-links a:visited,
body.landing-page .nav-links a:active,
body.landing-page .nav-links .nav-current,
body.landing-page .nav-drop-btn,
body.landing-page .theme-toggle-inline {
  color: var(--landing-hero-ink);
  text-shadow: var(--landing-hero-text-shadow);
}

/* Contact stays blue everywhere: it is the call to action, not a tab.
   The halo keeps it legible over the bright hero scenery. */
body.landing-page .top-contact,
body.landing-page .top-contact:visited,
body.landing-page .top-contact:active {
  color: var(--site-accent-bright, #9ecbff);
  text-shadow: var(--landing-hero-text-shadow);
}

body.landing-page .nav-links a:hover,
body.landing-page .nav-links .nav-current,
body.landing-page .nav-drop:hover .nav-drop-btn,
body.landing-page .nav-drop.open .nav-drop-btn {
  color: var(--landing-hero-ink);
  text-shadow: var(--landing-hero-text-shadow);
}

/* The Solutions / Our Work dropdown panels are their own frosted cards, so their
   links must read as panel ink — NOT the landing hero's white nav treatment
   (they live inside .nav-links, so they'd otherwise inherit the white). */
body.landing-page .nav-drop-panel a {
  color: #111411;
  -webkit-text-fill-color: #111411;
  text-shadow: none;
}
body.landing-page .nav-drop-panel a:hover {
  color: var(--site-accent);
  -webkit-text-fill-color: var(--site-accent);
  text-shadow: none;
}
html.theme-dark body.landing-page .nav-drop-panel a {
  color: rgba(247, 249, 255, 0.88);
}
html.theme-dark body.landing-page .nav-drop-panel a:hover {
  color: var(--site-accent-bright);
}

/* Logo follows the header text per mode on the landing (the PNG source is
   white): LIGHT (default) → white + the same directional drop shadow as the
   text; DARK → flat black (invert the white source). */
body.landing-page .brand img {
  filter: drop-shadow(0 2px 5px rgba(2, 7, 12, 0.55));
}
html.landing-text-dark body.landing-page .brand img {
  filter: invert(1);
}

body.landing-page .hero-solution-lockup,
body.landing-page .offer-solution-lockup,
body.landing-page .solution-static,
body.landing-page .offer-solution-static {
  color: var(--landing-hero-ink);
  text-shadow: var(--landing-hero-text-shadow);
}

body.landing-page .word-carousel span .bubble-text,
body.landing-page .offer-word-carousel span .bubble-text {
  color: var(--landing-hero-ink);
  -webkit-text-fill-color: var(--landing-hero-ink);
  text-shadow: var(--landing-hero-text-shadow);
}

body.landing-page .scroll-cue {
  color: var(--landing-hero-muted);
  text-shadow: var(--landing-hero-text-shadow);
}

/* The media copy uses the footer's fixed high-contrast treatment in both
   toggle states because the same glass crosses bright lake and dark cards. */
body.landing-page .orbit-copy h2 {
  color: #f7f9ff;
  text-shadow:
    0 2px 3px rgba(2, 7, 12, 0.76),
    0 0 18px rgba(2, 7, 12, 0.58);
}

body.landing-page .orbit-copy p {
  color: rgba(247, 249, 255, 0.86);
  text-shadow: 0 1px 7px rgba(2, 7, 12, 0.82);
}

body.landing-page .orbit-copy [data-orbit-kicker] {
  color: #9ecbff;
  text-shadow: 0 1px 7px rgba(2, 7, 12, 0.82);
}

/* The tags are the same clear material as the contoured copy panel. They keep
   their refraction on hover instead of swapping to a tinted or sharp fill. */
body.landing-page .orbit-tags .glass-button,
body.landing-page .orbit-tags .glass-button:hover {
  background: transparent;
  box-shadow:
    inset 0 -0.09em 0.1em rgba(0, 0, 0, 0.05),
    inset 0 0.09em 0.1em rgba(255, 255, 255, 0.5),
    inset 0 0 0.04em 0.045em rgba(255, 255, 255, 0.4);
}

body.landing-page .orbit-tags .glass-button-glass,
body.landing-page .orbit-tags .glass-button:hover .glass-button-glass {
  opacity: 1;
}

body.landing-page .orbit-tags .glass-button-text,
html.theme-dark body.landing-page .orbit-tags .glass-button-text {
  color: #f7f9ff;
  text-shadow: 0 1px 7px rgba(2, 7, 12, 0.82);
}

html.landing-text-dark body.landing-page .theme-toggle-knob {
  transform: translate(0.94rem, -50%);
}

/* Once the full header separates, its two controls cross every scene. White
   glyphs with a compact dark halo remain legible without changing materials. */
html.header-split body.landing-page .header-bubble {
  color: #ffffff;
  filter: drop-shadow(0 1px 4px rgba(4, 8, 16, 0.72));
}

html.header-split body.landing-page .header-logo-bubble img {
  filter: drop-shadow(0 1px 4px rgba(4, 8, 16, 0.72));
}

/* The lake is bright enough for the normal light-mode ink. The abyss and
   lava need one stable high-contrast treatment regardless of the toggle. */
body.landing-page .ecosystem-intro h2,
body.landing-page .ecosystem-copy h3,
body.landing-page .contact-band h2 {
  color: #f7f9ff;
  text-shadow:
    0 2px 3px rgba(2, 7, 12, 0.72),
    0 0 18px rgba(2, 7, 12, 0.5);
}

body.landing-page .ecosystem-intro p:last-child,
body.landing-page .ecosystem-copy p {
  color: rgba(247, 249, 255, 0.84);
  text-shadow: 0 1px 7px rgba(2, 7, 12, 0.82);
}

body.landing-page .ecosystem-copy .suite-eyebrow,
body.landing-page .contact-band .eyebrow {
  color: #9ecbff;
  text-shadow: 0 1px 7px rgba(2, 7, 12, 0.82);
}

/* ============================================================
   Landing text ink — WHITE everywhere, carrying the single directional
   DROP shadow (--landing-hero-text-shadow). No tint: the glass reverts to
   the original clear frost. Header / hero / nav / word-wheel / scroll-cue
   already pull ink + shadow from the vars above; the rules here just bring
   the media-carousel copy (which was fixed high-contrast) into line. The
   footer is left untouched.
   ============================================================ */
body.landing-page .orbit-section-label {
  /* keep the fade-in opacity (--orbit-label-text); white ink + drop shadow.
     -webkit-text-fill-color must be set too or it wins over `color`. */
  color: rgba(var(--landing-seg-rgb), var(--orbit-label-text));
  -webkit-text-fill-color: rgba(var(--landing-seg-rgb), var(--orbit-label-text));
  text-shadow: var(--landing-hero-text-shadow);
}
body.landing-page .orbit-copy h2 {
  color: var(--landing-seg-ink);
  text-shadow: var(--landing-hero-text-shadow);
}
body.landing-page .orbit-copy p {
  color: var(--landing-seg-muted);
  text-shadow: var(--landing-hero-text-shadow);
}
body.landing-page .orbit-tags .glass-button-text,
html.theme-dark body.landing-page .orbit-tags .glass-button-text {
  color: var(--landing-seg-ink);
  text-shadow: var(--landing-hero-text-shadow);
}

body.landing-page .ecosystem-divider {
  background:
    linear-gradient(90deg, transparent, rgba(247, 249, 255, 0.16), rgba(73, 170, 255, 0.5), rgba(247, 249, 255, 0.16), transparent);
}

body.landing-page .ecosystem-divider::after {
  background: rgba(158, 203, 255, 0.78);
  box-shadow: 0 0 0 5px rgba(73, 170, 255, 0.12);
}

body.landing-page .contact-band {
  border-top-color: rgba(247, 249, 255, 0.18);
}

/* Full-bleed liquid-glass landing footer. The wordmark is subtracted from the
   material, then receives the same static and cursor-lit rims as its edge. */
body.landing-page .contact-band.landing-footer {
  --footer-wordmark-size: clamp(7.5rem, 14.3vw, 17rem);
  --footer-wordmark-reserve: clamp(9rem, 17.2vw, 20.4rem);
  position: relative;
  z-index: 4;
  display: grid;
  width: 100%;
  min-height: clamp(33rem, 68svh, 48rem);
  align-items: start;
  margin: 0;
  overflow: hidden;
  padding:
    clamp(3.5rem, 6vw, 5.25rem)
    max(clamp(1rem, 4vw, 3.5rem), env(safe-area-inset-right))
    calc(var(--footer-wordmark-reserve) + max(clamp(1.25rem, 2.5vw, 2.5rem), env(safe-area-inset-bottom)))
    max(clamp(1rem, 4vw, 3.5rem), env(safe-area-inset-left));
  border: 0;
  border-radius: 0;
  background: transparent;
  box-shadow: none;
}

body.landing-page .landing-footer__material {
  position: absolute;
  inset: 0;
  display: block;
  pointer-events: none;
}

body.landing-page .landing-footer__surface {
  position: absolute;
  inset: 0;
  z-index: 1;
  display: block;
  border-radius: 0;
  background: rgba(4, 9, 14, 0.55);
  box-shadow:
    inset 0 -0.09em 0.1em rgba(0, 0, 0, 0.05),
    inset 0 0.09em 0.1em rgba(255, 255, 255, 0.5),
    inset 0 0 0.04em 0.045em rgba(255, 255, 255, 0.4);
  -webkit-mask: url(#landing-footer-word-cutout);
  mask: url(#landing-footer-word-cutout);
  pointer-events: none;
}

body.landing-page .landing-footer .bubble-liquid-base {
  z-index: 0;
  border-radius: 0;
  -webkit-backdrop-filter: blur(3px);
  backdrop-filter: blur(3px);
  filter: url(#glass-distortion);
}

body.landing-page .landing-footer__wordmark {
  position: absolute;
  inset: 0;
  z-index: 1;
  display: block;
  width: 100%;
  height: 100%;
  overflow: visible;
  pointer-events: none;
}

body.landing-page .landing-footer__wordmark-shape {
  font-family: Inter, Arial, sans-serif;
  font-size: var(--footer-wordmark-size);
  font-weight: 800;
  letter-spacing: 0.02em;
  font-kerning: none;
  font-feature-settings: "kern" 0;
  dominant-baseline: middle;
  text-anchor: start;
  text-rendering: geometricPrecision;
}

body.landing-page .landing-footer__wordmark-edge {
  opacity: 1;
  filter: drop-shadow(0 0 2.6px rgba(255, 255, 255, 0.9));
  mix-blend-mode: screen;
}

body.landing-page .landing-footer__wordmark-measure {
  fill: transparent;
  stroke: none;
  opacity: 0;
}

body.landing-page .landing-footer__content {
  position: relative;
  z-index: 2;
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  width: min(100%, 1160px);
  align-self: start;
  align-items: end;
  gap: clamp(2rem, 6vw, 7rem);
  margin-inline: auto;
}

body.landing-page .landing-footer h2 {
  max-width: 17ch;
  margin: 0;
  color: #f7f9ff;
  font-size: clamp(2.15rem, 5.1vw, 5.25rem);
  font-weight: 800;
  letter-spacing: -0.04em;
  line-height: 0.94;
  text-shadow:
    0 2px 3px rgba(2, 7, 12, 0.76),
    0 0 18px rgba(2, 7, 12, 0.58);
  text-wrap: balance;
}

body.landing-page .landing-footer .eyebrow {
  color: #cfe5ff;
  text-shadow: 0 1px 7px rgba(2, 7, 12, 0.82);
}

body.landing-page .landing-footer .contact-button {
  min-height: 3.25rem;
  padding: 0.9rem 1.35rem;
  border-radius: 999px;
  background: #111411;
  color: #ffffff;
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.22);
  transition:
    transform 180ms cubic-bezier(0.16, 1, 0.3, 1),
    background-color 180ms ease;
}

body.landing-page .landing-footer .contact-button:hover {
  background: #007aff;
  transform: translateY(-2px);
}

body.landing-page .landing-footer .contact-button:active {
  transform: translateY(0);
}

body.landing-page .landing-footer .contact-button:focus-visible {
  outline: 3px solid #007aff;
  outline-offset: 4px;
}

body.landing-page .landing-footer.glass-bubble::before {
  z-index: 4;
}

/* ---- settle-in text reveal (headers/descriptions on static pages + the
   landing footer). site-header.js tags the elements and flips .is-revealed
   as they enter the viewport; scroll-choreographed landing sections are
   excluded — their text rides the zone settle vars instead. ---- */
.text-reveal {
  opacity: 0;
  transform: translateY(0.85rem);
  transition:
    opacity 640ms cubic-bezier(0.22, 1, 0.36, 1),
    transform 640ms cubic-bezier(0.22, 1, 0.36, 1);
  transition-delay: var(--reveal-delay, 0ms);
}

.text-reveal.is-revealed {
  opacity: 1;
  transform: none;
}

@media (prefers-reduced-motion: reduce) {
  .text-reveal {
    opacity: 1 !important;
    transform: none !important;
    transition: none !important;
  }
}

@media (max-width: 840px) {
  .topbar {
    top: 0.6rem;
  }

  /* Two calm rows: brand left + compact toggle right, then the whole nav
     (Contact included) centered beneath. No horizontal scrolling, nothing
     hanging off the pill's edge. */
  .header-main {
    position: relative;   /* anchor for the pinned theme toggle below */
    flex-wrap: wrap;
    justify-content: center;
    row-gap: 0.42rem;
    padding: 0.55rem 0.8rem 0.62rem;
    border-radius: 22px;
  }

  /* One row, left to right: big logo, Solutions, Our Work, then the
     Contact/Inspo button and the theme toggle. The wordmark is dropped;
     the mark alone carries the brand at this size. */
  .brand {
    order: 1;
    flex: 0 0 auto;
    min-width: 0;
  }

  .brand img {
    width: 2.5rem;
  }

  .brand span {
    display: none;
  }

  .header-main > .theme-toggle-inline {
    order: 5;
    gap: 0;
    padding: 0.36rem 0.42rem;
  }

  .theme-toggle-inline .theme-toggle-label {
    display: none;
  }

  .nav-links {
    order: 2;
    width: auto;
    flex: 1 1 auto;
    justify-content: center;
    gap: 0.75rem;
    margin: 0;
    overflow: visible;
    padding-bottom: 0;
  }

  .nav-links a,
  .nav-links .nav-current {
    flex: 0 0 auto;
    font-size: 0.78rem;
  }

  /* Contact is one of the tabs on mobile, so it matches them exactly:
     same size, weight, casing and spacing. It keeps its outline to stay
     the call to action, but stops being its own smaller typeface. */
  .top-contact {
    order: 4;
    flex: 0 0 auto;
    margin-left: 0;
    padding: 0.2rem 0.66rem;
    font-size: 0.76rem;
    font-weight: 700;
    letter-spacing: normal;
    text-transform: none;
  }

  .header-main {
    flex-wrap: nowrap;
    gap: 0.5rem;
    padding: 0.5rem 0.6rem;
  }

  .hero {
    min-height: auto;
    padding-top: 10rem;
  }

  .slogan-hero {
    min-height: 100svh;
    padding-top: 8rem;
  }

  .slogan-hero h1 {
    font-size: clamp(3.2rem, 18vw, 6.6rem);
  }

  .hero-solution-lockup {
    flex-direction: column;
    gap: 0.35rem;
    font-size: clamp(3.1rem, 13.2vw, 5.6rem);
  }

  .word-carousel,
  .offer-word-carousel {
    width: min(94vw, 29rem);
    overflow: visible;
  }

  /* The wheel radius scales down with the type on phones (script.js), so the
     carousel box and the "Solutions" pull-up shrink with it — otherwise the
     compact wheel floats in a desktop-sized box with a dead gap below. */
  .word-carousel {
    height: clamp(11rem, 58vw, 15rem);
  }

  /* Must keep the --bubble-extra-current term. The wheel's transform centres
     with -50% MINUS half that value, to compensate for the pill growing by
     it. Dropping it from the width left the compensating shift unmatched, so
     every pill sat left of centre on phones. */
  .word-carousel span,
  .offer-word-carousel span {
    width: calc(min(94vw, 29rem) + var(--bubble-extra-current, 0px));
  }

  /* Both "Solutions" lockups are hidden on phones: the hero one and the
     carousel bridge copy that carries the outline treatment. Their look
     depended on a ridge mask traced for a wide crop, which never lined up
     once the video was cover cropped to a tall screen. The word wheel
     carries the hero on its own here. */
  .solution-static,
  .offer-solution-static {
    display: none;
  }

  /* The section label sits between the hamburger and logo bubbles, so it
     matches their height and weight rather than floating as small text. */
  .orbit-section-label {
    display: grid;
    place-items: center;
    min-height: 3.3rem;
    padding: 0 1.1rem;
    border-radius: 999px;
    font-size: 1.02rem;
    font-weight: 800;
  }

  .orbit-showcase {
    min-height: 330svh;
    margin-top: -100svh;
  }

  .orbit-scene {
    min-height: 100svh;
  }

  /* Sky composition (MOBILE ONLY — desktop keeps the classic side-by-side
     lockup): the wheel rides in the sky band, "Solutions" stands alone just
     above the summit at 52svh, sized to span nearly the full view. */
  .offer-solution-lockup {
    flex-direction: column;
    height: 100svh;
    justify-content: flex-start;
    /* Centers the wheel between the header's bottom edge (~94px) and the
       "Solutions" top (52svh − half word): padding = midpoint − boxHalf. */
    padding-top: clamp(3rem, calc(26svh + 42px - 29vw), 14rem);
    gap: 0;
    font-size: clamp(2.7rem, 12vw, 4.9rem);
  }

  .offer-solution-lockup .offer-word-carousel {
    /* flex-basis would become the HEIGHT in this column layout (main axis
       flipped) and override the height below — neutralize it. */
    flex-basis: auto;
    flex-shrink: 0;
    width: min(94vw, 29rem);
    height: clamp(11rem, 58vw, 15rem);
  }

  .solution-stack {
    position: absolute;
    top: 52svh;
    left: 50%;
    align-items: center;
    transform: translate(-50%, -50%);
  }

  .offer-solution-static {
    font-size: clamp(3.4rem, 17.5vw, 5.6rem);
    text-align: center;
    white-space: nowrap;
  }

  .offer-bridge-copy-front {
    -webkit-mask-image:
      radial-gradient(
        ellipse 52% 31% at 50% 48%,
        transparent 0 44%,
        rgba(0, 0, 0, 0.2) 58%,
        #000 78%
      );
    mask-image:
      radial-gradient(
        ellipse 52% 31% at 50% 48%,
        transparent 0 44%,
        rgba(0, 0, 0, 0.2) 58%,
        #000 78%
      );
  }

  .orbit-section-label {
    top: 1.45rem;
    left: 50%;
    max-width: calc(100vw - 1.6rem);
    font-size: clamp(1.35rem, 6.2vw, 2rem);
  }

  .orbit-section-label span {
    transform: scale(0.76);
  }

  .orbit-copy-primary {
    /* Anchored by the BOTTOM so the panel's lower edge lands ~93svh on
       every phone height (owner-drawn line, 2026-08-05). */
    top: auto;
    left: 1rem;
    right: 1rem;
    bottom: 7svh;
    width: auto;
    max-width: none;
    min-height: 12.5rem;
    align-content: start;
    justify-items: start;
    text-align: left;
    transform: translateX(calc((1 - var(--orbit-copy-progress)) * -1.2rem));
  }

  .orbit-copy-panel {
    --orbit-copy-radius: 12px;
    width: 100%;
    min-width: 0;
    max-width: 100%;
    padding: 0.68rem 0.85rem 0.85rem;
    border-radius: 12px;
  }

  /* Peak-outline trace is a mobile-only effect. */
  .offer-solution-static .solution-outline {
    display: inline-block;
  }

  /* Media segment on mobile: the JS skips the text-hugging fit + staircase
     (plain full-width glass card), and everything centers — title, copy,
     and the tag bubbles. */
  .orbit-copy h2,
  .orbit-copy p {
    text-align: center;
  }

  .orbit-tags {
    width: 100%;
    justify-self: stretch;
    justify-content: center;
  }

  .orbit-controls-panel {
    inset: 0;
    max-width: none;
  }

  .orbit-arrow {
    display: none;
  }

  .orbit-arrow-prev {
    left: 0.75rem;
  }

  .orbit-arrow-next {
    right: 0.75rem;
  }

  .orbit-thumbnails {
    right: 0.65rem;
    width: 3.8rem;
    height: min(52vh, 25rem);
  }

  .orbit-thumb {
    height: 2.75rem;
  }

  .orbit-thumb:hover {
    transform:
      translate3d(-0.28rem, calc(-50% + var(--thumb-y, 0px)), 0)
      scale(var(--thumb-hover-scale, 1.02));
  }

  .orbit-copy h2 {
    font-size: clamp(2.5rem, 14vw, 4.8rem);
  }

  .orbit-copy p {
    max-width: 28rem;
    font-size: 0.96rem;
  }

  .ecosystem-section {
    --ecosystem-intro-opacity: 0;
    --ecosystem-divider-opacity: 0;
    min-height: 420svh;
    margin-top: -220svh;
  }

  .ecosystem-pin {
    align-content: start;
    gap: 0.55rem;
    padding: 4.15rem 1rem 1.5rem;
  }

  .ecosystem-intro h2 {
    font-size: clamp(2.2rem, 10.5vw, 4.2rem);
  }

  .ecosystem-intro p:last-child {
    font-size: 0.9rem;
    line-height: 1.38;
  }

  html.theme-dark .ecosystem-intro p:last-child {
    color: rgba(247, 249, 255, 0.68);
  }

  .ecosystem-stage {
    position: relative;
    grid-template-columns: 1fr;
    min-height: 66svh;
    gap: 0.8rem;
    transform: translateY(calc(var(--ecosystem-stage-lift, 0px) + var(--ecosystem-mobile-stage-preroll, 0px)));
  }

  .ecosystem-copy {
    position: absolute;
    z-index: 3;
    top: 0;
    left: 0;
    right: 0;
    min-height: 0;
    padding-top: 0;
    transform:
      /* Was -11.2rem, sized for the old one line copy. The suite console is
         much taller, so that pull up shoved it under the header bubbles.
         Clears the header and lets the card stack sit lower with it. */
      /* Sits just under the header: far enough down not to collide with the
         bubbles, high enough that there is no dead band above it. */
      translateY(calc(((1 - var(--ecosystem-copy-opacity, 1)) * 0.35rem) - (var(--ecosystem-copy-opacity, 1) * 8rem)));
  }

  /* Mobile keeps the eyebrow + live readout only: the masthead
     and CTAs need room the stacked stage doesn't have. */
  .suite-head h3,
  .suite-head p,
  .suite-ctas {
    display: none;
  }

  .ecosystem-copy {
    gap: 0.55rem;
  }

  .suite-readout p {
    font-size: 0.9rem;
    line-height: 1.45;
  }

  /* Phones: a fixed five column grid, so it is always two tidy rows of five
     instead of wrapping into a 5+4+1 orphan on one width and something else
     on the next. Equal cells also read more deliberate than ragged wrapping. */
  .suite-map {
    gap: 0.26rem;
    max-width: none;
    margin-top: 0.4rem;
  }

  .suite-map-step {
    padding: 0.38rem 0.2rem;
    font-size: 0.6rem;
    letter-spacing: 0.03em;
  }


  html.theme-dark .ecosystem-copy p {
    color: rgba(247, 249, 255, 0.68);
  }

  /* The card stack follows the console down, so the two stay as one block
     instead of the cards crowding the header. */
  .ecosystem-media {
    min-height: 67svh;
    /* No blanket shift here: this box holds BOTH the scattered overview and
       the stacked suite, so pushing it down dragged the scatter down too.
       The suite console is offset on its own instead. */
    transform: none;
  }

  .ecosystem-canvas {
    inset: -15vh -34vw -10vh -34vw;
    width: calc(100% + 68vw);
    height: calc(100% + 25vh);
  }

  .ecosystem-glass {
    inset: -15vh -34vw -10vh -34vw;
  }

  .contact-band {
    display: grid;
  }

  body.landing-page .contact-band.landing-footer {
    --footer-wordmark-size: 10rem;
    --footer-wordmark-reserve: 12rem;
    min-height: clamp(25rem, 56svh, 34rem);
  }

  body.landing-page .landing-footer__wordmark {
    overflow: visible;
  }

  body.landing-page .landing-footer__content {
    grid-template-columns: 1fr;
    align-items: start;
    gap: 1.5rem;
  }

  body.landing-page .landing-footer h2 {
    max-width: 15ch;
    font-size: clamp(2.1rem, 9.5vw, 3.6rem);
    line-height: 0.97;
  }

  body.landing-page .landing-footer .contact-button {
    justify-self: start;
  }
}

/* ============================================================
   Touch-only: the page ENDS above the wordmark strip, and dragging past
   the bottom uncovers REZZOLUTIONS.

   The negative margin pulls the reserved strip out of the document's
   scrollable height, so the last thing you can scroll to is the CTA. The
   strip still paints below that end, which is what the rubber band
   exposes. Bounce has to be handed back for that (html turns it off site
   wide because it used to expose the fixed background) - here the
   wordmark strip is what fills the gap, so there is nothing raw to see.

   Desktop is deliberately untouched: no rubber band there, so the
   wordmark just stays lining the bottom as it always has.
   ============================================================ */
@media (hover: none) and (pointer: coarse) {
  html {
    overscroll-behavior-y: auto;
  }

  body.landing-page .contact-band.landing-footer {
    margin-bottom: calc(-1 * var(--footer-wordmark-reserve));
  }
}

@media (hover: none) and (pointer: coarse) {
  body.landing-page .landing-footer.glass-bubble::before {
    display: none;
  }
}

@media (min-width: 841px) and (max-height: 780px) {
  .orbit-copy-primary {
    bottom: 4.2rem;
  }

  .orbit-copy h2 {
    font-size: clamp(2.7rem, 6vw, 5.8rem);
  }
}

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

  body.landing-page .landing-footer .contact-button {
    transition: none;
  }

  body.landing-page .landing-footer .contact-button:hover {
    transform: none;
  }
}

@media (prefers-reduced-transparency: reduce) {
  .orbit-copy-panel .bubble-liquid-base {
    -webkit-backdrop-filter: none;
    backdrop-filter: none;
    filter: none;
  }

  .orbit-copy-panel__surface {
    background: rgba(255, 255, 255, 0.055);
  }
}

@media (prefers-reduced-transparency: reduce) {
  body.landing-page .landing-footer__surface {
    background: rgba(4, 9, 14, 0.72);
  }

  body.landing-page .landing-footer .bubble-liquid-base {
    -webkit-backdrop-filter: none;
    backdrop-filter: none;
    filter: none;
  }

}

/* ============================================================
   Phone paint budget (html.is-phone, set in script.js).

   During the ecosystem card flight every card is resized and moved each
   frame. Inset shadows, a masked glow ring, and backdrop blur all have to
   be re-rasterised whenever that happens, times ten cards, times two card
   faces. Together they were the difference between a smooth descent and a
   stuttering one. The glass reads almost identically without them at
   phone size, where each card is only a couple of hundred pixels wide.
   ============================================================ */
html.is-phone .ecosystem-dom-liquid-shine {
  box-shadow: none;
}

html.is-phone .ecosystem-dom-liquid-shine::before {
  display: none;
}

/* Belt and braces: .is-phone is set by JS and needs BOTH a narrow viewport
   and a coarse pointer, so a tablet (or a late-running script) could still
   land a cursor sheen on a device that has no cursor. This does not depend
   on JS at all. */
@media (hover: none) {
  .ecosystem-dom-liquid-shine::before,
  .orbit-dom-liquid-shine::before {
    display: none;
  }
}

/* The orbit cards get their shine simplified for the phone GPU. */
html.is-phone .orbit-dom-card .orbit-dom-liquid-shine {
  box-shadow: none;
}

/* The word wheel's bubbles run an SVG displacement filter plus a backdrop
   blur, on ten elements that move every frame. That combination is the
   single most expensive thing on the landing page for a phone GPU, and it
   is what made the words above "Solutions" stutter while standing still.
   Phones keep the frosted look, without the per-frame refraction. */
html.is-phone .word-carousel span .bubble-liquid-base,
html.is-phone .offer-word-carousel span .bubble-liquid-base {
  filter: none;
  -webkit-backdrop-filter: none;
  backdrop-filter: none;
  background: rgba(255, 255, 255, 0.24);
}

html.is-phone .glass-bubble .bubble-liquid-base {
  filter: none;
}

/* Cursor glow rings never render on a phone: no pointer to follow them,
   and each one is a masked radial gradient composited over glass. */
html.is-phone .orbit-dom-card .orbit-dom-glow,
html.is-phone .word-carousel span .bubble-glow,
html.is-phone .offer-word-carousel span .bubble-glow,
html.is-phone .glass-button-glass {
  display: none;
}

/* iOS applies :hover on tap, so the glass glow still lit up on touch even
   after the pointer-driven JS stopped writing it. Force the strength to
   zero on phones: !important in a stylesheet beats the inline values the
   render loops set, so nothing can turn it back on mid-scroll. */
html.is-phone .word-carousel span,
html.is-phone .offer-word-carousel span,
html.is-phone .glass-button,
html.is-phone .glass-bubble,
html.is-phone .ecosystem-dom-card,
html.is-phone .orbit-dom-card,
html.is-phone .tag-chip,
html.is-phone .seg-btn,
html.is-phone .work-card {
  --glow-strength: 0 !important;
  --glow-ring: 0px !important;
}

html.is-phone .word-carousel span:hover .bubble-liquid-base,
html.is-phone .offer-word-carousel span:hover .bubble-liquid-base,
html.is-phone .glass-bubble:hover .bubble-liquid-base {
  filter: none;
  -webkit-backdrop-filter: none;
  backdrop-filter: none;
}

/* iOS paints a grey box over whatever is tapped. It reads as a glitch on
   glass controls, especially the nav buttons, so it is cleared site wide.
   Focus rings are untouched: keyboard users still get their outline. */
html,
a,
button,
[role="button"],
input,
select,
textarea,
label,
summary {
  -webkit-tap-highlight-color: transparent;
}

/* Admin-only Upload button on the Media Work hero (injected by work.js). */
.work-upload-btn {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  margin-top: 0.9rem;
  padding: 0.55rem 1.15rem;
  border: 1px solid color-mix(in srgb, var(--site-accent, #007aff) 40%, transparent);
  border-radius: 999px;
  background: color-mix(in srgb, var(--site-accent, #007aff) 12%, transparent);
  color: var(--site-accent, #007aff);
  font-size: 0.82rem;
  font-weight: 800;
  text-decoration: none;
}

.work-upload-btn::before {
  content: "+";
  font-size: 1rem;
  line-height: 1;
}

.work-upload-btn:hover {
  background: color-mix(in srgb, var(--site-accent, #007aff) 20%, transparent);
}

/* Belt and braces with the JS gate: no SVG refraction filters anywhere on a
   phone. These re-rasterise their whole element every frame they animate. */
html.is-phone .offer-solution-lockup,
html.is-phone .offer-solution-lockup.is-genie-warp,
html.is-phone .orbit-copy-panel .bubble-liquid-base,
html.is-phone .word-carousel span .bubble-liquid-base,
html.is-phone .offer-word-carousel span .bubble-liquid-base {
  filter: none !important;
}

/* ============================================================
   Ecosystem card popup (phones tap a scattered card).

   Learned rules baked in: the BACKDROP owns the blur at constant strength
   and only ever fades; the panel is tint over it and animates transform
   and opacity only; it is always rendered, never display-toggled, so its
   entrance transition always runs.
   ============================================================ */
.eco-pop-backdrop {
  position: fixed;
  inset: 0;
  z-index: 220;
  background: rgba(7, 9, 13, 0.5);
  -webkit-backdrop-filter: blur(16px) saturate(1.4);
  backdrop-filter: blur(16px) saturate(1.4);
  opacity: 0;
  pointer-events: none;
  transition: opacity 220ms ease;
}

.eco-pop-backdrop.is-on {
  opacity: 1;
  pointer-events: auto;
}

.eco-pop {
  position: fixed;
  top: 50%;
  left: 50%;
  z-index: 230;
  width: min(92vw, 26rem);
  max-height: 86svh;
  overflow-y: auto;
  border-radius: 22px;
  background: rgba(248, 250, 253, 0.94);
  box-shadow: 0 36px 90px rgba(4, 8, 18, 0.45);
  opacity: 0;
  pointer-events: none;
  transform: translate(-50%, -50%) scale(0.92);
  transform-origin: 50% 50%;
  transition: transform 260ms cubic-bezier(0.22, 1, 0.36, 1), opacity 220ms ease;
  will-change: transform, opacity;
}

.eco-pop[hidden] {
  display: block;
  visibility: hidden;
}

.eco-pop.is-on {
  opacity: 1;
  pointer-events: auto;
  transform: translate(-50%, -50%) scale(1);
}

html.theme-dark .eco-pop,
html.landing-text-dark .eco-pop {
  background: rgba(16, 20, 27, 0.96);
}

.eco-pop-close {
  position: absolute;
  top: 0.7rem;
  right: 0.7rem;
  z-index: 2;
  display: grid;
  width: 2.1rem;
  height: 2.1rem;
  border: none;
  border-radius: 50%;
  background: rgba(7, 9, 13, 0.55);
  color: #fff;
  cursor: pointer;
  font-size: 0.85rem;
  place-items: center;
}

.eco-pop-visual {
  height: 11rem;
  background-color: #0b0f18;
  background-position: center;
  background-size: cover;
}

/* Workflow mode: the visual hosts the tool's inline SVG mockup instead of a
   cover photo. */
.eco-pop-visual--wf {
  overflow: hidden;
  background-color: #f7f9fd;
}

.eco-pop-visual--wf svg {
  display: block;
  width: 100%;
  height: 100%;
}

.eco-pop-body {
  display: grid;
  padding: 1.15rem 1.25rem 1.3rem;
  gap: 0.55rem;
  justify-items: start;
}

.eco-pop-kicker {
  display: flex;
  margin: 0;
  color: #007aff;
  font-size: 0.72rem;
  font-weight: 800;
  gap: 0.5rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
}

.eco-pop-kicker em {
  color: rgba(17, 20, 17, 0.55);
  font-style: normal;
}

html.theme-dark .eco-pop-kicker em,
html.landing-text-dark .eco-pop-kicker em {
  color: rgba(247, 249, 255, 0.55);
}

.eco-pop-title {
  margin: 0;
  color: var(--ink, #111411);
  font-size: 1.45rem;
  font-weight: 800;
  letter-spacing: -0.015em;
}

html.theme-dark .eco-pop-title,
html.landing-text-dark .eco-pop-title {
  color: #f7f9ff;
}

.eco-pop-desc {
  margin: 0;
  color: rgba(17, 20, 17, 0.72);
  font-size: 0.95rem;
  line-height: 1.6;
}

html.theme-dark .eco-pop-desc,
html.landing-text-dark .eco-pop-desc {
  color: rgba(247, 249, 255, 0.72);
}

.eco-pop-cta {
  display: inline-flex;
  margin-top: 0.5rem;
  padding: 0.75rem 1.4rem;
  border-radius: 999px;
  background: linear-gradient(180deg, #2b93ff, #007aff);
  color: #fff;
  font-size: 0.9rem;
  font-weight: 800;
  text-decoration: none;
  box-shadow: 0 10px 24px rgba(0, 122, 255, 0.35);
}

/* Mini UI mockups on the workflow suite card faces: the SVG covers the
   visual area behind the title panel. */
.ecosystem-dom-thumb {
  position: absolute;
  inset: 0;
  overflow: hidden;
  border-radius: inherit;
}

.ecosystem-dom-thumb svg {
  display: block;
  width: 100%;
  height: 100%;
}

/* ---- the header on a very narrow phone ----
   At 320px the logo was cut about 8px off the left and the theme toggle the
   same off the right, on every page - and with overflow-x: clip neither could
   be scrolled into view, so they were simply gone. The padding and the bubbles
   give back the few pixels that costs. */
@media (max-width: 22.5rem) {
  .header-main {
    padding-left: 0.6rem;
    padding-right: 0.4rem;
    gap: 0.4rem;
  }
  /* The contents were overflowing their own container rather than the header
     being too wide: flex items refuse to shrink past their content unless told
     they may, so the brand sat 19px outside the pill it lives in and the page
     clips overflow-x, which meant it could not even be scrolled to. */
  .header-main > * { min-width: 0; }
  .brand { flex: 0 1 auto; overflow: hidden; }
  .brand img { flex: none; }
  .header-bubble { width: 2.9rem; height: 2.9rem; }
  .header-inspo { padding-left: 0.7rem; padding-right: 0.7rem; }
}
