/* ==========================================================================
   Define IT Solution — animations.css
   Motion system: reveals, diagrams, interaction.
   Palette sampled from the supplied logo. See assets/images/brand/README.md.
   ========================================================================== */

/* --------------------------------------------------------------------------
   14. REVEAL ANIMATION
   -------------------------------------------------------------------------- */
/* Scoped to .js, which is set by an inline script in <head>. If JavaScript
   fails or is disabled, nothing is ever hidden. */
.js [data-reveal] {
  opacity: 0;
  transform: translateY(var(--reveal-distance));
  transition: opacity 700ms var(--ease-out-premium), transform 700ms var(--ease-out-premium);
  will-change: opacity, transform;
}
.js [data-reveal="left"]  { transform: translateX(-62px); }
.js [data-reveal="right"] { transform: translateX(62px); }
.js [data-reveal="scale"] { transform: scale(0.94); }
.js [data-reveal="fade"]  { transform: none; }
@media (max-width: 860px) {
  .js [data-reveal="left"],
  .js [data-reveal="right"] { transform: translateY(var(--reveal-distance)); }
}
.js [data-reveal].is-visible { opacity: 1; transform: none; will-change: auto; }
[data-reveal-delay="1"] { transition-delay: 100ms; }
[data-reveal-delay="2"] { transition-delay: 200ms; }
[data-reveal-delay="3"] { transition-delay: 300ms; }
[data-reveal-delay="4"] { transition-delay: 400ms; }
[data-reveal-delay="5"] { transition-delay: 500ms; }
[data-reveal-delay="6"] { transition-delay: 600ms; }
[data-reveal-delay="7"] { transition-delay: 700ms; }

/* --------------------------------------------------------------------------
   19. TECHNOLOGY DIAGRAMS
   Motion here is decorative and additive: every diagram reads correctly as a
   static image, and each carries a <title> and <desc> for assistive tech.
   -------------------------------------------------------------------------- */
.tech-svg rect, .tech-svg text, .tech-svg ellipse { opacity: 0; }
.tech-panel.is-visible .tech-svg rect,
.tech-panel.is-visible .tech-svg text,
.tech-panel.is-visible .tech-svg ellipse,
.tech-visual.is-visible rect, .tech-visual.is-visible text, .tech-visual.is-visible ellipse {
  animation: techFade 700ms var(--ease-out-premium) 350ms forwards;
}
@keyframes techFade { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: none; } }

.tech-panel {
  position: relative;
  background: var(--ink-800);
  border: 1px solid var(--line-invert);
  border-radius: var(--r-lg);
  padding: clamp(1rem, 0.5rem + 2vw, 2rem);
  overflow: hidden;
  isolation: isolate;
}
.tech-panel::before {
  content: "";
  position: absolute; inset: 0; z-index: -1;
  background:
    radial-gradient(600px 320px at 80% 0%, rgba(30, 107, 255, 0.22), transparent 62%),
    linear-gradient(180deg, rgba(255, 255, 255, 0.03), transparent 45%);
}
.tech-svg { width: 100%; height: auto; display: block; }

/* Illuminated connection lines */
.tech-svg .flow { stroke-dasharray: 1; stroke-dashoffset: 1; }
.tech-panel.is-visible .tech-svg .flow,
.tech-visual.is-visible .flow {
  animation: drawIn 1200ms var(--ease-out-premium) forwards;
}
@keyframes drawIn { to { stroke-dashoffset: 0; } }
/* Slowly breathing nodes */
/* Nodes appear in sequence once the diagram is on screen, then breathe */
.tech-svg .node-pulse { opacity: 0; transform-origin: center; transform-box: fill-box; }
.tech-panel.is-visible .tech-svg .node-pulse,
.tech-visual.is-visible .node-pulse {
  animation: nodeIn 500ms var(--ease-out-premium) forwards,
             nodePulse 3.6s var(--ease) 1100ms infinite;
}
@keyframes nodeIn { from { opacity: 0; transform: scale(.3); } to { opacity: 1; transform: scale(1); } }
/* Expanding ring behind a focal element */
.tech-svg .pulse-ring {
  transform-origin: center;
  transform-box: fill-box;
  animation: pulseRing 3.2s var(--ease) infinite;
}
@keyframes pulseRing {
  0%   { transform: scale(0.72); opacity: 0.55; }
  70%  { transform: scale(1.12); opacity: 0; }
  100% { transform: scale(1.12); opacity: 0; }
}
/* Status indicator */
.tech-svg .status-dot,
.tech-svg .secure-dot { animation: statusPulse 2.8s var(--ease) infinite; }
@keyframes statusPulse {
  0%, 100% { opacity: 0.35; }
  45%      { opacity: 1; }
}
/* Scanning sweep across the estate */
.tech-svg .scan-line {
  transform-origin: center;
  animation: scanSweep 6s var(--ease) infinite;
}
@keyframes scanSweep {
  0%, 100% { transform: translateY(-46px); opacity: 0; }
  12%      { opacity: 0.85; }
  50%      { transform: translateY(52px); opacity: 0.85; }
  88%      { opacity: 0; }
}
/* Monitoring sparklines */
.tech-svg .spark { animation: sparkPulse 4.2s var(--ease) infinite; transform-origin: bottom; }
@keyframes sparkPulse {
  0%, 100% { opacity: 0.28; }
  50%      { opacity: 0.85; }
}
/* Ecosystem category nodes */
.tech-svg .eco-node { animation: ecoNode 4.4s var(--ease) infinite; }
@keyframes ecoNode {
  0%, 100% { opacity: 0.72; }
  50%      { opacity: 1; }
}
.tech-svg .pkt { opacity: 0; }
.tech-panel.is-visible .tech-svg .pkt,
.tech-visual.is-visible .pkt { animation: pktIn 400ms var(--ease) 1100ms forwards; }
@keyframes pktIn { to { opacity: 1; } }

@media (prefers-reduced-motion: reduce) {
  .tech-svg .flow { animation: none; stroke-dasharray: none; stroke-dashoffset: 0; }
  .tech-svg rect, .tech-svg text, .tech-svg ellipse,
  .tech-svg .node-pulse, .tech-svg .pkt { opacity: 1; animation: none; transform: none; }
  .tech-svg .node-pulse,
  .tech-svg .status-dot,
  .tech-svg .secure-dot,
  .tech-svg .spark,
  .tech-svg .eco-node { animation: none; opacity: 1; }
  .tech-svg .pulse-ring,
  .tech-svg .scan-line,
  .tech-svg .pkt { display: none; }   /* removes animated data paths entirely */
}

/* Diagram beside page copy */
.tech-feature {
  display: grid;
  gap: clamp(2rem, 1rem + 4vw, 4rem);
  align-items: center;
  grid-template-columns: minmax(0, 1fr);
}
@media (min-width: 900px) {
  .tech-feature { grid-template-columns: minmax(0, 1fr) minmax(0, 1.05fr); }
}

/* --------------------------------------------------------------------------
   23. SCROLL PROGRESS
   -------------------------------------------------------------------------- */
.scroll-progress {
  position: fixed;
  top: 0; left: 0;
  height: 2px;
  width: 100%;
  transform: scaleX(0);
  transform-origin: 0 50%;
  background: linear-gradient(90deg, var(--azure-400), var(--azure-300));
  z-index: 300;
  pointer-events: none;
}
@media (prefers-reduced-motion: reduce) { .scroll-progress { display: none; } }

/* --------------------------------------------------------------------------
   24. HERO INTRODUCTION SEQUENCE
   Eyebrow, headline, copy, buttons, then the graphic. Under 1.5s total.
   Content is visible by default; only the .js class opts into the sequence.
   -------------------------------------------------------------------------- */
.js .hero-seq > * { opacity: 0; }
.js .hero-seq > *:nth-child(1) { animation: heroRise 800ms var(--ease-out-premium) 0ms forwards; }
.js .hero-seq > *:nth-child(2) { animation: heroRise 800ms var(--ease-out-premium) 150ms forwards; }
.js .hero-seq > *:nth-child(3) { animation: heroRise 800ms var(--ease-out-premium) 450ms forwards; }
.js .hero-seq > *:nth-child(4) { animation: heroRise 800ms var(--ease-out-premium) 600ms forwards; }
.js .hero-seq > *:nth-child(5) { animation: heroRise 800ms var(--ease-out-premium) 750ms forwards; }
.js .hero__visual { opacity: 0; animation: heroScale 1000ms var(--ease-out-premium) 850ms forwards; }
@keyframes heroRise { from { opacity: 0; transform: translateY(46px); } to { opacity: 1; transform: none; } }
@keyframes heroScale { from { opacity: 0; transform: scale(.94) translateY(30px); } to { opacity: 1; transform: none; } }

/* Line-by-line heading reveal. JS wraps each rendered line in an
   overflow-hidden mask; the inner span rises out of it. */
.line-mask { display: block; overflow: hidden; padding-bottom: 0.06em; }
.line-mask > span { display: block; transform: translateY(105%); opacity: 0; }
.js .line-mask > span {
  transition: transform 900ms var(--ease-out-premium), opacity 900ms var(--ease-out-premium);
}
.is-lines-in .line-mask > span,
.hero-seq .line-mask > span { transform: none; opacity: 1; }
.js .hero-seq h1 .line-mask > span { transform: translateY(105%); opacity: 0; }
.js .hero-seq.is-lines-in h1 .line-mask > span { transform: none; opacity: 1; }
.js .hero-seq.is-lines-in h1 .line-mask:nth-child(1) > span { transition-delay: 150ms; }
.js .hero-seq.is-lines-in h1 .line-mask:nth-child(2) > span { transition-delay: 260ms; }
.js .hero-seq.is-lines-in h1 .line-mask:nth-child(3) > span { transition-delay: 370ms; }
.js [data-split-lines]:not(.is-lines-in) .line-mask > span { transform: translateY(105%); opacity: 0; }
.js [data-split-lines].is-lines-in .line-mask:nth-child(1) > span { transition-delay: 0ms; }
.js [data-split-lines].is-lines-in .line-mask:nth-child(2) > span { transition-delay: 110ms; }
.js [data-split-lines].is-lines-in .line-mask:nth-child(3) > span { transition-delay: 220ms; }
@media (prefers-reduced-motion: reduce) {
  .js .hero-seq > *, .js .hero__visual { opacity: 1; animation: none; transform: none; }
}

/* --------------------------------------------------------------------------
   25. CARD AND ICON INTERACTION
   -------------------------------------------------------------------------- */
.card:hover, .industry-card:hover, .vendor-card:hover {
  border-color: rgba(36, 183, 232, 0.45);
  box-shadow: var(--glow);
}
.card:hover .card__icon {
  background: rgba(36, 183, 232, 0.18);
  border-color: rgba(36, 183, 232, 0.5);
  transform: translateY(-2px);
}
/* Icons animate only on hover, and only the stroke geometry moves */
.card__icon svg { transition: transform var(--dur) var(--ease); }
.card:hover .card__icon svg { transform: scale(1.06); }

/* --------------------------------------------------------------------------
   26. SIGNAL / ILLUSTRATION SYSTEM
   Shared motion vocabulary for the SVG technology illustrations.
   -------------------------------------------------------------------------- */
.tech-svg .sig-arc {
  transform-box: fill-box;
  transform-origin: center;
  animation: sigArc 3.6s var(--ease) infinite;
  opacity: 0;
}
/* Staggered so at least one arc is always mid-flight */
.tech-svg .sig-arc-2 { animation-delay: 1.2s; }
.tech-svg .sig-arc-3 { animation-delay: 2.4s; }
@keyframes sigArc {
  0%   { opacity: 0;   transform: scale(.68); }
  14%  { opacity: 1;   }
  58%  { opacity: .85; }
  88%  { opacity: 0;   transform: scale(1.18); }
  100% { opacity: 0;   transform: scale(1.18); }
}

/* A path that illuminates once the packet has passed it */
.tech-svg .path-secure {
  stroke-dasharray: 200;
  stroke-dashoffset: 200;
  animation: pathSecure 5.2s var(--ease) infinite;
}
@keyframes pathSecure {
  0%, 12%  { stroke-dashoffset: 200; opacity: .25; }
  46%      { stroke-dashoffset: 0; opacity: 1; }
  84%      { stroke-dashoffset: 0; opacity: 1; }
  100%     { stroke-dashoffset: 0; opacity: .25; }
}

/* Rack / cluster indicator lights */
.tech-svg .rack-led { animation: statusPulse 3.4s var(--ease) infinite; }
.tech-svg .rack-led:nth-of-type(2n) { animation-delay: .7s; }
.tech-svg .rack-led:nth-of-type(3n) { animation-delay: 1.4s; }

/* Soft glow under illustration strokes */
.tech-svg .glow { filter: drop-shadow(0 0 6px rgba(56, 201, 245, 0.45)); }

@media (prefers-reduced-motion: reduce) {
  .tech-svg .sig-arc { animation: none; opacity: .5; }
  .tech-svg .path-secure { animation: none; stroke-dashoffset: 0; opacity: 1; }
  .tech-svg .rack-led { animation: none; opacity: 1; }
  .tech-svg .glow { filter: none; }
}

/* Mobile: drop the finest detail so illustrations stay legible */
@media (max-width: 620px) {
  .tech-svg .detail-fine { display: none; }
  .tech-panel { padding: var(--sp-4); }
  .has-glow::after { width: 100%; opacity: .7; }
}

/* --------------------------------------------------------------------------
   28. MOTION SYSTEM — interaction layer
   -------------------------------------------------------------------------- */

/* Animated accent rule: draws across when its section enters */
.rule-draw {
  position: relative;
  height: 1px;
  background: var(--line);
  overflow: hidden;
  margin-block: var(--sp-6);
}
.rule-draw::after {
  content: "";
  position: absolute; inset: 0;
  background: linear-gradient(90deg, var(--azure-400), transparent);
  transform: scaleX(0);
  transform-origin: 0 50%;
  transition: transform 1100ms var(--ease-out-premium);
}
.rule-draw.is-visible::after { transform: scaleX(1); }

.divider-label::after { transform-origin: 0 50%; transition: transform 900ms var(--ease-out-premium); }
.js .divider-label:not(.is-visible)::after { transform: scaleX(0); }

/* Cards: a firmer lift, brighter border, stronger glow, arrow travel */
.card, .industry-card, .vendor-card {
  transition: transform 450ms var(--ease-out-premium),
              border-color 300ms var(--ease),
              box-shadow 400ms var(--ease);
}
/* The selectors below are deliberately specific. A revealed card matches
   `.js [data-reveal].is-visible { transform: none }`, which outranks a plain
   `.card:hover` and would silently cancel the hover lift. */
.card:hover, .industry-card:hover, .vendor-card:hover,
.js .card[data-reveal].is-visible:hover,
.js .industry-card[data-reveal].is-visible:hover,
.js .vendor-card[data-reveal].is-visible:hover {
  transform: translateY(-6px);
  border-color: rgba(0, 195, 255, 0.7);
  box-shadow: 0 0 0 1px rgba(0, 202, 253, 0.28), 0 20px 46px rgba(30, 109, 245, 0.22);
}
.card .link-arrow { transition: color 250ms var(--ease); }
.card .link-arrow::after { transition: transform 350ms var(--ease-out-premium); }
/* Icon lift, glow and Explore travel live in §35, with the rest of the
   service-card interaction. */

/* Partner logos */
.partner-carousel__item .vendor-logo { transition: transform 400ms var(--ease-out-premium), filter 400ms var(--ease); }
.partner-carousel__item:hover .vendor-logo,
.partner-carousel__item:focus-visible .vendor-logo {
  transform: translateY(-4px);
  filter: brightness(1.35);
}
.partner-carousel__item { position: relative; }
.partner-carousel__item::before {
  content: "";
  position: absolute; inset: 12% 8%;
  border-radius: 50%;
  background: radial-gradient(circle, rgba(36, 183, 232, 0.22), transparent 68%);
  opacity: 0;
  transition: opacity 400ms var(--ease);
  pointer-events: none;
}
.partner-carousel__item:hover::before,
.partner-carousel__item:focus-visible::before { opacity: 1; }

/* Buttons */
.btn { transition: background 250ms var(--ease), border-color 250ms var(--ease),
                   color 250ms var(--ease), transform 250ms var(--ease-out-premium),
                   box-shadow 300ms var(--ease); }
.btn--arrow::after { transition: transform 350ms var(--ease-out-premium); }
.btn--arrow:hover::after { transform: translateX(6px); }
.btn--secondary:hover { box-shadow: 0 0 0 1px rgba(36, 183, 232, 0.35); }

/* Sticky header reacts to scroll */
.site-header {
  background: rgba(5, 5, 5, 0.55);
  border-bottom-color: transparent;
  transition: background 400ms var(--ease), backdrop-filter 400ms var(--ease),
              border-color 400ms var(--ease), box-shadow 400ms var(--ease);
}
.site-header.is-stuck {
  background: rgba(5, 5, 5, 0.9);
  backdrop-filter: saturate(160%) blur(18px);
  -webkit-backdrop-filter: saturate(160%) blur(18px);
  border-bottom-color: rgba(255, 255, 255, 0.1);
  box-shadow: 0 10px 34px rgba(0, 0, 0, 0.6);
}
.site-header.is-stuck .site-header__inner { min-height: var(--header-h-stuck); }
.site-header__inner { transition: min-height 350ms var(--ease); }

/* Active navigation indicator */
.primary-nav__list { position: relative; }
.nav-indicator {
  position: absolute;
  bottom: 2px; left: 0;
  height: 2px;
  width: 0;
  border-radius: 2px;
  background: var(--azure-400);
  opacity: 0;
  transition: transform 420ms var(--ease-out-premium), width 420ms var(--ease-out-premium),
              opacity 250ms var(--ease);
  pointer-events: none;
}
.nav-indicator.is-on { opacity: 1; }

/* Mega menu: drops further, columns stagger */
.mega { transform: translateX(-50%) translateY(-14px); transition:
        opacity 250ms var(--ease), transform 250ms var(--ease-out-premium), visibility 250ms; }
.mega__grid > div { opacity: 0; transform: translateY(10px); }
.mega[data-open="true"] .mega__grid > div {
  animation: megaCol 380ms var(--ease-out-premium) forwards;
}
.mega[data-open="true"] .mega__grid > div:nth-child(2) { animation-delay: 60ms; }
.mega[data-open="true"] .mega__grid > div:nth-child(3) { animation-delay: 120ms; }
.mega[data-open="true"] .mega__grid > div:nth-child(4) { animation-delay: 180ms; }
.mega[data-open="true"] .mega__grid > div:nth-child(5) { animation-delay: 240ms; }
@keyframes megaCol { to { opacity: 1; transform: none; } }

/* Mobile drawer links reveal in sequence */
.mobile-nav__body > .mobile-nav__group { opacity: 0; transform: translateX(26px); }
.mobile-nav[data-open="true"] .mobile-nav__body > .mobile-nav__group {
  animation: drawerItem 480ms var(--ease-out-premium) forwards;
}
.mobile-nav[data-open="true"] .mobile-nav__body > .mobile-nav__group:nth-child(1) { animation-delay: 120ms; }
.mobile-nav[data-open="true"] .mobile-nav__body > .mobile-nav__group:nth-child(2) { animation-delay: 175ms; }
.mobile-nav[data-open="true"] .mobile-nav__body > .mobile-nav__group:nth-child(3) { animation-delay: 230ms; }
.mobile-nav[data-open="true"] .mobile-nav__body > .mobile-nav__group:nth-child(4) { animation-delay: 285ms; }
.mobile-nav[data-open="true"] .mobile-nav__body > .mobile-nav__group:nth-child(5) { animation-delay: 340ms; }
.mobile-nav[data-open="true"] .mobile-nav__body > .mobile-nav__group:nth-child(6) { animation-delay: 395ms; }
@keyframes drawerItem { to { opacity: 1; transform: none; } }

/* Page entrance.
   Opacity only, deliberately. A transform on <body> makes it the containing
   block for position: fixed descendants, which detaches the mobile drawer from
   the viewport and reintroduces horizontal overflow. */
.js body { animation: pageIn 420ms var(--ease-out-premium) both; }
@keyframes pageIn { from { opacity: 0; } to { opacity: 1; } }

/* Ambient background drift */
.has-glow::after { animation: glowDrift 22s ease-in-out infinite alternate; }
@keyframes glowDrift {
  from { transform: translate3d(0, 0, 0) scale(1); }
  to   { transform: translate3d(-5%, 4%, 0) scale(1.12); }
}
.has-grid::before { animation: gridBreathe 14s ease-in-out infinite alternate; }
@keyframes gridBreathe { from { opacity: .55; } to { opacity: 1; } }

/* Parallax targets — JS sets --py */
[data-parallax] { transform: translate3d(0, var(--py, 0px), 0); will-change: transform; }

/* Footer reveal */
.js .site-footer__brand,
.js .site-footer__nav > div {
  opacity: 0; transform: translateY(38px);
  transition: opacity 700ms var(--ease-out-premium), transform 700ms var(--ease-out-premium);
}
.site-footer.is-visible .site-footer__brand,
.site-footer.is-visible .site-footer__nav > div { opacity: 1; transform: none; }
.site-footer.is-visible .site-footer__nav > div:nth-child(1) { transition-delay: 100ms; }
.site-footer.is-visible .site-footer__nav > div:nth-child(2) { transition-delay: 200ms; }
.site-footer.is-visible .site-footer__nav > div:nth-child(3) { transition-delay: 300ms; }
.site-footer.is-visible .site-footer__nav > div:nth-child(4) { transition-delay: 400ms; }

/* Magnetic CTA — desktop pointer only, JS sets --mx/--my */
@media (hover: hover) and (pointer: fine) {
  [data-magnetic] { transition: transform 300ms var(--ease-out-premium); }
  [data-magnetic].is-pulled {
    transform: translate3d(var(--mx, 0px), var(--my, 0px), 0);
    transition: transform 120ms linear;
  }
}

@media (prefers-reduced-motion: reduce) {
  .js body { animation: none; }
  .has-glow::after, .has-grid::before { animation: none; }
  .rule-draw::after, .js .divider-label::after { transform: scaleX(1); transition: none; }
  .line-mask > span { transform: none !important; opacity: 1 !important; transition: none; }
  .mega__grid > div,
  .mobile-nav__body > .mobile-nav__group { opacity: 1; transform: none; animation: none; }
  .js .site-footer__brand, .js .site-footer__nav > div { opacity: 1; transform: none; transition: none; }
  [data-parallax] { transform: none; }
  .card:hover, .industry-card:hover, .vendor-card:hover,
  .js .card[data-reveal].is-visible:hover,
  .js .industry-card[data-reveal].is-visible:hover,
  .js .vendor-card[data-reveal].is-visible:hover { transform: none; }
  .partner-carousel__item:hover .vendor-logo { transform: none; }
  .nav-indicator { transition: none; }
}

/* --------------------------------------------------------------------------
   29. HERO NETWORK BACKGROUND
   A living mesh behind the hero copy. Kept at low opacity and masked away
   from the headline so it never competes with the text.
   -------------------------------------------------------------------------- */
.hero__bg {
  position: absolute;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  opacity: .55;
  -webkit-mask-image: radial-gradient(ellipse 70% 65% at 72% 45%, #000 20%, transparent 78%);
  mask-image: radial-gradient(ellipse 70% 65% at 72% 45%, #000 20%, transparent 78%);
}
.hero__bg-svg { width: 100%; height: 100%; display: block; }

.mesh-link { stroke-opacity: .12; }
.mesh-lit { animation: meshLit 6s var(--ease) infinite; }
@keyframes meshLit {
  0%, 100% { stroke-opacity: .1; }
  45%      { stroke-opacity: .7; }
}
.mesh-node {
  transform-box: fill-box; transform-origin: center;
  animation: meshNode 5s var(--ease) infinite;
}
@keyframes meshNode {
  0%, 100% { opacity: .25; transform: scale(.85); }
  50%      { opacity: .95; transform: scale(1.15); }
}
.mesh-pkt { opacity: .8; filter: drop-shadow(0 0 5px rgba(56, 201, 245, .6)); }

@media (max-width: 860px) { .hero__bg { opacity: .3; } }
@media (prefers-reduced-motion: reduce) {
  .mesh-lit, .mesh-node { animation: none; }
  .mesh-node { opacity: .6; }
  .mesh-pkt { display: none; }
}

/* --------------------------------------------------------------------------
   30. MASK REVEAL + STAGGER UTILITY
   -------------------------------------------------------------------------- */
.js [data-reveal="mask"] {
  opacity: 1;
  transform: none;
  position: relative;
  overflow: hidden;
}
.js [data-reveal="mask"]::after {
  content: "";
  position: absolute; inset: 0;
  background: linear-gradient(120deg, var(--ink-900) 40%, var(--azure-600));
  transform-origin: 100% 50%;
  transition: transform 900ms var(--ease-out-premium);
  z-index: 3;
  pointer-events: none;
}
.js [data-reveal="mask"] > * { opacity: 0; transition: opacity 500ms var(--ease) 320ms; }
.js [data-reveal="mask"].is-visible::after { transform: scaleX(0); }
.js [data-reveal="mask"].is-visible > * { opacity: 1; }

/* Scale reveal for major visuals */
.js [data-reveal="zoom"] { opacity: 0; transform: scale(1.08); }
.js [data-reveal="zoom"].is-visible { opacity: 1; transform: none; }

@media (prefers-reduced-motion: reduce) {
  .js [data-reveal="mask"]::after { display: none; }
  .js [data-reveal="mask"] > * { opacity: 1; transition: none; }
  .js [data-reveal="zoom"] { opacity: 1; transform: none; }
}

/* ==========================================================================
   31. SERVICE CARD ICON MOTION
   The solution pillars are one shared component — article.card holding
   span.icon-fx > svg.icon-NAME — so everything below applies wherever
   that component is used.

   Reusable behaviour vocabulary, combined per service further down:
     .ic-draw    the stroke draws itself on
     .ic-signal  the element illuminates in sequence
     .ic-status  an indicator LED
     .ic-pulse   a soft breathing pulse
     .ic-packet  a lit dot travels along the path
     .ic-scan    a line sweeps down through the icon
     .ic-dN      build-sequence delay step, N = 1..8

   Two states, both set on .icon-fx by motion.js:
     .is-playing   the one-shot build sequence — on reveal, hover and focus
     (no class)    the low-intensity idle loop

   Every element rests in its finished state, so with JavaScript off — or
   with motion reduced — the icon simply renders complete. Only transform,
   opacity and SVG stroke properties are animated, so card heights never
   move.
   ========================================================================== */
.icon-fx { position: relative; }
.icon-fx svg { overflow: visible; position: relative; z-index: 1; }
.icon-fx svg * { transform-box: fill-box; transform-origin: 50% 50%; }

/* Build-sequence delay steps. Consumed by the animations below through
   var(--ic-delay), so one declaration serves every behaviour. */
.ic-d1 { --ic-delay:  70ms; }
.ic-d2 { --ic-delay: 140ms; }
.ic-d3 { --ic-delay: 210ms; }
.ic-d4 { --ic-delay: 280ms; }
.ic-d5 { --ic-delay: 350ms; }
.ic-d6 { --ic-delay: 420ms; }
.ic-d7 { --ic-delay: 490ms; }
.ic-d8 { --ic-delay: 560ms; }

/* --- .ic-draw ------------------------------------------------------------
   Every animated path carries pathLength="100", so one dash length works
   for strokes of any real length. */
.icon-fx .ic-draw { stroke-dasharray: 100; stroke-dashoffset: 0; }
.icon-fx.is-playing .ic-draw {
  animation: icDraw 640ms var(--ease-out-premium) var(--ic-delay, 0ms) both;
}
@keyframes icDraw { from { stroke-dashoffset: 100; } to { stroke-dashoffset: 0; } }

/* --- .ic-signal ---------------------------------------------------------- */
.icon-fx.is-playing .ic-signal {
  animation: icSignal 520ms var(--ease-out-premium) var(--ic-delay, 0ms) both;
}
@keyframes icSignal {
  0%   { opacity: 0; transform: scale(.5); }
  55%  { opacity: 1; transform: scale(1.3); }
  100% { opacity: 1; transform: scale(1); }
}

/* --- .ic-status ---------------------------------------------------------- */
.icon-fx .ic-status { opacity: .9; }
.icon-fx.is-playing .ic-status {
  animation: icStatus 620ms var(--ease) var(--ic-delay, 0ms) both;
}
@keyframes icStatus {
  0%   { opacity: .12; transform: scale(.4); }
  45%  { opacity: 1;   transform: scale(1.7); }
  100% { opacity: .9;  transform: scale(1); }
}

/* --- .ic-pulse ----------------------------------------------------------- */
.icon-fx.is-playing .ic-pulse {
  animation: icPulse 760ms var(--ease) calc(var(--ic-delay, 0ms) + 200ms) 2 both;
}
.icon-fx:not(.is-playing) .ic-pulse {
  animation: icPulse 2.8s var(--ease) infinite;
}
@keyframes icPulse {
  0%, 100% { opacity: .8; transform: scale(1); }
  50%      { opacity: 1;  transform: scale(1.6); }
}

/* --- .ic-packet ----------------------------------------------------------
   A near-zero dash with a round cap reads as a lit dot; moving the dash
   offset walks it along the path. --ic-loop varies the idle period per
   service so the eight cards never beat in time with each other. */
.icon-fx .ic-packet {
  stroke: var(--define-cyan, #00CAFD);
  stroke-width: 3.4;
  stroke-linecap: round;
  stroke-dasharray: 0.5 200;
  stroke-dashoffset: 0;
  opacity: 0;
  filter: drop-shadow(0 0 3px rgba(0, 202, 253, .95));
}
.icon-fx.is-playing .ic-packet {
  animation: icPacket 900ms var(--ease) 700ms both;
}
.icon-fx:not(.is-playing) .ic-packet {
  animation: icPacketLoop var(--ic-loop, 4.2s) var(--ease) infinite;
}
@keyframes icPacket {
  0%   { stroke-dashoffset: 0;    opacity: 0; }
  12%  { opacity: 1; }
  85%  { opacity: 1; }
  100% { stroke-dashoffset: -100; opacity: 0; }
}
@keyframes icPacketLoop {
  0%,  8%   { stroke-dashoffset: 0;    opacity: 0; }
  16%       { opacity: 1; }
  44%       { opacity: 1; }
  52%, 100% { stroke-dashoffset: -100; opacity: 0; }
}

/* --- .ic-scan ------------------------------------------------------------
   Descends through the icon, narrowing as the shape narrows. */
.icon-fx .ic-scan {
  opacity: 0;
  stroke: var(--define-cyan, #00CAFD);
  stroke-width: 1.5;
}
@keyframes icScan {
  0%   { opacity: 0;   transform: translateY(0) scaleX(.94); }
  15%  { opacity: .95; }
  78%  { opacity: .95; }
  100% { opacity: 0;   transform: translateY(10px) scaleX(.34); }
}
@keyframes icScanLoop {
  0%,  6%   { opacity: 0;  transform: translateY(0) scaleX(.94); }
  13%       { opacity: .9; }
  27%       { opacity: .9; }
  34%, 100% { opacity: 0;  transform: translateY(10px) scaleX(.34); }
}

/* --------------------------------------------------------------------------
   Per service — the vocabulary above, choreographed
   -------------------------------------------------------------------------- */

/* Managed IT — monitoring sweep, sequential status marks, centre pulse */
.icon-fx .ic-sweep { stroke-dasharray: 24 76; stroke-width: 1.2; opacity: .42; }
.icon-fx.is-playing .ic-sweep { animation: icSweep 1300ms linear both; }
.icon-fx:not(.is-playing) .ic-sweep { animation: icSweep 6s linear infinite; }
@keyframes icSweep { from { transform: rotate(0deg); } to { transform: rotate(360deg); } }

/* Cloud — outline draws, a data point rises in, the cloud answers */
.icon-fx .ic-rise { opacity: 0; }
.icon-fx.is-playing .ic-rise { animation: icRise 900ms var(--ease-out-premium) 420ms both; }
.icon-fx:not(.is-playing) .ic-rise { animation: icRiseLoop 4.6s var(--ease) infinite; }
@keyframes icRise {
  0%   { opacity: 0; transform: translateY(0) scale(.5); }
  22%  { opacity: 1; transform: translateY(-2px) scale(1); }
  68%  { opacity: 1; transform: translateY(-6.5px) scale(1); }
  100% { opacity: 0; transform: translateY(-8.5px) scale(.4); }
}
@keyframes icRiseLoop {
  0%,  4%   { opacity: 0; transform: translateY(0) scale(.5); }
  14%       { opacity: 1; transform: translateY(-2px) scale(1); }
  38%       { opacity: 1; transform: translateY(-6.5px) scale(1); }
  48%, 100% { opacity: 0; transform: translateY(-8.5px) scale(.4); }
}
.icon-fx.is-playing .ic-cloud {
  animation: icDraw 640ms var(--ease-out-premium) both,
             icCloudGlow 720ms var(--ease) 820ms both;
}
.icon-fx:not(.is-playing) .ic-cloud { animation: icCloudBreathe 4.6s var(--ease) infinite; }
@keyframes icCloudGlow {
  0%, 100% { opacity: 1; filter: none; }
  50%      { opacity: .62; filter: drop-shadow(0 0 5px rgba(0, 202, 253, .95)); }
}
@keyframes icCloudBreathe {
  0%, 24%, 100% { opacity: 1; }
  40%           { opacity: .58; }
}

/* Cybersecurity — scan, verify, then a brief protected glow */
.icon-fx .ic-shield-glow { opacity: 0; stroke: var(--define-cyan, #00CAFD); stroke-width: 2.4; }
.icon-fx.is-playing .ic-scan { animation: icScan 820ms var(--ease) 300ms both; }
.icon-fx:not(.is-playing) .ic-scan { animation: icScanLoop 5.2s var(--ease) infinite; }
.icon-fx.is-playing .ic-check { animation: icDraw 460ms var(--ease-out-premium) 980ms both; }
.icon-fx.is-playing .ic-shield-glow { animation: icShieldGlow 720ms var(--ease) 1100ms both; }
@keyframes icShieldGlow {
  0%   { opacity: 0;   transform: scale(1); }
  35%  { opacity: .85; }
  100% { opacity: 0;   transform: scale(1.14); }
}

/* Enterprise infrastructure — racks, indicators, data between the sections */
.icon-fx .ic-link { opacity: .3; }
.icon-fx:not(.is-playing) .ic-led--live { animation: icLedLoop 2.9s var(--ease) infinite; }
@keyframes icLedLoop {
  0%, 52%, 100% { opacity: .35; transform: scale(1); }
  68%           { opacity: 1;   transform: scale(1.5); }
}

/* Networking — the hub fires first, then the links, then the outer nodes */
.icon-fx.is-playing .ic-node--hub {
  animation: icDraw 520ms var(--ease-out-premium) both,
             icPop 640ms var(--ease-out-premium) both;
}
.icon-fx:not(.is-playing) .ic-node--hub { animation: icHubLoop 3.4s var(--ease) infinite; }
@keyframes icPop {
  0%   { transform: scale(.3); }
  62%  { transform: scale(1.28); }
  100% { transform: scale(1); }
}
@keyframes icHubLoop {
  0%, 100% { opacity: .85; transform: scale(1); }
  42%      { opacity: 1;   transform: scale(1.22); }
}
.icon-network { --ic-loop: 3.4s; }

/* Microsoft solutions — frame, chrome, interior panel, sync indicator */
.icon-fx .ic-panel { opacity: .5; }
.icon-fx .ic-sync { opacity: .85; }
.icon-fx.is-playing .ic-panel { animation: icPanel 620ms var(--ease-out-premium) 480ms both; }
@keyframes icPanel {
  0%   { opacity: 0;  transform: translateX(-4px) scaleX(.45); }
  100% { opacity: .5; transform: none; }
}
.icon-fx.is-playing .ic-sync { animation: icSync 940ms var(--ease) 760ms both; }
.icon-fx:not(.is-playing) .ic-sync { animation: icSyncLoop 3.6s var(--ease) infinite; }
@keyframes icSync {
  0%   { opacity: 0;   transform: translateX(3px) scale(.3); }
  40%  { opacity: 1;   transform: translateX(0) scale(1.6); }
  70%  { opacity: 1;   transform: translateX(-1.6px) scale(1); }
  100% { opacity: .85; transform: none; }
}
@keyframes icSyncLoop {
  0%, 58%, 100% { opacity: .8; transform: none; }
  72%           { opacity: 1;  transform: translateX(-2.2px) scale(1.35); }
  86%           { opacity: 1;  transform: translateX(1.2px) scale(1.05); }
}

/* Backup & DR — a data vessel inside a recovery ring.

   Five beats, and the order is the message: the data exists, copies leave
   it, the ring closes around it, one copy comes back, the vessel confirms.

   Two things are worth knowing before changing this.

   First, the structure rests fully drawn and only the data moves on the idle
   loop. An idle loop that re-drew the vessel and ring every few seconds
   would have to snap them back to nothing at the loop seam — on eight cards
   at once, with no way to hide it. So the full build belongs to .is-playing,
   which motion.js fires on reveal, hover and focus, i.e. exactly when
   somebody is looking. The loop keeps the card alive in between.

   Second, the arrowhead is not spun separately. It is wound back 300deg —
   the arc's own sweep — and released in step with the ring drawing, so it
   rides the growing tip and comes to rest at the arc's end. That is what
   makes the ring read as one continuous rotation rather than a line that
   grows with a decoration stuck on the front. */
/* ic-guard, not ic-ring, and ic-inner, not ic-core: the gear icon already
   owns both of those names, and an equal-specificity rule further down this
   file would quietly take its inner ring's draw animation away from it. */
.icon-fx .ic-vessel,
.icon-fx .ic-guard { stroke-dasharray: 100; stroke-dashoffset: 0; }
.icon-fx .ic-blk  { fill: var(--define-cyan, #00CAFD); stroke: none; opacity: 0; }
/* Rotate about the ring's centre, not the arrowhead's own box — the shared
   .icon-fx svg * rule sets fill-box/50% 50%, which would spin it in place. */
.icon-fx .ic-head { transform-box: view-box; transform-origin: 12px 12px; }

/* The one-shot build, ~4.6s. --ic-play stops motion.js clearing .is-playing
   at its 1.9s default and cutting the sequence off after beat two. */
.icon-fx.is-playing .ic-vessel {
  animation: icDraw 720ms var(--ease-out-premium) var(--ic-delay, 0ms) both;
}
/* The confirm pulse lights the contents, not the container — the top ellipse
   and the middle shelf, never the outer body. Swapping the whole vessel to
   cyan reads as an alert; lighting the two inner arcs reads as the data being
   verified, which is the beat this is for. Both animations have to live in
   one declaration (a second `animation` rule would replace the draw rather
   than add to it), hence the extra class rather than a separate selector. */
.icon-fx.is-playing .ic-vessel.ic-inner {
  animation: icDraw 720ms var(--ease-out-premium) var(--ic-delay, 0ms) both,
             icVesselPulse 640ms var(--ease) 3960ms both;
}
.icon-fx.is-playing .ic-guard  { animation: icGuardDraw 1180ms var(--ease-out-premium) 1900ms both; }
.icon-fx.is-playing .ic-head   { animation: icHeadRide 1180ms var(--ease-out-premium) 1900ms both; }
.icon-fx.is-playing .ic-blk--1 { animation: icBlkOut 880ms var(--ease) 1180ms both; }
.icon-fx.is-playing .ic-blk--2 { animation: icBlkOut 880ms var(--ease) 1440ms both; }
.icon-fx.is-playing .ic-blk--3 { animation: icBlkOut 880ms var(--ease) 1700ms both; }
.icon-fx.is-playing .ic-blk--back { animation: icBlkBack 940ms var(--ease) 3120ms both; }

/* The idle loop: structure holds, data keeps moving. */
.icon-fx:not(.is-playing) .ic-blk--1    { animation: icBlkOutLoop  var(--ic-loop, 5.2s) var(--ease) infinite; }
.icon-fx:not(.is-playing) .ic-blk--2    { animation: icBlkOutLoop  var(--ic-loop, 5.2s) var(--ease) 260ms infinite; }
.icon-fx:not(.is-playing) .ic-blk--3    { animation: icBlkOutLoop  var(--ic-loop, 5.2s) var(--ease) 520ms infinite; }
.icon-fx:not(.is-playing) .ic-blk--back { animation: icBlkBackLoop var(--ic-loop, 5.2s) var(--ease) infinite; }
.icon-fx:not(.is-playing) .ic-vessel.ic-inner { animation: icVesselPulseLoop var(--ic-loop, 5.2s) var(--ease) infinite; }

@keyframes icVesselPulse {
  0%, 100% { stroke: currentColor; }
  38%      { stroke: var(--define-cyan, #00CAFD); }
}
/* Held at opacity 0 for the first frames: a fully offset dash still paints a
   round line-cap dot at the start point, which otherwise sits on the card as
   a stray speck for the whole first half of the sequence. */
@keyframes icGuardDraw {
  0%   { stroke-dashoffset: 100; opacity: 0; }
  7%   { opacity: 1; }
  100% { stroke-dashoffset: 0; opacity: 1; }
}
@keyframes icHeadRide {
  0%   { opacity: 0; transform: rotate(-300deg); }
  9%   { opacity: 1; }
  100% { opacity: 1; transform: rotate(0deg); }
}
@keyframes icBlkOut {
  0%   { opacity: 0;   transform: translateX(-.7px); }
  20%  { opacity: .95; }
  100% { opacity: 0;   transform: translateX(6.4px); }
}
@keyframes icBlkBack {
  0%   { opacity: 0; transform: translateX(6.4px); }
  18%  { opacity: 1; }
  80%  { opacity: 1; transform: translateX(0); }
  100% { opacity: 0; transform: translateX(-.7px); }
}
/* Loop variants carve the same moves out of one shared period, so the three
   outbound blocks stay in step with the returning one and the pulse. Every
   loop begins and ends at opacity 0, which is what makes the seam invisible. */
@keyframes icBlkOutLoop {
  0%, 6%    { opacity: 0;  transform: translateX(-.7px); }
  11%       { opacity: .9; }
  24%, 100% { opacity: 0;  transform: translateX(6.4px); }
}
@keyframes icBlkBackLoop {
  0%, 44%   { opacity: 0; transform: translateX(6.4px); }
  50%       { opacity: 1; }
  64%       { opacity: 1; transform: translateX(0); }
  68%, 100% { opacity: 0; transform: translateX(-.7px); }
}
@keyframes icVesselPulseLoop {
  0%, 68%, 100% { stroke: currentColor; }
  74%           { stroke: var(--define-cyan, #00CAFD); }
}
.icon-restore { --ic-loop: 5.2s; --ic-play: 5.2s; }

/* Digital solutions — < then / then > */
.icon-fx .ic-bracket,
.icon-fx .ic-slash { stroke-dasharray: 100; stroke-dashoffset: 0; }
.icon-fx.is-playing .ic-bracket--l {
  animation: icDraw 440ms var(--ease-out-premium) both,
             icSlideL 500ms var(--ease-out-premium) both;
}
.icon-fx.is-playing .ic-slash { animation: icDraw 420ms var(--ease-out-premium) 280ms both; }
.icon-fx.is-playing .ic-bracket--r {
  animation: icDraw 440ms var(--ease-out-premium) 560ms both,
             icSlideR 500ms var(--ease-out-premium) 560ms both;
}
.icon-fx:not(.is-playing) .ic-slash { animation: icSlashLoop 3.2s var(--ease) infinite; }
@keyframes icSlideL { from { transform: translateX(-5px); opacity: 0; } to { transform: none; opacity: 1; } }
@keyframes icSlideR { from { transform: translateX(5px);  opacity: 0; } to { transform: none; opacity: 1; } }
@keyframes icSlashLoop {
  0%, 60%, 100% { opacity: 1; }
  70%, 80%      { opacity: .22; }
}

@media (prefers-reduced-motion: reduce) {
  .icon-fx svg,
  .icon-fx svg *,
  .icon-fx.is-playing svg * { animation: none !important; transform: none !important; }
  .icon-fx .ic-draw,
  .icon-fx .ic-bracket,
  .icon-fx .ic-vessel,
  .icon-fx .ic-guard,
  .icon-fx .ic-slash { stroke-dashoffset: 0 !important; }
  .icon-fx .ic-packet,
  .icon-fx .ic-scan,
  .icon-fx .ic-rise,
  .icon-fx .ic-shield-glow { opacity: 0 !important; }
  /* The backup icon rests as vessel + ring + one data block. The other three
     blocks sit at the same coordinates and are only separated in time, so
     showing them all here would stack them into a single blob. */
  .icon-fx .ic-blk--1 { opacity: .85 !important; }
}

/* ==========================================================================
   35. SERVICE CARD ENTRANCE AND INTERACTION
   ========================================================================== */

/* Entrance — 45px rise over 680ms. The per-row stagger delay is written to
   --stagger-delay by motion.js, so the second row of a 4-up grid restarts
   at 0ms instead of carrying on to 700ms. */
.js .card[data-reveal] {
  transition: opacity 680ms var(--ease-out-premium), transform 680ms var(--ease-out-premium);
}
.js .card[data-reveal]:not(.is-visible) { transform: translateY(45px); }

/* Inside the card: icon, heading, description, Explore — 0 / 80 / 150 / 220ms
   after the card itself begins. */
.js .card[data-reveal] > .card__icon {
  opacity: 0;
  transition: opacity 460ms var(--ease-out-premium),
              transform 320ms var(--ease-out-premium),
              background 320ms var(--ease),
              border-color 320ms var(--ease),
              box-shadow 320ms var(--ease);
}
.js .card[data-reveal].is-visible > .card__icon {
  opacity: 1;
  transition: opacity 460ms var(--ease-out-premium) var(--stagger-delay, 0ms),
              transform 320ms var(--ease-out-premium),
              background 320ms var(--ease),
              border-color 320ms var(--ease),
              box-shadow 320ms var(--ease);
}
.js .card[data-reveal] > h3,
.js .card[data-reveal] > p,
.js .card[data-reveal] > .link-arrow {
  opacity: 0;
  transform: translateY(14px);
  transition: opacity 520ms var(--ease-out-premium), transform 520ms var(--ease-out-premium);
}
.js .card[data-reveal].is-visible > h3 {
  opacity: 1; transform: none;
  transition-delay: calc(var(--stagger-delay, 0ms) + 80ms);
}
.js .card[data-reveal].is-visible > p {
  opacity: 1; transform: none;
  transition-delay: calc(var(--stagger-delay, 0ms) + 150ms);
}
.js .card[data-reveal].is-visible > .link-arrow {
  opacity: 1; transform: none;
  transition: opacity 520ms var(--ease-out-premium) calc(var(--stagger-delay, 0ms) + 220ms),
              transform 520ms var(--ease-out-premium) calc(var(--stagger-delay, 0ms) + 220ms),
              color 250ms var(--ease);
}

/* Top edge — a cyan → blue → violet line runs across on hover and retracts
   when the pointer leaves. .card::before carries the gradient (styles.css). */
.card::before { transition: transform 520ms var(--ease-out-premium), box-shadow 300ms var(--ease); }
.card:hover::before { box-shadow: 0 0 14px rgba(0, 202, 253, .55); }

/* Icon container — brighter border, a soft radial glow, a 2px lift */
.card__icon::after {
  content: "";
  position: absolute;
  inset: -10px;
  border-radius: 50%;
  background: radial-gradient(circle, rgba(0, 202, 253, .34), transparent 70%);
  opacity: 0;
  transition: opacity 340ms var(--ease);
  pointer-events: none;
  z-index: 0;
}
.card:hover .card__icon::after { opacity: 1; }
.card:hover .card__icon {
  transform: translateY(-2px);
  border-color: rgba(0, 202, 253, .72);
  background: var(--define-gradient-soft);
}
.card:hover .card__icon svg { transform: scale(1.04); }

/* Explore — the arrow travels 5px and the label brightens */
.card:hover .link-arrow { color: #66E0FF; }
.card:hover .link-arrow::after { transform: translateX(5px); }

@media (prefers-reduced-motion: reduce) {
  .js .card[data-reveal] > .card__icon,
  .js .card[data-reveal] > .solution-sphere,
  .js .card[data-reveal] > h3,
  .js .card[data-reveal] > p,
  .js .card[data-reveal] > .link-arrow { opacity: 1; transform: none; }
  .js .card[data-reveal]:not(.is-visible) { transform: none; }
  .card:hover .card__icon { transform: none; }
  .card:hover .card__icon::after { opacity: 0; }
}

/* ==========================================================================
   36. SOLUTION SPHERE MOTION
   The globe itself is drawn by assets/js/solution-spheres.js — the particle
   build, rotation, scan rings and travellers all live there. What is left
   here is how the block joins the card: it reveals with the tile, and it
   answers on hover along with the centre icon and the halo.
   ========================================================================== */
.js .card[data-reveal] > .solution-sphere {
  opacity: 0;
  transition: opacity 620ms var(--ease-out-premium);
}
.js .card[data-reveal].is-visible > .solution-sphere {
  opacity: 1;
  transition-delay: var(--stagger-delay, 0ms);
}

/* The pseudo-element is the dark ground under the dust, so hover opens it
   out very slightly rather than brightening it — the answer on hover comes
   from the particles themselves, which lift and speed up in the renderer. */
.solution-sphere::before { transition: transform 620ms var(--ease-out-premium); }
.card:hover .solution-sphere::before { transform: translate(-50%, -50%) scale(1.05); }

.solution-sphere__icon {
  transition: transform 420ms var(--ease-out-premium), filter 420ms var(--ease);
}

/* The centre glyph is white here rather than cyan, so the few decorative
   strokes that read as accents in the small icon box are tinted back to
   cyan — otherwise they look like stray grey marks at this size. */
.solution-sphere__icon .ic-sweep { stroke: var(--define-cyan); opacity: .32; }
.solution-sphere__icon .ic-link { stroke: var(--define-cyan); opacity: .42; }
.solution-sphere__icon .ic-panel { stroke: var(--define-cyan); opacity: .62; }
.card:hover .solution-sphere__icon {
  transform: scale(1.07);
  filter: drop-shadow(0 0 18px rgba(0, 200, 248, .8))
          drop-shadow(0 0 5px rgba(0, 108, 255, .75));
}

@media (prefers-reduced-motion: reduce) {
  .card:hover .solution-sphere__icon { transform: none; }
  .card:hover .solution-sphere::before { transform: translate(-50%, -50%); }
}

/* --------------------------------------------------------------------------
   32. ANIMATED BRACKET MOTIF
   The eyebrow brackets draw outward when the label enters view.
   -------------------------------------------------------------------------- */
.js .eyebrow::before,
.js .eyebrow::after {
  display: inline-block;
  opacity: 0;
  transform: translateX(6px);
  transition: opacity 450ms var(--ease), transform 450ms var(--ease-out-premium);
}
.js .eyebrow::before { transform: translateX(-6px); }
.js .eyebrow.is-visible::before,
.js .eyebrow.is-visible::after { opacity: .55; transform: none; }
@media (prefers-reduced-motion: reduce) {
  .js .eyebrow::before, .js .eyebrow::after { opacity: .55; transform: none; transition: none; }
}

/* --------------------------------------------------------------------------
   33. SECTION NAVIGATOR (long solution pages)
   -------------------------------------------------------------------------- */
.section-nav {
  position: fixed;
  left: clamp(12px, 2vw, 28px);
  top: 50%;
  transform: translateY(-50%);
  z-index: 40;
  display: none;
  list-style: none;
  margin: 0; padding: 0;
}
@media (min-width: 1400px) { .section-nav { display: block; } }
.section-nav li { margin: 0 0 var(--sp-4); }
.section-nav a {
  display: flex; align-items: center; gap: 0.6rem;
  font-family: var(--font-mono);
  font-size: 0.6875rem;
  letter-spacing: 0.1em;
  color: var(--grey-500);
  transition: color 300ms var(--ease);
}
.section-nav a::before {
  content: "";
  width: 8px; height: 8px;
  border-radius: 50%;
  border: 1px solid var(--grey-400);
  transition: background 300ms var(--ease), border-color 300ms var(--ease),
              transform 300ms var(--ease-out-premium);
}
.section-nav a:hover { color: var(--azure-300); }
.section-nav a.is-active { color: var(--azure-300); }
.section-nav a.is-active::before {
  background: var(--azure-400);
  border-color: var(--azure-400);
  transform: scale(1.5);
  box-shadow: 0 0 10px rgba(36, 183, 232, .7);
}
.section-nav__label { opacity: 0; transform: translateX(-6px); transition: opacity 300ms var(--ease), transform 300ms var(--ease); }
.section-nav:hover .section-nav__label,
.section-nav a.is-active .section-nav__label { opacity: 1; transform: none; }

/* --------------------------------------------------------------------------
   34. FOOTER NETWORK LINE
   -------------------------------------------------------------------------- */
.footer-line { position: absolute; inset: 0; z-index: -1; pointer-events: none; opacity: .5; }
.footer-line path {
  stroke-dasharray: 1; stroke-dashoffset: 1;
  transition: stroke-dashoffset 2200ms var(--ease-out-premium);
}
.site-footer.is-visible .footer-line path { stroke-dashoffset: 0; }
.footer-line circle { opacity: 0; transition: opacity 600ms var(--ease) 900ms; }
.site-footer.is-visible .footer-line circle { opacity: .8; }
@media (prefers-reduced-motion: reduce) {
  .footer-line path { stroke-dashoffset: 0; transition: none; }
  .footer-line circle { opacity: .8; transition: none; }
}

/* Counter — only used for verified figures */
.counter { font-variant-numeric: tabular-nums; }

