PageSourceSearch

https://johnsteinmetz.net/contact

html johnsteinmetz.net collected 2026-10-06 13:08:30 UTC 11,134 bytes, 246 lines download raw bytes

1<!DOCTYPE html>
2<html lang="en">
3 <head>
4  
4<script>
5   (function () {
6  var proto = XMLHttpRequest.prototype;
7  var open = proto.open, setRequestHeader = proto.setRequestHeader, send = proto.send;
8  var TOKEN_KEY = 'base44_access_token';
9  var storedTokenHeader = function () {
10    try { return 'Bearer ' + localStorage.getItem(TOKEN_KEY); } catch (e) { return null; }
11  };
12  var forgetStoredToken = function () {
13    try { localStorage.removeItem(TOKEN_KEY); localStorage.removeItem('token'); } catch (e) {}
14  };
15
16  proto.open = function (method, url) {
17    this.__b44IsAuthCheck = /\/entities\/User\/me(\?|$)/.test(url);
18    return open.apply(this, arguments);
19  };
20
21  proto.setRequestHeader = function (name, value) {
22    if (this.__b44IsAuthCheck && name.toLowerCase() === 'authorization' && value === storedTokenHeader()) {
23      this.__b44SentToken = value;
24    }
25    return setRequestHeader.call(this, name, value);
26  };
27
28  proto.send = function () {
29    this.addEventListener('loadend', function () {
30      var rejectedTokenStillStored = this.status === 401 && this.__b44SentToken === storedTokenHeader();
31      if (rejectedTokenStillStored) forgetStoredToken();
32    });
33    return send.apply(this, arguments);
34  };
35})();
36  </script>
36
37  <meta charset="utf-8"/>
38  <link href="https://media.base44.com/images/public/6a5faa889e85ed8bcbd23f3a/1fc6d6d19_logo.png" rel="icon" type="image/svg+xml"/>
39  <meta content="width=device-width, initial-scale=1.0" name="viewport"/>
40  <link href="/manifest.json" rel="manifest"/>
41  <meta content="Contact on johnsteinmetz.net. johnsteinmetz." name="description"/>
42  <meta content="Fractional CDO, Chief Data Officer, Data Architecture, Data Governance, AI Implementation, Data Strategy, AI Readiness" name="keywords"/>
43  <meta content="index, follow, max-image-preview:large" name="robots"/>
44  <meta content="John Steinmetz" name="author"/>
45  <meta content="Contact | johnsteinmetz.net" property="og:title"/>
46  <meta content="Contact on johnsteinmetz.net. johnsteinmetz." property="og:description"/>
47  <meta content="website" property="og:type"/>
48  <meta content="johnsteinmetz.net" property="og:site_name"/>
49  <meta content="en_US" property="og:locale"/>
50  <meta content="https://media.base44.com/images/public/6a5faa889e85ed8bcbd23f3a/1fc6d6d19_logo.png/v1/fill/w_1200,h_630/1fc6d6d19_logo.png" property="og:image"/>
51  <meta content="1200" property="og:image:width"/>
52  <meta content="627" property="og:image:height"/>
53  <meta content="John Steinmetz — Fractional CDO, Architecting Intelligence" property="og:image:alt"/>
54  <meta content="summary_large_image" name="twitter:card"/>
55  <meta content="Contact | johnsteinmetz.net" name="twitter:title"/>
56  <meta content="Contact on johnsteinmetz.net. johnsteinmetz." name="twitter:description"/>
57  <meta content="https://media.base44.com/images/public/6a5faa889e85ed8bcbd23f3a/1fc6d6d19_logo.png/v1/fill/w_1200,h_630/1fc6d6d19_logo.png" name="twitter:image"/>
58  <meta content="@johnsteinmetz" name="twitter:site"/>
59  
59<script>
60   (function(){try{if(localStorage.getItem('theme')==='dark'){document.documentElement.classList.add('dark');}}catch(e){}})();
61  </script>
61
62  <title>
63   Contact | johnsteinmetz.net
64  </title>
65  <!-- Google tag (gtag.js) -->
vendor: 69 bytes, lines 65-66
65
66  <script async="" src="https://www.googletagmanager.com/gtag/js?id=
66G-PLPW4M49Y4
vendor: 17 bytes, lines 66-68
66">
67  </script>
68  
68<script>
69   
vendor: 145 bytes, lines 69-72
69window.dataLayer = window.dataLayer || [];
70      function gtag(){dataLayer.push(arguments);}
71      gtag('js', new Date());
72      gtag('config', '
72G-PLPW4M49Y4
vendor: 6 bytes, lines 72-73
72');
73  
73</script>
73
74  
74<script crossorigin="" src="/assets/index-BrdRdxaL.js" type="module">
75  </script>
75
76  <link crossorigin="" href="/assets/index-BfBupPKg.css" rel="stylesheet"/>
77  
77<script type="module">
78   if (window.self === window.top) {
79  let lastPath = "";
80  function getPageNameFromPath(path) {
81    const segments = path.split("/").filter(Boolean);
82    return segments[0] || null;
83  }
84  function trackPageView() {
85    const path = window.location.pathname;
86    if (path === lastPath) return;
87    lastPath = path;
88    const pageName = getPageNameFromPath(path) || "home";
89    const appId = "6a5faa889e85ed8bcbd23f3a";
90    if (!appId) return;
91    fetch(`/api/app-logs/${appId}/log-user-in-app/${pageName}`, {
92      method: "POST",
93    }).catch(() => {});
94  }
95  const originalPushState = history.pushState.bind(history);
96  history.pushState = function (...args) {
97    originalPushState(...args);
98    trackPageView();
99  };
100  const originalReplaceState = history.replaceState.bind(history);
101  history.replaceState = function (...args) {
102    originalReplaceState(...args);
103    trackPageView();
104  };
105  window.addEventListener("popstate", trackPageView);
106  trackPageView();
107}
108  </script>
108
109  <meta content="https://johnsteinmetz.net/contact" property="og:url"/>
110  <meta content="https://johnsteinmetz.net/contact" name="twitter:url"/>
111  
111<script>
112   (function () {
113  var titles = {"/login": "Login | johnsteinmetz.net", "/register": "Register | johnsteinmetz.net", "/forgot-password": "Forgot Password | johnsteinmetz.net", "/reset-password": "Reset Password | johnsteinmetz.net", "/unsubscribe": "Unsubscribe | johnsteinmetz.net", "/message-consent": "Message Consent | johnsteinmetz.net", "/verbal-opt-in": "Verbal Opt In | johnsteinmetz.net", "/": "johnsteinmetz.net", "/blog": "Blog | johnsteinmetz.net", "/about": "About | johnsteinmetz.net", "/contact": "Contact | johnsteinmetz.net", "/schedule": "Schedule | johnsteinmetz.net", "/home": "johnsteinmetz.net"};
114  // PascalCase boundaries -> "-", mirroring _PASCAL_BOUNDARY_RE without lookbehind
115  // (old Safari fails to PARSE a lookbehind, taking the whole script down).
116  var dashed = function (s) {
117    return s.replace(/([a-z0-9])([A-Z])/g, "$1-$2").replace(/([A-Z])([A-Z][a-z])/g, "$1-$2");
118  };
119  var decode = function (s) {
120    // A %2F inside ONE segment must not become a structural separator: /blog/foo%2Fbar is a
121    // dynamic slug, and letting it split would match a static /blog/foo/bar and write ITS title.
122    try { var d = decodeURIComponent(s); return d.indexOf("/") < 0 ? d : s; } catch (e) { return s; }
123  };
124  var segments = function (path) { return path.split("/").filter(Boolean).map(decode); };
125  var norm = function (path) {
126    var parts = segments(path).map(function (s) { return dashed(s).toLowerCase(); });
127    return parts.length ? "/" + parts.join("/") : "/";
128  };
129  var lastPath = location.pathname, written = document.title, on = true;
130  var sync = function () {
131    // Path only: a hash/query change is the same page, and the SDK strips its own auth
132    // params with replaceState -- neither must clobber a server-rendered title in place.
133    if (!on || location.pathname === lastPath) return;
134    lastPath = location.pathname;
135    if (document.title !== written) { on = false; return; }
136    // Write ONLY a title the map proves. An unmapped path is a dynamic route (/blog/:slug) the
137    // map cannot carry, and writing a placeholder there is destructive, not merely vague: the
138    // prerender worker runs no setUserAgent, so its capture browser reads as human and executes
139    // this script, and coordinator.js's settle wait explicitly tolerates a full navigation --
140    // a redirect landing on an unmapped path would bake the placeholder into the stored
141    // snapshot. Leaving the title alone degrades to today's behavior instead.
142    // Exact lookup only. A dash-stripped retry here would let a genuinely different /set-up
143    // claim /setup: the map carries a dash-free key only where a PascalCase boundary put the
144    // dashes there, and the client cannot tell the two apart. The map emits every reachable
145    // shape per segment instead (_client_url_variants).
146    var title = titles[norm(location.pathname)];
147    if (title === undefined) return;
148    document.title = title;
149    // Re-read: the setter stores verbatim but the getter strips and collapses whitespace, so
150    // keeping the assigned string would make the next sync() read our own write as the app's.
151    written = document.title;
152  };
153  var wrap = function (fn) {
154    // The history entry is already pushed by the time sync() runs, so a throw here would
155    // surface inside the host app's router and break navigation. This is injected into apps
156    // we cannot test, so it swallows rather than trusting the trace.
157    return function () {
158      var r = fn.apply(this, arguments);
159      try { sync(); } catch (e) {}
160      return r;
161    };
162  };
163  history.pushState = wrap(history.pushState);
164  history.replaceState = wrap(history.replaceState);
165  window.addEventListener("popstate", sync);
166})();
167  </script>
167
168  <meta content="yes" name="mobile-web-app-capable"/>
169  <meta content="black" name="apple-mobile-web-app-status-bar-style"/>
170  <meta content="johnsteinmetz.net" name="apple-mobile-web-app-title"/>
171  <link href="https://johnsteinmetz.net/contact" rel="canonical"/>
172  
172<script data-seo-source="builder" type="application/ld+json">
173   {"name": "johnsteinmetz.net", "@context": "https://schema.org", "@type": "WebSite", "url": "https://johnsteinmetz.com"}
174  </script>
174
175  
175<script data-seo-source="builder" type="application/ld+json">
176   {"name": "johnsteinmetz.net", "logo": "https://media.base44.com/images/public/6a5faa889e85ed8bcbd23f3a/1fc6d6d19_logo.png", "@context": "https://schema.org", "@type": "Organization", "url": "https://johnsteinmetz.com"}
177  </script>
177
178  
178<script data-seo-source="builder" type="application/ld+json">
179   {"@context": "https://schema.org", "@type": "BreadcrumbList", "itemListElement": [{"@type": "ListItem", "position": 1, "name": "Home", "item": "https://johnsteinmetz.net/"}, {"@type": "ListItem", "position": 2, "name": "Contact", "item": "https://johnsteinmetz.net/contact"}]}
180  </script>
180
181 </head>
182 <body>
183  <div id="root">
184   <div data-seo-source="builder" id="seo-snapshot" style="position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0;">
185    <main>
186     <header>
187      <h1>
188       Contact | johnsteinmetz.net
189      </h1>
190      <p>
191       Contact on johnsteinmetz.net. johnsteinmetz.
192      </p>
193     </header>
194     <nav aria-label="Pages">
195      <h2>
196       Pages
197      </h2>
198      <ul>
199       <li>
200        <a href="/">
201         Home
202        </a>
203       </li>
204       <li>
205        <a href="/login">
206         Login
207        </a>
208        — Login on johnsteinmetz.net. johnsteinmetz.
209       </li>
210       <li>
211        <a href="/register">
212         Register
213        </a>
214        — Register on johnsteinmetz.net. johnsteinmetz.
215       </li>
216       <li>
217        <a href="/blog">
218         Blog
219        </a>
220        — Blog on johnsteinmetz.net. johnsteinmetz.
221       </li>
222       <li>
223        <a href="/about">
224         About
225        </a>
226        — About on johnsteinmetz.net. johnsteinmetz.
227       </li>
228       <li>
229        <a href="/contact">
230         Contact
231        </a>
232        — Contact on johnsteinmetz.net. johnsteinmetz.
233       </li>
234       <li>
235        <a href="/schedule">
236         Schedule
237        </a>
238        — Schedule on johnsteinmetz.net. johnsteinmetz.
239       </li>
240      </ul>
241     </nav>
242    </main>
243   </div>
244  </div>
245 </body>
246</html>

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.