/* Shared look for every lesson: the lecture notebook, one beat at a time. */
:root{--paper:#fbfbf8;--grid:#dfe7ee;--pencil:#2b2b2b;--red:#d9534f;--margin:72px;--i:0;--tilt:2deg} /* --i, --tilt: defaults; drawings and photos set their own */
*{box-sizing:border-box}
[hidden]{display:none !important} /* hidden must beat display:flex and friends */
html,body{background-color:var(--paper)}
html{scroll-behavior:smooth}
body{margin:0;min-height:100vh;color:var(--pencil);background-image:linear-gradient(var(--grid) 1px,transparent 1px),linear-gradient(90deg,var(--grid) 1px,transparent 1px);background-size:24px 24px;
  font:400 clamp(17px,1.2vw,20px)/1.65 "PT Serif",Georgia,serif;position:relative;overflow-x:hidden}
body::before{content:"";position:absolute;left:var(--margin);top:0;bottom:0;width:2px;background:var(--red)}
h1,h2,p,dl,dd,figure,pre{margin:0}
a{color:inherit}
:focus-visible{outline:3px solid var(--red);outline-offset:3px}
code,pre{font-family:"PT Mono",Consolas,monospace}
p code,li code,dd code{font-size:.92em;background:rgba(43,43,43,.06);padding:.05em .3em}

.page{padding:0 24px 30vh calc(var(--margin) + 40px);max-width:calc(var(--margin) + 40px + 56rem)}
.top{display:flex;justify-content:space-between;align-items:center;min-height:64px;font:700 1.1rem Kalam,cursive}
.top a{display:inline-flex;align-items:center;min-height:44px}
.js .top{opacity:0;transition:opacity .6s}
.js .started .top{opacity:1}
.count{font:700 1rem Kalam,cursive;color:#777}
.sound{position:fixed;bottom:12px;right:16px;z-index:5;min-height:44px;padding:0 10px;font:700 1rem Kalam,cursive;color:#777;background:var(--paper);border:0;text-decoration:underline;cursor:pointer}
.sound:hover{color:var(--red)}
.speed{right:120px}
.restart{right:auto;left:16px}
.back{max-width:34em;margin:18px auto 0;font:700 1.2rem Kalam,cursive;color:var(--red)}

/* Beats: only the first is visible; each "next" adds one more below. */
.beat{padding-top:12vh}
.beat:first-of-type{padding-top:22vh}
.js .beat:not(.on){display:none}
h1{font:700 clamp(44px,7vw,108px)/1.02 Kalam,cursive}
h1 span{display:block}
h2{font:700 clamp(32px,4.4vw,64px)/1.05 Kalam,cursive;margin-bottom:14px}
.beat p,.beat ul,.beat ol{max-width:36em}
.beat p+p,.beat p+ul,.beat ul+p,.beat p+ol,.beat ol+p{margin-top:12px}
.hand{font:700 1.35rem/1.35 Kalam,cursive}
.js .write{clip-path:inset(0 100% 0 0);animation:write 1s ease-out forwards}
.js .write.later{animation-delay:1.3s}
@keyframes write{to{clip-path:inset(0 0 0 0)}}
/* the first button waits until the handwriting is done: the page starts with nothing to do */
.js .beat:first-of-type .next{animation:popIn .4s 2.5s cubic-bezier(.3,1.6,.5,1) backwards}
.js .beat:first-of-type .next[disabled]{animation:none} /* used, or reopened after a refresh: no pop-in */
/* a Next that pops up (beats.js adds .popped when it is unhidden) bounces in; "backwards" lets go after, so :active still moves it */
.js .next.popped{animation:popIn .4s cubic-bezier(.3,1.6,.5,1) backwards}
@keyframes popIn{from{opacity:0;transform:scale(.6)}}

.btn{display:inline-flex;align-items:center;min-height:48px;margin-top:22px;padding:0 20px;font:700 1.3rem Kalam,cursive;background:var(--paper);color:var(--pencil);border:2px solid var(--pencil);border-radius:10px 4px 12px 4px;cursor:pointer}
.btn:hover{color:var(--red);border-color:var(--red)}
.btn[disabled]{opacity:.35;cursor:default}
/* Next is the one filled button on each screen, pressed like the home page's start button */
.next{display:inline-flex;align-items:center;justify-content:center;min-height:52px;margin-top:26px;padding:0 34px;font:700 1.5rem Kalam,cursive;color:var(--paper);background:var(--pencil);border:2px solid var(--pencil);border-radius:10px 4px 12px 4px;box-shadow:3px 4px 0 rgba(43,43,43,.22);cursor:pointer;text-decoration:none;transition:background-color .15s,border-color .15s}
.next[disabled]{opacity:.45;cursor:default;box-shadow:none}
.next:not([disabled]):hover{background:var(--red);border-color:var(--red)}
.top a:hover{color:var(--red);transform:translateX(3px);transition:transform .15s,color .15s}
.top a:hover{transform:translateX(-3px)}

.ask{display:flex;flex-wrap:wrap;gap:10px;align-items:center;margin-top:22px}
.ask input,textarea{font:17px/1.5 "PT Serif",Georgia,serif;color:var(--pencil);background:#fff;border:2px solid var(--pencil);padding:10px 12px}
.ask input{min-height:48px;flex:1 1 220px;max-width:26em}
.ask .btn{margin-top:0}
textarea{display:block;width:100%;max-width:36em;min-height:150px;margin-top:16px}
.out{margin-top:16px;min-height:3.4em;padding:14px 18px;border:2px dashed var(--pencil);background:rgba(251,251,248,.9);font:16px/1.6 "PT Mono",Consolas,monospace;white-space:pre-wrap;overflow-wrap:anywhere;max-width:40em}
.out:empty{display:none}
.card{margin-top:18px;padding:16px 20px;max-width:36em;background:#fff;border:2px solid var(--pencil);transform:rotate(-.6deg)}
.note{margin-top:22px;max-width:30em;font:700 1.2rem/1.4 Kalam,cursive;color:var(--red);transform:rotate(-1deg)}
.note a{font:400 .85rem "PT Serif",Georgia,serif;color:var(--pencil)}

/* code you copy into agent.py */
.code{position:relative;margin:16px 0;max-width:62em}
.code pre{background:#fff;border:2px solid var(--pencil);padding:16px 18px;padding-right:84px;overflow-x:auto;font-size:15px;line-height:1.6}
/* code colors (classes from Pygments, added by tools/embed-python.py): coloured pens, all 4.5:1+ on white */
pre code .k,pre code .kn,pre code .ow{color:#7b2fbe;font-weight:700}
pre code .s1,pre code .s2,pre code .sa,pre code .si,pre code .se{color:#1e7a34}
pre code .sd,pre code .c1,pre code .c,pre code .ch{color:#68707a;font-style:italic}
pre code .mi,pre code .mf,pre code .kc{color:#b04a00}
pre code .nf,pre code .fm{color:#1556c0;font-weight:700}
pre code .nb,pre code .bp,pre code .vm{color:#0a7570}
/* new lines in a whole agent.py (tools/embed-python.py marks them), like a yellow highlighter */
pre code .hll{background:#ffec80}
/* the old lines around them go plain grey (still 4.9:1), so the eye lands on the new ones and the whole file stays readable */
pre code:has(.hll) > span:not(.hll){color:#707070;font-weight:400;font-style:normal}
.marknote{background:#ffec80;padding:0 .3em}
.copy{position:absolute;top:8px;right:8px;min-height:36px;padding:0 12px;font:700 1rem Kalam,cursive;background:var(--paper);color:var(--pencil);border:2px solid var(--pencil);cursor:pointer}
.copy:hover{color:var(--red);border-color:var(--red)}

/* pencil drawings */
.drawing{display:block;width:100%;max-width:520px;height:auto;margin-top:18px}
.ink{fill:none;stroke:var(--pencil);stroke-width:3;stroke-linecap:round;stroke-linejoin:round}
.redl{fill:none;stroke:var(--red);stroke-width:3.5;stroke-linecap:round;stroke-linejoin:round}
.lbl{font:700 22px Kalam,cursive;fill:var(--pencil)}
.lbl.r{fill:var(--red)}
.mono{font:16px "PT Mono",monospace;fill:var(--pencil)}

/* drawings that draw themselves: add .play to a .scene; --i staggers the strokes (needs pathLength="1") */
@keyframes draw{to{stroke-dashoffset:0}}
@keyframes show{to{opacity:1}}
.js .scene .d{stroke-dasharray:1;stroke-dashoffset:1}
.js .scene .fade{opacity:0}
.js .scene.play .d{animation:draw .8s ease-out forwards;animation-delay:calc(var(--i,0) * .25s)}
.js .scene.play .fade{animation:show .4s ease forwards;animation-delay:calc(var(--i,0) * .25s)}

/* a story: comic panels that draw in one after another */
.story{display:grid;grid-template-columns:repeat(auto-fill,minmax(200px,1fr));gap:20px;margin-top:20px;max-width:58rem}
.panel{margin:0;padding:10px 12px 12px;background:#fff;border:2px solid var(--pencil)}
.panel svg{display:block;width:100%;height:auto}
.panel figcaption{margin-top:6px;font:700 1.1rem/1.3 Kalam,cursive}
.js .story .panel{opacity:0;transition:opacity .3s}
.js .story .panel.play{opacity:1}

/* a spread: the words on the left, the drawing pinned on the right */
.beat.spread{display:grid;grid-template-columns:minmax(0,1.25fr) minmax(0,1fr);gap:0 36px;align-items:start}
.beat.spread>*{grid-column:1}
.beat.spread>.drawing{grid-column:2;grid-row:1/span 6;margin-top:0}
.js .beat.spread:not(.on){display:none}

.words{display:grid;grid-template-columns:minmax(8em,13em) minmax(0,1fr);gap:10px 26px;margin-top:18px;max-width:42em;border-top:2px solid var(--pencil);padding-top:14px}
.words dt{font:700 1.25rem Kalam,cursive}
/* the last beat is a page pinned on top */
.beat.last{margin-top:12vh;padding:28px 30px;background:#fff;border:2px solid var(--pencil);max-width:46rem}
.end{margin-top:40px;padding-top:20px;border-top:2px solid var(--pencil);display:flex;flex-wrap:wrap;justify-content:space-between;gap:16px;align-items:center;max-width:44rem}
.end a{font:700 1.6rem Kalam,cursive;color:var(--red);text-decoration-thickness:3px;text-underline-offset:6px;min-height:48px;display:inline-flex;align-items:center}

@media (max-width:700px){
  :root{--margin:24px}
  .page{padding:0 16px 30vh calc(var(--margin) + 16px)}
  .words,.beat.spread{grid-template-columns:1fr}
  .beat.spread>.drawing{grid-column:1;grid-row:auto;margin-top:18px}
  .beat.last{padding:20px 18px}
}
@media (prefers-reduced-motion:reduce){
  html{scroll-behavior:auto}
  .js .write{animation:none;clip-path:none}
  .js .beat:first-of-type .next,.js .next.popped{animation:none}
  .js .scene .d{stroke-dashoffset:0;animation:none}
  .js .scene .fade{opacity:1;animation:none}
  .js .story .panel{opacity:1}
}

/* "the Python behind it": code that folds out under an animation */
.py{margin-top:14px;max-width:42em}
.py summary{display:inline-flex;align-items:center;min-height:44px;font:700 1.2rem Kalam,cursive;text-decoration:underline;text-underline-offset:5px;cursor:pointer}
.py summary:hover{color:var(--red)}
.explain{display:grid;grid-template-columns:minmax(0,max-content) minmax(0,1fr);gap:8px 18px;margin:4px 0 10px;font-size:.92em;max-width:58em}
.explain dt code{font:15px "PT Mono",Consolas,monospace;overflow-wrap:anywhere}
.explain dd{margin:0}
@media (max-width:700px){.explain{grid-template-columns:1fr;gap:2px}.explain dd{margin:0 0 10px}}

/* each new step slides in like a page being turned */
@keyframes beatIn{from{opacity:0;transform:translateY(28px) rotate(-.4deg)}}
.js .beat.on:not(:first-of-type){animation:beatIn .55s cubic-bezier(.2,.7,.2,1)}
.btn:active{transform:translate(1px,2px)}
.next:not([disabled]):active{transform:translate(2px,3px);box-shadow:1px 1px 0 rgba(43,43,43,.22)}
@media (prefers-reduced-motion:reduce){.js .beat.on{animation:none}}

/* "Did you know?" and "Tip" notes: a sticky note that drops onto the page */
.fact{display:block;margin:22px auto 0;max-width:34em;padding:14px 18px 16px;background:#fff4a8;box-shadow:3px 4px 0 rgba(43,43,43,.14);transform:rotate(-1.2deg);text-align:left;font:700 1.12rem/1.4 Kalam,cursive}
.fact b{display:block;color:var(--red);font-size:1.15em}
.fact a{font:400 .8rem "PT Serif",Georgia,serif}
.fact code{font-size:.85em;background:rgba(43,43,43,.08);padding:0 .25em}
@keyframes dropIn{from{opacity:0;transform:translateY(-26px) rotate(-7deg)}}
.js .fact:not([hidden]){animation:dropIn .5s cubic-bezier(.3,1.5,.5,1) both}
@media (prefers-reduced-motion:reduce){.js .fact:not([hidden]){animation:none}}

/* each step's heading writes itself in, with the pencil sound */
@keyframes writeIn{from{clip-path:inset(0 100% 0 0)}to{clip-path:inset(0 0 0 0)}}
.js .beat.on:not(:first-of-type) h2{animation:writeIn .9s ease-out both}
@media (prefers-reduced-motion:reduce){.js .beat.on:not(:first-of-type) h2{animation:none}} /* as specific as the rule it switches off */

/* Professor Graves's margin quips: red handwriting that writes itself in */
.quip{max-width:32em;margin:20px auto 0;font:700 1.35rem/1.35 Kalam,cursive;color:var(--red);transform:rotate(-1.5deg);text-align:center}
.quip span{display:block;margin-top:2px;font-size:.75em;color:#8a8a8a}
.quip span::before{content:"\2014 "}
.js .quip:not([hidden]){animation:writeIn 1s ease-out both}
@media (prefers-reduced-motion:reduce){.js .quip:not([hidden]){animation:none}}

/* photo memes: a polaroid taped onto the page */
.polaroid{position:relative;width:min(320px,82%);margin:26px auto 0;padding:12px 12px 10px;background:#fff;box-shadow:2px 6px 14px rgba(43,43,43,.22);transform:rotate(var(--tilt,2deg))}
.polaroid img{display:block;width:100%;height:auto}
.polaroid figcaption{margin-top:8px;font:700 1.15rem/1.3 Kalam,cursive;text-align:center}
.polaroid small{display:block;margin-top:2px;font:400 .7rem "PT Serif",Georgia,serif;color:#999}
.polaroid::before{content:"";position:absolute;top:-12px;left:50%;width:96px;height:26px;transform:translateX(-50%) rotate(-3deg);background:rgba(255,244,168,.75);box-shadow:0 1px 2px rgba(0,0,0,.08)}
@keyframes photoIn{from{opacity:0;transform:translateY(-30px) rotate(-8deg) scale(1.08)}}
.js .polaroid:not([hidden]){animation:photoIn .6s cubic-bezier(.3,1.4,.5,1) both}
@media (prefers-reduced-motion:reduce){.js .polaroid:not([hidden]){animation:none}}

/* Shared by the lessons: 3D jars, the narrator line, the step ladder and the "What's still wrong" box */
/* the jar as a real 3D model (assets/jar3d.js); the pencil jar stays for browsers without WebGL */
.jarbox{position:relative;width:min(400px,92%);margin:18px auto 0}
.jar{display:block;width:100%;height:auto}
.jar3d{position:absolute;left:0;top:-10%;width:60%;height:112%;cursor:grab;touch-action:pan-y}
.jar3d:active{cursor:grabbing}
.jarbox:not(.has3d) .hint3d,.jarbox.has3d .jarpart{display:none}
.jarbox.solo{width:min(280px,72%);margin:14px auto 6px}
.jarbox.solo:not(.has3d){display:none} /* no WebGL: the paragraph above already says it */
.solo .stage{position:relative;aspect-ratio:5/6}
.solo .jar3d{top:0;width:100%;height:100%}
.solo figcaption{font:700 1.2rem Kalam,cursive;text-align:center}
/* the narrator (Lesson.say): each sentence is written under the earlier ones, which turn grey and stay */
.say{min-height:2.8em;max-width:34em;margin:6px auto 0;font:700 1.45rem/1.35 Kalam,cursive;color:var(--pencil);text-align:center}
.say .said{display:block}
.say .said.was{color:#8a8a8a;font-size:.82em}
.say .more{display:block;margin:8px auto 0;min-height:40px;padding:2px 14px;font-size:1.05rem} /* speed "step by step" */
.js .say .said:last-child{animation:writeIn .8s ease-out both}
/* the centred stage of lesson 0, for the other lessons: <div class="page centred"> */
.page.centred{max-width:none;padding-right:40px}
.centred main{max-width:1120px;margin:0 auto}
.centred .beat{text-align:center}
.centred .beat>:is(p,.card,textarea,.code,.py,figure,svg,ul,ol,dl,div,aside,details,pre){margin-left:auto;margin-right:auto}
.centred .beat>p{max-width:40em}
.centred .beat :is(.card,.out,textarea,.py,pre,ul,ol,.explain,.code){text-align:left}
.centred .explain{margin-left:auto;margin-right:auto} /* nested in a .stg too, not only a direct child */
.centred .beat.last{text-align:left}
@media (max-width:700px){.page.centred{padding-right:16px}}
/* the ladder: the steps of the lesson, the current one marked */
.ladder{display:flex;flex-wrap:wrap;justify-content:center;gap:8px 6px;margin:6px auto 10px}
.ladder span{padding:4px 10px;border:2px solid #c9c9c9;color:#9a9a9a;font:700 1rem Kalam,cursive;background:var(--paper)}
.ladder span b{margin-right:.3em}
.ladder .done{border-color:var(--pencil);color:var(--pencil)}
.ladder .done::after{content:" \2713";color:var(--red)}
.ladder .now{border-color:var(--red);color:var(--red);background:#fff;transform:scale(1.1)}
.new{max-width:34em;margin:6px auto 0;font:700 1.25rem/1.35 Kalam,cursive;color:var(--red)}
.new b{color:var(--pencil)}
/* what's still wrong: the problem the next step fixes */
.wrong{display:block;max-width:36em;margin:22px auto 0;padding:12px 16px;border:2px dashed var(--red);background:#fff;text-align:left}
.wrong b{display:block;font:700 1.25rem Kalam,cursive;color:var(--red)}
.wrong .why{display:block;margin-top:4px}
.wrong .why q{font-family:"PT Mono",Consolas,monospace;font-size:.92em;color:var(--red)}
.wrong .fix{display:block;margin-top:8px;font:700 1.12rem Kalam,cursive}
.js .wrong:not([hidden]){animation:dropIn .5s cubic-bezier(.3,1.5,.5,1) both}
@media (prefers-reduced-motion:reduce){.js .wrong:not([hidden]),.js .say .said{animation:none}}
/* "What will happen?" (Lesson.predict): tap a guess, then the right answer and why; nothing is scored */
.predict{max-width:36em;margin:18px auto;padding:14px 18px 12px;border:2px dashed var(--pencil);background:#fffdf4;text-align:center}
.predict .pq{margin:0 0 10px;font:700 1.3rem/1.3 Kalam,cursive}
.predict .popts{display:flex;flex-wrap:wrap;gap:10px;justify-content:center}
.predict .popt:disabled{opacity:1;cursor:default}
.predict .popt:disabled:not(.right):not(.nope){opacity:.4}
.predict .popt.right{border-color:#2e7d32;color:#2e7d32}
.predict .popt.right::after{content:" \2713"}
.predict .popt.nope{border-color:var(--red);color:var(--red);text-decoration:line-through}
.predict .pwhy{max-width:32em;margin:10px auto 0;font:700 1.15rem/1.35 Kalam,cursive}
.js .predict .pwhy:not(:empty){animation:writeIn .8s ease-out both}
@media (prefers-reduced-motion:reduce){.js .predict .pwhy:not(:empty){animation:none}}
/* "python notes: off" hides the line-by-line tables for readers who already know Python */
.no-explain .explain{display:none}
.notes{right:auto;left:130px}
@media (max-width:520px){.notes{left:16px;bottom:56px}} /* four buttons don't fit one row on a phone: notes go one row up */
/* the key word of each lesson title gets the home page's yellow marker, swiped on after the title writes itself */
.mark{display:inline;background:linear-gradient(#fff4a8,#fff4a8) 0 78%/100% 42% no-repeat} /* inline even inside titles whose spans are blocks */
@keyframes swipe{from{background-size:0 42%}}
.js .mark{animation:swipe .5s 1.1s ease-out backwards}
@media (prefers-reduced-motion:reduce){.js .mark{animation:none}}
.predict .popt{margin:0}
.predict.code .popts{flex-direction:column;align-items:stretch}
.predict.code .popt{justify-content:flex-start;min-height:44px;padding:8px 14px;text-align:left;white-space:pre-wrap;font:15px/1.5 "PT Mono",Consolas,monospace}
.predict.code{max-width:52em}
/* XP bar (fills with the steps reached) and achievement notes; beats.js builds them */
.xp{position:fixed;top:10px;left:50%;z-index:6;display:flex;align-items:center;gap:14px;padding:7px 14px;background:var(--paper);border:2px solid var(--pencil);border-radius:10px 4px 12px 4px;box-shadow:3px 4px 0 rgba(43,43,43,.18);transform:translateX(-50%) rotate(-.6deg)}
.js .xp{opacity:0;transition:opacity .6s}
.js .started .xp{opacity:1}
.xp + .top{padding-top:44px} /* padding, not margin: a margin collapses out of the page and leaves a strip without graph paper */
html{scroll-padding-top:64px}
.xpbar{display:flex;gap:6px}
/* each box gets a highlighter swipe when its step is reached */
.xpbar i{width:22px;height:16px;border:2px solid var(--pencil);border-radius:5px 2px 6px 2px;background:#fff linear-gradient(#ffd43b,#ffd43b) 0 0/0 100% no-repeat;transition:background-size .6s cubic-bezier(.2,.8,.2,1)}
.xpbar i:nth-child(even){border-radius:2px 6px 2px 5px}
.xpbar i.on{background-size:100% 100%}
.xpn{display:inline-block;font:700 1.2rem/1 Kalam,cursive;white-space:nowrap}
/* XP earned flies into the label: little XP boxes plus the amount, launched by beats.js */
.spark{position:fixed;left:0;top:0;z-index:8;width:10px;height:10px;margin:-5px 0 0 -5px;background:#ffd43b;border:1.5px solid var(--pencil);border-radius:3px 1px 4px 1px;pointer-events:none}
.xpplus{position:fixed;z-index:8;font:700 1.5rem Kalam,cursive;color:var(--red);white-space:nowrap;pointer-events:none}
.achs{position:fixed;top:48px;right:16px;z-index:7;display:grid;gap:10px;width:min(300px,calc(100vw - 32px));pointer-events:none}
.ach{display:grid;gap:2px;padding:12px 16px;background:#fff4a8;border:2px solid var(--pencil);border-radius:4px 12px 5px 14px;box-shadow:3px 4px 0 rgba(43,43,43,.18);transform:rotate(-1.5deg);transition:opacity .4s}
.js .ach{animation:dropIn .5s cubic-bezier(.3,1.5,.5,1) both}
.ach.gone{opacity:0}
.ach small{font:700 .85rem Kalam,cursive;color:var(--red);text-transform:uppercase;letter-spacing:.08em}
.ach b{font:700 1.35rem/1.2 Kalam,cursive}
.ach span{font-size:.95rem;line-height:1.4}
.ach em{justify-self:start;font:700 1rem Kalam,cursive;color:var(--red)} /* hugs its text: the XP flies from it */
.achlist{margin:30px 0 0;text-align:left}
.achlist ul{display:flex;flex-wrap:wrap;gap:10px;margin:10px 0 0;padding:0;list-style:none}
.achlist li{padding:6px 12px;font:700 1rem Kalam,cursive;color:#9a9a9a;border:2px dashed #b9b9b9}
.achlist li.got{color:var(--pencil);background:#fff4a8;border:2px solid var(--pencil)}
