f42fa1d28c
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
618 lines
17 KiB
HTML
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>
|