Wire expanded Early Beginner subjects and add themed group helpers

This commit is contained in:
Dan committed 2026-06-26 21:58:25 +00:00
1 parent 088cc4179d
commit b69408aad9
1 file changed
+247 -213
+247 -213
View File
@@ -1,213 +1,247 @@
/**
* DrawIt curriculum. Each Early Beginner subject is a 3-lesson set: Outline -> Details -> Color it.
* Outline/Detail steps use per-step `lines` (coloring-book strokes drawn one at a time). The Detail
* lesson awards the subject badge (it is gated behind Outline). `mode "shade"` = shading studio.
*/
export interface LevelMeta { key: string; name: string; emoji: string; blurb: string; }
export const LEVELS: LevelMeta[] = [
{ key: "early-beginner", name: "Early Beginner", emoji: "\u{1F331}", blurb: "First lines and simple shapes. Perfect for brand-new artists." },
{ key: "beginner", name: "Beginner", emoji: "\u270F\uFE0F", blurb: "Combine shapes into friendly characters and objects." },
{ key: "learner", name: "Learner", emoji: "\u{1F3A8}", blurb: "Add details, shading, and your own ideas." },
{ key: "advanced-learned", name: "Advanced Learner", emoji: "\u{1F680}", blurb: "Proportion, perspective, and more complex scenes." },
{ key: "superb", name: "Superb", emoji: "\u{1F31F}", blurb: "Confident, expressive drawing in your own style." },
];
export function getLevel(key: string): LevelMeta | undefined { return LEVELS.find((l) => l.key === key); }
export type StepKind = "construct" | "outline" | "color" | "shade" | "detail";
// Early Beginner uses outline/detail/extra; Beginner uses construct/outline/color/light.
export type Phase = "construct" | "outline" | "detail" | "color" | "light" | "extra";
export interface LessonStep { n: number; title: string; instruction: string; tip?: string; kind?: StepKind; svg?: string; lines?: string[]; }
export interface Lesson {
level: string; sublevel: number; slug: string; title: string; subject: string; emoji: string; intro: string;
badgeKey: string; badgeName: string; mode?: "draw" | "shade"; phase?: Phase; baseSvg?: string;
subjectKey?: string; subjectName?: string; subjectEmoji?: string; order?: number; packKey?: string; steps: LessonStep[];
}
const FISH_OUTLINE_STEPS: LessonStep[] = [
{ n: 1, title: "The body", instruction: "Draw the big rounded body — loop all the way around.", tip: "Slow and smooth, like an egg.", lines: [`<path d="M85 150 C 95 95, 180 78, 252 96 C 292 107, 306 128, 306 150 C 306 172, 292 193, 252 204 C 180 222, 95 205, 85 150 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
{ n: 2, title: "The tail", instruction: "Add the fan-shaped tail at the back.", lines: [`<path d="M300 150 C 332 130, 360 112, 390 100 C 374 130, 374 170, 390 200 C 360 188, 332 170, 300 150 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
{ n: 3, title: "The fins", instruction: "Draw a fin on the back, then one under the belly.", lines: [`<path d="M150 92 C 168 62, 212 60, 240 84" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`, `<path d="M150 178 C 168 208, 206 210, 228 196" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
];
const FISH_DETAIL_STEPS: LessonStep[] = [
{ n: 1, title: "The eye", instruction: "Draw a round eye, then a little dot inside.", lines: [`<path d="M147 134 a15 15 0 1 0 0.1 0" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`, `<circle cx="129" cy="136" r="6" fill="#2b2440"/>`] },
{ n: 2, title: "The mouth", instruction: "Add a small smile at the front.", lines: [`<path d="M90 162 q15 13 32 4" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
{ n: 3, title: "The gill", instruction: "Add a curved gill line behind the head.", lines: [`<path d="M160 104 q-16 46 0 92" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
{ n: 4, title: "Stripes", instruction: "Add a couple of curved stripes across the body.", tip: "Now it's ready to color!", lines: [`<path d="M205 96 q-20 54 4 110" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`, `<path d="M240 100 q-16 50 4 100" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
];
const FISH_OUTLINE_SVG = FISH_OUTLINE_STEPS.flatMap((s) => s.lines || []).join("\n");
const FISH_DETAIL_SVG = FISH_OUTLINE_SVG + "\n" + FISH_DETAIL_STEPS.flatMap((s) => s.lines || []).join("\n");
export const FISH_OUTLINE_LESSON: Lesson = { level:"early-beginner", sublevel:11, slug:"fish-outline", title:"Fish \u00b7 Outline", subject:"fish outline", emoji:"🐟", intro:"Let\u2019s draw Fish\u2019s outline, one line at a time. Watch each line, then trace it!", badgeKey:"", badgeName:"Fish Friend", phase:"outline", subjectKey:"fish", subjectName:"Fish", subjectEmoji:"🐟", order:1, steps: FISH_OUTLINE_STEPS };
export const FISH_DETAIL_LESSON: Lesson = { level:"early-beginner", sublevel:12, slug:"fish-detail", title:"Fish \u00b7 Details", subject:"fish details", emoji:"🐟", intro:"Now add the details that bring your fish to life.", badgeKey:"early-beginner-1-fish", badgeName:"Fish Friend", phase:"detail", baseSvg: FISH_OUTLINE_SVG, subjectKey:"fish", subjectName:"Fish", subjectEmoji:"🐟", order:1, steps: FISH_DETAIL_STEPS };
export const FISH_EXTRA_LESSON: Lesson = { level:"early-beginner", sublevel:13, slug:"fish-extra", title:"Fish \u00b7 Color it!", subject:"color the fish", emoji:"\u{1F3A8}", intro:"Bring your fish to life! Color it in, and try shading and layering.", badgeKey:"", badgeName:"Fish Friend", phase:"extra", baseSvg: FISH_DETAIL_SVG, subjectKey:"fish", subjectName:"Fish", subjectEmoji:"🐟", order:1, steps: [] };
const PANDA_OUTLINE_STEPS: LessonStep[] = [
{ n: 1, title: "The head", instruction: "Draw one big round face.", tip: "Nice and round.", lines: [`<ellipse cx="200" cy="162" rx="92" ry="84" fill="none" stroke="#2b2440" stroke-width="4"/>`] },
{ n: 2, title: "The ears", instruction: "Add two round ears on top.", lines: [`<circle cx="142" cy="92" r="30" fill="none" stroke="#2b2440" stroke-width="4"/>`, `<circle cx="258" cy="92" r="30" fill="none" stroke="#2b2440" stroke-width="4"/>`] },
{ n: 3, title: "Eye patches", instruction: "Draw a leaf-shaped patch for each eye.", lines: [`<path d="M150 132 C 136 148, 140 178, 164 184 C 186 188, 196 166, 189 146 C 183 132, 164 124, 150 132 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`, `<path d="M250 132 C 264 148, 260 178, 236 184 C 214 188, 204 166, 211 146 C 217 132, 236 124, 250 132 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
];
const PANDA_DETAIL_STEPS: LessonStep[] = [
{ n: 1, title: "The eyes", instruction: "Add a circle and a dot inside each patch.", lines: [`<circle cx="168" cy="156" r="9" fill="none" stroke="#2b2440" stroke-width="3"/>`, `<circle cx="170" cy="158" r="4" fill="#2b2440"/>`, `<circle cx="232" cy="156" r="9" fill="none" stroke="#2b2440" stroke-width="3"/>`, `<circle cx="230" cy="158" r="4" fill="#2b2440"/>`] },
{ n: 2, title: "The nose", instruction: "Draw a little rounded nose.", lines: [`<path d="M186 184 Q200 178 214 184 Q208 198 200 200 Q192 198 186 184 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
{ n: 3, title: "The mouth", instruction: "Add a line down and two smile curves.", lines: [`<path d="M200 200 L200 210" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`, `<path d="M200 210 q-13 12 -26 5" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`, `<path d="M200 210 q13 12 26 5" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
];
const PANDA_OUTLINE_SVG = PANDA_OUTLINE_STEPS.flatMap((s) => s.lines || []).join("\n");
const PANDA_DETAIL_SVG = PANDA_OUTLINE_SVG + "\n" + PANDA_DETAIL_STEPS.flatMap((s) => s.lines || []).join("\n");
export const PANDA_OUTLINE_LESSON: Lesson = { level:"early-beginner", sublevel:21, slug:"panda-outline", title:"Panda \u00b7 Outline", subject:"panda outline", emoji:"🐼", intro:"Let\u2019s draw Panda\u2019s outline, one line at a time. Watch each line, then trace it!", badgeKey:"", badgeName:"Panda Pal", phase:"outline", subjectKey:"panda", subjectName:"Panda", subjectEmoji:"🐼", order:2, steps: PANDA_OUTLINE_STEPS };
export const PANDA_DETAIL_LESSON: Lesson = { level:"early-beginner", sublevel:22, slug:"panda-detail", title:"Panda \u00b7 Details", subject:"panda details", emoji:"🐼", intro:"Now add the details that bring your panda to life.", badgeKey:"early-beginner-2-panda", badgeName:"Panda Pal", phase:"detail", baseSvg: PANDA_OUTLINE_SVG, subjectKey:"panda", subjectName:"Panda", subjectEmoji:"🐼", order:2, steps: PANDA_DETAIL_STEPS };
export const PANDA_EXTRA_LESSON: Lesson = { level:"early-beginner", sublevel:23, slug:"panda-extra", title:"Panda \u00b7 Color it!", subject:"color the panda", emoji:"\u{1F3A8}", intro:"Bring your panda to life! Color it in, and try shading and layering.", badgeKey:"", badgeName:"Panda Pal", phase:"extra", baseSvg: PANDA_DETAIL_SVG, subjectKey:"panda", subjectName:"Panda", subjectEmoji:"🐼", order:2, steps: [] };
const FLOWER_OUTLINE_STEPS: LessonStep[] = [
{ n: 1, title: "The center", instruction: "Draw a circle in the middle.", tip: "Leave room for petals.", lines: [`<circle cx="200" cy="118" r="34" fill="none" stroke="#2b2440" stroke-width="4"/>`] },
{ n: 2, title: "Petals", instruction: "Add petals on top, bottom, left, and right.", lines: [`<path d="M200 86 C 180 78, 176 52, 200 40 C 224 52, 220 78, 200 86 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linejoin="round" transform="rotate(0 200 118)"/>`, `<path d="M200 86 C 180 78, 176 52, 200 40 C 224 52, 220 78, 200 86 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linejoin="round" transform="rotate(90 200 118)"/>`, `<path d="M200 86 C 180 78, 176 52, 200 40 C 224 52, 220 78, 200 86 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linejoin="round" transform="rotate(180 200 118)"/>`, `<path d="M200 86 C 180 78, 176 52, 200 40 C 224 52, 220 78, 200 86 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linejoin="round" transform="rotate(270 200 118)"/>`] },
{ n: 3, title: "More petals", instruction: "Tuck four more petals into the gaps.", lines: [`<path d="M200 86 C 180 78, 176 52, 200 40 C 224 52, 220 78, 200 86 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linejoin="round" transform="rotate(45 200 118)"/>`, `<path d="M200 86 C 180 78, 176 52, 200 40 C 224 52, 220 78, 200 86 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linejoin="round" transform="rotate(135 200 118)"/>`, `<path d="M200 86 C 180 78, 176 52, 200 40 C 224 52, 220 78, 200 86 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linejoin="round" transform="rotate(225 200 118)"/>`, `<path d="M200 86 C 180 78, 176 52, 200 40 C 224 52, 220 78, 200 86 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linejoin="round" transform="rotate(315 200 118)"/>`] },
{ n: 4, title: "The stem", instruction: "Draw a stem going down from the flower.", lines: [`<path d="M195 152 C 191 186, 205 214, 197 256 L203 256 C 211 214, 199 186, 205 152 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
{ n: 5, title: "The leaves", instruction: "Add a leaf on each side of the stem.", lines: [`<path d="M200 198 C 168 184, 138 192, 150 218 C 162 240, 196 226, 200 198 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`, `<path d="M200 226 C 232 214, 262 222, 250 246 C 238 266, 204 252, 200 226 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
];
const FLOWER_DETAIL_STEPS: LessonStep[] = [
{ n: 1, title: "Seeds", instruction: "Dot some seeds in the middle.", lines: [`<circle cx="192" cy="112" r="4" fill="#2b2440"/>`, `<circle cx="208" cy="114" r="4" fill="#2b2440"/>`, `<circle cx="200" cy="124" r="4" fill="#2b2440"/>`, `<circle cx="190" cy="126" r="3" fill="#2b2440"/>`, `<circle cx="210" cy="126" r="3" fill="#2b2440"/>`] },
{ n: 2, title: "Leaf veins", instruction: "Add a line down each leaf.", lines: [`<path d="M170 210 q20 4 28 14" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`, `<path d="M232 240 q-18 2 -28 -10" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
{ n: 3, title: "Center ring", instruction: "Draw a little ring inside the middle.", lines: [`<circle cx="200" cy="118" r="18" fill="none" stroke="#2b2440" stroke-width="3"/>`] },
];
const FLOWER_OUTLINE_SVG = FLOWER_OUTLINE_STEPS.flatMap((s) => s.lines || []).join("\n");
const FLOWER_DETAIL_SVG = FLOWER_OUTLINE_SVG + "\n" + FLOWER_DETAIL_STEPS.flatMap((s) => s.lines || []).join("\n");
export const FLOWER_OUTLINE_LESSON: Lesson = { level:"early-beginner", sublevel:31, slug:"flower-outline", title:"Flower \u00b7 Outline", subject:"flower outline", emoji:"🌸", intro:"Let\u2019s draw Flower\u2019s outline, one line at a time. Watch each line, then trace it!", badgeKey:"", badgeName:"Flower Power", phase:"outline", subjectKey:"flower", subjectName:"Flower", subjectEmoji:"🌸", order:3, steps: FLOWER_OUTLINE_STEPS };
export const FLOWER_DETAIL_LESSON: Lesson = { level:"early-beginner", sublevel:32, slug:"flower-detail", title:"Flower \u00b7 Details", subject:"flower details", emoji:"🌸", intro:"Now add the details that bring your flower to life.", badgeKey:"early-beginner-3-flower", badgeName:"Flower Power", phase:"detail", baseSvg: FLOWER_OUTLINE_SVG, subjectKey:"flower", subjectName:"Flower", subjectEmoji:"🌸", order:3, steps: FLOWER_DETAIL_STEPS };
export const FLOWER_EXTRA_LESSON: Lesson = { level:"early-beginner", sublevel:33, slug:"flower-extra", title:"Flower \u00b7 Color it!", subject:"color the flower", emoji:"\u{1F3A8}", intro:"Bring your flower to life! Color it in, and try shading and layering.", badgeKey:"", badgeName:"Flower Power", phase:"extra", baseSvg: FLOWER_DETAIL_SVG, subjectKey:"flower", subjectName:"Flower", subjectEmoji:"🌸", order:3, steps: [] };
const UNICORN_OUTLINE_STEPS: LessonStep[] = [
{ n: 1, title: "The head", instruction: "Draw a rounded head.", lines: [`<path d="M120 152 C 120 96, 165 72, 200 72 C 235 72, 280 96, 280 152 C 280 202, 246 236, 200 240 C 154 236, 120 202, 120 152 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
{ n: 2, title: "The ears", instruction: "Add a pointy ear on each side.", lines: [`<path d="M150 98 C 138 72, 132 58, 150 60 C 166 64, 174 84, 180 96 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`, `<path d="M250 98 C 262 72, 268 58, 250 60 C 234 64, 226 84, 220 96 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
{ n: 3, title: "The horn", instruction: "Draw a tall horn on top.", lines: [`<path d="M200 80 L188 18 L212 80 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
{ n: 4, title: "The mane", instruction: "Draw the flowing mane and a curl on the forehead.", lines: [`<path d="M256 112 C 320 122, 320 200, 268 250 C 292 196, 280 150, 250 142 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`, `<path d="M176 92 C 165 110, 175 135, 196 130 C 184 118, 188 102, 200 96 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
];
const UNICORN_DETAIL_STEPS: LessonStep[] = [
{ n: 1, title: "The eyes", instruction: "Add two big eyes.", lines: [`<ellipse cx="172" cy="158" rx="7" ry="10" fill="none" stroke="#2b2440" stroke-width="4"/>`, `<ellipse cx="228" cy="158" rx="7" ry="10" fill="none" stroke="#2b2440" stroke-width="4"/>`] },
{ n: 2, title: "Nose & mouth", instruction: "Add two nostril dots and a smile.", lines: [`<circle cx="188" cy="210" r="3" fill="#2b2440"/>`, `<circle cx="214" cy="210" r="3" fill="#2b2440"/>`, `<path d="M186 224 q14 12 28 0" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
{ n: 3, title: "Horn swirls", instruction: "Add little swirl lines on the horn.", lines: [`<path d="M193 66 l14 -5" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`, `<path d="M196 50 l11 -4" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`, `<path d="M199 36 l8 -3" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
{ n: 4, title: "Mane lines", instruction: "Add flowing lines in the mane.", lines: [`<path d="M268 130 C 300 150, 296 200, 262 232" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`, `<path d="M258 140 C 286 160, 282 200, 256 226" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
];
const UNICORN_OUTLINE_SVG = UNICORN_OUTLINE_STEPS.flatMap((s) => s.lines || []).join("\n");
const UNICORN_DETAIL_SVG = UNICORN_OUTLINE_SVG + "\n" + UNICORN_DETAIL_STEPS.flatMap((s) => s.lines || []).join("\n");
export const UNICORN_OUTLINE_LESSON: Lesson = { level:"early-beginner", sublevel:41, slug:"unicorn-outline", title:"Unicorn \u00b7 Outline", subject:"unicorn outline", emoji:"🦄", intro:"Let\u2019s draw Unicorn\u2019s outline, one line at a time. Watch each line, then trace it!", badgeKey:"", badgeName:"Unicorn Magic", phase:"outline", subjectKey:"unicorn", subjectName:"Unicorn", subjectEmoji:"🦄", order:4, steps: UNICORN_OUTLINE_STEPS };
export const UNICORN_DETAIL_LESSON: Lesson = { level:"early-beginner", sublevel:42, slug:"unicorn-detail", title:"Unicorn \u00b7 Details", subject:"unicorn details", emoji:"🦄", intro:"Now add the details that bring your unicorn to life.", badgeKey:"early-beginner-4-unicorn", badgeName:"Unicorn Magic", phase:"detail", baseSvg: UNICORN_OUTLINE_SVG, subjectKey:"unicorn", subjectName:"Unicorn", subjectEmoji:"🦄", order:4, steps: UNICORN_DETAIL_STEPS };
export const UNICORN_EXTRA_LESSON: Lesson = { level:"early-beginner", sublevel:43, slug:"unicorn-extra", title:"Unicorn \u00b7 Color it!", subject:"color the unicorn", emoji:"\u{1F3A8}", intro:"Bring your unicorn to life! Color it in, and try shading and layering.", badgeKey:"", badgeName:"Unicorn Magic", phase:"extra", baseSvg: UNICORN_DETAIL_SVG, subjectKey:"unicorn", subjectName:"Unicorn", subjectEmoji:"🦄", order:4, steps: [] };
const TREE_OUTLINE_STEPS: LessonStep[] = [
{ n: 1, title: "The trunk", instruction: "Draw a tall trunk, wider at the bottom.", lines: [`<path d="M178 262 C 176 222, 184 200, 190 176 L210 176 C 216 200, 224 222, 222 262 C 214 266, 186 266, 178 262 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
{ n: 2, title: "Branches", instruction: "Add two branches reaching up.", lines: [`<path d="M198 180 C 190 152, 168 144, 152 134" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`, `<path d="M204 180 C 214 152, 236 146, 252 136" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
{ n: 3, title: "The treetop", instruction: "Draw a big bumpy treetop.", tip: "Nice and lumpy!", lines: [`<path d="M120 150 C 100 122, 122 96, 150 100 C 152 74, 200 62, 216 88 C 246 72, 286 92, 274 122 C 300 134, 292 168, 262 166 C 250 188, 150 188, 138 166 C 110 170, 104 152, 120 150 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
];
const TREE_DETAIL_STEPS: LessonStep[] = [
{ n: 1, title: "Apples", instruction: "Add a few round apples in the leaves.", lines: [`<circle cx="168" cy="118" r="7" fill="none" stroke="#2b2440" stroke-width="3"/>`, `<circle cx="228" cy="104" r="7" fill="none" stroke="#2b2440" stroke-width="3"/>`, `<circle cx="204" cy="150" r="7" fill="none" stroke="#2b2440" stroke-width="3"/>`] },
{ n: 2, title: "Grass", instruction: "Add wavy grass along the bottom.", lines: [`<path d="M120 262 q14 -16 28 0 q14 -16 28 0 q14 -16 28 0 q14 -16 28 0 q14 -16 28 0" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
{ n: 3, title: "Bark", instruction: "Add a couple of bark lines on the trunk.", lines: [`<path d="M194 200 q4 24 0 56" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`, `<path d="M206 200 q-3 24 0 54" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
];
const TREE_OUTLINE_SVG = TREE_OUTLINE_STEPS.flatMap((s) => s.lines || []).join("\n");
const TREE_DETAIL_SVG = TREE_OUTLINE_SVG + "\n" + TREE_DETAIL_STEPS.flatMap((s) => s.lines || []).join("\n");
export const TREE_OUTLINE_LESSON: Lesson = { level:"early-beginner", sublevel:51, slug:"tree-outline", title:"Tree \u00b7 Outline", subject:"tree outline", emoji:"🌳", intro:"Let\u2019s draw Tree\u2019s outline, one line at a time. Watch each line, then trace it!", badgeKey:"", badgeName:"Tree Top", phase:"outline", subjectKey:"tree", subjectName:"Tree", subjectEmoji:"🌳", order:5, steps: TREE_OUTLINE_STEPS };
export const TREE_DETAIL_LESSON: Lesson = { level:"early-beginner", sublevel:52, slug:"tree-detail", title:"Tree \u00b7 Details", subject:"tree details", emoji:"🌳", intro:"Now add the details that bring your tree to life.", badgeKey:"early-beginner-5-tree", badgeName:"Tree Top", phase:"detail", baseSvg: TREE_OUTLINE_SVG, subjectKey:"tree", subjectName:"Tree", subjectEmoji:"🌳", order:5, steps: TREE_DETAIL_STEPS };
export const TREE_EXTRA_LESSON: Lesson = { level:"early-beginner", sublevel:53, slug:"tree-extra", title:"Tree \u00b7 Color it!", subject:"color the tree", emoji:"\u{1F3A8}", intro:"Bring your tree to life! Color it in, and try shading and layering.", badgeKey:"", badgeName:"Tree Top", phase:"extra", baseSvg: TREE_DETAIL_SVG, subjectKey:"tree", subjectName:"Tree", subjectEmoji:"🌳", order:5, steps: [] };
const DINO_OUTLINE_STEPS: LessonStep[] = [
{ n: 1, title: "The body", instruction: "Draw a big round body.", lines: [`<path d="M118 200 C 118 166, 160 150, 210 150 C 270 150, 302 170, 302 200 C 302 230, 262 246, 205 246 C 155 246, 118 230, 118 200 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
{ n: 2, title: "The neck", instruction: "Sweep a long neck up from the left.", lines: [`<path d="M150 168 C 122 122, 112 80, 124 55 C 130 44, 148 46, 152 58 C 146 86, 162 124, 180 160 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
{ n: 3, title: "The head", instruction: "Add a small head on top of the neck.", lines: [`<path d="M100 46 C 100 32, 120 28, 138 34 C 152 39, 154 56, 142 64 C 126 72, 104 64, 98 54 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
{ n: 4, title: "The tail", instruction: "Draw a long tail swooping to the right.", lines: [`<path d="M298 198 C 338 184, 372 176, 398 168 C 380 192, 360 206, 304 220 C 300 213, 298 206, 298 198 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
{ n: 5, title: "The legs", instruction: "Add two sturdy legs underneath.", lines: [`<path d="M168 242 C 165 264, 166 278, 180 280 C 193 280, 192 264, 190 242 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`, `<path d="M232 244 C 229 266, 230 280, 244 282 C 257 282, 256 266, 254 244 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
];
const DINO_DETAIL_STEPS: LessonStep[] = [
{ n: 1, title: "Back plates", instruction: "Add triangle plates along the back.", lines: [`<path d="M150 150 l10 -16 l12 16 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`, `<path d="M186 146 l11 -17 l12 17 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`, `<path d="M224 147 l11 -16 l12 16 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`, `<path d="M262 152 l10 -15 l12 16 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
{ n: 2, title: "Spots", instruction: "Add a few spots on the body.", lines: [`<circle cx="205" cy="188" r="9" fill="none" stroke="#2b2440" stroke-width="3"/>`, `<circle cx="240" cy="200" r="7" fill="none" stroke="#2b2440" stroke-width="3"/>`, `<circle cx="180" cy="212" r="6" fill="none" stroke="#2b2440" stroke-width="3"/>`] },
{ n: 3, title: "The face", instruction: "Add an eye and a friendly smile.", lines: [`<circle cx="118" cy="46" r="4" fill="#2b2440"/>`, `<path d="M108 56 q12 10 24 3" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
];
const DINO_OUTLINE_SVG = DINO_OUTLINE_STEPS.flatMap((s) => s.lines || []).join("\n");
const DINO_DETAIL_SVG = DINO_OUTLINE_SVG + "\n" + DINO_DETAIL_STEPS.flatMap((s) => s.lines || []).join("\n");
export const DINO_OUTLINE_LESSON: Lesson = { level:"early-beginner", sublevel:61, slug:"dinosaur-outline", title:"Dinosaur \u00b7 Outline", subject:"dinosaur outline", emoji:"🦕", intro:"Let\u2019s draw Dinosaur\u2019s outline, one line at a time. Watch each line, then trace it!", badgeKey:"", badgeName:"Dino Master", phase:"outline", subjectKey:"dinosaur", subjectName:"Dinosaur", subjectEmoji:"🦕", order:6, steps: DINO_OUTLINE_STEPS };
export const DINO_DETAIL_LESSON: Lesson = { level:"early-beginner", sublevel:62, slug:"dinosaur-detail", title:"Dinosaur \u00b7 Details", subject:"dinosaur details", emoji:"🦕", intro:"Now add the details that bring your dinosaur to life.", badgeKey:"early-beginner-6-dinosaur", badgeName:"Dino Master", phase:"detail", baseSvg: DINO_OUTLINE_SVG, subjectKey:"dinosaur", subjectName:"Dinosaur", subjectEmoji:"🦕", order:6, steps: DINO_DETAIL_STEPS };
export const DINO_EXTRA_LESSON: Lesson = { level:"early-beginner", sublevel:63, slug:"dinosaur-extra", title:"Dinosaur \u00b7 Color it!", subject:"color the dinosaur", emoji:"\u{1F3A8}", intro:"Bring your dinosaur to life! Color it in, and try shading and layering.", badgeKey:"", badgeName:"Dino Master", phase:"extra", baseSvg: DINO_DETAIL_SVG, subjectKey:"dinosaur", subjectName:"Dinosaur", subjectEmoji:"🦕", order:6, steps: [] };
const SHADE_STEPS: LessonStep[] = [
{ n: 1, kind: "shade", title: "How to hold your pencil", instruction: "Watch the demo above. Near the tip + press hard = DARK; high up + soft = LIGHT.", tip: "Try it in the air first!", svg: `<g><circle cx="62" cy="46" r="20" fill="#ffd45e"/><g stroke="#ffd45e" stroke-width="4" stroke-linecap="round"><path d="M62 14v-8M62 86v8M30 46h-8M94 46h8M39 23l-6-6M85 23l6-6M39 69l-6 6M85 69l6 6"/></g></g><line x1="40" y1="234" x2="370" y2="234" stroke="#c9c4d6" stroke-width="3"/><circle cx="210" cy="150" r="84" fill="#f1f1f4" stroke="#2b2440" stroke-width="4"/>` },
{ n: 2, kind: "shade", title: "Find the light", instruction: "The sun is up-left. The side facing it stays brightest — that bright spot is the highlight.", svg: `<g><circle cx="62" cy="46" r="20" fill="#ffd45e"/><g stroke="#ffd45e" stroke-width="4" stroke-linecap="round"><path d="M62 14v-8M62 86v8M30 46h-8M94 46h8M39 23l-6-6M85 23l6-6M39 69l-6 6M85 69l6 6"/></g></g><line x1="40" y1="234" x2="370" y2="234" stroke="#c9c4d6" stroke-width="3"/><circle cx="210" cy="150" r="84" fill="#f1f1f4" stroke="#2b2440" stroke-width="4"/><circle cx="178" cy="118" r="22" fill="none" stroke="#4f7cff" stroke-width="3" stroke-dasharray="6 6"/>` },
{ n: 3, kind: "shade", title: "Soft shade all over", instruction: "Pick a light tone and softly shade the whole ball — leave the bright spot white.", tip: "Hold high and go easy.", svg: `<g><circle cx="62" cy="46" r="20" fill="#ffd45e"/><g stroke="#ffd45e" stroke-width="4" stroke-linecap="round"><path d="M62 14v-8M62 86v8M30 46h-8M94 46h8M39 23l-6-6M85 23l6-6M39 69l-6 6M85 69l6 6"/></g></g><line x1="40" y1="234" x2="370" y2="234" stroke="#c9c4d6" stroke-width="3"/><circle cx="210" cy="150" r="84" fill="#f1f1f4" stroke="#2b2440" stroke-width="4"/><circle cx="178" cy="118" r="22" fill="none" stroke="#4f7cff" stroke-width="3" stroke-dasharray="6 6"/>` },
{ n: 4, kind: "shade", title: "Build up the shadow", instruction: "Pick a darker tone and press harder on the side away from the sun. Build it up slowly.", tip: "More passes = darker.", svg: `<g><circle cx="62" cy="46" r="20" fill="#ffd45e"/><g stroke="#ffd45e" stroke-width="4" stroke-linecap="round"><path d="M62 14v-8M62 86v8M30 46h-8M94 46h8M39 23l-6-6M85 23l6-6M39 69l-6 6M85 69l6 6"/></g></g><line x1="40" y1="234" x2="370" y2="234" stroke="#c9c4d6" stroke-width="3"/><circle cx="210" cy="150" r="84" fill="#f1f1f4" stroke="#2b2440" stroke-width="4"/><path d="M250 96 A84 84 0 0 1 250 204 A60 70 0 0 0 250 96 Z" fill="#2b2440" opacity="0.18"/>` },
{ n: 5, kind: "shade", title: "Cast shadow & finish", instruction: "Shade the ground under the ball on the shadow side. Keep the highlight clean!", tip: "🌗", svg: `<g><circle cx="62" cy="46" r="20" fill="#ffd45e"/><g stroke="#ffd45e" stroke-width="4" stroke-linecap="round"><path d="M62 14v-8M62 86v8M30 46h-8M94 46h8M39 23l-6-6M85 23l6-6M39 69l-6 6M85 69l6 6"/></g></g><line x1="40" y1="234" x2="370" y2="234" stroke="#c9c4d6" stroke-width="3"/><circle cx="210" cy="150" r="84" fill="#f1f1f4" stroke="#2b2440" stroke-width="4"/><path d="M250 96 A84 84 0 0 1 250 204 A60 70 0 0 0 250 96 Z" fill="#2b2440" opacity="0.18"/><ellipse cx="262" cy="236" rx="78" ry="15" fill="#2b2440" opacity="0.18"/>` },
];
export const SHADE_BALL_LESSON: Lesson = { level:"learner", sublevel:1, slug:"shade-ball", title:"Light & Shadow: Shade a Ball", subject:"a shaded ball", emoji:"\u{1F317}", intro:"Make a flat circle look like a round ball using light and shadow!", badgeKey:"learner-1-shading", badgeName:"Shading Star", mode:"shade", subjectKey:"shade-ball", subjectName:"Shade a Ball", subjectEmoji:"\u{1F317}", order:1, steps: SHADE_STEPS };
import { BEGINNER_LESSONS, BEGINNER_PACKS, type Pack } from "./curriculum.beginner";
export { BEGINNER_PACKS, type Pack } from "./curriculum.beginner";
export { LIGHT_HINT_SVG } from "./curriculum.beginner";
const EARLY_BEGINNER_LESSONS: Lesson[] = [FISH_OUTLINE_LESSON, FISH_DETAIL_LESSON, FISH_EXTRA_LESSON, PANDA_OUTLINE_LESSON, PANDA_DETAIL_LESSON, PANDA_EXTRA_LESSON, FLOWER_OUTLINE_LESSON, FLOWER_DETAIL_LESSON, FLOWER_EXTRA_LESSON, UNICORN_OUTLINE_LESSON, UNICORN_DETAIL_LESSON, UNICORN_EXTRA_LESSON, TREE_OUTLINE_LESSON, TREE_DETAIL_LESSON, TREE_EXTRA_LESSON, DINO_OUTLINE_LESSON, DINO_DETAIL_LESSON, DINO_EXTRA_LESSON];
export const LESSONS: Lesson[] = [...EARLY_BEGINNER_LESSONS, ...BEGINNER_LESSONS, SHADE_BALL_LESSON];
export function getLesson(level: string, sublevel: number): Lesson | undefined { return LESSONS.find((l) => l.level === level && l.sublevel === sublevel); }
export function getLessonBySlug(slug: string): Lesson | undefined { return LESSONS.find((l) => l.slug === slug); }
export function cumulativeSvg(lesson: Lesson, upTo: number): string { return lesson.steps.filter((s) => s.n <= upTo).map((s) => s.svg || "").join("\n"); }
export function cumulativeLines(lesson: Lesson, upTo: number): string { return lesson.steps.filter((s) => s.n <= upTo).flatMap((s) => s.lines || []).join("\n"); }
export function getSubjects(level: string): { key: string; name: string; emoji: string; order: number; lessons: Lesson[] }[] {
const inLevel = LESSONS.filter((l) => l.level === level);
const map = new Map<string, { key: string; name: string; emoji: string; order: number; lessons: Lesson[] }>();
for (const l of inLevel) {
const key = l.subjectKey || l.slug;
if (!map.has(key)) map.set(key, { key, name: l.subjectName || l.title, emoji: l.subjectEmoji || l.emoji, order: l.order ?? l.sublevel, lessons: [] });
map.get(key)!.lessons.push(l);
}
// Covers both Early Beginner (outline/detail/extra) and Beginner (construct/outline/color/light).
const rank: Record<string, number> = { construct: 0, outline: 1, detail: 2, color: 3, extra: 3, light: 4 };
for (const s of map.values()) s.lessons.sort((a, b) => (rank[a.phase ?? ""] ?? 0) - (rank[b.phase ?? ""] ?? 0));
return [...map.values()].sort((a, b) => a.order - b.order);
}
/** All badge definitions: per-subject (Early Beginner) + per-pack (Beginner). */
export interface BadgeDef { badgeKey: string; name: string; emoji: string; groupKey: string; }
export function badgeDefs(): BadgeDef[] {
const fromLessons = LESSONS.filter((l) => l.badgeKey).map((l) => ({ badgeKey: l.badgeKey, name: l.badgeName, emoji: l.emoji, groupKey: `${l.level}-${l.sublevel}` }));
const fromPacks = BEGINNER_PACKS.map((p) => ({ badgeKey: p.badgeKey, name: p.badgeName, emoji: p.emoji, groupKey: "" }));
return [...fromLessons, ...fromPacks];
}
export function getPackByBadge(badgeKey: string): Pack | undefined { return BEGINNER_PACKS.find((p) => p.badgeKey === badgeKey); }
/** The pack a lesson belongs to (Beginner only), via its packKey. */
export function getPackForLesson(lesson: Lesson): Pack | undefined { return lesson.packKey ? BEGINNER_PACKS.find((p) => p.key === lesson.packKey) : undefined; }
/** Every lesson that belongs to a pack (all phases of all its subjects). */
export function lessonsInPack(pack: Pack): Lesson[] { return LESSONS.filter((l) => l.level === pack.level && l.subjectKey && pack.subjectKeys.includes(l.subjectKey)); }
/** Number of completed steps needed to count a lesson as done. */
export function lessonStepCount(l: Lesson): number { return l.steps.length || 1; }
/**
* DrawIt curriculum. Each Early Beginner subject is a 3-lesson set: Outline -> Details -> Color it.
* Outline/Detail steps use per-step `lines` (coloring-book strokes drawn one at a time). The Detail
* lesson awards the subject badge (it is gated behind Outline). `mode "shade"` = shading studio.
*/
export interface LevelMeta { key: string; name: string; emoji: string; blurb: string; }
export const LEVELS: LevelMeta[] = [
{ key: "early-beginner", name: "Early Beginner", emoji: "\u{1F331}", blurb: "First lines and simple shapes. Perfect for brand-new artists." },
{ key: "beginner", name: "Beginner", emoji: "\u270F\uFE0F", blurb: "Combine shapes into friendly characters and objects." },
{ key: "learner", name: "Learner", emoji: "\u{1F3A8}", blurb: "Add details, shading, and your own ideas." },
{ key: "advanced-learned", name: "Advanced Learner", emoji: "\u{1F680}", blurb: "Proportion, perspective, and more complex scenes." },
{ key: "superb", name: "Superb", emoji: "\u{1F31F}", blurb: "Confident, expressive drawing in your own style." },
];
export function getLevel(key: string): LevelMeta | undefined { return LEVELS.find((l) => l.key === key); }
export type StepKind = "construct" | "outline" | "color" | "shade" | "detail";
// Early Beginner uses outline/detail/extra; Beginner uses construct/outline/color/light.
export type Phase = "construct" | "outline" | "detail" | "color" | "light" | "extra";
export interface LessonStep { n: number; title: string; instruction: string; tip?: string; kind?: StepKind; svg?: string; lines?: string[]; }
export interface Lesson {
level: string; sublevel: number; slug: string; title: string; subject: string; emoji: string; intro: string;
badgeKey: string; badgeName: string; mode?: "draw" | "shade"; phase?: Phase; baseSvg?: string;
subjectKey?: string; subjectName?: string; subjectEmoji?: string; order?: number; packKey?: string; steps: LessonStep[];
}
const FISH_OUTLINE_STEPS: LessonStep[] = [
{ n: 1, title: "The body", instruction: "Draw the big rounded body — loop all the way around.", tip: "Slow and smooth, like an egg.", lines: [`<path d="M85 150 C 95 95, 180 78, 252 96 C 292 107, 306 128, 306 150 C 306 172, 292 193, 252 204 C 180 222, 95 205, 85 150 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
{ n: 2, title: "The tail", instruction: "Add the fan-shaped tail at the back.", lines: [`<path d="M300 150 C 332 130, 360 112, 390 100 C 374 130, 374 170, 390 200 C 360 188, 332 170, 300 150 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
{ n: 3, title: "The fins", instruction: "Draw a fin on the back, then one under the belly.", lines: [`<path d="M150 92 C 168 62, 212 60, 240 84" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`, `<path d="M150 178 C 168 208, 206 210, 228 196" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
];
const FISH_DETAIL_STEPS: LessonStep[] = [
{ n: 1, title: "The eye", instruction: "Draw a round eye, then a little dot inside.", lines: [`<path d="M147 134 a15 15 0 1 0 0.1 0" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`, `<circle cx="129" cy="136" r="6" fill="#2b2440"/>`] },
{ n: 2, title: "The mouth", instruction: "Add a small smile at the front.", lines: [`<path d="M90 162 q15 13 32 4" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
{ n: 3, title: "The gill", instruction: "Add a curved gill line behind the head.", lines: [`<path d="M160 104 q-16 46 0 92" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
{ n: 4, title: "Stripes", instruction: "Add a couple of curved stripes across the body.", tip: "Now it's ready to color!", lines: [`<path d="M205 96 q-20 54 4 110" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`, `<path d="M240 100 q-16 50 4 100" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
];
const FISH_OUTLINE_SVG = FISH_OUTLINE_STEPS.flatMap((s) => s.lines || []).join("\n");
const FISH_DETAIL_SVG = FISH_OUTLINE_SVG + "\n" + FISH_DETAIL_STEPS.flatMap((s) => s.lines || []).join("\n");
export const FISH_OUTLINE_LESSON: Lesson = { level:"early-beginner", sublevel:11, slug:"fish-outline", title:"Fish \u00b7 Outline", subject:"fish outline", emoji:"🐟", intro:"Let\u2019s draw Fish\u2019s outline, one line at a time. Watch each line, then trace it!", badgeKey:"", badgeName:"Fish Friend", phase:"outline", subjectKey:"fish", subjectName:"Fish", subjectEmoji:"🐟", order:1, steps: FISH_OUTLINE_STEPS };
export const FISH_DETAIL_LESSON: Lesson = { level:"early-beginner", sublevel:12, slug:"fish-detail", title:"Fish \u00b7 Details", subject:"fish details", emoji:"🐟", intro:"Now add the details that bring your fish to life.", badgeKey:"early-beginner-1-fish", badgeName:"Fish Friend", phase:"detail", baseSvg: FISH_OUTLINE_SVG, subjectKey:"fish", subjectName:"Fish", subjectEmoji:"🐟", order:1, steps: FISH_DETAIL_STEPS };
export const FISH_EXTRA_LESSON: Lesson = { level:"early-beginner", sublevel:13, slug:"fish-extra", title:"Fish \u00b7 Color it!", subject:"color the fish", emoji:"\u{1F3A8}", intro:"Bring your fish to life! Color it in, and try shading and layering.", badgeKey:"", badgeName:"Fish Friend", phase:"extra", baseSvg: FISH_DETAIL_SVG, subjectKey:"fish", subjectName:"Fish", subjectEmoji:"🐟", order:1, steps: [] };
const PANDA_OUTLINE_STEPS: LessonStep[] = [
{ n: 1, title: "The head", instruction: "Draw one big round face.", tip: "Nice and round.", lines: [`<ellipse cx="200" cy="162" rx="92" ry="84" fill="none" stroke="#2b2440" stroke-width="4"/>`] },
{ n: 2, title: "The ears", instruction: "Add two round ears on top.", lines: [`<circle cx="142" cy="92" r="30" fill="none" stroke="#2b2440" stroke-width="4"/>`, `<circle cx="258" cy="92" r="30" fill="none" stroke="#2b2440" stroke-width="4"/>`] },
{ n: 3, title: "Eye patches", instruction: "Draw a leaf-shaped patch for each eye.", lines: [`<path d="M150 132 C 136 148, 140 178, 164 184 C 186 188, 196 166, 189 146 C 183 132, 164 124, 150 132 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`, `<path d="M250 132 C 264 148, 260 178, 236 184 C 214 188, 204 166, 211 146 C 217 132, 236 124, 250 132 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
];
const PANDA_DETAIL_STEPS: LessonStep[] = [
{ n: 1, title: "The eyes", instruction: "Add a circle and a dot inside each patch.", lines: [`<circle cx="168" cy="156" r="9" fill="none" stroke="#2b2440" stroke-width="3"/>`, `<circle cx="170" cy="158" r="4" fill="#2b2440"/>`, `<circle cx="232" cy="156" r="9" fill="none" stroke="#2b2440" stroke-width="3"/>`, `<circle cx="230" cy="158" r="4" fill="#2b2440"/>`] },
{ n: 2, title: "The nose", instruction: "Draw a little rounded nose.", lines: [`<path d="M186 184 Q200 178 214 184 Q208 198 200 200 Q192 198 186 184 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
{ n: 3, title: "The mouth", instruction: "Add a line down and two smile curves.", lines: [`<path d="M200 200 L200 210" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`, `<path d="M200 210 q-13 12 -26 5" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`, `<path d="M200 210 q13 12 26 5" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
];
const PANDA_OUTLINE_SVG = PANDA_OUTLINE_STEPS.flatMap((s) => s.lines || []).join("\n");
const PANDA_DETAIL_SVG = PANDA_OUTLINE_SVG + "\n" + PANDA_DETAIL_STEPS.flatMap((s) => s.lines || []).join("\n");
export const PANDA_OUTLINE_LESSON: Lesson = { level:"early-beginner", sublevel:21, slug:"panda-outline", title:"Panda \u00b7 Outline", subject:"panda outline", emoji:"🐼", intro:"Let\u2019s draw Panda\u2019s outline, one line at a time. Watch each line, then trace it!", badgeKey:"", badgeName:"Panda Pal", phase:"outline", subjectKey:"panda", subjectName:"Panda", subjectEmoji:"🐼", order:2, steps: PANDA_OUTLINE_STEPS };
export const PANDA_DETAIL_LESSON: Lesson = { level:"early-beginner", sublevel:22, slug:"panda-detail", title:"Panda \u00b7 Details", subject:"panda details", emoji:"🐼", intro:"Now add the details that bring your panda to life.", badgeKey:"early-beginner-2-panda", badgeName:"Panda Pal", phase:"detail", baseSvg: PANDA_OUTLINE_SVG, subjectKey:"panda", subjectName:"Panda", subjectEmoji:"🐼", order:2, steps: PANDA_DETAIL_STEPS };
export const PANDA_EXTRA_LESSON: Lesson = { level:"early-beginner", sublevel:23, slug:"panda-extra", title:"Panda \u00b7 Color it!", subject:"color the panda", emoji:"\u{1F3A8}", intro:"Bring your panda to life! Color it in, and try shading and layering.", badgeKey:"", badgeName:"Panda Pal", phase:"extra", baseSvg: PANDA_DETAIL_SVG, subjectKey:"panda", subjectName:"Panda", subjectEmoji:"🐼", order:2, steps: [] };
const FLOWER_OUTLINE_STEPS: LessonStep[] = [
{ n: 1, title: "The center", instruction: "Draw a circle in the middle.", tip: "Leave room for petals.", lines: [`<circle cx="200" cy="118" r="34" fill="none" stroke="#2b2440" stroke-width="4"/>`] },
{ n: 2, title: "Petals", instruction: "Add petals on top, bottom, left, and right.", lines: [`<path d="M200 86 C 180 78, 176 52, 200 40 C 224 52, 220 78, 200 86 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linejoin="round" transform="rotate(0 200 118)"/>`, `<path d="M200 86 C 180 78, 176 52, 200 40 C 224 52, 220 78, 200 86 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linejoin="round" transform="rotate(90 200 118)"/>`, `<path d="M200 86 C 180 78, 176 52, 200 40 C 224 52, 220 78, 200 86 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linejoin="round" transform="rotate(180 200 118)"/>`, `<path d="M200 86 C 180 78, 176 52, 200 40 C 224 52, 220 78, 200 86 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linejoin="round" transform="rotate(270 200 118)"/>`] },
{ n: 3, title: "More petals", instruction: "Tuck four more petals into the gaps.", lines: [`<path d="M200 86 C 180 78, 176 52, 200 40 C 224 52, 220 78, 200 86 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linejoin="round" transform="rotate(45 200 118)"/>`, `<path d="M200 86 C 180 78, 176 52, 200 40 C 224 52, 220 78, 200 86 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linejoin="round" transform="rotate(135 200 118)"/>`, `<path d="M200 86 C 180 78, 176 52, 200 40 C 224 52, 220 78, 200 86 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linejoin="round" transform="rotate(225 200 118)"/>`, `<path d="M200 86 C 180 78, 176 52, 200 40 C 224 52, 220 78, 200 86 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linejoin="round" transform="rotate(315 200 118)"/>`] },
{ n: 4, title: "The stem", instruction: "Draw a stem going down from the flower.", lines: [`<path d="M195 152 C 191 186, 205 214, 197 256 L203 256 C 211 214, 199 186, 205 152 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
{ n: 5, title: "The leaves", instruction: "Add a leaf on each side of the stem.", lines: [`<path d="M200 198 C 168 184, 138 192, 150 218 C 162 240, 196 226, 200 198 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`, `<path d="M200 226 C 232 214, 262 222, 250 246 C 238 266, 204 252, 200 226 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
];
const FLOWER_DETAIL_STEPS: LessonStep[] = [
{ n: 1, title: "Seeds", instruction: "Dot some seeds in the middle.", lines: [`<circle cx="192" cy="112" r="4" fill="#2b2440"/>`, `<circle cx="208" cy="114" r="4" fill="#2b2440"/>`, `<circle cx="200" cy="124" r="4" fill="#2b2440"/>`, `<circle cx="190" cy="126" r="3" fill="#2b2440"/>`, `<circle cx="210" cy="126" r="3" fill="#2b2440"/>`] },
{ n: 2, title: "Leaf veins", instruction: "Add a line down each leaf.", lines: [`<path d="M170 210 q20 4 28 14" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`, `<path d="M232 240 q-18 2 -28 -10" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
{ n: 3, title: "Center ring", instruction: "Draw a little ring inside the middle.", lines: [`<circle cx="200" cy="118" r="18" fill="none" stroke="#2b2440" stroke-width="3"/>`] },
];
const FLOWER_OUTLINE_SVG = FLOWER_OUTLINE_STEPS.flatMap((s) => s.lines || []).join("\n");
const FLOWER_DETAIL_SVG = FLOWER_OUTLINE_SVG + "\n" + FLOWER_DETAIL_STEPS.flatMap((s) => s.lines || []).join("\n");
export const FLOWER_OUTLINE_LESSON: Lesson = { level:"early-beginner", sublevel:31, slug:"flower-outline", title:"Flower \u00b7 Outline", subject:"flower outline", emoji:"🌸", intro:"Let\u2019s draw Flower\u2019s outline, one line at a time. Watch each line, then trace it!", badgeKey:"", badgeName:"Flower Power", phase:"outline", subjectKey:"flower", subjectName:"Flower", subjectEmoji:"🌸", order:3, steps: FLOWER_OUTLINE_STEPS };
export const FLOWER_DETAIL_LESSON: Lesson = { level:"early-beginner", sublevel:32, slug:"flower-detail", title:"Flower \u00b7 Details", subject:"flower details", emoji:"🌸", intro:"Now add the details that bring your flower to life.", badgeKey:"early-beginner-3-flower", badgeName:"Flower Power", phase:"detail", baseSvg: FLOWER_OUTLINE_SVG, subjectKey:"flower", subjectName:"Flower", subjectEmoji:"🌸", order:3, steps: FLOWER_DETAIL_STEPS };
export const FLOWER_EXTRA_LESSON: Lesson = { level:"early-beginner", sublevel:33, slug:"flower-extra", title:"Flower \u00b7 Color it!", subject:"color the flower", emoji:"\u{1F3A8}", intro:"Bring your flower to life! Color it in, and try shading and layering.", badgeKey:"", badgeName:"Flower Power", phase:"extra", baseSvg: FLOWER_DETAIL_SVG, subjectKey:"flower", subjectName:"Flower", subjectEmoji:"🌸", order:3, steps: [] };
const UNICORN_OUTLINE_STEPS: LessonStep[] = [
{ n: 1, title: "The head", instruction: "Draw a rounded head.", lines: [`<path d="M120 152 C 120 96, 165 72, 200 72 C 235 72, 280 96, 280 152 C 280 202, 246 236, 200 240 C 154 236, 120 202, 120 152 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
{ n: 2, title: "The ears", instruction: "Add a pointy ear on each side.", lines: [`<path d="M150 98 C 138 72, 132 58, 150 60 C 166 64, 174 84, 180 96 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`, `<path d="M250 98 C 262 72, 268 58, 250 60 C 234 64, 226 84, 220 96 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
{ n: 3, title: "The horn", instruction: "Draw a tall horn on top.", lines: [`<path d="M200 80 L188 18 L212 80 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
{ n: 4, title: "The mane", instruction: "Draw the flowing mane and a curl on the forehead.", lines: [`<path d="M256 112 C 320 122, 320 200, 268 250 C 292 196, 280 150, 250 142 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`, `<path d="M176 92 C 165 110, 175 135, 196 130 C 184 118, 188 102, 200 96 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
];
const UNICORN_DETAIL_STEPS: LessonStep[] = [
{ n: 1, title: "The eyes", instruction: "Add two big eyes.", lines: [`<ellipse cx="172" cy="158" rx="7" ry="10" fill="none" stroke="#2b2440" stroke-width="4"/>`, `<ellipse cx="228" cy="158" rx="7" ry="10" fill="none" stroke="#2b2440" stroke-width="4"/>`] },
{ n: 2, title: "Nose & mouth", instruction: "Add two nostril dots and a smile.", lines: [`<circle cx="188" cy="210" r="3" fill="#2b2440"/>`, `<circle cx="214" cy="210" r="3" fill="#2b2440"/>`, `<path d="M186 224 q14 12 28 0" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
{ n: 3, title: "Horn swirls", instruction: "Add little swirl lines on the horn.", lines: [`<path d="M193 66 l14 -5" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`, `<path d="M196 50 l11 -4" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`, `<path d="M199 36 l8 -3" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
{ n: 4, title: "Mane lines", instruction: "Add flowing lines in the mane.", lines: [`<path d="M268 130 C 300 150, 296 200, 262 232" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`, `<path d="M258 140 C 286 160, 282 200, 256 226" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
];
const UNICORN_OUTLINE_SVG = UNICORN_OUTLINE_STEPS.flatMap((s) => s.lines || []).join("\n");
const UNICORN_DETAIL_SVG = UNICORN_OUTLINE_SVG + "\n" + UNICORN_DETAIL_STEPS.flatMap((s) => s.lines || []).join("\n");
export const UNICORN_OUTLINE_LESSON: Lesson = { level:"early-beginner", sublevel:41, slug:"unicorn-outline", title:"Unicorn \u00b7 Outline", subject:"unicorn outline", emoji:"🦄", intro:"Let\u2019s draw Unicorn\u2019s outline, one line at a time. Watch each line, then trace it!", badgeKey:"", badgeName:"Unicorn Magic", phase:"outline", subjectKey:"unicorn", subjectName:"Unicorn", subjectEmoji:"🦄", order:4, steps: UNICORN_OUTLINE_STEPS };
export const UNICORN_DETAIL_LESSON: Lesson = { level:"early-beginner", sublevel:42, slug:"unicorn-detail", title:"Unicorn \u00b7 Details", subject:"unicorn details", emoji:"🦄", intro:"Now add the details that bring your unicorn to life.", badgeKey:"early-beginner-4-unicorn", badgeName:"Unicorn Magic", phase:"detail", baseSvg: UNICORN_OUTLINE_SVG, subjectKey:"unicorn", subjectName:"Unicorn", subjectEmoji:"🦄", order:4, steps: UNICORN_DETAIL_STEPS };
export const UNICORN_EXTRA_LESSON: Lesson = { level:"early-beginner", sublevel:43, slug:"unicorn-extra", title:"Unicorn \u00b7 Color it!", subject:"color the unicorn", emoji:"\u{1F3A8}", intro:"Bring your unicorn to life! Color it in, and try shading and layering.", badgeKey:"", badgeName:"Unicorn Magic", phase:"extra", baseSvg: UNICORN_DETAIL_SVG, subjectKey:"unicorn", subjectName:"Unicorn", subjectEmoji:"🦄", order:4, steps: [] };
const TREE_OUTLINE_STEPS: LessonStep[] = [
{ n: 1, title: "The trunk", instruction: "Draw a tall trunk, wider at the bottom.", lines: [`<path d="M178 262 C 176 222, 184 200, 190 176 L210 176 C 216 200, 224 222, 222 262 C 214 266, 186 266, 178 262 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
{ n: 2, title: "Branches", instruction: "Add two branches reaching up.", lines: [`<path d="M198 180 C 190 152, 168 144, 152 134" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`, `<path d="M204 180 C 214 152, 236 146, 252 136" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
{ n: 3, title: "The treetop", instruction: "Draw a big bumpy treetop.", tip: "Nice and lumpy!", lines: [`<path d="M120 150 C 100 122, 122 96, 150 100 C 152 74, 200 62, 216 88 C 246 72, 286 92, 274 122 C 300 134, 292 168, 262 166 C 250 188, 150 188, 138 166 C 110 170, 104 152, 120 150 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
];
const TREE_DETAIL_STEPS: LessonStep[] = [
{ n: 1, title: "Apples", instruction: "Add a few round apples in the leaves.", lines: [`<circle cx="168" cy="118" r="7" fill="none" stroke="#2b2440" stroke-width="3"/>`, `<circle cx="228" cy="104" r="7" fill="none" stroke="#2b2440" stroke-width="3"/>`, `<circle cx="204" cy="150" r="7" fill="none" stroke="#2b2440" stroke-width="3"/>`] },
{ n: 2, title: "Grass", instruction: "Add wavy grass along the bottom.", lines: [`<path d="M120 262 q14 -16 28 0 q14 -16 28 0 q14 -16 28 0 q14 -16 28 0 q14 -16 28 0" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
{ n: 3, title: "Bark", instruction: "Add a couple of bark lines on the trunk.", lines: [`<path d="M194 200 q4 24 0 56" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`, `<path d="M206 200 q-3 24 0 54" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
];
const TREE_OUTLINE_SVG = TREE_OUTLINE_STEPS.flatMap((s) => s.lines || []).join("\n");
const TREE_DETAIL_SVG = TREE_OUTLINE_SVG + "\n" + TREE_DETAIL_STEPS.flatMap((s) => s.lines || []).join("\n");
export const TREE_OUTLINE_LESSON: Lesson = { level:"early-beginner", sublevel:51, slug:"tree-outline", title:"Tree \u00b7 Outline", subject:"tree outline", emoji:"🌳", intro:"Let\u2019s draw Tree\u2019s outline, one line at a time. Watch each line, then trace it!", badgeKey:"", badgeName:"Tree Top", phase:"outline", subjectKey:"tree", subjectName:"Tree", subjectEmoji:"🌳", order:5, steps: TREE_OUTLINE_STEPS };
export const TREE_DETAIL_LESSON: Lesson = { level:"early-beginner", sublevel:52, slug:"tree-detail", title:"Tree \u00b7 Details", subject:"tree details", emoji:"🌳", intro:"Now add the details that bring your tree to life.", badgeKey:"early-beginner-5-tree", badgeName:"Tree Top", phase:"detail", baseSvg: TREE_OUTLINE_SVG, subjectKey:"tree", subjectName:"Tree", subjectEmoji:"🌳", order:5, steps: TREE_DETAIL_STEPS };
export const TREE_EXTRA_LESSON: Lesson = { level:"early-beginner", sublevel:53, slug:"tree-extra", title:"Tree \u00b7 Color it!", subject:"color the tree", emoji:"\u{1F3A8}", intro:"Bring your tree to life! Color it in, and try shading and layering.", badgeKey:"", badgeName:"Tree Top", phase:"extra", baseSvg: TREE_DETAIL_SVG, subjectKey:"tree", subjectName:"Tree", subjectEmoji:"🌳", order:5, steps: [] };
const DINO_OUTLINE_STEPS: LessonStep[] = [
{ n: 1, title: "The body", instruction: "Draw a big round body.", lines: [`<path d="M118 200 C 118 166, 160 150, 210 150 C 270 150, 302 170, 302 200 C 302 230, 262 246, 205 246 C 155 246, 118 230, 118 200 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
{ n: 2, title: "The neck", instruction: "Sweep a long neck up from the left.", lines: [`<path d="M150 168 C 122 122, 112 80, 124 55 C 130 44, 148 46, 152 58 C 146 86, 162 124, 180 160 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
{ n: 3, title: "The head", instruction: "Add a small head on top of the neck.", lines: [`<path d="M100 46 C 100 32, 120 28, 138 34 C 152 39, 154 56, 142 64 C 126 72, 104 64, 98 54 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
{ n: 4, title: "The tail", instruction: "Draw a long tail swooping to the right.", lines: [`<path d="M298 198 C 338 184, 372 176, 398 168 C 380 192, 360 206, 304 220 C 300 213, 298 206, 298 198 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
{ n: 5, title: "The legs", instruction: "Add two sturdy legs underneath.", lines: [`<path d="M168 242 C 165 264, 166 278, 180 280 C 193 280, 192 264, 190 242 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`, `<path d="M232 244 C 229 266, 230 280, 244 282 C 257 282, 256 266, 254 244 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
];
const DINO_DETAIL_STEPS: LessonStep[] = [
{ n: 1, title: "Back plates", instruction: "Add triangle plates along the back.", lines: [`<path d="M150 150 l10 -16 l12 16 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`, `<path d="M186 146 l11 -17 l12 17 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`, `<path d="M224 147 l11 -16 l12 16 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`, `<path d="M262 152 l10 -15 l12 16 Z" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
{ n: 2, title: "Spots", instruction: "Add a few spots on the body.", lines: [`<circle cx="205" cy="188" r="9" fill="none" stroke="#2b2440" stroke-width="3"/>`, `<circle cx="240" cy="200" r="7" fill="none" stroke="#2b2440" stroke-width="3"/>`, `<circle cx="180" cy="212" r="6" fill="none" stroke="#2b2440" stroke-width="3"/>`] },
{ n: 3, title: "The face", instruction: "Add an eye and a friendly smile.", lines: [`<circle cx="118" cy="46" r="4" fill="#2b2440"/>`, `<path d="M108 56 q12 10 24 3" fill="none" stroke="#2b2440" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`] },
];
const DINO_OUTLINE_SVG = DINO_OUTLINE_STEPS.flatMap((s) => s.lines || []).join("\n");
const DINO_DETAIL_SVG = DINO_OUTLINE_SVG + "\n" + DINO_DETAIL_STEPS.flatMap((s) => s.lines || []).join("\n");
export const DINO_OUTLINE_LESSON: Lesson = { level:"early-beginner", sublevel:61, slug:"dinosaur-outline", title:"Dinosaur \u00b7 Outline", subject:"dinosaur outline", emoji:"🦕", intro:"Let\u2019s draw Dinosaur\u2019s outline, one line at a time. Watch each line, then trace it!", badgeKey:"", badgeName:"Dino Master", phase:"outline", subjectKey:"dinosaur", subjectName:"Dinosaur", subjectEmoji:"🦕", order:6, steps: DINO_OUTLINE_STEPS };
export const DINO_DETAIL_LESSON: Lesson = { level:"early-beginner", sublevel:62, slug:"dinosaur-detail", title:"Dinosaur \u00b7 Details", subject:"dinosaur details", emoji:"🦕", intro:"Now add the details that bring your dinosaur to life.", badgeKey:"early-beginner-6-dinosaur", badgeName:"Dino Master", phase:"detail", baseSvg: DINO_OUTLINE_SVG, subjectKey:"dinosaur", subjectName:"Dinosaur", subjectEmoji:"🦕", order:6, steps: DINO_DETAIL_STEPS };
export const DINO_EXTRA_LESSON: Lesson = { level:"early-beginner", sublevel:63, slug:"dinosaur-extra", title:"Dinosaur \u00b7 Color it!", subject:"color the dinosaur", emoji:"\u{1F3A8}", intro:"Bring your dinosaur to life! Color it in, and try shading and layering.", badgeKey:"", badgeName:"Dino Master", phase:"extra", baseSvg: DINO_DETAIL_SVG, subjectKey:"dinosaur", subjectName:"Dinosaur", subjectEmoji:"🦕", order:6, steps: [] };
const SHADE_STEPS: LessonStep[] = [
{ n: 1, kind: "shade", title: "How to hold your pencil", instruction: "Watch the demo above. Near the tip + press hard = DARK; high up + soft = LIGHT.", tip: "Try it in the air first!", svg: `<g><circle cx="62" cy="46" r="20" fill="#ffd45e"/><g stroke="#ffd45e" stroke-width="4" stroke-linecap="round"><path d="M62 14v-8M62 86v8M30 46h-8M94 46h8M39 23l-6-6M85 23l6-6M39 69l-6 6M85 69l6 6"/></g></g><line x1="40" y1="234" x2="370" y2="234" stroke="#c9c4d6" stroke-width="3"/><circle cx="210" cy="150" r="84" fill="#f1f1f4" stroke="#2b2440" stroke-width="4"/>` },
{ n: 2, kind: "shade", title: "Find the light", instruction: "The sun is up-left. The side facing it stays brightest — that bright spot is the highlight.", svg: `<g><circle cx="62" cy="46" r="20" fill="#ffd45e"/><g stroke="#ffd45e" stroke-width="4" stroke-linecap="round"><path d="M62 14v-8M62 86v8M30 46h-8M94 46h8M39 23l-6-6M85 23l6-6M39 69l-6 6M85 69l6 6"/></g></g><line x1="40" y1="234" x2="370" y2="234" stroke="#c9c4d6" stroke-width="3"/><circle cx="210" cy="150" r="84" fill="#f1f1f4" stroke="#2b2440" stroke-width="4"/><circle cx="178" cy="118" r="22" fill="none" stroke="#4f7cff" stroke-width="3" stroke-dasharray="6 6"/>` },
{ n: 3, kind: "shade", title: "Soft shade all over", instruction: "Pick a light tone and softly shade the whole ball — leave the bright spot white.", tip: "Hold high and go easy.", svg: `<g><circle cx="62" cy="46" r="20" fill="#ffd45e"/><g stroke="#ffd45e" stroke-width="4" stroke-linecap="round"><path d="M62 14v-8M62 86v8M30 46h-8M94 46h8M39 23l-6-6M85 23l6-6M39 69l-6 6M85 69l6 6"/></g></g><line x1="40" y1="234" x2="370" y2="234" stroke="#c9c4d6" stroke-width="3"/><circle cx="210" cy="150" r="84" fill="#f1f1f4" stroke="#2b2440" stroke-width="4"/><circle cx="178" cy="118" r="22" fill="none" stroke="#4f7cff" stroke-width="3" stroke-dasharray="6 6"/>` },
{ n: 4, kind: "shade", title: "Build up the shadow", instruction: "Pick a darker tone and press harder on the side away from the sun. Build it up slowly.", tip: "More passes = darker.", svg: `<g><circle cx="62" cy="46" r="20" fill="#ffd45e"/><g stroke="#ffd45e" stroke-width="4" stroke-linecap="round"><path d="M62 14v-8M62 86v8M30 46h-8M94 46h8M39 23l-6-6M85 23l6-6M39 69l-6 6M85 69l6 6"/></g></g><line x1="40" y1="234" x2="370" y2="234" stroke="#c9c4d6" stroke-width="3"/><circle cx="210" cy="150" r="84" fill="#f1f1f4" stroke="#2b2440" stroke-width="4"/><path d="M250 96 A84 84 0 0 1 250 204 A60 70 0 0 0 250 96 Z" fill="#2b2440" opacity="0.18"/>` },
{ n: 5, kind: "shade", title: "Cast shadow & finish", instruction: "Shade the ground under the ball on the shadow side. Keep the highlight clean!", tip: "🌗", svg: `<g><circle cx="62" cy="46" r="20" fill="#ffd45e"/><g stroke="#ffd45e" stroke-width="4" stroke-linecap="round"><path d="M62 14v-8M62 86v8M30 46h-8M94 46h8M39 23l-6-6M85 23l6-6M39 69l-6 6M85 69l6 6"/></g></g><line x1="40" y1="234" x2="370" y2="234" stroke="#c9c4d6" stroke-width="3"/><circle cx="210" cy="150" r="84" fill="#f1f1f4" stroke="#2b2440" stroke-width="4"/><path d="M250 96 A84 84 0 0 1 250 204 A60 70 0 0 0 250 96 Z" fill="#2b2440" opacity="0.18"/><ellipse cx="262" cy="236" rx="78" ry="15" fill="#2b2440" opacity="0.18"/>` },
];
export const SHADE_BALL_LESSON: Lesson = { level:"learner", sublevel:1, slug:"shade-ball", title:"Light & Shadow: Shade a Ball", subject:"a shaded ball", emoji:"\u{1F317}", intro:"Make a flat circle look like a round ball using light and shadow!", badgeKey:"learner-1-shading", badgeName:"Shading Star", mode:"shade", subjectKey:"shade-ball", subjectName:"Shade a Ball", subjectEmoji:"\u{1F317}", order:1, steps: SHADE_STEPS };
import { BEGINNER_LESSONS, BEGINNER_PACKS, type Pack } from "./curriculum.beginner";
import { EB2_LESSONS } from "./curriculum.eb2";
import { ANIMAL_LESSONS } from "./curriculum.animals";
export { BEGINNER_PACKS, type Pack } from "./curriculum.beginner";
export { LIGHT_HINT_SVG } from "./curriculum.beginner";
const EARLY_BEGINNER_LESSONS: Lesson[] = [FISH_OUTLINE_LESSON, FISH_DETAIL_LESSON, FISH_EXTRA_LESSON, PANDA_OUTLINE_LESSON, PANDA_DETAIL_LESSON, PANDA_EXTRA_LESSON, FLOWER_OUTLINE_LESSON, FLOWER_DETAIL_LESSON, FLOWER_EXTRA_LESSON, UNICORN_OUTLINE_LESSON, UNICORN_DETAIL_LESSON, UNICORN_EXTRA_LESSON, TREE_OUTLINE_LESSON, TREE_DETAIL_LESSON, TREE_EXTRA_LESSON, DINO_OUTLINE_LESSON, DINO_DETAIL_LESSON, DINO_EXTRA_LESSON, ...EB2_LESSONS, ...ANIMAL_LESSONS];
export const LESSONS: Lesson[] = [...EARLY_BEGINNER_LESSONS, ...BEGINNER_LESSONS, SHADE_BALL_LESSON];
export function getLesson(level: string, sublevel: number): Lesson | undefined { return LESSONS.find((l) => l.level === level && l.sublevel === sublevel); }
export function getLessonBySlug(slug: string): Lesson | undefined { return LESSONS.find((l) => l.slug === slug); }
export function cumulativeSvg(lesson: Lesson, upTo: number): string { return lesson.steps.filter((s) => s.n <= upTo).map((s) => s.svg || "").join("\n"); }
export function cumulativeLines(lesson: Lesson, upTo: number): string { return lesson.steps.filter((s) => s.n <= upTo).flatMap((s) => s.lines || []).join("\n"); }
export function getSubjects(level: string): { key: string; name: string; emoji: string; order: number; lessons: Lesson[] }[] {
const inLevel = LESSONS.filter((l) => l.level === level);
const map = new Map<string, { key: string; name: string; emoji: string; order: number; lessons: Lesson[] }>();
for (const l of inLevel) {
const key = l.subjectKey || l.slug;
if (!map.has(key)) map.set(key, { key, name: l.subjectName || l.title, emoji: l.subjectEmoji || l.emoji, order: l.order ?? l.sublevel, lessons: [] });
map.get(key)!.lessons.push(l);
}
// Covers both Early Beginner (outline/detail/extra) and Beginner (construct/outline/color/light).
const rank: Record<string, number> = { construct: 0, outline: 1, detail: 2, color: 3, extra: 3, light: 4 };
for (const s of map.values()) s.lessons.sort((a, b) => (rank[a.phase ?? ""] ?? 0) - (rank[b.phase ?? ""] ?? 0));
return [...map.values()].sort((a, b) => a.order - b.order);
}
/** Themed groups for browsing a level's subjects (Early Beginner). Keyed by subjectKey. */
export interface SubjectGroup { key: string; name: string; emoji: string; subjectKeys: string[]; }
export const EARLY_BEGINNER_GROUPS: SubjectGroup[] = [
{ key: "shapes", name: "Simple Shapes", emoji: "⭐", subjectKeys: ["square", "triangle", "heart"] },
{ key: "classics", name: "Animals & Nature", emoji: "\u{1F41F}", subjectKeys: ["fish", "panda", "flower", "unicorn", "tree", "dinosaur"] },
{ key: "safari", name: "Safari Animals", emoji: "\u{1F981}", subjectKeys: ["lion", "elephant", "giraffe", "zebra", "rhino", "leopard", "tiger", "monkey", "crocodile"] },
{ key: "woodland", name: "Woodland Critters", emoji: "\u{1F98A}", subjectKeys: ["fox", "bear", "deer", "rabbit", "hedgehog", "owl", "cat", "turtle"] },
{ key: "air", name: "Up in the Air", emoji: "\u{1F98B}", subjectKeys: ["bird", "butterfly"] },
{ key: "fun", name: "Fun Things", emoji: "\u{1F380}", subjectKeys: ["bow-tie", "football", "basketball", "cup", "trophy"] },
{ key: "wear", name: "Things to Wear", emoji: "\u{1F455}", subjectKeys: ["shirt", "dress", "socks"] },
{ key: "faces", name: "Faces & Features", emoji: "\u{1F642}", subjectKeys: ["eyes", "face"] },
];
export type GroupedSubjects = { key: string; name: string; emoji: string; subjects: ReturnType<typeof getSubjects> }[];
/** Bucket a level's subjects into ordered themed groups; anything unlisted falls into a "More" group. */
export function getGroupedSubjects(level: string): GroupedSubjects {
const subjects = getSubjects(level);
const byKey = new Map(subjects.map((s) => [s.key, s]));
const used = new Set<string>();
const cfg = level === "early-beginner" ? EARLY_BEGINNER_GROUPS : [];
const groups: GroupedSubjects = cfg
.map((g) => {
const subs = g.subjectKeys.map((k) => byKey.get(k)).filter(Boolean) as ReturnType<typeof getSubjects>;
subs.forEach((s) => used.add(s.key));
return { key: g.key, name: g.name, emoji: g.emoji, subjects: subs };
})
.filter((g) => g.subjects.length > 0);
const rest = subjects.filter((s) => !used.has(s.key));
if (rest.length) groups.push({ key: "more", name: "More", emoji: "✨", subjects: rest });
return groups;
}
/** All badge definitions: per-subject (Early Beginner) + per-pack (Beginner). */
export interface BadgeDef { badgeKey: string; name: string; emoji: string; groupKey: string; }
export function badgeDefs(): BadgeDef[] {
const fromLessons = LESSONS.filter((l) => l.badgeKey).map((l) => ({ badgeKey: l.badgeKey, name: l.badgeName, emoji: l.emoji, groupKey: `${l.level}-${l.sublevel}` }));
const fromPacks = BEGINNER_PACKS.map((p) => ({ badgeKey: p.badgeKey, name: p.badgeName, emoji: p.emoji, groupKey: "" }));
return [...fromLessons, ...fromPacks];
}
export function getPackByBadge(badgeKey: string): Pack | undefined { return BEGINNER_PACKS.find((p) => p.badgeKey === badgeKey); }
/** The pack a lesson belongs to (Beginner only), via its packKey. */
export function getPackForLesson(lesson: Lesson): Pack | undefined { return lesson.packKey ? BEGINNER_PACKS.find((p) => p.key === lesson.packKey) : undefined; }
/** Every lesson that belongs to a pack (all phases of all its subjects). */
export function lessonsInPack(pack: Pack): Lesson[] { return LESSONS.filter((l) => l.level === pack.level && l.subjectKey && pack.subjectKeys.includes(l.subjectKey)); }
/** Number of completed steps needed to count a lesson as done. */
export function lessonStepCount(l: Lesson): number { return l.steps.length || 1; }