Broken code
All checks were successful
Pre-release on non-main branches / prerelease (push) Successful in 29s

Signed-off-by: Alexander Lyall <alex@adcm.uk>
This commit is contained in:
2026-03-01 17:52:36 +00:00
parent 09e0499ba3
commit bb8e0c1969
6 changed files with 132 additions and 155 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 101 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 425 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 73 KiB

View File

@@ -2,36 +2,54 @@
import BaseLayout from "../layouts/BaseLayout.astro"; import BaseLayout from "../layouts/BaseLayout.astro";
--- ---
<BaseLayout title="About | Computing:Box"> <BaseLayout title="About - Computing:Box">
<article style="max-width: 900px; margin: 0 auto;"> <article style="max-width: 1100px; margin: 0 auto; width: 100%;">
<div class="card" style="margin-bottom: 40px;">
<h1 class="brandName" style="font-size: 32px; color: var(--text); margin-bottom: 16px;">The New Computing:Box Experience</h1> <div style="text-align: center; margin-bottom: 60px;">
<p style="color: var(--muted); font-size: 18px; line-height: 1.6;"> <img src="/images/computing-box-logo.svg" alt="Computing:Box Logo" style="width: 250px; border-radius: 20px; box-shadow: 0 12px 40px rgba(0,0,0,0.5);" />
The platform has been rebuilt from the ground up to provide a deeper, more professional simulation environment. <h1 class="brandName" style="font-size: 42px; margin-top: 20px; color: var(--text);">The New Computing:Box Experience</h1>
We've introduced several key simulators and interface upgrades to support the modern Computing classroom: </div>
</p>
<div class="divider"></div>
<ul style="list-style: none; padding: 0; display: grid; grid-template-columns: 1fr 1fr; gap: 15px;"> <div class="card" style="margin-bottom: 40px;">
<p style="color: var(--muted); font-size: 18px; line-height: 1.6; text-align: center;">
Rebuilt from the ground up to provide a deeper, more professional simulation environment for the UK Computing Curriculum.
</p>
<div class="divider"></div>
<ul style="list-style: none; padding: 0; display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 20px;">
<li style="color: var(--text);"><span style="color: var(--accent);">✔</span> New User Interface (Responsive)</li> <li style="color: var(--text);"><span style="color: var(--accent);">✔</span> New User Interface (Responsive)</li>
<li style="color: var(--text);"><span style="color: var(--accent);">✔</span> Two's Complement Simulator</li> <li style="color: var(--text);"><span style="color: var(--accent);">✔</span> Two's Complement Simulator</li>
<li style="color: var(--text);"><span style="color: var(--accent);">✔</span> Extended Binary Simulator (Custom bit sizes)</li> <li style="color: var(--text);"><span style="color: var(--accent);">✔</span> Extended Binary Simulator (Custom sizes)</li>
<li style="color: var(--text);"><span style="color: var(--accent);">✔</span> Unified Binary Simulator (Unsigned & Two's Complement)</li> <li style="color: var(--text);"><span style="color: var(--accent);">✔</span> Unified Binary Simulator</li>
<li style="color: var(--text);"><span style="color: var(--accent);">✔</span> Extended Hexadecimal Simulator</li> <li style="color: var(--text);"><span style="color: var(--accent);">✔</span> Extended Hexadecimal Simulator</li>
<li style="color: var(--text);"><span style="color: var(--accent);">✔</span> Unified Hexadecimal Simulator (GCSE & A Level)</li> <li style="color: var(--text);"><span style="color: var(--accent);">✔</span> Unified Hexadecimal Simulator</li>
<li style="color: var(--text);"><span style="color: var(--accent);">✔</span> Enhanced Gate Simulator (Truth Table Creator)</li> <li style="color: var(--text);"><span style="color: var(--accent);">✔</span> Enhanced Gate Simulator (Truth Tables)</li>
<li style="color: var(--text);"><span style="color: var(--accent);">✔</span> Compound Gate Simulator</li> <li style="color: var(--text);"><span style="color: var(--accent);">✔</span> Compound Gate Simulator</li>
<li style="color: var(--text);"><span style="color: var(--accent);">✔</span> Computer Components Simulator</li> <li style="color: var(--text);"><span style="color: var(--accent);">✔</span> Computer Components Simulator</li>
</ul> </ul>
</div> </div>
<div class="card"> <div style="display: grid; grid-template-columns: 1fr 1fr; gap: 40px; margin-bottom: 40px; align-items: center;">
<h2 class="brandName" style="font-size: 24px; margin-bottom: 12px;">Educational Impact</h2> <div class="card">
<p style="color: var(--muted);"> <h2 class="brandName" style="font-size: 24px; color: var(--accent);">From Bit:Box to Computing:Box</h2>
Computing:Box is designed to help students learn computing concepts step by step, using clear visuals and simple interactions. <p style="color: var(--muted); line-height: 1.6;">Computing:Box began as Bit:Box, a digital version of a physical Binary Light Box created by Mr Davis. The original device used lightbulbs to show how binary numbers are built.</p>
By changing values and seeing results straight away, students can build understanding at their own pace. </div>
</p> <img src="/images/BitBoxLogo.png" alt="Bit:Box Logo" style="width: 100%; border-radius: 16px; border: 1px solid var(--line);" />
</div>
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 40px; margin-bottom: 40px; align-items: center;">
<img src="/images/computingbox-concept-illustration.webp" alt="Simulator Illustration" style="width: 100%; border-radius: 16px; border: 1px solid var(--line);" />
<div class="card">
<h2 class="brandName" style="font-size: 24px; color: var(--accent);">Learning by Doing</h2>
<p style="color: var(--muted); line-height: 1.6;">Students can interact with simulations, change values, and see results instantly. This helps them understand how concepts work rather than memorising rules.</p>
</div>
</div>
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 40px; align-items: center;">
<div class="card">
<h2 class="brandName" style="font-size: 24px; color: var(--accent);">Educational Impact</h2>
<p style="color: var(--muted); line-height: 1.6;">Designed for use in lessons, independent study, and revision, helping students build confidence as they explore how computers work.</p>
</div>
<img src="/images/Educational_Impact.webp" alt="Classroom Impact" style="width: 100%; border-radius: 16px; border: 1px solid var(--line);" />
</div> </div>
</article> </article>
</BaseLayout> </BaseLayout>

View File

@@ -4,13 +4,17 @@
(() => { (() => {
const workspace = document.getElementById("workspace"); const workspace = document.getElementById("workspace");
const viewport = document.getElementById("viewport"); const viewport = document.getElementById("viewport");
const wireLayer = document.getElementById("wireLayer"); const internalLayer = document.getElementById("wireLayerInternal");
const externalLayer = document.getElementById("wireLayerExternal");
const specsContainer = document.getElementById("buildSpecsContainer"); const specsContainer = document.getElementById("buildSpecsContainer");
const toolboxGrid = document.getElementById("toolboxGrid"); const toolboxGrid = document.getElementById("toolboxGrid");
const btnClearBoard = document.getElementById("btnClearBoard"); const btnClearBoard = document.getElementById("btnClearBoard");
const toolboxToggle = document.getElementById("toolboxToggle"); const toolboxToggle = document.getElementById("toolboxToggle");
const pcPage = document.getElementById("pcPage"); const pcPage = document.getElementById("pcPage");
// Parts defined as "Outside the Chassis"
const EXTERNAL_TYPES = ['MONITOR', 'KEYBOARD', 'MOUSE', 'WEBCAM', 'SPEAKER', 'MIC', 'PRINTER'];
/* --- Extensive PC Component Library --- */ /* --- Extensive PC Component Library --- */
const PC_PARTS = { const PC_PARTS = {
'CASE': { 'CASE': {
@@ -98,17 +102,11 @@
panY = mouseY - (mouseY - panY) * (newZoom / zoom); panY = mouseY - (mouseY - panY) * (newZoom / zoom);
zoom = newZoom; updateViewport(); zoom = newZoom; updateViewport();
} }
function getPortCoords(nodeId, portDataAttr) { function getPortCoords(nodeId, portId) {
const node = nodes[nodeId]; const n = nodes[nodeId];
if (!node || !node.el) return {x:0, y:0}; const pEl = n.el.querySelector(`[data-port="${portId}"]`);
const portEl = node.el.querySelector(`[data-port="${portDataAttr}"]`); const r = pEl.getBoundingClientRect(), w = workspace.getBoundingClientRect();
if (!portEl) return {x:0, y:0}; return { x: (r.left - w.left - panX) / zoom, y: (r.top - w.top - panY) / zoom };
const wsRect = workspace.getBoundingClientRect();
const portRect = portEl.getBoundingClientRect();
return {
x: (portRect.left - wsRect.left - panX + portRect.width / 2) / zoom,
y: (portRect.top - wsRect.top - panY + portRect.height / 2) / zoom
};
} }
function drawBezier(x1, y1, x2, y2) { function drawBezier(x1, y1, x2, y2) {
const cpDist = Math.abs(x2 - x1) * 0.6 + 20; const cpDist = Math.abs(x2 - x1) * 0.6 + 20;
@@ -117,17 +115,22 @@
/* --- Rendering --- */ /* --- Rendering --- */
function renderWires() { function renderWires() {
let svgHTML = ''; let intHtml = '', extHtml = '';
connections.forEach(conn => { connections.forEach(c => {
const from = getPortCoords(conn.fromNode, conn.fromPort); const fromNode = nodes[c.fN];
const to = getPortCoords(conn.toNode, conn.toPort); const toNode = nodes[c.tN];
const isSelected = conn.id === selectedWireId; if (!fromNode || !toNode) return;
svgHTML += `<path class="pb-wire active ${isSelected ? 'selected' : ''}" d="${drawBezier(from.x, from.y, to.x, to.y)}" data-conn-id="${conn.id}" />`;
const p1 = getPortCoords(c.fN, c.fP);
const p2 = getPortCoords(c.tN, c.tP);
const path = `<path class="pb-wire active" d="M ${p1.x} ${p1.y} C ${p1.x+60} ${p1.y}, ${p2.x-60} ${p2.y}, ${p2.x} ${p2.y}" />`;
// Determine if cable is Internal (under panel) or External (on top)
(EXTERNAL_TYPES.includes(fromNode.type) || EXTERNAL_TYPES.includes(toNode.type)) ? extHtml += path : intHtml += path;
}); });
if (wiringStart && tempWirePath) { internalLayer.innerHTML = intHtml;
svgHTML += `<path class="pb-wire pb-wire-temp" d="${drawBezier(wiringStart.x, wiringStart.y, tempWirePath.x, tempWirePath.y)}" />`; externalLayer.innerHTML = extHtml;
} evaluateBuild();
wireLayer.innerHTML = svgHTML;
} }
function updateNodePositions() { function updateNodePositions() {
@@ -145,67 +148,10 @@
/* --- Seven-Segment Diagnostics Engine --- */ /* --- Seven-Segment Diagnostics Engine --- */
function evaluateBuild() { function evaluateBuild() {
if(!specsContainer) return; const hasMB = Object.values(nodes).some(n => n.type === 'MB' && n.snappedTo);
const hasCPU = Object.values(nodes).some(n => n.type === 'CPU' && n.snappedTo);
let hasCase = false, hasMB = false, hasCPU = false, hasCooler = false, hasRAM = false, hasPSU = false; const boot = hasMB && hasCPU && connections.some(c => nodes[c.fN].type === 'MONITOR' || nodes[c.tN].type === 'MONITOR');
let hasStorage = false, hasGPU = false; workspace.classList.toggle('system-running', boot);
let mbPwr = false, gpuPwr = false;
let usbCount = 0, dispConn = false, audConn = false;
let caseNode = Object.values(nodes).find(n => n.type === 'CASE');
let mbNode = Object.values(nodes).find(n => n.type === 'MB');
if (caseNode) {
hasCase = true;
if (caseNode.slots['MB1']) hasMB = true;
if (caseNode.slots['PSU1']) hasPSU = true;
if (caseNode.slots['HDD1'] || caseNode.slots['HDD2'] || caseNode.slots['SATA_SSD1'] || caseNode.slots['SATA_SSD2']) hasStorage = true;
} else if (mbNode) {
hasMB = true; // Motherboard exists outside case
}
if (mbNode) {
if (mbNode.slots['CPU1']) hasCPU = true;
if (mbNode.slots['COOLER1']) hasCooler = true;
if (mbNode.slots['RAM1'] || mbNode.slots['RAM2'] || mbNode.slots['RAM3'] || mbNode.slots['RAM4']) hasRAM = true;
if (mbNode.slots['PCIE1'] || mbNode.slots['PCIE2']) hasGPU = true;
if (mbNode.slots['M2_1'] || mbNode.slots['M2_2']) hasStorage = true;
}
// Check Cables
connections.forEach(c => {
let n1 = nodes[c.fromNode], n2 = nodes[c.toNode];
if(!n1 || !n2) return;
let types = [n1.type, n2.type];
if(types.includes('MB') && types.includes('PSU')) mbPwr = true;
if(types.includes('GPU') && types.includes('PSU')) gpuPwr = true;
if(types.includes('MB') && ['KEYBOARD','MOUSE','WEBCAM','MIC','PRINTER'].some(t => types.includes(t))) usbCount++;
if(types.includes('MB') && types.includes('SPEAKER')) audConn = true;
if((types.includes('MB') || types.includes('GPU')) && types.includes('MONITOR')) dispConn = true;
});
const isBootable = (hasMB && hasCPU && hasCooler && hasRAM && hasPSU && hasStorage && mbPwr && (hasGPU ? gpuPwr : true) && dispConn);
specsContainer.innerHTML = `
<div class="diag-cat">Core System</div>
<div class="diag-row"><span>CHASSIS</span><span style="color: ${hasCase ? '#28f07a' : '#ff5555'}">${hasCase ? 'OK' : 'ERR'}</span></div>
<div class="diag-row"><span>MOTHERBOARD</span><span style="color: ${hasMB ? '#28f07a' : '#ff5555'}">${hasMB ? 'OK' : 'ERR'}</span></div>
<div class="diag-row"><span>CPU</span><span style="color: ${hasCPU ? '#28f07a' : '#ff5555'}">${hasCPU ? 'OK' : 'ERR'}</span></div>
<div class="diag-row"><span>COOLING</span><span style="color: ${hasCooler ? '#28f07a' : '#ff5555'}">${hasCooler ? 'OK' : 'ERR'}</span></div>
<div class="diag-row"><span>MEMORY</span><span style="color: ${hasRAM ? '#28f07a' : '#ff5555'}">${hasRAM ? 'OK' : 'ERR'}</span></div>
<div class="diag-row"><span>POWER SPLY</span><span style="color: ${hasPSU ? '#28f07a' : '#ff5555'}">${hasPSU ? 'OK' : 'ERR'}</span></div>
<div class="diag-cat">Connections</div>
<div class="diag-row"><span>MB POWER</span><span style="color: ${mbPwr ? '#28f07a' : '#ff5555'}">${mbPwr ? 'OK' : 'ERR'}</span></div>
<div class="diag-row"><span>STORAGE</span><span style="color: ${hasStorage ? '#28f07a' : '#ff5555'}">${hasStorage ? 'OK' : 'ERR'}</span></div>
<div class="diag-row"><span>GPU POWER</span><span style="color: ${!hasGPU ? '#888' : (gpuPwr ? '#28f07a' : '#ff5555')}">${!hasGPU ? 'N/A' : (gpuPwr ? 'OK' : 'ERR')}</span></div>
<div class="diag-row"><span>DISPLAY</span><span style="color: ${dispConn ? '#28f07a' : '#ff5555'}">${dispConn ? 'OK' : 'ERR'}</span></div>
<div class="diag-row"><span>USB DEVS</span><span style="color: #55aaff">${usbCount}</span></div>
<hr style="border-color: rgba(255,255,255,0.1); margin: 12px 0 8px 0;">
<div style="text-align:center; font-size: 28px; color: ${isBootable ? '#28f07a' : '#ff5555'}; font-family: var(--bit-font); letter-spacing: 2px;">
${isBootable ? 'BOOTING...' : 'HALTED'}
</div>
`;
} }
/* --- Node Creation & Snapping --- */ /* --- Node Creation & Snapping --- */
@@ -250,16 +196,12 @@
evaluateBuild(); evaluateBuild();
} }
// Recursive movement to handle nested snaps (MB inside CASE inside ...) /* --- Movement & Snap Logic (Restored from your Verified Working Script) --- */
function moveNodeRecursive(nodeId, dx, dy) { function moveNodeRecursive(nodeId, dx, dy) {
const n = nodes[nodeId]; const n = nodes[nodeId]; if(!n) return;
if(!n) return;
n.x += dx; n.y += dy; n.x += dx; n.y += dy;
if(n.slots) { if(n.slots) { Object.keys(n.slots).forEach(k => { if(typeof n.slots[k] === 'string') moveNodeRecursive(n.slots[k], dx, dy); }); }
Object.keys(n.slots).forEach(k => { if(n.el) { n.el.style.left = n.x + 'px'; n.el.style.top = n.y + 'px'; }
if(typeof n.slots[k] === 'string') moveNodeRecursive(n.slots[k], dx, dy);
});
}
} }
/* --- Inspect Mode --- */ /* --- Inspect Mode --- */

View File

@@ -23,58 +23,75 @@
--text: #e8e8ee; --text: #e8e8ee;
--muted: #a9acb8; --muted: #a9acb8;
--line: rgba(255,255,255,.10); --line: rgba(255,255,255,.10);
--accent: #28f07a;
--ui-font: "Inter", system-ui, -apple-system, Segoe UI, Roboto, Arial, sans-serif; --ui-font: "Inter", system-ui, -apple-system, sans-serif;
--num-font: "DSEG7Classic", ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", monospace;
--bit-font: "SevenSegment", monospace; --bit-font: "SevenSegment", monospace;
} }
* { box-sizing: border-box; } * { box-sizing: border-box; }
html, body { height: 100%; } html, body { height: 100%; margin: 0; }
body { margin: 0; background: var(--bg); color: var(--text); font-family: var(--ui-font); display: flex; flex-direction: column; } body { background: var(--bg); color: var(--text); font-family: var(--ui-font); display: flex; flex-direction: column; }
/* --- BASE LAYOUT --- */ /* --- SITE NAVIGATION (Fixed Height) --- */
.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); } .siteNav {
.navInner { height: 100%; max-width: 1400px; margin: 0 auto; padding: 0 20px; display: flex; align-items: center; justify-content: space-between; gap: 24px; } position: sticky;
top: 0;
z-index: 500;
height: var(--nav-h);
background: rgba(45, 44, 56, 0.95);
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;
}
.brand { display: flex; align-items: center; gap: 12px; text-decoration: none; color: var(--text); } .brand { display: flex; align-items: center; gap: 12px; text-decoration: none; color: var(--text); }
.brandLogo { width: 2.5em; height: 2.5em; image-rendering: pixelated; } .brandLogo { width: 32px; height: 32px; }
.brandName { letter-spacing: .12em; font-weight: 900; font-size: 18px; } .brandName { letter-spacing: .12em; font-weight: 900; 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; }
.navLinks a:hover, .navLinks a.active { color: #e8e8ee; }
.pageWrap { flex: 1; max-width: 1400px; margin: 0 auto; padding: 0 20px 40px; width: 100%; display: flex; flex-direction: column; } .navLinks { display: flex; gap: 24px; }
.siteFooter { border-top: 1px solid var(--line); background: rgba(0,0,0,.08); } .navLinks a {
.footerInner { max-width: 1400px; margin: 0 auto; padding: 18px 20px; color: var(--muted); font-size: 12px; letter-spacing: .08em; display: flex; flex-direction: column; gap: 6px; } color: var(--muted);
text-decoration: none;
font-weight: 800;
font-size: 14px;
transition: color 0.2s;
}
/* --- SHARED UI COMPONENTS (Used by ALL Simulators) --- */ /* Active Link Fix */
.divider { height: 1px; background: rgba(255,255,255,.08); margin: 16px 0 16px; } .navLinks a.active,
.navLinks a:hover {
color: #ffffff !important;
}
.bulb { width: 44px; height: 44px; 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; } /* --- LAYOUT --- */
.bulb svg { width: 100%; height: 100%; display: block; } .pageWrap { flex: 1; width: 100%; max-width: 1400px; margin: 0 auto; padding: 40px 20px; display: flex; flex-direction: column; }
.bulb.on { color: #ffd86b !important; filter: drop-shadow(0 0 14px rgba(255, 216, 107, 1)) !important; }
.bulb.on svg { fill: #ffd86b !important; }
.switch { position: relative; width: 56px; height: 28px; display: inline-block; } /* --- FOOTER (Centered Links at Top) --- */
.switch input { display: none; } .siteFooter { background: rgba(0,0,0,.08); border-top: 1px solid var(--line); padding: 40px 0; }
.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; } .footerInner {
.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; pointer-events: none; } max-width: 1400px;
.switch input:checked + .slider { background: rgba(40,240,122,.25); border-color: rgba(40,240,122,.30); } margin: 0 auto;
.switch input:checked + .slider::before { transform: translateX(28px); } padding: 0 20px;
color: var(--muted);
font-size: 13px;
display: flex;
flex-direction: column;
align-items: center;
text-align: center;
gap: 12px;
}
.footerLegal { margin-bottom: 8px; }
.footerLegal a { color: var(--muted); text-decoration: underline; font-weight: 700; margin: 0 10px; }
.footerLegal a:hover { color: #fff; }
.card { background: rgba(255,255,255,.05); border: 1px solid rgba(255,255,255,.10); border-radius: 16px; padding: 16px; backdrop-filter: blur(10px); } /* Shared UI Components */
.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; } .card { background: rgba(255,255,255,.05); border: 1px solid rgba(255,255,255,.10); border-radius: 16px; padding: 24px; backdrop-filter: blur(10px); }
.hint { font-family: var(--bit-font); font-size: 13px; letter-spacing: .08em; text-transform: uppercase; color: rgba(232,232,238,.55); margin-top: 10px; line-height: 1.35; } .divider { height: 1px; background: rgba(255,255,255,.08); margin: 16px 0 16px; }
.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%; }
.btnReset { color: rgba(232,232,238,.95); }
.btnReset:hover { background: rgba(255,80,80,.18); border-color: rgba(255,80,80,.35); }
.toolboxToggle { position: fixed; top: var(--toolbox-toggle-top, calc(var(--nav-h) + 16px)); 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); }