synthos 0.10.1 → 0.11.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (311) hide show
  1. package/README.md +5 -5
  2. package/default-pages/elevenlabs_effects_studio/chat-history.json +1 -0
  3. package/default-pages/elevenlabs_effects_studio/page.html +1345 -1363
  4. package/default-pages/elevenlabs_effects_studio/page.json +13 -11
  5. package/default-pages/elevenlabs_voice_studio/chat-history.json +1 -0
  6. package/default-pages/elevenlabs_voice_studio/page.html +782 -801
  7. package/default-pages/elevenlabs_voice_studio/page.json +13 -11
  8. package/default-pages/json_tools/chat-history.json +1 -0
  9. package/default-pages/json_tools/page.html +70 -90
  10. package/default-pages/json_tools/page.json +12 -10
  11. package/default-pages/my_notes/chat-history.json +1 -0
  12. package/default-pages/my_notes/page.html +115 -131
  13. package/default-pages/my_notes/page.json +14 -12
  14. package/default-pages/neon_asteroids/chat-history.json +1 -0
  15. package/default-pages/neon_asteroids/page.html +1777 -1803
  16. package/default-pages/neon_asteroids/page.json +14 -12
  17. package/default-pages/oregon_trail/chat-history.json +1 -0
  18. package/default-pages/oregon_trail/page.html +290 -307
  19. package/default-pages/oregon_trail/page.json +14 -12
  20. package/default-pages/solar_explorer/chat-history.json +1 -0
  21. package/default-pages/solar_explorer/page.html +1929 -1951
  22. package/default-pages/solar_explorer/page.json +14 -12
  23. package/default-pages/solar_tutorial/chat-history.json +1 -0
  24. package/default-pages/solar_tutorial/page.html +464 -478
  25. package/default-pages/solar_tutorial/page.json +12 -10
  26. package/default-pages/us_map/chat-history.json +1 -0
  27. package/default-pages/us_map/page.html +170 -193
  28. package/default-pages/us_map/page.json +14 -12
  29. package/default-pages/us_map/page.light.png +0 -0
  30. package/default-pages/us_map_1850/chat-history.json +1 -0
  31. package/default-pages/us_map_1850/page.html +302 -326
  32. package/default-pages/us_map_1850/page.json +14 -12
  33. package/default-pages/western_cities_1850/chat-history.json +1 -0
  34. package/default-pages/western_cities_1850/page.html +503 -527
  35. package/default-pages/western_cities_1850/page.json +14 -12
  36. package/default-themes/aurora-dawn.v3.css +15 -14
  37. package/default-themes/aurora-dusk.v3.css +26 -26
  38. package/default-themes/cosmos-dawn.v3.css +15 -14
  39. package/default-themes/cosmos-dusk.v3.css +26 -26
  40. package/default-themes/elemental-dawn.v3.css +200 -0
  41. package/default-themes/nebula-dawn.v3.css +15 -14
  42. package/default-themes/nebula-dusk.v3.css +24 -24
  43. package/default-themes/solar-flare-dawn.v3.css +15 -14
  44. package/default-themes/solar-flare-dusk.v3.css +26 -26
  45. package/dist/builders/anthropic.d.ts +26 -2
  46. package/dist/builders/anthropic.d.ts.map +1 -1
  47. package/dist/builders/anthropic.js +132 -31
  48. package/dist/builders/anthropic.js.map +1 -1
  49. package/dist/builders/claudecode.d.ts +13 -0
  50. package/dist/builders/claudecode.d.ts.map +1 -0
  51. package/dist/builders/claudecode.js +253 -0
  52. package/dist/builders/claudecode.js.map +1 -0
  53. package/dist/builders/index.d.ts +2 -1
  54. package/dist/builders/index.d.ts.map +1 -1
  55. package/dist/builders/index.js +8 -1
  56. package/dist/builders/index.js.map +1 -1
  57. package/dist/builders/openai.js +2 -1
  58. package/dist/builders/openai.js.map +1 -1
  59. package/dist/builders/types.d.ts +31 -7
  60. package/dist/builders/types.d.ts.map +1 -1
  61. package/dist/builders/types.js +60 -28
  62. package/dist/builders/types.js.map +1 -1
  63. package/dist/connectors/types.d.ts +8 -0
  64. package/dist/connectors/types.d.ts.map +1 -1
  65. package/dist/init.d.ts.map +1 -1
  66. package/dist/init.js +13 -6
  67. package/dist/init.js.map +1 -1
  68. package/dist/migrations.d.ts.map +1 -1
  69. package/dist/migrations.js +161 -14
  70. package/dist/migrations.js.map +1 -1
  71. package/dist/models/anthropic.d.ts +1 -0
  72. package/dist/models/anthropic.d.ts.map +1 -1
  73. package/dist/models/anthropic.js +129 -29
  74. package/dist/models/anthropic.js.map +1 -1
  75. package/dist/models/chainOfThought.d.ts.map +1 -1
  76. package/dist/models/chainOfThought.js +32 -19
  77. package/dist/models/chainOfThought.js.map +1 -1
  78. package/dist/models/index.d.ts +2 -2
  79. package/dist/models/index.d.ts.map +1 -1
  80. package/dist/models/index.js +2 -1
  81. package/dist/models/index.js.map +1 -1
  82. package/dist/models/providers.d.ts +1 -0
  83. package/dist/models/providers.d.ts.map +1 -1
  84. package/dist/models/providers.js +12 -4
  85. package/dist/models/providers.js.map +1 -1
  86. package/dist/models/types.d.ts +15 -1
  87. package/dist/models/types.d.ts.map +1 -1
  88. package/dist/models/types.js.map +1 -1
  89. package/dist/pages.d.ts +57 -8
  90. package/dist/pages.d.ts.map +1 -1
  91. package/dist/pages.js +258 -45
  92. package/dist/pages.js.map +1 -1
  93. package/dist/service/createCompletePrompt.d.ts.map +1 -1
  94. package/dist/service/createCompletePrompt.js +5 -0
  95. package/dist/service/createCompletePrompt.js.map +1 -1
  96. package/dist/service/mediaCache.d.ts +36 -0
  97. package/dist/service/mediaCache.d.ts.map +1 -0
  98. package/dist/service/mediaCache.js +182 -0
  99. package/dist/service/mediaCache.js.map +1 -0
  100. package/dist/service/pageValidator.d.ts +25 -0
  101. package/dist/service/pageValidator.d.ts.map +1 -0
  102. package/dist/service/pageValidator.js +315 -0
  103. package/dist/service/pageValidator.js.map +1 -0
  104. package/dist/service/server.d.ts.map +1 -1
  105. package/dist/service/server.js +4 -0
  106. package/dist/service/server.js.map +1 -1
  107. package/dist/service/sharedTableSchema.d.ts +73 -0
  108. package/dist/service/sharedTableSchema.d.ts.map +1 -0
  109. package/dist/service/sharedTableSchema.js +206 -0
  110. package/dist/service/sharedTableSchema.js.map +1 -0
  111. package/dist/service/transformPage.d.ts +49 -11
  112. package/dist/service/transformPage.d.ts.map +1 -1
  113. package/dist/service/transformPage.js +354 -241
  114. package/dist/service/transformPage.js.map +1 -1
  115. package/dist/service/useApiRoutes.d.ts.map +1 -1
  116. package/dist/service/useApiRoutes.js +285 -34
  117. package/dist/service/useApiRoutes.js.map +1 -1
  118. package/dist/service/useConnectorRoutes.d.ts.map +1 -1
  119. package/dist/service/useConnectorRoutes.js +170 -32
  120. package/dist/service/useConnectorRoutes.js.map +1 -1
  121. package/dist/service/useDataRoutes.d.ts.map +1 -1
  122. package/dist/service/useDataRoutes.js +59 -2
  123. package/dist/service/useDataRoutes.js.map +1 -1
  124. package/dist/service/useExtractRoutes.d.ts +4 -0
  125. package/dist/service/useExtractRoutes.d.ts.map +1 -0
  126. package/dist/service/useExtractRoutes.js +304 -0
  127. package/dist/service/useExtractRoutes.js.map +1 -0
  128. package/dist/service/usePageRoutes.d.ts +17 -0
  129. package/dist/service/usePageRoutes.d.ts.map +1 -1
  130. package/dist/service/usePageRoutes.js +1388 -483
  131. package/dist/service/usePageRoutes.js.map +1 -1
  132. package/dist/service/useSharedDataRoutes.d.ts.map +1 -1
  133. package/dist/service/useSharedDataRoutes.js +54 -2
  134. package/dist/service/useSharedDataRoutes.js.map +1 -1
  135. package/dist/settings.d.ts +27 -0
  136. package/dist/settings.d.ts.map +1 -1
  137. package/dist/settings.js +40 -1
  138. package/dist/settings.js.map +1 -1
  139. package/dist/themes.d.ts +0 -5
  140. package/dist/themes.d.ts.map +1 -1
  141. package/dist/themes.js +3 -95
  142. package/dist/themes.js.map +1 -1
  143. package/migration-rules/v2-to-v3.md +277 -119
  144. package/package.json +5 -1
  145. package/{default-pages/application → required-pages/_shell}/page.html +56 -42
  146. package/required-pages/_shell/page.json +14 -0
  147. package/required-pages/_starters/page.html +534 -0
  148. package/required-pages/_starters/page.json +12 -0
  149. package/required-pages/builder/page.html +353 -43
  150. package/required-pages/builder/page.json +12 -10
  151. package/required-pages/pages/page.html +697 -924
  152. package/required-pages/pages/page.json +12 -10
  153. package/required-pages/settings/page.html +1888 -1753
  154. package/required-pages/settings/page.json +12 -10
  155. package/required-pages/synthos_apis/page.html +834 -845
  156. package/required-pages/synthos_apis/page.json +12 -10
  157. package/required-pages/synthos_scripts/page.html +74 -88
  158. package/required-pages/synthos_scripts/page.json +12 -10
  159. package/scripts/append-instructions.py +90 -0
  160. package/scripts/audit-instructions.py +76 -0
  161. package/scripts/cleanup-shell-markup.mjs +112 -0
  162. package/service-connectors/buffer/connector.json +46 -0
  163. package/service-connectors/canva/connector.json +67 -0
  164. package/service-connectors/elevenlabs/connector.json +1 -1
  165. package/src/builders/anthropic.ts +150 -25
  166. package/src/builders/claudecode.ts +310 -0
  167. package/src/builders/index.ts +7 -1
  168. package/src/builders/openai.ts +2 -1
  169. package/src/builders/types.ts +93 -32
  170. package/src/connectors/types.ts +8 -0
  171. package/src/init.ts +13 -7
  172. package/src/migrations.ts +187 -16
  173. package/src/models/anthropic.ts +140 -30
  174. package/src/models/chainOfThought.ts +33 -18
  175. package/src/models/index.ts +2 -2
  176. package/src/models/providers.ts +10 -1
  177. package/src/models/types.ts +21 -1
  178. package/src/pages.ts +271 -35
  179. package/src/service/createCompletePrompt.ts +6 -0
  180. package/src/service/mediaCache.ts +206 -0
  181. package/src/service/pageValidator.ts +337 -0
  182. package/src/service/server.ts +4 -0
  183. package/src/service/sharedTableSchema.ts +236 -0
  184. package/src/service/transformPage.ts +370 -260
  185. package/src/service/useApiRoutes.ts +283 -32
  186. package/src/service/useConnectorRoutes.ts +189 -34
  187. package/src/service/useDataRoutes.ts +198 -116
  188. package/src/service/useExtractRoutes.ts +331 -0
  189. package/src/service/usePageRoutes.ts +1414 -394
  190. package/src/service/useSharedDataRoutes.ts +184 -109
  191. package/src/settings.ts +65 -0
  192. package/src/themes.ts +78 -180
  193. package/starters/blank_starter/chat-history.json +1 -0
  194. package/starters/blank_starter/page.dark.png +0 -0
  195. package/starters/blank_starter/page.html +47 -0
  196. package/starters/blank_starter/page.json +13 -0
  197. package/starters/blank_starter/page.light.png +0 -0
  198. package/starters/calculator_starter/chat-history.json +1 -0
  199. package/starters/calculator_starter/page.dark.png +0 -0
  200. package/starters/calculator_starter/page.html +232 -0
  201. package/starters/calculator_starter/page.json +13 -0
  202. package/starters/calculator_starter/page.light.png +0 -0
  203. package/starters/calendar_starter/chat-history.json +1 -0
  204. package/starters/calendar_starter/page.dark.png +0 -0
  205. package/starters/calendar_starter/page.html +495 -0
  206. package/starters/calendar_starter/page.json +13 -0
  207. package/starters/calendar_starter/page.light.png +0 -0
  208. package/starters/chat_starter/chat-history.json +1 -0
  209. package/starters/chat_starter/page.dark.png +0 -0
  210. package/starters/chat_starter/page.html +351 -0
  211. package/starters/chat_starter/page.json +13 -0
  212. package/starters/chat_starter/page.light.png +0 -0
  213. package/starters/checklist_starter/chat-history.json +1 -0
  214. package/starters/checklist_starter/page.dark.png +0 -0
  215. package/starters/checklist_starter/page.html +437 -0
  216. package/starters/checklist_starter/page.json +13 -0
  217. package/starters/checklist_starter/page.light.png +0 -0
  218. package/starters/dashboard_starter/chat-history.json +1 -0
  219. package/starters/dashboard_starter/page.dark.png +0 -0
  220. package/starters/dashboard_starter/page.html +195 -0
  221. package/starters/dashboard_starter/page.json +13 -0
  222. package/starters/dashboard_starter/page.light.png +0 -0
  223. package/starters/form_starter/chat-history.json +1 -0
  224. package/starters/form_starter/page.dark.png +0 -0
  225. package/starters/form_starter/page.html +313 -0
  226. package/starters/form_starter/page.json +13 -0
  227. package/starters/form_starter/page.light.png +0 -0
  228. package/starters/gallery_starter/chat-history.json +1 -0
  229. package/starters/gallery_starter/page.dark.png +0 -0
  230. package/starters/gallery_starter/page.html +418 -0
  231. package/starters/gallery_starter/page.json +13 -0
  232. package/starters/gallery_starter/page.light.png +0 -0
  233. package/starters/generator_starter/chat-history.json +1 -0
  234. package/starters/generator_starter/page.dark.png +0 -0
  235. package/starters/generator_starter/page.html +261 -0
  236. package/starters/generator_starter/page.json +13 -0
  237. package/starters/generator_starter/page.light.png +0 -0
  238. package/starters/index.html +538 -0
  239. package/starters/kanban_starter/chat-history.json +1 -0
  240. package/starters/kanban_starter/page.dark.png +0 -0
  241. package/starters/kanban_starter/page.html +432 -0
  242. package/starters/kanban_starter/page.json +13 -0
  243. package/starters/kanban_starter/page.light.png +0 -0
  244. package/starters/presentation_builder/chat-history.json +1 -0
  245. package/starters/presentation_builder/page.dark.png +0 -0
  246. package/starters/presentation_builder/page.html +970 -0
  247. package/starters/presentation_builder/page.json +15 -0
  248. package/starters/presentation_builder/page.light.png +0 -0
  249. package/starters/presentation_builder/presentation_voice/voice_config.json +9 -0
  250. package/starters/pulse_starter/chat-history.json +1 -0
  251. package/starters/pulse_starter/page.dark.png +0 -0
  252. package/starters/pulse_starter/page.html +698 -0
  253. package/starters/pulse_starter/page.json +13 -0
  254. package/starters/pulse_starter/page.light.png +0 -0
  255. package/starters/quiz_starter/chat-history.json +1 -0
  256. package/starters/quiz_starter/page.dark.png +0 -0
  257. package/starters/quiz_starter/page.html +292 -0
  258. package/starters/quiz_starter/page.json +13 -0
  259. package/starters/quiz_starter/page.light.png +0 -0
  260. package/starters/reference_starter/chat-history.json +1 -0
  261. package/starters/reference_starter/page.dark.png +0 -0
  262. package/starters/reference_starter/page.html +250 -0
  263. package/starters/reference_starter/page.json +13 -0
  264. package/starters/reference_starter/page.light.png +0 -0
  265. package/starters/retro_game_starter/chat-history.json +1 -0
  266. package/starters/retro_game_starter/page.dark.png +0 -0
  267. package/{default-pages → starters}/retro_game_starter/page.html +1281 -1308
  268. package/starters/retro_game_starter/page.json +15 -0
  269. package/starters/retro_game_starter/page.light.png +0 -0
  270. package/starters/roster_starter/chat-history.json +1 -0
  271. package/starters/roster_starter/page.dark.png +0 -0
  272. package/starters/roster_starter/page.html +600 -0
  273. package/starters/roster_starter/page.json +13 -0
  274. package/starters/roster_starter/page.light.png +0 -0
  275. package/starters/server.js +182 -0
  276. package/starters/start.cmd +1 -0
  277. package/starters/timeline_starter/chat-history.json +1 -0
  278. package/starters/timeline_starter/page.dark.png +0 -0
  279. package/starters/timeline_starter/page.html +446 -0
  280. package/starters/timeline_starter/page.json +13 -0
  281. package/starters/timeline_starter/page.light.png +0 -0
  282. package/starters/tutorial_starter/chat-history.json +1 -0
  283. package/starters/tutorial_starter/page.dark.png +0 -0
  284. package/starters/tutorial_starter/page.html +283 -0
  285. package/starters/tutorial_starter/page.json +13 -0
  286. package/starters/tutorial_starter/page.light.png +0 -0
  287. package/static-files/agent.v3.js +122 -0
  288. package/static-files/connector.v3.js +48 -0
  289. package/static-files/extract.v3.js +188 -0
  290. package/static-files/helpers.v3.js +50 -6
  291. package/static-files/page-bridge.js +114 -0
  292. package/static-files/page.v3.js +1292 -1290
  293. package/static-files/script.v3.js +32 -0
  294. package/static-files/server.v3.js +89 -0
  295. package/static-files/shell-bridge.v3.js +174 -0
  296. package/static-files/shell-modals.v3.js +521 -0
  297. package/static-files/{shell.css → shell.v3.css} +271 -22
  298. package/static-files/shell.v3.js +1865 -0
  299. package/static-files/storage.v3.js +176 -0
  300. package/tests/anthropic.spec.ts +42 -7
  301. package/tests/builders.spec.ts +70 -2
  302. package/tests/pageValidator.spec.ts +548 -0
  303. package/tests/profiles.spec.ts +122 -0
  304. package/tests/sharedTableSchema.spec.ts +242 -0
  305. package/tests/transformPage.spec.ts +62 -81
  306. package/default-pages/application/page.json +0 -10
  307. package/default-pages/retro_game_starter/page.json +0 -12
  308. package/default-pages/sidebar_page/page.html +0 -51
  309. package/default-pages/sidebar_page/page.json +0 -10
  310. package/default-pages/two-panel_page/page.html +0 -68
  311. package/default-pages/two-panel_page/page.json +0 -10
@@ -1,307 +1,290 @@
1
- <!DOCTYPE html><html lang="en"><head>
2
- <meta charset="UTF-8">
3
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
4
- <title>SynthOS</title>
5
- <script id="theme-info" src="/api/theme-info.js" data-locked="true"></script>
6
- <link id="theme-css" rel="stylesheet" href="/api/theme.css" data-locked="true">
7
- <style>.idle-container{position:absolute;width:100%;height:100%;pointer-events:none;opacity:1;transition:opacity 1s ease-out}.idle-container.hidden{opacity:0}.breathing-orb{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);width:80px;height:80px;border-radius:50%;background:radial-gradient(circle,rgba(102,126,234,.15) 0,transparent 70%);animation:4s ease-in-out infinite breathe}.breathing-orb::before{content:'';position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);width:8px;height:8px;border-radius:50%;background:rgba(183,148,246,.6);box-shadow:0 0 20px rgba(183,148,246,.4);animation:4s ease-in-out infinite core-pulse}@keyframes breathe{0%,100%{width:80px;height:80px;opacity:.3}50%{width:120px;height:120px;opacity:.6}}@keyframes core-pulse{0%,100%{opacity:.4;box-shadow:0 0 20px rgba(183,148,246,.3)}50%{opacity:.8;box-shadow:0 0 30px rgba(183,148,246,.5)}}.orbit-ring{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);width:200px;height:200px;border:1px solid rgba(102,126,234,.1);border-radius:50%;animation:20s linear infinite orbit-rotate}.orbit-ring::after{content:'';position:absolute;top:-3px;left:50%;transform:translateX(-50%);width:6px;height:6px;background:rgba(240,147,251,.5);border-radius:50%;box-shadow:0 0 10px rgba(240,147,251,.3)}@keyframes orbit-rotate{from{transform:translate(-50%,-50%) rotate(0)}to{transform:translate(-50%,-50%) rotate(360deg)}}</style>
8
- <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/7.9.0/d3.min.js"></script>
9
- <script src="https://cdnjs.cloudflare.com/ajax/libs/marked/14.1.1/marked.min.js"></script>
10
- <script src="https://cdnjs.cloudflare.com/ajax/libs/mermaid/11.1.0/mermaid.min.js"></script>
11
- <script src="https://cdnjs.cloudflare.com/ajax/libs/html2canvas/1.4.1/html2canvas.min.js"></script>
12
- <script id="page-info" src="/api/page-info.js?page=builder"></script>
13
- <script src="https://cdnjs.cloudflare.com/ajax/libs/topojson/3.0.2/topojson.min.js" id="topojson-lib"></script><style id="us-map-styles">#us-map-container{width:100%;height:100%;display:flex;flex-direction:column;align-items:center;justify-content:center;position:relative;overflow:hidden}#us-map-header{min-height:var(--header-min-height);padding:var(--header-padding-vertical) var(--header-padding-horizontal);line-height:var(--header-line-height);display:flex;align-items:center;justify-content:center;box-sizing:border-box;font-size:1.25rem;font-weight:600;color:var(--bodyText);width:100%;border-bottom:1px solid var(--variantBorder);background-color:var(--bodyStandoutBackground);flex-shrink:0;position:relative}#us-map-svg-wrap{flex:1;width:100%;display:flex;align-items:center;justify-content:center;padding:16px;box-sizing:border-box;position:relative}#us-map-svg-wrap svg{width:100%;height:100%;position:absolute;top:0;left:0}.region-path{cursor:pointer;transition:filter .2s ease;stroke-linejoin:round}.region-path:hover{filter:brightness(1.25)}.region-state{fill:rgba(46,125,50,0.5);stroke:#1b5e20;stroke-width:1.2}.region-oregon{fill:rgba(230,81,0,0.45);stroke:#bf360c;stroke-width:1.5}.region-minnesota{fill:rgba(255,152,0,0.45);stroke:#e65100;stroke-width:1.5}.region-utah{fill:rgba(255,111,0,0.4);stroke:#bf360c;stroke-width:1.5}.region-newmexico{fill:rgba(239,108,0,0.45);stroke:#bf360c;stroke-width:1.5}.region-unorganized{fill:rgba(141,110,99,0.4);stroke:#4e342e;stroke-width:1.5}.region-indian{fill:rgba(121,85,72,0.45);stroke:#3e2723;stroke-width:1.5}.light-mode .region-state{fill:rgba(46,125,50,0.5);stroke:#1b5e20}.light-mode .region-oregon{fill:rgba(230,81,0,0.45);stroke:#bf360c}.light-mode .region-minnesota{fill:rgba(255,152,0,0.45);stroke:#e65100}.light-mode .region-utah{fill:rgba(255,111,0,0.4);stroke:#bf360c}.light-mode .region-newmexico{fill:rgba(239,108,0,0.45);stroke:#bf360c}.light-mode .region-unorganized{fill:rgba(141,110,99,0.4);stroke:#4e342e}.light-mode .region-indian{fill:rgba(121,85,72,0.45);stroke:#3e2723}#map-tooltip{position:absolute;pointer-events:none;background-color:var(--bodyBackground);color:var(--bodyText);border:1px solid var(--variantBorder);border-radius:var(--roundedCorner4);padding:6px 12px;font-size:0.85rem;font-weight:500;box-shadow:var(--elevation8);opacity:0;transition:opacity .15s ease;z-index:10;white-space:nowrap}#hist-legend{position:absolute;bottom:16px;left:16px;background-color:var(--bodyBackground);border:1px solid var(--variantBorder);border-radius:8px;padding:10px 14px;font-size:0.75rem;color:var(--bodyText);z-index:5;box-shadow:0 2px 8px rgba(0,0,0,0.1)}#hist-legend .legend-item{display:flex;align-items:center;gap:8px;margin-bottom:4px}#hist-legend .legend-item:last-child{margin-bottom:0}#hist-legend .legend-swatch{width:14px;height:14px;border-radius:3px;flex-shrink:0}.trail-path{fill:none;stroke:#d32f2f;stroke-width:3;stroke-dasharray:8,4;stroke-linecap:round;stroke-linejoin:round;filter:drop-shadow(0 0 3px rgba(211,47,47,0.5))}.trail-path-glow{fill:none;stroke:rgba(211,47,47,0.25);stroke-width:8;stroke-linecap:round;stroke-linejoin:round}.trail-stop{cursor:pointer}.trail-stop circle.stop-outer{fill:rgba(211,47,47,0.2);stroke:#d32f2f;stroke-width:1.5;transition:r .2s ease, fill .2s ease}.trail-stop circle.stop-inner{fill:#d32f2f;transition:r .2s ease}.trail-stop:hover circle.stop-outer{fill:rgba(211,47,47,0.4)}.trail-stop.active circle.stop-outer{fill:rgba(211,47,47,0.5);stroke-width:2.5;r:9}.trail-stop.active circle.stop-inner{r:4}.trail-label{font-size:8px;font-weight:600;fill:#b71c1c;text-anchor:middle;pointer-events:none;paint-order:stroke;stroke:rgba(255,255,255,0.8);stroke-width:2.5px;stroke-linecap:round;stroke-linejoin:round}.light-mode .trail-path{stroke:#c62828}.light-mode .trail-label{fill:#b71c1c;stroke:rgba(255,255,255,0.85)}#trail-detail-card{position:absolute;top:16px;right:16px;width:340px;max-height:calc(100% - 40px);background-color:var(--bodyBackground);border:1px solid var(--variantBorder);border-radius:var(--roundedCorner4);box-shadow:var(--elevation64);z-index:20;opacity:0;transform:translateY(-10px) scale(0.97);pointer-events:none;transition:opacity .25s ease,transform .25s ease;overflow:hidden;display:flex;flex-direction:column}#trail-detail-card.visible{opacity:1;transform:translateY(0) scale(1);pointer-events:auto}#trail-detail-card .card-header{display:flex;align-items:center;justify-content:space-between;padding:12px 14px 8px;border-bottom:1px solid var(--variantBorder);background-color:var(--bodyStandoutBackground);flex-shrink:0}#trail-detail-card .card-header h3{margin:0;font-size:0.95rem;font-weight:700;color:var(--bodyText);line-height:1.3}#trail-detail-card .card-header .card-close{background:none;border:none;font-size:1.3rem;cursor:pointer;color:var(--bodySubtext);padding:0 2px;line-height:1;border-radius:4px;transition:background .15s ease}#trail-detail-card .card-header .card-close:hover{background-color:var(--buttonBackgroundHovered)}#trail-detail-card .card-subtitle{font-size:0.72rem;color:var(--bodySubtext);margin:2px 0 0;font-weight:500}#trail-detail-card .card-tabs{display:flex;border-bottom:1px solid var(--variantBorder);flex-shrink:0}#trail-detail-card .card-tab{flex:1;padding:8px 10px;font-size:0.78rem;font-weight:600;text-align:center;cursor:pointer;border:none;background:none;color:var(--bodySubtext);transition:all .15s ease;border-bottom:2px solid transparent;position:relative}#trail-detail-card .card-tab:hover{color:var(--bodyText);background-color:var(--buttonBackgroundHovered)}#trail-detail-card .card-tab.active{color:var(--themePrimary);border-bottom-color:var(--themePrimary);background-color:var(--buttonBackgroundHovered)}#trail-detail-card .card-tab-icon{margin-right:4px}#trail-detail-card .tab-content{flex:1;overflow:hidden;display:flex;flex-direction:column;min-height:0}#trail-detail-card .tab-pane{display:none;flex:1;overflow-y:auto;flex-direction:column;min-height:0}#trail-detail-card .tab-pane.active{display:flex}#trail-detail-card .facts-pane{padding:12px 14px;overflow-y:auto}#trail-detail-card .fact-row{display:flex;gap:8px;margin-bottom:8px;align-items:flex-start}#trail-detail-card .fact-icon{font-size:1rem;flex-shrink:0;width:22px;text-align:center;margin-top:1px}#trail-detail-card .fact-content{flex:1}#trail-detail-card .fact-label{font-size:0.65rem;text-transform:uppercase;letter-spacing:0.5px;color:var(--bodySubtext);font-weight:600;margin-bottom:1px}#trail-detail-card .fact-value{font-size:0.8rem;color:var(--bodyText);line-height:1.4}#trail-detail-card .card-divider{height:1px;background-color:var(--variantBorder);margin:4px 0 8px}#trail-detail-card .card-description{font-size:0.78rem;color:var(--bodyText);line-height:1.5;margin-top:2px}#trail-detail-card .mile-marker{display:inline-flex;align-items:center;gap:4px;background:rgba(211,47,47,0.1);color:var(--themePrimary);font-size:0.68rem;font-weight:600;padding:2px 7px;border-radius:var(--roundedCorner4);margin-bottom:8px}.light-mode #trail-detail-card .mile-marker{background:rgba(211,47,47,0.1);color:#b71c1c}.chat-pane{display:flex;flex-direction:column;min-height:0;flex:1}.chat-pane .chat-messages-area{flex:1;overflow-y:auto;padding:10px 12px;display:flex;flex-direction:column;gap:8px;min-height:120px}.chat-pane .chat-welcome{text-align:center;padding:16px 8px;color:var(--bodySubtext);font-size:0.78rem;line-height:1.5}.chat-pane .chat-welcome .welcome-emoji{font-size:1.8rem;margin-bottom:6px;display:block}.chat-pane .chat-welcome strong{color:var(--bodyText)}.chat-pane .chat-bubble{max-width:92%;padding:8px 11px;border-radius:var(--roundedCorner4);font-size:0.78rem;line-height:1.45;word-wrap:break-word;animation:bubbleIn .2s ease}.chat-pane .chat-bubble.user-bubble{align-self:flex-end;background:linear-gradient(135deg,#667eea,#764ba2);color:#fff;border-bottom-right-radius:4px}.chat-pane .chat-bubble.ai-bubble{align-self:flex-start;background-color:var(--bodyStandoutBackground);color:var(--bodyText);border:1px solid var(--variantBorder);border-bottom-left-radius:4px}.chat-pane .chat-bubble.ai-bubble.loading{opacity:0.7}.chat-pane .typing-dots{display:inline-flex;gap:3px;padding:2px 0}.chat-pane .typing-dots span{width:5px;height:5px;border-radius:50%;background-color:var(--bodySubtext);animation:dotBounce .6s ease-in-out infinite}.chat-pane .typing-dots span:nth-child(2){animation-delay:.15s}.chat-pane .typing-dots span:nth-child(3){animation-delay:.3s}@keyframes dotBounce{0%,100%{transform:translateY(0)}50%{transform:translateY(-4px)}}@keyframes bubbleIn{from{opacity:0;transform:translateY(6px)}to{opacity:1;transform:translateY(0)}}.chat-pane .chat-input-area{display:flex;gap:6px;padding:8px 10px;border-top:1px solid var(--variantBorder);background-color:var(--bodyBackground);flex-shrink:0;border-radius:0 0 12px 12px}.chat-pane .chat-input-field{flex:1;border:1px solid var(--variantBorder);border-radius:18px;padding:7px 12px;font-size:0.78rem;background-color:var(--inputBackground);color:var(--bodyText);outline:none;transition:border-color .15s ease;font-family:inherit}.chat-pane .chat-input-field:focus{border-color:var(--themePrimary)}.chat-pane .chat-input-field::placeholder{color:var(--bodySubtext);opacity:0.7}.chat-pane .chat-send-btn{width:32px;height:32px;border-radius:50%;border:none;background:linear-gradient(135deg,#d32f2f,#b71c1c);color:#fff;font-size:0.9rem;cursor:pointer;display:flex;align-items:center;justify-content:center;transition:transform .1s ease,opacity .15s ease;flex-shrink:0}.chat-pane .chat-send-btn:hover{transform:scale(1.08)}.chat-pane .chat-send-btn:disabled{opacity:0.4;cursor:not-allowed;transform:none}.chat-pane .suggested-questions{display:flex;flex-wrap:wrap;gap:4px;padding:0 12px 8px}.chat-pane .suggested-q{font-size:0.68rem;padding:4px 8px;border-radius:var(--roundedCorner4);border:1px solid var(--variantBorder);background-color:var(--inputBackground);color:var(--bodySubtext);cursor:pointer;transition:all .15s ease;white-space:nowrap}.chat-pane .suggested-q:hover{background:rgba(211,47,47,0.08);color:var(--themePrimary);border-color:rgba(211,47,47,0.3)}</style></head>
14
- <body>
15
- <div class="shell-toolbar" data-locked="true">
16
- <button class="shell-toolbar-btn" id="builderToggle" aria-label="Page Builder" data-locked="true"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none"><path d="M7 18.5H6.2c-1.77 0-3.2-1.43-3.2-3.2V7.7C3 5.93 4.43 4.5 6.2 4.5h11.6c1.77 0 3.2 1.43 3.2 3.2v7.6c0 1.77-1.43 3.2-3.2 3.2H12l-4.2 3.2c-.5.38-1.2.02-1.2-.6V18.5Z" stroke="currentColor" stroke-width="1.8" stroke-linejoin="round"/><circle cx="8.5" cy="11.5" r="1" fill="currentColor"/><circle cx="12" cy="11.5" r="1" fill="currentColor"/><circle cx="15.5" cy="11.5" r="1" fill="currentColor"/></svg></button>
17
- <button class="shell-toolbar-btn" id="pagesBtn" aria-label="View All Pages" data-locked="true"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" fill="none"><rect x="3" y="3" width="11" height="12" rx="1.5" stroke="currentColor" stroke-width="1.8" stroke-linejoin="round"/><path d="M6 7.5h5M6 10h3" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/><rect x="18" y="3" width="11" height="12" rx="1.5" stroke="currentColor" stroke-width="1.8" stroke-linejoin="round"/><path d="M21 7.5h5M21 10h3" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/><rect x="3" y="18" width="11" height="12" rx="1.5" stroke="currentColor" stroke-width="1.8" stroke-linejoin="round"/><path d="M6 22.5h5M6 25h3" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/><rect x="18" y="18" width="11" height="12" rx="1.5" stroke="currentColor" stroke-width="1.8" stroke-linejoin="round"/><path d="M21 22.5h5M21 25h3" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/></svg></button>
18
- <button class="shell-toolbar-btn" id="saveBtn" aria-label="Save Page" data-locked="true"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none"><path d="M19 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11l5 5v11a2 2 0 0 1-2 2Z" stroke="currentColor" stroke-width="1.8" stroke-linejoin="round"/><path d="M17 21v-8H7v8" stroke="currentColor" stroke-width="1.8" stroke-linejoin="round"/><path d="M7 3v5h8" stroke="currentColor" stroke-width="1.8" stroke-linejoin="round"/></svg></button>
19
- <div class="shell-toolbar-spacer" data-locked="true"></div>
20
- <button class="shell-toolbar-btn" id="settingsBtn" aria-label="Settings" data-locked="true"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none"><path d="M12 15a3 3 0 1 0 0-6 3 3 0 0 0 0 6Z" stroke="currentColor" stroke-width="1.8"/><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 1 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 1 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 1 1-2.83-2.83l.06-.06A1.65 1.65 0 0 0 4.68 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 1 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 1 1 2.83-2.83l.06.06A1.65 1.65 0 0 0 9 4.68a1.65 1.65 0 0 0 1-1.51V3a2 2 0 1 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 1 1 2.83 2.83l-.06.06A1.65 1.65 0 0 0 19.4 9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 1 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1Z" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"/></svg></button>
21
- </div>
22
- <div class="chat-panel" data-locked="true">
23
- <div class="chat-header" data-locked="true"><span>Page Builder</span><button class="chat-header-close" id="builderClose" aria-label="Close builder" data-locked="true">&times;</button></div>
24
- <div class="chat-messages" id="chatMessages" data-locked="true">
25
- <div class="chat-message" id="defaultGreeting"><p><strong>SynthOS:</strong> Welcome to the <strong>Oregon Trail</strong> — an interactive 1850s map with the Oregon Trail overlaid. Click any trail stop to see historical facts and chat with Dusty, your AI trail guide!</p></div>
26
- </div>
27
- <form action="/" method="POST" id="chatForm" data-locked="true">
28
- <textarea class="chat-input" id="chatInput" name="message" rows="2" placeholder="Type a message..." data-locked="true"></textarea>
29
- </form>
30
- </div>
31
- <div class="viewer-panel" id="viewerPanel"><div id="us-map-container"><div id="us-map-header">United States — Circa 1850 — with Oregon Trail</div><div id="us-map-svg-wrap"><div id="hist-legend"><div style="font-weight:600;margin-bottom:6px;">Circa 1850</div><div class="legend-item"><div class="legend-swatch" style="background:rgba(46,125,50,0.6);border:1px solid #1b5e20;"></div><span>States (31)</span></div><div class="legend-item"><div class="legend-swatch" style="background:rgba(230,81,0,0.55);border:1px solid #bf360c;"></div><span>Oregon Territory</span></div><div class="legend-item"><div class="legend-swatch" style="background:rgba(255,152,0,0.55);border:1px solid #e65100;"></div><span>Minnesota Territory</span></div><div class="legend-item"><div class="legend-swatch" style="background:rgba(255,111,0,0.5);border:1px solid #bf360c;"></div><span>Utah Territory</span></div><div class="legend-item"><div class="legend-swatch" style="background:rgba(239,108,0,0.55);border:1px solid #bf360c;"></div><span>New Mexico Territory</span></div><div class="legend-item"><div class="legend-swatch" style="background:rgba(141,110,99,0.5);border:1px solid #4e342e;"></div><span>Unorganized Territory</span></div><div class="legend-item"><div class="legend-swatch" style="background:rgba(121,85,72,0.55);border:1px solid #3e2723;"></div><span>Indian Territory</span></div><div style="border-top:1px solid var(--border-color);margin-top:6px;padding-top:6px;"></div><div class="legend-item"><div class="legend-swatch" style="background:#d32f2f;border:1px solid #b71c1c;border-radius:50%;"></div><span style="font-weight:600;">Oregon Trail</span></div></div><div id="trail-detail-card"><div class="card-header"><div><h3 id="card-title"></h3><div class="card-subtitle" id="card-subtitle"></div></div><button class="card-close" id="card-close-btn" aria-label="Close">×</button></div><div class="card-tabs"><button class="card-tab active" data-tab="facts"><span class="card-tab-icon">📋</span>Facts</button><button class="card-tab" data-tab="chat"><span class="card-tab-icon">💬</span>Ask Me!</button></div><div class="tab-content"><div class="tab-pane facts-pane active" id="facts-pane"><div id="card-body"></div></div><div class="tab-pane chat-pane" id="chat-pane"><div class="chat-messages-area" id="trail-chat-messages"><div class="chat-welcome"><span class="welcome-emoji">🤠</span>Howdy, explorer! <strong>Click a trail stop</strong> and ask me anything about life on the Oregon Trail!</div></div><div class="suggested-questions" id="trail-suggested-q"></div><div class="chat-input-area"><input type="text" class="chat-input-field" id="trail-chat-input" placeholder="Ask me anything..." autocomplete="off"><button class="chat-send-btn" id="trail-chat-send" aria-label="Send">➤</button></div></div></div></div></div><div id="map-tooltip"></div></div><div id="loadingOverlay" class="loading-overlay"><div class="spinner"></div></div></div>
32
- <div id="instructions" style="display: none;" data-locked="true"></div>
33
- <div id="thoughts" style="display: none;" data-locked="true"></div>
34
- <script id="idle-animation">function hideIdleAnimation(){const idleContainer=document.getElementById("idleAnimation");idleContainer&&(idleContainer.classList.add("hidden"),setTimeout(()=>{idleContainer.style.display="none"},1e3))}function showIdleAnimation(){const idleContainer=document.getElementById("idleAnimation");idleContainer&&(idleContainer.style.display="block",setTimeout(()=>{idleContainer.classList.remove("hidden")},10))}</script>
35
- <button class="chat-toggle" aria-label="Toggle chat panel">
36
- <span class="chat-toggle-dots">
37
- <span class="chat-toggle-dot"></span>
38
- <span class="chat-toggle-dot"></span>
39
- <span class="chat-toggle-dot"></span>
40
- </span>
41
- </button>
42
-
43
-
44
- <script id="us-map-script">(function(){var FIPS_TO_NAME={1:'Alabama',2:'Alaska',4:'Arizona',5:'Arkansas',6:'California',8:'Colorado',9:'Connecticut',10:'Delaware',11:'District of Columbia',12:'Florida',13:'Georgia',15:'Hawaii',16:'Idaho',17:'Illinois',18:'Indiana',19:'Iowa',20:'Kansas',21:'Kentucky',22:'Louisiana',23:'Maine',24:'Maryland',25:'Massachusetts',26:'Michigan',27:'Minnesota',28:'Mississippi',29:'Missouri',30:'Montana',31:'Nebraska',32:'Nevada',33:'New Hampshire',34:'New Jersey',35:'New Mexico',36:'New York',37:'North Carolina',38:'North Dakota',39:'Ohio',40:'Oklahoma',41:'Oregon',42:'Pennsylvania',44:'Rhode Island',45:'South Carolina',46:'South Dakota',47:'Tennessee',48:'Texas',49:'Utah',50:'Vermont',51:'Virginia',53:'Washington',54:'West Virginia',55:'Wisconsin',56:'Wyoming'};
45
- var STATES_1850=[9,10,13,24,25,33,34,36,37,42,44,45,51,50,21,47,39,22,18,28,17,1,23,29,5,26,12,48,19,55,6,54,11];
46
- var OREGON_TERR=[41,53,16];
47
- var MINNESOTA_TERR=[27];
48
- var UTAH_TERR=[49,32];
49
- var NEWMEXICO_TERR=[35,4];
50
- var UNORGANIZED=[31,20,38,46,56,30,8];
51
- var INDIAN_TERR=[40];
52
- var TERRITORY_GROUPS=[{fips:OREGON_TERR,name:'Oregon Territory',cls:'region-oregon'},{fips:MINNESOTA_TERR,name:'Minnesota Territory',cls:'region-minnesota'},{fips:UTAH_TERR,name:'Utah Territory',cls:'region-utah'},{fips:NEWMEXICO_TERR,name:'New Mexico Territory',cls:'region-newmexico'},{fips:UNORGANIZED,name:'Unorganized Territory',cls:'region-unorganized'},{fips:INDIAN_TERR,name:'Indian Territory',cls:'region-indian'}];
53
- var TRAIL_POINTS=[[-94.41,39.09],[-95.20,39.05],[-95.68,39.05],[-96.50,39.20],[-97.30,39.50],[-98.00,40.00],[-99.00,40.65],[-99.80,40.85],[-100.77,41.12],[-101.80,41.40],[-103.00,41.70],[-103.70,41.83],[-104.52,42.21],[-105.50,42.50],[-106.31,42.87],[-107.13,42.49],[-108.00,42.40],[-108.80,42.33],[-109.50,42.00],[-110.39,41.32],[-110.90,42.00],[-111.60,42.66],[-112.43,42.92],[-113.50,42.70],[-114.46,42.56],[-115.30,43.00],[-116.20,43.62],[-117.00,44.05],[-117.80,44.80],[-118.50,45.30],[-119.50,45.55],[-120.50,45.60],[-121.18,45.60],[-121.80,45.50],[-122.61,45.36]];
54
- var TRAIL_STOPS=[{coords:[-94.41,39.09],name:'Independence, MO',labelPos:'below'},{coords:[-99.00,40.65],name:'Fort Kearny',labelPos:'above'},{coords:[-103.00,41.70],name:'Chimney Rock',labelPos:'above'},{coords:[-104.52,42.21],name:'Fort Laramie',labelPos:'above'},{coords:[-107.13,42.49],name:'Independence Rock',labelPos:'above'},{coords:[-108.80,42.33],name:'South Pass',labelPos:'below'},{coords:[-110.39,41.32],name:'Fort Bridger',labelPos:'below'},{coords:[-112.43,42.92],name:'Fort Hall',labelPos:'above'},{coords:[-116.20,43.62],name:'Fort Boise',labelPos:'above'},{coords:[-121.18,45.60],name:'The Dalles',labelPos:'above'},{coords:[-122.61,45.36],name:'Oregon City',labelPos:'below'}];
55
- var STOP_FACTS={'Independence, MO':{subtitle:'The Gateway to the West',established:'Settled 1827',elevation:'830 ft (253 m)',miles:'Mile 0 — Starting Point',territory:'State of Missouri',facts:[{icon:'🏪',label:'Role',value:'Primary "jumping-off" point for westward emigrants'},{icon:'📅',label:'Peak Years',value:'1843–1860, with up to 50,000 emigrants departing annually in peak years'},{icon:'🛒',label:'Supplies',value:'Emigrants stocked up on flour, bacon, coffee, sugar, and oxen — a typical family spent $600–$1,000 outfitting'},{icon:'👥',label:'Population',value:'About 3,000 residents by 1850, swelling each spring with emigrants'}],description:'Independence was the most popular starting point for the Oregon, California, and Santa Fe trails. Wagon trains typically departed between April and May to cross the mountains before winter snows.'},'Fort Kearny':{subtitle:'First Military Outpost on the Trail',established:'Est. 1848',elevation:'2,146 ft (654 m)',miles:'Mile 319 — ~2 weeks from Independence',territory:'Unorganized Territory (present-day Nebraska)',facts:[{icon:'⚔️',label:'Purpose',value:'U.S. Army post built to protect emigrants on the Oregon Trail'},{icon:'📬',label:'Services',value:'Post office, blacksmith shop, and supply store for travelers'},{icon:'🦬',label:'Wildlife',value:'Located in prime buffalo country — emigrants often saw herds of thousands'},{icon:'🪖',label:'Garrison',value:'Typically 200–400 soldiers stationed here'}],description:'Fort Kearny served as the first major rest stop and resupply point. It was also where the trail from St. Joseph merged with the Independence route. Soldiers here helped repair wagons and provided medical aid.'},'Chimney Rock':{subtitle:'The Most Famous Landmark on the Trail',established:'Named by fur traders, 1820s',elevation:'4,228 ft (1,289 m)',miles:'Mile 561 — ~5 weeks from Independence',territory:'Unorganized Territory (present-day Nebraska)',facts:[{icon:'🏔️',label:'Height',value:'Spire rises ~325 ft above the North Platte River valley'},{icon:'📖',label:'Mentions',value:'Referenced in more emigrant journals than any other landmark on the trail'},{icon:'⚡',label:'Erosion',value:'Was significantly taller in the 1840s — lightning and erosion have reduced it'},{icon:'🎯',label:'Significance',value:'Marked the transition from prairies to the rugged terrain of the West'}],description:'Chimney Rock was visible for days before emigrants reached it, serving as a beacon of progress. Many carved their names into its soft clay base. It told travelers they were about one-third of the way to Oregon.'},'Fort Laramie':{subtitle:'Crossroads of the Frontier',established:'Est. 1834 (fur trade); U.S. Army 1849',elevation:'4,519 ft (1,377 m)',miles:'Mile 650 — ~6 weeks from Independence',territory:'Unorganized Territory (present-day Wyoming)',facts:[{icon:'🏛️',label:'Origin',value:'Started as a fur trading post called Fort William, later Fort John'},{icon:'🤝',label:'Treaty',value:'Site of the 1851 Fort Laramie Treaty with Plains Indian nations'},{icon:'🔧',label:'Services',value:'Blacksmith, trading post, and the last reliable resupply before the Rockies'},{icon:'⚖️',label:'Lightening Loads',value:'Emigrants often abandoned heavy items here — the fort was surrounded by discarded goods'}],description:'Fort Laramie was the most important stop on the entire trail. Here emigrants rested, repaired wagons, traded worn-out oxen, and prepared for the difficult mountain crossings ahead. Many wrote letters home from here.'},'Independence Rock':{subtitle:'The Register of the Desert',established:'Named by fur trappers, ~1824',elevation:'5,900 ft (1,798 m)',miles:'Mile 838 — ~8 weeks from Independence',territory:'Unorganized Territory (present-day Wyoming)',facts:[{icon:'🪨',label:'Size',value:'Granite dome ~1,900 ft long, 700 ft wide, and 128 ft high'},{icon:'✍️',label:'Names',value:'Over 5,000 names were carved or painted on the rock by passing emigrants'},{icon:'📅',label:'Timing',value:'Emigrants aimed to reach it by July 4th (Independence Day) to stay on schedule'},{icon:'🎆',label:'Celebrations',value:'Many wagon trains held Fourth of July celebrations here'}],description:'This massive granite outcrop became the most famous "guest book" in American history. Reaching it by Independence Day meant you were on pace to cross the mountains before winter. Father De Smet called it the "Register of the Desert" in 1840.'},'South Pass':{subtitle:'Crossing the Continental Divide',established:'Discovered by Europeans ~1812; widely used from 1840s',elevation:'7,550 ft (2,301 m)',miles:'Mile 914 — ~9 weeks from Independence',territory:'Unorganized Territory (present-day Wyoming)',facts:[{icon:'⛰️',label:'Geography',value:'A broad, gentle 20-mile-wide saddle through the Rocky Mountains'},{icon:'💧',label:'Divide',value:'Waters east flow to the Atlantic; waters west flow to the Pacific'},{icon:'😮',label:'Surprise',value:'So gradual that many emigrants didn\'t realize they\'d crossed the Rockies'},{icon:'🗺️',label:'Importance',value:'The only feasible wagon route through the Rockies for 2,000 miles'}],description:'South Pass was the key to the entire Oregon Trail. Without this gentle crossing of the Continental Divide, wagon travel to Oregon would have been nearly impossible. Despite being at 7,550 feet, the pass was so wide and gradual that it felt like rolling prairie.'},'Fort Bridger':{subtitle:'Mountain Man\'s Trading Post',established:'Est. 1843 by Jim Bridger',elevation:'6,665 ft (2,031 m)',miles:'Mile 1,026 — ~10 weeks from Independence',territory:'Unorganized Territory (present-day Wyoming)',facts:[{icon:'🧔',label:'Founder',value:'Jim Bridger — legendary mountain man, fur trapper, and scout'},{icon:'🔀',label:'Junction',value:'Where the Oregon Trail and the Mormon Trail to Salt Lake diverged'},{icon:'🐎',label:'Trade',value:'Emigrants traded tired animals for fresh ones with local Shoshone people'},{icon:'🏚️',label:'Condition',value:'Many emigrants were disappointed — described it as a shabby collection of log huts'}],description:'Jim Bridger and Louis Vasquez established this trading post on Black\'s Fork of the Green River. Despite its rough appearance, it was a critical decision point where emigrants chose between routes. The Donner Party fatefully took the Hastings Cutoff from near here in 1846.'},'Fort Hall':{subtitle:'Hudson\'s Bay Company Outpost',established:'Est. 1834 by Nathaniel Wyeth',elevation:'4,400 ft (1,341 m)',miles:'Mile 1,217 — ~12 weeks from Independence',territory:'Oregon Territory (present-day Idaho)',facts:[{icon:'🇬🇧',label:'Ownership',value:'Sold to Britain\'s Hudson\'s Bay Company in 1837'},{icon:'⚠️',label:'Discouragement',value:'HBC agents often tried to discourage American emigrants from continuing to Oregon'},{icon:'🔀',label:'Split',value:'Where the California Trail branched south from the Oregon Trail'},{icon:'🛞',label:'Wagons',value:'Early emigrants were told to abandon wagons here — the first to take wagons through to Oregon did so in 1843'}],description:'Fort Hall sat at a strategic crossroads. The Hudson\'s Bay Company, wanting to keep Americans out of Oregon, often told emigrants the route ahead was impassable by wagon. Despite this, thousands pushed through. After 1843, it became the split point for California-bound gold seekers.'},'Fort Boise':{subtitle:'Snake River Oasis',established:'Est. 1834 by Hudson\'s Bay Company',elevation:'2,730 ft (832 m)',miles:'Mile 1,450 — ~14 weeks from Independence',territory:'Oregon Territory (present-day Idaho)',facts:[{icon:'🏊',label:'River Crossing',value:'Emigrants had to ford the dangerous Boise River and Snake River nearby'},{icon:'🌿',label:'Relief',value:'Green river valley was a welcome sight after the harsh Snake River Plain'},{icon:'💀',label:'Danger',value:'The Snake River crossings near here claimed many lives — swift current and hidden holes'},{icon:'🐟',label:'Food',value:'Emigrants could trade with local Shoshone for salmon and camas root'}],description:'After weeks of dusty, barren travel along the Snake River Plain, Fort Boise offered shade, fresh water, and a chance to trade. The river crossings in this area were among the most dangerous on the entire trail, with many drownings recorded in emigrant diaries.'},'The Dalles':{subtitle:'End of the Overland Road',established:'Methodist mission est. 1838',elevation:'200 ft (61 m)',miles:'Mile 1,880 — ~18 weeks from Independence',territory:'Oregon Territory (present-day Oregon)',facts:[{icon:'🌊',label:'Challenge',value:'The Columbia River Gorge ahead was too narrow and dangerous for wagons'},{icon:'🛶',label:'River Route',value:'Many emigrants built rafts or hired Native canoes to float to Oregon City'},{icon:'🛤️',label:'Barlow Road',value:'After 1846, Sam Barlow\'s toll road over Mt. Hood offered an alternative ($5 per wagon)'},{icon:'⛪',label:'Mission',value:'A Methodist mission here served as a waystation for weary travelers'}],description:'The Dalles marked the end of the overland trail and the beginning of the final, often harrowing leg of the journey. The Columbia River route through the gorge was treacherous — many lost everything on the rapids. The Barlow Road, though steep and muddy, became the preferred route after 1846.'},'Oregon City':{subtitle:'Journey\'s End — The Promised Land',established:'Founded 1829; incorporated 1844',elevation:'55 ft (17 m)',miles:'Mile 2,000 — ~5 months from Independence',territory:'Oregon Territory',facts:[{icon:'🏛️',label:'Capital',value:'First incorporated city west of the Rockies; capital of Oregon Territory 1848–1852'},{icon:'🌊',label:'Falls',value:'Built at Willamette Falls — the falls powered sawmills and flour mills'},{icon:'📜',label:'Land Claims',value:'Under the Donation Land Claim Act (1850), settlers could claim 320–640 acres free'},{icon:'👨‍👩‍👧‍👦',label:'Arrivals',value:'~53,000 emigrants reached Oregon by wagon between 1840–1860'}],description:'Oregon City was the official end of the Oregon Trail and the heart of the new American settlement in the Pacific Northwest. Exhausted emigrants arrived here after five months and 2,000 miles to claim their new lives. The fertile Willamette Valley beyond offered the rich farmland that had drawn them west.'}};
56
-
57
- var SUGGESTED_QUESTIONS={'Independence, MO':['What did kids do on the trail?','What food did they bring?','How long was the trip?'],'Fort Kearny':['Did kids go to school?','Were there really buffalo?','What was a typical day like?'],'Chimney Rock':['How tall is it really?','Did people climb it?','What did it look like back then?'],'Fort Laramie':['What did they trade?','Who lived at the fort?','Was it dangerous here?'],'Independence Rock':['Why did people carve names?','Can you still see the names?','What games did kids play?'],'South Pass':['How cold was it up there?','Did wagons ever get stuck?','What animals lived here?'],'Fort Bridger':['Who was Jim Bridger?','What happened to the Donner Party?','What did they eat here?'],'Fort Hall':['Why did the British want Oregon?','How did they cross rivers?','Were there any kids on the trail?'],'Fort Boise':['How dangerous were river crossings?','What fish could they catch?','Did anyone get lost?'],'The Dalles':['What were the rapids like?','How much was the toll road?','Did anyone\'s raft sink?'],'Oregon City':['What happened when they arrived?','How big was the free land?','What did they build first?']};
58
-
59
- var activeStop=null;
60
- var chatHistory=[];
61
- var currentStopName=null;
62
-
63
- function showDetailCard(stopName){
64
- var card=document.getElementById('trail-detail-card');
65
- var facts=STOP_FACTS[stopName];
66
- if(!facts||!card)return;
67
- document.getElementById('card-title').textContent=stopName;
68
- document.getElementById('card-subtitle').textContent=facts.subtitle;
69
- var body=document.getElementById('card-body');
70
- var html='';
71
- html+='<span class="mile-marker">📍 '+facts.miles+'</span>';
72
- html+='<div class="fact-row"><div class="fact-icon">🗓️</div><div class="fact-content"><div class="fact-label">Established</div><div class="fact-value">'+facts.established+'</div></div></div>';
73
- html+='<div class="fact-row"><div class="fact-icon">📏</div><div class="fact-content"><div class="fact-label">Elevation</div><div class="fact-value">'+facts.elevation+'</div></div></div>';
74
- html+='<div class="fact-row"><div class="fact-icon">🗺️</div><div class="fact-content"><div class="fact-label">Territory</div><div class="fact-value">'+facts.territory+'</div></div></div>';
75
- html+='<div class="card-divider"></div>';
76
- facts.facts.forEach(function(f){html+='<div class="fact-row"><div class="fact-icon">'+f.icon+'</div><div class="fact-content"><div class="fact-label">'+f.label+'</div><div class="fact-value">'+f.value+'</div></div></div>';});
77
- html+='<div class="card-divider"></div>';
78
- html+='<div class="card-description">'+facts.description+'</div>';
79
- body.innerHTML=html;
80
-
81
- if(currentStopName!==stopName){
82
- currentStopName=stopName;
83
- chatHistory=[];
84
- var msgArea=document.getElementById('trail-chat-messages');
85
- msgArea.innerHTML='<div class="chat-welcome"><span class="welcome-emoji">🤠</span>Howdy, explorer! You\'re at <strong>'+stopName+'</strong>! Ask me anything about this place or the Oregon Trail journey!</div>';
86
- }
87
- updateSuggestedQuestions(stopName);
88
-
89
- activeStop=stopName;
90
- card.classList.add('visible');
91
- setActiveTab('facts');
92
- d3.selectAll('.trail-stop').classed('active',false);
93
- d3.selectAll('.trail-stop').each(function(){var el=d3.select(this);if(el.attr('data-stop-name')===stopName)el.classed('active',true);});
94
- }
95
-
96
- function hideDetailCard(){
97
- var card=document.getElementById('trail-detail-card');
98
- if(card)card.classList.remove('visible');
99
- activeStop=null;
100
- d3.selectAll('.trail-stop').classed('active',false);
101
- }
102
-
103
- function setActiveTab(tabName){
104
- document.querySelectorAll('#trail-detail-card .card-tab').forEach(function(t){
105
- t.classList.toggle('active',t.getAttribute('data-tab')===tabName);
106
- });
107
- document.querySelectorAll('#trail-detail-card .tab-pane').forEach(function(p){
108
- var id=p.id;
109
- if(tabName==='facts')p.classList.toggle('active',id==='facts-pane');
110
- else p.classList.toggle('active',id==='chat-pane');
111
- });
112
- if(tabName==='chat'){
113
- var input=document.getElementById('trail-chat-input');
114
- if(input)setTimeout(function(){input.focus();},100);
115
- scrollChatToBottom();
116
- }
117
- }
118
-
119
- function updateSuggestedQuestions(stopName){
120
- var container=document.getElementById('trail-suggested-q');
121
- var questions=SUGGESTED_QUESTIONS[stopName]||['What was it like here?','Tell me something cool!','Was it dangerous?'];
122
- container.innerHTML='';
123
- questions.forEach(function(q){
124
- var btn=document.createElement('button');
125
- btn.className='suggested-q';
126
- btn.textContent=q;
127
- btn.addEventListener('click',function(){sendTrailChat(q);});
128
- container.appendChild(btn);
129
- });
130
- }
131
-
132
- function scrollChatToBottom(){
133
- var area=document.getElementById('trail-chat-messages');
134
- if(area)area.scrollTop=area.scrollHeight;
135
- }
136
-
137
- function addChatBubble(text,isUser){
138
- var area=document.getElementById('trail-chat-messages');
139
- var welcome=area.querySelector('.chat-welcome');
140
- if(welcome)welcome.remove();
141
- var bubble=document.createElement('div');
142
- bubble.className='chat-bubble '+(isUser?'user-bubble':'ai-bubble');
143
- bubble.textContent=text;
144
- area.appendChild(bubble);
145
- scrollChatToBottom();
146
- return bubble;
147
- }
148
-
149
- function addLoadingBubble(){
150
- var area=document.getElementById('trail-chat-messages');
151
- var bubble=document.createElement('div');
152
- bubble.className='chat-bubble ai-bubble loading';
153
- bubble.id='trail-loading-bubble';
154
- bubble.innerHTML='<div class="typing-dots"><span></span><span></span><span></span></div>';
155
- area.appendChild(bubble);
156
- scrollChatToBottom();
157
- return bubble;
158
- }
159
-
160
- function removeLoadingBubble(){
161
- var el=document.getElementById('trail-loading-bubble');
162
- if(el)el.remove();
163
- }
164
-
165
- function buildPrompt(userQuestion){
166
- var stopData=STOP_FACTS[currentStopName];
167
- var contextParts=[];
168
- contextParts.push('You are a friendly, enthusiastic trail guide named "Dusty" who teaches kids (ages 8-12) about the Oregon Trail and the 1850s American frontier.');
169
- contextParts.push('You are currently at: '+currentStopName+' '+stopData.subtitle);
170
- contextParts.push('Key facts about this location:');
171
- contextParts.push('- Established: '+stopData.established);
172
- contextParts.push('- Elevation: '+stopData.elevation);
173
- contextParts.push('- Position on trail: '+stopData.miles);
174
- contextParts.push('- Territory: '+stopData.territory);
175
- contextParts.push('- Description: '+stopData.description);
176
- stopData.facts.forEach(function(f){contextParts.push('- '+f.label+': '+f.value);});
177
- contextParts.push('');
178
- contextParts.push('RULES:');
179
- contextParts.push('- Use simple, fun language appropriate for 8-12 year olds');
180
- contextParts.push('- Keep answers to 2-4 short paragraphs max');
181
- contextParts.push('- Use vivid descriptions and comparisons kids can relate to');
182
- contextParts.push('- Include fun facts when possible');
183
- contextParts.push('- If you don\'t know something, say so honestly');
184
- contextParts.push('- You can reference other stops on the Oregon Trail for context');
185
- contextParts.push('- Use occasional emojis but don\'t overdo it');
186
- contextParts.push('- Never break character — you are Dusty the trail guide');
187
- contextParts.push('');
188
-
189
- if(chatHistory.length>0){
190
- contextParts.push('Recent conversation:');
191
- var recentHistory=chatHistory.slice(-10);
192
- recentHistory.forEach(function(turn){
193
- contextParts.push((turn.role==='user'?'Kid: ':'Dusty: ')+turn.text);
194
- });
195
- contextParts.push('');
196
- }
197
-
198
- contextParts.push('Kid\'s question: '+userQuestion);
199
- contextParts.push('');
200
- contextParts.push('Respond as Dusty the trail guide:');
201
- return contextParts.join('\n');
202
- }
203
-
204
- async function sendTrailChat(message){
205
- if(!message||!message.trim()||!currentStopName)return;
206
- var input=document.getElementById('trail-chat-input');
207
- var sendBtn=document.getElementById('trail-chat-send');
208
- message=message.trim();
209
- input.value='';
210
- sendBtn.disabled=true;
211
-
212
- setActiveTab('chat');
213
- addChatBubble(message,true);
214
- chatHistory.push({role:'user',text:message});
215
-
216
- addLoadingBubble();
217
-
218
- try{
219
- var prompt=buildPrompt(message);
220
- var result=await synthos.generate.completion({prompt:prompt,temperature:0.7});
221
- removeLoadingBubble();
222
- var answer=result.answer||'Hmm, my trail map got a little dusty! Try asking me again. 🤠';
223
- addChatBubble(answer,false);
224
- chatHistory.push({role:'assistant',text:answer});
225
- if(chatHistory.length>20)chatHistory=chatHistory.slice(-20);
226
- }catch(err){
227
- removeLoadingBubble();
228
- addChatBubble('Whoops! My wagon wheel got stuck. 🛞 Try asking again in a moment!',false);
229
- console.error('Trail chat error:',err);
230
- }
231
- sendBtn.disabled=false;
232
- input.focus();
233
- }
234
-
235
- window._showTrailDetail=showDetailCard;
236
- window._hideTrailDetail=hideDetailCard;
237
-
238
- document.getElementById('card-close-btn').addEventListener('click',function(e){e.stopPropagation();hideDetailCard();});
239
-
240
- document.querySelectorAll('#trail-detail-card .card-tab').forEach(function(tab){
241
- tab.addEventListener('click',function(e){
242
- e.stopPropagation();
243
- setActiveTab(tab.getAttribute('data-tab'));
244
- });
245
- });
246
-
247
- document.getElementById('trail-chat-send').addEventListener('click',function(e){
248
- e.stopPropagation();
249
- var input=document.getElementById('trail-chat-input');
250
- sendTrailChat(input.value);
251
- });
252
-
253
- document.getElementById('trail-chat-input').addEventListener('keydown',function(e){
254
- if(e.key==='Enter'&&!e.shiftKey){e.preventDefault();e.stopPropagation();sendTrailChat(this.value);}
255
- });
256
-
257
- document.getElementById('trail-chat-input').addEventListener('click',function(e){e.stopPropagation();});
258
-
259
- function init(){
260
- var wrap=document.getElementById('us-map-svg-wrap');
261
- if(!wrap)return;
262
- var tooltip=document.getElementById('map-tooltip');
263
- wrap.querySelectorAll('svg').forEach(function(s){s.remove();});
264
- var svg=d3.select(wrap).append('svg').attr('id','hist-map-svg').attr('viewBox','0 0 960 600').attr('preserveAspectRatio','xMidYMid meet').style('padding','16px').style('box-sizing','border-box');
265
- var projection=d3.geoAlbersUsa().scale(1280).translate([480,300]);
266
- var path=d3.geoPath().projection(projection);
267
- d3.json('https://cdn.jsdelivr.net/npm/us-atlas@3/states-10m.json').then(function(us){
268
- var statesGeo=us.objects.states;
269
- var allGeometries=statesGeo.geometries;
270
- function geomsForFips(fipsList){return allGeometries.filter(function(g){return fipsList.indexOf(+g.id)!==-1;});}
271
- var stateGeoms=geomsForFips(STATES_1850);
272
- stateGeoms.forEach(function(geom){
273
- var feature=topojson.feature(us,geom);
274
- svg.append('path').datum(feature).attr('class','region-path region-state').attr('d',path)
275
- .on('mousemove',function(event){var name=FIPS_TO_NAME[+geom.id]||'Unknown';tooltip.textContent=name+' (State)';tooltip.style.opacity='1';var rect=wrap.getBoundingClientRect();var x=event.clientX-rect.left+12;var y=event.clientY-rect.top-30;if(x+180>rect.width)x=x-200;if(y<0)y=event.clientY-rect.top+18;tooltip.style.left=x+'px';tooltip.style.top=y+'px';}).on('mouseleave',function(){tooltip.style.opacity='0';}).on('click',function(){hideDetailCard();});
276
- });
277
- TERRITORY_GROUPS.forEach(function(group){
278
- var geoms=geomsForFips(group.fips);
279
- if(geoms.length===0)return;
280
- var merged=topojson.merge(us,geoms);
281
- svg.append('path').datum(merged).attr('class','region-path '+group.cls).attr('d',path)
282
- .on('mousemove',function(event){tooltip.textContent=group.name;tooltip.style.opacity='1';var rect=wrap.getBoundingClientRect();var x=event.clientX-rect.left+12;var y=event.clientY-rect.top-30;if(x+180>rect.width)x=x-200;if(y<0)y=event.clientY-rect.top+18;tooltip.style.left=x+'px';tooltip.style.top=y+'px';}).on('mouseleave',function(){tooltip.style.opacity='0';}).on('click',function(){hideDetailCard();});
283
- });
284
- var stateGeoms2=geomsForFips(STATES_1850);
285
- svg.append('path').datum(topojson.mesh(us,{type:'GeometryCollection',geometries:stateGeoms2},function(a,b){return a!==b&&STATES_1850.indexOf(+a.id)!==-1&&STATES_1850.indexOf(+b.id)!==-1;})).attr('fill','none').attr('stroke','rgba(27,94,32,0.4)').attr('stroke-width','0.5').attr('d',path).style('pointer-events','none');
286
- var trailGroup=svg.append('g').attr('class','trail-group');
287
- var projectedPoints=[];
288
- TRAIL_POINTS.forEach(function(p){var pt=projection(p);if(pt)projectedPoints.push(pt);});
289
- if(projectedPoints.length>1){
290
- var lineGen=d3.line().x(function(d){return d[0];}).y(function(d){return d[1];}).curve(d3.curveCatmullRom.alpha(0.5));
291
- trailGroup.append('path').attr('class','trail-path-glow').attr('d',lineGen(projectedPoints));
292
- trailGroup.append('path').attr('class','trail-path').attr('d',lineGen(projectedPoints));
293
- }
294
- TRAIL_STOPS.forEach(function(stop){
295
- var pt=projection(stop.coords);
296
- if(!pt)return;
297
- var g=trailGroup.append('g').attr('class','trail-stop').attr('transform','translate('+pt[0]+','+pt[1]+')').attr('data-stop-name',stop.name);
298
- g.append('circle').attr('class','stop-outer').attr('r',6);
299
- g.append('circle').attr('class','stop-inner').attr('r',2.5);
300
- var labelY=stop.labelPos==='above'?-10:14;
301
- g.append('text').attr('class','trail-label').attr('y',labelY).text(stop.name);
302
- g.on('mousemove',function(event){tooltip.textContent='Oregon Trail: '+stop.name;tooltip.style.opacity='1';var rect=wrap.getBoundingClientRect();var x=event.clientX-rect.left+12;var y=event.clientY-rect.top-30;if(x+180>rect.width)x=x-200;if(y<0)y=event.clientY-rect.top+18;tooltip.style.left=x+'px';tooltip.style.top=y+'px';}).on('mouseleave',function(){tooltip.style.opacity='0';}).on('click',function(event){event.stopPropagation();showDetailCard(stop.name);});
303
- });
304
- }).catch(function(err){console.error('Failed to load US map data:',err);wrap.innerHTML='<p style="color:var(--text-secondary);text-align:center;">Failed to load map data. Please try again.</p>';});
305
- }
306
- if(typeof topojson!=='undefined'){init();}else{var check=setInterval(function(){if(typeof topojson!=='undefined'){clearInterval(check);init();}},100);setTimeout(function(){clearInterval(check);},10000);}
307
- })();</script><script id="page-helpers" src="/api/page-helpers.js?v=3" data-locked="true"></script><script id="page-script" src="/api/page-script.js?v=3" data-locked="true"></script></body></html>
1
+ <!DOCTYPE html><html lang="en"><head>
2
+ <meta charset="UTF-8">
3
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
4
+ <title>SynthOS</title>
5
+ <script id="theme-info" src="/api/theme-info.js" data-locked="true"></script>
6
+ <link id="theme-css" rel="stylesheet" href="/api/theme.css" data-locked="true">
7
+ <style>.idle-container{position:absolute;width:100%;height:100%;pointer-events:none;opacity:1;transition:opacity 1s ease-out}.idle-container.hidden{opacity:0}.breathing-orb{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);width:80px;height:80px;border-radius:50%;background:radial-gradient(circle,rgba(102,126,234,.15) 0,transparent 70%);animation:4s ease-in-out infinite breathe}.breathing-orb::before{content:'';position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);width:8px;height:8px;border-radius:50%;background:rgba(183,148,246,.6);box-shadow:0 0 20px rgba(183,148,246,.4);animation:4s ease-in-out infinite core-pulse}@keyframes breathe{0%,100%{width:80px;height:80px;opacity:.3}50%{width:120px;height:120px;opacity:.6}}@keyframes core-pulse{0%,100%{opacity:.4;box-shadow:0 0 20px rgba(183,148,246,.3)}50%{opacity:.8;box-shadow:0 0 30px rgba(183,148,246,.5)}}.orbit-ring{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);width:200px;height:200px;border:1px solid rgba(102,126,234,.1);border-radius:50%;animation:20s linear infinite orbit-rotate}.orbit-ring::after{content:'';position:absolute;top:-3px;left:50%;transform:translateX(-50%);width:6px;height:6px;background:rgba(240,147,251,.5);border-radius:50%;box-shadow:0 0 10px rgba(240,147,251,.3)}@keyframes orbit-rotate{from{transform:translate(-50%,-50%) rotate(0)}to{transform:translate(-50%,-50%) rotate(360deg)}}</style>
8
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/7.9.0/d3.min.js"></script>
9
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/marked/14.1.1/marked.min.js"></script>
10
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/mermaid/11.1.0/mermaid.min.js"></script>
11
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/html2canvas/1.4.1/html2canvas.min.js"></script>
12
+ <script id="page-info" src="/api/page-info.js?page=builder"></script>
13
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/topojson/3.0.2/topojson.min.js" id="topojson-lib"></script><style id="us-map-styles">#us-map-container{width:100%;height:100%;display:flex;flex-direction:column;align-items:center;justify-content:center;position:relative;overflow:hidden}#us-map-header{min-height:var(--header-min-height);padding:var(--header-padding-vertical) var(--header-padding-horizontal);line-height:var(--header-line-height);display:flex;align-items:center;justify-content:center;box-sizing:border-box;font-size:1.25rem;font-weight:600;color:var(--bodyText);width:100%;border-bottom:1px solid var(--variantBorder);background-color:var(--bodyStandoutBackground);flex-shrink:0;position:relative}#us-map-svg-wrap{flex:1;width:100%;display:flex;align-items:center;justify-content:center;padding:16px;box-sizing:border-box;position:relative}#us-map-svg-wrap svg{width:100%;height:100%;position:absolute;top:0;left:0}.region-path{cursor:pointer;transition:filter .2s ease;stroke-linejoin:round}.region-path:hover{filter:brightness(1.25)}.region-state{fill:rgba(46,125,50,0.5);stroke:#1b5e20;stroke-width:1.2}.region-oregon{fill:rgba(230,81,0,0.45);stroke:#bf360c;stroke-width:1.5}.region-minnesota{fill:rgba(255,152,0,0.45);stroke:#e65100;stroke-width:1.5}.region-utah{fill:rgba(255,111,0,0.4);stroke:#bf360c;stroke-width:1.5}.region-newmexico{fill:rgba(239,108,0,0.45);stroke:#bf360c;stroke-width:1.5}.region-unorganized{fill:rgba(141,110,99,0.4);stroke:#4e342e;stroke-width:1.5}.region-indian{fill:rgba(121,85,72,0.45);stroke:#3e2723;stroke-width:1.5}.light-mode .region-state{fill:rgba(46,125,50,0.5);stroke:#1b5e20}.light-mode .region-oregon{fill:rgba(230,81,0,0.45);stroke:#bf360c}.light-mode .region-minnesota{fill:rgba(255,152,0,0.45);stroke:#e65100}.light-mode .region-utah{fill:rgba(255,111,0,0.4);stroke:#bf360c}.light-mode .region-newmexico{fill:rgba(239,108,0,0.45);stroke:#bf360c}.light-mode .region-unorganized{fill:rgba(141,110,99,0.4);stroke:#4e342e}.light-mode .region-indian{fill:rgba(121,85,72,0.45);stroke:#3e2723}#map-tooltip{position:absolute;pointer-events:none;background-color:var(--bodyBackground);color:var(--bodyText);border:1px solid var(--variantBorder);border-radius:var(--roundedCorner4);padding:6px 12px;font-size:0.85rem;font-weight:500;box-shadow:var(--elevation8);opacity:0;transition:opacity .15s ease;z-index:10;white-space:nowrap}#hist-legend{position:absolute;bottom:16px;left:16px;background-color:var(--bodyBackground);border:1px solid var(--variantBorder);border-radius:8px;padding:10px 14px;font-size:0.75rem;color:var(--bodyText);z-index:5;box-shadow:0 2px 8px rgba(0,0,0,0.1)}#hist-legend .legend-item{display:flex;align-items:center;gap:8px;margin-bottom:4px}#hist-legend .legend-item:last-child{margin-bottom:0}#hist-legend .legend-swatch{width:14px;height:14px;border-radius:3px;flex-shrink:0}.trail-path{fill:none;stroke:#d32f2f;stroke-width:3;stroke-dasharray:8,4;stroke-linecap:round;stroke-linejoin:round;filter:drop-shadow(0 0 3px rgba(211,47,47,0.5))}.trail-path-glow{fill:none;stroke:rgba(211,47,47,0.25);stroke-width:8;stroke-linecap:round;stroke-linejoin:round}.trail-stop{cursor:pointer}.trail-stop circle.stop-outer{fill:rgba(211,47,47,0.2);stroke:#d32f2f;stroke-width:1.5;transition:r .2s ease, fill .2s ease}.trail-stop circle.stop-inner{fill:#d32f2f;transition:r .2s ease}.trail-stop:hover circle.stop-outer{fill:rgba(211,47,47,0.4)}.trail-stop.active circle.stop-outer{fill:rgba(211,47,47,0.5);stroke-width:2.5;r:9}.trail-stop.active circle.stop-inner{r:4}.trail-label{font-size:8px;font-weight:600;fill:#b71c1c;text-anchor:middle;pointer-events:none;paint-order:stroke;stroke:rgba(255,255,255,0.8);stroke-width:2.5px;stroke-linecap:round;stroke-linejoin:round}.light-mode .trail-path{stroke:#c62828}.light-mode .trail-label{fill:#b71c1c;stroke:rgba(255,255,255,0.85)}#trail-detail-card{position:absolute;top:16px;right:16px;width:340px;max-height:calc(100% - 40px);background-color:var(--bodyBackground);border:1px solid var(--variantBorder);border-radius:var(--roundedCorner4);box-shadow:var(--elevation64);z-index:20;opacity:0;transform:translateY(-10px) scale(0.97);pointer-events:none;transition:opacity .25s ease,transform .25s ease;overflow:hidden;display:flex;flex-direction:column}#trail-detail-card.visible{opacity:1;transform:translateY(0) scale(1);pointer-events:auto}#trail-detail-card .card-header{display:flex;align-items:center;justify-content:space-between;padding:12px 14px 8px;border-bottom:1px solid var(--variantBorder);background-color:var(--bodyStandoutBackground);flex-shrink:0}#trail-detail-card .card-header h3{margin:0;font-size:0.95rem;font-weight:700;color:var(--bodyText);line-height:1.3}#trail-detail-card .card-header .card-close{background:none;border:none;font-size:1.3rem;cursor:pointer;color:var(--bodySubtext);padding:0 2px;line-height:1;border-radius:4px;transition:background .15s ease}#trail-detail-card .card-header .card-close:hover{background-color:var(--buttonBackgroundHovered)}#trail-detail-card .card-subtitle{font-size:0.72rem;color:var(--bodySubtext);margin:2px 0 0;font-weight:500}#trail-detail-card .card-tabs{display:flex;border-bottom:1px solid var(--variantBorder);flex-shrink:0}#trail-detail-card .card-tab{flex:1;padding:8px 10px;font-size:0.78rem;font-weight:600;text-align:center;cursor:pointer;border:none;background:none;color:var(--bodySubtext);transition:all .15s ease;border-bottom:2px solid transparent;position:relative}#trail-detail-card .card-tab:hover{color:var(--bodyText);background-color:var(--buttonBackgroundHovered)}#trail-detail-card .card-tab.active{color:var(--themePrimary);border-bottom-color:var(--themePrimary);background-color:var(--buttonBackgroundHovered)}#trail-detail-card .card-tab-icon{margin-right:4px}#trail-detail-card .tab-content{flex:1;overflow:hidden;display:flex;flex-direction:column;min-height:0}#trail-detail-card .tab-pane{display:none;flex:1;overflow-y:auto;flex-direction:column;min-height:0}#trail-detail-card .tab-pane.active{display:flex}#trail-detail-card .facts-pane{padding:12px 14px;overflow-y:auto}#trail-detail-card .fact-row{display:flex;gap:8px;margin-bottom:8px;align-items:flex-start}#trail-detail-card .fact-icon{font-size:1rem;flex-shrink:0;width:22px;text-align:center;margin-top:1px}#trail-detail-card .fact-content{flex:1}#trail-detail-card .fact-label{font-size:0.65rem;text-transform:uppercase;letter-spacing:0.5px;color:var(--bodySubtext);font-weight:600;margin-bottom:1px}#trail-detail-card .fact-value{font-size:0.8rem;color:var(--bodyText);line-height:1.4}#trail-detail-card .card-divider{height:1px;background-color:var(--variantBorder);margin:4px 0 8px}#trail-detail-card .card-description{font-size:0.78rem;color:var(--bodyText);line-height:1.5;margin-top:2px}#trail-detail-card .mile-marker{display:inline-flex;align-items:center;gap:4px;background:rgba(211,47,47,0.1);color:var(--themePrimary);font-size:0.68rem;font-weight:600;padding:2px 7px;border-radius:var(--roundedCorner4);margin-bottom:8px}.light-mode #trail-detail-card .mile-marker{background:rgba(211,47,47,0.1);color:#b71c1c}.chat-pane{display:flex;flex-direction:column;min-height:0;flex:1}.chat-pane .chat-messages-area{flex:1;overflow-y:auto;padding:10px 12px;display:flex;flex-direction:column;gap:8px;min-height:120px}.chat-pane .chat-welcome{text-align:center;padding:16px 8px;color:var(--bodySubtext);font-size:0.78rem;line-height:1.5}.chat-pane .chat-welcome .welcome-emoji{font-size:1.8rem;margin-bottom:6px;display:block}.chat-pane .chat-welcome strong{color:var(--bodyText)}.chat-pane .chat-bubble{max-width:92%;padding:8px 11px;border-radius:var(--roundedCorner4);font-size:0.78rem;line-height:1.45;word-wrap:break-word;animation:bubbleIn .2s ease}.chat-pane .chat-bubble.user-bubble{align-self:flex-end;background:linear-gradient(135deg,#667eea,#764ba2);color:#fff;border-bottom-right-radius:4px}.chat-pane .chat-bubble.ai-bubble{align-self:flex-start;background-color:var(--bodyStandoutBackground);color:var(--bodyText);border:1px solid var(--variantBorder);border-bottom-left-radius:4px}.chat-pane .chat-bubble.ai-bubble.loading{opacity:0.7}.chat-pane .typing-dots{display:inline-flex;gap:3px;padding:2px 0}.chat-pane .typing-dots span{width:5px;height:5px;border-radius:50%;background-color:var(--bodySubtext);animation:dotBounce .6s ease-in-out infinite}.chat-pane .typing-dots span:nth-child(2){animation-delay:.15s}.chat-pane .typing-dots span:nth-child(3){animation-delay:.3s}@keyframes dotBounce{0%,100%{transform:translateY(0)}50%{transform:translateY(-4px)}}@keyframes bubbleIn{from{opacity:0;transform:translateY(6px)}to{opacity:1;transform:translateY(0)}}.chat-pane .chat-input-area{display:flex;gap:6px;padding:8px 10px;border-top:1px solid var(--variantBorder);background-color:var(--bodyBackground);flex-shrink:0;border-radius:0 0 12px 12px}.chat-pane .chat-input-field{flex:1;border:1px solid var(--variantBorder);border-radius:18px;padding:7px 12px;font-size:0.78rem;background-color:var(--inputBackground);color:var(--bodyText);outline:none;transition:border-color .15s ease;font-family:inherit}.chat-pane .chat-input-field:focus{border-color:var(--themePrimary)}.chat-pane .chat-input-field::placeholder{color:var(--bodySubtext);opacity:0.7}.chat-pane .chat-send-btn{width:32px;height:32px;border-radius:50%;border:none;background:linear-gradient(135deg,#d32f2f,#b71c1c);color:#fff;font-size:0.9rem;cursor:pointer;display:flex;align-items:center;justify-content:center;transition:transform .1s ease,opacity .15s ease;flex-shrink:0}.chat-pane .chat-send-btn:hover{transform:scale(1.08)}.chat-pane .chat-send-btn:disabled{opacity:0.4;cursor:not-allowed;transform:none}.chat-pane .suggested-questions{display:flex;flex-wrap:wrap;gap:4px;padding:0 12px 8px}.chat-pane .suggested-q{font-size:0.68rem;padding:4px 8px;border-radius:var(--roundedCorner4);border:1px solid var(--variantBorder);background-color:var(--inputBackground);color:var(--bodySubtext);cursor:pointer;transition:all .15s ease;white-space:nowrap}.chat-pane .suggested-q:hover{background:rgba(211,47,47,0.08);color:var(--themePrimary);border-color:rgba(211,47,47,0.3)}</style></head>
14
+ <body>
15
+ <div class="viewer-panel" id="viewerPanel"><div id="us-map-container"><div id="us-map-header">United States — Circa 1850 — with Oregon Trail</div><div id="us-map-svg-wrap"><div id="hist-legend"><div style="font-weight:600;margin-bottom:6px;">Circa 1850</div><div class="legend-item"><div class="legend-swatch" style="background:rgba(46,125,50,0.6);border:1px solid #1b5e20;"></div><span>States (31)</span></div><div class="legend-item"><div class="legend-swatch" style="background:rgba(230,81,0,0.55);border:1px solid #bf360c;"></div><span>Oregon Territory</span></div><div class="legend-item"><div class="legend-swatch" style="background:rgba(255,152,0,0.55);border:1px solid #e65100;"></div><span>Minnesota Territory</span></div><div class="legend-item"><div class="legend-swatch" style="background:rgba(255,111,0,0.5);border:1px solid #bf360c;"></div><span>Utah Territory</span></div><div class="legend-item"><div class="legend-swatch" style="background:rgba(239,108,0,0.55);border:1px solid #bf360c;"></div><span>New Mexico Territory</span></div><div class="legend-item"><div class="legend-swatch" style="background:rgba(141,110,99,0.5);border:1px solid #4e342e;"></div><span>Unorganized Territory</span></div><div class="legend-item"><div class="legend-swatch" style="background:rgba(121,85,72,0.55);border:1px solid #3e2723;"></div><span>Indian Territory</span></div><div style="border-top:1px solid var(--bodyDivider);margin-top:6px;padding-top:6px;"></div><div class="legend-item"><div class="legend-swatch" style="background:#d32f2f;border:1px solid #b71c1c;border-radius:50%;"></div><span style="font-weight:600;">Oregon Trail</span></div></div><div id="trail-detail-card"><div class="card-header"><div><h3 id="card-title"></h3><div class="card-subtitle" id="card-subtitle"></div></div><button class="card-close" id="card-close-btn" aria-label="Close">×</button></div><div class="card-tabs"><button class="card-tab active" data-tab="facts"><span class="card-tab-icon">📋</span>Facts</button><button class="card-tab" data-tab="chat"><span class="card-tab-icon">💬</span>Ask Me!</button></div><div class="tab-content"><div class="tab-pane facts-pane active" id="facts-pane"><div id="card-body"></div></div><div class="tab-pane chat-pane" id="chat-pane"><div class="chat-messages-area" id="trail-chat-messages"><div class="chat-welcome"><span class="welcome-emoji">🤠</span>Howdy, explorer! <strong>Click a trail stop</strong> and ask me anything about life on the Oregon Trail!</div></div><div class="suggested-questions" id="trail-suggested-q"></div><div class="chat-input-area"><input type="text" class="chat-input-field" id="trail-chat-input" placeholder="Ask me anything..." autocomplete="off"><button class="chat-send-btn" id="trail-chat-send" aria-label="Send">➤</button></div></div></div></div></div><div id="map-tooltip"></div></div></div>
16
+ <div id="instructions" style="display: none;" data-locked="true"></div>
17
+ <div id="thoughts" style="display: none;" data-locked="true"></div>
18
+ <script id="idle-animation">function hideIdleAnimation(){const idleContainer=document.getElementById("idleAnimation");idleContainer&&(idleContainer.classList.add("hidden"),setTimeout(()=>{idleContainer.style.display="none"},1e3))}function showIdleAnimation(){const idleContainer=document.getElementById("idleAnimation");idleContainer&&(idleContainer.style.display="block",setTimeout(()=>{idleContainer.classList.remove("hidden")},10))}</script>
19
+ <button class="chat-toggle" aria-label="Toggle chat panel">
20
+ <span class="chat-toggle-dots">
21
+ <span class="chat-toggle-dot"></span>
22
+ <span class="chat-toggle-dot"></span>
23
+ <span class="chat-toggle-dot"></span>
24
+ </span>
25
+ </button>
26
+
27
+ <script id="us-map-script">(function(){var FIPS_TO_NAME={1:'Alabama',2:'Alaska',4:'Arizona',5:'Arkansas',6:'California',8:'Colorado',9:'Connecticut',10:'Delaware',11:'District of Columbia',12:'Florida',13:'Georgia',15:'Hawaii',16:'Idaho',17:'Illinois',18:'Indiana',19:'Iowa',20:'Kansas',21:'Kentucky',22:'Louisiana',23:'Maine',24:'Maryland',25:'Massachusetts',26:'Michigan',27:'Minnesota',28:'Mississippi',29:'Missouri',30:'Montana',31:'Nebraska',32:'Nevada',33:'New Hampshire',34:'New Jersey',35:'New Mexico',36:'New York',37:'North Carolina',38:'North Dakota',39:'Ohio',40:'Oklahoma',41:'Oregon',42:'Pennsylvania',44:'Rhode Island',45:'South Carolina',46:'South Dakota',47:'Tennessee',48:'Texas',49:'Utah',50:'Vermont',51:'Virginia',53:'Washington',54:'West Virginia',55:'Wisconsin',56:'Wyoming'};
28
+ var STATES_1850=[9,10,13,24,25,33,34,36,37,42,44,45,51,50,21,47,39,22,18,28,17,1,23,29,5,26,12,48,19,55,6,54,11];
29
+ var OREGON_TERR=[41,53,16];
30
+ var MINNESOTA_TERR=[27];
31
+ var UTAH_TERR=[49,32];
32
+ var NEWMEXICO_TERR=[35,4];
33
+ var UNORGANIZED=[31,20,38,46,56,30,8];
34
+ var INDIAN_TERR=[40];
35
+ var TERRITORY_GROUPS=[{fips:OREGON_TERR,name:'Oregon Territory',cls:'region-oregon'},{fips:MINNESOTA_TERR,name:'Minnesota Territory',cls:'region-minnesota'},{fips:UTAH_TERR,name:'Utah Territory',cls:'region-utah'},{fips:NEWMEXICO_TERR,name:'New Mexico Territory',cls:'region-newmexico'},{fips:UNORGANIZED,name:'Unorganized Territory',cls:'region-unorganized'},{fips:INDIAN_TERR,name:'Indian Territory',cls:'region-indian'}];
36
+ var TRAIL_POINTS=[[-94.41,39.09],[-95.20,39.05],[-95.68,39.05],[-96.50,39.20],[-97.30,39.50],[-98.00,40.00],[-99.00,40.65],[-99.80,40.85],[-100.77,41.12],[-101.80,41.40],[-103.00,41.70],[-103.70,41.83],[-104.52,42.21],[-105.50,42.50],[-106.31,42.87],[-107.13,42.49],[-108.00,42.40],[-108.80,42.33],[-109.50,42.00],[-110.39,41.32],[-110.90,42.00],[-111.60,42.66],[-112.43,42.92],[-113.50,42.70],[-114.46,42.56],[-115.30,43.00],[-116.20,43.62],[-117.00,44.05],[-117.80,44.80],[-118.50,45.30],[-119.50,45.55],[-120.50,45.60],[-121.18,45.60],[-121.80,45.50],[-122.61,45.36]];
37
+ var TRAIL_STOPS=[{coords:[-94.41,39.09],name:'Independence, MO',labelPos:'below'},{coords:[-99.00,40.65],name:'Fort Kearny',labelPos:'above'},{coords:[-103.00,41.70],name:'Chimney Rock',labelPos:'above'},{coords:[-104.52,42.21],name:'Fort Laramie',labelPos:'above'},{coords:[-107.13,42.49],name:'Independence Rock',labelPos:'above'},{coords:[-108.80,42.33],name:'South Pass',labelPos:'below'},{coords:[-110.39,41.32],name:'Fort Bridger',labelPos:'below'},{coords:[-112.43,42.92],name:'Fort Hall',labelPos:'above'},{coords:[-116.20,43.62],name:'Fort Boise',labelPos:'above'},{coords:[-121.18,45.60],name:'The Dalles',labelPos:'above'},{coords:[-122.61,45.36],name:'Oregon City',labelPos:'below'}];
38
+ var STOP_FACTS={'Independence, MO':{subtitle:'The Gateway to the West',established:'Settled 1827',elevation:'830 ft (253 m)',miles:'Mile 0 — Starting Point',territory:'State of Missouri',facts:[{icon:'🏪',label:'Role',value:'Primary "jumping-off" point for westward emigrants'},{icon:'📅',label:'Peak Years',value:'1843–1860, with up to 50,000 emigrants departing annually in peak years'},{icon:'🛒',label:'Supplies',value:'Emigrants stocked up on flour, bacon, coffee, sugar, and oxen — a typical family spent $600–$1,000 outfitting'},{icon:'👥',label:'Population',value:'About 3,000 residents by 1850, swelling each spring with emigrants'}],description:'Independence was the most popular starting point for the Oregon, California, and Santa Fe trails. Wagon trains typically departed between April and May to cross the mountains before winter snows.'},'Fort Kearny':{subtitle:'First Military Outpost on the Trail',established:'Est. 1848',elevation:'2,146 ft (654 m)',miles:'Mile 319 — ~2 weeks from Independence',territory:'Unorganized Territory (present-day Nebraska)',facts:[{icon:'⚔️',label:'Purpose',value:'U.S. Army post built to protect emigrants on the Oregon Trail'},{icon:'📬',label:'Services',value:'Post office, blacksmith shop, and supply store for travelers'},{icon:'🦬',label:'Wildlife',value:'Located in prime buffalo country — emigrants often saw herds of thousands'},{icon:'🪖',label:'Garrison',value:'Typically 200–400 soldiers stationed here'}],description:'Fort Kearny served as the first major rest stop and resupply point. It was also where the trail from St. Joseph merged with the Independence route. Soldiers here helped repair wagons and provided medical aid.'},'Chimney Rock':{subtitle:'The Most Famous Landmark on the Trail',established:'Named by fur traders, 1820s',elevation:'4,228 ft (1,289 m)',miles:'Mile 561 — ~5 weeks from Independence',territory:'Unorganized Territory (present-day Nebraska)',facts:[{icon:'🏔️',label:'Height',value:'Spire rises ~325 ft above the North Platte River valley'},{icon:'📖',label:'Mentions',value:'Referenced in more emigrant journals than any other landmark on the trail'},{icon:'⚡',label:'Erosion',value:'Was significantly taller in the 1840s — lightning and erosion have reduced it'},{icon:'🎯',label:'Significance',value:'Marked the transition from prairies to the rugged terrain of the West'}],description:'Chimney Rock was visible for days before emigrants reached it, serving as a beacon of progress. Many carved their names into its soft clay base. It told travelers they were about one-third of the way to Oregon.'},'Fort Laramie':{subtitle:'Crossroads of the Frontier',established:'Est. 1834 (fur trade); U.S. Army 1849',elevation:'4,519 ft (1,377 m)',miles:'Mile 650 — ~6 weeks from Independence',territory:'Unorganized Territory (present-day Wyoming)',facts:[{icon:'🏛️',label:'Origin',value:'Started as a fur trading post called Fort William, later Fort John'},{icon:'🤝',label:'Treaty',value:'Site of the 1851 Fort Laramie Treaty with Plains Indian nations'},{icon:'🔧',label:'Services',value:'Blacksmith, trading post, and the last reliable resupply before the Rockies'},{icon:'⚖️',label:'Lightening Loads',value:'Emigrants often abandoned heavy items here — the fort was surrounded by discarded goods'}],description:'Fort Laramie was the most important stop on the entire trail. Here emigrants rested, repaired wagons, traded worn-out oxen, and prepared for the difficult mountain crossings ahead. Many wrote letters home from here.'},'Independence Rock':{subtitle:'The Register of the Desert',established:'Named by fur trappers, ~1824',elevation:'5,900 ft (1,798 m)',miles:'Mile 838 — ~8 weeks from Independence',territory:'Unorganized Territory (present-day Wyoming)',facts:[{icon:'🪨',label:'Size',value:'Granite dome ~1,900 ft long, 700 ft wide, and 128 ft high'},{icon:'✍️',label:'Names',value:'Over 5,000 names were carved or painted on the rock by passing emigrants'},{icon:'📅',label:'Timing',value:'Emigrants aimed to reach it by July 4th (Independence Day) to stay on schedule'},{icon:'🎆',label:'Celebrations',value:'Many wagon trains held Fourth of July celebrations here'}],description:'This massive granite outcrop became the most famous "guest book" in American history. Reaching it by Independence Day meant you were on pace to cross the mountains before winter. Father De Smet called it the "Register of the Desert" in 1840.'},'South Pass':{subtitle:'Crossing the Continental Divide',established:'Discovered by Europeans ~1812; widely used from 1840s',elevation:'7,550 ft (2,301 m)',miles:'Mile 914 — ~9 weeks from Independence',territory:'Unorganized Territory (present-day Wyoming)',facts:[{icon:'⛰️',label:'Geography',value:'A broad, gentle 20-mile-wide saddle through the Rocky Mountains'},{icon:'💧',label:'Divide',value:'Waters east flow to the Atlantic; waters west flow to the Pacific'},{icon:'😮',label:'Surprise',value:'So gradual that many emigrants didn\'t realize they\'d crossed the Rockies'},{icon:'🗺️',label:'Importance',value:'The only feasible wagon route through the Rockies for 2,000 miles'}],description:'South Pass was the key to the entire Oregon Trail. Without this gentle crossing of the Continental Divide, wagon travel to Oregon would have been nearly impossible. Despite being at 7,550 feet, the pass was so wide and gradual that it felt like rolling prairie.'},'Fort Bridger':{subtitle:'Mountain Man\'s Trading Post',established:'Est. 1843 by Jim Bridger',elevation:'6,665 ft (2,031 m)',miles:'Mile 1,026 — ~10 weeks from Independence',territory:'Unorganized Territory (present-day Wyoming)',facts:[{icon:'🧔',label:'Founder',value:'Jim Bridger — legendary mountain man, fur trapper, and scout'},{icon:'🔀',label:'Junction',value:'Where the Oregon Trail and the Mormon Trail to Salt Lake diverged'},{icon:'🐎',label:'Trade',value:'Emigrants traded tired animals for fresh ones with local Shoshone people'},{icon:'🏚️',label:'Condition',value:'Many emigrants were disappointed — described it as a shabby collection of log huts'}],description:'Jim Bridger and Louis Vasquez established this trading post on Black\'s Fork of the Green River. Despite its rough appearance, it was a critical decision point where emigrants chose between routes. The Donner Party fatefully took the Hastings Cutoff from near here in 1846.'},'Fort Hall':{subtitle:'Hudson\'s Bay Company Outpost',established:'Est. 1834 by Nathaniel Wyeth',elevation:'4,400 ft (1,341 m)',miles:'Mile 1,217 — ~12 weeks from Independence',territory:'Oregon Territory (present-day Idaho)',facts:[{icon:'🇬🇧',label:'Ownership',value:'Sold to Britain\'s Hudson\'s Bay Company in 1837'},{icon:'⚠️',label:'Discouragement',value:'HBC agents often tried to discourage American emigrants from continuing to Oregon'},{icon:'🔀',label:'Split',value:'Where the California Trail branched south from the Oregon Trail'},{icon:'🛞',label:'Wagons',value:'Early emigrants were told to abandon wagons here — the first to take wagons through to Oregon did so in 1843'}],description:'Fort Hall sat at a strategic crossroads. The Hudson\'s Bay Company, wanting to keep Americans out of Oregon, often told emigrants the route ahead was impassable by wagon. Despite this, thousands pushed through. After 1843, it became the split point for California-bound gold seekers.'},'Fort Boise':{subtitle:'Snake River Oasis',established:'Est. 1834 by Hudson\'s Bay Company',elevation:'2,730 ft (832 m)',miles:'Mile 1,450 — ~14 weeks from Independence',territory:'Oregon Territory (present-day Idaho)',facts:[{icon:'🏊',label:'River Crossing',value:'Emigrants had to ford the dangerous Boise River and Snake River nearby'},{icon:'🌿',label:'Relief',value:'Green river valley was a welcome sight after the harsh Snake River Plain'},{icon:'💀',label:'Danger',value:'The Snake River crossings near here claimed many lives — swift current and hidden holes'},{icon:'🐟',label:'Food',value:'Emigrants could trade with local Shoshone for salmon and camas root'}],description:'After weeks of dusty, barren travel along the Snake River Plain, Fort Boise offered shade, fresh water, and a chance to trade. The river crossings in this area were among the most dangerous on the entire trail, with many drownings recorded in emigrant diaries.'},'The Dalles':{subtitle:'End of the Overland Road',established:'Methodist mission est. 1838',elevation:'200 ft (61 m)',miles:'Mile 1,880 — ~18 weeks from Independence',territory:'Oregon Territory (present-day Oregon)',facts:[{icon:'🌊',label:'Challenge',value:'The Columbia River Gorge ahead was too narrow and dangerous for wagons'},{icon:'🛶',label:'River Route',value:'Many emigrants built rafts or hired Native canoes to float to Oregon City'},{icon:'🛤️',label:'Barlow Road',value:'After 1846, Sam Barlow\'s toll road over Mt. Hood offered an alternative ($5 per wagon)'},{icon:'⛪',label:'Mission',value:'A Methodist mission here served as a waystation for weary travelers'}],description:'The Dalles marked the end of the overland trail and the beginning of the final, often harrowing leg of the journey. The Columbia River route through the gorge was treacherous — many lost everything on the rapids. The Barlow Road, though steep and muddy, became the preferred route after 1846.'},'Oregon City':{subtitle:'Journey\'s End — The Promised Land',established:'Founded 1829; incorporated 1844',elevation:'55 ft (17 m)',miles:'Mile 2,000 — ~5 months from Independence',territory:'Oregon Territory',facts:[{icon:'🏛️',label:'Capital',value:'First incorporated city west of the Rockies; capital of Oregon Territory 1848–1852'},{icon:'🌊',label:'Falls',value:'Built at Willamette Falls — the falls powered sawmills and flour mills'},{icon:'📜',label:'Land Claims',value:'Under the Donation Land Claim Act (1850), settlers could claim 320–640 acres free'},{icon:'👨‍👩‍👧‍👦',label:'Arrivals',value:'~53,000 emigrants reached Oregon by wagon between 1840–1860'}],description:'Oregon City was the official end of the Oregon Trail and the heart of the new American settlement in the Pacific Northwest. Exhausted emigrants arrived here after five months and 2,000 miles to claim their new lives. The fertile Willamette Valley beyond offered the rich farmland that had drawn them west.'}};
39
+
40
+ var SUGGESTED_QUESTIONS={'Independence, MO':['What did kids do on the trail?','What food did they bring?','How long was the trip?'],'Fort Kearny':['Did kids go to school?','Were there really buffalo?','What was a typical day like?'],'Chimney Rock':['How tall is it really?','Did people climb it?','What did it look like back then?'],'Fort Laramie':['What did they trade?','Who lived at the fort?','Was it dangerous here?'],'Independence Rock':['Why did people carve names?','Can you still see the names?','What games did kids play?'],'South Pass':['How cold was it up there?','Did wagons ever get stuck?','What animals lived here?'],'Fort Bridger':['Who was Jim Bridger?','What happened to the Donner Party?','What did they eat here?'],'Fort Hall':['Why did the British want Oregon?','How did they cross rivers?','Were there any kids on the trail?'],'Fort Boise':['How dangerous were river crossings?','What fish could they catch?','Did anyone get lost?'],'The Dalles':['What were the rapids like?','How much was the toll road?','Did anyone\'s raft sink?'],'Oregon City':['What happened when they arrived?','How big was the free land?','What did they build first?']};
41
+
42
+ var activeStop=null;
43
+ var chatHistory=[];
44
+ var currentStopName=null;
45
+
46
+ function showDetailCard(stopName){
47
+ var card=document.getElementById('trail-detail-card');
48
+ var facts=STOP_FACTS[stopName];
49
+ if(!facts||!card)return;
50
+ document.getElementById('card-title').textContent=stopName;
51
+ document.getElementById('card-subtitle').textContent=facts.subtitle;
52
+ var body=document.getElementById('card-body');
53
+ var html='';
54
+ html+='<span class="mile-marker">📍 '+facts.miles+'</span>';
55
+ html+='<div class="fact-row"><div class="fact-icon">🗓️</div><div class="fact-content"><div class="fact-label">Established</div><div class="fact-value">'+facts.established+'</div></div></div>';
56
+ html+='<div class="fact-row"><div class="fact-icon">📏</div><div class="fact-content"><div class="fact-label">Elevation</div><div class="fact-value">'+facts.elevation+'</div></div></div>';
57
+ html+='<div class="fact-row"><div class="fact-icon">🗺️</div><div class="fact-content"><div class="fact-label">Territory</div><div class="fact-value">'+facts.territory+'</div></div></div>';
58
+ html+='<div class="card-divider"></div>';
59
+ facts.facts.forEach(function(f){html+='<div class="fact-row"><div class="fact-icon">'+f.icon+'</div><div class="fact-content"><div class="fact-label">'+f.label+'</div><div class="fact-value">'+f.value+'</div></div></div>';});
60
+ html+='<div class="card-divider"></div>';
61
+ html+='<div class="card-description">'+facts.description+'</div>';
62
+ body.innerHTML=html;
63
+
64
+ if(currentStopName!==stopName){
65
+ currentStopName=stopName;
66
+ chatHistory=[];
67
+ var msgArea=document.getElementById('trail-chat-messages');
68
+ msgArea.innerHTML='<div class="chat-welcome"><span class="welcome-emoji">🤠</span>Howdy, explorer! You\'re at <strong>'+stopName+'</strong>! Ask me anything about this place or the Oregon Trail journey!</div>';
69
+ }
70
+ updateSuggestedQuestions(stopName);
71
+
72
+ activeStop=stopName;
73
+ card.classList.add('visible');
74
+ setActiveTab('facts');
75
+ d3.selectAll('.trail-stop').classed('active',false);
76
+ d3.selectAll('.trail-stop').each(function(){var el=d3.select(this);if(el.attr('data-stop-name')===stopName)el.classed('active',true);});
77
+ }
78
+
79
+ function hideDetailCard(){
80
+ var card=document.getElementById('trail-detail-card');
81
+ if(card)card.classList.remove('visible');
82
+ activeStop=null;
83
+ d3.selectAll('.trail-stop').classed('active',false);
84
+ }
85
+
86
+ function setActiveTab(tabName){
87
+ document.querySelectorAll('#trail-detail-card .card-tab').forEach(function(t){
88
+ t.classList.toggle('active',t.getAttribute('data-tab')===tabName);
89
+ });
90
+ document.querySelectorAll('#trail-detail-card .tab-pane').forEach(function(p){
91
+ var id=p.id;
92
+ if(tabName==='facts')p.classList.toggle('active',id==='facts-pane');
93
+ else p.classList.toggle('active',id==='chat-pane');
94
+ });
95
+ if(tabName==='chat'){
96
+ var input=document.getElementById('trail-chat-input');
97
+ if(input)setTimeout(function(){input.focus();},100);
98
+ scrollChatToBottom();
99
+ }
100
+ }
101
+
102
+ function updateSuggestedQuestions(stopName){
103
+ var container=document.getElementById('trail-suggested-q');
104
+ var questions=SUGGESTED_QUESTIONS[stopName]||['What was it like here?','Tell me something cool!','Was it dangerous?'];
105
+ container.innerHTML='';
106
+ questions.forEach(function(q){
107
+ var btn=document.createElement('button');
108
+ btn.className='suggested-q';
109
+ btn.textContent=q;
110
+ btn.addEventListener('click',function(){sendTrailChat(q);});
111
+ container.appendChild(btn);
112
+ });
113
+ }
114
+
115
+ function scrollChatToBottom(){
116
+ var area=document.getElementById('trail-chat-messages');
117
+ if(area)area.scrollTop=area.scrollHeight;
118
+ }
119
+
120
+ function addChatBubble(text,isUser){
121
+ var area=document.getElementById('trail-chat-messages');
122
+ var welcome=area.querySelector('.chat-welcome');
123
+ if(welcome)welcome.remove();
124
+ var bubble=document.createElement('div');
125
+ bubble.className='chat-bubble '+(isUser?'user-bubble':'ai-bubble');
126
+ bubble.textContent=text;
127
+ area.appendChild(bubble);
128
+ scrollChatToBottom();
129
+ return bubble;
130
+ }
131
+
132
+ function addLoadingBubble(){
133
+ var area=document.getElementById('trail-chat-messages');
134
+ var bubble=document.createElement('div');
135
+ bubble.className='chat-bubble ai-bubble loading';
136
+ bubble.id='trail-loading-bubble';
137
+ bubble.innerHTML='<div class="typing-dots"><span></span><span></span><span></span></div>';
138
+ area.appendChild(bubble);
139
+ scrollChatToBottom();
140
+ return bubble;
141
+ }
142
+
143
+ function removeLoadingBubble(){
144
+ var el=document.getElementById('trail-loading-bubble');
145
+ if(el)el.remove();
146
+ }
147
+
148
+ function buildPrompt(userQuestion){
149
+ var stopData=STOP_FACTS[currentStopName];
150
+ var contextParts=[];
151
+ contextParts.push('You are a friendly, enthusiastic trail guide named "Dusty" who teaches kids (ages 8-12) about the Oregon Trail and the 1850s American frontier.');
152
+ contextParts.push('You are currently at: '+currentStopName+' — '+stopData.subtitle);
153
+ contextParts.push('Key facts about this location:');
154
+ contextParts.push('- Established: '+stopData.established);
155
+ contextParts.push('- Elevation: '+stopData.elevation);
156
+ contextParts.push('- Position on trail: '+stopData.miles);
157
+ contextParts.push('- Territory: '+stopData.territory);
158
+ contextParts.push('- Description: '+stopData.description);
159
+ stopData.facts.forEach(function(f){contextParts.push('- '+f.label+': '+f.value);});
160
+ contextParts.push('');
161
+ contextParts.push('RULES:');
162
+ contextParts.push('- Use simple, fun language appropriate for 8-12 year olds');
163
+ contextParts.push('- Keep answers to 2-4 short paragraphs max');
164
+ contextParts.push('- Use vivid descriptions and comparisons kids can relate to');
165
+ contextParts.push('- Include fun facts when possible');
166
+ contextParts.push('- If you don\'t know something, say so honestly');
167
+ contextParts.push('- You can reference other stops on the Oregon Trail for context');
168
+ contextParts.push('- Use occasional emojis but don\'t overdo it');
169
+ contextParts.push('- Never break character — you are Dusty the trail guide');
170
+ contextParts.push('');
171
+
172
+ if(chatHistory.length>0){
173
+ contextParts.push('Recent conversation:');
174
+ var recentHistory=chatHistory.slice(-10);
175
+ recentHistory.forEach(function(turn){
176
+ contextParts.push((turn.role==='user'?'Kid: ':'Dusty: ')+turn.text);
177
+ });
178
+ contextParts.push('');
179
+ }
180
+
181
+ contextParts.push('Kid\'s question: '+userQuestion);
182
+ contextParts.push('');
183
+ contextParts.push('Respond as Dusty the trail guide:');
184
+ return contextParts.join('\n');
185
+ }
186
+
187
+ async function sendTrailChat(message){
188
+ if(!message||!message.trim()||!currentStopName)return;
189
+ var input=document.getElementById('trail-chat-input');
190
+ var sendBtn=document.getElementById('trail-chat-send');
191
+ message=message.trim();
192
+ input.value='';
193
+ sendBtn.disabled=true;
194
+
195
+ setActiveTab('chat');
196
+ addChatBubble(message,true);
197
+ chatHistory.push({role:'user',text:message});
198
+
199
+ addLoadingBubble();
200
+
201
+ try{
202
+ var prompt=buildPrompt(message);
203
+ var result=await synthos.generate.completion({prompt:prompt,temperature:0.7});
204
+ removeLoadingBubble();
205
+ var answer=result.answer||'Hmm, my trail map got a little dusty! Try asking me again. 🤠';
206
+ addChatBubble(answer,false);
207
+ chatHistory.push({role:'assistant',text:answer});
208
+ if(chatHistory.length>20)chatHistory=chatHistory.slice(-20);
209
+ }catch(err){
210
+ removeLoadingBubble();
211
+ addChatBubble('Whoops! My wagon wheel got stuck. 🛞 Try asking again in a moment!',false);
212
+ console.error('Trail chat error:',err);
213
+ }
214
+ sendBtn.disabled=false;
215
+ input.focus();
216
+ }
217
+
218
+ window._showTrailDetail=showDetailCard;
219
+ window._hideTrailDetail=hideDetailCard;
220
+
221
+ document.getElementById('card-close-btn').addEventListener('click',function(e){e.stopPropagation();hideDetailCard();});
222
+
223
+ document.querySelectorAll('#trail-detail-card .card-tab').forEach(function(tab){
224
+ tab.addEventListener('click',function(e){
225
+ e.stopPropagation();
226
+ setActiveTab(tab.getAttribute('data-tab'));
227
+ });
228
+ });
229
+
230
+ document.getElementById('trail-chat-send').addEventListener('click',function(e){
231
+ e.stopPropagation();
232
+ var input=document.getElementById('trail-chat-input');
233
+ sendTrailChat(input.value);
234
+ });
235
+
236
+ document.getElementById('trail-chat-input').addEventListener('keydown',function(e){
237
+ if(e.key==='Enter'&&!e.shiftKey){e.preventDefault();e.stopPropagation();sendTrailChat(this.value);}
238
+ });
239
+
240
+ document.getElementById('trail-chat-input').addEventListener('click',function(e){e.stopPropagation();});
241
+
242
+ function init(){
243
+ var wrap=document.getElementById('us-map-svg-wrap');
244
+ if(!wrap)return;
245
+ var tooltip=document.getElementById('map-tooltip');
246
+ wrap.querySelectorAll('svg').forEach(function(s){s.remove();});
247
+ var svg=d3.select(wrap).append('svg').attr('id','hist-map-svg').attr('viewBox','0 0 960 600').attr('preserveAspectRatio','xMidYMid meet').style('padding','16px').style('box-sizing','border-box');
248
+ var projection=d3.geoAlbersUsa().scale(1280).translate([480,300]);
249
+ var path=d3.geoPath().projection(projection);
250
+ d3.json('https://cdn.jsdelivr.net/npm/us-atlas@3/states-10m.json').then(function(us){
251
+ var statesGeo=us.objects.states;
252
+ var allGeometries=statesGeo.geometries;
253
+ function geomsForFips(fipsList){return allGeometries.filter(function(g){return fipsList.indexOf(+g.id)!==-1;});}
254
+ var stateGeoms=geomsForFips(STATES_1850);
255
+ stateGeoms.forEach(function(geom){
256
+ var feature=topojson.feature(us,geom);
257
+ svg.append('path').datum(feature).attr('class','region-path region-state').attr('d',path)
258
+ .on('mousemove',function(event){var name=FIPS_TO_NAME[+geom.id]||'Unknown';tooltip.textContent=name+' (State)';tooltip.style.opacity='1';var rect=wrap.getBoundingClientRect();var x=event.clientX-rect.left+12;var y=event.clientY-rect.top-30;if(x+180>rect.width)x=x-200;if(y<0)y=event.clientY-rect.top+18;tooltip.style.left=x+'px';tooltip.style.top=y+'px';}).on('mouseleave',function(){tooltip.style.opacity='0';}).on('click',function(){hideDetailCard();});
259
+ });
260
+ TERRITORY_GROUPS.forEach(function(group){
261
+ var geoms=geomsForFips(group.fips);
262
+ if(geoms.length===0)return;
263
+ var merged=topojson.merge(us,geoms);
264
+ svg.append('path').datum(merged).attr('class','region-path '+group.cls).attr('d',path)
265
+ .on('mousemove',function(event){tooltip.textContent=group.name;tooltip.style.opacity='1';var rect=wrap.getBoundingClientRect();var x=event.clientX-rect.left+12;var y=event.clientY-rect.top-30;if(x+180>rect.width)x=x-200;if(y<0)y=event.clientY-rect.top+18;tooltip.style.left=x+'px';tooltip.style.top=y+'px';}).on('mouseleave',function(){tooltip.style.opacity='0';}).on('click',function(){hideDetailCard();});
266
+ });
267
+ var stateGeoms2=geomsForFips(STATES_1850);
268
+ svg.append('path').datum(topojson.mesh(us,{type:'GeometryCollection',geometries:stateGeoms2},function(a,b){return a!==b&&STATES_1850.indexOf(+a.id)!==-1&&STATES_1850.indexOf(+b.id)!==-1;})).attr('fill','none').attr('stroke','rgba(27,94,32,0.4)').attr('stroke-width','0.5').attr('d',path).style('pointer-events','none');
269
+ var trailGroup=svg.append('g').attr('class','trail-group');
270
+ var projectedPoints=[];
271
+ TRAIL_POINTS.forEach(function(p){var pt=projection(p);if(pt)projectedPoints.push(pt);});
272
+ if(projectedPoints.length>1){
273
+ var lineGen=d3.line().x(function(d){return d[0];}).y(function(d){return d[1];}).curve(d3.curveCatmullRom.alpha(0.5));
274
+ trailGroup.append('path').attr('class','trail-path-glow').attr('d',lineGen(projectedPoints));
275
+ trailGroup.append('path').attr('class','trail-path').attr('d',lineGen(projectedPoints));
276
+ }
277
+ TRAIL_STOPS.forEach(function(stop){
278
+ var pt=projection(stop.coords);
279
+ if(!pt)return;
280
+ var g=trailGroup.append('g').attr('class','trail-stop').attr('transform','translate('+pt[0]+','+pt[1]+')').attr('data-stop-name',stop.name);
281
+ g.append('circle').attr('class','stop-outer').attr('r',6);
282
+ g.append('circle').attr('class','stop-inner').attr('r',2.5);
283
+ var labelY=stop.labelPos==='above'?-10:14;
284
+ g.append('text').attr('class','trail-label').attr('y',labelY).text(stop.name);
285
+ g.on('mousemove',function(event){tooltip.textContent='Oregon Trail: '+stop.name;tooltip.style.opacity='1';var rect=wrap.getBoundingClientRect();var x=event.clientX-rect.left+12;var y=event.clientY-rect.top-30;if(x+180>rect.width)x=x-200;if(y<0)y=event.clientY-rect.top+18;tooltip.style.left=x+'px';tooltip.style.top=y+'px';}).on('mouseleave',function(){tooltip.style.opacity='0';}).on('click',function(event){event.stopPropagation();showDetailCard(stop.name);});
286
+ });
287
+ }).catch(function(err){console.error('Failed to load US map data:',err);wrap.innerHTML='<p style="color:var(--bodySubtext);text-align:center;">Failed to load map data. Please try again.</p>';});
288
+ }
289
+ if(typeof topojson!=='undefined'){init();}else{var check=setInterval(function(){if(typeof topojson!=='undefined'){clearInterval(check);init();}},100);setTimeout(function(){clearInterval(check);},10000);}
290
+ })();</script></body></html>