1<!doctype html> 2<html lang="en"> 3<head> 4<!-- Privacy-friendly analytics by Plausible -->
5<script async src="https://plausible.io/js/pa-QdPlluyoOE1jovqrkv-ed.js"></script>
vendor: 1 bytes, line 5
5
6<script> 7 window.plausible=window.plausible||function(){(plausible.q=plausible.q||[]).push(arguments)},plausible.init=plausible.init||function(i){plausible.o=i||{}}; 8 plausible.init() 9</script>
9 10<meta charset="utf-8"> 11<meta name="viewport" content="width=device-width,initial-scale=1"> 12<meta name="google-site-verification" content="-Vk03AgXINQMCfsTNJqtCnbcdC92p3WtyyDDOCnckAU" /> 13<title>Forge: Canonical Data Model for Industrial Equipment</title> 14<link rel="canonical" href="https://foundrynet.io/"> 15<meta name="description" content="985-field canonical data model for industrial telemetry. ISA-95 categorized. Deterministic resolution across <span data-fn="prod.oem_families">18</span> OEM families. Docker container."> 16<meta property="og:title" content="Forge: Canonical Data Model for Industrial Equipment"> 17<meta property="og:description" content="985-field canonical data model for industrial telemetry. ISA-95 categorized. Deterministic resolution across <span data-fn="prod.oem_families">18</span> OEM families. Docker container."> 18<link rel="preconnect" href="https://fonts.googleapis.com"> 19<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> 20<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600&family=JetBrains+Mono:wght@400;500;600&display=swap" rel="stylesheet">
21<script type="application/ld+json"> 22{ 23 "@context": "https://schema.org", 24 "@graph": [ 25 { 26 "@type": "TechArticle", 27 "@id": "https://foundrynet.io/#article", 28 "headline": "How to connect an AI agent to industrial equipment", 29 "name": "Foundry Labs: Connect your AI agent to industrial equipment", 30 "description": "The integration project kills 40% of industrial AI deployments. Forge collapses it into one MCP call: <span data-fn="prod.protocols">14</span> protocols, <span data-fn="prod.oem_families">18</span> manufacturer families and <span data-fn="prod.mappings">2,086</span> cross-OEM mappings normalize raw vendor telemetry into one canonical schema any AI agent can read.", 31 "url": "https://foundrynet.io/", 32 "mainEntityOfPage": {"@type": "WebPage", "@id": "https://foundrynet.io/"}, 33 "inLanguage": "en", 34 "dateModified": "2026-08-17", 35 "author": { 36 "@type": "Organization", 37 "name": "Foundry Labs LLC", 38 "url": "https://foundrynet.io", 39 "email": "[email protected]", 40 "logo": "https://foundrynet.io/logo-mark.png" 41 }, 42 "publisher": { 43 "@type": "Organization", 44 "name": "Foundry Labs LLC", 45 "url": "https://foundrynet.io", 46 "email": "[email protected]", 47 "logo": "https://foundrynet.io/logo-mark.png" 48 }, 49 "about": { 50 "@type": "SoftwareApplication", 51 "name": "Forge by Foundry Labs", 52 "url": "https://forge.foundrynet.io" 53 }, 54 "proficiencyLevel": "Expert", 55 "dependencies": "An MCP-compatible AI client (Claude, Cursor, LangChain, CrewAI, AutoGen) and a Forge API key. No new hardware, no PLC changes, read-only access to existing equipment.", 56 "keywords": "industrial AI infrastructure, MCP server for manufacturing, equipment canonical data model, cross-vendor equipment data, AI agent industrial equipment, Modbus OPC UA MTConnect, canonical schema manufacturing" 57 }, 58 { 59 "@type": "SoftwareApplication", 60 "@id": "https://forge.foundrynet.io#software", 61 "name": "Forge by Foundry Labs", 62 "url": "https://forge.foundrynet.io", 63 "applicationCategory": "BusinessApplication", 64 "applicationSubCategory": "Industrial AI Infrastructure", 65 "operatingSystem": "Cloud (HTTP API, MCP)", 66 "description": "The industrial AI infrastructure kernel. Forge resolves telemetry from any manufacturer into one canonical schema and exposes it to any AI agent over 32 MCP tools. 14 industrial protocols, <span data-fn="prod.oem_families">18</span> OEM families, <span data-fn="prod.mappings">2,086</span> cross-vendor mappings. Read-only by design.", 67 "softwareVersion": "3.4.4", 68 "featureList": [ 69 "32 MCP tools over Streamable HTTP", 70 "14 industrial protocols: MQTT, Modbus TCP, Modbus RTU, OPC UA, SNMP, BACnet, ROS2, J1939/CAN, MAVLink, MTConnect, EtherNet/IP, PROFINET, REST, CSV", 71 "<span data-fn="prod.mappings">2,086</span> cross-OEM tag mappings across <span data-fn="prod.oem_families">18</span> manufacturer families", 72 "Physics-validated normalization to a canonical schema", 73 "Failure prediction and plain-English root-cause diagnosis", 74 "OEE, shift reports and work-order tools", 75 "Guardrails, supervision and tamper-evident integrity records", 76 "Read-only: never sends commands to equipment", 77 "Free tier available" 78 ], 79 "offers": { 80 "@type": "Offer", 81 "price": "0", 82 "priceCurrency": "USD", 83 "description": "Free tier available. Paid plans metered from $0.30 per 1,000 normalized telemetry events, or a flat platform fee for production.", 84 "url": "https://forge.foundrynet.io/pricing" 85 }, 86 "provider": { 87 "@type": "Organization", 88 "name": "Foundry Labs LLC", 89 "url": "https://foundrynet.io", 90 "email": "[email protected]", 91 "logo": "https://foundrynet.io/logo-mark.png" 92 } 93 } 94 ] 95} 96</script>
vendor: 1 bytes, line 96
96
97<script type="application/ld+json"> 98{ 99 "@context": "https://schema.org", 100 "@type": "HowTo", 101 "@id": "https://foundrynet.io/#howto-connect-agent", 102 "name": "Connect an AI agent to industrial equipment with Forge", 103 "description": "Connect any MCP-compatible AI agent to any machine on the factory floor, from any manufacturer, over any of 14 industrial protocols. Forge resolves raw vendor telemetry into one canonical schema the agent can read, in a single command.", 104 "totalTime": "PT30S", 105 "estimatedCost": { 106 "@type": "MonetaryAmount", 107 "currency": "USD", 108 "value": "0" 109 }, 110 "supply": [ 111 {"@type": "HowToSupply", "name": "A Forge API key (free tier, no card required)"}, 112 {"@type": "HowToSupply", "name": "Equipment reachable over MQTT, Modbus TCP, Modbus RTU, OPC UA, SNMP, BACnet, ROS2, J1939/CAN, MAVLink, MTConnect, EtherNet/IP, PROFINET, REST or CSV"} 113 ], 114 "tool": [ 115 {"@type": "HowToTool", "name": "An MCP-compatible AI client such as Claude Code, Cursor, LangChain, CrewAI or AutoGen"} 116 ], 117 "step": [ 118 { 119 "@type": "HowToStep", 120 "position": 1, 121 "name": "Get a Forge API key", 122 "text": "Create an account at forge.foundrynet.io and copy your API key. The free tier needs no credit card and no sales call.", 123 "url": "https://forge.foundrynet.io/pricing" 124 }, 125 { 126 "@type": "HowToStep", 127 "position": 2, 128 "name": "Add the Forge MCP server to your agent", 129 "text": "Register the Forge MCP endpoint with your client over Streamable HTTP, passing your API key as a bearer token: claude mcp add --transport http forge https://mcp.foundrynet.io/mcp --header 'Authorization: Bearer YOUR_API_KEY'", 130 "url": "https://foundrynet.io/docs" 131 }, 132 { 133 "@type": "HowToStep", 134 "position": 3, 135 "name": "Normalize a reading from your equipment", 136 "text": "Call normalize_telemetry with a raw payload and the OEM name. Vendor tags such as Spindle_Speed or spd_rpm become the canonical field spindle_speed_rpm, returned with a coverage percentage and a confidence score. Works for any of the 18 supported OEM families.", 137 "url": "https://foundrynet.io/docs" 138 }, 139 { 140 "@type": "HowToStep", 141 "position": 4, 142 "name": "Predict, diagnose and govern", 143 "text": "Your agent now has 32 MCP tools: failure prediction, root-cause diagnosis, OEE, shift reports, work orders and guardrail pre-checks. Every reading and action is hash-chained and tamper-evident. Forge is read-only, so an agent can recommend but only your operator can act.", 144 "url": "https://foundrynet.io/docs" 145 } 146 ] 147} 148</script>
148 149<style> 150 :root { 151 --bg: #08080d; 152 --bg-elev: #0c0f17; 153 --bg-term: #000000; 154 --bg-code: #050609; 155 --rule: #16202e; 156 --rule-soft: #0f1722; 157 --fg: #d8dfeb; 158 --fg-mid: #8d99ad; 159 --fg-dim: #5a6678; 160 --jade: #1a9058; 161 --jade-soft: #2dbb78; 162 --jade-glow: rgba(45, 187, 120, 0.35); 163 --amber: #f0a030; 164 --amber-glow: rgba(240, 160, 48, 0.35); 165 --max: 1020px; 166 } 167 * { box-sizing: border-box; } 168 html, body { margin: 0; padding: 0; background: var(--bg); color: var(--fg); } 169 body { font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif; 170 font-size: 15px; line-height: 1.7; font-weight: 400; 171 -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; } 172 h1, h2, h3 { font-family: 'JetBrains Mono', ui-monospace, monospace; 173 font-weight: 500; margin: 0; letter-spacing: -0.01em; } 174 h1 { font-size: clamp(36px, 5.5vw, 60px); line-height: 1.05; 175 color: var(--fg); font-weight: 600; letter-spacing: -0.025em; } 176 h2 { font-size: clamp(22px, 2.6vw, 30px); color: var(--fg); 177 margin-bottom: 18px; font-weight: 500; letter-spacing: -0.01em; } 178 h3 { font-size: 11px; color: var(--jade-soft); margin-bottom: 10px; 179 font-weight: 500; letter-spacing: 0.18em; text-transform: uppercase; } 180 p { margin: 0 0 14px; color: var(--fg); } 181 a { color: var(--jade-soft); text-decoration: none; 182 transition: color .15s ease; } 183 a:hover { color: var(--amber); } 184 ul, ol { color: var(--fg-mid); padding-left: 20px; line-height: 1.85; } 185 li { margin: 4px 0; } 186 code { font-family: 'JetBrains Mono', monospace; font-size: 0.92em; 187 color: var(--amber); background: transparent; } 188 189 section { padding: 90px 28px; max-width: var(--max); margin: 0 auto; } 190 section.first { padding-top: 130px; } 191 192 .label { font-family: 'JetBrains Mono', monospace; font-size: 11px; 193 color: var(--fg-dim); font-weight: 400; letter-spacing: 0.22em; 194 text-transform: uppercase; } 195 196 /* HERO ---------------------------------------------------------- */ 197 .hero p.tagline { font-family: 'JetBrains Mono', ui-monospace, monospace; 198 font-size: clamp(19px, 2.8vw, 28px); line-height: 1.28; 199 color: var(--fg); opacity: 0.72; font-weight: 400; 200 letter-spacing: -0.015em; margin: 18px 0 0; max-width: 680px; } 201 .hero p.sub { font-size: 22px; color: var(--fg-mid); margin: 22px 0 32px; 202 max-width: 640px; font-weight: 300; line-height: 1.5; } 203 .proof { color: var(--jade-soft); font-size: 12px; letter-spacing: 0.18em; 204 text-transform: uppercase; margin-bottom: 44px; font-weight: 400; 205 font-family: 'JetBrains Mono', monospace; } 206 .proof .dot { color: var(--rule); margin: 0 12px; } 207 .ctas { display: flex; flex-wrap: wrap; gap: 14px; margin-bottom: 64px; } 208 .btn { display: inline-block; padding: 13px 24px; border-radius: 4px; 209 font-family: 'JetBrains Mono', monospace; font-size: 13px; 210 font-weight: 500; letter-spacing: 0.02em; 211 transition: all .18s ease; cursor: pointer; } 212 .btn.primary { background: var(--jade); color: #050609; 213 box-shadow: 0 0 0 1px var(--jade), 0 4px 24px rgba(26, 144, 88, 0.18); } 214 .btn.primary:hover { background: var(--jade-soft); color: #050609; 215 box-shadow: 0 0 0 1px var(--jade-soft), 0 6px 32px rgba(45, 187, 120, 0.32); } 216 .btn.secondary { background: transparent; color: var(--fg); 217 border: 1px solid var(--rule); } 218 .btn.secondary:hover { border-color: var(--jade-soft); color: var(--jade-soft); } 219 220 /* SIGNUP PANEL --------------------------------------------------- */ 221 .signup { display: none; margin: 0 0 64px; padding: 24px; 222 border: 1px solid var(--rule); border-left: 2px solid var(--jade); 223 background: var(--bg-elev); border-radius: 4px; 224 max-width: 640px; } 225 .signup.open { display: block; } 226 .signup .row { display: flex; flex-wrap: wrap; gap: 10px; 227 margin-top: 8px; } 228 .signup label { font-family: 'JetBrains Mono', monospace; font-size: 11px; 229 color: var(--fg-dim); letter-spacing: 0.18em; 230 text-transform: uppercase; } 231 .signup input[type=email] { flex: 1 1 280px; padding: 12px 14px; 232 background: var(--bg-code); color: var(--fg); 233 font-family: 'JetBrains Mono', monospace; 234 font-size: 13px; border: 1px solid var(--rule); 235 border-radius: 4px; outline: none; 236 transition: border-color .15s; } 237 .signup input[type=email]:focus { border-color: var(--jade-soft); } 238 .signup .help { color: var(--fg-dim); font-size: 12px; margin-top: 10px; } 239 .signup-options { display: grid; grid-template-columns: 1fr 1fr; 240 gap: 14px; } 241 .signup-options > div { background: var(--bg-code); 242 border: 1px solid var(--rule); border-radius: 5px; 243 padding: 18px; display: flex; flex-direction: column; 244 gap: 10px; } 245 .signup-options .free-option { border-left: 2px solid var(--jade); } 246 .signup-options .paid-option { border-left: 2px solid var(--amber); } 247 .signup-options h3 { font-family: 'JetBrains Mono', monospace; 248 font-size: 13px; color: var(--fg); 249 margin: 0; font-weight: 500; letter-spacing: 0.02em; } 250 .signup-options p { color: var(--fg-mid); font-size: 12.5px; 251 margin: 0 0 4px; line-height: 1.5; } 252 .signup-options input[type=email] { width: 100%; padding: 10px 12px; 253 background: var(--bg); color: var(--fg); 254 border: 1px solid var(--rule); 255 border-radius: 4px; 256 font-family: 'JetBrains Mono', monospace; 257 font-size: 12px; outline: none; } 258 .signup-options input[type=email]:focus { border-color: var(--jade-soft); } 259 .signup-options button { width: 100%; } 260 @media (max-width: 600px) { .signup-options { grid-template-columns: 1fr; } } 261 .signup .err { color: var(--amber); font-size: 12px; margin-top: 10px; 262 display: none; font-family: 'JetBrains Mono', monospace; } 263 .signup .err.show { display: block; } 264 .signup .keybox { margin-top: 10px; padding: 14px; background: var(--bg-code); 265 border: 1px dashed var(--jade); border-radius: 4px; 266 color: var(--amber); font-family: 'JetBrains Mono', monospace; 267 font-size: 13px; word-break: break-all; line-height: 1.5; } 268 .signup .copy { margin-top: 10px; } 269 .signup .ok-line { color: var(--jade-soft); font-family: 'JetBrains Mono', monospace; 270 font-size: 12px; letter-spacing: 0.06em; margin-top: 14px; } 271 .signup h3 { margin-bottom: 8px; } 272 273 /* TERMINAL MOCKUP ----------------------------------------------- */ 274 .term { background: var(--bg-term); border: 1px solid var(--rule); 275 border-radius: 6px; padding: 26px 28px; 276 font-family: 'JetBrains Mono', monospace; font-size: 13.5px; 277 line-height: 1.95; max-width: 740px; 278 box-shadow: 0 24px 80px rgba(0,0,0,.6), 279 0 0 0 1px rgba(45, 187, 120, 0.04); } 280 .term .you, .term .feed { color: var(--jade-soft); 281 text-shadow: 0 0 10px var(--jade-glow); } 282 .term .you::before, .term .feed::before { content: "> "; color: var(--jade); 283 text-shadow: 0 0 10px var(--jade-glow); } 284 .term .ok { color: var(--jade-soft); opacity: 0.95; } 285 .term .ok::before { content: "â "; color: var(--jade); } 286 .term .fire { color: var(--amber); font-weight: 500; 287 text-shadow: 0 0 12px var(--amber-glow); } 288 .term .fire::before { content: "ð¥ "; } 289 .term .gap { display: block; height: 12px; } 290 291 /* BEYOND MACHINES (operations pitch) ---------------------------- */ 292 .beyond { display: grid; grid-template-columns: 1fr 1fr; gap: 36px; 293 margin-top: 40px; } 294 .beyond .col h3 { font-family: 'JetBrains Mono', monospace; 295 color: var(--jade-soft); font-size: 12px; 296 font-weight: 500; letter-spacing: 0.18em; 297 text-transform: uppercase; margin-bottom: 14px; } 298 .beyond .col p { color: var(--fg-mid); font-size: 14.5px; 299 line-height: 1.65; margin: 0 0 18px; 300 font-weight: 400; } 301 .term.compact { padding: 18px 20px; font-size: 12.5px; 302 line-height: 1.85; max-width: none; box-shadow: none; 303 border: 1px solid var(--rule); } 304 .beyond-foot { margin-top: 32px; color: var(--fg-mid); 305 font-size: 14px; max-width: 720px; 306 font-style: normal; } 307 .beyond-foot strong { color: var(--fg); font-weight: 500; } 308 .beyond-sub { color: var(--fg-mid); font-size: 16px; line-height: 1.55; 309 margin: 6px 0 0; max-width: 760px; font-weight: 300; } 310 @media (max-width: 720px) { 311 .beyond { grid-template-columns: 1fr; } 312 } 313 314 /* HOW IT WORKS -------------------------------------------------- */ 315 .grid3 { display: grid; gap: 36px; 316 grid-template-columns: repeat(3, 1fr); margin-top: 40px; } 317 .col h3 { display: flex; align-items: baseline; gap: 12px; } 318 .col h3 .num { color: var(--amber); font-size: 12px; letter-spacing: 0.1em; } 319 .col p { color: var(--fg-mid); font-size: 14.5px; line-height: 1.65; 320 margin: 0; font-weight: 400; } 321 322 /* CONNECTIONS --------------------------------------------------- */ 323 .pillrow { display: flex; flex-wrap: wrap; align-items: center; 324 gap: 8px 8px; margin-bottom: 22px; } 325 .pillrow .label { width: 100%; margin-bottom: 8px; } 326 .pill { font-family: 'JetBrains Mono', monospace; font-size: 12.5px; 327 color: var(--fg-mid); background: transparent; 328 border: 1px solid var(--rule); border-radius: 3px; 329 padding: 7px 13px; font-weight: 400; 330 transition: all .15s ease; } 331 .pill:hover { color: var(--jade-soft); border-color: rgba(45,187,120,0.35); } 332 .footnote { color: var(--fg-dim); font-size: 13px; line-height: 1.7; 333 margin-top: 24px; max-width: 720px; font-weight: 400; } 334 335 /* PRICING (outlined cards) -------------------------------------- */ 336 .grid4 { display: grid; gap: 14px; 337 grid-template-columns: repeat(4, 1fr); margin: 32px 0 28px; } 338 .card { background: transparent; 339 border: 1px solid var(--rule); 340 border-top: 1px solid rgba(26, 144, 88, 0.45); 341 border-radius: 4px; padding: 26px 22px; 342 transition: border-color .2s ease, transform .2s ease; } 343 .card:hover { transform: translateY(-2px); border-top-color: var(--jade-soft); } 344 .card .name { color: var(--fg-mid); font-size: 11px; font-weight: 400; 345 letter-spacing: 0.18em; text-transform: uppercase; 346 margin-bottom: 14px; font-family: 'JetBrains Mono', monospace; } 347 .card .price { color: var(--fg); font-size: 28px; font-weight: 500; 348 margin-bottom: 12px; font-family: 'JetBrains Mono', monospace; 349 letter-spacing: -0.02em; } 350 .card .price .unit { color: var(--fg-dim); font-size: 13px; font-weight: 400; 351 letter-spacing: 0; margin-left: 2px; } 352 .card .desc { color: var(--fg-mid); font-size: 13px; line-height: 1.55; 353 font-weight: 400; } 354 .free-line { color: var(--fg-mid); font-size: 13px; line-height: 1.85; 355 font-weight: 400; max-width: 680px; margin-top: 22px; } 356 .free-line strong { color: var(--fg); font-family: 'JetBrains Mono', monospace; 357 font-weight: 500; } 358 359 /* PRICING TIERS ------------------------------------------------- */ 360 .tier-grid { display: grid; gap: 16px; margin: 32px 0 12px; 361 grid-template-columns: repeat(3, 1fr); } 362 .tier { background: var(--bg-elev); border: 1px solid var(--rule); 363 border-top: 1px solid var(--rule); 364 border-radius: 5px; padding: 26px 24px; 365 display: flex; flex-direction: column; gap: 16px; 366 transition: border-color .2s ease, transform .2s ease; } 367 .tier:hover { transform: translateY(-2px); } 368 .tier-starter { border-top: 2px solid var(--jade); 369 box-shadow: 0 0 0 1px rgba(26,144,88,.18); } 370 .tier-growth { border-top: 1px solid rgba(45,187,120,.45); } 371 .tier-enterprise{ border-top: 1px solid rgba(240,160,48,.45); } 372 .tier-head { display: flex; justify-content: space-between; align-items: baseline; } 373 .tier .name { font-family: 'JetBrains Mono', monospace; font-size: 11px; 374 color: var(--jade-soft); letter-spacing: 0.22em; 375 text-transform: uppercase; font-weight: 500; } 376 .tier-enterprise .name { color: var(--amber); } 377 .tier .badge { font-family: 'JetBrains Mono', monospace; font-size: 10px; 378 color: var(--fg-dim); letter-spacing: 0.06em; } 379 .tier .badge.recommended { color: var(--jade-soft); } 380 .tier .price-list { display: flex; flex-direction: column; gap: 6px; } 381 .tier .line { display: flex; justify-content: space-between; align-items: baseline; 382 font-family: 'JetBrains Mono', monospace; font-size: 13px; 383 color: var(--fg-mid); padding: 4px 0; 384 border-bottom: 1px dashed var(--rule-soft); } 385 .tier .line:last-child { border-bottom: none; } 386 .tier .line .what { color: var(--fg-mid); } 387 .tier .line .amt { color: var(--fg); font-weight: 500; } 388 .tier-growth .line .amt, 389 .tier-enterprise .line .amt { color: var(--jade-soft); }
390 .tier .desc { color: var(--fg-mid); font-size: 13px; line-height: 1.55; 391 margin: 0; } 392 .tier input[type=email] { width: 100%; padding: 10px 12px; 393 background: var(--bg-code); color: var(--fg); 394 border: 1px solid var(--rule); border-radius: 4px; 395 font-family: 'JetBrains Mono', monospace; 396 font-size: 12px; outline: none; box-sizing: border-box; } 397 .tier input[type=email]:focus { border-color: var(--jade-soft); } 398 .tier .cta { display: inline-block; padding: 11px 16px; border-radius: 4px; 399 font-family: 'JetBrains Mono', monospace; font-size: 12px; 400 font-weight: 500; letter-spacing: 0.04em; text-align: center; 401 transition: all .15s ease; cursor: pointer; border: none; } 402 .tier-starter .cta { background: var(--jade); color: #050609; 403 box-shadow: 0 0 0 1px var(--jade), 0 4px 18px rgba(26,144,88,.18); } 404 .tier-starter .cta:hover { background: var(--jade-soft); color: #050609; } 405 .tier-growth .cta, .tier-enterprise .cta { 406 background: transparent; color: var(--fg); border: 1px solid var(--rule); } 407 .tier-growth .cta:hover, .tier-enterprise .cta:hover { 408 border-color: var(--jade-soft); color: var(--jade-soft); } 409 .tier .hint { color: var(--fg-dim); font-size: 11px; margin: 0; 410 font-family: 'JetBrains Mono', monospace; letter-spacing: 0.04em; } 411 @media (max-width: 760px) { .tier-grid { grid-template-columns: 1fr; } } 412 413 /* INTEGRATORS --------------------------------------------------- */ 414 .integrators p { color: var(--fg-mid); max-width: 700px; 415 margin-bottom: 28px; font-size: 15.5px; font-weight: 400; 416 line-height: 1.7; } 417 .small-note { color: var(--fg-dim); font-size: 12.5px; 418 margin-top: 18px; font-style: italic; font-weight: 400; } 419 420 /* MCP / CODE ---------------------------------------------------- */ 421 pre.code, pre { background: var(--bg-term); border: 1px solid var(--rule); 422 border-radius: 6px; padding: 18px 22px; 423 font-family: 'JetBrains Mono', monospace; font-size: 12.5px; 424 color: var(--fg); overflow-x: auto; line-height: 1.55; 425 margin: 12px 0; max-width: 740px; 426 box-shadow: 0 16px 48px rgba(0,0,0,.4); } 427 pre code { color: var(--fg); font-size: inherit; } 428 pre.code .key { color: var(--amber); } 429 pre.code .str { color: var(--jade-soft); } 430 .mcp-summary { color: var(--fg-mid); font-size: 14px; margin-bottom: 22px; 431 font-weight: 400; } 432 .mcp-links a { margin-right: 24px; font-family: 'JetBrains Mono', monospace; 433 font-size: 13px; } 434 435 /* DOCS SECTION (nested below landing) --------------------------- */ 436 .docs-divider { border: 0; border-top: 1px solid var(--rule); 437 max-width: var(--max); margin: 60px auto 0; } 438 .docs-intro { color: var(--fg-mid); font-size: 14.5px; 439 margin-bottom: 32px; max-width: 720px; } 440 .docs h2 { margin-top: 56px; margin-bottom: 14px; } 441 .docs h2:first-of-type { margin-top: 0; } 442 .docs h3 { color: var(--jade-soft); margin-top: 30px; margin-bottom: 8px; } 443 .docs p { color: var(--fg-mid); font-size: 14px; line-height: 1.7; } 444 .docs ol, .docs ul { color: var(--fg-mid); font-size: 14px; } 445 446 /* LEGAL SECTION (TOS + Privacy, anchored at #terms / #privacy) -- */ 447 .legal h1 { font-family: 'JetBrains Mono', monospace; 448 color: var(--jade-soft); font-size: 24px; 449 font-weight: 500; margin: 0 0 8px; 450 letter-spacing: -0.01em; } 451 .legal h2 { color: var(--amber); font-size: 16px; 452 margin: 40px 0 12px; font-family: 'JetBrains Mono', monospace; } 453 .legal h2:first-of-type { margin-top: 24px; } 454 .legal h3 { color: var(--fg); font-size: 14px; 455 margin: 24px 0 8px; letter-spacing: 0; 456 text-transform: none; font-family: 'JetBrains Mono', monospace; } 457 .legal p { color: var(--fg-mid); font-size: 14px; line-height: 1.8; 458 margin: 0 0 14px; } 459 .legal .updated { color: var(--fg-dim); font-size: 12px; 460 margin-bottom: 32px; 461 font-family: 'JetBrains Mono', monospace; 462 letter-spacing: 0.06em; } 463 .legal hr { border: none; border-top: 1px solid var(--rule); 464 margin: 48px 0; } 465 .endpoint { border-top: 1px solid var(--rule); padding-top: 20px; 466 margin-top: 26px; } 467 .endpoint:first-of-type { margin-top: 18px; } 468 .endpoint .method { display: inline-block; color: var(--bg); 469 background: var(--jade); padding: 1px 9px; 470 border-radius: 3px; font-weight: 600; font-size: 11px; 471 vertical-align: 2px; font-family: 'JetBrains Mono', monospace; 472 letter-spacing: 0.05em; } 473 .endpoint .method.get { background: var(--amber); } 474 .endpoint .path { color: var(--fg); font-size: 14px; } 475 .endpoint .desc { color: var(--fg-mid); margin: 6px 0 12px; 476 font-size: 14px; line-height: 1.65; } 477 478 /* FOOTER (single line) ----------------------------------------- */ 479 footer { border-top: 1px solid var(--rule); padding: 28px; 480 max-width: var(--max); margin: 80px auto 0; 481 color: var(--fg-dim); font-size: 12px; text-align: center; 482 font-family: 'JetBrains Mono', monospace; font-weight: 400; 483 letter-spacing: 0.02em; line-height: 1.8; } 484 footer .sep { color: var(--rule); margin: 0 12px; } 485 footer a { color: var(--fg-mid); } 486 footer a:hover { color: var(--jade-soft); } 487 488 /* RESPONSIVE --------------------------------------------------- */ 489 @media (max-width: 760px) { 490 section { padding: 70px 22px; } 491 section.first { padding-top: 90px; } 492 .grid3 { grid-template-columns: 1fr; gap: 32px; } 493 .grid4 { grid-template-columns: 1fr 1fr; } 494 .term { font-size: 12px; padding: 20px; } 495 .hero p.tagline { font-size: 17px; margin-top: 14px; } 496 .hero p.sub { font-size: 18px; } 497 footer { font-size: 11px; } 498 footer .sep { margin: 0 8px; } 499 } 500 @media (max-width: 460px) { 501 .grid4 { grid-template-columns: 1fr; } 502 footer .sep.collapse { display: block; visibility: hidden; height: 4px; } 503 } 504 /* FN-NAV-CSS-OPEN */ 505 506 /* TOP NAV âââââââââââââââââââââââââââââââââââââââââââââââ */ 507 .topnav { position:sticky; top:0; z-index:8000; 508 background:rgba(8,8,13,0.92); backdrop-filter:blur(10px); 509 -webkit-backdrop-filter:blur(10px); 510 border-bottom:1px solid var(--rule-soft); 511 padding:12px 28px; display:flex; align-items:center; 512 gap:32px; } 513 .topnav-brand { font-family:'JetBrains Mono',monospace; 514 font-size:13px; font-weight:600; letter-spacing:0.02em; 515 color:var(--fg); text-decoration:none; } 516 .topnav-brand:hover { color:var(--jade-soft); } 517 .topnav-dropdown { position:relative; } 518 .topnav-dropdown summary { font-family:'JetBrains Mono',monospace; 519 font-size:12px; color:var(--fg-mid); 520 letter-spacing:0.04em; cursor:pointer; 521 list-style:none; padding:6px 10px; 522 border-radius:3px; user-select:none; 523 transition:color .15s ease; } 524 .topnav-dropdown summary::-webkit-details-marker { display:none; } 525 .topnav-dropdown summary:hover { color:var(--jade-soft); } 526 .topnav-dropdown summary::after { content:" â¾"; font-size:9px; 527 color:var(--fg-dim); margin-left:3px; } 528 .topnav-dropdown[open] summary { color:var(--jade-soft); } 529 .topnav-menu { position:absolute; top:calc(100% + 6px); left:0; 530 min-width:190px; background:var(--bg-elev); 531 border:1px solid var(--rule); border-radius:4px; 532 padding:6px; box-shadow:0 8px 24px rgba(0,0,0,0.45); 533 display:flex; flex-direction:column; gap:2px; } 534 .topnav-menu a { font-family:'JetBrains Mono',monospace; font-size:12px; 535 color:var(--fg-mid); padding:8px 12px; 536 text-decoration:none; border-radius:3px; 537 transition:all .12s ease; } 538 .topnav-menu a:hover { background:var(--bg-code); color:var(--jade-soft); } 539 .topnav-menu a.active { color:var(--jade-soft); 540 border-left:2px solid var(--jade-soft); padding-left:10px; } 541 .topnav-spacer { flex:1; } 542 .topnav-cta { font-family:'JetBrains Mono',monospace; font-size:12px; 543 color:var(--jade-soft); text-decoration:none; 544 letter-spacing:0.04em; padding:6px 10px; 545 transition:color .15s ease; } 546 .topnav-cta:hover { color:var(--amber); } 547 @media (max-width:640px) { 548 .topnav { padding:10px 14px; gap:8px 12px; flex-wrap:wrap; } 549 .topnav-spacer { flex-basis:100%; height:0; } 550 .topnav-cta { display:inline-block; font-size:12px; padding:4px 7px; } 551 } 552 553 /* FN-NAV-CSS-CLOSE */ 554 555 /* per-machine cost calculator (added 2026-05-29) */ 556 .price-calculator { 557 background: var(--bg-elev, #0c0f17); 558 border: 1px solid var(--rule, #16202e); 559 border-radius: 8px;
560 padding: 22px 26px; 561 margin: 0 auto 28px; 562 max-width: 760px; 563 } 564 .price-calculator .calc-intro { 565 margin: 0 0 14px; 566 font-size: 13px; 567 color: var(--fg-mid, #8d99ad); 568 letter-spacing: .04em; 569 text-transform: uppercase; 570 } 571 .price-calculator .calc-grid { 572 display: grid; 573 grid-template-columns: repeat(3, 1fr); 574 gap: 14px; 575 } 576 .price-calculator .calc-cell { 577 display: flex; 578 flex-direction: column; 579 gap: 4px; 580 padding: 14px 16px; 581 background: var(--bg, #08080d); 582 border: 1px solid var(--rule, #16202e); 583 border-radius: 6px; 584 } 585 .price-calculator .calc-cell .cad { 586 font-size: 11.5px; 587 color: var(--fg-dim, #5a6678); 588 letter-spacing: .08em; 589 text-transform: uppercase; 590 } 591 .price-calculator .calc-cell .est { 592 font-size: 18px; 593 color: var(--fg, #d8dfeb); 594 font-weight: 500; 595 } 596 .price-calculator .calc-foot { 597 margin: 14px 0 0; 598 font-size: 12px; 599 color: var(--fg-dim, #5a6678); 600 } 601 @media (max-width: 640px) { 602 .price-calculator .calc-grid { grid-template-columns: 1fr; } 603 } 604 605 /* ââ Phase 1 additions: sandbox / coverage / per-asset / about âââââââ */ 606 607 /* SANDBOX SECTION ----------------------------------------------- */ 608 .sandbox-section .sandbox-lede { 609 max-width: 720px; color: var(--fg-mid); font-size: 15px; 610 margin: 4px 0 28px; line-height: 1.65; 611 } 612 .sandbox-steps { list-style: none; padding: 0; margin: 0; 613 display: flex; flex-direction: column; gap: 18px; } 614 .sandbox-steps > li { display: flex; gap: 18px; 615 background: var(--bg-elev); 616 border: 1px solid var(--rule); 617 border-left: 2px solid var(--jade); 618 border-radius: 5px; padding: 22px 24px; } 619 .sandbox-steps .step-num { font-family: 'JetBrains Mono', monospace; 620 font-size: 13px; color: var(--jade-soft); 621 letter-spacing: 0.18em; padding-top: 2px; 622 min-width: 28px; } 623 .sandbox-steps .step-body { flex: 1; min-width: 0; } 624 .sandbox-steps h3 { font-family: 'JetBrains Mono', monospace; 625 font-size: 13px; color: var(--fg); 626 letter-spacing: 0.02em; text-transform: none; 627 margin: 0 0 6px; font-weight: 500; } 628 .sandbox-steps p { color: var(--fg-mid); margin: 0 0 12px; 629 font-size: 14px; line-height: 1.6; } 630 .sandbox-resp { margin-top: 12px; font-size: 12.5px; } 631 .sandbox-foot { margin-top: 22px; color: var(--fg-dim); 632 font-size: 12.5px; max-width: 720px; line-height: 1.65; } 633 634 /* COVERAGE SECTION ---------------------------------------------- */ 635 .coverage-section .coverage-lede { 636 max-width: 720px; color: var(--fg-mid); font-size: 14px; 637 margin: 4px 0 26px; line-height: 1.65; 638 } 639 .oem-grid { display: grid; grid-template-columns: repeat(4, 1fr); 640 gap: 10px; margin: 0 0 22px; } 641 .oem { display: flex; flex-direction: column; gap: 6px; 642 background: var(--bg-elev); border: 1px solid var(--rule); 643 border-radius: 4px; padding: 14px 16px; } 644 .oem-name { font-family: 'JetBrains Mono', monospace; 645 font-size: 13px; color: var(--fg); font-weight: 500; 646 letter-spacing: 0.01em; } 647 .oem-tier { font-family: 'JetBrains Mono', monospace; 648 font-size: 10.5px; letter-spacing: 0.12em; 649 text-transform: uppercase; font-weight: 500; } 650 .oem-tier.prod { color: var(--jade-soft); } 651 .oem-tier.beta { color: var(--amber); } 652 .oem-tier.early { color: var(--fg-dim); } 653 .coverage-foot { color: var(--fg-mid); font-size: 12.5px; 654 max-width: 720px; line-height: 1.65; 655 border-top: 1px dashed var(--rule); padding-top: 16px; } 656 @media (max-width: 760px) { .oem-grid { grid-template-columns: repeat(2, 1fr); } } 657 658 /* PRICING (Phase 1, per-asset headline) ------------------------ */ 659 .pricing-lede { max-width: 720px; color: var(--fg-mid); 660 font-size: 14px; margin: 4px 0 26px; line-height: 1.65; } 661 .pricing-tier-grid { grid-template-columns: 1fr 1.15fr 1fr; } 662 .tier-free { border-top: 1px solid var(--rule); } 663 .tier-perasset { border-top: 2px solid var(--jade); 664 box-shadow: 0 0 0 1px rgba(26,144,88,.22), 665 0 8px 32px rgba(26,144,88,.10); } 666 .tier .price-display { display: flex; align-items: baseline; gap: 6px; 667 font-family: 'JetBrains Mono', monospace; } 668 .tier .price-display .amount { font-size: 32px; color: var(--fg); 669 font-weight: 500; letter-spacing: -0.02em; } 670 .tier .price-display .unit { font-size: 12px; color: var(--fg-dim); 671 letter-spacing: 0; } 672 .tier-features { list-style: none; padding: 0; margin: 6px 0 4px; 673 display: flex; flex-direction: column; gap: 6px; } 674 .tier-features li { color: var(--fg-mid); font-size: 13px; 675 line-height: 1.5; padding: 4px 0; 676 border-bottom: 1px dashed var(--rule-soft); } 677 .tier-features li:last-child { border-bottom: none; } 678 .tier-features strong { color: var(--fg); font-weight: 500; } 679 .tier-features code { font-size: 12.5px; } 680 .gate-tag { font-family: 'JetBrains Mono', monospace; font-size: 9.5px; 681 letter-spacing: 0.14em; text-transform: uppercase; 682 color: var(--amber); border: 1px solid var(--amber); 683 padding: 1px 6px; border-radius: 3px; margin-left: 4px; } 684 .tier-perasset .cta { background: var(--jade); color: #050609; 685 box-shadow: 0 0 0 1px var(--jade), 686 0 4px 18px rgba(26,144,88,.20); } 687 .tier-perasset .cta:hover { background: var(--jade-soft); } 688 .onramp { display: flex; flex-direction: column; gap: 6px; 689 margin: 24px 0 4px; padding: 14px 18px; 690 background: var(--bg-code); border: 1px solid var(--rule); 691 border-radius: 4px; } 692 .onramp-label { font-family: 'JetBrains Mono', monospace; font-size: 11px; 693 color: var(--fg-mid); letter-spacing: 0.18em; 694 text-transform: uppercase; } 695 .onramp-rates { font-family: 'JetBrains Mono', monospace; font-size: 12.5px; 696 color: var(--fg); } 697 .onramp-rates code { color: var(--jade-soft); } 698 .onramp-foot { color: var(--fg-dim); font-size: 12px; line-height: 1.55; } 699 @media (max-width: 900px) { .pricing-tier-grid { grid-template-columns: 1fr; } } 700 701 /* ABOUT + SECURITY ---------------------------------------------- */ 702 .about-section .about-grid { display: grid; 703 grid-template-columns: 1fr 1fr; gap: 28px; } 704 .about-section h3 { font-family: 'JetBrains Mono', monospace; 705 font-size: 11px; color: var(--jade-soft); 706 letter-spacing: 0.18em; text-transform: uppercase; 707 font-weight: 500; margin-bottom: 14px; } 708 .about-section .about-col p { color: var(--fg-mid); font-size: 14px; 709 line-height: 1.65; } 710 .about-section .muted { color: var(--fg-dim); font-size: 13px; } 711 .security-list { list-style: none; padding: 0; margin: 0 0 12px; 712 display: flex; flex-direction: column; gap: 6px; } 713 .security-list li { color: var(--fg-mid); font-size: 13.5px; 714 line-height: 1.55; padding: 4px 0; 715 border-bottom: 1px dashed var(--rule-soft); } 716 .security-list li:last-child { border-bottom: none; } 717 .security-list strong { color: var(--fg); font-weight: 500; } 718 @media (max-width: 760px) { .about-section .about-grid { grid-template-columns: 1fr; } } 719 720 /* hide the now-superseded calculator (left in DOM for nobody, replaced) */ 721 .price-calculator { display: none; } 722 723 /* 4-DOOR HUB ----------------------------------------------------- */ 724 .hub { display:grid; gap:16px; grid-template-columns:1fr 1fr; margin-top:30px; } 725 .product-card { border:1px solid var(--rule); border-top:2px solid var(--jade); 726 border-radius:6px; padding:22px 22px 18px; background:var(--bg-elev); 727 display:flex; flex-direction:column; } 728 .product-card h4 { font-family:'JetBrains Mono',monospace; font-size:16px; 729 font-weight:600; color:var(--fg); margin:0 0 8px; letter-spacing:-.01em; } 730 .product-card p { color:var(--fg-mid); font-size:13.5px; line-height:1.55; margin:0 0 14px; flex:1; } 731 .product-card a { font-family:'JetBrains Mono',monospace; font-size:12px; letter-spacing:.02em; } 732 @media (max-width:640px){ .hub { grid-template-columns:1fr; } } 733 734 /* STAT BAR (verified sources) ----------------------------------- */ 735 .statbar { border:1px solid var(--rule); border-radius:6px; 736 background:var(--bg-elev); padding:26px 28px; margin-top:8px; } 737 .statbar .stat-grid { display:grid; grid-template-columns:repeat(3,1fr); gap:24px; } 738 .statbar .stat .n { font-family:'JetBrains Mono',monospace; font-size:30px; 739 color:var(--fg); font-weight:600; letter-spacing:-0.02em; } 740 .statbar .stat .t { color:var(--fg-mid); font-size:13px; line-height:1.5; margin-top:6px; } 741 .statbar .stat a { font-size:11px; color:var(--fg-dim); letter-spacing:0.04em; 742 border-bottom:1px dotted var(--rule); } 743 .statbar .stat a:hover { color:var(--jade-soft); border-bottom-color:var(--jade-soft); } 744 .statbar .stat-foot { margin:22px 0 0; color:var(--fg); font-size:14px; font-weight:500; } 745 @media (max-width:760px){ .statbar .stat-grid { grid-template-columns:1fr; gap:18px; } } 746 747 /* 5-TIER PRICING ------------------------------------------------- */ 748 .tier5 { display:grid; gap:14px; grid-template-columns:repeat(3,1fr); margin-top:30px; } 749 .tier5 .t5 { background:var(--bg-elev); border:1px solid var(--rule); 750 border-top:1px solid rgba(26,144,88,.4); border-radius:5px; 751 padding:24px 22px; display:flex; flex-direction:column; gap:12px; 752 transition:transform .2s ease, border-color .2s ease; } 753 .tier5 .t5:hover { transform:translateY(-2px); border-top-color:var(--jade-soft); } 754 .tier5 .t5.feature { border-top:2px solid var(--jade); 755 box-shadow:0 0 0 1px rgba(26,144,88,.18); } 756 .tier5 .t5.rescue { border-top:1px solid rgba(240,160,48,.5); } 757 .tier5 .t5 .n { font-family:'JetBrains Mono',monospace; font-size:11px; 758 color:var(--jade-soft); letter-spacing:0.2em; text-transform:uppercase; } 759 .tier5 .t5.rescue .n { color:var(--amber); } 760 .tier5 .t5 .p { font-family:'JetBrains Mono',monospace; font-size:24px; 761 color:var(--fg); font-weight:600; letter-spacing:-0.02em; } 762 .tier5 .t5 .p .u { font-size:12px; color:var(--fg-dim); font-weight:400; letter-spacing:0; } 763 .tier5 .t5 .d { color:var(--fg-mid); font-size:13px; line-height:1.55; flex:1; } 764 .tier5 .t5 .cta { display:inline-block; text-align:center; padding:10px 14px; 765 border-radius:4px; font-family:'JetBrains Mono',monospace; 766 font-size:12px; font-weight:500; letter-spacing:0.03em; 767 border:1px solid var(--rule); color:var(--fg); } 768 .tier5 .t5 .cta:hover { border-color:var(--jade-soft); color:var(--jade-soft); } 769 .tier5 .t5.feature .cta { background:var(--jade); color:#050609; border-color:var(--jade); } 770 .tier5 .t5.feature .cta:hover { background:var(--jade-soft); color:#050609; } 771 /* FN-PLATFORM-TIER: the licensing lane is a different motion (no checkout, 772 negotiated, usage-based) so it spans the grid instead of sitting as a 7th 773 orphan card in a 3-column row. */ 774 .tier5 .t5.platform { grid-column:1 / -1; border-top:2px solid var(--jade); 775 background:linear-gradient(180deg,rgba(45,187,120,.05),var(--bg-elev) 60%); } 776 .tier5 .t5.platform .n { color:var(--jade-soft); } 777 .tier5 .t5.platform .cta { background:var(--jade); color:#050609; border-color:var(--jade); } 778 .tier5 .t5.platform .cta:hover { background:var(--jade-soft); color:#050609; } 779 @media (max-width:560px){ .tier5 .t5.platform { grid-column:auto; } } 780 @media (max-width:860px){ .tier5 { grid-template-columns:1fr 1fr; } } 781 @media (max-width:560px){ .tier5 { grid-template-columns:1fr; } } 782 783 /* INTELLIGENCE ROWS --------------------------------------------- */ 784 .intel { display:flex; flex-direction:column; gap:0; margin-top:30px; 785 border:1px solid var(--rule); border-radius:6px; overflow:hidden; } 786 .intel .row { display:grid; grid-template-columns:130px 1fr; gap:20px; 787 padding:20px 24px; border-bottom:1px solid var(--rule-soft); } 788 .intel .row:last-child { border-bottom:none; } 789 .intel .row .k { font-family:'JetBrains Mono',monospace; font-size:12px; 790 color:var(--jade-soft); letter-spacing:0.14em; text-transform:uppercase; 791 padding-top:2px; } 792 .intel .row .v { color:var(--fg-mid); font-size:14px; line-height:1.6; } 793 .intel .row .v strong { color:var(--fg); font-weight:500; } 794 @media (max-width:640px){ .intel .row { grid-template-columns:1fr; gap:6px; } } 795 796 /* PRODUCT STATS STRIP -------------------------------------------- */ 797 .statstrip { display:flex; flex-wrap:wrap; justify-content:center; align-items:baseline; 798 gap:8px 20px; margin-top:28px; font-family:'JetBrains Mono',monospace; 799 font-size:13px; color:var(--fg-mid); border:1px solid var(--rule); 800 border-radius:6px; padding:18px 22px; background:var(--bg-elev); } 801 .statstrip b { color:var(--fg); font-weight:600; } 802 .statstrip .sep { color:var(--rule); } 803 .src { font-size:inherit; } 804 805 /* Lead capture */ 806 .leadform { background:var(--bg-elev); border:1px solid var(--rule); border-top:2px solid var(--jade); 807 border-radius:8px; padding:36px 32px; margin-top:22px; } 808 .leadform > p { margin:0 0 4px; color:var(--fg-mid); } 809 .leadform form { display:flex; flex-direction:column; gap:12px; max-width:520px; margin-top:20px; position:relative; } 810 .leadform input, .leadform select, .leadform textarea { 811 width:100%; padding:12px 14px; background:var(--bg-code); border:1px solid var(--rule); 812 border-radius:4px; color:var(--fg); font-family:'JetBrains Mono',monospace; font-size:13px; } 813 .leadform textarea { resize:vertical; font-family:'Inter',sans-serif; } 814 .leadform select:invalid { color:var(--fg-dim); } 815 .leadform input:focus, .leadform select:focus, .leadform textarea:focus { 816 outline:none; border-color:var(--jade-soft); } 817 .leadform .btn { align-self:flex-start; border:none; } 818 .leadform .hp { position:absolute; left:-9999px; width:1px; height:1px; overflow:hidden; } 819 .leadform .note { color:var(--fg-dim); font-size:12px; margin-top:14px; } 820 .leadform .lead-status { color:var(--jade-soft); font-family:'JetBrains Mono',monospace; 821 font-size:13px; margin-top:12px; } 822 823/* ââ responsive hardening: mobile horizontal-overflow fixes âââââââââââââââ */ 824/* grid/flex children default to min-width:auto and refuse to shrink below their 825 min-content width; a single long token then widens the whole document. */ 826.card{min-width:0;overflow-wrap:anywhere} 827.pill{max-width:100%;overflow-wrap:anywhere} 828table.map{display:block;overflow-x:auto;max-width:100%} 829pre,pre.code{max-width:100%} 830/* the footer template has no whitespace between links, so the line cannot wrap; 831 a zero-width space after each separator gives it a break opportunity. */ 832footer .sep::after{content:"\200B"} 833/* root-page navs only got flex-wrap inside a <768px media query, so at exactly 834 768px the nav ran 3px past the viewport instead of wrapping. */ 835.topnav{flex-wrap:wrap} 836</style> 837<!-- FN-FAVICON --> 838<link rel="icon" href="/favicon.ico" type="image/x-icon" sizes="16x16 32x32 48x48"> 839<link rel="icon" href="/icon-512.png" type="image/png" sizes="512x512"> 840<link rel="apple-touch-icon" href="/apple-touch-icon.png"> 841<!-- /FN-FAVICON --> 842</head> 843<body> 844<!-- FN-NAV-OPEN --> 845<nav class="topnav"> 846 <a class="topnav-brand" href="/">FoundryNet</a> 847 <details class="topnav-dropdown"> 848 <summary>Industries</summary> 849 <div class="topnav-menu"> 850 <a href="/cnc">Manufacturing</a> 851 <a href="/datacenters">Data cent
851ers</a> 852 <a href="/energy">Energy</a> 853 <a href="/coldchain">Cold chain</a> 854 </div> 855 </details> 856 <span class="topnav-spacer"></span> 857 <a class="topnav-cta" href="https://forge.foundrynet.io">Forge</a> 858 <a class="topnav-cta" href="/cdm/">Data Model</a> 859 <a class="topnav-cta" href="/sprint">Sprint</a> 860 <a class="topnav-cta" href="/licensing">Licensing</a> 861 <a class="topnav-cta" href="https://forge.foundrynet.io/pricing">Pricing</a> 862 <a class="topnav-cta" href="/docs">Docs</a> 863 <a class="topnav-cta" href="/technology">Technology</a> 864</nav> 865<!-- FN-NAV-CLOSE --> 866<!-- FN-NAV-CLOSE --> 867 868<!-- ââ 1. HERO âââââââââââââââââââââââââââââââââââââââââââââââââââââââ --> 869<section class="hero first"> 870 <h1>Canonical Data Model for Industrial Equipment</h1> 871 <p class="tagline">985 fields. <span data-fn="prod.oem_families">18</span> OEM families. ISA-95 categorized. Deterministic resolution. Ships as a Docker container.</p> 872 <p class="sub">The semantic dictionary your platform framework needs. Take any AI agent and make it work on any machine from any manufacturer: <span data-fn="prod.protocols">14</span> protocols, one canonical schema, governed data from any machine on the floor.</p> 873 <p class="hero-cta" style="margin:22px 0 4px;display:flex;gap:12px;flex-wrap:wrap;"> 874 <a href="/docs" class="btn primary">Developer Sandbox, Free →</a> 875 <a href="/licensing" class="btn secondary">Platform License →</a> 876 </p> 877 <!-- FN-AUDIENCE-SPLIT --> 878 <style> 879 .aud{display:grid;grid-template-columns:1fr 1fr;gap:0;margin:26px 0 4px;max-width:820px; 880 border:1px solid var(--rule);border-radius:10px;overflow:hidden;} 881 @media(max-width:720px){.aud{grid-template-columns:1fr}} 882 .aud .a{padding:20px 22px;background:var(--bg-elev);border-right:1px solid var(--rule-soft);} 883 .aud .a:last-child{border-right:0;background:rgba(45,187,120,.045);} 884 @media(max-width:720px){.aud .a{border-right:0;border-bottom:1px solid var(--rule-soft)} 885 .aud .a:last-child{border-bottom:0}} 886 .aud .who{display:block;font-family:'JetBrains Mono',monospace;font-size:10.5px;letter-spacing:.2em; 887 text-transform:uppercase;color:var(--fg-dim);margin-bottom:8px;} 888 .aud .a:last-child .who{color:var(--jade-soft);} 889 .aud .what{margin:0;font-size:15.5px;line-height:1.6;color:var(--fg-mid);} 890 .aud .what b{color:var(--fg);font-weight:500;} 891 </style> 892 <div class="aud"> 893 <div class="a"> 894 <span class="who">For developers</span> 895 <p class="what"><b>Normalize any vendor in 30 seconds.</b> One API call, one MCP tool, no integration project. <a href="/docs">Try the free sandbox →</a></p> 896 </div> 897 <div class="a"> 898 <span class="who">For platforms</span> 899 <p class="what"><b>License the engine.</b> Ship a cross-vendor canonical data model as your feature. Next quarter. <a href="/licensing">See platform licensing →</a></p> 900 </div> 901 </div> 902 <!-- /FN-AUDIENCE-SPLIT --> 903 <p class="hero-readonly"><b>Read-only.</b> Forge reads data from your equipment. It never sends commands. Your machines are safe.</p> 904 <div class="ctas"> 905 <a href="/licensing" class="btn primary">License the engine â</a> 906 <a href="/docs" class="btn secondary">Try the free sandbox â</a> 907 <!-- FN-DEMO-CTA --><a href="https://calendly.com/foundry25/30min?utm_source=foundrynet&utm_medium=site&utm_content=home-hero" class="btn secondary" target="_blank" rel="noopener">Book a 20-minute technical intro â</a><!-- /FN-DEMO-CTA --> 908 </div> 909 910 <!-- ââ TRY IT NOW: evaluating Forge IS deploying Forge ââââââââââââââââ --> 911 <style> 912 .trynow{margin:40px 0 8px;padding:24px;border:1px solid var(--jade);border-radius:12px; 913 background:var(--bg-code);box-shadow:0 0 0 1px rgba(16,185,129,.06),0 18px 50px -30px rgba(16,185,129,.35);} 914 .trynow-head{font-family:'JetBrains Mono',monospace;font-size:18px;font-weight:600;color:var(--fg); 915 margin:0 0 4px;letter-spacing:-.01em;} 916 .trynow-sub{color:var(--fg-mid);font-size:13px;margin:0 0 16px;} 917 .trynow pre.code{margin:0;overflow-x:auto;} 918 .trynow .code.out{margin-top:12px;border-left:2px solid var(--jade);} 919 .trynow-resp-label{display:inline-block;font-family:'JetBrains Mono',monospace;font-size:10px; 920 letter-spacing:.14em;text-transform:uppercase;color:var(--jade-soft);margin:14px 0 2px;} 921 .trynow-actions{display:flex;flex-wrap:wrap;align-items:center;gap:14px;margin-top:18px;} 922 .trynow-note{color:var(--fg-mid);font-size:13.5px;line-height:1.55;margin:16px 0 0;} 923 .trynow-note b{color:var(--fg);font-weight:600;} 924 925 /* READ-ONLY SAFETY ---------------------------------------------- */ 926 .hero-readonly{display:inline-block;margin:18px 0 0;padding:10px 16px; 927 border:1px solid var(--jade);border-radius:8px;background:rgba(16,185,129,.05); 928 color:var(--fg-mid);font-size:14px;line-height:1.55;} 929 .hero-readonly b{color:var(--jade-soft);font-weight:600;} 930 .readonly-callout{margin:38px 0 0;padding:26px 28px;border:1px solid var(--jade); 931 border-left:3px solid var(--jade);border-radius:12px;background:var(--bg-code); 932 box-shadow:0 0 0 1px rgba(16,185,129,.06),0 18px 50px -30px rgba(16,185,129,.35);} 933 .readonly-callout .ro-head{font-family:'JetBrains Mono',monospace;font-size:19px; 934 font-weight:600;color:var(--fg);margin:0 0 10px;letter-spacing:-.01em;} 935 .readonly-callout p{margin:0;color:var(--fg-mid);font-size:15px;line-height:1.65;} 936 .readonly-callout p + p{margin-top:10px;} 937 .readonly-callout b{color:var(--fg);font-weight:600;} 938 </style> 939 <div class="trynow"> 940 <div class="trynow-head">Try it now. Your data, 30 seconds, free.</div> 941 <div class="trynow-sub">Paste a reading from your own equipment. No key needed to see it work.</
941div> 942 <pre class="code"><code>curl -X POST https://forge.foundrynet.io/v1/normalize \ 943 -H "Content-Type: application/json" \ 944 -d '{"oem": "haas", "data": {"Spindle_Speed": 8500, "CoolantTemp": 24.1}}'</code></pre> 945 <span class="trynow-resp-label">response</span> 946 <pre class="code out"><code>{ 947 "normalized": { 948 "spindle_speed_rpm": 8500, 949 "sensor_readings.coolant_temp": 24.1 950 }, 951 "coverage_pct": 100, 952 "confidence": 0.97 953}</code></pre> 954 <div class="trynow-actions"> 955 <a href="/quickstart" class="btn primary">Get your API key â</a> 956 <a href="/docs" class="btn secondary">See it work â</a> 957 </div> 958 <p class="trynow-note">No sales call. No proof of concept. No integration project. <b>Paste your data, see the result. That IS the deployment.</b></p> 959 </div> 960 961 <div class="term"> 962 <div class="you">claude mcp add --transport http forge https://mcp.foundrynet.io/mcp --header 'Authorization: Bearer YOUR_API_KEY'</div> 963 <span class="gap"></span> 964 <div class="you">forge.normalize_telemetry({ data: {"Spindle_Speed": 2200, "spd_load": 85}, oem: "fanuc" })</div> 965 <div class="ok">{ spindle_speed_rpm: 2200, spindle_load_pct: 85, coverage: 100%, confidence: 0.97 }</div> 966 <span class="gap"></span> 967 <div class="feed">Your agent understands the machine. Took 30 seconds.</div> 968 </div> 969 <p class="trynow-note" style="margin-top:14px;">Need the key that goes in 970 <code>YOUR_API_KEY</code>? <a href="/quickstart">Get one free in 10 seconds</a>: email address, no card, 3 machines.</p> 971</section> 972 973<!-- ââ 2. THE PROBLEM (3 cards, cited) âââââââââââââââââââââââââââââââââ --> 974<section> 975 <h3>The problem</h3> 976 <h2>Industrial AI keeps failing. Here's why.</h2> 977 <div class="grid3" style="margin-top:34px;"> 978 <div class="card"> 979 <div class="desc" style="font-weight:600;color:var(--fg);font-size:15px;">
979Every vendor names the same measurement differently</div> 980 <p style="margin:8px 0 0;">A Fanuc reports spindle speed one way, a Haas another, a Siemens a third, in different units and scales. A frontier model can usually interpret them. But “usually” isn’t a spec: ask twice and the interpretation can change, and nobody owns whether it’s right. 79% of manufacturing leaders say integration challenges and poor data quality limit AI’s real impact (<a class="src" href="https://www.l2l.com/blog/manufacturing-data-report-press-release" target="_blank" rel="noopener">L2L 2026</a>).</p> 981 </div> 982 <div class="card"> 983 <div class="desc" style="font-weight:600;color:var(--fg);font-size:15px;">Most data isn’t ready for agents</div> 984 <p style="margin:8px 0 0;">Only 15% of enterprises are fully prepared to run agentic AI in production, and the most-cited barrier is data quality and lineage — knowing what a value means and where it came from (<a class="src" href="https://www.fivetran.com/press/fivetran-launches-2026-agentic-ai-readiness-index-revealing-gap-between-enterprise-investment-and-data-preparedness-for-agentic-ai" target="_blank" rel="noopener">Fivetran 2026</a>). A bare “4.2” is ambiguous until someone declares the unit. Forge makes that declaration explicit and carries it with the value.</p> 985 </div> 986 <div class="card"> 987 <div class="desc" style="font-weight:600;color:var(--fg);font-size:15px;">No audit trail, no production</div> 988 <p style="margin:8px 0 0;">Gartner expects over 40% of agentic AI projects to be canceled by 2027, citing rising costs, unclear business value, and inadequate risk controls (<a class="src" href="https://www.gartner.com/en/newsroom/press-releases/2025-06-25-gartner-predicts-over-40-percent-of-agentic-ai-projects-will-be-canceled-by-end-of-2027" target="_blank" rel="noopener">Gartner</a>). In an industrial setting, an agent decision nobody can reconstruct is a liability. Forge addresses the risk-control piece directly: every reading is validated, versioned, and hash-verifiable.</p> 989 </div> 990 </div> 991</section> 992 993<!-- ââ 3. THE SOLUTION (PREDICT / DIAGNOSE / GOVERN) âââââââââââââââââââ --> 994<section> 995 <h3>What you license</h3> 996 <h2>A verified vocabulary your platform embeds.</h2> 997 <p class="beyond-sub">Not another platform to run beside the one you already have. Forge is the 998 layer that makes your customers' data mean the same thing across every vendor that talks to you 999 — and lets you prove it.</p> 1000 <div class="grid3" style="margin-top:34px;"> 1001 <div class="card"> 1002 <div class="desc" style="font-weight:600;color:var(--fg);font-size:15px;">One vocabulary, versioned</div> 1003 <p style="margin:8px 0 0;"><span data-fn="prod.canonical_fields">985</span> canonical fields with declared units and types, resolved from 3,444 curated 1004 vendor mappings across <span data-fn="prod.oem_families">18</span> OEM families and <span data-fn="prod.protocols">14</span> protocols. Every corpus release is versioned, so the 1005 reading you stored last year is still interpretable this year.</p> 1006 </div> 1007 <div class="card"> 1008 <div class="desc" style="font-weight:600;color:var(--fg);font-size:15px;">It abstains</div> 1009 <p style="margin:8px 0 0;">Physics bounds, sentinel detection, and type checks run on every value. A 1010 register boundary or an impossible reading resolves to null <em>with a reason</em>, never to a number 1011 your model will treat as real. Confidence travels with the mapping.</p> 1012 </div> 1013 <div class="card"> 1014 <div class="desc" style="font-weight:600;color:var(--fg);font-size:15px;">It is provable</div> 1015 <p style="margin:8px 0 0;">Every response carries a corpus-versioned hash over the canonical output. 1016 Re-hash an archived reading and you can show which decision tables produced it. Same input, same 1017 bytes, every call — which is what an audit, an SLA, or a safety review actually requires.</p> 1018 </div> 1019 </div> 1020 <p class="beyond-sub" style="margin-top:26px;">Forge also ships a working agent on its own model 1021 — prediction, diagnosis, OEE, shift reports, work orders. That is proof the vocabulary is 1022 complete enough to build on, not the product you are buying. 1023 <a href="/reference-agent">See the reference agent →</a></p> 1024</section> 1025 1026<!-- ââ 4. HOW IT WORKS (4 steps) ââââââââââââââââââââââââââââââââââââââ --> 1027<section class="sandbox-section"> 1028 <h3>How it works</h3> 1029 <h2>From raw protocols to working agent in four steps.</h2> 1030 <ol class="sandbox-steps" style="margin-top:28px;"> 1031 <li>
1032 <span class="step-num">01</span> 1033 <div class="step-body"> 1034 <h3>Connect your equipment</h3> 1035 <p>Forge reads from any PLC, controller, or sensor through 14 industrial protocols: MQTT, Modbus TCP, Modbus RTU, OPC UA, SNMP, BACnet, ROS2, J1939/CAN, MAVLink, MTConnect, EtherNet/IP, PROFINET, REST, CSV. Point Forge at your equipment and it auto-detects the protocol and starts reading. No hardware to install. No PLC code to write. No downtime. Your existing equipment is already compatible.</p> 1036 <p><b>Read-only.</b> Forge reads data from your equipment. It never sends commands. Your machines are safe.</p> 1037 </div> 1038 </li> 1039 <li> 1040 <span class="step-num">02</span> 1041 <div class="step-body"> 1042 <h3>Your data normalizes automatically</h3> 1043 <p>Raw vendor tags become universal fields. <span data-fn="prod.mappings">2,086</span> cross-OEM mappings across <span data-fn="prod.oem_families">18</span> manufacturer families. Your Fanuc's "Spindle_Speed" and your Haas's "spd_rpm" become one universal field: <code>spindle_speed_rpm</code>. The engine handles the messy reality of factory floors: renamed tags, unit mismatches, sentinel values, and offline equipment. Unresolved tags are researched, cached and confirmed, then ship as verified mappings in signed corpus updates.</p> 1044 </div> 1045 </li> 1046 <li> 1047 <span class="step-num">03</span> 1048 <div class="step-body"> 1049 <h3>Connect your AI agent</h3> 1050 <p>One line. Your agent calls 32 MCP tools: normalize, predict, diagnose, calculate OEE, generate shift reports, manage work orders, read and pre-check safety guardrails. Works with Claude, Cursor, LangChain, CrewAI, AutoGen, or any MCP-compatible client.</p> 1051 <pre class="code"><code>claude mcp add --transport http forge https://mcp.foundrynet.io/mcp --header 'Authorization: Bearer YOUR_API_KEY'</code></pre> 1052 </div> 1053 </li> 1054 <li> 1055 <span class="step-num">04</span> 1056 <div class="step-body"> 1057 <h3>Govern and verify</h3> 1058 <p>Every agent action is supervised and recorded. Your operations team sees what every agent is doing, approves actions, and has a tamper-evident record of everything that happened.</p> 1059 </div> 1060 </li> 1061 </ol> 1062 1063 <!-- ââ PAYLOAD DEMO: raw vendor telemetry â canonical output âââââââââââ --> 1064 <style> 1065 .payload-demo{margin:34px 0 0;border:1px solid var(--rule);border-radius:12px; 1066 background:var(--bg-code);overflow:hidden;} 1067 .payload-demo[open]{border-color:var(--jade); 1068 box-shadow:0 0 0 1px rgba(16,185,129,.06),0 18px 50px -30px rgba(16,185,129,.35);} 1069 .payload-demo > summary{list-style:none;cursor:pointer;padding:18px 22px; 1070 display:flex;align-items:center;gap:12px; 1071 font-family:'JetBrains Mono',monospace;font-size:13.5px;color:var(--fg); 1072 transition:background .15s ease;} 1073 .payload-demo > summary::-webkit-details-marker{display:none} 1074 .payload-demo > summary:hover{background:rgba(45,187,120,.04)} 1075 .payload-demo > summary:focus-visible{outline:1px solid var(--jade-soft);outline-offset:-3px} 1076 .pd-caret{color:var(--jade-soft);font-size:11px;flex:0 0 auto; 1077 transition:transform .18s ease;transform-origin:50% 50%;} 1078 .payload-demo[open] .pd-caret{transform:rotate(90deg)} 1079 .pd-title{flex:1 1 auto;min-width:0;font-weight:500;letter-spacing:-.01em} 1080 .pd-hint{flex:0 0 auto;font-size:10px;letter-spacing:.14em;text-transform:uppercase; 1081 color:var(--fg-dim);border:1px solid var(--rule);border-radius:4px;padding:4px 7px} 1082 .pd-body{padding:0 22px 24px;display:grid;gap:18px;align-items:start; 1083 grid-template-columns:minmax(0,1fr) auto minmax(0,1fr);} 1084 .pd-col{min-width:0} 1085 .pd-label{font-family:'JetBrains Mono',monospace;font-size:10px;letter-spacing:.14em; 1086 text-transform:uppercase;margin:0 0 8px;} 1087 .pd-label-raw{color:var(--fg-dim)} 1088 .pd-label-out{color:var(--jade-soft)} 1089 .payload-demo pre.code{margin:0;max-width:100%;font-size:12px;padding:16px 18px; 1090 box-shadow:none;} 1091 .payload-demo pre.code.raw{border-left:2px solid var(--fg-dim)} 1092 .payload-demo pre.code.out{border-left:2px solid var(--jade)} 1093 .pd-arrow{align-self:center;color:var(--jade);font-size:20px;line-height:1; 1094 font-family:'JetBrains Mono',monospace} 1095 .pd-note{margin:10px 0 0;color:var(--fg-mid);font-size:13px;line-height:1.6} 1096 .pd-note code{color:var(--amber);font-size:.94em} 1097 .pd-foot{grid-column:1/-1;margin:8px 0 0;padding:14px 0 0; 1098 border-top:1px solid var(--rule-soft); 1099 color:var(--fg-mid);font-size:13.5px;line-height:1.6} 1100 .pd-foot b{color:var(--fg);font-weight:600} 1101 .pd-foot code{color:var(--amber);font-size:.94em} 1102 @media (max-width:860px){ 1103 .pd-body{grid-template-columns:minmax(0,1fr)} 1104 .pd-arrow{transform:rotate(90deg);justify-self:start} 1105 .payload-demo > summary{font-size:12.5px;padding:16px 16px;gap:10px} 1106 .pd-hint{display:none} 1107 .pd-body{padding:0 16px 20px} 1108 } 1109 </style> 1110 <details class="payload-demo"> 1111 <summary>
1112 <span class="pd-caret" aria-hidden="true">â¶</span> 1113 <span class="pd-title">See exactly what comes back: raw vendor telemetry â Forge canonical output</span> 1114 <span class="pd-hint">example</span> 1115 </summary> 1116 <div class="pd-body"> 1117 <div class="pd-col"> 1118 <div class="pd-label pd-label-raw">Before · raw vendor telemetry</div> 1119 <pre class="code raw"><code>{ 1120 <span class="key">"S1Temp"</span>: 72.1, 1121 <span class="key">"S1Load"</span>: 847, 1122 <span class="key">"SP_SPEED"</span>: 8500, 1123 <span class="key">"aa_000"</span>: 3.2 1124}</code></pre> 1125 <p class="pd-note">Four values off the controller. No names an agent can reason about, no units, no scale. <code>847</code> could be anything.</p> 1126 </div> 1127 <div class="pd-arrow" aria-hidden="true">â</div> 1128 <div class="pd-col"> 1129 <div class="pd-label pd-label-out">After · Forge canonical output</div> 1130 <pre class="code out"><code>{ 1131 <span class="key">"spindle_temperature_c"</span>: 72.1, 1132 <span class="key">"spindle_load_pct"</span>: 84.7, 1133 <span class="key">"spindle_speed_rpm"</span>: 8500, 1134 <span class="key">"coolant_pressure_bar"</span>: 3.2, 1135 <span class="key">"oem"</span>: <span class="str">"haas"</span>, 1136 <span class="key">"coverage_pct"</span>: 100, 1137 <span class="key">"match_type"</span>: <span class="str">"corpus"</span> 1138}</code></pre> 1139 <p class="pd-note">Named, typed, unitized. <code>847</code> was tenths of a percent, so it lands as <code>84.7</code>. <code>aa_000</code> was coolant pressure in bar. The <code>_c</code> comes from the unit the Haas pack declares for <code>S1Temp</code> — declare it on the tag instead (<code>S1Temp (degF)</code>) and Forge converts. It reads a declared unit; it never infers one.</p> 1140 </div> 1141 <p class="pd-foot">Same shape from every manufacturer. A Fanuc, a Haas, and a Mazak all return <code>spindle_speed_rpm</code>, so your agent is written once. <b>coverage_pct</b> tells you how much of the payload was mapped and <b>match_type</b> tells you how, so you always know whether to trust the reading.</p> 1142 </div> 1143 </details> 1144</section> 1145 1146<!-- ââ 5. BUILT-IN INTELLIGENCE âââââââââââââââââââââââââââââââââââââââ --> 1147 1148 1149<!-- ââ 6. DEPLOY (Edge / Cloud / Fleet / Hybrid) ââââââââââââââââââââââ --> 1150<section> 1151 <h3>Deploy</h3> 1152 <h2>
1152Runs anywhere your equipment lives.</h2> 1153 <div class="grid4" style="margin-top:30px;"> 1154 <div class="card"> 1155 <div class="desc" style="font-weight:600;color:var(--fg);font-size:15px;">EDGE</div> 1156 <p style="margin:8px 0 0;">Docker on any 4 GB Linux host, NUC, or industrial PC. Values never leave your building — only tag names and counts are shared for corpus updates.</p> 1157 </div> 1158 <div class="card"> 1159 <div class="desc" style="font-weight:600;color:var(--fg);font-size:15px;">CLOUD</div> 1160 <p style="margin:8px 0 0;"><b style="color:var(--fg);">No hardware to buy.</b> No industrial PCs. No edge servers. No IT approval process. Forge runs in the cloud, hosted at forge.foundrynet.io. Instant sandbox, full MCP gateway. Your data arrives through the protocols your equipment already speaks. When you want edge deployment, run the same Docker container on any 4 GB Linux host. Same kernel, your choice of where it runs.</p> 1161 </div> 1162 <div class="card"> 1163 <div class="desc" style="font-weight:600;color:var(--fg);font-size:15px;">FLEET</div> 1164 <p style="margin:8px 0 0;">AWS Greengrass component. Azure IoT Edge module. Deploy to thousands of devices through your existing edge infrastructure.</p> 1165 </div> 1166 <div class="card"> 1167 <div class="desc" style="font-weight:600;color:var(--fg);font-size:15px;">HYBRID</div> 1168 <p style="margin:8px 0 0;">Edge for real-time intelligence. Cloud sync for fleet analytics and cross-site learning.</p> 1169 </div> 1170 </div> 1171</section> 1172 1173<!-- ââ 6b. GUARDRAILS âââââââââââââââââââââââââââââââââââââââââââââââââ --> 1174<section id="guardrails"> 1175 <h3>Guardrails</h3> 1176 <h2>Deterministic safety. No alert fatigue.</h2> 1177 <p class="beyond-sub">Define hard boundaries for your equipment. If an agent tries to exceed them, the kernel blocks it instantly. No human approval needed, no rubber-stamping, no latency. Your plant engineer sets the rules. No agent can override them.</p> 1178 <div class="readonly-callout" style="margin-top:30px;"> 1179 <div class="ro-head">Guardrails are not alerts</div> 1180 <p>They don't ask for permission. They enforce the rules your engineers set, at the software layer: a rejected automation never leaves the kernel, and a denied agent never reads the machine. Every block is logged. Your operators stop babysitting approval prompts and start doing their actual jobs.</p> 1181 <p><b>An engineer writes the rule in plain English. The kernel enforces it deterministically, forever.</b></p> 1182 </div> 1183 <div class="grid3" style="margin-top:34px;"> 1184 <div class="card"> 1185 <div class="desc" style="font-weight:600;color:var(--fg);font-size:15px;">SET A HARD LIMIT</div> 1186 <p style="margin:8px 0 0;">"Block if spindle_speed_rpm > 12000." Value bounds, rate-of-change limits, physics-validation failures, and compound rules like "spindle load above 90% AND coolant above 45°C." Parsed once, reviewed by you, then enforced identically on every reading.</p> 1187 </div> 1188 <div class="card"> 1189 <div class="desc" style="font-weight:600;color:var(--fg);font-size:15px;">AGENTS CHECK BEFORE THEY ACT</div> 1190 <p style="margin:8px 0 0;">Agents can read your guardrails and test a proposed value against them before attempting anything. They self-correct instead of getting silently rejected. What they cannot do is create, edit, or disable a guardrail. That takes a human.</p> 1191 </div> 1192 <div class="card"> 1193 <div class="desc" style="font-weight:600;color:var(--fg);font-size:15px;">EVERY BLOCK IS ON THE RECORD</div> 1194 <p style="margin:8px 0 0;">What was blocked, when, why, and which agent tried. Deleting a guardrail is a soft delete with an audit trail, so "who removed the spindle limit, and when" is always answerable. Your compliance team gets proof, not assurances.</p> 1195 </div> 1196 </div> 1197 <p class="beyond-sub" style="margin-top:26px;">Guardrails block any agent-proposed action that violates a rule your engineers set, and log every block. Forge is read-only: it never sends commands to your machines, and your operators remain the only ones who can act on equipment.</p> 1198</section> 1199 1200<!-- ââ 6c. WORKS WITH âââââââââââââââââââââââââââââââââââââââââââââââââ --> 1201<style> 1202 .ww-grid { display: grid; gap: 12px; margin-top: 30px; 1203 grid-template-columns: repeat(4, 1fr); } 1204 @media (max-width: 860px) { .ww-grid { grid-template-columns: repeat(2, 1fr); } } 1205 @media (max-width: 460px) { .ww-grid { grid-template-columns: 1fr; } } 1206 .ww-item { display: block; border: 1px solid var(--rule); border-radius: 10px; 1207 background: var(--bg-elev); padding: 16px 18px; text-decoration: none; 1208 transition: border-color .16s ease, transform .16s ease; } 1209 a.ww-item:hover { border-color: var(--jade); transform: translateY(-1px); } 1210 .ww-name { color: var(--fg); font-weight: 600; font-size: 15px; letter-spacing: -.01em; } 1211 .ww-kind { color: var(--fg-dim); font-size: 12px; margin-top: 3px; 1212 font-family: 'JetBrains Mono', ui-monospace, monospace; letter-spacing: .04em; } 1213 .ww-ready { color: var(--jade-soft); font-size: 11.5px; margin-top: 9px; 1214 font-family: 'JetBrains Mono', ui-monospace, monospace; } 1215 .ww-note { color: var(--fg-mid); font-size: 14.5px; margin-top: 26px; } 1216</style> 1217<section id="works-with"> 1218 <h3>Works with</h3> 1219 <h2>Fits the stack you already run.</h2> 1220 <p class="beyond-sub">Forge resolves your equipment data, then pushes it wherever it needs to go. Four of these ship as pre-built templates: the field mapping, the exact payload the platform expects, and a curl command that creates the automation. The rest is any endpoint that accepts a webhook.</p> 1221
1222 <div class="ww-grid"> 1223 <a class="ww-item" href="https://forge.foundrynet.io/templates/sitewise?utm_source=foundrynet&utm_medium=works_with&utm_campaign=sitewise"> 1224 <div class="ww-name">AWS IoT SiteWise</div> 1225 <div class="ww-kind">DATA STREAMING</div> 1226 <div class="ww-ready">Template · ~5 min</div> 1227 </a> 1228 <a class="ww-item" href="https://forge.foundrynet.io/templates/servicenow?utm_source=foundrynet&utm_medium=works_with&utm_campaign=servicenow"> 1229 <div class="ww-name">ServiceNow</div> 1230 <div class="ww-kind">INCIDENT MANAGEMENT</div> 1231 <div class="ww-ready">Template · ~10 min</div> 1232 </a> 1233 <a class="ww-item" href="https://forge.foundrynet.io/templates/salesforce?utm_source=foundrynet&utm_medium=works_with&utm_campaign=salesforce"> 1234 <div class="ww-name">Salesforce</div> 1235 <div class="ww-kind">FIELD SERVICE</div> 1236 <div class="ww-ready">Template · ~10 min</div> 1237 </a> 1238 <a class="ww-item" href="https://forge.foundrynet.io/templates/pagerduty?utm_source=foundrynet&utm_medium=works_with&utm_campaign=pagerduty"> 1239 <div class="ww-name">PagerDuty</div> 1240 <div class="ww-kind">ALERTING</div> 1241 <div class="ww-ready">Template · ~2 min</div> 1242 </a> 1243 <a class="ww-item" href="https://forge.foundrynet.io/templates/pagerduty?utm_source=foundrynet&utm_medium=works_with&utm_campaign=opsgenie"> 1244 <div class="ww-name">Opsgenie</div> 1245 <div class="ww-kind">ALERTING</div> 1246 <div class="ww-ready">Template · ~2 min</div> 1247 </a> 1248 <div class="ww-item"> 1249 <div class="ww-name">Slack</div> 1250 <div class="ww-kind">NOTIFICATION</div> 1251 <div class="ww-ready">Incoming webhook</div> 1252 </div> 1253 <div class="ww-item"> 1254 <div class="ww-name">AWS Greengrass · Azure IoT Edge</div> 1255 <div class="ww-kind">FLEET DEPLOYMENT</div> 1256 <div class="ww-ready">Native component</div> 1257 </div> 1258 <div class="ww-item"> 1259 <div class="ww-name">Any webhook endpoint</div> 1260 <div class="ww-kind">HTTPS · POST / PUT / PATCH</div> 1261 <div class="ww-ready">Bearer · Basic · HMAC</div> 1262 </div> 1263 </div> 1264 1265 <p class="ww-note">An integration is a URL, a method, headers, an auth mode, and a JSON body template. If your system accepts a webhook, Forge can drive it, and the condition that fires it is written against a canonical field name that stays the same when you add a machine from a different manufacturer. <a href="https://forge.foundrynet.io/templates?utm_source=foundrynet&utm_medium=works_with&utm_campaign=hub">See all templates →</a></p> 1266</section> 1267 1268<!-- ââ 7. PRICING (6 tiers) âââââââââââââââââââââââââââââââââââââââââââ --> 1269<section id="pricing"> 1270 <h3>Pricing</h3> 1271 <h2>Start free. Pay when agents go to production.</h2> 1272 <p class="pricing-lede">Try the engine free on your own machine. Integrate directly through the API. Or license the engine and ship a cross-vendor canonical data model as a feature of your own platform.</p> 1273 <div class="tier5"> 1274 <div class="t5 feature"> 1275 <div class="n">Developer Sandbox</div> 1276 <div class="p">Free</div> 1277 <div class="d">The sandbox build: <span data-fn="sandbox.canonical_fields">
1277719</span> canonical fields, <span data-fn="sandbox.packs">29</span> packs. Full API, no time limit, no key required. Runs entirely on your own machine. <code>docker pull ghcr.io/foundrynet/forge-sandbox</code>. Send your vendor's tags, see what comes back.</div> 1278 <a class="cta" href="/docs">Read the quickstart â</a> 1279 </div> 1280 <div class="t5"> 1281 <div class="n">Forge API</div> 1282 <div class="p">$0.30<span class="u">/1,000 normalized events</span></div> 1283 <div class="d">For developers integrating Forge directly. No subscription, no minimum, no seat count. Graduated volume tiers down to $0.06 per 1,000. $0.10 predict, $0.10 diagnose, $0.20 fleet, $0.02 integrity proof, per action. Free: identify, coverage, mapping, OEE, shift report, energy, agent cards. Self-serve. API key issued by email.</div> 1284 <a class="cta" href="https://forge.foundrynet.io/pricing">Get API key, free â</a> 1285 </div> 1286 <!-- FN-PLATFORM-TIER --> 1287 <div class="t5 platform"> 1288 <div class="n">Platform License: embed the engine</div> 1289 <div class="p">$0.15<span class="u"> per 1,000 normalized events · graduated tiers to $0.03 at scale</span></div> 1290 <div class="d"><b style="color:var(--fg);">For platforms that want a cross-vendor canonical data model as a native feature.</b> You already connect to equipment. License the engine that makes your platform understand every vendor that talks to it: <span data-fn="prod.canonical_fields">985</span> canonical fields, <span data-fn="prod.mappings">2,086</span> mappings, <span data-fn="prod.oem_families">18</span> OEM families, <span data-fn="prod.protocols">14</span> protocols. Self-improving corpus, unit conversion, sentinel detection, physics validation, MCP-native. Docker drop-in, outbound HTTPS only, no inbound ports, remote corpus updates with no restart. Tag names and counts leave the site; values never do. <b style="color:var(--fg);">Your brand. Your deployment. Your revenue.</b> Annual platform license sized to your deployment. 14-day paid pilot ($25,000), credited in full against year one. <b style="color:var(--fg);">We license to platforms. We don't sell to your customers.</b></div> 1291 <a class="cta" href="/licensing">See platform licensing â</a> 1292 </div> 1293 <!-- /FN-PLATFORM-TIER --> 1294 </div> 1295</section> 1296 1297<!-- ââ 8. VERTICALS (AI agents for X) âââââââââââââââââââââââââââââââââ --> 1298<section id="products"> 1299 <h3>Verticals</h3> 1300 <h2>Built for manufacturing first.</h2> 1301 <p class="beyond-sub">The corpus is deepest where the equipment is hardest: CNC and robotics. 1302 Same kernel runs the rest.</p> 1303 <div class="hub" style="margin-top:28px;"> 1304 <div class="product-card"> 1305 <h4>AI agents for manufacturing</h4> 1306 <p>CNC, robotics, and mixed-vendor lines. Connect your agents to Fanuc, Siemens, Haas, ABB, KUKA, 1307 and 13 more through one data layer — the arm and the machine it tends in a single schema.</p> 1308 <a href="/cnc">Explore →</a> 1309 </div> 1310 </div> 1311 <p class="beyond-sub" style="margin-top:30px;font-size:14px;">Also supported on the same kernel: 1312 <a href="/datacenters">data centers</a> (UPS, PDUs, CRAC units) · 1313 <a href="/energy">energy</a> (inverters, storage, grid assets) · 1314 <a href="/coldchain">cold chain</a> (refrigeration controllers).</p> 1315</section> 1316 1317<!-- ââ LEAD CAPTURE ââââââââââââââââââââââââââââââââââââââââââââââââââ --> 1318<section id="contact"> 1319 <h3>Talk to us</h3> 1320 <h2>See Forge on your equipment</h2> 1321 <div class="leadform"> 1322 <p>Tell us what you're working with. We'll show you what Forge does with your data.</p> 1323 <form id="lead-form" novalidate> 1324 <input type="email" name="email" placeholder="Work email" required autocomplete="email"> 1325 <select name="interest" required> 1326 <option value="" disabled selected>What brings you here?</option> 1327 <option value="pilot_rescue">Stalled AI pilot</option> 1328 <option value="equipment_finance">Equipment finance / leasing</option> 1329 <option value="integrator">System integrator</option> 1330 <option value="monitoring">Equipment monitoring</option> 1331 <option value="agent_builder">Building AI agents</option> 1332 <option value="other">Other</option> 1333 </select> 1334 <textarea name="message" placeholder="What equipment or challenge? (optional)" rows="3"></textarea> 1335 <input class="hp" type="text" name="hp" tabindex="-1" autocomplete="off" aria-hidden="true"> 1336 <button type="submit" class="btn primary">Try your data</button> 1337 </form> 1338 <div class="lead-status" id="lead-status" hidden></div> 1339 <p class="note">
1339Or just email [email protected]. No sales pitch. Just a demo on your data.</p> 1340 </div> 1341
1341<script> 1342 (function(){ 1343 var f = document.getElementById('lead-form'); 1344 if (!f) return; 1345 var statusEl = document.getElementById('lead-status'); 1346 function show(msg){ statusEl.hidden = false; statusEl.textContent = msg; } 1347 f.addEventListener('submit', function(e){ 1348 e.preventDefault(); 1349 var email = (f.email.value || '').trim(); 1350 if (!email || email.indexOf('@') < 1) { show('Please enter a valid work email.'); return; } 1351 var utmSource = new URLSearchParams(window.location.search).get('utm_source') || null; 1352 var payload = { email: email, interest: f.interest.value || null, 1353 message: (f.message.value || '').trim() || null, 1354 utm_source: utmSource, hp: f.hp.value || '' }; 1355 var btn = f.querySelector('button[type=submit]'); 1356 btn.disabled = true; show('Sendingâ¦'); 1357 fetch('https://forge.foundrynet.io/v1/leads', { 1358 method: 'POST', headers: { 'Content-Type': 'application/json' }, 1359 body: JSON.stringify(payload) 1360 }).then(function(r){ 1361 if (!r.ok) throw new Error('http ' + r.status); 1362 return r.json(); 1363 }).then(function(){ 1364 if (window.plausible) plausible('Lead Submitted', { props: { interest: payload.interest || 'unspecified' } }); 1365 f.reset(); show("â Got it, we'll be in touch within 24 hours."); 1366 }).catch(function(){ 1367 // Network / 5xx fallback: open a pre-filled mail draft so the lead isn't lost. 1368 show('Could not reach the server. Opening an email draftâ¦'); 1369 var body = encodeURIComponent('Interest: ' + (payload.interest || '-') + '\n\n' + (payload.message || '')); 1370 window.location.href = 'mailto:[email protected]?subject=' + 1371 encodeURIComponent('Demo request: ' + email) + '&body=' + body; 1372 }).finally(function(){ btn.disabled = false; }); 1373 }); 1374 })(); 1375 </script>
1375 1376</section> 1377 1378<!-- ââ FOOTER ââââââââââââââââââââââââââââââââââââââââââââââââââââââââ --> 1379<!-- FN-SUBSTACK-FOOTER --> 1380<div style="text-align:center; padding:1.5rem 0; border-top:1px solid #333;"> 1381 <a href="https://substack.com/@themachineagent" target="_blank" rel="noopener" 1382 style="color:#8b949e; text-decoration:none; font-size:14px;"> 1383 Get the thesis behind this infrastructure: <span style="color:#58a6ff;">The Machine Agent</span>, weekly. 1384 </a> 1385</div> 1386<!-- /FN-SUBSTACK-FOOTER --> 1387<footer> 1388 Foundry Labs<span class="sep">·</span>Industrial AI Infrastructure<span class="sep collapse">·</span><a data-email href="#" class="foot-mail">[email protected]</a><span class="sep">·</span><a href="/docs">Docs</a><span class="sep">·</span><a href="/terms">Terms</a><span class="sep">·</span><a href="/privacy">Privacy</a><span class="sep">·</span><a href="https://assay.foundrynet.io">Assay</a> 1389 <span class="sep">·</span><a href="https://substack.com/@themachineagent" target="_blank">The Machine Agent</a> 1390</footer> 1391 1392<!-- ââ CHAT WIDGET (floating) âââââââââââââââââââââââââââââââââââââââ --> 1393<style> 1394 #fn-chat-fab { position:fixed; bottom:22px; right:22px; z-index:9000; 1395 width:56px; height:56px; border-radius:50%; 1396 background:var(--jade); border:none; cursor:pointer; 1397 box-shadow:0 6px 24px rgba(26,144,88,.35); 1398 color:#050609; display:flex; align-items:center; justify-content:center; 1399 transition:transform .15s, background .15s; } 1400 #fn-chat-fab:hover { background:var(--jade-soft); transform:translateY(-2px); } 1401 #fn-chat-fab svg { width:26px; height:26px; } 1402 #fn-chat-panel { position:fixed; bottom:90px; right:22px; z-index:9000; 1403 width:380px; max-width:calc(100vw - 32px); 1404 height:560px; max-height:calc(100vh - 110px); 1405 background:var(--bg-elev); border:1px solid var(--rule); 1406 border-radius:8px; display:none; flex-direction:column; 1407 box-shadow:0 10px 40px rgba(0,0,0,.5); overflow:hidden; } 1408 #fn-chat-panel.open { display:flex; } 1409 #fn-chat-head { padding:12px 16px; border-bottom:1px solid var(--rule); 1410 display:flex; justify-content:space-between; align-items:center; 1411 font-family:'JetBrains Mono',monospace; font-size:12px; 1412 color:var(--fg-mid); } 1413 #fn-chat-head .tag { color:var(--amber); font-size:10px; letter-spacing:.18em; 1414 text-transform:uppercase; padding:3px 8px; 1415 border:1px solid var(--rule); border-radius:3px; } 1416 #fn-chat-head button { background:none; border:none; color:var(--fg-dim); 1417 font-size:18px; cursor:pointer; padding:0 4px; } 1418 #fn-chat-log { flex:1; overflow-y:auto; padding:14px 16px; 1419 font-size:13.5px; line-height:1.55; } 1420 #fn-chat-log .m { margin:10px 0; white-space:pre-wrap; } 1421 #fn-chat-log .m.u { color:var(--fg); } 1422 #fn-chat-log .m.b { color:var(--fg); border-left:2px solid var(--jade); 1423 padding-left:10px; } 1424 #fn-chat-log .m.t { color:var(--fg-dim); font-family:'JetBrains Mono',monospace; 1425 font-size:10.5px; padding:5px 8px; background:var(--bg-code); 1426 border-radius:3px; } 1427 #fn-chat-log .role { font-family:'JetBrains Mono',monospace; font-size:9.5px; 1428 letter-spacing:.18em; text-transform:uppercase; 1429 color:var(--fg-dim); margin-bottom:3px; } 1430 #fn-chat-form { display:flex; gap:6px; padding:10px; border-top:1px solid var(--rule); } 1431 #fn-chat-form textarea { flex:1; resize:none; min-height:38px; max-height:110px; 1432 padding:8px 10px; background:var(--bg-code); color:var(--fg); 1433 border:1px solid var(--rule); border-radius:4px; 1434 font-family:'Inter',sans-serif; font-size:13px; outline:none; } 1435 #fn-chat-form textarea:focus { border-color:var(--jade-soft); } 1436 #fn-chat-form button { padding:8px 14px; background:var(--jade); color:#050609; 1437 border:none; border-radius:4px; cursor:pointer; 1438 font-family:'JetBrains Mono',monospace; font-size:11.5px; 1439 font-weight:500; letter-spacing:.04em; } 1440 #fn-chat-form button:hover { background:var(--jade-soft); } 1441 #fn-chat-form button:disabled { opacity:.55; cursor:not-allowed; } 1442 #fn-chat-upsell { display:none; padding:8px 14px; border-top:1px solid var(--rule); 1443 background:var(--bg-code); color:var(--amber); font-size:11.5px; 1444 text-align:center; } 1445 #fn-chat-upsell a { color:var(--amber); } 1446 #fn-chat-head .link { background:none; border:none; cursor:pointer; 1447 color:var(--jade-soft); font-family:'JetBrains Mono',monospace; 1448 font-size:10.5px; letter-spacing:.04em; padding:3px 6px; 1449 text-decoration:none; } 1450 #fn-chat-head .link:hover { color:var(--amber); } 1451 #fn-chat-head a.link { display:inline-flex; align-items:center; } 1452 #fn-chat-signin { display:none; padding:14px 16px; 1453 border-bottom:1px solid var(--rule); background:var(--bg-code); } 1454 #fn-chat-signin.open { display:block; } 1455 #fn-chat-signin p { margin:0 0 8px; color:var(--fg-mid); font-size:12px; line-height:1.5; } 1456 #fn-chat-signin p.hint { margin-top:10px; font-size:11px; color:var(--fg-dim); } 1457 #fn-chat-signin code { color:var(--amber); font-family:'JetBrains Mono',monospace; } 1458 #fn-chat-signin input { width:100%; padding:8px 10px; background:var(--bg); 1459 color:var(--fg); border:1px solid var(--rule); 1460 border-radius:4px; font-family:'JetBrains Mono',monospace; 1461 font-size:12px; outline:none; box-sizing:border-box; } 1462 #fn-chat-signin input:focus { border-color:var(--jade-soft); } 1463 #fn-chat-signin .row { display:flex; gap:6px; margin-top:8px; align-items:center; } 1464 #fn-chat-signin button { padding:7px 12px; background:var(--jade); color:#050609; 1465 border:none; border-radius:4px; cursor:pointer; 1466 font-family:'JetBrains Mono',monospace; font-size:11px; 1467 letter-spacing:.04em; } 1468 #fn-chat-signin button.link { background:none; color:var(--fg-dim); padding:5px; } 1469 #fn-chat-signin button.link:hover { color:var(--amber); } 1470 #fn-chat-signin .err { display:none; margin-top:8px; color:var(--amber); 1471 font-size:11px; font-family:'JetBrains Mono',monospace; } 1472 #fn-chat-signin .err.show { display:block; } 1473 @media (max-width:480px){ 1474 #fn-chat-panel { right:8px; left:8px; width:auto; bottom:78px; height:70vh; } 1475 } 1476</style> 1477 1478<button id="fn-chat-fab" aria-label="Open chat"> 1479 <svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"> 1480 <path d="M3 5.5C3 4.12 4.12 3 5.5 3h13C19.88 3 21 4.12 21 5.5v9c0 1.38-1.12 2.5-2.5 2.5H9l-4 4v-4h-.5C3.12 17 3 15.88 3 14.5v-9z"/> 1481 </svg> 1482</button> 1483 1484<div id="fn-chat-panel" role="dialog" aria-label="FoundryNet chat"> 1485 <div id="fn-chat-head"> 1486 <span>FoundryNet · <span style="color:var(--jade-soft)">ask anything</span></span> 1487 <span style="display:flex;align-items:center;gap:8px;">
1488 <span class="tag" id="fn-chat-tag">demo</span> 1489 <a id="fn-chat-mail" class="link" 1490 data-email href="#" title="Email FoundryNet">contact</a> 1491 <button type="button" id="fn-chat-auth" class="link">sign in</button> 1492 <button type="button" id="fn-chat-close" aria-label="Close">Ã</button> 1493 </span> 1494 </div> 1495 <div id="fn-chat-signin"> 1496 <p>Paste your <code>fnet_</code> API key to switch to live mode.</p> 1497 <input type="password" id="fn-chat-key" placeholder="fnet_â¦" autocomplete="off" spellcheck="false"> 1498 <div class="row"> 1499 <button type="button" id="fn-chat-verify">Verify & sign in</button> 1500 <button type="button" id="fn-chat-cancel" class="link">cancel</button> 1501 </div> 1502 <div class="err" id="fn-chat-keyerr"></div> 1503 <p class="hint">No key yet? <a href="https://forge.foundrynet.io/pricing">Get your API key</a>, takes about 2 minutes.</p> 1504 </div> 1505 <div id="fn-chat-log"> 1506 <div class="m b"> 1507 <div class="role">FoundryNet</div>Hi. Tell me about your machines and I'll help. Try: "I have a Fanuc Robodrill α-D21MiB5 on Line 6, can you connect it?" 1508 </div> 1509 </div> 1510 <form id="fn-chat-form" autocomplete="off"> 1511 <textarea id="fn-chat-input" placeholder="Type a messageâ¦" rows="1"></textarea> 1512 <button type="submit" id="fn-chat-send">Send</button> 1513 </form> 1514 <div id="fn-chat-upsell"> 1515 Demo mode. Get a free key at <a href="https://forge.foundrynet.io/pricing">forge.foundrynet.io/pricing</a>. 1516 </div> 1517</div> 1518
1519<script> 1520(function () { 1521 var API_BASE = 'https://forge.foundrynet.io'; 1522 1523 var panel = document.getElementById('signup-panel'); 1524 // Self-serve signup UI was removed for the enterprise page, no-op if absent. 1525 if (!panel) return; 1526 var toggle = document.getElementById('signup-toggle'); 1527 var stepEmail = document.getElementById('signup-step-email'); 1528 var stepActive = document.getElementById('signup-step-activating'); 1529 var stepKey = document.getElementById('signup-step-key'); 1530 var emailFree = document.getElementById('signup-email-free'); 1531 var emailPaid = document.getElementById('signup-email-paid'); 1532 var btnFree = document.getElementById('signup-free'); 1533 var btnPaid = document.getElementById('signup-paid'); 1534 var errBox = document.getElementById('signup-err'); 1535 var keyBox = document.getElementById('signup-key'); 1536 var copyBtn = document.getElementById('signup-copy'); 1537 var metaLine = document.getElementById('signup-meta'); 1538 1539 function openPanel() { 1540 panel.classList.add('open'); 1541 panel.scrollIntoView({ behavior: 'smooth', block: 'center' }); 1542 } 1543 function showError(msg) { 1544 errBox.textContent = msg; 1545 errBox.classList.add('show'); 1546 } 1547 function clearError() { 1548 errBox.textContent = ''; 1549 errBox.classList.remove('show'); 1550 } 1551 function showStep(which) { 1552 stepEmail.style.display = which === 'email' ? '' : 'none'; 1553 stepActive.style.display = which === 'activate' ? '' : 'none'; 1554 stepKey.style.display = which === 'key' ? '' : 'none'; 1555 } 1556 1557 toggle.addEventListener('click', function (e) { 1558 e.preventDefault(); 1559 openPanel(); 1560 showStep('email'); 1561 setTimeout(function () { emailFree.focus(); }, 200); 1562 }); 1563 1564 // Pricing-tier Starter card "Get free API key" CTA, same effect as 1565 // the hero "Get your API key" button: opens the signup panel scrolled 1566 // to email step, focuses the free input. 1567 // Sandbox / Coverage / Free tier CTAs, same effect as hero "Get a free 1568 // API key": open the signup panel scrolled to email step. 1569 ['sandbox-signup-toggle', 'coverage-signup-toggle', 'tier-free-cta', 1570 'tier-starter-cta' /* legacy id kept for backwards-compat */].forEac
1570h(function (id) { 1571 var el = document.getElementById(id); 1572 if (el) el.addEventListener('click', function (e) { 1573 e.preventDefault(); toggle.click(); 1574 }); 1575 }); 1576 1577 // Per-asset Early Access CTA, POSTs to /v1/forms/fleet-interest on the 1578 // Forge API, which inserts into the fleet_interest Supabase table. Falls 1579 // back to a mailto: draft if the API call errors out so the visitor never 1580 // hits a dead button. Intentionally NOT a Stripe charge, the per-asset 1581 // ("Fleet") price is live in Stripe but kept gated to Early Access until 1582 // we have onboarding capacity. 1583 var btnEarlyAccess = document.getElementById('early-access-cta'); 1584 if (btnEarlyAccess) btnEarlyAccess.addEventListener('click', async function () { 1585 var src = document.getElementById('signup-email-earlyaccess'); 1586 var typed = (src && src.value || '').trim(); 1587 if (!typed || typed.indexOf('@') < 1) { 1588 src && src.focus(); 1589 if (src) { src.style.borderColor = 'var(--amber)'; 1590 setTimeout(function(){ src.style.borderColor = ''; }, 1500); } 1591 return; 1592 } 1593 var origLabel = btnEarlyAccess.textContent; 1594 btnEarlyAccess.disabled = true; 1595 btnEarlyAccess.textContent = 'Sendingâ¦'; 1596 1597 // Mailto fallback, used if fetch errors, the API returns 5xx, or the 1598 // visitor's network blocks fetch(). Preserves the lead via the visitor's 1599 // own email client; [email protected] is the inbox. 1600 function fallbackMailto() { 1601 var subject = encodeURIComponent('Early Access, Fleet pricing'); 1602 var body = encodeURIComponent( 1603 'Hi FoundryNet,\n\n' + 1604 'I want to talk about per-machine pricing for our fleet.\n\n' + 1605 'Email: ' + typed + '\n' + 1606 'Company: \n' + 1607 'Fleet size (rough): \n' + 1608 'OEMs / models on the floor: \n' + 1609 'Current sampling rate: \n\n' + 1610 'Thanks.\n' 1611 ); 1612 window.location.href = 1613 'mailto:[email protected]?subject=' + subject + '&body=' + body; 1614 } 1615 1616 try { 1617 var res = await fetch(API_BASE + '/v1/forms/fleet-interest', { 1618 method: 'POST', 1619 headers: { 'Content-Type': 'application/json' }, 1620 body: JSON.stringify({ email: typed }) 1621 }); 1622 var data = await res.json().catch(function(){ return {}; }); 1623 if (!res.ok || !data.ok) { 1624 // 422 = bad email shape, 429 = rate-limited, 5xx = backend issue. 1625 // For 422/429 we show the message inline; for 5xx we fall through 1626 // to mailto so the visitor still has a path forward. 1627 if (res.status >= 500) { 1628 fallbackMailto(); 1629 return; 1630 } 1631 btnEarlyAccess.textContent = origLabel; 1632 btnEarlyAccess.disabled = false; 1633 alert(data.reason || data.detail || data.message || 1634 ('Could not submit (HTTP ' + res.status + ')')); 1635 return; 1636 } 1637 btnEarlyAccess.textContent = 'â Sent, we\'ll be in touch'; 1638 // Lock the button on success so users don't double-submit. 1639 } catch (e) { 1640 // Network failure â mailto fallback. Don't swallow the lead. 1641 fallbackMailto(); 1642 } 1643 }); 1644 1645 // Growth tier inline signup, self-serve Stripe Checkout with 1646 // tier=growth in the request body. Same redirect-to-Stripe flow as 1647 // the hero paid signup, just discounted prices on the subscription. 1648 var emailGrowth = document.getElementById('signup-email-growth'); 1649 var btnGrowth = document.getElementById('signup-growth'); 1650 async function submitGrowth() { 1651 var email = (emailGrowth.value || '').trim(); 1652 if (!email || email.indexOf('@') < 1) { 1653 emailGrowth.focus(); 1654 emailGrowth.style.borderColor = 'var(--amber)'; 1655 setTimeout(function () { emailGrowth.style.borderColor = ''; }, 1500); 1656 return; 1657 } 1658 var original = btnGrowth.textContent; 1659 btnGrowth.disabled = true; 1660 btnGrowth.textContent = 'Redirectingâ¦'; 1661 try { 1662 var res = await fetch(API_BASE + '/v1/keys/checkout', { 1663 method: 'POST', 1664 headers: { 'Content-Type': 'application/json' }, 1665 body: JSON.stringify({ email: email, tier: 'growth' }) 1666 }); 1667 var data = await res.json().catch(function () { return {}; }); 1668 if (!res.ok || !data.checkout_url) { 1669 btnGrowth.textContent = original; 1670 btnGrowth.disabled = false;
1671 alert(data.reason || data.detail || ('Signup failed (HTTP ' + res.status + ')')); 1672 return; 1673 } 1674 window.location.href = data.checkout_url; 1675 } catch (e) { 1676 btnGrowth.textContent = original; 1677 btnGrowth.disabled = false; 1678 alert('Network error: ' + e.message); 1679 } 1680 } 1681 if (btnGrowth) btnGrowth.addEventListener('click', submitGrowth); 1682 if (emailGrowth) emailGrowth.addEventListener('keydown', function (e) { 1683 if (e.key === 'Enter') { e.preventDefault(); submitGrowth(); } 1684 }); 1685 1686 function lockButtons(label, which) { 1687 btnFree.disabled = btnPaid.disabled = true; 1688 btnFree.dataset.original = btnFree.dataset.original || btnFree.textContent; 1689 btnPaid.dataset.original = btnPaid.dataset.original || btnPaid.textContent; 1690 if (which === 'free') btnFree.textContent = label; 1691 else btnPaid.textContent = label; 1692 } 1693 function unlockButtons() { 1694 btnFree.disabled = btnPaid.disabled = false; 1695 btnFree.textContent = btnFree.dataset.original || 'Get free API key â'; 1696 btnPaid.textContent = btnPaid.dataset.original || 'Continue to Stripe â'; 1697 } 1698 1699 async function submitSignup(tier) { 1700 clearError(); 1701 // Each tier card has its own email input, read whichever the user typed in. 1702 var src = tier === 'free' ? emailFree : emailPaid; 1703 var email = (src.value || '').trim(); 1704 // Fall back to the OTHER input if this card's was left empty (lets the 1705 // user type once and click either button). 1706 if (!email) { 1707 var alt = tier === 'free' ? emailPaid : emailFree; 1708 email = (alt.value || '').trim(); 1709 } 1710 if (!email || email.indexOf('@') < 1) { 1711 showError('Please enter a valid email.'); 1712 src.focus(); 1713 return; 1714 } 1715 lockButtons(tier === 'free' ? 'Minting keyâ¦' : 'Redirectingâ¦', tier); 1716 try { 1717 var res = await fetch(API_BASE + '/v1/keys/checkout', { 1718 method: 'POST', 1719 headers: { 'Content-Type': 'application/json' }, 1720 body: JSON.stringify({ email: email, tier: tier }) 1721 }); 1722 var data = await res.json().catch(function () { return {}; }); 1723 if (!res.ok) { 1724 showError(data.reason || data.detail || ('Signup failed (HTTP ' + res.status + ')')); 1725 unlockButtons(); 1726 return; 1727 } 1728 if (tier === 'free') { 1729 // Free tier returns the api_key directly, show key step inline. 1730 if (!data.api_key) { 1731 showError(data.warning || 'Free tier already issued for this email, check your records.'); 1732 unlockButtons(); 1733 return; 1734 } 1735 keyBox.textContent = data.api_key; 1736 metaLine.textContent = 1737 'free tier · ' + (data.calls_remaining || 50) + 1738 ' calls remaining · ' + (data.email || email); 1739 showStep('key'); 1740 return; 1741 } 1742 // Paid tier returns a checkout URL, redirect. 1743 if (!data.checkout_url) { 1744 showError(data.reason || data.detail || 'No checkout URL returned.'); 1745 unlockButtons(); 1746 return; 1747 } 1748 window.location.href = data.checkout_url; 1749 } catch (e) { 1750 showError('Network error: ' + e.message); 1751 unlockButtons(); 1752 } 1753 } 1754 btnFree.addEventListener('click', function () { submitSignup('free'); }); 1755 btnPaid.addEventListener('click', function () { submitSignup('paid'); }); 1756 // Enter inside each input submits its own card. 1757 emailFree.addEventListener('keydown', function (e) { 1758 if (e.key === 'Enter') { e.preventDefault(); btnFree.click(); } 1759 }); 1760 emailPaid.addEventListener('keydown', function (e) { 1761 if (e.key === 'Enter') { e.preventDefault(); btnPaid.click(); } 1762 }); 1763 1764 copyBtn.addEventListener('click', async function () { 1765 var key = keyBox.textContent.trim(); 1766 if (!key) return; 1767 try { 1768 await navigator.clipboard.writeText(key); 1769 copyBtn.textContent = 'Copied â'; 1770 setTimeout(function () { copyBtn.textContent = 'Copy key'; }, 1800); 1771 } catch (e) { 1772 // Fallback: select the text so the user can âC manually. 1773 var range = document.createRange(); 1774 range.selectNodeContents(keyBox); 1775 var sel = window.getSelection(); 1776 sel.removeAllRanges(); sel.addRange(range); 1777 copyBtn.textContent = 'Press âC / Ctrl-C'; 1778 setTimeout(function () { copyBtn.textContent = 'Copy key'; }, 2400); 1779 } 1780 }); 1781 1782 // ââ Activation handler âââââââââââââââââââââââââââââââââââââââââ 1783 // Stripe redirects back to ?session_id=cs_⦠on success. Pull it, 1784 // strip it from the URL (so refresh doesn't replay), and activate. 1785 async function maybeActivate() { 1786 var params = new URLSearchParams(window.location.search); 1787 var sessionId = params.get('session_id'); 1788 if (!sessionId) return; 1789 1790 // Clean the URL immediately so a refresh doesn't re-POST. We 1791 // keep the hash so #docs etc. still work. 1792 var clean = window.location.pathname + window.location.hash; 1793 window.history.replaceState({}, document.title, clean); 1794
1795 openPanel(); 1796 showStep('activate'); 1797 1798 try { 1799 var res = await fetch(API_BASE + '/v1/keys/activate', { 1800 method: 'POST', 1801 headers: { 'Content-Type': 'application/json' }, 1802 body: JSON.stringify({ session_id: sessionId }) 1803 }); 1804 var data = await res.json().catch(function () { return {}; }); 1805 if (!res.ok || !data.api_key) { 1806 showStep('email'); 1807 showError(data.reason || data.detail 1808 || ('Activation failed (HTTP ' + res.status + ')')); 1809 return; 1810 } 1811 keyBox.textContent = data.api_key; 1812 metaLine.textContent = 1813 'customer ' + (data.customer_id || '?') + 1814 ' · ' + (data.email || 'no email on file'); 1815 showStep('key'); 1816 } catch (e) { 1817 showStep('email'); 1818 showError('Network error during activation: ' + e.message); 1819 } 1820 } 1821 maybeActivate(); 1822})(); 1823</script>
1823 1824
1825<script> 1826// ââ Chat widget controller ââââââââââââââââââââââââââââââââââââââââââ 1827(function(){ 1828 var API='https://forge.foundrynet.io'; 1829 var fab=document.getElementById('fn-chat-fab'), 1830 panel=document.getElementById('fn-chat-panel'), 1831 closeBtn=document.getElementById('fn-chat-close'), 1832 log=document.getElementById('fn-chat-log'), 1833 form=document.getElementById('fn-chat-form'), 1834 input=document.getElementById('fn-chat-input'), 1835 send=document.getElementById('fn-chat-send'), 1836 tag=document.getElementById('fn-chat-tag'), 1837 upsell=document.getElementById('fn-chat-upsell'), 1838 authBtn=document.getElementById('fn-chat-auth'), 1839 signinPanel=document.getElementById('fn-chat-signin'), 1840 keyInput=document.getElementById('fn-chat-key'), 1841 verifyBtn=document.getElementById('fn-chat-verify'), 1842 cancelBtn=document.getElementById('fn-chat-cancel'), 1843 keyErr=document.getElementById('fn-chat-keyerr'); 1844 var sessionId=localStorage.getItem('fn_chat_session')||null; 1845 var apiKey=localStorage.getItem('fn_api_key')||null; 1846 1847 function bubble(cls,role,text){ 1848 var d=document.createElement('div'); d.className='m '+cls; 1849 if(role){ var r=document.createElement('div'); r.className='role'; r.textContent=role; d.appendChild(r); } 1850 d.appendChild(document.createTextNode(text)); 1851 log.appendChild(d); log.scrollTop=log.scrollHeight; return d; 1852 } 1853 function tool(text){ 1854 var d=document.createElement('div'); d.className='m t'; 1855 d.textContent='⺠'+text; log.appendChild(d); log.scrollTop=log.scrollHeight; 1856 } 1857 function open(){ panel.classList.add('open'); setTimeout(function(){input.focus();},80); } 1858 function close(){ panel.classList.remove('open'); } 1859 function setSignedInUI(signedIn){ 1860 tag.textContent=signedIn?'live':'demo'; 1861 upsell.style.display=signedIn?'none':'block'; 1862 authBtn.textContent=signedIn?'sign out':'sign in'; 1863 } 1864 function showSignin(show){ 1865 signinPanel.classList.toggle('open', show); 1866 keyErr.classList.remove('show'); keyErr.textContent=''; 1867 if(show){ setTimeout(function(){keyInput.focus();},80); } 1868 } 1869 1870 // Restore prior auth UI state on load. 1871 if(apiKey) setSignedInUI(true); 1872 1873 fab.addEventListener('click',function(){ panel.classList.contains('open')?close():open(); }); 1874 closeBtn.addEventListener('click',close); 1875 input.addEventListener('input',function(){input.style.height='auto';input.style.height=Math.min(input.scrollHeight,110)+'px';}); 1876 input.addEventListener('keydown',function(e){if(e.key==='Enter'&&!e.shiftKey){e.preventDefault();form.requestSubmit();}}); 1877 1878 // ââ Sign in / sign out âââââââââââââââââââââââââââââââââââââââââââ 1879 authBtn.addEventListener('click', function(){ 1880 if(apiKey){ 1881 // Sign out: clear key + bound demo session so a fresh demo starts. 1882 apiKey=null; 1883 localStorage.removeItem('fn_api_key'); 1884 localStorage.removeItem('fn_chat_session'); 1885 sessionId=null; 1886 setSignedInUI(false); 1887 bubble('b','FoundryNet','Signed out. Youâre back in demo mode.'); 1888 } else { 1889 showSignin(!signinPanel.classList.contains('open')); 1890 } 1891 }); 1892 cancelBtn.addEventListener('click', function(){ showSignin(false); }); 1893 keyInput.addEventListener('keydown', function(e){ 1894 if(e.key==='Enter'){ e.preventDefault(); verifyBtn.click(); } 1895 }); 1896 verifyBtn.addEventListener('click', async function(){ 1897 keyErr.classList.remove('show'); keyErr.textContent=''; 1898 var k=(keyInput.value||'').trim(); 1899 if(!k.startsWith('fnet_')){ 1900 keyErr.textContent='Keys start with fnet_'; keyErr.classList.add('show'); return; 1901 } 1902 verifyBtn.disabled=true; verifyBtn.textContent='Verifyingâ¦'; 1903 try{ 1904 var r=await fetch(API+'/v1/whoami',{ headers:{'Authorization':'Bearer '+k} }); 1905 var data=await r.json().catch(function(){return {};}); 1906 if(!r.ok){ 1907 keyErr.textContent=data.detail||data.reason||'Key rejected'; 1908 keyErr.classList.add('show'); 1909 verifyBtn.disabled=false; verifyBtn.textContent='Verify & sign in'; return; 1910 } 1911 // Looks good, store + reset session so the next message goes through auth. 1912 apiKey=k; localStorage.setItem('fn_api_key', k); 1913 localStorage.removeItem('fn_chat_session'); sessionId=null; 1914 keyInput.value=''; showSignin(false); setSignedInUI(true); 1915 var meta=[]; if(data.has_subscription) meta.push('billing active'); 1916 if(data.is_demo) meta.push('demo key, full mode disabled'); 1917 bubble('b','FoundryNet','Signed in.'+(meta.length?' ('+meta.join(', ')+')':'')); 1918 } catch(err){ 1919 keyErr.textContent='Network error: '+err.message; keyErr.classList.add('show'); 1920 } 1921 verifyBtn.disabled=false; verifyBtn.textContent='Verify & sign in'; 1922 }); 1923 1924 form.addEventListener('submit', async function(e){ 1925 e.preventDefault(); 1926 var text=input.value.trim(); if(!text) return; 1927 bubble('u','You',text); input.value=''; input.style.height='auto'; 1928 send.disabled=true; var thinking=bubble('b','FoundryNet','â¦'); 1929 try{ 1930 var headers={'Content-Type':'application/json'}; 1931 if(apiKey) headers['Authorization']='Bearer '+apiKey; 1932 var body={message:text}; if(sessionId) body.session_id=sessionId; 1933 var r=await fetch(API+'/v1/chat',{method:'POST',headers:headers,body:JSON.stringify(body)}); 1934 var data=await r.json().catch(function(){return {};}); 1935 thinking.remove(); 1936 if(!r.ok){ bubble('b','FoundryNet',(data.detail||data.reason||'Error '+r.status)); send.disabled=false; return; } 1937 sessionId=data.session_id; localStorage.setItem('fn_chat_session',sessionId); 1938 setSignedInUI(!data.is_demo);
1939 (data.tool_calls||[]).forEach(function(tc){ tool(tc.tool+'('+JSON.stringify(tc.args)+')'); }); 1940 bubble('b','FoundryNet',data.message||'(no response)'); 1941 }catch(err){ thinking.remove(); bubble('b','FoundryNet','Network error: '+err.message); } 1942 send.disabled=false; input.focus(); 1943 }); 1944})(); 1945</script>
vendor: 1 bytes, line 1945
1945
1946<script> 1947 (function(){ 1948 var e = ['forge','foundrynet.io'].join('@'); 1949 document.querySelectorAll('[data-email]').forEach(function(el){ 1950 el.textContent = e; el.setAttribute('href', 'mailto:' + e); 1951 }); 1952 })(); 1953</script>
vendor: 1 bytes, line 1953
1953
1954<script>(function(){function c(){var b=a.contentDocument||a.contentWindow.document;if(b){var d=b.createElement('script');d.innerHTML="window.__CF$cv$params={r:'a18c417fc8cb23fd',t:'MTc4MzY1MTc3Mw=='};var a=document.createElement('script');a.src='/cdn-cgi/challenge-platform/scripts/jsd/main.js';document.getElementsByTagName('head')[0].appendChild(a);";b.getElementsByTagName('head')[0].appendChild(d)}}if(document.body){var a=document.createElement('iframe');a.height=1;a.width=1;a.style.position='absolute';a.style.top=0;a.style.left=0;a.style.border='none';a.style.visibility='hidden';document.body.appendChild(a);if('loading'!==document.readyState)c();else if(window.addEventListener)document.addEventListener('DOMContentLoaded',c);else{var e=document.onreadystatechange||function(){};document.onreadystatechange=function(b){e(b);'loading'!==document.readyState&&(document.onreadystatechange=e,c())}}}})();</script>
1954<!-- FN-COPY-BTN-OPEN --> 1955<style> 1956/* click-to-copy on code blocks and terminal demos, no dependencies */ 1957.fn-cb { position: relative; display: block; max-width: 100%; } 1958/* reserve a gutter so a long single-line command never runs under the button */ 1959.fn-cb > .fn-cb-block { padding-right: 86px; } 1960.fn-cb-btn { 1961 position: absolute; top: 8px; right: 8px; z-index: 3; 1962 font-family: 'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, monospace; 1963 font-size: 10.5px; letter-spacing: .09em; text-transform: uppercase; 1964 line-height: 1; padding: 6px 9px; border-radius: 5px; cursor: pointer; 1965 color: #7c889c; background: #0d1219; border: 1px solid #1e2b3d; 1966 -webkit-appearance: none; appearance: none; 1967 /* opaque, never translucent: long commands scroll UNDER this button, and a 1968 see-through chip lets that text bleed through and reads as a glitch */ 1969 box-shadow: -10px 0 14px -6px rgba(0,0,0,.95); 1970 transition: color .15s ease, border-color .15s ease, background .15s ease; 1971} 1972.fn-cb:hover .fn-cb-btn { color: #b6c0cf; } 1973.fn-cb-btn:hover { color: #d8dfeb; border-color: #1a9058; background: #0f1720; } 1974.fn-cb-btn:focus-visible { outline: 1px solid #2dbb78; outline-offset: 2px; color: #d8dfeb; } 1975.fn-cb-btn.is-copied { color: #2dbb78; border-color: #1a9058; } 1976.fn-cb-btn.is-failed { color: #f0a030; border-color: #f0a030; } 1977@media (max-width: 600px) { 1978 .fn-cb-btn { top: 6px; right: 6px; padding: 5px 7px; font-size: 10px; letter-spacing: .06em; } 1979 .fn-cb > .fn-cb-block { padding-right: 66px; } 1980} 1981@media print { .fn-cb-btn { display: none; } } 1982</style>
1983<script> 1984(function () { 1985 'use strict'; 1986 1987 /* Terminal demos interleave commands (.you) with output (.ok/.feed) and 1988 blank spacers (.gap). Copy only the FIRST contiguous run of command lines 1989 so a click yields something pasteable, never a command plus its output. */ 1990 function termText(el) { 1991 var kids = el.children, lines = [], started = false; 1992 for (var i = 0; i < kids.length; i++) { 1993 var isCmd = kids[i].classList.contains('you'); 1994 if (isCmd) { lines.push(kids[i].textContent); started = true; } 1995 else if (started) { break; } 1996 } 1997 return lines.length ? lines.join('\n') : null; 1998 } 1999 2000 function clean(s) { 2001 /* indents render fine but break a shell paste */ 2002 return s.replace(/\u00a0/g, ' ').replace(/[ \t]+$/gm, '').replace(/\s+$/, ''); 2003 } 2004 2005 function payload(el) { 2006 var raw = el.classList.contains('term') ? termText(el) : el.textContent; 2007 if (!raw) return null; 2008 raw = clean(raw); 2009 return raw ? raw : null; 2010 } 2011 2012 function legacyCopy(text) { 2013 try { 2014 var ta = document.createElement('textarea'); 2015 ta.value = text; 2016 ta.setAttribute('readonly', ''); 2017 ta.style.cssText = 'position:fixed;top:0;left:-9999px;opacity:0'; 2018 document.body.appendChild(ta); 2019 ta.select(); 2020 ta.setSelectionRange(0, ta.value.length); 2021 var ok = document.execCommand('copy'); 2022 document.body.removeChild(ta); 2023 return ok; 2024 } catch (e) { return false; } 2025 } 2026 2027 function flash(btn, ok) { 2028 btn.textContent = ok ? 'Copied!' : 'Press \u2318C'; 2029 btn.classList.remove('is-copied', 'is-failed'); 2030 btn.classList.add(ok ? 'is-copied' : 'is-failed'); 2031 clearTimeout(btn._fnTimer); 2032 btn._fnTimer = setTimeout(function () { 2033 btn.textContent = 'Copy'; 2034 btn.classList.remove('is-copied', 'is-failed'); 2035 }, 2000); 2036 } 2037 2038 function copy(text, btn) { 2039 if (navigator.clipboard && navigator.clipboard.writeText && window.isSecureContext) { 2040 navigator.clipboard.writeText(text).then( 2041 function () { flash(btn, true); }, 2042 function () { flash(btn, legacyCopy(text)); } 2043 ); 2044 } else { 2045 flash(btn, legacyCopy(text)); 2046 } 2047 } 2048 2049 function attach(el) { 2050 if (el.getAttribute('data-fn-cb')) return; 2051 var text = payload(el); 2052 if (!text) return; 2053 el.setAttribute('data-fn-cb', '1'); 2054 el.classList.add('fn-cb-block'); 2055 2056 /* Wrap rather than position inside: pre/.term are overflow-x:auto, so a 2057 button parented to them would scroll away with the content. */ 2058 var wrap = document.createElement('div'); 2059 wrap.className = 'fn-cb'; 2060 el.parentNode.insertBefore(wrap, el); 2061 wrap.appendChild(el); 2062 2063 var btn = document.createElement('button'); 2064 btn.type = 'button'; 2065 btn.className = 'fn-cb-btn'; 2066 btn.textContent = 'Copy'; 2067 btn.setAttribute('aria-label', 'Copy code to clipboard'); 2068 btn.addEventListener('click', function (e) { 2069 e.preventDefault(); 2070 copy(text, btn); 2071 }); 2072 wrap.appendChild(btn); 2073 } 2074 2075 function init() { 2076 var nodes = document.querySelectorAll('pre, .term'); 2077 for (var i = 0; i < nodes.length; i++) attach(nodes[i]); 2078 } 2079 2080 if (document.readyState === 'loading') { 2081 document.addEventListener('DOMContentLoaded', init); 2082 } else { 2083 init(); 2084 } 2085})(); 2086</script>
2086 2087<!-- FN-COPY-BTN-CLOSE -->
2088<script>(function(){function c(){var b=a.contentDocument||(a.contentWindow&&a.contentWindow.document);if(b){var d=b.createElement('script');d.innerHTML="window.__CF$cv$params={r:'a44ef1d3ddf47e38',t:'MTc5MTA2MTk0Mg=='};var a=document.createElement('script');a.src='/cdn-cgi/challenge-platform/scripts/jsd/main.js';document.getElementsByTagName('head')[0].appendChild(a);";b.getElementsByTagName('head')[0].appendChild(d)}}if(document.body){var a=document.createElement('iframe');a.height=1;a.width=1;a.style.position='absolute';a.style.top=0;a.style.left=0;a.style.border='none';a.style.visibility='hidden';document.body.appendChild(a);if('loading'!==document.readyState)c();else if(window.addEventListener)document.addEventListener('DOMContentLoaded',c);else{var e=document.onreadystatechange||function(){};document.onreadystatechange=function(b){e(b);'loading'!==document.readyState&&(document.onreadystatechange=e,c())}}}})();</script>
2088<script type="module" src="https://static.cloudflareinsights.com/beacon.min.js/v31edd6df95cf4e85bb4c19e7a9bdbcba1788362987495" integrity="sha512-iIg7k2xntmwu6/uSb5tpc/hySgZc4eoL31yB29W6tJFo2akwjPWcEqnCEdJvGexCL0KEQwVYv5BlowfhVz26hg==" data-cf-beacon='{"version":"2024.11.0","token":"7d6d2efd170341a38bb6c5e09af862b7","r":1,"spa":2}' crossorigin="anonymous"></script>
2088 2089</body> 2090</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.