/* Switchboard — home page "What we build" band: the three-layer stack.

   Renders OUTSIDE the scaled 1440 stage, in the slot straight after the
   Solutions band, so everything here is native-size with its own
   responsive rules. Same arrangement the Solutions band uses; the
   fitStage() script only shifts the first non-stage child after a stage,
   so two native-size sections in a row lay out correctly.

   The figure is an exploded isometric drawing on the same axes as the
   home hero (X = 0.866,0.5  Z = 0.866,-0.5  Y = 0,1). It is generated
   from measured plane coordinates, not eyeballed: every link between
   planes is a true vertical because its two ends share one (a,b) plane
   coordinate. Colour carries the meaning inside the middle plane, so
   there are no labels on the nodes to collide with a link tip: blue is
   AI, yellow is a rule, salmon is the step that stays with a person.
   The only text in the drawing is the three layer numbers. */

.stk {
  background: var(--bg);
  padding: 110px var(--pad, 24px) 120px;
}
.stk__inner { max-width: 1160px; margin: 0 auto; }

.stk__head { max-width: 940px; margin: 0 auto; text-align: center; }
.stk__title {
  font: 500 50px/1.14 "Neue Montreal", system-ui, sans-serif;
  letter-spacing: -0.018em;
  color: #000;
  margin: 14px 0 0;
  text-wrap: balance;
}
.stk__title em { font-style: normal; color: var(--blue, #397ae7); }
/* Each sentence is its own inline-block, so a headline or punch line that
   has to wrap breaks BETWEEN sentences rather than mid-clause. A clause
   too wide for the container still wraps inside itself, so this never
   causes overflow the way white-space: nowrap did. */
.stk__cl { display: inline-block; }
/* the head is wide enough for the headline's second clause to hold one
   line; the lead is capped narrower so its measure stays readable */
.stk__lead {
  max-width: 820px;
  font: 400 19px/1.55 "Neue Montreal", system-ui, sans-serif;
  color: var(--muted, #656567);
  margin: 18px auto 0;
  text-wrap: pretty;
}
/* below this the head is narrower than the 50px headline needs */
@media (max-width: 1120px) { .stk__title { font-size: 40px; } }

/* ---- figure + copy ---- */
.stk__body {
  margin-top: 64px;
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: 56px;
  align-items: stretch;
}
.stk__fig { margin: 0; }
.stk__svg {
  display: block;
  width: 100%;
  /* never larger than the drawing's design size; below that it simply
     shrinks with its column, so the figure scales monotonically */
  max-width: 520px;
  height: auto;
  margin: 0 auto;
  overflow: visible;
}
.stk-sr {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0;
}

.stk__copy {
  display: flex;
  flex-direction: column;
  justify-content: space-evenly;
  gap: 8px;
}
.stk__layer {
  position: relative;
  padding: 20px 22px 20px 26px;
  border-radius: 10px;
  border: 1.5px solid transparent;
  background: transparent;
  transition: background .3s var(--ease), border-color .3s var(--ease);
}
/* the accent rail carries the layer's own colour */
.stk__layer::before {
  content: "";
  position: absolute;
  left: 0; top: 22px; bottom: 22px;
  width: 3px;
  border-radius: 3px;
  background: var(--stk-c);
  transition: top .3s var(--ease), bottom .3s var(--ease);
}
.stk__layer[data-layer="1"] { --stk-c: #9cbdf3; }
.stk__layer[data-layer="2"] { --stk-c: #f0d477; }
.stk__layer[data-layer="3"] { --stk-c: #a6d896; }
.stk__layer:hover,
.stk__layer:focus-within {
  background: #fff;
  border-color: rgba(0, 0, 0, 0.12);
}
.stk__layer:hover::before,
.stk__layer:focus-within::before { top: 14px; bottom: 14px; }

/* the three micro-labels sit at 0.58 rather than the 0.4-0.5 used
   elsewhere on the page: below that they fall under WCAG AA at this size */
.stk__num {
  font: 600 11px/1 ui-monospace, SFMono-Regular, Menlo, monospace;
  letter-spacing: 0.1em;
  color: rgba(0, 0, 0, 0.58);
}
.stk__kind {
  font: 500 11px/1 "Neue Montreal", system-ui, sans-serif;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: rgba(0, 0, 0, 0.58);
  margin-left: 10px;
}
.stk__name {
  font: 500 22px/1.25 "Neue Montreal", system-ui, sans-serif;
  letter-spacing: -0.012em;
  color: #000;
  margin: 10px 0 0;
}
.stk__body-text {
  font: 400 16px/1.55 "Neue Montreal", system-ui, sans-serif;
  color: var(--muted, #656567);
  margin: 8px 0 0;
  text-wrap: pretty;
}

/* ---- what the middle layer does: the capability strip ---- */
.stk__caps {
  margin: 80px auto 0;
  max-width: 780px;
  padding: 28px 30px 26px;
  border: 1.5px solid #000;
  border-radius: 14px;
  background: var(--cream, #f7f4e8);
  text-align: center;
}
.stk__caps-title {
  font: 500 12px/1 "Neue Montreal", system-ui, sans-serif;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: rgba(0, 0, 0, 0.58);
  margin: 0;
}
.stk__caps-list {
  list-style: none;
  margin: 18px 0 0;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 8px;
}
.stk__caps-list li {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 9px 15px;
  border: 1.5px solid #000;
  border-radius: 999px;
  background: #fdfcf8;
  font: 500 14px/1 "Neue Montreal", system-ui, sans-serif;
  letter-spacing: -0.006em;
  color: #000;
}
.stk__caps-list svg { width: 15px; height: 15px; flex: none; display: block; }
.stk__caps-note {
  margin: 18px 0 0;
  font: 400 15px/1.5 "Neue Montreal", system-ui, sans-serif;
  color: var(--muted, #656567);
  text-wrap: pretty;
}
.stk__caps-note a { color: var(--blue, #397ae7); text-decoration: none; border-bottom: 1px solid rgba(57, 122, 231, 0.35); }
.stk__caps-note a:hover { border-bottom-color: var(--blue, #397ae7); }
.stk__caps-arrow { margin-left: 6px; display: inline-block; transition: transform .25s var(--ease); }
.stk__caps-note a:hover .stk__caps-arrow { transform: translateX(3px); }

/* =====================================================================
   FIGURE MOTION

   Two loops only (the travelling mark and the cursor nudge), plus a
   one-shot explode that rests finished. The planes are positioned by a
   transform ATTRIBUTE on an outer <g>; the CSS transform lives on the
   inner .stk-plane so it never overwrites that placement.
   ===================================================================== */
.stk-plane {
  --stk-ex: 0px;
  transform: translateY(var(--stk-ex));
  /* expo-out: the planes decelerate into place instead of sliding at an
     even rate. No overshoot, which on a technical drawing reads as cheap. */
  transition: transform .84s cubic-bezier(.16, 1, .3, 1), opacity .32s var(--ease);
}
.stk-load { transition: opacity .44s var(--ease); }
.stk-between path { transition: opacity .4s var(--ease); }

/* collapsed start state applies only once the script has taken over, so
   a page without JS shows the finished drawing */
.stk.is-js:not(.is-seen) .stk-plane[data-layer="1"] { --stk-ex: 270px; }
.stk.is-js:not(.is-seen) .stk-plane[data-layer="3"] { --stk-ex: -270px; }
.stk.is-js:not(.is-seen) .stk-load,
.stk.is-js:not(.is-seen) .stk-between path,
.stk.is-js:not(.is-seen) .stk-badge { opacity: 0; }

/* the middle plane never moves; the other two peel away from it, and the
   slight offset between them reads as deliberate rather than a pop */
.stk.is-js .stk-plane[data-layer="1"] { transition-delay: 0s; }
.stk.is-js .stk-plane[data-layer="3"] { transition-delay: .1s; }
.stk.is-js .stk-load { transition-delay: .58s; }
.stk-badge { transition: opacity .4s var(--ease) .72s; }

/* the four links arrive in the order the work travels: down to the logic,
   down to the data, back up with the answer, up with the finished result */
.stk.is-js .stk-wire--ask path:nth-child(1) { transition-delay: .82s; }
.stk.is-js .stk-wire--ask path:nth-child(2) { transition-delay: .96s; }
.stk.is-js .stk-wire--ans path:nth-child(1) { transition-delay: 1.10s; }
.stk.is-js .stk-wire--ans path:nth-child(2) { transition-delay: 1.24s; }
.stk.is-js .stk-rw path { transition-delay: 1.34s; }

/* Interface items arrive one after another once the top plane has opened,
   so the screen reads as filling in rather than appearing whole. `--i` is
   set per item in the markup; these run once and rest finished. */
.stk-pop { transition: opacity .34s var(--ease), transform .34s var(--ease); }
.stk.is-js .stk-pop { transition-delay: calc(.82s + var(--i) * .055s); }
.stk.is-js:not(.is-seen) .stk-pop { opacity: 0; transform: translate(-7px, 4px); }

/* hovering a copy block lifts its plane and mutes the other two */
.stk[data-active] .stk-plane { opacity: .28; }
.stk[data-active="1"] .stk-plane[data-layer="1"],
.stk[data-active="2"] .stk-plane[data-layer="2"],
.stk[data-active="3"] .stk-plane[data-layer="3"] { opacity: 1; }
.stk[data-active] .stk-between { opacity: .18; }
.stk[data-active] .stk-rider,
.stk[data-active] .stk-feed { opacity: 0; }

/* The travelling mark: one job, down through the layers and back up. The
   path string matches the drawn #stkRide d exactly.

   It does not glide at a constant rate. The keyframe times below were
   computed from the real segment lengths (waypoints sit at 20.52, 25.88,
   46.48, 51.84, 72.43 and 79.48 percent of the path) so the mark travels
   a leg, then holds for a beat on each object it reaches. The hold is
   what reads as work being done there rather than a bead on a wire.
   It fades out on the result it delivered and fades back in as the next
   job arrives, so the loop restarts without a jump. The halo is the same
   circle at r=10 with a fixed fill-opacity, riding the identical path, so
   the glow simply follows the mark instead of blinking at every stop. */
.stk-rider {
  offset-path: path("M179.54 143L179.54 411L240.16 446L240.16 715L300.78 750L300.78 481L380.46 435L380.46 167");
  offset-distance: 100%;
  offset-rotate: 0deg;
  transition: opacity .3s var(--ease);
}
.stk.is-seen .stk-rider--lead { animation: stk-ride 9.66s linear 1.36s infinite backwards; }
.stk.is-seen .stk-rider--b    { animation: stk-ride 9.66s linear -1.86s infinite; }
.stk.is-seen .stk-rider--c    { animation: stk-ride 9.66s linear -5.08s infinite; }
.stk.is-seen .stk-rider--halo { animation: stk-ride 9.66s linear 1.36s infinite backwards; }
@keyframes stk-ride {
  0%     { offset-distance: 0%;      opacity: 0; }
  2%     { offset-distance: 0.9%;    opacity: 1; }
  11.8%  { offset-distance: 20.52%;  opacity: 1; }
  15.9%  { offset-distance: 20.52%;  opacity: 1; }
  18.9%  { offset-distance: 25.88%;  opacity: 1; }
  23%    { offset-distance: 25.88%;  opacity: 1; }
  34.9%  { offset-distance: 46.48%;  opacity: 1; }
  39%    { offset-distance: 46.48%;  opacity: 1; }
  42%    { offset-distance: 51.84%;  opacity: 1; }
  46.1%  { offset-distance: 51.84%;  opacity: 1; }
  57.9%  { offset-distance: 72.43%;  opacity: 1; }
  62%    { offset-distance: 72.43%;  opacity: 1; }
  66.1%  { offset-distance: 79.48%;  opacity: 1; }
  70.2%  { offset-distance: 79.48%;  opacity: 1; }
  81.9%  { offset-distance: 100%;    opacity: 1; }
  92%    { offset-distance: 100%;    opacity: 1; }
  97%, 100% { offset-distance: 100%; opacity: 0; }
}
/* the three AI marks breathe on their own clocks, so the layer looks live
   without anything marching in step */
.stk-ai-mark { transform: scale(1); transform-box: fill-box; transform-origin: center; }
.stk.is-seen .stk-ai-mark { animation: stk-breathe 4.6s var(--ease) var(--d, 0s) infinite; }
@keyframes stk-breathe {
  0%, 100% { transform: scale(1);    opacity: 1; }
  46%      { transform: scale(1.32); opacity: .62; }
}

/* 01: a selection stepping down the queue, one row at a time. This is the
   one place a discrete step is right, so the move between rows is quick and
   the hold on each row is long. The vector is the row pitch in iso: 30 plane
   units becomes (30 * 0.866, 30 * 0.5). */
.stk-select { transform: translate(0, 0); opacity: 1; }
.stk.is-seen .stk-select { animation: stk-select 9.2s var(--ease) 1.5s infinite backwards; }
@keyframes stk-select {
  0%, 3%    { transform: translate(0, 0);           opacity: 0; }
  7%, 20%   { transform: translate(0, 0);           opacity: 1; }
  24%, 44%  { transform: translate(24.25px, 14px);  opacity: 1; }
  48%, 68%  { transform: translate(48.50px, 28px);  opacity: 1; }
  72%, 92%  { transform: translate(72.75px, 42px);  opacity: 1; }
  97%, 100% { transform: translate(72.75px, 42px);  opacity: 0; }
}

/* 01: the card's active bar moves with the queue selection, on the same
   clock, so picking a row visibly changes what the detail shows. Bar pitch
   is 14 plane units, so (14 * 0.866, 14 * 0.5). */
.stk-active { transform: translate(0, 0); opacity: 1; }
.stk.is-seen .stk-active { animation: stk-active 9.2s var(--ease) 1.5s infinite backwards; }
@keyframes stk-active {
  0%, 3%    { transform: translate(0, 0);          opacity: 0; }
  7%, 20%   { transform: translate(0, 0);          opacity: 1; }
  24%, 44%  { transform: translate(12.12px, 7px);  opacity: 1; }
  48%, 68%  { transform: translate(24.25px, 14px); opacity: 1; }
  72%, 92%  { transform: translate(36.37px, 21px); opacity: 1; }
  97%, 100% { transform: translate(36.37px, 21px); opacity: 0; }
}

/* 01: the result bar reacts when the lead mark lands on it. Same clock as
   the ride, so the highlight and the arrival are the same event. */
/* the animation has to sit on the path, not the group: the path carries its
   own stroke-width presentation attribute, which beats a value inherited
   from an animated parent */
.stk.is-seen .stk-land path { animation: stk-land 9.66s linear 1.36s infinite; }
@keyframes stk-land {
  0%, 78%   { stroke-width: 1.3; }
  84%       { stroke-width: 3.2; }
  90%       { stroke-width: 1.3; }
  94%       { stroke-width: 2.4; }
  98%, 100% { stroke-width: 1.3; }
}

/* 03: data arriving from the systems that feed the layer, one mark per
   source, each on its own clock. They rest on the record they feed. */
.stk-feed { offset-distance: 100%; offset-rotate: 0deg; }
.stk.is-seen .stk-feed { animation: stk-feed 7.2s linear var(--d, 0s) infinite backwards; }
/* `backwards` on every delayed loop matters here: without it an element
   shows its BASE style during the delay, which for these means sitting
   visible at the end of its path and then jumping to the start. With it
   they wait unseen at their source. Reduced motion strips the animation
   entirely and the base style takes over, resting them on the record. */
/* Eight sources, staggered 0.9s across a 7.2s cycle so the ring never
   syncs up, and each mark is only visible for about 40% of its cycle so
   roughly three are travelling at any moment rather than all eight. */
@keyframes stk-feed {
  0%        { offset-distance: 0%;   opacity: 0; }
  4%        { offset-distance: 5%;   opacity: .9; }
  34%       { offset-distance: 100%; opacity: .9; }
  40%, 100% { offset-distance: 100%; opacity: 0; }
}

.stk-cursor { transform: translate(0, 0); }
.stk.is-seen .stk-cursor { animation: stk-nudge 6.8s var(--ease) 1.92s infinite; }
@keyframes stk-nudge {
  0%, 64%, 100% { transform: translate(0, 0); }
  72%, 80%      { transform: translate(-4px, 2.5px); }
}

@media (prefers-reduced-motion: reduce) {
  .stk-plane, .stk-load, .stk-between path, .stk-badge, .stk-pop { transition: none; }
  .stk.is-js:not(.is-seen) .stk-plane[data-layer="1"],
  .stk.is-js:not(.is-seen) .stk-plane[data-layer="3"] { --stk-ex: 0px; }
  .stk.is-js:not(.is-seen) .stk-load,
  .stk.is-js:not(.is-seen) .stk-between path,
  .stk.is-js:not(.is-seen) .stk-badge { opacity: 1; }
  .stk.is-js:not(.is-seen) .stk-pop { opacity: 1; transform: none; }
  .stk.is-seen .stk-rider, .stk.is-seen .stk-rider--halo,
  .stk.is-seen .stk-ai-mark, .stk.is-seen .stk-cursor,
  .stk.is-seen .stk-select, .stk.is-seen .stk-active, .stk.is-seen .stk-land path,
  .stk.is-seen .stk-feed { animation: none; }
}

/* ---- responsive ---- */
@media (max-width: 1000px) {
  .stk__body { grid-template-columns: minmax(0, 1fr); gap: 40px; margin-top: 52px; }
  /* between roughly 760 and 1000 the head is narrower than the 50px
     headline needs, and both clauses wrapped to two lines each */
  .stk__title { font-size: 42px; }
  .stk__copy { max-width: 640px; margin: 0 auto; justify-content: flex-start; }
}
@media (max-width: 700px) {
  .stk { padding: 84px var(--pad, 24px) 92px; }
  .stk__title { font-size: 38px; line-height: 1.16; }
  .stk__lead { font-size: 17px; }
  .stk__layer { padding: 16px 14px 16px 20px; }
  .stk__name { font-size: 20px; }
  .stk__body-text { font-size: 15px; }
  .stk__caps { padding: 24px 18px 22px; margin-top: 56px; }
  .stk__caps-list { gap: 7px; }
  .stk__caps-list li { padding: 8px 12px; font-size: 13px; }
}
@media (max-width: 420px) {
  .stk__title { font-size: 32px; }
  .stk__caps-list li { font-size: 12.5px; padding: 7px 11px; }
}
