/* Flat vector pictures (tutorial-art.js). Plain shapes; the craft is in the motion:
   springs that overshoot and settle, a small move back before a big move, squash on landing,
   arcs instead of straight lines, and parts that lag behind the whole.
   Each animation waits on its first frame until the picture is on screen (.is-play), then plays;
   .is-rest shows the finished drawing without motion. */
.ns-art{--a-paper:#fff;--a-line:#e8e8ee;--a-line2:#d9d9e0;--a-ink:#1d1d1f;--a-ink2:#3a3a3f;--a-p1:#7532d6;--a-p2:#a688ee;--a-p3:#efe8fc;--a-p4:#5b22b3;--a-y1:#ffc93c;--a-y2:#fff3cf;--a-y3:#f0a500;--a-g1:#2fbf71;--a-g2:#dcf5e7;--a-g3:#1f9a58;--a-c1:#ff7a59;--a-c2:#ffe3d9;--a-c3:#e85a39;--a-b1:#3d7bfd;--a-b2:#e2ecff;--a-b3:#b9cffd;--a-k1:#e3a868;--a-k2:#c9894a;--a-k3:#f6dcb6;--a-k4:#fbeedd;--a-cursor:#1d1d1f;--a-text:#1d1d1f;--a-muted:#6e6e73;
 /* Motion curves. --spring overshoots about 16% and settles; --spring-soft about 5%. */
 --spring:linear(0,.093,.307,.561,.795,.977,1.095,1.152,1.162,1.14,1.102,1.061,1.025,.998,.981,.974,.974,.979,.985,.992,.997,1.001,1.003,1.004,1.004,1.003,1.002,1.001,1,1,.999,.999,1);
 --spring-soft:linear(0,.068,.224,.411,.592,.747,.868,.954,1.01,1.04,1.053,1.053,1.047,1.038,1.027,1.018,1.011,1.005,1.001,.999,.997,.997,.997,.998,.998,.999,.999,1,1,1,1,1,1);
 --out:cubic-bezier(.22,1,.36,1);--in-out:cubic-bezier(.65,0,.35,1);--fall:cubic-bezier(.55,0,1,.45);--rise:cubic-bezier(.2,.75,.45,1);
 display:block;overflow:visible}
/* The site sets svg{stroke:currentColor;fill:none} for line icons; these pictures are filled shapes. */
svg.ns-art{width:100%;height:auto;stroke:none;fill:none;stroke-width:1}
:root[data-theme=dark] .ns-art{--a-paper:#2c2c2e;--a-line:#3a3a3e;--a-line2:#4a4a50;--a-ink:#0b0b0c;--a-ink2:#8e8e93;--a-p1:#8a5cf0;--a-p2:#6d4fc0;--a-p3:#2a2140;--a-p4:#6a3fd0;--a-y2:#3a3218;--a-g2:#173527;--a-g3:#1f8a4f;--a-c2:#40231b;--a-b2:#1a2742;--a-b3:#2f4a80;--a-k1:#b07a45;--a-k2:#8f5f33;--a-k3:#d6b48a;--a-k4:#3a2a1a;--a-cursor:#f5f5f7;--a-text:#f5f5f7;--a-muted:#a1a1a6}
.ns-art *{transform-box:fill-box;transform-origin:center}
/* :root svg.ns-art (0,2,1) outranks the per-picture animation shorthands (0,2,0), which reset the play state. */
:root svg.ns-art *{animation-play-state:paused}
:root svg.ns-art.is-play *{animation-play-state:running}
.ns-art.is-rest *{animation:none!important}
.tut-level.is-coming .ns-level{filter:saturate(.12);opacity:.65}
/* Level pictures keep their motion inside the tile. */
svg.ns-level{clip-path:inset(0 round 28.6%)}

/* L0 the coin falls, squashes on the stack, bounces once and settles; the stack gives a little. */
.ns-l0 .a-coin{transform-origin:50% 100%;animation:l0-coin 1.3s both}
.ns-l0 .a-stack{transform-origin:50% 100%;animation:l0-stack 1.3s both}
@keyframes l0-coin{0%{opacity:0;transform:translateY(-22px) scale(.9,1.12);animation-timing-function:var(--fall)}6%{opacity:1}38%{transform:translateY(0) scale(1.18,.8);animation-timing-function:cubic-bezier(.25,.8,.5,1)}56%{transform:translateY(-7px) scale(.95,1.06);animation-timing-function:cubic-bezier(.5,0,.75,.4)}70%{transform:translateY(0) scale(1.07,.93);animation-timing-function:var(--spring-soft)}100%{transform:none}}
@keyframes l0-stack{0%,37%{transform:none;animation-timing-function:cubic-bezier(.25,.8,.5,1)}45%{transform:scale(1.04,.9);animation-timing-function:var(--spring)}100%{transform:none}}
/* L1 the magnifier sweeps in on a curve, looks left and right, settles. */
.ns-l1 .a-glass{animation:l1-glass 1.45s both}
@keyframes l1-glass{0%{opacity:0;transform:translate(-20px,-14px) rotate(-18deg) scale(.85);animation-timing-function:var(--out)}15%{opacity:1}45%{transform:translate(4px,-3px) rotate(6deg);animation-timing-function:cubic-bezier(.45,0,.55,1)}68%{transform:translate(-2px,1px) rotate(-3deg) scale(1.06);animation-timing-function:var(--spring-soft)}100%{transform:none}}
/* L2 the parcel drops and squashes; the check springs in with a turn. */
.ns-l2 .a-box{transform-origin:50% 100%;animation:l2-box 1.05s both}
.ns-l2 .a-ok{animation:l2-ok 1.35s both}
@keyframes l2-box{0%{opacity:0;transform:translateY(-18px) scale(.94,1.07);animation-timing-function:var(--fall)}10%{opacity:1}42%{transform:translateY(0) scale(1.09,.87);animation-timing-function:var(--spring)}100%{transform:none}}
@keyframes l2-ok{0%,40%{opacity:0;transform:scale(0) rotate(-30deg);animation-timing-function:var(--spring)}41%{opacity:1}78%,100%{opacity:1;transform:none}}
/* L3 the page builds block by block, then its Buy button is pressed. */
.ns-l3 :is(.a-b1,.a-b2,.a-b3){animation:l3-block .65s var(--spring-soft) both}
.ns-l3 .a-b1{animation-delay:.05s}.ns-l3 .a-b2{animation-delay:.17s}.ns-l3 .a-b3{animation-delay:.29s}
.ns-l3 .a-b4{animation:l3-block .65s var(--spring-soft) .41s both,l3-press .55s 1s forwards}
@keyframes l3-block{from{opacity:0;transform:translateY(5px) scale(.7)}to{opacity:1;transform:none}}
@keyframes l3-press{0%{transform:none;animation-timing-function:cubic-bezier(.3,0,.6,1)}22%{transform:scale(.94,.7);animation-timing-function:var(--spring)}100%{transform:none}}
/* L4 the card glides in; the shield drops onto it with a turn and a bounce. */
.ns-l4 .a-card{animation:l4-card .75s var(--out) both}
.ns-l4 .a-shield{animation:l4-shield .85s var(--spring) .38s both}
@keyframes l4-card{from{opacity:0;transform:translateX(-14px)}to{opacity:1;transform:none}}
@keyframes l4-shield{from{opacity:0;transform:translateY(-12px) rotate(-24deg) scale(.6)}to{opacity:1;transform:none}}
/* L5 the bars spring up one after another. */
.ns-l5 .a-bar{transform-origin:50% 100%;animation:l5-bar .85s var(--spring) both}
.ns-l5 .a-bar2{animation-delay:.09s}.ns-l5 .a-bar3{animation-delay:.18s}
@keyframes l5-bar{from{transform:scaleY(0)}to{transform:none}}
/* L6 the truck rolls in and brakes: the body pitches forward and settles; the road slows with it. */
.ns-l6 .a-truck{animation:l6-truck 1.3s both}
.ns-l6 .a-body{transform-origin:70% 100%;animation:l6-body 1.3s both}
.ns-l6 .a-road{animation:l6-road 1.3s cubic-bezier(.16,.8,.3,1) both}
@keyframes l6-truck{0%{opacity:0;transform:translateX(-36px);animation-timing-function:cubic-bezier(.16,.8,.3,1)}18%{opacity:1}100%{transform:none}}
@keyframes l6-body{0%,40%{transform:none;animation-timing-function:cubic-bezier(.3,0,.5,1)}55%{transform:rotate(5deg);animation-timing-function:var(--spring)}100%{transform:none}}
@keyframes l6-road{from{stroke-dashoffset:40}to{stroke-dashoffset:0}}
/* L7 the line draws itself and the end point springs up. */
.ns-l7 .a-line{stroke-dasharray:1 1;animation:l7-line .9s var(--in-out) both}
.ns-l7 .a-dot{animation:l7-dot .75s var(--spring) .8s both}
@keyframes l7-line{from{stroke-dashoffset:1}to{stroke-dashoffset:0}}
@keyframes l7-dot{from{opacity:0;transform:scale(0)}to{opacity:1;transform:none}}

/* Hero: one order's trip, a 6.4 s loop while on screen.
   0–1.2 s a pointer curves in and taps Buy · 0.8–2.8 s the truck backs a touch, pulls away
   leaning back, bobs, brakes leaning forward · 2.8 s the house gives and the check springs up ·
   3.3–4.6 s the coin pops, flips twice along an arc and drops into the price, which jumps and
   turns green · 5.1–6.4 s the truck leaves and the next one rolls in. */
.ns-hero *{animation-duration:6.4s;animation-iteration-count:infinite;animation-timing-function:linear;animation-fill-mode:both}
.ns-hero :is(.a-pointer,.a-ok,.a-money){opacity:0}
.ns-hero .a-px{animation-name:h-px}.ns-hero .a-py{animation-name:h-py}
.ns-hero .a-pointer{transform-origin:0 0;animation-name:h-pointer}
.ns-hero .a-buy{animation-name:h-buy}
.ns-hero .a-truck{animation-name:h-truck}
.ns-hero .a-body{transform-origin:50% 100%;animation-name:h-body}
.ns-hero .a-house{transform-origin:50% 100%;animation-name:h-house}
.ns-hero .a-ok{animation-name:h-ok}
.ns-hero .a-mx{animation-name:h-mx}.ns-hero .a-my{animation-name:h-my}
.ns-hero .a-money{animation-name:h-money}
.ns-hero .a-price{animation-name:h-price}
@keyframes h-px{0%{transform:translateX(42px);animation-timing-function:var(--out)}11%{transform:none}19%{transform:none;animation-timing-function:cubic-bezier(.5,0,.75,0)}25%,100%{transform:translateX(14px)}}
@keyframes h-py{0%{transform:translateY(36px);animation-timing-function:cubic-bezier(.33,0,.15,1)}11%{transform:none}19%{transform:none;animation-timing-function:cubic-bezier(.5,0,.75,0)}25%,100%{transform:translateY(16px)}}
@keyframes h-pointer{0%{opacity:0;transform:none}4%{opacity:1}11%{transform:none;animation-timing-function:cubic-bezier(.3,0,.6,1)}12.5%{transform:scale(.82);animation-timing-function:var(--spring)}18%{transform:none}21%{opacity:1}25%,100%{opacity:0}}
@keyframes h-buy{0%,11%{transform:none;animation-timing-function:cubic-bezier(.3,0,.6,1)}12.5%{transform:scale(.97,.86);animation-timing-function:var(--spring)}19%,100%{transform:none}}
@keyframes h-truck{0%,13%{transform:none;opacity:1;animation-timing-function:cubic-bezier(.3,0,.5,1)}16%{transform:translateX(-5px);animation-timing-function:cubic-bezier(.55,0,.25,1)}43%,80%{transform:translateX(118px);opacity:1;animation-timing-function:cubic-bezier(.55,0,1,.45)}85%{transform:translateX(160px);opacity:0}86%{transform:translateX(-40px);opacity:0;animation-timing-function:cubic-bezier(.16,.9,.3,1)}94%,100%{transform:none;opacity:1}}
@keyframes h-body{0%,13%{transform:none;animation-timing-function:cubic-bezier(.3,0,.5,1)}16%{transform:scale(1.04,.92);animation-timing-function:cubic-bezier(.3,0,.3,1)}20%{transform:rotate(-3deg);animation-timing-function:cubic-bezier(.45,0,.55,1)}26%{transform:translateY(-1.2px);animation-timing-function:cubic-bezier(.45,0,.55,1)}31%{transform:none;animation-timing-function:cubic-bezier(.45,0,.55,1)}36%{transform:translateY(-1px);animation-timing-function:cubic-bezier(.2,0,.2,1)}42%{transform:none;animation-timing-function:cubic-bezier(.2,0,.3,1)}44.5%{transform:rotate(4deg);animation-timing-function:var(--spring)}56%,80%{transform:none;animation-timing-function:cubic-bezier(.4,0,.6,1)}82.5%{transform:rotate(-2.5deg);animation-timing-function:cubic-bezier(.4,0,.6,1)}86%,92%{transform:none;animation-timing-function:cubic-bezier(.2,0,.3,1)}94.5%{transform:rotate(3deg);animation-timing-function:var(--spring)}100%{transform:none}}
@keyframes h-house{0%,43%{transform:none;animation-timing-function:cubic-bezier(.3,0,.5,1)}45.5%{transform:scale(1.03,.94);animation-timing-function:var(--spring)}56%,100%{transform:none}}
@keyframes h-ok{0%,45%{opacity:0;transform:scale(0) rotate(-25deg);animation-timing-function:var(--spring)}46%{opacity:1}56%,74%{opacity:1;transform:none;animation-timing-function:cubic-bezier(.5,0,.75,0)}78%,100%{opacity:0;transform:scale(.4)}}
@keyframes h-money{0%,52%{opacity:0;transform:scale(0);animation-timing-function:var(--spring)}53%{opacity:1}57%{transform:none}61%{transform:scaleX(-1)}65%{transform:none}69%{transform:scaleX(-1);animation-timing-function:cubic-bezier(.5,0,.75,0)}72.5%{transform:scale(.3);opacity:1}73.5%,100%{opacity:0;transform:scale(.3)}}
@keyframes h-mx{0%,57%{transform:none;animation-timing-function:cubic-bezier(.45,0,.55,1)}72.5%,100%{transform:translateX(-157px)}}
@keyframes h-my{0%,57%{transform:none;animation-timing-function:var(--rise)}64%{transform:translateY(-108px);animation-timing-function:cubic-bezier(.55,0,.8,.35)}72.5%,100%{transform:translateY(-56px)}}
@keyframes h-price{0%,72%{transform:none;fill:var(--a-ink2);animation-timing-function:cubic-bezier(.2,0,.2,1)}74%{transform:scale(1.18,1.3);fill:var(--a-g1);animation-timing-function:var(--spring)}82%,92%{transform:none;fill:var(--a-g1)}97%,100%{fill:var(--a-ink2)}}

/* Lesson 0.1 money flow: one order's $30, 4.4 s, plays when it comes into view; click to replay.
   Each coin pops, arcs to its owner and shrinks in; the owner gives a little and springs back;
   the store's amount slides up out of the way as the next one slides in. At rest it shows the
   finished picture: every share taken, $4.80 left. */
.tb-art{container-type:inline-size;margin:4px 0 20px;cursor:pointer;-webkit-tap-highlight-color:transparent}
.tb-art .ns-flow.is-tall{max-width:340px;margin:0 auto}
@container (max-width:460px){.tb-art .ns-flow.is-wide{display:none}}
@container (min-width:461px){.tb-art .ns-flow.is-tall{display:none}}
.ns-flow text{font-family:inherit;font-size:13px;fill:var(--a-muted)}
.ns-flow .f-amt,.ns-flow .f-left-t{font-size:15px;font-weight:650;fill:var(--a-text);font-variant-numeric:tabular-nums}
.ns-flow .f-left-t{font-size:14px;fill:#fff}
.ns-flow .f-line{fill:none;stroke:var(--a-line);stroke-width:2.5;stroke-linecap:round;stroke-dasharray:2 7}
.ns-flow :is(.f-face,.f-t0,.f-t1,.f-t2){opacity:0}
.ns-flow *{animation-duration:4.4s;animation-timing-function:linear;animation-fill-mode:both}
.ns-flow .f-shop{animation-name:f-shop}
.ns-flow .f-c0{animation-name:f-x0}.ns-flow .f-c0y{animation-name:f-y0}.ns-flow .f-c0f{animation-name:f-f0}
.ns-flow .f-c1{animation-name:f-x1}.ns-flow .f-c1y{animation-name:f-y1}.ns-flow .f-c1f{animation-name:f-f1}
.ns-flow .f-c2{animation-name:f-x2}.ns-flow .f-c2y{animation-name:f-y2}.ns-flow .f-c2f{animation-name:f-f2}
.ns-flow .f-c3{animation-name:f-x3}.ns-flow .f-c3y{animation-name:f-y3}.ns-flow .f-c3f{animation-name:f-f3}
.ns-flow .f-t0{animation-name:f-t0}.ns-flow .f-t1{animation-name:f-t1}.ns-flow .f-t2{animation-name:f-t2}
.ns-flow .f-p1{animation-name:f-p1}.ns-flow .f-p2{animation-name:f-p2}.ns-flow .f-p3{animation-name:f-p3}
.ns-flow .f-a1{animation-name:f-a1}.ns-flow .f-a2{animation-name:f-a2}.ns-flow .f-a3{animation-name:f-a3}
.ns-flow .f-left{animation-name:f-left}
/* coin 0: buyer → store (0.15–1.05 s) */
@keyframes f-f0{0%,3.4%{opacity:0;transform:scale(0);animation-timing-function:var(--spring)}4%{opacity:1}8%,21.5%{opacity:1;transform:none;animation-timing-function:cubic-bezier(.5,0,.75,0)}23.9%,100%{opacity:0;transform:scale(.3)}}
@keyframes f-x0{0%,6.8%{transform:none;animation-timing-function:cubic-bezier(.45,0,.55,1)}22.7%,100%{transform:translateX(var(--dx))}}
@keyframes f-y0{0%,6.8%{transform:none;animation-timing-function:var(--rise)}14.8%{transform:translateY(var(--mid));animation-timing-function:cubic-bezier(.55,0,.8,.35)}22.7%,100%{transform:translateY(var(--dy))}}
@keyframes f-shop{0%,22.7%{transform:none;animation-timing-function:cubic-bezier(.3,0,.5,1)}25%{transform:scale(.93);animation-timing-function:var(--spring)}36%,100%{transform:none}}
/* coin 1: store → payment platform (1.05–1.75 s) */
@keyframes f-f1{0%,23.9%{opacity:0;transform:scale(0);animation-timing-function:var(--spring)}24.5%{opacity:1}28%,38.5%{opacity:1;transform:none;animation-timing-function:cubic-bezier(.5,0,.75,0)}40.5%,100%{opacity:0;transform:scale(.3)}}
@keyframes f-x1{0%,26.1%{transform:none;animation-timing-function:cubic-bezier(.45,0,.55,1)}39.8%,100%{transform:translateX(var(--dx))}}
@keyframes f-y1{0%,26.1%{transform:none;animation-timing-function:var(--rise)}33%{transform:translateY(var(--mid));animation-timing-function:cubic-bezier(.55,0,.8,.35)}39.8%,100%{transform:translateY(var(--dy))}}
/* coin 2: store → ad platform (1.8–2.5 s) */
@keyframes f-f2{0%,40.9%{opacity:0;transform:scale(0);animation-timing-function:var(--spring)}41.5%{opacity:1}45%,55.5%{opacity:1;transform:none;animation-timing-function:cubic-bezier(.5,0,.75,0)}57.5%,100%{opacity:0;transform:scale(.3)}}
@keyframes f-x2{0%,43.2%{transform:none;animation-timing-function:cubic-bezier(.45,0,.55,1)}56.8%,100%{transform:translateX(var(--dx))}}
@keyframes f-y2{0%,43.2%{transform:none;animation-timing-function:var(--rise)}50%{transform:translateY(var(--mid));animation-timing-function:cubic-bezier(.55,0,.8,.35)}56.8%,100%{transform:translateY(var(--dy))}}
/* coin 3: store → CJ (2.55–3.25 s) */
@keyframes f-f3{0%,58%{opacity:0;transform:scale(0);animation-timing-function:var(--spring)}58.6%{opacity:1}62%,72.6%{opacity:1;transform:none;animation-timing-function:cubic-bezier(.5,0,.75,0)}74.6%,100%{opacity:0;transform:scale(.3)}}
@keyframes f-x3{0%,60.2%{transform:none;animation-timing-function:cubic-bezier(.45,0,.55,1)}73.9%,100%{transform:translateX(var(--dx))}}
@keyframes f-y3{0%,60.2%{transform:none;animation-timing-function:var(--rise)}67%{transform:translateY(var(--mid));animation-timing-function:cubic-bezier(.55,0,.8,.35)}73.9%,100%{transform:translateY(var(--dy))}}
/* the store's amount slides through $30.00 → $28.80 → $18.80 */
@keyframes f-t0{0%,22.7%{opacity:0;transform:translateY(8px);animation-timing-function:var(--out)}28.4%,39.8%{opacity:1;transform:none;animation-timing-function:cubic-bezier(.5,0,.75,0)}44.3%,100%{opacity:0;transform:translateY(-8px)}}
@keyframes f-t1{0%,39.8%{opacity:0;transform:translateY(8px);animation-timing-function:var(--out)}46.6%,56.8%{opacity:1;transform:none;animation-timing-function:cubic-bezier(.5,0,.75,0)}61.4%,100%{opacity:0;transform:translateY(-8px)}}
@keyframes f-t2{0%,56.8%{opacity:0;transform:translateY(8px);animation-timing-function:var(--out)}63.6%,73.9%{opacity:1;transform:none;animation-timing-function:cubic-bezier(.5,0,.75,0)}78.4%,100%{opacity:0;transform:translateY(-8px)}}
/* each owner gives a little when its coin lands */
@keyframes f-p1{0%,39.8%{transform:none;animation-timing-function:cubic-bezier(.3,0,.5,1)}42%{transform:scale(.9);animation-timing-function:var(--spring)}52%,100%{transform:none}}
@keyframes f-p2{0%,56.8%{transform:none;animation-timing-function:cubic-bezier(.3,0,.5,1)}59%{transform:scale(.9);animation-timing-function:var(--spring)}69%,100%{transform:none}}
@keyframes f-p3{0%,73.9%{transform:none;animation-timing-function:cubic-bezier(.3,0,.5,1)}76.1%{transform:scale(.9);animation-timing-function:var(--spring)}86%,100%{transform:none}}
@keyframes f-a1{0%,39.8%{opacity:.35;transform:scale(.92);animation-timing-function:var(--spring)}52%,100%{opacity:1;transform:none}}
@keyframes f-a2{0%,56.8%{opacity:.35;transform:scale(.92);animation-timing-function:var(--spring)}69%,100%{opacity:1;transform:none}}
@keyframes f-a3{0%,73.9%{opacity:.35;transform:scale(.92);animation-timing-function:var(--spring)}86%,100%{opacity:1;transform:none}}
/* what is left springs in */
@keyframes f-left{0%,76.1%{opacity:0;transform:scale(.4);animation-timing-function:var(--spring)}77%{opacity:1}90%,100%{opacity:1;transform:none}}

@media(prefers-reduced-motion:reduce){.ns-art *{animation:none!important}}
