diff --git a/app/assets/stylesheets/application.css b/app/assets/stylesheets/application.css
index b5089e0..a66480d 100644
--- a/app/assets/stylesheets/application.css
+++ b/app/assets/stylesheets/application.css
@@ -388,6 +388,22 @@ main, .modal-panel {
animation: wave-slide-out var(--dur-fast) var(--ease-in) var(--dur-base) both;
}
+ .facts-modal {
+ transition: translate var(--dur-base) var(--ease-in);
+ }
+
+ .facts-modal::backdrop {
+ transition: background-color var(--dur-base) var(--ease-in);
+ }
+
+ .facts-modal.is-leaving {
+ translate: 0 100%;
+ }
+
+ .facts-modal.is-leaving::backdrop {
+ background-color: rgba(0, 0, 0, 0);
+ }
+
.facts-modal .carousel > li {
overflow: clip;
}
@@ -713,6 +729,15 @@ main, .modal-panel {
}
}
+.preload-media {
+ position: fixed;
+ inset: auto auto 0 0;
+ width: 1px;
+ height: 1px;
+ opacity: 0;
+ pointer-events: none;
+}
+
.hero-container, .carousel, .carousel-frame {
flex: 1 1 0;
min-height: 0;
diff --git a/app/javascript/modal_controller.js b/app/javascript/modal_controller.js
index 8ea691a..d869617 100644
--- a/app/javascript/modal_controller.js
+++ b/app/javascript/modal_controller.js
@@ -22,6 +22,22 @@ export default class extends Controller {
this.element.remove()
}
+ // The panel sits above the game frame rather than inside it, so the next
+ // screen renders behind it. Once it is there, the panel drops off the bottom
+ // and leaves the intro playing underneath.
+ leave() {
+ if (this.leaving) return
+
+ this.leaving = true
+ document.addEventListener("turbo:frame-render", this.#slideAway, { once: true })
+ }
+
+ #slideAway = async () => {
+ this.element.classList.add("is-leaving")
+ await this.#settle(this.element, "transitionend")
+ this.close()
+ }
+
// The screen underneath may still be animating out. The wave is the last
// thing to leave, so waiting on it holds the modal until the exit is done.
async #openWhenSettled() {
@@ -89,7 +105,11 @@ export default class extends Controller {
? parseFloat(style.animationDuration) + parseFloat(style.animationDelay)
: parseFloat(style.transitionDuration) + parseFloat(style.transitionDelay)
- const finish = () => {
+ // Descendants and ::backdrop send their own events up through the same
+ // element, and either would settle this early.
+ const finish = (event) => {
+ if (event && (event.target !== element || event.pseudoElement)) return
+
element.removeEventListener(eventName, finish)
clearTimeout(timer)
resolve()
diff --git a/app/views/game/_facts_content.html.erb b/app/views/game/_facts_content.html.erb
index 46ddf2a..0ff1e16 100644
--- a/app/views/game/_facts_content.html.erb
+++ b/app/views/game/_facts_content.html.erb
@@ -30,6 +30,7 @@
- <%= link_to tag.span(t("game.got_it_lets_get_started")), { action: "intro" }, class: "cta" %>
+ <%= link_to tag.span(t("game.got_it_lets_get_started")), { action: "intro" }, class: "cta",
+ data: { turbo_frame: "game", action: "click->modal#leave" } %>
diff --git a/app/views/game/_preload_intro.html.erb b/app/views/game/_preload_intro.html.erb
new file mode 100644
index 0000000..a26b8bf
--- /dev/null
+++ b/app/views/game/_preload_intro.html.erb
@@ -0,0 +1,11 @@
+<% intro_video = root_node&.children&.intro&.first&.attachments&.find { |a| a.asset&.file&.video? } %>
+
+<% if intro_video %>
+ <%= video_tag rails_storage_proxy_path(intro_video.asset.file),
+ preload: "auto",
+ muted: true,
+ playsinline: true,
+ class: "preload-media",
+ "aria-hidden": true,
+ tabindex: -1 %>
+<% end %>
diff --git a/app/views/game/facts.html.erb b/app/views/game/facts.html.erb
index a873715..ae69089 100644
--- a/app/views/game/facts.html.erb
+++ b/app/views/game/facts.html.erb
@@ -1,3 +1,5 @@
<%- content_for :title, node_title(@node) %>
<%= render "facts_content" %>
+
+<%= render partial: "preload_intro" %>
diff --git a/app/views/game/start.html.erb b/app/views/game/start.html.erb
index 6013805..aefb7de 100644
--- a/app/views/game/start.html.erb
+++ b/app/views/game/start.html.erb
@@ -23,4 +23,6 @@
<%= svg "ico-clock" %>
<%= tag.span t("game.play_time") %>
<% end %>
-
\ No newline at end of file
+
+
+<%= render partial: "preload_intro" %>
diff --git a/app/views/game/start.turbo_stream.erb b/app/views/game/start.turbo_stream.erb
index 1411237..0dec844 100644
--- a/app/views/game/start.turbo_stream.erb
+++ b/app/views/game/start.turbo_stream.erb
@@ -1,4 +1,4 @@
-<%= turbo_stream.append "game" do %>
+<%= turbo_stream.append "page" do %>