297 lines
11 KiB
HTML
297 lines
11 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>Retail Fixture Stress Comparator | Britten Mintz</title>
|
|
<style>
|
|
:root {
|
|
--bg: #0f0f13;
|
|
--surface: #1a1a20;
|
|
--accent: #ff4d4d; /* Neon red */
|
|
--text: #e0e0e0;
|
|
--muted: #888899;
|
|
--grid-line: rgba(255, 77, 77, 0.1);
|
|
}
|
|
body {
|
|
background-color: var(--bg);
|
|
color: var(--text);
|
|
font-family: 'Inter', system-ui, -apple-system, sans-serif;
|
|
margin: 0;
|
|
padding: 2rem;
|
|
line-height: 1.6;
|
|
max-width: 1200px;
|
|
margin-left: auto;
|
|
margin-right: auto;
|
|
}
|
|
header {
|
|
border-bottom: 1px solid var(--grid-line);
|
|
padding-bottom: 1.5rem;
|
|
margin-bottom: 2rem;
|
|
}
|
|
h1 {
|
|
font-size: 2.5rem;
|
|
letter-spacing: -0.05em;
|
|
margin: 0;
|
|
background: linear-gradient(90deg, #fff, var(--accent));
|
|
-webkit-background-clip: text;
|
|
-webkit-text-fill-color: transparent;
|
|
}
|
|
.subtitle {
|
|
color: var(--muted);
|
|
font-size: 1.1rem;
|
|
margin-top: 0.5rem;
|
|
}
|
|
.grid-layout {
|
|
display: grid;
|
|
grid-template-columns: 1fr 1fr;
|
|
gap: 2rem;
|
|
}
|
|
@media (max-width: 900px) {
|
|
.grid-layout { grid-template-columns: 1fr; }
|
|
}
|
|
.card {
|
|
background: var(--surface);
|
|
border: 1px solid rgba(255,255,255,0.05);
|
|
border-radius: 8px;
|
|
padding: 1.5rem;
|
|
}
|
|
label {
|
|
display: block;
|
|
font-size: 0.85rem;
|
|
color: var(--muted);
|
|
margin-bottom: 0.5rem;
|
|
text-transform: uppercase;
|
|
letter-spacing: 0.05em;
|
|
}
|
|
input, select {
|
|
width: 100%;
|
|
background: rgba(0,0,0,0.3);
|
|
border: 1px solid var(--grid-line);
|
|
color: white;
|
|
padding: 0.75rem;
|
|
border-radius: 4px;
|
|
font-family: monospace;
|
|
margin-bottom: 1.5rem;
|
|
box-sizing: border-box;
|
|
}
|
|
input:focus, select:focus {
|
|
outline: none;
|
|
border-color: var(--accent);
|
|
}
|
|
button {
|
|
background: var(--accent);
|
|
color: black;
|
|
border: none;
|
|
padding: 1rem 2rem;
|
|
font-weight: 700;
|
|
cursor: pointer;
|
|
text-transform: uppercase;
|
|
letter-spacing: 0.1em;
|
|
transition: all 0.2s;
|
|
}
|
|
button:hover {
|
|
transform: translateY(-2px);
|
|
box-shadow: 0 4px 12px rgba(255, 77, 77, 0.3);
|
|
}
|
|
.results-panel {
|
|
margin-top: 2rem;
|
|
}
|
|
.metric-row {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
padding: 1rem 0;
|
|
border-bottom: 1px solid rgba(255,255,255,0.05);
|
|
}
|
|
.metric-label { color: var(--muted); }
|
|
.metric-value { font-family: monospace; font-size: 1.1rem; }
|
|
.critical { color: var(--accent); }
|
|
|
|
.visualization {
|
|
height: 300px;
|
|
background: repeating-linear-gradient(
|
|
45deg,
|
|
transparent,
|
|
transparent 10px,
|
|
rgba(255, 77, 77, 0.05) 10px,
|
|
rgba(255, 77, 77, 0.05) 20px
|
|
);
|
|
border: 1px solid var(--grid-line);
|
|
position: relative;
|
|
overflow: hidden;
|
|
margin-top: 1rem;
|
|
}
|
|
.beam-deflection {
|
|
position: absolute;
|
|
bottom: 0;
|
|
left: 0;
|
|
right: 0;
|
|
height: 4px;
|
|
background: var(--accent);
|
|
transform-origin: center;
|
|
transition: transform 0.5s cubic-bezier(0.4, 0, 0.2, 1);
|
|
}
|
|
.data-source {
|
|
font-size: 0.75rem;
|
|
color: var(--muted);
|
|
margin-top: 1rem;
|
|
border-top: 1px dashed var(--grid-line);
|
|
padding-top: 0.5rem;
|
|
}
|
|
nav {
|
|
margin-bottom: 2rem;
|
|
font-size: 0.9rem;
|
|
}
|
|
nav a {
|
|
color: var(--muted);
|
|
text-decoration: none;
|
|
margin-right: 1.5rem;
|
|
}
|
|
nav a.active { color: var(--accent); }
|
|
|
|
.context-image {
|
|
width: 100%;
|
|
height: 200px;
|
|
object-fit: cover;
|
|
border-radius: 4px;
|
|
margin-bottom: 1.5rem;
|
|
filter: grayscale(0.3) contrast(1.1);
|
|
opacity: 0.9;
|
|
}
|
|
</style>
|
|
<script defer src="https://analytics.4ort.xyz/script.js" data-website-id="d3ed927c-888a-4a6c-ae5f-0b1c613ddf5b"></script>
|
|
</head>
|
|
<body>
|
|
<nav>
|
|
<a href="/index.html">Home</a>
|
|
<a href="/safety-net.html">Safety Net</a>
|
|
<a href="/fixture-comparator.html" class="active">Fixture Comparator</a>
|
|
</nav>
|
|
|
|
<header>
|
|
<h1>Fixture Stress Comparator</h1>
|
|
<div class="subtitle">Comparative analysis of structural timber vs. engineered composites under retail inventory loads.</div>
|
|
</header>
|
|
|
|
<main class="grid-layout">
|
|
<section class="controls card">
|
|
<img src="https://images.pexels.com/photos/32282975/pexels-photo-32282975.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=650&w=940" alt="Industrial shelving unit loaded with inventory" class="context-image">
|
|
|
|
<label>Span Length (mm)</label>
|
|
<input type="number" id="spanLength" value="1200" min="500" max="3000">
|
|
|
|
<label>Shelf Thickness (mm)</label>
|
|
<input type="number" id="thickness" value="18" min="10" max="50">
|
|
|
|
<label>Material Grade</label>
|
|
<select id="material">
|
|
<option value="fir">Douglas Fir (Structural)</option>
|
|
<option value="mdf">Medium Density Fiberboard (Standard)</option>
|
|
<option value="particle">Particle Board (Economy)</option>
|
|
</select>
|
|
|
|
<label>Distributed Load (kg/m)</label>
|
|
<input type="number" id="load" value="45" step="1">
|
|
|
|
<button onclick="calculate()">Compute Deflection</button>
|
|
|
|
<div class="data-source">
|
|
Constants sourced from ASTM D143-21 (Timber) & ISO 12469 (Composites). See <a href="/fixture-constants.json" style="color:var(--accent)">fixture-constants.json</a>.
|
|
</div>
|
|
</section>
|
|
|
|
<section class="results-panel card">
|
|
<div class="metric-row">
|
|
<span class="metric-label">Modulus of Elasticity (MOE)</span>
|
|
<span class="metric-value" id="moeResult">--</span>
|
|
</div>
|
|
<div class="metric-row">
|
|
<span class="metric-label">Max Allowable Deflection</span>
|
|
<span class="metric-value" id="allowDefl">--</span>
|
|
</div>
|
|
<div class="metric-row">
|
|
<span class="metric-label">Actual Deflection</span>
|
|
<span class="metric-value critical" id="actualDefl">--</span>
|
|
</div>
|
|
<div class="metric-row">
|
|
<span class="metric-label">Safety Factor</span>
|
|
<span class="metric-value" id="safetyFactor">--</span>
|
|
</div>
|
|
<div class="metric-row">
|
|
<span class="metric-label">Status</span>
|
|
<span class="metric-value" id="statusText" style="font-weight:bold;">WAITING</span>
|
|
</div>
|
|
|
|
<div class="visualization" id="vizContainer">
|
|
<!-- Beam representation -->
|
|
<div class="beam-deflection" id="beamVis"></div>
|
|
</div>
|
|
</section>
|
|
</main>
|
|
|
|
<script>
|
|
// Constants: MPa for MOE
|
|
const CONSTANTS = {
|
|
fir: { moe: 13000, density: 0.52, name: "Douglas Fir" },
|
|
mdf: { moe: 2800, density: 0.75, name: "MDF Standard" },
|
|
particle: { moe: 1900, density: 0.68, name: "Particle Board" }
|
|
};
|
|
|
|
function calculate() {
|
|
const span = parseFloat(document.getElementById('spanLength').value); // mm
|
|
const thickness = parseFloat(document.getElementById('thickness').value); // mm
|
|
const loadKg = parseFloat(document.getElementById('load').value); // kg/m
|
|
const matKey = document.getElementById('material').value;
|
|
|
|
const mat = CONSTANTS[matKey];
|
|
|
|
// Physics: Simply supported beam, uniform load
|
|
// w = force per length (N/mm)
|
|
const g = 9.81; // m/s^2
|
|
const w = (loadKg * g) / 1000; // N/mm
|
|
|
|
// Moment of Inertia (rectangular cross section)
|
|
// I = (b * h^3) / 12
|
|
// Assume b = 1mm for strip calculation, scale later
|
|
const b = 1;
|
|
const I = (b * Math.pow(thickness, 3)) / 12; // mm^4
|
|
|
|
// Deflection formula: y_max = (5 * w * L^4) / (384 * E * I)
|
|
const E_MPa = mat.moe; // N/mm^2
|
|
const deflection = (5 * w * Math.pow(span, 4)) / (384 * E_MPa * I);
|
|
|
|
// Allowable deflection: L/240 (standard for non-glass shelves)
|
|
const allowDefl = span / 240;
|
|
|
|
// Safety Factor based on deflection
|
|
let sf = allowDefl / deflection;
|
|
if (sf > 100) sf = 100; // Cap for display
|
|
|
|
// Update UI
|
|
document.getElementById('moeResult').textContent = `${mat.moe} MPa`;
|
|
document.getElementById('allowDefl').textContent = `${allowDefl.toFixed(2)} mm`;
|
|
document.getElementById('actualDefl').textContent = `${deflection.toFixed(2)} mm`;
|
|
document.getElementById('safetyFactor').textContent = sf.toFixed(2);
|
|
|
|
const statusEl = document.getElementById('statusText');
|
|
if (deflection <= allowDefl) {
|
|
statusEl.textContent = "STRUCTURALLY SOUND";
|
|
statusEl.style.color = "#4ade80";
|
|
} else {
|
|
statusEl.textContent = "FAILURE IMMINENT";
|
|
statusEl.style.color = "#ff4d4d";
|
|
}
|
|
|
|
// Visualize
|
|
const beamVis = document.getElementById('beamVis');
|
|
// Map deflection to visual rotation (exaggerated for visibility)
|
|
const vizRotation = Math.min(deflection * 2, 45);
|
|
beamVis.style.transform = `rotate(${vizRotation}deg)`;
|
|
}
|
|
|
|
// Init
|
|
calculate();
|
|
</script>
|
|
</body>
|
|
</html>
|