app
Resistor Flash Cards
Public Unreviewedby test5
Interactive flash cards for a circuits class: resistor color codes, SMD markings, formulas, and generated practice problems
← Commit history
Resistor flash cards: color code, SMD, formulas, calculations
2 files changed
+377−1
README.md+19−1@@ -1,3 +1,21 @@ # Resistor Flash Cards -Interactive flash cards for a circuits class: resistor color codes, SMD markings, formulas, and generated practice problems+Interactive flash cards for studying resistors in a circuits class. The page itself is the deck: open it, click a card to flip it, mark it "Got it" or "Again", and shuffle for a fresh set.++## Decks++- Color code: read 4-band and 5-band resistors, per-band digits and multipliers, mnemonics, the 0 ohm jumper, tempco bands+- Value to bands: given a value and tolerance, name the bands+- SMD codes: 3-digit, 4-digit, R-notation, and EIA-96 markings+- Formulas: Ohm's law, power, series and parallel, dividers, Kirchhoff, Wheatstone, tolerance, E-series, power ratings, tempco, resistor types+- Calculations: randomly generated series, parallel, divider, current, and power problems++The color-code, value-to-bands, and calculation cards are generated fresh on every shuffle, so the deck never repeats exactly.++## Keyboard++space flips, left and right arrows move, 1 marks Again, 2 marks Got it, s shuffles.++## Source++Everything lives in `readme.html`, a single self-contained file with no external assets.
readme.htmladded+358@@ -0,0 +1,358 @@+<!doctype html>+<html lang="en">+<head>+<meta charset="utf-8">+<meta name="viewport" content="width=device-width, initial-scale=1">+<title>Resistor Flash Cards</title>+<style>+ :root {+ --bg: #0b0f14; --panel: #121821; --panel2: #18202b; --line: #243040;+ --text: #e6edf3; --muted: #93a1b3; --teal: #14b8a6; --blue: #3b82f6; --purple: #8b5cf6;+ --good: #22c55e; --bad: #f97316;+ }+ * { box-sizing: border-box; }+ html, body { margin: 0; background: var(--bg); color: var(--text);+ font: 15px/1.5 Inter, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif; }+ .wrap { max-width: 880px; margin: 0 auto; padding: 20px 16px 28px; }+ h1 { font-size: 22px; margin: 0 0 4px; letter-spacing: -0.01em; }+ .sub { color: var(--muted); margin: 0 0 14px; font-size: 14px; }+ .chips { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 14px; }+ .chip { border: 1px solid var(--line); background: var(--panel); color: var(--muted); border-radius: 999px;+ padding: 6px 12px; cursor: pointer; font-size: 13px; user-select: none; }+ .chip.on { color: #fff; border-color: transparent;+ background: linear-gradient(90deg, var(--teal), var(--blue) 60%, var(--purple)); }+ .stage { perspective: 1400px; }+ .card { position: relative; min-height: 340px; cursor: pointer; transform-style: preserve-3d;+ transition: transform .45s cubic-bezier(.2,.8,.2,1); }+ .card.flipped { transform: rotateY(180deg); }+ .face { position: absolute; inset: 0; backface-visibility: hidden; -webkit-backface-visibility: hidden;+ background: var(--panel); border: 1px solid var(--line); border-radius: 16px; padding: 22px 24px;+ display: flex; flex-direction: column; justify-content: center; align-items: center; text-align: center; gap: 12px; }+ .face.back { transform: rotateY(180deg); background: var(--panel2); }+ .tag { position: absolute; top: 14px; left: 18px; font-size: 12px; color: var(--muted); letter-spacing: .04em; text-transform: uppercase; }+ .hint { position: absolute; bottom: 12px; right: 18px; font-size: 12px; color: var(--muted); }+ .q { font-size: 20px; font-weight: 600; max-width: 640px; }+ .a { font-size: 26px; font-weight: 700; max-width: 640px; }+ .a.small { font-size: 18px; font-weight: 500; }+ .note { color: var(--muted); font-size: 14px; max-width: 640px; }+ .big { font-size: 40px; font-weight: 800; font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; letter-spacing: .04em; }+ .mono { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; }+ .bands { display: flex; gap: 6px; flex-wrap: wrap; justify-content: center; }+ .band { display: inline-flex; align-items: center; gap: 6px; padding: 4px 10px; border-radius: 8px; background: #0f141b; border: 1px solid var(--line); font-size: 14px; }+ .sw { width: 14px; height: 14px; border-radius: 3px; border: 1px solid rgba(255,255,255,.15); }+ svg.res { width: 340px; max-width: 100%; height: auto; }+ .bar { display: flex; gap: 10px; align-items: center; margin-top: 14px; flex-wrap: wrap; }+ button { border: 1px solid var(--line); background: var(--panel); color: var(--text); border-radius: 10px;+ padding: 10px 16px; font-size: 14px; cursor: pointer; min-height: 44px; }+ button:hover { border-color: #3a4a60; }+ button.primary { background: linear-gradient(90deg, var(--teal), var(--blue)); border-color: transparent; color: #fff; font-weight: 600; }+ button.good { border-color: var(--good); color: var(--good); }+ button.bad { border-color: var(--bad); color: var(--bad); }+ .spacer { flex: 1; }+ .prog { color: var(--muted); font-size: 13px; }+ .meter { height: 6px; background: var(--panel); border: 1px solid var(--line); border-radius: 999px; overflow: hidden; margin-top: 10px; }+ .meter i { display: block; height: 100%; width: 0; background: linear-gradient(90deg, var(--teal), var(--blue), var(--purple)); transition: width .3s; }+ .keys { color: var(--muted); font-size: 12px; margin-top: 10px; }+ kbd { border: 1px solid var(--line); border-bottom-width: 2px; border-radius: 4px; padding: 0 5px; font-size: 11px; background: var(--panel); }+ details { margin-top: 20px; border: 1px solid var(--line); border-radius: 12px; background: var(--panel); padding: 10px 16px; }+ summary { cursor: pointer; font-weight: 600; }+ table { border-collapse: collapse; width: 100%; margin-top: 10px; font-size: 14px; }+ th, td { text-align: left; padding: 6px 8px; border-bottom: 1px solid var(--line); }+ th { color: var(--muted); font-weight: 500; }+ .done .q { color: var(--teal); }+</style>+</head>+<body>+<div class="wrap">+ <h1>Resistor Flash Cards</h1>+ <p class="sub">Color codes, SMD markings, formulas, and quick calculations. Click a card to flip it, then mark whether you got it.</p>++ <div class="chips" id="chips"></div>++ <div class="stage">+ <div class="card" id="card">+ <div class="face front"><span class="tag" id="tagF"></span><div id="front"></div><span class="hint">click or press space to flip</span></div>+ <div class="face back"><span class="tag" id="tagB"></span><div id="back"></div></div>+ </div>+ </div>++ <div class="bar">+ <button id="prev">Prev</button>+ <button id="flip" class="primary">Flip</button>+ <button id="next">Next</button>+ <span class="spacer"></span>+ <button id="again" class="bad">Again</button>+ <button id="got" class="good">Got it</button>+ <button id="shuffle">Shuffle</button>+ </div>+ <div class="meter"><i id="meter"></i></div>+ <div class="bar"><span class="prog" id="prog"></span></div>+ <div class="keys"><kbd>space</kbd> flip · <kbd>←</kbd> <kbd>→</kbd> prev / next · <kbd>1</kbd> again · <kbd>2</kbd> got it · <kbd>s</kbd> shuffle</div>++ <details>+ <summary>Color code cheat sheet</summary>+ <table id="cheat"></table>+ <p class="note" style="margin-top:10px">Read with the tolerance band (gold, silver, or the band spaced farthest from the others) on the right. 4-band: digit, digit, multiplier, tolerance. 5-band: digit, digit, digit, multiplier, tolerance. 6-band adds a temperature coefficient band.</p>+ </details>+</div>++<script>+(function () {+ // ---------- reference data ----------+ var COLORS = [+ { n: 'Black', hex: '#111111', d: 0, m: 1 },+ { n: 'Brown', hex: '#8b4513', d: 1, m: 10, t: 1 },+ { n: 'Red', hex: '#e0201a', d: 2, m: 100, t: 2 },+ { n: 'Orange', hex: '#f28c1a', d: 3, m: 1e3 },+ { n: 'Yellow', hex: '#f2d21a', d: 4, m: 1e4 },+ { n: 'Green', hex: '#21a04b', d: 5, m: 1e5, t: 0.5 },+ { n: 'Blue', hex: '#2260d8', d: 6, m: 1e6, t: 0.25 },+ { n: 'Violet', hex: '#8a3fd1', d: 7, m: 1e7, t: 0.1 },+ { n: 'Gray', hex: '#8f8f8f', d: 8, m: 1e8, t: 0.05 },+ { n: 'White', hex: '#f4f4f4', d: 9, m: 1e9 },+ { n: 'Gold', hex: '#d4a017', m: 0.1, t: 5 },+ { n: 'Silver', hex: '#c0c0c0', m: 0.01, t: 10 }+ ];+ var BY = {}; COLORS.forEach(function (c) { BY[c.n] = c; });+ var E12 = [10, 12, 15, 18, 22, 27, 33, 39, 47, 56, 68, 82];+ var E24 = [10, 11, 12, 13, 15, 16, 18, 20, 22, 24, 27, 30, 33, 36, 39, 43, 47, 51, 56, 62, 68, 75, 82, 91];+ var E96s = [100, 121, 150, 182, 221, 249, 274, 332, 402, 475, 549, 681, 825, 976];++ function fmt(ohms) {+ var u = 'Ω', v = ohms;+ if (ohms >= 1e6) { v = ohms / 1e6; u = 'MΩ'; }+ else if (ohms >= 1e3) { v = ohms / 1e3; u = 'kΩ'; }+ v = +v.toPrecision(3);+ return v + ' ' + u;+ }+ function esc(s) { return String(s).replace(/[&<>]/g, function (c) { return { '&': '&', '<': '<', '>': '>' }[c]; }); }+ function rnd(n) { return Math.floor(Math.random() * n); }+ function pick(a) { return a[rnd(a.length)]; }+ function shuffleArr(a) { for (var i = a.length - 1; i > 0; i--) { var j = rnd(i + 1); var t = a[i]; a[i] = a[j]; a[j] = t; } return a; }++ // ---------- resistor drawing ----------+ function resistorSVG(bandColors) {+ var n = bandColors.length;+ var xs = n === 4 ? [92, 116, 140, 196] : n === 5 ? [84, 106, 128, 150, 200] : [80, 100, 120, 140, 160, 205];+ var s = '<svg class="res" viewBox="0 0 340 90" xmlns="http://www.w3.org/2000/svg">';+ s += '<line x1="10" y1="45" x2="330" y2="45" stroke="#b8c0cc" stroke-width="5" stroke-linecap="round"/>';+ s += '<rect x="60" y="18" width="220" height="54" rx="22" fill="#d9c9a3" stroke="#6b5b3a" stroke-width="2"/>';+ s += '<rect x="66" y="22" width="208" height="14" rx="10" fill="rgba(255,255,255,.28)"/>';+ bandColors.forEach(function (c, i) {+ s += '<rect x="' + xs[i] + '" y="19" width="14" height="52" rx="2" fill="' + BY[c].hex + '" stroke="rgba(0,0,0,.25)"/>';+ });+ return s + '</svg>';+ }+ function bandList(names, roles) {+ return '<div class="bands">' + names.map(function (n, i) {+ return '<span class="band"><i class="sw" style="background:' + BY[n].hex + '"></i>' + n + (roles ? ' <span class="note">' + roles[i] + '</span>' : '') + '</span>';+ }).join('') + '</div>';+ }+ function bandsFor(sig, mult, tol) {+ var digits = String(sig).split('').map(function (d) { return COLORS[+d].n; });+ var mName = COLORS.filter(function (c) { return c.m === mult; })[0].n;+ var tName = COLORS.filter(function (c) { return c.t === tol; })[0].n;+ return digits.concat([mName, tName]);+ }+ function tolRange(ohms, tol) { return fmt(ohms * (1 - tol / 100)) + ' to ' + fmt(ohms * (1 + tol / 100)); }++ // ---------- generated decks ----------+ function colorCards(count) {+ var out = [];+ for (var i = 0; i < count; i++) {+ var five = i % 3 === 2;+ var sig = five ? pick(E96s) : pick(E24);+ var mExp = five ? rnd(5) : rnd(6);+ var mult = Math.pow(10, mExp);+ var tol = five ? 1 : pick([5, 10, 5, 1]);+ var ohms = sig * mult;+ var bands = bandsFor(sig, mult, tol);+ var roles = five ? ['1st digit', '2nd digit', '3rd digit', 'multiplier', 'tolerance'] : ['1st digit', '2nd digit', 'multiplier', 'tolerance'];+ out.push({+ deck: 'color',+ front: resistorSVG(bands) + '<div class="q">What is the value and tolerance?</div>',+ back: '<div class="a">' + fmt(ohms) + ' ±' + tol + '%</div>' + bandList(bands, roles) ++ '<div class="note">Acceptable range: ' + tolRange(ohms, tol) + '</div>'+ });+ }+ return out;+ }+ function reverseCards(count) {+ var out = [];+ for (var i = 0; i < count; i++) {+ var sig = pick(E12), mExp = rnd(6), mult = Math.pow(10, mExp), tol = pick([5, 10]);+ var ohms = sig * mult, bands = bandsFor(sig, mult, tol);+ out.push({+ deck: 'reverse',+ front: '<div class="big">' + fmt(ohms) + ' ±' + tol + '%</div><div class="q">Name the four color bands.</div>',+ back: resistorSVG(bands) + bandList(bands, ['1st digit', '2nd digit', 'multiplier', 'tolerance'])+ });+ }+ return out;+ }+ function digitCards() {+ var out = [];+ COLORS.slice(0, 10).forEach(function (c) {+ out.push({ deck: 'color',+ front: '<div class="bands"><span class="band" style="font-size:20px;padding:10px 18px"><i class="sw" style="width:22px;height:22px;background:' + c.hex + '"></i>' + c.n + '</span></div><div class="q">What digit is this band, and what multiplier?</div>',+ back: '<div class="a">Digit ' + c.d + ' · multiplier ×' + (c.m >= 1000 ? c.m.toLocaleString() : c.m) + '</div>' + (c.t ? '<div class="note">As a tolerance band: ±' + c.t + '%</div>' : '') });+ });+ out.push({ deck: 'color', front: '<div class="q">Gold as a multiplier band? As a tolerance band?</div>', back: '<div class="a">×0.1 · ±5%</div><div class="note">Silver is ×0.01 and ±10%. No tolerance band at all means ±20%.</div>' });+ out.push({ deck: 'color', front: '<div class="q">Mnemonic for Black through White?</div>', back: '<div class="a small">Better Be Right Or Your Great Big Venture Goes Wrong</div><div class="note">Black 0, Brown 1, Red 2, Orange 3, Yellow 4, Green 5, Blue 6, Violet 7, Gray 8, White 9</div>' });+ out.push({ deck: 'color', front: '<div class="q">Which end of the resistor do you read from?</div>', back: '<div class="a small">Tolerance band (gold or silver, or the band with the wider gap) goes on the RIGHT. Read left to right.</div>' });+ out.push({ deck: 'color', front: resistorSVG(['Black']) + '<div class="q">A resistor with a single black band?</div>', back: '<div class="a">0 Ω jumper</div><div class="note">Used as a configurable link on a PCB so the pick-and-place machine can place it like any other part.</div>' });+ out.push({ deck: 'color', front: '<div class="q">What does the sixth band on a 6-band resistor mean?</div>', back: '<div class="a small">Temperature coefficient (ppm/°C)</div><div class="note">Brown 100, Red 50, Orange 15, Yellow 25, Blue 10, Violet 5 ppm/°C</div>' });+ return out;+ }+ function smdCards() {+ var rows = [+ ['103', '10 kΩ', '3-digit: two digits then a power of ten. 10 × 10³'],+ ['472', '4.7 kΩ', '47 × 10²'],+ ['220', '22 Ω', '22 × 10⁰. Not 220 Ω, a common trap.'],+ ['221', '220 Ω', '22 × 10¹'],+ ['1R0', '1.0 Ω', 'R marks the decimal point'],+ ['4R7', '4.7 Ω', 'R marks the decimal point'],+ ['R47', '0.47 Ω', 'R marks the decimal point'],+ ['000', '0 Ω', 'Jumper'],+ ['1002', '10 kΩ', '4-digit (1% parts): three digits then a power of ten. 100 × 10²'],+ ['4701', '4.7 kΩ', '470 × 10¹'],+ ['3301', '3.3 kΩ', '330 × 10¹'],+ ['01C', '10 kΩ', 'EIA-96: code 01 = 100, letter C = ×100'],+ ['22A', '165 Ω', 'EIA-96: code 22 = 165, letter A = ×1'],+ ['01B', '1 kΩ', 'EIA-96: code 01 = 100, letter B = ×10']+ ];+ return rows.map(function (r) {+ return { deck: 'smd', front: '<div class="big">' + r[0] + '</div><div class="q">Value of this SMD marking?</div>', back: '<div class="a">' + r[1] + '</div><div class="note">' + esc(r[2]) + '</div>' };+ });+ }+ function formulaCards() {+ var rows = [+ ["Ohm's law", 'V = I·R, so I = V/R and R = V/I'],+ ['Power dissipated in a resistor', 'P = V·I = I²·R = V²/R'],+ ['Resistors in series', 'R<sub>total</sub> = R₁ + R₂ + … ; same current through each, voltages add'],+ ['Resistors in parallel', '1/R<sub>total</sub> = 1/R₁ + 1/R₂ + … ; same voltage across each, currents add'],+ ['Two resistors in parallel (product over sum)', 'R<sub>total</sub> = R₁·R₂ / (R₁ + R₂)'],+ ['N equal resistors R in parallel', 'R / N'],+ ['Voltage divider output', 'V<sub>out</sub> = V<sub>in</sub> · R₂ / (R₁ + R₂), with R₂ across the output'],+ ['Current divider (two branches)', 'I₁ = I<sub>total</sub> · R₂ / (R₁ + R₂). The current takes the smaller resistance more.'],+ ["Kirchhoff's voltage law", 'The sum of voltages around any closed loop is zero'],+ ["Kirchhoff's current law", 'The sum of currents entering a node equals the sum leaving it'],+ ['Wheatstone bridge balance condition', 'R₁/R₂ = R₃/R₄ (no current through the meter)'],+ ['Conductance', 'G = 1/R, measured in siemens (S). Parallel conductances add.'],+ ['Resistance of a wire', 'R = ρ·L / A (resistivity × length ÷ cross-section area)'],+ ['What does ±5% tolerance mean for 4.7 kΩ?', 'Actual value lies between 4.465 kΩ and 4.935 kΩ'],+ ['E12 series values', '10, 12, 15, 18, 22, 27, 33, 39, 47, 56, 68, 82 (each ~20% apart, made for ±10% parts)'],+ ['E24 series values', 'E12 plus 11, 13, 16, 20, 24, 30, 36, 43, 47…: 24 per decade, made for ±5% parts'],+ ['Why are standard values spaced logarithmically?', 'So adjacent values overlap at their tolerance and every resistance is covered with the fewest parts'],+ ['Typical power ratings', 'Through-hole: 1/8 W, 1/4 W, 1/2 W. SMD: 0402 ≈ 1/16 W, 0603 ≈ 1/10 W, 0805 ≈ 1/8 W, 1206 ≈ 1/4 W'],+ ['Derating', 'Above about 70 °C ambient the allowed power drops linearly toward zero at the max temperature. Run resistors at half their rating.'],+ ['Temperature coefficient, 100 ppm/°C', 'Resistance changes 0.01% per °C. A 10 kΩ part drifts 1 Ω per °C.'],+ ['Carbon film vs metal film', 'Carbon film: cheap, ±5%, noisier. Metal film: ±1% or better, low noise, low tempco. Default choice for signal work.'],+ ['Wirewound resistors', 'High power, very precise, but inductive: avoid in high-frequency paths'],+ ['Potentiometer vs rheostat', 'Potentiometer: three terminals, a variable voltage divider. Rheostat: two terminals, a variable resistance.'],+ ['Pull-up resistor', 'Ties a signal to V<sub>cc</sub> through a resistor (often 4.7 kΩ to 10 kΩ) so it reads high when nothing drives it'],+ ['LED current-limiting resistor', 'R = (V<sub>supply</sub> − V<sub>forward</sub>) / I<sub>LED</sub>'],+ ['Thermal noise (Johnson) of a resistor', 'v<sub>n</sub> = √(4·k·T·R·Δf). Bigger R means more noise.']+ ];+ return rows.map(function (r) { return { deck: 'formula', front: '<div class="q">' + r[0] + '</div>', back: '<div class="a small">' + r[1] + '</div>' }; });+ }+ function calcCards(count) {+ var out = [];+ for (var i = 0; i < count; i++) {+ var kind = i % 6, r1 = pick(E12) * Math.pow(10, 1 + rnd(3)), r2 = pick(E12) * Math.pow(10, 1 + rnd(3)), v = pick([3.3, 5, 9, 12, 24]);+ if (kind === 0) out.push({ deck: 'calc', front: '<div class="q">' + fmt(r1) + ' and ' + fmt(r2) + ' in series?</div>', back: '<div class="a">' + fmt(r1 + r2) + '</div><div class="note">Series resistances add.</div>' });+ else if (kind === 1) out.push({ deck: 'calc', front: '<div class="q">Two ' + fmt(r1) + ' resistors in parallel?</div>', back: '<div class="a">' + fmt(r1 / 2) + '</div><div class="note">Equal resistors in parallel: R / N.</div>' });+ else if (kind === 2) out.push({ deck: 'calc', front: '<div class="q">' + fmt(r1) + ' and ' + fmt(r2) + ' in parallel?</div>', back: '<div class="a">' + fmt(r1 * r2 / (r1 + r2)) + '</div><div class="note">Product over sum. The result is always smaller than the smaller resistor.</div>' });+ else if (kind === 3) out.push({ deck: 'calc', front: '<div class="q">Voltage divider: ' + v + ' V across ' + fmt(r1) + ' (top) and ' + fmt(r2) + ' (bottom). V<sub>out</sub> across the bottom?</div>', back: '<div class="a">' + (+(v * r2 / (r1 + r2)).toPrecision(3)) + ' V</div><div class="note">V<sub>out</sub> = V<sub>in</sub> · R₂ / (R₁ + R₂)</div>' });+ else if (kind === 4) { var iA = v / r1; out.push({ deck: 'calc', front: '<div class="q">' + v + ' V across ' + fmt(r1) + '. Current?</div>', back: '<div class="a">' + (iA >= 1e-3 ? +(iA * 1e3).toPrecision(3) + ' mA' : +(iA * 1e6).toPrecision(3) + ' µA') + '</div><div class="note">I = V / R</div>' }); }+ else { var p = v * v / r1; out.push({ deck: 'calc', front: '<div class="q">' + v + ' V across ' + fmt(r1) + '. Power dissipated? Is a 1/4 W part OK?</div>', back: '<div class="a">' + (p >= 1 ? +p.toPrecision(3) + ' W' : +(p * 1e3).toPrecision(3) + ' mW') + '</div><div class="note">P = V² / R. ' + (p <= 0.125 ? 'Yes, under half of 1/4 W.' : p <= 0.25 ? 'Marginal: within rating but over half of it.' : 'No, exceeds 1/4 W.') + '</div>' }); }+ }+ return out;+ }++ var DECKS = [+ { id: 'all', label: 'All' },+ { id: 'color', label: 'Color code' },+ { id: 'reverse', label: 'Value to bands' },+ { id: 'smd', label: 'SMD codes' },+ { id: 'formula', label: 'Formulas' },+ { id: 'calc', label: 'Calculations' }+ ];+ var ALL = [];+ function build() {+ ALL = shuffleArr(digitCards().concat(colorCards(18), reverseCards(12), smdCards(), formulaCards(), calcCards(18)));+ }++ // ---------- state ----------+ var deck = 'all', queue = [], idx = 0, flipped = false, done = 0, total = 0;+ var card = document.getElementById('card'), front = document.getElementById('front'), back = document.getElementById('back');+ var tagF = document.getElementById('tagF'), tagB = document.getElementById('tagB'), prog = document.getElementById('prog'), meter = document.getElementById('meter');+ function label(id) { return DECKS.filter(function (d) { return d.id === id; })[0].label; }++ function load() {+ queue = ALL.filter(function (c) { return deck === 'all' || c.deck === deck; });+ idx = 0; done = 0; total = queue.length; render();+ }+ function render() {+ flipped = false; card.classList.remove('flipped');+ if (!queue.length) {+ card.classList.add('done');+ front.innerHTML = '<div class="q">Deck complete. Shuffle to run it again.</div>'; back.innerHTML = '';+ tagF.textContent = label(deck); tagB.textContent = '';+ } else {+ card.classList.remove('done');+ var c = queue[idx];+ front.innerHTML = c.front; back.innerHTML = c.back;+ tagF.textContent = label(c.deck) + ' · ' + (idx + 1) + ' of ' + queue.length; tagB.textContent = tagF.textContent;+ }+ prog.textContent = done + ' of ' + total + ' marked "got it"' + (queue.length ? ', ' + queue.length + ' left in the queue' : '');+ meter.style.width = (total ? Math.round(done / total * 100) : 0) + '%';+ fit();+ }+ function flip() { if (!queue.length) return; flipped = !flipped; card.classList.toggle('flipped', flipped); }+ function next() { if (!queue.length) return; idx = (idx + 1) % queue.length; render(); }+ function prev() { if (!queue.length) return; idx = (idx - 1 + queue.length) % queue.length; render(); }+ function got() { if (!queue.length) return; queue.splice(idx, 1); done++; if (idx >= queue.length) idx = 0; render(); }+ function again() { if (queue.length < 2) { flipped = false; card.classList.remove('flipped'); return; } var c = queue.splice(idx, 1)[0]; queue.push(c); if (idx >= queue.length) idx = 0; render(); }+ function reshuffle() { build(); load(); }++ // ---------- UI wiring ----------+ var chips = document.getElementById('chips');+ DECKS.forEach(function (d) {+ var b = document.createElement('span'); b.className = 'chip' + (d.id === deck ? ' on' : ''); b.textContent = d.label;+ b.onclick = function () { deck = d.id; Array.prototype.forEach.call(chips.children, function (x) { x.classList.remove('on'); }); b.classList.add('on'); load(); };+ chips.appendChild(b);+ });+ card.onclick = flip;+ document.getElementById('flip').onclick = flip;+ document.getElementById('next').onclick = next;+ document.getElementById('prev').onclick = prev;+ document.getElementById('got').onclick = got;+ document.getElementById('again').onclick = again;+ document.getElementById('shuffle').onclick = reshuffle;+ document.addEventListener('keydown', function (e) {+ if (e.target && /input|textarea/i.test(e.target.tagName)) return;+ if (e.key === ' ') { e.preventDefault(); flip(); }+ else if (e.key === 'ArrowRight') next();+ else if (e.key === 'ArrowLeft') prev();+ else if (e.key === '1') again();+ else if (e.key === '2') got();+ else if (e.key === 's' || e.key === 'S') reshuffle();+ });++ // cheat sheet+ var cheat = document.getElementById('cheat');+ cheat.innerHTML = '<tr><th>Color</th><th>Digit</th><th>Multiplier</th><th>Tolerance</th></tr>' + COLORS.map(function (c) {+ return '<tr><td><span class="band"><i class="sw" style="background:' + c.hex + '"></i>' + c.n + '</span></td><td>' + (c.d === undefined ? '' : c.d) + '</td><td>×' + (c.m >= 1000 ? c.m.toLocaleString() : c.m) + '</td><td>' + (c.t ? '±' + c.t + '%' : '') + '</td></tr>';+ }).join('');++ // sandboxed-iframe height reporter+ function fit() { try { parent.postMessage({ type: 'readme-height', height: document.documentElement.scrollHeight }, '*'); } catch (e) {} }+ addEventListener('load', fit);+ if (window.ResizeObserver) new ResizeObserver(fit).observe(document.documentElement);++ build(); load();+})();+</script>+</body>+</html>