/* ---- 4 Oflaf op Mooss: one job becomes a workflow ----
   The beat that used to be a constellation of deliverables. A sky said "any
   idea, any size" but never said what we DO with one, and the thing we do is
   the whole offer: a task that keeps coming back is taken apart, the parts are
   set into a workflow, and the workflow delivers a finished result into your
   inbox. So the beat stopped being a picture of outputs and became the making
   of one, drawn once, on the beat's own canvas.

   Two stations: your inbox, and the flow that does the work. There is no third
   box for what comes back -- the competing offers sit in your inbox from the
   first frame and the comparison lands in that same inbox, on top of them. The border is drawn around the flow only, so the boundary in the picture
   is the boundary the copy talks about. */

/* Keep the headline close to the scale of the surrounding beats. The figure
   gives up a little air instead, so this chapter does not suddenly whisper
   between two full-size statements. */
/* The dust canvas is deliberately far wider than the station it belongs to --
   the sprinkle happens over in the job column and the delivery lands over in
   the inbox. Unclipped, that width is real page overflow: on a phone the
   browser widens the layout viewport to fit it and shrinks the whole page to
   suit, which moves every section on the page. The beat clips it instead. */
#koup{overflow:hidden}
#koup .display{max-width:19ch;font-size:clamp(2.8rem,6.8vw,5.8rem)}
/* The sub sits where every other beat's sub sits: same size, same distance
   under the headline. This beat gives up figure height, not typography. */
#koup .sub{margin-top:clamp(1.1rem,2.2svh,1.7rem);max-width:56ch}

/* Capped, not spread across the full text column: at a wide viewport the three
   stations drift to the far edges with the pipeline stranded in between, and a
   row you have to sweep your eyes across does not read as one chain. */
.stage{display:grid;
  grid-template-columns:minmax(240px,.56fr) minmax(300px,.6fr);
  align-items:center;justify-content:center;column-gap:clamp(1rem,2.2vw,2rem);
  max-width:1040px;margin:clamp(.45rem,1.7svh,1.5rem) auto 0 clamp(0rem,4vw,3.5rem)}

/* The one big job. It is on screen before anything moves -- that is the
   premise: the work already exists, undivided. */
/* Sized to the window, not to a number. The whole page's snapping stands down
   the moment ANY beat is taller than the screen -- so a fixed 330px here took
   the carried, one-screen-at-a-time scroll off every other section on a 800px
   window. The figure gives up height before the page gives up its rhythm. */
.job{position:relative;z-index:2;min-height:clamp(210px,31svh,330px)}
.job-box{position:absolute;z-index:2;left:50%;top:50%;transform:translate(-50%,-50%);
  width:min(84%,240px);border:1px solid var(--gold-dim);background:var(--bg);
  padding:1.1rem 1.1rem .9rem;text-align:left;display:grid}
.job-label{display:block;margin-bottom:.55rem;font:500 .6rem/1.5 var(--sans);letter-spacing:.2em;
  text-transform:uppercase;color:var(--gold)}
/* The competing offers. Unread until the comparison has landed on top of them;
   they stay, because the inbox is the one thing in this beat that never
   changes what it is. */
.mails{list-style:none;margin:0;padding:0}
.mails li{display:flex;align-items:center;gap:.6rem;padding:.46rem 0;
  border-top:1px solid var(--hairline);font:400 .74rem/1.2 var(--sans);color:var(--text);
  transition:color .6s ease}
.mails li::before{content:"";flex:none;width:5px;height:5px;border-radius:50%;
  background:var(--gold);box-shadow:0 0 8px rgba(191,165,96,.35);transition:opacity .6s ease}
.via{margin-left:auto;font-size:.56rem;letter-spacing:.14em;text-transform:uppercase;color:var(--muted)}

/* The comparison: collapsed to nothing until it arrives, then it opens on top
   of the offers it compares. Three bars, one recommendation, no numbers. */
.verdict{display:grid;grid-template-rows:0fr;transition:grid-template-rows .6s cubic-bezier(.4,0,.2,1)}
.verdict-in{min-height:0;overflow:hidden;display:grid;gap:.34rem}
.verdict-head{padding-top:.5rem;border-top:1px solid var(--gold-dim);font:500 .64rem/1 var(--sans);
  letter-spacing:.16em;text-transform:uppercase;color:var(--gold)}
.verdict-head::before{content:"↓ "}
.bar{display:grid;grid-template-columns:.9rem 1fr .9rem;align-items:center;
  font:400 .62rem/1 var(--sans);color:var(--muted)}
.bar:last-child{padding-bottom:.6rem}
.bar i{font-style:normal}
.bar b{display:block;width:calc(var(--w)*100%);height:3px;background:var(--muted);opacity:.55;
  transform:scaleX(0);transform-origin:left;transition:transform .7s cubic-bezier(.16,1,.3,1)}
.bar em{font-style:normal;text-align:right;color:var(--gold);opacity:0;transition:opacity .4s ease}
.bar.pick{color:var(--gold)}
.bar.pick b{background:var(--gold);opacity:1;box-shadow:0 0 8px rgba(191,165,96,.4)}

/* The subtasks are already there, scattered around the job, before anything
   happens. They are what the job is made of -- not a list the page produces. */
.source-task{position:absolute;z-index:1;font:400 .66rem/1.2 var(--sans);color:var(--muted);
  white-space:nowrap;transition:opacity .3s ease,transform .42s cubic-bezier(.4,0,.2,1),color .25s}
.source-task::before{content:"";display:inline-block;width:5px;height:5px;margin-right:.55rem;
  border-radius:50%;background:var(--gold-dim);box-shadow:0 0 8px rgba(191,165,96,.22)}
.source-task:nth-child(2){left:0;top:13%}.source-task:nth-child(3){right:0;top:18%}
.source-task:nth-child(4){left:2%;bottom:16%}.source-task:nth-child(5){right:-2%;bottom:12%}
.source-task:nth-child(6){left:50%;top:4%;transform:translateX(-50%)}
.source-task.consuming{color:var(--gold);transform:scale(1.08)}
.source-task.consumed{opacity:0;transform:scale(.2)}
.source-task:nth-child(6).consuming{transform:translateX(-50%) scale(1.08)}
.source-task:nth-child(6).consumed{transform:translateX(-50%) scale(.2)}

/* No border of its own: the frame is made of dust or there is no frame. */
.flow{position:relative;min-height:clamp(210px,31svh,330px);max-width:24rem;
  padding:clamp(1.6rem,3svh,2.3rem) 1.4rem clamp(1rem,2svh,1.4rem);overflow:visible}
.box-label{position:absolute;top:var(--label-y,-.45rem);left:var(--label-x,1.6rem);
  z-index:4;padding:.46rem .72rem;border:1px solid var(--gold);background:var(--bg);
  font:500 .58rem/1 var(--sans);letter-spacing:.14em;text-transform:uppercase;
  color:var(--text);white-space:nowrap;opacity:0;transform:translate(-50%,-50%);
  transition:opacity .7s ease}
.box-label.on{opacity:1}

/* max-content, centred: the column is exactly as wide as the longest entry and
   every entry stretches to it, so the border has nothing to hug but the
   entries. Stretched to the track instead, the box stood a third wider than its
   own contents and read as half empty. */
.steps{position:relative;z-index:1;display:grid;grid-template-columns:1fr;gap:.48rem;
  width:min(9.5rem,100%);margin-inline:auto}
.step{display:flex;align-items:center;justify-content:center;gap:.4rem;min-height:36px;
  min-width:0;padding:.4rem .45rem;border:0;background:rgba(5,5,5,.74);
  font:400 .68rem/1.35 var(--sans);letter-spacing:.01em;color:var(--muted);text-align:center;
  opacity:0;transform:scale(.9);transform-origin:center;
  transition:opacity .2s ease,transform .5s cubic-bezier(.16,1.35,.3,1),
             border-color .3s ease,color .3s ease}
.step.placed{opacity:1;transform:none;color:var(--text);
  animation:step-pop .48s cubic-bezier(.16,1.4,.3,1) both}
/* Opacity is 1 from the first frame: the words are legible the instant they
   land, with only the form around them still arriving. */
@keyframes step-pop{0%{opacity:1;transform:scale(.92)}65%{transform:scale(1.035)}100%{opacity:1;transform:none}}

/* An invisible measuring anchor: the dust draws whatever shape this element
   currently has. It starts as the grown border and contracts into the wheel,
   which is why the turn reads as a morph rather than a swap. */
.wheel{position:absolute;z-index:2;left:50%;top:var(--core-y,50%);
  width:var(--box-w,calc(100% - 2px));height:var(--box-h,calc(100% - 2px));
  border:0;background:transparent;opacity:0;transform:translate(-50%,-50%);
  transition:width 1.1s cubic-bezier(.4,0,.2,1),height 1.1s cubic-bezier(.4,0,.2,1),opacity .25s ease}

/* Topmost is the dust: it is the thing being drawn, not scenery behind the
   picture. Behind the entries it ran under their own backgrounds, which read as
   a black rectangle sitting on the border the dust was building. */
.magic{position:absolute;left:-40rem;top:-8rem;z-index:3;
  width:calc(100% + 52rem);height:calc(100% + 16rem);pointer-events:none;overflow:visible}

/* Above the dust: the finished analysis is a solid card that carries its own
   black ground, so it reads as an object being handed over. It is the one thing
   the dust does not run in front of. */
.result{position:absolute;left:50%;top:var(--core-y,50%);z-index:4;
  padding:.72rem 1.1rem;border:1px solid var(--gold);background:var(--bg);color:var(--text);
  font:500 .72rem/1 var(--sans);letter-spacing:.16em;text-transform:uppercase;white-space:nowrap;
  opacity:0;transform:translate(-50%,-50%) scale(.2);transform-origin:center;
  transition:opacity .25s ease,transform .85s cubic-bezier(.4,0,.2,1)}

/* Row two of the first column: it belongs under the inbox, because it is about
   what is now sitting in it. */
.ready{grid-column:1;margin:clamp(.4rem,1.4svh,1rem) 0 0;font:500 .66rem/1 var(--sans);
  letter-spacing:.24em;text-transform:uppercase;color:var(--gold);opacity:0;
  transition:opacity .7s ease;text-align:center}

/* ---- the stages ---- */
#koup[data-stage="wheel"] .box-label,#koup[data-stage="result"] .box-label,
#koup[data-stage="deliver"] .box-label,#koup[data-stage="done"] .box-label{opacity:0}
/* Gone before the border starts pulling in, so nothing is left to spill over
   the edge on the way to the square. */
#koup[data-stage="wheel"] .steps,#koup[data-stage="result"] .steps,
#koup[data-stage="deliver"] .steps{opacity:0;transition:opacity .25s ease}
/* Once round, the wheel holds exactly one size. Nothing about it scales or
   rotates: the turning is the dust running along the line, drawn in JS. */
#koup[data-stage="wheel"] .wheel,#koup[data-stage="result"] .wheel{opacity:1;width:148px;height:148px}
#koup[data-stage="deliver"] .wheel,#koup[data-stage="done"] .wheel{opacity:0;width:148px;height:148px;
  transition:opacity .45s ease}
/* It is born at the wheel, at the wheel's own position ... */
#koup[data-stage="result"] .result{opacity:1;transform:translate(-50%,-50%) scale(1);
  transition:opacity .25s ease .12s,transform .6s cubic-bezier(.16,1.4,.3,1) .08s}
/* ... and then it crosses to the inbox. */
#koup[data-stage="deliver"] .result{opacity:1;
  transform:translate(calc(-50% + var(--delivery-x,0px)),calc(-50% + var(--delivery-y,0px))) scale(1);
  transition:transform .95s cubic-bezier(.4,0,.2,1)}
/* Arriving is not the same as being delivered. The travelling card shrinks away
   into the inbox and the inbox reports what it now holds -- otherwise the
   analysis parks on top of its destination and hides it. */
#koup[data-stage="done"] .result{opacity:0;
  transform:translate(calc(-50% + var(--delivery-x,0px)),calc(-50% + var(--delivery-y,0px))) scale(.14);
  transition:opacity .4s ease .12s,transform .5s cubic-bezier(.4,0,.2,1)}
/* The end state is one thing: your inbox with the finished analysis in it. The
   workflow's frame and its label go with the rest of the machinery -- once the
   result has arrived there is nothing left to point at beside it. */
/* The list does not come back. Once the analysis is in your inbox the five
   subtasks have been said -- what stands is the workflow that holds them. */
#koup[data-stage="done"] .steps{opacity:0}
/* Landed: the comparison opens on top of the offers, its bars draw one after
   another, and the offers it answers are read. */
#koup[data-stage="done"] .verdict{grid-template-rows:1fr}
#koup[data-stage="done"] .bar b{transform:none}
#koup[data-stage="done"] .bar:nth-child(2) b{transition-delay:.35s}
#koup[data-stage="done"] .bar:nth-child(3) b{transition-delay:.5s}
#koup[data-stage="done"] .bar:nth-child(4) b{transition-delay:.65s}
#koup[data-stage="done"] .bar em{opacity:1;transition-delay:1.2s}
#koup[data-stage="done"] .mails li{color:var(--muted)}
#koup[data-stage="done"] .mails li::before{opacity:0}
#koup[data-stage="done"] .ready{opacity:1}
/* The reset between runs: transitions off, or the snap back to the whole job is
   itself animated and the split is skipped. */
#koup.instant,#koup.instant *{transition:none!important}

/* A short window is the same problem as a narrow one: this beat has to fit the
   screen, whatever the screen is, because the page's snapping is all-or-nothing
   -- one beat that does not fit takes the carried scroll off every section. So
   the words step down with the height, exactly as they do with the width. */
@media (max-height:820px){
  #koup .label{margin-bottom:1rem}
  #koup .display{font-size:clamp(2.4rem,5vw,4rem)}
}
@media (max-height:660px){
  #koup .label{margin-bottom:.7rem}
  #koup .display{font-size:clamp(2rem,3.8vw,2.8rem)}
  .job,.flow{min-height:clamp(170px,30svh,330px)}
  .step{min-height:34px;padding:.4rem .6rem}
  .steps{gap:.3rem}
}

@media (max-width:820px){
  /* Stacked, the three stations have to share one screen -- and the beat has to
     stay one screen tall, or the page's snapping stands down and every stop
     below it drifts. So each column is sized to itself and the desktop's
     breathing room comes out: the inbox is the end of the story and it has to
     be above the fold with the rest of it. */
  /* The beat is one screen or the page's snapping stands down, so the space
     between the words and the figure is the first thing to go. */
  #koup .label{margin-bottom:1.1rem}
  #koup .display{font-size:clamp(2rem,8vw,2.6rem)}
  #koup .sub{margin-top:.9rem}
  .stage{grid-template-columns:1fr;justify-items:center;row-gap:.35rem;text-align:center;
    margin-top:1.1rem}
  .job{width:min(100%,19rem);min-height:210px}
  .job-box{top:54%;width:min(74%,220px);padding:.7rem .85rem .55rem}
  .job-label{margin-bottom:.35rem}
  .mails li{padding:.34rem 0;font-size:.7rem}
  .flow{width:min(100%,19rem);min-height:0;padding:1.5rem .9rem .9rem}
  .steps{gap:.28rem}
  .step{min-height:34px;padding:.38rem .6rem}
  .box-label{left:50%}
  /* The desktop orbit needs more side room than a phone has. On the narrow
     canvas the five pieces form clean rows around the card instead, retaining
     the scattered premise without allowing labels to hide behind the task. */
  .source-task{font-size:.6rem}
  .source-task:nth-child(2){left:0;top:0}
  .source-task:nth-child(3){right:0;top:0}
  .source-task:nth-child(4){left:0;top:auto;bottom:0}
  .source-task:nth-child(5){right:0;top:auto;bottom:0}
  .source-task:nth-child(6){left:50%;top:1.75rem}
  .ready{margin-top:.9rem}
}
/* Nothing here carries meaning the motion alone supplies, so reduced motion
   gets the finished picture rather than a degraded performance. */
@media (prefers-reduced-motion:reduce){
  #koup *{transition:none!important;animation:none!important}
}
