-
-
-
-
+
+
Custom Number
+
+
+
+
+
+
Random runs briefly then stops automatically.
+
-
-
-
\ No newline at end of file
+
+
\ No newline at end of file
diff --git a/src/scripts/binary.js b/src/scripts/binary.js
new file mode 100644
index 0000000..e596092
--- /dev/null
+++ b/src/scripts/binary.js
@@ -0,0 +1,459 @@
+(() => {
+ /* -----------------------------
+ DOM
+ ----------------------------- */
+ const bitsGrid = document.getElementById("bitsGrid");
+ const denaryEl = document.getElementById("denaryNumber");
+ const binaryEl = document.getElementById("binaryNumber");
+ const bitsInput = document.getElementById("bitsInput");
+
+ const modeToggle = document.getElementById("modeToggle");
+ const modeHint = document.getElementById("modeHint");
+
+ // Connect the text labels to the JS
+ const lblUnsigned = document.getElementById("lblUnsigned");
+ const lblTwos = document.getElementById("lblTwos");
+
+ const btnCustomBinary = document.getElementById("btnCustomBinary");
+ const btnCustomDenary = document.getElementById("btnCustomDenary");
+ const btnShiftLeft = document.getElementById("btnShiftLeft");
+ const btnShiftRight = document.getElementById("btnShiftRight");
+
+ const btnDec = document.getElementById("btnDec");
+ const btnInc = document.getElementById("btnInc");
+ const btnClear = document.getElementById("btnClear");
+ const btnRandom = document.getElementById("btnRandom");
+
+ const btnBitsUp = document.getElementById("btnBitsUp");
+ const btnBitsDown = document.getElementById("btnBitsDown");
+
+ const toolboxToggle = document.getElementById("toolboxToggle");
+ const binaryPage = document.getElementById("binaryPage");
+
+ /* -----------------------------
+ STATE
+ ----------------------------- */
+ let bitCount = clampInt(Number(bitsInput?.value ?? 8), 1, 64);
+ let bits = new Array(bitCount).fill(false);
+ let randomTimer = null;
+
+ /* -----------------------------
+ HELPERS
+ ----------------------------- */
+ function clampInt(n, min, max) {
+ if (!Number.isFinite(n)) return min;
+ return Math.max(min, Math.min(max, Math.trunc(n)));
+ }
+
+ function isTwosMode() {
+ return !!modeToggle?.checked;
+ }
+
+ function pow2Big(n) {
+ return 1n << BigInt(n);
+ }
+
+ function unsignedMaxExclusive(nBits) {
+ return pow2Big(nBits);
+ }
+
+ function unsignedMaxValue(nBits) {
+ return pow2Big(nBits) - 1n;
+ }
+
+ function twosMin(nBits) {
+ return -pow2Big(nBits - 1);
+ }
+
+ function twosMax(nBits) {
+ return pow2Big(nBits - 1) - 1n;
+ }
+
+ function bitsToUnsignedBigInt() {
+ let v = 0n;
+ for (let i = 0; i < bitCount; i++) {
+ if (bits[i]) v += pow2Big(i);
+ }
+ return v;
+ }
+
+ function unsignedBigIntToBits(vUnsigned) {
+ const span = unsignedMaxExclusive(bitCount);
+ const v = ((vUnsigned % span) + span) % span;
+ for (let i = 0; i < bitCount; i++) {
+ bits[i] = ((v >> BigInt(i)) & 1n) === 1n;
+ }
+ }
+
+ function bitsToSignedBigIntTwos() {
+ const u = bitsToUnsignedBigInt();
+ const signBit = bits[bitCount - 1] === true;
+ if (!signBit) return u;
+ return u - pow2Big(bitCount);
+ }
+
+ function signedBigIntToBitsTwos(vSigned) {
+ const span = pow2Big(bitCount);
+ let v = vSigned;
+ v = ((v % span) + span) % span;
+ unsignedBigIntToBits(v);
+ }
+
+ function formatBinaryGrouped() {
+ let s = "";
+ for (let i = bitCount - 1; i >= 0; i--) {
+ s += bits[i] ? "1" : "0";
+ const posFromLeft = (bitCount - i);
+ if (i !== 0 && posFromLeft % 4 === 0) s += " ";
+ }
+ return s.trimEnd();
+ }
+
+ function updateModeHint() {
+ if (!modeHint) return;
+ if (isTwosMode()) {
+ modeHint.textContent = "Tip: In two's complement, the left-most bit (MSB) represents a negative value.";
+ } else {
+ modeHint.textContent = "Tip: In unsigned binary, all bits represent positive values.";
+ }
+ }
+
+ /* -----------------------------
+ RESPONSIVE GRID COLS
+ ----------------------------- */
+ function computeColsForBitsGrid() {
+ if (!bitsGrid) return;
+ const wrap = bitsGrid.parentElement;
+ if (!wrap) return;
+
+ const width = wrap.getBoundingClientRect().width;
+ const minCell = 100;
+ const cols = clampInt(Math.floor(width / minCell), 1, 12);
+ bitsGrid.style.setProperty("--cols", String(Math.min(cols, bitCount)));
+ }
+
+ /* -----------------------------
+ BUILD UI (BITS)
+ ----------------------------- */
+ function buildBits(count) {
+ bitCount = clampInt(count, 1, 64);
+ if (bitsInput) bitsInput.value = String(bitCount);
+
+ const oldBits = bits.slice();
+ bits = new Array(bitCount).fill(false);
+ for (let i = 0; i < Math.min(oldBits.length, bitCount); i++) bits[i] = oldBits[i];
+
+ bitsGrid.innerHTML = "";
+ bitsGrid.classList.toggle("bitsFew", bitCount < 8);
+
+ for (let i = bitCount - 1; i >= 0; i--) {
+ const bitEl = document.createElement("div");
+ bitEl.className = "bit";
+
+ bitEl.innerHTML = `
+
+
+
+ `;
+
+ bitsGrid.appendChild(bitEl);
+ }
+
+ bitsGrid.querySelectorAll('input[type="checkbox"]').forEach((input) => {
+ input.addEventListener("change", () => {
+ const i = Number(input.dataset.index);
+ bits[i] = input.checked;
+ updateUI();
+ });
+ });
+
+ computeColsForBitsGrid();
+ updateUI();
+ }
+
+ /* -----------------------------
+ UI UPDATE
+ ----------------------------- */
+ function updateBitLabels() {
+ for (let i = 0; i < bitCount; i++) {
+ const label = document.getElementById(`bitLabel-${i}`);
+ if (!label) continue;
+
+ let valStr;
+ if (isTwosMode() && i === bitCount - 1) {
+ valStr = `-${pow2Big(bitCount - 1).toString()}`;
+ } else {
+ valStr = pow2Big(i).toString();
+ }
+ label.textContent = valStr;
+ label.style.setProperty('--len', valStr.length);
+ }
+ }
+
+ function syncSwitchesToBits() {
+ bitsGrid.querySelectorAll('input[type="checkbox"]').forEach((input) => {
+ const i = Number(input.dataset.index);
+ input.checked = !!bits[i];
+ });
+ }
+
+ function updateBulbs() {
+ for (let i = 0; i < bitCount; i++) {
+ const bulb = document.getElementById(`bulb-${i}`);
+ if (!bulb) continue;
+ bulb.classList.toggle("on", bits[i] === true);
+ }
+ }
+
+ function updateReadout() {
+ if (!denaryEl || !binaryEl) return;
+ if (isTwosMode()) {
+ denaryEl.textContent = bitsToSignedBigIntTwos().toString();
+ } else {
+ denaryEl.textContent = bitsToUnsignedBigInt().toString();
+ }
+ binaryEl.textContent = formatBinaryGrouped();
+ }
+
+ function updateUI() {
+ updateModeHint();
+
+ // Toggle the glowing CSS class on the active mode text
+ if (lblUnsigned && lblTwos) {
+ lblUnsigned.classList.toggle("activeMode", !isTwosMode());
+ lblTwos.classList.toggle("activeMode", isTwosMode());
+ }
+
+ updateBitLabels();
+ syncSwitchesToBits();
+ updateBulbs();
+ updateReadout();
+ }
+
+ /* -----------------------------
+ SET FROM BINARY STRING
+ ----------------------------- */
+ function setFromBinaryString(binStr) {
+ const clean = String(binStr ?? "").replace(/\s+/g, "");
+ if (!/^[01]+$/.test(clean)) return false;
+
+ const padded = clean.slice(-bitCount).padStart(bitCount, "0");
+ for (let i = 0; i < bitCount; i++) {
+ const charFromRight = padded[padded.length - 1 - i];
+ bits[i] = charFromRight === "1";
+ }
+
+ updateUI();
+ return true;
+ }
+
+ /* -----------------------------
+ SET FROM DENARY INPUT
+ ----------------------------- */
+ function setFromDenaryInput(vStr) {
+ const raw = String(vStr ?? "").trim();
+ if (!raw) return false;
+
+ let v;
+ try {
+ if (!/^-?\d+$/.test(raw)) return false;
+ v = BigInt(raw);
+ } catch { return false; }
+
+ if (isTwosMode()) {
+ const min = twosMin(bitCount);
+ const max = twosMax(bitCount);
+ if (v < min || v > max) return false;
+ signedBigIntToBitsTwos(v);
+ } else {
+ if (v < 0n || v > unsignedMaxValue(bitCount)) return false;
+ unsignedBigIntToBits(v);
+ }
+
+ updateUI();
+ return true;
+ }
+
+ /* -----------------------------
+ SHIFTS
+ ----------------------------- */
+ function shiftLeft() {
+ for (let i = bitCount - 1; i >= 1; i--) { bits[i] = bits[i - 1]; }
+ bits[0] = false;
+ updateUI();
+ }
+
+ function shiftRight() {
+ const msb = bits[bitCount - 1];
+ for (let i = 0; i < bitCount - 1; i++) { bits[i] = bits[i + 1]; }
+ bits[bitCount - 1] = isTwosMode() ? msb : false;
+ updateUI();
+ }
+
+ /* -----------------------------
+ CLEAR / INC / DEC
+ ----------------------------- */
+ function clearAll() {
+ bits = [];
+ if (modeToggle) modeToggle.checked = false;
+ buildBits(8);
+ }
+
+ function increment() {
+ if (isTwosMode()) {
+ const min = twosMin(bitCount);
+ const max = twosMax(bitCount);
+ let v = bitsToSignedBigIntTwos() + 1n;
+ if (v > max) v = min;
+ signedBigIntToBitsTwos(v);
+ } else {
+ const span = unsignedMaxExclusive(bitCount);
+ unsignedBigIntToBits((bitsToUnsignedBigInt() + 1n) % span);
+ }
+ updateUI();
+ }
+
+ function decrement() {
+ if (isTwosMode()) {
+ const min = twosMin(bitCount);
+ const max = twosMax(bitCount);
+ let v = bitsToSignedBigIntTwos() - 1n;
+ if (v < min) v = max;
+ signedBigIntToBitsTwos(v);
+ } else {
+ const span = unsignedMaxExclusive(bitCount);
+ unsignedBigIntToBits((bitsToUnsignedBigInt() - 1n + span) % span);
+ }
+ updateUI();
+ }
+
+ /* -----------------------------
+ RANDOM
+ ----------------------------- */
+ function cryptoRandomBigInt(maxExclusive) {
+ if (maxExclusive <= 0n) return 0n;
+ const bitLen = maxExclusive.toString(2).length;
+ const byteLen = Math.ceil(bitLen / 8);
+
+ while (true) {
+ const bytes = new Uint8Array(byteLen);
+ crypto.getRandomValues(bytes);
+ let x = 0n;
+ for (const b of bytes) x = (x << 8n) | BigInt(b);
+
+ const extraBits = BigInt(byteLen * 8 - bitLen);
+ if (extraBits > 0n) x = x >> extraBits;
+
+ if (x < maxExclusive) return x;
+ }
+ }
+
+ function setRandomOnce() {
+ const span = unsignedMaxExclusive(bitCount);
+ const u = cryptoRandomBigInt(span);
+ unsignedBigIntToBits(u);
+ updateUI();
+ }
+
+ function setRandomRunning(isRunning) {
+ if (!btnRandom) return;
+ btnRandom.classList.toggle("btnRandomRunning", !!isRunning);
+ }
+
+ function runRandomBriefly() {
+ if (randomTimer) {
+ clearInterval(randomTimer);
+ randomTimer = null;
+ }
+
+ setRandomRunning(true);
+ const start = Date.now();
+ const durationMs = 1125;
+ const tickMs = 80;
+
+ randomTimer = setInterval(() => {
+ setRandomOnce();
+ if (Date.now() - start >= durationMs) {
+ clearInterval(randomTimer);
+ randomTimer = null;
+ setRandomRunning(false);
+ }
+ }, tickMs);
+ }
+
+ /* -----------------------------
+ BIT WIDTH CONTROLS
+ ----------------------------- */
+ function setBitWidth(n) {
+ const v = clampInt(n, 1, 64);
+ buildBits(v);
+ }
+
+ /* -----------------------------
+ TOOLBOX TOGGLE
+ ----------------------------- */
+ function setToolboxCollapsed(collapsed) {
+ if (!binaryPage) return;
+ binaryPage.classList.toggle("toolboxCollapsed", !!collapsed);
+ const expanded = !collapsed;
+ toolboxToggle?.setAttribute("aria-expanded", expanded ? "true" : "false");
+ }
+
+ /* -----------------------------
+ EVENTS
+ ----------------------------- */
+ modeToggle?.addEventListener("change", updateUI);
+
+ btnCustomBinary?.addEventListener("click", () => {
+ const v = prompt(`Enter binary (spaces allowed). Current width: ${bitCount} bits`);
+ if (v === null) return;
+ if (!setFromBinaryString(v)) alert("Invalid binary");
+ });
+
+ btnCustomDenary?.addEventListener("click", () => {
+ const v = prompt(
+ isTwosMode()
+ ? `Enter denary (${twosMin(bitCount).toString()} to ${twosMax(bitCount).toString()}):`
+ : `Enter denary (0 to ${unsignedMaxValue(bitCount).toString()}):`
+ );
+ if (v === null) return;
+ if (!setFromDenaryInput(v)) alert("Invalid denary for current mode/bit width");
+ });
+
+ btnShiftLeft?.addEventListener("click", shiftLeft);
+ btnShiftRight?.addEventListener("click", shiftRight);
+
+ btnInc?.addEventListener("click", increment);
+ btnDec?.addEventListener("click", decrement);
+
+ btnClear?.addEventListener("click", clearAll);
+ btnRandom?.addEventListener("click", runRandomBriefly);
+
+ btnBitsUp?.addEventListener("click", () => setBitWidth(bitCount + 1));
+ btnBitsDown?.addEventListener("click", () => setBitWidth(bitCount - 1));
+
+ bitsInput?.addEventListener("change", () => setBitWidth(Number(bitsInput.value)));
+
+ toolboxToggle?.addEventListener("click", () => {
+ const isCollapsed = binaryPage?.classList.contains("toolboxCollapsed");
+ setToolboxCollapsed(!isCollapsed);
+ });
+
+ window.addEventListener("resize", () => {
+ computeColsForBitsGrid();
+ });
+
+ /* -----------------------------
+ INIT
+ ----------------------------- */
+ updateModeHint();
+ buildBits(bitCount);
+ setToolboxCollapsed(false);
+})();
\ No newline at end of file
diff --git a/src/scripts/unsignedBinary.js b/src/scripts/unsignedBinary.js
deleted file mode 100644
index 58abb25..0000000
--- a/src/scripts/unsignedBinary.js
+++ /dev/null
@@ -1,54 +0,0 @@
-let bits = [128,64,32,16,8,4,2,1];
-let state = Array(8).fill(0);
-
-const denaryEl = document.getElementById("denaryNumber");
-const binaryEl = document.getElementById("binaryNumber");
-const bitEls = document.querySelectorAll(".bit");
-
-bitEls.forEach((el, i) => {
- el.addEventListener("click", () => {
- state[i] = state[i] ? 0 : 1;
- el.classList.toggle("on");
- update();
- });
-});
-
-function update() {
- const denary = state.reduce((sum, bit, i) => sum + bit * bits[i], 0);
- denaryEl.textContent = denary;
- binaryEl.textContent = state.join("");
-}
-
-function requestBinary() {
- const input = prompt("Enter 8-bit binary:");
- if (!/^[01]{8}$/.test(input)) return;
- state = input.split("").map(Number);
- bitEls.forEach((el,i)=>el.classList.toggle("on",state[i]));
- update();
-}
-
-function requestDenary() {
- const input = parseInt(prompt("Enter denary (0–255)"),10);
- if (isNaN(input) || input < 0 || input > 255) return;
-
- let value = input;
- state = bits.map(b => {
- if (value >= b) {
- value -= b;
- return 1;
- }
- return 0;
- });
-
- bitEls.forEach((el,i)=>el.classList.toggle("on",state[i]));
- update();
-}
-
-function shiftBinary(dir) {
- if (dir === "left") state.shift(), state.push(0);
- if (dir === "right") state.pop(), state.unshift(0);
- bitEls.forEach((el,i)=>el.classList.toggle("on",state[i]));
- update();
-}
-
-update();
diff --git a/src/styles/binary.css b/src/styles/binary.css
deleted file mode 100644
index 6a03738..0000000
--- a/src/styles/binary.css
+++ /dev/null
@@ -1,68 +0,0 @@
-.binary-container {
- max-width: 1100px;
- margin: auto;
- padding: 2rem;
- color: #e0e0e0;
-}
-
-.display {
- text-align: center;
-}
-
-.label {
- font-size: 1.2rem;
- opacity: 0.7;
-}
-
-.number {
- font-size: 3rem;
- margin-bottom: 1rem;
- color: #00ff66;
-}
-
-.controls {
- margin-top: 1rem;
-}
-
-.controls button {
- margin: 0.25rem;
- padding: 0.6rem 1rem;
- font-size: 1rem;
-}
-
-.bits {
- display: grid;
- grid-template-columns: repeat(8, 1fr);
- gap: 1rem;
- margin-top: 3rem;
-}
-
-.bit {
- width: 40px;
- height: 80px;
- background: #333;
- border-radius: 20px;
- position: relative;
- cursor: pointer;
-}
-
-.bit::after {
- content: "";
- width: 32px;
- height: 32px;
- background: #555;
- border-radius: 50%;
- position: absolute;
- bottom: 6px;
- left: 4px;
- transition: all 0.2s ease;
-}
-
-.bit.on {
- background: #00c853;
-}
-
-.bit.on::after {
- bottom: 42px;
- background: #eaffea;
-}
diff --git a/src/styles/global.css b/src/styles/global.css
new file mode 100644
index 0000000..9e7d095
--- /dev/null
+++ b/src/styles/global.css
@@ -0,0 +1,518 @@
+/* Global fonts */
+@font-face {
+ font-family: "SevenSegment";
+ src: url("/fonts/Seven-Segment.woff2") format("woff2"),
+ url("/fonts/Seven-Segment.woff") format("woff");
+ font-weight: 400;
+ font-style: normal;
+ font-display: swap;
+}
+
+@font-face {
+ font-family: "DSEG7Classic";
+ src: url("/fonts/DSEG7Classic-Regular.woff") format("woff"),
+ url("/fonts/DSEG7Classic-Regular.ttf") format("truetype");
+ font-weight: 400;
+ font-style: normal;
+ font-display: swap;
+}
+
+:root {
+ --nav-h: 92px;
+ --bg: #1f2027;
+ --text: #e8e8ee;
+ --muted: #a9acb8;
+ --line: rgba(255,255,255,.10);
+
+ /* Fonts */
+ --ui-font: "Inter", system-ui, -apple-system, Segoe UI, Roboto, Arial, sans-serif;
+ --num-font: "DSEG7Classic", ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", monospace;
+ --bit-font: "SevenSegment", monospace;
+}
+
+* { box-sizing: border-box; }
+
+html, body { height: 100%; }
+
+body {
+ margin: 0;
+ background: var(--bg);
+ color: var(--text);
+ font-family: var(--ui-font);
+ display: flex;
+ flex-direction: column;
+}
+
+/* --- BASE LAYOUT --- */
+.siteNav {
+ position: sticky;
+ top: 0;
+ z-index: 50;
+ height: var(--nav-h);
+ background: rgba(0,0,0,.10);
+ border-bottom: 1px solid var(--line);
+ backdrop-filter: blur(8px);
+}
+.navInner {
+ height: 100%;
+ max-width: 1400px;
+ margin: 0 auto;
+ padding: 0 20px;
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: 24px;
+}
+.brand {
+ display: flex;
+ align-items: center;
+ gap: 12px;
+ text-decoration: none;
+ color: var(--text);
+}
+.brandLogo {
+ width: 2.5em;
+ height: 2.5em;
+ image-rendering: pixelated;
+}
+.brandName {
+ letter-spacing: .12em;
+ font-weight: 900;
+ text-transform: uppercase;
+ font-size: 18px;
+}
+.navLinks {
+ display: flex;
+ align-items: center;
+ gap: 18px;
+ flex-wrap: wrap;
+}
+.navLinks a {
+ color: var(--muted);
+ text-decoration: none;
+ font-weight: 800;
+ letter-spacing: .12em;
+ font-size: 16px;
+ text-transform: uppercase;
+}
+.navLinks a:hover { color: var(--text); }
+
+.pageWrap {
+ flex: 1;
+ max-width: 1400px;
+ margin: 0 auto;
+ padding: 0 20px 40px;
+ width: 100%;
+}
+
+.siteFooter {
+ border-top: 1px solid var(--line);
+ background: rgba(0,0,0,.08);
+}
+.footerInner {
+ max-width: 1400px;
+ margin: 0 auto;
+ padding: 18px 20px;
+ color: var(--muted);
+ font-size: 12px;
+ letter-spacing: .08em;
+ text-transform: uppercase;
+ display: flex;
+ flex-direction: column;
+ gap: 6px;
+}
+
+/* --- BINARY APP LAYOUT --- */
+.binaryPage {
+ --toolbox-w: 360px;
+ --toolbox-gap: 22px;
+ --toolbox-toggle-top: calc(var(--nav-h) + 16px);
+ --toolbox-top: calc(var(--toolbox-toggle-top) + 60px);
+ position: relative;
+ padding-top: 26px;
+}
+
+.binaryPage:not(.toolboxCollapsed) {
+ padding-right: calc(var(--toolbox-w) + var(--toolbox-gap));
+}
+.binaryPage.toolboxCollapsed {
+ padding-right: 0;
+}
+
+.topGrid {
+ display: flex;
+ align-items: flex-start;
+ gap: 28px;
+}
+.leftCol {
+ flex: 1 1 auto;
+ min-width: 0;
+}
+
+.readout {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ gap: 8px;
+ padding-top: 14px;
+}
+.label {
+ font-family: var(--bit-font);
+ letter-spacing: .14em;
+ text-transform: uppercase;
+ font-size: 18px;
+ opacity: .75;
+}
+.num {
+ font-family: var(--num-font);
+ color: #28f07a;
+ text-shadow: 0 0 18px rgba(40,240,122,.35);
+ letter-spacing: 2px;
+}
+.denaryValue {
+ font-size: 68px; /* Increased */
+ line-height: 1;
+}
+.binaryValue {
+ font-size: 54px; /* Increased */
+ line-height: 1.1;
+ white-space: pre-wrap;
+ text-align: center;
+}
+
+.divider {
+ height: 1px;
+ background: rgba(255,255,255,.08);
+ margin: 22px 0 18px;
+}
+
+.bitsWrap { width: 100%; }
+.bitsGrid {
+ --cols: 8;
+ display: grid;
+ grid-template-columns: repeat(var(--cols), minmax(92px, 1fr));
+ gap: 26px 22px;
+ align-items: start;
+ justify-items: center;
+}
+.bitsGrid.bitsFew { justify-content: center; }
+
+.bit {
+ width: 100%;
+ max-width: 140px;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ gap: 8px;
+ container-type: inline-size;
+}
+
+.bulb {
+ width: 52px; /* Increased */
+ height: 52px; /* Increased */
+ color: rgba(255,255,255,.15);
+ margin-bottom: 8px;
+ flex-shrink: 0;
+ transition: 0.2s ease;
+ background: transparent;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+}
+.bulb svg {
+ width: 100%;
+ height: 100%;
+ display: block;
+}
+.bulb.on {
+ color: #ffd86b;
+ filter: drop-shadow(0 0 14px rgba(255, 216, 107, 1));
+}
+
+.bitVal {
+ font-family: var(--bit-font);
+ font-size: min(44px, calc(160cqw / var(--len, 1))); /* Increased size and scale */
+ letter-spacing: 2px;
+ color: rgba(232,232,238,.85);
+ white-space: nowrap;
+ line-height: 1;
+}
+
+.switch {
+ position: relative;
+ width: 56px;
+ height: 28px;
+ display: inline-block;
+}
+.switch input { display: none; }
+.slider {
+ position: absolute;
+ inset: 0;
+ background: rgba(255,255,255,.14);
+ border: 1px solid rgba(255,255,255,.14);
+ border-radius: 999px;
+ transition: .2s ease;
+}
+.slider::before {
+ content: "";
+ position: absolute;
+ width: 22px;
+ height: 22px;
+ left: 3px;
+ top: 2px;
+ background: rgba(255,255,255,.92);
+ border-radius: 999px;
+ transition: .2s ease;
+}
+.switch input:checked + .slider {
+ background: rgba(40,240,122,.25);
+ border-color: rgba(40,240,122,.30);
+}
+.switch input:checked + .slider::before {
+ transform: translateX(28px);
+}
+
+.toolboxToggle {
+ position: fixed;
+ top: var(--toolbox-toggle-top);
+ right: 22px;
+ z-index: 90;
+ display: flex;
+ align-items: center;
+ gap: 10px;
+ padding: 10px 14px;
+ border-radius: 12px;
+ border: 1px solid rgba(255,255,255,.12);
+ background: rgba(0,0,0,.15);
+ backdrop-filter: blur(8px);
+ color: rgba(232,232,238,.95);
+ font-family: var(--bit-font);
+ font-weight: 800;
+ font-size: 16px;
+ letter-spacing: .12em;
+ text-transform: uppercase;
+ cursor: pointer;
+}
+.toolboxIcon {
+ font-size: 20px;
+ filter: drop-shadow(0 0 8px rgba(255,105,180,.35));
+}
+.toolboxToggle:hover { border-color: rgba(255,255,255,.22); }
+
+.panelCol {
+ position: fixed;
+ top: var(--toolbox-top);
+ right: 22px;
+ width: var(--toolbox-w);
+ z-index: 80;
+ display: flex;
+ flex-direction: column;
+ gap: 16px;
+ transform: translateX(0);
+ opacity: 1;
+ transition: transform 420ms cubic-bezier(.2,.9,.2,1), opacity 220ms ease;
+}
+.binaryPage.toolboxCollapsed .panelCol {
+ transform: translateX(calc(var(--toolbox-w) + 32px));
+ opacity: 0;
+ pointer-events: none;
+}
+
+.card {
+ background: rgba(255,255,255,.05);
+ border: 1px solid rgba(255,255,255,.10);
+ border-radius: 16px;
+ padding: 16px;
+ backdrop-filter: blur(10px);
+}
+.cardTitle {
+ font-family: var(--bit-font);
+ font-weight: 900;
+ letter-spacing: .14em;
+ text-transform: uppercase;
+ font-size: 18px;
+ color: rgba(232,232,238,.9);
+ margin-bottom: 12px;
+}
+
+.hint {
+ font-family: var(--bit-font);
+ font-size: 13px; /* Increased */
+ letter-spacing: .08em;
+ text-transform: uppercase;
+ color: rgba(232,232,238,.55);
+ margin-top: 10px;
+ line-height: 1.35; /* Restored line height so wraps look good */
+ /* Removed nowrap so it can wrap naturally */
+}
+
+.toggleRow {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: 12px;
+}
+
+.toggleLabel {
+ font-family: var(--bit-font);
+ font-size: 16px;
+ letter-spacing: .12em;
+ text-transform: uppercase;
+ white-space: nowrap;
+ color: var(--muted);
+ transition: color 0.2s, text-shadow 0.2s;
+}
+.toggleLabel.activeMode {
+ color: #28f07a;
+ text-shadow: 0 0 12px rgba(40,240,122,.45);
+}
+
+.subCard {
+ margin-top: 12px;
+ padding: 12px;
+ border-radius: 14px;
+ border: 1px solid rgba(255,255,255,.10);
+ background: rgba(0,0,0,.12);
+}
+.subTitle {
+ font-family: var(--bit-font);
+ font-weight: 900;
+ letter-spacing: .14em;
+ text-transform: uppercase;
+ font-size: 16px;
+ margin-bottom: 10px;
+ opacity: .85;
+}
+.bitWidthRow {
+ display: flex;
+ align-items: center;
+ gap: 10px;
+}
+.miniBtn {
+ width: 44px;
+ height: 44px;
+ border-radius: 12px;
+ border: 1px solid rgba(255,255,255,.12);
+ background: rgba(255,255,255,.06);
+ color: rgba(232,232,238,.9);
+ font-family: var(--bit-font);
+ font-weight: 900;
+ font-size: 22px;
+ cursor: pointer;
+}
+.miniBtn:hover { border-color: rgba(255,255,255,.22); }
+.bitInputWrap {
+ flex: 1 1 auto;
+ min-width: 0;
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: 10px;
+ border-radius: 12px;
+ border: 1px solid rgba(255,255,255,.10);
+ background: rgba(255,255,255,.04);
+ padding: 10px 12px;
+}
+.bitInputLabel {
+ font-family: var(--bit-font);
+ font-size: 16px;
+ letter-spacing: .12em;
+ text-transform: uppercase;
+ opacity: .7;
+}
+.bitInput {
+ width: 70px;
+ text-align: right;
+ font-family: var(--num-font);
+ font-size: 28px;
+ letter-spacing: 2px;
+ color: #28f07a;
+ background: transparent;
+ border: none;
+ outline: none;
+}
+
+.btn {
+ border: 1px solid rgba(255,255,255,.12);
+ background: rgba(255,255,255,.06);
+ color: rgba(232,232,238,.92);
+ border-radius: 12px;
+ padding: 10px 12px;
+ font-family: var(--bit-font);
+ font-size: 14px;
+ letter-spacing: .12em;
+ text-transform: uppercase;
+ font-weight: 900;
+ cursor: pointer;
+}
+.btn:hover { border-color: rgba(255,255,255,.22); }
+.btnAccent {
+ background: rgba(40,240,122,.12);
+ border-color: rgba(40,240,122,.22);
+}
+.btnAccent:hover { border-color: rgba(40,240,122,.35); }
+.btnHalf { width: calc(50% - 6px); }
+.btnWide { width: 100%; }
+.controlsRow { display: flex; gap: 12px; margin-bottom: 12px; }
+
+.btnRandomRunning {
+ background: rgba(40,240,122,.18) !important;
+ border-color: rgba(40,240,122,.35) !important;
+ animation: randomPulse 900ms ease-in-out infinite;
+}
+@keyframes randomPulse {
+ 0% { box-shadow: 0 0 0 rgba(40,240,122,0); }
+ 50% { box-shadow: 0 0 22px rgba(40,240,122,.35); }
+ 100% { box-shadow: 0 0 0 rgba(40,240,122,0); }
+}
+
+.toolRowCentered {
+ display: flex;
+ justify-content: center;
+ gap: 10px;
+ margin-bottom: 10px;
+}
+.toolBtn {
+ width: 46px;
+ height: 46px;
+ border-radius: 12px;
+ border: 1px solid rgba(255,255,255,.12);
+ background: rgba(255,255,255,.06);
+ color: rgba(232,232,238,.92);
+ font-family: var(--bit-font);
+ font-size: 16px;
+ font-weight: 900;
+ cursor: pointer;
+}
+.toolDec { background: rgba(255,80,80,.20); border-color: rgba(255,80,80,.25); }
+.toolInc { background: rgba(40,240,122,.18); border-color: rgba(40,240,122,.25); }
+.toolRow2 { display: flex; gap: 10px; margin-bottom: 10px; }
+
+.btnReset { color: rgba(232,232,238,.95); }
+.btnReset:hover {
+ background: rgba(255,80,80,.18);
+ border-color: rgba(255,80,80,.35);
+ animation: resetPulse 750ms ease-in-out infinite;
+}
+@keyframes resetPulse {
+ 0% { box-shadow: 0 0 0 rgba(255,80,80,0); }
+ 50% { box-shadow: 0 0 22px rgba(255,80,80,.38); }
+ 100% { box-shadow: 0 0 0 rgba(255,80,80,0); }
+}
+
+/* Updated dynamic scaling so scaled items stay proportionately larger */
+.bits.size-sm .bulb { width: 36px; height: 36px; margin-bottom: 4px; } /* Scaled up */
+.bits.size-sm .bitVal { font-size: min(34px, calc(160cqw / var(--len, 1))); } /* Scaled up */
+
+.bits.size-xs .bulb { width: 24px; height: 24px; margin-bottom: 2px; } /* Scaled up */
+.bits.size-xs .bitVal { font-size: min(22px, calc(160cqw / var(--len, 1))); } /* Scaled up */
+
+@media (max-width: 1100px) {
+ .binaryPage { --toolbox-w: 330px; }
+ .denaryValue { font-size: 56px; }
+ .binaryValue { font-size: 44px; }
+}
+@media (max-width: 900px) {
+ .binaryPage { --toolbox-w: 320px; }
+ .bitsGrid { grid-template-columns: repeat(var(--cols), minmax(84px, 1fr)); }
+}
\ No newline at end of file