/* ==========================================================================
   CloudCore parts — the four platform components, one panel each.

   Sits in #platform on the homepage, under the "head-start every system
   inherits" heading. The heading and its paragraph make the claim in
   words; this is the evidence for it. It draws the four components that
   paragraph names and nothing else.

   It replaced an ops console that had to badge itself SAMPLE because
   every row in it was invented. Nothing here is invented: four drawings
   of what each component does, and the integration names are the same
   ones already published on cloudcore.php.

   Everything plays once, when the row is scrolled to, driven by .is-in
   from include/parts.js. Not on load — the row sits well down the page,
   so on load it would be over before anyone reached it. Same approach as
   the .tower in "Why companies choose us".

   Colours come from the surf-light token set in site.css. Nothing is
   hard-coded here except the terracotta inside the SVGs, which have to
   carry their own fills.
   ========================================================================== */

/* --------------------------------------------------------------------------
   Heading ornament: four laid blocks and a cube landing on them.

   Third in the set. #services has a block rolling down four steps, #why has
   four slabs stacking into a tower, and this is #platform's. The four blocks
   are the four components in the cards below; the cube is the client's own
   system, and it is the only thing that arrives.

   The movement is deliberately unlike the other two. The staircase runs
   down, the tower builds up, this one converges and lands, so three
   ornaments in the same drawing style still each read as their own thing.

   Plays once when scrolled to, like .tower. The staircase up the page
   already loops, and the four cards below this now carry idle loops of
   their own, so a third permanent animation here would be too much.
   -------------------------------------------------------------------------- */
#platform .section__lede{
  position: relative;
  max-width: none;
  padding-right: 476px;
}
#platform .plinth{
  position: absolute;
  /* Held well off the right edge rather than pinned to it. Out at the
     margin it read as an afterthought parked in the corner; brought in,
     it sits with the heading. */
  right: 170px;
  top: 50%;
  /* Not half the box height. The svg carries 132px of empty air at the top
     for the cube to fall through, so the visible object sits in the lower
     part of the box; centring on the box would centre the air. */
  margin-top: -262px;
}
.plinth{ width: 290px; height: 390px }
.plinth svg{ display: block; width: 290px; height: 390px; overflow: visible }

/* Labels set into the faces, same as the .tower slabs. The viewBox is
   205 units across and draws at 290px, so 6.4 here lands at about 9px on
   screen, which is what .tower uses. */
.plinth .pl-lab{
  font-family: var(--f-mono);
  font-size: 5.6px;
  letter-spacing: .5px;
  fill: rgba(10,12,15,.50);
}
/* On the terracotta cube, so it has to go light instead. */
.plinth .pl-lab--live{ font-size: 6.2px; fill: rgba(255,255,255,.88) }
/* The platform name sits in a wall only 12 units tall, so it runs smaller
   than the phrases lying flat on the tops. */
.plinth .pl-lab--name{ font-size: 5.6px; letter-spacing: .6px }

.plinth polygon{
  stroke: rgba(10,12,15,.42);
  stroke-width: 1;
  stroke-linejoin: round;
  vector-effect: non-scaling-stroke;
}
.plinth .ft{ fill: #FBFAF7 }
.plinth .fl{ fill: #DCD5C9 }
.plinth .fr{ fill: #C0B6A5 }
/* The cube is the part that is actually new, so it is the terracotta one. */
.plinth .ft--live{ fill: #BE4A24; stroke: rgba(50,14,5,.55) }
.plinth .fl--live{ fill: #A43818; stroke: rgba(50,14,5,.55) }
.plinth .fr--live{ fill: #7A2710; stroke: rgba(50,14,5,.55) }

.plinth .pl-pad,
.plinth .pl-name,
.plinth .pl-cube{ opacity: 0 }
/* The name rides in with the block it is written on. */
.plinth.is-built .pl-name{
  animation: pl-lay .55s cubic-bezier(.4,0,.35,1.18) .39s forwards;
}
@keyframes pl-lay{
  from{ opacity: 0; transform: translateY(-58px) }
  40% { opacity: 1 }
  to  { opacity: 1; transform: none }
}

/* Once they are down, the four blocks orbit the cube, each carrying its
   own word round with it. That is what puts a word on the fourth block:
   the cube hides whichever one is standing behind it, so a static label
   there could never be read, but a rotating one comes round into view.
   Clockwise on screen — back, right, front, left.

   Slow on purpose. Each block holds a position for 3.4s and takes 1.1s to
   slide to the next, so a full turn is 18s.

   Two delays on each, not one. These run the entrance and then the orbit,
   and a single animation-delay value gets applied to BOTH, which would
   start the orbit mid-drop and stamp on it. The second value is when the
   orbit takes over. */
.plinth.is-built .pl-pad{
  animation: pl-lay .55s cubic-bezier(.4,0,.35,1.18) forwards,
             pl-orbit 18s cubic-bezier(.65,0,.35,1) infinite;
}
.plinth.is-built .pl-pad--0{ animation-delay: 0s,   1.7s; animation-name: pl-lay, pl-orbit-0 }
.plinth.is-built .pl-pad--1{ animation-delay: .13s, 1.7s; animation-name: pl-lay, pl-orbit-1 }
.plinth.is-built .pl-pad--2{ animation-delay: .26s, 1.7s; animation-name: pl-lay, pl-orbit-2 }
.plinth.is-built .pl-pad--3{ animation-delay: .39s, 1.7s; animation-name: pl-lay, pl-orbit-3 }

/* The blocks travel round the perimeter of an ellipse, not straight from
   corner to corner.

   Straight edges cannot work at any gap. Two neighbours in transit at the
   same time pass within a clearance score of 1.17 where 2.0 is touching,
   and widening the gap barely moves that number, because both blocks slide
   inward off the corner at once. The extra waypoint at each 45 degree
   point bows the path outward and holds them apart the whole way round.

   Checked by sampling every pair across the full cycle: worst clearance is
   2.19, comfortably past the 2.0 that means touching. The gap between the
   blocks was widened from 8 to 26 to buy that margin. */
@keyframes pl-orbit-0{
  0.00% { transform: translate(0.00px, 0.00px) }
  18.90%{ transform: translate(0.00px, 0.00px) }
  21.95%{ transform: translate(44.09px, 10.54px) }
  25.00%{ transform: translate(62.35px, 36.00px) }
  43.90%{ transform: translate(62.35px, 36.00px) }
  46.95%{ transform: translate(44.09px, 61.46px) }
  50.00%{ transform: translate(0.00px, 72.00px) }
  68.90%{ transform: translate(0.00px, 72.00px) }
  71.95%{ transform: translate(-44.09px, 61.46px) }
  75.00%{ transform: translate(-62.35px, 36.00px) }
  93.90%{ transform: translate(-62.35px, 36.00px) }
  96.95%{ transform: translate(-44.09px, 10.54px) }
  100.00%{ transform: translate(0.00px, 0.00px) }
}
@keyframes pl-orbit-1{
  0.00% { transform: translate(0.00px, 0.00px) }
  18.90%{ transform: translate(0.00px, 0.00px) }
  21.95%{ transform: translate(-18.26px, 25.46px) }
  25.00%{ transform: translate(-62.35px, 36.00px) }
  43.90%{ transform: translate(-62.35px, 36.00px) }
  46.95%{ transform: translate(-106.44px, 25.46px) }
  50.00%{ transform: translate(-124.71px, 0.00px) }
  68.90%{ transform: translate(-124.71px, 0.00px) }
  71.95%{ transform: translate(-106.44px, -25.46px) }
  75.00%{ transform: translate(-62.35px, -36.00px) }
  93.90%{ transform: translate(-62.35px, -36.00px) }
  96.95%{ transform: translate(-18.26px, -25.46px) }
  100.00%{ transform: translate(0.00px, 0.00px) }
}
@keyframes pl-orbit-2{
  0.00% { transform: translate(0.00px, 0.00px) }
  18.90%{ transform: translate(0.00px, 0.00px) }
  21.95%{ transform: translate(18.26px, -25.46px) }
  25.00%{ transform: translate(62.35px, -36.00px) }
  43.90%{ transform: translate(62.35px, -36.00px) }
  46.95%{ transform: translate(106.44px, -25.46px) }
  50.00%{ transform: translate(124.71px, -0.00px) }
  68.90%{ transform: translate(124.71px, -0.00px) }
  71.95%{ transform: translate(106.44px, 25.46px) }
  75.00%{ transform: translate(62.35px, 36.00px) }
  93.90%{ transform: translate(62.35px, 36.00px) }
  96.95%{ transform: translate(18.26px, 25.46px) }
  100.00%{ transform: translate(0.00px, 0.00px) }
}
@keyframes pl-orbit-3{
  0.00% { transform: translate(0.00px, 0.00px) }
  18.90%{ transform: translate(0.00px, 0.00px) }
  21.95%{ transform: translate(-44.09px, -10.54px) }
  25.00%{ transform: translate(-62.35px, -36.00px) }
  43.90%{ transform: translate(-62.35px, -36.00px) }
  46.95%{ transform: translate(-44.09px, -61.46px) }
  50.00%{ transform: translate(-0.00px, -72.00px) }
  68.90%{ transform: translate(-0.00px, -72.00px) }
  71.95%{ transform: translate(44.09px, -61.46px) }
  75.00%{ transform: translate(62.35px, -36.00px) }
  93.90%{ transform: translate(62.35px, -36.00px) }
  96.95%{ transform: translate(44.09px, -10.54px) }
  100.00%{ transform: translate(0.00px, 0.00px) }
}


/* The cube waits for all four to be down before it lands. */
.plinth.is-built .pl-cube{
  animation: pl-land .62s cubic-bezier(.45,0,.35,1.22) .72s forwards;
}
@keyframes pl-land{
  from{ opacity: 0; transform: translateY(-124px) }
  35% { opacity: 1 }
  to  { opacity: 1; transform: none }
}

@media (max-width: 1100px){
  #platform .section__lede{ padding-right: 0 }
  .plinth{ display: none }
}
@media (prefers-reduced-motion: reduce){
  .plinth .pl-pad, .plinth .pl-name, .plinth .pl-cube{ opacity: 1 }
  .plinth.is-built .pl-pad, .plinth.is-built .pl-name, .plinth.is-built .pl-cube{ animation: none }
}

.parts-intro{ max-width: 62ch; margin: 0 0 26px }
.parts-intro__label{
  display: block;
  font-family: var(--f-mono);
  font-size: 11px;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--accent);
  margin-bottom: 11px;
}
.parts-intro p{
  font-size: 15px;
  line-height: 1.55;
  color: var(--text-2);
  margin: 0;
}
.parts-intro b{ color: var(--text); font-weight: 500 }

/* The hairline between panels is the grid gap showing the container
   through, so there is one rule between neighbours rather than two
   borders sitting on top of each other. */
.parts{
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 1px;
  background: var(--line-2);
  border: 1px solid var(--line-2);
}

.part{
  background: #FCFBF9;
  padding: 22px 22px 24px;
  display: flex;
  flex-direction: column;
  /* Panels rise in on a stagger; --d is set per panel in the markup. */
  opacity: 0;
  transform: translateY(14px);
  transition: opacity .6s ease var(--d),
              transform .6s cubic-bezier(.3,.8,.35,1) var(--d);
}
.parts.is-in .part{ opacity: 1; transform: none }

/* Each drawing sits on its own tinted shelf, so four different pictures
   still read as one row. */
.part__viz{
  background: var(--l-bg);
  border: 1px solid var(--line);
  margin: -4px -4px 18px;
  padding: 10px;
  height: 104px;
}
.part__viz svg{ display: block; width: 100%; height: 100% }

.part__n{
  font-family: var(--f-mono);
  font-size: 10px;
  letter-spacing: .16em;
  color: var(--text-3);
  margin-bottom: 7px;
}
.part h3{
  font-size: 16px;
  font-weight: 600;
  letter-spacing: -.01em;
  margin: 0 0 8px;
}
.part p{
  font-size: 13.5px;
  line-height: 1.5;
  color: var(--text-2);
  margin: 0;
}
.part p em{
  font-style: normal;
  font-family: var(--f-mono);
  font-size: 12px;
  color: var(--text);
}

/* --- shared drawing styles ------------------------------------------- */
.part__viz .ln{ stroke: var(--line-2); stroke-width: 1.4; fill: none }
.part__viz .ln-soft{ stroke: var(--line-2); stroke-width: 1.2; fill: none; opacity: .6 }
.part__viz .hot{ stroke: var(--accent); stroke-width: 1.6; fill: none }
.part__viz .hot-f{ fill: var(--accent) }
.part__viz .pale{ fill: var(--l-bg-3) }
.part__viz .paper{ fill: #fff; stroke: var(--line-2); stroke-width: 1.2 }
.part__viz .tiny{
  font-family: var(--f-mono);
  font-size: 7.5px;
  fill: var(--text-3);
  letter-spacing: .1em;
}

/* --- shared entrances ------------------------------------------------- */
.fade{ opacity: 0 }
.parts.is-in .fade{ animation: pt-fade .38s ease forwards }
@keyframes pt-fade{ to{ opacity: 1 } }

/* Nodes land rather than simply appear. fill-box keeps the origin on the
   shape itself rather than the whole SVG canvas, so each one scales about
   its own centre instead of flying in from the corner. */
.pop{
  opacity: 0;
  transform-box: fill-box;
  transform-origin: center;
  transform: scale(.4);
}
.parts.is-in .pop{ animation: pt-pop .4s cubic-bezier(.34,1.2,.4,1) forwards }
@keyframes pt-pop{ to{ opacity: 1; transform: none } }

/* Line drawing. The dash array equals the line's own length in user
   units, so it starts fully hidden and unrolls to nothing. */
@keyframes pt-draw{ to{ stroke-dashoffset: 0 } }

/* --- 01 the job clears its steps, left to right ----------------------- */
.w-run{ stroke-dasharray: 107; stroke-dashoffset: 107 }
.parts.is-in .w-run{ animation: pt-draw 1.05s cubic-bezier(.5,0,.35,1) .62s forwards }
.parts.is-in .w-n0{ animation-delay: .60s }
.parts.is-in .w-t0{ animation-delay: .78s }
.parts.is-in .w-n1{ animation-delay: 1.05s }
.parts.is-in .w-t1{ animation-delay: 1.23s }
.parts.is-in .w-n2{ animation-delay: 1.52s }
.parts.is-in .w-n3{ animation-delay: 1.72s }
.parts.is-in .w-lab{ animation-delay: 1.30s }

/* --- 02 documents land on the pile, oldest first ---------------------- */
.d-sheet{ opacity: 0 }
.parts.is-in .d-sheet{ animation: pt-drop .5s cubic-bezier(.35,.9,.35,1) forwards }
@keyframes pt-drop{
  from{ opacity: 0; transform: translate(-9px, -9px) }
  to  { opacity: 1; transform: none }
}
.parts.is-in .d-s0{ animation-delay: .58s }
.parts.is-in .d-s1{ animation-delay: .74s }
.parts.is-in .d-s2{ animation-delay: .90s }
.parts.is-in .d-rule{ animation-delay: 1.10s }
.parts.is-in .d-tag{ animation-delay: 1.28s }

/* --- 03 bars grow up out of the baseline ------------------------------ */
.k-bar{ transform-box: fill-box; transform-origin: bottom; transform: scaleY(0) }
.parts.is-in .k-bar{ animation: pt-grow .55s cubic-bezier(.3,.9,.3,1) forwards }
@keyframes pt-grow{ to{ transform: none } }
/* Two delays per bar, not one. These elements run two animations: the
   grow-in, then the idle loop. A single-value animation-delay is applied
   to BOTH of them, which would start the loop mid-entrance and stamp on
   the grow. The second value is when the idle loop takes over, and it is
   staggered as well so the bars never breathe in unison. */
.parts.is-in .k-b0{ animation-delay: .60s, 1.90s }
.parts.is-in .k-b1{ animation-delay: .69s, 2.15s }
.parts.is-in .k-b2{ animation-delay: .78s, 1.98s }
.parts.is-in .k-b3{ animation-delay: .87s, 2.32s }
.parts.is-in .k-b4{ animation-delay: .96s, 2.06s }
.parts.is-in .k-b5{ animation-delay: 1.08s, 2.24s }

/* --- 04 the rail is there, the connections come up off it ------------- */
.b-spine{ stroke-dasharray: 168; stroke-dashoffset: 168 }
.parts.is-in .b-spine{ animation: pt-draw .7s cubic-bezier(.5,0,.35,1) .58s forwards }
.b-post{ stroke-dasharray: 20; stroke-dashoffset: 20 }
.parts.is-in .b-post{ animation: pt-draw .3s ease forwards }
.parts.is-in .b-p0{ animation-delay: .94s }
.parts.is-in .b-p1{ animation-delay: 1.04s }
.parts.is-in .b-p2{ animation-delay: 1.14s }
.parts.is-in .b-d0{ animation-delay: .90s }
.parts.is-in .b-d1{ animation-delay: 1.00s }
.parts.is-in .b-d2{ animation-delay: 1.10s }
.parts.is-in .b-x0{ animation-delay: 1.18s }
.parts.is-in .b-x1{ animation-delay: 1.28s }
.parts.is-in .b-x2{ animation-delay: 1.38s }

/* ==========================================================================
   The idle loops.

   Once each drawing has finished arriving, it keeps moving. Every loop is
   tied to what its component actually does, so the motion carries meaning
   rather than being decoration: the step that is waiting keeps pulsing,
   documents keep arriving, the numbers keep moving, and traffic keeps
   running along the bus.

   All four are slow, low-contrast and out of sync with each other, so the
   row reads as alive rather than busy. Each one starts only after its
   entrance has landed, so the two never fight over the same property.
   ========================================================================== */

/* 01 — the step that is waiting for a person keeps pulsing. */
.w-halo{
  opacity: 0;
  transform-box: fill-box;
  transform-origin: center;
}
.parts.is-in .w-halo{
  animation: pt-halo 2.6s cubic-bezier(.25,.6,.4,1) 2.2s infinite;
}
@keyframes pt-halo{
  0%      { opacity: .55; transform: scale(1) }
  70%,100%{ opacity: 0;   transform: scale(2.1) }
}

/* 02 — another document arrives and merges into the pile. */
.d-new{ opacity: 0 }
.parts.is-in .d-new{ animation: pt-file 5s cubic-bezier(.4,0,.35,1) 2s infinite }
@keyframes pt-file{
  0%      { opacity: 0;   transform: translate(-26px, -20px) }
  22%     { opacity: .85; transform: translate(-16px, -12px) }
  55%     { opacity: .55; transform: translate(0, 0) }
  70%,100%{ opacity: 0;   transform: translate(0, 0) }
}

/* 03 — the numbers keep moving. Each bar runs on its own clock so the
   chart never pulses as one block. */
.parts.is-in .k-bar{
  animation: pt-grow .55s cubic-bezier(.3,.9,.3,1) forwards,
             pt-live var(--t, 5s) ease-in-out 1.9s infinite;
}
@keyframes pt-live{
  0%, 100%{ transform: scaleY(1) }
  50%     { transform: scaleY(var(--s, .86)) }
}
.k-b0{ --s: .78; --t: 5.2s }
.k-b1{ --s: .88; --t: 4.4s }
.k-b2{ --s: .72; --t: 6.1s }
.k-b3{ --s: .91; --t: 4.9s }
.k-b4{ --s: .80; --t: 5.6s }
.k-b5{ --s: .93; --t: 3.8s }

/* 04 — traffic keeps running along the bus. */
.b-pulse{ opacity: 0 }
.parts.is-in .b-pulse{ animation: pt-run 3.2s linear 2s infinite }
.parts.is-in .b-u1{ animation-delay: 3.1s }
.parts.is-in .b-u2{ animation-delay: 4.2s }
@keyframes pt-run{
  0%      { opacity: 0;  transform: translateX(0) }
  12%     { opacity: .95 }
  80%     { opacity: .95 }
  100%    { opacity: 0;  transform: translateX(168px) }
}

/* A little lift on hover, so the panels feel like objects. */
@media (hover: hover){
  .part{ transition: opacity .6s ease var(--d),
                     transform .6s cubic-bezier(.3,.8,.35,1) var(--d),
                     background .25s ease }
  .part:hover{ background: #FFFFFF }
}

@media (max-width: 1080px){
  .parts{ grid-template-columns: 1fr 1fr }
}
@media (max-width: 620px){
  .parts{ grid-template-columns: 1fr }
  .part{ padding: 20px 20px 22px }
}

/* Everything arrives in its finished state, nothing moves. */
@media (prefers-reduced-motion: reduce){
  .part{ opacity: 1; transform: none; transition: none }
  .fade, .pop, .d-sheet{ opacity: 1 }
  .pop, .k-bar, .d-sheet{ transform: none }
  .w-run, .b-spine, .b-post{ stroke-dashoffset: 0 }
  .parts.is-in *{ animation: none !important }
}

/* ==========================================================================
   #cases heading ornament — four systems, different sizes, still lit.

   Fourth in the set with the #services staircase, the #why tower and the
   #platform orbit. Four solids of unequal height and footprint, each with
   a terracotta light on the roof.

   The heading beside it reads "A selection of systems we've built and run
   today", and the paragraph under it says the four were picked to show
   the range of operational complexity. So the solids are deliberately
   unequal — four identical blocks would say the opposite of the copy.

   Built, then run. They rise once when scrolled to; the roof lights keep
   pulsing afterwards, each on its own clock so the row never blinks in
   unison.

   No shared base, unlike the #platform ornament. These are four separate
   client systems, not one platform, and a plinth would blur the two.
   ========================================================================== */
#cases .section__lede{
  position: relative;
  max-width: none;
  padding-right: 430px;
}
#cases .skyline{
  position: absolute;
  right: 96px;
  top: 50%;
  /* The drawing sits in the upper part of its box — the empty air is
     underneath, for the solids to rise up through — so this centres what
     is actually visible rather than the box. */
  margin-top: -86px;
}
.skyline{ width: 290px; height: 195px }
.skyline svg{ display: block; width: 290px; height: 195px; overflow: visible }

.skyline polygon{
  stroke: rgba(10,12,15,.42);
  stroke-width: 1;
  stroke-linejoin: round;
  vector-effect: non-scaling-stroke;
}
.skyline .ft{ fill: #FBFAF7 }
.skyline .fl{ fill: #DCD5C9 }
.skyline .fr{ fill: #C0B6A5 }
.skyline .ft--live{ fill: #BE4A24; stroke: rgba(50,14,5,.55) }
.skyline .fl--live{ fill: #A43818; stroke: rgba(50,14,5,.55) }
.skyline .fr--live{ fill: #7A2710; stroke: rgba(50,14,5,.55) }

/* Built: each one rises out of the floor, left to right. */
.skyline .sk-sys{ opacity: 0 }
.skyline.is-built .sk-sys{
  animation: sk-rise .6s cubic-bezier(.32,.85,.35,1) forwards;
}
.skyline.is-built .sk-sys--0{ animation-delay: 0s }
.skyline.is-built .sk-sys--1{ animation-delay: .13s }
.skyline.is-built .sk-sys--2{ animation-delay: .26s }
.skyline.is-built .sk-sys--3{ animation-delay: .39s }
@keyframes sk-rise{
  from{ opacity: 0; transform: translateY(30px) }
  45% { opacity: 1 }
  to  { opacity: 1; transform: none }
}

/* Run today: the roof lights never stop. Four different periods, so the
   row reads as four systems ticking over rather than one thing flashing. */
.skyline.is-built .sk-lamp{
  animation: sk-blink var(--t) ease-in-out var(--d) infinite;
}
@keyframes sk-blink{
  0%, 100%{ opacity: 1 }
  50%     { opacity: .38 }
}
.skyline .sk-lamp--0{ --t: 3.4s; --d: 1.1s }
.skyline .sk-lamp--1{ --t: 4.6s; --d: 1.5s }
.skyline .sk-lamp--2{ --t: 2.9s; --d: 1.3s }
.skyline .sk-lamp--3{ --t: 3.9s; --d: 1.7s }

@media (max-width: 1100px){
  #cases .section__lede{ padding-right: 0 }
  .skyline{ display: none }
}
@media (prefers-reduced-motion: reduce){
  .skyline .sk-sys, .skyline .sk-lamp{ opacity: 1 }
  .skyline.is-built .sk-sys, .skyline.is-built .sk-lamp{ animation: none }
}

/* ==========================================================================
   #method heading ornament — four stages, one of which never finishes.

   Fifth in the set. Four cubes matching the four stage cards below, the
   last one terracotta because operate is the stage with no exit
   condition. A marker crosses all four, over and over.

   An earlier version made stage four a long rail running on past the
   other three. It read as a plank, and at several times the size of a
   cube it took over the drawing rather than completing it. Colour and the
   marker carry that meaning now, at no cost to the balance.

   The cubes rise once when scrolled to and then stop. The marker never
   stops: "every engagement moves through the same four-stage flow".
   ========================================================================== */
#method .section__lede{
  position: relative;
  max-width: none;
  padding-right: 372px;
}
#method .method3d{
  position: absolute;
  right: 60px;
  top: 50%;
  margin-top: -100px;
}
.method3d{ width: 268px; height: 215px }
.method3d svg{ display: block; width: 268px; height: 215px; overflow: visible }

.method3d polygon{
  stroke: rgba(10,12,15,.42);
  stroke-width: 1;
  stroke-linejoin: round;
  vector-effect: non-scaling-stroke;
}
.method3d .ft{ fill: #FBFAF7 }
.method3d .fl{ fill: #DCD5C9 }
.method3d .fr{ fill: #C0B6A5 }
.method3d .ft--live{ fill: #BE4A24; stroke: rgba(50,14,5,.55) }
.method3d .fl--live{ fill: #A43818; stroke: rgba(50,14,5,.55) }
.method3d .fr--live{ fill: #7A2710; stroke: rgba(50,14,5,.55) }

/* The three finite stages arrive and then are done. */
.method3d .mt-step,
.method3d .mt-pip{ opacity: 0 }
.method3d.is-built .mt-step{
  animation: mt-land .55s cubic-bezier(.35,.9,.35,1) forwards;
}
.method3d.is-built .mt-step--0{ animation-delay: 0s }
.method3d.is-built .mt-step--1{ animation-delay: .14s }
.method3d.is-built .mt-step--2{ animation-delay: .28s }
.method3d.is-built .mt-step--3{ animation-delay: .42s }
@keyframes mt-land{
  from{ opacity: 0; transform: translateY(-26px) }
  45% { opacity: 1 }
  to  { opacity: 1; transform: none }
}

/* The marker never stops. --dx/--dy are the row's own direction,
   written into the markup by the generator, so the travel follows the
   30 degree grid instead of sliding flat across it. */
.method3d.is-built .mt-pip{
  animation: mt-run 4.6s cubic-bezier(.45,0,.55,1) 1.1s infinite;
}
@keyframes mt-run{
  0%      { opacity: 0; transform: translate(0, 0) }
  12%     { opacity: 1 }
  78%     { opacity: 1 }
  100%    { opacity: 0; transform: translate(var(--dx), var(--dy)) }
}

@media (max-width: 1100px){
  #method .section__lede{ padding-right: 0 }
  .method3d{ display: none }
}
@media (prefers-reduced-motion: reduce){
  .method3d .mt-step, .method3d .mt-pip{ opacity: 1 }
  .method3d.is-built .mt-step,
  .method3d.is-built .mt-pip{ animation: none }
}
