Created
March 23, 2026 20:24
-
-
Save 23maverick23/6cc64f27d1f64c6118fbe222790fcbfd to your computer and use it in GitHub Desktop.
SCLS: Jeopardy Game
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <!DOCTYPE html> | |
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>NetSuite AI Jeopardy!</title> | |
| <link href="https://fonts.googleapis.com/css2?family=Bebas+Neue&family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet"> | |
| <style> | |
| *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; } | |
| :root { | |
| --navy: #0a1628; | |
| --board: #0d2347; | |
| --cell: #1a3a6b; | |
| --cell-h: #1e4d8c; | |
| --gold: #f0b429; | |
| --gold-d: #c8941a; | |
| --teal: #2a9d8f; | |
| --teal-d: #1f7a6e; | |
| --red: #e63946; | |
| --red-d: #b02a31; | |
| --green: #2d9a5f; | |
| --green-d: #1f7044; | |
| --text: #e8edf5; | |
| --muted: #7a9abf; | |
| --done: #0f1e36; | |
| --done-t: #1e3a5a; | |
| } | |
| body { | |
| font-family: 'Inter', sans-serif; | |
| background: linear-gradient(160deg, #060f1e 0%, #0a1628 60%, #071020 100%); | |
| color: var(--text); | |
| min-height: 100vh; | |
| padding: 1.5rem 1rem 3rem; | |
| } | |
| /* ── Setup screen ── */ | |
| #setup-screen { max-width: 560px; margin: 0 auto; padding: 2rem 0; } | |
| .setup-title { font-family: 'Bebas Neue', sans-serif; font-size: 52px; letter-spacing: .04em; color: var(--gold); text-align: center; line-height: 1; margin-bottom: .25rem; text-shadow: 0 0 40px rgba(240,180,41,.3); } | |
| .setup-sub { text-align: center; color: var(--muted); font-size: 14px; margin-bottom: 2.5rem; } | |
| .setup-label { font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: .1em; color: var(--muted); margin-bottom: 8px; } | |
| .team-inputs { display: flex; flex-direction: column; gap: 12px; margin-bottom: 2rem; } | |
| .team-row { display: flex; align-items: center; gap: 10px; } | |
| .team-dot { width: 10px; height: 10px; border-radius: 50%; flex-shrink: 0; } | |
| .team-row input { | |
| flex: 1; padding: 11px 14px; | |
| background: rgba(255,255,255,.05); | |
| border: 1px solid rgba(255,255,255,.1); | |
| border-radius: 8px; color: var(--text); | |
| font-size: 15px; font-family: 'Inter', sans-serif; | |
| outline: none; transition: border-color .15s; | |
| } | |
| .team-row input:focus { border-color: var(--gold); } | |
| .setup-btn { | |
| width: 100%; padding: 14px; | |
| background: var(--gold); border: none; | |
| border-radius: 10px; color: var(--navy); | |
| font-size: 16px; font-weight: 700; | |
| cursor: pointer; letter-spacing: .03em; | |
| transition: background .15s, transform .1s; | |
| font-family: 'Inter', sans-serif; | |
| } | |
| .setup-btn:hover { background: var(--gold-d); } | |
| .setup-btn:active { transform: scale(.98); } | |
| /* ── Game wrapper ── */ | |
| #game-screen { display: none; max-width: 1240px; margin: 0 auto; } | |
| /* ── Header ── */ | |
| .game-title { font-family: 'Bebas Neue', sans-serif; font-size: 48px; letter-spacing: .05em; color: var(--gold); text-align: center; text-shadow: 0 0 40px rgba(240,180,41,.25); margin-bottom: .75rem; } | |
| /* ── Round tabs ── */ | |
| .round-tabs { display: flex; justify-content: center; gap: 8px; margin-bottom: 1.5rem; } | |
| .rtab { | |
| padding: 8px 22px; border-radius: 8px; | |
| border: 2px solid rgba(255,255,255,.15); | |
| background: transparent; color: var(--muted); | |
| font-size: 13px; font-weight: 700; | |
| cursor: pointer; transition: all .15s; | |
| font-family: 'Inter', sans-serif; | |
| letter-spacing: .03em; | |
| } | |
| .rtab:hover { border-color: rgba(255,255,255,.3); color: var(--text); } | |
| .rtab.active { background: var(--text); border-color: var(--text); color: var(--navy); } | |
| .rtab.reset { background: var(--red); border-color: var(--red); color: #fff; } | |
| .rtab.reset:hover { background: var(--red-d); border-color: var(--red-d); } | |
| /* ── Scoreboards ── */ | |
| .scoreboards { display: grid; grid-template-columns: repeat(3, 1fr); gap: 12px; margin-bottom: 1.5rem; } | |
| .scoreboard { | |
| background: var(--board); | |
| border: 1px solid rgba(255,255,255,.08); | |
| border-radius: 12px; padding: 14px 16px; | |
| text-align: center; | |
| } | |
| .sb-name { font-size: 13px; font-weight: 600; color: var(--muted); margin-bottom: 6px; } | |
| .sb-score { font-family: 'Bebas Neue', sans-serif; font-size: 36px; letter-spacing: .04em; color: var(--gold); line-height: 1; margin-bottom: 10px; } | |
| .sb-btns { display: flex; gap: 6px; justify-content: center; } | |
| .sb-btn { | |
| padding: 5px 12px; border-radius: 6px; border: none; | |
| font-size: 12px; font-weight: 700; cursor: pointer; | |
| font-family: 'Inter', sans-serif; transition: opacity .12s; | |
| } | |
| .sb-btn:hover { opacity: .85; } | |
| .sb-minus { background: var(--red); color: #fff; } | |
| .sb-plus { background: var(--green); color: #fff; } | |
| /* ── Board ── */ | |
| .board { display: grid; gap: 6px; } | |
| .board-4 { grid-template-columns: repeat(4, 1fr); } | |
| .board-6 { grid-template-columns: repeat(6, 1fr); } | |
| .cat-header { | |
| background: var(--cell); | |
| border-radius: 8px; padding: 14px 8px; | |
| text-align: center; | |
| font-size: 11px; font-weight: 700; | |
| text-transform: uppercase; letter-spacing: .08em; | |
| color: var(--text); line-height: 1.3; | |
| min-height: 64px; display: flex; | |
| align-items: center; justify-content: center; | |
| } | |
| .cell { | |
| background: var(--cell); | |
| border-radius: 8px; padding: 18px 8px; | |
| text-align: center; cursor: pointer; | |
| font-family: 'Bebas Neue', sans-serif; | |
| font-size: 26px; letter-spacing: .04em; | |
| color: var(--gold); min-height: 72px; | |
| display: flex; align-items: center; justify-content: center; | |
| transition: background .12s, transform .1s; | |
| position: relative; user-select: none; | |
| } | |
| .cell:hover:not(.used) { background: var(--cell-h); transform: scale(1.02); } | |
| .cell.used { background: var(--done); color: var(--done-t); cursor: default; } | |
| .cell.used:hover { transform: none; } | |
| .cell.daily-double::after { | |
| content: '★'; | |
| position: absolute; top: 4px; right: 6px; | |
| font-size: 10px; color: var(--gold); opacity: .6; | |
| } | |
| /* ── Modal overlay ── */ | |
| .overlay { | |
| position: fixed; inset: 0; | |
| background: rgba(0,0,0,.72); | |
| display: flex; align-items: center; justify-content: center; | |
| z-index: 100; padding: 1rem; | |
| backdrop-filter: blur(3px); | |
| } | |
| .modal { | |
| background: var(--board); | |
| border: 1px solid rgba(255,255,255,.1); | |
| border-radius: 16px; padding: 2rem 2.5rem; | |
| max-width: 680px; width: 100%; | |
| text-align: center; | |
| } | |
| .modal-cat { | |
| display: inline-block; | |
| background: var(--gold); color: var(--navy); | |
| font-size: 11px; font-weight: 800; | |
| text-transform: uppercase; letter-spacing: .12em; | |
| padding: 4px 14px; border-radius: 20px; | |
| margin-bottom: 1rem; | |
| } | |
| .modal-val { | |
| font-family: 'Bebas Neue', sans-serif; | |
| font-size: 52px; letter-spacing: .04em; | |
| color: var(--gold); line-height: 1; | |
| margin-bottom: 1rem; | |
| } | |
| .modal-clue { | |
| font-size: 18px; font-weight: 500; | |
| color: var(--text); line-height: 1.6; | |
| margin-bottom: 1.5rem; | |
| } | |
| .modal-answer { | |
| font-size: 17px; font-weight: 700; | |
| color: var(--teal); margin-bottom: 1.5rem; | |
| padding: 12px 20px; | |
| background: rgba(42,157,143,.12); | |
| border: 1px solid rgba(42,157,143,.3); | |
| border-radius: 10px; | |
| } | |
| .modal-timer { | |
| font-family: 'Bebas Neue', sans-serif; | |
| font-size: 48px; letter-spacing: .06em; | |
| margin-bottom: 1rem; | |
| transition: color .3s; | |
| } | |
| .timer-green { color: #4caf50; } | |
| .timer-amber { color: var(--gold); } | |
| .timer-red { color: var(--red); } | |
| .modal-btns { display: flex; flex-wrap: wrap; gap: 10px; justify-content: center; } | |
| .mbtn { | |
| padding: 11px 24px; border-radius: 10px; border: none; | |
| font-size: 14px; font-weight: 700; cursor: pointer; | |
| font-family: 'Inter', sans-serif; transition: opacity .12s, transform .1s; | |
| letter-spacing: .02em; | |
| } | |
| .mbtn:hover { opacity: .85; } | |
| .mbtn:active { transform: scale(.97); } | |
| .mbtn-show { background: var(--teal); color: #fff; } | |
| .mbtn-close { background: var(--red); color: #fff; } | |
| .mbtn-gold { background: var(--gold); color: var(--navy); } | |
| /* ── DD modal ── */ | |
| .dd-banner { | |
| font-family: 'Bebas Neue', sans-serif; | |
| font-size: 36px; letter-spacing: .1em; | |
| color: var(--gold); margin-bottom: .5rem; | |
| } | |
| .dd-sub { font-size: 14px; color: var(--muted); margin-bottom: 1.5rem; } | |
| .dd-team-grid { display: grid; grid-template-columns: repeat(3,1fr); gap: 10px; margin-bottom: 1.5rem; } | |
| .dd-team-box { background: rgba(255,255,255,.05); border: 1px solid rgba(255,255,255,.1); border-radius: 10px; padding: 12px; } | |
| .dd-team-name { font-size: 12px; font-weight: 600; color: var(--muted); margin-bottom: 4px; } | |
| .dd-team-bal { font-size: 13px; color: var(--text); margin-bottom: 8px; } | |
| .dd-team-box input { | |
| width: 100%; padding: 8px 10px; | |
| background: rgba(255,255,255,.08); | |
| border: 1px solid rgba(255,255,255,.12); | |
| border-radius: 6px; color: var(--text); | |
| font-size: 14px; font-weight: 700; text-align: center; | |
| font-family: 'Inter', sans-serif; outline: none; | |
| } | |
| /* ── Final Jeopardy ── */ | |
| .fj-wrap { max-width: 900px; margin: 0 auto; } | |
| .fj-header { | |
| font-family: 'Bebas Neue', sans-serif; | |
| font-size: 42px; letter-spacing: .08em; | |
| color: var(--gold); text-align: center; | |
| margin-bottom: .5rem; | |
| } | |
| .fj-cat { font-size: 18px; color: var(--text); text-align: center; margin-bottom: .5rem; } | |
| .fj-instr { font-size: 13px; color: var(--muted); text-align: center; margin-bottom: 1.75rem; } | |
| .fj-panel { | |
| background: var(--board); | |
| border: 1px solid rgba(255,255,255,.08); | |
| border-radius: 16px; padding: 2rem; | |
| } | |
| .fj-team-grid { display: grid; grid-template-columns: repeat(3,1fr); gap: 12px; margin-bottom: 1.75rem; } | |
| .fj-team-box { background: rgba(255,255,255,.05); border: 1px solid rgba(255,255,255,.1); border-radius: 12px; padding: 16px; text-align: center; } | |
| .fj-team-name { font-size: 13px; font-weight: 700; color: var(--text); margin-bottom: 4px; } | |
| .fj-team-bal { font-size: 12px; color: var(--muted); margin-bottom: 10px; } | |
| .fj-team-box input { | |
| width: 100%; padding: 9px 12px; text-align: center; | |
| background: rgba(255,255,255,.08); | |
| border: 1px solid rgba(255,255,255,.12); | |
| border-radius: 8px; color: var(--text); | |
| font-size: 16px; font-weight: 700; | |
| font-family: 'Inter', sans-serif; outline: none; | |
| transition: border-color .15s; margin-bottom: 8px; | |
| } | |
| .fj-team-box input:focus { border-color: var(--gold); } | |
| .fj-team-box textarea { | |
| width: 100%; padding: 9px 12px; | |
| background: rgba(255,255,255,.08); | |
| border: 1px solid rgba(255,255,255,.12); | |
| border-radius: 8px; color: var(--text); | |
| font-size: 13px; font-family: 'Inter', sans-serif; | |
| outline: none; resize: none; height: 70px; | |
| transition: border-color .15s; | |
| } | |
| .fj-team-box textarea:focus { border-color: var(--teal); } | |
| .fj-team-box textarea::placeholder { color: rgba(255,255,255,.25); } | |
| .fj-center { text-align: center; } | |
| .fj-timer { | |
| font-family: 'Bebas Neue', sans-serif; | |
| font-size: 56px; letter-spacing: .06em; | |
| margin-bottom: 1rem; transition: color .3s; | |
| } | |
| .fj-clue { font-size: 17px; color: var(--text); line-height: 1.6; margin-bottom: 1.25rem; max-width: 700px; margin-left: auto; margin-right: auto; } | |
| .fj-answer { | |
| font-size: 16px; font-weight: 700; color: var(--teal); | |
| padding: 12px 20px; border-radius: 10px; | |
| background: rgba(42,157,143,.12); | |
| border: 1px solid rgba(42,157,143,.3); | |
| margin-bottom: 1.5rem; display: inline-block; | |
| } | |
| .fj-bid-label { font-size: 11px; color: var(--muted); text-transform: uppercase; letter-spacing: .1em; margin-bottom: 4px; } | |
| </style> | |
| </head> | |
| <body> | |
| <!-- ═══════════ SETUP ═══════════ --> | |
| <div id="setup-screen"> | |
| <div class="setup-title">NetSuite AI<br>Jeopardy!</div> | |
| <div class="setup-sub">Enter team names to begin</div> | |
| <div class="setup-label">Team names</div> | |
| <div class="team-inputs"> | |
| <div class="team-row"> | |
| <div class="team-dot" style="background:#f0b429"></div> | |
| <input id="t1" type="text" placeholder="Team Alpha" value="Team Alpha"/> | |
| </div> | |
| <div class="team-row"> | |
| <div class="team-dot" style="background:#2a9d8f"></div> | |
| <input id="t2" type="text" placeholder="Team Beta" value="Team Beta"/> | |
| </div> | |
| <div class="team-row"> | |
| <div class="team-dot" style="background:#e63946"></div> | |
| <input id="t3" type="text" placeholder="Team Gamma" value="Team Gamma"/> | |
| </div> | |
| </div> | |
| <button class="setup-btn" onclick="startGame()">Start Game</button> | |
| </div> | |
| <!-- ═══════════ GAME ═══════════ --> | |
| <div id="game-screen"> | |
| <div class="game-title">NetSuite AI Jeopardy!</div> | |
| <div class="round-tabs"> | |
| <button class="rtab active" id="tab-r1" onclick="showRound(1)">Round 1</button> | |
| <button class="rtab" id="tab-r2" onclick="showRound(2)">Round 2</button> | |
| <button class="rtab" id="tab-fj" onclick="showRound('fj')">Final Jeopardy</button> | |
| <button class="rtab reset" onclick="resetGame()">Reset Game</button> | |
| </div> | |
| <div class="scoreboards" id="scoreboards"></div> | |
| <div id="board-area"></div> | |
| </div> | |
| <!-- ═══════════ QUESTION MODAL ═══════════ --> | |
| <div class="overlay" id="q-overlay" style="display:none"> | |
| <div class="modal" id="q-modal"> | |
| <div class="modal-cat" id="m-cat"></div> | |
| <div class="modal-val" id="m-val"></div> | |
| <div class="modal-clue" id="m-clue"></div> | |
| <div class="modal-timer" id="m-timer"></div> | |
| <div id="m-answer" style="display:none" class="modal-answer"></div> | |
| <div class="modal-btns" id="m-btns"></div> | |
| </div> | |
| </div> | |
| <!-- ═══════════ DAILY DOUBLE MODAL ═══════════ --> | |
| <div class="overlay" id="dd-overlay" style="display:none"> | |
| <div class="modal" id="dd-modal"> | |
| <div class="dd-banner">⭐ Daily Double!</div> | |
| <div class="dd-sub">Enter each team's wager before revealing the question</div> | |
| <div class="dd-team-grid" id="dd-wager-grid"></div> | |
| <div class="modal-btns"> | |
| <button class="mbtn mbtn-gold" onclick="revealDDQuestion()">Reveal Question</button> | |
| </div> | |
| </div> | |
| </div> | |
| <script> | |
| /* ═══════════════════════════════════════════ | |
| DATA | |
| ═══════════════════════════════════════════ */ | |
| const ROUNDS = { | |
| 1: { | |
| categories: [ | |
| "AI Fundamentals", | |
| "The AI Stack", | |
| "MCP & Protocols", | |
| "Human-in-the-Loop" | |
| ], | |
| questions: [ | |
| // AI Fundamentals — $200,$400,$600,$800,$1000 | |
| [ | |
| { clue: "The property of an AI system where the same input always produces the same output — like a SuiteFlow approval rule.", answer: "What is deterministic?" }, | |
| { clue: "LLM-based systems are described as this — context-aware and flexible, but outputs may vary for the same input.", answer: "What is non-deterministic (or probabilistic)?" }, | |
| { clue: "The unit of text — roughly one word — that determines the cost and speed of every LLM input and output.", answer: "What is a token?" }, | |
| { clue: "This technique reduces AI hallucination by fetching relevant source data from a knowledge base before the model generates its response.", answer: "What is RAG — Retrieval-Augmented Generation?" }, | |
| { clue: "The model's 'working memory' — the maximum amount of text it can read and reason over in a single response.", answer: "What is a context window?" } | |
| ], | |
| // The AI Stack — $200,$400,$600,$800,$1000 | |
| [ | |
| { clue: "The bottom layer of the AI stack — ERP, CRM, and HCM systems — described as the source of truth for all AI reasoning.", answer: "What is the Data & Systems layer?" }, | |
| { clue: "The AI stack layer containing SuiteScript, connectors, and MCP services — what the model can call to act on real data.", answer: "What is the Tools & APIs layer?" }, | |
| { clue: "The layer where LLMs, rules engines, and policies live — responsible for generating output.", answer: "What is the Reasoning & Models layer?" }, | |
| { clue: "The top layer of the AI stack — home to task execution, orchestration, and guardrails — and where agents live.", answer: "What is the AI Automation layer?" }, | |
| { clue: "This is why a company with fragmented data across five disconnected systems has a harder AI problem than one on a unified ERP.", answer: "What is: agents are only as reliable as the data layer beneath them (no single source of truth)?" } | |
| ], | |
| // MCP & Protocols — $200,$400,$600,$800,$1000 | |
| [ | |
| { clue: "MCP stands for this — an open, industry-standard interface that lets AI models connect to external tools consistently.", answer: "What is Model Context Protocol?" }, | |
| { clue: "Why MCP is described as 'the plug, not the thinking.'", answer: "What is: MCP provides the connection interface — reasoning and decision-making happen in the model layer, not in MCP itself?" }, | |
| { clue: "The correct answer when a prospect asks whether adopting NetSuite's MCP-based AI Connector will lock them into Oracle's infrastructure.", answer: "What is no — MCP is an open standard, not proprietary to Oracle or NetSuite?" }, | |
| { clue: "MCP serves as the foundation of this NetSuite capability that enables custom agentic-style workflows today.", answer: "What is the NetSuite AI Connector?" }, | |
| { clue: "This is the honest answer when a customer asks 'Is MCP required to use AI in NetSuite?'", answer: "What is no — MCP is not required; it enables the AI Connector ecosystem but AI features work without it?" } | |
| ], | |
| // Human-in-the-Loop — $200,$400,$600,$800,$1000 | |
| [ | |
| { clue: "The HITL category where AI acts fully autonomously — appropriate for low-risk, high-volume tasks like categorizing invoices.", answer: "What is Automate?" }, | |
| { clue: "The HITL pattern where AI generates a recommendation or draft, but no action executes until a human explicitly approves it.", answer: "What is Suggest and Confirm?" }, | |
| { clue: "Three examples of actions that must always remain in the 'Always Human' category — AI must never act alone on these.", answer: "What are employee data changes, GL overrides, and bank detail updates (accept any two)?" }, | |
| { clue: "In a SuiteAgents workflow, this is why the agent asks for confirmation before sending a collections email — even though it retrieved the data autonomously.", answer: "What is: sending an email is an action tool that changes state, requiring human approval under HITL?" }, | |
| { clue: "The most important reason a finance leader accepts AI in their ERP workflow — it comes down to this design principle.", answer: "What is deterministic controls around probabilistic reasoning (humans stay in the loop for high-risk decisions)?" } | |
| ] | |
| ], | |
| dailyDoubles: [[0, 4], [3, 2]] | |
| }, | |
| 2: { | |
| categories: [ | |
| "Agentic Levels", | |
| "Workflow Spectrum", | |
| "NetSuite AI Features", | |
| "Positioning & Objections", | |
| "Structured vs Unstructured", | |
| "SuiteAgents" | |
| ], | |
| questions: [ | |
| // Agentic Levels — $400,$800,$1200,$1600,$2000 | |
| [ | |
| { clue: "The AI maturity level covering Q&A and knowledge retrieval — where SuiteAnswers and Ask Oracle operate.", answer: "What is Answering (Level 1)?" }, | |
| { clue: "The maturity level covering drafting, summarizing, and suggesting — where the GenAI API and Exception Management sit.", answer: "What is Assisting (Level 2)?" }, | |
| { clue: "The maturity level for tool use, writebacks, and transactions — where Ask Oracle and AI Connector operate today.", answer: "What is Acting (Level 3)?" }, | |
| { clue: "The maturity level SuiteAgents targets — orchestrating multi-step workflows across multiple systems.", answer: "What is Coordinating (Level 4)?" }, | |
| { clue: "The highest rung on the AI maturity ladder — where the system learns from outcomes and refines its own behavior — currently described as emerging.", answer: "What is Optimizing (Level 5)?" } | |
| ], | |
| // Workflow Spectrum — $400,$800,$1200,$1600,$2000 | |
| [ | |
| { clue: "In the transportation analogy, a train on fixed tracks maps to this type of workflow.", answer: "What is a traditional deterministic workflow?" }, | |
| { clue: "A GPS app that reroutes around traffic but always heads to the same destination — the analogy for this workflow type.", answer: "What is an AI-enhanced workflow?" }, | |
| { clue: "A self-driving car where you only set the goal maps to this — the highest level of the workflow spectrum.", answer: "What is a fully agentic workflow?" }, | |
| { clue: "In an AI-enhanced workflow, this tool still owns the overall sequence even when AI steps are inserted.", answer: "What is SuiteFlow?" }, | |
| { clue: "The single word that best describes what an agent does at runtime that a pre-scripted AI workflow cannot — it makes this.", answer: "What is a decision (or: dynamically determines the next step)?" } | |
| ], | |
| // NetSuite AI Features — $400,$800,$1200,$1600,$2000 | |
| [ | |
| { clue: "This NetSuite feature lets users ask business questions in plain English and get answers from their own ERP data.", answer: "What is Ask Oracle?" }, | |
| { clue: "The NetSuite AI capability that automatically surfaces invoice mismatches, duplicate records, and payment anomalies for finance teams.", answer: "What is AI Exception Management?" }, | |
| { clue: "The NetSuite tool that lets developers embed custom generative AI capabilities directly into scripts and workflows.", answer: "What is the GenAI API?" }, | |
| { clue: "This upcoming NetSuite tool will let customers build and configure their own AI agents — no deep coding required.", answer: "What is Agent Studio?" }, | |
| { clue: "The NetSuite capability coming in NetSuite Next that orchestrates multi-step agentic workflows — the subject of this entire training.", answer: "What is SuiteAgents?" } | |
| ], | |
| // Positioning & Objections — $400,$800,$1200,$1600,$2000 | |
| [ | |
| { clue: "The first question to ask any prospect who opens with 'We want agentic AI' — before demoing anything.", answer: "What is: 'What specific problem are you trying to solve?'" }, | |
| { clue: "Why a prospect's concern about vendor lock-in is directly answered by confirming MCP's status as this.", answer: "What is an open, industry-standard protocol (not proprietary to Oracle)?" }, | |
| { clue: "The positioning phrase that reassures a CFO or auditor that NetSuite AI doesn't sacrifice control for flexibility.", answer: "What is: deterministic controls around probabilistic reasoning?" }, | |
| { clue: "When a skeptical buyer asks 'How do you stop the AI from making things up in my financial data?', this is the technical answer.", answer: "What is RAG — the AI retrieves structured data from NetSuite before generating, grounding the output in real records?" }, | |
| { clue: "The career-limiting risk of overselling AI autonomy before an implementation — what happens post-go-live.", answer: "What is: the customer loses trust when the system requires more human involvement than was promised?" } | |
| ], | |
| // Structured vs Unstructured — $400,$800,$1200,$1600,$2000 | |
| [ | |
| { clue: "Why ERP data — rows, schemas, defined fields — is particularly well-suited for AI compared to emails and PDFs.", answer: "What is: structured data is deterministic, grounded, and auditable — outputs can be traced to source records?" }, | |
| { clue: "What AI must do with unstructured inputs like emails or PDFs that it does not need to do with structured ERP data.", answer: "What is interpret (or: infer meaning using probabilistic reasoning)?" }, | |
| { clue: "The type of data where 'the same document can yield different outputs' — a key governance risk to explain to customers.", answer: "What is unstructured data?" }, | |
| { clue: "This is why an AI agent working from a NetSuite invoice record produces a more auditable output than one working from a scanned PDF of the same invoice.", answer: "What is: the ERP record is structured and traceable; the PDF requires inference to interpret?" }, | |
| { clue: "The term for grounding AI responses in real, retrieved data — the technique that makes structured ERP data NetSuite's biggest AI advantage.", answer: "What is RAG — Retrieval-Augmented Generation?" } | |
| ], | |
| // SuiteAgents — $400,$800,$1200,$1600,$2000 | |
| [ | |
| { clue: "In a SuiteAgents cashflow workflow, these tools retrieve AP balances, AR data, and forecasts — without requiring human confirmation.", answer: "What are read tools?" }, | |
| { clue: "The type of SuiteAgents tool that sends emails, updates records, or triggers transactions — and requires human approval before executing.", answer: "What are action tools?" }, | |
| { clue: "What SuiteAgents does after autonomously gathering data and synthesizing a recommendation — before taking any consequential action.", answer: "What is: requests human confirmation (human-in-the-loop gate)?" }, | |
| { clue: "The honest answer when a customer asks whether SuiteAgents is available to deploy today — accurate and commercially positioned.", answer: "What is: SuiteAgents is coming in NetSuite Next; AI Connector enables custom agentic flows today; HITL controls are part of the design?" }, | |
| { clue: "SuiteAgents sits at Level 4 of the maturity ladder, but this is what separates it from a Level 3 'Acting' capability.", answer: "What is: SuiteAgents coordinates multi-step workflows across systems — not just a single tool call or transaction?" } | |
| ] | |
| ], | |
| dailyDoubles: [[5, 3], [3, 4]] | |
| } | |
| }; | |
| const FINAL_JEOPARDY = { | |
| category: "NetSuite AI Positioning", | |
| clue: "A prospect's CFO says: 'I'm open to AI in our ERP, but I need to know that my team stays in control of financial decisions and that every AI action is auditable.' Give the complete positioning response that addresses both concerns simultaneously — in one sentence.", | |
| answer: "What is: NetSuite uses deterministic controls around probabilistic reasoning — AI provides the contextual intelligence while rules-based guardrails ensure every action is auditable and high-risk decisions always require human approval?" | |
| }; | |
| const ROUND_VALUES = { 1: [200,400,600,800,1000], 2: [400,800,1200,1600,2000] }; | |
| const TIMER_SECS = 15; | |
| const FJ_TIMER_SECS = 30; | |
| /* ═══════════════════════════════════════════ | |
| STATE | |
| ═══════════════════════════════════════════ */ | |
| let teams = [], scores = [0,0,0], usedCells = {}, currentRound = 1; | |
| let timerInterval = null, timerVal = 0; | |
| let pendingCell = null, ddWagers = [0,0,0]; | |
| let fjPhase = 'wager'; // wager | question | result | |
| /* ═══════════════════════════════════════════ | |
| SETUP | |
| ═══════════════════════════════════════════ */ | |
| function startGame() { | |
| teams = [ | |
| document.getElementById('t1').value.trim() || 'Team Alpha', | |
| document.getElementById('t2').value.trim() || 'Team Beta', | |
| document.getElementById('t3').value.trim() || 'Team Gamma' | |
| ]; | |
| document.getElementById('setup-screen').style.display = 'none'; | |
| document.getElementById('game-screen').style.display = 'block'; | |
| renderScoreboards(); | |
| showRound(1); | |
| } | |
| function resetGame() { | |
| if (!confirm('Reset everything and start a new game?')) return; | |
| scores = [0,0,0]; | |
| usedCells = {}; | |
| fjPhase = 'wager'; | |
| document.getElementById('game-screen').style.display = 'none'; | |
| document.getElementById('setup-screen').style.display = 'block'; | |
| } | |
| /* ═══════════════════════════════════════════ | |
| SCOREBOARDS | |
| ═══════════════════════════════════════════ */ | |
| function renderScoreboards() { | |
| const el = document.getElementById('scoreboards'); | |
| const colors = ['#f0b429','#2a9d8f','#e63946']; | |
| el.innerHTML = teams.map((t,i) => ` | |
| <div class="scoreboard"> | |
| <div class="sb-name" style="color:${colors[i]}">${t}</div> | |
| <div class="sb-score" id="score-${i}">$${scores[i].toLocaleString()}</div> | |
| <div class="sb-btns"> | |
| <button class="sb-btn sb-minus" onclick="adjustScore(${i},-100)">−100</button> | |
| <button class="sb-btn sb-plus" onclick="adjustScore(${i},+100)">+100</button> | |
| </div> | |
| </div>`).join(''); | |
| } | |
| function adjustScore(i, delta) { | |
| scores[i] = Math.max(0, scores[i] + delta); | |
| document.getElementById('score-'+i).textContent = '$' + scores[i].toLocaleString(); | |
| } | |
| function awardScore(i, amount) { | |
| scores[i] = Math.max(0, scores[i] + amount); | |
| document.getElementById('score-'+i).textContent = '$' + scores[i].toLocaleString(); | |
| } | |
| /* ═══════════════════════════════════════════ | |
| ROUNDS | |
| ═══════════════════════════════════════════ */ | |
| function showRound(r) { | |
| currentRound = r; | |
| ['r1','r2','fj'].forEach(id => { | |
| document.getElementById('tab-'+id).classList.toggle('active', id === (r === 'fj' ? 'fj' : 'r'+r)); | |
| }); | |
| if (r === 'fj') { renderFinalJeopardy(); return; } | |
| renderBoard(r); | |
| } | |
| function renderBoard(r) { | |
| const data = ROUNDS[r]; | |
| const vals = ROUND_VALUES[r]; | |
| const cols = data.categories.length; | |
| const gridClass = cols === 4 ? 'board-4' : 'board-6'; | |
| // Build daily double set | |
| const ddSet = new Set(data.dailyDoubles.map(([c,q]) => `${c}-${q}`)); | |
| let html = `<div class="board ${gridClass}">`; | |
| // Category headers | |
| data.categories.forEach(cat => { | |
| html += `<div class="cat-header">${cat}</div>`; | |
| }); | |
| // Question rows | |
| vals.forEach((val, qi) => { | |
| data.categories.forEach((cat, ci) => { | |
| const key = `${r}-${ci}-${qi}`; | |
| const used = !!usedCells[key]; | |
| const isDD = ddSet.has(`${ci}-${qi}`); | |
| html += `<div class="cell${used?' used':''}${isDD?' daily-double':''}" | |
| onclick="${used ? '' : `openQuestion(${r},${ci},${qi})`}" | |
| data-key="${key}"> | |
| ${used ? '' : '$'+val.toLocaleString()} | |
| </div>`; | |
| }); | |
| }); | |
| html += '</div>'; | |
| document.getElementById('board-area').innerHTML = html; | |
| } | |
| /* ═══════════════════════════════════════════ | |
| QUESTION MODAL | |
| ═══════════════════════════════════════════ */ | |
| function openQuestion(r, ci, qi) { | |
| const key = `${r}-${ci}-${qi}`; | |
| if (usedCells[key]) return; | |
| const data = ROUNDS[r]; | |
| const ddSet = new Set(data.dailyDoubles.map(([c,q]) => `${c}-${q}`)); | |
| const isDD = ddSet.has(`${ci}-${qi}`); | |
| const val = ROUND_VALUES[r][qi]; | |
| const q = data.questions[ci][qi]; | |
| pendingCell = { r, ci, qi, key, val, q }; | |
| if (isDD) { | |
| showDailyDouble(); | |
| return; | |
| } | |
| showQuestionModal(q.clue, q.answer, data.categories[ci], val, []); | |
| } | |
| function showQuestionModal(clue, answer, cat, val, extraBtns) { | |
| document.getElementById('m-cat').textContent = cat.toUpperCase(); | |
| document.getElementById('m-val').textContent = '$' + val.toLocaleString(); | |
| document.getElementById('m-clue').textContent = clue; | |
| document.getElementById('m-answer').style.display = 'none'; | |
| document.getElementById('m-answer').textContent = answer; | |
| const btns = document.getElementById('m-btns'); | |
| btns.innerHTML = `<button class="mbtn mbtn-show" onclick="revealAnswer()">Show Answer</button>`; | |
| extraBtns.forEach(b => btns.innerHTML += b); | |
| startTimer('m-timer', TIMER_SECS); | |
| document.getElementById('q-overlay').style.display = 'flex'; | |
| } | |
| function revealAnswer() { | |
| stopTimer(); | |
| document.getElementById('m-answer').style.display = 'block'; | |
| document.getElementById('m-timer').style.display = 'none'; | |
| renderScoringBtns(); | |
| } | |
| function renderScoringBtns() { | |
| const { key, val } = pendingCell; | |
| const btns = document.getElementById('m-btns'); | |
| let html = ''; | |
| teams.forEach((t, i) => { | |
| html += `<button class="mbtn" style="background:var(--green);color:#fff" onclick="scoreTeam(${i},${val})">✓ ${t}</button>`; | |
| }); | |
| html += `<button class="mbtn mbtn-close" onclick="closeQuestion()">Close</button>`; | |
| btns.innerHTML = html; | |
| } | |
| function scoreTeam(i, amount) { | |
| awardScore(i, amount); | |
| closeQuestion(); | |
| } | |
| function closeQuestion() { | |
| stopTimer(); | |
| if (pendingCell) { | |
| usedCells[pendingCell.key] = true; | |
| // update cell on board | |
| const el = document.querySelector(`[data-key="${pendingCell.key}"]`); | |
| if (el) { el.classList.add('used'); el.textContent = ''; el.onclick = null; } | |
| pendingCell = null; | |
| } | |
| document.getElementById('q-overlay').style.display = 'none'; | |
| document.getElementById('m-timer').style.display = 'block'; | |
| } | |
| /* ═══════════════════════════════════════════ | |
| DAILY DOUBLE | |
| ═══════════════════════════════════════════ */ | |
| function showDailyDouble() { | |
| const grid = document.getElementById('dd-wager-grid'); | |
| grid.innerHTML = teams.map((t, i) => ` | |
| <div class="dd-team-box"> | |
| <div class="dd-team-name">${t}</div> | |
| <div class="dd-team-bal">Balance: $${scores[i].toLocaleString()}</div> | |
| <input type="number" id="dd-wager-${i}" placeholder="0" min="0" max="${Math.max(scores[i],pendingCell.val)}" value="0"/> | |
| </div>`).join(''); | |
| document.getElementById('dd-overlay').style.display = 'flex'; | |
| } | |
| function revealDDQuestion() { | |
| ddWagers = teams.map((_,i) => parseInt(document.getElementById('dd-wager-'+i).value)||0); | |
| document.getElementById('dd-overlay').style.display = 'none'; | |
| const { q, r, ci, val } = pendingCell; | |
| showQuestionModal(q.clue, q.answer, ROUNDS[r].categories[ci], val, []); | |
| // Override scoring buttons to use wagers | |
| const origReveal = window.revealAnswer; | |
| window.revealAnswer = function() { | |
| stopTimer(); | |
| document.getElementById('m-answer').style.display = 'block'; | |
| document.getElementById('m-timer').style.display = 'none'; | |
| const btns = document.getElementById('m-btns'); | |
| let html = ''; | |
| teams.forEach((t, i) => { | |
| if (ddWagers[i] > 0) { | |
| html += `<button class="mbtn" style="background:var(--green);color:#fff" onclick="scoreDDTeam(${i},true)">✓ ${t} +$${ddWagers[i].toLocaleString()}</button>`; | |
| html += `<button class="mbtn" style="background:var(--red);color:#fff" onclick="scoreDDTeam(${i},false)">✗ ${t} −$${ddWagers[i].toLocaleString()}</button>`; | |
| } | |
| }); | |
| html += `<button class="mbtn mbtn-close" onclick="closeQuestion()">Close</button>`; | |
| btns.innerHTML = html; | |
| window.revealAnswer = origReveal; | |
| }; | |
| } | |
| function scoreDDTeam(i, correct) { | |
| awardScore(i, correct ? ddWagers[i] : -ddWagers[i]); | |
| } | |
| /* ═══════════════════════════════════════════ | |
| FINAL JEOPARDY | |
| ═══════════════════════════════════════════ */ | |
| function renderFinalJeopardy() { | |
| let html = '<div class="fj-wrap">'; | |
| html += `<div class="fj-header">Final Jeopardy</div>`; | |
| html += `<div class="fj-cat">Category: ${FINAL_JEOPARDY.category}</div>`; | |
| if (fjPhase === 'wager') { | |
| html += `<div class="fj-instr">Teams must place their final wagers before seeing the question.</div>`; | |
| html += `<div class="fj-panel">`; | |
| html += `<div class="fj-team-grid">` + teams.map((t,i) => ` | |
| <div class="fj-team-box"> | |
| <div class="fj-team-name">${t}</div> | |
| <div class="fj-team-bal">Current: $${scores[i].toLocaleString()}</div> | |
| <div class="fj-bid-label">Wager</div> | |
| <input type="number" id="fj-wager-${i}" min="0" max="${scores[i]}" value="0" placeholder="0"/> | |
| </div>`).join('') + `</div>`; | |
| html += `<div class="fj-center"><button class="mbtn mbtn-gold" style="font-size:16px;padding:14px 40px" onclick="revealFJQuestion()">Reveal Final Question</button></div>`; | |
| html += `</div>`; | |
| } | |
| else if (fjPhase === 'question') { | |
| html += `<div class="fj-panel">`; | |
| html += `<div class="fj-center">`; | |
| html += `<div class="fj-timer ${getFJTimerClass()}" id="fj-timer">${timerVal}s</div>`; | |
| html += `<div class="fj-clue">${FINAL_JEOPARDY.clue}</div>`; | |
| html += `</div>`; | |
| html += `<div class="fj-team-grid">` + teams.map((t,i) => { | |
| const w = parseInt(document.getElementById('fj-wager-'+i)?.value)||fjWagers[i]||0; | |
| return `<div class="fj-team-box"> | |
| <div class="fj-team-name">${t}</div> | |
| <div class="fj-team-bal">Bid: $${w.toLocaleString()}</div> | |
| <textarea id="fj-ans-${i}" placeholder="Team's answer..."></textarea> | |
| </div>`; | |
| }).join('') + `</div>`; | |
| html += `<div class="fj-center"><button class="mbtn mbtn-close" style="font-size:15px;padding:12px 32px" onclick="revealFJAnswer()">Time's Up — Show Correct Answer</button></div>`; | |
| html += `</div>`; | |
| } | |
| else if (fjPhase === 'result') { | |
| html += `<div class="fj-panel">`; | |
| html += `<div class="fj-center"><div class="fj-clue">${FINAL_JEOPARDY.clue}</div>`; | |
| html += `<div class="fj-answer">${FINAL_JEOPARDY.answer}</div></div>`; | |
| html += `<div class="fj-team-grid">` + teams.map((t,i) => ` | |
| <div class="fj-team-box"> | |
| <div class="fj-team-name">${t}</div> | |
| <div class="fj-team-bal">Bid: $${fjWagers[i].toLocaleString()}</div> | |
| <div style="font-size:12px;color:var(--muted);margin-bottom:8px;font-style:italic">${fjAnswers[i]||'—'}</div> | |
| <button class="mbtn" style="background:var(--green);color:#fff;width:100%;margin-bottom:6px" onclick="scoreFJ(${i},true)">✓ Correct +$${fjWagers[i].toLocaleString()}</button> | |
| <button class="mbtn" style="background:var(--red);color:#fff;width:100%" onclick="scoreFJ(${i},false)">✗ Wrong −$${fjWagers[i].toLocaleString()}</button> | |
| </div>`).join('') + `</div>`; | |
| html += `</div>`; | |
| } | |
| html += '</div>'; | |
| document.getElementById('board-area').innerHTML = html; | |
| if (fjPhase === 'question') { | |
| startFJTimer(); | |
| } | |
| } | |
| let fjWagers = [0,0,0], fjAnswers = ['','','']; | |
| function getFJTimerClass() { | |
| if (timerVal > 20) return 'timer-green'; | |
| if (timerVal > 10) return 'timer-amber'; | |
| return 'timer-red'; | |
| } | |
| function revealFJQuestion() { | |
| fjWagers = teams.map((_,i) => parseInt(document.getElementById('fj-wager-'+i)?.value)||0); | |
| fjPhase = 'question'; | |
| renderFinalJeopardy(); | |
| } | |
| function startFJTimer() { | |
| timerVal = FJ_TIMER_SECS; | |
| clearInterval(timerInterval); | |
| timerInterval = setInterval(() => { | |
| timerVal--; | |
| const el = document.getElementById('fj-timer'); | |
| if (el) { | |
| el.textContent = timerVal + 's'; | |
| el.className = 'fj-timer ' + getFJTimerClass(); | |
| } | |
| if (timerVal <= 0) stopTimer(); | |
| }, 1000); | |
| } | |
| function revealFJAnswer() { | |
| stopTimer(); | |
| fjAnswers = teams.map((_,i) => document.getElementById('fj-ans-'+i)?.value||''); | |
| fjPhase = 'result'; | |
| renderFinalJeopardy(); | |
| } | |
| function scoreFJ(i, correct) { | |
| awardScore(i, correct ? fjWagers[i] : -fjWagers[i]); | |
| } | |
| /* ═══════════════════════════════════════════ | |
| TIMER | |
| ═══════════════════════════════════════════ */ | |
| function startTimer(elId, secs) { | |
| timerVal = secs; | |
| const el = document.getElementById(elId); | |
| el.textContent = secs + 's'; | |
| el.className = 'modal-timer timer-green'; | |
| clearInterval(timerInterval); | |
| timerInterval = setInterval(() => { | |
| timerVal--; | |
| el.textContent = timerVal + 's'; | |
| if (timerVal > 10) el.className = 'modal-timer timer-green'; | |
| else if (timerVal > 5) el.className = 'modal-timer timer-amber'; | |
| else el.className = 'modal-timer timer-red'; | |
| if (timerVal <= 0) stopTimer(); | |
| }, 1000); | |
| } | |
| function stopTimer() { clearInterval(timerInterval); timerInterval = null; } | |
| </script> | |
| </body> | |
| </html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment