You've already forked computing-box
Broken code
All checks were successful
Pre-release on non-main branches / prerelease (push) Successful in 29s
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:
BIN
public/images/BitBoxLogo.png
Normal file
BIN
public/images/BitBoxLogo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 101 KiB |
BIN
public/images/Educational_Impact.webp
Normal file
BIN
public/images/Educational_Impact.webp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 425 KiB |
BIN
public/images/computingbox-concept-illustration.webp
Normal file
BIN
public/images/computingbox-concept-illustration.webp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 73 KiB |
@@ -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>
|
||||||
@@ -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 --- */
|
||||||
|
|||||||
@@ -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); }
|
|
||||||
Reference in New Issue
Block a user