/* ==========================================================================
   The six service modules.

   One board, six modules. Every card carries the same connector at the same
   height on both edges, the same grid behind it and the same pulse crossing
   it — so a row of them reads as one circuit rather than six illustrations.
   What differs is the middle: each module draws the thing its card describes,
   in blocks, because a block is what the rest of this design is made of.

   Each module takes its card's colour through `currentColor`, so the ramp
   that runs across the six headings runs through the six animations too.

   All of it is CSS. Nothing here is announced to a screen reader (the SVGs are
   aria-hidden), and every animation stops under prefers-reduced-motion.
   ========================================================================== */

.svc__art {
  /* Full-bleed against the card's padding: 24px top, 16px sides. */
  margin: calc(var(--s-5) * -1) calc(var(--s-4) * -1) var(--s-3);
  height: 72px;
  border-bottom: 1px solid var(--line);
  background:
    linear-gradient(180deg, rgba(120, 170, 255, .05), transparent),
    linear-gradient(rgba(120, 170, 255, .05) 1px, transparent 1px),
    linear-gradient(90deg, rgba(120, 170, 255, .05) 1px, transparent 1px);
  background-size: auto, 16px 16px, 16px 16px;
  color: var(--c, var(--accent));
  overflow: hidden;
}
.svc__art svg { width: 100%; height: 100%; display: block; }

/* Shared vocabulary ------------------------------------------------------- */
.svc__art rect, .svc__art circle { fill: currentColor; }
.svc__art path { fill: none; stroke: currentColor; }

/* the connector every module has on both sides */
.io-line { stroke-width: 1.2; opacity: .45; }
.io-pad  { opacity: .55; }
.io-pulse {
  stroke-width: 2.4; stroke-linecap: round;
  stroke-dasharray: 18 340; stroke-dashoffset: 358;
  opacity: .9;
  animation: io-run 9s linear infinite;
}
@keyframes io-run {
  0%   { stroke-dashoffset: 358; }
  70%  { stroke-dashoffset: 0; }
  100% { stroke-dashoffset: 0; opacity: 0; }
}
/* The signal crosses the six modules in order: card 1 sends, card 6 receives. */
.grid > .svc:nth-child(1) .io-pulse { animation-delay: 0s; }
.grid > .svc:nth-child(2) .io-pulse { animation-delay: 1.5s; }
.grid > .svc:nth-child(3) .io-pulse { animation-delay: 3s; }
.grid > .svc:nth-child(4) .io-pulse { animation-delay: 4.5s; }
.grid > .svc:nth-child(5) .io-pulse { animation-delay: 6s; }
.grid > .svc:nth-child(6) .io-pulse { animation-delay: 7.5s; }

/* 01 — agents: the run is a tree, so draw the tree ---------------------- */
.ag-root { opacity: .35; animation: ag-think 4.4s ease-in-out infinite; }
@keyframes ag-think { 0%, 100% { opacity: .35; } 30% { opacity: .8; } }
.ag-pin { opacity: .45; }

/* Branches draw themselves outward from the root, then the twigs, then the
   leaves light — a call tree expanding, one level at a time. */
.ag-branch, .ag-twig {
  stroke-width: 1.3; opacity: .25;
  stroke-dasharray: 120; stroke-dashoffset: 120;
  animation: ag-grow 4.4s ease-in-out infinite;
}
.ag-branch.b1 { animation-delay: .12s; } .ag-branch.b2 { animation-delay: .24s; }
.ag-twig { animation-name: ag-grow-late; }
.ag-twig.t1 { animation-delay: .12s; } .ag-twig.t2 { animation-delay: .24s; }
@keyframes ag-grow {
  0%       { stroke-dashoffset: 120; opacity: .15; }
  22%, 88% { stroke-dashoffset: 0;   opacity: .55; }
  100%     { stroke-dashoffset: 120; opacity: .15; }
}
@keyframes ag-grow-late {
  0%, 22%  { stroke-dashoffset: 120; opacity: .12; }
  42%, 88% { stroke-dashoffset: 0;   opacity: .45; }
  100%     { stroke-dashoffset: 120; opacity: .12; }
}
.ag-node { opacity: .3; animation: ag-hit 4.4s ease-in-out infinite; }
.ag-node.n1 { animation-delay: .12s; } .ag-node.n2 { animation-delay: .24s; }
@keyframes ag-hit { 0%, 20%, 100% { opacity: .3; } 34% { opacity: .95; } }
.ag-leaf { opacity: .25; animation: ag-leaf 4.4s ease-in-out infinite; }
.ag-leaf.l1 { animation-delay: .12s; } .ag-leaf.l2 { animation-delay: .24s; }
@keyframes ag-leaf { 0%, 44%, 100% { opacity: .25; } 58% { opacity: .9; } }
.svc__art .ag-return {
  stroke-width: 1.6; opacity: .3;
  stroke-dasharray: 12 44; stroke-dashoffset: 56;
  animation: ag-back 4.4s ease-in-out infinite;
}
@keyframes ag-back {
  0%, 58%  { stroke-dashoffset: 56; opacity: .2; }
  74%      { stroke-dashoffset: 0;  opacity: .95; }
  100%     { stroke-dashoffset: 0;  opacity: .2; }
}
.ag-out { opacity: .25; animation: ag-out 4.4s ease-in-out infinite; }
@keyframes ag-out { 0%, 68%, 100% { opacity: .25; } 82% { opacity: .95; } }

/* 02 — e-commerce: a cart, filled from the catalogue -------------------- */
.sh-tile  { opacity: .3; animation: sh-lift 5s ease-in-out infinite; }
.sh-tile.s1 { animation-delay: .5s; } .sh-tile.s2 { animation-delay: 1s; }
@keyframes sh-lift { 0%, 26%, 100% { opacity: .3; } 12% { opacity: .9; } }
.sh-shelf { opacity: .2; }

/* The cart itself: outline, so it reads as a cart and not as a filled box. */
.svc__art .sh-handle, .svc__art .sh-basket {
  fill: none; stroke: currentColor; stroke-width: 1.6;
  stroke-linecap: round; stroke-linejoin: round; opacity: .6;
}
.sh-wheel { opacity: .55; }
.sh-wheel.w1 { animation: sh-roll 5s ease-in-out infinite; }

/* Items fall from the catalogue into the basket. */
.sh-drop { opacity: 0; animation: sh-drop 5s ease-in-out infinite; }
.sh-drop.d1 { animation-delay: .45s; } .sh-drop.d2 { animation-delay: .9s; }
@keyframes sh-drop {
  0%, 10%  { opacity: 0;   transform: translateY(-12px); }
  20%      { opacity: .9;  transform: translateY(0); }
  34%      { opacity: .9;  transform: translateY(16px); }
  44%, 100%{ opacity: 0;   transform: translateY(18px); }
}
/* …and the total settles once they are in. */
.sh-total { opacity: .18; transform-origin: left center; animation: sh-total 5s ease-in-out infinite; }
@keyframes sh-total {
  0%, 44%  { transform: scaleX(.25); opacity: .18; }
  60%, 88% { transform: scaleX(1);   opacity: .5; }
  100%     { transform: scaleX(.25); opacity: .18; }
}
.svc__art .sh-check {
  fill: none; stroke: currentColor; stroke-width: 2.2;
  stroke-linecap: round; stroke-linejoin: round;
  opacity: 0; animation: sh-check 5s ease-in-out infinite;
}
@keyframes sh-check { 0%, 62%, 100% { opacity: 0; } 74%, 88% { opacity: .95; } }

/* 03 — generation: noise resolves into a tree and a flower -------------- */
.gn-px { animation: gn-noise 6s ease-in-out infinite; }
/* Everything that belongs to the picture resolves; the rest fades back out. */
.gn-px.is-in { animation-name: gn-form; }
@keyframes gn-noise {
  0%, 6%   { opacity: .4; }
  18%, 94% { opacity: .06; }
  100%     { opacity: .4; }
}
@keyframes gn-form {
  0%, 6%   { opacity: .34; }
  18%, 94% { opacity: 1; }
  100%     { opacity: .34; }
}
/* The canopy, the bloom and the ground settle at slightly different moments,
   which is what stops it reading as one flat stamp. */
.gn-px.is-tree   { animation-duration: 6s; }
.gn-px.is-flower { animation-duration: 6s; animation-delay: .35s; }
.gn-px.is-ground { animation-duration: 6s; animation-delay: .7s; }
.gn-px.is-ground { animation-name: gn-ground; }
@keyframes gn-ground { 0%, 6% { opacity: .3; } 18%, 94% { opacity: .45; } 100% { opacity: .3; } }
/* The bloom is the brightest thing in the picture, the way a flower is. */
.gn-px.is-bloom { animation-name: gn-bloom; }
@keyframes gn-bloom { 0%, 6% { opacity: .4; } 18%, 94% { opacity: 1; } 100% { opacity: .4; } }
.gn-px.n0 { animation-delay: 0s; }    .gn-px.n1 { animation-delay: .07s; }
.gn-px.n2 { animation-delay: .14s; }  .gn-px.n3 { animation-delay: .21s; }
.gn-px.n4 { animation-delay: .28s; }  .gn-px.n5 { animation-delay: .35s; }
.gn-px.n6 { animation-delay: .42s; }  .gn-px.n7 { animation-delay: .49s; }
.gn-px.n8 { animation-delay: .56s; }  .gn-px.n9 { animation-delay: .63s; }

/* 04 — vision: an eye that watches, tracks, and locks on ---------------- */
.svc__art .vs-lid   { fill: none; stroke: currentColor; stroke-width: 1.4; opacity: .5; }
.vs-iris-o { opacity: .3; }
.vs-pupil  { opacity: .95; }
.vs-glint  { opacity: .8; }
.svc__art .vs-focus { fill: none; stroke: currentColor; stroke-width: 1;
                      stroke-dasharray: 3 5; opacity: .35;
                      animation: vs-focus 5.6s ease-in-out infinite; }
@keyframes vs-focus {
  0%, 100% { opacity: .2;  transform: scale(1); }
  30%      { opacity: .55; transform: scale(.82); }
  70%      { opacity: .55; transform: scale(.82); }
}
/* The eye blinks once a loop. A detector that never blinks is a machine; the
   card says "human eye", so it blinks. */
.vs-eye { animation: vs-blink 5.6s ease-in-out infinite; }
@keyframes vs-blink {
  0%, 88%, 100% { transform: scaleY(1); }
  92%           { transform: scaleY(.06); }
  96%           { transform: scaleY(1); }
}
/* The iris goes where the box goes. */
.vs-iris { animation: vs-look 5.6s ease-in-out infinite; }
@keyframes vs-look {
  0%, 14%  { transform: translateX(-3px); }
  30%, 46% { transform: translateX(4px); }
  62%, 84% { transform: translateX(7px); }
  100%     { transform: translateX(-3px); }
}
.vs-thing { opacity: .22; animation: vs-seen 5.6s ease-in-out infinite; }
.vs-thing.t1 { animation-delay: 1.8s; }
.vs-thing.t2 { animation-delay: 3.5s; }
@keyframes vs-seen { 0%, 22%, 100% { opacity: .22; } 8% { opacity: .5; } }

/* The box walks from subject to subject, and the corners travel with it. */
.vs-lock { animation: vs-track 5.6s ease-in-out infinite; }
@keyframes vs-track {
  0%, 24%  { transform: translateX(0)    scale(1); opacity: 1; }
  32%      { transform: translateX(66px) scale(1); opacity: 1; }
  56%      { transform: translateX(66px) scale(1); opacity: 1; }
  64%      { transform: translateX(126px) scale(.62); opacity: 1; }
  86%      { transform: translateX(126px) scale(.62); opacity: 1; }
  92%      { transform: translateX(126px) scale(.62); opacity: 0; }
  100%     { transform: translateX(0)    scale(1); opacity: 0; }
}
.svc__art .vs-box { fill: none; stroke: currentColor; stroke-width: 1.4; opacity: .55; }
.vs-corner { stroke-width: 2.6; stroke-linecap: round; opacity: .95; }

/* 05 — analytics: events land, aggregate, answer ------------------------- */
.an-ev { opacity: .25; animation: an-in 4s ease-in-out infinite; }
.an-ev.e1 { animation-delay: .12s; } .an-ev.e2 { animation-delay: .24s; }
.an-ev.e3 { animation-delay: .36s; }
@keyframes an-in {
  0%       { opacity: .2; transform: translateX(0); }
  22%      { opacity: .9; transform: translateX(0); }
  42%, 100%{ opacity: 0;  transform: translateX(38px); }
}
.an-agg { opacity: .25; animation: an-fill 4s ease-in-out infinite; }
@keyframes an-fill { 0%, 30% { opacity: .18; } 48% { opacity: .6; } 100% { opacity: .18; } }
.an-bar {
  opacity: .75; transform-origin: center bottom;
  animation: an-grow 4s ease-out infinite;
}
.an-bar.b1 { animation-delay: .07s; } .an-bar.b2 { animation-delay: .14s; }
.an-bar.b3 { animation-delay: .21s; } .an-bar.b4 { animation-delay: .28s; }
.an-bar.b5 { animation-delay: .35s; }
@keyframes an-grow {
  0%       { transform: scaleY(.12); opacity: .35; }
  22%      { transform: scaleY(1);   opacity: .9; }
  88%      { transform: scaleY(1);   opacity: .9; }
  100%     { transform: scaleY(.12); opacity: .35; }
}
.an-target { stroke-width: 1; stroke-dasharray: 4 5; opacity: .35; }

/* 06 — apps and games: a session, and the loop people come back to ------ */
.svc__art .bc-dev { fill: none; stroke: currentColor; stroke-width: 1.4; opacity: .5; }
.bc-screen { opacity: .12; }
.bc-card   { opacity: .32; animation: bc-page 4.8s ease-in-out infinite; }
.bc-card.c1 { animation-delay: .3s; } .bc-card.c2 { animation-delay: .6s; }
@keyframes bc-page { 0%, 55%, 100% { opacity: .3; } 24% { opacity: .95; } }
.bc-tap { opacity: 0; animation: bc-tap 4.8s ease-out infinite; }
@keyframes bc-tap {
  0%, 8%  { opacity: 0; transform: scale(.4); }
  16%     { opacity: .9; transform: scale(1); }
  30%     { opacity: 0; transform: scale(1.9); }
  100%    { opacity: 0; transform: scale(1.9); }
}
.svc__art .bc-enter {
  stroke-width: 1.4; opacity: .3;
  stroke-dasharray: 10 40; stroke-dashoffset: 50;
  animation: bc-enter 4.8s ease-in-out infinite;
}
@keyframes bc-enter {
  0%, 14%  { stroke-dashoffset: 50; opacity: .2; }
  30%      { stroke-dashoffset: 0;  opacity: .9; }
  100%     { stroke-dashoffset: 0;  opacity: .2; }
}
/* The loop is the point: it never resolves, it comes round again. The dash and
   the gap add up to the path length (461), so the token wraps seamlessly. */
.svc__art .bc-loop { fill: none; stroke: currentColor; stroke-width: 1.2; opacity: .28; }
.svc__art .bc-token {
  fill: none; stroke: currentColor; stroke-width: 2.6; stroke-linecap: round;
  stroke-dasharray: 26 435.1;
  animation: bc-run 4.8s linear infinite;
}
@keyframes bc-run {
  from { stroke-dashoffset: 0; }
  to   { stroke-dashoffset: -461.1; }
}
/* Each station lights as the token passes it. */
.bc-stop { opacity: .25; animation: bc-stop 4.8s ease-in-out infinite; }
.bc-stop.s1 { animation-delay: 1.2s; }
.bc-stop.s2 { animation-delay: 2.4s; }
.bc-stop.s3 { animation-delay: 3.6s; }
@keyframes bc-stop { 0%, 22%, 100% { opacity: .25; } 8% { opacity: .95; } }

/* SVG children transform around the element box unless told otherwise; without
   this the translated pieces fly off to the corner of the viewBox. */
.svc__art rect, .svc__art circle, .svc__art path { transform-box: fill-box; }
.sh-total, .an-bar, .bc-ret { transform-box: fill-box; }

@media (prefers-reduced-motion: reduce) {
  .svc__art * { animation: none !important; }
  /* Leave every module in its finished state rather than mid-noise. */
  .gn-px.is-out { opacity: .12; }
  .gn-px.is-in  { opacity: .95; }
  .io-pulse { opacity: 0; }
  .sh-tile { opacity: .3; }
  .sh-drop { opacity: .7; transform: none; }
  .sh-check { opacity: .95; }
  .ag-branch, .ag-twig { stroke-dashoffset: 0; opacity: .5; }
  .an-bar { transform: none; }
  .sh-total { transform: none; }
  .vs-eye, .vs-iris, .vs-lock, .vs-focus { transform: none; }
  .vs-lock { opacity: 1; }
  .bc-token { opacity: 0; }
  .bc-stop { opacity: .5; }
}

/* ==========================================================================
   The board the six modules are mounted on.

   Same language as the hero, different job: there the traces radiate out of the
   wordmark, here they run in the gutters between the cards and out to the
   margins, so the six cards read as parts on one board. It sits behind the
   content and is masked away from the middle, where the text is.
   ========================================================================== */
.section--board { position: relative; overflow: hidden; }
.section--board > .wrap { position: relative; z-index: 1; }

.board {
  position: absolute; inset: 0; z-index: 0; pointer-events: none;
  color: #6FA8DC;
  opacity: 0;
  transition: opacity 1.4s var(--ease-out);
  /* Masked away from the heading and faded at the bottom, but full width
     across the grid: the cards are opaque, so the buses are hidden behind them
     and show only in the gutters — which is the point. A trace crossing a
     paragraph is noise; a trace between two cards is structure. */
  mask-image: linear-gradient(180deg, transparent 0%, transparent 16%, #000 30%,
                              #000 88%, transparent 100%);
  -webkit-mask-image: linear-gradient(180deg, transparent 0%, transparent 16%, #000 30%,
                              #000 88%, transparent 100%);
}
.is-ready .board { opacity: 1; }
.board svg { width: 100%; height: 100%; display: block; }

.bd-line  { fill: none; stroke: currentColor; stroke-width: 1; opacity: .18; }
.bd-pad   { fill: currentColor; opacity: .28; }
.bd-pulse {
  fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round;
  stroke-dasharray: 26 1600; stroke-dashoffset: 1626;
  opacity: .55;
  animation: bd-run 11s linear infinite;
}
@keyframes bd-run {
  0%   { stroke-dashoffset: 1626; }
  60%  { stroke-dashoffset: 0; }
  100% { stroke-dashoffset: 0; opacity: 0; }
}
.bd-pulses path:nth-child(6n+1) { animation-delay: 0s;   }
.bd-pulses path:nth-child(6n+2) { animation-delay: 1.8s; }
.bd-pulses path:nth-child(6n+3) { animation-delay: 3.6s; }
.bd-pulses path:nth-child(6n+4) { animation-delay: 5.4s; }
.bd-pulses path:nth-child(6n+5) { animation-delay: 7.2s; }
.bd-pulses path:nth-child(6n+6) { animation-delay: 9s;   }

@media (prefers-reduced-motion: reduce) {
  .bd-pulse { animation: none; opacity: 0; }
}
