Site phase 1: standard VARASYS header + nav; editor → PE-1; Concepts library

- Shared site header in src/base.css (.site-head/.site-nav/.brand-logo + theme-
  aware logo + .tbtn). Applied to player/stage/micro (replacing their text
  topbars) so the VARASYS logo + tagline + Editor/Concepts nav is on every page.
- Rebrand the editor: "Stackable Metronome" → "PE-1 — PolyMeter Editor" (title +
  h1), with a Concepts link in its header.
- New concepts.html — the PolyMeter Concepts library: cards for the editor and
  each form factor (PM-1 Initial/Stage, PM-µ Micro) + a "more coming" card.
- build.sh + deploy.sh build/deploy concepts.html; deploy.sh now loops over pages.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Me Here 2026-05-26 11:38:50 -05:00
parent c1488c0d32
commit 6568076563
8 changed files with 196 additions and 36 deletions

View file

@ -30,7 +30,6 @@ def build(name):
out.write_text(src) out.write_text(src)
return out.stat().st_size return out.stat().st_size
i = build("index.html"); p = build("player.html"); a = build("stage.html"); u = build("micro.html") for name in ("index.html","player.html","stage.html","micro.html","concepts.html"):
print("built index.html (%dKB) + player.html (%dKB) + stage.html (%dKB) + micro.html (%dKB)" print("built %s (%dKB)" % (name, build(name) // 1024))
% (i // 1024, p // 1024, a // 1024, u // 1024))
PY PY

126
concepts.html Normal file
View file

@ -0,0 +1,126 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>PolyMeter Concepts — VARASYS</title>
<link rel="icon" type="image/svg+xml" href="data:image/svg+xml;base64,@BUILD:favicon@">
<!-- The PolyMeter concept library: an ever-expanding gallery of form factors
(the PE-1 editor + the PM-1/PM-µ hardware mockups + web widgets). Static page. -->
<script>
(function(){ try{
var p = localStorage.getItem("metronome.theme");
if (p!=="light" && p!=="dark" && p!=="system") p = "system";
document.documentElement.dataset.theme = p==="system" ? (matchMedia("(prefers-color-scheme: light)").matches ? "light" : "dark") : p;
} catch(e){ document.documentElement.dataset.theme = "dark"; } })();
</script>
<style>
/*@BUILD:include:src/base.css@*/
:root{ --bg1:#12151c; --bg2:#05070a; --txt:#c7d0db; --muted:#7f8b9a; --link:#6cb6ff;
--panel-bg:#161b22; --panel-bd:#2a313c; }
:root[data-theme="light"]{ --bg1:#f5f8fc; --bg2:#dde4ec; --txt:#1e2630; --muted:#5c6776; --link:#1769c4;
--panel-bg:#ffffff; --panel-bd:#d2dae4; }
body{ margin:0; min-height:100vh; padding:22px 16px 56px; color:var(--txt);
background:radial-gradient(circle at 50% -8%, var(--bg1), var(--bg2)); }
a{ color:var(--link); }
main{ width:100%; max-width:980px; margin:26px auto 0; }
h1{ font-size:24px; margin:0 0 4px; }
.lead{ margin:0 0 22px; color:var(--muted); font-size:14px; line-height:1.55; max-width:62ch; }
.grid{ display:grid; grid-template-columns:repeat(auto-fit, minmax(250px, 1fr)); gap:16px; }
.card{ background:var(--panel-bg); border:1px solid var(--panel-bd); border-radius:14px; padding:16px;
display:flex; flex-direction:column; gap:9px; }
.card h3{ margin:0; font-size:16px; }
.chip{ align-self:flex-start; font-size:10px; text-transform:uppercase; letter-spacing:.08em;
padding:2px 9px; border-radius:999px; border:1px solid var(--panel-bd); color:var(--muted); }
.chip.hw{ color:var(--cyan); border-color:rgba(10,179,247,.45); }
.chip.app{ color:#2fe07a; border-color:rgba(47,224,160,.45); }
.card p{ margin:0; font-size:13px; color:var(--muted); line-height:1.5; flex:1; }
.card .links{ display:flex; gap:16px; margin-top:4px; }
.card .links a{ color:var(--link); text-decoration:none; font-size:13px; font-weight:600; }
.card.soon{ opacity:.65; border-style:dashed; }
.site-foot{ max-width:980px; margin:40px auto 0; font-size:12px; color:var(--muted); }
</style>
</head>
<body>
<header class="site-head">
<div class="head-left">
<a class="brand" href="/" title="VARASYS — Simplifying Complexity">
<img class="brand-logo brand-dark" src="data:image/png;base64,@BUILD:logo-dark@" alt="VARASYS — Simplifying Complexity" />
<img class="brand-logo brand-light" src="data:image/png;base64,@BUILD:logo-light@" alt="VARASYS — Simplifying Complexity" />
</a>
<span class="page-name"><b>PolyMeter</b> · Concepts</span>
</div>
<nav class="site-nav">
<a href="/index.html">Editor</a>
<span class="here">Concepts</span>
<button id="themeBtn" class="tbtn" title="toggle light / dark theme"></button>
</nav>
</header>
<main>
<h1>PolyMeter Concepts</h1>
<p class="lead">One polymeter engine, many form factors. The same firmware/share-language drives the
web editor and every device idea below — an ever-expanding library of concepts, from a full editor to
pocket practice hardware. Open one to try it live.</p>
<div class="grid">
<div class="card">
<span class="chip app">Web app</span>
<h3>PE1 — PolyMeter Editor</h3>
<p>The full editor: stack meter lanes, perstep accents / ghosts / mutes, swing &amp; ratio polyrhythm,
set lists, and shareable links. This is where you design grooves.</p>
<div class="links"><a href="/index.html">Open ↗</a></div>
</div>
<div class="card">
<span class="chip">Concept</span>
<h3>PM1 — Initial</h3>
<p>The original idealized device mock — full multilane display and setlist navigation. A northstar
concept (more than a single small unit can really show); the buildable take is Stage.</p>
<div class="links"><a href="/player.html">Open ↗</a></div>
</div>
<div class="card">
<span class="chip hw">Hardware</span>
<h3>PM1 — Stage</h3>
<p>Pedalboard build: 2.0″ colour TFT, arcade buttons, thumbroller, 1/4″ instrument passthrough with
analog click injection + balancedTRS out, 9 V DC / USBC. Beadblasted matteblack anodised.</p>
<div class="links"><a href="/stage.html">Open ↗</a></div>
</div>
<div class="card">
<span class="chip hw">Hardware</span>
<h3>PMµ — Micro</h3>
<p>Minimal homepractice unit: one push scrollencoder, a red 7segment LED, a speaker and USBC.
Spin = tempo · press = start/stop · hold + spin = switch track.</p>
<div class="links"><a href="/micro.html">Open ↗</a></div>
</div>
<div class="card soon">
<span class="chip">Coming</span>
<h3>More form factors</h3>
<p>The library keeps growing — desktop, Eurorack, wearable, headless module… each a widget you can
embed. Ideas welcome.</p>
</div>
</div>
</main>
<div class="site-foot">VARASYS · Simplifying Complexity — <span id="appVersion">v0.0.1-dev</span></div>
<script>
const APP_VERSION = "v0.0.1-dev";
const $ = (id)=>document.getElementById(id);
try{ $("appVersion").textContent = "v"+APP_VERSION.replace(/^v/,""); }catch(e){}
const THEMES=["system","light","dark"];
function effectiveTheme(p){ return p==="system" ? (matchMedia("(prefers-color-scheme: light)").matches?"light":"dark") : p; }
function themePref(){ try{ const p=localStorage.getItem("metronome.theme"); return (p==="light"||p==="dark"||p==="system")?p:"system"; }catch(e){ return "system"; } }
function applyTheme(p){ try{ localStorage.setItem("metronome.theme",p); }catch(e){}
document.documentElement.dataset.theme = effectiveTheme(p);
$("themeBtn").textContent = p==="system" ? "◐" : p==="light" ? "☀" : "☾"; $("themeBtn").title="Theme: "+p+" (system → light → dark)"; }
$("themeBtn").onclick = ()=> applyTheme(THEMES[(THEMES.indexOf(themePref())+1)%THEMES.length]);
matchMedia("(prefers-color-scheme: light)").addEventListener("change", ()=>{ if(themePref()==="system") applyTheme("system"); });
applyTheme(themePref());
</script>
</body>
</html>

View file

@ -39,14 +39,11 @@ else
fi fi
# stamp the version into the built copy only (source stays clean) # stamp the version into the built copy only (source stays clean)
sed "s|const APP_VERSION = \"[^\"]*\";|const APP_VERSION = \"$BUILD\";|" "$DIST_DIR/index.html" > "$DEST_DIR/index.html" echo "deployed v$BUILD -> $DEST_DIR"
echo "deployed v$BUILD ($(stat -c '%s' "$DEST_DIR/index.html") bytes) -> $DEST_DIR" for f in index.html player.html stage.html micro.html concepts.html; do
sed "s|const APP_VERSION = \"[^\"]*\";|const APP_VERSION = \"$BUILD\";|" "$DIST_DIR/player.html" > "$DEST_DIR/player.html" sed "s|const APP_VERSION = \"[^\"]*\";|const APP_VERSION = \"$BUILD\";|" "$DIST_DIR/$f" > "$DEST_DIR/$f"
echo "deployed player.html ($(stat -c '%s' "$DEST_DIR/player.html") bytes)" echo " $f ($(stat -c '%s' "$DEST_DIR/$f") bytes)"
sed "s|const APP_VERSION = \"[^\"]*\";|const APP_VERSION = \"$BUILD\";|" "$DIST_DIR/stage.html" > "$DEST_DIR/stage.html" done
echo "deployed stage.html ($(stat -c '%s' "$DEST_DIR/stage.html") bytes)"
sed "s|const APP_VERSION = \"[^\"]*\";|const APP_VERSION = \"$BUILD\";|" "$DIST_DIR/micro.html" > "$DEST_DIR/micro.html"
echo "deployed micro.html ($(stat -c '%s' "$DEST_DIR/micro.html") bytes)"
rm -f "$DEST_DIR/player-asbuilt.html" # renamed to stage.html rm -f "$DEST_DIR/player-asbuilt.html" # renamed to stage.html
# If real audio samples are added later (see the plan's GM-sample note), # If real audio samples are added later (see the plan's GM-sample note),

View file

@ -3,7 +3,7 @@
<head> <head>
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Stackable Metronome</title> <title>PE1 — PolyMeter Editor</title>
<link rel="icon" type="image/svg+xml" href="data:image/svg+xml;base64,@BUILD:favicon@"> <link rel="icon" type="image/svg+xml" href="data:image/svg+xml;base64,@BUILD:favicon@">
<script> <script>
// Set theme before first paint (avoids a flash). Preference is system|light|dark // Set theme before first paint (avoids a flash). Preference is system|light|dark
@ -216,8 +216,9 @@
<div id="app"> <div id="app">
<div class="device"> <div class="device">
<div class="row appheader" style="align-items:center; flex-wrap:wrap; gap:6px 14px; margin-bottom:8px"> <div class="row appheader" style="align-items:center; flex-wrap:wrap; gap:6px 14px; margin-bottom:8px">
<h1 style="margin:0">Stackable Metronome <span class="lane-meta" id="appVersion" title="build version">v0.0.1-dev</span></h1> <h1 style="margin:0">PE1 <span style="font-weight:400; opacity:.75">PolyMeter Editor</span> <span class="lane-meta" id="appVersion" title="build version">v0.0.1-dev</span></h1>
<div class="appheader-ctrls" style="display:flex; align-items:center; gap:10px"> <div class="appheader-ctrls" style="display:flex; align-items:center; gap:10px">
<a href="/concepts.html" style="font-size:13px; color:var(--muted); text-decoration:none" title="Concept gallery — hardware & widget form factors">Concepts</a>
<button id="themeBtn" title="toggle light / dark theme"></button> <button id="themeBtn" title="toggle light / dark theme"></button>
<button id="helpBtn" title="keyboard shortcuts (?)">?</button> <button id="helpBtn" title="keyboard shortcuts (?)">?</button>
<a class="brand" href="https://varasys.io" target="_blank" rel="noopener" <a class="brand" href="https://varasys.io" target="_blank" rel="noopener"

View file

@ -89,14 +89,20 @@
</head> </head>
<body> <body>
<div class="topbar"> <header class="site-head">
<span><b>VARASYS PMµ</b> · micro (home practice)</span> <div class="head-left">
<span class="topbar-right"> <a class="brand" href="/" title="VARASYS — Simplifying Complexity">
<img class="brand-logo brand-dark" src="data:image/png;base64,@BUILD:logo-dark@" alt="VARASYS — Simplifying Complexity" />
<img class="brand-logo brand-light" src="data:image/png;base64,@BUILD:logo-light@" alt="VARASYS — Simplifying Complexity" />
</a>
<span class="page-name"><b>PMµ</b> · Micro (home practice)</span>
</div>
<nav class="site-nav">
<a href="/index.html">Editor</a>
<a href="/concepts.html">Concepts</a>
<button id="themeBtn" class="tbtn" title="toggle light / dark theme"></button> <button id="themeBtn" class="tbtn" title="toggle light / dark theme"></button>
<a href="/stage.html">Stage ↗</a> </nav>
<a href="/index.html">Editor ↗</a> </header>
</span>
</div>
<div class="device"> <div class="device">
<div class="brandrow"> <div class="brandrow">

View file

@ -194,16 +194,21 @@
</head> </head>
<body> <body>
<div class="topbar"> <header class="site-head">
<span><b>VARASYS PM1</b> · hardware player (mockup)</span> <div class="head-left">
<span class="topbar-right"> <a class="brand" href="/" title="VARASYS — Simplifying Complexity">
<img class="brand-logo brand-dark" src="data:image/png;base64,@BUILD:logo-dark@" alt="VARASYS — Simplifying Complexity" />
<img class="brand-logo brand-light" src="data:image/png;base64,@BUILD:logo-light@" alt="VARASYS — Simplifying Complexity" />
</a>
<span class="page-name"><b>PM1</b> · Initial concept</span>
</div>
<nav class="site-nav">
<a href="/index.html">Editor</a>
<a href="/concepts.html">Concepts</a>
<button id="fsBtn" class="tbtn" title="Full screen (landscape)"></button> <button id="fsBtn" class="tbtn" title="Full screen (landscape)"></button>
<button id="themeBtn" class="tbtn" title="toggle light / dark theme"></button> <button id="themeBtn" class="tbtn" title="toggle light / dark theme"></button>
<a href="/stage.html">Stage ↗</a> </nav>
<a href="/micro.html">Micro ↗</a> </header>
<a href="/index.html">Open editor ↗</a>
</span>
</div>
<!-- ===================== THE DEVICE ===================== --> <!-- ===================== THE DEVICE ===================== -->
<div class="device"> <div class="device">

View file

@ -10,3 +10,24 @@ body {
font-family: "Segoe UI", Roboto, Helvetica, Arial, sans-serif; font-family: "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased; -webkit-font-smoothing: antialiased;
} }
/* ---- VARASYS site header (shared across every page) ---- */
.brand { display:inline-flex; align-items:center; flex:0 0 auto; }
.brand-logo { height:30px; width:auto; display:block; }
.brand-light { display:none; }
:root[data-theme="light"] .brand-dark { display:none; }
:root[data-theme="light"] .brand-light { display:block; }
.site-head { width:100%; max-width:980px; margin:0 auto; display:flex; align-items:center;
justify-content:space-between; gap:10px 16px; flex-wrap:wrap; }
.head-left { display:flex; align-items:center; gap:12px; flex-wrap:wrap; }
.page-name { font-size:13px; color:var(--muted,#7f8b9a); letter-spacing:.02em; }
.page-name b { color:var(--txt,#c7d0db); }
.site-nav { display:flex; align-items:center; gap:14px; font-size:13px; }
.site-nav a { color:var(--muted,#7f8b9a); text-decoration:none; }
.site-nav a:hover { color:var(--txt,#c7d0db); }
.site-nav a.here { color:var(--cyan); }
.tbtn { background:transparent; color:var(--muted,#7f8b9a); border:1px solid var(--panel-bd,#2a313c);
border-radius:8px; padding:3px 9px; font-size:14px; line-height:1; cursor:pointer; }
.tbtn:hover { color:var(--txt,#c7d0db); }
/* embed mode: pages opened with ?embed=1 strip all site chrome (the widget only) */
body[data-embed] .site-head, body[data-embed] .site-foot { display:none !important; }

View file

@ -196,15 +196,20 @@
</head> </head>
<body> <body>
<div class="topbar"> <header class="site-head">
<span><b>VARASYS PM1</b> · stage (pedalboard build)</span> <div class="head-left">
<span class="topbar-right"> <a class="brand" href="/" title="VARASYS — Simplifying Complexity">
<img class="brand-logo brand-dark" src="data:image/png;base64,@BUILD:logo-dark@" alt="VARASYS — Simplifying Complexity" />
<img class="brand-logo brand-light" src="data:image/png;base64,@BUILD:logo-light@" alt="VARASYS — Simplifying Complexity" />
</a>
<span class="page-name"><b>PM1</b> · Stage (pedalboard build)</span>
</div>
<nav class="site-nav">
<a href="/index.html">Editor</a>
<a href="/concepts.html">Concepts</a>
<button id="themeBtn" class="tbtn" title="toggle light / dark theme"></button> <button id="themeBtn" class="tbtn" title="toggle light / dark theme"></button>
<a href="/micro.html">Micro ↗</a> </nav>
<a href="/player.html">Initial ↗</a> </header>
<a href="/index.html">Editor ↗</a>
</span>
</div>
<div class="cols"> <div class="cols">
<div class="col-left"> <div class="col-left">