From df6ed78281c6a9a195f2b01c5125f524a626f024 Mon Sep 17 00:00:00 2001 From: Alex Dunmow Date: Sun, 5 Jul 2026 11:25:52 +0800 Subject: [PATCH] =?UTF-8?q?feat(kindergarten):=20Wave=20A=20theme=20lane?= =?UTF-8?q?=20(WO-TF-019)=20=E2=80=94=2049=20overrides,=207=20templates,?= =?UTF-8?q?=20GO-BIG=20mascot=20hero?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Preschool / daycare identity: crayon borders, sticker badges, confetti, and a dependency-free bouncing-mascot + confetti-canvas hero showpiece (hero/landing only, lazy-init, prefers-reduced-motion honored, static no-JS fallback). - All 49 mandatory builtin overrides (OVERRIDE-CONTRACT §1), field-correct against the frozen cms builtins, re-skinned to the crayon design system. Legacy button (label/link/style) and card (text/media) field reads reconciled. - 7 page templates: default, full-width, landing, article, blog-index, contact, auth — standard header/main/footer slots, dotted-paper chrome, dual-mode. - Blocks reworked: mascot_hero/big_cta/gallery_of_art/numbers_counter/ storybook_quote/footer converted to builtin overrides; 4 persona blocks kept/ added — alphabet_strip (array-shaped, gotcha-safe), schedule, art_wall, story_time. - 5 presets, all mode "both", bright-classroom light + quiet-time navy dark. - Bundled OFL woff2 (Baloo 2 700, Quicksand 400/700) in assets/fonts + LICENSES; all font-family via var(--font-*); RECOMMENDED_FONTS/ICONS updated; required_icon_packs declared. - Themed email-safe wrapper; master pages (site + auth); seed demo site (home/about/blog+3 posts/contact form+row_inserted email workflow/login), preschool voice. Known platform limit (OVERRIDE-CONTRACT §11): auth-form/auth-status/ password-reset-form/page-suggestions overrides are authored correct-to-model but do not dispatch (compiled BlockFuncs). page-suggestions also omits the dynamic suggestion list (no FTS in codeless). Verified: `make` (ninja plugin build) exit 0; check-safety exit 0. Visual quality UNVERIFIED until Wave B. Co-Authored-By: Claude Opus 4.8 --- RECOMMENDED_FONTS.md | 60 +- RECOMMENDED_ICONS.md | 26 + assets/fonts/LICENSES.md | 20 + assets/fonts/baloo2-700.woff2 | Bin 0 -> 19436 bytes assets/fonts/quicksand-400.woff2 | Bin 0 -> 15776 bytes assets/fonts/quicksand-700.woff2 | Bin 0 -> 15124 bytes blocks/alphabet_strip.ninjatpl | 2 +- blocks/alphabet_strip.schema.json | 19 +- blocks/art_wall.ninjatpl | 1 + blocks/art_wall.schema.json | 51 + blocks/big_cta.ninjatpl | 1 - blocks/big_cta.schema.json | 28 - blocks/blocks.yaml | 56 +- blocks/footer.ninjatpl | 1 - blocks/footer.schema.json | 48 - blocks/gallery_of_art.ninjatpl | 1 - blocks/gallery_of_art.schema.json | 52 -- blocks/mascot_hero.ninjatpl | 1 - blocks/mascot_hero.schema.json | 46 - blocks/numbers_counter.ninjatpl | 1 - blocks/numbers_counter.schema.json | 32 - blocks/story_time.ninjatpl | 1 + blocks/story_time.schema.json | 32 + blocks/storybook_quote.ninjatpl | 1 - blocks/storybook_quote.schema.json | 26 - fonts.json | 19 +- manifest.yaml | 869 +++++++++--------- master_pages.json | 28 +- plugin.mod | 5 +- presets.json | 320 +++++-- seed/seed.json | 228 +++++ seed/workflows.json | 19 + templates/kindergarten/article.ninjatpl | 18 +- templates/kindergarten/auth.ninjatpl | 13 + templates/kindergarten/blog-index.ninjatpl | 11 + templates/kindergarten/contact.ninjatpl | 11 + templates/kindergarten/default.ninjatpl | 12 +- templates/kindergarten/full-width.ninjatpl | 12 +- templates/kindergarten/landing.ninjatpl | 14 +- .../kindergarten/announcement-bar.ninjatpl | 21 + .../kindergarten/audio-embed.ninjatpl | 16 + .../overrides/kindergarten/auth-form.ninjatpl | 77 ++ .../kindergarten/auth-status.ninjatpl | 36 + .../kindergarten/author-bio-hero.ninjatpl | 21 + .../kindergarten/author-bio.ninjatpl | 13 + .../overrides/kindergarten/blog-hero.ninjatpl | 23 + .../kindergarten/breadcrumbs.ninjatpl | 15 + .../overrides/kindergarten/button.ninjatpl | 3 +- .../overrides/kindergarten/card.ninjatpl | 3 +- .../kindergarten/category-list.ninjatpl | 18 + .../kindergarten/contact-card.ninjatpl | 18 + .../kindergarten/contact-form.ninjatpl | 40 + .../overrides/kindergarten/countdown.ninjatpl | 49 + templates/overrides/kindergarten/cta.ninjatpl | 15 + .../overrides/kindergarten/divider.ninjatpl | 2 + .../kindergarten/event-list.ninjatpl | 41 + templates/overrides/kindergarten/faq.ninjatpl | 18 + .../kindergarten/feature-grid.ninjatpl | 18 + .../kindergarten/featured-posts.ninjatpl | 23 + .../overrides/kindergarten/footer.ninjatpl | 39 + .../overrides/kindergarten/gallery.ninjatpl | 52 ++ .../overrides/kindergarten/heading.ninjatpl | 8 +- .../overrides/kindergarten/hero.ninjatpl | 94 ++ .../kindergarten/hours-location.ninjatpl | 31 + .../overrides/kindergarten/image.ninjatpl | 2 + .../kindergarten/logo-strip.ninjatpl | 16 + .../overrides/kindergarten/menu-list.ninjatpl | 30 + .../overrides/kindergarten/navbar.ninjatpl | 104 +++ .../kindergarten/newsletter-signup.ninjatpl | 12 + .../kindergarten/page-suggestions.ninjatpl | 12 + .../kindergarten/password-reset-form.ninjatpl | 13 + .../kindergarten/popular-posts.ninjatpl | 16 + .../overrides/kindergarten/post-hero.ninjatpl | 21 + .../kindergarten/post-metadata.ninjatpl | 9 + .../overrides/kindergarten/pricing.ninjatpl | 25 + .../overrides/kindergarten/quote.ninjatpl | 2 + .../kindergarten/related-posts.ninjatpl | 17 + .../kindergarten/rich-section.ninjatpl | 14 + .../overrides/kindergarten/rich-text.ninjatpl | 2 + .../kindergarten/sidebar-nav.ninjatpl | 15 + .../kindergarten/social-links.ninjatpl | 14 + .../overrides/kindergarten/stats.ninjatpl | 49 + .../overrides/kindergarten/team.ninjatpl | 22 + .../kindergarten/testimonial.ninjatpl | 27 + .../overrides/kindergarten/text.ninjatpl | 3 +- .../overrides/kindergarten/timeline.ninjatpl | 22 + .../kindergarten/utility-bar.ninjatpl | 10 + .../kindergarten/video-embed.ninjatpl | 23 + 88 files changed, 2342 insertions(+), 917 deletions(-) create mode 100644 RECOMMENDED_ICONS.md create mode 100644 assets/fonts/LICENSES.md create mode 100644 assets/fonts/baloo2-700.woff2 create mode 100644 assets/fonts/quicksand-400.woff2 create mode 100644 assets/fonts/quicksand-700.woff2 create mode 100644 blocks/art_wall.ninjatpl create mode 100644 blocks/art_wall.schema.json delete mode 100644 blocks/big_cta.ninjatpl delete mode 100644 blocks/big_cta.schema.json delete mode 100644 blocks/footer.ninjatpl delete mode 100644 blocks/footer.schema.json delete mode 100644 blocks/gallery_of_art.ninjatpl delete mode 100644 blocks/gallery_of_art.schema.json delete mode 100644 blocks/mascot_hero.ninjatpl delete mode 100644 blocks/mascot_hero.schema.json delete mode 100644 blocks/numbers_counter.ninjatpl delete mode 100644 blocks/numbers_counter.schema.json create mode 100644 blocks/story_time.ninjatpl create mode 100644 blocks/story_time.schema.json delete mode 100644 blocks/storybook_quote.ninjatpl delete mode 100644 blocks/storybook_quote.schema.json create mode 100644 seed/seed.json create mode 100644 seed/workflows.json create mode 100644 templates/kindergarten/auth.ninjatpl create mode 100644 templates/kindergarten/blog-index.ninjatpl create mode 100644 templates/kindergarten/contact.ninjatpl create mode 100644 templates/overrides/kindergarten/announcement-bar.ninjatpl create mode 100644 templates/overrides/kindergarten/audio-embed.ninjatpl create mode 100644 templates/overrides/kindergarten/auth-form.ninjatpl create mode 100644 templates/overrides/kindergarten/auth-status.ninjatpl create mode 100644 templates/overrides/kindergarten/author-bio-hero.ninjatpl create mode 100644 templates/overrides/kindergarten/author-bio.ninjatpl create mode 100644 templates/overrides/kindergarten/blog-hero.ninjatpl create mode 100644 templates/overrides/kindergarten/breadcrumbs.ninjatpl create mode 100644 templates/overrides/kindergarten/category-list.ninjatpl create mode 100644 templates/overrides/kindergarten/contact-card.ninjatpl create mode 100644 templates/overrides/kindergarten/contact-form.ninjatpl create mode 100644 templates/overrides/kindergarten/countdown.ninjatpl create mode 100644 templates/overrides/kindergarten/cta.ninjatpl create mode 100644 templates/overrides/kindergarten/divider.ninjatpl create mode 100644 templates/overrides/kindergarten/event-list.ninjatpl create mode 100644 templates/overrides/kindergarten/faq.ninjatpl create mode 100644 templates/overrides/kindergarten/feature-grid.ninjatpl create mode 100644 templates/overrides/kindergarten/featured-posts.ninjatpl create mode 100644 templates/overrides/kindergarten/footer.ninjatpl create mode 100644 templates/overrides/kindergarten/gallery.ninjatpl create mode 100644 templates/overrides/kindergarten/hero.ninjatpl create mode 100644 templates/overrides/kindergarten/hours-location.ninjatpl create mode 100644 templates/overrides/kindergarten/image.ninjatpl create mode 100644 templates/overrides/kindergarten/logo-strip.ninjatpl create mode 100644 templates/overrides/kindergarten/menu-list.ninjatpl create mode 100644 templates/overrides/kindergarten/navbar.ninjatpl create mode 100644 templates/overrides/kindergarten/newsletter-signup.ninjatpl create mode 100644 templates/overrides/kindergarten/page-suggestions.ninjatpl create mode 100644 templates/overrides/kindergarten/password-reset-form.ninjatpl create mode 100644 templates/overrides/kindergarten/popular-posts.ninjatpl create mode 100644 templates/overrides/kindergarten/post-hero.ninjatpl create mode 100644 templates/overrides/kindergarten/post-metadata.ninjatpl create mode 100644 templates/overrides/kindergarten/pricing.ninjatpl create mode 100644 templates/overrides/kindergarten/quote.ninjatpl create mode 100644 templates/overrides/kindergarten/related-posts.ninjatpl create mode 100644 templates/overrides/kindergarten/rich-section.ninjatpl create mode 100644 templates/overrides/kindergarten/rich-text.ninjatpl create mode 100644 templates/overrides/kindergarten/sidebar-nav.ninjatpl create mode 100644 templates/overrides/kindergarten/social-links.ninjatpl create mode 100644 templates/overrides/kindergarten/stats.ninjatpl create mode 100644 templates/overrides/kindergarten/team.ninjatpl create mode 100644 templates/overrides/kindergarten/testimonial.ninjatpl create mode 100644 templates/overrides/kindergarten/timeline.ninjatpl create mode 100644 templates/overrides/kindergarten/utility-bar.ninjatpl create mode 100644 templates/overrides/kindergarten/video-embed.ninjatpl diff --git a/RECOMMENDED_FONTS.md b/RECOMMENDED_FONTS.md index 3abfe16..ab81536 100644 --- a/RECOMMENDED_FONTS.md +++ b/RECOMMENDED_FONTS.md @@ -1,43 +1,39 @@ -# Recommended fonts for Kindergarten +# Recommended fonts — Kindergarten -The kindergarten theme ships `fonts.json = []` per the wave-1 fonts policy -(`themes/docs/FONTS.md`). Admins assign fonts via the typography panel; this -file lists the picker recommendations sourced from the spec (`docs/works/kindergarten.md` §3). +This theme **bundles** two OFL (SIL Open Font License 1.1) latin-subset webfonts in +`assets/fonts/` and declares them in `fonts.json`. On install they become +`source=template` rows in the `fonts` table, so their `@font-face` declarations are +auto-emitted and they are selectable in the admin typography picker. No manual font setup +is required to get the intended look. -Until the admin makes a choice, the theme falls back to its system stack: +| Slot | Bundled family | File | Fallback stack (before assignment) | +|---|---|---|---| +| Heading (`--font-heading`) | **Baloo 2** | `fonts/baloo2-700.woff2` | `"Baloo 2", "Fredoka", "Quicksand", system-ui, sans-serif` | +| Body (`--font-body`) | **Quicksand** | `fonts/quicksand-400.woff2`, `fonts/quicksand-700.woff2` | `"Quicksand", "Nunito", system-ui, -apple-system, "Segoe UI", sans-serif` | +| Mono (`--font-mono`) | *(not bundled — system fallback)* | — | `ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace` | -- `--font-heading` → `Quicksand`, `Sniglet`, `Nunito`, system sans -- `--font-body` → `Nunito`, `Quicksand`, system sans -- `--font-mono` → `Fira Code`, `JetBrains Mono`, system monospace +## How the theme consumes them -## How to apply +Every `font-family` in the theme flows through a CSS variable with a bundled-family +fallback — nothing is hardcoded, so the admin picker always wins: -1. Open **Settings → Typography** in the BlockNinja admin. -2. Click the **Google Fonts** tab in the font picker. -3. Add each font below using the listed picker source. -4. Assign Heading / Body / Mono in the typography panel: - - Heading slot → `google:Quicksand` (700) - - Body slot → `google:Nunito` (400, 600) - - Mono slot → `google:Fira Code` (400) +- `.kg-display` -> `var(--font-heading, "Baloo 2", ...)` — headlines, hero, section titles, sticker numerals. +- `.kg-body` / `.kg-prose` -> `var(--font-body, "Quicksand", ...)` — body copy, forms, cards. +- `.kg-mono` -> `var(--font-mono, ...)` — schedule times and small numeric readouts. -## Recommendations +## To assign (optional) -| Slot | Source | Family | Weights / styles | Notes | -|---------|--------------------|-------------|---------------------|-------| -| Heading | `google:Quicksand` | Quicksand | 400, 600, 700 | Primary display face — rounded, friendly. | -| Heading (alt) | `google:Sniglet` | Sniglet | 400, 800 | Alt display face for extra-chunky banners. Confirm SIL OFL compatibility before bundling. | -| Body | `google:Nunito` | Nunito | 400, 600, 700 | Famously rounded body text. Pairs with Quicksand. | -| Mono | `google:Fira Code` | Fira Code | 400 | Use the regular weight; ligatures look great in classroom code samples. | +Open the typography panel and assign **Baloo 2 -> Heading** and **Quicksand -> Body**. Both +appear in the picker under the theme's bundled fonts. -## Why these picks +## Alternate direction -- **Quicksand** and **Nunito** are SIL OFL — safe to recommend and free to use. -- **Sniglet** is also SIL OFL with the original "Sniglet" family available via Google Fonts as a sibling of Comfortaa. The spec calls out Sniglet as the alternate display face; ship Comfortaa as a backup if your admin prefers something with a similar feel but slightly more conservative letterforms. -- **Fira Code** is SIL OFL. The spec calls for "Fira Code Rounded" — Google Fonts ships only the regular Fira Code variants today; use the regular as the closest available stand-in. +The locked WO-TF-019 direction pairs **Baloo 2** (rounded display) with **Quicksand** +(body). **Fredoka** (OFL) is an equally good rounded display alternative to Baloo 2 — both +are in the curated Google Fonts tab. **Nunito** (OFL) is a warmer-weight body alternative to +Quicksand. -## Wave-2 follow-up +## Licensing -If the brand requires Sniglet rounded everywhere, bundle the woff2 files -inside `assets/fonts/web/` and add the entries to `fonts.json` per the -schema in `themes/docs/FONTS.md`. Record the licence in `LICENSES.md` at the -theme root. +Both are OFL 1.1 and cleared for redistribution inside this artifact. Details and +attribution: `assets/fonts/LICENSES.md`. diff --git a/RECOMMENDED_ICONS.md b/RECOMMENDED_ICONS.md new file mode 100644 index 0000000..4ca94a2 --- /dev/null +++ b/RECOMMENDED_ICONS.md @@ -0,0 +1,26 @@ +# Recommended icons — Kindergarten + +Icons render through the CMS sprite pipeline: `` +(or the `::pack:name:size::` shorthand). The theme's overrides reference two bundled packs. + +## Required packs + +Declared in `plugin.mod` as `required_icon_packs = ["lucide", "simple-icons"]` +(forward-declared — the loader does not yet auto-install; install manually until it does). + +| Pack | Used for | Install | +|---|---|---| +| **lucide** | UI glyphs the builtin/override templates use: `arrow-right`, `check`, `chevron-down`, `chevron-left`, `chevron-right`, `x`, `star`, `flag`, `navigation`, `map-pin`, `download`, `play`, `sun`, `book-open`, `palette`, `user`, `globe` | Ships bundled with the CMS — no action needed. | +| **simple-icons** | Author/social brand glyphs: `x` (Twitter), `linkedin`, `github`, `facebook`, `instagram`, `youtube` | Ships bundled with the CMS — no action needed. | + +Both packs ship bundled with the CMS, so a fresh instance renders every override icon with +no manual step. If you author blocks that reference other packs (e.g. `tabler`, `phosphor`), +install them from `/admin/icons` (Browse -> install -> enable) before activation. + +## Note + +Icon values in block content are stored as `"pack:name"` strings and split with +`|split:":"|first` / `|last`. Never inline raw SVG path data for content icons — that +bypasses the sprite system and the admin pack manager. (The theme's decorative mascot, +schedule glyphs, and social marks in persona blocks are intrinsic illustrations, not +content-icon fields.) diff --git a/assets/fonts/LICENSES.md b/assets/fonts/LICENSES.md new file mode 100644 index 0000000..97c8d32 --- /dev/null +++ b/assets/fonts/LICENSES.md @@ -0,0 +1,20 @@ +# Bundled font licenses — Kindergarten theme + +Both families are bundled as latin-subset `.woff2` and are licensed under the +SIL Open Font License 1.1 (OFL). Redistribution inside this theme artifact is permitted. + +| File | Family | License | Source | +|---|---|---|---| +| `baloo2-700.woff2` | Baloo 2 (700) | OFL 1.1 | Google Fonts (fonts.gstatic.com, v23), latin subset | +| `quicksand-400.woff2` | Quicksand (400) | OFL 1.1 | Google Fonts (fonts.gstatic.com, v37), latin subset | +| `quicksand-700.woff2` | Quicksand (700) | OFL 1.1 | Google Fonts (fonts.gstatic.com, v37), latin subset | + +OFL 1.1 full text: https://openfontlicense.org + +- Baloo 2 © The Baloo 2 Project Authors (ektype). +- Quicksand © The Quicksand Project Authors (Andrew Paglinawan). + +These are the Google-hosted latin subsets (Latin + Latin-1 punctuation), chosen to keep +the bundle small (~50 KB total). They cover English content; sites needing extended Latin, +Devanagari, Cyrillic, Greek, or Vietnamese glyphs should assign the full family from the +admin Google Fonts picker instead. diff --git a/assets/fonts/baloo2-700.woff2 b/assets/fonts/baloo2-700.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..ceb751aec8f3d476bff789882c3cde4e9ef018a4 GIT binary patch literal 19436 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 literal 0 HcmV?d00001 diff --git a/assets/fonts/quicksand-400.woff2 b/assets/fonts/quicksand-400.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..8018cfd7d9bb09613508b2177421b529f9d2b743 GIT binary patch literal 15776 zcmV;RJzv6iPew8T0RR9106m}p5C8xG0ETz~06jba0RR9100000000000000000000 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
I}Y$OAS z^F*$XLx#nv6QQWFqF>N>0Og^xaUS@EmRcMrgjJnfq2|AjtrsMh6{da+Vv|u?IhxM4 z`}+1z62hU4+qkTGz6@+L|1!UW`aJFZnRz;;S1b>%aNyTD0{o$I78>6D))@%G1?U8K&ij3{z?nQ*?gcP?*RA zLir`DG0G%Qk=-FHjwrfZE=6Z?v_qbqBLj+}i0BbrMUnbb5~A(8T&ZoVRQmt~Bmw8b!M(Ry&J4RHi<&0GL)RCzrw?fD`4o4qt# z)7}wLa+I=euargnjig*tf;gg|K^Z2JCsM2rVzZS`?M-HDqr+isXtLO|8!Wa4hr`y` zV1A$p%M}s5UJ(u}G+G{@46{pRBrFH+L3A<2C+t^Rg0z^8irLM5vp|lfF%-rQO0{G0mjKuxW$n7rsLv;yZp8iGSuf zgaW?vmu^8?R#uuofQX7j!i-gGk?j;cNPwts~@@vz#qVR5xsmk0;0exUt(jFD3!~qyw-FfMeIg$a(-wPjqSkJwmxU2rIjze zHvx{PGKItB}pbB4MmRAss@i5pe@(EN48QD_Milt7{bg`Q2|V znY5ik+D0M|`N%o4Vc5ai)f;iZnJ(s&8q#t$>FoYI!TTLx4|?E)p=%XdqIBowx^s+{ ztD_g7ko=r-u1(_7;oBITcc|fZO)rAoWw3j#6C;PGqVd$`vJ26JDW?n!{cdLUC1%y7 z&)!Ed0<8zJz)>eS_0KD?E)geWK0!pRZd{yDQjR0I7%v%zx;Q_p_azaRoQi&;F^`pE z801jZS+58<8Q3$*zGBQCU~IpG$B~Z_I0yV!SjA0YK*@XzL386uLzE6NP7fI}MCF@d z?4_U|8!f~AvIyVlKhUun=M_DUsBG66Lw7gJGv;2~$l_R-c5w&l)-?}W{l=XsX3kz) zJ#r1_h3}YrA3Lhju#4`><>%N%YE?$jxdT$Z-)Ir}#S(tL(Jb5)7yyBgINcxlfy5%3-U@P zAcOiyw6fDnUZnkB6!vpB}Gx{5wSa^Q6B z%vzOm1K9aRsb|ax%(6VioS7bsG(SQ!_RrY-ut%6|&W9@(M#mlClqPewz49sALM3r0a}pkZz3-Bn{yc97wKt? z^#9WtY3aat?#l>`^RLbSy`zwpi<-LKmw)4}ua+*nO`(HSHyQ2QMEqHl5BeTxTM(dJ zXz+WAzaobVbX)J&#MU_ zOsjEHt5daXf;-;KJ#Hx51#g|WACS3s^6vX;S+AC7Z1s)rfMnpkdFN$)SlSHl!50fs z?SM#(V(_IwX^txK|rRQyYur1Y#K~x@nsqWyLR{+HMx~W{8gsPrP z09C6{!?(^Sk(qa9U9Tm-yRIB|;f?ta;~!Q7Q;j+e_%2dzAFn}|+8uPMIl2o|eP4a- zUFoY60~C~-*8(8AU9p|$?vyO`Iu&0p%jxMOhSLBzl^>jtffg@Phi74fbp$r0HfaRnOE)fVvv{~eB@Se zf}K4(9X}{+yqu!_f8D~({nsfx7#x4UvFms@cIDwNi>KPU@n4p|=7alH@g@)}vgCNj zvGcNS{)3*oE=IQo_U`rsL0Nd_IQEYw)G>R9w|Iy5_<)OiBuTyVLatrap}&{A-G&eL z{&u{_!-wG22S6!%OnuQ?>Tn?bbqZ*F^ERHu1D!`F(pp;WW`OGMSm!Pw0_nk>lnB)- z$ld8A+RM{pU{tmUMDUkzy@LOQ`%-j&lIy;ud0X`$X{K>K{y-KH>q5J3w`Vz+{?CATUp__83iJD+a?*!c5heCiiJt6n?1D}Q5 zQoz^wG4Z}FyuhqB-K}cF)^Fzjey0@z@oT9`zZ18Mpd<7sJ?}Y}#xp}hi~He9kOz8( z?!$>j**)2*w)aT~(hK{xLTRkBSju*^p`9s#?16k1NE?whc36xIEYfjg6= zf5?O8!tU8wIw1bh8;FUb?Yte!zdPH-60=aHg!h7BPFX=r;hsAbskZZ&!;sWnK&R=0 z?h*&jwu{_>Vmpa{Hw-E#@vp4B?I)hX{+$q-(!E`zSzf$d3HDO|r8268YT7v-pHun9 zaER92`K)1J?Jj@-rO5xy5gFN|n(bdzmj3~G>x~|-0KmI{c3RZjcy7IMxdLqSyaP);{y%`I;A#9F#b4zAm?^!Cxirt}fHhJ%o2IV=rMDN@OHv zGU%03qx{UFfmAq{4x_l8I-$4JB7NPkniTqs6W)+`U-Qxor}XxO_8K9hEAev+XNkhO z`lymxIB#Q8;uxWB8VCB6WXsblLY!G4f<*kiO7$bItbEw?4f*a!KbrzMB+gDk->j5C ztqOfbjP**LgmoB59v(z;agM<<2vi-5aSE1v*uKWjPLr8B3sxSQXxQ*oS&z29^VN@BV<$RjXYQ9pk05%yp{ ziSXVhtK+cGB+*=-S%^-bBA#nsArt0M1fVh?d^glVqgKsYjPa;HV7JjMlzQr@*Q>Q) zW3pFaG;1f;RbkE6Z57HD6fst-dZR)|txgSlgRMg@sxh;%Ev&X@F(rCAzN{{tIJ6X0 zR1E844*YO3bA+{_)dg3;%PJy!R7l~gcI(n@K(7?0!9X=Lj^aXX%$v``{`4+w8r64k zr?D1`E(6xiA+pC!>T^+0Lb^!Rwxey)ml^CF4 z?GTgU_(=g`sTe&Y@o^gE^4xg=p}Zg$bd}?FDK4mCeEA-Y+l-ok)~*JHi|XuZJn(Ky zWD!g$^q5FvGp@Lpq*?4O-Eh+_x5-OtX!g*XXxwpEvyUyc$6i{`>F8UXJNr_ zEUavvvh!#KYg=`7SkJ2{J<88PkMR>gUi12LKc1X4n*RTubz*6|gDJua&Li-)y zf`+=QkZK-wMiYNcJJQCagFHF z!&+4;ol~V%sgVN{n};Tgqxo>6S)(;ZJcf>>{ymyfBwXFvh1!vSuRs2f(l*5KVU63! zsIe1)`b~JyapbSa3+$o;$EEB%a$rCFLO`;I-|t7hMu!djVT2!lm?;3ZIsgRF1KP}RezN{M96@5+;dT}?i}3&O z8|WtDfqg|FPyP*B&{aaC)(=J=cG0R#OB#`XxL9AB-cg6_)o1LO>i}MAvWsydM-6Om zxJuJubBA}0`i?=2Nn!d9AM@3M86QV9#w?-LV|oK{HtvSJhW=+| z2OB7GMF(QoH~?tFdB;X0V%Ruktohe4tS9UoA z++jsbodi$gp8q+!R}TV`zN?go2)T(#Xwd#VZS(zqWN71=!!r=Yh`^|A&Y_JqShdns z1TH((MGcW|H3}#J64>l!TiT{V(n&%BBtTlDRU4&?V4<`VOQ{d)>EX3*IZ+f#?Wb2Q z==pk;=l?lG?|VO?fCW5Yv2-rL;=}?Md&(s)a6!7UwAP1+KjJqtyRVau3qRo3KoZEh zi38#|?OCz5xi<}wNGxxT*mNpe_c~e%l~6JH-)7{P=jaDmRiC-FX0D)Q$G4Btp#Je} z#K{W#M09{Dq`%EU77E~hzTdQlzWZYC9EQlQ2a>Gn*Qqm1653MnoJ;ZxzZUCioSH}k z{)tn6))2~t!c!o)R+m-ead`^VIN@V*%=KK<)xrPQUT1kZNmmzC5DL5AW&WmL=qelNDvH54a3CcT*M#ayU>rgU{{K0@ z)Si}@Du0swZy_>fpgyef!J8>HPZSf@_NBaa|Y zJb^s-9P-UKgkF6JKm9=j6S08^9ufip!N6dkIfUn%h!yph-zp2SgM1`-Z2p805YQAQ z9iIO=F%tyGzc%PVfCeZ<{$bVX*v~p0IDDt0LH2OsJQj=q$H_{Ti1t_O|Eve-Gpi(H10>^1UYIkbXRxb{h?nZpxlq+qlhJ|E5hdGW6sXt) zD$W6=`Z~G&@XNFIEGI0)Y)r*?jKt7PEobU!IMt{0l!X3yC*yb{_G39lXd#C!p8Xi`8c%V*@6EluXEm)Xd-#~`;O^e7YuMEgS-SIgCMIJnI-QPf-O26F zS8U~u-=fXg;JVkk$!l2k#;tHUi*C$DfE23Czj)hvc;3&;d2Eq1H|P3Xgsq;QqqBc@ z&StE~N-V})OwTlNM$hnRo91Z@wY5{;JDMq&EPx+Qq~?}5x>25wM?Tb$gA9XU5jZIL zkI~1lz-Q-e&wB{C?R~ZLo$PRX+up|3wmfJyBkBXJ!wP8;Ut*3uz4j8HT7!pnR8nar z<&}{)dzOq5TF4=uf_L!nW7aZ@Dj3BO&aDCcZI2&w(lST_1%~VzaBf_8UxA#6Sc)lT z4`_>642L}PsDBR`CFBg^NYO9K>cYAl$o2V?(zm6f_~I`dZN8&aEP%1?tIbplWRzbI zEHc24P%7+SSvmtHLb%`y+KkcIKF7*d-26b>kqNZ12E?U#vO!l<*O0EYSDFAt$j&gF zox{=(5M^ygRJh_R=)ZpHhbse@jsO=S4u-<~Up{6;S^NOVg;lJwXH4MHRc3za*0Ompl8FS?>*^YvYvDq`*Q~*$bKmNGjQnCQZg*Ms{A;Jwe zp7UHCv$oU$kQYvHK?fadqJs|CG*ClLnGaA^j&zZ)5=ONphq6_>CU#A1J5nreY& zmEZv{lv;t;7hi)rg5&amKj*(iLcmyXa~&MG$!ln!+1eMCI3txB?w7anOShxAn}7<- zoSROunMF613lplZv(s(C?)AiisYTN5L*(29g`HGOL0=TX`ekT3 z(?wr2nX|4s1P^n0(SypVn5(4E1{`YsoF{P1FBWiw$>^>kd4?KZq{b`5Wp_bOyvwrjOT zVBz0nR)r63i*_6-P$vbW7NL8jRtcuyG{Y>b8hBTU5tId*3gHVArUWC#Xk;k^$r_Wr z*j%3CP>8@4C7P|+klKrv#zEFw6b8%XCSMtq3RME15If{8v4vVUZcT(aLVYw`u^F3f zwZk=;DNt-m@wlga9=y@{NWfP@eyNDR08D|YSda)vlvtvTy#-z+!ID8zDKe63mfJ9% zY4~Q2Tpal*tV|{23cs7rKVJ-Su2J%Suxl&Xh<50DrLB^gI@Aj4|Gk%lHQtOp<)nr+); zavIoSj|2U{$Z>>%X{WV^{kR3B3Jn-D>i8US!@cfcD_h*u>dGovS?hhs!ZAiayy|)P zy4lq(b-oiFYP0p`nXEziV};8!3JZoFIQQ1=Sm6Oz@&7_R@Y!?WO2J2*6cws9Xws?6 zNTZE0);Qx$V9;%=ZMNHCr(O2g>wrTJJL0I*&N%Cw^DemVhL>J>wl0E?Gs z0A5`L^8wBP+2}nqc3(|cfQ`N%zWBQi<%F%XZ(&yZ%|w8q%V#vMlEtV-V%1v|>pIVD z6J5b?2Z5)7(*hOVXy*0`98V=`!WJ0uX(Oo5lw@J*+I6_v*G7j!mvMP)Fzl;t?-*Cx z1eXnCf;|*=7b>0@E5o|5H6$1ZM5~}BW{y!S%_IguRPXWa?B4-D0KYPb!{c}w&)`GS zz@vC9{mF{;a@DLS!rOEX$_w%ec{#e@@hg_392&Y;!MGTYxr)U=!$IR*q;ECC<9LET zZGB)SK1E`4vWsk5kT*Dq-YHF${mcH3f zZ@TWrp7SNz23DViXI6FQS$eqhYIt_I=6XtE`23Av)R^2mDM?&@uO3dkm#&^M0FU!cvv!=h;|xjKa(yU-9hwI(;=v{Rh^r1x7z8 zFD-!K5A;P<&~_pXL`TXsgrDkAGROduKZ1Zch3fmvDOsU0w9mPz&; z_zi*vWCzTvh;?x<0EA{vG%L37F6iuO(_oj>OblHIEp-!jW#LZmIObbu#o1FC9O&xV zs|X;>B&|b5;12*PlmNHA9JX|Wl=%idoitf*;>9p@u6mhd&ID^tQV0p$o`iENV=4&( zM9@=1;>uggSYZdH-3}Qxg1ucgEg7{JI!ITP1z0v+aqL7DHN$o<1BZa6wEtD-Zo^Xi zzVAYQx+PG#L*)ZNHv+^T?FaM@P`pAmXy4D-9X|WJtN{V1Tu7?g+y+@>5#qZ-OXC4E z*`OiuDft`fe-wGV8UM6(vb)_MM9>H}VgEp*UQ%xT?`!zAnGH+~~ZtnKse|LOmn{!xE>|Mmg=_vtsw9xr`t z0N|S!kH^JqV4i>r2f;;S33 zIqH~QUg~y$!5$lJ^4?qTY;zrY+A4&*V1o%$vX=#_4cmvqg@PL;cOHEB^5ZYiNqe30 z*^vNDf3dLDSn-mjNEKO6y0uPwp`cc%{FAGr*Pv08W^Imp;DnFX+3JyJ9((SgTmJjR za2I`Y(4ElXo)4B;9t2<9v?B~yYJ^KxS!uQH7%&Yj5U}IOK|fC9 zUZZ$Z@#H0dt7>6_g@_O;T%;ID5~w8_C_|?H;^Zk-piqe-Z)nx4QL9dia;=<|1~P_k zemn1i)6P2Q3VmI`6b=*ax$ z@*e$rz#WYN2Wh2UYRabT|fq03em1D$$e=XQCh`OgYa3jr#`b z#WfH^fe2QsWQmc&O*k37{_-C3A&rvXC!u1YD!CWxUDWG#>d3=H+Hi3oerd~KRzG=f zZ-Ac*IzrmKRAOOu%W7~at{b8%dQD_z)ms=}jRtSAtY%YlI7qjJOVsIpE|PuGMY|I#8+YZ*EBSnV)yj`Wxrl2_GoPF) z)uGKyjIj!CIklT6DuA(Ov9U#}btKzqSd<;L9dF8=adw$HxR0?sO^Jd_Nlx>c=&UmU zp!+W13I)hZ_@ZtN1%}&DcY`oCB+aHE(G&n8{<@?RUb6U`xhmvJ_`+5W#v1J!BTlaH z3xZO{OJ$VsaahZMXNg`lQ9iS11gc|4In14Npitu!SOD?JY3=vSQ(tT(WY;vG2PMGk zu#6rXZ*J@?)II6#3eEQvpztx}0jRI=w!Mgz|_0@O8 z2*l*-iA1iE#?Fp#-zWeSnq9;kByt-0cpVhqyPTlebVUSa;;DS*Z3w97OURQgF99Cl zaYpy3TuE57ZjmjFYu{lwOzgFrJWH=I79znofYfWffJlqfZg(_Ph?>p7y*Ym(JG2ZN zd{4GvzlSw%k9r#ET@<=$aKW^ix!<)yne^3-ZC;!KNI=?K)ao&#^3O{1$v~QHF)F;m zXR3JCJovkngFiCB{)?V&Vf+oaHBl>=DGxPn1<3E7fU%r=jD-|NTuIv8g1;EnIz3Eb zFjYA~uJ#z|LcgfBNnu0&Ep4{Jo<5~!*-nq%{BMHA&$Z3TxBVZM%gHhaWb!CvFX`Zx zJ41V&@k*S{G<^DK3}qSQip4Qzb=hz8VM*tu#B5~cGgIXqj>%U7T^Oyrw^ljo3uaro zU=ibtPIJFSbK3)2zP-8&pS{$n4PUShQ?E`@z%bU}oG!U)FEC-5Odu7eEN{|(KP>jz z*aUr-T=k~etXz|Ix5oazu`BW(6}dueRgBCX1UiF4o3#0y_!1aCO=77#AQ8Js^LN^b zbgZl15$e=F3O*T&`SS5R6CgEHuf;j@IwKksTKjiqv+fqeG)`RkNUoiYc;Oewco`EO zWriDNNEYn&$|cGJ&mL8vG3Z?c60L~z$bkumHLECb_M=+Y@@T2gNU$xM%bADNmz&z$ zl-(tgr!P%M0AD~SDu@B}amIW%96Kmo3^25x-s_9CrkO3__KNv!JTs6rdEA(_83hxW zlT_*SPR^1A)jB4?@%q|=xwcxfu@vFjXFpSimMV9$8tS^tJ2tJOD{1=;guU4DKe-!9 zkGT@EmwZfeqt3YC5%0}Hzu`!Vr8E-OMhn1DR7_|JF)+G?=m%>!7D+uT!wck{QZZx_ z9CW>gvYI=Ic>}I0D?XI4C^H*EhDolbOxkWvwfEQ#Fo*mm^f5`fYE7}5l^P{c*_)Fe zzeFRGDTd)ZMC>~CLS=ZN1sD#=EGnNBxa317p)7=>K9&NYH#4XtaIlshRd11fb=s53 zn`w!)*A1Gb8*GNZuD^>?+mg#lJY_q%cf}R1OfWw)Oz1G-Y*ulp%KB?Fe zvhTeml2iRg#ivh@B1JZeTgo>1%x-s(X@#=+8Le5EJ03JDkl~{F6r%$bKQGMp?qrPv z%XtTOCdE?YB8Kh+hNT5SnI6OG91l2|!*(I_RF(lf-qKT#a1#r&NEbFGA`a>8zZy8} z%GeBN(9`zcu0B7_1X8!s$QAm-i^dp#OSfWSEEVFbo_t6cY~Tvz;}0G0EPAYH{#L`< zII%V>D^7_d5uOw!VuzPl%UekJE|_=^@&Aq<0f2EFWu%%lRvI3&N}SB_d3YFWszc8+ z0ZQhcLWOE6q^(~7LJri4;7bn zwz?x8h!ZOp+Yo~Sd34^-@5bHGUG{){(Z0}|`-O-~EyHG@v=7VZ47MCQs(2^j=L%(& zz4Dkg(azwR*h~En@GXk_LzhI@(6J|b!K{iuHEN8ZewH~s-vT00rSym+$!XZCf@@Db z@H%l%82Pj+v7o|3ct)NFr4vGuZx0xU(f~C9U@#F6(ci@5a4F8{75%f&G0^V%-KIOL z75LF;I%BMRVio@1PPnv2?m-W3#pIqd)0YfH^O|UcN2oCkH-y$ z7RP(@4BlLE)~XP+Oql3Jzli{4PVF&4Dzq6rEq&k?7VEe&;}el?jpiIfS(#=staf5` zqP8TDHKl6~J#uT94s+wBFP~!GZ{4AmvQZwq|mb|AUkP>P1>d9c-FZ8?R$K+}T<*#TluQ@BXK7Dkd*t|F|X4ygM zmYxQYRlS@f+vKt5oP&uu`=T}V7aBgVGjUwKYvrtOy}rj_3E>FYi+#yXDzSHD>o6R# zOh(kR$ecolj3UWQa8{4ShV_<#iW}5Wdorw%k2C^3aYk=Ps^iQ&q~=zVLEVI-HkJ?R zEEi?$$FJFhV_9(hZROJC_CNG=qnbs31?O&u{WHF{$Ql!d1rasqC+9>O%?W2`BEy3> zxP3Hs{BY5y8GwQ{*4X#Z&S%-UQN6K6{g{$Yl<1ohrZ_munFWUDtMBKC_c9|S6>c5s z9cmS{bkE)%RgmVe%#HKMz9SyL54P^95|8HYjOikfhl-uOll<~KY#Q`{jwdsvcXXY3 z>Shg}=WO&%FDrGM1|!cn?3IpxpRj`4X7rF<;zEuyGX8n*@5=n$?2_I&7fCK8SETSd zzZ=M!^97d1iP3I04W!kJief)N)6OSXM+oWIS&>+Fxn6i! zm!Jq!jHrHf`(m!c#(yTZoT{Oo+oVnR_jAMVDV-hH!YS%Y-H&m}?Lu2=%AB@l7kx79 zHmlij>wIhOC~#tzxhe=WyU%k7V0P&5eND`dyF%q9BFc7#ZaZRb(3~K%>b80&b3mr# zG8g!w+xaLzmr5epJOmgnh+Y_F$%&eF8k^_#X)Xyas@!Q%(i+}>|IDA4)81aaY*!2_5cg;!E4eRnuu-qa#q$^Fe#^e}C92S>3DC+Trk zhmDs#>7}fiR4{T2XO6&7Mu9alOZuN#=DMWv?0%1(CgT#vx^s{**?5YHbsQHenjh==JR=EmqunUy(?>+JSVY7Qsuv{7wmHlnm@ACFbYuIy zSusyox9!K7Rh`pkqyUwIZHg{Pq#jq(zvt&$8Ea3k1}m}|YsnP25_)%&Tj}L4_gz`@ z=Oz>7>H5x@n{_N%{m(~Ai-^y_`;FL)19rH%}$6x zP(4Z#%m%W#cH-@0P!ws&leUDV&qiAdt0CjB4fa}FlSL&80rVP7)JIBOGj0NmJ9=)T z5f{d7(9@Oya8QV`Enc{EX_^QS74ZkzaI3OKGtI^sx?*~qBTZvab11==bNzld7<>WO zn;>yyt6ffx+wRyI><|^x9`aTZEPrZPL8TkBF|7IApOXWj9`;FghNb*P-5?^!k!iE7 z_<9W?R5Gk4eq?RRxQV1}(KU-03iDjlW!;$`88~T0=OI9JzqX-WWN&&SAuS(lR@seb z%FyT!^_6h4AVpi*FmyC2l9slm#J?h5Hf~{Vgxx&v^pxIs$;k;_-nNIkmN4s*fR}L> zf#P%SCqi%T77qmSZcQcX8ALQm{IX2&h`O$bY0(i!D#DBFkRvUaaO4ft7_pAZD0&CY z`TVljFoS##!fv;4N?$#hOpe_k5`DXf);AAh=jenj1x#&dL0H(%&Z2MnHgEHj0o1%} zf0ZAG*qczA>}~$EFah&%`5*DYNVbN>JTmp_>)tNoR0eY@uyyiMUUK;_Q+Q40bH5C@ zE_g1)`T&CGAkbXSRtop{@c-wmz~y{^5wHRexxC){<;fT|Ns)w_4BK#O*0t{^=lbS5 z8^cH;MrcSgDI_N>k%HL+rQ?c=G%9zo)NbKt#`xvhpv5lFb*VCAB=1NeX{UF`&n@4c z+p+t5?iI1(LlW9K|I)@mbymE}L;%CazWLIj*}l#I*O(_5mP)64%Uql1j4ONI&n(ewyp2F&oXujI;O9_}ikO{sKpK++|muurw&&hU-6Em}29azL^Z@A1kPyxwUe4^pr zcg=$Z!{7?m(1No~73GOD0|8=<@f`?i3Sr07Ul{lwM0m)wMO}Pby*0JLZcCj68ErgI zWZlxw7)_*rT)g{9CV3f=?Yh-KfkgwjolIkL2o7`2*Ila+a&zYE7YafbqzN{Z>Pz5csAQXGC3R}d9C)8 zanV=j4}7&~P_^bya+oBBNeAHo;|B11xQ7&y^p9&63~#xOKPP@o$+%hH{bS^L;Q`L( z1mb6wly{r_{w`BvECMLHNcYS${f>l&acQZk)MS|GbQmtIzcx_Iga2kR2PNA(yb;K+ zPnS%cucVvSEjLo_q>v;8B%^fhIM?PfuXK8_v?;;Mz%;V@FAx6weFuO3Zm6i@`Lm&o z8i!~Q$usKK&G8_S zqa&TOoTU9-9H}8mHM#eFI53}FhspX}cTblmC#Tz!HXyn6q>h8x$$FjK>yC|6_+nLF zT|Idb`1TQ2fT=mk*Sm$ca$q3ai-jQYf#czhv0wiZ z+&Y4rw9C7n#pyxLcljJbmPZ{zUm)nilT2IbcdyJKd=Rs7JM(ETx6bZ3EBAMjk z6<=smT0V%?1TTQ<5OV zD(Y5sOdT~wsv}vVm)v173BN-l@;ZE5bEs&!sz8wBO)|)X8<6Mm5w?>kL*v$gT|Avo!{S=-AJmS}C#6ekUtqE$h zlC>XsKusm(VZZQ@saTBnpH*w;Md;lMi9cQ|^*a?RB4Nk1Y?S;OC)!K!gv@E))b}!S z?A@(y@a7Q-vFis7Q9G;W-5{p9r+OS+!KAoyr!QBgFlEMT3R>R=urj{43r|;_N0GVg z=vOPor8)CewP>4ltYhXBhr3O>OXyT6g^pcP$uXfrsSrAk`M2h>4Ti$pX6#85l*861 z)Yv@{2Z4nq#{+t&w~U_7t!Q#Dxe9It$bE$N?m>yuFf9=xw)aK;C(9I1MOouj{9k-A zY42+RJWB2&VvjTWhW&xsSLmn*X;3H<`JMDBCGeK_OI zg=X-L<-yui@m3vUCn<7#Dm8H*i$qTpv{^jpP`)+4Ve$Jt5}_E2i8m{e(1nk03u9R% zy!%i|?stbo1UbW)b|#pgLr_d*1%i9Sdv+pju@cy#(K1M;0_YWvr~JVbPQ$Aax?dL8 zbhx5BdSMNSjmYtKce;W>Vy!$UgIjXX+?K76^E+j7IWjz!UU$h3Cc@}hCUR!|-3J)* z8;g1Nb@CqOQCPfY`9}Myc0v4-zxZ^!#Rsi(pgN}{Ff2<#?x?AjImTczIYDHdzYEpgx*l1ctxqzdj7@=eYON?lMZ3X|>? zeUW7{6r|b`D#n>o5*^eZojh|J|AzSKFRj#(tG)A!VC0vhOB(WV@l-Xj{O;e}eI&D~ zi=+rbLyjk(6U2QIxTAi@|5Mn%`1@U))lH5h_c6A{Mn)yl8Fd)vkccHcc`g1RDFkOs zq&jzfOIK4aA?2JEW7#2)&{?seH#rLbg9Bg4ZTHM9dDG*FmY2nwaIMp~ z1E^7MV*2KD2$73%lWub)Z8(NBqR4{{d(0K>8{#w=4p?J{SeDT^c3XGqp*v!t6>5h4HamJZihUIGig^V=cS3?2 z#6sH0bUY}NkDy4aAoeXt7OP}#Z=71?iEC_pw9Mm?--mnUnEQB@K+flp0zS^;#{nI! zptU4rTm8h8t-&Z@2E!-TO9meKZSuo{`mAh0-C?=xav7eTC2s0v@Jyy7-(K-1++^W; zf_k)k!n(PU+?aU09h|6U_?4AM-XjR+jh{U7T)wVP3s1>>eW)!0wW3fHSa8LL@Ezrb zgi^c7IJgvcm>4I0AmgIPK4xOeQO5SqzuNQR8*-1Vj{kt9U*Guk zzODT=baR14U7F~(O>9fbl6auaV}7G!;b0WOB(FLTJPVdrU;PeN8G$}9&+6=pAxCaX z90Vf*bspU+P+IheoUS`Qar^% zIO;5xT6@6J)M)`6nz2A>NvFyTLdFD1oui^QGk|;>iFqjWKr3OF&X031c>nyo&*Nf) zqGysktu?i-HpM!L(p(dYu4*Vsb5$g~rUA!jgq2{7eJ%O4quj@mr6hgasMNAYU}ia* z)pAdmlsdjH(Pksoa!OK5?O&YjTKM=zY+_PLd4eUSG$A}vi3;N7lR4~#yE&YX7qa6j zW%A0>PqnsWIrmO7MJ3Z(w(QzZoK3WnHfpI%XDG|39%Oxk#_KCaMYlnnkqQdPu@RH^>r z^Vlj?BseDBi2Pc!xH2pp&59ZCd2zsI-eX(s-s3L7DSHAIYz=1a+;Vb5?Gai@4#mSzdzoOx`g8uzq ztP4j6Fb=X1EDC2uMwPLl7-aOJs)`dRF2|W?IM4zjf^(h|vr@*T_rp~&%o=#DJZhfd z9UYdOR-0JrL+;_M zy9j!gg`Wc}fvBH3+AM7g-PH%@!*DLVnJgtoE2WcOYTCgNH`d0lq{uZzqf_Uvr>OWd z63M=JO7z%6PhG{ODy@9UFtmmNt%i_k5ThCXi1ix_`H6+TMOc4>v61Qy6j^j^-r4S_ zj#XK;qM(w=SlD?G0tIw!z%wEy8KtjST7+Sc4Esz5Er9!s3z0?t_!b3x=x z6g$4==tD-N6&f|1yXG0hUm{h=z|47-R~q%}ctLM^;xng{`*i2t7D_lxJRgr&tY$Y! zVsGD^lsRdIk7%!%WRfx)UZWOi7Ph~~2w{Y0jEomp)w|bpFaPMUm!d)`FLkA(p?l3Pt40t%GCYF;M>AkvmEHF@ z^j_HA(C@_)fvx0Gb7i_{C(OPYfHetsCG5nWVd$^e?IiZ;P8_rH5ptuC>fZO=$qsVo zLS{5-3{jCbsfP~9vA2{vWD|HairzCRRiYS$$stD5q(|#9QFt+Ee6t@Dr^qiOO19R4 z#<$KrASE>mqIAQ|Wm{OEBFLw#ztNk8e8K{v9nrf2y4raOTM7$_HO8L1qVd%+m~}~@ zQT(36A?$&aPcsPS5U}phdf}OU{;vL}AEseN?+WT_+A`FG@L~p8CqhSSy^IV4FQ6Gw zC2eY55ek2TAfLdaW>z;zMpTKSdfcM=@Ao2f$>aAXpQc9CBqv$B3yL;QGY=^6fr8uM4tp=xltlX#9h%2py1qnKZ&7445k@?(>Y*Aw% z_AlEen3paMpyRY|TV1<3r?fw@dy;EYm657;np#vR zo8@fD`C--UA?M_&y?GhS3Q|PvuJUrTq?r8pbx~@1gkukv7p8!me5o6tG|%J|YekYy znBz{XO3!jJepB+UJH$yEzuf2+7L4WF2=az`v2JSK*mfg4Ix5WJNolREnD9;P7NsQK zkXL4KxEfUw&>9U7;4y61*m+Xba{+(vT=D&&KizG&I8Ggd=vClvdMHtLr*KtDe8~wh zyzc4K;Z;4mwtZU}fqfzTX>EaTyj)f644B4ERTkW0Rx_)J%OHjomOa%`A3Da%vzQ>d z!kVje#uY(9f#=LnC@TsJUw8i=J4?X~>$^n9e$Qn6nF*!mS?Mz#Kh6EQM(T<)$}^nt z8LapZiq(JBV(7z{qpSOD91(l+SQze|cH2?L99y88G?Aok6HG`N*2VurT8BkYU@px+ z%T!)&n&l6eW|o)FGWpS(nE`*Vo!xytbB!V+Ls4V4D5@LdTC=$tM=Pr>=3YC$!2%v* zmCicBk!&ulOtzSgC6?^(dv*25kr?q-lQ6b$uORx!m8-A&E-b*i-d|a`p}P^czo&FRj@K4qc6h@A!Y)j_XE091|&-ecwNtYDLixAsk)O+*WtxnVESoBh3Y__LW+LCYf}R-E>2%$> zbNZR1-7%DrdL=L-=3;s@>U6vAo<9A%%cVyTu<{-DXQ%BNyF*_JYc2;zFdhZN`EK{` z`rYd4Ttpf2b-DsWf-XNYDB`~P2?kw(yS@>-{0hZhUB=MYukMYnUW4~#GwstJSF`@- zElg>3cBsY|8|M|L$Il!yzRTd3`r+Y!8*YJo0JBw?jkWIV`&UcUg(>iX$H1zmZ40ZMJFAMrWYrx^><_+43KC|rZS0vT z+tDF5c>ukS4Pd+3HZVDr$RneUOzZ>OHn6GAt(!*FABiIJ$*A@9?}tw753gSjCT@wG zOj(e%rt8vw`=#9Pe|*5b{>xl={c7T(Nt`1}+LOCWlGbzYf}v2YPcK`koa9s6)-%UH zGb>KMW}q+R?_@K%k9mS4947D5&&Uv*D_qfG?v$XZP`HBw8&OL1{!{d2@Oe_$w>cFC zLJ^H6L5w9E@&mUSF%=-MAbdtNtvIP_`7S1>FO)WSQ6Tq!E#S^`};a9;Sv8b;JaK5FVArpl8q{X4Q2*4X3B@>XPSHT2G(p1G+5& zfx7b~-*a=Gx-R0EW=8a2dacK}W5sdhNtm{0WOzeylKLd;^!C6@?fApW9WsF$Xt8%oE z`vzrD5A%(}%?t@he{3gdvp{0wS`#g^^J^ACmxf`EFk;hWiDE>znym;+t* z4rYQL?4JdbgFMg>Zjb>t!Dp=gduNy>G$PL|p*E?fLXt;?WGGC>)q)+2#SQZR3uWvm z<{r?`x^2o#@=>Z~>y3=J?0KTF>>ZE;)Sj%P_BhRfE0EdLgGi_cSx!C3Wa>e>E&urB zUS7})`aD~EHt@T4aITtfyCs8rjRX7tVVeKvN)jFXYorygOm39N1B2=lN~dpY`Gp(i zlYXq8Dt7A|__W*i7o3Q1_ejIN?*VfVhprlG8hO)5uK(wHE8iP=ZRH0i!+rVI0pgH8 zneF{^9SFwf6HBi}Zkl?x=1JsWtt(uKbga-U=QA#mfFw^$&JKVK(k457>MIA{ll8$m zO({duLp`yV*ENumGZDnHhDq3j$yk6XSc<7wjA>Yo=~#goSY^#@O`oMfucYVeF2PYd z8Mzft0gOp`3ow#n+!W{F)GY2lWyu7(02}Rr3}_o1MAeWMr)J0NR8eThR;5-PxQK44 z++Cy2;yS74zvPkgE^n@00IZ5DdiG_vTcVf$Qt*neZLb^wuEEt+BFef4_OA~I(L0hp z@}2x3Kc!z%64%P3eICqw_ChGbERGL@^0v0a=dVF79}PbS->_x_@h^u#)AGB?C6MlEKk5)_KeOo!+L$+gc#N>*o5|5Lc9Xei{JEFnEv*C<)@t2E zxKupR;Z#>r(=@}&HI*QpK^qw~i9uSRk?`y(6% zfyaA^01ypNq1y%gTj)N4=u}$cKLJ^P#9QdXj&fF;hRX$+@K|bRm7wA*XdI109H(N* z5OXP8!gWq8LHd?n#61ks-&?;33G#s+cdGOvBSMgl=AEmFO?>!{LZy(;=@hECOzt2- zv1)EFRJ^uy1ot@fG$2j2bsR-9Ot2!Vq?yGuq-h#hjNjUjj!lq48WfUSv7y`i>x^%5m>O20W~> z&dOQye1~_YiV>h{kxFJ9#?{9nJdRm~z01`H73C)DWY4Eeu?(}Kgt z9BuPLwFl14?3z}a8z9mY)SU(efm5gt^OXgmhR)b+OXsQrgK{$@{o_(l=5pDH(KqWJ z9{B&E%txTiF?$SZFkUgfGV;-x3fUo8BwgEwkn9zV!kc%nCPP)s|GktzE>ylH zJP$EzFJv|%OlrAkaCDFZtQ~opA&;?|ddPzUklK}Ql@2AP98To=?pz5}=3>+&d`mzz z2q%RRRvD^FdWiKzE|l4ju_|eZR1lP%P8aT(p!FSxB6Bz_y<`#f4CP=nL8vj45$F>* z04jJNGXEtYFtR|zMCv!D$ao|~U_qn7vVB$_a?LnwGZDM)z@dp)hgDnYTn&^7EaPp9Ag-g1vZ{YUv}Dp#aZy#hML64jDch(#+^H#uGR)_uq;;NsCHSEtuW+!Sx+i9x5L$AW(MSV70W0qVW_qAv10C$4NX8x-3}r&XVSwu#m+ zk68t9%9C3mRwKJHvtS2{HdSt=a>{56=&EiXD(bR zTC*>@M3E5g*tM5Xx$KHk*G#j?W}ZIq;!PwgzV3!HH_5m(@Lv2*uqfp!tvHQVTPpgC zietSMr{=%sYsJ&P_$_AaiJB7%jgk_E7$mdbt3|q2?K-qsW{52^e3Yrb0md09%TU9O zFw7v?2Ge+NuN=A`vSlY0Sxib+Q>kQ?mSl-QT>9T14vBndSM401FFmNAUXT5{} zPjnpi*v1-dg7GHm3PqmRMmm5QPK+`;&Io++*={y$S(~B2O5st!3RhLC)u=UIoq7!> zXw<}HktVtAfjjPc7(bIt)~v-8Q%%#VP5Tquw>oW;mfI4iu}-Ts*tB|WJgrX6_w3Ef z35;&Z>GYEq-tMQ1P=Fns`2|6`xscRi` z@3gvh6x>rX+&@oo?`^DWRMv;>Mgf=r5DL&opo2gla0~*W0$l_Ezca73C&fu!pMInx zCrjJ30IA3896KtOXIAWyh=1$@IBa?BCb%Q7x{Tu-CBer+&v=DP<#q81PgCOi^xDK- zO|%OQ%cI4U%4ua4EzQ2#ZtB!;vC*3A8Y;aa1HHDXY2qG!&{Bx1Y-*P7^K?5plbY8j zL_G}O&SC^g5g$jO4vXkK!|0Kx`w%x+%=aLD5$I(+za_7;JK_cQ-6Q@R^>ye{0000V C8#IRi literal 0 HcmV?d00001 diff --git a/blocks/alphabet_strip.ninjatpl b/blocks/alphabet_strip.ninjatpl index 02a95bb..c79355f 100644 --- a/blocks/alphabet_strip.ninjatpl +++ b/blocks/alphabet_strip.ninjatpl @@ -1 +1 @@ - + diff --git a/blocks/alphabet_strip.schema.json b/blocks/alphabet_strip.schema.json index 84b57b1..0e86f4c 100644 --- a/blocks/alphabet_strip.schema.json +++ b/blocks/alphabet_strip.schema.json @@ -5,11 +5,22 @@ "type": "object", "properties": { "letters": { - "type": "string", + "type": "array", "title": "Letters", - "description": "Sequence of letters to render. Each character becomes one tile.", - "default": "ABCDEFGHIJ", - "x-editor": "text" + "description": "Ordered list of letter tiles.", + "default": [], + "x-editor": "array", + "items": { + "type": "object", + "properties": { + "letter": { + "type": "string", + "title": "Letter", + "description": "A single letter or character for this tile.", + "x-editor": "text" + } + } + } }, "colorMode": { "type": "string", diff --git a/blocks/art_wall.ninjatpl b/blocks/art_wall.ninjatpl new file mode 100644 index 0000000..2a99f71 --- /dev/null +++ b/blocks/art_wall.ninjatpl @@ -0,0 +1 @@ +
{% set showNames = true %}{% if showChildName == false %}{% set showNames = false %}{% endif %}{% if title %}

{{ title }}

{% endif %}{% if not items %}
No artworks yet — pin a few pieces to fill the wall.
{% else %}{% endif %}
diff --git a/blocks/art_wall.schema.json b/blocks/art_wall.schema.json new file mode 100644 index 0000000..2c76c8e --- /dev/null +++ b/blocks/art_wall.schema.json @@ -0,0 +1,51 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Art Wall", + "description": "Polaroid-style wall of children's artwork with artist names.", + "type": "object", + "properties": { + "title": { + "type": "string", + "title": "Title", + "description": "Optional heading above the wall (e.g. 'Our art wall').", + "x-editor": "text" + }, + "showChildName": { + "type": "boolean", + "title": "Show artist name", + "description": "Show each child's name and age under their artwork.", + "default": true, + "x-editor": "select" + }, + "items": { + "type": "array", + "title": "Artworks", + "description": "Each piece of artwork.", + "default": [], + "x-editor": "array", + "items": { + "type": "object", + "properties": { + "image": { + "type": "string", + "title": "Artwork image", + "description": "The scanned or photographed artwork.", + "x-editor": "media" + }, + "childName": { + "type": "string", + "title": "Artist name", + "description": "First name of the child who made it.", + "x-editor": "text" + }, + "age": { + "type": "string", + "title": "Age", + "description": "The child's age (optional).", + "x-editor": "text" + } + } + } + } + } +} diff --git a/blocks/big_cta.ninjatpl b/blocks/big_cta.ninjatpl deleted file mode 100644 index 7150b32..0000000 --- a/blocks/big_cta.ninjatpl +++ /dev/null @@ -1 +0,0 @@ -
{% if label %}{{ label }}{% else %}Add a label and link to publish this call-to-action.{% endif %}
diff --git a/blocks/big_cta.schema.json b/blocks/big_cta.schema.json deleted file mode 100644 index b8d11e6..0000000 --- a/blocks/big_cta.schema.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "Big CTA", - "description": "Oversized pill button with sticker drop-shadow and crayon underline.", - "type": "object", - "properties": { - "label": { - "type": "string", - "title": "Label", - "description": "Button copy (e.g. 'Enrol today').", - "x-editor": "text" - }, - "href": { - "type": "string", - "title": "Link", - "description": "Where the button points.", - "x-editor": "link" - }, - "colorVariant": { - "type": "string", - "title": "Color Variant", - "description": "Which primary colorway the pill uses.", - "x-editor": "select", - "enum": ["red", "blue", "yellow", "green"], - "default": "yellow" - } - } -} diff --git a/blocks/blocks.yaml b/blocks/blocks.yaml index a070299..ede4ee6 100644 --- a/blocks/blocks.yaml +++ b/blocks/blocks.yaml @@ -1,54 +1,34 @@ -# Kindergarten theme block definitions (codeless .bnp, WO-WZ-021). +# Kindergarten theme persona block definitions (codeless .bnp, WO-TF-019). # Keys are fully qualified (kindergarten:) because the codeless loader # registers definitions verbatim — there is no PluginBlockRegistry prefixing # in the codeless path (unlike the wasm/templ path). The qualified keys match -# the hardcoded data-block attributes and the master_pages.json references. +# the data-block attributes and the master_pages.json / seed references. +# +# Builtin-duplicating blocks (hero, cta, footer, gallery, stats, quote) are NOT +# theme blocks — they are template overrides in templates/overrides/kindergarten/. +# Only true preschool furniture lives here. blocks: - - key: kindergarten:mascot_hero - title: Mascot Hero - description: Big rounded panel with mascot SVG and primary-colored confetti. - category: content - schema: mascot_hero.schema.json - template: mascot_hero.ninjatpl - key: kindergarten:alphabet_strip title: Alphabet Strip - description: Decorative letter band; jumps to anchors when used as TOC. + description: A-Z learning band of colorful crayon letter tiles. category: navigation schema: alphabet_strip.schema.json template: alphabet_strip.ninjatpl - key: kindergarten:schedule title: Day Schedule - description: Daycare/classroom day plan with crayon time-pills. + description: Daily class plan with crayon time-pills and activity icons. category: content schema: schedule.schema.json template: schedule.ninjatpl - - key: kindergarten:gallery_of_art - title: Gallery of Art - description: Polaroid-style grid for kid artwork. + - key: kindergarten:art_wall + title: Art Wall + description: Polaroid wall of children's artwork with artist names. category: content - schema: gallery_of_art.schema.json - template: gallery_of_art.ninjatpl - - key: kindergarten:numbers_counter - title: Numbers Counter - description: Big circular numeral badges for stats (e.g. '12 teachers'). + schema: art_wall.schema.json + template: art_wall.ninjatpl + - key: kindergarten:story_time + title: Story Time + description: Picture-book read-aloud panel — cover, title, and a line to read together. category: content - schema: numbers_counter.schema.json - template: numbers_counter.ninjatpl - - key: kindergarten:storybook_quote - title: Storybook Quote - description: Picture-book page treatment for a quote with optional illustration. - category: content - schema: storybook_quote.schema.json - template: storybook_quote.ninjatpl - - key: kindergarten:big_cta - title: Big CTA - description: Oversized pill button with crayon underline. - category: content - schema: big_cta.schema.json - template: big_cta.ninjatpl - - key: kindergarten:footer - title: Friendly Footer - description: Mascot wave, signup, contact, crayon-rule divider. - category: layout - schema: footer.schema.json - template: footer.ninjatpl + schema: story_time.schema.json + template: story_time.ninjatpl diff --git a/blocks/footer.ninjatpl b/blocks/footer.ninjatpl deleted file mode 100644 index c0c2186..0000000 --- a/blocks/footer.ninjatpl +++ /dev/null @@ -1 +0,0 @@ - diff --git a/blocks/footer.schema.json b/blocks/footer.schema.json deleted file mode 100644 index 4bdefa2..0000000 --- a/blocks/footer.schema.json +++ /dev/null @@ -1,48 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "Friendly Footer", - "description": "Mascot wave, optional signup, contact, social links, and crayon-rule divider.", - "type": "object", - "properties": { - "showSignup": { - "type": "string", - "title": "Show Newsletter Signup", - "description": "Render the small newsletter signup form.", - "x-editor": "select", - "enum": ["true", "false"], - "default": "true" - }, - "mascotName": { - "type": "string", - "title": "Mascot Name", - "description": "Name of the mascot displayed in the footer wave (e.g. 'Pip').", - "default": "Pip", - "x-editor": "text" - }, - "socialLinks": { - "type": "array", - "title": "Social Links", - "description": "Friendly icon links for social platforms.", - "default": [], - "x-editor": "array", - "items": { - "type": "object", - "properties": { - "platform": { - "type": "string", - "title": "Platform", - "description": "Which platform this link points to.", - "x-editor": "select", - "enum": ["facebook", "instagram", "youtube", "tiktok", "email"] - }, - "href": { - "type": "string", - "title": "URL", - "description": "Link target.", - "x-editor": "link" - } - } - } - } - } -} diff --git a/blocks/gallery_of_art.ninjatpl b/blocks/gallery_of_art.ninjatpl deleted file mode 100644 index f304e23..0000000 --- a/blocks/gallery_of_art.ninjatpl +++ /dev/null @@ -1 +0,0 @@ -
{% set showNames = true %}{% if showChildName == false %}{% set showNames = false %}{% endif %}{% if title %}

{{ title }}

{% endif %}{% if not items %}
No artworks yet — add one to fill the gallery wall.
{% else %}{% endif %}
diff --git a/blocks/gallery_of_art.schema.json b/blocks/gallery_of_art.schema.json deleted file mode 100644 index 863e631..0000000 --- a/blocks/gallery_of_art.schema.json +++ /dev/null @@ -1,52 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "Gallery of Art", - "description": "Polaroid-style grid for kid artwork with privacy toggle for child names.", - "type": "object", - "properties": { - "title": { - "type": "string", - "title": "Title", - "description": "Optional heading above the gallery.", - "x-editor": "text" - }, - "showChildName": { - "type": "string", - "title": "Show Child Names", - "description": "Privacy toggle. When 'false', child names are omitted from the rendered gallery.", - "x-editor": "select", - "enum": ["true", "false"], - "default": "true" - }, - "items": { - "type": "array", - "title": "Artworks", - "description": "Collection of artworks. Each entry has an image, child name, and age.", - "default": [], - "x-editor": "collection", - "items": { - "type": "object", - "properties": { - "image": { - "type": "string", - "title": "Image", - "description": "Artwork image (media reference).", - "x-editor": "media" - }, - "childName": { - "type": "string", - "title": "Child Name", - "description": "Artist's first name. Hidden when 'Show Child Names' is off.", - "x-editor": "text" - }, - "age": { - "type": "integer", - "title": "Age", - "description": "Artist's age (years).", - "x-editor": "number" - } - } - } - } - } -} diff --git a/blocks/mascot_hero.ninjatpl b/blocks/mascot_hero.ninjatpl deleted file mode 100644 index d1a7de3..0000000 --- a/blocks/mascot_hero.ninjatpl +++ /dev/null @@ -1 +0,0 @@ -
{% if headline %}

{{ headline }}

{% else %}

Welcome!

{% endif %}{% if tagline %}

{{ tagline }}

{% endif %}{% if ctaLabel and ctaHref %}{% endif %}
{% if mascot == "blocks" %}ABC{% elif mascot == "star" %}{% elif mascot == "balloon" %}{% else %}{% endif %}
diff --git a/blocks/mascot_hero.schema.json b/blocks/mascot_hero.schema.json deleted file mode 100644 index 45eb3d2..0000000 --- a/blocks/mascot_hero.schema.json +++ /dev/null @@ -1,46 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "Mascot Hero", - "description": "Big rounded panel with mascot SVG, headline, tagline, and a primary-colored CTA.", - "type": "object", - "properties": { - "mascot": { - "type": "string", - "title": "Mascot", - "description": "Mascot character to render in the hero panel.", - "x-editor": "select", - "enum": ["pip", "blocks", "star", "balloon"], - "default": "pip" - }, - "headline": { - "type": "string", - "title": "Headline", - "description": "Big, hand-lettered headline.", - "x-editor": "text" - }, - "tagline": { - "type": "string", - "title": "Tagline", - "description": "Friendly one-liner under the headline.", - "x-editor": "text" - }, - "ctaLabel": { - "type": "string", - "title": "CTA Label", - "description": "Pill button label (e.g. 'Enrol today').", - "x-editor": "text" - }, - "ctaHref": { - "type": "string", - "title": "CTA Link", - "description": "Where the pill button points.", - "x-editor": "link" - }, - "bgColor": { - "type": "string", - "title": "Background Wash", - "description": "Optional HSL triple (e.g. '48 95% 58%') to tint the hero wash. Leave blank to use theme accent.", - "x-editor": "color" - } - } -} diff --git a/blocks/numbers_counter.ninjatpl b/blocks/numbers_counter.ninjatpl deleted file mode 100644 index c2037eb..0000000 --- a/blocks/numbers_counter.ninjatpl +++ /dev/null @@ -1 +0,0 @@ -
{% if not items %}
No numbers yet — add a few stats to celebrate.
{% else %}
{% for item in items %}
{{ item.number|integer }}{% if item.label %}{{ item.label }}{% endif %}
{% endfor %}
{% endif %}
diff --git a/blocks/numbers_counter.schema.json b/blocks/numbers_counter.schema.json deleted file mode 100644 index 10757a2..0000000 --- a/blocks/numbers_counter.schema.json +++ /dev/null @@ -1,32 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "Numbers Counter", - "description": "Big circular numeral badges for stats (e.g. '12 teachers').", - "type": "object", - "properties": { - "items": { - "type": "array", - "title": "Numbers", - "description": "Collection of (number, label) pairs.", - "default": [], - "x-editor": "collection", - "items": { - "type": "object", - "properties": { - "number": { - "type": "integer", - "title": "Number", - "description": "Numeral rendered as a circular badge.", - "x-editor": "number" - }, - "label": { - "type": "string", - "title": "Label", - "description": "Plain-language description (e.g. 'teachers').", - "x-editor": "text" - } - } - } - } - } -} diff --git a/blocks/story_time.ninjatpl b/blocks/story_time.ninjatpl new file mode 100644 index 0000000..436e4fb --- /dev/null +++ b/blocks/story_time.ninjatpl @@ -0,0 +1 @@ +
{% if cover %}Cover of {{ bookTitle|default:'the story' }}{% else %}{% endif %}
Story time{% if bookTitle %}

{{ bookTitle }}

{% endif %}{% if line %}

"{{ line }}"

{% else %}

Add a line from the story to read together.

{% endif %}{% if author %}
by {{ author }}
{% endif %}
diff --git a/blocks/story_time.schema.json b/blocks/story_time.schema.json new file mode 100644 index 0000000..9f2f7b8 --- /dev/null +++ b/blocks/story_time.schema.json @@ -0,0 +1,32 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Story Time", + "description": "Picture-book read-aloud panel — cover, book title, and a line to read together.", + "type": "object", + "properties": { + "bookTitle": { + "type": "string", + "title": "Book title", + "description": "The name of the book or story.", + "x-editor": "text" + }, + "author": { + "type": "string", + "title": "Author", + "description": "Who wrote it (optional).", + "x-editor": "text" + }, + "line": { + "type": "string", + "title": "A line to read", + "description": "A short line from the story to read together.", + "x-editor": "textarea" + }, + "cover": { + "type": "string", + "title": "Cover image", + "description": "The book cover or an illustration (optional — a friendly open book shows if empty).", + "x-editor": "media" + } + } +} diff --git a/blocks/storybook_quote.ninjatpl b/blocks/storybook_quote.ninjatpl deleted file mode 100644 index 998a266..0000000 --- a/blocks/storybook_quote.ninjatpl +++ /dev/null @@ -1 +0,0 @@ -
{% if quote %}

"{{ quote }}"

{% else %}

Add a quote to inspire your readers.

{% endif %}{% if author %}
— {{ author }}
{% endif %}
{% if illustration %}Illustration accompanying the quote{% else %}{% endif %}
diff --git a/blocks/storybook_quote.schema.json b/blocks/storybook_quote.schema.json deleted file mode 100644 index 4c9bac6..0000000 --- a/blocks/storybook_quote.schema.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "Storybook Quote", - "description": "Picture-book page treatment for a quote with optional illustration.", - "type": "object", - "properties": { - "quote": { - "type": "string", - "title": "Quote", - "description": "The quoted text.", - "x-editor": "textarea" - }, - "author": { - "type": "string", - "title": "Author", - "description": "Who said it.", - "x-editor": "text" - }, - "illustration": { - "type": "string", - "title": "Illustration", - "description": "Optional picture-book illustration.", - "x-editor": "media" - } - } -} diff --git a/fonts.json b/fonts.json index fe51488..f866d93 100644 --- a/fonts.json +++ b/fonts.json @@ -1 +1,18 @@ -[] +[ + { + "name": "Baloo 2", + "family": "Baloo 2", + "variants": [ + { "weight": "700", "style": "normal", "file": "fonts/baloo2-700.woff2" } + ] + }, + { + "name": "Quicksand", + "family": "Quicksand", + "variants": [ + { "weight": "400", "style": "normal", "file": "fonts/quicksand-400.woff2" }, + { "weight": "500", "style": "normal", "file": "fonts/quicksand-400.woff2" }, + { "weight": "700", "style": "normal", "file": "fonts/quicksand-700.woff2" } + ] + } +] diff --git a/manifest.yaml b/manifest.yaml index bc49032..6be2b51 100644 --- a/manifest.yaml +++ b/manifest.yaml @@ -1,41 +1,110 @@ -# Kindergarten theme — codeless .bnp manifest (WO-WZ-021 Phase B). +# Kindergarten theme — codeless .bnp manifest (WO-TF-019, Wave A). # Synthesized into manifest.pb by `ninja plugin build` (no plugin.wasm). theme_presets: presets.json bundled_fonts: fonts.json master_pages: master_pages.json +required_icon_packs: ["lucide", "simple-icons"] system_templates: - key: kindergarten title: Kindergarten - description: "Primary-colored, hand-lettered theme for schools, daycares, toy shops, museums, and kids' publishers." + description: "Preschool / daycare theme — crayon-drawn borders, sticker badges, confetti, and a bouncing-mascot canvas hero. Warm, safe, and playful, in a bright classroom by day and a quiet-time navy by night." page_templates: - system: kindergarten key: default title: Default - description: "Standard rounded layout with header, main, footer" + description: "Standard rounded page — dotted-paper background, header, main, footer" + slots: [header, main, footer] + - system: kindergarten + key: full-width + title: Full Width + description: "Edge-to-edge classroom / gallery showcase, no max-width" slots: [header, main, footer] - system: kindergarten key: landing title: Landing - description: "Mascot-led hero plus stacked feature sections" - slots: [hero, main, cta, footer] + description: "Bouncing-mascot canvas hero, sticker feature cards, big confetti CTA" + slots: [header, main, footer] - system: kindergarten key: article title: Story Page - description: "Picture-book reading layout for blog posts and stories" + description: "Picture-book reading layout — prose column plus a sticker note aside" slots: [header, main, aside, footer] - system: kindergarten - key: full-width - title: Full Width - description: "Edge-to-edge gallery / classroom showcase" + key: blog-index + title: Blog Index + description: "Story-wall feed of rounded post cards" + slots: [header, main, footer] + - system: kindergarten + key: contact + title: Contact + description: "Friendly enrol / contact page — form card plus a contact sticker card" + slots: [header, main, footer] + - system: kindergarten + key: auth + title: Auth + description: "Centered rounded sign-in card for login / register / reset" slots: [header, main, footer] +# All 49 mandatory-tier builtin overrides (OVERRIDE-CONTRACT §1), dashed keys. template_overrides: + # Chrome (4) + - { template: kindergarten, block: navbar } + - { template: kindergarten, block: footer } + - { template: kindergarten, block: utility-bar } + - { template: kindergarten, block: announcement-bar } + # Marketing (12) + - { template: kindergarten, block: hero } + - { template: kindergarten, block: feature-grid } + - { template: kindergarten, block: rich-section } + - { template: kindergarten, block: stats } + - { template: kindergarten, block: testimonial } + - { template: kindergarten, block: logo-strip } + - { template: kindergarten, block: pricing } + - { template: kindergarten, block: team } + - { template: kindergarten, block: faq } + - { template: kindergarten, block: timeline } + - { template: kindergarten, block: cta } + - { template: kindergarten, block: countdown } + # Primitives (11) - { template: kindergarten, block: heading } - { template: kindergarten, block: text } + - { template: kindergarten, block: rich-text } + - { template: kindergarten, block: image } + - { template: kindergarten, block: quote } + - { template: kindergarten, block: divider } - { template: kindergarten, block: button } - { template: kindergarten, block: card } + - { template: kindergarten, block: gallery } + - { template: kindergarten, block: video-embed } + - { template: kindergarten, block: audio-embed } + # Commerce / local (6) + - { template: kindergarten, block: menu-list } + - { template: kindergarten, block: event-list } + - { template: kindergarten, block: contact-form } + - { template: kindergarten, block: contact-card } + - { template: kindergarten, block: hours-location } + - { template: kindergarten, block: social-links } + # Blog family (12) + - { template: kindergarten, block: blog-hero } + - { template: kindergarten, block: featured-posts } + - { template: kindergarten, block: related-posts } + - { template: kindergarten, block: popular-posts } + - { template: kindergarten, block: category-list } + - { template: kindergarten, block: post-hero } + - { template: kindergarten, block: post-metadata } + - { template: kindergarten, block: author-bio } + - { template: kindergarten, block: author-bio-hero } + - { template: kindergarten, block: newsletter-signup } + - { template: kindergarten, block: breadcrumbs } + - { template: kindergarten, block: sidebar-nav } + # Auth surface (3) + - { template: kindergarten, block: auth-form } + - { template: kindergarten, block: password-reset-form } + - { template: kindergarten, block: auth-status } + # System (1) + - { template: kindergarten, block: page-suggestions } email_wrappers: - kindergarten @@ -43,490 +112,370 @@ email_wrappers: css: input_css_append: | /* ============================================================ - Kindergarten — Primary-colored, hand-lettered theme - ------------------------------------------------------------ - Token strategy: every color reference goes through - `hsl(var(--))` and font-family goes through - `var(--font-{heading|body|mono}, )`. + Kindergarten — Preschool / daycare theme (WO-TF-019, Wave A). + Crayon borders, sticker badges, confetti, primary-colour joy, + and a GO-BIG bouncing-mascot canvas hero (hero/landing only). + Every colour goes through hsl(var(--token)); every font-family + goes through var(--font-{heading|body|mono}, ). + No hardcoded hex / rgb / named colours. ============================================================ */ - - /* ---- Typography (CSS-variable consumers + fallbacks) ------- */ - - .kg-page { - font-family: var(--font-body, "Nunito", "Quicksand", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif); - line-height: 1.7; - background-color: hsl(var(--background)); - color: hsl(var(--foreground)); + + /* ---- Typography -------------------------------------------- */ + /* Families flow through the admin-bound CSS variables; the + fallback leads with the bundled OFL family so the intended look + survives before the admin assigns a slot. @font-face for the + bundled families is auto-emitted from fonts.json. */ + + .kg-body { + font-family: var(--font-body, "Quicksand", "Nunito", system-ui, -apple-system, "Segoe UI", sans-serif); } - - .kg-display, - .kg-page h1, - .kg-page h2, - .kg-page h3, - .kg-page h4, - .kg-page h5, - .kg-page h6 { - font-family: var(--font-heading, "Quicksand", "Sniglet", "Nunito", -apple-system, sans-serif); - font-weight: 700; - letter-spacing: -0.01em; + .kg-display { + font-family: var(--font-heading, "Baloo 2", "Fredoka", "Quicksand", system-ui, sans-serif); + font-weight: 700; + letter-spacing: -0.01em; } - .kg-mono { - font-family: var(--font-mono, "Fira Code", "JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace); + font-family: var(--font-mono, ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace); } - - /* ---- Layout utilities -------------------------------------- */ - - .kg-section { - padding-block: 4rem; + .kg-prose, + .kg-prose p, + .kg-prose li { + font-family: var(--font-body, "Quicksand", "Nunito", sans-serif); + line-height: 1.75; } - - .kg-container { - max-width: 72rem; - margin-inline: auto; - padding-inline: 1.5rem; + .kg-prose code { + color: hsl(var(--primary)); + background-color: hsl(var(--muted)); + padding: 0.125rem 0.4rem; + border-radius: 0.5rem; + font-family: var(--font-mono, ui-monospace, Menlo, monospace); + font-size: 0.875em; } - - /* ---- Rounded primitives ------------------------------------ */ - - .kg-chip { - border-radius: 24px; - padding: 0.5rem 1rem; - display: inline-flex; - align-items: center; - gap: 0.5rem; + /* Friendly kicker: rounded, bold, primary — carries enough on its + own that it is never a silent no-op used alone. */ + .kg-eyebrow { + font-family: var(--font-heading, "Baloo 2", "Quicksand", sans-serif); + font-weight: 700; + font-size: 0.85rem; + letter-spacing: 0.02em; + color: hsl(var(--primary)); + display: inline-flex; + align-items: center; + gap: 0.4rem; } - - .kg-card { - border-radius: 32px; - background-color: hsl(var(--card)); - color: hsl(var(--card-foreground, var(--foreground))); - padding: 2rem; - border: 1px solid hsl(var(--border)); - position: relative; - overflow: hidden; - } - - .kg-card::before { - content: ""; - position: absolute; - inset: 0 0 auto 0; - height: 4px; - background-color: hsl(var(--primary)); - } - - .kg-hero-panel { - border-radius: 48px; - padding: clamp(2rem, 6vw, 4rem); - position: relative; - overflow: hidden; - background-color: hsl(var(--accent) / 0.15); - } - - .kg-pill { - display: inline-flex; - align-items: center; - justify-content: center; - gap: 0.5rem; - padding: 1.25rem 2.5rem; - border-radius: 9999px; - font-family: var(--font-heading, "Quicksand", "Sniglet", "Nunito", sans-serif); - font-weight: 700; - font-size: 1.125rem; - background-color: hsl(var(--primary)); - color: hsl(var(--primary-foreground)); - border: 0; - cursor: pointer; - box-shadow: 0 4px 0 0 hsl(var(--foreground) / 0.15); - text-decoration: none; - transition: transform 120ms ease, box-shadow 120ms ease; - } - - .kg-pill:hover { - transform: translateY(-1px); - box-shadow: 0 5px 0 0 hsl(var(--foreground) / 0.15); - } - - .kg-pill:active { - transform: translateY(2px); - box-shadow: 0 2px 0 0 hsl(var(--foreground) / 0.15); - } - - .kg-pill:focus-visible { - outline: 3px solid hsl(var(--ring)); - outline-offset: 3px; - } - - /* CTA colorways */ - .kg-pill-red { background-color: hsl(var(--destructive)); color: hsl(var(--destructive-foreground)); } - .kg-pill-blue { background-color: hsl(var(--secondary)); color: hsl(var(--secondary-foreground)); } - .kg-pill-yellow { background-color: hsl(var(--accent)); color: hsl(var(--accent-foreground)); } - .kg-pill-green { background-color: hsl(145 65% 45%); color: hsl(0 0% 100%); } - - /* ---- Crayon underline (utility on headings) ---------------- */ - + + /* ---- Crayon underline (decorative heading accent) ---------- */ + .kg-crayon-underline { - position: relative; - display: inline-block; + position: relative; + display: inline-block; } - .kg-crayon-underline::after { - content: ""; - position: absolute; - left: 0; - right: 0; - bottom: -6px; - height: 8px; - background: linear-gradient(90deg, - hsl(var(--primary)) 0 22%, - hsl(var(--accent)) 22% 50%, - hsl(var(--secondary)) 50% 78%, - currentColor 78% 100%); - border-radius: 4px; - transform: rotate(-1deg); - z-index: -1; + content: ""; + position: absolute; + left: 0; + right: 0; + bottom: -6px; + height: 8px; + background: linear-gradient(90deg, + hsl(var(--primary)) 0 26%, + hsl(var(--accent)) 26% 52%, + hsl(var(--secondary)) 52% 78%, + hsl(var(--primary)) 78% 100%); + border-radius: 6px; + transform: rotate(-1deg); + z-index: -1; } - - /* ---- Numeral badges ---------------------------------------- */ - - .kg-numeral { - display: inline-flex; - align-items: center; - justify-content: center; - width: 96px; - height: 96px; - border-radius: 50%; - aspect-ratio: 1 / 1; - background-color: hsl(var(--accent)); - color: hsl(var(--accent-foreground)); - font-family: var(--font-heading, "Quicksand", "Sniglet", sans-serif); - font-weight: 700; - font-size: 2.5rem; - box-shadow: 0 4px 0 0 hsl(var(--foreground) / 0.15); + + /* ---- Crayon / sticker primitives (drop-in surface chrome) -- */ + /* kg-crayon-shadow: chunky hand-cut offset shadow + playful lift. + Pairs with Tailwind border-* / rounded-* on the same element. */ + + .kg-crayon-shadow { + box-shadow: 0 4px 0 0 hsl(var(--foreground) / 0.12); + transition: transform 160ms ease, box-shadow 160ms ease; } - - /* ---- Polaroid frame for gallery_of_art --------------------- */ - + .kg-crayon-shadow:hover, + .kg-crayon-shadow:focus-within { + transform: translateY(-2px); + box-shadow: 0 8px 0 0 hsl(var(--foreground) / 0.14); + } + + /* kg-drop: chunky flat button shadow (the crayon "sticker" edge). */ + .kg-drop { + box-shadow: 0 4px 0 0 hsl(var(--foreground) / 0.15); + } + + /* kg-pop: playful hover bounce for buttons / interactive chips. */ + .kg-pop { + transition: transform 140ms cubic-bezier(0.34, 1.56, 0.64, 1); + } + .kg-pop:hover, + .kg-pop:focus-visible { + transform: translateY(-2px) scale(1.03); + } + .kg-pop:active { + transform: translateY(1px) scale(0.98); + } + + /* kg-sticker: peel-sticker badge — rounded, chunky, slight tilt. */ + .kg-sticker { + display: inline-flex; + align-items: center; + gap: 0.35rem; + padding: 0.3rem 0.85rem; + border-radius: 9999px; + font-family: var(--font-heading, "Baloo 2", "Quicksand", sans-serif); + font-weight: 700; + font-size: 0.8rem; + background-color: hsl(var(--accent)); + color: hsl(var(--accent-foreground)); + box-shadow: 0 3px 0 0 hsl(var(--foreground) / 0.12); + transform: rotate(-2deg); + } + + /* Global focus-visible ring (accessibility). */ + .kg-page a:focus-visible, + .kg-page button:focus-visible, + .kg-page input:focus-visible, + .kg-page textarea:focus-visible, + .kg-page select:focus-visible { + outline: 3px solid hsl(var(--ring)); + outline-offset: 2px; + } + + /* ---- Page shell ------------------------------------------- */ + + .kg-page { + font-family: var(--font-body, "Quicksand", "Nunito", system-ui, sans-serif); + line-height: 1.7; + background-color: hsl(var(--background)); + color: hsl(var(--foreground)); + } + .kg-page h1, .kg-page h2, .kg-page h3, + .kg-page h4, .kg-page h5, .kg-page h6 { + font-family: var(--font-heading, "Baloo 2", "Quicksand", sans-serif); + font-weight: 700; + letter-spacing: -0.01em; + } + .kg-container { + max-width: 72rem; + margin-inline: auto; + padding-inline: 1.5rem; + } + .kg-empty { + text-align: center; + padding: 2rem; + border-radius: 1.5rem; + border: 3px dashed hsl(var(--border)); + color: hsl(var(--muted-foreground)); + font-family: var(--font-body, "Quicksand", sans-serif); + } + /* Dotted-paper page background layer. */ + .kg-dotted-paper { + background-color: hsl(var(--background)); + background-image: radial-gradient(circle, hsl(var(--muted-foreground) / 0.14) 1px, transparent 1.6px); + background-size: 26px 26px; + } + + /* ---- Persona-block furniture ------------------------------ */ + + .kg-hero-panel { + border-radius: 2.5rem; + padding: clamp(2rem, 6vw, 4rem); + position: relative; + overflow: hidden; + background-color: hsl(var(--accent) / 0.15); + } + .kg-card { + border-radius: 2rem; + background-color: hsl(var(--card)); + color: hsl(var(--card-foreground)); + padding: 2rem; + border: 3px solid hsl(var(--border)); + position: relative; + overflow: hidden; + } + + /* Alphabet strip (persona block). */ + .kg-alphabet-strip { + display: flex; + flex-wrap: wrap; + gap: 0.6rem; + padding: 1rem; + } + .kg-alphabet-letter { + flex: 0 0 auto; + display: inline-flex; + align-items: center; + justify-content: center; + width: 56px; + height: 56px; + border-radius: 1.25rem; + font-family: var(--font-heading, "Baloo 2", "Quicksand", sans-serif); + font-weight: 700; + font-size: 1.5rem; + box-shadow: 0 4px 0 0 hsl(var(--foreground) / 0.12); + } + .kg-alphabet-letter:nth-child(4n+1) { background-color: hsl(var(--primary)); color: hsl(var(--primary-foreground)); } + .kg-alphabet-letter:nth-child(4n+2) { background-color: hsl(var(--secondary)); color: hsl(var(--secondary-foreground)); } + .kg-alphabet-letter:nth-child(4n+3) { background-color: hsl(var(--accent)); color: hsl(var(--accent-foreground)); } + .kg-alphabet-letter:nth-child(4n+4) { background-color: hsl(var(--destructive)); color: hsl(var(--destructive-foreground)); } + .kg-alphabet-mono .kg-alphabet-letter { background-color: hsl(var(--muted)); color: hsl(var(--muted-foreground)); } + .kg-alphabet-primary .kg-alphabet-letter { background-color: hsl(var(--primary)); color: hsl(var(--primary-foreground)); } + + /* Day schedule (persona block). */ + .kg-schedule-item { + display: grid; + grid-template-columns: auto 1fr auto; + gap: 1rem; + align-items: center; + padding: 1rem 1.25rem; + border-radius: 1.5rem; + background-color: hsl(var(--card)); + border: 3px solid hsl(var(--border)); + margin-bottom: 0.75rem; + } + .kg-schedule-time { + display: inline-flex; + align-items: center; + justify-content: center; + padding: 0.25rem 0.85rem; + border-radius: 9999px; + background-color: hsl(var(--accent)); + color: hsl(var(--accent-foreground)); + font-family: var(--font-mono, ui-monospace, monospace); + font-weight: 700; + } + .kg-schedule-activity { color: hsl(var(--foreground)); } + .kg-schedule-icon { color: hsl(var(--primary)); } + + /* Art-wall polaroid (persona block). */ + .kg-gallery-grid { + display: grid; + grid-template-columns: 1fr; + gap: 1.5rem; + } + @media (min-width: 640px) { .kg-gallery-grid { grid-template-columns: repeat(2, 1fr); } } + @media (min-width: 1024px) { .kg-gallery-grid { grid-template-columns: repeat(3, 1fr); } } .kg-polaroid { - background-color: hsl(var(--card)); - border-radius: 16px; - padding-top: 12px; - padding-inline: 12px; - padding-bottom: 36px; - box-shadow: 0 6px 24px hsl(var(--foreground) / 0.12); - transform: rotate(-1deg); - transition: transform 200ms ease; + background-color: hsl(var(--card)); + border-radius: 1rem; + padding: 12px 12px 36px; + box-shadow: 0 6px 24px hsl(var(--foreground) / 0.12); + transform: rotate(-1.5deg); + transition: transform 200ms ease; } - - .kg-polaroid:nth-child(2n) { transform: rotate(1.5deg); } - .kg-polaroid:nth-child(3n) { transform: rotate(-0.5deg); } - .kg-polaroid:hover { transform: rotate(0deg); } - + .kg-polaroid:nth-child(2n) { transform: rotate(1.5deg); } + .kg-polaroid:nth-child(3n) { transform: rotate(-0.5deg); } + .kg-polaroid:hover { transform: rotate(0deg); } .kg-polaroid > img, .kg-polaroid > .kg-polaroid-image { - display: block; - width: 100%; - aspect-ratio: 1 / 1; - object-fit: cover; - border-radius: 8px; - background-color: hsl(var(--muted)); + display: block; + width: 100%; + aspect-ratio: 1 / 1; + object-fit: cover; + border-radius: 0.5rem; + background-color: hsl(var(--muted)); } - .kg-polaroid-caption { - text-align: center; - margin-top: 0.75rem; - font-family: var(--font-heading, "Quicksand", "Sniglet", sans-serif); - color: hsl(var(--foreground)); + text-align: center; + margin-top: 0.75rem; + font-family: var(--font-heading, "Baloo 2", "Quicksand", sans-serif); + color: hsl(var(--foreground)); } - - /* ---- Alphabet strip ---------------------------------------- */ - - .kg-alphabet-strip { - display: flex; - overflow-x: auto; - gap: 0.5rem; - padding: 1rem; - scrollbar-width: thin; - } - - .kg-alphabet-letter { - flex: 0 0 auto; - display: inline-flex; - align-items: center; - justify-content: center; - width: 56px; - height: 56px; - border-radius: 24px; - background-color: hsl(var(--primary)); - color: hsl(var(--primary-foreground)); - font-family: var(--font-heading, "Quicksand", "Sniglet", sans-serif); - font-weight: 700; - font-size: 1.5rem; - box-shadow: 0 4px 0 0 hsl(var(--foreground) / 0.12); - } - - .kg-alphabet-letter:nth-child(4n+1) { background-color: hsl(var(--primary)); color: hsl(var(--primary-foreground)); } - .kg-alphabet-letter:nth-child(4n+2) { background-color: hsl(var(--secondary)); color: hsl(var(--secondary-foreground)); } - .kg-alphabet-letter:nth-child(4n+3) { background-color: hsl(var(--accent)); color: hsl(var(--accent-foreground)); } - .kg-alphabet-letter:nth-child(4n+4) { background-color: hsl(145 65% 45%); color: hsl(0 0% 100%); } - - .kg-alphabet-mono .kg-alphabet-letter { - background-color: hsl(var(--muted)); - color: hsl(var(--muted-foreground)); - } - - .kg-alphabet-primary .kg-alphabet-letter { - background-color: hsl(var(--primary)); - color: hsl(var(--primary-foreground)); - } - - /* ---- Day schedule pills ------------------------------------ */ - - .kg-schedule-item { - display: grid; - grid-template-columns: auto 1fr auto; - gap: 1rem; - align-items: center; - padding: 1rem; - border-radius: 24px; - background-color: hsl(var(--card)); - border: 1px solid hsl(var(--border)); - margin-bottom: 0.75rem; - } - - .kg-schedule-time { - display: inline-flex; - align-items: center; - justify-content: center; - padding: 0.25rem 0.75rem; - border-radius: 9999px; - background-color: hsl(var(--accent)); - color: hsl(var(--accent-foreground)); - font-family: var(--font-mono, "Fira Code", monospace); - font-weight: 700; - } - - .kg-schedule-activity { - font-family: var(--font-body, "Nunito", sans-serif); - color: hsl(var(--foreground)); - } - - .kg-schedule-icon { - color: hsl(var(--primary)); - } - - /* ---- Storybook quote --------------------------------------- */ - + + /* Story-time open book (persona block). */ .kg-storybook { - display: grid; - grid-template-columns: 1fr; - gap: 2rem; - padding: 2rem; - border-radius: 32px; - background-color: hsl(var(--card)); - border: 1px solid hsl(var(--border)); + display: grid; + grid-template-columns: 1fr; + gap: 2rem; + padding: 2rem; + border-radius: 2rem; + background-color: hsl(var(--card)); + border: 3px solid hsl(var(--border)); } - @media (min-width: 768px) { - .kg-storybook { - grid-template-columns: 1fr 1fr; - align-items: center; - } + .kg-storybook { grid-template-columns: 1fr 1fr; align-items: center; } } - .kg-storybook-quote { - font-family: var(--font-heading, "Quicksand", "Sniglet", serif); - font-size: clamp(1.25rem, 3vw, 2rem); - line-height: 1.4; - color: hsl(var(--foreground)); + font-family: var(--font-heading, "Baloo 2", "Quicksand", sans-serif); + font-size: clamp(1.25rem, 3vw, 2rem); + line-height: 1.4; + color: hsl(var(--foreground)); } - - .kg-storybook-author { - margin-top: 1rem; - color: hsl(var(--muted-foreground)); - font-family: var(--font-body, "Nunito", sans-serif); - } - .kg-storybook-illustration { - width: 100%; - aspect-ratio: 4 / 3; - object-fit: cover; - border-radius: 24px; - background-color: hsl(var(--muted)); + width: 100%; + aspect-ratio: 4 / 3; + object-fit: cover; + border-radius: 1.25rem; + background-color: hsl(var(--muted)); } - - /* ---- Friendly footer --------------------------------------- */ - - .kg-footer { - padding: 3rem 1.5rem; - background-color: hsl(var(--muted)); - color: hsl(var(--foreground)); - border-top: 6px dashed hsl(var(--primary)); - border-radius: 48px 48px 0 0; + + /* ---- GO-BIG motion: bouncing mascot + wiggling letters ----- */ + /* The canvas confetti/mascot showpiece lives ONLY on the hero + override (hero/landing). It lazy-inits on view, honours + prefers-reduced-motion, and degrades to a static confetti wash + with JS off. No canvas anywhere else in the theme. */ + + .kg-mascot { + transform-origin: center bottom; + animation: kg-bounce 2.4s ease-in-out infinite; } - - .kg-footer-mascot { - display: inline-flex; - align-items: center; - gap: 0.5rem; - margin-bottom: 1rem; - font-family: var(--font-heading, "Quicksand", sans-serif); + @keyframes kg-bounce { + 0%, 100% { transform: translateY(0) rotate(-1deg); } + 50% { transform: translateY(-16px) rotate(1deg); } } - - .kg-footer-signup { - display: flex; - flex-wrap: wrap; - gap: 0.5rem; - margin-block: 1.5rem; - max-width: 32rem; + .kg-wiggle-letter { + display: inline-block; + animation: kg-wiggle 2.2s ease-in-out infinite; } - - .kg-footer-signup input { - flex: 1 1 12rem; - padding: 0.75rem 1rem; - border-radius: 9999px; - border: 2px solid hsl(var(--border)); - background-color: hsl(var(--input)); - color: hsl(var(--foreground)); - font-family: var(--font-body, "Nunito", sans-serif); + .kg-wiggle-letter:nth-child(2n) { animation-delay: 0.15s; } + .kg-wiggle-letter:nth-child(3n) { animation-delay: 0.3s; } + .kg-wiggle-letter:nth-child(4n) { animation-delay: 0.45s; } + @keyframes kg-wiggle { + 0%, 100% { transform: rotate(-5deg); } + 50% { transform: rotate(5deg); } } - - .kg-footer-signup input:focus-visible { - outline: 3px solid hsl(var(--ring)); - outline-offset: 2px; + + /* Confetti canvas + static no-JS fallback. */ + .kg-confetti-wrap { + position: absolute; + inset: 0; + overflow: hidden; + pointer-events: none; } - - .kg-social-row { - display: flex; - gap: 0.75rem; - margin-top: 1rem; + .kg-confetti-canvas { + position: absolute; + inset: 0; + width: 100%; + height: 100%; + display: block; } - - .kg-social-link { - display: inline-flex; - align-items: center; - justify-content: center; - width: 40px; - height: 40px; - border-radius: 9999px; - background-color: hsl(var(--card)); - color: hsl(var(--primary)); - border: 1px solid hsl(var(--border)); + .kg-confetti-fallback { + position: absolute; + inset: 0; + pointer-events: none; + background-image: + radial-gradient(circle, hsl(var(--primary) / 0.55) 8px, transparent 9px), + radial-gradient(circle, hsl(var(--secondary) / 0.55) 6px, transparent 7px), + radial-gradient(circle, hsl(var(--accent) / 0.6) 5px, transparent 6px); + background-size: 90px 90px, 60px 60px, 40px 40px; + background-position: 12% 18%, 72% 28%, 42% 78%; + opacity: 0.55; } - - .kg-social-link:focus-visible { - outline: 3px solid hsl(var(--ring)); - outline-offset: 2px; - } - - /* ---- Mascot hero confetti dots ----------------------------- */ - - .kg-confetti { - position: absolute; - inset: 0; - pointer-events: none; - background-image: - radial-gradient(circle, hsl(var(--primary) / 0.5) 8px, transparent 9px), - radial-gradient(circle, hsl(var(--secondary) / 0.5) 6px, transparent 7px), - radial-gradient(circle, hsl(var(--accent) / 0.5) 5px, transparent 6px); - background-size: 80px 80px, 50px 50px, 30px 30px; - background-position: 10% 20%, 70% 30%, 40% 80%; - opacity: 0.6; - } - - /* ---- Dotted paper layer (page background) ------------------ */ - - .kg-dotted-paper { - background-color: hsl(var(--background)); - background-image: radial-gradient(circle, hsl(var(--muted-foreground) / 0.15) 1px, transparent 1.5px); - background-size: 24px 24px; - } - - /* ---- Heading override (numeral badge variant) -------------- */ - - .kg-heading-stepped { - display: flex; - align-items: center; - gap: 1rem; - } - - .kg-heading-step-badge { - display: inline-flex; - align-items: center; - justify-content: center; - width: 48px; - height: 48px; - border-radius: 50%; - background-color: hsl(var(--accent)); - color: hsl(var(--accent-foreground)); - font-family: var(--font-heading, "Quicksand", "Sniglet", sans-serif); - font-weight: 700; - font-size: 1.5rem; - flex: 0 0 auto; - } - - /* ---- Text override: rounded selection highlight ------------ */ - - .kg-text ::selection { - background-color: hsl(var(--accent)); - color: hsl(var(--accent-foreground)); - } - - .kg-text { - font-family: var(--font-body, "Nunito", sans-serif); - line-height: 1.7; - color: hsl(var(--foreground)); - } - - /* ---- Numbers counter grid ---------------------------------- */ - - .kg-numbers-grid { - display: grid; - grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); - gap: 2rem; - text-align: center; - } - - .kg-numbers-item { - display: flex; - flex-direction: column; - align-items: center; - gap: 0.75rem; - } - - .kg-numbers-label { - font-family: var(--font-body, "Nunito", sans-serif); - color: hsl(var(--muted-foreground)); - text-transform: uppercase; - letter-spacing: 0.05em; - font-size: 0.875rem; - } - - /* ---- Gallery grid ------------------------------------------ */ - - .kg-gallery-grid { - display: grid; - grid-template-columns: 1fr; - gap: 1.5rem; - } - - @media (min-width: 640px) { .kg-gallery-grid { grid-template-columns: repeat(2, 1fr); } } - @media (min-width: 1024px) { .kg-gallery-grid { grid-template-columns: repeat(3, 1fr); } } - - /* ---- Big CTA panel (spec §9 button override) --------------- */ - - .kg-big-cta { - text-align: center; - padding: 3rem 1.5rem; - } - - /* ---- Empty-state placeholder ------------------------------- */ - - .kg-empty { - text-align: center; - padding: 2rem; - border-radius: 24px; - border: 2px dashed hsl(var(--border)); - color: hsl(var(--muted-foreground)); - font-family: var(--font-body, "Nunito", sans-serif); + + /* ---- Reduced motion: freeze everything -------------------- */ + + @media (prefers-reduced-motion: reduce) { + .kg-mascot, + .kg-wiggle-letter, + .kg-confetti-canvas { + animation: none !important; + } + .kg-confetti-canvas { display: none; } + .kg-pop:hover, + .kg-pop:focus-visible, + .kg-crayon-shadow:hover, + .kg-crayon-shadow:focus-within { transform: none; } } diff --git a/master_pages.json b/master_pages.json index fca0431..ea359a1 100644 --- a/master_pages.json +++ b/master_pages.json @@ -1,25 +1,23 @@ [ { - "key": "kindergarten:default-master", - "title": "Kindergarten Default Master", - "page_templates": ["default", "article"], + "key": "kindergarten:site-master", + "title": "Kindergarten Site Master", + "page_templates": ["default", "full-width", "landing", "article", "blog-index", "contact"], "blocks": [ - { "block_key": "navbar", "title": "Crayon Nav", "content": { "menuName": "main" }, "slot": "header", "sort_order": 0 }, - { "block_key": "kindergarten:alphabet_strip", "title": "ABC Strip", "content": { "letters": "ABCDEFGHIJ", "colorMode": "rainbow" }, "slot": "header", "sort_order": 1 }, - { "block_key": "slot", "title": "Main Slot", "content": { "slotName": "main", "placeholder": "Page content" }, "slot": "main", "sort_order": 0 }, - { "block_key": "kindergarten:footer", "title": "Friendly Footer", "content": { "showSignup": "true", "mascotName": "Pip" }, "slot": "footer", "sort_order": 0 } + { "block_key": "navbar", "title": "Crayon Navbar", "content": { "menuName": "main", "logoType": "text", "logoText": { "text": "Little Sprouts" }, "companyName": "Little Sprouts", "stickyBehavior": "sticky", "cta": { "text": "Book a visit", "url": "/contact" } }, "slot": "header", "sort_order": 0 }, + { "block_key": "kindergarten:alphabet_strip", "title": "ABC Band", "content": { "colorMode": "rainbow", "letters": [ { "letter": "A" }, { "letter": "B" }, { "letter": "C" }, { "letter": "D" }, { "letter": "E" }, { "letter": "F" }, { "letter": "G" }, { "letter": "H" } ] }, "slot": "header", "sort_order": 1 }, + { "block_key": "slot", "title": "Main Content", "content": { "slotName": "main", "placeholder": "Page content" }, "slot": "main", "sort_order": 0 }, + { "block_key": "footer", "title": "Friendly Footer", "content": { "layout": "columns", "companyName": "Little Sprouts", "tagline": "A warm, playful place to learn and grow.", "copyright": "Little Sprouts Preschool", "showNewsletter": true, "newsletterTitle": "Stay in the loop", "newsletterDescription": "Notes home, event dates, and the odd finger-painting photo." }, "slot": "footer", "sort_order": 0 } ] }, { - "key": "kindergarten:landing-master", - "title": "Kindergarten Landing Master", - "page_templates": ["landing", "full-width"], + "key": "kindergarten:auth-master", + "title": "Kindergarten Auth Master", + "page_templates": ["auth"], "blocks": [ - { "block_key": "navbar", "title": "Crayon Nav", "content": { "menuName": "main" }, "slot": "header", "sort_order": 0 }, - { "block_key": "kindergarten:mascot_hero", "title": "Mascot Hero", "content": { "mascot": "pip", "headline": "Welcome to school!", "tagline": "Big fun, small humans." }, "slot": "hero", "sort_order": 0 }, - { "block_key": "slot", "title": "Main Slot", "content": { "slotName": "main" }, "slot": "main", "sort_order": 0 }, - { "block_key": "kindergarten:big_cta", "title": "Big Yellow CTA", "content": { "label": "Enrol today", "href": "/enrol", "colorVariant": "yellow" }, "slot": "cta", "sort_order": 0 }, - { "block_key": "kindergarten:footer", "title": "Friendly Footer", "content": { "showSignup": "true" }, "slot": "footer", "sort_order": 0 } + { "block_key": "navbar", "title": "Crayon Navbar", "content": { "menuName": "main", "logoType": "text", "logoText": { "text": "Little Sprouts" }, "companyName": "Little Sprouts" }, "slot": "header", "sort_order": 0 }, + { "block_key": "slot", "title": "Auth Content", "content": { "slotName": "main" }, "slot": "main", "sort_order": 0 }, + { "block_key": "footer", "title": "Friendly Footer", "content": { "layout": "minimal", "companyName": "Little Sprouts", "tagline": "A warm, playful place to learn and grow.", "copyright": "Little Sprouts Preschool" }, "slot": "footer", "sort_order": 0 } ] } ] diff --git a/plugin.mod b/plugin.mod index 9e6f6fc..6c5722d 100644 --- a/plugin.mod +++ b/plugin.mod @@ -3,7 +3,8 @@ name = "kindergarten" display_name = "Kindergarten" scope = "@themes" version = "0.2.0" -description = "Primary-colored, hand-lettered theme for schools, daycares, toy shops, museums, and kids' publishers." +description = "Primary-colored, hand-lettered theme for preschools and daycares — crayon borders, sticker badges, confetti, and a bouncing mascot showpiece." kind = "theme" categories = ["templates"] -tags = ["kids", "education", "playful", "family", "school", "museum", "friendly", "daycare", "publisher"] +tags = ["kids", "education", "playful", "family", "school", "daycare", "preschool", "friendly", "colorful"] +required_icon_packs = ["lucide", "simple-icons"] diff --git a/presets.json b/presets.json index 7b80111..aafb3f4 100644 --- a/presets.json +++ b/presets.json @@ -1,110 +1,252 @@ [ - { - "id": "recess", - "name": "Crayon Recess", - "description": "Light, sunny, primary-on-cream — for daytime classrooms.", - "theme": { - "lightColors": { - "background": "48 60% 97%", - "foreground": "220 40% 18%", - "card": "0 0% 100%", - "cardForeground": "220 40% 18%", - "popover": "0 0% 100%", - "popoverForeground": "220 40% 18%", - "primary": "4 80% 56%", - "primaryForeground": "0 0% 100%", - "secondary": "220 75% 56%", - "secondaryForeground": "0 0% 100%", - "muted": "48 40% 92%", - "mutedForeground": "220 25% 38%", - "accent": "48 95% 58%", - "accentForeground": "220 50% 14%", - "destructive": "0 84% 55%", - "destructiveForeground": "0 0% 100%", - "border": "220 30% 86%", - "input": "48 30% 90%", - "ring": "4 80% 56%" - }, - "mode": "light" - } - }, - { - "id": "chalkboard", - "name": "Chalkboard Night", - "description": "Deep navy chalkboard with crayon-bright accents.", - "theme": { - "darkColors": { - "background": "220 50% 10%", - "foreground": "48 60% 96%", - "card": "220 45% 14%", - "cardForeground": "48 60% 96%", - "popover": "220 45% 14%", - "popoverForeground": "48 60% 96%", - "primary": "48 95% 58%", - "primaryForeground": "220 50% 10%", - "secondary": "4 80% 60%", - "secondaryForeground": "0 0% 100%", - "muted": "220 35% 18%", - "mutedForeground": "220 20% 70%", - "accent": "145 70% 50%", - "accentForeground": "220 50% 10%", - "destructive": "0 84% 60%", - "destructiveForeground": "0 0% 100%", - "border": "220 35% 24%", - "input": "220 35% 20%", - "ring": "48 95% 58%" - }, - "mode": "dark" - } - }, { "id": "crayon-box", "name": "Crayon Box", - "description": "Full primary quartet — red, blue, yellow, green — in both light and dark.", + "description": "The default — crayon blue, red, and yellow. Bright classroom by day, quiet-time navy by night.", "theme": { + "mode": "both", "lightColors": { "background": "48 55% 98%", - "foreground": "220 45% 15%", + "foreground": "222 47% 16%", "card": "0 0% 100%", - "cardForeground": "220 45% 15%", + "cardForeground": "222 47% 16%", "popover": "0 0% 100%", - "popoverForeground": "220 45% 15%", - "primary": "220 78% 52%", + "popoverForeground": "222 47% 16%", + "primary": "222 80% 54%", "primaryForeground": "0 0% 100%", - "secondary": "4 78% 56%", + "secondary": "4 78% 57%", "secondaryForeground": "0 0% 100%", - "muted": "48 35% 93%", - "mutedForeground": "220 22% 38%", - "accent": "145 65% 45%", + "muted": "48 40% 93%", + "mutedForeground": "222 20% 40%", + "accent": "45 96% 56%", + "accentForeground": "222 50% 14%", + "destructive": "0 84% 55%", + "destructiveForeground": "0 0% 100%", + "border": "222 25% 86%", + "input": "48 30% 90%", + "ring": "222 80% 54%" + }, + "darkColors": { + "background": "224 46% 9%", + "foreground": "48 55% 95%", + "card": "224 42% 13%", + "cardForeground": "48 55% 95%", + "popover": "224 42% 13%", + "popoverForeground": "48 55% 95%", + "primary": "218 90% 66%", + "primaryForeground": "224 46% 9%", + "secondary": "4 84% 64%", + "secondaryForeground": "0 0% 100%", + "muted": "224 34% 17%", + "mutedForeground": "220 20% 70%", + "accent": "45 96% 62%", + "accentForeground": "224 46% 9%", + "destructive": "0 84% 62%", + "destructiveForeground": "0 0% 100%", + "border": "224 32% 22%", + "input": "224 32% 19%", + "ring": "45 96% 62%" + } + } + }, + { + "id": "sunny-recess", + "name": "Sunny Recess", + "description": "Warm red-and-sun classroom by day, soft navy with a glow-star yellow for nap time.", + "theme": { + "mode": "both", + "lightColors": { + "background": "40 70% 97%", + "foreground": "20 40% 18%", + "card": "0 0% 100%", + "cardForeground": "20 40% 18%", + "popover": "0 0% 100%", + "popoverForeground": "20 40% 18%", + "primary": "8 82% 56%", + "primaryForeground": "0 0% 100%", + "secondary": "205 75% 52%", + "secondaryForeground": "0 0% 100%", + "muted": "40 45% 92%", + "mutedForeground": "20 20% 40%", + "accent": "42 96% 55%", + "accentForeground": "20 45% 16%", + "destructive": "0 84% 55%", + "destructiveForeground": "0 0% 100%", + "border": "32 35% 85%", + "input": "40 35% 90%", + "ring": "8 82% 56%" + }, + "darkColors": { + "background": "222 45% 9%", + "foreground": "40 60% 95%", + "card": "222 40% 13%", + "cardForeground": "40 60% 95%", + "popover": "222 40% 13%", + "popoverForeground": "40 60% 95%", + "primary": "10 88% 64%", + "primaryForeground": "0 0% 100%", + "secondary": "205 82% 62%", + "secondaryForeground": "222 45% 9%", + "muted": "222 32% 17%", + "mutedForeground": "40 22% 72%", + "accent": "44 96% 62%", + "accentForeground": "222 45% 9%", + "destructive": "0 84% 62%", + "destructiveForeground": "0 0% 100%", + "border": "222 30% 22%", + "input": "222 30% 19%", + "ring": "44 96% 62%" + } + } + }, + { + "id": "bluebird", + "name": "Bluebird", + "description": "Calm blue-and-teal for reading corners. Deep quiet navy with a teal glow after dark.", + "theme": { + "mode": "both", + "lightColors": { + "background": "205 60% 98%", + "foreground": "218 45% 16%", + "card": "0 0% 100%", + "cardForeground": "218 45% 16%", + "popover": "0 0% 100%", + "popoverForeground": "218 45% 16%", + "primary": "212 84% 52%", + "primaryForeground": "0 0% 100%", + "secondary": "45 95% 55%", + "secondaryForeground": "218 50% 14%", + "muted": "205 40% 93%", + "mutedForeground": "218 22% 42%", + "accent": "175 70% 42%", "accentForeground": "0 0% 100%", "destructive": "0 84% 55%", "destructiveForeground": "0 0% 100%", - "border": "220 25% 85%", - "input": "48 25% 88%", - "ring": "48 95% 55%" + "border": "210 30% 86%", + "input": "205 30% 90%", + "ring": "212 84% 52%" }, "darkColors": { - "background": "220 40% 8%", - "foreground": "48 55% 95%", - "card": "220 40% 12%", - "cardForeground": "48 55% 95%", - "popover": "220 40% 12%", - "popoverForeground": "48 55% 95%", - "primary": "220 80% 60%", - "primaryForeground": "0 0% 100%", - "secondary": "4 80% 58%", - "secondaryForeground": "0 0% 100%", - "muted": "220 30% 16%", - "mutedForeground": "220 18% 68%", - "accent": "145 70% 50%", - "accentForeground": "220 40% 8%", - "destructive": "0 84% 60%", + "background": "220 48% 9%", + "foreground": "205 55% 95%", + "card": "220 44% 13%", + "cardForeground": "205 55% 95%", + "popover": "220 44% 13%", + "popoverForeground": "205 55% 95%", + "primary": "210 92% 66%", + "primaryForeground": "220 48% 9%", + "secondary": "45 96% 62%", + "secondaryForeground": "220 48% 9%", + "muted": "220 34% 17%", + "mutedForeground": "205 20% 72%", + "accent": "172 80% 52%", + "accentForeground": "220 48% 9%", + "destructive": "0 84% 62%", "destructiveForeground": "0 0% 100%", - "border": "220 30% 22%", - "input": "220 30% 18%", - "ring": "48 95% 60%" + "border": "220 32% 22%", + "input": "220 32% 19%", + "ring": "172 80% 52%" + } + } + }, + { + "id": "meadow", + "name": "Meadow Play", + "description": "Green-and-orange nature play by day, deep teal-navy with a warm glow for winding down.", + "theme": { + "mode": "both", + "lightColors": { + "background": "90 45% 97%", + "foreground": "140 35% 15%", + "card": "0 0% 100%", + "cardForeground": "140 35% 15%", + "popover": "0 0% 100%", + "popoverForeground": "140 35% 15%", + "primary": "145 62% 40%", + "primaryForeground": "0 0% 100%", + "secondary": "25 88% 55%", + "secondaryForeground": "0 0% 100%", + "muted": "90 30% 92%", + "mutedForeground": "140 18% 38%", + "accent": "48 95% 55%", + "accentForeground": "140 40% 14%", + "destructive": "0 84% 55%", + "destructiveForeground": "0 0% 100%", + "border": "110 25% 84%", + "input": "90 25% 89%", + "ring": "145 62% 40%" }, - "mode": "both" + "darkColors": { + "background": "200 40% 9%", + "foreground": "90 45% 94%", + "card": "200 36% 13%", + "cardForeground": "90 45% 94%", + "popover": "200 36% 13%", + "popoverForeground": "90 45% 94%", + "primary": "145 66% 52%", + "primaryForeground": "200 40% 9%", + "secondary": "25 90% 62%", + "secondaryForeground": "0 0% 100%", + "muted": "200 30% 17%", + "mutedForeground": "90 20% 70%", + "accent": "48 96% 62%", + "accentForeground": "200 40% 9%", + "destructive": "0 84% 62%", + "destructiveForeground": "0 0% 100%", + "border": "200 28% 22%", + "input": "200 28% 19%", + "ring": "48 96% 62%" + } + } + }, + { + "id": "bubblegum", + "name": "Bubblegum", + "description": "Playful pink-and-purple for toy shops and party rooms. Plum-navy with a soft glow at night.", + "theme": { + "mode": "both", + "lightColors": { + "background": "330 50% 98%", + "foreground": "300 35% 18%", + "card": "0 0% 100%", + "cardForeground": "300 35% 18%", + "popover": "0 0% 100%", + "popoverForeground": "300 35% 18%", + "primary": "330 80% 58%", + "primaryForeground": "0 0% 100%", + "secondary": "265 70% 60%", + "secondaryForeground": "0 0% 100%", + "muted": "330 35% 94%", + "mutedForeground": "300 18% 42%", + "accent": "42 95% 56%", + "accentForeground": "300 40% 16%", + "destructive": "0 84% 55%", + "destructiveForeground": "0 0% 100%", + "border": "320 30% 88%", + "input": "330 30% 92%", + "ring": "330 80% 58%" + }, + "darkColors": { + "background": "285 40% 9%", + "foreground": "330 45% 95%", + "card": "285 36% 13%", + "cardForeground": "330 45% 95%", + "popover": "285 36% 13%", + "popoverForeground": "330 45% 95%", + "primary": "330 88% 68%", + "primaryForeground": "285 40% 9%", + "secondary": "265 82% 72%", + "secondaryForeground": "285 40% 9%", + "muted": "285 30% 17%", + "mutedForeground": "330 20% 72%", + "accent": "45 96% 64%", + "accentForeground": "285 40% 9%", + "destructive": "0 84% 62%", + "destructiveForeground": "0 0% 100%", + "border": "285 28% 23%", + "input": "285 28% 20%", + "ring": "45 96% 64%" + } } } ] diff --git a/seed/seed.json b/seed/seed.json new file mode 100644 index 0000000..617bae7 --- /dev/null +++ b/seed/seed.json @@ -0,0 +1,228 @@ +{ + "demo": true, + "settings": { + "merge": { + "companyName": "Little Sprouts", + "tagline": "A warm, playful place to learn and grow." + } + }, + "data_tables": [ + { + "key": "contact_submissions", + "name": "Visit Requests", + "description": "Seeded contact-form submissions for the Little Sprouts demo. A submission becomes a row here and emails the site admins.", + "columns": [ + { "key": "name", "label": "Parent name", "type": "text", "required": true }, + { "key": "email", "label": "Email", "type": "email", "required": true }, + { "key": "childAge", "label": "Child's age", "type": "text", "required": false }, + { "key": "message", "label": "Message", "type": "text", "required": true } + ] + } + ], + "pages": [ + { + "slug": "/", + "title": "Little Sprouts", + "template_key": "landing", + "blocks": [ + { + "block_key": "hero", + "title": "Hero", + "slot": "main", + "sort_order": 1, + "content": { + "template": "centered", + "minHeight": "screen", + "eyebrow": "Now enrolling", + "headline": "A happy place to start", + "subheadline": "Play-based preschool for ages 2 to 5.", + "description": "Small groups, warm teachers, and a garden to dig in. Come and see a normal day.", + "ctaPrimary": { "text": "Book a visit", "url": "/contact" }, + "ctaSecondary": { "text": "See our day", "url": "/about" } + } + }, + { + "block_key": "feature-grid", + "title": "Why families choose us", + "slot": "main", + "sort_order": 2, + "content": { + "eyebrow": "Why families choose us", + "heading": "What your child gets", + "intro": "Every day has room to move, make, and rest.", + "layout": "card", + "columns": 3, + "items": [ + { "icon": "lucide:sun", "title": "Time outside every day", "text": "Rain or shine, we head to the garden and the yard." }, + { "icon": "lucide:book-open", "title": "Stories and songs", "text": "We read together, sing together, and make up our own tales." }, + { "icon": "lucide:palette", "title": "Room to make a mess", "text": "Paint, clay, glue, and glitter. Aprons on, big ideas out." } + ] + } + }, + { + "block_key": "kindergarten:schedule", + "title": "A day in the Sunflower Room", + "slot": "main", + "sort_order": 3, + "content": { + "title": "A day in the Sunflower Room", + "items": [ + { "time": "09:00", "activity": "Arrival and free play", "icon": "play" }, + { "time": "10:00", "activity": "Morning circle and songs", "icon": "book" }, + { "time": "10:30", "activity": "Snack and fruit", "icon": "snack" }, + { "time": "11:00", "activity": "Garden and outside play", "icon": "sun" }, + { "time": "12:00", "activity": "Lunch together", "icon": "snack" }, + { "time": "13:00", "activity": "Quiet time and rest", "icon": "nap" }, + { "time": "14:00", "activity": "Painting and making", "icon": "paint" } + ] + } + }, + { + "block_key": "kindergarten:art_wall", + "title": "Fresh off the easel", + "slot": "main", + "sort_order": 4, + "content": { + "title": "Fresh off the easel", + "showChildName": true, + "items": [ + { "childName": "Mia", "age": "4" }, + { "childName": "Noah", "age": "3" }, + { "childName": "Ada", "age": "5" } + ] + } + }, + { + "block_key": "cta", + "title": "CTA", + "slot": "main", + "sort_order": 5, + "content": { + "background": "band", + "heading": "Come and see for yourself", + "text": "Visits run most mornings. We will show you around and answer everything.", + "primaryLabel": "Book a visit", + "primaryUrl": "/contact" + } + } + ] + }, + { + "slug": "/about", + "title": "About us", + "template_key": "default", + "blocks": [ + { "block_key": "heading", "title": "Heading", "slot": "main", "sort_order": 1, "content": { "text": "About Little Sprouts", "level": 1 } }, + { "block_key": "rich-text", "title": "Story", "slot": "main", "sort_order": 2, "content": { "html": "

Little Sprouts started in a church hall with six children and a box of blocks. Families kept asking for more days, so we grew into a proper little school with a garden out the back.

Our rooms are small on purpose. Teachers learn every child by name in the first week. We follow the children's lead, keep the day gentle, and send them home tired and happy.

" } }, + { + "block_key": "kindergarten:story_time", + "title": "Story time", + "slot": "main", + "sort_order": 3, + "content": { + "bookTitle": "The Very Sleepy Bear", + "author": "our reading corner", + "line": "The bear yawned, and the whole forest yawned back." + } + }, + { "block_key": "divider", "title": "Divider", "slot": "main", "sort_order": 4, "content": { "variant": "line", "weight": "medium" } }, + { "block_key": "quote", "title": "Quote", "slot": "main", "sort_order": 5, "content": { "style": "pull", "quote": "My daughter runs to the door every morning.", "attribution": "A Little Sprouts parent" } } + ] + }, + { + "slug": "/blog", + "title": "Stories", + "template_key": "blog-index", + "blocks": [ + { "block_key": "card", "title": "Post: Settling in", "slot": "main", "sort_order": 1, "content": { "layout": "vertical", "title": "Settling in: the first week", "text": "

Little tips that help a new child feel at home, from the drop-off wave to the goodbye routine.

", "link": "/blog/settling-in", "linkLabel": "Read on" } }, + { "block_key": "card", "title": "Post: What to pack", "slot": "main", "sort_order": 2, "content": { "layout": "vertical", "title": "What to pack for a preschool day", "text": "

A short list of what to bring, what to label, and what to leave at home.

", "link": "/blog/what-to-pack", "linkLabel": "Read on" } }, + { "block_key": "card", "title": "Post: Reading at home", "slot": "main", "sort_order": 3, "content": { "layout": "vertical", "title": "Ways to read at home", "text": "

Reading together does not need to be fancy. Here is how we keep it easy and fun.

", "link": "/blog/reading-at-home", "linkLabel": "Read on" } } + ] + }, + { + "slug": "/blog/settling-in", + "title": "Settling in: the first week", + "template_key": "article", + "parent_slug": "/blog", + "blocks": [ + { "block_key": "heading", "title": "Title", "slot": "main", "sort_order": 1, "content": { "text": "Settling in: the first week", "level": 1 } }, + { "block_key": "rich-text", "title": "Body", "slot": "main", "sort_order": 2, "content": { "html": "

The first week is a big change for everyone. Some children walk straight in. Others hold on tight at the door. Both are normal.

Keep goodbyes short and warm. Tell your child when you will be back, then go. We will send a photo once they settle, and most do within minutes.

" } } + ] + }, + { + "slug": "/blog/what-to-pack", + "title": "What to pack for a preschool day", + "template_key": "article", + "parent_slug": "/blog", + "blocks": [ + { "block_key": "heading", "title": "Title", "slot": "main", "sort_order": 1, "content": { "text": "What to pack for a preschool day", "level": 1 } }, + { "block_key": "rich-text", "title": "Body", "slot": "main", "sort_order": 2, "content": { "html": "

Pack a spare set of clothes, a hat, and a water bottle. Label everything with your child's name.

Leave toys from home in the car. They tend to go missing, and sharing them is hard on a new friend. We have plenty here to play with.

" } } + ] + }, + { + "slug": "/blog/reading-at-home", + "title": "Ways to read at home", + "template_key": "article", + "parent_slug": "/blog", + "blocks": [ + { "block_key": "heading", "title": "Title", "slot": "main", "sort_order": 1, "content": { "text": "Ways to read at home", "level": 1 } }, + { "block_key": "rich-text", "title": "Body", "slot": "main", "sort_order": 2, "content": { "html": "

Read the same book as often as they ask. Repetition is how young children learn the shape of a story.

Point at the pictures and ask what happens next. Let them turn the pages. The goal is a warm few minutes together, not a perfect reading.

" } } + ] + }, + { + "slug": "/contact", + "title": "Book a visit", + "template_key": "contact", + "blocks": [ + { + "block_key": "contact-form", + "title": "Book a visit", + "slot": "main", + "sort_order": 1, + "content": { + "heading": "Book a visit", + "description": "Tell us a little about your family. We will call to set up a time.", + "submitLabel": "Send", + "successMessage": "Thanks. We will be in touch very soon.", + "fields": [ + { "name": "name", "label": "Your name", "type": "text", "required": true, "width": "half" }, + { "name": "email", "label": "Email", "type": "email", "required": true, "width": "half" }, + { "name": "childAge", "label": "Your child's age", "type": "text", "required": false, "width": "full" }, + { "name": "message", "label": "Anything you would like us to know", "type": "textarea", "required": true, "width": "full" } + ], + "formConfig": { "enabled": true, "captchaEnabled": false, "targetTable": "contact_submissions" } + } + }, + { + "block_key": "contact-card", + "title": "Find us", + "slot": "main", + "sort_order": 2, + "content": { + "heading": "Find us", + "columns": 3, + "channels": [ + { "icon": "lucide:mail", "label": "Email", "value": "hello@littlesprouts.example", "link": "mailto:hello@littlesprouts.example" }, + { "icon": "lucide:phone", "label": "Phone", "value": "(02) 5550 1234", "link": "tel:+61255501234" }, + { "icon": "lucide:map-pin", "label": "Visit", "value": "14 Garden Lane, Riverside" } + ] + } + } + ] + }, + { + "slug": "/login", + "title": "Sign in", + "template_key": "auth", + "blocks": [ + { "block_key": "auth-form", "title": "Sign in", "slot": "main", "sort_order": 1, "content": { "default_tab": "login", "show_social": false } } + ] + } + ], + "menu_items": [ + { "menu": "main", "label": "Home", "page_slug": "/", "sort_order": 1 }, + { "menu": "main", "label": "About", "page_slug": "/about", "sort_order": 2 }, + { "menu": "main", "label": "Stories", "page_slug": "/blog", "sort_order": 3 }, + { "menu": "main", "label": "Visit", "page_slug": "/contact", "sort_order": 4 } + ] +} diff --git a/seed/workflows.json b/seed/workflows.json new file mode 100644 index 0000000..879dbfb --- /dev/null +++ b/seed/workflows.json @@ -0,0 +1,19 @@ +{ + "workflows": [ + { + "key": "contact_notify_admins", + "name": "Visit request to admins", + "description": "On a contact-form submission, email the site admins.", + "enabled": true, + "trigger": { "type": "row_inserted", "table": "contact_submissions" }, + "steps": [ + { + "type": "email", + "name": "Email admins", + "template": "Form Submission Notification", + "recipients": { "mode": "admins", "scope": "all" } + } + ] + } + ] +} diff --git a/templates/kindergarten/article.ninjatpl b/templates/kindergarten/article.ninjatpl index 7f8e017..74bbd99 100644 --- a/templates/kindergarten/article.ninjatpl +++ b/templates/kindergarten/article.ninjatpl @@ -1,16 +1,14 @@ - + {{ head_html|safe }} - + {{ admin_banner_html|safe }} -
{{ slots.header|safe }}
-
-
- {% if slots.main %}
{{ slots.main|safe }}
{% else %}
No content yet — start a story.
{% endif %} -
- {% if slots.aside %}{% endif %} -
-
{{ slots.footer|safe }}
+
{{ slots.header|safe }}
+
+
{{ slots.main|safe }}
+ {% if slots.aside %}{% endif %} +
+
{{ slots.footer|safe }}
{{ body_end_html|safe }} diff --git a/templates/kindergarten/auth.ninjatpl b/templates/kindergarten/auth.ninjatpl new file mode 100644 index 0000000..ea85e22 --- /dev/null +++ b/templates/kindergarten/auth.ninjatpl @@ -0,0 +1,13 @@ + + +{{ head_html|safe }} + + {{ admin_banner_html|safe }} +
{{ slots.header|safe }}
+
+
{% if slots.main %}{{ slots.main|safe }}{% else %}

Sign-in form goes here.

{% endif %}
+
+
{{ slots.footer|safe }}
+ {{ body_end_html|safe }} + + diff --git a/templates/kindergarten/blog-index.ninjatpl b/templates/kindergarten/blog-index.ninjatpl new file mode 100644 index 0000000..c31f398 --- /dev/null +++ b/templates/kindergarten/blog-index.ninjatpl @@ -0,0 +1,11 @@ + + +{{ head_html|safe }} + + {{ admin_banner_html|safe }} +
{{ slots.header|safe }}
+
{% if slots.main %}{{ slots.main|safe }}{% else %}
No stories yet — check back soon.
{% endif %}
+
{{ slots.footer|safe }}
+ {{ body_end_html|safe }} + + diff --git a/templates/kindergarten/contact.ninjatpl b/templates/kindergarten/contact.ninjatpl new file mode 100644 index 0000000..3087ad7 --- /dev/null +++ b/templates/kindergarten/contact.ninjatpl @@ -0,0 +1,11 @@ + + +{{ head_html|safe }} + + {{ admin_banner_html|safe }} +
{{ slots.header|safe }}
+
{% if slots.main %}{{ slots.main|safe }}{% else %}
Add a contact form to this page.
{% endif %}
+
{{ slots.footer|safe }}
+ {{ body_end_html|safe }} + + diff --git a/templates/kindergarten/default.ninjatpl b/templates/kindergarten/default.ninjatpl index 9c074c2..f49caad 100644 --- a/templates/kindergarten/default.ninjatpl +++ b/templates/kindergarten/default.ninjatpl @@ -1,13 +1,11 @@ - + {{ head_html|safe }} - + {{ admin_banner_html|safe }} -
{{ slots.header|safe }}
-
- {% if slots.main %}{{ slots.main|safe }}{% else %}
No content blocks assigned to this page.
{% endif %} -
-
{{ slots.footer|safe }}
+
{{ slots.header|safe }}
+
{% if slots.main %}{{ slots.main|safe }}{% else %}
No content blocks assigned to this page yet.
{% endif %}
+
{{ slots.footer|safe }}
{{ body_end_html|safe }} diff --git a/templates/kindergarten/full-width.ninjatpl b/templates/kindergarten/full-width.ninjatpl index ff0bce7..c97dde0 100644 --- a/templates/kindergarten/full-width.ninjatpl +++ b/templates/kindergarten/full-width.ninjatpl @@ -1,13 +1,11 @@ - + {{ head_html|safe }} - + {{ admin_banner_html|safe }} -
{{ slots.header|safe }}
-
- {% if slots.main %}{{ slots.main|safe }}{% else %}
No content blocks assigned to this page.
{% endif %} -
-
{{ slots.footer|safe }}
+
{{ slots.header|safe }}
+
{% if slots.main %}{{ slots.main|safe }}{% else %}
No content blocks assigned to this page yet.
{% endif %}
+
{{ slots.footer|safe }}
{{ body_end_html|safe }} diff --git a/templates/kindergarten/landing.ninjatpl b/templates/kindergarten/landing.ninjatpl index 18d3b7f..220ec3d 100644 --- a/templates/kindergarten/landing.ninjatpl +++ b/templates/kindergarten/landing.ninjatpl @@ -1,15 +1,11 @@ - + {{ head_html|safe }} - + {{ admin_banner_html|safe }} -
{{ slots.header|safe }}
-
{{ slots.hero|safe }}
-
- {% if slots.main %}{{ slots.main|safe }}{% endif %} -
-
{{ slots.cta|safe }}
-
{{ slots.footer|safe }}
+
{{ slots.header|safe }}
+
{% if slots.main %}{{ slots.main|safe }}{% else %}
No content blocks assigned to this page yet.
{% endif %}
+
{{ slots.footer|safe }}
{{ body_end_html|safe }} diff --git a/templates/overrides/kindergarten/announcement-bar.ninjatpl b/templates/overrides/kindergarten/announcement-bar.ninjatpl new file mode 100644 index 0000000..4ffb8c6 --- /dev/null +++ b/templates/overrides/kindergarten/announcement-bar.ninjatpl @@ -0,0 +1,21 @@ +{# announcement_bar (WO-TF-006): dismissable banner; dismissal persists in localStorage keyed by a content hash (variant + slugified message) so it reappears when the message changes; no-JS fallback is a static non-dismissable strip. Kindergarten override: mono broadcast readout — data-* + script preserved. #} +{% set anncls = "bg-primary/10 text-foreground" %} +{% if variant == "promo" %}{% set anncls = "bg-primary text-primary-foreground" %}{% elif variant == "alert" %}{% set anncls = "bg-destructive text-destructive-foreground" %}{% endif %} +
+
+

{{ message }}{% if linkText and linkUrl %} {{ linkText }}{% endif %}

+{% if dismissable %}{% endif %} +
+
+{% if dismissable %}{% endif %} diff --git a/templates/overrides/kindergarten/audio-embed.ninjatpl b/templates/overrides/kindergarten/audio-embed.ninjatpl new file mode 100644 index 0000000..f4689ab --- /dev/null +++ b/templates/overrides/kindergarten/audio-embed.ninjatpl @@ -0,0 +1,16 @@ +{# audio-embed kindergarten override: neon panel, mono meta. Facade script + data-bn-* attributes + {% img %} preserved byte-for-byte. #} +
{% if artwork %}
{% img artwork alt=title|default:"" class="h-full w-full object-cover" %}
{% endif %}
{% if title %}

{{ title }}

{% endif %}{% if artist %}

{{ artist }}

{% endif %}
{% if download %}::lucide:download:sm:: Download{% endif %}
diff --git a/templates/overrides/kindergarten/auth-form.ninjatpl b/templates/overrides/kindergarten/auth-form.ninjatpl new file mode 100644 index 0000000..c8a870c --- /dev/null +++ b/templates/overrides/kindergarten/auth-form.ninjatpl @@ -0,0 +1,77 @@ +{# auth-form kindergarten override — reproduces the builtin's security-critical wiring: hardcoded HTMX endpoints /api/auth/login + /api/auth/register (target #auth-message), /forgot-password link, OAuth start URLs gated on context.site.*, and the exact Cap widget markup when captchaEnabled. Only the chrome is themed. #} +{% if context.isPublicLoggedIn %} +
+

Session active as {{ context.publicUsername }}

+ My Account +
+{% else %} +{% set loginActive = "border-primary text-primary" %} +{% set registerActive = "border-transparent text-muted-foreground" %} +{% set loginHidden = "" %} +{% set registerHidden = "hidden" %} +{% if default_tab == "register" %}{% set loginActive = "border-transparent text-muted-foreground" %}{% set registerActive = "border-primary text-primary" %}{% set loginHidden = "hidden" %}{% set registerHidden = "" %}{% endif %} +{% set tabbase = "flex-1 py-2 text-center kg-mono text-sm font-medium border-b-2 transition-colors" %} +{% macro captcha() %} + + +{% endmacro %} +{% macro social() %}{% if show_social != false and (context.site.google_oauth_client_id or context.site.facebook_app_id) %}
+

or
+ {% if context.site.google_oauth_client_id %}Continue with Google{% endif %} + {% if context.site.facebook_app_id %}Continue with Facebook{% endif %} +
{% endif %}{% endmacro %} +
+
+
+ + +
+
+
+
+
+ {% if captchaEnabled %}{{ captcha() }}{% endif %} + +

Forgot password?

+
+ {{ social() }} +
+
+
+
+
+
+ {% if captchaEnabled %}{{ captcha() }}{% endif %} + +
+ {{ social() }} +
+
+ +{% endif %} diff --git a/templates/overrides/kindergarten/auth-status.ninjatpl b/templates/overrides/kindergarten/auth-status.ninjatpl new file mode 100644 index 0000000..752cf51 --- /dev/null +++ b/templates/overrides/kindergarten/auth-status.ninjatpl @@ -0,0 +1,36 @@ +{# auth-status kindergarten override — reproduces the builtin's hardcoded wiring: /login + /login?tab=register when logged out; /account, /account/reviews, and hx-post /api/auth/logout dropdown when logged in; hx-post /api/auth/resend-verification banner when unverified. State from context.*. #} +{% if not context.isPublicLoggedIn %} +
+ Login + Sign Up +
+{% else %} +{% if not context.publicEmailVerified %}
+ Verify your email address. +
{% endif %} +
+ + + +
+{% endif %} diff --git a/templates/overrides/kindergarten/author-bio-hero.ninjatpl b/templates/overrides/kindergarten/author-bio-hero.ninjatpl new file mode 100644 index 0000000..3d26615 --- /dev/null +++ b/templates/overrides/kindergarten/author-bio-hero.ninjatpl @@ -0,0 +1,21 @@ +{# author-bio-hero (kindergarten override) — author-page header from the `authors` provider. #} +{% if author %} +
+
+{% if showAvatar and author.avatar_url %}
{% img author.avatar_url alt=author.display_name class="h-full w-full object-cover" %}
{% endif %} +

{{ author.display_name }}

+{% if author.job_title or author.company %}

{{ author.job_title }}{% if author.job_title and author.company %} · {% endif %}{{ author.company }}

{% endif %} +{% if author.bio %}

{{ author.bio }}

{% endif %} +
+{% if showPostCount %}{{ author.post_count|default:0 }} post{% if author.post_count != 1 %}s{% endif %}{% endif %} +{% if author.location %}{{ author.location }}{% endif %} +
+{% if showSocial %}
+{% if author.website_url %}{% endif %} +{% if author.twitter_handle %}{% endif %} +{% if author.linkedin_url %}{% endif %} +{% if author.github_url %}{% endif %} +
{% endif %} +
+
+{% endif %} diff --git a/templates/overrides/kindergarten/author-bio.ninjatpl b/templates/overrides/kindergarten/author-bio.ninjatpl new file mode 100644 index 0000000..8705078 --- /dev/null +++ b/templates/overrides/kindergarten/author-bio.ninjatpl @@ -0,0 +1,13 @@ +{# author-bio (kindergarten override) — byline card sourced from the `post` provider's author_* fields. #} +{% if post.author_name %} + +{% endif %} diff --git a/templates/overrides/kindergarten/blog-hero.ninjatpl b/templates/overrides/kindergarten/blog-hero.ninjatpl new file mode 100644 index 0000000..6cb83c2 --- /dev/null +++ b/templates/overrides/kindergarten/blog-hero.ninjatpl @@ -0,0 +1,23 @@ +{# blog-hero (kindergarten override) — blog-index header + featured posts from the `posts` provider. #} +
+
+
+{% if title %}

{{ title }}

{% endif %} +{% if subtitle %}

{{ subtitle }}

{% endif %} +
+{% if posts %} +
+{% for post in posts %} + +{% endfor %} +
+{% endif %} +
+
diff --git a/templates/overrides/kindergarten/breadcrumbs.ninjatpl b/templates/overrides/kindergarten/breadcrumbs.ninjatpl new file mode 100644 index 0000000..e6980af --- /dev/null +++ b/templates/overrides/kindergarten/breadcrumbs.ninjatpl @@ -0,0 +1,15 @@ +{# breadcrumbs (kindergarten override) — trail from the `breadcrumbs` provider (page ancestry). #} +{# showHome prepends a Home link; separator is text ("/") for default or a chevron #} +{# for the arrows template. The trailing is_current item renders as plain text. #} +{% set arrows = template == "arrows" %} + diff --git a/templates/overrides/kindergarten/button.ninjatpl b/templates/overrides/kindergarten/button.ninjatpl index 3dae610..0990930 100644 --- a/templates/overrides/kindergarten/button.ninjatpl +++ b/templates/overrides/kindergarten/button.ninjatpl @@ -1 +1,2 @@ -{% if text %}{% set lbl = text %}{% else %}{% set lbl = label %}{% endif %}{% if lbl %}{{ lbl }}{% else %}Add label and link to render this button.{% endif %} +{# kindergarten button — keep the {% button %} tag intact (new fields: label / link / style / size / icon / iconPosition / newTab); wrap in a rounded kg-pop bounce so the host-rendered button gets the playful hover. #} +{% with align=align|default:"left" %}
{% if newTab %}{% button style|default:"primary" text=label|default:"Let's go" href=link|default:"#" size=size|default:"md" icon=icon|default:"" icon_position=iconPosition|default:"left" target="_blank" %}{% else %}{% button style|default:"primary" text=label|default:"Let's go" href=link|default:"#" size=size|default:"md" icon=icon|default:"" icon_position=iconPosition|default:"left" %}{% endif %}
{% endwith %} diff --git a/templates/overrides/kindergarten/card.ninjatpl b/templates/overrides/kindergarten/card.ninjatpl index e2e2d1e..6783733 100644 --- a/templates/overrides/kindergarten/card.ninjatpl +++ b/templates/overrides/kindergarten/card.ninjatpl @@ -1 +1,2 @@ -
{% if title %}

{{ title }}

{% endif %}{% if body %}
{{ body|safe }}
{% endif %}{% if not title and not body %}
Add a title or body to fill this card.
{% endif %}
+{# kindergarten card — crayon-bordered sticker panel; new builtin fields layout / link / media / title / text / linkLabel (reconciled from legacy body/image). #} +{% with layout=layout|default:"vertical" %}{% endwith %} diff --git a/templates/overrides/kindergarten/category-list.ninjatpl b/templates/overrides/kindergarten/category-list.ninjatpl new file mode 100644 index 0000000..fa93587 --- /dev/null +++ b/templates/overrides/kindergarten/category-list.ninjatpl @@ -0,0 +1,18 @@ +{# category-list (kindergarten override) — from the `categories` provider. list or pills. #} +{% set pills = style == "pills" %} +
+{% if heading %}

{{ heading }}

{% endif %} +{% if pills %} +
+{% for cat in categories %} +{{ cat.name }}{% if showCount %}{{ cat.post_count }}{% endif %} +{% endfor %} +
+{% else %} + +{% endif %} +
diff --git a/templates/overrides/kindergarten/contact-card.ninjatpl b/templates/overrides/kindergarten/contact-card.ninjatpl new file mode 100644 index 0000000..3f7e837 --- /dev/null +++ b/templates/overrides/kindergarten/contact-card.ninjatpl @@ -0,0 +1,18 @@ +{# contact-card kindergarten override: neon channel panels. All field reads + control flow preserved. #} +
+
+ {% if heading %}

{{ heading }}

{% endif %} + {% if description %}

{{ description }}

{% endif %} +
+ {% for channel in channels %} +
+ {% if channel.icon %}{% endif %} +
+ {% if channel.label %}
{{ channel.label }}
{% endif %} + {% if channel.value %}
{% if channel.link %}{{ channel.value }}{% else %}{{ channel.value }}{% endif %}
{% endif %} +
+
+ {% endfor %} +
+
+
diff --git a/templates/overrides/kindergarten/contact-form.ninjatpl b/templates/overrides/kindergarten/contact-form.ninjatpl new file mode 100644 index 0000000..5885984 --- /dev/null +++ b/templates/overrides/kindergarten/contact-form.ninjatpl @@ -0,0 +1,40 @@ +{# contact-form kindergarten override: terminal console chrome. The {% form %} block, honeypot, field loop, form.submitted/errors reads and _block_id/bid submit wiring are preserved byte-for-byte — only presentational classes change. #} +{% with bid=_block_id|default:context.blockId %} +
+
+ {% if form.submitted %} +
+
+

{{ form.message|default:successMessage|default:"Thanks — your message has been sent." }}

+
+ {% else %} + {% if heading %}

{{ heading }}

{% endif %} + {% if description %}

{{ description }}

{% endif %} + {% if form.errors._form %}
{{ form.errors._form }}
{% endif %} + {% with formAction="/api/blocks/"|add:bid|add:"/submit" formTarget="#contact-form-"|add:bid %} + {% form action=formAction hx_target=formTarget hx_swap="outerHTML" class="mt-8 grid grid-cols-1 gap-5 sm:grid-cols-2" %} + {% for field in fields %} +
+ {% if field.label %}{% endif %} + {% if field.type == "textarea" %} + + {% elif field.type == "select" %} + + {% else %} + + {% endif %} +
+ {% endfor %} + +
+ +
+ {% endform %} + {% endwith %} + {% endif %} +
+
+{% endwith %} diff --git a/templates/overrides/kindergarten/countdown.ninjatpl b/templates/overrides/kindergarten/countdown.ninjatpl new file mode 100644 index 0000000..964f3aa --- /dev/null +++ b/templates/overrides/kindergarten/countdown.ninjatpl @@ -0,0 +1,49 @@ +{# countdown kindergarten override: neon HUD digit boxes, heading. Timer script + data attributes preserved byte-for-byte. #} +
+
+ {% if heading %}

{{ heading }}

{% endif %} + {% if intro %}

{{ intro }}

{% endif %} +
+
+
--{{ labelDays|default:"Days" }}
+
--{{ labelHours|default:"Hours" }}
+
--{{ labelMinutes|default:"Minutes" }}
+
--{{ labelSeconds|default:"Seconds" }}
+
+ +
+ +
+
+ diff --git a/templates/overrides/kindergarten/cta.ninjatpl b/templates/overrides/kindergarten/cta.ninjatpl new file mode 100644 index 0000000..8426816 --- /dev/null +++ b/templates/overrides/kindergarten/cta.ninjatpl @@ -0,0 +1,15 @@ +{# cta kindergarten override: scanline overlay, heading, kg-pop CTAs. All field reads preserved. #} +
+
+
+
+ {% if heading %}

{{ heading }}

{% endif %} + {% if text %}

{{ text }}

{% endif %} +
+
+ {% if primaryUrl and primaryLabel %}{{ primaryLabel }}{% endif %} + {% if secondaryUrl and secondaryLabel %}{{ secondaryLabel }}{% endif %} +
+
+
+
diff --git a/templates/overrides/kindergarten/divider.ninjatpl b/templates/overrides/kindergarten/divider.ninjatpl new file mode 100644 index 0000000..74fdfb7 --- /dev/null +++ b/templates/overrides/kindergarten/divider.ninjatpl @@ -0,0 +1,2 @@ +{# kindergarten divider — neon gradient rule / primary-tinted variants #} +{% with variant=variant|default:"line" weight=weight|default:"thin" width=width|default:"full" %}{% if variant == "ornament" %}{% elif variant == "spacer-rule" %}{% else %}{% endif %}{% endwith %} diff --git a/templates/overrides/kindergarten/event-list.ninjatpl b/templates/overrides/kindergarten/event-list.ninjatpl new file mode 100644 index 0000000..b430c91 --- /dev/null +++ b/templates/overrides/kindergarten/event-list.ninjatpl @@ -0,0 +1,41 @@ +{# event-list kindergarten override: neon cards, mono datetime. All field reads + control flow preserved. #} +
+
+ {% if heading %}

{{ heading }}

{% endif %} + {% if intro %}

{{ intro }}

{% endif %} + {% if layout == "cards" %} +
+ {% for event in events %}{% if not (hidePast and event.past) %} +
+ {% if event.media %}
{% img event.media alt=event.title class="h-full w-full object-cover" %}
{% endif %} +
+
{{ event.start }}{% if event.end %} – {{ event.end }}{% endif %}{% if event.timezone %} {{ event.timezone }}{% endif %}
+

{{ event.title }}

+ {% if event.venue %}

{{ event.venue }}

{% endif %} + {% if event.description %}

{{ event.description }}

{% endif %} + {% if event.link %}{{ event.linkLabel|default:"Details" }}{% endif %} +
+
+ {% endif %}{% endfor %} +
+ {% else %} +
    + {% for event in events %}{% if not (hidePast and event.past) %} +
  • +
    +
    {{ event.start }}
    + {% if event.end %}
    to {{ event.end }}
    {% endif %} + {% if event.timezone %}
    {{ event.timezone }}
    {% endif %} +
    +
    +

    {{ event.title }}

    + {% if event.venue %}

    {{ event.venue }}

    {% endif %} + {% if event.description %}

    {{ event.description }}

    {% endif %} +
    + {% if event.link %}{{ event.linkLabel|default:"Details" }}{% endif %} +
  • + {% endif %}{% endfor %} +
+ {% endif %} +
+
diff --git a/templates/overrides/kindergarten/faq.ninjatpl b/templates/overrides/kindergarten/faq.ninjatpl new file mode 100644 index 0000000..73ba402 --- /dev/null +++ b/templates/overrides/kindergarten/faq.ninjatpl @@ -0,0 +1,18 @@ +{# faq kindergarten override: heading, neon-tinted dividers, display questions. Chevron svg, {% for %}, and answer prose preserved. #} +
+
+ {% if heading %}

{{ heading }}

{% endif %} + {% if intro %}

{{ intro }}

{% endif %} +
+ {% for item in items %} +
+ + {{ item.question }} + + +
{{ item.answer|safe }}
+
+ {% endfor %} +
+
+
diff --git a/templates/overrides/kindergarten/feature-grid.ninjatpl b/templates/overrides/kindergarten/feature-grid.ninjatpl new file mode 100644 index 0000000..dd46d53 --- /dev/null +++ b/templates/overrides/kindergarten/feature-grid.ninjatpl @@ -0,0 +1,18 @@ +{# feature_grid kindergarten override: scanline hero overlay, mono eyebrow, heading, neon HUD cards. All field reads + control flow preserved. #} +
+
+ {% if eyebrow %}

{{ eyebrow }}

{% endif %} + {% if heading %}

{{ heading }}

{% endif %} + {% if intro %}

{{ intro }}

{% endif %} +
+ {% for item in items %} +
+ {% if item.icon %}
{% endif %} +

{% if item.linkUrl %}{{ item.title }}{% else %}{{ item.title }}{% endif %}

+ {% if item.text %}

{{ item.text }}

{% endif %} + {% if item.linkUrl and item.linkLabel %}{{ item.linkLabel }}{% endif %} +
+ {% endfor %} +
+
+
diff --git a/templates/overrides/kindergarten/featured-posts.ninjatpl b/templates/overrides/kindergarten/featured-posts.ninjatpl new file mode 100644 index 0000000..fbddb4d --- /dev/null +++ b/templates/overrides/kindergarten/featured-posts.ninjatpl @@ -0,0 +1,23 @@ +{# featured-posts (kindergarten override) — grid of cards from the `posts` provider #} +{# (params: limit, featured=true). Neon HUD card: image, title, excerpt, meta. #} +
+
+{% if heading %}

{{ heading }}

{% endif %} +
+{% for post in posts %} +
+{% if showFeaturedImage and post.featured_image_url %}{% img post.featured_image_url alt=post.title class="h-full w-full object-cover transition-transform duration-300 group-hover:scale-105" %}{% endif %} +
+

{{ post.title }}

+{% if showExcerpt and post.excerpt %}

{{ post.excerpt }}

{% endif %} +
+{% if showAuthor and post.author_name %}{{ post.author_name }}{% endif %} +{% if showDate and post.published_at %}{{ post.published_at|date:"M j, Y" }}{% endif %} +{% if post.reading_time %}{{ post.reading_time }} min read{% endif %} +
+
+
+{% endfor %} +
+
+
diff --git a/templates/overrides/kindergarten/footer.ninjatpl b/templates/overrides/kindergarten/footer.ninjatpl new file mode 100644 index 0000000..6e55255 --- /dev/null +++ b/templates/overrides/kindergarten/footer.ninjatpl @@ -0,0 +1,39 @@ +{# footer (WO-TF-006): layouts columns|minimal|centered; menu columns, legal bar, and social row from the `footer_menus` provider; newsletter section reuses the {% signup_form %} core (no duplicated form). Kindergarten override: mono column headers, neon top rule, display wordmark. #} +{% set fl = layout %} +{% if not fl %}{% if template == "centered" %}{% set fl = "centered" %}{% else %}{% set fl = "columns" %}{% endif %}{% endif %} +{% set fbg = backgroundColor|default:"bg-muted/50" %} +{% macro sociallinks(cls) %}{% if social_items %}{% endif %}{% endmacro %} +{% macro newsletter() %}{% if showNewsletter %}

{{ newsletterTitle|default:"Stay in the loop" }}

{% if newsletterDescription %}

{{ newsletterDescription }}

{% endif %}
{% signup_form list=newsletterListSlug|default:"main" source="footer" submit=newsletterButtonText|default:"Subscribe" %}
{% endif %}{% endmacro %} +{% macro legalbar() %}

{% if copyright %}© {{ copyright }}{% else %}© {{ companyName }}{% endif %}

{% if legal_items %}{% endif %}
{% endmacro %} +
+
+{% if fl == "minimal" %} +
+
{% if logo %}{{ companyName }}{% elif companyName %}{{ companyName }}{% endif %}{% if tagline %}{% endif %}
+{{ sociallinks("") }} +
+{{ legalbar() }} +{% elif fl == "centered" %} +
+{% if logo %}{{ companyName }}{% elif companyName %}{{ companyName }}{% endif %} +{% if tagline %}

{{ tagline }}

{% endif %} +{% if columns %}{% endif %} +{{ newsletter() }} +{{ sociallinks("justify-center") }} +
+{{ legalbar() }} +{% else %} +
+
+{% if logo %}{{ companyName }}{% elif companyName %}{{ companyName }}{% endif %} +{% if tagline %}

{{ tagline }}

{% endif %} +{% if address or email or phone %}
{% if address %}
{{ address }}
{% endif %}{% if email %}{% endif %}{% if phone %}{% endif %}
{% endif %} +{{ sociallinks("mt-5") }} +
+{% for col in columns %}

{{ col.header }}

{% endfor %} +{% if showNewsletter %}
{{ newsletter() }}
{% endif %} +
+{{ legalbar() }} +{% endif %} +
+
diff --git a/templates/overrides/kindergarten/gallery.ninjatpl b/templates/overrides/kindergarten/gallery.ninjatpl new file mode 100644 index 0000000..6293d4e --- /dev/null +++ b/templates/overrides/kindergarten/gallery.ninjatpl @@ -0,0 +1,52 @@ +{# gallery kindergarten override: crayon-bordered tiles. Lightbox script + data-bn-* attributes + {% img %} preserved byte-for-byte. #} +{% with layout=layout|default:"grid" gap=gap|default:"md" cols=columns|default:3 %}{% if lightbox %}{% endif %}{% endwith %} diff --git a/templates/overrides/kindergarten/heading.ninjatpl b/templates/overrides/kindergarten/heading.ninjatpl index aaa106d..0ae1de0 100644 --- a/templates/overrides/kindergarten/heading.ninjatpl +++ b/templates/overrides/kindergarten/heading.ninjatpl @@ -1 +1,7 @@ -{% if step|integer >= 1 and step|integer <= 9 %}
{% endif %}{% if level|integer == 1 %}

{{ text }}

{% elif level|integer == 3 %}

{{ text }}

{% elif level|integer == 4 %}

{{ text }}

{% elif level|integer == 5 %}
{{ text }}
{% elif level|integer == 6 %}
{{ text }}
{% else %}

{{ text }}

{% endif %}{% if step|integer >= 1 and step|integer <= 9 %}
{% endif %} +{# kindergarten heading — kg-display everywhere; on h1/h2 only #} +{% if level == 1 %}

{{ text }}

+{% elif level == 3 %}

{{ text }}

+{% elif level == 4 %}

{{ text }}

+{% elif level == 5 %}
{{ text }}
+{% elif level == 6 %}
{{ text }}
+{% else %}

{{ text }}

{% endif %} diff --git a/templates/overrides/kindergarten/hero.ninjatpl b/templates/overrides/kindergarten/hero.ninjatpl new file mode 100644 index 0000000..4694957 --- /dev/null +++ b/templates/overrides/kindergarten/hero.ninjatpl @@ -0,0 +1,94 @@ +{# hero kindergarten override (GO-BIG showpiece): a bouncing mascot, wiggling ABC letters, and a dependency-free confetti canvas fill the panel when there is no backgroundImage. The canvas lazy-inits on view, honours prefers-reduced-motion, and degrades to a static confetti wash with JS off. All builtin field reads + macro + branch structure are preserved from the default hero — only the styling changes. #} +{% set eyebrowText = eyebrow.text|default:eyebrow %} +{% set headlineText = headline.text|default:headline %} +{% set subText = subheadline.text|default:subheadline %} +{% set descText = description.text|default:description %} +{% set pText = ctaPrimary.text %} +{% set pUrl = ctaPrimary.url %} +{% set sText = ctaSecondary.text %} +{% set sUrl = ctaSecondary.url %} +{% set variant = template|default:"centered" %} +{% set hasImage = backgroundImage %} +{% set showOverlay = hasImage and overlayColor != "none" and overlayColor != "" %} +{% macro playmascot() %}{% endmacro %} +{% macro herocontent(align) %} +{% if eyebrowText %}{{ eyebrowText }}{% endif %} +{% if headlineText %}

{{ headlineText }}

{% endif %} + +{% if subText %}

{{ subText }}

{% endif %} +{% if descText %}

{{ descText }}

{% endif %} +{% if pText or sText %}
+{% if pText and pUrl %}{{ pText }}{% endif %} +{% if sText and sUrl %}{{ sText }}{% endif %} +
{% endif %} +{% if trustLogos %}
+

Loved by families at

+
{% for logo in trustLogos %}{% if logo.url %}{% img logo.image alt=logo.alt class="h-8 w-auto" %}{% else %}{% img logo.image alt=logo.alt class="h-8 w-auto opacity-70" %}{% endif %}{% endfor %}
+
{% endif %} +{% endmacro %} +{% if variant == "split" %} +
+
{% if hasImage %}{% img backgroundImage alt=headlineText class="absolute inset-0 h-full w-full object-cover" layout="hero" %}{% else %}{{ playmascot() }}{% endif %}
+
{{ herocontent("left") }}
+
+{% else %} +{% set align = "center" %} +{% if variant == "left-aligned" %}{% set align = "left" %}{% endif %} +
+{% if hasImage %}
{% img backgroundImage alt=headlineText class="h-full w-full object-cover" layout="hero" %}{% if showOverlay %}
{% endif %}
{% else %}
{{ playmascot() }}
{% endif %} +
{{ herocontent(align) }}
+
+{% endif %} + diff --git a/templates/overrides/kindergarten/hours-location.ninjatpl b/templates/overrides/kindergarten/hours-location.ninjatpl new file mode 100644 index 0000000..843f723 --- /dev/null +++ b/templates/overrides/kindergarten/hours-location.ninjatpl @@ -0,0 +1,31 @@ +{# hours-location kindergarten override: mono readout table, neon map panel. Map lazy-load button + inline handler preserved. #} +
+
+ {% if heading %}

{{ heading }}

{% endif %} +
+
+ {% if hours %} + + + {% for row in hours %} + + + + + {% endfor %} + +
{{ row.day }}{% if row.closed %}Closed{% else %}{{ row.hours }}{% endif %}
+ {% endif %} + {% if specialHoursNote %}

{{ specialHoursNote }}

{% endif %} + {% if address %}
Address
{{ address }}
{% endif %} + {% if phone %}{% endif %} + {% if directionsUrl %}Get directions{% endif %} +
+ {% if mapEmbedUrl %} +
+ +
+ {% endif %} +
+
+
diff --git a/templates/overrides/kindergarten/image.ninjatpl b/templates/overrides/kindergarten/image.ninjatpl new file mode 100644 index 0000000..2baa284 --- /dev/null +++ b/templates/overrides/kindergarten/image.ninjatpl @@ -0,0 +1,2 @@ +{# kindergarten image — crayon-bordered frame, mono caption; all size/fit branches preserved #} +{% with size=size|default:"full" align=align|default:"center" aspectRatio=aspectRatio|default:"auto" objectFit=objectFit|default:"cover" rounded=rounded|default:"lg" shadow=shadow|default:"none" %}
{% if objectFit == "contain" %}{% img src alt=alt class="w-full h-full object-contain" layout=size %}{% elif objectFit == "fill" %}{% img src alt=alt class="w-full h-full object-fill" layout=size %}{% elif objectFit == "none" %}{% img src alt=alt class="w-full h-full object-none" layout=size %}{% else %}{% img src alt=alt class="w-full h-full object-cover" layout=size %}{% endif %}
{% if caption %}
{{ caption }}
{% endif %}
{% endwith %} diff --git a/templates/overrides/kindergarten/logo-strip.ninjatpl b/templates/overrides/kindergarten/logo-strip.ninjatpl new file mode 100644 index 0000000..813df74 --- /dev/null +++ b/templates/overrides/kindergarten/logo-strip.ninjatpl @@ -0,0 +1,16 @@ +{# logo_strip kindergarten override: mono kicker. Marquee classes, {% img %}, and keyframes {% endif %} diff --git a/templates/overrides/kindergarten/menu-list.ninjatpl b/templates/overrides/kindergarten/menu-list.ninjatpl new file mode 100644 index 0000000..b40d3fc --- /dev/null +++ b/templates/overrides/kindergarten/menu-list.ninjatpl @@ -0,0 +1,30 @@ +{# menu-list kindergarten override: mono price readouts, neon section rules. All field reads + control flow preserved. #} +
+
+ {% if heading %}

{{ heading }}

{% endif %} + {% if intro %}

{{ intro }}

{% endif %} +
+ {% for section in sections %} + + {% endfor %} +
+ {% if footnote %}

{{ footnote }}

{% endif %} +
+
diff --git a/templates/overrides/kindergarten/navbar.ninjatpl b/templates/overrides/kindergarten/navbar.ninjatpl new file mode 100644 index 0000000..3d5c90c --- /dev/null +++ b/templates/overrides/kindergarten/navbar.ninjatpl @@ -0,0 +1,104 @@ +{# navbar (WO-TF-005): variants standard|centered-logo|split|transparent-over-hero; sticky none|sticky|shrink-on-scroll; dependency-free off-canvas drawer (pure-CSS baseline + progressive-JS focus-trap/Escape/scroll-lock); menu data from the `menus` provider. Kindergarten override: mono nav links, neon dropdown panels, CTA — drawer ids/style/script/data-bn-* preserved byte-for-byte. #} +{% set nv = variant %} +{% if not nv %}{% if template == "centered" %}{% set nv = "centered-logo" %}{% elif template == "transparent" %}{% set nv = "transparent-over-hero" %}{% else %}{% set nv = "standard" %}{% endif %}{% endif %} +{% set sb = stickyBehavior %} +{% if not sb %}{% if sticky %}{% set sb = "sticky" %}{% else %}{% set sb = "none" %}{% endif %}{% endif %} +{% set use_mega = megaMenu or template == "mega" %} +{% set is_transparent = nv == "transparent-over-hero" %} +{% set cta_text = cta.text|default:ctaLabel %} +{% set cta_url = cta.url|default:ctaUrl %} +{% set bg = backgroundColor|default:"bg-background" %} +{% macro navlink(item) %}{{ item.label }}{% endmacro %} +{% macro dropdown(item) %}
+ +{% if use_mega %} +{% else %}{% endif %} +
{% endmacro %} +{% macro brand(size) %}{% if logoType == "text" and logoText.text %}{{ logoText.text }}{% elif logo %}{{ alt|default:companyName }}{% elif companyName %}{{ companyName }}{% endif %}{% endmacro %} +{% macro ctabtn(extra) %}{% if cta_text %}{{ cta_text }}{% endif %}{% endmacro %} + + + diff --git a/templates/overrides/kindergarten/newsletter-signup.ninjatpl b/templates/overrides/kindergarten/newsletter-signup.ninjatpl new file mode 100644 index 0000000..3fa5a5d --- /dev/null +++ b/templates/overrides/kindergarten/newsletter-signup.ninjatpl @@ -0,0 +1,12 @@ +{# newsletter-signup (kindergarten override) — the {% signup_form %} tag carries the #} +{# untouchable subscribe wiring (/api/lists/subscribe HTMX post + honeypot); #} +{# this template supplies only the kindergarten chrome around it. #} +
+
+
+{% if title %}

{{ title }}

{% endif %} +{% if description %}

{{ description }}

{% endif %} +
{% signup_form list=listSlug source=source submit=submitText success_message=successMessage placeholder=placeholder %}
+
+
+
diff --git a/templates/overrides/kindergarten/page-suggestions.ninjatpl b/templates/overrides/kindergarten/page-suggestions.ninjatpl new file mode 100644 index 0000000..8142c92 --- /dev/null +++ b/templates/overrides/kindergarten/page-suggestions.ninjatpl @@ -0,0 +1,12 @@ +{# page-suggestions kindergarten override — themed 404. NOTE: the builtin builds its "did you mean" list from a Postgres full-text query (SearchSimilarPages), which a codeless template cannot run — so this override renders the on-theme 404 (title / message / requested path / home CTA) without the dynamic suggestion list. Recorded as a known deviation in WO-TF-019. Content fields honored: title, emptyMessage. #} +
+
+ Oops +

{{ title|default:"This page went to play outside" }}

+

{{ emptyMessage|default:"We could not find that page. It may have moved, or maybe it never existed. Let us take you back to the start." }}

+ {% if context.path %}

You asked for: {{ context.path }}

{% endif %} +
+ Back home +
+
+
diff --git a/templates/overrides/kindergarten/password-reset-form.ninjatpl b/templates/overrides/kindergarten/password-reset-form.ninjatpl new file mode 100644 index 0000000..21521c0 --- /dev/null +++ b/templates/overrides/kindergarten/password-reset-form.ninjatpl @@ -0,0 +1,13 @@ +{# password-reset-form kindergarten override — reproduces the builtin's hardcoded wiring: hx-post /api/auth/request-password-reset (target #reset-message), back-to-login link /login. Only the chrome is themed. #} +
+
+
+

Reset Password

+

Enter your email address and we will send you a link to reset your password.

+
+
+ +
+

Back to login

+
+
diff --git a/templates/overrides/kindergarten/popular-posts.ninjatpl b/templates/overrides/kindergarten/popular-posts.ninjatpl new file mode 100644 index 0000000..9618561 --- /dev/null +++ b/templates/overrides/kindergarten/popular-posts.ninjatpl @@ -0,0 +1,16 @@ +{# popular-posts (kindergarten override) — compact ranked list from the `posts` provider. #} +
+{% if heading %}

{{ heading }}

{% endif %} +
    +{% for post in posts %} +
  1. +{% if showRank %}{{ forloop.Counter }}{% endif %} +{% if showThumbnail and post.featured_image_url %}{% img post.featured_image_url alt=post.title class="h-full w-full object-cover" %}{% endif %} +
    +

    {{ post.title }}

    +{% if post.published_at %}

    {{ post.published_at|date:"M j, Y" }}

    {% endif %} +
    +
  2. +{% endfor %} +
+
diff --git a/templates/overrides/kindergarten/post-hero.ninjatpl b/templates/overrides/kindergarten/post-hero.ninjatpl new file mode 100644 index 0000000..eec514a --- /dev/null +++ b/templates/overrides/kindergarten/post-hero.ninjatpl @@ -0,0 +1,21 @@ +{# post-hero (kindergarten override) — current post from the `post` provider. When the post #} +{# has a featured image it becomes an overlaid banner; otherwise a clean text hero. #} +{% set center = align != "left" %} +{% if post.featured_image_url %} +
+
{% img post.featured_image_url alt=post.featured_image_alt class="h-full w-full object-cover" layout="hero" %}
+
+

{{ post.title }}

+{% if showExcerpt and post.excerpt %}

{{ post.excerpt }}

{% endif %} +{% if showMeta %}
{% if post.author_name %}{{ post.author_name }}{% endif %}{% if post.published_at %}{{ post.published_at|date:"F j, Y" }}{% endif %}{% if post.reading_time %}{{ post.reading_time }} min read{% endif %}
{% endif %} +
+
+{% else %} +
+
+

{{ post.title }}

+{% if showExcerpt and post.excerpt %}

{{ post.excerpt }}

{% endif %} +{% if showMeta %}
{% if post.author_name %}{{ post.author_name }}{% endif %}{% if post.published_at %}{{ post.published_at|date:"F j, Y" }}{% endif %}{% if post.reading_time %}{{ post.reading_time }} min read{% endif %}
{% endif %} +
+
+{% endif %} diff --git a/templates/overrides/kindergarten/post-metadata.ninjatpl b/templates/overrides/kindergarten/post-metadata.ninjatpl new file mode 100644 index 0000000..484c5af --- /dev/null +++ b/templates/overrides/kindergarten/post-metadata.ninjatpl @@ -0,0 +1,9 @@ +{# post-metadata (kindergarten override) — author/date/reading-time row from the `post` provider. #} +{% if post %} +
+{% if showAuthor and post.author_name %}{% if showAvatar and post.author_avatar %}{% img post.author_avatar alt=post.author_name class="h-7 w-7 rounded-full border border-primary/40 object-cover" %}{% endif %}{{ post.author_name }}{% endif %} +{% if showDate and post.published_at %}{% endif %} +{% if showReadingTime and post.reading_time %}{{ post.reading_time }} min read{% endif %} +{% if post.categories %}{% for cat in post.categories %}{{ cat.name }}{% endfor %}{% endif %} +
+{% endif %} diff --git a/templates/overrides/kindergarten/pricing.ninjatpl b/templates/overrides/kindergarten/pricing.ninjatpl new file mode 100644 index 0000000..3ee297a --- /dev/null +++ b/templates/overrides/kindergarten/pricing.ninjatpl @@ -0,0 +1,25 @@ +{# pricing kindergarten override: scanline overlay, heading, HUD neon tier panels (magenta highlight / cyan standard), CTAs. Field reads + control flow preserved. #} +
+
+ {% if heading %}

{{ heading }}

{% endif %} + {% if intro %}

{{ intro }}

{% endif %} +
+ {% for tier in tiers %} +
+ {% if tier.badge %}{{ tier.badge }}{% endif %} +

{{ tier.name }}

+

+ {% if currency %}{{ currency }}{% else %}${% endif %}{{ tier.price }} + {% if tier.period %}{{ tier.period }}{% endif %} +

+ {% if tier.description %}

{{ tier.description }}

{% endif %} + {% if tier.features %}
    + {% for feature in tier.features %}
  • {{ feature }}
  • {% endfor %} +
{% endif %} + {% if tier.ctaUrl and tier.ctaLabel %}{{ tier.ctaLabel }}{% endif %} +
+ {% endfor %} +
+ {% if billingNote %}

{{ billingNote }}

{% endif %} +
+
diff --git a/templates/overrides/kindergarten/quote.ninjatpl b/templates/overrides/kindergarten/quote.ninjatpl new file mode 100644 index 0000000..3ed8108 --- /dev/null +++ b/templates/overrides/kindergarten/quote.ninjatpl @@ -0,0 +1,2 @@ +{# kindergarten quote — dashed neon left border; kg-display for pull; mono figcaption #} +{% with style=style|default:"block" align=align|default:"left" %}
{{ quote|safe }}{% if attribution or role or cite %}
{% if attribution %}{{ attribution }}{% endif %}{% if role %}{{ role }}{% endif %}{% if cite %}Source{% endif %}
{% endif %}
{% endwith %} diff --git a/templates/overrides/kindergarten/related-posts.ninjatpl b/templates/overrides/kindergarten/related-posts.ninjatpl new file mode 100644 index 0000000..052bb94 --- /dev/null +++ b/templates/overrides/kindergarten/related-posts.ninjatpl @@ -0,0 +1,17 @@ +{# related-posts (kindergarten override) — grid from the `posts` provider. #} +{% if posts %} +
+
+

{{ heading|default:"Related posts" }}

+
+{% for post in posts %} + +{% endfor %} +
+
+
+{% endif %} diff --git a/templates/overrides/kindergarten/rich-section.ninjatpl b/templates/overrides/kindergarten/rich-section.ninjatpl new file mode 100644 index 0000000..89cc2b8 --- /dev/null +++ b/templates/overrides/kindergarten/rich-section.ninjatpl @@ -0,0 +1,14 @@ +{# rich_section kindergarten override: mono eyebrow, heading, HUD-framed media, CTA. Field reads + {% img %} preserved. #} +
+
+
+ {% if eyebrow %}

{{ eyebrow }}

{% endif %} + {% if heading %}

{{ heading }}

{% endif %} + {% if body %}
{{ body|safe }}
{% endif %} + {% if ctaUrl and ctaLabel %}{{ ctaLabel }}{% endif %} +
+
+ {% if media %}
{% img media alt=mediaAlt class="w-full h-auto object-cover" %}
{% endif %} +
+
+
diff --git a/templates/overrides/kindergarten/rich-text.ninjatpl b/templates/overrides/kindergarten/rich-text.ninjatpl new file mode 100644 index 0000000..690d369 --- /dev/null +++ b/templates/overrides/kindergarten/rich-text.ninjatpl @@ -0,0 +1,2 @@ +{# kindergarten rich-text — body font via kg-prose #} +{% if html %}
{{ html|safe }}
{% endif %} diff --git a/templates/overrides/kindergarten/sidebar-nav.ninjatpl b/templates/overrides/kindergarten/sidebar-nav.ninjatpl new file mode 100644 index 0000000..fa31303 --- /dev/null +++ b/templates/overrides/kindergarten/sidebar-nav.ninjatpl @@ -0,0 +1,15 @@ +{# sidebar-nav (kindergarten override) — vertical menu from the `menus` provider. Variants #} +{# default | compact. highlight-current carried as a progressive-enhancement #} +{# script keyed on data-bn-sidebar-link (marks the anchor matching location.pathname). #} +{% set compact = template == "compact" %} + +{% if highlightCurrent %}{% endif %} diff --git a/templates/overrides/kindergarten/social-links.ninjatpl b/templates/overrides/kindergarten/social-links.ninjatpl new file mode 100644 index 0000000..1bae3e2 --- /dev/null +++ b/templates/overrides/kindergarten/social-links.ninjatpl @@ -0,0 +1,14 @@ +{# kindergarten social-links — mono label, sharp neon-bordered chips; icon sprites preserved #} + diff --git a/templates/overrides/kindergarten/stats.ninjatpl b/templates/overrides/kindergarten/stats.ninjatpl new file mode 100644 index 0000000..a619e4e --- /dev/null +++ b/templates/overrides/kindergarten/stats.ninjatpl @@ -0,0 +1,49 @@ +{# stats kindergarten override: scanline overlay, heading, HUD stat tiles, mono labels. data-stats + countUp script/attrs preserved byte-for-byte. #} +
+
+ {% if heading %}

{{ heading }}

{% endif %} + {% if intro %}

{{ intro }}

{% endif %} +
+ {% for item in items %} +
+
{% if item.prefix %}{{ item.prefix }}{% endif %}{{ item.value }}{% if item.suffix %}{{ item.suffix }}{% endif %}
+ {% if item.label %}
{{ item.label }}
{% endif %} +
+ {% endfor %} +
+
+
+{% if countUp %}{% endif %} diff --git a/templates/overrides/kindergarten/team.ninjatpl b/templates/overrides/kindergarten/team.ninjatpl new file mode 100644 index 0000000..c1f163e --- /dev/null +++ b/templates/overrides/kindergarten/team.ninjatpl @@ -0,0 +1,22 @@ +{# team kindergarten override: heading, dashed-neon HUD member panels (lime), mono roles. {% img %} + social icon svgs + control flow preserved. #} +
+
+ {% if heading %}

{{ heading }}

{% endif %} + {% if intro %}

{{ intro }}

{% endif %} +
+ {% for member in members %} +
+ {% if member.photo %}{% img member.photo alt=member.name class="h-full w-full object-cover" %}{% endif %} +
+ {% if member.name %}

{{ member.name }}

{% endif %} + {% if member.role %}

{{ member.role }}

{% endif %} + {% if member.bio %}

{{ member.bio }}

{% endif %} + {% if member.socials %}
+ {% for social in member.socials %}{% if social.url %}{% endif %}{% endfor %} +
{% endif %} +
+
+ {% endfor %} +
+
+
diff --git a/templates/overrides/kindergarten/testimonial.ninjatpl b/templates/overrides/kindergarten/testimonial.ninjatpl new file mode 100644 index 0000000..266f1eb --- /dev/null +++ b/templates/overrides/kindergarten/testimonial.ninjatpl @@ -0,0 +1,27 @@ +{# testimonial kindergarten override: heading, dashed-neon HUD quote cards. Rating svgs, {% img %}, and control flow preserved. #} +
+
+ {% if heading %}

{{ heading }}

{% endif %} +
+ {% for q in quotes %} +
+ {% if q.rating %}
+ + + + + +
{% endif %} +
{% if q.quote %}“{{ q.quote }}”{% endif %}
+
+ {% if q.avatar %}{% img q.avatar alt=q.name class="h-full w-full object-cover" %}{% endif %} + + {% if q.name %}{{ q.name }}{% endif %} + {% if q.role %}{{ q.role }}{% endif %} + +
+
+ {% endfor %} +
+
+
diff --git a/templates/overrides/kindergarten/text.ninjatpl b/templates/overrides/kindergarten/text.ninjatpl index 2e1cfd7..1ba6bad 100644 --- a/templates/overrides/kindergarten/text.ninjatpl +++ b/templates/overrides/kindergarten/text.ninjatpl @@ -1 +1,2 @@ -
{{ text|safe }}
+{# kindergarten text — body font via kg-prose #} +
{{ text|safe }}
diff --git a/templates/overrides/kindergarten/timeline.ninjatpl b/templates/overrides/kindergarten/timeline.ninjatpl new file mode 100644 index 0000000..36e7ad1 --- /dev/null +++ b/templates/overrides/kindergarten/timeline.ninjatpl @@ -0,0 +1,22 @@ +{# timeline kindergarten override: heading, mono step labels, neon markers. All field reads + control flow preserved. #} +
+
+ {% if heading %}

{{ heading }}

{% endif %} + {% if intro %}

{{ intro }}

{% endif %} +
    + {% for step in steps %} +
  1. + + {% if marker == "numbered" %}{{ forloop.counter }}{% elif marker == "dot" %}{% else %}{% endif %} + +
    + {% if step.label %}

    {{ step.label }}

    {% endif %} + {% if step.title %}

    {{ step.title }}

    {% endif %} + {% if step.text %}

    {{ step.text }}

    {% endif %} + {% if step.media %}
    {% img step.media alt=step.title class="w-full h-auto object-cover" %}
    {% endif %} +
    +
  2. + {% endfor %} +
+
+
diff --git a/templates/overrides/kindergarten/utility-bar.ninjatpl b/templates/overrides/kindergarten/utility-bar.ninjatpl new file mode 100644 index 0000000..bc7ab00 --- /dev/null +++ b/templates/overrides/kindergarten/utility-bar.ninjatpl @@ -0,0 +1,10 @@ +{# utility_bar (WO-TF-006): thin top contact strip; left/right item lists {icon,label,link}; icons render through the sprite pipeline via the ::pack:name:: shorthand. Kindergarten override: mono terminal readout, neon underline. #} +{% set ubcls = "bg-muted text-muted-foreground border-b border-primary/30" %} +{% if variant == "primary" %}{% set ubcls = "bg-primary text-primary-foreground" %}{% elif variant == "dark" %}{% set ubcls = "bg-foreground text-background" %}{% endif %} +{% macro ubitem(it) %}{% if it.link %}{% if it.icon %}::{{ it.icon }}::{% endif %}{% if it.label %}{{ it.label }}{% endif %}{% else %}{% if it.icon %}::{{ it.icon }}::{% endif %}{% if it.label %}{{ it.label }}{% endif %}{% endif %}{% endmacro %} +
+
+
{% for it in leftItems %}{{ ubitem(it) }}{% endfor %}
+
{% for it in rightItems %}{{ ubitem(it) }}{% endfor %}
+
+
diff --git a/templates/overrides/kindergarten/video-embed.ninjatpl b/templates/overrides/kindergarten/video-embed.ninjatpl new file mode 100644 index 0000000..0694c78 --- /dev/null +++ b/templates/overrides/kindergarten/video-embed.ninjatpl @@ -0,0 +1,23 @@ +{# video-embed kindergarten override: neon HUD frame, play button. Facade script + data-bn-* attributes + {% img %} preserved byte-for-byte. #} +{% with aspect=aspect|default:"16:9" %}
{% if caption %}
{{ caption }}
{% endif %}
{% endwith %}