/* Sentient orb, region R2: index.html body (#products, #insights, #credibility, #services, #contact).
   Loads after style.css and the page's inline <style>. Every selector carries an .orb-body-* hook.
   Shared classes are overridden only through such a hook. No !important. Palette: :root tokens only. */

/* 1. Sections: transparent over the document gradient (style.css:89), one stacking context each,
      overflow: clip (never hidden) so view() timelines are not frozen. */
#products.orb-body-section,
#insights.orb-body-section,
#credibility.orb-body-section,
#services.orb-body-section,
#contact.orb-body-section {
  background: transparent;
  position: relative;
  isolation: isolate;
  overflow: clip;
}
#products.orb-body-section::before { content: none; }

/* 2. Light pools: one static radial per section, brightens on entry. */
/* Item 5 (fix A, approved 2026-09-07): the pool box used to start at -10%, above the section, so
   `overflow: clip` cut its gradient with a straight line exactly at the boundary and the lit state
   removed the only softening. The box now sits inside the section and a vertical mask fades it out
   at both edges, the same treatment the services texture below already uses and the geometry the
   echorb pools measure clean with. Ellipse centres move down ~10% to follow the box. */
.orb-body-section > .orb-body-pool {
  position: absolute;
  inset: 0 -20% auto -20%;
  height: 70%;
  -webkit-mask-image: linear-gradient(to bottom, transparent 0, #000 22%, #000 78%, transparent 100%);
  mask-image: linear-gradient(to bottom, transparent 0, #000 22%, #000 78%, transparent 100%);
  z-index: -1;
  pointer-events: none;
  opacity: 0.35;
  transform: translateY(6%);
  transition: opacity 900ms ease, transform 900ms ease;
}
.orb-body-section-products > .orb-body-pool {
  background:
    radial-gradient(ellipse 55% 60% at 22% 40%, rgba(255, 107, 0, 0.22), transparent 62%),
    radial-gradient(ellipse 50% 55% at 82% 65%, rgba(0, 212, 255, 0.20), transparent 62%);
}
.orb-body-section-insights > .orb-body-pool {
  background:
    radial-gradient(ellipse 60% 60% at 50% 45%, rgba(0, 212, 255, 0.20), transparent 62%),
    radial-gradient(ellipse 40% 50% at 12% 88%, rgba(153, 69, 255, 0.18), transparent 62%);
}
.orb-body-section-credibility > .orb-body-pool {
  background:
    radial-gradient(ellipse 50% 60% at 25% 55%, rgba(255, 0, 255, 0.22), transparent 62%),
    radial-gradient(ellipse 45% 50% at 85% 40%, rgba(0, 212, 255, 0.18), transparent 62%);
}
.orb-body-section-services > .orb-body-pool {
  background: radial-gradient(ellipse 60% 55% at 50% 30%, rgba(153, 69, 255, 0.24), transparent 62%);
}
.orb-body-section-contact > .orb-body-pool {
  background:
    radial-gradient(ellipse 55% 60% at 50% 40%, rgba(0, 212, 255, 0.20), transparent 62%),
    radial-gradient(ellipse 45% 50% at 15% 80%, rgba(255, 0, 153, 0.16), transparent 62%);
}
.orb-body-section.is-lit > .orb-body-pool { opacity: 1; transform: translateY(0); }

/* 3. Services: the hero's world returns in colour. The fade mask sits on a static, section-sized container;
      the 124% tile plane moves inside it, so both section edges always fade out whatever the drift. */
.orb-body-section-services > .orb-body-texture {
  position: absolute;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  -webkit-mask-image: linear-gradient(to bottom, transparent, #000 15%, #000 85%, transparent);
  mask-image: linear-gradient(to bottom, transparent, #000 15%, #000 85%, transparent);
}
/* ONE image, not a tile. This was `center / 60% repeat`, which is a whole painting used as
   wallpaper: at 60% it repeated several times across a desktop viewport and the seams read. A place
   does not repeat. It is now framed to the painting's own width and anchored above the frame, so
   neither its top nor its bottom edge can enter the section, and the drift moves it inside that
   overhang. The class name is left alone because other rules and the JS reference it. */
/* This one COVERS its section, which is a deliberate exception to the framing rule the rest of the
   site follows, taken on Giovanni's word after seeing both options side by side.

   The rule, that a painting is framed to its WIDTH and never cropped to fill, exists because the
   valley has a horizon and a floor: crop it and the horizon lands in the wrong place. This section's
   art is abstract plasma. There is no horizon to get wrong, so cropping costs nothing here.

   What it fixes, measured: the tile was locked to a 3:2 box at width 100%, so its height followed
   the VIEWPORT while the section's height followed its CONTENT. At 1440x900 the painting stopped
   698px into a 1753px section and two of the four cards sat on flat colour; on a 390 phone it
   stopped after 37px of 1979 and all four did. */
.orb-body-section-services .orb-body-texture-tile {
  position: absolute;
  left: 0;
  right: 0;
  /* 10% of slack at each end, because this tile DRIFTS: orb-body-drift moves it by 9.6% of its own
     height across the section's pass. Sized exactly to the section it left a 168px strip bare at
     one end or the other depending on where you had scrolled to, which is the same fault in a new
     costume.

     14% and not 10%, because the travel is 9.6% of the TILE's height and the tile grows with the
     slack, so the two chase each other: slack s must satisfy s >= 0.096 * (1 + 2s), which is 11.9%.
     10% left 22px bare at one end, measured across the pass rather than at one scroll position. */
  top: -14%;
  bottom: -14%;
  height: auto;
  background: url("../../../images/sentient/sentient2-1536.webp") center / cover no-repeat;
  opacity: 0.85;
  transform: translate3d(0, 0, 0);
}
.orb-body-section-services > .orb-body-mid {
  position: absolute;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  background:
    radial-gradient(ellipse 70% 38% at 50% 12%, rgba(10, 0, 21, 0.62), transparent 72%),
    radial-gradient(ellipse 45% 40% at 20% 30%, rgba(255, 0, 255, 0.35), transparent 65%),
    radial-gradient(ellipse 45% 40% at 80% 70%, rgba(0, 212, 255, 0.30), transparent 65%);
}
.orb-body-section-services .section-subtitle { text-shadow: 0 1px 12px rgba(10, 0, 21, 0.9); }

/* 4. Foreground planes: tint floor 0.72 under light text, static elevation, glass only where allowed. */
.orb-body-section .orb-body-plane {
  position: relative;
  isolation: isolate;
  background: rgba(10, 0, 21, var(--orb-body-tint, 0.72));
  box-shadow: 0 4px 20px rgba(0, 0, 0, 0.35), 0 0 0 1px rgba(255, 0, 255, 0.06) inset;
  -webkit-backdrop-filter: none;
  backdrop-filter: none;
}
.orb-body-section .orb-body-plane:hover {
  box-shadow: 0 14px 44px rgba(0, 0, 0, 0.45), 0 18px 52px rgba(255, 0, 255, 0.20);
}
@supports (backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px)) {
  .orb-body-section .orb-body-plane.is-glass {
    -webkit-backdrop-filter: blur(16px) saturate(1.3);
    backdrop-filter: blur(16px) saturate(1.3);
  }
}
@media (max-width: 767px) {
  .orb-body-section .orb-body-plane.is-glass:not(.orb-body-glass-sm) {
    -webkit-backdrop-filter: none;
    backdrop-filter: none;
  }
}
.orb-body-section .orb-body-contact { box-shadow: 0 4px 20px rgba(0, 0, 0, 0.35); }
.orb-body-section .orb-body-contact h3::before { content: none; }

/* Services cards: keep the shared reveal rule (style.css:1712) from hiding them without JS,
   and let the region choreograph the entrance instead of the rotateX drop. */
html:not([data-motion-js]) .orb-body-section .orb-body-service { opacity: 1; }
html:not([data-motion-js]) .orb-body-section .orb-body-service:not(:hover) { transform: none; }

/* Heading clip fix at Bootstrap lg (992 to 1199): .service-card padding 2.5rem + h3 1.5rem Orbitron
   overflowed the 216px column and overflow: hidden cut "WEB DEVELOPMENT". */
@media (min-width: 992px) and (max-width: 1199px) {
  .orb-body-section .orb-body-service { padding: 2rem 1.25rem; }
  .orb-body-section .orb-body-service h3 {
    font-size: clamp(1.05rem, 1.55vw, 1.35rem);
    overflow-wrap: anywhere;
    letter-spacing: 0;
  }
}
/* At 1200 and up "DEVELOPMENT" (239px with the shared 2px tracking) still overruns the 224px h3 box into the
   card padding; tracking 0 brings it to 217px. */
@media (min-width: 1200px) {
  .orb-body-section .orb-body-service h3 { letter-spacing: 0; overflow-wrap: anywhere; }
}
/* Same clip at phone widths ("DEVELOPMENT" is wider than a 214px card interior at 1.5rem). */
@media (max-width: 419px) {
  .orb-body-section .orb-body-service { padding: 2rem 1.25rem; }
  .orb-body-section .orb-body-service h3 {
    font-size: clamp(1.05rem, 6vw, 1.35rem);
    overflow-wrap: anywhere;
    letter-spacing: 0;
  }
}

/* 5. Reveal choreography: one observer per section, children stagger by --i. Hidden only when JS ran
      and motion is not still, so no-JS visitors see everything. Fill backwards, so hover transforms
      on the shared card classes keep working after the entrance. */
html[data-motion-js]:not([data-motion="still"]) .orb-body-section:not(.is-in) .orb-body-reveal {
  opacity: 0;
  transform: translateY(18px);
}
html[data-motion-js]:not([data-motion="still"]) .orb-body-section.is-in .orb-body-reveal {
  animation: orb-body-rise 800ms cubic-bezier(0.2, 0.7, 0.2, 1) backwards;
  animation-delay: calc(var(--i, 0) * 90ms);
}
@keyframes orb-body-rise {
  from { opacity: 0; transform: translateY(18px); }
  to { opacity: 1; transform: translateY(0); }
}

/* 6. Scroll-driven motion (Chrome 115+, Safari 26): pools brighten over entry, the Services texture
      tile drifts +9.6% to -9.6% of its own height (11.9% of the section) across the section's cover range. Declared after the
      shorthand. Firefox stable takes the JS fallback in body.js. */
@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    html:not([data-motion="still"]) .orb-body-section > .orb-body-pool {
      transition: none;
      animation: orb-body-pool-in linear both;
      animation-timeline: view();
      animation-range: entry 0% cover 35%;
    }
    html:not([data-motion="still"]) #services.orb-body-section { view-timeline: --orb-body-services block; }
    html:not([data-motion="still"]) .orb-body-section-services .orb-body-texture-tile {
      animation: orb-body-drift linear both;
      animation-timeline: --orb-body-services;
      animation-range: cover 0% cover 100%;
    }
  }
}
@keyframes orb-body-pool-in {
  from { opacity: 0.35; transform: translateY(6%); }
  to { opacity: 1; transform: translateY(0); }
}
@keyframes orb-body-drift {
  from { transform: translate3d(0, 9.6%, 0); }
  to { transform: translate3d(0, -9.6%, 0); }
}

/* 7. Off-screen budget: the two infinite animations inside this region (the About logo's hue-rotating
      border, index.html inline; iconFloat on the Services icons, style.css:1759) run only while their
      section is on screen. */
.orb-body-section-credibility:not(.is-on) .profile-image-wrapper::before { animation-play-state: paused; }
.orb-body-section-services:not(.is-on) .orb-body-service i { animation-play-state: paused; }

/* 8. Still composition: identical under the OS setting and the site control. Pools lit, texture at
      mid-travel, planes static, everything visible, the hue-rotating border and the icon float held. */
@media (prefers-reduced-motion: reduce) {
  html:not([data-motion="full"]) .orb-body-section > .orb-body-pool,
  html:not([data-motion="full"]) .orb-body-section.is-lit > .orb-body-pool { opacity: 1; transform: none; transition: none; animation: none; }
  html:not([data-motion="full"]) .orb-body-section-services .orb-body-texture-tile { transform: none; animation: none; }
  html:not([data-motion="full"]) .orb-body-section .orb-body-reveal { opacity: 1; transform: none; animation: none; }
  html:not([data-motion="full"]) .orb-body-section .orb-body-plane,
  html:not([data-motion="full"]) .orb-body-section .orb-body-plane:hover { transform: none; transition: border-color 0.3s ease, box-shadow 0.3s ease; }
  html:not([data-motion="full"]) .orb-body-section-credibility .profile-image-wrapper::before,
  html:not([data-motion="full"]) .orb-body-section .orb-body-service i { animation-play-state: paused; }
  /* Item 6: style.css sweeps .service-card::before across the card on hover, a left transition that
     Chrome scores as a layout shift under reduce. Hold it at its resting position. */
  html:not([data-motion="full"]) .orb-body-section .service-card::before,
  html:not([data-motion="full"]) .orb-body-section .service-card:hover::before { transition: none; transform: none; animation: none; }
}
html[data-motion="still"] .orb-body-section > .orb-body-pool,
html[data-motion="still"] .orb-body-section.is-lit > .orb-body-pool { opacity: 1; transform: none; transition: none; animation: none; }
html[data-motion="still"] .orb-body-section-services .orb-body-texture-tile { transform: none; animation: none; }
html[data-motion="still"] .orb-body-section .orb-body-reveal { opacity: 1; transform: none; animation: none; }
html[data-motion="still"] .orb-body-section .orb-body-plane,
html[data-motion="still"] .orb-body-section .orb-body-plane:hover { transform: none; transition: border-color 0.3s ease, box-shadow 0.3s ease; }
html[data-motion="still"] .orb-body-section-credibility .profile-image-wrapper::before,
html[data-motion="still"] .orb-body-section .orb-body-service i { animation-play-state: paused; }
/* Item 6, still composition: same hold for the shared services-card hover sweep. */
html[data-motion="still"] .orb-body-section .service-card::before,
html[data-motion="still"] .orb-body-section .service-card:hover::before { transition: none; transform: none; animation: none; }

/* 9. Narrow viewports: the 480 tile, pools at 60% height as above, first product card keeps its glass. */
@media (max-width: 767px) {
  .orb-body-section-services .orb-body-texture-tile {
    background-image: url("../../../images/sentient/sentient2-tile-768.webp");
  }
}
