StepWise-Math-AI / examples /001-visual-proof-probability-of-an-odd-sum.json
DreamyDetective's picture
feat: added application files
69ac033 verified
{
"appName": "VisualMath AI Export",
"exportedAt": "2025-11-25T00:42:01.682Z",
"input": {
"mode": "url",
"url": "https://cemc.uwaterloo.ca/sites/default/files/documents/2025/POTWC-25-D-N-10-P.html"
},
"concept": {
"conceptTitle": "Probability of an Odd Sum",
"educationalGoal": "Students will learn to calculate probabilities involving combinations and the parity of numbers by systematically identifying total and favorable outcomes.",
"explanation": "We are given five cards, numbered 1 through 5. If we randomly flip over three cards, what is the probability that the sum of the numbers on those three cards is odd? To solve this, we'll first categorize the cards by whether they are odd or even. Then, we'll explore the rules of addition for odd and even numbers to determine which combinations of three cards will result in an odd sum. Finally, we'll count all possible three-card combinations and the combinations that give an odd sum to calculate the probability.",
"steps": [
{
"stepTitle": "Categorize Cards by Parity",
"instruction": "Observe the five cards (1-5) and how they are separated into odd and even groups. Click 'Next' to continue.",
"visualFocus": "The initial five cards, with clear visual distinction/grouping for odd (1, 3, 5) and even (2, 4) numbers."
},
{
"stepTitle": "Understand Parity Sum Rules",
"instruction": "Drag and drop 'Odd' and 'Even' markers into the three slots to see how their sum's parity changes. Find the combinations that result in an 'Odd Sum'.",
"visualFocus": "Interactive display showing three input slots for 'Odd'/'Even' markers and a dynamic output for the 'Sum Parity'. Highlighting the resulting 'Odd Sum' outcome."
},
{
"stepTitle": "Identify Favorable Combinations",
"instruction": "Based on the parity rules you discovered, identify which specific combinations of three cards from our original set (1, 2, 3, 4, 5) will result in an odd sum. Click 'Next' to reveal them.",
"visualFocus": "All possible unique combinations of three cards from (1,2,3,4,5) are displayed. The combinations that result in an odd sum (e.g., O+O+O, O+E+E) will be highlighted."
},
{
"stepTitle": "Count Outcomes",
"instruction": "Count the total number of ways to choose 3 cards from 5 (Total Outcomes) and the number of ways that result in an odd sum (Favorable Outcomes).",
"visualFocus": "A visual display summarizing: 'Total Possible Combinations = C(5,3)' and 'Favorable Combinations (Odd Sum) = Counted from previous step'. Show the calculated numbers."
},
{
"stepTitle": "Calculate Probability",
"instruction": "Drag the 'Favorable Outcomes' and 'Total Outcomes' into the probability formula. See the final probability.",
"visualFocus": "A visual representation of the probability formula: 'Probability = Favorable Outcomes / Total Outcomes', with input fields for the numbers and a display for the calculated fractional and simplified probability."
}
],
"visualSpec": {
"elements": [
"cards_1_to_5: Rectangular card objects labeled 1, 2, 3, 4, 5.",
"parity_groups: Visually distinct containers/colors for odd (1, 3, 5) and even (2, 4) cards.",
"parity_markers: Draggable 'Odd' and 'Even' labels/icons.",
"sum_calculator_slots: Three empty rectangular slots to receive parity_markers.",
"sum_parity_display: Text area showing 'Odd Sum' or 'Even Sum' dynamically.",
"all_combinations_grid: A grid or list displaying all C(5,3) unique combinations of 3 cards.",
"favorable_highlight: A visual overlay or border to highlight combinations with an odd sum in the grid.",
"outcome_counters: Text labels displaying 'Total Outcomes: X' and 'Favorable Outcomes: Y'.",
"probability_formula: A visual fraction bar with input fields for numerator and denominator, and a result field."
],
"interactions": [
"next_button_click: User clicks a 'Next' button to advance through steps.",
"drag_parity_to_slot: User drags 'Odd'/'Even' markers into the sum_calculator_slots.",
"observe_sum_parity_update: System updates sum_parity_display based on user drag-and-drop.",
"click_to_reveal_favorable: User clicks a button to reveal highlighted favorable combinations.",
"drag_counts_to_formula: User drags numerical values from outcome_counters to probability_formula input fields."
],
"mathLogic": "1. Identify odd/even numbers: Odds = {1,3,5}, Evens = {2,4}. 2. Parity rules for sum of three numbers: O+O+O = O; O+E+E = O; O+O+E = E; E+E+E = E (not possible here). 3. Total combinations: C(n, k) = n! / (k!(n-k)!), specifically C(5,3) = 10. 4. Favorable combinations (odd sum): C(3 odd, 3 chosen) + C(3 odd, 1 chosen) * C(2 even, 2 chosen) = C(3,3) + C(3,1)*C(2,2) = 1 + 3*1 = 4. 5. Probability = Favorable Outcomes / Total Outcomes = 4/10 = 2/5."
}
},
"sourceCode": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <title>Probability of an Odd Sum</title>\n <style>\n :root {\n --bg-color: #0f172a;\n --panel-bg: #1e293b;\n --text-color: #e2e8f0;\n --accent-blue: #38bdf8;\n --accent-red: #fb7185;\n --accent-green: #4ade80;\n --accent-yellow: #facc15;\n --border-color: #334155;\n }\n\n * {\n box-sizing: border-box;\n margin: 0;\n padding: 0;\n font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;\n }\n\n body {\n background-color: var(--bg-color);\n color: var(--text-color);\n height: 100vh;\n display: flex;\n flex-direction: column;\n overflow: hidden;\n }\n\n /* Layout */\n header {\n padding: 1rem 2rem;\n border-bottom: 1px solid var(--border-color);\n display: flex;\n justify-content: space-between;\n align-items: center;\n background-color: var(--panel-bg);\n }\n\n h1 { font-size: 1.5rem; color: var(--accent-blue); }\n .step-indicator { font-size: 0.9rem; color: #94a3b8; }\n\n main {\n flex: 1;\n display: grid;\n grid-template-columns: 300px 1fr;\n gap: 0;\n overflow: hidden;\n }\n\n /* Sidebar for Instructions */\n .sidebar {\n background-color: #172033;\n border-right: 1px solid var(--border-color);\n padding: 2rem;\n display: flex;\n flex-direction: column;\n gap: 1rem;\n overflow-y: auto;\n }\n\n .sidebar h2 { font-size: 1.2rem; margin-bottom: 0.5rem; color: var(--accent-yellow); }\n .sidebar p { line-height: 1.6; font-size: 0.95rem; color: #cbd5e1; }\n .instruction-highlight { background: #334155; padding: 10px; border-radius: 6px; margin-top: 10px; border-left: 4px solid var(--accent-blue); }\n\n /* Visualization Area */\n .viz-container {\n background-color: var(--bg-color);\n position: relative;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n padding: 2rem;\n overflow-y: auto;\n }\n\n /* Footer Controls */\n footer {\n padding: 1rem 2rem;\n border-top: 1px solid var(--border-color);\n background-color: var(--panel-bg);\n display: flex;\n justify-content: space-between;\n align-items: center;\n }\n\n button {\n background-color: var(--accent-blue);\n color: #0f172a;\n border: none;\n padding: 0.75rem 1.5rem;\n border-radius: 6px;\n font-weight: bold;\n cursor: pointer;\n transition: transform 0.1s, opacity 0.2s;\n }\n\n button:hover { opacity: 0.9; }\n button:active { transform: scale(0.98); }\n button:disabled { background-color: #475569; color: #94a3b8; cursor: not-allowed; }\n\n /* Shared Components */\n .card {\n width: 60px;\n height: 80px;\n background: white;\n color: #0f172a;\n border-radius: 8px;\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 1.5rem;\n font-weight: bold;\n box-shadow: 0 4px 6px rgba(0,0,0,0.3);\n user-select: none;\n transition: all 0.3s ease;\n }\n \n .card.odd { border-bottom: 5px solid var(--accent-blue); }\n .card.even { border-bottom: 5px solid var(--accent-red); }\n\n .area-box {\n border: 2px dashed var(--border-color);\n border-radius: 12px;\n padding: 1rem;\n margin: 1rem;\n background: rgba(255,255,255,0.02);\n }\n\n /* Step 1 Styles */\n .sorting-area { display: flex; gap: 4rem; width: 100%; justify-content: center; margin-top: 2rem; }\n .group-container { display: flex; flex-direction: column; align-items: center; gap: 1rem; min-width: 150px; }\n .group-box { display: flex; gap: 10px; padding: 20px; background: rgba(56, 189, 248, 0.1); border-radius: 12px; min-height: 100px; border: 1px solid var(--border-color); }\n .group-box.even-box { background: rgba(251, 113, 133, 0.1); }\n\n /* Step 2 Styles */\n .drag-source { display: flex; gap: 20px; margin-bottom: 2rem; }\n .token {\n padding: 10px 20px;\n border-radius: 20px;\n font-weight: bold;\n cursor: grab;\n color: #0f172a;\n user-select: none;\n }\n .token.odd { background-color: var(--accent-blue); }\n .token.even { background-color: var(--accent-red); }\n \n .slots-container { display: flex; gap: 10px; align-items: center; }\n .plus-sign { font-size: 2rem; color: #64748b; }\n .slot {\n width: 100px;\n height: 60px;\n border: 2px dashed #64748b;\n border-radius: 8px;\n display: flex;\n align-items: center;\n justify-content: center;\n background: rgba(0,0,0,0.2);\n color: #64748b;\n }\n .slot.filled { border-style: solid; color: #0f172a; }\n .result-display { \n margin-top: 2rem; \n font-size: 1.5rem; \n padding: 1rem 2rem; \n background: #334155; \n border-radius: 8px; \n opacity: 0;\n transition: opacity 0.5s;\n }\n .result-display.visible { opacity: 1; }\n\n /* Step 3 Styles */\n .grid-container {\n display: grid;\n grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));\n gap: 15px;\n width: 100%;\n max-width: 800px;\n }\n .combo-item {\n background: var(--panel-bg);\n border: 1px solid var(--border-color);\n padding: 10px;\n border-radius: 8px;\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 5px;\n transition: all 0.3s;\n opacity: 0.6;\n }\n .combo-item.highlight {\n border-color: var(--accent-green);\n box-shadow: 0 0 15px rgba(74, 222, 128, 0.2);\n opacity: 1;\n transform: scale(1.05);\n }\n .mini-cards { display: flex; gap: 5px; }\n .mini-card {\n width: 25px;\n height: 35px;\n background: #fff;\n color: #000;\n display: flex;\n justify-content: center;\n align-items: center;\n font-weight: bold;\n border-radius: 3px;\n font-size: 0.8rem;\n }\n .combo-sum { font-size: 0.8rem; color: #94a3b8; }\n .highlight .combo-sum { color: var(--accent-green); font-weight: bold; }\n\n /* Step 4 Styles */\n .count-container { display: flex; gap: 3rem; align-items: center; margin-top: 2rem; }\n .count-box {\n background: var(--panel-bg);\n padding: 2rem;\n border-radius: 12px;\n text-align: center;\n border: 1px solid var(--border-color);\n position: relative;\n }\n .count-number { font-size: 3rem; font-weight: bold; margin: 10px 0; }\n .count-label { color: #94a3b8; font-size: 0.9rem; }\n .count-draggable { \n cursor: grab; \n background: var(--accent-yellow); \n color: black; \n width: 40px; \n height: 40px; \n border-radius: 50%; \n display: flex; \n align-items: center; \n justify-content: center;\n font-weight: bold;\n position: absolute;\n top: -10px;\n right: -10px;\n box-shadow: 0 2px 5px rgba(0,0,0,0.5);\n }\n\n /* Step 5 Styles */\n .formula-area {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 2rem;\n font-size: 1.5rem;\n }\n .fraction-builder {\n display: flex;\n align-items: center;\n gap: 20px;\n }\n .fraction {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 10px;\n }\n .fraction-line { width: 100px; height: 4px; background: white; }\n .drop-zone {\n width: 60px;\n height: 60px;\n border: 2px dashed #64748b;\n border-radius: 8px;\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 1.2rem;\n background: rgba(255,255,255,0.05);\n transition: all 0.2s;\n }\n .drop-zone.hover { border-color: var(--accent-green); background: rgba(74, 222, 128, 0.1); }\n .final-result { font-size: 2rem; color: var(--accent-green); font-weight: bold; opacity: 0; }\n\n @media (max-width: 768px) {\n main { grid-template-columns: 1fr; grid-template-rows: auto 1fr; }\n .sidebar { padding: 1rem; max-height: 200px; }\n .viz-container { padding: 1rem; }\n .card { width: 40px; height: 56px; font-size: 1rem; }\n .sorting-area { gap: 1rem; flex-direction: column; align-items: center; }\n }\n </style>\n</head>\n<body>\n\n <header>\n <h1>Probability: Odd Sum</h1>\n <div class=\"step-indicator\">Step <span id=\"step-num\">1</span> of 5</div>\n </header>\n\n <main>\n <div class=\"sidebar\">\n <h2 id=\"step-title\">Loading...</h2>\n <p id=\"step-desc\">Loading instructions...</p>\n <div id=\"step-instruction-extra\" class=\"instruction-highlight\"></div>\n </div>\n <div class=\"viz-container\" id=\"viz-root\">\n <!-- Content injected by JS -->\n </div>\n </main>\n\n <footer>\n <button id=\"prev-btn\" disabled>Previous</button>\n <button id=\"next-btn\">Next</button>\n </footer>\n\n <script>\n // --- Application State ---\n const state = {\n step: 0,\n totalSteps: 5,\n cards: [1, 2, 3, 4, 5],\n paritySlotValues: [null, null, null], // For Step 2\n probabilityInputs: { num: null, den: null } // For Step 5\n };\n\n // --- Data ---\n const stepsData = [\n {\n title: \"1. Categorize Cards by Parity\",\n desc: \"We have 5 cards numbered 1 through 5. To analyze the sum, we first need to separate them into Odd and Even numbers.\",\n extra: \"Observe how the numbers are grouped. Odds are 1, 3, 5. Evens are 2, 4.\",\n render: renderStep1\n },\n {\n title: \"2. Understand Parity Sum Rules\",\n desc: \"How do we get an ODD sum from three numbers? Let's experiment. Drag 'Odd' and 'Even' markers into the slots.\",\n extra: \"Hint: Adding two Odd numbers equals an Even number (1+3=4). Adding an Even to an Odd keeps it Odd.\",\n render: renderStep2\n },\n {\n title: \"3. Identify Favorable Combinations\",\n desc: \"We are choosing 3 cards. Here are all possible combinations (Total = 10).\",\n extra: \"The combinations resulting in an Odd Sum are highlighted. These are our Favorable Outcomes.\",\n render: renderStep3\n },\n {\n title: \"4. Count Outcomes\",\n desc: \"Let's formalize the count. We found the Total Outcomes using Combinations C(5,3) and counted the Favorable ones.\",\n extra: \"Favorable = 4, Total = 10.\",\n render: renderStep4\n },\n {\n title: \"5. Calculate Probability\",\n desc: \"Probability is the ratio of Favorable Outcomes to Total Outcomes.\",\n extra: \"Drag the 'Favorable' count to the top and 'Total' count to the bottom.\",\n render: renderStep5\n }\n ];\n\n // --- DOM Elements ---\n const dom = {\n stepNum: document.getElementById('step-num'),\n title: document.getElementById('step-title'),\n desc: document.getElementById('step-desc'),\n extra: document.getElementById('step-instruction-extra'),\n viz: document.getElementById('viz-root'),\n prevBtn: document.getElementById('prev-btn'),\n nextBtn: document.getElementById('next-btn')\n };\n\n // --- Initialization ---\n function init() {\n updateUI();\n dom.nextBtn.addEventListener('click', () => changeStep(1));\n dom.prevBtn.addEventListener('click', () => changeStep(-1));\n }\n\n function changeStep(delta) {\n const newStep = state.step + delta;\n if (newStep >= 0 && newStep < state.totalSteps) {\n state.step = newStep;\n updateUI();\n }\n }\n\n function updateUI() {\n const current = stepsData[state.step];\n \n // Update Sidebar\n dom.stepNum.textContent = state.step + 1;\n dom.title.textContent = current.title;\n dom.desc.textContent = current.desc;\n dom.extra.textContent = current.extra;\n\n // Update Buttons\n dom.prevBtn.disabled = state.step === 0;\n dom.nextBtn.disabled = state.step === state.totalSteps - 1;\n if(state.step === 1 || state.step === 4) {\n // In interactive steps, we might optionally disable next until complete, \n // but for exploration flow, we keep it open or just style it.\n // keeping enabled for smooth flow as requested.\n }\n\n // Render Visualization\n dom.viz.innerHTML = ''; // Clear previous\n current.render(dom.viz);\n }\n\n // --- Step 1: Categorize ---\n function renderStep1(container) {\n const wrapper = document.createElement('div');\n wrapper.style.width = \"100%\";\n \n // Initial Lineup\n const initialTitle = document.createElement('h3');\n initialTitle.textContent = \"Original Set\";\n initialTitle.style.textAlign = 'center';\n initialTitle.style.marginBottom = '1rem';\n wrapper.appendChild(initialTitle);\n\n const initialSet = document.createElement('div');\n initialSet.style.display = 'flex';\n initialSet.style.justifyContent = 'center';\n initialSet.style.gap = '10px';\n initialSet.style.marginBottom = '2rem';\n\n state.cards.forEach(num => {\n const card = createCard(num);\n initialSet.appendChild(card);\n });\n wrapper.appendChild(initialSet);\n\n // Split Groups\n const sortingArea = document.createElement('div');\n sortingArea.className = 'sorting-area';\n\n // Odd Box\n const oddGroup = document.createElement('div');\n oddGroup.className = 'group-container';\n oddGroup.innerHTML = '<div style=\"color: var(--accent-blue); font-weight:bold;\">ODD (1, 3, 5)</div>';\n const oddBox = document.createElement('div');\n oddBox.className = 'group-box';\n oddBox.id = 'odd-box';\n oddGroup.appendChild(oddBox);\n\n // Even Box\n const evenGroup = document.createElement('div');\n evenGroup.className = 'group-container';\n evenGroup.innerHTML = '<div style=\"color: var(--accent-red); font-weight:bold;\">EVEN (2, 4)</div>';\n const evenBox = document.createElement('div');\n evenBox.className = 'group-box even-box';\n evenBox.id = 'even-box';\n evenGroup.appendChild(evenBox);\n\n sortingArea.appendChild(oddGroup);\n sortingArea.appendChild(evenGroup);\n wrapper.appendChild(sortingArea);\n container.appendChild(wrapper);\n\n // Animation Logic\n setTimeout(() => {\n state.cards.forEach(num => {\n const card = createCard(num);\n card.style.opacity = '0';\n card.style.transform = 'translateY(-20px)';\n if (num % 2 !== 0) {\n card.classList.add('odd');\n document.getElementById('odd-box').appendChild(card);\n } else {\n card.classList.add('even');\n document.getElementById('even-box').appendChild(card);\n }\n // Staggered fade in\n setTimeout(() => {\n card.style.opacity = '1';\n card.style.transform = 'translateY(0)';\n }, num * 200);\n });\n }, 500);\n }\n\n // --- Step 2: Parity Rules ---\n function renderStep2(container) {\n state.paritySlotValues = [null, null, null];\n\n const wrapper = document.createElement('div');\n wrapper.style.display = 'flex';\n wrapper.style.flexDirection = 'column';\n wrapper.style.alignItems = 'center';\n\n // Source Tokens\n const source = document.createElement('div');\n source.className = 'drag-source';\n \n const oddToken = createDraggableToken('Odd', 'odd');\n const evenToken = createDraggableToken('Even', 'even');\n source.appendChild(oddToken);\n source.appendChild(evenToken);\n wrapper.appendChild(source);\n\n const hint = document.createElement('div');\n hint.textContent = \"Drag tokens to slots below:\";\n hint.style.marginBottom = '1rem';\n wrapper.appendChild(hint);\n\n // Slots\n const slotsContainer = document.createElement('div');\n slotsContainer.className = 'slots-container';\n\n for (let i = 0; i < 3; i++) {\n const slot = document.createElement('div');\n slot.className = 'slot';\n slot.dataset.index = i;\n slot.textContent = \"Drop\";\n \n // Drop Events\n slot.addEventListener('dragover', e => e.preventDefault());\n slot.addEventListener('drop', handleDropStep2);\n \n slotsContainer.appendChild(slot);\n \n if (i < 2) {\n const plus = document.createElement('div');\n plus.className = 'plus-sign';\n plus.textContent = '+';\n slotsContainer.appendChild(plus);\n }\n }\n wrapper.appendChild(slotsContainer);\n\n // Result\n const resultDisplay = document.createElement('div');\n resultDisplay.id = 'parity-result';\n resultDisplay.className = 'result-display';\n resultDisplay.textContent = \"Sum Parity: ?\";\n wrapper.appendChild(resultDisplay);\n\n container.appendChild(wrapper);\n }\n\n function createDraggableToken(text, type) {\n const el = document.createElement('div');\n el.className = `token ${type}`;\n el.textContent = text;\n el.draggable = true;\n el.addEventListener('dragstart', (e) => {\n e.dataTransfer.setData('text/plain', type);\n e.dataTransfer.effectAllowed = 'copy';\n });\n return el;\n }\n\n function handleDropStep2(e) {\n e.preventDefault();\n const type = e.dataTransfer.getData('text/plain');\n const index = e.target.dataset.index;\n \n if (!type || index === undefined) return;\n\n // Update visual\n e.target.className = `slot filled`;\n e.target.textContent = type === 'odd' ? 'ODD' : 'EVEN';\n e.target.style.backgroundColor = type === 'odd' ? 'var(--accent-blue)' : 'var(--accent-red)';\n e.target.style.borderColor = 'transparent';\n\n // Update state\n state.paritySlotValues[index] = type;\n\n // Check logic\n if (state.paritySlotValues.every(v => v !== null)) {\n checkParityLogic();\n }\n }\n\n function checkParityLogic() {\n const oddCount = state.paritySlotValues.filter(v => v === 'odd').length;\n const resultEl = document.getElementById('parity-result');\n const isOddSum = oddCount % 2 !== 0;\n\n resultEl.textContent = isOddSum ? \"Result: ODD Sum\" : \"Result: EVEN Sum\";\n resultEl.style.border = isOddSum ? \"2px solid var(--accent-green)\" : \"2px solid var(--accent-red)\";\n resultEl.style.color = isOddSum ? \"var(--accent-green)\" : \"#e2e8f0\";\n resultEl.classList.add('visible');\n\n // Update text based on combination\n let explanation = \"\";\n if (oddCount === 3) explanation = \"(Odd + Odd) + Odd = Even + Odd = Odd\";\n else if (oddCount === 2) explanation = \"(Odd + Odd) + Even = Even + Even = Even\";\n else if (oddCount === 1) explanation = \"(Even + Even) + Odd = Even + Odd = Odd\";\n else explanation = \"Even + Even + Even = Even\";\n \n const subText = document.createElement('div');\n subText.style.fontSize = \"0.9rem\";\n subText.style.marginTop = \"5px\";\n subText.style.color = \"#94a3b8\";\n subText.textContent = explanation;\n \n // Clear previous subtext if any\n if(resultEl.children.length > 0) resultEl.removeChild(resultEl.lastChild);\n resultEl.appendChild(subText);\n }\n\n // --- Step 3: All Combinations ---\n function renderStep3(container) {\n // Generate combinations\n const arr = [1, 2, 3, 4, 5];\n const combinations = [];\n for (let i = 0; i < arr.length; i++) {\n for (let j = i + 1; j < arr.length; j++) {\n for (let k = j + 1; k < arr.length; k++) {\n combinations.push([arr[i], arr[j], arr[k]]);\n }\n }\n }\n\n const grid = document.createElement('div');\n grid.className = 'grid-container';\n\n combinations.forEach((combo) => {\n const sum = combo.reduce((a, b) => a + b, 0);\n const isOdd = sum % 2 !== 0;\n\n const item = document.createElement('div');\n item.className = 'combo-item';\n if (isOdd) item.classList.add('highlight');\n\n const miniCards = document.createElement('div');\n miniCards.className = 'mini-cards';\n combo.forEach(num => {\n const span = document.createElement('div');\n span.className = 'mini-card';\n span.textContent = num;\n span.style.borderBottom = (num % 2 !== 0) ? \"3px solid var(--accent-blue)\" : \"3px solid var(--accent-red)\";\n miniCards.appendChild(span);\n });\n\n const sumText = document.createElement('div');\n sumText.className = 'combo-sum';\n sumText.textContent = `Sum: ${sum} (${isOdd ? 'Odd' : 'Even'})`;\n\n item.appendChild(miniCards);\n item.appendChild(sumText);\n grid.appendChild(item);\n });\n\n container.appendChild(grid);\n }\n\n // --- Step 4: Count Outcomes ---\n function renderStep4(container) {\n const wrapper = document.createElement('div');\n wrapper.className = 'count-container';\n\n // Favorable\n const favorableBox = document.createElement('div');\n favorableBox.className = 'count-box';\n favorableBox.innerHTML = `\n <div class=\"count-label\">Favorable Outcomes</div>\n <div class=\"count-label\">(Odd Sums)</div>\n <div class=\"count-number\" style=\"color: var(--accent-green)\">4</div>\n <div class=\"count-draggable\" draggable=\"true\" id=\"drag-fav\">4</div>\n `;\n \n // Total\n const totalBox = document.createElement('div');\n totalBox.className = 'count-box';\n totalBox.innerHTML = `\n <div class=\"count-label\">Total Outcomes</div>\n <div class=\"count-label\">C(5,3)</div>\n <div class=\"count-number\" style=\"color: var(--accent-blue)\">10</div>\n <div class=\"count-draggable\" draggable=\"true\" id=\"drag-total\">10</div>\n `;\n\n // Instructions for next step preview\n const info = document.createElement('div');\n info.style.textAlign = 'center';\n info.innerHTML = `\n <p style=\"margin-bottom:10px\">We found:</p>\n <ul style=\"list-style:none; text-align:left; display:inline-block; color: #cbd5e1;\">\n <li>• 1 way to pick 3 Odds</li>\n <li>• 3 ways to pick 1 Odd & 2 Evens</li>\n <li style=\"margin-top:5px; font-weight:bold; color:white;\">= 4 Favorable Outcomes</li>\n </ul>\n `;\n\n wrapper.appendChild(favorableBox);\n wrapper.appendChild(info);\n wrapper.appendChild(totalBox);\n container.appendChild(wrapper);\n\n // We are preparing for drag in step 5, but visual is enough here.\n }\n\n // --- Step 5: Formula ---\n function renderStep5(container) {\n state.probabilityInputs = { num: null, den: null };\n\n const wrapper = document.createElement('div');\n wrapper.className = 'formula-area';\n\n // Draggables Area\n const sourceArea = document.createElement('div');\n sourceArea.style.display = 'flex';\n sourceArea.style.gap = '2rem';\n sourceArea.style.marginBottom = '2rem';\n sourceArea.style.padding = '1rem';\n sourceArea.style.background = 'rgba(255,255,255,0.05)';\n sourceArea.style.borderRadius = '10px';\n\n const dragFav = document.createElement('div');\n dragFav.className = 'count-draggable';\n dragFav.style.position = 'relative';\n dragFav.style.top = '0';\n dragFav.style.right = '0';\n dragFav.textContent = \"4\";\n dragFav.id = \"source-4\";\n dragFav.draggable = true;\n dragFav.addEventListener('dragstart', (e) => {\n e.dataTransfer.setData('text/plain', '4');\n e.dataTransfer.effectAllowed = 'copy';\n });\n\n const dragTotal = document.createElement('div');\n dragTotal.className = 'count-draggable';\n dragTotal.style.position = 'relative';\n dragTotal.style.top = '0';\n dragTotal.style.right = '0';\n dragTotal.style.backgroundColor = 'var(--accent-blue)';\n dragTotal.textContent = \"10\";\n dragTotal.id = \"source-10\";\n dragTotal.draggable = true;\n dragTotal.addEventListener('dragstart', (e) => {\n e.dataTransfer.setData('text/plain', '10');\n e.dataTransfer.effectAllowed = 'copy';\n });\n\n const labelFav = document.createElement('div');\n labelFav.innerHTML = \"Favorable <br> (4)\";\n labelFav.style.textAlign = 'center';\n labelFav.style.fontSize = '0.8rem';\n \n const labelTot = document.createElement('div');\n labelTot.innerHTML = \"Total <br> (10)\";\n labelTot.style.textAlign = 'center';\n labelTot.style.fontSize = '0.8rem';\n\n const grp1 = document.createElement('div');\n grp1.style.display='flex'; grp1.style.flexDirection='column'; grp1.style.alignItems='center'; grp1.style.gap='5px';\n grp1.appendChild(dragFav); grp1.appendChild(labelFav);\n\n const grp2 = document.createElement('div');\n grp2.style.display='flex'; grp2.style.flexDirection='column'; grp2.style.alignItems='center'; grp2.style.gap='5px';\n grp2.appendChild(dragTotal); grp2.appendChild(labelTot);\n\n sourceArea.appendChild(grp1);\n sourceArea.appendChild(grp2);\n wrapper.appendChild(sourceArea);\n\n // Formula\n const formulaTitle = document.createElement('div');\n formulaTitle.textContent = \"P(Odd Sum) = \";\n \n const fractionBuilder = document.createElement('div');\n fractionBuilder.className = 'fraction-builder';\n\n const fraction = document.createElement('div');\n fraction.className = 'fraction';\n\n const numSlot = document.createElement('div');\n numSlot.className = 'drop-zone';\n numSlot.dataset.target = 'num';\n \n const line = document.createElement('div');\n line.className = 'fraction-line';\n \n const denSlot = document.createElement('div');\n denSlot.className = 'drop-zone';\n denSlot.dataset.target = 'den';\n\n fraction.appendChild(numSlot);\n fraction.appendChild(line);\n fraction.appendChild(denSlot);\n\n const equals = document.createElement('div');\n equals.textContent = \"=\";\n \n const finalRes = document.createElement('div');\n finalRes.className = 'final-result';\n finalRes.id = 'final-res';\n finalRes.innerHTML = \"2/5 <span style='font-size:1rem; color:#ccc'> (or 40%)</span>\";\n\n fractionBuilder.appendChild(formulaTitle);\n fractionBuilder.appendChild(fraction);\n fractionBuilder.appendChild(equals);\n fractionBuilder.appendChild(finalRes);\n\n wrapper.appendChild(fractionBuilder);\n container.appendChild(wrapper);\n\n // Events\n [numSlot, denSlot].forEach(slot => {\n slot.addEventListener('dragover', e => {\n e.preventDefault();\n slot.classList.add('hover');\n });\n slot.addEventListener('dragleave', () => slot.classList.remove('hover'));\n slot.addEventListener('drop', handleDropStep5);\n });\n }\n\n function handleDropStep5(e) {\n e.preventDefault();\n e.target.classList.remove('hover');\n const val = e.dataTransfer.getData('text/plain');\n const target = e.target.dataset.target;\n\n // Basic Validation\n if (target === 'num' && val !== '4') {\n alert(\"The numerator must be the Favorable Outcomes (4).\");\n return;\n }\n if (target === 'den' && val !== '10') {\n alert(\"The denominator must be the Total Outcomes (10).\");\n return;\n }\n\n // Update UI\n e.target.textContent = val;\n e.target.style.border = \"2px solid var(--accent-green)\";\n e.target.style.backgroundColor = \"rgba(74, 222, 128, 0.2)\";\n \n // Hide source\n document.getElementById(`source-${val}`).style.opacity = '0.3';\n document.getElementById(`source-${val}`).draggable = false;\n\n state.probabilityInputs[target] = val;\n\n if (state.probabilityInputs.num && state.probabilityInputs.den) {\n document.getElementById('final-res').style.opacity = '1';\n launchConfetti();\n }\n }\n\n // --- Utilities ---\n function createCard(num) {\n const el = document.createElement('div');\n el.className = 'card';\n el.textContent = num;\n return el;\n }\n\n function launchConfetti() {\n // Simple CSS animation or just visual feedback\n const colors = ['#fb7185', '#38bdf8', '#4ade80', '#facc15'];\n for(let i=0; i<30; i++) {\n const dot = document.createElement('div');\n dot.style.position = 'fixed';\n dot.style.left = '50%';\n dot.style.top = '50%';\n dot.style.width = '10px';\n dot.style.height = '10px';\n dot.style.backgroundColor = colors[Math.floor(Math.random()*colors.length)];\n dot.style.borderRadius = '50%';\n dot.style.transition = 'all 1s ease-out';\n document.body.appendChild(dot);\n\n // Animate\n setTimeout(() => {\n const x = (Math.random() - 0.5) * window.innerWidth * 0.5;\n const y = (Math.random() - 0.5) * window.innerHeight * 0.5;\n dot.style.transform = `translate(${x}px, ${y}px)`;\n dot.style.opacity = '0';\n }, 10);\n\n setTimeout(() => dot.remove(), 1000);\n }\n }\n\n // Start\n init();\n\n </script>\n</body>\n</html>"
}