Files
pi-multifx-pedal-ui/docs/helix-stadium-mockup.html
T
shawn f42fa1d28c fix: replace Inter with Space Grotesk (#audit-P3)
Inter was flagged as the only AI tell in the audit. Space Grotesk
fits the pedal brand — mechanical, precise character with excellent
small-screen readability.

Changes:
- src/index.css: body font-family Inter → Space Grotesk
- src/App.jsx: Google Fonts import + body font-family + inline style
- src/GlobalSettings.jsx, GlobalEQ.jsx, ModelBrowser.jsx, FocusView.jsx:
  inline fontFamily Inter → Space Grotesk
- index.html: added Google Fonts preconnect + Space Grotesk import
- docs/helix-stadium-mockup.html, docs/helix-stadium-concept.html: updated
  font imports and font-family
2026-06-14 17:26:03 -04:00

618 lines
17 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
<title>Pi Multi-FX — Helix Stadium Mockup</title>
<link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;700&family=Space+Grotesk:wght@300;400;500;600;700&display=swap" rel="stylesheet">
<style>
* { box-sizing: border-box; margin: 0; padding: 0; -webkit-tap-highlight-color: transparent; user-select: none; }
body { background: #0A0A0C; color: #F0EDE6; font-family: 'Space Grotesk', sans-serif; display: flex; justify-content: center; min-height: 100vh; }
.device {
width: 100%;
max-width: 800px;
height: 100vh;
display: flex;
flex-direction: column;
background: #0A0A0C;
overflow: hidden;
}
/* ── Status Bar ─────────────────────────────── */
.status-bar {
display: flex;
justify-content: space-between;
align-items: center;
padding: 8px 16px 6px;
background: #141418;
border-bottom: 1px solid #2A2A32;
height: 38px;
flex-shrink: 0;
}
.status-left { display: flex; align-items: center; gap: 8px; }
.status-dot { width: 7px; height: 7px; border-radius: 50%; background: #3AB87A; box-shadow: 0 0 8px #3AB87A; }
.status-label { font-family: 'JetBrains Mono', monospace; font-size: 10px; color: #8888A0; letter-spacing: .08em; }
.status-right { display: flex; align-items: center; gap: 14px; }
.status-preset { font-family: 'JetBrains Mono', monospace; font-size: 10px; color: #8888A0; }
.status-cpu { font-family: 'JetBrains Mono', monospace; font-size: 10px; color: #444458; }
.status-sig { font-size: 13px; color: #E8A030; }
/* ── MAIN AREA ───────────────────────────────── */
.main-area {
flex: 1;
display: flex;
flex-direction: column;
overflow: hidden;
min-height: 0;
}
/* ── Block Chain ─────────────────────────────── */
.chain-section {
padding: 12px 10px 10px;
background: #0A0A0C;
border-bottom: 1px solid #2A2A32;
flex-shrink: 0;
}
.chain-label {
font-size: 9px;
letter-spacing: .12em;
text-transform: uppercase;
font-weight: 600;
color: #444458;
margin-bottom: 8px;
padding: 0 4px;
}
.chain-scroll {
display: flex;
gap: 6px;
align-items: center;
overflow-x: auto;
padding: 4px 0;
scrollbar-width: none;
-ms-overflow-style: none;
}
.chain-scroll::-webkit-scrollbar { display: none; }
.terminator {
display: flex;
align-items: center;
justify-content: center;
min-width: 44px;
height: 56px;
padding: 0 14px;
border-radius: 8px;
font-size: 12px;
font-weight: 700;
letter-spacing: .08em;
text-transform: uppercase;
flex-shrink: 0;
}
.terminator-in { background: #1E4060; color: #3A7BA8; border: 1px solid #3A7BA855; }
.terminator-out { background: #1E4060; color: #3A7BA8; border: 1px solid #3A7BA855; }
.connector {
width: 18px;
height: 2px;
background: #2A2A32;
border-radius: 1px;
flex-shrink: 0;
position: relative;
}
.connector::after {
content: '';
position: absolute;
right: 0;
top: -2px;
width: 6px;
height: 6px;
border-right: 2px solid #2A2A32;
border-top: 2px solid #2A2A32;
transform: rotate(45deg);
}
.block-chip {
display: flex;
flex-direction: column;
align-items: stretch;
min-width: 120px;
height: 56px;
padding: 6px 10px;
border-radius: 8px;
background: #1C1C22;
border: 1px solid #2A2A32;
cursor: pointer;
flex-shrink: 0;
transition: all .12s;
position: relative;
}
.block-chip.active { border-color: #E8A030; background: #1C1C22; box-shadow: 0 0 12px #E8A03033; }
.block-chip.bypassed { opacity: .4; }
.block-chip.drag-over { border-color: #3AB87A; border-style: dashed; }
.block-top {
display: flex;
align-items: center;
gap: 6px;
}
.block-led {
width: 6px; height: 6px; border-radius: 50%; flex-shrink: 0;
}
.block-led.on { background: #3AB87A; box-shadow: 0 0 6px #3AB87A; }
.block-led.off { background: #444458; }
.block-name {
font-size: 12px;
font-weight: 600;
color: #F0EDE6;
flex: 1;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.block-type {
font-size: 8px;
font-weight: 700;
letter-spacing: .06em;
text-transform: uppercase;
padding: 1px 5px;
border-radius: 3px;
background: #7A521888;
color: #E8A030;
}
.block-drag {
position: absolute;
right: 4px;
top: 50%;
transform: translateY(-50%);
display: flex;
flex-direction: column;
gap: 2px;
padding: 4px;
cursor: grab;
opacity: .3;
}
.block-drag::before, .block-drag::after {
content: '';
display: block;
width: 3px;
height: 3px;
border-radius: 50%;
background: #8888A0;
}
/* ── PARAMS PANEL ────────────────────────────── */
.params-panel {
flex: 1;
display: flex;
flex-direction: column;
padding: 10px 12px;
overflow-y: auto;
min-height: 0;
gap: 12px;
}
.params-header {
display: flex;
justify-content: space-between;
align-items: center;
}
.params-title {
font-size: 10px;
letter-spacing: .12em;
text-transform: uppercase;
font-weight: 600;
color: #8888A0;
}
.param-actions {
display: flex;
gap: 6px;
}
.param-action {
padding: 4px 10px;
border-radius: 4px;
font-size: 10px;
font-weight: 600;
background: #1C1C22;
border: 1px solid #2A2A32;
color: #8888A0;
cursor: pointer;
transition: all .1s;
}
.param-action:active { transform: scale(.95); }
.knob-grid {
display: flex;
flex-wrap: wrap;
gap: 16px 20px;
justify-content: center;
padding: 8px 0;
}
.knob-wrap {
display: flex;
flex-direction: column;
align-items: center;
gap: 6px;
}
.knob-shell {
width: 60px;
height: 60px;
border-radius: 50%;
position: relative;
touch-action: none;
cursor: pointer;
}
.knob-bg {
width: 100%; height: 100%;
border-radius: 50%;
background: radial-gradient(circle at 35% 30%, #3A3A48, #1A1A22);
box-shadow: 0 2px 10px rgba(0,0,0,.6), inset 0 1px 0 rgba(255,255,255,.07);
}
.knob-dot {
position: absolute;
width: 3px;
height: 22px;
border-radius: 2px;
background: #E8A030;
left: 50%;
bottom: 8px;
transform-origin: bottom center;
transform: translateX(-50%) rotate(30deg);
box-shadow: 0 0 6px #E8A030;
}
.knob-value {
font-family: 'JetBrains Mono', monospace;
font-size: 11px;
color: #E8A030;
}
.knob-label {
font-size: 9px;
color: #8888A0;
letter-spacing: .06em;
text-transform: uppercase;
text-align: center;
}
/* slider */
.slider-group {
padding: 0 4px;
}
.slider-label-row {
display: flex;
justify-content: space-between;
font-size: 10px;
margin-bottom: 4px;
}
.slider-label { color: #8888A0; letter-spacing: .06em; text-transform: uppercase; }
.slider-value { font-family: 'JetBrains Mono', monospace; color: #E8A030; }
.slider-track {
width: 100%;
height: 6px;
border-radius: 3px;
background: #2A2A32;
position: relative;
cursor: pointer;
}
.slider-fill {
height: 100%;
border-radius: 3px;
background: #E8A030;
width: 65%;
box-shadow: 0 0 6px #E8A03066;
}
.slider-thumb {
position: absolute;
top: 50%;
left: 65%;
transform: translate(-50%, -50%);
width: 20px; height: 20px;
border-radius: 50%;
background: #F0EDE6;
border: 2px solid #0A0A0C;
box-shadow: 0 1px 6px rgba(0,0,0,.5);
}
.master-view {
display: flex;
flex-direction: column;
gap: 12px;
height: 100%;
justify-content: center;
}
.master-title {
font-size: 16px;
font-weight: 600;
color: #F0EDE6;
}
.master-sub {
font-size: 11px;
color: #8888A0;
}
/* ── FOOTSWITCH BAR ───────────────────────────── */
.footswitch-bar {
display: flex;
align-items: center;
gap: 6px;
padding: 6px 8px;
background: #141418;
border-top: 1px solid #2A2A32;
height: 72px;
flex-shrink: 0;
}
.bank-btn {
width: 32px;
height: 56px;
border-radius: 6px;
background: #1C1C22;
border: 1px solid #2A2A32;
color: #8888A0;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
font-size: 14px;
flex-shrink: 0;
transition: all .1s;
}
.bank-btn:active { background: #2A2A32; transform: scale(.95); }
.footswitch {
flex: 1;
height: 56px;
border-radius: 6px;
background: #1C1C22;
border: 1px solid #2A2A32;
cursor: pointer;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 2px;
transition: all .12s;
position: relative;
overflow: hidden;
}
.footswitch.active { border-color: #E8A03066; box-shadow: 0 0 10px #E8A03022; }
.footswitch:active { transform: scale(.96); }
.fs-number {
font-family: 'JetBrains Mono', monospace;
font-size: 11px;
font-weight: 700;
color: #E8A030;
}
.fs-label {
font-size: 9px;
font-weight: 500;
color: #F0EDE6;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
max-width: 90%;
text-align: center;
}
.fs-sub {
font-size: 7px;
color: #444458;
letter-spacing: .04em;
}
/* ── Add Block Button ── */
.add-block {
display: flex;
align-items: center;
justify-content: center;
min-width: 36px;
height: 56px;
border-radius: 8px;
border: 1px dashed #2A2A32;
background: transparent;
color: #444458;
font-size: 18px;
cursor: pointer;
flex-shrink: 0;
transition: all .12s;
}
.add-block:hover { border-color: #3A7BA8; color: #3A7BA8; }
</style>
</head>
<body>
<div class="device">
<!-- ── Status Bar ──────────────────────────── -->
<div class="status-bar">
<div class="status-left">
<div class="status-dot"></div>
<span class="status-label">CONNECTED</span>
</div>
<div class="status-right">
<span class="status-preset">Plexi Crunch</span>
<span class="status-cpu">CPU 12%</span>
<span class="status-sig">▃▅▇█</span>
</div>
</div>
<!-- ── Main Area ───────────────────────────── -->
<div class="main-area">
<!-- ── Block Chain ────────────────────────── -->
<div class="chain-section">
<div class="chain-label">Signal Chain</div>
<div class="chain-scroll">
<div class="terminator terminator-in">IN</div>
<div class="connector"></div>
<div class="block-chip active" onclick="selectBlock(this, 0)">
<div class="block-top">
<div class="block-led on"></div>
<span class="block-name">Compressor</span>
<span class="block-type">DYN</span>
</div>
<div class="block-drag"></div>
</div>
<div class="connector"></div>
<div class="block-chip" onclick="selectBlock(this, 1)">
<div class="block-top">
<div class="block-led on"></div>
<span class="block-name">Overdrive</span>
<span class="block-type">DRV</span>
</div>
<div class="block-drag"></div>
</div>
<div class="connector"></div>
<div class="block-chip bypassed" onclick="selectBlock(this, 2)">
<div class="block-top">
<div class="block-led off"></div>
<span class="block-name">Chorus</span>
<span class="block-type">MOD</span>
</div>
<div class="block-drag"></div>
</div>
<div class="connector"></div>
<div class="block-chip" onclick="selectBlock(this, 3)">
<div class="block-top">
<div class="block-led on"></div>
<span class="block-name">Delay</span>
<span class="block-type">TIME</span>
</div>
<div class="block-drag"></div>
</div>
<div class="connector"></div>
<div class="block-chip" onclick="selectBlock(this, 4)">
<div class="block-top">
<div class="block-led on"></div>
<span class="block-name">Reverb</span>
<span class="block-type">AMB</span>
</div>
<div class="block-drag"></div>
</div>
<div class="connector"></div>
<div class="terminator terminator-out">OUT</div>
<div class="add-block" title="Add block">+</div>
</div>
</div>
<!-- ── Params Panel ───────────────────────── -->
<div class="params-panel" id="paramsPanel">
<div class="params-header">
<div class="params-title" id="paramsTitle">Parameters — Compressor</div>
<div class="param-actions">
<div class="param-action" style="color:#3AB87A">○ Bypass</div>
<div class="param-action" style="color:#C84040">✕ Delete</div>
<div class="param-action">↻ Reset</div>
</div>
</div>
<div class="knob-grid">
<div class="knob-wrap">
<div class="knob-shell">
<div class="knob-bg"></div>
<div class="knob-dot" style="transform: translateX(-50%) rotate(-30deg)"></div>
</div>
<div class="knob-value" style="color: #E8A030">-20</div>
<div class="knob-label">Threshold</div>
</div>
<div class="knob-wrap">
<div class="knob-shell">
<div class="knob-bg"></div>
<div class="knob-dot" style="transform: translateX(-50%) rotate(45deg)"></div>
</div>
<div class="knob-value" style="color: #3A7BA8">3:1</div>
<div class="knob-label">Ratio</div>
</div>
<div class="knob-wrap">
<div class="knob-shell">
<div class="knob-bg"></div>
<div class="knob-dot" style="transform: translateX(-50%) rotate(-15deg)"></div>
</div>
<div class="knob-value" style="color: #E8A030">5</div>
<div class="knob-label">Attack</div>
</div>
<div class="knob-wrap">
<div class="knob-shell">
<div class="knob-bg"></div>
<div class="knob-dot" style="transform: translateX(-50%) rotate(20deg)"></div>
</div>
<div class="knob-value" style="color: #E8A030">100</div>
<div class="knob-label">Release</div>
</div>
<div class="knob-wrap">
<div class="knob-shell">
<div class="knob-bg"></div>
<div class="knob-dot" style="transform: translateX(-50%) rotate(60deg)"></div>
</div>
<div class="knob-value" style="color: #E8A030">1.0</div>
<div class="knob-label">Makeup</div>
</div>
</div>
<div class="slider-group">
<div class="slider-label-row">
<span class="slider-label">Mix</span>
<span class="slider-value">65%</span>
</div>
<div class="slider-track">
<div class="slider-fill" style="width:65%"></div>
<div class="slider-thumb" style="left:65%"></div>
</div>
</div>
</div>
</div>
<!-- ── Footswitch Bar ────────────────────────── -->
<div class="footswitch-bar">
<div class="bank-btn"></div>
<div class="footswitch active">
<div class="fs-number">1</div>
<div class="fs-label">Plexi Crunch</div>
<div class="fs-sub">Preset</div>
</div>
<div class="footswitch">
<div class="fs-number">2</div>
<div class="fs-label">Mod Hi-Gain</div>
<div class="fs-sub">Preset</div>
</div>
<div class="footswitch">
<div class="fs-number">3</div>
<div class="fs-label">Clean Chimey</div>
<div class="fs-sub">Preset</div>
</div>
<div class="footswitch">
<div class="fs-number">4</div>
<div class="fs-label">Soul Blues</div>
<div class="fs-sub">Preset</div>
</div>
<div class="bank-btn"></div>
</div>
</div>
<script>
let selectedIdx = 0;
const chips = document.querySelectorAll('.block-chip');
const paramsTitle = document.getElementById('paramsTitle');
function selectBlock(el, idx) {
chips.forEach(c => c.classList.remove('active'));
el.classList.add('active');
selectedIdx = idx;
const name = el.querySelector('.block-name').textContent;
paramsTitle.textContent = 'Parameters — ' + name;
}
</script>
</body>
</html>