diff --git a/404.html b/404.html
index 3c4a766..7c6f339 100644
--- a/404.html
+++ b/404.html
@@ -2,13 +2,22 @@
-
+
+
페이지를 찾을 수 없습니다 | Causal Inference Hub
diff --git a/article.html b/article.html
index 4627f28..1ca2ac5 100644
--- a/article.html
+++ b/article.html
@@ -2,13 +2,22 @@
-
+
+
읽을거리 | Causal Inference Hub
diff --git a/assets/js/hub.js b/assets/js/hub.js
index 88ab80c..059df26 100644
--- a/assets/js/hub.js
+++ b/assets/js/hub.js
@@ -782,6 +782,7 @@
function openStation(id) {
const r = state.repos.get(id);
if (!r) return;
+ if (window.hubTrack) window.hubTrack("station_open", { repo: id, level: r.level || "", from_link: stationFromHash() === id ? "yes" : "no" });
if (r.kind !== "paid") {
state.recent = [{ id, t: Date.now() }, ...state.recent.filter((x) => x.id !== id)].slice(0, 6);
saveRecent(); renderWeek();
diff --git a/assets/js/track.js b/assets/js/track.js
new file mode 100644
index 0000000..ddfc17d
--- /dev/null
+++ b/assets/js/track.js
@@ -0,0 +1,51 @@
+/* Analytics helpers shared by every hub page.
+ - hubTrack(name, params): send a GA4 event when the tag is present.
+ - Links to the team's other sites on this domain (the web book, causal-studio, ...) open in the same tab,
+ carry utm_* so the other site's GA property can see the visit came from the hub, and send a click event. */
+(function () {
+ "use strict";
+ window.hubTrack = function (name, params) {
+ try { if (typeof window.gtag === "function") window.gtag("event", name, params || {}); } catch (e) {}
+ };
+
+ const HUB_PATHS = /^\/(?:$|index\.html$|article\.html$|404\.html$|assets\/|data\/|articles\/)/;
+ const WEBBOOK = "Causal-Inference-with-Python";
+
+ function teamSite(a) {
+ let u;
+ try { u = new URL(a.href, location.href); } catch (e) { return null; }
+ if (u.origin !== location.origin || HUB_PATHS.test(u.pathname)) return null;
+ const site = u.pathname.split("/")[1];
+ return site ? { url: u, site } : null;
+ }
+
+ function placement(a) {
+ const spots = [
+ ["#featured", "featured"], [".partner", "partner"], ["#station-dialog", "station"],
+ ["#shelf, #week, .spot", "curriculum"], [".reader, .prose", "article"], [".site-footer", "footer"], [".banner", "banner"]
+ ];
+ for (const [sel, name] of spots) if (a.closest(sel)) return name;
+ return "other";
+ }
+
+ function decorate(e) {
+ const a = e.target.closest && e.target.closest("a[href]");
+ if (!a) return;
+ const t = teamSite(a);
+ if (!t) return;
+ const where = placement(a);
+ const u = t.url;
+ if (!u.searchParams.has("utm_source")) {
+ u.searchParams.set("utm_source", "hub");
+ u.searchParams.set("utm_medium", "referral");
+ u.searchParams.set("utm_campaign", "crosslink");
+ u.searchParams.set("utm_content", where);
+ a.href = u.toString();
+ }
+ /* team sites are one family: open them in place, unless the reader asked for a new tab */
+ if (e.type === "click" && e.button === 0 && !e.metaKey && !e.ctrlKey && !e.shiftKey && !e.altKey) a.removeAttribute("target");
+ window.hubTrack(t.site === WEBBOOK ? "webbook_click" : "team_site_click", { site: t.site, placement: where, link_url: u.origin + u.pathname });
+ }
+ document.addEventListener("click", decorate, true);
+ document.addEventListener("auxclick", decorate, true);
+})();
diff --git a/index.html b/index.html
index 87c031c..53332aa 100644
--- a/index.html
+++ b/index.html
@@ -2,13 +2,22 @@
-
+
+
Causal Inference Hub | 가짜연구소 인과추론팀