[{"data":1,"prerenderedAt":1540},["ShallowReactive",2],{"ecosystem-page":3},{"_path":4,"_dir":5,"_draft":6,"_partial":6,"_locale":5,"title":7,"description":8,"body":9,"_type":1534,"_id":1535,"_source":1536,"_file":1537,"_stem":1538,"_extension":1539},"/ecosystem","",false,"Ecosystem Architecture","How production projects connect as one intentional platform — shared ingress, embeds, and ops habits.",{"type":10,"children":11,"toc":1510},"root",[12,20,24,31,363,372,375,381,397,408,426,451,454,467,476,494,517,520,533,542,567,592,607,610,623,632,641,651,666,669,682,691,716,731,734,740,898,901,907,913,980,986,1045,1051,1089,1092,1098,1287,1290,1296,1343,1346,1352,1360,1435,1442,1486,1489,1504],{"type":13,"tag":14,"props":15,"children":16},"element","p",{},[17],{"type":18,"value":19},"text","These projects share ingress patterns, embeddable services, and repeatable ops habits—not five unrelated demos. The portfolio is the hub; production apps feed data back into it and into GitHub.",{"type":13,"tag":21,"props":22,"children":23},"hr",{},[],{"type":13,"tag":25,"props":26,"children":28},"h2",{"id":27},"architecture-overview",[29],{"type":18,"value":30},"Architecture overview",{"type":13,"tag":32,"props":33,"children":37},"pre",{"className":34,"code":35,"language":36,"meta":5,"style":5},"language-mermaid shiki shiki-themes github-dark github-dark-dimmed","flowchart TB\n  Users[\"Users / browsers\"]\n\n  subgraph apps[\"Apps\"]\n    Portfolio[\"jovylle.com\u003Cbr/>Nuxt portfolio + widget\"]\n    Playbase[\"fast.jovylle.com\u003Cbr/>Playbase reaction game\"]\n    D1G[\"d1g.uk\u003Cbr/>Desert digging tool\"]\n    ChatW[\"chat-widget.uft1.com\u003Cbr/>Embeddable GPT chat\"]\n  end\n\n  subgraph shared[\"Shared services\"]\n    CDN[\"content.jovylle.com\u003Cbr/>Project catalog JSON\"]\n    Pocket[\"pocket.uft1.com\u003Cbr/>Notifications + highlights\"]\n    PMate[\"projectmate.uft1.com\u003Cbr/>Feedback & updates overlay\"]\n  end\n\n  subgraph platform[\"Platform layer\"]\n    Netlify[\"Netlify\u003Cbr/>Build + serverless functions\"]\n    GH[\"GitHub\u003Cbr/>Repos + Actions\"]\n  end\n\n  Obs[\"Umami analytics\u003Cbr/>jovylle.com\"]\n\n  Users --> Portfolio\n  Users --> Playbase\n  Users --> D1G\n  Users --> ChatW\n  Portfolio --> Netlify\n  Portfolio --> Playbase\n  Portfolio --> Pocket\n  Portfolio --> PMate\n  Portfolio --> CDN\n  Playbase --> GH\n  GH --> CDN\n  GH --> Netlify\n  Users --> Obs\n","mermaid",[38],{"type":13,"tag":39,"props":40,"children":41},"code",{"__ignoreMap":5},[42,53,62,72,81,90,99,108,117,126,134,143,152,161,170,178,186,195,204,213,221,229,238,246,255,264,273,282,291,300,309,318,327,336,345,354],{"type":13,"tag":43,"props":44,"children":47},"span",{"class":45,"line":46},"line",1,[48],{"type":13,"tag":43,"props":49,"children":50},{},[51],{"type":18,"value":52},"flowchart TB\n",{"type":13,"tag":43,"props":54,"children":56},{"class":45,"line":55},2,[57],{"type":13,"tag":43,"props":58,"children":59},{},[60],{"type":18,"value":61},"  Users[\"Users / browsers\"]\n",{"type":13,"tag":43,"props":63,"children":65},{"class":45,"line":64},3,[66],{"type":13,"tag":43,"props":67,"children":69},{"emptyLinePlaceholder":68},true,[70],{"type":18,"value":71},"\n",{"type":13,"tag":43,"props":73,"children":75},{"class":45,"line":74},4,[76],{"type":13,"tag":43,"props":77,"children":78},{},[79],{"type":18,"value":80},"  subgraph apps[\"Apps\"]\n",{"type":13,"tag":43,"props":82,"children":84},{"class":45,"line":83},5,[85],{"type":13,"tag":43,"props":86,"children":87},{},[88],{"type":18,"value":89},"    Portfolio[\"jovylle.com\u003Cbr/>Nuxt portfolio + widget\"]\n",{"type":13,"tag":43,"props":91,"children":93},{"class":45,"line":92},6,[94],{"type":13,"tag":43,"props":95,"children":96},{},[97],{"type":18,"value":98},"    Playbase[\"fast.jovylle.com\u003Cbr/>Playbase reaction game\"]\n",{"type":13,"tag":43,"props":100,"children":102},{"class":45,"line":101},7,[103],{"type":13,"tag":43,"props":104,"children":105},{},[106],{"type":18,"value":107},"    D1G[\"d1g.uk\u003Cbr/>Desert digging tool\"]\n",{"type":13,"tag":43,"props":109,"children":111},{"class":45,"line":110},8,[112],{"type":13,"tag":43,"props":113,"children":114},{},[115],{"type":18,"value":116},"    ChatW[\"chat-widget.uft1.com\u003Cbr/>Embeddable GPT chat\"]\n",{"type":13,"tag":43,"props":118,"children":120},{"class":45,"line":119},9,[121],{"type":13,"tag":43,"props":122,"children":123},{},[124],{"type":18,"value":125},"  end\n",{"type":13,"tag":43,"props":127,"children":129},{"class":45,"line":128},10,[130],{"type":13,"tag":43,"props":131,"children":132},{"emptyLinePlaceholder":68},[133],{"type":18,"value":71},{"type":13,"tag":43,"props":135,"children":137},{"class":45,"line":136},11,[138],{"type":13,"tag":43,"props":139,"children":140},{},[141],{"type":18,"value":142},"  subgraph shared[\"Shared services\"]\n",{"type":13,"tag":43,"props":144,"children":146},{"class":45,"line":145},12,[147],{"type":13,"tag":43,"props":148,"children":149},{},[150],{"type":18,"value":151},"    CDN[\"content.jovylle.com\u003Cbr/>Project catalog JSON\"]\n",{"type":13,"tag":43,"props":153,"children":155},{"class":45,"line":154},13,[156],{"type":13,"tag":43,"props":157,"children":158},{},[159],{"type":18,"value":160},"    Pocket[\"pocket.uft1.com\u003Cbr/>Notifications + highlights\"]\n",{"type":13,"tag":43,"props":162,"children":164},{"class":45,"line":163},14,[165],{"type":13,"tag":43,"props":166,"children":167},{},[168],{"type":18,"value":169},"    PMate[\"projectmate.uft1.com\u003Cbr/>Feedback & updates overlay\"]\n",{"type":13,"tag":43,"props":171,"children":173},{"class":45,"line":172},15,[174],{"type":13,"tag":43,"props":175,"children":176},{},[177],{"type":18,"value":125},{"type":13,"tag":43,"props":179,"children":181},{"class":45,"line":180},16,[182],{"type":13,"tag":43,"props":183,"children":184},{"emptyLinePlaceholder":68},[185],{"type":18,"value":71},{"type":13,"tag":43,"props":187,"children":189},{"class":45,"line":188},17,[190],{"type":13,"tag":43,"props":191,"children":192},{},[193],{"type":18,"value":194},"  subgraph platform[\"Platform layer\"]\n",{"type":13,"tag":43,"props":196,"children":198},{"class":45,"line":197},18,[199],{"type":13,"tag":43,"props":200,"children":201},{},[202],{"type":18,"value":203},"    Netlify[\"Netlify\u003Cbr/>Build + serverless functions\"]\n",{"type":13,"tag":43,"props":205,"children":207},{"class":45,"line":206},19,[208],{"type":13,"tag":43,"props":209,"children":210},{},[211],{"type":18,"value":212},"    GH[\"GitHub\u003Cbr/>Repos + Actions\"]\n",{"type":13,"tag":43,"props":214,"children":216},{"class":45,"line":215},20,[217],{"type":13,"tag":43,"props":218,"children":219},{},[220],{"type":18,"value":125},{"type":13,"tag":43,"props":222,"children":224},{"class":45,"line":223},21,[225],{"type":13,"tag":43,"props":226,"children":227},{"emptyLinePlaceholder":68},[228],{"type":18,"value":71},{"type":13,"tag":43,"props":230,"children":232},{"class":45,"line":231},22,[233],{"type":13,"tag":43,"props":234,"children":235},{},[236],{"type":18,"value":237},"  Obs[\"Umami analytics\u003Cbr/>jovylle.com\"]\n",{"type":13,"tag":43,"props":239,"children":241},{"class":45,"line":240},23,[242],{"type":13,"tag":43,"props":243,"children":244},{"emptyLinePlaceholder":68},[245],{"type":18,"value":71},{"type":13,"tag":43,"props":247,"children":249},{"class":45,"line":248},24,[250],{"type":13,"tag":43,"props":251,"children":252},{},[253],{"type":18,"value":254},"  Users --> Portfolio\n",{"type":13,"tag":43,"props":256,"children":258},{"class":45,"line":257},25,[259],{"type":13,"tag":43,"props":260,"children":261},{},[262],{"type":18,"value":263},"  Users --> Playbase\n",{"type":13,"tag":43,"props":265,"children":267},{"class":45,"line":266},26,[268],{"type":13,"tag":43,"props":269,"children":270},{},[271],{"type":18,"value":272},"  Users --> D1G\n",{"type":13,"tag":43,"props":274,"children":276},{"class":45,"line":275},27,[277],{"type":13,"tag":43,"props":278,"children":279},{},[280],{"type":18,"value":281},"  Users --> ChatW\n",{"type":13,"tag":43,"props":283,"children":285},{"class":45,"line":284},28,[286],{"type":13,"tag":43,"props":287,"children":288},{},[289],{"type":18,"value":290},"  Portfolio --> Netlify\n",{"type":13,"tag":43,"props":292,"children":294},{"class":45,"line":293},29,[295],{"type":13,"tag":43,"props":296,"children":297},{},[298],{"type":18,"value":299},"  Portfolio --> Playbase\n",{"type":13,"tag":43,"props":301,"children":303},{"class":45,"line":302},30,[304],{"type":13,"tag":43,"props":305,"children":306},{},[307],{"type":18,"value":308},"  Portfolio --> Pocket\n",{"type":13,"tag":43,"props":310,"children":312},{"class":45,"line":311},31,[313],{"type":13,"tag":43,"props":314,"children":315},{},[316],{"type":18,"value":317},"  Portfolio --> PMate\n",{"type":13,"tag":43,"props":319,"children":321},{"class":45,"line":320},32,[322],{"type":13,"tag":43,"props":323,"children":324},{},[325],{"type":18,"value":326},"  Portfolio --> CDN\n",{"type":13,"tag":43,"props":328,"children":330},{"class":45,"line":329},33,[331],{"type":13,"tag":43,"props":332,"children":333},{},[334],{"type":18,"value":335},"  Playbase --> GH\n",{"type":13,"tag":43,"props":337,"children":339},{"class":45,"line":338},34,[340],{"type":13,"tag":43,"props":341,"children":342},{},[343],{"type":18,"value":344},"  GH --> CDN\n",{"type":13,"tag":43,"props":346,"children":348},{"class":45,"line":347},35,[349],{"type":13,"tag":43,"props":350,"children":351},{},[352],{"type":18,"value":353},"  GH --> Netlify\n",{"type":13,"tag":43,"props":355,"children":357},{"class":45,"line":356},36,[358],{"type":13,"tag":43,"props":359,"children":360},{},[361],{"type":18,"value":362},"  Users --> Obs\n",{"type":13,"tag":14,"props":364,"children":365},{},[366],{"type":13,"tag":367,"props":368,"children":369},"em",{},[370],{"type":18,"value":371},"Alt text: Architecture diagram showing browsers connecting to portfolio, Playbase, d1g.uk, and chat-widget; shared CDN and notification services; Netlify and GitHub as platform; Umami for site analytics.",{"type":13,"tag":21,"props":373,"children":374},{},[],{"type":13,"tag":25,"props":376,"children":378},{"id":377},"products-in-the-ecosystem",[379],{"type":18,"value":380},"Products in the ecosystem",{"type":13,"tag":382,"props":383,"children":385},"h3",{"id":384},"portfolio-jovyllecom",[386,388],{"type":18,"value":387},"Portfolio — ",{"type":13,"tag":389,"props":390,"children":394},"a",{"href":391,"rel":392},"https://jovylle.com",[393],"nofollow",[395],{"type":18,"value":396},"jovylle.com",{"type":13,"tag":14,"props":398,"children":399},{},[400,406],{"type":13,"tag":401,"props":402,"children":403},"strong",{},[404],{"type":18,"value":405},"Problem:",{"type":18,"value":407}," Recruiters need one place to see shipped work, not a scatter of repos.",{"type":13,"tag":14,"props":409,"children":410},{},[411,416,418,424],{"type":13,"tag":401,"props":412,"children":413},{},[414],{"type":18,"value":415},"Role in the platform:",{"type":18,"value":417}," Central hub. Nuxt 3 site on Netlify with a floating widget (AI chat, Playbase leaderboard, notifications), ProjectMate embed for feedback/updates, and a prerendered project archive fed by ",{"type":13,"tag":39,"props":419,"children":421},{"className":420},[],[422],{"type":18,"value":423},"content.jovylle.com",{"type":18,"value":425},".",{"type":13,"tag":14,"props":427,"children":428},{},[429,434,436,442,444],{"type":13,"tag":401,"props":430,"children":431},{},[432],{"type":18,"value":433},"Case study:",{"type":18,"value":435}," ",{"type":13,"tag":389,"props":437,"children":439},{"href":438},"/personal-projects",[440],{"type":18,"value":441},"Personal projects archive",{"type":18,"value":443}," · ",{"type":13,"tag":389,"props":445,"children":448},{"href":446,"rel":447},"https://github.com/jovylle/jovylle.com",[393],[449],{"type":18,"value":450},"GitHub",{"type":13,"tag":21,"props":452,"children":453},{},[],{"type":13,"tag":382,"props":455,"children":457},{"id":456},"d1guk-d1guk",[458,460],{"type":18,"value":459},"d1g.uk — ",{"type":13,"tag":389,"props":461,"children":464},{"href":462,"rel":463},"https://d1g.uk",[393],[465],{"type":18,"value":466},"d1g.uk",{"type":13,"tag":14,"props":468,"children":469},{},[470,474],{"type":13,"tag":401,"props":471,"children":472},{},[473],{"type":18,"value":405},{"type":18,"value":475}," Sunflower Land players need a fast, visual way to plan Desert digs when the in-game API only shows today.",{"type":13,"tag":14,"props":477,"children":478},{},[479,483,485,492],{"type":13,"tag":401,"props":480,"children":481},{},[482],{"type":18,"value":415},{"type":18,"value":484}," Highest-traffic standalone product in the ecosystem. Companion hub at ",{"type":13,"tag":389,"props":486,"children":489},{"href":487,"rel":488},"https://hub.d1g.uk",[393],[490],{"type":18,"value":491},"hub.d1g.uk",{"type":18,"value":493}," for saved/shared community grids. Shares the uft1/d1g domain family with other tools.",{"type":13,"tag":14,"props":495,"children":496},{},[497,501,502,509,510],{"type":13,"tag":401,"props":498,"children":499},{},[500],{"type":18,"value":433},{"type":18,"value":435},{"type":13,"tag":389,"props":503,"children":506},{"href":504,"rel":505},"https://github.com/jovylle/sfl-crab",[393],[507],{"type":18,"value":508},"GitHub — sfl-crab",{"type":18,"value":443},{"type":13,"tag":389,"props":511,"children":514},{"href":512,"rel":513},"https://d1g.uk/feedbacks",[393],[515],{"type":18,"value":516},"User feedback",{"type":13,"tag":21,"props":518,"children":519},{},[],{"type":13,"tag":382,"props":521,"children":523},{"id":522},"playbase-fastjovyllecom",[524,526],{"type":18,"value":525},"Playbase — ",{"type":13,"tag":389,"props":527,"children":530},{"href":528,"rel":529},"https://fast.jovylle.com",[393],[531],{"type":18,"value":532},"fast.jovylle.com",{"type":13,"tag":14,"props":534,"children":535},{},[536,540],{"type":13,"tag":401,"props":537,"children":538},{},[539],{"type":18,"value":405},{"type":18,"value":541}," A lightweight, replayable skill game that keeps score across sessions and surfaces results outside the game tab.",{"type":13,"tag":14,"props":543,"children":544},{},[545,549,551,557,559,565],{"type":13,"tag":401,"props":546,"children":547},{},[548],{"type":18,"value":415},{"type":18,"value":550}," Gamification layer. Reaction-test game with an all-time leaderboard JSON API (",{"type":13,"tag":39,"props":552,"children":554},{"className":553},[],[555],{"type":18,"value":556},"/reaction/top.json",{"type":18,"value":558},"). The portfolio widget proxies this via ",{"type":13,"tag":39,"props":560,"children":562},{"className":561},[],[563],{"type":18,"value":564},"/api/leaderboard",{"type":18,"value":566}," and links directly to play. Repo describes an automated seasonal leaderboard synced through GitHub Actions.",{"type":13,"tag":14,"props":568,"children":569},{},[570,575,577,582,584,590],{"type":13,"tag":401,"props":571,"children":572},{},[573],{"type":18,"value":574},"Note:",{"type":18,"value":576}," Live game is at ",{"type":13,"tag":39,"props":578,"children":580},{"className":579},[],[581],{"type":18,"value":532},{"type":18,"value":583},". A ",{"type":13,"tag":39,"props":585,"children":587},{"className":586},[],[588],{"type":18,"value":589},"playbase.jovylle.com",{"type":18,"value":591}," hostname is planned in ecosystem docs but not verified in current deployment config.",{"type":13,"tag":14,"props":593,"children":594},{},[595,599,600],{"type":13,"tag":401,"props":596,"children":597},{},[598],{"type":18,"value":433},{"type":18,"value":435},{"type":13,"tag":389,"props":601,"children":604},{"href":602,"rel":603},"https://github.com/jovylle/playbase",[393],[605],{"type":18,"value":606},"GitHub — playbase",{"type":13,"tag":21,"props":608,"children":609},{},[],{"type":13,"tag":382,"props":611,"children":613},{"id":612},"chat-widget-chat-widgetuft1com",[614,616],{"type":18,"value":615},"chat-widget — ",{"type":13,"tag":389,"props":617,"children":620},{"href":618,"rel":619},"https://chat-widget.uft1.com",[393],[621],{"type":18,"value":622},"chat-widget.uft1.com",{"type":13,"tag":14,"props":624,"children":625},{},[626,630],{"type":13,"tag":401,"props":627,"children":628},{},[629],{"type":18,"value":405},{"type":18,"value":631}," Drop a GPT-powered chatbot onto any site without rebuilding the host app.",{"type":13,"tag":14,"props":633,"children":634},{},[635,639],{"type":13,"tag":401,"props":636,"children":637},{},[638],{"type":18,"value":415},{"type":18,"value":640}," Reusable embed product (separate from the portfolio's own AI widget). Standalone script + Netlify/serverless backend pattern, same \"one script tag\" philosophy as ProjectMate.",{"type":13,"tag":14,"props":642,"children":643},{},[644,649],{"type":13,"tag":401,"props":645,"children":646},{},[647],{"type":18,"value":648},"Consumer sites:",{"type":18,"value":650}," TBD — embed is site-agnostic; confirm specific hosts before listing publicly.",{"type":13,"tag":14,"props":652,"children":653},{},[654,658,659],{"type":13,"tag":401,"props":655,"children":656},{},[657],{"type":18,"value":433},{"type":18,"value":435},{"type":13,"tag":389,"props":660,"children":663},{"href":661,"rel":662},"https://github.com/jovylle/chatbot-widget",[393],[664],{"type":18,"value":665},"GitHub — chatbot-widget",{"type":13,"tag":21,"props":667,"children":668},{},[],{"type":13,"tag":382,"props":670,"children":672},{"id":671},"projectmate-projectmateuft1com",[673,675],{"type":18,"value":674},"ProjectMate — ",{"type":13,"tag":389,"props":676,"children":679},{"href":677,"rel":678},"https://projectmate.uft1.com",[393],[680],{"type":18,"value":681},"projectmate.uft1.com",{"type":13,"tag":14,"props":683,"children":684},{},[685,689],{"type":13,"tag":401,"props":686,"children":687},{},[688],{"type":18,"value":405},{"type":18,"value":690}," Visitors need in-context feedback and release notes without leaving the page or opening GitHub Issues.",{"type":13,"tag":14,"props":692,"children":693},{},[694,698,700,706,708,714],{"type":13,"tag":401,"props":695,"children":696},{},[697],{"type":18,"value":415},{"type":18,"value":699}," Cross-site support overlay. Loaded on jovylle.com via ",{"type":13,"tag":39,"props":701,"children":703},{"className":702},[],[704],{"type":18,"value":705},"embed.js",{"type":18,"value":707}," with ",{"type":13,"tag":39,"props":709,"children":711},{"className":710},[],[712],{"type":18,"value":713},"projectId: jovylle-com",{"type":18,"value":715}," — feedback, updates, and about panel; chat disabled on portfolio. Same embed pattern can attach to other properties in the uft1.com family.",{"type":13,"tag":14,"props":717,"children":718},{},[719,723,724],{"type":13,"tag":401,"props":720,"children":721},{},[722],{"type":18,"value":433},{"type":18,"value":435},{"type":13,"tag":389,"props":725,"children":728},{"href":726,"rel":727},"https://github.com/jovylle/projectmate-embedded-app",[393],[729],{"type":18,"value":730},"GitHub — projectmate-embedded-app",{"type":13,"tag":21,"props":732,"children":733},{},[],{"type":13,"tag":25,"props":735,"children":737},{"id":736},"infrastructure-highlights",[738],{"type":18,"value":739},"Infrastructure highlights",{"type":13,"tag":741,"props":742,"children":743},"ul",{},[744,770,787,812,830,847,870,888],{"type":13,"tag":745,"props":746,"children":747},"li",{},[748,753,755,761,763,768],{"type":13,"tag":401,"props":749,"children":750},{},[751],{"type":18,"value":752},"Netlify for jovylle.com",{"type":18,"value":754}," — static Nuxt build, ",{"type":13,"tag":39,"props":756,"children":758},{"className":757},[],[759],{"type":18,"value":760},"/.netlify/functions/chatbot",{"type":18,"value":762}," for production AI chat, ",{"type":13,"tag":39,"props":764,"children":766},{"className":765},[],[767],{"type":18,"value":564},{"type":18,"value":769}," proxy in dev and prod.",{"type":13,"tag":745,"props":771,"children":772},{},[773,778,780,785],{"type":13,"tag":401,"props":774,"children":775},{},[776],{"type":18,"value":777},"Decoupled content CDN",{"type":18,"value":779}," — project catalog at ",{"type":13,"tag":39,"props":781,"children":783},{"className":782},[],[784],{"type":18,"value":423},{"type":18,"value":786},"; portfolio rebuild triggered by GitHub Actions → Netlify build hook after CDN publish (hook URL in GitHub secrets, not in git).",{"type":13,"tag":745,"props":788,"children":789},{},[790,795,797,803,805,811],{"type":13,"tag":401,"props":791,"children":792},{},[793],{"type":18,"value":794},"Notification bus",{"type":18,"value":796}," — ",{"type":13,"tag":39,"props":798,"children":800},{"className":799},[],[801],{"type":18,"value":802},"pocket.uft1.com/notifications/index.json",{"type":18,"value":804}," feeds the portfolio widget's alert tab; highlights JSON on the same host powers ",{"type":13,"tag":39,"props":806,"children":808},{"className":807},[],[809],{"type":18,"value":810},"/highlights",{"type":18,"value":425},{"type":13,"tag":745,"props":813,"children":814},{},[815,820,822,828],{"type":13,"tag":401,"props":816,"children":817},{},[818],{"type":18,"value":819},"Embeds over iframes where it matters",{"type":18,"value":821}," — ProjectMate overlay, portfolio widget (",{"type":13,"tag":39,"props":823,"children":825},{"className":824},[],[826],{"type":18,"value":827},"embed-inline.js",{"type":18,"value":829},"), and chat-widget each ship as a single async script.",{"type":13,"tag":745,"props":831,"children":832},{},[833,838,839,845],{"type":13,"tag":401,"props":834,"children":835},{},[836],{"type":18,"value":837},"Secrets out of repo",{"type":18,"value":796},{"type":13,"tag":39,"props":840,"children":842},{"className":841},[],[843],{"type":18,"value":844},"OPENAI_API_KEY",{"type":18,"value":846}," via Netlify env; build hooks via GitHub Actions secrets.",{"type":13,"tag":745,"props":848,"children":849},{},[850,855,856,861,863,868],{"type":13,"tag":401,"props":851,"children":852},{},[853],{"type":18,"value":854},"Prerender vs live fetch",{"type":18,"value":796},{"type":13,"tag":39,"props":857,"children":859},{"className":858},[],[860],{"type":18,"value":438},{"type":18,"value":862}," prerendered from CDN JSON at build time; ",{"type":13,"tag":39,"props":864,"children":866},{"className":865},[],[867],{"type":18,"value":810},{"type":18,"value":869}," fetches live at runtime (different freshness tradeoffs, intentional).",{"type":13,"tag":745,"props":871,"children":872},{},[873,878,880,886],{"type":13,"tag":401,"props":874,"children":875},{},[876],{"type":18,"value":877},"GitHub as integration bus",{"type":18,"value":879}," — profile automation repo (",{"type":13,"tag":39,"props":881,"children":883},{"className":882},[],[884],{"type":18,"value":885},"jovylle/jovylle",{"type":18,"value":887},", GitHub Actions), content rebuild webhooks, and Playbase leaderboard automation.",{"type":13,"tag":745,"props":889,"children":890},{},[891,896],{"type":13,"tag":401,"props":892,"children":893},{},[894],{"type":18,"value":895},"Edge / CDN provider for uft1.com & d1g.uk:",{"type":18,"value":897}," TBD — confirm Cloudflare (or other) before claiming in public docs.",{"type":13,"tag":21,"props":899,"children":900},{},[],{"type":13,"tag":25,"props":902,"children":904},{"id":903},"cross-project-flows",[905],{"type":18,"value":906},"Cross-project flows",{"type":13,"tag":382,"props":908,"children":910},{"id":909},"_1-play-score-portfolio-and-github",[911],{"type":18,"value":912},"1. Play → score → portfolio (and GitHub)",{"type":13,"tag":914,"props":915,"children":916},"ol",{},[917,928,940,959],{"type":13,"tag":745,"props":918,"children":919},{},[920,922,927],{"type":18,"value":921},"User plays the reaction test at ",{"type":13,"tag":389,"props":923,"children":925},{"href":528,"rel":924},[393],[926],{"type":18,"value":532},{"type":18,"value":425},{"type":13,"tag":745,"props":929,"children":930},{},[931,933,939],{"type":18,"value":932},"Score is persisted server-side; top entries exposed at ",{"type":13,"tag":39,"props":934,"children":936},{"className":935},[],[937],{"type":18,"value":938},"https://fast.jovylle.com/reaction/top.json",{"type":18,"value":425},{"type":13,"tag":745,"props":941,"children":942},{},[943,945,950,952,957],{"type":18,"value":944},"Portfolio widget on ",{"type":13,"tag":389,"props":946,"children":948},{"href":391,"rel":947},[393],[949],{"type":18,"value":396},{"type":18,"value":951}," fetches via ",{"type":13,"tag":39,"props":953,"children":955},{"className":954},[],[956],{"type":18,"value":564},{"type":18,"value":958}," and shows live top players with a \"Play now\" link.",{"type":13,"tag":745,"props":960,"children":961},{},[962,964,970,972,978],{"type":18,"value":963},"GitHub profile README updated by Actions in the Playbase / profile automation repos (exact pipeline: see ",{"type":13,"tag":389,"props":965,"children":967},{"href":602,"rel":966},[393],[968],{"type":18,"value":969},"playbase",{"type":18,"value":971}," and ",{"type":13,"tag":389,"props":973,"children":976},{"href":974,"rel":975},"https://github.com/jovylle/jovylle",[393],[977],{"type":18,"value":885},{"type":18,"value":979},").",{"type":13,"tag":382,"props":981,"children":983},{"id":982},"_2-visitor-uses-d1guk",[984],{"type":18,"value":985},"2. Visitor uses d1g.uk",{"type":13,"tag":914,"props":987,"children":988},{},[989,1001,1021,1040],{"type":13,"tag":745,"props":990,"children":991},{},[992,994,999],{"type":18,"value":993},"Player opens ",{"type":13,"tag":389,"props":995,"children":997},{"href":462,"rel":996},[393],[998],{"type":18,"value":466},{"type":18,"value":1000}," for today's Desert grid visualization.",{"type":13,"tag":745,"props":1002,"children":1003},{},[1004,1006,1012,1014,1020],{"type":18,"value":1005},"Tool runs as a Nuxt/serverless front-end (repo: ",{"type":13,"tag":39,"props":1007,"children":1009},{"className":1008},[],[1010],{"type":18,"value":1011},"sfl-crab",{"type":18,"value":1013},"); optional feedback via ",{"type":13,"tag":389,"props":1015,"children":1017},{"href":512,"rel":1016},[393],[1018],{"type":18,"value":1019},"d1g.uk/feedbacks",{"type":18,"value":425},{"type":13,"tag":745,"props":1022,"children":1023},{},[1024,1026,1031,1033,1039],{"type":18,"value":1025},"Community history and shared grids live on ",{"type":13,"tag":389,"props":1027,"children":1029},{"href":487,"rel":1028},[393],[1030],{"type":18,"value":491},{"type":18,"value":1032}," (separate repo: ",{"type":13,"tag":39,"props":1034,"children":1036},{"className":1035},[],[1037],{"type":18,"value":1038},"sfl-digging-hub",{"type":18,"value":979},{"type":13,"tag":745,"props":1041,"children":1042},{},[1043],{"type":18,"value":1044},"Backend/data-store details per environment: TBD in public docs.",{"type":13,"tag":382,"props":1046,"children":1048},{"id":1047},"_3-support-notifications-on-portfolio",[1049],{"type":18,"value":1050},"3. Support & notifications on portfolio",{"type":13,"tag":914,"props":1052,"children":1053},{},[1054,1066,1084],{"type":13,"tag":745,"props":1055,"children":1056},{},[1057,1059,1065],{"type":18,"value":1058},"Visitor lands on jovylle.com; widget loads notifications from ",{"type":13,"tag":39,"props":1060,"children":1062},{"className":1061},[],[1063],{"type":18,"value":1064},"pocket.uft1.com",{"type":18,"value":425},{"type":13,"tag":745,"props":1067,"children":1068},{},[1069,1075,1077,1082],{"type":13,"tag":39,"props":1070,"children":1072},{"className":1071},[],[1073],{"type":18,"value":1074},"#support",{"type":18,"value":1076}," hash or support action opens ProjectMate overlay (",{"type":13,"tag":39,"props":1078,"children":1080},{"className":1079},[],[1081],{"type":18,"value":681},{"type":18,"value":1083},") for feedback and release notes.",{"type":13,"tag":745,"props":1085,"children":1086},{},[1087],{"type":18,"value":1088},"AI chat tab calls Netlify serverless function with portfolio context (when enabled); widget itself carries no third-party analytics.",{"type":13,"tag":21,"props":1090,"children":1091},{},[],{"type":13,"tag":25,"props":1093,"children":1095},{"id":1094},"impact-metrics",[1096],{"type":18,"value":1097},"Impact & metrics",{"type":13,"tag":1099,"props":1100,"children":1101},"table",{},[1102,1126],{"type":13,"tag":1103,"props":1104,"children":1105},"thead",{},[1106],{"type":13,"tag":1107,"props":1108,"children":1109},"tr",{},[1110,1116,1121],{"type":13,"tag":1111,"props":1112,"children":1113},"th",{},[1114],{"type":18,"value":1115},"Metric",{"type":13,"tag":1111,"props":1117,"children":1118},{},[1119],{"type":18,"value":1120},"Source",{"type":13,"tag":1111,"props":1122,"children":1123},{},[1124],{"type":18,"value":1125},"Note",{"type":13,"tag":1127,"props":1128,"children":1129},"tbody",{},[1130,1149,1174,1196,1221,1252,1270],{"type":13,"tag":1107,"props":1131,"children":1132},{},[1133,1139,1144],{"type":13,"tag":1134,"props":1135,"children":1136},"td",{},[1137],{"type":18,"value":1138},"d1g.uk daily visitors",{"type":13,"tag":1134,"props":1140,"children":1141},{},[1142],{"type":18,"value":1143},"Project catalog metadata",{"type":13,"tag":1134,"props":1145,"children":1146},{},[1147],{"type":18,"value":1148},"~300/day (self-reported in CMS catalog; verify with analytics export)",{"type":13,"tag":1107,"props":1150,"children":1151},{},[1152,1157,1169],{"type":13,"tag":1134,"props":1153,"children":1154},{},[1155],{"type":18,"value":1156},"Portfolio traffic",{"type":13,"tag":1134,"props":1158,"children":1159},{},[1160,1162,1167],{"type":18,"value":1161},"Umami (",{"type":13,"tag":39,"props":1163,"children":1165},{"className":1164},[],[1166],{"type":18,"value":396},{"type":18,"value":1168},")",{"type":13,"tag":1134,"props":1170,"children":1171},{},[1172],{"type":18,"value":1173},"Regular daily usage; no public DAU/WAU figure",{"type":13,"tag":1107,"props":1175,"children":1176},{},[1177,1182,1191],{"type":13,"tag":1134,"props":1178,"children":1179},{},[1180],{"type":18,"value":1181},"Playbase leaderboard",{"type":13,"tag":1134,"props":1183,"children":1184},{},[1185],{"type":13,"tag":39,"props":1186,"children":1188},{"className":1187},[],[1189],{"type":18,"value":1190},"fast.jovylle.com/reaction/top.json",{"type":13,"tag":1134,"props":1192,"children":1193},{},[1194],{"type":18,"value":1195},"Public JSON; all-time archive on game site",{"type":13,"tag":1107,"props":1197,"children":1198},{},[1199,1204,1209],{"type":13,"tag":1134,"props":1200,"children":1201},{},[1202],{"type":18,"value":1203},"Content freshness",{"type":13,"tag":1134,"props":1205,"children":1206},{},[1207],{"type":18,"value":1208},"GitHub Actions → Netlify hook",{"type":13,"tag":1134,"props":1210,"children":1211},{},[1212,1214,1219],{"type":18,"value":1213},"Rebuild after ",{"type":13,"tag":39,"props":1215,"children":1217},{"className":1216},[],[1218],{"type":18,"value":423},{"type":18,"value":1220}," JSON updates",{"type":13,"tag":1107,"props":1222,"children":1223},{},[1224,1229,1239],{"type":13,"tag":1134,"props":1225,"children":1226},{},[1227],{"type":18,"value":1228},"Widget notification reach",{"type":13,"tag":1134,"props":1230,"children":1231},{},[1232,1237],{"type":13,"tag":39,"props":1233,"children":1235},{"className":1234},[],[1236],{"type":18,"value":1064},{"type":18,"value":1238}," index",{"type":13,"tag":1134,"props":1240,"children":1241},{},[1242,1244,1250],{"type":18,"value":1243},"Tag-filtered (",{"type":13,"tag":39,"props":1245,"children":1247},{"className":1246},[],[1248],{"type":18,"value":1249},"jovylle.com,all",{"type":18,"value":1251},") on portfolio",{"type":13,"tag":1107,"props":1253,"children":1254},{},[1255,1260,1265],{"type":13,"tag":1134,"props":1256,"children":1257},{},[1258],{"type":18,"value":1259},"chat-widget adoption",{"type":13,"tag":1134,"props":1261,"children":1262},{},[1263],{"type":18,"value":1264},"TBD",{"type":13,"tag":1134,"props":1266,"children":1267},{},[1268],{"type":18,"value":1269},"Confirm embed domains before publishing counts",{"type":13,"tag":1107,"props":1271,"children":1272},{},[1273,1278,1282],{"type":13,"tag":1134,"props":1274,"children":1275},{},[1276],{"type":18,"value":1277},"Cloudflare Web Analytics",{"type":13,"tag":1134,"props":1279,"children":1280},{},[1281],{"type":18,"value":1264},{"type":13,"tag":1134,"props":1283,"children":1284},{},[1285],{"type":18,"value":1286},"Not wired on jovylle.com today (Umami in use)",{"type":13,"tag":21,"props":1288,"children":1289},{},[],{"type":13,"tag":25,"props":1291,"children":1293},{"id":1292},"what-id-improve-next",[1294],{"type":18,"value":1295},"What I'd improve next",{"type":13,"tag":914,"props":1297,"children":1298},{},[1299,1309,1333],{"type":13,"tag":745,"props":1300,"children":1301},{},[1302,1307],{"type":13,"tag":401,"props":1303,"children":1304},{},[1305],{"type":18,"value":1306},"Single observability layer",{"type":18,"value":1308}," — Umami covers the portfolio; d1g.uk, Playbase, and uft1 subdomains lack a unified dashboard. I'd add consistent uptime checks and error logging across the ecosystem, not just the main site.",{"type":13,"tag":745,"props":1310,"children":1311},{},[1312,1317,1319,1324,1326,1331],{"type":13,"tag":401,"props":1313,"children":1314},{},[1315],{"type":18,"value":1316},"Hostname clarity",{"type":18,"value":1318}," — Playbase (",{"type":13,"tag":39,"props":1320,"children":1322},{"className":1321},[],[1323],{"type":18,"value":532},{"type":18,"value":1325}," vs ",{"type":13,"tag":39,"props":1327,"children":1329},{"className":1328},[],[1330],{"type":18,"value":589},{"type":18,"value":1332},") and the three embed products (portfolio widget, chat-widget, ProjectMate) need a public map so integrators know which script to use.",{"type":13,"tag":745,"props":1334,"children":1335},{},[1336,1341],{"type":13,"tag":401,"props":1337,"children":1338},{},[1339],{"type":18,"value":1340},"Document cross-repo data contracts",{"type":18,"value":1342}," — leaderboard JSON, notification index, and CDN project schema are integration APIs today but undocumented for external consumers; I'd version and publish them.",{"type":13,"tag":21,"props":1344,"children":1345},{},[],{"type":13,"tag":25,"props":1347,"children":1349},{"id":1348},"explore-the-ecosystem",[1350],{"type":18,"value":1351},"Explore the ecosystem",{"type":13,"tag":14,"props":1353,"children":1354},{},[1355],{"type":13,"tag":401,"props":1356,"children":1357},{},[1358],{"type":18,"value":1359},"Live",{"type":13,"tag":741,"props":1361,"children":1362},{},[1363,1373,1383,1393,1403,1413,1423],{"type":13,"tag":745,"props":1364,"children":1365},{},[1366,1371],{"type":13,"tag":389,"props":1367,"children":1369},{"href":391,"rel":1368},[393],[1370],{"type":18,"value":396},{"type":18,"value":1372}," — Portfolio & widget hub",{"type":13,"tag":745,"props":1374,"children":1375},{},[1376,1381],{"type":13,"tag":389,"props":1377,"children":1379},{"href":462,"rel":1378},[393],[1380],{"type":18,"value":466},{"type":18,"value":1382}," — Desert digging tool",{"type":13,"tag":745,"props":1384,"children":1385},{},[1386,1391],{"type":13,"tag":389,"props":1387,"children":1389},{"href":487,"rel":1388},[393],[1390],{"type":18,"value":491},{"type":18,"value":1392}," — Community digging hub",{"type":13,"tag":745,"props":1394,"children":1395},{},[1396,1401],{"type":13,"tag":389,"props":1397,"children":1399},{"href":528,"rel":1398},[393],[1400],{"type":18,"value":532},{"type":18,"value":1402}," — Playbase reaction game",{"type":13,"tag":745,"props":1404,"children":1405},{},[1406,1411],{"type":13,"tag":389,"props":1407,"children":1409},{"href":618,"rel":1408},[393],[1410],{"type":18,"value":622},{"type":18,"value":1412}," — Embeddable chatbot",{"type":13,"tag":745,"props":1414,"children":1415},{},[1416,1421],{"type":13,"tag":389,"props":1417,"children":1419},{"href":677,"rel":1418},[393],[1420],{"type":18,"value":681},{"type":18,"value":1422}," — Feedback & updates overlay",{"type":13,"tag":745,"props":1424,"children":1425},{},[1426,1433],{"type":13,"tag":389,"props":1427,"children":1430},{"href":1428,"rel":1429},"https://uft1.com",[393],[1431],{"type":18,"value":1432},"uft1.com",{"type":18,"value":1434}," — Utility tools hub",{"type":13,"tag":14,"props":1436,"children":1437},{},[1438],{"type":13,"tag":401,"props":1439,"children":1440},{},[1441],{"type":18,"value":450},{"type":13,"tag":741,"props":1443,"children":1444},{},[1445],{"type":13,"tag":745,"props":1446,"children":1447},{},[1448,1453,1454,1459,1460,1465,1466,1472,1473,1479,1480],{"type":13,"tag":389,"props":1449,"children":1451},{"href":446,"rel":1450},[393],[1452],{"type":18,"value":396},{"type":18,"value":443},{"type":13,"tag":389,"props":1455,"children":1457},{"href":504,"rel":1456},[393],[1458],{"type":18,"value":1011},{"type":18,"value":443},{"type":13,"tag":389,"props":1461,"children":1463},{"href":602,"rel":1462},[393],[1464],{"type":18,"value":969},{"type":18,"value":443},{"type":13,"tag":389,"props":1467,"children":1469},{"href":661,"rel":1468},[393],[1470],{"type":18,"value":1471},"chatbot-widget",{"type":18,"value":443},{"type":13,"tag":389,"props":1474,"children":1476},{"href":726,"rel":1475},[393],[1477],{"type":18,"value":1478},"projectmate-embedded-app",{"type":18,"value":443},{"type":13,"tag":389,"props":1481,"children":1483},{"href":974,"rel":1482},[393],[1484],{"type":18,"value":1485},"Profile automation",{"type":13,"tag":21,"props":1487,"children":1488},{},[],{"type":13,"tag":14,"props":1490,"children":1491},{},[1492],{"type":13,"tag":367,"props":1493,"children":1494},{},[1495,1497,1503],{"type":18,"value":1496},"Built by Jovylle Bermudez — infrastructure-minded full-stack engineer. Questions: ",{"type":13,"tag":389,"props":1498,"children":1500},{"href":1499},"mailto:me@jovylle.com",[1501],{"type":18,"value":1502},"me@jovylle.com",{"type":18,"value":425},{"type":13,"tag":1505,"props":1506,"children":1507},"style",{},[1508],{"type":18,"value":1509},"html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":5,"searchDepth":55,"depth":55,"links":1511},[1512,1513,1525,1526,1531,1532,1533],{"id":27,"depth":55,"text":30},{"id":377,"depth":55,"text":380,"children":1514},[1515,1517,1519,1521,1523],{"id":384,"depth":64,"text":1516},"Portfolio — jovylle.com",{"id":456,"depth":64,"text":1518},"d1g.uk — d1g.uk",{"id":522,"depth":64,"text":1520},"Playbase — fast.jovylle.com",{"id":612,"depth":64,"text":1522},"chat-widget — chat-widget.uft1.com",{"id":671,"depth":64,"text":1524},"ProjectMate — projectmate.uft1.com",{"id":736,"depth":55,"text":739},{"id":903,"depth":55,"text":906,"children":1527},[1528,1529,1530],{"id":909,"depth":64,"text":912},{"id":982,"depth":64,"text":985},{"id":1047,"depth":64,"text":1050},{"id":1094,"depth":55,"text":1097},{"id":1292,"depth":55,"text":1295},{"id":1348,"depth":55,"text":1351},"markdown","content:ecosystem.md","content","ecosystem.md","ecosystem","md",1780715705719]