1<!doctype html> 2<html lang="en-AU"> 3 <head> 4 <meta charset="UTF-8" /> 5 <!-- 6 The document is served at any path, and the Flutter loader that embed.js 7 appends resolves main.dart.js and its assets against document.baseURI: 8 without this, a deep link at /lesson-plan/<id> would fetch them from 9 /lesson-plan/. The shell's own URLs are absolute under /_shell/. 10 --> 11 <base href="/" /> 12 <meta name="viewport" content="width=device-width, initial-scale=1.0" /> 13 <!-- 14 The marketing front door's purple (R158): the browser paints its own chrome 15 with this before a pixel of the page, so a blue bar over a purple hero is 16 the first thing a visitor sees get corrected. Tutoring's document keeps its 17 own; `src/documents.test.ts` holds the pair in step on everything else. 18 --> 19 <meta name="theme-color" content="#B300EE" /> 20 <!-- The marker the smoke reads: this document is the shell's, not Flutter's. --> 21 <meta name="tutero-app" content="shell" /> 22 <!-- 23 A returning browser may still run the old Flutter page's COI service 24 worker (coi-serviceworker.js, scope /): it re-issues every request, and 25 a navigation re-issued that way reads as no document at the edge, so the 26 browser was handed the old page instead of this one (2026-09-06). Evict 27 every registration and, if this document came through one, load it again 28 clean â once: with nothing registered, the second load evicts nothing. 29 --> 30
30<script> 31 if ('serviceWorker' in navigator) { 32 navigator.serviceWorker 33 .getRegistrations() 34 .then(function (registrations) { 35 return Promise.all( 36 registrations.map(function (registration) { 37 return registration.unregister(); 38 }), 39 ); 40 }) 41 .then(function (gone) { 42 if (gone.some(Boolean) && navigator.serviceWorker.controller) 43 location.reload(); 44 }) 45 .catch(function () {}); 46 } 47 </script>
47 48 49 <!-- 50 The ad networks' bootstrap, the Flutter build's own file, from the origin 51 (`shell-analytics` design D2). Flutter's index.html loaded it and this 52 document replaced that page, so without this line the Meta pixel never 53 initialises, the landing's click ids and campaign are never captured, and 54 every `metaPixel*` global the hosted app calls after the reveal is 55 undefined - which that app's own guards turn into silence rather than an 56 error, so nothing anywhere said the conversions had stopped (the audit, 57 2026-09-16). 58 59 The file's own `META_TRACKED_HOSTS` gate is what keeps dev, staging, 60 preview and the rig from reporting; the capture into storage runs on every 61 host that loads it, and a signup on a later document reads it back. 62 63 `defer`, as Flutter's page has it: a module script is deferred too, so 64 document order still runs this first - before React mounts, and so before 65 the shell replaces the entry the visitor arrived by, which is the one 66 thing that would strip the query this file exists to read. Root-absolute 67 and the origin's, like every other asset in this head: it is not a 68 document, so the Worker forwards it to Flutter's nginx. 69 70 Tutoring's document does not carry it. The pixel, its id and the Mixpanel 71 tokens beside it are the SCHOOLS project's, and `src/documents.test.ts` 72 holds that difference. 73 --> 74
74<script src="/ad_pixel.js" defer></script>
74 75 76 <!-- 77 The public front door's head, ported from schools-app/web/index.html: this 78 document replaces Flutter's on every URL of tutero.ai, so the title, the 79 description, the canonical and the social card have to be here or they are 80 gone. The tutoring product's head is its own document, tutoring.html 81 beside this file, and the Worker picks by the row's product (tutoring-shell 82 design D2); src/documents.test.ts keeps the two in step. Every asset 83 below is root-relative and the Flutter origin's â /icons, /favicon.*, 84 /manifest.json are files, so the Worker forwards them there. 85 --> 86 <title>The AI Teaching Platform for Schools | Tutero AI</title> 87
87<script> 88 // The <title> above stays descriptive for a crawler that reads raw HTML. 89 // People see this instead, from the first frame: Flutter sets "Tutero AI" 90 // once it boots (schools-app #4690), and its own index.html does the same 91 // pre-boot (#4749), but that page is never the document here â so without 92 // this the tab shows the long SEO title for the whole loading state. 93 document.title = 'Tutero AI'; 94 </script>
94 95 <meta 96 name="title" 97 content="The AI Teaching Platform for Schools | Tutero AI" 98 /> 99 <meta 100 name="description" 101 content="Curriculum-aligned resources for every subject, every year level, every student. Free for teachers. Built for schools." 102 /> 103 <!-- One search surface: every host and route points at tutero.ai. --> 104 <link rel="canonical" href="https://tutero.ai/" /> 105 <meta property="og:type" content="website" /> 106 <meta property="og:site_name" content="Tutero AI" /> 107 <meta property="og:locale" content="en_AU" /> 108 <meta property="og:locale:alternate" content="en_US" /> 109 <meta property="og:url" content="https://tutero.ai/" /> 110 <meta 111 property="og:title" 112 content="The AI Teaching Platform for Schools | Tutero AI" 113 /> 114 <meta 115 property="og:description" 116 content="Curriculum-aligned resources for every subject, every year level, every student. Free for teachers. Built for schools." 117 /> 118 <!-- 119 Root-relative on purpose, as Flutter's page had it: the card ships with the 120 Flutter build and resolves against whichever host serves the page; an 121 absolute tutero.ai URL 404s on every other host. og:url and canonical stay 122 absolute â those are identity, not per-host assets. 123 --> 124 <meta property="og:image" content="/icons/tutero_ai_card.png" /> 125 <meta property="og:image:width" content="1200" /> 126 <meta property="og:image:height" content="630" /> 127 <meta 128 property="og:image:alt" 129 content="Tutero AI â The AI Teaching Platform for Schools" 130 /> 131 <meta name="twitter:card" content="summary_large_image" /> 132 <meta name="twitter:url" content="https://tutero.ai/" /> 133 <meta 134 name="twitter:title" 135 content="The AI Teaching Platform for Schools | Tutero AI" 136 /> 137 <meta 138 name="twitter:description" 139 content="Curriculum-aligned resources for every subject, every year level, every student. Free for teachers. Built for schools." 140 /> 141 <meta name="twitter:image" content="/icons/tutero_ai_card.png" /> 142 143 <meta name="apple-mobile-web-app-capable" content="yes" /> 144 <meta name="apple-mobile-web-app-status-bar-style" content="black" /> 145 <meta name="apple-mobile-web-app-title" content="Tutero AI" /> 146 <!-- 147 Icons keep fixed paths on the Flutter origin, whose nginx caches them for 148 30 days, so an unversioned URL pins whatever face a browser saw first. 149 Flutter's postbuild.sh versions its own index.html, which is never the 150 document here. Bump v when the schools icons are redrawn (2026-09-03: 151 the gradient bulb, schools-app e4cc48ef9). 152 --> 153 <link 154 rel="apple-touch-icon" 155 sizes="180x180" 156 href="/icons/apple-touch-icon-180.png?v=20260903" 157 /> 158 <link rel="icon" type="image/svg+xml" href="/favicon.svg?v=20260903" /> 159 <link rel="icon" type="image/x-icon" href="/favicon.ico?v=20260903" /> 160 <link 161 rel="icon" 162 type="image/png" 163 sizes="32x32" 164 href="/favicon-32.png?v=20260903" 165 /> 166 <link rel="manifest" href="/manifest.json" /> 167
167<script type="application/ld+json"> 168 { 169 "@context": "https://schema.org", 170 "@type": "WebSite", 171 "name": "Tutero AI", 172 "url": "https://tutero.ai/", 173 "description": "Curriculum-aligned resources for every subject, every year level, every student. Free for teachers. Built for schools.", 174 "publisher": { 175 "@type": "Organization", 176 "name": "Tutero", 177 "url": "https://tutero.ai/", 178 "logo": "https://tutero.ai/icons/Icon-512.png" 179 } 180 } 181 </script>
181 182
182<script type="module" crossorigin src="/_shell/assets/jsx-runtime-B-hcVAMW.js"></script>
182 183
183<script type="module" crossorigin src="/_shell/assets/react-dom-sF8Euvcq.js"></script>
183 184
184<script type="module" crossorigin src="/_shell/assets/gateway-ddsAfzqb.js"></script>
184 185
185<script type="module" crossorigin src="/_shell/assets/createLucideIcon-obE1SVnH.js"></script>
185 186
186<script type="module" crossorigin src="/_shell/assets/hosts--siLJs60.js"></script>
186 187
187<script type="module" crossorigin src="/_shell/assets/index.esm-BUF99XsV.js"></script>
187 188
188<script type="module" crossorigin src="/_shell/assets/routes-B_nT1eGE.js"></script>
188 189
189<script type="module" crossorigin src="/_shell/assets/graphql-BS9dRCSv.js"></script>
189 190
190<script type="module" crossorigin src="/_shell/assets/ClientError-DAeenrJp.js"></script>
190 191
191<script type="module" crossorigin src="/_shell/assets/glyphs-rCf5WIFi.js"></script>
191 192
192<script type="module" crossorigin src="/_shell/assets/main-Dv6ObtyW.js"></script>
192 193 <link rel="stylesheet" crossorigin href="/_shell/assets/main-BI0iH-4l.css"> 194 <link rel="preload" as="font" type="font/woff2" href="/_shell/assets/material-symbols-rounded.subset-vCmJCx2P.woff2" crossorigin=""> 195 <link rel="preload" as="font" type="font/woff2" href="/_shell/assets/material-icons.subset-Iozlg0Ir.woff2" crossorigin=""> 196 <link rel="preload" as="font" type="font/woff2" href="/_shell/assets/satoshi-900-Dz0lUPy3.woff2" crossorigin=""> 197 <link rel="preload" as="font" type="font/woff2" href="/_shell/assets/satoshi-700-D1Ud6wMl.woff2" crossorigin=""> 198 <link rel="preload" as="font" type="font/woff2" href="/_shell/assets/hanken-grotesk-400-cejaMU9T.woff2" crossorigin=""> 199 </head> 200 <body> 201 <div id="root"></div> 202 </body> 203</html>
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.