{"id":13,"date":"2026-11-01T02:27:05","date_gmt":"2026-11-01T02:27:05","guid":{"rendered":"https:\/\/app.ratiandherbiblebuddies.co.za\/?page_id=13"},"modified":"2026-11-01T02:42:38","modified_gmt":"2026-11-01T02:42:38","slug":"rati-app","status":"publish","type":"page","link":"https:\/\/app.ratiandherbiblebuddies.co.za\/","title":{"rendered":"Rati App"},"content":{"rendered":"\t\t<div data-elementor-type=\"wp-page\" data-elementor-id=\"13\" class=\"elementor elementor-13\">\n\t\t\t\t<div class=\"elementor-element elementor-element-dfa702e e-flex e-con-boxed e-con e-parent\" data-id=\"dfa702e\" data-element_type=\"container\" data-e-type=\"container\">\n\t\t\t\t\t<div class=\"e-con-inner\">\n\t\t\t\t<div class=\"elementor-element elementor-element-1f2965f elementor-widget elementor-widget-html\" data-id=\"1f2965f\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"html.default\">\n\t\t\t\t\t<!-- ===== Rati and Her Bible Buddies \u00b7 PWA front door ===== -->\n<!-- Paste ALL of this into the Elementor HTML widget on the \"Rati App\" page (Elementor Canvas template). -->\n<link rel=\"preconnect\" href=\"https:\/\/fonts.googleapis.com\">\n<link rel=\"stylesheet\" href=\"https:\/\/fonts.googleapis.com\/css2?family=Baloo+2:wght@600;700;800&family=Atkinson+Hyperlegible:wght@400;700&display=swap\">\n\n<div id=\"rati-app\" aria-live=\"polite\">\n  <div class=\"rb-floaters\" aria-hidden=\"true\"><\/div>\n  <div class=\"rb-shell\">\n    <div id=\"rb-top\"><\/div>\n    <main class=\"rb-screen\" id=\"rb-screen\"><\/main>\n    <nav class=\"rb-nav\" id=\"rb-nav\" hidden><\/nav>\n  <\/div>\n  <div class=\"rb-modal\" id=\"rb-modal\" hidden><\/div>\n  <div class=\"rb-toast\" id=\"rb-toast\" role=\"status\"><\/div>\n<\/div>\n\n<style>\n\/* Bright, playful kids' look: sunny sky, protea pink, veld green, big rounded type. *\/\n#rati-app{\n  --sky:#2f8fd8; --sky-soft:#dcefff; --protea:#e0457b; --protea-dk:#b02d5c; --protea-soft:#ffe1ec;\n  --sun:#f6b31b; --sun-soft:#fff2cc; --veld:#2f9e5b; --veld-soft:#dcf4e5; --plum:#7a4bc4; --plum-soft:#ece3fb;\n  --ink:#1f2a3a; --ink-soft:#56627a; --paper:#fffaf2; --card:#ffffff; --line:#eadfcd;\n  --display:\"Baloo 2\",\"Trebuchet MS\",system-ui,sans-serif; --body:\"Atkinson Hyperlegible\",system-ui,-apple-system,\"Segoe UI\",sans-serif;\n  --w:var(--protea); --w-soft:var(--protea-soft);\n  position:fixed; inset:0; z-index:99999; overflow:hidden; color:var(--ink); font-family:var(--body); font-size:16px; line-height:1.5;\n  background:radial-gradient(120% 70% at 50% 0%, #fff4d6 0%, #ffe9f1 45%, #e3f2ff 100%);\n  -webkit-font-smoothing:antialiased; color-scheme:light;\n}\n#rati-app *{box-sizing:border-box}\n#rati-app button{font:inherit;color:inherit;cursor:pointer;border:0;background:none;margin:0;padding:0;line-height:inherit;text-transform:none;letter-spacing:normal;box-shadow:none}\n#rati-app input,#rati-app select,#rati-app textarea{font:inherit;color:var(--ink)}\n#rati-app h1,#rati-app h2,#rati-app h3{font-family:var(--display);margin:0;line-height:1.1;color:var(--ink);text-wrap:balance;letter-spacing:0}\n#rati-app h1{font-size:30px;font-weight:800}#rati-app h2{font-size:25px;font-weight:800}#rati-app h3{font-size:19px;font-weight:700}\n#rati-app p{margin:0}\n#rati-app :focus-visible{outline:3px solid var(--sky);outline-offset:2px}\n#rati-app [hidden]{display:none!important}\n.rb-shell{position:relative;z-index:1;height:100%;max-width:480px;margin:0 auto;display:flex;flex-direction:column;\n  padding-top:env(safe-area-inset-top,0px);padding-left:env(safe-area-inset-left,0px);padding-right:env(safe-area-inset-right,0px)}\n.rb-screen{flex:1;overflow-y:auto;overflow-x:hidden;padding:16px 18px 28px;display:flex;flex-direction:column;gap:16px;-webkit-overflow-scrolling:touch}\n.rb-screen>*{flex-shrink:0}\n.rb-muted{color:var(--ink-soft)}\n.rb-small{font-size:13.5px}\n\n\/* floating background: game controllers, music notes, stars *\/\n.rb-floaters{position:absolute;inset:0;z-index:0;pointer-events:none;overflow:hidden}\n.rb-floaters span{position:absolute;bottom:-60px;font-size:26px;opacity:.22;animation:rb-rise linear infinite}\n@keyframes rb-rise{0%{transform:translateY(0) rotate(0)}100%{transform:translateY(-120vh) rotate(25deg)}}\n\n\/* buttons *\/\n.rb-big{display:inline-flex;align-items:center;justify-content:center;gap:8px;background:var(--protea)!important;color:#fff!important;font-family:var(--display)!important;font-weight:800!important;font-size:21px!important;padding:13px 28px!important;border-radius:999px!important;box-shadow:0 5px 0 var(--protea-dk)!important;transition:transform .1s}\n.rb-big:active{transform:translateY(3px);box-shadow:0 2px 0 var(--protea-dk)!important}\n.rb-big.green{background:var(--veld)!important;box-shadow:0 5px 0 #1f7442!important}\n.rb-pill{border-radius:999px!important;padding:10px 18px!important;font-weight:700!important;background:var(--card)!important;border:2px solid var(--line)!important}\n.rb-pill.primary{background:var(--w)!important;border-color:var(--w)!important;color:#fff!important}\n.rb-link{color:var(--sky)!important;font-weight:700!important;text-decoration:underline!important}\n.rb-back{font-weight:700;color:var(--ink-soft)!important;align-self:flex-start;display:inline-flex;gap:6px;align-items:center}\n\n\/* Rati + bubble *\/\n.rb-rati-row{display:flex;align-items:flex-end;gap:10px}\n.rb-face{width:62px;height:62px;flex:none;border-radius:50%;object-fit:cover;background:var(--sun-soft);border:3px solid #fff;box-shadow:0 3px 10px rgba(80,40,10,.15)}\n.rb-bubble{background:var(--card);border:2.5px solid var(--w);border-radius:20px 20px 20px 4px;padding:10px 14px;font-weight:700;line-height:1.35;min-width:0}\n\n\/* ===== Welcome \/ index ===== *\/\n.rb-welcome{align-items:center;text-align:center;gap:10px;padding-top:10px}\n.rb-logo{width:min(250px,64vw);height:auto;filter:drop-shadow(0 6px 12px rgba(80,40,10,.15));animation:rb-pop .6s ease-out both}\n.rb-logo-text{font-family:var(--display);font-weight:800;font-size:34px;line-height:1;color:var(--protea)}\n.rb-logo-text small{display:block;font-size:22px;color:var(--sky)}\n.rb-stage{position:relative;width:100%;height:340px;margin-top:0}\n.rb-girl{position:absolute;left:24%;width:52%;bottom:70px;height:240px;object-fit:contain;object-position:center bottom;z-index:2;animation:rb-pop .7s .2s ease-out both;filter:drop-shadow(0 8px 14px rgba(80,40,10,.2))}\n.rb-girl.svg{height:auto;left:27%;width:46%;bottom:30px}\n.rb-girl.svg svg{width:100%;height:auto;display:block}\n.rb-bible{position:absolute;left:29%;bottom:4px;width:42%;aspect-ratio:1.45;z-index:3;perspective:700px;animation:rb-pop .6s .9s ease-out both}\n.rb-glow{position:absolute;left:50%;top:40%;width:150%;aspect-ratio:1;transform:translate(-50%,-50%) scale(.2);border-radius:50%;\n  background:radial-gradient(circle,rgba(255,230,140,.95) 0%,rgba(255,214,90,.45) 35%,rgba(255,214,90,0) 70%);opacity:0;animation:rb-glow 1.2s 1.9s ease-out forwards}\n@keyframes rb-glow{to{opacity:1;transform:translate(-50%,-50%) scale(1)}}\n.rb-pages{position:absolute;inset:8% 4% 0;background:linear-gradient(90deg,#fff 0%,#f6efe0 48%,#e9dfca 50%,#f6efe0 52%,#fff 100%);border-radius:6px;box-shadow:0 6px 0 #c9b48e,0 10px 18px rgba(60,30,10,.25)}\n.rb-pages::before,.rb-pages::after{content:\"\";position:absolute;top:22%;width:30%;height:3px;background:#e2d6bd;box-shadow:0 10px 0 #e2d6bd,0 20px 0 #e2d6bd,0 30px 0 #e2d6bd}\n.rb-pages::before{left:10%}.rb-pages::after{right:10%}\n.rb-cover{position:absolute;top:8%;bottom:0;right:4%;width:46%;transform-origin:left center;background:linear-gradient(135deg,#253f8c,#1a2e6b);border-radius:4px 8px 8px 4px;\n  box-shadow:inset 0 0 0 3px #c9a23b;display:grid;place-items:center;color:#f4d27a;font-family:var(--display);font-weight:800;font-size:13px;line-height:1.05;text-align:center;\n  animation:rb-open 1s 1.6s cubic-bezier(.5,.1,.3,1) forwards;backface-visibility:hidden}\n.rb-cover b{display:block;font-size:22px}\n.rb-cover-l{position:absolute;top:8%;bottom:0;left:4%;width:46%;background:linear-gradient(135deg,#1a2e6b,#253f8c);border-radius:8px 4px 4px 8px;box-shadow:inset 0 0 0 3px #c9a23b}\n.rb-bible.open .rb-cover-l{opacity:0;transition:opacity .2s .9s}\n@keyframes rb-open{0%{transform:rotateY(0)}100%{transform:rotateY(-180deg);opacity:0}}\n.rb-char{position:absolute;left:var(--sx);top:var(--sy);width:24%;z-index:4;display:flex;flex-direction:column;align-items:center;gap:2px;opacity:0;\n  animation:rb-fly .9s var(--d) cubic-bezier(.2,1.4,.4,1) forwards, rb-bob 3.2s calc(var(--d) + .9s) ease-in-out infinite}\n.rb-char .em{width:52px;height:52px;border-radius:50%;background:#fff;display:grid;place-items:center;font-size:28px;box-shadow:0 4px 10px rgba(60,30,10,.18);border:3px solid var(--c)}\n.rb-char .nm{font-family:var(--display);font-weight:800;font-size:12.5px;max-width:100%;overflow:hidden;text-overflow:ellipsis;background:var(--c);color:#fff;border-radius:999px;padding:0 9px;line-height:1.5;white-space:nowrap}\n@keyframes rb-fly{0%{opacity:0;transform:translate(var(--fx),var(--fy)) scale(.2)}100%{opacity:1;transform:translate(0,0) scale(1)}}\n@keyframes rb-bob{0%,100%{translate:0 0}50%{translate:0 -9px}}\n.rb-wave{transform-box:view-box;transform-origin:66px 176px;animation:rb-wavey 1.6s ease-in-out infinite}\n@keyframes rb-wavey{0%,100%{transform:rotate(0)}50%{transform:rotate(-14deg)}}\n@keyframes rb-pop{0%{opacity:0;transform:scale(.6)}70%{transform:scale(1.06)}100%{opacity:1;transform:scale(1)}}\n.rb-ask{background:#fff;border-radius:24px;padding:16px 16px 18px;box-shadow:0 8px 24px rgba(80,40,10,.12);border:3px solid var(--sun);display:flex;flex-direction:column;align-items:center;gap:10px;width:100%;animation:rb-pop .6s 3.4s ease-out both}\n.rb-ask h2{font-size:26px}\n.rb-ask .rb-big{width:100%;max-width:320px}\n.rb-ask .alt{display:flex;flex-direction:column;align-items:center;gap:6px;margin-top:4px}\n.rb-ask .alt b{font-family:var(--display);font-size:17px}\n.rb-login-btn{background:var(--sky)!important;color:#fff!important;font-family:var(--display)!important;font-weight:800!important;font-size:18px!important;padding:9px 22px!important;border-radius:999px!important;box-shadow:0 4px 0 #1f6aa6!important}\n.rb-install-hint{font-size:13.5px;color:var(--ink-soft)}\n\n\/* forms *\/\n.rb-card{background:#fff;border-radius:22px;padding:18px;box-shadow:0 6px 18px rgba(80,40,10,.1);display:flex;flex-direction:column;gap:12px}\n.rb-field{display:flex;flex-direction:column;gap:6px}\n.rb-field label{font-weight:700;font-size:14.5px}\n.rb-field input,.rb-field select,.rb-field textarea{width:100%;padding:12px 14px!important;border-radius:14px!important;border:2px solid var(--line)!important;background:#fff!important;font-size:16px!important;line-height:1.3!important;box-shadow:none!important;height:auto!important}\n.rb-field input:focus,.rb-field textarea:focus{border-color:var(--sky)!important;outline:none}\n.rb-err{color:var(--protea-dk);font-weight:700;font-size:14.5px}\n.rb-ok{color:var(--veld);font-weight:700;font-size:14.5px}\n.rb-step{font-size:13px;font-weight:700;color:var(--ink-soft);letter-spacing:.04em;text-transform:uppercase}\n\n\/* preview note *\/\n.rb-note{background:var(--sun-soft);border:3px dashed var(--sun);border-radius:22px;padding:16px;display:flex;flex-direction:column;gap:8px}\n.rb-note ul{margin:0;padding-left:20px;display:flex;flex-direction:column;gap:4px}\n\n\/* symbols *\/\n.rb-symbols{display:grid;grid-template-columns:repeat(3,1fr);gap:10px}\n.rb-symbols button{aspect-ratio:1;max-width:100%;border-radius:20px!important;background:#fff!important;border:3px solid var(--line)!important;display:flex!important;flex-direction:column;align-items:center;justify-content:center;gap:2px;font-size:34px!important;transition:transform .15s}\n.rb-symbols button small{font-size:12px;font-weight:700;color:var(--ink-soft);font-family:var(--body)}\n.rb-symbols button[aria-pressed=\"true\"]{border-color:var(--protea)!important;background:var(--protea-soft)!important;transform:scale(1.05)}\n\n\/* ages *\/\n.rb-hub-top{display:flex;align-items:center;gap:10px}\n.rb-badge{width:40px;height:40px;border-radius:50%;background:#fff;border:2px solid var(--line);display:grid;place-items:center;font-size:22px;flex:none}\n.rb-hub-top .ttl{font-family:var(--display);font-weight:800;font-size:18px;line-height:1.1;flex:1;min-width:0}\n.rb-icon-btn{width:42px;height:42px;border-radius:50%!important;display:grid!important;place-items:center;font-size:20px!important;background:#fff!important;border:2px solid var(--line)!important}\n.rb-hi{display:inline-block;transition:opacity .35s}.rb-hi.fade{opacity:0}\n.rb-lang{font-size:12px;font-weight:700;color:var(--ink-soft);margin-top:2px}\n.rb-worlds{display:grid;gap:12px}\n.rb-world{display:flex!important;align-items:center;gap:14px;padding:14px!important;border-radius:22px!important;text-align:left;border:3px solid transparent!important;box-shadow:0 4px 0 rgba(0,0,0,.06)}\n.rb-world .em{font-size:36px;width:60px;height:60px;display:grid;place-items:center;border-radius:18px;background:rgba(255,255,255,.75);flex:none}\n.rb-world b{font-family:var(--display);font-size:20px;display:block;line-height:1.15}\n.rb-world small{font-size:14px;color:var(--ink-soft)}\n\n\/* world *\/\n.rb-topbar{display:flex;align-items:center;gap:10px;padding:10px 14px;background:var(--w);color:#fff}\n.rb-topbar .nm{font-family:var(--display);font-weight:800;font-size:18px;flex:1;min-width:0;display:flex;align-items:center;gap:8px}\n.rb-topbar .rb-icon-btn{background:rgba(255,255,255,.25)!important;border-color:transparent!important;color:#fff!important}\n.rb-nav{display:grid;grid-template-columns:repeat(5,1fr);background:#fff;border-top:1px solid var(--line);padding:6px 4px calc(6px + env(safe-area-inset-bottom,0px))}\n.rb-nav button{display:flex!important;flex-direction:column;align-items:center;gap:1px;padding:6px 0!important;border-radius:14px!important;font-size:11.5px!important;font-weight:700!important;color:var(--ink-soft)!important}\n.rb-nav button .i{font-size:22px;line-height:1}\n.rb-nav button[aria-current=\"page\"]{background:var(--w-soft)!important;color:var(--ink)!important}\n.rb-soon{background:#fff;border-radius:24px;padding:22px 18px;text-align:center;display:flex;flex-direction:column;align-items:center;gap:10px;border:3px solid var(--w);box-shadow:0 6px 18px rgba(80,40,10,.1)}\n.rb-soon .big{font-size:56px;line-height:1;animation:rb-bob 3s ease-in-out infinite}\n.rb-coming{display:grid;grid-template-columns:repeat(3,1fr);gap:10px}\n.rb-coming div{background:#fff;border-radius:18px;padding:12px 6px;text-align:center;font-size:13px;font-weight:700;border:2px solid var(--line)}\n.rb-coming div span{display:block;font-size:30px}\n\n\/* grown-ups *\/\n.rb-hold{position:relative;align-self:center;width:150px;height:150px;border-radius:50%;background:#fff;border:3px solid var(--line);display:grid;place-items:center;font-weight:700;text-align:center;padding:16px;touch-action:none;user-select:none;-webkit-user-select:none}\n.rb-hold svg{position:absolute;inset:-3px;width:calc(100% + 6px);height:calc(100% + 6px);transform:rotate(-90deg)}\n#rati-app details{background:#fff;border-radius:18px;padding:12px 14px;border:2px solid var(--line)}\n#rati-app summary{font-weight:700;cursor:pointer}\n#rati-app details[open] summary{margin-bottom:10px}\n.rb-stars{display:flex;gap:4px}\n.rb-stars button{font-size:32px!important;filter:grayscale(1);opacity:.35}\n.rb-stars button.on{filter:none;opacity:1}\n.rb-yn{display:flex;gap:8px}\n.rb-yn button{flex:1;padding:10px!important;border-radius:14px!important;border:2px solid var(--line)!important;background:#fff!important;font-weight:700!important}\n.rb-yn button[aria-pressed=\"true\"]{border-color:var(--veld)!important;background:var(--veld-soft)!important}\n.rb-q{display:flex;flex-direction:column;gap:8px;padding-top:12px;border-top:2px dashed var(--line)}\n.rb-q.first{border-top:0;padding-top:0}\n.rb-q b{font-family:var(--display);font-size:17px;line-height:1.2}\n.rb-steps{margin:0;padding-left:20px;display:flex;flex-direction:column;gap:4px}\n\n\/* modal + toast *\/\n.rb-modal{position:absolute;inset:0;z-index:5;background:rgba(31,42,58,.45);display:flex;align-items:flex-end;justify-content:center;padding:16px;padding-bottom:calc(16px + env(safe-area-inset-bottom,0px))}\n.rb-sheet{background:#fff;border-radius:26px;padding:20px;width:100%;max-width:440px;max-height:85%;overflow-y:auto;display:flex;flex-direction:column;gap:12px;animation:rb-up .35s ease-out}\n@keyframes rb-up{from{transform:translateY(40px);opacity:0}to{transform:none;opacity:1}}\n.rb-sheet .icon{width:64px;height:64px;border-radius:16px;object-fit:cover;align-self:center;box-shadow:0 4px 12px rgba(0,0,0,.12)}\n.rb-tabs{display:flex;gap:8px}\n.rb-tabs button{flex:1;padding:8px!important;border-radius:12px!important;border:2px solid var(--line)!important;font-weight:700!important;background:#fff!important}\n.rb-tabs button[aria-pressed=\"true\"]{border-color:var(--sky)!important;background:var(--sky-soft)!important}\n.rb-toast{position:absolute;left:50%;bottom:calc(90px + env(safe-area-inset-bottom,0px));transform:translate(-50%,10px);z-index:6;background:var(--ink);color:#fff;border-radius:14px;padding:10px 16px;font-weight:700;opacity:0;transition:all .25s;pointer-events:none;max-width:90%;text-align:center}\n.rb-toast.show{opacity:1;transform:translate(-50%,0)}\n.rb-demo{position:absolute;top:calc(6px + env(safe-area-inset-top,0px));right:8px;z-index:7;background:var(--sun);color:var(--ink);font-size:11px;font-weight:700;border-radius:999px;padding:2px 9px}\n@media (prefers-reduced-motion:reduce){#rati-app *{animation:none!important;transition:none!important}.rb-char,.rb-ask,.rb-logo,.rb-girl{opacity:1!important}.rb-cover{display:none}}\n<\/style>\n\n<script>\n(function(){\n\/* ============ SETTINGS: paste your two image links here ============ *\/\nvar LOGO_URL = \"\";   \/\/ the Rati and Her Bible Buddies logo (Media \u2192 Copy URL)\nvar RATI_URL = \"\";   \/\/ the picture of Rati, the girl (Media \u2192 Copy URL)\n\/* =================================================================== *\/\n\nvar R = window.RATI || null;          \/\/ filled in by the WPCode snippet\nvar DEMO = !R;                        \/\/ no snippet = preview mode (saves in this browser only)\nvar $ = function(s){ return document.querySelector(s); };\nvar app = $(\"#rati-app\"), scr = $(\"#rb-screen\"), topBar = $(\"#rb-top\"), nav = $(\"#rb-nav\"), modal = $(\"#rb-modal\");\nvar state = { user: R && R.user ? R.user : null, tab: \"home\", draft: {} };\n\nvar SYMBOLS = [[\"dove\",\"\ud83d\udd4a\ufe0f\",\"Dove\"],[\"olive\",\"\ud83c\udf3f\",\"Olive leaf\"],[\"star\",\"\u2b50\",\"Star\"],[\"lamb\",\"\ud83d\udc11\",\"Lamb\"],[\"fish\",\"\ud83d\udc1f\",\"Fish\"],[\"rainbow\",\"\ud83c\udf08\",\"Rainbow\"],[\"cross\",\"\u271d\ufe0f\",\"Cross\"],[\"crown\",\"\ud83d\udc51\",\"Crown\"],[\"lamp\",\"\ud83e\ude94\",\"Lamp\"],[\"wheat\",\"\ud83c\udf3e\",\"Wheat\"],[\"lily\",\"\ud83c\udf38\",\"Lily\"],[\"vine\",\"\ud83c\udf47\",\"Vine\"]];\nvar WORLDS = {\n  toddler:{name:\"Toddler World\",ages:\"Ages 2\u20133\",em:\"\ud83e\uddf8\",c:\"--sun\",s:\"--sun-soft\",blurb:\"Watch Bible stories, sing and tap\"},\n  explorers:{name:\"Little Explorers\",ages:\"Ages 3\u20135\",em:\"\ud83d\udd0d\",c:\"--sky\",s:\"--sky-soft\",blurb:\"Big pictures, listen or read\"},\n  adventurers:{name:\"Bible Adventurers\",ages:\"Ages 5\u20137\",em:\"\ud83d\uddfa\ufe0f\",c:\"--veld\",s:\"--veld-soft\",blurb:\"Tap, answer and discover\"},\n  scripture:{name:\"Scripture Explorers\",ages:\"Ages 8\u201310\",em:\"\ud83d\udcdc\",c:\"--plum\",s:\"--plum-soft\",blurb:\"Stories, facts and big questions\"}\n};\nvar CHARACTERS = [[\"Noah\",\"\ud83d\udea2\",\"--sky\"],[\"Moses\",\"\ud83c\udf0a\",\"--plum\"],[\"David\",\"\ud83e\udea8\",\"--veld\"],[\"Daniel\",\"\ud83e\udd81\",\"--sun\"],[\"Jonah\",\"\ud83d\udc0b\",\"--sky\"],[\"Joseph\",\"\ud83e\udde5\",\"--protea\"],[\"Esther\",\"\ud83d\udc51\",\"--plum\"],[\"Adam & Eve\",\"\ud83c\udf4e\",\"--veld\"]];\nvar GREETINGS = [[\"Hi friend!\",\"English\"],[\"Sawubona mngane!\",\"isiZulu\"],[\"Molo mhlobo!\",\"isiXhosa\"],[\"Hallo vriend!\",\"Afrikaans\"],[\"Dumela mokgotsi!\",\"Sesotho\"],[\"Dumela tsala!\",\"Setswana\"],[\"Thobela mogwera!\",\"Sepedi\"],[\"Avuxeni munghana!\",\"Xitsonga\"],[\"Ndaa, khonani!\",\"Tshiven\u1e13a\"],[\"Sawubona mngani!\",\"siSwati\"],[\"Lotjhani mngani!\",\"isiNdebele\"]];\n\nfunction esc(s){ return String(s == null ? \"\" : s).replace(\/[&<>\"]\/g, function(c){ return {\"&\":\"&amp;\",\"<\":\"&lt;\",\">\":\"&gt;\",'\"':\"&quot;\"}[c]; }); }\nfunction sym(k){ for (var i=0;i<SYMBOLS.length;i++) if (SYMBOLS[i][0]===k) return SYMBOLS[i]; return null; }\nfunction toast(t){ var el=$(\"#rb-toast\"); el.textContent=t; el.classList.add(\"show\"); clearTimeout(toast.t); toast.t=setTimeout(function(){ el.classList.remove(\"show\"); },2400); }\nfunction store(k,v){ try{ if(v===undefined) return JSON.parse(localStorage.getItem(k)||\"null\"); localStorage.setItem(k,JSON.stringify(v)); }catch(e){ return null; } }\n\n\/* Rati: real picture if a link is set, else a friendly drawing *\/\nvar RATI_PARTS = '<g class=\"rb-wave\"><line x1=\"66\" y1=\"176\" x2=\"36\" y2=\"128\" stroke=\"#a86b45\" stroke-width=\"16\" stroke-linecap=\"round\"\/><circle cx=\"32\" cy=\"118\" r=\"12\" fill=\"#a86b45\"\/><path d=\"M24 110 l-4 -12 M31 107 l-1 -13 M38 108 l3 -12\" stroke=\"#a86b45\" stroke-width=\"6\" stroke-linecap=\"round\"\/><\/g><line x1=\"134\" y1=\"178\" x2=\"148\" y2=\"230\" stroke=\"#a86b45\" stroke-width=\"16\" stroke-linecap=\"round\"\/><circle cx=\"149\" cy=\"234\" r=\"9\" fill=\"#a86b45\"\/><rect x=\"80\" y=\"258\" width=\"13\" height=\"26\" rx=\"6\" fill=\"#a86b45\"\/><rect x=\"107\" y=\"258\" width=\"13\" height=\"26\" rx=\"6\" fill=\"#a86b45\"\/><ellipse cx=\"85\" cy=\"288\" rx=\"12\" ry=\"7\" fill=\"#e0457b\"\/><ellipse cx=\"115\" cy=\"288\" rx=\"12\" ry=\"7\" fill=\"#e0457b\"\/><path d=\"M64 168 Q100 154 136 168 L156 262 L44 262 Z\" fill=\"#f6c445\"\/><path d=\"M48 242 L152 242 L154 251 L46 251 Z\" fill=\"#d9443a\"\/><path d=\"M46 251 L154 251 L156 262 L44 262 Z\" fill=\"#2f9e5b\"\/><circle cx=\"66\" cy=\"174\" r=\"12\" fill=\"#f6c445\"\/><circle cx=\"134\" cy=\"174\" r=\"12\" fill=\"#f6c445\"\/><rect x=\"92\" y=\"148\" width=\"16\" height=\"16\" fill=\"#a86b45\"\/><circle cx=\"54\" cy=\"40\" r=\"27\" fill=\"#241712\"\/><circle cx=\"146\" cy=\"40\" r=\"27\" fill=\"#241712\"\/><circle cx=\"100\" cy=\"94\" r=\"62\" fill=\"#241712\"\/><circle cx=\"76\" cy=\"54\" r=\"6.5\" fill=\"#e0457b\"\/><circle cx=\"68\" cy=\"62\" r=\"6.5\" fill=\"#f6c445\"\/><circle cx=\"124\" cy=\"54\" r=\"6.5\" fill=\"#e0457b\"\/><circle cx=\"132\" cy=\"62\" r=\"6.5\" fill=\"#f6c445\"\/><circle cx=\"46\" cy=\"108\" r=\"10\" fill=\"#a86b45\"\/><circle cx=\"154\" cy=\"108\" r=\"10\" fill=\"#a86b45\"\/><ellipse cx=\"100\" cy=\"108\" rx=\"52\" ry=\"50\" fill=\"#a86b45\"\/><path d=\"M48 100 Q50 52 100 52 Q150 52 152 100 Q132 74 100 76 Q68 74 48 100 Z\" fill=\"#241712\"\/><path d=\"M70 94 Q80 88 90 94 M110 94 Q120 88 130 94\" stroke=\"#241712\" stroke-width=\"3.5\" fill=\"none\" stroke-linecap=\"round\"\/><ellipse cx=\"80\" cy=\"111\" rx=\"7\" ry=\"9\" fill=\"#1f2a3a\"\/><ellipse cx=\"120\" cy=\"111\" rx=\"7\" ry=\"9\" fill=\"#1f2a3a\"\/><circle cx=\"82.5\" cy=\"107\" r=\"2.6\" fill=\"#fff\"\/><circle cx=\"122.5\" cy=\"107\" r=\"2.6\" fill=\"#fff\"\/><circle cx=\"64\" cy=\"128\" r=\"9\" fill=\"#e0457b\" opacity=\".35\"\/><circle cx=\"136\" cy=\"128\" r=\"9\" fill=\"#e0457b\" opacity=\".35\"\/><path d=\"M86 132 Q100 148 114 132 Z\" fill=\"#7a2e2a\"\/>';\nvar RATI_SVG = '<svg viewBox=\"0 0 200 300\" role=\"img\" aria-label=\"Rati\">'+RATI_PARTS+'<\/svg>';\nvar RATI_HEAD = '<svg viewBox=\"26 8 148 158\" role=\"img\" aria-label=\"Rati\">'+RATI_PARTS+'<\/svg>';\nfunction face(){ return RATI_URL ? '<img decoding=\"async\" class=\"rb-face\" src=\"'+esc(RATI_URL)+'\" alt=\"Rati\">' : '<span class=\"rb-face\" style=\"display:block;overflow:hidden;padding:3px 2px 0\">'+RATI_HEAD+'<\/span>'; }\n\n\/* ---------- talking to WordPress ---------- *\/\nfunction api(path, body){\n  if (DEMO) return demoApi(path, body || {});\n  var h = {\"Content-Type\":\"application\/json\"};\n  if (R.nonce) h[\"X-WP-Nonce\"] = R.nonce;\n  return fetch(R.rest + path, {method:\"POST\", headers:h, credentials:\"same-origin\", body:JSON.stringify(body||{})})\n    .then(function(res){ return res.json().catch(function(){ return {}; }).then(function(j){\n      if (!res.ok) throw new Error(j.message || \"Something went wrong. Check your internet connection and try again.\");\n      if (j.nonce) R.nonce = j.nonce;\n      return j;\n    }); })\n    .catch(function(e){ throw new Error(e.message && e.message !== \"Failed to fetch\" ? e.message : \"No internet connection. Check your connection and try again.\"); });\n}\nfunction demoApi(path, b){\n  return new Promise(function(ok, fail){ setTimeout(function(){\n    var accts = store(\"rb-demo-accts\") || {}; var me = store(\"rb-demo-me\");\n    if (path===\"register\"){ if (accts[b.email]) return fail(new Error(\"There\u2019s already an account with this email. Log in instead.\")); accts[b.email]={pw:b.password,symbol:b.symbol,world:\"\"}; store(\"rb-demo-accts\",accts); store(\"rb-demo-me\",b.email); return ok({user:{email:b.email,symbol:b.symbol,world:\"\"}}); }\n    if (path===\"login\"){ var a=accts[b.email]; if(!a||a.pw!==b.password) return fail(new Error(\"That email and password don\u2019t match. Try again, or reset your password.\")); store(\"rb-demo-me\",b.email); return ok({user:{email:b.email,symbol:a.symbol,world:a.world}}); }\n    if (path===\"profile\"){ var x=accts[me]||{}; if(b.symbol) x.symbol=b.symbol; if(b.world) x.world=b.world; accts[me]=x; store(\"rb-demo-accts\",accts); return ok({user:{email:me,symbol:x.symbol,world:x.world}}); }\n    if (path===\"logout\"){ store(\"rb-demo-me\",\"\"); return ok({ok:true}); }\n    return ok({ok:true});\n  }, 350); });\n}\nif (DEMO){ var me0 = store(\"rb-demo-me\"), ac0 = store(\"rb-demo-accts\") || {}; if (me0 && ac0[me0]) state.user = {email:me0, symbol:ac0[me0].symbol, world:ac0[me0].world}; }\n\n\/* ---------- floating background ---------- *\/\n(function(){ var f=app.querySelector(\".rb-floaters\"), items=[\"\ud83c\udfae\",\"\ud83c\udfb5\",\"\ud83c\udfb6\",\"\u2b50\",\"\ud83c\udfae\",\"\u2728\",\"\ud83c\udfb5\",\"\ud83d\udc9b\",\"\ud83c\udfb6\",\"\u2b50\",\"\ud83c\udfae\",\"\ud83c\udf08\"];\n  items.forEach(function(e,i){ var s=document.createElement(\"span\"); s.textContent=e; s.style.left=(4+i*8)%96+\"%\"; s.style.animationDuration=(16+(i*7)%12)+\"s\"; s.style.animationDelay=(-i*2.3)+\"s\"; s.style.fontSize=(20+(i*5)%16)+\"px\"; f.appendChild(s); });\n})();\nif (DEMO){ var d=document.createElement(\"div\"); d.className=\"rb-demo\"; d.textContent=\"Preview mode\"; app.appendChild(d); }\n\n\/* ---------- navigation ---------- *\/\nvar hiTimer = null, holdRaf = 0;\nfunction setColors(){ var w = WORLDS[state.user && state.user.world]; app.style.setProperty(\"--w\", w && state.inWorld ? \"var(\"+w.c+\")\" : \"var(--protea)\"); app.style.setProperty(\"--w-soft\", w && state.inWorld ? \"var(\"+w.s+\")\" : \"var(--protea-soft)\"); }\nfunction go(view, arg){\n  clearInterval(hiTimer); cancelAnimationFrame(holdRaf); scr.scrollTop = 0;\n  state.inWorld = (view === \"world\");\n  if (!state.user && [\"welcome\",\"signup\",\"preview\",\"symbol\",\"login\",\"forgot\",\"reset\"].indexOf(view) < 0) view = \"welcome\";\n  setColors(); topBar.innerHTML = \"\"; nav.hidden = true;\n  VIEWS[view](arg);\n}\n\n\/* ---------- 1. Welcome (index) ---------- *\/\nfunction welcome(){\n  var girl = RATI_URL ? '<img decoding=\"async\" class=\"rb-girl\" src=\"'+esc(RATI_URL)+'\" alt=\"Rati\">' : '<div class=\"rb-girl svg\">'+RATI_SVG+'<\/div>';\n  var logo = LOGO_URL ? '<img decoding=\"async\" class=\"rb-logo\" src=\"'+esc(LOGO_URL)+'\" alt=\"Rati and Her Bible Buddies\">' : '<div class=\"rb-logo rb-logo-text\">Rati<small>and Her Bible Buddies<\/small><\/div>';\n  var spots = [[\"0%\",\"0px\"],[\"76%\",\"0px\"],[\"0%\",\"84px\"],[\"76%\",\"84px\"],[\"0%\",\"168px\"],[\"76%\",\"168px\"],[\"0%\",\"252px\"],[\"76%\",\"252px\"]];\n  var chars = CHARACTERS.map(function(c,i){ var left = i%2===0; return '<div class=\"rb-char\" style=\"--sx:'+spots[i][0]+';--sy:'+spots[i][1]+';--fx:'+(left?110:-110)+'px;--fy:'+(260-parseInt(spots[i][1],10))+'px;--d:'+(2.2+i*.15)+'s;--c:var('+c[2]+')\"><span class=\"em\">'+c[1]+'<\/span><span class=\"nm\">'+c[0]+'<\/span><\/div>'; }).join(\"\");\n  scr.className = \"rb-screen rb-welcome\";\n  scr.innerHTML = logo +\n    '<div class=\"rb-stage\" aria-label=\"Rati opens her Bible and her Bible buddies pop out\">'+ girl + chars +\n      '<div class=\"rb-bible\" id=\"rb-bible\"><div class=\"rb-glow\"><\/div><div class=\"rb-cover-l\"><\/div><div class=\"rb-pages\"><\/div><div class=\"rb-cover\"><span>HOLY<b>\u271d<\/b>BIBLE<\/span><\/div><\/div>'+\n    '<\/div>'+\n    '<div class=\"rb-ask\">'+\n      '<h2>Want to be my Bible buddy?<\/h2>'+\n      '<p class=\"rb-muted\">I\u2019m Rati, and I\u2019m still learning about God. Let\u2019s learn the Bible together!<\/p>'+\n      '<button class=\"rb-big\" id=\"rb-su\">Yes! Sign me up<\/button>'+\n      '<div class=\"alt\"><b>Already a Bible buddy?<\/b><button class=\"rb-login-btn\" id=\"rb-li\">Log in<\/button><\/div>'+\n    '<\/div>'+\n    '<button class=\"rb-link rb-install-hint\" id=\"rb-inst\">\ud83d\udcf2 How to put this app on your phone<\/button>'+\n    '<p class=\"rb-small rb-muted\">Free preview \u00b7 28 November 2026<\/p>';\n  setTimeout(function(){ var b=$(\"#rb-bible\"); if(b) b.classList.add(\"open\"); }, 1600);\n  $(\"#rb-su\").onclick = function(){ state.draft = {}; go(\"signup\"); };\n  $(\"#rb-li\").onclick = function(){ go(\"login\"); };\n  $(\"#rb-inst\").onclick = showInstall;\n  maybeAutoInstall();\n}\n\n\/* ---------- 2. Sign up: email + password ---------- *\/\nfunction signup(err){\n  scr.className = \"rb-screen\";\n  scr.innerHTML = '<button class=\"rb-back\" id=\"rb-bk\">\u2190 Back<\/button>'+\n    '<div class=\"rb-rati-row\">'+face()+'<div class=\"rb-bubble\">Yay! Grown-up, please make an account for us.<\/div><\/div>'+\n    '<form class=\"rb-card\" id=\"rb-f\" novalidate>'+\n      '<div class=\"rb-step\">Step 1 of 3<\/div><h2>Create your account<\/h2>'+\n      '<p class=\"rb-small rb-muted\">For parents, caregivers and Sunday school teachers. We only keep your email. Your password is stored safely and nobody can see it.<\/p>'+\n      '<div class=\"rb-field\"><label for=\"rb-e\">Email address<\/label><input id=\"rb-e\" type=\"email\" inputmode=\"email\" autocomplete=\"email\" placeholder=\"you@example.com\" value=\"'+esc(state.draft.email||\"\")+'\"><\/div>'+\n      '<div class=\"rb-field\"><label for=\"rb-p1\">Create a password<\/label><input id=\"rb-p1\" type=\"password\" autocomplete=\"new-password\" placeholder=\"At least 8 characters\"><\/div>'+\n      '<div class=\"rb-field\"><label for=\"rb-p2\">Type the password again<\/label><input id=\"rb-p2\" type=\"password\" autocomplete=\"new-password\"><\/div>'+\n      '<div id=\"rb-m\">'+(err?'<p class=\"rb-err\">'+esc(err)+'<\/p>':'')+'<\/div>'+\n      '<button class=\"rb-big\" type=\"submit\">Next \u2192<\/button>'+\n    '<\/form>'+\n    '<p class=\"rb-muted\" style=\"text-align:center\">Already have an account? <button class=\"rb-link\" id=\"rb-li\">Log in<\/button><\/p>';\n  $(\"#rb-bk\").onclick = function(){ go(\"welcome\"); };\n  $(\"#rb-li\").onclick = function(){ go(\"login\"); };\n  $(\"#rb-f\").onsubmit = function(e){ e.preventDefault();\n    var em=$(\"#rb-e\").value.trim().toLowerCase(), p1=$(\"#rb-p1\").value, p2=$(\"#rb-p2\").value, m=$(\"#rb-m\");\n    if (!\/^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$\/.test(em)) { m.innerHTML='<p class=\"rb-err\">Enter a valid email address, like you@example.com.<\/p>'; return; }\n    if (p1.length < 8) { m.innerHTML='<p class=\"rb-err\">Your password needs at least 8 characters.<\/p>'; return; }\n    if (p1 !== p2) { m.innerHTML='<p class=\"rb-err\">The two passwords don\u2019t match. Type them again.<\/p>'; return; }\n    state.draft = {email:em, password:p1, mode:\"new\"}; go(\"preview\");\n  };\n}\n\n\/* ---------- 3. Preview message ---------- *\/\nfunction preview(){\n  scr.className = \"rb-screen\";\n  scr.innerHTML = '<div class=\"rb-step\">Step 2 of 3<\/div>'+\n    '<div class=\"rb-rati-row\">'+face()+'<div class=\"rb-bubble\">Before we start, here\u2019s something for grown-ups!<\/div><\/div>'+\n    '<div class=\"rb-note\"><h2>This is a preview \ud83d\udee0\ufe0f<\/h2>'+\n      '<p>Thank you for trying Rati and Her Bible Buddies early! This preview is a first look at the app before it arrives on Google Play and the App Store.<\/p>'+\n      '<ul><li>New Bible stories, games and songs are added every week.<\/li><li>Some parts say \u201ccoming soon\u201d while we build them.<\/li><li>Please tell us what you think in the Grown-ups area (\u2699\ufe0f). Your feedback shapes the final app.<\/li><\/ul>'+\n      '<p class=\"rb-small rb-muted\">It\u2019s free, with no ads and nothing to buy.<\/p><\/div>'+\n    '<button class=\"rb-big\" id=\"rb-ok\">Got it! Next \u2192<\/button>';\n  $(\"#rb-ok\").onclick = function(){ go(\"symbol\"); };\n}\n\n\/* ---------- 4. Pick a symbol ---------- *\/\nfunction symbol(){\n  var pick = state.draft.symbol || \"\";\n  scr.className = \"rb-screen\";\n  scr.innerHTML = '<div class=\"rb-step\">Step 3 of 3<\/div>'+\n    '<div class=\"rb-rati-row\">'+face()+'<div class=\"rb-bubble\">Now pick your special Bible symbol! It shows it\u2019s you.<\/div><\/div>'+\n    '<div class=\"rb-symbols\">'+SYMBOLS.map(function(s){ return '<button data-k=\"'+s[0]+'\" aria-pressed=\"'+(pick===s[0])+'\" aria-label=\"'+s[2]+'\">'+s[1]+'<small>'+s[2]+'<\/small><\/button>'; }).join(\"\")+'<\/div>'+\n    '<p class=\"rb-small rb-muted\">We use the symbol, not your child\u2019s name, so their progress stays private.<\/p>'+\n    '<div id=\"rb-m\"><\/div><button class=\"rb-big green\" id=\"rb-go\">Let\u2019s go! \ud83c\udf89<\/button>';\n  scr.querySelectorAll(\".rb-symbols button\").forEach(function(b){ b.onclick=function(){ pick=b.dataset.k; state.draft.symbol=pick; scr.querySelectorAll(\".rb-symbols button\").forEach(function(x){ x.setAttribute(\"aria-pressed\", x===b); }); }; });\n  $(\"#rb-go\").onclick = function(){\n    if (!pick) { $(\"#rb-m\").innerHTML='<p class=\"rb-err\">Tap a symbol to choose it.<\/p>'; return; }\n    var btn=this; btn.disabled=true; btn.textContent=\"Just a moment\u2026\";\n    var call = state.draft.mode === \"new\"\n      ? api(\"register\", {email:state.draft.email, password:state.draft.password, symbol:pick})\n      : api(\"profile\", {symbol:pick});\n    call.then(function(j){ state.user = j.user; state.draft = {}; toast(\"Welcome, Bible buddy! \ud83c\udf89\"); go(\"ages\"); })\n        .catch(function(e){ if (state.draft.mode===\"new\" && \/already\/.test(e.message)) { go(\"signup\", e.message); return; } btn.disabled=false; btn.textContent=\"Let\u2019s go! \ud83c\udf89\"; $(\"#rb-m\").innerHTML='<p class=\"rb-err\">'+esc(e.message)+'<\/p>'; });\n  };\n}\n\n\/* ---------- Log in \/ forgot \/ reset ---------- *\/\nfunction login(msg){\n  scr.className = \"rb-screen\";\n  scr.innerHTML = '<button class=\"rb-back\" id=\"rb-bk\">\u2190 Back<\/button>'+\n    '<div class=\"rb-rati-row\">'+face()+'<div class=\"rb-bubble\">Welcome back, Bible buddy! \ud83d\udc9b<\/div><\/div>'+\n    '<form class=\"rb-card\" id=\"rb-f\" novalidate><h2>Log in<\/h2>'+\n      '<div class=\"rb-field\"><label for=\"rb-e\">Email address<\/label><input id=\"rb-e\" type=\"email\" inputmode=\"email\" autocomplete=\"email\"><\/div>'+\n      '<div class=\"rb-field\"><label for=\"rb-p\">Password<\/label><input id=\"rb-p\" type=\"password\" autocomplete=\"current-password\"><\/div>'+\n      '<div id=\"rb-m\">'+(msg?'<p class=\"rb-ok\">'+esc(msg)+'<\/p>':'')+'<\/div>'+\n      '<button class=\"rb-big\" type=\"submit\">Log in<\/button>'+\n      '<button type=\"button\" class=\"rb-link\" id=\"rb-fg\" style=\"align-self:center\">Forgot your password?<\/button>'+\n    '<\/form>'+\n    '<p class=\"rb-muted\" style=\"text-align:center\">New here? <button class=\"rb-link\" id=\"rb-su\">Become a Bible buddy<\/button><\/p>';\n  $(\"#rb-bk\").onclick = function(){ go(\"welcome\"); };\n  $(\"#rb-fg\").onclick = function(){ go(\"forgot\"); };\n  $(\"#rb-su\").onclick = function(){ state.draft={}; go(\"signup\"); };\n  $(\"#rb-f\").onsubmit = function(e){ e.preventDefault();\n    var m=$(\"#rb-m\"), btn=this.querySelector(\"button[type=submit]\"); btn.disabled=true; btn.textContent=\"Logging in\u2026\";\n    api(\"login\", {email:$(\"#rb-e\").value.trim().toLowerCase(), password:$(\"#rb-p\").value})\n      .then(function(j){ state.user=j.user; if (!state.user.symbol) { state.draft={mode:\"existing\"}; go(\"preview\"); } else go(\"ages\"); })\n      .catch(function(err){ btn.disabled=false; btn.textContent=\"Log in\"; m.innerHTML='<p class=\"rb-err\">'+esc(err.message)+'<\/p>'; });\n  };\n}\nfunction forgot(){\n  scr.className = \"rb-screen\";\n  scr.innerHTML = '<button class=\"rb-back\" id=\"rb-bk\">\u2190 Back to log in<\/button>'+\n    '<form class=\"rb-card\" id=\"rb-f\" novalidate><h2>Reset your password<\/h2>'+\n      '<p class=\"rb-muted\">Type your email and we\u2019ll send you a link to choose a new password.<\/p>'+\n      '<div class=\"rb-field\"><label for=\"rb-e\">Email address<\/label><input id=\"rb-e\" type=\"email\" inputmode=\"email\" autocomplete=\"email\"><\/div>'+\n      '<div id=\"rb-m\"><\/div><button class=\"rb-big\" type=\"submit\">Send reset link<\/button><\/form>';\n  $(\"#rb-bk\").onclick = function(){ go(\"login\"); };\n  $(\"#rb-f\").onsubmit = function(e){ e.preventDefault();\n    var em=$(\"#rb-e\").value.trim().toLowerCase(), m=$(\"#rb-m\"), btn=this.querySelector(\"button[type=submit]\");\n    if (!\/^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$\/.test(em)) { m.innerHTML='<p class=\"rb-err\">Enter a valid email address.<\/p>'; return; }\n    btn.disabled=true; btn.textContent=\"Sending\u2026\";\n    api(\"forgot\",{email:em}).then(function(){ m.innerHTML='<p class=\"rb-ok\">If there\u2019s an account for '+esc(em)+', a reset link is on its way. Check your inbox and spam folder.<\/p>'; btn.textContent=\"Sent \u2713\"; })\n      .catch(function(err){ btn.disabled=false; btn.textContent=\"Send reset link\"; m.innerHTML='<p class=\"rb-err\">'+esc(err.message)+'<\/p>'; });\n  };\n}\nfunction reset(p){\n  scr.className = \"rb-screen\";\n  scr.innerHTML = '<form class=\"rb-card\" id=\"rb-f\" novalidate><h2>Choose a new password<\/h2>'+\n    '<div class=\"rb-field\"><label for=\"rb-p1\">New password<\/label><input id=\"rb-p1\" type=\"password\" autocomplete=\"new-password\" placeholder=\"At least 8 characters\"><\/div>'+\n    '<div class=\"rb-field\"><label for=\"rb-p2\">Type it again<\/label><input id=\"rb-p2\" type=\"password\" autocomplete=\"new-password\"><\/div>'+\n    '<div id=\"rb-m\"><\/div><button class=\"rb-big\" type=\"submit\">Save new password<\/button><\/form>';\n  $(\"#rb-f\").onsubmit = function(e){ e.preventDefault();\n    var p1=$(\"#rb-p1\").value, p2=$(\"#rb-p2\").value, m=$(\"#rb-m\");\n    if (p1.length<8){ m.innerHTML='<p class=\"rb-err\">Your password needs at least 8 characters.<\/p>'; return; }\n    if (p1!==p2){ m.innerHTML='<p class=\"rb-err\">The two passwords don\u2019t match.<\/p>'; return; }\n    api(\"reset\",{key:p.key, login:p.login, password:p1}).then(function(){ go(\"login\",\"Password changed. Log in with your new password.\"); })\n      .catch(function(err){ m.innerHTML='<p class=\"rb-err\">'+esc(err.message)+'<\/p>'; });\n  };\n}\n\n\/* ---------- 5. Ages page ---------- *\/\nfunction ages(){\n  var s = sym(state.user.symbol);\n  scr.className = \"rb-screen\";\n  scr.innerHTML = '<div class=\"rb-hub-top\"><span class=\"rb-badge\" aria-label=\"Your symbol\">'+(s?s[1]:\"\u2b50\")+'<\/span><span class=\"ttl\">Rati and Her<br>Bible Buddies<\/span><button class=\"rb-icon-btn\" id=\"rb-gu\" aria-label=\"Grown-ups area\">\u2699\ufe0f<\/button><\/div>'+\n    '<div class=\"rb-rati-row\">'+face()+'<div class=\"rb-bubble\"><span class=\"rb-hi\" id=\"rb-hi\">'+GREETINGS[0][0]+'<\/span> I\u2019m Rati. Which world is just right for you?<div class=\"rb-lang\" id=\"rb-lang\">'+GREETINGS[0][1]+'<\/div><\/div><\/div>'+\n    '<div class=\"rb-worlds\">'+Object.keys(WORLDS).map(function(k){ var w=WORLDS[k]; return '<button class=\"rb-world\" data-k=\"'+k+'\" style=\"background:var('+w.s+')!important;border-color:'+(k===state.user.world?'var('+w.c+')':'transparent')+'!important\"><span class=\"em\">'+w.em+'<\/span><span><b>'+w.name+'<\/b><small>'+w.ages+' \u00b7 '+w.blurb+'<\/small><\/span><\/button>'; }).join(\"\")+'<\/div>'+\n    '<p class=\"rb-small rb-muted\">Tap \u2190 at the top of any world to come back here and pick a different age.<\/p>';\n  $(\"#rb-gu\").onclick = function(){ go(\"gate\"); };\n  scr.querySelectorAll(\".rb-world\").forEach(function(b){ b.onclick=function(){ state.user.world=b.dataset.k; state.tab=\"home\"; api(\"profile\",{world:b.dataset.k}).catch(function(){}); go(\"world\"); }; });\n  var g=0, hi=$(\"#rb-hi\"), lang=$(\"#rb-lang\");\n  hiTimer = setInterval(function(){ hi.classList.add(\"fade\"); setTimeout(function(){ g=(g+1)%GREETINGS.length; hi.textContent=GREETINGS[g][0]; lang.textContent=GREETINGS[g][1]; hi.classList.remove(\"fade\"); },350); },2400);\n}\n\n\/* ---------- 6. Inside a world: coming soon ---------- *\/\nvar TABS = [[\"home\",\"\ud83c\udfe0\",\"Home\"],[\"stories\",\"\ud83d\udcd6\",\"Stories\"],[\"songs\",\"\ud83c\udfb5\",\"Songs\"],[\"games\",\"\ud83c\udfae\",\"Games\"],[\"chars\",\"\ud83d\ude4b\",\"Characters\"]];\nvar SOON = {\n  home:[\"\ud83d\udcd6\",\"Stories are on their way!\",\"Rati is getting the first Bible stories ready for you. Come back soon!\"],\n  stories:[\"\ud83d\udcd6\",\"Bible stories coming soon\",\"Creation, Noah\u2019s ark, David and Goliath and more are on their way.\"],\n  songs:[\"\ud83c\udfb5\",\"Songs coming soon\",\"Happy sing-along songs about each Bible story.\"],\n  games:[\"\ud83c\udfae\",\"Games coming soon\",\"Fun Bible games to play after every story. Win stickers!\"],\n  chars:[\"\ud83d\ude4b\",\"Characters coming soon\",\"Every time you finish a story, a Bible friend will pop in to say hello.\"]\n};\nfunction world(){\n  var w = WORLDS[state.user.world], s = sym(state.user.symbol);\n  topBar.innerHTML = '<div class=\"rb-topbar\"><button class=\"rb-icon-btn\" id=\"rb-ages\" aria-label=\"Back to all ages\">\u2190<\/button><span class=\"nm\">'+w.em+' '+w.name+'<\/span><span class=\"rb-badge\" style=\"border:0\">'+(s?s[1]:\"\")+'<\/span><button class=\"rb-icon-btn\" id=\"rb-gu\" aria-label=\"Grown-ups area\">\u2699\ufe0f<\/button><\/div>';\n  $(\"#rb-ages\").onclick = function(){ go(\"ages\"); };\n  $(\"#rb-gu\").onclick = function(){ go(\"gate\"); };\n  nav.hidden = false;\n  nav.innerHTML = TABS.map(function(t){ return '<button data-k=\"'+t[0]+'\"'+(state.tab===t[0]?' aria-current=\"page\"':'')+'><span class=\"i\">'+t[1]+'<\/span>'+t[2]+'<\/button>'; }).join(\"\");\n  nav.querySelectorAll(\"button\").forEach(function(b){ b.onclick=function(){ state.tab=b.dataset.k; go(\"world\"); }; });\n  var c = SOON[state.tab];\n  scr.className = \"rb-screen\";\n  scr.innerHTML = '<div class=\"rb-rati-row\">'+face()+'<div class=\"rb-bubble\">Hi friend! Welcome to '+w.name+'! \ud83c\udf89<\/div><\/div>'+\n    '<div class=\"rb-soon\"><span class=\"big\">'+c[0]+'<\/span><h2>'+c[1]+'<\/h2><p class=\"rb-muted\">'+c[2]+'<\/p><\/div>'+\n    (state.tab===\"home\" ? '<h3>Coming to '+w.name+'<\/h3><div class=\"rb-coming\"><div><span>\ud83d\udcd6<\/span>Stories<\/div><div><span>\ud83c\udfb5<\/span>Songs<\/div><div><span>\ud83c\udfae<\/span>Games<\/div><div><span>\ud83d\ude4b<\/span>Characters<\/div><div><span>\u2b50<\/span>Stickers<\/div><div><span>\ud83c\udfac<\/span>Videos<\/div><\/div>' : '');\n}\n\n\/* ---------- Grown-ups ---------- *\/\nfunction gate(){\n  scr.className = \"rb-screen\";\n  scr.innerHTML = '<button class=\"rb-back\" id=\"rb-bk\">\u2190 Back<\/button><h2>For grown-ups<\/h2><p class=\"rb-muted\">Press and hold the circle for 2 seconds.<\/p>'+\n    '<div class=\"rb-hold\" id=\"rb-hold\" tabindex=\"0\"><svg viewBox=\"0 0 100 100\" aria-hidden=\"true\"><circle cx=\"50\" cy=\"50\" r=\"48\" fill=\"none\" stroke=\"#e0457b\" stroke-width=\"4\" stroke-dasharray=\"302\" stroke-dashoffset=\"302\" id=\"rb-ring\" stroke-linecap=\"round\"\/><\/svg><span>Hold here<\/span><\/div>';\n  $(\"#rb-bk\").onclick = function(){ go(state.user.world ? \"world\" : \"ages\"); };\n  var ring=$(\"#rb-ring\"), h=$(\"#rb-hold\"), t0=0;\n  function tick(){ var p=Math.min(1,(performance.now()-t0)\/2000); ring.style.strokeDashoffset=302*(1-p); if(p>=1){ go(\"grownups\"); return; } holdRaf=requestAnimationFrame(tick); }\n  function stop(){ cancelAnimationFrame(holdRaf); ring.style.strokeDashoffset=302; }\n  h.addEventListener(\"pointerdown\", function(e){ e.preventDefault(); t0=performance.now(); holdRaf=requestAnimationFrame(tick); });\n  [\"pointerup\",\"pointerleave\",\"pointercancel\"].forEach(function(ev){ h.addEventListener(ev, stop); });\n  h.addEventListener(\"keydown\", function(e){ if(e.key===\"Enter\") go(\"grownups\"); });\n}\nfunction grownups(){\n  var s = sym(state.user.symbol), w = WORLDS[state.user.world];\n  scr.className = \"rb-screen\";\n  scr.innerHTML = '<button class=\"rb-back\" id=\"rb-bk\">\u2190 Back to the app<\/button><h2>Grown-ups area<\/h2>'+\n    '<div class=\"rb-card\"><b>Your account<\/b><p style=\"overflow-wrap:anywhere\">'+(s?s[1]+' ':'')+esc(state.user.email)+'<\/p><p class=\"rb-small rb-muted\">World: '+(w?w.name+' ('+w.ages+')':'not chosen yet')+'<\/p>'+\n      '<div style=\"display:flex;gap:8px;flex-wrap:wrap\"><button class=\"rb-pill\" id=\"rb-cw\">Change world<\/button><button class=\"rb-pill\" id=\"rb-lo\">Log out<\/button><\/div><\/div>'+\n    '<details open><summary>\ud83d\udcac Tell us what you think<\/summary><form id=\"rb-fb\" style=\"display:flex;flex-direction:column;gap:14px\" novalidate>'+\n      '<div class=\"rb-q first\"><b>1. Did you enjoy the app?<\/b><div class=\"rb-stars\" id=\"rb-st\">'+[1,2,3,4,5].map(function(n){ return '<button type=\"button\" data-n=\"'+n+'\" aria-label=\"'+n+' out of 5\">\u2b50<\/button>'; }).join(\"\")+'<\/div>'+\n        '<div class=\"rb-field\"><label for=\"rb-c1\">Comment<\/label><textarea id=\"rb-c1\" rows=\"2\"><\/textarea><\/div><\/div>'+\n      '<div class=\"rb-q\"><b>2. Would you recommend the app to other parents, Sunday school teachers or caregivers?<\/b><div class=\"rb-yn\" id=\"rb-yn\"><button type=\"button\" data-v=\"yes\" aria-pressed=\"false\">\ud83d\udc4d Yes<\/button><button type=\"button\" data-v=\"no\" aria-pressed=\"false\">\ud83d\udc4e No<\/button><\/div>'+\n        '<div class=\"rb-field\"><label for=\"rb-c2\">Comment<\/label><textarea id=\"rb-c2\" rows=\"2\"><\/textarea><\/div><\/div>'+\n      '<div class=\"rb-q\"><b>3. Any other feedback to help us improve the app?<\/b><div class=\"rb-field\"><label for=\"rb-c3\">Comment<\/label><textarea id=\"rb-c3\" rows=\"3\"><\/textarea><\/div><\/div>'+\n      '<p class=\"rb-small rb-muted\">Please don\u2019t include your child\u2019s name or personal details.<\/p>'+\n      '<div id=\"rb-fm\"><\/div><button class=\"rb-big\" type=\"submit\">Send feedback<\/button><\/form><\/details>'+\n    '<details><summary>\ud83d\udcf2 Put the app on your phone<\/summary><button class=\"rb-pill primary\" id=\"rb-in\">Show me how<\/button><\/details>'+\n    '<details><summary>\ud83d\udd12 Privacy<\/summary><p>We keep your email address so you can log in, and your child\u2019s symbol so we can save their progress. Your password is stored encrypted: nobody, including us, can see it. Children never share their name or personal details. We will never sell your information. Questions: info@ratiandherbiblebuddies.co.za<\/p><\/details>';\n  $(\"#rb-bk\").onclick = function(){ go(state.user.world ? \"world\" : \"ages\"); };\n  $(\"#rb-cw\").onclick = function(){ go(\"ages\"); };\n  $(\"#rb-in\").onclick = showInstall;\n  $(\"#rb-lo\").onclick = function(){ api(\"logout\").catch(function(){}).then(function(){ state.user=null; if(R) R.nonce=\"\"; toast(\"Logged out\"); go(\"welcome\"); }); };\n  var rating=0, rec=\"\";\n  scr.querySelectorAll(\"#rb-st button\").forEach(function(b){ b.onclick=function(){ rating=+b.dataset.n; scr.querySelectorAll(\"#rb-st button\").forEach(function(x){ x.classList.toggle(\"on\", +x.dataset.n<=rating); }); }; });\n  scr.querySelectorAll(\"#rb-yn button\").forEach(function(b){ b.onclick=function(){ rec=b.dataset.v; scr.querySelectorAll(\"#rb-yn button\").forEach(function(x){ x.setAttribute(\"aria-pressed\", x===b); }); }; });\n  $(\"#rb-fb\").onsubmit = function(e){ e.preventDefault(); var m=$(\"#rb-fm\"), btn=this.querySelector(\"button[type=submit]\");\n    if(!rating){ m.innerHTML='<p class=\"rb-err\">Please rate the app out of 5 (question 1).<\/p>'; return; }\n    if(!rec){ m.innerHTML='<p class=\"rb-err\">Please choose Yes or No for question 2.<\/p>'; return; }\n    btn.disabled=true; btn.textContent=\"Sending\u2026\";\n    api(\"feedback\",{rating:rating, recommend:rec, enjoy_comment:$(\"#rb-c1\").value, recommend_comment:$(\"#rb-c2\").value, other_comment:$(\"#rb-c3\").value})\n      .then(function(){ m.innerHTML='<p class=\"rb-ok\">Thank you! \ud83d\udc9b Your feedback helps shape the app.<\/p>'; btn.textContent=\"Sent \u2713\"; })\n      .catch(function(err){ btn.disabled=false; btn.textContent=\"Send feedback\"; m.innerHTML='<p class=\"rb-err\">'+esc(err.message)+'<\/p>'; });\n  };\n}\n\n\/* ---------- Add to home screen pop-up ---------- *\/\nvar deferredPrompt = null;\nwindow.addEventListener(\"beforeinstallprompt\", function(e){ e.preventDefault(); deferredPrompt = e; });\nfunction isInstalled(){ return (window.matchMedia && matchMedia(\"(display-mode: standalone)\").matches) || navigator.standalone === true; }\nfunction isIOS(){ return \/iphone|ipad|ipod\/i.test(navigator.userAgent) || (navigator.platform===\"MacIntel\" && navigator.maxTouchPoints>1); }\nfunction showInstall(){\n  if (isInstalled()) { toast(\"The app is already on your home screen \ud83c\udf89\"); return; }\n  var ios = isIOS(), tab = ios ? \"ios\" : \"android\";\n  function render(){\n    var icon = LOGO_URL ? '<img decoding=\"async\" class=\"icon\" src=\"'+esc(LOGO_URL)+'\" alt=\"\">' : '';\n    var steps = tab===\"ios\"\n      ? '<ol class=\"rb-steps\"><li>Open this page in <b>Safari<\/b>.<\/li><li>Tap the <b>Share<\/b> button (the square with an arrow \u2b06\ufe0f).<\/li><li>Scroll down and tap <b>\u201cAdd to Home Screen\u201d<\/b>.<\/li><li>Tap <b>Add<\/b>. Rati is now on your home screen!<\/li><\/ol>'\n      : '<ol class=\"rb-steps\"><li>Open this page in <b>Chrome<\/b>.<\/li><li>Tap the <b>\u22ee<\/b> menu at the top right.<\/li><li>Tap <b>\u201cAdd to Home screen\u201d<\/b> or <b>\u201cInstall app\u201d<\/b>.<\/li><li>Tap <b>Install<\/b>. Rati is now on your home screen!<\/li><\/ol>';\n    modal.innerHTML = '<div class=\"rb-sheet\" role=\"dialog\" aria-modal=\"true\" aria-labelledby=\"rb-it\">'+icon+\n      '<h2 id=\"rb-it\" style=\"text-align:center\">Put Rati on your phone! \ud83d\udcf2<\/h2>'+\n      '<p class=\"rb-muted\" style=\"text-align:center\">This preview is a web app. Add it to your home screen and it opens with one tap, just like a real app. Nothing to download from an app store.<\/p>'+\n      (deferredPrompt && tab===\"android\" ? '<button class=\"rb-big green\" id=\"rb-now\">Install now<\/button>' : '')+\n      '<div class=\"rb-tabs\"><button data-t=\"android\" aria-pressed=\"'+(tab===\"android\")+'\">Android<\/button><button data-t=\"ios\" aria-pressed=\"'+(tab===\"ios\")+'\">iPhone \/ iPad<\/button><\/div>'+ steps +\n      '<button class=\"rb-pill\" id=\"rb-x\" style=\"align-self:center\">Maybe later<\/button><\/div>';\n    modal.hidden = false;\n    modal.querySelectorAll(\".rb-tabs button\").forEach(function(b){ b.onclick=function(){ tab=b.dataset.t; render(); }; });\n    $(\"#rb-x\").onclick = closeModal;\n    var now=$(\"#rb-now\"); if(now) now.onclick=function(){ deferredPrompt.prompt(); deferredPrompt.userChoice.finally(function(){ deferredPrompt=null; closeModal(); }); };\n  }\n  render();\n}\nfunction closeModal(){ modal.hidden = true; modal.innerHTML = \"\"; store(\"rb-install-seen\", Date.now()); }\nmodal.addEventListener(\"click\", function(e){ if (e.target === modal) closeModal(); });\nfunction maybeAutoInstall(){ if (isInstalled()) return; var seen = store(\"rb-install-seen\"); if (seen && Date.now()-seen < 3*864e5) return; setTimeout(function(){ if (!state.user && modal.hidden && scr.classList.contains(\"rb-welcome\")) showInstall(); }, 4200); }\n\n\/* ---------- start ---------- *\/\nvar VIEWS = {welcome:welcome, signup:signup, preview:preview, symbol:symbol, login:login, forgot:forgot, reset:reset, ages:ages, world:world, gate:gate, grownups:grownups};\nvar q = new URLSearchParams(location.search);\nif (q.get(\"rati_reset\") && q.get(\"key\")) {\n  var p = {key:q.get(\"key\"), login:q.get(\"login\")||\"\"};\n  try { history.replaceState(null, \"\", location.pathname); } catch(e){}\n  go(\"reset\", p);\n} else if (state.user) {\n  if (!state.user.symbol) { state.draft={mode:\"existing\"}; go(\"preview\"); } else go(\"ages\");\n} else go(\"welcome\");\n})();\n<\/script>\n<!-- ===== end of Rati app ===== -->\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t","protected":false},"excerpt":{"rendered":"","protected":false},"author":1,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"elementor_canvas","meta":{"footnotes":""},"class_list":["post-13","page","type-page","status-publish","hentry"],"_links":{"self":[{"href":"https:\/\/app.ratiandherbiblebuddies.co.za\/index.php?rest_route=\/wp\/v2\/pages\/13","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/app.ratiandherbiblebuddies.co.za\/index.php?rest_route=\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/app.ratiandherbiblebuddies.co.za\/index.php?rest_route=\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/app.ratiandherbiblebuddies.co.za\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/app.ratiandherbiblebuddies.co.za\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=13"}],"version-history":[{"count":13,"href":"https:\/\/app.ratiandherbiblebuddies.co.za\/index.php?rest_route=\/wp\/v2\/pages\/13\/revisions"}],"predecessor-version":[{"id":30,"href":"https:\/\/app.ratiandherbiblebuddies.co.za\/index.php?rest_route=\/wp\/v2\/pages\/13\/revisions\/30"}],"wp:attachment":[{"href":"https:\/\/app.ratiandherbiblebuddies.co.za\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=13"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}